diff options
Diffstat (limited to 'libdw/dwarf_getalt.c')
-rw-r--r-- | libdw/dwarf_getalt.c | 158 |
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) |