This is the mail archive of the gdb-patches@sources.redhat.com mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[rfa] New test sigbpt.{c,exp}


Hello,

This test checks for interaction between signal trampolines, single-step and breakpoints. If nothing else, it highlights broken kernel code - given a system call, both the system call instruction, and the one following are executed.

I've a hacked i386 (Linux) and PPC (NetBSD) kernel where all the tests pass.

ok?
Andrew
2004-06-18  Andrew Cagney  <cagney@gnu.org>

	* gdb.base/sigbpt.exp, gdb.base/sigbpt.c: New test.

Index: ./gdb/testsuite/gdb.base/sigbpt.c
===================================================================
RCS file: ./gdb/testsuite/gdb.base/sigbpt.c
diff -N ./gdb/testsuite/gdb.base/sigbpt.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ./gdb/testsuite/gdb.base/sigbpt.c	23 Jun 2004 13:52:43 -0000
@@ -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 <signal.h>
+#include <stdlib.h>
+#include <string.h>
+
+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;
+}
Index: ./gdb/testsuite/gdb.base/sigbpt.exp
===================================================================
RCS file: ./gdb/testsuite/gdb.base/sigbpt.exp
diff -N ./gdb/testsuite/gdb.base/sigbpt.exp
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ./gdb/testsuite/gdb.base/sigbpt.exp	23 Jun 2004 13:52:43 -0000
@@ -0,0 +1,231 @@
+# 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 signal.
+
+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.  Gather
+# the address of every stepped instruction in bowler (step_insn), and
+# the address of the SIGSEGV (segv_insn).
+
+set step_insn 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_insn $expect_out(1,string)
+	pass "$test"
+    }
+    -re " .*pc *(0x\[0-9a-f\]*).*bowler.*$gdb_prompt $" {
+	set step_insn [concat $expect_out(1,string) $step_insn]
+	send_gdb "stepi\n"
+	exp_continue
+    }
+}
+
+# Now find the address of the instruction after the faulting
+# instruction.
+
+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 step_insn [concat $expect_out(2,string) $step_insn]
+	pass "$test"
+    }
+}
+
+set test "Verify that SIGSEGV occures at the last STEPI insn"
+if {[string compare $segv_insn [lindex $step_insn 1]] == 0} {
+    pass "$test"
+} else {
+    fail "$test ($segv_insn [lindex $step_insn 1])"
+}
+
+# Check that the inferior is correctly single stepped all the way back
+# to a faulting instruction.
+
+proc stepi_out { name step_insn 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 for $name"
+    gdb_test "continue" "keeper.*" "continue to keeper for $name"
+    gdb_test "handle SIGSEGV stop print nopass" "" "pass SIGSEGV for $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] for $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 for $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 $" {
+	    fail "$test (executed fault insn)"
+	}
+	-re "Breakpoint.*pc *[lindex $step_insn 1] .*bowler.*$gdb_prompt $" {
+	    pass "$test (at breakpoint)"
+	}
+	-re "Breakpoint.*pc *[lindex $step_insn 0] .*bowler.*$gdb_prompt $" {
+	    fail "$test (executed breakpoint)"
+	}
+	-re "pc *[lindex $step_insn 1] .*bowler.*$gdb_prompt $" {
+	    pass "$test"
+	}
+	-re "pc *[lindex $step_insn 0] .*bowler.*$gdb_prompt $" {
+	    fail "$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] for $name"
+    }
+}
+
+stepi_out "no breakpoints" $step_insn
+
+# Try to confuse DECR_PC_AFTER_BREAK architectures by scattering
+# breakpoints around the faulting address.
+
+stepi_out "bp before segv" $step_insn \
+    "*[lindex $step_insn 2]"
+stepi_out "bp at segv" $step_insn \
+    "*[lindex $step_insn 1]"
+stepi_out "bp before and at segv" $step_insn \
+    "*[lindex $step_insn 1]" "*[lindex $step_insn 2]"
+
+# Let a signal handler exit, returning to a breakpoint instruction.
+# Check that the breakpoint is hit, and then single stepping triggers
+# a further SIGSEGV.
+
+proc cont_to_bp { name step_insn 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 for $name"
+    gdb_test "continue" "keeper.*" "continue to keeper for $name"
+    gdb_test "handle SIGSEGV stop print nopass" "" "pass SIGSEGV for $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 "*[lindex $step_insn 1]"]
+    for {set i 0} {$i < [llength $args]} {incr i} {
+	gdb_test "break [lindex $args $i]" "Breakpoint.*" \
+	    "set breakpoint $i  of [llength $args] for $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 *[lindex $step_insn 1] .*" \
+	"continue to breakpoint at fault for $name"
+
+    # Now single step the faulted instrction at that breakpoint.
+    gdb_test "stepi" \
+	"Program received signal SIGSEGV.*pc *[lindex $step_insn 1] .*" \
+	"stepi fault for $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] for $name"
+    }
+
+}
+
+cont_to_bp "no breakpoints" $step_insn
+
+# Try to confuse DECR_PC_AFTER_BREAK architectures by scattering
+# breakpoints around the faulting address.
+cont_to_bp "no breakpoints" $step_insn \
+    "*[lindex $step_insn 2]"
+cont_to_bp "no breakpoints" $step_insn \
+    "*[lindex $step_insn 2]" "*[lindex $step_insn 0]"

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]