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