summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Wielaard <mjw@redhat.com>2014-05-01 14:48:27 +0200
committerMark Wielaard <mjw@redhat.com>2014-05-02 17:00:48 +0200
commitdf85bf99021119fcbb2ced66dd69f1cceafb180c (patch)
treea0ee033052e69b9b00e87ee31bf4962dac3cec17
parentd81d32d2a4f92355e4c677b578147dfe819251b9 (diff)
downloadelfutils-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/ChangeLog6
-rw-r--r--libdw/dwarf_end.c6
-rw-r--r--libdw/dwarf_setalt.c3
-rw-r--r--libdw/libdwP.h5
-rw-r--r--libdwfl/ChangeLog33
-rw-r--r--libdwfl/dwfl_build_id_find_debuginfo.c55
-rw-r--r--libdwfl/dwfl_build_id_find_elf.c32
-rw-r--r--libdwfl/dwfl_module.c14
-rw-r--r--libdwfl/dwfl_module_getdwarf.c208
-rw-r--r--libdwfl/find-debuginfo.c98
-rw-r--r--libdwfl/libdwflP.h10
-rw-r--r--libdwfl/linux-kernel-modules.c11
-rw-r--r--src/ChangeLog5
-rw-r--r--src/readelf.c34
-rw-r--r--tests/ChangeLog5
-rwxr-xr-xtests/run-readelf-dwz-multi.sh105
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