diff --git a/lldb/include/lldb/Utility/ConstString.h b/lldb/include/lldb/Utility/ConstString.h --- a/lldb/include/lldb/Utility/ConstString.h +++ b/lldb/include/lldb/Utility/ConstString.h @@ -408,6 +408,17 @@ /// in memory. static size_t StaticMemorySize(); + class Hasher { + public: + size_t operator()(const ConstString &key) const { + // https://stackoverflow.com/questions/7666509/hash-function-for-string + size_t hash = 5381; + for (const char *p = key.m_string; *p; ++p) + hash = hash * 33 + static_cast(*p); + return hash; + } + }; + protected: template friend struct ::llvm::DenseMapInfo; /// Only used by DenseMapInfo. diff --git a/lldb/include/lldb/Utility/FileSpec.h b/lldb/include/lldb/Utility/FileSpec.h --- a/lldb/include/lldb/Utility/FileSpec.h +++ b/lldb/include/lldb/Utility/FileSpec.h @@ -397,6 +397,14 @@ ConstString GetLastPathComponent() const; + 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: friend struct llvm::yaml::MappingTraits; diff --git a/lldb/source/Plugins/SymbolFile/DWARF/CMakeLists.txt b/lldb/source/Plugins/SymbolFile/DWARF/CMakeLists.txt --- a/lldb/source/Plugins/SymbolFile/DWARF/CMakeLists.txt +++ b/lldb/source/Plugins/SymbolFile/DWARF/CMakeLists.txt @@ -37,6 +37,7 @@ NameToDIE.cpp SymbolFileDWARF.cpp SymbolFileDWARFDwo.cpp + SymbolFileDWARFDwz.cpp SymbolFileDWARFDebugMap.cpp UniqueDWARFASTType.cpp diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFBaseDIE.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFBaseDIE.cpp --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFBaseDIE.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFBaseDIE.cpp @@ -30,7 +30,9 @@ return DIERef( m_cu->GetSymbolFileDWARF().GetDwoNum(), (!main_unit ? llvm::None : llvm::Optional(main_unit->GetID())), - DIERef::MainDwz, m_cu->GetDebugSection(), m_die->GetOffset()); + GetCU()->GetSymbolFileDWARF().GetIsDwz() ? DIERef::CommonDwz + : DIERef::MainDwz, + m_cu->GetDebugSection(), m_die->GetOffset()); } dw_tag_t DWARFBaseDIE::Tag() const { diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.cpp --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.cpp @@ -37,6 +37,10 @@ // First get the compile unit DIE only and check contains ranges information. const DWARFDebugInfoEntry *die = GetUnitDIEPtrOnly(); + // DWZ partial units never contain any PC. + if (die && die->Tag() == DW_TAG_partial_unit) + return; + const dw_offset_t cu_offset = GetOffset(); if (die) { DWARFRangeList ranges; @@ -117,6 +121,8 @@ } DWARFUnit *DWARFCompileUnit::GetMainDWARFUnit(DWARFUnit *main_unit) { + if (GetUnitDIEOnly().Tag() != DW_TAG_partial_unit) + main_unit = this; main_unit = &main_unit->GetNonSkeletonUnit(); return main_unit; } diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.cpp --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.cpp @@ -25,6 +25,7 @@ #include "DWARFDebugInfoEntry.h" #include "DWARFFormValue.h" #include "DWARFTypeUnit.h" +#include "SymbolFileDWARFDwz.h" using namespace lldb; using namespace lldb_private; @@ -39,6 +40,8 @@ if (m_cu_aranges_up) return *m_cu_aranges_up; + lldbassert(!m_dwarf.GetIsDwz()); + m_cu_aranges_up = std::make_unique(); const DWARFDataExtractor &debug_aranges_data = m_context.getOrLoadArangesData(); @@ -151,12 +154,22 @@ } DWARFUnit *DWARFDebugInfo::GetUnit(const DIERef &die_ref) { + bool dwz_common = + die_ref.main_cu() && die_ref.dwz_common() == DIERef::CommonDwz; + if (dwz_common && !m_dwarf.GetIsDwz()) { + lldbassert(m_dwarf.GetDwzSymbolFile()); + return m_dwarf.GetDwzSymbolFile()->DebugInfo().GetUnit(die_ref); + } + lldbassert(dwz_common == m_dwarf.GetIsDwz()); return GetUnitContainingDIEOffset(die_ref.section(), die_ref.die_offset()); } DWARFUnit *DWARFDebugInfo::GetMainUnit(const DIERef &die_ref) { - // This function will be extended by a later DWZ patch. - DWARFUnit *main_unit = GetUnit(die_ref); + DWARFUnit *main_unit; + if (!die_ref.main_cu()) + main_unit = GetUnit(die_ref); + else + main_unit = GetUnitAtIndex(*m_dwarf.GetDWARFUnitIndex(*die_ref.main_cu())); return main_unit; } diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp @@ -115,6 +115,9 @@ else form_size = 4; break; + case DW_FORM_GNU_ref_alt: + form_size = 4; + break; // 0 sized form case DW_FORM_flag_present: @@ -179,6 +182,7 @@ case DW_FORM_strp: case DW_FORM_line_strp: + case DW_FORM_GNU_strp_alt: case DW_FORM_sec_offset: data.GetU32(&offset); break; diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp @@ -16,6 +16,7 @@ #include "DWARFDebugInfo.h" #include "DWARFFormValue.h" #include "DWARFUnit.h" +#include "SymbolFileDWARFDwz.h" class DWARFUnit; @@ -76,6 +77,7 @@ case DW_FORM_strp: case DW_FORM_line_strp: case DW_FORM_sec_offset: + case DW_FORM_GNU_strp_alt: m_value.value.uval = data.GetMaxU64(offset_ptr, 4); break; case DW_FORM_addrx1: @@ -124,6 +126,11 @@ ref_addr_size = 4; m_value.value.uval = data.GetMaxU64(offset_ptr, ref_addr_size); break; + case DW_FORM_GNU_ref_alt: + assert(m_unit); + ref_addr_size = 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; @@ -255,6 +262,11 @@ *offset_ptr += ref_addr_size; return true; + case DW_FORM_GNU_ref_alt: + ref_addr_size = 4; + *offset_ptr += ref_addr_size; + return true; + // 0 bytes values (implied from DW_FORM) case DW_FORM_flag_present: case DW_FORM_implicit_const: @@ -287,6 +299,7 @@ case DW_FORM_sec_offset: case DW_FORM_strp: case DW_FORM_line_strp: + case DW_FORM_GNU_strp_alt: *offset_ptr += 4; return true; @@ -435,6 +448,9 @@ case DW_FORM_ref_udata: unit_relative_offset = true; break; + case DW_FORM_GNU_ref_alt: + DumpAddress(s.AsRawOstream(), uvalue, 4 * 2); + break; // All DW_FORM_indirect attributes should be resolved prior to calling this // function @@ -477,6 +493,20 @@ if (m_form == DW_FORM_line_strp) return context.getOrLoadLineStrData().PeekCStr(m_value.value.uval); + if (m_form == DW_FORM_GNU_strp_alt) { + SymbolFileDWARFDwz *dwz_symbol_file = + m_unit->GetSymbolFileDWARF().GetDwzSymbolFile(); + if (!dwz_symbol_file) { + m_unit->GetSymbolFileDWARF().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->GetDWARFContext().getOrLoadStrData().PeekCStr( + m_value.value.uval); + } + return nullptr; } @@ -508,6 +538,7 @@ case DW_FORM_ref_udata: assert(m_unit); // Unit must be valid for DW_FORM_ref forms that are compile // unit relative or we will get this wrong + // The offset adjustment is already appropriate inside this CU. value += m_unit->GetOffset(); if (!m_unit->ContainsDIEOffset(value)) { m_unit->GetSymbolFileDWARF().GetObjectFile()->GetModule()->ReportError( @@ -517,7 +548,48 @@ } return const_cast(m_unit)->GetDIE(value); + case DW_FORM_GNU_ref_alt: { + assert(m_unit); + SymbolFileDWARF &symbol_file = m_unit->GetSymbolFileDWARF(); + if (symbol_file.GetIsDwz()) + symbol_file.GetObjectFile()->GetModule()->ReportError( + "DW_FORM_GNU_ref_alt to DWZ offset 0x%8.8" PRIx64 + " is in a DWZ common file itself", + value); + else if (!symbol_file.GetDwzSymbolFile()) { + symbol_file.GetObjectFile()->GetModule()->ReportError( + "DW_FORM_GNU_ref_alt to DWZ offset 0x%8.8" PRIx64 + " has no DWZ common file", + value); + return {}; + } + SymbolFileDWARF *target_symbol_file = &symbol_file; + if (!target_symbol_file->GetIsDwz()) { + target_symbol_file = symbol_file.GetDwzSymbolFile(); + if (!target_symbol_file) { + symbol_file.GetObjectFile()->GetModule()->ReportError( + "There is Reference to DWZ offset 0x%8.8" PRIx64 + " but the DWZ common file is missing", + value); + return {}; + } + } + lldbassert(target_symbol_file->GetIsDwz()); + DWARFDebugInfo &target_debuginfo = target_symbol_file->DebugInfo(); + DWARFUnit *target_cu = target_debuginfo.GetUnitContainingDIEOffset( + DIERef::Section::DebugInfo, value); + if (!target_cu) { + symbol_file.GetObjectFile()->GetModule()->ReportError( + "There is Reference to DWZ offset 0x%8.8" PRIx64 + " but the DWZ common file does not contain a unit for that offset", + value); + return {}; + } + return target_cu->GetDIE(value); + } + case DW_FORM_ref_addr: { + assert(m_unit); DWARFUnit *ref_cu = m_unit->GetSymbolFileDWARF().DebugInfo().GetUnitContainingDIEOffset( DIERef::Section::DebugInfo, value); @@ -629,6 +701,8 @@ case DW_FORM_GNU_str_index: case DW_FORM_GNU_addr_index: case DW_FORM_implicit_const: + case DW_FORM_GNU_ref_alt: + case DW_FORM_GNU_strp_alt: return true; default: break; diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFTypeUnit.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFTypeUnit.h --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFTypeUnit.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFTypeUnit.h @@ -31,7 +31,9 @@ const DWARFUnitHeader &header, const DWARFAbbreviationDeclarationSet &abbrevs, DIERef::Section section, bool is_dwo) - : DWARFUnit(dwarf, uid, header, abbrevs, section, is_dwo) {} + : DWARFUnit(dwarf, uid, header, abbrevs, section, is_dwo) { + assert(!dwarf.GetIsDwz()); + } friend class DWARFUnit; }; diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp @@ -651,6 +651,8 @@ } uint64_t DWARFUnit::GetDWARFLanguageType() { + lldbassert(!GetSymbolFileDWARF().GetIsDwz()); + if (m_language_type) return *m_language_type; diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.cpp --- a/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.cpp @@ -46,12 +46,14 @@ if (!cu_offset) return llvm::None; + // FIXME: .debug_names have no DWZ support yet. DWARFUnit *cu = m_debug_info.GetUnitAtOffset(DIERef::Section::DebugInfo, *cu_offset); if (!cu) return llvm::None; cu = &cu->GetNonSkeletonUnit(); if (llvm::Optional die_offset = entry.getDIEUnitOffset()) + // FIXME: .debug_names have no DWZ support yet. return DIERef(cu->GetSymbolFileDWARF().GetDwoNum(), llvm::None, DIERef::MainDwz, DIERef::Section::DebugInfo, cu->GetOffset() + *die_offset); @@ -170,6 +172,7 @@ if (!ref) continue; + // FIXME: .debug_names have no DWZ support yet. DWARFUnit *cu = m_debug_info.GetUnit(*ref); if (!cu || !cu->Supports_DW_AT_APPLE_objc_complete_type()) { incomplete_types.push_back(*ref); diff --git a/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp b/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp --- a/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp @@ -120,6 +120,15 @@ unit.GetOffset()); } + // DWZ DW_TAG_partial_unit will get indexed through DW_TAG_imported_unit from + // its main DW_TAG_compile_unit (possibly transitively). Indexing it + // standalone is not possible as we would not have its main CU. Also try to + // prevent calling GetUnitDIEOnly() which may be expensive. + if (unit.GetSymbolFileDWARF().GetIsDwz()) + return; + if (unit.GetUnitDIEOnly().Tag() == DW_TAG_partial_unit) + return; + const LanguageType cu_language = SymbolFileDWARF::GetLanguage(unit); IndexUnitImpl(unit, &unit, cu_language, set); @@ -160,6 +169,7 @@ case DW_TAG_union_type: case DW_TAG_unspecified_type: case DW_TAG_variable: + case DW_TAG_imported_unit: break; default: @@ -175,7 +185,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, attributes); if (num_attributes > 0) { for (uint32_t i = 0; i < num_attributes; ++i) { @@ -219,6 +229,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; } } } @@ -321,6 +336,39 @@ } 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; + } + DWARFDIE import_die = import_die_form.Reference(); + if (!import_die.IsValid()) + break; + DWARFUnit *import_cu = import_die.GetCU(); + dw_offset_t import_cu_firstdie_offset = import_cu->GetFirstDIEOffset(); + if (import_die.GetOffset() != 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_die.GetOffset(), + import_cu_firstdie_offset); + break; + } + IndexUnitImpl(*import_cu, main_unit, cu_language, set); + } + break; + default: continue; } diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h @@ -52,6 +52,7 @@ class SymbolFileDWARFDebugMap; class SymbolFileDWARFDwo; class SymbolFileDWARFDwp; +class SymbolFileDWARFDwz; #define DIE_IS_BEING_PARSED ((lldb_private::Type *)1) @@ -75,6 +76,7 @@ friend class DWARFCompileUnit; friend class DWARFDIE; friend class DWARFASTParserClang; + friend class SymbolFileDWARFDwz; // Static Functions static void Initialize(); @@ -323,6 +325,9 @@ static lldb::LanguageType GetLanguage(DWARFUnit &unit); + SymbolFileDWARFDwz *GetDwzSymbolFile() const { return m_dwz_symfile; } + virtual bool GetIsDwz() const { return false; } + llvm::Optional GetDWARFUnitIndex(uint32_t cu_idx); protected: @@ -492,6 +497,8 @@ const lldb_private::FileSpecList &GetSharedUnitSupportFiles(DWARFUnit &tu); + SymbolFileDWARFDwz *m_dwz_symfile = nullptr; + lldb::ModuleWP m_debug_map_module_wp; SymbolFileDWARFDebugMap *m_debug_map_symfile; diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp @@ -71,6 +71,7 @@ #include "ManualDWARFIndex.h" #include "SymbolFileDWARFDebugMap.h" #include "SymbolFileDWARFDwo.h" +#include "SymbolFileDWARFDwz.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/Support/FileSystem.h" @@ -437,7 +438,10 @@ m_fetched_external_modules(false), m_supports_DW_AT_APPLE_objc_complete_type(eLazyBoolCalculate) {} -SymbolFileDWARF::~SymbolFileDWARF() {} +SymbolFileDWARF::~SymbolFileDWARF() { + if (m_dwz_symfile) + m_dwz_symfile->ClearForDWARF(*this); +} static ConstString GetDWARFMachOSegmentName() { static ConstString g_dwarf_section_name("__DWARF"); @@ -454,6 +458,8 @@ llvm::Expected SymbolFileDWARF::GetTypeSystemForLanguage(LanguageType language) { + lldbassert(!GetIsDwz()); + if (SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile()) return debug_map_symfile->GetTypeSystemForLanguage(language); @@ -468,6 +474,9 @@ void SymbolFileDWARF::InitializeObject() { Log *log = LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_INFO); + // DWZ needs to be already prepared for indexing below. + SymbolFileDWARFDwz::InitializeForDWARF(*this); + if (!GetGlobalPluginProperties()->IgnoreFileIndexes()) { DWARFDataExtractor apple_names, apple_namespaces, apple_types, apple_objc; LoadSectionData(eSectionTypeDWARFAppleNames, apple_names); @@ -676,6 +685,7 @@ } lldb::CompUnitSP SymbolFileDWARF::ParseCompileUnit(DWARFCompileUnit &dwarf_cu) { + lldbassert(!GetIsDwz()); CompUnitSP cu_sp; CompileUnit *comp_unit = (CompileUnit *)dwarf_cu.GetUserData(); if (comp_unit) { @@ -908,9 +918,13 @@ FileSpec SymbolFileDWARF::GetFile(DWARFUnit &unit, size_t file_idx) { if (auto *dwarf_cu = llvm::dyn_cast(&unit)) { - if (CompileUnit *lldb_cu = GetCompUnitForDWARFCompUnit(*dwarf_cu)) - return lldb_cu->GetSupportFiles().GetFileSpecAtIndex(file_idx); - return FileSpec(); + // Try to prevent GetUnitDIEOnly() which may be expensive. + if (!unit.GetSymbolFileDWARF().GetIsDwz() && + unit.GetUnitDIEOnly().Tag() == DW_TAG_compile_unit) { + if (CompileUnit *lldb_cu = GetCompUnitForDWARFCompUnit(*dwarf_cu)) + return lldb_cu->GetSupportFiles().GetFileSpecAtIndex(file_idx); + return FileSpec(); + } } return GetSharedUnitSupportFiles(unit).GetFileSpecAtIndex(file_idx); @@ -1271,8 +1285,11 @@ const DWARFBaseDIE &die) { if (!die.IsValid()) return LLDB_INVALID_UID; - // if (die.GetCU()->GetUnitDIEOnly().Tag() != DW_TAG_partial_unit) - main_unit = nullptr; + // Try to prevent GetUnitDIEOnly() which may be expensive. + if (!GetIsDwz() && die.GetCU()->GetUnitDIEOnly().Tag() != DW_TAG_partial_unit) + main_unit = nullptr; + else + lldbassert(main_unit); return GetUID(main_unit, die.GetDIERef(main_unit)); } @@ -1280,6 +1297,10 @@ if (GetDebugMapSymfile()) return GetID() | ref.die_offset(); + if (GetIsDwz()) { + lldbassert(main_unit); + return main_unit->GetSymbolFileDWARF().GetUID(main_unit, ref); + } if (main_unit) main_unit = &main_unit->GetNonSkeletonUnit(); if (ref.dwo_num().hasValue()) @@ -1308,6 +1329,9 @@ DWARFUnit *main_unit_check; DWARFDIE dwarfdie_check2 = GetDIE(retval, &main_unit_check); lldbassert(dwarfdie_check2 == dwarfdie_check); + lldbassert(dwarfdie_check.GetCU()->GetUnitDIEOnly().Tag() != + DW_TAG_partial_unit || + main_unit_check == main_unit); #endif return retval; @@ -1345,8 +1369,15 @@ if (kind == DIERef::Kind::DwoKind) dwo_num = uid >> 32 & 0x1fffffff; - return DecodedUID{ - *this, {dwo_num, llvm::None, DIERef::MainDwz, section, die_offset}}; + llvm::Optional main_cu = uid >> 32 & 0x1fffffff; + if (kind != DIERef::Kind::MainDwzKind && kind != DIERef::Kind::DwzCommonKind) + main_cu = llvm::None; + + return DecodedUID{*this, + {dwo_num, main_cu, + kind == DIERef::Kind::DwzCommonKind ? DIERef::CommonDwz + : DIERef::MainDwz, + section, die_offset}}; } DWARFDIE @@ -1619,7 +1650,11 @@ DWARFDIE SymbolFileDWARF::GetDIE(const DIERef &die_ref, DWARFUnit **main_unit_return) { - if (die_ref.dwo_num()) { + if (die_ref.dwo_num() && !GetIsDwz()) { + if (GetDwzSymbolFile()) { + lldbassert(0 == *die_ref.dwo_num()); + return GetDwzSymbolFile()->GetDIE(die_ref, main_unit_return); + } SymbolFileDWARF *dwarf = *die_ref.dwo_num() == 0x1fffffff ? m_dwp_symfile.get() : this->DebugInfo() @@ -4028,5 +4063,7 @@ } LanguageType SymbolFileDWARF::GetLanguage(DWARFUnit &unit) { + // Caller should have called GetMainDWARFUnit(). + lldbassert(unit.GetUnitDIEOnly().Tag() != DW_TAG_partial_unit); return LanguageTypeFromDWARF(unit.GetDWARFLanguageType()); } diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwz.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwz.h new file mode 100644 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwz.h @@ -0,0 +1,36 @@ +//===-- SymbolFileDWARFDwz.h ------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef SymbolFileDWARFDwz_SymbolFileDWARFDwz_h_ +#define SymbolFileDWARFDwz_SymbolFileDWARFDwz_h_ + +#include "SymbolFileDWARF.h" + +typedef std::unique_ptr SymbolFileDWARFDwzUP; + +class SymbolFileDWARFDwz : public SymbolFileDWARF { +public: + SymbolFileDWARFDwz(lldb::ObjectFileSP objfile, lldb::ModuleSP m_module); + + bool GetIsDwz() const override { return true; } + + static void InitializeForDWARF(SymbolFileDWARF &dwarf); + + void ClearForDWARF(SymbolFileDWARF &dwarf); + +private: + void SetForDWARF(SymbolFileDWARF &dwarf); + + size_t m_use_count = 0; + lldb::ModuleSP m_module; + + SymbolFileDWARFDwz(const SymbolFileDWARFDwz &) = delete; + const SymbolFileDWARFDwz &operator=(const SymbolFileDWARFDwz &) = delete; +}; + +#endif // SymbolFileDWARFDwz_SymbolFileDWARFDwz_h_ diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwz.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwz.cpp new file mode 100644 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwz.cpp @@ -0,0 +1,175 @@ +//===-- SymbolFileDWARFDwz.cpp ----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "SymbolFileDWARFDwz.h" +#include "DWARFDebugInfo.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Utility/FileSpec.h" +#include "llvm/ADT/PointerUnion.h" + +using namespace lldb; +using namespace lldb_private; + +SymbolFileDWARFDwz::SymbolFileDWARFDwz(ObjectFileSP objfile, ModuleSP module) + : SymbolFileDWARF(std::move(objfile), /*dwo_section_list*/ nullptr), + m_module(std::move(module)) { + InitializeObject(); + // Call private DWARFDebugInfo::ParseCompileUnitHeadersIfNeeded() as + // otherwise DWARFCompileUnit::GetAbbreviations() would have no data. + DebugInfo().GetNumUnits(); +} + +// C++20: Use heterogeneous lookup for unordered container instead. +class DwzAsKey { +public: + DwzAsKey(SymbolFileDWARFDwz &dwz) : ptr(&dwz) {} + DwzAsKey(FileSpec &fspec) : ptr(&fspec) {} + DwzAsKey() {} + SymbolFileDWARFDwz &GetDwz() const { + return *ptr.get(); + } + FileSpec &GetFileSpec() const { + if (ptr.is()) + return *ptr.get(); + return GetDwz().GetObjectFile()->GetFileSpec(); + } + bool operator==(const DwzAsKey &rhs) const { + return GetFileSpec() == rhs.GetFileSpec(); + } + bool operator!=(const DwzAsKey &rhs) const { return !(*this == rhs); } + class Hasher { + public: + size_t operator()(const DwzAsKey &key) const { + return FileSpec::Hasher()(key.GetFileSpec()); + } + }; + +private: + llvm::PointerUnion ptr; +}; + +static std::unordered_map + dwz_map; +static llvm::sys::RWMutex dwz_map_mutex; + +void SymbolFileDWARFDwz::InitializeForDWARF(SymbolFileDWARF &dwarf) { + const DWARFDataExtractor §ion_extractor( + dwarf.GetDWARFContext().getOrLoadGNUDebugAltLink()); + if (section_extractor.GetByteSize() == 0) + return; // .gnu_debugaltlink does not exist + if (dwarf.GetIsDwz()) { + dwarf.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) { + dwarf.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) { + dwarf.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()) { + dwarf.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); + dwz_fspec.PrependPathComponent( + dwarf.GetObjectFile()->GetFileSpec().CopyByRemovingLastPathComponent()); + DwzAsKey dwz_fspec_lookup(dwz_fspec); + { + llvm::sys::ScopedReader lock(dwz_map_mutex); + const auto it = dwz_map.find(dwz_fspec_lookup); + if (it != dwz_map.end()) { + it->second->SetForDWARF(dwarf); + return; + } + } + dwz_module_spec.GetArchitecture() = + dwarf.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, FileSystem::Instance().GetByteSize(dwz_fspec), + dwz_file_data_sp, dwz_file_data_offset); + if (!dwz_obj_file) { + dwarf.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 = dwz_obj_file->GetUUID(); + if (!dwz_uuid) { + dwarf.GetObjectFile()->GetModule()->ReportWarning( + "Cannot get DWZ common file - file \"%s\" does not have build-id", + dwz_fspec.GetCString()); + return; + } + if (link_uuid != dwz_uuid) { + dwarf.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 = + std::make_unique(dwz_obj_file, std::move(dwz_module)); + assert(DwzAsKey(*dwz_symbol_file) == dwz_fspec_lookup); + { + llvm::sys::ScopedWriter lock(dwz_map_mutex); + const auto it = dwz_map.find(dwz_fspec_lookup); + if (it != dwz_map.end()) + it->second->SetForDWARF(dwarf); + else { + dwz_symbol_file->SetForDWARF(dwarf); + const auto insertpair = dwz_map.emplace(DwzAsKey(*dwz_symbol_file), + std::move(dwz_symbol_file)); + lldbassert(insertpair.second); + } + } +} + +void SymbolFileDWARFDwz::SetForDWARF(SymbolFileDWARF &dwarf) { + lldbassert(&dwarf != this); + lldbassert(!dwarf.m_dwz_symfile); + dwarf.m_dwz_symfile = this; + ++m_use_count; +} + +void SymbolFileDWARFDwz::ClearForDWARF(SymbolFileDWARF &main_dwarf) { + lldbassert(main_dwarf.m_dwz_symfile == this); + llvm::sys::ScopedWriter lock(dwz_map_mutex); + lldbassert(m_use_count); + if (--m_use_count) + return; + size_t erased = dwz_map.erase(DwzAsKey(*this)); + lldbassert(erased == 1); +}