Home | History | Annotate | Line # | Download | only in gdb.debuginfod
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