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

login
register
mail settings
Submitter Pedro Alves
Date April 7, 2015, 12:49 p.m.
Message ID <1428410990-28560-4-git-send-email-palves@redhat.com>
Download mbox | patch
Permalink /patch/6046/
State New
Headers show

Comments

Pedro Alves - April 7, 2015, 12:49 p.m.
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
Pedro Alves - April 9, 2015, 12:46 p.m.
On 04/07/2015 01:49 PM, Pedro Alves wrote:
> 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).

Hmm, actually not all targets get the entry point from symbols -- when
Cell debugging is enabled, PPC gets the entry point from AT_ENTRY from
the auxv...  We should probably make that the default on GNU/Linux.
I just tried that now on x86-64 and it worked fine.

Thanks,
Pedro Alves

Patch

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
+    }
+}