[PR,gdb/27570] missing support for debuginfod in core_target::build_file_mappings

Message ID 20210429022218.1195563-1-amerey@redhat.com
State New
Headers show
Series
  • [PR,gdb/27570] missing support for debuginfod in core_target::build_file_mappings
Related show

Commit Message

Tankut Baris Aktemur via Gdb-patches April 29, 2021, 2:22 a.m.
Add debuginfod support at various stages of core file processing to enable
the downloading of missing executables and shared libraries from debuginfod
servers.

In order to perform these queries debuginfod requires the build-id of the
missing executable or shared library. These are read from the core file in
linux_read_core_file_mappings and passed to core_target::build_file_mappings
where the query occurs if the file that backs the core file mapping cannot be
found.

GDB will attempt to open shared libraries found in core file mappings during
solib_map_sections even if they were already opened during
core_target::build_file_mappings. This presented a problem since libraries
downloaded from debuginfod won't be found at the path which solib_map_sections
attempts to open. They will instead be stored in the local debuginfod cache.

We already have a BFD handle for these downloaded libraries opened in
core_target::build_file_mappings but there is no easy way to identify and
reuse the correct BFD during solib_map_sections. This is because the filename
used to open the library in solib_map_sections may differ from the filename
used in core_target::build_file_mappings, even though they refer to the same
shared object. This difference can be seen by comparing library filenames from
'info proc mapping' with those from 'info sharedlibrary' (for example,
"libc-2.32.so" vs "libc.so.6").

Therefore GDB needs a way to associate library filenames used in
solib_map_sections with the corresponding build-id from the core file mappings
in case it needs to reopen the library from the local debuginfod cache. This
patch adds facilities to debuginfod-support.c to make this possible.

We populate a map of sonames to build-ids during core_target::build_file_mappings
by using the new function debuginfod_set_soname_buildid. The filenames used in
solib_map_sections originate from each library's DT_SONAME tag in its .dynamic
segment so we added another function scan_soname_tag (which is modelled after
solib-svr4.c:scan_dyntag) that will lookup the value of DT_SONAME in the dynamic
string table segment of the library. This name gets mapped to the corresponding
build-id. Then using the new function debuginfod_get_soname_buildid we can
retrieve the desired build-id in solib_map_sections and check the debuginfod
cache in case the library can't otherwise be found.

Tested on Fedora 33 x86_64.

gdb/ChangeLog:

	PR gdb/27570
	* arch-utils.c (default_read_core_file_mappings): Add build_id
	parameter.
	* arch-utils.h (default_read_core_file_mappings): Add build_id
	parameter.
	* corelow.c (core_target::build_file_mappings): Add debuginfod
	executable query and map library soname to build-id.
	(locate_exec_from_corefile_build_id): Add debuginfod executable query
	* debuginfod-support.c (debuginfod_exec_query): New function.
	(debuginfod_set_soname_buildid): New function.
	(debuginfod_get_soname_buildid): New function.
	(scan_soname_tag): New function.
	* debuginfod-support.h (debuginfod_exec_query): New function.
	(debuginfod_set_soname_buildid): New function.
	(debuginfod_get_soname_buildid): New function.
	* gcore.in: unset $DEBUGINFOD_URLS.
	* gdbarch.c: Regenerate.
	* gdbarch.h: Regenerate.
	* gdbarch.sh (read_core_file_mappings): Add build_id parameter.
	* linux-tdep.c (linux_read_core_file_mappings): Map shared library
	filenames in core file mappings to build-id.
	(linux_core_info_proc_mappings): Add build-id parameter.
	* solib.c (solib_map_sections): Add debuginfod executable query.

gdb/testsuite/ChangeLog:

	PR gdb/27570
	* gdb.debuginfod/fetch_src_and_symbols.exp: Add new testcases.
---
 gdb/arch-utils.c                              |   4 +-
 gdb/arch-utils.h                              |   4 +-
 gdb/corelow.c                                 |  39 +++-
 gdb/debuginfod-support.c                      | 191 ++++++++++++++++++
 gdb/debuginfod-support.h                      |  38 ++++
 gdb/gcore.in                                  |   3 +
 gdb/gdbarch.c                                 |   2 +-
 gdb/gdbarch.h                                 |   4 +-
 gdb/gdbarch.sh                                |   2 +-
 gdb/linux-tdep.c                              |  34 +++-
 gdb/solib.c                                   |  20 ++
 .../gdb.debuginfod/fetch_src_and_symbols.exp  |  25 ++-
 12 files changed, 354 insertions(+), 12 deletions(-)

-- 
2.30.2

Patch

diff --git a/gdb/arch-utils.c b/gdb/arch-utils.c
index 5bb9c758449..38cc12749e3 100644
--- a/gdb/arch-utils.c
+++ b/gdb/arch-utils.c
@@ -1102,7 +1102,9 @@  default_read_core_file_mappings (struct gdbarch *gdbarch,
 							  ULONGEST start,
 							  ULONGEST end,
 							  ULONGEST file_ofs,
-							  const char *filename)>
+							  const char *filename,
+							  const bfd_build_id
+							    *build_id)>
 				   loop_cb)
 {
 }
diff --git a/gdb/arch-utils.h b/gdb/arch-utils.h
index a5b40ad8ff1..6c288f0fe92 100644
--- a/gdb/arch-utils.h
+++ b/gdb/arch-utils.h
@@ -309,6 +309,8 @@  extern void default_read_core_file_mappings (struct gdbarch *gdbarch,
 								      ULONGEST start,
 								      ULONGEST end,
 								      ULONGEST file_ofs,
-								      const char *filename)>
+								      const char *filename,
+								      const bfd_build_id
+									*build_id)>
 					       loop_cb);
 #endif /* ARCH_UTILS_H */
diff --git a/gdb/corelow.c b/gdb/corelow.c
index a1943ab2ea6..568350c649a 100644
--- a/gdb/corelow.c
+++ b/gdb/corelow.c
@@ -46,6 +46,8 @@ 
 #include "gdbsupport/filestuff.h"
 #include "build-id.h"
 #include "gdbsupport/pathstuff.h"
+#include "gdbsupport/scoped_fd.h"
+#include "debuginfod-support.h"
 #include <unordered_map>
 #include <unordered_set>
 #include "gdbcmd.h"
@@ -200,7 +202,7 @@  core_target::build_file_mappings ()
     /* read_core_file_mappings will invoke this lambda for each mapping
        that it finds.  */
     [&] (int num, ULONGEST start, ULONGEST end, ULONGEST file_ofs,
-	 const char *filename)
+	 const char *filename, const bfd_build_id *build_id)
       {
 	/* Architecture-specific read_core_mapping methods are expected to
 	   weed out non-file-backed mappings.  */
@@ -215,6 +217,11 @@  core_target::build_file_mappings ()
 	       canonical) pathname will be provided.  */
 	    gdb::unique_xmalloc_ptr<char> expanded_fname
 	      = exec_file_find (filename, NULL);
+
+	    if (expanded_fname == nullptr && build_id != nullptr)
+	      debuginfod_exec_query (build_id->data, build_id->size,
+				     filename, &expanded_fname);
+
 	    if (expanded_fname == nullptr)
 	      {
 		m_core_unavailable_mappings.emplace_back (start, end - start);
@@ -268,6 +275,10 @@  core_target::build_file_mappings ()
 
 	/* Set target_section fields.  */
 	m_core_file_mappings.emplace_back (start, end, sec);
+
+	/* If this bfd is of a shared library, record its soname and build id
+	   for debuginfod.  */
+	debuginfod_set_soname_buildid (bfd, build_id);
       });
 
   normalize_mem_ranges (&m_core_unavailable_mappings);
@@ -391,6 +402,32 @@  locate_exec_from_corefile_build_id (bfd *abfd, int from_tty)
       symbol_file_add_main (bfd_get_filename (execbfd.get ()),
 			    symfile_add_flag (from_tty ? SYMFILE_VERBOSE : 0));
     }
+  else
+    {
+      gdb::unique_xmalloc_ptr<char> execpath;
+      scoped_fd fd (debuginfod_exec_query (build_id->data, build_id->size,
+					   abfd->filename, &execpath));
+
+      if (fd.get () >= 0)
+	{
+	  execbfd = gdb_bfd_open (execpath.get (), gnutarget);
+
+	  if (execbfd == nullptr)
+	    warning (_("File \"%s\" from debuginfod cannot be opened as bfd"),
+		     execpath.get ());
+	  else if (!build_id_verify (execbfd.get (), build_id->size,
+				     build_id->data))
+	    execbfd.reset (nullptr);
+	  else
+	    {
+	      /* Successful download */
+	      exec_file_attach (execpath.get (), from_tty);
+	      symbol_file_add_main (execpath.get (),
+				    symfile_add_flag (from_tty ?
+						      SYMFILE_VERBOSE : 0));
+	    }
+	}
+    }
 }
 
 /* See gdbcore.h.  */
diff --git a/gdb/debuginfod-support.c b/gdb/debuginfod-support.c
index 9778e2e4cfe..208020ca48e 100644
--- a/gdb/debuginfod-support.c
+++ b/gdb/debuginfod-support.c
@@ -41,8 +41,35 @@  debuginfod_debuginfo_query (const unsigned char *build_id,
 {
   return scoped_fd (-ENOSYS);
 }
+
+scoped_fd
+debuginfod_exec_query (const unsigned char *build_id,
+		       int build_id_len,
+		       const char *filename,
+		       gdb::unique_xmalloc_ptr<char> *destname)
+{
+  return scoped_fd (-ENOSYS);
+}
+
+int
+debuginfod_set_soname_buildid (struct bfd *bfd, const bfd_build_id *build_id)
+{
+  return 0;
+}
+
+int
+debuginfod_get_soname_buildid (char *soname, gdb::unique_xmalloc_ptr<char> *buildid_hexstr)
+{
+  return 0;
+}
 #else
+#include "elf-bfd.h"
+#include "gdbcore.h"
+#include <libgen.h>
 #include <elfutils/debuginfod.h>
+#include <unordered_map>
+
+static std::unordered_map<std::string, std::string> soname_to_buildid;
 
 struct user_data
 {
@@ -186,4 +213,168 @@  debuginfod_debuginfo_query (const unsigned char *build_id,
 
   return fd;
 }
+
+/* See debuginfod-support.h  */
+
+scoped_fd
+debuginfod_exec_query (const unsigned char *build_id,
+		       int build_id_len,
+		       const char *filename,
+		       gdb::unique_xmalloc_ptr<char> *destname)
+{
+  const char *urls_env_var = getenv (DEBUGINFOD_URLS_ENV_VAR);
+  if (urls_env_var == NULL || urls_env_var[0] == '\0')
+    return scoped_fd (-ENOSYS);
+
+  debuginfod_client_up c = debuginfod_init ();
+
+  if (c == nullptr)
+    return scoped_fd (-ENOMEM);
+
+  char *dname = nullptr;
+  user_data data ("executable for", filename);
+
+  debuginfod_set_user_data (c.get (), &data);
+  scoped_fd fd (debuginfod_find_executable (c.get (), build_id, build_id_len,
+					    &dname));
+
+  if (fd.get () < 0 && fd.get () != -ENOENT)
+    printf_filtered (_("Download failed: %s.  Continuing without executable for %ps.\n"),
+		     safe_strerror (-fd.get ()),
+		     styled_string (file_name_style.style (),  filename));
+
+  if (fd.get () >= 0)
+    destname->reset (dname);
+
+  return fd;
+}
+
+
+static int
+scan_soname_tag (struct bfd *abfd, char **soname)
+{
+  if (abfd == nullptr)
+    return 0;
+
+  if (bfd_get_flavour (abfd) != bfd_target_elf_flavour)
+    return 0;
+
+  int arch_size = bfd_get_arch_size (abfd);
+  if (arch_size == -1)
+    return 0;
+
+  /* Find the start address of the .dynamic and .dynstr section.  */
+  struct bfd_section *sect = bfd_get_section_by_name (abfd, ".dynamic");
+  if (sect == nullptr)
+    return 0;
+
+  /* Read in .dynamic and .dynstr from the BFD.  */
+  int sect_size = bfd_section_size (sect);
+  gdb_byte *bufstart = (gdb_byte *) alloca (sect_size);
+  gdb_byte *buf = bufstart;
+
+  if (!bfd_get_section_contents (abfd, sect,
+				 buf, 0, sect_size))
+    return 0;
+
+  struct bfd_section *dynstr = nullptr;
+  CORE_ADDR soname_idx = 0;
+  int step = (arch_size == 32) ? sizeof (Elf32_External_Dyn)
+			       : sizeof (Elf64_External_Dyn);
+
+  /* Iterate over BUF and scan for DT_SONAME and DT_STRTAB.  */
+  for (gdb_byte *bufend = buf + sect_size;
+       (buf < bufend) && !(soname_idx && dynstr);
+       buf += step)
+  {
+    long current_dyntag;
+    CORE_ADDR dyn_ptr;
+
+    if (arch_size == 32)
+      {
+	Elf32_External_Dyn *x_dynp_32 = (Elf32_External_Dyn *) buf;
+	current_dyntag = bfd_h_get_32 (abfd, (bfd_byte *) x_dynp_32->d_tag);
+	dyn_ptr = bfd_h_get_32 (abfd, (bfd_byte *) x_dynp_32->d_un.d_ptr);
+      }
+    else
+      {
+	Elf64_External_Dyn *x_dynp_64 = (Elf64_External_Dyn *) buf;
+	current_dyntag = bfd_h_get_64 (abfd, (bfd_byte *) x_dynp_64->d_tag);
+	dyn_ptr = bfd_h_get_64 (abfd, (bfd_byte *) x_dynp_64->d_un.d_ptr);
+      }
+
+    if (current_dyntag == DT_NULL)
+      buf = bufend;
+    else if (current_dyntag == DT_SONAME)
+      soname_idx = dyn_ptr;
+    else if (current_dyntag == DT_STRTAB)
+      for (struct bfd_section *s = abfd->sections; s != nullptr; s = s->next)
+	if (s->vma == dyn_ptr)
+	  dynstr = s;
+  }
+
+  if (soname_idx == 0 || dynstr == nullptr)
+    return 0;
+
+  sect_size = bfd_section_size (dynstr);
+  if (sect_size <= soname_idx)
+    return 0;
+
+  /* Read the soname from the string table.  */
+  buf = (gdb_byte *) (xmalloc (sect_size));
+  if (!bfd_get_section_contents (abfd, dynstr, buf, 0, sect_size)) {
+    return 0;
+  }
+
+  *soname = (char *) (buf + soname_idx);
+  return 1;
+}
+
+/* See debuginfod-support.h  */
+
+int
+debuginfod_set_soname_buildid (struct bfd *bfd, const bfd_build_id *build_id)
+{
+  gdb_bfd_ref_ptr abfd (gdb_bfd_open (bfd->filename, gnutarget));
+  bfd_check_format (abfd.get (), bfd_object);
+
+  if (build_id == nullptr)
+    return 0;
+
+  /* Check that bfd is an ET_DYN ELF file.  */
+  if (abfd == nullptr
+      || bfd_get_flavour (abfd.get ()) != bfd_target_elf_flavour
+      || !(bfd_get_file_flags (abfd.get ()) & DYNAMIC))
+    return 0;
+
+#define MAX_BUILDID_LEN 64
+  char *soname;
+  char *buf = (char *) (alloca (MAX_BUILDID_LEN * 2 + 1));
+
+  /* Determine soname of shared library.  If found map soname to build-id.  */
+  if (scan_soname_tag (abfd.get (), &soname))
+    {
+      for (int i = 0; i < build_id->size; i++)
+	sprintf (buf + (i * 2), "%02x", build_id->data[i]);
+
+      buf[build_id->size * 2] = '\0';
+      soname_to_buildid[soname] = buf;
+      return 1;
+    }
+
+  return 0;
+}
+
+/* See debuginfod-support.h  */
+
+int
+debuginfod_get_soname_buildid (char *soname, gdb::unique_xmalloc_ptr<char> *buildid_hexstr)
+{
+  auto it = soname_to_buildid.find (basename (soname));
+  if (it == soname_to_buildid.end ())
+    return 0;
+
+  buildid_hexstr->reset (xstrdup (it->second.c_str ()));
+  return 1;
+}
 #endif
diff --git a/gdb/debuginfod-support.h b/gdb/debuginfod-support.h
index 5e5aab56e74..ca10a37d87e 100644
--- a/gdb/debuginfod-support.h
+++ b/gdb/debuginfod-support.h
@@ -61,4 +61,42 @@  debuginfod_debuginfo_query (const unsigned char *build_id,
 			    const char *filename,
 			    gdb::unique_xmalloc_ptr<char> *destname);
 
+/* Query debuginfod servers for an executable 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 associated with the executable.
+   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_exec_query (const unsigned char *build_id,
+		       int build_id_len,
+		       const char *filename,
+		       gdb::unique_xmalloc_ptr<char> *destname);
+
+/* If BFD is of an ELF shared library then associate its soname with
+   BUILD_ID so that it can be retrieved with debuginfod_get_soname_buildid().
+
+   Return 1 if the soname was successully associated with BUILD-ID, otherwise
+   return 0.  */
+
+extern int
+debuginfod_set_soname_buildid (struct bfd *bfd, const bfd_build_id *build_id);
+
+/* If SONAME had a build-id associated with it by a previous call to
+   debuginfod_set_soname_buildid() then store the build-id in BUILDID_HEXSTR
+   as a NULL-terminated string.
+
+   Return 1 if SONAME's build-id was found and stored in BUILDID_HEXSTR,
+   otherwise return 0.  */
+
+extern int
+debuginfod_get_soname_buildid (char *soname,
+			       gdb::unique_xmalloc_ptr<char> *buildid_hexstr);
+
 #endif /* DEBUGINFOD_SUPPORT_H */
diff --git a/gdb/gcore.in b/gdb/gcore.in
index 24354a79e27..25b24c3cd3d 100644
--- a/gdb/gcore.in
+++ b/gdb/gcore.in
@@ -89,6 +89,9 @@  if [ ! -f "$binary_path/@GDB_TRANSFORM_NAME@" ]; then
   exit 1
 fi
 
+# Prevent unnecessary debuginfod queries during core file generation.
+unset DEBUGINFOD_URLS
+
 # Initialise return code.
 rc=0
 
diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c
index 208cf4b5aaa..8a2d88c5f96 100644
--- a/gdb/gdbarch.c
+++ b/gdb/gdbarch.c
@@ -5411,7 +5411,7 @@  set_gdbarch_get_pc_address_flags (struct gdbarch *gdbarch,
 }
 
 void
-gdbarch_read_core_file_mappings (struct gdbarch *gdbarch, struct bfd *cbfd, gdb::function_view<void (ULONGEST count)> pre_loop_cb, gdb::function_view<void (int num, ULONGEST start, ULONGEST end, ULONGEST file_ofs, const char *filename)> loop_cb)
+gdbarch_read_core_file_mappings (struct gdbarch *gdbarch, struct bfd *cbfd, gdb::function_view<void (ULONGEST count)> pre_loop_cb, gdb::function_view<void (int num, ULONGEST start, ULONGEST end, ULONGEST file_ofs, const char *filename, const bfd_build_id *build_id)> loop_cb)
 {
   gdb_assert (gdbarch != NULL);
   gdb_assert (gdbarch->read_core_file_mappings != NULL);
diff --git a/gdb/gdbarch.h b/gdb/gdbarch.h
index 7157e5596fd..ca7949bafb1 100644
--- a/gdb/gdbarch.h
+++ b/gdb/gdbarch.h
@@ -1709,8 +1709,8 @@  extern void set_gdbarch_get_pc_address_flags (struct gdbarch *gdbarch, gdbarch_g
 
 /* Read core file mappings */
 
-typedef void (gdbarch_read_core_file_mappings_ftype) (struct gdbarch *gdbarch, struct bfd *cbfd, gdb::function_view<void (ULONGEST count)> pre_loop_cb, gdb::function_view<void (int num, ULONGEST start, ULONGEST end, ULONGEST file_ofs, const char *filename)> loop_cb);
-extern void gdbarch_read_core_file_mappings (struct gdbarch *gdbarch, struct bfd *cbfd, gdb::function_view<void (ULONGEST count)> pre_loop_cb, gdb::function_view<void (int num, ULONGEST start, ULONGEST end, ULONGEST file_ofs, const char *filename)> loop_cb);
+typedef void (gdbarch_read_core_file_mappings_ftype) (struct gdbarch *gdbarch, struct bfd *cbfd, gdb::function_view<void (ULONGEST count)> pre_loop_cb, gdb::function_view<void (int num, ULONGEST start, ULONGEST end, ULONGEST file_ofs, const char *filename, const bfd_build_id *build_id)> loop_cb);
+extern void gdbarch_read_core_file_mappings (struct gdbarch *gdbarch, struct bfd *cbfd, gdb::function_view<void (ULONGEST count)> pre_loop_cb, gdb::function_view<void (int num, ULONGEST start, ULONGEST end, ULONGEST file_ofs, const char *filename, const bfd_build_id *build_id)> loop_cb);
 extern void set_gdbarch_read_core_file_mappings (struct gdbarch *gdbarch, gdbarch_read_core_file_mappings_ftype *read_core_file_mappings);
 
 extern struct gdbarch_tdep *gdbarch_tdep (struct gdbarch *gdbarch);
diff --git a/gdb/gdbarch.sh b/gdb/gdbarch.sh
index 43e51341f97..ea651e8c0dc 100755
--- a/gdb/gdbarch.sh
+++ b/gdb/gdbarch.sh
@@ -1209,7 +1209,7 @@  m;ULONGEST;type_align;struct type *type;type;;default_type_align;;0
 f;std::string;get_pc_address_flags;frame_info *frame, CORE_ADDR pc;frame, pc;;default_get_pc_address_flags;;0
 
 # Read core file mappings
-m;void;read_core_file_mappings;struct bfd *cbfd, gdb::function_view<void (ULONGEST count)> pre_loop_cb, gdb::function_view<void (int num, ULONGEST start, ULONGEST end, ULONGEST file_ofs, const char *filename)> loop_cb;cbfd, pre_loop_cb, loop_cb;;default_read_core_file_mappings;;0
+m;void;read_core_file_mappings;struct bfd *cbfd, gdb::function_view<void (ULONGEST count)> pre_loop_cb, gdb::function_view<void (int num, ULONGEST start, ULONGEST end, ULONGEST file_ofs, const char *filename, const bfd_build_id *build_id)> loop_cb;cbfd, pre_loop_cb, loop_cb;;default_read_core_file_mappings;;0
 
 EOF
 }
diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c
index 927e69bf1e1..757b5bc02e9 100644
--- a/gdb/linux-tdep.c
+++ b/gdb/linux-tdep.c
@@ -43,6 +43,7 @@ 
 #include "gcore-elf.h"
 
 #include <ctype.h>
+#include <unordered_map>
 
 /* This enum represents the values that the user can choose when
    informing the Linux kernel about which memory mappings will be
@@ -1104,7 +1105,8 @@  linux_read_core_file_mappings (struct gdbarch *gdbarch,
 							ULONGEST start,
 							ULONGEST end,
 							ULONGEST file_ofs,
-							const char *filename)>
+							const char *filename,
+							const bfd_build_id *build_id)>
 				 loop_cb)
 {
   /* Ensure that ULONGEST is big enough for reading 64-bit core files.  */
@@ -1174,6 +1176,23 @@  linux_read_core_file_mappings (struct gdbarch *gdbarch,
   if (f != descend)
     warning (_("malformed note - filename area is too big"));
 
+  const bfd_build_id *orig_build_id = cbfd->build_id;
+  std::unordered_map<ULONGEST, const bfd_build_id *> vma_map;
+  std::unordered_map<char *, const bfd_build_id *> filename_map;
+
+  /* Search for solib build-ids in the core file.  Each time one is found,
+     map the start vma of the corresponding elf header to the build-id.  */
+  for (bfd_section *sec = cbfd->sections; sec != nullptr; sec = sec->next)
+    {
+      cbfd->build_id = nullptr;
+
+      if (sec->flags & SEC_LOAD
+	  && get_elf_backend_data (cbfd)->elf_backend_core_find_build_id
+	       (cbfd, (bfd_vma) sec->filepos))
+	vma_map[sec->vma] = cbfd->build_id;
+    }
+
+  cbfd->build_id = orig_build_id;
   pre_loop_cb (count);
 
   for (int i = 0; i < count; i++)
@@ -1187,8 +1206,17 @@  linux_read_core_file_mappings (struct gdbarch *gdbarch,
       descdata += addr_size;
       char * filename = filenames;
       filenames += strlen ((char *) filenames) + 1;
+      const bfd_build_id *build_id = nullptr;
+
+      /* Map filename to the build-id associated with this start vma,
+	 if such a build-id was found.  Otherwise use the build-id
+	 already associated with this filename if it exists. */
+      if ((build_id = vma_map[start]) != nullptr)
+	filename_map[filename] = build_id;
+      else
+	build_id = filename_map[filename];
 
-      loop_cb (i, start, end, file_ofs, filename);
+      loop_cb (i, start, end, file_ofs, filename, build_id);
     }
 }
 
@@ -1217,7 +1245,7 @@  linux_core_info_proc_mappings (struct gdbarch *gdbarch, const char *args)
 	  }
       },
     [=] (int num, ULONGEST start, ULONGEST end, ULONGEST file_ofs,
-	 const char *filename)
+	 const char *filename, const bfd_build_id *build_id)
       {
 	if (gdbarch_addr_bit (gdbarch) == 32)
 	  printf_filtered ("\t%10s %10s %10s %10s %s\n",
diff --git a/gdb/solib.c b/gdb/solib.c
index f3cd48fde77..53b74e5d2ac 100644
--- a/gdb/solib.c
+++ b/gdb/solib.c
@@ -23,6 +23,7 @@ 
 #include <fcntl.h>
 #include "symtab.h"
 #include "bfd.h"
+#include "elf-bfd.h"
 #include "symfile.h"
 #include "objfiles.h"
 #include "gdbcore.h"
@@ -46,6 +47,8 @@ 
 #include "filesystem.h"
 #include "gdb_bfd.h"
 #include "gdbsupport/filestuff.h"
+#include "gdbsupport/scoped_fd.h"
+#include "debuginfod-support.h"
 #include "source.h"
 #include "cli/cli-style.h"
 
@@ -536,6 +539,23 @@  solib_map_sections (struct so_list *so)
   gdb::unique_xmalloc_ptr<char> filename (tilde_expand (so->so_name));
   gdb_bfd_ref_ptr abfd (ops->bfd_open (filename.get ()));
 
+  if (abfd == NULL)
+    {
+      gdb::unique_xmalloc_ptr<char> buildid_hexstr;
+
+      /* If a build-id was previously associated with this soname
+	 then use it to query debuginfod for the library.  */
+      if (debuginfod_get_soname_buildid (so->so_name, &buildid_hexstr))
+	{
+	  scoped_fd fd = debuginfod_exec_query
+			  ((const unsigned char*) buildid_hexstr.get (),
+			   0, so->so_name, &filename);
+
+	  if (fd.get () >= 0)
+	    abfd = ops->bfd_open (filename.get ());
+	}
+    }
+
   if (abfd == NULL)
     return 0;
 
diff --git a/gdb/testsuite/gdb.debuginfod/fetch_src_and_symbols.exp b/gdb/testsuite/gdb.debuginfod/fetch_src_and_symbols.exp
index 81d4791eb6d..79ec4e6f853 100644
--- a/gdb/testsuite/gdb.debuginfod/fetch_src_and_symbols.exp
+++ b/gdb/testsuite/gdb.debuginfod/fetch_src_and_symbols.exp
@@ -58,6 +58,11 @@  if { [gdb_compile "$sourcetmp" "$binfile" executable {debug}] != "" } {
     return -1
 }
 
+if { [gdb_compile "$sourcetmp" "${binfile}2" executable {debug}] != "" } {
+    fail "compile"
+    return -1
+}
+
 # Write some assembly that just has a .gnu_debugaltlink section.
 # Copied from testsuite/gdb.dwarf2/dwzbuildid.exp.
 proc write_just_debugaltlink {filename dwzname buildid} {
@@ -109,8 +114,10 @@  proc write_dwarf_file {filename buildid {value 99}} {
     }
 }
 
+set corefile "corefile"
+
 proc no_url { } {
-    global binfile outputdir debugdir
+    global binfile corefile outputdir debugdir
 
     setenv DEBUGINFOD_URLS ""
 
@@ -162,10 +169,20 @@  proc no_url { } {
     gdb_test "file ${binfile}_alt.o" \
 	".*could not find '.gnu_debugaltlink'.*" \
 	"file [file tail ${binfile}_alt.o]"
+
+    # Generate a core file and test that gdb cannot find the executable
+    clean_restart ${binfile}2
+    gdb_test "start" "Temporary breakpoint.*"
+    gdb_test "generate-core-file $corefile" \
+	"Saved corefile $corefile"
+    file rename -force ${binfile}2 $debugdir
+
+    clean_restart
+    gdb_test "core $corefile" ".*in ?? ().*"
 }
 
 proc local_url { } {
-    global binfile outputdir db debugdir
+    global binfile corefile outputdir db debugdir
 
     # Find an unused port
     set port 7999
@@ -228,6 +245,10 @@  proc local_url { } {
     gdb_test "file ${binfile}_alt.o" \
 	".*Reading symbols from ${binfile}_alt.o\.\.\.*" \
 	"file [file tail ${binfile}_alt.o]"
+
+    # gdb should now find the executable file
+    clean_restart
+    gdb_test "core $corefile" ".*return 0.*"
 }
 
 set envlist \