* Stepping off breakpoints in non-stop debugging mode (resubmit)
@ 2008-04-10 3:14 Pedro Alves
2008-04-10 6:23 ` Eli Zaretskii
0 siblings, 1 reply; 16+ messages in thread
From: Pedro Alves @ 2008-04-10 3:14 UTC (permalink / raw)
To: gdb-patches
[-- Attachment #1: Type: text/plain, Size: 1851 bytes --]
This is an update of Jim Blandy's patch described here:
[Stepping off breakpoints in non-stop debugging mode]
http://sourceware.org/ml/gdb/2007-12/msg00064.html
The differences to the earlier patch are:
- Enable displaced stepping on x86-pc-linux-gnu instead of
on all x86 targets, for two reasons. 1) x86_64 was
inheriting the setting, which meant it broke because we
don't handle any x86_64 specific instructions or modes.
2) We found that the choice of the a few bytes off of the
entry point for scratchspace breaks on the Ericsson OS
we are also implementing non-stop for. It is reasonable to
think that is can break on other targets too. Since this
is needed for non-stop only, it can be switch on on other
targets on a need basis. OTOH, the Ericsson OS doesn't
really need this, since in that case the thread specific
breakpoints are handled on the target/stub side.
- In non-stop mode, we can have more than one
thread stepping over a breakpoint. Since the current
implementation exposes only one scratch space for the
whole process, we needed to serialize the stepping over
breakpoints. If/when a target exposes a scratchspace
per thread, we can add a gdbarch method/property to
enable/disable the serialization.
- There was still one place in the earlier version where
we were still removing breakpoints from the
target. There should be none in this version.
- Added "maint set can-use-displaced-stepping" -- default on,
and "set debug displaced" commands. Documented them.
- Really tested with non-stop mode this time.
This patch applies on top of Vladimir's "breakpoints always
inserted" patch.
Tested on x86-pc-linux-gnu and x86_64-unknown-linux-gnu to
ensure nothing broke there.
Does it look OK to apply after Vlad's patch has gone in?
--
Pedro Alves
[-- Attachment #2: displaced_stepping.diff --]
[-- Type: text/x-diff, Size: 66029 bytes --]
2008-04-10 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.
* Makefile.in (arch-utils.o): Update dependencies.
* 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.
* 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.
* i386-linux-tdep.c (i386_linux_init_abi): Register
gdbarch_displaced_step_copy, gdbarch_displaced_step_fixup,
gdbarch_displaced_step_free_closure, and
gdbarch_displaced_step_location functions.
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 | 15 +
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 | 531 ++++++++++++++++++++++++++++++++++++++--
gdb/testsuite/gdb.asm/asmsrc1.s | 12
13 files changed, 1177 insertions(+), 32 deletions(-)
Index: src/gdb/Makefile.in
===================================================================
--- src.orig/gdb/Makefile.in 2008-04-09 23:19:15.000000000 +0100
+++ src/gdb/Makefile.in 2008-04-09 23:19:17.000000000 +0100
@@ -1892,7 +1892,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) \
@@ -2229,7 +2229,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-09 22:55:27.000000000 +0100
+++ src/gdb/arch-utils.c 2008-04-09 23:19:17.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, ¤t_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-09 22:55:27.000000000 +0100
+++ src/gdb/arch-utils.h 2008-04-09 23:19:17.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-09 22:55:27.000000000 +0100
+++ src/gdb/gdbarch.sh 2008-04-09 23:19:17.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-09 22:55:27.000000000 +0100
+++ src/gdb/i386-tdep.c 2008-04-09 23:19:17.000000000 +0100
@@ -276,6 +276,225 @@ i386_breakpoint_from_pc (struct gdbarch
return break_insn;
}
\f
+/* 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));
+ }
+}
+
+
+\f
#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++)
@@ -2434,6 +2653,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-09 22:55:27.000000000 +0100
+++ src/gdb/i386-tdep.h 2008-04-09 23:19:17.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-09 23:19:15.000000000 +0100
+++ src/gdb/infrun.c 2008-04-09 23:26:48.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;
\f
+/* 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 indendent and simultanous 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);
+ }
+}
+
+\f
+/* 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);
}
\f
+/* 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'. */
@@ -790,14 +1202,18 @@ proceed (CORE_ADDR addr, enum target_sig
/* 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 breakpoints are always inserted, we'll trap if trying to
+ single-step over breakpoint. Disable all breakpoints. If we
+ can use displaced stepping, then we don't need to remove the
+ breakpoints. */
+ 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 +1324,10 @@ init_wait_for_inferior (void)
deferred_step_ptid = null_ptid;
target_last_wait_ptid = minus_one_ptid;
+
+ displaced_step_clear ();
}
+
\f
/* 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
@@ -1585,10 +2004,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 (¤t_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)
{
@@ -1736,7 +2176,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");
@@ -1751,7 +2191,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
@@ -1815,9 +2259,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
@@ -1920,10 +2361,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))
@@ -2048,7 +2493,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. */
@@ -2056,6 +2501,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;
@@ -2079,6 +2528,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;
@@ -2911,7 +3365,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
{
@@ -4014,6 +4472,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 *)
@@ -4109,9 +4575,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-09 22:55:27.000000000 +0100
+++ src/gdb/gdbarch.c 2008-04-09 23:19:17.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-09 22:55:27.000000000 +0100
+++ src/gdb/gdbarch.h 2008-04-09 23:19:17.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-09 22:55:27.000000000 +0100
+++ src/gdb/inferior.h 2008-04-09 23:19:17.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);
+
\f
/* 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-09 22:55:27.000000000 +0100
+++ src/gdb/testsuite/gdb.asm/asmsrc1.s 2008-04-09 23:19:17.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-09 22:55:27.000000000 +0100
+++ src/gdb/i386-linux-tdep.c 2008-04-09 23:19:17.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-09 23:19:15.000000000 +0100
+++ src/gdb/doc/gdb.texinfo 2008-04-09 23:19:17.000000000 +0100
@@ -16432,6 +16432,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 @value{GDBN} displaced stepping
+debugging info.
@item set debug event
@cindex event debugging info
Turns on or off display of @value{GDBN} event debugging info. The
@@ -23090,6 +23097,14 @@ 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.
+
@kindex maint check-symtabs
@item maint check-symtabs
Check the consistency of psymtabs and symtabs.
^ permalink raw reply [flat|nested] 16+ messages in thread* Re: Stepping off breakpoints in non-stop debugging mode (resubmit) 2008-04-10 3:14 Stepping off breakpoints in non-stop debugging mode (resubmit) Pedro Alves @ 2008-04-10 6:23 ` Eli Zaretskii 2008-04-10 14:02 ` Pedro Alves 0 siblings, 1 reply; 16+ messages in thread From: Eli Zaretskii @ 2008-04-10 6:23 UTC (permalink / raw) To: Pedro Alves; +Cc: gdb-patches > From: Pedro Alves <pedro@codesourcery.com> > Date: Thu, 10 Apr 2008 00:03:05 +0100 > > Does it look OK to apply after Vlad's patch has gone in? The documentation needs more work. It needs to explain what is "displaced stepping". Without such an explanation, the text you wrote simply cannot make sense to the reader. Also, this: > +Displays the current state of @value{GDBN} displaced stepping > +debugging info. leaves me wondering what is meant by ``the state ... of info''. How can info have a state? Thanks. ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: Stepping off breakpoints in non-stop debugging mode (resubmit) 2008-04-10 6:23 ` Eli Zaretskii @ 2008-04-10 14:02 ` Pedro Alves 2008-04-10 20:12 ` Eli Zaretskii 0 siblings, 1 reply; 16+ messages in thread From: Pedro Alves @ 2008-04-10 14:02 UTC (permalink / raw) To: gdb-patches, Eli Zaretskii [-- Attachment #1: Type: text/plain, Size: 1702 bytes --] A Thursday 10 April 2008 04:12:40, Eli Zaretskii wrote: > > From: Pedro Alves <pedro@codesourcery.com> > > Date: Thu, 10 Apr 2008 00:03:05 +0100 > > > > Does it look OK to apply after Vlad's patch has gone in? > > The documentation needs more work. It needs to explain what is > "displaced stepping". Without such an explanation, the text you wrote > simply cannot make sense to the reader. > How about this? @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. 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 under the breakpoint. It is also known as out-of-line single-stepping. > Also, this: > > +Displays the current state of @value{GDBN} displaced stepping > > +debugging info. > > leaves me wondering what is meant by ``the state ... of info''. How > can info have a state? > I was trying to follow the trend here: @item show debug serial Displays the current state of displaying @value{GDBN} serial debugging info. @item show debug target Displays the current state of displaying @value{GDBN} target debugging info. (etc.) Turns out I'm not a great follower. I happened to miss the "displaying" word. Should be: +Displays the current state of displaying @value{GDBN} displaced stepping +debugging info. A couple of typos were also fixed in comments. Updated patch attached. -- Pedro Alves [-- Attachment #2: displaced_stepping.diff --] [-- Type: text/x-diff, Size: 66292 bytes --] 2008-04-10 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. * Makefile.in (arch-utils.o): Update dependencies. * 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. * 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. * i386-linux-tdep.c (i386_linux_init_abi): Register gdbarch_displaced_step_copy, gdbarch_displaced_step_fixup, gdbarch_displaced_step_free_closure, and gdbarch_displaced_step_location functions. 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 | 18 + 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 | 531 ++++++++++++++++++++++++++++++++++++++-- gdb/testsuite/gdb.asm/asmsrc1.s | 12 13 files changed, 1180 insertions(+), 32 deletions(-) Index: src/gdb/Makefile.in =================================================================== --- src.orig/gdb/Makefile.in 2008-04-09 23:19:15.000000000 +0100 +++ src/gdb/Makefile.in 2008-04-09 23:19:17.000000000 +0100 @@ -1892,7 +1892,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) \ @@ -2229,7 +2229,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-09 22:55:27.000000000 +0100 +++ src/gdb/arch-utils.c 2008-04-09 23:19:17.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, ¤t_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-09 22:55:27.000000000 +0100 +++ src/gdb/arch-utils.h 2008-04-09 23:19:17.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-09 22:55:27.000000000 +0100 +++ src/gdb/gdbarch.sh 2008-04-09 23:19:17.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-09 22:55:27.000000000 +0100 +++ src/gdb/i386-tdep.c 2008-04-09 23:19:17.000000000 +0100 @@ -276,6 +276,225 @@ i386_breakpoint_from_pc (struct gdbarch return break_insn; } \f +/* 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)); + } +} + + +\f #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++) @@ -2434,6 +2653,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-09 22:55:27.000000000 +0100 +++ src/gdb/i386-tdep.h 2008-04-09 23:19:17.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-09 23:19:15.000000000 +0100 +++ src/gdb/infrun.c 2008-04-10 11:29:23.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; \f +/* 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); + } +} + +\f +/* 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); } \f +/* 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'. */ @@ -790,14 +1202,18 @@ proceed (CORE_ADDR addr, enum target_sig /* 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 breakpoints are always inserted, we'll trap if trying to + single-step over breakpoint. Disable all breakpoints. If we + can use displaced stepping, then we don't need to remove the + breakpoints. */ + 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 +1324,10 @@ init_wait_for_inferior (void) deferred_step_ptid = null_ptid; target_last_wait_ptid = minus_one_ptid; + + displaced_step_clear (); } + \f /* 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 @@ -1585,10 +2004,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 (¤t_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) { @@ -1736,7 +2176,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"); @@ -1751,7 +2191,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 @@ -1815,9 +2259,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 @@ -1920,10 +2361,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)) @@ -2048,7 +2493,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. */ @@ -2056,6 +2501,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; @@ -2079,6 +2528,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; @@ -2911,7 +3365,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 { @@ -4014,6 +4472,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 *) @@ -4109,9 +4575,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-09 22:55:27.000000000 +0100 +++ src/gdb/gdbarch.c 2008-04-09 23:19:17.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-09 22:55:27.000000000 +0100 +++ src/gdb/gdbarch.h 2008-04-09 23:19:17.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-09 22:55:27.000000000 +0100 +++ src/gdb/inferior.h 2008-04-09 23:19:17.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); + \f /* 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-09 22:55:27.000000000 +0100 +++ src/gdb/testsuite/gdb.asm/asmsrc1.s 2008-04-09 23:19:17.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-09 22:55:27.000000000 +0100 +++ src/gdb/i386-linux-tdep.c 2008-04-09 23:19:17.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-09 23:19:15.000000000 +0100 +++ src/gdb/doc/gdb.texinfo 2008-04-10 11:45:42.000000000 +0100 @@ -16432,6 +16432,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} displaced +stepping debugging info. @item set debug event @cindex event debugging info Turns on or off display of @value{GDBN} event debugging info. The @@ -23090,6 +23097,17 @@ 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. 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 under +the breakpoint. 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. ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: Stepping off breakpoints in non-stop debugging mode (resubmit) 2008-04-10 14:02 ` Pedro Alves @ 2008-04-10 20:12 ` Eli Zaretskii 2008-04-10 23:13 ` Pedro Alves 0 siblings, 1 reply; 16+ messages in thread From: Eli Zaretskii @ 2008-04-10 20:12 UTC (permalink / raw) To: Pedro Alves; +Cc: gdb-patches > From: Pedro Alves <pedro@codesourcery.com> > Date: Thu, 10 Apr 2008 11:49:35 +0100 > > How about this? > > @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. 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 under > the breakpoint. It is also known as out-of-line single-stepping. This is okay, but instead of ``under the breakpoint'' I'd suggest to say ``at the breakpoint location''. > +Displays the current state of displaying @value{GDBN} displaced stepping > +debugging info. This is fine, but I think this is even better: +Displays the current state of displaying @value{GDBN} debugging +info related to displaced stepping. > A couple of typos were also fixed in comments. Updated > patch attached. Thanks, approved, with the above gotchas. ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: Stepping off breakpoints in non-stop debugging mode (resubmit) 2008-04-10 20:12 ` Eli Zaretskii @ 2008-04-10 23:13 ` Pedro Alves 2008-04-11 9:15 ` Eli Zaretskii 0 siblings, 1 reply; 16+ messages in thread From: Pedro Alves @ 2008-04-10 23:13 UTC (permalink / raw) To: gdb-patches, Eli Zaretskii A Thursday 10 April 2008 21:00:04, Eli Zaretskii escreveu: > > From: Pedro Alves <pedro@codesourcery.com> > > Date: Thu, 10 Apr 2008 11:49:35 +0100 > > > > How about this? > > > > @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. 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 under > > the breakpoint. It is also known as out-of-line single-stepping. > > This is okay, but instead of ``under the breakpoint'' I'd suggest to > say ``at the breakpoint location''. > Not sure about that. There seems to be an ambiguity with the word "location", because depending on how you parse it, it can sound that's where the copy is being executed. Is it just me? "by executing an out-of-line copy of the instruction at the breakpoint location." > > +Displays the current state of displaying @value{GDBN} displaced > > stepping +debugging info. > > This is fine, but I think this is even better: > > +Displays the current state of displaying @value{GDBN} debugging > +info related to displaced stepping. > Thanks. I've updated to that form. -- Pedro Alves ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: Stepping off breakpoints in non-stop debugging mode (resubmit) 2008-04-10 23:13 ` Pedro Alves @ 2008-04-11 9:15 ` Eli Zaretskii 2008-04-11 15:47 ` Pedro Alves 0 siblings, 1 reply; 16+ messages in thread From: Eli Zaretskii @ 2008-04-11 9:15 UTC (permalink / raw) To: Pedro Alves; +Cc: gdb-patches > From: Pedro Alves <pedro@codesourcery.com> > Date: Thu, 10 Apr 2008 23:09:52 +0100 > > A Thursday 10 April 2008 21:00:04, Eli Zaretskii escreveu: > > > From: Pedro Alves <pedro@codesourcery.com> > > > Date: Thu, 10 Apr 2008 11:49:35 +0100 > > > > > > How about this? > > > > > > @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. 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 under > > > the breakpoint. It is also known as out-of-line single-stepping. > > > > This is okay, but instead of ``under the breakpoint'' I'd suggest to > > say ``at the breakpoint location''. > > > > Not sure about that. There seems to be an ambiguity with the > word "location", because depending on how you parse it, it can sound > that's where the copy is being executed. Is it just me? Then how about ``by executing an out-of-line copy of the instruction that was originally at the breakpoint location''? ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: Stepping off breakpoints in non-stop debugging mode (resubmit) 2008-04-11 9:15 ` Eli Zaretskii @ 2008-04-11 15:47 ` Pedro Alves 2008-04-11 16:19 ` Eli Zaretskii 0 siblings, 1 reply; 16+ messages in thread From: Pedro Alves @ 2008-04-11 15:47 UTC (permalink / raw) To: gdb-patches, Eli Zaretskii [-- Attachment #1: Type: text/plain, Size: 246 bytes --] A Friday 11 April 2008 09:46:05, Eli Zaretskii wrote: > Then how about ``by executing an out-of-line copy of the instruction > that was originally at the breakpoint location''? Sounds perfect! Thanks. Updated patch attached. -- Pedro Alves [-- Attachment #2: displaced_stepping.diff --] [-- Type: text/x-diff, Size: 66374 bytes --] 2008-04-11 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 | 531 ++++++++++++++++++++++++++++++++++++++-- gdb/testsuite/gdb.asm/asmsrc1.s | 12 13 files changed, 1181 insertions(+), 32 deletions(-) Index: src/gdb/Makefile.in =================================================================== --- src.orig/gdb/Makefile.in 2008-04-10 22:59:19.000000000 +0100 +++ src/gdb/Makefile.in 2008-04-10 22:59:21.000000000 +0100 @@ -1892,7 +1892,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) \ @@ -2229,7 +2229,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-10 17:50:15.000000000 +0100 +++ src/gdb/arch-utils.c 2008-04-10 22:59:21.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, ¤t_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-10 17:50:15.000000000 +0100 +++ src/gdb/arch-utils.h 2008-04-10 22:59:21.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-10 17:50:15.000000000 +0100 +++ src/gdb/gdbarch.sh 2008-04-10 22:59:21.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-10 17:50:15.000000000 +0100 +++ src/gdb/i386-tdep.c 2008-04-10 22:59:21.000000000 +0100 @@ -276,6 +276,225 @@ i386_breakpoint_from_pc (struct gdbarch return break_insn; } \f +/* 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)); + } +} + + +\f #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++) @@ -2434,6 +2653,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-10 17:50:15.000000000 +0100 +++ src/gdb/i386-tdep.h 2008-04-10 22:59:21.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-10 22:59:19.000000000 +0100 +++ src/gdb/infrun.c 2008-04-10 22:59:21.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; \f +/* 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); + } +} + +\f +/* 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); } \f +/* 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'. */ @@ -790,14 +1202,18 @@ proceed (CORE_ADDR addr, enum target_sig /* 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 breakpoints are always inserted, we'll trap if trying to + single-step over breakpoint. Disable all breakpoints. If we + can use displaced stepping, then we don't need to remove the + breakpoints. */ + 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 +1324,10 @@ init_wait_for_inferior (void) deferred_step_ptid = null_ptid; target_last_wait_ptid = minus_one_ptid; + + displaced_step_clear (); } + \f /* 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 @@ -1585,10 +2004,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 (¤t_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) { @@ -1736,7 +2176,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"); @@ -1751,7 +2191,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 @@ -1815,9 +2259,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 @@ -1920,10 +2361,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)) @@ -2048,7 +2493,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. */ @@ -2056,6 +2501,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; @@ -2079,6 +2528,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; @@ -2911,7 +3365,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 { @@ -4014,6 +4472,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 *) @@ -4109,9 +4575,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-10 17:50:15.000000000 +0100 +++ src/gdb/gdbarch.c 2008-04-10 22:59:21.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-10 17:50:15.000000000 +0100 +++ src/gdb/gdbarch.h 2008-04-10 22:59:21.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-10 17:50:15.000000000 +0100 +++ src/gdb/inferior.h 2008-04-10 22:59:21.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); + \f /* 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-10 17:50:15.000000000 +0100 +++ src/gdb/testsuite/gdb.asm/asmsrc1.s 2008-04-10 22:59:21.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-10 17:50:15.000000000 +0100 +++ src/gdb/i386-linux-tdep.c 2008-04-10 22:59:21.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-10 22:59:19.000000000 +0100 +++ src/gdb/doc/gdb.texinfo 2008-04-11 14:03:43.000000000 +0100 @@ -16435,6 +16435,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 @@ -23093,6 +23100,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. 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. ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: Stepping off breakpoints in non-stop debugging mode (resubmit) 2008-04-11 15:47 ` Pedro Alves @ 2008-04-11 16:19 ` Eli Zaretskii 2008-04-11 17:19 ` Pedro Alves 0 siblings, 1 reply; 16+ messages in thread From: Eli Zaretskii @ 2008-04-11 16:19 UTC (permalink / raw) To: Pedro Alves; +Cc: gdb-patches > From: Pedro Alves <pedro@codesourcery.com> > Date: Fri, 11 Apr 2008 14:22:39 +0100 > > A Friday 11 April 2008 09:46:05, Eli Zaretskii wrote: > > > Then how about ``by executing an out-of-line copy of the instruction > > that was originally at the breakpoint location''? > > Sounds perfect! Thanks. Updated patch attached. Thanks, the documentation patch is okay with me, but please enclose the first instance of "displaced stepping" in @dfn{}, as we do with new terms we introduce. ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: Stepping off breakpoints in non-stop debugging mode (resubmit) 2008-04-11 16:19 ` Eli Zaretskii @ 2008-04-11 17:19 ` Pedro Alves 2008-04-11 21:03 ` Eli Zaretskii 0 siblings, 1 reply; 16+ messages in thread From: Pedro Alves @ 2008-04-11 17:19 UTC (permalink / raw) To: gdb-patches, Eli Zaretskii A Friday 11 April 2008 15:31:26, Eli Zaretskii wrote: > Thanks, the documentation patch is okay with me, but please enclose > the first instance of "displaced stepping" in @dfn{}, as we do with > new terms we introduce. I've added it like so, hope that's what you mean: +@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. + It's the first time I'm looking at @dfn{], so I looked here for guidelines: http://www.mcs.vuw.ac.nz/cgi-bin/info2www?(texinfo)dfn Thanks, -- Pedro Alves ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: Stepping off breakpoints in non-stop debugging mode (resubmit) 2008-04-11 17:19 ` Pedro Alves @ 2008-04-11 21:03 ` Eli Zaretskii 2008-04-25 21:51 ` Pedro Alves 0 siblings, 1 reply; 16+ messages in thread From: Eli Zaretskii @ 2008-04-11 21:03 UTC (permalink / raw) To: Pedro Alves; +Cc: gdb-patches > From: Pedro Alves <pedro@codesourcery.com> > Date: Fri, 11 Apr 2008 16:46:54 +0100 > > A Friday 11 April 2008 15:31:26, Eli Zaretskii wrote: > > Thanks, the documentation patch is okay with me, but please enclose > > the first instance of "displaced stepping" in @dfn{}, as we do with > > new terms we introduce. > > I've added it like so, hope that's what you mean: > > +@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. That's okay, thanks. ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: Stepping off breakpoints in non-stop debugging mode (resubmit) 2008-04-11 21:03 ` Eli Zaretskii @ 2008-04-25 21:51 ` Pedro Alves 2008-04-26 5:47 ` Eli Zaretskii 0 siblings, 1 reply; 16+ messages in thread From: Pedro Alves @ 2008-04-25 21:51 UTC (permalink / raw) To: gdb-patches [-- Attachment #1: Type: text/plain, Size: 85 bytes --] Any comments on this? Here's a refreshed version. All its dependencies are now in. [-- Attachment #2: displaced_stepping.diff --] [-- Type: text/x-diff, Size: 66451 bytes --] 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, ¤t_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; } \f +/* 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)); + } +} + + +\f #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; \f +/* 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); + } +} + +\f +/* 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); } \f +/* 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 (); } + \f /* 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 (¤t_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); + \f /* 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. ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: Stepping off breakpoints in non-stop debugging mode (resubmit) 2008-04-25 21:51 ` Pedro Alves @ 2008-04-26 5:47 ` Eli Zaretskii 2008-04-26 7:14 ` Pedro Alves 0 siblings, 1 reply; 16+ messages in thread From: Eli Zaretskii @ 2008-04-26 5:47 UTC (permalink / raw) To: Pedro Alves; +Cc: gdb-patches > From: Pedro Alves <pedro@codesourcery.com> > Date: Fri, 25 Apr 2008 20:18:31 +0100 > > + 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."), _("\ These two sentences must not take more than one line, I think. That's because some of the help commands, like apropos, only show one line, so in this case they will show an incomplete sentence. > +@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 It is better to have @dfn at the first usage of the term, but that's a minor nit. > +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. If "out-of-line single-stepping" is a term known to people, it would be good to have an index entry here for that term. Otherwise, the doco bits are fine with me. Thanks. ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: Stepping off breakpoints in non-stop debugging mode (resubmit) 2008-04-26 5:47 ` Eli Zaretskii @ 2008-04-26 7:14 ` Pedro Alves 2008-04-26 13:16 ` Eli Zaretskii 2008-05-02 15:03 ` Daniel Jacobowitz 0 siblings, 2 replies; 16+ messages in thread From: Pedro Alves @ 2008-04-26 7:14 UTC (permalink / raw) To: gdb-patches, Eli Zaretskii [-- Attachment #1: Type: text/plain, Size: 2217 bytes --] A Friday 25 April 2008 20:44:57, Eli Zaretskii wrote: > > From: Pedro Alves <pedro@codesourcery.com> > > Date: Fri, 25 Apr 2008 20:18:31 +0100 > > > > + 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."), _("\ > > These two sentences must not take more than one line, I think. That's > because some of the help commands, like apropos, only show one line, > so in this case they will show an incomplete sentence. > Ah. OK, done. Made the sentence shorter. This is a maintainance command afterall. > > +@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 > > It is better to have @dfn at the first usage of the term, but that's a > minor nit. > Okay... I was following (from texinfo.info): " Use the `@dfn' command to identify the introductory or defining use of a technical term. (...) Mere passing mention of a term for the first time does not deserve `@dfn'. (...) As a general rule, a sentence containing the defining occurrence of a term should be a definition of the term. " ... by placing @dfn on the definition of the term. > > +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. > > If "out-of-line single-stepping" is a term known to people, it would > be good to have an index entry here for that term. > Yep. E.g., kprobes, systemtap, are examples of people using it by that term. Done. > Otherwise, the doco bits are fine with me. > > Thanks. Thank you for the review. -- Pedro Alves [-- Attachment #2: displaced_stepping.diff --] [-- Type: text/x-diff, Size: 66428 bytes --] 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 | 20 + 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 | 532 +++++++++++++++++++++++++++++++++++++--- gdb/testsuite/gdb.asm/asmsrc1.s | 12 13 files changed, 1181 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, ¤t_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; } \f +/* 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)); + } +} + + +\f #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 21:11:38.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; \f +/* 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); + } +} + +\f +/* 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); } \f +/* 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 (); } + \f /* 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 (¤t_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,22 @@ 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."), _("\ +Show debugger's willingness to use displaced stepping."), _("\ +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); + \f /* 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 20:55:12.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,19 @@ Shared library events. @end table +@kindex maint set can-use-displaced-stepping +@kindex maint show can-use-displaced-stepping +@cindex displaced stepping support +@cindex out-of-line single-stepping +@item maint set can-use-displaced-stepping +@itemx maint show can-use-displaced-stepping +Control whether or not @value{GDBN} will do @dfn{displaced stepping} +if the target supports it. The default is on. 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. ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: Stepping off breakpoints in non-stop debugging mode (resubmit) 2008-04-26 7:14 ` Pedro Alves @ 2008-04-26 13:16 ` Eli Zaretskii 2008-05-02 15:03 ` Daniel Jacobowitz 1 sibling, 0 replies; 16+ messages in thread From: Eli Zaretskii @ 2008-04-26 13:16 UTC (permalink / raw) To: Pedro Alves; +Cc: gdb-patches > From: Pedro Alves <pedro@codesourcery.com> > Date: Fri, 25 Apr 2008 21:13:12 +0100 > > > > +@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 > > > > It is better to have @dfn at the first usage of the term, but that's a > > minor nit. > > > > Okay... I was following (from texinfo.info): > > " > Use the `@dfn' command to identify the introductory or defining use of > a technical term. > > (...) > Mere passing mention of a term for the first time does not > deserve `@dfn'. > > (...) > As a general rule, a sentence containing the defining occurrence of a > term should be a definition of the term. > " Well, yes, but in your case the two sentences in question follow one another, so they could be interpreted as a single passage that introduces and defines the term. In any case, this is a minor nit, as I said. Thanks. ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: Stepping off breakpoints in non-stop debugging mode (resubmit) 2008-04-26 7:14 ` Pedro Alves 2008-04-26 13:16 ` Eli Zaretskii @ 2008-05-02 15:03 ` Daniel Jacobowitz 2008-05-02 17:27 ` Pedro Alves 1 sibling, 1 reply; 16+ messages in thread From: Daniel Jacobowitz @ 2008-05-02 15:03 UTC (permalink / raw) To: Pedro Alves; +Cc: gdb-patches, Eli Zaretskii On Fri, Apr 25, 2008 at 09:13:12PM +0100, Pedro Alves wrote: > 2008-04-25 Jim Blandy <jimb@codesourcery.com> > Pedro Alves <pedro@codesourcery.com> > > Implement displaced stepping. ... This looks OK to me. Let's go for it! -- Daniel Jacobowitz CodeSourcery ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: Stepping off breakpoints in non-stop debugging mode (resubmit) 2008-05-02 15:03 ` Daniel Jacobowitz @ 2008-05-02 17:27 ` Pedro Alves 0 siblings, 0 replies; 16+ messages in thread From: Pedro Alves @ 2008-05-02 17:27 UTC (permalink / raw) To: Daniel Jacobowitz; +Cc: gdb-patches, Eli Zaretskii, jimb [-- Attachment #1: Type: text/plain, Size: 446 bytes --] A Friday 02 May 2008 16:00:54, Daniel Jacobowitz wrote: > On Fri, Apr 25, 2008 at 09:13:12PM +0100, Pedro Alves wrote: > > 2008-04-25 Jim Blandy <jimb@codesourcery.com> > > Pedro Alves <pedro@codesourcery.com> > > > > Implement displaced stepping. > > ... > > This looks OK to me. Let's go for it! Done. Hurray! Big thanks to you and to Eli for the review, and of course, to Jim for writing this in the first place. -- Pedro Alves [-- Attachment #2: displaced_stepping.diff --] [-- Type: text/x-diff, Size: 66572 bytes --] 2008-05-02 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 | 20 + 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 | 532 +++++++++++++++++++++++++++++++++++++--- gdb/testsuite/gdb.asm/asmsrc1.s | 12 13 files changed, 1181 insertions(+), 34 deletions(-) Index: src/gdb/Makefile.in =================================================================== --- src.orig/gdb/Makefile.in 2008-05-02 17:27:55.000000000 +0100 +++ src/gdb/Makefile.in 2008-05-02 17:34:21.000000000 +0100 @@ -1917,7 +1917,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) \ @@ -2257,7 +2257,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-05-02 17:27:56.000000000 +0100 +++ src/gdb/arch-utils.c 2008-05-02 17:34:21.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, ¤t_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-05-02 17:27:55.000000000 +0100 +++ src/gdb/arch-utils.h 2008-05-02 17:34:21.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-05-02 17:27:56.000000000 +0100 +++ src/gdb/gdbarch.sh 2008-05-02 17:34:21.000000000 +0100 @@ -616,6 +616,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 @@ -742,6 +811,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-05-02 17:27:56.000000000 +0100 +++ src/gdb/i386-tdep.c 2008-05-02 17:34:21.000000000 +0100 @@ -276,6 +276,225 @@ i386_breakpoint_from_pc (struct gdbarch return break_insn; } \f +/* 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)); + } +} + + +\f #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++) @@ -2375,6 +2594,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-05-02 17:27:55.000000000 +0100 +++ src/gdb/i386-tdep.h 2008-05-02 17:34:21.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-05-02 17:27:56.000000000 +0100 +++ src/gdb/infrun.c 2008-05-02 17:34:21.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; \f +/* 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); + } +} + +\f +/* 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); } \f +/* 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 (); } + \f /* 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 (¤t_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)) @@ -2045,7 +2489,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. */ @@ -2053,6 +2497,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; @@ -2076,6 +2524,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; @@ -2905,7 +3358,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 { @@ -4011,6 +4468,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 *) @@ -4106,9 +4571,22 @@ 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."), _("\ +Show debugger's willingness to use displaced stepping."), _("\ +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-05-02 17:27:55.000000000 +0100 +++ src/gdb/gdbarch.c 2008-05-02 17:34:21.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; @@ -350,6 +355,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 */ @@ -435,6 +445,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->target_signal_from_host = default_target_signal_from_host; gdbarch->target_signal_to_host = default_target_signal_to_host; /* gdbarch_alloc() */ @@ -592,6 +605,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 */ @@ -717,6 +737,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, @@ -819,6 +857,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, @@ -2907,6 +2951,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-05-02 17:27:55.000000000 +0100 +++ src/gdb/gdbarch.h 2008-05-02 17:34:21.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; @@ -663,6 +664,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-05-02 17:27:55.000000000 +0100 +++ src/gdb/inferior.h 2008-05-02 17:34:21.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); + \f /* 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-05-02 17:27:55.000000000 +0100 +++ src/gdb/testsuite/gdb.asm/asmsrc1.s 2008-05-02 17:34:21.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-05-02 17:27:55.000000000 +0100 +++ src/gdb/i386-linux-tdep.c 2008-05-02 17:34:21.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-05-02 17:31:40.000000000 +0100 +++ src/gdb/doc/gdb.texinfo 2008-05-02 17:34:21.000000000 +0100 @@ -16483,6 +16483,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 @@ -23159,6 +23166,19 @@ Shared library events. @end table +@kindex maint set can-use-displaced-stepping +@kindex maint show can-use-displaced-stepping +@cindex displaced stepping support +@cindex out-of-line single-stepping +@item maint set can-use-displaced-stepping +@itemx maint show can-use-displaced-stepping +Control whether or not @value{GDBN} will do @dfn{displaced stepping} +if the target supports it. The default is on. 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. ^ permalink raw reply [flat|nested] 16+ messages in thread
end of thread, other threads:[~2008-05-02 16:55 UTC | newest] Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2008-04-10 3:14 Stepping off breakpoints in non-stop debugging mode (resubmit) Pedro Alves 2008-04-10 6:23 ` Eli Zaretskii 2008-04-10 14:02 ` Pedro Alves 2008-04-10 20:12 ` Eli Zaretskii 2008-04-10 23:13 ` Pedro Alves 2008-04-11 9:15 ` Eli Zaretskii 2008-04-11 15:47 ` Pedro Alves 2008-04-11 16:19 ` Eli Zaretskii 2008-04-11 17:19 ` Pedro Alves 2008-04-11 21:03 ` Eli Zaretskii 2008-04-25 21:51 ` Pedro Alves 2008-04-26 5:47 ` Eli Zaretskii 2008-04-26 7:14 ` Pedro Alves 2008-04-26 13:16 ` Eli Zaretskii 2008-05-02 15:03 ` Daniel Jacobowitz 2008-05-02 17:27 ` Pedro Alves
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox