[PATCH v2 03/23] PR13858 - Can't do displaced stepping with no symbols

Pedro Alves
2015-04-07T12:50:00Z

Running break-interp.exp with the target always in non-stop mode trips
on PR13858, as enabling non-stop also enables displaced stepping.

The problem is that when GDB doesn't know where the entry point is, it
doesn't know where to put the displaced stepping scratch pad. The
test added by this commit exercises this. Without the fix, we get:

(gdb) PASS: gdb.base/step-over-no-symbols.exp: displaced=on: break *$pc
set displaced-stepping on
(gdb) PASS: gdb.base/step-over-no-symbols.exp: displaced=on: set displaced-stepping on
stepi
0x00000000004005be in ?? ()
Entry point address is not known.
(gdb) PASS: gdb.base/step-over-no-symbols.exp: displaced=on: stepi
p /x $pc
$2 = 0x4005be
(gdb) PASS: gdb.base/step-over-no-symbols.exp: displaced=on: get after PC
FAIL: gdb.base/step-over-no-symbols.exp: displaced=on: advanced

The fix is to fall back to stepping over the breakpoint in-line if we
don't know where the entry point address is.

This is enough to fix all-stop + "set displaced on". For non-stop,
we'll need to teach core gdb to pause all threads to be able to start
the in-line step-over (because then we need to remove the breakpoint
from the target temporarily).

gdb/ChangeLog:
2015-04-07 Pedro Alves <palves@redhat.com>

PR gdb/13858
* infrun.c (use_displaced_stepping): Rename to ...
(can_use_displaced_stepping_p): ... this.
(use_displaced_stepping_now_p): New function.
(resume): Clear trap_expected if waiting for vfork-done. Use
use_displaced_stepping_now_p.
(keep_going): Use use_displaced_stepping_now_p now.

gdb/testsuite/
2015-04-07 Pedro Alves <palves@redhat.com>

PR gdb/13858
* gdb.base/step-over-no-symbols.exp: New file.
---
gdb/infrun.c | 83 +++++++++++++----------
gdb/testsuite/gdb.base/step-over-no-symbols.exp | 88 +++++++++++++++++++++++++
2 files changed, 138 insertions(+), 33 deletions(-)
create mode 100644 gdb/testsuite/gdb.base/step-over-no-symbols.exp

diff --git a/gdb/infrun.c b/gdb/infrun.c
index 466bc4a..48da790 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -1581,7 +1581,7 @@ show_can_use_displaced_stepping (struct ui_file *file, int from_tty,
over breakpoints. */

static int
-use_displaced_stepping (struct gdbarch *gdbarch)
+can_use_displaced_stepping_p (struct gdbarch *gdbarch)
{
return (((can_use_displaced_stepping == AUTO_BOOLEAN_AUTO && non_stop)
|| can_use_displaced_stepping == AUTO_BOOLEAN_TRUE)
@@ -1589,6 +1589,24 @@ use_displaced_stepping (struct gdbarch *gdbarch)
&& find_record_target () == NULL);
}

+/* Return non-zero if displaced stepping should be used to step
+ over a breakpoint in the current thread. */
+
+static int
+use_displaced_stepping_now_p (struct gdbarch *gdbarch,
+ enum gdb_signal sig)
+{
+ CORE_ADDR retval;
+
+ /* 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. */
+ return (sig == GDB_SIGNAL_0
+ && can_use_displaced_stepping_p (gdbarch)
+ && entry_point_address_query (&retval));
+}
+
/* Clean out any stray displaced stepping state. */
static void
displaced_step_clear (struct displaced_step_inferior_state *displaced)
@@ -2140,6 +2158,10 @@ resume (enum gdb_signal sig)
fprintf_unfiltered (gdb_stdlog,
"infrun: resume : clear step\n");
step = 0;
+
+ /* Likewise, make sure we don't use displaced stepping, which
+ would poke the scratch buffer in the child. */
+ tp->control.trap_expected = 0;
}

if (debug_infrun)
@@ -2235,20 +2257,8 @@ resume (enum gdb_signal sig)
tp->control.may_range_step = 0;

/* 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.
-
- We can't use displaced stepping when we are waiting for vfork_done
- event, displaced stepping breaks the vfork child similarly as single
- step software breakpoint. */
- if (use_displaced_stepping (gdbarch)
- && tp->control.trap_expected
- && sig == GDB_SIGNAL_0
- && !current_inferior ()->waiting_for_vfork_done)
+ instruction at a different address. */
+ if (tp->control.trap_expected && use_displaced_stepping_now_p (gdbarch, sig))
{
struct displaced_step_inferior_state *displaced;

@@ -2391,8 +2401,8 @@ resume (enum gdb_signal sig)
}

if (debug_displaced
- && use_displaced_stepping (gdbarch)
- && tp->control.trap_expected)
+ && tp->control.trap_expected
+ && use_displaced_stepping_now_p (gdbarch, sig))
{
struct regcache *resume_regcache = get_thread_regcache (tp->ptid);
struct gdbarch *resume_gdbarch = get_regcache_arch (resume_regcache);
@@ -2707,7 +2717,9 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal)
displaced stepping to do so, insert all breakpoints (watchpoints,
etc.) but the one we're stepping over, step one instruction, and
then re-insert the breakpoint when that step is finished. */
- if (tp->stepping_over_breakpoint && !use_displaced_stepping (gdbarch))
+ if (tp->stepping_over_breakpoint
+ && !use_displaced_stepping_now_p (gdbarch,
+ tp->suspend.stop_signal))
{
struct regcache *regcache = get_current_regcache ();

@@ -6255,6 +6267,7 @@ keep_going (struct execution_control_state *ecs)
struct regcache *regcache = get_current_regcache ();
int remove_bp;
int remove_wps;
+ enum gdb_signal signo;

/* Either the trap was not expected, but we are continuing
anyway (if we got a signal, the user asked it be passed to
@@ -6280,7 +6293,25 @@ keep_going (struct execution_control_state *ecs)
remove_wps = (ecs->event_thread->stepping_over_watchpoint
&& !target_have_steppable_watchpoint);

- if (remove_bp && !use_displaced_stepping (get_regcache_arch (regcache)))
+ /* Do not deliver GDB_SIGNAL_TRAP (except when the user
+ explicitly specifies that such a signal should be delivered
+ to the target program). Typically, that would occur when a
+ user is debugging a target monitor on a simulator: the target
+ monitor sets a breakpoint; the simulator encounters this
+ breakpoint and halts the simulation handing control to GDB;
+ GDB, noting that the stop address doesn't map to any known
+ breakpoint, returns control back to the simulator; the
+ simulator then delivers the hardware equivalent of a
+ GDB_SIGNAL_TRAP to the program being debugged. */
+ if (ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_TRAP
+ && !signal_program[ecs->event_thread->suspend.stop_signal])
+ ecs->event_thread->suspend.stop_signal = GDB_SIGNAL_0;
+
+ signo = ecs->event_thread->suspend.stop_signal;
+
+ if (remove_bp
+ && !use_displaced_stepping_now_p (get_regcache_arch (regcache),
+ signo))
{
set_step_over_info (get_regcache_aspace (regcache),
regcache_read_pc (regcache), remove_wps);
@@ -6306,20 +6337,6 @@ keep_going (struct execution_control_state *ecs)

ecs->event_thread->control.trap_expected = (remove_bp || remove_wps);

- /* Do not deliver GDB_SIGNAL_TRAP (except when the user
- explicitly specifies that such a signal should be delivered
- to the target program). Typically, that would occur when a
- user is debugging a target monitor on a simulator: the target
- monitor sets a breakpoint; the simulator encounters this
- breakpoint and halts the simulation handing control to GDB;
- GDB, noting that the stop address doesn't map to any known
- breakpoint, returns control back to the simulator; the
- simulator then delivers the hardware equivalent of a
- GDB_SIGNAL_TRAP to the program being debugged. */
- if (ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_TRAP
- && !signal_program[ecs->event_thread->suspend.stop_signal])
- ecs->event_thread->suspend.stop_signal = GDB_SIGNAL_0;
-
discard_cleanups (old_cleanups);
resume (ecs->event_thread->suspend.stop_signal);
}
diff --git a/gdb/testsuite/gdb.base/step-over-no-symbols.exp b/gdb/testsuite/gdb.base/step-over-no-symbols.exp
new file mode 100644
index 0000000..d7bfd64
--- /dev/null
+++ b/gdb/testsuite/gdb.base/step-over-no-symbols.exp
@@ -0,0 +1,88 @@
+# Copyright (C) 2015 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# Test that GDB can step past a breakpoint even if GDB doesn't have
+# symbols for the main binary.
+
+standard_testfile start.c
+
+if { ![support_displaced_stepping] } {
+ unsupported "displaced stepping"
+ return -1
+}
+
+if { [build_executable "failed to build" ${testfile} $srcfile] } {
+ return -1
+}
+
+# Get the current PC. MSG is used as test message.
+
+proc get_pc { msg } {
+ global hex gdb_prompt
+
+ set addr ""
+ gdb_test_multiple "p /x \$pc" "$msg" {
+ -re " = ($hex).*$gdb_prompt $" {
+ set addr $expect_out(1,string)
+ pass "$msg"
+ }
+ }
+
+ return $addr
+}
+
+# Test stepping past a breakpoint with no symbols. DISPLACED is one
+# of the "set displaced-stepping" options. GDB should be able to fall
+# back to stepping past the breakpoint using an in-line step-over.
+
+proc test_step_over {displaced} {
+ global hex
+ global binfile
+
+ clean_restart $binfile
+
+ if ![runto_main] {
+ fail "couldn't run to main"
+ untested "stepping over breakpoint with displaced=$displaced"
+ return -1
+ }
+
+ delete_breakpoints
+
+ set msg "purging symbols"
+ gdb_test_multiple "symbol-file" "$msg" {
+ -re "Discard symbol table.*y or n. $" {
+ gdb_test "y" "No symbol file now." "$msg"
+ }
+ }
+
+ set before_addr [get_pc "get before PC"]
+
+ gdb_test "break *\$pc" "Breakpoint .* at $hex"
+
+ gdb_test_no_output "set displaced-stepping $displaced"
+
+ gdb_test "stepi" "$hex in \?\? .*"
+
+ set after_addr [get_pc "get after PC"]
+
+ gdb_assert {$before_addr != $after_addr} "advanced"
+}
+
+foreach displaced { "off" "on" "auto" } {
+ with_test_prefix "displaced=$displaced" {
+ test_step_over $displaced
+ }
+}
--
1.9.3