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

Aaron Merey amerey@redhat.com
Mon Jun 21 22:18:09 GMT 2021


Ping

Thanks,
Aaron

On Wed, Jun 2, 2021 at 8:01 PM Aaron Merey via Gdb-patches
<gdb-patches@sourceware.org> wrote:
>
> 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