@ -26,10 +26,6 @@
# include "mi-getopt.h"
# include "ui-out.h"
static int gdb_dis_asm_read_memory ( bfd_vma memaddr , bfd_byte * myaddr , unsigned int len ,
disassemble_info * info ) ;
static int compare_lines ( const PTR mle1p , const PTR mle2p ) ;
/* Disassemble functions. FIXME: these do not really belong here. We
should get rid of all the duplicate code in gdb that does the same
thing : disassemble_command ( ) and the gdbtk variation . */
@ -86,6 +82,293 @@ compare_lines (const PTR mle1p, const PTR mle2p)
return mle1 - > start_pc - mle2 - > start_pc ;
}
static int
dump_insns ( disassemble_info * di , CORE_ADDR low , CORE_ADDR high ,
int how_many , struct ui_stream * stb )
{
int num_displayed = 0 ;
CORE_ADDR pc ;
/* parts of the symbolic representation of the address */
int unmapped ;
char * filename = NULL ;
char * name = NULL ;
int offset ;
int line ;
for ( pc = low ; pc < high ; )
{
QUIT ;
if ( how_many > = 0 )
{
if ( num_displayed > = how_many )
break ;
else
num_displayed + + ;
}
ui_out_tuple_begin ( uiout , NULL ) ;
ui_out_field_core_addr ( uiout , " address " , pc ) ;
if ( ! build_address_symbolic ( pc , 0 , & name , & offset , & filename ,
& line , & unmapped ) )
{
/* We don't care now about line, filename and
unmapped . But we might in the future . */
ui_out_field_string ( uiout , " func-name " , name ) ;
ui_out_field_int ( uiout , " offset " , offset ) ;
}
if ( filename ! = NULL )
xfree ( filename ) ;
if ( name ! = NULL )
xfree ( name ) ;
ui_file_rewind ( stb - > stream ) ;
pc + = ( * tm_print_insn ) ( pc , di ) ;
ui_out_field_stream ( uiout , " inst " , stb ) ;
ui_file_rewind ( stb - > stream ) ;
ui_out_tuple_end ( uiout ) ;
}
return num_displayed ;
}
/* The idea here is to present a source-O-centric view of a
function to the user . This means that things are presented
in source order , with ( possibly ) out of order assembly
immediately following . */
static void
do_mixed_source_and_assembly ( struct disassemble_info * di , int nlines ,
struct linetable_entry * le ,
CORE_ADDR low , CORE_ADDR high ,
struct symtab * symtab ,
int how_many , struct ui_stream * stb )
{
int newlines = 0 ;
struct dis_line_entry * mle ;
struct symtab_and_line sal ;
int i ;
int out_of_order = 0 ;
int next_line = 0 ;
CORE_ADDR pc ;
int num_displayed = 0 ;
mle = ( struct dis_line_entry * ) alloca ( nlines
* sizeof ( struct dis_line_entry ) ) ;
/* Copy linetable entries for this function into our data
structure , creating end_pc ' s and setting out_of_order as
appropriate . */
/* First, skip all the preceding functions. */
for ( i = 0 ; i < nlines - 1 & & le [ i ] . pc < low ; i + + ) ;
/* Now, copy all entries before the end of this function. */
for ( ; i < nlines - 1 & & le [ i ] . pc < high ; i + + )
{
if ( le [ i ] . line = = le [ i + 1 ] . line
& & le [ i ] . pc = = le [ i + 1 ] . pc )
continue ; /* Ignore duplicates */
/* Skip any end-of-function markers. */
if ( le [ i ] . line = = 0 )
continue ;
mle [ newlines ] . line = le [ i ] . line ;
if ( le [ i ] . line > le [ i + 1 ] . line )
out_of_order = 1 ;
mle [ newlines ] . start_pc = le [ i ] . pc ;
mle [ newlines ] . end_pc = le [ i + 1 ] . pc ;
newlines + + ;
}
/* If we're on the last line, and it's part of the function,
then we need to get the end pc in a special way . */
if ( i = = nlines - 1
& & le [ i ] . pc < high )
{
mle [ newlines ] . line = le [ i ] . line ;
mle [ newlines ] . start_pc = le [ i ] . pc ;
sal = find_pc_line ( le [ i ] . pc , 0 ) ;
mle [ newlines ] . end_pc = sal . end ;
newlines + + ;
}
/* Now, sort mle by line #s (and, then by addresses within
lines ) . */
if ( out_of_order )
qsort ( mle , newlines , sizeof ( struct dis_line_entry ) , compare_lines ) ;
/* Now, for each line entry, emit the specified lines (unless
they have been emitted before ) , followed by the assembly code
for that line . */
ui_out_list_begin ( uiout , " asm_insns " ) ;
for ( i = 0 ; i < newlines ; i + + )
{
int close_list = 1 ;
/* Print out everything from next_line to the current line. */
if ( mle [ i ] . line > = next_line )
{
if ( next_line ! = 0 )
{
/* Just one line to print. */
if ( next_line = = mle [ i ] . line )
{
ui_out_tuple_begin ( uiout , " src_and_asm_line " ) ;
print_source_lines ( symtab , next_line , mle [ i ] . line + 1 , 0 ) ;
}
else
{
/* Several source lines w/o asm instructions associated. */
for ( ; next_line < mle [ i ] . line ; next_line + + )
{
ui_out_tuple_begin ( uiout , " src_and_asm_line " ) ;
print_source_lines ( symtab , next_line , next_line + 1 , 0 ) ;
ui_out_list_begin ( uiout , " line_asm_insn " ) ;
ui_out_list_end ( uiout ) ;
ui_out_tuple_end ( uiout ) ;
}
/* Print the last line and leave list open for
asm instructions to be added . */
ui_out_tuple_begin ( uiout , " src_and_asm_line " ) ;
print_source_lines ( symtab , next_line , mle [ i ] . line + 1 , 0 ) ;
}
}
else
{
ui_out_tuple_begin ( uiout , " src_and_asm_line " ) ;
print_source_lines ( symtab , mle [ i ] . line , mle [ i ] . line + 1 , 0 ) ;
}
next_line = mle [ i ] . line + 1 ;
ui_out_list_begin ( uiout , " line_asm_insn " ) ;
/* Don't close the list if the lines are not in order. */
if ( i < ( newlines - 1 ) & & mle [ i + 1 ] . line < = mle [ i ] . line )
close_list = 0 ;
}
num_displayed + = dump_insns ( di , mle [ i ] . start_pc , mle [ i ] . end_pc ,
how_many , stb ) ;
if ( close_list )
{
ui_out_list_end ( uiout ) ;
ui_out_tuple_end ( uiout ) ;
close_list = 0 ;
}
if ( how_many > = 0 )
if ( num_displayed > = how_many )
break ;
}
ui_out_list_end ( uiout ) ;
}
static void
do_assembly_only ( disassemble_info * di , CORE_ADDR low ,
CORE_ADDR high , int how_many , struct ui_stream * stb )
{
int num_displayed = 0 ;
ui_out_list_begin ( uiout , " asm_insns " ) ;
num_displayed = dump_insns ( di , low , high , how_many , stb ) ;
ui_out_list_end ( uiout ) ;
}
enum mi_cmd_result
do_disassembly ( char * file_string ,
int line_num ,
int mixed_source_and_assembly ,
int how_many ,
CORE_ADDR low ,
CORE_ADDR high )
{
static disassemble_info di ;
static int di_initialized ;
/* To collect the instruction outputted from opcodes. */
static struct ui_stream * stb = NULL ;
struct symtab * symtab = NULL ;
struct linetable_entry * le = NULL ;
int nlines = - 1 ;
if ( ! di_initialized )
{
/* We don't add a cleanup for this, because the allocation of
the stream is done once only for each gdb run , and we need to
keep it around until the end . Hopefully there won ' t be any
errors in the init code below , that make this function bail
out . */
stb = ui_out_stream_new ( uiout ) ;
INIT_DISASSEMBLE_INFO_NO_ARCH ( di , stb - > stream ,
( fprintf_ftype ) fprintf_unfiltered ) ;
di . flavour = bfd_target_unknown_flavour ;
di . memory_error_func = dis_asm_memory_error ;
di . print_address_func = dis_asm_print_address ;
di_initialized = 1 ;
}
di . mach = TARGET_PRINT_INSN_INFO - > mach ;
if ( TARGET_BYTE_ORDER = = BFD_ENDIAN_BIG )
di . endian = BFD_ENDIAN_BIG ;
else
di . endian = BFD_ENDIAN_LITTLE ;
/* If gdb_disassemble_from_exec == -1, then we use the following heuristic to
determine whether or not to do disassembly from target memory or from the
exec file :
If we ' re debugging a local process , read target memory , instead of the
exec file . This makes disassembly of functions in shared libs work
correctly . Also , read target memory if we are debugging native threads .
Else , we ' re debugging a remote process , and should disassemble from the
exec file for speed . However , this is no good if the target modifies its
code ( for relocation , or whatever ) . */
if ( gdb_disassemble_from_exec = = - 1 )
{
if ( strcmp ( target_shortname , " child " ) = = 0
| | strcmp ( target_shortname , " procfs " ) = = 0
| | strcmp ( target_shortname , " vxprocess " ) = = 0
| | strstr ( target_shortname , " -threads " ) ! = NULL )
gdb_disassemble_from_exec = 0 ; /* It's a child process, read inferior mem */
else
gdb_disassemble_from_exec = 1 ; /* It's remote, read the exec file */
}
if ( gdb_disassemble_from_exec )
di . read_memory_func = gdb_dis_asm_read_memory ;
else
di . read_memory_func = dis_asm_read_memory ;
/* Assume symtab is valid for whole PC range */
symtab = find_pc_symtab ( low ) ;
if ( symtab ! = NULL & & symtab - > linetable ! = NULL )
{
/* Convert the linetable to a bunch of my_line_entry's. */
le = symtab - > linetable - > item ;
nlines = symtab - > linetable - > nitems ;
}
if ( ! mixed_source_and_assembly | | nlines < = 0
| | symtab = = NULL | | symtab - > linetable = = NULL )
do_assembly_only ( & di , low , high , how_many , stb ) ;
else if ( mixed_source_and_assembly )
do_mixed_source_and_assembly ( & di , nlines , le , low ,
high , symtab , how_many , stb ) ;
gdb_flush ( gdb_stdout ) ;
return MI_CMD_DONE ;
}
/* The arguments to be passed on the command line and parsed here are:
either :
@ -106,30 +389,15 @@ compare_lines (const PTR mle1p, const PTR mle2p)
MODE : 0 or 1 for disassembly only , or mixed source and disassembly ,
respectively . */
enum mi_cmd_result
mi_cmd_disassemble ( char * command , char * * argv , int argc )
{
CORE_ADDR pc ;
enum mi_cmd_result retval ;
CORE_ADDR start ;
int mixed_source_and_assembly ;
int num_displayed ;
static disassemble_info di ;
static int di_initialized ;
struct symtab * s ;
/* To collect the instruction outputted from opcodes. */
static struct ui_stream * stb = NULL ;
/* parts of the symbolic representation of the address */
int line ;
int offset ;
int unmapped ;
char * filename = NULL ;
char * name = NULL ;
/* Which options have we processed ... */
int file_seen = 0 ;
int line_seen = 0 ;
@ -211,6 +479,7 @@ mi_cmd_disassemble (char *command, char **argv, int argc)
if ( ( mixed_source_and_assembly ! = 0 ) & & ( mixed_source_and_assembly ! = 1 ) )
error ( " mi_cmd_disassemble: Mixed_mode argument must be 0 or 1. " ) ;
/* We must get the function beginning and end where line_num is
contained . */
@ -225,275 +494,11 @@ mi_cmd_disassemble (char *command, char **argv, int argc)
error ( " mi_cmd_disassemble: No function contains specified address " ) ;
}
if ( ! di_initialized )
{
/* We don't add a cleanup for this, because the allocation of
the stream is done once only for each gdb run , and we need to
keep it around until the end . Hopefully there won ' t be any
errors in the init code below , that make this function bail
out . */
stb = ui_out_stream_new ( uiout ) ;
INIT_DISASSEMBLE_INFO_NO_ARCH ( di , stb - > stream ,
( fprintf_ftype ) fprintf_unfiltered ) ;
di . flavour = bfd_target_unknown_flavour ;
di . memory_error_func = dis_asm_memory_error ;
di . print_address_func = dis_asm_print_address ;
di_initialized = 1 ;
}
di . mach = TARGET_PRINT_INSN_INFO - > mach ;
if ( TARGET_BYTE_ORDER = = BFD_ENDIAN_BIG )
di . endian = BFD_ENDIAN_BIG ;
else
di . endian = BFD_ENDIAN_LITTLE ;
/* If gdb_disassemble_from_exec == -1, then we use the following heuristic to
determine whether or not to do disassembly from target memory or from the
exec file :
If we ' re debugging a local process , read target memory , instead of the
exec file . This makes disassembly of functions in shared libs work
correctly . Also , read target memory if we are debugging native threads .
Else , we ' re debugging a remote process , and should disassemble from the
exec file for speed . However , this is no good if the target modifies its
code ( for relocation , or whatever ) .
*/
if ( gdb_disassemble_from_exec = = - 1 )
{
if ( strcmp ( target_shortname , " child " ) = = 0
| | strcmp ( target_shortname , " procfs " ) = = 0
| | strcmp ( target_shortname , " vxprocess " ) = = 0
| | strstr ( target_shortname , " -threads " ) ! = NULL )
gdb_disassemble_from_exec = 0 ; /* It's a child process, read inferior mem */
else
gdb_disassemble_from_exec = 1 ; /* It's remote, read the exec file */
}
if ( gdb_disassemble_from_exec )
di . read_memory_func = gdb_dis_asm_read_memory ;
else
di . read_memory_func = dis_asm_read_memory ;
/* If just doing straight assembly, all we need to do is disassemble
everything between low and high . If doing mixed source / assembly ,
we ' ve got a totally different path to follow . */
if ( mixed_source_and_assembly )
{
/* Come here for mixed source/assembly */
/* The idea here is to present a source-O-centric view of a
function to the user . This means that things are presented
in source order , with ( possibly ) out of order assembly
immediately following . */
struct symtab * symtab ;
struct linetable_entry * le ;
int nlines ;
int newlines ;
struct dis_line_entry * mle ;
struct symtab_and_line sal ;
int i ;
int out_of_order ;
int next_line ;
/* Assume symtab is valid for whole PC range */
symtab = find_pc_symtab ( low ) ;
if ( ! symtab | | ! symtab - > linetable )
goto assembly_only ;
/* First, convert the linetable to a bunch of my_line_entry's. */
le = symtab - > linetable - > item ;
nlines = symtab - > linetable - > nitems ;
if ( nlines < = 0 )
goto assembly_only ;
mle = ( struct dis_line_entry * ) alloca ( nlines * sizeof ( struct dis_line_entry ) ) ;
out_of_order = 0 ;
/* Copy linetable entries for this function into our data
structure , creating end_pc ' s and setting out_of_order as
appropriate . */
/* First, skip all the preceding functions. */
for ( i = 0 ; i < nlines - 1 & & le [ i ] . pc < low ; i + + ) ;
/* Now, copy all entries before the end of this function. */
newlines = 0 ;
for ( ; i < nlines - 1 & & le [ i ] . pc < high ; i + + )
{
if ( le [ i ] . line = = le [ i + 1 ] . line
& & le [ i ] . pc = = le [ i + 1 ] . pc )
continue ; /* Ignore duplicates */
/* Skip any end-of-function markers. */
if ( le [ i ] . line = = 0 )
continue ;
mle [ newlines ] . line = le [ i ] . line ;
if ( le [ i ] . line > le [ i + 1 ] . line )
out_of_order = 1 ;
mle [ newlines ] . start_pc = le [ i ] . pc ;
mle [ newlines ] . end_pc = le [ i + 1 ] . pc ;
newlines + + ;
}
/* If we're on the last line, and it's part of the function,
then we need to get the end pc in a special way . */
if ( i = = nlines - 1
& & le [ i ] . pc < high )
{
mle [ newlines ] . line = le [ i ] . line ;
mle [ newlines ] . start_pc = le [ i ] . pc ;
sal = find_pc_line ( le [ i ] . pc , 0 ) ;
mle [ newlines ] . end_pc = sal . end ;
newlines + + ;
}
/* Now, sort mle by line #s (and, then by addresses within
lines ) . */
if ( out_of_order )
qsort ( mle , newlines , sizeof ( struct dis_line_entry ) , compare_lines ) ;
/* Now, for each line entry, emit the specified lines (unless
they have been emitted before ) , followed by the assembly code
for that line . */
next_line = 0 ; /* Force out first line */
ui_out_list_begin ( uiout , " asm_insns " ) ;
num_displayed = 0 ;
for ( i = 0 ; i < newlines ; i + + )
{
int close_list = 1 ;
/* Print out everything from next_line to the current line. */
if ( mle [ i ] . line > = next_line )
{
if ( next_line ! = 0 )
{
/* Just one line to print. */
if ( next_line = = mle [ i ] . line )
{
ui_out_tuple_begin ( uiout , " src_and_asm_line " ) ;
print_source_lines ( symtab , next_line , mle [ i ] . line + 1 , 0 ) ;
}
else
{
/* Several source lines w/o asm instructions associated. */
for ( ; next_line < mle [ i ] . line ; next_line + + )
{
ui_out_tuple_begin ( uiout , " src_and_asm_line " ) ;
print_source_lines ( symtab , next_line , mle [ i ] . line + 1 , 0 ) ;
ui_out_list_begin ( uiout , " line_asm_insn " ) ;
ui_out_list_end ( uiout ) ;
ui_out_tuple_end ( uiout ) ;
}
/* Print the last line and leave list open for
asm instructions to be added . */
ui_out_tuple_begin ( uiout , " src_and_asm_line " ) ;
print_source_lines ( symtab , next_line , mle [ i ] . line + 1 , 0 ) ;
}
}
else
{
ui_out_tuple_begin ( uiout , " src_and_asm_line " ) ;
print_source_lines ( symtab , mle [ i ] . line , mle [ i ] . line + 1 , 0 ) ;
}
next_line = mle [ i ] . line + 1 ;
ui_out_list_begin ( uiout , " line_asm_insn " ) ;
if ( i + 1 < newlines & & mle [ i + 1 ] . line < = mle [ i ] . line )
close_list = 0 ;
}
for ( pc = mle [ i ] . start_pc ; pc < mle [ i ] . end_pc ; )
{
QUIT ;
if ( how_many > = 0 )
{
if ( num_displayed > = how_many )
break ;
else
num_displayed + + ;
}
ui_out_tuple_begin ( uiout , NULL ) ;
ui_out_field_core_addr ( uiout , " address " , pc ) ;
if ( ! build_address_symbolic ( pc , 0 , & name , & offset , & filename , & line , & unmapped ) )
{
/* We don't care now about line, filename and
unmapped , but we might in the future . */
ui_out_field_string ( uiout , " func-name " , name ) ;
ui_out_field_int ( uiout , " offset " , offset ) ;
}
if ( filename ! = NULL )
xfree ( filename ) ;
if ( name ! = NULL )
xfree ( name ) ;
ui_file_rewind ( stb - > stream ) ;
pc + = TARGET_PRINT_INSN ( pc , & di ) ;
ui_out_field_stream ( uiout , " inst " , stb ) ;
ui_file_rewind ( stb - > stream ) ;
ui_out_tuple_end ( uiout ) ;
}
if ( close_list )
{
ui_out_list_end ( uiout ) ;
ui_out_tuple_end ( uiout ) ;
close_list = 0 ;
}
if ( how_many > = 0 )
if ( num_displayed > = how_many )
break ;
}
ui_out_list_end ( uiout ) ;
}
else
{
assembly_only :
ui_out_list_begin ( uiout , " asm_insns " ) ;
num_displayed = 0 ;
for ( pc = low ; pc < high ; )
{
QUIT ;
if ( how_many > = 0 )
{
if ( num_displayed > = how_many )
break ;
else
num_displayed + + ;
}
ui_out_tuple_begin ( uiout , NULL ) ;
ui_out_field_core_addr ( uiout , " address " , pc ) ;
if ( ! build_address_symbolic ( pc , 0 , & name , & offset , & filename , & line , & unmapped ) )
{
/* We don't care now about line, filename and
unmapped . But we might in the future . */
ui_out_field_string ( uiout , " func-name " , name ) ;
ui_out_field_int ( uiout , " offset " , offset ) ;
}
if ( filename ! = NULL )
xfree ( filename ) ;
if ( name ! = NULL )
xfree ( name ) ;
ui_file_rewind ( stb - > stream ) ;
pc + = TARGET_PRINT_INSN ( pc , & di ) ;
ui_out_field_stream ( uiout , " inst " , stb ) ;
ui_file_rewind ( stb - > stream ) ;
ui_out_tuple_end ( uiout ) ;
}
ui_out_list_end ( uiout ) ;
}
gdb_flush ( gdb_stdout ) ;
return MI_CMD_DONE ;
retval = do_disassembly ( file_string ,
line_num ,
mixed_source_and_assembly ,
how_many ,
low ,
high ) ;
return retval ;
}