[RFC] Add debuginfod support to objdump and readelf

Message ID CAJDtP-SK6U5ji+TKsAW68p-uWVgGwTm_wc7LSmJNByN+P97acA@mail.gmail.com
State New
Headers show
Series
  • [RFC] Add debuginfod support to objdump and readelf
Related show

Commit Message

Aaron Merey Nov. 22, 2019, 11:21 p.m.
debuginfod is a lightweight web service that indexes debugging
resources such as separate debug files by build-id and serves
them over HTTP. This patch adds debuginfod support to objdump and
readelf allowing them to query debuginfod servers when they are
otherwise not able to find dwz and separate debug files, if enabled
to do so. For more information regarding debuginfod see
https://developers.redhat.com/blog/2019/10/14/introducing-debuginfod
-the-elfutils-debuginfo-server/

Some questions regarding this patch:

Since build-ids are needed to query debuginfod servers, I've added
get_build_id functions to objdump and readelf. These are used to get
the build-id of the debuglink file (build-ids for debugaltlink files
are handled separately). Objdump's get_build_id simply grabs the
build-id from the bfd handle. Since there does not appear to be a
straightforward way to get a build-id from Filedata, readelf's
get_build_id iterates through the file's ELF notes searching for
NT_GNU_BUILD_ID. The code borrows a lot from process_notes_at,
including separate handling of IA-64 files. Should this separate
handling for IA-64 remain or does it just result in clutter?

One feature of the debuginfod client library is the ability to attach
a user-defined progress update function to a query that prints
information during a download such as the size of the file being
downloaded or the completion percentage of the download. This patch
does not make use of this feature. Should it be added? It may be
helpful for users if long/slow downloads are accompanied by regular
progress updates. It may be worth pointing out that during downloads
objdump and readelf will still respond to ctrl+c.

Any other comments or suggestions are appreciated.

Aaron Merey

ChangeLog:

        * objdump.c (get_build_id): New function. Get build-id of file.
        * readelf.c (get_build_id): Likewise.
        * dwarf.h (get_build_id): Add declaration.
        * dwarf.c (debuginfod_fetch_separate_debug_info): New function.
          Query debuginfod servers for the target debuglink or
          debugaltlink file.
          (load_separate_debug_info): Call
          debuginfo_fetch_separate_debug_info if debuginfod support
          is enabled. If file cannot be found print the URLs that
          were queried.
          (load_separate_debug_files): Add file to the
          load_separate_debuginfo calls.
        * configure.ac: Add '--with-debuginfod' option and check
          for debuginfod library and header file.
        * Makefile.am: Add libdebuginfod to readelf_LDADD and
          objdump_LDADD if enabled.
        * Makefile.in: Regenerate.
        * configure: Likewise.
        * config.in: Likewise.

Comments

Nick Clifton Nov. 25, 2019, 12:30 p.m. | #1
Hi Aaron,

> Since there does not appear to be a

> straightforward way to get a build-id from Filedata, readelf's

> get_build_id iterates through the file's ELF notes searching for

> NT_GNU_BUILD_ID. The code borrows a lot from process_notes_at,

> including separate handling of IA-64 files. Should this separate

> handling for IA-64 remain or does it just result in clutter?


Yes.  Best to be safe in this area I think.

> One feature of the debuginfod client library is the ability to attach

> a user-defined progress update function to a query that prints

> information during a download such as the size of the file being

> downloaded or the completion percentage of the download. This patch

> does not make use of this feature. Should it be added?  


As an extra option yes.  I think that the default should be to not
display anything though.

> Any other comments or suggestions are appreciated.


It would helpful if you could include updates to the binutils/NEWS
and binutils/doc/binutils.texi files as well.  It would also be
really nice if you could create some new entries in the binutils 
testsuite to check the feature, but I can see how this might be
rather hard to do.

I am slightly worried however about adding a new dependency to the
binutils, albeit one that is only needed if enabled via a configuration
option.  Is the database querying code in libdebuginfod complex ?
Maybe it could be separated out into a separate library (ie not part of
elfutils) or even imported directly into the binutils sources.
(This probably is a bad idea, but I just wanted to check).

The configure check does not appear to work correctly.  I tried the
following:

  % mkdir /dev/shm/test
  % cd /edv/shm/test
  % /work/sources/binutils/current/configure --quiet --with-debuginfod --enable-gold
  % make all-binutils
  mkdir -p -- ./intl
  Configuring in ./intl
  mkdir -p -- ./binutils
  Configuring in ./binutils
  configure: error: debuginfod is missing or unusable
  make: *** [Makefile:3566: configure-binutils] Error 1

So the configure succeeded, but the build failed.  (This was on an 
x86_64 box running Fedora 29).

Cheers
  Nick
Tom Tromey Nov. 26, 2019, 1:49 p.m. | #2
>>>>> "Aaron" == Aaron Merey <amerey@redhat.com> writes:


Aaron> +# Enable debuginfod
Aaron> +AC_ARG_WITH([debuginfod],
Aaron> +        AC_HELP_STRING([--with-debuginfod],
Aaron> +                       [Enable debuginfo lookups with debuginfod (auto/yes/no)]),
Aaron> +        [], [with_debuginfod=auto])
Aaron> +AC_MSG_CHECKING([whether to use debuginfod])
Aaron> +AC_MSG_RESULT([$with_debuginfod])
Aaron> +
Aaron> +if test "${with_debuginfod}" = no; then
Aaron> +  AC_MSG_WARN([debuginfod support disabled; some features may be unavailable.])
Aaron> +else
Aaron> +  AC_CHECK_LIB([debuginfod], [debuginfod_begin], [have_debuginfod_lib=yes])
Aaron> +  AC_CHECK_DECL([debuginfod_begin], [have_debuginfod_h=yes], [],
Aaron> +                [#include <elfutils/debuginfod.h>])
Aaron> +  if test "x$have_debuginfod_lib" = "xyes" -a \
Aaron> +          "x$have_debuginfod_h" = "xyes"; then
Aaron> +    AC_DEFINE([HAVE_LIBDEBUGINFOD], [1],
Aaron> +              [Define to 1 if debuginfod is enabled.])
Aaron> +    AC_SUBST([LIBDEBUGINFOD], ["-ldebuginfod"])
Aaron> +  else
Aaron> +    AC_SUBST([LIBDEBUGINFOD], [])
Aaron> +    if test "$with_debuginfod" = yes; then
Aaron> +      AC_MSG_ERROR([debuginfod is missing or unusable])
Aaron> +    else
Aaron> +      AC_MSG_WARN([debuginfod is missing or unusable; some features may be unavailable.])
Aaron> +    fi
Aaron> +  fi
Aaron> +fi

How does this relate to the gdb patch?  The two patches seem to do
similar things but use different configure options and different names.
Maybe I'm looking at an outdated version of the gdb patch?

Anyway, how about shipping a pkg-config file for the library so that
some of this configury can be removed?  Also, if the code is shared, how
about either putting it into BFD for reuse, or at least putting the
configury into a new .m4 file in config/ so that this can be reused?

thanks,
Tom
Aaron Merey Nov. 26, 2019, 3:31 p.m. | #3
On Mon, Nov 25, 2019 at 7:32 AM Nick Clifton <nickc@redhat.com> wrote:
> It would helpful if you could include updates to the binutils/NEWS

> and binutils/doc/binutils.texi files as well.  It would also be

> really nice if you could create some new entries in the binutils

> testsuite to check the feature, but I can see how this might be

> rather hard to do.


Will do.

> I am slightly worried however about adding a new dependency to the

> binutils, albeit one that is only needed if enabled via a configuration

> option.  Is the database querying code in libdebuginfod complex ?

> Maybe it could be separated out into a separate library (ie not part of

> elfutils) or even imported directly into the binutils sources.

> (This probably is a bad idea, but I just wanted to check).


The debuginfod shared library used in this patch is distributed with elfutils
but it does not depend on any elfutils libraries and can be installed
separately.
This shlib simply issues HTTP GET requests to debuginfod servers using libcurl,
stores downloaded files in a local cache and makes them available to the
caller of the shlib functions. The database querying code is contained entirely
within the server, which is separate from the client shlib used in
this patch. So
I think that importing the server/database code into binutils would increase the
number of new dependencies and greatly increase the complexity of the code
being added to binutils.

> The configure check does not appear to work correctly.  I tried the

> following:

>

>   % mkdir /dev/shm/test

>   % cd /edv/shm/test

>   % /work/sources/binutils/current/configure --quiet --with-debuginfod --enable-gold

>   % make all-binutils

>   mkdir -p -- ./intl

>   Configuring in ./intl

>   mkdir -p -- ./binutils

>   Configuring in ./binutils

>   configure: error: debuginfod is missing or unusable

>   make: *** [Makefile:3566: configure-binutils] Error 1

>

> So the configure succeeded, but the build failed.  (This was on an

> x86_64 box running Fedora 29).


Ah so the check for the shlib and header should occur in
binutils-gdb/configure.ac, not binutils-gdb/binutils/configure.ac. That
should cause configure to fail properly. I will fix this in the next patch.

Aaron
Aaron Merey Nov. 26, 2019, 3:45 p.m. | #4
On Tue, Nov 26, 2019 at 8:49 AM Tom Tromey <tom@tromey.com> wrote:
>

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

>

> Aaron> +# Enable debuginfod

> Aaron> +AC_ARG_WITH([debuginfod],

> Aaron> +        AC_HELP_STRING([--with-debuginfod],

> Aaron> +                       [Enable debuginfo lookups with debuginfod (auto/yes/no)]),

> Aaron> +        [], [with_debuginfod=auto])

> Aaron> +AC_MSG_CHECKING([whether to use debuginfod])

> Aaron> +AC_MSG_RESULT([$with_debuginfod])

> Aaron> +

> Aaron> +if test "${with_debuginfod}" = no; then

> Aaron> +  AC_MSG_WARN([debuginfod support disabled; some features may be unavailable.])

> Aaron> +else

> Aaron> +  AC_CHECK_LIB([debuginfod], [debuginfod_begin], [have_debuginfod_lib=yes])

> Aaron> +  AC_CHECK_DECL([debuginfod_begin], [have_debuginfod_h=yes], [],

> Aaron> +                [#include <elfutils/debuginfod.h>])

> Aaron> +  if test "x$have_debuginfod_lib" = "xyes" -a \

> Aaron> +          "x$have_debuginfod_h" = "xyes"; then

> Aaron> +    AC_DEFINE([HAVE_LIBDEBUGINFOD], [1],

> Aaron> +              [Define to 1 if debuginfod is enabled.])

> Aaron> +    AC_SUBST([LIBDEBUGINFOD], ["-ldebuginfod"])

> Aaron> +  else

> Aaron> +    AC_SUBST([LIBDEBUGINFOD], [])

> Aaron> +    if test "$with_debuginfod" = yes; then

> Aaron> +      AC_MSG_ERROR([debuginfod is missing or unusable])

> Aaron> +    else

> Aaron> +      AC_MSG_WARN([debuginfod is missing or unusable; some features may be unavailable.])

> Aaron> +    fi

> Aaron> +  fi

> Aaron> +fi

>

> How does this relate to the gdb patch?  The two patches seem to do

> similar things but use different configure options and different names.

> Maybe I'm looking at an outdated version of the gdb patch?

>

> Anyway, how about shipping a pkg-config file for the library so that

> some of this configury can be removed?  Also, if the code is shared, how

> about either putting it into BFD for reuse, or at least putting the

> configury into a new .m4 file in config/ so that this can be reused?


The patch I posted to gdb-patches@ is a bit outdated and we have
renamed dbgserver to debuginfod. This patch allows objdump and readelf
to query debuginfod servers for dwz and separate debug files much like
the gdb patch (dwz lookups have since been added to gdb in our
experimental branch users/fche/dbgserver, the branch name has not been
updated since the name change). The debuginfod configury for binutils
and gdb should definitely be consolidated, I will make these
changes to the revised patch.

Aaron

Patch

diff --git a/binutils/Makefile.am b/binutils/Makefile.am
index 4c90a0bef3..2e13b774b6 100644
--- a/binutils/Makefile.am
+++ b/binutils/Makefile.am
@@ -53,6 +53,8 @@  AM_CFLAGS = $(WARN_CFLAGS) $(ZLIBINC)
 AM_CFLAGS_FOR_BUILD = $(WARN_CFLAGS_FOR_BUILD) $(ZLIBINC)
 LIBICONV = @LIBICONV@
 
+LIBDEBUGINFOD = @LIBDEBUGINFOD@
+
 # these two are almost the same program
 AR_PROG=ar
 RANLIB_PROG=ranlib
@@ -245,7 +247,7 @@  objcopy_SOURCES = objcopy.c not-strip.c rename.c $(WRITE_DEBUG_SRCS) $(BULIBS)
 strings_SOURCES = strings.c $(BULIBS)
 
 readelf_SOURCES = readelf.c version.c unwind-ia64.c dwarf.c $(ELFLIBS)
-readelf_LDADD   = $(LIBINTL) $(LIBCTF_NOBFD) $(LIBIBERTY) $(ZLIB)
+readelf_LDADD   = $(LIBINTL) $(LIBCTF_NOBFD) $(LIBIBERTY) $(ZLIB) $(LIBDEBUGINFOD)
 
 elfedit_SOURCES = elfedit.c version.c $(ELFLIBS)
 elfedit_LDADD = $(LIBINTL) $(LIBIBERTY)
@@ -256,7 +258,7 @@  nm_new_SOURCES = nm.c $(BULIBS)
 
 objdump_SOURCES = objdump.c dwarf.c prdbg.c $(DEBUG_SRCS) $(BULIBS) $(ELFLIBS)
 EXTRA_objdump_SOURCES = od-xcoff.c
-objdump_LDADD = $(OBJDUMP_PRIVATE_OFILES) $(OPCODES) $(LIBCTF) $(BFDLIB) $(LIBIBERTY) $(LIBINTL)
+objdump_LDADD = $(OBJDUMP_PRIVATE_OFILES) $(OPCODES) $(LIBCTF) $(BFDLIB) $(LIBIBERTY) $(LIBINTL) $(LIBDEBUGINFOD)
 
 objdump.@OBJEXT@:objdump.c
 if am__fastdepCC
diff --git a/binutils/Makefile.in b/binutils/Makefile.in
index be96d2db0e..0ae82ae657 100644
--- a/binutils/Makefile.in
+++ b/binutils/Makefile.in
@@ -449,6 +449,7 @@  LDFLAGS = @LDFLAGS@
 LEX = `if [ -f ../flex/flex ]; then echo ../flex/flex; else echo @LEX@; fi`
 LEXLIB = @LEXLIB@
 LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@
+LIBDEBUGINFOD = @LIBDEBUGINFOD@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBINTL_DEP = @LIBINTL_DEP@
@@ -706,14 +707,14 @@  size_SOURCES = size.c $(BULIBS)
 objcopy_SOURCES = objcopy.c not-strip.c rename.c $(WRITE_DEBUG_SRCS) $(BULIBS)
 strings_SOURCES = strings.c $(BULIBS)
 readelf_SOURCES = readelf.c version.c unwind-ia64.c dwarf.c $(ELFLIBS)
-readelf_LDADD = $(LIBINTL) $(LIBCTF_NOBFD) $(LIBIBERTY) $(ZLIB)
+readelf_LDADD = $(LIBINTL) $(LIBCTF_NOBFD) $(LIBIBERTY) $(ZLIB) $(LIBDEBUGINFOD)
 elfedit_SOURCES = elfedit.c version.c $(ELFLIBS)
 elfedit_LDADD = $(LIBINTL) $(LIBIBERTY)
 strip_new_SOURCES = objcopy.c is-strip.c rename.c $(WRITE_DEBUG_SRCS) $(BULIBS)
 nm_new_SOURCES = nm.c $(BULIBS)
 objdump_SOURCES = objdump.c dwarf.c prdbg.c $(DEBUG_SRCS) $(BULIBS) $(ELFLIBS)
 EXTRA_objdump_SOURCES = od-xcoff.c
-objdump_LDADD = $(OBJDUMP_PRIVATE_OFILES) $(OPCODES) $(LIBCTF) $(BFDLIB) $(LIBIBERTY) $(LIBINTL)
+objdump_LDADD = $(OBJDUMP_PRIVATE_OFILES) $(OPCODES) $(LIBCTF) $(BFDLIB) $(LIBIBERTY) $(LIBINTL) $(LIBDEBUGINFOD)
 cxxfilt_SOURCES = cxxfilt.c $(BULIBS)
 ar_SOURCES = arparse.y arlex.l ar.c not-ranlib.c arsup.c rename.c binemul.c \
 	emul_$(EMULATION).c $(BULIBS)
diff --git a/binutils/config.in b/binutils/config.in
index 72ead4eb61..703f7b15e7 100644
--- a/binutils/config.in
+++ b/binutils/config.in
@@ -109,6 +109,9 @@ 
 /* Define if your <locale.h> file defines LC_MESSAGES. */
 #undef HAVE_LC_MESSAGES
 
+/* Define to 1 if debuginfod is enabled. */
+#undef HAVE_LIBDEBUGINFOD
+
 /* Define to 1 if you have the <limits.h> header file. */
 #undef HAVE_LIMITS_H
 
diff --git a/binutils/configure b/binutils/configure
index 50f8d5b1a2..ac11c8976f 100755
--- a/binutils/configure
+++ b/binutils/configure
@@ -684,6 +684,7 @@  WARN_WRITE_STRINGS
 NO_WERROR
 WARN_CFLAGS_FOR_BUILD
 WARN_CFLAGS
+LIBDEBUGINFOD
 OTOOL64
 OTOOL
 LIPO
@@ -813,6 +814,7 @@  enable_largefile
 enable_targets
 enable_deterministic_archives
 enable_default_strings_all
+with_debuginfod
 enable_werror
 enable_build_warnings
 enable_nls
@@ -1483,6 +1485,8 @@  Optional Packages:
   --with-pic              try to use only PIC/non-PIC objects [default=use
                           both]
   --with-gnu-ld           assume the C compiler uses GNU ld [default=no]
+  --with-debuginfod       Enable debuginfo lookups with debuginfod
+                          (auto/yes/no)
   --with-system-zlib      use installed libz
   --with-gnu-ld           assume the C compiler uses GNU ld default=no
   --with-libiconv-prefix[=DIR]  search for libiconv in DIR/include and DIR/lib
@@ -1932,6 +1936,52 @@  $as_echo "$ac_res" >&6; }
 
 } # ac_fn_c_check_func
 
+# 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_compute_int LINENO EXPR VAR INCLUDES
 # --------------------------------------------
 # Tries to find the compile-time value of EXPR in a program that includes
@@ -2168,52 +2218,6 @@  $as_echo "$ac_res" >&6; }
   eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
 
 } # 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
 cat >config.log <<_ACEOF
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
@@ -11523,7 +11527,7 @@  else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 11526 "configure"
+#line 11530 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -11629,7 +11633,7 @@  else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 11632 "configure"
+#line 11636 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -12235,6 +12239,88 @@  else
 fi
 
 
+# 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
+
 
 cat >>confdefs.h <<_ACEOF
 #define DEFAULT_STRINGS_ALL $default_strings_all
diff --git a/binutils/configure.ac b/binutils/configure.ac
index b1cd4552f2..b2ab38322b 100644
--- a/binutils/configure.ac
+++ b/binutils/configure.ac
@@ -63,6 +63,35 @@  else
   default_strings_all=1
 fi], [default_strings_all=1])
 
+# Enable debuginfod
+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 "${with_debuginfod}" = no; then
+  AC_MSG_WARN([debuginfod support disabled; some features may be unavailable.])
+else
+  AC_CHECK_LIB([debuginfod], [debuginfod_begin], [have_debuginfod_lib=yes])
+  AC_CHECK_DECL([debuginfod_begin], [have_debuginfod_h=yes], [],
+                [#include <elfutils/debuginfod.h>])
+  if test "x$have_debuginfod_lib" = "xyes" -a \
+          "x$have_debuginfod_h" = "xyes"; then
+    AC_DEFINE([HAVE_LIBDEBUGINFOD], [1],
+              [Define to 1 if debuginfod is enabled.])
+    AC_SUBST([LIBDEBUGINFOD], ["-ldebuginfod"])
+  else
+    AC_SUBST([LIBDEBUGINFOD], [])
+    if test "$with_debuginfod" = yes; then
+      AC_MSG_ERROR([debuginfod is missing or unusable])
+    else
+      AC_MSG_WARN([debuginfod is missing or unusable; some features may be unavailable.])
+    fi
+  fi
+fi
+
 AC_DEFINE_UNQUOTED(DEFAULT_STRINGS_ALL, $default_strings_all,
 		   [Should strings use -a behavior by default?])
 
diff --git a/binutils/dwarf.c b/binutils/dwarf.c
index 2fe469f060..18fabf78b3 100644
--- a/binutils/dwarf.c
+++ b/binutils/dwarf.c
@@ -32,6 +32,10 @@ 
 #include "safe-ctype.h"
 #include <assert.h>
 
+#ifdef HAVE_LIBDEBUGINFOD
+#include <elfutils/debuginfod.h>
+#endif
+
 #undef MAX
 #undef MIN
 #define MAX(a, b) ((a) > (b) ? (a) : (b))
@@ -10267,12 +10271,83 @@  add_separate_debug_file (const char * filename, void * handle)
   first_separate_info = i;
 }
 
+#if HAVE_LIBDEBUGINFOD
+/* Query debuginfod servers for the target debuglink or debugaltlink
+   file. If successful, store the path of the file in filename and
+   return TRUE, otherwise return FALSE.  */
+
+static bfd_boolean
+debuginfod_fetch_separate_debug_info (struct dwarf_section * section,
+                                      char ** filename,
+                                      void * file)
+{
+  size_t build_id_len;
+  unsigned char * build_id;
+
+  if (strcmp (section->uncompressed_name, ".gnu_debuglink") == 0)
+    {
+      /* Get the build-id of file.  */
+      build_id = get_build_id (file);
+      build_id_len = 0;
+    }
+  else if (strcmp (section->uncompressed_name, ".gnu_debugaltlink") == 0)
+    {
+      /* Get the build-id of the debugaltlink file.  */
+      unsigned int filelen;
+
+      filelen = strnlen ((const char *)section->start, section->size);
+      if (filelen == section->size)
+        /* Corrupt debugaltlink.  */
+        return FALSE;
+
+      build_id = section->start + filelen + 1;
+      build_id_len = section->size - (filelen + 1);
+
+      if (build_id_len == 0)
+        return FALSE;
+    }
+  else
+    return FALSE;
+
+  if (build_id)
+    {
+      int fd;
+      debuginfod_client * client;
+
+      client = debuginfod_begin();
+      if (client == NULL)
+        return FALSE;
+
+      /* Query debuginfod servers for the target file. If found its path
+         will be stored in filename.  */
+      fd = debuginfod_find_debuginfo (client, build_id, build_id_len, filename);
+      debuginfod_end (client);
+
+      /* Only free build_id if we allocated space for a hex string
+         in get_build_id().  */
+      if (build_id_len == 0)
+        free (build_id);
+
+      if (fd >= 0)
+        {
+          /* File successfully retrieved. Close fd since we want to
+             use open_debug_file() on filename instead.  */
+          close (fd);
+          return TRUE;
+        }
+    }
+
+  return FALSE;
+}
+#endif
+
 static void *
 load_separate_debug_info (const char *            main_filename,
 			  struct dwarf_section *  xlink,
 			  parse_func_type         parse_func,
 			  check_func_type         check_func,
-			  void *                  func_data)
+			  void *                  func_data,
+			  void *                  file ATTRIBUTE_UNUSED)
 {
   const char *   separate_filename;
   char *         debug_filename;
@@ -10374,6 +10449,23 @@  load_separate_debug_info (const char *            main_filename,
   if (check_func (debug_filename, func_data))
     goto found;
 
+#if HAVE_LIBDEBUGINFOD
+  {
+    char * tmp_filename;
+
+    if (debuginfod_fetch_separate_debug_info (xlink,
+                                              & tmp_filename,
+                                              file))
+      {
+        /* File successfully downloaded from server, replace
+           debug_filename with the file's path.  */
+        free (debug_filename);
+        debug_filename = tmp_filename;
+        goto found;
+      }
+  }
+#endif
+
   /* Failed to find the file.  */
   warn (_("could not find separate debug file '%s'\n"), separate_filename);
   warn (_("tried: %s\n"), debug_filename);
@@ -10403,6 +10495,16 @@  load_separate_debug_info (const char *            main_filename,
   sprintf (debug_filename, "%s", separate_filename);
   warn (_("tried: %s\n"), debug_filename);
 
+#if HAVE_LIBDEBUGINFOD
+  {
+    char *urls = getenv(DEBUGINFOD_URLS_ENV_VAR);
+    if (urls == NULL)
+      urls = "";
+
+    warn (_("tried: DEBUGINFOD_URLS=%s\n"), urls);
+  }
+#endif
+
   free (canon_dir);
   free (debug_filename);
   return NULL;
@@ -10549,7 +10651,8 @@  load_separate_debug_files (void * file, const char * filename)
 				& debug_displays[gnu_debugaltlink].section,
 				parse_gnu_debugaltlink,
 				check_gnu_debugaltlink,
-				& build_id_data);
+				& build_id_data,
+				file);
     }
 
   if (load_debug_section (gnu_debuglink, file))
@@ -10560,7 +10663,8 @@  load_separate_debug_files (void * file, const char * filename)
 				& debug_displays[gnu_debuglink].section,
 				parse_gnu_debuglink,
 				check_gnu_debuglink,
-				& crc32);
+				& crc32,
+				file);
     }
 
   if (first_separate_info != NULL)
diff --git a/binutils/dwarf.h b/binutils/dwarf.h
index ca2f062efb..106263e31b 100644
--- a/binutils/dwarf.h
+++ b/binutils/dwarf.h
@@ -258,3 +258,7 @@  extern dwarf_vma read_leb128 (unsigned char *, unsigned int *, bfd_boolean, cons
    relocation against the given debug section at the given
    offset.  */
 extern bfd_boolean reloc_at (struct dwarf_section *, dwarf_vma);
+
+#if HAVE_LIBDEBUGINFOD
+extern unsigned char * get_build_id (void *);
+#endif
diff --git a/binutils/objdump.c b/binutils/objdump.c
index 1be3b23235..21209f3d5c 100644
--- a/binutils/objdump.c
+++ b/binutils/objdump.c
@@ -2922,6 +2922,33 @@  open_debug_file (const char * pathname)
   return data;
 }
 
+#if HAVE_LIBDEBUGINFOD
+/* Return a hex string represention of the build-id.  */
+
+unsigned char *
+get_build_id (void * data)
+{
+  unsigned i;
+  char * build_id_str;
+  bfd * abfd = (bfd *) data;
+  const struct bfd_build_id * build_id;
+
+  build_id = abfd->build_id;
+  if (build_id == NULL)
+    return NULL;
+
+  build_id_str = malloc(build_id->size * 2 + 1);
+  if (build_id_str == NULL)
+    return NULL;
+
+  for (i = 0; i < build_id->size; i++)
+    sprintf(build_id_str + (i * 2), "%02x", build_id->data[i]);
+  build_id_str[build_id->size * 2] = '\0';
+
+  return (unsigned char *)build_id_str;
+}
+#endif /* HAVE_LIBDEBUGINFOD */
+
 static void
 dump_dwarf_section (bfd *abfd, asection *section,
 		    void *arg ATTRIBUTE_UNUSED)
diff --git a/binutils/readelf.c b/binutils/readelf.c
index fab8214664..d3d46d64e1 100644
--- a/binutils/readelf.c
+++ b/binutils/readelf.c
@@ -14176,6 +14176,140 @@  load_specific_debug_section (enum dwarf_section_display_enum  debug,
   return TRUE;
 }
 
+#if HAVE_LIBDEBUGINFOD
+/* Return a hex string representation of the build-id.  */
+
+unsigned char *
+get_build_id (void * data)
+{
+  Filedata * filedata = (Filedata *)data;
+  Elf_Internal_Shdr * shdr;
+  unsigned long i;
+
+  /* Iterate through notes to find NT_GNU_BUILD_ID.  */
+  for (i = 0, shdr = filedata->section_headers;
+       i < filedata->file_header.e_shnum && shdr != NULL;
+       i++, shdr++)
+    {
+      if (shdr->sh_type != SHT_NOTE)
+        continue;
+
+      char * next;
+      char * end;
+      size_t data_remaining;
+      size_t min_notesz;
+      Elf_External_Note * enote;
+      Elf_Internal_Note inote;
+
+      bfd_vma offset = shdr->sh_offset;
+      bfd_vma align = shdr->sh_addralign;
+      bfd_vma length = shdr->sh_size;
+
+      enote = (Elf_External_Note *) get_section_contents (shdr, filedata);
+      if (enote == NULL)
+        continue;
+
+      if (align < 4)
+        align = 4;
+      else if (align != 4 && align != 8)
+        continue;
+
+      end = (char *) enote + length;
+      data_remaining = end - (char *) enote;
+
+      if (!is_ia64_vms (filedata))
+        {
+          min_notesz = offsetof (Elf_External_Note, name);
+          if (data_remaining < min_notesz)
+            {
+              warn (ngettext ("debuginfod: Corrupt note: only %ld byte remains, "
+                              "not enough for a full note\n",
+                              "Corrupt note: only %ld bytes remain, "
+                              "not enough for a full note\n",
+                              data_remaining),
+                    (long) data_remaining);
+              break;
+            }
+          data_remaining -= min_notesz;
+
+          inote.type     = BYTE_GET (enote->type);
+          inote.namesz   = BYTE_GET (enote->namesz);
+          inote.namedata = enote->name;
+          inote.descsz   = BYTE_GET (enote->descsz);
+          inote.descdata = ((char *) enote
+                            + ELF_NOTE_DESC_OFFSET (inote.namesz, align));
+          inote.descpos  = offset + (inote.descdata - (char *) enote);
+          next = ((char *) enote
+                  + ELF_NOTE_NEXT_OFFSET (inote.namesz, inote.descsz, align));
+        }
+      else
+        {
+          Elf64_External_VMS_Note *vms_enote;
+
+          /* PR binutils/15191
+             Make sure that there is enough data to read.  */
+          min_notesz = offsetof (Elf64_External_VMS_Note, name);
+          if (data_remaining < min_notesz)
+            {
+              warn (ngettext ("debuginfod: Corrupt note: only %ld byte remains, "
+                              "not enough for a full note\n",
+                              "Corrupt note: only %ld bytes remain, "
+                              "not enough for a full note\n",
+                              data_remaining),
+                    (long) data_remaining);
+              break;
+            }
+          data_remaining -= min_notesz;
+
+          vms_enote = (Elf64_External_VMS_Note *) enote;
+          inote.type     = BYTE_GET (vms_enote->type);
+          inote.namesz   = BYTE_GET (vms_enote->namesz);
+          inote.namedata = vms_enote->name;
+          inote.descsz   = BYTE_GET (vms_enote->descsz);
+          inote.descdata = inote.namedata + align_power (inote.namesz, 3);
+          inote.descpos  = offset + (inote.descdata - (char *) enote);
+          next = inote.descdata + align_power (inote.descsz, 3);
+        }
+
+      /* Skip malformed notes.  */
+      if ((size_t) (inote.descdata - inote.namedata) < inote.namesz
+          || (size_t) (inote.descdata - inote.namedata) > data_remaining
+          || (size_t) (next - inote.descdata) < inote.descsz
+          || ((size_t) (next - inote.descdata)
+              > data_remaining - (size_t) (inote.descdata - inote.namedata)))
+        {
+          warn (_("debuginfod: note with invalid namesz and/or descsz found\n"));
+          warn (_(" type: 0x%lx, namesize: 0x%08lx, descsize: 0x%08lx, alignment: %u\n"),
+                inote.type, inote.namesz, inote.descsz, (int) align);
+          continue;
+        }
+
+      /* Check if this is the build-id note. If so then convert the build-id
+         bytes to a hex string.  */
+      if (inote.namesz > 0
+          && const_strneq (inote.namedata, "GNU")
+          && inote.type == NT_GNU_BUILD_ID)
+        {
+          unsigned long j;
+          char * build_id;
+
+          build_id = malloc (inote.descsz * 2 + 1);
+          if (build_id == NULL)
+              return NULL;
+
+          for (j = 0; j < inote.descsz; ++j)
+            sprintf(build_id + (j * 2), "%02x", inote.descdata[j] & 0xff);
+          build_id[inote.descsz * 2] = '\0';
+
+          return (unsigned char *)build_id;
+        }
+    }
+
+  return NULL;
+}
+#endif /* HAVE_LIBDEBUGINFOD */
+
+
 /* If this is not NULL, load_debug_section will only look for sections
    within the list of sections given here.  */
 static unsigned int * section_subset = NULL;