Fix latent bug in dwarf2_find_containing_comp_unit

dwarf2_find_containing_comp_unit has this in its binary search:

      if (mid_cu->is_dwz > offset_in_dwz
	  || (mid_cu->is_dwz == offset_in_dwz
	      && mid_cu->sect_off + mid_cu->length >= sect_off))
	high = mid;

The intent here is to determine whether SECT_OFF appears in or before
MID_CU.

I believe this has an off-by-one error, and that the check should use
">" rather than ">=".  If the two side are equal, then SECT_OFF
actually appears at the start of the next CU.

I've had this patch kicking around for ages but I forget how I found
the problem.

gdb/ChangeLog
2020-02-20  Tom Tromey  <tom@tromey.com>

	* dwarf2/read.c (dwarf2_find_containing_comp_unit): Use ">", not
	">=", in binary search.
	(dwarf2_find_containing_comp_unit): New overload.
	(run_test): New self-test.
	(_initialize_dwarf2_read): Register new test.
This commit is contained in:
Tom Tromey 2020-02-20 18:22:09 -07:00
parent 54b2aec10d
commit 22b6cd7043
2 changed files with 99 additions and 19 deletions

View File

@ -1,3 +1,11 @@
2020-02-20 Tom Tromey <tom@tromey.com>
* dwarf2/read.c (dwarf2_find_containing_comp_unit): Use ">", not
">=", in binary search.
(dwarf2_find_containing_comp_unit): New overload.
(run_test): New self-test.
(_initialize_dwarf2_read): Register new test.
2020-02-20 Nelson Chu <nelson.chu@sifive.com>
* riscv-tdep.c: Updated since the DECLARE_CSR is changed.

View File

@ -24136,6 +24136,39 @@ dwarf2_per_cu_data::addr_type () const
return addr_type;
}
/* A helper function for dwarf2_find_containing_comp_unit that returns
the index of the result, and that searches a vector. It will
return a result even if the offset in question does not actually
occur in any CU. This is separate so that it can be unit
tested. */
static int
dwarf2_find_containing_comp_unit
(sect_offset sect_off,
unsigned int offset_in_dwz,
const std::vector<dwarf2_per_cu_data *> &all_comp_units)
{
int low, high;
low = 0;
high = all_comp_units.size () - 1;
while (high > low)
{
struct dwarf2_per_cu_data *mid_cu;
int mid = low + (high - low) / 2;
mid_cu = all_comp_units[mid];
if (mid_cu->is_dwz > offset_in_dwz
|| (mid_cu->is_dwz == offset_in_dwz
&& mid_cu->sect_off + mid_cu->length > sect_off))
high = mid;
else
low = mid + 1;
}
gdb_assert (low == high);
return low;
}
/* Locate the .debug_info compilation unit from CU's objfile which contains
the DIE at OFFSET. Raises an error on failure. */
@ -24144,26 +24177,12 @@ dwarf2_find_containing_comp_unit (sect_offset sect_off,
unsigned int offset_in_dwz,
struct dwarf2_per_objfile *dwarf2_per_objfile)
{
struct dwarf2_per_cu_data *this_cu;
int low, high;
int low
= dwarf2_find_containing_comp_unit (sect_off, offset_in_dwz,
dwarf2_per_objfile->all_comp_units);
struct dwarf2_per_cu_data *this_cu
= dwarf2_per_objfile->all_comp_units[low];
low = 0;
high = dwarf2_per_objfile->all_comp_units.size () - 1;
while (high > low)
{
struct dwarf2_per_cu_data *mid_cu;
int mid = low + (high - low) / 2;
mid_cu = dwarf2_per_objfile->all_comp_units[mid];
if (mid_cu->is_dwz > offset_in_dwz
|| (mid_cu->is_dwz == offset_in_dwz
&& mid_cu->sect_off + mid_cu->length >= sect_off))
high = mid;
else
low = mid + 1;
}
gdb_assert (low == high);
this_cu = dwarf2_per_objfile->all_comp_units[low];
if (this_cu->is_dwz != offset_in_dwz || this_cu->sect_off > sect_off)
{
if (low == 0 || this_cu->is_dwz != offset_in_dwz)
@ -24186,6 +24205,57 @@ dwarf2_find_containing_comp_unit (sect_offset sect_off,
}
}
#if GDB_SELF_TEST
namespace selftests {
namespace find_containing_comp_unit {
static void
run_test ()
{
struct dwarf2_per_cu_data one {};
struct dwarf2_per_cu_data two {};
struct dwarf2_per_cu_data three {};
struct dwarf2_per_cu_data four {};
one.length = 5;
two.sect_off = sect_offset (one.length);
two.length = 7;
three.length = 5;
three.is_dwz = 1;
four.sect_off = sect_offset (three.length);
four.length = 7;
four.is_dwz = 1;
std::vector<dwarf2_per_cu_data *> units;
units.push_back (&one);
units.push_back (&two);
units.push_back (&three);
units.push_back (&four);
int result;
result = dwarf2_find_containing_comp_unit (sect_offset (0), 0, units);
SELF_CHECK (units[result] == &one);
result = dwarf2_find_containing_comp_unit (sect_offset (3), 0, units);
SELF_CHECK (units[result] == &one);
result = dwarf2_find_containing_comp_unit (sect_offset (5), 0, units);
SELF_CHECK (units[result] == &two);
result = dwarf2_find_containing_comp_unit (sect_offset (0), 1, units);
SELF_CHECK (units[result] == &three);
result = dwarf2_find_containing_comp_unit (sect_offset (3), 1, units);
SELF_CHECK (units[result] == &three);
result = dwarf2_find_containing_comp_unit (sect_offset (5), 1, units);
SELF_CHECK (units[result] == &four);
}
}
}
#endif /* GDB_SELF_TEST */
/* Initialize dwarf2_cu CU, owned by PER_CU. */
dwarf2_cu::dwarf2_cu (struct dwarf2_per_cu_data *per_cu_)
@ -24690,5 +24760,7 @@ Warning: This option must be enabled before gdb reads the file."),
#if GDB_SELF_TEST
selftests::register_test ("dw2_expand_symtabs_matching",
selftests::dw2_expand_symtabs_matching::run_test);
selftests::register_test ("dwarf2_find_containing_comp_unit",
selftests::find_containing_comp_unit::run_test);
#endif
}