diff options
Diffstat (limited to 'libdw/libdw_findcu.c')
| -rw-r--r-- | libdw/libdw_findcu.c | 189 |
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; +} |
