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