mirror of
https://github.com/ianlancetaylor/libbacktrace.git
synced 2026-04-05 00:49:07 +08:00
libbacktrace: don't get confused by overlapping address ranges
Fixes https://github.com/ianlancetaylor/libbacktrace/issues/137. * dwarf.c (resolve_unit_addrs_overlap_walk): New static function. (resolve_unit_addrs_overlap): New static function. (build_dwarf_data): Call resolve_unit_addrs_overlap.
This commit is contained in:
214
dwarf.c
214
dwarf.c
@@ -1610,6 +1610,194 @@ unit_addrs_search (const void *vkey, const void *ventry)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Fill in overlapping ranges as needed. This is a subroutine of
|
||||||
|
resolve_unit_addrs_overlap. */
|
||||||
|
|
||||||
|
static int
|
||||||
|
resolve_unit_addrs_overlap_walk (struct backtrace_state *state,
|
||||||
|
size_t *pfrom, size_t *pto,
|
||||||
|
struct unit_addrs *enclosing,
|
||||||
|
struct unit_addrs_vector *old_vec,
|
||||||
|
backtrace_error_callback error_callback,
|
||||||
|
void *data,
|
||||||
|
struct unit_addrs_vector *new_vec)
|
||||||
|
{
|
||||||
|
struct unit_addrs *old_addrs;
|
||||||
|
size_t old_count;
|
||||||
|
struct unit_addrs *new_addrs;
|
||||||
|
size_t from;
|
||||||
|
size_t to;
|
||||||
|
|
||||||
|
old_addrs = (struct unit_addrs *) old_vec->vec.base;
|
||||||
|
old_count = old_vec->count;
|
||||||
|
new_addrs = (struct unit_addrs *) new_vec->vec.base;
|
||||||
|
|
||||||
|
for (from = *pfrom, to = *pto; from < old_count; from++, to++)
|
||||||
|
{
|
||||||
|
/* If we are in the scope of a larger range that can no longer
|
||||||
|
cover any further ranges, return back to the caller. */
|
||||||
|
|
||||||
|
if (enclosing != NULL
|
||||||
|
&& enclosing->high <= old_addrs[from].low)
|
||||||
|
{
|
||||||
|
*pfrom = from;
|
||||||
|
*pto = to;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
new_addrs[to] = old_addrs[from];
|
||||||
|
|
||||||
|
/* If we are in scope of a larger range, fill in any gaps
|
||||||
|
between this entry and the next one.
|
||||||
|
|
||||||
|
There is an extra entry at the end of the vector, so it's
|
||||||
|
always OK to refer to from + 1. */
|
||||||
|
|
||||||
|
if (enclosing != NULL
|
||||||
|
&& enclosing->high > old_addrs[from].high
|
||||||
|
&& old_addrs[from].high < old_addrs[from + 1].low)
|
||||||
|
{
|
||||||
|
void *grew;
|
||||||
|
size_t new_high;
|
||||||
|
|
||||||
|
grew = backtrace_vector_grow (state, sizeof (struct unit_addrs),
|
||||||
|
error_callback, data, &new_vec->vec);
|
||||||
|
if (grew == NULL)
|
||||||
|
return 0;
|
||||||
|
new_addrs = (struct unit_addrs *) new_vec->vec.base;
|
||||||
|
to++;
|
||||||
|
new_addrs[to].low = old_addrs[from].high;
|
||||||
|
new_high = old_addrs[from + 1].low;
|
||||||
|
if (enclosing->high < new_high)
|
||||||
|
new_high = enclosing->high;
|
||||||
|
new_addrs[to].high = new_high;
|
||||||
|
new_addrs[to].u = enclosing->u;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If this range has a larger scope than the next one, use it to
|
||||||
|
fill in any gaps. */
|
||||||
|
|
||||||
|
if (old_addrs[from].high > old_addrs[from + 1].high)
|
||||||
|
{
|
||||||
|
*pfrom = from + 1;
|
||||||
|
*pto = to + 1;
|
||||||
|
if (!resolve_unit_addrs_overlap_walk (state, pfrom, pto,
|
||||||
|
&old_addrs[from], old_vec,
|
||||||
|
error_callback, data, new_vec))
|
||||||
|
return 0;
|
||||||
|
from = *pfrom;
|
||||||
|
to = *pto;
|
||||||
|
|
||||||
|
/* Undo the increment the loop is about to do. */
|
||||||
|
from--;
|
||||||
|
to--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enclosing == NULL)
|
||||||
|
{
|
||||||
|
struct unit_addrs *pa;
|
||||||
|
|
||||||
|
/* Add trailing entry. */
|
||||||
|
|
||||||
|
pa = ((struct unit_addrs *)
|
||||||
|
backtrace_vector_grow (state, sizeof (struct unit_addrs),
|
||||||
|
error_callback, data, &new_vec->vec));
|
||||||
|
if (pa == NULL)
|
||||||
|
return 0;
|
||||||
|
pa->low = 0;
|
||||||
|
--pa->low;
|
||||||
|
pa->high = pa->low;
|
||||||
|
pa->u = NULL;
|
||||||
|
|
||||||
|
new_vec->count = to;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* It is possible for the unit_addrs list to contain overlaps, as in
|
||||||
|
|
||||||
|
10: low == 10, high == 20, unit 1
|
||||||
|
11: low == 12, high == 15, unit 2
|
||||||
|
12: low == 20, high == 30, unit 1
|
||||||
|
|
||||||
|
In such a case, for pc == 17, a search using units_addr_search will
|
||||||
|
return entry 11. However, pc == 17 doesn't fit in that range. We
|
||||||
|
actually want range 10.
|
||||||
|
|
||||||
|
It seems that in general we might have an arbitrary number of
|
||||||
|
ranges in between 10 and 12.
|
||||||
|
|
||||||
|
To handle this we look for cases where range R1 is followed by
|
||||||
|
range R2 such that R2 is a strict subset of R1. In such cases we
|
||||||
|
insert a new range R3 following R2 that fills in the remainder of
|
||||||
|
the address space covered by R1. That lets a relatively simple
|
||||||
|
search find the correct range.
|
||||||
|
|
||||||
|
These overlaps can occur because of the range merging we do in
|
||||||
|
add_unit_addr. When the linker de-duplicates functions, it can
|
||||||
|
leave behind an address range that refers to the address range of
|
||||||
|
the retained duplicate. If the retained duplicate address range is
|
||||||
|
merged with others, then after sorting we can see overlapping
|
||||||
|
address ranges.
|
||||||
|
|
||||||
|
See https://github.com/ianlancetaylor/libbacktrace/issues/137. */
|
||||||
|
|
||||||
|
static int
|
||||||
|
resolve_unit_addrs_overlap (struct backtrace_state *state,
|
||||||
|
backtrace_error_callback error_callback,
|
||||||
|
void *data, struct unit_addrs_vector *addrs_vec)
|
||||||
|
{
|
||||||
|
struct unit_addrs *addrs;
|
||||||
|
size_t count;
|
||||||
|
int found;
|
||||||
|
struct unit_addrs *entry;
|
||||||
|
size_t i;
|
||||||
|
struct unit_addrs_vector new_vec;
|
||||||
|
void *grew;
|
||||||
|
size_t from;
|
||||||
|
size_t to;
|
||||||
|
|
||||||
|
addrs = (struct unit_addrs *) addrs_vec->vec.base;
|
||||||
|
count = addrs_vec->count;
|
||||||
|
|
||||||
|
if (count == 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
/* Optimistically assume that overlaps are rare. */
|
||||||
|
found = 0;
|
||||||
|
entry = addrs;
|
||||||
|
for (i = 0; i < count - 1; i++)
|
||||||
|
{
|
||||||
|
if (entry->low < (entry + 1)->low
|
||||||
|
&& entry->high > (entry + 1)->high)
|
||||||
|
{
|
||||||
|
found = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
entry++;
|
||||||
|
}
|
||||||
|
if (!found)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
memset (&new_vec, 0, sizeof new_vec);
|
||||||
|
grew = backtrace_vector_grow (state,
|
||||||
|
count * sizeof (struct unit_addrs),
|
||||||
|
error_callback, data, &new_vec.vec);
|
||||||
|
if (grew == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
from = 0;
|
||||||
|
to = 0;
|
||||||
|
resolve_unit_addrs_overlap_walk (state, &from, &to, NULL, addrs_vec,
|
||||||
|
error_callback, data, &new_vec);
|
||||||
|
backtrace_vector_free (state, &addrs_vec->vec, error_callback, data);
|
||||||
|
*addrs_vec = new_vec;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* Sort the line vector by PC. We want a stable sort here to maintain
|
/* Sort the line vector by PC. We want a stable sort here to maintain
|
||||||
the order of lines for the same PC values. Since the sequence is
|
the order of lines for the same PC values. Since the sequence is
|
||||||
being sorted in place, their addresses cannot be relied on to
|
being sorted in place, their addresses cannot be relied on to
|
||||||
@@ -3314,7 +3502,7 @@ read_line_info (struct backtrace_state *state, struct dwarf_data *ddata,
|
|||||||
|
|
||||||
if (vec.count == 0)
|
if (vec.count == 0)
|
||||||
{
|
{
|
||||||
/* This is not a failure in the sense of a generating an error,
|
/* This is not a failure in the sense of generating an error,
|
||||||
but it is a failure in that sense that we have no useful
|
but it is a failure in that sense that we have no useful
|
||||||
information. */
|
information. */
|
||||||
goto fail;
|
goto fail;
|
||||||
@@ -4300,11 +4488,7 @@ build_dwarf_data (struct backtrace_state *state,
|
|||||||
void *data)
|
void *data)
|
||||||
{
|
{
|
||||||
struct unit_addrs_vector addrs_vec;
|
struct unit_addrs_vector addrs_vec;
|
||||||
struct unit_addrs *addrs;
|
|
||||||
size_t addrs_count;
|
|
||||||
struct unit_vector units_vec;
|
struct unit_vector units_vec;
|
||||||
struct unit **units;
|
|
||||||
size_t units_count;
|
|
||||||
struct dwarf_data *fdata;
|
struct dwarf_data *fdata;
|
||||||
|
|
||||||
if (!build_address_map (state, base_address, dwarf_sections, is_bigendian,
|
if (!build_address_map (state, base_address, dwarf_sections, is_bigendian,
|
||||||
@@ -4316,12 +4500,12 @@ build_dwarf_data (struct backtrace_state *state,
|
|||||||
return NULL;
|
return NULL;
|
||||||
if (!backtrace_vector_release (state, &units_vec.vec, error_callback, data))
|
if (!backtrace_vector_release (state, &units_vec.vec, error_callback, data))
|
||||||
return NULL;
|
return NULL;
|
||||||
addrs = (struct unit_addrs *) addrs_vec.vec.base;
|
|
||||||
units = (struct unit **) units_vec.vec.base;
|
backtrace_qsort ((struct unit_addrs *) addrs_vec.vec.base, addrs_vec.count,
|
||||||
addrs_count = addrs_vec.count;
|
sizeof (struct unit_addrs), unit_addrs_compare);
|
||||||
units_count = units_vec.count;
|
if (!resolve_unit_addrs_overlap (state, error_callback, data, &addrs_vec))
|
||||||
backtrace_qsort (addrs, addrs_count, sizeof (struct unit_addrs),
|
return NULL;
|
||||||
unit_addrs_compare);
|
|
||||||
/* No qsort for units required, already sorted. */
|
/* No qsort for units required, already sorted. */
|
||||||
|
|
||||||
fdata = ((struct dwarf_data *)
|
fdata = ((struct dwarf_data *)
|
||||||
@@ -4333,10 +4517,10 @@ build_dwarf_data (struct backtrace_state *state,
|
|||||||
fdata->next = NULL;
|
fdata->next = NULL;
|
||||||
fdata->altlink = altlink;
|
fdata->altlink = altlink;
|
||||||
fdata->base_address = base_address;
|
fdata->base_address = base_address;
|
||||||
fdata->addrs = addrs;
|
fdata->addrs = (struct unit_addrs *) addrs_vec.vec.base;
|
||||||
fdata->addrs_count = addrs_count;
|
fdata->addrs_count = addrs_vec.count;
|
||||||
fdata->units = units;
|
fdata->units = (struct unit **) units_vec.vec.base;
|
||||||
fdata->units_count = units_count;
|
fdata->units_count = units_vec.count;
|
||||||
fdata->dwarf_sections = *dwarf_sections;
|
fdata->dwarf_sections = *dwarf_sections;
|
||||||
fdata->is_bigendian = is_bigendian;
|
fdata->is_bigendian = is_bigendian;
|
||||||
memset (&fdata->fvec, 0, sizeof fdata->fvec);
|
memset (&fdata->fvec, 0, sizeof fdata->fvec);
|
||||||
|
|||||||
Reference in New Issue
Block a user