From: Keith Seitz Date: Sat, 7 Dec 2019 17:35:03 +0000 (-0800) Subject: Core file build-id support X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=aa2d5a422;p=binutils-gdb.git Core file build-id support This patch uses new BFD support for detecting build-ids in core files. After this patch, it is possible to run gdb with only the core file, and gdb will automatically load the executable and debug info [example from tests]: $ gdb -nx -q (gdb) core-file corefile-buildid.core [New LWP 29471] Reading symbols from gdb.base/corefile-buildid/debugdir-exec/.build-id/36/fe5722c5a7ca3ac746a84e223c6a2a69193a24... Core was generated by `outputs/gdb.base/coref'. Program terminated with signal SIGABRT, Aborted. (gdb) This work is based on functionality available in Fedora originally written by Jan Kratochvil. Regression tested on buildbot. gdb/ChangeLog: 2019-12-07 Keith Seitz * build-id.c (build_id_bfd_get): Permit bfd_core, too. (build_id_to_debug_bfd): Make static, rewriting to use build_id_to_bfd_suffix. (build_id_to_bfd_suffix): Copy of build_id_to_debug_bfd, adding `suffix' parameter. Append SUFFIX to file names when searching for matching files. (build_id_to_debug_bfd): Use build_id_to_bfd_suffix. (build_id_to_exec_bfd): Likewise. * build-id.h (build_id_to_debug_bfd): Clarify that function searches for BFD of debug info file. (build_id_to_exec_bfd): Declare. * corelow.c: Include build-id.h. (locate_exec_from_corefile_build_id): New function. (core_target_open): If no executable BFD is found, search for a core file BFD using build-id. gdb/testsuite/ChangeLog: 2019-12-07 Keith Seitz * gdb.base/corefile-buildid-shlib-shr.c: New file. * gdb.base/corefile-buildid-shlib.c: New file. * gdb.base/corefile-buildid.c: New file. * gdb.base/corefile-buildid.exp: New file. Change-Id: I15e9e8e58f10c68b5cae55e2eba58df1e8aef529 --- diff --git a/gdb/ChangeLog b/gdb/ChangeLog index f06ad13870a..ee45156e391 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,21 @@ +2019-12-07 Keith Seitz + + * build-id.c (build_id_bfd_get): Permit bfd_core, too. + (build_id_to_debug_bfd): Make static, rewriting to use + build_id_to_bfd_suffix. + (build_id_to_bfd_suffix): Copy of build_id_to_debug_bfd, + adding `suffix' parameter. Append SUFFIX to file names + when searching for matching files. + (build_id_to_debug_bfd): Use build_id_to_bfd_suffix. + (build_id_to_exec_bfd): Likewise. + * build-id.h (build_id_to_debug_bfd): Clarify that function + searches for BFD of debug info file. + (build_id_to_exec_bfd): Declare. + * corelow.c: Include build-id.h. + (locate_exec_from_corefile_build_id): New function. + (core_target_open): If no executable BFD is found, + search for a core file BFD using build-id. + 2019-12-06 Christian Biesinger * bcache.c: Put in namespace gdb. diff --git a/gdb/build-id.c b/gdb/build-id.c index 048da2af86f..e8d77bb44c8 100644 --- a/gdb/build-id.c +++ b/gdb/build-id.c @@ -32,7 +32,8 @@ const struct bfd_build_id * build_id_bfd_get (bfd *abfd) { - if (!bfd_check_format (abfd, bfd_object)) + if (!bfd_check_format (abfd, bfd_object) + && !bfd_check_format (abfd, bfd_core)) return NULL; if (abfd->build_id != NULL) @@ -117,10 +118,13 @@ build_id_to_debug_bfd_1 (const std::string &link, size_t build_id_len, return debug_bfd; } -/* See build-id.h. */ +/* Common code for finding BFDs of a given build-id. This function + works with both debuginfo files (SUFFIX == ".debug") and executable + files (SUFFIX == ""). */ -gdb_bfd_ref_ptr -build_id_to_debug_bfd (size_t build_id_len, const bfd_byte *build_id) +static gdb_bfd_ref_ptr +build_id_to_bfd_suffix (size_t build_id_len, const bfd_byte *build_id, + const char *suffix) { /* Keep backward compatibility so that DEBUG_FILE_DIRECTORY being "" will cause "/.build-id/..." lookups. */ @@ -149,7 +153,7 @@ build_id_to_debug_bfd (size_t build_id_len, const bfd_byte *build_id) while (size-- > 0) string_appendf (link, "%02x", (unsigned) *data++); - link += ".debug"; + link += suffix; gdb_bfd_ref_ptr debug_bfd = build_id_to_debug_bfd_1 (link, build_id_len, build_id); @@ -177,6 +181,22 @@ build_id_to_debug_bfd (size_t build_id_len, const bfd_byte *build_id) /* See build-id.h. */ +gdb_bfd_ref_ptr +build_id_to_debug_bfd (size_t build_id_len, const bfd_byte *build_id) +{ + return build_id_to_bfd_suffix (build_id_len, build_id, ".debug"); +} + +/* See build-id.h. */ + +gdb_bfd_ref_ptr +build_id_to_exec_bfd (size_t build_id_len, const bfd_byte *build_id) +{ + return build_id_to_bfd_suffix (build_id_len, build_id, ""); +} + +/* See build-id.h. */ + std::string find_separate_debug_file_by_buildid (struct objfile *objfile) { diff --git a/gdb/build-id.h b/gdb/build-id.h index 2835a760fae..d251bc1e5d4 100644 --- a/gdb/build-id.h +++ b/gdb/build-id.h @@ -34,13 +34,19 @@ extern int build_id_verify (bfd *abfd, size_t check_len, const bfd_byte *check); -/* Find and open a BFD given a build-id. If no BFD can be found, - return NULL. The returned reference to the BFD must be released by - the caller. */ +/* Find and open a BFD for a debuginfo file given a build-id. If no BFD + can be found, return NULL. */ extern gdb_bfd_ref_ptr build_id_to_debug_bfd (size_t build_id_len, const bfd_byte *build_id); +/* Find and open a BFD for an executable file given a build-id. If no BFD + can be found, return NULL. The returned reference to the BFD must be + released by the caller. */ + +extern gdb_bfd_ref_ptr build_id_to_exec_bfd (size_t build_id_len, + const bfd_byte *build_id); + /* Find the separate debug file for OBJFILE, by using the build-id associated with OBJFILE's BFD. If successful, returns the file name for the separate debug file, otherwise, return an empty string. */ diff --git a/gdb/corelow.c b/gdb/corelow.c index fa1661ee61d..bdbfae372d7 100644 --- a/gdb/corelow.c +++ b/gdb/corelow.c @@ -43,6 +43,7 @@ #include "gdb_bfd.h" #include "completer.h" #include "gdbsupport/filestuff.h" +#include "build-id.h" #ifndef O_LARGEFILE #define O_LARGEFILE 0 @@ -351,6 +352,27 @@ core_file_command (const char *filename, int from_tty) core_target_open (filename, from_tty); } +/* Locate (and load) an executable file (and symbols) given the core file + BFD ABFD. */ + +static void +locate_exec_from_corefile_build_id (bfd *abfd, int from_tty) +{ + const bfd_build_id *build_id = build_id_bfd_get (abfd); + if (build_id == nullptr) + return; + + gdb_bfd_ref_ptr execbfd + = build_id_to_exec_bfd (build_id->size, build_id->data); + + if (execbfd != nullptr) + { + exec_file_attach (bfd_get_filename (execbfd.get ()), from_tty); + symbol_file_add_main (bfd_get_filename (execbfd.get ()), + symfile_add_flag (from_tty ? SYMFILE_VERBOSE : 0)); + } +} + /* See gdbcore.h. */ void @@ -456,6 +478,9 @@ core_target_open (const char *arg, int from_tty) switch_to_thread (thread); } + if (exec_bfd == nullptr) + locate_exec_from_corefile_build_id (core_bfd, from_tty); + post_create_inferior (target, from_tty); /* Now go through the target stack looking for threads since there diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index c9f66ad40aa..17ae51a37a9 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,10 @@ +2019-12-07 Keith Seitz + + * gdb.base/corefile-buildid-shlib-shr.c: New file. + * gdb.base/corefile-buildid-shlib.c: New file. + * gdb.base/corefile-buildid.c: New file. + * gdb.base/corefile-buildid.exp: New file. + 2019-12-06 Tom de Vries * gdb.dwarf2/varval.exp: Add decl before def test. diff --git a/gdb/testsuite/gdb.base/corefile-buildid-shlib-shr.c b/gdb/testsuite/gdb.base/corefile-buildid-shlib-shr.c new file mode 100644 index 00000000000..55b9bfae6d9 --- /dev/null +++ b/gdb/testsuite/gdb.base/corefile-buildid-shlib-shr.c @@ -0,0 +1,29 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2019 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 . */ + +#include +#include + +static int crashfunc_global = 1234; + +int +crashfunc (void) +{ + printf ("in crashfunc\n"); + abort (); + return crashfunc_global; +} diff --git a/gdb/testsuite/gdb.base/corefile-buildid-shlib.c b/gdb/testsuite/gdb.base/corefile-buildid-shlib.c new file mode 100644 index 00000000000..10c523d701a --- /dev/null +++ b/gdb/testsuite/gdb.base/corefile-buildid-shlib.c @@ -0,0 +1,58 @@ +/* Copyright (C) 2007-2019 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 . */ + +/* This shared library will dlopen another shared object. + This is based on gdb.base/solib-disc.c. */ + +#include +#include + +#ifdef __WIN32__ +#include +#define dlopen(name, mode) LoadLibrary (name) +#define dlsym(handle, func) GetProcAddress (handle, func) +#define dlclose(handle) FreeLibrary (handle) +#define dlerror() "an error occurred" +#else +#include +#endif + +const char *the_shlib = SHLIB_NAME; + +int +shlib_function (void) +{ + void *handle; + int (*func) (void); + int result; + + handle = dlopen (the_shlib, RTLD_LAZY); + if (!handle) + { + fprintf (stderr, "%s\n", dlerror ()); + exit (1); + } + + func = (int (*)(void)) dlsym (handle, "crashfunc"); + if (func == NULL) + { + fprintf (stderr, "%s\n", dlerror ()); + exit (1); + } + + result = func (); + dlclose (handle); + return result; +} diff --git a/gdb/testsuite/gdb.base/corefile-buildid.c b/gdb/testsuite/gdb.base/corefile-buildid.c new file mode 100644 index 00000000000..ae0a1cabdc2 --- /dev/null +++ b/gdb/testsuite/gdb.base/corefile-buildid.c @@ -0,0 +1,43 @@ +/* Copyright (C) 2019 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 . */ + +#ifdef TEST_SHARED +/* A function in the shared library linked with this program. */ +extern int shlib_function (void); +#else +#include + +static int crashfunc_global = 4321; + +static int +crashfunc (void) +{ + abort (); + return crashfunc_global; +} + +int +shlib_function (void) +{ + return crashfunc (); +} +#endif + +int +main (void) +{ + int ret = shlib_function (); + return ret; +} diff --git a/gdb/testsuite/gdb.base/corefile-buildid.exp b/gdb/testsuite/gdb.base/corefile-buildid.exp new file mode 100644 index 00000000000..8a29359854d --- /dev/null +++ b/gdb/testsuite/gdb.base/corefile-buildid.exp @@ -0,0 +1,271 @@ +# Copyright 2019 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 . + +# 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 + +# Build a non-shared executable. + +proc build_corefile_buildid_exec {} { + global testfile srcfile binfile execdir + + if {[build_executable $testfile.exp $testfile $srcfile debug] == -1} { + untested "failed to compile" + return false + } + + # Move executable to non-default path. + set builddir [standard_output_file $execdir] + remote_exec build "rm -rf $builddir" + remote_exec build "mkdir $builddir" + remote_exec build "mv $binfile [file join $builddir [file tail $binfile]]" + + return true +} + +# Build a shared executable. + +proc build_corefile_buildid_shared {} { + global srcdir subdir testfile binfile srcfile sharedir + + set builddir [standard_output_file $sharedir] + + # Compile DSO. + set srcdso [file join $srcdir $subdir $testfile-shlib-shr.c] + set objdso [standard_output_file $testfile-shlib-shr.so] + if {[gdb_compile_shlib $srcdso $objdso {debug}] != ""} { + untested "failed to compile dso" + return false + } + + # Compile shared library. + set srclib [file join $srcdir $subdir $testfile-shlib.c] + set libname lib$testfile.so + set objlib [standard_output_file $libname] + set dlopen_lib [shlib_target_file \ + [file join $builddir [file tail $objdso]]] + set opts [list debug shlib_load \ + additional_flags=-DSHLIB_NAME=\"$dlopen_lib\"] + if {[gdb_compile_shlib $srclib $objlib $opts] != ""} { + untested "failed to compile shared library" + return false + } + + # Compile main program. + set srcexec [file join $srcdir $subdir $srcfile] + set binfile [standard_output_file $testfile-shared] + set opts [list debug shlib=$objlib additional_flags=-DTEST_SHARED] + if {[gdb_compile $srcexec $binfile executable $opts] != ""} { + untested "failed to compile shared executable" + return false + } + + # Move objects to non-default path. + remote_exec build "rm -rf $builddir" + remote_exec build "mkdir $builddir" + remote_exec build "mv $binfile $builddir" + remote_exec build "mv $objdso $builddir" + remote_exec build "mv $objlib $builddir" + + 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} { + send_log "expecting exec file \"$file\"\n" + gdb_test "info files" "Local exec file:\[\r\n\t\ \]+`[string_to_regexp $file]'.*" +} + +# 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 suffix \ + sepdebug symlink shared} { + global testfile binfile srcfile + + clean_restart + + # Set up the build-id directory and symlink the binary there. + if {$symlink} { + set d "symlinkdir" + } else { + set d "debugdir" + } + set debugdir [standard_output_file $d-$suffix] + remote_exec build "rm -rf $debugdir" + remote_exec build \ + "mkdir -p [file join $debugdir [file dirname $buildid]]" + + set files_list {} + if {$sepdebug} { + lappend files_list "$binfile.stripped" $buildid + lappend files_list "$binfile.debug" "$buildid.debug" + } else { + lappend files_list $binfile $buildid + } + if {$shared} { + global sharedir + set builddir [standard_output_file $sharedir] + } else { + global execdir + set builddir [standard_output_file $execdir] + } + foreach {target name} $files_list { + set t [file join $builddir [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} { + if {$sepdebug} { + set expected_file [file join $builddir \ + [file tail "$binfile.stripped"]] + } else { + set expected_file [file join $builddir [file tail $binfile]] + } + } else { + set expected_file $buildid + } + check_exec_file [file join $debugdir $expected_file] +} + +# Run a build-id tests on a core file. +# Supported options: "-shared" and "-sepdebug" for running tests +# of shared and/or stripped/.debug executables. + +proc do_corefile_buildid_tests {args} { + global binfile testfile srcfile execdir sharedir + + # Parse options. + parse_args [list {sepdebug} {shared}] + + # PROGRAM to run to generate core file. This could be different + # than the program that was originally built, e.g., for a stripped + # executable. + if {$shared} { + set builddir [standard_output_file $sharedir] + } else { + set builddir [standard_output_file $execdir] + } + set program_to_run [file join $builddir [file tail $binfile]] + + # A list of suffixes to use to describe the test and the .build-id + # directory for the test. The suffix will be used, joined with spaces, + # to prefix all tests for the given run. It will be used, joined with + # dashes, to create a unique build-id directory. + set suffix {} + if {$shared} { + lappend suffix "shared" + } else { + lappend suffix "exec" + } + + if {$sepdebug} { + # Strip debuginfo into its own file. + if {[gdb_gnu_strip_debug [standard_output_file $program_to_run]] \ + != 0} { + untested "could not strip executable for [join $suffix \ ]" + return + } + + # Run the stripped program instead of the original. + set program_to_run [file join $builddir \ + [file tail "$binfile.stripped"]] + lappend suffix "sepdebug" + } + + # Find the core file. + set corefile [core_find $program_to_run] + if {$corefile == ""} { + untested "could not generate core file" + return + } + verbose -log "corefile is $corefile" + + # Grab the build-id from the binary, removing ".debug" from the end. + set buildid [build_id_debug_filename_get $program_to_run] + if {$buildid == ""} { + untested "binary for [join $suffix \ ] has no build-id" + } + regsub {\.debug$} $buildid {} buildid + verbose -log "build-id is $buildid" + + with_test_prefix "[join $suffix \ ]" { + locate_exec_from_core_build_id $corefile $buildid \ + [join $suffix -] $sepdebug false $shared + } + + with_test_prefix "symlink [join $suffix \ ]" { + locate_exec_from_core_build_id $corefile $buildid \ + [join $suffix -] $sepdebug true $shared + } +} + +# Directories where executables will be moved before testing. +set execdir "build-exec" +set sharedir "build-shared" + +# +# Do tests +# + +build_corefile_buildid_exec +do_corefile_buildid_tests +do_corefile_buildid_tests -sepdebug + +if {![skip_shlib_tests]} { + build_corefile_buildid_shared + do_corefile_buildid_tests -shared + do_corefile_buildid_tests -shared -sepdebug +}