]> git.ipfire.org Git - thirdparty/elfutils.git/commitdiff
libdw: Resolve alt file on first use.
authorMark Wielaard <mark@klomp.org>
Fri, 19 Jan 2018 22:59:21 +0000 (23:59 +0100)
committerMark Wielaard <mark@klomp.org>
Thu, 25 Jan 2018 15:30:47 +0000 (16:30 +0100)
Add a new alt_fd field to the Dwarf struct. This tracks whether we tried
to open the alt file ourselves. This is used in dwarf_getalt to see if
we should try to find and open the alt file ourselves (if the user hasn't
called dwarf_setalt yet). dwarf_formref_die and dwarf_formstring now call
dwarf_getalt instead of accessing the alt_dwarf Dwarf field directly.

For applications using libdwfl nothing changes (dwfl will find, set and
clean up the alt file). For programs that set the alt file themselves
already through other means, nothing changes. But for applications that
don't create the Dwarf through libdwfl and don't set the alt file already
libdw will now try to find and set it on first access. If found the
application will now not get errors for missing alt files.

Add a simple testcase based on the existing allfcts test which already
tries to set the alt file, but is too simplistic to find it in some
subdir (relative to the main debug file).

Signed-off-by: Mark Wielaard <mark@klomp.org>
13 files changed:
libdw/ChangeLog
libdw/Makefile.am
libdw/dwarf_begin_elf.c
libdw/dwarf_end.c
libdw/dwarf_formref_die.c
libdw/dwarf_formstring.c
libdw/dwarf_getalt.c
libdw/dwarf_setalt.c
libdw/libdw.h
libdw/libdwP.h
tests/ChangeLog
tests/allfcts.c
tests/run-allfcts-multi.sh

index 479dd42b38a1a1269138f5d66035d3fa3cb16793..cb5e61ac6529c249563789ce18d7f72760edc737 100644 (file)
@@ -1,3 +1,22 @@
+2018-01-22  Mark Wielaard  <mark@klomp.org>
+
+       * Makefile.am (AM_CPPFLAGS): Add -I libdwelf.
+       * dwarf_begin_elf.c (dwarf_begin_elf): Initialize Dwarf alt_fd to -1.
+       * dwarf_end.c (dwarf_end): Call dwarf_end and close on the alt_dwarf
+       and alt_fd if we allocated them.
+       * dwarf_fromref_die.c (dwarf_formref_die): Call dwarf_getalt.
+       * dwarf_formstring.c (dwarf_formstring): Likewise.
+       * dwarf_getalt.c (__libdw_filepath): New internal function.
+       (find_debug_altlink): New static function.
+       (dwarf_getalt): Check Dwarf alt_dwarf and call find_debug_altlink.
+       Cache result.
+       * dwarf_setalt.c (dwarf_setalt): Clean up Dwarf alt_dwarf and alt_fd
+       if we allocated.
+       * libdw.h (dwarf_getalt): Extend documentation.
+       (dwarf_setalt): Likewise.
+       * libdwP.h (struct Dwarf): Add alt_fd field.
+       (filepath): Declare new internal function.
+
 2018-01-14  Petr Machata  <pmachata@gmail.com>
 
        * dwarf_formsdata.c (dwarf_formsdata):
index 8ee46802ac63c4a8d185513e0817d0fe30107ede..8545b5b48053f9e15d9bf621be2a5355a6cf14c6 100644 (file)
@@ -1,6 +1,6 @@
 ## Process this file with automake to create Makefile.in
 ##
-## Copyright (C) 2002-2010, 2012, 2014, 2016 Red Hat, Inc.
+## Copyright (C) 2002-2010, 2012, 2014, 2016, 2018 Red Hat, Inc.
 ## This file is part of elfutils.
 ##
 ## This file is free software; you can redistribute it and/or modify
@@ -31,7 +31,7 @@ include $(top_srcdir)/config/eu.am
 if BUILD_STATIC
 AM_CFLAGS += $(fpic_CFLAGS)
 endif
-AM_CPPFLAGS += -I$(srcdir)/../libelf
+AM_CPPFLAGS += -I$(srcdir)/../libelf -I$(srcdir)/../libdwelf
 VERSION = 1
 
 lib_LIBRARIES = libdw.a
index 7c3fe103604e20e7f277eda3cc996aacfb81c7b0..6834ac533f19749b9703d2435d4837f8dc5a759f 100644 (file)
@@ -1,5 +1,5 @@
 /* Create descriptor from ELF descriptor for processing file.
-   Copyright (C) 2002-2011, 2014, 2015 Red Hat, Inc.
+   Copyright (C) 2002-2011, 2014, 2015, 2018 Red Hat, Inc.
    This file is part of elfutils.
    Written by Ulrich Drepper <drepper@redhat.com>, 2002.
 
@@ -326,6 +326,7 @@ dwarf_begin_elf (Elf *elf, Dwarf_Cmd cmd, Elf_Scn *scngrp)
     result->other_byte_order = true;
 
   result->elf = elf;
+  result->alt_fd = -1;
 
   /* Initialize the memory handling.  */
   result->mem_default_size = mem_default_size;
index 6c6d985ac3a3bec8c59b7af7f058ae97210019a3..f6915abf6f56f2898f7c7aa0f0e8dd96dd5c05ab 100644 (file)
@@ -1,5 +1,5 @@
 /* Release debugging handling context.
-   Copyright (C) 2002-2011, 2014 Red Hat, Inc.
+   Copyright (C) 2002-2011, 2014, 2018 Red Hat, Inc.
    This file is part of elfutils.
    Written by Ulrich Drepper <drepper@redhat.com>, 2002.
 
@@ -35,6 +35,7 @@
 #include <stdlib.h>
 #include <assert.h>
 #include <string.h>
+#include <unistd.h>
 
 #include "libdwP.h"
 #include "cfi.h"
@@ -103,6 +104,13 @@ dwarf_end (Dwarf *dwarf)
          free (dwarf->fake_loc_cu);
        }
 
+      /* Did we find and allocate the alt Dwarf ourselves?  */
+      if (dwarf->alt_fd != -1)
+       {
+         INTUSE(dwarf_end) (dwarf->alt_dwarf);
+         close (dwarf->alt_fd);
+       }
+
       /* Free the context descriptor.  */
       free (dwarf);
     }
index 7e2b11a2bb6b828d50b9c733ee6c23bd16f4819c..704816f8b101e5dde29d92e8c554b4d2cfe22903 100644 (file)
@@ -1,5 +1,5 @@
 /* Look up the DIE in a reference-form attribute.
-   Copyright (C) 2005-2010 Red Hat, Inc.
+   Copyright (C) 2005-2010, 2018 Red Hat, Inc.
    This file is part of elfutils.
 
    This file is free software; you can redistribute it and/or modify
@@ -53,7 +53,7 @@ dwarf_formref_die (Dwarf_Attribute *attr, Dwarf_Die *result)
                          : cu->offset_size);
 
       Dwarf *dbg_ret = (attr->form == DW_FORM_GNU_ref_alt
-                       ? cu->dbg->alt_dwarf : cu->dbg);
+                       ? INTUSE(dwarf_getalt) (cu->dbg) : cu->dbg);
 
       if (dbg_ret == NULL)
        {
index 83abd53d341e86c53712482ddb160bbe0307e7b8..4eae0edef71074784afef56153f3ba0602076b4d 100644 (file)
@@ -1,5 +1,5 @@
 /* Return string associated with given attribute.
-   Copyright (C) 2003-2010, 2013 Red Hat, Inc.
+   Copyright (C) 2003-2010, 2013, 2018 Red Hat, Inc.
    This file is part of elfutils.
    Written by Ulrich Drepper <drepper@redhat.com>, 2003.
 
@@ -48,7 +48,8 @@ dwarf_formstring (Dwarf_Attribute *attrp)
     return (const char *) attrp->valp;
 
   Dwarf *dbg = attrp->cu->dbg;
-  Dwarf *dbg_ret = attrp->form == DW_FORM_GNU_strp_alt ? dbg->alt_dwarf : dbg;
+  Dwarf *dbg_ret = (attrp->form == DW_FORM_GNU_strp_alt
+                   ? INTUSE(dwarf_getalt) (dbg) : dbg);
 
   if (unlikely (dbg_ret == NULL))
     {
index cc434f031083b672fed39c0405bf37dc72b5d34a..3e5af151260a3833443f07b6fc71ed831b50af35 100644 (file)
@@ -1,5 +1,5 @@
 /* Retrieves the DWARF descriptor for debugaltlink data.
-   Copyright (C) 2014 Red Hat, Inc.
+   Copyright (C) 2014, 2018 Red Hat, Inc.
    This file is part of elfutils.
 
    This file is free software; you can redistribute it and/or modify
 #endif
 
 #include "libdwP.h"
+#include "libelfP.h"
+#include "libdwelfP.h"
+#include "system.h"
+
+#include <inttypes.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+
+char *
+internal_function
+__libdw_filepath (int fd, const char *dir, const char *file)
+{
+  if (file == NULL)
+    return NULL;
+
+  if (file[0] == '/')
+    return strdup (file);
+
+  if (dir != NULL && dir[0] == '/')
+    {
+      size_t dirlen = strlen (dir);
+      size_t filelen = strlen (file);
+      size_t len = dirlen + 1 + filelen + 1;
+      char *path = malloc (len);
+      if (path != NULL)
+       {
+         char *c = mempcpy (path, dir, dirlen);
+         if (dir[dirlen - 1] != '/')
+           *c++ = '/';
+         mempcpy (c, file, filelen + 1);
+       }
+      return path;
+    }
+
+  if (fd >= 0)
+    {
+      /* strlen ("/proc/self/fd/") = 14 + strlen (<MAXINT>) = 10 + 1 = 25.  */
+      char devfdpath[25];
+      sprintf (devfdpath, "/proc/self/fd/%u", fd);
+      char *fdpath = realpath (devfdpath, NULL);
+      char *path = NULL;
+      char *fddir;
+      if (fdpath != NULL && fdpath[0] == '/'
+         && (fddir = strrchr (fdpath, '/')) != NULL)
+       {
+         *++fddir = '\0';
+         size_t fdpathlen = strlen (fdpath);
+         size_t dirlen = dir != NULL ? strlen (dir) : 0;
+         size_t filelen = strlen (file);
+         size_t len = fdpathlen + 1 + dirlen + 1 + filelen + 1;
+         path = malloc (len);
+         if (path != NULL)
+           {
+             char *c = mempcpy (path, fdpath, fdpathlen);
+             if (dirlen > 0)
+               {
+                 c = mempcpy (c, dir, dirlen);
+                 if (dir[dirlen - 1] != '/')
+                   *c++ = '/';
+               }
+             mempcpy (c, file, filelen + 1);
+           }
+       }
+      free (fdpath);
+      return path;
+    }
+
+  return NULL;
+}
+
+static void
+find_debug_altlink (Dwarf *dbg)
+{
+  const char *altname;
+  const void *build_id;
+  ssize_t build_id_len = INTUSE(dwelf_dwarf_gnu_debugaltlink) (dbg,
+                                                              &altname,
+                                                              &build_id);
+
+  /* Couldn't even get the debugaltlink.  It probably doesn't exist.  */
+  if (build_id_len <= 0)
+    return;
+
+  const uint8_t *id = (const uint8_t *) build_id;
+  size_t id_len = build_id_len;
+  int fd = -1;
+
+  /* We only look in the standard path.  And relative to the dbg file.  */
+#define DEBUGINFO_PATH "/usr/lib/debug"
+
+  /* We don't handle very short or really large build-ids.  We need at
+     at least 3 and allow for up to 64 (normally ids are 20 long).  */
+#define MIN_BUILD_ID_BYTES 3
+#define MAX_BUILD_ID_BYTES 64
+  if (id_len >= MIN_BUILD_ID_BYTES && id_len <= MAX_BUILD_ID_BYTES)
+    {
+      /* Note sizeof a string literal includes the trailing zero.  */
+      char id_path[sizeof DEBUGINFO_PATH - 1 + sizeof "/.build-id/" - 1
+                  + 2 + 1 + (MAX_BUILD_ID_BYTES - 2) * 2 + sizeof ".debug"];
+      sprintf (&id_path[0], "%s%s", DEBUGINFO_PATH, "/.build-id/");
+      sprintf (&id_path[sizeof DEBUGINFO_PATH - 1 + sizeof "/.build-id/" - 1],
+              "%02" PRIx8 "/", (uint8_t) id[0]);
+      for (size_t i = 1; i < id_len; ++i)
+       sprintf (&id_path[sizeof DEBUGINFO_PATH - 1 + sizeof "/.build-id/" - 1
+                         + 3 + (i - 1) * 2], "%02" PRIx8, (uint8_t) id[i]);
+      strcpy (&id_path[sizeof DEBUGINFO_PATH - 1 + sizeof "/.build-id/" - 1
+                      + 3 + (id_len - 1) * 2], ".debug");
+
+      fd = TEMP_FAILURE_RETRY (open (id_path, O_RDONLY));
+    }
+
+  /* Fall back on (possible relative) alt file path.  */
+  if (fd < 0)
+    {
+      char *altpath = __libdw_filepath (dbg->elf->fildes, NULL, altname);
+      if (altpath != NULL)
+       {
+         fd = TEMP_FAILURE_RETRY (open (altpath, O_RDONLY));
+         free (altpath);
+       }
+    }
+
+  if (fd >= 0)
+    {
+      Dwarf *alt = dwarf_begin (fd, O_RDONLY);
+      if (alt != NULL)
+       {
+         dbg->alt_dwarf = alt;
+         dbg->alt_fd = fd;
+       }
+      else
+       close (fd);
+    }
+}
 
 Dwarf *
 dwarf_getalt (Dwarf *main)
 {
-  if (main == NULL)
+  /* Only try once.  */
+  if (main == NULL || main->alt_dwarf == (void *) -1)
     return NULL;
+
+  if (main->alt_dwarf != NULL)
+    return main->alt_dwarf;
+
+  find_debug_altlink (main);
+
+  /* If we found nothing, make sure we don't try again.  */
+  if (main->alt_dwarf == NULL)
+    {
+      main->alt_dwarf = (void *) -1;
+      return NULL;
+    }
+
   return main->alt_dwarf;
 }
 INTDEF (dwarf_getalt)
index 9bd566ff31f3890f82b7697d0dad9f98c65b1b8a..9051b8e0d79d21bca6a2704dac2b00b76b56b73e 100644 (file)
@@ -1,5 +1,5 @@
 /* Provides the data referenced by the .gnu_debugaltlink section.
-   Copyright (C) 2014 Red Hat, Inc.
+   Copyright (C) 2014, 2018 Red Hat, Inc.
    This file is part of elfutils.
 
    This file is free software; you can redistribute it and/or modify
 
 #include "libdwP.h"
 
+#include <unistd.h>
+
 void
 dwarf_setalt (Dwarf *main, Dwarf *alt)
 {
+  if (main->alt_fd != -1)
+    {
+      INTUSE(dwarf_end) (main->alt_dwarf);
+      close (main->alt_fd);
+      main->alt_fd = -1;
+    }
+
   main->alt_dwarf = alt;
 }
 INTDEF (dwarf_setalt)
index e42c087397bb2e0ec6fe2aff0ac9b4c5948af0a2..1dcc81531489bd10593786fbdbe2f09c654b1bd3 100644 (file)
@@ -1,5 +1,5 @@
 /* Interfaces for libdw.
-   Copyright (C) 2002-2010, 2013, 2014, 2016 Red Hat, Inc.
+   Copyright (C) 2002-2010, 2013, 2014, 2016, 2018 Red Hat, Inc.
    This file is part of elfutils.
 
    This file is free software; you can redistribute it and/or modify
@@ -250,7 +250,9 @@ extern Elf *dwarf_getelf (Dwarf *dwarf);
 extern Dwarf *dwarf_cu_getdwarf (Dwarf_CU *cu);
 
 /* Retrieves the DWARF descriptor for debugaltlink data.  Returns NULL
-   if no alternate debug data has been supplied.  */
+   if no alternate debug data has been supplied yet.  libdw will try
+   to set the alt file on first use of an alt FORM if not yet explicitly
+   provided by dwarf_setalt.  */
 extern Dwarf *dwarf_getalt (Dwarf *main);
 
 /* Provides the data referenced by the .gnu_debugaltlink section.  The
@@ -258,7 +260,9 @@ extern Dwarf *dwarf_getalt (Dwarf *main);
    same build ID).  It is the responsibility of the caller to ensure
    that the data referenced by ALT stays valid while it is used by
    MAIN, until dwarf_setalt is called on MAIN with a different
-   descriptor, or dwarf_end.  */
+   descriptor, or dwarf_end.  Must be called before inspecting DIEs
+   that might have alt FORMs.  Otherwise libdw will try to set the
+   alt file itself on first use.  */
 extern void dwarf_setalt (Dwarf *main, Dwarf *alt);
 
 /* Release debugging handling context.  */
index fbb8ed08e4bf7e1add23d305fb22436f8f6f226c..0681aa15e6d3769e1877329377ec21962d1185a8 100644 (file)
@@ -1,5 +1,5 @@
 /* Internal definitions for libdwarf.
-   Copyright (C) 2002-2011, 2013-2017 Red Hat, Inc.
+   Copyright (C) 2002-2011, 2013-2018 Red Hat, Inc.
    This file is part of elfutils.
    Written by Ulrich Drepper <drepper@redhat.com>, 2002.
 
@@ -154,6 +154,10 @@ struct Dwarf
   /* If true, we allocated the ELF descriptor ourselves.  */
   bool free_elf;
 
+  /* If >= 0, we allocated the alt_dwarf ourselves and must end it and
+     close this file descriptor.  */
+  int alt_fd;
+
   /* Information for traversing the .debug_pubnames section.  This is
      an array and separately allocated with malloc.  */
   struct pubnames_s
@@ -760,6 +764,22 @@ int __libdw_getsrclines (Dwarf *dbg, Dwarf_Off debug_line_offset,
 /* Load and return value of DW_AT_comp_dir from CUDIE.  */
 const char *__libdw_getcompdir (Dwarf_Die *cudie);
 
+/* Given a file descriptor, dir and file returns a full path.  If the
+   file is absolute (starts with a /) a copy of file is returned.  If
+   the file isn't absolute, but dir is absolute, then a path that is
+   the concatenation of dir and file is returned.  If neither file,
+   nor dir is absolute, the path will be constructed using dir (if not
+   NULL) and file relative to the path of the given file descriptor
+   (if valid).
+
+   The file descriptor may be -1 and the dir may be NULL (in which
+   case they aren't used). If file is NULL, or no full path can be
+   constructed NULL is returned.
+
+   The caller is responsible for freeing the result if not NULL.  */
+char * filepath (int fd, const char *dir, const char *file)
+  internal_function;
+
 
 /* Aliases to avoid PLTs.  */
 INTDECL (dwarf_aggregate_size)
index be203adf80eada493bc05685b1f109538a6e2212..89c531b422b3ac79f33d979d7163dbe8d256cde2 100644 (file)
@@ -1,3 +1,10 @@
+2018-01-22  Mark Wielaard  <mark@klomp.org>
+
+       * allfcts.c (setup_alt): Print warning when alt file couldn't be
+       found.
+       * run-allfcts-multi.sh: Add testcase where alt file is in a subdir
+       where it cannot be found by allfcts itself (but it can by libdw).
+
 2018-01-25  Mark Wielaard  <mark@klomp.org>
 
        * elfstrmerge.c (main): Initialize and check symtabshdr instead of
index d3c8d26ad539b0c13f0cade2228e289c9f7ec537..f637311741bb9eaf688c680c1d675b08a1ca1333 100644 (file)
@@ -51,7 +51,10 @@ setup_alt (Dwarf *main)
     errx (1, "dwelf_dwarf_gnu_debugaltlink: %s", dwarf_errmsg (-1));
   int fd = open (alt_name, O_RDONLY);
   if (fd < 0)
-    err (1, "open (%s)", alt_name);
+    {
+      printf ("Warning: no alt file found.\n");
+      return NULL;
+    }
   Dwarf *dbg_alt = dwarf_begin (fd, DWARF_C_READ);
   if (dbg_alt == NULL)
     errx (1, "dwarf_begin (%s): %s", alt_name, dwarf_errmsg (-1));
index 727b76ee5c721c1bc5a89a27ce0cfcf2f41b908c..ef7bb30e5282ab3b1048b5c8a3ce454f67e53d18 100755 (executable)
@@ -53,4 +53,20 @@ testrun_compare cat allfcts.out <<\EOF
 /tmp/test-offset-loop.c:7:main
 EOF
 
+# allfcts has a too simple mechanism for setting the alt file.
+# check that if we don't set it, things still work (because libdw will
+# find the alt file for us).
+mkdir subdir
+mv test-offset-loop test-offset-loop.alt subdir/
+testrun ${abs_builddir}/allfcts subdir/test-offset-loop > allfcts.out
+testrun_compare cat allfcts.out <<\EOF
+Warning: no alt file found.
+/tmp/test-offset-loop.c:6:get_errno
+/tmp/test-offset-loop.c:5:is_error
+/tmp/test-offset-loop.c:4:padding
+/tmp/test-offset-loop.c:7:main
+EOF
+
+rm -rf subdir
+
 exit 0