2018-12-10 Tom de Vries <tdevr...@suse.de> * dwarf.c (enum attr_val_encoding): Add ATTR_VAL_REF_ALT_INFO. (struct unit): Add low and high fields. (struct unit_vector): New type. (struct dwarf_data): Add units and units_counts fields. (read_attribute): Handle DW_FORM_GNU_ref_alt using ATTR_VAL_REF_ALT_INFO. (find_unit): New function. (find_address_ranges): Add and handle unit_tag parameter. (build_address_map): Add and handle units_vec parameter. (read_referenced_name_1): Handle DW_FORM_GNU_ref_alt. (build_dwarf_data): Pass units_vec to build_address_map. Store resulting units vector. --- libbacktrace/dwarf.c | 101 ++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 91 insertions(+), 10 deletions(-)
diff --git a/libbacktrace/dwarf.c b/libbacktrace/dwarf.c index 99e5f4c3f51..9a0b93120c8 100644 --- a/libbacktrace/dwarf.c +++ b/libbacktrace/dwarf.c @@ -143,6 +143,8 @@ enum attr_val_encoding ATTR_VAL_REF_UNIT, /* An offset to other data within the .dwarf_info section. */ ATTR_VAL_REF_INFO, + /* An offset to other data within the alt .dwarf_info section. */ + ATTR_VAL_REF_ALT_INFO, /* An offset to data in some other section. */ ATTR_VAL_REF_SECTION, /* A type signature. */ @@ -281,6 +283,10 @@ struct unit /* The offset of UNIT_DATA from the start of the information for this compilation unit. */ size_t unit_data_offset; + /* Start of the compilation unit. */ + size_t low; + /* End of the compilation unit. */ + size_t high; /* DWARF version. */ int version; /* Whether unit is DWARF64. */ @@ -339,6 +345,14 @@ struct unit_addrs_vector size_t count; }; +/* A growable vector of compilation unit pointer. */ + +struct unit_vector +{ + struct backtrace_vector vec; + size_t count; +}; + /* The information we need to map a PC to a file and line. */ struct dwarf_data @@ -353,6 +367,10 @@ struct dwarf_data struct unit_addrs *addrs; /* Number of address ranges in list. */ size_t addrs_count; + /* A sorted list of units. */ + struct unit **units; + /* Number of units in the list. */ + size_t units_count; /* The unparsed .debug_info section. */ const unsigned char *dwarf_info; size_t dwarf_info_size; @@ -840,7 +858,7 @@ read_attribute (enum dwarf_form form, struct dwarf_buf *buf, val->encoding = ATTR_VAL_NONE; return 1; } - val->encoding = ATTR_VAL_REF_SECTION; + val->encoding = ATTR_VAL_REF_ALT_INFO; return 1; case DW_FORM_GNU_strp_alt: { @@ -866,6 +884,34 @@ read_attribute (enum dwarf_form form, struct dwarf_buf *buf, } } +/* Compare a unit offset against a unit for bsearch. */ + +static int +units_search (const void *vkey, const void *ventry) +{ + const uintptr_t *key = (const uintptr_t *) vkey; + const struct unit *entry = *((const struct unit *const *) ventry); + uintptr_t offset; + + offset = *key; + if (offset < entry->low) + return -1; + else if (offset >= entry->high) + return 1; + else + return 0; +} + +/* Find a unit in PU containing OFFSET. */ + +static struct unit * +find_unit (struct unit **pu, size_t units_count, size_t offset) +{ + struct unit **u; + u = bsearch (&offset, pu, units_count, sizeof (struct unit *), units_search); + return u == NULL ? NULL : *u; +} + /* Compare function_addrs for qsort. When ranges are nested, make the smallest one sort last. */ @@ -1299,7 +1345,7 @@ find_address_ranges (struct backtrace_state *state, uintptr_t base_address, int is_bigendian, backtrace_error_callback error_callback, void *data, struct unit *u, struct unit_addrs_vector *addrs, - struct dwarf_data *altlink) + struct dwarf_data *altlink, enum dwarf_tag *unit_tag) { while (unit_buf->left > 0) { @@ -1322,6 +1368,9 @@ find_address_ranges (struct backtrace_state *state, uintptr_t base_address, if (abbrev == NULL) return 0; + if (unit_tag != NULL) + *unit_tag = abbrev->tag; + lowpc = 0; have_lowpc = 0; highpc = 0; @@ -1434,7 +1483,7 @@ find_address_ranges (struct backtrace_state *state, uintptr_t base_address, dwarf_str, dwarf_str_size, dwarf_ranges, dwarf_ranges_size, is_bigendian, error_callback, data, - u, addrs, altlink)) + u, addrs, altlink, NULL)) return 0; } } @@ -1454,6 +1503,7 @@ build_address_map (struct backtrace_state *state, uintptr_t base_address, const unsigned char *dwarf_str, size_t dwarf_str_size, int is_bigendian, backtrace_error_callback error_callback, void *data, struct unit_addrs_vector *addrs, + struct unit_vector *unit_vec, struct dwarf_data *altlink) { struct dwarf_buf info; @@ -1462,9 +1512,12 @@ build_address_map (struct backtrace_state *state, uintptr_t base_address, size_t i; struct unit **pu; size_t prev_addrs_count; + size_t unit_offset = 0; memset (&addrs->vec, 0, sizeof addrs->vec); + memset (&unit_vec->vec, 0, sizeof unit_vec->vec); addrs->count = 0; + unit_vec->count = 0; prev_addrs_count = 0; /* Read through the .debug_info section. FIXME: Should we use the @@ -1493,6 +1546,7 @@ build_address_map (struct backtrace_state *state, uintptr_t base_address, uint64_t abbrev_offset; int addrsize; struct unit *u; + enum dwarf_tag unit_tag; if (info.reported_underflow) goto fail; @@ -1535,6 +1589,9 @@ build_address_map (struct backtrace_state *state, uintptr_t base_address, addrsize = read_byte (&unit_buf); + u->low = unit_offset; + unit_offset += len + (is_dwarf64 ? 12 : 4); + u->high = unit_offset; u->unit_data = unit_buf.buf; u->unit_data_len = unit_buf.left; u->unit_data_offset = unit_buf.buf - unit_data_start; @@ -1556,13 +1613,13 @@ build_address_map (struct backtrace_state *state, uintptr_t base_address, dwarf_str, dwarf_str_size, dwarf_ranges, dwarf_ranges_size, is_bigendian, error_callback, data, - u, addrs, altlink)) + u, addrs, altlink, &unit_tag)) goto fail; if (unit_buf.reported_underflow) goto fail; - if (addrs->count == prev_addrs_count) + if (unit_tag != DW_TAG_partial_unit && addrs->count == prev_addrs_count) { --units_count; units.size -= sizeof (u); @@ -1576,11 +1633,8 @@ build_address_map (struct backtrace_state *state, uintptr_t base_address, if (info.reported_underflow) goto fail; - // We only kept the list of units to free them on failure. On - // success the units are retained, pointed to by the entries in - // addrs. - backtrace_vector_free (state, &units, error_callback, data); - + unit_vec->vec = units; + unit_vec->count = units_count; return 1; fail: @@ -2144,6 +2198,22 @@ read_referenced_name_1 (struct dwarf_data *ddata, struct unit *u, || val->encoding == ATTR_VAL_REF_UNIT) return read_referenced_name (ddata, u, val->u.uint, error_callback, data); + if (val->encoding == ATTR_VAL_REF_ALT_INFO) + { + struct unit *alt_unit + = find_unit (ddata->altlink->units, ddata->altlink->units_count, + val->u.uint); + if (alt_unit == NULL) + { + error_callback (data, + "Could not find unit for DW_FORM_GNU_ref_alt", 0); + return NULL; + } + uint64_t unit_offset = val->u.uint - alt_unit->low; + return read_referenced_name (ddata->altlink, alt_unit, unit_offset, + error_callback, data); + } + return NULL; } @@ -3028,21 +3098,30 @@ build_dwarf_data (struct backtrace_state *state, struct unit_addrs_vector addrs_vec; struct unit_addrs *addrs; size_t addrs_count; + struct unit_vector units_vec; + struct unit **units; + size_t units_count; struct dwarf_data *fdata; if (!build_address_map (state, base_address, dwarf_info, dwarf_info_size, dwarf_abbrev, dwarf_abbrev_size, dwarf_ranges, dwarf_ranges_size, dwarf_str, dwarf_str_size, is_bigendian, error_callback, data, &addrs_vec, + &units_vec, altlink)) return NULL; if (!backtrace_vector_release (state, &addrs_vec.vec, error_callback, data)) return NULL; + if (!backtrace_vector_release (state, &units_vec.vec, error_callback, data)) + return NULL; addrs = (struct unit_addrs *) addrs_vec.vec.base; + units = (struct unit **) units_vec.vec.base; addrs_count = addrs_vec.count; + units_count = units_vec.count; backtrace_qsort (addrs, addrs_count, sizeof (struct unit_addrs), unit_addrs_compare); + /* No qsort for units required, already sorted. */ fdata = ((struct dwarf_data *) backtrace_alloc (state, sizeof (struct dwarf_data), @@ -3055,6 +3134,8 @@ build_dwarf_data (struct backtrace_state *state, fdata->base_address = base_address; fdata->addrs = addrs; fdata->addrs_count = addrs_count; + fdata->units = units; + fdata->units_count = units_count; fdata->dwarf_info = dwarf_info; fdata->dwarf_info_size = dwarf_info_size; fdata->dwarf_line = dwarf_line; -- 2.16.4