/* Disassemble support for GDB.
- Copyright (C) 2000-2020 Free Software Foundation, Inc.
+ Copyright (C) 2000-2022 Free Software Foundation, Inc.
This file is part of GDB.
/* This variable is used to hold the prospective disassembler_options value
which is set by the "set disassembler_options" command. */
-static char *prospective_options = NULL;
+static std::string prospective_options;
/* This structure is used to store line number information for the
deprecated /m option.
gdb_disassembler *self
= static_cast<gdb_disassembler *>(info->application_data);
- self->m_err_memaddr = memaddr;
+ self->m_err_memaddr.emplace (memaddr);
}
/* Wrapper of print_address. */
print_address (self->arch (), addr, self->stream ());
}
+/* Format disassembler output to STREAM. */
+
+int
+gdb_disassembler::dis_asm_fprintf (void *stream, const char *format, ...)
+{
+ va_list args;
+
+ va_start (args, format);
+ vfprintf_filtered ((struct ui_file *) stream, format, args);
+ va_end (args);
+ /* Something non -ve. */
+ return 0;
+}
+
static bool
line_is_less_than (const deprecated_dis_line_entry &mle1,
const deprecated_dis_line_entry &mle2)
if (mle1.start_pc != mle2.start_pc)
val = mle1.start_pc < mle2.start_pc;
else
- val = mle1.line < mle2.line;
+ val = mle1.line < mle2.line;
}
else
{
if (mle1.line != mle2.line)
val = mle1.line < mle2.line;
else
- val = mle1.start_pc < mle2.start_pc;
+ val = mle1.start_pc < mle2.start_pc;
}
return val;
}
std::string name, filename;
bool omit_fname = ((flags & DISASSEMBLY_OMIT_FNAME) != 0);
if (!build_address_symbolic (gdbarch, pc, false, omit_fname, &name,
- &offset, &filename, &line, &unmapped))
+ &offset, &filename, &line, &unmapped))
{
/* We don't care now about line, filename and unmapped. But we might in
the future. */
m_uiout->text (" <");
if (!omit_fname)
- m_uiout->field_string ("func-name", name.c_str (),
+ m_uiout->field_string ("func-name", name,
function_name_style.style ());
/* For negative offsets, avoid displaying them as +-N; the sign of
the offset takes the place of the "+" here. */
else
m_uiout->text (":\t");
+ /* Clear the buffer into which we will disassemble the instruction. */
m_insn_stb.clear ();
+ /* A helper function to write the M_INSN_STB buffer, followed by a
+ newline. This can be called in a couple of situations. */
+ auto write_out_insn_buffer = [&] ()
+ {
+ m_uiout->field_stream ("inst", m_insn_stb);
+ m_uiout->text ("\n");
+ };
+
+ try
+ {
+ /* Now we can disassemble the instruction. If the disassembler
+ returns a negative value this indicates an error and is handled
+ within the print_insn call, resulting in an exception being
+ thrown. Returning zero makes no sense, as this indicates we
+ disassembled something successfully, but it was something of no
+ size? */
+ size = m_di.print_insn (pc);
+ gdb_assert (size > 0);
+ }
+ catch (const gdb_exception &ex)
+ {
+ /* An exception was thrown while disassembling the instruction.
+ However, the disassembler might still have written something
+ out, so ensure that we flush the instruction buffer before
+ rethrowing the exception. We can't perform this write from an
+ object destructor as the write itself might throw an exception
+ if the pager kicks in, and the user selects quit. */
+ write_out_insn_buffer ();
+ throw ex;
+ }
+
if (flags & DISASSEMBLY_RAW_INSN)
{
CORE_ADDR end_pc;
write them out in a single go for the MI. */
m_opcode_stb.clear ();
- size = m_di.print_insn (pc);
end_pc = pc + size;
for (;pc < end_pc; ++pc)
m_uiout->field_stream ("opcodes", m_opcode_stb);
m_uiout->text ("\t");
}
- else
- size = m_di.print_insn (pc);
- m_uiout->field_stream ("inst", m_insn_stb);
+ /* Disassembly was a success, write out the instruction buffer. */
+ write_out_insn_buffer ();
}
- m_uiout->text ("\n");
return size;
}
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. */
+ assembly range for this source line, close out the list/tuple. */
if (i == (newlines - 1) || mle[i + 1].line > mle[i].line)
{
inner_list_emitter.reset ();
dump_insns (gdbarch, uiout, low, high, how_many, flags, NULL);
}
-/* Initialize the disassemble info struct ready for the specified
- stream. */
-
-static int ATTRIBUTE_PRINTF (2, 3)
-fprintf_disasm (void *stream, const char *format, ...)
-{
- va_list args;
-
- va_start (args, format);
- vfprintf_filtered ((struct ui_file *) stream, format, args);
- va_end (args);
- /* Something non -ve. */
- return 0;
-}
-
/* Combine implicit and user disassembler options and return them
in a newly-created string. */
gdb_disassembler::gdb_disassembler (struct gdbarch *gdbarch,
struct ui_file *file,
di_read_memory_ftype read_memory_func)
- : m_gdbarch (gdbarch),
- m_err_memaddr (0)
+ : m_gdbarch (gdbarch)
{
- init_disassemble_info (&m_di, file, fprintf_disasm);
+ init_disassemble_info (&m_di, file, dis_asm_fprintf);
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;
disassemble_init_for_target (&m_di);
}
+gdb_disassembler::~gdb_disassembler ()
+{
+ disassemble_free_target (&m_di);
+}
+
int
gdb_disassembler::print_insn (CORE_ADDR memaddr,
int *branch_delay_insns)
{
- m_err_memaddr = 0;
+ m_err_memaddr.reset ();
int length = gdbarch_print_insn (arch (), memaddr, &m_di);
if (length < 0)
- memory_error (TARGET_XFER_E_IO, m_err_memaddr);
+ {
+ if (m_err_memaddr.has_value ())
+ memory_error (TARGET_XFER_E_IO, *m_err_memaddr);
+ else
+ error (_("unknown disassembler error (error = %d)"), length);
+ }
if (branch_delay_insns != NULL)
{
gdb_buffered_insn_length_init_dis (gdbarch, &di, insn, max_len, addr,
&disassembler_options_holder);
- return gdbarch_print_insn (gdbarch, addr, &di);
+ int result = gdbarch_print_insn (gdbarch, addr, &di);
+ disassemble_free_target (&di);
+ return result;
}
char *
}
void
-set_disassembler_options (char *prospective_options)
+set_disassembler_options (const char *prospective_options)
{
struct gdbarch *gdbarch = get_current_arch ();
char **disassembler_options = gdbarch_disassembler_options (gdbarch);
const disasm_options_and_args_t *valid_options_and_args;
const disasm_options_t *valid_options;
- char *options = remove_whitespace_and_extra_commas (prospective_options);
+ gdb::unique_xmalloc_ptr<char> prospective_options_local
+ = make_unique_xstrdup (prospective_options);
+ char *options = remove_whitespace_and_extra_commas
+ (prospective_options_local.get ());
const char *opt;
/* Allow all architectures, even ones that do not support 'set disassembler',
set_disassembler_options_sfunc (const char *args, int from_tty,
struct cmd_list_element *c)
{
- set_disassembler_options (prospective_options);
+ set_disassembler_options (prospective_options.c_str ());
}
static void
/* Initialization code. */
+void _initialize_disasm ();
void
-_initialize_disasm (void)
+_initialize_disasm ()
{
- struct cmd_list_element *cmd;
-
/* Add the command that controls the disassembler options. */
- cmd = add_setshow_string_noescape_cmd ("disassembler-options", no_class,
- &prospective_options, _("\
+ set_show_commands set_show_disas_opts
+ = add_setshow_string_noescape_cmd ("disassembler-options", no_class,
+ &prospective_options, _("\
Set the disassembler options.\n\
Usage: set disassembler-options OPTION [,OPTION]...\n\n\
See: 'show disassembler-options' for valid option values."), _("\
set_disassembler_options_sfunc,
show_disassembler_options_sfunc,
&setlist, &showlist);
- set_cmd_completer (cmd, disassembler_options_completer);
+ set_cmd_completer (set_show_disas_opts.set, disassembler_options_completer);
}