gdb: add new base class to gdb_disassembler

The motivation for this change is an upcoming Python disassembler API
that I would like to add.  As part of that change I need to create a
new disassembler like class that contains a disassemble_info and a
gdbarch.  The management of these two objects is identical to how we
manage these objects within gdb_disassembler, so it might be tempting
for my new class to inherit from gdb_disassembler.

The problem however, is that gdb_disassembler has a tight connection
between its constructor, and its print_insn method.  In the
constructor the ui_file* that is passed in is replaced with a member
variable string_file*, and then in print_insn, the contents of the
member variable string_file are printed to the original ui_file*.

What this means is that the gdb_disassembler class has a tight
coupling between its constructor and print_insn; the class just isn't
intended to be used in a situation where print_insn is not going to be
called, which is how my (upcoming) sub-class would need to operate.

My solution then, is to separate out the management of the
disassemble_info and gdbarch into a new gdb_disassemble_info class,
and make this class a parent of gdb_disassembler.

In arm-tdep.c and mips-tdep.c, where we used to cast the
disassemble_info->application_data to a gdb_disassembler, we can now
cast to a gdb_disassemble_info as we only need to access the gdbarch
information.

Now, my new Python disassembler sub-class will still want to print
things to an output stream, and so we will want access to the
dis_asm_fprintf functionality for printing.

However, rather than move this printing code into the
gdb_disassemble_info base class, I have added yet another level of
hierarchy, a gdb_printing_disassembler, thus the class structure is
now:

  struct gdb_disassemble_info {};
  struct gdb_printing_disassembler : public gdb_disassemble_info {};
  struct gdb_disassembler : public gdb_printing_disassembler {};

In a later commit my new Python disassembler will inherit from
gdb_printing_disassembler.

The reason for adding the additional layer to the class hierarchy is
that in yet another commit I intend to rewrite the function
gdb_buffered_insn_length, and to do this I will be creating yet more
disassembler like classes, however, these will not print anything,
thus I will add a gdb_non_printing_disassembler class that also
inherits from gdb_disassemble_info.  Knowing that that change is
coming, I've gone with the above class hierarchy now.

There should be no user visible changes after this commit.
This commit is contained in:
Andrew Burgess
2021-10-27 10:07:56 +01:00
committed by Andrew Burgess
parent 8a0b60471a
commit f0c2e3e020
4 changed files with 156 additions and 54 deletions

View File

@@ -26,43 +26,137 @@ struct gdbarch;
struct ui_out;
struct ui_file;
class gdb_disassembler
/* A wrapper around a disassemble_info and a gdbarch. This is the core
set of data that all disassembler sub-classes will need. This class
doesn't actually implement the disassembling process, that is something
that sub-classes will do, with each sub-class doing things slightly
differently.
The constructor of this class is protected, you should not create
instances of this class directly, instead create an instance of an
appropriate sub-class. */
struct gdb_disassemble_info
{
using di_read_memory_ftype = decltype (disassemble_info::read_memory_func);
DISABLE_COPY_AND_ASSIGN (gdb_disassemble_info);
public:
gdb_disassembler (struct gdbarch *gdbarch, struct ui_file *file)
: gdb_disassembler (gdbarch, file, dis_asm_read_memory)
{}
~gdb_disassembler ();
DISABLE_COPY_AND_ASSIGN (gdb_disassembler);
int print_insn (CORE_ADDR memaddr, int *branch_delay_insns = NULL);
/* Return the gdbarch of gdb_disassembler. */
/* Return the gdbarch we are disassembling for. */
struct gdbarch *arch ()
{ return m_gdbarch; }
protected:
gdb_disassembler (struct gdbarch *gdbarch, struct ui_file *file,
di_read_memory_ftype func);
/* Return a pointer to the disassemble_info, this will be needed for
passing into the libopcodes disassembler. */
struct disassemble_info *disasm_info ()
{ return &m_di; }
protected:
/* Types for the function callbacks within m_di. */
using read_memory_ftype = decltype (disassemble_info::read_memory_func);
using memory_error_ftype = decltype (disassemble_info::memory_error_func);
using print_address_ftype = decltype (disassemble_info::print_address_func);
using fprintf_ftype = decltype (disassemble_info::fprintf_func);
using fprintf_styled_ftype = decltype (disassemble_info::fprintf_styled_func);
/* Constructor, many fields in m_di are initialized from GDBARCH. STREAM
is where the output of the disassembler will be written too, the
remaining arguments are function callbacks that are written into
m_di. Of these function callbacks FPRINTF_FUNC and
FPRINTF_STYLED_FUNC must not be nullptr. If READ_MEMORY_FUNC,
MEMORY_ERROR_FUNC, or PRINT_ADDRESS_FUNC are nullptr, then that field
within m_di is left with its default value (see the libopcodes
function init_disassemble_info for the defaults). */
gdb_disassemble_info (struct gdbarch *gdbarch,
struct ui_file *stream,
read_memory_ftype read_memory_func,
memory_error_ftype memory_error_func,
print_address_ftype print_address_func,
fprintf_ftype fprintf_func,
fprintf_styled_ftype fprintf_styled_func);
/* Destructor. */
virtual ~gdb_disassemble_info ();
/* The stream that disassembler output is being written too. */
struct ui_file *stream ()
{ return (struct ui_file *) m_di.stream; }
private:
struct gdbarch *m_gdbarch;
/* Stores data required for disassembling instructions in
opcodes. */
struct disassemble_info m_di;
private:
/* The architecture we are disassembling for. */
struct gdbarch *m_gdbarch;
/* If we own the string in `m_di.disassembler_options', we do so
using this field. */
std::string m_disassembler_options_holder;
};
/* A wrapper around gdb_disassemble_info. This class adds default
print functions that are supplied to the disassemble_info within the
parent class. These default print functions write to the stream, which
is also contained in the parent class.
As with the parent class, the constructor for this class is protected,
you should not create instances of this class, but create an
appropriate sub-class instead. */
struct gdb_printing_disassembler : public gdb_disassemble_info
{
DISABLE_COPY_AND_ASSIGN (gdb_printing_disassembler);
protected:
/* Constructor. All the arguments are just passed to the parent class.
We also add the two print functions to the arguments passed to the
parent. See gdb_disassemble_info for a description of how the
arguments are handled. */
gdb_printing_disassembler (struct gdbarch *gdbarch,
struct ui_file *stream,
read_memory_ftype read_memory_func,
memory_error_ftype memory_error_func,
print_address_ftype print_address_func)
: gdb_disassemble_info (gdbarch, stream, read_memory_func,
memory_error_func, print_address_func,
fprintf_func, fprintf_styled_func)
{ /* Nothing. */ }
/* Callback used as the disassemble_info's fprintf_func callback, this
writes to STREAM, which will be m_di.stream. */
static int fprintf_func (void *stream, const char *format, ...)
ATTRIBUTE_PRINTF(2,3);
/* Callback used as the disassemble_info's fprintf_styled_func callback,
this writes to STREAM, which will be m_di.stream. */
static int fprintf_styled_func (void *stream,
enum disassembler_style style,
const char *format, ...)
ATTRIBUTE_PRINTF(3,4);
};
/* A dissassembler class that provides 'print_insn', a method for
disassembling a single instruction to the output stream. */
struct gdb_disassembler : public gdb_printing_disassembler
{
gdb_disassembler (struct gdbarch *gdbarch, struct ui_file *file)
: gdb_disassembler (gdbarch, file, dis_asm_read_memory)
{ /* Nothing. */ }
DISABLE_COPY_AND_ASSIGN (gdb_disassembler);
/* Disassemble a single instruction at MEMADDR to the ui_file* that was
passed to the constructor. If a memory error occurs while
disassembling this instruction then an error will be thrown. */
int print_insn (CORE_ADDR memaddr, int *branch_delay_insns = NULL);
protected:
gdb_disassembler (struct gdbarch *gdbarch, struct ui_file *file,
read_memory_ftype func);
private:
/* This member variable is given a value by calling dis_asm_memory_error.
If after calling into the libopcodes disassembler we get back a
negative value (which indicates an error), then, if this variable has
@@ -95,16 +189,6 @@ private:
(currently just to addresses and symbols) as it goes. */
static bool use_ext_lang_colorization_p;
static int dis_asm_fprintf (void *stream, const char *format, ...)
ATTRIBUTE_PRINTF(2,3);
/* Print formatted message to STREAM, the content can be styled based on
STYLE if desired. */
static int dis_asm_styled_fprintf (void *stream,
enum disassembler_style style,
const char *format, ...)
ATTRIBUTE_PRINTF(3,4);
static int dis_asm_read_memory (bfd_vma memaddr, gdb_byte *myaddr,
unsigned int len,
struct disassemble_info *info);