The existing code was first trying .gnu_debugaltlink paths directly, so relative paths would start from the current program's working directory. That's unlikely to be useful, so it was almost always falling back to a build-id based path.
In Fedora, these paths are relative to the debug file itself, so that's a better thing to try first. We don't actually have the debug path in libdw, but we can usually find it from elf->fildes and /proc/self/fd/. Signed-off-by: Josh Stone <[email protected]> --- libdw/ChangeLog | 5 +++++ libdw/dwarf_begin_elf.c | 44 +++++++++++++++++++++++++++++++++++++++++--- tests/ChangeLog | 4 ++++ tests/run-allfcts-multi.sh | 14 ++++++++++---- 4 files changed, 60 insertions(+), 7 deletions(-) diff --git a/libdw/ChangeLog b/libdw/ChangeLog index aa7b9ca486c2..a540a5219856 100644 --- a/libdw/ChangeLog +++ b/libdw/ChangeLog @@ -1,3 +1,8 @@ +2013-12-14 Josh Stone <[email protected]> + + * dwarf_begin_elf.c (build_rel_debugaltlink): New function. + (open_debugaltlink): Use it. + 2013-12-10 Josh Stone <[email protected]> * memory-access.h (get_uleb128_rest_return): Removed. diff --git a/libdw/dwarf_begin_elf.c b/libdw/dwarf_begin_elf.c index 6cf3aa17855d..eeb84c051770 100644 --- a/libdw/dwarf_begin_elf.c +++ b/libdw/dwarf_begin_elf.c @@ -42,8 +42,10 @@ #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> +#include <libgen.h> #include "libdwP.h" +#include "libelfP.h" #if USE_ZLIB # include <endian.h> @@ -136,14 +138,50 @@ try_debugaltlink (Dwarf *result, const char *try_name, return NULL; } +/* Make relative links into an absolute path. */ +static char * +build_rel_debugaltlink (Dwarf *result, const char *alt_name) +{ + if (alt_name[0] == '/' || result->elf->fildes < 0) + return NULL; + +#define SELFFD_PREFIX "/proc/self/fd/" + char fd_name[sizeof SELFFD_PREFIX + 12]; + sprintf (fd_name, "%s%d", SELFFD_PREFIX, result->elf->fildes); + + /* Note, readlink(2) recommends sizing the buffer based on lstat(2), but + procfs always reports st_size=64, even if it's really longer. */ + char fd_link[PATH_MAX + 1]; + ssize_t len_link = readlink (fd_name, fd_link, sizeof fd_link); + if (len_link == -1 || (size_t) len_link > sizeof fd_link - 1) + return NULL; + fd_link[len_link] = '\0'; + + char *rel_name; + if (asprintf (&rel_name, "%s/%s", dirname (fd_link), alt_name) == -1) + return NULL; + + return rel_name; +} + /* 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. */ + /* First try it relative to the debug file. */ + char *rel_name = build_rel_debugaltlink (result, alt_name); + if (rel_name != NULL) + { + Dwarf *alt = try_debugaltlink (result, rel_name, build_id, id_len); + free (rel_name); + if (alt != NULL) + return result; + } + + /* Then try the name itself, it is either an absolute path or + a relative one. (Using a path relative to the tool's cwd + doesn't make much sense in general, but it's also harmless.) */ if (try_debugaltlink (result, alt_name, build_id, id_len) != NULL) return result; diff --git a/tests/ChangeLog b/tests/ChangeLog index 008198166da6..6325a74d5915 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,3 +1,7 @@ +2013-12-14 Josh Stone <[email protected]> + + * run-allfcts-multi.sh: Test relative altlinks. + 2013-12-10 Mark Wielaard <[email protected]> * Makefile.am (backtrace_child_biarch_SOURCES): New backtrace-child.c. diff --git a/tests/run-allfcts-multi.sh b/tests/run-allfcts-multi.sh index 727b76ee5c72..35e3177a57ae 100755 --- a/tests/run-allfcts-multi.sh +++ b/tests/run-allfcts-multi.sh @@ -42,15 +42,21 @@ EOF # dwz test-offset-loop test-offset-loop2 -m test-offset-loop.alt testfiles test-offset-loop test-offset-loop.alt -tempfiles allfcts.out +tempfiles allfcts.in allfcts.out -# Use head to capture output because the output could be infinite... -testrun ${abs_builddir}/allfcts test-offset-loop | head -n 20 > allfcts.out -testrun_compare cat allfcts.out <<\EOF +cat >allfcts.in <<\EOF /tmp/test-offset-loop.c:6:get_errno /tmp/test-offset-loop.c:5:is_error /tmp/test-offset-loop.c:4:padding /tmp/test-offset-loop.c:7:main EOF +# Use head to capture output because the output could be infinite... +testrun ${abs_builddir}/allfcts test-offset-loop | head -n 20 > allfcts.out +testrun_compare cat allfcts.out <allfcts.in + +# Repeat from a different directory for relative .gnu_debugaltlink +(cd / && testrun ${abs_builddir}/allfcts $OLDPWD/test-offset-loop) | head -n 20 > allfcts.out +testrun_compare cat allfcts.out <allfcts.in + exit 0 -- 1.8.4.2
