List Info

Thread: Stepping off breakpoints in non-stop debugging mode




Stepping off breakpoints in non-stop debugging mode
country flaguser name
United States
2007-12-08 03:23:38
In non-stop mode, breakpoint handling will require special
care.
Some issues which have come up in discussion:

- When a thread has hit a breakpoint and the user would like
to
  continue it, how can we allow the thread to execute the
  instruction the breakpoint replaced while still ensuring
that
  other threads will hit it?  All-stop GDB simply backs out
the
  breakpoint, steps the one thread by itself, reinserts the
  breakpoint, and lets everyone continue.  This doesn't work
in
  non-stop debugging mode, as other threads might miss the
  breakpoint.

  It would be nice to support the case where the instruction
being
  stepped past is an I/O instruction that will block until
some
  other thread takes some action.  In this case, it's
critical that
  the other threads really be allowed to run while we do the
step.
  For full credit, if the I/O instruction in question
performs a
  barrier synchronization, stopping each thread until all
threads
  have reached it, GDB should allow any number of threads to
execute
  the instruction simultaneously (after hitting the
breakpoint).

  Roland McGrath has discussed ways to change the kernel to
give
  certain threads their own copy of a particular page; GDB
would
  give each thread that should be allowed to continue its
own copy
  of the page with the breakpoint omitted, step it, and
then
  re-unify the thread's page table with the rest of the
process.

  My co-worker Carlos O'Donnell and I discussed a few
strategies for
  doing this; one idea was to continue to share the physical
page
  between all threads, but to make the page non-executable
for all but
  the stepping thread.  However, if the instruction being
stepped
  caused the thread to block until some other thread
executed some
  other instruction on the same page, this would cause a
deadlock.

  The Linux 'kprobes' facility implements non-stop debugging
in the
  Linux kernel.  kprobes handles this by copying the
instruction to
  a different address and executing it there, and then
fixing up the
  state for instructions that need it (say, pc-relative
  instructions).  It would probably be good for us to
follow
  kprobes' lead here, since that's known to work, and
doesn't
  require kernel hacking.

- On an SMP system, inserting a software breakpoint is
necessarily
  cross-modifying code, which Intel errata AH33 in
 
ftp://download.intel.com/design/mobile/SPECUPDT/31407915.pdf
says
  is no good.  However, Googling further revealed that,
according to
  Intel engineers, the 'int3' breakpoint instruction is
specifically
  exempted from this issue.  So we can go ahead and pitch
software
  breakpoints into code as we wish.

- Most GDB does not use read_memory_nobpt to read from
memory; this
  is usually not a problem in the current code because GDB
has
  removed the breakpoints before doing the reads.  In
non-stop
  debugging mode, the breakpoints will always be inserted,
so more
  code may trip over them.

[The above is cribbed from pre-existing notes; I think Vlad
may have
addressed the last point.]

For CodeSourcery's contract with Ericsson, I've implemented
the
kprobes strategy for stepping off breakpoints in GDB for the
i386; the
full patch is at the bottom of this message.  It introduces
no
regressions on i386, using displaced stepping for stepping
off all
breakpoints.  Combined with Vlad's work to leave breakpoints
inserted
at all times, this gives us breakpoint behavior suitable for
non-stop
debugging.

As far as the public GDB project is concerned, what do folks
think
about the kprobes approach?

Here are the general comments, from infrun.c:


/* 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.  */

If the kprobes approach seems suitable for GDB, here are the
gdbarch
methods I'm using.  Does this interface there seem
reasonable?


# 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)


I have some concerns with the current patch:

- The current implementation of
gdbarch_displaced_step_location is
  pretty questionable, but I'm not sure where else would be
better.

- It seems that it is never necessary to have more than one
thread
  doing displaced stepping at a time --- or else the assert
in
  displaced_step_prepare would trigger --- but I don't see
why this
  should be so.

- As noted in the comments, I assume that displaced stepping
and
  software single-step don't work together.  I haven't taken
this on
  at this point in the project because I'm guessing that
software
  single-step will need work to play nicely with non-stop
debugging
  anyway.

- Perhaps i386_syscall_p should be a gdbarch method, so that
ABIs can
  override it.


If anybody makes it this far, thank you very much.  Here is
the patch.
Note that the regenerated gdbarch.c and gdbarch.h are
omitted.

---

gdb/ChangeLog:
2007-12-07  Jim Blandy  <jimbcodesourcery.com>

	Implement displaced stepping.

	Define gdbarch methods for executing an instruction that
has been
	copied to an alternative location.
	* 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.
	* i386-tdep.c (debug_displaced): New variable.
	(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.
	(i386_gdbarch_init): Set gdbarch_max_insn_length.
	Register gdbarch_displaced_step_copy,
	gdbarch_displaced_step_fixup,
gdbarch_displaced_step_free_closure,
	and gdbarch_displaced_step_location functions.
	* i386-tdep.h (I386_TF_MASK, I386_MAX_INSN_LEN): New
constants.
	* 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.
	* Makefile.in (arch-utils.o): Update dependencies.
	* infrun.c (use_displaced_stepping, displaced_step_ptid)
	(displaced_step_gdbarch, displaced_step_closure)
	(displaced_step_original, displaced_step_copy)
	(displaced_step_saved_copy): New variables.
	(displaced_step_clear, cleanup_displaced_step_closure)
	(displaced_step_prepare, displaced_step_clear_cleanup)
	(displaced_step_fixup): New functions.
	(resume): Call displaced_step_prepare.
	(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.
	(init_wait_for_inferior): Call displaced_step_clear.
	(_initialize_infrun): Clear displaced_step_ptid.

	* infrun.c (resume): Call read_pc once, and remember the
value.

	* i386-tdep.c (I386_MAX_MATCHED_INSN_LEN): Renamed from
	I386_MAX_INSN_LEN.  Uses changed.

diff -r 3afe85e26f07 gdb/Makefile.in
--- a/gdb/Makefile.in	Fri Dec 07 13:39:22 2007 -0800
+++ b/gdb/Makefile.in	Thu Dec 06 22:26:48 2007 -0800
 -1888,7
+1888,7  arch-utils.o: arch-utils.c $(defs_h) $(a
 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) 
diff -r 3afe85e26f07 gdb/arch-utils.c
--- a/gdb/arch-utils.c	Fri Dec 07 13:39:22 2007 -0800
+++ b/gdb/arch-utils.c	Sat Dec 08 00:37:09 2007 -0800
 -31,11
+31,45 
 #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);
+
+  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)
+{
+  /* Make certain that the address points at real code, and
not a
+     function descriptor.  */
+  return gdbarch_convert_from_func_ptr_addr (gdbarch,
+                                            
entry_point_address (),
+                                            
&current_target);
+}
 
 int
 legacy_register_sim_regno (struct gdbarch *gdbarch, int
regnum)
diff -r 3afe85e26f07 gdb/arch-utils.h
--- a/gdb/arch-utils.h	Fri Dec 07 13:39:22 2007 -0800
+++ b/gdb/arch-utils.h	Thu Dec 06 22:26:49 2007 -0800
 -29,6
+29,29  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.  */
+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);
diff -r 3afe85e26f07 gdb/gdbarch.sh
--- a/gdb/gdbarch.sh	Fri Dec 07 13:39:22 2007 -0800
+++ b/gdb/gdbarch.sh	Sat Dec 08 00:44:48 2007 -0800
 -605,6
+605,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
 
 -724,6
+793,7  struct obstack;
 struct obstack;
 struct bp_target_info;
 struct target_desc;
+struct displaced_step_closure;
 
 extern struct gdbarch *current_gdbarch;
 EOF
diff -r 3afe85e26f07 gdb/i386-tdep.c
--- a/gdb/i386-tdep.c	Fri Dec 07 13:39:22 2007 -0800
+++ b/gdb/i386-tdep.c	Fri Dec 07 23:15:33 2007 -0800
 -284,6
+284,242  i386_breakpoint_from_pc (struct gdbarch 
   return break_insn;
 }
 
+/* Displaced instruction handling.  */
+
+
+static int debug_displaced = 0;
+
+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.  */
+static 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!  */
+
+  /* Clear the TF flag in EFLAGS, which will always be set.
 */
+  {
+    ULONGEST eflags;
+    regcache_cooked_read_unsigned (regs,
I386_EFLAGS_REGNUM, &eflags);
+    eflags &= ~I386_TF_MASK;
+    regcache_cooked_write_unsigned (regs,
I386_EFLAGS_REGNUM, eflags);
+  }
+
+  /* Relocate the %eip, if necessary.  */
+
+  /* In the case of absolute or indirect jump or call
instructions, or
+     a return instruction, the new %eip needs no
relocation.  */
+  if (i386_absolute_jmp_p (insn)
+      || i386_absolute_call_p (insn)
+      || i386_ret_p (insn))
+    ;
+
+  /* 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
relocatingn");
+        }
+      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
breakpointn");
+              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%sn",
+                                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%sn",
+                            paddr_nz (esp),
+                            paddr_nz (retaddr));
+    }
+}
+
+
+
 #ifdef I386_REGNO_TO_SYMMETRY
 #error "The Sequent Symmetry is no longer
supported."
 #endif
 -529,14
+765,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
 -555,12
+791,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);
 
 	  read_memory_nobpt (pc + 1, buf, insn->len - 1);
 	  for (i = 1; i < insn->len; i++)
 -2411,6
+2647,14  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_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);
 
   set_gdbarch_frame_args_skip (gdbarch, 8);
 
diff -r 3afe85e26f07 gdb/i386-tdep.h
--- a/gdb/i386-tdep.h	Fri Dec 07 13:39:22 2007 -0800
+++ b/gdb/i386-tdep.h	Sun Dec 02 11:15:24 2007 -0800
 -152,6
+152,9  enum i386_regnum
 /* Size of the largest register.  */
 #define I386_MAX_REGISTER_SIZE	16
 
+/* Bit for TF (trace flag) in EFLAGS register.  */
+#define I386_TF_MASK (0x00000100)
+
 /* Types for i386-specific registers.  */
 extern struct type *i386_eflags_type;
 extern struct type *i386_mxcsr_type;
 -163,6
+166,10  extern struct type *i386_sse_type (struc
 #define I386_SEL_RPL	0x0003  /* Requester's Privilege Level
mask.  */
 #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);
diff -r 3afe85e26f07 gdb/infrun.c
--- a/gdb/infrun.c	Fri Dec 07 13:39:22 2007 -0800
+++ b/gdb/infrun.c	Sat Dec 08 01:10:30 2007 -0800
 -472,6
+472,264  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.  */
+
+/* When this is non-zero, we use displaced stepping.  When
this is
+   zero, we use traditional the hold-and-step approach. 
*/
+int use_displaced_stepping = 1;
+
+/* 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;
+
+/* 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;
+
+/* 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);
+}
+
+/* Prepare to single-step, using displaced stepping.  */
+static void
+displaced_step_prepare (ptid_t ptid, enum target_signal
sig)
+{
+  struct cleanup *old_cleanups;
+  struct regcache *regcache = get_thread_regcache (ptid);
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
+  CORE_ADDR original, copy;
+  ULONGEST len;
+  gdb_byte *saved_copy;
+  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 ruling out displaced stepping
for more
+     than one thread at a time.  */
+  gdb_assert (ptid_equal (displaced_step_ptid,
null_ptid));
+
+  displaced_step_clear ();
+
+  original = read_pc_pid (ptid);
+
+  /* If we have a breakpoint inserted at this address,
which we have
+     decided to step at (GDB does do this from time to
time), there's no
+     need to displace the instruction.  */
+  if (breakpoint_inserted_here_p (original))
+    return;
+
+  /* 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.
+
+     However, if we went into a signal handler, we must not
try to fix
+     up the resulting state --- we never executed the
instruction we
+     displaced.  So in order to use displaced stepping, we
would need
+     to distinguish these two cases.
+
+     We avoid this problem simply by not using displaced
stepping when
+     we're delivering a signal.  We leave the PC at the
original
+     address, and ensure that a step-resume breakpoint is
set and
+     inserted there; see infrun.c.  If we get the trap and
the PC is
+     pointing at the breakpoint, then we'll just start the
step again
+     from scratch --- but without the signal this time, as
we know
+     it's been dealt with.
+
+     But let's check that this is working as intended.  At
this point
+     we know we don't have a breakpoint inserted at the PC,
so there
+     had better not be a signal.  */
+  gdb_assert (sig == TARGET_SIGNAL_0);
+
+  copy = gdbarch_displaced_step_location (gdbarch);
+  len = gdbarch_max_insn_length (gdbarch);
+
+  /* Save the original contents of the copy area.  */
+  saved_copy = xmalloc (len);
+  old_cleanups = make_cleanup (xfree, saved_copy);
+  read_memory (copy, 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_infrun)
+    fprintf_unfiltered (gdb_stdlog, "infrun: displaced
pc to 0x%sn",
+                        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;
+  displaced_step_saved_copy = saved_copy;
+}
+
+static void
+displaced_step_clear_cleanup (void *ignore)
+{
+  displaced_step_clear ();
+}
+
+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 (displaced_step_copy,
displaced_step_saved_copy, len);
+  }
+
+  /* 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);
+}
+
+
+/* Resuming.  */
 
 /* Things to clean up if we QUIT out of resume ().  */
 static void
 -523,14
+781,12  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! */
-
 
   /* Some targets (e.g. Solaris x86) have a kernel bug when
stepping
      over an instruction that causes a page fault without
triggering
 -548,7
+804,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,
 -559,6
+815,9  how to step past a permanent breakpoint 
 how to step past a permanent breakpoint on this
architecture.  Try usingn
 a command like `return' or `jump' to continue
execution."));
     }
+
+  if (stepping_over_breakpoint &&
use_displaced_stepping)
+    displaced_step_prepare (inferior_ptid, sig);
 
   if (step && gdbarch_software_single_step_p
(current_gdbarch))
     {
 -571,7
+830,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;
         }
     }
 
 -627,8
+886,8  a command like `return' or `jump' to con
 	}
 
       if ((step || singlestep_breakpoints_inserted_p)
-	  && breakpoint_here_p (read_pc ())
-	  && !breakpoint_inserted_here_p (read_pc ()))
+	  && breakpoint_here_p (pc)
+	  && !breakpoint_inserted_here_p (pc))
 	{
 	  /* We're stepping, have breakpoint at PC, and it's 
 	     not inserted.  Most likely, proceed has noticed that
 -656,7
+915,7  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;
 	}
       target_resume (resume_ptid, step, sig);
 -665,6
+924,7  a command like `return' or `jump' to con
   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'.  */
 -914,7
+1174,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
 -1601,6
+1864,11  handle_inferior_event (struct execution_
       prepare_to_wait (ecs);
       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);
 
 -2083,7
+2351,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.  */
 -2091,8
+2359,18  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 "
+                                "breakpointn");
 
 	  insert_step_resume_breakpoint_at_frame
(get_current_frame ());
+          /* We cannot use displaced stepping when we
deliver a
+             signal, so make sure the step-resume
breakpoint is
+             inserted early. The comments in
displaced_step_prepare
+             explain why this is necessary.  */
+          if (use_displaced_stepping)
+            insert_breakpoints ();
 	  ecs->step_after_step_resume_breakpoint = 1;
 	  keep_going (ecs);
 	  return;
 -2114,6
+2392,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
rangen");
+
 	  insert_step_resume_breakpoint_at_frame
(get_current_frame ());
 	  keep_going (ecs);
 	  return;
 -4167,4
+4450,5  function is skipped and the step command
   minus_one_ptid = ptid_build (-1, 0, 0);
   inferior_ptid = null_ptid;
   target_last_wait_ptid = minus_one_ptid;
-}
+  displaced_step_ptid = null_ptid;
+}
diff -r 3afe85e26f07 gdb/linux-thread-db.c
--- a/gdb/linux-thread-db.c	Fri Dec 07 13:39:22 2007 -0800
+++ b/gdb/linux-thread-db.c	Fri Dec 07 23:08:52 2007 -0800
 -648,6
+648,7  check_for_thread_db (void)
 
       enable_thread_event_reporting ();
       thread_db_find_new_threads ();
+      inferior_ptid = thread_from_lwp (inferior_ptid);
       break;
 
     default:

Re: Stepping off breakpoints in non-stop debugging mode
country flaguser name
United States
2007-12-08 11:03:59
On Sat, Dec 08, 2007 at 01:23:38AM -0800, Jim Blandy wrote:
> - The current implementation of
gdbarch_displaced_step_location is
>   pretty questionable, but I'm not sure where else
would be better.

Ditto.  This may do.

> - It seems that it is never necessary to have more than
one thread
>   doing displaced stepping at a time --- or else the
assert in
>   displaced_step_prepare would trigger --- but I don't
see why this
>   should be so.

Isn't this because you haven't been testing combined with
Vladimir's
leave-breakpoints-inserted code?  resume is still going to
force only
the current thread to resume and wait, so the single step
will finish
before anything else gets a chance to hit a new breakpoint. 
Combine
those two patches, this will start happening.  Add non-stop
debugging
and it will happen even more.

> +static int
> +i386_syscall_p (gdb_byte *insn, ULONGEST *lengthp)
> +{
> +  if (insn[0] == 0xcd)
> +    {
> +      *lengthp = 2;
> +      return 1;
> +    }
> +
> +  return 0;
> +}

That's int, I assume.  May need sysenter / syscall,
depending on the
platform.

> +  /* 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!  */
> +
> +  /* Clear the TF flag in EFLAGS, which will always be
set.  */
> +  {
> +    ULONGEST eflags;
> +    regcache_cooked_read_unsigned (regs,
I386_EFLAGS_REGNUM, &eflags);
> +    eflags &= ~I386_TF_MASK;
> +    regcache_cooked_write_unsigned (regs,
I386_EFLAGS_REGNUM, eflags);
> +  }

Does this manual adjustment of TF apply to GDB?  The kernel
is
supposed to handle TF entirely inside ptrace, and expose the
original
%eflags to GDB (though various kernel versions have gotten
this wrong,
I believe it is right at last).  So if TF is set here, that
means the
program being debugged had TF set already.

> +  /* Relocate the %eip, if necessary.  */
> +
> +  /* In the case of absolute or indirect jump or call
instructions, or
> +     a return instruction, the new %eip needs no
relocation.  */
> +  if (i386_absolute_jmp_p (insn)
> +      || i386_absolute_call_p (insn)
> +      || i386_ret_p (insn))
> +    ;
> +
> +  /* 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))

These two if statements look quite strange together.

-- 
Daniel Jacobowitz
CodeSourcery

Re: Stepping off breakpoints in non-stop debugging mode
country flaguser name
United States
2007-12-10 17:05:01
Daniel Jacobowitz <drow at false.org> writes:
>> - It seems that it is never necessary to have more
than one thread
>>   doing displaced stepping at a time --- or else
the assert in
>>   displaced_step_prepare would trigger --- but I
don't see why this
>>   should be so.
>
> Isn't this because you haven't been testing combined
with Vladimir's
> leave-breakpoints-inserted code?  resume is still going
to force only
> the current thread to resume and wait, so the single
step will finish
> before anything else gets a chance to hit a new
breakpoint.  Combine
> those two patches, this will start happening.  Add
non-stop debugging
> and it will happen even more.

I don't think Vlad's leave-breakpoints-inserted code will
change that
aspect of the thread management.

I'm not sure that it must happen even in non-stop mode ---
GDB can
step threads that have hit breakpoints past their
breakpoints one at a
time.  I would *think* that would be the less ambitious
approach.

>> +static int
>> +i386_syscall_p (gdb_byte *insn, ULONGEST
*lengthp)
>> +{
>> +  if (insn[0] == 0xcd)
>> +    {
>> +      *lengthp = 2;
>> +      return 1;
>> +    }
>> +
>> +  return 0;
>> +}
>
> That's int, I assume.  May need sysenter / syscall,
depending on the
> platform.

Right.  This shows up in sigstep.exp, where we single-step
out of a
signal handler: that system call is interesting to us
because it
actually does a transfer of control (back to the context
that was
interrupted by the signal).  We mustn't relocate the PC
after it's
done, because the PC has been set correctly by the syscall.

If signal handler trampolines can use sysenter / syscall,
then we'll
need to recognize those, too.  (Can they?)

>> +  /* 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!  */
>> +
>> +  /* Clear the TF flag in EFLAGS, which will
always be set.  */
>> +  {
>> +    ULONGEST eflags;
>> +    regcache_cooked_read_unsigned (regs,
I386_EFLAGS_REGNUM, &eflags);
>> +    eflags &= ~I386_TF_MASK;
>> +    regcache_cooked_write_unsigned (regs,
I386_EFLAGS_REGNUM, eflags);
>> +  }
>
> Does this manual adjustment of TF apply to GDB?  The
kernel is
> supposed to handle TF entirely inside ptrace, and
expose the original
> %eflags to GDB (though various kernel versions have
gotten this wrong,
> I believe it is right at last).  So if TF is set here,
that means the
> program being debugged had TF set already.

It does apply to GDB!  You can write a statement that does
"pushfl;
popl %0" and behaves differently depending on whether
you continue
over it or step over it.

>> +  /* Relocate the %eip, if necessary.  */
>> +
>> +  /* In the case of absolute or indirect jump or
call instructions, or
>> +     a return instruction, the new %eip needs no
relocation.  */
>> +  if (i386_absolute_jmp_p (insn)
>> +      || i386_absolute_call_p (insn)
>> +      || i386_ret_p (insn))
>> +    ;
>> +
>> +  /* 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))
>
> These two if statements look quite strange together.

Oops.  The first is detritus.  Thanks.

Re: Stepping off breakpoints in non-stop debugging mode
country flaguser name
United States
2007-12-10 17:17:11
On Mon, Dec 10, 2007 at 03:05:01PM -0800, Jim Blandy wrote:
> If signal handler trampolines can use sysenter /
syscall, then we'll
> need to recognize those, too.  (Can they?)

Yes, I think so.  It depends on the kernel version and the
vDSO
variant it selected.  Maybe not, though - there was some
trouble about
getting the right registers saved.

> >> +  /* 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!  */
> >> +
> >> +  /* Clear the TF flag in EFLAGS, which will
always be set.  */
> >> +  {
> >> +    ULONGEST eflags;
> >> +    regcache_cooked_read_unsigned (regs,
I386_EFLAGS_REGNUM, &eflags);
> >> +    eflags &= ~I386_TF_MASK;
> >> +    regcache_cooked_write_unsigned (regs,
I386_EFLAGS_REGNUM, eflags);
> >> +  }
> >
> > Does this manual adjustment of TF apply to GDB? 
The kernel is
> > supposed to handle TF entirely inside ptrace, and
expose the original
> > %eflags to GDB (though various kernel versions
have gotten this wrong,
> > I believe it is right at last).  So if TF is set
here, that means the
> > program being debugged had TF set already.
> 
> It does apply to GDB!  You can write a statement that
does "pushfl;
> popl %0" and behaves differently depending on
whether you continue
> over it or step over it.

You're not supposed to be able to do that.  The kernel goes
to great
trouble to make sure that you can PTRACE_SINGLESTEP through
such a
sequence and have the same thing happen that would if no
debugger
was attached.  My rule of thumb is that every time you have
to
explicitly modify TF, you're clobbering the program's state.
 Am
I wrong?

See is_setting_trap_flag in the kernel.  Of course, it
leaves the
pushf case for debuggers to handle if they want to.  Wine
probably
does.

-- 
Daniel Jacobowitz
CodeSourcery

Re: Stepping off breakpoints in non-stop debugging mode
country flaguser name
United States
2007-12-11 12:24:44
Daniel Jacobowitz <drow at false.org> writes:
> On Mon, Dec 10, 2007 at 03:05:01PM -0800, Jim Blandy
wrote:
>> If signal handler trampolines can use sysenter /
syscall, then we'll
>> need to recognize those, too.  (Can they?)
>
> Yes, I think so.  It depends on the kernel version and
the vDSO
> variant it selected.  Maybe not, though - there was
some trouble about
> getting the right registers saved.
>
>> >> +  /* 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!  */
>> >> +
>> >> +  /* Clear the TF flag in EFLAGS, which
will always be set.  */
>> >> +  {
>> >> +    ULONGEST eflags;
>> >> +    regcache_cooked_read_unsigned (regs,
I386_EFLAGS_REGNUM, &eflags);
>> >> +    eflags &= ~I386_TF_MASK;
>> >> +    regcache_cooked_write_unsigned (regs,
I386_EFLAGS_REGNUM, eflags);
>> >> +  }
>> >
>> > Does this manual adjustment of TF apply to
GDB?  The kernel is
>> > supposed to handle TF entirely inside ptrace,
and expose the original
>> > %eflags to GDB (though various kernel versions
have gotten this wrong,
>> > I believe it is right at last).  So if TF is
set here, that means the
>> > program being debugged had TF set already.
>> 
>> It does apply to GDB!  You can write a statement
that does "pushfl;
>> popl %0" and behaves differently depending on
whether you continue
>> over it or step over it.
>
> You're not supposed to be able to do that.  The kernel
goes to great
> trouble to make sure that you can PTRACE_SINGLESTEP
through such a
> sequence and have the same thing happen that would if
no debugger
> was attached.  My rule of thumb is that every time you
have to
> explicitly modify TF, you're clobbering the program's
state.  Am
> I wrong?

You're right in your original assertion, that that code
isn't
necessary where I had it.  I've taken it out.

As far as PTRACE_SINGLESTEP is concerned, isn't pushfl the
only
user instruction that can observe the state of TF anyway?

Here's the test program I was using.  What am I seeing
here?

  $ uname -a
  Linux pip 2.6.22.9-61.fc6 #1 SMP Thu Sep 27 18:48:03 EDT
2007 i686 i686 i386 GNU/Linux
  $ cat pushfl.c
  #include <stdio.h>

  int
  main (int argc, char **argv)
  {
    unsigned int flags;

    asm ("pushfl; popl %0" : "=r"
(flags));

    printf ("0x%08xn", flags);

    return 0;
  }
  $ ~/gdb/pub/nat/gdb/gdb pushfl
  GNU gdb 6.7.50.20071206-cvs
  Copyright (C) 2007 Free Software Foundation, Inc.
  License GPLv3+: GNU GPL version 3 or later <http://gnu.org/l
icenses/gpl.html>
  This is free software: you are free to change and
redistribute it.
  There is NO WARRANTY, to the extent permitted by law. 
Type "show copying"
  and "show warranty" for details.
  This GDB was configured as
"i686-pc-linux-gnu"...
  (gdb) start
  Breakpoint 1 at 0x8048390: file pushfl.c, line 5.
  Starting program: /home/jimb/gdb/ool/play/pushfl 
  main () at pushfl.c:5
  5       {
  (gdb) step
  main () at pushfl.c:8
  8         asm ("pushfl; popl %0" :
"=r" (flags));
  (gdb) 
  5       {
  (gdb) 
  10        printf ("0x%08xn", flags);
  (gdb) c
  Continuing.
  0x00000386

  Program exited normally.
  (gdb) start
  Breakpoint 2 at 0x8048390: file pushfl.c, line 5.
  Starting program: /home/jimb/gdb/ool/play/pushfl 
  main () at pushfl.c:5
  5       {
  (gdb) c
  Continuing.
  0x00000282

  Program exited normally.
  (gdb) 


Re: Stepping off breakpoints in non-stop debugging mode
country flaguser name
United States
2007-12-11 12:44:05
On Tue, Dec 11, 2007 at 10:24:44AM -0800, Jim Blandy wrote:
> As far as PTRACE_SINGLESTEP is concerned, isn't pushfl
the only
> user instruction that can observe the state of TF
anyway?

I can't answer that.  Maybe Paul could.  The focus of the
kernel code
I was referring to is instructions which modify the state of
TF;
you can observe changes by telling whether you get a trap,
if
your debugger's signal handling is good enough.  The
debugger can do
a software single step over the pushfl instruction if it
cares about
accuracy enough to bother, and/or fix up the resulting stack
value.

-- 
Daniel Jacobowitz
CodeSourcery

Re: Stepping off breakpoints in non-stop debugging mode
country flaguser name
United States
2007-12-16 21:18:09
On Sat, 2007-12-08 at 01:23 -0800, Jim Blandy wrote:
> For CodeSourcery's contract with Ericsson, I've
implemented the
> kprobes strategy for stepping off breakpoints in GDB
for the i386; the
> full patch is at the bottom of this message.  It
introduces no
> regressions on i386, using displaced stepping for
stepping off all
> breakpoints.  Combined with Vlad's work to leave
breakpoints inserted
> at all times, this gives us breakpoint behavior
suitable for non-stop
> debugging.
> 
> As far as the public GDB project is concerned, what do
folks think
> about the kprobes approach?

Apologies if if you've already thought these through...

Did you give any thoughts to the x86_64 instructions? I
don't actually
know much about them, but in this e-mail

http://sourceware.org/ml/systemtap/2007-q1/msg00571.html


Roland McGrath mentions some complexities involved in
pc-relative
addressing there, which don't exist in the i386 case. Also,
he is
concerned that instruction parsing would need to be
carefully done.

So, is it possible that this technique cannot be done for
some
instructions? If so, a mechanism for emulating them or
falling back to
all-stop mode for that specific breakpoint could be used
(this is the
approach taken by Frysk, AFAIK).

I looked at the Power ISA document and it is similar to the
i386 in that
only flow control insns are of concern. I'll try to confirm
this with
someone more experienced than me, however.

> If anybody makes it this far, thank you very much.

Two or three times, even. It's not easy for this stuff to
sink in. 

-- 
[]'s
Thiago Jung Bauermann
Software Engineer
IBM Linux Technology Center


Re: Stepping off breakpoints in non-stop debugging mode
country flaguser name
United States
2007-12-17 13:41:35
Thiago Jung Bauermann <bauerman at br.ibm.com>
writes:
> On Sat, 2007-12-08 at 01:23 -0800, Jim Blandy wrote:
>> For CodeSourcery's contract with Ericsson, I've
implemented the
>> kprobes strategy for stepping off breakpoints in
GDB for the i386; the
>> full patch is at the bottom of this message.  It
introduces no
>> regressions on i386, using displaced stepping for
stepping off all
>> breakpoints.  Combined with Vlad's work to leave
breakpoints inserted
>> at all times, this gives us breakpoint behavior
suitable for non-stop
>> debugging.
>> 
>> As far as the public GDB project is concerned, what
do folks think
>> about the kprobes approach?
>
> Apologies if if you've already thought these
through...
>
> Did you give any thoughts to the x86_64 instructions? I
don't actually
> know much about them, but in this e-mail
>
> http://sourceware.org/ml/systemtap/2007-q1/msg00571.html

>
> Roland McGrath mentions some complexities involved in
pc-relative
> addressing there, which don't exist in the i386 case.
Also, he is
> concerned that instruction parsing would need to be
carefully done.

This is a great find --- thanks very much.  I was aware of
the
PC-relative addressing on the x86_64, but figured we'd cross
that
bridge when we came to it.  From what Roland has written, it
seems
that will be quite involved.

The interface I posted does allow the 'copy_insn' gdbarch
method to
simply say, "I've taken care of emulating this
instruction for you".
I don't know yet if that's practical in all the cases we
care about.

[1-8]

about | contact  Other archives ( Real Estate discussion Medical topics )