aboutsummaryrefslogtreecommitdiff
path: root/gdb/testsuite/gdb.debuginfod/solib-with-soname.exp
blob: 98c4535fca7ae844cc5fd5393b592ab296f3ccae (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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
# Copyright 2024 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/>.  */

# This test exercises GDB's ability to validate build-ids when loading
# shared libraries for a core file.
#
# The test creates two "versions" of a shared library, sets up a
# symlink to point to one version of the library, and creates a core file.
#
# We then try re-loading the core file and executable and check that
# GDB is able to correctly load the shared library.  To confuse things
# we retarget the library symlink at the other version of the library.
#
# After that we repeat the test, but this time deleting the symlink
# completely.
#
# Then we remove the version of the library completely, at this point
# we do expect GDB to give a warning about being unable to load the library.
#
# And finally, we setup debuginfod and have it serve the missing
# library file, GDB should correctly download the library file.
#
# Despite this test living in the gdb.debuginfod/ directory, only the last
# part of this test actually uses debuginfod, everything up to that point is
# pretty generic.

load_lib debuginfod-support.exp

require allow_shlib_tests
require {istarget "*-linux*"}
require {!is_remote host}
require {!using_fission}

standard_testfile -1.c -2.c

# Build two similar, but slightly different versions of the shared
# library.  Both libraries have DT_SONAME set to the generic
# libfoo.so, we'll create a symlink with that name later.
set library_1_filename [standard_output_file "libfoo_1.so"]
set library_2_filename [standard_output_file "libfoo_2.so"]

# The generic name for the library.
set library_filename [standard_output_file "libfoo.so"]

# When compiling a shared library the -Wl,-soname,NAME option is
# automatically added based on the final name of the library.  We want
# to compile libfoo_1.so, but set the soname to libfoo.so.  To achieve
# this we first compile into libfoo.so, and then rename the library to
# libfoo_1.so.
if {[build_executable "build libfoo_1.so" $library_filename \
	 $srcfile \
	      { debug shlib build-id \
		    additional_flags=-DLIB_VERSION=1 }] == -1} {
    return
}
remote_exec build "mv ${library_filename} ${library_1_filename}"

# See the comment above, but this time we rename to libfoo_2.so.
if {[build_executable "build libfoo_2.so" $library_filename \
	 $srcfile \
	      { debug shlib build-id \
		    additional_flags=-DLIB_VERSION=2 }] == -1} {
    return
}
remote_exec build "mv ${library_filename} ${library_2_filename}"

# Create libfoo.so symlink to the libfoo_1.so library.  If this
# symlink creation fails then we assume we can't create symlinks on
# this host.  If this succeeds then later symlink creation is required
# to succeed, and will trigger an FAIL if it doesn't.
set status \
    [remote_exec build \
	 "ln -sf ${library_1_filename} ${library_filename}"]
if {[lindex $status 0] != 0} {
    unsupported "host does not support symbolic links"
    return
}

# Build the executable.  This links against libfoo.so, which is
# poining at libfoo_1.so.  Just to confuse things even more, this
# executable uses dlopen to load libfoo_2.so.  Weird!
if { [build_executable "build executable" ${binfile} ${srcfile2} \
	  [list debug shlib=${library_filename} shlib_load]] == -1 } {
    return
}

# If the board file is automatically splitting the debug information
# into a separate file (e.g. the cc-with-gnu-debuglink.exp board) then
# this test isn't going to work.
clean_restart
gdb_file_cmd $binfile
if {$gdb_file_cmd_debug_info ne "debug"} {
    unsupported "failed to find debug information"
    return
}
if {[regexp "${testfile}.debug" $gdb_file_cmd_msg]} {
    unsupported "debug information has been split to a separate file"
    return
}

# Run BINFILE which will generate a corefile.
set corefile [core_find $binfile]
if {$corefile eq ""} {
    untested "could not generate core file"
    return
}

# Helper proc to load global BINFILE and then load global COREFILE.
#
# If EXPECT_WARNING is true then we require a warning about being
# unable to load the shared library symbols, otherwise, EXPECT_WARNING
# is false and we require no warning.
#
# If EXPECT_DOWNLOAD is true then we require a line indicating that
# the shared library is being downloaded from debuginfod, otherwise
# the shared library should not be downloaded.
#
# If DEBUGDIR is not the empty string then 'debug-file-directory' is
# set to the value of DEBUGDIR.
proc load_exec_and_core_file { expect_warning expect_download testname \
				   {debugdir ""} } {
    with_test_prefix $testname {
	clean_restart $::binfile

	if { $debugdir ne "" } {
	    gdb_test_no_output "set debug-file-directory $debugdir" \
		"set debug directory"
	}

	set saw_warning false
	set saw_download false
	set saw_generated false
	set saw_terminated false

	gdb_test_multiple "core-file $::corefile" "load core file" {
	    -re "^Core was generated by \[^\r\n\]+\r\n" {
		set saw_generated true
		exp_continue
	    }
	    -re "^Program terminated with signal \[^\r\n\]+\r\n" {
		set saw_terminated true
		exp_continue
	    }
	    -re "^warning: Can't open file \[^\r\n\]+ during file-backed mapping note processing\r\n" {
		# Ignore warnings from the file backed mapping phase.
		exp_continue
	    }
	    -re "^warning: Could not load shared library symbols for \[^\r\n\]+/libfoo\\.so\\.\r\n" {
		set saw_warning true
		exp_continue
	    }
	    -re "^Downloading\[^\r\n\]*file \[^\r\n\]+/libfoo_1\\.so\\.\\.\\.\r\n" {
		set saw_download true
		exp_continue
	    }
	    -re "^$::gdb_prompt $" {
		gdb_assert { $saw_generated && $saw_terminated \
				 && $saw_warning == $expect_warning \
				 && $saw_download == $expect_download } \
		    $gdb_test_name
	    }
	    -re "^\[^\r\n\]*\r\n" {
		exp_continue
	    }
	}

	# If we don't expect a warning then debug symbols from the
	# shared library should be available.  Confirm we can read a
	# variable from the shared library.  If we do expect a warning
	# then the shared library debug symbols have not loaded, and
	# the library variable should not be available.
	if { !$expect_warning } {
	    gdb_test "print/x library_1_var" " = 0x12345678" \
		"check library_1_var can be read"
	} else {
	    gdb_test "print/x library_1_var" \
		"^No symbol \"library_1_var\" in current context\\." \
		"check library_1_var cannot be read"
	}
    }
}

# Initial test, just load the executable and core file.  At this point
# everything should load fine as everything is where we expect to find
# it.
load_exec_and_core_file false false \
    "load core file, all libraries as expected"

# Update libfoo.so symlink to point at the second library then reload
# the core file.  GDB should spot that the symlink points to the wrong
# file, but should be able to figure out the correct file to load as
# the right file will be in the mapped file list.
set status [remote_exec build \
		"ln -sf ${library_2_filename} ${library_filename}"]
gdb_assert { [lindex $status 0] == 0 } \
    "update library symlink to point to the wrong file"

load_exec_and_core_file false false \
    "load core file, symlink points to wrong file"

# Remove libfoo.so symlink and reload the core file.  As in the
# previous test GDB should be able to figure out the correct file to
# load as the correct file will still appear in the mapped file list.
set status [remote_exec build "rm -f ${library_filename}"]
gdb_assert { [lindex $status 0] == 0 } "remove library symlink"

load_exec_and_core_file false false \
    "load core file, symlink removed"

# Remove LIBRARY_1_FILENAME.  We'll now see a warning that the mapped
# file can't be loaded (we ignore that warning), and we'll see a
# warning that the shared library can't be loaded.
set library_1_backup_filename ${library_1_filename}.backup
set status \
    [remote_exec build \
	 "mv ${library_1_filename} ${library_1_backup_filename}"]
gdb_assert { [lindex $status 0] == 0 } \
    "remove libfoo_1.so"

load_exec_and_core_file true false \
    "load core file, libfoo_1.so removed"

# Symlink the .build-id/xx/xxx...xxx filename within the debug
# directory to LIBRARY_1_BACKUP_FILENAME, now when we restart GDB it
# should find the missing library within the debug directory.
set debugdir [standard_output_file "debugdir"]
set build_id_filename \
    $debugdir/[build_id_debug_filename_get $library_1_backup_filename ""]
set status \
    [remote_exec build \
	 "mkdir -p [file dirname $build_id_filename]"]
gdb_assert { [lindex $status 0] == 0 } \
    "create sub-directory within the debug directory"
set status \
    [remote_exec build \
	 "ln -sf $library_1_backup_filename $build_id_filename"]
gdb_assert { [lindex $status 0] == 0 } \
    "create symlink within the debug directory "

load_exec_and_core_file false false \
    "load core file, find libfoo_1.so through debug-file-directory" \
    $debugdir

# Setup a debuginfod server which can serve the original shared
# library file.
if {![allow_debuginfod_tests]} {
    untested "skippig debuginfod parts of this test"
    return
}

set server_dir [standard_output_file "debuginfod.server"]
file mkdir $server_dir
file rename -force $library_1_backup_filename $server_dir

prepare_for_debuginfod cache db

set url [start_debuginfod $db $server_dir]
if { $url eq "" } {
    unresolved "failed to start debuginfod server"
    return
}

with_debuginfod_env $cache {
    setenv DEBUGINFOD_URLS $url

    save_vars { GDBFLAGS } {
	append GDBFLAGS " -ex \"set debuginfod enabled on\""

	# Reload the executable and core file.  GDB should download
	# the file libfoo_1.so using debuginfod during the mapped file
	# phase, but should then reuse that download during the shared
	# library phase.
	load_exec_and_core_file false true \
	    "load core file, use debuginfod"
    }
}

stop_debuginfod