| # This testcase is part of GDB, the GNU debugger. |
| |
| # Copyright 2004-2021 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/>. |
| |
| # Check that GDB can and only executes single instructions when |
| # stepping through a sequence of breakpoints interleaved by a signal |
| # handler. |
| |
| # This test is known to tickle the following problems: kernel letting |
| # the inferior execute both the system call, and the instruction |
| # following, when single-stepping a system call; kernel failing to |
| # propogate the single-step state when single-stepping the sigreturn |
| # system call, instead resuming the inferior at full speed; GDB |
| # doesn't know how to software single-step across a sigreturn |
| # instruction. Since the kernel problems can be "fixed" using |
| # software single-step this is KFAILed rather than XFAILed. |
| |
| if [target_info exists gdb,nosignals] { |
| verbose "Skipping sigbpt.exp because of nosignals." |
| continue |
| } |
| |
| |
| standard_testfile |
| |
| if {[prepare_for_testing "failed to prepare" $testfile $srcfile debug]} { |
| return -1 |
| } |
| |
| # |
| # Run to `main' where we begin our tests. |
| # |
| |
| if ![runto_main] then { |
| return 0 |
| } |
| |
| # 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. |
| |
| if { [is_address_zero_readable] } { |
| untested "memory at address 0 is possibly executable" |
| return |
| } |
| |
| gdb_test "break keeper" |
| |
| # Run to bowler, and then single step until there's a SIGSEGV. Record |
| # the address of each single-step instruction (up to and including the |
| # instruction that causes the SIGSEGV) in bowler_addrs, and the address |
| # of the actual SIGSEGV in segv_addr. |
| # Note: this test detects which signal is received. Usually it is SIGSEGV |
| # (and we use SIGSEGV in comments) but on Darwin it is SIGBUS. |
| |
| set bowler_addrs bowler |
| set segv_addr none |
| gdb_test {display/i $pc} |
| gdb_test "advance bowler" "bowler.*" "advance to the bowler" |
| set test "stepping to fault" |
| set signame "SIGSEGV" |
| gdb_test_multiple "stepi" "$test" { |
| -re "Program received signal (SIGBUS|SIGSEGV).*pc(\r\n| *) *=> (0x\[0-9a-f\]*).*$gdb_prompt $" { |
| set signame $expect_out(1,string) |
| set segv_addr $expect_out(3,string) |
| pass "$test" |
| } |
| -re " .*pc(\r\n| *)=> (0x\[0-9a-f\]*).*bowler.*$gdb_prompt $" { |
| set bowler_addrs [concat $expect_out(2,string) $bowler_addrs] |
| send_gdb "stepi\n" |
| exp_continue |
| } |
| } |
| |
| # Now record the address of the instruction following the faulting |
| # instruction in bowler_addrs. |
| |
| set test "get insn after fault" |
| gdb_test_multiple {x/2i $pc} "$test" { |
| -re "=> (0x\[0-9a-f\]*).*bowler.*(0x\[0-9a-f\]*).*bowler.*$gdb_prompt $" { |
| set bowler_addrs [concat $expect_out(2,string) $bowler_addrs] |
| pass "$test" |
| } |
| } |
| |
| # Procedures for returning the address of the instruction before, at |
| # and after, the faulting instruction. |
| |
| proc before_segv { } { |
| global bowler_addrs |
| return [lindex $bowler_addrs 2] |
| } |
| |
| proc at_segv { } { |
| global bowler_addrs |
| return [lindex $bowler_addrs 1] |
| } |
| |
| proc after_segv { } { |
| global bowler_addrs |
| return [lindex $bowler_addrs 0] |
| } |
| |
| # Check that the address table and SIGSEGV correspond. |
| |
| set test "verify that ${signame} occurs at the last STEPI insn" |
| if {[string compare $segv_addr [at_segv]] == 0} { |
| pass "$test" |
| } else { |
| fail "$test ($segv_addr [at_segv])" |
| } |
| |
| # Check that the inferior is correctly single stepped all the way back |
| # to a faulting instruction. |
| |
| proc stepi_out { name args } { |
| global gdb_prompt |
| global signame |
| |
| # Set SIGSEGV to pass+nostop and then run the inferior all the way |
| # through to the signal handler. With the handler is reached, |
| # disable SIGSEGV, ensuring that further signals stop the |
| # inferior. Stops a SIGSEGV infinite loop when a broke system |
| # keeps re-executing the faulting instruction. |
| with_test_prefix $name { |
| rerun_to_main |
| } |
| gdb_test "handle ${signame} nostop print pass" ".*" "${name}; pass ${signame}" |
| gdb_test "continue" "keeper.*" "${name}; continue to keeper" |
| gdb_test "handle ${signame} stop print nopass" ".*" "${name}; nopass ${signame}" |
| |
| # Insert all the breakpoints. To avoid the need to step over |
| # these instructions, this is delayed until after the keeper has |
| # been reached. |
| for {set i 0} {$i < [llength $args]} {incr i} { |
| gdb_test "break [lindex $args $i]" "Breakpoint.*" \ |
| "${name}; set breakpoint $i of [llength $args]" |
| } |
| |
| # Single step our way out of the keeper, through the signal |
| # trampoline, and back to the instruction that faulted. |
| set test "${name}; stepi out of handler" |
| gdb_test_multiple "stepi" "$test" { |
| -re "Could not insert single-step breakpoint.*$gdb_prompt $" { |
| setup_kfail gdb/8841 "sparc*-*-openbsd*" |
| fail "$test (could not insert single-step breakpoint)" |
| } |
| -re "Cannot insert breakpoint.*Cannot access memory.*$gdb_prompt $" { |
| setup_kfail gdb/8841 "nios2*-*-linux*" |
| fail "$test (could not insert single-step breakpoint)" |
| } |
| -re "keeper.*$gdb_prompt $" { |
| send_gdb "stepi\n" |
| exp_continue |
| } |
| -re "signal handler.*$gdb_prompt $" { |
| send_gdb "stepi\n" |
| exp_continue |
| } |
| -re "Program received signal SIGSEGV.*$gdb_prompt $" { |
| kfail gdb/8807 "$test (executed fault insn)" |
| } |
| -re "Breakpoint.*pc(\r\n| *)[at_segv] .*bowler.*$gdb_prompt $" { |
| pass "$test (at breakpoint)" |
| } |
| -re "Breakpoint.*pc(\r\n| *)[after_segv] .*bowler.*$gdb_prompt $" { |
| kfail gdb/8807 "$test (executed breakpoint)" |
| } |
| -re "pc(\r\n| *)[at_segv] .*bowler.*$gdb_prompt $" { |
| pass "$test" |
| } |
| -re "pc(\r\n| *)[after_segv] .*bowler.*$gdb_prompt $" { |
| kfail gdb/8807 "$test (skipped fault insn)" |
| } |
| -re "pc(\r\n| *)=> 0x\[a-z0-9\]* .*bowler.*$gdb_prompt $" { |
| kfail gdb/8807 "$test (corrupt pc)" |
| } |
| } |
| |
| # Clear any breakpoints |
| for {set i 0} {$i < [llength $args]} {incr i} { |
| gdb_test "clear [lindex $args $i]" "Deleted .*" \ |
| "${name}; clear breakpoint $i of [llength $args]" |
| } |
| } |
| |
| # Let a signal handler exit, returning to a breakpoint instruction |
| # inserted at the original fault instruction. Check that the |
| # breakpoint is hit, and that single stepping off that breakpoint |
| # executes the underlying fault instruction causing a SIGSEGV. |
| |
| proc cont_out { name args } { |
| global gdb_prompt |
| global signame |
| |
| # Set SIGSEGV to pass+nostop and then run the inferior all the way |
| # through to the signal handler. With the handler is reached, |
| # disable SIGSEGV, ensuring that further signals stop the |
| # inferior. Stops a SIGSEGV infinite loop when a broke system |
| # keeps re-executing the faulting instruction. |
| with_test_prefix $name { |
| rerun_to_main |
| } |
| gdb_test "handle ${signame} nostop print pass" ".*" "${name}; pass ${signame}" |
| gdb_test "continue" "keeper.*" "${name}; continue to keeper" |
| gdb_test "handle ${signame} stop print nopass" ".*" "${name}; nopass ${signame}" |
| |
| # Insert all the breakpoints. To avoid the need to step over |
| # these instructions, this is delayed until after the keeper has |
| # been reached. Always set a breakpoint at the signal trampoline |
| # instruction. |
| set args [concat $args "*[at_segv]"] |
| for {set i 0} {$i < [llength $args]} {incr i} { |
| gdb_test "break [lindex $args $i]" "Breakpoint.*" \ |
| "${name}; set breakpoint $i of [llength $args]" |
| } |
| |
| # Let the handler return, it should "appear to hit" the breakpoint |
| # inserted at the faulting instruction. Note that the breakpoint |
| # instruction wasn't executed, rather the inferior was SIGTRAPed |
| # with the PC at the breakpoint. |
| gdb_test "continue" "Breakpoint.*pc(\r\n| *)=> [at_segv] .*" \ |
| "${name}; continue to breakpoint at fault" |
| |
| # Now single step the faulted instrction at that breakpoint. |
| gdb_test "stepi" \ |
| "Program received signal ${signame}.*pc(\r\n| *)=> [at_segv] .*" \ |
| "${name}; stepi fault" |
| |
| # Clear any breakpoints |
| for {set i 0} {$i < [llength $args]} {incr i} { |
| gdb_test "clear [lindex $args $i]" "Deleted .*" \ |
| "${name}; clear breakpoint $i of [llength $args]" |
| } |
| |
| } |
| |
| |
| |
| # Try to confuse DECR_PC_AFTER_BREAK architectures by scattering |
| # breakpoints around the faulting address. In all cases the inferior |
| # should single-step out of the signal trampoline halting (but not |
| # executing) the fault instruction. |
| |
| stepi_out "stepi" |
| stepi_out "stepi bp before segv" "*[before_segv]" |
| stepi_out "stepi bp at segv" "*[at_segv]" |
| stepi_out "stepi bp before and at segv" "*[at_segv]" "*[before_segv]" |
| |
| |
| # Try to confuse DECR_PC_AFTER_BREAK architectures by scattering |
| # breakpoints around the faulting address. In all cases the inferior |
| # should exit the signal trampoline halting at the breakpoint that |
| # replaced the fault instruction. |
| cont_out "cont" |
| cont_out "cont bp after segv" "*[before_segv]" |
| cont_out "cont bp before and after segv" "*[before_segv]" "*[after_segv]" |