From 02fe1eef8e4753cfa686db52fc375e17f5d23c84 Mon Sep 17 00:00:00 2001 From: Zequan Wu Date: Thu, 25 May 2023 15:40:44 -0400 Subject: [PATCH] Fix reading DW_AT_ranges in split dwarf. Bug: b/280290608, chromium:1448979 Change-Id: I3f9e4c3d62b4c858238ccbbda0366926c306e27f Reviewed-on: https://chromium-review.googlesource.com/c/breakpad/breakpad/+/4568824 Reviewed-by: Joshua Peraza --- src/common/dwarf/dwarf2enums.h | 6 +++--- src/common/dwarf/dwarf2reader.cc | 29 ++++++++++++++++++++--------- src/common/dwarf/dwarf2reader.h | 28 +++++++++++----------------- src/common/dwarf_cu_to_module.cc | 26 ++++++++++++++++++-------- src/common/dwarf_cu_to_module.h | 4 +++- src/common/linux/dump_symbols.cc | 23 ++++++++++++++++------- src/common/mac/dump_syms.cc | 22 +++++++++++++++------- 7 files changed, 86 insertions(+), 52 deletions(-) diff --git a/src/common/dwarf/dwarf2enums.h b/src/common/dwarf/dwarf2enums.h index 777d9bfc..f1c995f9 100644 --- a/src/common/dwarf/dwarf2enums.h +++ b/src/common/dwarf/dwarf2enums.h @@ -580,10 +580,10 @@ enum DwarfSectionId { DW_SECT_TYPES = 2, DW_SECT_ABBREV = 3, DW_SECT_LINE = 4, - DW_SECT_LOC = 5, + DW_SECT_LOCLISTS = 5, DW_SECT_STR_OFFSETS = 6, - DW_SECT_MACINFO = 7, - DW_SECT_MACRO = 8 + DW_SECT_MACRO = 7, + DW_SECT_RNGLISTS = 8 }; // Source languages. These are values for DW_AT_language. diff --git a/src/common/dwarf/dwarf2reader.cc b/src/common/dwarf/dwarf2reader.cc index 4f63b979..0a1ff060 100644 --- a/src/common/dwarf/dwarf2reader.cc +++ b/src/common/dwarf/dwarf2reader.cc @@ -80,7 +80,7 @@ CompilationUnit::CompilationUnit(const string& path, str_offsets_buffer_(NULL), str_offsets_buffer_length_(0), addr_buffer_(NULL), addr_buffer_length_(0), is_split_dwarf_(false), is_type_unit_(false), dwo_id_(0), dwo_name_(), - skeleton_dwo_id_(0), ranges_base_(0), addr_base_(0), + skeleton_dwo_id_(0), addr_base_(0), str_offsets_base_(0), have_checked_for_dwp_(false), should_process_split_dwarf_(false) {} @@ -91,16 +91,11 @@ CompilationUnit::CompilationUnit(const string& path, // the executable file, and call it as if we were still // processing the original compilation unit. -void CompilationUnit::SetSplitDwarf(const uint8_t* addr_buffer, - uint64_t addr_buffer_length, +void CompilationUnit::SetSplitDwarf( uint64_t addr_base, - uint64_t ranges_base, uint64_t dwo_id) { is_split_dwarf_ = true; - addr_buffer_ = addr_buffer; - addr_buffer_length_ = addr_buffer_length; addr_base_ = addr_base; - ranges_base_ = ranges_base; skeleton_dwo_id_ = dwo_id; } @@ -889,7 +884,9 @@ const uint8_t* CompilationUnit::ProcessDIE(uint64_t dieoffset, // DW_AT_str_offsets_base or DW_AT_addr_base. If it does, that attribute must // be found and processed before trying to process the other attributes; // otherwise the string or address values will all come out incorrect. - if (abbrev.tag == DW_TAG_compile_unit && header_.version == 5) { + if ((abbrev.tag == DW_TAG_compile_unit || + abbrev.tag == DW_TAG_skeleton_unit) && + header_.version == 5) { uint64_t dieoffset_copy = dieoffset; const uint8_t* start_copy = start; for (AttributeList::const_iterator i = abbrev.attributes.begin(); @@ -1016,7 +1013,8 @@ bool CompilationUnit::ProcessSplitDwarf(std::string& split_file, string debug_suffix(".debug"); dwp_path = path_; size_t found = path_.rfind(debug_suffix); - if (found + debug_suffix.length() == path_.length()) + if (found != string::npos && + found + debug_suffix.length() == path_.length()) dwp_path = dwp_path.replace(found, debug_suffix.length(), dwp_suffix); } if (stat(dwp_path.c_str(), &statbuf) == 0) { @@ -1133,6 +1131,8 @@ void DwpReader::Initialize() { info_data_ = elf_reader_->GetSectionByName(".debug_info.dwo", &info_size_); str_offsets_data_ = elf_reader_->GetSectionByName(".debug_str_offsets.dwo", &str_offsets_size_); + rnglist_data_ = + elf_reader_->GetSectionByName(".debug_rnglists.dwo", &rnglist_size_); if (size_table_ >= cu_index_ + cu_index_size_) { version_ = 0; } @@ -1245,6 +1245,12 @@ void DwpReader::ReadDebugSectionsForCU(uint64_t dwo_id, ".debug_str_offsets", std::make_pair(reinterpret_cast (str_offsets_data_) + offset, size))); + } else if (section_id == DW_SECT_RNGLISTS) { + sections->insert(std::make_pair( + ".debug_rnglists", + std::make_pair( + reinterpret_cast(rnglist_data_) + offset, + size))); } } sections->insert(std::make_pair( @@ -1830,6 +1836,11 @@ bool RangeListReader::ReadRanges(enum DwarfForm form, uint64_t data) { return ReadDebugRngList(data); } } else if (form == DW_FORM_rnglistx) { + if (cu_info_->ranges_base_ == 0) { + // In split dwarf, there's no DW_AT_rnglists_base attribute, range_base + // will just be the first byte after the header. + cu_info_->ranges_base_ = reader_->OffsetSize() == 4? 12: 20; + } offset_array_ = cu_info_->ranges_base_; uint64_t index_offset = reader_->OffsetSize() * data; uint64_t range_list_offset = diff --git a/src/common/dwarf/dwarf2reader.h b/src/common/dwarf/dwarf2reader.h index cd676dea..a5c5febf 100644 --- a/src/common/dwarf/dwarf2reader.h +++ b/src/common/dwarf/dwarf2reader.h @@ -469,8 +469,7 @@ class CompilationUnit { // compilation unit. We also inherit the Dwarf2Handler from // the executable file, and call it as if we were still // processing the original compilation unit. - void SetSplitDwarf(const uint8_t* addr_buffer, uint64_t addr_buffer_length, - uint64_t addr_base, uint64_t ranges_base, uint64_t dwo_id); + void SetSplitDwarf(uint64_t addr_base, uint64_t dwo_id); // Begin reading a Dwarf2 compilation unit, and calling the // callbacks in the Dwarf2Handler @@ -493,7 +492,7 @@ class CompilationUnit { uint64_t GetAddrBase() { return addr_base_; } - uint64_t GetRangeBase() { return ranges_base_; } + uint64_t GetLowPC() { return low_pc_; } uint64_t GetDWOID() { return dwo_id_; } @@ -583,14 +582,8 @@ class CompilationUnit { else if (attr == DW_AT_str_offsets_base) { str_offsets_base_ = data; } - 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, - // that base will apply to DW_AT_ranges attributes in the - // skeleton CU as well as in the .dwo/.dwp files. - else if (attr == DW_AT_ranges && is_split_dwarf_) { - data += ranges_base_; + else if (attr == DW_AT_low_pc) { + low_pc_ = data; } handler_->ProcessAttributeUnsigned(offset, attr, form, data); } @@ -745,10 +738,6 @@ class CompilationUnit { // from the skeleton CU. uint64_t skeleton_dwo_id_; - // 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. uint64_t addr_base_; @@ -762,9 +751,12 @@ class CompilationUnit { std::unique_ptr split_elf_reader_; // DWP reader. - std::unique_ptr dwp_reader_; + std::unique_ptr dwp_reader_; - bool should_process_split_dwarf_; + bool should_process_split_dwarf_; + + // The value of the DW_AT_low_pc attribute, if any. + uint64_t low_pc_; }; // A Reader for a .dwp file. Supports the fetching of DWARF debug @@ -851,6 +843,8 @@ class DwpReader { size_t info_size_; const char* str_offsets_data_; size_t str_offsets_size_; + const char* rnglist_data_; + size_t rnglist_size_; }; // This class is a reader for DWARF's Call Frame Information. CFI diff --git a/src/common/dwarf_cu_to_module.cc b/src/common/dwarf_cu_to_module.cc index 2a23ca7c..a6cfb49c 100644 --- a/src/common/dwarf_cu_to_module.cc +++ b/src/common/dwarf_cu_to_module.cc @@ -169,19 +169,23 @@ bool DwarfCUToModule::FileContext::IsUnhandledInterCUReference( // parsing. This is for data shared across the CU's entire DIE tree, // and parameters from the code invoking the CU parser. struct DwarfCUToModule::CUContext { - CUContext(FileContext* file_context_arg, WarningReporter* reporter_arg, - RangesHandler* ranges_handler_arg) + CUContext(FileContext* file_context_arg, + WarningReporter* reporter_arg, + RangesHandler* ranges_handler_arg, + uint64_t low_pc, + uint64_t addr_base) : version(0), file_context(file_context_arg), reporter(reporter_arg), ranges_handler(ranges_handler_arg), language(Language::CPlusPlus), - low_pc(0), + low_pc(low_pc), high_pc(0), ranges_form(DW_FORM_sec_offset), ranges_data(0), ranges_base(0), - str_offsets_base(0) { } + addr_base(addr_base), + str_offsets_base(0) {} ~CUContext() { for (vector::iterator it = functions.begin(); @@ -854,7 +858,7 @@ void DwarfCUToModule::FuncHandler::Finish() { iter->second->name = name_; } - if (!ranges_data_) { + if (low_pc_ && high_pc_) { // Make high_pc_ an address, if it isn't already. if (high_pc_form_ != DW_FORM_addr && high_pc_form_ != DW_FORM_GNU_addr_index && @@ -868,7 +872,7 @@ void DwarfCUToModule::FuncHandler::Finish() { Module::Range range(low_pc_, high_pc_ - low_pc_); ranges.push_back(range); - } else { + } else if (ranges_data_) { RangesHandler* ranges_handler = cu_context_->ranges_handler; if (ranges_handler) { RangeListReader::CURangesInfo cu_info; @@ -1069,10 +1073,16 @@ DwarfCUToModule::DwarfCUToModule(FileContext* file_context, LineToModuleHandler* line_reader, RangesHandler* ranges_handler, WarningReporter* reporter, - bool handle_inline) + bool handle_inline, + uint64_t low_pc, + uint64_t addr_base) : RootDIEHandler(handle_inline), line_reader_(line_reader), - cu_context_(new CUContext(file_context, reporter, ranges_handler)), + cu_context_(new CUContext(file_context, + reporter, + ranges_handler, + low_pc, + addr_base)), child_context_(new DIEContext()), has_source_line_info_(false) {} diff --git a/src/common/dwarf_cu_to_module.h b/src/common/dwarf_cu_to_module.h index 5a800104..90ee2dec 100644 --- a/src/common/dwarf_cu_to_module.h +++ b/src/common/dwarf_cu_to_module.h @@ -264,7 +264,9 @@ class DwarfCUToModule: public RootDIEHandler { LineToModuleHandler* line_reader, RangesHandler* ranges_handler, WarningReporter* reporter, - bool handle_inline = false); + bool handle_inline = false, + uint64_t low_pc = 0, + uint64_t addr_base = 0); ~DwarfCUToModule(); void ProcessAttributeSigned(enum DwarfAttribute attr, diff --git a/src/common/linux/dump_symbols.cc b/src/common/linux/dump_symbols.cc index 0bfc74c3..2f468dfe 100644 --- a/src/common/linux/dump_symbols.cc +++ b/src/common/linux/dump_symbols.cc @@ -348,18 +348,27 @@ void StartProcessSplitDwarf(google_breakpad::CompilationUnit* reader, return; DwarfCUToModule::FileContext file_context(split_file, module, handle_inter_cu_refs); + for (auto section : split_sections) + file_context.AddSectionToSectionMap(section.first, section.second.first, + section.second.second); + // Because DWP/DWO file doesn't have .debug_addr/.debug_line, its debug info + // will refer to .debug_addr/.debug_line in the main binary. + if (file_context.section_map().find(".debug_addr") == + file_context.section_map().end()) + file_context.AddSectionToSectionMap(".debug_addr", reader->GetAddrBuffer(), + reader->GetAddrBufferLen()); + DumperRangesHandler ranges_handler(&split_byte_reader); DumperLineToModule line_to_module(&split_byte_reader); DwarfCUToModule::WarningReporter reporter(split_file, cu_offset); DwarfCUToModule root_handler(&file_context, &line_to_module, &ranges_handler, - &reporter, handle_inline); + &reporter, handle_inline, reader->GetLowPC(), + reader->GetAddrBase()); google_breakpad::DIEDispatcher die_dispatcher(&root_handler); - google_breakpad::CompilationUnit split_reader(split_file, split_sections, - cu_offset, &split_byte_reader, - &die_dispatcher); - split_reader.SetSplitDwarf(reader->GetAddrBuffer(), - reader->GetAddrBufferLen(), reader->GetAddrBase(), - reader->GetRangeBase(), reader->GetDWOID()); + google_breakpad::CompilationUnit split_reader( + split_file, file_context.section_map(), cu_offset, &split_byte_reader, + &die_dispatcher); + split_reader.SetSplitDwarf(reader->GetAddrBase(), reader->GetDWOID()); split_reader.Start(); // Normally, it won't happen unless we have transitive reference. if (split_reader.ShouldProcessSplitDwarf()) { diff --git a/src/common/mac/dump_syms.cc b/src/common/mac/dump_syms.cc index d7c3e695..2f48cb4f 100644 --- a/src/common/mac/dump_syms.cc +++ b/src/common/mac/dump_syms.cc @@ -439,18 +439,26 @@ void DumpSymbols::StartProcessSplitDwarf( return; DwarfCUToModule::FileContext file_context(split_file, module, handle_inter_cu_refs); + for (auto section : split_sections) + file_context.AddSectionToSectionMap(section.first, section.second.first, + section.second.second); + // If DWP/DWO file doesn't have .debug_addr, its debug info will refer to + // .debug_addr in the main binary. + if (file_context.section_map().find(".debug_addr") == + file_context.section_map().end()) + file_context.AddSectionToSectionMap(".debug_addr", reader->GetAddrBuffer(), + reader->GetAddrBufferLen()); DumperRangesHandler ranges_handler(&split_byte_reader); DumperLineToModule line_to_module(&split_byte_reader); DwarfCUToModule::WarningReporter reporter(split_file, cu_offset); DwarfCUToModule root_handler(&file_context, &line_to_module, &ranges_handler, - &reporter, handle_inline); + &reporter, handle_inline, reader->GetLowPC(), + reader->GetAddrBase()); google_breakpad::DIEDispatcher die_dispatcher(&root_handler); - google_breakpad::CompilationUnit split_reader(split_file, split_sections, - cu_offset, &split_byte_reader, - &die_dispatcher); - split_reader.SetSplitDwarf(reader->GetAddrBuffer(), - reader->GetAddrBufferLen(), reader->GetAddrBase(), - reader->GetRangeBase(), reader->GetDWOID()); + google_breakpad::CompilationUnit split_reader( + split_file, file_context.section_map(), cu_offset, &split_byte_reader, + &die_dispatcher); + split_reader.SetSplitDwarf(reader->GetAddrBase(), reader->GetDWOID()); split_reader.Start(); // Normally, it won't happen unless we have transitive reference. if (split_reader.ShouldProcessSplitDwarf()) {