diff options
-rw-r--r-- | gdb/ChangeLog | 7 | ||||
-rw-r--r-- | gdb/infrun.c | 99 | ||||
-rw-r--r-- | gdb/testsuite/ChangeLog | 5 | ||||
-rw-r--r-- | gdb/testsuite/gdb.base/signest.c | 53 | ||||
-rw-r--r-- | gdb/testsuite/gdb.base/signest.exp | 67 |
5 files changed, 195 insertions, 36 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog index c688ecd..bcbff9e 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,10 @@ +2011-04-28 Ulrich Weigand <ulrich.weigand@linaro.org> + + * infrun.c (proceed): Revert previous change. + (resume): Instead, handle the case of signal delivery while stepping + off a breakpoint location here, and only if software single-stepping + is used. Handle nested signals. + 2011-04-28 Yao Qi <yao@codesourcery.com> * arm-tdep.c (copy_unmodified): Rename to ... diff --git a/gdb/infrun.c b/gdb/infrun.c index 1d2a9c7..2d6d523 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -1703,6 +1703,51 @@ a command like `return' or `jump' to continue execution.")); else if (step) step = maybe_software_singlestep (gdbarch, pc); + /* Currently, our software single-step implementation leads to different + results than hardware single-stepping in one situation: when stepping + into delivering a signal which has an associated signal handler, + hardware single-step will stop at the first instruction of the handler, + while software single-step will simply skip execution of the handler. + + For now, this difference in behavior is accepted since there is no + easy way to actually implement single-stepping into a signal handler + without kernel support. + + However, there is one scenario where this difference leads to follow-on + problems: if we're stepping off a breakpoint by removing all breakpoints + and then single-stepping. In this case, the software single-step + behavior means that even if there is a *breakpoint* in the signal + handler, GDB still would not stop. + + Fortunately, we can at least fix this particular issue. We detect + here the case where we are about to deliver a signal while software + single-stepping with breakpoints removed. In this situation, we + revert the decisions to remove all breakpoints and insert single- + step breakpoints, and instead we install a step-resume breakpoint + at the current address, deliver the signal without stepping, and + once we arrive back at the step-resume breakpoint, actually step + over the breakpoint we originally wanted to step over. */ + if (singlestep_breakpoints_inserted_p + && tp->control.trap_expected && sig != TARGET_SIGNAL_0) + { + /* If we have nested signals or a pending signal is delivered + immediately after a handler returns, might might already have + a step-resume breakpoint set on the earlier handler. We cannot + set another step-resume breakpoint; just continue on until the + original breakpoint is hit. */ + if (tp->control.step_resume_breakpoint == NULL) + { + insert_step_resume_breakpoint_at_frame (get_current_frame ()); + tp->step_after_step_resume_breakpoint = 1; + } + + remove_single_step_breakpoints (); + singlestep_breakpoints_inserted_p = 0; + + insert_breakpoints (); + tp->control.trap_expected = 0; + } + if (should_resume) { ptid_t resume_ptid; @@ -2064,6 +2109,24 @@ proceed (CORE_ADDR addr, enum target_signal siggnal, int step) /* prepare_to_proceed may change the current thread. */ tp = inferior_thread (); + if (oneproc) + { + tp->control.trap_expected = 1; + /* 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 (gdbarch)) + remove_breakpoints (); + } + + /* 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 (! tp->control.trap_expected || use_displaced_stepping (gdbarch)) + insert_breakpoints (); + if (!non_stop) { /* Pass the last stop signal to the thread we're resuming, @@ -2133,42 +2196,6 @@ proceed (CORE_ADDR addr, enum target_signal siggnal, int step) /* Reset to normal state. */ init_infwait_state (); - /* Stepping over a breakpoint while at the same time delivering a signal - has a problem: we cannot use displaced stepping, but we also cannot - use software single-stepping, because we do not know where execution - will continue if a signal handler is installed. - - On the other hand, if there is a signal handler we'd have to step - over it anyway. So what we do instead is to install a step-resume - handler at the current address right away, deliver the signal without - stepping, and once we arrive back at the step-resume breakpoint, step - once more over the original breakpoint we wanted to step over. */ - if (oneproc && tp->suspend.stop_signal != TARGET_SIGNAL_0 - && execution_direction != EXEC_REVERSE) - { - insert_step_resume_breakpoint_at_frame (get_current_frame ()); - tp->step_after_step_resume_breakpoint = 1; - oneproc = 0; - } - - if (oneproc) - { - tp->control.trap_expected = 1; - /* 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 (gdbarch)) - remove_breakpoints (); - } - - /* 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 (! tp->control.trap_expected || use_displaced_stepping (gdbarch)) - insert_breakpoints (); - /* Resume inferior. */ resume (oneproc || step || bpstat_should_step (), tp->suspend.stop_signal); diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index c565e40..d2a6344 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2011-04-28 Ulrich Weigand <ulrich.weigand@linaro.org> + + * gdb.base/signest.exp: New file. + * gdb.base/signest.c: Likewise. + 2011-04-28 Jan Kratochvil <jan.kratochvil@redhat.com> * lib/mi-support.exp (mi_expect_stop) <stopped at wrong place>: Accept diff --git a/gdb/testsuite/gdb.base/signest.c b/gdb/testsuite/gdb.base/signest.c new file mode 100644 index 0000000..127c77f --- /dev/null +++ b/gdb/testsuite/gdb.base/signest.c @@ -0,0 +1,53 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2011 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/>. */ + +#include <signal.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +volatile char *p = NULL; + +extern long +bowler (void) +{ + return *p; +} + +extern void +keeper (int sig) +{ + static int recurse = 0; + if (++recurse < 3) + bowler (); + + _exit (0); +} + +int +main (void) +{ + struct sigaction act; + memset (&act, 0, sizeof act); + act.sa_handler = keeper; + act.sa_flags = SA_NODEFER; + sigaction (SIGSEGV, &act, NULL); + sigaction (SIGBUS, &act, NULL); + + bowler (); + return 0; +} diff --git a/gdb/testsuite/gdb.base/signest.exp b/gdb/testsuite/gdb.base/signest.exp new file mode 100644 index 0000000..02f8134 --- /dev/null +++ b/gdb/testsuite/gdb.base/signest.exp @@ -0,0 +1,67 @@ +# This testcase is part of GDB, the GNU debugger. + +# Copyright 2011 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/>. + +set testfile "signest" +set srcfile ${testfile}.c + +if [target_info exists gdb,nosignals] { + verbose "Skipping ${testfile}.exp because of nosignals." + return -1 +} + +if [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile} {debug}] { + untested ${testfile}.exp + return -1 +} + +if ![runto_main] then { + untested ${testfile}.exp + return -1 +} + +# If we can examine what's at memory address 0, it is possible that we +# could also execute it. This could probably make us run away, +# executing random code, which could have all sorts of ill effects, +# especially on targets without an MMU. Don't run the tests in that +# case. + +gdb_test_multiple "x 0" "memory at address 0" { + -re "0x0:.*Cannot access memory at address 0x0.*$gdb_prompt $" { } + -re "0x0:.*Error accessing memory address 0x0.*$gdb_prompt $" { } + -re ".*$gdb_prompt $" { + untested "Memory at address 0 is possibly executable" + return -1 + } +} + +# Run until we hit the SIGSEGV (or SIGBUS on some platforms). +gdb_test "continue" \ + ".*Program received signal (SIGBUS|SIGSEGV).*bowler.*" \ + "continue to fault" + +# Insert conditional breakpoint at faulting instruction +gdb_test "break if 0" ".*" "set conditional breakpoint" + +# Set SIGSEGV/SIGBUS to pass+nostop +gdb_test "handle SIGSEGV nostop print pass" ".*" "pass SIGSEGV" +gdb_test "handle SIGBUS nostop print pass" ".*" "pass SIGBUS" + +# Step off the faulting instruction into the handler, triggering nested faults +gdb_test "continue" \ + ".*Program received signal (SIGBUS|SIGSEGV).*Program received signal (SIGBUS|SIGSEGV).*exited normally.*" \ + "run through nested faults" + |