diff options
author | Mark Wielaard <mjw@redhat.com> | 2012-06-22 12:02:45 +0200 |
---|---|---|
committer | Mark Wielaard <mjw@redhat.com> | 2012-08-09 16:32:43 +0200 |
commit | 775375e3bd177cb19acb5020b6e0917551807276 (patch) | |
tree | 26ec5e40f426d5ace9fd636818197cd0025b672a /libdw/dwarf_begin_elf.c | |
parent | 836db608432ba50f76c0a63ec7bd05694341a1d4 (diff) | |
download | elfutils-775375e3bd177cb19acb5020b6e0917551807276.tar.gz |
libdw: Add support for DWZ multifile forms DW_FORM_GNU_ref_alt/strp_alt.
DWZ multifile forms http://www.dwarfstd.org/ShowIssue.php?issue=120604.1
DW_FORM_GNU_ref_alt and DW_FORM_GNU_strp_alt reference an alternative
debuginfo file. dwarf_begin and dwarf_begin_elf will try to use this
automatically. There are no user visible changes to the libdw interface.
dwarf_formref_die, dwarf_formstring and dwarf_formudata can now return
a Dwarf_Die which comes from a CU in the alternative Dwarf descriptor.
__libdw_read_offset was adjusted to take an alternative Dwarf descriptor
into account.
Signed-off-by: Mark Wielaard <mjw@redhat.com>
Diffstat (limited to 'libdw/dwarf_begin_elf.c')
-rw-r--r-- | libdw/dwarf_begin_elf.c | 123 |
1 files changed, 123 insertions, 0 deletions
diff --git a/libdw/dwarf_begin_elf.c b/libdw/dwarf_begin_elf.c index 3e01800a..fd957701 100644 --- a/libdw/dwarf_begin_elf.c +++ b/libdw/dwarf_begin_elf.c @@ -31,12 +31,17 @@ # include <config.h> #endif +#include <assert.h> +#include <inttypes.h> #include <stdbool.h> #include <stddef.h> #include <stdlib.h> +#include <stdio.h> #include <string.h> #include <unistd.h> +#include <sys/types.h> #include <sys/stat.h> +#include <fcntl.h> #include "libdwP.h" @@ -66,6 +71,110 @@ static const char dwarf_scnnames[IDX_last][17] = }; #define ndwarf_scnnames (sizeof (dwarf_scnnames) / sizeof (dwarf_scnnames[0])) +internal_function int +__check_build_id (Dwarf *dw, const uint8_t *build_id, const size_t id_len) +{ + if (dw == NULL) + return -1; + + Elf *elf = dw->elf; + Elf_Scn *scn = elf_nextscn (elf, NULL); + if (scn == NULL) + return -1; + + do + { + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + if (likely (shdr != NULL) && shdr->sh_type == SHT_NOTE) + { + size_t pos = 0; + GElf_Nhdr nhdr; + size_t name_pos; + size_t desc_pos; + Elf_Data *data = elf_getdata (scn, NULL); + while ((pos = gelf_getnote (data, pos, &nhdr, &name_pos, + &desc_pos)) > 0) + if (nhdr.n_type == NT_GNU_BUILD_ID + && nhdr.n_namesz == sizeof "GNU" + && ! memcmp (data->d_buf + name_pos, "GNU", sizeof "GNU")) + return (nhdr.n_descsz == id_len + && ! memcmp (data->d_buf + desc_pos, + build_id, id_len)) ? 0 : 1; + } + } + while ((scn = elf_nextscn (elf, scn)) != NULL); + + return -1; +} + +/* Try to open an debug alt link by name, checking build_id. + Marks free_alt on success, return NULL on failure. */ +static Dwarf * +try_debugaltlink (Dwarf *result, const char *try_name, + const uint8_t *build_id, const size_t id_len) +{ + int fd = open (try_name, O_RDONLY); + if (fd > 0) + { + result->alt_dwarf = INTUSE (dwarf_begin) (fd, DWARF_C_READ); + if (result->alt_dwarf != NULL) + { + Elf *elf = result->alt_dwarf->elf; + if (__check_build_id (result->alt_dwarf, build_id, id_len) == 0 + && elf_cntl (elf, ELF_C_FDREAD) == 0) + { + close (fd); + result->free_alt = 1; + return result; + } + INTUSE (dwarf_end) (result->alt_dwarf); + } + close (fd); + } + return NULL; +} + +/* For dwz multifile support, ignore if it looks wrong. */ +static Dwarf * +open_debugaltlink (Dwarf *result, const char *alt_name, + const uint8_t *build_id, const size_t id_len) +{ + /* First try the name itself, it is either an absolute path or + a relative one. Sadly we don't know relative from where at + this point. */ + if (try_debugaltlink (result, alt_name, build_id, id_len) != NULL) + return result; + + /* Lets try based on the build-id. This is somewhat distro specific, + we are following the Fedora implementation described at + https://fedoraproject.org/wiki/Releases/FeatureBuildId#Find_files_by_build_ID + */ +#define DEBUG_PREFIX "/usr/lib/debug/.build-id/" +#define PREFIX_LEN sizeof (DEBUG_PREFIX) + char id_name[PREFIX_LEN + 1 + id_len * 2 + sizeof ".debug" - 1]; + strcpy (id_name, DEBUG_PREFIX); + int n = snprintf (&id_name[PREFIX_LEN - 1], + 4, "%02" PRIx8 "/", (uint8_t) build_id[0]); + assert (n == 3); + for (size_t i = 1; i < id_len; ++i) + { + n = snprintf (&id_name[PREFIX_LEN - 1 + 3 + (i - 1) * 2], + 3, "%02" PRIx8, (uint8_t) build_id[i]); + assert (n == 2); + } + strcpy (&id_name[PREFIX_LEN - 1 + 3 + (id_len - 1) * 2], + ".debug"); + + if (try_debugaltlink (result, id_name, build_id, id_len)) + return result; + + /* Everything failed, mark this Dwarf as not having an alternate, + but don't fail the load. The user may want to set it by hand + before usage. */ + result->alt_dwarf = NULL; + return result; +} static Dwarf * check_section (Dwarf *result, GElf_Ehdr *ehdr, Elf_Scn *scn, bool inscngrp) @@ -110,6 +219,20 @@ check_section (Dwarf *result, GElf_Ehdr *ehdr, Elf_Scn *scn, bool inscngrp) return NULL; } + /* For dwz multifile support, ignore if it looks wrong. */ + if (strcmp (scnname, ".gnu_debugaltlink") == 0) + { + Elf_Data *data = elf_getdata (scn, NULL); + if (data != NULL && data->d_size != 0) + { + const char *alt_name = data->d_buf; + const void *build_id = memchr (data->d_buf, '\0', data->d_size); + const int id_len = data->d_size - (build_id - data->d_buf + 1); + if (alt_name && build_id && id_len > 0) + return open_debugaltlink (result, alt_name, build_id + 1, id_len); + } + } + /* Recognize the various sections. Most names start with .debug_. */ size_t cnt; |