[v5] Add debuginfod support to GDB

Message ID CAJDtP-S3tXgiXOu+rWs2xeNir14UM7D-C4=0VZ6nKXyRg-EKfA@mail.gmail.com
State New
Headers show
Series
  • [v5] Add debuginfod support to GDB
Related show

Commit Message

Aaron Merey Feb. 25, 2020, 10:29 p.m.
After some discussion on #gdb concerning the verbosity
of debuginfod-related output I've revised the patch so that
messages are only printed when a download actually
takes place. Additionally error messages are only printed
when there's an actual error returned by the debuginfod
library. If a file is not found on the server there is no longer
any warning. In a future patch a 'set debug debuginfod 1'
may be added so that additional verbosity can be controlled
by the user.

Aaron

Comments

Simon Marchi Feb. 25, 2020, 11:30 p.m. | #1
On 2020-02-25 5:29 p.m., Aaron Merey wrote:
> After some discussion on #gdb concerning the verbosity

> of debuginfod-related output I've revised the patch so that

> messages are only printed when a download actually

> takes place. Additionally error messages are only printed

> when there's an actual error returned by the debuginfod

> library. If a file is not found on the server there is no longer

> any warning. In a future patch a 'set debug debuginfod 1'

> may be added so that additional verbosity can be controlled

> by the user.

> 

> Aaron


Thanks for doing these changes.  At this point it pretty much looks good
to me, I just noted some nits below.  Tom, do you have any more comments?


> +static int

> +progressfn (debuginfod_client *c, long cur, long total)

> +{

> +  if (check_quit_flag ())

> +    {

> +      printf_unfiltered ("Cancelling download of %ps...\n",

> +			 styled_string (file_name_style.style (), fname));

> +      return 1;

> +    }

> +

> +  if (!has_printed && total != 0)

> +    {

> +      /* Print this message only once.  */

> +      has_printed = true;

> +      printf_unfiltered ("Debuginfod downloading %ps...\n",

> +			 styled_string (file_name_style.style (), fname));


The message is bit misleading, it says:

  Debuginfod downloading /tmp/mybinary...

When downloading debug info, it should probably say:

  Debuginfod downloading separate debug info for /tmp/mybinary...

Or even just:

  Downloading separate debug info for /tmp/mybinary...

unless you really want to have the "Debuginfod" string in there for exposure ;).

I haven't seen how it looks when it downloads source files, I'm not set up for it.
can you give an example?


> diff --git a/gdb/debuginfod-support.h b/gdb/debuginfod-support.h

> new file mode 100644

> index 0000000000..94d4eba99b

> --- /dev/null

> +++ b/gdb/debuginfod-support.h

> @@ -0,0 +1,59 @@

> +/* 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 FILENAME. If GDB is not built with debuginfod, this


s/FILENAME/DESTNAME/

Use two spaces after periods.

Please keep the parameter names in the header and in the source file in sync.

> +   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 debuginfo 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.

> +

> +   If the file is successfully retrieved, its path on the local machine

> +   is stored in FILENAME. If GDB is not built with debuginfod, this


Same here.

Might as well document that FILENAME is used for printing messages to the user,
and not for finding anything.


> @@ -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 the build id fails to verify, this should print a warning.

> +	}

> +    }

> +

>    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..d842d5b573 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;

> +		    }


If the build id fails to verify, this should print a warning.

Simon
Tom Tromey Feb. 26, 2020, 3:53 a.m. | #2
>>>>> "Aaron" == Aaron Merey <amerey@redhat.com> writes:


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

Aaron>         * Makefile.in: Handle optional debuginfod support.
Aaron>         * NEWS: Update.

Thank you again.

Aaron> +    printf_filtered (_("Download failed: %s. Continuing without source file %ps.\n"),

GNU style is two spaces after a period.

Aaron> +    printf_filtered (_("Download failed: %s. Continuing without debug info for %ps.\n"),

Here too.

Aaron> +		     strerror (-fd.get ()),

safe_strerror.  There was another spot that needed this too.

Ok with those nits fixed.  You don't have to re-submit it.

Tom
Aaron Merey Feb. 26, 2020, 7:34 p.m. | #3
On Tue, Feb 25, 2020 at 6:30 PM Simon Marchi <simark@simark.ca> wrote:
> Thanks for doing these changes.  At this point it pretty much looks good

> to me, I just noted some nits below.  Tom, do you have any more comments?


Thanks for reviewing these patches.

> The message is bit misleading, it says:

>

>   Debuginfod downloading /tmp/mybinary...

>

> When downloading debug info, it should probably say:

>

>   Debuginfod downloading separate debug info for /tmp/mybinary...

>

> Or even just:

>

>   Downloading separate debug info for /tmp/mybinary...

>

> unless you really want to have the "Debuginfod" string in there for exposure ;).


Fixed.

> I haven't seen how it looks when it downloads source files, I'm not set up for it.

> can you give an example?


"Downloading source file /a/b/c.xyz..."

> > +   If the file is successfully retrieved, its path on the local machine

> > +   is stored in FILENAME. If GDB is not built with debuginfod, this

>

> s/FILENAME/DESTNAME/

>

> Use two spaces after periods.

>

> Please keep the parameter names in the header and in the source file in sync.

>

> > +   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 debuginfo 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.

> > +

> > +   If the file is successfully retrieved, its path on the local machine

> > +   is stored in FILENAME. If GDB is not built with debuginfod, this

>

> Same here.

>

> Might as well document that FILENAME is used for printing messages to the user,

> and not for finding anything.


Fixed.

> > @@ -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 the build id fails to verify, this should print a warning.

>

> > +     }

> > +    }

> > +

> >    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..d842d5b573 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;

> > +                 }

>

> If the build id fails to verify, this should print a warning.


I didn't print those warnings since build_id_verify already prints it
in case of a build-id absence or mismatch.

On Tue, Feb 25, 2020 at 10:53 PM Tom Tromey <tom@tromey.com> wrote:
>

> >>>>> "Aaron" == Aaron Merey <amerey@redhat.com> writes:

>

> Aaron> gdb/ChangeLog:

> Aaron> 2020-02-25  Aaron Merey  <amerey@redhat.com>

>

> Aaron>         * Makefile.in: Handle optional debuginfod support.

> Aaron>         * NEWS: Update.

>

> Thank you again.


Thanks for reviewing these patches.

> Aaron> +    printf_filtered (_("Download failed: %s. Continuing without source file %ps.\n"),

>

> GNU style is two spaces after a period.

>

> Aaron> +    printf_filtered (_("Download failed: %s. Continuing without debug info for %ps.\n"),

>

> Here too.

>

> Aaron> +                     strerror (-fd.get ()),

>

> safe_strerror.  There was another spot that needed this too.

>

> Ok with those nits fixed.  You don't have to re-submit it.


I've re-attached the patch with these changes since I wanted to also add
what Simon suggested.

Aaron
From cefa26b5a10a91e617982d9b8c1c3ba8e5b61ebb Mon Sep 17 00:00:00 2001
From: Aaron Merey <amerey@redhat.com>
Date: Wed, 26 Feb 2020 14:03:25 -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-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.

gdb/testsuite/ChangeLog:
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.
---
 gdb/Makefile.in                               |   4 +-
 gdb/NEWS                                      |  14 ++
 gdb/README                                    |   9 +
 gdb/config.in                                 |   3 +
 gdb/configure                                 | 182 +++++++++++----
 gdb/configure.ac                              |   6 +-
 gdb/debuginfod-support.c                      | 155 +++++++++++++
 gdb/debuginfod-support.h                      |  62 +++++
 gdb/doc/gdb.texinfo                           |   8 +
 gdb/dwarf2/read.c                             |  24 ++
 gdb/elfread.c                                 |  32 ++-
 gdb/source.c                                  |  30 +++
 .../gdb.debuginfod/fetch_src_and_symbols.exp  | 214 ++++++++++++++++++
 gdb/testsuite/gdb.debuginfod/main.c           |  25 ++
 gdb/top.c                                     |  10 +
 15 files changed, 728 insertions(+), 50 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 f9606b8fc7..7c0a0aefbc 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 \
diff --git a/gdb/NEWS b/gdb/NEWS
index e33d838dd1..d3a3605a58 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.
+
 * 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 3895758ece..0ec1605ce5 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 9c5c1ce834..7a34b85e57 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 e6b5a510f6..f99cbe40f1 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 a51c5eda6b..1cba1e832b 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 0000000000..e0f0fac076
--- /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 0000000000..676c217b64
--- /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 f1798e35b5..b947c5dfb4 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 6849644748..d6b34d8586 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 453bca527e..d842d5b573 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 4f889e4b95..051caf5c57 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/gdb.debuginfod/fetch_src_and_symbols.exp b/gdb/testsuite/gdb.debuginfod/fetch_src_and_symbols.exp
new file mode 100644
index 0000000000..0bf18f2d12
--- /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 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\
Simon Marchi Feb. 26, 2020, 9:52 p.m. | #4
On 2020-02-26 2:34 p.m., Aaron Merey wrote:
>> I haven't seen how it looks when it downloads source files, I'm not set up for it.

>> can you give an example?

> 

> "Downloading source file /a/b/c.xyz..."


Ok.

>> If the build id fails to verify, this should print a warning.

> 

> I didn't print those warnings since build_id_verify already prints it

> in case of a build-id absence or mismatch.


Ack.

>> safe_strerror.  There was another spot that needed this too.

>>

>> Ok with those nits fixed.  You don't have to re-submit it.

> 

> I've re-attached the patch with these changes since I wanted to also add

> what Simon suggested.


Thanks, this is ready to push.  Do you have push access?  Or maybe Frank can
push it for you?

Simon
Aaron Merey Feb. 26, 2020, 10:47 p.m. | #5
On Wed, Feb 26, 2020 at 4:52 PM Simon Marchi <simark@simark.ca> wrote:
> Thanks, this is ready to push.  Do you have push access?  Or maybe Frank can

> push it for you?


Great, I've pushed to master.

Aaron
Tom de Vries Feb. 27, 2020, 10:23 a.m. | #6
On 26-02-2020 23:47, Aaron Merey wrote:
> On Wed, Feb 26, 2020 at 4:52 PM Simon Marchi <simark@simark.ca> wrote:

>> Thanks, this is ready to push.  Do you have push access?  Or maybe Frank can

>> push it for you?

> 

> Great, I've pushed to master.

> 


This caused the following regressions:
...
Running
/data/gdb_versions/devel/src/gdb/testsuite/gdb.dwarf2/dw2-compdir-oldgcc.exp
...
ERROR: GDB process no longer exists
ERROR: Couldn't send info source to GDB.
Running
/data/gdb_versions/devel/src/gdb/testsuite/gdb.python/py-framefilter-invalidarg.exp
...
ERROR: couldn't run to breakpoint
Running
/data/gdb_versions/devel/src/gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp ...
ERROR: GDB process no longer exists
...

Looking at the dw2-ranges-base one in more detail:
...
$ gdb
build/gdb/testsuite/outputs/gdb.dwarf2/dw2-ranges-base/dw2-ranges-base
-ex start -ex "maint info line-table" -batch
...

We have the backtrace:
...
(gdb) bt
#0  0x00007ffff50842c1 in __strlen_avx2 () from /lib64/libc.so.6
#1  0x00007ffff5638755 in std::__cxx11::basic_string<char,
std::char_traits<char>, std::allocator<char> >::operator=(char const*)
() from /usr/lib64/libstdc++.so.6
#2  0x0000000000880ae7 in open_source_file (s=0x1a14880)
    at /data/gdb_versions/devel/src/gdb/source.c:1165
#3  0x0000000000880c4f in symtab_to_fullname (s=0x1a14880)
    at /data/gdb_versions/devel/src/gdb/source.c:1202
#4  0x00000000008b3fdd in maintenance_print_one_line_table
(symtab=0x1a14880, data=0x0)
    at /data/gdb_versions/devel/src/gdb/symmisc.c:977
...

The problem seems to be here:
...
(gdb)
#2  0x0000000000880ae7 in open_source_file (s=0x1a14880)
    at /data/gdb_versions/devel/src/gdb/source.c:1165
1165                  srcpath = SYMTAB_DIRNAME (s);
(gdb) p s->compunit_symtab->dirname
$3 = 0x0
...

Thanks,
- Tom
Aaron Merey Feb. 27, 2020, 5:58 p.m. | #7
On Thu, Feb 27, 2020 at 5:23 AM Tom de Vries <tdevries@suse.de> wrote:
> The problem seems to be here:

> ...

> (gdb)

> #2  0x0000000000880ae7 in open_source_file (s=0x1a14880)

>     at /data/gdb_versions/devel/src/gdb/source.c:1165

> 1165                  srcpath = SYMTAB_DIRNAME (s);

> (gdb) p s->compunit_symtab->dirname

> $3 = 0x0


Thanks Tom, I'll post a patch to fix this.

Aaron

Patch

From 3cbb00f23aaad75e88d43d125246c46b20587383 Mon Sep 17 00:00:00 2001
From: Aaron Merey <amerey@redhat.com>
Date: Tue, 25 Feb 2020 17:05:19 -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-25  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.

gdb/testsuite/ChangeLog:
2020-02-25  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                               |   4 +-
 gdb/NEWS                                      |  14 ++
 gdb/README                                    |   9 +
 gdb/config.in                                 |   3 +
 gdb/configure                                 | 182 +++++++++++----
 gdb/configure.ac                              |   6 +-
 gdb/debuginfod-support.c                      | 150 ++++++++++++
 gdb/debuginfod-support.h                      |  59 +++++
 gdb/doc/gdb.texinfo                           |   8 +
 gdb/dwarf2/read.c                             |  24 ++
 gdb/elfread.c                                 |  32 ++-
 gdb/source.c                                  |  30 +++
 .../gdb.debuginfod/fetch_src_and_symbols.exp  | 214 ++++++++++++++++++
 gdb/testsuite/gdb.debuginfod/main.c           |  25 ++
 gdb/top.c                                     |  10 +
 15 files changed, 720 insertions(+), 50 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 f9606b8fc7..7c0a0aefbc 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 \
diff --git a/gdb/NEWS b/gdb/NEWS
index e33d838dd1..d3a3605a58 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.
+
 * 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 3895758ece..0ec1605ce5 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 9c5c1ce834..7a34b85e57 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 e6b5a510f6..f99cbe40f1 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 a51c5eda6b..1cba1e832b 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 0000000000..53e3a50d31
--- /dev/null
+++ b/gdb/debuginfod-support.c
@@ -0,0 +1,150 @@ 
+/* 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 const char *fname;
+static bool has_printed;
+
+static int
+progressfn (debuginfod_client *c, long cur, long total)
+{
+  if (check_quit_flag ())
+    {
+      printf_unfiltered ("Cancelling download of %ps...\n",
+			 styled_string (file_name_style.style (), fname));
+      return 1;
+    }
+
+  if (!has_printed && total != 0)
+    {
+      /* Print this message only once.  */
+      has_printed = true;
+      printf_unfiltered ("Debuginfod downloading %ps...\n",
+			 styled_string (file_name_style.style (), fname));
+    }
+
+  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);
+
+  fname = 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"),
+		     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);
+
+  fname = 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"),
+		     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 0000000000..94d4eba99b
--- /dev/null
+++ b/gdb/debuginfod-support.h
@@ -0,0 +1,59 @@ 
+/* 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 FILENAME. 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 debuginfo 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.
+
+   If the file is successfully retrieved, its path on the local machine
+   is stored in FILENAME. 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 f1798e35b5..b947c5dfb4 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 8c40ddb727..6885834220 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 453bca527e..d842d5b573 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 4f889e4b95..051caf5c57 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/gdb.debuginfod/fetch_src_and_symbols.exp b/gdb/testsuite/gdb.debuginfod/fetch_src_and_symbols.exp
new file mode 100644
index 0000000000..0bf18f2d12
--- /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 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