2018-12-10 Tom de Vries <[email protected]>
* 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