From 4fc8125ee9fbad9dbcc2da913c278cfa2efa726e Mon Sep 17 00:00:00 2001
From: Aaron Merey <amerey@redhat.com>
Date: Thu, 6 Feb 2020 14:05:05 -0500
Subject: [PATCH] Add debuginfod support to GDB

debuginfod is a lightweight web service that indexes ELF/DWARF debugging
resources by build-id and serves them over HTTP.

This patch enables GDB to query debuginfod servers for separate debug
files and source code when it is otherwise not able to find them.

GDB can be built with debuginfod using the --with-debuginfod configure
option.

This requires that libdebuginfod be installed and found at configure time.

debuginfod is packaged with elfutils, starting with version 0.178.

For more information see https://sourceware.org/elfutils/.

Tested on x86_64 Fedora 31.

gdb/ChangeLog:
2020-02-06  Aaron Merey  <amerey@redhat.com>

        * Makefile.in: Handle optional debuginfod support.
        * NEWS: Update.
        * README: Add --with-debuginfod summary.
        * aclocal.m4: Regenerate.
        * 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.
        * dwarf2read.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.

gdb/testsuite/ChangeLog:
2020-02-06  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.
---
 gdb/Makefile.in                               |   7 +-
 gdb/NEWS                                      |  14 +
 gdb/README                                    |   9 +
 gdb/aclocal.m4                                | 344 ++++++++++++++++++
 gdb/config.in                                 |   3 +
 gdb/configure                                 | 246 ++++++++++++-
 gdb/configure.ac                              |  20 +-
 gdb/debuginfod-support.c                      | 102 ++++++
 gdb/debuginfod-support.h                      |  43 +++
 gdb/doc/gdb.texinfo                           |   8 +
 gdb/dwarf2read.c                              |  25 ++
 gdb/elfread.c                                 |  33 +-
 gdb/source.c                                  |  44 +++
 .../gdb.debuginfod/fetch_src_and_symbols.exp  | 232 ++++++++++++
 gdb/testsuite/gdb.debuginfod/main.c           |  25 ++
 gdb/top.c                                     |  10 +
 16 files changed, 1159 insertions(+), 6 deletions(-)
 create mode 100644 gdb/debuginfod-support.c
 create mode 100644 gdb/debuginfod-support.h
 create mode 100644 gdb/testsuite/gdb.debuginfod/fetch_src_and_symbols.exp
 create mode 100644 gdb/testsuite/gdb.debuginfod/main.c

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 45d1586e85..e31493df9e 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -591,7 +591,8 @@ INTERNAL_CFLAGS_BASE = \
 	$(GDB_CFLAGS) $(OPCODES_CFLAGS) $(READLINE_CFLAGS) $(ZLIBINC) \
 	$(BFD_CFLAGS) $(INCLUDE_CFLAGS) $(LIBDECNUMBER_CFLAGS) \
 	$(INTL_CFLAGS) $(INCGNU) $(INCSUPPORT) $(ENABLE_CFLAGS) \
-	$(INTERNAL_CPPFLAGS) $(SRCHIGH_CFLAGS) $(TOP_CFLAGS) $(PTHREAD_CFLAGS)
+	$(INTERNAL_CPPFLAGS) $(SRCHIGH_CFLAGS) $(TOP_CFLAGS) $(PTHREAD_CFLAGS) \
+	@DEBUGINFOD_CFLAGS@
 INTERNAL_WARN_CFLAGS = $(INTERNAL_CFLAGS_BASE) $(GDB_WARN_CFLAGS)
 INTERNAL_CFLAGS = $(INTERNAL_WARN_CFLAGS) $(GDB_WERROR_CFLAGS)
 
@@ -616,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) \
+	@DEBUGINFOD_LIBS@
 CDEPS = $(NAT_CDEPS) $(SIM) $(BFD) $(READLINE_DEPS) $(LIBCTF) \
 	$(OPCODES) $(INTL_DEPS) $(LIBIBERTY) $(CONFIG_DEPS) $(LIBGNU) \
 	$(LIBSUPPORT)
@@ -990,6 +992,7 @@ COMMON_SFILES = \
 	dbxread.c \
 	dcache.c \
 	debug.c \
+	debuginfod-support.c \
 	dictionary.c \
 	disasm.c \
 	disasm-selftests.c \
diff --git a/gdb/NEWS b/gdb/NEWS
index d4e2e70f38..2f9e7756b0 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -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.
+
 * Debugging MS-Windows processes now sets $_exitsignal when the
   inferior is terminated by a signal, instead of setting $_exitcode.
 
diff --git a/gdb/README b/gdb/README
index be7fdcb65d..840cf1f85c 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/aclocal.m4 b/gdb/aclocal.m4
index 110b416e61..ea8816ee21 100644
--- a/gdb/aclocal.m4
+++ b/gdb/aclocal.m4
@@ -12,6 +12,350 @@
 # PARTICULAR PURPOSE.
 
 m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])])
+# pkg.m4 - Macros to locate and utilise pkg-config.   -*- Autoconf -*-
+# serial 11 (pkg-config-0.29.1)
+
+dnl Copyright Â© 2004 Scott James Remnant <scott@netsplit.com>.
+dnl Copyright Â© 2012-2015 Dan Nicholson <dbn.lists@gmail.com>
+dnl
+dnl This program is free software; you can redistribute it and/or modify
+dnl it under the terms of the GNU General Public License as published by
+dnl the Free Software Foundation; either version 2 of the License, or
+dnl (at your option) any later version.
+dnl
+dnl This program is distributed in the hope that it will be useful, but
+dnl WITHOUT ANY WARRANTY; without even the implied warranty of
+dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+dnl General Public License for more details.
+dnl
+dnl You should have received a copy of the GNU General Public License
+dnl along with this program; if not, write to the Free Software
+dnl Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+dnl 02111-1307, USA.
+dnl
+dnl As a special exception to the GNU General Public License, if you
+dnl distribute this file as part of a program that contains a
+dnl configuration script generated by Autoconf, you may include it under
+dnl the same distribution terms that you use for the rest of that
+dnl program.
+
+dnl PKG_PREREQ(MIN-VERSION)
+dnl -----------------------
+dnl Since: 0.29
+dnl
+dnl Verify that the version of the pkg-config macros are at least
+dnl MIN-VERSION. Unlike PKG_PROG_PKG_CONFIG, which checks the user's
+dnl installed version of pkg-config, this checks the developer's version
+dnl of pkg.m4 when generating configure.
+dnl
+dnl To ensure that this macro is defined, also add:
+dnl m4_ifndef([PKG_PREREQ],
+dnl     [m4_fatal([must install pkg-config 0.29 or later before running autoconf/autogen])])
+dnl
+dnl See the "Since" comment for each macro you use to see what version
+dnl of the macros you require.
+m4_defun([PKG_PREREQ],
+[m4_define([PKG_MACROS_VERSION], [0.29.1])
+m4_if(m4_version_compare(PKG_MACROS_VERSION, [$1]), -1,
+    [m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])])
+])dnl PKG_PREREQ
+
+dnl PKG_PROG_PKG_CONFIG([MIN-VERSION])
+dnl ----------------------------------
+dnl Since: 0.16
+dnl
+dnl Search for the pkg-config tool and set the PKG_CONFIG variable to
+dnl first found in the path. Checks that the version of pkg-config found
+dnl is at least MIN-VERSION. If MIN-VERSION is not specified, 0.9.0 is
+dnl used since that's the first version where most current features of
+dnl pkg-config existed.
+AC_DEFUN([PKG_PROG_PKG_CONFIG],
+[m4_pattern_forbid([^_?PKG_[A-Z_]+$])
+m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$])
+m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$])
+AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])
+AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path])
+AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path])
+
+if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
+	AC_PATH_TOOL([PKG_CONFIG], [pkg-config])
+fi
+if test -n "$PKG_CONFIG"; then
+	_pkg_min_version=m4_default([$1], [0.9.0])
+	AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version])
+	if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then
+		AC_MSG_RESULT([yes])
+	else
+		AC_MSG_RESULT([no])
+		PKG_CONFIG=""
+	fi
+fi[]dnl
+])dnl PKG_PROG_PKG_CONFIG
+
+dnl PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
+dnl -------------------------------------------------------------------
+dnl Since: 0.18
+dnl
+dnl Check to see whether a particular set of modules exists. Similar to
+dnl PKG_CHECK_MODULES(), but does not set variables or print errors.
+dnl
+dnl Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG])
+dnl only at the first occurence in configure.ac, so if the first place
+dnl it's called might be skipped (such as if it is within an "if", you
+dnl have to call PKG_CHECK_EXISTS manually
+AC_DEFUN([PKG_CHECK_EXISTS],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
+if test -n "$PKG_CONFIG" && \
+    AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then
+  m4_default([$2], [:])
+m4_ifvaln([$3], [else
+  $3])dnl
+fi])
+
+dnl _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES])
+dnl ---------------------------------------------
+dnl Internal wrapper calling pkg-config via PKG_CONFIG and setting
+dnl pkg_failed based on the result.
+m4_define([_PKG_CONFIG],
+[if test -n "$$1"; then
+    pkg_cv_[]$1="$$1"
+ elif test -n "$PKG_CONFIG"; then
+    PKG_CHECK_EXISTS([$3],
+                     [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`
+		      test "x$?" != "x0" && pkg_failed=yes ],
+		     [pkg_failed=yes])
+ else
+    pkg_failed=untried
+fi[]dnl
+])dnl _PKG_CONFIG
+
+dnl _PKG_SHORT_ERRORS_SUPPORTED
+dnl ---------------------------
+dnl Internal check to see if pkg-config supports short errors.
+AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi[]dnl
+])dnl _PKG_SHORT_ERRORS_SUPPORTED
+
+
+dnl PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
+dnl   [ACTION-IF-NOT-FOUND])
+dnl --------------------------------------------------------------
+dnl Since: 0.4.0
+dnl
+dnl Note that if there is a possibility the first call to
+dnl PKG_CHECK_MODULES might not happen, you should be sure to include an
+dnl explicit call to PKG_PROG_PKG_CONFIG in your configure.ac
+AC_DEFUN([PKG_CHECK_MODULES],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
+AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl
+AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl
+
+pkg_failed=no
+AC_MSG_CHECKING([for $1])
+
+_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2])
+_PKG_CONFIG([$1][_LIBS], [libs], [$2])
+
+m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS
+and $1[]_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details.])
+
+if test $pkg_failed = yes; then
+	AC_MSG_RESULT([no])
+        _PKG_SHORT_ERRORS_SUPPORTED
+        if test $_pkg_short_errors_supported = yes; then
+	        $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1`
+        else
+	        $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1`
+        fi
+	# Put the nasty error message in config.log where it belongs
+	echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD
+
+	m4_default([$4], [AC_MSG_ERROR(
+[Package requirements ($2) were not met:
+
+$$1_PKG_ERRORS
+
+Consider adjusting the PKG_CONFIG_PATH environment variable if you
+installed software in a non-standard prefix.
+
+_PKG_TEXT])[]dnl
+        ])
+elif test $pkg_failed = untried; then
+	AC_MSG_RESULT([no])
+	m4_default([$4], [AC_MSG_FAILURE(
+[The pkg-config script could not be found or is too old.  Make sure it
+is in your PATH or set the PKG_CONFIG environment variable to the full
+path to pkg-config.
+
+_PKG_TEXT
+
+To get pkg-config, see <http://pkg-config.freedesktop.org/>.])[]dnl
+        ])
+else
+	$1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS
+	$1[]_LIBS=$pkg_cv_[]$1[]_LIBS
+        AC_MSG_RESULT([yes])
+	$3
+fi[]dnl
+])dnl PKG_CHECK_MODULES
+
+
+dnl PKG_CHECK_MODULES_STATIC(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
+dnl   [ACTION-IF-NOT-FOUND])
+dnl ---------------------------------------------------------------------
+dnl Since: 0.29
+dnl
+dnl Checks for existence of MODULES and gathers its build flags with
+dnl static libraries enabled. Sets VARIABLE-PREFIX_CFLAGS from --cflags
+dnl and VARIABLE-PREFIX_LIBS from --libs.
+dnl
+dnl Note that if there is a possibility the first call to
+dnl PKG_CHECK_MODULES_STATIC might not happen, you should be sure to
+dnl include an explicit call to PKG_PROG_PKG_CONFIG in your
+dnl configure.ac.
+AC_DEFUN([PKG_CHECK_MODULES_STATIC],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
+_save_PKG_CONFIG=$PKG_CONFIG
+PKG_CONFIG="$PKG_CONFIG --static"
+PKG_CHECK_MODULES($@)
+PKG_CONFIG=$_save_PKG_CONFIG[]dnl
+])dnl PKG_CHECK_MODULES_STATIC
+
+
+dnl PKG_INSTALLDIR([DIRECTORY])
+dnl -------------------------
+dnl Since: 0.27
+dnl
+dnl Substitutes the variable pkgconfigdir as the location where a module
+dnl should install pkg-config .pc files. By default the directory is
+dnl $libdir/pkgconfig, but the default can be changed by passing
+dnl DIRECTORY. The user can override through the --with-pkgconfigdir
+dnl parameter.
+AC_DEFUN([PKG_INSTALLDIR],
+[m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])])
+m4_pushdef([pkg_description],
+    [pkg-config installation directory @<:@]pkg_default[@:>@])
+AC_ARG_WITH([pkgconfigdir],
+    [AS_HELP_STRING([--with-pkgconfigdir], pkg_description)],,
+    [with_pkgconfigdir=]pkg_default)
+AC_SUBST([pkgconfigdir], [$with_pkgconfigdir])
+m4_popdef([pkg_default])
+m4_popdef([pkg_description])
+])dnl PKG_INSTALLDIR
+
+
+dnl PKG_NOARCH_INSTALLDIR([DIRECTORY])
+dnl --------------------------------
+dnl Since: 0.27
+dnl
+dnl Substitutes the variable noarch_pkgconfigdir as the location where a
+dnl module should install arch-independent pkg-config .pc files. By
+dnl default the directory is $datadir/pkgconfig, but the default can be
+dnl changed by passing DIRECTORY. The user can override through the
+dnl --with-noarch-pkgconfigdir parameter.
+AC_DEFUN([PKG_NOARCH_INSTALLDIR],
+[m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])])
+m4_pushdef([pkg_description],
+    [pkg-config arch-independent installation directory @<:@]pkg_default[@:>@])
+AC_ARG_WITH([noarch-pkgconfigdir],
+    [AS_HELP_STRING([--with-noarch-pkgconfigdir], pkg_description)],,
+    [with_noarch_pkgconfigdir=]pkg_default)
+AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir])
+m4_popdef([pkg_default])
+m4_popdef([pkg_description])
+])dnl PKG_NOARCH_INSTALLDIR
+
+
+dnl PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE,
+dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
+dnl -------------------------------------------
+dnl Since: 0.28
+dnl
+dnl Retrieves the value of the pkg-config variable for the given module.
+AC_DEFUN([PKG_CHECK_VAR],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
+AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl
+
+_PKG_CONFIG([$1], [variable="][$3]["], [$2])
+AS_VAR_COPY([$1], [pkg_cv_][$1])
+
+AS_VAR_IF([$1], [""], [$5], [$4])dnl
+])dnl PKG_CHECK_VAR
+
+dnl PKG_WITH_MODULES(VARIABLE-PREFIX, MODULES,
+dnl   [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND],
+dnl   [DESCRIPTION], [DEFAULT])
+dnl ------------------------------------------
+dnl
+dnl Prepare a "--with-" configure option using the lowercase
+dnl [VARIABLE-PREFIX] name, merging the behaviour of AC_ARG_WITH and
+dnl PKG_CHECK_MODULES in a single macro.
+AC_DEFUN([PKG_WITH_MODULES],
+[
+m4_pushdef([with_arg], m4_tolower([$1]))
+
+m4_pushdef([description],
+           [m4_default([$5], [build with ]with_arg[ support])])
+
+m4_pushdef([def_arg], [m4_default([$6], [auto])])
+m4_pushdef([def_action_if_found], [AS_TR_SH([with_]with_arg)=yes])
+m4_pushdef([def_action_if_not_found], [AS_TR_SH([with_]with_arg)=no])
+
+m4_case(def_arg,
+            [yes],[m4_pushdef([with_without], [--without-]with_arg)],
+            [m4_pushdef([with_without],[--with-]with_arg)])
+
+AC_ARG_WITH(with_arg,
+     AS_HELP_STRING(with_without, description[ @<:@default=]def_arg[@:>@]),,
+    [AS_TR_SH([with_]with_arg)=def_arg])
+
+AS_CASE([$AS_TR_SH([with_]with_arg)],
+            [yes],[PKG_CHECK_MODULES([$1],[$2],$3,$4)],
+            [auto],[PKG_CHECK_MODULES([$1],[$2],
+                                        [m4_n([def_action_if_found]) $3],
+                                        [m4_n([def_action_if_not_found]) $4])])
+
+m4_popdef([with_arg])
+m4_popdef([description])
+m4_popdef([def_arg])
+
+])dnl PKG_WITH_MODULES
+
+dnl PKG_HAVE_WITH_MODULES(VARIABLE-PREFIX, MODULES,
+dnl   [DESCRIPTION], [DEFAULT])
+dnl -----------------------------------------------
+dnl
+dnl Convenience macro to trigger AM_CONDITIONAL after PKG_WITH_MODULES
+dnl check._[VARIABLE-PREFIX] is exported as make variable.
+AC_DEFUN([PKG_HAVE_WITH_MODULES],
+[
+PKG_WITH_MODULES([$1],[$2],,,[$3],[$4])
+
+AM_CONDITIONAL([HAVE_][$1],
+               [test "$AS_TR_SH([with_]m4_tolower([$1]))" = "yes"])
+])dnl PKG_HAVE_WITH_MODULES
+
+dnl PKG_HAVE_DEFINE_WITH_MODULES(VARIABLE-PREFIX, MODULES,
+dnl   [DESCRIPTION], [DEFAULT])
+dnl ------------------------------------------------------
+dnl
+dnl Convenience macro to run AM_CONDITIONAL and AC_DEFINE after
+dnl PKG_WITH_MODULES check. HAVE_[VARIABLE-PREFIX] is exported as make
+dnl and preprocessor variable.
+AC_DEFUN([PKG_HAVE_DEFINE_WITH_MODULES],
+[
+PKG_HAVE_WITH_MODULES([$1],[$2],[$3],[$4])
+
+AS_IF([test "$AS_TR_SH([with_]m4_tolower([$1]))" = "yes"],
+        [AC_DEFINE([HAVE_][$1], 1, [Enable ]m4_tolower([$1])[ support])])
+])dnl PKG_HAVE_DEFINE_WITH_MODULES
+
 # AM_AUX_DIR_EXPAND                                         -*- Autoconf -*-
 
 # Copyright (C) 2001-2017 Free Software Foundation, Inc.
diff --git a/gdb/config.in b/gdb/config.in
index cb886ba8e1..5939b8e436 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 72ffad8d37..be60595661 100755
--- a/gdb/configure
+++ b/gdb/configure
@@ -758,6 +758,11 @@ REPORT_BUGS_TEXI
 REPORT_BUGS_TO
 PKGVERSION
 CODESIGN_CERT
+DEBUGINFOD_LIBS
+DEBUGINFOD_CFLAGS
+PKG_CONFIG_LIBDIR
+PKG_CONFIG_PATH
+PKG_CONFIG
 HAVE_NATIVE_GCORE_TARGET
 TARGET_OBS
 subdirs
@@ -869,6 +874,7 @@ enable_64_bit_bfd
 enable_gdbmi
 enable_tui
 enable_gdbtk
+with_debuginfod
 with_libunwind_ia64
 with_curses
 enable_profiling
@@ -926,6 +932,11 @@ CCC
 CPP
 MAKEINFO
 MAKEINFOFLAGS
+PKG_CONFIG
+PKG_CONFIG_PATH
+PKG_CONFIG_LIBDIR
+DEBUGINFOD_CFLAGS
+DEBUGINFOD_LIBS
 YACC
 YFLAGS
 XMKMF'
@@ -1598,6 +1609,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
@@ -1661,6 +1674,15 @@ Some influential environment variables:
   MAKEINFO    Parent configure detects if it is of sufficient version.
   MAKEINFOFLAGS
               Parameters for MAKEINFO.
+  PKG_CONFIG  path to pkg-config utility
+  PKG_CONFIG_PATH
+              directories to add to pkg-config's search path
+  PKG_CONFIG_LIBDIR
+              path overriding pkg-config's built-in search path
+  DEBUGINFOD_CFLAGS
+              C compiler flags for DEBUGINFOD, overriding pkg-config
+  DEBUGINFOD_LIBS
+              linker flags for DEBUGINFOD, overriding pkg-config
   YACC        The `Yet Another Compiler Compiler' implementation to use.
               Defaults to the first program found out of: `bison -y', `byacc',
               `yacc'.
@@ -6824,8 +6846,230 @@ $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
+
+# 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 "x$with_debuginfod" != xno; then
+
+
+
+
+
+
+
+if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
+	if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args.
+set dummy ${ac_tool_prefix}pkg-config; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_PKG_CONFIG+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  case $PKG_CONFIG in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+  ;;
+esac
+fi
+PKG_CONFIG=$ac_cv_path_PKG_CONFIG
+if test -n "$PKG_CONFIG"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5
+$as_echo "$PKG_CONFIG" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_path_PKG_CONFIG"; then
+  ac_pt_PKG_CONFIG=$PKG_CONFIG
+  # Extract the first word of "pkg-config", so it can be a program name with args.
+set dummy pkg-config; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_ac_pt_PKG_CONFIG+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  case $ac_pt_PKG_CONFIG in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_path_ac_pt_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+  ;;
+esac
+fi
+ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG
+if test -n "$ac_pt_PKG_CONFIG"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_PKG_CONFIG" >&5
+$as_echo "$ac_pt_PKG_CONFIG" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_pt_PKG_CONFIG" = x; then
+    PKG_CONFIG=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    PKG_CONFIG=$ac_pt_PKG_CONFIG
+  fi
+else
+  PKG_CONFIG="$ac_cv_path_PKG_CONFIG"
+fi
+
+fi
+if test -n "$PKG_CONFIG"; then
+	_pkg_min_version=0.9.0
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: checking pkg-config is at least version $_pkg_min_version" >&5
+$as_echo_n "checking pkg-config is at least version $_pkg_min_version... " >&6; }
+	if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then
+		{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+	else
+		{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+		PKG_CONFIG=""
+	fi
+fi
 
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for DEBUGINFOD" >&5
+$as_echo_n "checking for DEBUGINFOD... " >&6; }
+
+if test -n "$DEBUGINFOD_CFLAGS"; then
+    pkg_cv_DEBUGINFOD_CFLAGS="$DEBUGINFOD_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libdebuginfod >= 0.178\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "libdebuginfod >= 0.178") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_DEBUGINFOD_CFLAGS=`$PKG_CONFIG --cflags "libdebuginfod >= 0.178" 2>/dev/null`
+		      test "x$?" != "x0" && pkg_failed=yes
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+if test -n "$DEBUGINFOD_LIBS"; then
+    pkg_cv_DEBUGINFOD_LIBS="$DEBUGINFOD_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libdebuginfod >= 0.178\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "libdebuginfod >= 0.178") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_DEBUGINFOD_LIBS=`$PKG_CONFIG --libs "libdebuginfod >= 0.178" 2>/dev/null`
+		      test "x$?" != "x0" && pkg_failed=yes
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi
+        if test $_pkg_short_errors_supported = yes; then
+	        DEBUGINFOD_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libdebuginfod >= 0.178" 2>&1`
+        else
+	        DEBUGINFOD_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libdebuginfod >= 0.178" 2>&1`
+        fi
+	# Put the nasty error message in config.log where it belongs
+	echo "$DEBUGINFOD_PKG_ERRORS" >&5
+
+	if test "x$with_debuginfod" = xyes; then
+       as_fn_error $? "\"--with-debuginfod was given, but libdebuginfod is missing or unusable.\"" "$LINENO" 5
+     else
+       { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: libdebuginfod is missing or unusable; some features may be unavailable." >&5
+$as_echo "$as_me: WARNING: libdebuginfod is missing or unusable; some features may be unavailable." >&2;}
+     fi
+elif test $pkg_failed = untried; then
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+	if test "x$with_debuginfod" = xyes; then
+       as_fn_error $? "\"--with-debuginfod was given, but libdebuginfod is missing or unusable.\"" "$LINENO" 5
+     else
+       { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: libdebuginfod is missing or unusable; some features may be unavailable." >&5
+$as_echo "$as_me: WARNING: libdebuginfod is missing or unusable; some features may be unavailable." >&2;}
+     fi
+else
+	DEBUGINFOD_CFLAGS=$pkg_cv_DEBUGINFOD_CFLAGS
+	DEBUGINFOD_LIBS=$pkg_cv_DEBUGINFOD_LIBS
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+$as_echo "#define HAVE_LIBDEBUGINFOD 1" >>confdefs.h
+
+fi
+else
+  { $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;}
+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 0ca169101b..dd82f155b0 100644
--- a/gdb/configure.ac
+++ b/gdb/configure.ac
@@ -323,8 +323,26 @@ case $host_os in
     enable_gdbtk=no ;;
 esac
 
-# Libunwind support for ia64.
+# Handle optional debuginfod support
+AC_ARG_WITH([debuginfod],
+  AC_HELP_STRING([--with-debuginfod], [Enable debuginfo lookups with debuginfod (auto/yes/no)]),
+  [], [with_debuginfod=auto])
+AC_MSG_CHECKING([whether to use debuginfod])
+AC_MSG_RESULT([$with_debuginfod])
+
+if test "x$with_debuginfod" != xno; then
+  PKG_CHECK_MODULES([DEBUGINFOD], [libdebuginfod >= 0.178],
+    [AC_DEFINE([HAVE_LIBDEBUGINFOD], [1], [Define to 1 if debuginfod is enabled.])],
+    [if test "x$with_debuginfod" = xyes; then
+       AC_MSG_ERROR(["--with-debuginfod was given, but libdebuginfod is missing or unusable."])
+     else
+       AC_MSG_WARN([libdebuginfod is missing or unusable; some features may be unavailable.])
+     fi])
+else
+  AC_MSG_WARN([debuginfod support disabled; some features may be unavailable.])
+fi
 
+# 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 0000000000..04b55ca1d5
--- /dev/null
+++ b/gdb/debuginfod-support.c
@@ -0,0 +1,102 @@
+/* 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 "debuginfod-support.h"
+
+#ifndef HAVE_LIBDEBUGINFOD
+int
+debuginfod_source_query (const unsigned char *build_id __attribute__((unused)),
+                         int build_id_len __attribute__((unused)),
+                         const char *srcpath __attribute__((unused)),
+                         char **filename __attribute__((unused)))
+{
+  return -ENOSYS;
+}
+
+int
+debuginfod_debuginfo_query (const unsigned char *build_id __attribute__((unused)),
+                            int build_id_len __attribute__((unused)),
+                            char **filename __attribute__((unused)))
+{
+  return -ENOSYS;
+}
+#else
+#include <elfutils/debuginfod.h>
+
+static int
+progressfn (debuginfod_client *c,
+             long a __attribute__((unused)),
+             long b __attribute__((unused)))
+{
+  return check_quit_flag ();
+}
+
+static debuginfod_client *
+debuginfod_init ()
+{
+  debuginfod_client *c = debuginfod_begin ();
+
+  if (c != nullptr)
+    debuginfod_set_progressfn (c, progressfn);
+
+  return c;
+}
+
+/* See debuginfod-support.h  */
+
+int
+debuginfod_source_query (const unsigned char *build_id,
+                         int build_id_len,
+                         const char *srcpath,
+                         char **filename)
+{
+  debuginfod_client *c = debuginfod_init ();
+
+  if (c == nullptr)
+    return -ENOMEM;
+
+  int fd = debuginfod_find_source (c,
+                                   build_id,
+                                   build_id_len,
+                                   srcpath,
+                                   filename);
+  debuginfod_end (c);
+
+  return fd;
+}
+
+/* See debuginfod-support.h  */
+
+int
+debuginfod_debuginfo_query (const unsigned char *build_id,
+                            int build_id_len,
+                            char **filename)
+{
+  debuginfod_client *c = debuginfod_init ();
+
+  if (c == nullptr)
+    return -ENOMEM;
+
+  int fd = debuginfod_find_debuginfo (c, build_id, build_id_len, filename);
+  debuginfod_end (c);
+
+  return fd;
+}
+#endif
diff --git a/gdb/debuginfod-support.h b/gdb/debuginfod-support.h
new file mode 100644
index 0000000000..e3fdeb4526
--- /dev/null
+++ b/gdb/debuginfod-support.h
@@ -0,0 +1,43 @@
+/* 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
+   an executable with build_id. src_path should be the source
+   file's absolute path that includes the compilation directory of
+   the CU associated with the source file. If the file is
+   successfully retrieved, its path on the local machine is stored
+   at filename. The caller should free() this value. If GDB is not
+   built with debuginfod, this function returns -ENOSYS.  */
+
+extern int debuginfod_source_query (const unsigned char *build_id,
+                                    int build_id_len,
+                                    const char *src_path,
+                                    char **filename);
+
+/* Query debuginfod servers for a debuginfo file with build_id. If the
+   file is successfully retrieved, its path on the local machine is
+   stored at filename. The caller should free() this value. If GDB
+   is not built with debuginfod, this function returns -ENOSYS.  */
+
+extern int debuginfod_debuginfo_query (const unsigned char *build_id,
+                                       int build_id_len,
+                                       char **filename);
+#endif /* DEBUGINFOD_SUPPORT_H */
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index a2866a2833..9d2922be34 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -37794,6 +37794,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/dwarf2read.c b/gdb/dwarf2read.c
index dafe01d94a..4121789571 100644
--- a/gdb/dwarf2read.c
+++ b/gdb/dwarf2read.c
@@ -77,6 +77,9 @@
 #include "gdbsupport/selftest.h"
 #include "rust-lang.h"
 #include "gdbsupport/pathstuff.h"
+#if HAVE_LIBDEBUGINFOD
+#include "debuginfod-support.h"
+#endif
 
 /* When == 1, print basic high level tracing messages.
    When > 1, be more verbose.
@@ -2746,6 +2749,28 @@ 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 HAVE_LIBDEBUGINFOD
+  if (dwz_bfd == nullptr)
+    {
+      char *alt_filename;
+      scoped_fd fd (debuginfod_debuginfo_query (buildid,
+                                                buildid_len,
+                                                &alt_filename));
+
+      if (fd.get () >= 0)
+        {
+          /* File successfully retrieved from server.  */
+          dwz_bfd = gdb_bfd_open (alt_filename, gnutarget, -1);
+
+          if (dwz_bfd != nullptr
+              && !build_id_verify (dwz_bfd.get (), buildid_len, buildid))
+            dwz_bfd.reset (nullptr);
+
+          xfree (alt_filename);
+        }
+    }
+#endif /* HAVE_LIBDEBUGINFOD */
+
   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 453bca527e..84496258df 100644
--- a/gdb/elfread.c
+++ b/gdb/elfread.c
@@ -49,6 +49,10 @@
 #include "mdebugread.h"
 #include "ctfread.h"
 #include "gdbsupport/gdb_string_view.h"
+#include "gdbsupport/scoped_fd.h"
+#if HAVE_LIBDEBUGINFOD
+#include "debuginfod-support.h"
+#endif
 
 /* Forward declarations.  */
 extern const struct sym_fns elf_sym_fns_gdb_index;
@@ -1316,8 +1320,33 @@ 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
-	  has_dwarf2 = false;
+      else
+        {
+          has_dwarf2 = false;
+
+#if HAVE_LIBDEBUGINFOD
+          const struct bfd_build_id *build_id = build_id_bfd_get (objfile->obfd);
+
+          if (build_id != nullptr)
+            {
+              char *symfile_path;
+              scoped_fd fd (debuginfod_debuginfo_query (build_id->data,
+                                                        build_id->size,
+                                                        &symfile_path));
+
+              if (fd.get () >= 0)
+                {
+                  /* File successfully retrieved from server.  */
+                  gdb_bfd_ref_ptr debug_bfd (symfile_bfd_open (symfile_path));
+
+                  symbol_file_add_separate (debug_bfd.get (), symfile_path,
+                                            symfile_flags, objfile);
+                  xfree (symfile_path);
+                  has_dwarf2 = true;
+                }
+            }
+#endif /* HAVE_LIBDEBUGINFOD */
+        }
     }
 
   /* Read the CTF section only if there is no DWARF info.  */
diff --git a/gdb/source.c b/gdb/source.c
index 327e9c1229..0513ce6655 100644
--- a/gdb/source.c
+++ b/gdb/source.c
@@ -48,6 +48,10 @@
 #include "source-cache.h"
 #include "cli/cli-style.h"
 #include "observable.h"
+#include "build-id.h"
+#ifdef HAVE_LIBDEBUGINFOD
+#include "debuginfod-support.h"
+#endif
 
 #define OPEN_MODE (O_RDONLY | O_BINARY)
 #define FDOPEN_MODE FOPEN_RB
@@ -1153,6 +1157,46 @@ open_source_file (struct symtab *s)
   s->fullname = NULL;
   scoped_fd fd = find_and_open_source (s->filename, SYMTAB_DIRNAME (s),
 				       &fullname);
+
+#if HAVE_LIBDEBUGINFOD
+  if (fd.get () < 0)
+    {
+      if (SYMTAB_COMPUNIT (s) != nullptr)
+        {
+          const objfile *ofp = COMPUNIT_OBJFILE (SYMTAB_COMPUNIT (s));
+
+          std::string srcpath;
+          if (IS_DIR_SEPARATOR (s->filename[0]))
+            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);
+
+          if (build_id != nullptr)
+            {
+              /* Query debuginfod for the source file.  */
+              char *filename;
+              scoped_fd src_fd (debuginfod_source_query (build_id->data,
+                                                         build_id->size,
+                                                         srcpath.c_str (),
+                                                         &filename));
+
+              if (src_fd.get () >= 0)
+                fullname.reset (filename);
+
+              s->fullname = fullname.release ();
+              return src_fd;
+
+            }
+        }
+    }
+#endif /* HAVE_LIBDEBUGINFOD */
+
   s->fullname = fullname.release ();
   return fd;
 }
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 0000000000..a4042ac93a
--- /dev/null
+++ b/gdb/testsuite/gdb.debuginfod/fetch_src_and_symbols.exp
@@ -0,0 +1,232 @@
+# 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
+
+set test "debuginfod"
+
+standard_testfile main.c
+
+load_lib dwarf.exp
+
+if { [which debuginfod] == 0 } {
+    untested "$test (cannot find debuginfod)"
+    return -1
+}
+
+if { [which curl] == 0 } {
+    untested "$test (cannot find curl)"
+    return -1
+}
+
+# skip testing if gdb was not configured with debuginfod
+if { [string first "with-debuginfod" [exec $GDB --configuration]] == -1 } {
+    untested "$test (gdb not configured with debuginfod)"
+    return -1
+}
+
+set cache [file join [standard_output_file {}] ".client_cache"]
+set db [file join [standard_output_file {}] ".debuginfod.db"]
+
+# make sure there isn't an old client cache or db lying around
+file delete -force $cache
+file delete -force $db
+
+# make a copy source file that we can move around
+if { [catch {file copy -force ${srcdir}/${subdir}/${srcfile} \
+              [standard_output_file tmp-${srcfile}]}] != 0 } {
+    error "$test (create temporary file)"
+    return -1
+}
+
+set sourcetmp [standard_output_file tmp-${srcfile}]
+set outputdir [standard_output_file {}]
+
+if { [gdb_compile "$sourcetmp" "$binfile" executable {debug}] != "" } {
+    fail "$test (compile)"
+    return -1
+}
+
+# Find an unused port
+set port [exec sh -c "while true; do PORT=`expr '(' \$RANDOM % 1000 ')' + 9000`; ss -atn | fgrep \":\$PORT\" || break; done; echo \$PORT"]
+
+setenv DEBUGINFOD_URLS ""
+setenv DEBUGINFOD_TIMEOUT 30
+setenv DEBUGINFOD_CACHE_PATH $cache
+
+# test that gdb cannot find source without debuginfod
+gdb_start
+gdb_load $binfile
+gdb_test_no_output "set substitute-path $outputdir /dev/null"
+gdb_test "list" ".*No such file or directory.*"
+gdb_exit
+
+# strip symbols into separate file and move it so gdb cannot find it without debuginfod
+if { [gdb_gnu_strip_debug $binfile ""] != 0 } {
+    fail "$test (strip debuginfo)"
+    return -1
+}
+
+set debugdir [file join [standard_output_file {}] "debug"]
+set debuginfo [file join [standard_output_file {}] "fetch_src_and_symbols.debug"]
+
+file mkdir $debugdir
+file rename -force $debuginfo $debugdir
+
+# test that gdb cannot find symbols without debuginfod
+gdb_start
+gdb_load $binfile
+gdb_test "file" ".*No symbol file.*"
+gdb_exit
+
+# 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 "$test (compile main with altlink)"
+    return -1
+}
+
+if {[gdb_compile ${binfile}_dwz.S ${binfile}_dwz.o object nodebug] != ""} {
+    fail "$test (compile altlink)"
+    return -1
+}
+
+file rename -force ${binfile}_dwz.o $debugdir
+
+# Test that gdb cannot find dwz without debuginfod.
+gdb_start
+gdb_test "file ${binfile}_alt.o" ".*could not find '.gnu_debugaltlink'.*"
+gdb_exit
+
+set debuginfod_pid 0
+
+# Kill the server if we abort early
+proc sigint_handler {} {
+    global debuginfod_pid
+
+    if { $debuginfod_pid != 0 } {
+        catch {exec kill -INT $debuginfod_pid}
+    }
+
+    exit
+}
+
+trap sigint_handler INT
+
+# Start up debuginfod
+set debuginfod_pid [exec debuginfod -d $db -p $port -F $debugdir 2>/dev/null &]
+
+if { !$debuginfod_pid } {
+    fail "$test (server init)"
+    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 "$test (server init timeout)"
+    catch {exec kill -INT $debuginfod_pid}
+    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
+gdb_start
+gdb_load $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_exit
+
+# gdb should now find the debugaltlink file
+gdb_start
+gdb_test "file ${binfile}_alt.o" ".*Reading symbols from ${binfile}_alt.o\.\.\.\[\r\n\]"
+gdb_exit
+
+catch {exec kill -INT $debuginfod_pid}
diff --git a/gdb/testsuite/gdb.debuginfod/main.c b/gdb/testsuite/gdb.debuginfod/main.c
new file mode 100644
index 0000000000..73abaf58b1
--- /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 f702af9acd..1a98ae198c 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\
-- 
2.24.1

