summaryrefslogtreecommitdiffstats
path: root/libdw/libdw_findcu.c
diff options
context:
space:
mode:
Diffstat (limited to 'libdw/libdw_findcu.c')
-rw-r--r--libdw/libdw_findcu.c189
1 files changed, 164 insertions, 25 deletions
diff --git a/libdw/libdw_findcu.c b/libdw/libdw_findcu.c
index 082307b0..ed744231 100644
--- a/libdw/libdw_findcu.c
+++ b/libdw/libdw_findcu.c
@@ -1,5 +1,5 @@
/* Find CU for given offset.
- Copyright (C) 2003-2010, 2014 Red Hat, Inc.
+ Copyright (C) 2003-2010, 2014, 2016, 2017, 2018 Red Hat, Inc.
This file is part of elfutils.
Written by Ulrich Drepper <[email protected]>, 2003.
@@ -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)
@@ -71,30 +105,41 @@ __libdw_intern_next_unit (Dwarf *dbg, bool debug_types)
Dwarf_Off oldoff = *offsetp;
uint16_t version;
+ uint8_t unit_type;
uint8_t address_size;
uint8_t offset_size;
Dwarf_Off abbrev_offset;
- uint64_t type_sig8 = 0;
- Dwarf_Off type_offset = 0;
-
- if (INTUSE(dwarf_next_unit) (dbg, oldoff, offsetp, NULL,
- &version, &abbrev_offset,
- &address_size, &offset_size,
- debug_types ? &type_sig8 : NULL,
- debug_types ? &type_offset : NULL) != 0)
+ uint64_t unit_id8;
+ Dwarf_Off subdie_offset;
+
+ if (__libdw_next_unit (dbg, debug_types, oldoff, offsetp, NULL,
+ &version, &unit_type, &abbrev_offset,
+ &address_size, &offset_size,
+ &unit_id8, &subdie_offset) != 0)
/* No more entries. */
return NULL;
- /* We only know how to handle the DWARF version 2 through 4 formats. */
- if (unlikely (version < 2) || unlikely (version > 4))
+ /* We only know how to handle the DWARF version 2 through 5 formats.
+ For v4 debug types we only handle version 4. */
+ if (unlikely (version < 2) || unlikely (version > 5)
+ || (debug_types && unlikely (version != 4)))
{
- __libdw_seterrno (DWARF_E_INVALID_DWARF);
+ __libdw_seterrno (DWARF_E_VERSION);
return NULL;
}
+ /* We only handle 32 or 64 bit (4 or 8 byte) addresses and offsets.
+ Just assume we are dealing with 64bit in case the size is "unknown".
+ Too much code assumes if it isn't 4 then it is 8 (or the other way
+ around). */
+ if (unlikely (address_size != 4 && address_size != 8))
+ address_size = 8;
+ if (unlikely (offset_size != 4 && offset_size != 8))
+ offset_size = 8;
+
/* Invalid or truncated debug section data? */
- Elf_Data *data = dbg->sectiondata[debug_types
- ? IDX_debug_types : IDX_debug_info];
+ size_t sec_idx = debug_types ? IDX_debug_types : IDX_debug_info;
+ Elf_Data *data = dbg->sectiondata[sec_idx];
if (unlikely (*offsetp > data->d_size))
*offsetp = data->d_size;
@@ -102,24 +147,71 @@ __libdw_intern_next_unit (Dwarf *dbg, bool debug_types)
struct Dwarf_CU *newp = libdw_typed_alloc (dbg, struct Dwarf_CU);
newp->dbg = dbg;
+ newp->sec_idx = sec_idx;
newp->start = oldoff;
newp->end = *offsetp;
newp->address_size = address_size;
newp->offset_size = offset_size;
newp->version = version;
- newp->type_sig8 = type_sig8;
- newp->type_offset = type_offset;
+ newp->unit_id8 = unit_id8;
+ newp->subdie_offset = subdie_offset;
Dwarf_Abbrev_Hash_init (&newp->abbrev_hash, 41);
newp->orig_abbrev_offset = newp->last_abbrev_offset = abbrev_offset;
+ newp->files = NULL;
newp->lines = NULL;
newp->locs = NULL;
-
- if (debug_types)
- Dwarf_Sig8_Hash_insert (&dbg->sig8_hash, type_sig8, newp);
+ newp->split = (Dwarf_CU *) -1;
+ newp->base_address = (Dwarf_Addr) -1;
+ newp->addr_base = (Dwarf_Off) -1;
+ newp->str_off_base = (Dwarf_Off) -1;
+ newp->ranges_base = (Dwarf_Off) -1;
+ newp->locs_base = (Dwarf_Off) -1;
newp->startp = data->d_buf + newp->start;
newp->endp = data->d_buf + newp->end;
+ /* v4 debug type units have version == 4 and unit_type == DW_UT_type. */
+ if (debug_types)
+ newp->unit_type = DW_UT_type;
+ else if (version < 5)
+ {
+ /* This is a reasonable guess (and needed to get the CUDIE). */
+ newp->unit_type = DW_UT_compile;
+
+ /* But set it correctly from the actual CUDIE tag. */
+ Dwarf_Die cudie = CUDIE (newp);
+ int tag = INTUSE(dwarf_tag) (&cudie);
+ if (tag == DW_TAG_compile_unit)
+ {
+ Dwarf_Attribute dwo_id;
+ if (INTUSE(dwarf_attr) (&cudie, DW_AT_GNU_dwo_id, &dwo_id) != NULL)
+ {
+ Dwarf_Word id8;
+ if (INTUSE(dwarf_formudata) (&dwo_id, &id8) == 0)
+ {
+ if (INTUSE(dwarf_haschildren) (&cudie) == 0
+ && INTUSE(dwarf_hasattr) (&cudie,
+ DW_AT_GNU_dwo_name) == 1)
+ newp->unit_type = DW_UT_skeleton;
+ else
+ newp->unit_type = DW_UT_split_compile;
+
+ newp->unit_id8 = id8;
+ }
+ }
+ }
+ else if (tag == DW_TAG_partial_unit)
+ newp->unit_type = DW_UT_partial;
+ else if (tag == DW_TAG_type_unit)
+ newp->unit_type = DW_UT_type;
+ }
+ else
+ newp->unit_type = unit_type;
+
+ /* Store a reference to any type unit ids in the hash for quick lookup. */
+ if (unit_type == DW_UT_type || unit_type == DW_UT_split_type)
+ Dwarf_Sig8_Hash_insert (&dbg->sig8_hash, unit_id8, newp);
+
/* Add the new entry to the search tree. */
if (tsearch (newp, tree, findcu_cb) == NULL)
{
@@ -134,11 +226,11 @@ __libdw_intern_next_unit (Dwarf *dbg, bool debug_types)
struct Dwarf_CU *
internal_function
-__libdw_findcu (Dwarf *dbg, Dwarf_Off start, bool debug_types)
+__libdw_findcu (Dwarf *dbg, Dwarf_Off start, bool v4_debug_types)
{
- void **tree = debug_types ? &dbg->tu_tree : &dbg->cu_tree;
+ void **tree = v4_debug_types ? &dbg->tu_tree : &dbg->cu_tree;
Dwarf_Off *next_offset
- = debug_types ? &dbg->next_tu_offset : &dbg->next_cu_offset;
+ = v4_debug_types ? &dbg->next_tu_offset : &dbg->next_cu_offset;
/* Maybe we already know that CU. */
struct Dwarf_CU fake = { .start = start, .end = 0 };
@@ -155,14 +247,61 @@ __libdw_findcu (Dwarf *dbg, Dwarf_Off start, bool debug_types)
/* No. Then read more CUs. */
while (1)
{
- struct Dwarf_CU *newp = __libdw_intern_next_unit (dbg, debug_types);
+ struct Dwarf_CU *newp = __libdw_intern_next_unit (dbg, v4_debug_types);
if (newp == NULL)
return NULL;
/* Is this the one we are looking for? */
- if (start < *next_offset)
- // XXX Match exact offset.
+ if (start < *next_offset || start == newp->start)
return newp;
}
/* NOTREACHED */
}
+
+struct Dwarf_CU *
+internal_function
+__libdw_findcu_addr (Dwarf *dbg, void *addr)
+{
+ void **tree;
+ Dwarf_Off start;
+ if (addr >= dbg->sectiondata[IDX_debug_info]->d_buf
+ && addr < (dbg->sectiondata[IDX_debug_info]->d_buf
+ + dbg->sectiondata[IDX_debug_info]->d_size))
+ {
+ tree = &dbg->cu_tree;
+ start = addr - dbg->sectiondata[IDX_debug_info]->d_buf;
+ }
+ else if (dbg->sectiondata[IDX_debug_types] != NULL
+ && addr >= dbg->sectiondata[IDX_debug_types]->d_buf
+ && addr < (dbg->sectiondata[IDX_debug_types]->d_buf
+ + dbg->sectiondata[IDX_debug_types]->d_size))
+ {
+ tree = &dbg->tu_tree;
+ start = addr - dbg->sectiondata[IDX_debug_types]->d_buf;
+ }
+ else
+ return NULL;
+
+ struct Dwarf_CU fake = { .start = start, .end = 0 };
+ struct Dwarf_CU **found = tfind (&fake, tree, findcu_cb);
+
+ if (found != NULL)
+ return *found;
+
+ 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;
+}