diff options
author | Mark Wielaard <mjw@redhat.com> | 2014-05-01 14:48:27 +0200 |
---|---|---|
committer | Mark Wielaard <mjw@redhat.com> | 2014-05-02 17:00:48 +0200 |
commit | df85bf99021119fcbb2ced66dd69f1cceafb180c (patch) | |
tree | a0ee033052e69b9b00e87ee31bf4962dac3cec17 | |
parent | d81d32d2a4f92355e4c677b578147dfe819251b9 (diff) | |
download | elfutils-df85bf99021119fcbb2ced66dd69f1cceafb180c.tar.gz |
libdwfl: Move dwz alt multi file searching to find_debuginfo callback.
Don't hard code the Dwarf dwz alt multi file search but allow the user
to override it through the standard Dwfl_Callbacks. Also move ownership
completely to the user of dwarf_setalt by removing free_alt from Dwarf
and adding alt, fd and elf fields to Dwfl_Module. Add a relative .dwz
file test case.
Signed-off-by: Mark Wielaard <mjw@redhat.com>
-rw-r--r-- | libdw/ChangeLog | 6 | ||||
-rw-r--r-- | libdw/dwarf_end.c | 6 | ||||
-rw-r--r-- | libdw/dwarf_setalt.c | 3 | ||||
-rw-r--r-- | libdw/libdwP.h | 5 | ||||
-rw-r--r-- | libdwfl/ChangeLog | 33 | ||||
-rw-r--r-- | libdwfl/dwfl_build_id_find_debuginfo.c | 55 | ||||
-rw-r--r-- | libdwfl/dwfl_build_id_find_elf.c | 32 | ||||
-rw-r--r-- | libdwfl/dwfl_module.c | 14 | ||||
-rw-r--r-- | libdwfl/dwfl_module_getdwarf.c | 208 | ||||
-rw-r--r-- | libdwfl/find-debuginfo.c | 98 | ||||
-rw-r--r-- | libdwfl/libdwflP.h | 10 | ||||
-rw-r--r-- | libdwfl/linux-kernel-modules.c | 11 | ||||
-rw-r--r-- | src/ChangeLog | 5 | ||||
-rw-r--r-- | src/readelf.c | 34 | ||||
-rw-r--r-- | tests/ChangeLog | 5 | ||||
-rwxr-xr-x | tests/run-readelf-dwz-multi.sh | 105 |
16 files changed, 461 insertions, 169 deletions
diff --git a/libdw/ChangeLog b/libdw/ChangeLog index 94ef03ca..960c8315 100644 --- a/libdw/ChangeLog +++ b/libdw/ChangeLog @@ -1,3 +1,9 @@ +2014-05-01 Mark Wielaard <mjw@redhat.com> + + * libdwP.h (struct Dwarf): Remove free_alt. + * dwarf_end.c (dwarf_end): Don't check free_alt, don't end alt_dwarf. + * dwarf_setalt.c (dwarf_setalt): Don't check or set free_alt. + 2014-04-30 Mark Wielaard <mjw@redhat.com> * libdw.map (ELFUTILS_0.159): Add dwelf_elf_gnu_build_id. diff --git a/libdw/dwarf_end.c b/libdw/dwarf_end.c index e65314ab..241a257a 100644 --- a/libdw/dwarf_end.c +++ b/libdw/dwarf_end.c @@ -1,5 +1,5 @@ /* Release debugging handling context. - Copyright (C) 2002-2011 Red Hat, Inc. + Copyright (C) 2002-2011, 2014 Red Hat, Inc. This file is part of elfutils. Written by Ulrich Drepper <drepper@redhat.com>, 2002. @@ -111,10 +111,6 @@ dwarf_end (dwarf) if (dwarf->free_elf) elf_end (dwarf->elf); - /* Free the alternative Dwarf descriptor if necessary. */ - if (dwarf->free_alt) - INTUSE (dwarf_end) (dwarf->alt_dwarf); - /* Free the context descriptor. */ free (dwarf); } diff --git a/libdw/dwarf_setalt.c b/libdw/dwarf_setalt.c index 3b5b9353..9bd566ff 100644 --- a/libdw/dwarf_setalt.c +++ b/libdw/dwarf_setalt.c @@ -35,9 +35,6 @@ void dwarf_setalt (Dwarf *main, Dwarf *alt) { - if (main->free_alt) - INTUSE (dwarf_end) (main->alt_dwarf); - main->free_alt = false; main->alt_dwarf = alt; } INTDEF (dwarf_setalt) diff --git a/libdw/libdwP.h b/libdw/libdwP.h index 1c947673..41361350 100644 --- a/libdw/libdwP.h +++ b/libdw/libdwP.h @@ -1,5 +1,5 @@ /* Internal definitions for libdwarf. - Copyright (C) 2002-2011, 2013 Red Hat, Inc. + Copyright (C) 2002-2011, 2013, 2014 Red Hat, Inc. This file is part of elfutils. Written by Ulrich Drepper <drepper@redhat.com>, 2002. @@ -147,9 +147,6 @@ struct Dwarf /* If true, we allocated the ELF descriptor ourselves. */ bool free_elf; - /* If true, we allocated the Dwarf descriptor for alt_dwarf ourselves. */ - bool free_alt; - /* Information for traversing the .debug_pubnames section. This is an array and separately allocated with malloc. */ struct pubnames_s diff --git a/libdwfl/ChangeLog b/libdwfl/ChangeLog index 7e4234db..a33bd25a 100644 --- a/libdwfl/ChangeLog +++ b/libdwfl/ChangeLog @@ -1,3 +1,36 @@ +2014-05-01 Mark Wielaard <mjw@redhat.com> + + * libdwflP.h (struct Dwfl_Module): Add alt, alt_fd and alt_elf fields. + (__libdwfl_open_mod_by_build_id): Renamed __libdwfl_open_by_build_id. + (__libdwfl_open_by_build_id): New declaration that takes an explicit + build-id. + * dwfl_build_id_find_debuginfo.c (dwfl_build_id_find_debuginfo): If + we already have the Dwarf then look for the alt dwz multi file by + build-id. + * dwfl_build_id_find_elf.c (__libdwfl_open_by_build_id): Add the + build-id we are looking for as argument. + (__libdwfl_open_mod_by_build_id): New function, calls + __libdwfl_open_by_build_id. + (dwfl_build_id_find_elf): Call __libdwfl_open_mod_by_build_id. + * dwfl_module.c (__libdwfl_module_free): Release alt, alt_elf and + close alt_fd if necessary. + * dwfl_module_getdwarf.c (__check_build_id): Removed. + (try_debugaltlink): Removed. + (open_debugaltlink): Removed. + (open_elf_file): First half of open_elf that just opens the elf + file but doesn't setup the load address. + (open_elf): Call open_elf_file. + (find_debug_altlink): New function. + (load_dw): Remove loading of dwz multifile. + (find_dw): Call find_debug_altlink. + * find-debuginfo.c (validate): Handle alt debug case using + dwelf_dwarf_gnu_debugaltlink and mod->alt_elf. + (find_debuginfo_in_path): Handle alt debug files possibly in .dwz + subdirs. + * linux-kernel-modules.c (try_kernel_name): Use fakemod.debug.name + to store name to find by dwfl_standard_find_debuginfo instead of + allocating an extra variable on stack. + 2014-04-30 Mark Wielaard <mjw@redhat.com> * dwfl_module_build_id.c (__libdwfl_find_elf_build_id): Moved to diff --git a/libdwfl/dwfl_build_id_find_debuginfo.c b/libdwfl/dwfl_build_id_find_debuginfo.c index a955735c..f1c64bcd 100644 --- a/libdwfl/dwfl_build_id_find_debuginfo.c +++ b/libdwfl/dwfl_build_id_find_debuginfo.c @@ -1,5 +1,5 @@ /* Find the debuginfo file for a module from its build ID. - Copyright (C) 2007, 2009 Red Hat, Inc. + Copyright (C) 2007, 2009, 2014 Red Hat, Inc. This file is part of elfutils. This file is free software; you can redistribute it and/or modify @@ -41,10 +41,61 @@ dwfl_build_id_find_debuginfo (Dwfl_Module *mod, char **debuginfo_file_name) { int fd = -1; + + /* Are we looking for a separate debug file for the main file or for + an alternate (dwz multi) debug file? Alternatively we could check + whether the dwbias == -1. */ + if (mod->dw != NULL) + { + const void *build_id; + const char *altname; + ssize_t build_id_len = INTUSE(dwelf_dwarf_gnu_debugaltlink) (mod->dw, + &altname, + &build_id); + if (build_id_len > 0) + fd = __libdwfl_open_by_build_id (mod, true, debuginfo_file_name, + build_id_len, build_id); + + if (fd >= 0) + { + /* We need to open an Elf handle on the file so we can check its + build ID note for validation. Backdoor the handle into the + module data structure since we had to open it early anyway. */ + Dwfl_Error error = __libdw_open_file (&fd, &mod->alt_elf, + true, false); + if (error != DWFL_E_NOERROR) + __libdwfl_seterrno (error); + else + { + const void *alt_build_id; + ssize_t alt_len = INTUSE(dwelf_elf_gnu_build_id) (mod->alt_elf, + &alt_build_id); + if (alt_len > 0 && alt_len == build_id_len + && memcmp (build_id, alt_build_id, alt_len) == 0) + return fd; + else + { + /* A mismatch! */ + elf_end (mod->alt_elf); + mod->alt_elf = NULL; + close (fd); + fd = -1; + } + free (*debuginfo_file_name); + *debuginfo_file_name = NULL; + errno = 0; + } + } + return fd; + } + + /* We don't even have the Dwarf yet and it isn't in the main file. + Try to find separate debug file now using the module build id. */ const unsigned char *bits; GElf_Addr vaddr; + if (INTUSE(dwfl_module_build_id) (mod, &bits, &vaddr) > 0) - fd = __libdwfl_open_by_build_id (mod, true, debuginfo_file_name); + fd = __libdwfl_open_mod_by_build_id (mod, true, debuginfo_file_name); if (fd >= 0) { /* We need to open an Elf handle on the file so we can check its diff --git a/libdwfl/dwfl_build_id_find_elf.c b/libdwfl/dwfl_build_id_find_elf.c index a4f03263..15550082 100644 --- a/libdwfl/dwfl_build_id_find_elf.c +++ b/libdwfl/dwfl_build_id_find_elf.c @@ -1,5 +1,5 @@ /* Find an ELF file for a module from its build ID. - Copyright (C) 2007-2010 Red Hat, Inc. + Copyright (C) 2007-2010, 2014 Red Hat, Inc. This file is part of elfutils. This file is free software; you can redistribute it and/or modify @@ -34,17 +34,9 @@ int internal_function -__libdwfl_open_by_build_id (Dwfl_Module *mod, bool debug, char **file_name) +__libdwfl_open_by_build_id (Dwfl_Module *mod, bool debug, char **file_name, + const size_t id_len, const uint8_t *id) { - /* If *FILE_NAME was primed into the module, leave it there - as the fallback when we have nothing to offer. */ - errno = 0; - if (mod->build_id_len <= 0) - return -1; - - const size_t id_len = mod->build_id_len; - const uint8_t *id = mod->build_id_bits; - /* Search debuginfo_path directories' .build-id/ subdirectories. */ char id_name[sizeof "/.build-id/" + 1 + id_len * 2 + sizeof ".debug" - 1]; @@ -109,6 +101,22 @@ __libdwfl_open_by_build_id (Dwfl_Module *mod, bool debug, char **file_name) } int +internal_function +__libdwfl_open_mod_by_build_id (Dwfl_Module *mod, bool debug, char **file_name) +{ + /* If *FILE_NAME was primed into the module, leave it there + as the fallback when we have nothing to offer. */ + errno = 0; + if (mod->build_id_len <= 0) + return -1; + + const size_t id_len = mod->build_id_len; + const uint8_t *id = mod->build_id_bits; + + return __libdwfl_open_by_build_id (mod, debug, file_name, id_len, id); +} + +int dwfl_build_id_find_elf (Dwfl_Module *mod, void **userdata __attribute__ ((unused)), const char *modname __attribute__ ((unused)), @@ -133,7 +141,7 @@ dwfl_build_id_find_elf (Dwfl_Module *mod, close (fd); } } - int fd = __libdwfl_open_by_build_id (mod, false, file_name); + int fd = __libdwfl_open_mod_by_build_id (mod, false, file_name); if (fd >= 0) { Dwfl_Error error = __libdw_open_file (&fd, elfp, true, false); diff --git a/libdwfl/dwfl_module.c b/libdwfl/dwfl_module.c index bb167ab2..8efcfaa5 100644 --- a/libdwfl/dwfl_module.c +++ b/libdwfl/dwfl_module.c @@ -1,5 +1,5 @@ /* Maintenance of module list in libdwfl. - Copyright (C) 2005, 2006, 2007, 2008 Red Hat, Inc. + Copyright (C) 2005, 2006, 2007, 2008, 2014 Red Hat, Inc. This file is part of elfutils. This file is free software; you can redistribute it and/or modify @@ -71,7 +71,17 @@ __libdwfl_module_free (Dwfl_Module *mod) } if (mod->dw != NULL) - INTUSE(dwarf_end) (mod->dw); + { + INTUSE(dwarf_end) (mod->dw); + if (mod->alt != NULL) + { + INTUSE(dwarf_end) (mod->alt); + if (mod->alt_elf != NULL) + elf_end (mod->alt_elf); + if (mod->alt_fd != -1) + close (mod->alt_fd); + } + } if (mod->ebl != NULL) ebl_closebackend (mod->ebl); diff --git a/libdwfl/dwfl_module_getdwarf.c b/libdwfl/dwfl_module_getdwarf.c index e8087bfb..293e8e75 100644 --- a/libdwfl/dwfl_module_getdwarf.c +++ b/libdwfl/dwfl_module_getdwarf.c @@ -34,99 +34,10 @@ #include "../libdw/libdwP.h" /* DWARF_E_* values are here. */ #include "../libelf/libelfP.h" -#ifdef ENABLE_DWZ -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; - const void *elf_build_id; - ssize_t elf_id_len = INTUSE(dwelf_elf_gnu_build_id) (elf, &elf_build_id); - if (elf_id_len < 0) - return -1; - - return (id_len == (size_t) elf_id_len - && memcmp (build_id, elf_build_id, id_len) == 0) ? 0 : 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) - { - Dwarf *alt_dwarf = INTUSE (dwarf_begin) (fd, DWARF_C_READ); - if (alt_dwarf != NULL) - { - Elf *elf = alt_dwarf->elf; - if (__check_build_id (alt_dwarf, build_id, id_len) == 0 - && elf_cntl (elf, ELF_C_FDREAD) == 0) - { - close (fd); - INTUSE (dwarf_setalt) (result, alt_dwarf); - result->free_alt = true; - 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; -} -#endif /* ENABLE_DWZ */ - -/* Open libelf FILE->fd and compute the load base of ELF as loaded in MOD. - When we return success, FILE->elf and FILE->vaddr are set up. */ static inline Dwfl_Error -open_elf (Dwfl_Module *mod, struct dwfl_file *file) +open_elf_file (Elf **elf, int *fd, char **name) { - if (file->elf == NULL) + if (*elf == NULL) { /* CBFAIL uses errno if it's set, so clear it first in case we don't set it with an open failure below. */ @@ -134,25 +45,36 @@ open_elf (Dwfl_Module *mod, struct dwfl_file *file) /* If there was a pre-primed file name left that the callback left behind, try to open that file name. */ - if (file->fd < 0 && file->name != NULL) - file->fd = TEMP_FAILURE_RETRY (open64 (file->name, O_RDONLY)); + if (*fd < 0 && *name != NULL) + *fd = TEMP_FAILURE_RETRY (open64 (*name, O_RDONLY)); - if (file->fd < 0) + if (*fd < 0) return CBFAIL; - Dwfl_Error error = __libdw_open_file (&file->fd, &file->elf, true, false); - if (error != DWFL_E_NOERROR) - return error; + return __libdw_open_file (fd, elf, true, false); } - else if (unlikely (elf_kind (file->elf) != ELF_K_ELF)) + else if (unlikely (elf_kind (*elf) != ELF_K_ELF)) { - elf_end (file->elf); - file->elf = NULL; - close (file->fd); - file->fd = -1; + elf_end (*elf); + *elf = NULL; + close (*fd); + *fd = -1; return DWFL_E_BADELF; } + /* Elf file already open and looks fine. */ + return DWFL_E_NOERROR; +} + +/* Open libelf FILE->fd and compute the load base of ELF as loaded in MOD. + When we return success, FILE->elf and FILE->vaddr are set up. */ +static inline Dwfl_Error +open_elf (Dwfl_Module *mod, struct dwfl_file *file) +{ + Dwfl_Error error = open_elf_file (&file->elf, &file->fd, &file->name); + if (error != DWFL_E_NOERROR) + return error; + GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (file->elf, &ehdr_mem); if (ehdr == NULL) { @@ -586,6 +508,57 @@ find_debuginfo (Dwfl_Module *mod) return result; } +#ifdef ENABLE_DWZ +/* Try to find the alternative debug link for the given DWARF and set + it if found. Only called when mod->dw is already setup but still + might need an alternative (dwz multi) debug file. filename is either + the main or debug name from which the Dwarf was created. */ +static void +find_debug_altlink (Dwfl_Module *mod, const char *filename) +{ + assert (mod->dw != NULL); + + const char *altname; + const void *build_id; + ssize_t build_id_len = INTUSE(dwelf_dwarf_gnu_debugaltlink) (mod->dw, + &altname, + &build_id); + + if (build_id_len > 0) + { + /* We could store altfile in the module, but don't really need it. */ + char *altfile = NULL; + mod->alt_fd = (*mod->dwfl->callbacks->find_debuginfo) (MODCB_ARGS (mod), + filename, + altname, + 0, + &altfile); + + /* The (internal) callbacks might just set mod->alt_elf directly + because they open the Elf anyway for sanity checking. + Otherwise open either the given file name or use the fd + returned. */ + Dwfl_Error error = open_elf_file (&mod->alt_elf, &mod->alt_fd, + &altfile); + if (error == DWFL_E_NOERROR) + { + mod->alt = INTUSE(dwarf_begin_elf) (mod->alt_elf, + DWARF_C_READ, NULL); + if (mod->alt == NULL) + { + elf_end (mod->alt_elf); + mod->alt_elf = NULL; + close (mod->alt_fd); + mod->alt_fd = -1; + } + else + dwarf_setalt (mod->dw, mod->alt); + } + + free (altfile); /* See above, we don't really need it. */ + } +} +#endif /* ENABLE_DWZ */ /* Try to find a symbol table in FILE. Returns DWFL_E_NOERROR if a proper one is found. @@ -1209,19 +1182,6 @@ load_dw (Dwfl_Module *mod, struct dwfl_file *debugfile) return err == DWARF_E_NO_DWARF ? DWFL_E_NO_DWARF : DWFL_E (LIBDW, err); } -#ifdef ENABLE_DWZ - /* For dwz multifile support, ignore if it looks wrong. */ - { - const void *build_id; - const char *alt_name; - size_t id_len = INTUSE (dwelf_dwarf_gnu_debugaltlink) (mod->dw, - &alt_name, - &build_id); - if (id_len > 0) - open_debugaltlink (mod->dw, alt_name, build_id, id_len); - } -#endif /* ENABLE_DWZ */ - /* Until we have iterated through all CU's, we might do lazy lookups. */ mod->lazycu = 1; @@ -1248,6 +1208,13 @@ find_dw (Dwfl_Module *mod) case DWFL_E_NOERROR: mod->debug.elf = mod->main.elf; mod->debug.address_sync = mod->main.address_sync; + +#ifdef ENABLE_DWZ + /* The Dwarf might need an alt debug file, find that now after + everything about the debug file has been setup (the + find_debuginfo callback might need it). */ + find_debug_altlink (mod, mod->main.name); +#endif /* ENABLE_DWZ */ return; case DWFL_E_NO_DWARF: @@ -1263,6 +1230,17 @@ find_dw (Dwfl_Module *mod) { case DWFL_E_NOERROR: mod->dwerr = load_dw (mod, &mod->debug); + if (mod->dwerr == DWFL_E_NOERROR) + { +#ifdef ENABLE_DWZ + /* The Dwarf might need an alt debug file, find that now after + everything about the debug file has been setup (the + find_debuginfo callback might need it). */ + find_debug_altlink (mod, mod->debug.name); +#endif /* ENABLE_DWZ */ + return; + } + break; case DWFL_E_CB: /* The find_debuginfo hook failed. */ diff --git a/libdwfl/find-debuginfo.c b/libdwfl/find-debuginfo.c index 21db91a4..3f5314ad 100644 --- a/libdwfl/find-debuginfo.c +++ b/libdwfl/find-debuginfo.c @@ -1,5 +1,5 @@ /* Standard find_debuginfo callback for libdwfl. - Copyright (C) 2005-2010 Red Hat, Inc. + Copyright (C) 2005-2010, 2014 Red Hat, Inc. This file is part of elfutils. This file is free software; you can redistribute it and/or modify @@ -84,6 +84,45 @@ check_crc (int fd, GElf_Word debuglink_crc) static bool validate (Dwfl_Module *mod, int fd, bool check, GElf_Word debuglink_crc) { + /* For alt debug files always check the build-id from the Dwarf and alt. */ + if (mod->dw != NULL) + { + bool valid = false; + const void *build_id; + const char *altname; + ssize_t build_id_len = INTUSE(dwelf_dwarf_gnu_debugaltlink) (mod->dw, + &altname, + &build_id); + if (build_id_len > 0) + { + /* We need to open an Elf handle on the file so we can check its + build ID note for validation. Backdoor the handle into the + module data structure since we had to open it early anyway. */ + Dwfl_Error error = __libdw_open_file (&fd, &mod->alt_elf, + false, false); + if (error != DWFL_E_NOERROR) + __libdwfl_seterrno (error); + else + { + const void *alt_build_id; + ssize_t alt_len = INTUSE(dwelf_elf_gnu_build_id) (mod->alt_elf, + &alt_build_id); + if (alt_len > 0 && alt_len == build_id_len + && memcmp (build_id, alt_build_id, alt_len) == 0) + valid = true; + else + { + /* A mismatch! */ + elf_end (mod->alt_elf); + mod->alt_elf = NULL; + close (fd); + fd = -1; + } + } + } + return valid; + } + /* If we have a build ID, check only that. */ if (mod->build_id_len > 0) { @@ -124,7 +163,9 @@ find_debuginfo_in_path (Dwfl_Module *mod, const char *file_name, const char *file_basename = file_name == NULL ? NULL : basename (file_name); if (debuglink_file == NULL) { - if (file_basename == NULL) + /* For a alt debug multi file we need a name, for a separate debug + name we may be able to fall back on file_basename.debug. */ + if (file_basename == NULL || mod->dw != NULL) { errno = 0; return -1; @@ -174,38 +215,67 @@ find_debuginfo_in_path (Dwfl_Module *mod, const char *file_name, check = *p++ == '+'; check = check && cancheck; - const char *dir, *subdir; + const char *dir, *subdir, *file; switch (p[0]) { case '\0': /* An empty entry says to try the main file's directory. */ dir = file_dirname; subdir = NULL; + file = debuglink_file; break; case '/': /* An absolute path says to look there for a subdirectory - named by the main file's absolute directory. - This cannot be applied to a relative file name. */ - if (file_dirname == NULL || file_dirname[0] != '/') + named by the main file's absolute directory. This cannot + be applied to a relative file name. For alt debug files + it means to look for the basename file in that dir or the + .dwz subdir (see below). */ + if (mod->dw == NULL + && (file_dirname == NULL || file_dirname[0] != '/')) continue; dir = p; - subdir = file_dirname + 1; + if (mod->dw == NULL) + { + subdir = file_dirname + 1; + file = debuglink_file; + } + else + { + subdir = NULL; + file = basename (debuglink_file); + } break; default: /* A relative path says to try a subdirectory of that name in the main file's directory. */ dir = file_dirname; subdir = p; + file = debuglink_file; break; } char *fname = NULL; - int fd = try_open (&main_stat, dir, subdir, debuglink_file, &fname); + int fd = try_open (&main_stat, dir, subdir, file, &fname); if (fd < 0) switch (errno) { case ENOENT: case ENOTDIR: + /* If we are looking for the alt file also try the .dwz subdir. + But only if this is the empty or absolute path. */ + if (mod->dw != NULL && (p[0] == '\0' || p[0] == '/')) + { + fd = try_open (&main_stat, dir, ".dwz", + basename (file), &fname); + if (fd < 0) + { + if (errno != ENOENT && errno != ENOTDIR) + return -1; + else + continue; + } + break; + } continue; default: return -1; @@ -240,11 +310,21 @@ dwfl_standard_find_debuginfo (Dwfl_Module *mod, GElf_Addr vaddr; if (INTUSE(dwfl_module_build_id) (mod, &bits, &vaddr) > 0) { + /* Dropping most arguments means we cannot rely on them in + dwfl_build_id_find_debuginfo. But leave it that way since + some user code out there also does this, so we'll have to + handle it anyway. */ int fd = INTUSE(dwfl_build_id_find_debuginfo) (mod, NULL, NULL, 0, NULL, NULL, 0, debuginfo_file_name); - if (fd >= 0 || mod->debug.elf != NULL || errno != 0) + + /* Did the build_id callback find something or report an error? + Then we are done. Otherwise fallback on path based search. */ + if (fd >= 0 + || (mod->dw == NULL && mod->debug.elf != NULL) + || (mod->dw != NULL && mod->alt_elf != NULL) + || errno != 0) return fd; } diff --git a/libdwfl/libdwflP.h b/libdwfl/libdwflP.h index e4ab60e1..9b03d8a7 100644 --- a/libdwfl/libdwflP.h +++ b/libdwfl/libdwflP.h @@ -182,6 +182,9 @@ struct Dwfl_Module Elf_Data *aux_symxndxdata; /* Data in the extended auxiliary table. */ Dwarf *dw; /* libdw handle for its debugging info. */ + Dwarf *alt; /* Dwarf used for dwarf_setalt, or NULL. */ + int alt_fd; /* descriptor, only valid when alt != NULL. */ + Elf *alt_elf; /* Elf for alt Dwarf. */ Dwfl_Error symerr; /* Previous failure to load symbols. */ Dwfl_Error dwerr; /* Previous failure to load DWARF. */ @@ -518,8 +521,13 @@ extern int __libdwfl_find_build_id (Dwfl_Module *mod, bool set, Elf *elf) internal_function; /* Open a main or debuginfo file by its build ID, returns the fd. */ +extern int __libdwfl_open_mod_by_build_id (Dwfl_Module *mod, bool debug, + char **file_name) internal_function; + +/* Same, but takes an explicit build_id, can also be used for alt debug. */ extern int __libdwfl_open_by_build_id (Dwfl_Module *mod, bool debug, - char **file_name) internal_function; + char **file_name, const size_t id_len, + const uint8_t *id) internal_function; extern uint32_t __libdwfl_crc32 (uint32_t crc, unsigned char *buf, size_t len) attribute_hidden; diff --git a/libdwfl/linux-kernel-modules.c b/libdwfl/linux-kernel-modules.c index 53075183..1ad7d2f6 100644 --- a/libdwfl/linux-kernel-modules.c +++ b/libdwfl/linux-kernel-modules.c @@ -1,5 +1,5 @@ /* Standard libdwfl callbacks for debugging the running Linux kernel. - Copyright (C) 2005-2011, 2013 Red Hat, Inc. + Copyright (C) 2005-2011, 2013, 2014 Red Hat, Inc. This file is part of elfutils. This file is free software; you can redistribute it and/or modify @@ -88,23 +88,22 @@ try_kernel_name (Dwfl *dwfl, char **fname, bool try_debug) if (fd < 0) { - char *debugfname = NULL; Dwfl_Module fakemod = { .dwfl = dwfl }; /* First try the file's unadorned basename as DEBUGLINK_FILE, to look for "vmlinux" files. */ fd = INTUSE(dwfl_standard_find_debuginfo) (&fakemod, NULL, NULL, 0, *fname, basename (*fname), 0, - &debugfname); + &fakemod.debug.name); if (fd < 0 && try_debug) /* Next, let the call use the default of basename + ".debug", to look for "vmlinux.debug" files. */ fd = INTUSE(dwfl_standard_find_debuginfo) (&fakemod, NULL, NULL, 0, *fname, NULL, 0, - &debugfname); - if (debugfname != NULL) + &fakemod.debug.name); + if (fakemod.debug.name != NULL) { free (*fname); - *fname = debugfname; + *fname = fakemod.debug.name; } } diff --git a/src/ChangeLog b/src/ChangeLog index 429a7eea..341787d6 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,8 @@ +2014-05-01 Mark Wielaard <mjw@redhat.com> + + * readelf.c (find_no_debuginfo): Call dwfl_standard_find_debuginfo + if looking for alternate debug file. + 2014-04-11 Mark Wielaard <mjw@redhat.com> * Makefile.am (AM_CPPFLAGS): Add -I libdwelf. diff --git a/src/readelf.c b/src/readelf.c index 697a361b..45b19105 100644 --- a/src/readelf.c +++ b/src/readelf.c @@ -693,18 +693,32 @@ process_dwflmod (Dwfl_Module *dwflmod, return DWARF_CB_OK; } -/* Stub libdwfl callback, only the ELF handle already open is ever used. */ +/* Stub libdwfl callback, only the ELF handle already open is ever used. + Only used for finding the alternate debug file if the Dwarf comes from + the main file. We are not interested in separate debuginfo. */ static int -find_no_debuginfo (Dwfl_Module *mod __attribute__ ((unused)), - void **userdata __attribute__ ((unused)), - const char *modname __attribute__ ((unused)), - Dwarf_Addr base __attribute__ ((unused)), - const char *file_name __attribute__ ((unused)), - const char *debuglink_file __attribute__ ((unused)), - GElf_Word debuglink_crc __attribute__ ((unused)), - char **debuginfo_file_name __attribute__ ((unused))) +find_no_debuginfo (Dwfl_Module *mod, + void **userdata, + const char *modname, + Dwarf_Addr base, + const char *file_name, + const char *debuglink_file, + GElf_Word debuglink_crc, + char **debuginfo_file_name) { - return -1; + Dwarf_Addr dwbias; + dwfl_module_info (mod, NULL, NULL, NULL, &dwbias, NULL, NULL, NULL); + + /* We are only interested if the Dwarf has been setup on the main + elf file but is only missing the alternate debug link. If dwbias + hasn't even been setup, this is searching for separate debuginfo + for the main elf. We don't care in that case. */ + if (dwbias == (Dwarf_Addr) -1) + return -1; + + return dwfl_standard_find_debuginfo (mod, userdata, modname, base, + file_name, debuglink_file, + debuglink_crc, debuginfo_file_name); } /* Process one input file. */ diff --git a/tests/ChangeLog b/tests/ChangeLog index ff396d50..f180fe4b 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,3 +1,8 @@ +2014-05-01 Mark Wielaard <mjw@redhat.com> + + * run-readelf-dwz-multi.sh: Add tests with alt debug files in .dwz + subdir. + 2014-04-30 Mark Wielaard <mjw@redhat.com> * buildid.c, buildid.sh, testfile42_noshdrs.bz2: New files. diff --git a/tests/run-readelf-dwz-multi.sh b/tests/run-readelf-dwz-multi.sh index 44d24758..27e0f38c 100755 --- a/tests/run-readelf-dwz-multi.sh +++ b/tests/run-readelf-dwz-multi.sh @@ -136,6 +136,109 @@ DWARF section [28] '.debug_info' at offset 0x1078: type (ref_udata) [ 2b] EOF +# Same as above, but find alt debug file in a .dwz subdir. +mkdir .dwz +mv testfile_multi.dwz .dwz +testrun_compare ${abs_top_builddir}/src/readelf --debug-dump=info testfile_multi_main <<\EOF + +DWARF section [28] '.debug_info' at offset 0x1078: + [Offset] + Compilation unit at offset 0: + Version: 4, Abbreviation section offset: 0, Address size: 8, Offset size: 4 + [ b] compile_unit + producer (strp) "GNU C 4.7.0 20120507 (Red Hat 4.7.0-5) -mtune=generic -march=x86-64 -g" + language (data1) C89 (1) + name (strp) "main.c" + comp_dir (GNU_strp_alt) "/home/mark/src/tests/dwz" + low_pc (addr) 0x00000000004006ac <main> + high_pc (udata) 44 (0x00000000004006d8) + stmt_list (sec_offset) 0 + [ 26] imported_unit + import (GNU_ref_alt) [ b] + [ 2b] pointer_type + byte_size (data1) 8 + type (GNU_ref_alt) [ 53] + [ 31] subprogram + external (flag_present) + name (strp) "main" + decl_file (data1) 1 + decl_line (data1) 3 + prototyped (flag_present) + type (GNU_ref_alt) [ 3e] + low_pc (addr) 0x00000000004006ac <main> + high_pc (udata) 44 (0x00000000004006d8) + frame_base (exprloc) + [ 0] call_frame_cfa + GNU_all_tail_call_sites (flag_present) + sibling (ref_udata) [ 6e] + [ 48] formal_parameter + name (strp) "argc" + decl_file (data1) 1 + decl_line (data1) 3 + type (GNU_ref_alt) [ 3e] + location (exprloc) + [ 0] fbreg -36 + [ 56] formal_parameter + name (strp) "argv" + decl_file (data1) 1 + decl_line (data1) 3 + type (ref_udata) [ 6e] + location (exprloc) + [ 0] fbreg -48 + [ 61] variable + name (string) "b" + decl_file (data1) 1 + decl_line (data1) 5 + type (GNU_ref_alt) [ 5a] + location (exprloc) + [ 0] fbreg -32 + [ 6e] pointer_type + byte_size (data1) 8 + type (ref_udata) [ 2b] +EOF +mv .dwz/testfile_multi.dwz . +rmdir .dwz + +testrun_compare ${abs_top_builddir}/src/readelf --debug-dump=info libtestfile_multi_shared.so <<\EOF + +DWARF section [25] '.debug_info' at offset 0x106c: + [Offset] + Compilation unit at offset 0: + Version: 4, Abbreviation section offset: 0, Address size: 8, Offset size: 4 + [ b] compile_unit + producer (strp) "GNU C 4.7.0 20120507 (Red Hat 4.7.0-5) -fpreprocessed -mtune=generic -march=x86-64 -g -fPIC" + language (data1) C89 (1) + name (strp) "shared.c" + comp_dir (GNU_strp_alt) "/home/mark/src/tests/dwz" + low_pc (addr) +0x0000000000000670 <call_foo> + high_pc (udata) 23 (+0x0000000000000687) + stmt_list (sec_offset) 0 + [ 26] imported_unit + import (GNU_ref_alt) [ b] + [ 2b] subprogram + external (flag_present) + name (strp) "call_foo" + decl_file (data1) 1 + decl_line (data1) 3 + prototyped (flag_present) + type (GNU_ref_alt) [ 3e] + low_pc (addr) +0x0000000000000670 <call_foo> + high_pc (udata) 23 (+0x0000000000000687) + frame_base (exprloc) + [ 0] call_frame_cfa + GNU_all_call_sites (flag_present) + [ 41] formal_parameter + name (string) "fb" + decl_file (data1) 1 + decl_line (data1) 3 + type (GNU_ref_alt) [ 76] + location (exprloc) + [ 0] fbreg -24 +EOF + +# Same as above, but find alt debug file in a .dwz subdir. +mkdir .dwz +mv testfile_multi.dwz .dwz testrun_compare ${abs_top_builddir}/src/readelf --debug-dump=info libtestfile_multi_shared.so <<\EOF DWARF section [25] '.debug_info' at offset 0x106c: @@ -172,6 +275,8 @@ DWARF section [25] '.debug_info' at offset 0x106c: location (exprloc) [ 0] fbreg -24 EOF +mv .dwz/testfile_multi.dwz . +rmdir .dwz testrun_compare ${abs_top_builddir}/src/readelf --debug-dump=info testfile-dwzstr <<\EOF |