Index: include/lldb/Utility/ConstString.h =================================================================== --- include/lldb/Utility/ConstString.h +++ include/lldb/Utility/ConstString.h @@ -466,6 +466,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 @@ -574,6 +574,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: //------------------------------------------------------------------ // Member variables Index: source/Plugins/SymbolFile/DWARF/CMakeLists.txt =================================================================== --- source/Plugins/SymbolFile/DWARF/CMakeLists.txt +++ source/Plugins/SymbolFile/DWARF/CMakeLists.txt @@ -22,7 +22,9 @@ DWARFDefines.cpp DWARFDIE.cpp DWARFDIECollection.cpp + DWARFFileOffset.cpp DWARFFormValue.cpp + DWARFPartialUnit.cpp DWARFUnit.cpp HashedNameToDIE.cpp LogChannelDWARF.cpp Index: source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.h =================================================================== --- source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.h +++ source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.h @@ -11,10 +11,15 @@ #define SymbolFileDWARF_DWARFCompileUnit_h_ #include "DWARFUnit.h" -#include "lldb/Utility/LLDBAssert.h" +#include "DWARFFileOffset.h" + +class DWARFCompileUnitDWZ; +class DWARFPartialUnit; class DWARFCompileUnit : public DWARFUnit { friend class DWARFUnit; + friend class DWARFPartialUnit; + friend class DWARFFileOffset; public: static DWARFUnitSP Extract(SymbolFileDWARF *dwarf2Data, @@ -75,14 +80,16 @@ bool GetIsOptimized(); + // DW_TAG_compile_unit with DW_TAG_imported_unit for this DW_TAG_partial_unit. + virtual DWARFUnit *GetMainCU() override { return this; } + virtual const DWARFUnit *GetMainCU() const override { return this; } + protected: virtual DWARFCompileUnit &Data() override { return *this; } virtual const DWARFCompileUnit &Data() const override { return *this; } dw_offset_t GetFileOffset() const; - dw_offset_t MainCU_FileOffsetToUniqOffset(dw_offset_t file) const { - return file; - } + dw_offset_t MainCU_FileOffsetToUniqOffset(DWARFFileOffset file) const; dw_offset_t GetNextCompileUnitFileOffset() const { return ThisCUUniqToFileOffset(GetNextCompileUnitOffset() - 1) + 1; } @@ -117,11 +124,14 @@ // 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; + std::unique_ptr m_dwz_up; std::mutex m_extractdies_mutex; void ParseProducerInfo(); + DWARFUnit *MainCU_FileOffsetToCU(DWARFFileOffset file); + private: DWARFCompileUnit(SymbolFileDWARF *dwarf2Data); Index: source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.cpp =================================================================== --- source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.cpp +++ source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.cpp @@ -20,6 +20,7 @@ #include "lldb/Utility/StreamString.h" #include "lldb/Utility/Timer.h" +#include "DWARFCompileUnitDWZ.h" #include "DWARFDIECollection.h" #include "DWARFDebugAbbrev.h" #include "DWARFDebugAranges.h" @@ -51,6 +52,13 @@ const DWARFDataExtractor &debug_info = dwarf2Data->get_debug_info_data(); cu_sp->m_offset = *file_offset_ptr; + // All CUs in a base symbol file are shifted after its DWZ file (if exists). + SymbolFileDWARF *dwz_symbol_file = dwarf2Data->GetDWZSymbolFile(); + if (dwz_symbol_file) { + const DWARFDataExtractor &dwz_debug_info_data = + dwz_symbol_file->get_debug_info_data(); + cu_sp->m_offset += dwz_debug_info_data.GetByteSize(); + } if (debug_info.ValidOffset(*file_offset_ptr)) { dw_offset_t abbr_offset; @@ -101,6 +109,11 @@ m_die_array.push_back(tmp_array.front()); ++m_die_array_size_atomic; } + + while (m_dwz_up && !m_dwz_up->m_extracted_pu.empty()) { + m_dwz_up->m_extracted_pu.front()->ClearDIEs(keep_compile_unit_die); + m_dwz_up->m_extracted_pu.pop_front(); + } } if (m_dwo_symbol_file) @@ -378,6 +391,10 @@ // First get the compile unit DIE only and check if it has a DW_AT_ranges const DWARFDebugInfoEntry *die = GetCompileUnitDIEPtrOnly(); + // 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; @@ -643,7 +660,13 @@ } dw_offset_t DWARFCompileUnit::GetFileOffset() const { - return GetOffset(); + // 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) + return GetOffset(); + const DWARFDataExtractor &dwz_debug_info_data = + dwz_symbol_file->get_debug_info_data(); + return GetOffset() - dwz_debug_info_data.GetByteSize(); } TypeSystem *DWARFCompileUnit::GetTypeSystem() { @@ -667,3 +690,83 @@ m_ranges_base = ranges_base; m_base_obj_offset = base_obj_offset; } + +DWARFUnit *DWARFCompileUnit::MainCU_FileOffsetToCU(DWARFFileOffset file) { + lldbassert(GetMainCU() == this); + if (!file.GetIsDWZ() && ContainsFileOffset(file.GetFileOffset())) + return this; + SymbolFileDWARF *main_symbol_file = m_dwarf2Data; + lldbassert(!main_symbol_file->GetIsDWZ()); + SymbolFileDWARF *file_symbol_file = main_symbol_file; + SymbolFileDWARF *dwz_symbol_file = main_symbol_file->GetDWZSymbolFile(); + if (file.GetIsDWZ()) { + file_symbol_file = dwz_symbol_file; + if (!file_symbol_file) { + main_symbol_file->GetObjectFile()->GetModule()->ReportError( + "File reference 0x%8.8" PRIx32 " (DWZ file = 1)" + " is in a file without associated DWZ common file", + file.GetFileOffset()); + return nullptr; + } + } + const DWARFDataExtractor &file_debug_info_data = + file_symbol_file->get_debug_info_data(); + if (file.GetFileOffset() >= file_debug_info_data.GetByteSize()) { + main_symbol_file->GetObjectFile()->GetModule()->ReportError( + "File reference 0x%8.8" PRIx32 " (DWZ file = %d)" + " does not belong to the file of size 0x%8.8" PRIx64, + file.GetFileOffset(), file.GetIsDWZ(), + file_debug_info_data.GetByteSize()); + return nullptr; + } + if (m_dwz_up) { + auto it = m_dwz_up->m_dwz_file_to_cu.upper_bound(file); + if (it != m_dwz_up->m_dwz_file_to_cu.begin()) { + --it; + DWARFPartialUnit *pu = it->second; + if (pu->GetSymbolFileDWARF()->GetIsDWZ() == file.GetIsDWZ() + && pu->ContainsFileOffset(file.GetFileOffset())) + return pu; + } + } + dw_offset_t file_as_uniq = file.GetFileOffset(); + if (!file.GetIsDWZ() && dwz_symbol_file) { + // All CUs in a base symbol file are shifted after its DWZ file (if exists). + const DWARFDataExtractor &dwz_debug_info_data = + dwz_symbol_file->get_debug_info_data(); + file_as_uniq += dwz_debug_info_data.GetByteSize(); + } + DWARFDebugInfo *main_debuginfo = main_symbol_file->DebugInfo(); + DWARFUnit *file_u = + main_debuginfo->GetCompileUnitContainingDIEOffset(file_as_uniq); + if (!file_u) { + main_symbol_file->GetObjectFile()->GetModule()->ReportError( + "There is no CU for DIE file offset 0x%8.8" PRIx32, + file.GetFileOffset()); + return nullptr; + } + lldbassert(file_u->GetMainCU() == file_u); + DWARFCompileUnit *file_cu = static_cast(file_u); + auto pu_sp = std::make_shared(file_cu, this); + DWARFPartialUnit *pu = pu_sp.get(); + main_debuginfo->InsertPUGetUniqOffset(std::move(pu_sp)); + DWARFFileOffset file_start = DWARFFileOffset(file_cu); + lldbassert(file_start.GetIsDWZ() == file.GetIsDWZ()); + lldbassert(file_start.GetFileOffset() < file.GetFileOffset()); + if (!m_dwz_up) + m_dwz_up = llvm::make_unique(); + auto insertpair = m_dwz_up->m_dwz_file_to_cu.emplace(file_start, pu); + lldbassert(insertpair.second); + return pu; +} + +dw_offset_t DWARFCompileUnit::MainCU_FileOffsetToUniqOffset( + DWARFFileOffset file) const { + DWARFUnit *cu = + const_cast(this)->MainCU_FileOffsetToCU(file); + if (!cu) { + // MainCU_FileOffsetToCU already did ReportError. + return file.GetFileOffset(); + } + return cu->ThisCUFileOffsetToUniq(file.GetFileOffset()); +} Index: source/Plugins/SymbolFile/DWARF/DWARFCompileUnitDWZ.h =================================================================== --- /dev/null +++ source/Plugins/SymbolFile/DWARF/DWARFCompileUnitDWZ.h @@ -0,0 +1,40 @@ +//===-- DWARFCompileUnitDWZ.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef SymbolFileDWARF_DWARFCompileUnitDWZ_h_ +#define SymbolFileDWARF_DWARFCompileUnitDWZ_h_ + +#include +#include + +#include "DWARFFileOffset.h" + +class DWARFFileOffset; +class DWARFPartialUnit; +class DWARFUnit; + +class DWARFCompileUnitDWZ { + friend class DWARFUnit; + friend class DWARFCompileUnit; + +public: + DWARFCompileUnitDWZ() {} + +protected: + std::map + m_dwz_file_to_cu; + + // All DW_TAG_partial_unit's extracted for Index-ing this DW_TAG_compile_unit. + std::forward_list m_extracted_pu; + +private: + DISALLOW_COPY_AND_ASSIGN(DWARFCompileUnitDWZ); +}; + +#endif // SymbolFileDWARF_DWARFCompileUnitDWZ_h_ Index: source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.h =================================================================== --- source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.h +++ source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.h @@ -15,6 +15,7 @@ #include "DWARFUnit.h" #include "DWARFDIE.h" +#include "DWARFPartialUnit.h" #include "SymbolFileDWARF.h" #include "lldb/Core/STLUtils.h" #include "lldb/lldb-private.h" @@ -61,12 +62,20 @@ DWARFDebugAranges &GetCompileUnitAranges(); + void InsertPUGetUniqOffset(DWARFPartialUnitSP pu_sp); + protected: + typedef std::shared_ptr DWARFCompileUnitSP; + static bool OffsetLessThanCompileUnitOffset(dw_offset_t offset, const DWARFUnitSP &cu_sp); typedef std::vector CompileUnitColl; + DWARFDebugInfo *DWZRedirect(dw_offset_t &offset); + DWARFUnit *GetCompileUnitInFile(dw_offset_t cu_offset, uint32_t *idx_ptr); + DWARFUnit *GetCompileUnitContainingDIEOffsetInFile(dw_offset_t die_offset); + //---------------------------------------------------------------------- // Member variables //---------------------------------------------------------------------- @@ -76,7 +85,10 @@ m_cu_aranges_ap; // A quick address to compile unit table std::atomic_size_t m_compile_units_size_atomic { 0 }; + // Protect also m_dwz_uniq_last. mutable llvm::sys::RWMutex m_compile_units_mutex; + // Last offset after existing files for remapping DW_TAG_partial_unit's. + dw_offset_t m_dwz_uniq_last = DW_INVALID_OFFSET; private: // All parsing needs to be done partially any managed by this class as Index: source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.cpp =================================================================== --- source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.cpp +++ source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.cpp @@ -44,6 +44,25 @@ m_compile_units_size_atomic = 0; m_dwarf2Data = dwarf2Data; m_compile_units.clear(); + SymbolFileDWARF *main_symbol_file = m_dwarf2Data; + const DWARFDataExtractor &main_debug_info_data = + main_symbol_file->get_debug_info_data(); + m_dwz_uniq_last = main_debug_info_data.GetByteSize(); + SymbolFileDWARF *dwz_symbol_file = main_symbol_file->GetDWZSymbolFile(); + if (dwz_symbol_file) { + const DWARFDataExtractor &dwz_debug_info_data = + dwz_symbol_file->get_debug_info_data(); + m_dwz_uniq_last += dwz_debug_info_data.GetByteSize(); + } +} + +void DWARFDebugInfo::InsertPUGetUniqOffset(DWARFPartialUnitSP pu_sp) { + lldbassert(!m_dwarf2Data->GetIsDWZ()); + llvm::sys::ScopedWriter lock(m_compile_units_mutex); + pu_sp->SetOffset(m_dwz_uniq_last); + m_dwz_uniq_last += pu_sp->Size() + pu_sp->GetDebugInfoSize(); + m_compile_units.push_back(std::move(pu_sp)); + ++m_compile_units_size_atomic; } DWARFDebugAranges &DWARFDebugInfo::GetCompileUnitAranges() { @@ -60,6 +79,14 @@ ".debug_aranges", m_dwarf2Data->GetObjectFile()->GetFileSpec().GetPath().c_str()); 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); } @@ -150,8 +177,26 @@ return offset < cu_sp->GetOffset(); } +DWARFDebugInfo *DWARFDebugInfo::DWZRedirect(dw_offset_t &offset) { + lldbassert(!m_dwarf2Data->GetIsDWZ()); + 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) { + 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) { @@ -196,6 +241,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(); llvm::sys::ScopedReader lock(m_compile_units_mutex); @@ -257,6 +309,7 @@ void DWARFDebugInfo::Parse(SymbolFileDWARF *dwarf2Data, Callback callback, void *userData) { if (dwarf2Data) { + lldbassert(!dwarf2Data->GetIsDWZ()); lldb::offset_t file_offset = 0; uint32_t depth = 0; DWARFDebugInfoEntry die; 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(&file_offset); @@ -278,6 +282,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: @@ -325,6 +332,7 @@ break; case DW_FORM_strp: + case DW_FORM_GNU_strp_alt: case DW_FORM_sec_offset: if (cu->IsDWARF64()) debug_info_data.GetU64(&file_offset); Index: source/Plugins/SymbolFile/DWARF/DWARFFileOffset.h =================================================================== --- /dev/null +++ source/Plugins/SymbolFile/DWARF/DWARFFileOffset.h @@ -0,0 +1,51 @@ + +//===-- DWARFFileOffset.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef SymbolFileDWARF_DWARFFileOffset_h_ +#define SymbolFileDWARF_DWARFFileOffset_h_ + +#include "lldb/Core/dwarf.h" +#include "lldb/Utility/LLDBAssert.h" + +class DWARFCompileUnit; + +class DWARFFileOffset { +public: + DWARFFileOffset() : m_file_offset(DW_INVALID_OFFSET) {} + DWARFFileOffset(dw_offset_t file_offset, bool is_dwz) + : m_file_offset(file_offset), m_is_dwz(is_dwz) {} + DWARFFileOffset(DWARFCompileUnit *cu); + dw_offset_t GetFileOffset() const { + lldbassert(m_file_offset != DW_INVALID_OFFSET); + return m_file_offset; + } + bool GetIsDWZ() const { + lldbassert(m_file_offset != DW_INVALID_OFFSET); + return m_is_dwz; + } + explicit operator bool() const { return m_file_offset != DW_INVALID_OFFSET; } + + friend class Less; + struct Less { + bool operator()(const DWARFFileOffset &a, const DWARFFileOffset &b) const { + if (a.m_is_dwz != b.m_is_dwz) + return a.m_is_dwz < b.m_is_dwz; + if (a.m_file_offset != b.m_file_offset) + return a.m_file_offset < b.m_file_offset; + return false; + } + }; + +protected: + dw_offset_t m_file_offset; + bool m_is_dwz; +}; + +#endif // SymbolFileDWARF_DWARFFileOffset_h_ Index: source/Plugins/SymbolFile/DWARF/DWARFFileOffset.cpp =================================================================== --- /dev/null +++ source/Plugins/SymbolFile/DWARF/DWARFFileOffset.cpp @@ -0,0 +1,15 @@ +//===-- DWARFFileOffset.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DWARFFileOffset.h" +#include "DWARFCompileUnit.h" + +DWARFFileOffset::DWARFFileOffset(DWARFCompileUnit *cu) + : m_file_offset(cu->GetFileOffset()), + m_is_dwz(cu->m_dwarf2Data->GetIsDWZ()) {} Index: source/Plugins/SymbolFile/DWARF/DWARFFormValue.h =================================================================== --- source/Plugins/SymbolFile/DWARF/DWARFFormValue.h +++ source/Plugins/SymbolFile/DWARF/DWARFFormValue.h @@ -14,6 +14,7 @@ #include // for NULL class DWARFUnit; +class DWARFFileOffset; class DWARFFormValue { public: @@ -65,7 +66,7 @@ bool ExtractValue(const lldb_private::DWARFDataExtractor &data, lldb::offset_t *file_offset_ptr); const uint8_t *BlockData() const; - uint64_t ReferenceInFile() const; + DWARFFileOffset ReferenceInFile() const; uint64_t Reference() const; uint64_t Reference(dw_offset_t offset) const; bool Boolean() const { return m_value.value.uval != 0; } Index: source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp =================================================================== --- source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp +++ source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp @@ -11,9 +11,13 @@ #include "lldb/Core/dwarf.h" #include "lldb/Utility/Stream.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Core/Module.h" #include "DWARFUnit.h" +#include "DWARFFileOffset.h" #include "DWARFFormValue.h" +#include "DWARFDebugInfo.h" class DWARFUnit; @@ -218,6 +222,7 @@ m_value.value.sval = data.GetSLEB128(file_offset_ptr); break; case DW_FORM_strp: + case DW_FORM_GNU_strp_alt: assert(m_cu); m_value.value.uval = data.GetMaxU64(file_offset_ptr, DWARFUnit::IsDWARF64(m_cu) ? 8 : 4); @@ -250,6 +255,11 @@ case DW_FORM_ref_udata: m_value.value.uval = data.GetULEB128(file_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(file_offset_ptr, ref_addr_size); + break; case DW_FORM_indirect: m_form = data.GetULEB128(file_offset_ptr); indirect = true; @@ -345,6 +355,13 @@ *file_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; + *file_offset_ptr += ref_addr_size; + return true; + // 0 bytes values (implied from DW_FORM) case DW_FORM_flag_present: return true; @@ -365,6 +382,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); *file_offset_ptr += (cu->IsDWARF64() ? 8 : 4); return true; @@ -506,6 +524,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 @@ -546,6 +571,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; } @@ -568,28 +603,33 @@ return symbol_file->get_debug_addr_data().GetMaxU64(&offset, index_size); } -uint64_t DWARFFormValue::ReferenceInFile() const { +DWARFFileOffset DWARFFormValue::ReferenceInFile() const { uint64_t die_file_offset = m_value.value.uval; + assert(m_cu); + bool is_dwz = m_cu->GetSymbolFileDWARF()->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_file_offset += m_cu->GetFileOffset(); break; + case DW_FORM_GNU_ref_alt: // DWZ + is_dwz = true; + break; + + case DW_FORM_ref_addr: default: break; } - return die_file_offset; + return DWARFFileOffset(die_file_offset, is_dwz); } uint64_t DWARFFormValue::Reference() const { - uint64_t file = ReferenceInFile(); + DWARFFileOffset file = ReferenceInFile(); return m_cu->FileOffsetToUniqOffset(file); } @@ -713,6 +753,7 @@ case DW_FORM_ref4: case DW_FORM_ref8: case DW_FORM_ref_udata: { + case DW_FORM_GNU_ref_alt: // DWZ uint64_t a = a_value.Reference(); uint64_t b = b_value.Reference(); if (a < b) @@ -761,6 +802,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/DWARFPartialUnit.h =================================================================== --- /dev/null +++ source/Plugins/SymbolFile/DWARF/DWARFPartialUnit.h @@ -0,0 +1,38 @@ +//===-- DWARFPartialUnit.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef SymbolFileDWARF_DWARFPartialUnit_h_ +#define SymbolFileDWARF_DWARFPartialUnit_h_ + +#include "DWARFUnit.h" +#include "DWARFCompileUnit.h" + +class DWARFPartialUnit; + +typedef std::shared_ptr DWARFPartialUnitSP; + +class DWARFPartialUnit : public DWARFUnit { +public: + DWARFPartialUnit(DWARFCompileUnit *cu, DWARFCompileUnit *main_cu); + + // DW_TAG_compile_unit with DW_TAG_imported_unit for this DW_TAG_partial_unit. + virtual DWARFUnit *GetMainCU() override { return m_main_cu; } + virtual const DWARFUnit *GetMainCU() const override { return m_main_cu; } + +protected: + virtual DWARFCompileUnit &Data() override { return *m_cu; } + virtual const DWARFCompileUnit &Data() const override { return *m_cu; } + +private: + DWARFCompileUnit *m_cu, *m_main_cu; + + DISALLOW_COPY_AND_ASSIGN(DWARFPartialUnit); +}; + +#endif // SymbolFileDWARF_DWARFPartialUnit_h_ Index: source/Plugins/SymbolFile/DWARF/DWARFPartialUnit.cpp =================================================================== --- /dev/null +++ source/Plugins/SymbolFile/DWARF/DWARFPartialUnit.cpp @@ -0,0 +1,16 @@ +//===-- DWARFPartialUnit.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DWARFPartialUnit.h" + +DWARFPartialUnit::DWARFPartialUnit( + DWARFCompileUnit *cu, DWARFCompileUnit *main_cu) + : m_cu(cu), m_main_cu(main_cu) { + lldbassert(!m_main_cu->m_dwarf2Data->GetIsDWZ()); +} Index: source/Plugins/SymbolFile/DWARF/DWARFUnit.h =================================================================== --- source/Plugins/SymbolFile/DWARF/DWARFUnit.h +++ source/Plugins/SymbolFile/DWARF/DWARFUnit.h @@ -46,6 +46,7 @@ void Dump(lldb_private::Stream *s) const; // Offset of the initial length field. dw_offset_t GetOffset() const { return m_offset; } + void SetOffset(dw_offset_t offset); lldb::user_id_t GetID() const; // Size in bytes of the initial length + compile unit header. uint32_t Size() const; @@ -144,7 +145,7 @@ lldbassert(ContainsDIEOffset(uniq)); return uniq; } - dw_offset_t FileOffsetToUniqOffset(dw_offset_t file) const; + dw_offset_t FileOffsetToUniqOffset(DWARFFileOffset file) const; dw_offset_t ThisCUUniqToFileOffset(dw_offset_t uniq) const; bool ContainsFileOffset(dw_offset_t file) const { return ContainsDIEOffset(ThisCUFileOffsetToUniq_nocheck(file)); @@ -152,9 +153,8 @@ dw_offset_t GetNextCompileUnitFileOffset() const; // DW_TAG_compile_unit with DW_TAG_imported_unit for this DW_TAG_partial_unit. - DWARFUnit *GetMainCU() const { - return const_cast(this); - } + virtual DWARFUnit *GetMainCU() = 0; + virtual const DWARFUnit *GetMainCU() const = 0; protected: virtual DWARFCompileUnit &Data() = 0; @@ -171,7 +171,7 @@ NameToDIE &globals, NameToDIE &types, NameToDIE &namespaces); // Offset of the initial length field. - dw_offset_t m_offset; + dw_offset_t m_offset = DW_INVALID_OFFSET; private: const DWARFDebugInfoEntry *GetCompileUnitDIEPtrOnly(); Index: source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp =================================================================== --- source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp +++ source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp @@ -14,6 +14,7 @@ #include "lldb/Symbol/ObjectFile.h" #include "DWARFCompileUnit.h" +#include "DWARFCompileUnitDWZ.h" #include "DWARFDebugInfo.h" #include "LogChannelDWARF.h" #include "SymbolFileDWARFDwo.h" @@ -280,8 +281,8 @@ return Data().GetFileOffset(); } -dw_offset_t DWARFUnit::FileOffsetToUniqOffset(dw_offset_t file) const { - return static_cast(this) +dw_offset_t DWARFUnit::FileOffsetToUniqOffset(DWARFFileOffset file) const { + return static_cast(GetMainCU()) ->MainCU_FileOffsetToUniqOffset(file); } @@ -303,6 +304,11 @@ return Data().DIEPtr(); } +void DWARFUnit::SetOffset(dw_offset_t offset) { + lldbassert(m_offset == DW_INVALID_OFFSET); + m_offset = offset; +} + void DWARFUnit::Index(NameToDIE &func_basenames, NameToDIE &func_fullnames, NameToDIE &func_methods, NameToDIE &func_selectors, @@ -313,6 +319,15 @@ "DWARFUnit associated with .dwo or .dwp " "should not be indexed directly"); + // Are we called for DWARFCompileUnit (contrary to DWARFPartialUnit)? + if (GetMainCU() == this) { + // DWZ DW_TAG_partial_unit will get indexed by DW_AT_import + // from its DW_TAG_compile_unit (possibly transitively). + const DWARFDebugInfoEntry *die = GetCompileUnitDIEPtrOnly(); + if (die && die->Tag() == DW_TAG_partial_unit) + return; + } + Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS)); if (log) { @@ -325,7 +340,7 @@ const LanguageType cu_language = GetLanguageType(); DWARFFormValue::FixedFormSizes fixed_form_sizes = DWARFFormValue::GetFixedFormSizesForAddressSize(GetAddressByteSize(), - IsDWARF64()); + Data().m_is_dwarf64); IndexPrivate(this, cu_language, fixed_form_sizes, GetOffset(), func_basenames, func_fullnames, func_methods, func_selectors, @@ -372,6 +387,7 @@ case DW_TAG_union_type: case DW_TAG_unspecified_type: case DW_TAG_variable: + case DW_TAG_imported_unit: break; default: @@ -387,7 +403,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(dwarf_cu, fixed_form_sizes, attributes); if (num_attributes > 0) { @@ -491,6 +507,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; } } } @@ -652,6 +673,51 @@ } 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)) { + dwarf_cu->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", + dwarf_cu->GetOffset(), die.GetOffset(dwarf_cu)); + break; + } + DWARFFileOffset import_file = import_die_form.ReferenceInFile(); + DWARFCompileUnit *main_cu = + static_cast(dwarf_cu->GetMainCU()); + DWARFUnit *import_cu = main_cu->MainCU_FileOffsetToCU(import_file); + if (!import_cu) + break; + dw_offset_t import_cu_firstdie_file = + import_cu->ThisCUUniqToFileOffset(import_cu->GetFirstDIEOffset()); + if (import_file.GetFileOffset() != import_cu_firstdie_file) { + dwarf_cu->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, + dwarf_cu->GetOffset(), die.GetOffset(dwarf_cu), + import_file.GetFileOffset(), import_cu_firstdie_file); + break; + } + if (import_cu->ExtractDIEsIfNeeded(false) > 1) { + std::lock_guard + guard(main_cu->m_extractdies_mutex); + lldbassert(!!main_cu->m_dwz_up); + main_cu->m_dwz_up->m_extracted_pu.push_front(import_cu); + } + import_cu->Index(func_basenames, func_fullnames, func_methods, + func_selectors, objc_class_selectors, globals, types, namespaces); + } + 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" @@ -312,6 +314,13 @@ // the method returns a pointer to the base compile unit. virtual DWARFUnit *GetBaseCompileUnit(); + 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; @@ -473,6 +482,44 @@ 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 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 @@ -410,7 +410,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"); @@ -426,6 +428,7 @@ } TypeSystem *SymbolFileDWARF::GetTypeSystemForLanguage(LanguageType language) { + lldbassert(!GetIsDWZ()); SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile(); TypeSystem *type_system; if (debug_map_symfile) { @@ -488,6 +491,8 @@ else m_apple_objc_ap.reset(); } + + InitializeDWZ(); } bool SymbolFileDWARF::SupportedVersion(uint16_t version) { @@ -2056,6 +2061,8 @@ if (dwarf_cu) { // dwarf_cu->ExtractDIEsIfNeeded(false) will return zero if the // DIEs for a compile unit have already been parsed. + // While DW_TAG_partial_unit will not be indexed directly each such + // unit will be used by some other unit of this SymbolFileDWARF. if (dwarf_cu->ExtractDIEsIfNeeded(false) > 1) clear_cu_dies[cu_idx] = true; } @@ -4390,3 +4397,134 @@ }); 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; + } + lldb_private::UUID link_uuid; + if (!link_uuid.SetBytes(uuid_bytes,uuid_bytes_size)) { + 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()) { + m_dwz_common_file = const_cast(&*it); + ++m_dwz_common_file->m_use_count; + 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); + lldb_private::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()) + m_dwz_common_file = 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); + m_dwz_common_file = const_cast(&*insertpair.first); + lldbassert(*m_dwz_common_file == dwz_fspec_lookup); + } + ++m_dwz_common_file->m_use_count; + } +} + +void SymbolFileDWARF::DWZCommonFileClear() { + if (!m_dwz_common_file) + return; + 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 lldb_private::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()) {}