summaryrefslogtreecommitdiff
path: root/gdb/testsuite/gdb.base/rtld-step.exp
blob: 0aa2ab0fe1fb757c32aca7c135dd4be01f6001bc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# Copyright 2022 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

# Test stepping through a runtime loader / dynamic linker (RTLD):
#
# While it'd be nice to have a test which steps through an actual
# runtime loader / dynamic linker, constructing such a test would be
# non-portable; we would need to know implementation details such
# as the names of some of the symbols and the order of calls to
# various functions that implement the RTLD.  So, instead, we'll use a
# program which doesn't even pretend to implement this functionality,
# but which will instead be invoked in the same fashion (for ELF
# binaries anyway) as would be expected for an ELF-based RTLD.
#
# To that end, we have two programs, one which will pretend to be an
# RTLD and the other which will be caused to use the pretend RTLD.
#
# When the main program is run, the pretend/fake RTLD is run instead,
# due to it being specified as the ELF interpreter for the main
# program.  Within GDB, we then attempt to do some simple debugging
# involving 'step', 'next', and 'finish'.

# This test can't be run on targets lacking shared library support
# or for non-ELF targets.  (We're not really testing or building
# shared libraries here, but having a RTLD implies having shared
# libraries on the target.)
if { [skip_shlib_tests] || ![is_elf_target] } {
    return 0
}

# (Pretend) RTLD file names and flags:
set rtld_basename ${::gdb_test_file_name}-rtld
set srcfile_rtld ${srcdir}/${subdir}/${rtld_basename}.c
set binfile_rtld [standard_output_file ${rtld_basename}]

# Placing 'pie' in the flag list (for rtld_flags) doesn't work, but
# using -static-pie -FPIE in additional_flags does.  Apparently, when
# 'pie' is listed, gdb_compile will (on Linux) use both -fPIE and
# -pie.	 Testing shows that use of -pie creates a dynamically linked
# executable when either a static or static-pie executable is desired
# instead.  (This is probably fragile.)
#
# While developing this code on Fedora Linux, it was found that (only)
# the flags -static-pie -fPIE were needed for Fedora 35 through Fedora
# 38.  The source file rtld-step-rtld.c didn't need the _start()
# function either.  And, better still, it was possible to call
# printf() to output progress messages in the pretend/fake RTLD. 
# Sadly, these output statements had to be removed in order to obtain
# code which would work on other Linux distributions / releases.
#
# When testing against earlier versions of Fedora, RHEL 9, and
# also Ubuntu 22.04, that short flag list didn't work.	For these
# linux releases, it was found that -nostdlib -lc were also required.
# Due to the use of -nostdlib, a _start() function had to be added
# to the RTLD code.
#
# Finally, on FreeBSD, it was found that in order to end up with a
# statically linked executable, -static was also needed.
# Unfortunately, when attempting to run the rtld-step-main under GDB
# on FreeBSD 13.1, this message was/is encountered:
#
# ELF interpreter /path/to/rtld-step-rtld not found, error 22
#
# So, sadly, this test does not currently work on FreeBSD.  If you try
# to make it work on FreeBSD, you'll probably need to enable the
# declarations for __progname and environ in rtld-step-rtld.c.
#
# If this test becomes broken at some point in the future, you might
# try removing -static from the flags below as it is not needed for
# Linux.
#
# Also, because the RTLD is static, you'll need static versions of
# libc/glibc installed on your system.  (A message such as "cannot
# find -lc" is a clue that you're missing a static version of libc.)

set rtld_flags [list debug additional_flags=[list -static-pie -fPIE \
						  -nostdlib -static -lc]]

if { ![gdb_can_simple_compile static-pie-static-libc \
	   "void _start (void) { _exit (0); }" \
	   executable $rtld_flags] } {
    set reason "-static-pie not supported or static libc missing"
    untested "failed to compile ($reason)"
    return -1
}

# Main program file names and flags:
set main_basename ${::gdb_test_file_name}-main
set srcfile_main ${srcdir}/${subdir}/${main_basename}.c
set binfile_main [standard_output_file ${main_basename}]
set main_flags [list debug additional_flags="-Wl,--dynamic-linker=${binfile_rtld}"]

# Compile pretend RTLD:
if { [gdb_compile ${srcfile_rtld} ${binfile_rtld} executable $rtld_flags] != "" } {
    untested "failed to compile"
    return -1
}

# Compile main program:
if { [gdb_compile ${srcfile_main} ${binfile_main} executable $main_flags] != "" } {
    untested "failed to compile"
    return -1
}

clean_restart ${binfile_main}

if {![runto_main]} {
    return 0
}

# Running the command 'info sharedlibrary' should output a path to
# the pretend/fake RTLD along with the address range.  Check that
# this path is present and, if so, extract the address range.
gdb_test_multiple "info sharedlibrary" "" {
    -re -wrap "($hex)\[ \t\]+($hex)\[ \t\]+Yes\[ \t\]+$fullname_syntax$rtld_basename" {
	set rtld_lower $expect_out(1,string)
	set rtld_upper $expect_out(2,string)
	pass $gdb_test_name
    }
}

# Fetch PC value.
set pc [get_hexadecimal_valueof "\$pc" 0]

# Verify that PC is in the address range of the pretend/fake RTLD.
gdb_assert { $rtld_lower <= $pc && $pc < $rtld_upper } "pc is in rtld"

gdb_test "next" {bar \(\);} "next over foo 0"
gdb_test "step" {bar \(\) at.*foo \(1\);.*} "step into bar"
gdb_test "step" {baz \(.*?\);} "step into foo 1"
gdb_test "finish" {Run till exit.*bar \(\).*baz.*} "finish out of foo 1"
gdb_test "next" {foo \(2\);} "next over baz in bar"
gdb_test "step" {baz \(.*?\);} "step into foo 2"
gdb_test "next" "\}\[\r\n\]+" "next over baz in foo"
gdb_test "step" "bar \\(\\).*}\[\r\n\]+.*" "step out of foo back into bar"

gdb_continue_to_end