aboutsummaryrefslogtreecommitdiff
path: root/gdb/testsuite/gdb.base/sigbpt.exp
blob: d79941ec8a9456a86f06295431a055d0590e504f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
# This testcase is part of GDB, the GNU debugger.

# Copyright 2004-2005, 2007-2012 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
}

if $tracelevel {
    strace $tracelevel
}


set testfile "sigbpt"
set srcfile ${testfile}.c
set binfile ${objdir}/${subdir}/${testfile}
if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
    untested sigbpt.exp
    return -1
}

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.

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

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.
    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/1736 "sparc*-*-openbsd*"
	    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/1702 "$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/1702 "$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/1702 "$test (skipped fault insn)"
	}
	-re "pc(\r\n| *)=> 0x\[a-z0-9\]* .*bowler.*$gdb_prompt $" {
	    kfail gdb/1702 "$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.
    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]"