aboutsummaryrefslogtreecommitdiff
path: root/libdw
diff options
context:
space:
mode:
authorMark Wielaard <mark@klomp.org>2018-05-29 23:49:21 +0200
committerMark Wielaard <mark@klomp.org>2018-05-31 17:03:19 +0200
commit7d6fe0a39f6ae5c516ffd63558e12b24297bf982 (patch)
tree30469e6da3f107797bb69a1d0a480eafc4ba50fd /libdw
parentb37feac1a8ceebb0748cb28d219aa8387d0885dd (diff)
downloadelfutils-7d6fe0a39f6ae5c516ffd63558e12b24297bf982.tar.gz
libdw: Handle split Dwarf Dies in dwarf_die_addr_die.
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>
Diffstat (limited to 'libdw')
-rw-r--r--libdw/ChangeLog14
-rw-r--r--libdw/dwarf_cuoffset.c2
-rw-r--r--libdw/dwarf_die_addr_die.c14
-rw-r--r--libdw/dwarf_end.c3
-rw-r--r--libdw/libdwP.h9
-rw-r--r--libdw/libdw_find_split_unit.c9
-rw-r--r--libdw/libdw_findcu.c49
7 files changed, 98 insertions, 2 deletions
diff --git a/libdw/ChangeLog b/libdw/ChangeLog
index eb0b01ad..5a33d9c1 100644
--- a/libdw/ChangeLog
+++ b/libdw/ChangeLog
@@ -1,5 +1,19 @@
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.
+ * dwarf_end (dwarf_end): Destroy split_tree.
+
+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 ba376486..f13b02fd 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 02d63b7f..65729166 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/dwarf_end.c b/libdw/dwarf_end.c
index 23a50a0b..29795c10 100644
--- a/libdw/dwarf_end.c
+++ b/libdw/dwarf_end.c
@@ -91,6 +91,9 @@ dwarf_end (Dwarf *dwarf)
/* Search tree for decoded .debug_lines units. */
tdestroy (dwarf->files_lines, noop_free);
+ /* And the split Dwarf. */
+ tdestroy (dwarf->split_tree, noop_free);
+
struct libdw_memblock *memp = dwarf->mem_tail;
/* The first block is allocated together with the Dwarf object. */
while (memp->prev != NULL)
diff --git a/libdw/libdwP.h b/libdw/libdwP.h
index dd47009b..1c8dd0d2 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 d6527e07..dc62e0d0 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 9d231999..2f5c6c42 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;
+}