Index: include/lldb/Utility/ConstString.h =================================================================== --- include/lldb/Utility/ConstString.h +++ include/lldb/Utility/ConstString.h @@ -458,6 +458,18 @@ //------------------------------------------------------------------ static size_t StaticMemorySize(); + class Hasher { + public: + size_t operator()(const ConstString &key) const { + // https://stackoverflow.com/questions/7666509/hash-function-for-string + // C++17: std::string_view + size_t hash = 5381; + for (const char *p = key.m_string; *p; ++p) + hash = hash * 33 + static_cast(*p); + return hash; + } + }; + protected: //------------------------------------------------------------------ // Member variables Index: include/lldb/Utility/FileSpec.h =================================================================== --- include/lldb/Utility/FileSpec.h +++ include/lldb/Utility/FileSpec.h @@ -563,6 +563,14 @@ llvm::sys::fs::file_type file_type, const FileSpec &spec)> DirectoryCallback; + class Hasher { + public: + size_t operator()(const FileSpec &key) const { + return (ConstString::Hasher()(key.m_directory) << 16) + ^ ConstString::Hasher()(key.m_filename) ^ key.m_is_resolved; + } + }; + protected: //------------------------------------------------------------------ // Convenience method for setting the file without changing the style. Index: source/Plugins/SymbolFile/DWARF/DWARFDataExtractor.h =================================================================== --- source/Plugins/SymbolFile/DWARF/DWARFDataExtractor.h +++ source/Plugins/SymbolFile/DWARF/DWARFDataExtractor.h @@ -32,6 +32,28 @@ size_t GetDWARFSizeOfOffset() const { return m_is_dwarf64 ? 8 : 4; } bool IsDWARF64() const { return m_is_dwarf64; } + //------------------------------------------------------------------ + /// Slide the data in the buffer so that access to the data will + /// start at offset \a offset. + /// + /// This is currently used to provide access to the .debug_types + /// section and pretend it starts at at offset of the size of the + /// .debug_info section. This allows a minimally invasive change to + /// add support for .debug_types by allowing all DIEs to have unique + /// offsets and thus allowing no code to change in the DWARF parser. + /// Modifying the offsets in the .debug_types doesn't affect + /// anything because since all info in the .debug_types is type unit + /// relative and no types within a type unit can refer to any DIEs + /// outside of the type unit without using DW_AT_signature. It also + /// sets us up to move to DWARF5 where there is no .debug_types + /// section as compile units and type units are in the .debug_info. + //------------------------------------------------------------------ + void OffsetData(lldb::offset_t offset) + { + if (GetByteSize()) + m_start -= offset; + } + protected: mutable bool m_is_dwarf64; }; Index: source/Plugins/SymbolFile/DWARF/DWARFDebugAranges.h =================================================================== --- source/Plugins/SymbolFile/DWARF/DWARFDebugAranges.h +++ source/Plugins/SymbolFile/DWARF/DWARFDebugAranges.h @@ -30,7 +30,8 @@ void Clear() { m_aranges.Clear(); } - bool Extract(const lldb_private::DWARFDataExtractor &debug_aranges_data); + bool Extract(const lldb_private::DWARFDataExtractor &debug_aranges_data, + dw_offset_t dwz_offset); bool Generate(SymbolFileDWARF *dwarf2Data); Index: source/Plugins/SymbolFile/DWARF/DWARFDebugAranges.cpp =================================================================== --- source/Plugins/SymbolFile/DWARF/DWARFDebugAranges.cpp +++ source/Plugins/SymbolFile/DWARF/DWARFDebugAranges.cpp @@ -48,7 +48,8 @@ //---------------------------------------------------------------------- // Extract //---------------------------------------------------------------------- -bool DWARFDebugAranges::Extract(const DWARFDataExtractor &debug_aranges_data) { +bool DWARFDebugAranges::Extract( + const DWARFDataExtractor &debug_aranges_data, dw_offset_t dwz_offset) { if (debug_aranges_data.ValidOffset(0)) { lldb::offset_t offset = 0; @@ -57,7 +58,8 @@ while (set.Extract(debug_aranges_data, &offset)) { const uint32_t num_descriptors = set.NumDescriptors(); if (num_descriptors > 0) { - const dw_offset_t cu_offset = set.GetCompileUnitDIEOffset(); + const dw_offset_t cu_offset = set.GetCompileUnitDIEOffset() + + dwz_offset; for (uint32_t i = 0; i < num_descriptors; ++i) { const DWARFDebugArangeSet::Descriptor &descriptor = Index: source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.h =================================================================== --- source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.h +++ source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.h @@ -40,6 +40,8 @@ DWARFUnit *GetCompileUnitAtIndex(uint32_t idx); DWARFUnit *GetCompileUnit(dw_offset_t cu_offset, uint32_t *idx_ptr = NULL); DWARFUnit *GetCompileUnitContainingDIEOffset(dw_offset_t die_offset); + // Used only by DWARFFormValue::Reference(). + DWARFUnit *GetCompileUnitContainingDIEOffsetInFile(dw_offset_t die_offset); DWARFUnit *GetCompileUnit(const DIERef &die_ref); DWARFDIE GetDIEForDIEOffset(dw_offset_t die_offset); DWARFDIE GetDIE(const DIERef &die_ref); @@ -59,6 +61,9 @@ typedef std::vector CompileUnitColl; + DWARFDebugInfo *DWZRedirect(dw_offset_t offset); + DWARFUnit *GetCompileUnitInFile(dw_offset_t cu_offset, uint32_t *idx_ptr); + //---------------------------------------------------------------------- // Member variables //---------------------------------------------------------------------- Index: source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.cpp =================================================================== --- source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.cpp +++ source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.cpp @@ -12,8 +12,10 @@ #include #include +#include "lldb/Core/Module.h" #include "lldb/Host/PosixApi.h" #include "lldb/Symbol/ObjectFile.h" +#include "lldb/Utility/LLDBAssert.h" #include "lldb/Utility/RegularExpression.h" #include "lldb/Utility/Stream.h" #include "lldb/Core/Module.h" @@ -57,7 +59,16 @@ "DWARFDebugInfo::GetCompileUnitAranges() for \"%s\" from " ".debug_aranges", m_dwarf2Data->GetObjectFile()->GetFileSpec().GetPath().c_str()); - m_cu_aranges_ap->Extract(debug_aranges_data); + dw_offset_t dwz_offset = 0; + lldbassert(!m_dwarf2Data->GetIsDWZ()); + SymbolFileDWARF *dwz_symbol_file = m_dwarf2Data->GetDWZSymbolFile(); + if (dwz_symbol_file) { + const DWARFDataExtractor &dwz_debug_info_data = + dwz_symbol_file->get_debug_info_data(); + auto dwz_debug_info_size = dwz_debug_info_data.GetByteSize(); + dwz_offset += dwz_debug_info_size; + } + m_cu_aranges_ap->Extract(debug_aranges_data, dwz_offset); } // Make a list of all CUs represented by the arange data in the file. @@ -100,6 +111,14 @@ if (m_compile_units.empty()) { if (m_dwarf2Data != NULL) { lldb::offset_t offset = 0; + // All CUs in a base symbol file are shifted after its DWZ file (if exists). + SymbolFileDWARF *dwz_symbol_file = m_dwarf2Data->GetDWZSymbolFile(); + if (dwz_symbol_file) { + const DWARFDataExtractor &dwz_debug_info_data = + dwz_symbol_file->get_debug_info_data(); + offset += dwz_debug_info_data.GetByteSize(); + } + DWARFUnitSP cu_sp; const auto &debug_info_data = m_dwarf2Data->get_debug_info_data(); while ((cu_sp = DWARFCompileUnit::Extract(m_dwarf2Data, debug_info_data, @@ -142,8 +161,33 @@ return offset < cu_sp->GetOffset(); } +DWARFDebugInfo *DWARFDebugInfo::DWZRedirect(dw_offset_t offset) { + if (m_dwarf2Data->GetIsDWZ()) { + m_dwarf2Data->GetObjectFile()->GetModule()->ReportError( + "Internal LLDB error - unable to resolve offset 0x%8.8" PRIx32 + " as DWZ common file has been passed, change the callers to use:" + " DWARFDIE::GetDWARF() -> DWARFDIE::GetMainDWARF()", + offset); + return this; + } + SymbolFileDWARF *dwz_symbol_file = m_dwarf2Data->GetDWZSymbolFile(); + if (!dwz_symbol_file) + return this; + const DWARFDataExtractor &dwz_debug_info_data = + dwz_symbol_file->get_debug_info_data(); + auto dwz_debug_info_size = dwz_debug_info_data.GetByteSize(); + if (offset < dwz_debug_info_size) + return dwz_symbol_file->DebugInfo(); + return this; +} + DWARFUnit *DWARFDebugInfo::GetCompileUnit(dw_offset_t cu_offset, - uint32_t *idx_ptr) { + uint32_t *idx_ptr) { + return DWZRedirect(cu_offset)->GetCompileUnitInFile(cu_offset, idx_ptr); +} + +DWARFUnit *DWARFDebugInfo::GetCompileUnitInFile(dw_offset_t cu_offset, + uint32_t *idx_ptr) { DWARFUnitSP cu_sp; uint32_t cu_idx = DW_INVALID_INDEX; if (cu_offset != DW_INVALID_OFFSET) { @@ -187,6 +231,13 @@ DWARFUnit * DWARFDebugInfo::GetCompileUnitContainingDIEOffset(dw_offset_t die_offset) { + return DWZRedirect(die_offset)->GetCompileUnitContainingDIEOffsetInFile( + die_offset); +} + +DWARFUnit * +DWARFDebugInfo::GetCompileUnitContainingDIEOffsetInFile( + dw_offset_t die_offset) { ParseCompileUnitHeadersIfNeeded(); DWARFUnitSP cu_sp; Index: source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp =================================================================== --- source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp +++ source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp @@ -112,6 +112,9 @@ else form_size = cu->IsDWARF64() ? 8 : 4; break; + case DW_FORM_GNU_ref_alt: + form_size = cu->IsDWARF64() ? 8 : 4; + break; // 0 sized form case DW_FORM_flag_present: @@ -159,6 +162,7 @@ break; case DW_FORM_strp: + case DW_FORM_GNU_strp_alt: case DW_FORM_sec_offset: if (cu->IsDWARF64()) debug_info_data.GetU64(&offset); @@ -276,6 +280,9 @@ else form_size = cu->IsDWARF64() ? 8 : 4; break; + case DW_FORM_GNU_ref_alt: + form_size = cu->IsDWARF64() ? 8 : 4; + break; // 0 sized form case DW_FORM_flag_present: @@ -323,6 +330,7 @@ break; case DW_FORM_strp: + case DW_FORM_GNU_strp_alt: case DW_FORM_sec_offset: if (cu->IsDWARF64()) debug_info_data.GetU64(&offset); Index: source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp =================================================================== --- source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp +++ source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp @@ -10,10 +10,14 @@ #include #include "lldb/Core/dwarf.h" +#include "lldb/Utility/LLDBAssert.h" #include "lldb/Utility/Stream.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Core/Module.h" #include "DWARFUnit.h" #include "DWARFFormValue.h" +#include "DWARFDebugInfo.h" class DWARFUnit; @@ -218,6 +222,7 @@ m_value.value.sval = data.GetSLEB128(offset_ptr); break; case DW_FORM_strp: + case DW_FORM_GNU_strp_alt: assert(m_cu); m_value.value.uval = data.GetMaxU64(offset_ptr, DWARFUnit::IsDWARF64(m_cu) ? 8 : 4); @@ -250,6 +255,11 @@ case DW_FORM_ref_udata: m_value.value.uval = data.GetULEB128(offset_ptr); break; + case DW_FORM_GNU_ref_alt: + assert(m_cu); + ref_addr_size = m_cu->IsDWARF64() ? 8 : 4; + m_value.value.uval = data.GetMaxU64(offset_ptr, ref_addr_size); + break; case DW_FORM_indirect: m_form = data.GetULEB128(offset_ptr); indirect = true; @@ -344,6 +354,13 @@ *offset_ptr += ref_addr_size; return true; + case DW_FORM_GNU_ref_alt: + assert(cu); // CU must be valid for DW_FORM_GNU_ref_alt objects or we will get + // this wrong + ref_addr_size = cu->IsDWARF64() ? 8 : 4; + *offset_ptr += ref_addr_size; + return true; + // 0 bytes values (implied from DW_FORM) case DW_FORM_flag_present: return true; @@ -364,6 +381,7 @@ // 32 bit for DWARF 32, 64 for DWARF 64 case DW_FORM_sec_offset: case DW_FORM_strp: + case DW_FORM_GNU_strp_alt: assert(cu); *offset_ptr += (cu->IsDWARF64() ? 8 : 4); return true; @@ -505,6 +523,13 @@ case DW_FORM_ref_udata: cu_relative_offset = true; break; + case DW_FORM_GNU_ref_alt: { + assert(m_cu); // CU must be valid for DW_FORM_GNU_ref_alt objects or we will + // get this wrong + s.Address(uvalue, 4 * 2); // 4 for DWARF32, 8 for DWARF64, but we don't + // support DWARF64 yet + break; + } // All DW_FORM_indirect attributes should be resolved prior to calling this // function @@ -545,6 +570,16 @@ symbol_file->get_debug_str_offsets_data().GetMaxU64(&offset, index_size); return symbol_file->get_debug_str_data().PeekCStr(str_offset); + } else if (m_form == DW_FORM_GNU_strp_alt) { + SymbolFileDWARF *dwz_symbol_file = symbol_file->GetDWZSymbolFile(); + if (!dwz_symbol_file) { + symbol_file->GetObjectFile()->GetModule()->ReportError( + "String reference to DWZ offset 0x%8.8" PRIx64 + " is in a file without associated DWZ common file", + m_value.value.uval); + return nullptr; + } + return dwz_symbol_file->get_debug_str_data().PeekCStr(m_value.value.uval); } return nullptr; } @@ -569,21 +604,74 @@ uint64_t DWARFFormValue::Reference() const { uint64_t die_offset = m_value.value.uval; + assert(m_cu); + SymbolFileDWARF *cu_symbol_file = m_cu->GetSymbolFileDWARF(); + bool is_dwz = cu_symbol_file->GetIsDWZ(); switch (m_form) { case DW_FORM_ref1: case DW_FORM_ref2: case DW_FORM_ref4: case DW_FORM_ref8: case DW_FORM_ref_udata: - assert(m_cu); // CU must be valid for DW_FORM_ref forms that are compile - // unit relative or we will get this wrong die_offset += m_cu->GetOffset(); + // The offset adjustment is already appropriate inside this CU. + return die_offset; + + case DW_FORM_GNU_ref_alt: + if (is_dwz) + cu_symbol_file->GetObjectFile()->GetModule()->ReportError( + "DW_FORM_GNU_ref_alt to DWZ offset 0x%8.8" PRIx64 + " is in a DWZ common file itself", + die_offset); + else if (!cu_symbol_file->GetDWZSymbolFile()) { + cu_symbol_file->GetObjectFile()->GetModule()->ReportError( + "DW_FORM_GNU_ref_alt to DWZ offset 0x%8.8" PRIx64 + " has no DWZ common file", + die_offset); + return DW_INVALID_OFFSET; + } + is_dwz = true; break; + case DW_FORM_ref_addr: default: break; } + if (!is_dwz) { + lldbassert(!cu_symbol_file->GetIsDWZ()); + SymbolFileDWARF *dwz_symbol_file = cu_symbol_file->GetDWZSymbolFile(); + if (dwz_symbol_file) { + // All CUs in a base symbol file are shifted after its DWZ file. + const DWARFDataExtractor &dwz_debug_info_data = + dwz_symbol_file->get_debug_info_data(); + die_offset += dwz_debug_info_data.GetByteSize(); + } + } + + if (!m_cu->ContainsDIEOffset(die_offset)) { + SymbolFileDWARF *target_symbol_file = cu_symbol_file; + if (is_dwz && !target_symbol_file->GetIsDWZ()) { + target_symbol_file = cu_symbol_file->GetDWZSymbolFile(); + if (!target_symbol_file) { + cu_symbol_file->GetObjectFile()->GetModule()->ReportError( + "There is Reference to DWZ offset 0x%8.8" PRIx64 + " but the DWZ common file is missing", + die_offset); + target_symbol_file = cu_symbol_file; + } + } + lldbassert(is_dwz == target_symbol_file->GetIsDWZ()); + DWARFDebugInfo *target_debuginfo = target_symbol_file->DebugInfo(); + // FIXME: This is expensive for files containing no DWZ. + // But there is no way to easily find in advance if this file has been + // DWZ-processed (in the case of DWARF <=4). + DWARFUnit *target_cu = + target_debuginfo->GetCompileUnitContainingDIEOffsetInFile(die_offset); + if (target_cu) + target_cu->SetMainCU(const_cast(m_cu->GetMainCU())); + } + return die_offset; } @@ -707,6 +795,7 @@ case DW_FORM_ref4: case DW_FORM_ref8: case DW_FORM_ref_udata: { + case DW_FORM_GNU_ref_alt: uint64_t a = a_value.Reference(); uint64_t b = b_value.Reference(); if (a < b) @@ -755,6 +844,8 @@ case DW_FORM_ref_sig8: case DW_FORM_GNU_str_index: case DW_FORM_GNU_addr_index: + case DW_FORM_GNU_ref_alt: + case DW_FORM_GNU_strp_alt: return true; default: break; Index: source/Plugins/SymbolFile/DWARF/DWARFUnit.h =================================================================== --- source/Plugins/SymbolFile/DWARF/DWARFUnit.h +++ source/Plugins/SymbolFile/DWARF/DWARFUnit.h @@ -180,9 +180,10 @@ } // DW_TAG_compile_unit with DW_TAG_imported_unit for this DW_TAG_partial_unit. - DWARFUnit *GetMainCU() const { - return const_cast(this); - } + DWARFUnit *GetMainCU() { return m_main_cu; } + const DWARFUnit *GetMainCU() const { return m_main_cu; } + void SetMainCU(DWARFUnit *new_main_cu); + void DWZDeleteSymbolFile(SymbolFileDWARF *symbol_file); protected: DWARFUnit(SymbolFileDWARF *dwarf); @@ -223,6 +224,13 @@ // If this is a dwo compile unit this is the offset of the base compile unit // in the main object file dw_offset_t m_base_obj_offset = DW_INVALID_OFFSET; + // DW_TAG_compile_unit with DW_TAG_imported_unit for this DW_TAG_partial_unit. + std::atomic m_main_cu; + // It is used for restoring valid m_main_cu if its current SymbolFileDWARF + // gets removed. Then choose any DWARFUnit from any other file which also + // has DW_TAG_imported_unit for this DW_TAG_partial_unit. + std::unordered_map m_main_cu_map; + llvm::sys::RWMutex m_main_cu_map_mutex; // Offset of the initial length field. dw_offset_t m_offset; Index: source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp =================================================================== --- source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp +++ source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp @@ -32,7 +32,7 @@ extern int g_verbose; DWARFUnit::DWARFUnit(SymbolFileDWARF *dwarf) - : m_dwarf(dwarf), m_cancel_scopes(false) {} + : m_dwarf(dwarf), m_cancel_scopes(false), m_main_cu(this) {} DWARFUnit::~DWARFUnit() {} @@ -418,6 +418,10 @@ // First get the compile unit DIE only and check if it has a DW_AT_ranges const DWARFDebugInfoEntry *die = GetUnitDIEPtrOnly(); + // DWZ partial units never contain PC. + if (die && die->Tag() == DW_TAG_partial_unit) + return; + const dw_offset_t cu_offset = GetOffset(); if (die) { DWARFRangeList ranges; @@ -555,7 +559,11 @@ // Don't specify the compile unit offset as we don't know it because the // DIE belongs to // a different compile unit in the same symbol file. - return GetMainCU()->m_dwarf->DebugInfo()->GetDIEForDIEOffset(die_offset); + DWARFDIE die = + GetMainCU()->m_dwarf->DebugInfo()->GetDIEForDIEOffset(die_offset); + if (die) + die.GetCU()->SetMainCU(GetMainCU()); + return die; } } m_dwarf->GetObjectFile()->GetModule()->ReportError( @@ -726,6 +734,51 @@ dw_offset_t DWARFUnit::GetBaseObjOffset() const { return m_base_obj_offset; } +void DWARFUnit::SetMainCU(DWARFUnit *new_main_cu) { + if (new_main_cu == this) + return; + if (m_main_cu == new_main_cu) + return; + if (m_main_cu != this + && (*m_main_cu).GetSymbolFileDWARF() == new_main_cu->GetSymbolFileDWARF()) + return; + const DWARFDebugInfoEntry *die = GetUnitDIEPtrOnly(); + if (!die || die->Tag() != DW_TAG_partial_unit) + return; + bool found; + { + llvm::sys::ScopedReader lock(m_main_cu_map_mutex); + found = m_main_cu_map.find(new_main_cu->GetSymbolFileDWARF()) + != m_main_cu_map.cend(); + } + if (found) + lldbassert(m_main_cu != this); + else { + llvm::sys::ScopedWriter lock(m_main_cu_map_mutex); + DWARFUnit *&ref = + m_main_cu_map[new_main_cu->GetSymbolFileDWARF()]; + if (ref) + lldbassert(m_main_cu != this); + else { + ref = new_main_cu; + if (m_main_cu == this) + m_main_cu = new_main_cu; + } + } +} + +void DWARFUnit::DWZDeleteSymbolFile(SymbolFileDWARF *symbol_file) { + llvm::sys::ScopedWriter lock(m_main_cu_map_mutex); + if (!m_main_cu_map.erase(symbol_file)) + return; + if ((*m_main_cu).GetSymbolFileDWARF() != symbol_file) + return; + if (!m_main_cu_map.empty()) + m_main_cu = m_main_cu_map.cbegin()->second; + else + m_main_cu = this; +} + const DWARFDebugAranges &DWARFUnit::GetFunctionAranges() { if (m_func_aranges_ap.get() == NULL) { m_func_aranges_ap.reset(new DWARFDebugAranges()); Index: source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.h =================================================================== --- source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.h +++ source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.h @@ -58,7 +58,7 @@ void Index(); void IndexUnit(DWARFUnit &unit, IndexSet &set); - static void + void IndexUnitImpl(DWARFUnit &unit, const lldb::LanguageType cu_language, const DWARFFormValue::FixedFormSizes &fixed_form_sizes, const dw_offset_t cu_offset, IndexSet &set); Index: source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp =================================================================== --- source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp +++ source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp @@ -94,6 +94,17 @@ } void ManualDWARFIndex::IndexUnit(DWARFUnit &unit, IndexSet &set) { + // Are we called for DW_TAG_compile_unit (contrary to DW_TAG_partial_unit)? + if (unit.GetMainCU() == &unit) { + // DWZ DW_TAG_partial_unit will get indexed by DW_AT_import + // from its DW_TAG_compile_unit (possibly transitively). + // GetUnitDIEPtrOnly() is too expensive. + if (unit.GetSymbolFileDWARF()->GetIsDWZ()) + return; + if (unit.GetUnitDIEOnly().Tag() == DW_TAG_partial_unit) + return; + } + Log *log = LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS); if (log) { @@ -137,6 +148,7 @@ case DW_TAG_union_type: case DW_TAG_unspecified_type: case DW_TAG_variable: + case DW_TAG_imported_unit: break; default: @@ -152,7 +164,7 @@ bool has_location_or_const_value = false; bool is_global_or_static_variable = false; - DWARFFormValue specification_die_form; + DWARFFormValue specification_die_form, import_die_form; const size_t num_attributes = die.GetAttributes(&unit, fixed_form_sizes, attributes); if (num_attributes > 0) { @@ -244,6 +256,11 @@ if (attributes.ExtractFormValueAtIndex(i, form_value)) specification_die_form = form_value; break; + + case DW_AT_import: + if (attributes.ExtractFormValueAtIndex(i, form_value)) + import_die_form = form_value; + break; } } } @@ -354,6 +371,48 @@ } break; + case DW_TAG_imported_unit: + if (import_die_form.IsValid()) { + // DWZ is using DW_TAG_imported_unit only at the top level of CUs. + // Therefore GetParent() of any top level DIE in partial unit is always + // DW_TAG_compile_unit (possibly after multiple import levels). + const DWARFDebugInfoEntry *parent_die = die.GetParent(); + if (!parent_die || (parent_die->Tag() != DW_TAG_compile_unit + && parent_die->Tag() != DW_TAG_partial_unit)) { + unit.GetSymbolFileDWARF()->GetObjectFile()->GetModule() + ->ReportError( + "CU 0x%8.8" PRIx32 " DIE 0x%8.8" PRIx32 + " DW_TAG_imported_unit does not have DW_TAG_compile_unit" + " as its parent", + unit.GetOffset(), die.GetOffset()); + break; + } + dw_offset_t import_offset = import_die_form.Reference(); + // main_unit may be different from our callers as GetMainCU() returns + // only the first unit which DW_TAG_imported this unit. + DWARFUnit &main_unit = *unit.GetMainCU(); + DWARFDebugInfo *main_debuginfo = + main_unit.GetSymbolFileDWARF()->DebugInfo(); + DWARFUnit *import_cu = + main_debuginfo->GetCompileUnitContainingDIEOffset(import_offset); + if (!import_cu) + break; + dw_offset_t import_cu_firstdie_offset = import_cu->GetFirstDIEOffset(); + if (import_offset != import_cu_firstdie_offset) { + unit.GetSymbolFileDWARF()->GetObjectFile()->GetModule() + ->ReportError( + "CU 0x%8.8" PRIx32 " DIE 0x%8.8" PRIx32 + " DW_TAG_imported_unit of DIE 0x%16.16" PRIx32 + " is not the target CU first DIE 0x%8.8" PRIx32, + unit.GetOffset(), die.GetOffset(), + import_offset, import_cu_firstdie_offset); + break; + } + import_cu->SetMainCU(&main_unit); + IndexUnit(*import_cu,set); + } + break; + default: continue; } Index: source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h =================================================================== --- source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h +++ source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h @@ -18,10 +18,12 @@ #include #include #include +#include // Other libraries and framework includes #include "llvm/ADT/DenseMap.h" #include "llvm/Support/Threading.h" +#include "llvm/Support/RWMutex.h" #include "lldb/Utility/Flags.h" @@ -318,6 +320,13 @@ void Dump(lldb_private::Stream &s) override; + SymbolFileDWARF *GetDWZSymbolFile() const { + if (!m_dwz_common_file) + return nullptr; + return m_dwz_common_file->SymbolFile(); + } + bool GetIsDWZ() const { return m_is_dwz; } + protected: typedef llvm::DenseMap DIEToTypePtr; @@ -452,6 +461,45 @@ SymbolFileDWARFDwp *GetDwpSymbolFile(); + void InitializeDWZ(); + + class DWZCommonFile { + public: + // C++14: Use heterogenous lookup. + DWZCommonFile(const lldb_private::FileSpec &filespec_ref); + DWZCommonFile(std::unique_ptr symbol_file, + lldb::ObjectFileSP obj_file, lldb::ModuleSP module); + SymbolFileDWARF *SymbolFile() const { return m_symbol_file.get(); } + + bool operator==(const DWZCommonFile &rhs) const { + return m_filespec_ref == rhs.m_filespec_ref; + } + bool operator!=(const DWZCommonFile &rhs) const { return !(*this == rhs); } + class Hasher { + public: + size_t operator()(const DWZCommonFile &key) const { + return lldb_private::FileSpec::Hasher()(key.m_filespec_ref); + } + }; + + size_t m_use_count = 0; + + private: + std::unique_ptr m_symbol_file; + lldb::ObjectFileSP m_obj_file; + lldb::ModuleSP m_module; + const lldb_private::FileSpec &m_filespec_ref; + + DISALLOW_COPY_AND_ASSIGN(DWZCommonFile); + }; + DWZCommonFile *m_dwz_common_file = nullptr; + void DWZCommonFileSet(DWZCommonFile *dwz_common_file); + void DWZCommonFileClear(); + static std::unordered_set + m_filespec_to_dwzcommonfile; + static llvm::sys::RWMutex m_filespec_to_dwzcommonfile_mutex; + bool m_is_dwz = false; + lldb::ModuleWP m_debug_map_module_wp; SymbolFileDWARFDebugMap *m_debug_map_symfile; Index: source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp =================================================================== --- source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp +++ source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp @@ -419,7 +419,9 @@ m_supports_DW_AT_APPLE_objc_complete_type(eLazyBoolCalculate), m_ranges(), m_unique_ast_type_map() {} -SymbolFileDWARF::~SymbolFileDWARF() {} +SymbolFileDWARF::~SymbolFileDWARF() { + DWZCommonFileClear(); +} static const ConstString &GetDWARFMachOSegmentName() { static ConstString g_dwarf_section_name("__DWARF"); @@ -435,6 +437,7 @@ } TypeSystem *SymbolFileDWARF::GetTypeSystemForLanguage(LanguageType language) { + lldbassert(!GetIsDWZ()); SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile(); TypeSystem *type_system; if (debug_map_symfile) { @@ -459,6 +462,9 @@ m_obj_file->ReadSectionData(section, m_dwarf_data); } + // DWZ needs to be already prepared for indexing below. + InitializeDWZ(); + if (!GetGlobalPluginProperties()->IgnoreFileIndexes()) { DWARFDataExtractor apple_names, apple_namespaces, apple_types, apple_objc; LoadSectionData(eSectionTypeDWARFAppleNames, apple_names); @@ -596,6 +602,13 @@ DWARFDataSegment &data_segment) { llvm::call_once(data_segment.m_flag, [this, sect_type, &data_segment] { this->LoadSectionData(sect_type, std::ref(data_segment.m_data)); + if (sect_type == eSectionTypeDWARFDebugInfo && m_dwz_common_file) { + SymbolFileDWARF *dwz_symbol_file = m_dwz_common_file->SymbolFile(); + const DWARFDataExtractor &dwz_debug_info_data = + dwz_symbol_file->get_debug_info_data(); + lldbassert(dwz_debug_info_data.GetByteSize()); + data_segment.m_data.OffsetData(dwz_debug_info_data.GetByteSize()); + } }); return data_segment.m_data; } @@ -3788,3 +3801,158 @@ }); return m_dwp_symfile.get(); } + +std::unordered_set + SymbolFileDWARF::m_filespec_to_dwzcommonfile; +llvm::sys::RWMutex SymbolFileDWARF::m_filespec_to_dwzcommonfile_mutex; + +void SymbolFileDWARF::InitializeDWZ() { + const DWARFDataExtractor §ion_extractor(get_gnu_debugaltlink()); + if (section_extractor.GetByteSize() == 0) + return; // .gnu_debugaltlink does not exist + if (GetIsDWZ()) { + GetObjectFile()->GetModule()->ReportWarning( + "Error reading DWZ common file - it does contain .gnu_debugaltlink"); + return; + } + lldb::offset_t offset = 0; + const char *link_cstr(section_extractor.GetCStr(&offset)); + if (!link_cstr) { + GetObjectFile()->GetModule()->ReportWarning( + "Cannot get DWZ common file - missing section .gnu_debugaltlink"); + return; + } + lldb::offset_t uuid_bytes_size = section_extractor.BytesLeft(offset); + const void *uuid_bytes(section_extractor.GetData(&offset, uuid_bytes_size)); + if (!uuid_bytes) { + GetObjectFile()->GetModule()->ReportWarning( + "Cannot get DWZ common file - missing build-id" + " in section .gnu_debugaltlink with string \"%s\"", + link_cstr); + return; + } + UUID link_uuid = UUID::fromOptionalData(uuid_bytes, uuid_bytes_size); + if (!link_uuid.IsValid()) { + GetObjectFile()->GetModule()->ReportWarning( + "Cannot get DWZ common file - invalid build-id size %" PRIu64 + " in section .gnu_debugaltlink with string \"%s\"", + uuid_bytes_size, link_cstr); + return; + } + // For objfile "/usr/lib/debug/usr/bin/true.debug" + // link_cstr is "../../.dwz/coreutils-8.25-17.fc25.x86_64". + ModuleSpec dwz_module_spec; + FileSpec &dwz_fspec = dwz_module_spec.GetFileSpec(); + dwz_fspec = FileSpec(link_cstr, false /* resolve_path */); + dwz_fspec.PrependPathComponent( + GetObjectFile()->GetFileSpec().CopyByRemovingLastPathComponent()); + // C++14: Use heterogenous lookup. + DWZCommonFile dwz_fspec_lookup(dwz_fspec); + { + llvm::sys::ScopedReader lock(m_filespec_to_dwzcommonfile_mutex); + const auto it = m_filespec_to_dwzcommonfile.find(dwz_fspec_lookup); + if (it != m_filespec_to_dwzcommonfile.end()) { + DWZCommonFileSet(const_cast(&*it)); + return; + } + } + dwz_module_spec.GetArchitecture() = + GetObjectFile()->GetModule()->GetArchitecture(); + ModuleSP dwz_module = std::make_shared(dwz_module_spec); + DataBufferSP dwz_file_data_sp; + lldb::offset_t dwz_file_data_offset = 0; + lldb::ObjectFileSP dwz_obj_file = ObjectFile::FindPlugin( + dwz_module, &dwz_fspec, 0, dwz_fspec.GetByteSize(), + dwz_file_data_sp, dwz_file_data_offset); + if (!dwz_obj_file) { + GetObjectFile()->GetModule()->ReportWarning( + "Cannot get DWZ common file - file \"%s\" cannot be opened", + dwz_fspec.GetCString()); + return; + } + lldbassert(dwz_obj_file->GetFileSpec() == dwz_fspec); + UUID dwz_uuid; + if (!dwz_obj_file->GetUUID(&dwz_uuid)) { + GetObjectFile()->GetModule()->ReportWarning( + "Cannot get DWZ common file - file \"%s\" does not have build-id", + dwz_fspec.GetCString()); + return; + } + if (link_uuid != dwz_uuid) { + GetObjectFile()->GetModule()->ReportWarning( + "Cannot get DWZ common file - expected build-id %s but file \"%s\"" + " has build-id %s", + link_uuid.GetAsString().c_str(), dwz_fspec.GetCString(), + dwz_uuid.GetAsString().c_str()); + return; + } + auto dwz_symbol_file = + llvm::make_unique(dwz_obj_file.get()); + dwz_symbol_file->m_is_dwz = true; + dwz_symbol_file->InitializeObject(); + // Call private DWARFDebugInfo::ParseCompileUnitHeadersIfNeeded() as + // otherwise DWARFCompileUnit::GetAbbreviations() would have no data. + dwz_symbol_file->DebugInfo()->GetNumCompileUnits(); + { + llvm::sys::ScopedWriter lock(m_filespec_to_dwzcommonfile_mutex); + const auto it = m_filespec_to_dwzcommonfile.find(dwz_fspec_lookup); + if (it != m_filespec_to_dwzcommonfile.end()) + DWZCommonFileSet(const_cast(&*it)); + else { + const auto insertpair = m_filespec_to_dwzcommonfile.emplace( + std::move(dwz_symbol_file), std::move(dwz_obj_file), + std::move(dwz_module)); + lldbassert(insertpair.second); + DWZCommonFileSet(const_cast(&*insertpair.first)); + lldbassert(*m_dwz_common_file == dwz_fspec_lookup); + } + } +} + +void SymbolFileDWARF::DWZCommonFileSet(DWZCommonFile *dwz_common_file) { + if (m_dwz_common_file == dwz_common_file) + return; + DWZCommonFileClear(); + m_dwz_common_file = dwz_common_file; + ++m_dwz_common_file->m_use_count; + + // GetCachedSectionData calls OffsetData as m_dwarf_data is not yet fetched. +} + +void SymbolFileDWARF::DWZCommonFileClear() { + if (!m_dwz_common_file) + return; + SymbolFileDWARF *dwz_symbol_file = m_dwz_common_file->SymbolFile(); + + DWARFDebugInfo *dwz_debuginfo = dwz_symbol_file->DebugInfo(); + if (dwz_debuginfo) { + const size_t dwz_num_cus = dwz_debuginfo->GetNumCompileUnits(); + for (size_t dwz_cu_idx = 0; dwz_cu_idx < dwz_num_cus; ++dwz_cu_idx) { + DWARFUnit *dwz_cu = dwz_debuginfo->GetCompileUnitAtIndex(dwz_cu_idx); + if (dwz_cu) + dwz_cu->DWZDeleteSymbolFile(this); + } + } + + const DWARFDataExtractor &dwz_debug_info_data = + dwz_symbol_file->get_debug_info_data(); + lldbassert(dwz_debug_info_data.GetByteSize()); + m_dwarf_data.OffsetData(-dwz_debug_info_data.GetByteSize()); + + llvm::sys::ScopedWriter lock(m_filespec_to_dwzcommonfile_mutex); + lldbassert(m_dwz_common_file->m_use_count); + if (--m_dwz_common_file->m_use_count) + return; + size_t erased = m_filespec_to_dwzcommonfile.erase(*m_dwz_common_file); + lldbassert(erased == 1); +} + +SymbolFileDWARF::DWZCommonFile::DWZCommonFile(const FileSpec &filespec_ref) + : m_filespec_ref(filespec_ref) {} + +SymbolFileDWARF::DWZCommonFile::DWZCommonFile( + std::unique_ptr symbol_file, + lldb::ObjectFileSP obj_file, lldb::ModuleSP module) + : m_symbol_file(std::move(symbol_file)), m_obj_file(std::move(obj_file)), + m_module(std::move(module)), m_filespec_ref(m_obj_file->GetFileSpec()) {} Index: source/Utility/DataEncoder.cpp =================================================================== --- source/Utility/DataEncoder.cpp +++ source/Utility/DataEncoder.cpp @@ -79,7 +79,8 @@ if (data != nullptr) { const uint8_t *data_bytes = data->GetBytes(); if (data_bytes != nullptr) { - assert(m_start >= data_bytes); + // For DWARFDataExtractor::OffsetData we need to return negative value. + // assert(m_start >= data_bytes); return m_start - data_bytes; } } Index: source/Utility/DataExtractor.cpp =================================================================== --- source/Utility/DataExtractor.cpp +++ source/Utility/DataExtractor.cpp @@ -230,7 +230,8 @@ if (data != nullptr) { const uint8_t *data_bytes = data->GetBytes(); if (data_bytes != nullptr) { - assert(m_start >= data_bytes); + // For DWARFDataExtractor::OffsetData we need to return negative value. + // assert(m_start >= data_bytes); return m_start - data_bytes; } }