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