Implement dwarf5 range lists.

This is a big change. dwarf5 range lists are quite a bit more complicated
than dwarf 4 range lists, both in the contextual information required, and
in their own representation and interpretation.

The big design choice here is how to pass the CU information all the
way down to the reader. I chose a structure, because otherwise the
parameter list gets very long and error prone (and has to be passed
down several levels). This structure could be made a parto of the CU
context itself, or the range handler, so it wouldn't have to be
separately assembled at range-list read time, but both of those
solutions get even more invasive, and harder to follow.

I've tried to figure out how to break this into smaller changes, but it
affects nearly everything that has to do with a compilation unit's
own addresses and when decisions must be made about how to read them.
Dependency injection will do that to you.

It does add tests for range list reading, which did not exist before.

Change-Id: I923b9a2c3379a0f52609bc05310097de5cbb7227
Reviewed-on: https://chromium-review.googlesource.com/c/breakpad/breakpad/+/2446635
Reviewed-by: Joshua Peraza <jperaza@chromium.org>
This commit is contained in:
Sterling Augustine 2020-10-02 11:14:49 -07:00
parent 5c7535af78
commit 9ecccc5512
10 changed files with 471 additions and 105 deletions

View file

@ -491,3 +491,142 @@ INSTANTIATE_TEST_CASE_P(
DwarfHeaderParams(kBigEndian, 8, 3, 8),
DwarfHeaderParams(kBigEndian, 8, 4, 4),
DwarfHeaderParams(kBigEndian, 8, 4, 8)));
class MockRangeListHandler: public dwarf2reader::RangeListHandler {
public:
MOCK_METHOD(void, AddRange, (uint64_t begin, uint64_t end));
MOCK_METHOD(void, Finish, ());
};
TEST(RangeList, Dwarf4ReadRangeList) {
using dwarf2reader::RangeListReader;
using dwarf2reader::DW_FORM_sec_offset;
// Create a dwarf4 .debug_ranges section.
google_breakpad::test_assembler::Section ranges(kBigEndian);
std::string padding_offset = "padding offset";
ranges.Append(padding_offset);
const uint64_t section_offset = ranges.Size();
ranges.D32(1).D32(2); // (2, 3)
ranges.D32(0xFFFFFFFF).D32(3); // base_address = 3.
ranges.D32(1).D32(2); // (4, 5)
ranges.D32(0).D32(1); // (3, 4) An out of order entry is legal.
ranges.D32(0).D32(0); // End of range.
std::string section_contents;
ranges.GetContents(&section_contents);
ByteReader byte_reader(ENDIANNESS_BIG);
byte_reader.SetAddressSize(4);
RangeListReader::CURangesInfo cu_info;
// Only set the fields that matter for dwarf 4.
cu_info.version_ = 4;
cu_info.base_address_ = 1;
cu_info.buffer_ = reinterpret_cast<const uint8_t*>(section_contents.data());
cu_info.size_ = section_contents.size();
MockRangeListHandler handler;
dwarf2reader::RangeListReader range_list_reader(&byte_reader, &cu_info,
&handler);
EXPECT_CALL(handler, AddRange(2, 3));
EXPECT_CALL(handler, AddRange(4, 5));
EXPECT_CALL(handler, AddRange(3, 4));
EXPECT_CALL(handler, Finish());
EXPECT_TRUE(range_list_reader.ReadRanges(DW_FORM_sec_offset,
section_offset));
}
TEST(RangeList, Dwarf5ReadRangeList) {
using dwarf2reader::RangeListReader;
using dwarf2reader::DW_RLE_base_addressx;
using dwarf2reader::DW_RLE_startx_endx;
using dwarf2reader::DW_RLE_startx_length;
using dwarf2reader::DW_RLE_offset_pair;
using dwarf2reader::DW_RLE_end_of_list;
using dwarf2reader::DW_RLE_base_address;
using dwarf2reader::DW_RLE_offset_pair;
using dwarf2reader::DW_RLE_start_end;
using dwarf2reader::DW_RLE_start_length;
using dwarf2reader::DW_RLE_end_of_list;
using dwarf2reader::DW_FORM_sec_offset;
using dwarf2reader::DW_FORM_rnglistx;
// .debug_addr for the indexed entries like startx.
Section addr;
addr.set_endianness(kBigEndian);
// Test addr_base handling with a padding address at 0.
addr.D32(0).D32(1).D32(2).D32(3).D32(4);
std::string addr_contents;
assert(addr.GetContents(&addr_contents));
// .debug_rnglists is the dwarf 5 section.
Section rnglists;
rnglists.set_endianness(kBigEndian);
std::string padding_offset = "padding offset";
rnglists.Append(padding_offset);
const uint64_t ranges_base = rnglists.Size();
// Header
Label section_size;
rnglists.Append(kBigEndian, 4, section_size);
rnglists.D16(5); // Version
rnglists.D8(4); // Address size
rnglists.D8(0); // Segment selector size
rnglists.D32(2); // Offset entry count
// Offset entries.
Label range0;
rnglists.Append(kBigEndian, 4, range0);
Label range1;
rnglists.Append(kBigEndian, 4, range1);
// Range 0 (will be read via DW_AT_ranges, DW_FORM_sec_offset).
range0 = rnglists.Size();
rnglists.D8(DW_RLE_base_addressx).ULEB128(0); // base_addr = 1
rnglists.D8(DW_RLE_startx_endx).ULEB128(1).ULEB128(2); // (2, 3)
rnglists.D8(DW_RLE_startx_length).ULEB128(3).ULEB128(1); // (4, 5)
rnglists.D8(DW_RLE_offset_pair).ULEB128(5).ULEB128(6); // (6, 7)
rnglists.D8(DW_RLE_end_of_list);
// Range 1 (will be read via DW_AT_ranges, DW_FORM_rnglistx).
range1 = rnglists.Size();
rnglists.D8(DW_RLE_base_address).D32(8); // base_addr = 8
rnglists.D8(DW_RLE_offset_pair).ULEB128(1).ULEB128(2); // (9, 10)
rnglists.D8(DW_RLE_start_end).D32(10).D32(11); // (10, 11)
rnglists.D8(DW_RLE_start_length).D32(12).ULEB128(1); // (12, 13)
rnglists.D8(DW_RLE_end_of_list);
section_size = rnglists.Size();
std::string rnglists_contents;
assert(rnglists.GetContents(&rnglists_contents));
RangeListReader::CURangesInfo cu_info;
// Only set the fields that matter for dwarf 4.
cu_info.version_ = 5;
cu_info.base_address_ = 1;
cu_info.ranges_base_ = ranges_base;
cu_info.buffer_ =
reinterpret_cast<const uint8_t*>(rnglists_contents.data());
cu_info.size_ = rnglists_contents.size();
cu_info.addr_buffer_ =
reinterpret_cast<const uint8_t*>(addr_contents.data());
cu_info.addr_buffer_size_ = addr_contents.size();
cu_info.addr_base_ = 4;
ByteReader byte_reader(ENDIANNESS_BIG);
byte_reader.SetAddressSize(4);
MockRangeListHandler handler;
dwarf2reader::RangeListReader range_list_reader(&byte_reader, &cu_info,
&handler);
EXPECT_CALL(handler, AddRange(2, 3));
EXPECT_CALL(handler, AddRange(4, 5));
EXPECT_CALL(handler, AddRange(6, 7));
EXPECT_CALL(handler, AddRange(9, 10));
EXPECT_CALL(handler, AddRange(10, 11));
EXPECT_CALL(handler, AddRange(12, 13));
EXPECT_CALL(handler, Finish()).Times(2);
EXPECT_TRUE(range_list_reader.ReadRanges(DW_FORM_rnglistx, 1));
EXPECT_TRUE(range_list_reader.ReadRanges(DW_FORM_sec_offset,
range0.Value()));
// Out of range index, should result in no calls.
EXPECT_FALSE(range_list_reader.ReadRanges(DW_FORM_rnglistx, 2));
}