summaryrefslogtreecommitdiffstats
path: root/libdw/dwarf_getalt.c
diff options
context:
space:
mode:
authorMark Wielaard <[email protected]>2018-01-19 23:59:21 +0100
committerMark Wielaard <[email protected]>2018-01-25 16:30:47 +0100
commit240a068fcf3eb6bbcda525f80c8778de62621d9e (patch)
treee6ed4947e17dc97c77e99ddc1d649231c92c49e8 /libdw/dwarf_getalt.c
parent4407903a7433327dbeda74519d036fc82ef64de3 (diff)
libdw: Resolve alt file on first use.
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 <[email protected]>
Diffstat (limited to 'libdw/dwarf_getalt.c')
-rw-r--r--libdw/dwarf_getalt.c158
1 files changed, 156 insertions, 2 deletions
diff --git a/libdw/dwarf_getalt.c b/libdw/dwarf_getalt.c
index cc434f03..3e5af151 100644
--- a/libdw/dwarf_getalt.c
+++ b/libdw/dwarf_getalt.c
@@ -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
@@ -31,12 +31,166 @@
#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)