* i386-tdep.h (i386_displaced_step_copy_insn): Declare.
authorDoug Evans <dje@google.com>
Tue, 22 Jun 2010 00:09:10 +0000 (00:09 +0000)
committerDoug Evans <dje@google.com>
Tue, 22 Jun 2010 00:09:10 +0000 (00:09 +0000)
* i386-tdep.c (i386_displaced_step_copy_insn): New function.
(i386_syscall_p): Change type of lengthp to int.
(i386_displaced_step_fixup): Handle kernels that run one past a
syscall insn.
* i386-linux-tdep.c (i386_linux_init_abi): Use
i386_displaced_step_copy_insn instead of
simple_displaced_step_copy_insn.

gdb/ChangeLog
gdb/i386-linux-tdep.c
gdb/i386-tdep.c
gdb/i386-tdep.h

index dce4a93..568f55c 100644 (file)
@@ -1,3 +1,14 @@
+2010-06-21  Doug Evans  <dje@google.com>
+
+       * i386-tdep.h (i386_displaced_step_copy_insn): Declare.
+       * i386-tdep.c (i386_displaced_step_copy_insn): New function.
+       (i386_syscall_p): Change type of lengthp to int.
+       (i386_displaced_step_fixup): Handle kernels that run one past a
+       syscall insn.
+       * i386-linux-tdep.c (i386_linux_init_abi): Use
+       i386_displaced_step_copy_insn instead of
+       simple_displaced_step_copy_insn.
+
 2010-06-21  Tom Tromey  <tromey@redhat.com>
 
        * dwarf2read.c (read_base_type): Handle DW_ATE_UTF.
index 3ae19a7..8ca7377 100644 (file)
@@ -888,7 +888,7 @@ i386_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
 
   /* Displaced stepping.  */
   set_gdbarch_displaced_step_copy_insn (gdbarch,
-                                        simple_displaced_step_copy_insn);
+                                        i386_displaced_step_copy_insn);
   set_gdbarch_displaced_step_fixup (gdbarch, i386_displaced_step_fixup);
   set_gdbarch_displaced_step_free_closure (gdbarch,
                                            simple_displaced_step_free_closure);
index 5e366ea..e0f519d 100644 (file)
@@ -518,7 +518,7 @@ i386_call_p (const gdb_byte *insn)
    length in bytes.  Otherwise, return zero.  */
 
 static int
-i386_syscall_p (const gdb_byte *insn, ULONGEST *lengthp)
+i386_syscall_p (const gdb_byte *insn, int *lengthp)
 {
   if (insn[0] == 0xcd)
     {
@@ -529,6 +529,43 @@ i386_syscall_p (const gdb_byte *insn, ULONGEST *lengthp)
   return 0;
 }
 
+/* Some kernels may run one past a syscall insn, so we have to cope.
+   Otherwise this is just simple_displaced_step_copy_insn.  */
+
+struct displaced_step_closure *
+i386_displaced_step_copy_insn (struct gdbarch *gdbarch,
+                              CORE_ADDR from, CORE_ADDR to,
+                              struct regcache *regs)
+{
+  size_t len = gdbarch_max_insn_length (gdbarch);
+  gdb_byte *buf = xmalloc (len);
+
+  read_memory (from, buf, len);
+
+  /* GDB may get control back after the insn after the syscall.
+     Presumably this is a kernel bug.
+     If this is a syscall, make sure there's a nop afterwards.  */
+  {
+    int syscall_length;
+    gdb_byte *insn;
+
+    insn = i386_skip_prefixes (buf, len);
+    if (insn != NULL && i386_syscall_p (insn, &syscall_length))
+      insn[syscall_length] = NOP_OPCODE;
+  }
+
+  write_memory (to, buf, len);
+
+  if (debug_displaced)
+    {
+      fprintf_unfiltered (gdb_stdlog, "displaced: copy %s->%s: ",
+                          paddress (gdbarch, from), paddress (gdbarch, to));
+      displaced_step_dump_bytes (gdb_stdlog, buf, len);
+    }
+
+  return (struct displaced_step_closure *) buf;
+}
+
 /* Fix up the state of registers and memory after having single-stepped
    a displaced instruction.  */
 
@@ -587,7 +624,7 @@ i386_displaced_step_fixup (struct gdbarch *gdbarch,
       && ! i386_ret_p (insn))
     {
       ULONGEST orig_eip;
-      ULONGEST insn_len;
+      int insn_len;
 
       regcache_cooked_read_unsigned (regs, I386_EIP_REGNUM, &orig_eip);
 
@@ -606,7 +643,12 @@ i386_displaced_step_fixup (struct gdbarch *gdbarch,
          it unrelocated.  Goodness help us if there are PC-relative
          system calls.  */
       if (i386_syscall_p (insn, &insn_len)
-          && orig_eip != to + (insn - insn_start) + insn_len)
+          && orig_eip != to + (insn - insn_start) + insn_len
+         /* GDB can get control back after the insn after the syscall.
+            Presumably this is a kernel bug.
+            i386_displaced_step_copy_insn ensures its a nop,
+            we add one to the length for it.  */
+          && orig_eip != to + (insn - insn_start) + insn_len + 1)
         {
           if (debug_displaced)
             fprintf_unfiltered (gdb_stdlog,
index 6520d67..49e0727 100644 (file)
@@ -362,6 +362,9 @@ extern const struct regset *
                                 const char *sect_name, size_t sect_size);
 
 
+extern struct displaced_step_closure *i386_displaced_step_copy_insn
+  (struct gdbarch *gdbarch, CORE_ADDR from, CORE_ADDR to,
+   struct regcache *regs);
 extern void i386_displaced_step_fixup (struct gdbarch *gdbarch,
                                       struct displaced_step_closure *closure,
                                       CORE_ADDR from, CORE_ADDR to,