| # Copyright 2019-2025 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/>. |
| |
| # Based on break.exp, written by Rob Savoye. (rob@cygnus.com) |
| # Modified to test gdb's handling of separate debug info files. |
| # Modified to test gdb's handling of a debug-id retrieval. |
| |
| # Build-id-related tests for core files. |
| |
| standard_testfile .c -shlib-shr.c -shlib.c |
| |
| # Create a corefile from PROGNAME. Return the name of the generated |
| # corefile, or the empty string if anything goes wrong. |
| # |
| # The generated corefile must contain a buildid for PROGNAME. If it |
| # doesn't then an empty string will be returned. |
| proc create_core_file { progname } { |
| # Generate a corefile. |
| set corefile [core_find $progname] |
| if {$corefile == ""} { |
| untested "could not generate core file" |
| return "" |
| } |
| verbose -log "corefile is $corefile" |
| |
| # Check the corefile has a build-id for the executable. |
| if { [catch "exec [gdb_find_eu-unstrip] -n --core $corefile" output] == 0 } { |
| set line [lindex [split $output "\n"] 0] |
| set binfile_re (?:[string_to_regexp $progname]|\\\[(?:exe|pie)\\\]) |
| if { ![regexp "^${::hex}\\+${::hex} \[a-f0-9\]+@${::hex}.*$binfile_re$" $line] } { |
| unsupported "no build-id for executable in corefile" |
| return "" |
| } |
| } else { |
| unsupported "eu-unstrip tool failed" |
| return "" |
| } |
| |
| return $corefile |
| } |
| |
| |
| # Build a non-shared executable. |
| |
| proc build_corefile_buildid_exec { progname } { |
| return [expr {[build_executable "build non-shared exec" $progname $::srcfile] != -1}] |
| } |
| |
| # Build a shared executable. |
| |
| proc build_corefile_buildid_shared { progname } { |
| # Compile DSO. |
| set objdso [standard_output_file $::testfile-shlib-shr.so] |
| if {[build_executable "build dso" $objdso $::srcfile2 {debug shlib}] == -1} { |
| return false |
| } |
| |
| |
| # Compile shared library. |
| set srclib $::srcfile3 |
| set libname lib$::testfile.so |
| set objlib [standard_output_file $libname] |
| set dlopen_lib [shlib_target_file $objdso] |
| set opts [list debug shlib_load shlib \ |
| additional_flags=-DSHLIB_NAME=\"$dlopen_lib\"] |
| if {[build_executable "build solib" $objlib $::srcfile3 $opts] == -1} { |
| return false |
| } |
| |
| # Compile main program. |
| set opts [list debug shlib=$objlib additional_flags=-DTEST_SHARED] |
| if {[build_executable "build shared exec" $progname $::srcfile $opts] == -1} { |
| return false |
| } |
| |
| return true |
| } |
| |
| # Append DEBUGDIR to the debug-file-directory path. |
| |
| proc append_debug_dir {debugdir} { |
| global gdb_prompt |
| |
| set orig_debugdir {} |
| gdb_test_multiple "show debug-file-directory" \ |
| "get debug-file-directory" { |
| -re "The directory where separate debug symbols are searched for is \"(.*)\"\.\[\r\n\]+$gdb_prompt $" { |
| set orig_debugdir $expect_out(1,string) |
| pass "get debug-file-directory" |
| } |
| } |
| gdb_test_no_output "set debug-file-directory $debugdir:$orig_debugdir" \ |
| "append debug directory" |
| } |
| |
| # A convenience procedure to check if "info files" mentions the exec file |
| # FILE. |
| |
| proc check_exec_file {file} { |
| global gdb_prompt |
| send_log "expecting exec file \"$file\"\n" |
| |
| # Get line with "Local exec file:". |
| set ok 0 |
| gdb_test_multiple "info files" "" -lbl { |
| -re "\r\nLocal exec file:" { |
| set test_name $gdb_test_name |
| set ok 1 |
| } |
| } |
| |
| if { $ok == 0 } { |
| return |
| } |
| |
| # Get subsequent line with $file. |
| set ok 0 |
| gdb_test_multiple "" $test_name -lbl { |
| -re "\r\n\[\t\ \]+`[string_to_regexp $file]'\[^\r\n\]*" { |
| set ok 1 |
| } |
| } |
| |
| if { $ok == 0 } { |
| return |
| } |
| |
| # Skip till prompt. |
| gdb_test_multiple "" $test_name -lbl { |
| -re "\r\n$gdb_prompt $" { |
| pass $gdb_test_name |
| } |
| } |
| } |
| |
| # Test whether gdb can find an exec file from a core file's build-id. |
| # The executable (and separate debuginfo if SEPDEBUG is true) is |
| # copied to the .build-id directory. |
| # |
| # SUFFIX is appended to the .builid-id parent directory name to |
| # keep all tests separate. |
| # SYMLINK specifies whether build-id files should be copied or symlinked. |
| # SHARED is a boolean indicating whether we are testing the shared |
| # library core dump test case. |
| |
| proc locate_exec_from_core_build_id {corefile buildid \ |
| dirname progname \ |
| sepdebug symlink shared} { |
| clean_restart |
| |
| # Set up the build-id directory and symlink the binary there. |
| set d "debugdir" |
| if {$shared} { |
| set d "${d}_shared" |
| } else { |
| set d "${d}_not-shared" |
| } |
| if {$symlink} { |
| set d "${d}_symlink" |
| } else { |
| set d "${d}_copy" |
| } |
| if {$sepdebug} { |
| set d "${d}_stripped" |
| } else { |
| set d "${d}_not-stripped" |
| } |
| |
| set debugdir [standard_output_file $d] |
| remote_exec build \ |
| "mkdir -p [file join $debugdir [file dirname $buildid]]" |
| |
| set files_list {} |
| lappend files_list [file join $dirname [file tail $progname]] \ |
| $buildid |
| if {$sepdebug} { |
| lappend files_list [file join $dirname [file tail $progname]].debug \ |
| "$buildid.debug" |
| } |
| |
| foreach {target name} $files_list { |
| set t [file join $dirname [file tail $target]] |
| if {$symlink} { |
| remote_exec build "ln -s $t [file join $debugdir $name]" |
| } else { |
| remote_exec build "cp $t [file join $debugdir $name]" |
| } |
| } |
| |
| # Append the debugdir to the separate debug directory search path. |
| append_debug_dir $debugdir |
| |
| gdb_test "core-file $corefile" "Program terminated with .*" \ |
| "load core file" |
| if {$symlink} { |
| set expected_file [file join $dirname [file tail $progname]] |
| } else { |
| set expected_file $buildid |
| } |
| check_exec_file [file join $debugdir $expected_file] |
| } |
| |
| foreach_with_prefix mode { exec shared } { |
| # Build the executable. |
| set progname ${binfile}-$mode |
| set build_proc build_corefile_buildid_${mode} |
| if { ![$build_proc $progname] } { |
| return -1 |
| } |
| |
| # Generate a corefile. |
| set corefile [create_core_file $progname] |
| if { $corefile eq "" } { |
| return -1 |
| } |
| |
| # Get the build-id filename without ".debug" on the end. This |
| # will have the format: '.build-id/xx/xxxxx' |
| set buildid [build_id_debug_filename_get $progname ""] |
| if {$buildid == ""} { |
| untested "binary has no build-id" |
| return |
| } |
| verbose -log "build-id is $buildid" |
| |
| # Create a directory for the non-stripped test. |
| set combined_dirname [standard_output_file ${mode}_non-stripped] |
| remote_exec build "mkdir -p $combined_dirname" |
| remote_exec build "cp $progname $combined_dirname" |
| |
| # Create a directory for the stripped test. |
| if {[gdb_gnu_strip_debug [standard_output_file $progname] no-debuglink] != 0} { |
| untested "could not strip executable for [join $suffix \ ]" |
| return |
| } |
| set sepdebug_dirname [standard_output_file ${mode}_stripped] |
| remote_exec build "mkdir -p $sepdebug_dirname" |
| remote_exec build "mv $progname $sepdebug_dirname" |
| remote_exec build "mv ${progname}.debug $sepdebug_dirname" |
| |
| # Now do the actual testing part. Fill out a debug directory with |
| # build-id related files (copies or symlinks) and then load the |
| # corefile. Check GDB finds the executable and debug information |
| # via the build-id related debug directory contents. |
| foreach_with_prefix sepdebug { false true } { |
| if { $sepdebug } { |
| set dirname $sepdebug_dirname |
| } else { |
| set dirname $combined_dirname |
| } |
| |
| foreach_with_prefix symlink { false true } { |
| locate_exec_from_core_build_id $corefile $buildid \ |
| $dirname $progname \ |
| $sepdebug $symlink [expr {$mode eq "shared"}] |
| } |
| } |
| } |