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

Aaron Merey amerey@redhat.com
Thu Jun 3 00:01:21 GMT 2021


Hi Tom,

Thanks for the review, I've included some comments below as well as v2
the patch based on your feedback.

On Wed, May 26, 2021 at 4:43 PM Tom Tromey <tom@tromey.com> wrote:
> Aaron> +      gdb::unique_xmalloc_ptr<char> execpath;
> Aaron> +      scoped_fd fd (debuginfod_exec_query (build_id->data, build_id->size,
> Aaron> +                                           abfd->filename, &execpath));
> Aaron> +
> Aaron> +      if (fd.get () >= 0)
> Aaron> +        {
> Aaron> +          execbfd = gdb_bfd_open (execpath.get (), gnutarget);
>
> Does 'fd' already refer to this same file?  If so it can just be reused,
> I think, by passing 'fd.release ()' to gdb_bfd_open.
>
> Or maybe there's some reason not to do this?  If so just let me know.

I didn't do this because when an fd is passed to gdb_bfd_open the resulting
bfd has a null build_id. Not sure if this is intended behavior.

> Aaron> +static std::unordered_map<std::string, std::string> soname_to_buildid;
>
> It seems like there ought to be some spot where this map is cleared.
> For example, it's not uncommon to rebuild a program and then re-"run" it
> in an existing gdb session.  In this situation the mapping could change.

Added a function debuginfod_clear_soname_buildids to do this. It gets
called in core_target::close.

> Aaron> +
> Aaron> +static int
> Aaron> +scan_soname_tag (struct bfd *abfd, char **soname)
> Aaron> +{
>
> This should have an intro comment explaining its purpose, the return
> value, and parameters.

Done.

> Aaron> +  /* Read in .dynamic and .dynstr from the BFD.  */
> Aaron> +  int sect_size = bfd_section_size (sect);
> Aaron> +  gdb_byte *bufstart = (gdb_byte *) alloca (sect_size);
>
> I think we should avoid unbounded use of alloca.
> A gdb::byte_vector would be fine.

Done.

> Aaron> +  /* Read the soname from the string table.  */
> Aaron> +  buf = (gdb_byte *) (xmalloc (sect_size));
> Aaron> +  if (!bfd_get_section_contents (abfd, dynstr, buf, 0, sect_size)) {
> Aaron> +    return 0;
> Aaron> +  }
> Aaron> +
> Aaron> +  *soname = (char *) (buf + soname_idx);
>
> I think this leaks 'buf'.

Fixed by using a gdb::char_vector instead of xmalloc.

> Aaron> +int
> Aaron> +debuginfod_set_soname_buildid (struct bfd *bfd, const bfd_build_id *build_id)
> Aaron> +{
> Aaron> +  gdb_bfd_ref_ptr abfd (gdb_bfd_open (bfd->filename, gnutarget));
> Aaron> +  bfd_check_format (abfd.get (), bfd_object);
>
> Does this work ok if abfd == nullptr?

Added an early return when abfd == nullptr.

> Aaron> +
> Aaron> +  if (build_id == nullptr)
> Aaron> +    return 0;
>
> Seems like this check could be hoisted above opening the BFD.

Done.

> Aaron> +extern scoped_fd
> Aaron> +debuginfod_exec_query (const unsigned char *build_id,
> Aaron> +                       int build_id_len,
> Aaron> +                       const char *filename,
> Aaron> +                       gdb::unique_xmalloc_ptr<char> *destname);
>
> gdb style is to put the type on the same line as the name in
> declarations (but not in definitions).

Done.

> Aaron> +      /* If a build-id was previously associated with this soname
> Aaron> +         then use it to query debuginfod for the library.  */ 
> Aaron> +      if (debuginfod_get_soname_buildid (so->so_name, &buildid_hexstr))
> Aaron> +        {
> Aaron> +          scoped_fd fd = debuginfod_exec_query
> Aaron> +                          ((const unsigned char*) buildid_hexstr.get (), 
> Aaron> +                           0, so->so_name, &filename);
>
> I didn't really track through this too well, but seeing this comment
> made me wonder if the build-id should just be directly attached to the 
> solib.

The resulting solib bfd (execbfd) already ends up containing the build-id.


One other point I should mention is this debuginfod support for downloading
solibs referenced in core files depends on the presence of proc file mappings
in the core file. If no mappings can be found then there will be no soname
build-id information available in solib_map_sections, which is needed to query
debuginfod. (Debuginfo and source queries for local solibs would still work
properly though.)

It may be possible to support solib downloading for core files without proc
mappings but it could get a bit tricky. We could still search each ELF section
of the core file for a build-id but we wouldn't be able to tell which filename
it belongs to without the proc mappings. To get the missing solibs we could
query debuginfod for binaries with build-ids that are present in the core file
but were not seen among the solibs that GDB found locally.

The problem is that debuginfod would have to indiscriminately download all of
the binaries associated with these build-ids when there is a chance some still
might be found locally. This could happen when a missing library links to
another library which is present on the local machine. In this case debuginfod
would download this library before GDB has tried to look for it. 

AFAIK it's only valgrind's vgcore files which don't currently include proc
file mappings. I might approach the valgrind folks about adding proc file
mappings to vgcore files. 

Anyways I just wanted to make sure this proc file mapping dependency was 
made clear.

Thanks,
Aaron

---

>From 0f874e434f37f7e24798a5da17b4dbabb2e46b60 Mon Sep 17 00:00:00 2001
From: Aaron Merey <amerey@redhat.com>
Date: Wed, 2 Jun 2021 17:41:26 -0400
Subject: [PATCH] [PR gdb/27570] missing support for debuginfod in
 core_target::build_file_mappings

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.
	(core_target::close): Call debuginfod_clear_soname_buildids.
	(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.
	(debuginfod_clear_soname_buildids): 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.
	(debuginfod_clear_soname_buildids): 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                                 |  40 +++-
 gdb/debuginfod-support.c                      | 210 ++++++++++++++++++
 gdb/debuginfod-support.h                      |  41 ++++
 gdb/gcore.in                                  |   3 +
 gdb/gdbarch.c                                 |   2 +-
 gdb/gdbarch.h                                 |   4 +-
 gdb/gdbarch.sh                                |   2 +-
 gdb/linux-tdep.c                              |  34 ++-
 gdb/solib.c                                   |  19 ++
 .../gdb.debuginfod/fetch_src_and_symbols.exp  |  25 ++-
 12 files changed, 376 insertions(+), 12 deletions(-)

diff --git a/gdb/arch-utils.c b/gdb/arch-utils.c
index c75a79757ea..44ffaa2c2c3 100644
--- a/gdb/arch-utils.c
+++ b/gdb/arch-utils.c
@@ -1104,7 +1104,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..9d060591b31 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);
@@ -290,6 +301,7 @@ core_target::close ()
       /* Clear out solib state while the bfd is still open.  See
 	 comments in clear_solib in solib.c.  */
       clear_solib ();
+      debuginfod_clear_soname_buildids ();
 
       current_program_space->cbfd.reset (nullptr);
     }
@@ -391,6 +403,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 2d626e335a0..19f12feb0fd 100644
--- a/gdb/debuginfod-support.c
+++ b/gdb/debuginfod-support.c
@@ -41,8 +41,41 @@ 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> *build_id_hex)
+{
+  return 0;
+}
+
+void
+debuginfod_clear_soname_buildids ()
+{
+  return;
+}
 #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
 {
@@ -194,4 +227,181 @@ 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 *c = get_debuginfod_client ();
+
+  if (c == nullptr)
+    return scoped_fd (-ENOMEM);
+
+  char *dname = nullptr;
+  user_data data ("executable for", filename);
+
+  debuginfod_set_user_data (c, &data);
+  scoped_fd fd (debuginfod_find_executable (c, build_id, build_id_len, &dname));
+  debuginfod_set_user_data (c, nullptr);
+
+  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;
+}
+
+/* Attempt to locate the DT_SONAME tag within ABFD's .dynamic section.
+   If found copy the string referred to by the tag to SONAME and return 1.
+   Otherwise return 0.  */
+
+static int
+scan_soname_tag (struct bfd *abfd, std::string &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 section.  */
+  struct bfd_section *sect = bfd_get_section_by_name (abfd, ".dynamic");
+  if (sect == nullptr)
+    return 0;
+
+  /* Read in .dynamic from the BFD.  */
+  int sect_size = bfd_section_size (sect);
+  gdb::byte_vector bufstart (sect_size);
+
+  if (!bfd_get_section_contents (abfd, sect, bufstart.data (), 0, sect_size))
+    return 0;
+
+  gdb_byte *buf = bufstart.data ();
+  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 search for DT_SONAME and DT_STRTAB.  */
+  for (gdb_byte *bufend = buf + sect_size;
+       (buf < bufend) && (soname_idx == 0 || dynstr == nullptr);
+       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.  */
+  gdb::char_vector dynstr_buf (sect_size);
+  if (!bfd_get_section_contents (abfd, dynstr, dynstr_buf.data (), 0, sect_size))
+    return 0;
+
+  soname = dynstr_buf.data () + soname_idx;
+  return 1;
+}
+
+/* See debuginfod-support.h  */
+
+int
+debuginfod_set_soname_buildid (struct bfd *bfd, const bfd_build_id *build_id)
+{
+  if (bfd == nullptr || build_id == nullptr)
+    return 0;
+
+  gdb_bfd_ref_ptr abfd (gdb_bfd_open (bfd->filename, gnutarget));
+
+  if (abfd == nullptr)
+    return 0;
+
+  /* Check that bfd is an ET_DYN ELF file.  */
+  bfd_check_format (abfd.get (), bfd_object);
+  if (bfd_get_flavour (abfd.get ()) != bfd_target_elf_flavour
+      || !(bfd_get_file_flags (abfd.get ()) & DYNAMIC))
+    return 0;
+
+  std::string soname;
+  std::string build_id_hex;
+
+  /* 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++)
+	{
+	  build_id_hex += "0123456789abcdef"[build_id->data[i] >> 4];
+	  build_id_hex += "0123456789abcdef"[build_id->data[i] & 0xf];
+	}
+
+      soname_to_buildid[soname] = build_id_hex;
+      return 1;
+    }
+
+  return 0;
+}
+
+/* See debuginfod-support.h  */
+
+int
+debuginfod_get_soname_buildid (char *soname, gdb::unique_xmalloc_ptr<char> *build_id_hex)
+{
+  auto it = soname_to_buildid.find (basename (soname));
+  if (it == soname_to_buildid.end ())
+    return 0;
+
+  build_id_hex->reset (xstrdup (it->second.c_str ()));
+  return 1;
+}
+
+/* See debuginfod-support.h  */
+
+void debuginfod_clear_soname_buildids ()
+{
+  soname_to_buildid.clear ();
+  return;
+}
+
 #endif
diff --git a/gdb/debuginfod-support.h b/gdb/debuginfod-support.h
index 5e5aab56e74..185c5be22fd 100644
--- a/gdb/debuginfod-support.h
+++ b/gdb/debuginfod-support.h
@@ -61,4 +61,45 @@ 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 BUILD_ID_HEX
+   as a NULL-terminated string.
+
+   Return 1 if SONAME's build-id was found and stored in BUILD_ID_HEX,
+   otherwise return 0.  */
+
+extern int debuginfod_get_soname_buildid (char *soname,
+					  gdb::unique_xmalloc_ptr<char> *build_id_hex);
+
+/* Clear all soname-to-build-id mappings that have been created by
+   debuginfod_set_soname_buildid.  */
+
+extern void debuginfod_clear_soname_buildids ();
+
 #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 2df52118143..5f96d463aa4 100644
--- a/gdb/solib.c
+++ b/gdb/solib.c
@@ -46,6 +46,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 +538,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> build_id_hex;
+
+      /* 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, &build_id_hex))
+	{
+	  scoped_fd fd = debuginfod_exec_query
+			 ((const unsigned char*) build_id_hex.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 \
-- 
2.31.1



More information about the Gdb-patches mailing list