[PATCH] libdw: Handle split Dwarf Dies in dwarf_die_addr_die.

Mark Wielaard mark@klomp.org
Tue May 29 21:57:00 GMT 2018


dwarf_die_addr_die can be used to turn an Dwarf_Die addr back into a
full Dwarf_Die, just given the original Dwarf debug handle. This now
also works for Dwarf_Dies which originated from a split Dwarf. Whenever
a split Dwarf_CU is found the Dwarf it originated from is registered
with the Dwarf that the skeleton Dwarf_CU came from. All registered
split Dwarfs are then searched by dwarf_die_addr_die if the addr didn't
match the main Dwarf or the alt Dwarf.

One limitation in this implementation is that only DIEs that come from
the main .debug_info in the .dwo are supported. Theoretically there could
also be DIEs in an .debug_type or from other/multiple (comdat) sections.

New tests are added for dwarf-4, dwarf-5, split-dwarf-4, split-dwarf-5
and version 4 and 5 dwo files.

Signed-off-by: Mark Wielaard <mark@klomp.org>
---
 libdw/ChangeLog                 | 13 +++++++++++
 libdw/dwarf_cuoffset.c          |  2 +-
 libdw/dwarf_die_addr_die.c      | 14 +++++++++++-
 libdw/libdwP.h                  |  9 ++++++++
 libdw/libdw_find_split_unit.c   |  9 ++++++++
 libdw/libdw_findcu.c            | 49 +++++++++++++++++++++++++++++++++++++++++
 tests/ChangeLog                 |  7 ++++++
 tests/dwarf-die-addr-die.c      | 14 ++++++++++++
 tests/run-dwarf-die-addr-die.sh | 14 ++++++++++++
 9 files changed, 129 insertions(+), 2 deletions(-)

diff --git a/libdw/ChangeLog b/libdw/ChangeLog
index eb0b01a..e3b15f0 100644
--- a/libdw/ChangeLog
+++ b/libdw/ChangeLog
@@ -1,5 +1,18 @@
 2018-05-29  Mark Wielaard  <mark@klomp.org>
 
+	* dwarf_cuoffset.c (dwarf_cuoffset): Check die->cu is not NULL.
+	* dwarf_die_addr_die.c (dwarf_die_addr_die): Also search split
+	Dwarfs.
+	* libdwP.h (struct Dwarf): Add split_tree field.
+	(__libdw_find_split_dbg_addr): New internal function definition.
+	(__libdw_finddbg_cb): Likewise.
+	* libdw_find_split_unit.c (__libdw_find_split_unit): Insert split
+	Dwarf into skeleton dbg split_tree.
+	* libdw_findcu.c (__libdw_finddbg_cb): New function.
+	(__libdw_find_split_dbg_addr): Likewise.
+
+2018-05-29  Mark Wielaard  <mark@klomp.org>
+
 	* dwarf.h: Add GNU DebugFission list entry encodings
 	DW_LLE_GNU_end_of_list_entry,
 	DW_LLE_GNU_base_address_selection_entry,
diff --git a/libdw/dwarf_cuoffset.c b/libdw/dwarf_cuoffset.c
index ba37648..f13b02f 100644
--- a/libdw/dwarf_cuoffset.c
+++ b/libdw/dwarf_cuoffset.c
@@ -38,7 +38,7 @@
 Dwarf_Off
 dwarf_cuoffset (Dwarf_Die *die)
 {
-  return (die == NULL
+  return ((die == NULL || die->cu == NULL)
 	  ? (Dwarf_Off) -1l
 	  : (Dwarf_Off) (die->addr - die->cu->startp));
 }
diff --git a/libdw/dwarf_die_addr_die.c b/libdw/dwarf_die_addr_die.c
index 02d63b7..6572916 100644
--- a/libdw/dwarf_die_addr_die.c
+++ b/libdw/dwarf_die_addr_die.c
@@ -30,6 +30,8 @@
 # include <config.h>
 #endif
 
+#include <string.h>
+
 #include <dwarf.h>
 #include "libdwP.h"
 
@@ -50,7 +52,17 @@ dwarf_die_addr_die (Dwarf *dbg, void *addr, Dwarf_Die *result)
     }
 
   if (cu == NULL)
-    return NULL;
+    {
+      Dwarf *split = __libdw_find_split_dbg_addr (dbg, addr);
+      if (split != NULL)
+	cu = __libdw_findcu_addr (split, addr);
+    }
+
+  if (cu == NULL)
+    {
+      memset (result, '\0', sizeof (Dwarf_Die));
+      return NULL;
+    }
 
   *result = (Dwarf_Die) { .addr = addr, .cu = cu };
 
diff --git a/libdw/libdwP.h b/libdw/libdwP.h
index dd47009..1c8dd0d 100644
--- a/libdw/libdwP.h
+++ b/libdw/libdwP.h
@@ -193,6 +193,9 @@ struct Dwarf
   Dwarf_Off next_tu_offset;
   Dwarf_Sig8_Hash sig8_hash;
 
+  /* Search tree for split Dwarf associated with CUs in this debug.  */
+  void *split_tree;
+
   /* Search tree for .debug_macro operator tables.  */
   void *macro_ops;
 
@@ -619,6 +622,10 @@ extern struct Dwarf_CU *__libdw_findcu (Dwarf *dbg, Dwarf_Off offset, bool tu)
 extern struct Dwarf_CU *__libdw_findcu_addr (Dwarf *dbg, void *addr)
      __nonnull_attribute__ (1) internal_function;
 
+/* Find split Dwarf for given DIE address.  */
+extern struct Dwarf *__libdw_find_split_dbg_addr (Dwarf *dbg, void *addr)
+     __nonnull_attribute__ (1) internal_function;
+
 /* Find the split (or skeleton) unit.  */
 extern struct Dwarf_CU *__libdw_find_split_unit (Dwarf_CU *cu)
      internal_function;
@@ -1261,6 +1268,8 @@ __libdw_cu_locs_base (Dwarf_CU *cu)
   return cu->locs_base;
 }
 
+/* Helper function for tsearch/tfind split_tree Dwarf.  */
+int __libdw_finddbg_cb (const void *arg1, const void *arg2);
 
 /* Link skeleton and split compile units.  */
 static inline void
diff --git a/libdw/libdw_find_split_unit.c b/libdw/libdw_find_split_unit.c
index d6527e0..dc62e0d 100644
--- a/libdw/libdw_find_split_unit.c
+++ b/libdw/libdw_find_split_unit.c
@@ -34,6 +34,7 @@
 #include "libelfP.h"
 
 #include <limits.h>
+#include <search.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/types.h>
@@ -84,6 +85,14 @@ __libdw_find_split_unit (Dwarf_CU *cu)
 			  if (split->unit_type == DW_UT_split_compile
 			      && cu->unit_id8 == split->unit_id8)
 			    {
+			      if (tsearch (split->dbg, &cu->dbg->split_tree,
+					   __libdw_finddbg_cb) == NULL)
+				{
+				  /* Something went wrong.  Don't link.  */
+				  __libdw_seterrno (DWARF_E_NOMEM);
+				  break;
+				}
+
 			      /* Link skeleton and split compile units.  */
 			      __libdw_link_skel_split (cu, split);
 
diff --git a/libdw/libdw_findcu.c b/libdw/libdw_findcu.c
index 9d23199..2f5c6c4 100644
--- a/libdw/libdw_findcu.c
+++ b/libdw/libdw_findcu.c
@@ -61,6 +61,40 @@ findcu_cb (const void *arg1, const void *arg2)
   return 0;
 }
 
+int
+__libdw_finddbg_cb (const void *arg1, const void *arg2)
+{
+  Dwarf *dbg1 = (Dwarf *) arg1;
+  Dwarf *dbg2 = (Dwarf *) arg2;
+
+  Elf_Data *dbg1_data = dbg1->sectiondata[IDX_debug_info];
+  unsigned char *dbg1_start = dbg1_data->d_buf;
+  size_t dbg1_size = dbg1_data->d_size;
+
+  Elf_Data *dbg2_data = dbg2->sectiondata[IDX_debug_info];
+  unsigned char *dbg2_start = dbg2_data->d_buf;
+  size_t dbg2_size = dbg2_data->d_size;
+
+  /* Find out which of the two arguments is the search value.  It has
+     a size of 0.  */
+  if (dbg1_size == 0)
+    {
+      if (dbg1_start < dbg2_start)
+	return -1;
+      if (dbg1_start >= dbg2_start + dbg2_size)
+	return 1;
+    }
+  else
+    {
+      if (dbg2_start < dbg1_start)
+	return 1;
+      if (dbg2_start >= dbg1_start + dbg1_size)
+	return -1;
+    }
+
+  return 0;
+}
+
 struct Dwarf_CU *
 internal_function
 __libdw_intern_next_unit (Dwarf *dbg, bool debug_types)
@@ -247,3 +281,18 @@ __libdw_findcu_addr (Dwarf *dbg, void *addr)
 
   return NULL;
 }
+
+Dwarf *
+internal_function
+__libdw_find_split_dbg_addr (Dwarf *dbg, void *addr)
+{
+  /* XXX Assumes split DWARF only has CUs in main IDX_debug_info.  */
+  Elf_Data fake_data = { .d_buf = addr, .d_size = 0 };
+  Dwarf fake = { .sectiondata[IDX_debug_info] = &fake_data };
+  Dwarf **found = tfind (&fake, &dbg->split_tree, __libdw_finddbg_cb);
+
+  if (found != NULL)
+    return *found;
+
+  return NULL;
+}
diff --git a/tests/ChangeLog b/tests/ChangeLog
index 2b255c7..b656bee 100644
--- a/tests/ChangeLog
+++ b/tests/ChangeLog
@@ -1,5 +1,12 @@
 2018-05-29  Mark Wielaard  <mark@klomp.org>
 
+	* dwarf-die-addr-die.c (check_dbg): Also check subdies, split or
+	type, gotten through dwarf_get_units.
+	* run-dwarf-die-addr-die.sh: Add tests for dwarf-4, dwarf-5,
+	split-dwarf-4, split-dwarf-5 and dwo files.
+
+2018-05-29  Mark Wielaard  <mark@klomp.org>
+
 	* run-readelf-loc.sh: Add GNU DebugFission split-dwarf variant.
 	* run-varlocs.sh: Likewise.
 
diff --git a/tests/dwarf-die-addr-die.c b/tests/dwarf-die-addr-die.c
index b4f6dbc..7899988 100644
--- a/tests/dwarf-die-addr-die.c
+++ b/tests/dwarf-die-addr-die.c
@@ -134,6 +134,20 @@ check_dbg (Dwarf *dbg)
       res |= check_dbg (alt);
     }
 
+  // Split or Type Dwarf_Dies gotten through dwarf_get_units.
+  Dwarf_CU *cu = NULL;
+  Dwarf_Die subdie;
+  uint8_t unit_type;
+  while (dwarf_get_units (dbg, cu, &cu, NULL,
+                          &unit_type, NULL, &subdie) == 0)
+    {
+      if (dwarf_tag (&subdie) != DW_TAG_invalid)
+        {
+	  printf ("checking %" PRIx8 " subdie\n", unit_type);
+	  res |= check_die (&subdie);
+	}
+    }
+
   return res;
 }
 
diff --git a/tests/run-dwarf-die-addr-die.sh b/tests/run-dwarf-die-addr-die.sh
index 16fe7b0..951d1c5 100755
--- a/tests/run-dwarf-die-addr-die.sh
+++ b/tests/run-dwarf-die-addr-die.sh
@@ -32,6 +32,20 @@ testfiles testfilebazdbgppc64.debug
 
 testrun ${abs_builddir}/dwarf-die-addr-die testfilebazdbgppc64.debug
 
+# see tests/testfile-dwarf-45.source
+testfiles testfile-dwarf-4 testfile-dwarf-5
+testfiles testfile-splitdwarf-4 testfile-hello4.dwo testfile-world4.dwo
+testfiles testfile-splitdwarf-5 testfile-hello5.dwo testfile-world5.dwo
+
+testrun ${abs_builddir}/dwarf-die-addr-die testfile-dwarf-4
+testrun ${abs_builddir}/dwarf-die-addr-die testfile-dwarf-5
+testrun ${abs_builddir}/dwarf-die-addr-die testfile-splitdwarf-4
+testrun ${abs_builddir}/dwarf-die-addr-die testfile-splitdwarf-5
+testrun ${abs_builddir}/dwarf-die-addr-die testfile-hello4.dwo
+testrun ${abs_builddir}/dwarf-die-addr-die testfile-world4.dwo
+testrun ${abs_builddir}/dwarf-die-addr-die testfile-hello5.dwo
+testrun ${abs_builddir}/dwarf-die-addr-die testfile-world5.dwo
+
 # Self test
 testrun_on_self ${abs_builddir}/dwarf-die-addr-die
 
-- 
1.8.3.1



More information about the Elfutils-devel mailing list