#include "defs.h"
#include "target.h"
#include "value.h"
#include "ui-out.h"
#include "gdb_string.h"
#include "disasm.h"
#include "gdbcore.h"
#include "dis-asm.h"
#include "gdb_assert.h"
struct dis_line_entry
{
int line;
CORE_ADDR start_pc;
CORE_ADDR end_pc;
};
static int
dis_asm_read_memory (bfd_vma memaddr, gdb_byte *myaddr, unsigned int len,
struct disassemble_info *info)
{
return target_read_memory (memaddr, myaddr, len);
}
static void
dis_asm_memory_error (int status, bfd_vma memaddr,
struct disassemble_info *info)
{
memory_error (status, memaddr);
}
static void
dis_asm_print_address (bfd_vma addr, struct disassemble_info *info)
{
print_address (addr, info->stream);
}
static int
compare_lines (const void *mle1p, const void *mle2p)
{
struct dis_line_entry *mle1, *mle2;
int val;
mle1 = (struct dis_line_entry *) mle1p;
mle2 = (struct dis_line_entry *) mle2p;
val = mle1->line - mle2->line;
if (val != 0)
return val;
return mle1->start_pc - mle2->start_pc;
}
static int
dump_insns (struct ui_out *uiout, struct disassemble_info * di,
CORE_ADDR low, CORE_ADDR high,
int how_many, struct ui_stream *stb)
{
int num_displayed = 0;
CORE_ADDR pc;
int unmapped;
int offset;
int line;
struct cleanup *ui_out_chain;
for (pc = low; pc < high;)
{
char *filename = NULL;
char *name = NULL;
QUIT;
if (how_many >= 0)
{
if (num_displayed >= how_many)
break;
else
num_displayed++;
}
ui_out_chain = make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
ui_out_field_core_addr (uiout, "address", pc);
if (!build_address_symbolic (pc, 0, &name, &offset, &filename,
&line, &unmapped))
{
ui_out_text (uiout, " <");
ui_out_field_string (uiout, "func-name", name);
ui_out_text (uiout, "+");
ui_out_field_int (uiout, "offset", offset);
ui_out_text (uiout, ">:\t");
}
else
ui_out_text (uiout, ":\t");
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);
do_cleanups (ui_out_chain);
ui_out_text (uiout, "\n");
}
return num_displayed;
}
static void
do_mixed_source_and_assembly (struct ui_out *uiout,
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;
int num_displayed = 0;
struct cleanup *ui_out_chain;
struct cleanup *ui_out_tuple_chain = make_cleanup (null_cleanup, 0);
struct cleanup *ui_out_list_chain = make_cleanup (null_cleanup, 0);
mle = (struct dis_line_entry *) alloca (nlines
* sizeof (struct dis_line_entry));
for (i = 0; i < nlines - 1 && le[i].pc < low; i++);
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;
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 (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++;
}
if (out_of_order)
qsort (mle, newlines, sizeof (struct dis_line_entry), compare_lines);
ui_out_chain = make_cleanup_ui_out_list_begin_end (uiout, "asm_insns");
for (i = 0; i < newlines; i++)
{
if (mle[i].line >= next_line)
{
if (next_line != 0)
{
if (next_line == mle[i].line)
{
ui_out_tuple_chain
= make_cleanup_ui_out_tuple_begin_end (uiout,
"src_and_asm_line");
print_source_lines (symtab, next_line, mle[i].line + 1, 0);
}
else
{
for (; next_line < mle[i].line; next_line++)
{
struct cleanup *ui_out_list_chain_line;
struct cleanup *ui_out_tuple_chain_line;
ui_out_tuple_chain_line
= make_cleanup_ui_out_tuple_begin_end (uiout,
"src_and_asm_line");
print_source_lines (symtab, next_line, next_line + 1,
0);
ui_out_list_chain_line
= make_cleanup_ui_out_list_begin_end (uiout,
"line_asm_insn");
do_cleanups (ui_out_list_chain_line);
do_cleanups (ui_out_tuple_chain_line);
}
ui_out_tuple_chain
= make_cleanup_ui_out_tuple_begin_end (uiout,
"src_and_asm_line");
print_source_lines (symtab, next_line, mle[i].line + 1, 0);
}
}
else
{
ui_out_tuple_chain
= make_cleanup_ui_out_tuple_begin_end (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_chain
= make_cleanup_ui_out_list_begin_end (uiout, "line_asm_insn");
}
num_displayed += dump_insns (uiout, di, mle[i].start_pc, mle[i].end_pc,
how_many, stb);
if (i == (newlines - 1) || mle[i + 1].line > mle[i].line)
{
do_cleanups (ui_out_list_chain);
do_cleanups (ui_out_tuple_chain);
ui_out_tuple_chain = make_cleanup (null_cleanup, 0);
ui_out_list_chain = make_cleanup (null_cleanup, 0);
ui_out_text (uiout, "\n");
}
if (how_many >= 0 && num_displayed >= how_many)
break;
}
do_cleanups (ui_out_chain);
}
static void
do_assembly_only (struct ui_out *uiout, struct disassemble_info * di,
CORE_ADDR low, CORE_ADDR high,
int how_many, struct ui_stream *stb)
{
int num_displayed = 0;
struct cleanup *ui_out_chain;
ui_out_chain = make_cleanup_ui_out_list_begin_end (uiout, "asm_insns");
num_displayed = dump_insns (uiout, di, low, high, how_many, stb);
do_cleanups (ui_out_chain);
}
static int ATTR_FORMAT (printf, 2, 3)
fprintf_disasm (void *stream, const char *format, ...)
{
va_list args;
va_start (args, format);
vfprintf_filtered (stream, format, args);
va_end (args);
return 0;
}
static struct disassemble_info
gdb_disassemble_info (struct gdbarch *gdbarch, struct ui_file *file)
{
struct disassemble_info di;
init_disassemble_info (&di, file, fprintf_disasm);
di.flavour = bfd_target_unknown_flavour;
di.memory_error_func = dis_asm_memory_error;
di.print_address_func = dis_asm_print_address;
di.read_memory_func = dis_asm_read_memory;
di.arch = gdbarch_bfd_arch_info (gdbarch)->arch;
di.mach = gdbarch_bfd_arch_info (gdbarch)->mach;
di.endian = gdbarch_byte_order (gdbarch);
disassemble_init_for_target (&di);
return di;
}
static struct disassemble_info
gdb_disassemble_info_null (struct gdbarch *gdbarch)
{
struct disassemble_info ret = gdb_disassemble_info (gdbarch, gdb_null);
return ret;
}
void
gdb_disassembly (struct ui_out *uiout,
CORE_ADDR low, CORE_ADDR high,
int mixed_source_and_assembly,
int how_many)
{
struct ui_stream *stb = ui_out_stream_new (uiout);
struct cleanup *cleanups = make_cleanup_ui_out_stream_delete (stb);
struct disassemble_info di = gdb_disassemble_info (current_gdbarch, stb->stream);
struct symtab *symtab = NULL;
struct linetable_entry *le = NULL;
int nlines = -1;
symtab = find_pc_symtab (low);
if (symtab != NULL && symtab->linetable != NULL)
{
le = symtab->linetable->item;
nlines = symtab->linetable->nitems;
}
if (!mixed_source_and_assembly || nlines <= 0
|| symtab == NULL || symtab->linetable == NULL)
do_assembly_only (uiout, &di, low, high, how_many, stb);
else if (mixed_source_and_assembly)
do_mixed_source_and_assembly (uiout, &di, nlines, le, low,
high, symtab, how_many, stb);
do_cleanups (cleanups);
gdb_flush (gdb_stdout);
}
static
int gdbarch_instruction_length (struct gdbarch *gdbarch)
{
const struct bfd_arch_info *info = gdbarch_bfd_arch_info (gdbarch);
switch (info->arch)
{
case bfd_arch_powerpc:
return 4;
default:
return -1;
}
}
int
find_pc_offset (CORE_ADDR start, CORE_ADDR *result, int offset, int funclimit,
int peeklimit)
{
CORE_ADDR low = INVALID_ADDRESS;
CORE_ADDR high = INVALID_ADDRESS;
CORE_ADDR cur;
CORE_ADDR constrained;
int length;
struct disassemble_info di = gdb_disassemble_info_null (current_gdbarch);
CORE_ADDR *addrs = NULL;
unsigned int index;
struct cleanup *cleanup = NULL;
*result = INVALID_ADDRESS;
cur = start;
if (funclimit)
{
if (find_pc_partial_function_no_inlined (start, NULL, &low, &high) == 0)
{
return -1;
}
}
length = gdbarch_instruction_length (current_gdbarch);
if (length > 0)
{
cur = start + length * offset;
if (funclimit && (cur > high))
constrained = high;
else if (funclimit && (cur < low))
constrained = low;
else
constrained = cur;
*result = constrained;
return (constrained != cur);
}
if ((! funclimit) && (offset < 0))
{
return -1;
}
cur = start;
while (offset > 0)
{
cur += TARGET_PRINT_INSN (cur, &di);
offset--;
if (funclimit && (cur > high))
{
return -1;
}
if (funclimit && (cur == high))
{
*result = cur;
return (offset > 0);
}
}
if (offset == 0)
{
*result = cur;
return 0;
}
gdb_assert (low <= start);
gdb_assert (offset < 0);
if (start - low > -offset && start - low > 16384
&& ui_out_is_mi_like_p (uiout))
{
*result = start;
return 1;
}
if (peeklimit < 0 || peeklimit > (start - low))
peeklimit = start - low;
addrs = (CORE_ADDR *) xmalloc (peeklimit * sizeof (CORE_ADDR));
cleanup = make_cleanup (xfree, addrs);
gdb_assert (funclimit);
cur = low;
index = 0;
for (;;)
{
if (cur >= start)
break;
if (index >= peeklimit)
break;
gdb_assert (index < peeklimit);
addrs[index++] = cur;
cur += TARGET_PRINT_INSN (cur, &di);
}
if (cur == start)
{
gdb_assert (funclimit);
gdb_assert (offset < 0);
if (index < -offset)
{
*result = low;
do_cleanups (cleanup);
return 1;
}
else
{
*result = addrs[index + offset];
do_cleanups (cleanup);
return 0;
}
}
if (cur > start)
{
do_cleanups (cleanup);
return -1;
}
if (index >= peeklimit)
{
do_cleanups (cleanup);
return -1;
}
internal_error (__FILE__, __LINE__, "should never have reached here");
do_cleanups (cleanup);
return -1;
}
int
gdb_print_insn (CORE_ADDR memaddr, struct ui_file *stream)
{
struct disassemble_info di = gdb_disassemble_info (current_gdbarch, stream);
return TARGET_PRINT_INSN (memaddr, &di);
}