From 9ecccc5512f51982700c5ec8827de4ca7e9f0462 Mon Sep 17 00:00:00 2001 From: Sterling Augustine Date: Fri, 2 Oct 2020 11:14:49 -0700 Subject: [PATCH] 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 --- src/common/dwarf/dwarf2enums.h | 16 ++ src/common/dwarf/dwarf2reader.cc | 147 ++++++++++++++++-- src/common/dwarf/dwarf2reader.h | 74 +++++++-- src/common/dwarf/dwarf2reader_die_unittest.cc | 139 +++++++++++++++++ src/common/dwarf_cu_to_module.cc | 97 ++++++++++-- src/common/dwarf_cu_to_module.h | 12 +- src/common/dwarf_range_list_handler.cc | 6 +- src/common/dwarf_range_list_handler.h | 11 +- src/common/linux/dump_symbols.cc | 37 ++--- src/common/mac/dump_syms.cc | 37 ++--- 10 files changed, 471 insertions(+), 105 deletions(-) diff --git a/src/common/dwarf/dwarf2enums.h b/src/common/dwarf/dwarf2enums.h index 7bd39792..b93510a7 100644 --- a/src/common/dwarf/dwarf2enums.h +++ b/src/common/dwarf/dwarf2enums.h @@ -169,6 +169,7 @@ enum DwarfForm { DW_FORM_ref_sig8 = 0x20, // Added in DWARF 5: + DW_FORM_rnglistx = 0x23, DW_FORM_strx1 = 0x25, DW_FORM_strx2 = 0x26, DW_FORM_strx3 = 0x27, @@ -264,6 +265,9 @@ enum DwarfAttribute { DW_AT_call_line = 0x59, // DWARF 4 DW_AT_linkage_name = 0x6e, + // DWARF 5 + DW_AT_addr_base = 0x73, + DW_AT_rnglists_base = 0x74, // SGI/MIPS extensions. DW_AT_MIPS_fde = 0x2001, DW_AT_MIPS_loop_begin = 0x2002, @@ -316,6 +320,18 @@ enum DwarfAttribute { DW_AT_PGI_lstride = 0x3a02 }; +// .debug_rngslist entry types +enum DwarfRngListEntry { + DW_RLE_end_of_list = 0, + DW_RLE_base_addressx = 1, + DW_RLE_startx_endx = 2, + DW_RLE_startx_length = 3, + DW_RLE_offset_pair = 4, + DW_RLE_base_address = 5, + DW_RLE_start_end = 6, + DW_RLE_start_length = 7, +}; + // Line number content type codes (DWARF 5). enum DwarfLineNumberContentType { DW_LNCT_path = 1, diff --git a/src/common/dwarf/dwarf2reader.cc b/src/common/dwarf/dwarf2reader.cc index aca83677..f1f4007d 100644 --- a/src/common/dwarf/dwarf2reader.cc +++ b/src/common/dwarf/dwarf2reader.cc @@ -226,6 +226,7 @@ const uint8_t* CompilationUnit::SkipAttribute(const uint8_t* start, case DW_FORM_GNU_str_index: case DW_FORM_GNU_addr_index: case DW_FORM_addrx: + case DW_FORM_rnglistx: reader_->ReadUnsignedLEB128(start, &len); return start + len; @@ -657,6 +658,10 @@ const uint8_t* CompilationUnit::ProcessAttribute( ProcessAttributeAddrIndex( dieoffset, attr, form, reader_->ReadFourBytes(start)); return start + 4; + case DW_FORM_rnglistx: + ProcessAttributeUnsigned( + dieoffset, attr, form, reader_->ReadUnsignedLEB128(start, &len)); + return start + len; } fprintf(stderr, "Unhandled form type\n"); return NULL; @@ -1568,11 +1573,76 @@ void LineInfo::ReadLines() { after_header_ = lengthstart + header_.total_length; } -RangeListReader::RangeListReader(const uint8_t* buffer, uint64_t size, - ByteReader* reader, RangeListHandler* handler) - : buffer_(buffer), size_(size), reader_(reader), handler_(handler) { } +bool RangeListReader::SetRangesBase(uint64_t offset) { + // Versions less than 5 don't use ranges base. + if (cu_info_->version_ < 5) { + return true; + } + // Length may not be 12 bytes, but if 12 bytes aren't available + // at this point, then the header is too short. + if (offset + 12 >= cu_info_->size_) { + return false; + } + // The length of this CU's contribution. + uint64_t cu_length = reader_->ReadFourBytes(cu_info_->buffer_ + offset); + offset += 4; + if (cu_length == 0xffffffffUL) { + cu_length = reader_->ReadEightBytes(cu_info_->buffer_ + offset); + offset += 8; + } -bool RangeListReader::ReadRangeList(uint64_t offset) { + // Truncating size here results in correctly ignoring everything not from + // this cu from here on out. + cu_info_->size_ = offset + cu_length; + + // Check for the rest of the header in advance. + if (offset + 8 >= cu_info_->size_) { + return false; + } + // Version. Can only read version 5. + if (reader_->ReadTwoBytes(cu_info_->buffer_ + offset) != 5) { + return false; + } + offset += 2; + // Address size + if (reader_->ReadOneByte(cu_info_->buffer_ + offset) != + reader_->AddressSize()) { + return false; + } + offset += 1; + // Segment selectors are unsupported + if (reader_->ReadOneByte(cu_info_->buffer_ + offset) != 0) { + return false; + } + offset += 1; + offset_entry_count_ = reader_->ReadFourBytes(cu_info_->buffer_ + offset); + offset += 4; + offset_array_ = offset; + return true; +} + +bool RangeListReader::ReadRanges(enum DwarfForm form, uint64_t data) { + if (form == DW_FORM_sec_offset) { + if (cu_info_->version_ <= 4) { + return ReadDebugRanges(data); + } else { + return ReadDebugRngList(data); + } + } else if (form == DW_FORM_rnglistx) { + SetRangesBase(cu_info_->ranges_base_); + if (data >= offset_entry_count_) { + return false; + } + uint64_t index_offset = reader_->AddressSize() * data; + uint64_t range_list_offset = + reader_->ReadAddress(cu_info_->buffer_ + offset_array_ + index_offset); + + return ReadDebugRngList(range_list_offset); + } + return false; +} + +bool RangeListReader::ReadDebugRanges(uint64_t offset) { const uint64_t max_address = (reader_->AddressSize() == 4) ? 0xffffffffUL : 0xffffffffffffffffULL; @@ -1580,21 +1650,22 @@ bool RangeListReader::ReadRangeList(uint64_t offset) { bool list_end = false; do { - if (offset > size_ - entry_size) { + if (offset > cu_info_->size_ - entry_size) { return false; // Invalid range detected } - uint64_t start_address = reader_->ReadAddress(buffer_ + offset); - uint64_t end_address = - reader_->ReadAddress(buffer_ + offset + reader_->AddressSize()); + uint64_t start_address = reader_->ReadAddress(cu_info_->buffer_ + offset); + uint64_t end_address = reader_->ReadAddress( + cu_info_->buffer_ + offset + reader_->AddressSize()); if (start_address == max_address) { // Base address selection - handler_->SetBaseAddress(end_address); + cu_info_->base_address_ = end_address; } else if (start_address == 0 && end_address == 0) { // End-of-list handler_->Finish(); list_end = true; } else { // Add a range entry - handler_->AddRange(start_address, end_address); + handler_->AddRange(start_address + cu_info_->base_address_, + end_address + cu_info_->base_address_); } offset += entry_size; @@ -1603,6 +1674,62 @@ bool RangeListReader::ReadRangeList(uint64_t offset) { return true; } +bool RangeListReader::ReadDebugRngList(uint64_t offset) { + uint64_t start = 0; + uint64_t end = 0; + uint64_t range_len = 0; + uint64_t index = 0; + // A uleb128's length isn't known until after it has been read, so overruns + // are only caught after an entire entry. + while (offset < cu_info_->size_) { + uint8_t entry_type = reader_->ReadOneByte(cu_info_->buffer_ + offset); + offset += 1; + // Handle each entry type per Dwarf 5 Standard, section 2.17.3. + switch (entry_type) { + case DW_RLE_end_of_list: + handler_->Finish(); + return true; + case DW_RLE_base_addressx: + offset += ReadULEB(offset, &index); + cu_info_->base_address_ = GetAddressAtIndex(index); + break; + case DW_RLE_startx_endx: + offset += ReadULEB(offset, &index); + start = GetAddressAtIndex(index); + offset += ReadULEB(offset, &index); + end = GetAddressAtIndex(index); + handler_->AddRange(start, end); + break; + case DW_RLE_startx_length: + offset += ReadULEB(offset, &index); + start = GetAddressAtIndex(index); + offset += ReadULEB(offset, &range_len); + handler_->AddRange(start, start + range_len); + break; + case DW_RLE_offset_pair: + offset += ReadULEB(offset, &start); + offset += ReadULEB(offset, &end); + handler_->AddRange(start + cu_info_->base_address_, + end + cu_info_->base_address_); + break; + case DW_RLE_base_address: + offset += ReadAddress(offset, &cu_info_->base_address_); + break; + case DW_RLE_start_end: + offset += ReadAddress(offset, &start); + offset += ReadAddress(offset, &end); + handler_->AddRange(start, end); + break; + case DW_RLE_start_length: + offset += ReadAddress(offset, &start); + offset += ReadULEB(offset, &end); + handler_->AddRange(start, start + end); + break; + } + } + return false; +} + // A DWARF rule for recovering the address or value of a register, or // computing the canonical frame address. There is one subclass of this for // each '*Rule' member function in CallFrameInfo::Handler. diff --git a/src/common/dwarf/dwarf2reader.h b/src/common/dwarf/dwarf2reader.h index e405e3a7..d041dd86 100644 --- a/src/common/dwarf/dwarf2reader.h +++ b/src/common/dwarf/dwarf2reader.h @@ -234,25 +234,78 @@ class RangeListHandler { // Add a range. virtual void AddRange(uint64_t begin, uint64_t end) { }; - // A new base address must be set for computing the ranges' addresses. - virtual void SetBaseAddress(uint64_t base_address) { }; - // Finish processing the range list. virtual void Finish() { }; }; class RangeListReader { public: - RangeListReader(const uint8_t* buffer, uint64_t size, ByteReader* reader, - RangeListHandler* handler); + // Reading a range list requires quite a bit of information + // from the compilation unit. Package it conveniently. + struct CURangesInfo { + CURangesInfo() : + version_(0), base_address_(0), ranges_base_(0), + buffer_(nullptr), size_(0), addr_buffer_(nullptr), + addr_buffer_size_(0), addr_base_(0) { } - bool ReadRangeList(uint64_t offset); + uint16_t version_; + // Ranges base address. Ordinarily the CU's low_pc. + uint64_t base_address_; + // Offset into .debug_rnglists for this CU's rangelists. + uint64_t ranges_base_; + // Contents of either .debug_ranges or .debug_rnglists. + const uint8_t* buffer_; + uint64_t size_; + // Contents of .debug_addr. This cu's contribution starts at + // addr_base_ + const uint8_t* addr_buffer_; + uint64_t addr_buffer_size_; + uint64_t addr_base_; + }; + + RangeListReader(ByteReader* reader, CURangesInfo* cu_info, + RangeListHandler* handler) : + reader_(reader), cu_info_(cu_info), handler_(handler), + offset_array_(0), offset_entry_count_(0) { } + + // Read ranges from cu_info as specified by form and data. + bool ReadRanges(enum DwarfForm form, uint64_t data); private: - const uint8_t* buffer_; - uint64_t size_; + bool SetRangesBase(uint64_t base); + + // Read dwarf4 .debug_ranges at offset. + bool ReadDebugRanges(uint64_t offset); + // Read dwarf5 .debug_rngslist at offset. + bool ReadDebugRngList(uint64_t offset); + + // Convenience functions to handle the mechanics of reading entries in the + // ranges section. + uint64_t ReadULEB(uint64_t offset, uint64_t* value) { + uint64_t len; + *value = reader_->ReadUnsignedLEB128(cu_info_->buffer_ + offset, &len); + return len; + } + + uint64_t ReadAddress(uint64_t offset, uint64_t* value) { + *value = reader_->ReadAddress(cu_info_->buffer_ + offset); + return reader_->AddressSize(); + } + + // Read the address at this CU's addr_index in the .debug_addr section. + uint64_t GetAddressAtIndex(uint64_t addr_index) { + assert(cu_info_->addr_buffer_ != nullptr); + uint64_t offset = + cu_info_->addr_base_ + addr_index * reader_->AddressSize(); + assert(offset < cu_info_->addr_buffer_size_); + return reader_->ReadAddress(cu_info_->addr_buffer_ + offset); + } + ByteReader* reader_; + CURangesInfo* cu_info_; RangeListHandler* handler_; + uint64_t offset_array_; + uint64_t offset_entry_count_; }; // This class is the main interface between the reader and the @@ -492,7 +545,7 @@ class CompilationUnit { else if (attr == DW_AT_GNU_addr_base) { addr_base_ = data; } - else if (attr == DW_AT_GNU_ranges_base) { + else if (attr == DW_AT_GNU_ranges_base || attr == DW_AT_rnglists_base) { ranges_base_ = data; } // TODO(yunlian): When we add DW_AT_ranges_base from DWARF-5, @@ -654,7 +707,8 @@ class CompilationUnit { // from the skeleton CU. uint64_t skeleton_dwo_id_; - // The value of the DW_AT_GNU_ranges_base attribute, if any. + // The value of the DW_AT_GNU_ranges_base or DW_AT_rnglists_base attribute, + // if any. uint64_t ranges_base_; // The value of the DW_AT_GNU_addr_base attribute, if any. diff --git a/src/common/dwarf/dwarf2reader_die_unittest.cc b/src/common/dwarf/dwarf2reader_die_unittest.cc index 87819322..0d8811bb 100644 --- a/src/common/dwarf/dwarf2reader_die_unittest.cc +++ b/src/common/dwarf/dwarf2reader_die_unittest.cc @@ -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(§ion_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(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(rnglists_contents.data()); + cu_info.size_ = rnglists_contents.size(); + cu_info.addr_buffer_ = + reinterpret_cast(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)); +} diff --git a/src/common/dwarf_cu_to_module.cc b/src/common/dwarf_cu_to_module.cc index a5bc7d6c..168c4665 100644 --- a/src/common/dwarf_cu_to_module.cc +++ b/src/common/dwarf_cu_to_module.cc @@ -172,13 +172,16 @@ bool DwarfCUToModule::FileContext::IsUnhandledInterCUReference( struct DwarfCUToModule::CUContext { CUContext(FileContext* file_context_arg, WarningReporter* reporter_arg, RangesHandler* ranges_handler_arg) - : file_context(file_context_arg), + : version(0), + file_context(file_context_arg), reporter(reporter_arg), ranges_handler(ranges_handler_arg), language(Language::CPlusPlus), low_pc(0), high_pc(0), - ranges(0) {} + ranges_form(dwarf2reader::DW_FORM_sec_offset), + ranges_data(0), + ranges_base(0) { } ~CUContext() { for (vector::iterator it = functions.begin(); @@ -187,6 +190,9 @@ struct DwarfCUToModule::CUContext { } }; + // Dwarf version of the source CU. + uint8_t version; + // The DWARF-bearing file into which this CU was incorporated. FileContext* file_context; @@ -200,11 +206,54 @@ struct DwarfCUToModule::CUContext { const Language* language; // Addresses covered by this CU. If high_pc_ is non-zero then the CU covers - // low_pc to high_pc, otherwise ranges is non-zero and low_pc represents - // the base address of the ranges covered by the CU. + // low_pc to high_pc, otherwise ranges_data is non-zero and low_pc represents + // the base address of the ranges covered by the CU. ranges_data will define + // the CU's actual ranges. uint64_t low_pc; uint64_t high_pc; - uint64_t ranges; + + // Ranges for this CU are read according to this form. + enum dwarf2reader::DwarfForm ranges_form; + uint64_t ranges_data; + + // Offset into .debug_rngslists where this CU's ranges are stored. + // Data in DW_FORM_rnglistx is relative to this offset. + uint64_t ranges_base; + + // Offset into .debug_addr where this CU's addresses are stored. Data in + // form DW_FORM_addrxX is relative to this offset. + uint64_t addr_base; + + // Collect all the data from the CU that a RangeListReader needs to read a + // range. + bool AssembleRangeListInfo( + dwarf2reader::RangeListReader::CURangesInfo* info) { + const dwarf2reader::SectionMap& section_map + = file_context->section_map(); + info->version_ = version; + info->base_address_ = low_pc; + info->ranges_base_ = ranges_base; + const char* section_name = (version <= 4 ? + ".debug_ranges" : ".debug_rnglists"); + dwarf2reader::SectionMap::const_iterator map_entry + = dwarf2reader::GetSectionByName(section_map, section_name); + if (map_entry == section_map.end()) { + return false; + } + info->buffer_ = map_entry->second.first; + info->size_ = map_entry->second.second; + if (version > 4) { + dwarf2reader::SectionMap::const_iterator map_entry + = dwarf2reader::GetSectionByName(section_map, ".debug_addr"); + if (map_entry == section_map.end()) { + return false; + } + info->addr_buffer_ = map_entry->second.first; + info->addr_buffer_size_ = map_entry->second.second; + info->addr_base_ = addr_base; + } + return true; + } // The functions defined in this compilation unit. We accumulate // them here during parsing. Then, in DwarfCUToModule::Finish, we @@ -470,7 +519,9 @@ class DwarfCUToModule::FuncHandler: public GenericDIEHandler { uint64_t offset) : GenericDIEHandler(cu_context, parent_context, offset), low_pc_(0), high_pc_(0), high_pc_form_(dwarf2reader::DW_FORM_addr), - ranges_(0), abstract_origin_(NULL), inline_(false) { } + ranges_form_(dwarf2reader::DW_FORM_sec_offset), ranges_data_(0), + abstract_origin_(NULL), inline_(false) { } + void ProcessAttributeUnsigned(enum DwarfAttribute attr, enum DwarfForm form, uint64_t data); @@ -490,7 +541,8 @@ class DwarfCUToModule::FuncHandler: public GenericDIEHandler { string name_; uint64_t low_pc_, high_pc_; // DW_AT_low_pc, DW_AT_high_pc DwarfForm high_pc_form_; // DW_AT_high_pc can be length or address. - uint64_t ranges_; // DW_AT_ranges + DwarfForm ranges_form_; // DW_FORM_sec_offset or DW_FORM_rnglistx + uint64_t ranges_data_; // DW_AT_ranges const AbstractOrigin* abstract_origin_; bool inline_; }; @@ -511,7 +563,8 @@ void DwarfCUToModule::FuncHandler::ProcessAttributeUnsigned( high_pc_ = data; break; case dwarf2reader::DW_AT_ranges: - ranges_ = data; + ranges_data_ = data; + ranges_form_ = form; break; default: @@ -590,7 +643,7 @@ void DwarfCUToModule::FuncHandler::Finish() { iter->second->name = name_; } - if (!ranges_) { + if (!ranges_data_) { // Make high_pc_ an address, if it isn't already. if (high_pc_form_ != dwarf2reader::DW_FORM_addr && high_pc_form_ != dwarf2reader::DW_FORM_GNU_addr_index && @@ -606,14 +659,17 @@ void DwarfCUToModule::FuncHandler::Finish() { ranges.push_back(range); } else { RangesHandler* ranges_handler = cu_context_->ranges_handler; - if (ranges_handler) { - if (!ranges_handler->ReadRanges(ranges_, cu_context_->low_pc, &ranges)) { - ranges.clear(); - cu_context_->reporter->MalformedRangeList(ranges_); + dwarf2reader::RangeListReader::CURangesInfo cu_info; + if (cu_context_->AssembleRangeListInfo(&cu_info)) { + if (!ranges_handler->ReadRanges(ranges_form_, ranges_data_, + &cu_info, &ranges)) { + ranges.clear(); + cu_context_->reporter->MalformedRangeList(ranges_data_); + } + } else { + cu_context_->reporter->MissingRanges(); } - } else { - cu_context_->reporter->MissingRanges(); } } @@ -843,7 +899,15 @@ void DwarfCUToModule::ProcessAttributeUnsigned(enum DwarfAttribute attr, cu_context_->high_pc = data; break; case dwarf2reader::DW_AT_ranges: - cu_context_->ranges = data; + cu_context_->ranges_data = data; + cu_context_->ranges_form = form; + break; + case dwarf2reader::DW_AT_rnglists_base: + cu_context_->ranges_base = data; + break; + case dwarf2reader::DW_AT_addr_base: + case dwarf2reader::DW_AT_GNU_addr_base: + cu_context_->addr_base = data; break; default: @@ -1262,6 +1326,7 @@ bool DwarfCUToModule::StartCompilationUnit(uint64_t offset, uint8_t offset_size, uint64_t cu_length, uint8_t dwarf_version) { + cu_context_->version = dwarf_version; return dwarf_version >= 2; } diff --git a/src/common/dwarf_cu_to_module.h b/src/common/dwarf_cu_to_module.h index 3e15b667..386d6281 100644 --- a/src/common/dwarf_cu_to_module.h +++ b/src/common/dwarf_cu_to_module.h @@ -45,7 +45,6 @@ #include "common/language.h" #include "common/module.h" -#include "common/dwarf/bytereader.h" #include "common/dwarf/dwarf2diehandler.h" #include "common/dwarf/dwarf2reader.h" #include "common/scoped_ptr.h" @@ -131,12 +130,11 @@ class DwarfCUToModule: public dwarf2reader::RootDIEHandler { virtual ~RangesHandler() { } // Called when finishing a function to populate the function's ranges. - // The ranges' entries are read starting from offset in the .debug_ranges - // section, base_address holds the base PC the range list values are - // offsets off. Return false if the rangelist falls out of the - // .debug_ranges section. - virtual bool ReadRanges(uint64_t offset, Module::Address base_address, - vector* ranges) = 0; + // The entries are read according to the form and data. + virtual bool ReadRanges( + enum dwarf2reader::DwarfForm form, uint64_t data, + dwarf2reader::RangeListReader::CURangesInfo* cu_info, + vector* ranges) = 0; }; // An abstract base class for handlers that handle DWARF line data diff --git a/src/common/dwarf_range_list_handler.cc b/src/common/dwarf_range_list_handler.cc index 58982aac..3fecb564 100644 --- a/src/common/dwarf_range_list_handler.cc +++ b/src/common/dwarf_range_list_handler.cc @@ -40,15 +40,11 @@ namespace google_breakpad { void DwarfRangeListHandler::AddRange(uint64_t begin, uint64_t end) { - Module::Range r(begin + base_address_, end - begin); + Module::Range r(begin, end - begin); ranges_->push_back(r); } -void DwarfRangeListHandler::SetBaseAddress(uint64_t base_address) { - base_address_ = base_address; -} - void DwarfRangeListHandler::Finish() { std::sort(ranges_->begin(), ranges_->end(), [](const Module::Range& a, const Module::Range& b) { diff --git a/src/common/dwarf_range_list_handler.h b/src/common/dwarf_range_list_handler.h index 2adb2f9a..5db5d1e9 100644 --- a/src/common/dwarf_range_list_handler.h +++ b/src/common/dwarf_range_list_handler.h @@ -51,25 +51,18 @@ namespace google_breakpad { class DwarfRangeListHandler: public dwarf2reader::RangeListHandler { public: - DwarfRangeListHandler(uint64_t base_address, vector* ranges) - : base_address_(base_address), ranges_(ranges) { } + DwarfRangeListHandler(vector* ranges) + : ranges_(ranges) { } ~DwarfRangeListHandler() { } // Add a range to the list void AddRange(uint64_t begin, uint64_t end); - // Record the new base address and use it for the following entries - void SetBaseAddress(uint64_t base_address); - // Sort the ranges so that they are in ascending order of starting address void Finish(); private: - // The current PC to add to every entry, this can be overridden by a special - // list entry - uint64_t base_address_; - // The list of ranges to be populated vector* ranges_; }; diff --git a/src/common/linux/dump_symbols.cc b/src/common/linux/dump_symbols.cc index 8ecf0bc4..0514f5a2 100644 --- a/src/common/linux/dump_symbols.cc +++ b/src/common/linux/dump_symbols.cc @@ -232,22 +232,20 @@ bool LoadStabs(const typename ElfClass::Ehdr* elf_header, // owned by a function) with the results. class DumperRangesHandler : public DwarfCUToModule::RangesHandler { public: - DumperRangesHandler(const uint8_t* buffer, uint64_t size, - dwarf2reader::ByteReader* reader) - : buffer_(buffer), size_(size), reader_(reader) { } + DumperRangesHandler(dwarf2reader::ByteReader* reader) : + reader_(reader) { } - bool ReadRanges(uint64_t offset, Module::Address base_address, - vector* ranges) { - DwarfRangeListHandler handler(base_address, ranges); - dwarf2reader::RangeListReader rangelist_reader(buffer_, size_, reader_, - &handler); - - return rangelist_reader.ReadRangeList(offset); + bool ReadRanges( + enum dwarf2reader::DwarfForm form, uint64_t data, + dwarf2reader::RangeListReader::CURangesInfo* cu_info, + vector* ranges) { + DwarfRangeListHandler handler(ranges); + dwarf2reader::RangeListReader range_list_reader(reader_, cu_info, + &handler); + return range_list_reader.ReadRanges(form, data); } private: - const uint8_t* buffer_; - uint64_t size_; dwarf2reader::ByteReader* reader_; }; @@ -313,17 +311,8 @@ bool LoadDwarf(const string& dwarf_filename, file_context.AddSectionToSectionMap(name, contents, section->sh_size); } - // Optional .debug_ranges reader - scoped_ptr ranges_handler; - dwarf2reader::SectionMap::const_iterator ranges_entry = - file_context.section_map().find(".debug_ranges"); - if (ranges_entry != file_context.section_map().end()) { - const std::pair& ranges_section = - ranges_entry->second; - ranges_handler.reset( - new DumperRangesHandler(ranges_section.first, ranges_section.second, - &byte_reader)); - } + // .debug_ranges and .debug_rnglists reader + DumperRangesHandler ranges_handler(&byte_reader); // Parse all the compilation units in the .debug_info section. DumperLineToModule line_to_module(&byte_reader); @@ -341,7 +330,7 @@ bool LoadDwarf(const string& dwarf_filename, // data that was found. DwarfCUToModule::WarningReporter reporter(dwarf_filename, offset); DwarfCUToModule root_handler(&file_context, &line_to_module, - ranges_handler.get(), &reporter); + &ranges_handler, &reporter); // Make a Dwarf2Handler that drives the DIEHandler. dwarf2reader::DIEDispatcher die_dispatcher(&root_handler); // Make a DWARF parser for the compilation unit at OFFSET. diff --git a/src/common/mac/dump_syms.cc b/src/common/mac/dump_syms.cc index 24bcd653..672323c8 100644 --- a/src/common/mac/dump_syms.cc +++ b/src/common/mac/dump_syms.cc @@ -311,22 +311,20 @@ string DumpSymbols::Identifier() { class DumpSymbols::DumperRangesHandler: public DwarfCUToModule::RangesHandler { public: - DumperRangesHandler(const uint8_t* buffer, uint64_t size, - dwarf2reader::ByteReader* reader) - : buffer_(buffer), size_(size), reader_(reader) { } + DumperRangesHandler(dwarf2reader::ByteReader* reader) : + reader_(reader) { } - bool ReadRanges(uint64_t offset, Module::Address base_address, - vector* ranges) { - DwarfRangeListHandler handler(base_address, ranges); - dwarf2reader::RangeListReader rangelist_reader(buffer_, size_, reader_, - &handler); - - return rangelist_reader.ReadRangeList(offset); + bool ReadRanges( + enum dwarf2reader::DwarfForm form, uint64_t data, + dwarf2reader::RangeListReader::CURangesInfo* cu_info, + vector* ranges) { + DwarfRangeListHandler handler(ranges); + dwarf2reader::RangeListReader range_list_reader(reader_, cu_info, + &handler); + return range_list_reader.ReadRanges(form, data); } private: - const uint8_t* buffer_; - uint64_t size_; dwarf2reader::ByteReader* reader_; }; @@ -456,17 +454,8 @@ void DumpSymbols::ReadDwarf(google_breakpad::Module* module, // Build a line-to-module loader for the root handler to use. DumperLineToModule line_to_module(&byte_reader); - // Optional .debug_ranges reader - scoped_ptr ranges_handler; - dwarf2reader::SectionMap::const_iterator ranges_entry = - file_context.section_map().find("__debug_ranges"); - if (ranges_entry != file_context.section_map().end()) { - const std::pair& ranges_section = - ranges_entry->second; - ranges_handler.reset( - new DumperRangesHandler(ranges_section.first, ranges_section.second, - &byte_reader)); - } + // .debug_ranges and .debug_rngslists reader + DumperRangesHandler ranges_handler(&byte_reader); // Walk the __debug_info section, one compilation unit at a time. uint64_t debug_info_length = debug_info_section.second; @@ -476,7 +465,7 @@ void DumpSymbols::ReadDwarf(google_breakpad::Module* module, DwarfCUToModule::WarningReporter reporter(selected_object_name_, offset); DwarfCUToModule root_handler(&file_context, &line_to_module, - ranges_handler.get(), &reporter); + &ranges_handler, &reporter); // Make a Dwarf2Handler that drives our DIEHandler. dwarf2reader::DIEDispatcher die_dispatcher(&root_handler); // Make a DWARF parser for the compilation unit at OFFSET.