[RFC][PATCH 7/7] Add the patch command to dynamically inject code in the inferior

paul-naert paul_naert@hotmail.fr
Thu May 14 14:08:49 GMT 2020


From: Paul Naert <paul.naert@polymtl.ca>

The goal of this command is to compile and inject code in the inferior.
This commit adds two commands : "patch code" and "patch file", which respectively take as input C/C++ code and a source file.
Compared to the compile command, these new commands leave the code in the binary, so that it can be executed several times.
The injection of code is done through a trampoline in a similar fashion to GDB fast tracepoints.
The original instruction is replaced with a jump to the trampoline which calls the gdb_expr() function generated by gcc (same process as in the compile command).
---
 gdb/Makefile.in             |   3 +-
 gdb/amd64-tdep.c            | 198 +++++++++++++++++++++++++++
 gdb/compile/compile-patch.c | 322 ++++++++++++++++++++++++++++++++++++++++++++
 gdb/gdbarch.c               |  82 +++++++++++
 gdb/gdbarch.h               |  13 ++
 5 files changed, 617 insertions(+), 1 deletion(-)
 create mode 100644 gdb/compile/compile-patch.c

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index b6caa2c..737be8e 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -315,7 +315,8 @@ SUBDIR_GCC_COMPILE_SRCS = \
 	compile/compile-cplus-types.c \
 	compile/compile-loc2c.c \
 	compile/compile-object-load.c \
-	compile/compile-object-run.c
+	compile/compile-object-run.c\
+	compile/compile-patch.c
 
 SUBDIR_GCC_COMPILE_OBS = $(patsubst %.c,%.o,$(filter %.c,$(SUBDIR_GCC_COMPILE_SRCS)))
 
diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c
index a1604a8..d180875 100644
--- a/gdb/amd64-tdep.c
+++ b/gdb/amd64-tdep.c
@@ -49,6 +49,7 @@
 #include "gdbsupport/byte-vector.h"
 #include "osabi.h"
 #include "x86-tdep.h"
+#include "compile/compile-object-load.h"
 
 /* Note that the AMD64 architecture was previously known as x86-64.
    The latter is (forever) engraved into the canonical system name as
@@ -3119,6 +3120,198 @@ amd64_in_indirect_branch_thunk (struct gdbarch *gdbarch, CORE_ADDR pc)
 				       AMD64_RAX_REGNUM,
 				       AMD64_RIP_REGNUM);
 }
+/* Fills the trampoline buffer with instructions to store the register referenced by reg_to_store.  */
+static int 
+amd64_store_reg(unsigned char *trampoline_instr, struct regs_store_data reg_to_store)
+{
+  /* The numbering of registers needs to be adjusted to simplify machine instructions production.  */
+  int correct[16]={0,3,1,2,6,7,5,4,8,9,10,11,12,13,14,15};
+  ULONGEST offset = reg_to_store.reg_offset;
+  int regnum = reg_to_store.regnum;
+  if(regnum == -1)
+  {
+    /* Dummy register.  */
+    return 0;
+  }
+  if(regnum>15 || regnum < 0)
+  {
+    fprintf_filtered(gdb_stdlog, "Expected register number < 16\n");
+    fprintf_filtered(gdb_stdlog, "Got number %d \n", regnum);
+    return 0;
+  }
+  regnum = correct[regnum];
+
+  int i = 0;
+  /* Move register value to *rdi + offset.  */
+  if(regnum<8)
+    trampoline_instr[i++] = '\x48';
+  else
+    trampoline_instr[i++] = '\x4c';
+  trampoline_instr[i++] = '\x89';
+  trampoline_instr[i++] = '\x87'+'\x8'*(regnum%8);
+  int32_t offset32 = (int32_t) offset;
+  memcpy(trampoline_instr+i, &offset32, 4);
+  i+=4;
+  return i;
+}
+
+/* This fills the trampoline_instr buffer with instructions to save
+   and restore the registers, but does not relocate the replaced 
+   instruction. It returns the length of the trampoline.  */
+static int
+amd64_fill_trampoline_buffer (unsigned char *trampoline_instr, 
+                       struct compile_module *module)
+{
+  int i = 0;
+  CORE_ADDR called = BLOCK_ENTRY_PC (SYMBOL_BLOCK_VALUE (module->func_sym));
+  CORE_ADDR regs_struct_addr = module->regs_addr;
+  struct regs_store_data *regs_to_store = module->deferred_regs_store;
+  int regs_store_num = module->regs_store_num;
+  /* save registers */
+
+  /* Store rax at rsp -0xc8 */
+  trampoline_instr[i++] = 0x48;
+  trampoline_instr[i++] = 0x89;
+  trampoline_instr[i++] = 0x84;
+  trampoline_instr[i++] = 0x24;
+  trampoline_instr[i++] = 0x38;
+  trampoline_instr[i++] = 0xff;
+  trampoline_instr[i++] = 0xff;
+  trampoline_instr[i++] = 0xff;
+  
+  /* Move flags to ah */
+  trampoline_instr[i++] = 0x9f; /* lahf */
+
+  /* rsp -= 0x80 for the red zone*/
+  trampoline_instr[i++] = 0x48; 
+  trampoline_instr[i++] = 0x83; 
+  trampoline_instr[i++] = 0xc4; 
+  trampoline_instr[i++] = 0x80; 
+  
+  trampoline_instr[i++] = 0x50; /* push %rax i.e. flags */
+  trampoline_instr[i++] = 0x54; /* push %rsp */
+  trampoline_instr[i++] = 0x55; /* push %rbp */
+  trampoline_instr[i++] = 0x57; /* push %rdi */
+  trampoline_instr[i++] = 0x56; /* push %rsi */
+  trampoline_instr[i++] = 0x52; /* push %rdx */
+  trampoline_instr[i++] = 0x51; /* push %rcx */
+  trampoline_instr[i++] = 0x53; /* push %rbx */
+  trampoline_instr[i++] = 0x48; /* skip this position (old rax is there) */
+  trampoline_instr[i++] = 0x83; 
+  trampoline_instr[i++] = 0xc4;
+  trampoline_instr[i++] = 0xf8;
+  trampoline_instr[i++] = 0x41;
+  trampoline_instr[i++] = 0x57; /* push %r15 */
+  trampoline_instr[i++] = 0x41;
+  trampoline_instr[i++] = 0x56; /* push %r14 */
+  trampoline_instr[i++] = 0x41;
+  trampoline_instr[i++] = 0x55; /* push %r13 */
+  trampoline_instr[i++] = 0x41;
+  trampoline_instr[i++] = 0x54; /* push %r12 */
+  trampoline_instr[i++] = 0x41;
+  trampoline_instr[i++] = 0x53; /* push %r11 */
+  trampoline_instr[i++] = 0x41;
+  trampoline_instr[i++] = 0x52; /* push %r10 */
+  trampoline_instr[i++] = 0x41;
+  trampoline_instr[i++] = 0x51; /* push %r9 */
+  trampoline_instr[i++] = 0x41;
+  trampoline_instr[i++] = 0x50; /* push %r8 */
+
+
+
+  /* Provide gdb_expr () arguments.  */
+  trampoline_instr[i++] = 0x48; /* movabs <arg>,%rdi */
+  trampoline_instr[i++] = 0xbf;
+  memcpy (trampoline_instr + i, &regs_struct_addr, 8);
+  i += 8;
+  /* Store required register values into *rdi.  */
+  for(int j =0; j < regs_store_num; j++)
+  {
+    i += amd64_store_reg(trampoline_instr+i, regs_to_store[j]);
+  }
+
+  /* call gdb_expr () */
+  trampoline_instr[i++] = 0x48; /* movabs <called>,%rax */
+  trampoline_instr[i++] = 0xb8;
+  memcpy (trampoline_instr + i, &called, 8);
+  i += 8;
+  trampoline_instr[i++] = 0xff; /* callq *%rax */
+  trampoline_instr[i++] = 0xd0;
+
+  /* restore registers */
+  trampoline_instr[i++] = 0x41;
+  trampoline_instr[i++] = 0x58; /* pop %r8 */
+  trampoline_instr[i++] = 0x41;
+  trampoline_instr[i++] = 0x59; /* pop %r9 */
+  trampoline_instr[i++] = 0x41;
+  trampoline_instr[i++] = 0x5a; /* pop %r10 */
+  trampoline_instr[i++] = 0x41;
+  trampoline_instr[i++] = 0x5b; /* pop %r11 */
+  trampoline_instr[i++] = 0x41;
+  trampoline_instr[i++] = 0x5c; /* pop %r12 */
+  trampoline_instr[i++] = 0x41;
+  trampoline_instr[i++] = 0x5d; /* pop %r13 */
+  trampoline_instr[i++] = 0x41;
+  trampoline_instr[i++] = 0x5e; /* pop %r14 */
+  trampoline_instr[i++] = 0x41;
+  trampoline_instr[i++] = 0x5f; /* pop %r15 */
+  trampoline_instr[i++] = 0x58; /* pop %rax (will be overwritten right after) */
+  trampoline_instr[i++] = 0x5b; /* pop %rbx */
+  trampoline_instr[i++] = 0x59; /* pop %rcx */
+  trampoline_instr[i++] = 0x5a; /* pop %rdx */
+  trampoline_instr[i++] = 0x5e; /* pop %rsi */
+  trampoline_instr[i++] = 0x5f; /* pop %rdi */
+  trampoline_instr[i++] = 0x5d; /* pop %rbp */
+  trampoline_instr[i++] = 0x5c; /* pop %rsp */
+  trampoline_instr[i++] = 0x58; /* pop %rax */
+  /* rsp += 0x80 for the red zone.  */
+  trampoline_instr[i++] = 0x48; 
+  trampoline_instr[i++] = 0x81; 
+  trampoline_instr[i++] = 0xc4; 
+  trampoline_instr[i++] = 0x80; 
+  trampoline_instr[i++] = 0x00; 
+  trampoline_instr[i++] = 0x00; 
+  trampoline_instr[i++] = 0x00; 
+
+  trampoline_instr[i++] = 0x9e; /* sahf */
+
+  /* Restore rax from rsp -0xc8 */
+  trampoline_instr[i++] = 0x48;
+  trampoline_instr[i++] = 0x8B;
+  trampoline_instr[i++] = 0x84;
+  trampoline_instr[i++] = 0x24;
+  trampoline_instr[i++] = 0x38;
+  trampoline_instr[i++] = 0xff;
+  trampoline_instr[i++] = 0xff;
+  trampoline_instr[i++] = 0xff;
+
+  return i;
+}
+
+/* Inserts a jump from ''from'' to ''to''. If fill not is set to 1,
+   this functions sets the bytes after the jump to NOP to clarify the code.  */
+bool
+amd64_patch_jump (struct gdbarch *gdbarch, CORE_ADDR from, CORE_ADDR to)
+{
+  int64_t long_jump_offset = to - (from + 5);
+  if (long_jump_offset > INT_MAX || long_jump_offset < INT_MIN)
+    {
+      fprintf_filtered (
+          gdb_stderr,
+          "E.Jump pad too far from instruction for jump (offset 0x%" PRIx64
+          " > int32). \n",
+          long_jump_offset);
+      return 0;
+    }
+
+  int32_t jump_offset = (int32_t)long_jump_offset;
+  fprintf_filtered (gdb_stdlog, "Patched in a jump from 0x%lx to 0x%lx \n",
+                    from, to);
+  gdb_byte jump_insn[] = { 0xe9, 0, 0, 0, 0 };
+  memcpy (jump_insn + 1, &jump_offset, 4);
+  target_write_memory (from, jump_insn, 5);
+  return 1;
+}
 
 void
 amd64_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch,
@@ -3225,6 +3418,11 @@ amd64_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch,
   set_gdbarch_ps_regnum (gdbarch, AMD64_EFLAGS_REGNUM); /* %eflags */
   set_gdbarch_fp0_regnum (gdbarch, AMD64_ST0_REGNUM); /* %st(0) */
 
+  /* Functions necessary for the patch command.  */
+  set_gdbarch_fill_trampoline_buffer(gdbarch, amd64_fill_trampoline_buffer);
+  set_gdbarch_patch_jump(gdbarch, amd64_patch_jump);
+  set_gdbarch_jmp_insn_length(gdbarch);
+
   /* The "default" register numbering scheme for AMD64 is referred to
      as the "DWARF Register Number Mapping" in the System V psABI.
      The preferred debugging format for all known AMD64 targets is
diff --git a/gdb/compile/compile-patch.c b/gdb/compile/compile-patch.c
new file mode 100644
index 0000000..4088a80
--- /dev/null
+++ b/gdb/compile/compile-patch.c
@@ -0,0 +1,322 @@
+#include "defs.h"
+#include "gdbcmd.h"
+#include "arch-utils.h"
+#include "gdbsupport/gdb_unlinker.h"
+#include "gdbsupport/pathstuff.h"
+#include "linespec.h"
+#include "objfiles.h"
+#include "compile.h"
+#include "compile-object-load.h"
+#include "observable.h"
+#include <map>
+
+#define PAGE_SIZE sysconf (_SC_PAGE_SIZE)
+#define PAGE_ADDRESS(addr) (addr / PAGE_SIZE) * PAGE_SIZE
+#define TP_MAX_SIZE 0x100
+
+/* Hold "patch" commands.  */
+
+static struct cmd_list_element *compile_patch_command_list;
+
+/* Finds the return address for a trampoline placed at insn_addr.  */
+static CORE_ADDR
+find_return_address (struct gdbarch *gdbarch, CORE_ADDR insn_addr, bool verbose)
+{
+  /* In this version, we only check if we have enough room to put a jump.  */
+  if (gdb_insn_length (gdbarch, insn_addr)
+      < gdbarch_jmp_insn_length (gdbarch, 0))
+    {
+      return 0;
+    }
+  CORE_ADDR return_address = insn_addr + gdb_insn_length (gdbarch, insn_addr);
+  return return_address;
+}
+
+/* Is the page containing addr available ? If so map it.  */
+static CORE_ADDR
+try_map_page (gdbarch *gdbarch, CORE_ADDR trampoline_addr, int trampoline_size)
+{
+  int page_size = PAGE_SIZE;
+  const int prot
+    = GDB_MMAP_PROT_READ | GDB_MMAP_PROT_WRITE | GDB_MMAP_PROT_EXEC;
+
+  CORE_ADDR mapped_page
+    = gdbarch_infcall_mmap (gdbarch, trampoline_addr, 1, prot);
+
+  if (mapped_page != PAGE_ADDRESS (trampoline_addr))
+    {
+      gdbarch_infcall_munmap (gdbarch, mapped_page, page_size);
+      return (CORE_ADDR) 0;
+    }
+  /* Check if the trampoline overlaps several pages.  */
+  if (mapped_page + page_size < trampoline_addr + trampoline_size)
+    {
+      CORE_ADDR second_page
+	= gdbarch_infcall_mmap (gdbarch, mapped_page + page_size, page_size,
+				prot);
+      if (second_page == mapped_page + page_size)
+	{
+	  return mapped_page;
+	}
+      else
+	{
+	  gdbarch_infcall_munmap (gdbarch, mapped_page, page_size);
+	  gdbarch_infcall_munmap (gdbarch, second_page, page_size);
+	  return (CORE_ADDR) 0;
+	}
+    }
+  return mapped_page;
+}
+
+/* This function aims to find the next available chunk of memory around
+   insn_address where we have enough room to put a trampoline of size
+   trampoline_size. It keeps a map pointing to the next available portion of
+   memory given a minimum address.  */
+static CORE_ADDR
+allocate_trampoline (gdbarch *gdbarch, CORE_ADDR insn_address,
+		     int trampoline_size)
+{
+  int max_pages_searched = 100;
+  int page_size = PAGE_SIZE;
+  static std::map<CORE_ADDR, CORE_ADDR> current_stack_top;
+
+  /* We do not handle the case where the trampoline is larger than a page.  */
+  gdb_assert (trampoline_size < page_size);
+
+  /* Return value.  */
+  int bit_shift = gdbarch_jmp_insn_length (gdbarch, 1) * 8 - 3;
+
+  CORE_ADDR trampoline_address
+    = ((insn_address >> bit_shift) > 0
+	 ? (((insn_address >> bit_shift) - 1) << bit_shift)
+	 : 100 * PAGE_SIZE);
+
+  if (current_stack_top.find (trampoline_address) != current_stack_top.end ())
+    {
+      trampoline_address = current_stack_top[trampoline_address];
+      CORE_ADDR next_page = PAGE_ADDRESS (trampoline_address) + page_size;
+      CORE_ADDR trampoline_end = trampoline_address + trampoline_size;
+      if (trampoline_end <= next_page)
+	{
+	  current_stack_top[trampoline_address] = trampoline_end;
+	  return trampoline_address;
+	}
+      /* We don't have enought room to put all of the trampoline
+	on this page but the next one is available.  */
+      if (try_map_page (gdbarch, next_page, 0) != 0)
+	{
+	  current_stack_top[trampoline_address] = trampoline_end;
+	  return trampoline_address;
+	}
+    }
+
+  for (int i = 0; i < max_pages_searched; i++)
+    {
+      if (try_map_page (gdbarch, trampoline_address, trampoline_size) != 0)
+	{
+	  current_stack_top[trampoline_address]
+	    = trampoline_address + trampoline_size;
+	  return trampoline_address;
+	}
+      trampoline_address = PAGE_ADDRESS (trampoline_address + page_size);
+    }
+
+  return 0;
+}
+
+static CORE_ADDR
+build_compile_trampoline (struct gdbarch *gdbarch,
+			  struct compile_module *module, CORE_ADDR insn_addr,
+			  CORE_ADDR return_address)
+{
+  /* Allocate memory for the trampoline in the inferior.  */
+  CORE_ADDR trampoline = allocate_trampoline (gdbarch, insn_addr, TP_MAX_SIZE);
+
+  if (trampoline == 0)
+    {
+      return 0;
+    }
+
+  /* Build trampoline.  */
+  gdb_byte trampoline_instr[TP_MAX_SIZE];
+  int trampoline_size
+    = gdbarch_fill_trampoline_buffer (gdbarch, trampoline_instr, module);
+
+  gdb_assert (trampoline_size < TP_MAX_SIZE);
+
+  /* Copy content of trampoline_instr to inferior memory.  */
+  target_write_memory (trampoline, trampoline_instr, trampoline_size);
+
+  /* Relocate replaced instruction */
+  CORE_ADDR trampoline_end = trampoline + trampoline_size;
+
+  gdbarch_relocate_instruction (gdbarch, &trampoline_end, insn_addr);
+
+  gdb_assert (trampoline_end - trampoline + gdbarch_jmp_insn_length (gdbarch, 0)
+	      < TP_MAX_SIZE);
+
+  /* Jump back to return address.  */
+  gdbarch_patch_jump (gdbarch, trampoline_end, return_address);
+
+  return trampoline;
+}
+
+/* Convert a string describing a location to an instruction address.
+   Here we assume the location to correspond to only one pc. */
+static CORE_ADDR
+location_to_pc (const char *location)
+{
+  struct linespec_result canonical;
+
+  event_location_up event_location
+    = string_to_event_location (&location, current_language);
+  create_sals_from_location_default (event_location.get (), &canonical,
+				     bp_breakpoint);
+  CORE_ADDR addr = canonical.lsals[0].sals[0].pc;
+  return addr;
+}
+
+/* The central function for the patch command. */
+static void
+patch_code (const char *location, const char *code)
+{
+  struct gdbarch *gdbarch = target_gdbarch ();
+
+  /* Convert location string to an instruction address.  */
+  CORE_ADDR insn_addr = location_to_pc (location);
+
+  /* Compile code.  */
+  enum compile_i_scope_types scope = COMPILE_I_PATCH_SCOPE;
+  compile_file_names fnames = compile_to_object (NULL, code, scope, insn_addr);
+  gdb::unlinker object_remover (fnames.object_file ());
+  gdb::unlinker source_remover (fnames.source_file ());
+
+  /* Load compiled code into memory.  */
+  struct compile_module *compile_module
+    = compile_object_load (fnames, scope, NULL);
+
+  /* Build a trampoline which calls the compiled code.  */
+  CORE_ADDR return_address = find_return_address (gdbarch, insn_addr, true);
+
+  if (return_address != 0)
+    {
+      CORE_ADDR trampoline_address
+	= build_compile_trampoline (gdbarch, compile_module, insn_addr,
+				    return_address);
+
+      if (trampoline_address == 0)
+	{
+	  fprintf_filtered (gdb_stderr, "Unable to build a trampoline.\n");
+    unlink (compile_module->source_file);
+    xfree (compile_module->source_file);
+    unlink (objfile_name (compile_module->objfile));
+    xfree (compile_module);
+    return;
+	}
+
+      /* Patch in the jump to the trampoline.  */
+      bool success
+	= gdbarch_patch_jump (gdbarch, insn_addr, trampoline_address);
+      if (!success)
+	{
+	  fprintf_filtered (gdb_stderr,
+			    "Unable to insert the code at the given location.\n\
+Make sure the instruction is long enough \n\
+to be replaced by a jump instruction.\n");
+	}
+    }
+  /* Free unused memory */
+  /* Some memory is left allocated in the inferior because
+     we still need to access it to execute the compiled code.  */
+  unlink (compile_module->source_file);
+  xfree (compile_module->source_file);
+  unlink (objfile_name (compile_module->objfile));
+  xfree (compile_module);
+}
+
+/* Handle the input from the 'patch code' command.  The
+   "patch code" command is used to patch in the code an expression
+   containing calls to the GCC compiler.  The language expected in this
+   command is the language currently set in GDB.  */
+static void
+compile_patch_code_command (const char *arg, int from_tty)
+{
+  if (arg == NULL)
+    {
+      error ("No arguments were entered for the patch code command.");
+    }
+  char *dup = strdup (arg);
+  const char *location = strtok (dup, " ");
+  const char *code = strtok (NULL, "\0");
+  if (code == NULL)
+    {
+      free (dup);
+      error ("Missing the code argument for the patch code command.");
+    }
+  patch_code (location, code);
+  free (dup);
+}
+
+/* Handle the input from the 'patch file' command.  The
+   "patch file" command is used to patch in the code an expression
+   containing calls to the GCC compiler. It takes as argument
+   a source file.  The language expected in this command
+   is the language currently set in GDB. */
+static void
+compile_patch_file_command (const char *arg, int from_tty)
+{
+  if (arg == NULL)
+    {
+      error ("No arguments were entered for the patch file command.");
+    }
+  char *dup = strdup (arg);
+  const char *location = strtok (dup, " ");
+  const char *source_file = strtok (NULL, " ");
+  if (source_file == NULL)
+    {
+      free (dup);
+      error ("Missing the second argument for the patch file command.");
+    }
+  gdb::unique_xmalloc_ptr<char> abspath = gdb_abspath (source_file);
+  std::string code_buf = string_printf ("#include \"%s\"\n", abspath.get ());
+  patch_code (location, code_buf.c_str ());
+  free (dup);
+}
+
+/* The patch command without a suffix is interpreted as patch code.  */
+static void
+compile_patch_command (const char *arg, int from_tty)
+{
+  compile_patch_code_command (arg, from_tty);
+}
+
+void _initialize_compile_patch();
+static void
+_initialize_compile_patch (void)
+{
+  struct cmd_list_element *c = NULL;
+
+  compile_cmd_element
+    = add_prefix_cmd ("patch", class_obscure, compile_patch_command, _ ("\
+Command to compile source code and patch it into the inferior."),
+		      &compile_patch_command_list, "patch ", 1, &cmdlist);
+
+  add_cmd ("code", class_obscure, compile_patch_code_command, _ ("\
+Compile, and patch code at location.\n\
+\n\
+Usage: patch code [LOCATION] [CODE]\n\
+\n\
+The source code may be specified as a simple one line expression, e.g.:\n\
+\n\
+    patch code main:2 printf(\"Hello world\\n\");\n\
+\n\
+It will be executed each time the instruction at location is hit."),
+	   &compile_patch_command_list);
+
+  c = add_cmd ("file", class_obscure, compile_patch_file_command, _ ("\
+Compile and patch in a file containing source code.\n\
+\n\
+Usage: compile patch file [LOCATION] [FILENAME]"),
+	       &compile_patch_command_list);
+  set_cmd_completer (c, filename_completer);
+}
diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c
index d049771..9adb77a 100644
--- a/gdb/gdbarch.c
+++ b/gdb/gdbarch.c
@@ -359,6 +359,10 @@ struct gdbarch
   const disasm_options_and_args_t * valid_disassembler_options;
   gdbarch_type_align_ftype *type_align;
   gdbarch_get_pc_address_flags_ftype *get_pc_address_flags;
+  gdbarch_fill_trampoline_buffer_ftype *fill_trampoline_buffer;
+  gdbarch_patch_jump_ftype *patch_jump;
+  int jmp_insn_length;
+  int jmp_opcode_length;
 };
 
 /* Create a new ``struct gdbarch'' based on information provided by
@@ -475,6 +479,10 @@ gdbarch_alloc (const struct gdbarch_info *info,
   gdbarch->addressable_memory_unit_size = default_addressable_memory_unit_size;
   gdbarch->type_align = default_type_align;
   gdbarch->get_pc_address_flags = default_get_pc_address_flags;
+  gdbarch->fill_trampoline_buffer = NULL;
+  gdbarch->patch_jump = NULL;
+  gdbarch->jmp_insn_length = -1;
+  gdbarch->jmp_opcode_length = -1;
   /* gdbarch_alloc() */
 
   return gdbarch;
@@ -724,6 +732,10 @@ verify_gdbarch (struct gdbarch *gdbarch)
   /* Skip verify of valid_disassembler_options, invalid_p == 0 */
   /* Skip verify of type_align, invalid_p == 0 */
   /* Skip verify of get_pc_address_flags, invalid_p == 0 */
+  /* Skip verify of fill_trampoline_buffer, invalid_p == 0 */
+  /* Skip verify of patch_jump, invalid_p == 0 */
+  /* Skip verify of jmp_insn_length, invalid_p == 0 */
+  /* Skip verify of jmp_opcode_length, invalid_p == 0 */
   if (!log.empty ())
     internal_error (__FILE__, __LINE__,
                     _("verify_gdbarch: the following are invalid ...%s"),
@@ -1003,6 +1015,18 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
                       "gdbarch_dump: fetch_tls_load_module_address = <%s>\n",
                       host_address_to_string (gdbarch->fetch_tls_load_module_address));
   fprintf_unfiltered (file,
+                      "gdbarch_dump: fill_trampoline_buffer = <%s>\n",
+                      host_address_to_string (gdbarch->fill_trampoline_buffer));
+  fprintf_unfiltered (file,
+                      "gdbarch_dump: patch_jump = <%s>\n",
+                      host_address_to_string (gdbarch->patch_jump));
+  fprintf_unfiltered (file,
+                      "gdbarch_dump: jmp_insn_length = <%d>\n",
+                      gdbarch->jmp_insn_length);
+  fprintf_unfiltered (file,
+                      "gdbarch_dump: jmp_opcode_length = <%d>\n",
+                      gdbarch->jmp_opcode_length);
+  fprintf_unfiltered (file,
                       "gdbarch_dump: gdbarch_find_memory_regions_p() = %d\n",
                       gdbarch_find_memory_regions_p (gdbarch));
   fprintf_unfiltered (file,
@@ -4054,6 +4078,64 @@ set_gdbarch_relocate_instruction (struct gdbarch *gdbarch,
   gdbarch->relocate_instruction = relocate_instruction;
 }
 
+void
+set_gdbarch_fill_trampoline_buffer (struct gdbarch *gdbarch,
+                                  gdbarch_fill_trampoline_buffer_ftype fill_trampoline_buffer)
+{
+  gdbarch->fill_trampoline_buffer = fill_trampoline_buffer;
+}
+
+int 
+gdbarch_fill_trampoline_buffer (struct gdbarch *gdbarch,
+                             unsigned char *trampoline_instr,
+                             compile_module *module)
+{
+  gdb_assert (gdbarch != NULL);
+  gdb_assert (gdbarch->fill_trampoline_buffer != NULL);
+  if (gdbarch_debug >= 2)
+    fprintf_unfiltered (gdb_stdlog, "gdbarch_fill_trampoline_buffer called\n");
+  return gdbarch->fill_trampoline_buffer(trampoline_instr, module);
+}
+
+void
+set_gdbarch_patch_jump (struct gdbarch *gdbarch,
+                        gdbarch_patch_jump_ftype patch_jump)
+{
+  gdbarch->patch_jump = patch_jump;
+}
+
+bool 
+gdbarch_patch_jump (struct gdbarch *gdbarch,
+                        CORE_ADDR from,
+                        CORE_ADDR to)
+{
+  gdb_assert (gdbarch != NULL);
+  gdb_assert (gdbarch->patch_jump != NULL);
+  if (gdbarch_debug >= 2)
+    fprintf_unfiltered (gdb_stdlog, "gdbarch_patch_jump called\n");
+  return gdbarch->patch_jump(gdbarch, from, to);
+}
+
+void
+set_gdbarch_jmp_insn_length (struct gdbarch *gdbarch)
+{
+  gdbarch->jmp_insn_length = 5;
+  gdbarch->jmp_opcode_length = 1;
+}
+
+int
+gdbarch_jmp_insn_length(struct gdbarch *gdbarch, bool without_opcode)
+{
+  gdb_assert (gdbarch != NULL);
+  gdb_assert (gdbarch->jmp_insn_length != -1);
+  gdb_assert (gdbarch->jmp_opcode_length != -1);
+  if (gdbarch_debug >= 2)
+    fprintf_unfiltered (gdb_stdlog, "gdbarch_jmp_insn_length called\n");
+  if(without_opcode)
+    return gdbarch->jmp_insn_length - gdbarch->jmp_opcode_length;
+  return gdbarch->jmp_insn_length;
+}
+
 int
 gdbarch_overlay_update_p (struct gdbarch *gdbarch)
 {
diff --git a/gdb/gdbarch.h b/gdb/gdbarch.h
index 054a077..d1b9f18 100644
--- a/gdb/gdbarch.h
+++ b/gdb/gdbarch.h
@@ -66,6 +66,7 @@ struct mem_range;
 struct syscalls_info;
 struct thread_info;
 struct ui_out;
+struct compile_module;
 
 #include "regcache.h"
 
@@ -1648,6 +1649,18 @@ extern void set_gdbarch_get_pc_address_flags (struct gdbarch *gdbarch, gdbarch_g
 
 extern struct gdbarch_tdep *gdbarch_tdep (struct gdbarch *gdbarch);
 
+/* Fill a trampoline by saving and restoring registers and calling the function from the compile module.  */
+typedef int (gdbarch_fill_trampoline_buffer_ftype) (unsigned char *trampoline_instr, compile_module *module);
+extern int gdbarch_fill_trampoline_buffer (struct gdbarch *gdbarch, unsigned char *trampoline_instr, compile_module *module);
+extern void set_gdbarch_fill_trampoline_buffer (struct gdbarch *gdbarch, gdbarch_fill_trampoline_buffer_ftype *fill_trampoline_buffer);
+
+/* Insert a jump instruction from ''from'' to ''to'' */
+typedef bool (gdbarch_patch_jump_ftype) (struct gdbarch *gdbarch, CORE_ADDR from, CORE_ADDR to);
+extern bool gdbarch_patch_jump (struct gdbarch *gdbarch, CORE_ADDR from, CORE_ADDR to);
+extern void set_gdbarch_patch_jump (struct gdbarch *gdbarch, gdbarch_patch_jump_ftype *patch_jump);
+
+extern int gdbarch_jmp_insn_length(gdbarch *gdbarch, bool without_opcode);
+extern void set_gdbarch_jmp_insn_length(gdbarch *gdbarch);
 
 /* Mechanism for co-ordinating the selection of a specific
    architecture.
-- 
2.7.4



More information about the Gdb-patches mailing list