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
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
|
# Copyright 2024-2025 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/>.
# Tests for GDB's handling of a shared library being unloaded via a
# call to dlclose. See the individual test_* procs for a description
# of each test.
standard_testfile .c -lib.c
# One of the tests uses this Python file. The test_* proc checks that
# GDB supports Python tests. Some of the other procs don't use this
# Python file.
set pyfile [gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py]
# Build the library and copy it to the target.
set libname ${testfile}-lib
set libfile [standard_output_file $libname]
if { [build_executable "build shlib" $libfile $srcfile2 {debug shlib}] == -1} {
return
}
set libfile_on_target [gdb_download_shlib $libfile]
# Build the executable.
set opts [list debug shlib_load additional_flags=-DSHLIB_NAME=\"${libname}\"]
if { [build_executable "build exec" $binfile $srcfile $opts] == -1} {
return
}
# The line number of the dlclose call.
set bp_line [gdb_get_line_number "Break here" $srcfile]
# If the target is remote, then the library name in the bp_disabled_re
# below will have a 'target:' prefix.
if {[is_remote target]} {
set target_prefix_re "target:"
} else {
set target_prefix_re ""
}
# The line emitted when GDB disables breakpoints after unloading a
# shared library.
set bp_disabled_re "warning: Temporarily disabling breakpoints for unloaded shared library \"$target_prefix_re[string_to_regexp $::libfile_on_target]\""
# The complete regexp for when GDB stops on the line after BP_LINE,
# assuming that GDB has disabled some breakpoints.
set stop_after_bp_re [multi_line \
"^$::bp_disabled_re" \
"[expr $::bp_line + 1]\\s+assert \\(res == 0\\);"]
# Checking that a breakpoint with multiple locations in a shared
# library only triggers a single breakpoint modified event from
# disable_breakpoints_in_unloaded_shlib when the shared library is
# unloaded.
proc_with_prefix test_bp_modified_events {} {
if { ![allow_python_tests] } {
unsupported "python support needed"
return
}
clean_restart $::binfile
if {![runto_main]} {
return
}
# If the debug information doesn't allow GDB to identify inline
# functions then this test isn't going to work.
get_debug_format
if { [skip_inline_frame_tests] } {
unsupported "skipping inline frame tests"
return
}
gdb_breakpoint $::srcfile:$::bp_line
gdb_continue_to_breakpoint "stop before dlclose"
gdb_breakpoint inline_func
set bp_num [get_integer_valueof "\$bpnum" "*UNKNOWN*" \
"get b/p number"]
gdb_test_no_output "source $::pyfile" "import python scripts"
gdb_test "next" $::stop_after_bp_re
# The breakpoint should have been modified once when some of its
# locations are made pending after the shared library is unloaded.
gdb_test_multiple "python print(bp_modified_counts\[$bp_num\])" "" {
-re -wrap "^1" {
pass $gdb_test_name
}
-re -wrap "^2" {
# A second event occurs when the pending breakpoint is
# incorrectly deleted.
kfail gdb/32404 $gdb_test_name
}
-re -wrap "^$::decimal" {
fail $gdb_test_name
}
}
}
# Check that GDB disables dprintf breakpoints within a shared library
# when the shared library is unloaded.
proc_with_prefix test_dprintf_after_unload {} {
clean_restart $::binfile
if {![runto_main]} {
return
}
gdb_breakpoint $::srcfile:$::bp_line
gdb_continue_to_breakpoint "stop before dlclose"
# Setup a dprintf within the shared library.
gdb_test "dprintf foo,\"In foo\""
set bp_num [get_integer_valueof "\$bpnum" "*UNKNOWN*" \
"get b/p number"]
# Unload the shared library, GDB should disable our b/p.
gdb_test "next" $::stop_after_bp_re
# Check that our b/p is now showing as disabled.
gdb_test "info breakpoints $bp_num" \
[multi_line \
"^Num\\s+Type\\s+Disp\\s+Enb\\s+Address\\s+What" \
"$bp_num\\s+dprintf\\s+keep\\s+y\\s+<PENDING>\\s+foo" \
"\\s+printf \"In foo\""]
}
# Create a dprintf breakpoint in a shared library. Restart the
# inferior. We should not get an error about re-setting the dprintf
# breakpoint.
proc_with_prefix test_dprintf_with_rerun {} {
clean_restart $::binfile
if {![runto_main]} {
return
}
gdb_breakpoint $::srcfile:$::bp_line
gdb_continue_to_breakpoint "stop before dlclose"
# Setup a dprintf within the shared library.
gdb_test "dprintf foo,\"In foo\""
set bp_num [get_integer_valueof "\$bpnum" "*UNKNOWN*" \
"get b/p number"]
# Check that the dprintf b/p is initially not pending.
gdb_test "info breakpoints $bp_num" \
[multi_line \
"^Num\\s+Type\\s+Disp\\s+Enb\\s+Address\\s+What" \
"$bp_num\\s+dprintf\\s+keep\\s+y\\s+$::hex\\s+in foo at \[^\r\n\]+" \
"\\s+printf \"In foo\""] \
"dprintf is non-pending before restart"
# Restart the inferior.
gdb_run_cmd
# The inferior will stop at the initial 'main' breakpoint. This
# is at a location before any of the shlibs have been loaded.
set saw_bp_reset_error false
set saw_bp_disable_warning false
gdb_test_multiple "" "stop before shlib are reloaded" {
-re "warning: Temporarily disabling breakpoints for unloaded shared library \"\[^\r\n\]+/$::libname\"\r\n" {
set saw_bp_disable_warning true
exp_continue
}
-re "Error in re-setting breakpoint $bp_num: \[^\r\n\]+" {
set saw_bp_reset_error true
exp_continue
}
-re "Breakpoint $::decimal, main \\(\\) at \[^\r\n\]+\r\n$::decimal\\s+\[^\r\n\]+\r\n$::gdb_prompt $" {
gdb_assert { !$saw_bp_reset_error && !$saw_bp_disable_warning } $gdb_test_name
}
}
# Check that the dprintf b/p is still enabled, but marked pending
# before the shlib are loaded.
gdb_test "info breakpoints $bp_num" \
[multi_line \
"^Num\\s+Type\\s+Disp\\s+Enb\\s+Address\\s+What" \
"$bp_num\\s+dprintf\\s+keep\\s+y\\s+<PENDING>\\s+foo" \
"\\s+printf \"In foo\""] \
"dprintf is pending before shlib reload"
set saw_in_foo_output false
gdb_test_multiple "continue" "stop after libraries are reloaded" {
-re "^continue\r\n" {
exp_continue
}
-re "^Continuing\\.\r\n" {
exp_continue
}
-re "^In foo\r\n" {
set saw_in_foo_output true
exp_continue
}
-re "^Breakpoint $::decimal, main \\(\\) at .* Break here\\. \\*/\r\n$::gdb_prompt $" {
gdb_assert { $saw_in_foo_output } $gdb_test_name
}
}
# Check that the dprintf b/p is still enabled, but is now, no
# longer pending.
gdb_test "info breakpoints $bp_num" \
[multi_line \
"^Num\\s+Type\\s+Disp\\s+Enb\\s+Address\\s+What" \
"$bp_num\\s+dprintf\\s+keep\\s+y\\s+$::hex\\s+in foo at \[^\r\n\]+" \
"\\s+breakpoint already hit 1 time" \
"\\s+printf \"In foo\""] \
"dprintf is non-pending after restart"
}
# Check that we see breakpoint modified events (where appropriate)
# when the 'nosharedlibrary' command is used to unload all shared
# libraries.
#
# Also check that the 'nosharedlibrary' doesn't trigger a warning
# about shared library breakpoints being disabled.
proc_with_prefix test_silent_nosharedlib {} {
if { ![allow_python_tests] } {
unsupported "python support needed"
return
}
foreach_with_prefix type { breakpoint dprintf } {
clean_restart $::binfile
if {![runto_main]} {
return
}
gdb_breakpoint $::srcfile:$::bp_line
gdb_continue_to_breakpoint "stop before dlclose"
# Setup a dprintf or breakpoint in the shared library.
if { $type eq "breakpoint" } {
gdb_test "break foo"
} else {
gdb_test "dprintf foo,\"In foo\""
}
# Record the number of the b/p (or dprintf) we just inserted.
set bp_num [get_integer_valueof "\$bpnum" "*UNKNOWN*" \
"get b/p number"]
# Load Python library to track b/p modifications.
gdb_test_no_output "source $::pyfile" "import python scripts"
# Initialise the b/p modified hash. Currently dprintf style
# breakpoints are not visible from Python, so the modification
# count will remain unchanged in that case.
gdb_test_no_output "python bp_modified_counts\[$bp_num\] = 0"
# Discard symbols from all loaded shared libraries.
gdb_test_no_output "nosharedlibrary"
# Check that our b/p is now showing as disabled.
if { $type eq "breakpoint" } {
set re \
[list "$bp_num\\s+breakpoint\\s+keep\\s+y\\s+<PENDING>\\s+foo"]
set count 1
} else {
set re \
[list \
"$bp_num\\s+dprintf\\s+keep\\s+y\\s+<PENDING>\\s+foo" \
"\\s+printf \"In foo\""]
set count 0
}
gdb_test "info breakpoints $bp_num" \
[multi_line "^Num\\s+Type\\s+Disp\\s+Enb\\s+Address\\s+What" \
{*}$re]
# Check we've seen the expected number of breakpoint modified
# events. Currently dprintf breakpoints are not visible from
# Python, so we will not see an event in that case.
gdb_test "python print(bp_modified_counts\[$bp_num\])" "^$count"
}
}
test_bp_modified_events
test_dprintf_after_unload
test_dprintf_with_rerun
test_silent_nosharedlib
|