This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: Stepping off breakpoints in non-stop debugging mode (resubmit)


Any comments on this?

Here's a refreshed version.  All its dependencies are now in.
2008-04-25  Jim Blandy  <jimb@codesourcery.com>
	    Pedro Alves  <pedro@codesourcery.com>

	Implement displaced stepping.

	gdb/
	* gdbarch.sh (max_insn_length): New 'variable'.
	(displaced_step_copy, displaced_step_fixup)
	(displaced_step_free_closure, displaced_step_location): New
	functions.
	(struct displaced_step_closure): Add forward declaration.  *
	gdbarch.c, gdbarch.h: Regenerated.

	* arch-utils.c: #include "objfiles.h".
	(simple_displaced_step_copy_insn)
	(simple_displaced_step_free_closure)
	(displaced_step_at_entry_point): New functions.
	* arch-utils.h (simple_displaced_step_copy_insn)
	(simple_displaced_step_free_closure)
	(displaced_step_at_entry_point): New prototypes.

	* i386-tdep.c (I386_MAX_INSN_LEN): Rename to...
	(I386_MAX_MATCHED_INSN_LEN): ... this.
	(i386_absolute_jmp_p, i386_absolute_call_p)
	(i386_ret_p, i386_call_p, i386_breakpoint_p, i386_syscall_p)
	(i386_displaced_step_fixup): New functions.
	(struct i386_insn, i386_match_insn): Update.
	(i386_gdbarch_init): Set gdbarch_max_insn_length.
	* i386-tdep.h (I386_MAX_INSN_LEN): New.
	(i386_displaced_step_fixup): New prototype.
	* i386-linux-tdep.c (i386_linux_init_abi): Include "arch-utils.h".
	Register gdbarch_displaced_step_copy,
	gdbarch_displaced_step_fixup, gdbarch_displaced_step_free_closure,
	and gdbarch_displaced_step_location functions.

	* infrun.c (debug_displaced): New variable.
	(show_debug_displaced): New function.
	(struct displaced_step_request): New struct.
	(displaced_step_request_queue, displaced_step_ptid)
	(displaced_step_gdbarch, displaced_step_closure)
	(displaced_step_original, displaced_step_copy)
	(displaced_step_saved_copy, can_use_displaced_stepping): New
	variables.
	(show_can_use_displaced_stepping, use_displaced_stepping)
	(displaced_step_clear, cleanup_displaced_step_closure)
	(displaced_step_dump_bytes, displaced_step_prepare)
	(displaced_step_clear_cleanup, write_memory_ptid)
	(displaced_step_fixup): New functions.
	(resume): Call displaced_step_prepare.
	(proceed): Call read_pc once, and remember the value.  If using
	displaced stepping, don't remove breakpoints.
	(handle_inferior_event): Call displaced_step_fixup.  Add some
	debugging output.  When we try to step over a breakpoint, but get
	a signal to deliver to the thread instead, ensure the step-resume
	breakpoint is actually inserted.  If a thread hop is needed, and
	displaced stepping is enabled, don't remove breakpoints.
	(init_wait_for_inferior): Call displaced_step_clear.
	(_initialize_infrun): Add "set debug displaced" command.  Add
	"maint set can-use-displaced-stepping" command.  Clear
	displaced_step_ptid.
	* inferior.h (debug_displaced): Declare variable.
	(displaced_step_dump_bytes): Declare function.

	* Makefile.in (arch-utils.o, i386-linux-tdep.o): Update
	dependencies.

	gdb/testsuite/
	* gdb.asm/asmsrc1.s: Add scratch space.

	gdb/doc/
	* gdb.texinfo (Debugging Output): Document "set/show debug
	displaced".
	(Maintenance Commands): Document "maint set/show
	can-use-displaced-stepping".

---
 gdb/Makefile.in                 |    5 
 gdb/arch-utils.c                |   52 +++
 gdb/arch-utils.h                |   24 +
 gdb/doc/gdb.texinfo             |   19 +
 gdb/gdbarch.c                   |  152 +++++++++++
 gdb/gdbarch.h                   |   90 ++++++
 gdb/gdbarch.sh                  |   70 +++++
 gdb/i386-linux-tdep.c           |   10 
 gdb/i386-tdep.c                 |  230 ++++++++++++++++-
 gdb/i386-tdep.h                 |   10 
 gdb/inferior.h                  |    8 
 gdb/infrun.c                    |  534 +++++++++++++++++++++++++++++++++++++---
 gdb/testsuite/gdb.asm/asmsrc1.s |   12 
 13 files changed, 1182 insertions(+), 34 deletions(-)

Index: src/gdb/Makefile.in
===================================================================
--- src.orig/gdb/Makefile.in	2008-04-25 18:16:04.000000000 +0100
+++ src/gdb/Makefile.in	2008-04-25 18:17:00.000000000 +0100
@@ -1915,7 +1915,7 @@ annotate.o: annotate.c $(defs_h) $(annot
 arch-utils.o: arch-utils.c $(defs_h) $(arch_utils_h) $(buildsym_h) \
 	$(gdbcmd_h) $(inferior_h) $(gdb_string_h) $(regcache_h) \
 	$(gdb_assert_h) $(sim_regno_h) $(gdbcore_h) $(osabi_h) $(version_h) \
-	$(floatformat_h) $(target_descriptions_h)
+	$(floatformat_h) $(target_descriptions_h) $(objfiles_h)
 arm-linux-nat.o: arm-linux-nat.c $(defs_h) $(inferior_h) $(gdbcore_h) \
 	$(gdb_string_h) $(regcache_h) $(arm_tdep_h) $(gregset_h) \
 	$(target_h) $(linux_nat_h) $(gdb_proc_service_h) $(arm_linux_tdep_h) \
@@ -2252,7 +2252,8 @@ i386-linux-nat.o: i386-linux-nat.c $(def
 i386-linux-tdep.o: i386-linux-tdep.c $(defs_h) $(gdbcore_h) $(frame_h) \
 	$(value_h) $(regcache_h) $(inferior_h) $(osabi_h) $(reggroups_h) \
 	$(dwarf2_frame_h) $(gdb_string_h) $(i386_tdep_h) \
-	$(i386_linux_tdep_h) $(glibc_tdep_h) $(solib_svr4_h) $(symtab_h)
+	$(i386_linux_tdep_h) $(glibc_tdep_h) $(solib_svr4_h) $(symtab_h) \
+	$(arch_utils_h)
 i386-nat.o: i386-nat.c $(defs_h) $(breakpoint_h) $(command_h) $(gdbcmd_h) \
 	$(target_h)
 i386nbsd-nat.o: i386nbsd-nat.c $(defs_h) $(gdbcore_h) $(regcache_h) \
Index: src/gdb/arch-utils.c
===================================================================
--- src.orig/gdb/arch-utils.c	2008-04-25 18:16:05.000000000 +0100
+++ src/gdb/arch-utils.c	2008-04-25 18:17:00.000000000 +0100
@@ -31,12 +31,64 @@
 #include "gdbcore.h"
 #include "osabi.h"
 #include "target-descriptions.h"
+#include "objfiles.h"
 
 #include "version.h"
 
 #include "floatformat.h"
 
 
+struct displaced_step_closure *
+simple_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);
+  write_memory (to, buf, len);
+
+  if (debug_displaced)
+    {
+      fprintf_unfiltered (gdb_stdlog, "displaced: copy 0x%s->0x%s: ",
+                          paddr_nz (from), paddr_nz (to));
+      displaced_step_dump_bytes (gdb_stdlog, buf, len);
+    }
+
+  return (struct displaced_step_closure *) buf;
+}
+
+
+void
+simple_displaced_step_free_closure (struct gdbarch *gdbarch,
+                                    struct displaced_step_closure *closure)
+{
+  xfree (closure);
+}
+
+
+CORE_ADDR
+displaced_step_at_entry_point (struct gdbarch *gdbarch)
+{
+  CORE_ADDR addr;
+  int bp_len;
+
+  addr = entry_point_address ();
+
+  /* Make certain that the address points at real code, and not a
+     function descriptor.  */
+  addr = gdbarch_convert_from_func_ptr_addr (gdbarch, addr, &current_target);
+
+  /* Inferior calls also use the entry point as a breakpoint location.
+     We don't want displaced stepping to interfere with those
+     breakpoints, so leave space.  */
+  gdbarch_breakpoint_from_pc (gdbarch, &addr, &bp_len);
+  addr += bp_len * 2;
+
+  return addr;
+}
+
 int
 legacy_register_sim_regno (struct gdbarch *gdbarch, int regnum)
 {
Index: src/gdb/arch-utils.h
===================================================================
--- src.orig/gdb/arch-utils.h	2008-04-25 18:16:05.000000000 +0100
+++ src/gdb/arch-utils.h	2008-04-25 18:17:00.000000000 +0100
@@ -30,6 +30,30 @@ struct gdbarch_info;
 /* gdbarch trace variable */
 extern int gdbarch_debug;
 
+/* An implementation of gdbarch_displaced_step_copy_insn for
+   processors that don't need to modify the instruction before
+   single-stepping the displaced copy.
+
+   Simply copy gdbarch_max_insn_length (ARCH) bytes from FROM to TO.
+   The closure is an array of that many bytes containing the
+   instruction's bytes, allocated with xmalloc.  */
+extern struct displaced_step_closure *
+  simple_displaced_step_copy_insn (struct gdbarch *gdbarch,
+                                   CORE_ADDR from, CORE_ADDR to,
+                                   struct regcache *regs);
+
+/* Simple implementation of gdbarch_displaced_step_free_closure: Call
+   xfree.
+   This is appropriate for use with simple_displaced_step_copy_insn.  */
+extern void
+  simple_displaced_step_free_closure (struct gdbarch *gdbarch,
+                                      struct displaced_step_closure *closure);
+
+/* Possible value for gdbarch_displaced_step_location:
+   Place displaced instructions at the program's entry point,
+   leaving space for inferior function call return breakpoints.  */
+extern CORE_ADDR displaced_step_at_entry_point (struct gdbarch *gdbarch);
+
 /* The only possible cases for inner_than. */
 extern int core_addr_lessthan (CORE_ADDR lhs, CORE_ADDR rhs);
 extern int core_addr_greaterthan (CORE_ADDR lhs, CORE_ADDR rhs);
Index: src/gdb/gdbarch.sh
===================================================================
--- src.orig/gdb/gdbarch.sh	2008-04-25 18:16:05.000000000 +0100
+++ src/gdb/gdbarch.sh	2008-04-25 18:17:00.000000000 +0100
@@ -610,6 +610,75 @@ v:int:vbit_in_delta:::0:0::0
 # Advance PC to next instruction in order to skip a permanent breakpoint.
 F:void:skip_permanent_breakpoint:struct regcache *regcache:regcache
 
+# The maximum length of an instruction on this architecture.
+V:ULONGEST:max_insn_length:::0:0
+
+# Copy the instruction at FROM to TO, and make any adjustments
+# necessary to single-step it at that address.
+#
+# REGS holds the state the thread's registers will have before
+# executing the copied instruction; the PC in REGS will refer to FROM,
+# not the copy at TO.  The caller should update it to point at TO later.
+#
+# Return a pointer to data of the architecture's choice to be passed
+# to gdbarch_displaced_step_fixup.  Or, return NULL to indicate that
+# the instruction's effects have been completely simulated, with the
+# resulting state written back to REGS.
+#
+# For a general explanation of displaced stepping and how GDB uses it,
+# see the comments in infrun.c.
+#
+# The TO area is only guaranteed to have space for
+# gdbarch_max_insn_length (arch) bytes, so this function must not
+# write more bytes than that to that area.
+#
+# If you do not provide this function, GDB assumes that the
+# architecture does not support displaced stepping.
+#
+# If your architecture doesn't need to adjust instructions before
+# single-stepping them, consider using simple_displaced_step_copy_insn
+# here.
+M:struct displaced_step_closure *:displaced_step_copy_insn:CORE_ADDR from, CORE_ADDR to, struct regcache *regs:from, to, regs
+
+# Fix up the state resulting from successfully single-stepping a
+# displaced instruction, to give the result we would have gotten from
+# stepping the instruction in its original location.
+#
+# REGS is the register state resulting from single-stepping the
+# displaced instruction.
+#
+# CLOSURE is the result from the matching call to
+# gdbarch_displaced_step_copy_insn.
+#
+# If you provide gdbarch_displaced_step_copy_insn.but not this
+# function, then GDB assumes that no fixup is needed after
+# single-stepping the instruction.
+#
+# For a general explanation of displaced stepping and how GDB uses it,
+# see the comments in infrun.c.
+M:void:displaced_step_fixup:struct displaced_step_closure *closure, CORE_ADDR from, CORE_ADDR to, struct regcache *regs:closure, from, to, regs::NULL
+
+# Free a closure returned by gdbarch_displaced_step_copy_insn.
+#
+# If you provide gdbarch_displaced_step_copy_insn, you must provide
+# this function as well.
+#
+# If your architecture uses closures that don't need to be freed, then
+# you can use simple_displaced_step_free_closure here.
+#
+# For a general explanation of displaced stepping and how GDB uses it,
+# see the comments in infrun.c.
+m:void:displaced_step_free_closure:struct displaced_step_closure *closure:closure::NULL::(! gdbarch->displaced_step_free_closure) != (! gdbarch->displaced_step_copy_insn)
+
+# Return the address of an appropriate place to put displaced
+# instructions while we step over them.  There need only be one such
+# place, since we're only stepping one thread over a breakpoint at a
+# time.
+#
+# For a general explanation of displaced stepping and how GDB uses it,
+# see the comments in infrun.c.
+m:CORE_ADDR:displaced_step_location:void:::NULL::(! gdbarch->displaced_step_location) != (! gdbarch->displaced_step_copy_insn)
+
 # Refresh overlay mapped state for section OSECT.
 F:void:overlay_update:struct obj_section *osect:osect
 
@@ -729,6 +798,7 @@ struct target_ops;
 struct obstack;
 struct bp_target_info;
 struct target_desc;
+struct displaced_step_closure;
 
 extern struct gdbarch *current_gdbarch;
 EOF
Index: src/gdb/i386-tdep.c
===================================================================
--- src.orig/gdb/i386-tdep.c	2008-04-25 18:16:24.000000000 +0100
+++ src/gdb/i386-tdep.c	2008-04-25 18:17:00.000000000 +0100
@@ -276,6 +276,225 @@ i386_breakpoint_from_pc (struct gdbarch 
   return break_insn;
 }
 
+/* Displaced instruction handling.  */
+
+
+static int
+i386_absolute_jmp_p (gdb_byte *insn)
+{
+  /* jmp far (absolute address in operand) */
+  if (insn[0] == 0xea)
+    return 1;
+
+  if (insn[0] == 0xff)
+    {
+      /* jump near, absolute indirect (/4) */
+      if ((insn[1] & 0x38) == 0x20)
+        return 1;
+
+      /* jump far, absolute indirect (/5) */
+      if ((insn[1] & 0x38) == 0x28)
+        return 1;
+    }
+
+  return 0;
+}
+
+static int
+i386_absolute_call_p (gdb_byte *insn)
+{
+  /* call far, absolute */
+  if (insn[0] == 0x9a)
+    return 1;
+
+  if (insn[0] == 0xff)
+    {
+      /* Call near, absolute indirect (/2) */
+      if ((insn[1] & 0x38) == 0x10)
+        return 1;
+
+      /* Call far, absolute indirect (/3) */
+      if ((insn[1] & 0x38) == 0x18)
+        return 1;
+    }
+
+  return 0;
+}
+
+static int
+i386_ret_p (gdb_byte *insn)
+{
+  switch (insn[0])
+    {
+    case 0xc2: /* ret near, pop N bytes */
+    case 0xc3: /* ret near */
+    case 0xca: /* ret far, pop N bytes */
+    case 0xcb: /* ret far */
+    case 0xcf: /* iret */
+      return 1;
+
+    default:
+      return 0;
+    }
+}
+
+static int
+i386_call_p (gdb_byte *insn)
+{
+  if (i386_absolute_call_p (insn))
+    return 1;
+
+  /* call near, relative */
+  if (insn[0] == 0xe8)
+    return 1;
+
+  return 0;
+}
+
+static int
+i386_breakpoint_p (gdb_byte *insn)
+{
+  return insn[0] == 0xcc;       /* int 3 */
+}
+
+/* Return non-zero if INSN is a system call, and set *LENGTHP to its
+   length in bytes.  Otherwise, return zero.  */
+static int
+i386_syscall_p (gdb_byte *insn, ULONGEST *lengthp)
+{
+  if (insn[0] == 0xcd)
+    {
+      *lengthp = 2;
+      return 1;
+    }
+
+  return 0;
+}
+
+/* Fix up the state of registers and memory after having single-stepped
+   a displaced instruction.  */
+void
+i386_displaced_step_fixup (struct gdbarch *gdbarch,
+                           struct displaced_step_closure *closure,
+                           CORE_ADDR from, CORE_ADDR to,
+                           struct regcache *regs)
+{
+  /* The offset we applied to the instruction's address.
+     This could well be negative (when viewed as a signed 32-bit
+     value), but ULONGEST won't reflect that, so take care when
+     applying it.  */
+  ULONGEST insn_offset = to - from;
+
+  /* Since we use simple_displaced_step_copy_insn, our closure is a
+     copy of the instruction.  */
+  gdb_byte *insn = (gdb_byte *) closure;
+
+  if (debug_displaced)
+    fprintf_unfiltered (gdb_stdlog,
+                        "displaced: fixup (0x%s, 0x%s), "
+                        "insn = 0x%02x 0x%02x ...\n",
+                        paddr_nz (from), paddr_nz (to), insn[0], insn[1]);
+
+  /* The list of issues to contend with here is taken from
+     resume_execution in arch/i386/kernel/kprobes.c, Linux 2.6.20.
+     Yay for Free Software!  */
+
+  /* Relocate the %eip, if necessary.  */
+
+  /* Except in the case of absolute or indirect jump or call
+     instructions, or a return instruction, the new eip is relative to
+     the displaced instruction; make it relative.  Well, signal
+     handler returns don't need relocation either, but we use the
+     value of %eip to recognize those; see below.  */
+  if (! i386_absolute_jmp_p (insn)
+      && ! i386_absolute_call_p (insn)
+      && ! i386_ret_p (insn))
+    {
+      ULONGEST orig_eip;
+      ULONGEST insn_len;
+
+      regcache_cooked_read_unsigned (regs, I386_EIP_REGNUM, &orig_eip);
+
+      /* A signal trampoline system call changes the %eip, resuming
+         execution of the main program after the signal handler has
+         returned.  That makes them like 'return' instructions; we
+         shouldn't relocate %eip.
+
+         But most system calls don't, and we do need to relocate %eip.
+
+         Our heuristic for distinguishing these cases: if stepping
+         over the system call instruction left control directly after
+         the instruction, the we relocate --- control almost certainly
+         doesn't belong in the displaced copy.  Otherwise, we assume
+         the instruction has put control where it belongs, and leave
+         it unrelocated.  Goodness help us if there are PC-relative
+         system calls.  */
+      if (i386_syscall_p (insn, &insn_len)
+          && orig_eip != to + insn_len)
+        {
+          if (debug_displaced)
+            fprintf_unfiltered (gdb_stdlog,
+                                "displaced: syscall changed %%eip; "
+                                "not relocating\n");
+        }
+      else
+        {
+          ULONGEST eip = (orig_eip - insn_offset) & 0xffffffffUL;
+
+          /* If we have stepped over a breakpoint, set the %eip to
+             point at the breakpoint instruction itself.
+
+             (gdbarch_decr_pc_after_break was never something the core
+             of GDB should have been concerned with; arch-specific
+             code should be making PC values consistent before
+             presenting them to GDB.)  */
+          if (i386_breakpoint_p (insn))
+            {
+              fprintf_unfiltered (gdb_stdlog,
+                                  "displaced: stepped breakpoint\n");
+              eip--;
+            }
+
+          regcache_cooked_write_unsigned (regs, I386_EIP_REGNUM, eip);
+
+          if (debug_displaced)
+            fprintf_unfiltered (gdb_stdlog,
+                                "displaced: "
+                                "relocated %%eip from 0x%s to 0x%s\n",
+                                paddr_nz (orig_eip), paddr_nz (eip));
+        }
+    }
+
+  /* If the instruction was PUSHFL, then the TF bit will be set in the
+     pushed value, and should be cleared.  We'll leave this for later,
+     since GDB already messes up the TF flag when stepping over a
+     pushfl.  */
+
+  /* If the instruction was a call, the return address now atop the
+     stack is the address following the copied instruction.  We need
+     to make it the address following the original instruction.  */
+  if (i386_call_p (insn))
+    {
+      ULONGEST esp;
+      ULONGEST retaddr;
+      const ULONGEST retaddr_len = 4;
+
+      regcache_cooked_read_unsigned (regs, I386_ESP_REGNUM, &esp);
+      retaddr = read_memory_unsigned_integer (esp, retaddr_len);
+      retaddr = (retaddr - insn_offset) & 0xffffffffUL;
+      write_memory_unsigned_integer (esp, retaddr_len, retaddr);
+
+      if (debug_displaced)
+        fprintf_unfiltered (gdb_stdlog,
+                            "displaced: relocated return addr at 0x%s "
+                            "to 0x%s\n",
+                            paddr_nz (esp),
+                            paddr_nz (retaddr));
+    }
+}
+
+
+
 #ifdef I386_REGNO_TO_SYMMETRY
 #error "The Sequent Symmetry is no longer supported."
 #endif
@@ -521,14 +740,14 @@ i386_analyze_stack_align (CORE_ADDR pc, 
 }
 
 /* Maximum instruction length we need to handle.  */
-#define I386_MAX_INSN_LEN	6
+#define I386_MAX_MATCHED_INSN_LEN	6
 
 /* Instruction description.  */
 struct i386_insn
 {
   size_t len;
-  gdb_byte insn[I386_MAX_INSN_LEN];
-  gdb_byte mask[I386_MAX_INSN_LEN];
+  gdb_byte insn[I386_MAX_MATCHED_INSN_LEN];
+  gdb_byte mask[I386_MAX_MATCHED_INSN_LEN];
 };
 
 /* Search for the instruction at PC in the list SKIP_INSNS.  Return
@@ -547,12 +766,12 @@ i386_match_insn (CORE_ADDR pc, struct i3
     {
       if ((op & insn->mask[0]) == insn->insn[0])
 	{
-	  gdb_byte buf[I386_MAX_INSN_LEN - 1];
+	  gdb_byte buf[I386_MAX_MATCHED_INSN_LEN - 1];
 	  int insn_matched = 1;
 	  size_t i;
 
 	  gdb_assert (insn->len > 1);
-	  gdb_assert (insn->len <= I386_MAX_INSN_LEN);
+	  gdb_assert (insn->len <= I386_MAX_MATCHED_INSN_LEN);
 
 	  target_read_memory (pc + 1, buf, insn->len - 1);
 	  for (i = 1; i < insn->len; i++)
@@ -2430,6 +2649,7 @@ i386_gdbarch_init (struct gdbarch_info i
 
   set_gdbarch_breakpoint_from_pc (gdbarch, i386_breakpoint_from_pc);
   set_gdbarch_decr_pc_after_break (gdbarch, 1);
+  set_gdbarch_max_insn_length (gdbarch, I386_MAX_INSN_LEN);
 
   set_gdbarch_frame_args_skip (gdbarch, 8);
 
Index: src/gdb/i386-tdep.h
===================================================================
--- src.orig/gdb/i386-tdep.h	2008-04-25 18:16:05.000000000 +0100
+++ src/gdb/i386-tdep.h	2008-04-25 18:17:00.000000000 +0100
@@ -164,6 +164,10 @@ extern struct type *i386_sse_type (struc
 #define I386_SEL_UPL	0x0003	/* User Privilige Level. */
 #define I386_SEL_KPL	0x0000	/* Kernel Privilige Level. */
 
+/* The length of the longest i386 instruction (according to
+   include/asm-i386/kprobes.h in Linux 2.6.  */
+#define I386_MAX_INSN_LEN (16)
+
 /* Functions exported from i386-tdep.c.  */
 extern CORE_ADDR i386_pe_skip_trampoline_code (CORE_ADDR pc, char *name);
 
@@ -195,6 +199,12 @@ extern const struct regset *
   i386_regset_from_core_section (struct gdbarch *gdbarch,
 				 const char *sect_name, size_t sect_size);
 
+
+extern void i386_displaced_step_fixup (struct gdbarch *gdbarch,
+				       struct displaced_step_closure *closure,
+				       CORE_ADDR from, CORE_ADDR to,
+				       struct regcache *regs);
+
 /* Initialize a basic ELF architecture variant.  */
 extern void i386_elf_init_abi (struct gdbarch_info, struct gdbarch *);
 
Index: src/gdb/infrun.c
===================================================================
--- src.orig/gdb/infrun.c	2008-04-25 18:16:24.000000000 +0100
+++ src/gdb/infrun.c	2008-04-25 18:17:00.000000000 +0100
@@ -103,6 +103,14 @@ int sync_execution = 0;
 
 static ptid_t previous_inferior_ptid;
 
+int debug_displaced = 0;
+static void
+show_debug_displaced (struct ui_file *file, int from_tty,
+		      struct cmd_list_element *c, const char *value)
+{
+  fprintf_filtered (file, _("Displace stepping debugging is %s.\n"), value);
+}
+
 static int debug_infrun = 0;
 static void
 show_debug_infrun (struct ui_file *file, int from_tty,
@@ -459,6 +467,377 @@ static int stepping_past_singlestep_brea
    stepping the thread user has selected.  */
 static ptid_t deferred_step_ptid;
 
+/* Displaced stepping.  */
+
+/* In non-stop debugging mode, we must take special care to manage
+   breakpoints properly; in particular, the traditional strategy for
+   stepping a thread past a breakpoint it has hit is unsuitable.
+   'Displaced stepping' is a tactic for stepping one thread past a
+   breakpoint it has hit while ensuring that other threads running
+   concurrently will hit the breakpoint as they should.
+
+   The traditional way to step a thread T off a breakpoint in a
+   multi-threaded program in all-stop mode is as follows:
+
+   a0) Initially, all threads are stopped, and breakpoints are not
+       inserted.
+   a1) We single-step T, leaving breakpoints uninserted.
+   a2) We insert breakpoints, and resume all threads.
+
+   In non-stop debugging, however, this strategy is unsuitable: we
+   don't want to have to stop all threads in the system in order to
+   continue or step T past a breakpoint.  Instead, we use displaced
+   stepping:
+
+   n0) Initially, T is stopped, other threads are running, and
+       breakpoints are inserted.
+   n1) We copy the instruction "under" the breakpoint to a separate
+       location, outside the main code stream, making any adjustments
+       to the instruction, register, and memory state as directed by
+       T's architecture.
+   n2) We single-step T over the instruction at its new location.
+   n3) We adjust the resulting register and memory state as directed
+       by T's architecture.  This includes resetting T's PC to point
+       back into the main instruction stream.
+   n4) We resume T.
+
+   This approach depends on the following gdbarch methods:
+
+   - gdbarch_max_insn_length and gdbarch_displaced_step_location
+     indicate where to copy the instruction, and how much space must
+     be reserved there.  We use these in step n1.
+
+   - gdbarch_displaced_step_copy_insn copies a instruction to a new
+     address, and makes any necessary adjustments to the instruction,
+     register contents, and memory.  We use this in step n1.
+
+   - gdbarch_displaced_step_fixup adjusts registers and memory after
+     we have successfuly single-stepped the instruction, to yield the
+     same effect the instruction would have had if we had executed it
+     at its original address.  We use this in step n3.
+
+   - gdbarch_displaced_step_free_closure provides cleanup.
+
+   The gdbarch_displaced_step_copy_insn and
+   gdbarch_displaced_step_fixup functions must be written so that
+   copying an instruction with gdbarch_displaced_step_copy_insn,
+   single-stepping across the copied instruction, and then applying
+   gdbarch_displaced_insn_fixup should have the same effects on the
+   thread's memory and registers as stepping the instruction in place
+   would have.  Exactly which responsibilities fall to the copy and
+   which fall to the fixup is up to the author of those functions.
+
+   See the comments in gdbarch.sh for details.
+
+   Note that displaced stepping and software single-step cannot
+   currently be used in combination, although with some care I think
+   they could be made to.  Software single-step works by placing
+   breakpoints on all possible subsequent instructions; if the
+   displaced instruction is a PC-relative jump, those breakpoints
+   could fall in very strange places --- on pages that aren't
+   executable, or at addresses that are not proper instruction
+   boundaries.  (We do generally let other threads run while we wait
+   to hit the software single-step breakpoint, and they might
+   encounter such a corrupted instruction.)  One way to work around
+   this would be to have gdbarch_displaced_step_copy_insn fully
+   simulate the effect of PC-relative instructions (and return NULL)
+   on architectures that use software single-stepping.
+
+   In non-stop mode, we can have independent and simultaneous step
+   requests, so more than one thread may need to simultaneously step
+   over a breakpoint.  The current implementation assumes there is
+   only one scratch space per process.  In this case, we have to
+   serialize access to the scratch space.  If thread A wants to step
+   over a breakpoint, but we are currently waiting for some other
+   thread to complete a displaced step, we leave thread A stopped and
+   place it in the displaced_step_request_queue.  Whenever a displaced
+   step finishes, we pick the next thread in the queue and start a new
+   displaced step operation on it.  See displaced_step_prepare and
+   displaced_step_fixup for details.  */
+
+/* If this is not null_ptid, this is the thread carrying out a
+   displaced single-step.  This thread's state will require fixing up
+   once it has completed its step.  */
+static ptid_t displaced_step_ptid;
+
+struct displaced_step_request
+{
+  ptid_t ptid;
+  struct displaced_step_request *next;
+};
+
+/* A queue of pending displaced stepping requests.  */
+struct displaced_step_request *displaced_step_request_queue;
+
+/* The architecture the thread had when we stepped it.  */
+static struct gdbarch *displaced_step_gdbarch;
+
+/* The closure provided gdbarch_displaced_step_copy_insn, to be used
+   for post-step cleanup.  */
+static struct displaced_step_closure *displaced_step_closure;
+
+/* The address of the original instruction, and the copy we made.  */
+static CORE_ADDR displaced_step_original, displaced_step_copy;
+
+/* Saved contents of copy area.  */
+static gdb_byte *displaced_step_saved_copy;
+
+/* When this is non-zero, we are allowed to use displaced stepping, if
+   the architecture supports it.  When this is zero, we use
+   traditional the hold-and-step approach.  */
+int can_use_displaced_stepping = 1;
+static void
+show_can_use_displaced_stepping (struct ui_file *file, int from_tty,
+				 struct cmd_list_element *c,
+				 const char *value)
+{
+  fprintf_filtered (file, _("\
+Debugger's willingness to use displaced stepping to step over "
+"breakpoints is %s.\n"), value);
+}
+
+/* Return non-zero if displaced stepping is enabled, and can be used
+   with GDBARCH.  */
+static int
+use_displaced_stepping (struct gdbarch *gdbarch)
+{
+  return (can_use_displaced_stepping
+	  && gdbarch_displaced_step_copy_insn_p (gdbarch));
+}
+
+/* Clean out any stray displaced stepping state.  */
+static void
+displaced_step_clear (void)
+{
+  /* Indicate that there is no cleanup pending.  */
+  displaced_step_ptid = null_ptid;
+
+  if (displaced_step_closure)
+    {
+      gdbarch_displaced_step_free_closure (displaced_step_gdbarch,
+                                           displaced_step_closure);
+      displaced_step_closure = NULL;
+    }
+}
+
+static void
+cleanup_displaced_step_closure (void *ptr)
+{
+  struct displaced_step_closure *closure = ptr;
+
+  gdbarch_displaced_step_free_closure (current_gdbarch, closure);
+}
+
+/* Dump LEN bytes at BUF in hex to FILE, followed by a newline.  */
+void
+displaced_step_dump_bytes (struct ui_file *file,
+                           const gdb_byte *buf,
+                           size_t len)
+{
+  int i;
+
+  for (i = 0; i < len; i++)
+    fprintf_unfiltered (file, "%02x ", buf[i]);
+  fputs_unfiltered ("\n", file);
+}
+
+/* Prepare to single-step, using displaced stepping.
+
+   Note that we cannot use displaced stepping when we have a signal to
+   deliver.  If we have a signal to deliver and an instruction to step
+   over, then after the step, there will be no indication from the
+   target whether the thread entered a signal handler or ignored the
+   signal and stepped over the instruction successfully --- both cases
+   result in a simple SIGTRAP.  In the first case we mustn't do a
+   fixup, and in the second case we must --- but we can't tell which.
+   Comments in the code for 'random signals' in handle_inferior_event
+   explain how we handle this case instead.
+
+   Returns 1 if preparing was successful -- this thread is going to be
+   stepped now; or 0 if displaced stepping this thread got queued.  */
+static int
+displaced_step_prepare (ptid_t ptid)
+{
+  struct cleanup *old_cleanups;
+  struct regcache *regcache = get_thread_regcache (ptid);
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
+  CORE_ADDR original, copy;
+  ULONGEST len;
+  struct displaced_step_closure *closure;
+
+  /* We should never reach this function if the architecture does not
+     support displaced stepping.  */
+  gdb_assert (gdbarch_displaced_step_copy_insn_p (gdbarch));
+
+  /* For the first cut, we're displaced stepping one thread at a
+     time.  */
+
+  if (!ptid_equal (displaced_step_ptid, null_ptid))
+    {
+      /* Already waiting for a displaced step to finish.  Defer this
+	 request and place in queue.  */
+      struct displaced_step_request *req, *new_req;
+
+      if (debug_displaced)
+	fprintf_unfiltered (gdb_stdlog,
+			    "displaced: defering step of %s\n",
+			    target_pid_to_str (ptid));
+
+      new_req = xmalloc (sizeof (*new_req));
+      new_req->ptid = ptid;
+      new_req->next = NULL;
+
+      if (displaced_step_request_queue)
+	{
+	  for (req = displaced_step_request_queue;
+	       req && req->next;
+	       req = req->next)
+	    ;
+	  req->next = new_req;
+	}
+      else
+	displaced_step_request_queue = new_req;
+
+      return 0;
+    }
+  else
+    {
+      if (debug_displaced)
+	fprintf_unfiltered (gdb_stdlog,
+			    "displaced: stepping %s now\n",
+			    target_pid_to_str (ptid));
+    }
+
+  displaced_step_clear ();
+
+  original = read_pc_pid (ptid);
+
+  copy = gdbarch_displaced_step_location (gdbarch);
+  len = gdbarch_max_insn_length (gdbarch);
+
+  /* Save the original contents of the copy area.  */
+  displaced_step_saved_copy = xmalloc (len);
+  old_cleanups = make_cleanup (free_current_contents,
+                               &displaced_step_saved_copy);
+  read_memory (copy, displaced_step_saved_copy, len);
+  if (debug_displaced)
+    {
+      fprintf_unfiltered (gdb_stdlog, "displaced: saved 0x%s: ",
+			  paddr_nz (copy));
+      displaced_step_dump_bytes (gdb_stdlog, displaced_step_saved_copy, len);
+    };
+
+  closure = gdbarch_displaced_step_copy_insn (gdbarch,
+                                              original, copy, regcache);
+
+  /* We don't support the fully-simulated case at present.  */
+  gdb_assert (closure);
+
+  make_cleanup (cleanup_displaced_step_closure, closure);
+
+  /* Resume execution at the copy.  */
+  write_pc_pid (copy, ptid);
+
+  discard_cleanups (old_cleanups);
+
+  if (debug_displaced)
+    fprintf_unfiltered (gdb_stdlog, "displaced: displaced pc to 0x%s\n",
+                        paddr_nz (copy));
+
+  /* Save the information we need to fix things up if the step
+     succeeds.  */
+  displaced_step_ptid = ptid;
+  displaced_step_gdbarch = gdbarch;
+  displaced_step_closure = closure;
+  displaced_step_original = original;
+  displaced_step_copy = copy;
+  return 1;
+}
+
+static void
+displaced_step_clear_cleanup (void *ignore)
+{
+  displaced_step_clear ();
+}
+
+static void
+write_memory_ptid (ptid_t ptid, CORE_ADDR memaddr, const gdb_byte *myaddr, int len)
+{
+  struct cleanup *ptid_cleanup = save_inferior_ptid ();
+  inferior_ptid = ptid;
+  write_memory (memaddr, myaddr, len);
+  do_cleanups (ptid_cleanup);
+}
+
+static void
+displaced_step_fixup (ptid_t event_ptid, enum target_signal signal)
+{
+  struct cleanup *old_cleanups;
+
+  /* Was this event for the pid we displaced?  */
+  if (ptid_equal (displaced_step_ptid, null_ptid)
+      || ! ptid_equal (displaced_step_ptid, event_ptid))
+    return;
+
+  old_cleanups = make_cleanup (displaced_step_clear_cleanup, 0);
+
+  /* Restore the contents of the copy area.  */
+  {
+    ULONGEST len = gdbarch_max_insn_length (displaced_step_gdbarch);
+    write_memory_ptid (displaced_step_ptid, displaced_step_copy,
+		       displaced_step_saved_copy, len);
+    if (debug_displaced)
+      fprintf_unfiltered (gdb_stdlog, "displaced: restored 0x%s\n",
+                          paddr_nz (displaced_step_copy));
+  }
+
+  /* Did the instruction complete successfully?  */
+  if (signal == TARGET_SIGNAL_TRAP)
+    {
+      /* Fix up the resulting state.  */
+      gdbarch_displaced_step_fixup (displaced_step_gdbarch,
+                                    displaced_step_closure,
+                                    displaced_step_original,
+                                    displaced_step_copy,
+                                    get_thread_regcache (displaced_step_ptid));
+    }
+  else
+    {
+      /* Since the instruction didn't complete, all we can do is
+         relocate the PC.  */
+      CORE_ADDR pc = read_pc_pid (event_ptid);
+      pc = displaced_step_original + (pc - displaced_step_copy);
+      write_pc_pid (pc, event_ptid);
+    }
+
+  do_cleanups (old_cleanups);
+
+  /* Are there any pending displaced stepping requests?  If so, run
+     one now.  */
+  if (displaced_step_request_queue)
+    {
+      struct displaced_step_request *head;
+      ptid_t ptid;
+
+      head = displaced_step_request_queue;
+      ptid = head->ptid;
+      displaced_step_request_queue = head->next;
+      xfree (head);
+
+      if (debug_displaced)
+	fprintf_unfiltered (gdb_stdlog,
+			    "displaced: stepping queued %s now\n",
+			    target_pid_to_str (ptid));
+
+
+      displaced_step_ptid = null_ptid;
+      displaced_step_prepare (ptid);
+      target_resume (ptid, 1, TARGET_SIGNAL_0);
+    }
+}
+
+
+/* Resuming.  */
 
 /* Things to clean up if we QUIT out of resume ().  */
 static void
@@ -510,14 +889,14 @@ resume (int step, enum target_signal sig
 {
   int should_resume = 1;
   struct cleanup *old_cleanups = make_cleanup (resume_cleanups, 0);
+  CORE_ADDR pc = read_pc ();
   QUIT;
 
   if (debug_infrun)
-    fprintf_unfiltered (gdb_stdlog, "infrun: resume (step=%d, signal=%d)\n",
-			step, sig);
-
-  /* FIXME: calling breakpoint_here_p (read_pc ()) three times! */
-
+    fprintf_unfiltered (gdb_stdlog,
+                        "infrun: resume (step=%d, signal=%d), "
+                        "stepping_over_breakpoint=%d\n",
+			step, sig, stepping_over_breakpoint);
 
   /* Some targets (e.g. Solaris x86) have a kernel bug when stepping
      over an instruction that causes a page fault without triggering
@@ -535,7 +914,7 @@ resume (int step, enum target_signal sig
      removed or inserted, as appropriate.  The exception is if we're sitting
      at a permanent breakpoint; we need to step over it, but permanent
      breakpoints can't be removed.  So we have to test for it here.  */
-  if (breakpoint_here_p (read_pc ()) == permanent_breakpoint_here)
+  if (breakpoint_here_p (pc) == permanent_breakpoint_here)
     {
       if (gdbarch_skip_permanent_breakpoint_p (current_gdbarch))
 	gdbarch_skip_permanent_breakpoint (current_gdbarch,
@@ -547,6 +926,24 @@ how to step past a permanent breakpoint 
 a command like `return' or `jump' to continue execution."));
     }
 
+  /* If enabled, step over breakpoints by executing a copy of the
+     instruction at a different address.
+
+     We can't use displaced stepping when we have a signal to deliver;
+     the comments for displaced_step_prepare explain why.  The
+     comments in the handle_inferior event for dealing with 'random
+     signals' explain what we do instead.  */
+  if (use_displaced_stepping (current_gdbarch)
+      && stepping_over_breakpoint
+      && sig == TARGET_SIGNAL_0)
+    {
+      if (!displaced_step_prepare (inferior_ptid))
+	/* Got placed in displaced stepping queue.  Will be resumed
+	   later when all the currently queued displaced stepping
+	   requests finish.  */
+	return;
+    }
+
   if (step && gdbarch_software_single_step_p (current_gdbarch))
     {
       /* Do it the hard way, w/temp breakpoints */
@@ -558,7 +955,7 @@ a command like `return' or `jump' to con
           `wait_for_inferior' */
           singlestep_breakpoints_inserted_p = 1;
           singlestep_ptid = inferior_ptid;
-          singlestep_pc = read_pc ();
+          singlestep_pc = pc;
         }
     }
 
@@ -642,15 +1039,30 @@ a command like `return' or `jump' to con
 	  /* Most targets can step a breakpoint instruction, thus
 	     executing it normally.  But if this one cannot, just
 	     continue and we will hit it anyway.  */
-	  if (step && breakpoint_inserted_here_p (read_pc ()))
+	  if (step && breakpoint_inserted_here_p (pc))
 	    step = 0;
 	}
+
+      if (debug_displaced
+          && use_displaced_stepping (current_gdbarch)
+          && stepping_over_breakpoint)
+        {
+          CORE_ADDR actual_pc = read_pc_pid (resume_ptid);
+          gdb_byte buf[4];
+
+          fprintf_unfiltered (gdb_stdlog, "displaced: run 0x%s: ",
+                              paddr_nz (actual_pc));
+          read_memory (actual_pc, buf, sizeof (buf));
+          displaced_step_dump_bytes (gdb_stdlog, buf, sizeof (buf));
+        }
+
       target_resume (resume_ptid, step, sig);
     }
 
   discard_cleanups (old_cleanups);
 }
 
+/* Proceeding.  */
 
 /* Clear out all variables saying what to do when inferior is continued.
    First do this, then set the ones you want, then call `proceed'.  */
@@ -787,17 +1199,20 @@ proceed (CORE_ADDR addr, enum target_sig
 
   if (oneproc)
     {
-      /* We will get a trace trap after one instruction.
-	 Continue it automatically and insert breakpoints then.  */
       stepping_over_breakpoint = 1;
-      /* FIXME: if breakpoints are always inserted, we'll trap
-       if trying to single-step over breakpoint.  Disable
-      all breakpoints.  In future, we'd need to invent some
-      smart way of stepping over breakpoint instruction without
-      hitting breakpoint.  */
-      remove_breakpoints ();
+      /* If displaced stepping is enabled, we can step over the
+	 breakpoint without hitting it, so leave all breakpoints
+	 inserted.  Otherwise we need to disable all breakpoints, step
+	 one instruction, and then re-add them when that step is
+	 finished.  */
+      if (!use_displaced_stepping (current_gdbarch))
+	remove_breakpoints ();
     }
-  else
+
+  /* We can insert breakpoints if we're not trying to step over one,
+     or if we are stepping over one but we're using displaced stepping
+     to do so.  */
+  if (! stepping_over_breakpoint || use_displaced_stepping (current_gdbarch))
     insert_breakpoints ();
 
   if (siggnal != TARGET_SIGNAL_DEFAULT)
@@ -908,7 +1323,10 @@ init_wait_for_inferior (void)
   deferred_step_ptid = null_ptid;
 
   target_last_wait_ptid = minus_one_ptid;
+
+  displaced_step_clear ();
 }
+
 
 /* This enum encodes possible reasons for doing a target_wait, so that
    wfi can call target_wait in one place.  (Ultimately the call will be
@@ -1580,10 +1998,31 @@ handle_inferior_event (struct execution_
       return;
     }
 
+  /* Do we need to clean up the state of a thread that has completed a
+     displaced single-step?  (Doing so usually affects the PC, so do
+     it here, before we set stop_pc.)  */
+  displaced_step_fixup (ecs->ptid, stop_signal);
+
   stop_pc = read_pc_pid (ecs->ptid);
 
   if (debug_infrun)
-    fprintf_unfiltered (gdb_stdlog, "infrun: stop_pc = 0x%s\n", paddr_nz (stop_pc));
+    {
+      fprintf_unfiltered (gdb_stdlog, "infrun: stop_pc = 0x%s\n",
+                          paddr_nz (stop_pc));
+      if (STOPPED_BY_WATCHPOINT (&ecs->ws))
+	{
+          CORE_ADDR addr;
+	  fprintf_unfiltered (gdb_stdlog, "infrun: stopped by watchpoint\n");
+
+          if (target_stopped_data_address (&current_target, &addr))
+            fprintf_unfiltered (gdb_stdlog,
+                                "infrun: stopped data address = 0x%s\n",
+                                paddr_nz (addr));
+          else
+            fprintf_unfiltered (gdb_stdlog,
+                                "infrun: (no data address available)\n");
+	}
+    }
 
   if (stepping_past_singlestep_breakpoint)
     {
@@ -1731,7 +2170,7 @@ handle_inferior_event (struct execution_
 
       if (thread_hop_needed)
 	{
-	  int remove_status;
+	  int remove_status = 0;
 
 	  if (debug_infrun)
 	    fprintf_unfiltered (gdb_stdlog, "infrun: thread_hop_needed\n");
@@ -1746,7 +2185,11 @@ handle_inferior_event (struct execution_
 	      singlestep_breakpoints_inserted_p = 0;
 	    }
 
-	  remove_status = remove_breakpoints ();
+	  /* If the arch can displace step, don't remove the
+	     breakpoints.  */
+	  if (!use_displaced_stepping (current_gdbarch))
+	    remove_status = remove_breakpoints ();
+
 	  /* Did we fail to remove breakpoints?  If so, try
 	     to set the PC past the bp.  (There's at least
 	     one situation in which we can fail to remove
@@ -1810,9 +2253,6 @@ handle_inferior_event (struct execution_
       && (HAVE_STEPPABLE_WATCHPOINT
 	  || gdbarch_have_nonsteppable_watchpoint (current_gdbarch)))
     {
-      if (debug_infrun)
-	fprintf_unfiltered (gdb_stdlog, "infrun: STOPPED_BY_WATCHPOINT\n");
-
       /* At this point, we are stopped at an instruction which has
          attempted to write to a piece of memory under control of
          a watchpoint.  The instruction hasn't actually executed
@@ -1915,10 +2355,14 @@ handle_inferior_event (struct execution_
      when we're trying to execute a breakpoint instruction on a
      non-executable stack.  This happens for call dummy breakpoints
      for architectures like SPARC that place call dummies on the
-     stack.  */
+     stack.
 
+     If we're doing a displaced step past a breakpoint, then the
+     breakpoint is always inserted at the original instruction;
+     non-standard signals can't be explained by the breakpoint.  */
   if (stop_signal == TARGET_SIGNAL_TRAP
-      || (breakpoint_inserted_here_p (stop_pc)
+      || (! stepping_over_breakpoint
+          && breakpoint_inserted_here_p (stop_pc)
 	  && (stop_signal == TARGET_SIGNAL_ILL
 	      || stop_signal == TARGET_SIGNAL_SEGV
 	      || stop_signal == TARGET_SIGNAL_EMT))
@@ -2043,7 +2487,7 @@ process_event_stop_test:
 	{
 	  /* We were just starting a new sequence, attempting to
 	     single-step off of a breakpoint and expecting a SIGTRAP.
-	     Intead this signal arrives.  This signal will take us out
+	     Instead this signal arrives.  This signal will take us out
 	     of the stepping range so GDB needs to remember to, when
 	     the signal handler returns, resume stepping off that
 	     breakpoint.  */
@@ -2051,6 +2495,10 @@ process_event_stop_test:
 	     code paths as single-step - set a breakpoint at the
 	     signal return address and then, once hit, step off that
 	     breakpoint.  */
+          if (debug_infrun)
+            fprintf_unfiltered (gdb_stdlog,
+                                "infrun: signal arrived while stepping over "
+                                "breakpoint\n");
 
 	  insert_step_resume_breakpoint_at_frame (get_current_frame ());
 	  ecs->step_after_step_resume_breakpoint = 1;
@@ -2074,6 +2522,11 @@ process_event_stop_test:
 	     Note that this is only needed for a signal delivered
 	     while in the single-step range.  Nested signals aren't a
 	     problem as they eventually all return.  */
+          if (debug_infrun)
+            fprintf_unfiltered (gdb_stdlog,
+                                "infrun: signal may take us out of "
+                                "single-step range\n");
+
 	  insert_step_resume_breakpoint_at_frame (get_current_frame ());
 	  keep_going (ecs);
 	  return;
@@ -2903,7 +3356,11 @@ keep_going (struct execution_control_sta
       
       if (ecs->stepping_over_breakpoint)
 	{
-	  remove_breakpoints ();
+	  if (! use_displaced_stepping (current_gdbarch))
+	    /* Since we can't do a displaced step, we have to remove
+	       the breakpoint while we step it.  To keep things
+	       simple, we remove them all.  */
+	    remove_breakpoints ();
 	}
       else
 	{
@@ -4004,6 +4461,14 @@ When non-zero, inferior specific debuggi
 			    show_debug_infrun,
 			    &setdebuglist, &showdebuglist);
 
+  add_setshow_boolean_cmd ("displaced", class_maintenance, &debug_displaced, _("\
+Set displaced stepping debugging."), _("\
+Show displaced stepping debugging."), _("\
+When non-zero, displaced stepping specific debugging is enabled."),
+			    NULL,
+			    show_debug_displaced,
+			    &setdebuglist, &showdebuglist);
+
   numsigs = (int) TARGET_SIGNAL_LAST;
   signal_stop = (unsigned char *) xmalloc (sizeof (signal_stop[0]) * numsigs);
   signal_print = (unsigned char *)
@@ -4099,9 +4564,24 @@ function is skipped and the step command
 			   show_step_stop_if_no_debug,
 			   &setlist, &showlist);
 
+  add_setshow_boolean_cmd ("can-use-displaced-stepping", class_maintenance,
+			    &can_use_displaced_stepping, _("\
+Set debugger's willingness to use displaced stepping to step \n\
+over breakpoints."), _("\
+Show debugger's willingness to use displaced stepping to step \n\
+over breakpoints."), _("\
+If zero, gdb will not use to use displaced stepping to step over\n\
+breakpoints, even if such is supported by the target."),
+			    NULL,
+			    show_can_use_displaced_stepping,
+			    &maintenance_set_cmdlist,
+			   &maintenance_show_cmdlist);
+
+
   /* ptid initializations */
   null_ptid = ptid_build (0, 0, 0);
   minus_one_ptid = ptid_build (-1, 0, 0);
   inferior_ptid = null_ptid;
   target_last_wait_ptid = minus_one_ptid;
+  displaced_step_ptid = null_ptid;
 }
Index: src/gdb/gdbarch.c
===================================================================
--- src.orig/gdb/gdbarch.c	2008-04-25 18:16:05.000000000 +0100
+++ src/gdb/gdbarch.c	2008-04-25 18:17:00.000000000 +0100
@@ -226,6 +226,11 @@ struct gdbarch
   int vtable_function_descriptors;
   int vbit_in_delta;
   gdbarch_skip_permanent_breakpoint_ftype *skip_permanent_breakpoint;
+  ULONGEST max_insn_length;
+  gdbarch_displaced_step_copy_insn_ftype *displaced_step_copy_insn;
+  gdbarch_displaced_step_fixup_ftype *displaced_step_fixup;
+  gdbarch_displaced_step_free_closure_ftype *displaced_step_free_closure;
+  gdbarch_displaced_step_location_ftype *displaced_step_location;
   gdbarch_overlay_update_ftype *overlay_update;
   gdbarch_core_read_description_ftype *core_read_description;
   gdbarch_static_transform_name_ftype *static_transform_name;
@@ -348,6 +353,11 @@ struct gdbarch startup_gdbarch =
   0,  /* vtable_function_descriptors */
   0,  /* vbit_in_delta */
   0,  /* skip_permanent_breakpoint */
+  0,  /* max_insn_length */
+  0,  /* displaced_step_copy_insn */
+  0,  /* displaced_step_fixup */
+  NULL,  /* displaced_step_free_closure */
+  NULL,  /* displaced_step_location */
   0,  /* overlay_update */
   0,  /* core_read_description */
   0,  /* static_transform_name */
@@ -431,6 +441,9 @@ gdbarch_alloc (const struct gdbarch_info
   gdbarch->coff_make_msymbol_special = default_coff_make_msymbol_special;
   gdbarch->name_of_malloc = "malloc";
   gdbarch->register_reggroup_p = default_register_reggroup_p;
+  gdbarch->displaced_step_fixup = NULL;
+  gdbarch->displaced_step_free_closure = NULL;
+  gdbarch->displaced_step_location = NULL;
   /* gdbarch_alloc() */
 
   return gdbarch;
@@ -586,6 +599,13 @@ verify_gdbarch (struct gdbarch *gdbarch)
   /* Skip verify of vtable_function_descriptors, invalid_p == 0 */
   /* Skip verify of vbit_in_delta, invalid_p == 0 */
   /* Skip verify of skip_permanent_breakpoint, has predicate */
+  /* Skip verify of max_insn_length, has predicate */
+  /* Skip verify of displaced_step_copy_insn, has predicate */
+  /* Skip verify of displaced_step_fixup, has predicate */
+  if ((! gdbarch->displaced_step_free_closure) != (! gdbarch->displaced_step_copy_insn))
+    fprintf_unfiltered (log, "\n\tdisplaced_step_free_closure");
+  if ((! gdbarch->displaced_step_location) != (! gdbarch->displaced_step_copy_insn))
+    fprintf_unfiltered (log, "\n\tdisplaced_step_location");
   /* Skip verify of overlay_update, has predicate */
   /* Skip verify of core_read_description, has predicate */
   /* Skip verify of static_transform_name, has predicate */
@@ -709,6 +729,24 @@ gdbarch_dump (struct gdbarch *gdbarch, s
                       "gdbarch_dump: deprecated_function_start_offset = 0x%s\n",
                       paddr_nz (gdbarch->deprecated_function_start_offset));
   fprintf_unfiltered (file,
+                      "gdbarch_dump: gdbarch_displaced_step_copy_insn_p() = %d\n",
+                      gdbarch_displaced_step_copy_insn_p (gdbarch));
+  fprintf_unfiltered (file,
+                      "gdbarch_dump: displaced_step_copy_insn = <0x%lx>\n",
+                      (long) gdbarch->displaced_step_copy_insn);
+  fprintf_unfiltered (file,
+                      "gdbarch_dump: gdbarch_displaced_step_fixup_p() = %d\n",
+                      gdbarch_displaced_step_fixup_p (gdbarch));
+  fprintf_unfiltered (file,
+                      "gdbarch_dump: displaced_step_fixup = <0x%lx>\n",
+                      (long) gdbarch->displaced_step_fixup);
+  fprintf_unfiltered (file,
+                      "gdbarch_dump: displaced_step_free_closure = <0x%lx>\n",
+                      (long) gdbarch->displaced_step_free_closure);
+  fprintf_unfiltered (file,
+                      "gdbarch_dump: displaced_step_location = <0x%lx>\n",
+                      (long) gdbarch->displaced_step_location);
+  fprintf_unfiltered (file,
                       "gdbarch_dump: double_bit = %s\n",
                       paddr_d (gdbarch->double_bit));
   fprintf_unfiltered (file,
@@ -805,6 +843,12 @@ gdbarch_dump (struct gdbarch *gdbarch, s
                       "gdbarch_dump: long_long_bit = %s\n",
                       paddr_d (gdbarch->long_long_bit));
   fprintf_unfiltered (file,
+                      "gdbarch_dump: gdbarch_max_insn_length_p() = %d\n",
+                      gdbarch_max_insn_length_p (gdbarch));
+  fprintf_unfiltered (file,
+                      "gdbarch_dump: max_insn_length = %s\n",
+                      paddr_d (gdbarch->max_insn_length));
+  fprintf_unfiltered (file,
                       "gdbarch_dump: memory_insert_breakpoint = <0x%lx>\n",
                       (long) gdbarch->memory_insert_breakpoint);
   fprintf_unfiltered (file,
@@ -2893,6 +2937,114 @@ set_gdbarch_skip_permanent_breakpoint (s
 }
 
 int
+gdbarch_max_insn_length_p (struct gdbarch *gdbarch)
+{
+  gdb_assert (gdbarch != NULL);
+  return gdbarch->max_insn_length != 0;
+}
+
+ULONGEST
+gdbarch_max_insn_length (struct gdbarch *gdbarch)
+{
+  gdb_assert (gdbarch != NULL);
+  /* Check variable changed from pre-default.  */
+  gdb_assert (gdbarch->max_insn_length != 0);
+  if (gdbarch_debug >= 2)
+    fprintf_unfiltered (gdb_stdlog, "gdbarch_max_insn_length called\n");
+  return gdbarch->max_insn_length;
+}
+
+void
+set_gdbarch_max_insn_length (struct gdbarch *gdbarch,
+                             ULONGEST max_insn_length)
+{
+  gdbarch->max_insn_length = max_insn_length;
+}
+
+int
+gdbarch_displaced_step_copy_insn_p (struct gdbarch *gdbarch)
+{
+  gdb_assert (gdbarch != NULL);
+  return gdbarch->displaced_step_copy_insn != NULL;
+}
+
+struct displaced_step_closure *
+gdbarch_displaced_step_copy_insn (struct gdbarch *gdbarch, CORE_ADDR from, CORE_ADDR to, struct regcache *regs)
+{
+  gdb_assert (gdbarch != NULL);
+  gdb_assert (gdbarch->displaced_step_copy_insn != NULL);
+  if (gdbarch_debug >= 2)
+    fprintf_unfiltered (gdb_stdlog, "gdbarch_displaced_step_copy_insn called\n");
+  return gdbarch->displaced_step_copy_insn (gdbarch, from, to, regs);
+}
+
+void
+set_gdbarch_displaced_step_copy_insn (struct gdbarch *gdbarch,
+                                      gdbarch_displaced_step_copy_insn_ftype displaced_step_copy_insn)
+{
+  gdbarch->displaced_step_copy_insn = displaced_step_copy_insn;
+}
+
+int
+gdbarch_displaced_step_fixup_p (struct gdbarch *gdbarch)
+{
+  gdb_assert (gdbarch != NULL);
+  return gdbarch->displaced_step_fixup != NULL;
+}
+
+void
+gdbarch_displaced_step_fixup (struct gdbarch *gdbarch, struct displaced_step_closure *closure, CORE_ADDR from, CORE_ADDR to, struct regcache *regs)
+{
+  gdb_assert (gdbarch != NULL);
+  gdb_assert (gdbarch->displaced_step_fixup != NULL);
+  /* Do not check predicate: gdbarch->displaced_step_fixup != NULL, allow call.  */
+  if (gdbarch_debug >= 2)
+    fprintf_unfiltered (gdb_stdlog, "gdbarch_displaced_step_fixup called\n");
+  gdbarch->displaced_step_fixup (gdbarch, closure, from, to, regs);
+}
+
+void
+set_gdbarch_displaced_step_fixup (struct gdbarch *gdbarch,
+                                  gdbarch_displaced_step_fixup_ftype displaced_step_fixup)
+{
+  gdbarch->displaced_step_fixup = displaced_step_fixup;
+}
+
+void
+gdbarch_displaced_step_free_closure (struct gdbarch *gdbarch, struct displaced_step_closure *closure)
+{
+  gdb_assert (gdbarch != NULL);
+  gdb_assert (gdbarch->displaced_step_free_closure != NULL);
+  if (gdbarch_debug >= 2)
+    fprintf_unfiltered (gdb_stdlog, "gdbarch_displaced_step_free_closure called\n");
+  gdbarch->displaced_step_free_closure (gdbarch, closure);
+}
+
+void
+set_gdbarch_displaced_step_free_closure (struct gdbarch *gdbarch,
+                                         gdbarch_displaced_step_free_closure_ftype displaced_step_free_closure)
+{
+  gdbarch->displaced_step_free_closure = displaced_step_free_closure;
+}
+
+CORE_ADDR
+gdbarch_displaced_step_location (struct gdbarch *gdbarch)
+{
+  gdb_assert (gdbarch != NULL);
+  gdb_assert (gdbarch->displaced_step_location != NULL);
+  if (gdbarch_debug >= 2)
+    fprintf_unfiltered (gdb_stdlog, "gdbarch_displaced_step_location called\n");
+  return gdbarch->displaced_step_location (gdbarch);
+}
+
+void
+set_gdbarch_displaced_step_location (struct gdbarch *gdbarch,
+                                     gdbarch_displaced_step_location_ftype displaced_step_location)
+{
+  gdbarch->displaced_step_location = displaced_step_location;
+}
+
+int
 gdbarch_overlay_update_p (struct gdbarch *gdbarch)
 {
   gdb_assert (gdbarch != NULL);
Index: src/gdb/gdbarch.h
===================================================================
--- src.orig/gdb/gdbarch.h	2008-04-25 18:16:05.000000000 +0100
+++ src/gdb/gdbarch.h	2008-04-25 18:17:00.000000000 +0100
@@ -50,6 +50,7 @@ struct target_ops;
 struct obstack;
 struct bp_target_info;
 struct target_desc;
+struct displaced_step_closure;
 
 extern struct gdbarch *current_gdbarch;
 
@@ -656,6 +657,95 @@ typedef void (gdbarch_skip_permanent_bre
 extern void gdbarch_skip_permanent_breakpoint (struct gdbarch *gdbarch, struct regcache *regcache);
 extern void set_gdbarch_skip_permanent_breakpoint (struct gdbarch *gdbarch, gdbarch_skip_permanent_breakpoint_ftype *skip_permanent_breakpoint);
 
+/* The maximum length of an instruction on this architecture. */
+
+extern int gdbarch_max_insn_length_p (struct gdbarch *gdbarch);
+
+extern ULONGEST gdbarch_max_insn_length (struct gdbarch *gdbarch);
+extern void set_gdbarch_max_insn_length (struct gdbarch *gdbarch, ULONGEST max_insn_length);
+
+/* Copy the instruction at FROM to TO, and make any adjustments
+   necessary to single-step it at that address.
+
+   REGS holds the state the thread's registers will have before
+   executing the copied instruction; the PC in REGS will refer to FROM,
+   not the copy at TO.  The caller should update it to point at TO later.
+
+   Return a pointer to data of the architecture's choice to be passed
+   to gdbarch_displaced_step_fixup.  Or, return NULL to indicate that
+   the instruction's effects have been completely simulated, with the
+   resulting state written back to REGS.
+
+   For a general explanation of displaced stepping and how GDB uses it,
+   see the comments in infrun.c.
+
+   The TO area is only guaranteed to have space for
+   gdbarch_max_insn_length (arch) bytes, so this function must not
+   write more bytes than that to that area.
+
+   If you do not provide this function, GDB assumes that the
+   architecture does not support displaced stepping.
+
+   If your architecture doesn't need to adjust instructions before
+   single-stepping them, consider using simple_displaced_step_copy_insn
+   here. */
+
+extern int gdbarch_displaced_step_copy_insn_p (struct gdbarch *gdbarch);
+
+typedef struct displaced_step_closure * (gdbarch_displaced_step_copy_insn_ftype) (struct gdbarch *gdbarch, CORE_ADDR from, CORE_ADDR to, struct regcache *regs);
+extern struct displaced_step_closure * gdbarch_displaced_step_copy_insn (struct gdbarch *gdbarch, CORE_ADDR from, CORE_ADDR to, struct regcache *regs);
+extern void set_gdbarch_displaced_step_copy_insn (struct gdbarch *gdbarch, gdbarch_displaced_step_copy_insn_ftype *displaced_step_copy_insn);
+
+/* Fix up the state resulting from successfully single-stepping a
+   displaced instruction, to give the result we would have gotten from
+   stepping the instruction in its original location.
+
+   REGS is the register state resulting from single-stepping the
+   displaced instruction.
+
+   CLOSURE is the result from the matching call to
+   gdbarch_displaced_step_copy_insn.
+
+   If you provide gdbarch_displaced_step_copy_insn.but not this
+   function, then GDB assumes that no fixup is needed after
+   single-stepping the instruction.
+
+   For a general explanation of displaced stepping and how GDB uses it,
+   see the comments in infrun.c. */
+
+extern int gdbarch_displaced_step_fixup_p (struct gdbarch *gdbarch);
+
+typedef void (gdbarch_displaced_step_fixup_ftype) (struct gdbarch *gdbarch, struct displaced_step_closure *closure, CORE_ADDR from, CORE_ADDR to, struct regcache *regs);
+extern void gdbarch_displaced_step_fixup (struct gdbarch *gdbarch, struct displaced_step_closure *closure, CORE_ADDR from, CORE_ADDR to, struct regcache *regs);
+extern void set_gdbarch_displaced_step_fixup (struct gdbarch *gdbarch, gdbarch_displaced_step_fixup_ftype *displaced_step_fixup);
+
+/* Free a closure returned by gdbarch_displaced_step_copy_insn.
+
+   If you provide gdbarch_displaced_step_copy_insn, you must provide
+   this function as well.
+
+   If your architecture uses closures that don't need to be freed, then
+   you can use simple_displaced_step_free_closure here.
+
+   For a general explanation of displaced stepping and how GDB uses it,
+   see the comments in infrun.c. */
+
+typedef void (gdbarch_displaced_step_free_closure_ftype) (struct gdbarch *gdbarch, struct displaced_step_closure *closure);
+extern void gdbarch_displaced_step_free_closure (struct gdbarch *gdbarch, struct displaced_step_closure *closure);
+extern void set_gdbarch_displaced_step_free_closure (struct gdbarch *gdbarch, gdbarch_displaced_step_free_closure_ftype *displaced_step_free_closure);
+
+/* Return the address of an appropriate place to put displaced
+   instructions while we step over them.  There need only be one such
+   place, since we're only stepping one thread over a breakpoint at a
+   time.
+
+   For a general explanation of displaced stepping and how GDB uses it,
+   see the comments in infrun.c. */
+
+typedef CORE_ADDR (gdbarch_displaced_step_location_ftype) (struct gdbarch *gdbarch);
+extern CORE_ADDR gdbarch_displaced_step_location (struct gdbarch *gdbarch);
+extern void set_gdbarch_displaced_step_location (struct gdbarch *gdbarch, gdbarch_displaced_step_location_ftype *displaced_step_location);
+
 /* Refresh overlay mapped state for section OSECT. */
 
 extern int gdbarch_overlay_update_p (struct gdbarch *gdbarch);
Index: src/gdb/inferior.h
===================================================================
--- src.orig/gdb/inferior.h	2008-04-25 18:16:05.000000000 +0100
+++ src/gdb/inferior.h	2008-04-25 18:17:00.000000000 +0100
@@ -387,6 +387,14 @@ extern struct regcache *stop_registers;
    than forked.  */
 
 extern int attach_flag;
+
+/* True if we are debugging displaced stepping.  */
+extern int debug_displaced;
+
+/* Dump LEN bytes at BUF in hex to FILE, followed by a newline.  */
+void displaced_step_dump_bytes (struct ui_file *file,
+                                const gdb_byte *buf, size_t len);
+
 
 /* Possible values for gdbarch_call_dummy_location.  */
 #define ON_STACK 1
Index: src/gdb/testsuite/gdb.asm/asmsrc1.s
===================================================================
--- src.orig/gdb/testsuite/gdb.asm/asmsrc1.s	2008-04-25 18:16:05.000000000 +0100
+++ src/gdb/testsuite/gdb.asm/asmsrc1.s	2008-04-25 18:17:00.000000000 +0100
@@ -16,6 +16,18 @@
 	gdbasm_exit0
 	gdbasm_end _start
 
+        comment "Displaced stepping requires scratch space at _start"
+        comment "at least as large as the largest instruction.  No"
+        comment "breakpoints should be set within the scratch space."
+        gdbasm_several_nops
+        gdbasm_several_nops
+        gdbasm_several_nops
+        gdbasm_several_nops
+        gdbasm_several_nops
+        gdbasm_several_nops
+        gdbasm_several_nops
+        gdbasm_several_nops
+
 	comment "main routine for assembly source debugging test"
 	comment "This particular testcase uses macros in <arch>.inc to achieve"
 	comment "machine independence."
Index: src/gdb/i386-linux-tdep.c
===================================================================
--- src.orig/gdb/i386-linux-tdep.c	2008-04-25 18:16:05.000000000 +0100
+++ src/gdb/i386-linux-tdep.c	2008-04-25 18:17:00.000000000 +0100
@@ -34,6 +34,7 @@
 #include "glibc-tdep.h"
 #include "solib-svr4.h"
 #include "symtab.h"
+#include "arch-utils.h"
 
 /* Return the name of register REG.  */
 
@@ -446,6 +447,15 @@ i386_linux_init_abi (struct gdbarch_info
   /* Enable TLS support.  */
   set_gdbarch_fetch_tls_load_module_address (gdbarch,
                                              svr4_fetch_objfile_link_map);
+
+  /* Displaced stepping.  */
+  set_gdbarch_displaced_step_copy_insn (gdbarch,
+                                        simple_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);
+  set_gdbarch_displaced_step_location (gdbarch,
+                                       displaced_step_at_entry_point);
 }
 
 /* Provide a prototype to silence -Wmissing-prototypes.  */
Index: src/gdb/doc/gdb.texinfo
===================================================================
--- src.orig/gdb/doc/gdb.texinfo	2008-04-25 18:16:05.000000000 +0100
+++ src/gdb/doc/gdb.texinfo	2008-04-25 18:17:00.000000000 +0100
@@ -16463,6 +16463,13 @@ Display debugging messages about inner w
 module.
 @item show debug aix-thread
 Show the current state of AIX thread debugging info display.
+@item set debug displaced
+@cindex displaced stepping debugging info
+Turns on or off display of @value{GDBN} debugging info for the
+displaced stepping support.  The default is off.
+@item show debug displaced
+Displays the current state of displaying @value{GDBN} debugging info
+related to displaced stepping.
 @item set debug event
 @cindex event debugging info
 Turns on or off display of @value{GDBN} event debugging info.  The
@@ -23139,6 +23146,18 @@ Shared library events.
 
 @end table
 
+@kindex maint set can-use-displaced-stepping
+@kindex maint show can-use-displaced-stepping
+@cindex displaced stepping support
+@item maint set can-use-displaced-stepping
+@itemx maint show can-use-displaced-stepping
+Control whether or not @value{GDBN} will do displaced stepping if the
+target supports it.  The default is on.  @dfn{Displaced stepping} is a
+way to single-step over breakpoints without removing them from the
+inferior, by executing an out-of-line copy of the instruction that was
+originally at the breakpoint location.  It is also known as
+out-of-line single-stepping.
+
 @kindex maint check-symtabs
 @item maint check-symtabs
 Check the consistency of psymtabs and symtabs.

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]