solib-with-soname.exp revision 1.1 1 # Copyright 2024 Free Software Foundation, Inc.
2 #
3 # This program is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation; either version 3 of the License, or
6 # (at your option) any later version.
7 #
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
12 #
13 # You should have received a copy of the GNU General Public License
14 # along with this program. If not, see <http://www.gnu.org/licenses/>. */
15
16 # This test exercises GDB's ability to validate build-ids when loading
17 # shared libraries for a core file.
18 #
19 # The test creates two "versions" of a shared library, sets up a
20 # symlink to point to one version of the library, and creates a core file.
21 #
22 # We then try re-loading the core file and executable and check that
23 # GDB is able to correctly load the shared library. To confuse things
24 # we retarget the library symlink at the other version of the library.
25 #
26 # After that we repeat the test, but this time deleting the symlink
27 # completely.
28 #
29 # Then we remove the version of the library completely, at this point
30 # we do expect GDB to give a warning about being unable to load the library.
31 #
32 # And finally, we setup debuginfod and have it serve the missing
33 # library file, GDB should correctly download the library file.
34 #
35 # Despite this test living in the gdb.debuginfod/ directory, only the last
36 # part of this test actually uses debuginfod, everything up to that point is
37 # pretty generic.
38
39 load_lib debuginfod-support.exp
40
41 require allow_shlib_tests
42 require {istarget "*-linux*"}
43 require {!is_remote host}
44 require {!using_fission}
45
46 standard_testfile -1.c -2.c
47
48 # Build two similar, but slightly different versions of the shared
49 # library. Both libraries have DT_SONAME set to the generic
50 # libfoo.so, we'll create a symlink with that name later.
51 set library_1_filename [standard_output_file "libfoo_1.so"]
52 set library_2_filename [standard_output_file "libfoo_2.so"]
53
54 # The generic name for the library.
55 set library_filename [standard_output_file "libfoo.so"]
56
57 # When compiling a shared library the -Wl,-soname,NAME option is
58 # automatically added based on the final name of the library. We want
59 # to compile libfoo_1.so, but set the soname to libfoo.so. To achieve
60 # this we first compile into libfoo.so, and then rename the library to
61 # libfoo_1.so.
62 if {[build_executable "build libfoo_1.so" $library_filename \
63 $srcfile \
64 { debug shlib build-id \
65 additional_flags=-DLIB_VERSION=1 }] == -1} {
66 return
67 }
68 remote_exec build "mv ${library_filename} ${library_1_filename}"
69
70 # See the comment above, but this time we rename to libfoo_2.so.
71 if {[build_executable "build libfoo_2.so" $library_filename \
72 $srcfile \
73 { debug shlib build-id \
74 additional_flags=-DLIB_VERSION=2 }] == -1} {
75 return
76 }
77 remote_exec build "mv ${library_filename} ${library_2_filename}"
78
79 # Create libfoo.so symlink to the libfoo_1.so library. If this
80 # symlink creation fails then we assume we can't create symlinks on
81 # this host. If this succeeds then later symlink creation is required
82 # to succeed, and will trigger an FAIL if it doesn't.
83 set status \
84 [remote_exec build \
85 "ln -sf ${library_1_filename} ${library_filename}"]
86 if {[lindex $status 0] != 0} {
87 unsupported "host does not support symbolic links"
88 return
89 }
90
91 # Build the executable. This links against libfoo.so, which is
92 # poining at libfoo_1.so. Just to confuse things even more, this
93 # executable uses dlopen to load libfoo_2.so. Weird!
94 if { [build_executable "build executable" ${binfile} ${srcfile2} \
95 [list debug shlib=${library_filename} shlib_load]] == -1 } {
96 return
97 }
98
99 # If the board file is automatically splitting the debug information
100 # into a separate file (e.g. the cc-with-gnu-debuglink.exp board) then
101 # this test isn't going to work.
102 clean_restart
103 gdb_file_cmd $binfile
104 if {$gdb_file_cmd_debug_info ne "debug"} {
105 unsupported "failed to find debug information"
106 return
107 }
108 if {[regexp "${testfile}.debug" $gdb_file_cmd_msg]} {
109 unsupported "debug information has been split to a separate file"
110 return
111 }
112
113 # Run BINFILE which will generate a corefile.
114 set corefile [core_find $binfile]
115 if {$corefile eq ""} {
116 untested "could not generate core file"
117 return
118 }
119
120 # Helper proc to load global BINFILE and then load global COREFILE.
121 #
122 # If EXPECT_WARNING is true then we require a warning about being
123 # unable to load the shared library symbols, otherwise, EXPECT_WARNING
124 # is false and we require no warning.
125 #
126 # If EXPECT_DOWNLOAD is true then we require a line indicating that
127 # the shared library is being downloaded from debuginfod, otherwise
128 # the shared library should not be downloaded.
129 #
130 # If DEBUGDIR is not the empty string then 'debug-file-directory' is
131 # set to the value of DEBUGDIR.
132 proc load_exec_and_core_file { expect_warning expect_download testname \
133 {debugdir ""} } {
134 with_test_prefix $testname {
135 clean_restart $::binfile
136
137 if { $debugdir ne "" } {
138 gdb_test_no_output "set debug-file-directory $debugdir" \
139 "set debug directory"
140 }
141
142 set saw_warning false
143 set saw_download false
144 set saw_generated false
145 set saw_terminated false
146
147 gdb_test_multiple "core-file $::corefile" "load core file" {
148 -re "^Core was generated by \[^\r\n\]+\r\n" {
149 set saw_generated true
150 exp_continue
151 }
152 -re "^Program terminated with signal \[^\r\n\]+\r\n" {
153 set saw_terminated true
154 exp_continue
155 }
156 -re "^warning: Can't open file \[^\r\n\]+ during file-backed mapping note processing\r\n" {
157 # Ignore warnings from the file backed mapping phase.
158 exp_continue
159 }
160 -re "^warning: Could not load shared library symbols for \[^\r\n\]+/libfoo\\.so\\.\r\n" {
161 set saw_warning true
162 exp_continue
163 }
164 -re "^Downloading\[^\r\n\]*file \[^\r\n\]+/libfoo_1\\.so\\.\\.\\.\r\n" {
165 set saw_download true
166 exp_continue
167 }
168 -re "^$::gdb_prompt $" {
169 gdb_assert { $saw_generated && $saw_terminated \
170 && $saw_warning == $expect_warning \
171 && $saw_download == $expect_download } \
172 $gdb_test_name
173 }
174 -re "^\[^\r\n\]*\r\n" {
175 exp_continue
176 }
177 }
178
179 # If we don't expect a warning then debug symbols from the
180 # shared library should be available. Confirm we can read a
181 # variable from the shared library. If we do expect a warning
182 # then the shared library debug symbols have not loaded, and
183 # the library variable should not be available.
184 if { !$expect_warning } {
185 gdb_test "print/x library_1_var" " = 0x12345678" \
186 "check library_1_var can be read"
187 } else {
188 gdb_test "print/x library_1_var" \
189 "^No symbol \"library_1_var\" in current context\\." \
190 "check library_1_var cannot be read"
191 }
192 }
193 }
194
195 # Initial test, just load the executable and core file. At this point
196 # everything should load fine as everything is where we expect to find
197 # it.
198 load_exec_and_core_file false false \
199 "load core file, all libraries as expected"
200
201 # Update libfoo.so symlink to point at the second library then reload
202 # the core file. GDB should spot that the symlink points to the wrong
203 # file, but should be able to figure out the correct file to load as
204 # the right file will be in the mapped file list.
205 set status [remote_exec build \
206 "ln -sf ${library_2_filename} ${library_filename}"]
207 gdb_assert { [lindex $status 0] == 0 } \
208 "update library symlink to point to the wrong file"
209
210 load_exec_and_core_file false false \
211 "load core file, symlink points to wrong file"
212
213 # Remove libfoo.so symlink and reload the core file. As in the
214 # previous test GDB should be able to figure out the correct file to
215 # load as the correct file will still appear in the mapped file list.
216 set status [remote_exec build "rm -f ${library_filename}"]
217 gdb_assert { [lindex $status 0] == 0 } "remove library symlink"
218
219 load_exec_and_core_file false false \
220 "load core file, symlink removed"
221
222 # Remove LIBRARY_1_FILENAME. We'll now see a warning that the mapped
223 # file can't be loaded (we ignore that warning), and we'll see a
224 # warning that the shared library can't be loaded.
225 set library_1_backup_filename ${library_1_filename}.backup
226 set status \
227 [remote_exec build \
228 "mv ${library_1_filename} ${library_1_backup_filename}"]
229 gdb_assert { [lindex $status 0] == 0 } \
230 "remove libfoo_1.so"
231
232 load_exec_and_core_file true false \
233 "load core file, libfoo_1.so removed"
234
235 # Symlink the .build-id/xx/xxx...xxx filename within the debug
236 # directory to LIBRARY_1_BACKUP_FILENAME, now when we restart GDB it
237 # should find the missing library within the debug directory.
238 set debugdir [standard_output_file "debugdir"]
239 set build_id_filename \
240 $debugdir/[build_id_debug_filename_get $library_1_backup_filename ""]
241 set status \
242 [remote_exec build \
243 "mkdir -p [file dirname $build_id_filename]"]
244 gdb_assert { [lindex $status 0] == 0 } \
245 "create sub-directory within the debug directory"
246 set status \
247 [remote_exec build \
248 "ln -sf $library_1_backup_filename $build_id_filename"]
249 gdb_assert { [lindex $status 0] == 0 } \
250 "create symlink within the debug directory "
251
252 load_exec_and_core_file false false \
253 "load core file, find libfoo_1.so through debug-file-directory" \
254 $debugdir
255
256 # Setup a debuginfod server which can serve the original shared
257 # library file.
258 if {![allow_debuginfod_tests]} {
259 untested "skippig debuginfod parts of this test"
260 return
261 }
262
263 set server_dir [standard_output_file "debuginfod.server"]
264 file mkdir $server_dir
265 file rename -force $library_1_backup_filename $server_dir
266
267 prepare_for_debuginfod cache db
268
269 set url [start_debuginfod $db $server_dir]
270 if { $url eq "" } {
271 unresolved "failed to start debuginfod server"
272 return
273 }
274
275 with_debuginfod_env $cache {
276 setenv DEBUGINFOD_URLS $url
277
278 save_vars { GDBFLAGS } {
279 append GDBFLAGS " -ex \"set debuginfod enabled on\""
280
281 # Reload the executable and core file. GDB should download
282 # the file libfoo_1.so using debuginfod during the mapped file
283 # phase, but should then reuse that download during the shared
284 # library phase.
285 load_exec_and_core_file false true \
286 "load core file, use debuginfod"
287 }
288 }
289
290 stop_debuginfod
291