This is the mail archive of the elfutils-devel@sourceware.org mailing list for the elfutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH 8/9] 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>
---
 libdw/ChangeLog                        |   6 +
 libdw/dwarf_end.c                      |   6 +-
 libdw/dwarf_setalt.c                   |   3 -
 libdw/libdwP.h                         |   5 +-
 libdwfl/ChangeLog                      |  33 ++++++
 libdwfl/dwfl_build_id_find_debuginfo.c |  55 ++++++++-
 libdwfl/dwfl_build_id_find_elf.c       |  32 +++--
 libdwfl/dwfl_module.c                  |  14 ++-
 libdwfl/dwfl_module_getdwarf.c         | 208 +++++++++++++++------------------
 libdwfl/find-debuginfo.c               |  98 ++++++++++++++--
 libdwfl/libdwflP.h                     |  10 +-
 libdwfl/linux-kernel-modules.c         |  11 +-
 src/ChangeLog                          |   5 +
 src/readelf.c                          |  34 ++++--
 tests/ChangeLog                        |   5 +
 tests/run-readelf-dwz-multi.sh         | 105 +++++++++++++++++
 16 files changed, 461 insertions(+), 169 deletions(-)

diff --git a/libdw/ChangeLog b/libdw/ChangeLog
index 94ef03c..960c831 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 e65314a..241a257 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 3b5b935..9bd566f 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 1c94767..4136135 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 7e4234d..a33bd25 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 a955735..f1c64bc 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 a4f0326..1555008 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 bb167ab..8efcfaa 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 e8087bf..293e8e7 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 21db91a..3f5314a 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 e4ab60e..9b03d8a 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 5307518..1ad7d2f 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 429a7ee..341787d 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 697a361..45b1910 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 ff396d5..f180fe4 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 44d2475..27e0f38 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
 
-- 
1.9.0


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]