Add debuginfod support for objdump -S
authorAaron Merey <amerey@redhat.com>
Fri, 2 Sep 2022 23:01:56 +0000 (19:01 -0400)
committerAaron Merey <amerey@redhat.com>
Tue, 6 Sep 2022 14:43:07 +0000 (10:43 -0400)
Currently objdump -S is not able to make use files downloaded from debuginfod.
This is due to bfd_find_nearest_line_discriminator being unable to locate any
separate debuginfo files in the debuginfod cache. Additionally objdump lacked
a call to debuginfod_find_source in order to download missing source files.

Fix this by using bfd_find_nearest_line_with_alt instead of
bfd_find_nearest_line_discriminator. Also add a call to
debuginfod_find_source in order to download missing source files.

Co-authored-by: Nick Clifton <nickc@redhat.com>
binutils/ChangeLog
binutils/objdump.c

index b5711097d50766217224c96fcb6208048f25b58a..cfb6417b55eee3540e76a4c8d6422e05bcb453fe 100644 (file)
@@ -1,3 +1,16 @@
+2022-09-02  Aaron Merey  <amerey@redhat.com>
+
+       PR 29075
+       * objdump.c (find_separate_debug): New function. Finds the bfd
+       corresponding to the separate debuginfod of a given bfd.
+       (find_alt_debug): New function. Finds the bfd corresponding to the
+       .gnu_debugaltlink of a given bfd.
+       (slurp_file): Add bfd parameter. If a source file cannot be found
+       attempt to download it from debuginfod, if enabled.
+       (try_print_file_open): Add bfd parameter and pass it to slurp_file.
+       (show_line): If debuginfod is enabled, call
+       bfd_find_nearest_line_with_alt instead of bfd_find_nearest_line.
+
 2022-09-01  Nick Clifton  <nickc@redhat.com>
 
        PR 29534
index b34df5392b96ab1f663225751a24ab7f3309a07c..d34f269b40600f6db2d66562304c327400322262 100644 (file)
 #include <sys/mman.h>
 #endif
 
+#ifdef HAVE_LIBDEBUGINFOD
+#include <elfutils/debuginfod.h>
+#endif
+
 /* Internal headers for the ELF .stab-dump code - sorry.  */
 #define        BYTES_IN_WORD   32
 #include "aout/aout64.h"
@@ -1735,28 +1739,174 @@ static struct print_file_list *print_files;
 
 #define SHOW_PRECEDING_CONTEXT_LINES (5)
 
-/* Read a complete file into memory.  */
+#if HAVE_LIBDEBUGINFOD
+/* Return a hex string represention of the build-id.  */
+
+unsigned char *
+get_build_id (void * data)
+{
+  unsigned i;
+  char * build_id_str;
+  bfd * abfd = (bfd *) data;
+  const struct bfd_build_id * build_id;
+
+  build_id = abfd->build_id;
+  if (build_id == NULL)
+    return NULL;
+
+  build_id_str = malloc (build_id->size * 2 + 1);
+  if (build_id_str == NULL)
+    return NULL;
+
+  for (i = 0; i < build_id->size; i++)
+    sprintf (build_id_str + (i * 2), "%02x", build_id->data[i]);
+  build_id_str[build_id->size * 2] = '\0';
+
+  return (unsigned char *) build_id_str;
+}
+
+/* Search for a separate debug file matching ABFD's build-id.  */
+
+static bfd *
+find_separate_debug (const bfd * abfd)
+{
+  const struct bfd_build_id * build_id = abfd->build_id;
+  separate_info * i = first_separate_info;
+
+  if (build_id == NULL || i == NULL)
+    return NULL;
+
+  while (i != NULL)
+    {
+      const bfd * i_bfd = (bfd *) i->handle;
+
+      if (abfd != NULL && i_bfd->build_id != NULL)
+       {
+         const unsigned char * data = i_bfd->build_id->data;
+         size_t size = i_bfd->build_id->size;
+
+         if (size == build_id->size
+             && memcmp (data, build_id->data, size) == 0)
+           return (bfd *) i->handle;
+       }
+
+      i = i->next;
+    }
+
+  return NULL;
+}
+
+/* Search for a separate debug file matching ABFD's .gnu_debugaltlink
+    build-id.  */
+
+static bfd *
+find_alt_debug (const bfd * abfd)
+{
+  size_t namelen;
+  size_t id_len;
+  const char * name;
+  struct dwarf_section * section;
+  const struct bfd_build_id * build_id = abfd->build_id;
+  separate_info * i = first_separate_info;
+
+  if (i == NULL
+      || build_id == NULL
+      || !load_debug_section (gnu_debugaltlink, (void *) abfd))
+    return NULL;
+
+  section = &debug_displays[gnu_debugaltlink].section;
+  if (section == NULL)
+    return NULL;
+
+  name = (const char *) section->start;
+  namelen = strnlen (name, section->size) + 1;
+  if (namelen == 1)
+    return NULL;
+  if (namelen >= section->size)
+    return NULL;
+
+  id_len = section->size - namelen;
+  if (id_len < 0x14)
+    return NULL;
+
+  /* Compare the .gnu_debugaltlink build-id with the build-ids of the
+     known separate_info files.  */
+  while (i != NULL)
+    {
+      const bfd * i_bfd = (bfd *) i->handle;
+
+      if (i_bfd != NULL && i_bfd->build_id != NULL)
+       {
+         const unsigned char * data = i_bfd->build_id->data;
+         size_t size = i_bfd->build_id->size;
+
+         if (id_len == size
+             && memcmp (section->start + namelen, data, size) == 0)
+           return (bfd *) i->handle;
+       }
+
+      i = i->next;
+    }
+
+  return NULL;
+}
+
+#endif /* HAVE_LIBDEBUGINFOD */
+
+/* Reads the contents of file FN into memory.  Returns a pointer to the buffer.
+   Also returns the size of the buffer in SIZE_RETURN and a filled out
+   stat structure in FST_RETURN.  Returns NULL upon failure.  */
 
 static const char *
-slurp_file (const char *fn, size_t *size, struct stat *fst)
+slurp_file (const char *   fn,
+           size_t *       size_return,
+           struct stat *  fst_return,
+           bfd *          abfd ATTRIBUTE_UNUSED)
 {
 #ifdef HAVE_MMAP
-  int ps = getpagesize ();
+  int ps;
   size_t msize;
 #endif
   const char *map;
-  int fd = open (fn, O_RDONLY | O_BINARY);
+  int fd;
+
+  /* Paranoia.  */
+  if (fn == NULL || * fn == 0 || size_return == NULL || fst_return == NULL)
+    return NULL;
+
+  fd = open (fn, O_RDONLY | O_BINARY);
+
+#if HAVE_LIBDEBUGINFOD
+  if (fd < 0 && use_debuginfod && fn[0] == '/' && abfd != NULL)
+    {
+      unsigned char * build_id;
+      debuginfod_client * client;
+
+      client = debuginfod_begin ();
+      if (client == NULL)
+       return NULL;
+
+      build_id = get_build_id (abfd);
+      fd = debuginfod_find_source (client, build_id, 0, fn, NULL);
+      free (build_id);
+      debuginfod_end (client);
+    }
+#endif
 
   if (fd < 0)
     return NULL;
-  if (fstat (fd, fst) < 0)
+
+  if (fstat (fd, fst_return) < 0)
     {
       close (fd);
       return NULL;
     }
-  *size = fst->st_size;
+
+  *size_return = fst_return->st_size;
+
 #ifdef HAVE_MMAP
-  msize = (*size + ps - 1) & ~(ps - 1);
+  ps = getpagesize ();
+  msize = (*size_return + ps - 1) & ~(ps - 1);
   map = mmap (NULL, msize, PROT_READ, MAP_SHARED, fd, 0);
   if (map != (char *) -1L)
     {
@@ -1764,8 +1914,9 @@ slurp_file (const char *fn, size_t *size, struct stat *fst)
       return map;
     }
 #endif
-  map = (const char *) malloc (*size);
-  if (!map || (size_t) read (fd, (char *) map, *size) != *size)
+
+  map = (const char *) malloc (*size_return);
+  if (!map || (size_t) read (fd, (char *) map, *size_return) != *size_return)
     {
       free ((void *) map);
       map = NULL;
@@ -1831,16 +1982,20 @@ index_file (const char *map, size_t size, unsigned int *maxline)
 }
 
 /* Tries to open MODNAME, and if successful adds a node to print_files
-   linked list and returns that node.  Returns NULL on failure.  */
+   linked list and returns that node.  Also fills in the stat structure
+   pointed to by FST_RETURN.  Returns NULL on failure.  */
 
 static struct print_file_list *
-try_print_file_open (const char *origname, const char *modname, struct stat *fst)
+try_print_file_open (const char *   origname,
+                    const char *   modname,
+                    struct stat *  fst_return,
+                    bfd *          abfd)
 {
   struct print_file_list *p;
 
   p = (struct print_file_list *) xmalloc (sizeof (struct print_file_list));
 
-  p->map = slurp_file (modname, &p->mapsize, fst);
+  p->map = slurp_file (modname, &p->mapsize, fst_return, abfd);
   if (p->map == NULL)
     {
       free (p);
@@ -1870,7 +2025,7 @@ update_source_path (const char *filename, bfd *abfd)
   struct stat fst;
   int i;
 
-  p = try_print_file_open (filename, filename, &fst);
+  p = try_print_file_open (filename, filename, &fst, abfd);
   if (p == NULL)
     {
       if (include_path_count == 0)
@@ -1886,7 +2041,7 @@ update_source_path (const char *filename, bfd *abfd)
          char *modname = concat (include_paths[i], "/", fname,
                                  (const char *) 0);
 
-         p = try_print_file_open (filename, modname, &fst);
+         p = try_print_file_open (filename, modname, &fst, abfd);
          if (p)
            break;
 
@@ -1956,10 +2111,52 @@ show_line (bfd *abfd, asection *section, bfd_vma addr_offset)
   if (! with_line_numbers && ! with_source_code)
     return;
 
+#ifdef HAVE_LIBDEBUGINFOD
+  {
+    bfd *debug_bfd;
+    const char *alt_filename = NULL;
+
+    if (use_debuginfod)
+      {
+       bfd *alt_bfd;
+
+       /* PR 29075: Check for separate debuginfo and .gnu_debugaltlink files.
+          They need to be passed to bfd_find_nearest_line_with_alt in case they
+          were downloaded from debuginfod.  Otherwise libbfd will attempt to
+          search for them and fail to locate them.  */
+       debug_bfd = find_separate_debug (abfd);
+       if (debug_bfd == NULL)
+         debug_bfd = abfd;
+
+       alt_bfd = find_alt_debug (debug_bfd);
+       if (alt_bfd != NULL)
+         alt_filename = bfd_get_filename (alt_bfd);
+      }
+    else
+      debug_bfd = abfd;
+
+    bfd_set_error (bfd_error_no_error);
+    if (! bfd_find_nearest_line_with_alt (debug_bfd, alt_filename,
+                                         section, syms,
+                                         addr_offset, &filename,
+                                         &functionname, &linenumber,
+                                         &discriminator))
+      {
+       if (bfd_get_error () == bfd_error_no_error)
+         return;
+       if (! bfd_find_nearest_line_discriminator (abfd, section, syms,
+                                                  addr_offset, &filename,
+                                                  &functionname, &linenumber,
+                                                  &discriminator))
+         return;
+      }
+  }
+#else
   if (! bfd_find_nearest_line_discriminator (abfd, section, syms, addr_offset,
                                             &filename, &functionname,
                                             &linenumber, &discriminator))
     return;
+#endif
 
   if (filename != NULL && *filename == '\0')
     filename = NULL;
@@ -4136,33 +4333,6 @@ open_debug_file (const char * pathname)
   return data;
 }
 
-#if HAVE_LIBDEBUGINFOD
-/* Return a hex string represention of the build-id.  */
-
-unsigned char *
-get_build_id (void * data)
-{
-  unsigned i;
-  char * build_id_str;
-  bfd * abfd = (bfd *) data;
-  const struct bfd_build_id * build_id;
-
-  build_id = abfd->build_id;
-  if (build_id == NULL)
-    return NULL;
-
-  build_id_str = malloc (build_id->size * 2 + 1);
-  if (build_id_str == NULL)
-    return NULL;
-
-  for (i = 0; i < build_id->size; i++)
-    sprintf (build_id_str + (i * 2), "%02x", build_id->data[i]);
-  build_id_str[build_id->size * 2] = '\0';
-
-  return (unsigned char *)build_id_str;
-}
-#endif /* HAVE_LIBDEBUGINFOD */
-
 static void
 dump_dwarf_section (bfd *abfd, asection *section,
                    void *arg)