From 45a83408cd75e6572cc3bc432ac33a8989d8f663 Mon Sep 17 00:00:00 2001 From: Andrew Cagney Date: Tue, 6 Jul 2004 15:08:54 +0000 Subject: [PATCH] 2004-07-06 Andrew Cagney * gdb.base/sigbpt.exp, gdb.base/sigbpt.c: New test. --- gdb/testsuite/ChangeLog | 4 + gdb/testsuite/gdb.base/sigbpt.c | 53 ++++++ gdb/testsuite/gdb.base/sigbpt.exp | 263 ++++++++++++++++++++++++++++++ 3 files changed, 320 insertions(+) create mode 100644 gdb/testsuite/gdb.base/sigbpt.c create mode 100644 gdb/testsuite/gdb.base/sigbpt.exp diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 7eff41e9c2d..2f83f7b5f27 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,7 @@ +2004-07-06 Andrew Cagney + + * gdb.base/sigbpt.exp, gdb.base/sigbpt.c: New test. + 2004-07-02 Michael Chastain * lib/compiler.c: Accept __HP_CXD_SPP for old hp ansi c compiler. diff --git a/gdb/testsuite/gdb.base/sigbpt.c b/gdb/testsuite/gdb.base/sigbpt.c new file mode 100644 index 00000000000..b189f6da7da --- /dev/null +++ b/gdb/testsuite/gdb.base/sigbpt.c @@ -0,0 +1,53 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2004 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. */ + +#include +#include +#include + +extern void +keeper (int sig) +{ +} + +volatile long v1 = 0; +volatile long v2 = 0; +volatile long v3 = 0; + +extern long +bowler (void) +{ + /* Try to read address zero. Do it in a slightly convoluted way so + that more than one instruction is used. */ + return *(char *) (v1 + v2 + v3); +} + +int +main () +{ + static volatile int i; + + struct sigaction act; + memset (&act, 0, sizeof act); + act.sa_handler = keeper; + sigaction (SIGSEGV, &act, NULL); + + bowler (); + return 0; +} diff --git a/gdb/testsuite/gdb.base/sigbpt.exp b/gdb/testsuite/gdb.base/sigbpt.exp new file mode 100644 index 00000000000..46d0c86a4b9 --- /dev/null +++ b/gdb/testsuite/gdb.base/sigbpt.exp @@ -0,0 +1,263 @@ +# This testcase is part of GDB, the GNU debugger. + +# Copyright 2004 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 2 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, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +# 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 $tracelevel { + strace $tracelevel +} + +set prms_id 0 +set bug_id 0 + +set testfile "sigbpt" +set srcfile ${testfile}.c +set binfile ${objdir}/${subdir}/${testfile} +if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } { + gdb_suppress_entire_file "Testcase compile failed, so all tests in this file will automatically fail." +} + +gdb_exit +gdb_start +gdb_reinitialize_dir $srcdir/$subdir +gdb_load ${binfile} + +# +# Run to `main' where we begin our tests. +# + +if ![runto_main] then { + gdb_suppress_tests +} + +# 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. + +send_gdb "x 0\n" +gdb_expect { + -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 + } +} + +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. + +set bowler_addrs bowler +gdb_test {display/i $pc} +gdb_test "advance *bowler" "bowler.*" "advance to the bowler" +set test "stepping to SIGSEGV" +gdb_test_multiple "stepi" "$test" { + -re "Program received signal SIGSEGV.*pc *(0x\[0-9a-f\]*).*$gdb_prompt $" { + set segv_addr $expect_out(1,string) + pass "$test" + } + -re " .*pc *(0x\[0-9a-f\]*).*bowler.*$gdb_prompt $" { + set bowler_addrs [concat $expect_out(1,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 SIGSEGV 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 + + # 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. + rerun_to_main + gdb_test "handle SIGSEGV nostop print pass" "" "pass SIGSEGV; $name" + gdb_test "continue" "keeper.*" "continue to keeper; $name" + gdb_test "handle SIGSEGV stop print nopass" "" "nopass SIGSEGV; $name" + + # 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.*" \ + "set breakpoint $i of [llength $args]; $name" + } + + # Single step our way out of the keeper, through the signal + # trampoline, and back to the instruction that faulted. + set test "stepi out of handler; $name" + gdb_test_multiple "stepi" "$test" { + -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/1702 "$test (executed fault insn)" + } + -re "Breakpoint.*pc *[at_segv] .*bowler.*$gdb_prompt $" { + pass "$test (at breakpoint)" + } + -re "Breakpoint.*pc *[after_segv] .*bowler.*$gdb_prompt $" { + kfail gdb/1702 "$test (executed breakpoint)" + } + -re "pc *[at_segv] .*bowler.*$gdb_prompt $" { + pass "$test" + } + -re "pc *[after_segv] .*bowler.*$gdb_prompt $" { + kfail gdb/1702 "$test (skipped fault insn)" + } + } + + # Clear any breakpoints + for {set i 0} {$i < [llength $args]} {incr i} { + gdb_test "clear [lindex $args $i]" "Deleted .*" \ + "clear breakpoint $i of [llength $args]; $name" + } +} + +# 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 + + # 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. + rerun_to_main + gdb_test "handle SIGSEGV nostop print pass" "" "pass SIGSEGV; $name" + gdb_test "continue" "keeper.*" "continue to keeper; $name" + gdb_test "handle SIGSEGV stop print nopass" "" "nopass SIGSEGV; $name" + + # 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.*" \ + "set breakpoint $i of [llength $args]; $name" + } + + # 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 *[at_segv] .*" \ + "continue to breakpoint at fault; $name" + + # Now single step the faulted instrction at that breakpoint. + gdb_test "stepi" \ + "Program received signal SIGSEGV.*pc *[at_segv] .*" \ + "stepi fault; $name" + + # Clear any breakpoints + for {set i 0} {$i < [llength $args]} {incr i} { + gdb_test "clear [lindex $args $i]" "Deleted .*" \ + "clear breakpoint $i of [llength $args]; $name" + } + +} + + + +# 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]"