mirror of
https://github.com/espressif/binutils-gdb.git
synced 2025-06-20 18:08:24 +08:00
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".
This commit is contained in:
532
gdb/infrun.c
532
gdb/infrun.c
@ -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_breakpoint;
|
||||
stepping the thread user has selected. */
|
||||
static ptid_t deferred_step_ptid;
|
||||
|
||||
/* Displaced stepping. */
|
||||
|
||||
/* In non-stop debugging mode, we must take special care to manage
|
||||
breakpoints properly; in particular, the traditional strategy for
|
||||
stepping a thread past a breakpoint it has hit is unsuitable.
|
||||
'Displaced stepping' is a tactic for stepping one thread past a
|
||||
breakpoint it has hit while ensuring that other threads running
|
||||
concurrently will hit the breakpoint as they should.
|
||||
|
||||
The traditional way to step a thread T off a breakpoint in a
|
||||
multi-threaded program in all-stop mode is as follows:
|
||||
|
||||
a0) Initially, all threads are stopped, and breakpoints are not
|
||||
inserted.
|
||||
a1) We single-step T, leaving breakpoints uninserted.
|
||||
a2) We insert breakpoints, and resume all threads.
|
||||
|
||||
In non-stop debugging, however, this strategy is unsuitable: we
|
||||
don't want to have to stop all threads in the system in order to
|
||||
continue or step T past a breakpoint. Instead, we use displaced
|
||||
stepping:
|
||||
|
||||
n0) Initially, T is stopped, other threads are running, and
|
||||
breakpoints are inserted.
|
||||
n1) We copy the instruction "under" the breakpoint to a separate
|
||||
location, outside the main code stream, making any adjustments
|
||||
to the instruction, register, and memory state as directed by
|
||||
T's architecture.
|
||||
n2) We single-step T over the instruction at its new location.
|
||||
n3) We adjust the resulting register and memory state as directed
|
||||
by T's architecture. This includes resetting T's PC to point
|
||||
back into the main instruction stream.
|
||||
n4) We resume T.
|
||||
|
||||
This approach depends on the following gdbarch methods:
|
||||
|
||||
- gdbarch_max_insn_length and gdbarch_displaced_step_location
|
||||
indicate where to copy the instruction, and how much space must
|
||||
be reserved there. We use these in step n1.
|
||||
|
||||
- gdbarch_displaced_step_copy_insn copies a instruction to a new
|
||||
address, and makes any necessary adjustments to the instruction,
|
||||
register contents, and memory. We use this in step n1.
|
||||
|
||||
- gdbarch_displaced_step_fixup adjusts registers and memory after
|
||||
we have successfuly single-stepped the instruction, to yield the
|
||||
same effect the instruction would have had if we had executed it
|
||||
at its original address. We use this in step n3.
|
||||
|
||||
- gdbarch_displaced_step_free_closure provides cleanup.
|
||||
|
||||
The gdbarch_displaced_step_copy_insn and
|
||||
gdbarch_displaced_step_fixup functions must be written so that
|
||||
copying an instruction with gdbarch_displaced_step_copy_insn,
|
||||
single-stepping across the copied instruction, and then applying
|
||||
gdbarch_displaced_insn_fixup should have the same effects on the
|
||||
thread's memory and registers as stepping the instruction in place
|
||||
would have. Exactly which responsibilities fall to the copy and
|
||||
which fall to the fixup is up to the author of those functions.
|
||||
|
||||
See the comments in gdbarch.sh for details.
|
||||
|
||||
Note that displaced stepping and software single-step cannot
|
||||
currently be used in combination, although with some care I think
|
||||
they could be made to. Software single-step works by placing
|
||||
breakpoints on all possible subsequent instructions; if the
|
||||
displaced instruction is a PC-relative jump, those breakpoints
|
||||
could fall in very strange places --- on pages that aren't
|
||||
executable, or at addresses that are not proper instruction
|
||||
boundaries. (We do generally let other threads run while we wait
|
||||
to hit the software single-step breakpoint, and they might
|
||||
encounter such a corrupted instruction.) One way to work around
|
||||
this would be to have gdbarch_displaced_step_copy_insn fully
|
||||
simulate the effect of PC-relative instructions (and return NULL)
|
||||
on architectures that use software single-stepping.
|
||||
|
||||
In non-stop mode, we can have independent and simultaneous step
|
||||
requests, so more than one thread may need to simultaneously step
|
||||
over a breakpoint. The current implementation assumes there is
|
||||
only one scratch space per process. In this case, we have to
|
||||
serialize access to the scratch space. If thread A wants to step
|
||||
over a breakpoint, but we are currently waiting for some other
|
||||
thread to complete a displaced step, we leave thread A stopped and
|
||||
place it in the displaced_step_request_queue. Whenever a displaced
|
||||
step finishes, we pick the next thread in the queue and start a new
|
||||
displaced step operation on it. See displaced_step_prepare and
|
||||
displaced_step_fixup for details. */
|
||||
|
||||
/* If this is not null_ptid, this is the thread carrying out a
|
||||
displaced single-step. This thread's state will require fixing up
|
||||
once it has completed its step. */
|
||||
static ptid_t displaced_step_ptid;
|
||||
|
||||
struct displaced_step_request
|
||||
{
|
||||
ptid_t ptid;
|
||||
struct displaced_step_request *next;
|
||||
};
|
||||
|
||||
/* A queue of pending displaced stepping requests. */
|
||||
struct displaced_step_request *displaced_step_request_queue;
|
||||
|
||||
/* The architecture the thread had when we stepped it. */
|
||||
static struct gdbarch *displaced_step_gdbarch;
|
||||
|
||||
/* The closure provided gdbarch_displaced_step_copy_insn, to be used
|
||||
for post-step cleanup. */
|
||||
static struct displaced_step_closure *displaced_step_closure;
|
||||
|
||||
/* The address of the original instruction, and the copy we made. */
|
||||
static CORE_ADDR displaced_step_original, displaced_step_copy;
|
||||
|
||||
/* Saved contents of copy area. */
|
||||
static gdb_byte *displaced_step_saved_copy;
|
||||
|
||||
/* When this is non-zero, we are allowed to use displaced stepping, if
|
||||
the architecture supports it. When this is zero, we use
|
||||
traditional the hold-and-step approach. */
|
||||
int can_use_displaced_stepping = 1;
|
||||
static void
|
||||
show_can_use_displaced_stepping (struct ui_file *file, int from_tty,
|
||||
struct cmd_list_element *c,
|
||||
const char *value)
|
||||
{
|
||||
fprintf_filtered (file, _("\
|
||||
Debugger's willingness to use displaced stepping to step over "
|
||||
"breakpoints is %s.\n"), value);
|
||||
}
|
||||
|
||||
/* Return non-zero if displaced stepping is enabled, and can be used
|
||||
with GDBARCH. */
|
||||
static int
|
||||
use_displaced_stepping (struct gdbarch *gdbarch)
|
||||
{
|
||||
return (can_use_displaced_stepping
|
||||
&& gdbarch_displaced_step_copy_insn_p (gdbarch));
|
||||
}
|
||||
|
||||
/* Clean out any stray displaced stepping state. */
|
||||
static void
|
||||
displaced_step_clear (void)
|
||||
{
|
||||
/* Indicate that there is no cleanup pending. */
|
||||
displaced_step_ptid = null_ptid;
|
||||
|
||||
if (displaced_step_closure)
|
||||
{
|
||||
gdbarch_displaced_step_free_closure (displaced_step_gdbarch,
|
||||
displaced_step_closure);
|
||||
displaced_step_closure = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
cleanup_displaced_step_closure (void *ptr)
|
||||
{
|
||||
struct displaced_step_closure *closure = ptr;
|
||||
|
||||
gdbarch_displaced_step_free_closure (current_gdbarch, closure);
|
||||
}
|
||||
|
||||
/* Dump LEN bytes at BUF in hex to FILE, followed by a newline. */
|
||||
void
|
||||
displaced_step_dump_bytes (struct ui_file *file,
|
||||
const gdb_byte *buf,
|
||||
size_t len)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
fprintf_unfiltered (file, "%02x ", buf[i]);
|
||||
fputs_unfiltered ("\n", file);
|
||||
}
|
||||
|
||||
/* Prepare to single-step, using displaced stepping.
|
||||
|
||||
Note that we cannot use displaced stepping when we have a signal to
|
||||
deliver. If we have a signal to deliver and an instruction to step
|
||||
over, then after the step, there will be no indication from the
|
||||
target whether the thread entered a signal handler or ignored the
|
||||
signal and stepped over the instruction successfully --- both cases
|
||||
result in a simple SIGTRAP. In the first case we mustn't do a
|
||||
fixup, and in the second case we must --- but we can't tell which.
|
||||
Comments in the code for 'random signals' in handle_inferior_event
|
||||
explain how we handle this case instead.
|
||||
|
||||
Returns 1 if preparing was successful -- this thread is going to be
|
||||
stepped now; or 0 if displaced stepping this thread got queued. */
|
||||
static int
|
||||
displaced_step_prepare (ptid_t ptid)
|
||||
{
|
||||
struct cleanup *old_cleanups;
|
||||
struct regcache *regcache = get_thread_regcache (ptid);
|
||||
struct gdbarch *gdbarch = get_regcache_arch (regcache);
|
||||
CORE_ADDR original, copy;
|
||||
ULONGEST len;
|
||||
struct displaced_step_closure *closure;
|
||||
|
||||
/* We should never reach this function if the architecture does not
|
||||
support displaced stepping. */
|
||||
gdb_assert (gdbarch_displaced_step_copy_insn_p (gdbarch));
|
||||
|
||||
/* For the first cut, we're displaced stepping one thread at a
|
||||
time. */
|
||||
|
||||
if (!ptid_equal (displaced_step_ptid, null_ptid))
|
||||
{
|
||||
/* Already waiting for a displaced step to finish. Defer this
|
||||
request and place in queue. */
|
||||
struct displaced_step_request *req, *new_req;
|
||||
|
||||
if (debug_displaced)
|
||||
fprintf_unfiltered (gdb_stdlog,
|
||||
"displaced: defering step of %s\n",
|
||||
target_pid_to_str (ptid));
|
||||
|
||||
new_req = xmalloc (sizeof (*new_req));
|
||||
new_req->ptid = ptid;
|
||||
new_req->next = NULL;
|
||||
|
||||
if (displaced_step_request_queue)
|
||||
{
|
||||
for (req = displaced_step_request_queue;
|
||||
req && req->next;
|
||||
req = req->next)
|
||||
;
|
||||
req->next = new_req;
|
||||
}
|
||||
else
|
||||
displaced_step_request_queue = new_req;
|
||||
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (debug_displaced)
|
||||
fprintf_unfiltered (gdb_stdlog,
|
||||
"displaced: stepping %s now\n",
|
||||
target_pid_to_str (ptid));
|
||||
}
|
||||
|
||||
displaced_step_clear ();
|
||||
|
||||
original = read_pc_pid (ptid);
|
||||
|
||||
copy = gdbarch_displaced_step_location (gdbarch);
|
||||
len = gdbarch_max_insn_length (gdbarch);
|
||||
|
||||
/* Save the original contents of the copy area. */
|
||||
displaced_step_saved_copy = xmalloc (len);
|
||||
old_cleanups = make_cleanup (free_current_contents,
|
||||
&displaced_step_saved_copy);
|
||||
read_memory (copy, displaced_step_saved_copy, len);
|
||||
if (debug_displaced)
|
||||
{
|
||||
fprintf_unfiltered (gdb_stdlog, "displaced: saved 0x%s: ",
|
||||
paddr_nz (copy));
|
||||
displaced_step_dump_bytes (gdb_stdlog, displaced_step_saved_copy, len);
|
||||
};
|
||||
|
||||
closure = gdbarch_displaced_step_copy_insn (gdbarch,
|
||||
original, copy, regcache);
|
||||
|
||||
/* We don't support the fully-simulated case at present. */
|
||||
gdb_assert (closure);
|
||||
|
||||
make_cleanup (cleanup_displaced_step_closure, closure);
|
||||
|
||||
/* Resume execution at the copy. */
|
||||
write_pc_pid (copy, ptid);
|
||||
|
||||
discard_cleanups (old_cleanups);
|
||||
|
||||
if (debug_displaced)
|
||||
fprintf_unfiltered (gdb_stdlog, "displaced: displaced pc to 0x%s\n",
|
||||
paddr_nz (copy));
|
||||
|
||||
/* Save the information we need to fix things up if the step
|
||||
succeeds. */
|
||||
displaced_step_ptid = ptid;
|
||||
displaced_step_gdbarch = gdbarch;
|
||||
displaced_step_closure = closure;
|
||||
displaced_step_original = original;
|
||||
displaced_step_copy = copy;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
displaced_step_clear_cleanup (void *ignore)
|
||||
{
|
||||
displaced_step_clear ();
|
||||
}
|
||||
|
||||
static void
|
||||
write_memory_ptid (ptid_t ptid, CORE_ADDR memaddr, const gdb_byte *myaddr, int len)
|
||||
{
|
||||
struct cleanup *ptid_cleanup = save_inferior_ptid ();
|
||||
inferior_ptid = ptid;
|
||||
write_memory (memaddr, myaddr, len);
|
||||
do_cleanups (ptid_cleanup);
|
||||
}
|
||||
|
||||
static void
|
||||
displaced_step_fixup (ptid_t event_ptid, enum target_signal signal)
|
||||
{
|
||||
struct cleanup *old_cleanups;
|
||||
|
||||
/* Was this event for the pid we displaced? */
|
||||
if (ptid_equal (displaced_step_ptid, null_ptid)
|
||||
|| ! ptid_equal (displaced_step_ptid, event_ptid))
|
||||
return;
|
||||
|
||||
old_cleanups = make_cleanup (displaced_step_clear_cleanup, 0);
|
||||
|
||||
/* Restore the contents of the copy area. */
|
||||
{
|
||||
ULONGEST len = gdbarch_max_insn_length (displaced_step_gdbarch);
|
||||
write_memory_ptid (displaced_step_ptid, displaced_step_copy,
|
||||
displaced_step_saved_copy, len);
|
||||
if (debug_displaced)
|
||||
fprintf_unfiltered (gdb_stdlog, "displaced: restored 0x%s\n",
|
||||
paddr_nz (displaced_step_copy));
|
||||
}
|
||||
|
||||
/* Did the instruction complete successfully? */
|
||||
if (signal == TARGET_SIGNAL_TRAP)
|
||||
{
|
||||
/* Fix up the resulting state. */
|
||||
gdbarch_displaced_step_fixup (displaced_step_gdbarch,
|
||||
displaced_step_closure,
|
||||
displaced_step_original,
|
||||
displaced_step_copy,
|
||||
get_thread_regcache (displaced_step_ptid));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Since the instruction didn't complete, all we can do is
|
||||
relocate the PC. */
|
||||
CORE_ADDR pc = read_pc_pid (event_ptid);
|
||||
pc = displaced_step_original + (pc - displaced_step_copy);
|
||||
write_pc_pid (pc, event_ptid);
|
||||
}
|
||||
|
||||
do_cleanups (old_cleanups);
|
||||
|
||||
/* Are there any pending displaced stepping requests? If so, run
|
||||
one now. */
|
||||
if (displaced_step_request_queue)
|
||||
{
|
||||
struct displaced_step_request *head;
|
||||
ptid_t ptid;
|
||||
|
||||
head = displaced_step_request_queue;
|
||||
ptid = head->ptid;
|
||||
displaced_step_request_queue = head->next;
|
||||
xfree (head);
|
||||
|
||||
if (debug_displaced)
|
||||
fprintf_unfiltered (gdb_stdlog,
|
||||
"displaced: stepping queued %s now\n",
|
||||
target_pid_to_str (ptid));
|
||||
|
||||
|
||||
displaced_step_ptid = null_ptid;
|
||||
displaced_step_prepare (ptid);
|
||||
target_resume (ptid, 1, TARGET_SIGNAL_0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Resuming. */
|
||||
|
||||
/* Things to clean up if we QUIT out of resume (). */
|
||||
static void
|
||||
@ -510,14 +889,14 @@ resume (int step, enum target_signal sig)
|
||||
{
|
||||
int should_resume = 1;
|
||||
struct cleanup *old_cleanups = make_cleanup (resume_cleanups, 0);
|
||||
CORE_ADDR pc = read_pc ();
|
||||
QUIT;
|
||||
|
||||
if (debug_infrun)
|
||||
fprintf_unfiltered (gdb_stdlog, "infrun: resume (step=%d, signal=%d)\n",
|
||||
step, sig);
|
||||
|
||||
/* FIXME: calling breakpoint_here_p (read_pc ()) three times! */
|
||||
|
||||
fprintf_unfiltered (gdb_stdlog,
|
||||
"infrun: resume (step=%d, signal=%d), "
|
||||
"stepping_over_breakpoint=%d\n",
|
||||
step, sig, stepping_over_breakpoint);
|
||||
|
||||
/* Some targets (e.g. Solaris x86) have a kernel bug when stepping
|
||||
over an instruction that causes a page fault without triggering
|
||||
@ -535,7 +914,7 @@ resume (int step, enum target_signal sig)
|
||||
removed or inserted, as appropriate. The exception is if we're sitting
|
||||
at a permanent breakpoint; we need to step over it, but permanent
|
||||
breakpoints can't be removed. So we have to test for it here. */
|
||||
if (breakpoint_here_p (read_pc ()) == permanent_breakpoint_here)
|
||||
if (breakpoint_here_p (pc) == permanent_breakpoint_here)
|
||||
{
|
||||
if (gdbarch_skip_permanent_breakpoint_p (current_gdbarch))
|
||||
gdbarch_skip_permanent_breakpoint (current_gdbarch,
|
||||
@ -547,6 +926,24 @@ how to step past a permanent breakpoint on this architecture. Try using\n\
|
||||
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 continue execution."));
|
||||
`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 continue execution."));
|
||||
/* Most targets can step a breakpoint instruction, thus
|
||||
executing it normally. But if this one cannot, just
|
||||
continue and we will hit it anyway. */
|
||||
if (step && breakpoint_inserted_here_p (read_pc ()))
|
||||
if (step && breakpoint_inserted_here_p (pc))
|
||||
step = 0;
|
||||
}
|
||||
|
||||
if (debug_displaced
|
||||
&& use_displaced_stepping (current_gdbarch)
|
||||
&& stepping_over_breakpoint)
|
||||
{
|
||||
CORE_ADDR actual_pc = read_pc_pid (resume_ptid);
|
||||
gdb_byte buf[4];
|
||||
|
||||
fprintf_unfiltered (gdb_stdlog, "displaced: run 0x%s: ",
|
||||
paddr_nz (actual_pc));
|
||||
read_memory (actual_pc, buf, sizeof (buf));
|
||||
displaced_step_dump_bytes (gdb_stdlog, buf, sizeof (buf));
|
||||
}
|
||||
|
||||
target_resume (resume_ptid, step, sig);
|
||||
}
|
||||
|
||||
discard_cleanups (old_cleanups);
|
||||
}
|
||||
|
||||
/* Proceeding. */
|
||||
|
||||
/* Clear out all variables saying what to do when inferior is continued.
|
||||
First do this, then set the ones you want, then call `proceed'. */
|
||||
@ -787,17 +1199,20 @@ proceed (CORE_ADDR addr, enum target_signal siggnal, int step)
|
||||
|
||||
if (oneproc)
|
||||
{
|
||||
/* We will get a trace trap after one instruction.
|
||||
Continue it automatically and insert breakpoints then. */
|
||||
stepping_over_breakpoint = 1;
|
||||
/* FIXME: if breakpoints are always inserted, we'll trap
|
||||
if trying to single-step over breakpoint. Disable
|
||||
all breakpoints. In future, we'd need to invent some
|
||||
smart way of stepping over breakpoint instruction without
|
||||
hitting breakpoint. */
|
||||
remove_breakpoints ();
|
||||
/* If displaced stepping is enabled, we can step over the
|
||||
breakpoint without hitting it, so leave all breakpoints
|
||||
inserted. Otherwise we need to disable all breakpoints, step
|
||||
one instruction, and then re-add them when that step is
|
||||
finished. */
|
||||
if (!use_displaced_stepping (current_gdbarch))
|
||||
remove_breakpoints ();
|
||||
}
|
||||
else
|
||||
|
||||
/* We can insert breakpoints if we're not trying to step over one,
|
||||
or if we are stepping over one but we're using displaced stepping
|
||||
to do so. */
|
||||
if (! stepping_over_breakpoint || use_displaced_stepping (current_gdbarch))
|
||||
insert_breakpoints ();
|
||||
|
||||
if (siggnal != TARGET_SIGNAL_DEFAULT)
|
||||
@ -908,7 +1323,10 @@ init_wait_for_inferior (void)
|
||||
deferred_step_ptid = null_ptid;
|
||||
|
||||
target_last_wait_ptid = minus_one_ptid;
|
||||
|
||||
displaced_step_clear ();
|
||||
}
|
||||
|
||||
|
||||
/* This enum encodes possible reasons for doing a target_wait, so that
|
||||
wfi can call target_wait in one place. (Ultimately the call will be
|
||||
@ -1580,10 +1998,31 @@ handle_inferior_event (struct execution_control_state *ecs)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Do we need to clean up the state of a thread that has completed a
|
||||
displaced single-step? (Doing so usually affects the PC, so do
|
||||
it here, before we set stop_pc.) */
|
||||
displaced_step_fixup (ecs->ptid, stop_signal);
|
||||
|
||||
stop_pc = read_pc_pid (ecs->ptid);
|
||||
|
||||
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_control_state *ecs)
|
||||
|
||||
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_control_state *ecs)
|
||||
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_control_state *ecs)
|
||||
&& (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_control_state *ecs)
|
||||
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_state *ecs)
|
||||
|
||||
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 debugging is enabled."),
|
||||
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 stops at a different source line."),
|
||||
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;
|
||||
}
|
||||
|
Reference in New Issue
Block a user