daily update
[deliverable/binutils-gdb.git] / gdb / amd64-tdep.c
index 1aa49b9eef4e903522d6df6dfc5e9e9e5b6e0038..9feed905a65e1c0928eb9a9121c9076a10e33e30 100644 (file)
@@ -36,7 +36,7 @@
 #include "regcache.h"
 #include "regset.h"
 #include "symfile.h"
-
+#include "disasm.h"
 #include "gdb_assert.h"
 
 #include "amd64-tdep.h"
@@ -1022,57 +1022,6 @@ amd64_skip_prefixes (gdb_byte *insn)
   return insn;
 }
 
-/* fprintf-function for amd64_insn_length.
-   This function is a nop, we don't want to print anything, we just want to
-   compute the length of the insn.  */
-
-static int ATTR_FORMAT (printf, 2, 3)
-amd64_insn_length_fprintf (void *stream, const char *format, ...)
-{
-  return 0;
-}
-
-/* Initialize a struct disassemble_info for amd64_insn_length. */
-
-static void
-amd64_insn_length_init_dis (struct gdbarch *gdbarch,
-                           struct disassemble_info *di,
-                           const gdb_byte *insn, int max_len,
-                           CORE_ADDR addr)
-{
-  init_disassemble_info (di, NULL, amd64_insn_length_fprintf);
-
-  /* init_disassemble_info installs buffer_read_memory, etc.
-     so we don't need to do that here.
-     The cast is necessary until disassemble_info is const-ified.  */
-  di->buffer = (gdb_byte *) insn;
-  di->buffer_length = max_len;
-  di->buffer_vma = addr;
-
-  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);
-
-  disassemble_init_for_target (di);
-}
-
-/* Return the length in bytes of INSN.
-   MAX_LEN is the size of the buffer containing INSN.
-   libopcodes currently doesn't export a utility to compute the
-   instruction length, so use the disassembler until then.  */
-
-static int
-amd64_insn_length (struct gdbarch *gdbarch,
-                  const gdb_byte *insn, int max_len, CORE_ADDR addr)
-{
-  struct disassemble_info di;
-
-  amd64_insn_length_init_dis (gdbarch, &di, insn, max_len, addr);
-
-  return gdbarch_print_insn (gdbarch, addr, &di);
-}
-
 /* Return an integer register (other than RSP) that is unused as an input
    operand in INSN.
    In order to not require adding a rex prefix if the insn doesn't already
@@ -1238,7 +1187,8 @@ fixup_riprel (struct gdbarch *gdbarch, struct displaced_step_closure *dsc,
 
   /* Compute the rip-relative address. */
   disp = extract_signed_integer (insn, sizeof (int32_t), byte_order);
-  insn_length = amd64_insn_length (gdbarch, dsc->insn_buf, dsc->max_len, from);
+  insn_length = gdb_buffered_insn_length (gdbarch, dsc->insn_buf,
+                                         dsc->max_len, from);
   rip_base = from + insn_length;
 
   /* We need a register to hold the address.
@@ -1557,6 +1507,123 @@ amd64_displaced_step_fixup (struct gdbarch *gdbarch,
                            paddress (gdbarch, retaddr));
     }
 }
+
+/* If the instruction INSN uses RIP-relative addressing, return the
+   offset into the raw INSN where the displacement to be adjusted is
+   found.  Returns 0 if the instruction doesn't use RIP-relative
+   addressing.  */
+
+static int
+rip_relative_offset (struct amd64_insn *insn)
+{
+  if (insn->modrm_offset != -1)
+    {
+      gdb_byte modrm = insn->raw_insn[insn->modrm_offset];
+
+      if ((modrm & 0xc7) == 0x05)
+       {
+         /* The displacement is found right after the ModRM byte.  */
+         return insn->modrm_offset + 1;
+       }
+    }
+
+  return 0;
+}
+
+static void
+append_insns (CORE_ADDR *to, ULONGEST len, const gdb_byte *buf)
+{
+  target_write_memory (*to, buf, len);
+  *to += len;
+}
+
+void
+amd64_relocate_instruction (struct gdbarch *gdbarch,
+                           CORE_ADDR *to, CORE_ADDR oldloc)
+{
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  int len = gdbarch_max_insn_length (gdbarch);
+  /* Extra space for sentinels.  */
+  int fixup_sentinel_space = len;
+  gdb_byte *buf = xmalloc (len + fixup_sentinel_space);
+  struct amd64_insn insn_details;
+  int offset = 0;
+  LONGEST rel32, newrel;
+  gdb_byte *insn;
+  int insn_length;
+
+  read_memory (oldloc, buf, len);
+
+  /* Set up the sentinel space so we don't have to worry about running
+     off the end of the buffer.  An excessive number of leading prefixes
+     could otherwise cause this.  */
+  memset (buf + len, 0, fixup_sentinel_space);
+
+  insn = buf;
+  amd64_get_insn_details (insn, &insn_details);
+
+  insn_length = gdb_buffered_insn_length (gdbarch, insn, len, oldloc);
+
+  /* Skip legacy instruction prefixes.  */
+  insn = amd64_skip_prefixes (insn);
+
+  /* Adjust calls with 32-bit relative addresses as push/jump, with
+     the address pushed being the location where the original call in
+     the user program would return to.  */
+  if (insn[0] == 0xe8)
+    {
+      gdb_byte push_buf[16];
+      unsigned int ret_addr;
+
+      /* Where "ret" in the original code will return to.  */
+      ret_addr = oldloc + insn_length;
+      push_buf[0] = 0x68; /* pushq $... */
+      memcpy (&push_buf[1], &ret_addr, 4);
+      /* Push the push.  */
+      append_insns (to, 5, push_buf);
+
+      /* Convert the relative call to a relative jump.  */
+      insn[0] = 0xe9;
+
+      /* Adjust the destination offset.  */
+      rel32 = extract_signed_integer (insn + 1, 4, byte_order);
+      newrel = (oldloc - *to) + rel32;
+      store_signed_integer (insn + 1, 4, newrel, byte_order);
+
+      /* Write the adjusted jump into its displaced location.  */
+      append_insns (to, 5, insn);
+      return;
+    }
+
+  offset = rip_relative_offset (&insn_details);
+  if (!offset)
+    {
+      /* Adjust jumps with 32-bit relative addresses.  Calls are
+        already handled above.  */
+      if (insn[0] == 0xe9)
+       offset = 1;
+      /* Adjust conditional jumps.  */
+      else if (insn[0] == 0x0f && (insn[1] & 0xf0) == 0x80)
+       offset = 2;
+    }
+
+  if (offset)
+    {
+      rel32 = extract_signed_integer (insn + offset, 4, byte_order);
+      newrel = (oldloc - *to) + rel32;
+      store_signed_integer (insn + offset, 4, newrel, byte_order);
+      if (debug_displaced)
+       fprintf_unfiltered (gdb_stdlog,
+                           "Adjusted insn rel32=0x%s at 0x%s to"
+                           " rel32=0x%s at 0x%s\n",
+                           hex_string (rel32), paddress (gdbarch, oldloc),
+                           hex_string (newrel), paddress (gdbarch, *to));
+    }
+
+  /* Write the adjusted instruction into its displaced location.  */
+  append_insns (to, insn_length, buf);
+}
+
 \f
 /* The maximum number of saved registers.  This should include %rip.  */
 #define AMD64_NUM_SAVED_REGS   AMD64_NUM_GREGS
@@ -2217,7 +2284,6 @@ amd64_supply_xstateregset (const struct regset *regset,
                           struct regcache *regcache, int regnum,
                           const void *xstateregs, size_t len)
 {
-  const struct gdbarch_tdep *tdep = gdbarch_tdep (regset->arch);
   amd64_supply_xsave (regcache, regnum, xstateregs);
 }
 
@@ -2228,7 +2294,6 @@ amd64_collect_xstateregset (const struct regset *regset,
                            const struct regcache *regcache,
                            int regnum, void *xstateregs, size_t len)
 {
-  const struct gdbarch_tdep *tdep = gdbarch_tdep (regset->arch);
   amd64_collect_xsave (regcache, regnum, xstateregs, 1);
 }
 
@@ -2415,6 +2480,8 @@ amd64_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
                                          amd64_regset_from_core_section);
 
   set_gdbarch_get_longjmp_target (gdbarch, amd64_get_longjmp_target);
+
+  set_gdbarch_relocate_instruction (gdbarch, amd64_relocate_instruction);
 }
 
 /* Provide a prototype to silence -Wmissing-prototypes.  */
This page took 0.025296 seconds and 4 git commands to generate.