I've added some read-side C++ support for rangelistptr attr values and for
aranges. The dwarf::range_list class is really just for the DW_AT_ranges
attr comparisons. debug_info_entry.ranges () is what things will use for
semantics (it uses dwarf_ranges, i.e. combining either ranges or lo/hi pc).
dwarf.aranges () is a slow, copying presentation of the aranges suitable
for comparing elements to a compile_unit.ranges (). I didn't make it a
thin reader because we won't really use this outside of dwarflint.
(The unit_by_addr, whenever I get to it, will be a thin layer on
dwarf.aranges () yields a map of compile_unit to arange_list.
arange_list and the other flavors from die.ranges () or attr_value.ranges ()
all act like set<pair<Dwarf_Addr, Dwarf_Addr>> (or unordered_set) for
templatey purposes. You can compare them to each other, and all have
a .to_string () for pretty-printing.
The comparisons canonicalize by coalescing contiguous adjacent ranges.
That is true for "semantically equal", and also notes for later what we can
do as an optimization in generating ranges and aranges for output. But
possibly one might want not to coalesce for dwarflint comparison, since
ranges/aranges not matching exactly from the generator seems suspicious.
Lots of tiny ranges that are actually adjacent and contiguous but generated
as many entries can be normal. It's especially common for C++ where
everything is in a zillion tiny little .gnu.linkonce sections. When the
program is finally linked, it all winds up in .text and packed in, so the
many entries have adjacent addresses and look superfluous. In fact they
are superfluous and we can coalesce them away for smaller .debug_aranges
sections. But they're not suspicious, since they made perfect sense as
emitted before the final link.
Another thing to note for output-side optimization is that it's common to
have many entries that are adjacent except for a gap of a few bytes. This
too is normal and correct as generated. The gaps are alignment bits in
.text between multiple entry points in a CU. It's correct precision to
leave the gaps there in DIE range lists. But .debug_aranges exists purely
for quick lookup of the containing CU. So when a gap that's not part of
any CU is small (i.e. less than sh_addralign of the section containing both
addresses), we can just coalesce across it so the addresses in the gap map
to the CU it's in the middle of. If someone is actually searching for the
gap addresses, they will just descend the CU and not find it, which is
fine. More commonly, we'll reduce the entries to just a few for each CU
and keep the table small so searches are fast.
The following is what I used to test this. But it really belongs in a
dwarflint high-level check, not in dwarfcmp. This code already showed some
interesting anomalies. dwarflint should eventually show the differences in
some way that's especially helpful for debugging the generators. That will
be handy in diagnosing and reporting the details of what GCC does wrong.
Someone should investigate these anomalies, but for now it will have to
take a back seat to our critical path on DWARF writing/transformation work.
diff --git a/src/dwarfcmp.cc b/src/dwarfcmp.cc
index 522b719..0000000 100644
@@ -304,6 +304,20 @@ main (int argc, char *argv)
dwarf file1 (dw1);
dwarf file2 (dw2);
+ const dwarf::aranges_map aranges = file1.aranges ();
+ for (dwarf::aranges_map::const_iterator i = aranges.begin ();
+ i != aranges.end ();
+ const dwarf::compile_unit &cu = (*i).first;
+ const dwarf::arange_list &ranges = (*i).second;
+ cout << (cu.ranges () == ranges ? "match" : "mismatch")
+ << " aranges " << ranges.to_string () << " vs
+ << cu.ranges ().to_string () << endl;
result = !(file1 == file2);