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
|
# This testcase is part of GDB, the GNU debugger.
#
# Copyright 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/>.
#
#
# Test several things related to handling linker namespaces:
# * That the user-facing namespace ID is consistent;
require allow_dlmopen_tests
standard_testfile -main.c -lib.c
set srcfile_lib $srcfile2
set so_name dlmopen-lib.so
set binfile_lib [standard_output_file $so_name]
if { [build_executable "build shlib" $binfile_lib $srcfile_lib \
[list debug shlib]] == -1 } {
return
}
if { [build_executable "failed to build" $testfile $srcfile \
[list additional_flags=-DDSO_NAME=\"$binfile_lib\" \
shlib_load debug]] } {
return
}
# Run the command "info sharedlibrary" and get the first namespace
# for the so
proc get_first_so_ns {} {
set ns -1
set lib_regexp [string_to_regexp ${::binfile_lib}]
gdb_test_multiple "info sharedlibrary $::so_name" "get SO namespace" -lbl {
-re "\r\nFrom\\s+To\\s+\(NS\\s+\)?Syms\\s+Read\\s+Shared Object Library(?=\r\n)" {
exp_continue
}
-re "\r\n$::hex\\s+$::hex\\s+\\\[\\\[($::decimal)\\\]\\\]\\s+\[^\r\n]+${lib_regexp}(?=\r\n)" {
if {$ns == -1} {
set ns $expect_out(1,string)
}
exp_continue
}
-re -wrap "" {
}
}
return $ns
}
# Run the tests relating to the command "info sharedlibrary", to
# verify that the namespace ID is consistent.
proc test_info_shared {} {
clean_restart $::binfile
if { ![runto_main] } {
return
}
# First test that we don't print a namespace column at the start.
gdb_test "info sharedlibrary" \
"From\\s+To\\s+Syms\\s+Read\\s+Shared Object Library.*" \
"before loading anything"
gdb_breakpoint [gdb_get_line_number "TAG: first dlclose"]
gdb_continue_to_breakpoint "TAG: first dlclose"
# Next, test that we *do* print a namespace column after loading SOs.
gdb_test "info sharedlibrary" \
"From\\s+To\\s+NS\\s+Syms\\s+Read\\s+Shared Object Library.*" \
"after loading everything"
gdb_assert {[get_first_so_ns] == 1} "before closing any library"
gdb_test "next" ".*second dlclose.*" "close first library"
gdb_assert {[get_first_so_ns] == 2} "after closing one library"
gdb_test "next" ".*third dlclose.*" "close second library"
gdb_assert {[get_first_so_ns] == 3} "before closing two libraries"
gdb_breakpoint [gdb_get_line_number "TAG: fourth dlclose"]
gdb_continue_to_breakpoint "TAG: fourth dlclose"
# As of writing this test, glibc's LMID is just an index on an array of
# namespaces. After closing a namespace, requesting a new one will
# return the index of the lowest-closed namespace, so this will likely
# be namespace 1, and because of glibc's reuse of the r_debug object,
# GDB should be able to assign the same number.
gdb_assert {[get_first_so_ns] == [get_integer_valueof "lmid" "-1"]} \
"reopen a namespace"
gdb_test "next" ".*return 0.*" "final namespace inactive"
gdb_test "info sharedlibrary" \
"From\\s+To\\s+Syms\\s+Read\\s+Shared Object Library.*" \
"after unloading everything"
}
# Run all tests related to the linkage namespaces convenience
# variables, _active_namespaces and _current_namespaces.
proc_with_prefix test_conv_vars {} {
clean_restart $::binfile
gdb_test "print \$_active_linker_namespaces" "1" \
"1 namespace before starting inferior"
gdb_test "print \$_current_linker_namespace" "No registers." \
"No current namespace before starting inferior"
if { ![runto_main] } {
return
}
gdb_test "print \$_active_linker_namespaces" "1" \
"Before activating namespaces"
gdb_test "print \$_current_linker_namespace" ".*\"\\\[\\\[0\\\]\\\]\"" \
"Still in the default namespace"
gdb_breakpoint "inc" allow-pending
gdb_breakpoint [gdb_get_line_number "TAG: first dlclose"]
foreach_with_prefix dl {3 2 1} {
gdb_continue_to_breakpoint "inc"
gdb_test "print \$_current_linker_namespace" ".*\"\\\[\\\[$dl\\\]\\\]\"" \
"Verify we're in namespace $dl"
}
gdb_continue_to_breakpoint "first dlclose"
gdb_test "print \$_active_linker_namespaces" "4" "all SOs loaded"
gdb_test "next" ".*second dlclose.*" "close one SO"
gdb_test "print \$_active_linker_namespaces" "3" "one SOs unloaded"
gdb_test "next" ".*third dlclose.*" "close another SO"
gdb_test "print \$_active_linker_namespaces" "2" "two SOs unloaded"
# Restarting GDB so that we can test setting a breakpoint
# using the convenience variable, while a proper bp syntax
# isn't implemented for namespaces
clean_restart $::binfile
if {![runto_main]} {
return
}
# We need to load one SO because you can't have confitional
# breakpoints and pending breakpoints at the same time with
# gdb_breakpoint.
gdb_test "next" ".*assert.*" "load the first SO"
gdb_breakpoint "inc if \$_streq(\$_current_linker_namespace, \"\[\[2\]\]\")"
gdb_continue_to_breakpoint "inc"
gdb_continue_to_end "" continue 1
}
# Run several tests relating to the command "info namespaces".
proc test_info_linker_namespaces {} {
clean_restart $::binfile
if { ![runto_main] } {
return
}
with_test_prefix "info linker-namespaces" {
gdb_breakpoint [gdb_get_line_number "TAG: first dlclose"]
gdb_continue_to_breakpoint "TAG: first dlclose"
}
# First, test printing a single namespace, and ensure all of
# them are correct, using both syntaxes.
set found_all_libs false
gdb_test_multiple "info linker-namespaces \[\[0\]\]" "print namespace 0" -lbl {
-re "^\r\nThere are ($::decimal) libraries loaded in linker namespace \\\[\\\[0\\\]\\\]" {
# Some systems may add libc and libm to every loaded namespace,
# others may load only one or neither, because the SO doesn't
# actually use either library. The best we can do is check if
# we found the dynamic linker, and up to 2 more libraries.
set libs $expect_out(1,string)
set found_all_libs [expr $libs - 1 <= 2]
exp_continue
}
-re "^\r\n$::gdb_prompt $" {
gdb_assert $found_all_libs "the correct number of libraries was reported"
}
-re "(^\r\n)?\[^\r\n\]+(?=\r\n)" {
exp_continue
}
}
foreach_with_prefix ns {1 2 3} {
set found_test_so false
set found_all_libs false
gdb_test_multiple "info linker-namespaces $ns" "print namespace $ns" -lbl {
-re "^\r\nThere are ($::decimal) libraries loaded in linker namespace \\\[\\\[$ns\\\]\\\]" {
set libs $expect_out(1,string)
# Some systems may add libc and libm to every loaded namespace,
# others may load only one or neither, because the SO doesn't
# actually use either library. The best we can do is check if
# we found the dynamic linker, the test SO, and maybe up to 2
# more libraries.
set found_all_libs [expr $libs - 2 <= 2]
exp_continue
}
-re "^\r\n\[^\r\n\]+${::binfile_lib}\[^\r\n\]*(?=\r\n)" {
set found_test_so true
exp_continue
}
-re "^\r\n$::gdb_prompt $" {
gdb_assert $found_test_so "this testfle's SO was reported"
gdb_assert $found_all_libs "the correct number of libraries was reported"
}
-re "(^\r\n)?\[^\r\n\]+(?=\r\n)" {
exp_continue
}
}
}
# These patterns are simpler, and purposefully glob multiple lines.
# The point is to ensure that we find and display all the namespaces,
# without worrying about the libraries printed, since that was tested
# above.
gdb_test "info linker-namespaces" \
[multi_line "There are 4 linker namespaces loaded" \
"There are $::decimal libraries loaded in linker namespace ..0.." \
".*" \
"There are $::decimal libraries loaded in linker namespace ..1.." \
".*" \
"There are $::decimal libraries loaded in linker namespace ..2.." \
".*" \
"There are $::decimal libraries loaded in linker namespace ..3.." \
".*" ] "print namespaces with no argument"
}
test_info_shared
test_conv_vars
test_info_linker_namespaces
|