diff options
Diffstat (limited to 'contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF')
71 files changed, 21570 insertions, 0 deletions
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/AppleDWARFIndex.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/AppleDWARFIndex.cpp new file mode 100644 index 000000000000..9ae047da1325 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/AppleDWARFIndex.cpp @@ -0,0 +1,175 @@ +//===-- AppleDWARFIndex.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 "Plugins/SymbolFile/DWARF/AppleDWARFIndex.h" +#include "Plugins/SymbolFile/DWARF/DWARFDeclContext.h" +#include "Plugins/SymbolFile/DWARF/DWARFUnit.h" +#include "Plugins/SymbolFile/DWARF/LogChannelDWARF.h" + +#include "lldb/Core/Module.h" +#include "lldb/Symbol/Function.h" + +using namespace lldb_private; +using namespace lldb; + +std::unique_ptr<AppleDWARFIndex> AppleDWARFIndex::Create( + Module &module, DWARFDataExtractor apple_names, + DWARFDataExtractor apple_namespaces, DWARFDataExtractor apple_types, + DWARFDataExtractor apple_objc, DWARFDataExtractor debug_str) { + auto apple_names_table_up = llvm::make_unique<DWARFMappedHash::MemoryTable>( + apple_names, debug_str, ".apple_names"); + if (!apple_names_table_up->IsValid()) + apple_names_table_up.reset(); + + auto apple_namespaces_table_up = + llvm::make_unique<DWARFMappedHash::MemoryTable>( + apple_namespaces, debug_str, ".apple_namespaces"); + if (!apple_namespaces_table_up->IsValid()) + apple_namespaces_table_up.reset(); + + auto apple_types_table_up = llvm::make_unique<DWARFMappedHash::MemoryTable>( + apple_types, debug_str, ".apple_types"); + if (!apple_types_table_up->IsValid()) + apple_types_table_up.reset(); + + auto apple_objc_table_up = llvm::make_unique<DWARFMappedHash::MemoryTable>( + apple_objc, debug_str, ".apple_objc"); + if (!apple_objc_table_up->IsValid()) + apple_objc_table_up.reset(); + + if (apple_names_table_up || apple_names_table_up || apple_types_table_up || + apple_objc_table_up) + return llvm::make_unique<AppleDWARFIndex>( + module, std::move(apple_names_table_up), + std::move(apple_namespaces_table_up), std::move(apple_types_table_up), + std::move(apple_objc_table_up)); + + return nullptr; +} + +void AppleDWARFIndex::GetGlobalVariables(ConstString basename, DIEArray &offsets) { + if (m_apple_names_up) + m_apple_names_up->FindByName(basename.GetStringRef(), offsets); +} + +void AppleDWARFIndex::GetGlobalVariables(const RegularExpression ®ex, + DIEArray &offsets) { + if (!m_apple_names_up) + return; + + DWARFMappedHash::DIEInfoArray hash_data; + if (m_apple_names_up->AppendAllDIEsThatMatchingRegex(regex, hash_data)) + DWARFMappedHash::ExtractDIEArray(hash_data, offsets); +} + +void AppleDWARFIndex::GetGlobalVariables(const DWARFUnit &cu, + DIEArray &offsets) { + if (!m_apple_names_up) + return; + + DWARFMappedHash::DIEInfoArray hash_data; + if (m_apple_names_up->AppendAllDIEsInRange(cu.GetOffset(), + cu.GetNextUnitOffset(), hash_data)) + DWARFMappedHash::ExtractDIEArray(hash_data, offsets); +} + +void AppleDWARFIndex::GetObjCMethods(ConstString class_name, + DIEArray &offsets) { + if (m_apple_objc_up) + m_apple_objc_up->FindByName(class_name.GetStringRef(), offsets); +} + +void AppleDWARFIndex::GetCompleteObjCClass(ConstString class_name, + bool must_be_implementation, + DIEArray &offsets) { + if (m_apple_types_up) { + m_apple_types_up->FindCompleteObjCClassByName( + class_name.GetStringRef(), offsets, must_be_implementation); + } +} + +void AppleDWARFIndex::GetTypes(ConstString name, DIEArray &offsets) { + if (m_apple_types_up) + m_apple_types_up->FindByName(name.GetStringRef(), offsets); +} + +void AppleDWARFIndex::GetTypes(const DWARFDeclContext &context, + DIEArray &offsets) { + if (!m_apple_types_up) + return; + + Log *log = LogChannelDWARF::GetLogIfAny(DWARF_LOG_TYPE_COMPLETION | + DWARF_LOG_LOOKUPS); + const bool has_tag = m_apple_types_up->GetHeader().header_data.ContainsAtom( + DWARFMappedHash::eAtomTypeTag); + const bool has_qualified_name_hash = + m_apple_types_up->GetHeader().header_data.ContainsAtom( + DWARFMappedHash::eAtomTypeQualNameHash); + const ConstString type_name(context[0].name); + const dw_tag_t tag = context[0].tag; + if (has_tag && has_qualified_name_hash) { + const char *qualified_name = context.GetQualifiedName(); + const uint32_t qualified_name_hash = llvm::djbHash(qualified_name); + if (log) + m_module.LogMessage(log, "FindByNameAndTagAndQualifiedNameHash()"); + m_apple_types_up->FindByNameAndTagAndQualifiedNameHash( + type_name.GetStringRef(), tag, qualified_name_hash, offsets); + } else if (has_tag) { + if (log) + m_module.LogMessage(log, "FindByNameAndTag()"); + m_apple_types_up->FindByNameAndTag(type_name.GetStringRef(), tag, offsets); + } else + m_apple_types_up->FindByName(type_name.GetStringRef(), offsets); +} + +void AppleDWARFIndex::GetNamespaces(ConstString name, DIEArray &offsets) { + if (m_apple_namespaces_up) + m_apple_namespaces_up->FindByName(name.GetStringRef(), offsets); +} + +void AppleDWARFIndex::GetFunctions(ConstString name, SymbolFileDWARF &dwarf, + const CompilerDeclContext &parent_decl_ctx, + uint32_t name_type_mask, + std::vector<DWARFDIE> &dies) { + DIEArray offsets; + m_apple_names_up->FindByName(name.GetStringRef(), offsets); + for (const DIERef &die_ref : offsets) { + ProcessFunctionDIE(name.GetStringRef(), die_ref, dwarf, parent_decl_ctx, + name_type_mask, dies); + } +} + +void AppleDWARFIndex::GetFunctions(const RegularExpression ®ex, + DIEArray &offsets) { + if (!m_apple_names_up) + return; + + DWARFMappedHash::DIEInfoArray hash_data; + if (m_apple_names_up->AppendAllDIEsThatMatchingRegex(regex, hash_data)) + DWARFMappedHash::ExtractDIEArray(hash_data, offsets); +} + +void AppleDWARFIndex::ReportInvalidDIERef(const DIERef &ref, + llvm::StringRef name) { + m_module.ReportErrorIfModifyDetected( + "the DWARF debug information has been modified (accelerator table had " + "bad die 0x%8.8x for '%s')\n", + ref.die_offset(), name.str().c_str()); +} + +void AppleDWARFIndex::Dump(Stream &s) { + if (m_apple_names_up) + s.PutCString(".apple_names index present\n"); + if (m_apple_namespaces_up) + s.PutCString(".apple_namespaces index present\n"); + if (m_apple_types_up) + s.PutCString(".apple_types index present\n"); + if (m_apple_objc_up) + s.PutCString(".apple_objc index present\n"); + // TODO: Dump index contents +} diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/AppleDWARFIndex.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/AppleDWARFIndex.h new file mode 100644 index 000000000000..d15d61e270b3 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/AppleDWARFIndex.h @@ -0,0 +1,62 @@ +//===-- AppleDWARFIndex.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 LLDB_APPLEDWARFINDEX_H +#define LLDB_APPLEDWARFINDEX_H + +#include "Plugins/SymbolFile/DWARF/DWARFIndex.h" +#include "Plugins/SymbolFile/DWARF/HashedNameToDIE.h" + +namespace lldb_private { +class AppleDWARFIndex : public DWARFIndex { +public: + static std::unique_ptr<AppleDWARFIndex> + Create(Module &module, DWARFDataExtractor apple_names, + DWARFDataExtractor apple_namespaces, DWARFDataExtractor apple_types, + DWARFDataExtractor apple_objc, DWARFDataExtractor debug_str); + + AppleDWARFIndex( + Module &module, std::unique_ptr<DWARFMappedHash::MemoryTable> apple_names, + std::unique_ptr<DWARFMappedHash::MemoryTable> apple_namespaces, + std::unique_ptr<DWARFMappedHash::MemoryTable> apple_types, + std::unique_ptr<DWARFMappedHash::MemoryTable> apple_objc) + : DWARFIndex(module), m_apple_names_up(std::move(apple_names)), + m_apple_namespaces_up(std::move(apple_namespaces)), + m_apple_types_up(std::move(apple_types)), + m_apple_objc_up(std::move(apple_objc)) {} + + void Preload() override {} + + void GetGlobalVariables(ConstString basename, DIEArray &offsets) override; + void GetGlobalVariables(const RegularExpression ®ex, + DIEArray &offsets) override; + void GetGlobalVariables(const DWARFUnit &cu, DIEArray &offsets) override; + void GetObjCMethods(ConstString class_name, DIEArray &offsets) override; + void GetCompleteObjCClass(ConstString class_name, bool must_be_implementation, + DIEArray &offsets) override; + void GetTypes(ConstString name, DIEArray &offsets) override; + void GetTypes(const DWARFDeclContext &context, DIEArray &offsets) override; + void GetNamespaces(ConstString name, DIEArray &offsets) override; + void GetFunctions(ConstString name, SymbolFileDWARF &dwarf, + const CompilerDeclContext &parent_decl_ctx, + uint32_t name_type_mask, + std::vector<DWARFDIE> &dies) override; + void GetFunctions(const RegularExpression ®ex, DIEArray &offsets) override; + + void ReportInvalidDIERef(const DIERef &ref, llvm::StringRef name) override; + void Dump(Stream &s) override; + +private: + std::unique_ptr<DWARFMappedHash::MemoryTable> m_apple_names_up; + std::unique_ptr<DWARFMappedHash::MemoryTable> m_apple_namespaces_up; + std::unique_ptr<DWARFMappedHash::MemoryTable> m_apple_types_up; + std::unique_ptr<DWARFMappedHash::MemoryTable> m_apple_objc_up; +}; +} // namespace lldb_private + +#endif // LLDB_APPLEDWARFINDEX_H diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DIERef.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DIERef.cpp new file mode 100644 index 000000000000..f7f2a5bf006b --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DIERef.cpp @@ -0,0 +1,18 @@ +//===-- DIERef.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 "DIERef.h" +#include "llvm/Support/Format.h" + +void llvm::format_provider<DIERef>::format(const DIERef &ref, raw_ostream &OS, + StringRef Style) { + if (ref.dwo_num()) + OS << format_hex_no_prefix(*ref.dwo_num(), 8) << "/"; + OS << (ref.section() == DIERef::DebugInfo ? "INFO" : "TYPE"); + OS << "/" << format_hex_no_prefix(ref.die_offset(), 8); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DIERef.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DIERef.h new file mode 100644 index 000000000000..5546bb7e8b86 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DIERef.h @@ -0,0 +1,63 @@ +//===-- DIERef.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 SymbolFileDWARF_DIERef_h_ +#define SymbolFileDWARF_DIERef_h_ + +#include "lldb/Core/dwarf.h" +#include "llvm/ADT/Optional.h" +#include "llvm/Support/FormatProviders.h" +#include <cassert> +#include <vector> + +/// Identifies a DWARF debug info entry within a given Module. It contains three +/// "coordinates": +/// - dwo_num: identifies the dwo file in the Module. If this field is not set, +/// the DIERef references the main file. +/// - section: identifies the section of the debug info entry in the given file: +/// debug_info or debug_types. +/// - die_offset: The offset of the debug info entry as an absolute offset from +/// the beginning of the section specified in the section field. +class DIERef { +public: + enum Section : uint8_t { DebugInfo, DebugTypes }; + + DIERef(llvm::Optional<uint32_t> dwo_num, Section section, + dw_offset_t die_offset) + : m_dwo_num(dwo_num.getValueOr(0)), m_dwo_num_valid(bool(dwo_num)), + m_section(section), m_die_offset(die_offset) { + assert(this->dwo_num() == dwo_num && "Dwo number out of range?"); + } + + llvm::Optional<uint32_t> dwo_num() const { + if (m_dwo_num_valid) + return m_dwo_num; + return llvm::None; + } + + Section section() const { return static_cast<Section>(m_section); } + + dw_offset_t die_offset() const { return m_die_offset; } + +private: + uint32_t m_dwo_num : 30; + uint32_t m_dwo_num_valid : 1; + uint32_t m_section : 1; + dw_offset_t m_die_offset; +}; +static_assert(sizeof(DIERef) == 8, ""); + +typedef std::vector<DIERef> DIEArray; + +namespace llvm { +template<> struct format_provider<DIERef> { + static void format(const DIERef &ref, raw_ostream &OS, StringRef Style); +}; +} // namespace llvm + +#endif // SymbolFileDWARF_DIERef_h_ diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParser.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParser.h new file mode 100644 index 000000000000..e7927b31b9c3 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParser.h @@ -0,0 +1,59 @@ +//===-- DWARFASTParser.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 SymbolFileDWARF_DWARFASTParser_h_ +#define SymbolFileDWARF_DWARFASTParser_h_ + +#include "DWARFDefines.h" +#include "lldb/Core/PluginInterface.h" +#include "lldb/Symbol/SymbolFile.h" +#include "lldb/Symbol/CompilerDecl.h" +#include "lldb/Symbol/CompilerDeclContext.h" + +class DWARFDIE; +namespace lldb_private { +class CompileUnit; +class ExecutionContext; +} +class SymbolFileDWARF; + +class DWARFASTParser { +public: + virtual ~DWARFASTParser() {} + + virtual lldb::TypeSP ParseTypeFromDWARF(const lldb_private::SymbolContext &sc, + const DWARFDIE &die, + lldb_private::Log *log, + bool *type_is_new_ptr) = 0; + + virtual lldb_private::Function * + ParseFunctionFromDWARF(lldb_private::CompileUnit &comp_unit, + const DWARFDIE &die) = 0; + + virtual bool + CompleteTypeFromDWARF(const DWARFDIE &die, lldb_private::Type *type, + lldb_private::CompilerType &compiler_type) = 0; + + virtual lldb_private::CompilerDecl + GetDeclForUIDFromDWARF(const DWARFDIE &die) = 0; + + virtual lldb_private::CompilerDeclContext + GetDeclContextForUIDFromDWARF(const DWARFDIE &die) = 0; + + virtual lldb_private::CompilerDeclContext + GetDeclContextContainingUIDFromDWARF(const DWARFDIE &die) = 0; + + virtual std::vector<DWARFDIE> + GetDIEForDeclContext(lldb_private::CompilerDeclContext decl_context) = 0; + + static llvm::Optional<lldb_private::SymbolFile::ArrayInfo> + ParseChildArrayInfo(const DWARFDIE &parent_die, + const lldb_private::ExecutionContext *exe_ctx = nullptr); +}; + +#endif // SymbolFileDWARF_DWARFASTParser_h_ diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp new file mode 100644 index 000000000000..b85ab54a10d3 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp @@ -0,0 +1,3885 @@ +//===-- DWARFASTParserClang.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 <stdlib.h> + +#include "DWARFASTParserClang.h" +#include "DWARFDIE.h" +#include "DWARFDebugInfo.h" +#include "DWARFDeclContext.h" +#include "DWARFDefines.h" +#include "SymbolFileDWARF.h" +#include "SymbolFileDWARFDwo.h" +#include "SymbolFileDWARFDebugMap.h" +#include "UniqueDWARFASTType.h" + +#include "Plugins/Language/ObjC/ObjCLanguage.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/Value.h" +#include "lldb/Host/Host.h" +#include "lldb/Symbol/ClangASTImporter.h" +#include "lldb/Symbol/ClangExternalASTSourceCommon.h" +#include "lldb/Symbol/ClangUtil.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolVendor.h" +#include "lldb/Symbol/TypeList.h" +#include "lldb/Symbol/TypeMap.h" +#include "lldb/Target/Language.h" +#include "lldb/Utility/LLDBAssert.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/StreamString.h" + +#include "clang/AST/CXXInheritance.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/DeclTemplate.h" + +#include <map> +#include <memory> +#include <vector> + +//#define ENABLE_DEBUG_PRINTF // COMMENT OUT THIS LINE PRIOR TO CHECKIN + +#ifdef ENABLE_DEBUG_PRINTF +#include <stdio.h> +#define DEBUG_PRINTF(fmt, ...) printf(fmt, __VA_ARGS__) +#else +#define DEBUG_PRINTF(fmt, ...) +#endif + +using namespace lldb; +using namespace lldb_private; +DWARFASTParserClang::DWARFASTParserClang(ClangASTContext &ast) + : m_ast(ast), m_die_to_decl_ctx(), m_decl_ctx_to_die() {} + +DWARFASTParserClang::~DWARFASTParserClang() {} + +static AccessType DW_ACCESS_to_AccessType(uint32_t dwarf_accessibility) { + switch (dwarf_accessibility) { + case DW_ACCESS_public: + return eAccessPublic; + case DW_ACCESS_private: + return eAccessPrivate; + case DW_ACCESS_protected: + return eAccessProtected; + default: + break; + } + return eAccessNone; +} + +static bool DeclKindIsCXXClass(clang::Decl::Kind decl_kind) { + switch (decl_kind) { + case clang::Decl::CXXRecord: + case clang::Decl::ClassTemplateSpecialization: + return true; + default: + break; + } + return false; +} + +struct BitfieldInfo { + uint64_t bit_size; + uint64_t bit_offset; + + BitfieldInfo() + : bit_size(LLDB_INVALID_ADDRESS), bit_offset(LLDB_INVALID_ADDRESS) {} + + void Clear() { + bit_size = LLDB_INVALID_ADDRESS; + bit_offset = LLDB_INVALID_ADDRESS; + } + + bool IsValid() const { + return (bit_size != LLDB_INVALID_ADDRESS) && + (bit_offset != LLDB_INVALID_ADDRESS); + } + + bool NextBitfieldOffsetIsValid(const uint64_t next_bit_offset) const { + if (IsValid()) { + // This bitfield info is valid, so any subsequent bitfields must not + // overlap and must be at a higher bit offset than any previous bitfield + // + size. + return (bit_size + bit_offset) <= next_bit_offset; + } else { + // If the this BitfieldInfo is not valid, then any offset isOK + return true; + } + } +}; + +ClangASTImporter &DWARFASTParserClang::GetClangASTImporter() { + if (!m_clang_ast_importer_up) { + m_clang_ast_importer_up.reset(new ClangASTImporter); + } + return *m_clang_ast_importer_up; +} + +/// Detect a forward declaration that is nested in a DW_TAG_module. +static bool IsClangModuleFwdDecl(const DWARFDIE &Die) { + if (!Die.GetAttributeValueAsUnsigned(DW_AT_declaration, 0)) + return false; + auto Parent = Die.GetParent(); + while (Parent.IsValid()) { + if (Parent.Tag() == DW_TAG_module) + return true; + Parent = Parent.GetParent(); + } + return false; +} + +TypeSP DWARFASTParserClang::ParseTypeFromDWO(const DWARFDIE &die, Log *log) { + ModuleSP dwo_module_sp = die.GetContainingDWOModule(); + if (!dwo_module_sp) + return TypeSP(); + + // If this type comes from a Clang module, look in the DWARF section + // of the pcm file in the module cache. Clang generates DWO skeleton + // units as breadcrumbs to find them. + std::vector<CompilerContext> decl_context; + die.GetDeclContext(decl_context); + TypeMap dwo_types; + + if (!dwo_module_sp->GetSymbolVendor()->FindTypes(decl_context, true, + dwo_types)) { + if (!IsClangModuleFwdDecl(die)) + return TypeSP(); + + // Since this this type is defined in one of the Clang modules imported by + // this symbol file, search all of them. + auto &sym_file = die.GetCU()->GetSymbolFileDWARF(); + for (const auto &name_module : sym_file.getExternalTypeModules()) { + if (!name_module.second) + continue; + SymbolVendor *sym_vendor = name_module.second->GetSymbolVendor(); + if (sym_vendor->FindTypes(decl_context, true, dwo_types)) + break; + } + } + + if (dwo_types.GetSize() != 1) + return TypeSP(); + + // We found a real definition for this type in the Clang module, so lets use + // it and cache the fact that we found a complete type for this die. + TypeSP dwo_type_sp = dwo_types.GetTypeAtIndex(0); + if (!dwo_type_sp) + return TypeSP(); + + lldb_private::CompilerType dwo_type = dwo_type_sp->GetForwardCompilerType(); + + lldb_private::CompilerType type = + GetClangASTImporter().CopyType(m_ast, dwo_type); + + if (!type) + return TypeSP(); + + SymbolFileDWARF *dwarf = die.GetDWARF(); + TypeSP type_sp(new Type( + die.GetID(), dwarf, dwo_type_sp->GetName(), dwo_type_sp->GetByteSize(), + nullptr, LLDB_INVALID_UID, Type::eEncodingInvalid, + &dwo_type_sp->GetDeclaration(), type, Type::eResolveStateForward)); + + dwarf->GetTypeList()->Insert(type_sp); + dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get(); + clang::TagDecl *tag_decl = ClangASTContext::GetAsTagDecl(type); + if (tag_decl) + LinkDeclContextToDIE(tag_decl, die); + else { + clang::DeclContext *defn_decl_ctx = GetCachedClangDeclContextForDIE(die); + if (defn_decl_ctx) + LinkDeclContextToDIE(defn_decl_ctx, die); + } + + return type_sp; +} + +static void CompleteExternalTagDeclType(ClangASTImporter &ast_importer, + clang::DeclContext *decl_ctx, + DWARFDIE die, + const char *type_name_cstr) { + auto *tag_decl_ctx = clang::dyn_cast<clang::TagDecl>(decl_ctx); + if (!tag_decl_ctx) + return; + + // If this type was not imported from an external AST, there's nothing to do. + CompilerType type = ClangASTContext::GetTypeForDecl(tag_decl_ctx); + if (!type || !ast_importer.CanImport(type)) + return; + + auto qual_type = ClangUtil::GetQualType(type); + if (!ast_importer.RequireCompleteType(qual_type)) { + die.GetDWARF()->GetObjectFile()->GetModule()->ReportError( + "Unable to complete the Decl context for DIE '%s' at offset " + "0x%8.8x.\nPlease file a bug report.", + type_name_cstr ? type_name_cstr : "", die.GetOffset()); + // We need to make the type look complete otherwise, we might crash in + // Clang when adding children. + if (ClangASTContext::StartTagDeclarationDefinition(type)) + ClangASTContext::CompleteTagDeclarationDefinition(type); + } +} + +namespace { +/// Parsed form of all attributes that are relevant for type reconstruction. +/// Some attributes are relevant for all kinds of types (declaration), while +/// others are only meaningful to a specific type (is_virtual) +struct ParsedTypeAttributes { + explicit ParsedTypeAttributes(const DWARFDIE &die); + + AccessType accessibility = eAccessNone; + bool is_artificial = false; + bool is_complete_objc_class = false; + bool is_explicit = false; + bool is_forward_declaration = false; + bool is_inline = false; + bool is_scoped_enum = false; + bool is_vector = false; + bool is_virtual = false; + clang::StorageClass storage = clang::SC_None; + const char *mangled_name = nullptr; + ConstString name; + Declaration decl; + DWARFDIE object_pointer; + DWARFFormValue abstract_origin; + DWARFFormValue containing_type; + DWARFFormValue signature; + DWARFFormValue specification; + DWARFFormValue type; + LanguageType class_language = eLanguageTypeUnknown; + llvm::Optional<uint64_t> byte_size; + size_t calling_convention = llvm::dwarf::DW_CC_normal; + uint32_t bit_stride = 0; + uint32_t byte_stride = 0; + uint32_t encoding = 0; +}; +} // namespace + +ParsedTypeAttributes::ParsedTypeAttributes(const DWARFDIE &die) { + DWARFAttributes attributes; + size_t num_attributes = die.GetAttributes(attributes); + for (size_t i = 0; i < num_attributes; ++i) { + dw_attr_t attr = attributes.AttributeAtIndex(i); + DWARFFormValue form_value; + if (!attributes.ExtractFormValueAtIndex(i, form_value)) + continue; + switch (attr) { + case DW_AT_abstract_origin: + abstract_origin = form_value; + break; + + case DW_AT_accessibility: + accessibility = DW_ACCESS_to_AccessType(form_value.Unsigned()); + break; + + case DW_AT_artificial: + is_artificial = form_value.Boolean(); + break; + + case DW_AT_bit_stride: + bit_stride = form_value.Unsigned(); + break; + + case DW_AT_byte_size: + byte_size = form_value.Unsigned(); + break; + + case DW_AT_byte_stride: + byte_stride = form_value.Unsigned(); + break; + + case DW_AT_calling_convention: + calling_convention = form_value.Unsigned(); + break; + + case DW_AT_containing_type: + containing_type = form_value; + break; + + case DW_AT_decl_file: + decl.SetFile(die.GetCU()->GetFile(form_value.Unsigned())); + break; + case DW_AT_decl_line: + decl.SetLine(form_value.Unsigned()); + break; + case DW_AT_decl_column: + decl.SetColumn(form_value.Unsigned()); + break; + + case DW_AT_declaration: + is_forward_declaration = form_value.Boolean(); + break; + + case DW_AT_encoding: + encoding = form_value.Unsigned(); + break; + + case DW_AT_enum_class: + is_scoped_enum = form_value.Boolean(); + break; + + case DW_AT_explicit: + is_explicit = form_value.Boolean(); + break; + + case DW_AT_external: + if (form_value.Unsigned()) + storage = clang::SC_Extern; + break; + + case DW_AT_inline: + is_inline = form_value.Boolean(); + break; + + case DW_AT_linkage_name: + case DW_AT_MIPS_linkage_name: + mangled_name = form_value.AsCString(); + break; + + case DW_AT_name: + name.SetCString(form_value.AsCString()); + break; + + case DW_AT_object_pointer: + object_pointer = form_value.Reference(); + break; + + case DW_AT_signature: + signature = form_value; + break; + + case DW_AT_specification: + specification = form_value; + break; + + case DW_AT_type: + type = form_value; + break; + + case DW_AT_virtuality: + is_virtual = form_value.Boolean(); + break; + + case DW_AT_APPLE_objc_complete_type: + is_complete_objc_class = form_value.Signed(); + break; + + case DW_AT_APPLE_runtime_class: + class_language = (LanguageType)form_value.Signed(); + break; + + case DW_AT_GNU_vector: + is_vector = form_value.Boolean(); + break; + } + } +} + +static std::string GetUnitName(const DWARFDIE &die) { + if (DWARFUnit *unit = die.GetCU()) + return unit->GetAbsolutePath().GetPath(); + return "<missing DWARF unit path>"; +} + +TypeSP DWARFASTParserClang::ParseTypeFromDWARF(const SymbolContext &sc, + const DWARFDIE &die, Log *log, + bool *type_is_new_ptr) { + if (type_is_new_ptr) + *type_is_new_ptr = false; + + if (!die) + return nullptr; + SymbolFileDWARF *dwarf = die.GetDWARF(); + if (log) { + DWARFDIE context_die; + clang::DeclContext *context = + GetClangDeclContextContainingDIE(die, &context_die); + + dwarf->GetObjectFile()->GetModule()->LogMessage( + log, + "SymbolFileDWARF::ParseType (die = 0x%8.8x, decl_ctx = %p (die " + "0x%8.8x)) %s name = '%s')", + die.GetOffset(), static_cast<void *>(context), context_die.GetOffset(), + die.GetTagAsCString(), die.GetName()); + } + + + Type *type_ptr = dwarf->GetDIEToType().lookup(die.GetDIE()); + if (type_ptr == DIE_IS_BEING_PARSED) + return nullptr; + if (type_ptr) + return type_ptr->shared_from_this(); + // Set a bit that lets us know that we are currently parsing this + dwarf->GetDIEToType()[die.GetDIE()] = DIE_IS_BEING_PARSED; + + ParsedTypeAttributes attrs(die); + + if (DWARFDIE signature_die = attrs.signature.Reference()) { + if (TypeSP type_sp = + ParseTypeFromDWARF(sc, signature_die, log, type_is_new_ptr)) { + dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get(); + if (clang::DeclContext *decl_ctx = + GetCachedClangDeclContextForDIE(signature_die)) + LinkDeclContextToDIE(decl_ctx, die); + return type_sp; + } + return nullptr; + } + + TypeList *type_list = dwarf->GetTypeList(); + if (type_is_new_ptr) + *type_is_new_ptr = true; + + const dw_tag_t tag = die.Tag(); + + Type::ResolveState resolve_state = Type::eResolveStateUnresolved; + + Type::EncodingDataType encoding_data_type = Type::eEncodingIsUID; + CompilerType clang_type; + + TypeSP type_sp; + LanguageType cu_language = die.GetLanguage(); + switch (tag) { + case DW_TAG_typedef: + case DW_TAG_base_type: + case DW_TAG_pointer_type: + case DW_TAG_reference_type: + case DW_TAG_rvalue_reference_type: + case DW_TAG_const_type: + case DW_TAG_restrict_type: + case DW_TAG_volatile_type: + case DW_TAG_unspecified_type: { + if (tag == DW_TAG_typedef && attrs.type.IsValid()) { + // Try to parse a typedef from the DWO file first as modules can + // contain typedef'ed structures that have no names like: + // + // typedef struct { int a; } Foo; + // + // In this case we will have a structure with no name and a typedef + // named "Foo" that points to this unnamed structure. The name in the + // typedef is the only identifier for the struct, so always try to + // get typedefs from DWO files if possible. + // + // The type_sp returned will be empty if the typedef doesn't exist in + // a DWO file, so it is cheap to call this function just to check. + // + // If we don't do this we end up creating a TypeSP that says this is + // a typedef to type 0x123 (the DW_AT_type value would be 0x123 in + // the DW_TAG_typedef), and this is the unnamed structure type. We + // will have a hard time tracking down an unnammed structure type in + // the module DWO file, so we make sure we don't get into this + // situation by always resolving typedefs from the DWO file. + const DWARFDIE encoding_die = attrs.type.Reference(); + + // First make sure that the die that this is typedef'ed to _is_ just + // a declaration (DW_AT_declaration == 1), not a full definition + // since template types can't be represented in modules since only + // concrete instances of templates are ever emitted and modules won't + // contain those + if (encoding_die && + encoding_die.GetAttributeValueAsUnsigned(DW_AT_declaration, 0) == 1) { + type_sp = ParseTypeFromDWO(die, log); + if (type_sp) + return type_sp; + } + } + + DEBUG_PRINTF("0x%8.8" PRIx64 ": %s (\"%s\") type => 0x%8.8lx\n", + die.GetID(), DW_TAG_value_to_name(tag), type_name_cstr, + encoding_uid.Reference()); + + switch (tag) { + default: + break; + + case DW_TAG_unspecified_type: + if (attrs.name == "nullptr_t" || attrs.name == "decltype(nullptr)") { + resolve_state = Type::eResolveStateFull; + clang_type = m_ast.GetBasicType(eBasicTypeNullPtr); + break; + } + // Fall through to base type below in case we can handle the type + // there... + LLVM_FALLTHROUGH; + + case DW_TAG_base_type: + resolve_state = Type::eResolveStateFull; + clang_type = m_ast.GetBuiltinTypeForDWARFEncodingAndBitSize( + attrs.name.GetCString(), attrs.encoding, + attrs.byte_size.getValueOr(0) * 8); + break; + + case DW_TAG_pointer_type: + encoding_data_type = Type::eEncodingIsPointerUID; + break; + case DW_TAG_reference_type: + encoding_data_type = Type::eEncodingIsLValueReferenceUID; + break; + case DW_TAG_rvalue_reference_type: + encoding_data_type = Type::eEncodingIsRValueReferenceUID; + break; + case DW_TAG_typedef: + encoding_data_type = Type::eEncodingIsTypedefUID; + break; + case DW_TAG_const_type: + encoding_data_type = Type::eEncodingIsConstUID; + break; + case DW_TAG_restrict_type: + encoding_data_type = Type::eEncodingIsRestrictUID; + break; + case DW_TAG_volatile_type: + encoding_data_type = Type::eEncodingIsVolatileUID; + break; + } + + if (!clang_type && (encoding_data_type == Type::eEncodingIsPointerUID || + encoding_data_type == Type::eEncodingIsTypedefUID)) { + if (tag == DW_TAG_pointer_type) { + DWARFDIE target_die = die.GetReferencedDIE(DW_AT_type); + + if (target_die.GetAttributeValueAsUnsigned(DW_AT_APPLE_block, 0)) { + // Blocks have a __FuncPtr inside them which is a pointer to a + // function of the proper type. + + for (DWARFDIE child_die = target_die.GetFirstChild(); + child_die.IsValid(); child_die = child_die.GetSibling()) { + if (!strcmp(child_die.GetAttributeValueAsString(DW_AT_name, ""), + "__FuncPtr")) { + DWARFDIE function_pointer_type = + child_die.GetReferencedDIE(DW_AT_type); + + if (function_pointer_type) { + DWARFDIE function_type = + function_pointer_type.GetReferencedDIE(DW_AT_type); + + bool function_type_is_new_pointer; + TypeSP lldb_function_type_sp = ParseTypeFromDWARF( + sc, function_type, log, &function_type_is_new_pointer); + + if (lldb_function_type_sp) { + clang_type = m_ast.CreateBlockPointerType( + lldb_function_type_sp->GetForwardCompilerType()); + encoding_data_type = Type::eEncodingIsUID; + attrs.type.Clear(); + resolve_state = Type::eResolveStateFull; + } + } + + break; + } + } + } + } + + if (cu_language == eLanguageTypeObjC || + cu_language == eLanguageTypeObjC_plus_plus) { + if (attrs.name) { + static ConstString g_objc_type_name_id("id"); + static ConstString g_objc_type_name_Class("Class"); + static ConstString g_objc_type_name_selector("SEL"); + + if (attrs.name == g_objc_type_name_id) { + if (log) + dwarf->GetObjectFile()->GetModule()->LogMessage( + log, + "SymbolFileDWARF::ParseType (die = 0x%8.8x) %s '%s' " + "is Objective-C 'id' built-in type.", + die.GetOffset(), die.GetTagAsCString(), die.GetName()); + clang_type = m_ast.GetBasicType(eBasicTypeObjCID); + encoding_data_type = Type::eEncodingIsUID; + attrs.type.Clear(); + resolve_state = Type::eResolveStateFull; + + } else if (attrs.name == g_objc_type_name_Class) { + if (log) + dwarf->GetObjectFile()->GetModule()->LogMessage( + log, + "SymbolFileDWARF::ParseType (die = 0x%8.8x) %s '%s' " + "is Objective-C 'Class' built-in type.", + die.GetOffset(), die.GetTagAsCString(), die.GetName()); + clang_type = m_ast.GetBasicType(eBasicTypeObjCClass); + encoding_data_type = Type::eEncodingIsUID; + attrs.type.Clear(); + resolve_state = Type::eResolveStateFull; + } else if (attrs.name == g_objc_type_name_selector) { + if (log) + dwarf->GetObjectFile()->GetModule()->LogMessage( + log, + "SymbolFileDWARF::ParseType (die = 0x%8.8x) %s '%s' " + "is Objective-C 'selector' built-in type.", + die.GetOffset(), die.GetTagAsCString(), die.GetName()); + clang_type = m_ast.GetBasicType(eBasicTypeObjCSel); + encoding_data_type = Type::eEncodingIsUID; + attrs.type.Clear(); + resolve_state = Type::eResolveStateFull; + } + } else if (encoding_data_type == Type::eEncodingIsPointerUID && + attrs.type.IsValid()) { + // Clang sometimes erroneously emits id as objc_object*. In that + // case we fix up the type to "id". + + const DWARFDIE encoding_die = attrs.type.Reference(); + + if (encoding_die && encoding_die.Tag() == DW_TAG_structure_type) { + if (const char *struct_name = encoding_die.GetName()) { + if (!strcmp(struct_name, "objc_object")) { + if (log) + dwarf->GetObjectFile()->GetModule()->LogMessage( + log, + "SymbolFileDWARF::ParseType (die = 0x%8.8x) %s " + "'%s' is 'objc_object*', which we overrode to " + "'id'.", + die.GetOffset(), die.GetTagAsCString(), die.GetName()); + clang_type = m_ast.GetBasicType(eBasicTypeObjCID); + encoding_data_type = Type::eEncodingIsUID; + attrs.type.Clear(); + resolve_state = Type::eResolveStateFull; + } + } + } + } + } + } + + type_sp = std::make_shared<Type>( + die.GetID(), dwarf, attrs.name, attrs.byte_size, nullptr, + dwarf->GetUID(attrs.type.Reference()), encoding_data_type, &attrs.decl, + clang_type, resolve_state); + + dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get(); + } break; + + case DW_TAG_structure_type: + case DW_TAG_union_type: + case DW_TAG_class_type: { + // UniqueDWARFASTType is large, so don't create a local variables on + // the stack, put it on the heap. This function is often called + // recursively and clang isn't good and sharing the stack space for + // variables in different blocks. + std::unique_ptr<UniqueDWARFASTType> unique_ast_entry_up( + new UniqueDWARFASTType()); + + ConstString unique_typename(attrs.name); + Declaration unique_decl(attrs.decl); + + if (attrs.name) { + if (Language::LanguageIsCPlusPlus(cu_language)) { + // For C++, we rely solely upon the one definition rule that says + // only one thing can exist at a given decl context. We ignore the + // file and line that things are declared on. + std::string qualified_name; + if (die.GetQualifiedName(qualified_name)) + unique_typename = ConstString(qualified_name); + unique_decl.Clear(); + } + + if (dwarf->GetUniqueDWARFASTTypeMap().Find( + unique_typename, die, unique_decl, attrs.byte_size.getValueOr(-1), + *unique_ast_entry_up)) { + type_sp = unique_ast_entry_up->m_type_sp; + if (type_sp) { + dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get(); + return type_sp; + } + } + } + + DEBUG_PRINTF("0x%8.8" PRIx64 ": %s (\"%s\")\n", die.GetID(), + DW_TAG_value_to_name(tag), type_name_cstr); + + int tag_decl_kind = -1; + AccessType default_accessibility = eAccessNone; + if (tag == DW_TAG_structure_type) { + tag_decl_kind = clang::TTK_Struct; + default_accessibility = eAccessPublic; + } else if (tag == DW_TAG_union_type) { + tag_decl_kind = clang::TTK_Union; + default_accessibility = eAccessPublic; + } else if (tag == DW_TAG_class_type) { + tag_decl_kind = clang::TTK_Class; + default_accessibility = eAccessPrivate; + } + + if (attrs.byte_size && *attrs.byte_size == 0 && attrs.name && + !die.HasChildren() && cu_language == eLanguageTypeObjC) { + // Work around an issue with clang at the moment where forward + // declarations for objective C classes are emitted as: + // DW_TAG_structure_type [2] + // DW_AT_name( "ForwardObjcClass" ) + // DW_AT_byte_size( 0x00 ) + // DW_AT_decl_file( "..." ) + // DW_AT_decl_line( 1 ) + // + // Note that there is no DW_AT_declaration and there are no children, + // and the byte size is zero. + attrs.is_forward_declaration = true; + } + + if (attrs.class_language == eLanguageTypeObjC || + attrs.class_language == eLanguageTypeObjC_plus_plus) { + if (!attrs.is_complete_objc_class && + die.Supports_DW_AT_APPLE_objc_complete_type()) { + // We have a valid eSymbolTypeObjCClass class symbol whose name + // matches the current objective C class that we are trying to find + // and this DIE isn't the complete definition (we checked + // is_complete_objc_class above and know it is false), so the real + // definition is in here somewhere + type_sp = + dwarf->FindCompleteObjCDefinitionTypeForDIE(die, attrs.name, true); + + if (!type_sp) { + SymbolFileDWARFDebugMap *debug_map_symfile = + dwarf->GetDebugMapSymfile(); + if (debug_map_symfile) { + // We weren't able to find a full declaration in this DWARF, + // see if we have a declaration anywhere else... + type_sp = debug_map_symfile->FindCompleteObjCDefinitionTypeForDIE( + die, attrs.name, true); + } + } + + if (type_sp) { + if (log) { + dwarf->GetObjectFile()->GetModule()->LogMessage( + log, + "SymbolFileDWARF(%p) - 0x%8.8x: %s type \"%s\" is an " + "incomplete objc type, complete type is 0x%8.8" PRIx64, + static_cast<void *>(this), die.GetOffset(), + DW_TAG_value_to_name(tag), attrs.name.GetCString(), + type_sp->GetID()); + } + + // We found a real definition for this type elsewhere so lets use + // it and cache the fact that we found a complete type for this + // die + dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get(); + return type_sp; + } + } + } + + if (attrs.is_forward_declaration) { + // We have a forward declaration to a type and we need to try and + // find a full declaration. We look in the current type index just in + // case we have a forward declaration followed by an actual + // declarations in the DWARF. If this fails, we need to look + // elsewhere... + if (log) { + dwarf->GetObjectFile()->GetModule()->LogMessage( + log, + "SymbolFileDWARF(%p) - 0x%8.8x: %s type \"%s\" is a " + "forward declaration, trying to find complete type", + static_cast<void *>(this), die.GetOffset(), + DW_TAG_value_to_name(tag), attrs.name.GetCString()); + } + + // See if the type comes from a DWO module and if so, track down that + // type. + type_sp = ParseTypeFromDWO(die, log); + if (type_sp) + return type_sp; + + DWARFDeclContext die_decl_ctx; + die.GetDWARFDeclContext(die_decl_ctx); + + // type_sp = FindDefinitionTypeForDIE (dwarf_cu, die, + // type_name_const_str); + type_sp = dwarf->FindDefinitionTypeForDWARFDeclContext(die_decl_ctx); + + if (!type_sp) { + SymbolFileDWARFDebugMap *debug_map_symfile = + dwarf->GetDebugMapSymfile(); + if (debug_map_symfile) { + // We weren't able to find a full declaration in this DWARF, see + // if we have a declaration anywhere else... + type_sp = debug_map_symfile->FindDefinitionTypeForDWARFDeclContext( + die_decl_ctx); + } + } + + if (type_sp) { + if (log) { + dwarf->GetObjectFile()->GetModule()->LogMessage( + log, + "SymbolFileDWARF(%p) - 0x%8.8x: %s type \"%s\" is a " + "forward declaration, complete type is 0x%8.8" PRIx64, + static_cast<void *>(this), die.GetOffset(), + DW_TAG_value_to_name(tag), attrs.name.GetCString(), + type_sp->GetID()); + } + + // We found a real definition for this type elsewhere so lets use + // it and cache the fact that we found a complete type for this die + dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get(); + clang::DeclContext *defn_decl_ctx = + GetCachedClangDeclContextForDIE(dwarf->GetDIE(type_sp->GetID())); + if (defn_decl_ctx) + LinkDeclContextToDIE(defn_decl_ctx, die); + return type_sp; + } + } + assert(tag_decl_kind != -1); + bool clang_type_was_created = false; + clang_type.SetCompilerType( + &m_ast, dwarf->GetForwardDeclDieToClangType().lookup(die.GetDIE())); + if (!clang_type) { + clang::DeclContext *decl_ctx = + GetClangDeclContextContainingDIE(die, nullptr); + + // If your decl context is a record that was imported from another + // AST context (in the gmodules case), we need to make sure the type + // backing the Decl is complete before adding children to it. This is + // not an issue in the non-gmodules case because the debug info will + // always contain a full definition of parent types in that case. + CompleteExternalTagDeclType(GetClangASTImporter(), decl_ctx, die, + attrs.name.GetCString()); + + if (attrs.accessibility == eAccessNone && decl_ctx) { + // Check the decl context that contains this class/struct/union. If + // it is a class we must give it an accessibility. + const clang::Decl::Kind containing_decl_kind = decl_ctx->getDeclKind(); + if (DeclKindIsCXXClass(containing_decl_kind)) + attrs.accessibility = default_accessibility; + } + + ClangASTMetadata metadata; + metadata.SetUserID(die.GetID()); + metadata.SetIsDynamicCXXType(dwarf->ClassOrStructIsVirtual(die)); + + if (attrs.name.GetStringRef().contains('<')) { + ClangASTContext::TemplateParameterInfos template_param_infos; + if (ParseTemplateParameterInfos(die, template_param_infos)) { + clang::ClassTemplateDecl *class_template_decl = + m_ast.ParseClassTemplateDecl(decl_ctx, attrs.accessibility, + attrs.name.GetCString(), + tag_decl_kind, template_param_infos); + if (!class_template_decl) { + if (log) { + dwarf->GetObjectFile()->GetModule()->LogMessage( + log, + "SymbolFileDWARF(%p) - 0x%8.8x: %s type \"%s\" " + "clang::ClassTemplateDecl failed to return a decl.", + static_cast<void *>(this), die.GetOffset(), + DW_TAG_value_to_name(tag), attrs.name.GetCString()); + } + return TypeSP(); + } + + clang::ClassTemplateSpecializationDecl *class_specialization_decl = + m_ast.CreateClassTemplateSpecializationDecl( + decl_ctx, class_template_decl, tag_decl_kind, + template_param_infos); + clang_type = m_ast.CreateClassTemplateSpecializationType( + class_specialization_decl); + clang_type_was_created = true; + + m_ast.SetMetadata(class_template_decl, metadata); + m_ast.SetMetadata(class_specialization_decl, metadata); + } + } + + if (!clang_type_was_created) { + clang_type_was_created = true; + clang_type = m_ast.CreateRecordType( + decl_ctx, attrs.accessibility, attrs.name.GetCString(), + tag_decl_kind, attrs.class_language, &metadata); + } + } + + // Store a forward declaration to this class type in case any + // parameters in any class methods need it for the clang types for + // function prototypes. + LinkDeclContextToDIE(m_ast.GetDeclContextForType(clang_type), die); + type_sp = std::make_shared<Type>(die.GetID(), dwarf, attrs.name, + attrs.byte_size, nullptr, LLDB_INVALID_UID, + Type::eEncodingIsUID, &attrs.decl, clang_type, + Type::eResolveStateForward); + + type_sp->SetIsCompleteObjCClass(attrs.is_complete_objc_class); + + // Add our type to the unique type map so we don't end up creating many + // copies of the same type over and over in the ASTContext for our + // module + unique_ast_entry_up->m_type_sp = type_sp; + unique_ast_entry_up->m_die = die; + unique_ast_entry_up->m_declaration = unique_decl; + unique_ast_entry_up->m_byte_size = attrs.byte_size.getValueOr(0); + dwarf->GetUniqueDWARFASTTypeMap().Insert(unique_typename, + *unique_ast_entry_up); + + if (attrs.is_forward_declaration && die.HasChildren()) { + // Check to see if the DIE actually has a definition, some version of + // GCC will + // emit DIEs with DW_AT_declaration set to true, but yet still have + // subprogram, members, or inheritance, so we can't trust it + DWARFDIE child_die = die.GetFirstChild(); + while (child_die) { + switch (child_die.Tag()) { + case DW_TAG_inheritance: + case DW_TAG_subprogram: + case DW_TAG_member: + case DW_TAG_APPLE_property: + case DW_TAG_class_type: + case DW_TAG_structure_type: + case DW_TAG_enumeration_type: + case DW_TAG_typedef: + case DW_TAG_union_type: + child_die.Clear(); + attrs.is_forward_declaration = false; + break; + default: + child_die = child_die.GetSibling(); + break; + } + } + } + + if (!attrs.is_forward_declaration) { + // Always start the definition for a class type so that if the class + // has child classes or types that require the class to be created + // for use as their decl contexts the class will be ready to accept + // these child definitions. + if (!die.HasChildren()) { + // No children for this struct/union/class, lets finish it + if (ClangASTContext::StartTagDeclarationDefinition(clang_type)) { + ClangASTContext::CompleteTagDeclarationDefinition(clang_type); + } else { + dwarf->GetObjectFile()->GetModule()->ReportError( + "DWARF DIE at 0x%8.8x named \"%s\" was not able to start its " + "definition.\nPlease file a bug and attach the file at the " + "start of this error message", + die.GetOffset(), attrs.name.GetCString()); + } + + if (tag == DW_TAG_structure_type) // this only applies in C + { + clang::RecordDecl *record_decl = + ClangASTContext::GetAsRecordDecl(clang_type); + + if (record_decl) { + GetClangASTImporter().InsertRecordDecl( + record_decl, ClangASTImporter::LayoutInfo()); + } + } + } else if (clang_type_was_created) { + // Start the definition if the class is not objective C since the + // underlying decls respond to isCompleteDefinition(). Objective + // C decls don't respond to isCompleteDefinition() so we can't + // start the declaration definition right away. For C++ + // class/union/structs we want to start the definition in case the + // class is needed as the declaration context for a contained class + // or type without the need to complete that type.. + + if (attrs.class_language != eLanguageTypeObjC && + attrs.class_language != eLanguageTypeObjC_plus_plus) + ClangASTContext::StartTagDeclarationDefinition(clang_type); + + // Leave this as a forward declaration until we need to know the + // details of the type. lldb_private::Type will automatically call + // the SymbolFile virtual function + // "SymbolFileDWARF::CompleteType(Type *)" When the definition + // needs to be defined. + assert(!dwarf->GetForwardDeclClangTypeToDie().count( + ClangUtil::RemoveFastQualifiers(clang_type) + .GetOpaqueQualType()) && + "Type already in the forward declaration map!"); + // Can't assume m_ast.GetSymbolFile() is actually a + // SymbolFileDWARF, it can be a SymbolFileDWARFDebugMap for Apple + // binaries. + dwarf->GetForwardDeclDieToClangType()[die.GetDIE()] = + clang_type.GetOpaqueQualType(); + dwarf->GetForwardDeclClangTypeToDie() + [ClangUtil::RemoveFastQualifiers(clang_type).GetOpaqueQualType()] = + die.GetID(); + m_ast.SetHasExternalStorage(clang_type.GetOpaqueQualType(), true); + } + } + + // If we made a clang type, set the trivial abi if applicable: We only + // do this for pass by value - which implies the Trivial ABI. There + // isn't a way to assert that something that would normally be pass by + // value is pass by reference, so we ignore that attribute if set. + if (attrs.calling_convention == llvm::dwarf::DW_CC_pass_by_value) { + clang::CXXRecordDecl *record_decl = + m_ast.GetAsCXXRecordDecl(clang_type.GetOpaqueQualType()); + if (record_decl) { + record_decl->setHasTrivialSpecialMemberForCall(); + } + } + + if (attrs.calling_convention == llvm::dwarf::DW_CC_pass_by_reference) { + clang::CXXRecordDecl *record_decl = + m_ast.GetAsCXXRecordDecl(clang_type.GetOpaqueQualType()); + if (record_decl) + record_decl->setArgPassingRestrictions( + clang::RecordDecl::APK_CannotPassInRegs); + } + + } break; + + case DW_TAG_enumeration_type: { + if (attrs.is_forward_declaration) { + type_sp = ParseTypeFromDWO(die, log); + if (type_sp) + return type_sp; + + DWARFDeclContext die_decl_ctx; + die.GetDWARFDeclContext(die_decl_ctx); + + type_sp = dwarf->FindDefinitionTypeForDWARFDeclContext(die_decl_ctx); + + if (!type_sp) { + SymbolFileDWARFDebugMap *debug_map_symfile = + dwarf->GetDebugMapSymfile(); + if (debug_map_symfile) { + // We weren't able to find a full declaration in this DWARF, + // see if we have a declaration anywhere else... + type_sp = debug_map_symfile->FindDefinitionTypeForDWARFDeclContext( + die_decl_ctx); + } + } + + if (type_sp) { + if (log) { + dwarf->GetObjectFile()->GetModule()->LogMessage( + log, + "SymbolFileDWARF(%p) - 0x%8.8x: %s type \"%s\" is a " + "forward declaration, complete type is 0x%8.8" PRIx64, + static_cast<void *>(this), die.GetOffset(), + DW_TAG_value_to_name(tag), attrs.name.GetCString(), + type_sp->GetID()); + } + + // We found a real definition for this type elsewhere so lets use + // it and cache the fact that we found a complete type for this + // die + dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get(); + clang::DeclContext *defn_decl_ctx = + GetCachedClangDeclContextForDIE(dwarf->GetDIE(type_sp->GetID())); + if (defn_decl_ctx) + LinkDeclContextToDIE(defn_decl_ctx, die); + return type_sp; + } + } + DEBUG_PRINTF("0x%8.8" PRIx64 ": %s (\"%s\")\n", die.GetID(), + DW_TAG_value_to_name(tag), type_name_cstr); + + CompilerType enumerator_clang_type; + clang_type.SetCompilerType( + &m_ast, dwarf->GetForwardDeclDieToClangType().lookup(die.GetDIE())); + if (!clang_type) { + if (attrs.type.IsValid()) { + Type *enumerator_type = + dwarf->ResolveTypeUID(attrs.type.Reference(), true); + if (enumerator_type) + enumerator_clang_type = enumerator_type->GetFullCompilerType(); + } + + if (!enumerator_clang_type) { + if (attrs.byte_size) { + enumerator_clang_type = + m_ast.GetBuiltinTypeForDWARFEncodingAndBitSize( + NULL, DW_ATE_signed, *attrs.byte_size * 8); + } else { + enumerator_clang_type = m_ast.GetBasicType(eBasicTypeInt); + } + } + + clang_type = m_ast.CreateEnumerationType( + attrs.name.GetCString(), + GetClangDeclContextContainingDIE(die, nullptr), attrs.decl, + enumerator_clang_type, attrs.is_scoped_enum); + } else { + enumerator_clang_type = + m_ast.GetEnumerationIntegerType(clang_type.GetOpaqueQualType()); + } + + LinkDeclContextToDIE(ClangASTContext::GetDeclContextForType(clang_type), + die); + + type_sp = std::make_shared<Type>( + die.GetID(), dwarf, attrs.name, attrs.byte_size, nullptr, + dwarf->GetUID(attrs.type.Reference()), Type::eEncodingIsUID, + &attrs.decl, clang_type, Type::eResolveStateForward); + + if (ClangASTContext::StartTagDeclarationDefinition(clang_type)) { + if (die.HasChildren()) { + bool is_signed = false; + enumerator_clang_type.IsIntegerType(is_signed); + ParseChildEnumerators(clang_type, is_signed, + type_sp->GetByteSize().getValueOr(0), die); + } + ClangASTContext::CompleteTagDeclarationDefinition(clang_type); + } else { + dwarf->GetObjectFile()->GetModule()->ReportError( + "DWARF DIE at 0x%8.8x named \"%s\" was not able to start its " + "definition.\nPlease file a bug and attach the file at the " + "start of this error message", + die.GetOffset(), attrs.name.GetCString()); + } + } break; + + case DW_TAG_inlined_subroutine: + case DW_TAG_subprogram: + case DW_TAG_subroutine_type: { + bool is_variadic = false; + bool is_static = false; + bool has_template_params = false; + + unsigned type_quals = 0; + + std::string object_pointer_name; + if (attrs.object_pointer) { + const char *object_pointer_name_cstr = attrs.object_pointer.GetName(); + if (object_pointer_name_cstr) + object_pointer_name = object_pointer_name_cstr; + } + + DEBUG_PRINTF("0x%8.8" PRIx64 ": %s (\"%s\")\n", die.GetID(), + DW_TAG_value_to_name(tag), type_name_cstr); + + CompilerType return_clang_type; + Type *func_type = NULL; + + if (attrs.type.IsValid()) + func_type = dwarf->ResolveTypeUID(attrs.type.Reference(), true); + + if (func_type) + return_clang_type = func_type->GetForwardCompilerType(); + else + return_clang_type = m_ast.GetBasicType(eBasicTypeVoid); + + std::vector<CompilerType> function_param_types; + std::vector<clang::ParmVarDecl *> function_param_decls; + + // Parse the function children for the parameters + + DWARFDIE decl_ctx_die; + clang::DeclContext *containing_decl_ctx = + GetClangDeclContextContainingDIE(die, &decl_ctx_die); + const clang::Decl::Kind containing_decl_kind = + containing_decl_ctx->getDeclKind(); + + bool is_cxx_method = DeclKindIsCXXClass(containing_decl_kind); + // Start off static. This will be set to false in + // ParseChildParameters(...) if we find a "this" parameters as the + // first parameter + if (is_cxx_method) { + is_static = true; + } + + if (die.HasChildren()) { + bool skip_artificial = true; + ParseChildParameters(containing_decl_ctx, die, skip_artificial, is_static, + is_variadic, has_template_params, + function_param_types, function_param_decls, + type_quals); + } + + bool ignore_containing_context = false; + // Check for templatized class member functions. If we had any + // DW_TAG_template_type_parameter or DW_TAG_template_value_parameter + // the DW_TAG_subprogram DIE, then we can't let this become a method in + // a class. Why? Because templatized functions are only emitted if one + // of the templatized methods is used in the current compile unit and + // we will end up with classes that may or may not include these member + // functions and this means one class won't match another class + // definition and it affects our ability to use a class in the clang + // expression parser. So for the greater good, we currently must not + // allow any template member functions in a class definition. + if (is_cxx_method && has_template_params) { + ignore_containing_context = true; + is_cxx_method = false; + } + + // clang_type will get the function prototype clang type after this + // call + clang_type = m_ast.CreateFunctionType( + return_clang_type, function_param_types.data(), + function_param_types.size(), is_variadic, type_quals); + + if (attrs.name) { + bool type_handled = false; + if (tag == DW_TAG_subprogram || tag == DW_TAG_inlined_subroutine) { + ObjCLanguage::MethodName objc_method(attrs.name.GetStringRef(), true); + if (objc_method.IsValid(true)) { + CompilerType class_opaque_type; + ConstString class_name(objc_method.GetClassName()); + if (class_name) { + TypeSP complete_objc_class_type_sp( + dwarf->FindCompleteObjCDefinitionTypeForDIE(DWARFDIE(), + class_name, false)); + + if (complete_objc_class_type_sp) { + CompilerType type_clang_forward_type = + complete_objc_class_type_sp->GetForwardCompilerType(); + if (ClangASTContext::IsObjCObjectOrInterfaceType( + type_clang_forward_type)) + class_opaque_type = type_clang_forward_type; + } + } + + if (class_opaque_type) { + // If accessibility isn't set to anything valid, assume public + // for now... + if (attrs.accessibility == eAccessNone) + attrs.accessibility = eAccessPublic; + + clang::ObjCMethodDecl *objc_method_decl = + m_ast.AddMethodToObjCObjectType( + class_opaque_type, attrs.name.GetCString(), clang_type, + attrs.accessibility, attrs.is_artificial, is_variadic); + type_handled = objc_method_decl != NULL; + if (type_handled) { + LinkDeclContextToDIE( + ClangASTContext::GetAsDeclContext(objc_method_decl), die); + m_ast.SetMetadataAsUserID(objc_method_decl, die.GetID()); + } else { + dwarf->GetObjectFile()->GetModule()->ReportError( + "{0x%8.8x}: invalid Objective-C method 0x%4.4x (%s), " + "please file a bug and attach the file at the start of " + "this error message", + die.GetOffset(), tag, DW_TAG_value_to_name(tag)); + } + } + } else if (is_cxx_method) { + // Look at the parent of this DIE and see if is is a class or + // struct and see if this is actually a C++ method + Type *class_type = dwarf->ResolveType(decl_ctx_die); + if (class_type) { + bool alternate_defn = false; + if (class_type->GetID() != decl_ctx_die.GetID() || + decl_ctx_die.GetContainingDWOModuleDIE()) { + alternate_defn = true; + + // We uniqued the parent class of this function to another + // class so we now need to associate all dies under + // "decl_ctx_die" to DIEs in the DIE for "class_type"... + DWARFDIE class_type_die = dwarf->GetDIE(class_type->GetID()); + + if (class_type_die) { + std::vector<DWARFDIE> failures; + + CopyUniqueClassMethodTypes(decl_ctx_die, class_type_die, + class_type, failures); + + // FIXME do something with these failures that's smarter + // than + // just dropping them on the ground. Unfortunately classes + // don't like having stuff added to them after their + // definitions are complete... + + type_ptr = dwarf->GetDIEToType()[die.GetDIE()]; + if (type_ptr && type_ptr != DIE_IS_BEING_PARSED) { + type_sp = type_ptr->shared_from_this(); + break; + } + } + } + + if (attrs.specification.IsValid()) { + // We have a specification which we are going to base our + // function prototype off of, so we need this type to be + // completed so that the m_die_to_decl_ctx for the method in + // the specification has a valid clang decl context. + class_type->GetForwardCompilerType(); + // If we have a specification, then the function type should + // have been made with the specification and not with this + // die. + DWARFDIE spec_die = attrs.specification.Reference(); + clang::DeclContext *spec_clang_decl_ctx = + GetClangDeclContextForDIE(spec_die); + if (spec_clang_decl_ctx) { + LinkDeclContextToDIE(spec_clang_decl_ctx, die); + } else { + dwarf->GetObjectFile()->GetModule()->ReportWarning( + "0x%8.8" PRIx64 ": DW_AT_specification(0x%8.8x" + ") has no decl\n", + die.GetID(), spec_die.GetOffset()); + } + type_handled = true; + } else if (attrs.abstract_origin.IsValid()) { + // We have a specification which we are going to base our + // function prototype off of, so we need this type to be + // completed so that the m_die_to_decl_ctx for the method in + // the abstract origin has a valid clang decl context. + class_type->GetForwardCompilerType(); + + DWARFDIE abs_die = attrs.abstract_origin.Reference(); + clang::DeclContext *abs_clang_decl_ctx = + GetClangDeclContextForDIE(abs_die); + if (abs_clang_decl_ctx) { + LinkDeclContextToDIE(abs_clang_decl_ctx, die); + } else { + dwarf->GetObjectFile()->GetModule()->ReportWarning( + "0x%8.8" PRIx64 ": DW_AT_abstract_origin(0x%8.8x" + ") has no decl\n", + die.GetID(), abs_die.GetOffset()); + } + type_handled = true; + } else { + CompilerType class_opaque_type = + class_type->GetForwardCompilerType(); + if (ClangASTContext::IsCXXClassType(class_opaque_type)) { + if (class_opaque_type.IsBeingDefined() || alternate_defn) { + if (!is_static && !die.HasChildren()) { + // We have a C++ member function with no children (this + // pointer!) and clang will get mad if we try and make + // a function that isn't well formed in the DWARF, so + // we will just skip it... + type_handled = true; + } else { + bool add_method = true; + if (alternate_defn) { + // If an alternate definition for the class exists, + // then add the method only if an equivalent is not + // already present. + clang::CXXRecordDecl *record_decl = + m_ast.GetAsCXXRecordDecl( + class_opaque_type.GetOpaqueQualType()); + if (record_decl) { + for (auto method_iter = record_decl->method_begin(); + method_iter != record_decl->method_end(); + method_iter++) { + clang::CXXMethodDecl *method_decl = *method_iter; + if (method_decl->getNameInfo().getAsString() == + attrs.name.GetStringRef()) { + if (method_decl->getType() == + ClangUtil::GetQualType(clang_type)) { + add_method = false; + LinkDeclContextToDIE( + ClangASTContext::GetAsDeclContext( + method_decl), + die); + type_handled = true; + + break; + } + } + } + } + } + + if (add_method) { + llvm::PrettyStackTraceFormat stack_trace( + "SymbolFileDWARF::ParseType() is adding a method " + "%s to class %s in DIE 0x%8.8" PRIx64 " from %s", + attrs.name.GetCString(), + class_type->GetName().GetCString(), die.GetID(), + dwarf->GetObjectFile() + ->GetFileSpec() + .GetPath() + .c_str()); + + const bool is_attr_used = false; + // Neither GCC 4.2 nor clang++ currently set a valid + // accessibility in the DWARF for C++ methods... + // Default to public for now... + if (attrs.accessibility == eAccessNone) + attrs.accessibility = eAccessPublic; + + clang::CXXMethodDecl *cxx_method_decl = + m_ast.AddMethodToCXXRecordType( + class_opaque_type.GetOpaqueQualType(), + attrs.name.GetCString(), attrs.mangled_name, + clang_type, attrs.accessibility, attrs.is_virtual, + is_static, attrs.is_inline, attrs.is_explicit, + is_attr_used, attrs.is_artificial); + + type_handled = cxx_method_decl != NULL; + + if (type_handled) { + LinkDeclContextToDIE( + ClangASTContext::GetAsDeclContext(cxx_method_decl), + die); + + ClangASTMetadata metadata; + metadata.SetUserID(die.GetID()); + + if (!object_pointer_name.empty()) { + metadata.SetObjectPtrName( + object_pointer_name.c_str()); + if (log) + log->Printf( + "Setting object pointer name: %s on method " + "object %p.\n", + object_pointer_name.c_str(), + static_cast<void *>(cxx_method_decl)); + } + m_ast.SetMetadata(cxx_method_decl, metadata); + } else { + ignore_containing_context = true; + } + } + } + } else { + // We were asked to parse the type for a method in a + // class, yet the class hasn't been asked to complete + // itself through the clang::ExternalASTSource protocol, + // so we need to just have the class complete itself and + // do things the right way, then our + // DIE should then have an entry in the + // dwarf->GetDIEToType() map. First + // we need to modify the dwarf->GetDIEToType() so it + // doesn't think we are trying to parse this DIE + // anymore... + dwarf->GetDIEToType()[die.GetDIE()] = NULL; + + // Now we get the full type to force our class type to + // complete itself using the clang::ExternalASTSource + // protocol which will parse all base classes and all + // methods (including the method for this DIE). + class_type->GetFullCompilerType(); + + // The type for this DIE should have been filled in the + // function call above + type_ptr = dwarf->GetDIEToType()[die.GetDIE()]; + if (type_ptr && type_ptr != DIE_IS_BEING_PARSED) { + type_sp = type_ptr->shared_from_this(); + break; + } + + // FIXME This is fixing some even uglier behavior but we + // really need to + // uniq the methods of each class as well as the class + // itself. <rdar://problem/11240464> + type_handled = true; + } + } + } + } + } + } + + if (!type_handled) { + clang::FunctionDecl *function_decl = nullptr; + clang::FunctionDecl *template_function_decl = nullptr; + + if (attrs.abstract_origin.IsValid()) { + DWARFDIE abs_die = attrs.abstract_origin.Reference(); + + if (dwarf->ResolveType(abs_die)) { + function_decl = llvm::dyn_cast_or_null<clang::FunctionDecl>( + GetCachedClangDeclContextForDIE(abs_die)); + + if (function_decl) { + LinkDeclContextToDIE(function_decl, die); + } + } + } + + if (!function_decl) { + // We just have a function that isn't part of a class + function_decl = m_ast.CreateFunctionDeclaration( + ignore_containing_context ? m_ast.GetTranslationUnitDecl() + : containing_decl_ctx, + attrs.name.GetCString(), clang_type, attrs.storage, + attrs.is_inline); + + if (has_template_params) { + ClangASTContext::TemplateParameterInfos template_param_infos; + ParseTemplateParameterInfos(die, template_param_infos); + template_function_decl = m_ast.CreateFunctionDeclaration( + ignore_containing_context ? m_ast.GetTranslationUnitDecl() + : containing_decl_ctx, + attrs.name.GetCString(), clang_type, attrs.storage, + attrs.is_inline); + clang::FunctionTemplateDecl *func_template_decl = + m_ast.CreateFunctionTemplateDecl( + containing_decl_ctx, template_function_decl, + attrs.name.GetCString(), template_param_infos); + m_ast.CreateFunctionTemplateSpecializationInfo( + function_decl, func_template_decl, template_param_infos); + } + + lldbassert(function_decl); + + if (function_decl) { + LinkDeclContextToDIE(function_decl, die); + + if (!function_param_decls.empty()) { + m_ast.SetFunctionParameters(function_decl, + &function_param_decls.front(), + function_param_decls.size()); + if (template_function_decl) + m_ast.SetFunctionParameters(template_function_decl, + &function_param_decls.front(), + function_param_decls.size()); + } + + ClangASTMetadata metadata; + metadata.SetUserID(die.GetID()); + + if (!object_pointer_name.empty()) { + metadata.SetObjectPtrName(object_pointer_name.c_str()); + if (log) + log->Printf("Setting object pointer name: %s on function " + "object %p.", + object_pointer_name.c_str(), + static_cast<void *>(function_decl)); + } + m_ast.SetMetadata(function_decl, metadata); + } + } + } + } + type_sp = std::make_shared<Type>( + die.GetID(), dwarf, attrs.name, llvm::None, nullptr, LLDB_INVALID_UID, + Type::eEncodingIsUID, &attrs.decl, clang_type, Type::eResolveStateFull); + assert(type_sp.get()); + } break; + + case DW_TAG_array_type: { + DEBUG_PRINTF("0x%8.8" PRIx64 ": %s (\"%s\")\n", die.GetID(), + DW_TAG_value_to_name(tag), type_name_cstr); + + DWARFDIE type_die = attrs.type.Reference(); + Type *element_type = dwarf->ResolveTypeUID(type_die, true); + + if (element_type) { + auto array_info = ParseChildArrayInfo(die); + if (array_info) { + attrs.byte_stride = array_info->byte_stride; + attrs.bit_stride = array_info->bit_stride; + } + if (attrs.byte_stride == 0 && attrs.bit_stride == 0) + attrs.byte_stride = element_type->GetByteSize().getValueOr(0); + CompilerType array_element_type = element_type->GetForwardCompilerType(); + + if (ClangASTContext::IsCXXClassType(array_element_type) && + !array_element_type.GetCompleteType()) { + ModuleSP module_sp = die.GetModule(); + if (module_sp) { + if (die.GetCU()->GetProducer() == eProducerClang) + module_sp->ReportError( + "DWARF DW_TAG_array_type DIE at 0x%8.8x has a " + "class/union/struct element type DIE 0x%8.8x that is a " + "forward declaration, not a complete definition.\nTry " + "compiling the source file with -fstandalone-debug or " + "disable -gmodules", + die.GetOffset(), type_die.GetOffset()); + else + module_sp->ReportError( + "DWARF DW_TAG_array_type DIE at 0x%8.8x has a " + "class/union/struct element type DIE 0x%8.8x that is a " + "forward declaration, not a complete definition.\nPlease " + "file a bug against the compiler and include the " + "preprocessed output for %s", + die.GetOffset(), type_die.GetOffset(), + GetUnitName(die).c_str()); + } + + // We have no choice other than to pretend that the element class + // type is complete. If we don't do this, clang will crash when + // trying to layout the class. Since we provide layout + // assistance, all ivars in this class and other classes will be + // fine, this is the best we can do short of crashing. + if (ClangASTContext::StartTagDeclarationDefinition( + array_element_type)) { + ClangASTContext::CompleteTagDeclarationDefinition(array_element_type); + } else { + module_sp->ReportError("DWARF DIE at 0x%8.8x was not able to " + "start its definition.\nPlease file a " + "bug and attach the file at the start " + "of this error message", + type_die.GetOffset()); + } + } + + uint64_t array_element_bit_stride = + attrs.byte_stride * 8 + attrs.bit_stride; + if (array_info && array_info->element_orders.size() > 0) { + uint64_t num_elements = 0; + auto end = array_info->element_orders.rend(); + for (auto pos = array_info->element_orders.rbegin(); pos != end; + ++pos) { + num_elements = *pos; + clang_type = m_ast.CreateArrayType(array_element_type, num_elements, + attrs.is_vector); + array_element_type = clang_type; + array_element_bit_stride = + num_elements ? array_element_bit_stride * num_elements + : array_element_bit_stride; + } + } else { + clang_type = m_ast.CreateArrayType(array_element_type, 0, attrs.is_vector); + } + ConstString empty_name; + type_sp = std::make_shared<Type>( + die.GetID(), dwarf, empty_name, array_element_bit_stride / 8, nullptr, + dwarf->GetUID(type_die), Type::eEncodingIsUID, &attrs.decl, + clang_type, Type::eResolveStateFull); + type_sp->SetEncodingType(element_type); + m_ast.SetMetadataAsUserID(clang_type.GetOpaqueQualType(), die.GetID()); + } + } break; + + case DW_TAG_ptr_to_member_type: { + Type *pointee_type = dwarf->ResolveTypeUID(attrs.type.Reference(), true); + Type *class_type = + dwarf->ResolveTypeUID(attrs.containing_type.Reference(), true); + + CompilerType pointee_clang_type = pointee_type->GetForwardCompilerType(); + CompilerType class_clang_type = class_type->GetLayoutCompilerType(); + + clang_type = ClangASTContext::CreateMemberPointerType(class_clang_type, + pointee_clang_type); + + if (llvm::Optional<uint64_t> clang_type_size = + clang_type.GetByteSize(nullptr)) { + type_sp = std::make_shared<Type>( + die.GetID(), dwarf, attrs.name, *clang_type_size, nullptr, + LLDB_INVALID_UID, Type::eEncodingIsUID, nullptr, clang_type, + Type::eResolveStateForward); + } + + break; + } + default: + dwarf->GetObjectFile()->GetModule()->ReportError( + "{0x%8.8x}: unhandled type tag 0x%4.4x (%s), please file a bug and " + "attach the file at the start of this error message", + die.GetOffset(), tag, DW_TAG_value_to_name(tag)); + break; + } + + if (type_sp.get()) { + DWARFDIE sc_parent_die = SymbolFileDWARF::GetParentSymbolContextDIE(die); + dw_tag_t sc_parent_tag = sc_parent_die.Tag(); + + SymbolContextScope *symbol_context_scope = NULL; + if (sc_parent_tag == DW_TAG_compile_unit || + sc_parent_tag == DW_TAG_partial_unit) { + symbol_context_scope = sc.comp_unit; + } else if (sc.function != NULL && sc_parent_die) { + symbol_context_scope = + sc.function->GetBlock(true).FindBlockByID(sc_parent_die.GetID()); + if (symbol_context_scope == NULL) + symbol_context_scope = sc.function; + } else + symbol_context_scope = sc.module_sp.get(); + + if (symbol_context_scope != NULL) { + type_sp->SetSymbolContextScope(symbol_context_scope); + } + + // We are ready to put this type into the uniqued list up at the module + // level + type_list->Insert(type_sp); + + dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get(); + } + return type_sp; +} + +// DWARF parsing functions + +class DWARFASTParserClang::DelayedAddObjCClassProperty { +public: + DelayedAddObjCClassProperty( + const CompilerType &class_opaque_type, const char *property_name, + const CompilerType &property_opaque_type, // The property type is only + // required if you don't have an + // ivar decl + clang::ObjCIvarDecl *ivar_decl, const char *property_setter_name, + const char *property_getter_name, uint32_t property_attributes, + const ClangASTMetadata *metadata) + : m_class_opaque_type(class_opaque_type), m_property_name(property_name), + m_property_opaque_type(property_opaque_type), m_ivar_decl(ivar_decl), + m_property_setter_name(property_setter_name), + m_property_getter_name(property_getter_name), + m_property_attributes(property_attributes) { + if (metadata != nullptr) { + m_metadata_up.reset(new ClangASTMetadata()); + *m_metadata_up = *metadata; + } + } + + DelayedAddObjCClassProperty(const DelayedAddObjCClassProperty &rhs) { + *this = rhs; + } + + DelayedAddObjCClassProperty & + operator=(const DelayedAddObjCClassProperty &rhs) { + m_class_opaque_type = rhs.m_class_opaque_type; + m_property_name = rhs.m_property_name; + m_property_opaque_type = rhs.m_property_opaque_type; + m_ivar_decl = rhs.m_ivar_decl; + m_property_setter_name = rhs.m_property_setter_name; + m_property_getter_name = rhs.m_property_getter_name; + m_property_attributes = rhs.m_property_attributes; + + if (rhs.m_metadata_up) { + m_metadata_up.reset(new ClangASTMetadata()); + *m_metadata_up = *rhs.m_metadata_up; + } + return *this; + } + + bool Finalize() { + return ClangASTContext::AddObjCClassProperty( + m_class_opaque_type, m_property_name, m_property_opaque_type, + m_ivar_decl, m_property_setter_name, m_property_getter_name, + m_property_attributes, m_metadata_up.get()); + } + +private: + CompilerType m_class_opaque_type; + const char *m_property_name; + CompilerType m_property_opaque_type; + clang::ObjCIvarDecl *m_ivar_decl; + const char *m_property_setter_name; + const char *m_property_getter_name; + uint32_t m_property_attributes; + std::unique_ptr<ClangASTMetadata> m_metadata_up; +}; + +bool DWARFASTParserClang::ParseTemplateDIE( + const DWARFDIE &die, + ClangASTContext::TemplateParameterInfos &template_param_infos) { + const dw_tag_t tag = die.Tag(); + bool is_template_template_argument = false; + + switch (tag) { + case DW_TAG_GNU_template_parameter_pack: { + template_param_infos.packed_args.reset( + new ClangASTContext::TemplateParameterInfos); + for (DWARFDIE child_die = die.GetFirstChild(); child_die.IsValid(); + child_die = child_die.GetSibling()) { + if (!ParseTemplateDIE(child_die, *template_param_infos.packed_args)) + return false; + } + if (const char *name = die.GetName()) { + template_param_infos.pack_name = name; + } + return true; + } + case DW_TAG_GNU_template_template_param: + is_template_template_argument = true; + LLVM_FALLTHROUGH; + case DW_TAG_template_type_parameter: + case DW_TAG_template_value_parameter: { + DWARFAttributes attributes; + const size_t num_attributes = die.GetAttributes(attributes); + const char *name = nullptr; + const char *template_name = nullptr; + CompilerType clang_type; + uint64_t uval64 = 0; + bool uval64_valid = false; + if (num_attributes > 0) { + DWARFFormValue form_value; + for (size_t i = 0; i < num_attributes; ++i) { + const dw_attr_t attr = attributes.AttributeAtIndex(i); + + switch (attr) { + case DW_AT_name: + if (attributes.ExtractFormValueAtIndex(i, form_value)) + name = form_value.AsCString(); + break; + + case DW_AT_GNU_template_name: + if (attributes.ExtractFormValueAtIndex(i, form_value)) + template_name = form_value.AsCString(); + break; + + case DW_AT_type: + if (attributes.ExtractFormValueAtIndex(i, form_value)) { + Type *lldb_type = die.ResolveTypeUID(form_value.Reference()); + if (lldb_type) + clang_type = lldb_type->GetForwardCompilerType(); + } + break; + + case DW_AT_const_value: + if (attributes.ExtractFormValueAtIndex(i, form_value)) { + uval64_valid = true; + uval64 = form_value.Unsigned(); + } + break; + default: + break; + } + } + + clang::ASTContext *ast = m_ast.getASTContext(); + if (!clang_type) + clang_type = m_ast.GetBasicType(eBasicTypeVoid); + + if (!is_template_template_argument) { + bool is_signed = false; + if (name && name[0]) + template_param_infos.names.push_back(name); + else + template_param_infos.names.push_back(NULL); + + // Get the signed value for any integer or enumeration if available + clang_type.IsIntegerOrEnumerationType(is_signed); + + if (tag == DW_TAG_template_value_parameter && uval64_valid) { + llvm::Optional<uint64_t> size = clang_type.GetBitSize(nullptr); + if (!size) + return false; + llvm::APInt apint(*size, uval64, is_signed); + template_param_infos.args.push_back( + clang::TemplateArgument(*ast, llvm::APSInt(apint, !is_signed), + ClangUtil::GetQualType(clang_type))); + } else { + template_param_infos.args.push_back( + clang::TemplateArgument(ClangUtil::GetQualType(clang_type))); + } + } else { + auto *tplt_type = m_ast.CreateTemplateTemplateParmDecl(template_name); + template_param_infos.names.push_back(name); + template_param_infos.args.push_back( + clang::TemplateArgument(clang::TemplateName(tplt_type))); + } + } + } + return true; + + default: + break; + } + return false; +} + +bool DWARFASTParserClang::ParseTemplateParameterInfos( + const DWARFDIE &parent_die, + ClangASTContext::TemplateParameterInfos &template_param_infos) { + + if (!parent_die) + return false; + + for (DWARFDIE die = parent_die.GetFirstChild(); die.IsValid(); + die = die.GetSibling()) { + const dw_tag_t tag = die.Tag(); + + switch (tag) { + case DW_TAG_template_type_parameter: + case DW_TAG_template_value_parameter: + case DW_TAG_GNU_template_parameter_pack: + case DW_TAG_GNU_template_template_param: + ParseTemplateDIE(die, template_param_infos); + break; + + default: + break; + } + } + if (template_param_infos.args.empty()) + return false; + return template_param_infos.args.size() == template_param_infos.names.size(); +} + +bool DWARFASTParserClang::CompleteTypeFromDWARF(const DWARFDIE &die, + lldb_private::Type *type, + CompilerType &clang_type) { + SymbolFileDWARF *dwarf = die.GetDWARF(); + + std::lock_guard<std::recursive_mutex> guard( + dwarf->GetObjectFile()->GetModule()->GetMutex()); + + // Disable external storage for this type so we don't get anymore + // clang::ExternalASTSource queries for this type. + m_ast.SetHasExternalStorage(clang_type.GetOpaqueQualType(), false); + + if (!die) + return false; + +#if defined LLDB_CONFIGURATION_DEBUG + // For debugging purposes, the LLDB_DWARF_DONT_COMPLETE_TYPENAMES environment + // variable can be set with one or more typenames separated by ';' + // characters. This will cause this function to not complete any types whose + // names match. + // + // Examples of setting this environment variable: + // + // LLDB_DWARF_DONT_COMPLETE_TYPENAMES=Foo + // LLDB_DWARF_DONT_COMPLETE_TYPENAMES=Foo;Bar;Baz + const char *dont_complete_typenames_cstr = + getenv("LLDB_DWARF_DONT_COMPLETE_TYPENAMES"); + if (dont_complete_typenames_cstr && dont_complete_typenames_cstr[0]) { + const char *die_name = die.GetName(); + if (die_name && die_name[0]) { + const char *match = strstr(dont_complete_typenames_cstr, die_name); + if (match) { + size_t die_name_length = strlen(die_name); + while (match) { + const char separator_char = ';'; + const char next_char = match[die_name_length]; + if (next_char == '\0' || next_char == separator_char) { + if (match == dont_complete_typenames_cstr || + match[-1] == separator_char) + return false; + } + match = strstr(match + 1, die_name); + } + } + } + } +#endif + + const dw_tag_t tag = die.Tag(); + + Log *log = + nullptr; // (LogChannelDWARF::GetLogIfAny(DWARF_LOG_DEBUG_INFO|DWARF_LOG_TYPE_COMPLETION)); + if (log) + dwarf->GetObjectFile()->GetModule()->LogMessageVerboseBacktrace( + log, "0x%8.8" PRIx64 ": %s '%s' resolving forward declaration...", + die.GetID(), die.GetTagAsCString(), type->GetName().AsCString()); + assert(clang_type); + DWARFAttributes attributes; + switch (tag) { + case DW_TAG_structure_type: + case DW_TAG_union_type: + case DW_TAG_class_type: { + ClangASTImporter::LayoutInfo layout_info; + + { + if (die.HasChildren()) { + LanguageType class_language = eLanguageTypeUnknown; + if (ClangASTContext::IsObjCObjectOrInterfaceType(clang_type)) { + class_language = eLanguageTypeObjC; + // For objective C we don't start the definition when the class is + // created. + ClangASTContext::StartTagDeclarationDefinition(clang_type); + } + + int tag_decl_kind = -1; + AccessType default_accessibility = eAccessNone; + if (tag == DW_TAG_structure_type) { + tag_decl_kind = clang::TTK_Struct; + default_accessibility = eAccessPublic; + } else if (tag == DW_TAG_union_type) { + tag_decl_kind = clang::TTK_Union; + default_accessibility = eAccessPublic; + } else if (tag == DW_TAG_class_type) { + tag_decl_kind = clang::TTK_Class; + default_accessibility = eAccessPrivate; + } + + std::vector<std::unique_ptr<clang::CXXBaseSpecifier>> bases; + std::vector<int> member_accessibilities; + bool is_a_class = false; + // Parse members and base classes first + std::vector<DWARFDIE> member_function_dies; + + DelayedPropertyList delayed_properties; + ParseChildMembers(die, clang_type, class_language, bases, + member_accessibilities, member_function_dies, + delayed_properties, default_accessibility, is_a_class, + layout_info); + + // Now parse any methods if there were any... + for (const DWARFDIE &die : member_function_dies) + dwarf->ResolveType(die); + + if (class_language == eLanguageTypeObjC) { + ConstString class_name(clang_type.GetTypeName()); + if (class_name) { + DIEArray method_die_offsets; + dwarf->GetObjCMethodDIEOffsets(class_name, method_die_offsets); + + if (!method_die_offsets.empty()) { + DWARFDebugInfo *debug_info = dwarf->DebugInfo(); + + const size_t num_matches = method_die_offsets.size(); + for (size_t i = 0; i < num_matches; ++i) { + const DIERef &die_ref = method_die_offsets[i]; + DWARFDIE method_die = debug_info->GetDIE(die_ref); + + if (method_die) + method_die.ResolveType(); + } + } + + for (DelayedPropertyList::iterator pi = delayed_properties.begin(), + pe = delayed_properties.end(); + pi != pe; ++pi) + pi->Finalize(); + } + } + + // If we have a DW_TAG_structure_type instead of a DW_TAG_class_type we + // need to tell the clang type it is actually a class. + if (class_language != eLanguageTypeObjC) { + if (is_a_class && tag_decl_kind != clang::TTK_Class) + m_ast.SetTagTypeKind(ClangUtil::GetQualType(clang_type), + clang::TTK_Class); + } + + // Since DW_TAG_structure_type gets used for both classes and + // structures, we may need to set any DW_TAG_member fields to have a + // "private" access if none was specified. When we parsed the child + // members we tracked that actual accessibility value for each + // DW_TAG_member in the "member_accessibilities" array. If the value + // for the member is zero, then it was set to the + // "default_accessibility" which for structs was "public". Below we + // correct this by setting any fields to "private" that weren't + // correctly set. + if (is_a_class && !member_accessibilities.empty()) { + // This is a class and all members that didn't have their access + // specified are private. + m_ast.SetDefaultAccessForRecordFields( + m_ast.GetAsRecordDecl(clang_type), eAccessPrivate, + &member_accessibilities.front(), member_accessibilities.size()); + } + + if (!bases.empty()) { + // Make sure all base classes refer to complete types and not forward + // declarations. If we don't do this, clang will crash with an + // assertion in the call to clang_type.TransferBaseClasses() + for (const auto &base_class : bases) { + clang::TypeSourceInfo *type_source_info = + base_class->getTypeSourceInfo(); + if (type_source_info) { + CompilerType base_class_type( + &m_ast, type_source_info->getType().getAsOpaquePtr()); + if (!base_class_type.GetCompleteType()) { + auto module = dwarf->GetObjectFile()->GetModule(); + module->ReportError(":: Class '%s' has a base class '%s' which " + "does not have a complete definition.", + die.GetName(), + base_class_type.GetTypeName().GetCString()); + if (die.GetCU()->GetProducer() == eProducerClang) + module->ReportError(":: Try compiling the source file with " + "-fstandalone-debug."); + + // We have no choice other than to pretend that the base class + // is complete. If we don't do this, clang will crash when we + // call setBases() inside of + // "clang_type.TransferBaseClasses()" below. Since we + // provide layout assistance, all ivars in this class and other + // classes will be fine, this is the best we can do short of + // crashing. + if (ClangASTContext::StartTagDeclarationDefinition( + base_class_type)) { + ClangASTContext::CompleteTagDeclarationDefinition( + base_class_type); + } + } + } + } + + m_ast.TransferBaseClasses(clang_type.GetOpaqueQualType(), + std::move(bases)); + } + } + } + + m_ast.AddMethodOverridesForCXXRecordType(clang_type.GetOpaqueQualType()); + ClangASTContext::BuildIndirectFields(clang_type); + ClangASTContext::CompleteTagDeclarationDefinition(clang_type); + + if (!layout_info.field_offsets.empty() || + !layout_info.base_offsets.empty() || + !layout_info.vbase_offsets.empty()) { + if (type) + layout_info.bit_size = type->GetByteSize().getValueOr(0) * 8; + if (layout_info.bit_size == 0) + layout_info.bit_size = + die.GetAttributeValueAsUnsigned(DW_AT_byte_size, 0) * 8; + + clang::CXXRecordDecl *record_decl = + m_ast.GetAsCXXRecordDecl(clang_type.GetOpaqueQualType()); + if (record_decl) { + if (log) { + ModuleSP module_sp = dwarf->GetObjectFile()->GetModule(); + + if (module_sp) { + module_sp->LogMessage( + log, + "ClangASTContext::CompleteTypeFromDWARF (clang_type = %p) " + "caching layout info for record_decl = %p, bit_size = %" PRIu64 + ", alignment = %" PRIu64 + ", field_offsets[%u], base_offsets[%u], vbase_offsets[%u])", + static_cast<void *>(clang_type.GetOpaqueQualType()), + static_cast<void *>(record_decl), layout_info.bit_size, + layout_info.alignment, + static_cast<uint32_t>(layout_info.field_offsets.size()), + static_cast<uint32_t>(layout_info.base_offsets.size()), + static_cast<uint32_t>(layout_info.vbase_offsets.size())); + + uint32_t idx; + { + llvm::DenseMap<const clang::FieldDecl *, uint64_t>::const_iterator + pos, + end = layout_info.field_offsets.end(); + for (idx = 0, pos = layout_info.field_offsets.begin(); pos != end; + ++pos, ++idx) { + module_sp->LogMessage( + log, "ClangASTContext::CompleteTypeFromDWARF (clang_type = " + "%p) field[%u] = { bit_offset=%u, name='%s' }", + static_cast<void *>(clang_type.GetOpaqueQualType()), idx, + static_cast<uint32_t>(pos->second), + pos->first->getNameAsString().c_str()); + } + } + + { + llvm::DenseMap<const clang::CXXRecordDecl *, + clang::CharUnits>::const_iterator base_pos, + base_end = layout_info.base_offsets.end(); + for (idx = 0, base_pos = layout_info.base_offsets.begin(); + base_pos != base_end; ++base_pos, ++idx) { + module_sp->LogMessage( + log, "ClangASTContext::CompleteTypeFromDWARF (clang_type = " + "%p) base[%u] = { byte_offset=%u, name='%s' }", + clang_type.GetOpaqueQualType(), idx, + (uint32_t)base_pos->second.getQuantity(), + base_pos->first->getNameAsString().c_str()); + } + } + { + llvm::DenseMap<const clang::CXXRecordDecl *, + clang::CharUnits>::const_iterator vbase_pos, + vbase_end = layout_info.vbase_offsets.end(); + for (idx = 0, vbase_pos = layout_info.vbase_offsets.begin(); + vbase_pos != vbase_end; ++vbase_pos, ++idx) { + module_sp->LogMessage( + log, "ClangASTContext::CompleteTypeFromDWARF (clang_type = " + "%p) vbase[%u] = { byte_offset=%u, name='%s' }", + static_cast<void *>(clang_type.GetOpaqueQualType()), idx, + static_cast<uint32_t>(vbase_pos->second.getQuantity()), + vbase_pos->first->getNameAsString().c_str()); + } + } + } + } + GetClangASTImporter().InsertRecordDecl(record_decl, layout_info); + } + } + } + + return (bool)clang_type; + + case DW_TAG_enumeration_type: + if (ClangASTContext::StartTagDeclarationDefinition(clang_type)) { + if (die.HasChildren()) { + bool is_signed = false; + clang_type.IsIntegerType(is_signed); + ParseChildEnumerators(clang_type, is_signed, + type->GetByteSize().getValueOr(0), die); + } + ClangASTContext::CompleteTagDeclarationDefinition(clang_type); + } + return (bool)clang_type; + + default: + assert(false && "not a forward clang type decl!"); + break; + } + + return false; +} + +std::vector<DWARFDIE> DWARFASTParserClang::GetDIEForDeclContext( + lldb_private::CompilerDeclContext decl_context) { + std::vector<DWARFDIE> result; + for (auto it = m_decl_ctx_to_die.find( + (clang::DeclContext *)decl_context.GetOpaqueDeclContext()); + it != m_decl_ctx_to_die.end(); it++) + result.push_back(it->second); + return result; +} + +CompilerDecl DWARFASTParserClang::GetDeclForUIDFromDWARF(const DWARFDIE &die) { + clang::Decl *clang_decl = GetClangDeclForDIE(die); + if (clang_decl != nullptr) + return CompilerDecl(&m_ast, clang_decl); + return CompilerDecl(); +} + +CompilerDeclContext +DWARFASTParserClang::GetDeclContextForUIDFromDWARF(const DWARFDIE &die) { + clang::DeclContext *clang_decl_ctx = GetClangDeclContextForDIE(die); + if (clang_decl_ctx) + return CompilerDeclContext(&m_ast, clang_decl_ctx); + return CompilerDeclContext(); +} + +CompilerDeclContext +DWARFASTParserClang::GetDeclContextContainingUIDFromDWARF(const DWARFDIE &die) { + clang::DeclContext *clang_decl_ctx = + GetClangDeclContextContainingDIE(die, nullptr); + if (clang_decl_ctx) + return CompilerDeclContext(&m_ast, clang_decl_ctx); + return CompilerDeclContext(); +} + +size_t DWARFASTParserClang::ParseChildEnumerators( + lldb_private::CompilerType &clang_type, bool is_signed, + uint32_t enumerator_byte_size, const DWARFDIE &parent_die) { + if (!parent_die) + return 0; + + size_t enumerators_added = 0; + + for (DWARFDIE die = parent_die.GetFirstChild(); die.IsValid(); + die = die.GetSibling()) { + const dw_tag_t tag = die.Tag(); + if (tag == DW_TAG_enumerator) { + DWARFAttributes attributes; + const size_t num_child_attributes = die.GetAttributes(attributes); + if (num_child_attributes > 0) { + const char *name = nullptr; + bool got_value = false; + int64_t enum_value = 0; + Declaration decl; + + uint32_t i; + for (i = 0; i < num_child_attributes; ++i) { + const dw_attr_t attr = attributes.AttributeAtIndex(i); + DWARFFormValue form_value; + if (attributes.ExtractFormValueAtIndex(i, form_value)) { + switch (attr) { + case DW_AT_const_value: + got_value = true; + if (is_signed) + enum_value = form_value.Signed(); + else + enum_value = form_value.Unsigned(); + break; + + case DW_AT_name: + name = form_value.AsCString(); + break; + + case DW_AT_description: + default: + case DW_AT_decl_file: + decl.SetFile(die.GetCU()->GetFile(form_value.Unsigned())); + break; + case DW_AT_decl_line: + decl.SetLine(form_value.Unsigned()); + break; + case DW_AT_decl_column: + decl.SetColumn(form_value.Unsigned()); + break; + case DW_AT_sibling: + break; + } + } + } + + if (name && name[0] && got_value) { + m_ast.AddEnumerationValueToEnumerationType( + clang_type, decl, name, enum_value, enumerator_byte_size * 8); + ++enumerators_added; + } + } + } + } + return enumerators_added; +} + +#if defined(LLDB_CONFIGURATION_DEBUG) || defined(LLDB_CONFIGURATION_RELEASE) + +class DIEStack { +public: + void Push(const DWARFDIE &die) { m_dies.push_back(die); } + + void LogDIEs(Log *log) { + StreamString log_strm; + const size_t n = m_dies.size(); + log_strm.Printf("DIEStack[%" PRIu64 "]:\n", (uint64_t)n); + for (size_t i = 0; i < n; i++) { + std::string qualified_name; + const DWARFDIE &die = m_dies[i]; + die.GetQualifiedName(qualified_name); + log_strm.Printf("[%" PRIu64 "] 0x%8.8x: %s name='%s'\n", (uint64_t)i, + die.GetOffset(), die.GetTagAsCString(), + qualified_name.c_str()); + } + log->PutCString(log_strm.GetData()); + } + void Pop() { m_dies.pop_back(); } + + class ScopedPopper { + public: + ScopedPopper(DIEStack &die_stack) + : m_die_stack(die_stack), m_valid(false) {} + + void Push(const DWARFDIE &die) { + m_valid = true; + m_die_stack.Push(die); + } + + ~ScopedPopper() { + if (m_valid) + m_die_stack.Pop(); + } + + protected: + DIEStack &m_die_stack; + bool m_valid; + }; + +protected: + typedef std::vector<DWARFDIE> Stack; + Stack m_dies; +}; +#endif + +Function *DWARFASTParserClang::ParseFunctionFromDWARF(CompileUnit &comp_unit, + const DWARFDIE &die) { + DWARFRangeList func_ranges; + const char *name = nullptr; + const char *mangled = nullptr; + int decl_file = 0; + int decl_line = 0; + int decl_column = 0; + int call_file = 0; + int call_line = 0; + int call_column = 0; + DWARFExpression frame_base; + + const dw_tag_t tag = die.Tag(); + + if (tag != DW_TAG_subprogram) + return nullptr; + + if (die.GetDIENamesAndRanges(name, mangled, func_ranges, decl_file, decl_line, + decl_column, call_file, call_line, call_column, + &frame_base)) { + + // Union of all ranges in the function DIE (if the function is + // discontiguous) + AddressRange func_range; + lldb::addr_t lowest_func_addr = func_ranges.GetMinRangeBase(0); + lldb::addr_t highest_func_addr = func_ranges.GetMaxRangeEnd(0); + if (lowest_func_addr != LLDB_INVALID_ADDRESS && + lowest_func_addr <= highest_func_addr) { + ModuleSP module_sp(die.GetModule()); + func_range.GetBaseAddress().ResolveAddressUsingFileSections( + lowest_func_addr, module_sp->GetSectionList()); + if (func_range.GetBaseAddress().IsValid()) + func_range.SetByteSize(highest_func_addr - lowest_func_addr); + } + + if (func_range.GetBaseAddress().IsValid()) { + Mangled func_name; + if (mangled) + func_name.SetValue(ConstString(mangled), true); + else if ((die.GetParent().Tag() == DW_TAG_compile_unit || + die.GetParent().Tag() == DW_TAG_partial_unit) && + Language::LanguageIsCPlusPlus(die.GetLanguage()) && + !Language::LanguageIsObjC(die.GetLanguage()) && name && + strcmp(name, "main") != 0) { + // If the mangled name is not present in the DWARF, generate the + // demangled name using the decl context. We skip if the function is + // "main" as its name is never mangled. + bool is_static = false; + bool is_variadic = false; + bool has_template_params = false; + unsigned type_quals = 0; + std::vector<CompilerType> param_types; + std::vector<clang::ParmVarDecl *> param_decls; + DWARFDeclContext decl_ctx; + StreamString sstr; + + die.GetDWARFDeclContext(decl_ctx); + sstr << decl_ctx.GetQualifiedName(); + + clang::DeclContext *containing_decl_ctx = + GetClangDeclContextContainingDIE(die, nullptr); + ParseChildParameters(containing_decl_ctx, die, true, is_static, + is_variadic, has_template_params, param_types, + param_decls, type_quals); + sstr << "("; + for (size_t i = 0; i < param_types.size(); i++) { + if (i > 0) + sstr << ", "; + sstr << param_types[i].GetTypeName(); + } + if (is_variadic) + sstr << ", ..."; + sstr << ")"; + if (type_quals & clang::Qualifiers::Const) + sstr << " const"; + + func_name.SetValue(ConstString(sstr.GetString()), false); + } else + func_name.SetValue(ConstString(name), false); + + FunctionSP func_sp; + std::unique_ptr<Declaration> decl_up; + if (decl_file != 0 || decl_line != 0 || decl_column != 0) + decl_up.reset(new Declaration(die.GetCU()->GetFile(decl_file), + decl_line, decl_column)); + + SymbolFileDWARF *dwarf = die.GetDWARF(); + // Supply the type _only_ if it has already been parsed + Type *func_type = dwarf->GetDIEToType().lookup(die.GetDIE()); + + assert(func_type == nullptr || func_type != DIE_IS_BEING_PARSED); + + if (dwarf->FixupAddress(func_range.GetBaseAddress())) { + const user_id_t func_user_id = die.GetID(); + func_sp = + std::make_shared<Function>(&comp_unit, + func_user_id, // UserID is the DIE offset + func_user_id, func_name, func_type, + func_range); // first address range + + if (func_sp.get() != nullptr) { + if (frame_base.IsValid()) + func_sp->GetFrameBaseExpression() = frame_base; + comp_unit.AddFunction(func_sp); + return func_sp.get(); + } + } + } + } + return nullptr; +} + +bool DWARFASTParserClang::ParseChildMembers( + const DWARFDIE &parent_die, CompilerType &class_clang_type, + const LanguageType class_language, + std::vector<std::unique_ptr<clang::CXXBaseSpecifier>> &base_classes, + std::vector<int> &member_accessibilities, + std::vector<DWARFDIE> &member_function_dies, + DelayedPropertyList &delayed_properties, AccessType &default_accessibility, + bool &is_a_class, ClangASTImporter::LayoutInfo &layout_info) { + if (!parent_die) + return false; + + // Get the parent byte size so we can verify any members will fit + const uint64_t parent_byte_size = + parent_die.GetAttributeValueAsUnsigned(DW_AT_byte_size, UINT64_MAX); + const uint64_t parent_bit_size = + parent_byte_size == UINT64_MAX ? UINT64_MAX : parent_byte_size * 8; + + uint32_t member_idx = 0; + BitfieldInfo last_field_info; + + ModuleSP module_sp = parent_die.GetDWARF()->GetObjectFile()->GetModule(); + ClangASTContext *ast = + llvm::dyn_cast_or_null<ClangASTContext>(class_clang_type.GetTypeSystem()); + if (ast == nullptr) + return false; + + for (DWARFDIE die = parent_die.GetFirstChild(); die.IsValid(); + die = die.GetSibling()) { + dw_tag_t tag = die.Tag(); + + switch (tag) { + case DW_TAG_member: + case DW_TAG_APPLE_property: { + DWARFAttributes attributes; + const size_t num_attributes = die.GetAttributes(attributes); + if (num_attributes > 0) { + const char *name = nullptr; + const char *prop_name = nullptr; + const char *prop_getter_name = nullptr; + const char *prop_setter_name = nullptr; + uint32_t prop_attributes = 0; + + bool is_artificial = false; + DWARFFormValue encoding_form; + AccessType accessibility = eAccessNone; + uint32_t member_byte_offset = + (parent_die.Tag() == DW_TAG_union_type) ? 0 : UINT32_MAX; + llvm::Optional<uint64_t> byte_size; + int64_t bit_offset = 0; + uint64_t data_bit_offset = UINT64_MAX; + size_t bit_size = 0; + bool is_external = + false; // On DW_TAG_members, this means the member is static + uint32_t i; + for (i = 0; i < num_attributes && !is_artificial; ++i) { + const dw_attr_t attr = attributes.AttributeAtIndex(i); + DWARFFormValue form_value; + if (attributes.ExtractFormValueAtIndex(i, form_value)) { + switch (attr) { + case DW_AT_name: + name = form_value.AsCString(); + break; + case DW_AT_type: + encoding_form = form_value; + break; + case DW_AT_bit_offset: + bit_offset = form_value.Signed(); + break; + case DW_AT_bit_size: + bit_size = form_value.Unsigned(); + break; + case DW_AT_byte_size: + byte_size = form_value.Unsigned(); + break; + case DW_AT_data_bit_offset: + data_bit_offset = form_value.Unsigned(); + break; + case DW_AT_data_member_location: + if (form_value.BlockData()) { + Value initialValue(0); + Value memberOffset(0); + const DWARFDataExtractor &debug_info_data = die.GetData(); + uint32_t block_length = form_value.Unsigned(); + uint32_t block_offset = + form_value.BlockData() - debug_info_data.GetDataStart(); + if (DWARFExpression::Evaluate( + nullptr, // ExecutionContext * + nullptr, // RegisterContext * + module_sp, debug_info_data, die.GetCU(), block_offset, + block_length, eRegisterKindDWARF, &initialValue, + nullptr, memberOffset, nullptr)) { + member_byte_offset = + memberOffset.ResolveValue(nullptr).UInt(); + } + } else { + // With DWARF 3 and later, if the value is an integer constant, + // this form value is the offset in bytes from the beginning of + // the containing entity. + member_byte_offset = form_value.Unsigned(); + } + break; + + case DW_AT_accessibility: + accessibility = DW_ACCESS_to_AccessType(form_value.Unsigned()); + break; + case DW_AT_artificial: + is_artificial = form_value.Boolean(); + break; + case DW_AT_APPLE_property_name: + prop_name = form_value.AsCString(); + break; + case DW_AT_APPLE_property_getter: + prop_getter_name = form_value.AsCString(); + break; + case DW_AT_APPLE_property_setter: + prop_setter_name = form_value.AsCString(); + break; + case DW_AT_APPLE_property_attribute: + prop_attributes = form_value.Unsigned(); + break; + case DW_AT_external: + is_external = form_value.Boolean(); + break; + + default: + case DW_AT_declaration: + case DW_AT_description: + case DW_AT_mutable: + case DW_AT_visibility: + case DW_AT_sibling: + break; + } + } + } + + if (prop_name) { + ConstString fixed_getter; + ConstString fixed_setter; + + // Check if the property getter/setter were provided as full names. + // We want basenames, so we extract them. + + if (prop_getter_name && prop_getter_name[0] == '-') { + ObjCLanguage::MethodName prop_getter_method(prop_getter_name, true); + prop_getter_name = prop_getter_method.GetSelector().GetCString(); + } + + if (prop_setter_name && prop_setter_name[0] == '-') { + ObjCLanguage::MethodName prop_setter_method(prop_setter_name, true); + prop_setter_name = prop_setter_method.GetSelector().GetCString(); + } + + // If the names haven't been provided, they need to be filled in. + + if (!prop_getter_name) { + prop_getter_name = prop_name; + } + if (!prop_setter_name && prop_name[0] && + !(prop_attributes & DW_APPLE_PROPERTY_readonly)) { + StreamString ss; + + ss.Printf("set%c%s:", toupper(prop_name[0]), &prop_name[1]); + + fixed_setter.SetString(ss.GetString()); + prop_setter_name = fixed_setter.GetCString(); + } + } + + // Clang has a DWARF generation bug where sometimes it represents + // fields that are references with bad byte size and bit size/offset + // information such as: + // + // DW_AT_byte_size( 0x00 ) + // DW_AT_bit_size( 0x40 ) + // DW_AT_bit_offset( 0xffffffffffffffc0 ) + // + // So check the bit offset to make sure it is sane, and if the values + // are not sane, remove them. If we don't do this then we will end up + // with a crash if we try to use this type in an expression when clang + // becomes unhappy with its recycled debug info. + + if (byte_size.getValueOr(0) == 0 && bit_offset < 0) { + bit_size = 0; + bit_offset = 0; + } + + // FIXME: Make Clang ignore Objective-C accessibility for expressions + if (class_language == eLanguageTypeObjC || + class_language == eLanguageTypeObjC_plus_plus) + accessibility = eAccessNone; + + // Handle static members + if (is_external && member_byte_offset == UINT32_MAX) { + Type *var_type = die.ResolveTypeUID(encoding_form.Reference()); + + if (var_type) { + if (accessibility == eAccessNone) + accessibility = eAccessPublic; + ClangASTContext::AddVariableToRecordType( + class_clang_type, name, var_type->GetLayoutCompilerType(), + accessibility); + } + break; + } + + if (!is_artificial) { + Type *member_type = die.ResolveTypeUID(encoding_form.Reference()); + + clang::FieldDecl *field_decl = nullptr; + if (tag == DW_TAG_member) { + if (member_type) { + if (accessibility == eAccessNone) + accessibility = default_accessibility; + member_accessibilities.push_back(accessibility); + + uint64_t field_bit_offset = + (member_byte_offset == UINT32_MAX ? 0 + : (member_byte_offset * 8)); + if (bit_size > 0) { + + BitfieldInfo this_field_info; + this_field_info.bit_offset = field_bit_offset; + this_field_info.bit_size = bit_size; + + ///////////////////////////////////////////////////////////// + // How to locate a field given the DWARF debug information + // + // AT_byte_size indicates the size of the word in which the bit + // offset must be interpreted. + // + // AT_data_member_location indicates the byte offset of the + // word from the base address of the structure. + // + // AT_bit_offset indicates how many bits into the word + // (according to the host endianness) the low-order bit of the + // field starts. AT_bit_offset can be negative. + // + // AT_bit_size indicates the size of the field in bits. + ///////////////////////////////////////////////////////////// + + if (data_bit_offset != UINT64_MAX) { + this_field_info.bit_offset = data_bit_offset; + } else { + if (!byte_size) + byte_size = member_type->GetByteSize(); + + ObjectFile *objfile = die.GetDWARF()->GetObjectFile(); + if (objfile->GetByteOrder() == eByteOrderLittle) { + this_field_info.bit_offset += byte_size.getValueOr(0) * 8; + this_field_info.bit_offset -= (bit_offset + bit_size); + } else { + this_field_info.bit_offset += bit_offset; + } + } + + if ((this_field_info.bit_offset >= parent_bit_size) || + !last_field_info.NextBitfieldOffsetIsValid( + this_field_info.bit_offset)) { + ObjectFile *objfile = die.GetDWARF()->GetObjectFile(); + objfile->GetModule()->ReportWarning( + "0x%8.8" PRIx64 ": %s bitfield named \"%s\" has invalid " + "bit offset (0x%8.8" PRIx64 + ") member will be ignored. Please file a bug against the " + "compiler and include the preprocessed output for %s\n", + die.GetID(), DW_TAG_value_to_name(tag), name, + this_field_info.bit_offset, + GetUnitName(parent_die).c_str()); + this_field_info.Clear(); + continue; + } + + // Update the field bit offset we will report for layout + field_bit_offset = this_field_info.bit_offset; + + // If the member to be emitted did not start on a character + // boundary and there is empty space between the last field and + // this one, then we need to emit an anonymous member filling + // up the space up to its start. There are three cases here: + // + // 1 If the previous member ended on a character boundary, then + // we can emit an + // anonymous member starting at the most recent character + // boundary. + // + // 2 If the previous member did not end on a character boundary + // and the distance + // from the end of the previous member to the current member + // is less than a + // word width, then we can emit an anonymous member starting + // right after the + // previous member and right before this member. + // + // 3 If the previous member did not end on a character boundary + // and the distance + // from the end of the previous member to the current member + // is greater than + // or equal a word width, then we act as in Case 1. + + const uint64_t character_width = 8; + const uint64_t word_width = 32; + + // Objective-C has invalid DW_AT_bit_offset values in older + // versions of clang, so we have to be careful and only insert + // unnamed bitfields if we have a new enough clang. + bool detect_unnamed_bitfields = true; + + if (class_language == eLanguageTypeObjC || + class_language == eLanguageTypeObjC_plus_plus) + detect_unnamed_bitfields = + die.GetCU()->Supports_unnamed_objc_bitfields(); + + if (detect_unnamed_bitfields) { + BitfieldInfo anon_field_info; + + if ((this_field_info.bit_offset % character_width) != + 0) // not char aligned + { + uint64_t last_field_end = 0; + + if (last_field_info.IsValid()) + last_field_end = + last_field_info.bit_offset + last_field_info.bit_size; + + if (this_field_info.bit_offset != last_field_end) { + if (((last_field_end % character_width) == 0) || // case 1 + (this_field_info.bit_offset - last_field_end >= + word_width)) // case 3 + { + anon_field_info.bit_size = + this_field_info.bit_offset % character_width; + anon_field_info.bit_offset = + this_field_info.bit_offset - + anon_field_info.bit_size; + } else // case 2 + { + anon_field_info.bit_size = + this_field_info.bit_offset - last_field_end; + anon_field_info.bit_offset = last_field_end; + } + } + } + + if (anon_field_info.IsValid()) { + clang::FieldDecl *unnamed_bitfield_decl = + ClangASTContext::AddFieldToRecordType( + class_clang_type, llvm::StringRef(), + m_ast.GetBuiltinTypeForEncodingAndBitSize( + eEncodingSint, word_width), + accessibility, anon_field_info.bit_size); + + layout_info.field_offsets.insert(std::make_pair( + unnamed_bitfield_decl, anon_field_info.bit_offset)); + } + } + last_field_info = this_field_info; + } else { + last_field_info.Clear(); + } + + CompilerType member_clang_type = + member_type->GetLayoutCompilerType(); + if (!member_clang_type.IsCompleteType()) + member_clang_type.GetCompleteType(); + + { + // Older versions of clang emit array[0] and array[1] in the + // same way (<rdar://problem/12566646>). If the current field + // is at the end of the structure, then there is definitely no + // room for extra elements and we override the type to + // array[0]. + + CompilerType member_array_element_type; + uint64_t member_array_size; + bool member_array_is_incomplete; + + if (member_clang_type.IsArrayType( + &member_array_element_type, &member_array_size, + &member_array_is_incomplete) && + !member_array_is_incomplete) { + uint64_t parent_byte_size = + parent_die.GetAttributeValueAsUnsigned(DW_AT_byte_size, + UINT64_MAX); + + if (member_byte_offset >= parent_byte_size) { + if (member_array_size != 1 && + (member_array_size != 0 || + member_byte_offset > parent_byte_size)) { + module_sp->ReportError( + "0x%8.8" PRIx64 + ": DW_TAG_member '%s' refers to type 0x%8.8x" + " which extends beyond the bounds of 0x%8.8" PRIx64, + die.GetID(), name, + encoding_form.Reference().GetOffset(), + parent_die.GetID()); + } + + member_clang_type = m_ast.CreateArrayType( + member_array_element_type, 0, false); + } + } + } + + if (ClangASTContext::IsCXXClassType(member_clang_type) && + !member_clang_type.GetCompleteType()) { + if (die.GetCU()->GetProducer() == eProducerClang) + module_sp->ReportError( + "DWARF DIE at 0x%8.8x (class %s) has a member variable " + "0x%8.8x (%s) whose type is a forward declaration, not a " + "complete definition.\nTry compiling the source file " + "with -fstandalone-debug", + parent_die.GetOffset(), parent_die.GetName(), + die.GetOffset(), name); + else + module_sp->ReportError( + "DWARF DIE at 0x%8.8x (class %s) has a member variable " + "0x%8.8x (%s) whose type is a forward declaration, not a " + "complete definition.\nPlease file a bug against the " + "compiler and include the preprocessed output for %s", + parent_die.GetOffset(), parent_die.GetName(), + die.GetOffset(), name, GetUnitName(parent_die).c_str()); + // We have no choice other than to pretend that the member + // class is complete. If we don't do this, clang will crash + // when trying to layout the class. Since we provide layout + // assistance, all ivars in this class and other classes will + // be fine, this is the best we can do short of crashing. + if (ClangASTContext::StartTagDeclarationDefinition( + member_clang_type)) { + ClangASTContext::CompleteTagDeclarationDefinition( + member_clang_type); + } else { + module_sp->ReportError( + "DWARF DIE at 0x%8.8x (class %s) has a member variable " + "0x%8.8x (%s) whose type claims to be a C++ class but we " + "were not able to start its definition.\nPlease file a " + "bug and attach the file at the start of this error " + "message", + parent_die.GetOffset(), parent_die.GetName(), + die.GetOffset(), name); + } + } + + field_decl = ClangASTContext::AddFieldToRecordType( + class_clang_type, name, member_clang_type, accessibility, + bit_size); + + m_ast.SetMetadataAsUserID(field_decl, die.GetID()); + + layout_info.field_offsets.insert( + std::make_pair(field_decl, field_bit_offset)); + } else { + if (name) + module_sp->ReportError( + "0x%8.8" PRIx64 + ": DW_TAG_member '%s' refers to type 0x%8.8x" + " which was unable to be parsed", + die.GetID(), name, encoding_form.Reference().GetOffset()); + else + module_sp->ReportError( + "0x%8.8" PRIx64 ": DW_TAG_member refers to type 0x%8.8x" + " which was unable to be parsed", + die.GetID(), encoding_form.Reference().GetOffset()); + } + } + + if (prop_name != nullptr && member_type) { + clang::ObjCIvarDecl *ivar_decl = nullptr; + + if (field_decl) { + ivar_decl = clang::dyn_cast<clang::ObjCIvarDecl>(field_decl); + assert(ivar_decl != nullptr); + } + + ClangASTMetadata metadata; + metadata.SetUserID(die.GetID()); + delayed_properties.push_back(DelayedAddObjCClassProperty( + class_clang_type, prop_name, + member_type->GetLayoutCompilerType(), ivar_decl, + prop_setter_name, prop_getter_name, prop_attributes, + &metadata)); + + if (ivar_decl) + m_ast.SetMetadataAsUserID(ivar_decl, die.GetID()); + } + } + } + ++member_idx; + } break; + + case DW_TAG_subprogram: + // Let the type parsing code handle this one for us. + member_function_dies.push_back(die); + break; + + case DW_TAG_inheritance: { + is_a_class = true; + if (default_accessibility == eAccessNone) + default_accessibility = eAccessPrivate; + // TODO: implement DW_TAG_inheritance type parsing + DWARFAttributes attributes; + const size_t num_attributes = die.GetAttributes(attributes); + if (num_attributes > 0) { + DWARFFormValue encoding_form; + AccessType accessibility = default_accessibility; + bool is_virtual = false; + bool is_base_of_class = true; + off_t member_byte_offset = 0; + uint32_t i; + for (i = 0; i < num_attributes; ++i) { + const dw_attr_t attr = attributes.AttributeAtIndex(i); + DWARFFormValue form_value; + if (attributes.ExtractFormValueAtIndex(i, form_value)) { + switch (attr) { + case DW_AT_type: + encoding_form = form_value; + break; + case DW_AT_data_member_location: + if (form_value.BlockData()) { + Value initialValue(0); + Value memberOffset(0); + const DWARFDataExtractor &debug_info_data = die.GetData(); + uint32_t block_length = form_value.Unsigned(); + uint32_t block_offset = + form_value.BlockData() - debug_info_data.GetDataStart(); + if (DWARFExpression::Evaluate(nullptr, nullptr, module_sp, + debug_info_data, die.GetCU(), + block_offset, block_length, + eRegisterKindDWARF, &initialValue, + nullptr, memberOffset, nullptr)) { + member_byte_offset = + memberOffset.ResolveValue(nullptr).UInt(); + } + } else { + // With DWARF 3 and later, if the value is an integer constant, + // this form value is the offset in bytes from the beginning of + // the containing entity. + member_byte_offset = form_value.Unsigned(); + } + break; + + case DW_AT_accessibility: + accessibility = DW_ACCESS_to_AccessType(form_value.Unsigned()); + break; + + case DW_AT_virtuality: + is_virtual = form_value.Boolean(); + break; + + case DW_AT_sibling: + break; + + default: + break; + } + } + } + + Type *base_class_type = die.ResolveTypeUID(encoding_form.Reference()); + if (base_class_type == nullptr) { + module_sp->ReportError("0x%8.8x: DW_TAG_inheritance failed to " + "resolve the base class at 0x%8.8x" + " from enclosing type 0x%8.8x. \nPlease file " + "a bug and attach the file at the start of " + "this error message", + die.GetOffset(), + encoding_form.Reference().GetOffset(), + parent_die.GetOffset()); + break; + } + + CompilerType base_class_clang_type = + base_class_type->GetFullCompilerType(); + assert(base_class_clang_type); + if (class_language == eLanguageTypeObjC) { + ast->SetObjCSuperClass(class_clang_type, base_class_clang_type); + } else { + std::unique_ptr<clang::CXXBaseSpecifier> result = + ast->CreateBaseClassSpecifier( + base_class_clang_type.GetOpaqueQualType(), accessibility, + is_virtual, is_base_of_class); + if (!result) + break; + + base_classes.push_back(std::move(result)); + + if (is_virtual) { + // Do not specify any offset for virtual inheritance. The DWARF + // produced by clang doesn't give us a constant offset, but gives + // us a DWARF expressions that requires an actual object in memory. + // the DW_AT_data_member_location for a virtual base class looks + // like: + // DW_AT_data_member_location( DW_OP_dup, DW_OP_deref, + // DW_OP_constu(0x00000018), DW_OP_minus, DW_OP_deref, + // DW_OP_plus ) + // Given this, there is really no valid response we can give to + // clang for virtual base class offsets, and this should eventually + // be removed from LayoutRecordType() in the external + // AST source in clang. + } else { + layout_info.base_offsets.insert(std::make_pair( + ast->GetAsCXXRecordDecl( + base_class_clang_type.GetOpaqueQualType()), + clang::CharUnits::fromQuantity(member_byte_offset))); + } + } + } + } break; + + default: + break; + } + } + + return true; +} + +size_t DWARFASTParserClang::ParseChildParameters( + clang::DeclContext *containing_decl_ctx, const DWARFDIE &parent_die, + bool skip_artificial, bool &is_static, bool &is_variadic, + bool &has_template_params, std::vector<CompilerType> &function_param_types, + std::vector<clang::ParmVarDecl *> &function_param_decls, + unsigned &type_quals) { + if (!parent_die) + return 0; + + size_t arg_idx = 0; + for (DWARFDIE die = parent_die.GetFirstChild(); die.IsValid(); + die = die.GetSibling()) { + const dw_tag_t tag = die.Tag(); + switch (tag) { + case DW_TAG_formal_parameter: { + DWARFAttributes attributes; + const size_t num_attributes = die.GetAttributes(attributes); + if (num_attributes > 0) { + const char *name = nullptr; + DWARFFormValue param_type_die_form; + bool is_artificial = false; + // one of None, Auto, Register, Extern, Static, PrivateExtern + + clang::StorageClass storage = clang::SC_None; + uint32_t i; + for (i = 0; i < num_attributes; ++i) { + const dw_attr_t attr = attributes.AttributeAtIndex(i); + DWARFFormValue form_value; + if (attributes.ExtractFormValueAtIndex(i, form_value)) { + switch (attr) { + case DW_AT_name: + name = form_value.AsCString(); + break; + case DW_AT_type: + param_type_die_form = form_value; + break; + case DW_AT_artificial: + is_artificial = form_value.Boolean(); + break; + case DW_AT_location: + case DW_AT_const_value: + case DW_AT_default_value: + case DW_AT_description: + case DW_AT_endianity: + case DW_AT_is_optional: + case DW_AT_segment: + case DW_AT_variable_parameter: + default: + case DW_AT_abstract_origin: + case DW_AT_sibling: + break; + } + } + } + + bool skip = false; + if (skip_artificial && is_artificial) { + // In order to determine if a C++ member function is "const" we + // have to look at the const-ness of "this"... + if (arg_idx == 0 && + DeclKindIsCXXClass(containing_decl_ctx->getDeclKind()) && + // Often times compilers omit the "this" name for the + // specification DIEs, so we can't rely upon the name being in + // the formal parameter DIE... + (name == nullptr || ::strcmp(name, "this") == 0)) { + Type *this_type = + die.ResolveTypeUID(param_type_die_form.Reference()); + if (this_type) { + uint32_t encoding_mask = this_type->GetEncodingMask(); + if (encoding_mask & Type::eEncodingIsPointerUID) { + is_static = false; + + if (encoding_mask & (1u << Type::eEncodingIsConstUID)) + type_quals |= clang::Qualifiers::Const; + if (encoding_mask & (1u << Type::eEncodingIsVolatileUID)) + type_quals |= clang::Qualifiers::Volatile; + } + } + } + skip = true; + } + + if (!skip) { + Type *type = die.ResolveTypeUID(param_type_die_form.Reference()); + if (type) { + function_param_types.push_back(type->GetForwardCompilerType()); + + clang::ParmVarDecl *param_var_decl = + m_ast.CreateParameterDeclaration(containing_decl_ctx, name, + type->GetForwardCompilerType(), + storage); + assert(param_var_decl); + function_param_decls.push_back(param_var_decl); + + m_ast.SetMetadataAsUserID(param_var_decl, die.GetID()); + } + } + } + arg_idx++; + } break; + + case DW_TAG_unspecified_parameters: + is_variadic = true; + break; + + case DW_TAG_template_type_parameter: + case DW_TAG_template_value_parameter: + case DW_TAG_GNU_template_parameter_pack: + // The one caller of this was never using the template_param_infos, and + // the local variable was taking up a large amount of stack space in + // SymbolFileDWARF::ParseType() so this was removed. If we ever need the + // template params back, we can add them back. + // ParseTemplateDIE (dwarf_cu, die, template_param_infos); + has_template_params = true; + break; + + default: + break; + } + } + return arg_idx; +} + +llvm::Optional<SymbolFile::ArrayInfo> +DWARFASTParser::ParseChildArrayInfo(const DWARFDIE &parent_die, + const ExecutionContext *exe_ctx) { + SymbolFile::ArrayInfo array_info; + if (!parent_die) + return llvm::None; + + for (DWARFDIE die = parent_die.GetFirstChild(); die.IsValid(); + die = die.GetSibling()) { + const dw_tag_t tag = die.Tag(); + switch (tag) { + case DW_TAG_subrange_type: { + DWARFAttributes attributes; + const size_t num_child_attributes = die.GetAttributes(attributes); + if (num_child_attributes > 0) { + uint64_t num_elements = 0; + uint64_t lower_bound = 0; + uint64_t upper_bound = 0; + bool upper_bound_valid = false; + uint32_t i; + for (i = 0; i < num_child_attributes; ++i) { + const dw_attr_t attr = attributes.AttributeAtIndex(i); + DWARFFormValue form_value; + if (attributes.ExtractFormValueAtIndex(i, form_value)) { + switch (attr) { + case DW_AT_name: + break; + + case DW_AT_count: + if (DWARFDIE var_die = die.GetReferencedDIE(DW_AT_count)) { + if (var_die.Tag() == DW_TAG_variable) + if (exe_ctx) { + if (auto frame = exe_ctx->GetFrameSP()) { + Status error; + lldb::VariableSP var_sp; + auto valobj_sp = frame->GetValueForVariableExpressionPath( + var_die.GetName(), eNoDynamicValues, 0, var_sp, + error); + if (valobj_sp) { + num_elements = valobj_sp->GetValueAsUnsigned(0); + break; + } + } + } + } else + num_elements = form_value.Unsigned(); + break; + + case DW_AT_bit_stride: + array_info.bit_stride = form_value.Unsigned(); + break; + + case DW_AT_byte_stride: + array_info.byte_stride = form_value.Unsigned(); + break; + + case DW_AT_lower_bound: + lower_bound = form_value.Unsigned(); + break; + + case DW_AT_upper_bound: + upper_bound_valid = true; + upper_bound = form_value.Unsigned(); + break; + + default: + case DW_AT_abstract_origin: + case DW_AT_accessibility: + case DW_AT_allocated: + case DW_AT_associated: + case DW_AT_data_location: + case DW_AT_declaration: + case DW_AT_description: + case DW_AT_sibling: + case DW_AT_threads_scaled: + case DW_AT_type: + case DW_AT_visibility: + break; + } + } + } + + if (num_elements == 0) { + if (upper_bound_valid && upper_bound >= lower_bound) + num_elements = upper_bound - lower_bound + 1; + } + + array_info.element_orders.push_back(num_elements); + } + } break; + } + } + return array_info; +} + +Type *DWARFASTParserClang::GetTypeForDIE(const DWARFDIE &die) { + if (die) { + SymbolFileDWARF *dwarf = die.GetDWARF(); + DWARFAttributes attributes; + const size_t num_attributes = die.GetAttributes(attributes); + if (num_attributes > 0) { + DWARFFormValue type_die_form; + for (size_t i = 0; i < num_attributes; ++i) { + dw_attr_t attr = attributes.AttributeAtIndex(i); + DWARFFormValue form_value; + + if (attr == DW_AT_type && + attributes.ExtractFormValueAtIndex(i, form_value)) + return dwarf->ResolveTypeUID(form_value.Reference(), true); + } + } + } + + return nullptr; +} + +clang::Decl *DWARFASTParserClang::GetClangDeclForDIE(const DWARFDIE &die) { + if (!die) + return nullptr; + + switch (die.Tag()) { + case DW_TAG_variable: + case DW_TAG_constant: + case DW_TAG_formal_parameter: + case DW_TAG_imported_declaration: + case DW_TAG_imported_module: + break; + default: + return nullptr; + } + + DIEToDeclMap::iterator cache_pos = m_die_to_decl.find(die.GetDIE()); + if (cache_pos != m_die_to_decl.end()) + return cache_pos->second; + + if (DWARFDIE spec_die = die.GetReferencedDIE(DW_AT_specification)) { + clang::Decl *decl = GetClangDeclForDIE(spec_die); + m_die_to_decl[die.GetDIE()] = decl; + m_decl_to_die[decl].insert(die.GetDIE()); + return decl; + } + + if (DWARFDIE abstract_origin_die = + die.GetReferencedDIE(DW_AT_abstract_origin)) { + clang::Decl *decl = GetClangDeclForDIE(abstract_origin_die); + m_die_to_decl[die.GetDIE()] = decl; + m_decl_to_die[decl].insert(die.GetDIE()); + return decl; + } + + clang::Decl *decl = nullptr; + switch (die.Tag()) { + case DW_TAG_variable: + case DW_TAG_constant: + case DW_TAG_formal_parameter: { + SymbolFileDWARF *dwarf = die.GetDWARF(); + Type *type = GetTypeForDIE(die); + if (dwarf && type) { + const char *name = die.GetName(); + clang::DeclContext *decl_context = + ClangASTContext::DeclContextGetAsDeclContext( + dwarf->GetDeclContextContainingUID(die.GetID())); + decl = m_ast.CreateVariableDeclaration( + decl_context, name, + ClangUtil::GetQualType(type->GetForwardCompilerType())); + } + break; + } + case DW_TAG_imported_declaration: { + SymbolFileDWARF *dwarf = die.GetDWARF(); + DWARFDIE imported_uid = die.GetAttributeValueAsReferenceDIE(DW_AT_import); + if (imported_uid) { + CompilerDecl imported_decl = imported_uid.GetDecl(); + if (imported_decl) { + clang::DeclContext *decl_context = + ClangASTContext::DeclContextGetAsDeclContext( + dwarf->GetDeclContextContainingUID(die.GetID())); + if (clang::NamedDecl *clang_imported_decl = + llvm::dyn_cast<clang::NamedDecl>( + (clang::Decl *)imported_decl.GetOpaqueDecl())) + decl = + m_ast.CreateUsingDeclaration(decl_context, clang_imported_decl); + } + } + break; + } + case DW_TAG_imported_module: { + SymbolFileDWARF *dwarf = die.GetDWARF(); + DWARFDIE imported_uid = die.GetAttributeValueAsReferenceDIE(DW_AT_import); + + if (imported_uid) { + CompilerDeclContext imported_decl_ctx = imported_uid.GetDeclContext(); + if (imported_decl_ctx) { + clang::DeclContext *decl_context = + ClangASTContext::DeclContextGetAsDeclContext( + dwarf->GetDeclContextContainingUID(die.GetID())); + if (clang::NamespaceDecl *ns_decl = + ClangASTContext::DeclContextGetAsNamespaceDecl( + imported_decl_ctx)) + decl = m_ast.CreateUsingDirectiveDeclaration(decl_context, ns_decl); + } + } + break; + } + default: + break; + } + + m_die_to_decl[die.GetDIE()] = decl; + m_decl_to_die[decl].insert(die.GetDIE()); + + return decl; +} + +clang::DeclContext * +DWARFASTParserClang::GetClangDeclContextForDIE(const DWARFDIE &die) { + if (die) { + clang::DeclContext *decl_ctx = GetCachedClangDeclContextForDIE(die); + if (decl_ctx) + return decl_ctx; + + bool try_parsing_type = true; + switch (die.Tag()) { + case DW_TAG_compile_unit: + case DW_TAG_partial_unit: + decl_ctx = m_ast.GetTranslationUnitDecl(); + try_parsing_type = false; + break; + + case DW_TAG_namespace: + decl_ctx = ResolveNamespaceDIE(die); + try_parsing_type = false; + break; + + case DW_TAG_lexical_block: + decl_ctx = GetDeclContextForBlock(die); + try_parsing_type = false; + break; + + default: + break; + } + + if (decl_ctx == nullptr && try_parsing_type) { + Type *type = die.GetDWARF()->ResolveType(die); + if (type) + decl_ctx = GetCachedClangDeclContextForDIE(die); + } + + if (decl_ctx) { + LinkDeclContextToDIE(decl_ctx, die); + return decl_ctx; + } + } + return nullptr; +} + +static bool IsSubroutine(const DWARFDIE &die) { + switch (die.Tag()) { + case DW_TAG_subprogram: + case DW_TAG_inlined_subroutine: + return true; + default: + return false; + } +} + +static DWARFDIE GetContainingFunctionWithAbstractOrigin(const DWARFDIE &die) { + for (DWARFDIE candidate = die; candidate; candidate = candidate.GetParent()) { + if (IsSubroutine(candidate)) { + if (candidate.GetReferencedDIE(DW_AT_abstract_origin)) { + return candidate; + } else { + return DWARFDIE(); + } + } + } + assert(0 && "Shouldn't call GetContainingFunctionWithAbstractOrigin on " + "something not in a function"); + return DWARFDIE(); +} + +static DWARFDIE FindAnyChildWithAbstractOrigin(const DWARFDIE &context) { + for (DWARFDIE candidate = context.GetFirstChild(); candidate.IsValid(); + candidate = candidate.GetSibling()) { + if (candidate.GetReferencedDIE(DW_AT_abstract_origin)) { + return candidate; + } + } + return DWARFDIE(); +} + +static DWARFDIE FindFirstChildWithAbstractOrigin(const DWARFDIE &block, + const DWARFDIE &function) { + assert(IsSubroutine(function)); + for (DWARFDIE context = block; context != function.GetParent(); + context = context.GetParent()) { + assert(!IsSubroutine(context) || context == function); + if (DWARFDIE child = FindAnyChildWithAbstractOrigin(context)) { + return child; + } + } + return DWARFDIE(); +} + +clang::DeclContext * +DWARFASTParserClang::GetDeclContextForBlock(const DWARFDIE &die) { + assert(die.Tag() == DW_TAG_lexical_block); + DWARFDIE containing_function_with_abstract_origin = + GetContainingFunctionWithAbstractOrigin(die); + if (!containing_function_with_abstract_origin) { + return (clang::DeclContext *)ResolveBlockDIE(die); + } + DWARFDIE child = FindFirstChildWithAbstractOrigin( + die, containing_function_with_abstract_origin); + CompilerDeclContext decl_context = + GetDeclContextContainingUIDFromDWARF(child); + return (clang::DeclContext *)decl_context.GetOpaqueDeclContext(); +} + +clang::BlockDecl *DWARFASTParserClang::ResolveBlockDIE(const DWARFDIE &die) { + if (die && die.Tag() == DW_TAG_lexical_block) { + clang::BlockDecl *decl = + llvm::cast_or_null<clang::BlockDecl>(m_die_to_decl_ctx[die.GetDIE()]); + + if (!decl) { + DWARFDIE decl_context_die; + clang::DeclContext *decl_context = + GetClangDeclContextContainingDIE(die, &decl_context_die); + decl = m_ast.CreateBlockDeclaration(decl_context); + + if (decl) + LinkDeclContextToDIE((clang::DeclContext *)decl, die); + } + + return decl; + } + return nullptr; +} + +clang::NamespaceDecl * +DWARFASTParserClang::ResolveNamespaceDIE(const DWARFDIE &die) { + if (die && die.Tag() == DW_TAG_namespace) { + // See if we already parsed this namespace DIE and associated it with a + // uniqued namespace declaration + clang::NamespaceDecl *namespace_decl = + static_cast<clang::NamespaceDecl *>(m_die_to_decl_ctx[die.GetDIE()]); + if (namespace_decl) + return namespace_decl; + else { + const char *namespace_name = die.GetName(); + clang::DeclContext *containing_decl_ctx = + GetClangDeclContextContainingDIE(die, nullptr); + bool is_inline = + die.GetAttributeValueAsUnsigned(DW_AT_export_symbols, 0) != 0; + + namespace_decl = m_ast.GetUniqueNamespaceDeclaration( + namespace_name, containing_decl_ctx, is_inline); + Log *log = + nullptr; // (LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_INFO)); + if (log) { + SymbolFileDWARF *dwarf = die.GetDWARF(); + if (namespace_name) { + dwarf->GetObjectFile()->GetModule()->LogMessage( + log, "ASTContext => %p: 0x%8.8" PRIx64 + ": DW_TAG_namespace with DW_AT_name(\"%s\") => " + "clang::NamespaceDecl *%p (original = %p)", + static_cast<void *>(m_ast.getASTContext()), die.GetID(), + namespace_name, static_cast<void *>(namespace_decl), + static_cast<void *>(namespace_decl->getOriginalNamespace())); + } else { + dwarf->GetObjectFile()->GetModule()->LogMessage( + log, "ASTContext => %p: 0x%8.8" PRIx64 + ": DW_TAG_namespace (anonymous) => clang::NamespaceDecl *%p " + "(original = %p)", + static_cast<void *>(m_ast.getASTContext()), die.GetID(), + static_cast<void *>(namespace_decl), + static_cast<void *>(namespace_decl->getOriginalNamespace())); + } + } + + if (namespace_decl) + LinkDeclContextToDIE((clang::DeclContext *)namespace_decl, die); + return namespace_decl; + } + } + return nullptr; +} + +clang::DeclContext *DWARFASTParserClang::GetClangDeclContextContainingDIE( + const DWARFDIE &die, DWARFDIE *decl_ctx_die_copy) { + SymbolFileDWARF *dwarf = die.GetDWARF(); + + DWARFDIE decl_ctx_die = dwarf->GetDeclContextDIEContainingDIE(die); + + if (decl_ctx_die_copy) + *decl_ctx_die_copy = decl_ctx_die; + + if (decl_ctx_die) { + clang::DeclContext *clang_decl_ctx = + GetClangDeclContextForDIE(decl_ctx_die); + if (clang_decl_ctx) + return clang_decl_ctx; + } + return m_ast.GetTranslationUnitDecl(); +} + +clang::DeclContext * +DWARFASTParserClang::GetCachedClangDeclContextForDIE(const DWARFDIE &die) { + if (die) { + DIEToDeclContextMap::iterator pos = m_die_to_decl_ctx.find(die.GetDIE()); + if (pos != m_die_to_decl_ctx.end()) + return pos->second; + } + return nullptr; +} + +void DWARFASTParserClang::LinkDeclContextToDIE(clang::DeclContext *decl_ctx, + const DWARFDIE &die) { + m_die_to_decl_ctx[die.GetDIE()] = decl_ctx; + // There can be many DIEs for a single decl context + // m_decl_ctx_to_die[decl_ctx].insert(die.GetDIE()); + m_decl_ctx_to_die.insert(std::make_pair(decl_ctx, die)); +} + +bool DWARFASTParserClang::CopyUniqueClassMethodTypes( + const DWARFDIE &src_class_die, const DWARFDIE &dst_class_die, + lldb_private::Type *class_type, std::vector<DWARFDIE> &failures) { + if (!class_type || !src_class_die || !dst_class_die) + return false; + if (src_class_die.Tag() != dst_class_die.Tag()) + return false; + + // We need to complete the class type so we can get all of the method types + // parsed so we can then unique those types to their equivalent counterparts + // in "dst_cu" and "dst_class_die" + class_type->GetFullCompilerType(); + + DWARFDIE src_die; + DWARFDIE dst_die; + UniqueCStringMap<DWARFDIE> src_name_to_die; + UniqueCStringMap<DWARFDIE> dst_name_to_die; + UniqueCStringMap<DWARFDIE> src_name_to_die_artificial; + UniqueCStringMap<DWARFDIE> dst_name_to_die_artificial; + for (src_die = src_class_die.GetFirstChild(); src_die.IsValid(); + src_die = src_die.GetSibling()) { + if (src_die.Tag() == DW_TAG_subprogram) { + // Make sure this is a declaration and not a concrete instance by looking + // for DW_AT_declaration set to 1. Sometimes concrete function instances + // are placed inside the class definitions and shouldn't be included in + // the list of things are are tracking here. + if (src_die.GetAttributeValueAsUnsigned(DW_AT_declaration, 0) == 1) { + const char *src_name = src_die.GetMangledName(); + if (src_name) { + ConstString src_const_name(src_name); + if (src_die.GetAttributeValueAsUnsigned(DW_AT_artificial, 0)) + src_name_to_die_artificial.Append(src_const_name, src_die); + else + src_name_to_die.Append(src_const_name, src_die); + } + } + } + } + for (dst_die = dst_class_die.GetFirstChild(); dst_die.IsValid(); + dst_die = dst_die.GetSibling()) { + if (dst_die.Tag() == DW_TAG_subprogram) { + // Make sure this is a declaration and not a concrete instance by looking + // for DW_AT_declaration set to 1. Sometimes concrete function instances + // are placed inside the class definitions and shouldn't be included in + // the list of things are are tracking here. + if (dst_die.GetAttributeValueAsUnsigned(DW_AT_declaration, 0) == 1) { + const char *dst_name = dst_die.GetMangledName(); + if (dst_name) { + ConstString dst_const_name(dst_name); + if (dst_die.GetAttributeValueAsUnsigned(DW_AT_artificial, 0)) + dst_name_to_die_artificial.Append(dst_const_name, dst_die); + else + dst_name_to_die.Append(dst_const_name, dst_die); + } + } + } + } + const uint32_t src_size = src_name_to_die.GetSize(); + const uint32_t dst_size = dst_name_to_die.GetSize(); + Log *log = nullptr; // (LogChannelDWARF::GetLogIfAny(DWARF_LOG_DEBUG_INFO | + // DWARF_LOG_TYPE_COMPLETION)); + + // Is everything kosher so we can go through the members at top speed? + bool fast_path = true; + + if (src_size != dst_size) { + if (src_size != 0 && dst_size != 0) { + if (log) + log->Printf("warning: trying to unique class DIE 0x%8.8x to 0x%8.8x, " + "but they didn't have the same size (src=%d, dst=%d)", + src_class_die.GetOffset(), dst_class_die.GetOffset(), + src_size, dst_size); + } + + fast_path = false; + } + + uint32_t idx; + + if (fast_path) { + for (idx = 0; idx < src_size; ++idx) { + src_die = src_name_to_die.GetValueAtIndexUnchecked(idx); + dst_die = dst_name_to_die.GetValueAtIndexUnchecked(idx); + + if (src_die.Tag() != dst_die.Tag()) { + if (log) + log->Printf("warning: tried to unique class DIE 0x%8.8x to 0x%8.8x, " + "but 0x%8.8x (%s) tags didn't match 0x%8.8x (%s)", + src_class_die.GetOffset(), dst_class_die.GetOffset(), + src_die.GetOffset(), src_die.GetTagAsCString(), + dst_die.GetOffset(), dst_die.GetTagAsCString()); + fast_path = false; + } + + const char *src_name = src_die.GetMangledName(); + const char *dst_name = dst_die.GetMangledName(); + + // Make sure the names match + if (src_name == dst_name || (strcmp(src_name, dst_name) == 0)) + continue; + + if (log) + log->Printf("warning: tried to unique class DIE 0x%8.8x to 0x%8.8x, " + "but 0x%8.8x (%s) names didn't match 0x%8.8x (%s)", + src_class_die.GetOffset(), dst_class_die.GetOffset(), + src_die.GetOffset(), src_name, dst_die.GetOffset(), + dst_name); + + fast_path = false; + } + } + + DWARFASTParserClang *src_dwarf_ast_parser = + (DWARFASTParserClang *)src_die.GetDWARFParser(); + DWARFASTParserClang *dst_dwarf_ast_parser = + (DWARFASTParserClang *)dst_die.GetDWARFParser(); + + // Now do the work of linking the DeclContexts and Types. + if (fast_path) { + // We can do this quickly. Just run across the tables index-for-index + // since we know each node has matching names and tags. + for (idx = 0; idx < src_size; ++idx) { + src_die = src_name_to_die.GetValueAtIndexUnchecked(idx); + dst_die = dst_name_to_die.GetValueAtIndexUnchecked(idx); + + clang::DeclContext *src_decl_ctx = + src_dwarf_ast_parser->m_die_to_decl_ctx[src_die.GetDIE()]; + if (src_decl_ctx) { + if (log) + log->Printf("uniquing decl context %p from 0x%8.8x for 0x%8.8x", + static_cast<void *>(src_decl_ctx), src_die.GetOffset(), + dst_die.GetOffset()); + dst_dwarf_ast_parser->LinkDeclContextToDIE(src_decl_ctx, dst_die); + } else { + if (log) + log->Printf("warning: tried to unique decl context from 0x%8.8x for " + "0x%8.8x, but none was found", + src_die.GetOffset(), dst_die.GetOffset()); + } + + Type *src_child_type = + dst_die.GetDWARF()->GetDIEToType()[src_die.GetDIE()]; + if (src_child_type) { + if (log) + log->Printf( + "uniquing type %p (uid=0x%" PRIx64 ") from 0x%8.8x for 0x%8.8x", + static_cast<void *>(src_child_type), src_child_type->GetID(), + src_die.GetOffset(), dst_die.GetOffset()); + dst_die.GetDWARF()->GetDIEToType()[dst_die.GetDIE()] = src_child_type; + } else { + if (log) + log->Printf("warning: tried to unique lldb_private::Type from " + "0x%8.8x for 0x%8.8x, but none was found", + src_die.GetOffset(), dst_die.GetOffset()); + } + } + } else { + // We must do this slowly. For each member of the destination, look up a + // member in the source with the same name, check its tag, and unique them + // if everything matches up. Report failures. + + if (!src_name_to_die.IsEmpty() && !dst_name_to_die.IsEmpty()) { + src_name_to_die.Sort(); + + for (idx = 0; idx < dst_size; ++idx) { + ConstString dst_name = dst_name_to_die.GetCStringAtIndex(idx); + dst_die = dst_name_to_die.GetValueAtIndexUnchecked(idx); + src_die = src_name_to_die.Find(dst_name, DWARFDIE()); + + if (src_die && (src_die.Tag() == dst_die.Tag())) { + clang::DeclContext *src_decl_ctx = + src_dwarf_ast_parser->m_die_to_decl_ctx[src_die.GetDIE()]; + if (src_decl_ctx) { + if (log) + log->Printf("uniquing decl context %p from 0x%8.8x for 0x%8.8x", + static_cast<void *>(src_decl_ctx), + src_die.GetOffset(), dst_die.GetOffset()); + dst_dwarf_ast_parser->LinkDeclContextToDIE(src_decl_ctx, dst_die); + } else { + if (log) + log->Printf("warning: tried to unique decl context from 0x%8.8x " + "for 0x%8.8x, but none was found", + src_die.GetOffset(), dst_die.GetOffset()); + } + + Type *src_child_type = + dst_die.GetDWARF()->GetDIEToType()[src_die.GetDIE()]; + if (src_child_type) { + if (log) + log->Printf("uniquing type %p (uid=0x%" PRIx64 + ") from 0x%8.8x for 0x%8.8x", + static_cast<void *>(src_child_type), + src_child_type->GetID(), src_die.GetOffset(), + dst_die.GetOffset()); + dst_die.GetDWARF()->GetDIEToType()[dst_die.GetDIE()] = + src_child_type; + } else { + if (log) + log->Printf("warning: tried to unique lldb_private::Type from " + "0x%8.8x for 0x%8.8x, but none was found", + src_die.GetOffset(), dst_die.GetOffset()); + } + } else { + if (log) + log->Printf("warning: couldn't find a match for 0x%8.8x", + dst_die.GetOffset()); + + failures.push_back(dst_die); + } + } + } + } + + const uint32_t src_size_artificial = src_name_to_die_artificial.GetSize(); + const uint32_t dst_size_artificial = dst_name_to_die_artificial.GetSize(); + + if (src_size_artificial && dst_size_artificial) { + dst_name_to_die_artificial.Sort(); + + for (idx = 0; idx < src_size_artificial; ++idx) { + ConstString src_name_artificial = + src_name_to_die_artificial.GetCStringAtIndex(idx); + src_die = src_name_to_die_artificial.GetValueAtIndexUnchecked(idx); + dst_die = + dst_name_to_die_artificial.Find(src_name_artificial, DWARFDIE()); + + if (dst_die) { + // Both classes have the artificial types, link them + clang::DeclContext *src_decl_ctx = + src_dwarf_ast_parser->m_die_to_decl_ctx[src_die.GetDIE()]; + if (src_decl_ctx) { + if (log) + log->Printf("uniquing decl context %p from 0x%8.8x for 0x%8.8x", + static_cast<void *>(src_decl_ctx), src_die.GetOffset(), + dst_die.GetOffset()); + dst_dwarf_ast_parser->LinkDeclContextToDIE(src_decl_ctx, dst_die); + } else { + if (log) + log->Printf("warning: tried to unique decl context from 0x%8.8x " + "for 0x%8.8x, but none was found", + src_die.GetOffset(), dst_die.GetOffset()); + } + + Type *src_child_type = + dst_die.GetDWARF()->GetDIEToType()[src_die.GetDIE()]; + if (src_child_type) { + if (log) + log->Printf( + "uniquing type %p (uid=0x%" PRIx64 ") from 0x%8.8x for 0x%8.8x", + static_cast<void *>(src_child_type), src_child_type->GetID(), + src_die.GetOffset(), dst_die.GetOffset()); + dst_die.GetDWARF()->GetDIEToType()[dst_die.GetDIE()] = src_child_type; + } else { + if (log) + log->Printf("warning: tried to unique lldb_private::Type from " + "0x%8.8x for 0x%8.8x, but none was found", + src_die.GetOffset(), dst_die.GetOffset()); + } + } + } + } + + if (dst_size_artificial) { + for (idx = 0; idx < dst_size_artificial; ++idx) { + ConstString dst_name_artificial = + dst_name_to_die_artificial.GetCStringAtIndex(idx); + dst_die = dst_name_to_die_artificial.GetValueAtIndexUnchecked(idx); + if (log) + log->Printf("warning: need to create artificial method for 0x%8.8x for " + "method '%s'", + dst_die.GetOffset(), dst_name_artificial.GetCString()); + + failures.push_back(dst_die); + } + } + + return !failures.empty(); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h new file mode 100644 index 000000000000..5b5d83d65932 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h @@ -0,0 +1,151 @@ +//===-- DWARFASTParserClang.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 SymbolFileDWARF_DWARFASTParserClang_h_ +#define SymbolFileDWARF_DWARFASTParserClang_h_ + +#include "clang/AST/CharUnits.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/SmallVector.h" + +#include "DWARFASTParser.h" +#include "DWARFDefines.h" +#include "lldb/Core/ClangForward.h" +#include "lldb/Core/PluginInterface.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/ClangASTImporter.h" + +#include <vector> + +namespace lldb_private { +class CompileUnit; +} +class DWARFDebugInfoEntry; +class SymbolFileDWARF; + +class DWARFASTParserClang : public DWARFASTParser { +public: + DWARFASTParserClang(lldb_private::ClangASTContext &ast); + + ~DWARFASTParserClang() override; + + // DWARFASTParser interface. + lldb::TypeSP ParseTypeFromDWARF(const lldb_private::SymbolContext &sc, + const DWARFDIE &die, lldb_private::Log *log, + bool *type_is_new_ptr) override; + + lldb_private::Function * + ParseFunctionFromDWARF(lldb_private::CompileUnit &comp_unit, + const DWARFDIE &die) override; + + bool + CompleteTypeFromDWARF(const DWARFDIE &die, lldb_private::Type *type, + lldb_private::CompilerType &compiler_type) override; + + lldb_private::CompilerDecl + GetDeclForUIDFromDWARF(const DWARFDIE &die) override; + + std::vector<DWARFDIE> + GetDIEForDeclContext(lldb_private::CompilerDeclContext decl_context) override; + + lldb_private::CompilerDeclContext + GetDeclContextForUIDFromDWARF(const DWARFDIE &die) override; + + lldb_private::CompilerDeclContext + GetDeclContextContainingUIDFromDWARF(const DWARFDIE &die) override; + + lldb_private::ClangASTImporter &GetClangASTImporter(); + +protected: + class DelayedAddObjCClassProperty; + typedef std::vector<DelayedAddObjCClassProperty> DelayedPropertyList; + + clang::DeclContext *GetDeclContextForBlock(const DWARFDIE &die); + + clang::BlockDecl *ResolveBlockDIE(const DWARFDIE &die); + + clang::NamespaceDecl *ResolveNamespaceDIE(const DWARFDIE &die); + + bool ParseTemplateDIE(const DWARFDIE &die, + lldb_private::ClangASTContext::TemplateParameterInfos + &template_param_infos); + bool ParseTemplateParameterInfos( + const DWARFDIE &parent_die, + lldb_private::ClangASTContext::TemplateParameterInfos + &template_param_infos); + + bool ParseChildMembers( + const DWARFDIE &die, lldb_private::CompilerType &class_compiler_type, + const lldb::LanguageType class_language, + std::vector<std::unique_ptr<clang::CXXBaseSpecifier>> &base_classes, + std::vector<int> &member_accessibilities, + std::vector<DWARFDIE> &member_function_dies, + DelayedPropertyList &delayed_properties, + lldb::AccessType &default_accessibility, bool &is_a_class, + lldb_private::ClangASTImporter::LayoutInfo &layout_info); + + size_t + ParseChildParameters(clang::DeclContext *containing_decl_ctx, + const DWARFDIE &parent_die, bool skip_artificial, + bool &is_static, bool &is_variadic, + bool &has_template_params, + std::vector<lldb_private::CompilerType> &function_args, + std::vector<clang::ParmVarDecl *> &function_param_decls, + unsigned &type_quals); + + size_t ParseChildEnumerators(lldb_private::CompilerType &compiler_type, + bool is_signed, uint32_t enumerator_byte_size, + const DWARFDIE &parent_die); + + lldb_private::Type *GetTypeForDIE(const DWARFDIE &die); + + clang::Decl *GetClangDeclForDIE(const DWARFDIE &die); + + clang::DeclContext *GetClangDeclContextForDIE(const DWARFDIE &die); + + clang::DeclContext *GetClangDeclContextContainingDIE(const DWARFDIE &die, + DWARFDIE *decl_ctx_die); + + bool CopyUniqueClassMethodTypes(const DWARFDIE &src_class_die, + const DWARFDIE &dst_class_die, + lldb_private::Type *class_type, + std::vector<DWARFDIE> &failures); + + clang::DeclContext *GetCachedClangDeclContextForDIE(const DWARFDIE &die); + + void LinkDeclContextToDIE(clang::DeclContext *decl_ctx, const DWARFDIE &die); + + void LinkDeclToDIE(clang::Decl *decl, const DWARFDIE &die); + + lldb::TypeSP ParseTypeFromDWO(const DWARFDIE &die, lldb_private::Log *log); + + // Return true if this type is a declaration to a type in an external + // module. + lldb::ModuleSP GetModuleForType(const DWARFDIE &die); + + typedef llvm::SmallPtrSet<const DWARFDebugInfoEntry *, 4> DIEPointerSet; + typedef llvm::DenseMap<const DWARFDebugInfoEntry *, clang::DeclContext *> + DIEToDeclContextMap; + // typedef llvm::DenseMap<const clang::DeclContext *, DIEPointerSet> + // DeclContextToDIEMap; + typedef std::multimap<const clang::DeclContext *, const DWARFDIE> + DeclContextToDIEMap; + typedef llvm::DenseMap<const DWARFDebugInfoEntry *, clang::Decl *> + DIEToDeclMap; + typedef llvm::DenseMap<const clang::Decl *, DIEPointerSet> DeclToDIEMap; + + lldb_private::ClangASTContext &m_ast; + DIEToDeclMap m_die_to_decl; + DeclToDIEMap m_decl_to_die; + DIEToDeclContextMap m_die_to_decl_ctx; + DeclContextToDIEMap m_decl_ctx_to_die; + std::unique_ptr<lldb_private::ClangASTImporter> m_clang_ast_importer_up; +}; + +#endif // SymbolFileDWARF_DWARFASTParserClang_h_ diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFAbbreviationDeclaration.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFAbbreviationDeclaration.cpp new file mode 100644 index 000000000000..6128163a2926 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFAbbreviationDeclaration.cpp @@ -0,0 +1,89 @@ +//===-- DWARFAbbreviationDeclaration.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 "DWARFAbbreviationDeclaration.h" + +#include "lldb/Core/dwarf.h" +#include "lldb/Utility/Stream.h" + +#include "llvm/Object/Error.h" + +#include "DWARFFormValue.h" + +using namespace lldb_private; + +DWARFAbbreviationDeclaration::DWARFAbbreviationDeclaration() + : m_code(InvalidCode), m_tag(0), m_has_children(0), m_attributes() {} + +DWARFAbbreviationDeclaration::DWARFAbbreviationDeclaration(dw_tag_t tag, + uint8_t has_children) + : m_code(InvalidCode), m_tag(tag), m_has_children(has_children), + m_attributes() {} + +llvm::Expected<DWARFEnumState> +DWARFAbbreviationDeclaration::extract(const DWARFDataExtractor &data, + lldb::offset_t *offset_ptr) { + m_code = data.GetULEB128(offset_ptr); + if (m_code == 0) + return DWARFEnumState::Complete; + + m_attributes.clear(); + m_tag = data.GetULEB128(offset_ptr); + if (m_tag == DW_TAG_null) + return llvm::make_error<llvm::object::GenericBinaryError>( + "abbrev decl requires non-null tag."); + + m_has_children = data.GetU8(offset_ptr); + + while (data.ValidOffset(*offset_ptr)) { + dw_attr_t attr = data.GetULEB128(offset_ptr); + dw_form_t form = data.GetULEB128(offset_ptr); + + // This is the last attribute for this abbrev decl, but there may still be + // more abbrev decls, so return MoreItems to indicate to the caller that + // they should call this function again. + if (!attr && !form) + return DWARFEnumState::MoreItems; + + if (!attr || !form) + return llvm::make_error<llvm::object::GenericBinaryError>( + "malformed abbreviation declaration attribute"); + + DWARFFormValue::ValueType val; + + if (form == DW_FORM_implicit_const) + val.value.sval = data.GetULEB128(offset_ptr); + + m_attributes.push_back(DWARFAttribute(attr, form, val)); + } + + return llvm::make_error<llvm::object::GenericBinaryError>( + "abbreviation declaration attribute list not terminated with a null " + "entry"); +} + +bool DWARFAbbreviationDeclaration::IsValid() { + return m_code != 0 && m_tag != 0; +} + +uint32_t +DWARFAbbreviationDeclaration::FindAttributeIndex(dw_attr_t attr) const { + uint32_t i; + const uint32_t kNumAttributes = m_attributes.size(); + for (i = 0; i < kNumAttributes; ++i) { + if (m_attributes[i].get_attr() == attr) + return i; + } + return DW_INVALID_INDEX; +} + +bool DWARFAbbreviationDeclaration:: +operator==(const DWARFAbbreviationDeclaration &rhs) const { + return Tag() == rhs.Tag() && HasChildren() == rhs.HasChildren() && + m_attributes == rhs.m_attributes; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFAbbreviationDeclaration.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFAbbreviationDeclaration.h new file mode 100644 index 000000000000..c0cf8823a368 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFAbbreviationDeclaration.h @@ -0,0 +1,65 @@ +//===-- DWARFAbbreviationDeclaration.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 liblldb_DWARFAbbreviationDeclaration_h_ +#define liblldb_DWARFAbbreviationDeclaration_h_ + +#include "DWARFAttribute.h" +#include "DWARFDefines.h" +#include "SymbolFileDWARF.h" +#include "llvm/Support/Error.h" + +class DWARFAbbreviationDeclaration { +public: + enum { InvalidCode = 0 }; + DWARFAbbreviationDeclaration(); + + // For hand crafting an abbreviation declaration + DWARFAbbreviationDeclaration(dw_tag_t tag, uint8_t has_children); + + dw_uleb128_t Code() const { return m_code; } + void SetCode(dw_uleb128_t code) { m_code = code; } + dw_tag_t Tag() const { return m_tag; } + bool HasChildren() const { return m_has_children; } + size_t NumAttributes() const { return m_attributes.size(); } + dw_form_t GetFormByIndex(uint32_t idx) const { + return m_attributes.size() > idx ? m_attributes[idx].get_form() : 0; + } + + // idx is assumed to be valid when calling GetAttrAndFormByIndex() + void GetAttrAndFormValueByIndex(uint32_t idx, dw_attr_t &attr, + DWARFFormValue &form_value) const { + m_attributes[idx].get(attr, form_value.FormRef(), form_value.ValueRef()); + } + dw_form_t GetFormByIndexUnchecked(uint32_t idx) const { + return m_attributes[idx].get_form(); + } + uint32_t FindAttributeIndex(dw_attr_t attr) const; + + /// Extract one abbreviation declaration and all of its associated attributes. + /// Possible return values: + /// DWARFEnumState::Complete - the extraction completed successfully. This + /// was the last abbrev decl in a sequence, and the user should not call + /// this function again. + /// DWARFEnumState::MoreItems - the extraction completed successfully. The + /// user should call this function again to retrieve the next decl. + /// llvm::Error - A parsing error occurred. The debug info is malformed. + llvm::Expected<lldb_private::DWARFEnumState> + extract(const lldb_private::DWARFDataExtractor &data, + lldb::offset_t *offset_ptr); + bool IsValid(); + bool operator==(const DWARFAbbreviationDeclaration &rhs) const; + +protected: + dw_uleb128_t m_code; + dw_tag_t m_tag; + uint8_t m_has_children; + DWARFAttribute::collection m_attributes; +}; + +#endif // liblldb_DWARFAbbreviationDeclaration_h_ diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFAttribute.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFAttribute.cpp new file mode 100644 index 000000000000..b3594a455680 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFAttribute.cpp @@ -0,0 +1,58 @@ +//===-- DWARFAttribute.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 "DWARFAttribute.h" +#include "DWARFUnit.h" +#include "DWARFDebugInfo.h" + +DWARFAttributes::DWARFAttributes() : m_infos() {} + +DWARFAttributes::~DWARFAttributes() {} + +uint32_t DWARFAttributes::FindAttributeIndex(dw_attr_t attr) const { + collection::const_iterator end = m_infos.end(); + collection::const_iterator beg = m_infos.begin(); + collection::const_iterator pos; + for (pos = beg; pos != end; ++pos) { + if (pos->attr.get_attr() == attr) + return std::distance(beg, pos); + } + return UINT32_MAX; +} + +void DWARFAttributes::Append(const DWARFUnit *cu, dw_offset_t attr_die_offset, + dw_attr_t attr, dw_form_t form) { + AttributeValue attr_value = { + cu, attr_die_offset, {attr, form, DWARFFormValue::ValueType()}}; + m_infos.push_back(attr_value); +} + +bool DWARFAttributes::ExtractFormValueAtIndex( + uint32_t i, DWARFFormValue &form_value) const { + const DWARFUnit *cu = CompileUnitAtIndex(i); + form_value.SetUnit(cu); + form_value.SetForm(FormAtIndex(i)); + lldb::offset_t offset = DIEOffsetAtIndex(i); + return form_value.ExtractValue(cu->GetData(), &offset); +} + +DWARFDIE +DWARFAttributes::FormValueAsReference(dw_attr_t attr) const { + const uint32_t attr_idx = FindAttributeIndex(attr); + if (attr_idx != UINT32_MAX) + return FormValueAsReferenceAtIndex(attr_idx); + return {}; +} + +DWARFDIE +DWARFAttributes::FormValueAsReferenceAtIndex(uint32_t i) const { + DWARFFormValue form_value; + if (ExtractFormValueAtIndex(i, form_value)) + return form_value.Reference(); + return {}; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFAttribute.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFAttribute.h new file mode 100644 index 000000000000..58427b19100a --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFAttribute.h @@ -0,0 +1,85 @@ +//===-- DWARFAttribute.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 SymbolFileDWARF_DWARFAttribute_h_ +#define SymbolFileDWARF_DWARFAttribute_h_ + +#include "DWARFDefines.h" +#include "DWARFFormValue.h" +#include "llvm/ADT/SmallVector.h" +#include <vector> + +class DWARFUnit; + +class DWARFAttribute { +public: + DWARFAttribute(dw_attr_t attr, dw_form_t form, + DWARFFormValue::ValueType value) + : m_attr(attr), m_form(form), m_value(value) {} + + void set(dw_attr_t attr, dw_form_t form) { + m_attr = attr; + m_form = form; + } + dw_attr_t get_attr() const { return m_attr; } + dw_form_t get_form() const { return m_form; } + void get(dw_attr_t &attr, dw_form_t &form, + DWARFFormValue::ValueType &val) const { + attr = m_attr; + form = m_form; + val = m_value; + } + bool operator==(const DWARFAttribute &rhs) const { + return m_attr == rhs.m_attr && m_form == rhs.m_form; + } + typedef std::vector<DWARFAttribute> collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + +protected: + dw_attr_t m_attr; + dw_form_t m_form; + DWARFFormValue::ValueType m_value; +}; + +class DWARFAttributes { +public: + DWARFAttributes(); + ~DWARFAttributes(); + + void Append(const DWARFUnit *cu, dw_offset_t attr_die_offset, + dw_attr_t attr, dw_form_t form); + const DWARFUnit *CompileUnitAtIndex(uint32_t i) const { + return m_infos[i].cu; + } + dw_offset_t DIEOffsetAtIndex(uint32_t i) const { + return m_infos[i].die_offset; + } + dw_attr_t AttributeAtIndex(uint32_t i) const { + return m_infos[i].attr.get_attr(); + } + dw_attr_t FormAtIndex(uint32_t i) const { return m_infos[i].attr.get_form(); } + bool ExtractFormValueAtIndex(uint32_t i, DWARFFormValue &form_value) const; + DWARFDIE FormValueAsReferenceAtIndex(uint32_t i) const; + DWARFDIE FormValueAsReference(dw_attr_t attr) const; + uint32_t FindAttributeIndex(dw_attr_t attr) const; + void Clear() { m_infos.clear(); } + size_t Size() const { return m_infos.size(); } + +protected: + struct AttributeValue { + const DWARFUnit *cu; // Keep the compile unit with each attribute in + // case we have DW_FORM_ref_addr values + dw_offset_t die_offset; + DWARFAttribute attr; + }; + typedef llvm::SmallVector<AttributeValue, 8> collection; + collection m_infos; +}; + +#endif // SymbolFileDWARF_DWARFAttribute_h_ diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFBaseDIE.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFBaseDIE.cpp new file mode 100644 index 000000000000..96adb72c9532 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFBaseDIE.cpp @@ -0,0 +1,149 @@ +//===-- DWARFBaseDIE.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 "DWARFBaseDIE.h" + +#include "DWARFUnit.h" +#include "DWARFDebugInfoEntry.h" +#include "SymbolFileDWARF.h" + +#include "lldb/Core/Module.h" +#include "lldb/Symbol/ObjectFile.h" + +using namespace lldb_private; + +llvm::Optional<DIERef> DWARFBaseDIE::GetDIERef() const { + if (!IsValid()) + return llvm::None; + + return DIERef(m_cu->GetSymbolFileDWARF().GetDwoNum(), m_cu->GetDebugSection(), + m_die->GetOffset()); +} + +dw_tag_t DWARFBaseDIE::Tag() const { + if (m_die) + return m_die->Tag(); + else + return 0; +} + +const char *DWARFBaseDIE::GetTagAsCString() const { + return lldb_private::DW_TAG_value_to_name(Tag()); +} + +const char *DWARFBaseDIE::GetAttributeValueAsString(const dw_attr_t attr, + const char *fail_value) const { + if (IsValid()) + return m_die->GetAttributeValueAsString(GetCU(), attr, fail_value); + else + return fail_value; +} + +uint64_t DWARFBaseDIE::GetAttributeValueAsUnsigned(const dw_attr_t attr, + uint64_t fail_value) const { + if (IsValid()) + return m_die->GetAttributeValueAsUnsigned(GetCU(), attr, fail_value); + else + return fail_value; +} + +uint64_t DWARFBaseDIE::GetAttributeValueAsAddress(const dw_attr_t attr, + uint64_t fail_value) const { + if (IsValid()) + return m_die->GetAttributeValueAsAddress(GetCU(), attr, fail_value); + else + return fail_value; +} + +lldb::user_id_t DWARFBaseDIE::GetID() const { + if (IsValid()) + return GetDWARF()->GetUID(*this); + return LLDB_INVALID_UID; +} + +const char *DWARFBaseDIE::GetName() const { + if (IsValid()) + return m_die->GetName(m_cu); + else + return nullptr; +} + +lldb::LanguageType DWARFBaseDIE::GetLanguage() const { + if (IsValid()) + return m_cu->GetLanguageType(); + else + return lldb::eLanguageTypeUnknown; +} + +lldb::ModuleSP DWARFBaseDIE::GetModule() const { + SymbolFileDWARF *dwarf = GetDWARF(); + if (dwarf) + return dwarf->GetObjectFile()->GetModule(); + else + return lldb::ModuleSP(); +} + +dw_offset_t DWARFBaseDIE::GetOffset() const { + if (IsValid()) + return m_die->GetOffset(); + else + return DW_INVALID_OFFSET; +} + +SymbolFileDWARF *DWARFBaseDIE::GetDWARF() const { + if (m_cu) + return &m_cu->GetSymbolFileDWARF(); + else + return nullptr; +} + +lldb_private::TypeSystem *DWARFBaseDIE::GetTypeSystem() const { + if (m_cu) + return m_cu->GetTypeSystem(); + else + return nullptr; +} + +DWARFASTParser *DWARFBaseDIE::GetDWARFParser() const { + lldb_private::TypeSystem *type_system = GetTypeSystem(); + if (type_system) + return type_system->GetDWARFParser(); + else + return nullptr; +} + +bool DWARFBaseDIE::HasChildren() const { + return m_die && m_die->HasChildren(); +} + +bool DWARFBaseDIE::Supports_DW_AT_APPLE_objc_complete_type() const { + return IsValid() && GetDWARF()->Supports_DW_AT_APPLE_objc_complete_type(m_cu); +} + +size_t DWARFBaseDIE::GetAttributes(DWARFAttributes &attributes, + uint32_t depth) const { + if (IsValid()) + return m_die->GetAttributes(m_cu, attributes, depth); + if (depth == 0) + attributes.Clear(); + return 0; +} + +bool operator==(const DWARFBaseDIE &lhs, const DWARFBaseDIE &rhs) { + return lhs.GetDIE() == rhs.GetDIE() && lhs.GetCU() == rhs.GetCU(); +} + +bool operator!=(const DWARFBaseDIE &lhs, const DWARFBaseDIE &rhs) { + return !(lhs == rhs); +} + +const DWARFDataExtractor &DWARFBaseDIE::GetData() const { + // Clients must check if this DIE is valid before calling this function. + assert(IsValid()); + return m_cu->GetData(); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFBaseDIE.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFBaseDIE.h new file mode 100644 index 000000000000..0058043017cd --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFBaseDIE.h @@ -0,0 +1,127 @@ +//===-- DWARFBaseDIE.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 SymbolFileDWARF_DWARFBaseDIE_h_ +#define SymbolFileDWARF_DWARFBaseDIE_h_ + +#include "lldb/Core/dwarf.h" +#include "lldb/lldb-types.h" + +class DIERef; +class DWARFASTParser; +class DWARFAttributes; +class DWARFUnit; +class DWARFDebugInfoEntry; +class DWARFDeclContext; +class SymbolFileDWARF; + +class DWARFBaseDIE { +public: + DWARFBaseDIE() : m_cu(nullptr), m_die(nullptr) {} + + DWARFBaseDIE(DWARFUnit *cu, DWARFDebugInfoEntry *die) + : m_cu(cu), m_die(die) {} + + DWARFBaseDIE(const DWARFUnit *cu, DWARFDebugInfoEntry *die) + : m_cu(const_cast<DWARFUnit *>(cu)), m_die(die) {} + + DWARFBaseDIE(DWARFUnit *cu, const DWARFDebugInfoEntry *die) + : m_cu(cu), m_die(const_cast<DWARFDebugInfoEntry *>(die)) {} + + DWARFBaseDIE(const DWARFUnit *cu, const DWARFDebugInfoEntry *die) + : m_cu(const_cast<DWARFUnit *>(cu)), + m_die(const_cast<DWARFDebugInfoEntry *>(die)) {} + + // Tests + explicit operator bool() const { return IsValid(); } + + bool IsValid() const { return m_cu && m_die; } + + bool HasChildren() const; + + bool Supports_DW_AT_APPLE_objc_complete_type() const; + + // Accessors + SymbolFileDWARF *GetDWARF() const; + + DWARFUnit *GetCU() const { return m_cu; } + + DWARFDebugInfoEntry *GetDIE() const { return m_die; } + + llvm::Optional<DIERef> GetDIERef() const; + + lldb_private::TypeSystem *GetTypeSystem() const; + + DWARFASTParser *GetDWARFParser() const; + + void Set(DWARFUnit *cu, DWARFDebugInfoEntry *die) { + if (cu && die) { + m_cu = cu; + m_die = die; + } else { + Clear(); + } + } + + void Clear() { + m_cu = nullptr; + m_die = nullptr; + } + + // Get the data that contains the attribute values for this DIE. Support + // for .debug_types means that any DIE can have its data either in the + // .debug_info or the .debug_types section; this method will return the + // correct section data. + // + // Clients must validate that this object is valid before calling this. + const lldb_private::DWARFDataExtractor &GetData() const; + + // Accessing information about a DIE + dw_tag_t Tag() const; + + const char *GetTagAsCString() const; + + dw_offset_t GetOffset() const; + + // Get the LLDB user ID for this DIE. This is often just the DIE offset, + // but it might have a SymbolFileDWARF::GetID() in the high 32 bits if + // we are doing Darwin DWARF in .o file, or DWARF stand alone debug + // info. + lldb::user_id_t GetID() const; + + const char *GetName() const; + + lldb::LanguageType GetLanguage() const; + + lldb::ModuleSP GetModule() const; + + // Getting attribute values from the DIE. + // + // GetAttributeValueAsXXX() functions should only be used if you are + // looking for one or two attributes on a DIE. If you are trying to + // parse all attributes, use GetAttributes (...) instead + const char *GetAttributeValueAsString(const dw_attr_t attr, + const char *fail_value) const; + + uint64_t GetAttributeValueAsUnsigned(const dw_attr_t attr, + uint64_t fail_value) const; + + uint64_t GetAttributeValueAsAddress(const dw_attr_t attr, + uint64_t fail_value) const; + + size_t GetAttributes(DWARFAttributes &attributes, uint32_t depth = 0) const; + +protected: + DWARFUnit *m_cu; + DWARFDebugInfoEntry *m_die; +}; + +bool operator==(const DWARFBaseDIE &lhs, const DWARFBaseDIE &rhs); +bool operator!=(const DWARFBaseDIE &lhs, const DWARFBaseDIE &rhs); + +#endif // SymbolFileDWARF_DWARFBaseDIE_h_ diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.cpp new file mode 100644 index 000000000000..718f0537d6ed --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.cpp @@ -0,0 +1,115 @@ +//===-- DWARFCompileUnit.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 "DWARFCompileUnit.h" +#include "DWARFDebugAranges.h" +#include "SymbolFileDWARFDebugMap.h" + +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/LineTable.h" +#include "lldb/Utility/Stream.h" + +using namespace lldb; +using namespace lldb_private; + +void DWARFCompileUnit::Dump(Stream *s) const { + s->Printf("0x%8.8x: Compile Unit: length = 0x%8.8x, version = 0x%4.4x, " + "abbr_offset = 0x%8.8x, addr_size = 0x%2.2x (next CU at " + "{0x%8.8x})\n", + GetOffset(), GetLength(), GetVersion(), GetAbbrevOffset(), + GetAddressByteSize(), GetNextUnitOffset()); +} + +void DWARFCompileUnit::BuildAddressRangeTable( + DWARFDebugAranges *debug_aranges) { + // This function is usually called if there in no .debug_aranges section in + // order to produce a compile unit level set of address ranges that is + // accurate. + + size_t num_debug_aranges = debug_aranges->GetNumRanges(); + + // First get the compile unit DIE only and check if it has a DW_AT_ranges + const DWARFDebugInfoEntry *die = GetUnitDIEPtrOnly(); + + const dw_offset_t cu_offset = GetOffset(); + if (die) { + DWARFRangeList ranges; + const size_t num_ranges = + die->GetAttributeAddressRanges(this, ranges, false); + if (num_ranges > 0) { + // This compile unit has DW_AT_ranges, assume this is correct if it is + // present since clang no longer makes .debug_aranges by default and it + // emits DW_AT_ranges for DW_TAG_compile_units. GCC also does this with + // recent GCC builds. + for (size_t i = 0; i < num_ranges; ++i) { + const DWARFRangeList::Entry &range = ranges.GetEntryRef(i); + debug_aranges->AppendRange(cu_offset, range.GetRangeBase(), + range.GetRangeEnd()); + } + + return; // We got all of our ranges from the DW_AT_ranges attribute + } + } + // We don't have a DW_AT_ranges attribute, so we need to parse the DWARF + + // If the DIEs weren't parsed, then we don't want all dies for all compile + // units to stay loaded when they weren't needed. So we can end up parsing + // the DWARF and then throwing them all away to keep memory usage down. + ScopedExtractDIEs clear_dies(ExtractDIEsScoped()); + + die = DIEPtr(); + if (die) + die->BuildAddressRangeTable(this, debug_aranges); + + if (debug_aranges->GetNumRanges() == num_debug_aranges) { + // We got nothing from the functions, maybe we have a line tables only + // situation. Check the line tables and build the arange table from this. + SymbolContext sc; + sc.comp_unit = m_dwarf.GetCompUnitForDWARFCompUnit(*this); + if (sc.comp_unit) { + SymbolFileDWARFDebugMap *debug_map_sym_file = + m_dwarf.GetDebugMapSymfile(); + if (debug_map_sym_file == nullptr) { + if (LineTable *line_table = sc.comp_unit->GetLineTable()) { + LineTable::FileAddressRanges file_ranges; + const bool append = true; + const size_t num_ranges = + line_table->GetContiguousFileAddressRanges(file_ranges, append); + for (uint32_t idx = 0; idx < num_ranges; ++idx) { + const LineTable::FileAddressRanges::Entry &range = + file_ranges.GetEntryRef(idx); + debug_aranges->AppendRange(cu_offset, range.GetRangeBase(), + range.GetRangeEnd()); + } + } + } else + debug_map_sym_file->AddOSOARanges(&m_dwarf, debug_aranges); + } + } + + if (debug_aranges->GetNumRanges() == num_debug_aranges) { + // We got nothing from the functions, maybe we have a line tables only + // situation. Check the line tables and build the arange table from this. + SymbolContext sc; + sc.comp_unit = m_dwarf.GetCompUnitForDWARFCompUnit(*this); + if (sc.comp_unit) { + if (LineTable *line_table = sc.comp_unit->GetLineTable()) { + LineTable::FileAddressRanges file_ranges; + const bool append = true; + const size_t num_ranges = + line_table->GetContiguousFileAddressRanges(file_ranges, append); + for (uint32_t idx = 0; idx < num_ranges; ++idx) { + const LineTable::FileAddressRanges::Entry &range = + file_ranges.GetEntryRef(idx); + debug_aranges->AppendRange(GetOffset(), range.GetRangeBase(), + range.GetRangeEnd()); + } + } + } + } +} diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.h new file mode 100644 index 000000000000..75647dbb082f --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.h @@ -0,0 +1,35 @@ +//===-- DWARFCompileUnit.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 SymbolFileDWARF_DWARFCompileUnit_h_ +#define SymbolFileDWARF_DWARFCompileUnit_h_ + +#include "DWARFUnit.h" +#include "llvm/Support/Error.h" + +class DWARFCompileUnit : public DWARFUnit { +public: + void BuildAddressRangeTable(DWARFDebugAranges *debug_aranges) override; + + void Dump(lldb_private::Stream *s) const override; + + static bool classof(const DWARFUnit *unit) { return !unit->IsTypeUnit(); } + +private: + DWARFCompileUnit(SymbolFileDWARF &dwarf, lldb::user_id_t uid, + const DWARFUnitHeader &header, + const DWARFAbbreviationDeclarationSet &abbrevs, + DIERef::Section section) + : DWARFUnit(dwarf, uid, header, abbrevs, section) {} + + DISALLOW_COPY_AND_ASSIGN(DWARFCompileUnit); + + friend class DWARFUnit; +}; + +#endif // SymbolFileDWARF_DWARFCompileUnit_h_ diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFContext.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFContext.cpp new file mode 100644 index 000000000000..eb307ce1cce1 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFContext.cpp @@ -0,0 +1,138 @@ +//===-- DWARFContext.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 "DWARFContext.h" + +#include "lldb/Core/Section.h" + +using namespace lldb; +using namespace lldb_private; + +static DWARFDataExtractor LoadSection(SectionList *section_list, + SectionType section_type) { + if (!section_list) + return DWARFDataExtractor(); + + auto section_sp = section_list->FindSectionByType(section_type, true); + if (!section_sp) + return DWARFDataExtractor(); + + DWARFDataExtractor data; + section_sp->GetSectionData(data); + return data; +} + +const DWARFDataExtractor & +DWARFContext::LoadOrGetSection(SectionType main_section_type, + llvm::Optional<SectionType> dwo_section_type, + SectionData &data) { + llvm::call_once(data.flag, [&] { + if (dwo_section_type && isDwo()) + data.data = LoadSection(m_dwo_section_list, *dwo_section_type); + else + data.data = LoadSection(m_main_section_list, main_section_type); + }); + return data.data; +} + +const DWARFDataExtractor &DWARFContext::getOrLoadAbbrevData() { + return LoadOrGetSection(eSectionTypeDWARFDebugAbbrev, + eSectionTypeDWARFDebugAbbrevDwo, m_data_debug_abbrev); +} + +const DWARFDataExtractor &DWARFContext::getOrLoadArangesData() { + return LoadOrGetSection(eSectionTypeDWARFDebugAranges, llvm::None, + m_data_debug_aranges); +} + +const DWARFDataExtractor &DWARFContext::getOrLoadAddrData() { + return LoadOrGetSection(eSectionTypeDWARFDebugAddr, llvm::None, + m_data_debug_addr); +} + +const DWARFDataExtractor &DWARFContext::getOrLoadDebugInfoData() { + return LoadOrGetSection(eSectionTypeDWARFDebugInfo, + eSectionTypeDWARFDebugInfoDwo, m_data_debug_info); +} + +const DWARFDataExtractor &DWARFContext::getOrLoadLineData() { + return LoadOrGetSection(eSectionTypeDWARFDebugLine, llvm::None, + m_data_debug_line); +} + +const DWARFDataExtractor &DWARFContext::getOrLoadLineStrData() { + return LoadOrGetSection(eSectionTypeDWARFDebugLineStr, llvm::None, + m_data_debug_line_str); +} + +const DWARFDataExtractor &DWARFContext::getOrLoadMacroData() { + return LoadOrGetSection(eSectionTypeDWARFDebugMacro, llvm::None, + m_data_debug_macro); +} + +const DWARFDataExtractor &DWARFContext::getOrLoadRangesData() { + return LoadOrGetSection(eSectionTypeDWARFDebugRanges, llvm::None, + m_data_debug_ranges); +} + +const DWARFDataExtractor &DWARFContext::getOrLoadRngListsData() { + return LoadOrGetSection(eSectionTypeDWARFDebugRngLists, llvm::None, + m_data_debug_rnglists); +} + +const DWARFDataExtractor &DWARFContext::getOrLoadStrData() { + return LoadOrGetSection(eSectionTypeDWARFDebugStr, + eSectionTypeDWARFDebugStrDwo, m_data_debug_str); +} + +const DWARFDataExtractor &DWARFContext::getOrLoadStrOffsetsData() { + return LoadOrGetSection(eSectionTypeDWARFDebugStrOffsets, + eSectionTypeDWARFDebugStrOffsetsDwo, + m_data_debug_str_offsets); +} + +const DWARFDataExtractor &DWARFContext::getOrLoadDebugTypesData() { + return LoadOrGetSection(eSectionTypeDWARFDebugTypes, + eSectionTypeDWARFDebugTypesDwo, m_data_debug_types); +} + +llvm::DWARFContext &DWARFContext::GetAsLLVM() { + if (!m_llvm_context) { + llvm::StringMap<std::unique_ptr<llvm::MemoryBuffer>> section_map; + uint8_t addr_size = 0; + + auto AddSection = [&](Section §ion) { + DataExtractor section_data; + section.GetSectionData(section_data); + + // Set the address size the first time we see it. + if (addr_size == 0) + addr_size = section_data.GetByteSize(); + + llvm::StringRef data = llvm::toStringRef(section_data.GetData()); + llvm::StringRef name = section.GetName().GetStringRef(); + if (name.startswith(".")) + name = name.drop_front(); + section_map.try_emplace( + name, llvm::MemoryBuffer::getMemBuffer(data, name, false)); + }; + + if (m_main_section_list) { + for (auto §ion : *m_main_section_list) + AddSection(*section); + } + + if (m_dwo_section_list) { + for (auto §ion : *m_dwo_section_list) + AddSection(*section); + } + + m_llvm_context = llvm::DWARFContext::create(section_map, addr_size); + } + return *m_llvm_context; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFContext.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFContext.h new file mode 100644 index 000000000000..add042384039 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFContext.h @@ -0,0 +1,74 @@ +//===-- DWARFContext.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 LLDB_PLUGINS_SYMBOLFILE_DWARF_DWARFCONTEXT_H +#define LLDB_PLUGINS_SYMBOLFILE_DWARF_DWARFCONTEXT_H + +#include "DWARFDataExtractor.h" +#include "lldb/Core/Section.h" +#include "llvm/ADT/Optional.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/Support/Threading.h" +#include <memory> + +namespace lldb_private { +class DWARFContext { +private: + SectionList *m_main_section_list; + SectionList *m_dwo_section_list; + mutable std::unique_ptr<llvm::DWARFContext> m_llvm_context; + + struct SectionData { + llvm::once_flag flag; + DWARFDataExtractor data; + }; + + SectionData m_data_debug_abbrev; + SectionData m_data_debug_addr; + SectionData m_data_debug_aranges; + SectionData m_data_debug_info; + SectionData m_data_debug_line; + SectionData m_data_debug_line_str; + SectionData m_data_debug_macro; + SectionData m_data_debug_ranges; + SectionData m_data_debug_rnglists; + SectionData m_data_debug_str; + SectionData m_data_debug_str_offsets; + SectionData m_data_debug_types; + + bool isDwo() { return m_dwo_section_list != nullptr; } + + const DWARFDataExtractor & + LoadOrGetSection(lldb::SectionType main_section_type, + llvm::Optional<lldb::SectionType> dwo_section_type, + SectionData &data); + +public: + explicit DWARFContext(SectionList *main_section_list, + SectionList *dwo_section_list) + : m_main_section_list(main_section_list), + m_dwo_section_list(dwo_section_list) {} + + const DWARFDataExtractor &getOrLoadAbbrevData(); + const DWARFDataExtractor &getOrLoadAddrData(); + const DWARFDataExtractor &getOrLoadArangesData(); + const DWARFDataExtractor &getOrLoadDebugInfoData(); + const DWARFDataExtractor &getOrLoadLineData(); + const DWARFDataExtractor &getOrLoadLineStrData(); + const DWARFDataExtractor &getOrLoadMacroData(); + const DWARFDataExtractor &getOrLoadRangesData(); + const DWARFDataExtractor &getOrLoadRngListsData(); + const DWARFDataExtractor &getOrLoadStrData(); + const DWARFDataExtractor &getOrLoadStrOffsetsData(); + const DWARFDataExtractor &getOrLoadDebugTypesData(); + + llvm::DWARFContext &GetAsLLVM(); +}; +} // namespace lldb_private + +#endif diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.cpp new file mode 100644 index 000000000000..9d97ca15a252 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.cpp @@ -0,0 +1,483 @@ +//===-- DWARFDIE.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 "DWARFDIE.h" + +#include "DWARFASTParser.h" +#include "DWARFDebugInfo.h" +#include "DWARFDebugInfoEntry.h" +#include "DWARFDeclContext.h" +#include "DWARFUnit.h" + +using namespace lldb_private; + +namespace { + +/// Iterate through all DIEs elaborating (i.e. reachable by a chain of +/// DW_AT_specification and DW_AT_abstract_origin attributes) a given DIE. For +/// convenience, the starting die is included in the sequence as the first +/// item. +class ElaboratingDIEIterator + : public std::iterator<std::input_iterator_tag, DWARFDIE> { + + // The operating invariant is: top of m_worklist contains the "current" item + // and the rest of the list are items yet to be visited. An empty worklist + // means we've reached the end. + // Infinite recursion is prevented by maintaining a list of seen DIEs. + // Container sizes are optimized for the case of following DW_AT_specification + // and DW_AT_abstract_origin just once. + llvm::SmallVector<DWARFDIE, 2> m_worklist; + llvm::SmallSet<lldb::user_id_t, 3> m_seen; + + void Next() { + assert(!m_worklist.empty() && "Incrementing end iterator?"); + + // Pop the current item from the list. + DWARFDIE die = m_worklist.back(); + m_worklist.pop_back(); + + // And add back any items that elaborate it. + for (dw_attr_t attr : {DW_AT_specification, DW_AT_abstract_origin}) { + if (DWARFDIE d = die.GetReferencedDIE(attr)) + if (m_seen.insert(die.GetID()).second) + m_worklist.push_back(d); + } + } + +public: + /// An iterator starting at die d. + explicit ElaboratingDIEIterator(DWARFDIE d) : m_worklist(1, d) {} + + /// End marker + ElaboratingDIEIterator() {} + + const DWARFDIE &operator*() const { return m_worklist.back(); } + ElaboratingDIEIterator &operator++() { + Next(); + return *this; + } + ElaboratingDIEIterator operator++(int) { + ElaboratingDIEIterator I = *this; + Next(); + return I; + } + + friend bool operator==(const ElaboratingDIEIterator &a, + const ElaboratingDIEIterator &b) { + if (a.m_worklist.empty() || b.m_worklist.empty()) + return a.m_worklist.empty() == b.m_worklist.empty(); + return a.m_worklist.back() == b.m_worklist.back(); + } + friend bool operator!=(const ElaboratingDIEIterator &a, + const ElaboratingDIEIterator &b) { + return !(a == b); + } +}; + +llvm::iterator_range<ElaboratingDIEIterator> +elaborating_dies(const DWARFDIE &die) { + return llvm::make_range(ElaboratingDIEIterator(die), + ElaboratingDIEIterator()); +} +} // namespace + +DWARFDIE +DWARFDIE::GetParent() const { + if (IsValid()) + return DWARFDIE(m_cu, m_die->GetParent()); + else + return DWARFDIE(); +} + +DWARFDIE +DWARFDIE::GetFirstChild() const { + if (IsValid()) + return DWARFDIE(m_cu, m_die->GetFirstChild()); + else + return DWARFDIE(); +} + +DWARFDIE +DWARFDIE::GetSibling() const { + if (IsValid()) + return DWARFDIE(m_cu, m_die->GetSibling()); + else + return DWARFDIE(); +} + +DWARFDIE +DWARFDIE::GetReferencedDIE(const dw_attr_t attr) const { + if (IsValid()) + return m_die->GetAttributeValueAsReference(GetCU(), attr); + else + return {}; +} + +DWARFDIE +DWARFDIE::GetDIE(dw_offset_t die_offset) const { + if (IsValid()) + return m_cu->GetDIE(die_offset); + else + return DWARFDIE(); +} + +DWARFDIE +DWARFDIE::GetAttributeValueAsReferenceDIE(const dw_attr_t attr) const { + if (IsValid()) { + DWARFUnit *cu = GetCU(); + const bool check_specification_or_abstract_origin = true; + DWARFFormValue form_value; + if (m_die->GetAttributeValue(cu, attr, form_value, nullptr, + check_specification_or_abstract_origin)) + return form_value.Reference(); + } + return DWARFDIE(); +} + +DWARFDIE +DWARFDIE::LookupDeepestBlock(lldb::addr_t file_addr) const { + if (IsValid()) { + SymbolFileDWARF *dwarf = GetDWARF(); + DWARFUnit *cu = GetCU(); + DWARFDebugInfoEntry *function_die = nullptr; + DWARFDebugInfoEntry *block_die = nullptr; + if (m_die->LookupAddress(file_addr, cu, &function_die, &block_die)) { + if (block_die && block_die != function_die) { + if (cu->ContainsDIEOffset(block_die->GetOffset())) + return DWARFDIE(cu, block_die); + else + return DWARFDIE(dwarf->DebugInfo()->GetUnit(DIERef( + cu->GetSymbolFileDWARF().GetDwoNum(), + cu->GetDebugSection(), block_die->GetOffset())), + block_die); + } + } + } + return DWARFDIE(); +} + +const char *DWARFDIE::GetMangledName() const { + if (IsValid()) + return m_die->GetMangledName(m_cu); + else + return nullptr; +} + +const char *DWARFDIE::GetPubname() const { + if (IsValid()) + return m_die->GetPubname(m_cu); + else + return nullptr; +} + +const char *DWARFDIE::GetQualifiedName(std::string &storage) const { + if (IsValid()) + return m_die->GetQualifiedName(m_cu, storage); + else + return nullptr; +} + +// GetName +// +// Get value of the DW_AT_name attribute and place that value into the supplied +// stream object. If the DIE is a NULL object "NULL" is placed into the stream, +// and if no DW_AT_name attribute exists for the DIE then nothing is printed. +void DWARFDIE::GetName(Stream &s) const { + if (!IsValid()) + return; + if (GetDIE()->IsNULL()) { + s.PutCString("NULL"); + return; + } + const char *name = GetDIE()->GetAttributeValueAsString(GetCU(), DW_AT_name, nullptr, true); + if (!name) + return; + s.PutCString(name); +} + +// AppendTypeName +// +// Follows the type name definition down through all needed tags to end up with +// a fully qualified type name and dump the results to the supplied stream. +// This is used to show the name of types given a type identifier. +void DWARFDIE::AppendTypeName(Stream &s) const { + if (!IsValid()) + return; + if (GetDIE()->IsNULL()) { + s.PutCString("NULL"); + return; + } + if (const char *name = GetPubname()) { + s.PutCString(name); + return; + } + switch (Tag()) { + case DW_TAG_array_type: + break; // print out a "[]" after printing the full type of the element + // below + case DW_TAG_base_type: + s.PutCString("base "); + break; + case DW_TAG_class_type: + s.PutCString("class "); + break; + case DW_TAG_const_type: + s.PutCString("const "); + break; + case DW_TAG_enumeration_type: + s.PutCString("enum "); + break; + case DW_TAG_file_type: + s.PutCString("file "); + break; + case DW_TAG_interface_type: + s.PutCString("interface "); + break; + case DW_TAG_packed_type: + s.PutCString("packed "); + break; + case DW_TAG_pointer_type: + break; // print out a '*' after printing the full type below + case DW_TAG_ptr_to_member_type: + break; // print out a '*' after printing the full type below + case DW_TAG_reference_type: + break; // print out a '&' after printing the full type below + case DW_TAG_restrict_type: + s.PutCString("restrict "); + break; + case DW_TAG_set_type: + s.PutCString("set "); + break; + case DW_TAG_shared_type: + s.PutCString("shared "); + break; + case DW_TAG_string_type: + s.PutCString("string "); + break; + case DW_TAG_structure_type: + s.PutCString("struct "); + break; + case DW_TAG_subrange_type: + s.PutCString("subrange "); + break; + case DW_TAG_subroutine_type: + s.PutCString("function "); + break; + case DW_TAG_thrown_type: + s.PutCString("thrown "); + break; + case DW_TAG_union_type: + s.PutCString("union "); + break; + case DW_TAG_unspecified_type: + s.PutCString("unspecified "); + break; + case DW_TAG_volatile_type: + s.PutCString("volatile "); + break; + default: + return; + } + + // Follow the DW_AT_type if possible + if (DWARFDIE next_die = GetAttributeValueAsReferenceDIE(DW_AT_type)) + next_die.AppendTypeName(s); + + switch (Tag()) { + case DW_TAG_array_type: + s.PutCString("[]"); + break; + case DW_TAG_pointer_type: + s.PutChar('*'); + break; + case DW_TAG_ptr_to_member_type: + s.PutChar('*'); + break; + case DW_TAG_reference_type: + s.PutChar('&'); + break; + default: + break; + } +} + +lldb_private::Type *DWARFDIE::ResolveType() const { + if (IsValid()) + return GetDWARF()->ResolveType(*this, true); + else + return nullptr; +} + +lldb_private::Type *DWARFDIE::ResolveTypeUID(const DWARFDIE &die) const { + if (SymbolFileDWARF *dwarf = GetDWARF()) + return dwarf->ResolveTypeUID(die, true); + return nullptr; +} + +std::vector<DWARFDIE> DWARFDIE::GetDeclContextDIEs() const { + if (!IsValid()) + return {}; + + std::vector<DWARFDIE> result; + DWARFDIE parent = GetParentDeclContextDIE(); + while (parent.IsValid() && parent.GetDIE() != GetDIE()) { + result.push_back(std::move(parent)); + parent = parent.GetParentDeclContextDIE(); + } + + return result; +} + +void DWARFDIE::GetDWARFDeclContext(DWARFDeclContext &dwarf_decl_ctx) const { + if (IsValid()) { + dwarf_decl_ctx.SetLanguage(GetLanguage()); + m_die->GetDWARFDeclContext(GetCU(), dwarf_decl_ctx); + } else { + dwarf_decl_ctx.Clear(); + } +} + +void DWARFDIE::GetDeclContext(std::vector<CompilerContext> &context) const { + const dw_tag_t tag = Tag(); + if (tag == DW_TAG_compile_unit || tag == DW_TAG_partial_unit) + return; + DWARFDIE parent = GetParent(); + if (parent) + parent.GetDeclContext(context); + switch (tag) { + case DW_TAG_module: + context.push_back( + CompilerContext(CompilerContextKind::Module, ConstString(GetName()))); + break; + case DW_TAG_namespace: + context.push_back(CompilerContext(CompilerContextKind::Namespace, + ConstString(GetName()))); + break; + case DW_TAG_structure_type: + context.push_back(CompilerContext(CompilerContextKind::Structure, + ConstString(GetName()))); + break; + case DW_TAG_union_type: + context.push_back( + CompilerContext(CompilerContextKind::Union, ConstString(GetName()))); + break; + case DW_TAG_class_type: + context.push_back( + CompilerContext(CompilerContextKind::Class, ConstString(GetName()))); + break; + case DW_TAG_enumeration_type: + context.push_back(CompilerContext(CompilerContextKind::Enumeration, + ConstString(GetName()))); + break; + case DW_TAG_subprogram: + context.push_back(CompilerContext(CompilerContextKind::Function, + ConstString(GetPubname()))); + break; + case DW_TAG_variable: + context.push_back(CompilerContext(CompilerContextKind::Variable, + ConstString(GetPubname()))); + break; + case DW_TAG_typedef: + context.push_back( + CompilerContext(CompilerContextKind::Typedef, ConstString(GetName()))); + break; + default: + break; + } +} + +DWARFDIE +DWARFDIE::GetParentDeclContextDIE() const { + if (IsValid()) + return m_die->GetParentDeclContextDIE(m_cu); + else + return DWARFDIE(); +} + +bool DWARFDIE::IsStructUnionOrClass() const { + const dw_tag_t tag = Tag(); + return tag == DW_TAG_class_type || tag == DW_TAG_structure_type || + tag == DW_TAG_union_type; +} + +bool DWARFDIE::IsMethod() const { + for (DWARFDIE d : elaborating_dies(*this)) + if (d.GetParent().IsStructUnionOrClass()) + return true; + return false; +} + +DWARFDIE +DWARFDIE::GetContainingDWOModuleDIE() const { + if (IsValid()) { + DWARFDIE top_module_die; + // Now make sure this DIE is scoped in a DW_TAG_module tag and return true + // if so + for (DWARFDIE parent = GetParent(); parent.IsValid(); + parent = parent.GetParent()) { + const dw_tag_t tag = parent.Tag(); + if (tag == DW_TAG_module) + top_module_die = parent; + else if (tag == DW_TAG_compile_unit || tag == DW_TAG_partial_unit) + break; + } + + return top_module_die; + } + return DWARFDIE(); +} + +lldb::ModuleSP DWARFDIE::GetContainingDWOModule() const { + if (IsValid()) { + DWARFDIE dwo_module_die = GetContainingDWOModuleDIE(); + + if (dwo_module_die) { + const char *module_name = dwo_module_die.GetName(); + if (module_name) + return GetDWARF()->GetDWOModule(lldb_private::ConstString(module_name)); + } + } + return lldb::ModuleSP(); +} + +bool DWARFDIE::GetDIENamesAndRanges( + const char *&name, const char *&mangled, DWARFRangeList &ranges, + int &decl_file, int &decl_line, int &decl_column, int &call_file, + int &call_line, int &call_column, + lldb_private::DWARFExpression *frame_base) const { + if (IsValid()) { + return m_die->GetDIENamesAndRanges( + GetCU(), name, mangled, ranges, decl_file, decl_line, decl_column, + call_file, call_line, call_column, frame_base); + } else + return false; +} + +CompilerDecl DWARFDIE::GetDecl() const { + DWARFASTParser *dwarf_ast = GetDWARFParser(); + if (dwarf_ast) + return dwarf_ast->GetDeclForUIDFromDWARF(*this); + else + return CompilerDecl(); +} + +CompilerDeclContext DWARFDIE::GetDeclContext() const { + DWARFASTParser *dwarf_ast = GetDWARFParser(); + if (dwarf_ast) + return dwarf_ast->GetDeclContextForUIDFromDWARF(*this); + else + return CompilerDeclContext(); +} + +CompilerDeclContext DWARFDIE::GetContainingDeclContext() const { + DWARFASTParser *dwarf_ast = GetDWARFParser(); + if (dwarf_ast) + return dwarf_ast->GetDeclContextContainingUIDFromDWARF(*this); + else + return CompilerDeclContext(); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.h new file mode 100644 index 000000000000..7753ec9008cb --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.h @@ -0,0 +1,107 @@ +//===-- DWARFDIE.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 SymbolFileDWARF_DWARFDIE_h_ +#define SymbolFileDWARF_DWARFDIE_h_ + +#include "DWARFBaseDIE.h" +#include "llvm/ADT/SmallSet.h" + +class DWARFDIE : public DWARFBaseDIE { +public: + using DWARFBaseDIE::DWARFBaseDIE; + + // Tests + bool IsStructUnionOrClass() const; + + bool IsMethod() const; + + // Accessors + lldb::ModuleSP GetContainingDWOModule() const; + + DWARFDIE + GetContainingDWOModuleDIE() const; + + // Accessing information about a DIE + const char *GetMangledName() const; + + const char *GetPubname() const; + + const char *GetQualifiedName(std::string &storage) const; + + using DWARFBaseDIE::GetName; + void GetName(lldb_private::Stream &s) const; + + void AppendTypeName(lldb_private::Stream &s) const; + + lldb_private::Type *ResolveType() const; + + // Resolve a type by UID using this DIE's DWARF file + lldb_private::Type *ResolveTypeUID(const DWARFDIE &die) const; + + // Functions for obtaining DIE relations and references + + DWARFDIE + GetParent() const; + + DWARFDIE + GetFirstChild() const; + + DWARFDIE + GetSibling() const; + + DWARFDIE + GetReferencedDIE(const dw_attr_t attr) const; + + // Get a another DIE from the same DWARF file as this DIE. This will + // check the current DIE's compile unit first to see if "die_offset" is + // in the same compile unit, and fall back to checking the DWARF file. + DWARFDIE + GetDIE(dw_offset_t die_offset) const; + using DWARFBaseDIE::GetDIE; + + DWARFDIE + LookupDeepestBlock(lldb::addr_t file_addr) const; + + DWARFDIE + GetParentDeclContextDIE() const; + + // DeclContext related functions + std::vector<DWARFDIE> GetDeclContextDIEs() const; + + void GetDWARFDeclContext(DWARFDeclContext &dwarf_decl_ctx) const; + + /// Return this DIE's decl context as it is needed to look up types + /// in Clang's -gmodules debug info format. + void + GetDeclContext(std::vector<lldb_private::CompilerContext> &context) const; + + // Getting attribute values from the DIE. + // + // GetAttributeValueAsXXX() functions should only be used if you are + // looking for one or two attributes on a DIE. If you are trying to + // parse all attributes, use GetAttributes (...) instead + DWARFDIE + GetAttributeValueAsReferenceDIE(const dw_attr_t attr) const; + + bool GetDIENamesAndRanges(const char *&name, const char *&mangled, + DWARFRangeList &ranges, int &decl_file, + int &decl_line, int &decl_column, int &call_file, + int &call_line, int &call_column, + lldb_private::DWARFExpression *frame_base) const; + + // CompilerDecl related functions + + lldb_private::CompilerDecl GetDecl() const; + + lldb_private::CompilerDeclContext GetDeclContext() const; + + lldb_private::CompilerDeclContext GetContainingDeclContext() const; +}; + +#endif // SymbolFileDWARF_DWARFDIE_h_ diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDataExtractor.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDataExtractor.cpp new file mode 100644 index 000000000000..1678b228137f --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDataExtractor.cpp @@ -0,0 +1,30 @@ +//===-- DWARFDataExtractor.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 "DWARFDataExtractor.h" +#include "llvm/ADT/StringRef.h" + +namespace lldb_private { + +uint64_t +DWARFDataExtractor::GetDWARFInitialLength(lldb::offset_t *offset_ptr) const { + return GetU32(offset_ptr); +} + +dw_offset_t +DWARFDataExtractor::GetDWARFOffset(lldb::offset_t *offset_ptr) const { + return GetMaxU64(offset_ptr, GetDWARFSizeOfOffset()); +} + +llvm::DWARFDataExtractor DWARFDataExtractor::GetAsLLVM() const { + return llvm::DWARFDataExtractor( + llvm::StringRef(reinterpret_cast<const char *>(GetDataStart()), + GetByteSize()), + GetByteOrder() == lldb::eByteOrderLittle, GetAddressByteSize()); +} +} // namespace lldb_private diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDataExtractor.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDataExtractor.h new file mode 100644 index 000000000000..22db5e8c0b79 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDataExtractor.h @@ -0,0 +1,37 @@ +//===-- DWARFDataExtractor.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 liblldb_DWARFDataExtractor_h_ +#define liblldb_DWARFDataExtractor_h_ + +#include "lldb/Core/dwarf.h" +#include "lldb/Utility/DataExtractor.h" +#include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h" + +namespace lldb_private { + +class DWARFDataExtractor : public DataExtractor { +public: + DWARFDataExtractor() = default; + + DWARFDataExtractor(const DWARFDataExtractor &data, lldb::offset_t offset, + lldb::offset_t length) + : DataExtractor(data, offset, length) {} + + uint64_t GetDWARFInitialLength(lldb::offset_t *offset_ptr) const; + + dw_offset_t GetDWARFOffset(lldb::offset_t *offset_ptr) const; + + size_t GetDWARFSizeofInitialLength() const { return 4; } + size_t GetDWARFSizeOfOffset() const { return 4; } + + llvm::DWARFDataExtractor GetAsLLVM() const; +}; +} + +#endif // liblldb_DWARFDataExtractor_h_ diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAbbrev.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAbbrev.cpp new file mode 100644 index 000000000000..26301566a8e1 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAbbrev.cpp @@ -0,0 +1,146 @@ +//===-- DWARFDebugAbbrev.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 "DWARFDebugAbbrev.h" +#include "DWARFDataExtractor.h" +#include "lldb/Utility/Stream.h" + +using namespace lldb; +using namespace lldb_private; +using namespace std; + +// DWARFAbbreviationDeclarationSet::Clear() +void DWARFAbbreviationDeclarationSet::Clear() { + m_idx_offset = 0; + m_decls.clear(); +} + +// DWARFAbbreviationDeclarationSet::Extract() +llvm::Error +DWARFAbbreviationDeclarationSet::extract(const DWARFDataExtractor &data, + lldb::offset_t *offset_ptr) { + const lldb::offset_t begin_offset = *offset_ptr; + m_offset = begin_offset; + Clear(); + DWARFAbbreviationDeclaration abbrevDeclaration; + dw_uleb128_t prev_abbr_code = 0; + while (true) { + llvm::Expected<DWARFEnumState> es = + abbrevDeclaration.extract(data, offset_ptr); + if (!es) + return es.takeError(); + if (*es == DWARFEnumState::Complete) + break; + m_decls.push_back(abbrevDeclaration); + if (m_idx_offset == 0) + m_idx_offset = abbrevDeclaration.Code(); + else if (prev_abbr_code + 1 != abbrevDeclaration.Code()) { + // Out of order indexes, we can't do O(1) lookups... + m_idx_offset = UINT32_MAX; + } + prev_abbr_code = abbrevDeclaration.Code(); + } + return llvm::ErrorSuccess(); +} + +// DWARFAbbreviationDeclarationSet::GetAbbreviationDeclaration() +const DWARFAbbreviationDeclaration * +DWARFAbbreviationDeclarationSet::GetAbbreviationDeclaration( + dw_uleb128_t abbrCode) const { + if (m_idx_offset == UINT32_MAX) { + DWARFAbbreviationDeclarationCollConstIter pos; + DWARFAbbreviationDeclarationCollConstIter end = m_decls.end(); + for (pos = m_decls.begin(); pos != end; ++pos) { + if (pos->Code() == abbrCode) + return &(*pos); + } + } else { + uint32_t idx = abbrCode - m_idx_offset; + if (idx < m_decls.size()) + return &m_decls[idx]; + } + return nullptr; +} + + +// DWARFAbbreviationDeclarationSet::GetUnsupportedForms() +void DWARFAbbreviationDeclarationSet::GetUnsupportedForms( + std::set<dw_form_t> &invalid_forms) const { + for (const auto &abbr_decl : m_decls) { + const size_t num_attrs = abbr_decl.NumAttributes(); + for (size_t i=0; i<num_attrs; ++i) { + dw_form_t form = abbr_decl.GetFormByIndex(i); + if (!DWARFFormValue::FormIsSupported(form)) + invalid_forms.insert(form); + } + } +} + +// Encode +// +// Encode the abbreviation table onto the end of the buffer provided into a +// byte representation as would be found in a ".debug_abbrev" debug information +// section. +// void +// DWARFAbbreviationDeclarationSet::Encode(BinaryStreamBuf& debug_abbrev_buf) +// const +//{ +// DWARFAbbreviationDeclarationCollConstIter pos; +// DWARFAbbreviationDeclarationCollConstIter end = m_decls.end(); +// for (pos = m_decls.begin(); pos != end; ++pos) +// pos->Append(debug_abbrev_buf); +// debug_abbrev_buf.Append8(0); +//} + +// DWARFDebugAbbrev constructor +DWARFDebugAbbrev::DWARFDebugAbbrev() + : m_abbrevCollMap(), m_prev_abbr_offset_pos(m_abbrevCollMap.end()) {} + +// DWARFDebugAbbrev::Parse() +llvm::Error DWARFDebugAbbrev::parse(const DWARFDataExtractor &data) { + lldb::offset_t offset = 0; + + while (data.ValidOffset(offset)) { + uint32_t initial_cu_offset = offset; + DWARFAbbreviationDeclarationSet abbrevDeclSet; + + llvm::Error error = abbrevDeclSet.extract(data, &offset); + if (error) + return error; + + m_abbrevCollMap[initial_cu_offset] = abbrevDeclSet; + } + m_prev_abbr_offset_pos = m_abbrevCollMap.end(); + return llvm::ErrorSuccess(); +} + +// DWARFDebugAbbrev::GetAbbreviationDeclarationSet() +const DWARFAbbreviationDeclarationSet * +DWARFDebugAbbrev::GetAbbreviationDeclarationSet( + dw_offset_t cu_abbr_offset) const { + DWARFAbbreviationDeclarationCollMapConstIter end = m_abbrevCollMap.end(); + DWARFAbbreviationDeclarationCollMapConstIter pos; + if (m_prev_abbr_offset_pos != end && + m_prev_abbr_offset_pos->first == cu_abbr_offset) + return &(m_prev_abbr_offset_pos->second); + else { + pos = m_abbrevCollMap.find(cu_abbr_offset); + m_prev_abbr_offset_pos = pos; + } + + if (pos != m_abbrevCollMap.end()) + return &(pos->second); + return nullptr; +} + +// DWARFDebugAbbrev::GetUnsupportedForms() +void DWARFDebugAbbrev::GetUnsupportedForms( + std::set<dw_form_t> &invalid_forms) const { + for (const auto &pair : m_abbrevCollMap) + pair.second.GetUnsupportedForms(invalid_forms); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAbbrev.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAbbrev.h new file mode 100644 index 000000000000..9c4729326081 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAbbrev.h @@ -0,0 +1,81 @@ +//===-- DWARFDebugAbbrev.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 SymbolFileDWARF_DWARFDebugAbbrev_h_ +#define SymbolFileDWARF_DWARFDebugAbbrev_h_ + +#include <list> +#include <map> + +#include "lldb/lldb-private.h" + +#include "DWARFAbbreviationDeclaration.h" +#include "DWARFDefines.h" + +typedef std::vector<DWARFAbbreviationDeclaration> + DWARFAbbreviationDeclarationColl; +typedef DWARFAbbreviationDeclarationColl::iterator + DWARFAbbreviationDeclarationCollIter; +typedef DWARFAbbreviationDeclarationColl::const_iterator + DWARFAbbreviationDeclarationCollConstIter; + +class DWARFAbbreviationDeclarationSet { +public: + DWARFAbbreviationDeclarationSet() + : m_offset(DW_INVALID_OFFSET), m_idx_offset(0), m_decls() {} + + DWARFAbbreviationDeclarationSet(dw_offset_t offset, uint32_t idx_offset) + : m_offset(offset), m_idx_offset(idx_offset), m_decls() {} + + void Clear(); + dw_offset_t GetOffset() const { return m_offset; } + + /// Extract all abbrev decls in a set. Returns llvm::ErrorSuccess() on + /// success, and an appropriate llvm::Error object otherwise. + llvm::Error extract(const lldb_private::DWARFDataExtractor &data, + lldb::offset_t *offset_ptr); + // void Encode(BinaryStreamBuf& debug_abbrev_buf) const; + void GetUnsupportedForms(std::set<dw_form_t> &invalid_forms) const; + + const DWARFAbbreviationDeclaration * + GetAbbreviationDeclaration(dw_uleb128_t abbrCode) const; + + /// Unit test accessor functions. + /// @{ + uint32_t GetIndexOffset() const { return m_idx_offset; } + /// @} +private: + dw_offset_t m_offset; + uint32_t m_idx_offset; + std::vector<DWARFAbbreviationDeclaration> m_decls; +}; + +typedef std::map<dw_offset_t, DWARFAbbreviationDeclarationSet> + DWARFAbbreviationDeclarationCollMap; +typedef DWARFAbbreviationDeclarationCollMap::iterator + DWARFAbbreviationDeclarationCollMapIter; +typedef DWARFAbbreviationDeclarationCollMap::const_iterator + DWARFAbbreviationDeclarationCollMapConstIter; + +class DWARFDebugAbbrev { +public: + DWARFDebugAbbrev(); + const DWARFAbbreviationDeclarationSet * + GetAbbreviationDeclarationSet(dw_offset_t cu_abbr_offset) const; + /// Extract all abbreviations for a particular compile unit. Returns + /// llvm::ErrorSuccess() on success, and an appropriate llvm::Error object + /// otherwise. + llvm::Error parse(const lldb_private::DWARFDataExtractor &data); + void GetUnsupportedForms(std::set<dw_form_t> &invalid_forms) const; + +protected: + DWARFAbbreviationDeclarationCollMap m_abbrevCollMap; + mutable DWARFAbbreviationDeclarationCollMapConstIter m_prev_abbr_offset_pos; +}; + +#endif // SymbolFileDWARF_DWARFDebugAbbrev_h_ diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugArangeSet.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugArangeSet.cpp new file mode 100644 index 000000000000..86ce3b329b25 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugArangeSet.cpp @@ -0,0 +1,140 @@ +//===-- DWARFDebugArangeSet.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 "DWARFDebugArangeSet.h" +#include "DWARFDataExtractor.h" +#include "llvm/Object/Error.h" +#include <cassert> + +using namespace lldb_private; + +DWARFDebugArangeSet::DWARFDebugArangeSet() + : m_offset(DW_INVALID_OFFSET), m_header(), m_arange_descriptors() { + m_header.length = 0; + m_header.version = 0; + m_header.cu_offset = 0; + m_header.addr_size = 0; + m_header.seg_size = 0; +} + +void DWARFDebugArangeSet::Clear() { + m_offset = DW_INVALID_OFFSET; + m_header.length = 0; + m_header.version = 0; + m_header.cu_offset = 0; + m_header.addr_size = 0; + m_header.seg_size = 0; + m_arange_descriptors.clear(); +} + +llvm::Error DWARFDebugArangeSet::extract(const DWARFDataExtractor &data, + lldb::offset_t *offset_ptr) { + assert(data.ValidOffset(*offset_ptr)); + + m_arange_descriptors.clear(); + m_offset = *offset_ptr; + + // 7.20 Address Range Table + // + // Each set of entries in the table of address ranges contained in the + // .debug_aranges section begins with a header consisting of: a 4-byte + // length containing the length of the set of entries for this compilation + // unit, not including the length field itself; a 2-byte version identifier + // containing the value 2 for DWARF Version 2; a 4-byte offset into + // the.debug_infosection; a 1-byte unsigned integer containing the size in + // bytes of an address (or the offset portion of an address for segmented + // addressing) on the target system; and a 1-byte unsigned integer + // containing the size in bytes of a segment descriptor on the target + // system. This header is followed by a series of tuples. Each tuple + // consists of an address and a length, each in the size appropriate for an + // address on the target architecture. + m_header.length = data.GetDWARFInitialLength(offset_ptr); + m_header.version = data.GetU16(offset_ptr); + m_header.cu_offset = data.GetDWARFOffset(offset_ptr); + m_header.addr_size = data.GetU8(offset_ptr); + m_header.seg_size = data.GetU8(offset_ptr); + + // Try to avoid reading invalid arange sets by making sure: + // 1 - the version looks good + // 2 - the address byte size looks plausible + // 3 - the length seems to make sense + // size looks plausible + if (m_header.version < 2 || m_header.version > 5) + return llvm::make_error<llvm::object::GenericBinaryError>( + "Invalid arange header version"); + + if (m_header.addr_size != 4 && m_header.addr_size != 8) + return llvm::make_error<llvm::object::GenericBinaryError>( + "Invalid arange header address size"); + + if (m_header.length == 0) + return llvm::make_error<llvm::object::GenericBinaryError>( + "Invalid arange header length"); + + if (!data.ValidOffset(m_offset + sizeof(m_header.length) + m_header.length - + 1)) + return llvm::make_error<llvm::object::GenericBinaryError>( + "Invalid arange header length"); + + // The first tuple following the header in each set begins at an offset + // that is a multiple of the size of a single tuple (that is, twice the + // size of an address). The header is padded, if necessary, to the + // appropriate boundary. + const uint32_t header_size = *offset_ptr - m_offset; + const uint32_t tuple_size = m_header.addr_size << 1; + uint32_t first_tuple_offset = 0; + while (first_tuple_offset < header_size) + first_tuple_offset += tuple_size; + + *offset_ptr = m_offset + first_tuple_offset; + + Descriptor arangeDescriptor; + + static_assert(sizeof(arangeDescriptor.address) == + sizeof(arangeDescriptor.length), + "DWARFDebugArangeSet::Descriptor.address and " + "DWARFDebugArangeSet::Descriptor.length must have same size"); + + while (data.ValidOffset(*offset_ptr)) { + arangeDescriptor.address = data.GetMaxU64(offset_ptr, m_header.addr_size); + arangeDescriptor.length = data.GetMaxU64(offset_ptr, m_header.addr_size); + + // Each set of tuples is terminated by a 0 for the address and 0 for + // the length. + if (!arangeDescriptor.address && !arangeDescriptor.length) + return llvm::ErrorSuccess(); + + m_arange_descriptors.push_back(arangeDescriptor); + } + + return llvm::make_error<llvm::object::GenericBinaryError>( + "arange descriptors not terminated by null entry"); +} + +class DescriptorContainsAddress { +public: + DescriptorContainsAddress(dw_addr_t address) : m_address(address) {} + bool operator()(const DWARFDebugArangeSet::Descriptor &desc) const { + return (m_address >= desc.address) && + (m_address < (desc.address + desc.length)); + } + +private: + const dw_addr_t m_address; +}; + +dw_offset_t DWARFDebugArangeSet::FindAddress(dw_addr_t address) const { + DescriptorConstIter end = m_arange_descriptors.end(); + DescriptorConstIter pos = + std::find_if(m_arange_descriptors.begin(), end, // Range + DescriptorContainsAddress(address)); // Predicate + if (pos != end) + return m_header.cu_offset; + + return DW_INVALID_OFFSET; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugArangeSet.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugArangeSet.h new file mode 100644 index 000000000000..db0cf22a3f45 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugArangeSet.h @@ -0,0 +1,62 @@ +//===-- DWARFDebugArangeSet.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 SymbolFileDWARF_DWARFDebugArangeSet_h_ +#define SymbolFileDWARF_DWARFDebugArangeSet_h_ + +#include "lldb/Core/dwarf.h" +#include <cstdint> +#include <vector> + +class DWARFDebugArangeSet { +public: + struct Header { + uint32_t length; // The total length of the entries for that set, not + // including the length field itself. + uint16_t version; // The DWARF version number + uint32_t cu_offset; // The offset from the beginning of the .debug_info + // section of the compilation unit entry referenced by + // the table. + uint8_t addr_size; // The size in bytes of an address on the target + // architecture. For segmented addressing, this is the + // size of the offset portion of the address + uint8_t seg_size; // The size in bytes of a segment descriptor on the target + // architecture. If the target system uses a flat address + // space, this value is 0. + }; + + struct Descriptor { + dw_addr_t address; + dw_addr_t length; + dw_addr_t end_address() const { return address + length; } + }; + + DWARFDebugArangeSet(); + void Clear(); + void SetOffset(uint32_t offset) { m_offset = offset; } + llvm::Error extract(const lldb_private::DWARFDataExtractor &data, + lldb::offset_t *offset_ptr); + dw_offset_t FindAddress(dw_addr_t address) const; + size_t NumDescriptors() const { return m_arange_descriptors.size(); } + const Header &GetHeader() const { return m_header; } + + const Descriptor &GetDescriptorRef(uint32_t i) const { + return m_arange_descriptors[i]; + } + +protected: + typedef std::vector<Descriptor> DescriptorColl; + typedef DescriptorColl::iterator DescriptorIter; + typedef DescriptorColl::const_iterator DescriptorConstIter; + + uint32_t m_offset; + Header m_header; + DescriptorColl m_arange_descriptors; +}; + +#endif // SymbolFileDWARF_DWARFDebugArangeSet_h_ diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAranges.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAranges.cpp new file mode 100644 index 000000000000..ccf33e6dc341 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAranges.cpp @@ -0,0 +1,95 @@ +//===-- DWARFDebugAranges.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 "DWARFDebugAranges.h" +#include "DWARFDebugArangeSet.h" +#include "DWARFUnit.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/Timer.h" + +using namespace lldb; +using namespace lldb_private; + +// Constructor +DWARFDebugAranges::DWARFDebugAranges() : m_aranges() {} + +// CountArangeDescriptors +class CountArangeDescriptors { +public: + CountArangeDescriptors(uint32_t &count_ref) : count(count_ref) { + // printf("constructor CountArangeDescriptors()\n"); + } + void operator()(const DWARFDebugArangeSet &set) { + count += set.NumDescriptors(); + } + uint32_t &count; +}; + +// Extract +llvm::Error +DWARFDebugAranges::extract(const DWARFDataExtractor &debug_aranges_data) { + lldb::offset_t offset = 0; + + DWARFDebugArangeSet set; + Range range; + while (debug_aranges_data.ValidOffset(offset)) { + llvm::Error error = set.extract(debug_aranges_data, &offset); + if (!error) + return error; + + const uint32_t num_descriptors = set.NumDescriptors(); + if (num_descriptors > 0) { + const dw_offset_t cu_offset = set.GetHeader().cu_offset; + + for (uint32_t i = 0; i < num_descriptors; ++i) { + const DWARFDebugArangeSet::Descriptor &descriptor = + set.GetDescriptorRef(i); + m_aranges.Append(RangeToDIE::Entry(descriptor.address, + descriptor.length, cu_offset)); + } + } + set.Clear(); + } + return llvm::ErrorSuccess(); +} + +void DWARFDebugAranges::Dump(Log *log) const { + if (log == nullptr) + return; + + const size_t num_entries = m_aranges.GetSize(); + for (size_t i = 0; i < num_entries; ++i) { + const RangeToDIE::Entry *entry = m_aranges.GetEntryAtIndex(i); + if (entry) + log->Printf("0x%8.8x: [0x%" PRIx64 " - 0x%" PRIx64 ")", entry->data, + entry->GetRangeBase(), entry->GetRangeEnd()); + } +} + +void DWARFDebugAranges::AppendRange(dw_offset_t offset, dw_addr_t low_pc, + dw_addr_t high_pc) { + if (high_pc > low_pc) + m_aranges.Append(RangeToDIE::Entry(low_pc, high_pc - low_pc, offset)); +} + +void DWARFDebugAranges::Sort(bool minimize) { + static Timer::Category func_cat(LLVM_PRETTY_FUNCTION); + Timer scoped_timer(func_cat, "%s this = %p", LLVM_PRETTY_FUNCTION, + static_cast<void *>(this)); + + m_aranges.Sort(); + m_aranges.CombineConsecutiveEntriesWithEqualData(); +} + +// FindAddress +dw_offset_t DWARFDebugAranges::FindAddress(dw_addr_t address) const { + const RangeToDIE::Entry *entry = m_aranges.FindEntryThatContains(address); + if (entry) + return entry->data; + return DW_INVALID_OFFSET; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAranges.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAranges.h new file mode 100644 index 000000000000..74ba011b27af --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAranges.h @@ -0,0 +1,55 @@ +//===-- DWARFDebugAranges.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 SymbolFileDWARF_DWARFDebugAranges_h_ +#define SymbolFileDWARF_DWARFDebugAranges_h_ + +#include "lldb/Core/dwarf.h" +#include "lldb/Utility/RangeMap.h" +#include "llvm/Support/Error.h" + +class DWARFDebugAranges { +protected: + typedef lldb_private::RangeDataVector<dw_addr_t, uint32_t, dw_offset_t> + RangeToDIE; + +public: + typedef RangeToDIE::Entry Range; + typedef std::vector<RangeToDIE::Entry> RangeColl; + + DWARFDebugAranges(); + + void Clear() { m_aranges.Clear(); } + + llvm::Error + extract(const lldb_private::DWARFDataExtractor &debug_aranges_data); + + // Use append range multiple times and then call sort + void AppendRange(dw_offset_t cu_offset, dw_addr_t low_pc, dw_addr_t high_pc); + + void Sort(bool minimize); + + void Dump(lldb_private::Log *log) const; + + dw_offset_t FindAddress(dw_addr_t address) const; + + bool IsEmpty() const { return m_aranges.IsEmpty(); } + size_t GetNumRanges() const { return m_aranges.GetSize(); } + + dw_offset_t OffsetAtIndex(uint32_t idx) const { + const Range *range = m_aranges.GetEntryAtIndex(idx); + if (range) + return range->data; + return DW_INVALID_OFFSET; + } + +protected: + RangeToDIE m_aranges; +}; + +#endif // SymbolFileDWARF_DWARFDebugAranges_h_ diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.cpp new file mode 100644 index 000000000000..100f35f8c6b0 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.cpp @@ -0,0 +1,197 @@ +//===-- DWARFDebugInfo.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 "SymbolFileDWARF.h" + +#include <algorithm> +#include <set> + +#include "lldb/Host/PosixApi.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Utility/RegularExpression.h" +#include "lldb/Utility/Stream.h" +#include "llvm/Support/Casting.h" + +#include "DWARFCompileUnit.h" +#include "DWARFContext.h" +#include "DWARFDebugAranges.h" +#include "DWARFDebugInfo.h" +#include "DWARFDebugInfoEntry.h" +#include "DWARFFormValue.h" +#include "DWARFTypeUnit.h" + +using namespace lldb; +using namespace lldb_private; +using namespace std; + +// Constructor +DWARFDebugInfo::DWARFDebugInfo(SymbolFileDWARF &dwarf, + lldb_private::DWARFContext &context) + : m_dwarf(dwarf), m_context(context), m_units(), m_cu_aranges_up() {} + +llvm::Expected<DWARFDebugAranges &> DWARFDebugInfo::GetCompileUnitAranges() { + if (m_cu_aranges_up) + return *m_cu_aranges_up; + + m_cu_aranges_up = llvm::make_unique<DWARFDebugAranges>(); + const DWARFDataExtractor &debug_aranges_data = + m_context.getOrLoadArangesData(); + if (llvm::Error error = m_cu_aranges_up->extract(debug_aranges_data)) + return std::move(error); + + // Make a list of all CUs represented by the arange data in the file. + std::set<dw_offset_t> cus_with_data; + for (size_t n = 0; n < m_cu_aranges_up->GetNumRanges(); n++) { + dw_offset_t offset = m_cu_aranges_up->OffsetAtIndex(n); + if (offset != DW_INVALID_OFFSET) + cus_with_data.insert(offset); + } + + // Manually build arange data for everything that wasn't in the + // .debug_aranges table. + const size_t num_units = GetNumUnits(); + for (size_t idx = 0; idx < num_units; ++idx) { + DWARFUnit *cu = GetUnitAtIndex(idx); + + dw_offset_t offset = cu->GetOffset(); + if (cus_with_data.find(offset) == cus_with_data.end()) + cu->BuildAddressRangeTable(m_cu_aranges_up.get()); + } + + const bool minimize = true; + m_cu_aranges_up->Sort(minimize); + return *m_cu_aranges_up; +} + +void DWARFDebugInfo::ParseUnitsFor(DIERef::Section section) { + DWARFDataExtractor data = section == DIERef::Section::DebugTypes + ? m_context.getOrLoadDebugTypesData() + : m_context.getOrLoadDebugInfoData(); + lldb::offset_t offset = 0; + while (data.ValidOffset(offset)) { + llvm::Expected<DWARFUnitSP> unit_sp = + DWARFUnit::extract(m_dwarf, m_units.size(), data, section, &offset); + + if (!unit_sp) { + // FIXME: Propagate this error up. + llvm::consumeError(unit_sp.takeError()); + return; + } + + // If it didn't return an error, then it should be returning a valid Unit. + assert(*unit_sp); + m_units.push_back(*unit_sp); + offset = (*unit_sp)->GetNextUnitOffset(); + + if (auto *type_unit = llvm::dyn_cast<DWARFTypeUnit>(unit_sp->get())) { + m_type_hash_to_unit_index.emplace_back(type_unit->GetTypeHash(), + unit_sp.get()->GetID()); + } + } +} + +void DWARFDebugInfo::ParseUnitHeadersIfNeeded() { + if (!m_units.empty()) + return; + + ParseUnitsFor(DIERef::Section::DebugInfo); + ParseUnitsFor(DIERef::Section::DebugTypes); + llvm::sort(m_type_hash_to_unit_index, llvm::less_first()); +} + +size_t DWARFDebugInfo::GetNumUnits() { + ParseUnitHeadersIfNeeded(); + return m_units.size(); +} + +DWARFUnit *DWARFDebugInfo::GetUnitAtIndex(user_id_t idx) { + DWARFUnit *cu = nullptr; + if (idx < GetNumUnits()) + cu = m_units[idx].get(); + return cu; +} + +uint32_t DWARFDebugInfo::FindUnitIndex(DIERef::Section section, + dw_offset_t offset) { + ParseUnitHeadersIfNeeded(); + + // llvm::lower_bound is not used as for DIE offsets it would still return + // index +1 and GetOffset() returning index itself would be a special case. + auto pos = llvm::upper_bound( + m_units, std::make_pair(section, offset), + [](const std::pair<DIERef::Section, dw_offset_t> &lhs, + const DWARFUnitSP &rhs) { + return lhs < std::make_pair(rhs->GetDebugSection(), rhs->GetOffset()); + }); + uint32_t idx = std::distance(m_units.begin(), pos); + if (idx == 0) + return DW_INVALID_OFFSET; + return idx - 1; +} + +DWARFUnit *DWARFDebugInfo::GetUnitAtOffset(DIERef::Section section, + dw_offset_t cu_offset, + uint32_t *idx_ptr) { + uint32_t idx = FindUnitIndex(section, cu_offset); + DWARFUnit *result = GetUnitAtIndex(idx); + if (result && result->GetOffset() != cu_offset) { + result = nullptr; + idx = DW_INVALID_INDEX; + } + if (idx_ptr) + *idx_ptr = idx; + return result; +} + +DWARFUnit *DWARFDebugInfo::GetUnit(const DIERef &die_ref) { + return GetUnitContainingDIEOffset(die_ref.section(), die_ref.die_offset()); +} + +DWARFUnit * +DWARFDebugInfo::GetUnitContainingDIEOffset(DIERef::Section section, + dw_offset_t die_offset) { + uint32_t idx = FindUnitIndex(section, die_offset); + DWARFUnit *result = GetUnitAtIndex(idx); + if (result && !result->ContainsDIEOffset(die_offset)) + return nullptr; + return result; +} + +DWARFTypeUnit *DWARFDebugInfo::GetTypeUnitForHash(uint64_t hash) { + auto pos = llvm::lower_bound(m_type_hash_to_unit_index, + std::make_pair(hash, 0u), llvm::less_first()); + if (pos == m_type_hash_to_unit_index.end() || pos->first != hash) + return nullptr; + return llvm::cast<DWARFTypeUnit>(GetUnitAtIndex(pos->second)); +} + +bool DWARFDebugInfo::ContainsTypeUnits() { + ParseUnitHeadersIfNeeded(); + return !m_type_hash_to_unit_index.empty(); +} + +DWARFDIE +DWARFDebugInfo::GetDIEForDIEOffset(DIERef::Section section, + dw_offset_t die_offset) { + DWARFUnit *cu = GetUnitContainingDIEOffset(section, die_offset); + if (cu) + return cu->GetDIE(die_offset); + return DWARFDIE(); +} + +// GetDIE() +// +// Get the DIE (Debug Information Entry) with the specified offset. +DWARFDIE +DWARFDebugInfo::GetDIE(const DIERef &die_ref) { + DWARFUnit *cu = GetUnit(die_ref); + if (cu) + return cu->GetDIE(die_ref.die_offset()); + return DWARFDIE(); // Not found +} + diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.h new file mode 100644 index 000000000000..d1b066ffe80c --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.h @@ -0,0 +1,88 @@ +//===-- DWARFDebugInfo.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 SymbolFileDWARF_DWARFDebugInfo_h_ +#define SymbolFileDWARF_DWARFDebugInfo_h_ + +#include <map> +#include <vector> + +#include "DWARFDIE.h" +#include "DWARFTypeUnit.h" +#include "DWARFUnit.h" +#include "SymbolFileDWARF.h" +#include "lldb/Core/STLUtils.h" +#include "lldb/lldb-private.h" +#include "llvm/Support/Error.h" + +namespace lldb_private { +class DWARFContext; +} + +typedef std::multimap<const char *, dw_offset_t, CStringCompareFunctionObject> + CStringToDIEMap; +typedef CStringToDIEMap::iterator CStringToDIEMapIter; +typedef CStringToDIEMap::const_iterator CStringToDIEMapConstIter; + +class DWARFDebugInfo { +public: + typedef dw_offset_t (*Callback)(SymbolFileDWARF *dwarf2Data, + DWARFUnit *cu, + DWARFDebugInfoEntry *die, + const dw_offset_t next_offset, + const uint32_t depth, void *userData); + + explicit DWARFDebugInfo(SymbolFileDWARF &dwarf, + lldb_private::DWARFContext &context); + + size_t GetNumUnits(); + DWARFUnit *GetUnitAtIndex(lldb::user_id_t idx); + DWARFUnit *GetUnitAtOffset(DIERef::Section section, dw_offset_t cu_offset, + uint32_t *idx_ptr = nullptr); + DWARFUnit *GetUnitContainingDIEOffset(DIERef::Section section, + dw_offset_t die_offset); + DWARFUnit *GetUnit(const DIERef &die_ref); + DWARFTypeUnit *GetTypeUnitForHash(uint64_t hash); + bool ContainsTypeUnits(); + DWARFDIE GetDIEForDIEOffset(DIERef::Section section, + dw_offset_t die_offset); + DWARFDIE GetDIE(const DIERef &die_ref); + + enum { + eDumpFlag_Verbose = (1 << 0), // Verbose dumping + eDumpFlag_ShowForm = (1 << 1), // Show the DW_form type + eDumpFlag_ShowAncestors = + (1 << 2) // Show all parent DIEs when dumping single DIEs + }; + + llvm::Expected<DWARFDebugAranges &> GetCompileUnitAranges(); + +protected: + typedef std::vector<DWARFUnitSP> UnitColl; + + SymbolFileDWARF &m_dwarf; + lldb_private::DWARFContext &m_context; + UnitColl m_units; + std::unique_ptr<DWARFDebugAranges> + m_cu_aranges_up; // A quick address to compile unit table + + std::vector<std::pair<uint64_t, uint32_t>> m_type_hash_to_unit_index; + +private: + // All parsing needs to be done partially any managed by this class as + // accessors are called. + void ParseUnitHeadersIfNeeded(); + + void ParseUnitsFor(DIERef::Section section); + + uint32_t FindUnitIndex(DIERef::Section section, dw_offset_t offset); + + DISALLOW_COPY_AND_ASSIGN(DWARFDebugInfo); +}; + +#endif // SymbolFileDWARF_DWARFDebugInfo_h_ diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp new file mode 100644 index 000000000000..2f55b7d40ed9 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp @@ -0,0 +1,1246 @@ +//===-- DWARFDebugInfoEntry.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 "DWARFDebugInfoEntry.h" + +#include <assert.h> + +#include <algorithm> + +#include "llvm/Support/LEB128.h" + +#include "lldb/Core/Module.h" +#include "lldb/Expression/DWARFExpression.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Utility/Stream.h" + +#include "DWARFCompileUnit.h" +#include "DWARFDebugAbbrev.h" +#include "DWARFDebugAranges.h" +#include "DWARFDebugInfo.h" +#include "DWARFDebugRanges.h" +#include "DWARFDeclContext.h" +#include "DWARFFormValue.h" +#include "DWARFUnit.h" +#include "SymbolFileDWARF.h" +#include "SymbolFileDWARFDwo.h" + +using namespace lldb_private; +using namespace std; +extern int g_verbose; + +// Extract a debug info entry for a given DWARFUnit from the data +// starting at the offset in offset_ptr +bool DWARFDebugInfoEntry::Extract(const DWARFDataExtractor &data, + const DWARFUnit *cu, + lldb::offset_t *offset_ptr) { + m_offset = *offset_ptr; + m_parent_idx = 0; + m_sibling_idx = 0; + const uint64_t abbr_idx = data.GetULEB128(offset_ptr); + lldbassert(abbr_idx <= UINT16_MAX); + m_abbr_idx = abbr_idx; + + // assert (fixed_form_sizes); // For best performance this should be + // specified! + + if (m_abbr_idx) { + lldb::offset_t offset = *offset_ptr; + const auto *abbrevDecl = GetAbbreviationDeclarationPtr(cu); + if (abbrevDecl == nullptr) { + cu->GetSymbolFileDWARF().GetObjectFile()->GetModule()->ReportError( + "{0x%8.8x}: invalid abbreviation code %u, please file a bug and " + "attach the file at the start of this error message", + m_offset, (unsigned)abbr_idx); + // WE can't parse anymore if the DWARF is borked... + *offset_ptr = UINT32_MAX; + return false; + } + m_tag = abbrevDecl->Tag(); + m_has_children = abbrevDecl->HasChildren(); + // Skip all data in the .debug_info or .debug_types for the attributes + const uint32_t numAttributes = abbrevDecl->NumAttributes(); + uint32_t i; + dw_form_t form; + for (i = 0; i < numAttributes; ++i) { + form = abbrevDecl->GetFormByIndexUnchecked(i); + llvm::Optional<uint8_t> fixed_skip_size = + DWARFFormValue::GetFixedSize(form, cu); + if (fixed_skip_size) + offset += *fixed_skip_size; + else { + bool form_is_indirect = false; + do { + form_is_indirect = false; + uint32_t form_size = 0; + switch (form) { + // Blocks if inlined data that have a length field and the data bytes + // inlined in the .debug_info/.debug_types + case DW_FORM_exprloc: + case DW_FORM_block: + form_size = data.GetULEB128(&offset); + break; + case DW_FORM_block1: + form_size = data.GetU8_unchecked(&offset); + break; + case DW_FORM_block2: + form_size = data.GetU16_unchecked(&offset); + break; + case DW_FORM_block4: + form_size = data.GetU32_unchecked(&offset); + break; + + // Inlined NULL terminated C-strings + case DW_FORM_string: + data.GetCStr(&offset); + break; + + // Compile unit address sized values + case DW_FORM_addr: + form_size = cu->GetAddressByteSize(); + break; + case DW_FORM_ref_addr: + if (cu->GetVersion() <= 2) + form_size = cu->GetAddressByteSize(); + else + form_size = 4; + break; + + // 0 sized form + case DW_FORM_flag_present: + form_size = 0; + break; + + // 1 byte values + case DW_FORM_addrx1: + case DW_FORM_data1: + case DW_FORM_flag: + case DW_FORM_ref1: + case DW_FORM_strx1: + form_size = 1; + break; + + // 2 byte values + case DW_FORM_addrx2: + case DW_FORM_data2: + case DW_FORM_ref2: + case DW_FORM_strx2: + form_size = 2; + break; + + // 3 byte values + case DW_FORM_addrx3: + case DW_FORM_strx3: + form_size = 3; + break; + + // 4 byte values + case DW_FORM_addrx4: + case DW_FORM_data4: + case DW_FORM_ref4: + case DW_FORM_strx4: + form_size = 4; + break; + + // 8 byte values + case DW_FORM_data8: + case DW_FORM_ref8: + case DW_FORM_ref_sig8: + form_size = 8; + break; + + // signed or unsigned LEB 128 values + case DW_FORM_addrx: + case DW_FORM_rnglistx: + case DW_FORM_sdata: + case DW_FORM_udata: + case DW_FORM_ref_udata: + case DW_FORM_GNU_addr_index: + case DW_FORM_GNU_str_index: + case DW_FORM_strx: + data.Skip_LEB128(&offset); + break; + + case DW_FORM_indirect: + form_is_indirect = true; + form = data.GetULEB128(&offset); + break; + + case DW_FORM_strp: + case DW_FORM_sec_offset: + data.GetU32(&offset); + break; + + case DW_FORM_implicit_const: + form_size = 0; + break; + + default: + *offset_ptr = m_offset; + return false; + } + offset += form_size; + + } while (form_is_indirect); + } + } + *offset_ptr = offset; + return true; + } else { + m_tag = 0; + m_has_children = false; + return true; // NULL debug tag entry + } + + return false; +} + +static DWARFRangeList GetRangesOrReportError(const DWARFUnit &unit, + const DWARFDebugInfoEntry &die, + const DWARFFormValue &value) { + llvm::Expected<DWARFRangeList> expected_ranges = + (value.Form() == DW_FORM_rnglistx) + ? unit.FindRnglistFromIndex(value.Unsigned()) + : unit.FindRnglistFromOffset(value.Unsigned()); + if (expected_ranges) + return std::move(*expected_ranges); + unit.GetSymbolFileDWARF().GetObjectFile()->GetModule()->ReportError( + "{0x%8.8x}: DIE has DW_AT_ranges(0x%" PRIx64 ") attribute, but " + "range extraction failed (%s), please file a bug " + "and attach the file at the start of this error message", + die.GetOffset(), value.Unsigned(), + toString(expected_ranges.takeError()).c_str()); + return DWARFRangeList(); +} + +// GetDIENamesAndRanges +// +// Gets the valid address ranges for a given DIE by looking for a +// DW_AT_low_pc/DW_AT_high_pc pair, DW_AT_entry_pc, or DW_AT_ranges attributes. +bool DWARFDebugInfoEntry::GetDIENamesAndRanges( + const DWARFUnit *cu, const char *&name, const char *&mangled, + DWARFRangeList &ranges, int &decl_file, int &decl_line, int &decl_column, + int &call_file, int &call_line, int &call_column, + DWARFExpression *frame_base) const { + dw_addr_t lo_pc = LLDB_INVALID_ADDRESS; + dw_addr_t hi_pc = LLDB_INVALID_ADDRESS; + std::vector<DWARFDIE> dies; + bool set_frame_base_loclist_addr = false; + + const auto *abbrevDecl = GetAbbreviationDeclarationPtr(cu); + + SymbolFileDWARF &dwarf = cu->GetSymbolFileDWARF(); + lldb::ModuleSP module = dwarf.GetObjectFile()->GetModule(); + + if (abbrevDecl) { + const DWARFDataExtractor &data = cu->GetData(); + lldb::offset_t offset = GetFirstAttributeOffset(); + + if (!data.ValidOffset(offset)) + return false; + + const uint32_t numAttributes = abbrevDecl->NumAttributes(); + bool do_offset = false; + + for (uint32_t i = 0; i < numAttributes; ++i) { + DWARFFormValue form_value(cu); + dw_attr_t attr; + abbrevDecl->GetAttrAndFormValueByIndex(i, attr, form_value); + + if (form_value.ExtractValue(data, &offset)) { + switch (attr) { + case DW_AT_low_pc: + lo_pc = form_value.Address(); + + if (do_offset) + hi_pc += lo_pc; + do_offset = false; + break; + + case DW_AT_entry_pc: + lo_pc = form_value.Address(); + break; + + case DW_AT_high_pc: + if (form_value.Form() == DW_FORM_addr || + form_value.Form() == DW_FORM_addrx || + form_value.Form() == DW_FORM_GNU_addr_index) { + hi_pc = form_value.Address(); + } else { + hi_pc = form_value.Unsigned(); + if (lo_pc == LLDB_INVALID_ADDRESS) + do_offset = hi_pc != LLDB_INVALID_ADDRESS; + else + hi_pc += lo_pc; // DWARF 4 introduces <offset-from-lo-pc> to save + // on relocations + } + break; + + case DW_AT_ranges: + ranges = GetRangesOrReportError(*cu, *this, form_value); + break; + + case DW_AT_name: + if (name == nullptr) + name = form_value.AsCString(); + break; + + case DW_AT_MIPS_linkage_name: + case DW_AT_linkage_name: + if (mangled == nullptr) + mangled = form_value.AsCString(); + break; + + case DW_AT_abstract_origin: + dies.push_back(form_value.Reference()); + break; + + case DW_AT_specification: + dies.push_back(form_value.Reference()); + break; + + case DW_AT_decl_file: + if (decl_file == 0) + decl_file = form_value.Unsigned(); + break; + + case DW_AT_decl_line: + if (decl_line == 0) + decl_line = form_value.Unsigned(); + break; + + case DW_AT_decl_column: + if (decl_column == 0) + decl_column = form_value.Unsigned(); + break; + + case DW_AT_call_file: + if (call_file == 0) + call_file = form_value.Unsigned(); + break; + + case DW_AT_call_line: + if (call_line == 0) + call_line = form_value.Unsigned(); + break; + + case DW_AT_call_column: + if (call_column == 0) + call_column = form_value.Unsigned(); + break; + + case DW_AT_frame_base: + if (frame_base) { + if (form_value.BlockData()) { + uint32_t block_offset = + form_value.BlockData() - data.GetDataStart(); + uint32_t block_length = form_value.Unsigned(); + *frame_base = DWARFExpression(module, data, cu, + block_offset, block_length); + } else { + const DWARFDataExtractor &debug_loc_data = dwarf.DebugLocData(); + const dw_offset_t debug_loc_offset = form_value.Unsigned(); + + size_t loc_list_length = DWARFExpression::LocationListSize( + cu, debug_loc_data, debug_loc_offset); + if (loc_list_length > 0) { + *frame_base = + DWARFExpression(module, debug_loc_data, cu, + debug_loc_offset, loc_list_length); + if (lo_pc != LLDB_INVALID_ADDRESS) { + assert(lo_pc >= cu->GetBaseAddress()); + frame_base->SetLocationListSlide(lo_pc - + cu->GetBaseAddress()); + } else { + set_frame_base_loclist_addr = true; + } + } + } + } + break; + + default: + break; + } + } + } + } + + if (ranges.IsEmpty()) { + if (lo_pc != LLDB_INVALID_ADDRESS) { + if (hi_pc != LLDB_INVALID_ADDRESS && hi_pc > lo_pc) + ranges.Append(DWARFRangeList::Entry(lo_pc, hi_pc - lo_pc)); + else + ranges.Append(DWARFRangeList::Entry(lo_pc, 0)); + } + } + + if (set_frame_base_loclist_addr) { + dw_addr_t lowest_range_pc = ranges.GetMinRangeBase(0); + assert(lowest_range_pc >= cu->GetBaseAddress()); + frame_base->SetLocationListSlide(lowest_range_pc - cu->GetBaseAddress()); + } + + if (ranges.IsEmpty() || name == nullptr || mangled == nullptr) { + for (const DWARFDIE &die : dies) { + if (die) { + die.GetDIE()->GetDIENamesAndRanges(die.GetCU(), name, mangled, ranges, + decl_file, decl_line, decl_column, + call_file, call_line, call_column); + } + } + } + return !ranges.IsEmpty(); +} + +// Dump +// +// Dumps a debug information entry and all of it's attributes to the specified +// stream. +void DWARFDebugInfoEntry::Dump(const DWARFUnit *cu, Stream &s, + uint32_t recurse_depth) const { + const DWARFDataExtractor &data = cu->GetData(); + lldb::offset_t offset = m_offset; + + if (data.ValidOffset(offset)) { + dw_uleb128_t abbrCode = data.GetULEB128(&offset); + + s.Printf("\n0x%8.8x: ", m_offset); + s.Indent(); + if (abbrCode != m_abbr_idx) { + s.Printf("error: DWARF has been modified\n"); + } else if (abbrCode) { + const auto *abbrevDecl = GetAbbreviationDeclarationPtr(cu); + if (abbrevDecl) { + s.PutCString(DW_TAG_value_to_name(abbrevDecl->Tag())); + s.Printf(" [%u] %c\n", abbrCode, abbrevDecl->HasChildren() ? '*' : ' '); + + // Dump all data in the .debug_info/.debug_types for the attributes + const uint32_t numAttributes = abbrevDecl->NumAttributes(); + for (uint32_t i = 0; i < numAttributes; ++i) { + DWARFFormValue form_value(cu); + dw_attr_t attr; + abbrevDecl->GetAttrAndFormValueByIndex(i, attr, form_value); + + DumpAttribute(cu, data, &offset, s, attr, form_value); + } + + const DWARFDebugInfoEntry *child = GetFirstChild(); + if (recurse_depth > 0 && child) { + s.IndentMore(); + + while (child) { + child->Dump(cu, s, recurse_depth - 1); + child = child->GetSibling(); + } + s.IndentLess(); + } + } else + s.Printf("Abbreviation code note found in 'debug_abbrev' class for " + "code: %u\n", + abbrCode); + } else { + s.Printf("NULL\n"); + } + } +} + +// DumpAttribute +// +// Dumps a debug information entry attribute along with it's form. Any special +// display of attributes is done (disassemble location lists, show enumeration +// values for attributes, etc). +void DWARFDebugInfoEntry::DumpAttribute( + const DWARFUnit *cu, const DWARFDataExtractor &data, + lldb::offset_t *offset_ptr, Stream &s, dw_attr_t attr, + DWARFFormValue &form_value) { + bool show_form = s.GetFlags().Test(DWARFDebugInfo::eDumpFlag_ShowForm); + + s.Printf(" "); + s.Indent(DW_AT_value_to_name(attr)); + + if (show_form) { + s.Printf("[%s", DW_FORM_value_to_name(form_value.Form())); + } + + if (!form_value.ExtractValue(data, offset_ptr)) + return; + + if (show_form) { + if (form_value.Form() == DW_FORM_indirect) { + s.Printf(" [%s]", DW_FORM_value_to_name(form_value.Form())); + } + + s.PutCString("] "); + } + + s.PutCString("( "); + + SymbolFileDWARF &dwarf = cu->GetSymbolFileDWARF(); + + // Check to see if we have any special attribute formatters + switch (attr) { + case DW_AT_stmt_list: + s.Printf("0x%8.8" PRIx64, form_value.Unsigned()); + break; + + case DW_AT_language: + s.PutCString(DW_LANG_value_to_name(form_value.Unsigned())); + break; + + case DW_AT_encoding: + s.PutCString(DW_ATE_value_to_name(form_value.Unsigned())); + break; + + case DW_AT_frame_base: + case DW_AT_location: + case DW_AT_data_member_location: { + const uint8_t *blockData = form_value.BlockData(); + if (blockData) { + // Location description is inlined in data in the form value + DWARFDataExtractor locationData(data, + (*offset_ptr) - form_value.Unsigned(), + form_value.Unsigned()); + DWARFExpression::PrintDWARFExpression( + s, locationData, DWARFUnit::GetAddressByteSize(cu), 4, false); + } else { + // We have a location list offset as the value that is the offset into + // the .debug_loc section that describes the value over it's lifetime + uint64_t debug_loc_offset = form_value.Unsigned(); + DWARFExpression::PrintDWARFLocationList(s, cu, dwarf.DebugLocData(), + debug_loc_offset); + } + } break; + + case DW_AT_abstract_origin: + case DW_AT_specification: { + DWARFDIE abstract_die = form_value.Reference(); + form_value.Dump(s); + // *ostrm_ptr << HEX32 << abstract_die.GetOffset() << " ( "; + abstract_die.GetName(s); + } break; + + case DW_AT_type: { + DWARFDIE type_die = form_value.Reference(); + s.PutCString(" ( "); + type_die.AppendTypeName(s); + s.PutCString(" )"); + } break; + + default: + break; + } + + s.PutCString(" )\n"); +} + +// Get all attribute values for a given DIE, including following any +// specification or abstract origin attributes and including those in the +// results. Any duplicate attributes will have the first instance take +// precedence (this can happen for declaration attributes). +size_t DWARFDebugInfoEntry::GetAttributes( + const DWARFUnit *cu, DWARFAttributes &attributes, + uint32_t curr_depth) const { + const auto *abbrevDecl = GetAbbreviationDeclarationPtr(cu); + if (abbrevDecl) { + const DWARFDataExtractor &data = cu->GetData(); + lldb::offset_t offset = GetFirstAttributeOffset(); + + const uint32_t num_attributes = abbrevDecl->NumAttributes(); + for (uint32_t i = 0; i < num_attributes; ++i) { + DWARFFormValue form_value(cu); + dw_attr_t attr; + abbrevDecl->GetAttrAndFormValueByIndex(i, attr, form_value); + const dw_form_t form = form_value.Form(); + + // If we are tracking down DW_AT_specification or DW_AT_abstract_origin + // attributes, the depth will be non-zero. We need to omit certain + // attributes that don't make sense. + switch (attr) { + case DW_AT_sibling: + case DW_AT_declaration: + if (curr_depth > 0) { + // This attribute doesn't make sense when combined with the DIE that + // references this DIE. We know a DIE is referencing this DIE because + // curr_depth is not zero + break; + } + LLVM_FALLTHROUGH; + default: + attributes.Append(cu, offset, attr, form); + break; + } + + if ((attr == DW_AT_specification) || (attr == DW_AT_abstract_origin)) { + if (form_value.ExtractValue(data, &offset)) { + DWARFDIE spec_die = form_value.Reference(); + if (spec_die) + spec_die.GetAttributes(attributes, curr_depth + 1); + } + } else { + llvm::Optional<uint8_t> fixed_skip_size = DWARFFormValue::GetFixedSize(form, cu); + if (fixed_skip_size) + offset += *fixed_skip_size; + else + DWARFFormValue::SkipValue(form, data, &offset, cu); + } + } + } else { + attributes.Clear(); + } + return attributes.Size(); +} + +// GetAttributeValue +// +// Get the value of an attribute and return the .debug_info or .debug_types +// offset of the attribute if it was properly extracted into form_value, +// or zero if we fail since an offset of zero is invalid for an attribute (it +// would be a compile unit header). +dw_offset_t DWARFDebugInfoEntry::GetAttributeValue( + const DWARFUnit *cu, const dw_attr_t attr, DWARFFormValue &form_value, + dw_offset_t *end_attr_offset_ptr, + bool check_specification_or_abstract_origin) const { + if (const auto *abbrevDecl = GetAbbreviationDeclarationPtr(cu)) { + uint32_t attr_idx = abbrevDecl->FindAttributeIndex(attr); + + if (attr_idx != DW_INVALID_INDEX) { + const DWARFDataExtractor &data = cu->GetData(); + lldb::offset_t offset = GetFirstAttributeOffset(); + + uint32_t idx = 0; + while (idx < attr_idx) + DWARFFormValue::SkipValue(abbrevDecl->GetFormByIndex(idx++), + data, &offset, cu); + + const dw_offset_t attr_offset = offset; + form_value.SetUnit(cu); + form_value.SetForm(abbrevDecl->GetFormByIndex(idx)); + if (form_value.ExtractValue(data, &offset)) { + if (end_attr_offset_ptr) + *end_attr_offset_ptr = offset; + return attr_offset; + } + } + } + + if (check_specification_or_abstract_origin) { + if (GetAttributeValue(cu, DW_AT_specification, form_value)) { + DWARFDIE die = form_value.Reference(); + if (die) { + dw_offset_t die_offset = die.GetDIE()->GetAttributeValue( + die.GetCU(), attr, form_value, end_attr_offset_ptr, false); + if (die_offset) + return die_offset; + } + } + + if (GetAttributeValue(cu, DW_AT_abstract_origin, form_value)) { + DWARFDIE die = form_value.Reference(); + if (die) { + dw_offset_t die_offset = die.GetDIE()->GetAttributeValue( + die.GetCU(), attr, form_value, end_attr_offset_ptr, false); + if (die_offset) + return die_offset; + } + } + } + + // If we're a unit DIE, also check the attributes of the dwo unit (if any). + if (GetParent()) + return 0; + SymbolFileDWARFDwo *dwo_symbol_file = cu->GetDwoSymbolFile(); + if (!dwo_symbol_file) + return 0; + + DWARFCompileUnit *dwo_cu = dwo_symbol_file->GetCompileUnit(); + if (!dwo_cu) + return 0; + + DWARFBaseDIE dwo_cu_die = dwo_cu->GetUnitDIEOnly(); + if (!dwo_cu_die.IsValid()) + return 0; + + return dwo_cu_die.GetDIE()->GetAttributeValue( + dwo_cu, attr, form_value, end_attr_offset_ptr, + check_specification_or_abstract_origin); +} + +// GetAttributeValueAsString +// +// Get the value of an attribute as a string return it. The resulting pointer +// to the string data exists within the supplied SymbolFileDWARF and will only +// be available as long as the SymbolFileDWARF is still around and it's content +// doesn't change. +const char *DWARFDebugInfoEntry::GetAttributeValueAsString( + const DWARFUnit *cu, const dw_attr_t attr, const char *fail_value, + bool check_specification_or_abstract_origin) const { + DWARFFormValue form_value; + if (GetAttributeValue(cu, attr, form_value, nullptr, + check_specification_or_abstract_origin)) + return form_value.AsCString(); + return fail_value; +} + +// GetAttributeValueAsUnsigned +// +// Get the value of an attribute as unsigned and return it. +uint64_t DWARFDebugInfoEntry::GetAttributeValueAsUnsigned( + const DWARFUnit *cu, const dw_attr_t attr, uint64_t fail_value, + bool check_specification_or_abstract_origin) const { + DWARFFormValue form_value; + if (GetAttributeValue(cu, attr, form_value, nullptr, + check_specification_or_abstract_origin)) + return form_value.Unsigned(); + return fail_value; +} + +// GetAttributeValueAsReference +// +// Get the value of an attribute as reference and fix up and compile unit +// relative offsets as needed. +DWARFDIE DWARFDebugInfoEntry::GetAttributeValueAsReference( + const DWARFUnit *cu, const dw_attr_t attr, + bool check_specification_or_abstract_origin) const { + DWARFFormValue form_value; + if (GetAttributeValue(cu, attr, form_value, nullptr, + check_specification_or_abstract_origin)) + return form_value.Reference(); + return {}; +} + +uint64_t DWARFDebugInfoEntry::GetAttributeValueAsAddress( + const DWARFUnit *cu, const dw_attr_t attr, uint64_t fail_value, + bool check_specification_or_abstract_origin) const { + DWARFFormValue form_value; + if (GetAttributeValue(cu, attr, form_value, nullptr, + check_specification_or_abstract_origin)) + return form_value.Address(); + return fail_value; +} + +// GetAttributeHighPC +// +// Get the hi_pc, adding hi_pc to lo_pc when specified as an <offset-from-low- +// pc>. +// +// Returns the hi_pc or fail_value. +dw_addr_t DWARFDebugInfoEntry::GetAttributeHighPC( + const DWARFUnit *cu, dw_addr_t lo_pc, uint64_t fail_value, + bool check_specification_or_abstract_origin) const { + DWARFFormValue form_value; + if (GetAttributeValue(cu, DW_AT_high_pc, form_value, nullptr, + check_specification_or_abstract_origin)) { + dw_form_t form = form_value.Form(); + if (form == DW_FORM_addr || form == DW_FORM_addrx || + form == DW_FORM_GNU_addr_index) + return form_value.Address(); + + // DWARF4 can specify the hi_pc as an <offset-from-lowpc> + return lo_pc + form_value.Unsigned(); + } + return fail_value; +} + +// GetAttributeAddressRange +// +// Get the lo_pc and hi_pc, adding hi_pc to lo_pc when specified as an <offset- +// from-low-pc>. +// +// Returns true or sets lo_pc and hi_pc to fail_value. +bool DWARFDebugInfoEntry::GetAttributeAddressRange( + const DWARFUnit *cu, dw_addr_t &lo_pc, dw_addr_t &hi_pc, + uint64_t fail_value, bool check_specification_or_abstract_origin) const { + lo_pc = GetAttributeValueAsAddress(cu, DW_AT_low_pc, fail_value, + check_specification_or_abstract_origin); + if (lo_pc != fail_value) { + hi_pc = GetAttributeHighPC(cu, lo_pc, fail_value, + check_specification_or_abstract_origin); + if (hi_pc != fail_value) + return true; + } + lo_pc = fail_value; + hi_pc = fail_value; + return false; +} + +size_t DWARFDebugInfoEntry::GetAttributeAddressRanges( + const DWARFUnit *cu, DWARFRangeList &ranges, bool check_hi_lo_pc, + bool check_specification_or_abstract_origin) const { + ranges.Clear(); + + DWARFFormValue form_value; + if (GetAttributeValue(cu, DW_AT_ranges, form_value)) { + ranges = GetRangesOrReportError(*cu, *this, form_value); + } else if (check_hi_lo_pc) { + dw_addr_t lo_pc = LLDB_INVALID_ADDRESS; + dw_addr_t hi_pc = LLDB_INVALID_ADDRESS; + if (GetAttributeAddressRange(cu, lo_pc, hi_pc, LLDB_INVALID_ADDRESS, + check_specification_or_abstract_origin)) { + if (lo_pc < hi_pc) + ranges.Append(DWARFRangeList::Entry(lo_pc, hi_pc - lo_pc)); + } + } + return ranges.GetSize(); +} + +// GetName +// +// Get value of the DW_AT_name attribute and return it if one exists, else +// return NULL. +const char *DWARFDebugInfoEntry::GetName(const DWARFUnit *cu) const { + return GetAttributeValueAsString(cu, DW_AT_name, nullptr, true); +} + +// GetMangledName +// +// Get value of the DW_AT_MIPS_linkage_name attribute and return it if one +// exists, else return the value of the DW_AT_name attribute +const char * +DWARFDebugInfoEntry::GetMangledName(const DWARFUnit *cu, + bool substitute_name_allowed) const { + const char *name = nullptr; + + name = GetAttributeValueAsString(cu, DW_AT_MIPS_linkage_name, nullptr, true); + if (name) + return name; + + name = GetAttributeValueAsString(cu, DW_AT_linkage_name, nullptr, true); + if (name) + return name; + + if (!substitute_name_allowed) + return nullptr; + + name = GetAttributeValueAsString(cu, DW_AT_name, nullptr, true); + return name; +} + +// GetPubname +// +// Get value the name for a DIE as it should appear for a .debug_pubnames or +// .debug_pubtypes section. +const char *DWARFDebugInfoEntry::GetPubname(const DWARFUnit *cu) const { + const char *name = nullptr; + if (!cu) + return name; + + name = GetAttributeValueAsString(cu, DW_AT_MIPS_linkage_name, nullptr, true); + if (name) + return name; + + name = GetAttributeValueAsString(cu, DW_AT_linkage_name, nullptr, true); + if (name) + return name; + + name = GetAttributeValueAsString(cu, DW_AT_name, nullptr, true); + return name; +} + +// BuildAddressRangeTable +void DWARFDebugInfoEntry::BuildAddressRangeTable( + const DWARFUnit *cu, DWARFDebugAranges *debug_aranges) const { + if (m_tag) { + if (m_tag == DW_TAG_subprogram) { + dw_addr_t lo_pc = LLDB_INVALID_ADDRESS; + dw_addr_t hi_pc = LLDB_INVALID_ADDRESS; + if (GetAttributeAddressRange(cu, lo_pc, hi_pc, LLDB_INVALID_ADDRESS)) { + /// printf("BuildAddressRangeTable() 0x%8.8x: %30s: [0x%8.8x - + /// 0x%8.8x)\n", m_offset, DW_TAG_value_to_name(tag), lo_pc, hi_pc); + debug_aranges->AppendRange(cu->GetOffset(), lo_pc, hi_pc); + } + } + + const DWARFDebugInfoEntry *child = GetFirstChild(); + while (child) { + child->BuildAddressRangeTable(cu, debug_aranges); + child = child->GetSibling(); + } + } +} + +// BuildFunctionAddressRangeTable +// +// This function is very similar to the BuildAddressRangeTable function except +// that the actual DIE offset for the function is placed in the table instead +// of the compile unit offset (which is the way the standard .debug_aranges +// section does it). +void DWARFDebugInfoEntry::BuildFunctionAddressRangeTable( + const DWARFUnit *cu, DWARFDebugAranges *debug_aranges) const { + if (m_tag) { + if (m_tag == DW_TAG_subprogram) { + dw_addr_t lo_pc = LLDB_INVALID_ADDRESS; + dw_addr_t hi_pc = LLDB_INVALID_ADDRESS; + if (GetAttributeAddressRange(cu, lo_pc, hi_pc, LLDB_INVALID_ADDRESS)) { + // printf("BuildAddressRangeTable() 0x%8.8x: [0x%16.16" PRIx64 " - + // 0x%16.16" PRIx64 ")\n", m_offset, lo_pc, hi_pc); // DEBUG ONLY + debug_aranges->AppendRange(GetOffset(), lo_pc, hi_pc); + } + } + + const DWARFDebugInfoEntry *child = GetFirstChild(); + while (child) { + child->BuildFunctionAddressRangeTable(cu, debug_aranges); + child = child->GetSibling(); + } + } +} + +void DWARFDebugInfoEntry::GetDWARFDeclContext( + DWARFUnit *cu, DWARFDeclContext &dwarf_decl_ctx) const { + const dw_tag_t tag = Tag(); + if (tag != DW_TAG_compile_unit && tag != DW_TAG_partial_unit) { + dwarf_decl_ctx.AppendDeclContext(tag, GetName(cu)); + DWARFDIE parent_decl_ctx_die = GetParentDeclContextDIE(cu); + if (parent_decl_ctx_die && parent_decl_ctx_die.GetDIE() != this) { + if (parent_decl_ctx_die.Tag() != DW_TAG_compile_unit && + parent_decl_ctx_die.Tag() != DW_TAG_partial_unit) + parent_decl_ctx_die.GetDIE()->GetDWARFDeclContext( + parent_decl_ctx_die.GetCU(), dwarf_decl_ctx); + } + } +} + +DWARFDIE +DWARFDebugInfoEntry::GetParentDeclContextDIE(DWARFUnit *cu) const { + DWARFAttributes attributes; + GetAttributes(cu, attributes); + return GetParentDeclContextDIE(cu, attributes); +} + +DWARFDIE +DWARFDebugInfoEntry::GetParentDeclContextDIE( + DWARFUnit *cu, const DWARFAttributes &attributes) const { + DWARFDIE die(cu, const_cast<DWARFDebugInfoEntry *>(this)); + + while (die) { + // If this is the original DIE that we are searching for a declaration for, + // then don't look in the cache as we don't want our own decl context to be + // our decl context... + if (die.GetDIE() != this) { + switch (die.Tag()) { + case DW_TAG_compile_unit: + case DW_TAG_partial_unit: + case DW_TAG_namespace: + case DW_TAG_structure_type: + case DW_TAG_union_type: + case DW_TAG_class_type: + return die; + + default: + break; + } + } + + DWARFDIE spec_die = attributes.FormValueAsReference(DW_AT_specification); + if (spec_die) { + DWARFDIE decl_ctx_die = spec_die.GetParentDeclContextDIE(); + if (decl_ctx_die) + return decl_ctx_die; + } + + DWARFDIE abs_die = attributes.FormValueAsReference(DW_AT_abstract_origin); + if (abs_die) { + DWARFDIE decl_ctx_die = abs_die.GetParentDeclContextDIE(); + if (decl_ctx_die) + return decl_ctx_die; + } + + die = die.GetParent(); + } + return DWARFDIE(); +} + +const char *DWARFDebugInfoEntry::GetQualifiedName(DWARFUnit *cu, + std::string &storage) const { + DWARFAttributes attributes; + GetAttributes(cu, attributes); + return GetQualifiedName(cu, attributes, storage); +} + +const char * +DWARFDebugInfoEntry::GetQualifiedName(DWARFUnit *cu, + const DWARFAttributes &attributes, + std::string &storage) const { + + const char *name = GetName(cu); + + if (name) { + DWARFDIE parent_decl_ctx_die = GetParentDeclContextDIE(cu); + storage.clear(); + // TODO: change this to get the correct decl context parent.... + while (parent_decl_ctx_die) { + const dw_tag_t parent_tag = parent_decl_ctx_die.Tag(); + switch (parent_tag) { + case DW_TAG_namespace: { + const char *namespace_name = parent_decl_ctx_die.GetName(); + if (namespace_name) { + storage.insert(0, "::"); + storage.insert(0, namespace_name); + } else { + storage.insert(0, "(anonymous namespace)::"); + } + parent_decl_ctx_die = parent_decl_ctx_die.GetParentDeclContextDIE(); + } break; + + case DW_TAG_class_type: + case DW_TAG_structure_type: + case DW_TAG_union_type: { + const char *class_union_struct_name = parent_decl_ctx_die.GetName(); + + if (class_union_struct_name) { + storage.insert(0, "::"); + storage.insert(0, class_union_struct_name); + } + parent_decl_ctx_die = parent_decl_ctx_die.GetParentDeclContextDIE(); + } break; + + default: + parent_decl_ctx_die.Clear(); + break; + } + } + + if (storage.empty()) + storage.append("::"); + + storage.append(name); + } + if (storage.empty()) + return nullptr; + return storage.c_str(); +} + +bool DWARFDebugInfoEntry::LookupAddress(const dw_addr_t address, + const DWARFUnit *cu, + DWARFDebugInfoEntry **function_die, + DWARFDebugInfoEntry **block_die) { + bool found_address = false; + if (m_tag) { + bool check_children = false; + bool match_addr_range = false; + // printf("0x%8.8x: %30s: address = 0x%8.8x - ", m_offset, + // DW_TAG_value_to_name(tag), address); + switch (m_tag) { + case DW_TAG_array_type: + break; + case DW_TAG_class_type: + check_children = true; + break; + case DW_TAG_entry_point: + case DW_TAG_enumeration_type: + case DW_TAG_formal_parameter: + case DW_TAG_imported_declaration: + case DW_TAG_label: + break; + case DW_TAG_lexical_block: + check_children = true; + match_addr_range = true; + break; + case DW_TAG_member: + case DW_TAG_pointer_type: + case DW_TAG_reference_type: + break; + case DW_TAG_compile_unit: + match_addr_range = true; + break; + case DW_TAG_string_type: + break; + case DW_TAG_structure_type: + check_children = true; + break; + case DW_TAG_subroutine_type: + case DW_TAG_typedef: + case DW_TAG_union_type: + case DW_TAG_unspecified_parameters: + case DW_TAG_variant: + break; + case DW_TAG_common_block: + check_children = true; + break; + case DW_TAG_common_inclusion: + case DW_TAG_inheritance: + break; + case DW_TAG_inlined_subroutine: + check_children = true; + match_addr_range = true; + break; + case DW_TAG_module: + match_addr_range = true; + break; + case DW_TAG_ptr_to_member_type: + case DW_TAG_set_type: + case DW_TAG_subrange_type: + case DW_TAG_with_stmt: + case DW_TAG_access_declaration: + case DW_TAG_base_type: + break; + case DW_TAG_catch_block: + match_addr_range = true; + break; + case DW_TAG_const_type: + case DW_TAG_constant: + case DW_TAG_enumerator: + case DW_TAG_file_type: + case DW_TAG_friend: + case DW_TAG_namelist: + case DW_TAG_namelist_item: + case DW_TAG_packed_type: + break; + case DW_TAG_subprogram: + match_addr_range = true; + break; + case DW_TAG_template_type_parameter: + case DW_TAG_template_value_parameter: + case DW_TAG_GNU_template_parameter_pack: + case DW_TAG_thrown_type: + break; + case DW_TAG_try_block: + match_addr_range = true; + break; + case DW_TAG_variant_part: + case DW_TAG_variable: + case DW_TAG_volatile_type: + case DW_TAG_dwarf_procedure: + case DW_TAG_restrict_type: + case DW_TAG_interface_type: + break; + case DW_TAG_namespace: + check_children = true; + break; + case DW_TAG_imported_module: + case DW_TAG_unspecified_type: + break; + case DW_TAG_partial_unit: + match_addr_range = true; + break; + case DW_TAG_imported_unit: + case DW_TAG_shared_type: + default: + break; + } + + if (match_addr_range) { + dw_addr_t lo_pc = + GetAttributeValueAsAddress(cu, DW_AT_low_pc, LLDB_INVALID_ADDRESS); + if (lo_pc != LLDB_INVALID_ADDRESS) { + dw_addr_t hi_pc = GetAttributeHighPC(cu, lo_pc, LLDB_INVALID_ADDRESS); + if (hi_pc != LLDB_INVALID_ADDRESS) { + // printf("\n0x%8.8x: %30s: address = 0x%8.8x [0x%8.8x - 0x%8.8x) ", + // m_offset, DW_TAG_value_to_name(tag), address, lo_pc, hi_pc); + if ((lo_pc <= address) && (address < hi_pc)) { + found_address = true; + // puts("***MATCH***"); + switch (m_tag) { + case DW_TAG_compile_unit: // File + case DW_TAG_partial_unit: // File + check_children = + ((function_die != nullptr) || (block_die != nullptr)); + break; + + case DW_TAG_subprogram: // Function + if (function_die) + *function_die = this; + check_children = (block_die != nullptr); + break; + + case DW_TAG_inlined_subroutine: // Inlined Function + case DW_TAG_lexical_block: // Block { } in code + if (block_die) { + *block_die = this; + check_children = true; + } + break; + + default: + check_children = true; + break; + } + } + } else { + // Compile units may not have a valid high/low pc when there + // are address gaps in subroutines so we must always search + // if there is no valid high and low PC. + check_children = + (m_tag == DW_TAG_compile_unit || m_tag == DW_TAG_partial_unit) && + ((function_die != nullptr) || (block_die != nullptr)); + } + } else { + DWARFRangeList ranges; + if (GetAttributeAddressRanges(cu, ranges, /*check_hi_lo_pc*/ false) && + ranges.FindEntryThatContains(address)) { + found_address = true; + // puts("***MATCH***"); + switch (m_tag) { + case DW_TAG_compile_unit: // File + case DW_TAG_partial_unit: // File + check_children = + ((function_die != nullptr) || (block_die != nullptr)); + break; + + case DW_TAG_subprogram: // Function + if (function_die) + *function_die = this; + check_children = (block_die != nullptr); + break; + + case DW_TAG_inlined_subroutine: // Inlined Function + case DW_TAG_lexical_block: // Block { } in code + if (block_die) { + *block_die = this; + check_children = true; + } + break; + + default: + check_children = true; + break; + } + } else { + check_children = false; + } + } + } + + if (check_children) { + // printf("checking children\n"); + DWARFDebugInfoEntry *child = GetFirstChild(); + while (child) { + if (child->LookupAddress(address, cu, function_die, block_die)) + return true; + child = child->GetSibling(); + } + } + } + return found_address; +} + +lldb::offset_t DWARFDebugInfoEntry::GetFirstAttributeOffset() const { + return GetOffset() + llvm::getULEB128Size(m_abbr_idx); +} + +const DWARFAbbreviationDeclaration * +DWARFDebugInfoEntry::GetAbbreviationDeclarationPtr(const DWARFUnit *cu) const { + if (cu) { + const DWARFAbbreviationDeclarationSet *abbrev_set = cu->GetAbbreviations(); + if (abbrev_set) + return abbrev_set->GetAbbreviationDeclaration(m_abbr_idx); + } + return nullptr; +} + +bool DWARFDebugInfoEntry::operator==(const DWARFDebugInfoEntry &rhs) const { + return m_offset == rhs.m_offset && m_parent_idx == rhs.m_parent_idx && + m_sibling_idx == rhs.m_sibling_idx && + m_abbr_idx == rhs.m_abbr_idx && m_has_children == rhs.m_has_children && + m_tag == rhs.m_tag; +} + +bool DWARFDebugInfoEntry::operator!=(const DWARFDebugInfoEntry &rhs) const { + return !(*this == rhs); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.h new file mode 100644 index 000000000000..1e7b5f27642d --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.h @@ -0,0 +1,185 @@ +//===-- DWARFDebugInfoEntry.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 SymbolFileDWARF_DWARFDebugInfoEntry_h_ +#define SymbolFileDWARF_DWARFDebugInfoEntry_h_ + +#include "SymbolFileDWARF.h" +#include "llvm/ADT/SmallVector.h" + +#include "DWARFAbbreviationDeclaration.h" +#include "DWARFDebugAbbrev.h" +#include "DWARFDebugRanges.h" +#include <map> +#include <set> +#include <vector> + +class DWARFDeclContext; + +#define DIE_SIBLING_IDX_BITSIZE 31 + +class DWARFDebugInfoEntry { +public: + typedef std::vector<DWARFDebugInfoEntry> collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + + DWARFDebugInfoEntry() + : m_offset(DW_INVALID_OFFSET), m_parent_idx(0), m_sibling_idx(0), + m_has_children(false), m_abbr_idx(0), m_tag(0) {} + + explicit operator bool() const { return m_offset != DW_INVALID_OFFSET; } + bool operator==(const DWARFDebugInfoEntry &rhs) const; + bool operator!=(const DWARFDebugInfoEntry &rhs) const; + + void BuildAddressRangeTable(const DWARFUnit *cu, + DWARFDebugAranges *debug_aranges) const; + + void BuildFunctionAddressRangeTable(const DWARFUnit *cu, + DWARFDebugAranges *debug_aranges) const; + + bool Extract(const lldb_private::DWARFDataExtractor &data, + const DWARFUnit *cu, lldb::offset_t *offset_ptr); + + bool LookupAddress(const dw_addr_t address, const DWARFUnit *cu, + DWARFDebugInfoEntry **function_die, + DWARFDebugInfoEntry **block_die); + + size_t GetAttributes(const DWARFUnit *cu, + DWARFAttributes &attrs, + uint32_t curr_depth = 0) + const; // "curr_depth" for internal use only, don't set this yourself!!! + + dw_offset_t + GetAttributeValue(const DWARFUnit *cu, const dw_attr_t attr, + DWARFFormValue &formValue, + dw_offset_t *end_attr_offset_ptr = nullptr, + bool check_specification_or_abstract_origin = false) const; + + const char *GetAttributeValueAsString( + const DWARFUnit *cu, const dw_attr_t attr, const char *fail_value, + bool check_specification_or_abstract_origin = false) const; + + uint64_t GetAttributeValueAsUnsigned( + const DWARFUnit *cu, const dw_attr_t attr, uint64_t fail_value, + bool check_specification_or_abstract_origin = false) const; + + DWARFDIE GetAttributeValueAsReference( + const DWARFUnit *cu, const dw_attr_t attr, + bool check_specification_or_abstract_origin = false) const; + + uint64_t GetAttributeValueAsAddress( + const DWARFUnit *cu, const dw_attr_t attr, uint64_t fail_value, + bool check_specification_or_abstract_origin = false) const; + + dw_addr_t + GetAttributeHighPC(const DWARFUnit *cu, dw_addr_t lo_pc, uint64_t fail_value, + bool check_specification_or_abstract_origin = false) const; + + bool GetAttributeAddressRange( + const DWARFUnit *cu, dw_addr_t &lo_pc, dw_addr_t &hi_pc, + uint64_t fail_value, + bool check_specification_or_abstract_origin = false) const; + + size_t GetAttributeAddressRanges( + const DWARFUnit *cu, DWARFRangeList &ranges, bool check_hi_lo_pc, + bool check_specification_or_abstract_origin = false) const; + + const char *GetName(const DWARFUnit *cu) const; + + const char *GetMangledName(const DWARFUnit *cu, + bool substitute_name_allowed = true) const; + + const char *GetPubname(const DWARFUnit *cu) const; + + const char *GetQualifiedName(DWARFUnit *cu, std::string &storage) const; + + const char *GetQualifiedName(DWARFUnit *cu, const DWARFAttributes &attributes, + std::string &storage) const; + + void Dump(const DWARFUnit *cu, lldb_private::Stream &s, + uint32_t recurse_depth) const; + + static void + DumpAttribute(const DWARFUnit *cu, + const lldb_private::DWARFDataExtractor &data, + lldb::offset_t *offset_ptr, lldb_private::Stream &s, + dw_attr_t attr, DWARFFormValue &form_value); + + bool GetDIENamesAndRanges( + const DWARFUnit *cu, const char *&name, const char *&mangled, + DWARFRangeList &rangeList, int &decl_file, int &decl_line, + int &decl_column, int &call_file, int &call_line, int &call_column, + lldb_private::DWARFExpression *frame_base = nullptr) const; + + const DWARFAbbreviationDeclaration * + GetAbbreviationDeclarationPtr(const DWARFUnit *cu) const; + + lldb::offset_t GetFirstAttributeOffset() const; + + dw_tag_t Tag() const { return m_tag; } + + bool IsNULL() const { return m_abbr_idx == 0; } + + dw_offset_t GetOffset() const { return m_offset; } + + bool HasChildren() const { return m_has_children; } + + void SetHasChildren(bool b) { m_has_children = b; } + + // We know we are kept in a vector of contiguous entries, so we know + // our parent will be some index behind "this". + DWARFDebugInfoEntry *GetParent() { + return m_parent_idx > 0 ? this - m_parent_idx : nullptr; + } + const DWARFDebugInfoEntry *GetParent() const { + return m_parent_idx > 0 ? this - m_parent_idx : nullptr; + } + // We know we are kept in a vector of contiguous entries, so we know + // our sibling will be some index after "this". + DWARFDebugInfoEntry *GetSibling() { + return m_sibling_idx > 0 ? this + m_sibling_idx : nullptr; + } + const DWARFDebugInfoEntry *GetSibling() const { + return m_sibling_idx > 0 ? this + m_sibling_idx : nullptr; + } + // We know we are kept in a vector of contiguous entries, so we know + // we don't need to store our child pointer, if we have a child it will + // be the next entry in the list... + DWARFDebugInfoEntry *GetFirstChild() { + return HasChildren() ? this + 1 : nullptr; + } + const DWARFDebugInfoEntry *GetFirstChild() const { + return HasChildren() ? this + 1 : nullptr; + } + + void GetDWARFDeclContext(DWARFUnit *cu, + DWARFDeclContext &dwarf_decl_ctx) const; + + DWARFDIE GetParentDeclContextDIE(DWARFUnit *cu) const; + DWARFDIE GetParentDeclContextDIE(DWARFUnit *cu, + const DWARFAttributes &attributes) const; + + void SetSiblingIndex(uint32_t idx) { m_sibling_idx = idx; } + void SetParentIndex(uint32_t idx) { m_parent_idx = idx; } + +protected: + dw_offset_t m_offset; // Offset within the .debug_info/.debug_types + uint32_t m_parent_idx; // How many to subtract from "this" to get the parent. + // If zero this die has no parent + uint32_t m_sibling_idx : 31, // How many to add to "this" to get the sibling. + // If it is zero, then the DIE doesn't have children, or the + // DWARF claimed it had children but the DIE only contained + // a single NULL terminating child. + m_has_children : 1; + uint16_t m_abbr_idx; + uint16_t m_tag; // A copy of the DW_TAG value so we don't have to go through + // the compile unit abbrev table +}; + +#endif // SymbolFileDWARF_DWARFDebugInfoEntry_h_ diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugLine.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugLine.cpp new file mode 100644 index 000000000000..953089fee22b --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugLine.cpp @@ -0,0 +1,1038 @@ +//===-- DWARFDebugLine.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 "DWARFDebugLine.h" + +//#define ENABLE_DEBUG_PRINTF // DO NOT LEAVE THIS DEFINED: DEBUG ONLY!!! +#include <assert.h> + +#include <memory> + +#include "lldb/Core/FileSpecList.h" +#include "lldb/Core/Module.h" +#include "lldb/Host/Host.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/Timer.h" + +#include "DWARFUnit.h" +#include "LogChannelDWARF.h" +#include "SymbolFileDWARF.h" + +using namespace lldb; +using namespace lldb_private; +using namespace std; + +// Parse +// +// Parse all information in the debug_line_data into an internal +// representation. +void DWARFDebugLine::Parse(const DWARFDataExtractor &debug_line_data) { + m_lineTableMap.clear(); + lldb::offset_t offset = 0; + LineTable::shared_ptr line_table_sp(new LineTable); + while (debug_line_data.ValidOffset(offset)) { + const lldb::offset_t debug_line_offset = offset; + + if (line_table_sp.get() == nullptr) + break; + + if (ParseStatementTable(debug_line_data, &offset, line_table_sp.get(), nullptr)) { + // Make sure we don't don't loop infinitely + if (offset <= debug_line_offset) + break; + // DEBUG_PRINTF("m_lineTableMap[0x%8.8x] = line_table_sp\n", + // debug_line_offset); + m_lineTableMap[debug_line_offset] = line_table_sp; + line_table_sp = std::make_shared<LineTable>(); + } else + ++offset; // Try next byte in line table + } +} + +void DWARFDebugLine::ParseIfNeeded(const DWARFDataExtractor &debug_line_data) { + if (m_lineTableMap.empty()) + Parse(debug_line_data); +} + +// DWARFDebugLine::GetLineTable +DWARFDebugLine::LineTable::shared_ptr +DWARFDebugLine::GetLineTable(const dw_offset_t offset) const { + DWARFDebugLine::LineTable::shared_ptr line_table_shared_ptr; + LineTableConstIter pos = m_lineTableMap.find(offset); + if (pos != m_lineTableMap.end()) + line_table_shared_ptr = pos->second; + return line_table_shared_ptr; +} + +// Parse +// +// Parse the entire line table contents calling callback each time a new +// prologue is parsed and every time a new row is to be added to the line +// table. +void DWARFDebugLine::Parse(const DWARFDataExtractor &debug_line_data, + DWARFDebugLine::State::Callback callback, + void *userData) { + lldb::offset_t offset = 0; + if (debug_line_data.ValidOffset(offset)) { + if (!ParseStatementTable(debug_line_data, &offset, callback, userData, nullptr)) + ++offset; // Skip to next byte in .debug_line section + } +} + +namespace { +struct EntryDescriptor { + dw_sleb128_t code; + dw_sleb128_t form; +}; + +static std::vector<EntryDescriptor> +ReadDescriptors(const DWARFDataExtractor &debug_line_data, + lldb::offset_t *offset_ptr) { + std::vector<EntryDescriptor> ret; + uint8_t n = debug_line_data.GetU8(offset_ptr); + for (uint8_t i = 0; i < n; ++i) { + EntryDescriptor ent; + ent.code = debug_line_data.GetULEB128(offset_ptr); + ent.form = debug_line_data.GetULEB128(offset_ptr); + ret.push_back(ent); + } + return ret; +} +} // namespace + +// DWARFDebugLine::ParsePrologue +bool DWARFDebugLine::ParsePrologue(const DWARFDataExtractor &debug_line_data, + lldb::offset_t *offset_ptr, + Prologue *prologue, DWARFUnit *dwarf_cu) { + const lldb::offset_t prologue_offset = *offset_ptr; + + // DEBUG_PRINTF("0x%8.8x: ParsePrologue()\n", *offset_ptr); + + prologue->Clear(); + uint32_t i; + const char *s; + prologue->total_length = debug_line_data.GetDWARFInitialLength(offset_ptr); + prologue->version = debug_line_data.GetU16(offset_ptr); + if (prologue->version < 2 || prologue->version > 5) + return false; + + if (prologue->version >= 5) { + prologue->address_size = debug_line_data.GetU8(offset_ptr); + prologue->segment_selector_size = debug_line_data.GetU8(offset_ptr); + } + + prologue->prologue_length = debug_line_data.GetDWARFOffset(offset_ptr); + const lldb::offset_t end_prologue_offset = + prologue->prologue_length + *offset_ptr; + prologue->min_inst_length = debug_line_data.GetU8(offset_ptr); + if (prologue->version >= 4) + prologue->maximum_operations_per_instruction = + debug_line_data.GetU8(offset_ptr); + else + prologue->maximum_operations_per_instruction = 1; + prologue->default_is_stmt = debug_line_data.GetU8(offset_ptr); + prologue->line_base = debug_line_data.GetU8(offset_ptr); + prologue->line_range = debug_line_data.GetU8(offset_ptr); + prologue->opcode_base = debug_line_data.GetU8(offset_ptr); + + prologue->standard_opcode_lengths.reserve(prologue->opcode_base - 1); + + for (i = 1; i < prologue->opcode_base; ++i) { + uint8_t op_len = debug_line_data.GetU8(offset_ptr); + prologue->standard_opcode_lengths.push_back(op_len); + } + + if (prologue->version >= 5) { + std::vector<EntryDescriptor> dirEntryFormatV = + ReadDescriptors(debug_line_data, offset_ptr); + uint8_t dirCount = debug_line_data.GetULEB128(offset_ptr); + for (int i = 0; i < dirCount; ++i) { + for (EntryDescriptor &ent : dirEntryFormatV) { + DWARFFormValue value(dwarf_cu, ent.form); + if (ent.code != DW_LNCT_path) { + if (!value.SkipValue(debug_line_data, offset_ptr)) + return false; + continue; + } + + if (!value.ExtractValue(debug_line_data, offset_ptr)) + return false; + prologue->include_directories.push_back(value.AsCString()); + } + } + + std::vector<EntryDescriptor> filesEntryFormatV = + ReadDescriptors(debug_line_data, offset_ptr); + llvm::DenseSet<std::pair<uint64_t, uint64_t>> seen; + uint8_t n = debug_line_data.GetULEB128(offset_ptr); + for (int i = 0; i < n; ++i) { + FileNameEntry entry; + for (EntryDescriptor &ent : filesEntryFormatV) { + DWARFFormValue value(dwarf_cu, ent.form); + if (!value.ExtractValue(debug_line_data, offset_ptr)) + return false; + + switch (ent.code) { + case DW_LNCT_path: + entry.name = value.AsCString(); + break; + case DW_LNCT_directory_index: + entry.dir_idx = value.Unsigned(); + break; + case DW_LNCT_timestamp: + entry.mod_time = value.Unsigned(); + break; + case DW_LNCT_size: + entry.length = value.Unsigned(); + break; + case DW_LNCT_MD5: + assert(value.Unsigned() == 16); + std::uninitialized_copy_n(value.BlockData(), 16, + entry.checksum.Bytes.begin()); + break; + default: + break; + } + } + + if (seen.insert(entry.checksum.words()).second) + prologue->file_names.push_back(entry); + } + } else { + while (*offset_ptr < end_prologue_offset) { + s = debug_line_data.GetCStr(offset_ptr); + if (s && s[0]) + prologue->include_directories.push_back(s); + else + break; + } + + while (*offset_ptr < end_prologue_offset) { + const char *name = debug_line_data.GetCStr(offset_ptr); + if (name && name[0]) { + FileNameEntry fileEntry; + fileEntry.name = name; + fileEntry.dir_idx = debug_line_data.GetULEB128(offset_ptr); + fileEntry.mod_time = debug_line_data.GetULEB128(offset_ptr); + fileEntry.length = debug_line_data.GetULEB128(offset_ptr); + prologue->file_names.push_back(fileEntry); + } else + break; + } + } + + // XXX GNU as is broken for 64-Bit DWARF + if (*offset_ptr != end_prologue_offset) { + Host::SystemLog(Host::eSystemLogWarning, + "warning: parsing line table prologue at 0x%8.8" PRIx64 + " should have ended at 0x%8.8" PRIx64 + " but it ended at 0x%8.8" PRIx64 "\n", + prologue_offset, end_prologue_offset, *offset_ptr); + } + return end_prologue_offset; +} + +bool DWARFDebugLine::ParseSupportFiles( + const lldb::ModuleSP &module_sp, const DWARFDataExtractor &debug_line_data, + dw_offset_t stmt_list, FileSpecList &support_files, DWARFUnit *dwarf_cu) { + lldb::offset_t offset = stmt_list; + + Prologue prologue; + if (!ParsePrologue(debug_line_data, &offset, &prologue, dwarf_cu)) { + Host::SystemLog(Host::eSystemLogError, "error: parsing line table prologue " + "at 0x%8.8x (parsing ended around " + "0x%8.8" PRIx64 "\n", + stmt_list, offset); + return false; + } + + FileSpec file_spec; + std::string remapped_file; + + for (uint32_t file_idx = 1; + prologue.GetFile(file_idx, dwarf_cu->GetCompilationDirectory(), + dwarf_cu->GetPathStyle(), file_spec); + ++file_idx) { + if (module_sp->RemapSourceFile(file_spec.GetPath(), remapped_file)) + file_spec.SetFile(remapped_file, FileSpec::Style::native); + support_files.Append(file_spec); + } + return true; +} + +// ParseStatementTable +// +// Parse a single line table (prologue and all rows) and call the callback +// function once for the prologue (row in state will be zero) and each time a +// row is to be added to the line table. +bool DWARFDebugLine::ParseStatementTable( + const DWARFDataExtractor &debug_line_data, lldb::offset_t *offset_ptr, + DWARFDebugLine::State::Callback callback, void *userData, DWARFUnit *dwarf_cu) { + Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_LINE)); + Prologue::shared_ptr prologue(new Prologue()); + + const dw_offset_t debug_line_offset = *offset_ptr; + + static Timer::Category func_cat(LLVM_PRETTY_FUNCTION); + Timer scoped_timer( + func_cat, "DWARFDebugLine::ParseStatementTable (.debug_line[0x%8.8x])", + debug_line_offset); + + if (!ParsePrologue(debug_line_data, offset_ptr, prologue.get(), dwarf_cu)) { + if (log) + log->Error("failed to parse DWARF line table prologue"); + // Restore our offset and return false to indicate failure! + *offset_ptr = debug_line_offset; + return false; + } + + if (log) + prologue->Dump(log); + + const dw_offset_t end_offset = + debug_line_offset + prologue->total_length + + (debug_line_data.GetDWARFSizeofInitialLength()); + + State state(prologue, log, callback, userData); + + while (*offset_ptr < end_offset) { + // DEBUG_PRINTF("0x%8.8x: ", *offset_ptr); + uint8_t opcode = debug_line_data.GetU8(offset_ptr); + + if (opcode == 0) { + // Extended Opcodes always start with a zero opcode followed by a uleb128 + // length so you can skip ones you don't know about + lldb::offset_t ext_offset = *offset_ptr; + dw_uleb128_t len = debug_line_data.GetULEB128(offset_ptr); + dw_offset_t arg_size = len - (*offset_ptr - ext_offset); + + // DEBUG_PRINTF("Extended: <%2u> ", len); + uint8_t sub_opcode = debug_line_data.GetU8(offset_ptr); + switch (sub_opcode) { + case DW_LNE_end_sequence: + // Set the end_sequence register of the state machine to true and + // append a row to the matrix using the current values of the state- + // machine registers. Then reset the registers to the initial values + // specified above. Every statement program sequence must end with a + // DW_LNE_end_sequence instruction which creates a row whose address is + // that of the byte after the last target machine instruction of the + // sequence. + state.end_sequence = true; + state.AppendRowToMatrix(*offset_ptr); + state.Reset(); + break; + + case DW_LNE_set_address: + // Takes a single relocatable address as an operand. The size of the + // operand is the size appropriate to hold an address on the target + // machine. Set the address register to the value given by the + // relocatable address. All of the other statement program opcodes that + // affect the address register add a delta to it. This instruction + // stores a relocatable value into it instead. + if (arg_size == 4) + state.address = debug_line_data.GetU32(offset_ptr); + else // arg_size == 8 + state.address = debug_line_data.GetU64(offset_ptr); + break; + + case DW_LNE_define_file: + // Takes 4 arguments. The first is a null terminated string containing + // a source file name. The second is an unsigned LEB128 number + // representing the directory index of the directory in which the file + // was found. The third is an unsigned LEB128 number representing the + // time of last modification of the file. The fourth is an unsigned + // LEB128 number representing the length in bytes of the file. The time + // and length fields may contain LEB128(0) if the information is not + // available. + // + // The directory index represents an entry in the include_directories + // section of the statement program prologue. The index is LEB128(0) if + // the file was found in the current directory of the compilation, + // LEB128(1) if it was found in the first directory in the + // include_directories section, and so on. The directory index is + // ignored for file names that represent full path names. + // + // The files are numbered, starting at 1, in the order in which they + // appear; the names in the prologue come before names defined by the + // DW_LNE_define_file instruction. These numbers are used in the file + // register of the state machine. + { + FileNameEntry fileEntry; + fileEntry.name = debug_line_data.GetCStr(offset_ptr); + fileEntry.dir_idx = debug_line_data.GetULEB128(offset_ptr); + fileEntry.mod_time = debug_line_data.GetULEB128(offset_ptr); + fileEntry.length = debug_line_data.GetULEB128(offset_ptr); + state.prologue->file_names.push_back(fileEntry); + } + break; + + default: + // Length doesn't include the zero opcode byte or the length itself, + // but it does include the sub_opcode, so we have to adjust for that + // below + (*offset_ptr) += arg_size; + break; + } + } else if (opcode < prologue->opcode_base) { + switch (opcode) { + // Standard Opcodes + case DW_LNS_copy: + // Takes no arguments. Append a row to the matrix using the current + // values of the state-machine registers. Then set the basic_block + // register to false. + state.AppendRowToMatrix(*offset_ptr); + break; + + case DW_LNS_advance_pc: + // Takes a single unsigned LEB128 operand, multiplies it by the + // min_inst_length field of the prologue, and adds the result to the + // address register of the state machine. + state.address += + debug_line_data.GetULEB128(offset_ptr) * prologue->min_inst_length; + break; + + case DW_LNS_advance_line: + // Takes a single signed LEB128 operand and adds that value to the line + // register of the state machine. + state.line += debug_line_data.GetSLEB128(offset_ptr); + break; + + case DW_LNS_set_file: + // Takes a single unsigned LEB128 operand and stores it in the file + // register of the state machine. + state.file = debug_line_data.GetULEB128(offset_ptr); + break; + + case DW_LNS_set_column: + // Takes a single unsigned LEB128 operand and stores it in the column + // register of the state machine. + state.column = debug_line_data.GetULEB128(offset_ptr); + break; + + case DW_LNS_negate_stmt: + // Takes no arguments. Set the is_stmt register of the state machine to + // the logical negation of its current value. + state.is_stmt = !state.is_stmt; + break; + + case DW_LNS_set_basic_block: + // Takes no arguments. Set the basic_block register of the state + // machine to true + state.basic_block = true; + break; + + case DW_LNS_const_add_pc: + // Takes no arguments. Add to the address register of the state machine + // the address increment value corresponding to special opcode 255. The + // motivation for DW_LNS_const_add_pc is this: when the statement + // program needs to advance the address by a small amount, it can use a + // single special opcode, which occupies a single byte. When it needs + // to advance the address by up to twice the range of the last special + // opcode, it can use DW_LNS_const_add_pc followed by a special opcode, + // for a total of two bytes. Only if it needs to advance the address by + // more than twice that range will it need to use both + // DW_LNS_advance_pc and a special opcode, requiring three or more + // bytes. + { + uint8_t adjust_opcode = 255 - prologue->opcode_base; + dw_addr_t addr_offset = (adjust_opcode / prologue->line_range) * + prologue->min_inst_length; + state.address += addr_offset; + } + break; + + case DW_LNS_fixed_advance_pc: + // Takes a single uhalf operand. Add to the address register of the + // state machine the value of the (unencoded) operand. This is the only + // extended opcode that takes an argument that is not a variable length + // number. The motivation for DW_LNS_fixed_advance_pc is this: existing + // assemblers cannot emit DW_LNS_advance_pc or special opcodes because + // they cannot encode LEB128 numbers or judge when the computation of a + // special opcode overflows and requires the use of DW_LNS_advance_pc. + // Such assemblers, however, can use DW_LNS_fixed_advance_pc instead, + // sacrificing compression. + state.address += debug_line_data.GetU16(offset_ptr); + break; + + case DW_LNS_set_prologue_end: + // Takes no arguments. Set the prologue_end register of the state + // machine to true + state.prologue_end = true; + break; + + case DW_LNS_set_epilogue_begin: + // Takes no arguments. Set the basic_block register of the state + // machine to true + state.epilogue_begin = true; + break; + + case DW_LNS_set_isa: + // Takes a single unsigned LEB128 operand and stores it in the column + // register of the state machine. + state.isa = debug_line_data.GetULEB128(offset_ptr); + break; + + default: + // Handle any unknown standard opcodes here. We know the lengths of + // such opcodes because they are specified in the prologue as a + // multiple of LEB128 operands for each opcode. + { + uint8_t i; + assert(static_cast<size_t>(opcode - 1) < + prologue->standard_opcode_lengths.size()); + const uint8_t opcode_length = + prologue->standard_opcode_lengths[opcode - 1]; + for (i = 0; i < opcode_length; ++i) + debug_line_data.Skip_LEB128(offset_ptr); + } + break; + } + } else { + // Special Opcodes + + // A special opcode value is chosen based on the amount that needs + // to be added to the line and address registers. The maximum line + // increment for a special opcode is the value of the line_base field in + // the header, plus the value of the line_range field, minus 1 (line base + // + line range - 1). If the desired line increment is greater than the + // maximum line increment, a standard opcode must be used instead of a + // special opcode. The "address advance" is calculated by dividing the + // desired address increment by the minimum_instruction_length field from + // the header. The special opcode is then calculated using the following + // formula: + // + // opcode = (desired line increment - line_base) + (line_range * address + // advance) + opcode_base + // + // If the resulting opcode is greater than 255, a standard opcode must be + // used instead. + // + // To decode a special opcode, subtract the opcode_base from the opcode + // itself to give the adjusted opcode. The amount to increment the + // address register is the result of the adjusted opcode divided by the + // line_range multiplied by the minimum_instruction_length field from the + // header. That is: + // + // address increment = (adjusted opcode / line_range) * + // minimum_instruction_length + // + // The amount to increment the line register is the line_base plus the + // result of the adjusted opcode modulo the line_range. That is: + // + // line increment = line_base + (adjusted opcode % line_range) + + uint8_t adjust_opcode = opcode - prologue->opcode_base; + dw_addr_t addr_offset = + (adjust_opcode / prologue->line_range) * prologue->min_inst_length; + int32_t line_offset = + prologue->line_base + (adjust_opcode % prologue->line_range); + state.line += line_offset; + state.address += addr_offset; + state.AppendRowToMatrix(*offset_ptr); + } + } + + state.Finalize(*offset_ptr); + + return end_offset; +} + +// ParseStatementTableCallback +static void ParseStatementTableCallback(dw_offset_t offset, + const DWARFDebugLine::State &state, + void *userData) { + DWARFDebugLine::LineTable *line_table = (DWARFDebugLine::LineTable *)userData; + if (state.row == DWARFDebugLine::State::StartParsingLineTable) { + // Just started parsing the line table, so lets keep a reference to the + // prologue using the supplied shared pointer + line_table->prologue = state.prologue; + } else if (state.row == DWARFDebugLine::State::DoneParsingLineTable) { + // Done parsing line table, nothing to do for the cleanup + } else { + // We have a new row, lets append it + line_table->AppendRow(state); + } +} + +// ParseStatementTable +// +// Parse a line table at offset and populate the LineTable class with the +// prologue and all rows. +bool DWARFDebugLine::ParseStatementTable( + const DWARFDataExtractor &debug_line_data, lldb::offset_t *offset_ptr, + LineTable *line_table, DWARFUnit *dwarf_cu) { + return ParseStatementTable(debug_line_data, offset_ptr, + ParseStatementTableCallback, line_table, dwarf_cu); +} + +inline bool DWARFDebugLine::Prologue::IsValid() const { + return SymbolFileDWARF::SupportedVersion(version); +} + +// DWARFDebugLine::Prologue::Dump +void DWARFDebugLine::Prologue::Dump(Log *log) { + uint32_t i; + + log->Printf("Line table prologue:"); + log->Printf(" total_length: 0x%8.8x", total_length); + log->Printf(" version: %u", version); + log->Printf("prologue_length: 0x%8.8x", prologue_length); + log->Printf("min_inst_length: %u", min_inst_length); + log->Printf("default_is_stmt: %u", default_is_stmt); + log->Printf(" line_base: %i", line_base); + log->Printf(" line_range: %u", line_range); + log->Printf(" opcode_base: %u", opcode_base); + + for (i = 0; i < standard_opcode_lengths.size(); ++i) { + log->Printf("standard_opcode_lengths[%s] = %u", DW_LNS_value_to_name(i + 1), + standard_opcode_lengths[i]); + } + + if (!include_directories.empty()) { + for (i = 0; i < include_directories.size(); ++i) { + log->Printf("include_directories[%3u] = '%s'", i + 1, + include_directories[i]); + } + } + + if (!file_names.empty()) { + log->PutCString(" Dir Mod Time File Len File Name"); + log->PutCString(" ---- ---------- ---------- " + "---------------------------"); + for (i = 0; i < file_names.size(); ++i) { + const FileNameEntry &fileEntry = file_names[i]; + log->Printf("file_names[%3u] %4u 0x%8.8x 0x%8.8x %s", i + 1, + fileEntry.dir_idx, fileEntry.mod_time, fileEntry.length, + fileEntry.name); + } + } +} + +// DWARFDebugLine::ParsePrologue::Append +// +// Append the contents of the prologue to the binary stream buffer +// void +// DWARFDebugLine::Prologue::Append(BinaryStreamBuf& buff) const +//{ +// uint32_t i; +// +// buff.Append32(total_length); +// buff.Append16(version); +// buff.Append32(prologue_length); +// buff.Append8(min_inst_length); +// buff.Append8(default_is_stmt); +// buff.Append8(line_base); +// buff.Append8(line_range); +// buff.Append8(opcode_base); +// +// for (i=0; i<standard_opcode_lengths.size(); ++i) +// buff.Append8(standard_opcode_lengths[i]); +// +// for (i=0; i<include_directories.size(); ++i) +// buff.AppendCStr(include_directories[i].c_str()); +// buff.Append8(0); // Terminate the include directory section with empty +// string +// +// for (i=0; i<file_names.size(); ++i) +// { +// buff.AppendCStr(file_names[i].name.c_str()); +// buff.Append32_as_ULEB128(file_names[i].dir_idx); +// buff.Append32_as_ULEB128(file_names[i].mod_time); +// buff.Append32_as_ULEB128(file_names[i].length); +// } +// buff.Append8(0); // Terminate the file names section with empty string +//} + +bool DWARFDebugLine::Prologue::GetFile(uint32_t file_idx, + const FileSpec &comp_dir, + FileSpec::Style style, + FileSpec &file) const { + uint32_t idx = file_idx - 1; // File indexes are 1 based... + if (idx < file_names.size()) { + file.SetFile(file_names[idx].name, style); + if (file.IsRelative()) { + if (file_names[idx].dir_idx > 0) { + const uint32_t dir_idx = file_names[idx].dir_idx - 1; + if (dir_idx < include_directories.size()) { + file.PrependPathComponent(include_directories[dir_idx]); + if (!file.IsRelative()) + return true; + } + } + + if (comp_dir) + file.PrependPathComponent(comp_dir); + } + return true; + } + return false; +} + +void DWARFDebugLine::LineTable::AppendRow(const DWARFDebugLine::Row &state) { + rows.push_back(state); +} + +// Compare function for the binary search in +// DWARFDebugLine::LineTable::LookupAddress() +static bool FindMatchingAddress(const DWARFDebugLine::Row &row1, + const DWARFDebugLine::Row &row2) { + return row1.address < row2.address; +} + +// DWARFDebugLine::LineTable::LookupAddress +uint32_t DWARFDebugLine::LineTable::LookupAddress(dw_addr_t address, + dw_addr_t cu_high_pc) const { + uint32_t index = UINT32_MAX; + if (!rows.empty()) { + // Use the lower_bound algorithm to perform a binary search since we know + // that our line table data is ordered by address. + DWARFDebugLine::Row row; + row.address = address; + Row::const_iterator begin_pos = rows.begin(); + Row::const_iterator end_pos = rows.end(); + Row::const_iterator pos = + lower_bound(begin_pos, end_pos, row, FindMatchingAddress); + if (pos == end_pos) { + if (address < cu_high_pc) + return rows.size() - 1; + } else { + // Rely on fact that we are using a std::vector and we can do pointer + // arithmetic to find the row index (which will be one less that what we + // found since it will find the first position after the current address) + // since std::vector iterators are just pointers to the container type. + index = pos - begin_pos; + if (pos->address > address) { + if (index > 0) + --index; + else + index = UINT32_MAX; + } + } + } + return index; // Failed to find address +} + +// DWARFDebugLine::Row::Row +DWARFDebugLine::Row::Row(bool default_is_stmt) + : address(0), line(1), column(0), file(1), is_stmt(default_is_stmt), + basic_block(false), end_sequence(false), prologue_end(false), + epilogue_begin(false), isa(0) {} + +// Called after a row is appended to the matrix +void DWARFDebugLine::Row::PostAppend() { + basic_block = false; + prologue_end = false; + epilogue_begin = false; +} + +// DWARFDebugLine::Row::Reset +void DWARFDebugLine::Row::Reset(bool default_is_stmt) { + address = 0; + line = 1; + column = 0; + file = 1; + is_stmt = default_is_stmt; + basic_block = false; + end_sequence = false; + prologue_end = false; + epilogue_begin = false; + isa = 0; +} +// DWARFDebugLine::Row::Dump +void DWARFDebugLine::Row::Dump(Log *log) const { + log->Printf("0x%16.16" PRIx64 " %6u %6u %6u %3u %s%s%s%s%s", address, line, + column, file, isa, is_stmt ? " is_stmt" : "", + basic_block ? " basic_block" : "", + prologue_end ? " prologue_end" : "", + epilogue_begin ? " epilogue_begin" : "", + end_sequence ? " end_sequence" : ""); +} + +// Compare function LineTable structures +static bool AddressLessThan(const DWARFDebugLine::Row &a, + const DWARFDebugLine::Row &b) { + return a.address < b.address; +} + +// Insert a row at the correct address if the addresses can be out of order +// which can only happen when we are linking a line table that may have had +// it's contents rearranged. +void DWARFDebugLine::Row::Insert(Row::collection &state_coll, + const Row &state) { + // If we don't have anything yet, or if the address of the last state in our + // line table is less than the current one, just append the current state + if (state_coll.empty() || AddressLessThan(state_coll.back(), state)) { + state_coll.push_back(state); + } else { + // Do a binary search for the correct entry + pair<Row::iterator, Row::iterator> range(equal_range( + state_coll.begin(), state_coll.end(), state, AddressLessThan)); + + // If the addresses are equal, we can safely replace the previous entry + // with the current one if the one it is replacing is an end_sequence + // entry. We currently always place an extra end sequence when ever we exit + // a valid address range for a function in case the functions get + // rearranged by optimizations or by order specifications. These extra end + // sequences will disappear by getting replaced with valid consecutive + // entries within a compile unit if there are no gaps. + if (range.first == range.second) { + state_coll.insert(range.first, state); + } else { + if ((distance(range.first, range.second) == 1) && + range.first->end_sequence == true) { + *range.first = state; + } else { + state_coll.insert(range.second, state); + } + } + } +} + +// DWARFDebugLine::State::State +DWARFDebugLine::State::State(Prologue::shared_ptr &p, Log *l, + DWARFDebugLine::State::Callback cb, void *userData) + : Row(p->default_is_stmt), prologue(p), log(l), callback(cb), + callbackUserData(userData), row(StartParsingLineTable) { + // Call the callback with the initial row state of zero for the prologue + if (callback) + callback(0, *this, callbackUserData); +} + +// DWARFDebugLine::State::Reset +void DWARFDebugLine::State::Reset() { Row::Reset(prologue->default_is_stmt); } + +// DWARFDebugLine::State::AppendRowToMatrix +void DWARFDebugLine::State::AppendRowToMatrix(dw_offset_t offset) { + // Each time we are to add an entry into the line table matrix call the + // callback function so that someone can do something with the current state + // of the state machine (like build a line table or dump the line table!) + if (log) { + if (row == 0) { + log->PutCString("Address Line Column File ISA Flags"); + log->PutCString( + "------------------ ------ ------ ------ --- -------------"); + } + Dump(log); + } + + ++row; // Increase the row number before we call our callback for a real row + if (callback) + callback(offset, *this, callbackUserData); + PostAppend(); +} + +// DWARFDebugLine::State::Finalize +void DWARFDebugLine::State::Finalize(dw_offset_t offset) { + // Call the callback with a special row state when we are done parsing a line + // table + row = DoneParsingLineTable; + if (callback) + callback(offset, *this, callbackUserData); +} + +// void +// DWARFDebugLine::AppendLineTableData +//( +// const DWARFDebugLine::Prologue* prologue, +// const DWARFDebugLine::Row::collection& state_coll, +// const uint32_t addr_size, +// BinaryStreamBuf &debug_line_data +//) +//{ +// if (state_coll.empty()) +// { +// // We have no entries, just make an empty line table +// debug_line_data.Append8(0); +// debug_line_data.Append8(1); +// debug_line_data.Append8(DW_LNE_end_sequence); +// } +// else +// { +// DWARFDebugLine::Row::const_iterator pos; +// Row::const_iterator end = state_coll.end(); +// bool default_is_stmt = prologue->default_is_stmt; +// const DWARFDebugLine::Row reset_state(default_is_stmt); +// const DWARFDebugLine::Row* prev_state = &reset_state; +// const int32_t max_line_increment_for_special_opcode = +// prologue->MaxLineIncrementForSpecialOpcode(); +// for (pos = state_coll.begin(); pos != end; ++pos) +// { +// const DWARFDebugLine::Row& curr_state = *pos; +// int32_t line_increment = 0; +// dw_addr_t addr_offset = curr_state.address - prev_state->address; +// dw_addr_t addr_advance = (addr_offset) / prologue->min_inst_length; +// line_increment = (int32_t)(curr_state.line - prev_state->line); +// +// // If our previous state was the reset state, then let's emit the +// // address to keep GDB's DWARF parser happy. If we don't start each +// // sequence with a DW_LNE_set_address opcode, the line table won't +// // get slid properly in GDB. +// +// if (prev_state == &reset_state) +// { +// debug_line_data.Append8(0); // Extended opcode +// debug_line_data.Append32_as_ULEB128(addr_size + 1); // Length of +// opcode bytes +// debug_line_data.Append8(DW_LNE_set_address); +// debug_line_data.AppendMax64(curr_state.address, addr_size); +// addr_advance = 0; +// } +// +// if (prev_state->file != curr_state.file) +// { +// debug_line_data.Append8(DW_LNS_set_file); +// debug_line_data.Append32_as_ULEB128(curr_state.file); +// } +// +// if (prev_state->column != curr_state.column) +// { +// debug_line_data.Append8(DW_LNS_set_column); +// debug_line_data.Append32_as_ULEB128(curr_state.column); +// } +// +// // Don't do anything fancy if we are at the end of a sequence +// // as we don't want to push any extra rows since the +// DW_LNE_end_sequence +// // will push a row itself! +// if (curr_state.end_sequence) +// { +// if (line_increment != 0) +// { +// debug_line_data.Append8(DW_LNS_advance_line); +// debug_line_data.Append32_as_SLEB128(line_increment); +// } +// +// if (addr_advance > 0) +// { +// debug_line_data.Append8(DW_LNS_advance_pc); +// debug_line_data.Append32_as_ULEB128(addr_advance); +// } +// +// // Now push the end sequence on! +// debug_line_data.Append8(0); +// debug_line_data.Append8(1); +// debug_line_data.Append8(DW_LNE_end_sequence); +// +// prev_state = &reset_state; +// } +// else +// { +// if (line_increment || addr_advance) +// { +// if (line_increment > max_line_increment_for_special_opcode) +// { +// debug_line_data.Append8(DW_LNS_advance_line); +// debug_line_data.Append32_as_SLEB128(line_increment); +// line_increment = 0; +// } +// +// uint32_t special_opcode = (line_increment >= +// prologue->line_base) ? ((line_increment - +// prologue->line_base) + (prologue->line_range * addr_advance) +// + prologue->opcode_base) : 256; +// if (special_opcode > 255) +// { +// // Both the address and line won't fit in one special +// opcode +// // check to see if just the line advance will? +// uint32_t special_opcode_line = ((line_increment >= +// prologue->line_base) && (line_increment != 0)) ? +// ((line_increment - prologue->line_base) + +// prologue->opcode_base) : 256; +// +// +// if (special_opcode_line > 255) +// { +// // Nope, the line advance won't fit by itself, check +// the address increment by itself +// uint32_t special_opcode_addr = addr_advance ? +// ((0 - prologue->line_base) + +// (prologue->line_range * addr_advance) + +// prologue->opcode_base) : 256; +// +// if (special_opcode_addr > 255) +// { +// // Neither the address nor the line will fit in +// a +// // special opcode, we must manually enter both +// then +// // do a DW_LNS_copy to push a row (special +// opcode +// // automatically imply a new row is pushed) +// if (line_increment != 0) +// { +// debug_line_data.Append8(DW_LNS_advance_line); +// debug_line_data.Append32_as_SLEB128(line_increment); +// } +// +// if (addr_advance > 0) +// { +// debug_line_data.Append8(DW_LNS_advance_pc); +// debug_line_data.Append32_as_ULEB128(addr_advance); +// } +// +// // Now push a row onto the line table manually +// debug_line_data.Append8(DW_LNS_copy); +// +// } +// else +// { +// // The address increment alone will fit into a +// special opcode +// // so modify our line change, then issue a +// special opcode +// // for the address increment and it will push a +// row into the +// // line table +// if (line_increment != 0) +// { +// debug_line_data.Append8(DW_LNS_advance_line); +// debug_line_data.Append32_as_SLEB128(line_increment); +// } +// +// // Advance of line and address will fit into a +// single byte special opcode +// // and this will also push a row onto the line +// table +// debug_line_data.Append8(special_opcode_addr); +// } +// } +// else +// { +// // The line change alone will fit into a special +// opcode +// // so modify our address increment first, then issue +// a +// // special opcode for the line change and it will +// push +// // a row into the line table +// if (addr_advance > 0) +// { +// debug_line_data.Append8(DW_LNS_advance_pc); +// debug_line_data.Append32_as_ULEB128(addr_advance); +// } +// +// // Advance of line and address will fit into a +// single byte special opcode +// // and this will also push a row onto the line table +// debug_line_data.Append8(special_opcode_line); +// } +// } +// else +// { +// // Advance of line and address will fit into a single +// byte special opcode +// // and this will also push a row onto the line table +// debug_line_data.Append8(special_opcode); +// } +// } +// prev_state = &curr_state; +// } +// } +// } +//} diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugLine.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugLine.h new file mode 100644 index 000000000000..0d236ca686b5 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugLine.h @@ -0,0 +1,227 @@ +//===-- DWARFDebugLine.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 SymbolFileDWARF_DWARFDebugLine_h_ +#define SymbolFileDWARF_DWARFDebugLine_h_ + +#include <map> +#include <string> +#include <vector> + +#include "lldb/Utility/FileSpec.h" +#include "lldb/lldb-private.h" + +#include "DWARFDataExtractor.h" +#include "DWARFDefines.h" + +#include "llvm/Support/MD5.h" + +class DWARFUnit; +class SymbolFileDWARF; + +// DWARFDebugLine +class DWARFDebugLine { +public: + // FileNameEntry + struct FileNameEntry { + FileNameEntry() + : name(nullptr), dir_idx(0), mod_time(0), length(0), checksum() {} + + const char *name; + dw_sleb128_t dir_idx; + dw_sleb128_t mod_time; + dw_sleb128_t length; + llvm::MD5::MD5Result checksum; + }; + + // Prologue + struct Prologue { + + Prologue() + : total_length(0), version(0), prologue_length(0), min_inst_length(0), + default_is_stmt(0), line_base(0), line_range(0), opcode_base(0), + standard_opcode_lengths(), include_directories(), file_names() {} + + typedef std::shared_ptr<Prologue> shared_ptr; + + uint32_t total_length; // The size in bytes of the statement information for + // this compilation unit (not including the + // total_length field itself). + uint16_t + version; // Version identifier for the statement information format. + + uint8_t address_size; + uint8_t segment_selector_size; + + uint32_t prologue_length; // The number of bytes following the + // prologue_length field to the beginning of the + // first byte of the statement program itself. + uint8_t min_inst_length; // The size in bytes of the smallest target machine + // instruction. Statement program opcodes that + // alter the address register first multiply their + // operands by this value. + uint8_t maximum_operations_per_instruction; // New in DWARF4. The maximum + // number of individual + // operations that may be + // encoded in an instruction. + uint8_t default_is_stmt; // The initial value of theis_stmtregister. + int8_t line_base; // This parameter affects the meaning of the special + // opcodes. See below. + uint8_t line_range; // This parameter affects the meaning of the special + // opcodes. See below. + uint8_t opcode_base; // The number assigned to the first special opcode. + std::vector<uint8_t> standard_opcode_lengths; + std::vector<const char *> include_directories; + std::vector<FileNameEntry> file_names; + + int32_t MaxLineIncrementForSpecialOpcode() const { + return line_base + (int8_t)line_range - 1; + } + bool IsValid() const; + // void Append(BinaryStreamBuf& buff) const; + void Dump(lldb_private::Log *log); + void Clear() { + total_length = version = prologue_length = min_inst_length = line_base = + line_range = opcode_base = 0; + line_base = 0; + standard_opcode_lengths.clear(); + include_directories.clear(); + file_names.clear(); + } + bool GetFile(uint32_t file_idx, const lldb_private::FileSpec &cu_comp_dir, + lldb_private::FileSpec::Style style, + lldb_private::FileSpec &file) const; + }; + + // Standard .debug_line state machine structure + struct Row { + typedef std::vector<Row> collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + + Row(bool default_is_stmt = false); + virtual ~Row() {} + void PostAppend(); + void Reset(bool default_is_stmt); + void Dump(lldb_private::Log *log) const; + static void Insert(Row::collection &state_coll, const Row &state); + + dw_addr_t address; // The program-counter value corresponding to a machine + // instruction generated by the compiler. + uint32_t line; // An unsigned integer indicating a source line number. Lines + // are numbered beginning at 1. The compiler may emit the + // value 0 in cases where an instruction cannot be attributed + // to any source line. + uint16_t column; // An unsigned integer indicating a column number within a + // source line. Columns are numbered beginning at 1. The + // value 0 is reserved to indicate that a statement begins + // at the 'left edge' of the line. + uint16_t file; // An unsigned integer indicating the identity of the source + // file corresponding to a machine instruction. + uint8_t is_stmt : 1, // A boolean indicating that the current instruction is + // the beginning of a statement. + basic_block : 1, // A boolean indicating that the current instruction is + // the beginning of a basic block. + end_sequence : 1, // A boolean indicating that the current address is + // that of the first byte after the end of a sequence + // of target machine instructions. + prologue_end : 1, // A boolean indicating that the current address is + // one (of possibly many) where execution should be + // suspended for an entry breakpoint of a function. + epilogue_begin : 1; // A boolean indicating that the current address is + // one (of possibly many) where execution should be + // suspended for an exit breakpoint of a function. + uint32_t isa; // An unsigned integer whose value encodes the applicable + // instruction set architecture for the current instruction. + }; + + // LineTable + struct LineTable { + typedef std::shared_ptr<LineTable> shared_ptr; + + LineTable() : prologue(), rows() {} + + void AppendRow(const DWARFDebugLine::Row &state); + void Clear() { + prologue.reset(); + rows.clear(); + } + + uint32_t LookupAddress(dw_addr_t address, dw_addr_t cu_high_pc) const; + + Prologue::shared_ptr prologue; + Row::collection rows; + }; + + // State + struct State : public Row { + typedef void (*Callback)(dw_offset_t offset, const State &state, + void *userData); + + // Special row codes used when calling the callback + enum { StartParsingLineTable = 0, DoneParsingLineTable = -1 }; + + State(Prologue::shared_ptr &prologue_sp, lldb_private::Log *log, + Callback callback, void *userData); + + void AppendRowToMatrix(dw_offset_t offset); + + void Finalize(dw_offset_t offset); + + void Reset(); + + Prologue::shared_ptr prologue; + lldb_private::Log *log; + Callback callback; // Callback function that gets called each time an entry + // is to be added to the matrix + void *callbackUserData; + int row; // The row number that starts at zero for the prologue, and + // increases for each row added to the matrix + private: + DISALLOW_COPY_AND_ASSIGN(State); + }; + + static bool + ParseSupportFiles(const lldb::ModuleSP &module_sp, + const lldb_private::DWARFDataExtractor &debug_line_data, + dw_offset_t stmt_list, + lldb_private::FileSpecList &support_files, + DWARFUnit *dwarf_cu); + static bool + ParsePrologue(const lldb_private::DWARFDataExtractor &debug_line_data, + lldb::offset_t *offset_ptr, Prologue *prologue, + DWARFUnit *dwarf_cu = nullptr); + static bool + ParseStatementTable(const lldb_private::DWARFDataExtractor &debug_line_data, + lldb::offset_t *offset_ptr, State::Callback callback, + void *userData, DWARFUnit *dwarf_cu); + static bool + ParseStatementTable(const lldb_private::DWARFDataExtractor &debug_line_data, + lldb::offset_t *offset_ptr, LineTable *line_table, + DWARFUnit *dwarf_cu); + static void Parse(const lldb_private::DWARFDataExtractor &debug_line_data, + DWARFDebugLine::State::Callback callback, void *userData); + // static void AppendLineTableData(const DWARFDebugLine::Prologue* prologue, + // const DWARFDebugLine::Row::collection& state_coll, const uint32_t + // addr_size, BinaryStreamBuf &debug_line_data); + + DWARFDebugLine() : m_lineTableMap() {} + + void Parse(const lldb_private::DWARFDataExtractor &debug_line_data); + void ParseIfNeeded(const lldb_private::DWARFDataExtractor &debug_line_data); + LineTable::shared_ptr GetLineTable(const dw_offset_t offset) const; + +protected: + typedef std::map<dw_offset_t, LineTable::shared_ptr> LineTableMap; + typedef LineTableMap::iterator LineTableIter; + typedef LineTableMap::const_iterator LineTableConstIter; + + LineTableMap m_lineTableMap; +}; + +#endif // SymbolFileDWARF_DWARFDebugLine_h_ diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugMacro.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugMacro.cpp new file mode 100644 index 000000000000..4238be7ec1c3 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugMacro.cpp @@ -0,0 +1,126 @@ +//===-- DWARFDebugMacro.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 "DWARFDebugMacro.h" +#include "SymbolFileDWARF.h" + +#include "lldb/Symbol/DebugMacros.h" + +#include "DWARFDataExtractor.h" + +using namespace lldb_private; + +DWARFDebugMacroHeader +DWARFDebugMacroHeader::ParseHeader(const DWARFDataExtractor &debug_macro_data, + lldb::offset_t *offset) { + DWARFDebugMacroHeader header; + + // Skip over the version field in header. + header.m_version = debug_macro_data.GetU16(offset); + + uint8_t flags = debug_macro_data.GetU8(offset); + header.m_offset_is_64_bit = (flags & OFFSET_SIZE_MASK) != 0; + + if (flags & DEBUG_LINE_OFFSET_MASK) { + if (header.m_offset_is_64_bit) + header.m_debug_line_offset = debug_macro_data.GetU64(offset); + else + header.m_debug_line_offset = debug_macro_data.GetU32(offset); + } + + // Skip over the operands table if it is present. + if (flags & OPCODE_OPERANDS_TABLE_MASK) + SkipOperandTable(debug_macro_data, offset); + + return header; +} + +void DWARFDebugMacroHeader::SkipOperandTable( + const DWARFDataExtractor &debug_macro_data, lldb::offset_t *offset) { + uint8_t entry_count = debug_macro_data.GetU8(offset); + for (uint8_t i = 0; i < entry_count; i++) { + // Skip over the opcode number. + debug_macro_data.GetU8(offset); + + uint64_t operand_count = debug_macro_data.GetULEB128(offset); + + for (uint64_t j = 0; j < operand_count; j++) { + // Skip over the operand form + debug_macro_data.GetU8(offset); + } + } +} + +void DWARFDebugMacroEntry::ReadMacroEntries( + const DWARFDataExtractor &debug_macro_data, + const DWARFDataExtractor &debug_str_data, const bool offset_is_64_bit, + lldb::offset_t *offset, SymbolFileDWARF *sym_file_dwarf, + DebugMacrosSP &debug_macros_sp) { + llvm::dwarf::MacroEntryType type = + static_cast<llvm::dwarf::MacroEntryType>(debug_macro_data.GetU8(offset)); + while (type != 0) { + lldb::offset_t new_offset = 0, str_offset = 0; + uint32_t line = 0; + const char *macro_str = nullptr; + uint32_t debug_line_file_idx = 0; + + switch (type) { + case DW_MACRO_define: + case DW_MACRO_undef: + line = debug_macro_data.GetULEB128(offset); + macro_str = debug_macro_data.GetCStr(offset); + if (type == DW_MACRO_define) + debug_macros_sp->AddMacroEntry( + DebugMacroEntry::CreateDefineEntry(line, macro_str)); + else + debug_macros_sp->AddMacroEntry( + DebugMacroEntry::CreateUndefEntry(line, macro_str)); + break; + case DW_MACRO_define_strp: + case DW_MACRO_undef_strp: + line = debug_macro_data.GetULEB128(offset); + if (offset_is_64_bit) + str_offset = debug_macro_data.GetU64(offset); + else + str_offset = debug_macro_data.GetU32(offset); + macro_str = debug_str_data.GetCStr(&str_offset); + if (type == DW_MACRO_define_strp) + debug_macros_sp->AddMacroEntry( + DebugMacroEntry::CreateDefineEntry(line, macro_str)); + else + debug_macros_sp->AddMacroEntry( + DebugMacroEntry::CreateUndefEntry(line, macro_str)); + break; + case DW_MACRO_start_file: + line = debug_macro_data.GetULEB128(offset); + debug_line_file_idx = debug_macro_data.GetULEB128(offset); + debug_macros_sp->AddMacroEntry( + DebugMacroEntry::CreateStartFileEntry(line, debug_line_file_idx)); + break; + case DW_MACRO_end_file: + // This operation has no operands. + debug_macros_sp->AddMacroEntry(DebugMacroEntry::CreateEndFileEntry()); + break; + case DW_MACRO_import: + if (offset_is_64_bit) + new_offset = debug_macro_data.GetU64(offset); + else + new_offset = debug_macro_data.GetU32(offset); + debug_macros_sp->AddMacroEntry(DebugMacroEntry::CreateIndirectEntry( + sym_file_dwarf->ParseDebugMacros(&new_offset))); + break; + default: + // TODO: Add support for other standard operations. + // TODO: Provide mechanism to hook handling of non-standard/extension + // operands. + return; + } + type = static_cast<llvm::dwarf::MacroEntryType>( + debug_macro_data.GetU8(offset)); + } +} diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugMacro.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugMacro.h new file mode 100644 index 000000000000..c3a93a9f4d14 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugMacro.h @@ -0,0 +1,61 @@ +//===-- DWARFDebugMacro.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 SymbolFileDWARF_DWARFDebugMacro_h_ +#define SymbolFileDWARF_DWARFDebugMacro_h_ + +#include <map> + +#include "lldb/Core/dwarf.h" +#include "lldb/Symbol/DebugMacros.h" +#include "lldb/lldb-types.h" + +namespace lldb_private { + +class DWARFDataExtractor; + +} // namespace lldb_private + +class SymbolFileDWARF; + +class DWARFDebugMacroHeader { +public: + enum HeaderFlagMask { + OFFSET_SIZE_MASK = 0x1, + DEBUG_LINE_OFFSET_MASK = 0x2, + OPCODE_OPERANDS_TABLE_MASK = 0x4 + }; + + static DWARFDebugMacroHeader + ParseHeader(const lldb_private::DWARFDataExtractor &debug_macro_data, + lldb::offset_t *offset); + + bool OffsetIs64Bit() const { return m_offset_is_64_bit; } + +private: + static void + SkipOperandTable(const lldb_private::DWARFDataExtractor &debug_macro_data, + lldb::offset_t *offset); + + uint16_t m_version; + bool m_offset_is_64_bit; + uint64_t m_debug_line_offset; +}; + +class DWARFDebugMacroEntry { +public: + static void + ReadMacroEntries(const lldb_private::DWARFDataExtractor &debug_macro_data, + const lldb_private::DWARFDataExtractor &debug_str_data, + const bool offset_is_64_bit, lldb::offset_t *sect_offset, + SymbolFileDWARF *sym_file_dwarf, + lldb_private::DebugMacrosSP &debug_macros_sp); +}; + +#endif // SymbolFileDWARF_DWARFDebugMacro_h_ diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugRanges.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugRanges.cpp new file mode 100644 index 000000000000..207c71211c9a --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugRanges.cpp @@ -0,0 +1,290 @@ +//===-- DWARFDebugRanges.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 "DWARFDebugRanges.h" +#include "DWARFUnit.h" +#include "lldb/Utility/Stream.h" + +using namespace lldb_private; + +static dw_addr_t GetBaseAddressMarker(uint32_t addr_size) { + switch(addr_size) { + case 2: + return 0xffff; + case 4: + return 0xffffffff; + case 8: + return 0xffffffffffffffff; + } + llvm_unreachable("GetBaseAddressMarker unsupported address size."); +} + +DWARFDebugRanges::DWARFDebugRanges() : m_range_map() {} + +void DWARFDebugRanges::Extract(DWARFContext &context) { + DWARFRangeList range_list; + lldb::offset_t offset = 0; + dw_offset_t debug_ranges_offset = offset; + while (Extract(context, &offset, range_list)) { + range_list.Sort(); + m_range_map[debug_ranges_offset] = range_list; + debug_ranges_offset = offset; + } +} + +bool DWARFDebugRanges::Extract(DWARFContext &context, + lldb::offset_t *offset_ptr, + DWARFRangeList &range_list) { + range_list.Clear(); + + lldb::offset_t range_offset = *offset_ptr; + const DWARFDataExtractor &debug_ranges_data = context.getOrLoadRangesData(); + uint32_t addr_size = debug_ranges_data.GetAddressByteSize(); + dw_addr_t base_addr = 0; + dw_addr_t base_addr_marker = GetBaseAddressMarker(addr_size); + + while ( + debug_ranges_data.ValidOffsetForDataOfSize(*offset_ptr, 2 * addr_size)) { + dw_addr_t begin = debug_ranges_data.GetMaxU64(offset_ptr, addr_size); + dw_addr_t end = debug_ranges_data.GetMaxU64(offset_ptr, addr_size); + + if (!begin && !end) { + // End of range list + break; + } + + if (begin == base_addr_marker) { + base_addr = end; + continue; + } + + // Filter out empty ranges + if (begin < end) + range_list.Append(DWARFRangeList::Entry(begin + base_addr, end - begin)); + } + + // Make sure we consumed at least something + return range_offset != *offset_ptr; +} + +void DWARFDebugRanges::Dump(Stream &s, + const DWARFDataExtractor &debug_ranges_data, + lldb::offset_t *offset_ptr, + dw_addr_t cu_base_addr) { + uint32_t addr_size = s.GetAddressByteSize(); + + dw_addr_t base_addr = cu_base_addr; + while ( + debug_ranges_data.ValidOffsetForDataOfSize(*offset_ptr, 2 * addr_size)) { + dw_addr_t begin = debug_ranges_data.GetMaxU64(offset_ptr, addr_size); + dw_addr_t end = debug_ranges_data.GetMaxU64(offset_ptr, addr_size); + // Extend 4 byte addresses that consists of 32 bits of 1's to be 64 bits of + // ones + if (begin == 0xFFFFFFFFull && addr_size == 4) + begin = LLDB_INVALID_ADDRESS; + + s.Indent(); + if (begin == 0 && end == 0) { + s.PutCString(" End"); + break; + } else if (begin == LLDB_INVALID_ADDRESS) { + // A base address selection entry + base_addr = end; + s.Address(base_addr, sizeof(dw_addr_t), " Base address = "); + } else { + // Convert from offset to an address + dw_addr_t begin_addr = begin + base_addr; + dw_addr_t end_addr = end + base_addr; + + s.AddressRange(begin_addr, end_addr, sizeof(dw_addr_t), nullptr); + } + } +} + +bool DWARFDebugRanges::FindRanges(const DWARFUnit *cu, + dw_offset_t debug_ranges_offset, + DWARFRangeList &range_list) const { + dw_addr_t debug_ranges_address = cu->GetRangesBase() + debug_ranges_offset; + range_map_const_iterator pos = m_range_map.find(debug_ranges_address); + if (pos != m_range_map.end()) { + range_list = pos->second; + + // All DW_AT_ranges are relative to the base address of the compile + // unit. We add the compile unit base address to make sure all the + // addresses are properly fixed up. + range_list.Slide(cu->GetBaseAddress()); + return true; + } + return false; +} + +uint64_t DWARFDebugRanges::GetOffset(size_t Index) const { + lldbassert(false && "DW_FORM_rnglistx is not present before DWARF5"); + return 0; +} + +bool DWARFDebugRngLists::ExtractRangeList( + const DWARFDataExtractor &data, uint8_t addrSize, + lldb::offset_t *offset_ptr, std::vector<RngListEntry> &rangeList) { + rangeList.clear(); + + bool error = false; + while (!error) { + switch (data.GetU8(offset_ptr)) { + case DW_RLE_end_of_list: + return true; + + case DW_RLE_start_length: { + dw_addr_t begin = data.GetMaxU64(offset_ptr, addrSize); + dw_addr_t len = data.GetULEB128(offset_ptr); + rangeList.push_back({DW_RLE_start_length, begin, len}); + break; + } + + case DW_RLE_start_end: { + dw_addr_t begin = data.GetMaxU64(offset_ptr, addrSize); + dw_addr_t end = data.GetMaxU64(offset_ptr, addrSize); + rangeList.push_back({DW_RLE_start_end, begin, end}); + break; + } + + case DW_RLE_base_address: { + dw_addr_t base = data.GetMaxU64(offset_ptr, addrSize); + rangeList.push_back({DW_RLE_base_address, base, 0}); + break; + } + + case DW_RLE_offset_pair: { + dw_addr_t begin = data.GetULEB128(offset_ptr); + dw_addr_t end = data.GetULEB128(offset_ptr); + rangeList.push_back({DW_RLE_offset_pair, begin, end}); + break; + } + + case DW_RLE_base_addressx: { + dw_addr_t base = data.GetULEB128(offset_ptr); + rangeList.push_back({DW_RLE_base_addressx, base, 0}); + break; + } + + case DW_RLE_startx_endx: { + dw_addr_t start = data.GetULEB128(offset_ptr); + dw_addr_t end = data.GetULEB128(offset_ptr); + rangeList.push_back({DW_RLE_startx_endx, start, end}); + break; + } + + case DW_RLE_startx_length: { + dw_addr_t start = data.GetULEB128(offset_ptr); + dw_addr_t length = data.GetULEB128(offset_ptr); + rangeList.push_back({DW_RLE_startx_length, start, length}); + break; + } + + default: + lldbassert(0 && "unknown range list entry encoding"); + error = true; + } + } + + return false; +} + +static uint64_t ReadAddressFromDebugAddrSection(const DWARFUnit *cu, + uint32_t index) { + uint32_t index_size = cu->GetAddressByteSize(); + dw_offset_t addr_base = cu->GetAddrBase(); + lldb::offset_t offset = addr_base + index * index_size; + return cu->GetSymbolFileDWARF() + .GetDWARFContext() + .getOrLoadAddrData() + .GetMaxU64(&offset, index_size); +} + +bool DWARFDebugRngLists::FindRanges(const DWARFUnit *cu, + dw_offset_t debug_ranges_offset, + DWARFRangeList &range_list) const { + range_list.Clear(); + dw_addr_t debug_ranges_address = cu->GetRangesBase() + debug_ranges_offset; + auto pos = m_range_map.find(debug_ranges_address); + if (pos != m_range_map.end()) { + dw_addr_t BaseAddr = cu->GetBaseAddress(); + for (const RngListEntry &E : pos->second) { + switch (E.encoding) { + case DW_RLE_start_length: + range_list.Append(DWARFRangeList::Entry(E.value0, E.value1)); + break; + case DW_RLE_base_address: + BaseAddr = E.value0; + break; + case DW_RLE_start_end: + range_list.Append(DWARFRangeList::Entry(E.value0, E.value1 - E.value0)); + break; + case DW_RLE_offset_pair: + range_list.Append( + DWARFRangeList::Entry(BaseAddr + E.value0, E.value1 - E.value0)); + break; + case DW_RLE_base_addressx: { + BaseAddr = ReadAddressFromDebugAddrSection(cu, E.value0); + break; + } + case DW_RLE_startx_endx: { + dw_addr_t start = ReadAddressFromDebugAddrSection(cu, E.value0); + dw_addr_t end = ReadAddressFromDebugAddrSection(cu, E.value1); + range_list.Append(DWARFRangeList::Entry(start, end - start)); + break; + } + case DW_RLE_startx_length: { + dw_addr_t start = ReadAddressFromDebugAddrSection(cu, E.value0); + range_list.Append(DWARFRangeList::Entry(start, E.value1)); + break; + } + default: + llvm_unreachable("unexpected encoding"); + } + } + return true; + } + return false; +} + +void DWARFDebugRngLists::Extract(DWARFContext &context) { + const DWARFDataExtractor &data = context.getOrLoadRngListsData(); + lldb::offset_t offset = 0; + + uint64_t length = data.GetU32(&offset); + // FIXME: Handle DWARF64. + lldb::offset_t end = offset + length; + + // Check version. + if (data.GetU16(&offset) < 5) + return; + + uint8_t addrSize = data.GetU8(&offset); + + // We do not support non-zero segment selector size. + if (data.GetU8(&offset) != 0) { + lldbassert(0 && "not implemented"); + return; + } + + uint32_t offsetsAmount = data.GetU32(&offset); + for (uint32_t i = 0; i < offsetsAmount; ++i) + Offsets.push_back(data.GetMaxU64(&offset, 4)); + + lldb::offset_t listOffset = offset; + std::vector<RngListEntry> rangeList; + while (offset < end && ExtractRangeList(data, addrSize, &offset, rangeList)) { + m_range_map[listOffset] = rangeList; + listOffset = offset; + } +} + +uint64_t DWARFDebugRngLists::GetOffset(size_t Index) const { + return Offsets[Index]; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugRanges.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugRanges.h new file mode 100644 index 000000000000..baf2667f0afe --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugRanges.h @@ -0,0 +1,76 @@ +//===-- DWARFDebugRanges.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 SymbolFileDWARF_DWARFDebugRanges_h_ +#define SymbolFileDWARF_DWARFDebugRanges_h_ + +#include "lldb/Core/dwarf.h" +#include <map> + +class DWARFUnit; +namespace lldb_private { +class DWARFContext; +} + +class DWARFDebugRangesBase { +public: + virtual ~DWARFDebugRangesBase(){}; + + virtual void Extract(lldb_private::DWARFContext &context) = 0; + virtual bool FindRanges(const DWARFUnit *cu, dw_offset_t debug_ranges_offset, + DWARFRangeList &range_list) const = 0; + virtual uint64_t GetOffset(size_t Index) const = 0; +}; + +class DWARFDebugRanges final : public DWARFDebugRangesBase { +public: + DWARFDebugRanges(); + + void Extract(lldb_private::DWARFContext &context) override; + bool FindRanges(const DWARFUnit *cu, dw_offset_t debug_ranges_offset, + DWARFRangeList &range_list) const override; + uint64_t GetOffset(size_t Index) const override; + + static void Dump(lldb_private::Stream &s, + const lldb_private::DWARFDataExtractor &debug_ranges_data, + lldb::offset_t *offset_ptr, dw_addr_t cu_base_addr); + +protected: + bool Extract(lldb_private::DWARFContext &context, lldb::offset_t *offset_ptr, + DWARFRangeList &range_list); + + typedef std::map<dw_offset_t, DWARFRangeList> range_map; + typedef range_map::iterator range_map_iterator; + typedef range_map::const_iterator range_map_const_iterator; + range_map m_range_map; +}; + +// DWARF v5 .debug_rnglists section. +class DWARFDebugRngLists final : public DWARFDebugRangesBase { + struct RngListEntry { + uint8_t encoding; + uint64_t value0; + uint64_t value1; + }; + +public: + void Extract(lldb_private::DWARFContext &context) override; + bool FindRanges(const DWARFUnit *cu, dw_offset_t debug_ranges_offset, + DWARFRangeList &range_list) const override; + uint64_t GetOffset(size_t Index) const override; + +protected: + bool ExtractRangeList(const lldb_private::DWARFDataExtractor &data, + uint8_t addrSize, lldb::offset_t *offset_ptr, + std::vector<RngListEntry> &list); + + std::vector<uint64_t> Offsets; + std::map<dw_offset_t, std::vector<RngListEntry>> m_range_map; +}; + +#endif // SymbolFileDWARF_DWARFDebugRanges_h_ diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDeclContext.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDeclContext.cpp new file mode 100644 index 000000000000..a664314035e4 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDeclContext.cpp @@ -0,0 +1,87 @@ +//===-- DWARFDeclContext.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 "DWARFDeclContext.h" + +const char *DWARFDeclContext::GetQualifiedName() const { + if (m_qualified_name.empty()) { + // The declaration context array for a class named "foo" in namespace + // "a::b::c" will be something like: + // [0] DW_TAG_class_type "foo" + // [1] DW_TAG_namespace "c" + // [2] DW_TAG_namespace "b" + // [3] DW_TAG_namespace "a" + if (!m_entries.empty()) { + if (m_entries.size() == 1) { + if (m_entries[0].name) { + m_qualified_name.append("::"); + m_qualified_name.append(m_entries[0].name); + } + } else { + collection::const_reverse_iterator pos; + collection::const_reverse_iterator begin = m_entries.rbegin(); + collection::const_reverse_iterator end = m_entries.rend(); + for (pos = begin; pos != end; ++pos) { + if (pos != begin) + m_qualified_name.append("::"); + if (pos->name == nullptr) { + if (pos->tag == DW_TAG_namespace) + m_qualified_name.append("(anonymous namespace)"); + else if (pos->tag == DW_TAG_class_type) + m_qualified_name.append("(anonymous class)"); + else if (pos->tag == DW_TAG_structure_type) + m_qualified_name.append("(anonymous struct)"); + else if (pos->tag == DW_TAG_union_type) + m_qualified_name.append("(anonymous union)"); + else + m_qualified_name.append("(anonymous)"); + } else + m_qualified_name.append(pos->name); + } + } + } + } + if (m_qualified_name.empty()) + return nullptr; + return m_qualified_name.c_str(); +} + +bool DWARFDeclContext::operator==(const DWARFDeclContext &rhs) const { + if (m_entries.size() != rhs.m_entries.size()) + return false; + + collection::const_iterator pos; + collection::const_iterator begin = m_entries.begin(); + collection::const_iterator end = m_entries.end(); + + collection::const_iterator rhs_pos; + collection::const_iterator rhs_begin = rhs.m_entries.begin(); + // The two entry arrays have the same size + + // First compare the tags before we do expensive name compares + for (pos = begin, rhs_pos = rhs_begin; pos != end; ++pos, ++rhs_pos) { + if (pos->tag != rhs_pos->tag) { + // Check for DW_TAG_structure_type and DW_TAG_class_type as they are + // often used interchangeably in GCC + if (pos->tag == DW_TAG_structure_type && + rhs_pos->tag == DW_TAG_class_type) + continue; + if (pos->tag == DW_TAG_class_type && + rhs_pos->tag == DW_TAG_structure_type) + continue; + return false; + } + } + // The tags all match, now compare the names + for (pos = begin, rhs_pos = rhs_begin; pos != end; ++pos, ++rhs_pos) { + if (!pos->NameMatches(*rhs_pos)) + return false; + } + // All tags and names match + return true; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDeclContext.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDeclContext.h new file mode 100644 index 000000000000..d0d70dd5123e --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDeclContext.h @@ -0,0 +1,88 @@ +//===-- DWARFDeclContext.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 SymbolFileDWARF_DWARFDeclContext_h_ +#define SymbolFileDWARF_DWARFDeclContext_h_ + +#include <string> +#include <vector> +#include "lldb/Utility/ConstString.h" +#include "DWARFDefines.h" + +// DWARFDeclContext +// +// A class that represents a declaration context all the way down to a +// DIE. This is useful when trying to find a DIE in one DWARF to a DIE +// in another DWARF file. + +class DWARFDeclContext { +public: + struct Entry { + Entry() : tag(0), name(nullptr) {} + Entry(dw_tag_t t, const char *n) : tag(t), name(n) {} + + bool NameMatches(const Entry &rhs) const { + if (name == rhs.name) + return true; + else if (name && rhs.name) + return strcmp(name, rhs.name) == 0; + return false; + } + + // Test operator + explicit operator bool() const { return tag != 0; } + + dw_tag_t tag; + const char *name; + }; + + DWARFDeclContext() : m_entries(), m_language(lldb::eLanguageTypeUnknown) {} + + void AppendDeclContext(dw_tag_t tag, const char *name) { + m_entries.push_back(Entry(tag, name)); + } + + bool operator==(const DWARFDeclContext &rhs) const; + + uint32_t GetSize() const { return m_entries.size(); } + + Entry &operator[](uint32_t idx) { + // "idx" must be valid + return m_entries[idx]; + } + + const Entry &operator[](uint32_t idx) const { + // "idx" must be valid + return m_entries[idx]; + } + + const char *GetQualifiedName() const; + + // Same as GetQaulifiedName, but the life time of the returned string will + // be that of the LLDB session. + lldb_private::ConstString GetQualifiedNameAsConstString() const { + return lldb_private::ConstString(GetQualifiedName()); + } + + void Clear() { + m_entries.clear(); + m_qualified_name.clear(); + } + + lldb::LanguageType GetLanguage() const { return m_language; } + + void SetLanguage(lldb::LanguageType language) { m_language = language; } + +protected: + typedef std::vector<Entry> collection; + collection m_entries; + mutable std::string m_qualified_name; + lldb::LanguageType m_language; +}; + +#endif // SymbolFileDWARF_DWARFDeclContext_h_ diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDefines.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDefines.cpp new file mode 100644 index 000000000000..3bf0bb088227 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDefines.cpp @@ -0,0 +1,402 @@ +//===-- DWARFDefines.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 "DWARFDefines.h" +#include "lldb/Utility/ConstString.h" +#include <cstdio> +#include <cstring> +#include <string> + +namespace lldb_private { + +const char *DW_TAG_value_to_name(uint32_t val) { + static char invalid[100]; + + if (val == 0) + return "NULL"; + + llvm::StringRef llvmstr = llvm::dwarf::TagString(val); + if (llvmstr.empty()) { + snprintf(invalid, sizeof(invalid), "Unknown DW_TAG constant: 0x%x", val); + return invalid; + } + return llvmstr.data(); +} + +const char *DW_AT_value_to_name(uint32_t val) { + static char invalid[100]; + llvm::StringRef llvmstr = llvm::dwarf::AttributeString(val); + if (llvmstr.empty()) { + snprintf(invalid, sizeof(invalid), "Unknown DW_AT constant: 0x%x", val); + return invalid; + } + return llvmstr.data(); +} + +const char *DW_FORM_value_to_name(uint32_t val) { + static char invalid[100]; + llvm::StringRef llvmstr = llvm::dwarf::FormEncodingString(val); + if (llvmstr.empty()) { + snprintf(invalid, sizeof(invalid), "Unknown DW_FORM constant: 0x%x", val); + return invalid; + } + return llvmstr.data(); +} + +const char *DW_OP_value_to_name(uint32_t val) { + static char invalid[100]; + llvm::StringRef llvmstr = llvm::dwarf::OperationEncodingString(val); + if (llvmstr.empty()) { + snprintf(invalid, sizeof(invalid), "Unknown DW_OP constant: 0x%x", val); + return invalid; + } + return llvmstr.data(); +} + +DRC_class DW_OP_value_to_class(uint32_t val) { + switch (val) { + case 0x03: + return DRC_ONEOPERAND; + case 0x06: + return DRC_ZEROOPERANDS; + case 0x08: + return DRC_ONEOPERAND; + case 0x09: + return DRC_ONEOPERAND; + case 0x0a: + return DRC_ONEOPERAND; + case 0x0b: + return DRC_ONEOPERAND; + case 0x0c: + return DRC_ONEOPERAND; + case 0x0d: + return DRC_ONEOPERAND; + case 0x0e: + return DRC_ONEOPERAND; + case 0x0f: + return DRC_ONEOPERAND; + case 0x10: + return DRC_ONEOPERAND; + case 0x11: + return DRC_ONEOPERAND; + case 0x12: + return DRC_ZEROOPERANDS; + case 0x13: + return DRC_ZEROOPERANDS; + case 0x14: + return DRC_ZEROOPERANDS; + case 0x15: + return DRC_ONEOPERAND; + case 0x16: + return DRC_ZEROOPERANDS; + case 0x17: + return DRC_ZEROOPERANDS; + case 0x18: + return DRC_ZEROOPERANDS; + case 0x19: + return DRC_ZEROOPERANDS; + case 0x1a: + return DRC_ZEROOPERANDS; + case 0x1b: + return DRC_ZEROOPERANDS; + case 0x1c: + return DRC_ZEROOPERANDS; + case 0x1d: + return DRC_ZEROOPERANDS; + case 0x1e: + return DRC_ZEROOPERANDS; + case 0x1f: + return DRC_ZEROOPERANDS; + case 0x20: + return DRC_ZEROOPERANDS; + case 0x21: + return DRC_ZEROOPERANDS; + case 0x22: + return DRC_ZEROOPERANDS; + case 0x23: + return DRC_ONEOPERAND; + case 0x24: + return DRC_ZEROOPERANDS; + case 0x25: + return DRC_ZEROOPERANDS; + case 0x26: + return DRC_ZEROOPERANDS; + case 0x27: + return DRC_ZEROOPERANDS; + case 0x2f: + return DRC_ONEOPERAND; + case 0x28: + return DRC_ONEOPERAND; + case 0x29: + return DRC_ZEROOPERANDS; + case 0x2a: + return DRC_ZEROOPERANDS; + case 0x2b: + return DRC_ZEROOPERANDS; + case 0x2c: + return DRC_ZEROOPERANDS; + case 0x2d: + return DRC_ZEROOPERANDS; + case 0x2e: + return DRC_ZEROOPERANDS; + case 0x30: + return DRC_ZEROOPERANDS; + case 0x31: + return DRC_ZEROOPERANDS; + case 0x32: + return DRC_ZEROOPERANDS; + case 0x33: + return DRC_ZEROOPERANDS; + case 0x34: + return DRC_ZEROOPERANDS; + case 0x35: + return DRC_ZEROOPERANDS; + case 0x36: + return DRC_ZEROOPERANDS; + case 0x37: + return DRC_ZEROOPERANDS; + case 0x38: + return DRC_ZEROOPERANDS; + case 0x39: + return DRC_ZEROOPERANDS; + case 0x3a: + return DRC_ZEROOPERANDS; + case 0x3b: + return DRC_ZEROOPERANDS; + case 0x3c: + return DRC_ZEROOPERANDS; + case 0x3d: + return DRC_ZEROOPERANDS; + case 0x3e: + return DRC_ZEROOPERANDS; + case 0x3f: + return DRC_ZEROOPERANDS; + case 0x40: + return DRC_ZEROOPERANDS; + case 0x41: + return DRC_ZEROOPERANDS; + case 0x42: + return DRC_ZEROOPERANDS; + case 0x43: + return DRC_ZEROOPERANDS; + case 0x44: + return DRC_ZEROOPERANDS; + case 0x45: + return DRC_ZEROOPERANDS; + case 0x46: + return DRC_ZEROOPERANDS; + case 0x47: + return DRC_ZEROOPERANDS; + case 0x48: + return DRC_ZEROOPERANDS; + case 0x49: + return DRC_ZEROOPERANDS; + case 0x4a: + return DRC_ZEROOPERANDS; + case 0x4b: + return DRC_ZEROOPERANDS; + case 0x4c: + return DRC_ZEROOPERANDS; + case 0x4d: + return DRC_ZEROOPERANDS; + case 0x4e: + return DRC_ZEROOPERANDS; + case 0x4f: + return DRC_ZEROOPERANDS; + case 0x50: + return DRC_ZEROOPERANDS; + case 0x51: + return DRC_ZEROOPERANDS; + case 0x52: + return DRC_ZEROOPERANDS; + case 0x53: + return DRC_ZEROOPERANDS; + case 0x54: + return DRC_ZEROOPERANDS; + case 0x55: + return DRC_ZEROOPERANDS; + case 0x56: + return DRC_ZEROOPERANDS; + case 0x57: + return DRC_ZEROOPERANDS; + case 0x58: + return DRC_ZEROOPERANDS; + case 0x59: + return DRC_ZEROOPERANDS; + case 0x5a: + return DRC_ZEROOPERANDS; + case 0x5b: + return DRC_ZEROOPERANDS; + case 0x5c: + return DRC_ZEROOPERANDS; + case 0x5d: + return DRC_ZEROOPERANDS; + case 0x5e: + return DRC_ZEROOPERANDS; + case 0x5f: + return DRC_ZEROOPERANDS; + case 0x60: + return DRC_ZEROOPERANDS; + case 0x61: + return DRC_ZEROOPERANDS; + case 0x62: + return DRC_ZEROOPERANDS; + case 0x63: + return DRC_ZEROOPERANDS; + case 0x64: + return DRC_ZEROOPERANDS; + case 0x65: + return DRC_ZEROOPERANDS; + case 0x66: + return DRC_ZEROOPERANDS; + case 0x67: + return DRC_ZEROOPERANDS; + case 0x68: + return DRC_ZEROOPERANDS; + case 0x69: + return DRC_ZEROOPERANDS; + case 0x6a: + return DRC_ZEROOPERANDS; + case 0x6b: + return DRC_ZEROOPERANDS; + case 0x6c: + return DRC_ZEROOPERANDS; + case 0x6d: + return DRC_ZEROOPERANDS; + case 0x6e: + return DRC_ZEROOPERANDS; + case 0x6f: + return DRC_ZEROOPERANDS; + case 0x70: + return DRC_ONEOPERAND; + case 0x71: + return DRC_ONEOPERAND; + case 0x72: + return DRC_ONEOPERAND; + case 0x73: + return DRC_ONEOPERAND; + case 0x74: + return DRC_ONEOPERAND; + case 0x75: + return DRC_ONEOPERAND; + case 0x76: + return DRC_ONEOPERAND; + case 0x77: + return DRC_ONEOPERAND; + case 0x78: + return DRC_ONEOPERAND; + case 0x79: + return DRC_ONEOPERAND; + case 0x7a: + return DRC_ONEOPERAND; + case 0x7b: + return DRC_ONEOPERAND; + case 0x7c: + return DRC_ONEOPERAND; + case 0x7d: + return DRC_ONEOPERAND; + case 0x7e: + return DRC_ONEOPERAND; + case 0x7f: + return DRC_ONEOPERAND; + case 0x80: + return DRC_ONEOPERAND; + case 0x81: + return DRC_ONEOPERAND; + case 0x82: + return DRC_ONEOPERAND; + case 0x83: + return DRC_ONEOPERAND; + case 0x84: + return DRC_ONEOPERAND; + case 0x85: + return DRC_ONEOPERAND; + case 0x86: + return DRC_ONEOPERAND; + case 0x87: + return DRC_ONEOPERAND; + case 0x88: + return DRC_ONEOPERAND; + case 0x89: + return DRC_ONEOPERAND; + case 0x8a: + return DRC_ONEOPERAND; + case 0x8b: + return DRC_ONEOPERAND; + case 0x8c: + return DRC_ONEOPERAND; + case 0x8d: + return DRC_ONEOPERAND; + case 0x8e: + return DRC_ONEOPERAND; + case 0x8f: + return DRC_ONEOPERAND; + case 0x90: + return DRC_ONEOPERAND; + case 0x91: + return DRC_ONEOPERAND; + case 0x92: + return DRC_TWOOPERANDS; + case 0x93: + return DRC_ONEOPERAND; + case 0x94: + return DRC_ONEOPERAND; + case 0x95: + return DRC_ONEOPERAND; + case 0x96: + return DRC_ZEROOPERANDS; + case 0x97: + return DRC_DWARFv3 | DRC_ZEROOPERANDS; + case 0x98: + return DRC_DWARFv3 | DRC_ONEOPERAND; + case 0x99: + return DRC_DWARFv3 | DRC_ONEOPERAND; + case 0x9a: + return DRC_DWARFv3 | DRC_ONEOPERAND; + case 0xf0: + return DRC_ZEROOPERANDS; /* DW_OP_APPLE_uninit */ + case 0xe0: + return 0; + case 0xff: + return 0; + default: + return 0; + } +} + +const char *DW_ATE_value_to_name(uint32_t val) { + static char invalid[100]; + llvm::StringRef llvmstr = llvm::dwarf::AttributeEncodingString(val); + if (llvmstr.empty()) { + snprintf(invalid, sizeof(invalid), "Unknown DW_ATE constant: 0x%x", val); + return invalid; + } + return llvmstr.data(); +} + +const char *DW_LANG_value_to_name(uint32_t val) { + static char invalid[100]; + llvm::StringRef llvmstr = llvm::dwarf::LanguageString(val); + if (llvmstr.empty()) { + snprintf(invalid, sizeof(invalid), "Unknown DW_LANG constant: 0x%x", val); + return invalid; + } + return llvmstr.data(); +} + +const char *DW_LNS_value_to_name(uint32_t val) { + static char invalid[100]; + llvm::StringRef llvmstr = llvm::dwarf::LNStandardString(val); + if (llvmstr.empty()) { + snprintf(invalid, sizeof(invalid), "Unknown DW_LNS constant: 0x%x", val); + return invalid; + } + return llvmstr.data(); +} + +} // namespace lldb_private diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDefines.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDefines.h new file mode 100644 index 000000000000..d16cb07baf88 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDefines.h @@ -0,0 +1,77 @@ +//===-- DWARFDefines.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 SymbolFileDWARF_DWARFDefines_h_ +#define SymbolFileDWARF_DWARFDefines_h_ + +#include "lldb/Core/dwarf.h" +#include <stdint.h> + +namespace lldb_private { + +enum class DWARFEnumState { MoreItems, Complete }; + +typedef uint32_t DRC_class; // Holds DRC_* class bitfields + +const char *DW_TAG_value_to_name(uint32_t val); + +const char *DW_AT_value_to_name(uint32_t val); + +const char *DW_FORM_value_to_name(uint32_t val); + +const char *DW_OP_value_to_name(uint32_t val); + +DRC_class DW_OP_value_to_class(uint32_t val); + +const char *DW_ATE_value_to_name(uint32_t val); + +const char *DW_LANG_value_to_name(uint32_t val); + +const char *DW_LNS_value_to_name(uint32_t val); + +/* These DRC are entirely our own construction, + although they are derived from various comments in the DWARF standard. + Most of these are not useful to the parser, but the DW_AT and DW_FORM + classes should prove to be usable in some fashion. */ + +#define DRC_0x65 0x1 +#define DRC_ADDRESS 0x2 +#define DRC_BLOCK 0x4 +#define DRC_CONSTANT 0x8 +#define DRC_DWARFv3 0x10 +#define DRC_FLAG 0x20 +#define DRC_INDIRECT_SPECIAL 0x40 +#define DRC_LINEPTR 0x80 +#define DRC_LOCEXPR 0x100 +#define DRC_LOCLISTPTR 0x200 +#define DRC_MACPTR 0x400 +#define DRC_ONEOPERAND 0x800 +#define DRC_OPERANDONE_1BYTE_DELTA 0x1000 +#define DRC_OPERANDONE_2BYTE_DELTA 0x2000 +#define DRC_OPERANDONE_4BYTE_DELTA 0x4000 +#define DRC_OPERANDONE_ADDRESS 0x8000 +#define DRC_OPERANDONE_BLOCK 0x10000 +#define DRC_OPERANDONE_SLEB128_OFFSET 0x20000 +#define DRC_OPERANDONE_ULEB128_OFFSET 0x40000 +#define DRC_OPERANDONE_ULEB128_REGISTER 0x80000 +#define DRC_OPERANDTWO_BLOCK 0x100000 +#define DRC_OPERANDTWO_SLEB128_OFFSET 0x200000 +#define DRC_OPERANDTWO_ULEB128_OFFSET 0x400000 +#define DRC_OPERANDTWO_ULEB128_REGISTER 0x800000 +#define DRC_OPERNADONE_ULEB128_REGISTER 0x1000000 +#define DRC_RANGELISTPTR 0x2000000 +#define DRC_REFERENCE 0x4000000 +#define DRC_STRING 0x8000000 +#define DRC_TWOOPERANDS 0x10000000 +#define DRC_VENDOR_GNU 0x20000000 +#define DRC_VENDOR_MIPS 0x40000000 +#define DRC_ZEROOPERANDS 0x80000000 + +} // namespace lldb_private + +#endif // SymbolFileDWARF_DWARFDefines_h_ diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp new file mode 100644 index 000000000000..046ae67446af --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp @@ -0,0 +1,739 @@ +//===-- DWARFFormValue.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 <assert.h> + +#include "lldb/Core/Module.h" +#include "lldb/Core/dwarf.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Utility/Stream.h" + +#include "DWARFDebugInfo.h" +#include "DWARFFormValue.h" +#include "DWARFUnit.h" + +class DWARFUnit; + +using namespace lldb_private; + +void DWARFFormValue::Clear() { + m_unit = nullptr; + m_form = 0; + m_value = ValueTypeTag(); +} + +bool DWARFFormValue::ExtractValue(const DWARFDataExtractor &data, + lldb::offset_t *offset_ptr) { + if (m_form == DW_FORM_implicit_const) + return true; + + bool indirect = false; + bool is_block = false; + m_value.data = nullptr; + uint8_t ref_addr_size; + // Read the value for the form into value and follow and DW_FORM_indirect + // instances we run into + do { + indirect = false; + switch (m_form) { + case DW_FORM_addr: + assert(m_unit); + m_value.value.uval = + data.GetMaxU64(offset_ptr, DWARFUnit::GetAddressByteSize(m_unit)); + break; + case DW_FORM_block1: + m_value.value.uval = data.GetU8(offset_ptr); + is_block = true; + break; + case DW_FORM_block2: + m_value.value.uval = data.GetU16(offset_ptr); + is_block = true; + break; + case DW_FORM_block4: + m_value.value.uval = data.GetU32(offset_ptr); + is_block = true; + break; + case DW_FORM_data16: + m_value.value.uval = 16; + is_block = true; + break; + case DW_FORM_exprloc: + case DW_FORM_block: + m_value.value.uval = data.GetULEB128(offset_ptr); + is_block = true; + break; + case DW_FORM_string: + m_value.value.cstr = data.GetCStr(offset_ptr); + break; + case DW_FORM_sdata: + m_value.value.sval = data.GetSLEB128(offset_ptr); + break; + case DW_FORM_strp: + case DW_FORM_line_strp: + case DW_FORM_sec_offset: + m_value.value.uval = data.GetMaxU64(offset_ptr, 4); + break; + case DW_FORM_addrx1: + case DW_FORM_strx1: + case DW_FORM_ref1: + case DW_FORM_data1: + case DW_FORM_flag: + m_value.value.uval = data.GetU8(offset_ptr); + break; + case DW_FORM_addrx2: + case DW_FORM_strx2: + case DW_FORM_ref2: + case DW_FORM_data2: + m_value.value.uval = data.GetU16(offset_ptr); + break; + case DW_FORM_addrx3: + case DW_FORM_strx3: + m_value.value.uval = data.GetMaxU64(offset_ptr, 3); + break; + case DW_FORM_addrx4: + case DW_FORM_strx4: + case DW_FORM_ref4: + case DW_FORM_data4: + m_value.value.uval = data.GetU32(offset_ptr); + break; + case DW_FORM_data8: + case DW_FORM_ref8: + case DW_FORM_ref_sig8: + m_value.value.uval = data.GetU64(offset_ptr); + break; + case DW_FORM_addrx: + case DW_FORM_rnglistx: + case DW_FORM_strx: + case DW_FORM_udata: + case DW_FORM_ref_udata: + case DW_FORM_GNU_str_index: + case DW_FORM_GNU_addr_index: + m_value.value.uval = data.GetULEB128(offset_ptr); + break; + case DW_FORM_ref_addr: + assert(m_unit); + if (m_unit->GetVersion() <= 2) + ref_addr_size = m_unit->GetAddressByteSize(); + else + 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; + break; + case DW_FORM_flag_present: + m_value.value.uval = 1; + break; + default: + return false; + } + } while (indirect); + + if (is_block) { + m_value.data = data.PeekData(*offset_ptr, m_value.value.uval); + if (m_value.data != nullptr) { + *offset_ptr += m_value.value.uval; + } + } + + return true; +} + +struct FormSize { + uint8_t valid:1, size:7; +}; +static FormSize g_form_sizes[] = { + {0,0}, // 0x00 unused + {0,0}, // 0x01 DW_FORM_addr + {0,0}, // 0x02 unused + {0,0}, // 0x03 DW_FORM_block2 + {0,0}, // 0x04 DW_FORM_block4 + {1,2}, // 0x05 DW_FORM_data2 + {1,4}, // 0x06 DW_FORM_data4 + {1,8}, // 0x07 DW_FORM_data8 + {0,0}, // 0x08 DW_FORM_string + {0,0}, // 0x09 DW_FORM_block + {0,0}, // 0x0a DW_FORM_block1 + {1,1}, // 0x0b DW_FORM_data1 + {1,1}, // 0x0c DW_FORM_flag + {0,0}, // 0x0d DW_FORM_sdata + {1,4}, // 0x0e DW_FORM_strp + {0,0}, // 0x0f DW_FORM_udata + {0,0}, // 0x10 DW_FORM_ref_addr (addr size for DWARF2 and earlier, 4 bytes for + // DWARF32, 8 bytes for DWARF32 in DWARF 3 and later + {1,1}, // 0x11 DW_FORM_ref1 + {1,2}, // 0x12 DW_FORM_ref2 + {1,4}, // 0x13 DW_FORM_ref4 + {1,8}, // 0x14 DW_FORM_ref8 + {0,0}, // 0x15 DW_FORM_ref_udata + {0,0}, // 0x16 DW_FORM_indirect + {1,4}, // 0x17 DW_FORM_sec_offset + {0,0}, // 0x18 DW_FORM_exprloc + {1,0}, // 0x19 DW_FORM_flag_present + {0,0}, // 0x1a + {0,0}, // 0x1b + {0,0}, // 0x1c + {0,0}, // 0x1d + {0,0}, // 0x1e + {0,0}, // 0x1f + {1,8}, // 0x20 DW_FORM_ref_sig8 +}; + +llvm::Optional<uint8_t> +DWARFFormValue::GetFixedSize(dw_form_t form, const DWARFUnit *u) { + if (form <= DW_FORM_ref_sig8 && g_form_sizes[form].valid) + return g_form_sizes[form].size; + if (form == DW_FORM_addr && u) + return u->GetAddressByteSize(); + return llvm::None; +} + +llvm::Optional<uint8_t> DWARFFormValue::GetFixedSize() const { + return GetFixedSize(m_form, m_unit); +} + +bool DWARFFormValue::SkipValue(const DWARFDataExtractor &debug_info_data, + lldb::offset_t *offset_ptr) const { + return DWARFFormValue::SkipValue(m_form, debug_info_data, offset_ptr, m_unit); +} + +bool DWARFFormValue::SkipValue(dw_form_t form, + const DWARFDataExtractor &debug_info_data, + lldb::offset_t *offset_ptr, + const DWARFUnit *unit) { + uint8_t ref_addr_size; + switch (form) { + // Blocks if inlined data that have a length field and the data bytes inlined + // in the .debug_info + case DW_FORM_exprloc: + case DW_FORM_block: { + dw_uleb128_t size = debug_info_data.GetULEB128(offset_ptr); + *offset_ptr += size; + } + return true; + case DW_FORM_block1: { + dw_uleb128_t size = debug_info_data.GetU8(offset_ptr); + *offset_ptr += size; + } + return true; + case DW_FORM_block2: { + dw_uleb128_t size = debug_info_data.GetU16(offset_ptr); + *offset_ptr += size; + } + return true; + case DW_FORM_block4: { + dw_uleb128_t size = debug_info_data.GetU32(offset_ptr); + *offset_ptr += size; + } + return true; + + // Inlined NULL terminated C-strings + case DW_FORM_string: + debug_info_data.GetCStr(offset_ptr); + return true; + + // Compile unit address sized values + case DW_FORM_addr: + *offset_ptr += DWARFUnit::GetAddressByteSize(unit); + return true; + + case DW_FORM_ref_addr: + ref_addr_size = 4; + assert(unit); // Unit must be valid for DW_FORM_ref_addr objects or we will + // get this wrong + if (unit->GetVersion() <= 2) + ref_addr_size = unit->GetAddressByteSize(); + else + 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: + return true; + + // 1 byte values + case DW_FORM_addrx1: + case DW_FORM_data1: + case DW_FORM_flag: + case DW_FORM_ref1: + case DW_FORM_strx1: + *offset_ptr += 1; + return true; + + // 2 byte values + case DW_FORM_addrx2: + case DW_FORM_data2: + case DW_FORM_ref2: + case DW_FORM_strx2: + *offset_ptr += 2; + return true; + + // 3 byte values + case DW_FORM_addrx3: + case DW_FORM_strx3: + *offset_ptr += 3; + return true; + + // 32 bit for DWARF 32, 64 for DWARF 64 + case DW_FORM_sec_offset: + case DW_FORM_strp: + *offset_ptr += 4; + return true; + + // 4 byte values + case DW_FORM_addrx4: + case DW_FORM_data4: + case DW_FORM_ref4: + case DW_FORM_strx4: + *offset_ptr += 4; + return true; + + // 8 byte values + case DW_FORM_data8: + case DW_FORM_ref8: + case DW_FORM_ref_sig8: + *offset_ptr += 8; + return true; + + // signed or unsigned LEB 128 values + case DW_FORM_addrx: + case DW_FORM_rnglistx: + case DW_FORM_sdata: + case DW_FORM_udata: + case DW_FORM_ref_udata: + case DW_FORM_GNU_addr_index: + case DW_FORM_GNU_str_index: + case DW_FORM_strx: + debug_info_data.Skip_LEB128(offset_ptr); + return true; + + case DW_FORM_indirect: { + dw_form_t indirect_form = debug_info_data.GetULEB128(offset_ptr); + return DWARFFormValue::SkipValue(indirect_form, debug_info_data, offset_ptr, + unit); + } + + default: + break; + } + return false; +} + +void DWARFFormValue::Dump(Stream &s) const { + uint64_t uvalue = Unsigned(); + bool unit_relative_offset = false; + + switch (m_form) { + case DW_FORM_addr: + s.Address(uvalue, sizeof(uint64_t)); + break; + case DW_FORM_flag: + case DW_FORM_data1: + s.PutHex8(uvalue); + break; + case DW_FORM_data2: + s.PutHex16(uvalue); + break; + case DW_FORM_sec_offset: + case DW_FORM_data4: + s.PutHex32(uvalue); + break; + case DW_FORM_ref_sig8: + case DW_FORM_data8: + s.PutHex64(uvalue); + break; + case DW_FORM_string: + s.QuotedCString(AsCString()); + break; + case DW_FORM_exprloc: + case DW_FORM_block: + case DW_FORM_block1: + case DW_FORM_block2: + case DW_FORM_block4: + if (uvalue > 0) { + switch (m_form) { + case DW_FORM_exprloc: + case DW_FORM_block: + s.Printf("<0x%" PRIx64 "> ", uvalue); + break; + case DW_FORM_block1: + s.Printf("<0x%2.2x> ", (uint8_t)uvalue); + break; + case DW_FORM_block2: + s.Printf("<0x%4.4x> ", (uint16_t)uvalue); + break; + case DW_FORM_block4: + s.Printf("<0x%8.8x> ", (uint32_t)uvalue); + break; + default: + break; + } + + const uint8_t *data_ptr = m_value.data; + if (data_ptr) { + const uint8_t *end_data_ptr = + data_ptr + uvalue; // uvalue contains size of block + while (data_ptr < end_data_ptr) { + s.Printf("%2.2x ", *data_ptr); + ++data_ptr; + } + } else + s.PutCString("NULL"); + } + break; + + case DW_FORM_sdata: + s.PutSLEB128(uvalue); + break; + case DW_FORM_udata: + s.PutULEB128(uvalue); + break; + case DW_FORM_strp: { + const char *dbg_str = AsCString(); + if (dbg_str) { + s.QuotedCString(dbg_str); + } else { + s.PutHex32(uvalue); + } + } break; + + case DW_FORM_ref_addr: { + assert(m_unit); // Unit must be valid for DW_FORM_ref_addr objects or we + // will get this wrong + if (m_unit->GetVersion() <= 2) + s.Address(uvalue, sizeof(uint64_t) * 2); + else + s.Address(uvalue, 4 * 2); // 4 for DWARF32, 8 for DWARF64, but we don't + // support DWARF64 yet + break; + } + case DW_FORM_ref1: + unit_relative_offset = true; + break; + case DW_FORM_ref2: + unit_relative_offset = true; + break; + case DW_FORM_ref4: + unit_relative_offset = true; + break; + case DW_FORM_ref8: + unit_relative_offset = true; + break; + case DW_FORM_ref_udata: + unit_relative_offset = true; + break; + + // All DW_FORM_indirect attributes should be resolved prior to calling this + // function + case DW_FORM_indirect: + s.PutCString("DW_FORM_indirect"); + break; + case DW_FORM_flag_present: + break; + default: + s.Printf("DW_FORM(0x%4.4x)", m_form); + break; + } + + if (unit_relative_offset) { + assert(m_unit); // Unit must be valid for DW_FORM_ref forms that are compile + // unit relative or we will get this wrong + s.Printf("{0x%8.8" PRIx64 "}", uvalue + m_unit->GetOffset()); + } +} + +const char *DWARFFormValue::AsCString() const { + SymbolFileDWARF &symbol_file = m_unit->GetSymbolFileDWARF(); + + if (m_form == DW_FORM_string) { + return m_value.value.cstr; + } else if (m_form == DW_FORM_strp) { + return symbol_file.GetDWARFContext().getOrLoadStrData().PeekCStr( + m_value.value.uval); + } else if (m_form == DW_FORM_GNU_str_index) { + uint32_t index_size = 4; + lldb::offset_t offset = m_value.value.uval * index_size; + dw_offset_t str_offset = + symbol_file.GetDWARFContext().getOrLoadStrOffsetsData().GetMaxU64( + &offset, index_size); + return symbol_file.GetDWARFContext().getOrLoadStrData().PeekCStr( + str_offset); + } + + if (m_form == DW_FORM_strx || m_form == DW_FORM_strx1 || + m_form == DW_FORM_strx2 || m_form == DW_FORM_strx3 || + m_form == DW_FORM_strx4) { + + // The same code as above. + uint32_t indexSize = 4; + lldb::offset_t offset = + m_unit->GetStrOffsetsBase() + m_value.value.uval * indexSize; + dw_offset_t strOffset = + symbol_file.GetDWARFContext().getOrLoadStrOffsetsData().GetMaxU64( + &offset, indexSize); + return symbol_file.GetDWARFContext().getOrLoadStrData().PeekCStr(strOffset); + } + + if (m_form == DW_FORM_line_strp) + return symbol_file.GetDWARFContext().getOrLoadLineStrData().PeekCStr( + m_value.value.uval); + + return nullptr; +} + +dw_addr_t DWARFFormValue::Address() const { + SymbolFileDWARF &symbol_file = m_unit->GetSymbolFileDWARF(); + + if (m_form == DW_FORM_addr) + return Unsigned(); + + assert(m_unit); + assert(m_form == DW_FORM_GNU_addr_index || m_form == DW_FORM_addrx || + m_form == DW_FORM_addrx1 || m_form == DW_FORM_addrx2 || + m_form == DW_FORM_addrx3 || m_form == DW_FORM_addrx4); + + uint32_t index_size = m_unit->GetAddressByteSize(); + dw_offset_t addr_base = m_unit->GetAddrBase(); + lldb::offset_t offset = addr_base + m_value.value.uval * index_size; + return symbol_file.GetDWARFContext().getOrLoadAddrData().GetMaxU64( + &offset, index_size); +} + +DWARFDIE DWARFFormValue::Reference() const { + uint64_t value = m_value.value.uval; + 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_unit); // Unit must be valid for DW_FORM_ref forms that are compile + // unit relative or we will get this wrong + value += m_unit->GetOffset(); + if (!m_unit->ContainsDIEOffset(value)) { + m_unit->GetSymbolFileDWARF().GetObjectFile()->GetModule()->ReportError( + "DW_FORM_ref* DIE reference 0x%" PRIx64 " is outside of its CU", + value); + return {}; + } + return const_cast<DWARFUnit *>(m_unit)->GetDIE(value); + + case DW_FORM_ref_addr: { + DWARFUnit *ref_cu = + m_unit->GetSymbolFileDWARF().DebugInfo()->GetUnitContainingDIEOffset( + DIERef::Section::DebugInfo, value); + if (!ref_cu) { + m_unit->GetSymbolFileDWARF().GetObjectFile()->GetModule()->ReportError( + "DW_FORM_ref_addr DIE reference 0x%" PRIx64 " has no matching CU", + value); + return {}; + } + return ref_cu->GetDIE(value); + } + + case DW_FORM_ref_sig8: { + DWARFTypeUnit *tu = + m_unit->GetSymbolFileDWARF().DebugInfo()->GetTypeUnitForHash(value); + if (!tu) + return {}; + return tu->GetDIE(tu->GetTypeOffset()); + } + + default: + return {}; + } +} + +uint64_t DWARFFormValue::Reference(dw_offset_t base_offset) const { + uint64_t value = m_value.value.uval; + switch (m_form) { + case DW_FORM_ref1: + case DW_FORM_ref2: + case DW_FORM_ref4: + case DW_FORM_ref8: + case DW_FORM_ref_udata: + return value + base_offset; + + case DW_FORM_ref_addr: + case DW_FORM_ref_sig8: + case DW_FORM_GNU_ref_alt: + return value; + + default: + return DW_INVALID_OFFSET; + } +} + +const uint8_t *DWARFFormValue::BlockData() const { return m_value.data; } + +bool DWARFFormValue::IsBlockForm(const dw_form_t form) { + switch (form) { + case DW_FORM_exprloc: + case DW_FORM_block: + case DW_FORM_block1: + case DW_FORM_block2: + case DW_FORM_block4: + return true; + } + return false; +} + +bool DWARFFormValue::IsDataForm(const dw_form_t form) { + switch (form) { + case DW_FORM_sdata: + case DW_FORM_udata: + case DW_FORM_data1: + case DW_FORM_data2: + case DW_FORM_data4: + case DW_FORM_data8: + return true; + } + return false; +} + +int DWARFFormValue::Compare(const DWARFFormValue &a_value, + const DWARFFormValue &b_value) { + dw_form_t a_form = a_value.Form(); + dw_form_t b_form = b_value.Form(); + if (a_form < b_form) + return -1; + if (a_form > b_form) + return 1; + switch (a_form) { + case DW_FORM_addr: + case DW_FORM_addrx: + case DW_FORM_flag: + case DW_FORM_data1: + case DW_FORM_data2: + case DW_FORM_data4: + case DW_FORM_data8: + case DW_FORM_udata: + case DW_FORM_ref_addr: + case DW_FORM_sec_offset: + case DW_FORM_flag_present: + case DW_FORM_ref_sig8: + case DW_FORM_GNU_addr_index: { + uint64_t a = a_value.Unsigned(); + uint64_t b = b_value.Unsigned(); + if (a < b) + return -1; + if (a > b) + return 1; + return 0; + } + + case DW_FORM_sdata: { + int64_t a = a_value.Signed(); + int64_t b = b_value.Signed(); + if (a < b) + return -1; + if (a > b) + return 1; + return 0; + } + + case DW_FORM_string: + case DW_FORM_strp: + case DW_FORM_GNU_str_index: { + const char *a_string = a_value.AsCString(); + const char *b_string = b_value.AsCString(); + if (a_string == b_string) + return 0; + else if (a_string && b_string) + return strcmp(a_string, b_string); + else if (a_string == nullptr) + return -1; // A string is NULL, and B is valid + else + return 1; // A string valid, and B is NULL + } + + case DW_FORM_block: + case DW_FORM_block1: + case DW_FORM_block2: + case DW_FORM_block4: + case DW_FORM_exprloc: { + uint64_t a_len = a_value.Unsigned(); + uint64_t b_len = b_value.Unsigned(); + if (a_len < b_len) + return -1; + if (a_len > b_len) + return 1; + // The block lengths are the same + return memcmp(a_value.BlockData(), b_value.BlockData(), a_value.Unsigned()); + } break; + + case DW_FORM_ref1: + case DW_FORM_ref2: + case DW_FORM_ref4: + case DW_FORM_ref8: + case DW_FORM_ref_udata: { + uint64_t a = a_value.m_value.value.uval; + uint64_t b = b_value.m_value.value.uval; + if (a < b) + return -1; + if (a > b) + return 1; + return 0; + } + + case DW_FORM_indirect: + llvm_unreachable( + "This shouldn't happen after the form has been extracted..."); + + default: + llvm_unreachable("Unhandled DW_FORM"); + } + return -1; +} + +bool DWARFFormValue::FormIsSupported(dw_form_t form) { + switch (form) { + case DW_FORM_addr: + case DW_FORM_addrx: + case DW_FORM_rnglistx: + case DW_FORM_block2: + case DW_FORM_block4: + case DW_FORM_data2: + case DW_FORM_data4: + case DW_FORM_data8: + case DW_FORM_string: + case DW_FORM_block: + case DW_FORM_block1: + case DW_FORM_data1: + case DW_FORM_flag: + case DW_FORM_sdata: + case DW_FORM_strp: + case DW_FORM_strx: + case DW_FORM_strx1: + case DW_FORM_strx2: + case DW_FORM_strx3: + case DW_FORM_strx4: + case DW_FORM_udata: + case DW_FORM_ref_addr: + case DW_FORM_ref1: + case DW_FORM_ref2: + case DW_FORM_ref4: + case DW_FORM_ref8: + case DW_FORM_ref_udata: + case DW_FORM_indirect: + case DW_FORM_sec_offset: + case DW_FORM_exprloc: + case DW_FORM_flag_present: + case DW_FORM_ref_sig8: + case DW_FORM_GNU_str_index: + case DW_FORM_GNU_addr_index: + case DW_FORM_implicit_const: + return true; + default: + break; + } + return false; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.h new file mode 100644 index 000000000000..848db2990ded --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.h @@ -0,0 +1,89 @@ +//===-- DWARFFormValue.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 SymbolFileDWARF_DWARFFormValue_h_ +#define SymbolFileDWARF_DWARFFormValue_h_ + +#include "DWARFDataExtractor.h" +#include <stddef.h> +#include "llvm/ADT/Optional.h" + +class DWARFUnit; +class SymbolFileDWARF; +class DWARFDIE; + +class DWARFFormValue { +public: + typedef struct ValueTypeTag { + ValueTypeTag() : value(), data(nullptr) { value.uval = 0; } + + union { + uint64_t uval; + int64_t sval; + const char *cstr; + } value; + const uint8_t *data; + } ValueType; + + enum { + eValueTypeInvalid = 0, + eValueTypeUnsigned, + eValueTypeSigned, + eValueTypeCStr, + eValueTypeBlock + }; + + DWARFFormValue() = default; + DWARFFormValue(const DWARFUnit *unit) : m_unit(unit) {} + DWARFFormValue(const DWARFUnit *unit, dw_form_t form) + : m_unit(unit), m_form(form) {} + void SetUnit(const DWARFUnit *unit) { m_unit = unit; } + dw_form_t Form() const { return m_form; } + dw_form_t& FormRef() { return m_form; } + void SetForm(dw_form_t form) { m_form = form; } + const ValueType &Value() const { return m_value; } + ValueType &ValueRef() { return m_value; } + void SetValue(const ValueType &val) { m_value = val; } + + void Dump(lldb_private::Stream &s) const; + bool ExtractValue(const lldb_private::DWARFDataExtractor &data, + lldb::offset_t *offset_ptr); + const uint8_t *BlockData() const; + static llvm::Optional<uint8_t> GetFixedSize(dw_form_t form, + const DWARFUnit *u); + llvm::Optional<uint8_t> GetFixedSize() const; + DWARFDIE Reference() const; + uint64_t Reference(dw_offset_t offset) const; + bool Boolean() const { return m_value.value.uval != 0; } + uint64_t Unsigned() const { return m_value.value.uval; } + void SetUnsigned(uint64_t uval) { m_value.value.uval = uval; } + int64_t Signed() const { return m_value.value.sval; } + void SetSigned(int64_t sval) { m_value.value.sval = sval; } + const char *AsCString() const; + dw_addr_t Address() const; + bool IsValid() const { return m_form != 0; } + bool SkipValue(const lldb_private::DWARFDataExtractor &debug_info_data, + lldb::offset_t *offset_ptr) const; + static bool SkipValue(const dw_form_t form, + const lldb_private::DWARFDataExtractor &debug_info_data, + lldb::offset_t *offset_ptr, const DWARFUnit *unit); + static bool IsBlockForm(const dw_form_t form); + static bool IsDataForm(const dw_form_t form); + static int Compare(const DWARFFormValue &a, const DWARFFormValue &b); + void Clear(); + static bool FormIsSupported(dw_form_t form); + +protected: + // Compile unit where m_value was located. + // It may be different from compile unit where m_value refers to. + const DWARFUnit *m_unit = nullptr; // Unit for this form + dw_form_t m_form = 0; // Form for this value + ValueType m_value; // Contains all data for the form +}; + +#endif // SymbolFileDWARF_DWARFFormValue_h_ diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.cpp new file mode 100644 index 000000000000..c122f756e81a --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.cpp @@ -0,0 +1,66 @@ +//===-- DWARFIndex.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 "Plugins/SymbolFile/DWARF/DWARFIndex.h" +#include "Plugins/Language/ObjC/ObjCLanguage.h" +#include "Plugins/SymbolFile/DWARF/DWARFDIE.h" +#include "Plugins/SymbolFile/DWARF/SymbolFileDWARF.h" + +using namespace lldb_private; +using namespace lldb; + +DWARFIndex::~DWARFIndex() = default; + +void DWARFIndex::ProcessFunctionDIE(llvm::StringRef name, DIERef ref, + SymbolFileDWARF &dwarf, + const CompilerDeclContext &parent_decl_ctx, + uint32_t name_type_mask, + std::vector<DWARFDIE> &dies) { + DWARFDIE die = dwarf.GetDIE(ref); + if (!die) { + ReportInvalidDIERef(ref, name); + return; + } + + // Exit early if we're searching exclusively for methods or selectors and + // we have a context specified (no methods in namespaces). + uint32_t looking_for_nonmethods = + name_type_mask & ~(eFunctionNameTypeMethod | eFunctionNameTypeSelector); + if (!looking_for_nonmethods && parent_decl_ctx.IsValid()) + return; + + // Otherwise, we need to also check that the context matches. If it does not + // match, we do nothing. + if (!SymbolFileDWARF::DIEInDeclContext(&parent_decl_ctx, die)) + return; + + // In case of a full match, we just insert everything we find. + if (name_type_mask & eFunctionNameTypeFull) { + dies.push_back(die); + return; + } + + // If looking for ObjC selectors, we need to also check if the name is a + // possible selector. + if (name_type_mask & eFunctionNameTypeSelector && + ObjCLanguage::IsPossibleObjCMethodName(die.GetName())) { + dies.push_back(die); + return; + } + + bool looking_for_methods = name_type_mask & lldb::eFunctionNameTypeMethod; + bool looking_for_functions = name_type_mask & lldb::eFunctionNameTypeBase; + if (looking_for_methods || looking_for_functions) { + // If we're looking for either methods or functions, we definitely want this + // die. Otherwise, only keep it if the die type matches what we are + // searching for. + if ((looking_for_methods && looking_for_functions) || + looking_for_methods == die.IsMethod()) + dies.push_back(die); + } +} diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.h new file mode 100644 index 000000000000..e3c7c5e63ac8 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.h @@ -0,0 +1,66 @@ +//===-- DWARFIndex.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 LLDB_DWARFINDEX_H +#define LLDB_DWARFINDEX_H + +#include "Plugins/SymbolFile/DWARF/DIERef.h" +#include "Plugins/SymbolFile/DWARF/DWARFDIE.h" +#include "Plugins/SymbolFile/DWARF/DWARFFormValue.h" + +class DWARFDeclContext; +class DWARFDIE; + +namespace lldb_private { +class DWARFIndex { +public: + DWARFIndex(Module &module) : m_module(module) {} + virtual ~DWARFIndex(); + + virtual void Preload() = 0; + + /// Finds global variables with the given base name. Any additional filtering + /// (e.g., to only retrieve variables from a given context) should be done by + /// the consumer. + virtual void GetGlobalVariables(ConstString basename, DIEArray &offsets) = 0; + + virtual void GetGlobalVariables(const RegularExpression ®ex, + DIEArray &offsets) = 0; + virtual void GetGlobalVariables(const DWARFUnit &cu, DIEArray &offsets) = 0; + virtual void GetObjCMethods(ConstString class_name, DIEArray &offsets) = 0; + virtual void GetCompleteObjCClass(ConstString class_name, + bool must_be_implementation, + DIEArray &offsets) = 0; + virtual void GetTypes(ConstString name, DIEArray &offsets) = 0; + virtual void GetTypes(const DWARFDeclContext &context, DIEArray &offsets) = 0; + virtual void GetNamespaces(ConstString name, DIEArray &offsets) = 0; + virtual void GetFunctions(ConstString name, SymbolFileDWARF &dwarf, + const CompilerDeclContext &parent_decl_ctx, + uint32_t name_type_mask, + std::vector<DWARFDIE> &dies) = 0; + virtual void GetFunctions(const RegularExpression ®ex, + DIEArray &offsets) = 0; + + virtual void ReportInvalidDIERef(const DIERef &ref, llvm::StringRef name) = 0; + virtual void Dump(Stream &s) = 0; + +protected: + Module &m_module; + + /// Helper function implementing common logic for processing function dies. If + /// the function given by "ref" matches search criteria given by + /// "parent_decl_ctx" and "name_type_mask", it is inserted into the "dies" + /// vector. + void ProcessFunctionDIE(llvm::StringRef name, DIERef ref, + SymbolFileDWARF &dwarf, + const CompilerDeclContext &parent_decl_ctx, + uint32_t name_type_mask, std::vector<DWARFDIE> &dies); +}; +} // namespace lldb_private + +#endif // LLDB_DWARFINDEX_H diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFTypeUnit.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFTypeUnit.cpp new file mode 100644 index 000000000000..fcc031bf1ea0 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFTypeUnit.cpp @@ -0,0 +1,23 @@ +//===-- DWARFTypeUnit.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 "DWARFTypeUnit.h" + +#include "SymbolFileDWARF.h" +#include "lldb/Utility/Stream.h" + +using namespace lldb; +using namespace lldb_private; + +void DWARFTypeUnit::Dump(Stream *s) const { + s->Printf("0x%8.8x: Type Unit: length = 0x%8.8x, version = 0x%4.4x, " + "abbr_offset = 0x%8.8x, addr_size = 0x%2.2x (next CU at " + "{0x%8.8x})\n", + GetOffset(), GetLength(), GetVersion(), GetAbbrevOffset(), + GetAddressByteSize(), GetNextUnitOffset()); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFTypeUnit.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFTypeUnit.h new file mode 100644 index 000000000000..6ff73ecd8efa --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFTypeUnit.h @@ -0,0 +1,37 @@ +//===-- DWARFTypeUnit.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 SymbolFileDWARF_DWARFTypeUnit_h_ +#define SymbolFileDWARF_DWARFTypeUnit_h_ + +#include "DWARFUnit.h" +#include "llvm/Support/Error.h" + +class DWARFTypeUnit : public DWARFUnit { +public: + void BuildAddressRangeTable(DWARFDebugAranges *debug_aranges) override {} + + void Dump(lldb_private::Stream *s) const override; + + uint64_t GetTypeHash() { return m_header.GetTypeHash(); } + + dw_offset_t GetTypeOffset() { return GetOffset() + m_header.GetTypeOffset(); } + + static bool classof(const DWARFUnit *unit) { return unit->IsTypeUnit(); } + +private: + DWARFTypeUnit(SymbolFileDWARF &dwarf, lldb::user_id_t uid, + const DWARFUnitHeader &header, + const DWARFAbbreviationDeclarationSet &abbrevs, + DIERef::Section section) + : DWARFUnit(dwarf, uid, header, abbrevs, section) {} + + friend class DWARFUnit; +}; + +#endif // SymbolFileDWARF_DWARFTypeUnit_h_ diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp new file mode 100644 index 000000000000..33e83d1fe57f --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp @@ -0,0 +1,878 @@ +//===-- DWARFUnit.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 "DWARFUnit.h" + +#include "lldb/Core/Module.h" +#include "lldb/Host/StringConvert.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Utility/LLDBAssert.h" +#include "lldb/Utility/StreamString.h" +#include "lldb/Utility/Timer.h" +#include "llvm/Object/Error.h" + +#include "DWARFCompileUnit.h" +#include "DWARFDebugAranges.h" +#include "DWARFDebugInfo.h" +#include "DWARFTypeUnit.h" +#include "LogChannelDWARF.h" +#include "SymbolFileDWARFDwo.h" + +using namespace lldb; +using namespace lldb_private; +using namespace std; + +extern int g_verbose; + +DWARFUnit::DWARFUnit(SymbolFileDWARF &dwarf, lldb::user_id_t uid, + const DWARFUnitHeader &header, + const DWARFAbbreviationDeclarationSet &abbrevs, + DIERef::Section section) + : UserID(uid), m_dwarf(dwarf), m_header(header), m_abbrevs(&abbrevs), + m_cancel_scopes(false), m_section(section) {} + +DWARFUnit::~DWARFUnit() = default; + +// Parses first DIE of a compile unit. +void DWARFUnit::ExtractUnitDIEIfNeeded() { + { + llvm::sys::ScopedReader lock(m_first_die_mutex); + if (m_first_die) + return; // Already parsed + } + llvm::sys::ScopedWriter lock(m_first_die_mutex); + if (m_first_die) + return; // Already parsed + + static Timer::Category func_cat(LLVM_PRETTY_FUNCTION); + Timer scoped_timer(func_cat, "%8.8x: DWARFUnit::ExtractUnitDIEIfNeeded()", + GetOffset()); + + // Set the offset to that of the first DIE and calculate the start of the + // next compilation unit header. + lldb::offset_t offset = GetFirstDIEOffset(); + + // We are in our compile unit, parse starting at the offset we were told to + // parse + const DWARFDataExtractor &data = GetData(); + if (offset < GetNextUnitOffset() && + m_first_die.Extract(data, this, &offset)) { + AddUnitDIE(m_first_die); + return; + } +} + +// Parses a compile unit and indexes its DIEs if it hasn't already been done. +// It will leave this compile unit extracted forever. +void DWARFUnit::ExtractDIEsIfNeeded() { + m_cancel_scopes = true; + + { + llvm::sys::ScopedReader lock(m_die_array_mutex); + if (!m_die_array.empty()) + return; // Already parsed + } + llvm::sys::ScopedWriter lock(m_die_array_mutex); + if (!m_die_array.empty()) + return; // Already parsed + + ExtractDIEsRWLocked(); +} + +// Parses a compile unit and indexes its DIEs if it hasn't already been done. +// It will clear this compile unit after returned instance gets out of scope, +// no other ScopedExtractDIEs instance is running for this compile unit +// and no ExtractDIEsIfNeeded() has been executed during this ScopedExtractDIEs +// lifetime. +DWARFUnit::ScopedExtractDIEs DWARFUnit::ExtractDIEsScoped() { + ScopedExtractDIEs scoped(*this); + + { + llvm::sys::ScopedReader lock(m_die_array_mutex); + if (!m_die_array.empty()) + return scoped; // Already parsed + } + llvm::sys::ScopedWriter lock(m_die_array_mutex); + if (!m_die_array.empty()) + return scoped; // Already parsed + + // Otherwise m_die_array would be already populated. + lldbassert(!m_cancel_scopes); + + ExtractDIEsRWLocked(); + scoped.m_clear_dies = true; + return scoped; +} + +DWARFUnit::ScopedExtractDIEs::ScopedExtractDIEs(DWARFUnit &cu) : m_cu(&cu) { + m_cu->m_die_array_scoped_mutex.lock_shared(); +} + +DWARFUnit::ScopedExtractDIEs::~ScopedExtractDIEs() { + if (!m_cu) + return; + m_cu->m_die_array_scoped_mutex.unlock_shared(); + if (!m_clear_dies || m_cu->m_cancel_scopes) + return; + // Be sure no other ScopedExtractDIEs is running anymore. + llvm::sys::ScopedWriter lock_scoped(m_cu->m_die_array_scoped_mutex); + llvm::sys::ScopedWriter lock(m_cu->m_die_array_mutex); + if (m_cu->m_cancel_scopes) + return; + m_cu->ClearDIEsRWLocked(); +} + +DWARFUnit::ScopedExtractDIEs::ScopedExtractDIEs(ScopedExtractDIEs &&rhs) + : m_cu(rhs.m_cu), m_clear_dies(rhs.m_clear_dies) { + rhs.m_cu = nullptr; +} + +DWARFUnit::ScopedExtractDIEs &DWARFUnit::ScopedExtractDIEs::operator=( + DWARFUnit::ScopedExtractDIEs &&rhs) { + m_cu = rhs.m_cu; + rhs.m_cu = nullptr; + m_clear_dies = rhs.m_clear_dies; + return *this; +} + +// Parses a compile unit and indexes its DIEs, m_die_array_mutex must be +// held R/W and m_die_array must be empty. +void DWARFUnit::ExtractDIEsRWLocked() { + llvm::sys::ScopedWriter first_die_lock(m_first_die_mutex); + + static Timer::Category func_cat(LLVM_PRETTY_FUNCTION); + Timer scoped_timer(func_cat, "%8.8x: DWARFUnit::ExtractDIEsIfNeeded()", + GetOffset()); + + // Set the offset to that of the first DIE and calculate the start of the + // next compilation unit header. + lldb::offset_t offset = GetFirstDIEOffset(); + lldb::offset_t next_cu_offset = GetNextUnitOffset(); + + DWARFDebugInfoEntry die; + + uint32_t depth = 0; + // We are in our compile unit, parse starting at the offset we were told to + // parse + const DWARFDataExtractor &data = GetData(); + std::vector<uint32_t> die_index_stack; + die_index_stack.reserve(32); + die_index_stack.push_back(0); + bool prev_die_had_children = false; + while (offset < next_cu_offset && die.Extract(data, this, &offset)) { + const bool null_die = die.IsNULL(); + if (depth == 0) { + assert(m_die_array.empty() && "Compile unit DIE already added"); + + // The average bytes per DIE entry has been seen to be around 14-20 so + // lets pre-reserve half of that since we are now stripping the NULL + // tags. + + // Only reserve the memory if we are adding children of the main + // compile unit DIE. The compile unit DIE is always the first entry, so + // if our size is 1, then we are adding the first compile unit child + // DIE and should reserve the memory. + m_die_array.reserve(GetDebugInfoSize() / 24); + m_die_array.push_back(die); + + if (!m_first_die) + AddUnitDIE(m_die_array.front()); + + // With -fsplit-dwarf-inlining, clang will emit non-empty skeleton compile + // units. We are not able to access these DIE *and* the dwo file + // simultaneously. We also don't need to do that as the dwo file will + // contain a superset of information. So, we don't even attempt to parse + // any remaining DIEs. + if (m_dwo_symbol_file) { + m_die_array.front().SetHasChildren(false); + break; + } + + } else { + if (null_die) { + if (prev_die_had_children) { + // This will only happen if a DIE says is has children but all it + // contains is a NULL tag. Since we are removing the NULL DIEs from + // the list (saves up to 25% in C++ code), we need a way to let the + // DIE know that it actually doesn't have children. + if (!m_die_array.empty()) + m_die_array.back().SetHasChildren(false); + } + } else { + die.SetParentIndex(m_die_array.size() - die_index_stack[depth - 1]); + + if (die_index_stack.back()) + m_die_array[die_index_stack.back()].SetSiblingIndex( + m_die_array.size() - die_index_stack.back()); + + // Only push the DIE if it isn't a NULL DIE + m_die_array.push_back(die); + } + } + + if (null_die) { + // NULL DIE. + if (!die_index_stack.empty()) + die_index_stack.pop_back(); + + if (depth > 0) + --depth; + prev_die_had_children = false; + } else { + die_index_stack.back() = m_die_array.size() - 1; + // Normal DIE + const bool die_has_children = die.HasChildren(); + if (die_has_children) { + die_index_stack.push_back(0); + ++depth; + } + prev_die_had_children = die_has_children; + } + + if (depth == 0) + break; // We are done with this compile unit! + } + + if (!m_die_array.empty()) { + if (m_first_die) { + // Only needed for the assertion. + m_first_die.SetHasChildren(m_die_array.front().HasChildren()); + lldbassert(m_first_die == m_die_array.front()); + } + m_first_die = m_die_array.front(); + } + + m_die_array.shrink_to_fit(); + + if (m_dwo_symbol_file) { + DWARFUnit *dwo_cu = m_dwo_symbol_file->GetCompileUnit(); + dwo_cu->ExtractDIEsIfNeeded(); + } +} + +// This is used when a split dwarf is enabled. +// A skeleton compilation unit may contain the DW_AT_str_offsets_base attribute +// that points to the first string offset of the CU contribution to the +// .debug_str_offsets. At the same time, the corresponding split debug unit also +// may use DW_FORM_strx* forms pointing to its own .debug_str_offsets.dwo and +// for that case, we should find the offset (skip the section header). +static void SetDwoStrOffsetsBase(DWARFUnit *dwo_cu) { + lldb::offset_t baseOffset = 0; + + const DWARFDataExtractor &strOffsets = + dwo_cu->GetSymbolFileDWARF().GetDWARFContext().getOrLoadStrOffsetsData(); + uint64_t length = strOffsets.GetU32(&baseOffset); + if (length == 0xffffffff) + length = strOffsets.GetU64(&baseOffset); + + // Check version. + if (strOffsets.GetU16(&baseOffset) < 5) + return; + + // Skip padding. + baseOffset += 2; + + dwo_cu->SetStrOffsetsBase(baseOffset); +} + +// m_die_array_mutex must be already held as read/write. +void DWARFUnit::AddUnitDIE(const DWARFDebugInfoEntry &cu_die) { + llvm::Optional<uint64_t> addr_base, gnu_addr_base, ranges_base, + gnu_ranges_base; + + DWARFAttributes attributes; + size_t num_attributes = cu_die.GetAttributes(this, attributes); + for (size_t i = 0; i < num_attributes; ++i) { + dw_attr_t attr = attributes.AttributeAtIndex(i); + DWARFFormValue form_value; + if (!attributes.ExtractFormValueAtIndex(i, form_value)) + continue; + switch (attr) { + case DW_AT_addr_base: + addr_base = form_value.Unsigned(); + SetAddrBase(*addr_base); + break; + case DW_AT_rnglists_base: + ranges_base = form_value.Unsigned(); + SetRangesBase(*ranges_base); + break; + case DW_AT_str_offsets_base: + SetStrOffsetsBase(form_value.Unsigned()); + break; + case DW_AT_low_pc: + SetBaseAddress(form_value.Address()); + break; + case DW_AT_entry_pc: + // If the value was already set by DW_AT_low_pc, don't update it. + if (m_base_addr == LLDB_INVALID_ADDRESS) + SetBaseAddress(form_value.Address()); + break; + case DW_AT_stmt_list: + m_line_table_offset = form_value.Unsigned(); + break; + case DW_AT_GNU_addr_base: + gnu_addr_base = form_value.Unsigned(); + break; + case DW_AT_GNU_ranges_base: + gnu_ranges_base = form_value.Unsigned(); + break; + } + } + + std::unique_ptr<SymbolFileDWARFDwo> dwo_symbol_file = + m_dwarf.GetDwoSymbolFileForCompileUnit(*this, cu_die); + if (!dwo_symbol_file) + return; + + DWARFUnit *dwo_cu = dwo_symbol_file->GetCompileUnit(); + if (!dwo_cu) + return; // Can't fetch the compile unit from the dwo file. + + DWARFBaseDIE dwo_cu_die = dwo_cu->GetUnitDIEOnly(); + if (!dwo_cu_die.IsValid()) + return; // Can't fetch the compile unit DIE from the dwo file. + + uint64_t main_dwo_id = + cu_die.GetAttributeValueAsUnsigned(this, DW_AT_GNU_dwo_id, 0); + uint64_t sub_dwo_id = + dwo_cu_die.GetAttributeValueAsUnsigned(DW_AT_GNU_dwo_id, 0); + if (main_dwo_id != sub_dwo_id) + return; // The 2 dwo ID isn't match. Don't use the dwo file as it belongs to + // a differectn compilation. + + m_dwo_symbol_file = std::move(dwo_symbol_file); + + // Here for DWO CU we want to use the address base set in the skeleton unit + // (DW_AT_addr_base) if it is available and use the DW_AT_GNU_addr_base + // otherwise. We do that because pre-DWARF v5 could use the DW_AT_GNU_* + // attributes which were applicable to the DWO units. The corresponding + // DW_AT_* attributes standardized in DWARF v5 are also applicable to the main + // unit in contrast. + if (addr_base) + dwo_cu->SetAddrBase(*addr_base); + else if (gnu_addr_base) + dwo_cu->SetAddrBase(*gnu_addr_base); + + if (ranges_base) + dwo_cu->SetRangesBase(*ranges_base); + else if (gnu_ranges_base) + dwo_cu->SetRangesBase(*gnu_ranges_base); + + for (size_t i = 0; i < m_dwo_symbol_file->DebugInfo()->GetNumUnits(); ++i) { + DWARFUnit *unit = m_dwo_symbol_file->DebugInfo()->GetUnitAtIndex(i); + SetDwoStrOffsetsBase(unit); + } +} + +DWARFDIE DWARFUnit::LookupAddress(const dw_addr_t address) { + if (DIE()) { + const DWARFDebugAranges &func_aranges = GetFunctionAranges(); + + // Re-check the aranges auto pointer contents in case it was created above + if (!func_aranges.IsEmpty()) + return GetDIE(func_aranges.FindAddress(address)); + } + return DWARFDIE(); +} + +size_t DWARFUnit::AppendDIEsWithTag(const dw_tag_t tag, + std::vector<DWARFDIE> &dies, + uint32_t depth) const { + size_t old_size = dies.size(); + { + llvm::sys::ScopedReader lock(m_die_array_mutex); + DWARFDebugInfoEntry::const_iterator pos; + DWARFDebugInfoEntry::const_iterator end = m_die_array.end(); + for (pos = m_die_array.begin(); pos != end; ++pos) { + if (pos->Tag() == tag) + dies.emplace_back(this, &(*pos)); + } + } + + // Return the number of DIEs added to the collection + return dies.size() - old_size; +} + +size_t DWARFUnit::GetDebugInfoSize() const { + return GetLengthByteSize() + GetLength() - GetHeaderByteSize(); +} + +const DWARFAbbreviationDeclarationSet *DWARFUnit::GetAbbreviations() const { + return m_abbrevs; +} + +dw_offset_t DWARFUnit::GetAbbrevOffset() const { + return m_abbrevs ? m_abbrevs->GetOffset() : DW_INVALID_OFFSET; +} + +dw_offset_t DWARFUnit::GetLineTableOffset() { + ExtractUnitDIEIfNeeded(); + return m_line_table_offset; +} + +void DWARFUnit::SetAddrBase(dw_addr_t addr_base) { m_addr_base = addr_base; } + +void DWARFUnit::SetRangesBase(dw_addr_t ranges_base) { + m_ranges_base = ranges_base; +} + +void DWARFUnit::SetStrOffsetsBase(dw_offset_t str_offsets_base) { + m_str_offsets_base = str_offsets_base; +} + +// It may be called only with m_die_array_mutex held R/W. +void DWARFUnit::ClearDIEsRWLocked() { + m_die_array.clear(); + m_die_array.shrink_to_fit(); + + if (m_dwo_symbol_file) + m_dwo_symbol_file->GetCompileUnit()->ClearDIEsRWLocked(); +} + +lldb::ByteOrder DWARFUnit::GetByteOrder() const { + return m_dwarf.GetObjectFile()->GetByteOrder(); +} + +TypeSystem *DWARFUnit::GetTypeSystem() { + return m_dwarf.GetTypeSystemForLanguage(GetLanguageType()); +} + +void DWARFUnit::SetBaseAddress(dw_addr_t base_addr) { m_base_addr = base_addr; } + +// Compare function DWARFDebugAranges::Range structures +static bool CompareDIEOffset(const DWARFDebugInfoEntry &die, + const dw_offset_t die_offset) { + return die.GetOffset() < die_offset; +} + +// GetDIE() +// +// Get the DIE (Debug Information Entry) with the specified offset by first +// checking if the DIE is contained within this compile unit and grabbing the +// DIE from this compile unit. Otherwise we grab the DIE from the DWARF file. +DWARFDIE +DWARFUnit::GetDIE(dw_offset_t die_offset) { + if (die_offset != DW_INVALID_OFFSET) { + if (GetDwoSymbolFile()) + return GetDwoSymbolFile()->GetCompileUnit()->GetDIE(die_offset); + + if (ContainsDIEOffset(die_offset)) { + ExtractDIEsIfNeeded(); + DWARFDebugInfoEntry::const_iterator end = m_die_array.cend(); + DWARFDebugInfoEntry::const_iterator pos = + lower_bound(m_die_array.cbegin(), end, die_offset, CompareDIEOffset); + if (pos != end) { + if (die_offset == (*pos).GetOffset()) + return DWARFDIE(this, &(*pos)); + } + } else + GetSymbolFileDWARF().GetObjectFile()->GetModule()->ReportError( + "GetDIE for DIE 0x%" PRIx32 " is outside of its CU 0x%" PRIx32, + die_offset, GetOffset()); + } + return DWARFDIE(); // Not found +} + +DWARFUnit &DWARFUnit::GetNonSkeletonUnit() { + if (SymbolFileDWARFDwo *dwo = GetDwoSymbolFile()) + return *dwo->GetCompileUnit(); + return *this; +} + +uint8_t DWARFUnit::GetAddressByteSize(const DWARFUnit *cu) { + if (cu) + return cu->GetAddressByteSize(); + return DWARFUnit::GetDefaultAddressSize(); +} + +uint8_t DWARFUnit::GetDefaultAddressSize() { return 4; } + +void *DWARFUnit::GetUserData() const { return m_user_data; } + +void DWARFUnit::SetUserData(void *d) { + m_user_data = d; + if (m_dwo_symbol_file) + m_dwo_symbol_file->GetCompileUnit()->SetUserData(d); +} + +bool DWARFUnit::Supports_DW_AT_APPLE_objc_complete_type() { + return GetProducer() != eProducerLLVMGCC; +} + +bool DWARFUnit::DW_AT_decl_file_attributes_are_invalid() { + // llvm-gcc makes completely invalid decl file attributes and won't ever be + // fixed, so we need to know to ignore these. + return GetProducer() == eProducerLLVMGCC; +} + +bool DWARFUnit::Supports_unnamed_objc_bitfields() { + if (GetProducer() == eProducerClang) { + const uint32_t major_version = GetProducerVersionMajor(); + return major_version > 425 || + (major_version == 425 && GetProducerVersionUpdate() >= 13); + } + return true; // Assume all other compilers didn't have incorrect ObjC bitfield + // info +} + +void DWARFUnit::ParseProducerInfo() { + m_producer_version_major = UINT32_MAX; + m_producer_version_minor = UINT32_MAX; + m_producer_version_update = UINT32_MAX; + + const DWARFDebugInfoEntry *die = GetUnitDIEPtrOnly(); + if (die) { + + const char *producer_cstr = + die->GetAttributeValueAsString(this, DW_AT_producer, nullptr); + if (producer_cstr) { + RegularExpression llvm_gcc_regex( + llvm::StringRef("^4\\.[012]\\.[01] \\(Based on Apple " + "Inc\\. build [0-9]+\\) \\(LLVM build " + "[\\.0-9]+\\)$")); + if (llvm_gcc_regex.Execute(llvm::StringRef(producer_cstr))) { + m_producer = eProducerLLVMGCC; + } else if (strstr(producer_cstr, "clang")) { + static RegularExpression g_clang_version_regex( + llvm::StringRef("clang-([0-9]+)\\.([0-9]+)\\.([0-9]+)")); + RegularExpression::Match regex_match(3); + if (g_clang_version_regex.Execute(llvm::StringRef(producer_cstr), + ®ex_match)) { + std::string str; + if (regex_match.GetMatchAtIndex(producer_cstr, 1, str)) + m_producer_version_major = + StringConvert::ToUInt32(str.c_str(), UINT32_MAX, 10); + if (regex_match.GetMatchAtIndex(producer_cstr, 2, str)) + m_producer_version_minor = + StringConvert::ToUInt32(str.c_str(), UINT32_MAX, 10); + if (regex_match.GetMatchAtIndex(producer_cstr, 3, str)) + m_producer_version_update = + StringConvert::ToUInt32(str.c_str(), UINT32_MAX, 10); + } + m_producer = eProducerClang; + } else if (strstr(producer_cstr, "GNU")) + m_producer = eProducerGCC; + } + } + if (m_producer == eProducerInvalid) + m_producer = eProcucerOther; +} + +DWARFProducer DWARFUnit::GetProducer() { + if (m_producer == eProducerInvalid) + ParseProducerInfo(); + return m_producer; +} + +uint32_t DWARFUnit::GetProducerVersionMajor() { + if (m_producer_version_major == 0) + ParseProducerInfo(); + return m_producer_version_major; +} + +uint32_t DWARFUnit::GetProducerVersionMinor() { + if (m_producer_version_minor == 0) + ParseProducerInfo(); + return m_producer_version_minor; +} + +uint32_t DWARFUnit::GetProducerVersionUpdate() { + if (m_producer_version_update == 0) + ParseProducerInfo(); + return m_producer_version_update; +} +LanguageType DWARFUnit::LanguageTypeFromDWARF(uint64_t val) { + // Note: user languages between lo_user and hi_user must be handled + // explicitly here. + switch (val) { + case DW_LANG_Mips_Assembler: + return eLanguageTypeMipsAssembler; + case DW_LANG_GOOGLE_RenderScript: + return eLanguageTypeExtRenderScript; + default: + return static_cast<LanguageType>(val); + } +} + +LanguageType DWARFUnit::GetLanguageType() { + if (m_language_type != eLanguageTypeUnknown) + return m_language_type; + + const DWARFDebugInfoEntry *die = GetUnitDIEPtrOnly(); + if (die) + m_language_type = LanguageTypeFromDWARF( + die->GetAttributeValueAsUnsigned(this, DW_AT_language, 0)); + return m_language_type; +} + +bool DWARFUnit::GetIsOptimized() { + if (m_is_optimized == eLazyBoolCalculate) { + const DWARFDebugInfoEntry *die = GetUnitDIEPtrOnly(); + if (die) { + m_is_optimized = eLazyBoolNo; + if (die->GetAttributeValueAsUnsigned(this, DW_AT_APPLE_optimized, 0) == + 1) { + m_is_optimized = eLazyBoolYes; + } + } + } + return m_is_optimized == eLazyBoolYes; +} + +FileSpec::Style DWARFUnit::GetPathStyle() { + if (!m_comp_dir) + ComputeCompDirAndGuessPathStyle(); + return m_comp_dir->GetPathStyle(); +} + +const FileSpec &DWARFUnit::GetCompilationDirectory() { + if (!m_comp_dir) + ComputeCompDirAndGuessPathStyle(); + return *m_comp_dir; +} + +const FileSpec &DWARFUnit::GetAbsolutePath() { + if (!m_file_spec) + ComputeAbsolutePath(); + return *m_file_spec; +} + +FileSpec DWARFUnit::GetFile(size_t file_idx) { + return m_dwarf.GetFile(*this, file_idx); +} + +// DWARF2/3 suggests the form hostname:pathname for compilation directory. +// Remove the host part if present. +static llvm::StringRef +removeHostnameFromPathname(llvm::StringRef path_from_dwarf) { + llvm::StringRef host, path; + std::tie(host, path) = path_from_dwarf.split(':'); + + if (host.contains('/')) + return path_from_dwarf; + + // check whether we have a windows path, and so the first character is a + // drive-letter not a hostname. + if (host.size() == 1 && llvm::isAlpha(host[0]) && path.startswith("\\")) + return path_from_dwarf; + + return path; +} + +static FileSpec resolveCompDir(const FileSpec &path) { + bool is_symlink = SymbolFileDWARF::GetSymlinkPaths().FindFileIndex( + 0, path, /*full*/ true) != UINT32_MAX; + + if (!is_symlink) + return path; + + namespace fs = llvm::sys::fs; + if (fs::get_file_type(path.GetPath(), false) != fs::file_type::symlink_file) + return path; + + FileSpec resolved_symlink; + const auto error = FileSystem::Instance().Readlink(path, resolved_symlink); + if (error.Success()) + return resolved_symlink; + + return path; +} + +void DWARFUnit::ComputeCompDirAndGuessPathStyle() { + m_comp_dir = FileSpec(); + const DWARFDebugInfoEntry *die = GetUnitDIEPtrOnly(); + if (!die) + return; + + llvm::StringRef comp_dir = removeHostnameFromPathname( + die->GetAttributeValueAsString(this, DW_AT_comp_dir, nullptr)); + if (!comp_dir.empty()) { + FileSpec::Style comp_dir_style = + FileSpec::GuessPathStyle(comp_dir).getValueOr(FileSpec::Style::native); + m_comp_dir = resolveCompDir(FileSpec(comp_dir, comp_dir_style)); + } else { + // Try to detect the style based on the DW_AT_name attribute, but just store + // the detected style in the m_comp_dir field. + const char *name = + die->GetAttributeValueAsString(this, DW_AT_name, nullptr); + m_comp_dir = FileSpec( + "", FileSpec::GuessPathStyle(name).getValueOr(FileSpec::Style::native)); + } +} + +void DWARFUnit::ComputeAbsolutePath() { + m_file_spec = FileSpec(); + const DWARFDebugInfoEntry *die = GetUnitDIEPtrOnly(); + if (!die) + return; + + m_file_spec = + FileSpec(die->GetAttributeValueAsString(this, DW_AT_name, nullptr), + GetPathStyle()); + + if (m_file_spec->IsRelative()) + m_file_spec->MakeAbsolute(GetCompilationDirectory()); +} + +SymbolFileDWARFDwo *DWARFUnit::GetDwoSymbolFile() const { + return m_dwo_symbol_file.get(); +} + +const DWARFDebugAranges &DWARFUnit::GetFunctionAranges() { + if (m_func_aranges_up == nullptr) { + m_func_aranges_up.reset(new DWARFDebugAranges()); + const DWARFDebugInfoEntry *die = DIEPtr(); + if (die) + die->BuildFunctionAddressRangeTable(this, m_func_aranges_up.get()); + + if (m_dwo_symbol_file) { + DWARFUnit *dwo_cu = m_dwo_symbol_file->GetCompileUnit(); + const DWARFDebugInfoEntry *dwo_die = dwo_cu->DIEPtr(); + if (dwo_die) + dwo_die->BuildFunctionAddressRangeTable(dwo_cu, + m_func_aranges_up.get()); + } + + const bool minimize = false; + m_func_aranges_up->Sort(minimize); + } + return *m_func_aranges_up; +} + +llvm::Expected<DWARFUnitHeader> +DWARFUnitHeader::extract(const DWARFDataExtractor &data, DIERef::Section section, + lldb::offset_t *offset_ptr) { + DWARFUnitHeader header; + header.m_offset = *offset_ptr; + header.m_length = data.GetDWARFInitialLength(offset_ptr); + header.m_version = data.GetU16(offset_ptr); + if (header.m_version == 5) { + header.m_unit_type = data.GetU8(offset_ptr); + header.m_addr_size = data.GetU8(offset_ptr); + header.m_abbr_offset = data.GetDWARFOffset(offset_ptr); + if (header.m_unit_type == llvm::dwarf::DW_UT_skeleton) + header.m_dwo_id = data.GetU64(offset_ptr); + } else { + header.m_abbr_offset = data.GetDWARFOffset(offset_ptr); + header.m_addr_size = data.GetU8(offset_ptr); + header.m_unit_type = + section == DIERef::Section::DebugTypes ? DW_UT_type : DW_UT_compile; + } + + if (header.IsTypeUnit()) { + header.m_type_hash = data.GetU64(offset_ptr); + header.m_type_offset = data.GetDWARFOffset(offset_ptr); + } + + bool length_OK = data.ValidOffset(header.GetNextUnitOffset() - 1); + bool version_OK = SymbolFileDWARF::SupportedVersion(header.m_version); + bool addr_size_OK = (header.m_addr_size == 4) || (header.m_addr_size == 8); + bool type_offset_OK = + !header.IsTypeUnit() || (header.m_type_offset <= header.GetLength()); + + if (!length_OK) + return llvm::make_error<llvm::object::GenericBinaryError>( + "Invalid unit length"); + if (!version_OK) + return llvm::make_error<llvm::object::GenericBinaryError>( + "Unsupported unit version"); + if (!addr_size_OK) + return llvm::make_error<llvm::object::GenericBinaryError>( + "Invalid unit address size"); + if (!type_offset_OK) + return llvm::make_error<llvm::object::GenericBinaryError>( + "Type offset out of range"); + + return header; +} + +llvm::Expected<DWARFUnitSP> +DWARFUnit::extract(SymbolFileDWARF &dwarf, user_id_t uid, + const DWARFDataExtractor &debug_info, + DIERef::Section section, lldb::offset_t *offset_ptr) { + assert(debug_info.ValidOffset(*offset_ptr)); + + auto expected_header = + DWARFUnitHeader::extract(debug_info, section, offset_ptr); + if (!expected_header) + return expected_header.takeError(); + + const DWARFDebugAbbrev *abbr = dwarf.DebugAbbrev(); + if (!abbr) + return llvm::make_error<llvm::object::GenericBinaryError>( + "No debug_abbrev data"); + + bool abbr_offset_OK = + dwarf.GetDWARFContext().getOrLoadAbbrevData().ValidOffset( + expected_header->GetAbbrOffset()); + if (!abbr_offset_OK) + return llvm::make_error<llvm::object::GenericBinaryError>( + "Abbreviation offset for unit is not valid"); + + const DWARFAbbreviationDeclarationSet *abbrevs = + abbr->GetAbbreviationDeclarationSet(expected_header->GetAbbrOffset()); + if (!abbrevs) + return llvm::make_error<llvm::object::GenericBinaryError>( + "No abbrev exists at the specified offset."); + + if (expected_header->IsTypeUnit()) + return DWARFUnitSP( + new DWARFTypeUnit(dwarf, uid, *expected_header, *abbrevs, section)); + return DWARFUnitSP( + new DWARFCompileUnit(dwarf, uid, *expected_header, *abbrevs, section)); +} + +const lldb_private::DWARFDataExtractor &DWARFUnit::GetData() const { + return m_section == DIERef::Section::DebugTypes + ? m_dwarf.GetDWARFContext().getOrLoadDebugTypesData() + : m_dwarf.GetDWARFContext().getOrLoadDebugInfoData(); +} + +uint32_t DWARFUnit::GetHeaderByteSize() const { + switch (m_header.GetUnitType()) { + case llvm::dwarf::DW_UT_compile: + case llvm::dwarf::DW_UT_partial: + return GetVersion() < 5 ? 11 : 12; + case llvm::dwarf::DW_UT_skeleton: + case llvm::dwarf::DW_UT_split_compile: + return 20; + case llvm::dwarf::DW_UT_type: + case llvm::dwarf::DW_UT_split_type: + return GetVersion() < 5 ? 23 : 24; + } + llvm_unreachable("invalid UnitType."); +} + +llvm::Expected<DWARFRangeList> +DWARFUnit::FindRnglistFromOffset(dw_offset_t offset) const { + const DWARFDebugRangesBase *debug_ranges; + llvm::StringRef section; + if (GetVersion() <= 4) { + debug_ranges = m_dwarf.GetDebugRanges(); + section = "debug_ranges"; + } else { + debug_ranges = m_dwarf.GetDebugRngLists(); + section = "debug_rnglists"; + } + if (!debug_ranges) + return llvm::make_error<llvm::object::GenericBinaryError>("No " + section + + " section"); + + DWARFRangeList ranges; + debug_ranges->FindRanges(this, offset, ranges); + return ranges; +} + +llvm::Expected<DWARFRangeList> +DWARFUnit::FindRnglistFromIndex(uint32_t index) const { + const DWARFDebugRangesBase *debug_rnglists = m_dwarf.GetDebugRngLists(); + if (!debug_rnglists) + return llvm::make_error<llvm::object::GenericBinaryError>( + "No debug_rnglists section"); + return FindRnglistFromOffset(debug_rnglists->GetOffset(index)); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h new file mode 100644 index 000000000000..8aa1e449f3ed --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h @@ -0,0 +1,306 @@ +//===-- DWARFUnit.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 SymbolFileDWARF_DWARFUnit_h_ +#define SymbolFileDWARF_DWARFUnit_h_ + +#include "DWARFDIE.h" +#include "DWARFDebugInfoEntry.h" +#include "lldb/lldb-enumerations.h" +#include "llvm/Support/RWMutex.h" +#include <atomic> + +class DWARFUnit; +class DWARFCompileUnit; +class NameToDIE; +class SymbolFileDWARF; +class SymbolFileDWARFDwo; + +typedef std::shared_ptr<DWARFUnit> DWARFUnitSP; + +enum DWARFProducer { + eProducerInvalid = 0, + eProducerClang, + eProducerGCC, + eProducerLLVMGCC, + eProcucerOther +}; + +/// Base class describing the header of any kind of "unit." Some information +/// is specific to certain unit types. We separate this class out so we can +/// parse the header before deciding what specific kind of unit to construct. +class DWARFUnitHeader { + dw_offset_t m_offset = 0; + dw_offset_t m_length = 0; + uint16_t m_version = 0; + dw_offset_t m_abbr_offset = 0; + uint8_t m_unit_type = 0; + uint8_t m_addr_size = 0; + + uint64_t m_type_hash = 0; + uint32_t m_type_offset = 0; + + uint64_t m_dwo_id = 0; + + DWARFUnitHeader() = default; + +public: + dw_offset_t GetOffset() const { return m_offset; } + uint16_t GetVersion() const { return m_version; } + uint16_t GetAddressByteSize() const { return m_addr_size; } + dw_offset_t GetLength() const { return m_length; } + dw_offset_t GetAbbrOffset() const { return m_abbr_offset; } + uint8_t GetUnitType() const { return m_unit_type; } + uint64_t GetTypeHash() const { return m_type_hash; } + dw_offset_t GetTypeOffset() const { return m_type_offset; } + bool IsTypeUnit() const { + return m_unit_type == DW_UT_type || m_unit_type == DW_UT_split_type; + } + uint32_t GetNextUnitOffset() const { return m_offset + m_length + 4; } + + static llvm::Expected<DWARFUnitHeader> + extract(const lldb_private::DWARFDataExtractor &data, DIERef::Section section, + lldb::offset_t *offset_ptr); +}; + +class DWARFUnit : public lldb_private::UserID { + using die_iterator_range = + llvm::iterator_range<DWARFDebugInfoEntry::collection::iterator>; + +public: + static llvm::Expected<DWARFUnitSP> + extract(SymbolFileDWARF &dwarf2Data, lldb::user_id_t uid, + const lldb_private::DWARFDataExtractor &debug_info, + DIERef::Section section, lldb::offset_t *offset_ptr); + virtual ~DWARFUnit(); + + void ExtractUnitDIEIfNeeded(); + void ExtractDIEsIfNeeded(); + + class ScopedExtractDIEs { + DWARFUnit *m_cu; + public: + bool m_clear_dies = false; + ScopedExtractDIEs(DWARFUnit &cu); + ~ScopedExtractDIEs(); + DISALLOW_COPY_AND_ASSIGN(ScopedExtractDIEs); + ScopedExtractDIEs(ScopedExtractDIEs &&rhs); + ScopedExtractDIEs &operator=(ScopedExtractDIEs &&rhs); + }; + ScopedExtractDIEs ExtractDIEsScoped(); + + DWARFDIE LookupAddress(const dw_addr_t address); + size_t AppendDIEsWithTag(const dw_tag_t tag, std::vector<DWARFDIE> &dies, + uint32_t depth = UINT32_MAX) const; + bool Verify(lldb_private::Stream *s) const; + virtual void Dump(lldb_private::Stream *s) const = 0; + /// Get the data that contains the DIE information for this unit. + /// + /// This will return the correct bytes that contain the data for + /// this DWARFUnit. It could be .debug_info or .debug_types + /// depending on where the data for this unit originates. + /// + /// \return + /// The correct data for the DIE information in this unit. + const lldb_private::DWARFDataExtractor &GetData() const; + + /// Get the size in bytes of the unit header. + /// + /// \return + /// Byte size of the unit header + uint32_t GetHeaderByteSize() const; + + // Offset of the initial length field. + dw_offset_t GetOffset() const { return m_header.GetOffset(); } + /// Get the size in bytes of the length field in the header. + /// + /// In DWARF32 this is just 4 bytes + /// + /// \return + /// Byte size of the compile unit header length field + size_t GetLengthByteSize() const { return 4; } + + bool ContainsDIEOffset(dw_offset_t die_offset) const { + return die_offset >= GetFirstDIEOffset() && + die_offset < GetNextUnitOffset(); + } + dw_offset_t GetFirstDIEOffset() const { + return GetOffset() + GetHeaderByteSize(); + } + dw_offset_t GetNextUnitOffset() const { return m_header.GetNextUnitOffset(); } + // Size of the CU data (without initial length and without header). + size_t GetDebugInfoSize() const; + // Size of the CU data incl. header but without initial length. + uint32_t GetLength() const { return m_header.GetLength(); } + uint16_t GetVersion() const { return m_header.GetVersion(); } + const DWARFAbbreviationDeclarationSet *GetAbbreviations() const; + dw_offset_t GetAbbrevOffset() const; + uint8_t GetAddressByteSize() const { return m_header.GetAddressByteSize(); } + dw_addr_t GetAddrBase() const { return m_addr_base; } + dw_addr_t GetBaseAddress() const { return m_base_addr; } + dw_offset_t GetLineTableOffset(); + dw_addr_t GetRangesBase() const { return m_ranges_base; } + dw_addr_t GetStrOffsetsBase() const { return m_str_offsets_base; } + void SetAddrBase(dw_addr_t addr_base); + void SetRangesBase(dw_addr_t ranges_base); + void SetStrOffsetsBase(dw_offset_t str_offsets_base); + virtual void BuildAddressRangeTable(DWARFDebugAranges *debug_aranges) = 0; + + lldb::ByteOrder GetByteOrder() const; + + lldb_private::TypeSystem *GetTypeSystem(); + + const DWARFDebugAranges &GetFunctionAranges(); + + void SetBaseAddress(dw_addr_t base_addr); + + DWARFBaseDIE GetUnitDIEOnly() { return DWARFDIE(this, GetUnitDIEPtrOnly()); } + + DWARFDIE DIE() { return DWARFDIE(this, DIEPtr()); } + + DWARFDIE GetDIE(dw_offset_t die_offset); + + DWARFUnit &GetNonSkeletonUnit(); + + static uint8_t GetAddressByteSize(const DWARFUnit *cu); + + static uint8_t GetDefaultAddressSize(); + + void *GetUserData() const; + + void SetUserData(void *d); + + bool Supports_DW_AT_APPLE_objc_complete_type(); + + bool DW_AT_decl_file_attributes_are_invalid(); + + bool Supports_unnamed_objc_bitfields(); + + SymbolFileDWARF &GetSymbolFileDWARF() const { return m_dwarf; } + + DWARFProducer GetProducer(); + + uint32_t GetProducerVersionMajor(); + + uint32_t GetProducerVersionMinor(); + + uint32_t GetProducerVersionUpdate(); + + static lldb::LanguageType LanguageTypeFromDWARF(uint64_t val); + + lldb::LanguageType GetLanguageType(); + + bool GetIsOptimized(); + + const lldb_private::FileSpec &GetCompilationDirectory(); + const lldb_private::FileSpec &GetAbsolutePath(); + lldb_private::FileSpec GetFile(size_t file_idx); + lldb_private::FileSpec::Style GetPathStyle(); + + SymbolFileDWARFDwo *GetDwoSymbolFile() const; + + die_iterator_range dies() { + ExtractDIEsIfNeeded(); + return die_iterator_range(m_die_array.begin(), m_die_array.end()); + } + + DIERef::Section GetDebugSection() const { return m_section; } + + uint8_t GetUnitType() const { return m_header.GetUnitType(); } + bool IsTypeUnit() const { return m_header.IsTypeUnit(); } + + /// Return a list of address ranges resulting from a (possibly encoded) + /// range list starting at a given offset in the appropriate ranges section. + llvm::Expected<DWARFRangeList> FindRnglistFromOffset(dw_offset_t offset) const; + + /// Return a list of address ranges retrieved from an encoded range + /// list whose offset is found via a table lookup given an index (DWARF v5 + /// and later). + llvm::Expected<DWARFRangeList> FindRnglistFromIndex(uint32_t index) const; + +protected: + DWARFUnit(SymbolFileDWARF &dwarf, lldb::user_id_t uid, + const DWARFUnitHeader &header, + const DWARFAbbreviationDeclarationSet &abbrevs, + DIERef::Section section); + + llvm::Error ExtractHeader(SymbolFileDWARF &dwarf, + const lldb_private::DWARFDataExtractor &data, + lldb::offset_t *offset_ptr); + + // Get the DWARF unit DWARF debug information entry. Parse the single DIE + // if needed. + const DWARFDebugInfoEntry *GetUnitDIEPtrOnly() { + ExtractUnitDIEIfNeeded(); + // m_first_die_mutex is not required as m_first_die is never cleared. + if (!m_first_die) + return NULL; + return &m_first_die; + } + + // Get all DWARF debug informration entries. Parse all DIEs if needed. + const DWARFDebugInfoEntry *DIEPtr() { + ExtractDIEsIfNeeded(); + if (m_die_array.empty()) + return NULL; + return &m_die_array[0]; + } + + SymbolFileDWARF &m_dwarf; + std::unique_ptr<SymbolFileDWARFDwo> m_dwo_symbol_file; + DWARFUnitHeader m_header; + const DWARFAbbreviationDeclarationSet *m_abbrevs = nullptr; + void *m_user_data = nullptr; + // The compile unit debug information entry item + DWARFDebugInfoEntry::collection m_die_array; + mutable llvm::sys::RWMutex m_die_array_mutex; + // It is used for tracking of ScopedExtractDIEs instances. + mutable llvm::sys::RWMutex m_die_array_scoped_mutex; + // ScopedExtractDIEs instances should not call ClearDIEsRWLocked() + // as someone called ExtractDIEsIfNeeded(). + std::atomic<bool> m_cancel_scopes; + // GetUnitDIEPtrOnly() needs to return pointer to the first DIE. + // But the first element of m_die_array after ExtractUnitDIEIfNeeded() + // would possibly move in memory after later ExtractDIEsIfNeeded(). + DWARFDebugInfoEntry m_first_die; + llvm::sys::RWMutex m_first_die_mutex; + // A table similar to the .debug_aranges table, but this one points to the + // exact DW_TAG_subprogram DIEs + std::unique_ptr<DWARFDebugAranges> m_func_aranges_up; + dw_addr_t m_base_addr = 0; + DWARFProducer m_producer = eProducerInvalid; + uint32_t m_producer_version_major = 0; + uint32_t m_producer_version_minor = 0; + uint32_t m_producer_version_update = 0; + lldb::LanguageType m_language_type = lldb::eLanguageTypeUnknown; + lldb_private::LazyBool m_is_optimized = lldb_private::eLazyBoolCalculate; + llvm::Optional<lldb_private::FileSpec> m_comp_dir; + llvm::Optional<lldb_private::FileSpec> m_file_spec; + dw_addr_t m_addr_base = 0; // Value of DW_AT_addr_base + dw_addr_t m_ranges_base = 0; // Value of DW_AT_ranges_base + + /// Value of DW_AT_stmt_list. + dw_offset_t m_line_table_offset = DW_INVALID_OFFSET; + + dw_offset_t m_str_offsets_base = 0; // Value of DW_AT_str_offsets_base. + const DIERef::Section m_section; + +private: + void ParseProducerInfo(); + void ExtractDIEsRWLocked(); + void ClearDIEsRWLocked(); + + void AddUnitDIE(const DWARFDebugInfoEntry &cu_die); + + void ComputeCompDirAndGuessPathStyle(); + void ComputeAbsolutePath(); + + DISALLOW_COPY_AND_ASSIGN(DWARFUnit); +}; + +#endif // SymbolFileDWARF_DWARFUnit_h_ diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.cpp new file mode 100644 index 000000000000..9746ad76c930 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.cpp @@ -0,0 +1,272 @@ +//===-- DebugNamesDWARFIndex.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 "Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.h" +#include "Plugins/SymbolFile/DWARF/DWARFDebugInfo.h" +#include "Plugins/SymbolFile/DWARF/DWARFDeclContext.h" +#include "Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.h" +#include "lldb/Utility/RegularExpression.h" +#include "lldb/Utility/Stream.h" + +using namespace lldb_private; +using namespace lldb; + +llvm::Expected<std::unique_ptr<DebugNamesDWARFIndex>> +DebugNamesDWARFIndex::Create(Module &module, DWARFDataExtractor debug_names, + DWARFDataExtractor debug_str, + DWARFDebugInfo *debug_info) { + if (!debug_info) { + return llvm::make_error<llvm::StringError>("debug info null", + llvm::inconvertibleErrorCode()); + } + auto index_up = llvm::make_unique<DebugNames>(debug_names.GetAsLLVM(), + debug_str.GetAsLLVM()); + if (llvm::Error E = index_up->extract()) + return std::move(E); + + return std::unique_ptr<DebugNamesDWARFIndex>(new DebugNamesDWARFIndex( + module, std::move(index_up), debug_names, debug_str, *debug_info)); +} + +llvm::DenseSet<dw_offset_t> +DebugNamesDWARFIndex::GetUnits(const DebugNames &debug_names) { + llvm::DenseSet<dw_offset_t> result; + for (const DebugNames::NameIndex &ni : debug_names) { + for (uint32_t cu = 0; cu < ni.getCUCount(); ++cu) + result.insert(ni.getCUOffset(cu)); + } + return result; +} + +llvm::Optional<DIERef> +DebugNamesDWARFIndex::ToDIERef(const DebugNames::Entry &entry) { + llvm::Optional<uint64_t> cu_offset = entry.getCUOffset(); + if (!cu_offset) + return llvm::None; + + DWARFUnit *cu = m_debug_info.GetUnitAtOffset(DIERef::Section::DebugInfo, *cu_offset); + if (!cu) + return llvm::None; + + // This initializes the DWO symbol file. It's not possible for + // GetDwoSymbolFile to call this automatically because of mutual recursion + // between this and DWARFDebugInfoEntry::GetAttributeValue. + cu->ExtractUnitDIEIfNeeded(); + cu = &cu->GetNonSkeletonUnit(); + + if (llvm::Optional<uint64_t> die_offset = entry.getDIEUnitOffset()) + return DIERef(cu->GetSymbolFileDWARF().GetDwoNum(), + DIERef::Section::DebugInfo, cu->GetOffset() + *die_offset); + + return llvm::None; +} + +void DebugNamesDWARFIndex::Append(const DebugNames::Entry &entry, + DIEArray &offsets) { + if (llvm::Optional<DIERef> ref = ToDIERef(entry)) + offsets.push_back(*ref); +} + +void DebugNamesDWARFIndex::MaybeLogLookupError(llvm::Error error, + const DebugNames::NameIndex &ni, + llvm::StringRef name) { + // Ignore SentinelErrors, log everything else. + LLDB_LOG_ERROR( + LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS), + handleErrors(std::move(error), [](const DebugNames::SentinelError &) {}), + "Failed to parse index entries for index at {1:x}, name {2}: {0}", + ni.getUnitOffset(), name); +} + +void DebugNamesDWARFIndex::GetGlobalVariables(ConstString basename, + DIEArray &offsets) { + m_fallback.GetGlobalVariables(basename, offsets); + + for (const DebugNames::Entry &entry : + m_debug_names_up->equal_range(basename.GetStringRef())) { + if (entry.tag() != DW_TAG_variable) + continue; + + Append(entry, offsets); + } +} + +void DebugNamesDWARFIndex::GetGlobalVariables(const RegularExpression ®ex, + DIEArray &offsets) { + m_fallback.GetGlobalVariables(regex, offsets); + + for (const DebugNames::NameIndex &ni: *m_debug_names_up) { + for (DebugNames::NameTableEntry nte: ni) { + if (!regex.Execute(nte.getString())) + continue; + + uint32_t entry_offset = nte.getEntryOffset(); + llvm::Expected<DebugNames::Entry> entry_or = ni.getEntry(&entry_offset); + for (; entry_or; entry_or = ni.getEntry(&entry_offset)) { + if (entry_or->tag() != DW_TAG_variable) + continue; + + Append(*entry_or, offsets); + } + MaybeLogLookupError(entry_or.takeError(), ni, nte.getString()); + } + } +} + +void DebugNamesDWARFIndex::GetGlobalVariables(const DWARFUnit &cu, + DIEArray &offsets) { + m_fallback.GetGlobalVariables(cu, offsets); + + uint64_t cu_offset = cu.GetOffset(); + for (const DebugNames::NameIndex &ni: *m_debug_names_up) { + for (DebugNames::NameTableEntry nte: ni) { + uint32_t entry_offset = nte.getEntryOffset(); + llvm::Expected<DebugNames::Entry> entry_or = ni.getEntry(&entry_offset); + for (; entry_or; entry_or = ni.getEntry(&entry_offset)) { + if (entry_or->tag() != DW_TAG_variable) + continue; + if (entry_or->getCUOffset() != cu_offset) + continue; + + Append(*entry_or, offsets); + } + MaybeLogLookupError(entry_or.takeError(), ni, nte.getString()); + } + } +} + +void DebugNamesDWARFIndex::GetCompleteObjCClass(ConstString class_name, + bool must_be_implementation, + DIEArray &offsets) { + m_fallback.GetCompleteObjCClass(class_name, must_be_implementation, offsets); + + // Keep a list of incomplete types as fallback for when we don't find the + // complete type. + DIEArray incomplete_types; + + for (const DebugNames::Entry &entry : + m_debug_names_up->equal_range(class_name.GetStringRef())) { + if (entry.tag() != DW_TAG_structure_type && + entry.tag() != DW_TAG_class_type) + continue; + + llvm::Optional<DIERef> ref = ToDIERef(entry); + if (!ref) + continue; + + DWARFUnit *cu = m_debug_info.GetUnit(*ref); + if (!cu || !cu->Supports_DW_AT_APPLE_objc_complete_type()) { + incomplete_types.push_back(*ref); + continue; + } + + // FIXME: We should return DWARFDIEs so we don't have to resolve it twice. + DWARFDIE die = m_debug_info.GetDIE(*ref); + if (!die) + continue; + + if (die.GetAttributeValueAsUnsigned(DW_AT_APPLE_objc_complete_type, 0)) { + // If we find the complete version we're done. + offsets.push_back(*ref); + return; + } else { + incomplete_types.push_back(*ref); + } + } + + offsets.insert(offsets.end(), incomplete_types.begin(), + incomplete_types.end()); +} + +void DebugNamesDWARFIndex::GetTypes(ConstString name, DIEArray &offsets) { + m_fallback.GetTypes(name, offsets); + + for (const DebugNames::Entry &entry : + m_debug_names_up->equal_range(name.GetStringRef())) { + if (isType(entry.tag())) + Append(entry, offsets); + } +} + +void DebugNamesDWARFIndex::GetTypes(const DWARFDeclContext &context, + DIEArray &offsets) { + m_fallback.GetTypes(context, offsets); + + for (const DebugNames::Entry &entry : + m_debug_names_up->equal_range(context[0].name)) { + if (entry.tag() == context[0].tag) + Append(entry, offsets); + } +} + +void DebugNamesDWARFIndex::GetNamespaces(ConstString name, DIEArray &offsets) { + m_fallback.GetNamespaces(name, offsets); + + for (const DebugNames::Entry &entry : + m_debug_names_up->equal_range(name.GetStringRef())) { + if (entry.tag() == DW_TAG_namespace) + Append(entry, offsets); + } +} + +void DebugNamesDWARFIndex::GetFunctions( + ConstString name, SymbolFileDWARF &dwarf, + const CompilerDeclContext &parent_decl_ctx, uint32_t name_type_mask, + std::vector<DWARFDIE> &dies) { + + std::vector<DWARFDIE> v; + m_fallback.GetFunctions(name, dwarf, parent_decl_ctx, name_type_mask, v); + + for (const DebugNames::Entry &entry : + m_debug_names_up->equal_range(name.GetStringRef())) { + Tag tag = entry.tag(); + if (tag != DW_TAG_subprogram && tag != DW_TAG_inlined_subroutine) + continue; + + if (llvm::Optional<DIERef> ref = ToDIERef(entry)) + ProcessFunctionDIE(name.GetStringRef(), *ref, dwarf, parent_decl_ctx, + name_type_mask, v); + } + + std::set<DWARFDebugInfoEntry *> seen; + for (DWARFDIE die : v) + if (seen.insert(die.GetDIE()).second) + dies.push_back(die); +} + +void DebugNamesDWARFIndex::GetFunctions(const RegularExpression ®ex, + DIEArray &offsets) { + m_fallback.GetFunctions(regex, offsets); + + for (const DebugNames::NameIndex &ni: *m_debug_names_up) { + for (DebugNames::NameTableEntry nte: ni) { + if (!regex.Execute(nte.getString())) + continue; + + uint32_t entry_offset = nte.getEntryOffset(); + llvm::Expected<DebugNames::Entry> entry_or = ni.getEntry(&entry_offset); + for (; entry_or; entry_or = ni.getEntry(&entry_offset)) { + Tag tag = entry_or->tag(); + if (tag != DW_TAG_subprogram && tag != DW_TAG_inlined_subroutine) + continue; + + Append(*entry_or, offsets); + } + MaybeLogLookupError(entry_or.takeError(), ni, nte.getString()); + } + } +} + +void DebugNamesDWARFIndex::Dump(Stream &s) { + m_fallback.Dump(s); + + std::string data; + llvm::raw_string_ostream os(data); + m_debug_names_up->dump(os); + s.PutCString(os.str()); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.h new file mode 100644 index 000000000000..dca25496373f --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.h @@ -0,0 +1,81 @@ +//===-- DebugNamesDWARFIndex.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 LLDB_DEBUGNAMESDWARFINDEX_H +#define LLDB_DEBUGNAMESDWARFINDEX_H + +#include "Plugins/SymbolFile/DWARF/DWARFIndex.h" +#include "Plugins/SymbolFile/DWARF/LogChannelDWARF.h" +#include "Plugins/SymbolFile/DWARF/ManualDWARFIndex.h" +#include "lldb/Utility/ConstString.h" +#include "llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h" + +namespace lldb_private { +class DebugNamesDWARFIndex : public DWARFIndex { +public: + static llvm::Expected<std::unique_ptr<DebugNamesDWARFIndex>> + Create(Module &module, DWARFDataExtractor debug_names, + DWARFDataExtractor debug_str, DWARFDebugInfo *debug_info); + + void Preload() override { m_fallback.Preload(); } + + void GetGlobalVariables(ConstString basename, DIEArray &offsets) override; + void GetGlobalVariables(const RegularExpression ®ex, + DIEArray &offsets) override; + void GetGlobalVariables(const DWARFUnit &cu, DIEArray &offsets) override; + void GetObjCMethods(ConstString class_name, DIEArray &offsets) override {} + void GetCompleteObjCClass(ConstString class_name, bool must_be_implementation, + DIEArray &offsets) override; + void GetTypes(ConstString name, DIEArray &offsets) override; + void GetTypes(const DWARFDeclContext &context, DIEArray &offsets) override; + void GetNamespaces(ConstString name, DIEArray &offsets) override; + void GetFunctions(ConstString name, SymbolFileDWARF &dwarf, + const CompilerDeclContext &parent_decl_ctx, + uint32_t name_type_mask, + std::vector<DWARFDIE> &dies) override; + void GetFunctions(const RegularExpression ®ex, + DIEArray &offsets) override; + + void ReportInvalidDIERef(const DIERef &ref, llvm::StringRef name) override {} + void Dump(Stream &s) override; + +private: + DebugNamesDWARFIndex(Module &module, + std::unique_ptr<llvm::DWARFDebugNames> debug_names_up, + DWARFDataExtractor debug_names_data, + DWARFDataExtractor debug_str_data, + DWARFDebugInfo &debug_info) + : DWARFIndex(module), m_debug_info(debug_info), + m_debug_names_data(debug_names_data), m_debug_str_data(debug_str_data), + m_debug_names_up(std::move(debug_names_up)), + m_fallback(module, &debug_info, GetUnits(*m_debug_names_up)) {} + + DWARFDebugInfo &m_debug_info; + + // LLVM DWARFDebugNames will hold a non-owning reference to this data, so keep + // track of the ownership here. + DWARFDataExtractor m_debug_names_data; + DWARFDataExtractor m_debug_str_data; + + using DebugNames = llvm::DWARFDebugNames; + std::unique_ptr<DebugNames> m_debug_names_up; + ManualDWARFIndex m_fallback; + + llvm::Optional<DIERef> ToDIERef(const DebugNames::Entry &entry); + void Append(const DebugNames::Entry &entry, DIEArray &offsets); + + static void MaybeLogLookupError(llvm::Error error, + const DebugNames::NameIndex &ni, + llvm::StringRef name); + + static llvm::DenseSet<dw_offset_t> GetUnits(const DebugNames &debug_names); +}; + +} // namespace lldb_private + +#endif // LLDB_DEBUGNAMESDWARFINDEX_H diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/HashedNameToDIE.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/HashedNameToDIE.cpp new file mode 100644 index 000000000000..62b0ad37a9fc --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/HashedNameToDIE.cpp @@ -0,0 +1,583 @@ +//===-- HashedNameToDIE.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 "HashedNameToDIE.h" +#include "llvm/ADT/StringRef.h" + +void DWARFMappedHash::ExtractDIEArray(const DIEInfoArray &die_info_array, + DIEArray &die_offsets) { + const size_t count = die_info_array.size(); + for (size_t i = 0; i < count; ++i) + die_offsets.emplace_back(die_info_array[i]); +} + +void DWARFMappedHash::ExtractDIEArray(const DIEInfoArray &die_info_array, + const dw_tag_t tag, + DIEArray &die_offsets) { + if (tag == 0) { + ExtractDIEArray(die_info_array, die_offsets); + } else { + const size_t count = die_info_array.size(); + for (size_t i = 0; i < count; ++i) { + const dw_tag_t die_tag = die_info_array[i].tag; + bool tag_matches = die_tag == 0 || tag == die_tag; + if (!tag_matches) { + if (die_tag == DW_TAG_class_type || die_tag == DW_TAG_structure_type) + tag_matches = + tag == DW_TAG_structure_type || tag == DW_TAG_class_type; + } + if (tag_matches) + die_offsets.emplace_back(die_info_array[i]); + } + } +} + +void DWARFMappedHash::ExtractDIEArray(const DIEInfoArray &die_info_array, + const dw_tag_t tag, + const uint32_t qualified_name_hash, + DIEArray &die_offsets) { + if (tag == 0) { + ExtractDIEArray(die_info_array, die_offsets); + } else { + const size_t count = die_info_array.size(); + for (size_t i = 0; i < count; ++i) { + if (qualified_name_hash != die_info_array[i].qualified_name_hash) + continue; + const dw_tag_t die_tag = die_info_array[i].tag; + bool tag_matches = die_tag == 0 || tag == die_tag; + if (!tag_matches) { + if (die_tag == DW_TAG_class_type || die_tag == DW_TAG_structure_type) + tag_matches = + tag == DW_TAG_structure_type || tag == DW_TAG_class_type; + } + if (tag_matches) + die_offsets.emplace_back(die_info_array[i]); + } + } +} + +void DWARFMappedHash::ExtractClassOrStructDIEArray( + const DIEInfoArray &die_info_array, + bool return_implementation_only_if_available, DIEArray &die_offsets) { + const size_t count = die_info_array.size(); + for (size_t i = 0; i < count; ++i) { + const dw_tag_t die_tag = die_info_array[i].tag; + if (die_tag == 0 || die_tag == DW_TAG_class_type || + die_tag == DW_TAG_structure_type) { + if (die_info_array[i].type_flags & eTypeFlagClassIsImplementation) { + if (return_implementation_only_if_available) { + // We found the one true definition for this class, so only return + // that + die_offsets.clear(); + die_offsets.emplace_back(die_info_array[i]); + return; + } else { + // Put the one true definition as the first entry so it matches first + die_offsets.emplace(die_offsets.begin(), die_info_array[i]); + } + } else { + die_offsets.emplace_back(die_info_array[i]); + } + } + } +} + +void DWARFMappedHash::ExtractTypesFromDIEArray( + const DIEInfoArray &die_info_array, uint32_t type_flag_mask, + uint32_t type_flag_value, DIEArray &die_offsets) { + const size_t count = die_info_array.size(); + for (size_t i = 0; i < count; ++i) { + if ((die_info_array[i].type_flags & type_flag_mask) == type_flag_value) + die_offsets.emplace_back(die_info_array[i]); + } +} + +const char *DWARFMappedHash::GetAtomTypeName(uint16_t atom) { + switch (atom) { + case eAtomTypeNULL: + return "NULL"; + case eAtomTypeDIEOffset: + return "die-offset"; + case eAtomTypeCUOffset: + return "cu-offset"; + case eAtomTypeTag: + return "die-tag"; + case eAtomTypeNameFlags: + return "name-flags"; + case eAtomTypeTypeFlags: + return "type-flags"; + case eAtomTypeQualNameHash: + return "qualified-name-hash"; + } + return "<invalid>"; +} + +DWARFMappedHash::DIEInfo::DIEInfo(dw_offset_t o, dw_tag_t t, uint32_t f, + uint32_t h) + : die_offset(o), tag(t), type_flags(f), qualified_name_hash(h) {} + +DWARFMappedHash::Prologue::Prologue(dw_offset_t _die_base_offset) + : die_base_offset(_die_base_offset), atoms(), atom_mask(0), + min_hash_data_byte_size(0), hash_data_has_fixed_byte_size(true) { + // Define an array of DIE offsets by first defining an array, and then define + // the atom type for the array, in this case we have an array of DIE offsets + AppendAtom(eAtomTypeDIEOffset, DW_FORM_data4); +} + +void DWARFMappedHash::Prologue::ClearAtoms() { + hash_data_has_fixed_byte_size = true; + min_hash_data_byte_size = 0; + atom_mask = 0; + atoms.clear(); +} + +bool DWARFMappedHash::Prologue::ContainsAtom(AtomType atom_type) const { + return (atom_mask & (1u << atom_type)) != 0; +} + +void DWARFMappedHash::Prologue::Clear() { + die_base_offset = 0; + ClearAtoms(); +} + +void DWARFMappedHash::Prologue::AppendAtom(AtomType type, dw_form_t form) { + atoms.push_back({type, form}); + atom_mask |= 1u << type; + switch (form) { + case DW_FORM_indirect: + case DW_FORM_exprloc: + case DW_FORM_flag_present: + case DW_FORM_ref_sig8: + llvm_unreachable("Unhandled atom form"); + + case DW_FORM_addrx: + case DW_FORM_string: + case DW_FORM_block: + case DW_FORM_block1: + case DW_FORM_sdata: + case DW_FORM_udata: + case DW_FORM_ref_udata: + case DW_FORM_GNU_addr_index: + case DW_FORM_GNU_str_index: + hash_data_has_fixed_byte_size = false; + LLVM_FALLTHROUGH; + case DW_FORM_flag: + case DW_FORM_data1: + case DW_FORM_ref1: + case DW_FORM_sec_offset: + min_hash_data_byte_size += 1; + break; + + case DW_FORM_block2: + hash_data_has_fixed_byte_size = false; + LLVM_FALLTHROUGH; + case DW_FORM_data2: + case DW_FORM_ref2: + min_hash_data_byte_size += 2; + break; + + case DW_FORM_block4: + hash_data_has_fixed_byte_size = false; + LLVM_FALLTHROUGH; + case DW_FORM_data4: + case DW_FORM_ref4: + case DW_FORM_addr: + case DW_FORM_ref_addr: + case DW_FORM_strp: + min_hash_data_byte_size += 4; + break; + + case DW_FORM_data8: + case DW_FORM_ref8: + min_hash_data_byte_size += 8; + break; + } +} + +lldb::offset_t +DWARFMappedHash::Prologue::Read(const lldb_private::DataExtractor &data, + lldb::offset_t offset) { + ClearAtoms(); + + die_base_offset = data.GetU32(&offset); + + const uint32_t atom_count = data.GetU32(&offset); + if (atom_count == 0x00060003u) { + // Old format, deal with contents of old pre-release format + while (data.GetU32(&offset)) + /* do nothing */; + + // Hardcode to the only known value for now. + AppendAtom(eAtomTypeDIEOffset, DW_FORM_data4); + } else { + for (uint32_t i = 0; i < atom_count; ++i) { + AtomType type = (AtomType)data.GetU16(&offset); + dw_form_t form = (dw_form_t)data.GetU16(&offset); + AppendAtom(type, form); + } + } + return offset; +} + +size_t DWARFMappedHash::Prologue::GetByteSize() const { + // Add an extra count to the atoms size for the zero termination Atom that + // gets written to disk + return sizeof(die_base_offset) + sizeof(uint32_t) + + atoms.size() * sizeof(Atom); +} + +size_t DWARFMappedHash::Prologue::GetMinimumHashDataByteSize() const { + return min_hash_data_byte_size; +} + +bool DWARFMappedHash::Prologue::HashDataHasFixedByteSize() const { + return hash_data_has_fixed_byte_size; +} + +size_t DWARFMappedHash::Header::GetByteSize(const HeaderData &header_data) { + return header_data.GetByteSize(); +} + +lldb::offset_t DWARFMappedHash::Header::Read(lldb_private::DataExtractor &data, + lldb::offset_t offset) { + offset = MappedHash::Header<Prologue>::Read(data, offset); + if (offset != UINT32_MAX) { + offset = header_data.Read(data, offset); + } + return offset; +} + +bool DWARFMappedHash::Header::Read(const lldb_private::DWARFDataExtractor &data, + lldb::offset_t *offset_ptr, + DIEInfo &hash_data) const { + const size_t num_atoms = header_data.atoms.size(); + if (num_atoms == 0) + return false; + + for (size_t i = 0; i < num_atoms; ++i) { + DWARFFormValue form_value(nullptr, header_data.atoms[i].form); + + if (!form_value.ExtractValue(data, offset_ptr)) + return false; + + switch (header_data.atoms[i].type) { + case eAtomTypeDIEOffset: // DIE offset, check form for encoding + hash_data.die_offset = + DWARFFormValue::IsDataForm(form_value.Form()) + ? form_value.Unsigned() + : form_value.Reference(header_data.die_base_offset); + break; + + case eAtomTypeTag: // DW_TAG value for the DIE + hash_data.tag = (dw_tag_t)form_value.Unsigned(); + break; + + case eAtomTypeTypeFlags: // Flags from enum TypeFlags + hash_data.type_flags = (uint32_t)form_value.Unsigned(); + break; + + case eAtomTypeQualNameHash: // Flags from enum TypeFlags + hash_data.qualified_name_hash = form_value.Unsigned(); + break; + + default: + // We can always skip atoms we don't know about + break; + } + } + return hash_data.die_offset != DW_INVALID_OFFSET; +} + +DWARFMappedHash::MemoryTable::MemoryTable( + lldb_private::DWARFDataExtractor &table_data, + const lldb_private::DWARFDataExtractor &string_table, const char *name) + : MappedHash::MemoryTable<uint32_t, Header, DIEInfoArray>(table_data), + m_data(table_data), m_string_table(string_table), m_name(name) {} + +const char * +DWARFMappedHash::MemoryTable::GetStringForKeyType(KeyType key) const { + // The key in the DWARF table is the .debug_str offset for the string + return m_string_table.PeekCStr(key); +} + +bool DWARFMappedHash::MemoryTable::ReadHashData(uint32_t hash_data_offset, + HashData &hash_data) const { + lldb::offset_t offset = hash_data_offset; + offset += 4; // Skip string table offset that contains offset of hash name in + // .debug_str + const uint32_t count = m_data.GetU32(&offset); + if (count > 0) { + hash_data.resize(count); + for (uint32_t i = 0; i < count; ++i) { + if (!m_header.Read(m_data, &offset, hash_data[i])) + return false; + } + } else + hash_data.clear(); + return true; +} + +DWARFMappedHash::MemoryTable::Result +DWARFMappedHash::MemoryTable::GetHashDataForName( + llvm::StringRef name, lldb::offset_t *hash_data_offset_ptr, + Pair &pair) const { + pair.key = m_data.GetU32(hash_data_offset_ptr); + pair.value.clear(); + + // If the key is zero, this terminates our chain of HashData objects for this + // hash value. + if (pair.key == 0) + return eResultEndOfHashData; + + // There definitely should be a string for this string offset, if there + // isn't, there is something wrong, return and error + const char *strp_cstr = m_string_table.PeekCStr(pair.key); + if (strp_cstr == nullptr) { + *hash_data_offset_ptr = UINT32_MAX; + return eResultError; + } + + const uint32_t count = m_data.GetU32(hash_data_offset_ptr); + const size_t min_total_hash_data_size = + count * m_header.header_data.GetMinimumHashDataByteSize(); + if (count > 0 && + m_data.ValidOffsetForDataOfSize(*hash_data_offset_ptr, + min_total_hash_data_size)) { + // We have at least one HashData entry, and we have enough data to parse at + // least "count" HashData entries. + + // First make sure the entire C string matches... + const bool match = name == strp_cstr; + + if (!match && m_header.header_data.HashDataHasFixedByteSize()) { + // If the string doesn't match and we have fixed size data, we can just + // add the total byte size of all HashData objects to the hash data + // offset and be done... + *hash_data_offset_ptr += min_total_hash_data_size; + } else { + // If the string does match, or we don't have fixed size data then we + // need to read the hash data as a stream. If the string matches we also + // append all HashData objects to the value array. + for (uint32_t i = 0; i < count; ++i) { + DIEInfo die_info; + if (m_header.Read(m_data, hash_data_offset_ptr, die_info)) { + // Only happened if the HashData of the string matched... + if (match) + pair.value.push_back(die_info); + } else { + // Something went wrong while reading the data + *hash_data_offset_ptr = UINT32_MAX; + return eResultError; + } + } + } + // Return the correct response depending on if the string matched or not... + if (match) + return eResultKeyMatch; // The key (cstring) matches and we have lookup + // results! + else + return eResultKeyMismatch; // The key doesn't match, this function will + // get called + // again for the next key/value or the key terminator which in our case is + // a zero .debug_str offset. + } else { + *hash_data_offset_ptr = UINT32_MAX; + return eResultError; + } +} + +DWARFMappedHash::MemoryTable::Result +DWARFMappedHash::MemoryTable::AppendHashDataForRegularExpression( + const lldb_private::RegularExpression ®ex, + lldb::offset_t *hash_data_offset_ptr, Pair &pair) const { + pair.key = m_data.GetU32(hash_data_offset_ptr); + // If the key is zero, this terminates our chain of HashData objects for this + // hash value. + if (pair.key == 0) + return eResultEndOfHashData; + + // There definitely should be a string for this string offset, if there + // isn't, there is something wrong, return and error + const char *strp_cstr = m_string_table.PeekCStr(pair.key); + if (strp_cstr == nullptr) + return eResultError; + + const uint32_t count = m_data.GetU32(hash_data_offset_ptr); + const size_t min_total_hash_data_size = + count * m_header.header_data.GetMinimumHashDataByteSize(); + if (count > 0 && + m_data.ValidOffsetForDataOfSize(*hash_data_offset_ptr, + min_total_hash_data_size)) { + const bool match = regex.Execute(llvm::StringRef(strp_cstr)); + + if (!match && m_header.header_data.HashDataHasFixedByteSize()) { + // If the regex doesn't match and we have fixed size data, we can just + // add the total byte size of all HashData objects to the hash data + // offset and be done... + *hash_data_offset_ptr += min_total_hash_data_size; + } else { + // If the string does match, or we don't have fixed size data then we + // need to read the hash data as a stream. If the string matches we also + // append all HashData objects to the value array. + for (uint32_t i = 0; i < count; ++i) { + DIEInfo die_info; + if (m_header.Read(m_data, hash_data_offset_ptr, die_info)) { + // Only happened if the HashData of the string matched... + if (match) + pair.value.push_back(die_info); + } else { + // Something went wrong while reading the data + *hash_data_offset_ptr = UINT32_MAX; + return eResultError; + } + } + } + // Return the correct response depending on if the string matched or not... + if (match) + return eResultKeyMatch; // The key (cstring) matches and we have lookup + // results! + else + return eResultKeyMismatch; // The key doesn't match, this function will + // get called + // again for the next key/value or the key terminator which in our case is + // a zero .debug_str offset. + } else { + *hash_data_offset_ptr = UINT32_MAX; + return eResultError; + } +} + +size_t DWARFMappedHash::MemoryTable::AppendAllDIEsThatMatchingRegex( + const lldb_private::RegularExpression ®ex, + DIEInfoArray &die_info_array) const { + const uint32_t hash_count = m_header.hashes_count; + Pair pair; + for (uint32_t offset_idx = 0; offset_idx < hash_count; ++offset_idx) { + lldb::offset_t hash_data_offset = GetHashDataOffset(offset_idx); + while (hash_data_offset != UINT32_MAX) { + const lldb::offset_t prev_hash_data_offset = hash_data_offset; + Result hash_result = + AppendHashDataForRegularExpression(regex, &hash_data_offset, pair); + if (prev_hash_data_offset == hash_data_offset) + break; + + // Check the result of getting our hash data + switch (hash_result) { + case eResultKeyMatch: + case eResultKeyMismatch: + // Whether we matches or not, it doesn't matter, we keep looking. + break; + + case eResultEndOfHashData: + case eResultError: + hash_data_offset = UINT32_MAX; + break; + } + } + } + die_info_array.swap(pair.value); + return die_info_array.size(); +} + +size_t DWARFMappedHash::MemoryTable::AppendAllDIEsInRange( + const uint32_t die_offset_start, const uint32_t die_offset_end, + DIEInfoArray &die_info_array) const { + const uint32_t hash_count = m_header.hashes_count; + for (uint32_t offset_idx = 0; offset_idx < hash_count; ++offset_idx) { + bool done = false; + lldb::offset_t hash_data_offset = GetHashDataOffset(offset_idx); + while (!done && hash_data_offset != UINT32_MAX) { + KeyType key = m_data.GetU32(&hash_data_offset); + // If the key is zero, this terminates our chain of HashData objects for + // this hash value. + if (key == 0) + break; + + const uint32_t count = m_data.GetU32(&hash_data_offset); + for (uint32_t i = 0; i < count; ++i) { + DIEInfo die_info; + if (m_header.Read(m_data, &hash_data_offset, die_info)) { + if (die_info.die_offset == 0) + done = true; + if (die_offset_start <= die_info.die_offset && + die_info.die_offset < die_offset_end) + die_info_array.push_back(die_info); + } + } + } + } + return die_info_array.size(); +} + +size_t DWARFMappedHash::MemoryTable::FindByName(llvm::StringRef name, + DIEArray &die_offsets) { + if (name.empty()) + return 0; + + DIEInfoArray die_info_array; + if (FindByName(name, die_info_array)) + DWARFMappedHash::ExtractDIEArray(die_info_array, die_offsets); + return die_info_array.size(); +} + +size_t DWARFMappedHash::MemoryTable::FindByNameAndTag(llvm::StringRef name, + const dw_tag_t tag, + DIEArray &die_offsets) { + DIEInfoArray die_info_array; + if (FindByName(name, die_info_array)) + DWARFMappedHash::ExtractDIEArray(die_info_array, tag, die_offsets); + return die_info_array.size(); +} + +size_t DWARFMappedHash::MemoryTable::FindByNameAndTagAndQualifiedNameHash( + llvm::StringRef name, const dw_tag_t tag, + const uint32_t qualified_name_hash, DIEArray &die_offsets) { + DIEInfoArray die_info_array; + if (FindByName(name, die_info_array)) + DWARFMappedHash::ExtractDIEArray(die_info_array, tag, qualified_name_hash, + die_offsets); + return die_info_array.size(); +} + +size_t DWARFMappedHash::MemoryTable::FindCompleteObjCClassByName( + llvm::StringRef name, DIEArray &die_offsets, bool must_be_implementation) { + DIEInfoArray die_info_array; + if (FindByName(name, die_info_array)) { + if (must_be_implementation && + GetHeader().header_data.ContainsAtom(eAtomTypeTypeFlags)) { + // If we have two atoms, then we have the DIE offset and the type flags + // so we can find the objective C class efficiently. + DWARFMappedHash::ExtractTypesFromDIEArray(die_info_array, UINT32_MAX, + eTypeFlagClassIsImplementation, + die_offsets); + } else { + // We don't only want the one true definition, so try and see what we can + // find, and only return class or struct DIEs. If we do have the full + // implementation, then return it alone, else return all possible + // matches. + const bool return_implementation_only_if_available = true; + DWARFMappedHash::ExtractClassOrStructDIEArray( + die_info_array, return_implementation_only_if_available, die_offsets); + } + } + return die_offsets.size(); +} + +size_t DWARFMappedHash::MemoryTable::FindByName(llvm::StringRef name, + DIEInfoArray &die_info_array) { + if (name.empty()) + return 0; + + Pair kv_pair; + size_t old_size = die_info_array.size(); + if (Find(name, kv_pair)) { + die_info_array.swap(kv_pair.value); + return die_info_array.size() - old_size; + } + return 0; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/HashedNameToDIE.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/HashedNameToDIE.h new file mode 100644 index 000000000000..a01612b59528 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/HashedNameToDIE.h @@ -0,0 +1,193 @@ +//===-- HashedNameToDIE.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 SymbolFileDWARF_HashedNameToDIE_h_ +#define SymbolFileDWARF_HashedNameToDIE_h_ + +#include <vector> + +#include "lldb/Core/MappedHash.h" +#include "lldb/Core/dwarf.h" +#include "lldb/Utility/RegularExpression.h" +#include "lldb/lldb-defines.h" + +#include "DWARFDefines.h" +#include "DWARFFormValue.h" +#include "NameToDIE.h" + +class DWARFMappedHash { +public: + enum AtomType : uint16_t { + eAtomTypeNULL = 0u, + eAtomTypeDIEOffset = 1u, // DIE offset, check form for encoding + eAtomTypeCUOffset = 2u, // DIE offset of the compiler unit header that + // contains the item in question + eAtomTypeTag = 3u, // DW_TAG_xxx value, should be encoded as DW_FORM_data1 + // (if no tags exceed 255) or DW_FORM_data2 + eAtomTypeNameFlags = 4u, // Flags from enum NameFlags + eAtomTypeTypeFlags = 5u, // Flags from enum TypeFlags, + eAtomTypeQualNameHash = 6u // A 32 bit hash of the full qualified name + // (since all hash entries are basename only) + // For example a type like "std::vector<int>::iterator" would have a name of + // "iterator" + // and a 32 bit hash for "std::vector<int>::iterator" to allow us to not + // have to pull + // in debug info for a type when we know the fully qualified name. + }; + + // Bit definitions for the eAtomTypeTypeFlags flags + enum TypeFlags { + // Always set for C++, only set for ObjC if this is the + // @implementation for class + eTypeFlagClassIsImplementation = (1u << 1) + }; + + struct DIEInfo { + dw_offset_t die_offset = DW_INVALID_OFFSET; + dw_tag_t tag = 0; + + /// Any flags for this DIEInfo + uint32_t type_flags = 0; + + /// A 32 bit hash of the fully qualified name + uint32_t qualified_name_hash = 0; + + DIEInfo() = default; + DIEInfo(dw_offset_t o, dw_tag_t t, uint32_t f, uint32_t h); + + explicit operator DIERef() const { + return DIERef(llvm::None, DIERef::Section::DebugInfo, die_offset); + } + }; + + struct Atom { + AtomType type; + dw_form_t form; + }; + + typedef std::vector<DIEInfo> DIEInfoArray; + typedef std::vector<Atom> AtomArray; + + class Prologue { + public: + Prologue(dw_offset_t _die_base_offset = 0); + + void ClearAtoms(); + + bool ContainsAtom(AtomType atom_type) const; + + void Clear(); + + void AppendAtom(AtomType type, dw_form_t form); + + lldb::offset_t Read(const lldb_private::DataExtractor &data, + lldb::offset_t offset); + + size_t GetByteSize() const; + + size_t GetMinimumHashDataByteSize() const; + + bool HashDataHasFixedByteSize() const; + + // DIE offset base so die offsets in hash_data can be CU relative + dw_offset_t die_base_offset; + AtomArray atoms; + uint32_t atom_mask; + size_t min_hash_data_byte_size; + bool hash_data_has_fixed_byte_size; + }; + + class Header : public MappedHash::Header<Prologue> { + public: + size_t GetByteSize(const HeaderData &header_data) override; + + lldb::offset_t Read(lldb_private::DataExtractor &data, + lldb::offset_t offset) override; + + bool Read(const lldb_private::DWARFDataExtractor &data, + lldb::offset_t *offset_ptr, DIEInfo &hash_data) const; + }; + + // A class for reading and using a saved hash table from a block of data + // in memory + class MemoryTable + : public MappedHash::MemoryTable<uint32_t, DWARFMappedHash::Header, + DIEInfoArray> { + public: + MemoryTable(lldb_private::DWARFDataExtractor &table_data, + const lldb_private::DWARFDataExtractor &string_table, + const char *name); + + const char *GetStringForKeyType(KeyType key) const override; + + bool ReadHashData(uint32_t hash_data_offset, + HashData &hash_data) const override; + + size_t + AppendAllDIEsThatMatchingRegex(const lldb_private::RegularExpression ®ex, + DIEInfoArray &die_info_array) const; + + size_t AppendAllDIEsInRange(const uint32_t die_offset_start, + const uint32_t die_offset_end, + DIEInfoArray &die_info_array) const; + + size_t FindByName(llvm::StringRef name, DIEArray &die_offsets); + + size_t FindByNameAndTag(llvm::StringRef name, const dw_tag_t tag, + DIEArray &die_offsets); + + size_t FindByNameAndTagAndQualifiedNameHash( + llvm::StringRef name, const dw_tag_t tag, + const uint32_t qualified_name_hash, DIEArray &die_offsets); + + size_t FindCompleteObjCClassByName(llvm::StringRef name, + DIEArray &die_offsets, + bool must_be_implementation); + + protected: + Result AppendHashDataForRegularExpression( + const lldb_private::RegularExpression ®ex, + lldb::offset_t *hash_data_offset_ptr, Pair &pair) const; + + size_t FindByName(llvm::StringRef name, DIEInfoArray &die_info_array); + + Result GetHashDataForName(llvm::StringRef name, + lldb::offset_t *hash_data_offset_ptr, + Pair &pair) const override; + + lldb_private::DWARFDataExtractor m_data; + lldb_private::DWARFDataExtractor m_string_table; + std::string m_name; + }; + + static void ExtractDIEArray(const DIEInfoArray &die_info_array, + DIEArray &die_offsets); + +protected: + static void ExtractDIEArray(const DIEInfoArray &die_info_array, + const dw_tag_t tag, DIEArray &die_offsets); + + static void ExtractDIEArray(const DIEInfoArray &die_info_array, + const dw_tag_t tag, + const uint32_t qualified_name_hash, + DIEArray &die_offsets); + + static void + ExtractClassOrStructDIEArray(const DIEInfoArray &die_info_array, + bool return_implementation_only_if_available, + DIEArray &die_offsets); + + static void ExtractTypesFromDIEArray(const DIEInfoArray &die_info_array, + uint32_t type_flag_mask, + uint32_t type_flag_value, + DIEArray &die_offsets); + + static const char *GetAtomTypeName(uint16_t atom); +}; + +#endif // SymbolFileDWARF_HashedNameToDIE_h_ diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/LogChannelDWARF.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/LogChannelDWARF.cpp new file mode 100644 index 000000000000..8495016d4280 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/LogChannelDWARF.cpp @@ -0,0 +1,33 @@ +//===-- LogChannelDWARF.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 "LogChannelDWARF.h" + +using namespace lldb_private; + +static constexpr Log::Category g_categories[] = { + {{"comp"}, + {"log insertions of object files into DWARF debug maps"}, + DWARF_LOG_TYPE_COMPLETION}, + {{"info"}, {"log the parsing of .debug_info"}, DWARF_LOG_DEBUG_INFO}, + {{"line"}, {"log the parsing of .debug_line"}, DWARF_LOG_DEBUG_LINE}, + {{"lookups"}, + {"log any lookups that happen by name, regex, or address"}, + DWARF_LOG_LOOKUPS}, + {{"map"}, + {"log struct/unions/class type completions"}, + DWARF_LOG_DEBUG_MAP}, +}; + +Log::Channel LogChannelDWARF::g_channel(g_categories, DWARF_LOG_DEFAULT); + +void LogChannelDWARF::Initialize() { + Log::Register("dwarf", g_channel); +} + +void LogChannelDWARF::Terminate() { Log::Unregister("dwarf"); } diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/LogChannelDWARF.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/LogChannelDWARF.h new file mode 100644 index 000000000000..a89c686735d2 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/LogChannelDWARF.h @@ -0,0 +1,35 @@ +//===-- LogChannelDWARF.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 SymbolFileDWARF_LogChannelDWARF_h_ +#define SymbolFileDWARF_LogChannelDWARF_h_ + +#include "lldb/Utility/Log.h" + +#define DWARF_LOG_DEBUG_INFO (1u << 1) +#define DWARF_LOG_DEBUG_LINE (1u << 2) +#define DWARF_LOG_LOOKUPS (1u << 3) +#define DWARF_LOG_TYPE_COMPLETION (1u << 4) +#define DWARF_LOG_DEBUG_MAP (1u << 5) +#define DWARF_LOG_ALL (UINT32_MAX) +#define DWARF_LOG_DEFAULT (DWARF_LOG_DEBUG_INFO) + +namespace lldb_private { +class LogChannelDWARF { + static Log::Channel g_channel; + +public: + static void Initialize(); + static void Terminate(); + + static Log *GetLogIfAll(uint32_t mask) { return g_channel.GetLogIfAll(mask); } + static Log *GetLogIfAny(uint32_t mask) { return g_channel.GetLogIfAny(mask); } +}; +} + +#endif // SymbolFileDWARF_LogChannelDWARF_h_ diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp new file mode 100644 index 000000000000..aff8b5d8c15f --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp @@ -0,0 +1,476 @@ +//===-- ManualDWARFIndex.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 "Plugins/SymbolFile/DWARF/ManualDWARFIndex.h" +#include "Plugins/Language/ObjC/ObjCLanguage.h" +#include "Plugins/SymbolFile/DWARF/DWARFDebugInfo.h" +#include "Plugins/SymbolFile/DWARF/DWARFDeclContext.h" +#include "Plugins/SymbolFile/DWARF/LogChannelDWARF.h" +#include "Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.h" +#include "lldb/Core/Module.h" +#include "lldb/Host/TaskPool.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Utility/Stream.h" +#include "lldb/Utility/Timer.h" + +using namespace lldb_private; +using namespace lldb; + +void ManualDWARFIndex::Index() { + if (!m_debug_info) + return; + + DWARFDebugInfo &debug_info = *m_debug_info; + m_debug_info = nullptr; + + static Timer::Category func_cat(LLVM_PRETTY_FUNCTION); + Timer scoped_timer(func_cat, "%p", static_cast<void *>(&debug_info)); + + std::vector<DWARFUnit *> units_to_index; + units_to_index.reserve(debug_info.GetNumUnits()); + for (size_t U = 0; U < debug_info.GetNumUnits(); ++U) { + DWARFUnit *unit = debug_info.GetUnitAtIndex(U); + if (unit && m_units_to_avoid.count(unit->GetOffset()) == 0) + units_to_index.push_back(unit); + } + if (units_to_index.empty()) + return; + + std::vector<IndexSet> sets(units_to_index.size()); + + // Keep memory down by clearing DIEs for any units if indexing + // caused us to load the unit's DIEs. + std::vector<llvm::Optional<DWARFUnit::ScopedExtractDIEs>> clear_cu_dies( + units_to_index.size()); + auto parser_fn = [&](size_t cu_idx) { + IndexUnit(*units_to_index[cu_idx], sets[cu_idx]); + }; + + auto extract_fn = [&units_to_index, &clear_cu_dies](size_t cu_idx) { + clear_cu_dies[cu_idx] = units_to_index[cu_idx]->ExtractDIEsScoped(); + }; + + // Create a task runner that extracts dies for each DWARF unit in a + // separate thread + // First figure out which units didn't have their DIEs already + // parsed and remember this. If no DIEs were parsed prior to this index + // function call, we are going to want to clear the CU dies after we are + // done indexing to make sure we don't pull in all DWARF dies, but we need + // to wait until all units have been indexed in case a DIE in one + // unit refers to another and the indexes accesses those DIEs. + TaskMapOverInt(0, units_to_index.size(), extract_fn); + + // Now create a task runner that can index each DWARF unit in a + // separate thread so we can index quickly. + + TaskMapOverInt(0, units_to_index.size(), parser_fn); + + auto finalize_fn = [this, &sets](NameToDIE(IndexSet::*index)) { + NameToDIE &result = m_set.*index; + for (auto &set : sets) + result.Append(set.*index); + result.Finalize(); + }; + + TaskPool::RunTasks([&]() { finalize_fn(&IndexSet::function_basenames); }, + [&]() { finalize_fn(&IndexSet::function_fullnames); }, + [&]() { finalize_fn(&IndexSet::function_methods); }, + [&]() { finalize_fn(&IndexSet::function_selectors); }, + [&]() { finalize_fn(&IndexSet::objc_class_selectors); }, + [&]() { finalize_fn(&IndexSet::globals); }, + [&]() { finalize_fn(&IndexSet::types); }, + [&]() { finalize_fn(&IndexSet::namespaces); }); +} + +void ManualDWARFIndex::IndexUnit(DWARFUnit &unit, IndexSet &set) { + assert( + !unit.GetSymbolFileDWARF().GetBaseCompileUnit() && + "DWARFUnit associated with .dwo or .dwp should not be indexed directly"); + + Log *log = LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS); + + if (log) { + m_module.LogMessage( + log, "ManualDWARFIndex::IndexUnit for unit at .debug_info[0x%8.8x]", + unit.GetOffset()); + } + + const LanguageType cu_language = unit.GetLanguageType(); + + IndexUnitImpl(unit, cu_language, set); + + if (SymbolFileDWARFDwo *dwo_symbol_file = unit.GetDwoSymbolFile()) { + DWARFDebugInfo &dwo_info = *dwo_symbol_file->DebugInfo(); + for (size_t i = 0; i < dwo_info.GetNumUnits(); ++i) + IndexUnitImpl(*dwo_info.GetUnitAtIndex(i), cu_language, set); + } +} + +void ManualDWARFIndex::IndexUnitImpl(DWARFUnit &unit, + const LanguageType cu_language, + IndexSet &set) { + for (const DWARFDebugInfoEntry &die : unit.dies()) { + const dw_tag_t tag = die.Tag(); + + switch (tag) { + case DW_TAG_array_type: + case DW_TAG_base_type: + case DW_TAG_class_type: + case DW_TAG_constant: + case DW_TAG_enumeration_type: + case DW_TAG_inlined_subroutine: + case DW_TAG_namespace: + case DW_TAG_string_type: + case DW_TAG_structure_type: + case DW_TAG_subprogram: + case DW_TAG_subroutine_type: + case DW_TAG_typedef: + case DW_TAG_union_type: + case DW_TAG_unspecified_type: + case DW_TAG_variable: + break; + + default: + continue; + } + + DWARFAttributes attributes; + const char *name = nullptr; + const char *mangled_cstr = nullptr; + bool is_declaration = false; + // bool is_artificial = false; + bool has_address = false; + bool has_location_or_const_value = false; + bool is_global_or_static_variable = false; + + DWARFFormValue specification_die_form; + const size_t num_attributes = die.GetAttributes(&unit, attributes); + if (num_attributes > 0) { + for (uint32_t i = 0; i < num_attributes; ++i) { + dw_attr_t attr = attributes.AttributeAtIndex(i); + DWARFFormValue form_value; + switch (attr) { + case DW_AT_name: + if (attributes.ExtractFormValueAtIndex(i, form_value)) + name = form_value.AsCString(); + break; + + case DW_AT_declaration: + if (attributes.ExtractFormValueAtIndex(i, form_value)) + is_declaration = form_value.Unsigned() != 0; + break; + + // case DW_AT_artificial: + // if (attributes.ExtractFormValueAtIndex(i, + // form_value)) + // is_artificial = form_value.Unsigned() != 0; + // break; + + case DW_AT_MIPS_linkage_name: + case DW_AT_linkage_name: + if (attributes.ExtractFormValueAtIndex(i, form_value)) + mangled_cstr = form_value.AsCString(); + break; + + case DW_AT_low_pc: + case DW_AT_high_pc: + case DW_AT_ranges: + has_address = true; + break; + + case DW_AT_entry_pc: + has_address = true; + break; + + case DW_AT_location: + case DW_AT_const_value: + has_location_or_const_value = true; + if (tag == DW_TAG_variable) { + const DWARFDebugInfoEntry *parent_die = die.GetParent(); + while (parent_die != nullptr) { + switch (parent_die->Tag()) { + case DW_TAG_subprogram: + case DW_TAG_lexical_block: + case DW_TAG_inlined_subroutine: + // Even if this is a function level static, we don't add it. We + // could theoretically add these if we wanted to by + // introspecting into the DW_AT_location and seeing if the + // location describes a hard coded address, but we don't want + // the performance penalty of that right now. + is_global_or_static_variable = false; + // if (attributes.ExtractFormValueAtIndex(dwarf, i, + // form_value)) { + // // If we have valid block data, then we have location + // // expression bytesthat are fixed (not a location list). + // const uint8_t *block_data = form_value.BlockData(); + // if (block_data) { + // uint32_t block_length = form_value.Unsigned(); + // if (block_length == 1 + + // attributes.UnitAtIndex(i)->GetAddressByteSize()) { + // if (block_data[0] == DW_OP_addr) + // add_die = true; + // } + // } + // } + parent_die = nullptr; // Terminate the while loop. + break; + + case DW_TAG_compile_unit: + case DW_TAG_partial_unit: + is_global_or_static_variable = true; + parent_die = nullptr; // Terminate the while loop. + break; + + default: + parent_die = + parent_die->GetParent(); // Keep going in the while loop. + break; + } + } + } + break; + + case DW_AT_specification: + if (attributes.ExtractFormValueAtIndex(i, form_value)) + specification_die_form = form_value; + break; + } + } + } + + DIERef ref = *DWARFDIE(&unit, &die).GetDIERef(); + switch (tag) { + case DW_TAG_inlined_subroutine: + case DW_TAG_subprogram: + if (has_address) { + if (name) { + bool is_objc_method = false; + if (cu_language == eLanguageTypeObjC || + cu_language == eLanguageTypeObjC_plus_plus) { + ObjCLanguage::MethodName objc_method(name, true); + if (objc_method.IsValid(true)) { + is_objc_method = true; + ConstString class_name_with_category( + objc_method.GetClassNameWithCategory()); + ConstString objc_selector_name(objc_method.GetSelector()); + ConstString objc_fullname_no_category_name( + objc_method.GetFullNameWithoutCategory(true)); + ConstString class_name_no_category(objc_method.GetClassName()); + set.function_fullnames.Insert(ConstString(name), ref); + if (class_name_with_category) + set.objc_class_selectors.Insert(class_name_with_category, ref); + if (class_name_no_category && + class_name_no_category != class_name_with_category) + set.objc_class_selectors.Insert(class_name_no_category, ref); + if (objc_selector_name) + set.function_selectors.Insert(objc_selector_name, ref); + if (objc_fullname_no_category_name) + set.function_fullnames.Insert(objc_fullname_no_category_name, + ref); + } + } + // If we have a mangled name, then the DW_AT_name attribute is + // usually the method name without the class or any parameters + bool is_method = DWARFDIE(&unit, &die).IsMethod(); + + if (is_method) + set.function_methods.Insert(ConstString(name), ref); + else + set.function_basenames.Insert(ConstString(name), ref); + + if (!is_method && !mangled_cstr && !is_objc_method) + set.function_fullnames.Insert(ConstString(name), ref); + } + if (mangled_cstr) { + // Make sure our mangled name isn't the same string table entry as + // our name. If it starts with '_', then it is ok, else compare the + // string to make sure it isn't the same and we don't end up with + // duplicate entries + if (name && name != mangled_cstr && + ((mangled_cstr[0] == '_') || + (::strcmp(name, mangled_cstr) != 0))) { + set.function_fullnames.Insert(ConstString(mangled_cstr), ref); + } + } + } + break; + + case DW_TAG_array_type: + case DW_TAG_base_type: + case DW_TAG_class_type: + case DW_TAG_constant: + case DW_TAG_enumeration_type: + case DW_TAG_string_type: + case DW_TAG_structure_type: + case DW_TAG_subroutine_type: + case DW_TAG_typedef: + case DW_TAG_union_type: + case DW_TAG_unspecified_type: + if (name && !is_declaration) + set.types.Insert(ConstString(name), ref); + if (mangled_cstr && !is_declaration) + set.types.Insert(ConstString(mangled_cstr), ref); + break; + + case DW_TAG_namespace: + if (name) + set.namespaces.Insert(ConstString(name), ref); + break; + + case DW_TAG_variable: + if (name && has_location_or_const_value && is_global_or_static_variable) { + set.globals.Insert(ConstString(name), ref); + // Be sure to include variables by their mangled and demangled names if + // they have any since a variable can have a basename "i", a mangled + // named "_ZN12_GLOBAL__N_11iE" and a demangled mangled name + // "(anonymous namespace)::i"... + + // Make sure our mangled name isn't the same string table entry as our + // name. If it starts with '_', then it is ok, else compare the string + // to make sure it isn't the same and we don't end up with duplicate + // entries + if (mangled_cstr && name != mangled_cstr && + ((mangled_cstr[0] == '_') || (::strcmp(name, mangled_cstr) != 0))) { + set.globals.Insert(ConstString(mangled_cstr), ref); + } + } + break; + + default: + continue; + } + } +} + +void ManualDWARFIndex::GetGlobalVariables(ConstString basename, DIEArray &offsets) { + Index(); + m_set.globals.Find(basename, offsets); +} + +void ManualDWARFIndex::GetGlobalVariables(const RegularExpression ®ex, + DIEArray &offsets) { + Index(); + m_set.globals.Find(regex, offsets); +} + +void ManualDWARFIndex::GetGlobalVariables(const DWARFUnit &unit, + DIEArray &offsets) { + Index(); + m_set.globals.FindAllEntriesForUnit(unit, offsets); +} + +void ManualDWARFIndex::GetObjCMethods(ConstString class_name, + DIEArray &offsets) { + Index(); + m_set.objc_class_selectors.Find(class_name, offsets); +} + +void ManualDWARFIndex::GetCompleteObjCClass(ConstString class_name, + bool must_be_implementation, + DIEArray &offsets) { + Index(); + m_set.types.Find(class_name, offsets); +} + +void ManualDWARFIndex::GetTypes(ConstString name, DIEArray &offsets) { + Index(); + m_set.types.Find(name, offsets); +} + +void ManualDWARFIndex::GetTypes(const DWARFDeclContext &context, + DIEArray &offsets) { + Index(); + m_set.types.Find(ConstString(context[0].name), offsets); +} + +void ManualDWARFIndex::GetNamespaces(ConstString name, DIEArray &offsets) { + Index(); + m_set.namespaces.Find(name, offsets); +} + +void ManualDWARFIndex::GetFunctions(ConstString name, SymbolFileDWARF &dwarf, + const CompilerDeclContext &parent_decl_ctx, + uint32_t name_type_mask, + std::vector<DWARFDIE> &dies) { + Index(); + + if (name_type_mask & eFunctionNameTypeFull) { + DIEArray offsets; + m_set.function_basenames.Find(name, offsets); + m_set.function_methods.Find(name, offsets); + m_set.function_fullnames.Find(name, offsets); + for (const DIERef &die_ref: offsets) { + DWARFDIE die = dwarf.GetDIE(die_ref); + if (!die) + continue; + if (SymbolFileDWARF::DIEInDeclContext(&parent_decl_ctx, die)) + dies.push_back(die); + } + } + if (name_type_mask & eFunctionNameTypeBase) { + DIEArray offsets; + m_set.function_basenames.Find(name, offsets); + for (const DIERef &die_ref: offsets) { + DWARFDIE die = dwarf.GetDIE(die_ref); + if (!die) + continue; + if (SymbolFileDWARF::DIEInDeclContext(&parent_decl_ctx, die)) + dies.push_back(die); + } + offsets.clear(); + } + + if (name_type_mask & eFunctionNameTypeMethod && !parent_decl_ctx.IsValid()) { + DIEArray offsets; + m_set.function_methods.Find(name, offsets); + for (const DIERef &die_ref: offsets) { + if (DWARFDIE die = dwarf.GetDIE(die_ref)) + dies.push_back(die); + } + } + + if (name_type_mask & eFunctionNameTypeSelector && + !parent_decl_ctx.IsValid()) { + DIEArray offsets; + m_set.function_selectors.Find(name, offsets); + for (const DIERef &die_ref: offsets) { + if (DWARFDIE die = dwarf.GetDIE(die_ref)) + dies.push_back(die); + } + } +} + +void ManualDWARFIndex::GetFunctions(const RegularExpression ®ex, + DIEArray &offsets) { + Index(); + + m_set.function_basenames.Find(regex, offsets); + m_set.function_fullnames.Find(regex, offsets); +} + +void ManualDWARFIndex::Dump(Stream &s) { + s.Format("Manual DWARF index for ({0}) '{1:F}':", + m_module.GetArchitecture().GetArchitectureName(), + m_module.GetObjectFile()->GetFileSpec()); + s.Printf("\nFunction basenames:\n"); + m_set.function_basenames.Dump(&s); + s.Printf("\nFunction fullnames:\n"); + m_set.function_fullnames.Dump(&s); + s.Printf("\nFunction methods:\n"); + m_set.function_methods.Dump(&s); + s.Printf("\nFunction selectors:\n"); + m_set.function_selectors.Dump(&s); + s.Printf("\nObjective-C class selectors:\n"); + m_set.objc_class_selectors.Dump(&s); + s.Printf("\nGlobals and statics:\n"); + m_set.globals.Dump(&s); + s.Printf("\nTypes:\n"); + m_set.types.Dump(&s); + s.Printf("\nNamespaces:\n"); + m_set.namespaces.Dump(&s); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.h new file mode 100644 index 000000000000..dd03b103c936 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.h @@ -0,0 +1,74 @@ +//===-- ManualDWARFIndex.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 LLDB_MANUALDWARFINDEX_H +#define LLDB_MANUALDWARFINDEX_H + +#include "Plugins/SymbolFile/DWARF/DWARFIndex.h" +#include "Plugins/SymbolFile/DWARF/NameToDIE.h" +#include "llvm/ADT/DenseSet.h" + +class DWARFDebugInfo; + +namespace lldb_private { +class ManualDWARFIndex : public DWARFIndex { +public: + ManualDWARFIndex(Module &module, DWARFDebugInfo *debug_info, + llvm::DenseSet<dw_offset_t> units_to_avoid = {}) + : DWARFIndex(module), m_debug_info(debug_info), + m_units_to_avoid(std::move(units_to_avoid)) {} + + void Preload() override { Index(); } + + void GetGlobalVariables(ConstString basename, DIEArray &offsets) override; + void GetGlobalVariables(const RegularExpression ®ex, + DIEArray &offsets) override; + void GetGlobalVariables(const DWARFUnit &unit, DIEArray &offsets) override; + void GetObjCMethods(ConstString class_name, DIEArray &offsets) override; + void GetCompleteObjCClass(ConstString class_name, bool must_be_implementation, + DIEArray &offsets) override; + void GetTypes(ConstString name, DIEArray &offsets) override; + void GetTypes(const DWARFDeclContext &context, DIEArray &offsets) override; + void GetNamespaces(ConstString name, DIEArray &offsets) override; + void GetFunctions(ConstString name, SymbolFileDWARF &dwarf, + const CompilerDeclContext &parent_decl_ctx, + uint32_t name_type_mask, + std::vector<DWARFDIE> &dies) override; + void GetFunctions(const RegularExpression ®ex, DIEArray &offsets) override; + + void ReportInvalidDIERef(const DIERef &ref, llvm::StringRef name) override {} + void Dump(Stream &s) override; + +private: + struct IndexSet { + NameToDIE function_basenames; + NameToDIE function_fullnames; + NameToDIE function_methods; + NameToDIE function_selectors; + NameToDIE objc_class_selectors; + NameToDIE globals; + NameToDIE types; + NameToDIE namespaces; + }; + void Index(); + void IndexUnit(DWARFUnit &unit, IndexSet &set); + + static void IndexUnitImpl(DWARFUnit &unit, + const lldb::LanguageType cu_language, + IndexSet &set); + + /// Non-null value means we haven't built the index yet. + DWARFDebugInfo *m_debug_info; + /// Which dwarf units should we skip while building the index. + llvm::DenseSet<dw_offset_t> m_units_to_avoid; + + IndexSet m_set; +}; +} // namespace lldb_private + +#endif // LLDB_MANUALDWARFINDEX_H diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/NameToDIE.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/NameToDIE.cpp new file mode 100644 index 000000000000..7d81afb1b226 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/NameToDIE.cpp @@ -0,0 +1,78 @@ +//===-- NameToDIE.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 "NameToDIE.h" +#include "DWARFUnit.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/Utility/RegularExpression.h" +#include "lldb/Utility/Stream.h" +#include "lldb/Utility/StreamString.h" + +using namespace lldb; +using namespace lldb_private; + +void NameToDIE::Finalize() { + m_map.Sort(); + m_map.SizeToFit(); +} + +void NameToDIE::Insert(ConstString name, const DIERef &die_ref) { + m_map.Append(name, die_ref); +} + +size_t NameToDIE::Find(ConstString name, DIEArray &info_array) const { + return m_map.GetValues(name, info_array); +} + +size_t NameToDIE::Find(const RegularExpression ®ex, + DIEArray &info_array) const { + return m_map.GetValues(regex, info_array); +} + +size_t NameToDIE::FindAllEntriesForUnit(const DWARFUnit &unit, + DIEArray &info_array) const { + const size_t initial_size = info_array.size(); + const uint32_t size = m_map.GetSize(); + for (uint32_t i = 0; i < size; ++i) { + const DIERef &die_ref = m_map.GetValueAtIndexUnchecked(i); + if (unit.GetSymbolFileDWARF().GetDwoNum() == die_ref.dwo_num() && + unit.GetDebugSection() == die_ref.section() && + unit.GetOffset() <= die_ref.die_offset() && + die_ref.die_offset() < unit.GetNextUnitOffset()) + info_array.push_back(die_ref); + } + return info_array.size() - initial_size; +} + +void NameToDIE::Dump(Stream *s) { + const uint32_t size = m_map.GetSize(); + for (uint32_t i = 0; i < size; ++i) { + s->Format("{0} \"{1}\"\n", m_map.GetValueAtIndexUnchecked(i), + m_map.GetCStringAtIndexUnchecked(i)); + } +} + +void NameToDIE::ForEach( + std::function<bool(ConstString name, const DIERef &die_ref)> const + &callback) const { + const uint32_t size = m_map.GetSize(); + for (uint32_t i = 0; i < size; ++i) { + if (!callback(m_map.GetCStringAtIndexUnchecked(i), + m_map.GetValueAtIndexUnchecked(i))) + break; + } +} + +void NameToDIE::Append(const NameToDIE &other) { + const uint32_t size = other.m_map.GetSize(); + for (uint32_t i = 0; i < size; ++i) { + m_map.Append(other.m_map.GetCStringAtIndexUnchecked(i), + other.m_map.GetValueAtIndexUnchecked(i)); + } +} diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/NameToDIE.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/NameToDIE.h new file mode 100644 index 000000000000..b504f45e81b5 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/NameToDIE.h @@ -0,0 +1,53 @@ +//===-- NameToDIE.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 SymbolFileDWARF_NameToDIE_h_ +#define SymbolFileDWARF_NameToDIE_h_ + +#include <functional> + +#include "DIERef.h" +#include "lldb/Core/UniqueCStringMap.h" +#include "lldb/Core/dwarf.h" +#include "lldb/lldb-defines.h" + +class DWARFUnit; + +class NameToDIE { +public: + NameToDIE() : m_map() {} + + ~NameToDIE() {} + + void Dump(lldb_private::Stream *s); + + void Insert(lldb_private::ConstString name, const DIERef &die_ref); + + void Append(const NameToDIE &other); + + void Finalize(); + + size_t Find(lldb_private::ConstString name, + DIEArray &info_array) const; + + size_t Find(const lldb_private::RegularExpression ®ex, + DIEArray &info_array) const; + + size_t FindAllEntriesForUnit(const DWARFUnit &unit, + DIEArray &info_array) const; + + void + ForEach(std::function<bool(lldb_private::ConstString name, + const DIERef &die_ref)> const + &callback) const; + +protected: + lldb_private::UniqueCStringMap<DIERef> m_map; +}; + +#endif // SymbolFileDWARF_NameToDIE_h_ diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp new file mode 100644 index 000000000000..e2ddcfc5d64b --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp @@ -0,0 +1,3762 @@ +//===-- SymbolFileDWARF.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 "SymbolFileDWARF.h" + +#include "llvm/Support/Casting.h" +#include "llvm/Support/Threading.h" + +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/Value.h" +#include "lldb/Utility/ArchSpec.h" +#include "lldb/Utility/RegularExpression.h" +#include "lldb/Utility/Scalar.h" +#include "lldb/Utility/StreamString.h" +#include "lldb/Utility/Timer.h" + +#include "Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.h" +#include "Plugins/Language/CPlusPlus/CPlusPlusLanguage.h" + +#include "lldb/Host/FileSystem.h" +#include "lldb/Host/Host.h" + +#include "lldb/Interpreter/OptionValueFileSpecList.h" +#include "lldb/Interpreter/OptionValueProperties.h" + +#include "lldb/Symbol/Block.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/ClangUtil.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/CompilerDecl.h" +#include "lldb/Symbol/CompilerDeclContext.h" +#include "lldb/Symbol/DebugMacros.h" +#include "lldb/Symbol/LineTable.h" +#include "lldb/Symbol/LocateSymbolFile.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolVendor.h" +#include "lldb/Symbol/TypeMap.h" +#include "lldb/Symbol/TypeSystem.h" +#include "lldb/Symbol/VariableList.h" + +#include "lldb/Target/Language.h" +#include "lldb/Target/Target.h" + +#include "AppleDWARFIndex.h" +#include "DWARFASTParser.h" +#include "DWARFASTParserClang.h" +#include "DWARFCompileUnit.h" +#include "DWARFDebugAbbrev.h" +#include "DWARFDebugAranges.h" +#include "DWARFDebugInfo.h" +#include "DWARFDebugLine.h" +#include "DWARFDebugMacro.h" +#include "DWARFDebugRanges.h" +#include "DWARFDeclContext.h" +#include "DWARFFormValue.h" +#include "DWARFTypeUnit.h" +#include "DWARFUnit.h" +#include "DebugNamesDWARFIndex.h" +#include "LogChannelDWARF.h" +#include "ManualDWARFIndex.h" +#include "SymbolFileDWARFDebugMap.h" +#include "SymbolFileDWARFDwo.h" +#include "SymbolFileDWARFDwp.h" + +#include "llvm/Support/FileSystem.h" + +#include <algorithm> +#include <map> +#include <memory> + +#include <ctype.h> +#include <string.h> + +//#define ENABLE_DEBUG_PRINTF // COMMENT OUT THIS LINE PRIOR TO CHECKIN + +#ifdef ENABLE_DEBUG_PRINTF +#include <stdio.h> +#define DEBUG_PRINTF(fmt, ...) printf(fmt, __VA_ARGS__) +#else +#define DEBUG_PRINTF(fmt, ...) +#endif + +using namespace lldb; +using namespace lldb_private; + +// static inline bool +// child_requires_parent_class_union_or_struct_to_be_completed (dw_tag_t tag) +//{ +// switch (tag) +// { +// default: +// break; +// case DW_TAG_subprogram: +// case DW_TAG_inlined_subroutine: +// case DW_TAG_class_type: +// case DW_TAG_structure_type: +// case DW_TAG_union_type: +// return true; +// } +// return false; +//} +// + +namespace { + +static constexpr PropertyDefinition g_properties[] = { + {"comp-dir-symlink-paths", OptionValue::eTypeFileSpecList, true, 0, nullptr, + {}, + "If the DW_AT_comp_dir matches any of these paths the symbolic " + "links will be resolved at DWARF parse time."}, + {"ignore-file-indexes", OptionValue::eTypeBoolean, true, 0, nullptr, {}, + "Ignore indexes present in the object files and always index DWARF " + "manually."}}; + +enum { + ePropertySymLinkPaths, + ePropertyIgnoreIndexes, +}; + +class PluginProperties : public Properties { +public: + static ConstString GetSettingName() { + return SymbolFileDWARF::GetPluginNameStatic(); + } + + PluginProperties() { + m_collection_sp = std::make_shared<OptionValueProperties>(GetSettingName()); + m_collection_sp->Initialize(g_properties); + } + + FileSpecList GetSymLinkPaths() { + const OptionValueFileSpecList *option_value = + m_collection_sp->GetPropertyAtIndexAsOptionValueFileSpecList( + nullptr, true, ePropertySymLinkPaths); + assert(option_value); + return option_value->GetCurrentValue(); + } + + bool IgnoreFileIndexes() const { + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, ePropertyIgnoreIndexes, false); + } +}; + +typedef std::shared_ptr<PluginProperties> SymbolFileDWARFPropertiesSP; + +static const SymbolFileDWARFPropertiesSP &GetGlobalPluginProperties() { + static const auto g_settings_sp(std::make_shared<PluginProperties>()); + return g_settings_sp; +} + +} // anonymous namespace end + +FileSpecList SymbolFileDWARF::GetSymlinkPaths() { + return GetGlobalPluginProperties()->GetSymLinkPaths(); +} + +void SymbolFileDWARF::Initialize() { + LogChannelDWARF::Initialize(); + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), CreateInstance, + DebuggerInitialize); +} + +void SymbolFileDWARF::DebuggerInitialize(Debugger &debugger) { + if (!PluginManager::GetSettingForSymbolFilePlugin( + debugger, PluginProperties::GetSettingName())) { + const bool is_global_setting = true; + PluginManager::CreateSettingForSymbolFilePlugin( + debugger, GetGlobalPluginProperties()->GetValueProperties(), + ConstString("Properties for the dwarf symbol-file plug-in."), + is_global_setting); + } +} + +void SymbolFileDWARF::Terminate() { + PluginManager::UnregisterPlugin(CreateInstance); + LogChannelDWARF::Terminate(); +} + +lldb_private::ConstString SymbolFileDWARF::GetPluginNameStatic() { + static ConstString g_name("dwarf"); + return g_name; +} + +const char *SymbolFileDWARF::GetPluginDescriptionStatic() { + return "DWARF and DWARF3 debug symbol file reader."; +} + +SymbolFile *SymbolFileDWARF::CreateInstance(ObjectFile *obj_file) { + return new SymbolFileDWARF(obj_file, + /*dwo_section_list*/ nullptr); +} + +TypeList *SymbolFileDWARF::GetTypeList() { + // This method can be called without going through the symbol vendor so we + // need to lock the module. + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile(); + if (debug_map_symfile) + return debug_map_symfile->GetTypeList(); + else + return m_obj_file->GetModule()->GetTypeList(); +} +void SymbolFileDWARF::GetTypes(const DWARFDIE &die, dw_offset_t min_die_offset, + dw_offset_t max_die_offset, uint32_t type_mask, + TypeSet &type_set) { + if (die) { + const dw_offset_t die_offset = die.GetOffset(); + + if (die_offset >= max_die_offset) + return; + + if (die_offset >= min_die_offset) { + const dw_tag_t tag = die.Tag(); + + bool add_type = false; + + switch (tag) { + case DW_TAG_array_type: + add_type = (type_mask & eTypeClassArray) != 0; + break; + case DW_TAG_unspecified_type: + case DW_TAG_base_type: + add_type = (type_mask & eTypeClassBuiltin) != 0; + break; + case DW_TAG_class_type: + add_type = (type_mask & eTypeClassClass) != 0; + break; + case DW_TAG_structure_type: + add_type = (type_mask & eTypeClassStruct) != 0; + break; + case DW_TAG_union_type: + add_type = (type_mask & eTypeClassUnion) != 0; + break; + case DW_TAG_enumeration_type: + add_type = (type_mask & eTypeClassEnumeration) != 0; + break; + case DW_TAG_subroutine_type: + case DW_TAG_subprogram: + case DW_TAG_inlined_subroutine: + add_type = (type_mask & eTypeClassFunction) != 0; + break; + case DW_TAG_pointer_type: + add_type = (type_mask & eTypeClassPointer) != 0; + break; + case DW_TAG_rvalue_reference_type: + case DW_TAG_reference_type: + add_type = (type_mask & eTypeClassReference) != 0; + break; + case DW_TAG_typedef: + add_type = (type_mask & eTypeClassTypedef) != 0; + break; + case DW_TAG_ptr_to_member_type: + add_type = (type_mask & eTypeClassMemberPointer) != 0; + break; + } + + if (add_type) { + const bool assert_not_being_parsed = true; + Type *type = ResolveTypeUID(die, assert_not_being_parsed); + if (type) { + if (type_set.find(type) == type_set.end()) + type_set.insert(type); + } + } + } + + for (DWARFDIE child_die = die.GetFirstChild(); child_die.IsValid(); + child_die = child_die.GetSibling()) { + GetTypes(child_die, min_die_offset, max_die_offset, type_mask, type_set); + } + } +} + +size_t SymbolFileDWARF::GetTypes(SymbolContextScope *sc_scope, + TypeClass type_mask, TypeList &type_list) + +{ + ASSERT_MODULE_LOCK(this); + TypeSet type_set; + + CompileUnit *comp_unit = nullptr; + DWARFUnit *dwarf_cu = nullptr; + if (sc_scope) + comp_unit = sc_scope->CalculateSymbolContextCompileUnit(); + + if (comp_unit) { + dwarf_cu = GetDWARFCompileUnit(comp_unit); + if (dwarf_cu == nullptr) + return 0; + GetTypes(dwarf_cu->DIE(), dwarf_cu->GetOffset(), + dwarf_cu->GetNextUnitOffset(), type_mask, type_set); + } else { + DWARFDebugInfo *info = DebugInfo(); + if (info) { + const size_t num_cus = info->GetNumUnits(); + for (size_t cu_idx = 0; cu_idx < num_cus; ++cu_idx) { + dwarf_cu = info->GetUnitAtIndex(cu_idx); + if (dwarf_cu) { + GetTypes(dwarf_cu->DIE(), 0, UINT32_MAX, type_mask, type_set); + } + } + } + } + + std::set<CompilerType> compiler_type_set; + size_t num_types_added = 0; + for (Type *type : type_set) { + CompilerType compiler_type = type->GetForwardCompilerType(); + if (compiler_type_set.find(compiler_type) == compiler_type_set.end()) { + compiler_type_set.insert(compiler_type); + type_list.Insert(type->shared_from_this()); + ++num_types_added; + } + } + return num_types_added; +} + +// Gets the first parent that is a lexical block, function or inlined +// subroutine, or compile unit. +DWARFDIE +SymbolFileDWARF::GetParentSymbolContextDIE(const DWARFDIE &child_die) { + DWARFDIE die; + for (die = child_die.GetParent(); die; die = die.GetParent()) { + dw_tag_t tag = die.Tag(); + + switch (tag) { + case DW_TAG_compile_unit: + case DW_TAG_partial_unit: + case DW_TAG_subprogram: + case DW_TAG_inlined_subroutine: + case DW_TAG_lexical_block: + return die; + } + } + return DWARFDIE(); +} + +SymbolFileDWARF::SymbolFileDWARF(ObjectFile *objfile, + SectionList *dwo_section_list) + : SymbolFile(objfile), + UserID(0x7fffffff00000000), // Used by SymbolFileDWARFDebugMap to + // when this class parses .o files to + // contain the .o file index/ID + m_debug_map_module_wp(), m_debug_map_symfile(nullptr), + m_context(objfile->GetModule()->GetSectionList(), dwo_section_list), + m_data_debug_loc(), m_abbr(), m_info(), m_fetched_external_modules(false), + m_supports_DW_AT_APPLE_objc_complete_type(eLazyBoolCalculate), + m_unique_ast_type_map() {} + +SymbolFileDWARF::~SymbolFileDWARF() {} + +static ConstString GetDWARFMachOSegmentName() { + static ConstString g_dwarf_section_name("__DWARF"); + return g_dwarf_section_name; +} + +UniqueDWARFASTTypeMap &SymbolFileDWARF::GetUniqueDWARFASTTypeMap() { + SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile(); + if (debug_map_symfile) + return debug_map_symfile->GetUniqueDWARFASTTypeMap(); + else + return m_unique_ast_type_map; +} + +TypeSystem *SymbolFileDWARF::GetTypeSystemForLanguage(LanguageType language) { + SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile(); + TypeSystem *type_system; + if (debug_map_symfile) { + type_system = debug_map_symfile->GetTypeSystemForLanguage(language); + } else { + type_system = m_obj_file->GetModule()->GetTypeSystemForLanguage(language); + if (type_system) + type_system->SetSymbolFile(this); + } + return type_system; +} + +void SymbolFileDWARF::InitializeObject() { + Log *log = LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_INFO); + + if (!GetGlobalPluginProperties()->IgnoreFileIndexes()) { + DWARFDataExtractor apple_names, apple_namespaces, apple_types, apple_objc; + LoadSectionData(eSectionTypeDWARFAppleNames, apple_names); + LoadSectionData(eSectionTypeDWARFAppleNamespaces, apple_namespaces); + LoadSectionData(eSectionTypeDWARFAppleTypes, apple_types); + LoadSectionData(eSectionTypeDWARFAppleObjC, apple_objc); + + m_index = AppleDWARFIndex::Create( + *GetObjectFile()->GetModule(), apple_names, apple_namespaces, + apple_types, apple_objc, m_context.getOrLoadStrData()); + + if (m_index) + return; + + DWARFDataExtractor debug_names; + LoadSectionData(eSectionTypeDWARFDebugNames, debug_names); + if (debug_names.GetByteSize() > 0) { + llvm::Expected<std::unique_ptr<DebugNamesDWARFIndex>> index_or = + DebugNamesDWARFIndex::Create( + *GetObjectFile()->GetModule(), debug_names, + m_context.getOrLoadStrData(), DebugInfo()); + if (index_or) { + m_index = std::move(*index_or); + return; + } + LLDB_LOG_ERROR(log, index_or.takeError(), + "Unable to read .debug_names data: {0}"); + } + } + + m_index = llvm::make_unique<ManualDWARFIndex>(*GetObjectFile()->GetModule(), + DebugInfo()); +} + +bool SymbolFileDWARF::SupportedVersion(uint16_t version) { + return version >= 2 && version <= 5; +} + +uint32_t SymbolFileDWARF::CalculateAbilities() { + uint32_t abilities = 0; + if (m_obj_file != nullptr) { + const Section *section = nullptr; + const SectionList *section_list = m_obj_file->GetSectionList(); + if (section_list == nullptr) + return 0; + + uint64_t debug_abbrev_file_size = 0; + uint64_t debug_info_file_size = 0; + uint64_t debug_line_file_size = 0; + + section = section_list->FindSectionByName(GetDWARFMachOSegmentName()).get(); + + if (section) + section_list = §ion->GetChildren(); + + section = + section_list->FindSectionByType(eSectionTypeDWARFDebugInfo, true).get(); + if (section != nullptr) { + debug_info_file_size = section->GetFileSize(); + + section = + section_list->FindSectionByType(eSectionTypeDWARFDebugAbbrev, true) + .get(); + if (section) + debug_abbrev_file_size = section->GetFileSize(); + + DWARFDebugAbbrev *abbrev = DebugAbbrev(); + if (abbrev) { + std::set<dw_form_t> invalid_forms; + abbrev->GetUnsupportedForms(invalid_forms); + if (!invalid_forms.empty()) { + StreamString error; + error.Printf("unsupported DW_FORM value%s:", invalid_forms.size() > 1 ? "s" : ""); + for (auto form : invalid_forms) + error.Printf(" %#x", form); + m_obj_file->GetModule()->ReportWarning("%s", error.GetString().str().c_str()); + return 0; + } + } + + section = + section_list->FindSectionByType(eSectionTypeDWARFDebugLine, true) + .get(); + if (section) + debug_line_file_size = section->GetFileSize(); + } else { + const char *symfile_dir_cstr = + m_obj_file->GetFileSpec().GetDirectory().GetCString(); + if (symfile_dir_cstr) { + if (strcasestr(symfile_dir_cstr, ".dsym")) { + if (m_obj_file->GetType() == ObjectFile::eTypeDebugInfo) { + // We have a dSYM file that didn't have a any debug info. If the + // string table has a size of 1, then it was made from an + // executable with no debug info, or from an executable that was + // stripped. + section = + section_list->FindSectionByType(eSectionTypeDWARFDebugStr, true) + .get(); + if (section && section->GetFileSize() == 1) { + m_obj_file->GetModule()->ReportWarning( + "empty dSYM file detected, dSYM was created with an " + "executable with no debug info."); + } + } + } + } + } + + if (debug_abbrev_file_size > 0 && debug_info_file_size > 0) + abilities |= CompileUnits | Functions | Blocks | GlobalVariables | + LocalVariables | VariableTypes; + + if (debug_line_file_size > 0) + abilities |= LineTables; + } + return abilities; +} + +const DWARFDataExtractor & +SymbolFileDWARF::GetCachedSectionData(lldb::SectionType sect_type, + 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)); + }); + return data_segment.m_data; +} + +void SymbolFileDWARF::LoadSectionData(lldb::SectionType sect_type, + DWARFDataExtractor &data) { + ModuleSP module_sp(m_obj_file->GetModule()); + const SectionList *section_list = module_sp->GetSectionList(); + if (!section_list) + return; + + SectionSP section_sp(section_list->FindSectionByType(sect_type, true)); + if (!section_sp) + return; + + data.Clear(); + m_obj_file->ReadSectionData(section_sp.get(), data); +} + +const DWARFDataExtractor &SymbolFileDWARF::DebugLocData() { + const DWARFDataExtractor &debugLocData = get_debug_loc_data(); + if (debugLocData.GetByteSize() > 0) + return debugLocData; + return get_debug_loclists_data(); +} + +const DWARFDataExtractor &SymbolFileDWARF::get_debug_loc_data() { + return GetCachedSectionData(eSectionTypeDWARFDebugLoc, m_data_debug_loc); +} + +const DWARFDataExtractor &SymbolFileDWARF::get_debug_loclists_data() { + return GetCachedSectionData(eSectionTypeDWARFDebugLocLists, + m_data_debug_loclists); +} + +DWARFDebugAbbrev *SymbolFileDWARF::DebugAbbrev() { + if (m_abbr) + return m_abbr.get(); + + const DWARFDataExtractor &debug_abbrev_data = m_context.getOrLoadAbbrevData(); + if (debug_abbrev_data.GetByteSize() == 0) + return nullptr; + + auto abbr = llvm::make_unique<DWARFDebugAbbrev>(); + llvm::Error error = abbr->parse(debug_abbrev_data); + if (error) { + Log *log = LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_INFO); + LLDB_LOG_ERROR(log, std::move(error), + "Unable to read .debug_abbrev section: {0}"); + return nullptr; + } + + m_abbr = std::move(abbr); + return m_abbr.get(); +} + +const DWARFDebugAbbrev *SymbolFileDWARF::DebugAbbrev() const { + return m_abbr.get(); +} + +DWARFDebugInfo *SymbolFileDWARF::DebugInfo() { + if (m_info == nullptr) { + static Timer::Category func_cat(LLVM_PRETTY_FUNCTION); + Timer scoped_timer(func_cat, "%s this = %p", LLVM_PRETTY_FUNCTION, + static_cast<void *>(this)); + if (m_context.getOrLoadDebugInfoData().GetByteSize() > 0) + m_info = llvm::make_unique<DWARFDebugInfo>(*this, m_context); + } + return m_info.get(); +} + +const DWARFDebugInfo *SymbolFileDWARF::DebugInfo() const { + return m_info.get(); +} + +DWARFUnit * +SymbolFileDWARF::GetDWARFCompileUnit(lldb_private::CompileUnit *comp_unit) { + if (!comp_unit) + return nullptr; + + DWARFDebugInfo *info = DebugInfo(); + if (info) { + // The compile unit ID is the index of the DWARF unit. + DWARFUnit *dwarf_cu = info->GetUnitAtIndex(comp_unit->GetID()); + if (dwarf_cu && dwarf_cu->GetUserData() == nullptr) + dwarf_cu->SetUserData(comp_unit); + return dwarf_cu; + } + return nullptr; +} + +DWARFDebugRangesBase *SymbolFileDWARF::GetDebugRanges() { + if (!m_ranges) { + static Timer::Category func_cat(LLVM_PRETTY_FUNCTION); + Timer scoped_timer(func_cat, "%s this = %p", LLVM_PRETTY_FUNCTION, + static_cast<void *>(this)); + + if (m_context.getOrLoadRangesData().GetByteSize() > 0) + m_ranges.reset(new DWARFDebugRanges()); + + if (m_ranges) + m_ranges->Extract(m_context); + } + return m_ranges.get(); +} + +DWARFDebugRangesBase *SymbolFileDWARF::GetDebugRngLists() { + if (!m_rnglists) { + static Timer::Category func_cat(LLVM_PRETTY_FUNCTION); + Timer scoped_timer(func_cat, "%s this = %p", LLVM_PRETTY_FUNCTION, + static_cast<void *>(this)); + + if (m_context.getOrLoadRngListsData().GetByteSize() > 0) + m_rnglists.reset(new DWARFDebugRngLists()); + + if (m_rnglists) + m_rnglists->Extract(m_context); + } + return m_rnglists.get(); +} + +lldb::CompUnitSP SymbolFileDWARF::ParseCompileUnit(DWARFCompileUnit &dwarf_cu) { + CompUnitSP cu_sp; + CompileUnit *comp_unit = (CompileUnit *)dwarf_cu.GetUserData(); + if (comp_unit) { + // We already parsed this compile unit, had out a shared pointer to it + cu_sp = comp_unit->shared_from_this(); + } else { + if (&dwarf_cu.GetSymbolFileDWARF() != this) { + return dwarf_cu.GetSymbolFileDWARF().ParseCompileUnit(dwarf_cu); + } else if (dwarf_cu.GetOffset() == 0 && GetDebugMapSymfile()) { + // Let the debug map create the compile unit + cu_sp = m_debug_map_symfile->GetCompileUnit(this); + dwarf_cu.SetUserData(cu_sp.get()); + } else { + ModuleSP module_sp(m_obj_file->GetModule()); + if (module_sp) { + const DWARFDIE cu_die = dwarf_cu.DIE(); + if (cu_die) { + FileSpec cu_file_spec(cu_die.GetName(), dwarf_cu.GetPathStyle()); + if (cu_file_spec) { + // If we have a full path to the compile unit, we don't need to + // resolve the file. This can be expensive e.g. when the source + // files are NFS mounted. + cu_file_spec.MakeAbsolute(dwarf_cu.GetCompilationDirectory()); + + std::string remapped_file; + if (module_sp->RemapSourceFile(cu_file_spec.GetPath(), + remapped_file)) + cu_file_spec.SetFile(remapped_file, FileSpec::Style::native); + } + + LanguageType cu_language = DWARFUnit::LanguageTypeFromDWARF( + cu_die.GetAttributeValueAsUnsigned(DW_AT_language, 0)); + + bool is_optimized = dwarf_cu.GetIsOptimized(); + BuildCuTranslationTable(); + cu_sp = std::make_shared<CompileUnit>( + module_sp, &dwarf_cu, cu_file_spec, + *GetDWARFUnitIndex(dwarf_cu.GetID()), cu_language, + is_optimized ? eLazyBoolYes : eLazyBoolNo); + + dwarf_cu.SetUserData(cu_sp.get()); + + m_obj_file->GetModule()->GetSymbolVendor()->SetCompileUnitAtIndex( + dwarf_cu.GetID(), cu_sp); + } + } + } + } + return cu_sp; +} + +void SymbolFileDWARF::BuildCuTranslationTable() { + if (!m_lldb_cu_to_dwarf_unit.empty()) + return; + + DWARFDebugInfo *info = DebugInfo(); + if (!info) + return; + + if (!info->ContainsTypeUnits()) { + // We can use a 1-to-1 mapping. No need to build a translation table. + return; + } + for (uint32_t i = 0, num = info->GetNumUnits(); i < num; ++i) { + if (auto *cu = llvm::dyn_cast<DWARFCompileUnit>(info->GetUnitAtIndex(i))) { + cu->SetID(m_lldb_cu_to_dwarf_unit.size()); + m_lldb_cu_to_dwarf_unit.push_back(i); + } + } +} + +llvm::Optional<uint32_t> SymbolFileDWARF::GetDWARFUnitIndex(uint32_t cu_idx) { + BuildCuTranslationTable(); + if (m_lldb_cu_to_dwarf_unit.empty()) + return cu_idx; + if (cu_idx >= m_lldb_cu_to_dwarf_unit.size()) + return llvm::None; + return m_lldb_cu_to_dwarf_unit[cu_idx]; +} + +uint32_t SymbolFileDWARF::GetNumCompileUnits() { + DWARFDebugInfo *info = DebugInfo(); + if (!info) + return 0; + BuildCuTranslationTable(); + return m_lldb_cu_to_dwarf_unit.empty() ? info->GetNumUnits() + : m_lldb_cu_to_dwarf_unit.size(); +} + +CompUnitSP SymbolFileDWARF::ParseCompileUnitAtIndex(uint32_t cu_idx) { + ASSERT_MODULE_LOCK(this); + DWARFDebugInfo *info = DebugInfo(); + if (!info) + return {}; + + if (llvm::Optional<uint32_t> dwarf_idx = GetDWARFUnitIndex(cu_idx)) { + if (auto *dwarf_cu = llvm::cast_or_null<DWARFCompileUnit>( + info->GetUnitAtIndex(*dwarf_idx))) + return ParseCompileUnit(*dwarf_cu); + } + return {}; +} + +Function *SymbolFileDWARF::ParseFunction(CompileUnit &comp_unit, + const DWARFDIE &die) { + ASSERT_MODULE_LOCK(this); + if (die.IsValid()) { + TypeSystem *type_system = + GetTypeSystemForLanguage(die.GetCU()->GetLanguageType()); + + if (type_system) { + DWARFASTParser *dwarf_ast = type_system->GetDWARFParser(); + if (dwarf_ast) + return dwarf_ast->ParseFunctionFromDWARF(comp_unit, die); + } + } + return nullptr; +} + +bool SymbolFileDWARF::FixupAddress(Address &addr) { + SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile(); + if (debug_map_symfile) { + return debug_map_symfile->LinkOSOAddress(addr); + } + // This is a normal DWARF file, no address fixups need to happen + return true; +} +lldb::LanguageType SymbolFileDWARF::ParseLanguage(CompileUnit &comp_unit) { + ASSERT_MODULE_LOCK(this); + DWARFUnit *dwarf_cu = GetDWARFCompileUnit(&comp_unit); + if (dwarf_cu) + return dwarf_cu->GetLanguageType(); + else + return eLanguageTypeUnknown; +} + +size_t SymbolFileDWARF::ParseFunctions(CompileUnit &comp_unit) { + ASSERT_MODULE_LOCK(this); + DWARFUnit *dwarf_cu = GetDWARFCompileUnit(&comp_unit); + if (!dwarf_cu) + return 0; + + size_t functions_added = 0; + std::vector<DWARFDIE> function_dies; + dwarf_cu->AppendDIEsWithTag(DW_TAG_subprogram, function_dies); + for (const DWARFDIE &die : function_dies) { + if (comp_unit.FindFunctionByUID(die.GetID())) + continue; + if (ParseFunction(comp_unit, die)) + ++functions_added; + } + // FixupTypes(); + return functions_added; +} + +bool SymbolFileDWARF::ParseSupportFiles(CompileUnit &comp_unit, + FileSpecList &support_files) { + ASSERT_MODULE_LOCK(this); + if (DWARFUnit *unit = GetDWARFCompileUnit(&comp_unit)) { + const dw_offset_t stmt_list = unit->GetLineTableOffset(); + if (stmt_list != DW_INVALID_OFFSET) { + // All file indexes in DWARF are one based and a file of index zero is + // supposed to be the compile unit itself. + support_files.Append(comp_unit); + return DWARFDebugLine::ParseSupportFiles(comp_unit.GetModule(), + m_context.getOrLoadLineData(), + stmt_list, support_files, unit); + } + } + return false; +} + +FileSpec SymbolFileDWARF::GetFile(DWARFUnit &unit, size_t file_idx) { + if (auto *dwarf_cu = llvm::dyn_cast<DWARFCompileUnit>(&unit)) { + if (CompileUnit *lldb_cu = GetCompUnitForDWARFCompUnit(*dwarf_cu)) + return lldb_cu->GetSupportFiles().GetFileSpecAtIndex(file_idx); + return FileSpec(); + } + + auto &tu = llvm::cast<DWARFTypeUnit>(unit); + return GetTypeUnitSupportFiles(tu).GetFileSpecAtIndex(file_idx); +} + +const FileSpecList & +SymbolFileDWARF::GetTypeUnitSupportFiles(DWARFTypeUnit &tu) { + static FileSpecList empty_list; + + dw_offset_t offset = tu.GetLineTableOffset(); + if (offset == DW_INVALID_OFFSET || + offset == llvm::DenseMapInfo<dw_offset_t>::getEmptyKey() || + offset == llvm::DenseMapInfo<dw_offset_t>::getTombstoneKey()) + return empty_list; + + // Many type units can share a line table, so parse the support file list + // once, and cache it based on the offset field. + auto iter_bool = m_type_unit_support_files.try_emplace(offset); + FileSpecList &list = iter_bool.first->second; + if (iter_bool.second) { + list.Append(FileSpec()); + DWARFDebugLine::ParseSupportFiles(GetObjectFile()->GetModule(), + m_context.getOrLoadLineData(), offset, + list, &tu); + } + return list; +} + +bool SymbolFileDWARF::ParseIsOptimized(CompileUnit &comp_unit) { + ASSERT_MODULE_LOCK(this); + DWARFUnit *dwarf_cu = GetDWARFCompileUnit(&comp_unit); + if (dwarf_cu) + return dwarf_cu->GetIsOptimized(); + return false; +} + +bool SymbolFileDWARF::ParseImportedModules( + const lldb_private::SymbolContext &sc, + std::vector<SourceModule> &imported_modules) { + ASSERT_MODULE_LOCK(this); + assert(sc.comp_unit); + DWARFUnit *dwarf_cu = GetDWARFCompileUnit(sc.comp_unit); + if (!dwarf_cu) + return false; + if (!ClangModulesDeclVendor::LanguageSupportsClangModules( + sc.comp_unit->GetLanguage())) + return false; + UpdateExternalModuleListIfNeeded(); + + const DWARFDIE die = dwarf_cu->DIE(); + if (!die) + return false; + + for (DWARFDIE child_die = die.GetFirstChild(); child_die; + child_die = child_die.GetSibling()) { + if (child_die.Tag() != DW_TAG_imported_declaration) + continue; + + DWARFDIE module_die = child_die.GetReferencedDIE(DW_AT_import); + if (module_die.Tag() != DW_TAG_module) + continue; + + if (const char *name = + module_die.GetAttributeValueAsString(DW_AT_name, nullptr)) { + SourceModule module; + module.path.push_back(ConstString(name)); + + DWARFDIE parent_die = module_die; + while ((parent_die = parent_die.GetParent())) { + if (parent_die.Tag() != DW_TAG_module) + break; + if (const char *name = + parent_die.GetAttributeValueAsString(DW_AT_name, nullptr)) + module.path.push_back(ConstString(name)); + } + std::reverse(module.path.begin(), module.path.end()); + if (const char *include_path = module_die.GetAttributeValueAsString( + DW_AT_LLVM_include_path, nullptr)) + module.search_path = ConstString(include_path); + if (const char *sysroot = module_die.GetAttributeValueAsString( + DW_AT_LLVM_isysroot, nullptr)) + module.sysroot = ConstString(sysroot); + imported_modules.push_back(module); + } + } + return true; +} + +struct ParseDWARFLineTableCallbackInfo { + LineTable *line_table; + std::unique_ptr<LineSequence> sequence_up; + lldb::addr_t addr_mask; +}; + +// ParseStatementTableCallback +static void ParseDWARFLineTableCallback(dw_offset_t offset, + const DWARFDebugLine::State &state, + void *userData) { + if (state.row == DWARFDebugLine::State::StartParsingLineTable) { + // Just started parsing the line table + } else if (state.row == DWARFDebugLine::State::DoneParsingLineTable) { + // Done parsing line table, nothing to do for the cleanup + } else { + ParseDWARFLineTableCallbackInfo *info = + (ParseDWARFLineTableCallbackInfo *)userData; + LineTable *line_table = info->line_table; + + // If this is our first time here, we need to create a sequence container. + if (!info->sequence_up) { + info->sequence_up.reset(line_table->CreateLineSequenceContainer()); + assert(info->sequence_up.get()); + } + line_table->AppendLineEntryToSequence( + info->sequence_up.get(), state.address & info->addr_mask, state.line, + state.column, state.file, state.is_stmt, state.basic_block, + state.prologue_end, state.epilogue_begin, state.end_sequence); + if (state.end_sequence) { + // First, put the current sequence into the line table. + line_table->InsertSequence(info->sequence_up.get()); + // Then, empty it to prepare for the next sequence. + info->sequence_up->Clear(); + } + } +} + +bool SymbolFileDWARF::ParseLineTable(CompileUnit &comp_unit) { + ASSERT_MODULE_LOCK(this); + if (comp_unit.GetLineTable() != nullptr) + return true; + + DWARFUnit *dwarf_cu = GetDWARFCompileUnit(&comp_unit); + if (dwarf_cu) { + const DWARFBaseDIE dwarf_cu_die = dwarf_cu->GetUnitDIEOnly(); + if (dwarf_cu_die) { + const dw_offset_t cu_line_offset = + dwarf_cu_die.GetAttributeValueAsUnsigned(DW_AT_stmt_list, + DW_INVALID_OFFSET); + if (cu_line_offset != DW_INVALID_OFFSET) { + std::unique_ptr<LineTable> line_table_up(new LineTable(&comp_unit)); + if (line_table_up) { + ParseDWARFLineTableCallbackInfo info; + info.line_table = line_table_up.get(); + + /* + * MIPS: + * The SymbolContext may not have a valid target, thus we may not be + * able + * to call Address::GetOpcodeLoadAddress() which would clear the bit + * #0 + * for MIPS. Use ArchSpec to clear the bit #0. + */ + switch (GetObjectFile()->GetArchitecture().GetMachine()) { + case llvm::Triple::mips: + case llvm::Triple::mipsel: + case llvm::Triple::mips64: + case llvm::Triple::mips64el: + info.addr_mask = ~((lldb::addr_t)1); + break; + default: + info.addr_mask = ~((lldb::addr_t)0); + break; + } + + lldb::offset_t offset = cu_line_offset; + DWARFDebugLine::ParseStatementTable( + m_context.getOrLoadLineData(), &offset, + ParseDWARFLineTableCallback, &info, dwarf_cu); + SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile(); + if (debug_map_symfile) { + // We have an object file that has a line table with addresses that + // are not linked. We need to link the line table and convert the + // addresses that are relative to the .o file into addresses for + // the main executable. + comp_unit.SetLineTable( + debug_map_symfile->LinkOSOLineTable(this, line_table_up.get())); + } else { + comp_unit.SetLineTable(line_table_up.release()); + return true; + } + } + } + } + } + return false; +} + +lldb_private::DebugMacrosSP +SymbolFileDWARF::ParseDebugMacros(lldb::offset_t *offset) { + auto iter = m_debug_macros_map.find(*offset); + if (iter != m_debug_macros_map.end()) + return iter->second; + + const DWARFDataExtractor &debug_macro_data = m_context.getOrLoadMacroData(); + if (debug_macro_data.GetByteSize() == 0) + return DebugMacrosSP(); + + lldb_private::DebugMacrosSP debug_macros_sp(new lldb_private::DebugMacros()); + m_debug_macros_map[*offset] = debug_macros_sp; + + const DWARFDebugMacroHeader &header = + DWARFDebugMacroHeader::ParseHeader(debug_macro_data, offset); + DWARFDebugMacroEntry::ReadMacroEntries( + debug_macro_data, m_context.getOrLoadStrData(), header.OffsetIs64Bit(), + offset, this, debug_macros_sp); + + return debug_macros_sp; +} + +bool SymbolFileDWARF::ParseDebugMacros(CompileUnit &comp_unit) { + ASSERT_MODULE_LOCK(this); + + DWARFUnit *dwarf_cu = GetDWARFCompileUnit(&comp_unit); + if (dwarf_cu == nullptr) + return false; + + const DWARFBaseDIE dwarf_cu_die = dwarf_cu->GetUnitDIEOnly(); + if (!dwarf_cu_die) + return false; + + lldb::offset_t sect_offset = + dwarf_cu_die.GetAttributeValueAsUnsigned(DW_AT_macros, DW_INVALID_OFFSET); + if (sect_offset == DW_INVALID_OFFSET) + sect_offset = dwarf_cu_die.GetAttributeValueAsUnsigned(DW_AT_GNU_macros, + DW_INVALID_OFFSET); + if (sect_offset == DW_INVALID_OFFSET) + return false; + + comp_unit.SetDebugMacros(ParseDebugMacros(§_offset)); + + return true; +} + +size_t SymbolFileDWARF::ParseBlocksRecursive( + lldb_private::CompileUnit &comp_unit, Block *parent_block, + const DWARFDIE &orig_die, addr_t subprogram_low_pc, uint32_t depth) { + size_t blocks_added = 0; + DWARFDIE die = orig_die; + while (die) { + dw_tag_t tag = die.Tag(); + + switch (tag) { + case DW_TAG_inlined_subroutine: + case DW_TAG_subprogram: + case DW_TAG_lexical_block: { + Block *block = nullptr; + if (tag == DW_TAG_subprogram) { + // Skip any DW_TAG_subprogram DIEs that are inside of a normal or + // inlined functions. These will be parsed on their own as separate + // entities. + + if (depth > 0) + break; + + block = parent_block; + } else { + BlockSP block_sp(new Block(die.GetID())); + parent_block->AddChild(block_sp); + block = block_sp.get(); + } + DWARFRangeList ranges; + const char *name = nullptr; + const char *mangled_name = nullptr; + + int decl_file = 0; + int decl_line = 0; + int decl_column = 0; + int call_file = 0; + int call_line = 0; + int call_column = 0; + if (die.GetDIENamesAndRanges(name, mangled_name, ranges, decl_file, + decl_line, decl_column, call_file, call_line, + call_column, nullptr)) { + if (tag == DW_TAG_subprogram) { + assert(subprogram_low_pc == LLDB_INVALID_ADDRESS); + subprogram_low_pc = ranges.GetMinRangeBase(0); + } else if (tag == DW_TAG_inlined_subroutine) { + // We get called here for inlined subroutines in two ways. The first + // time is when we are making the Function object for this inlined + // concrete instance. Since we're creating a top level block at + // here, the subprogram_low_pc will be LLDB_INVALID_ADDRESS. So we + // need to adjust the containing address. The second time is when we + // are parsing the blocks inside the function that contains the + // inlined concrete instance. Since these will be blocks inside the + // containing "real" function the offset will be for that function. + if (subprogram_low_pc == LLDB_INVALID_ADDRESS) { + subprogram_low_pc = ranges.GetMinRangeBase(0); + } + } + + const size_t num_ranges = ranges.GetSize(); + for (size_t i = 0; i < num_ranges; ++i) { + const DWARFRangeList::Entry &range = ranges.GetEntryRef(i); + const addr_t range_base = range.GetRangeBase(); + if (range_base >= subprogram_low_pc) + block->AddRange(Block::Range(range_base - subprogram_low_pc, + range.GetByteSize())); + else { + GetObjectFile()->GetModule()->ReportError( + "0x%8.8" PRIx64 ": adding range [0x%" PRIx64 "-0x%" PRIx64 + ") which has a base that is less than the function's low PC " + "0x%" PRIx64 ". Please file a bug and attach the file at the " + "start of this error message", + block->GetID(), range_base, range.GetRangeEnd(), + subprogram_low_pc); + } + } + block->FinalizeRanges(); + + if (tag != DW_TAG_subprogram && + (name != nullptr || mangled_name != nullptr)) { + std::unique_ptr<Declaration> decl_up; + if (decl_file != 0 || decl_line != 0 || decl_column != 0) + decl_up.reset(new Declaration( + comp_unit.GetSupportFiles().GetFileSpecAtIndex(decl_file), + decl_line, decl_column)); + + std::unique_ptr<Declaration> call_up; + if (call_file != 0 || call_line != 0 || call_column != 0) + call_up.reset(new Declaration( + comp_unit.GetSupportFiles().GetFileSpecAtIndex(call_file), + call_line, call_column)); + + block->SetInlinedFunctionInfo(name, mangled_name, decl_up.get(), + call_up.get()); + } + + ++blocks_added; + + if (die.HasChildren()) { + blocks_added += + ParseBlocksRecursive(comp_unit, block, die.GetFirstChild(), + subprogram_low_pc, depth + 1); + } + } + } break; + default: + break; + } + + // Only parse siblings of the block if we are not at depth zero. A depth of + // zero indicates we are currently parsing the top level DW_TAG_subprogram + // DIE + + if (depth == 0) + die.Clear(); + else + die = die.GetSibling(); + } + return blocks_added; +} + +bool SymbolFileDWARF::ClassOrStructIsVirtual(const DWARFDIE &parent_die) { + if (parent_die) { + for (DWARFDIE die = parent_die.GetFirstChild(); die; + die = die.GetSibling()) { + dw_tag_t tag = die.Tag(); + bool check_virtuality = false; + switch (tag) { + case DW_TAG_inheritance: + case DW_TAG_subprogram: + check_virtuality = true; + break; + default: + break; + } + if (check_virtuality) { + if (die.GetAttributeValueAsUnsigned(DW_AT_virtuality, 0) != 0) + return true; + } + } + } + return false; +} + +void SymbolFileDWARF::ParseDeclsForContext(CompilerDeclContext decl_ctx) { + TypeSystem *type_system = decl_ctx.GetTypeSystem(); + DWARFASTParser *ast_parser = type_system->GetDWARFParser(); + std::vector<DWARFDIE> decl_ctx_die_list = + ast_parser->GetDIEForDeclContext(decl_ctx); + + for (DWARFDIE decl_ctx_die : decl_ctx_die_list) + for (DWARFDIE decl = decl_ctx_die.GetFirstChild(); decl; + decl = decl.GetSibling()) + ast_parser->GetDeclForUIDFromDWARF(decl); +} + +user_id_t SymbolFileDWARF::GetUID(DIERef ref) { + if (GetDebugMapSymfile()) + return GetID() | ref.die_offset(); + + return user_id_t(GetDwoNum().getValueOr(0x7fffffff)) << 32 | + ref.die_offset() | + (lldb::user_id_t(ref.section() == DIERef::Section::DebugTypes) << 63); +} + +llvm::Optional<SymbolFileDWARF::DecodedUID> +SymbolFileDWARF::DecodeUID(lldb::user_id_t uid) { + // This method can be called without going through the symbol vendor so we + // need to lock the module. + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + // Anytime we get a "lldb::user_id_t" from an lldb_private::SymbolFile API we + // must make sure we use the correct DWARF file when resolving things. On + // MacOSX, when using SymbolFileDWARFDebugMap, we will use multiple + // SymbolFileDWARF classes, one for each .o file. We can often end up with + // references to other DWARF objects and we must be ready to receive a + // "lldb::user_id_t" that specifies a DIE from another SymbolFileDWARF + // instance. + if (SymbolFileDWARFDebugMap *debug_map = GetDebugMapSymfile()) { + SymbolFileDWARF *dwarf = debug_map->GetSymbolFileByOSOIndex( + debug_map->GetOSOIndexFromUserID(uid)); + return DecodedUID{ + *dwarf, {llvm::None, DIERef::Section::DebugInfo, dw_offset_t(uid)}}; + } + dw_offset_t die_offset = uid; + if (die_offset == DW_INVALID_OFFSET) + return llvm::None; + + DIERef::Section section = + uid >> 63 ? DIERef::Section::DebugTypes : DIERef::Section::DebugInfo; + + llvm::Optional<uint32_t> dwo_num = uid >> 32 & 0x7fffffff; + if (*dwo_num == 0x7fffffff) + dwo_num = llvm::None; + + return DecodedUID{*this, {dwo_num, section, die_offset}}; +} + +DWARFDIE +SymbolFileDWARF::GetDIE(lldb::user_id_t uid) { + // This method can be called without going through the symbol vendor so we + // need to lock the module. + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + + llvm::Optional<DecodedUID> decoded = DecodeUID(uid); + + if (decoded) + return decoded->dwarf.GetDIE(decoded->ref); + + return DWARFDIE(); +} + +CompilerDecl SymbolFileDWARF::GetDeclForUID(lldb::user_id_t type_uid) { + // This method can be called without going through the symbol vendor so we + // need to lock the module. + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + // Anytime we have a lldb::user_id_t, we must get the DIE by calling + // SymbolFileDWARF::GetDIE(). See comments inside the + // SymbolFileDWARF::GetDIE() for details. + if (DWARFDIE die = GetDIE(type_uid)) + return die.GetDecl(); + return CompilerDecl(); +} + +CompilerDeclContext +SymbolFileDWARF::GetDeclContextForUID(lldb::user_id_t type_uid) { + // This method can be called without going through the symbol vendor so we + // need to lock the module. + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + // Anytime we have a lldb::user_id_t, we must get the DIE by calling + // SymbolFileDWARF::GetDIE(). See comments inside the + // SymbolFileDWARF::GetDIE() for details. + if (DWARFDIE die = GetDIE(type_uid)) + return die.GetDeclContext(); + return CompilerDeclContext(); +} + +CompilerDeclContext +SymbolFileDWARF::GetDeclContextContainingUID(lldb::user_id_t type_uid) { + // This method can be called without going through the symbol vendor so we + // need to lock the module. + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + // Anytime we have a lldb::user_id_t, we must get the DIE by calling + // SymbolFileDWARF::GetDIE(). See comments inside the + // SymbolFileDWARF::GetDIE() for details. + if (DWARFDIE die = GetDIE(type_uid)) + return die.GetContainingDeclContext(); + return CompilerDeclContext(); +} + +Type *SymbolFileDWARF::ResolveTypeUID(lldb::user_id_t type_uid) { + // This method can be called without going through the symbol vendor so we + // need to lock the module. + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + // Anytime we have a lldb::user_id_t, we must get the DIE by calling + // SymbolFileDWARF::GetDIE(). See comments inside the + // SymbolFileDWARF::GetDIE() for details. + if (DWARFDIE type_die = GetDIE(type_uid)) + return type_die.ResolveType(); + else + return nullptr; +} + +llvm::Optional<SymbolFile::ArrayInfo> +SymbolFileDWARF::GetDynamicArrayInfoForUID( + lldb::user_id_t type_uid, const lldb_private::ExecutionContext *exe_ctx) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + if (DWARFDIE type_die = GetDIE(type_uid)) + return DWARFASTParser::ParseChildArrayInfo(type_die, exe_ctx); + else + return llvm::None; +} + +Type *SymbolFileDWARF::ResolveTypeUID(const DIERef &die_ref) { + return ResolveType(GetDIE(die_ref), true); +} + +Type *SymbolFileDWARF::ResolveTypeUID(const DWARFDIE &die, + bool assert_not_being_parsed) { + if (die) { + Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_INFO)); + if (log) + GetObjectFile()->GetModule()->LogMessage( + log, "SymbolFileDWARF::ResolveTypeUID (die = 0x%8.8x) %s '%s'", + die.GetOffset(), die.GetTagAsCString(), die.GetName()); + + // We might be coming in in the middle of a type tree (a class within a + // class, an enum within a class), so parse any needed parent DIEs before + // we get to this one... + DWARFDIE decl_ctx_die = GetDeclContextDIEContainingDIE(die); + if (decl_ctx_die) { + if (log) { + switch (decl_ctx_die.Tag()) { + case DW_TAG_structure_type: + case DW_TAG_union_type: + case DW_TAG_class_type: { + // Get the type, which could be a forward declaration + if (log) + GetObjectFile()->GetModule()->LogMessage( + log, "SymbolFileDWARF::ResolveTypeUID (die = 0x%8.8x) %s '%s' " + "resolve parent forward type for 0x%8.8x", + die.GetOffset(), die.GetTagAsCString(), die.GetName(), + decl_ctx_die.GetOffset()); + } break; + + default: + break; + } + } + } + return ResolveType(die); + } + return nullptr; +} + +// This function is used when SymbolFileDWARFDebugMap owns a bunch of +// SymbolFileDWARF objects to detect if this DWARF file is the one that can +// resolve a compiler_type. +bool SymbolFileDWARF::HasForwardDeclForClangType( + const CompilerType &compiler_type) { + CompilerType compiler_type_no_qualifiers = + ClangUtil::RemoveFastQualifiers(compiler_type); + if (GetForwardDeclClangTypeToDie().count( + compiler_type_no_qualifiers.GetOpaqueQualType())) { + return true; + } + TypeSystem *type_system = compiler_type.GetTypeSystem(); + + ClangASTContext *clang_type_system = + llvm::dyn_cast_or_null<ClangASTContext>(type_system); + if (!clang_type_system) + return false; + DWARFASTParserClang *ast_parser = + static_cast<DWARFASTParserClang *>(clang_type_system->GetDWARFParser()); + return ast_parser->GetClangASTImporter().CanImport(compiler_type); +} + +bool SymbolFileDWARF::CompleteType(CompilerType &compiler_type) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + + ClangASTContext *clang_type_system = + llvm::dyn_cast_or_null<ClangASTContext>(compiler_type.GetTypeSystem()); + if (clang_type_system) { + DWARFASTParserClang *ast_parser = + static_cast<DWARFASTParserClang *>(clang_type_system->GetDWARFParser()); + if (ast_parser && + ast_parser->GetClangASTImporter().CanImport(compiler_type)) + return ast_parser->GetClangASTImporter().CompleteType(compiler_type); + } + + // We have a struct/union/class/enum that needs to be fully resolved. + CompilerType compiler_type_no_qualifiers = + ClangUtil::RemoveFastQualifiers(compiler_type); + auto die_it = GetForwardDeclClangTypeToDie().find( + compiler_type_no_qualifiers.GetOpaqueQualType()); + if (die_it == GetForwardDeclClangTypeToDie().end()) { + // We have already resolved this type... + return true; + } + + DWARFDIE dwarf_die = GetDIE(die_it->getSecond()); + if (dwarf_die) { + // Once we start resolving this type, remove it from the forward + // declaration map in case anyone child members or other types require this + // type to get resolved. The type will get resolved when all of the calls + // to SymbolFileDWARF::ResolveClangOpaqueTypeDefinition are done. + GetForwardDeclClangTypeToDie().erase(die_it); + + Type *type = GetDIEToType().lookup(dwarf_die.GetDIE()); + + Log *log(LogChannelDWARF::GetLogIfAny(DWARF_LOG_DEBUG_INFO | + DWARF_LOG_TYPE_COMPLETION)); + if (log) + GetObjectFile()->GetModule()->LogMessageVerboseBacktrace( + log, "0x%8.8" PRIx64 ": %s '%s' resolving forward declaration...", + dwarf_die.GetID(), dwarf_die.GetTagAsCString(), + type->GetName().AsCString()); + assert(compiler_type); + DWARFASTParser *dwarf_ast = dwarf_die.GetDWARFParser(); + if (dwarf_ast) + return dwarf_ast->CompleteTypeFromDWARF(dwarf_die, type, compiler_type); + } + return false; +} + +Type *SymbolFileDWARF::ResolveType(const DWARFDIE &die, + bool assert_not_being_parsed, + bool resolve_function_context) { + if (die) { + Type *type = GetTypeForDIE(die, resolve_function_context).get(); + + if (assert_not_being_parsed) { + if (type != DIE_IS_BEING_PARSED) + return type; + + GetObjectFile()->GetModule()->ReportError( + "Parsing a die that is being parsed die: 0x%8.8x: %s %s", + die.GetOffset(), die.GetTagAsCString(), die.GetName()); + + } else + return type; + } + return nullptr; +} + +CompileUnit * +SymbolFileDWARF::GetCompUnitForDWARFCompUnit(DWARFCompileUnit &dwarf_cu) { + // Check if the symbol vendor already knows about this compile unit? + if (dwarf_cu.GetUserData() == nullptr) { + // The symbol vendor doesn't know about this compile unit, we need to parse + // and add it to the symbol vendor object. + return ParseCompileUnit(dwarf_cu).get(); + } + return (CompileUnit *)dwarf_cu.GetUserData(); +} + +size_t SymbolFileDWARF::GetObjCMethodDIEOffsets(ConstString class_name, + DIEArray &method_die_offsets) { + method_die_offsets.clear(); + m_index->GetObjCMethods(class_name, method_die_offsets); + return method_die_offsets.size(); +} + +bool SymbolFileDWARF::GetFunction(const DWARFDIE &die, SymbolContext &sc) { + sc.Clear(false); + + if (die && llvm::isa<DWARFCompileUnit>(die.GetCU())) { + // Check if the symbol vendor already knows about this compile unit? + sc.comp_unit = + GetCompUnitForDWARFCompUnit(llvm::cast<DWARFCompileUnit>(*die.GetCU())); + + sc.function = sc.comp_unit->FindFunctionByUID(die.GetID()).get(); + if (sc.function == nullptr) + sc.function = ParseFunction(*sc.comp_unit, die); + + if (sc.function) { + sc.module_sp = sc.function->CalculateSymbolContextModule(); + return true; + } + } + + return false; +} + +lldb::ModuleSP SymbolFileDWARF::GetDWOModule(ConstString name) { + UpdateExternalModuleListIfNeeded(); + const auto &pos = m_external_type_modules.find(name); + if (pos != m_external_type_modules.end()) + return pos->second; + else + return lldb::ModuleSP(); +} + +DWARFDIE +SymbolFileDWARF::GetDIE(const DIERef &die_ref) { + if (die_ref.dwo_num()) { + return DebugInfo() + ->GetUnitAtIndex(*die_ref.dwo_num()) + ->GetDwoSymbolFile() + ->GetDIE(die_ref); + } + + + DWARFDebugInfo *debug_info = DebugInfo(); + if (debug_info) + return debug_info->GetDIE(die_ref); + else + return DWARFDIE(); +} + +std::unique_ptr<SymbolFileDWARFDwo> +SymbolFileDWARF::GetDwoSymbolFileForCompileUnit( + DWARFUnit &unit, const DWARFDebugInfoEntry &cu_die) { + // If we are using a dSYM file, we never want the standard DWO files since + // the -gmodules support uses the same DWO machanism to specify full debug + // info files for modules. + if (GetDebugMapSymfile()) + return nullptr; + + DWARFCompileUnit *dwarf_cu = llvm::dyn_cast<DWARFCompileUnit>(&unit); + // Only compile units can be split into two parts. + if (!dwarf_cu) + return nullptr; + + const char *dwo_name = + cu_die.GetAttributeValueAsString(dwarf_cu, DW_AT_GNU_dwo_name, nullptr); + if (!dwo_name) + return nullptr; + + SymbolFileDWARFDwp *dwp_symfile = GetDwpSymbolFile(); + if (dwp_symfile) { + uint64_t dwo_id = + cu_die.GetAttributeValueAsUnsigned(dwarf_cu, DW_AT_GNU_dwo_id, 0); + std::unique_ptr<SymbolFileDWARFDwo> dwo_symfile = + dwp_symfile->GetSymbolFileForDwoId(*dwarf_cu, dwo_id); + if (dwo_symfile) + return dwo_symfile; + } + + FileSpec dwo_file(dwo_name); + FileSystem::Instance().Resolve(dwo_file); + if (dwo_file.IsRelative()) { + const char *comp_dir = + cu_die.GetAttributeValueAsString(dwarf_cu, DW_AT_comp_dir, nullptr); + if (!comp_dir) + return nullptr; + + dwo_file.SetFile(comp_dir, FileSpec::Style::native); + FileSystem::Instance().Resolve(dwo_file); + dwo_file.AppendPathComponent(dwo_name); + } + + if (!FileSystem::Instance().Exists(dwo_file)) + return nullptr; + + const lldb::offset_t file_offset = 0; + DataBufferSP dwo_file_data_sp; + lldb::offset_t dwo_file_data_offset = 0; + ObjectFileSP dwo_obj_file = ObjectFile::FindPlugin( + GetObjectFile()->GetModule(), &dwo_file, file_offset, + FileSystem::Instance().GetByteSize(dwo_file), dwo_file_data_sp, + dwo_file_data_offset); + if (dwo_obj_file == nullptr) + return nullptr; + + return llvm::make_unique<SymbolFileDWARFDwo>(dwo_obj_file, *dwarf_cu); +} + +void SymbolFileDWARF::UpdateExternalModuleListIfNeeded() { + if (m_fetched_external_modules) + return; + m_fetched_external_modules = true; + + DWARFDebugInfo *debug_info = DebugInfo(); + + const uint32_t num_compile_units = GetNumCompileUnits(); + for (uint32_t cu_idx = 0; cu_idx < num_compile_units; ++cu_idx) { + DWARFUnit *dwarf_cu = debug_info->GetUnitAtIndex(cu_idx); + + const DWARFBaseDIE die = dwarf_cu->GetUnitDIEOnly(); + if (die && !die.HasChildren()) { + const char *name = die.GetAttributeValueAsString(DW_AT_name, nullptr); + + if (name) { + ConstString const_name(name); + if (m_external_type_modules.find(const_name) == + m_external_type_modules.end()) { + ModuleSP module_sp; + const char *dwo_path = + die.GetAttributeValueAsString(DW_AT_GNU_dwo_name, nullptr); + if (dwo_path) { + ModuleSpec dwo_module_spec; + dwo_module_spec.GetFileSpec().SetFile(dwo_path, + FileSpec::Style::native); + if (dwo_module_spec.GetFileSpec().IsRelative()) { + const char *comp_dir = + die.GetAttributeValueAsString(DW_AT_comp_dir, nullptr); + if (comp_dir) { + dwo_module_spec.GetFileSpec().SetFile(comp_dir, + FileSpec::Style::native); + FileSystem::Instance().Resolve(dwo_module_spec.GetFileSpec()); + dwo_module_spec.GetFileSpec().AppendPathComponent(dwo_path); + } + } + dwo_module_spec.GetArchitecture() = + m_obj_file->GetModule()->GetArchitecture(); + + // When LLDB loads "external" modules it looks at the presence of + // DW_AT_GNU_dwo_name. However, when the already created module + // (corresponding to .dwo itself) is being processed, it will see + // the presence of DW_AT_GNU_dwo_name (which contains the name of + // dwo file) and will try to call ModuleList::GetSharedModule + // again. In some cases (i.e. for empty files) Clang 4.0 generates + // a *.dwo file which has DW_AT_GNU_dwo_name, but no + // DW_AT_comp_dir. In this case the method + // ModuleList::GetSharedModule will fail and the warning will be + // printed. However, as one can notice in this case we don't + // actually need to try to load the already loaded module + // (corresponding to .dwo) so we simply skip it. + if (m_obj_file->GetFileSpec().GetFileNameExtension() == ".dwo" && + llvm::StringRef(m_obj_file->GetFileSpec().GetPath()) + .endswith(dwo_module_spec.GetFileSpec().GetPath())) { + continue; + } + + Status error = ModuleList::GetSharedModule( + dwo_module_spec, module_sp, nullptr, nullptr, nullptr); + if (!module_sp) { + GetObjectFile()->GetModule()->ReportWarning( + "0x%8.8x: unable to locate module needed for external types: " + "%s\nerror: %s\nDebugging will be degraded due to missing " + "types. Rebuilding your project will regenerate the needed " + "module files.", + die.GetOffset(), + dwo_module_spec.GetFileSpec().GetPath().c_str(), + error.AsCString("unknown error")); + } + } + m_external_type_modules[const_name] = module_sp; + } + } + } + } +} + +SymbolFileDWARF::GlobalVariableMap &SymbolFileDWARF::GetGlobalAranges() { + if (!m_global_aranges_up) { + m_global_aranges_up.reset(new GlobalVariableMap()); + + ModuleSP module_sp = GetObjectFile()->GetModule(); + if (module_sp) { + const size_t num_cus = module_sp->GetNumCompileUnits(); + for (size_t i = 0; i < num_cus; ++i) { + CompUnitSP cu_sp = module_sp->GetCompileUnitAtIndex(i); + if (cu_sp) { + VariableListSP globals_sp = cu_sp->GetVariableList(true); + if (globals_sp) { + const size_t num_globals = globals_sp->GetSize(); + for (size_t g = 0; g < num_globals; ++g) { + VariableSP var_sp = globals_sp->GetVariableAtIndex(g); + if (var_sp && !var_sp->GetLocationIsConstantValueData()) { + const DWARFExpression &location = var_sp->LocationExpression(); + Value location_result; + Status error; + if (location.Evaluate(nullptr, LLDB_INVALID_ADDRESS, nullptr, + nullptr, location_result, &error)) { + if (location_result.GetValueType() == + Value::eValueTypeFileAddress) { + lldb::addr_t file_addr = + location_result.GetScalar().ULongLong(); + lldb::addr_t byte_size = 1; + if (var_sp->GetType()) + byte_size = + var_sp->GetType()->GetByteSize().getValueOr(0); + m_global_aranges_up->Append(GlobalVariableMap::Entry( + file_addr, byte_size, var_sp.get())); + } + } + } + } + } + } + } + } + m_global_aranges_up->Sort(); + } + return *m_global_aranges_up; +} + +uint32_t SymbolFileDWARF::ResolveSymbolContext(const Address &so_addr, + SymbolContextItem resolve_scope, + SymbolContext &sc) { + static Timer::Category func_cat(LLVM_PRETTY_FUNCTION); + Timer scoped_timer(func_cat, + "SymbolFileDWARF::" + "ResolveSymbolContext (so_addr = { " + "section = %p, offset = 0x%" PRIx64 + " }, resolve_scope = 0x%8.8x)", + static_cast<void *>(so_addr.GetSection().get()), + so_addr.GetOffset(), resolve_scope); + uint32_t resolved = 0; + if (resolve_scope & + (eSymbolContextCompUnit | eSymbolContextFunction | eSymbolContextBlock | + eSymbolContextLineEntry | eSymbolContextVariable)) { + lldb::addr_t file_vm_addr = so_addr.GetFileAddress(); + + DWARFDebugInfo *debug_info = DebugInfo(); + if (debug_info) { + llvm::Expected<DWARFDebugAranges &> aranges = + debug_info->GetCompileUnitAranges(); + if (!aranges) { + Log *log = LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_INFO); + LLDB_LOG_ERROR(log, aranges.takeError(), + "SymbolFileDWARF::ResolveSymbolContext failed to get cu " + "aranges. {0}"); + return 0; + } + + const dw_offset_t cu_offset = aranges->FindAddress(file_vm_addr); + if (cu_offset == DW_INVALID_OFFSET) { + // Global variables are not in the compile unit address ranges. The + // only way to currently find global variables is to iterate over the + // .debug_pubnames or the __apple_names table and find all items in + // there that point to DW_TAG_variable DIEs and then find the address + // that matches. + if (resolve_scope & eSymbolContextVariable) { + GlobalVariableMap &map = GetGlobalAranges(); + const GlobalVariableMap::Entry *entry = + map.FindEntryThatContains(file_vm_addr); + if (entry && entry->data) { + Variable *variable = entry->data; + SymbolContextScope *scc = variable->GetSymbolContextScope(); + if (scc) { + scc->CalculateSymbolContext(&sc); + sc.variable = variable; + } + return sc.GetResolvedMask(); + } + } + } else { + uint32_t cu_idx = DW_INVALID_INDEX; + if (auto *dwarf_cu = llvm::dyn_cast_or_null<DWARFCompileUnit>( + debug_info->GetUnitAtOffset(DIERef::Section::DebugInfo, + cu_offset, &cu_idx))) { + sc.comp_unit = GetCompUnitForDWARFCompUnit(*dwarf_cu); + if (sc.comp_unit) { + resolved |= eSymbolContextCompUnit; + + bool force_check_line_table = false; + if (resolve_scope & + (eSymbolContextFunction | eSymbolContextBlock)) { + DWARFDIE function_die = dwarf_cu->LookupAddress(file_vm_addr); + DWARFDIE block_die; + if (function_die) { + sc.function = + sc.comp_unit->FindFunctionByUID(function_die.GetID()).get(); + if (sc.function == nullptr) + sc.function = ParseFunction(*sc.comp_unit, function_die); + + if (sc.function && (resolve_scope & eSymbolContextBlock)) + block_die = function_die.LookupDeepestBlock(file_vm_addr); + } else { + // We might have had a compile unit that had discontiguous + // address ranges where the gaps are symbols that don't have + // any debug info. Discontiguous compile unit address ranges + // should only happen when there aren't other functions from + // other compile units in these gaps. This helps keep the size + // of the aranges down. + force_check_line_table = true; + } + + if (sc.function != nullptr) { + resolved |= eSymbolContextFunction; + + if (resolve_scope & eSymbolContextBlock) { + Block &block = sc.function->GetBlock(true); + + if (block_die) + sc.block = block.FindBlockByID(block_die.GetID()); + else + sc.block = block.FindBlockByID(function_die.GetID()); + if (sc.block) + resolved |= eSymbolContextBlock; + } + } + } + + if ((resolve_scope & eSymbolContextLineEntry) || + force_check_line_table) { + LineTable *line_table = sc.comp_unit->GetLineTable(); + if (line_table != nullptr) { + // And address that makes it into this function should be in + // terms of this debug file if there is no debug map, or it + // will be an address in the .o file which needs to be fixed up + // to be in terms of the debug map executable. Either way, + // calling FixupAddress() will work for us. + Address exe_so_addr(so_addr); + if (FixupAddress(exe_so_addr)) { + if (line_table->FindLineEntryByAddress(exe_so_addr, + sc.line_entry)) { + resolved |= eSymbolContextLineEntry; + } + } + } + } + + if (force_check_line_table && + !(resolved & eSymbolContextLineEntry)) { + // We might have had a compile unit that had discontiguous + // address ranges where the gaps are symbols that don't have any + // debug info. Discontiguous compile unit address ranges should + // only happen when there aren't other functions from other + // compile units in these gaps. This helps keep the size of the + // aranges down. + sc.comp_unit = nullptr; + resolved &= ~eSymbolContextCompUnit; + } + } else { + GetObjectFile()->GetModule()->ReportWarning( + "0x%8.8x: compile unit %u failed to create a valid " + "lldb_private::CompileUnit class.", + cu_offset, cu_idx); + } + } + } + } + } + return resolved; +} + +uint32_t SymbolFileDWARF::ResolveSymbolContext(const FileSpec &file_spec, + uint32_t line, + bool check_inlines, + SymbolContextItem resolve_scope, + SymbolContextList &sc_list) { + const uint32_t prev_size = sc_list.GetSize(); + if (resolve_scope & eSymbolContextCompUnit) { + for (uint32_t cu_idx = 0, num_cus = GetNumCompileUnits(); cu_idx < num_cus; + ++cu_idx) { + CompileUnit *dc_cu = ParseCompileUnitAtIndex(cu_idx).get(); + if (!dc_cu) + continue; + + const bool full_match = (bool)file_spec.GetDirectory(); + bool file_spec_matches_cu_file_spec = + FileSpec::Equal(file_spec, *dc_cu, full_match); + if (check_inlines || file_spec_matches_cu_file_spec) { + SymbolContext sc(m_obj_file->GetModule()); + sc.comp_unit = dc_cu; + uint32_t file_idx = UINT32_MAX; + + // If we are looking for inline functions only and we don't find it + // in the support files, we are done. + if (check_inlines) { + file_idx = + sc.comp_unit->GetSupportFiles().FindFileIndex(1, file_spec, true); + if (file_idx == UINT32_MAX) + continue; + } + + if (line != 0) { + LineTable *line_table = sc.comp_unit->GetLineTable(); + + if (line_table != nullptr && line != 0) { + // We will have already looked up the file index if we are + // searching for inline entries. + if (!check_inlines) + file_idx = sc.comp_unit->GetSupportFiles().FindFileIndex( + 1, file_spec, true); + + if (file_idx != UINT32_MAX) { + uint32_t found_line; + uint32_t line_idx = line_table->FindLineEntryIndexByFileIndex( + 0, file_idx, line, false, &sc.line_entry); + found_line = sc.line_entry.line; + + while (line_idx != UINT32_MAX) { + sc.function = nullptr; + sc.block = nullptr; + if (resolve_scope & + (eSymbolContextFunction | eSymbolContextBlock)) { + const lldb::addr_t file_vm_addr = + sc.line_entry.range.GetBaseAddress().GetFileAddress(); + if (file_vm_addr != LLDB_INVALID_ADDRESS) { + DWARFDIE function_die = + GetDWARFCompileUnit(dc_cu)->LookupAddress(file_vm_addr); + DWARFDIE block_die; + if (function_die) { + sc.function = + sc.comp_unit->FindFunctionByUID(function_die.GetID()) + .get(); + if (sc.function == nullptr) + sc.function = + ParseFunction(*sc.comp_unit, function_die); + + if (sc.function && (resolve_scope & eSymbolContextBlock)) + block_die = + function_die.LookupDeepestBlock(file_vm_addr); + } + + if (sc.function != nullptr) { + Block &block = sc.function->GetBlock(true); + + if (block_die) + sc.block = block.FindBlockByID(block_die.GetID()); + else if (function_die) + sc.block = block.FindBlockByID(function_die.GetID()); + } + } + } + + sc_list.Append(sc); + line_idx = line_table->FindLineEntryIndexByFileIndex( + line_idx + 1, file_idx, found_line, true, &sc.line_entry); + } + } + } else if (file_spec_matches_cu_file_spec && !check_inlines) { + // only append the context if we aren't looking for inline call + // sites by file and line and if the file spec matches that of + // the compile unit + sc_list.Append(sc); + } + } else if (file_spec_matches_cu_file_spec && !check_inlines) { + // only append the context if we aren't looking for inline call + // sites by file and line and if the file spec matches that of + // the compile unit + sc_list.Append(sc); + } + + if (!check_inlines) + break; + } + } + } + return sc_list.GetSize() - prev_size; +} + +void SymbolFileDWARF::PreloadSymbols() { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + m_index->Preload(); +} + +std::recursive_mutex &SymbolFileDWARF::GetModuleMutex() const { + lldb::ModuleSP module_sp(m_debug_map_module_wp.lock()); + if (module_sp) + return module_sp->GetMutex(); + return GetObjectFile()->GetModule()->GetMutex(); +} + +bool SymbolFileDWARF::DeclContextMatchesThisSymbolFile( + const lldb_private::CompilerDeclContext *decl_ctx) { + if (decl_ctx == nullptr || !decl_ctx->IsValid()) { + // Invalid namespace decl which means we aren't matching only things in + // this symbol file, so return true to indicate it matches this symbol + // file. + return true; + } + + TypeSystem *decl_ctx_type_system = decl_ctx->GetTypeSystem(); + TypeSystem *type_system = GetTypeSystemForLanguage( + decl_ctx_type_system->GetMinimumLanguage(nullptr)); + if (decl_ctx_type_system == type_system) + return true; // The type systems match, return true + + // The namespace AST was valid, and it does not match... + Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS)); + + if (log) + GetObjectFile()->GetModule()->LogMessage( + log, "Valid namespace does not match symbol file"); + + return false; +} + +uint32_t SymbolFileDWARF::FindGlobalVariables( + ConstString name, const CompilerDeclContext *parent_decl_ctx, + uint32_t max_matches, VariableList &variables) { + Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS)); + + if (log) + GetObjectFile()->GetModule()->LogMessage( + log, + "SymbolFileDWARF::FindGlobalVariables (name=\"%s\", " + "parent_decl_ctx=%p, max_matches=%u, variables)", + name.GetCString(), static_cast<const void *>(parent_decl_ctx), + max_matches); + + if (!DeclContextMatchesThisSymbolFile(parent_decl_ctx)) + return 0; + + DWARFDebugInfo *info = DebugInfo(); + if (info == nullptr) + return 0; + + // Remember how many variables are in the list before we search. + const uint32_t original_size = variables.GetSize(); + + llvm::StringRef basename; + llvm::StringRef context; + bool name_is_mangled = (bool)Mangled(name); + + if (!CPlusPlusLanguage::ExtractContextAndIdentifier(name.GetCString(), + context, basename)) + basename = name.GetStringRef(); + + DIEArray die_offsets; + m_index->GetGlobalVariables(ConstString(basename), die_offsets); + const size_t num_die_matches = die_offsets.size(); + if (num_die_matches) { + SymbolContext sc; + sc.module_sp = m_obj_file->GetModule(); + assert(sc.module_sp); + + // Loop invariant: Variables up to this index have been checked for context + // matches. + uint32_t pruned_idx = original_size; + + bool done = false; + for (size_t i = 0; i < num_die_matches && !done; ++i) { + const DIERef &die_ref = die_offsets[i]; + DWARFDIE die = GetDIE(die_ref); + + if (die) { + switch (die.Tag()) { + default: + case DW_TAG_subprogram: + case DW_TAG_inlined_subroutine: + case DW_TAG_try_block: + case DW_TAG_catch_block: + break; + + case DW_TAG_variable: { + auto *dwarf_cu = llvm::dyn_cast<DWARFCompileUnit>(die.GetCU()); + if (!dwarf_cu) + continue; + sc.comp_unit = GetCompUnitForDWARFCompUnit(*dwarf_cu); + + if (parent_decl_ctx) { + DWARFASTParser *dwarf_ast = die.GetDWARFParser(); + if (dwarf_ast) { + CompilerDeclContext actual_parent_decl_ctx = + dwarf_ast->GetDeclContextContainingUIDFromDWARF(die); + if (!actual_parent_decl_ctx || + actual_parent_decl_ctx != *parent_decl_ctx) + continue; + } + } + + ParseVariables(sc, die, LLDB_INVALID_ADDRESS, false, false, + &variables); + while (pruned_idx < variables.GetSize()) { + VariableSP var_sp = variables.GetVariableAtIndex(pruned_idx); + if (name_is_mangled || + var_sp->GetName().GetStringRef().contains(name.GetStringRef())) + ++pruned_idx; + else + variables.RemoveVariableAtIndex(pruned_idx); + } + + if (variables.GetSize() - original_size >= max_matches) + done = true; + } break; + } + } else { + m_index->ReportInvalidDIERef(die_ref, name.GetStringRef()); + } + } + } + + // Return the number of variable that were appended to the list + const uint32_t num_matches = variables.GetSize() - original_size; + if (log && num_matches > 0) { + GetObjectFile()->GetModule()->LogMessage( + log, + "SymbolFileDWARF::FindGlobalVariables (name=\"%s\", " + "parent_decl_ctx=%p, max_matches=%u, variables) => %u", + name.GetCString(), static_cast<const void *>(parent_decl_ctx), + max_matches, num_matches); + } + return num_matches; +} + +uint32_t SymbolFileDWARF::FindGlobalVariables(const RegularExpression ®ex, + uint32_t max_matches, + VariableList &variables) { + Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS)); + + if (log) { + GetObjectFile()->GetModule()->LogMessage( + log, + "SymbolFileDWARF::FindGlobalVariables (regex=\"%s\", " + "max_matches=%u, variables)", + regex.GetText().str().c_str(), max_matches); + } + + DWARFDebugInfo *info = DebugInfo(); + if (info == nullptr) + return 0; + + // Remember how many variables are in the list before we search. + const uint32_t original_size = variables.GetSize(); + + DIEArray die_offsets; + m_index->GetGlobalVariables(regex, die_offsets); + + SymbolContext sc; + sc.module_sp = m_obj_file->GetModule(); + assert(sc.module_sp); + + const size_t num_matches = die_offsets.size(); + if (num_matches) { + for (size_t i = 0; i < num_matches; ++i) { + const DIERef &die_ref = die_offsets[i]; + DWARFDIE die = GetDIE(die_ref); + + if (die) { + DWARFCompileUnit *dwarf_cu = + llvm::dyn_cast<DWARFCompileUnit>(die.GetCU()); + if (!dwarf_cu) + continue; + sc.comp_unit = GetCompUnitForDWARFCompUnit(*dwarf_cu); + + ParseVariables(sc, die, LLDB_INVALID_ADDRESS, false, false, &variables); + + if (variables.GetSize() - original_size >= max_matches) + break; + } else + m_index->ReportInvalidDIERef(die_ref, regex.GetText()); + } + } + + // Return the number of variable that were appended to the list + return variables.GetSize() - original_size; +} + +bool SymbolFileDWARF::ResolveFunction(const DWARFDIE &orig_die, + bool include_inlines, + SymbolContextList &sc_list) { + SymbolContext sc; + + if (!orig_die) + return false; + + // If we were passed a die that is not a function, just return false... + if (!(orig_die.Tag() == DW_TAG_subprogram || + (include_inlines && orig_die.Tag() == DW_TAG_inlined_subroutine))) + return false; + + DWARFDIE die = orig_die; + DWARFDIE inlined_die; + if (die.Tag() == DW_TAG_inlined_subroutine) { + inlined_die = die; + + while (true) { + die = die.GetParent(); + + if (die) { + if (die.Tag() == DW_TAG_subprogram) + break; + } else + break; + } + } + assert(die && die.Tag() == DW_TAG_subprogram); + if (GetFunction(die, sc)) { + Address addr; + // Parse all blocks if needed + if (inlined_die) { + Block &function_block = sc.function->GetBlock(true); + sc.block = function_block.FindBlockByID(inlined_die.GetID()); + if (sc.block == nullptr) + sc.block = function_block.FindBlockByID(inlined_die.GetOffset()); + if (sc.block == nullptr || !sc.block->GetStartAddress(addr)) + addr.Clear(); + } else { + sc.block = nullptr; + addr = sc.function->GetAddressRange().GetBaseAddress(); + } + + if (addr.IsValid()) { + sc_list.Append(sc); + return true; + } + } + + return false; +} + +bool SymbolFileDWARF::DIEInDeclContext(const CompilerDeclContext *decl_ctx, + const DWARFDIE &die) { + // If we have no parent decl context to match this DIE matches, and if the + // parent decl context isn't valid, we aren't trying to look for any + // particular decl context so any die matches. + if (decl_ctx == nullptr || !decl_ctx->IsValid()) + return true; + + if (die) { + DWARFASTParser *dwarf_ast = die.GetDWARFParser(); + if (dwarf_ast) { + CompilerDeclContext actual_decl_ctx = + dwarf_ast->GetDeclContextContainingUIDFromDWARF(die); + if (actual_decl_ctx) + return decl_ctx->IsContainedInLookup(actual_decl_ctx); + } + } + return false; +} + +uint32_t SymbolFileDWARF::FindFunctions( + ConstString name, const CompilerDeclContext *parent_decl_ctx, + FunctionNameType name_type_mask, bool include_inlines, bool append, + SymbolContextList &sc_list) { + static Timer::Category func_cat(LLVM_PRETTY_FUNCTION); + Timer scoped_timer(func_cat, "SymbolFileDWARF::FindFunctions (name = '%s')", + name.AsCString()); + + // eFunctionNameTypeAuto should be pre-resolved by a call to + // Module::LookupInfo::LookupInfo() + assert((name_type_mask & eFunctionNameTypeAuto) == 0); + + Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS)); + + if (log) { + GetObjectFile()->GetModule()->LogMessage( + log, "SymbolFileDWARF::FindFunctions (name=\"%s\", " + "name_type_mask=0x%x, append=%u, sc_list)", + name.GetCString(), name_type_mask, append); + } + + // If we aren't appending the results to this list, then clear the list + if (!append) + sc_list.Clear(); + + if (!DeclContextMatchesThisSymbolFile(parent_decl_ctx)) + return 0; + + // If name is empty then we won't find anything. + if (name.IsEmpty()) + return 0; + + // Remember how many sc_list are in the list before we search in case we are + // appending the results to a variable list. + + const uint32_t original_size = sc_list.GetSize(); + + llvm::DenseSet<const DWARFDebugInfoEntry *> resolved_dies; + DIEArray offsets; + CompilerDeclContext empty_decl_ctx; + if (!parent_decl_ctx) + parent_decl_ctx = &empty_decl_ctx; + + std::vector<DWARFDIE> dies; + m_index->GetFunctions(name, *this, *parent_decl_ctx, name_type_mask, dies); + for (const DWARFDIE &die: dies) { + if (resolved_dies.insert(die.GetDIE()).second) + ResolveFunction(die, include_inlines, sc_list); + } + + // Return the number of variable that were appended to the list + const uint32_t num_matches = sc_list.GetSize() - original_size; + + if (log && num_matches > 0) { + GetObjectFile()->GetModule()->LogMessage( + log, "SymbolFileDWARF::FindFunctions (name=\"%s\", " + "name_type_mask=0x%x, include_inlines=%d, append=%u, sc_list) => " + "%u", + name.GetCString(), name_type_mask, include_inlines, append, + num_matches); + } + return num_matches; +} + +uint32_t SymbolFileDWARF::FindFunctions(const RegularExpression ®ex, + bool include_inlines, bool append, + SymbolContextList &sc_list) { + static Timer::Category func_cat(LLVM_PRETTY_FUNCTION); + Timer scoped_timer(func_cat, "SymbolFileDWARF::FindFunctions (regex = '%s')", + regex.GetText().str().c_str()); + + Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS)); + + if (log) { + GetObjectFile()->GetModule()->LogMessage( + log, + "SymbolFileDWARF::FindFunctions (regex=\"%s\", append=%u, sc_list)", + regex.GetText().str().c_str(), append); + } + + // If we aren't appending the results to this list, then clear the list + if (!append) + sc_list.Clear(); + + DWARFDebugInfo *info = DebugInfo(); + if (!info) + return 0; + + // Remember how many sc_list are in the list before we search in case we are + // appending the results to a variable list. + uint32_t original_size = sc_list.GetSize(); + + DIEArray offsets; + m_index->GetFunctions(regex, offsets); + + llvm::DenseSet<const DWARFDebugInfoEntry *> resolved_dies; + for (DIERef ref : offsets) { + DWARFDIE die = info->GetDIE(ref); + if (!die) { + m_index->ReportInvalidDIERef(ref, regex.GetText()); + continue; + } + if (resolved_dies.insert(die.GetDIE()).second) + ResolveFunction(die, include_inlines, sc_list); + } + + // Return the number of variable that were appended to the list + return sc_list.GetSize() - original_size; +} + +void SymbolFileDWARF::GetMangledNamesForFunction( + const std::string &scope_qualified_name, + std::vector<ConstString> &mangled_names) { + DWARFDebugInfo *info = DebugInfo(); + uint32_t num_comp_units = 0; + if (info) + num_comp_units = info->GetNumUnits(); + + for (uint32_t i = 0; i < num_comp_units; i++) { + DWARFUnit *cu = info->GetUnitAtIndex(i); + if (cu == nullptr) + continue; + + SymbolFileDWARFDwo *dwo = cu->GetDwoSymbolFile(); + if (dwo) + dwo->GetMangledNamesForFunction(scope_qualified_name, mangled_names); + } + + for (lldb::user_id_t uid : + m_function_scope_qualified_name_map.lookup(scope_qualified_name)) { + DWARFDIE die = GetDIE(uid); + mangled_names.push_back(ConstString(die.GetMangledName())); + } +} + +uint32_t SymbolFileDWARF::FindTypes( + ConstString name, const CompilerDeclContext *parent_decl_ctx, + bool append, uint32_t max_matches, + llvm::DenseSet<lldb_private::SymbolFile *> &searched_symbol_files, + TypeMap &types) { + // If we aren't appending the results to this list, then clear the list + if (!append) + types.Clear(); + + // Make sure we haven't already searched this SymbolFile before... + if (searched_symbol_files.count(this)) + return 0; + else + searched_symbol_files.insert(this); + + DWARFDebugInfo *info = DebugInfo(); + if (info == nullptr) + return 0; + + Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS)); + + if (log) { + if (parent_decl_ctx) + GetObjectFile()->GetModule()->LogMessage( + log, "SymbolFileDWARF::FindTypes (sc, name=\"%s\", parent_decl_ctx = " + "%p (\"%s\"), append=%u, max_matches=%u, type_list)", + name.GetCString(), static_cast<const void *>(parent_decl_ctx), + parent_decl_ctx->GetName().AsCString("<NULL>"), append, max_matches); + else + GetObjectFile()->GetModule()->LogMessage( + log, "SymbolFileDWARF::FindTypes (sc, name=\"%s\", parent_decl_ctx = " + "NULL, append=%u, max_matches=%u, type_list)", + name.GetCString(), append, max_matches); + } + + if (!DeclContextMatchesThisSymbolFile(parent_decl_ctx)) + return 0; + + DIEArray die_offsets; + m_index->GetTypes(name, die_offsets); + const size_t num_die_matches = die_offsets.size(); + + if (num_die_matches) { + const uint32_t initial_types_size = types.GetSize(); + for (size_t i = 0; i < num_die_matches; ++i) { + const DIERef &die_ref = die_offsets[i]; + DWARFDIE die = GetDIE(die_ref); + + if (die) { + if (!DIEInDeclContext(parent_decl_ctx, die)) + continue; // The containing decl contexts don't match + + Type *matching_type = ResolveType(die, true, true); + if (matching_type) { + // We found a type pointer, now find the shared pointer form our type + // list + types.InsertUnique(matching_type->shared_from_this()); + if (types.GetSize() >= max_matches) + break; + } + } else { + m_index->ReportInvalidDIERef(die_ref, name.GetStringRef()); + } + } + const uint32_t num_matches = types.GetSize() - initial_types_size; + if (log && num_matches) { + if (parent_decl_ctx) { + GetObjectFile()->GetModule()->LogMessage( + log, "SymbolFileDWARF::FindTypes (sc, name=\"%s\", parent_decl_ctx " + "= %p (\"%s\"), append=%u, max_matches=%u, type_list) => %u", + name.GetCString(), static_cast<const void *>(parent_decl_ctx), + parent_decl_ctx->GetName().AsCString("<NULL>"), append, max_matches, + num_matches); + } else { + GetObjectFile()->GetModule()->LogMessage( + log, "SymbolFileDWARF::FindTypes (sc, name=\"%s\", parent_decl_ctx " + "= NULL, append=%u, max_matches=%u, type_list) => %u", + name.GetCString(), append, max_matches, num_matches); + } + } + return num_matches; + } else { + UpdateExternalModuleListIfNeeded(); + + for (const auto &pair : m_external_type_modules) { + ModuleSP external_module_sp = pair.second; + if (external_module_sp) { + SymbolVendor *sym_vendor = external_module_sp->GetSymbolVendor(); + if (sym_vendor) { + const uint32_t num_external_matches = + sym_vendor->FindTypes(name, parent_decl_ctx, append, max_matches, + searched_symbol_files, types); + if (num_external_matches) + return num_external_matches; + } + } + } + } + + return 0; +} + +size_t SymbolFileDWARF::FindTypes(const std::vector<CompilerContext> &context, + bool append, TypeMap &types) { + if (!append) + types.Clear(); + + if (context.empty()) + return 0; + + ConstString name = context.back().name; + + if (!name) + return 0; + + DIEArray die_offsets; + m_index->GetTypes(name, die_offsets); + const size_t num_die_matches = die_offsets.size(); + + if (num_die_matches) { + size_t num_matches = 0; + for (size_t i = 0; i < num_die_matches; ++i) { + const DIERef &die_ref = die_offsets[i]; + DWARFDIE die = GetDIE(die_ref); + + if (die) { + std::vector<CompilerContext> die_context; + die.GetDeclContext(die_context); + if (die_context != context) + continue; + + Type *matching_type = ResolveType(die, true, true); + if (matching_type) { + // We found a type pointer, now find the shared pointer form our type + // list + types.InsertUnique(matching_type->shared_from_this()); + ++num_matches; + } + } else { + m_index->ReportInvalidDIERef(die_ref, name.GetStringRef()); + } + } + return num_matches; + } + return 0; +} + +CompilerDeclContext +SymbolFileDWARF::FindNamespace(ConstString name, + const CompilerDeclContext *parent_decl_ctx) { + Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS)); + + if (log) { + GetObjectFile()->GetModule()->LogMessage( + log, "SymbolFileDWARF::FindNamespace (sc, name=\"%s\")", + name.GetCString()); + } + + CompilerDeclContext namespace_decl_ctx; + + if (!DeclContextMatchesThisSymbolFile(parent_decl_ctx)) + return namespace_decl_ctx; + + DWARFDebugInfo *info = DebugInfo(); + if (info) { + DIEArray die_offsets; + m_index->GetNamespaces(name, die_offsets); + const size_t num_matches = die_offsets.size(); + if (num_matches) { + for (size_t i = 0; i < num_matches; ++i) { + const DIERef &die_ref = die_offsets[i]; + DWARFDIE die = GetDIE(die_ref); + + if (die) { + if (!DIEInDeclContext(parent_decl_ctx, die)) + continue; // The containing decl contexts don't match + + DWARFASTParser *dwarf_ast = die.GetDWARFParser(); + if (dwarf_ast) { + namespace_decl_ctx = dwarf_ast->GetDeclContextForUIDFromDWARF(die); + if (namespace_decl_ctx) + break; + } + } else { + m_index->ReportInvalidDIERef(die_ref, name.GetStringRef()); + } + } + } + } + if (log && namespace_decl_ctx) { + GetObjectFile()->GetModule()->LogMessage( + log, "SymbolFileDWARF::FindNamespace (sc, name=\"%s\") => " + "CompilerDeclContext(%p/%p) \"%s\"", + name.GetCString(), + static_cast<const void *>(namespace_decl_ctx.GetTypeSystem()), + static_cast<const void *>(namespace_decl_ctx.GetOpaqueDeclContext()), + namespace_decl_ctx.GetName().AsCString("<NULL>")); + } + + return namespace_decl_ctx; +} + +TypeSP SymbolFileDWARF::GetTypeForDIE(const DWARFDIE &die, + bool resolve_function_context) { + TypeSP type_sp; + if (die) { + Type *type_ptr = GetDIEToType().lookup(die.GetDIE()); + if (type_ptr == nullptr) { + SymbolContextScope *scope; + if (auto *dwarf_cu = llvm::dyn_cast<DWARFCompileUnit>(die.GetCU())) + scope = GetCompUnitForDWARFCompUnit(*dwarf_cu); + else + scope = GetObjectFile()->GetModule().get(); + assert(scope); + SymbolContext sc(scope); + const DWARFDebugInfoEntry *parent_die = die.GetParent().GetDIE(); + while (parent_die != nullptr) { + if (parent_die->Tag() == DW_TAG_subprogram) + break; + parent_die = parent_die->GetParent(); + } + SymbolContext sc_backup = sc; + if (resolve_function_context && parent_die != nullptr && + !GetFunction(DWARFDIE(die.GetCU(), parent_die), sc)) + sc = sc_backup; + + type_sp = ParseType(sc, die, nullptr); + } else if (type_ptr != DIE_IS_BEING_PARSED) { + // Grab the existing type from the master types lists + type_sp = type_ptr->shared_from_this(); + } + } + return type_sp; +} + +DWARFDIE +SymbolFileDWARF::GetDeclContextDIEContainingDIE(const DWARFDIE &orig_die) { + if (orig_die) { + DWARFDIE die = orig_die; + + while (die) { + // If this is the original DIE that we are searching for a declaration + // for, then don't look in the cache as we don't want our own decl + // context to be our decl context... + if (orig_die != die) { + switch (die.Tag()) { + case DW_TAG_compile_unit: + case DW_TAG_partial_unit: + case DW_TAG_namespace: + case DW_TAG_structure_type: + case DW_TAG_union_type: + case DW_TAG_class_type: + case DW_TAG_lexical_block: + case DW_TAG_subprogram: + return die; + case DW_TAG_inlined_subroutine: { + DWARFDIE abs_die = die.GetReferencedDIE(DW_AT_abstract_origin); + if (abs_die) { + return abs_die; + } + break; + } + default: + break; + } + } + + DWARFDIE spec_die = die.GetReferencedDIE(DW_AT_specification); + if (spec_die) { + DWARFDIE decl_ctx_die = GetDeclContextDIEContainingDIE(spec_die); + if (decl_ctx_die) + return decl_ctx_die; + } + + DWARFDIE abs_die = die.GetReferencedDIE(DW_AT_abstract_origin); + if (abs_die) { + DWARFDIE decl_ctx_die = GetDeclContextDIEContainingDIE(abs_die); + if (decl_ctx_die) + return decl_ctx_die; + } + + die = die.GetParent(); + } + } + return DWARFDIE(); +} + +Symbol * +SymbolFileDWARF::GetObjCClassSymbol(ConstString objc_class_name) { + Symbol *objc_class_symbol = nullptr; + if (m_obj_file) { + Symtab *symtab = m_obj_file->GetSymtab(); + if (symtab) { + objc_class_symbol = symtab->FindFirstSymbolWithNameAndType( + objc_class_name, eSymbolTypeObjCClass, Symtab::eDebugNo, + Symtab::eVisibilityAny); + } + } + return objc_class_symbol; +} + +// Some compilers don't emit the DW_AT_APPLE_objc_complete_type attribute. If +// they don't then we can end up looking through all class types for a complete +// type and never find the full definition. We need to know if this attribute +// is supported, so we determine this here and cache th result. We also need to +// worry about the debug map +// DWARF file +// if we are doing darwin DWARF in .o file debugging. +bool SymbolFileDWARF::Supports_DW_AT_APPLE_objc_complete_type( + DWARFUnit *cu) { + if (m_supports_DW_AT_APPLE_objc_complete_type == eLazyBoolCalculate) { + m_supports_DW_AT_APPLE_objc_complete_type = eLazyBoolNo; + if (cu && cu->Supports_DW_AT_APPLE_objc_complete_type()) + m_supports_DW_AT_APPLE_objc_complete_type = eLazyBoolYes; + else { + DWARFDebugInfo *debug_info = DebugInfo(); + const uint32_t num_compile_units = GetNumCompileUnits(); + for (uint32_t cu_idx = 0; cu_idx < num_compile_units; ++cu_idx) { + DWARFUnit *dwarf_cu = debug_info->GetUnitAtIndex(cu_idx); + if (dwarf_cu != cu && + dwarf_cu->Supports_DW_AT_APPLE_objc_complete_type()) { + m_supports_DW_AT_APPLE_objc_complete_type = eLazyBoolYes; + break; + } + } + } + if (m_supports_DW_AT_APPLE_objc_complete_type == eLazyBoolNo && + GetDebugMapSymfile()) + return m_debug_map_symfile->Supports_DW_AT_APPLE_objc_complete_type(this); + } + return m_supports_DW_AT_APPLE_objc_complete_type == eLazyBoolYes; +} + +// This function can be used when a DIE is found that is a forward declaration +// DIE and we want to try and find a type that has the complete definition. +TypeSP SymbolFileDWARF::FindCompleteObjCDefinitionTypeForDIE( + const DWARFDIE &die, ConstString type_name, + bool must_be_implementation) { + + TypeSP type_sp; + + if (!type_name || (must_be_implementation && !GetObjCClassSymbol(type_name))) + return type_sp; + + DIEArray die_offsets; + m_index->GetCompleteObjCClass(type_name, must_be_implementation, die_offsets); + + const size_t num_matches = die_offsets.size(); + + if (num_matches) { + for (size_t i = 0; i < num_matches; ++i) { + const DIERef &die_ref = die_offsets[i]; + DWARFDIE type_die = GetDIE(die_ref); + + if (type_die) { + bool try_resolving_type = false; + + // Don't try and resolve the DIE we are looking for with the DIE + // itself! + if (type_die != die) { + switch (type_die.Tag()) { + case DW_TAG_class_type: + case DW_TAG_structure_type: + try_resolving_type = true; + break; + default: + break; + } + } + + if (try_resolving_type) { + if (must_be_implementation && + type_die.Supports_DW_AT_APPLE_objc_complete_type()) + try_resolving_type = type_die.GetAttributeValueAsUnsigned( + DW_AT_APPLE_objc_complete_type, 0); + + if (try_resolving_type) { + Type *resolved_type = ResolveType(type_die, false, true); + if (resolved_type && resolved_type != DIE_IS_BEING_PARSED) { + DEBUG_PRINTF("resolved 0x%8.8" PRIx64 " from %s to 0x%8.8" PRIx64 + " (cu 0x%8.8" PRIx64 ")\n", + die.GetID(), + m_obj_file->GetFileSpec().GetFilename().AsCString( + "<Unknown>"), + type_die.GetID(), type_cu->GetID()); + + if (die) + GetDIEToType()[die.GetDIE()] = resolved_type; + type_sp = resolved_type->shared_from_this(); + break; + } + } + } + } else { + m_index->ReportInvalidDIERef(die_ref, type_name.GetStringRef()); + } + } + } + return type_sp; +} + +// This function helps to ensure that the declaration contexts match for two +// different DIEs. Often times debug information will refer to a forward +// declaration of a type (the equivalent of "struct my_struct;". There will +// often be a declaration of that type elsewhere that has the full definition. +// When we go looking for the full type "my_struct", we will find one or more +// matches in the accelerator tables and we will then need to make sure the +// type was in the same declaration context as the original DIE. This function +// can efficiently compare two DIEs and will return true when the declaration +// context matches, and false when they don't. +bool SymbolFileDWARF::DIEDeclContextsMatch(const DWARFDIE &die1, + const DWARFDIE &die2) { + if (die1 == die2) + return true; + + std::vector<DWARFDIE> decl_ctx_1; + std::vector<DWARFDIE> decl_ctx_2; + // The declaration DIE stack is a stack of the declaration context DIEs all + // the way back to the compile unit. If a type "T" is declared inside a class + // "B", and class "B" is declared inside a class "A" and class "A" is in a + // namespace "lldb", and the namespace is in a compile unit, there will be a + // stack of DIEs: + // + // [0] DW_TAG_class_type for "B" + // [1] DW_TAG_class_type for "A" + // [2] DW_TAG_namespace for "lldb" + // [3] DW_TAG_compile_unit or DW_TAG_partial_unit for the source file. + // + // We grab both contexts and make sure that everything matches all the way + // back to the compiler unit. + + // First lets grab the decl contexts for both DIEs + decl_ctx_1 = die1.GetDeclContextDIEs(); + decl_ctx_2 = die2.GetDeclContextDIEs(); + // Make sure the context arrays have the same size, otherwise we are done + const size_t count1 = decl_ctx_1.size(); + const size_t count2 = decl_ctx_2.size(); + if (count1 != count2) + return false; + + // Make sure the DW_TAG values match all the way back up the compile unit. If + // they don't, then we are done. + DWARFDIE decl_ctx_die1; + DWARFDIE decl_ctx_die2; + size_t i; + for (i = 0; i < count1; i++) { + decl_ctx_die1 = decl_ctx_1[i]; + decl_ctx_die2 = decl_ctx_2[i]; + if (decl_ctx_die1.Tag() != decl_ctx_die2.Tag()) + return false; + } +#ifndef NDEBUG + + // Make sure the top item in the decl context die array is always + // DW_TAG_compile_unit or DW_TAG_partial_unit. If it isn't then + // something went wrong in the DWARFDIE::GetDeclContextDIEs() + // function. + dw_tag_t cu_tag = decl_ctx_1[count1 - 1].Tag(); + UNUSED_IF_ASSERT_DISABLED(cu_tag); + assert(cu_tag == DW_TAG_compile_unit || cu_tag == DW_TAG_partial_unit); + +#endif + // Always skip the compile unit when comparing by only iterating up to "count + // - 1". Here we compare the names as we go. + for (i = 0; i < count1 - 1; i++) { + decl_ctx_die1 = decl_ctx_1[i]; + decl_ctx_die2 = decl_ctx_2[i]; + const char *name1 = decl_ctx_die1.GetName(); + const char *name2 = decl_ctx_die2.GetName(); + // If the string was from a DW_FORM_strp, then the pointer will often be + // the same! + if (name1 == name2) + continue; + + // Name pointers are not equal, so only compare the strings if both are not + // NULL. + if (name1 && name2) { + // If the strings don't compare, we are done... + if (strcmp(name1, name2) != 0) + return false; + } else { + // One name was NULL while the other wasn't + return false; + } + } + // We made it through all of the checks and the declaration contexts are + // equal. + return true; +} + +TypeSP SymbolFileDWARF::FindDefinitionTypeForDWARFDeclContext( + const DWARFDeclContext &dwarf_decl_ctx) { + TypeSP type_sp; + + const uint32_t dwarf_decl_ctx_count = dwarf_decl_ctx.GetSize(); + if (dwarf_decl_ctx_count > 0) { + const ConstString type_name(dwarf_decl_ctx[0].name); + const dw_tag_t tag = dwarf_decl_ctx[0].tag; + + if (type_name) { + Log *log(LogChannelDWARF::GetLogIfAny(DWARF_LOG_TYPE_COMPLETION | + DWARF_LOG_LOOKUPS)); + if (log) { + GetObjectFile()->GetModule()->LogMessage( + log, "SymbolFileDWARF::FindDefinitionTypeForDWARFDeclContext(tag=%" + "s, qualified-name='%s')", + DW_TAG_value_to_name(dwarf_decl_ctx[0].tag), + dwarf_decl_ctx.GetQualifiedName()); + } + + DIEArray die_offsets; + m_index->GetTypes(dwarf_decl_ctx, die_offsets); + const size_t num_matches = die_offsets.size(); + + // Get the type system that we are looking to find a type for. We will + // use this to ensure any matches we find are in a language that this + // type system supports + const LanguageType language = dwarf_decl_ctx.GetLanguage(); + TypeSystem *type_system = (language == eLanguageTypeUnknown) + ? nullptr + : GetTypeSystemForLanguage(language); + + if (num_matches) { + for (size_t i = 0; i < num_matches; ++i) { + const DIERef &die_ref = die_offsets[i]; + DWARFDIE type_die = GetDIE(die_ref); + + if (type_die) { + // Make sure type_die's langauge matches the type system we are + // looking for. We don't want to find a "Foo" type from Java if we + // are looking for a "Foo" type for C, C++, ObjC, or ObjC++. + if (type_system && + !type_system->SupportsLanguage(type_die.GetLanguage())) + continue; + bool try_resolving_type = false; + + // Don't try and resolve the DIE we are looking for with the DIE + // itself! + const dw_tag_t type_tag = type_die.Tag(); + // Make sure the tags match + if (type_tag == tag) { + // The tags match, lets try resolving this type + try_resolving_type = true; + } else { + // The tags don't match, but we need to watch our for a forward + // declaration for a struct and ("struct foo") ends up being a + // class ("class foo { ... };") or vice versa. + switch (type_tag) { + case DW_TAG_class_type: + // We had a "class foo", see if we ended up with a "struct foo + // { ... };" + try_resolving_type = (tag == DW_TAG_structure_type); + break; + case DW_TAG_structure_type: + // We had a "struct foo", see if we ended up with a "class foo + // { ... };" + try_resolving_type = (tag == DW_TAG_class_type); + break; + default: + // Tags don't match, don't event try to resolve using this type + // whose name matches.... + break; + } + } + + if (try_resolving_type) { + DWARFDeclContext type_dwarf_decl_ctx; + type_die.GetDWARFDeclContext(type_dwarf_decl_ctx); + + if (log) { + GetObjectFile()->GetModule()->LogMessage( + log, "SymbolFileDWARF::" + "FindDefinitionTypeForDWARFDeclContext(tag=%s, " + "qualified-name='%s') trying die=0x%8.8x (%s)", + DW_TAG_value_to_name(dwarf_decl_ctx[0].tag), + dwarf_decl_ctx.GetQualifiedName(), type_die.GetOffset(), + type_dwarf_decl_ctx.GetQualifiedName()); + } + + // Make sure the decl contexts match all the way up + if (dwarf_decl_ctx == type_dwarf_decl_ctx) { + Type *resolved_type = ResolveType(type_die, false); + if (resolved_type && resolved_type != DIE_IS_BEING_PARSED) { + type_sp = resolved_type->shared_from_this(); + break; + } + } + } else { + if (log) { + std::string qualified_name; + type_die.GetQualifiedName(qualified_name); + GetObjectFile()->GetModule()->LogMessage( + log, "SymbolFileDWARF::" + "FindDefinitionTypeForDWARFDeclContext(tag=%s, " + "qualified-name='%s') ignoring die=0x%8.8x (%s)", + DW_TAG_value_to_name(dwarf_decl_ctx[0].tag), + dwarf_decl_ctx.GetQualifiedName(), type_die.GetOffset(), + qualified_name.c_str()); + } + } + } else { + m_index->ReportInvalidDIERef(die_ref, type_name.GetStringRef()); + } + } + } + } + } + return type_sp; +} + +TypeSP SymbolFileDWARF::ParseType(const SymbolContext &sc, const DWARFDIE &die, + bool *type_is_new_ptr) { + if (!die) + return {}; + + TypeSystem *type_system = + GetTypeSystemForLanguage(die.GetCU()->GetLanguageType()); + if (!type_system) + return {}; + + DWARFASTParser *dwarf_ast = type_system->GetDWARFParser(); + if (!dwarf_ast) + return {}; + + Log *log = LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_INFO); + TypeSP type_sp = dwarf_ast->ParseTypeFromDWARF(sc, die, log, type_is_new_ptr); + if (type_sp) { + TypeList *type_list = GetTypeList(); + if (type_list) + type_list->Insert(type_sp); + + if (die.Tag() == DW_TAG_subprogram) { + std::string scope_qualified_name(GetDeclContextForUID(die.GetID()) + .GetScopeQualifiedName() + .AsCString("")); + if (scope_qualified_name.size()) { + m_function_scope_qualified_name_map[scope_qualified_name].insert( + die.GetID()); + } + } + } + + return type_sp; +} + +size_t SymbolFileDWARF::ParseTypes(const SymbolContext &sc, + const DWARFDIE &orig_die, + bool parse_siblings, bool parse_children) { + size_t types_added = 0; + DWARFDIE die = orig_die; + while (die) { + bool type_is_new = false; + if (ParseType(sc, die, &type_is_new).get()) { + if (type_is_new) + ++types_added; + } + + if (parse_children && die.HasChildren()) { + if (die.Tag() == DW_TAG_subprogram) { + SymbolContext child_sc(sc); + child_sc.function = sc.comp_unit->FindFunctionByUID(die.GetID()).get(); + types_added += ParseTypes(child_sc, die.GetFirstChild(), true, true); + } else + types_added += ParseTypes(sc, die.GetFirstChild(), true, true); + } + + if (parse_siblings) + die = die.GetSibling(); + else + die.Clear(); + } + return types_added; +} + +size_t SymbolFileDWARF::ParseBlocksRecursive(Function &func) { + ASSERT_MODULE_LOCK(this); + CompileUnit *comp_unit = func.GetCompileUnit(); + lldbassert(comp_unit); + + DWARFUnit *dwarf_cu = GetDWARFCompileUnit(comp_unit); + if (!dwarf_cu) + return 0; + + size_t functions_added = 0; + const dw_offset_t function_die_offset = func.GetID(); + DWARFDIE function_die = dwarf_cu->GetDIE(function_die_offset); + if (function_die) { + ParseBlocksRecursive(*comp_unit, &func.GetBlock(false), function_die, + LLDB_INVALID_ADDRESS, 0); + } + + return functions_added; +} + +size_t SymbolFileDWARF::ParseTypes(CompileUnit &comp_unit) { + ASSERT_MODULE_LOCK(this); + size_t types_added = 0; + DWARFUnit *dwarf_cu = GetDWARFCompileUnit(&comp_unit); + if (dwarf_cu) { + DWARFDIE dwarf_cu_die = dwarf_cu->DIE(); + if (dwarf_cu_die && dwarf_cu_die.HasChildren()) { + SymbolContext sc; + sc.comp_unit = &comp_unit; + types_added = ParseTypes(sc, dwarf_cu_die.GetFirstChild(), true, true); + } + } + + return types_added; +} + +size_t SymbolFileDWARF::ParseVariablesForContext(const SymbolContext &sc) { + ASSERT_MODULE_LOCK(this); + if (sc.comp_unit != nullptr) { + DWARFDebugInfo *info = DebugInfo(); + if (info == nullptr) + return 0; + + if (sc.function) { + DWARFDIE function_die = GetDIE(sc.function->GetID()); + + const dw_addr_t func_lo_pc = function_die.GetAttributeValueAsAddress( + DW_AT_low_pc, LLDB_INVALID_ADDRESS); + if (func_lo_pc != LLDB_INVALID_ADDRESS) { + const size_t num_variables = ParseVariables( + sc, function_die.GetFirstChild(), func_lo_pc, true, true); + + // Let all blocks know they have parse all their variables + sc.function->GetBlock(false).SetDidParseVariables(true, true); + return num_variables; + } + } else if (sc.comp_unit) { + DWARFUnit *dwarf_cu = info->GetUnitAtIndex(sc.comp_unit->GetID()); + + if (dwarf_cu == nullptr) + return 0; + + uint32_t vars_added = 0; + VariableListSP variables(sc.comp_unit->GetVariableList(false)); + + if (variables.get() == nullptr) { + variables = std::make_shared<VariableList>(); + sc.comp_unit->SetVariableList(variables); + + DIEArray die_offsets; + m_index->GetGlobalVariables(dwarf_cu->GetNonSkeletonUnit(), + die_offsets); + const size_t num_matches = die_offsets.size(); + if (num_matches) { + for (size_t i = 0; i < num_matches; ++i) { + const DIERef &die_ref = die_offsets[i]; + DWARFDIE die = GetDIE(die_ref); + if (die) { + VariableSP var_sp( + ParseVariableDIE(sc, die, LLDB_INVALID_ADDRESS)); + if (var_sp) { + variables->AddVariableIfUnique(var_sp); + ++vars_added; + } + } else + m_index->ReportInvalidDIERef(die_ref, ""); + } + } + } + return vars_added; + } + } + return 0; +} + +VariableSP SymbolFileDWARF::ParseVariableDIE(const SymbolContext &sc, + const DWARFDIE &die, + const lldb::addr_t func_low_pc) { + if (die.GetDWARF() != this) + return die.GetDWARF()->ParseVariableDIE(sc, die, func_low_pc); + + VariableSP var_sp; + if (!die) + return var_sp; + + var_sp = GetDIEToVariable()[die.GetDIE()]; + if (var_sp) + return var_sp; // Already been parsed! + + const dw_tag_t tag = die.Tag(); + ModuleSP module = GetObjectFile()->GetModule(); + + if ((tag == DW_TAG_variable) || (tag == DW_TAG_constant) || + (tag == DW_TAG_formal_parameter && sc.function)) { + DWARFAttributes attributes; + const size_t num_attributes = die.GetAttributes(attributes); + DWARFDIE spec_die; + if (num_attributes > 0) { + const char *name = nullptr; + const char *mangled = nullptr; + Declaration decl; + uint32_t i; + DWARFFormValue type_die_form; + DWARFExpression location; + bool is_external = false; + bool is_artificial = false; + bool location_is_const_value_data = false; + bool has_explicit_location = false; + DWARFFormValue const_value; + Variable::RangeList scope_ranges; + // AccessType accessibility = eAccessNone; + + for (i = 0; i < num_attributes; ++i) { + dw_attr_t attr = attributes.AttributeAtIndex(i); + DWARFFormValue form_value; + + if (attributes.ExtractFormValueAtIndex(i, form_value)) { + switch (attr) { + case DW_AT_decl_file: + decl.SetFile(sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex( + form_value.Unsigned())); + break; + case DW_AT_decl_line: + decl.SetLine(form_value.Unsigned()); + break; + case DW_AT_decl_column: + decl.SetColumn(form_value.Unsigned()); + break; + case DW_AT_name: + name = form_value.AsCString(); + break; + case DW_AT_linkage_name: + case DW_AT_MIPS_linkage_name: + mangled = form_value.AsCString(); + break; + case DW_AT_type: + type_die_form = form_value; + break; + case DW_AT_external: + is_external = form_value.Boolean(); + break; + case DW_AT_const_value: + // If we have already found a DW_AT_location attribute, ignore this + // attribute. + if (!has_explicit_location) { + location_is_const_value_data = true; + // The constant value will be either a block, a data value or a + // string. + auto debug_info_data = die.GetData(); + if (DWARFFormValue::IsBlockForm(form_value.Form())) { + // Retrieve the value as a block expression. + uint32_t block_offset = + form_value.BlockData() - debug_info_data.GetDataStart(); + uint32_t block_length = form_value.Unsigned(); + location = DWARFExpression(module, debug_info_data, die.GetCU(), + block_offset, block_length); + } else if (DWARFFormValue::IsDataForm(form_value.Form())) { + // Retrieve the value as a data expression. + uint32_t data_offset = attributes.DIEOffsetAtIndex(i); + if (auto data_length = form_value.GetFixedSize()) + location = + DWARFExpression(module, debug_info_data, die.GetCU(), + data_offset, *data_length); + else { + const uint8_t *data_pointer = form_value.BlockData(); + if (data_pointer) { + form_value.Unsigned(); + } else if (DWARFFormValue::IsDataForm(form_value.Form())) { + // we need to get the byte size of the type later after we + // create the variable + const_value = form_value; + } + } + } else { + // Retrieve the value as a string expression. + if (form_value.Form() == DW_FORM_strp) { + uint32_t data_offset = attributes.DIEOffsetAtIndex(i); + if (auto data_length = form_value.GetFixedSize()) + location = + DWARFExpression(module, debug_info_data, die.GetCU(), + data_offset, *data_length); + } else { + const char *str = form_value.AsCString(); + uint32_t string_offset = + str - (const char *)debug_info_data.GetDataStart(); + uint32_t string_length = strlen(str) + 1; + location = + DWARFExpression(module, debug_info_data, die.GetCU(), + string_offset, string_length); + } + } + } + break; + case DW_AT_location: { + location_is_const_value_data = false; + has_explicit_location = true; + if (DWARFFormValue::IsBlockForm(form_value.Form())) { + auto data = die.GetData(); + + uint32_t block_offset = + form_value.BlockData() - data.GetDataStart(); + uint32_t block_length = form_value.Unsigned(); + location = DWARFExpression(module, data, die.GetCU(), + block_offset, block_length); + } else { + const DWARFDataExtractor &debug_loc_data = DebugLocData(); + const dw_offset_t debug_loc_offset = form_value.Unsigned(); + + size_t loc_list_length = DWARFExpression::LocationListSize( + die.GetCU(), debug_loc_data, debug_loc_offset); + if (loc_list_length > 0) { + location = DWARFExpression(module, debug_loc_data, die.GetCU(), + debug_loc_offset, loc_list_length); + assert(func_low_pc != LLDB_INVALID_ADDRESS); + location.SetLocationListSlide( + func_low_pc - + attributes.CompileUnitAtIndex(i)->GetBaseAddress()); + } + } + } break; + case DW_AT_specification: + spec_die = form_value.Reference(); + break; + case DW_AT_start_scope: + // TODO: Implement this. + break; + case DW_AT_artificial: + is_artificial = form_value.Boolean(); + break; + case DW_AT_accessibility: + break; // accessibility = + // DW_ACCESS_to_AccessType(form_value.Unsigned()); break; + case DW_AT_declaration: + case DW_AT_description: + case DW_AT_endianity: + case DW_AT_segment: + case DW_AT_visibility: + default: + case DW_AT_abstract_origin: + case DW_AT_sibling: + break; + } + } + } + + const DWARFDIE parent_context_die = GetDeclContextDIEContainingDIE(die); + const dw_tag_t parent_tag = die.GetParent().Tag(); + bool is_static_member = + (parent_tag == DW_TAG_compile_unit || + parent_tag == DW_TAG_partial_unit) && + (parent_context_die.Tag() == DW_TAG_class_type || + parent_context_die.Tag() == DW_TAG_structure_type); + + ValueType scope = eValueTypeInvalid; + + const DWARFDIE sc_parent_die = GetParentSymbolContextDIE(die); + SymbolContextScope *symbol_context_scope = nullptr; + + bool has_explicit_mangled = mangled != nullptr; + if (!mangled) { + // LLDB relies on the mangled name (DW_TAG_linkage_name or + // DW_AT_MIPS_linkage_name) to generate fully qualified names + // of global variables with commands like "frame var j". For + // example, if j were an int variable holding a value 4 and + // declared in a namespace B which in turn is contained in a + // namespace A, the command "frame var j" returns + // "(int) A::B::j = 4". + // If the compiler does not emit a linkage name, we should be + // able to generate a fully qualified name from the + // declaration context. + if ((parent_tag == DW_TAG_compile_unit || + parent_tag == DW_TAG_partial_unit) && + Language::LanguageIsCPlusPlus(die.GetLanguage())) { + DWARFDeclContext decl_ctx; + + die.GetDWARFDeclContext(decl_ctx); + mangled = decl_ctx.GetQualifiedNameAsConstString().GetCString(); + } + } + + if (tag == DW_TAG_formal_parameter) + scope = eValueTypeVariableArgument; + else { + // DWARF doesn't specify if a DW_TAG_variable is a local, global + // or static variable, so we have to do a little digging: + // 1) DW_AT_linkage_name implies static lifetime (but may be missing) + // 2) An empty DW_AT_location is an (optimized-out) static lifetime var. + // 3) DW_AT_location containing a DW_OP_addr implies static lifetime. + // Clang likes to combine small global variables into the same symbol + // with locations like: DW_OP_addr(0x1000), DW_OP_constu(2), DW_OP_plus + // so we need to look through the whole expression. + bool is_static_lifetime = + has_explicit_mangled || + (has_explicit_location && !location.IsValid()); + // Check if the location has a DW_OP_addr with any address value... + lldb::addr_t location_DW_OP_addr = LLDB_INVALID_ADDRESS; + if (!location_is_const_value_data) { + bool op_error = false; + location_DW_OP_addr = location.GetLocation_DW_OP_addr(0, op_error); + if (op_error) { + StreamString strm; + location.DumpLocationForAddress(&strm, eDescriptionLevelFull, 0, 0, + nullptr); + GetObjectFile()->GetModule()->ReportError( + "0x%8.8x: %s has an invalid location: %s", die.GetOffset(), + die.GetTagAsCString(), strm.GetData()); + } + if (location_DW_OP_addr != LLDB_INVALID_ADDRESS) + is_static_lifetime = true; + } + SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile(); + if (debug_map_symfile) + // Set the module of the expression to the linked module + // instead of the oject file so the relocated address can be + // found there. + location.SetModule(debug_map_symfile->GetObjectFile()->GetModule()); + + if (is_static_lifetime) { + if (is_external) + scope = eValueTypeVariableGlobal; + else + scope = eValueTypeVariableStatic; + + if (debug_map_symfile) { + // When leaving the DWARF in the .o files on darwin, when we have a + // global variable that wasn't initialized, the .o file might not + // have allocated a virtual address for the global variable. In + // this case it will have created a symbol for the global variable + // that is undefined/data and external and the value will be the + // byte size of the variable. When we do the address map in + // SymbolFileDWARFDebugMap we rely on having an address, we need to + // do some magic here so we can get the correct address for our + // global variable. The address for all of these entries will be + // zero, and there will be an undefined symbol in this object file, + // and the executable will have a matching symbol with a good + // address. So here we dig up the correct address and replace it in + // the location for the variable, and set the variable's symbol + // context scope to be that of the main executable so the file + // address will resolve correctly. + bool linked_oso_file_addr = false; + if (is_external && location_DW_OP_addr == 0) { + // we have a possible uninitialized extern global + ConstString const_name(mangled ? mangled : name); + ObjectFile *debug_map_objfile = + debug_map_symfile->GetObjectFile(); + if (debug_map_objfile) { + Symtab *debug_map_symtab = debug_map_objfile->GetSymtab(); + if (debug_map_symtab) { + Symbol *exe_symbol = + debug_map_symtab->FindFirstSymbolWithNameAndType( + const_name, eSymbolTypeData, Symtab::eDebugYes, + Symtab::eVisibilityExtern); + if (exe_symbol) { + if (exe_symbol->ValueIsAddress()) { + const addr_t exe_file_addr = + exe_symbol->GetAddressRef().GetFileAddress(); + if (exe_file_addr != LLDB_INVALID_ADDRESS) { + if (location.Update_DW_OP_addr(exe_file_addr)) { + linked_oso_file_addr = true; + symbol_context_scope = exe_symbol; + } + } + } + } + } + } + } + + if (!linked_oso_file_addr) { + // The DW_OP_addr is not zero, but it contains a .o file address + // which needs to be linked up correctly. + const lldb::addr_t exe_file_addr = + debug_map_symfile->LinkOSOFileAddress(this, + location_DW_OP_addr); + if (exe_file_addr != LLDB_INVALID_ADDRESS) { + // Update the file address for this variable + location.Update_DW_OP_addr(exe_file_addr); + } else { + // Variable didn't make it into the final executable + return var_sp; + } + } + } + } else { + if (location_is_const_value_data) + scope = eValueTypeVariableStatic; + else { + scope = eValueTypeVariableLocal; + if (debug_map_symfile) { + // We need to check for TLS addresses that we need to fixup + if (location.ContainsThreadLocalStorage()) { + location.LinkThreadLocalStorage( + debug_map_symfile->GetObjectFile()->GetModule(), + [this, debug_map_symfile]( + lldb::addr_t unlinked_file_addr) -> lldb::addr_t { + return debug_map_symfile->LinkOSOFileAddress( + this, unlinked_file_addr); + }); + scope = eValueTypeVariableThreadLocal; + } + } + } + } + } + + if (symbol_context_scope == nullptr) { + switch (parent_tag) { + case DW_TAG_subprogram: + case DW_TAG_inlined_subroutine: + case DW_TAG_lexical_block: + if (sc.function) { + symbol_context_scope = sc.function->GetBlock(true).FindBlockByID( + sc_parent_die.GetID()); + if (symbol_context_scope == nullptr) + symbol_context_scope = sc.function; + } + break; + + default: + symbol_context_scope = sc.comp_unit; + break; + } + } + + if (symbol_context_scope) { + SymbolFileTypeSP type_sp( + new SymbolFileType(*this, GetUID(type_die_form.Reference()))); + + if (const_value.Form() && type_sp && type_sp->GetType()) + location.UpdateValue(const_value.Unsigned(), + type_sp->GetType()->GetByteSize().getValueOr(0), + die.GetCU()->GetAddressByteSize()); + + var_sp = std::make_shared<Variable>( + die.GetID(), name, mangled, type_sp, scope, symbol_context_scope, + scope_ranges, &decl, location, is_external, is_artificial, + is_static_member); + + var_sp->SetLocationIsConstantValueData(location_is_const_value_data); + } else { + // Not ready to parse this variable yet. It might be a global or static + // variable that is in a function scope and the function in the symbol + // context wasn't filled in yet + return var_sp; + } + } + // Cache var_sp even if NULL (the variable was just a specification or was + // missing vital information to be able to be displayed in the debugger + // (missing location due to optimization, etc)) so we don't re-parse this + // DIE over and over later... + GetDIEToVariable()[die.GetDIE()] = var_sp; + if (spec_die) + GetDIEToVariable()[spec_die.GetDIE()] = var_sp; + } + return var_sp; +} + +DWARFDIE +SymbolFileDWARF::FindBlockContainingSpecification( + const DIERef &func_die_ref, dw_offset_t spec_block_die_offset) { + // Give the concrete function die specified by "func_die_offset", find the + // concrete block whose DW_AT_specification or DW_AT_abstract_origin points + // to "spec_block_die_offset" + return FindBlockContainingSpecification(DebugInfo()->GetDIE(func_die_ref), + spec_block_die_offset); +} + +DWARFDIE +SymbolFileDWARF::FindBlockContainingSpecification( + const DWARFDIE &die, dw_offset_t spec_block_die_offset) { + if (die) { + switch (die.Tag()) { + case DW_TAG_subprogram: + case DW_TAG_inlined_subroutine: + case DW_TAG_lexical_block: { + if (die.GetReferencedDIE(DW_AT_specification).GetOffset() == + spec_block_die_offset) + return die; + + if (die.GetReferencedDIE(DW_AT_abstract_origin).GetOffset() == + spec_block_die_offset) + return die; + } break; + } + + // Give the concrete function die specified by "func_die_offset", find the + // concrete block whose DW_AT_specification or DW_AT_abstract_origin points + // to "spec_block_die_offset" + for (DWARFDIE child_die = die.GetFirstChild(); child_die; + child_die = child_die.GetSibling()) { + DWARFDIE result_die = + FindBlockContainingSpecification(child_die, spec_block_die_offset); + if (result_die) + return result_die; + } + } + + return DWARFDIE(); +} + +size_t SymbolFileDWARF::ParseVariables(const SymbolContext &sc, + const DWARFDIE &orig_die, + const lldb::addr_t func_low_pc, + bool parse_siblings, bool parse_children, + VariableList *cc_variable_list) { + if (!orig_die) + return 0; + + VariableListSP variable_list_sp; + + size_t vars_added = 0; + DWARFDIE die = orig_die; + while (die) { + dw_tag_t tag = die.Tag(); + + // Check to see if we have already parsed this variable or constant? + VariableSP var_sp = GetDIEToVariable()[die.GetDIE()]; + if (var_sp) { + if (cc_variable_list) + cc_variable_list->AddVariableIfUnique(var_sp); + } else { + // We haven't already parsed it, lets do that now. + if ((tag == DW_TAG_variable) || (tag == DW_TAG_constant) || + (tag == DW_TAG_formal_parameter && sc.function)) { + if (variable_list_sp.get() == nullptr) { + DWARFDIE sc_parent_die = GetParentSymbolContextDIE(orig_die); + dw_tag_t parent_tag = sc_parent_die.Tag(); + switch (parent_tag) { + case DW_TAG_compile_unit: + case DW_TAG_partial_unit: + if (sc.comp_unit != nullptr) { + variable_list_sp = sc.comp_unit->GetVariableList(false); + if (variable_list_sp.get() == nullptr) { + variable_list_sp = std::make_shared<VariableList>(); + } + } else { + GetObjectFile()->GetModule()->ReportError( + "parent 0x%8.8" PRIx64 " %s with no valid compile unit in " + "symbol context for 0x%8.8" PRIx64 + " %s.\n", + sc_parent_die.GetID(), sc_parent_die.GetTagAsCString(), + orig_die.GetID(), orig_die.GetTagAsCString()); + } + break; + + case DW_TAG_subprogram: + case DW_TAG_inlined_subroutine: + case DW_TAG_lexical_block: + if (sc.function != nullptr) { + // Check to see if we already have parsed the variables for the + // given scope + + Block *block = sc.function->GetBlock(true).FindBlockByID( + sc_parent_die.GetID()); + if (block == nullptr) { + // This must be a specification or abstract origin with a + // concrete block counterpart in the current function. We need + // to find the concrete block so we can correctly add the + // variable to it + const DWARFDIE concrete_block_die = + FindBlockContainingSpecification( + GetDIE(sc.function->GetID()), + sc_parent_die.GetOffset()); + if (concrete_block_die) + block = sc.function->GetBlock(true).FindBlockByID( + concrete_block_die.GetID()); + } + + if (block != nullptr) { + const bool can_create = false; + variable_list_sp = block->GetBlockVariableList(can_create); + if (variable_list_sp.get() == nullptr) { + variable_list_sp = std::make_shared<VariableList>(); + block->SetVariableList(variable_list_sp); + } + } + } + break; + + default: + GetObjectFile()->GetModule()->ReportError( + "didn't find appropriate parent DIE for variable list for " + "0x%8.8" PRIx64 " %s.\n", + orig_die.GetID(), orig_die.GetTagAsCString()); + break; + } + } + + if (variable_list_sp) { + VariableSP var_sp(ParseVariableDIE(sc, die, func_low_pc)); + if (var_sp) { + variable_list_sp->AddVariableIfUnique(var_sp); + if (cc_variable_list) + cc_variable_list->AddVariableIfUnique(var_sp); + ++vars_added; + } + } + } + } + + bool skip_children = (sc.function == nullptr && tag == DW_TAG_subprogram); + + if (!skip_children && parse_children && die.HasChildren()) { + vars_added += ParseVariables(sc, die.GetFirstChild(), func_low_pc, true, + true, cc_variable_list); + } + + if (parse_siblings) + die = die.GetSibling(); + else + die.Clear(); + } + return vars_added; +} + +/// Collect call graph edges present in a function DIE. +static std::vector<lldb_private::CallEdge> +CollectCallEdges(DWARFDIE function_die) { + // Check if the function has a supported call site-related attribute. + // TODO: In the future it may be worthwhile to support call_all_source_calls. + uint64_t has_call_edges = + function_die.GetAttributeValueAsUnsigned(DW_AT_call_all_calls, 0); + if (!has_call_edges) + return {}; + + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + LLDB_LOG(log, "CollectCallEdges: Found call site info in {0}", + function_die.GetPubname()); + + // Scan the DIE for TAG_call_site entries. + // TODO: A recursive scan of all blocks in the subprogram is needed in order + // to be DWARF5-compliant. This may need to be done lazily to be performant. + // For now, assume that all entries are nested directly under the subprogram + // (this is the kind of DWARF LLVM produces) and parse them eagerly. + std::vector<CallEdge> call_edges; + for (DWARFDIE child = function_die.GetFirstChild(); child.IsValid(); + child = child.GetSibling()) { + if (child.Tag() != DW_TAG_call_site) + continue; + + // Extract DW_AT_call_origin (the call target's DIE). + DWARFDIE call_origin = child.GetReferencedDIE(DW_AT_call_origin); + if (!call_origin.IsValid()) { + LLDB_LOG(log, "CollectCallEdges: Invalid call origin in {0}", + function_die.GetPubname()); + continue; + } + + // Extract DW_AT_call_return_pc (the PC the call returns to) if it's + // available. It should only ever be unavailable for tail call edges, in + // which case use LLDB_INVALID_ADDRESS. + addr_t return_pc = child.GetAttributeValueAsAddress(DW_AT_call_return_pc, + LLDB_INVALID_ADDRESS); + + LLDB_LOG(log, "CollectCallEdges: Found call origin: {0} (retn-PC: {1:x})", + call_origin.GetPubname(), return_pc); + call_edges.emplace_back(call_origin.GetMangledName(), return_pc); + } + return call_edges; +} + +std::vector<lldb_private::CallEdge> +SymbolFileDWARF::ParseCallEdgesInFunction(UserID func_id) { + DWARFDIE func_die = GetDIE(func_id.GetID()); + if (func_die.IsValid()) + return CollectCallEdges(func_die); + return {}; +} + +// PluginInterface protocol +ConstString SymbolFileDWARF::GetPluginName() { return GetPluginNameStatic(); } + +uint32_t SymbolFileDWARF::GetPluginVersion() { return 1; } + +void SymbolFileDWARF::Dump(lldb_private::Stream &s) { m_index->Dump(s); } + +void SymbolFileDWARF::DumpClangAST(Stream &s) { + TypeSystem *ts = GetTypeSystemForLanguage(eLanguageTypeC_plus_plus); + ClangASTContext *clang = llvm::dyn_cast_or_null<ClangASTContext>(ts); + if (!clang) + return; + clang->Dump(s); +} + +SymbolFileDWARFDebugMap *SymbolFileDWARF::GetDebugMapSymfile() { + if (m_debug_map_symfile == nullptr && !m_debug_map_module_wp.expired()) { + lldb::ModuleSP module_sp(m_debug_map_module_wp.lock()); + if (module_sp) { + SymbolVendor *sym_vendor = module_sp->GetSymbolVendor(); + if (sym_vendor) + m_debug_map_symfile = + (SymbolFileDWARFDebugMap *)sym_vendor->GetSymbolFile(); + } + } + return m_debug_map_symfile; +} + +DWARFExpression::LocationListFormat +SymbolFileDWARF::GetLocationListFormat() const { + if (m_data_debug_loclists.m_data.GetByteSize() > 0) + return DWARFExpression::LocLists; + return DWARFExpression::RegularLocationList; +} + +SymbolFileDWARFDwp *SymbolFileDWARF::GetDwpSymbolFile() { + llvm::call_once(m_dwp_symfile_once_flag, [this]() { + ModuleSpec module_spec; + module_spec.GetFileSpec() = m_obj_file->GetFileSpec(); + module_spec.GetSymbolFileSpec() = + FileSpec(m_obj_file->GetFileSpec().GetPath() + ".dwp"); + + FileSpecList search_paths = Target::GetDefaultDebugFileSearchPaths(); + FileSpec dwp_filespec = + Symbols::LocateExecutableSymbolFile(module_spec, search_paths); + if (FileSystem::Instance().Exists(dwp_filespec)) { + m_dwp_symfile = SymbolFileDWARFDwp::Create(GetObjectFile()->GetModule(), + dwp_filespec); + } + }); + return m_dwp_symfile.get(); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h new file mode 100644 index 000000000000..018af47305f4 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h @@ -0,0 +1,492 @@ +//===-- SymbolFileDWARF.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 SymbolFileDWARF_SymbolFileDWARF_h_ +#define SymbolFileDWARF_SymbolFileDWARF_h_ + +#include <list> +#include <map> +#include <mutex> +#include <set> +#include <unordered_map> +#include <vector> + +#include "llvm/ADT/DenseMap.h" +#include "llvm/Support/Threading.h" + +#include "lldb/Core/UniqueCStringMap.h" +#include "lldb/Core/dwarf.h" +#include "lldb/Expression/DWARFExpression.h" +#include "lldb/Symbol/DebugMacros.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/SymbolFile.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/Utility/Flags.h" +#include "lldb/Utility/RangeMap.h" +#include "lldb/lldb-private.h" + +#include "DWARFContext.h" +#include "DWARFDataExtractor.h" +#include "DWARFDefines.h" +#include "DWARFIndex.h" +#include "UniqueDWARFASTType.h" + +// Forward Declarations for this DWARF plugin +class DebugMapModule; +class DWARFAbbreviationDeclaration; +class DWARFAbbreviationDeclarationSet; +class DWARFCompileUnit; +class DWARFDebugAbbrev; +class DWARFDebugAranges; +class DWARFDebugInfo; +class DWARFDebugInfoEntry; +class DWARFDebugLine; +class DWARFDebugRangesBase; +class DWARFDeclContext; +class DWARFFormValue; +class DWARFTypeUnit; +class SymbolFileDWARFDebugMap; +class SymbolFileDWARFDwo; +class SymbolFileDWARFDwp; + +#define DIE_IS_BEING_PARSED ((lldb_private::Type *)1) + +class SymbolFileDWARF : public lldb_private::SymbolFile, + public lldb_private::UserID { +public: + friend class SymbolFileDWARFDebugMap; + friend class SymbolFileDWARFDwo; + friend class DebugMapModule; + friend class DWARFCompileUnit; + friend class DWARFDIE; + friend class DWARFASTParserClang; + + // Static Functions + static void Initialize(); + + static void Terminate(); + + static void DebuggerInitialize(lldb_private::Debugger &debugger); + + static lldb_private::ConstString GetPluginNameStatic(); + + static const char *GetPluginDescriptionStatic(); + + static lldb_private::SymbolFile * + CreateInstance(lldb_private::ObjectFile *obj_file); + + static lldb_private::FileSpecList GetSymlinkPaths(); + + // Constructors and Destructors + + SymbolFileDWARF(lldb_private::ObjectFile *ofile, + lldb_private::SectionList *dwo_section_list); + + ~SymbolFileDWARF() override; + + uint32_t CalculateAbilities() override; + + void InitializeObject() override; + + // Compile Unit function calls + + uint32_t GetNumCompileUnits() override; + + lldb::CompUnitSP ParseCompileUnitAtIndex(uint32_t index) override; + + lldb::LanguageType + ParseLanguage(lldb_private::CompileUnit &comp_unit) override; + + size_t ParseFunctions(lldb_private::CompileUnit &comp_unit) override; + + bool ParseLineTable(lldb_private::CompileUnit &comp_unit) override; + + bool ParseDebugMacros(lldb_private::CompileUnit &comp_unit) override; + + bool ParseSupportFiles(lldb_private::CompileUnit &comp_unit, + lldb_private::FileSpecList &support_files) override; + + bool ParseIsOptimized(lldb_private::CompileUnit &comp_unit) override; + + size_t ParseTypes(lldb_private::CompileUnit &comp_unit) override; + + bool ParseImportedModules( + const lldb_private::SymbolContext &sc, + std::vector<lldb_private::SourceModule> &imported_modules) override; + + size_t ParseBlocksRecursive(lldb_private::Function &func) override; + + size_t + ParseVariablesForContext(const lldb_private::SymbolContext &sc) override; + + lldb_private::Type *ResolveTypeUID(lldb::user_id_t type_uid) override; + llvm::Optional<ArrayInfo> GetDynamicArrayInfoForUID( + lldb::user_id_t type_uid, + const lldb_private::ExecutionContext *exe_ctx) override; + + bool CompleteType(lldb_private::CompilerType &compiler_type) override; + + lldb_private::Type *ResolveType(const DWARFDIE &die, + bool assert_not_being_parsed = true, + bool resolve_function_context = false); + + lldb_private::CompilerDecl GetDeclForUID(lldb::user_id_t uid) override; + + lldb_private::CompilerDeclContext + GetDeclContextForUID(lldb::user_id_t uid) override; + + lldb_private::CompilerDeclContext + GetDeclContextContainingUID(lldb::user_id_t uid) override; + + void + ParseDeclsForContext(lldb_private::CompilerDeclContext decl_ctx) override; + + uint32_t ResolveSymbolContext(const lldb_private::Address &so_addr, + lldb::SymbolContextItem resolve_scope, + lldb_private::SymbolContext &sc) override; + + uint32_t + ResolveSymbolContext(const lldb_private::FileSpec &file_spec, uint32_t line, + bool check_inlines, + lldb::SymbolContextItem resolve_scope, + lldb_private::SymbolContextList &sc_list) override; + + uint32_t + FindGlobalVariables(lldb_private::ConstString name, + const lldb_private::CompilerDeclContext *parent_decl_ctx, + uint32_t max_matches, + lldb_private::VariableList &variables) override; + + uint32_t FindGlobalVariables(const lldb_private::RegularExpression ®ex, + uint32_t max_matches, + lldb_private::VariableList &variables) override; + + uint32_t + FindFunctions(lldb_private::ConstString name, + const lldb_private::CompilerDeclContext *parent_decl_ctx, + lldb::FunctionNameType name_type_mask, bool include_inlines, + bool append, lldb_private::SymbolContextList &sc_list) override; + + uint32_t FindFunctions(const lldb_private::RegularExpression ®ex, + bool include_inlines, bool append, + lldb_private::SymbolContextList &sc_list) override; + + void GetMangledNamesForFunction( + const std::string &scope_qualified_name, + std::vector<lldb_private::ConstString> &mangled_names) override; + + uint32_t + FindTypes(lldb_private::ConstString name, + const lldb_private::CompilerDeclContext *parent_decl_ctx, + bool append, uint32_t max_matches, + llvm::DenseSet<lldb_private::SymbolFile *> &searched_symbol_files, + lldb_private::TypeMap &types) override; + + size_t FindTypes(const std::vector<lldb_private::CompilerContext> &context, + bool append, lldb_private::TypeMap &types) override; + + lldb_private::TypeList *GetTypeList() override; + + size_t GetTypes(lldb_private::SymbolContextScope *sc_scope, + lldb::TypeClass type_mask, + lldb_private::TypeList &type_list) override; + + lldb_private::TypeSystem * + GetTypeSystemForLanguage(lldb::LanguageType language) override; + + lldb_private::CompilerDeclContext FindNamespace( + lldb_private::ConstString name, + const lldb_private::CompilerDeclContext *parent_decl_ctx) override; + + void PreloadSymbols() override; + + std::recursive_mutex &GetModuleMutex() const override; + + // PluginInterface protocol + lldb_private::ConstString GetPluginName() override; + + uint32_t GetPluginVersion() override; + + const lldb_private::DWARFDataExtractor &get_debug_loc_data(); + const lldb_private::DWARFDataExtractor &get_debug_loclists_data(); + + DWARFDebugAbbrev *DebugAbbrev(); + + const DWARFDebugAbbrev *DebugAbbrev() const; + + DWARFDebugInfo *DebugInfo(); + + const DWARFDebugInfo *DebugInfo() const; + + DWARFDebugRangesBase *GetDebugRanges(); + DWARFDebugRangesBase *GetDebugRngLists(); + + const lldb_private::DWARFDataExtractor &DebugLocData(); + + static bool SupportedVersion(uint16_t version); + + DWARFDIE + GetDeclContextDIEContainingDIE(const DWARFDIE &die); + + bool + HasForwardDeclForClangType(const lldb_private::CompilerType &compiler_type); + + lldb_private::CompileUnit * + GetCompUnitForDWARFCompUnit(DWARFCompileUnit &dwarf_cu); + + virtual size_t GetObjCMethodDIEOffsets(lldb_private::ConstString class_name, + DIEArray &method_die_offsets); + + bool Supports_DW_AT_APPLE_objc_complete_type(DWARFUnit *cu); + + lldb_private::DebugMacrosSP ParseDebugMacros(lldb::offset_t *offset); + + static DWARFDIE GetParentSymbolContextDIE(const DWARFDIE &die); + + virtual lldb::CompUnitSP ParseCompileUnit(DWARFCompileUnit &dwarf_cu); + + virtual lldb_private::DWARFExpression::LocationListFormat + GetLocationListFormat() const; + + lldb::ModuleSP GetDWOModule(lldb_private::ConstString name); + + typedef std::map<lldb_private::ConstString, lldb::ModuleSP> + ExternalTypeModuleMap; + + /// Return the list of Clang modules imported by this SymbolFile. + const ExternalTypeModuleMap& getExternalTypeModules() const { + return m_external_type_modules; + } + + virtual DWARFDIE GetDIE(const DIERef &die_ref); + + DWARFDIE GetDIE(lldb::user_id_t uid); + + lldb::user_id_t GetUID(const DWARFBaseDIE &die) { + return GetUID(die.GetDIERef()); + } + + lldb::user_id_t GetUID(const llvm::Optional<DIERef> &ref) { + return ref ? GetUID(*ref) : LLDB_INVALID_UID; + } + + lldb::user_id_t GetUID(DIERef ref); + + virtual std::unique_ptr<SymbolFileDWARFDwo> + GetDwoSymbolFileForCompileUnit(DWARFUnit &dwarf_cu, + const DWARFDebugInfoEntry &cu_die); + + // For regular SymbolFileDWARF instances the method returns nullptr, + // for the instances of the subclass SymbolFileDWARFDwo + // the method returns a pointer to the base compile unit. + virtual DWARFCompileUnit *GetBaseCompileUnit() { return nullptr; } + + virtual llvm::Optional<uint32_t> GetDwoNum() { return llvm::None; } + + static bool + DIEInDeclContext(const lldb_private::CompilerDeclContext *parent_decl_ctx, + const DWARFDIE &die); + + std::vector<lldb_private::CallEdge> + ParseCallEdgesInFunction(UserID func_id) override; + + void Dump(lldb_private::Stream &s) override; + + void DumpClangAST(lldb_private::Stream &s) override; + + lldb_private::DWARFContext &GetDWARFContext() { return m_context; } + + lldb_private::FileSpec GetFile(DWARFUnit &unit, size_t file_idx); + +protected: + typedef llvm::DenseMap<const DWARFDebugInfoEntry *, lldb_private::Type *> + DIEToTypePtr; + typedef llvm::DenseMap<const DWARFDebugInfoEntry *, lldb::VariableSP> + DIEToVariableSP; + typedef llvm::DenseMap<const DWARFDebugInfoEntry *, + lldb::opaque_compiler_type_t> + DIEToClangType; + typedef llvm::DenseMap<lldb::opaque_compiler_type_t, lldb::user_id_t> + ClangTypeToDIE; + + struct DWARFDataSegment { + llvm::once_flag m_flag; + lldb_private::DWARFDataExtractor m_data; + }; + + DISALLOW_COPY_AND_ASSIGN(SymbolFileDWARF); + + const lldb_private::DWARFDataExtractor & + GetCachedSectionData(lldb::SectionType sect_type, + DWARFDataSegment &data_segment); + + virtual void LoadSectionData(lldb::SectionType sect_type, + lldb_private::DWARFDataExtractor &data); + + bool DeclContextMatchesThisSymbolFile( + const lldb_private::CompilerDeclContext *decl_ctx); + + virtual DWARFUnit * + GetDWARFCompileUnit(lldb_private::CompileUnit *comp_unit); + + DWARFUnit *GetNextUnparsedDWARFCompileUnit(DWARFUnit *prev_cu); + + bool GetFunction(const DWARFDIE &die, lldb_private::SymbolContext &sc); + + lldb_private::Function *ParseFunction(lldb_private::CompileUnit &comp_unit, + const DWARFDIE &die); + + size_t ParseBlocksRecursive(lldb_private::CompileUnit &comp_unit, + lldb_private::Block *parent_block, + const DWARFDIE &die, + lldb::addr_t subprogram_low_pc, uint32_t depth); + + size_t ParseTypes(const lldb_private::SymbolContext &sc, const DWARFDIE &die, + bool parse_siblings, bool parse_children); + + lldb::TypeSP ParseType(const lldb_private::SymbolContext &sc, + const DWARFDIE &die, bool *type_is_new); + + lldb_private::Type *ResolveTypeUID(const DWARFDIE &die, + bool assert_not_being_parsed); + + lldb_private::Type *ResolveTypeUID(const DIERef &die_ref); + + lldb::VariableSP ParseVariableDIE(const lldb_private::SymbolContext &sc, + const DWARFDIE &die, + const lldb::addr_t func_low_pc); + + size_t ParseVariables(const lldb_private::SymbolContext &sc, + const DWARFDIE &orig_die, + const lldb::addr_t func_low_pc, bool parse_siblings, + bool parse_children, + lldb_private::VariableList *cc_variable_list = nullptr); + + bool ClassOrStructIsVirtual(const DWARFDIE &die); + + // Given a die_offset, figure out the symbol context representing that die. + bool ResolveFunction(const DWARFDIE &die, bool include_inlines, + lldb_private::SymbolContextList &sc_list); + + virtual lldb::TypeSP + FindDefinitionTypeForDWARFDeclContext(const DWARFDeclContext &die_decl_ctx); + + virtual lldb::TypeSP FindCompleteObjCDefinitionTypeForDIE( + const DWARFDIE &die, lldb_private::ConstString type_name, + bool must_be_implementation); + + lldb_private::Symbol * + GetObjCClassSymbol(lldb_private::ConstString objc_class_name); + + lldb::TypeSP GetTypeForDIE(const DWARFDIE &die, + bool resolve_function_context = false); + + void SetDebugMapModule(const lldb::ModuleSP &module_sp) { + m_debug_map_module_wp = module_sp; + } + + SymbolFileDWARFDebugMap *GetDebugMapSymfile(); + + DWARFDIE + FindBlockContainingSpecification(const DIERef &func_die_ref, + dw_offset_t spec_block_die_offset); + + DWARFDIE + FindBlockContainingSpecification(const DWARFDIE &die, + dw_offset_t spec_block_die_offset); + + virtual UniqueDWARFASTTypeMap &GetUniqueDWARFASTTypeMap(); + + bool DIEDeclContextsMatch(const DWARFDIE &die1, const DWARFDIE &die2); + + bool ClassContainsSelector(const DWARFDIE &class_die, + lldb_private::ConstString selector); + + bool FixupAddress(lldb_private::Address &addr); + + typedef std::set<lldb_private::Type *> TypeSet; + + void GetTypes(const DWARFDIE &die, dw_offset_t min_die_offset, + dw_offset_t max_die_offset, uint32_t type_mask, + TypeSet &type_set); + + typedef lldb_private::RangeDataVector<lldb::addr_t, lldb::addr_t, + lldb_private::Variable *> + GlobalVariableMap; + + GlobalVariableMap &GetGlobalAranges(); + + void UpdateExternalModuleListIfNeeded(); + + virtual DIEToTypePtr &GetDIEToType() { return m_die_to_type; } + + virtual DIEToVariableSP &GetDIEToVariable() { return m_die_to_variable_sp; } + + virtual DIEToClangType &GetForwardDeclDieToClangType() { + return m_forward_decl_die_to_clang_type; + } + + virtual ClangTypeToDIE &GetForwardDeclClangTypeToDie() { + return m_forward_decl_clang_type_to_die; + } + + void BuildCuTranslationTable(); + llvm::Optional<uint32_t> GetDWARFUnitIndex(uint32_t cu_idx); + + struct DecodedUID { + SymbolFileDWARF &dwarf; + DIERef ref; + }; + llvm::Optional<DecodedUID> DecodeUID(lldb::user_id_t uid); + + SymbolFileDWARFDwp *GetDwpSymbolFile(); + + const lldb_private::FileSpecList &GetTypeUnitSupportFiles(DWARFTypeUnit &tu); + + lldb::ModuleWP m_debug_map_module_wp; + SymbolFileDWARFDebugMap *m_debug_map_symfile; + + llvm::once_flag m_dwp_symfile_once_flag; + std::unique_ptr<SymbolFileDWARFDwp> m_dwp_symfile; + + lldb_private::DWARFContext m_context; + + DWARFDataSegment m_data_debug_loc; + DWARFDataSegment m_data_debug_loclists; + + // The unique pointer items below are generated on demand if and when someone + // accesses them through a non const version of this class. + std::unique_ptr<DWARFDebugAbbrev> m_abbr; + std::unique_ptr<DWARFDebugInfo> m_info; + std::unique_ptr<GlobalVariableMap> m_global_aranges_up; + + typedef std::unordered_map<lldb::offset_t, lldb_private::DebugMacrosSP> + DebugMacrosMap; + DebugMacrosMap m_debug_macros_map; + + ExternalTypeModuleMap m_external_type_modules; + std::unique_ptr<lldb_private::DWARFIndex> m_index; + bool m_fetched_external_modules : 1; + lldb_private::LazyBool m_supports_DW_AT_APPLE_objc_complete_type; + + typedef std::set<lldb::user_id_t> DIERefSet; + typedef llvm::StringMap<DIERefSet> NameToOffsetMap; + NameToOffsetMap m_function_scope_qualified_name_map; + std::unique_ptr<DWARFDebugRangesBase> m_ranges; + std::unique_ptr<DWARFDebugRangesBase> m_rnglists; + UniqueDWARFASTTypeMap m_unique_ast_type_map; + DIEToTypePtr m_die_to_type; + DIEToVariableSP m_die_to_variable_sp; + DIEToClangType m_forward_decl_die_to_clang_type; + ClangTypeToDIE m_forward_decl_clang_type_to_die; + llvm::DenseMap<dw_offset_t, lldb_private::FileSpecList> + m_type_unit_support_files; + std::vector<uint32_t> m_lldb_cu_to_dwarf_unit; +}; + +#endif // SymbolFileDWARF_SymbolFileDWARF_h_ diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp new file mode 100644 index 000000000000..8ec64dbaf764 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp @@ -0,0 +1,1440 @@ +//===-- SymbolFileDWARFDebugMap.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 "SymbolFileDWARFDebugMap.h" +#include "DWARFDebugAranges.h" + +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Section.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Utility/RangeMap.h" +#include "lldb/Utility/RegularExpression.h" +#include "lldb/Utility/Timer.h" + +//#define DEBUG_OSO_DMAP // DO NOT CHECKIN WITH THIS NOT COMMENTED OUT +#if defined(DEBUG_OSO_DMAP) +#include "lldb/Core/StreamFile.h" +#endif + +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/LineTable.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolVendor.h" +#include "lldb/Symbol/TypeMap.h" +#include "lldb/Symbol/VariableList.h" +#include "llvm/Support/ScopedPrinter.h" + +#include "LogChannelDWARF.h" +#include "SymbolFileDWARF.h" + +#include <memory> + +using namespace lldb; +using namespace lldb_private; + +// Subclass lldb_private::Module so we can intercept the +// "Module::GetObjectFile()" (so we can fixup the object file sections) and +// also for "Module::GetSymbolVendor()" (so we can fixup the symbol file id. + +const SymbolFileDWARFDebugMap::FileRangeMap & +SymbolFileDWARFDebugMap::CompileUnitInfo::GetFileRangeMap( + SymbolFileDWARFDebugMap *exe_symfile) { + if (file_range_map_valid) + return file_range_map; + + file_range_map_valid = true; + + Module *oso_module = exe_symfile->GetModuleByCompUnitInfo(this); + if (!oso_module) + return file_range_map; + + ObjectFile *oso_objfile = oso_module->GetObjectFile(); + if (!oso_objfile) + return file_range_map; + + Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_MAP)); + if (log) + log->Printf( + "%p: SymbolFileDWARFDebugMap::CompileUnitInfo::GetFileRangeMap ('%s')", + static_cast<void *>(this), + oso_module->GetSpecificationDescription().c_str()); + + std::vector<SymbolFileDWARFDebugMap::CompileUnitInfo *> cu_infos; + if (exe_symfile->GetCompUnitInfosForModule(oso_module, cu_infos)) { + for (auto comp_unit_info : cu_infos) { + Symtab *exe_symtab = exe_symfile->GetObjectFile()->GetSymtab(); + ModuleSP oso_module_sp(oso_objfile->GetModule()); + Symtab *oso_symtab = oso_objfile->GetSymtab(); + + /// const uint32_t fun_resolve_flags = SymbolContext::Module | + /// eSymbolContextCompUnit | eSymbolContextFunction; + // SectionList *oso_sections = oso_objfile->Sections(); + // Now we need to make sections that map from zero based object file + // addresses to where things ended up in the main executable. + + assert(comp_unit_info->first_symbol_index != UINT32_MAX); + // End index is one past the last valid symbol index + const uint32_t oso_end_idx = comp_unit_info->last_symbol_index + 1; + for (uint32_t idx = comp_unit_info->first_symbol_index + + 2; // Skip the N_SO and N_OSO + idx < oso_end_idx; ++idx) { + Symbol *exe_symbol = exe_symtab->SymbolAtIndex(idx); + if (exe_symbol) { + if (!exe_symbol->IsDebug()) + continue; + + switch (exe_symbol->GetType()) { + default: + break; + + case eSymbolTypeCode: { + // For each N_FUN, or function that we run into in the debug map we + // make a new section that we add to the sections found in the .o + // file. This new section has the file address set to what the + // addresses are in the .o file, and the load address is adjusted + // to match where it ended up in the final executable! We do this + // before we parse any dwarf info so that when it goes get parsed + // all section/offset addresses that get registered will resolve + // correctly to the new addresses in the main executable. + + // First we find the original symbol in the .o file's symbol table + Symbol *oso_fun_symbol = oso_symtab->FindFirstSymbolWithNameAndType( + exe_symbol->GetMangled().GetName(lldb::eLanguageTypeUnknown, + Mangled::ePreferMangled), + eSymbolTypeCode, Symtab::eDebugNo, Symtab::eVisibilityAny); + if (oso_fun_symbol) { + // Add the inverse OSO file address to debug map entry mapping + exe_symfile->AddOSOFileRange( + this, exe_symbol->GetAddressRef().GetFileAddress(), + exe_symbol->GetByteSize(), + oso_fun_symbol->GetAddressRef().GetFileAddress(), + oso_fun_symbol->GetByteSize()); + } + } break; + + case eSymbolTypeData: { + // For each N_GSYM we remap the address for the global by making a + // new section that we add to the sections found in the .o file. + // This new section has the file address set to what the addresses + // are in the .o file, and the load address is adjusted to match + // where it ended up in the final executable! We do this before we + // parse any dwarf info so that when it goes get parsed all + // section/offset addresses that get registered will resolve + // correctly to the new addresses in the main executable. We + // initially set the section size to be 1 byte, but will need to + // fix up these addresses further after all globals have been + // parsed to span the gaps, or we can find the global variable + // sizes from the DWARF info as we are parsing. + + // Next we find the non-stab entry that corresponds to the N_GSYM + // in the .o file + Symbol *oso_gsym_symbol = + oso_symtab->FindFirstSymbolWithNameAndType( + exe_symbol->GetMangled().GetName(lldb::eLanguageTypeUnknown, + Mangled::ePreferMangled), + eSymbolTypeData, Symtab::eDebugNo, Symtab::eVisibilityAny); + if (exe_symbol && oso_gsym_symbol && exe_symbol->ValueIsAddress() && + oso_gsym_symbol->ValueIsAddress()) { + // Add the inverse OSO file address to debug map entry mapping + exe_symfile->AddOSOFileRange( + this, exe_symbol->GetAddressRef().GetFileAddress(), + exe_symbol->GetByteSize(), + oso_gsym_symbol->GetAddressRef().GetFileAddress(), + oso_gsym_symbol->GetByteSize()); + } + } break; + } + } + } + + exe_symfile->FinalizeOSOFileRanges(this); + // We don't need the symbols anymore for the .o files + oso_objfile->ClearSymtab(); + } + } + return file_range_map; +} + +class DebugMapModule : public Module { +public: + DebugMapModule(const ModuleSP &exe_module_sp, uint32_t cu_idx, + const FileSpec &file_spec, const ArchSpec &arch, + const ConstString *object_name, off_t object_offset, + const llvm::sys::TimePoint<> object_mod_time) + : Module(file_spec, arch, object_name, object_offset, object_mod_time), + m_exe_module_wp(exe_module_sp), m_cu_idx(cu_idx) {} + + ~DebugMapModule() override = default; + + SymbolVendor * + GetSymbolVendor(bool can_create = true, + lldb_private::Stream *feedback_strm = nullptr) override { + // Scope for locker + if (m_symfile_up.get() || !can_create) + return m_symfile_up.get(); + + ModuleSP exe_module_sp(m_exe_module_wp.lock()); + if (exe_module_sp) { + // Now get the object file outside of a locking scope + ObjectFile *oso_objfile = GetObjectFile(); + if (oso_objfile) { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + SymbolVendor *symbol_vendor = + Module::GetSymbolVendor(can_create, feedback_strm); + if (symbol_vendor) { + // Set a pointer to this class to set our OSO DWARF file know that + // the DWARF is being used along with a debug map and that it will + // have the remapped sections that we do below. + SymbolFileDWARF *oso_symfile = + SymbolFileDWARFDebugMap::GetSymbolFileAsSymbolFileDWARF( + symbol_vendor->GetSymbolFile()); + + if (!oso_symfile) + return nullptr; + + ObjectFile *exe_objfile = exe_module_sp->GetObjectFile(); + SymbolVendor *exe_sym_vendor = exe_module_sp->GetSymbolVendor(); + + if (exe_objfile && exe_sym_vendor) { + oso_symfile->SetDebugMapModule(exe_module_sp); + // Set the ID of the symbol file DWARF to the index of the OSO + // shifted left by 32 bits to provide a unique prefix for any + // UserID's that get created in the symbol file. + oso_symfile->SetID(((uint64_t)m_cu_idx + 1ull) << 32ull); + } + return symbol_vendor; + } + } + } + return nullptr; + } + +protected: + ModuleWP m_exe_module_wp; + const uint32_t m_cu_idx; +}; + +void SymbolFileDWARFDebugMap::Initialize() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), CreateInstance); +} + +void SymbolFileDWARFDebugMap::Terminate() { + PluginManager::UnregisterPlugin(CreateInstance); +} + +lldb_private::ConstString SymbolFileDWARFDebugMap::GetPluginNameStatic() { + static ConstString g_name("dwarf-debugmap"); + return g_name; +} + +const char *SymbolFileDWARFDebugMap::GetPluginDescriptionStatic() { + return "DWARF and DWARF3 debug symbol file reader (debug map)."; +} + +SymbolFile *SymbolFileDWARFDebugMap::CreateInstance(ObjectFile *obj_file) { + return new SymbolFileDWARFDebugMap(obj_file); +} + +SymbolFileDWARFDebugMap::SymbolFileDWARFDebugMap(ObjectFile *ofile) + : SymbolFile(ofile), m_flags(), m_compile_unit_infos(), m_func_indexes(), + m_glob_indexes(), + m_supports_DW_AT_APPLE_objc_complete_type(eLazyBoolCalculate) {} + +SymbolFileDWARFDebugMap::~SymbolFileDWARFDebugMap() {} + +void SymbolFileDWARFDebugMap::InitializeObject() {} + +void SymbolFileDWARFDebugMap::InitOSO() { + if (m_flags.test(kHaveInitializedOSOs)) + return; + + m_flags.set(kHaveInitializedOSOs); + + // If the object file has been stripped, there is no sense in looking further + // as all of the debug symbols for the debug map will not be available + if (m_obj_file->IsStripped()) + return; + + // Also make sure the file type is some sort of executable. Core files, debug + // info files (dSYM), object files (.o files), and stub libraries all can + switch (m_obj_file->GetType()) { + case ObjectFile::eTypeInvalid: + case ObjectFile::eTypeCoreFile: + case ObjectFile::eTypeDebugInfo: + case ObjectFile::eTypeObjectFile: + case ObjectFile::eTypeStubLibrary: + case ObjectFile::eTypeUnknown: + case ObjectFile::eTypeJIT: + return; + + case ObjectFile::eTypeExecutable: + case ObjectFile::eTypeDynamicLinker: + case ObjectFile::eTypeSharedLibrary: + break; + } + + // In order to get the abilities of this plug-in, we look at the list of + // N_OSO entries (object files) from the symbol table and make sure that + // these files exist and also contain valid DWARF. If we get any of that then + // we return the abilities of the first N_OSO's DWARF. + + Symtab *symtab = m_obj_file->GetSymtab(); + if (symtab) { + Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_MAP)); + + std::vector<uint32_t> oso_indexes; + // When a mach-o symbol is encoded, the n_type field is encoded in bits + // 23:16, and the n_desc field is encoded in bits 15:0. + // + // To find all N_OSO entries that are part of the DWARF + debug map we find + // only object file symbols with the flags value as follows: bits 23:16 == + // 0x66 (N_OSO) bits 15: 0 == 0x0001 (specifies this is a debug map object + // file) + const uint32_t k_oso_symbol_flags_value = 0x660001u; + + const uint32_t oso_index_count = + symtab->AppendSymbolIndexesWithTypeAndFlagsValue( + eSymbolTypeObjectFile, k_oso_symbol_flags_value, oso_indexes); + + if (oso_index_count > 0) { + symtab->AppendSymbolIndexesWithType(eSymbolTypeCode, Symtab::eDebugYes, + Symtab::eVisibilityAny, + m_func_indexes); + symtab->AppendSymbolIndexesWithType(eSymbolTypeData, Symtab::eDebugYes, + Symtab::eVisibilityAny, + m_glob_indexes); + + symtab->SortSymbolIndexesByValue(m_func_indexes, true); + symtab->SortSymbolIndexesByValue(m_glob_indexes, true); + + for (uint32_t sym_idx : m_func_indexes) { + const Symbol *symbol = symtab->SymbolAtIndex(sym_idx); + lldb::addr_t file_addr = symbol->GetAddressRef().GetFileAddress(); + lldb::addr_t byte_size = symbol->GetByteSize(); + DebugMap::Entry debug_map_entry( + file_addr, byte_size, OSOEntry(sym_idx, LLDB_INVALID_ADDRESS)); + m_debug_map.Append(debug_map_entry); + } + for (uint32_t sym_idx : m_glob_indexes) { + const Symbol *symbol = symtab->SymbolAtIndex(sym_idx); + lldb::addr_t file_addr = symbol->GetAddressRef().GetFileAddress(); + lldb::addr_t byte_size = symbol->GetByteSize(); + DebugMap::Entry debug_map_entry( + file_addr, byte_size, OSOEntry(sym_idx, LLDB_INVALID_ADDRESS)); + m_debug_map.Append(debug_map_entry); + } + m_debug_map.Sort(); + + m_compile_unit_infos.resize(oso_index_count); + + for (uint32_t i = 0; i < oso_index_count; ++i) { + const uint32_t so_idx = oso_indexes[i] - 1; + const uint32_t oso_idx = oso_indexes[i]; + const Symbol *so_symbol = symtab->SymbolAtIndex(so_idx); + const Symbol *oso_symbol = symtab->SymbolAtIndex(oso_idx); + if (so_symbol && oso_symbol && + so_symbol->GetType() == eSymbolTypeSourceFile && + oso_symbol->GetType() == eSymbolTypeObjectFile) { + m_compile_unit_infos[i].so_file.SetFile( + so_symbol->GetName().AsCString(), FileSpec::Style::native); + m_compile_unit_infos[i].oso_path = oso_symbol->GetName(); + m_compile_unit_infos[i].oso_mod_time = + llvm::sys::toTimePoint(oso_symbol->GetIntegerValue(0)); + uint32_t sibling_idx = so_symbol->GetSiblingIndex(); + // The sibling index can't be less that or equal to the current index + // "i" + if (sibling_idx == UINT32_MAX) { + m_obj_file->GetModule()->ReportError( + "N_SO in symbol with UID %u has invalid sibling in debug map, " + "please file a bug and attach the binary listed in this error", + so_symbol->GetID()); + } else { + const Symbol *last_symbol = symtab->SymbolAtIndex(sibling_idx - 1); + m_compile_unit_infos[i].first_symbol_index = so_idx; + m_compile_unit_infos[i].last_symbol_index = sibling_idx - 1; + m_compile_unit_infos[i].first_symbol_id = so_symbol->GetID(); + m_compile_unit_infos[i].last_symbol_id = last_symbol->GetID(); + + if (log) + log->Printf("Initialized OSO 0x%8.8x: file=%s", i, + oso_symbol->GetName().GetCString()); + } + } else { + if (oso_symbol == nullptr) + m_obj_file->GetModule()->ReportError( + "N_OSO symbol[%u] can't be found, please file a bug and attach " + "the binary listed in this error", + oso_idx); + else if (so_symbol == nullptr) + m_obj_file->GetModule()->ReportError( + "N_SO not found for N_OSO symbol[%u], please file a bug and " + "attach the binary listed in this error", + oso_idx); + else if (so_symbol->GetType() != eSymbolTypeSourceFile) + m_obj_file->GetModule()->ReportError( + "N_SO has incorrect symbol type (%u) for N_OSO symbol[%u], " + "please file a bug and attach the binary listed in this error", + so_symbol->GetType(), oso_idx); + else if (oso_symbol->GetType() != eSymbolTypeSourceFile) + m_obj_file->GetModule()->ReportError( + "N_OSO has incorrect symbol type (%u) for N_OSO symbol[%u], " + "please file a bug and attach the binary listed in this error", + oso_symbol->GetType(), oso_idx); + } + } + } + } +} + +Module *SymbolFileDWARFDebugMap::GetModuleByOSOIndex(uint32_t oso_idx) { + const uint32_t cu_count = GetNumCompileUnits(); + if (oso_idx < cu_count) + return GetModuleByCompUnitInfo(&m_compile_unit_infos[oso_idx]); + return nullptr; +} + +Module *SymbolFileDWARFDebugMap::GetModuleByCompUnitInfo( + CompileUnitInfo *comp_unit_info) { + if (!comp_unit_info->oso_sp) { + auto pos = m_oso_map.find( + {comp_unit_info->oso_path, comp_unit_info->oso_mod_time}); + if (pos != m_oso_map.end()) { + comp_unit_info->oso_sp = pos->second; + } else { + ObjectFile *obj_file = GetObjectFile(); + comp_unit_info->oso_sp = std::make_shared<OSOInfo>(); + m_oso_map[{comp_unit_info->oso_path, comp_unit_info->oso_mod_time}] = + comp_unit_info->oso_sp; + const char *oso_path = comp_unit_info->oso_path.GetCString(); + FileSpec oso_file(oso_path); + ConstString oso_object; + if (FileSystem::Instance().Exists(oso_file)) { + // The modification time returned by the FS can have a higher precision + // than the one from the CU. + auto oso_mod_time = std::chrono::time_point_cast<std::chrono::seconds>( + FileSystem::Instance().GetModificationTime(oso_file)); + if (oso_mod_time != comp_unit_info->oso_mod_time) { + obj_file->GetModule()->ReportError( + "debug map object file '%s' has changed (actual time is " + "%s, debug map time is %s" + ") since this executable was linked, file will be ignored", + oso_file.GetPath().c_str(), llvm::to_string(oso_mod_time).c_str(), + llvm::to_string(comp_unit_info->oso_mod_time).c_str()); + return nullptr; + } + + } else { + const bool must_exist = true; + + if (!ObjectFile::SplitArchivePathWithObject(oso_path, oso_file, + oso_object, must_exist)) { + return nullptr; + } + } + // Always create a new module for .o files. Why? Because we use the debug + // map, to add new sections to each .o file and even though a .o file + // might not have changed, the sections that get added to the .o file can + // change. + ArchSpec oso_arch; + // Only adopt the architecture from the module (not the vendor or OS) + // since .o files for "i386-apple-ios" will historically show up as "i386 + // -apple-macosx" due to the lack of a LC_VERSION_MIN_MACOSX or + // LC_VERSION_MIN_IPHONEOS load command... + oso_arch.SetTriple(m_obj_file->GetModule() + ->GetArchitecture() + .GetTriple() + .getArchName() + .str() + .c_str()); + comp_unit_info->oso_sp->module_sp = std::make_shared<DebugMapModule>( + obj_file->GetModule(), GetCompUnitInfoIndex(comp_unit_info), oso_file, + oso_arch, oso_object ? &oso_object : nullptr, 0, + oso_object ? comp_unit_info->oso_mod_time : llvm::sys::TimePoint<>()); + } + } + if (comp_unit_info->oso_sp) + return comp_unit_info->oso_sp->module_sp.get(); + return nullptr; +} + +bool SymbolFileDWARFDebugMap::GetFileSpecForSO(uint32_t oso_idx, + FileSpec &file_spec) { + if (oso_idx < m_compile_unit_infos.size()) { + if (m_compile_unit_infos[oso_idx].so_file) { + file_spec = m_compile_unit_infos[oso_idx].so_file; + return true; + } + } + return false; +} + +ObjectFile *SymbolFileDWARFDebugMap::GetObjectFileByOSOIndex(uint32_t oso_idx) { + Module *oso_module = GetModuleByOSOIndex(oso_idx); + if (oso_module) + return oso_module->GetObjectFile(); + return nullptr; +} + +SymbolFileDWARF * +SymbolFileDWARFDebugMap::GetSymbolFile(const SymbolContext &sc) { + return GetSymbolFile(*sc.comp_unit); +} + +SymbolFileDWARF * +SymbolFileDWARFDebugMap::GetSymbolFile(const CompileUnit &comp_unit) { + CompileUnitInfo *comp_unit_info = GetCompUnitInfo(comp_unit); + if (comp_unit_info) + return GetSymbolFileByCompUnitInfo(comp_unit_info); + return nullptr; +} + +ObjectFile *SymbolFileDWARFDebugMap::GetObjectFileByCompUnitInfo( + CompileUnitInfo *comp_unit_info) { + Module *oso_module = GetModuleByCompUnitInfo(comp_unit_info); + if (oso_module) + return oso_module->GetObjectFile(); + return nullptr; +} + +uint32_t SymbolFileDWARFDebugMap::GetCompUnitInfoIndex( + const CompileUnitInfo *comp_unit_info) { + if (!m_compile_unit_infos.empty()) { + const CompileUnitInfo *first_comp_unit_info = &m_compile_unit_infos.front(); + const CompileUnitInfo *last_comp_unit_info = &m_compile_unit_infos.back(); + if (first_comp_unit_info <= comp_unit_info && + comp_unit_info <= last_comp_unit_info) + return comp_unit_info - first_comp_unit_info; + } + return UINT32_MAX; +} + +SymbolFileDWARF * +SymbolFileDWARFDebugMap::GetSymbolFileByOSOIndex(uint32_t oso_idx) { + unsigned size = m_compile_unit_infos.size(); + if (oso_idx < size) + return GetSymbolFileByCompUnitInfo(&m_compile_unit_infos[oso_idx]); + return nullptr; +} + +SymbolFileDWARF * +SymbolFileDWARFDebugMap::GetSymbolFileAsSymbolFileDWARF(SymbolFile *sym_file) { + if (sym_file && + sym_file->GetPluginName() == SymbolFileDWARF::GetPluginNameStatic()) + return (SymbolFileDWARF *)sym_file; + return nullptr; +} + +SymbolFileDWARF *SymbolFileDWARFDebugMap::GetSymbolFileByCompUnitInfo( + CompileUnitInfo *comp_unit_info) { + Module *oso_module = GetModuleByCompUnitInfo(comp_unit_info); + if (oso_module) { + SymbolVendor *sym_vendor = oso_module->GetSymbolVendor(); + if (sym_vendor) + return GetSymbolFileAsSymbolFileDWARF(sym_vendor->GetSymbolFile()); + } + return nullptr; +} + +uint32_t SymbolFileDWARFDebugMap::CalculateAbilities() { + // In order to get the abilities of this plug-in, we look at the list of + // N_OSO entries (object files) from the symbol table and make sure that + // these files exist and also contain valid DWARF. If we get any of that then + // we return the abilities of the first N_OSO's DWARF. + + const uint32_t oso_index_count = GetNumCompileUnits(); + if (oso_index_count > 0) { + InitOSO(); + if (!m_compile_unit_infos.empty()) { + return SymbolFile::CompileUnits | SymbolFile::Functions | + SymbolFile::Blocks | SymbolFile::GlobalVariables | + SymbolFile::LocalVariables | SymbolFile::VariableTypes | + SymbolFile::LineTables; + } + } + return 0; +} + +uint32_t SymbolFileDWARFDebugMap::GetNumCompileUnits() { + InitOSO(); + return m_compile_unit_infos.size(); +} + +CompUnitSP SymbolFileDWARFDebugMap::ParseCompileUnitAtIndex(uint32_t cu_idx) { + CompUnitSP comp_unit_sp; + const uint32_t cu_count = GetNumCompileUnits(); + + if (cu_idx < cu_count) { + Module *oso_module = GetModuleByCompUnitInfo(&m_compile_unit_infos[cu_idx]); + if (oso_module) { + FileSpec so_file_spec; + if (GetFileSpecForSO(cu_idx, so_file_spec)) { + // User zero as the ID to match the compile unit at offset zero in each + // .o file since each .o file can only have one compile unit for now. + lldb::user_id_t cu_id = 0; + m_compile_unit_infos[cu_idx].compile_unit_sp = + std::make_shared<CompileUnit>( + m_obj_file->GetModule(), nullptr, so_file_spec, cu_id, + eLanguageTypeUnknown, eLazyBoolCalculate); + + if (m_compile_unit_infos[cu_idx].compile_unit_sp) { + // Let our symbol vendor know about this compile unit + m_obj_file->GetModule()->GetSymbolVendor()->SetCompileUnitAtIndex( + cu_idx, m_compile_unit_infos[cu_idx].compile_unit_sp); + } + } + } + comp_unit_sp = m_compile_unit_infos[cu_idx].compile_unit_sp; + } + + return comp_unit_sp; +} + +SymbolFileDWARFDebugMap::CompileUnitInfo * +SymbolFileDWARFDebugMap::GetCompUnitInfo(const SymbolContext &sc) { + return GetCompUnitInfo(*sc.comp_unit); +} + +SymbolFileDWARFDebugMap::CompileUnitInfo * +SymbolFileDWARFDebugMap::GetCompUnitInfo(const CompileUnit &comp_unit) { + const uint32_t cu_count = GetNumCompileUnits(); + for (uint32_t i = 0; i < cu_count; ++i) { + if (comp_unit == m_compile_unit_infos[i].compile_unit_sp.get()) + return &m_compile_unit_infos[i]; + } + return nullptr; +} + +size_t SymbolFileDWARFDebugMap::GetCompUnitInfosForModule( + const lldb_private::Module *module, + std::vector<CompileUnitInfo *> &cu_infos) { + const uint32_t cu_count = GetNumCompileUnits(); + for (uint32_t i = 0; i < cu_count; ++i) { + if (module == GetModuleByCompUnitInfo(&m_compile_unit_infos[i])) + cu_infos.push_back(&m_compile_unit_infos[i]); + } + return cu_infos.size(); +} + +lldb::LanguageType +SymbolFileDWARFDebugMap::ParseLanguage(CompileUnit &comp_unit) { + SymbolFileDWARF *oso_dwarf = GetSymbolFile(comp_unit); + if (oso_dwarf) + return oso_dwarf->ParseLanguage(comp_unit); + return eLanguageTypeUnknown; +} + +size_t SymbolFileDWARFDebugMap::ParseFunctions(CompileUnit &comp_unit) { + SymbolFileDWARF *oso_dwarf = GetSymbolFile(comp_unit); + if (oso_dwarf) + return oso_dwarf->ParseFunctions(comp_unit); + return 0; +} + +bool SymbolFileDWARFDebugMap::ParseLineTable(CompileUnit &comp_unit) { + SymbolFileDWARF *oso_dwarf = GetSymbolFile(comp_unit); + if (oso_dwarf) + return oso_dwarf->ParseLineTable(comp_unit); + return false; +} + +bool SymbolFileDWARFDebugMap::ParseDebugMacros(CompileUnit &comp_unit) { + SymbolFileDWARF *oso_dwarf = GetSymbolFile(comp_unit); + if (oso_dwarf) + return oso_dwarf->ParseDebugMacros(comp_unit); + return false; +} + +bool SymbolFileDWARFDebugMap::ParseSupportFiles(CompileUnit &comp_unit, + FileSpecList &support_files) { + SymbolFileDWARF *oso_dwarf = GetSymbolFile(comp_unit); + if (oso_dwarf) + return oso_dwarf->ParseSupportFiles(comp_unit, support_files); + return false; +} + +bool SymbolFileDWARFDebugMap::ParseIsOptimized(CompileUnit &comp_unit) { + SymbolFileDWARF *oso_dwarf = GetSymbolFile(comp_unit); + if (oso_dwarf) + return oso_dwarf->ParseIsOptimized(comp_unit); + return false; +} + +bool SymbolFileDWARFDebugMap::ParseImportedModules( + const SymbolContext &sc, std::vector<SourceModule> &imported_modules) { + SymbolFileDWARF *oso_dwarf = GetSymbolFile(sc); + if (oso_dwarf) + return oso_dwarf->ParseImportedModules(sc, imported_modules); + return false; +} + +size_t SymbolFileDWARFDebugMap::ParseBlocksRecursive(Function &func) { + CompileUnit *comp_unit = func.GetCompileUnit(); + if (!comp_unit) + return 0; + + SymbolFileDWARF *oso_dwarf = GetSymbolFile(*comp_unit); + if (oso_dwarf) + return oso_dwarf->ParseBlocksRecursive(func); + return 0; +} + +size_t SymbolFileDWARFDebugMap::ParseTypes(CompileUnit &comp_unit) { + SymbolFileDWARF *oso_dwarf = GetSymbolFile(comp_unit); + if (oso_dwarf) + return oso_dwarf->ParseTypes(comp_unit); + return 0; +} + +size_t +SymbolFileDWARFDebugMap::ParseVariablesForContext(const SymbolContext &sc) { + SymbolFileDWARF *oso_dwarf = GetSymbolFile(sc); + if (oso_dwarf) + return oso_dwarf->ParseVariablesForContext(sc); + return 0; +} + +Type *SymbolFileDWARFDebugMap::ResolveTypeUID(lldb::user_id_t type_uid) { + const uint64_t oso_idx = GetOSOIndexFromUserID(type_uid); + SymbolFileDWARF *oso_dwarf = GetSymbolFileByOSOIndex(oso_idx); + if (oso_dwarf) + return oso_dwarf->ResolveTypeUID(type_uid); + return nullptr; +} + +llvm::Optional<SymbolFile::ArrayInfo> +SymbolFileDWARFDebugMap::GetDynamicArrayInfoForUID( + lldb::user_id_t type_uid, const lldb_private::ExecutionContext *exe_ctx) { + const uint64_t oso_idx = GetOSOIndexFromUserID(type_uid); + SymbolFileDWARF *oso_dwarf = GetSymbolFileByOSOIndex(oso_idx); + if (oso_dwarf) + return oso_dwarf->GetDynamicArrayInfoForUID(type_uid, exe_ctx); + return llvm::None; +} + +bool SymbolFileDWARFDebugMap::CompleteType(CompilerType &compiler_type) { + bool success = false; + if (compiler_type) { + ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) -> bool { + if (oso_dwarf->HasForwardDeclForClangType(compiler_type)) { + oso_dwarf->CompleteType(compiler_type); + success = true; + return true; + } + return false; + }); + } + return success; +} + +uint32_t +SymbolFileDWARFDebugMap::ResolveSymbolContext(const Address &exe_so_addr, + SymbolContextItem resolve_scope, + SymbolContext &sc) { + uint32_t resolved_flags = 0; + Symtab *symtab = m_obj_file->GetSymtab(); + if (symtab) { + const addr_t exe_file_addr = exe_so_addr.GetFileAddress(); + + const DebugMap::Entry *debug_map_entry = + m_debug_map.FindEntryThatContains(exe_file_addr); + if (debug_map_entry) { + + sc.symbol = + symtab->SymbolAtIndex(debug_map_entry->data.GetExeSymbolIndex()); + + if (sc.symbol != nullptr) { + resolved_flags |= eSymbolContextSymbol; + + uint32_t oso_idx = 0; + CompileUnitInfo *comp_unit_info = + GetCompileUnitInfoForSymbolWithID(sc.symbol->GetID(), &oso_idx); + if (comp_unit_info) { + comp_unit_info->GetFileRangeMap(this); + Module *oso_module = GetModuleByCompUnitInfo(comp_unit_info); + if (oso_module) { + lldb::addr_t oso_file_addr = + exe_file_addr - debug_map_entry->GetRangeBase() + + debug_map_entry->data.GetOSOFileAddress(); + Address oso_so_addr; + if (oso_module->ResolveFileAddress(oso_file_addr, oso_so_addr)) { + resolved_flags |= + oso_module->GetSymbolVendor()->ResolveSymbolContext( + oso_so_addr, resolve_scope, sc); + } + } + } + } + } + } + return resolved_flags; +} + +uint32_t SymbolFileDWARFDebugMap::ResolveSymbolContext( + const FileSpec &file_spec, uint32_t line, bool check_inlines, + SymbolContextItem resolve_scope, SymbolContextList &sc_list) { + const uint32_t initial = sc_list.GetSize(); + const uint32_t cu_count = GetNumCompileUnits(); + + for (uint32_t i = 0; i < cu_count; ++i) { + // If we are checking for inlines, then we need to look through all compile + // units no matter if "file_spec" matches. + bool resolve = check_inlines; + + if (!resolve) { + FileSpec so_file_spec; + if (GetFileSpecForSO(i, so_file_spec)) { + // Match the full path if the incoming file_spec has a directory (not + // just a basename) + const bool full_match = (bool)file_spec.GetDirectory(); + resolve = FileSpec::Equal(file_spec, so_file_spec, full_match); + } + } + if (resolve) { + SymbolFileDWARF *oso_dwarf = GetSymbolFileByOSOIndex(i); + if (oso_dwarf) + oso_dwarf->ResolveSymbolContext(file_spec, line, check_inlines, + resolve_scope, sc_list); + } + } + return sc_list.GetSize() - initial; +} + +uint32_t SymbolFileDWARFDebugMap::PrivateFindGlobalVariables( + ConstString name, const CompilerDeclContext *parent_decl_ctx, + const std::vector<uint32_t> + &indexes, // Indexes into the symbol table that match "name" + uint32_t max_matches, VariableList &variables) { + const uint32_t original_size = variables.GetSize(); + const size_t match_count = indexes.size(); + for (size_t i = 0; i < match_count; ++i) { + uint32_t oso_idx; + CompileUnitInfo *comp_unit_info = + GetCompileUnitInfoForSymbolWithIndex(indexes[i], &oso_idx); + if (comp_unit_info) { + SymbolFileDWARF *oso_dwarf = GetSymbolFileByOSOIndex(oso_idx); + if (oso_dwarf) { + if (oso_dwarf->FindGlobalVariables(name, parent_decl_ctx, max_matches, + variables)) + if (variables.GetSize() > max_matches) + break; + } + } + } + return variables.GetSize() - original_size; +} + +uint32_t SymbolFileDWARFDebugMap::FindGlobalVariables( + ConstString name, const CompilerDeclContext *parent_decl_ctx, + uint32_t max_matches, VariableList &variables) { + + // Remember how many variables are in the list before we search. + const uint32_t original_size = variables.GetSize(); + + uint32_t total_matches = 0; + + ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) -> bool { + const uint32_t oso_matches = oso_dwarf->FindGlobalVariables( + name, parent_decl_ctx, max_matches, variables); + if (oso_matches > 0) { + total_matches += oso_matches; + + // Are we getting all matches? + if (max_matches == UINT32_MAX) + return false; // Yep, continue getting everything + + // If we have found enough matches, lets get out + if (max_matches >= total_matches) + return true; + + // Update the max matches for any subsequent calls to find globals in any + // other object files with DWARF + max_matches -= oso_matches; + } + + return false; + }); + + // Return the number of variable that were appended to the list + return variables.GetSize() - original_size; +} + +uint32_t +SymbolFileDWARFDebugMap::FindGlobalVariables(const RegularExpression ®ex, + uint32_t max_matches, + VariableList &variables) { + // Remember how many variables are in the list before we search. + const uint32_t original_size = variables.GetSize(); + + uint32_t total_matches = 0; + ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) -> bool { + const uint32_t oso_matches = + oso_dwarf->FindGlobalVariables(regex, max_matches, variables); + if (oso_matches > 0) { + total_matches += oso_matches; + + // Are we getting all matches? + if (max_matches == UINT32_MAX) + return false; // Yep, continue getting everything + + // If we have found enough matches, lets get out + if (max_matches >= total_matches) + return true; + + // Update the max matches for any subsequent calls to find globals in any + // other object files with DWARF + max_matches -= oso_matches; + } + + return false; + }); + + // Return the number of variable that were appended to the list + return variables.GetSize() - original_size; +} + +int SymbolFileDWARFDebugMap::SymbolContainsSymbolWithIndex( + uint32_t *symbol_idx_ptr, const CompileUnitInfo *comp_unit_info) { + const uint32_t symbol_idx = *symbol_idx_ptr; + + if (symbol_idx < comp_unit_info->first_symbol_index) + return -1; + + if (symbol_idx <= comp_unit_info->last_symbol_index) + return 0; + + return 1; +} + +int SymbolFileDWARFDebugMap::SymbolContainsSymbolWithID( + user_id_t *symbol_idx_ptr, const CompileUnitInfo *comp_unit_info) { + const user_id_t symbol_id = *symbol_idx_ptr; + + if (symbol_id < comp_unit_info->first_symbol_id) + return -1; + + if (symbol_id <= comp_unit_info->last_symbol_id) + return 0; + + return 1; +} + +SymbolFileDWARFDebugMap::CompileUnitInfo * +SymbolFileDWARFDebugMap::GetCompileUnitInfoForSymbolWithIndex( + uint32_t symbol_idx, uint32_t *oso_idx_ptr) { + const uint32_t oso_index_count = m_compile_unit_infos.size(); + CompileUnitInfo *comp_unit_info = nullptr; + if (oso_index_count) { + comp_unit_info = (CompileUnitInfo *)bsearch( + &symbol_idx, &m_compile_unit_infos[0], m_compile_unit_infos.size(), + sizeof(CompileUnitInfo), + (ComparisonFunction)SymbolContainsSymbolWithIndex); + } + + if (oso_idx_ptr) { + if (comp_unit_info != nullptr) + *oso_idx_ptr = comp_unit_info - &m_compile_unit_infos[0]; + else + *oso_idx_ptr = UINT32_MAX; + } + return comp_unit_info; +} + +SymbolFileDWARFDebugMap::CompileUnitInfo * +SymbolFileDWARFDebugMap::GetCompileUnitInfoForSymbolWithID( + user_id_t symbol_id, uint32_t *oso_idx_ptr) { + const uint32_t oso_index_count = m_compile_unit_infos.size(); + CompileUnitInfo *comp_unit_info = nullptr; + if (oso_index_count) { + comp_unit_info = (CompileUnitInfo *)::bsearch( + &symbol_id, &m_compile_unit_infos[0], m_compile_unit_infos.size(), + sizeof(CompileUnitInfo), + (ComparisonFunction)SymbolContainsSymbolWithID); + } + + if (oso_idx_ptr) { + if (comp_unit_info != nullptr) + *oso_idx_ptr = comp_unit_info - &m_compile_unit_infos[0]; + else + *oso_idx_ptr = UINT32_MAX; + } + return comp_unit_info; +} + +static void RemoveFunctionsWithModuleNotEqualTo(const ModuleSP &module_sp, + SymbolContextList &sc_list, + uint32_t start_idx) { + // We found functions in .o files. Not all functions in the .o files will + // have made it into the final output file. The ones that did make it into + // the final output file will have a section whose module matches the module + // from the ObjectFile for this SymbolFile. When the modules don't match, + // then we have something that was in a .o file, but doesn't map to anything + // in the final executable. + uint32_t i = start_idx; + while (i < sc_list.GetSize()) { + SymbolContext sc; + sc_list.GetContextAtIndex(i, sc); + if (sc.function) { + const SectionSP section_sp( + sc.function->GetAddressRange().GetBaseAddress().GetSection()); + if (section_sp->GetModule() != module_sp) { + sc_list.RemoveContextAtIndex(i); + continue; + } + } + ++i; + } +} + +uint32_t SymbolFileDWARFDebugMap::FindFunctions( + ConstString name, const CompilerDeclContext *parent_decl_ctx, + FunctionNameType name_type_mask, bool include_inlines, bool append, + SymbolContextList &sc_list) { + static Timer::Category func_cat(LLVM_PRETTY_FUNCTION); + Timer scoped_timer(func_cat, + "SymbolFileDWARFDebugMap::FindFunctions (name = %s)", + name.GetCString()); + + uint32_t initial_size = 0; + if (append) + initial_size = sc_list.GetSize(); + else + sc_list.Clear(); + + ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) -> bool { + uint32_t sc_idx = sc_list.GetSize(); + if (oso_dwarf->FindFunctions(name, parent_decl_ctx, name_type_mask, + include_inlines, true, sc_list)) { + RemoveFunctionsWithModuleNotEqualTo(m_obj_file->GetModule(), sc_list, + sc_idx); + } + return false; + }); + + return sc_list.GetSize() - initial_size; +} + +uint32_t SymbolFileDWARFDebugMap::FindFunctions(const RegularExpression ®ex, + bool include_inlines, + bool append, + SymbolContextList &sc_list) { + static Timer::Category func_cat(LLVM_PRETTY_FUNCTION); + Timer scoped_timer(func_cat, + "SymbolFileDWARFDebugMap::FindFunctions (regex = '%s')", + regex.GetText().str().c_str()); + + uint32_t initial_size = 0; + if (append) + initial_size = sc_list.GetSize(); + else + sc_list.Clear(); + + ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) -> bool { + uint32_t sc_idx = sc_list.GetSize(); + + if (oso_dwarf->FindFunctions(regex, include_inlines, true, sc_list)) { + RemoveFunctionsWithModuleNotEqualTo(m_obj_file->GetModule(), sc_list, + sc_idx); + } + return false; + }); + + return sc_list.GetSize() - initial_size; +} + +size_t SymbolFileDWARFDebugMap::GetTypes(SymbolContextScope *sc_scope, + lldb::TypeClass type_mask, + TypeList &type_list) { + static Timer::Category func_cat(LLVM_PRETTY_FUNCTION); + Timer scoped_timer(func_cat, + "SymbolFileDWARFDebugMap::GetTypes (type_mask = 0x%8.8x)", + type_mask); + + uint32_t initial_size = type_list.GetSize(); + SymbolFileDWARF *oso_dwarf = nullptr; + if (sc_scope) { + SymbolContext sc; + sc_scope->CalculateSymbolContext(&sc); + + CompileUnitInfo *cu_info = GetCompUnitInfo(sc); + if (cu_info) { + oso_dwarf = GetSymbolFileByCompUnitInfo(cu_info); + if (oso_dwarf) + oso_dwarf->GetTypes(sc_scope, type_mask, type_list); + } + } else { + ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) -> bool { + oso_dwarf->GetTypes(sc_scope, type_mask, type_list); + return false; + }); + } + return type_list.GetSize() - initial_size; +} + +std::vector<lldb_private::CallEdge> +SymbolFileDWARFDebugMap::ParseCallEdgesInFunction(UserID func_id) { + uint32_t oso_idx = GetOSOIndexFromUserID(func_id.GetID()); + SymbolFileDWARF *oso_dwarf = GetSymbolFileByOSOIndex(oso_idx); + if (oso_dwarf) + return oso_dwarf->ParseCallEdgesInFunction(func_id); + return {}; +} + +TypeSP SymbolFileDWARFDebugMap::FindDefinitionTypeForDWARFDeclContext( + const DWARFDeclContext &die_decl_ctx) { + TypeSP type_sp; + ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) -> bool { + type_sp = oso_dwarf->FindDefinitionTypeForDWARFDeclContext(die_decl_ctx); + return ((bool)type_sp); + }); + return type_sp; +} + +bool SymbolFileDWARFDebugMap::Supports_DW_AT_APPLE_objc_complete_type( + SymbolFileDWARF *skip_dwarf_oso) { + if (m_supports_DW_AT_APPLE_objc_complete_type == eLazyBoolCalculate) { + m_supports_DW_AT_APPLE_objc_complete_type = eLazyBoolNo; + ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) -> bool { + if (skip_dwarf_oso != oso_dwarf && + oso_dwarf->Supports_DW_AT_APPLE_objc_complete_type(nullptr)) { + m_supports_DW_AT_APPLE_objc_complete_type = eLazyBoolYes; + return true; + } + return false; + }); + } + return m_supports_DW_AT_APPLE_objc_complete_type == eLazyBoolYes; +} + +TypeSP SymbolFileDWARFDebugMap::FindCompleteObjCDefinitionTypeForDIE( + const DWARFDIE &die, ConstString type_name, + bool must_be_implementation) { + // If we have a debug map, we will have an Objective-C symbol whose name is + // the type name and whose type is eSymbolTypeObjCClass. If we can find that + // symbol and find its containing parent, we can locate the .o file that will + // contain the implementation definition since it will be scoped inside the + // N_SO and we can then locate the SymbolFileDWARF that corresponds to that + // N_SO. + SymbolFileDWARF *oso_dwarf = nullptr; + TypeSP type_sp; + ObjectFile *module_objfile = m_obj_file->GetModule()->GetObjectFile(); + if (module_objfile) { + Symtab *symtab = module_objfile->GetSymtab(); + if (symtab) { + Symbol *objc_class_symbol = symtab->FindFirstSymbolWithNameAndType( + type_name, eSymbolTypeObjCClass, Symtab::eDebugAny, + Symtab::eVisibilityAny); + if (objc_class_symbol) { + // Get the N_SO symbol that contains the objective C class symbol as + // this should be the .o file that contains the real definition... + const Symbol *source_file_symbol = symtab->GetParent(objc_class_symbol); + + if (source_file_symbol && + source_file_symbol->GetType() == eSymbolTypeSourceFile) { + const uint32_t source_file_symbol_idx = + symtab->GetIndexForSymbol(source_file_symbol); + if (source_file_symbol_idx != UINT32_MAX) { + CompileUnitInfo *compile_unit_info = + GetCompileUnitInfoForSymbolWithIndex(source_file_symbol_idx, + nullptr); + if (compile_unit_info) { + oso_dwarf = GetSymbolFileByCompUnitInfo(compile_unit_info); + if (oso_dwarf) { + TypeSP type_sp(oso_dwarf->FindCompleteObjCDefinitionTypeForDIE( + die, type_name, must_be_implementation)); + if (type_sp) { + return type_sp; + } + } + } + } + } + } + } + } + + // Only search all .o files for the definition if we don't need the + // implementation because otherwise, with a valid debug map we should have + // the ObjC class symbol and the code above should have found it. + if (!must_be_implementation) { + TypeSP type_sp; + + ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) -> bool { + type_sp = oso_dwarf->FindCompleteObjCDefinitionTypeForDIE( + die, type_name, must_be_implementation); + return (bool)type_sp; + }); + + return type_sp; + } + return TypeSP(); +} + +uint32_t SymbolFileDWARFDebugMap::FindTypes( + ConstString name, const CompilerDeclContext *parent_decl_ctx, + bool append, uint32_t max_matches, + llvm::DenseSet<lldb_private::SymbolFile *> &searched_symbol_files, + TypeMap &types) { + if (!append) + types.Clear(); + + const uint32_t initial_types_size = types.GetSize(); + + ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) -> bool { + oso_dwarf->FindTypes(name, parent_decl_ctx, append, max_matches, + searched_symbol_files, types); + return types.GetSize() >= max_matches; + }); + + return types.GetSize() - initial_types_size; +} + +// +// uint32_t +// SymbolFileDWARFDebugMap::FindTypes (const SymbolContext& sc, const +// RegularExpression& regex, bool append, uint32_t max_matches, Type::Encoding +// encoding, lldb::user_id_t udt_uid, TypeList& types) +//{ +// SymbolFileDWARF *oso_dwarf = GetSymbolFile (sc); +// if (oso_dwarf) +// return oso_dwarf->FindTypes (sc, regex, append, max_matches, encoding, +// udt_uid, types); +// return 0; +//} + +CompilerDeclContext SymbolFileDWARFDebugMap::FindNamespace( + lldb_private::ConstString name, + const CompilerDeclContext *parent_decl_ctx) { + CompilerDeclContext matching_namespace; + + ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) -> bool { + matching_namespace = oso_dwarf->FindNamespace(name, parent_decl_ctx); + + return (bool)matching_namespace; + }); + + return matching_namespace; +} + +void SymbolFileDWARFDebugMap::DumpClangAST(Stream &s) { + ForEachSymbolFile([&s](SymbolFileDWARF *oso_dwarf) -> bool { + oso_dwarf->DumpClangAST(s); + return true; + }); +} + +// PluginInterface protocol +lldb_private::ConstString SymbolFileDWARFDebugMap::GetPluginName() { + return GetPluginNameStatic(); +} + +uint32_t SymbolFileDWARFDebugMap::GetPluginVersion() { return 1; } + +lldb::CompUnitSP +SymbolFileDWARFDebugMap::GetCompileUnit(SymbolFileDWARF *oso_dwarf) { + if (oso_dwarf) { + const uint32_t cu_count = GetNumCompileUnits(); + for (uint32_t cu_idx = 0; cu_idx < cu_count; ++cu_idx) { + SymbolFileDWARF *oso_symfile = + GetSymbolFileByCompUnitInfo(&m_compile_unit_infos[cu_idx]); + if (oso_symfile == oso_dwarf) { + if (!m_compile_unit_infos[cu_idx].compile_unit_sp) + m_compile_unit_infos[cu_idx].compile_unit_sp = + ParseCompileUnitAtIndex(cu_idx); + + return m_compile_unit_infos[cu_idx].compile_unit_sp; + } + } + } + llvm_unreachable("this shouldn't happen"); +} + +SymbolFileDWARFDebugMap::CompileUnitInfo * +SymbolFileDWARFDebugMap::GetCompileUnitInfo(SymbolFileDWARF *oso_dwarf) { + if (oso_dwarf) { + const uint32_t cu_count = GetNumCompileUnits(); + for (uint32_t cu_idx = 0; cu_idx < cu_count; ++cu_idx) { + SymbolFileDWARF *oso_symfile = + GetSymbolFileByCompUnitInfo(&m_compile_unit_infos[cu_idx]); + if (oso_symfile == oso_dwarf) { + return &m_compile_unit_infos[cu_idx]; + } + } + } + return nullptr; +} + +void SymbolFileDWARFDebugMap::SetCompileUnit(SymbolFileDWARF *oso_dwarf, + const CompUnitSP &cu_sp) { + if (oso_dwarf) { + const uint32_t cu_count = GetNumCompileUnits(); + for (uint32_t cu_idx = 0; cu_idx < cu_count; ++cu_idx) { + SymbolFileDWARF *oso_symfile = + GetSymbolFileByCompUnitInfo(&m_compile_unit_infos[cu_idx]); + if (oso_symfile == oso_dwarf) { + if (m_compile_unit_infos[cu_idx].compile_unit_sp) { + assert(m_compile_unit_infos[cu_idx].compile_unit_sp.get() == + cu_sp.get()); + } else { + m_compile_unit_infos[cu_idx].compile_unit_sp = cu_sp; + m_obj_file->GetModule()->GetSymbolVendor()->SetCompileUnitAtIndex( + cu_idx, cu_sp); + } + } + } + } +} + +CompilerDeclContext +SymbolFileDWARFDebugMap::GetDeclContextForUID(lldb::user_id_t type_uid) { + const uint64_t oso_idx = GetOSOIndexFromUserID(type_uid); + SymbolFileDWARF *oso_dwarf = GetSymbolFileByOSOIndex(oso_idx); + if (oso_dwarf) + return oso_dwarf->GetDeclContextForUID(type_uid); + return CompilerDeclContext(); +} + +CompilerDeclContext +SymbolFileDWARFDebugMap::GetDeclContextContainingUID(lldb::user_id_t type_uid) { + const uint64_t oso_idx = GetOSOIndexFromUserID(type_uid); + SymbolFileDWARF *oso_dwarf = GetSymbolFileByOSOIndex(oso_idx); + if (oso_dwarf) + return oso_dwarf->GetDeclContextContainingUID(type_uid); + return CompilerDeclContext(); +} + +void SymbolFileDWARFDebugMap::ParseDeclsForContext( + lldb_private::CompilerDeclContext decl_ctx) { + ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) -> bool { + oso_dwarf->ParseDeclsForContext(decl_ctx); + return true; // Keep iterating + }); +} + +bool SymbolFileDWARFDebugMap::AddOSOFileRange(CompileUnitInfo *cu_info, + lldb::addr_t exe_file_addr, + lldb::addr_t exe_byte_size, + lldb::addr_t oso_file_addr, + lldb::addr_t oso_byte_size) { + const uint32_t debug_map_idx = + m_debug_map.FindEntryIndexThatContains(exe_file_addr); + if (debug_map_idx != UINT32_MAX) { + DebugMap::Entry *debug_map_entry = + m_debug_map.FindEntryThatContains(exe_file_addr); + debug_map_entry->data.SetOSOFileAddress(oso_file_addr); + addr_t range_size = std::min<addr_t>(exe_byte_size, oso_byte_size); + if (range_size == 0) { + range_size = std::max<addr_t>(exe_byte_size, oso_byte_size); + if (range_size == 0) + range_size = 1; + } + cu_info->file_range_map.Append( + FileRangeMap::Entry(oso_file_addr, range_size, exe_file_addr)); + return true; + } + return false; +} + +void SymbolFileDWARFDebugMap::FinalizeOSOFileRanges(CompileUnitInfo *cu_info) { + cu_info->file_range_map.Sort(); +#if defined(DEBUG_OSO_DMAP) + const FileRangeMap &oso_file_range_map = cu_info->GetFileRangeMap(this); + const size_t n = oso_file_range_map.GetSize(); + printf("SymbolFileDWARFDebugMap::FinalizeOSOFileRanges (cu_info = %p) %s\n", + cu_info, cu_info->oso_sp->module_sp->GetFileSpec().GetPath().c_str()); + for (size_t i = 0; i < n; ++i) { + const FileRangeMap::Entry &entry = oso_file_range_map.GetEntryRef(i); + printf("oso [0x%16.16" PRIx64 " - 0x%16.16" PRIx64 + ") ==> exe [0x%16.16" PRIx64 " - 0x%16.16" PRIx64 ")\n", + entry.GetRangeBase(), entry.GetRangeEnd(), entry.data, + entry.data + entry.GetByteSize()); + } +#endif +} + +lldb::addr_t +SymbolFileDWARFDebugMap::LinkOSOFileAddress(SymbolFileDWARF *oso_symfile, + lldb::addr_t oso_file_addr) { + CompileUnitInfo *cu_info = GetCompileUnitInfo(oso_symfile); + if (cu_info) { + const FileRangeMap::Entry *oso_range_entry = + cu_info->GetFileRangeMap(this).FindEntryThatContains(oso_file_addr); + if (oso_range_entry) { + const DebugMap::Entry *debug_map_entry = + m_debug_map.FindEntryThatContains(oso_range_entry->data); + if (debug_map_entry) { + const lldb::addr_t offset = + oso_file_addr - oso_range_entry->GetRangeBase(); + const lldb::addr_t exe_file_addr = + debug_map_entry->GetRangeBase() + offset; + return exe_file_addr; + } + } + } + return LLDB_INVALID_ADDRESS; +} + +bool SymbolFileDWARFDebugMap::LinkOSOAddress(Address &addr) { + // Make sure this address hasn't been fixed already + Module *exe_module = GetObjectFile()->GetModule().get(); + Module *addr_module = addr.GetModule().get(); + if (addr_module == exe_module) + return true; // Address is already in terms of the main executable module + + CompileUnitInfo *cu_info = GetCompileUnitInfo(GetSymbolFileAsSymbolFileDWARF( + addr_module->GetSymbolVendor()->GetSymbolFile())); + if (cu_info) { + const lldb::addr_t oso_file_addr = addr.GetFileAddress(); + const FileRangeMap::Entry *oso_range_entry = + cu_info->GetFileRangeMap(this).FindEntryThatContains(oso_file_addr); + if (oso_range_entry) { + const DebugMap::Entry *debug_map_entry = + m_debug_map.FindEntryThatContains(oso_range_entry->data); + if (debug_map_entry) { + const lldb::addr_t offset = + oso_file_addr - oso_range_entry->GetRangeBase(); + const lldb::addr_t exe_file_addr = + debug_map_entry->GetRangeBase() + offset; + return exe_module->ResolveFileAddress(exe_file_addr, addr); + } + } + } + return true; +} + +LineTable *SymbolFileDWARFDebugMap::LinkOSOLineTable(SymbolFileDWARF *oso_dwarf, + LineTable *line_table) { + CompileUnitInfo *cu_info = GetCompileUnitInfo(oso_dwarf); + if (cu_info) + return line_table->LinkLineTable(cu_info->GetFileRangeMap(this)); + return nullptr; +} + +size_t +SymbolFileDWARFDebugMap::AddOSOARanges(SymbolFileDWARF *dwarf2Data, + DWARFDebugAranges *debug_aranges) { + size_t num_line_entries_added = 0; + if (debug_aranges && dwarf2Data) { + CompileUnitInfo *compile_unit_info = GetCompileUnitInfo(dwarf2Data); + if (compile_unit_info) { + const FileRangeMap &file_range_map = + compile_unit_info->GetFileRangeMap(this); + for (size_t idx = 0; idx < file_range_map.GetSize(); idx++) { + const FileRangeMap::Entry *entry = file_range_map.GetEntryAtIndex(idx); + if (entry) { + debug_aranges->AppendRange(dwarf2Data->GetID(), entry->GetRangeBase(), + entry->GetRangeEnd()); + num_line_entries_added++; + } + } + } + } + return num_line_entries_added; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h new file mode 100644 index 000000000000..afc6142e8231 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h @@ -0,0 +1,362 @@ +//===-- SymbolFileDWARFDebugMap.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 SymbolFileDWARF_SymbolFileDWARFDebugMap_h_ +#define SymbolFileDWARF_SymbolFileDWARFDebugMap_h_ + +#include "lldb/Symbol/SymbolFile.h" +#include "lldb/Utility/RangeMap.h" +#include "llvm/Support/Chrono.h" +#include <bitset> +#include <map> +#include <vector> + +#include "UniqueDWARFASTType.h" + +class SymbolFileDWARF; +class DWARFDebugAranges; +class DWARFDeclContext; + +class SymbolFileDWARFDebugMap : public lldb_private::SymbolFile { +public: + // Static Functions + static void Initialize(); + + static void Terminate(); + + static lldb_private::ConstString GetPluginNameStatic(); + + static const char *GetPluginDescriptionStatic(); + + static lldb_private::SymbolFile * + CreateInstance(lldb_private::ObjectFile *obj_file); + + // Constructors and Destructors + SymbolFileDWARFDebugMap(lldb_private::ObjectFile *ofile); + ~SymbolFileDWARFDebugMap() override; + + uint32_t CalculateAbilities() override; + void InitializeObject() override; + + // Compile Unit function calls + uint32_t GetNumCompileUnits() override; + lldb::CompUnitSP ParseCompileUnitAtIndex(uint32_t index) override; + + lldb::LanguageType + ParseLanguage(lldb_private::CompileUnit &comp_unit) override; + + size_t ParseFunctions(lldb_private::CompileUnit &comp_unit) override; + + bool ParseLineTable(lldb_private::CompileUnit &comp_unit) override; + + bool ParseDebugMacros(lldb_private::CompileUnit &comp_unit) override; + + bool ParseSupportFiles(lldb_private::CompileUnit &comp_unit, + lldb_private::FileSpecList &support_files) override; + + bool ParseIsOptimized(lldb_private::CompileUnit &comp_unit) override; + + size_t ParseTypes(lldb_private::CompileUnit &comp_unit) override; + + bool ParseImportedModules( + const lldb_private::SymbolContext &sc, + std::vector<lldb_private::SourceModule> &imported_modules) override; + size_t ParseBlocksRecursive(lldb_private::Function &func) override; + size_t + ParseVariablesForContext(const lldb_private::SymbolContext &sc) override; + + lldb_private::Type *ResolveTypeUID(lldb::user_id_t type_uid) override; + llvm::Optional<ArrayInfo> GetDynamicArrayInfoForUID( + lldb::user_id_t type_uid, + const lldb_private::ExecutionContext *exe_ctx) override; + + lldb_private::CompilerDeclContext + GetDeclContextForUID(lldb::user_id_t uid) override; + lldb_private::CompilerDeclContext + GetDeclContextContainingUID(lldb::user_id_t uid) override; + void + ParseDeclsForContext(lldb_private::CompilerDeclContext decl_ctx) override; + + bool CompleteType(lldb_private::CompilerType &compiler_type) override; + uint32_t ResolveSymbolContext(const lldb_private::Address &so_addr, + lldb::SymbolContextItem resolve_scope, + lldb_private::SymbolContext &sc) override; + uint32_t + ResolveSymbolContext(const lldb_private::FileSpec &file_spec, uint32_t line, + bool check_inlines, + lldb::SymbolContextItem resolve_scope, + lldb_private::SymbolContextList &sc_list) override; + uint32_t + FindGlobalVariables(lldb_private::ConstString name, + const lldb_private::CompilerDeclContext *parent_decl_ctx, + uint32_t max_matches, + lldb_private::VariableList &variables) override; + uint32_t FindGlobalVariables(const lldb_private::RegularExpression ®ex, + uint32_t max_matches, + lldb_private::VariableList &variables) override; + uint32_t + FindFunctions(lldb_private::ConstString name, + const lldb_private::CompilerDeclContext *parent_decl_ctx, + lldb::FunctionNameType name_type_mask, bool include_inlines, + bool append, lldb_private::SymbolContextList &sc_list) override; + uint32_t FindFunctions(const lldb_private::RegularExpression ®ex, + bool include_inlines, bool append, + lldb_private::SymbolContextList &sc_list) override; + uint32_t + FindTypes(lldb_private::ConstString name, + const lldb_private::CompilerDeclContext *parent_decl_ctx, + bool append, uint32_t max_matches, + llvm::DenseSet<lldb_private::SymbolFile *> &searched_symbol_files, + lldb_private::TypeMap &types) override; + lldb_private::CompilerDeclContext FindNamespace( + lldb_private::ConstString name, + const lldb_private::CompilerDeclContext *parent_decl_ctx) override; + size_t GetTypes(lldb_private::SymbolContextScope *sc_scope, + lldb::TypeClass type_mask, + lldb_private::TypeList &type_list) override; + std::vector<lldb_private::CallEdge> + ParseCallEdgesInFunction(lldb_private::UserID func_id) override; + + void DumpClangAST(lldb_private::Stream &s) override; + + // PluginInterface protocol + lldb_private::ConstString GetPluginName() override; + + uint32_t GetPluginVersion() override; + +protected: + enum { kHaveInitializedOSOs = (1 << 0), kNumFlags }; + + friend class DebugMapModule; + friend class DWARFASTParserClang; + friend class DWARFCompileUnit; + friend class SymbolFileDWARF; + struct OSOInfo { + lldb::ModuleSP module_sp; + + OSOInfo() : module_sp() {} + }; + + typedef std::shared_ptr<OSOInfo> OSOInfoSP; + + typedef lldb_private::RangeDataVector<lldb::addr_t, lldb::addr_t, + lldb::addr_t> + FileRangeMap; + + // Class specific types + struct CompileUnitInfo { + lldb_private::FileSpec so_file; + lldb_private::ConstString oso_path; + llvm::sys::TimePoint<> oso_mod_time; + OSOInfoSP oso_sp; + lldb::CompUnitSP compile_unit_sp; + uint32_t first_symbol_index; + uint32_t last_symbol_index; + uint32_t first_symbol_id; + uint32_t last_symbol_id; + FileRangeMap file_range_map; + bool file_range_map_valid; + + CompileUnitInfo() + : so_file(), oso_path(), oso_mod_time(), oso_sp(), compile_unit_sp(), + first_symbol_index(UINT32_MAX), last_symbol_index(UINT32_MAX), + first_symbol_id(UINT32_MAX), last_symbol_id(UINT32_MAX), + file_range_map(), file_range_map_valid(false) {} + + const FileRangeMap &GetFileRangeMap(SymbolFileDWARFDebugMap *exe_symfile); + }; + + // Protected Member Functions + void InitOSO(); + + static uint32_t GetOSOIndexFromUserID(lldb::user_id_t uid) { + return (uint32_t)((uid >> 32ull) - 1ull); + } + + static SymbolFileDWARF *GetSymbolFileAsSymbolFileDWARF(SymbolFile *sym_file); + + bool GetFileSpecForSO(uint32_t oso_idx, lldb_private::FileSpec &file_spec); + + CompileUnitInfo *GetCompUnitInfo(const lldb_private::SymbolContext &sc); + CompileUnitInfo *GetCompUnitInfo(const lldb_private::CompileUnit &comp_unit); + + size_t GetCompUnitInfosForModule(const lldb_private::Module *oso_module, + std::vector<CompileUnitInfo *> &cu_infos); + + lldb_private::Module * + GetModuleByCompUnitInfo(CompileUnitInfo *comp_unit_info); + + lldb_private::Module *GetModuleByOSOIndex(uint32_t oso_idx); + + lldb_private::ObjectFile * + GetObjectFileByCompUnitInfo(CompileUnitInfo *comp_unit_info); + + lldb_private::ObjectFile *GetObjectFileByOSOIndex(uint32_t oso_idx); + + uint32_t GetCompUnitInfoIndex(const CompileUnitInfo *comp_unit_info); + + SymbolFileDWARF *GetSymbolFile(const lldb_private::SymbolContext &sc); + SymbolFileDWARF *GetSymbolFile(const lldb_private::CompileUnit &comp_unit); + + SymbolFileDWARF *GetSymbolFileByCompUnitInfo(CompileUnitInfo *comp_unit_info); + + SymbolFileDWARF *GetSymbolFileByOSOIndex(uint32_t oso_idx); + + // If closure returns "false", iteration continues. If it returns + // "true", iteration terminates. + void ForEachSymbolFile(std::function<bool(SymbolFileDWARF *)> closure) { + for (uint32_t oso_idx = 0, num_oso_idxs = m_compile_unit_infos.size(); + oso_idx < num_oso_idxs; ++oso_idx) { + if (SymbolFileDWARF *oso_dwarf = GetSymbolFileByOSOIndex(oso_idx)) { + if (closure(oso_dwarf)) + return; + } + } + } + + CompileUnitInfo *GetCompileUnitInfoForSymbolWithIndex(uint32_t symbol_idx, + uint32_t *oso_idx_ptr); + + CompileUnitInfo *GetCompileUnitInfoForSymbolWithID(lldb::user_id_t symbol_id, + uint32_t *oso_idx_ptr); + + static int + SymbolContainsSymbolWithIndex(uint32_t *symbol_idx_ptr, + const CompileUnitInfo *comp_unit_info); + + static int SymbolContainsSymbolWithID(lldb::user_id_t *symbol_idx_ptr, + const CompileUnitInfo *comp_unit_info); + + uint32_t PrivateFindGlobalVariables( + lldb_private::ConstString name, + const lldb_private::CompilerDeclContext *parent_decl_ctx, + const std::vector<uint32_t> &name_symbol_indexes, uint32_t max_matches, + lldb_private::VariableList &variables); + + void SetCompileUnit(SymbolFileDWARF *oso_dwarf, + const lldb::CompUnitSP &cu_sp); + + lldb::CompUnitSP GetCompileUnit(SymbolFileDWARF *oso_dwarf); + + CompileUnitInfo *GetCompileUnitInfo(SymbolFileDWARF *oso_dwarf); + + lldb::TypeSP + FindDefinitionTypeForDWARFDeclContext(const DWARFDeclContext &die_decl_ctx); + + bool Supports_DW_AT_APPLE_objc_complete_type(SymbolFileDWARF *skip_dwarf_oso); + + lldb::TypeSP FindCompleteObjCDefinitionTypeForDIE( + const DWARFDIE &die, lldb_private::ConstString type_name, + bool must_be_implementation); + + UniqueDWARFASTTypeMap &GetUniqueDWARFASTTypeMap() { + return m_unique_ast_type_map; + } + + // OSOEntry + class OSOEntry { + public: + OSOEntry() + : m_exe_sym_idx(UINT32_MAX), m_oso_file_addr(LLDB_INVALID_ADDRESS) {} + + OSOEntry(uint32_t exe_sym_idx, lldb::addr_t oso_file_addr) + : m_exe_sym_idx(exe_sym_idx), m_oso_file_addr(oso_file_addr) {} + + uint32_t GetExeSymbolIndex() const { return m_exe_sym_idx; } + + bool operator<(const OSOEntry &rhs) const { + return m_exe_sym_idx < rhs.m_exe_sym_idx; + } + + lldb::addr_t GetOSOFileAddress() const { return m_oso_file_addr; } + + void SetOSOFileAddress(lldb::addr_t oso_file_addr) { + m_oso_file_addr = oso_file_addr; + } + + protected: + uint32_t m_exe_sym_idx; + lldb::addr_t m_oso_file_addr; + }; + + typedef lldb_private::RangeDataVector<lldb::addr_t, lldb::addr_t, OSOEntry> + DebugMap; + + // Member Variables + std::bitset<kNumFlags> m_flags; + std::vector<CompileUnitInfo> m_compile_unit_infos; + std::vector<uint32_t> m_func_indexes; // Sorted by address + std::vector<uint32_t> m_glob_indexes; + std::map<std::pair<lldb_private::ConstString, llvm::sys::TimePoint<>>, + OSOInfoSP> + m_oso_map; + UniqueDWARFASTTypeMap m_unique_ast_type_map; + lldb_private::LazyBool m_supports_DW_AT_APPLE_objc_complete_type; + DebugMap m_debug_map; + + // When an object file from the debug map gets parsed in + // SymbolFileDWARF, it needs to tell the debug map about the object + // files addresses by calling this function once for each N_FUN, + // N_GSYM and N_STSYM and after all entries in the debug map have + // been matched up, FinalizeOSOFileRanges() should be called. + bool AddOSOFileRange(CompileUnitInfo *cu_info, lldb::addr_t exe_file_addr, + lldb::addr_t exe_byte_size, lldb::addr_t oso_file_addr, + lldb::addr_t oso_byte_size); + + // Called after calling AddOSOFileRange() for each object file debug + // map entry to finalize the info for the unlinked compile unit. + void FinalizeOSOFileRanges(CompileUnitInfo *cu_info); + + /// Convert \a addr from a .o file address, to an executable address. + /// + /// \param[in] addr + /// A section offset address from a .o file + /// + /// \return + /// Returns true if \a addr was converted to be an executable + /// section/offset address, false otherwise. + bool LinkOSOAddress(lldb_private::Address &addr); + + /// Convert a .o file "file address" to an executable "file address". + /// + /// \param[in] oso_symfile + /// The DWARF symbol file that contains \a oso_file_addr + /// + /// \param[in] oso_file_addr + /// A .o file "file address" to convert. + /// + /// \return + /// LLDB_INVALID_ADDRESS if \a oso_file_addr is not in the + /// linked executable, otherwise a valid "file address" from the + /// linked executable that contains the debug map. + lldb::addr_t LinkOSOFileAddress(SymbolFileDWARF *oso_symfile, + lldb::addr_t oso_file_addr); + + /// Given a line table full of lines with "file addresses" that are + /// for a .o file represented by \a oso_symfile, link a new line table + /// and return it. + /// + /// \param[in] oso_symfile + /// The DWARF symbol file that produced the \a line_table + /// + /// \param[in] addr + /// A section offset address from a .o file + /// + /// \return + /// Returns a valid line table full of linked addresses, or NULL + /// if none of the line table addresses exist in the main + /// executable. + lldb_private::LineTable * + LinkOSOLineTable(SymbolFileDWARF *oso_symfile, + lldb_private::LineTable *line_table); + + size_t AddOSOARanges(SymbolFileDWARF *dwarf2Data, + DWARFDebugAranges *debug_aranges); +}; + +#endif // #ifndef SymbolFileDWARF_SymbolFileDWARFDebugMap_h_ diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.cpp new file mode 100644 index 000000000000..c5b54b65ea29 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.cpp @@ -0,0 +1,153 @@ +//===-- SymbolFileDWARFDwo.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 "SymbolFileDWARFDwo.h" + +#include "lldb/Core/Section.h" +#include "lldb/Expression/DWARFExpression.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Utility/LLDBAssert.h" +#include "llvm/Support/Casting.h" + +#include "DWARFCompileUnit.h" +#include "DWARFDebugInfo.h" +#include "DWARFUnit.h" + +using namespace lldb; +using namespace lldb_private; + +SymbolFileDWARFDwo::SymbolFileDWARFDwo(ObjectFileSP objfile, + DWARFCompileUnit &dwarf_cu) + : SymbolFileDWARF(objfile.get(), objfile->GetSectionList( + /*update_module_section_list*/ false)), + m_obj_file_sp(objfile), m_base_dwarf_cu(dwarf_cu) { + SetID(((lldb::user_id_t)dwarf_cu.GetID()) << 32); +} + +void SymbolFileDWARFDwo::LoadSectionData(lldb::SectionType sect_type, + DWARFDataExtractor &data) { + const SectionList *section_list = + m_obj_file->GetSectionList(false /* update_module_section_list */); + if (section_list) { + SectionSP section_sp(section_list->FindSectionByType(sect_type, true)); + if (section_sp) { + + if (m_obj_file->ReadSectionData(section_sp.get(), data) != 0) + return; + + data.Clear(); + } + } + + SymbolFileDWARF::LoadSectionData(sect_type, data); +} + +lldb::CompUnitSP +SymbolFileDWARFDwo::ParseCompileUnit(DWARFCompileUnit &dwarf_cu) { + assert(GetCompileUnit() == &dwarf_cu && + "SymbolFileDWARFDwo::ParseCompileUnit called with incompatible " + "compile unit"); + return GetBaseSymbolFile().ParseCompileUnit(m_base_dwarf_cu); +} + +DWARFCompileUnit *SymbolFileDWARFDwo::GetCompileUnit() { + if (!m_cu) + m_cu = ComputeCompileUnit(); + return m_cu; +} + +DWARFCompileUnit *SymbolFileDWARFDwo::ComputeCompileUnit() { + DWARFDebugInfo *debug_info = DebugInfo(); + if (!debug_info) + return nullptr; + + // Right now we only support dwo files with one compile unit. If we don't have + // type units, we can just check for the unit count. + if (!debug_info->ContainsTypeUnits() && debug_info->GetNumUnits() == 1) + return llvm::cast<DWARFCompileUnit>(debug_info->GetUnitAtIndex(0)); + + // Otherwise, we have to run through all units, and find the compile unit that + // way. + DWARFCompileUnit *cu = nullptr; + for (size_t i = 0; i < debug_info->GetNumUnits(); ++i) { + if (auto *candidate = + llvm::dyn_cast<DWARFCompileUnit>(debug_info->GetUnitAtIndex(i))) { + if (cu) + return nullptr; // More that one CU found. + cu = candidate; + } + } + return cu; +} + +DWARFUnit * +SymbolFileDWARFDwo::GetDWARFCompileUnit(lldb_private::CompileUnit *comp_unit) { + return GetCompileUnit(); +} + +SymbolFileDWARF::DIEToTypePtr &SymbolFileDWARFDwo::GetDIEToType() { + return GetBaseSymbolFile().GetDIEToType(); +} + +SymbolFileDWARF::DIEToVariableSP &SymbolFileDWARFDwo::GetDIEToVariable() { + return GetBaseSymbolFile().GetDIEToVariable(); +} + +SymbolFileDWARF::DIEToClangType & +SymbolFileDWARFDwo::GetForwardDeclDieToClangType() { + return GetBaseSymbolFile().GetForwardDeclDieToClangType(); +} + +SymbolFileDWARF::ClangTypeToDIE & +SymbolFileDWARFDwo::GetForwardDeclClangTypeToDie() { + return GetBaseSymbolFile().GetForwardDeclClangTypeToDie(); +} + +size_t SymbolFileDWARFDwo::GetObjCMethodDIEOffsets( + lldb_private::ConstString class_name, DIEArray &method_die_offsets) { + return GetBaseSymbolFile().GetObjCMethodDIEOffsets(class_name, + method_die_offsets); +} + +UniqueDWARFASTTypeMap &SymbolFileDWARFDwo::GetUniqueDWARFASTTypeMap() { + return GetBaseSymbolFile().GetUniqueDWARFASTTypeMap(); +} + +lldb::TypeSP SymbolFileDWARFDwo::FindDefinitionTypeForDWARFDeclContext( + const DWARFDeclContext &die_decl_ctx) { + return GetBaseSymbolFile().FindDefinitionTypeForDWARFDeclContext( + die_decl_ctx); +} + +lldb::TypeSP SymbolFileDWARFDwo::FindCompleteObjCDefinitionTypeForDIE( + const DWARFDIE &die, lldb_private::ConstString type_name, + bool must_be_implementation) { + return GetBaseSymbolFile().FindCompleteObjCDefinitionTypeForDIE( + die, type_name, must_be_implementation); +} + +SymbolFileDWARF &SymbolFileDWARFDwo::GetBaseSymbolFile() { + return m_base_dwarf_cu.GetSymbolFileDWARF(); +} + +DWARFExpression::LocationListFormat +SymbolFileDWARFDwo::GetLocationListFormat() const { + return DWARFExpression::SplitDwarfLocationList; +} + +TypeSystem * +SymbolFileDWARFDwo::GetTypeSystemForLanguage(LanguageType language) { + return GetBaseSymbolFile().GetTypeSystemForLanguage(language); +} + +DWARFDIE +SymbolFileDWARFDwo::GetDIE(const DIERef &die_ref) { + if (*die_ref.dwo_num() == GetDwoNum()) + return DebugInfo()->GetDIE(die_ref); + return GetBaseSymbolFile().GetDIE(die_ref); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.h new file mode 100644 index 000000000000..9b2f3bb84c4f --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.h @@ -0,0 +1,79 @@ +//===-- SymbolFileDWARFDwo.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 SymbolFileDWARFDwo_SymbolFileDWARFDwo_h_ +#define SymbolFileDWARFDwo_SymbolFileDWARFDwo_h_ + +#include "SymbolFileDWARF.h" + +class SymbolFileDWARFDwo : public SymbolFileDWARF { +public: + SymbolFileDWARFDwo(lldb::ObjectFileSP objfile, DWARFCompileUnit &dwarf_cu); + + ~SymbolFileDWARFDwo() override = default; + + lldb::CompUnitSP ParseCompileUnit(DWARFCompileUnit &dwarf_cu) override; + + DWARFCompileUnit *GetCompileUnit(); + + DWARFUnit * + GetDWARFCompileUnit(lldb_private::CompileUnit *comp_unit) override; + + lldb_private::DWARFExpression::LocationListFormat + GetLocationListFormat() const override; + + size_t GetObjCMethodDIEOffsets(lldb_private::ConstString class_name, + DIEArray &method_die_offsets) override; + + lldb_private::TypeSystem * + GetTypeSystemForLanguage(lldb::LanguageType language) override; + + DWARFDIE + GetDIE(const DIERef &die_ref) override; + + std::unique_ptr<SymbolFileDWARFDwo> + GetDwoSymbolFileForCompileUnit(DWARFUnit &dwarf_cu, + const DWARFDebugInfoEntry &cu_die) override { + return nullptr; + } + + DWARFCompileUnit *GetBaseCompileUnit() override { return &m_base_dwarf_cu; } + + llvm::Optional<uint32_t> GetDwoNum() override { return GetID() >> 32; } + +protected: + void LoadSectionData(lldb::SectionType sect_type, + lldb_private::DWARFDataExtractor &data) override; + + DIEToTypePtr &GetDIEToType() override; + + DIEToVariableSP &GetDIEToVariable() override; + + DIEToClangType &GetForwardDeclDieToClangType() override; + + ClangTypeToDIE &GetForwardDeclClangTypeToDie() override; + + UniqueDWARFASTTypeMap &GetUniqueDWARFASTTypeMap() override; + + lldb::TypeSP FindDefinitionTypeForDWARFDeclContext( + const DWARFDeclContext &die_decl_ctx) override; + + lldb::TypeSP FindCompleteObjCDefinitionTypeForDIE( + const DWARFDIE &die, lldb_private::ConstString type_name, + bool must_be_implementation) override; + + SymbolFileDWARF &GetBaseSymbolFile(); + + DWARFCompileUnit *ComputeCompileUnit(); + + lldb::ObjectFileSP m_obj_file_sp; + DWARFCompileUnit &m_base_dwarf_cu; + DWARFCompileUnit *m_cu = nullptr; +}; + +#endif // SymbolFileDWARFDwo_SymbolFileDWARFDwo_h_ diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwoDwp.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwoDwp.cpp new file mode 100644 index 000000000000..efea192b17ce --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwoDwp.cpp @@ -0,0 +1,35 @@ +//===-- SymbolFileDWARFDwoDwp.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 "SymbolFileDWARFDwoDwp.h" + +#include "lldb/Core/Section.h" +#include "lldb/Expression/DWARFExpression.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Utility/LLDBAssert.h" + +#include "DWARFUnit.h" +#include "DWARFDebugInfo.h" + +using namespace lldb; +using namespace lldb_private; + +SymbolFileDWARFDwoDwp::SymbolFileDWARFDwoDwp(SymbolFileDWARFDwp *dwp_symfile, + ObjectFileSP objfile, + DWARFCompileUnit &dwarf_cu, + uint64_t dwo_id) + : SymbolFileDWARFDwo(objfile, dwarf_cu), m_dwp_symfile(dwp_symfile), + m_dwo_id(dwo_id) {} + +void SymbolFileDWARFDwoDwp::LoadSectionData(lldb::SectionType sect_type, + DWARFDataExtractor &data) { + if (m_dwp_symfile->LoadSectionData(m_dwo_id, sect_type, data)) + return; + + SymbolFileDWARF::LoadSectionData(sect_type, data); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwoDwp.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwoDwp.h new file mode 100644 index 000000000000..2105e1a8f6cb --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwoDwp.h @@ -0,0 +1,29 @@ +//===-- SymbolFileDWARFDwoDwp.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 SymbolFileDWARFDwoDwp_SymbolFileDWARFDwoDwp_h_ +#define SymbolFileDWARFDwoDwp_SymbolFileDWARFDwoDwp_h_ + +#include "SymbolFileDWARFDwo.h" +#include "SymbolFileDWARFDwp.h" + +class SymbolFileDWARFDwoDwp : public SymbolFileDWARFDwo { +public: + SymbolFileDWARFDwoDwp(SymbolFileDWARFDwp *dwp_symfile, + lldb::ObjectFileSP objfile, DWARFCompileUnit &dwarf_cu, + uint64_t dwo_id); + +protected: + void LoadSectionData(lldb::SectionType sect_type, + lldb_private::DWARFDataExtractor &data) override; + + SymbolFileDWARFDwp *m_dwp_symfile; + uint64_t m_dwo_id; +}; + +#endif // SymbolFileDWARFDwoDwp_SymbolFileDWARFDwoDwp_h_ diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwp.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwp.cpp new file mode 100644 index 000000000000..08e6e1c8c2f3 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwp.cpp @@ -0,0 +1,138 @@ +//===-- SymbolFileDWARFDwp.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 "SymbolFileDWARFDwp.h" + +#include "lldb/Core/Section.h" +#include "lldb/Symbol/ObjectFile.h" + +#include "SymbolFileDWARFDwoDwp.h" + +static llvm::DWARFSectionKind +lldbSectTypeToLlvmSectionKind(lldb::SectionType type) { + switch (type) { + case lldb::eSectionTypeDWARFDebugInfo: + return llvm::DW_SECT_INFO; + // case lldb::eSectionTypeDWARFDebugTypes: + // return llvm::DW_SECT_TYPES; + case lldb::eSectionTypeDWARFDebugAbbrev: + return llvm::DW_SECT_ABBREV; + case lldb::eSectionTypeDWARFDebugLine: + return llvm::DW_SECT_LINE; + case lldb::eSectionTypeDWARFDebugLoc: + return llvm::DW_SECT_LOC; + case lldb::eSectionTypeDWARFDebugStrOffsets: + return llvm::DW_SECT_STR_OFFSETS; + // case lldb::eSectionTypeDWARFDebugMacinfo: + // return llvm::DW_SECT_MACINFO; + case lldb::eSectionTypeDWARFDebugMacro: + return llvm::DW_SECT_MACRO; + default: + // Note: 0 is an invalid dwarf section kind. + return llvm::DWARFSectionKind(0); + } +} + +std::unique_ptr<SymbolFileDWARFDwp> +SymbolFileDWARFDwp::Create(lldb::ModuleSP module_sp, + const lldb_private::FileSpec &file_spec) { + const lldb::offset_t file_offset = 0; + lldb::DataBufferSP file_data_sp; + lldb::offset_t file_data_offset = 0; + lldb::ObjectFileSP obj_file = lldb_private::ObjectFile::FindPlugin( + module_sp, &file_spec, file_offset, + lldb_private::FileSystem::Instance().GetByteSize(file_spec), file_data_sp, + file_data_offset); + if (obj_file == nullptr) + return nullptr; + + std::unique_ptr<SymbolFileDWARFDwp> dwp_symfile( + new SymbolFileDWARFDwp(module_sp, obj_file)); + + lldb_private::DWARFDataExtractor debug_cu_index; + if (!dwp_symfile->LoadRawSectionData(lldb::eSectionTypeDWARFDebugCuIndex, + debug_cu_index)) + return nullptr; + + llvm::DataExtractor llvm_debug_cu_index( + llvm::StringRef(debug_cu_index.PeekCStr(0), debug_cu_index.GetByteSize()), + debug_cu_index.GetByteOrder() == lldb::eByteOrderLittle, + debug_cu_index.GetAddressByteSize()); + if (!dwp_symfile->m_debug_cu_index.parse(llvm_debug_cu_index)) + return nullptr; + dwp_symfile->InitDebugCUIndexMap(); + return dwp_symfile; +} + +void SymbolFileDWARFDwp::InitDebugCUIndexMap() { + m_debug_cu_index_map.clear(); + for (const auto &entry : m_debug_cu_index.getRows()) + m_debug_cu_index_map.emplace(entry.getSignature(), &entry); +} + +SymbolFileDWARFDwp::SymbolFileDWARFDwp(lldb::ModuleSP module_sp, + lldb::ObjectFileSP obj_file) + : m_obj_file(std::move(obj_file)), m_debug_cu_index(llvm::DW_SECT_INFO) +{} + +std::unique_ptr<SymbolFileDWARFDwo> +SymbolFileDWARFDwp::GetSymbolFileForDwoId(DWARFCompileUnit &dwarf_cu, + uint64_t dwo_id) { + return std::unique_ptr<SymbolFileDWARFDwo>( + new SymbolFileDWARFDwoDwp(this, m_obj_file, dwarf_cu, dwo_id)); +} + +bool SymbolFileDWARFDwp::LoadSectionData( + uint64_t dwo_id, lldb::SectionType sect_type, + lldb_private::DWARFDataExtractor &data) { + lldb_private::DWARFDataExtractor section_data; + if (!LoadRawSectionData(sect_type, section_data)) + return false; + + auto it = m_debug_cu_index_map.find(dwo_id); + if (it == m_debug_cu_index_map.end()) + return false; + + auto *offsets = + it->second->getOffset(lldbSectTypeToLlvmSectionKind(sect_type)); + if (offsets) { + data.SetData(section_data, offsets->Offset, offsets->Length); + } else { + data.SetData(section_data, 0, section_data.GetByteSize()); + } + return true; +} + +bool SymbolFileDWARFDwp::LoadRawSectionData( + lldb::SectionType sect_type, lldb_private::DWARFDataExtractor &data) { + std::lock_guard<std::mutex> lock(m_sections_mutex); + + auto it = m_sections.find(sect_type); + if (it != m_sections.end()) { + if (it->second.GetByteSize() == 0) + return false; + + data = it->second; + return true; + } + + const lldb_private::SectionList *section_list = + m_obj_file->GetSectionList(false /* update_module_section_list */); + if (section_list) { + lldb::SectionSP section_sp( + section_list->FindSectionByType(sect_type, true)); + if (section_sp) { + if (m_obj_file->ReadSectionData(section_sp.get(), data) != 0) { + m_sections[sect_type] = data; + return true; + } + } + } + m_sections[sect_type].Clear(); + return false; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwp.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwp.h new file mode 100644 index 000000000000..ef06b9dca8bb --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwp.h @@ -0,0 +1,50 @@ +//===-- SymbolFileDWARFDwp.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 SymbolFileDWARFDwp_SymbolFileDWARFDwp_h_ +#define SymbolFileDWARFDwp_SymbolFileDWARFDwp_h_ + +#include <memory> + +#include "llvm/DebugInfo/DWARF/DWARFUnitIndex.h" + +#include "lldb/Core/Module.h" + +#include "DWARFDataExtractor.h" +#include "SymbolFileDWARFDwo.h" + +class SymbolFileDWARFDwp { +public: + static std::unique_ptr<SymbolFileDWARFDwp> + Create(lldb::ModuleSP module_sp, const lldb_private::FileSpec &file_spec); + + std::unique_ptr<SymbolFileDWARFDwo> + GetSymbolFileForDwoId(DWARFCompileUnit &dwarf_cu, uint64_t dwo_id); + + bool LoadSectionData(uint64_t dwo_id, lldb::SectionType sect_type, + lldb_private::DWARFDataExtractor &data); + +private: + explicit SymbolFileDWARFDwp(lldb::ModuleSP module_sp, + lldb::ObjectFileSP obj_file); + + bool LoadRawSectionData(lldb::SectionType sect_type, + lldb_private::DWARFDataExtractor &data); + + void InitDebugCUIndexMap(); + + lldb::ObjectFileSP m_obj_file; + + std::mutex m_sections_mutex; + std::map<lldb::SectionType, lldb_private::DWARFDataExtractor> m_sections; + + llvm::DWARFUnitIndex m_debug_cu_index; + std::map<uint64_t, const llvm::DWARFUnitIndex::Entry *> m_debug_cu_index_map; +}; + +#endif // SymbolFileDWARFDwp_SymbolFileDWARFDwp_h_ diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/UniqueDWARFASTType.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/UniqueDWARFASTType.cpp new file mode 100644 index 000000000000..8da7e2226266 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/UniqueDWARFASTType.cpp @@ -0,0 +1,73 @@ +//===-- UniqueDWARFASTType.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 "UniqueDWARFASTType.h" + +#include "lldb/Symbol/Declaration.h" + +bool UniqueDWARFASTTypeList::Find(const DWARFDIE &die, + const lldb_private::Declaration &decl, + const int32_t byte_size, + UniqueDWARFASTType &entry) const { + for (const UniqueDWARFASTType &udt : m_collection) { + // Make sure the tags match + if (udt.m_die.Tag() == die.Tag()) { + // Validate byte sizes of both types only if both are valid. + if (udt.m_byte_size < 0 || byte_size < 0 || + udt.m_byte_size == byte_size) { + // Make sure the file and line match + if (udt.m_declaration == decl) { + // The type has the same name, and was defined on the same file and + // line. Now verify all of the parent DIEs match. + DWARFDIE parent_arg_die = die.GetParent(); + DWARFDIE parent_pos_die = udt.m_die.GetParent(); + bool match = true; + bool done = false; + while (!done && match && parent_arg_die && parent_pos_die) { + const dw_tag_t parent_arg_tag = parent_arg_die.Tag(); + const dw_tag_t parent_pos_tag = parent_pos_die.Tag(); + if (parent_arg_tag == parent_pos_tag) { + switch (parent_arg_tag) { + case DW_TAG_class_type: + case DW_TAG_structure_type: + case DW_TAG_union_type: + case DW_TAG_namespace: { + const char *parent_arg_die_name = parent_arg_die.GetName(); + if (parent_arg_die_name == + nullptr) // Anonymous (i.e. no-name) struct + { + match = false; + } else { + const char *parent_pos_die_name = parent_pos_die.GetName(); + if (parent_pos_die_name == nullptr || + ((parent_arg_die_name != parent_pos_die_name) && + strcmp(parent_arg_die_name, parent_pos_die_name))) + match = false; + } + } break; + + case DW_TAG_compile_unit: + case DW_TAG_partial_unit: + done = true; + break; + } + } + parent_arg_die = parent_arg_die.GetParent(); + parent_pos_die = parent_pos_die.GetParent(); + } + + if (match) { + entry = udt; + return true; + } + } + } + } + } + return false; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/UniqueDWARFASTType.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/UniqueDWARFASTType.h new file mode 100644 index 000000000000..1269dbac7126 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/UniqueDWARFASTType.h @@ -0,0 +1,103 @@ +//===-- UniqueDWARFASTType.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 lldb_UniqueDWARFASTType_h_ +#define lldb_UniqueDWARFASTType_h_ + +#include <vector> + +#include "llvm/ADT/DenseMap.h" + +#include "DWARFDIE.h" +#include "lldb/Symbol/Declaration.h" + +class UniqueDWARFASTType { +public: + // Constructors and Destructors + UniqueDWARFASTType() + : m_type_sp(), m_die(), m_declaration(), + m_byte_size( + -1) // Set to negative value to make sure we have a valid value + {} + + UniqueDWARFASTType(lldb::TypeSP &type_sp, const DWARFDIE &die, + const lldb_private::Declaration &decl, int32_t byte_size) + : m_type_sp(type_sp), m_die(die), m_declaration(decl), + m_byte_size(byte_size) {} + + UniqueDWARFASTType(const UniqueDWARFASTType &rhs) + : m_type_sp(rhs.m_type_sp), m_die(rhs.m_die), + m_declaration(rhs.m_declaration), m_byte_size(rhs.m_byte_size) {} + + ~UniqueDWARFASTType() {} + + UniqueDWARFASTType &operator=(const UniqueDWARFASTType &rhs) { + if (this != &rhs) { + m_type_sp = rhs.m_type_sp; + m_die = rhs.m_die; + m_declaration = rhs.m_declaration; + m_byte_size = rhs.m_byte_size; + } + return *this; + } + + lldb::TypeSP m_type_sp; + DWARFDIE m_die; + lldb_private::Declaration m_declaration; + int32_t m_byte_size; +}; + +class UniqueDWARFASTTypeList { +public: + UniqueDWARFASTTypeList() : m_collection() {} + + ~UniqueDWARFASTTypeList() {} + + uint32_t GetSize() { return (uint32_t)m_collection.size(); } + + void Append(const UniqueDWARFASTType &entry) { + m_collection.push_back(entry); + } + + bool Find(const DWARFDIE &die, const lldb_private::Declaration &decl, + const int32_t byte_size, UniqueDWARFASTType &entry) const; + +protected: + typedef std::vector<UniqueDWARFASTType> collection; + collection m_collection; +}; + +class UniqueDWARFASTTypeMap { +public: + UniqueDWARFASTTypeMap() : m_collection() {} + + ~UniqueDWARFASTTypeMap() {} + + void Insert(lldb_private::ConstString name, + const UniqueDWARFASTType &entry) { + m_collection[name.GetCString()].Append(entry); + } + + bool Find(lldb_private::ConstString name, const DWARFDIE &die, + const lldb_private::Declaration &decl, const int32_t byte_size, + UniqueDWARFASTType &entry) const { + const char *unique_name_cstr = name.GetCString(); + collection::const_iterator pos = m_collection.find(unique_name_cstr); + if (pos != m_collection.end()) { + return pos->second.Find(die, decl, byte_size, entry); + } + return false; + } + +protected: + // A unique name string should be used + typedef llvm::DenseMap<const char *, UniqueDWARFASTTypeList> collection; + collection m_collection; +}; + +#endif // lldb_UniqueDWARFASTType_h_ |