diff options
-rw-r--r-- | gdb/ChangeLog | 21 | ||||
-rw-r--r-- | gdb/Makefile.in | 4 | ||||
-rw-r--r-- | gdb/NEWS | 14 | ||||
-rw-r--r-- | gdb/README | 9 | ||||
-rw-r--r-- | gdb/config.in | 3 | ||||
-rwxr-xr-x | gdb/configure | 182 | ||||
-rw-r--r-- | gdb/configure.ac | 6 | ||||
-rw-r--r-- | gdb/debuginfod-support.c | 155 | ||||
-rw-r--r-- | gdb/debuginfod-support.h | 62 | ||||
-rw-r--r-- | gdb/doc/gdb.texinfo | 8 | ||||
-rw-r--r-- | gdb/dwarf2/read.c | 24 | ||||
-rw-r--r-- | gdb/elfread.c | 32 | ||||
-rw-r--r-- | gdb/source.c | 30 | ||||
-rw-r--r-- | gdb/testsuite/ChangeLog | 6 | ||||
-rw-r--r-- | gdb/testsuite/gdb.debuginfod/fetch_src_and_symbols.exp | 214 | ||||
-rw-r--r-- | gdb/testsuite/gdb.debuginfod/main.c | 25 | ||||
-rw-r--r-- | gdb/top.c | 10 |
17 files changed, 755 insertions, 50 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 385cd010af9..9f2387d5f50 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,24 @@ +2020-02-26 Aaron Merey <amerey@redhat.com> + + * Makefile.in: Handle optional debuginfod support. + * NEWS: Update. + * README: Add --with-debuginfod summary. + * config.in: Regenerate. + * configure: Regenerate. + * configure.ac: Handle optional debuginfod support. + * debuginfod-support.c: debuginfod helper functions. + * debuginfod-support.h: Ditto. + * doc/gdb.texinfo: Add --with-debuginfod to configure options + summary. + * dwarf2/read.c (dwarf2_get_dwz_file): Query debuginfod servers + when a dwz file cannot be found. + * elfread.c (elf_symfile_read): Query debuginfod servers when a + debuginfo file cannot be found. + * source.c (open_source_file): Query debuginfod servers when a + source file cannot be found. + * top.c (print_gdb_configuration): Include + --{with,without}-debuginfod in the output. + 2020-02-26 Jérémie Galarneau <jeremie.galarneau@efficios.com> * thread.c (thr_try_catch_cmd): Print thread name. diff --git a/gdb/Makefile.in b/gdb/Makefile.in index f9606b8fc76..7c0a0aefbc2 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -617,7 +617,8 @@ CLIBS = $(SIM) $(READLINE) $(OPCODES) $(BFD) $(LIBCTF) $(ZLIB) \ @LIBS@ @GUILE_LIBS@ @PYTHON_LIBS@ \ $(LIBEXPAT) $(LIBLZMA) $(LIBBABELTRACE) $(LIBIPT) \ $(WIN32LIBS) $(LIBGNU) $(LIBICONV) \ - $(LIBMPFR) $(SRCHIGH_LIBS) $(LIBXXHASH) $(PTHREAD_LIBS) + $(LIBMPFR) $(SRCHIGH_LIBS) $(LIBXXHASH) $(PTHREAD_LIBS) \ + @LIBDEBUGINFOD@ CDEPS = $(NAT_CDEPS) $(SIM) $(BFD) $(READLINE_DEPS) $(LIBCTF) \ $(OPCODES) $(INTL_DEPS) $(LIBIBERTY) $(CONFIG_DEPS) $(LIBGNU) \ $(LIBSUPPORT) @@ -991,6 +992,7 @@ COMMON_SFILES = \ dbxread.c \ dcache.c \ debug.c \ + debuginfod-support.c \ dictionary.c \ disasm.c \ disasm-selftests.c \ @@ -3,6 +3,20 @@ *** Changes since GDB 9 +* GDB now supports debuginfod, an HTTP server for distributing ELF/DWARF + debugging information as well as source code. + + When built with debuginfod, GDB can automatically query debuginfod + servers for the separate debug files and source code of the executable + being debugged. + + To build GDB with debuginfod, pass --with-debuginfod to configure (this + requires libdebuginfod, the debuginfod client library). + + debuginfod is distributed with elfutils, starting with version 0.178. + + You can get the latest version from https://sourceware.org/elfutils. + * New features in the GDB remote stub, GDBserver ** GDBserver is now supported on RISC-V GNU/Linux. diff --git a/gdb/README b/gdb/README index 3895758ece5..0ec1605ce5a 100644 --- a/gdb/README +++ b/gdb/README @@ -432,6 +432,15 @@ more obscure GDB `configure' options are not listed here. Use the curses library instead of the termcap library, for text-mode terminal operations. +`--with-debuginfod' + Build GDB with libdebuginfod, the debuginfod client library. Used + to automatically fetch source files and separate debug files from + debuginfod servers using the associated executable's build ID. + Enabled by default if libdebuginfod is installed and found at + configure time. debuginfod is packaged with elfutils, starting + with version 0.178. You can get the latest version from + 'https://sourceware.org/elfutils/'. + `--with-libunwind-ia64' Use the libunwind library for unwinding function call stack on ia64 target platforms. diff --git a/gdb/config.in b/gdb/config.in index 9c5c1ce834d..7a34b85e575 100644 --- a/gdb/config.in +++ b/gdb/config.in @@ -227,6 +227,9 @@ /* Define if you have the babeltrace library. */ #undef HAVE_LIBBABELTRACE +/* Define to 1 if debuginfod is enabled. */ +#undef HAVE_LIBDEBUGINFOD + /* Define if you have the expat library. */ #undef HAVE_LIBEXPAT diff --git a/gdb/configure b/gdb/configure index e6b5a510f6c..f99cbe40f11 100755 --- a/gdb/configure +++ b/gdb/configure @@ -758,6 +758,7 @@ REPORT_BUGS_TEXI REPORT_BUGS_TO PKGVERSION CODESIGN_CERT +LIBDEBUGINFOD HAVE_NATIVE_GCORE_TARGET TARGET_OBS subdirs @@ -869,6 +870,7 @@ enable_64_bit_bfd enable_gdbmi enable_tui enable_gdbtk +with_debuginfod with_libunwind_ia64 with_curses enable_profiling @@ -1602,6 +1604,8 @@ Optional Packages: [--with-auto-load-dir] --without-auto-load-safe-path do not restrict auto-loaded files locations + --with-debuginfod Enable debuginfo lookups with debuginfod + (auto/yes/no) --with-libunwind-ia64 use libunwind frame unwinding for ia64 targets --with-curses use the curses library instead of the termcap library @@ -2264,6 +2268,52 @@ rm -f conftest.val } # ac_fn_c_compute_int +# ac_fn_c_check_decl LINENO SYMBOL VAR INCLUDES +# --------------------------------------------- +# Tests whether SYMBOL is declared in INCLUDES, setting cache variable VAR +# accordingly. +ac_fn_c_check_decl () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + as_decl_name=`echo $2|sed 's/ *(.*//'` + as_decl_use=`echo $2|sed -e 's/(/((/' -e 's/)/) 0&/' -e 's/,/) 0& (/g'` + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $as_decl_name is declared" >&5 +$as_echo_n "checking whether $as_decl_name is declared... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +#ifndef $as_decl_name +#ifdef __cplusplus + (void) $as_decl_use; +#else + (void) $as_decl_name; +#endif +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_decl + # ac_fn_c_check_func LINENO FUNC VAR # ---------------------------------- # Tests whether FUNC exists, setting the cache variable VAR accordingly @@ -2385,52 +2435,6 @@ $as_echo "$ac_res" >&6; } } # ac_fn_c_check_type -# ac_fn_c_check_decl LINENO SYMBOL VAR INCLUDES -# --------------------------------------------- -# Tests whether SYMBOL is declared in INCLUDES, setting cache variable VAR -# accordingly. -ac_fn_c_check_decl () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - as_decl_name=`echo $2|sed 's/ *(.*//'` - as_decl_use=`echo $2|sed -e 's/(/((/' -e 's/)/) 0&/' -e 's/,/) 0& (/g'` - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $as_decl_name is declared" >&5 -$as_echo_n "checking whether $as_decl_name is declared... " >&6; } -if eval \${$3+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$4 -int -main () -{ -#ifndef $as_decl_name -#ifdef __cplusplus - (void) $as_decl_use; -#else - (void) $as_decl_name; -#endif -#endif - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - eval "$3=yes" -else - eval "$3=no" -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -eval ac_res=\$$3 - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - -} # ac_fn_c_check_decl - # ac_fn_c_check_member LINENO AGGR MEMBER VAR INCLUDES # ---------------------------------------------------- # Tries to find if the field MEMBER exists in type AGGR, after including @@ -6836,8 +6840,92 @@ $as_echo "$as_me: WARNING: gdbtk isn't supported on $host; disabling" >&2;} enable_gdbtk=no ;; esac -# Libunwind support for ia64. +# Handle optional debuginfod support + +# Enable debuginfod + +# Check whether --with-debuginfod was given. +if test "${with_debuginfod+set}" = set; then : + withval=$with_debuginfod; +else + with_debuginfod=auto +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to use debuginfod" >&5 +$as_echo_n "checking whether to use debuginfod... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_debuginfod" >&5 +$as_echo "$with_debuginfod" >&6; } +if test "${with_debuginfod}" = no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: debuginfod support disabled; some features may be unavailable." >&5 +$as_echo "$as_me: WARNING: debuginfod support disabled; some features may be unavailable." >&2;} +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for debuginfod_begin in -ldebuginfod" >&5 +$as_echo_n "checking for debuginfod_begin in -ldebuginfod... " >&6; } +if ${ac_cv_lib_debuginfod_debuginfod_begin+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldebuginfod $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char debuginfod_begin (); +int +main () +{ +return debuginfod_begin (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_debuginfod_debuginfod_begin=yes +else + ac_cv_lib_debuginfod_debuginfod_begin=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_debuginfod_debuginfod_begin" >&5 +$as_echo "$ac_cv_lib_debuginfod_debuginfod_begin" >&6; } +if test "x$ac_cv_lib_debuginfod_debuginfod_begin" = xyes; then : + have_debuginfod_lib=yes +fi + + ac_fn_c_check_decl "$LINENO" "debuginfod_begin" "ac_cv_have_decl_debuginfod_begin" "#include <elfutils/debuginfod.h> +" +if test "x$ac_cv_have_decl_debuginfod_begin" = xyes; then : + have_debuginfod_h=yes +fi + + if test "x$have_debuginfod_lib" = "xyes" -a \ + "x$have_debuginfod_h" = "xyes"; then + +$as_echo "#define HAVE_LIBDEBUGINFOD 1" >>confdefs.h + + LIBDEBUGINFOD="-ldebuginfod" + + else + + if test "$with_debuginfod" = yes; then + as_fn_error $? "debuginfod is missing or unusable" "$LINENO" 5 + else + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: debuginfod is missing or unusable; some features may be unavailable." >&5 +$as_echo "$as_me: WARNING: debuginfod is missing or unusable; some features may be unavailable." >&2;} + fi + fi +fi + + +# Libunwind support for ia64. # Check whether --with-libunwind-ia64 was given. if test "${with_libunwind_ia64+set}" = set; then : diff --git a/gdb/configure.ac b/gdb/configure.ac index a51c5eda6b5..1cba1e832bb 100644 --- a/gdb/configure.ac +++ b/gdb/configure.ac @@ -18,6 +18,8 @@ dnl along with this program. If not, see <http://www.gnu.org/licenses/>. dnl Process this file with autoconf to produce a configure script. +m4_include(../config/debuginfod.m4) + AC_INIT(main.c) AC_CONFIG_HEADERS(config.h:config.in, [echo > stamp-h]) AM_MAINTAINER_MODE @@ -322,8 +324,10 @@ case $host_os in enable_gdbtk=no ;; esac -# Libunwind support for ia64. +# Handle optional debuginfod support +AC_DEBUGINFOD +# Libunwind support for ia64. AC_ARG_WITH(libunwind-ia64, AS_HELP_STRING([--with-libunwind-ia64], [use libunwind frame unwinding for ia64 targets]),, diff --git a/gdb/debuginfod-support.c b/gdb/debuginfod-support.c new file mode 100644 index 00000000000..e0f0fac0766 --- /dev/null +++ b/gdb/debuginfod-support.c @@ -0,0 +1,155 @@ +/* debuginfod utilities for GDB. + Copyright (C) 2020 Free Software Foundation, Inc. + + This file is part of GDB. + + 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/>. */ + +#include <errno.h> +#include "defs.h" +#include "cli/cli-style.h" +#include "gdbsupport/scoped_fd.h" +#include "debuginfod-support.h" + +#ifndef HAVE_LIBDEBUGINFOD +scoped_fd +debuginfod_source_query (const unsigned char *build_id, + int build_id_len, + const char *srcpath, + gdb::unique_xmalloc_ptr<char> *destname) +{ + return scoped_fd (-ENOSYS); +} + +scoped_fd +debuginfod_debuginfo_query (const unsigned char *build_id, + int build_id_len, + const char *filename, + gdb::unique_xmalloc_ptr<char> *destname) +{ + return scoped_fd (-ENOSYS); +} +#else +#include <elfutils/debuginfod.h> + +/* TODO: Use debuginfod API extensions instead of these globals. */ +static std::string desc; +static std::string fname; +static bool has_printed; + +static int +progressfn (debuginfod_client *c, long cur, long total) +{ + if (check_quit_flag ()) + { + printf_filtered ("Cancelling download of %s %ps...\n", + desc.c_str (), + styled_string (file_name_style.style (), fname.c_str ())); + return 1; + } + + if (!has_printed && total != 0) + { + /* Print this message only once. */ + has_printed = true; + printf_filtered ("Downloading %s %ps...\n", + desc.c_str (), + styled_string (file_name_style.style (), fname.c_str ())); + } + + return 0; +} + +static debuginfod_client * +debuginfod_init () +{ + debuginfod_client *c = debuginfod_begin (); + + if (c != nullptr) + debuginfod_set_progressfn (c, progressfn); + + return c; +} + +/* See debuginfod-support.h */ + +scoped_fd +debuginfod_source_query (const unsigned char *build_id, + int build_id_len, + const char *srcpath, + gdb::unique_xmalloc_ptr<char> *destname) +{ + if (getenv (DEBUGINFOD_URLS_ENV_VAR) == NULL) + return scoped_fd (-ENOSYS); + + debuginfod_client *c = debuginfod_init (); + + if (c == nullptr) + return scoped_fd (-ENOMEM); + + desc = std::string ("source file"); + fname = std::string (srcpath); + has_printed = false; + + scoped_fd fd (debuginfod_find_source (c, + build_id, + build_id_len, + srcpath, + nullptr)); + + /* TODO: Add 'set debug debuginfod' command to control when error messages are shown. */ + if (fd.get () < 0 && fd.get () != -ENOENT) + printf_filtered (_("Download failed: %s. Continuing without source file %ps.\n"), + safe_strerror (-fd.get ()), + styled_string (file_name_style.style (), srcpath)); + else + destname->reset (xstrdup (srcpath)); + + debuginfod_end (c); + return fd; +} + +/* See debuginfod-support.h */ + +scoped_fd +debuginfod_debuginfo_query (const unsigned char *build_id, + int build_id_len, + const char *filename, + gdb::unique_xmalloc_ptr<char> *destname) +{ + if (getenv (DEBUGINFOD_URLS_ENV_VAR) == NULL) + return scoped_fd (-ENOSYS); + + debuginfod_client *c = debuginfod_init (); + + if (c == nullptr) + return scoped_fd (-ENOMEM); + + desc = std::string ("separate debug info for"); + fname = std::string (filename); + has_printed = false; + char *dname = nullptr; + + scoped_fd fd (debuginfod_find_debuginfo (c, build_id, build_id_len, &dname)); + + if (fd.get () < 0 && fd.get () != -ENOENT) + printf_filtered (_("Download failed: %s. Continuing without debug info for %ps.\n"), + safe_strerror (-fd.get ()), + styled_string (file_name_style.style (), filename)); + + destname->reset (dname); + debuginfod_end (c); + return fd; +} +#endif diff --git a/gdb/debuginfod-support.h b/gdb/debuginfod-support.h new file mode 100644 index 00000000000..676c217b648 --- /dev/null +++ b/gdb/debuginfod-support.h @@ -0,0 +1,62 @@ +/* debuginfod utilities for GDB. + Copyright (C) 2020 Free Software Foundation, Inc. + + This file is part of GDB. + + 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/>. */ + +#ifndef DEBUGINFOD_SUPPORT_H +#define DEBUGINFOD_SUPPORT_H + +/* Query debuginfod servers for a source file associated with an + executable with BUILD_ID. BUILD_ID can be given as a binary blob or + a null-terminated string. If given as a binary blob, BUILD_ID_LEN + should be the number of bytes. If given as a null-terminated string, + BUILD_ID_LEN should be 0. + + SRC_PATH should be the source file's absolute path that includes the + compilation directory of the CU associated with the source file. + For example if a CU's compilation directory is `/my/build` and the + source file path is `/my/source/foo.c`, then SRC_PATH should be + `/my/build/../source/foo.c`. + + If the file is successfully retrieved, its path on the local machine + is stored in DESTNAME. If GDB is not built with debuginfod, this + function returns -ENOSYS. */ + +extern scoped_fd +debuginfod_source_query (const unsigned char *build_id, + int build_id_len, + const char *src_path, + gdb::unique_xmalloc_ptr<char> *destname); + +/* Query debuginfod servers for a debug info file with BUILD_ID. + BUILD_ID can be given as a binary blob or a null-terminated string. + If given as a binary blob, BUILD_ID_LEN should be the number of bytes. + If given as a null-terminated string, BUILD_ID_LEN should be 0. + + FILENAME should be the name or path of the main binary associated with + the separate debug info. It is used for printing messages to the user. + + If the file is successfully retrieved, its path on the local machine + is stored in DESTNAME. If GDB is not built with debuginfod, this + function returns -ENOSYS. */ + +extern scoped_fd +debuginfod_debuginfo_query (const unsigned char *build_id, + int build_id_len, + const char *filename, + gdb::unique_xmalloc_ptr<char> *destname); + +#endif /* DEBUGINFOD_SUPPORT_H */ diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index f1798e35b5f..b947c5dfb48 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -37848,6 +37848,14 @@ supported). Use the curses library instead of the termcap library, for text-mode terminal operations. +@item --with-debuginfod +Build @value{GDBN} with libdebuginfod, the debuginfod client library. +Used to automatically fetch source files and separate debug files from +debuginfod servers using the associated executable's build ID. Enabled +by default if libdebuginfod is installed and found at configure time. +debuginfod is packaged with elfutils, starting with version 0.178. You +can get the latest version from `https://sourceware.org/elfutils/'. + @item --with-libunwind-ia64 Use the libunwind library for unwinding function call stack on ia64 target platforms. See http://www.nongnu.org/libunwind/index.html for diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c index 68496447484..d6b34d8586e 100644 --- a/gdb/dwarf2/read.c +++ b/gdb/dwarf2/read.c @@ -83,6 +83,7 @@ #include "rust-lang.h" #include "gdbsupport/pathstuff.h" #include "count-one-bits.h" +#include "debuginfod-support.h" /* When == 1, print basic high level tracing messages. When > 1, be more verbose. @@ -2147,6 +2148,29 @@ dwarf2_get_dwz_file (struct dwarf2_per_objfile *dwarf2_per_objfile) if (dwz_bfd == NULL) dwz_bfd = build_id_to_debug_bfd (buildid_len, buildid); + if (dwz_bfd == nullptr) + { + gdb::unique_xmalloc_ptr<char> alt_filename; + const char *origname = dwarf2_per_objfile->objfile->original_name; + + scoped_fd fd (debuginfod_debuginfo_query (buildid, + buildid_len, + origname, + &alt_filename)); + + if (fd.get () >= 0) + { + /* File successfully retrieved from server. */ + dwz_bfd = gdb_bfd_open (alt_filename.get (), gnutarget, -1); + + if (dwz_bfd == nullptr) + warning (_("File \"%s\" from debuginfod cannot be opened as bfd"), + alt_filename.get ()); + else if (!build_id_verify (dwz_bfd.get (), buildid_len, buildid)) + dwz_bfd.reset (nullptr); + } + } + if (dwz_bfd == NULL) error (_("could not find '.gnu_debugaltlink' file for %s"), objfile_name (dwarf2_per_objfile->objfile)); diff --git a/gdb/elfread.c b/gdb/elfread.c index 453bca527e9..d842d5b573d 100644 --- a/gdb/elfread.c +++ b/gdb/elfread.c @@ -49,6 +49,8 @@ #include "mdebugread.h" #include "ctfread.h" #include "gdbsupport/gdb_string_view.h" +#include "gdbsupport/scoped_fd.h" +#include "debuginfod-support.h" /* Forward declarations. */ extern const struct sym_fns elf_sym_fns_gdb_index; @@ -1316,8 +1318,36 @@ elf_symfile_read (struct objfile *objfile, symfile_add_flags symfile_flags) symbol_file_add_separate (debug_bfd.get (), debugfile.c_str (), symfile_flags, objfile); } - else + else + { has_dwarf2 = false; + const struct bfd_build_id *build_id = build_id_bfd_get (objfile->obfd); + + if (build_id != nullptr) + { + gdb::unique_xmalloc_ptr<char> symfile_path; + scoped_fd fd (debuginfod_debuginfo_query (build_id->data, + build_id->size, + objfile->original_name, + &symfile_path)); + + if (fd.get () >= 0) + { + /* File successfully retrieved from server. */ + gdb_bfd_ref_ptr debug_bfd (symfile_bfd_open (symfile_path.get ())); + + if (debug_bfd == nullptr) + warning (_("File \"%s\" from debuginfod cannot be opened as bfd"), + objfile->original_name); + else if (build_id_verify (debug_bfd.get (), build_id->size, build_id->data)) + { + symbol_file_add_separate (debug_bfd.get (), symfile_path.get (), + symfile_flags, objfile); + has_dwarf2 = true; + } + } + } + } } /* Read the CTF section only if there is no DWARF info. */ diff --git a/gdb/source.c b/gdb/source.c index 4f889e4b95a..051caf5c57f 100644 --- a/gdb/source.c +++ b/gdb/source.c @@ -48,6 +48,8 @@ #include "source-cache.h" #include "cli/cli-style.h" #include "observable.h" +#include "build-id.h" +#include "debuginfod-support.h" #define OPEN_MODE (O_RDONLY | O_BINARY) #define FDOPEN_MODE FOPEN_RB @@ -1148,6 +1150,34 @@ open_source_file (struct symtab *s) s->fullname = NULL; scoped_fd fd = find_and_open_source (s->filename, SYMTAB_DIRNAME (s), &fullname); + + if (fd.get () < 0) + { + if (SYMTAB_COMPUNIT (s) != nullptr) + { + const objfile *ofp = COMPUNIT_OBJFILE (SYMTAB_COMPUNIT (s)); + + std::string srcpath; + if (IS_ABSOLUTE_PATH (s->filename)) + srcpath = s->filename; + else + { + srcpath = SYMTAB_DIRNAME (s); + srcpath += SLASH_STRING; + srcpath += s->filename; + } + + const struct bfd_build_id *build_id = build_id_bfd_get (ofp->obfd); + + /* Query debuginfod for the source file. */ + if (build_id != nullptr) + fd = debuginfod_source_query (build_id->data, + build_id->size, + srcpath.c_str (), + &fullname); + } + } + s->fullname = fullname.release (); return fd; } diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index a352c74f0f8..52c46f9e2f0 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,9 @@ +2020-02-26 Aaron Merey <amerey@redhat.com> + + * gdb.debuginfod: New directory for debuginfod tests. + * gdb.debuginfod/main.c: New test file. + * gdb.debuginfod/fetch_src_and_symbols.exp: New tests. + 2020-02-26 Tom de Vries <tdevries@suse.de> PR gdb/25603 diff --git a/gdb/testsuite/gdb.debuginfod/fetch_src_and_symbols.exp b/gdb/testsuite/gdb.debuginfod/fetch_src_and_symbols.exp new file mode 100644 index 00000000000..0bf18f2d124 --- /dev/null +++ b/gdb/testsuite/gdb.debuginfod/fetch_src_and_symbols.exp @@ -0,0 +1,214 @@ +# Copyright 2020 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 debuginfod functionality + +standard_testfile main.c + +load_lib dwarf.exp + +if { [which debuginfod] == 0 } { + untested "cannot find debuginfod" + return -1 +} + +if { [which curl] == 0 } { + untested "cannot find curl" + return -1 +} + +# Skip testing if gdb was not configured with debuginfod +if { [string first "with-debuginfod" [exec $GDB --configuration]] == -1 } { + untested "gdb not configured with debuginfod" + return -1 +} + +set cache [standard_output_file ".client_cache"] +set db [standard_output_file ".debuginfod.db"] + +# Delete any preexisting test files +file delete -force $cache +file delete -force $db + +set sourcetmp [standard_output_file tmp-${srcfile}] +set outputdir [standard_output_file {}] + +# Make a copy source file that we can move around +if { [catch {file copy -force ${srcdir}/${subdir}/${srcfile} \ + [standard_output_file ${sourcetmp}]}] != 0 } { + error "create temporary file" + return -1 +} + +if { [gdb_compile "$sourcetmp" "$binfile" executable {debug}] != "" } { + fail "compile" + return -1 +} + +setenv DEBUGINFOD_URLS "" +setenv DEBUGINFOD_TIMEOUT 30 +setenv DEBUGINFOD_CACHE_PATH $cache + +# Test that gdb cannot find source without debuginfod +clean_restart $binfile +gdb_test_no_output "set substitute-path $outputdir /dev/null" +gdb_test "list" ".*No such file or directory.*" + +# Strip symbols into separate file and move it so gdb cannot find it without debuginfod +if { [gdb_gnu_strip_debug $binfile ""] != 0 } { + fail "strip debuginfo" + return -1 +} + +set debugdir [standard_output_file "debug"] +set debuginfo [standard_output_file "fetch_src_and_symbols.debug"] + +file mkdir $debugdir +file rename -force $debuginfo $debugdir + +# Test that gdb cannot find symbols without debuginfod +clean_restart $binfile +gdb_test "file" ".*No symbol file.*" + +# Write some assembly that just has a .gnu_debugaltlink section. +# Copied from testsuite/gdb.dwarf2/dwzbuildid.exp. +proc write_just_debugaltlink {filename dwzname buildid} { + set asm_file [standard_output_file $filename] + + Dwarf::assemble $asm_file { + upvar dwzname dwzname + upvar buildid buildid + + gnu_debugaltlink $dwzname $buildid + + # Only the DWARF reader checks .gnu_debugaltlink, so make sure + # there is a bit of DWARF in here. + cu {} { + compile_unit {{language @DW_LANG_C}} { + } + } + } +} + +# Write some DWARF that also sets the buildid. +# Copied from testsuite/gdb.dwarf2/dwzbuildid.exp. +proc write_dwarf_file {filename buildid {value 99}} { + set asm_file [standard_output_file $filename] + + Dwarf::assemble $asm_file { + declare_labels int_label int_label2 + + upvar buildid buildid + upvar value value + + build_id $buildid + + cu {} { + compile_unit {{language @DW_LANG_C}} { + int_label2: base_type { + {name int} + {byte_size 4 sdata} + {encoding @DW_ATE_signed} + } + + constant { + {name the_int} + {type :$int_label2} + {const_value $value data1} + } + } + } + } +} + +set buildid "01234567890abcdef0123456" + +write_just_debugaltlink ${binfile}_has_altlink.S ${binfile}_dwz.o $buildid +write_dwarf_file ${binfile}_dwz.S $buildid + +if {[gdb_compile ${binfile}_has_altlink.S ${binfile}_alt.o object nodebug] != ""} { + fail "compile main with altlink" + return -1 +} + +if {[gdb_compile ${binfile}_dwz.S ${binfile}_dwz.o object nodebug] != ""} { + fail "compile altlink" + return -1 +} + +file rename -force ${binfile}_dwz.o $debugdir + +# Test that gdb cannot find dwz without debuginfod. +clean_restart +gdb_test "file ${binfile}_alt.o" ".*could not find '.gnu_debugaltlink'.*" + +# Find an unused port +set port 7999 +set found 0 +while { ! $found } { + incr port + if { $port == 65536 } { + fail "no available ports" + return -1 + } + + spawn debuginfod -vvvv -d $db -p $port -F $debugdir + expect { + "started http server on IPv4 IPv6 port=$port" { set found 1 } + "failed to bind to port" { kill_wait_spawned_process $spawn_id } + timeout { + fail "find port timeout" + return -1 + } + } +} + +set metrics [list "ready 1" \ + "thread_work_total{role=\"traverse\"} 1" \ + "thread_work_pending{role=\"scan\"} 0" \ + "thread_busy{role=\"scan\"} 0"] + +# Check server metrics to confirm init has completed. +foreach m $metrics { + set timelim 20 + while { $timelim != 0 } { + sleep 0.5 + catch {exec curl -s http://127.0.0.1:$port/metrics} got + + if { [regexp $m $got] } { + break + } + + incr timelim -1 + } + + if { $timelim == 0 } { + fail "server init timeout" + return -1 + } +} + +# Point the client to the server +setenv DEBUGINFOD_URLS http://127.0.0.1:$port + +# gdb should now find the symbol and source files +clean_restart $binfile +gdb_test_no_output "set substitute-path $outputdir /dev/null" +gdb_test "br main" "Breakpoint 1 at.*file.*" +gdb_test "l" ".*This program is distributed in the hope.*" + +# gdb should now find the debugaltlink file +clean_restart +gdb_test "file ${binfile}_alt.o" ".*Reading symbols from ${binfile}_alt.o\.\.\.*" diff --git a/gdb/testsuite/gdb.debuginfod/main.c b/gdb/testsuite/gdb.debuginfod/main.c new file mode 100644 index 00000000000..73abaf58b1c --- /dev/null +++ b/gdb/testsuite/gdb.debuginfod/main.c @@ -0,0 +1,25 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2020 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/>. */ + +/* Dummy main function. */ + +int +main() +{ + asm ("main_label: .globl main_label"); + return 0; +} diff --git a/gdb/top.c b/gdb/top.c index f702af9acd3..1a98ae198c0 100644 --- a/gdb/top.c +++ b/gdb/top.c @@ -1528,6 +1528,16 @@ This GDB was configured as follows:\n\ ")); #endif +#if HAVE_LIBDEBUGINFOD + fprintf_filtered (stream, _("\ + --with-debuginfod\n\ +")); +#else + fprintf_filtered (stream, _("\ + --without-debuginfod\n\ +")); +#endif + #if HAVE_GUILE fprintf_filtered (stream, _("\ --with-guile\n\ |