[4/5] gdb: add extension language print_insn hook

Message ID 150de80644ec955df5850ef74f6274ccc032a6f8.1634162144.git.andrew.burgess@embecosm.com
State New
Headers show
Series
  • Add Python API for the disassembler
Related show

Commit Message

Andrew Burgess Oct. 13, 2021, 9:59 p.m.
This commit is setup for the next commit.

In the next commit I plan to add a Python API to intercept the
print_insn calls within GDB, each print_insn call is responsible for
disassembling, and print one instruction.  After the next commit it
will be possible for a user to write Python code that either wraps
around the existing disassembler, or even, in extreme situations,
entirely replaces the existing disassembler.

This commit does not add any new Python API.

What this commit does is put the extension language framework in place
for a print_insn hook.  There's a new callback added to 'struct
extension_language_ops', which is then filled in with NULL for Python
and Guile.

Finally, in the disassembler, the code is restructured so that the new
extension language function ext_lang_print_insn is called before we
delegate to gdbarch_print_insn.

After this, the next commit can focus entirely on providing a Python
implementation of the new print_insn callback.

There should be no user visible change after this commit.
---
 gdb/disasm.c         | 27 +++++++++++++++++++++++++--
 gdb/extension-priv.h | 15 +++++++++++++++
 gdb/extension.c      | 20 ++++++++++++++++++++
 gdb/extension.h      | 17 +++++++++++++++++
 gdb/guile/guile.c    |  6 +++++-
 gdb/python/python.c  |  2 ++
 6 files changed, 84 insertions(+), 3 deletions(-)

-- 
2.25.4

Comments

Tom Tromey Oct. 20, 2021, 9:06 p.m. | #1
>>>>> "Andrew" == Andrew Burgess <andrew.burgess@embecosm.com> writes:


Andrew> What this commit does is put the extension language framework in place
Andrew> for a print_insn hook.  There's a new callback added to 'struct
Andrew> extension_language_ops', which is then filled in with NULL for Python
Andrew> and Guile.

This looks good to me, thanks.

Tom

Patch

diff --git a/gdb/disasm.c b/gdb/disasm.c
index c045dfc94a6..0c384c778f5 100644
--- a/gdb/disasm.c
+++ b/gdb/disasm.c
@@ -784,13 +784,36 @@  gdb_disassembler::~gdb_disassembler ()
   disassemble_free_target (&m_di);
 }
 
+/* Wrapper around calling gdbarch_print_insn.  This function takes care of
+   first calling the extension language hooks for print_insn, and, if none
+   of the extension languages can print this instruction, calls
+   gdbarch_print_insn to do the work.
+
+   GDBARCH is the architecture to disassemble in, VMA is the address of the
+   instruction being disassembled, and INFO is the libopcodes disassembler
+   related information.  */
+
+static int
+gdb_print_insn_1 (struct gdbarch *gdbarch, CORE_ADDR vma,
+		  struct disassemble_info *info)
+{
+  /* Call into the extension languages to do the disassembly.  */
+  gdb::optional<int> length = ext_lang_print_insn (gdbarch, vma, info);
+  if (length.has_value ())
+    return *length;
+
+  /* No extension language wanted to do the disassembly, so do it
+     manually.  */
+  return gdbarch_print_insn (gdbarch, vma, info);
+}
+
 int
 gdb_disassembler::print_insn (CORE_ADDR memaddr,
 			      int *branch_delay_insns)
 {
   m_err_memaddr.reset ();
 
-  int length = gdbarch_print_insn (arch (), memaddr, &m_di);
+  int length = gdb_print_insn_1 (arch (), memaddr, &m_di);
 
   if (length < 0)
     {
@@ -916,7 +939,7 @@  gdb_buffered_insn_length (struct gdbarch *gdbarch,
   gdb_buffered_insn_length_init_dis (gdbarch, &di, insn, max_len, addr,
 				     &disassembler_options_holder);
 
-  int result = gdbarch_print_insn (gdbarch, addr, &di);
+  int result = gdb_print_insn_1 (gdbarch, addr, &di);
   disassemble_free_target (&di);
   return result;
 }
diff --git a/gdb/extension-priv.h b/gdb/extension-priv.h
index 77f23e0f911..6c5cde12ffd 100644
--- a/gdb/extension-priv.h
+++ b/gdb/extension-priv.h
@@ -257,6 +257,21 @@  struct extension_language_ops
      or an empty option.  */
   gdb::optional<std::string> (*colorize) (const std::string &name,
 					  const std::string &contents);
+
+  /* Print a single instruction from ADDRESS in architecture GDBARCH.  INFO
+     is the standard libopcodes disassembler_info structure.  Bytes for the
+     instruction being printed should be read using INFO->read_memory_func
+     as the actual instruction bytes might be in a buffer.
+
+     Use INFO->fprintf_func to print the results of the disassembly, and
+     return the length of the instruction.
+
+     If no instruction can be disassembled then return an empty value and
+     other extension languages will get a chance to perform the
+     disassembly.  */
+  gdb::optional<int> (*print_insn) (struct gdbarch *gdbarch,
+				    CORE_ADDR address,
+				    struct disassemble_info *info);
 };
 
 /* State necessary to restore a signal handler to its previous value.  */
diff --git a/gdb/extension.c b/gdb/extension.c
index 27dce9befa0..9a002e425b1 100644
--- a/gdb/extension.c
+++ b/gdb/extension.c
@@ -893,6 +893,26 @@  ext_lang_colorize (const std::string &filename, const std::string &contents)
   return result;
 }
 
+/* See extension.h.  */
+
+gdb::optional<int>
+ext_lang_print_insn (struct gdbarch *gdbarch, CORE_ADDR address,
+		     struct disassemble_info *info)
+{
+  for (const struct extension_language_defn *extlang : extension_languages)
+    {
+      if (extlang->ops == nullptr
+	  || extlang->ops->print_insn == nullptr)
+	continue;
+      gdb::optional<int> length
+	(extlang->ops->print_insn (gdbarch, address, info));
+      if (length.has_value ())
+	return length;
+    }
+
+  return {};
+}
+
 /* Called via an observer before gdb prints its prompt.
    Iterate over the extension languages giving them a chance to
    change the prompt.  The first one to change the prompt wins,
diff --git a/gdb/extension.h b/gdb/extension.h
index 56f57560de3..fa292a6cb4f 100644
--- a/gdb/extension.h
+++ b/gdb/extension.h
@@ -319,4 +319,21 @@  extern void get_matching_xmethod_workers
 extern gdb::optional<std::string> ext_lang_colorize
   (const std::string &filename, const std::string &contents);
 
+/* Try to disassemble a single instruction.  ADDRESS is the address that
+   the instructions apparent address, though bytes for the instruction
+   should be read by calling INFO->read_memory_func as we might be
+   disassembling out of a buffer.  GDBARCH is the architecture in which we
+   are performing the disassembly.
+
+   The disassembled instruction should be printed by calling
+   INFO->fprintf_func, and the length (in octets) of the disassembled
+   instruction should be returned.
+
+   If no instruction could be disassembled then an empty value is returned
+   and GDB will call gdbarch_print_insn to perform the disassembly
+   itself.  */
+
+extern gdb::optional<int> ext_lang_print_insn
+  (struct gdbarch *gdbarch, CORE_ADDR address, struct disassemble_info *info);
+
 #endif /* EXTENSION_H */
diff --git a/gdb/guile/guile.c b/gdb/guile/guile.c
index 8ba840cba6a..92f2f5c78ef 100644
--- a/gdb/guile/guile.c
+++ b/gdb/guile/guile.c
@@ -130,8 +130,12 @@  static const struct extension_language_ops guile_extension_ops =
   gdbscm_breakpoint_has_cond,
   gdbscm_breakpoint_cond_says_stop,
 
-  NULL, /* gdbscm_check_quit_flag, */
   NULL, /* gdbscm_set_quit_flag, */
+  NULL, /* gdbscm_check_quit_flag, */
+  NULL, /* gdbscm_before_prompt, */
+  NULL, /* gdbscm_get_matching_xmethod_workers */
+  NULL, /* gdbscm_colorize */
+  NULL, /* gdbscm_print_insn */
 };
 #endif
 
diff --git a/gdb/python/python.c b/gdb/python/python.c
index 2c2d8c5f217..d817bd5bf27 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -189,6 +189,8 @@  const struct extension_language_ops python_extension_ops =
   gdbpy_get_matching_xmethod_workers,
 
   gdbpy_colorize,
+
+  NULL, /* gdbpy_print_insn, */
 };
 
 /* Architecture and language to be used in callbacks from