/* Disassemble support for GDB.
- Copyright (C) 2000-2015 Free Software Foundation, Inc.
+ Copyright (C) 2000-2017 Free Software Foundation, Inc.
This file is part of GDB.
#include "gdbcore.h"
#include "dis-asm.h"
#include "source.h"
+#include <algorithm>
/* Disassemble functions.
FIXME: We should get rid of all the duplicate code in gdb that does
xfree, xcalloc, xfree);
}
-/* Add DLE to TABLE.
- Returns 1 if added, 0 if already present. */
+/* Add a new dis_line_entry containing SYMTAB and LINE to TABLE. */
static void
-maybe_add_dis_line_entry (htab_t table, struct symtab *symtab, int line)
+add_dis_line_entry (htab_t table, struct symtab *symtab, int line)
{
void **slot;
struct dis_line_entry dle, *dlep;
return htab_find (table, &dle) != NULL;
}
-/* Like target_read_memory, but slightly different parameters. */
-static int
-dis_asm_read_memory (bfd_vma memaddr, gdb_byte *myaddr, unsigned int len,
- struct disassemble_info *info)
+/* Wrapper of target_read_code. */
+
+int
+gdb_disassembler::dis_asm_read_memory (bfd_vma memaddr, gdb_byte *myaddr,
+ unsigned int len,
+ struct disassemble_info *info)
{
return target_read_code (memaddr, myaddr, len);
}
-/* Like memory_error with slightly different parameters. */
-static void
-dis_asm_memory_error (int err, bfd_vma memaddr,
- struct disassemble_info *info)
+/* Wrapper of memory_error. */
+
+void
+gdb_disassembler::dis_asm_memory_error (int err, bfd_vma memaddr,
+ struct disassemble_info *info)
{
memory_error (TARGET_XFER_E_IO, memaddr);
}
-/* Like print_address with slightly different parameters. */
-static void
-dis_asm_print_address (bfd_vma addr, struct disassemble_info *info)
+/* Wrapper of print_address. */
+
+void
+gdb_disassembler::dis_asm_print_address (bfd_vma addr,
+ struct disassemble_info *info)
{
- struct gdbarch *gdbarch = (struct gdbarch *) info->application_data;
+ gdb_disassembler *self
+ = static_cast<gdb_disassembler *>(info->application_data);
- print_address (gdbarch, addr, (struct ui_file *) info->stream);
+ print_address (self->arch (), addr, self->stream ());
}
static int
return val;
}
-static int
-dump_insns (struct gdbarch *gdbarch, struct ui_out *uiout,
- struct disassemble_info * di,
- CORE_ADDR low, CORE_ADDR high,
- int how_many, int flags, struct ui_file *stb,
- CORE_ADDR *end_pc)
-{
- int num_displayed = 0;
- CORE_ADDR pc;
+/* See disasm.h. */
+int
+gdb_disassembler::pretty_print_insn (struct ui_out *uiout,
+ const struct disasm_insn *insn,
+ int flags)
+{
/* parts of the symbolic representation of the address */
int unmapped;
int offset;
int line;
+ int size;
struct cleanup *ui_out_chain;
+ char *filename = NULL;
+ char *name = NULL;
+ CORE_ADDR pc;
+ struct ui_file *stb = stream ();
+ struct gdbarch *gdbarch = arch ();
- for (pc = low; pc < high;)
+ ui_out_chain = make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
+ pc = insn->addr;
+
+ if (insn->number != 0)
{
- char *filename = NULL;
- char *name = NULL;
+ uiout->field_fmt ("insn-number", "%u", insn->number);
+ uiout->text ("\t");
+ }
- QUIT;
- if (how_many >= 0)
+ if ((flags & DISASSEMBLY_SPECULATIVE) != 0)
+ {
+ if (insn->is_speculative)
{
- if (num_displayed >= how_many)
- break;
+ uiout->field_string ("is-speculative", "?");
+
+ /* The speculative execution indication overwrites the first
+ character of the PC prefix.
+ We assume a PC prefix length of 3 characters. */
+ if ((flags & DISASSEMBLY_OMIT_PC) == 0)
+ uiout->text (pc_prefix (pc) + 1);
else
- num_displayed++;
+ uiout->text (" ");
}
- ui_out_chain = make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
+ else if ((flags & DISASSEMBLY_OMIT_PC) == 0)
+ uiout->text (pc_prefix (pc));
+ else
+ uiout->text (" ");
+ }
+ else if ((flags & DISASSEMBLY_OMIT_PC) == 0)
+ uiout->text (pc_prefix (pc));
+ uiout->field_core_addr ("address", gdbarch, pc);
+
+ if (!build_address_symbolic (gdbarch, pc, 0, &name, &offset, &filename,
+ &line, &unmapped))
+ {
+ /* We don't care now about line, filename and unmapped. But we might in
+ the future. */
+ uiout->text (" <");
+ if ((flags & DISASSEMBLY_OMIT_FNAME) == 0)
+ uiout->field_string ("func-name", name);
+ uiout->text ("+");
+ uiout->field_int ("offset", offset);
+ uiout->text (">:\t");
+ }
+ else
+ uiout->text (":\t");
+
+ if (filename != NULL)
+ xfree (filename);
+ if (name != NULL)
+ xfree (name);
+
+ ui_file_rewind (stb);
+ if (flags & DISASSEMBLY_RAW_INSN)
+ {
+ CORE_ADDR end_pc;
+ bfd_byte data;
+ int err;
+ const char *spacer = "";
+
+ /* Build the opcodes using a temporary stream so we can
+ write them out in a single go for the MI. */
+ struct ui_file *opcode_stream = mem_fileopen ();
+ struct cleanup *cleanups =
+ make_cleanup_ui_file_delete (opcode_stream);
- if ((flags & DISASSEMBLY_OMIT_PC) == 0)
- ui_out_text (uiout, pc_prefix (pc));
- ui_out_field_core_addr (uiout, "address", gdbarch, pc);
+ size = print_insn (pc);
+ end_pc = pc + size;
- if (!build_address_symbolic (gdbarch, pc, 0, &name, &offset, &filename,
- &line, &unmapped))
+ for (;pc < end_pc; ++pc)
{
- /* We don't care now about line, filename and
- unmapped. But we might in the future. */
- ui_out_text (uiout, " <");
- if ((flags & DISASSEMBLY_OMIT_FNAME) == 0)
- ui_out_field_string (uiout, "func-name", name);
- ui_out_text (uiout, "+");
- ui_out_field_int (uiout, "offset", offset);
- ui_out_text (uiout, ">:\t");
+ err = m_di.read_memory_func (pc, &data, 1, &m_di);
+ if (err != 0)
+ m_di.memory_error_func (err, pc, &m_di);
+ fprintf_filtered (opcode_stream, "%s%02x",
+ spacer, (unsigned) data);
+ spacer = " ";
}
- else
- ui_out_text (uiout, ":\t");
-
- if (filename != NULL)
- xfree (filename);
- if (name != NULL)
- xfree (name);
-
- ui_file_rewind (stb);
- if (flags & DISASSEMBLY_RAW_INSN)
- {
- CORE_ADDR old_pc = pc;
- bfd_byte data;
- int err;
- const char *spacer = "";
-
- /* Build the opcodes using a temporary stream so we can
- write them out in a single go for the MI. */
- struct ui_file *opcode_stream = mem_fileopen ();
- struct cleanup *cleanups =
- make_cleanup_ui_file_delete (opcode_stream);
-
- pc += gdbarch_print_insn (gdbarch, pc, di);
- for (;old_pc < pc; old_pc++)
- {
- err = (*di->read_memory_func) (old_pc, &data, 1, di);
- if (err != 0)
- (*di->memory_error_func) (err, old_pc, di);
- fprintf_filtered (opcode_stream, "%s%02x",
- spacer, (unsigned) data);
- spacer = " ";
- }
- ui_out_field_stream (uiout, "opcodes", opcode_stream);
- ui_out_text (uiout, "\t");
-
- do_cleanups (cleanups);
- }
- else
- pc += gdbarch_print_insn (gdbarch, pc, di);
- ui_out_field_stream (uiout, "inst", stb);
- ui_file_rewind (stb);
- do_cleanups (ui_out_chain);
- ui_out_text (uiout, "\n");
+
+ uiout->field_stream ("opcodes", opcode_stream);
+ uiout->text ("\t");
+
+ do_cleanups (cleanups);
+ }
+ else
+ size = print_insn (pc);
+
+ uiout->field_stream ("inst", stb);
+ ui_file_rewind (stb);
+ do_cleanups (ui_out_chain);
+ uiout->text ("\n");
+
+ return size;
+}
+
+static int
+dump_insns (struct ui_out *uiout, gdb_disassembler *di,
+ CORE_ADDR low, CORE_ADDR high,
+ int how_many, int flags,
+ CORE_ADDR *end_pc)
+{
+ struct disasm_insn insn;
+ int num_displayed = 0;
+
+ memset (&insn, 0, sizeof (insn));
+ insn.addr = low;
+
+ while (insn.addr < high && (how_many < 0 || num_displayed < how_many))
+ {
+ int size;
+
+ size = di->pretty_print_insn (uiout, &insn, flags);
+ if (size <= 0)
+ break;
+
+ ++num_displayed;
+ insn.addr += size;
+
+ /* Allow user to bail out with ^C. */
+ QUIT;
}
if (end_pc != NULL)
- *end_pc = pc;
+ *end_pc = insn.addr;
+
return num_displayed;
}
static void
do_mixed_source_and_assembly_deprecated
- (struct gdbarch *gdbarch, struct ui_out *uiout,
- struct disassemble_info *di, struct symtab *symtab,
+ (struct ui_out *uiout,
+ gdb_disassembler *di, struct symtab *symtab,
CORE_ADDR low, CORE_ADDR high,
- int how_many, int flags, struct ui_file *stb)
+ int how_many, int flags)
{
int newlines = 0;
int nlines;
int out_of_order = 0;
int next_line = 0;
int num_displayed = 0;
- enum print_source_lines_flags psl_flags = 0;
+ print_source_lines_flags psl_flags = 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);
= make_cleanup_ui_out_list_begin_end (uiout, "line_asm_insn");
}
- num_displayed += dump_insns (gdbarch, uiout, di,
+ num_displayed += dump_insns (uiout, di,
mle[i].start_pc, mle[i].end_pc,
- how_many, flags, stb, NULL);
+ how_many, flags, NULL);
/* When we've reached the end of the mle array, or we've seen the last
assembly range for this source line, close out the list/tuple. */
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");
+ uiout->text ("\n");
}
if (how_many >= 0 && num_displayed >= how_many)
break;
immediately following. */
static void
-do_mixed_source_and_assembly (struct gdbarch *gdbarch, struct ui_out *uiout,
- struct disassemble_info *di,
+do_mixed_source_and_assembly (struct gdbarch *gdbarch,
+ struct ui_out *uiout,
+ gdb_disassembler *di,
struct symtab *main_symtab,
CORE_ADDR low, CORE_ADDR high,
- int how_many, int flags, struct ui_file *stb)
+ int how_many, int flags)
{
- int newlines = 0;
const struct linetable_entry *le, *first_le;
- struct symtab_and_line sal;
int i, nlines;
- int out_of_order = 0;
- int next_line = 0;
int num_displayed = 0;
- enum print_source_lines_flags psl_flags = 0;
- struct cleanup *cleanups;
+ print_source_lines_flags psl_flags = 0;
struct cleanup *ui_out_chain;
struct cleanup *ui_out_tuple_chain;
struct cleanup *ui_out_list_chain;
CORE_ADDR pc;
struct symtab *last_symtab;
int last_line;
- htab_t dis_line_table;
gdb_assert (main_symtab != NULL && SYMTAB_LINETABLE (main_symtab) != NULL);
but if that text is for code that will be disassembled later, then
we'll want to defer printing it until later with its associated code. */
- dis_line_table = allocate_dis_line_table ();
- cleanups = make_cleanup_htab_delete (dis_line_table);
+ htab_up dis_line_table (allocate_dis_line_table ());
pc = low;
pc += length;
if (sal.symtab != NULL)
- maybe_add_dis_line_entry (dis_line_table, sal.symtab, sal.line);
+ add_dis_line_entry (dis_line_table.get (), sal.symtab, sal.line);
}
/* Second pass: print the disassembly.
which is where we put file name and source line contents output.
Cleanup usage:
- cleanups:
- For things created at the beginning of this function and need to be
- kept until the end of this function.
ui_out_chain
Handles the outer "asm_insns" list.
ui_out_tuple_chain
while (pc < high)
{
- struct linetable_entry *le = NULL;
struct symtab_and_line sal;
CORE_ADDR end_pc;
int start_preceding_line_to_display = 0;
not associated with code that we'll print later. */
for (l = sal.line - 1; l > last_line; --l)
{
- if (line_has_code_p (dis_line_table, sal.symtab, l))
+ if (line_has_code_p (dis_line_table.get (),
+ sal.symtab, l))
break;
}
if (l < sal.line - 1)
{
/* Skip the newline if this is the first instruction. */
if (pc > low)
- ui_out_text (uiout, "\n");
+ uiout->text ("\n");
if (ui_out_tuple_chain != NULL)
{
gdb_assert (ui_out_list_chain != NULL);
output includes the source specs for each line. */
if (sal.symtab != NULL)
{
- ui_out_text (uiout,
- symtab_to_filename_for_display (sal.symtab));
+ uiout->text (symtab_to_filename_for_display (sal.symtab));
}
else
- ui_out_text (uiout, "unknown");
- ui_out_text (uiout, ":\n");
+ uiout->text ("unknown");
+ uiout->text (":\n");
}
if (start_preceding_line_to_display > 0)
{
if (sal.symtab != NULL)
print_source_lines (sal.symtab, sal.line, sal.line + 1, psl_flags);
else
- ui_out_text (uiout, _("--- no source info for this pc ---\n"));
+ uiout->text (_("--- no source info for this pc ---\n"));
ui_out_list_chain
= make_cleanup_ui_out_list_begin_end (uiout, "line_asm_insn");
}
}
if (sal.end != 0)
- end_pc = min (sal.end, high);
+ end_pc = std::min (sal.end, high);
else
end_pc = pc + 1;
- num_displayed += dump_insns (gdbarch, uiout, di, pc, end_pc,
- how_many, flags, stb, &end_pc);
+ num_displayed += dump_insns (uiout, di, pc, end_pc,
+ how_many, flags, &end_pc);
pc = end_pc;
if (how_many >= 0 && num_displayed >= how_many)
}
do_cleanups (ui_out_chain);
- do_cleanups (cleanups);
}
static void
-do_assembly_only (struct gdbarch *gdbarch, struct ui_out *uiout,
- struct disassemble_info * di,
+do_assembly_only (struct ui_out *uiout,
+ gdb_disassembler *di,
CORE_ADDR low, CORE_ADDR high,
- int how_many, int flags, struct ui_file *stb)
+ int how_many, int flags)
{
- 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 (gdbarch, uiout, di, low, high, how_many,
- flags, stb, NULL);
+ dump_insns (uiout, di, low, high, how_many, flags, NULL);
do_cleanups (ui_out_chain);
}
return 0;
}
-struct disassemble_info
-gdb_disassemble_info (struct gdbarch *gdbarch, struct ui_file *file)
+gdb_disassembler::gdb_disassembler (struct gdbarch *gdbarch,
+ struct ui_file *file,
+ di_read_memory_ftype read_memory_func)
+ : m_gdbarch (gdbarch)
{
- 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;
+ init_disassemble_info (&m_di, file, fprintf_disasm);
+ m_di.flavour = bfd_target_unknown_flavour;
+ m_di.memory_error_func = dis_asm_memory_error;
+ m_di.print_address_func = dis_asm_print_address;
/* NOTE: cagney/2003-04-28: The original code, from the old Insight
disassembler had a local optomization here. By default it would
access the executable file, instead of the target memory (there
didn't work as they relied on the access going to the target.
Further, it has been supperseeded by trust-read-only-sections
(although that should be superseeded by target_trust..._p()). */
- 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);
- di.endian_code = gdbarch_byte_order_for_code (gdbarch);
- di.application_data = gdbarch;
- disassemble_init_for_target (&di);
- return di;
+ m_di.read_memory_func = read_memory_func;
+ m_di.arch = gdbarch_bfd_arch_info (gdbarch)->arch;
+ m_di.mach = gdbarch_bfd_arch_info (gdbarch)->mach;
+ m_di.endian = gdbarch_byte_order (gdbarch);
+ m_di.endian_code = gdbarch_byte_order_for_code (gdbarch);
+ m_di.application_data = this;
+ disassemble_init_for_target (&m_di);
+}
+
+int
+gdb_disassembler::print_insn (CORE_ADDR memaddr,
+ int *branch_delay_insns)
+{
+ int length = gdbarch_print_insn (arch (), memaddr, &m_di);
+
+ if (branch_delay_insns != NULL)
+ {
+ if (m_di.insn_info_valid)
+ *branch_delay_insns = m_di.branch_delay_insns;
+ else
+ *branch_delay_insns = 0;
+ }
+ return length;
}
void
{
struct ui_file *stb = mem_fileopen ();
struct cleanup *cleanups = make_cleanup_ui_file_delete (stb);
- struct disassemble_info di = gdb_disassemble_info (gdbarch, stb);
+ gdb_disassembler di (gdbarch, stb);
struct symtab *symtab;
- struct linetable_entry *le = NULL;
int nlines = -1;
/* Assume symtab is valid for whole PC range. */
if (!(flags & (DISASSEMBLY_SOURCE_DEPRECATED | DISASSEMBLY_SOURCE))
|| nlines <= 0)
- do_assembly_only (gdbarch, uiout, &di, low, high, how_many, flags, stb);
+ do_assembly_only (uiout, &di, low, high, how_many, flags);
else if (flags & DISASSEMBLY_SOURCE)
do_mixed_source_and_assembly (gdbarch, uiout, &di, symtab, low, high,
- how_many, flags, stb);
+ how_many, flags);
else if (flags & DISASSEMBLY_SOURCE_DEPRECATED)
- do_mixed_source_and_assembly_deprecated (gdbarch, uiout, &di, symtab,
- low, high, how_many, flags, stb);
+ do_mixed_source_and_assembly_deprecated (uiout, &di, symtab,
+ low, high, how_many, flags);
do_cleanups (cleanups);
gdb_flush (gdb_stdout);
gdb_print_insn (struct gdbarch *gdbarch, CORE_ADDR memaddr,
struct ui_file *stream, int *branch_delay_insns)
{
- struct disassemble_info di;
- int length;
- di = gdb_disassemble_info (gdbarch, stream);
- length = gdbarch_print_insn (gdbarch, memaddr, &di);
- if (branch_delay_insns)
- {
- if (di.insn_info_valid)
- *branch_delay_insns = di.branch_delay_insns;
- else
- *branch_delay_insns = 0;
- }
- return length;
-}
+ gdb_disassembler di (gdbarch, stream);
-static void
-do_ui_file_delete (void *arg)
-{
- ui_file_delete ((struct ui_file *) arg);
+ return di.print_insn (memaddr, branch_delay_insns);
}
/* Return the length in bytes of the instruction at address MEMADDR in
int
gdb_insn_length (struct gdbarch *gdbarch, CORE_ADDR addr)
{
- static struct ui_file *null_stream = NULL;
-
- /* Dummy file descriptor for the disassembler. */
- if (!null_stream)
- {
- null_stream = ui_file_new ();
- make_final_cleanup (do_ui_file_delete, null_stream);
- }
-
- return gdb_print_insn (gdbarch, addr, null_stream, NULL);
+ return gdb_print_insn (gdbarch, addr, null_stream (), NULL);
}
/* fprintf-function for gdb_buffered_insn_length. This function is a