1# Copyright 2019-2023 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# Based on break.exp, written by Rob Savoye. (rob@cygnus.com)
17# Modified to test gdb's handling of separate debug info files.
18# Modified to test gdb's handling of a debug-id retrieval.
19
20# Build-id-related tests for core files.
21
22standard_testfile
23
24# Build a non-shared executable.
25
26proc build_corefile_buildid_exec {} {
27    global testfile srcfile binfile execdir
28
29    if {[build_executable $testfile.exp $testfile $srcfile debug] == -1} {
30	untested "failed to compile"
31	return false
32    }
33
34    # Move executable to non-default path.
35    set builddir [standard_output_file $execdir]
36    remote_exec build "rm -rf $builddir"
37    remote_exec build "mkdir $builddir"
38    remote_exec build "mv $binfile [file join $builddir [file tail $binfile]]"
39
40    return true
41}
42
43# Build a shared executable.
44
45proc build_corefile_buildid_shared {} {
46    global srcdir subdir testfile binfile srcfile sharedir
47
48    set builddir [standard_output_file $sharedir]
49
50    # Compile DSO.
51    set srcdso [file join $srcdir $subdir $testfile-shlib-shr.c]
52    set objdso [standard_output_file $testfile-shlib-shr.so]
53    if {[gdb_compile_shlib $srcdso $objdso {debug}] != ""} {
54	untested "failed to compile dso"
55	return false
56    }
57
58    # Compile shared library.
59    set srclib [file join $srcdir $subdir $testfile-shlib.c]
60    set libname lib$testfile.so
61    set objlib [standard_output_file $libname]
62    set dlopen_lib [shlib_target_file \
63			[file join $builddir [file tail $objdso]]]
64    set opts [list debug shlib_load \
65		  additional_flags=-DSHLIB_NAME=\"$dlopen_lib\"]
66    if {[gdb_compile_shlib $srclib $objlib $opts] != ""} {
67	untested "failed to compile shared library"
68	return false
69    }
70
71    # Compile main program.
72    set srcexec [file join $srcdir $subdir $srcfile]
73    set binfile [standard_output_file $testfile-shared]
74    set opts [list debug shlib=$objlib additional_flags=-DTEST_SHARED]
75    if {[gdb_compile $srcexec $binfile executable $opts] != ""} {
76	untested "failed to compile shared executable"
77	return false
78    }
79
80    # Move objects to non-default path.
81    remote_exec build "rm -rf $builddir"
82    remote_exec build "mkdir $builddir"
83    remote_exec build "mv $binfile $builddir"
84    remote_exec build "mv $objdso  $builddir"
85    remote_exec build "mv $objlib $builddir"
86
87    return true
88}
89
90# Append DEBUGDIR to the debug-file-directory path.
91
92proc append_debug_dir {debugdir} {
93    global gdb_prompt
94
95    set orig_debugdir {}
96    gdb_test_multiple "show debug-file-directory" \
97	"get debug-file-directory" {
98	    -re "The directory where separate debug symbols are searched for is \"(.*)\"\.\[\r\n\]+$gdb_prompt $" {
99		set orig_debugdir $expect_out(1,string)
100		pass "get debug-file-directory"
101	    }
102	}
103    gdb_test_no_output "set debug-file-directory $debugdir:$orig_debugdir" \
104	"append debug directory"
105}
106
107# A convenience procedure to check if "info files" mentions the exec file
108# FILE.
109
110proc check_exec_file {file} {
111    global gdb_prompt
112    send_log "expecting exec file \"$file\"\n"
113
114    # Get line with "Local exec file:".
115    set ok 0
116    gdb_test_multiple "info files" "" -lbl {
117	-re "\r\nLocal exec file:" {
118	    set test_name $gdb_test_name
119	    set ok 1
120	}
121    }
122
123    if { $ok == 0 } {
124	return
125    }
126
127    # Get subsequent line with $file.
128    set ok 0
129    gdb_test_multiple "" $test_name -lbl {
130	-re "\r\n\[\t\ \]+`[string_to_regexp $file]'\[^\r\n\]*" {
131	    set ok 1
132	}
133    }
134
135    if { $ok == 0 } {
136	return
137    }
138
139    # Skip till prompt.
140    gdb_test_multiple "" $test_name -lbl {
141	-re "\r\n$gdb_prompt $" {
142	    pass $gdb_test_name
143	}
144    }
145}
146
147# Test whether gdb can find an exec file from a core file's build-id.
148# The executable (and separate debuginfo if SEPDEBUG is true) is
149# copied to the .build-id directory.
150#
151# SUFFIX is appended to the .builid-id parent directory name to
152# keep all tests separate.
153# SYMLINK specifies whether build-id files should be copied or symlinked.
154# SHARED is a boolean indicating whether we are testing the shared
155# library core dump test case.
156
157proc locate_exec_from_core_build_id {corefile buildid suffix \
158					 sepdebug symlink shared} {
159    global testfile binfile srcfile
160
161    clean_restart
162
163    # Set up the build-id directory and symlink the binary there.
164    if {$symlink} {
165	set d "symlinkdir"
166    } else {
167	set d "debugdir"
168    }
169    set debugdir [standard_output_file $d-$suffix]
170    remote_exec build "rm -rf $debugdir"
171    remote_exec build \
172	"mkdir -p [file join $debugdir [file dirname $buildid]]"
173
174    set files_list {}
175    if {$sepdebug} {
176	lappend files_list "$binfile.stripped" $buildid
177	lappend files_list "$binfile.debug" "$buildid.debug"
178    } else {
179	lappend files_list $binfile $buildid
180    }
181    if {$shared} {
182	global sharedir
183	set builddir [standard_output_file $sharedir]
184    } else {
185	global execdir
186	set builddir [standard_output_file $execdir]
187    }
188    foreach {target name} $files_list {
189	set t [file join $builddir [file tail $target]]
190	if {$symlink} {
191	    remote_exec build "ln -s $t [file join $debugdir $name]"
192	} else {
193	    remote_exec build "cp $t [file join $debugdir $name]"
194	}
195    }
196
197    # Append the debugdir to the separate debug directory search path.
198    append_debug_dir $debugdir
199
200    gdb_test "core-file $corefile" "Program terminated with .*" \
201	"load core file"
202    if {$symlink} {
203	if {$sepdebug} {
204	    set expected_file [file join $builddir \
205				   [file tail "$binfile.stripped"]]
206	} else {
207	    set expected_file [file join $builddir [file tail $binfile]]
208	}
209    } else {
210	set expected_file $buildid
211    }
212    check_exec_file [file join $debugdir $expected_file]
213}
214
215# Run a build-id tests on a core file.
216# Supported options: "-shared" and "-sepdebug" for running tests
217# of shared and/or stripped/.debug executables.
218
219proc do_corefile_buildid_tests {args} {
220    global binfile testfile srcfile execdir sharedir
221
222    # Parse options.
223    parse_args [list {sepdebug} {shared}]
224
225    # PROGRAM to run to generate core file.  This could be different
226    # than the program that was originally built, e.g., for a stripped
227    # executable.
228    if {$shared} {
229	set builddir [standard_output_file $sharedir]
230    } else {
231	set builddir [standard_output_file $execdir]
232    }
233    set program_to_run [file join $builddir [file tail $binfile]]
234
235    # A list of suffixes to use to describe the test and the .build-id
236    # directory for the test.  The suffix will be used, joined with spaces,
237    # to prefix all tests for the given run.  It will be used, joined with
238    # dashes, to create a unique build-id directory.
239    set suffix {}
240    if {$shared} {
241	lappend suffix "shared"
242    } else {
243	lappend suffix "exec"
244    }
245
246    if {$sepdebug} {
247	# Strip debuginfo into its own file.
248	if {[gdb_gnu_strip_debug [standard_output_file $program_to_run]] \
249		!= 0} {
250	    untested "could not strip executable  for [join $suffix \ ]"
251	    return
252	}
253
254	# Run the stripped program instead of the original.
255	set program_to_run [file join $builddir \
256				[file tail "$binfile.stripped"]]
257	lappend suffix "sepdebug"
258    }
259
260    with_test_prefix "[join $suffix \ ]" {
261	# Find the core file.
262	set corefile [core_find $program_to_run]
263	if {$corefile == ""} {
264	    untested "could not generate core file"
265	    return
266	}
267	verbose -log "corefile is $corefile"
268
269	# Grab the build-id from the binary, removing ".debug" from the end.
270	set buildid [build_id_debug_filename_get $program_to_run]
271	if {$buildid == ""} {
272	    untested "binary has no build-id"
273	}
274	regsub {\.debug$} $buildid {} buildid
275	verbose -log "build-id is $buildid"
276
277	locate_exec_from_core_build_id $corefile $buildid \
278	    [join $suffix -] $sepdebug false $shared
279
280	with_test_prefix "symlink" {
281	    locate_exec_from_core_build_id $corefile $buildid \
282		[join $suffix -] $sepdebug true $shared
283	}
284    }
285}
286
287# Directories where executables will be moved before testing.
288set execdir "build-exec"
289set sharedir "build-shared"
290
291#
292# Do tests
293#
294
295build_corefile_buildid_exec
296do_corefile_buildid_tests
297do_corefile_buildid_tests -sepdebug
298
299if {![skip_shlib_tests]} {
300    build_corefile_buildid_shared
301    do_corefile_buildid_tests -shared
302    do_corefile_buildid_tests -shared -sepdebug
303}
304