Home | History | Annotate | Line # | Download | only in gdb.base
      1 # Copyright 2022-2024 Free Software Foundation, Inc.
      2 # This program is free software; you can redistribute it and/or modify
      3 # it under the terms of the GNU General Public License as published by
      4 # the Free Software Foundation; either version 3 of the License, or
      5 # (at your option) any later version.
      6 #
      7 # This program is distributed in the hope that it will be useful,
      8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
      9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	See the
     10 # GNU General Public License for more details.
     11 #
     12 # You should have received a copy of the GNU General Public License
     13 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
     14 
     15 # Test stepping through a runtime loader / dynamic linker (RTLD):
     16 #
     17 # While it'd be nice to have a test which steps through an actual
     18 # runtime loader / dynamic linker, constructing such a test would be
     19 # non-portable; we would need to know implementation details such
     20 # as the names of some of the symbols and the order of calls to
     21 # various functions that implement the RTLD.  So, instead, we'll use a
     22 # program which doesn't even pretend to implement this functionality,
     23 # but which will instead be invoked in the same fashion (for ELF
     24 # binaries anyway) as would be expected for an ELF-based RTLD.
     25 #
     26 # To that end, we have two programs, one which will pretend to be an
     27 # RTLD and the other which will be caused to use the pretend RTLD.
     28 #
     29 # When the main program is run, the pretend/fake RTLD is run instead,
     30 # due to it being specified as the ELF interpreter for the main
     31 # program.  Within GDB, we then attempt to do some simple debugging
     32 # involving 'step', 'next', and 'finish'.
     33 
     34 # This test can't be run on targets lacking shared library support
     35 # or for non-ELF targets.  (We're not really testing or building
     36 # shared libraries here, but having a RTLD implies having shared
     37 # libraries on the target.)
     38 require allow_shlib_tests is_elf_target
     39 
     40 # (Pretend) RTLD file names and flags:
     41 set rtld_basename ${::gdb_test_file_name}-rtld
     42 set srcfile_rtld ${srcdir}/${subdir}/${rtld_basename}.c
     43 set binfile_rtld [standard_output_file ${rtld_basename}]
     44 
     45 # Placing 'pie' in the flag list (for rtld_flags) doesn't work, but
     46 # using -static-pie -FPIE in additional_flags does.  Apparently, when
     47 # 'pie' is listed, gdb_compile will (on Linux) use both -fPIE and
     48 # -pie.	 Testing shows that use of -pie creates a dynamically linked
     49 # executable when either a static or static-pie executable is desired
     50 # instead.  (This is probably fragile.)
     51 #
     52 # While developing this code on Fedora Linux, it was found that (only)
     53 # the flags -static-pie -fPIE were needed for Fedora 35 through Fedora
     54 # 38.  The source file rtld-step-rtld.c didn't need the _start()
     55 # function either.  And, better still, it was possible to call
     56 # printf() to output progress messages in the pretend/fake RTLD.
     57 # Sadly, these output statements had to be removed in order to obtain
     58 # code which would work on other Linux distributions / releases.
     59 #
     60 # When testing against earlier versions of Fedora, RHEL 9, and
     61 # also Ubuntu 22.04, that short flag list didn't work.	For these
     62 # linux releases, it was found that -nostdlib -lc were also required.
     63 # Due to the use of -nostdlib, a _start() function had to be added
     64 # to the RTLD code.
     65 #
     66 # Finally, on FreeBSD, it was found that in order to end up with a
     67 # statically linked executable, -static was also needed.
     68 # Unfortunately, when attempting to run the rtld-step-main under GDB
     69 # on FreeBSD 13.1, this message was/is encountered:
     70 #
     71 # ELF interpreter /path/to/rtld-step-rtld not found, error 22
     72 #
     73 # So, sadly, this test does not currently work on FreeBSD.  If you try
     74 # to make it work on FreeBSD, you'll probably need to enable the
     75 # declarations for __progname and environ in rtld-step-rtld.c.
     76 #
     77 # If this test becomes broken at some point in the future, you might
     78 # try removing -static from the flags below as it is not needed for
     79 # Linux.
     80 #
     81 # Also, because the RTLD is static, you'll need static versions of
     82 # libc/glibc installed on your system.  (A message such as "cannot
     83 # find -lc" is a clue that you're missing a static version of libc.)
     84 
     85 set rtld_flags [list debug additional_flags=[list -static-pie -fPIE \
     86 						  -nostdlib -static -lc]]
     87 
     88 if { ![gdb_can_simple_compile static-pie-static-libc \
     89 	   {
     90 	       #include <unistd.h>
     91 	       void _start (void) { _exit (0); }
     92 	   } \
     93 	   executable $rtld_flags] } {
     94     set reason "-static-pie not supported or static libc missing"
     95     untested "failed to compile ($reason)"
     96     return -1
     97 }
     98 
     99 # Main program file names and flags:
    100 set main_basename ${::gdb_test_file_name}-main
    101 set srcfile_main ${srcdir}/${subdir}/${main_basename}.c
    102 set binfile_main [standard_output_file ${main_basename}]
    103 set main_flags [list debug additional_flags="-Wl,--dynamic-linker=${binfile_rtld}"]
    104 
    105 # Compile pretend RTLD:
    106 if { [gdb_compile ${srcfile_rtld} ${binfile_rtld} executable $rtld_flags] != "" } {
    107     untested "failed to compile"
    108     return -1
    109 }
    110 
    111 # Compile main program:
    112 if { [gdb_compile ${srcfile_main} ${binfile_main} executable $main_flags] != "" } {
    113     untested "failed to compile"
    114     return -1
    115 }
    116 
    117 clean_restart ${binfile_main}
    118 
    119 if {![runto_main]} {
    120     return 0
    121 }
    122 
    123 # Running the command 'info sharedlibrary' should output a path to
    124 # the pretend/fake RTLD along with the address range.  Check that
    125 # this path is present and, if so, extract the address range.
    126 gdb_test_multiple "info sharedlibrary" "" {
    127     -re -wrap "($hex)\[ \t\]+($hex)\[ \t\]+Yes\[ \t\]+$fullname_syntax$rtld_basename" {
    128 	set rtld_lower $expect_out(1,string)
    129 	set rtld_upper $expect_out(2,string)
    130 	pass $gdb_test_name
    131     }
    132 }
    133 
    134 # Fetch PC value.
    135 set pc [get_hexadecimal_valueof "\$pc" 0]
    136 
    137 # Verify that PC is in the address range of the pretend/fake RTLD.
    138 gdb_assert { $rtld_lower <= $pc && $pc < $rtld_upper } "pc is in rtld"
    139 
    140 gdb_test "next" {bar \(\);} "next over foo 0"
    141 gdb_test "step" {bar \(\) at.*foo \(1\);.*} "step into bar"
    142 gdb_test "step" {baz \(.*?\);} "step into foo 1"
    143 gdb_test "finish" {Run till exit.*bar \(\).*baz.*} "finish out of foo 1"
    144 gdb_test "next" {foo \(2\);} "next over baz in bar"
    145 gdb_test "step" {baz \(.*?\);} "step into foo 2"
    146 gdb_test "next" "\}" "next over baz in foo"
    147 gdb_test "step" "bar \\(\\).*}" "step out of foo back into bar"
    148 
    149 gdb_continue_to_end
    150