diff options
Diffstat (limited to 'contrib/llvm-project/lldb/source/Plugins/SymbolFile')
96 files changed, 36737 insertions, 0 deletions
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.cpp new file mode 100644 index 000000000000..3977dc3a6d67 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.cpp @@ -0,0 +1,924 @@ +//===-- SymbolFileBreakpad.cpp --------------------------------------------===// +// +// 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/Breakpad/SymbolFileBreakpad.h" +#include "Plugins/ObjectFile/Breakpad/BreakpadRecords.h" +#include "Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Section.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolVendor.h" +#include "lldb/Symbol/TypeMap.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/StreamString.h" +#include "llvm/ADT/StringExtras.h" +#include <optional> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::breakpad; + +LLDB_PLUGIN_DEFINE(SymbolFileBreakpad) + +char SymbolFileBreakpad::ID; + +class SymbolFileBreakpad::LineIterator { +public: + // begin iterator for sections of given type + LineIterator(ObjectFile &obj, Record::Kind section_type) + : m_obj(&obj), m_section_type(toString(section_type)), + m_next_section_idx(0), m_next_line(llvm::StringRef::npos) { + ++*this; + } + + // An iterator starting at the position given by the bookmark. + LineIterator(ObjectFile &obj, Record::Kind section_type, Bookmark bookmark); + + // end iterator + explicit LineIterator(ObjectFile &obj) + : m_obj(&obj), + m_next_section_idx(m_obj->GetSectionList()->GetNumSections(0)), + m_current_line(llvm::StringRef::npos), + m_next_line(llvm::StringRef::npos) {} + + friend bool operator!=(const LineIterator &lhs, const LineIterator &rhs) { + assert(lhs.m_obj == rhs.m_obj); + if (lhs.m_next_section_idx != rhs.m_next_section_idx) + return true; + if (lhs.m_current_line != rhs.m_current_line) + return true; + assert(lhs.m_next_line == rhs.m_next_line); + return false; + } + + const LineIterator &operator++(); + llvm::StringRef operator*() const { + return m_section_text.slice(m_current_line, m_next_line); + } + + Bookmark GetBookmark() const { + return Bookmark{m_next_section_idx, m_current_line}; + } + +private: + ObjectFile *m_obj; + ConstString m_section_type; + uint32_t m_next_section_idx; + llvm::StringRef m_section_text; + size_t m_current_line; + size_t m_next_line; + + void FindNextLine() { + m_next_line = m_section_text.find('\n', m_current_line); + if (m_next_line != llvm::StringRef::npos) { + ++m_next_line; + if (m_next_line >= m_section_text.size()) + m_next_line = llvm::StringRef::npos; + } + } +}; + +SymbolFileBreakpad::LineIterator::LineIterator(ObjectFile &obj, + Record::Kind section_type, + Bookmark bookmark) + : m_obj(&obj), m_section_type(toString(section_type)), + m_next_section_idx(bookmark.section), m_current_line(bookmark.offset) { + Section § = + *obj.GetSectionList()->GetSectionAtIndex(m_next_section_idx - 1); + assert(sect.GetName() == m_section_type); + + DataExtractor data; + obj.ReadSectionData(§, data); + m_section_text = toStringRef(data.GetData()); + + assert(m_current_line < m_section_text.size()); + FindNextLine(); +} + +const SymbolFileBreakpad::LineIterator & +SymbolFileBreakpad::LineIterator::operator++() { + const SectionList &list = *m_obj->GetSectionList(); + size_t num_sections = list.GetNumSections(0); + while (m_next_line != llvm::StringRef::npos || + m_next_section_idx < num_sections) { + if (m_next_line != llvm::StringRef::npos) { + m_current_line = m_next_line; + FindNextLine(); + return *this; + } + + Section § = *list.GetSectionAtIndex(m_next_section_idx++); + if (sect.GetName() != m_section_type) + continue; + DataExtractor data; + m_obj->ReadSectionData(§, data); + m_section_text = toStringRef(data.GetData()); + m_next_line = 0; + } + // We've reached the end. + m_current_line = m_next_line; + return *this; +} + +llvm::iterator_range<SymbolFileBreakpad::LineIterator> +SymbolFileBreakpad::lines(Record::Kind section_type) { + return llvm::make_range(LineIterator(*m_objfile_sp, section_type), + LineIterator(*m_objfile_sp)); +} + +namespace { +// A helper class for constructing the list of support files for a given compile +// unit. +class SupportFileMap { +public: + // Given a breakpad file ID, return a file ID to be used in the support files + // for this compile unit. + size_t operator[](size_t file) { + return m_map.try_emplace(file, m_map.size() + 1).first->second; + } + + // Construct a FileSpecList containing only the support files relevant for + // this compile unit (in the correct order). + FileSpecList translate(const FileSpec &cu_spec, + llvm::ArrayRef<FileSpec> all_files); + +private: + llvm::DenseMap<size_t, size_t> m_map; +}; +} // namespace + +FileSpecList SupportFileMap::translate(const FileSpec &cu_spec, + llvm::ArrayRef<FileSpec> all_files) { + std::vector<FileSpec> result; + result.resize(m_map.size() + 1); + result[0] = cu_spec; + for (const auto &KV : m_map) { + if (KV.first < all_files.size()) + result[KV.second] = all_files[KV.first]; + } + return FileSpecList(std::move(result)); +} + +void SymbolFileBreakpad::Initialize() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), CreateInstance, + DebuggerInitialize); +} + +void SymbolFileBreakpad::Terminate() { + PluginManager::UnregisterPlugin(CreateInstance); +} + +uint32_t SymbolFileBreakpad::CalculateAbilities() { + if (!m_objfile_sp || !llvm::isa<ObjectFileBreakpad>(*m_objfile_sp)) + return 0; + + return CompileUnits | Functions | LineTables; +} + +uint32_t SymbolFileBreakpad::CalculateNumCompileUnits() { + ParseCUData(); + return m_cu_data->GetSize(); +} + +CompUnitSP SymbolFileBreakpad::ParseCompileUnitAtIndex(uint32_t index) { + if (index >= m_cu_data->GetSize()) + return nullptr; + + CompUnitData &data = m_cu_data->GetEntryRef(index).data; + + ParseFileRecords(); + + FileSpec spec; + + // The FileSpec of the compile unit will be the file corresponding to the + // first LINE record. + LineIterator It(*m_objfile_sp, Record::Func, data.bookmark), + End(*m_objfile_sp); + assert(Record::classify(*It) == Record::Func); + ++It; // Skip FUNC record. + // Skip INLINE records. + while (It != End && Record::classify(*It) == Record::Inline) + ++It; + + if (It != End) { + auto record = LineRecord::parse(*It); + if (record && record->FileNum < m_files->size()) + spec = (*m_files)[record->FileNum]; + } + + auto cu_sp = std::make_shared<CompileUnit>( + m_objfile_sp->GetModule(), + /*user_data*/ nullptr, std::make_shared<SupportFile>(spec), index, + eLanguageTypeUnknown, + /*is_optimized*/ eLazyBoolNo); + + SetCompileUnitAtIndex(index, cu_sp); + return cu_sp; +} + +FunctionSP SymbolFileBreakpad::GetOrCreateFunction(CompileUnit &comp_unit) { + user_id_t id = comp_unit.GetID(); + if (FunctionSP func_sp = comp_unit.FindFunctionByUID(id)) + return func_sp; + + Log *log = GetLog(LLDBLog::Symbols); + FunctionSP func_sp; + addr_t base = GetBaseFileAddress(); + if (base == LLDB_INVALID_ADDRESS) { + LLDB_LOG(log, "Unable to fetch the base address of object file. Skipping " + "symtab population."); + return func_sp; + } + + const SectionList *list = comp_unit.GetModule()->GetSectionList(); + CompUnitData &data = m_cu_data->GetEntryRef(id).data; + LineIterator It(*m_objfile_sp, Record::Func, data.bookmark); + assert(Record::classify(*It) == Record::Func); + + if (auto record = FuncRecord::parse(*It)) { + Mangled func_name; + func_name.SetValue(ConstString(record->Name)); + addr_t address = record->Address + base; + SectionSP section_sp = list->FindSectionContainingFileAddress(address); + if (section_sp) { + AddressRange func_range( + section_sp, address - section_sp->GetFileAddress(), record->Size); + // Use the CU's id because every CU has only one function inside. + func_sp = std::make_shared<Function>(&comp_unit, id, 0, func_name, + nullptr, func_range); + comp_unit.AddFunction(func_sp); + } + } + return func_sp; +} + +size_t SymbolFileBreakpad::ParseFunctions(CompileUnit &comp_unit) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + return GetOrCreateFunction(comp_unit) ? 1 : 0; +} + +bool SymbolFileBreakpad::ParseLineTable(CompileUnit &comp_unit) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + CompUnitData &data = m_cu_data->GetEntryRef(comp_unit.GetID()).data; + + if (!data.line_table_up) + ParseLineTableAndSupportFiles(comp_unit, data); + + comp_unit.SetLineTable(data.line_table_up.release()); + return true; +} + +bool SymbolFileBreakpad::ParseSupportFiles(CompileUnit &comp_unit, + SupportFileList &support_files) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + CompUnitData &data = m_cu_data->GetEntryRef(comp_unit.GetID()).data; + if (!data.support_files) + ParseLineTableAndSupportFiles(comp_unit, data); + + for (auto &fs : *data.support_files) + support_files.Append(fs); + return true; +} + +size_t SymbolFileBreakpad::ParseBlocksRecursive(Function &func) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + CompileUnit *comp_unit = func.GetCompileUnit(); + lldbassert(comp_unit); + ParseInlineOriginRecords(); + // A vector of current each level's parent block. For example, when parsing + // "INLINE 0 ...", the current level is 0 and its parent block is the + // function block at index 0. + std::vector<Block *> blocks; + Block &block = func.GetBlock(false); + block.AddRange(Block::Range(0, func.GetAddressRange().GetByteSize())); + blocks.push_back(&block); + + size_t blocks_added = 0; + addr_t func_base = func.GetAddressRange().GetBaseAddress().GetOffset(); + CompUnitData &data = m_cu_data->GetEntryRef(comp_unit->GetID()).data; + LineIterator It(*m_objfile_sp, Record::Func, data.bookmark), + End(*m_objfile_sp); + ++It; // Skip the FUNC record. + size_t last_added_nest_level = 0; + while (It != End && Record::classify(*It) == Record::Inline) { + if (auto record = InlineRecord::parse(*It)) { + if (record->InlineNestLevel == 0 || + record->InlineNestLevel <= last_added_nest_level + 1) { + last_added_nest_level = record->InlineNestLevel; + BlockSP block_sp = std::make_shared<Block>(It.GetBookmark().offset); + FileSpec callsite_file; + if (record->CallSiteFileNum < m_files->size()) + callsite_file = (*m_files)[record->CallSiteFileNum]; + llvm::StringRef name; + if (record->OriginNum < m_inline_origins->size()) + name = (*m_inline_origins)[record->OriginNum]; + + Declaration callsite(callsite_file, record->CallSiteLineNum); + block_sp->SetInlinedFunctionInfo(name.str().c_str(), + /*mangled=*/nullptr, + /*decl_ptr=*/nullptr, &callsite); + for (const auto &range : record->Ranges) { + block_sp->AddRange( + Block::Range(range.first - func_base, range.second)); + } + block_sp->FinalizeRanges(); + + blocks[record->InlineNestLevel]->AddChild(block_sp); + if (record->InlineNestLevel + 1 >= blocks.size()) { + blocks.resize(blocks.size() + 1); + } + blocks[record->InlineNestLevel + 1] = block_sp.get(); + ++blocks_added; + } + } + ++It; + } + return blocks_added; +} + +void SymbolFileBreakpad::ParseInlineOriginRecords() { + if (m_inline_origins) + return; + m_inline_origins.emplace(); + + Log *log = GetLog(LLDBLog::Symbols); + for (llvm::StringRef line : lines(Record::InlineOrigin)) { + auto record = InlineOriginRecord::parse(line); + if (!record) { + LLDB_LOG(log, "Failed to parse: {0}. Skipping record.", line); + continue; + } + + if (record->Number >= m_inline_origins->size()) + m_inline_origins->resize(record->Number + 1); + (*m_inline_origins)[record->Number] = record->Name; + } +} + +uint32_t +SymbolFileBreakpad::ResolveSymbolContext(const Address &so_addr, + SymbolContextItem resolve_scope, + SymbolContext &sc) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + if (!(resolve_scope & (eSymbolContextCompUnit | eSymbolContextLineEntry | + eSymbolContextFunction | eSymbolContextBlock))) + return 0; + + ParseCUData(); + uint32_t idx = + m_cu_data->FindEntryIndexThatContains(so_addr.GetFileAddress()); + if (idx == UINT32_MAX) + return 0; + + sc.comp_unit = GetCompileUnitAtIndex(idx).get(); + SymbolContextItem result = eSymbolContextCompUnit; + if (resolve_scope & eSymbolContextLineEntry) { + if (sc.comp_unit->GetLineTable()->FindLineEntryByAddress(so_addr, + sc.line_entry)) { + result |= eSymbolContextLineEntry; + } + } + + if (resolve_scope & (eSymbolContextFunction | eSymbolContextBlock)) { + FunctionSP func_sp = GetOrCreateFunction(*sc.comp_unit); + if (func_sp) { + sc.function = func_sp.get(); + result |= eSymbolContextFunction; + if (resolve_scope & eSymbolContextBlock) { + Block &block = func_sp->GetBlock(true); + sc.block = block.FindInnermostBlockByOffset( + so_addr.GetFileAddress() - + sc.function->GetAddressRange().GetBaseAddress().GetFileAddress()); + if (sc.block) + result |= eSymbolContextBlock; + } + } + } + + return result; +} + +uint32_t SymbolFileBreakpad::ResolveSymbolContext( + const SourceLocationSpec &src_location_spec, + lldb::SymbolContextItem resolve_scope, SymbolContextList &sc_list) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + if (!(resolve_scope & eSymbolContextCompUnit)) + return 0; + + uint32_t old_size = sc_list.GetSize(); + for (size_t i = 0, size = GetNumCompileUnits(); i < size; ++i) { + CompileUnit &cu = *GetCompileUnitAtIndex(i); + cu.ResolveSymbolContext(src_location_spec, resolve_scope, sc_list); + } + return sc_list.GetSize() - old_size; +} + +void SymbolFileBreakpad::FindFunctions( + const Module::LookupInfo &lookup_info, + const CompilerDeclContext &parent_decl_ctx, bool include_inlines, + SymbolContextList &sc_list) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + // TODO: Implement this with supported FunctionNameType. + + ConstString name = lookup_info.GetLookupName(); + for (uint32_t i = 0; i < GetNumCompileUnits(); ++i) { + CompUnitSP cu_sp = GetCompileUnitAtIndex(i); + FunctionSP func_sp = GetOrCreateFunction(*cu_sp); + if (func_sp && name == func_sp->GetNameNoArguments()) { + SymbolContext sc; + sc.comp_unit = cu_sp.get(); + sc.function = func_sp.get(); + sc.module_sp = func_sp->CalculateSymbolContextModule(); + sc_list.Append(sc); + } + } +} + +void SymbolFileBreakpad::FindFunctions(const RegularExpression ®ex, + bool include_inlines, + SymbolContextList &sc_list) { + // TODO +} + +void SymbolFileBreakpad::AddSymbols(Symtab &symtab) { + Log *log = GetLog(LLDBLog::Symbols); + Module &module = *m_objfile_sp->GetModule(); + addr_t base = GetBaseFileAddress(); + if (base == LLDB_INVALID_ADDRESS) { + LLDB_LOG(log, "Unable to fetch the base address of object file. Skipping " + "symtab population."); + return; + } + + const SectionList &list = *module.GetSectionList(); + llvm::DenseSet<addr_t> found_symbol_addresses; + std::vector<Symbol> symbols; + auto add_symbol = [&](addr_t address, std::optional<addr_t> size, + llvm::StringRef name) { + address += base; + SectionSP section_sp = list.FindSectionContainingFileAddress(address); + if (!section_sp) { + LLDB_LOG(log, + "Ignoring symbol {0}, whose address ({1}) is outside of the " + "object file. Mismatched symbol file?", + name, address); + return; + } + // Keep track of what addresses were already added so far and only add + // the symbol with the first address. + if (!found_symbol_addresses.insert(address).second) + return; + symbols.emplace_back( + /*symID*/ 0, Mangled(name), eSymbolTypeCode, + /*is_global*/ true, /*is_debug*/ false, + /*is_trampoline*/ false, /*is_artificial*/ false, + AddressRange(section_sp, address - section_sp->GetFileAddress(), + size.value_or(0)), + size.has_value(), /*contains_linker_annotations*/ false, /*flags*/ 0); + }; + + for (llvm::StringRef line : lines(Record::Public)) { + if (auto record = PublicRecord::parse(line)) + add_symbol(record->Address, std::nullopt, record->Name); + else + LLDB_LOG(log, "Failed to parse: {0}. Skipping record.", line); + } + + for (Symbol &symbol : symbols) + symtab.AddSymbol(std::move(symbol)); + symtab.Finalize(); +} + +llvm::Expected<lldb::addr_t> +SymbolFileBreakpad::GetParameterStackSize(Symbol &symbol) { + ParseUnwindData(); + if (auto *entry = m_unwind_data->win.FindEntryThatContains( + symbol.GetAddress().GetFileAddress())) { + auto record = StackWinRecord::parse( + *LineIterator(*m_objfile_sp, Record::StackWin, entry->data)); + assert(record); + return record->ParameterSize; + } + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "Parameter size unknown."); +} + +static std::optional<std::pair<llvm::StringRef, llvm::StringRef>> +GetRule(llvm::StringRef &unwind_rules) { + // Unwind rules are of the form + // register1: expression1 register2: expression2 ... + // We assume none of the tokens in expression<n> end with a colon. + + llvm::StringRef lhs, rest; + std::tie(lhs, rest) = getToken(unwind_rules); + if (!lhs.consume_back(":")) + return std::nullopt; + + // Seek forward to the next register: expression pair + llvm::StringRef::size_type pos = rest.find(": "); + if (pos == llvm::StringRef::npos) { + // No pair found, this means the rest of the string is a single expression. + unwind_rules = llvm::StringRef(); + return std::make_pair(lhs, rest); + } + + // Go back one token to find the end of the current rule. + pos = rest.rfind(' ', pos); + if (pos == llvm::StringRef::npos) + return std::nullopt; + + llvm::StringRef rhs = rest.take_front(pos); + unwind_rules = rest.drop_front(pos); + return std::make_pair(lhs, rhs); +} + +static const RegisterInfo * +ResolveRegister(const llvm::Triple &triple, + const SymbolFile::RegisterInfoResolver &resolver, + llvm::StringRef name) { + if (triple.isX86() || triple.isMIPS()) { + // X86 and MIPS registers have '$' in front of their register names. Arm and + // AArch64 don't. + if (!name.consume_front("$")) + return nullptr; + } + return resolver.ResolveName(name); +} + +static const RegisterInfo * +ResolveRegisterOrRA(const llvm::Triple &triple, + const SymbolFile::RegisterInfoResolver &resolver, + llvm::StringRef name) { + if (name == ".ra") + return resolver.ResolveNumber(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); + return ResolveRegister(triple, resolver, name); +} + +llvm::ArrayRef<uint8_t> SymbolFileBreakpad::SaveAsDWARF(postfix::Node &node) { + ArchSpec arch = m_objfile_sp->GetArchitecture(); + StreamString dwarf(Stream::eBinary, arch.GetAddressByteSize(), + arch.GetByteOrder()); + ToDWARF(node, dwarf); + uint8_t *saved = m_allocator.Allocate<uint8_t>(dwarf.GetSize()); + std::memcpy(saved, dwarf.GetData(), dwarf.GetSize()); + return {saved, dwarf.GetSize()}; +} + +bool SymbolFileBreakpad::ParseCFIUnwindRow(llvm::StringRef unwind_rules, + const RegisterInfoResolver &resolver, + UnwindPlan::Row &row) { + Log *log = GetLog(LLDBLog::Symbols); + + llvm::BumpPtrAllocator node_alloc; + llvm::Triple triple = m_objfile_sp->GetArchitecture().GetTriple(); + while (auto rule = GetRule(unwind_rules)) { + node_alloc.Reset(); + llvm::StringRef lhs = rule->first; + postfix::Node *rhs = postfix::ParseOneExpression(rule->second, node_alloc); + if (!rhs) { + LLDB_LOG(log, "Could not parse `{0}` as unwind rhs.", rule->second); + return false; + } + + bool success = postfix::ResolveSymbols( + rhs, [&](postfix::SymbolNode &symbol) -> postfix::Node * { + llvm::StringRef name = symbol.GetName(); + if (name == ".cfa" && lhs != ".cfa") + return postfix::MakeNode<postfix::InitialValueNode>(node_alloc); + + if (const RegisterInfo *info = + ResolveRegister(triple, resolver, name)) { + return postfix::MakeNode<postfix::RegisterNode>( + node_alloc, info->kinds[eRegisterKindLLDB]); + } + return nullptr; + }); + + if (!success) { + LLDB_LOG(log, "Resolving symbols in `{0}` failed.", rule->second); + return false; + } + + llvm::ArrayRef<uint8_t> saved = SaveAsDWARF(*rhs); + if (lhs == ".cfa") { + row.GetCFAValue().SetIsDWARFExpression(saved.data(), saved.size()); + } else if (const RegisterInfo *info = + ResolveRegisterOrRA(triple, resolver, lhs)) { + UnwindPlan::Row::RegisterLocation loc; + loc.SetIsDWARFExpression(saved.data(), saved.size()); + row.SetRegisterInfo(info->kinds[eRegisterKindLLDB], loc); + } else + LLDB_LOG(log, "Invalid register `{0}` in unwind rule.", lhs); + } + if (unwind_rules.empty()) + return true; + + LLDB_LOG(log, "Could not parse `{0}` as an unwind rule.", unwind_rules); + return false; +} + +UnwindPlanSP +SymbolFileBreakpad::GetUnwindPlan(const Address &address, + const RegisterInfoResolver &resolver) { + ParseUnwindData(); + if (auto *entry = + m_unwind_data->cfi.FindEntryThatContains(address.GetFileAddress())) + return ParseCFIUnwindPlan(entry->data, resolver); + if (auto *entry = + m_unwind_data->win.FindEntryThatContains(address.GetFileAddress())) + return ParseWinUnwindPlan(entry->data, resolver); + return nullptr; +} + +UnwindPlanSP +SymbolFileBreakpad::ParseCFIUnwindPlan(const Bookmark &bookmark, + const RegisterInfoResolver &resolver) { + addr_t base = GetBaseFileAddress(); + if (base == LLDB_INVALID_ADDRESS) + return nullptr; + + LineIterator It(*m_objfile_sp, Record::StackCFI, bookmark), + End(*m_objfile_sp); + std::optional<StackCFIRecord> init_record = StackCFIRecord::parse(*It); + assert(init_record && init_record->Size && + "Record already parsed successfully in ParseUnwindData!"); + + auto plan_sp = std::make_shared<UnwindPlan>(lldb::eRegisterKindLLDB); + plan_sp->SetSourceName("breakpad STACK CFI"); + plan_sp->SetUnwindPlanValidAtAllInstructions(eLazyBoolNo); + plan_sp->SetUnwindPlanForSignalTrap(eLazyBoolNo); + plan_sp->SetSourcedFromCompiler(eLazyBoolYes); + plan_sp->SetPlanValidAddressRange( + AddressRange(base + init_record->Address, *init_record->Size, + m_objfile_sp->GetModule()->GetSectionList())); + + auto row_sp = std::make_shared<UnwindPlan::Row>(); + row_sp->SetOffset(0); + if (!ParseCFIUnwindRow(init_record->UnwindRules, resolver, *row_sp)) + return nullptr; + plan_sp->AppendRow(row_sp); + for (++It; It != End; ++It) { + std::optional<StackCFIRecord> record = StackCFIRecord::parse(*It); + if (!record) + return nullptr; + if (record->Size) + break; + + row_sp = std::make_shared<UnwindPlan::Row>(*row_sp); + row_sp->SetOffset(record->Address - init_record->Address); + if (!ParseCFIUnwindRow(record->UnwindRules, resolver, *row_sp)) + return nullptr; + plan_sp->AppendRow(row_sp); + } + return plan_sp; +} + +UnwindPlanSP +SymbolFileBreakpad::ParseWinUnwindPlan(const Bookmark &bookmark, + const RegisterInfoResolver &resolver) { + Log *log = GetLog(LLDBLog::Symbols); + addr_t base = GetBaseFileAddress(); + if (base == LLDB_INVALID_ADDRESS) + return nullptr; + + LineIterator It(*m_objfile_sp, Record::StackWin, bookmark); + std::optional<StackWinRecord> record = StackWinRecord::parse(*It); + assert(record && "Record already parsed successfully in ParseUnwindData!"); + + auto plan_sp = std::make_shared<UnwindPlan>(lldb::eRegisterKindLLDB); + plan_sp->SetSourceName("breakpad STACK WIN"); + plan_sp->SetUnwindPlanValidAtAllInstructions(eLazyBoolNo); + plan_sp->SetUnwindPlanForSignalTrap(eLazyBoolNo); + plan_sp->SetSourcedFromCompiler(eLazyBoolYes); + plan_sp->SetPlanValidAddressRange( + AddressRange(base + record->RVA, record->CodeSize, + m_objfile_sp->GetModule()->GetSectionList())); + + auto row_sp = std::make_shared<UnwindPlan::Row>(); + row_sp->SetOffset(0); + + llvm::BumpPtrAllocator node_alloc; + std::vector<std::pair<llvm::StringRef, postfix::Node *>> program = + postfix::ParseFPOProgram(record->ProgramString, node_alloc); + + if (program.empty()) { + LLDB_LOG(log, "Invalid unwind rule: {0}.", record->ProgramString); + return nullptr; + } + auto it = program.begin(); + llvm::Triple triple = m_objfile_sp->GetArchitecture().GetTriple(); + const auto &symbol_resolver = + [&](postfix::SymbolNode &symbol) -> postfix::Node * { + llvm::StringRef name = symbol.GetName(); + for (const auto &rule : llvm::make_range(program.begin(), it)) { + if (rule.first == name) + return rule.second; + } + if (const RegisterInfo *info = ResolveRegister(triple, resolver, name)) + return postfix::MakeNode<postfix::RegisterNode>( + node_alloc, info->kinds[eRegisterKindLLDB]); + return nullptr; + }; + + // We assume the first value will be the CFA. It is usually called T0, but + // clang will use T1, if it needs to realign the stack. + auto *symbol = llvm::dyn_cast<postfix::SymbolNode>(it->second); + if (symbol && symbol->GetName() == ".raSearch") { + row_sp->GetCFAValue().SetRaSearch(record->LocalSize + + record->SavedRegisterSize); + } else { + if (!postfix::ResolveSymbols(it->second, symbol_resolver)) { + LLDB_LOG(log, "Resolving symbols in `{0}` failed.", + record->ProgramString); + return nullptr; + } + llvm::ArrayRef<uint8_t> saved = SaveAsDWARF(*it->second); + row_sp->GetCFAValue().SetIsDWARFExpression(saved.data(), saved.size()); + } + + // Replace the node value with InitialValueNode, so that subsequent + // expressions refer to the CFA value instead of recomputing the whole + // expression. + it->second = postfix::MakeNode<postfix::InitialValueNode>(node_alloc); + + + // Now process the rest of the assignments. + for (++it; it != program.end(); ++it) { + const RegisterInfo *info = ResolveRegister(triple, resolver, it->first); + // It is not an error if the resolution fails because the program may + // contain temporary variables. + if (!info) + continue; + if (!postfix::ResolveSymbols(it->second, symbol_resolver)) { + LLDB_LOG(log, "Resolving symbols in `{0}` failed.", + record->ProgramString); + return nullptr; + } + + llvm::ArrayRef<uint8_t> saved = SaveAsDWARF(*it->second); + UnwindPlan::Row::RegisterLocation loc; + loc.SetIsDWARFExpression(saved.data(), saved.size()); + row_sp->SetRegisterInfo(info->kinds[eRegisterKindLLDB], loc); + } + + plan_sp->AppendRow(row_sp); + return plan_sp; +} + +addr_t SymbolFileBreakpad::GetBaseFileAddress() { + return m_objfile_sp->GetModule() + ->GetObjectFile() + ->GetBaseAddress() + .GetFileAddress(); +} + +// Parse out all the FILE records from the breakpad file. These will be needed +// when constructing the support file lists for individual compile units. +void SymbolFileBreakpad::ParseFileRecords() { + if (m_files) + return; + m_files.emplace(); + + Log *log = GetLog(LLDBLog::Symbols); + for (llvm::StringRef line : lines(Record::File)) { + auto record = FileRecord::parse(line); + if (!record) { + LLDB_LOG(log, "Failed to parse: {0}. Skipping record.", line); + continue; + } + + if (record->Number >= m_files->size()) + m_files->resize(record->Number + 1); + FileSpec::Style style = FileSpec::GuessPathStyle(record->Name) + .value_or(FileSpec::Style::native); + (*m_files)[record->Number] = FileSpec(record->Name, style); + } +} + +void SymbolFileBreakpad::ParseCUData() { + if (m_cu_data) + return; + + m_cu_data.emplace(); + Log *log = GetLog(LLDBLog::Symbols); + addr_t base = GetBaseFileAddress(); + if (base == LLDB_INVALID_ADDRESS) { + LLDB_LOG(log, "SymbolFile parsing failed: Unable to fetch the base address " + "of object file."); + } + + // We shall create one compile unit for each FUNC record. So, count the number + // of FUNC records, and store them in m_cu_data, together with their ranges. + for (LineIterator It(*m_objfile_sp, Record::Func), End(*m_objfile_sp); + It != End; ++It) { + if (auto record = FuncRecord::parse(*It)) { + m_cu_data->Append(CompUnitMap::Entry(base + record->Address, record->Size, + CompUnitData(It.GetBookmark()))); + } else + LLDB_LOG(log, "Failed to parse: {0}. Skipping record.", *It); + } + m_cu_data->Sort(); +} + +// Construct the list of support files and line table entries for the given +// compile unit. +void SymbolFileBreakpad::ParseLineTableAndSupportFiles(CompileUnit &cu, + CompUnitData &data) { + addr_t base = GetBaseFileAddress(); + assert(base != LLDB_INVALID_ADDRESS && + "How did we create compile units without a base address?"); + + SupportFileMap map; + std::vector<std::unique_ptr<LineSequence>> sequences; + std::unique_ptr<LineSequence> line_seq_up = + LineTable::CreateLineSequenceContainer(); + std::optional<addr_t> next_addr; + auto finish_sequence = [&]() { + LineTable::AppendLineEntryToSequence( + line_seq_up.get(), *next_addr, /*line=*/0, /*column=*/0, + /*file_idx=*/0, /*is_start_of_statement=*/false, + /*is_start_of_basic_block=*/false, /*is_prologue_end=*/false, + /*is_epilogue_begin=*/false, /*is_terminal_entry=*/true); + sequences.push_back(std::move(line_seq_up)); + line_seq_up = LineTable::CreateLineSequenceContainer(); + }; + + LineIterator It(*m_objfile_sp, Record::Func, data.bookmark), + End(*m_objfile_sp); + assert(Record::classify(*It) == Record::Func); + for (++It; It != End; ++It) { + // Skip INLINE records + if (Record::classify(*It) == Record::Inline) + continue; + + auto record = LineRecord::parse(*It); + if (!record) + break; + + record->Address += base; + + if (next_addr && *next_addr != record->Address) { + // Discontiguous entries. Finish off the previous sequence and reset. + finish_sequence(); + } + LineTable::AppendLineEntryToSequence( + line_seq_up.get(), record->Address, record->LineNum, /*column=*/0, + map[record->FileNum], /*is_start_of_statement=*/true, + /*is_start_of_basic_block=*/false, /*is_prologue_end=*/false, + /*is_epilogue_begin=*/false, /*is_terminal_entry=*/false); + next_addr = record->Address + record->Size; + } + if (next_addr) + finish_sequence(); + data.line_table_up = std::make_unique<LineTable>(&cu, std::move(sequences)); + data.support_files = map.translate(cu.GetPrimaryFile(), *m_files); +} + +void SymbolFileBreakpad::ParseUnwindData() { + if (m_unwind_data) + return; + m_unwind_data.emplace(); + + Log *log = GetLog(LLDBLog::Symbols); + addr_t base = GetBaseFileAddress(); + if (base == LLDB_INVALID_ADDRESS) { + LLDB_LOG(log, "SymbolFile parsing failed: Unable to fetch the base address " + "of object file."); + } + + for (LineIterator It(*m_objfile_sp, Record::StackCFI), End(*m_objfile_sp); + It != End; ++It) { + if (auto record = StackCFIRecord::parse(*It)) { + if (record->Size) + m_unwind_data->cfi.Append(UnwindMap::Entry( + base + record->Address, *record->Size, It.GetBookmark())); + } else + LLDB_LOG(log, "Failed to parse: {0}. Skipping record.", *It); + } + m_unwind_data->cfi.Sort(); + + for (LineIterator It(*m_objfile_sp, Record::StackWin), End(*m_objfile_sp); + It != End; ++It) { + if (auto record = StackWinRecord::parse(*It)) { + m_unwind_data->win.Append(UnwindMap::Entry( + base + record->RVA, record->CodeSize, It.GetBookmark())); + } else + LLDB_LOG(log, "Failed to parse: {0}. Skipping record.", *It); + } + m_unwind_data->win.Sort(); +} + +uint64_t SymbolFileBreakpad::GetDebugInfoSize(bool load_all_debug_info) { + // Breakpad files are all debug info. + return m_objfile_sp->GetByteSize(); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.h new file mode 100644 index 000000000000..041b388f9f34 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.h @@ -0,0 +1,235 @@ +//===-- SymbolFileBreakpad.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_SOURCE_PLUGINS_SYMBOLFILE_BREAKPAD_SYMBOLFILEBREAKPAD_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_BREAKPAD_SYMBOLFILEBREAKPAD_H + +#include "Plugins/ObjectFile/Breakpad/BreakpadRecords.h" +#include "lldb/Symbol/LineTable.h" +#include "lldb/Symbol/PostfixExpression.h" +#include "lldb/Symbol/SymbolFile.h" +#include "lldb/Symbol/UnwindPlan.h" +#include "lldb/Utility/FileSpecList.h" +#include <optional> + +namespace lldb_private { + +namespace breakpad { + +class SymbolFileBreakpad : public SymbolFileCommon { + /// LLVM RTTI support. + static char ID; + +public: + /// LLVM RTTI support. + /// \{ + bool isA(const void *ClassID) const override { + return ClassID == &ID || SymbolFileCommon::isA(ClassID); + } + static bool classof(const SymbolFile *obj) { return obj->isA(&ID); } + /// \} + + // Static Functions + static void Initialize(); + static void Terminate(); + static void DebuggerInitialize(Debugger &debugger) {} + static llvm::StringRef GetPluginNameStatic() { return "breakpad"; } + + static llvm::StringRef GetPluginDescriptionStatic() { + return "Breakpad debug symbol file reader."; + } + + static SymbolFile *CreateInstance(lldb::ObjectFileSP objfile_sp) { + return new SymbolFileBreakpad(std::move(objfile_sp)); + } + + // Constructors and Destructors + SymbolFileBreakpad(lldb::ObjectFileSP objfile_sp) + : SymbolFileCommon(std::move(objfile_sp)) {} + + ~SymbolFileBreakpad() override = default; + + uint32_t CalculateAbilities() override; + + void InitializeObject() override {} + + // Compile Unit function calls + + lldb::LanguageType ParseLanguage(CompileUnit &comp_unit) override { + return lldb::eLanguageTypeUnknown; + } + + lldb::FunctionSP GetOrCreateFunction(CompileUnit &comp_unit); + + size_t ParseFunctions(CompileUnit &comp_unit) override; + + bool ParseLineTable(CompileUnit &comp_unit) override; + + bool ParseDebugMacros(CompileUnit &comp_unit) override { return false; } + + bool ParseSupportFiles(CompileUnit &comp_unit, + SupportFileList &support_files) override; + size_t ParseTypes(CompileUnit &cu) override { return 0; } + + bool ParseImportedModules( + const SymbolContext &sc, + std::vector<lldb_private::SourceModule> &imported_modules) override { + return false; + } + + size_t ParseBlocksRecursive(Function &func) override; + + void FindGlobalVariables(ConstString name, + const CompilerDeclContext &parent_decl_ctx, + uint32_t max_matches, + VariableList &variables) override {} + + size_t ParseVariablesForContext(const SymbolContext &sc) override { + return 0; + } + Type *ResolveTypeUID(lldb::user_id_t type_uid) override { return nullptr; } + std::optional<ArrayInfo> GetDynamicArrayInfoForUID( + lldb::user_id_t type_uid, + const lldb_private::ExecutionContext *exe_ctx) override { + return std::nullopt; + } + + bool CompleteType(CompilerType &compiler_type) override { return false; } + uint32_t ResolveSymbolContext(const Address &so_addr, + lldb::SymbolContextItem resolve_scope, + SymbolContext &sc) override; + + uint32_t ResolveSymbolContext(const SourceLocationSpec &src_location_spec, + lldb::SymbolContextItem resolve_scope, + SymbolContextList &sc_list) override; + + void GetTypes(SymbolContextScope *sc_scope, lldb::TypeClass type_mask, + TypeList &type_list) override {} + + void FindFunctions(const Module::LookupInfo &lookup_info, + const CompilerDeclContext &parent_decl_ctx, + bool include_inlines, SymbolContextList &sc_list) override; + + void FindFunctions(const RegularExpression ®ex, bool include_inlines, + SymbolContextList &sc_list) override; + + llvm::Expected<lldb::TypeSystemSP> + GetTypeSystemForLanguage(lldb::LanguageType language) override { + return llvm::createStringError( + "SymbolFileBreakpad does not support GetTypeSystemForLanguage"); + } + + CompilerDeclContext FindNamespace(ConstString name, + const CompilerDeclContext &parent_decl_ctx, + bool only_root_namespaces) override { + return CompilerDeclContext(); + } + + void AddSymbols(Symtab &symtab) override; + + llvm::Expected<lldb::addr_t> GetParameterStackSize(Symbol &symbol) override; + + lldb::UnwindPlanSP + GetUnwindPlan(const Address &address, + const RegisterInfoResolver &resolver) override; + + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } + + uint64_t GetDebugInfoSize(bool load_all_debug_info = false) override; + +private: + // A class representing a position in the breakpad file. Useful for + // remembering the position so we can go back to it later and parse more data. + // Can be converted to/from a LineIterator, but it has a much smaller memory + // footprint. + struct Bookmark { + uint32_t section; + size_t offset; + + friend bool operator<(const Bookmark &lhs, const Bookmark &rhs) { + return std::tie(lhs.section, lhs.offset) < + std::tie(rhs.section, rhs.offset); + } + }; + + // At iterator class for simplifying algorithms reading data from the breakpad + // file. It iterates over all records (lines) in the sections of a given type. + // It also supports saving a specific position (via the GetBookmark() method) + // and then resuming from it afterwards. + class LineIterator; + + // Return an iterator range for all records in the given object file of the + // given type. + llvm::iterator_range<LineIterator> lines(Record::Kind section_type); + + // Breakpad files do not contain sufficient information to correctly + // reconstruct compile units. The approach chosen here is to treat each + // function as a compile unit. The compile unit name is the name if the first + // line entry belonging to this function. + // This class is our internal representation of a compile unit. It stores the + // CompileUnit object and a bookmark pointing to the FUNC record of the + // compile unit function. It also lazily construct the list of support files + // and line table entries for the compile unit, when these are needed. + class CompUnitData { + public: + CompUnitData(Bookmark bookmark) : bookmark(bookmark) {} + + CompUnitData() = default; + CompUnitData(const CompUnitData &rhs) : bookmark(rhs.bookmark) {} + CompUnitData &operator=(const CompUnitData &rhs) { + bookmark = rhs.bookmark; + support_files.reset(); + line_table_up.reset(); + return *this; + } + friend bool operator<(const CompUnitData &lhs, const CompUnitData &rhs) { + return lhs.bookmark < rhs.bookmark; + } + + Bookmark bookmark; + std::optional<FileSpecList> support_files; + std::unique_ptr<LineTable> line_table_up; + }; + + uint32_t CalculateNumCompileUnits() override; + lldb::CompUnitSP ParseCompileUnitAtIndex(uint32_t index) override; + + lldb::addr_t GetBaseFileAddress(); + void ParseFileRecords(); + void ParseCUData(); + void ParseLineTableAndSupportFiles(CompileUnit &cu, CompUnitData &data); + void ParseUnwindData(); + llvm::ArrayRef<uint8_t> SaveAsDWARF(postfix::Node &node); + lldb::UnwindPlanSP ParseCFIUnwindPlan(const Bookmark &bookmark, + const RegisterInfoResolver &resolver); + bool ParseCFIUnwindRow(llvm::StringRef unwind_rules, + const RegisterInfoResolver &resolver, + UnwindPlan::Row &row); + lldb::UnwindPlanSP ParseWinUnwindPlan(const Bookmark &bookmark, + const RegisterInfoResolver &resolver); + void ParseInlineOriginRecords(); + + using CompUnitMap = RangeDataVector<lldb::addr_t, lldb::addr_t, CompUnitData>; + + std::optional<std::vector<FileSpec>> m_files; + std::optional<CompUnitMap> m_cu_data; + std::optional<std::vector<llvm::StringRef>> m_inline_origins; + + using UnwindMap = RangeDataVector<lldb::addr_t, lldb::addr_t, Bookmark>; + struct UnwindData { + UnwindMap cfi; + UnwindMap win; + }; + std::optional<UnwindData> m_unwind_data; + llvm::BumpPtrAllocator m_allocator; +}; + +} // namespace breakpad +} // namespace lldb_private + +#endif diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/CTF/CTFTypes.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/CTF/CTFTypes.h new file mode 100644 index 000000000000..c1016b2af0c6 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/CTF/CTFTypes.h @@ -0,0 +1,203 @@ +//===-- CTFTypes.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_SOURCE_PLUGINS_SYMBOLFILE_CTF_CTFTYPES_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_CTF_CTFTYPES_H + +#include "lldb/lldb-types.h" +#include "llvm/ADT/StringRef.h" + +namespace lldb_private { + +struct CTFType { + enum Kind : uint32_t { + eUnknown = 0, + eInteger = 1, + eFloat = 2, + ePointer = 3, + eArray = 4, + eFunction = 5, + eStruct = 6, + eUnion = 7, + eEnum = 8, + eForward = 9, + eTypedef = 10, + eVolatile = 11, + eConst = 12, + eRestrict = 13, + eSlice = 14, + }; + + Kind kind; + lldb::user_id_t uid; + llvm::StringRef name; + + CTFType(Kind kind, lldb::user_id_t uid, llvm::StringRef name) + : kind(kind), uid(uid), name(name) {} +}; + +struct CTFInteger : public CTFType { + CTFInteger(lldb::user_id_t uid, llvm::StringRef name, uint32_t bits, + uint32_t encoding) + : CTFType(eInteger, uid, name), bits(bits), encoding(encoding) {} + + static bool classof(const CTFType *T) { return T->kind == eInteger; } + + uint32_t bits; + uint32_t encoding; +}; + +struct CTFModifier : public CTFType { +protected: + CTFModifier(Kind kind, lldb::user_id_t uid, uint32_t type) + : CTFType(kind, uid, ""), type(type) {} + + static bool classof(const CTFType *T) { + return T->kind == ePointer || T->kind == eConst || T->kind == eVolatile || + T->kind == eRestrict; + } + +public: + uint32_t type; +}; + +struct CTFPointer : public CTFModifier { + CTFPointer(lldb::user_id_t uid, uint32_t type) + : CTFModifier(ePointer, uid, type) {} + + static bool classof(const CTFType *T) { return T->kind == ePointer; } +}; + +struct CTFConst : public CTFModifier { + CTFConst(lldb::user_id_t uid, uint32_t type) + : CTFModifier(eConst, uid, type) {} + + static bool classof(const CTFType *T) { return T->kind == eConst; } +}; + +struct CTFVolatile : public CTFModifier { + CTFVolatile(lldb::user_id_t uid, uint32_t type) + : CTFModifier(eVolatile, uid, type) {} + + static bool classof(const CTFType *T) { return T->kind == eVolatile; } +}; + +struct CTFRestrict : public CTFModifier { + CTFRestrict(lldb::user_id_t uid, uint32_t type) + : CTFModifier(eRestrict, uid, type) {} + static bool classof(const CTFType *T) { return T->kind == eRestrict; } +}; + +struct CTFTypedef : public CTFType { + CTFTypedef(lldb::user_id_t uid, llvm::StringRef name, uint32_t type) + : CTFType(eTypedef, uid, name), type(type) {} + + static bool classof(const CTFType *T) { return T->kind == eTypedef; } + + uint32_t type; +}; + +struct CTFArray : public CTFType { + CTFArray(lldb::user_id_t uid, llvm::StringRef name, uint32_t type, + uint32_t index, uint32_t nelems) + : CTFType(eArray, uid, name), type(type), index(index), nelems(nelems) {} + + static bool classof(const CTFType *T) { return T->kind == eArray; } + + uint32_t type; + uint32_t index; + uint32_t nelems; +}; + +struct CTFEnum : public CTFType { + struct Value { + Value(llvm::StringRef name, uint32_t value) : name(name), value(value){}; + llvm::StringRef name; + uint32_t value; + }; + + CTFEnum(lldb::user_id_t uid, llvm::StringRef name, uint32_t nelems, + uint32_t size, std::vector<Value> values) + : CTFType(eEnum, uid, name), nelems(nelems), size(size), + values(std::move(values)) { + assert(this->values.size() == nelems); + } + + static bool classof(const CTFType *T) { return T->kind == eEnum; } + + uint32_t nelems; + uint32_t size; + std::vector<Value> values; +}; + +struct CTFFunction : public CTFType { + CTFFunction(lldb::user_id_t uid, llvm::StringRef name, uint32_t nargs, + uint32_t return_type, std::vector<uint32_t> args, bool variadic) + : CTFType(eFunction, uid, name), nargs(nargs), return_type(return_type), + args(std::move(args)), variadic(variadic) {} + + static bool classof(const CTFType *T) { return T->kind == eFunction; } + + uint32_t nargs; + uint32_t return_type; + + std::vector<uint32_t> args; + bool variadic = false; +}; + +struct CTFRecord : public CTFType { +public: + struct Field { + Field(llvm::StringRef name, uint32_t type, uint64_t offset) + : name(name), type(type), offset(offset) {} + + llvm::StringRef name; + uint32_t type; + uint64_t offset; + }; + + CTFRecord(Kind kind, lldb::user_id_t uid, llvm::StringRef name, + uint32_t nfields, uint32_t size, std::vector<Field> fields) + : CTFType(kind, uid, name), nfields(nfields), size(size), + fields(std::move(fields)) {} + + static bool classof(const CTFType *T) { + return T->kind == eStruct || T->kind == eUnion; + } + + uint32_t nfields; + uint32_t size; + std::vector<Field> fields; +}; + +struct CTFStruct : public CTFRecord { + CTFStruct(lldb::user_id_t uid, llvm::StringRef name, uint32_t nfields, + uint32_t size, std::vector<Field> fields) + : CTFRecord(eStruct, uid, name, nfields, size, std::move(fields)){}; + + static bool classof(const CTFType *T) { return T->kind == eStruct; } +}; + +struct CTFUnion : public CTFRecord { + CTFUnion(lldb::user_id_t uid, llvm::StringRef name, uint32_t nfields, + uint32_t size, std::vector<Field> fields) + : CTFRecord(eUnion, uid, name, nfields, size, std::move(fields)){}; + + static bool classof(const CTFType *T) { return T->kind == eUnion; } +}; + +struct CTFForward : public CTFType { + CTFForward(lldb::user_id_t uid, llvm::StringRef name) + : CTFType(eForward, uid, name) {} + + static bool classof(const CTFType *T) { return T->kind == eForward; } +}; + +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_SYMBOLFILE_CTF_CTFTYPES_H diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/CTF/SymbolFileCTF.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/CTF/SymbolFileCTF.cpp new file mode 100644 index 000000000000..386ba44c5ea6 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/CTF/SymbolFileCTF.cpp @@ -0,0 +1,1118 @@ +//===-- SymbolFileCTF.cpp ----------------------------------------------===// +// +// 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 "SymbolFileCTF.h" + +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Host/Config.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/Symtab.h" +#include "lldb/Symbol/TypeList.h" +#include "lldb/Symbol/TypeMap.h" +#include "lldb/Symbol/Variable.h" +#include "lldb/Symbol/VariableList.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/RegularExpression.h" +#include "lldb/Utility/StreamBuffer.h" +#include "lldb/Utility/StreamString.h" +#include "lldb/Utility/Timer.h" +#include "llvm/Support/MemoryBuffer.h" + +#include "Plugins/ExpressionParser/Clang/ClangASTMetadata.h" +#include "Plugins/TypeSystem/Clang/TypeSystemClang.h" + +#include <memory> +#include <optional> + +#if LLVM_ENABLE_ZLIB +#include <zlib.h> +#endif + +using namespace llvm; +using namespace lldb; +using namespace lldb_private; + +LLDB_PLUGIN_DEFINE(SymbolFileCTF) + +char SymbolFileCTF::ID; + +SymbolFileCTF::SymbolFileCTF(lldb::ObjectFileSP objfile_sp) + : SymbolFileCommon(std::move(objfile_sp)) {} + +void SymbolFileCTF::Initialize() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), CreateInstance); +} + +void SymbolFileCTF::Terminate() { + PluginManager::UnregisterPlugin(CreateInstance); +} + +llvm::StringRef SymbolFileCTF::GetPluginDescriptionStatic() { + return "Compact C Type Format Symbol Reader"; +} + +SymbolFile *SymbolFileCTF::CreateInstance(ObjectFileSP objfile_sp) { + return new SymbolFileCTF(std::move(objfile_sp)); +} + +bool SymbolFileCTF::ParseHeader() { + if (m_header) + return true; + + Log *log = GetLog(LLDBLog::Symbols); + + ModuleSP module_sp(m_objfile_sp->GetModule()); + const SectionList *section_list = module_sp->GetSectionList(); + if (!section_list) + return false; + + SectionSP section_sp( + section_list->FindSectionByType(lldb::eSectionTypeCTF, true)); + if (!section_sp) + return false; + + m_objfile_sp->ReadSectionData(section_sp.get(), m_data); + + if (m_data.GetByteSize() == 0) + return false; + + StreamString module_desc; + GetObjectFile()->GetModule()->GetDescription(module_desc.AsRawOstream(), + lldb::eDescriptionLevelBrief); + LLDB_LOG(log, "Parsing Compact C Type format for {0}", module_desc.GetData()); + + lldb::offset_t offset = 0; + + // Parse CTF header. + constexpr size_t ctf_header_size = sizeof(ctf_header_t); + if (!m_data.ValidOffsetForDataOfSize(offset, ctf_header_size)) { + LLDB_LOG(log, "CTF parsing failed: insufficient data for CTF header"); + return false; + } + + m_header.emplace(); + + ctf_header_t &ctf_header = *m_header; + ctf_header.preamble.magic = m_data.GetU16(&offset); + ctf_header.preamble.version = m_data.GetU8(&offset); + ctf_header.preamble.flags = m_data.GetU8(&offset); + ctf_header.parlabel = m_data.GetU32(&offset); + ctf_header.parname = m_data.GetU32(&offset); + ctf_header.lbloff = m_data.GetU32(&offset); + ctf_header.objtoff = m_data.GetU32(&offset); + ctf_header.funcoff = m_data.GetU32(&offset); + ctf_header.typeoff = m_data.GetU32(&offset); + ctf_header.stroff = m_data.GetU32(&offset); + ctf_header.strlen = m_data.GetU32(&offset); + + // Validate the preamble. + if (ctf_header.preamble.magic != g_ctf_magic) { + LLDB_LOG(log, "CTF parsing failed: invalid magic: {0:x}", + ctf_header.preamble.magic); + return false; + } + + if (ctf_header.preamble.version != g_ctf_version) { + LLDB_LOG(log, "CTF parsing failed: unsupported version: {0}", + ctf_header.preamble.version); + return false; + } + + LLDB_LOG(log, "Parsed valid CTF preamble: version {0}, flags {1:x}", + ctf_header.preamble.version, ctf_header.preamble.flags); + + m_body_offset = offset; + + if (ctf_header.preamble.flags & eFlagCompress) { + // The body has been compressed with zlib deflate. Header offsets point into + // the decompressed data. +#if LLVM_ENABLE_ZLIB + const std::size_t decompressed_size = ctf_header.stroff + ctf_header.strlen; + DataBufferSP decompressed_data = + std::make_shared<DataBufferHeap>(decompressed_size, 0x0); + + z_stream zstr; + memset(&zstr, 0, sizeof(zstr)); + zstr.next_in = (Bytef *)const_cast<uint8_t *>(m_data.GetDataStart() + + sizeof(ctf_header_t)); + zstr.avail_in = m_data.BytesLeft(offset); + zstr.next_out = + (Bytef *)const_cast<uint8_t *>(decompressed_data->GetBytes()); + zstr.avail_out = decompressed_size; + + int rc = inflateInit(&zstr); + if (rc != Z_OK) { + LLDB_LOG(log, "CTF parsing failed: inflate initialization error: {0}", + zError(rc)); + return false; + } + + rc = inflate(&zstr, Z_FINISH); + if (rc != Z_STREAM_END) { + LLDB_LOG(log, "CTF parsing failed: inflate error: {0}", zError(rc)); + return false; + } + + rc = inflateEnd(&zstr); + if (rc != Z_OK) { + LLDB_LOG(log, "CTF parsing failed: inflate end error: {0}", zError(rc)); + return false; + } + + if (zstr.total_out != decompressed_size) { + LLDB_LOG(log, + "CTF parsing failed: decompressed size ({0}) doesn't match " + "expected size ([1})", + zstr.total_out, decompressed_size); + return false; + } + + m_data = DataExtractor(decompressed_data, m_data.GetByteOrder(), + m_data.GetAddressByteSize()); + m_body_offset = 0; +#else + LLDB_LOG( + log, + "CTF parsing failed: data is compressed but no zlib inflate support"); + return false; +#endif + } + + // Validate the header. + if (!m_data.ValidOffset(m_body_offset + ctf_header.lbloff)) { + LLDB_LOG(log, + "CTF parsing failed: invalid label section offset in header: {0}", + ctf_header.lbloff); + return false; + } + + if (!m_data.ValidOffset(m_body_offset + ctf_header.objtoff)) { + LLDB_LOG(log, + "CTF parsing failed: invalid object section offset in header: {0}", + ctf_header.objtoff); + return false; + } + + if (!m_data.ValidOffset(m_body_offset + ctf_header.funcoff)) { + LLDB_LOG( + log, + "CTF parsing failed: invalid function section offset in header: {0}", + ctf_header.funcoff); + return false; + } + + if (!m_data.ValidOffset(m_body_offset + ctf_header.typeoff)) { + LLDB_LOG(log, + "CTF parsing failed: invalid type section offset in header: {0}", + ctf_header.typeoff); + return false; + } + + if (!m_data.ValidOffset(m_body_offset + ctf_header.stroff)) { + LLDB_LOG(log, + "CTF parsing failed: invalid string section offset in header: {0}", + ctf_header.stroff); + return false; + } + + const lldb::offset_t str_end_offset = + m_body_offset + ctf_header.stroff + ctf_header.strlen; + if (!m_data.ValidOffset(str_end_offset - 1)) { + LLDB_LOG(log, + "CTF parsing failed: invalid string section length in header: {0}", + ctf_header.strlen); + return false; + } + + if (m_body_offset + ctf_header.stroff + ctf_header.parlabel > + str_end_offset) { + LLDB_LOG(log, + "CTF parsing failed: invalid parent label offset: {0} exceeds end " + "of string section ({1})", + ctf_header.parlabel, str_end_offset); + return false; + } + + if (m_body_offset + ctf_header.stroff + ctf_header.parname > str_end_offset) { + LLDB_LOG(log, + "CTF parsing failed: invalid parent name offset: {0} exceeds end " + "of string section ({1})", + ctf_header.parname, str_end_offset); + return false; + } + + LLDB_LOG(log, + "Parsed valid CTF header: lbloff = {0}, objtoff = {1}, funcoff = " + "{2}, typeoff = {3}, stroff = {4}, strlen = {5}", + ctf_header.lbloff, ctf_header.objtoff, ctf_header.funcoff, + ctf_header.typeoff, ctf_header.stroff, ctf_header.strlen); + + return true; +} + +void SymbolFileCTF::InitializeObject() { + Log *log = GetLog(LLDBLog::Symbols); + + auto type_system_or_err = GetTypeSystemForLanguage(lldb::eLanguageTypeC); + if (auto err = type_system_or_err.takeError()) { + LLDB_LOG_ERROR(log, std::move(err), "Unable to get type system: {0}"); + return; + } + + auto ts = *type_system_or_err; + m_ast = llvm::dyn_cast_or_null<TypeSystemClang>(ts.get()); + LazyBool optimized = eLazyBoolNo; + m_comp_unit_sp = std::make_shared<CompileUnit>( + m_objfile_sp->GetModule(), nullptr, "", 0, eLanguageTypeC, optimized); + + ParseTypes(*m_comp_unit_sp); +} + +llvm::StringRef SymbolFileCTF::ReadString(lldb::offset_t str_offset) const { + lldb::offset_t offset = m_body_offset + m_header->stroff + str_offset; + if (!m_data.ValidOffset(offset)) + return "(invalid)"; + const char *str = m_data.GetCStr(&offset); + if (str && !*str) + return "(anon)"; + return llvm::StringRef(str); +} + +/// Return the integer display representation encoded in the given data. +static uint32_t GetEncoding(uint32_t data) { + // Mask bits 24–31. + return ((data)&0xff000000) >> 24; +} + +/// Return the integral width in bits encoded in the given data. +static uint32_t GetBits(uint32_t data) { + // Mask bits 0-15. + return (data)&0x0000ffff; +} + +/// Return the type kind encoded in the given data. +uint32_t GetKind(uint32_t data) { + // Mask bits 26–31. + return ((data)&0xf800) >> 11; +} + +/// Return the variable length encoded in the given data. +uint32_t GetVLen(uint32_t data) { + // Mask bits 0–24. + return (data)&0x3ff; +} + +static uint32_t GetBytes(uint32_t bits) { return bits / sizeof(unsigned); } + +static clang::TagTypeKind TranslateRecordKind(CTFType::Kind type) { + switch (type) { + case CTFType::Kind::eStruct: + return clang::TagTypeKind::Struct; + case CTFType::Kind::eUnion: + return clang::TagTypeKind::Union; + default: + lldbassert(false && "Invalid record kind!"); + return clang::TagTypeKind::Struct; + } +} + +llvm::Expected<TypeSP> +SymbolFileCTF::CreateInteger(const CTFInteger &ctf_integer) { + lldb::BasicType basic_type = + TypeSystemClang::GetBasicTypeEnumeration(ctf_integer.name); + if (basic_type == eBasicTypeInvalid) + return llvm::make_error<llvm::StringError>( + llvm::formatv("unsupported integer type: no corresponding basic clang " + "type for '{0}'", + ctf_integer.name), + llvm::inconvertibleErrorCode()); + + CompilerType compiler_type = m_ast->GetBasicType(basic_type); + + if (basic_type != eBasicTypeVoid && basic_type != eBasicTypeBool) { + // Make sure the type we got is an integer type. + bool compiler_type_is_signed = false; + if (!compiler_type.IsIntegerType(compiler_type_is_signed)) + return llvm::make_error<llvm::StringError>( + llvm::formatv( + "Found compiler type for '{0}' but it's not an integer type: {1}", + ctf_integer.name, + compiler_type.GetDisplayTypeName().GetStringRef()), + llvm::inconvertibleErrorCode()); + + // Make sure the signing matches between the CTF and the compiler type. + const bool type_is_signed = (ctf_integer.encoding & IntEncoding::eSigned); + if (compiler_type_is_signed != type_is_signed) + return llvm::make_error<llvm::StringError>( + llvm::formatv("Found integer compiler type for {0} but compiler type " + "is {1} and {0} is {2}", + ctf_integer.name, + compiler_type_is_signed ? "signed" : "unsigned", + type_is_signed ? "signed" : "unsigned"), + llvm::inconvertibleErrorCode()); + } + + Declaration decl; + return MakeType(ctf_integer.uid, ConstString(ctf_integer.name), + GetBytes(ctf_integer.bits), nullptr, LLDB_INVALID_UID, + lldb_private::Type::eEncodingIsUID, decl, compiler_type, + lldb_private::Type::ResolveState::Full); +} + +llvm::Expected<lldb::TypeSP> +SymbolFileCTF::CreateModifier(const CTFModifier &ctf_modifier) { + Type *ref_type = ResolveTypeUID(ctf_modifier.type); + if (!ref_type) + return llvm::make_error<llvm::StringError>( + llvm::formatv("Could not find modified type: {0}", ctf_modifier.type), + llvm::inconvertibleErrorCode()); + + CompilerType compiler_type; + + switch (ctf_modifier.kind) { + case CTFType::ePointer: + compiler_type = ref_type->GetFullCompilerType().GetPointerType(); + break; + case CTFType::eConst: + compiler_type = ref_type->GetFullCompilerType().AddConstModifier(); + break; + case CTFType::eVolatile: + compiler_type = ref_type->GetFullCompilerType().AddVolatileModifier(); + break; + case CTFType::eRestrict: + compiler_type = ref_type->GetFullCompilerType().AddRestrictModifier(); + break; + default: + return llvm::make_error<llvm::StringError>( + llvm::formatv("ParseModifier called with unsupported kind: {0}", + ctf_modifier.kind), + llvm::inconvertibleErrorCode()); + } + + Declaration decl; + return MakeType(ctf_modifier.uid, ConstString(), 0, nullptr, LLDB_INVALID_UID, + Type::eEncodingIsUID, decl, compiler_type, + lldb_private::Type::ResolveState::Full); +} + +llvm::Expected<lldb::TypeSP> +SymbolFileCTF::CreateTypedef(const CTFTypedef &ctf_typedef) { + Type *underlying_type = ResolveTypeUID(ctf_typedef.type); + if (!underlying_type) + return llvm::make_error<llvm::StringError>( + llvm::formatv("Could not find typedef underlying type: {0}", + ctf_typedef.type), + llvm::inconvertibleErrorCode()); + + CompilerType target_ast_type = underlying_type->GetFullCompilerType(); + clang::DeclContext *decl_ctx = m_ast->GetTranslationUnitDecl(); + CompilerType ast_typedef = target_ast_type.CreateTypedef( + ctf_typedef.name.data(), m_ast->CreateDeclContext(decl_ctx), 0); + + Declaration decl; + return MakeType(ctf_typedef.uid, ConstString(ctf_typedef.name), 0, nullptr, + LLDB_INVALID_UID, lldb_private::Type::eEncodingIsUID, decl, + ast_typedef, lldb_private::Type::ResolveState::Full); +} + +llvm::Expected<lldb::TypeSP> +SymbolFileCTF::CreateArray(const CTFArray &ctf_array) { + Type *element_type = ResolveTypeUID(ctf_array.type); + if (!element_type) + return llvm::make_error<llvm::StringError>( + llvm::formatv("Could not find array element type: {0}", ctf_array.type), + llvm::inconvertibleErrorCode()); + + std::optional<uint64_t> element_size = element_type->GetByteSize(nullptr); + if (!element_size) + return llvm::make_error<llvm::StringError>( + llvm::formatv("could not get element size of type: {0}", + ctf_array.type), + llvm::inconvertibleErrorCode()); + + uint64_t size = ctf_array.nelems * *element_size; + + CompilerType compiler_type = m_ast->CreateArrayType( + element_type->GetFullCompilerType(), ctf_array.nelems, + /*is_gnu_vector*/ false); + + Declaration decl; + return MakeType(ctf_array.uid, ConstString(), size, nullptr, LLDB_INVALID_UID, + Type::eEncodingIsUID, decl, compiler_type, + lldb_private::Type::ResolveState::Full); +} + +llvm::Expected<lldb::TypeSP> +SymbolFileCTF::CreateEnum(const CTFEnum &ctf_enum) { + Declaration decl; + CompilerType enum_type = m_ast->CreateEnumerationType( + ctf_enum.name, m_ast->GetTranslationUnitDecl(), OptionalClangModuleID(), + decl, m_ast->GetBasicType(eBasicTypeInt), + /*is_scoped=*/false); + + for (const CTFEnum::Value &value : ctf_enum.values) { + Declaration value_decl; + m_ast->AddEnumerationValueToEnumerationType( + enum_type, value_decl, value.name.data(), value.value, ctf_enum.size); + } + TypeSystemClang::CompleteTagDeclarationDefinition(enum_type); + + return MakeType(ctf_enum.uid, ConstString(), 0, nullptr, LLDB_INVALID_UID, + Type::eEncodingIsUID, decl, enum_type, + lldb_private::Type::ResolveState::Full); +} + +llvm::Expected<lldb::TypeSP> +SymbolFileCTF::CreateFunction(const CTFFunction &ctf_function) { + std::vector<CompilerType> arg_types; + for (uint32_t arg : ctf_function.args) { + if (Type *arg_type = ResolveTypeUID(arg)) + arg_types.push_back(arg_type->GetFullCompilerType()); + } + + Type *ret_type = ResolveTypeUID(ctf_function.return_type); + if (!ret_type) + return llvm::make_error<llvm::StringError>( + llvm::formatv("Could not find function return type: {0}", + ctf_function.return_type), + llvm::inconvertibleErrorCode()); + + CompilerType func_type = m_ast->CreateFunctionType( + ret_type->GetFullCompilerType(), arg_types.data(), arg_types.size(), + ctf_function.variadic, 0, clang::CallingConv::CC_C); + + Declaration decl; + return MakeType(ctf_function.uid, ConstString(ctf_function.name), 0, nullptr, + LLDB_INVALID_UID, Type::eEncodingIsUID, decl, func_type, + lldb_private::Type::ResolveState::Full); +} + +llvm::Expected<lldb::TypeSP> +SymbolFileCTF::CreateRecord(const CTFRecord &ctf_record) { + const clang::TagTypeKind tag_kind = TranslateRecordKind(ctf_record.kind); + CompilerType record_type = m_ast->CreateRecordType( + nullptr, OptionalClangModuleID(), eAccessPublic, ctf_record.name.data(), + llvm::to_underlying(tag_kind), eLanguageTypeC); + m_compiler_types[record_type.GetOpaqueQualType()] = &ctf_record; + Declaration decl; + return MakeType(ctf_record.uid, ConstString(ctf_record.name), ctf_record.size, + nullptr, LLDB_INVALID_UID, lldb_private::Type::eEncodingIsUID, + decl, record_type, lldb_private::Type::ResolveState::Forward); +} + +bool SymbolFileCTF::CompleteType(CompilerType &compiler_type) { + // Check if we have a CTF type for the given incomplete compiler type. + auto it = m_compiler_types.find(compiler_type.GetOpaqueQualType()); + if (it == m_compiler_types.end()) + return false; + + const CTFType *ctf_type = it->second; + assert(ctf_type && "m_compiler_types should only contain valid CTF types"); + + // We only support resolving record types. + assert(llvm::isa<CTFRecord>(ctf_type)); + + // Cast to the appropriate CTF type. + const CTFRecord *ctf_record = static_cast<const CTFRecord *>(ctf_type); + + // If any of the fields are incomplete, we cannot complete the type. + for (const CTFRecord::Field &field : ctf_record->fields) { + if (!ResolveTypeUID(field.type)) { + LLDB_LOG(GetLog(LLDBLog::Symbols), + "Cannot complete type {0} because field {1} is incomplete", + ctf_type->uid, field.type); + return false; + } + } + + // Complete the record type. + m_ast->StartTagDeclarationDefinition(compiler_type); + for (const CTFRecord::Field &field : ctf_record->fields) { + Type *field_type = ResolveTypeUID(field.type); + assert(field_type && "field must be complete"); + const uint32_t field_size = field_type->GetByteSize(nullptr).value_or(0); + TypeSystemClang::AddFieldToRecordType(compiler_type, field.name, + field_type->GetFullCompilerType(), + eAccessPublic, field_size); + } + m_ast->CompleteTagDeclarationDefinition(compiler_type); + + // Now that the compiler type is complete, we don't need to remember it + // anymore and can remove the CTF record type. + m_compiler_types.erase(compiler_type.GetOpaqueQualType()); + m_ctf_types.erase(ctf_type->uid); + + return true; +} + +llvm::Expected<lldb::TypeSP> +SymbolFileCTF::CreateForward(const CTFForward &ctf_forward) { + CompilerType forward_compiler_type = m_ast->CreateRecordType( + nullptr, OptionalClangModuleID(), eAccessPublic, ctf_forward.name, + llvm::to_underlying(clang::TagTypeKind::Struct), eLanguageTypeC); + Declaration decl; + return MakeType(ctf_forward.uid, ConstString(ctf_forward.name), 0, nullptr, + LLDB_INVALID_UID, Type::eEncodingIsUID, decl, + forward_compiler_type, Type::ResolveState::Forward); +} + +llvm::Expected<TypeSP> SymbolFileCTF::CreateType(CTFType *ctf_type) { + if (!ctf_type) + return llvm::make_error<llvm::StringError>( + "cannot create type for unparsed type", llvm::inconvertibleErrorCode()); + + switch (ctf_type->kind) { + case CTFType::Kind::eInteger: + return CreateInteger(*static_cast<CTFInteger *>(ctf_type)); + case CTFType::Kind::eConst: + case CTFType::Kind::ePointer: + case CTFType::Kind::eRestrict: + case CTFType::Kind::eVolatile: + return CreateModifier(*static_cast<CTFModifier *>(ctf_type)); + case CTFType::Kind::eTypedef: + return CreateTypedef(*static_cast<CTFTypedef *>(ctf_type)); + case CTFType::Kind::eArray: + return CreateArray(*static_cast<CTFArray *>(ctf_type)); + case CTFType::Kind::eEnum: + return CreateEnum(*static_cast<CTFEnum *>(ctf_type)); + case CTFType::Kind::eFunction: + return CreateFunction(*static_cast<CTFFunction *>(ctf_type)); + case CTFType::Kind::eStruct: + case CTFType::Kind::eUnion: + return CreateRecord(*static_cast<CTFRecord *>(ctf_type)); + case CTFType::Kind::eForward: + return CreateForward(*static_cast<CTFForward *>(ctf_type)); + case CTFType::Kind::eUnknown: + case CTFType::Kind::eFloat: + case CTFType::Kind::eSlice: + return llvm::make_error<llvm::StringError>( + llvm::formatv("unsupported type (uid = {0}, name = {1}, kind = {2})", + ctf_type->uid, ctf_type->name, ctf_type->kind), + llvm::inconvertibleErrorCode()); + } + llvm_unreachable("Unexpected CTF type kind"); +} + +llvm::Expected<std::unique_ptr<CTFType>> +SymbolFileCTF::ParseType(lldb::offset_t &offset, lldb::user_id_t uid) { + ctf_stype_t ctf_stype; + ctf_stype.name = m_data.GetU32(&offset); + ctf_stype.info = m_data.GetU32(&offset); + ctf_stype.size = m_data.GetU32(&offset); + + llvm::StringRef name = ReadString(ctf_stype.name); + const uint32_t kind = GetKind(ctf_stype.info); + const uint32_t variable_length = GetVLen(ctf_stype.info); + const uint32_t type = ctf_stype.GetType(); + const uint32_t size = ctf_stype.GetSize(); + + switch (kind) { + case TypeKind::eInteger: { + const uint32_t vdata = m_data.GetU32(&offset); + const uint32_t bits = GetBits(vdata); + const uint32_t encoding = GetEncoding(vdata); + return std::make_unique<CTFInteger>(uid, name, bits, encoding); + } + case TypeKind::eConst: + return std::make_unique<CTFConst>(uid, type); + case TypeKind::ePointer: + return std::make_unique<CTFPointer>(uid, type); + case TypeKind::eRestrict: + return std::make_unique<CTFRestrict>(uid, type); + case TypeKind::eVolatile: + return std::make_unique<CTFVolatile>(uid, type); + case TypeKind::eTypedef: + return std::make_unique<CTFTypedef>(uid, name, type); + case TypeKind::eArray: { + const uint32_t type = m_data.GetU32(&offset); + const uint32_t index = m_data.GetU32(&offset); + const uint32_t nelems = m_data.GetU32(&offset); + return std::make_unique<CTFArray>(uid, name, type, index, nelems); + } + case TypeKind::eEnum: { + std::vector<CTFEnum::Value> values; + for (uint32_t i = 0; i < variable_length; ++i) { + const uint32_t value_name = m_data.GetU32(&offset); + const uint32_t value = m_data.GetU32(&offset); + values.emplace_back(ReadString(value_name), value); + } + return std::make_unique<CTFEnum>(uid, name, variable_length, size, values); + } + case TypeKind::eFunction: { + std::vector<uint32_t> args; + bool variadic = false; + for (uint32_t i = 0; i < variable_length; ++i) { + const uint32_t arg_uid = m_data.GetU32(&offset); + // If the last argument is 0, this is a variadic function. + if (arg_uid == 0) { + variadic = true; + break; + } + args.push_back(arg_uid); + } + // If the number of arguments is odd, a single uint32_t of padding is + // inserted to maintain alignment. + if (variable_length % 2 == 1) + m_data.GetU32(&offset); + return std::make_unique<CTFFunction>(uid, name, variable_length, type, args, + variadic); + } + case TypeKind::eStruct: + case TypeKind::eUnion: { + std::vector<CTFRecord::Field> fields; + for (uint32_t i = 0; i < variable_length; ++i) { + const uint32_t field_name = m_data.GetU32(&offset); + const uint32_t type = m_data.GetU32(&offset); + uint64_t field_offset = 0; + if (size < g_ctf_field_threshold) { + field_offset = m_data.GetU16(&offset); + m_data.GetU16(&offset); // Padding + } else { + const uint32_t offset_hi = m_data.GetU32(&offset); + const uint32_t offset_lo = m_data.GetU32(&offset); + field_offset = (((uint64_t)offset_hi) << 32) | ((uint64_t)offset_lo); + } + fields.emplace_back(ReadString(field_name), type, field_offset); + } + return std::make_unique<CTFRecord>(static_cast<CTFType::Kind>(kind), uid, + name, variable_length, size, fields); + } + case TypeKind::eForward: + return std::make_unique<CTFForward>(uid, name); + case TypeKind::eUnknown: + return std::make_unique<CTFType>(static_cast<CTFType::Kind>(kind), uid, + name); + case TypeKind::eFloat: + case TypeKind::eSlice: + offset += (variable_length * sizeof(uint32_t)); + break; + } + + return llvm::make_error<llvm::StringError>( + llvm::formatv("unsupported type (name = {0}, kind = {1}, vlength = {2})", + name, kind, variable_length), + llvm::inconvertibleErrorCode()); +} + +size_t SymbolFileCTF::ParseTypes(CompileUnit &cu) { + if (!ParseHeader()) + return 0; + + if (!m_types.empty()) + return 0; + + if (!m_ast) + return 0; + + Log *log = GetLog(LLDBLog::Symbols); + LLDB_LOG(log, "Parsing CTF types"); + + lldb::offset_t type_offset = m_body_offset + m_header->typeoff; + const lldb::offset_t type_offset_end = m_body_offset + m_header->stroff; + + lldb::user_id_t type_uid = 1; + while (type_offset < type_offset_end) { + llvm::Expected<std::unique_ptr<CTFType>> type_or_error = + ParseType(type_offset, type_uid); + if (type_or_error) { + m_ctf_types[(*type_or_error)->uid] = std::move(*type_or_error); + } else { + LLDB_LOG_ERROR(log, type_or_error.takeError(), + "Failed to parse type {1} at offset {2}: {0}", type_uid, + type_offset); + } + type_uid++; + } + + LLDB_LOG(log, "Parsed {0} CTF types", m_ctf_types.size()); + + for (lldb::user_id_t uid = 1; uid < type_uid; ++uid) + ResolveTypeUID(uid); + + LLDB_LOG(log, "Created {0} CTF types", m_types.size()); + + return m_types.size(); +} + +size_t SymbolFileCTF::ParseFunctions(CompileUnit &cu) { + if (!ParseHeader()) + return 0; + + if (!m_functions.empty()) + return 0; + + if (!m_ast) + return 0; + + Symtab *symtab = GetObjectFile()->GetModule()->GetSymtab(); + if (!symtab) + return 0; + + Log *log = GetLog(LLDBLog::Symbols); + LLDB_LOG(log, "Parsing CTF functions"); + + lldb::offset_t function_offset = m_body_offset + m_header->funcoff; + const lldb::offset_t function_offset_end = m_body_offset + m_header->typeoff; + + uint32_t symbol_idx = 0; + Declaration decl; + while (function_offset < function_offset_end) { + const uint32_t info = m_data.GetU32(&function_offset); + const uint16_t kind = GetKind(info); + const uint16_t variable_length = GetVLen(info); + + Symbol *symbol = symtab->FindSymbolWithType( + eSymbolTypeCode, Symtab::eDebugYes, Symtab::eVisibilityAny, symbol_idx); + + // Skip padding. + if (kind == TypeKind::eUnknown && variable_length == 0) + continue; + + // Skip unexpected kinds. + if (kind != TypeKind::eFunction) + continue; + + const uint32_t ret_uid = m_data.GetU32(&function_offset); + const uint32_t num_args = variable_length; + + std::vector<CompilerType> arg_types; + arg_types.reserve(num_args); + + bool is_variadic = false; + for (uint32_t i = 0; i < variable_length; i++) { + const uint32_t arg_uid = m_data.GetU32(&function_offset); + + // If the last argument is 0, this is a variadic function. + if (arg_uid == 0) { + is_variadic = true; + break; + } + + Type *arg_type = ResolveTypeUID(arg_uid); + arg_types.push_back(arg_type ? arg_type->GetFullCompilerType() + : CompilerType()); + } + + if (symbol) { + Type *ret_type = ResolveTypeUID(ret_uid); + AddressRange func_range = + AddressRange(symbol->GetFileAddress(), symbol->GetByteSize(), + GetObjectFile()->GetModule()->GetSectionList()); + + // Create function type. + CompilerType func_type = m_ast->CreateFunctionType( + ret_type ? ret_type->GetFullCompilerType() : CompilerType(), + arg_types.data(), arg_types.size(), is_variadic, 0, + clang::CallingConv::CC_C); + lldb::user_id_t function_type_uid = m_types.size() + 1; + TypeSP type_sp = + MakeType(function_type_uid, symbol->GetName(), 0, nullptr, + LLDB_INVALID_UID, Type::eEncodingIsUID, decl, func_type, + lldb_private::Type::ResolveState::Full); + m_types[function_type_uid] = type_sp; + + // Create function. + lldb::user_id_t func_uid = m_functions.size(); + FunctionSP function_sp = std::make_shared<Function>( + &cu, func_uid, function_type_uid, symbol->GetMangled(), type_sp.get(), + func_range); + m_functions.emplace_back(function_sp); + cu.AddFunction(function_sp); + } + } + + LLDB_LOG(log, "CTF parsed {0} functions", m_functions.size()); + + return m_functions.size(); +} + +static DWARFExpression CreateDWARFExpression(ModuleSP module_sp, + const Symbol &symbol) { + if (!module_sp) + return DWARFExpression(); + + const ArchSpec &architecture = module_sp->GetArchitecture(); + ByteOrder byte_order = architecture.GetByteOrder(); + uint32_t address_size = architecture.GetAddressByteSize(); + uint32_t byte_size = architecture.GetDataByteSize(); + + StreamBuffer<32> stream(Stream::eBinary, address_size, byte_order); + stream.PutHex8(lldb_private::dwarf::DW_OP_addr); + stream.PutMaxHex64(symbol.GetFileAddress(), address_size, byte_order); + + DataBufferSP buffer = + std::make_shared<DataBufferHeap>(stream.GetData(), stream.GetSize()); + lldb_private::DataExtractor extractor(buffer, byte_order, address_size, + byte_size); + DWARFExpression result(extractor); + result.SetRegisterKind(eRegisterKindDWARF); + + return result; +} + +size_t SymbolFileCTF::ParseObjects(CompileUnit &comp_unit) { + if (!ParseHeader()) + return 0; + + if (!m_variables.empty()) + return 0; + + if (!m_ast) + return 0; + + ModuleSP module_sp = GetObjectFile()->GetModule(); + Symtab *symtab = module_sp->GetSymtab(); + if (!symtab) + return 0; + + Log *log = GetLog(LLDBLog::Symbols); + LLDB_LOG(log, "Parsing CTF objects"); + + lldb::offset_t object_offset = m_body_offset + m_header->objtoff; + const lldb::offset_t object_offset_end = m_body_offset + m_header->funcoff; + + uint32_t symbol_idx = 0; + Declaration decl; + while (object_offset < object_offset_end) { + const uint32_t type_uid = m_data.GetU32(&object_offset); + + if (Symbol *symbol = + symtab->FindSymbolWithType(eSymbolTypeData, Symtab::eDebugYes, + Symtab::eVisibilityAny, symbol_idx)) { + Variable::RangeList ranges; + ranges.Append(symbol->GetFileAddress(), symbol->GetByteSize()); + + auto type_sp = std::make_shared<SymbolFileType>(*this, type_uid); + + DWARFExpressionList location( + module_sp, CreateDWARFExpression(module_sp, *symbol), nullptr); + + lldb::user_id_t variable_type_uid = m_variables.size(); + m_variables.emplace_back(std::make_shared<Variable>( + variable_type_uid, symbol->GetName().AsCString(), + symbol->GetName().AsCString(), type_sp, eValueTypeVariableGlobal, + m_comp_unit_sp.get(), ranges, &decl, location, symbol->IsExternal(), + /*artificial=*/false, + /*location_is_constant_data*/ false)); + } + } + + LLDB_LOG(log, "Parsed {0} CTF objects", m_variables.size()); + + return m_variables.size(); +} + +uint32_t SymbolFileCTF::CalculateAbilities() { + if (!m_objfile_sp) + return 0; + + if (!ParseHeader()) + return 0; + + return VariableTypes | Functions | GlobalVariables; +} + +uint32_t SymbolFileCTF::ResolveSymbolContext(const Address &so_addr, + SymbolContextItem resolve_scope, + SymbolContext &sc) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + if (m_objfile_sp->GetSymtab() == nullptr) + return 0; + + uint32_t resolved_flags = 0; + + // Resolve symbols. + if (resolve_scope & eSymbolContextSymbol) { + sc.symbol = m_objfile_sp->GetSymtab()->FindSymbolContainingFileAddress( + so_addr.GetFileAddress()); + if (sc.symbol) + resolved_flags |= eSymbolContextSymbol; + } + + // Resolve functions. + if (resolve_scope & eSymbolContextFunction) { + for (FunctionSP function_sp : m_functions) { + if (function_sp->GetAddressRange().ContainsFileAddress( + so_addr.GetFileAddress())) { + sc.function = function_sp.get(); + resolved_flags |= eSymbolContextFunction; + break; + } + } + } + + // Resolve variables. + if (resolve_scope & eSymbolContextVariable) { + for (VariableSP variable_sp : m_variables) { + if (variable_sp->LocationIsValidForAddress(so_addr.GetFileAddress())) { + sc.variable = variable_sp.get(); + break; + } + } + } + + return resolved_flags; +} + +CompUnitSP SymbolFileCTF::ParseCompileUnitAtIndex(uint32_t idx) { + if (idx == 0) + return m_comp_unit_sp; + return {}; +} + +size_t +SymbolFileCTF::ParseVariablesForContext(const lldb_private::SymbolContext &sc) { + return ParseObjects(*m_comp_unit_sp); +} + +void SymbolFileCTF::AddSymbols(Symtab &symtab) { + // CTF does not encode symbols. + // We rely on the existing symbol table to map symbols to type. +} + +lldb_private::Type *SymbolFileCTF::ResolveTypeUID(lldb::user_id_t type_uid) { + auto type_it = m_types.find(type_uid); + if (type_it != m_types.end()) + return type_it->second.get(); + + auto ctf_type_it = m_ctf_types.find(type_uid); + if (ctf_type_it == m_ctf_types.end()) + return nullptr; + + CTFType *ctf_type = ctf_type_it->second.get(); + assert(ctf_type && "m_ctf_types should only contain valid CTF types"); + + Log *log = GetLog(LLDBLog::Symbols); + + llvm::Expected<TypeSP> type_or_error = CreateType(ctf_type); + if (!type_or_error) { + LLDB_LOG_ERROR(log, type_or_error.takeError(), + "Failed to create type for {1}: {0}", ctf_type->uid); + return {}; + } + + TypeSP type_sp = *type_or_error; + + if (log) { + StreamString ss; + type_sp->Dump(&ss, true); + LLDB_LOGV(log, "Adding type {0}: {1}", type_sp->GetID(), + llvm::StringRef(ss.GetString()).rtrim()); + } + + m_types[type_uid] = type_sp; + + // Except for record types which we'll need to complete later, we don't need + // the CTF type anymore. + if (!isa<CTFRecord>(ctf_type)) + m_ctf_types.erase(type_uid); + + return type_sp.get(); +} + +void SymbolFileCTF::FindTypes(const lldb_private::TypeQuery &match, + lldb_private::TypeResults &results) { + // Make sure we haven't already searched this SymbolFile before. + if (results.AlreadySearched(this)) + return; + + ConstString name = match.GetTypeBasename(); + for (TypeSP type_sp : GetTypeList().Types()) { + if (type_sp && type_sp->GetName() == name) { + results.InsertUnique(type_sp); + if (results.Done(match)) + return; + } + } +} + +void SymbolFileCTF::FindTypesByRegex( + const lldb_private::RegularExpression ®ex, uint32_t max_matches, + lldb_private::TypeMap &types) { + ParseTypes(*m_comp_unit_sp); + + size_t matches = 0; + for (TypeSP type_sp : GetTypeList().Types()) { + if (matches == max_matches) + break; + if (type_sp && regex.Execute(type_sp->GetName())) + types.Insert(type_sp); + matches++; + } +} + +void SymbolFileCTF::FindFunctions( + const lldb_private::Module::LookupInfo &lookup_info, + const lldb_private::CompilerDeclContext &parent_decl_ctx, + bool include_inlines, lldb_private::SymbolContextList &sc_list) { + ParseFunctions(*m_comp_unit_sp); + + ConstString name = lookup_info.GetLookupName(); + for (FunctionSP function_sp : m_functions) { + if (function_sp && function_sp->GetName() == name) { + lldb_private::SymbolContext sc; + sc.comp_unit = m_comp_unit_sp.get(); + sc.function = function_sp.get(); + sc_list.Append(sc); + } + } +} + +void SymbolFileCTF::FindFunctions(const lldb_private::RegularExpression ®ex, + bool include_inlines, + lldb_private::SymbolContextList &sc_list) { + for (FunctionSP function_sp : m_functions) { + if (function_sp && regex.Execute(function_sp->GetName())) { + lldb_private::SymbolContext sc; + sc.comp_unit = m_comp_unit_sp.get(); + sc.function = function_sp.get(); + sc_list.Append(sc); + } + } +} + +void SymbolFileCTF::FindGlobalVariables( + lldb_private::ConstString name, + const lldb_private::CompilerDeclContext &parent_decl_ctx, + uint32_t max_matches, lldb_private::VariableList &variables) { + ParseObjects(*m_comp_unit_sp); + + size_t matches = 0; + for (VariableSP variable_sp : m_variables) { + if (matches == max_matches) + break; + if (variable_sp && variable_sp->GetName() == name) { + variables.AddVariable(variable_sp); + matches++; + } + } +} + +void SymbolFileCTF::FindGlobalVariables( + const lldb_private::RegularExpression ®ex, uint32_t max_matches, + lldb_private::VariableList &variables) { + ParseObjects(*m_comp_unit_sp); + + size_t matches = 0; + for (VariableSP variable_sp : m_variables) { + if (matches == max_matches) + break; + if (variable_sp && regex.Execute(variable_sp->GetName())) { + variables.AddVariable(variable_sp); + matches++; + } + } +} diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/CTF/SymbolFileCTF.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/CTF/SymbolFileCTF.h new file mode 100644 index 000000000000..3a80138fffbc --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/CTF/SymbolFileCTF.h @@ -0,0 +1,264 @@ +//===-- SymbolFileCTF.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_SOURCE_PLUGINS_SYMBOLFILE_CTF_SYMBOLFILECTF_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_CTF_SYMBOLFILECTF_H + +#include <map> +#include <optional> +#include <vector> + +#include "CTFTypes.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/SymbolFile.h" + +namespace lldb_private { + +class SymbolFileCTF : public lldb_private::SymbolFileCommon { + /// LLVM RTTI support. + static char ID; + +public: + /// LLVM RTTI support. + /// \{ + bool isA(const void *ClassID) const override { + return ClassID == &ID || SymbolFileCommon::isA(ClassID); + } + static bool classof(const SymbolFile *obj) { return obj->isA(&ID); } + /// \} + + SymbolFileCTF(lldb::ObjectFileSP objfile_sp); + + static void Initialize(); + + static void Terminate(); + + static llvm::StringRef GetPluginNameStatic() { return "CTF"; } + + static llvm::StringRef GetPluginDescriptionStatic(); + + static lldb_private::SymbolFile * + CreateInstance(lldb::ObjectFileSP objfile_sp); + + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } + + uint32_t CalculateAbilities() override; + + void InitializeObject() override; + + lldb::LanguageType ParseLanguage(CompileUnit &comp_unit) override { + return lldb::eLanguageTypeUnknown; + } + + bool ParseHeader(); + + size_t ParseFunctions(CompileUnit &comp_unit) override; + + size_t ParseObjects(CompileUnit &comp_unit); + + bool ParseLineTable(CompileUnit &comp_unit) override { return false; } + + bool ParseDebugMacros(CompileUnit &comp_unit) override { return false; } + + bool ParseSupportFiles(CompileUnit &comp_unit, + SupportFileList &support_files) override { + return false; + } + + size_t ParseTypes(CompileUnit &cu) override; + + bool ParseImportedModules( + const SymbolContext &sc, + std::vector<lldb_private::SourceModule> &imported_modules) override { + return false; + } + + size_t ParseBlocksRecursive(Function &func) override { return 0; } + + size_t ParseVariablesForContext(const SymbolContext &sc) override; + + uint32_t CalculateNumCompileUnits() override { return 0; } + + lldb::CompUnitSP ParseCompileUnitAtIndex(uint32_t index) override; + + Type *ResolveTypeUID(lldb::user_id_t type_uid) override; + std::optional<ArrayInfo> GetDynamicArrayInfoForUID( + lldb::user_id_t type_uid, + const lldb_private::ExecutionContext *exe_ctx) override { + return std::nullopt; + } + + bool CompleteType(CompilerType &compiler_type) override; + + uint32_t ResolveSymbolContext(const lldb_private::Address &so_addr, + lldb::SymbolContextItem resolve_scope, + lldb_private::SymbolContext &sc) override; + + void AddSymbols(Symtab &symtab) override; + + void GetTypes(lldb_private::SymbolContextScope *sc_scope, + lldb::TypeClass type_mask, + lldb_private::TypeList &type_list) override {} + + void FindTypes(const lldb_private::TypeQuery &match, + lldb_private::TypeResults &results) override; + + void FindTypesByRegex(const lldb_private::RegularExpression ®ex, + uint32_t max_matches, lldb_private::TypeMap &types); + + void FindFunctions(const lldb_private::Module::LookupInfo &lookup_info, + const lldb_private::CompilerDeclContext &parent_decl_ctx, + bool include_inlines, + lldb_private::SymbolContextList &sc_list) override; + + void FindFunctions(const lldb_private::RegularExpression ®ex, + bool include_inlines, + lldb_private::SymbolContextList &sc_list) override; + + void + FindGlobalVariables(lldb_private::ConstString name, + const lldb_private::CompilerDeclContext &parent_decl_ctx, + uint32_t max_matches, + lldb_private::VariableList &variables) override; + + void FindGlobalVariables(const lldb_private::RegularExpression ®ex, + uint32_t max_matches, + lldb_private::VariableList &variables) override; + + enum TypeKind : uint32_t { + eUnknown = 0, + eInteger = 1, + eFloat = 2, + ePointer = 3, + eArray = 4, + eFunction = 5, + eStruct = 6, + eUnion = 7, + eEnum = 8, + eForward = 9, + eTypedef = 10, + eVolatile = 11, + eConst = 12, + eRestrict = 13, + eSlice = 14, + }; + +private: + enum Flags : uint32_t { + eFlagCompress = (1u << 0), + eFlagNewFuncInfo = (1u << 1), + eFlagIdxSorted = (1u << 2), + eFlagDynStr = (1u << 3), + }; + + enum IntEncoding : uint32_t { + eSigned = 0x1, + eChar = 0x2, + eBool = 0x4, + eVarArgs = 0x8, + }; + + struct ctf_preamble_t { + uint16_t magic; + uint8_t version; + uint8_t flags; + }; + + struct ctf_header_t { + ctf_preamble_t preamble; + uint32_t parlabel; + uint32_t parname; + uint32_t lbloff; + uint32_t objtoff; + uint32_t funcoff; + uint32_t typeoff; + uint32_t stroff; + uint32_t strlen; + }; + + struct ctf_type_t { + uint32_t name; + uint32_t info; + union { + uint32_t size; + uint32_t type; + }; + uint32_t lsizehi; + uint32_t lsizelo; + }; + + struct ctf_stype_t { + uint32_t name; + uint32_t info; + union { + uint32_t size; + uint32_t type; + }; + + bool IsLargeType() const { return size == 0xffff; } + uint32_t GetStructSize() const { + if (IsLargeType()) + return sizeof(ctf_type_t); + return sizeof(ctf_stype_t); + } + uint32_t GetType() const { return type; } + uint32_t GetSize() const { return size; } + }; + + llvm::Expected<std::unique_ptr<CTFType>> ParseType(lldb::offset_t &offset, + lldb::user_id_t uid); + + llvm::Expected<lldb::TypeSP> CreateType(CTFType *ctf_type); + llvm::Expected<lldb::TypeSP> CreateInteger(const CTFInteger &ctf_integer); + llvm::Expected<lldb::TypeSP> CreateModifier(const CTFModifier &ctf_modifier); + llvm::Expected<lldb::TypeSP> CreateTypedef(const CTFTypedef &ctf_typedef); + llvm::Expected<lldb::TypeSP> CreateArray(const CTFArray &ctf_array); + llvm::Expected<lldb::TypeSP> CreateEnum(const CTFEnum &ctf_enum); + llvm::Expected<lldb::TypeSP> CreateFunction(const CTFFunction &ctf_function); + llvm::Expected<lldb::TypeSP> CreateRecord(const CTFRecord &ctf_record); + llvm::Expected<lldb::TypeSP> CreateForward(const CTFForward &ctf_forward); + + llvm::StringRef ReadString(lldb::offset_t offset) const; + + std::vector<uint16_t> GetFieldSizes(lldb::offset_t field_offset, + uint32_t fields, uint32_t struct_size); + + DataExtractor m_data; + + /// The start offset of the CTF body into m_data. If the body is uncompressed, + /// m_data contains the header and the body and the body starts after the + /// header. If the body is compressed, m_data only contains the body and the + /// offset is zero. + lldb::offset_t m_body_offset = 0; + + TypeSystemClang *m_ast; + lldb::CompUnitSP m_comp_unit_sp; + + std::optional<ctf_header_t> m_header; + + /// Parsed CTF types. + llvm::DenseMap<lldb::user_id_t, std::unique_ptr<CTFType>> m_ctf_types; + + /// Parsed LLDB types. + llvm::DenseMap<lldb::user_id_t, lldb::TypeSP> m_types; + + /// To complete types, we need a way to map (imcomplete) compiler types back + /// to parsed CTF types. + llvm::DenseMap<lldb::opaque_compiler_type_t, const CTFType *> + m_compiler_types; + + std::vector<lldb::FunctionSP> m_functions; + std::vector<lldb::VariableSP> m_variables; + + static constexpr uint16_t g_ctf_magic = 0xcff1; + static constexpr uint8_t g_ctf_version = 4; + static constexpr uint16_t g_ctf_field_threshold = 0x2000; +}; +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_SYMBOLFILE_CTF_SYMBOLFILECTF_H 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..1703597a7cd2 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/AppleDWARFIndex.cpp @@ -0,0 +1,313 @@ +//===-- AppleDWARFIndex.cpp -----------------------------------------------===// +// +// 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" +#include "llvm/Support/DJB.h" + +using namespace lldb_private; +using namespace lldb; +using namespace lldb_private::dwarf; +using namespace lldb_private::plugin::dwarf; + +std::unique_ptr<AppleDWARFIndex> AppleDWARFIndex::Create( + Module &module, DWARFDataExtractor apple_names, + DWARFDataExtractor apple_namespaces, DWARFDataExtractor apple_types, + DWARFDataExtractor apple_objc, DWARFDataExtractor debug_str) { + + llvm::DataExtractor llvm_debug_str = debug_str.GetAsLLVM(); + + auto apple_names_table_up = std::make_unique<llvm::AppleAcceleratorTable>( + apple_names.GetAsLLVMDWARF(), llvm_debug_str); + + auto apple_namespaces_table_up = + std::make_unique<llvm::AppleAcceleratorTable>( + apple_namespaces.GetAsLLVMDWARF(), llvm_debug_str); + + auto apple_types_table_up = std::make_unique<llvm::AppleAcceleratorTable>( + apple_types.GetAsLLVMDWARF(), llvm_debug_str); + + auto apple_objc_table_up = std::make_unique<llvm::AppleAcceleratorTable>( + apple_objc.GetAsLLVMDWARF(), llvm_debug_str); + + auto extract_and_check = [](auto &TablePtr) { + if (auto E = TablePtr->extract()) { + llvm::consumeError(std::move(E)); + TablePtr.reset(); + } + }; + + extract_and_check(apple_names_table_up); + extract_and_check(apple_namespaces_table_up); + extract_and_check(apple_types_table_up); + extract_and_check(apple_objc_table_up); + assert(apple_names.GetByteSize() == 0 || apple_names.GetSharedDataBuffer()); + assert(apple_namespaces.GetByteSize() == 0 || + apple_namespaces.GetSharedDataBuffer()); + assert(apple_types.GetByteSize() == 0 || apple_types.GetSharedDataBuffer()); + assert(apple_objc.GetByteSize() == 0 || apple_objc.GetSharedDataBuffer()); + + if (apple_names_table_up || apple_namespaces_table_up || + apple_types_table_up || apple_objc_table_up) + return std::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), apple_names.GetSharedDataBuffer(), + apple_namespaces.GetSharedDataBuffer(), + apple_types.GetSharedDataBuffer(), apple_objc.GetSharedDataBuffer()); + + return nullptr; +} + +/// Returns true if `tag` is a class_type of structure_type tag. +static bool IsClassOrStruct(dw_tag_t tag) { + return tag == DW_TAG_class_type || tag == DW_TAG_structure_type; +} + +/// Returns true if `entry` has an extractable DW_ATOM_qual_name_hash and it +/// matches `expected_hash`. +static bool +EntryHasMatchingQualhash(const llvm::AppleAcceleratorTable::Entry &entry, + uint32_t expected_hash) { + std::optional<llvm::DWARFFormValue> form_value = + entry.lookup(dwarf::DW_ATOM_qual_name_hash); + if (!form_value) + return false; + std::optional<uint64_t> hash = form_value->getAsUnsignedConstant(); + return hash && (*hash == expected_hash); +} + +/// Returns true if `entry` has an extractable DW_ATOM_die_tag and it matches +/// `expected_tag`. We also consider it a match if the tags are different but +/// in the set of {TAG_class_type, TAG_struct_type}. +static bool EntryHasMatchingTag(const llvm::AppleAcceleratorTable::Entry &entry, + dw_tag_t expected_tag) { + std::optional<llvm::DWARFFormValue> form_value = + entry.lookup(dwarf::DW_ATOM_die_tag); + if (!form_value) + return false; + std::optional<uint64_t> maybe_tag = form_value->getAsUnsignedConstant(); + if (!maybe_tag) + return false; + auto tag = static_cast<dw_tag_t>(*maybe_tag); + return tag == expected_tag || + (IsClassOrStruct(tag) && IsClassOrStruct(expected_tag)); +} + +/// Returns true if `entry` has an extractable DW_ATOM_type_flags and the flag +/// "DW_FLAG_type_implementation" is set. +static bool +HasImplementationFlag(const llvm::AppleAcceleratorTable::Entry &entry) { + std::optional<llvm::DWARFFormValue> form_value = + entry.lookup(dwarf::DW_ATOM_type_flags); + if (!form_value) + return false; + std::optional<uint64_t> Flags = form_value->getAsUnsignedConstant(); + return Flags && + (*Flags & llvm::dwarf::AcceleratorTable::DW_FLAG_type_implementation); +} + +void AppleDWARFIndex::SearchFor(const llvm::AppleAcceleratorTable &table, + llvm::StringRef name, + llvm::function_ref<bool(DWARFDIE die)> callback, + std::optional<dw_tag_t> search_for_tag, + std::optional<uint32_t> search_for_qualhash) { + auto converted_cb = DIERefCallback(callback, name); + for (const auto &entry : table.equal_range(name)) { + if (search_for_qualhash && + !EntryHasMatchingQualhash(entry, *search_for_qualhash)) + continue; + if (search_for_tag && !EntryHasMatchingTag(entry, *search_for_tag)) + continue; + if (!converted_cb(entry)) + break; + } +} + +void AppleDWARFIndex::GetGlobalVariables( + ConstString basename, llvm::function_ref<bool(DWARFDIE die)> callback) { + if (!m_apple_names_up) + return; + SearchFor(*m_apple_names_up, basename, callback); +} + +void AppleDWARFIndex::GetGlobalVariables( + const RegularExpression ®ex, + llvm::function_ref<bool(DWARFDIE die)> callback) { + if (!m_apple_names_up) + return; + + DIERefCallbackImpl converted_cb = DIERefCallback(callback, regex.GetText()); + + for (const auto &entry : m_apple_names_up->entries()) + if (std::optional<llvm::StringRef> name = entry.readName(); + name && Mangled(*name).NameMatches(regex)) + if (!converted_cb(entry.BaseEntry)) + return; +} + +void AppleDWARFIndex::GetGlobalVariables( + DWARFUnit &cu, llvm::function_ref<bool(DWARFDIE die)> callback) { + if (!m_apple_names_up) + return; + + const DWARFUnit &non_skeleton_cu = cu.GetNonSkeletonUnit(); + dw_offset_t lower_bound = non_skeleton_cu.GetOffset(); + dw_offset_t upper_bound = non_skeleton_cu.GetNextUnitOffset(); + auto is_in_range = [lower_bound, upper_bound](std::optional<uint32_t> val) { + return val.has_value() && *val >= lower_bound && *val < upper_bound; + }; + + DIERefCallbackImpl converted_cb = DIERefCallback(callback); + for (auto entry : m_apple_names_up->entries()) { + if (is_in_range(entry.BaseEntry.getDIESectionOffset())) + if (!converted_cb(entry.BaseEntry)) + return; + } +} + +void AppleDWARFIndex::GetObjCMethods( + ConstString class_name, llvm::function_ref<bool(DWARFDIE die)> callback) { + if (!m_apple_objc_up) + return; + SearchFor(*m_apple_objc_up, class_name, callback); +} + +void AppleDWARFIndex::GetCompleteObjCClass( + ConstString class_name, bool must_be_implementation, + llvm::function_ref<bool(DWARFDIE die)> callback) { + if (!m_apple_types_up) + return; + + llvm::SmallVector<DIERef> decl_dies; + auto converted_cb = DIERefCallback(callback, class_name); + + for (const auto &entry : m_apple_types_up->equal_range(class_name)) { + if (HasImplementationFlag(entry)) { + converted_cb(entry); + return; + } + + decl_dies.emplace_back(std::nullopt, DIERef::Section::DebugInfo, + *entry.getDIESectionOffset()); + } + + if (must_be_implementation) + return; + for (DIERef ref : decl_dies) + if (!converted_cb(ref)) + return; +} + +void AppleDWARFIndex::GetTypes( + ConstString name, llvm::function_ref<bool(DWARFDIE die)> callback) { + if (!m_apple_types_up) + return; + SearchFor(*m_apple_types_up, name, callback); +} + +void AppleDWARFIndex::GetTypes( + const DWARFDeclContext &context, + llvm::function_ref<bool(DWARFDIE die)> callback) { + if (!m_apple_types_up) + return; + + Log *log = GetLog(DWARFLog::TypeCompletion | DWARFLog::Lookups); + const bool entries_have_tag = + m_apple_types_up->containsAtomType(DW_ATOM_die_tag); + const bool entries_have_qual_hash = + m_apple_types_up->containsAtomType(DW_ATOM_qual_name_hash); + + llvm::StringRef expected_name = context[0].name; + + if (entries_have_tag && entries_have_qual_hash) { + const dw_tag_t expected_tag = context[0].tag; + const uint32_t expected_qualname_hash = + llvm::djbHash(context.GetQualifiedName()); + if (log) + m_module.LogMessage(log, "FindByNameAndTagAndQualifiedNameHash()"); + SearchFor(*m_apple_types_up, expected_name, callback, expected_tag, + expected_qualname_hash); + return; + } + + // Historically, if there are no tags, we also ignore qual_hash (why?) + if (!entries_have_tag) { + SearchFor(*m_apple_names_up, expected_name, callback); + return; + } + + // We have a tag but no qual hash. + + // When searching for a scoped type (for example, + // "std::vector<int>::const_iterator") searching for the innermost + // name alone ("const_iterator") could yield many false + // positives. By searching for the parent type ("vector<int>") + // first we can avoid extracting type DIEs from object files that + // would fail the filter anyway. + if ((context.GetSize() > 1) && IsClassOrStruct(context[1].tag)) + if (m_apple_types_up->equal_range(context[1].name).empty()) + return; + + if (log) + m_module.LogMessage(log, "FindByNameAndTag()"); + const dw_tag_t expected_tag = context[0].tag; + SearchFor(*m_apple_types_up, expected_name, callback, expected_tag); + return; +} + +void AppleDWARFIndex::GetNamespaces( + ConstString name, llvm::function_ref<bool(DWARFDIE die)> callback) { + if (!m_apple_namespaces_up) + return; + SearchFor(*m_apple_namespaces_up, name, callback); +} + +void AppleDWARFIndex::GetFunctions( + const Module::LookupInfo &lookup_info, SymbolFileDWARF &dwarf, + const CompilerDeclContext &parent_decl_ctx, + llvm::function_ref<bool(DWARFDIE die)> callback) { + if (!m_apple_names_up) + return; + + ConstString name = lookup_info.GetLookupName(); + for (const auto &entry : m_apple_names_up->equal_range(name)) { + DIERef die_ref(std::nullopt, DIERef::Section::DebugInfo, + *entry.getDIESectionOffset()); + DWARFDIE die = dwarf.GetDIE(die_ref); + if (!die) { + ReportInvalidDIERef(die_ref, name); + continue; + } + if (!ProcessFunctionDIE(lookup_info, die, parent_decl_ctx, callback)) + return; + } +} + +void AppleDWARFIndex::GetFunctions( + const RegularExpression ®ex, + llvm::function_ref<bool(DWARFDIE die)> callback) { + return GetGlobalVariables(regex, callback); +} + +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..73de75b583bd --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/AppleDWARFIndex.h @@ -0,0 +1,100 @@ +//===-- 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_SOURCE_PLUGINS_SYMBOLFILE_DWARF_APPLEDWARFINDEX_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_APPLEDWARFINDEX_H + +#include "Plugins/SymbolFile/DWARF/DWARFIndex.h" +#include "llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h" + +namespace lldb_private::plugin { +namespace dwarf { +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<llvm::AppleAcceleratorTable> apple_names, + std::unique_ptr<llvm::AppleAcceleratorTable> apple_namespaces, + std::unique_ptr<llvm::AppleAcceleratorTable> apple_types, + std::unique_ptr<llvm::AppleAcceleratorTable> apple_objc, + lldb::DataBufferSP apple_names_storage, + lldb::DataBufferSP apple_namespaces_storage, + lldb::DataBufferSP apple_types_storage, + lldb::DataBufferSP apple_objc_storage) + : 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)), + m_apple_names_storage(apple_names_storage), + m_apple_namespaces_storage(apple_namespaces_storage), + m_apple_types_storage(apple_types_storage), + m_apple_objc_storage(apple_objc_storage) {} + + void Preload() override {} + + void + GetGlobalVariables(ConstString basename, + llvm::function_ref<bool(DWARFDIE die)> callback) override; + void + GetGlobalVariables(const RegularExpression ®ex, + llvm::function_ref<bool(DWARFDIE die)> callback) override; + void + GetGlobalVariables(DWARFUnit &cu, + llvm::function_ref<bool(DWARFDIE die)> callback) override; + void GetObjCMethods(ConstString class_name, + llvm::function_ref<bool(DWARFDIE die)> callback) override; + void GetCompleteObjCClass( + ConstString class_name, bool must_be_implementation, + llvm::function_ref<bool(DWARFDIE die)> callback) override; + void GetTypes(ConstString name, + llvm::function_ref<bool(DWARFDIE die)> callback) override; + void GetTypes(const DWARFDeclContext &context, + llvm::function_ref<bool(DWARFDIE die)> callback) override; + void GetNamespaces(ConstString name, + llvm::function_ref<bool(DWARFDIE die)> callback) override; + void GetFunctions(const Module::LookupInfo &lookup_info, + SymbolFileDWARF &dwarf, + const CompilerDeclContext &parent_decl_ctx, + llvm::function_ref<bool(DWARFDIE die)> callback) override; + void GetFunctions(const RegularExpression ®ex, + llvm::function_ref<bool(DWARFDIE die)> callback) override; + + void Dump(Stream &s) override; + +private: + std::unique_ptr<llvm::AppleAcceleratorTable> m_apple_names_up; + std::unique_ptr<llvm::AppleAcceleratorTable> m_apple_namespaces_up; + std::unique_ptr<llvm::AppleAcceleratorTable> m_apple_types_up; + std::unique_ptr<llvm::AppleAcceleratorTable> m_apple_objc_up; + /// The following storage variables hold the data that the apple accelerator + /// tables tables above point to. + /// { + lldb::DataBufferSP m_apple_names_storage; + lldb::DataBufferSP m_apple_namespaces_storage; + lldb::DataBufferSP m_apple_types_storage; + lldb::DataBufferSP m_apple_objc_storage; + /// } + + /// Search for entries whose name is `name` in `table`, calling `callback` for + /// each match. If `search_for_tag` is provided, ignore entries whose tag is + /// not `search_for_tag`. If `search_for_qualhash` is provided, ignore entries + /// whose qualified name hash does not match `search_for_qualhash`. + /// If `callback` returns false for an entry, the search is interrupted. + void SearchFor(const llvm::AppleAcceleratorTable &table, llvm::StringRef name, + llvm::function_ref<bool(DWARFDIE die)> callback, + std::optional<dw_tag_t> search_for_tag = std::nullopt, + std::optional<uint32_t> search_for_qualhash = std::nullopt); +}; +} // namespace dwarf +} // namespace lldb_private::plugin + +#endif // LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_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..163e9f4c081c --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DIERef.cpp @@ -0,0 +1,39 @@ +//===-- DIERef.cpp --------------------------------------------------------===// +// +// 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 "lldb/Utility/DataEncoder.h" +#include "lldb/Utility/DataExtractor.h" +#include "llvm/Support/Format.h" +#include <optional> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::plugin::dwarf; + +void llvm::format_provider<DIERef>::format(const DIERef &ref, raw_ostream &OS, + StringRef Style) { + if (ref.file_index()) + OS << format_hex_no_prefix(*ref.file_index(), 8) << "/"; + OS << (ref.section() == DIERef::DebugInfo ? "INFO" : "TYPE"); + OS << "/" << format_hex_no_prefix(ref.die_offset(), 8); +} + +std::optional<DIERef> DIERef::Decode(const DataExtractor &data, + lldb::offset_t *offset_ptr) { + DIERef die_ref(data.GetU64(offset_ptr)); + + // DIE offsets can't be zero and if we fail to decode something from data, + // it will return 0 + if (!die_ref.die_offset()) + return std::nullopt; + + return die_ref; +} + +void DIERef::Encode(DataEncoder &encoder) const { encoder.AppendU64(get_id()); } 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..ad443aacb46e --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DIERef.h @@ -0,0 +1,146 @@ +//===-- 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 LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_DIEREF_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_DIEREF_H + +#include "lldb/Core/dwarf.h" +#include "lldb/Utility/LLDBAssert.h" +#include <cassert> +#include <optional> + +namespace lldb_private::plugin { +namespace dwarf { +/// Identifies a DWARF debug info entry within a given Module. It contains three +/// "coordinates": +/// - file_index: identifies the separate stand alone debug info file +/// that is referred to by the main debug info file. This will be the +/// index of a DWO file for fission, or the .o file on mac when not +/// using a dSYM file. If this field is not set, then this references +/// a DIE inside the original object 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(std::optional<uint32_t> file_index, Section section, + dw_offset_t die_offset) + : m_die_offset(die_offset), m_file_index(file_index.value_or(0)), + m_file_index_valid(file_index ? true : false), m_section(section) { + assert(this->file_index() == file_index && "File Index is out of range?"); + } + + explicit DIERef(lldb::user_id_t uid) { + m_die_offset = uid & k_die_offset_mask; + m_file_index_valid = (uid & k_file_index_valid_bit) != 0; + m_file_index = m_file_index_valid + ? (uid >> k_die_offset_bit_size) & k_file_index_mask + : 0; + m_section = + (uid & k_section_bit) != 0 ? Section::DebugTypes : Section::DebugInfo; + } + + lldb::user_id_t get_id() const { + if (m_die_offset == k_die_offset_mask) + return LLDB_INVALID_UID; + + return lldb::user_id_t(file_index().value_or(0)) << k_die_offset_bit_size | + die_offset() | (m_file_index_valid ? k_file_index_valid_bit : 0) | + (section() == Section::DebugTypes ? k_section_bit : 0); + } + + std::optional<uint32_t> file_index() const { + if (m_file_index_valid) + return m_file_index; + return std::nullopt; + } + + Section section() const { return static_cast<Section>(m_section); } + + dw_offset_t die_offset() const { return m_die_offset; } + + bool operator<(DIERef other) const { + if (m_file_index_valid != other.m_file_index_valid) + return m_file_index_valid < other.m_file_index_valid; + if (m_file_index_valid && (m_file_index != other.m_file_index)) + return m_file_index < other.m_file_index; + if (m_section != other.m_section) + return m_section < other.m_section; + return m_die_offset < other.m_die_offset; + } + + bool operator==(const DIERef &rhs) const { + return file_index() == rhs.file_index() && m_section == rhs.m_section && + m_die_offset == rhs.m_die_offset; + } + + bool operator!=(const DIERef &rhs) const { return !(*this == rhs); } + + /// Decode a serialized version of this object from data. + /// + /// \param data + /// The decoder object that references the serialized data. + /// + /// \param offset_ptr + /// A pointer that contains the offset from which the data will be decoded + /// from that gets updated as data gets decoded. + /// + /// \return + /// Returns a valid DIERef if decoding succeeded, std::nullopt if there was + /// unsufficient or invalid values that were decoded. + static std::optional<DIERef> Decode(const DataExtractor &data, + lldb::offset_t *offset_ptr); + + /// Encode this object into a data encoder object. + /// + /// This allows this object to be serialized to disk. + /// + /// \param encoder + /// A data encoder object that serialized bytes will be encoded into. + /// + void Encode(DataEncoder &encoder) const; + + static constexpr uint64_t k_die_offset_bit_size = DW_DIE_OFFSET_MAX_BITSIZE; + static constexpr uint64_t k_file_index_bit_size = + 64 - DW_DIE_OFFSET_MAX_BITSIZE - /* size of control bits */ 2; + + static constexpr uint64_t k_file_index_valid_bit = + (1ull << (k_file_index_bit_size + k_die_offset_bit_size)); + static constexpr uint64_t k_section_bit = + (1ull << (k_file_index_bit_size + k_die_offset_bit_size + 1)); + static constexpr uint64_t + k_file_index_mask = (~0ull) >> (64 - k_file_index_bit_size); // 0x3fffff; + static constexpr uint64_t k_die_offset_mask = (~0ull) >> + (64 - k_die_offset_bit_size); + +private: + // Allow 2TB of .debug_info/.debug_types offset + dw_offset_t m_die_offset : k_die_offset_bit_size; + // Used for DWO index or for .o file index on mac + dw_offset_t m_file_index : k_file_index_bit_size; + // Set to 1 if m_file_index is a DWO number + dw_offset_t m_file_index_valid : 1; + // Set to 0 for .debug_info 1 for .debug_types, + dw_offset_t m_section : 1; +}; +static_assert(sizeof(DIERef) == 8); + +typedef std::vector<DIERef> DIEArray; +} // namespace dwarf +} // namespace lldb_private::plugin + +namespace llvm { +template <> struct format_provider<lldb_private::plugin::dwarf::DIERef> { + static void format(const lldb_private::plugin::dwarf::DIERef &ref, + raw_ostream &OS, StringRef Style); +}; +} // namespace llvm + +#endif // LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_DIEREF_H diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParser.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParser.cpp new file mode 100644 index 000000000000..409e9bb37ab2 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParser.cpp @@ -0,0 +1,141 @@ +//===-- DWARFASTParser.cpp ------------------------------------------------===// +// +// 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 "DWARFASTParser.h" +#include "DWARFAttribute.h" +#include "DWARFDIE.h" +#include "SymbolFileDWARF.h" + +#include "lldb/Core/ValueObject.h" +#include "lldb/Symbol/SymbolFile.h" +#include "lldb/Target/StackFrame.h" +#include <optional> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::dwarf; +using namespace lldb_private::plugin::dwarf; + +std::optional<SymbolFile::ArrayInfo> +DWARFASTParser::ParseChildArrayInfo(const DWARFDIE &parent_die, + const ExecutionContext *exe_ctx) { + SymbolFile::ArrayInfo array_info; + if (!parent_die) + return std::nullopt; + + for (DWARFDIE die : parent_die.children()) { + const dw_tag_t tag = die.Tag(); + if (tag != DW_TAG_subrange_type) + continue; + + DWARFAttributes attributes = die.GetAttributes(); + if (attributes.Size() == 0) + continue; + + uint64_t num_elements = 0; + uint64_t lower_bound = 0; + uint64_t upper_bound = 0; + bool upper_bound_valid = false; + for (size_t i = 0; i < attributes.Size(); ++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: + 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); + } + return array_info; +} + +Type *DWARFASTParser::GetTypeForDIE(const DWARFDIE &die) { + if (!die) + return nullptr; + + SymbolFileDWARF *dwarf = die.GetDWARF(); + if (!dwarf) + return nullptr; + + DWARFAttributes attributes = die.GetAttributes(); + if (attributes.Size() == 0) + return nullptr; + + DWARFFormValue type_die_form; + for (size_t i = 0; i < attributes.Size(); ++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; +} + +AccessType +DWARFASTParser::GetAccessTypeFromDWARF(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; +} 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..abaeb2502cbb --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParser.h @@ -0,0 +1,79 @@ +//===-- 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 LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_DWARFASTPARSER_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_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" +#include "lldb/lldb-enumerations.h" +#include <optional> + +namespace lldb_private { +class CompileUnit; +class ExecutionContext; +} + +namespace lldb_private::plugin { +namespace dwarf { +class DWARFDIE; +class SymbolFileDWARF; + +class DWARFASTParser { +public: + enum class Kind { DWARFASTParserClang }; + DWARFASTParser(Kind kind) : m_kind(kind) {} + + virtual ~DWARFASTParser() = default; + + virtual lldb::TypeSP ParseTypeFromDWARF(const SymbolContext &sc, + const DWARFDIE &die, + bool *type_is_new_ptr) = 0; + + virtual ConstString ConstructDemangledNameFromDWARF(const DWARFDIE &die) = 0; + + virtual Function *ParseFunctionFromDWARF(CompileUnit &comp_unit, + const DWARFDIE &die, + const AddressRange &range) = 0; + + virtual bool CompleteTypeFromDWARF(const DWARFDIE &die, Type *type, + CompilerType &compiler_type) = 0; + + virtual CompilerDecl GetDeclForUIDFromDWARF(const DWARFDIE &die) = 0; + + virtual CompilerDeclContext + GetDeclContextForUIDFromDWARF(const DWARFDIE &die) = 0; + + virtual CompilerDeclContext + GetDeclContextContainingUIDFromDWARF(const DWARFDIE &die) = 0; + + virtual void EnsureAllDIEsInDeclContextHaveBeenParsed( + CompilerDeclContext decl_context) = 0; + + virtual std::string GetDIEClassTemplateParams(const DWARFDIE &die) = 0; + + static std::optional<SymbolFile::ArrayInfo> + ParseChildArrayInfo(const DWARFDIE &parent_die, + const ExecutionContext *exe_ctx = nullptr); + + lldb_private::Type *GetTypeForDIE(const DWARFDIE &die); + + static lldb::AccessType GetAccessTypeFromDWARF(uint32_t dwarf_accessibility); + + Kind GetKind() const { return m_kind; } + +private: + const Kind m_kind; +}; +} // namespace dwarf +} // namespace lldb_private::plugin + +#endif // LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_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..ac769ad9fbd5 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp @@ -0,0 +1,3862 @@ +//===-- DWARFASTParserClang.cpp -------------------------------------------===// +// +// 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 <cstdlib> + +#include "DWARFASTParser.h" +#include "DWARFASTParserClang.h" +#include "DWARFDebugInfo.h" +#include "DWARFDeclContext.h" +#include "DWARFDefines.h" +#include "SymbolFileDWARF.h" +#include "SymbolFileDWARFDebugMap.h" +#include "SymbolFileDWARFDwo.h" +#include "UniqueDWARFASTType.h" + +#include "Plugins/ExpressionParser/Clang/ClangASTImporter.h" +#include "Plugins/ExpressionParser/Clang/ClangASTMetadata.h" +#include "Plugins/ExpressionParser/Clang/ClangUtil.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/CompileUnit.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolFile.h" +#include "lldb/Symbol/TypeList.h" +#include "lldb/Symbol/TypeMap.h" +#include "lldb/Symbol/VariableList.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/DeclBase.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/AST/Type.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Demangle/Demangle.h" + +#include <map> +#include <memory> +#include <optional> +#include <vector> + +//#define ENABLE_DEBUG_PRINTF // COMMENT OUT THIS LINE PRIOR TO CHECKIN + +#ifdef ENABLE_DEBUG_PRINTF +#include <cstdio> +#define DEBUG_PRINTF(fmt, ...) printf(fmt, __VA_ARGS__) +#else +#define DEBUG_PRINTF(fmt, ...) +#endif + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::dwarf; +using namespace lldb_private::plugin::dwarf; + +DWARFASTParserClang::DWARFASTParserClang(TypeSystemClang &ast) + : DWARFASTParser(Kind::DWARFASTParserClang), m_ast(ast), + m_die_to_decl_ctx(), m_decl_ctx_to_die() {} + +DWARFASTParserClang::~DWARFASTParserClang() = default; + +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; +} + + +ClangASTImporter &DWARFASTParserClang::GetClangASTImporter() { + if (!m_clang_ast_importer_up) { + m_clang_ast_importer_up = std::make_unique<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; +} + +static DWARFDIE GetContainingClangModuleDIE(const DWARFDIE &die) { + if (die.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 = die.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(); +} + +static lldb::ModuleSP GetContainingClangModule(const DWARFDIE &die) { + if (die.IsValid()) { + DWARFDIE clang_module_die = GetContainingClangModuleDIE(die); + + if (clang_module_die) { + const char *module_name = clang_module_die.GetName(); + if (module_name) + return die.GetDWARF()->GetExternalModule( + lldb_private::ConstString(module_name)); + } + } + return lldb::ModuleSP(); +} + +// Returns true if the given artificial field name should be ignored when +// parsing the DWARF. +static bool ShouldIgnoreArtificialField(llvm::StringRef FieldName) { + return FieldName.starts_with("_vptr$") + // gdb emit vtable pointer as "_vptr.classname" + || FieldName.starts_with("_vptr."); +} + +/// Returns true for C++ constructs represented by clang::CXXRecordDecl +static bool TagIsRecordType(dw_tag_t tag) { + switch (tag) { + case DW_TAG_class_type: + case DW_TAG_structure_type: + case DW_TAG_union_type: + return true; + default: + return false; + } +} + +TypeSP DWARFASTParserClang::ParseTypeFromClangModule(const SymbolContext &sc, + const DWARFDIE &die, + Log *log) { + ModuleSP clang_module_sp = GetContainingClangModule(die); + if (!clang_module_sp) + return TypeSP(); + + // If this type comes from a Clang module, recursively 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<lldb_private::CompilerContext> die_context = die.GetDeclContext(); + TypeQuery query(die_context, TypeQueryOptions::e_module_search | + TypeQueryOptions::e_find_one); + TypeResults results; + + // The type in the Clang module must have the same language as the current CU. + query.AddLanguage(SymbolFileDWARF::GetLanguageFamily(*die.GetCU())); + clang_module_sp->FindTypes(query, results); + TypeSP pcm_type_sp = results.GetTypeMap().FirstType(); + if (!pcm_type_sp) { + // Since this type is defined in one of the Clang modules imported + // by this symbol file, search all of them. Instead of calling + // sym_file->FindTypes(), which would return this again, go straight + // to the imported modules. + auto &sym_file = die.GetCU()->GetSymbolFileDWARF(); + + // Well-formed clang modules never form cycles; guard against corrupted + // ones by inserting the current file. + results.AlreadySearched(&sym_file); + sym_file.ForEachExternalModule( + *sc.comp_unit, results.GetSearchedSymbolFiles(), [&](Module &module) { + module.FindTypes(query, results); + pcm_type_sp = results.GetTypeMap().FirstType(); + return (bool)pcm_type_sp; + }); + } + + if (!pcm_type_sp) + 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. + lldb_private::CompilerType pcm_type = pcm_type_sp->GetForwardCompilerType(); + lldb_private::CompilerType type = + GetClangASTImporter().CopyType(m_ast, pcm_type); + + if (!type) + return TypeSP(); + + // Under normal operation pcm_type is a shallow forward declaration + // that gets completed later. This is necessary to support cyclic + // data structures. If, however, pcm_type is already complete (for + // example, because it was loaded for a different target before), + // the definition needs to be imported right away, too. + // Type::ResolveClangType() effectively ignores the ResolveState + // inside type_sp and only looks at IsDefined(), so it never calls + // ClangASTImporter::ASTImporterDelegate::ImportDefinitionTo(), + // which does extra work for Objective-C classes. This would result + // in only the forward declaration to be visible. + if (pcm_type.IsDefined()) + GetClangASTImporter().RequireCompleteType(ClangUtil::GetQualType(type)); + + SymbolFileDWARF *dwarf = die.GetDWARF(); + auto type_sp = dwarf->MakeType( + die.GetID(), pcm_type_sp->GetName(), pcm_type_sp->GetByteSize(nullptr), + nullptr, LLDB_INVALID_UID, Type::eEncodingInvalid, + &pcm_type_sp->GetDeclaration(), type, Type::ResolveState::Forward, + TypePayloadClang(GetOwningClangModule(die))); + clang::TagDecl *tag_decl = TypeSystemClang::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; +} + +/// This function ensures we are able to add members (nested types, functions, +/// etc.) to this type. It does so by starting its definition even if one cannot +/// be found in the debug info. This means the type may need to be "forcibly +/// completed" later -- see CompleteTypeFromDWARF). +static void PrepareContextToReceiveMembers(TypeSystemClang &ast, + 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; // Non-tag context are always ready. + + // We have already completed the type or it is already prepared. + if (tag_decl_ctx->isCompleteDefinition() || tag_decl_ctx->isBeingDefined()) + return; + + // If this tag was imported from another AST context (in the gmodules case), + // we can complete the type by doing a full import. + + // If this type was not imported from an external AST, there's nothing to do. + CompilerType type = ast.GetTypeForDecl(tag_decl_ctx); + if (type && ast_importer.CanImport(type)) { + auto qual_type = ClangUtil::GetQualType(type); + if (ast_importer.RequireCompleteType(qual_type)) + return; + die.GetDWARF()->GetObjectFile()->GetModule()->ReportError( + "Unable to complete the Decl context for DIE {0} at offset " + "{1:x16}.\nPlease file a bug report.", + type_name_cstr ? type_name_cstr : "", die.GetOffset()); + } + + // We don't have a type definition and/or the import failed, but we need to + // add members to it. Start the definition to make that possible. If the type + // has no external storage we also have to complete the definition. Otherwise, + // that will happen when we are asked to complete the type + // (CompleteTypeFromDWARF). + ast.StartTagDeclarationDefinition(type); + if (!tag_decl_ctx->hasExternalLexicalStorage()) { + ast.SetDeclIsForcefullyCompleted(tag_decl_ctx); + ast.CompleteTagDeclarationDefinition(type); + } +} + +ParsedDWARFTypeAttributes::ParsedDWARFTypeAttributes(const DWARFDIE &die) { + DWARFAttributes attributes = die.GetAttributes(); + for (size_t i = 0; i < attributes.Size(); ++i) { + dw_attr_t attr = attributes.AttributeAtIndex(i); + DWARFFormValue form_value; + if (!attributes.ExtractFormValueAtIndex(i, form_value)) + continue; + switch (attr) { + default: + break; + case DW_AT_abstract_origin: + abstract_origin = form_value; + break; + + case DW_AT_accessibility: + accessibility = + DWARFASTParser::GetAccessTypeFromDWARF(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_alignment: + alignment = 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: + // die.GetCU() can differ if DW_AT_specification uses DW_FORM_ref_addr. + decl.SetFile( + attributes.CompileUnitAtIndex(i)->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_objc_direct: + is_objc_direct_call = true; + 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; + case DW_AT_export_symbols: + exports_symbols = form_value.Boolean(); + break; + case DW_AT_rvalue_reference: + ref_qual = clang::RQ_RValue; + break; + case DW_AT_reference: + ref_qual = clang::RQ_LValue; + 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, + bool *type_is_new_ptr) { + if (type_is_new_ptr) + *type_is_new_ptr = false; + + if (!die) + return nullptr; + + Log *log = GetLog(DWARFLog::TypeCompletion | DWARFLog::Lookups); + + SymbolFileDWARF *dwarf = die.GetDWARF(); + if (log) { + DWARFDIE context_die; + clang::DeclContext *context = + GetClangDeclContextContainingDIE(die, &context_die); + + dwarf->GetObjectFile()->GetModule()->LogMessage( + log, + "DWARFASTParserClang::ParseTypeFromDWARF " + "(die = {0:x16}, decl_ctx = {1:p} (die " + "{2:x16})) {3} ({4}) name = '{5}')", + die.GetOffset(), static_cast<void *>(context), context_die.GetOffset(), + DW_TAG_value_to_name(die.Tag()), die.Tag(), die.GetName()); + } + + // Set a bit that lets us know that we are currently parsing this + if (auto [it, inserted] = + dwarf->GetDIEToType().try_emplace(die.GetDIE(), DIE_IS_BEING_PARSED); + !inserted) { + if (it->getSecond() == nullptr || it->getSecond() == DIE_IS_BEING_PARSED) + return nullptr; + return it->getSecond()->shared_from_this(); + } + + ParsedDWARFTypeAttributes attrs(die); + + TypeSP type_sp; + if (DWARFDIE signature_die = attrs.signature.Reference()) { + type_sp = ParseTypeFromDWARF(sc, signature_die, type_is_new_ptr); + if (type_sp) { + if (clang::DeclContext *decl_ctx = + GetCachedClangDeclContextForDIE(signature_die)) + LinkDeclContextToDIE(decl_ctx, die); + } + } else { + if (type_is_new_ptr) + *type_is_new_ptr = true; + + const dw_tag_t tag = die.Tag(); + + 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_LLVM_ptrauth_type: + case DW_TAG_atomic_type: + case DW_TAG_unspecified_type: + type_sp = ParseTypeModifier(sc, die, attrs); + break; + case DW_TAG_structure_type: + case DW_TAG_union_type: + case DW_TAG_class_type: + type_sp = ParseStructureLikeDIE(sc, die, attrs); + break; + case DW_TAG_enumeration_type: + type_sp = ParseEnum(sc, die, attrs); + break; + case DW_TAG_inlined_subroutine: + case DW_TAG_subprogram: + case DW_TAG_subroutine_type: + type_sp = ParseSubroutine(die, attrs); + break; + case DW_TAG_array_type: + type_sp = ParseArrayType(die, attrs); + break; + case DW_TAG_ptr_to_member_type: + type_sp = ParsePointerToMemberType(die, attrs); + break; + default: + dwarf->GetObjectFile()->GetModule()->ReportError( + "[{0:x16}]: unhandled type tag {1:x4} ({2}), " + "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; + } + UpdateSymbolContextScopeForType(sc, die, type_sp); + } + if (type_sp) { + dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get(); + } + return type_sp; +} + +static std::optional<uint32_t> +ExtractDataMemberLocation(DWARFDIE const &die, DWARFFormValue const &form_value, + ModuleSP module_sp) { + Log *log = GetLog(DWARFLog::TypeCompletion | DWARFLog::Lookups); + + // 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. + if (!form_value.BlockData()) + return form_value.Unsigned(); + + Value initialValue(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(); + + llvm::Expected<Value> memberOffset = DWARFExpression::Evaluate( + /*ExecutionContext=*/nullptr, + /*RegisterContext=*/nullptr, module_sp, + DataExtractor(debug_info_data, block_offset, block_length), die.GetCU(), + eRegisterKindDWARF, &initialValue, nullptr); + if (!memberOffset) { + LLDB_LOG_ERROR(log, memberOffset.takeError(), + "ExtractDataMemberLocation failed: {0}"); + return {}; + } + + return memberOffset->ResolveValue(nullptr).UInt(); +} + +static TypePayloadClang GetPtrAuthMofidierPayload(const DWARFDIE &die) { + auto getAttr = [&](llvm::dwarf::Attribute Attr, unsigned defaultValue = 0) { + return die.GetAttributeValueAsUnsigned(Attr, defaultValue); + }; + const unsigned key = getAttr(DW_AT_LLVM_ptrauth_key); + const bool addr_disc = getAttr(DW_AT_LLVM_ptrauth_address_discriminated); + const unsigned extra = getAttr(DW_AT_LLVM_ptrauth_extra_discriminator); + const bool isapointer = getAttr(DW_AT_LLVM_ptrauth_isa_pointer); + const bool authenticates_null_values = + getAttr(DW_AT_LLVM_ptrauth_authenticates_null_values); + const unsigned authentication_mode_int = getAttr( + DW_AT_LLVM_ptrauth_authentication_mode, + static_cast<unsigned>(clang::PointerAuthenticationMode::SignAndAuth)); + clang::PointerAuthenticationMode authentication_mode = + clang::PointerAuthenticationMode::SignAndAuth; + if (authentication_mode_int >= + static_cast<unsigned>(clang::PointerAuthenticationMode::None) && + authentication_mode_int <= + static_cast<unsigned>( + clang::PointerAuthenticationMode::SignAndAuth)) { + authentication_mode = + static_cast<clang::PointerAuthenticationMode>(authentication_mode_int); + } else { + die.GetDWARF()->GetObjectFile()->GetModule()->ReportError( + "[{0:x16}]: invalid pointer authentication mode method {1:x4}", + die.GetOffset(), authentication_mode_int); + } + auto ptr_auth = clang::PointerAuthQualifier::Create( + key, addr_disc, extra, authentication_mode, isapointer, + authenticates_null_values); + return TypePayloadClang(ptr_auth.getAsOpaqueValue()); +} + +lldb::TypeSP +DWARFASTParserClang::ParseTypeModifier(const SymbolContext &sc, + const DWARFDIE &die, + ParsedDWARFTypeAttributes &attrs) { + Log *log = GetLog(DWARFLog::TypeCompletion | DWARFLog::Lookups); + SymbolFileDWARF *dwarf = die.GetDWARF(); + const dw_tag_t tag = die.Tag(); + LanguageType cu_language = SymbolFileDWARF::GetLanguage(*die.GetCU()); + Type::ResolveState resolve_state = Type::ResolveState::Unresolved; + Type::EncodingDataType encoding_data_type = Type::eEncodingIsUID; + TypePayloadClang payload(GetOwningClangModule(die)); + TypeSP type_sp; + CompilerType clang_type; + + if (tag == DW_TAG_typedef) { + // DeclContext will be populated when the clang type is materialized in + // Type::ResolveCompilerType. + PrepareContextToReceiveMembers( + m_ast, GetClangASTImporter(), + GetClangDeclContextContainingDIE(die, nullptr), die, + attrs.name.GetCString()); + + if (attrs.type.IsValid()) { + // Try to parse a typedef from the (DWARF embedded in the) Clang + // module 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 Clang modules + // if possible. + // + // The type_sp returned will be empty if the typedef doesn't + // exist in a module 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 debug info, so we make + // sure we don't get into this situation by always resolving + // typedefs from the module. + 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 = ParseTypeFromClangModule(sc, 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::ResolveState::Full; + clang_type = m_ast.GetBasicType(eBasicTypeNullPtr); + break; + } + // Fall through to base type below in case we can handle the type + // there... + [[fallthrough]]; + + case DW_TAG_base_type: + resolve_state = Type::ResolveState::Full; + clang_type = m_ast.GetBuiltinTypeForDWARFEncodingAndBitSize( + attrs.name.GetStringRef(), attrs.encoding, + attrs.byte_size.value_or(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; + case DW_TAG_LLVM_ptrauth_type: + encoding_data_type = Type::eEncodingIsLLVMPtrAuthUID; + payload = GetPtrAuthMofidierPayload(die); + break; + case DW_TAG_atomic_type: + encoding_data_type = Type::eEncodingIsAtomicUID; + 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.children()) { + 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, &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::ResolveState::Full; + } + } + + break; + } + } + } + } + + if (cu_language == eLanguageTypeObjC || + cu_language == eLanguageTypeObjC_plus_plus) { + if (attrs.name) { + if (attrs.name == "id") { + if (log) + dwarf->GetObjectFile()->GetModule()->LogMessage( + log, + "SymbolFileDWARF::ParseType (die = {0:x16}) {1} ({2}) '{3}' " + "is Objective-C 'id' built-in type.", + die.GetOffset(), DW_TAG_value_to_name(die.Tag()), die.Tag(), + die.GetName()); + clang_type = m_ast.GetBasicType(eBasicTypeObjCID); + encoding_data_type = Type::eEncodingIsUID; + attrs.type.Clear(); + resolve_state = Type::ResolveState::Full; + } else if (attrs.name == "Class") { + if (log) + dwarf->GetObjectFile()->GetModule()->LogMessage( + log, + "SymbolFileDWARF::ParseType (die = {0:x16}) {1} ({2}) '{3}' " + "is Objective-C 'Class' built-in type.", + die.GetOffset(), DW_TAG_value_to_name(die.Tag()), die.Tag(), + die.GetName()); + clang_type = m_ast.GetBasicType(eBasicTypeObjCClass); + encoding_data_type = Type::eEncodingIsUID; + attrs.type.Clear(); + resolve_state = Type::ResolveState::Full; + } else if (attrs.name == "SEL") { + if (log) + dwarf->GetObjectFile()->GetModule()->LogMessage( + log, + "SymbolFileDWARF::ParseType (die = {0:x16}) {1} ({2}) '{3}' " + "is Objective-C 'selector' built-in type.", + die.GetOffset(), DW_TAG_value_to_name(die.Tag()), die.Tag(), + die.GetName()); + clang_type = m_ast.GetBasicType(eBasicTypeObjCSel); + encoding_data_type = Type::eEncodingIsUID; + attrs.type.Clear(); + resolve_state = Type::ResolveState::Full; + } + } 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) { + llvm::StringRef struct_name = encoding_die.GetName(); + if (struct_name == "objc_object") { + if (log) + dwarf->GetObjectFile()->GetModule()->LogMessage( + log, + "SymbolFileDWARF::ParseType (die = {0:x16}) {1} ({2}) '{3}' " + "is 'objc_object*', which we overrode to 'id'.", + die.GetOffset(), DW_TAG_value_to_name(die.Tag()), die.Tag(), + die.GetName()); + clang_type = m_ast.GetBasicType(eBasicTypeObjCID); + encoding_data_type = Type::eEncodingIsUID; + attrs.type.Clear(); + resolve_state = Type::ResolveState::Full; + } + } + } + } + } + + return dwarf->MakeType(die.GetID(), attrs.name, attrs.byte_size, nullptr, + attrs.type.Reference().GetID(), encoding_data_type, + &attrs.decl, clang_type, resolve_state, payload); +} + +std::string +DWARFASTParserClang::GetDIEClassTemplateParams(const DWARFDIE &die) { + if (llvm::StringRef(die.GetName()).contains("<")) + return {}; + + TypeSystemClang::TemplateParameterInfos template_param_infos; + if (ParseTemplateParameterInfos(die, template_param_infos)) + return m_ast.PrintTemplateParams(template_param_infos); + + return {}; +} + +void DWARFASTParserClang::MapDeclDIEToDefDIE( + const lldb_private::plugin::dwarf::DWARFDIE &decl_die, + const lldb_private::plugin::dwarf::DWARFDIE &def_die) { + LinkDeclContextToDIE(GetCachedClangDeclContextForDIE(decl_die), def_die); + SymbolFileDWARF *dwarf = def_die.GetDWARF(); + ParsedDWARFTypeAttributes decl_attrs(decl_die); + ParsedDWARFTypeAttributes def_attrs(def_die); + ConstString unique_typename(decl_attrs.name); + Declaration decl_declaration(decl_attrs.decl); + GetUniqueTypeNameAndDeclaration( + decl_die, SymbolFileDWARF::GetLanguage(*decl_die.GetCU()), + unique_typename, decl_declaration); + if (UniqueDWARFASTType *unique_ast_entry_type = + dwarf->GetUniqueDWARFASTTypeMap().Find( + unique_typename, decl_die, decl_declaration, + decl_attrs.byte_size.value_or(0), + decl_attrs.is_forward_declaration)) { + unique_ast_entry_type->UpdateToDefDIE(def_die, def_attrs.decl, + def_attrs.byte_size.value_or(0)); + } else if (Log *log = GetLog(DWARFLog::TypeCompletion | DWARFLog::Lookups)) { + const dw_tag_t tag = decl_die.Tag(); + LLDB_LOG(log, + "Failed to find {0:x16} {1} ({2}) type \"{3}\" in " + "UniqueDWARFASTTypeMap", + decl_die.GetID(), DW_TAG_value_to_name(tag), tag, unique_typename); + } +} + +TypeSP DWARFASTParserClang::ParseEnum(const SymbolContext &sc, + const DWARFDIE &decl_die, + ParsedDWARFTypeAttributes &attrs) { + Log *log = GetLog(DWARFLog::TypeCompletion | DWARFLog::Lookups); + SymbolFileDWARF *dwarf = decl_die.GetDWARF(); + const dw_tag_t tag = decl_die.Tag(); + + DWARFDIE def_die; + if (attrs.is_forward_declaration) { + if (TypeSP type_sp = ParseTypeFromClangModule(sc, decl_die, log)) + return type_sp; + + def_die = dwarf->FindDefinitionDIE(decl_die); + + if (!def_die) { + 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... + def_die = debug_map_symfile->FindDefinitionDIE(decl_die); + } + } + + if (log) { + dwarf->GetObjectFile()->GetModule()->LogMessage( + log, + "SymbolFileDWARF({0:p}) - {1:x16}}: {2} ({3}) type \"{4}\" is a " + "forward declaration, complete DIE is {5}", + static_cast<void *>(this), decl_die.GetID(), DW_TAG_value_to_name(tag), + tag, attrs.name.GetCString(), + def_die ? llvm::utohexstr(def_die.GetID()) : "not found"); + } + } + if (def_die) { + if (auto [it, inserted] = dwarf->GetDIEToType().try_emplace( + def_die.GetDIE(), DIE_IS_BEING_PARSED); + !inserted) { + if (it->getSecond() == nullptr || it->getSecond() == DIE_IS_BEING_PARSED) + return nullptr; + return it->getSecond()->shared_from_this(); + } + attrs = ParsedDWARFTypeAttributes(def_die); + } else { + // No definition found. Proceed with the declaration die. We can use it to + // create a forward-declared type. + def_die = decl_die; + } + + CompilerType enumerator_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( + "", DW_ATE_signed, *attrs.byte_size * 8); + } else { + enumerator_clang_type = m_ast.GetBasicType(eBasicTypeInt); + } + } + + CompilerType clang_type = m_ast.CreateEnumerationType( + attrs.name.GetStringRef(), GetClangDeclContextContainingDIE(def_die, nullptr), + GetOwningClangModule(def_die), attrs.decl, enumerator_clang_type, + attrs.is_scoped_enum); + TypeSP type_sp = + dwarf->MakeType(def_die.GetID(), attrs.name, attrs.byte_size, nullptr, + attrs.type.Reference().GetID(), Type::eEncodingIsUID, + &attrs.decl, clang_type, Type::ResolveState::Forward, + TypePayloadClang(GetOwningClangModule(def_die))); + + clang::DeclContext *type_decl_ctx = + TypeSystemClang::GetDeclContextForType(clang_type); + LinkDeclContextToDIE(type_decl_ctx, decl_die); + if (decl_die != def_die) { + LinkDeclContextToDIE(type_decl_ctx, def_die); + dwarf->GetDIEToType()[def_die.GetDIE()] = type_sp.get(); + // Declaration DIE is inserted into the type map in ParseTypeFromDWARF + } + + + if (TypeSystemClang::StartTagDeclarationDefinition(clang_type)) { + if (def_die.HasChildren()) { + bool is_signed = false; + enumerator_clang_type.IsIntegerType(is_signed); + ParseChildEnumerators(clang_type, is_signed, + type_sp->GetByteSize(nullptr).value_or(0), def_die); + } + TypeSystemClang::CompleteTagDeclarationDefinition(clang_type); + } else { + dwarf->GetObjectFile()->GetModule()->ReportError( + "DWARF DIE at {0:x16} named \"{1}\" was not able to start its " + "definition.\nPlease file a bug and attach the file at the " + "start of this error message", + def_die.GetOffset(), attrs.name.GetCString()); + } + return type_sp; +} + +static clang::CallingConv +ConvertDWARFCallingConventionToClang(const ParsedDWARFTypeAttributes &attrs) { + switch (attrs.calling_convention) { + case llvm::dwarf::DW_CC_normal: + return clang::CC_C; + case llvm::dwarf::DW_CC_BORLAND_stdcall: + return clang::CC_X86StdCall; + case llvm::dwarf::DW_CC_BORLAND_msfastcall: + return clang::CC_X86FastCall; + case llvm::dwarf::DW_CC_LLVM_vectorcall: + return clang::CC_X86VectorCall; + case llvm::dwarf::DW_CC_BORLAND_pascal: + return clang::CC_X86Pascal; + case llvm::dwarf::DW_CC_LLVM_Win64: + return clang::CC_Win64; + case llvm::dwarf::DW_CC_LLVM_X86_64SysV: + return clang::CC_X86_64SysV; + case llvm::dwarf::DW_CC_LLVM_X86RegCall: + return clang::CC_X86RegCall; + default: + break; + } + + Log *log = GetLog(DWARFLog::TypeCompletion | DWARFLog::Lookups); + LLDB_LOG(log, "Unsupported DW_AT_calling_convention value: {0}", + attrs.calling_convention); + // Use the default calling convention as a fallback. + return clang::CC_C; +} + +bool DWARFASTParserClang::ParseObjCMethod( + const ObjCLanguage::MethodName &objc_method, const DWARFDIE &die, + CompilerType clang_type, const ParsedDWARFTypeAttributes &attrs, + bool is_variadic) { + SymbolFileDWARF *dwarf = die.GetDWARF(); + assert(dwarf); + + const auto tag = die.Tag(); + ConstString class_name(objc_method.GetClassName()); + if (!class_name) + return false; + + TypeSP complete_objc_class_type_sp = + dwarf->FindCompleteObjCDefinitionTypeForDIE(DWARFDIE(), class_name, + false); + + if (!complete_objc_class_type_sp) + return false; + + CompilerType type_clang_forward_type = + complete_objc_class_type_sp->GetForwardCompilerType(); + + if (!type_clang_forward_type) + return false; + + if (!TypeSystemClang::IsObjCObjectOrInterfaceType(type_clang_forward_type)) + return false; + + clang::ObjCMethodDecl *objc_method_decl = m_ast.AddMethodToObjCObjectType( + type_clang_forward_type, attrs.name.GetCString(), clang_type, + attrs.is_artificial, is_variadic, attrs.is_objc_direct_call); + + if (!objc_method_decl) { + dwarf->GetObjectFile()->GetModule()->ReportError( + "[{0:x16}]: invalid Objective-C method {1:x4} ({2}), " + "please file a bug and attach the file at the start of " + "this error message", + die.GetOffset(), tag, DW_TAG_value_to_name(tag)); + return false; + } + + LinkDeclContextToDIE(objc_method_decl, die); + m_ast.SetMetadataAsUserID(objc_method_decl, die.GetID()); + + return true; +} + +std::pair<bool, TypeSP> DWARFASTParserClang::ParseCXXMethod( + const DWARFDIE &die, CompilerType clang_type, + const ParsedDWARFTypeAttributes &attrs, const DWARFDIE &decl_ctx_die, + bool is_static, bool &ignore_containing_context) { + Log *log = GetLog(DWARFLog::TypeCompletion | DWARFLog::Lookups); + SymbolFileDWARF *dwarf = die.GetDWARF(); + assert(dwarf); + + Type *class_type = dwarf->ResolveType(decl_ctx_die); + if (!class_type) + return {}; + + if (class_type->GetID() != decl_ctx_die.GetID() || + IsClangModuleFwdDecl(decl_ctx_die)) { + + // 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"... + if (DWARFDIE class_type_die = dwarf->GetDIE(class_type->GetID())) { + 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 *type_ptr = dwarf->GetDIEToType().lookup(die.GetDIE()); + if (type_ptr && type_ptr != DIE_IS_BEING_PARSED) + return {true, type_ptr->shared_from_this()}; + } + } + + 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( + "{0:x8}: DW_AT_specification({1:x16}" + ") has no decl\n", + die.GetID(), spec_die.GetOffset()); + + return {true, nullptr}; + } + + 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( + "{0:x8}: DW_AT_abstract_origin({1:x16}" + ") has no decl\n", + die.GetID(), abs_die.GetOffset()); + + return {true, nullptr}; + } + + CompilerType class_opaque_type = class_type->GetForwardCompilerType(); + if (!TypeSystemClang::IsCXXClassType(class_opaque_type)) + return {}; + + PrepareContextToReceiveMembers( + m_ast, GetClangASTImporter(), + TypeSystemClang::GetDeclContextForType(class_opaque_type), die, + attrs.name.GetCString()); + + // 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... + if (!is_static && !die.HasChildren()) + return {true, nullptr}; + + 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... + const auto accessibility = + attrs.accessibility == eAccessNone ? eAccessPublic : attrs.accessibility; + + clang::CXXMethodDecl *cxx_method_decl = m_ast.AddMethodToCXXRecordType( + class_opaque_type.GetOpaqueQualType(), attrs.name.GetCString(), + attrs.mangled_name, clang_type, accessibility, attrs.is_virtual, + is_static, attrs.is_inline, attrs.is_explicit, is_attr_used, + attrs.is_artificial); + + if (cxx_method_decl) { + LinkDeclContextToDIE(cxx_method_decl, die); + + ClangASTMetadata metadata; + metadata.SetUserID(die.GetID()); + + char const *object_pointer_name = + attrs.object_pointer ? attrs.object_pointer.GetName() : nullptr; + if (object_pointer_name) { + metadata.SetObjectPtrName(object_pointer_name); + LLDB_LOGF(log, "Setting object pointer name: %s on method object %p.\n", + object_pointer_name, static_cast<void *>(cxx_method_decl)); + } + m_ast.SetMetadata(cxx_method_decl, metadata); + } else { + ignore_containing_context = true; + } + + // Artificial methods are always handled even when we + // don't create a new declaration for them. + const bool type_handled = cxx_method_decl != nullptr || attrs.is_artificial; + + return {type_handled, nullptr}; +} + +TypeSP +DWARFASTParserClang::ParseSubroutine(const DWARFDIE &die, + const ParsedDWARFTypeAttributes &attrs) { + Log *log = GetLog(DWARFLog::TypeCompletion | DWARFLog::Lookups); + + SymbolFileDWARF *dwarf = die.GetDWARF(); + const dw_tag_t tag = die.Tag(); + + bool is_variadic = false; + bool is_static = false; + bool has_template_params = false; + + unsigned type_quals = 0; + + 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 = nullptr; + + 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::CallingConv calling_convention = + ConvertDWARFCallingConventionToClang(attrs); + + // clang_type will get the function prototype clang type after this + // call + CompilerType clang_type = + m_ast.CreateFunctionType(return_clang_type, function_param_types.data(), + function_param_types.size(), is_variadic, + type_quals, calling_convention, attrs.ref_qual); + + if (attrs.name) { + bool type_handled = false; + if (tag == DW_TAG_subprogram || tag == DW_TAG_inlined_subroutine) { + if (std::optional<const ObjCLanguage::MethodName> objc_method = + ObjCLanguage::MethodName::Create(attrs.name.GetStringRef(), + true)) { + type_handled = + ParseObjCMethod(*objc_method, die, clang_type, attrs, is_variadic); + } else if (is_cxx_method) { + auto [handled, type_sp] = + ParseCXXMethod(die, clang_type, attrs, decl_ctx_die, is_static, + ignore_containing_context); + if (type_sp) + return type_sp; + + type_handled = handled; + } + } + + 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) { + char *name_buf = nullptr; + llvm::StringRef name = attrs.name.GetStringRef(); + + // We currently generate function templates with template parameters in + // their name. In order to get closer to the AST that clang generates + // we want to strip these from the name when creating the AST. + if (attrs.mangled_name) { + llvm::ItaniumPartialDemangler D; + if (!D.partialDemangle(attrs.mangled_name)) { + name_buf = D.getFunctionBaseName(nullptr, nullptr); + name = name_buf; + } + } + + // 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, + GetOwningClangModule(die), name, clang_type, attrs.storage, + attrs.is_inline); + std::free(name_buf); + + if (has_template_params) { + TypeSystemClang::TemplateParameterInfos template_param_infos; + ParseTemplateParameterInfos(die, template_param_infos); + template_function_decl = m_ast.CreateFunctionDeclaration( + ignore_containing_context ? m_ast.GetTranslationUnitDecl() + : containing_decl_ctx, + GetOwningClangModule(die), attrs.name.GetStringRef(), clang_type, + attrs.storage, attrs.is_inline); + clang::FunctionTemplateDecl *func_template_decl = + m_ast.CreateFunctionTemplateDecl( + containing_decl_ctx, GetOwningClangModule(die), + template_function_decl, template_param_infos); + m_ast.CreateFunctionTemplateSpecializationInfo( + template_function_decl, func_template_decl, template_param_infos); + } + + lldbassert(function_decl); + + if (function_decl) { + // Attach an asm(<mangled_name>) label to the FunctionDecl. + // This ensures that clang::CodeGen emits function calls + // using symbols that are mangled according to the DW_AT_linkage_name. + // If we didn't do this, the external symbols wouldn't exactly + // match the mangled name LLDB knows about and the IRExecutionUnit + // would have to fall back to searching object files for + // approximately matching function names. The motivating + // example is generating calls to ABI-tagged template functions. + // This is done separately for member functions in + // AddMethodToCXXRecordType. + if (attrs.mangled_name) + function_decl->addAttr(clang::AsmLabelAttr::CreateImplicit( + m_ast.getASTContext(), attrs.mangled_name, /*literal=*/false)); + + LinkDeclContextToDIE(function_decl, die); + + if (!function_param_decls.empty()) { + m_ast.SetFunctionParameters(function_decl, function_param_decls); + if (template_function_decl) + m_ast.SetFunctionParameters(template_function_decl, + function_param_decls); + } + + ClangASTMetadata metadata; + metadata.SetUserID(die.GetID()); + + char const *object_pointer_name = + attrs.object_pointer ? attrs.object_pointer.GetName() : nullptr; + if (object_pointer_name) { + metadata.SetObjectPtrName(object_pointer_name); + LLDB_LOGF(log, + "Setting object pointer name: %s on function " + "object %p.", + object_pointer_name, static_cast<void *>(function_decl)); + } + m_ast.SetMetadata(function_decl, metadata); + } + } + } + } + return dwarf->MakeType( + die.GetID(), attrs.name, std::nullopt, nullptr, LLDB_INVALID_UID, + Type::eEncodingIsUID, &attrs.decl, clang_type, Type::ResolveState::Full); +} + +TypeSP +DWARFASTParserClang::ParseArrayType(const DWARFDIE &die, + const ParsedDWARFTypeAttributes &attrs) { + SymbolFileDWARF *dwarf = die.GetDWARF(); + + 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) + return nullptr; + + std::optional<SymbolFile::ArrayInfo> array_info = ParseChildArrayInfo(die); + uint32_t byte_stride = attrs.byte_stride; + uint32_t bit_stride = attrs.bit_stride; + if (array_info) { + byte_stride = array_info->byte_stride; + bit_stride = array_info->bit_stride; + } + if (byte_stride == 0 && bit_stride == 0) + byte_stride = element_type->GetByteSize(nullptr).value_or(0); + CompilerType array_element_type = element_type->GetForwardCompilerType(); + TypeSystemClang::RequireCompleteType(array_element_type); + + uint64_t array_element_bit_stride = byte_stride * 8 + bit_stride; + CompilerType clang_type; + 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; + TypeSP type_sp = + dwarf->MakeType(die.GetID(), empty_name, array_element_bit_stride / 8, + nullptr, type_die.GetID(), Type::eEncodingIsUID, + &attrs.decl, clang_type, Type::ResolveState::Full); + type_sp->SetEncodingType(element_type); + const clang::Type *type = ClangUtil::GetQualType(clang_type).getTypePtr(); + m_ast.SetMetadataAsUserID(type, die.GetID()); + return type_sp; +} + +TypeSP DWARFASTParserClang::ParsePointerToMemberType( + const DWARFDIE &die, const ParsedDWARFTypeAttributes &attrs) { + SymbolFileDWARF *dwarf = die.GetDWARF(); + Type *pointee_type = dwarf->ResolveTypeUID(attrs.type.Reference(), true); + Type *class_type = + dwarf->ResolveTypeUID(attrs.containing_type.Reference(), true); + + // Check to make sure pointers are not NULL before attempting to + // dereference them. + if ((class_type == nullptr) || (pointee_type == nullptr)) + return nullptr; + + CompilerType pointee_clang_type = pointee_type->GetForwardCompilerType(); + CompilerType class_clang_type = class_type->GetForwardCompilerType(); + + CompilerType clang_type = TypeSystemClang::CreateMemberPointerType( + class_clang_type, pointee_clang_type); + + if (std::optional<uint64_t> clang_type_size = + clang_type.GetByteSize(nullptr)) { + return dwarf->MakeType(die.GetID(), attrs.name, *clang_type_size, nullptr, + LLDB_INVALID_UID, Type::eEncodingIsUID, nullptr, + clang_type, Type::ResolveState::Forward); + } + return nullptr; +} + +void DWARFASTParserClang::ParseInheritance( + const DWARFDIE &die, const DWARFDIE &parent_die, + const CompilerType class_clang_type, const AccessType default_accessibility, + const lldb::ModuleSP &module_sp, + std::vector<std::unique_ptr<clang::CXXBaseSpecifier>> &base_classes, + ClangASTImporter::LayoutInfo &layout_info) { + auto ast = + class_clang_type.GetTypeSystem().dyn_cast_or_null<TypeSystemClang>(); + if (ast == nullptr) + return; + + // TODO: implement DW_TAG_inheritance type parsing. + DWARFAttributes attributes = die.GetAttributes(); + if (attributes.Size() == 0) + return; + + DWARFFormValue encoding_form; + AccessType accessibility = default_accessibility; + bool is_virtual = false; + bool is_base_of_class = true; + off_t member_byte_offset = 0; + + for (uint32_t i = 0; i < attributes.Size(); ++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 (auto maybe_offset = + ExtractDataMemberLocation(die, form_value, module_sp)) + member_byte_offset = *maybe_offset; + break; + + case DW_AT_accessibility: + accessibility = + DWARFASTParser::GetAccessTypeFromDWARF(form_value.Unsigned()); + break; + + case DW_AT_virtuality: + is_virtual = form_value.Boolean(); + break; + + default: + break; + } + } + } + + Type *base_class_type = die.ResolveTypeUID(encoding_form.Reference()); + if (base_class_type == nullptr) { + module_sp->ReportError("{0:x16}: DW_TAG_inheritance failed to " + "resolve the base class at {1:x16}" + " from enclosing type {2:x16}. \nPlease file " + "a bug and attach the file at the start of " + "this error message", + die.GetOffset(), + encoding_form.Reference().GetOffset(), + parent_die.GetOffset()); + return; + } + + CompilerType base_class_clang_type = base_class_type->GetFullCompilerType(); + assert(base_class_clang_type); + if (TypeSystemClang::IsObjCObjectOrInterfaceType(class_clang_type)) { + ast->SetObjCSuperClass(class_clang_type, base_class_clang_type); + return; + } + std::unique_ptr<clang::CXXBaseSpecifier> result = + ast->CreateBaseClassSpecifier(base_class_clang_type.GetOpaqueQualType(), + accessibility, is_virtual, + is_base_of_class); + if (!result) + return; + + 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))); + } +} + +TypeSP DWARFASTParserClang::UpdateSymbolContextScopeForType( + const SymbolContext &sc, const DWARFDIE &die, TypeSP type_sp) { + if (!type_sp) + return type_sp; + + DWARFDIE sc_parent_die = SymbolFileDWARF::GetParentSymbolContextDIE(die); + dw_tag_t sc_parent_tag = sc_parent_die.Tag(); + + SymbolContextScope *symbol_context_scope = nullptr; + 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 != nullptr && sc_parent_die) { + symbol_context_scope = + sc.function->GetBlock(true).FindBlockByID(sc_parent_die.GetID()); + if (symbol_context_scope == nullptr) + symbol_context_scope = sc.function; + } else { + symbol_context_scope = sc.module_sp.get(); + } + + if (symbol_context_scope != nullptr) + type_sp->SetSymbolContextScope(symbol_context_scope); + return type_sp; +} + +void DWARFASTParserClang::GetUniqueTypeNameAndDeclaration( + const lldb_private::plugin::dwarf::DWARFDIE &die, + lldb::LanguageType language, lldb_private::ConstString &unique_typename, + lldb_private::Declaration &decl_declaration) { + // 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. + if (!die.IsValid() || !Language::LanguageIsCPlusPlus(language) || + unique_typename.IsEmpty()) + return; + decl_declaration.Clear(); + std::string qualified_name; + DWARFDIE parent_decl_ctx_die = die.GetParentDeclContextDIE(); + // TODO: change this to get the correct decl context parent.... + while (parent_decl_ctx_die) { + // The name may not contain template parameters due to + // -gsimple-template-names; we must reconstruct the full name from child + // template parameter dies via GetDIEClassTemplateParams(). + const dw_tag_t parent_tag = parent_decl_ctx_die.Tag(); + switch (parent_tag) { + case DW_TAG_namespace: { + if (const char *namespace_name = parent_decl_ctx_die.GetName()) { + qualified_name.insert(0, "::"); + qualified_name.insert(0, namespace_name); + } else { + qualified_name.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: { + if (const char *class_union_struct_name = parent_decl_ctx_die.GetName()) { + qualified_name.insert( + 0, GetDIEClassTemplateParams(parent_decl_ctx_die)); + qualified_name.insert(0, "::"); + qualified_name.insert(0, class_union_struct_name); + } + parent_decl_ctx_die = parent_decl_ctx_die.GetParentDeclContextDIE(); + break; + } + + default: + parent_decl_ctx_die.Clear(); + break; + } + } + + if (qualified_name.empty()) + qualified_name.append("::"); + + qualified_name.append(unique_typename.GetCString()); + qualified_name.append(GetDIEClassTemplateParams(die)); + + unique_typename = ConstString(qualified_name); +} + +TypeSP +DWARFASTParserClang::ParseStructureLikeDIE(const SymbolContext &sc, + const DWARFDIE &die, + ParsedDWARFTypeAttributes &attrs) { + CompilerType clang_type; + const dw_tag_t tag = die.Tag(); + SymbolFileDWARF *dwarf = die.GetDWARF(); + LanguageType cu_language = SymbolFileDWARF::GetLanguage(*die.GetCU()); + Log *log = GetLog(DWARFLog::TypeCompletion | DWARFLog::Lookups); + + ConstString unique_typename(attrs.name); + Declaration unique_decl(attrs.decl); + uint64_t byte_size = attrs.byte_size.value_or(0); + 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.name) { + GetUniqueTypeNameAndDeclaration(die, cu_language, unique_typename, + unique_decl); + if (UniqueDWARFASTType *unique_ast_entry_type = + dwarf->GetUniqueDWARFASTTypeMap().Find( + unique_typename, die, unique_decl, byte_size, + attrs.is_forward_declaration)) { + if (TypeSP type_sp = unique_ast_entry_type->m_type_sp) { + dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get(); + LinkDeclContextToDIE( + GetCachedClangDeclContextForDIE(unique_ast_entry_type->m_die), die); + // If the DIE being parsed in this function is a definition and the + // entry in the map is a declaration, then we need to update the entry + // to point to the definition DIE. + if (!attrs.is_forward_declaration && + unique_ast_entry_type->m_is_forward_declaration) { + unique_ast_entry_type->UpdateToDefDIE(die, unique_decl, byte_size); + clang_type = type_sp->GetForwardCompilerType(); + + CompilerType compiler_type_no_qualifiers = + ClangUtil::RemoveFastQualifiers(clang_type); + dwarf->GetForwardDeclCompilerTypeToDIE().insert_or_assign( + compiler_type_no_qualifiers.GetOpaqueQualType(), + *die.GetDIERef()); + } + 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 = llvm::to_underlying(clang::TagTypeKind::Struct); + default_accessibility = eAccessPublic; + } else if (tag == DW_TAG_union_type) { + tag_decl_kind = llvm::to_underlying(clang::TagTypeKind::Union); + default_accessibility = eAccessPublic; + } else if (tag == DW_TAG_class_type) { + tag_decl_kind = llvm::to_underlying(clang::TagTypeKind::Class); + default_accessibility = eAccessPrivate; + } + + if ((attrs.class_language == eLanguageTypeObjC || + attrs.class_language == eLanguageTypeObjC_plus_plus) && + !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 + TypeSP 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({0:p}) - {1:x16}: {2} ({3}) type \"{4}\" is an " + "incomplete objc type, complete type is {5:x8}", + static_cast<void *>(this), die.GetID(), DW_TAG_value_to_name(tag), + tag, attrs.name.GetCString(), type_sp->GetID()); + } + return type_sp; + } + } + + if (attrs.is_forward_declaration) { + // See if the type comes from a Clang module and if so, track down + // that type. + TypeSP type_sp = ParseTypeFromClangModule(sc, die, log); + if (type_sp) + return type_sp; + } + + assert(tag_decl_kind != -1); + UNUSED_IF_ASSERT_DISABLED(tag_decl_kind); + clang::DeclContext *containing_decl_ctx = + GetClangDeclContextContainingDIE(die, nullptr); + + PrepareContextToReceiveMembers(m_ast, GetClangASTImporter(), + containing_decl_ctx, die, + attrs.name.GetCString()); + + if (attrs.accessibility == eAccessNone && containing_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 = + containing_decl_ctx->getDeclKind(); + if (DeclKindIsCXXClass(containing_decl_kind)) + attrs.accessibility = default_accessibility; + } + + ClangASTMetadata metadata; + metadata.SetUserID(die.GetID()); + metadata.SetIsDynamicCXXType(dwarf->ClassOrStructIsVirtual(die)); + + TypeSystemClang::TemplateParameterInfos template_param_infos; + if (ParseTemplateParameterInfos(die, template_param_infos)) { + clang::ClassTemplateDecl *class_template_decl = + m_ast.ParseClassTemplateDecl( + containing_decl_ctx, GetOwningClangModule(die), attrs.accessibility, + attrs.name.GetCString(), tag_decl_kind, template_param_infos); + if (!class_template_decl) { + if (log) { + dwarf->GetObjectFile()->GetModule()->LogMessage( + log, + "SymbolFileDWARF({0:p}) - {1:x16}: {2} ({3}) type \"{4}\" " + "clang::ClassTemplateDecl failed to return a decl.", + static_cast<void *>(this), die.GetID(), DW_TAG_value_to_name(tag), + tag, attrs.name.GetCString()); + } + return TypeSP(); + } + + clang::ClassTemplateSpecializationDecl *class_specialization_decl = + m_ast.CreateClassTemplateSpecializationDecl( + containing_decl_ctx, GetOwningClangModule(die), class_template_decl, + tag_decl_kind, template_param_infos); + clang_type = + m_ast.CreateClassTemplateSpecializationType(class_specialization_decl); + + m_ast.SetMetadata(class_template_decl, metadata); + m_ast.SetMetadata(class_specialization_decl, metadata); + } + + if (!clang_type) { + clang_type = m_ast.CreateRecordType( + containing_decl_ctx, GetOwningClangModule(die), attrs.accessibility, + attrs.name.GetCString(), tag_decl_kind, attrs.class_language, &metadata, + attrs.exports_symbols); + } + + TypeSP type_sp = dwarf->MakeType( + die.GetID(), attrs.name, attrs.byte_size, nullptr, LLDB_INVALID_UID, + Type::eEncodingIsUID, &attrs.decl, clang_type, + Type::ResolveState::Forward, + TypePayloadClang(OptionalClangModuleID(), attrs.is_complete_objc_class)); + + // 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. + clang::DeclContext *type_decl_ctx = + TypeSystemClang::GetDeclContextForType(clang_type); + LinkDeclContextToDIE(type_decl_ctx, die); + + // 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 at sharing the stack space for variables in different + // blocks. + auto unique_ast_entry_up = std::make_unique<UniqueDWARFASTType>(); + // 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 = byte_size; + unique_ast_entry_up->m_is_forward_declaration = attrs.is_forward_declaration; + dwarf->GetUniqueDWARFASTTypeMap().Insert(unique_typename, + *unique_ast_entry_up); + + // 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. + bool inserted = + dwarf->GetForwardDeclCompilerTypeToDIE() + .try_emplace( + ClangUtil::RemoveFastQualifiers(clang_type).GetOpaqueQualType(), + *die.GetDIERef()) + .second; + assert(inserted && "Type already in the forward declaration map!"); + (void)inserted; + 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->getDefinition()) { + 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::RecordArgPassingKind::CannotPassInRegs); + } + 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 + 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_property_setter_name(property_setter_name), + m_property_getter_name(property_getter_name), + m_property_attributes(property_attributes) { + if (metadata != nullptr) { + m_metadata_up = std::make_unique<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_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 = std::make_unique<ClangASTMetadata>(); + *m_metadata_up = *rhs.m_metadata_up; + } + return *this; + } + + bool Finalize() { + return TypeSystemClang::AddObjCClassProperty( + m_class_opaque_type, m_property_name, m_property_opaque_type, + /*ivar_decl=*/nullptr, 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; + 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, + TypeSystemClang::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.SetParameterPack( + std::make_unique<TypeSystemClang::TemplateParameterInfos>()); + for (DWARFDIE child_die : die.children()) { + if (!ParseTemplateDIE(child_die, template_param_infos.GetParameterPack())) + return false; + } + if (const char *name = die.GetName()) { + template_param_infos.SetPackName(name); + } + return true; + } + case DW_TAG_GNU_template_template_param: + is_template_template_argument = true; + [[fallthrough]]; + case DW_TAG_template_type_parameter: + case DW_TAG_template_value_parameter: { + DWARFAttributes attributes = die.GetAttributes(); + if (attributes.Size() == 0) + return true; + + const char *name = nullptr; + const char *template_name = nullptr; + CompilerType clang_type; + uint64_t uval64 = 0; + bool uval64_valid = false; + bool is_default_template_arg = false; + DWARFFormValue form_value; + for (size_t i = 0; i < attributes.Size(); ++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; + case DW_AT_default_value: + if (attributes.ExtractFormValueAtIndex(i, form_value)) + is_default_template_arg = form_value.Boolean(); + 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; + // Get the signed value for any integer or enumeration if available + clang_type.IsIntegerOrEnumerationType(is_signed); + + if (name && !name[0]) + name = nullptr; + + if (tag == DW_TAG_template_value_parameter && uval64_valid) { + std::optional<uint64_t> size = clang_type.GetBitSize(nullptr); + if (!size) + return false; + llvm::APInt apint(*size, uval64, is_signed); + template_param_infos.InsertArg( + name, clang::TemplateArgument(ast, llvm::APSInt(apint, !is_signed), + ClangUtil::GetQualType(clang_type), + is_default_template_arg)); + } else { + template_param_infos.InsertArg( + name, clang::TemplateArgument(ClangUtil::GetQualType(clang_type), + /*isNullPtr*/ false, + is_default_template_arg)); + } + } else { + auto *tplt_type = m_ast.CreateTemplateTemplateParmDecl(template_name); + template_param_infos.InsertArg( + name, clang::TemplateArgument(clang::TemplateName(tplt_type), + is_default_template_arg)); + } + } + return true; + + default: + break; + } + return false; +} + +bool DWARFASTParserClang::ParseTemplateParameterInfos( + const DWARFDIE &parent_die, + TypeSystemClang::TemplateParameterInfos &template_param_infos) { + + if (!parent_die) + return false; + + for (DWARFDIE die : parent_die.children()) { + 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; + } + } + + return !template_param_infos.IsEmpty() || + template_param_infos.hasParameterPack(); +} + +bool DWARFASTParserClang::CompleteRecordType(const DWARFDIE &die, + lldb_private::Type *type, + CompilerType &clang_type) { + const dw_tag_t tag = die.Tag(); + SymbolFileDWARF *dwarf = die.GetDWARF(); + + ClangASTImporter::LayoutInfo layout_info; + std::vector<DWARFDIE> contained_type_dies; + + if (die.GetAttributeValueAsUnsigned(DW_AT_declaration, 0)) + return false; // No definition, cannot complete. + + // Start the definition if the type is not being defined already. This can + // happen (e.g.) when adding nested types to a class type -- see + // PrepareContextToReceiveMembers. + if (!clang_type.IsBeingDefined()) + TypeSystemClang::StartTagDeclarationDefinition(clang_type); + + AccessType default_accessibility = eAccessNone; + if (tag == DW_TAG_structure_type) { + default_accessibility = eAccessPublic; + } else if (tag == DW_TAG_union_type) { + default_accessibility = eAccessPublic; + } else if (tag == DW_TAG_class_type) { + default_accessibility = eAccessPrivate; + } + + std::vector<std::unique_ptr<clang::CXXBaseSpecifier>> bases; + // Parse members and base classes first + std::vector<DWARFDIE> member_function_dies; + + DelayedPropertyList delayed_properties; + ParseChildMembers(die, clang_type, bases, member_function_dies, + contained_type_dies, delayed_properties, + default_accessibility, layout_info); + + // Now parse any methods if there were any... + for (const DWARFDIE &die : member_function_dies) + dwarf->ResolveType(die); + + if (TypeSystemClang::IsObjCObjectOrInterfaceType(clang_type)) { + ConstString class_name(clang_type.GetTypeName()); + if (class_name) { + dwarf->GetObjCMethods(class_name, [&](DWARFDIE method_die) { + method_die.ResolveType(); + return true; + }); + + for (DelayedAddObjCClassProperty &property : delayed_properties) + property.Finalize(); + } + } + + 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) + TypeSystemClang::RequireCompleteType( + m_ast.GetType(type_source_info->getType())); + } + + m_ast.TransferBaseClasses(clang_type.GetOpaqueQualType(), std::move(bases)); + } + + m_ast.AddMethodOverridesForCXXRecordType(clang_type.GetOpaqueQualType()); + TypeSystemClang::BuildIndirectFields(clang_type); + TypeSystemClang::CompleteTagDeclarationDefinition(clang_type); + + if (type) + layout_info.bit_size = type->GetByteSize(nullptr).value_or(0) * 8; + if (layout_info.bit_size == 0) + layout_info.bit_size = + die.GetAttributeValueAsUnsigned(DW_AT_byte_size, 0) * 8; + if (layout_info.alignment == 0) + layout_info.alignment = + die.GetAttributeValueAsUnsigned(llvm::dwarf::DW_AT_alignment, 0) * 8; + + clang::CXXRecordDecl *record_decl = + m_ast.GetAsCXXRecordDecl(clang_type.GetOpaqueQualType()); + if (record_decl) + GetClangASTImporter().SetRecordLayout(record_decl, layout_info); + + // Now parse all contained types inside of the class. We make forward + // declarations to all classes, but we need the CXXRecordDecl to have decls + // for all contained types because we don't get asked for them via the + // external AST support. + for (const DWARFDIE &die : contained_type_dies) + dwarf->ResolveType(die); + + return (bool)clang_type; +} + +bool DWARFASTParserClang::CompleteEnumType(const DWARFDIE &die, + lldb_private::Type *type, + CompilerType &clang_type) { + if (TypeSystemClang::StartTagDeclarationDefinition(clang_type)) { + if (die.HasChildren()) { + bool is_signed = false; + clang_type.IsIntegerType(is_signed); + ParseChildEnumerators(clang_type, is_signed, + type->GetByteSize(nullptr).value_or(0), die); + } + TypeSystemClang::CompleteTagDeclarationDefinition(clang_type); + } + return (bool)clang_type; +} + +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; + + const dw_tag_t tag = die.Tag(); + + assert(clang_type); + switch (tag) { + case DW_TAG_structure_type: + case DW_TAG_union_type: + case DW_TAG_class_type: + CompleteRecordType(die, type, clang_type); + break; + case DW_TAG_enumeration_type: + CompleteEnumType(die, type, clang_type); + break; + default: + assert(false && "not a forward clang type decl!"); + break; + } + + // If the type is still not fully defined at this point, it means we weren't + // able to find its definition. We must forcefully complete it to preserve + // clang AST invariants. + if (clang_type.IsBeingDefined()) { + TypeSystemClang::CompleteTagDeclarationDefinition(clang_type); + m_ast.SetDeclIsForcefullyCompleted(ClangUtil::GetAsTagDecl(clang_type)); + } + + return true; +} + +void DWARFASTParserClang::EnsureAllDIEsInDeclContextHaveBeenParsed( + lldb_private::CompilerDeclContext decl_context) { + auto opaque_decl_ctx = + (clang::DeclContext *)decl_context.GetOpaqueDeclContext(); + for (auto it = m_decl_ctx_to_die.find(opaque_decl_ctx); + it != m_decl_ctx_to_die.end() && it->first == opaque_decl_ctx; + it = m_decl_ctx_to_die.erase(it)) + for (DWARFDIE decl : it->second.children()) + GetClangDeclForDIE(decl); +} + +CompilerDecl DWARFASTParserClang::GetDeclForUIDFromDWARF(const DWARFDIE &die) { + clang::Decl *clang_decl = GetClangDeclForDIE(die); + if (clang_decl != nullptr) + return m_ast.GetCompilerDecl(clang_decl); + return {}; +} + +CompilerDeclContext +DWARFASTParserClang::GetDeclContextForUIDFromDWARF(const DWARFDIE &die) { + clang::DeclContext *clang_decl_ctx = GetClangDeclContextForDIE(die); + if (clang_decl_ctx) + return m_ast.CreateDeclContext(clang_decl_ctx); + return {}; +} + +CompilerDeclContext +DWARFASTParserClang::GetDeclContextContainingUIDFromDWARF(const DWARFDIE &die) { + clang::DeclContext *clang_decl_ctx = + GetClangDeclContextContainingDIE(die, nullptr); + if (clang_decl_ctx) + return m_ast.CreateDeclContext(clang_decl_ctx); + return {}; +} + +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.children()) { + const dw_tag_t tag = die.Tag(); + if (tag != DW_TAG_enumerator) + continue; + + DWARFAttributes attributes = die.GetAttributes(); + if (attributes.Size() == 0) + continue; + + const char *name = nullptr; + bool got_value = false; + int64_t enum_value = 0; + Declaration decl; + + for (size_t i = 0; i < attributes.Size(); ++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( + attributes.CompileUnitAtIndex(i)->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; +} + +ConstString +DWARFASTParserClang::ConstructDemangledNameFromDWARF(const DWARFDIE &die) { + 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; + StreamString sstr; + + DWARFDeclContext decl_ctx = die.GetDWARFDeclContext(); + 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"; + + return ConstString(sstr.GetString()); +} + +Function * +DWARFASTParserClang::ParseFunctionFromDWARF(CompileUnit &comp_unit, + const DWARFDIE &die, + const AddressRange &func_range) { + assert(func_range.GetBaseAddress().IsValid()); + DWARFRangeList func_ranges; + const char *name = nullptr; + const char *mangled = nullptr; + std::optional<int> decl_file; + std::optional<int> decl_line; + std::optional<int> decl_column; + std::optional<int> call_file; + std::optional<int> call_line; + std::optional<int> call_column; + DWARFExpressionList 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)) { + Mangled func_name; + if (mangled) + func_name.SetValue(ConstString(mangled)); + else if ((die.GetParent().Tag() == DW_TAG_compile_unit || + die.GetParent().Tag() == DW_TAG_partial_unit) && + Language::LanguageIsCPlusPlus( + SymbolFileDWARF::GetLanguage(*die.GetCU())) && + !Language::LanguageIsObjC( + SymbolFileDWARF::GetLanguage(*die.GetCU())) && + 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. + func_name.SetValue(ConstructDemangledNameFromDWARF(die)); + } else + func_name.SetValue(ConstString(name)); + + FunctionSP func_sp; + std::unique_ptr<Declaration> decl_up; + if (decl_file || decl_line || decl_column) + decl_up = std::make_unique<Declaration>( + die.GetCU()->GetFile(decl_file ? *decl_file : 0), + decl_line ? *decl_line : 0, decl_column ? *decl_column : 0); + + 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); + + 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; +} + +namespace { +/// Parsed form of all attributes that are relevant for parsing Objective-C +/// properties. +struct PropertyAttributes { + explicit PropertyAttributes(const DWARFDIE &die); + const char *prop_name = nullptr; + const char *prop_getter_name = nullptr; + const char *prop_setter_name = nullptr; + /// \see clang::ObjCPropertyAttribute + uint32_t prop_attributes = 0; +}; + +struct DiscriminantValue { + explicit DiscriminantValue(const DWARFDIE &die, ModuleSP module_sp); + + uint32_t byte_offset; + uint32_t byte_size; + DWARFFormValue type_ref; +}; + +struct VariantMember { + explicit VariantMember(DWARFDIE &die, ModuleSP module_sp); + bool IsDefault() const; + + std::optional<uint32_t> discr_value; + DWARFFormValue type_ref; + ConstString variant_name; + uint32_t byte_offset; + ConstString GetName() const; +}; + +struct VariantPart { + explicit VariantPart(const DWARFDIE &die, const DWARFDIE &parent_die, + ModuleSP module_sp); + + std::vector<VariantMember> &members(); + + DiscriminantValue &discriminant(); + +private: + std::vector<VariantMember> _members; + DiscriminantValue _discriminant; +}; + +} // namespace + +ConstString VariantMember::GetName() const { return this->variant_name; } + +bool VariantMember::IsDefault() const { return !discr_value; } + +VariantMember::VariantMember(DWARFDIE &die, lldb::ModuleSP module_sp) { + assert(die.Tag() == llvm::dwarf::DW_TAG_variant); + this->discr_value = + die.GetAttributeValueAsOptionalUnsigned(DW_AT_discr_value); + + for (auto child_die : die.children()) { + switch (child_die.Tag()) { + case llvm::dwarf::DW_TAG_member: { + DWARFAttributes attributes = child_die.GetAttributes(); + for (std::size_t i = 0; i < attributes.Size(); ++i) { + DWARFFormValue form_value; + const dw_attr_t attr = attributes.AttributeAtIndex(i); + if (attributes.ExtractFormValueAtIndex(i, form_value)) { + switch (attr) { + case DW_AT_name: + variant_name = ConstString(form_value.AsCString()); + break; + case DW_AT_type: + type_ref = form_value; + break; + + case DW_AT_data_member_location: + if (auto maybe_offset = + ExtractDataMemberLocation(die, form_value, module_sp)) + byte_offset = *maybe_offset; + break; + + default: + break; + } + } + } + break; + } + default: + break; + } + break; + } +} + +DiscriminantValue::DiscriminantValue(const DWARFDIE &die, ModuleSP module_sp) { + auto referenced_die = die.GetReferencedDIE(DW_AT_discr); + DWARFAttributes attributes = referenced_die.GetAttributes(); + for (std::size_t i = 0; i < attributes.Size(); ++i) { + const dw_attr_t attr = attributes.AttributeAtIndex(i); + DWARFFormValue form_value; + if (attributes.ExtractFormValueAtIndex(i, form_value)) { + switch (attr) { + case DW_AT_type: + type_ref = form_value; + break; + case DW_AT_data_member_location: + if (auto maybe_offset = + ExtractDataMemberLocation(die, form_value, module_sp)) + byte_offset = *maybe_offset; + break; + default: + break; + } + } + } +} + +VariantPart::VariantPart(const DWARFDIE &die, const DWARFDIE &parent_die, + lldb::ModuleSP module_sp) + : _members(), _discriminant(die, module_sp) { + + for (auto child : die.children()) { + if (child.Tag() == llvm::dwarf::DW_TAG_variant) { + _members.push_back(VariantMember(child, module_sp)); + } + } +} + +std::vector<VariantMember> &VariantPart::members() { return this->_members; } + +DiscriminantValue &VariantPart::discriminant() { return this->_discriminant; } + +DWARFASTParserClang::MemberAttributes::MemberAttributes( + const DWARFDIE &die, const DWARFDIE &parent_die, ModuleSP module_sp) { + DWARFAttributes attributes = die.GetAttributes(); + for (size_t i = 0; i < attributes.Size(); ++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_const_value: + const_value_form = form_value; + break; + case DW_AT_data_bit_offset: + data_bit_offset = form_value.Unsigned(); + break; + case DW_AT_data_member_location: + if (auto maybe_offset = + ExtractDataMemberLocation(die, form_value, module_sp)) + member_byte_offset = *maybe_offset; + break; + + case DW_AT_accessibility: + accessibility = + DWARFASTParser::GetAccessTypeFromDWARF(form_value.Unsigned()); + break; + case DW_AT_artificial: + is_artificial = form_value.Boolean(); + break; + case DW_AT_declaration: + is_declaration = form_value.Boolean(); + break; + default: + break; + } + } + } + + // 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.value_or(0) == 0 && bit_offset < 0) { + bit_size = 0; + bit_offset = 0; + } +} + +PropertyAttributes::PropertyAttributes(const DWARFDIE &die) { + + DWARFAttributes attributes = die.GetAttributes(); + for (size_t i = 0; i < attributes.Size(); ++i) { + const dw_attr_t attr = attributes.AttributeAtIndex(i); + DWARFFormValue form_value; + if (attributes.ExtractFormValueAtIndex(i, form_value)) { + switch (attr) { + 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; + default: + break; + } + } + } + + if (!prop_name) + return; + 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] == '-') { + std::optional<const ObjCLanguage::MethodName> prop_getter_method = + ObjCLanguage::MethodName::Create(prop_getter_name, true); + if (prop_getter_method) + prop_getter_name = + ConstString(prop_getter_method->GetSelector()).GetCString(); + } + + if (prop_setter_name && prop_setter_name[0] == '-') { + std::optional<const ObjCLanguage::MethodName> prop_setter_method = + ObjCLanguage::MethodName::Create(prop_setter_name, true); + if (prop_setter_method) + prop_setter_name = + ConstString(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(); + } +} + +void DWARFASTParserClang::ParseObjCProperty( + const DWARFDIE &die, const DWARFDIE &parent_die, + const lldb_private::CompilerType &class_clang_type, + DelayedPropertyList &delayed_properties) { + // This function can only parse DW_TAG_APPLE_property. + assert(die.Tag() == DW_TAG_APPLE_property); + + ModuleSP module_sp = parent_die.GetDWARF()->GetObjectFile()->GetModule(); + + const MemberAttributes attrs(die, parent_die, module_sp); + const PropertyAttributes propAttrs(die); + + if (!propAttrs.prop_name) { + module_sp->ReportError("{0:x8}: DW_TAG_APPLE_property has no name.", + die.GetID()); + return; + } + + Type *member_type = die.ResolveTypeUID(attrs.encoding_form.Reference()); + if (!member_type) { + module_sp->ReportError( + "{0:x8}: DW_TAG_APPLE_property '{1}' refers to type {2:x16}" + " which was unable to be parsed", + die.GetID(), propAttrs.prop_name, + attrs.encoding_form.Reference().GetOffset()); + return; + } + + ClangASTMetadata metadata; + metadata.SetUserID(die.GetID()); + delayed_properties.push_back(DelayedAddObjCClassProperty( + class_clang_type, propAttrs.prop_name, + member_type->GetLayoutCompilerType(), propAttrs.prop_setter_name, + propAttrs.prop_getter_name, propAttrs.prop_attributes, &metadata)); +} + +llvm::Expected<llvm::APInt> DWARFASTParserClang::ExtractIntFromFormValue( + const CompilerType &int_type, const DWARFFormValue &form_value) const { + clang::QualType qt = ClangUtil::GetQualType(int_type); + assert(qt->isIntegralOrEnumerationType()); + auto ts_ptr = int_type.GetTypeSystem().dyn_cast_or_null<TypeSystemClang>(); + if (!ts_ptr) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "TypeSystem not clang"); + TypeSystemClang &ts = *ts_ptr; + clang::ASTContext &ast = ts.getASTContext(); + + const unsigned type_bits = ast.getIntWidth(qt); + const bool is_unsigned = qt->isUnsignedIntegerType(); + + // The maximum int size supported at the moment by this function. Limited + // by the uint64_t return type of DWARFFormValue::Signed/Unsigned. + constexpr std::size_t max_bit_size = 64; + + // For values bigger than 64 bit (e.g. __int128_t values), + // DWARFFormValue's Signed/Unsigned functions will return wrong results so + // emit an error for now. + if (type_bits > max_bit_size) { + auto msg = llvm::formatv("Can only parse integers with up to {0} bits, but " + "given integer has {1} bits.", + max_bit_size, type_bits); + return llvm::createStringError(llvm::inconvertibleErrorCode(), msg.str()); + } + + // Construct an APInt with the maximum bit size and the given integer. + llvm::APInt result(max_bit_size, form_value.Unsigned(), !is_unsigned); + + // Calculate how many bits are required to represent the input value. + // For unsigned types, take the number of active bits in the APInt. + // For signed types, ask APInt how many bits are required to represent the + // signed integer. + const unsigned required_bits = + is_unsigned ? result.getActiveBits() : result.getSignificantBits(); + + // If the input value doesn't fit into the integer type, return an error. + if (required_bits > type_bits) { + std::string value_as_str = is_unsigned + ? std::to_string(form_value.Unsigned()) + : std::to_string(form_value.Signed()); + auto msg = llvm::formatv("Can't store {0} value {1} in integer with {2} " + "bits.", + (is_unsigned ? "unsigned" : "signed"), + value_as_str, type_bits); + return llvm::createStringError(llvm::inconvertibleErrorCode(), msg.str()); + } + + // Trim the result to the bit width our the int type. + if (result.getBitWidth() > type_bits) + result = result.trunc(type_bits); + return result; +} + +void DWARFASTParserClang::CreateStaticMemberVariable( + const DWARFDIE &die, const MemberAttributes &attrs, + const lldb_private::CompilerType &class_clang_type) { + Log *log = GetLog(DWARFLog::TypeCompletion | DWARFLog::Lookups); + assert(die.Tag() == DW_TAG_member || die.Tag() == DW_TAG_variable); + + Type *var_type = die.ResolveTypeUID(attrs.encoding_form.Reference()); + + if (!var_type) + return; + + auto accessibility = + attrs.accessibility == eAccessNone ? eAccessPublic : attrs.accessibility; + + CompilerType ct = var_type->GetForwardCompilerType(); + clang::VarDecl *v = TypeSystemClang::AddVariableToRecordType( + class_clang_type, attrs.name, ct, accessibility); + if (!v) { + LLDB_LOG(log, "Failed to add variable to the record type"); + return; + } + + bool unused; + // TODO: Support float/double static members as well. + if (!ct.IsIntegerOrEnumerationType(unused) || !attrs.const_value_form) + return; + + llvm::Expected<llvm::APInt> const_value_or_err = + ExtractIntFromFormValue(ct, *attrs.const_value_form); + if (!const_value_or_err) { + LLDB_LOG_ERROR(log, const_value_or_err.takeError(), + "Failed to add const value to variable {1}: {0}", + v->getQualifiedNameAsString()); + return; + } + + TypeSystemClang::SetIntegerInitializerForVariable(v, *const_value_or_err); +} + +void DWARFASTParserClang::ParseSingleMember( + const DWARFDIE &die, const DWARFDIE &parent_die, + const lldb_private::CompilerType &class_clang_type, + lldb::AccessType default_accessibility, + lldb_private::ClangASTImporter::LayoutInfo &layout_info, + FieldInfo &last_field_info) { + // This function can only parse DW_TAG_member. + assert(die.Tag() == DW_TAG_member); + + ModuleSP module_sp = parent_die.GetDWARF()->GetObjectFile()->GetModule(); + const dw_tag_t tag = die.Tag(); + // 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; + + const MemberAttributes attrs(die, parent_die, module_sp); + + // Handle static members, which are typically members without + // locations. However, GCC doesn't emit DW_AT_data_member_location + // for any union members (regardless of linkage). + // Non-normative text pre-DWARFv5 recommends marking static + // data members with an DW_AT_external flag. Clang emits this consistently + // whereas GCC emits it only for static data members if not part of an + // anonymous namespace. The flag that is consistently emitted for static + // data members is DW_AT_declaration, so we check it instead. + // The following block is only necessary to support DWARFv4 and earlier. + // Starting with DWARFv5, static data members are marked DW_AT_variable so we + // can consistently detect them on both GCC and Clang without below heuristic. + if (attrs.member_byte_offset == UINT32_MAX && + attrs.data_bit_offset == UINT64_MAX && attrs.is_declaration) { + CreateStaticMemberVariable(die, attrs, class_clang_type); + return; + } + + Type *member_type = die.ResolveTypeUID(attrs.encoding_form.Reference()); + if (!member_type) { + if (attrs.name) + module_sp->ReportError( + "{0:x8}: DW_TAG_member '{1}' refers to type {2:x16}" + " which was unable to be parsed", + die.GetID(), attrs.name, attrs.encoding_form.Reference().GetOffset()); + else + module_sp->ReportError("{0:x8}: DW_TAG_member refers to type {1:x16}" + " which was unable to be parsed", + die.GetID(), + attrs.encoding_form.Reference().GetOffset()); + return; + } + + const uint64_t character_width = 8; + const uint64_t word_width = 32; + CompilerType member_clang_type = member_type->GetLayoutCompilerType(); + + const auto accessibility = attrs.accessibility == eAccessNone + ? default_accessibility + : attrs.accessibility; + + uint64_t field_bit_offset = (attrs.member_byte_offset == UINT32_MAX + ? 0 + : (attrs.member_byte_offset * 8ULL)); + + if (attrs.bit_size > 0) { + FieldInfo this_field_info; + this_field_info.bit_offset = field_bit_offset; + this_field_info.bit_size = attrs.bit_size; + + if (attrs.data_bit_offset != UINT64_MAX) { + this_field_info.bit_offset = attrs.data_bit_offset; + } else { + auto byte_size = attrs.byte_size; + if (!byte_size) + byte_size = member_type->GetByteSize(nullptr); + + ObjectFile *objfile = die.GetDWARF()->GetObjectFile(); + if (objfile->GetByteOrder() == eByteOrderLittle) { + this_field_info.bit_offset += byte_size.value_or(0) * 8; + this_field_info.bit_offset -= (attrs.bit_offset + attrs.bit_size); + } else { + this_field_info.bit_offset += attrs.bit_offset; + } + } + + // The ObjC runtime knows the byte offset but we still need to provide + // the bit-offset in the layout. It just means something different then + // what it does in C and C++. So we skip this check for ObjC types. + // + // We also skip this for fields of a union since they will all have a + // zero offset. + if (!TypeSystemClang::IsObjCObjectOrInterfaceType(class_clang_type) && + !(parent_die.Tag() == DW_TAG_union_type && + this_field_info.bit_offset == 0) && + ((this_field_info.bit_offset >= parent_bit_size) || + (last_field_info.IsBitfield() && + !last_field_info.NextBitfieldOffsetIsValid( + this_field_info.bit_offset)))) { + ObjectFile *objfile = die.GetDWARF()->GetObjectFile(); + objfile->GetModule()->ReportWarning( + "{0:x16}: {1} ({2}) bitfield named \"{3}\" has invalid " + "bit offset ({4:x8}) member will be ignored. Please file a bug " + "against the " + "compiler and include the preprocessed output for {5}\n", + die.GetID(), DW_TAG_value_to_name(tag), tag, attrs.name, + this_field_info.bit_offset, GetUnitName(parent_die).c_str()); + return; + } + + // Update the field bit offset we will report for layout + field_bit_offset = this_field_info.bit_offset; + + // 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 (TypeSystemClang::IsObjCObjectOrInterfaceType(class_clang_type)) + detect_unnamed_bitfields = + die.GetCU()->Supports_unnamed_objc_bitfields(); + + if (detect_unnamed_bitfields) { + std::optional<FieldInfo> unnamed_field_info; + uint64_t last_field_end = + last_field_info.bit_offset + last_field_info.bit_size; + + if (!last_field_info.IsBitfield()) { + // The last field was not a bit-field... + // but if it did take up the entire word then we need to extend + // last_field_end so the bit-field does not step into the last + // fields padding. + if (last_field_end != 0 && ((last_field_end % word_width) != 0)) + last_field_end += word_width - (last_field_end % word_width); + } + + if (ShouldCreateUnnamedBitfield(last_field_info, last_field_end, + this_field_info, layout_info)) { + unnamed_field_info = FieldInfo{}; + unnamed_field_info->bit_size = + this_field_info.bit_offset - last_field_end; + unnamed_field_info->bit_offset = last_field_end; + } + + if (unnamed_field_info) { + clang::FieldDecl *unnamed_bitfield_decl = + TypeSystemClang::AddFieldToRecordType( + class_clang_type, llvm::StringRef(), + m_ast.GetBuiltinTypeForEncodingAndBitSize(eEncodingSint, + word_width), + accessibility, unnamed_field_info->bit_size); + + layout_info.field_offsets.insert(std::make_pair( + unnamed_bitfield_decl, unnamed_field_info->bit_offset)); + } + } + + last_field_info = this_field_info; + last_field_info.SetIsBitfield(true); + } else { + last_field_info.bit_offset = field_bit_offset; + + if (std::optional<uint64_t> clang_type_size = + member_type->GetByteSize(nullptr)) { + last_field_info.bit_size = *clang_type_size * character_width; + } + + last_field_info.SetIsBitfield(false); + } + + // Don't turn artificial members such as vtable pointers into real FieldDecls + // in our AST. Clang will re-create those articial members and they would + // otherwise just overlap in the layout with the FieldDecls we add here. + // This needs to be done after updating FieldInfo which keeps track of where + // field start/end so we don't later try to fill the space of this + // artificial member with (unnamed bitfield) padding. + if (attrs.is_artificial && ShouldIgnoreArtificialField(attrs.name)) { + last_field_info.SetIsArtificial(true); + return; + } + + if (!member_clang_type.IsCompleteType()) + member_clang_type.GetCompleteType(); + + { + // Older versions of clang emit the same DWARF for array[0] and array[1]. 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]. This was fixed by f454dfb6b5af. + 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 (attrs.member_byte_offset >= parent_byte_size) { + if (member_array_size != 1 && + (member_array_size != 0 || + attrs.member_byte_offset > parent_byte_size)) { + module_sp->ReportError( + "{0:x8}: DW_TAG_member '{1}' refers to type {2:x16}" + " which extends beyond the bounds of {3:x8}", + die.GetID(), attrs.name, + attrs.encoding_form.Reference().GetOffset(), parent_die.GetID()); + } + + member_clang_type = + m_ast.CreateArrayType(member_array_element_type, 0, false); + } + } + } + + TypeSystemClang::RequireCompleteType(member_clang_type); + + clang::FieldDecl *field_decl = TypeSystemClang::AddFieldToRecordType( + class_clang_type, attrs.name, member_clang_type, accessibility, + attrs.bit_size); + + m_ast.SetMetadataAsUserID(field_decl, die.GetID()); + + layout_info.field_offsets.insert( + std::make_pair(field_decl, field_bit_offset)); +} + +bool DWARFASTParserClang::ParseChildMembers( + const DWARFDIE &parent_die, CompilerType &class_clang_type, + std::vector<std::unique_ptr<clang::CXXBaseSpecifier>> &base_classes, + std::vector<DWARFDIE> &member_function_dies, + std::vector<DWARFDIE> &contained_type_dies, + DelayedPropertyList &delayed_properties, + const AccessType default_accessibility, + ClangASTImporter::LayoutInfo &layout_info) { + if (!parent_die) + return false; + + FieldInfo last_field_info; + + ModuleSP module_sp = parent_die.GetDWARF()->GetObjectFile()->GetModule(); + auto ts = class_clang_type.GetTypeSystem(); + auto ast = ts.dyn_cast_or_null<TypeSystemClang>(); + if (ast == nullptr) + return false; + + for (DWARFDIE die : parent_die.children()) { + dw_tag_t tag = die.Tag(); + + switch (tag) { + case DW_TAG_APPLE_property: + ParseObjCProperty(die, parent_die, class_clang_type, delayed_properties); + break; + + case DW_TAG_variant_part: + if (die.GetCU()->GetDWARFLanguageType() == eLanguageTypeRust) { + ParseRustVariantPart(die, parent_die, class_clang_type, + default_accessibility, layout_info); + } + break; + + case DW_TAG_variable: { + const MemberAttributes attrs(die, parent_die, module_sp); + CreateStaticMemberVariable(die, attrs, class_clang_type); + } break; + case DW_TAG_member: + ParseSingleMember(die, parent_die, class_clang_type, + default_accessibility, layout_info, last_field_info); + 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: + ParseInheritance(die, parent_die, class_clang_type, default_accessibility, + module_sp, base_classes, layout_info); + break; + + default: + if (llvm::dwarf::isType(tag)) + contained_type_dies.push_back(die); + 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.children()) { + const dw_tag_t tag = die.Tag(); + switch (tag) { + case DW_TAG_formal_parameter: { + DWARFAttributes attributes = die.GetAttributes(); + if (attributes.Size() == 0) { + arg_idx++; + break; + } + + 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 < attributes.Size(); ++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, GetOwningClangModule(die), 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; +} + +clang::Decl *DWARFASTParserClang::GetClangDeclForDIE(const DWARFDIE &die) { + if (!die) + return nullptr; + + switch (die.Tag()) { + case DW_TAG_constant: + case DW_TAG_formal_parameter: + case DW_TAG_imported_declaration: + case DW_TAG_imported_module: + break; + case DW_TAG_variable: + // This means 'die' is a C++ static data member. + // We don't want to create decls for such members + // here. + if (auto parent = die.GetParent(); + parent.IsValid() && TagIsRecordType(parent.Tag())) + return nullptr; + 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; + 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; + 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 = + TypeSystemClang::DeclContextGetAsDeclContext( + dwarf->GetDeclContextContainingUID(die.GetID())); + decl = m_ast.CreateVariableDeclaration( + decl_context, GetOwningClangModule(die), 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 = SymbolFileDWARF::GetDecl(imported_uid); + if (imported_decl) { + clang::DeclContext *decl_context = + TypeSystemClang::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, OptionalClangModuleID(), 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 = + SymbolFileDWARF::GetDeclContext(imported_uid); + if (imported_decl_ctx) { + clang::DeclContext *decl_context = + TypeSystemClang::DeclContextGetAsDeclContext( + dwarf->GetDeclContextContainingUID(die.GetID())); + if (clang::NamespaceDecl *ns_decl = + TypeSystemClang::DeclContextGetAsNamespaceDecl( + imported_decl_ctx)) + decl = m_ast.CreateUsingDirectiveDeclaration( + decl_context, OptionalClangModuleID(), ns_decl); + } + } + break; + } + default: + break; + } + + m_die_to_decl[die.GetDIE()] = decl; + + 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_imported_declaration: + decl_ctx = ResolveImportedDeclarationDIE(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; +} + +OptionalClangModuleID +DWARFASTParserClang::GetOwningClangModule(const DWARFDIE &die) { + if (!die.IsValid()) + return {}; + + for (DWARFDIE parent = die.GetParent(); parent.IsValid(); + parent = parent.GetParent()) { + const dw_tag_t tag = parent.Tag(); + if (tag == DW_TAG_module) { + DWARFDIE module_die = parent; + auto it = m_die_to_module.find(module_die.GetDIE()); + if (it != m_die_to_module.end()) + return it->second; + const char *name = + module_die.GetAttributeValueAsString(DW_AT_name, nullptr); + if (!name) + return {}; + + OptionalClangModuleID id = + m_ast.GetOrCreateClangModule(name, GetOwningClangModule(module_die)); + m_die_to_module.insert({module_die.GetDIE(), id}); + return id; + } + } + return {}; +} + +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.children()) { + 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, GetOwningClangModule(die)); + + 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, GetOwningClangModule(die), + is_inline); + + if (namespace_decl) + LinkDeclContextToDIE((clang::DeclContext *)namespace_decl, die); + return namespace_decl; + } + } + return nullptr; +} + +clang::NamespaceDecl * +DWARFASTParserClang::ResolveImportedDeclarationDIE(const DWARFDIE &die) { + assert(die && die.Tag() == DW_TAG_imported_declaration); + + // See if we cached a NamespaceDecl for this imported declaration + // already + auto it = m_die_to_decl_ctx.find(die.GetDIE()); + if (it != m_die_to_decl_ctx.end()) + return static_cast<clang::NamespaceDecl *>(it->getSecond()); + + clang::NamespaceDecl *namespace_decl = nullptr; + + const DWARFDIE imported_uid = + die.GetAttributeValueAsReferenceDIE(DW_AT_import); + if (!imported_uid) + return nullptr; + + switch (imported_uid.Tag()) { + case DW_TAG_imported_declaration: + namespace_decl = ResolveImportedDeclarationDIE(imported_uid); + break; + case DW_TAG_namespace: + namespace_decl = ResolveNamespaceDIE(imported_uid); + break; + default: + return nullptr; + } + + if (!namespace_decl) + return nullptr; + + LinkDeclContextToDIE(namespace_decl, die); + + return namespace_decl; +} + +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(); + + auto gather = [](DWARFDIE die, UniqueCStringMap<DWARFDIE> &map, + UniqueCStringMap<DWARFDIE> &map_artificial) { + if (die.Tag() != DW_TAG_subprogram) + return; + // 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 that are tracking here. + if (die.GetAttributeValueAsUnsigned(DW_AT_declaration, 0) != 1) + return; + + if (const char *name = die.GetMangledName()) { + ConstString const_name(name); + if (die.GetAttributeValueAsUnsigned(DW_AT_artificial, 0)) + map_artificial.Append(const_name, die); + else + map.Append(const_name, 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 (DWARFDIE src_die = src_class_die.GetFirstChild(); src_die.IsValid(); + src_die = src_die.GetSibling()) { + gather(src_die, src_name_to_die, src_name_to_die_artificial); + } + for (DWARFDIE dst_die = dst_class_die.GetFirstChild(); dst_die.IsValid(); + dst_die = dst_die.GetSibling()) { + gather(dst_die, dst_name_to_die, dst_name_to_die_artificial); + } + const uint32_t src_size = src_name_to_die.GetSize(); + const uint32_t dst_size = dst_name_to_die.GetSize(); + + // Is everything kosher so we can go through the members at top speed? + bool fast_path = true; + + if (src_size != dst_size) + fast_path = false; + + uint32_t idx; + + if (fast_path) { + for (idx = 0; idx < src_size; ++idx) { + DWARFDIE src_die = src_name_to_die.GetValueAtIndexUnchecked(idx); + DWARFDIE dst_die = dst_name_to_die.GetValueAtIndexUnchecked(idx); + + if (src_die.Tag() != dst_die.Tag()) + 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; + + fast_path = false; + } + } + + DWARFASTParserClang *src_dwarf_ast_parser = + static_cast<DWARFASTParserClang *>( + SymbolFileDWARF::GetDWARFParser(*src_class_die.GetCU())); + DWARFASTParserClang *dst_dwarf_ast_parser = + static_cast<DWARFASTParserClang *>( + SymbolFileDWARF::GetDWARFParser(*dst_class_die.GetCU())); + auto link = [&](DWARFDIE src, DWARFDIE dst) { + SymbolFileDWARF::DIEToTypePtr &die_to_type = + dst_class_die.GetDWARF()->GetDIEToType(); + clang::DeclContext *dst_decl_ctx = + dst_dwarf_ast_parser->m_die_to_decl_ctx[dst.GetDIE()]; + if (dst_decl_ctx) + src_dwarf_ast_parser->LinkDeclContextToDIE(dst_decl_ctx, src); + + if (Type *src_child_type = die_to_type.lookup(src.GetDIE())) + die_to_type[dst.GetDIE()] = src_child_type; + }; + + // 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) { + link(src_name_to_die.GetValueAtIndexUnchecked(idx), + dst_name_to_die.GetValueAtIndexUnchecked(idx)); + } + } 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); + DWARFDIE dst_die = dst_name_to_die.GetValueAtIndexUnchecked(idx); + DWARFDIE src_die = src_name_to_die.Find(dst_name, DWARFDIE()); + + if (src_die && (src_die.Tag() == dst_die.Tag())) + link(src_die, dst_die); + else + 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); + DWARFDIE src_die = + src_name_to_die_artificial.GetValueAtIndexUnchecked(idx); + DWARFDIE dst_die = + dst_name_to_die_artificial.Find(src_name_artificial, DWARFDIE()); + + // Both classes have the artificial types, link them + if (dst_die) + link(src_die, dst_die); + } + } + + if (dst_size_artificial) { + for (idx = 0; idx < dst_size_artificial; ++idx) { + failures.push_back( + dst_name_to_die_artificial.GetValueAtIndexUnchecked(idx)); + } + } + + return !failures.empty(); +} + +bool DWARFASTParserClang::ShouldCreateUnnamedBitfield( + FieldInfo const &last_field_info, uint64_t last_field_end, + FieldInfo const &this_field_info, + lldb_private::ClangASTImporter::LayoutInfo const &layout_info) const { + // If we have a gap between the last_field_end and the current + // field we have an unnamed bit-field. + if (this_field_info.bit_offset <= last_field_end) + return false; + + // If we have a base class, we assume there is no unnamed + // bit-field if either of the following is true: + // (a) this is the first field since the gap can be + // attributed to the members from the base class. + // FIXME: This assumption is not correct if the first field of + // the derived class is indeed an unnamed bit-field. We currently + // do not have the machinary to track the offset of the last field + // of classes we have seen before, so we are not handling this case. + // (b) Or, the first member of the derived class was a vtable pointer. + // In this case we don't want to create an unnamed bitfield either + // since those will be inserted by clang later. + const bool have_base = layout_info.base_offsets.size() != 0; + const bool this_is_first_field = + last_field_info.bit_offset == 0 && last_field_info.bit_size == 0; + const bool first_field_is_vptr = + last_field_info.bit_offset == 0 && last_field_info.IsArtificial(); + + if (have_base && (this_is_first_field || first_field_is_vptr)) + return false; + + return true; +} + +void DWARFASTParserClang::ParseRustVariantPart( + DWARFDIE &die, const DWARFDIE &parent_die, CompilerType &class_clang_type, + const lldb::AccessType default_accesibility, + ClangASTImporter::LayoutInfo &layout_info) { + assert(die.Tag() == llvm::dwarf::DW_TAG_variant_part); + assert(SymbolFileDWARF::GetLanguage(*die.GetCU()) == + LanguageType::eLanguageTypeRust); + + ModuleSP module_sp = parent_die.GetDWARF()->GetObjectFile()->GetModule(); + + VariantPart variants(die, parent_die, module_sp); + + auto discriminant_type = + die.ResolveTypeUID(variants.discriminant().type_ref.Reference()); + + auto decl_context = m_ast.GetDeclContextForType(class_clang_type); + + auto inner_holder = m_ast.CreateRecordType( + decl_context, OptionalClangModuleID(), lldb::eAccessPublic, + std::string( + llvm::formatv("{0}$Inner", class_clang_type.GetTypeName(false))), + llvm::to_underlying(clang::TagTypeKind::Union), lldb::eLanguageTypeRust); + m_ast.StartTagDeclarationDefinition(inner_holder); + m_ast.SetIsPacked(inner_holder); + + for (auto member : variants.members()) { + + auto has_discriminant = !member.IsDefault(); + + auto member_type = die.ResolveTypeUID(member.type_ref.Reference()); + + auto field_type = m_ast.CreateRecordType( + m_ast.GetDeclContextForType(inner_holder), OptionalClangModuleID(), + lldb::eAccessPublic, + std::string(llvm::formatv("{0}$Variant", member.GetName())), + llvm::to_underlying(clang::TagTypeKind::Struct), + lldb::eLanguageTypeRust); + + m_ast.StartTagDeclarationDefinition(field_type); + auto offset = member.byte_offset; + + if (has_discriminant) { + m_ast.AddFieldToRecordType( + field_type, "$discr$", discriminant_type->GetFullCompilerType(), + lldb::eAccessPublic, variants.discriminant().byte_offset); + offset += discriminant_type->GetByteSize(nullptr).value_or(0); + } + + m_ast.AddFieldToRecordType(field_type, "value", + member_type->GetFullCompilerType(), + lldb::eAccessPublic, offset * 8); + + m_ast.CompleteTagDeclarationDefinition(field_type); + + auto name = has_discriminant + ? llvm::formatv("$variant${0}", member.discr_value.value()) + : std::string("$variant$"); + + auto variant_decl = + m_ast.AddFieldToRecordType(inner_holder, llvm::StringRef(name), + field_type, default_accesibility, 0); + + layout_info.field_offsets.insert({variant_decl, 0}); + } + + auto inner_field = m_ast.AddFieldToRecordType(class_clang_type, + llvm::StringRef("$variants$"), + inner_holder, eAccessPublic, 0); + + m_ast.CompleteTagDeclarationDefinition(inner_holder); + + layout_info.field_offsets.insert({inner_field, 0}); +} 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..4b0ae026bce7 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h @@ -0,0 +1,516 @@ +//===-- 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 LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_DWARFASTPARSERCLANG_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_DWARFASTPARSERCLANG_H + +#include "clang/AST/CharUnits.h" +#include "clang/AST/Type.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/SmallVector.h" + +#include "DWARFASTParser.h" +#include "DWARFDIE.h" +#include "DWARFDefines.h" +#include "DWARFFormValue.h" +#include "LogChannelDWARF.h" +#include "lldb/Core/PluginInterface.h" + +#include "Plugins/ExpressionParser/Clang/ClangASTImporter.h" +#include "Plugins/Language/ObjC/ObjCLanguage.h" +#include "Plugins/TypeSystem/Clang/TypeSystemClang.h" + +#include <optional> +#include <vector> + +namespace lldb_private { +class CompileUnit; +} +namespace lldb_private::plugin { +namespace dwarf { +class DWARFDebugInfoEntry; +class SymbolFileDWARF; +} // namespace dwarf +} // namespace lldb_private::plugin + +struct ParsedDWARFTypeAttributes; + +class DWARFASTParserClang : public lldb_private::plugin::dwarf::DWARFASTParser { +public: + DWARFASTParserClang(lldb_private::TypeSystemClang &ast); + + ~DWARFASTParserClang() override; + + // DWARFASTParser interface. + lldb::TypeSP + ParseTypeFromDWARF(const lldb_private::SymbolContext &sc, + const lldb_private::plugin::dwarf::DWARFDIE &die, + bool *type_is_new_ptr) override; + + lldb_private::ConstString ConstructDemangledNameFromDWARF( + const lldb_private::plugin::dwarf::DWARFDIE &die) override; + + lldb_private::Function * + ParseFunctionFromDWARF(lldb_private::CompileUnit &comp_unit, + const lldb_private::plugin::dwarf::DWARFDIE &die, + const lldb_private::AddressRange &func_range) override; + + bool + CompleteTypeFromDWARF(const lldb_private::plugin::dwarf::DWARFDIE &die, + lldb_private::Type *type, + lldb_private::CompilerType &compiler_type) override; + + lldb_private::CompilerDecl GetDeclForUIDFromDWARF( + const lldb_private::plugin::dwarf::DWARFDIE &die) override; + + void EnsureAllDIEsInDeclContextHaveBeenParsed( + lldb_private::CompilerDeclContext decl_context) override; + + lldb_private::CompilerDeclContext GetDeclContextForUIDFromDWARF( + const lldb_private::plugin::dwarf::DWARFDIE &die) override; + + lldb_private::CompilerDeclContext GetDeclContextContainingUIDFromDWARF( + const lldb_private::plugin::dwarf::DWARFDIE &die) override; + + lldb_private::ClangASTImporter &GetClangASTImporter(); + + /// Extracts an value for a given Clang integer type from a DWARFFormValue. + /// + /// \param int_type The Clang type that defines the bit size and signedness + /// of the integer that should be extracted. Has to be either + /// an integer type or an enum type. For enum types the + /// underlying integer type will be considered as the + /// expected integer type that should be extracted. + /// \param form_value The DWARFFormValue that contains the integer value. + /// \return An APInt containing the same integer value as the given + /// DWARFFormValue with the bit width of the given integer type. + /// Returns an error if the value in the DWARFFormValue does not fit + /// into the given integer type or the integer type isn't supported. + llvm::Expected<llvm::APInt> ExtractIntFromFormValue( + const lldb_private::CompilerType &int_type, + const lldb_private::plugin::dwarf::DWARFFormValue &form_value) const; + + /// Returns the template parameters of a class DWARFDIE as a string. + /// + /// This is mostly useful for -gsimple-template-names which omits template + /// parameters from the DIE name and instead always adds template parameter + /// children DIEs. + /// + /// \param die The struct/class DWARFDIE containing template parameters. + /// \return A string, including surrounding '<>', of the template parameters. + /// If the DIE's name already has '<>', returns an empty string because + /// it's assumed that the caller is using the DIE name anyway. + std::string GetDIEClassTemplateParams( + const lldb_private::plugin::dwarf::DWARFDIE &die) override; + + void MapDeclDIEToDefDIE(const lldb_private::plugin::dwarf::DWARFDIE &decl_die, + const lldb_private::plugin::dwarf::DWARFDIE &def_die); + +protected: + /// Protected typedefs and members. + /// @{ + class DelayedAddObjCClassProperty; + typedef std::vector<DelayedAddObjCClassProperty> DelayedPropertyList; + + typedef llvm::DenseMap< + const lldb_private::plugin::dwarf::DWARFDebugInfoEntry *, + clang::DeclContext *> + DIEToDeclContextMap; + typedef std::multimap<const clang::DeclContext *, + const lldb_private::plugin::dwarf::DWARFDIE> + DeclContextToDIEMap; + typedef llvm::DenseMap< + const lldb_private::plugin::dwarf::DWARFDebugInfoEntry *, + lldb_private::OptionalClangModuleID> + DIEToModuleMap; + typedef llvm::DenseMap< + const lldb_private::plugin::dwarf::DWARFDebugInfoEntry *, clang::Decl *> + DIEToDeclMap; + + lldb_private::TypeSystemClang &m_ast; + DIEToDeclMap m_die_to_decl; + DIEToDeclContextMap m_die_to_decl_ctx; + DeclContextToDIEMap m_decl_ctx_to_die; + DIEToModuleMap m_die_to_module; + std::unique_ptr<lldb_private::ClangASTImporter> m_clang_ast_importer_up; + /// @} + + clang::DeclContext * + GetDeclContextForBlock(const lldb_private::plugin::dwarf::DWARFDIE &die); + + clang::BlockDecl * + ResolveBlockDIE(const lldb_private::plugin::dwarf::DWARFDIE &die); + + clang::NamespaceDecl * + ResolveNamespaceDIE(const lldb_private::plugin::dwarf::DWARFDIE &die); + + /// Returns the namespace decl that a DW_TAG_imported_declaration imports. + /// + /// \param[in] die The import declaration to resolve. If the DIE is not a + /// DW_TAG_imported_declaration the behaviour is undefined. + /// + /// \returns The decl corresponding to the namespace that the specified + /// 'die' imports. If the imported entity is not a namespace + /// or another import declaration, returns nullptr. If an error + /// occurs, returns nullptr. + clang::NamespaceDecl *ResolveImportedDeclarationDIE( + const lldb_private::plugin::dwarf::DWARFDIE &die); + + bool ParseTemplateDIE(const lldb_private::plugin::dwarf::DWARFDIE &die, + lldb_private::TypeSystemClang::TemplateParameterInfos + &template_param_infos); + + bool ParseTemplateParameterInfos( + const lldb_private::plugin::dwarf::DWARFDIE &parent_die, + lldb_private::TypeSystemClang::TemplateParameterInfos + &template_param_infos); + + void GetUniqueTypeNameAndDeclaration( + const lldb_private::plugin::dwarf::DWARFDIE &die, + lldb::LanguageType language, lldb_private::ConstString &unique_typename, + lldb_private::Declaration &decl_declaration); + + bool ParseChildMembers( + const lldb_private::plugin::dwarf::DWARFDIE &die, + lldb_private::CompilerType &class_compiler_type, + std::vector<std::unique_ptr<clang::CXXBaseSpecifier>> &base_classes, + std::vector<lldb_private::plugin::dwarf::DWARFDIE> &member_function_dies, + std::vector<lldb_private::plugin::dwarf::DWARFDIE> &contained_type_dies, + DelayedPropertyList &delayed_properties, + const lldb::AccessType default_accessibility, + lldb_private::ClangASTImporter::LayoutInfo &layout_info); + + size_t + ParseChildParameters(clang::DeclContext *containing_decl_ctx, + const lldb_private::plugin::dwarf::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 lldb_private::plugin::dwarf::DWARFDIE &parent_die); + + /// Parse a structure, class, or union type DIE. + lldb::TypeSP + ParseStructureLikeDIE(const lldb_private::SymbolContext &sc, + const lldb_private::plugin::dwarf::DWARFDIE &die, + ParsedDWARFTypeAttributes &attrs); + + clang::Decl * + GetClangDeclForDIE(const lldb_private::plugin::dwarf::DWARFDIE &die); + + clang::DeclContext * + GetClangDeclContextForDIE(const lldb_private::plugin::dwarf::DWARFDIE &die); + + clang::DeclContext *GetClangDeclContextContainingDIE( + const lldb_private::plugin::dwarf::DWARFDIE &die, + lldb_private::plugin::dwarf::DWARFDIE *decl_ctx_die); + lldb_private::OptionalClangModuleID + GetOwningClangModule(const lldb_private::plugin::dwarf::DWARFDIE &die); + + bool CopyUniqueClassMethodTypes( + const lldb_private::plugin::dwarf::DWARFDIE &src_class_die, + const lldb_private::plugin::dwarf::DWARFDIE &dst_class_die, + lldb_private::Type *class_type, + std::vector<lldb_private::plugin::dwarf::DWARFDIE> &failures); + + clang::DeclContext *GetCachedClangDeclContextForDIE( + const lldb_private::plugin::dwarf::DWARFDIE &die); + + void LinkDeclContextToDIE(clang::DeclContext *decl_ctx, + const lldb_private::plugin::dwarf::DWARFDIE &die); + + void LinkDeclToDIE(clang::Decl *decl, + const lldb_private::plugin::dwarf::DWARFDIE &die); + + /// If \p type_sp is valid, calculate and set its symbol context scope, and + /// update the type list for its backing symbol file. + /// + /// Returns \p type_sp. + lldb::TypeSP UpdateSymbolContextScopeForType( + const lldb_private::SymbolContext &sc, + const lldb_private::plugin::dwarf::DWARFDIE &die, lldb::TypeSP type_sp); + + /// Follow Clang Module Skeleton CU references to find a type definition. + lldb::TypeSP + ParseTypeFromClangModule(const lldb_private::SymbolContext &sc, + const lldb_private::plugin::dwarf::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 lldb_private::plugin::dwarf::DWARFDIE &die); + + static bool classof(const DWARFASTParser *Parser) { + return Parser->GetKind() == Kind::DWARFASTParserClang; + } + +private: + struct FieldInfo { + uint64_t bit_size = 0; + uint64_t bit_offset = 0; + bool is_bitfield = false; + bool is_artificial = false; + + FieldInfo() = default; + + void SetIsBitfield(bool flag) { is_bitfield = flag; } + bool IsBitfield() { return is_bitfield; } + + void SetIsArtificial(bool flag) { is_artificial = flag; } + bool IsArtificial() const { return is_artificial; } + + bool NextBitfieldOffsetIsValid(const uint64_t next_bit_offset) const { + // 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; + } + }; + + /// Parsed form of all attributes that are relevant for parsing type members. + struct MemberAttributes { + explicit MemberAttributes( + const lldb_private::plugin::dwarf::DWARFDIE &die, + const lldb_private::plugin::dwarf::DWARFDIE &parent_die, + lldb::ModuleSP module_sp); + const char *name = nullptr; + /// Indicates how many bits into the word (according to the host endianness) + /// the low-order bit of the field starts. Can be negative. + int64_t bit_offset = 0; + /// Indicates the size of the field in bits. + size_t bit_size = 0; + uint64_t data_bit_offset = UINT64_MAX; + lldb::AccessType accessibility = lldb::eAccessNone; + std::optional<uint64_t> byte_size; + std::optional<lldb_private::plugin::dwarf::DWARFFormValue> const_value_form; + lldb_private::plugin::dwarf::DWARFFormValue encoding_form; + /// Indicates the byte offset of the word from the base address of the + /// structure. + uint32_t member_byte_offset = UINT32_MAX; + bool is_artificial = false; + bool is_declaration = false; + }; + + /// Returns 'true' if we should create an unnamed bitfield + /// and add it to the parser's current AST. + /// + /// \param[in] last_field_info FieldInfo of the previous DW_TAG_member + /// we parsed. + /// \param[in] last_field_end Offset (in bits) where the last parsed field + /// ended. + /// \param[in] this_field_info FieldInfo of the current DW_TAG_member + /// being parsed. + /// \param[in] layout_info Layout information of all decls parsed by the + /// current parser. + bool ShouldCreateUnnamedBitfield( + FieldInfo const &last_field_info, uint64_t last_field_end, + FieldInfo const &this_field_info, + lldb_private::ClangASTImporter::LayoutInfo const &layout_info) const; + + /// Parses a DW_TAG_APPLE_property DIE and appends the parsed data to the + /// list of delayed Objective-C properties. + /// + /// Note: The delayed property needs to be finalized to actually create the + /// property declarations in the module AST. + /// + /// \param die The DW_TAG_APPLE_property DIE that will be parsed. + /// \param parent_die The parent DIE. + /// \param class_clang_type The Objective-C class that will contain the + /// created property. + /// \param delayed_properties The list of delayed properties that the result + /// will be appended to. + void + ParseObjCProperty(const lldb_private::plugin::dwarf::DWARFDIE &die, + const lldb_private::plugin::dwarf::DWARFDIE &parent_die, + const lldb_private::CompilerType &class_clang_type, + DelayedPropertyList &delayed_properties); + + void + ParseSingleMember(const lldb_private::plugin::dwarf::DWARFDIE &die, + const lldb_private::plugin::dwarf::DWARFDIE &parent_die, + const lldb_private::CompilerType &class_clang_type, + lldb::AccessType default_accessibility, + lldb_private::ClangASTImporter::LayoutInfo &layout_info, + FieldInfo &last_field_info); + + /// If the specified 'die' represents a static data member, creates + /// a 'clang::VarDecl' for it and attaches it to specified parent + /// 'class_clang_type'. + /// + /// \param[in] die The member declaration we want to create a + /// clang::VarDecl for. + /// + /// \param[in] attrs The parsed attributes for the specified 'die'. + /// + /// \param[in] class_clang_type The parent RecordType of the static + /// member this function will create. + void CreateStaticMemberVariable( + const lldb_private::plugin::dwarf::DWARFDIE &die, + const MemberAttributes &attrs, + const lldb_private::CompilerType &class_clang_type); + + bool CompleteRecordType(const lldb_private::plugin::dwarf::DWARFDIE &die, + lldb_private::Type *type, + lldb_private::CompilerType &clang_type); + bool CompleteEnumType(const lldb_private::plugin::dwarf::DWARFDIE &die, + lldb_private::Type *type, + lldb_private::CompilerType &clang_type); + + lldb::TypeSP + ParseTypeModifier(const lldb_private::SymbolContext &sc, + const lldb_private::plugin::dwarf::DWARFDIE &die, + ParsedDWARFTypeAttributes &attrs); + lldb::TypeSP ParseEnum(const lldb_private::SymbolContext &sc, + const lldb_private::plugin::dwarf::DWARFDIE &die, + ParsedDWARFTypeAttributes &attrs); + lldb::TypeSP ParseSubroutine(const lldb_private::plugin::dwarf::DWARFDIE &die, + const ParsedDWARFTypeAttributes &attrs); + + /// Helper function called by \ref ParseSubroutine when parsing ObjC-methods. + /// + /// \param[in] objc_method Name of the ObjC method being parsed. + /// + /// \param[in] die The DIE that represents the ObjC method being parsed. + /// + /// \param[in] clang_type The CompilerType representing the function prototype + /// of the ObjC method being parsed. + /// + /// \param[in] attrs DWARF attributes for \ref die. + /// + /// \param[in] is_variadic Is true iff we're parsing a variadic method. + /// + /// \returns true on success + bool + ParseObjCMethod(const lldb_private::ObjCLanguage::MethodName &objc_method, + const lldb_private::plugin::dwarf::DWARFDIE &die, + lldb_private::CompilerType clang_type, + const ParsedDWARFTypeAttributes &attrs, bool is_variadic); + + /// Helper function called by \ref ParseSubroutine when parsing C++ methods. + /// + /// \param[in] die The DIE that represents the C++ method being parsed. + /// + /// \param[in] clang_type The CompilerType representing the function prototype + /// of the C++ method being parsed. + /// + /// \param[in] attrs DWARF attributes for \ref die. + /// + /// \param[in] decl_ctx_die The DIE representing the DeclContext of the C++ + /// method being parsed. + /// + /// \param[in] is_static Is true iff we're parsing a static method. + /// + /// \param[out] ignore_containing_context Will get set to true if the caller + /// should treat this C++ method as-if it was not a C++ method. + /// Currently used as a hack to work around templated C++ methods + /// causing class definitions to mismatch between CUs. + /// + /// \returns A pair of <bool, TypeSP>. The first element is 'true' on success. + /// The second element is non-null if we have previously parsed this + /// method (a null TypeSP does not indicate failure). + std::pair<bool, lldb::TypeSP> + ParseCXXMethod(const lldb_private::plugin::dwarf::DWARFDIE &die, + lldb_private::CompilerType clang_type, + const ParsedDWARFTypeAttributes &attrs, + const lldb_private::plugin::dwarf::DWARFDIE &decl_ctx_die, + bool is_static, bool &ignore_containing_context); + + lldb::TypeSP ParseArrayType(const lldb_private::plugin::dwarf::DWARFDIE &die, + const ParsedDWARFTypeAttributes &attrs); + lldb::TypeSP + ParsePointerToMemberType(const lldb_private::plugin::dwarf::DWARFDIE &die, + const ParsedDWARFTypeAttributes &attrs); + + /// Parses a DW_TAG_inheritance DIE into a base/super class. + /// + /// \param die The DW_TAG_inheritance DIE to parse. + /// \param parent_die The parent DIE of the given DIE. + /// \param class_clang_type The C++/Objective-C class representing parent_die. + /// For an Objective-C class this method sets the super class on success. For + /// a C++ class this will *not* add the result as a base class. + /// \param default_accessibility The default accessibility that is given to + /// base classes if they don't have an explicit accessibility set. + /// \param module_sp The current Module. + /// \param base_classes The list of C++ base classes that will be appended + /// with the parsed base class on success. + /// \param layout_info The layout information that will be updated for C++ + /// base classes with the base offset. + void ParseInheritance( + const lldb_private::plugin::dwarf::DWARFDIE &die, + const lldb_private::plugin::dwarf::DWARFDIE &parent_die, + const lldb_private::CompilerType class_clang_type, + const lldb::AccessType default_accessibility, + const lldb::ModuleSP &module_sp, + std::vector<std::unique_ptr<clang::CXXBaseSpecifier>> &base_classes, + lldb_private::ClangASTImporter::LayoutInfo &layout_info); + + /// Parses DW_TAG_variant_part DIE into a structure that encodes all variants + /// Note that this is currently being emitted by rustc and not Clang + /// \param die DW_TAG_variant_part DIE to parse + /// \param parent_die The parent DW_TAG_structure_type to parse + /// \param class_clang_type The Rust struct representing parent_die. + /// \param default_accesibility The default accessibility that is given to + /// base classes if they don't have an explicit accessibility set + /// \param layout_info The layout information that will be updated for + // base classes with the base offset + void + ParseRustVariantPart(lldb_private::plugin::dwarf::DWARFDIE &die, + const lldb_private::plugin::dwarf::DWARFDIE &parent_die, + lldb_private::CompilerType &class_clang_type, + const lldb::AccessType default_accesibility, + lldb_private::ClangASTImporter::LayoutInfo &layout_info); +}; + +/// 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 ParsedDWARFTypeAttributes { + explicit ParsedDWARFTypeAttributes( + const lldb_private::plugin::dwarf::DWARFDIE &die); + + lldb::AccessType accessibility = lldb::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; + bool is_objc_direct_call = false; + bool exports_symbols = false; + clang::StorageClass storage = clang::SC_None; + const char *mangled_name = nullptr; + lldb_private::ConstString name; + lldb_private::Declaration decl; + lldb_private::plugin::dwarf::DWARFDIE object_pointer; + lldb_private::plugin::dwarf::DWARFFormValue abstract_origin; + lldb_private::plugin::dwarf::DWARFFormValue containing_type; + lldb_private::plugin::dwarf::DWARFFormValue signature; + lldb_private::plugin::dwarf::DWARFFormValue specification; + lldb_private::plugin::dwarf::DWARFFormValue type; + lldb::LanguageType class_language = lldb::eLanguageTypeUnknown; + std::optional<uint64_t> byte_size; + std::optional<uint64_t> alignment; + size_t calling_convention = llvm::dwarf::DW_CC_normal; + uint32_t bit_stride = 0; + uint32_t byte_stride = 0; + uint32_t encoding = 0; + clang::RefQualifierKind ref_qual = + clang::RQ_None; ///< Indicates ref-qualifier of + ///< C++ member function if present. + ///< Is RQ_None otherwise. +}; + +#endif // LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_DWARFASTPARSERCLANG_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..3d35775e081e --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFAttribute.cpp @@ -0,0 +1,66 @@ +//===-- DWARFAttribute.cpp ------------------------------------------------===// +// +// 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" + +using namespace lldb_private::dwarf; +using namespace lldb_private::plugin::dwarf; + +DWARFAttributes::DWARFAttributes() : m_infos() {} + +DWARFAttributes::~DWARFAttributes() = default; + +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 DWARFFormValue &form_value, + dw_offset_t attr_die_offset, dw_attr_t attr) { + AttributeValue attr_value = {const_cast<DWARFUnit *>(form_value.GetUnit()), + attr_die_offset, + {attr, form_value.Form(), form_value.Value()}}; + 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)); + if (form_value.Form() == DW_FORM_implicit_const) { + form_value.SetValue(ValueAtIndex(i)); + return true; + } + 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..e05ccc980d92 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFAttribute.h @@ -0,0 +1,81 @@ +//===-- 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 LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_DWARFATTRIBUTE_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_DWARFATTRIBUTE_H + +#include "DWARFDefines.h" +#include "DWARFFormValue.h" +#include "llvm/ADT/SmallVector.h" +#include <vector> + +namespace lldb_private::plugin { +namespace dwarf { +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) {} + + dw_attr_t get_attr() const { return m_attr; } + dw_form_t get_form() const { return m_form; } + DWARFFormValue::ValueType get_value() const { return m_value; } + void get(dw_attr_t &attr, dw_form_t &form, + DWARFFormValue::ValueType &val) const { + attr = m_attr; + form = m_form; + val = m_value; + } + +protected: + dw_attr_t m_attr; + dw_form_t m_form; + DWARFFormValue::ValueType m_value; +}; + +class DWARFAttributes { +public: + DWARFAttributes(); + ~DWARFAttributes(); + + void Append(const DWARFFormValue &form_value, dw_offset_t attr_die_offset, + dw_attr_t attr); + 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_form_t FormAtIndex(uint32_t i) const { return m_infos[i].attr.get_form(); } + DWARFFormValue::ValueType ValueAtIndex(uint32_t i) const { + return m_infos[i].attr.get_value(); + } + 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 { + 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; +}; +} // namespace dwarf +} // namespace lldb_private::plugin + +#endif // LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_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..c2ebeed4c860 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFBaseDIE.cpp @@ -0,0 +1,136 @@ +//===-- DWARFBaseDIE.cpp --------------------------------------------------===// +// +// 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" +#include "lldb/Utility/Log.h" +#include <optional> + +using namespace lldb_private; +using namespace lldb_private::plugin::dwarf; + +std::optional<DIERef> DWARFBaseDIE::GetDIERef() const { + if (!IsValid()) + return std::nullopt; + + return DIERef(m_cu->GetSymbolFileDWARF().GetFileIndex(), + m_cu->GetDebugSection(), m_die->GetOffset()); +} + +dw_tag_t DWARFBaseDIE::Tag() const { + if (m_die) + return m_die->Tag(); + else + return llvm::dwarf::DW_TAG_null; +} + +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; +} + +std::optional<uint64_t> +DWARFBaseDIE::GetAttributeValueAsOptionalUnsigned(const dw_attr_t attr) const { + if (IsValid()) + return m_die->GetAttributeValueAsOptionalUnsigned(GetCU(), attr); + return std::nullopt; +} + +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 { + const std::optional<DIERef> &ref = this->GetDIERef(); + if (ref) + return ref->get_id(); + + return LLDB_INVALID_UID; +} + +const char *DWARFBaseDIE::GetName() const { + if (IsValid()) + return m_die->GetName(m_cu); + else + return nullptr; +} + +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; +} + +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); +} + +DWARFAttributes DWARFBaseDIE::GetAttributes(Recurse recurse) const { + if (IsValid()) + return m_die->GetAttributes(m_cu, recurse); + return DWARFAttributes(); +} + +namespace lldb_private::plugin { +namespace dwarf { +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); +} +} // namespace dwarf +} // namespace lldb_private::plugin + +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..235343d22712 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFBaseDIE.h @@ -0,0 +1,130 @@ +//===-- 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 LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_DWARFBASEDIE_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_DWARFBASEDIE_H + +#include "lldb/Core/dwarf.h" +#include "lldb/lldb-types.h" + +#include "llvm/Support/Error.h" +#include <optional> + +namespace lldb_private::plugin { +namespace dwarf { +class DIERef; +class DWARFASTParser; +class DWARFAttributes; +class DWARFUnit; +class DWARFDebugInfoEntry; +class DWARFDeclContext; +class SymbolFileDWARF; + +class DWARFBaseDIE { +public: + DWARFBaseDIE() = default; + + 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; } + + std::optional<DIERef> GetDIERef() 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 DWARFDataExtractor &GetData() const; + + // Accessing information about a DIE + dw_tag_t Tag() 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::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; + + std::optional<uint64_t> + GetAttributeValueAsOptionalUnsigned(const dw_attr_t attr) const; + + uint64_t GetAttributeValueAsAddress(const dw_attr_t attr, + uint64_t fail_value) const; + + enum class Recurse : bool { no, yes }; + DWARFAttributes GetAttributes(Recurse recurse = Recurse::yes) const; + +protected: + DWARFUnit *m_cu = nullptr; + DWARFDebugInfoEntry *m_die = nullptr; +}; + +bool operator==(const DWARFBaseDIE &lhs, const DWARFBaseDIE &rhs); +bool operator!=(const DWARFBaseDIE &lhs, const DWARFBaseDIE &rhs); +} // namespace dwarf +} // namespace lldb_private::plugin + +#endif // LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_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..ec4c297cf7e1 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.cpp @@ -0,0 +1,108 @@ +//===-- DWARFCompileUnit.cpp ----------------------------------------------===// +// +// 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; +using namespace lldb_private::plugin::dwarf; + +void DWARFCompileUnit::Dump(Stream *s) const { + s->Format( + + "{0:x16}: Compile Unit: length = {1:x8}, version = {2:x}, " + "abbr_offset = {3:x8}, addr_size = {4:x2} (next CU at " + "[{5:x16}])\n", + GetOffset(), GetLength(), GetVersion(), (uint32_t)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 contains ranges information. + const DWARFDebugInfoEntry *die = GetUnitDIEPtrOnly(); + + const dw_offset_t cu_offset = GetOffset(); + if (die) { + DWARFRangeList ranges = + die->GetAttributeAddressRanges(this, /*check_hi_lo_pc=*/true); + for (const DWARFRangeList::Entry &range : ranges) + debug_aranges->AppendRange(cu_offset, range.GetRangeBase(), + range.GetRangeEnd()); + + if (!ranges.IsEmpty()) + return; + } + + if (debug_aranges->GetNumRanges() == num_debug_aranges) { + // We got nothing from the debug info, try to build the arange table from + // the debug map OSO aranges. + 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) { + auto *cu_info = + debug_map_sym_file->GetCompileUnitInfo(&GetSymbolFileDWARF()); + // If there are extra compile units the OSO entries aren't a reliable + // source of information. + if (cu_info->compile_units_sps.empty()) + 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()); + } + } + } + } +} + +DWARFCompileUnit &DWARFCompileUnit::GetNonSkeletonUnit() { + return llvm::cast<DWARFCompileUnit>(DWARFUnit::GetNonSkeletonUnit()); +} + +DWARFDIE DWARFCompileUnit::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(); +} 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..b8344f548ac3 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.h @@ -0,0 +1,48 @@ +//===-- 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 LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_DWARFCOMPILEUNIT_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_DWARFCOMPILEUNIT_H + +#include "DWARFUnit.h" +#include "llvm/Support/Error.h" + +namespace llvm { +class DWARFAbbreviationDeclarationSet; +} // namespace llvm + +namespace lldb_private::plugin { +namespace dwarf { +class DWARFCompileUnit : public DWARFUnit { +public: + void BuildAddressRangeTable(DWARFDebugAranges *debug_aranges) override; + + void Dump(Stream *s) const override; + + static bool classof(const DWARFUnit *unit) { return !unit->IsTypeUnit(); } + + DWARFCompileUnit &GetNonSkeletonUnit(); + + DWARFDIE LookupAddress(const dw_addr_t address); + +private: + DWARFCompileUnit(SymbolFileDWARF &dwarf, lldb::user_id_t uid, + const llvm::DWARFUnitHeader &header, + const llvm::DWARFAbbreviationDeclarationSet &abbrevs, + DIERef::Section section, bool is_dwo) + : DWARFUnit(dwarf, uid, header, abbrevs, section, is_dwo) {} + + DWARFCompileUnit(const DWARFCompileUnit &) = delete; + const DWARFCompileUnit &operator=(const DWARFCompileUnit &) = delete; + + friend class DWARFUnit; +}; +} // namespace dwarf +} // namespace lldb_private::plugin + +#endif // LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_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..e3872dc626be --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFContext.cpp @@ -0,0 +1,152 @@ +//===-- DWARFContext.cpp --------------------------------------------------===// +// +// 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" +#include <optional> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::plugin::dwarf; + +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(std::optional<SectionType> main_section_type, + std::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 if (main_section_type) + data.data = LoadSection(m_main_section_list, *main_section_type); + }); + return data.data; +} + +const DWARFDataExtractor &DWARFContext::getOrLoadCuIndexData() { + return LoadOrGetSection(std::nullopt, eSectionTypeDWARFDebugCuIndex, + m_data_debug_cu_index); +} + +const DWARFDataExtractor &DWARFContext::getOrLoadTuIndexData() { + return LoadOrGetSection(std::nullopt, eSectionTypeDWARFDebugTuIndex, + m_data_debug_tu_index); +} + +const DWARFDataExtractor &DWARFContext::getOrLoadAbbrevData() { + return LoadOrGetSection(eSectionTypeDWARFDebugAbbrev, + eSectionTypeDWARFDebugAbbrevDwo, m_data_debug_abbrev); +} + +const DWARFDataExtractor &DWARFContext::getOrLoadArangesData() { + return LoadOrGetSection(eSectionTypeDWARFDebugAranges, std::nullopt, + m_data_debug_aranges); +} + +const DWARFDataExtractor &DWARFContext::getOrLoadAddrData() { + return LoadOrGetSection(eSectionTypeDWARFDebugAddr, std::nullopt, + m_data_debug_addr); +} + +const DWARFDataExtractor &DWARFContext::getOrLoadDebugInfoData() { + return LoadOrGetSection(eSectionTypeDWARFDebugInfo, + eSectionTypeDWARFDebugInfoDwo, m_data_debug_info); +} + +const DWARFDataExtractor &DWARFContext::getOrLoadLineData() { + return LoadOrGetSection(eSectionTypeDWARFDebugLine, std::nullopt, + m_data_debug_line); +} + +const DWARFDataExtractor &DWARFContext::getOrLoadLineStrData() { + return LoadOrGetSection(eSectionTypeDWARFDebugLineStr, std::nullopt, + m_data_debug_line_str); +} + +const DWARFDataExtractor &DWARFContext::getOrLoadLocData() { + return LoadOrGetSection(eSectionTypeDWARFDebugLoc, + eSectionTypeDWARFDebugLocDwo, m_data_debug_loc); +} + +const DWARFDataExtractor &DWARFContext::getOrLoadLocListsData() { + return LoadOrGetSection(eSectionTypeDWARFDebugLocLists, + eSectionTypeDWARFDebugLocListsDwo, + m_data_debug_loclists); +} + +const DWARFDataExtractor &DWARFContext::getOrLoadMacroData() { + return LoadOrGetSection(eSectionTypeDWARFDebugMacro, std::nullopt, + m_data_debug_macro); +} + +const DWARFDataExtractor &DWARFContext::getOrLoadRangesData() { + return LoadOrGetSection(eSectionTypeDWARFDebugRanges, std::nullopt, + m_data_debug_ranges); +} + +const DWARFDataExtractor &DWARFContext::getOrLoadRngListsData() { + return LoadOrGetSection(eSectionTypeDWARFDebugRngLists, + eSectionTypeDWARFDebugRngListsDwo, + 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 = [&](llvm::StringRef name, DWARFDataExtractor data) { + // Set the address size the first time we see it. + if (addr_size == 0) + addr_size = data.GetAddressByteSize(); + + section_map.try_emplace( + name, llvm::MemoryBuffer::getMemBuffer(toStringRef(data.GetData()), + name, false)); + }; + + AddSection("debug_line_str", getOrLoadLineStrData()); + AddSection("debug_cu_index", getOrLoadCuIndexData()); + AddSection("debug_tu_index", getOrLoadTuIndexData()); + if (isDwo()) { + AddSection("debug_info.dwo", getOrLoadDebugInfoData()); + AddSection("debug_types.dwo", getOrLoadDebugTypesData()); + } + 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..87c6eb209337 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFContext.h @@ -0,0 +1,85 @@ +//===-- 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_SOURCE_PLUGINS_SYMBOLFILE_DWARF_DWARFCONTEXT_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_DWARFCONTEXT_H + +#include "DWARFDataExtractor.h" +#include "lldb/Core/Section.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/Support/Threading.h" +#include <memory> +#include <optional> + +namespace lldb_private::plugin { +namespace dwarf { +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_cu_index; + SectionData m_data_debug_info; + SectionData m_data_debug_line; + SectionData m_data_debug_line_str; + SectionData m_data_debug_loc; + SectionData m_data_debug_loclists; + 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_tu_index; + SectionData m_data_debug_types; + + const DWARFDataExtractor & + LoadOrGetSection(std::optional<lldb::SectionType> main_section_type, + std::optional<lldb::SectionType> dwo_section_type, + SectionData &data); + + const DWARFDataExtractor &getOrLoadCuIndexData(); + const DWARFDataExtractor &getOrLoadTuIndexData(); + +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 &getOrLoadLocData(); + const DWARFDataExtractor &getOrLoadLocListsData(); + const DWARFDataExtractor &getOrLoadMacroData(); + const DWARFDataExtractor &getOrLoadRangesData(); + const DWARFDataExtractor &getOrLoadRngListsData(); + const DWARFDataExtractor &getOrLoadStrData(); + const DWARFDataExtractor &getOrLoadStrOffsetsData(); + const DWARFDataExtractor &getOrLoadDebugTypesData(); + + bool isDwo() { return m_dwo_section_list != nullptr; } + + llvm::DWARFContext &GetAsLLVM(); +}; +} // namespace dwarf +} // namespace lldb_private::plugin + +#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..fb32e2adeb3f --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.cpp @@ -0,0 +1,593 @@ +//===-- DWARFDIE.cpp ------------------------------------------------------===// +// +// 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" +#include "lldb/Symbol/Type.h" + +#include "llvm/ADT/iterator.h" +#include "llvm/BinaryFormat/Dwarf.h" + +using namespace lldb_private; +using namespace lldb_private::dwarf; +using namespace lldb_private::plugin::dwarf; + +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 llvm::iterator_facade_base< + ElaboratingDIEIterator, std::input_iterator_tag, DWARFDIE, + std::ptrdiff_t, DWARFDIE *, 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<DWARFDebugInfoEntry *, 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.GetDIE()).second) + m_worklist.push_back(d); + } + } + +public: + /// An iterator starting at die d. + explicit ElaboratingDIEIterator(DWARFDIE d) : m_worklist(1, d) {} + + /// End marker + ElaboratingDIEIterator() = default; + + const DWARFDIE &operator*() const { return m_worklist.back(); } + ElaboratingDIEIterator &operator++() { + Next(); + return *this; + } + + 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(); + } +}; + +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 address) const { + if (!IsValid()) + return DWARFDIE(); + + DWARFDIE result; + bool check_children = false; + bool match_addr_range = false; + switch (Tag()) { + case DW_TAG_class_type: + case DW_TAG_namespace: + case DW_TAG_structure_type: + case DW_TAG_common_block: + check_children = true; + break; + case DW_TAG_compile_unit: + case DW_TAG_module: + case DW_TAG_catch_block: + case DW_TAG_subprogram: + case DW_TAG_try_block: + case DW_TAG_partial_unit: + match_addr_range = true; + break; + case DW_TAG_lexical_block: + case DW_TAG_inlined_subroutine: + check_children = true; + match_addr_range = true; + break; + default: + break; + } + + if (match_addr_range) { + DWARFRangeList ranges = + m_die->GetAttributeAddressRanges(m_cu, /*check_hi_lo_pc=*/true); + if (ranges.FindEntryThatContains(address)) { + check_children = true; + switch (Tag()) { + default: + break; + + case DW_TAG_inlined_subroutine: // Inlined Function + case DW_TAG_lexical_block: // Block { } in code + result = *this; + break; + } + } else { + check_children = false; + } + } + + if (check_children) { + for (DWARFDIE child : children()) { + if (DWARFDIE child_result = child.LookupDeepestBlock(address)) + return child_result; + } + } + return result; +} + +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; +} + +// 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; + case DW_TAG_LLVM_ptrauth_type: { + unsigned key = GetAttributeValueAsUnsigned(DW_AT_LLVM_ptrauth_key, 0); + bool isAddressDiscriminated = GetAttributeValueAsUnsigned( + DW_AT_LLVM_ptrauth_address_discriminated, 0); + unsigned extraDiscriminator = + GetAttributeValueAsUnsigned(DW_AT_LLVM_ptrauth_extra_discriminator, 0); + bool isaPointer = + GetAttributeValueAsUnsigned(DW_AT_LLVM_ptrauth_isa_pointer, 0); + bool authenticatesNullValues = GetAttributeValueAsUnsigned( + DW_AT_LLVM_ptrauth_authenticates_null_values, 0); + unsigned authenticationMode = + GetAttributeValueAsUnsigned(DW_AT_LLVM_ptrauth_authentication_mode, 3); + + s.Printf("__ptrauth(%d, %d, 0x0%x, %d, %d, %d)", key, + isAddressDiscriminated, extraDiscriminator, isaPointer, + authenticatesNullValues, authenticationMode); + 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; +} + +static void GetDeclContextImpl(DWARFDIE die, + llvm::SmallSet<lldb::user_id_t, 4> &seen, + std::vector<CompilerContext> &context) { + // Stop if we hit a cycle. + while (die && seen.insert(die.GetID()).second) { + // Handle outline member function DIEs by following the specification. + if (DWARFDIE spec = die.GetReferencedDIE(DW_AT_specification)) { + die = spec; + continue; + } + // To find the name of a type in a type unit, we must follow the signature. + if (DWARFDIE spec = die.GetReferencedDIE(DW_AT_signature)) { + die = spec; + continue; + } + + // Add this DIE's contribution at the end of the chain. + auto push_ctx = [&](CompilerContextKind kind, llvm::StringRef name) { + context.push_back({kind, ConstString(name)}); + }; + switch (die.Tag()) { + case DW_TAG_module: + push_ctx(CompilerContextKind::Module, die.GetName()); + break; + case DW_TAG_namespace: + push_ctx(CompilerContextKind::Namespace, die.GetName()); + break; + case DW_TAG_class_type: + case DW_TAG_structure_type: + push_ctx(CompilerContextKind::ClassOrStruct, die.GetName()); + break; + case DW_TAG_union_type: + push_ctx(CompilerContextKind::Union, die.GetName()); + break; + case DW_TAG_enumeration_type: + push_ctx(CompilerContextKind::Enum, die.GetName()); + break; + case DW_TAG_subprogram: + push_ctx(CompilerContextKind::Function, die.GetName()); + break; + case DW_TAG_variable: + push_ctx(CompilerContextKind::Variable, die.GetPubname()); + break; + case DW_TAG_typedef: + push_ctx(CompilerContextKind::Typedef, die.GetName()); + break; + default: + break; + } + // Now process the parent. + die = die.GetParent(); + } +} + +std::vector<CompilerContext> DWARFDIE::GetDeclContext() const { + llvm::SmallSet<lldb::user_id_t, 4> seen; + std::vector<CompilerContext> context; + GetDeclContextImpl(*this, seen, context); + std::reverse(context.begin(), context.end()); + return context; +} + +static void GetTypeLookupContextImpl(DWARFDIE die, + llvm::SmallSet<lldb::user_id_t, 4> &seen, + std::vector<CompilerContext> &context) { + // Stop if we hit a cycle. + while (die && seen.insert(die.GetID()).second) { + // To find the name of a type in a type unit, we must follow the signature. + if (DWARFDIE spec = die.GetReferencedDIE(DW_AT_signature)) { + die = spec; + continue; + } + + // If there is no name, then there is no need to look anything up for this + // DIE. + const char *name = die.GetName(); + if (!name || !name[0]) + return; + + // Add this DIE's contribution at the end of the chain. + auto push_ctx = [&](CompilerContextKind kind, llvm::StringRef name) { + context.push_back({kind, ConstString(name)}); + }; + switch (die.Tag()) { + case DW_TAG_namespace: + push_ctx(CompilerContextKind::Namespace, die.GetName()); + break; + case DW_TAG_class_type: + case DW_TAG_structure_type: + push_ctx(CompilerContextKind::ClassOrStruct, die.GetName()); + break; + case DW_TAG_union_type: + push_ctx(CompilerContextKind::Union, die.GetName()); + break; + case DW_TAG_enumeration_type: + push_ctx(CompilerContextKind::Enum, die.GetName()); + break; + case DW_TAG_variable: + push_ctx(CompilerContextKind::Variable, die.GetPubname()); + break; + case DW_TAG_typedef: + push_ctx(CompilerContextKind::Typedef, die.GetName()); + break; + case DW_TAG_base_type: + push_ctx(CompilerContextKind::Builtin, name); + break; + // If any of the tags below appear in the parent chain, stop the decl + // context and return. Prior to these being in here, if a type existed in a + // namespace "a" like "a::my_struct", but we also have a function in that + // same namespace "a" which contained a type named "my_struct", both would + // return "a::my_struct" as the declaration context since the + // DW_TAG_subprogram would be skipped and its parent would be found. + case DW_TAG_compile_unit: + case DW_TAG_type_unit: + case DW_TAG_subprogram: + case DW_TAG_lexical_block: + case DW_TAG_inlined_subroutine: + return; + default: + break; + } + // Now process the parent. + die = die.GetParent(); + } +} + +std::vector<CompilerContext> DWARFDIE::GetTypeLookupContext() const { + llvm::SmallSet<lldb::user_id_t, 4> seen; + std::vector<CompilerContext> context; + GetTypeLookupContextImpl(*this, seen, context); + std::reverse(context.begin(), context.end()); + return context; +} + +static DWARFDeclContext GetDWARFDeclContextImpl(DWARFDIE die) { + DWARFDeclContext dwarf_decl_ctx; + while (die) { + const dw_tag_t tag = die.Tag(); + if (tag == DW_TAG_compile_unit || tag == DW_TAG_partial_unit) + break; + dwarf_decl_ctx.AppendDeclContext(tag, die.GetName()); + DWARFDIE parent_decl_ctx_die = die.GetParentDeclContextDIE(); + if (parent_decl_ctx_die == die) + break; + die = parent_decl_ctx_die; + } + return dwarf_decl_ctx; +} + +DWARFDeclContext DWARFDIE::GetDWARFDeclContext() const { + return GetDWARFDeclContextImpl(*this); +} + +static DWARFDIE GetParentDeclContextDIEImpl(DWARFDIE die) { + DWARFDIE orig_die = 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 (die != orig_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: + return die; + + default: + break; + } + } + + if (DWARFDIE spec_die = die.GetReferencedDIE(DW_AT_specification)) { + if (DWARFDIE decl_ctx_die = spec_die.GetParentDeclContextDIE()) + return decl_ctx_die; + } + + if (DWARFDIE abs_die = die.GetReferencedDIE(DW_AT_abstract_origin)) { + if (DWARFDIE decl_ctx_die = abs_die.GetParentDeclContextDIE()) + return decl_ctx_die; + } + + die = die.GetParent(); + } + return DWARFDIE(); +} + +DWARFDIE +DWARFDIE::GetParentDeclContextDIE() const { + return GetParentDeclContextDIEImpl(*this); +} + +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; +} + +bool DWARFDIE::GetDIENamesAndRanges( + const char *&name, const char *&mangled, DWARFRangeList &ranges, + std::optional<int> &decl_file, std::optional<int> &decl_line, + std::optional<int> &decl_column, std::optional<int> &call_file, + std::optional<int> &call_line, std::optional<int> &call_column, + lldb_private::DWARFExpressionList *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; +} + +llvm::iterator_range<DWARFDIE::child_iterator> DWARFDIE::children() const { + return llvm::make_range(child_iterator(*this), child_iterator()); +} 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..e1318953a384 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.h @@ -0,0 +1,145 @@ +//===-- 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 LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_DWARFDIE_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_DWARFDIE_H + +#include "DWARFBaseDIE.h" +#include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/iterator_range.h" + +namespace lldb_private::plugin { +namespace dwarf { +class DWARFDIE : public DWARFBaseDIE { +public: + class child_iterator; + using DWARFBaseDIE::DWARFBaseDIE; + + // Tests + bool IsStructUnionOrClass() const; + + bool IsMethod() const; + + // Accessors + + // Accessing information about a DIE + const char *GetMangledName() const; + + const char *GetPubname() const; + + using DWARFBaseDIE::GetName; + void GetName(Stream &s) const; + + void AppendTypeName(Stream &s) const; + + Type *ResolveType() const; + + // Resolve a type by UID using this DIE's DWARF file + 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; + + /// Return this DIE's decl context as it is needed to look up types + /// in Clang modules. This context will include any modules or functions that + /// the type is declared in so an exact module match can be efficiently made. + std::vector<CompilerContext> GetDeclContext() const; + + /// Get a context to a type so it can be looked up. + /// + /// This function uses the current DIE to fill in a CompilerContext array + /// that is suitable for type lookup for comparison to a TypeQuery's compiler + /// context (TypeQuery::GetContextRef()). If this DIE represents a named type, + /// it should fill out the compiler context with the type itself as the last + /// entry. The declaration context should be above the type and stop at an + /// appropriate time, like either the translation unit or at a function + /// context. This is designed to allow users to efficiently look for types + /// using a full or partial CompilerContext array. + std::vector<CompilerContext> GetTypeLookupContext() const; + + DWARFDeclContext GetDWARFDeclContext() 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, + std::optional<int> &decl_file, std::optional<int> &decl_line, + std::optional<int> &decl_column, std::optional<int> &call_file, + std::optional<int> &call_line, std::optional<int> &call_column, + DWARFExpressionList *frame_base) const; + + /// The range of all the children of this DIE. + llvm::iterator_range<child_iterator> children() const; +}; + +class DWARFDIE::child_iterator + : public llvm::iterator_facade_base<DWARFDIE::child_iterator, + std::forward_iterator_tag, DWARFDIE> { + /// The current child or an invalid DWARFDie. + DWARFDIE m_die; + +public: + child_iterator() = default; + child_iterator(const DWARFDIE &parent) : m_die(parent.GetFirstChild()) {} + bool operator==(const child_iterator &it) const { + // DWARFDIE's operator== differentiates between an invalid DWARFDIE that + // has a CU but no DIE and one that has neither CU nor DIE. The 'end' + // iterator could be default constructed, so explicitly allow + // (CU, (DIE)nullptr) == (nullptr, nullptr) -> true + if (!m_die.IsValid() && !it.m_die.IsValid()) + return true; + return m_die == it.m_die; + } + const DWARFDIE &operator*() const { + assert(m_die.IsValid() && "Derefencing invalid iterator?"); + return m_die; + } + DWARFDIE &operator*() { + assert(m_die.IsValid() && "Derefencing invalid iterator?"); + return m_die; + } + child_iterator &operator++() { + assert(m_die.IsValid() && "Incrementing invalid iterator?"); + m_die = m_die.GetSibling(); + return *this; + } +}; +} // namespace dwarf +} // namespace lldb_private::plugin + +#endif // LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_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..c5876502b8f2 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDataExtractor.cpp @@ -0,0 +1,34 @@ +//===-- DWARFDataExtractor.cpp --------------------------------------------===// +// +// 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::GetAsLLVMDWARF() const { + return llvm::DWARFDataExtractor(llvm::ArrayRef(GetDataStart(), GetByteSize()), + GetByteOrder() == lldb::eByteOrderLittle, + GetAddressByteSize()); +} +llvm::DataExtractor DWARFDataExtractor::GetAsLLVM() const { + return llvm::DataExtractor(llvm::ArrayRef(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..41b8e9ad0217 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDataExtractor.h @@ -0,0 +1,38 @@ +//===-- 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 LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_DWARFDATAEXTRACTOR_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_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 GetAsLLVMDWARF() const; + llvm::DataExtractor GetAsLLVM() const; +}; +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_DWARFDATAEXTRACTOR_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..8461b94abca6 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugArangeSet.cpp @@ -0,0 +1,176 @@ +//===-- DWARFDebugArangeSet.cpp -------------------------------------------===// +// +// 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 "LogChannelDWARF.h" +#include "llvm/Object/Error.h" +#include <cassert> + +using namespace lldb_private; +using namespace lldb_private::plugin::dwarf; + +DWARFDebugArangeSet::DWARFDebugArangeSet() + : m_offset(DW_INVALID_OFFSET), m_next_offset(DW_INVALID_OFFSET) {} + +void DWARFDebugArangeSet::Clear() { + m_offset = DW_INVALID_OFFSET; + m_next_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); + // The length could be 4 bytes or 12 bytes, so use the current offset to + // determine the next offset correctly. + if (m_header.length > 0) + m_next_offset = *offset_ptr + m_header.length; + else + m_next_offset = DW_INVALID_OFFSET; + 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 + // 4 - size looks plausible + // 5 - the arange tuples do not contain a segment field + 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"); + + if (m_header.seg_size) + return llvm::make_error<llvm::object::GenericBinaryError>( + "segmented arange entries are not supported"); + + // 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"); + + const lldb::offset_t next_offset = GetNextOffset(); + assert(next_offset != DW_INVALID_OFFSET); + uint32_t num_terminators = 0; + bool last_was_terminator = false; + while (*offset_ptr < next_offset) { + 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. Some linkers can emit .debug_aranges with multiple + // terminator pair entries that are still withing the length of the + // DWARFDebugArangeSet. We want to be sure to parse all entries for + // this DWARFDebugArangeSet so that we don't stop parsing early and end up + // treating addresses as a header of the next DWARFDebugArangeSet. We also + // need to make sure we parse all valid address pairs so we don't omit them + // from the aranges result, so we can't stop at the first terminator entry + // we find. + if (arangeDescriptor.address == 0 && arangeDescriptor.length == 0) { + ++num_terminators; + last_was_terminator = true; + } else { + last_was_terminator = false; + // Only add .debug_aranges address entries that have a non zero size. + // Some linkers will zero out the length field for some .debug_aranges + // entries if they were stripped. We also could watch out for multiple + // entries at address zero and remove those as well. + if (arangeDescriptor.length > 0) + m_arange_descriptors.push_back(arangeDescriptor); + } + } + if (num_terminators > 1) { + Log *log = GetLog(DWARFLog::DebugInfo); + LLDB_LOG(log, + "warning: DWARFDebugArangeSet at %#" PRIx64 " contains %u " + "terminator entries", + m_offset, num_terminators); + } + if (last_was_terminator) + return llvm::ErrorSuccess(); + + 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..ecdbe953f58b --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugArangeSet.h @@ -0,0 +1,70 @@ +//===-- 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 LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_DWARFDEBUGARANGESET_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_DWARFDEBUGARANGESET_H + +#include "lldb/Core/dwarf.h" +#include <cstdint> +#include <vector> + +namespace lldb_private::plugin { +namespace dwarf { +class DWARFDebugArangeSet { +public: + struct Header { + /// The total length of the entries for that set, not including the length + /// field itself. + uint32_t length = 0; + /// The DWARF version number. + uint16_t version = 0; + /// The offset from the beginning of the .debug_info section of the + /// compilation unit entry referenced by the table. + uint32_t cu_offset = 0; + /// 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 addr_size = 0; + /// 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. + uint8_t seg_size = 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 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; } + dw_offset_t GetNextOffset() const { return m_next_offset; } + 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; + + dw_offset_t m_offset; + dw_offset_t m_next_offset; + Header m_header; + DescriptorColl m_arange_descriptors; +}; +} // namespace dwarf +} // namespace lldb_private::plugin + +#endif // LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_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..f383261e8a5f --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAranges.cpp @@ -0,0 +1,104 @@ +//===-- DWARFDebugAranges.cpp ---------------------------------------------===// +// +// 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 "LogChannelDWARF.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/Timer.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::plugin::dwarf; + +// 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 +void DWARFDebugAranges::extract(const DWARFDataExtractor &debug_aranges_data) { + lldb::offset_t offset = 0; + + DWARFDebugArangeSet set; + Range range; + while (debug_aranges_data.ValidOffset(offset)) { + const lldb::offset_t set_offset = offset; + if (llvm::Error error = set.extract(debug_aranges_data, &offset)) { + Log *log = GetLog(DWARFLog::DebugInfo); + LLDB_LOG_ERROR(log, std::move(error), + "DWARFDebugAranges::extract failed to extract " + ".debug_aranges set at offset {1:x}: {0}", + set_offset); + } else { + 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)); + } + } + } + // Always use the previous DWARFDebugArangeSet's information to calculate + // the offset of the next DWARFDebugArangeSet in case we entouncter an + // error in the current DWARFDebugArangeSet and our offset position is + // still in the middle of the data. If we do this, we can parse all valid + // DWARFDebugArangeSet objects without returning invalid errors. + offset = set.GetNextOffset(); + set.Clear(); + } +} + +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) + LLDB_LOG(log, "{0:x8}: [{1:x16} - {2:x16})", 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) { + LLDB_SCOPED_TIMER(); + + 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..99e2108b85c6 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAranges.h @@ -0,0 +1,57 @@ +//===-- 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 LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_DWARFDEBUGARANGES_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_DWARFDEBUGARANGES_H + +#include "lldb/Core/dwarf.h" +#include "lldb/Utility/RangeMap.h" +#include "llvm/Support/Error.h" + +namespace lldb_private::plugin { +namespace dwarf { +class DWARFDebugAranges { +protected: + typedef 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(); } + + void extract(const 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(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; +}; +} // namespace dwarf +} // namespace lldb_private::plugin + +#endif // LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_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..f7df38d24019 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.cpp @@ -0,0 +1,260 @@ +//===-- DWARFDebugInfo.cpp ------------------------------------------------===// +// +// 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" +#include "LogChannelDWARF.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::plugin::dwarf; + +// Constructor +DWARFDebugInfo::DWARFDebugInfo(SymbolFileDWARF &dwarf, DWARFContext &context) + : m_dwarf(dwarf), m_context(context), m_units(), m_cu_aranges_up() {} + +const DWARFDebugAranges &DWARFDebugInfo::GetCompileUnitAranges() { + if (m_cu_aranges_up) + return *m_cu_aranges_up; + + m_cu_aranges_up = std::make_unique<DWARFDebugAranges>(); + const DWARFDataExtractor &debug_aranges_data = + m_context.getOrLoadArangesData(); + + // Extract what we can from the .debug_aranges first. + m_cu_aranges_up->extract(debug_aranges_data); + + // Make a list of all CUs represented by the .debug_aranges data. + 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 .debug_aranges. + // The .debug_aranges accelerator is not guaranteed to be complete. + // Tools such as dsymutil can provide stronger guarantees than required by the + // standard. Without that guarantee, we have to iterate over every CU in the + // .debug_info and make sure there's a corresponding entry in the table and if + // not, add one for every subprogram. + ObjectFile *OF = m_dwarf.GetObjectFile(); + if (!OF || !OF->CanTrustAddressRanges()) { + 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)) { + const lldb::offset_t unit_header_offset = offset; + llvm::Expected<DWARFUnitSP> expected_unit_sp = + DWARFUnit::extract(m_dwarf, m_units.size(), data, section, &offset); + + if (!expected_unit_sp) { + Log *log = GetLog(DWARFLog::DebugInfo); + if (log) + LLDB_LOG(log, "Unable to extract DWARFUnitHeader at {0:x}: {1}", + unit_header_offset, + llvm::toString(expected_unit_sp.takeError())); + else + llvm::consumeError(expected_unit_sp.takeError()); + return; + } + + DWARFUnitSP unit_sp = *expected_unit_sp; + + // If it didn't return an error, then it should be returning a valid Unit. + assert((bool)unit_sp); + + // Keep a map of DWO ID back to the skeleton units. Sometimes accelerator + // table lookups can cause the DWO files to be accessed before the skeleton + // compile unit is parsed, so we keep a map to allow us to match up the DWO + // file to the back to the skeleton compile units. + if (unit_sp->GetUnitType() == lldb_private::dwarf::DW_UT_skeleton) { + if (std::optional<uint64_t> unit_dwo_id = unit_sp->GetHeaderDWOId()) + m_dwarf5_dwo_id_to_skeleton_unit[*unit_dwo_id] = unit_sp.get(); + } + + 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->GetID()); + } + } +} + +DWARFUnit *DWARFDebugInfo::GetSkeletonUnit(DWARFUnit *dwo_unit) { + // If this isn't a DWO unit, don't try and find the skeleton unit. + if (!dwo_unit->IsDWOUnit()) + return nullptr; + + auto dwo_id = dwo_unit->GetDWOId(); + if (!dwo_id.has_value()) + return nullptr; + + // Parse the unit headers so that m_dwarf5_dwo_id_to_skeleton_unit is filled + // in with all of the DWARF5 skeleton compile units DWO IDs since it is easy + // to access the DWO IDs in the DWARFUnitHeader for each DWARFUnit. + ParseUnitHeadersIfNeeded(); + + // Find the value in our cache and return it we we find it. This cache may + // only contain DWARF5 units. + auto iter = m_dwarf5_dwo_id_to_skeleton_unit.find(*dwo_id); + if (iter != m_dwarf5_dwo_id_to_skeleton_unit.end()) + return iter->second; + + // DWARF5 unit headers have the DWO ID and should have already been in the map + // so if it wasn't found in the above find() call, then we didn't find it and + // don't need to do the more expensive DWARF4 search. + if (dwo_unit->GetVersion() >= 5) + return nullptr; + + // Parse all DWO IDs from all DWARF4 and earlier compile units that have DWO + // IDs. It is more expensive to get the DWO IDs from DWARF4 compile units as + // we need to parse the unit DIE and extract the DW_AT_dwo_id or + // DW_AT_GNU_dwo_id attribute values, so do this only if we didn't find our + // match above search and only for DWARF4 and earlier compile units. + llvm::call_once(m_dwarf4_dwo_id_to_skeleton_unit_once_flag, [this]() { + for (uint32_t i = 0, num = GetNumUnits(); i < num; ++i) { + if (DWARFUnit *unit = GetUnitAtIndex(i)) { + if (unit->GetVersion() < 5) { + if (std::optional<uint64_t> unit_dwo_id = unit->GetDWOId()) + m_dwarf4_dwo_id_to_skeleton_unit[*unit_dwo_id] = unit; + } + } + } + }); + + // Search the DWARF4 DWO results that we parsed lazily. + iter = m_dwarf4_dwo_id_to_skeleton_unit.find(*dwo_id); + if (iter != m_dwarf4_dwo_id_to_skeleton_unit.end()) + return iter->second; + return nullptr; +} + +void DWARFDebugInfo::ParseUnitHeadersIfNeeded() { + llvm::call_once(m_units_once_flag, [&] { + 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(size_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_INDEX; + 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::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; +} + +const std::shared_ptr<SymbolFileDWARFDwo> &DWARFDebugInfo::GetDwpSymbolFile() { + return m_dwarf.GetDwpSymbolFile(); +} + +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(); +} + +// GetDIE() +// +// Get the DIE (Debug Information Entry) with the specified offset. +DWARFDIE +DWARFDebugInfo::GetDIE(DIERef::Section section, dw_offset_t die_offset) { + if (DWARFUnit *cu = GetUnitContainingDIEOffset(section, die_offset)) + return cu->GetNonSkeletonUnit().GetDIE(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..598739bf3cb9 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.h @@ -0,0 +1,89 @@ +//===-- 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 LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_DWARFDEBUGINFO_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_DWARFDEBUGINFO_H + +#include <map> +#include <vector> + +#include "DWARFDIE.h" +#include "DWARFTypeUnit.h" +#include "DWARFUnit.h" +#include "SymbolFileDWARF.h" +#include "lldb/lldb-private.h" +#include "llvm/Support/Error.h" + +namespace lldb_private::plugin { +namespace dwarf { +class DWARFContext; + +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, DWARFContext &context); + + size_t GetNumUnits(); + DWARFUnit *GetUnitAtIndex(size_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 *GetSkeletonUnit(DWARFUnit *dwo_unit); + DWARFTypeUnit *GetTypeUnitForHash(uint64_t hash); + bool ContainsTypeUnits(); + DWARFDIE GetDIE(DIERef::Section section, dw_offset_t die_offset); + + 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 + }; + + const DWARFDebugAranges &GetCompileUnitAranges(); + + const std::shared_ptr<SymbolFileDWARFDwo> &GetDwpSymbolFile(); + +protected: + typedef std::vector<DWARFUnitSP> UnitColl; + + SymbolFileDWARF &m_dwarf; + DWARFContext &m_context; + + llvm::once_flag m_units_once_flag; + 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; + llvm::DenseMap<uint64_t, DWARFUnit *> m_dwarf5_dwo_id_to_skeleton_unit; + llvm::DenseMap<uint64_t, DWARFUnit *> m_dwarf4_dwo_id_to_skeleton_unit; + llvm::once_flag m_dwarf4_dwo_id_to_skeleton_unit_once_flag; + +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); + + DWARFDebugInfo(const DWARFDebugInfo &) = delete; + const DWARFDebugInfo &operator=(const DWARFDebugInfo &) = delete; +}; +} // namespace dwarf +} // namespace lldb_private::plugin + +#endif // LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_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..e2660735ea7d --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp @@ -0,0 +1,662 @@ +//===-- DWARFDebugInfoEntry.cpp -------------------------------------------===// +// +// 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 <cassert> + +#include <algorithm> +#include <limits> +#include <optional> + +#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 "lldb/Utility/StreamString.h" + +#include "DWARFCompileUnit.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" + +#include "llvm/DebugInfo/DWARF/DWARFDebugAbbrev.h" + +using namespace lldb_private; +using namespace lldb_private::dwarf; +using namespace lldb_private::plugin::dwarf; +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 &unit, + lldb::offset_t *offset_ptr) { + m_offset = *offset_ptr; + auto report_error = [&](const char *fmt, const auto &...vals) { + unit.GetSymbolFileDWARF().GetObjectFile()->GetModule()->ReportError( + "[{0:x16}]: {1}, please file a bug and " + "attach the file at the start of this error message", + static_cast<uint64_t>(m_offset), llvm::formatv(fmt, vals...)); + *offset_ptr = std::numeric_limits<lldb::offset_t>::max(); + return false; + }; + + m_parent_idx = 0; + m_sibling_idx = 0; + const uint64_t abbr_idx = data.GetULEB128(offset_ptr); + if (abbr_idx > std::numeric_limits<uint16_t>::max()) + return report_error("abbreviation code {0} too big", abbr_idx); + m_abbr_idx = abbr_idx; + + if (m_abbr_idx == 0) { + m_tag = llvm::dwarf::DW_TAG_null; + m_has_children = false; + return true; // NULL debug tag entry + } + + const auto *abbrevDecl = GetAbbreviationDeclarationPtr(&unit); + if (abbrevDecl == nullptr) + return report_error("invalid abbreviation code {0}", abbr_idx); + + m_tag = abbrevDecl->getTag(); + m_has_children = abbrevDecl->hasChildren(); + // Skip all data in the .debug_info or .debug_types for the attributes + for (const auto &attribute : abbrevDecl->attributes()) { + if (DWARFFormValue::SkipValue(attribute.Form, data, offset_ptr, &unit)) + continue; + + return report_error("Unsupported DW_FORM_{1:x}", attribute.Form); + } + return true; +} + +static DWARFRangeList GetRangesOrReportError(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( + "[{0:x16}]: DIE has DW_AT_ranges({1} {2:x16}) attribute, but " + "range extraction failed ({3}), please file a bug " + "and attach the file at the start of this error message", + die.GetOffset(), + llvm::dwarf::FormEncodingString(value.Form()).str().c_str(), + value.Unsigned(), toString(expected_ranges.takeError()).c_str()); + return DWARFRangeList(); +} + +static void ExtractAttrAndFormValue( + const llvm::DWARFAbbreviationDeclaration::AttributeSpec &attr_spec, + dw_attr_t &attr, DWARFFormValue &form_value) { + attr = attr_spec.Attr; + form_value.FormRef() = attr_spec.Form; + if (attr_spec.isImplicitConst()) + form_value.SetSigned(attr_spec.getImplicitConstValue()); +} + +// 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( + DWARFUnit *cu, const char *&name, const char *&mangled, + DWARFRangeList &ranges, std::optional<int> &decl_file, + std::optional<int> &decl_line, std::optional<int> &decl_column, + std::optional<int> &call_file, std::optional<int> &call_line, + std::optional<int> &call_column, DWARFExpressionList *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; + + SymbolFileDWARF &dwarf = cu->GetSymbolFileDWARF(); + lldb::ModuleSP module = dwarf.GetObjectFile()->GetModule(); + + if (const auto *abbrevDecl = GetAbbreviationDeclarationPtr(cu)) { + const DWARFDataExtractor &data = cu->GetData(); + lldb::offset_t offset = GetFirstAttributeOffset(); + + if (!data.ValidOffset(offset)) + return false; + + bool do_offset = false; + + for (const auto &attribute : abbrevDecl->attributes()) { + DWARFFormValue form_value(cu); + dw_attr_t attr; + ExtractAttrAndFormValue(attribute, 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) + decl_file = form_value.Unsigned(); + break; + + case DW_AT_decl_line: + if (!decl_line) + decl_line = form_value.Unsigned(); + break; + + case DW_AT_decl_column: + if (!decl_column) + decl_column = form_value.Unsigned(); + break; + + case DW_AT_call_file: + if (!call_file) + call_file = form_value.Unsigned(); + break; + + case DW_AT_call_line: + if (!call_line) + call_line = form_value.Unsigned(); + break; + + case DW_AT_call_column: + if (!call_column) + 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 = + DWARFExpressionList(module, + DWARFExpression(DataExtractor( + data, block_offset, block_length)), + cu); + } else { + DataExtractor data = cu->GetLocationData(); + const dw_offset_t offset = form_value.Unsigned(); + if (data.ValidOffset(offset)) { + data = DataExtractor(data, offset, data.GetByteSize() - offset); + if (lo_pc != LLDB_INVALID_ADDRESS) { + assert(lo_pc >= cu->GetBaseAddress()); + DWARFExpression::ParseDWARFLocationList(cu, data, frame_base); + frame_base->SetFuncFileAddress(lo_pc); + } 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->SetFuncFileAddress(lowest_range_pc); + } + + 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(); +} + +// 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). +void DWARFDebugInfoEntry::GetAttributes(DWARFUnit *cu, + DWARFAttributes &attributes, + Recurse recurse, + uint32_t curr_depth) const { + const auto *abbrevDecl = GetAbbreviationDeclarationPtr(cu); + if (!abbrevDecl) { + attributes.Clear(); + return; + } + + const DWARFDataExtractor &data = cu->GetData(); + lldb::offset_t offset = GetFirstAttributeOffset(); + + for (const auto &attribute : abbrevDecl->attributes()) { + DWARFFormValue form_value(cu); + dw_attr_t attr; + ExtractAttrAndFormValue(attribute, attr, form_value); + + // 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; + } + [[fallthrough]]; + default: + attributes.Append(form_value, offset, attr); + break; + } + + if (recurse == Recurse::yes && + ((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.GetDIE()->GetAttributes(spec_die.GetCU(), attributes, + recurse, curr_depth + 1); + } + } else { + const dw_form_t form = form_value.Form(); + std::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); + } + } +} + +// 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)) { + std::optional<uint32_t> attr_idx = abbrevDecl->findAttributeIndex(attr); + + if (attr_idx) { + 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; + } + } + } + return 0; +} + +// 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; +} + +std::optional<uint64_t> +DWARFDebugInfoEntry::GetAttributeValueAsOptionalUnsigned( + 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.Unsigned(); + return std::nullopt; +} + +// 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; +} + +DWARFRangeList DWARFDebugInfoEntry::GetAttributeAddressRanges( + DWARFUnit *cu, bool check_hi_lo_pc, + bool check_specification_or_abstract_origin) const { + + DWARFFormValue form_value; + if (GetAttributeValue(cu, DW_AT_ranges, form_value)) + return GetRangesOrReportError(*cu, *this, form_value); + + DWARFRangeList ranges; + 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; +} + +// 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; +} + +/// This function is builds a table very similar to the standard .debug_aranges +/// table, except that the actual DIE offset for the function is placed in the +/// table instead of the compile unit offset. +void DWARFDebugInfoEntry::BuildFunctionAddressRangeTable( + DWARFUnit *cu, DWARFDebugAranges *debug_aranges) const { + if (m_tag) { + if (m_tag == DW_TAG_subprogram) { + DWARFRangeList ranges = + GetAttributeAddressRanges(cu, /*check_hi_lo_pc=*/true); + for (const auto &r : ranges) { + debug_aranges->AppendRange(GetOffset(), r.GetRangeBase(), + r.GetRangeEnd()); + } + } + + const DWARFDebugInfoEntry *child = GetFirstChild(); + while (child) { + child->BuildFunctionAddressRangeTable(cu, debug_aranges); + child = child->GetSibling(); + } + } +} + +lldb::offset_t DWARFDebugInfoEntry::GetFirstAttributeOffset() const { + return GetOffset() + llvm::getULEB128Size(m_abbr_idx); +} + +const llvm::DWARFAbbreviationDeclaration * +DWARFDebugInfoEntry::GetAbbreviationDeclarationPtr(const DWARFUnit *cu) const { + if (!cu) + return nullptr; + + const llvm::DWARFAbbreviationDeclarationSet *abbrev_set = + cu->GetAbbreviations(); + if (!abbrev_set) + return nullptr; + + return abbrev_set->getAbbreviationDeclaration(m_abbr_idx); +} + +bool DWARFDebugInfoEntry::IsGlobalOrStaticScopeVariable() const { + if (Tag() != DW_TAG_variable) + return false; + const DWARFDebugInfoEntry *parent_die = GetParent(); + while (parent_die != nullptr) { + switch (parent_die->Tag()) { + case DW_TAG_subprogram: + case DW_TAG_lexical_block: + case DW_TAG_inlined_subroutine: + return false; + + case DW_TAG_compile_unit: + case DW_TAG_partial_unit: + return true; + + default: + break; + } + parent_die = parent_die->GetParent(); + } + return false; +} + +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..3816c6500717 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.h @@ -0,0 +1,191 @@ +//===-- 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 LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_DWARFDEBUGINFOENTRY_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_DWARFDEBUGINFOENTRY_H + +#include "SymbolFileDWARF.h" +#include "llvm/ADT/SmallVector.h" + +#include "DWARFAttribute.h" +#include "DWARFBaseDIE.h" +#include "DWARFDebugRanges.h" +#include <map> +#include <optional> +#include <set> +#include <vector> + +#include "llvm/DebugInfo/DWARF/DWARFAbbreviationDeclaration.h" + +namespace lldb_private::plugin { +namespace dwarf { +class DWARFDeclContext; + +#define DIE_SIBLING_IDX_BITSIZE 31 + +/// DWARFDebugInfoEntry objects assume that they are living in one big +/// vector and do pointer arithmetic on their this pointers. Don't +/// pass them by value. Due to the way they are constructed in a +/// std::vector, we cannot delete the copy constructor. +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) {} + + explicit operator bool() const { return m_offset != DW_INVALID_OFFSET; } + bool operator==(const DWARFDebugInfoEntry &rhs) const; + bool operator!=(const DWARFDebugInfoEntry &rhs) const; + + void BuildFunctionAddressRangeTable(DWARFUnit *cu, + DWARFDebugAranges *debug_aranges) const; + + bool Extract(const DWARFDataExtractor &data, const DWARFUnit &cu, + lldb::offset_t *offset_ptr); + + using Recurse = DWARFBaseDIE::Recurse; + DWARFAttributes GetAttributes(DWARFUnit *cu, + Recurse recurse = Recurse::yes) const { + DWARFAttributes attrs; + GetAttributes(cu, attrs, recurse, 0 /* curr_depth */); + return attrs; + } + + 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; + + std::optional<uint64_t> GetAttributeValueAsOptionalUnsigned( + const DWARFUnit *cu, const dw_attr_t attr, + 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; + + DWARFRangeList GetAttributeAddressRanges( + DWARFUnit *cu, 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; + + bool GetDIENamesAndRanges(DWARFUnit *cu, const char *&name, + const char *&mangled, DWARFRangeList &rangeList, + std::optional<int> &decl_file, + std::optional<int> &decl_line, + std::optional<int> &decl_column, + std::optional<int> &call_file, + std::optional<int> &call_line, + std::optional<int> &call_column, + DWARFExpressionList *frame_base = nullptr) const; + + const llvm::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 SetSiblingIndex(uint32_t idx) { m_sibling_idx = idx; } + void SetParentIndex(uint32_t idx) { m_parent_idx = idx; } + + // This function returns true if the variable scope is either + // global or (file-static). It will return false for static variables + // that are local to a function, as they have local scope. + bool IsGlobalOrStaticScopeVariable() const; + +protected: + // Up to 2TB offset within the .debug_info/.debug_types + dw_offset_t m_offset : DW_DIE_OFFSET_MAX_BITSIZE; + // How many to subtract from "this" to get the parent. If zero this die has no + // parent + dw_offset_t m_parent_idx : 64 - DW_DIE_OFFSET_MAX_BITSIZE; + // 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. + uint32_t m_sibling_idx : 31, m_has_children : 1; + uint16_t m_abbr_idx = 0; + /// A copy of the DW_TAG value so we don't have to go through the compile + /// unit abbrev table + dw_tag_t m_tag = llvm::dwarf::DW_TAG_null; + +private: + void GetAttributes(DWARFUnit *cu, DWARFAttributes &attrs, Recurse recurse, + uint32_t curr_depth) const; +}; +} // namespace dwarf +} // namespace lldb_private::plugin + +#endif // LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_DWARFDEBUGINFOENTRY_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..2cd84bc55b75 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugMacro.cpp @@ -0,0 +1,128 @@ +//===-- DWARFDebugMacro.cpp -----------------------------------------------===// +// +// 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; +using namespace lldb_private::dwarf; +using namespace lldb_private::plugin::dwarf; + +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..67d1cde8d5de --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugMacro.h @@ -0,0 +1,62 @@ +//===-- 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 LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_DWARFDEBUGMACRO_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_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::plugin { +namespace dwarf { +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 DWARFDataExtractor &debug_macro_data, + lldb::offset_t *offset); + + bool OffsetIs64Bit() const { return m_offset_is_64_bit; } + +private: + static void SkipOperandTable(const DWARFDataExtractor &debug_macro_data, + lldb::offset_t *offset); + + uint16_t m_version = 0; + bool m_offset_is_64_bit = false; + uint64_t m_debug_line_offset = 0; +}; + +class DWARFDebugMacroEntry { +public: + static void ReadMacroEntries(const DWARFDataExtractor &debug_macro_data, + const DWARFDataExtractor &debug_str_data, + const bool offset_is_64_bit, + lldb::offset_t *sect_offset, + SymbolFileDWARF *sym_file_dwarf, + DebugMacrosSP &debug_macros_sp); +}; +} // namespace dwarf +} // namespace lldb_private::plugin + +#endif // LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_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..fd8f4e12ff77 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugRanges.cpp @@ -0,0 +1,56 @@ +//===-- DWARFDebugRanges.cpp ----------------------------------------------===// +// +// 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 "llvm/DebugInfo/DWARF/DWARFDebugRangeList.h" + +using namespace lldb_private; +using namespace lldb_private::plugin::dwarf; + +DWARFDebugRanges::DWARFDebugRanges() : m_range_map() {} + +void DWARFDebugRanges::Extract(DWARFContext &context) { + llvm::DWARFDataExtractor extractor = + context.getOrLoadRangesData().GetAsLLVMDWARF(); + llvm::DWARFDebugRangeList extracted_list; + uint64_t current_offset = 0; + auto extract_next_list = [&] { + if (auto error = extracted_list.extract(extractor, ¤t_offset)) { + consumeError(std::move(error)); + return false; + } + return true; + }; + + uint64_t previous_offset = current_offset; + while (extractor.isValidOffset(current_offset) && extract_next_list()) { + DWARFRangeList &lldb_range_list = m_range_map[previous_offset]; + lldb_range_list.Reserve(extracted_list.getEntries().size()); + for (auto &range : extracted_list.getEntries()) + lldb_range_list.Append(range.StartAddress, + range.EndAddress - range.StartAddress); + lldb_range_list.Sort(); + previous_offset = current_offset; + } +} + +DWARFRangeList +DWARFDebugRanges::FindRanges(const DWARFUnit *cu, + dw_offset_t debug_ranges_offset) const { + dw_addr_t debug_ranges_address = cu->GetRangesBase() + debug_ranges_offset; + auto pos = m_range_map.find(debug_ranges_address); + DWARFRangeList ans = + pos == m_range_map.end() ? DWARFRangeList() : 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. + ans.Slide(cu->GetBaseAddress()); + return ans; +} 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..a04fcf59d5bf --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugRanges.h @@ -0,0 +1,34 @@ +//===-- 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 LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_DWARFDEBUGRANGES_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_DWARFDEBUGRANGES_H + +#include "lldb/Core/dwarf.h" +#include <map> + +namespace lldb_private::plugin { +namespace dwarf { +class DWARFUnit; +class DWARFContext; + +class DWARFDebugRanges { +public: + DWARFDebugRanges(); + + void Extract(DWARFContext &context); + DWARFRangeList FindRanges(const DWARFUnit *cu, + dw_offset_t debug_ranges_offset) const; + +protected: + std::map<dw_offset_t, DWARFRangeList> m_range_map; +}; +} // namespace dwarf +} // namespace lldb_private::plugin + +#endif // LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_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..f759cb8fae61 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDeclContext.cpp @@ -0,0 +1,89 @@ +//===-- DWARFDeclContext.cpp ----------------------------------------------===// +// +// 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" +#include "llvm/Support/raw_ostream.h" + +using namespace lldb_private::dwarf; +using namespace lldb_private::plugin::dwarf; + +const char *DWARFDeclContext::Entry::GetName() const { + if (name != nullptr) + return name; + if (tag == DW_TAG_namespace) + return "(anonymous namespace)"; + if (tag == DW_TAG_class_type) + return "(anonymous class)"; + if (tag == DW_TAG_structure_type) + return "(anonymous struct)"; + if (tag == DW_TAG_union_type) + return "(anonymous union)"; + return "(anonymous)"; +} + +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 { + llvm::raw_string_ostream string_stream(m_qualified_name); + llvm::interleave( + llvm::reverse(m_entries), string_stream, + [&](auto entry) { string_stream << entry.GetName(); }, "::"); + } + } + } + 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..b563d1c4417b --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDeclContext.h @@ -0,0 +1,110 @@ +//===-- 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 LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_DWARFDECLCONTEXT_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_DWARFDECLCONTEXT_H + +#include "DWARFDefines.h" +#include "lldb/Utility/ConstString.h" +#include "llvm/ADT/StringExtras.h" + +#include <cassert> +#include <string> +#include <vector> + +namespace lldb_private::plugin { +namespace dwarf { +// 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() = default; + 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; + } + + /// Returns the name of this entry if it has one, or the appropriate + /// "anonymous {namespace, class, struct, union}". + const char *GetName() const; + + // Test operator + explicit operator bool() const { return tag != 0; } + + dw_tag_t tag = llvm::dwarf::DW_TAG_null; + const char *name = nullptr; + }; + + DWARFDeclContext() : m_entries() {} + + DWARFDeclContext(llvm::ArrayRef<Entry> entries) { + llvm::append_range(m_entries, entries); + } + + void AppendDeclContext(dw_tag_t tag, const char *name) { + m_entries.push_back(Entry(tag, name)); + } + + bool operator==(const DWARFDeclContext &rhs) const; + bool operator!=(const DWARFDeclContext &rhs) const { return !(*this == rhs); } + + uint32_t GetSize() const { return m_entries.size(); } + + Entry &operator[](uint32_t idx) { + assert(idx < m_entries.size() && "invalid index"); + return m_entries[idx]; + } + + const Entry &operator[](uint32_t idx) const { + assert(idx < m_entries.size() && "invalid index"); + return m_entries[idx]; + } + + const char *GetQualifiedName() const; + + // Same as GetQualifiedName, but the life time of the returned string will + // be that of the LLDB session. + ConstString GetQualifiedNameAsConstString() const { + return ConstString(GetQualifiedName()); + } + + void Clear() { + m_entries.clear(); + m_qualified_name.clear(); + } + + friend llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, + const DWARFDeclContext &ctx) { + OS << "DWARFDeclContext{"; + llvm::ListSeparator LS; + for (const Entry &e : ctx.m_entries) { + OS << LS << "{" << DW_TAG_value_to_name(e.tag) << ", " << e.GetName() + << "}"; + } + return OS << "}"; + } + +protected: + typedef std::vector<Entry> collection; + collection m_entries; + mutable std::string m_qualified_name; +}; +} // namespace dwarf +} // namespace lldb_private::plugin + +#endif // LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_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..2fb0c224bf8e --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDefines.cpp @@ -0,0 +1,37 @@ +//===-- DWARFDefines.cpp --------------------------------------------------===// +// +// 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::plugin { +namespace dwarf { + +llvm::StringRef DW_TAG_value_to_name(dw_tag_t tag) { + static constexpr llvm::StringLiteral s_unknown_tag_name("<unknown DW_TAG>"); + if (llvm::StringRef tag_name = llvm::dwarf::TagString(tag); !tag_name.empty()) + return tag_name; + + return s_unknown_tag_name; +} + +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(); +} + +} // namespace dwarf +} // namespace lldb_private::plugin 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..be81cb0f5df1 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFDefines.h @@ -0,0 +1,25 @@ +//===-- 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 LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_DWARFDEFINES_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_DWARFDEFINES_H + +#include "lldb/Core/dwarf.h" +#include <cstdint> + +namespace lldb_private::plugin { +namespace dwarf { + +llvm::StringRef DW_TAG_value_to_name(dw_tag_t tag); + +const char *DW_OP_value_to_name(uint32_t val); + +} // namespace dwarf +} // namespace lldb_private::plugin + +#endif // LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_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..e1f73f1997e3 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp @@ -0,0 +1,647 @@ +//===-- DWARFFormValue.cpp ------------------------------------------------===// +// +// 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 <cassert> +#include <optional> + +#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" + +using namespace lldb_private; +using namespace lldb_private::dwarf; +using namespace lldb_private::plugin::dwarf; + +void DWARFFormValue::Clear() { + m_unit = nullptr; + m_form = dw_form_t(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_loclistx: + 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 = static_cast<dw_form_t>(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 DW_FORM_strx (ULEB128) + {0, 0}, // 0x1b DW_FORM_addrx (ULEB128) + {1, 4}, // 0x1c DW_FORM_ref_sup4 + {0, 0}, // 0x1d DW_FORM_strp_sup (4 bytes for DWARF32, 8 bytes for DWARF64) + {1, 16}, // 0x1e DW_FORM_data16 + {1, 4}, // 0x1f DW_FORM_line_strp + {1, 8}, // 0x20 DW_FORM_ref_sig8 +}; + +std::optional<uint8_t> DWARFFormValue::GetFixedSize(dw_form_t form, + const DWARFUnit *u) { + if (form <= DW_FORM_ref_sig8 && g_form_sizes[form].valid) + return static_cast<uint8_t>(g_form_sizes[form].size); + if (form == DW_FORM_addr && u) + return u->GetAddressByteSize(); + return std::nullopt; +} + +std::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: { + uint64_t size = debug_info_data.GetULEB128(offset_ptr); + *offset_ptr += size; + } + return true; + case DW_FORM_block1: { + uint8_t size = debug_info_data.GetU8(offset_ptr); + *offset_ptr += size; + } + return true; + case DW_FORM_block2: { + uint16_t size = debug_info_data.GetU16(offset_ptr); + *offset_ptr += size; + } + return true; + case DW_FORM_block4: { + uint32_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: + case DW_FORM_line_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_loclistx: + 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: { + auto indirect_form = + static_cast<dw_form_t>(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: + DumpAddress(s.AsRawOstream(), 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: + case DW_FORM_line_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) + DumpAddress(s.AsRawOstream(), uvalue, sizeof(uint64_t) * 2); + else + DumpAddress(s.AsRawOstream(), 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 { + DWARFContext &context = m_unit->GetSymbolFileDWARF().GetDWARFContext(); + + if (m_form == DW_FORM_string) + return m_value.value.cstr; + if (m_form == DW_FORM_strp) + return context.getOrLoadStrData().PeekCStr(m_value.value.uval); + + if (m_form == DW_FORM_GNU_str_index || 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) { + + std::optional<uint64_t> offset = + m_unit->GetStringOffsetSectionItem(m_value.value.uval); + if (!offset) + return nullptr; + return context.getOrLoadStrData().PeekCStr(*offset); + } + + if (m_form == DW_FORM_line_strp) + return context.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); +} + +std::pair<DWARFUnit *, uint64_t> +DWARFFormValue::ReferencedUnitAndOffset() 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 {0:x16} is outside of its CU", value); + return {nullptr, 0}; + } + return {const_cast<DWARFUnit *>(m_unit), 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 {0:x16} has no matching CU", value); + return {nullptr, 0}; + } + return {ref_cu, value}; + } + + case DW_FORM_ref_sig8: { + DWARFTypeUnit *tu = + m_unit->GetSymbolFileDWARF().DebugInfo().GetTypeUnitForHash(value); + if (!tu) + return {nullptr, 0}; + return {tu, tu->GetTypeOffset()}; + } + + default: + return {nullptr, 0}; + } +} + +DWARFDIE DWARFFormValue::Reference() const { + auto [unit, offset] = ReferencedUnitAndOffset(); + return unit ? unit->GetDIE(offset) : DWARFDIE(); +} + +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; + default: + return false; + } + llvm_unreachable("All cases handled above!"); +} + +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; + default: + return false; + } + llvm_unreachable("All cases handled above!"); +} + +bool DWARFFormValue::FormIsSupported(dw_form_t form) { + switch (form) { + case DW_FORM_addr: + case DW_FORM_addrx: + case DW_FORM_loclistx: + 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_line_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..fdd5b3c278a4 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.h @@ -0,0 +1,99 @@ +//===-- 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 LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_DWARFFORMVALUE_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_DWARFFORMVALUE_H + +#include "DWARFDataExtractor.h" +#include <cstddef> +#include <optional> + +namespace lldb_private::plugin { +namespace dwarf { +class DWARFUnit; +class SymbolFileDWARF; +class DWARFDIE; + +class DWARFFormValue { +public: + typedef struct ValueTypeTag { + ValueTypeTag() : value() { value.uval = 0; } + + union { + uint64_t uval; + int64_t sval; + const char *cstr; + } value; + const uint8_t *data = nullptr; + } 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) {} + const DWARFUnit *GetUnit() const { return m_unit; } + 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(Stream &s) const; + bool ExtractValue(const DWARFDataExtractor &data, lldb::offset_t *offset_ptr); + const uint8_t *BlockData() const; + static std::optional<uint8_t> GetFixedSize(dw_form_t form, + const DWARFUnit *u); + std::optional<uint8_t> GetFixedSize() const; + DWARFDIE Reference() const; + + /// If this is a reference to another DIE, return the corresponding DWARFUnit + /// and DIE offset such that Unit->GetDIE(offset) produces the desired DIE. + /// Otherwise, a nullptr and unspecified offset are returned. + std::pair<DWARFUnit *, uint64_t> ReferencedUnitAndOffset() 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 DWARFDataExtractor &debug_info_data, + lldb::offset_t *offset_ptr) const; + static bool SkipValue(const dw_form_t form, + const 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 = dw_form_t(0); // Form for this value + ValueType m_value; // Contains all data for the form +}; +} // namespace dwarf +} // namespace lldb_private::plugin + +#endif // LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_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..eafddbad03f5 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.cpp @@ -0,0 +1,128 @@ +//===-- DWARFIndex.cpp ----------------------------------------------------===// +// +// 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 "DWARFDebugInfoEntry.h" +#include "DWARFDeclContext.h" +#include "Plugins/Language/ObjC/ObjCLanguage.h" +#include "Plugins/SymbolFile/DWARF/DWARFDIE.h" +#include "Plugins/SymbolFile/DWARF/SymbolFileDWARF.h" + +#include "lldb/Core/Mangled.h" +#include "lldb/Core/Module.h" +#include "lldb/Target/Language.h" + +using namespace lldb_private; +using namespace lldb; +using namespace lldb_private::plugin::dwarf; + +DWARFIndex::~DWARFIndex() = default; + +bool DWARFIndex::ProcessFunctionDIE( + const Module::LookupInfo &lookup_info, DWARFDIE die, + const CompilerDeclContext &parent_decl_ctx, + llvm::function_ref<bool(DWARFDIE die)> callback) { + llvm::StringRef name = lookup_info.GetLookupName().GetStringRef(); + FunctionNameType name_type_mask = lookup_info.GetNameTypeMask(); + + if (!(name_type_mask & eFunctionNameTypeFull)) { + ConstString name_to_match_against; + if (const char *mangled_die_name = die.GetMangledName()) { + name_to_match_against = ConstString(mangled_die_name); + } else { + SymbolFileDWARF *symbols = die.GetDWARF(); + if (ConstString demangled_die_name = + symbols->ConstructFunctionDemangledName(die)) + name_to_match_against = demangled_die_name; + } + + if (!lookup_info.NameMatchesLookupInfo(name_to_match_against, + lookup_info.GetLanguageType())) + return true; + } + + // 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 true; + + // 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 true; + + // In case of a full match, we just insert everything we find. + if (name_type_mask & eFunctionNameTypeFull && die.GetMangledName() == name) + return callback(die); + + // 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())) + return callback(die); + + 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()) + return callback(die); + } + + return true; +} + +DWARFIndex::DIERefCallbackImpl::DIERefCallbackImpl( + const DWARFIndex &index, llvm::function_ref<bool(DWARFDIE die)> callback, + llvm::StringRef name) + : m_index(index), + m_dwarf(*llvm::cast<SymbolFileDWARF>( + index.m_module.GetSymbolFile()->GetBackingSymbolFile())), + m_callback(callback), m_name(name) {} + +bool DWARFIndex::DIERefCallbackImpl::operator()(DIERef ref) const { + if (DWARFDIE die = m_dwarf.GetDIE(ref)) + return m_callback(die); + m_index.ReportInvalidDIERef(ref, m_name); + return true; +} + +bool DWARFIndex::DIERefCallbackImpl::operator()( + const llvm::AppleAcceleratorTable::Entry &entry) const { + return this->operator()(DIERef(std::nullopt, DIERef::Section::DebugInfo, + *entry.getDIESectionOffset())); +} + +void DWARFIndex::ReportInvalidDIERef(DIERef ref, llvm::StringRef name) const { + m_module.ReportErrorIfModifyDetected( + "the DWARF debug information has been modified (accelerator table had " + "bad die {0:x16} for '{1}')\n", + ref.die_offset(), name.str().c_str()); +} + +void DWARFIndex::GetFullyQualifiedType( + const DWARFDeclContext &context, + llvm::function_ref<bool(DWARFDIE die)> callback) { + GetTypes(context, [&](DWARFDIE die) { + return GetFullyQualifiedTypeImpl(context, die, callback); + }); +} + +bool DWARFIndex::GetFullyQualifiedTypeImpl( + const DWARFDeclContext &context, DWARFDIE die, + llvm::function_ref<bool(DWARFDIE die)> callback) { + DWARFDeclContext dwarf_decl_ctx = die.GetDWARFDeclContext(); + if (dwarf_decl_ctx == context) + return callback(die); + return true; +} 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..cb3ae8a06d78 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.h @@ -0,0 +1,122 @@ +//===-- 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_SOURCE_PLUGINS_SYMBOLFILE_DWARF_DWARFINDEX_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_DWARFINDEX_H + +#include "Plugins/SymbolFile/DWARF/DIERef.h" +#include "Plugins/SymbolFile/DWARF/DWARFDIE.h" +#include "Plugins/SymbolFile/DWARF/DWARFFormValue.h" +#include "llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h" + +#include "lldb/Core/Module.h" +#include "lldb/Target/Statistics.h" + +namespace lldb_private::plugin { +namespace dwarf { +class DWARFDeclContext; +class DWARFDIE; + +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, + llvm::function_ref<bool(DWARFDIE die)> callback) = 0; + + virtual void + GetGlobalVariables(const RegularExpression ®ex, + llvm::function_ref<bool(DWARFDIE die)> callback) = 0; + /// \a cu must be the skeleton unit if possible, not GetNonSkeletonUnit(). + virtual void + GetGlobalVariables(DWARFUnit &cu, + llvm::function_ref<bool(DWARFDIE die)> callback) = 0; + virtual void + GetObjCMethods(ConstString class_name, + llvm::function_ref<bool(DWARFDIE die)> callback) = 0; + virtual void + GetCompleteObjCClass(ConstString class_name, bool must_be_implementation, + llvm::function_ref<bool(DWARFDIE die)> callback) = 0; + virtual void GetTypes(ConstString name, + llvm::function_ref<bool(DWARFDIE die)> callback) = 0; + virtual void GetTypes(const DWARFDeclContext &context, + llvm::function_ref<bool(DWARFDIE die)> callback) = 0; + + /// Finds all DIEs whose fully qualified name matches `context`. A base + /// implementation is provided, and it uses the entire CU to check the DIE + /// parent hierarchy. Specializations should override this if they are able + /// to provide a faster implementation. + virtual void + GetFullyQualifiedType(const DWARFDeclContext &context, + llvm::function_ref<bool(DWARFDIE die)> callback); + virtual void + GetNamespaces(ConstString name, + llvm::function_ref<bool(DWARFDIE die)> callback) = 0; + virtual void + GetFunctions(const Module::LookupInfo &lookup_info, SymbolFileDWARF &dwarf, + const CompilerDeclContext &parent_decl_ctx, + llvm::function_ref<bool(DWARFDIE die)> callback) = 0; + virtual void + GetFunctions(const RegularExpression ®ex, + llvm::function_ref<bool(DWARFDIE die)> callback) = 0; + + virtual void Dump(Stream &s) = 0; + + StatsDuration::Duration GetIndexTime() { return m_index_time; } + +protected: + Module &m_module; + StatsDuration m_index_time; + + /// Helper function implementing common logic for processing function dies. If + /// the function given by "die" matches search criteria given by + /// "parent_decl_ctx" and "name_type_mask", it calls the callback with the + /// given die. + bool ProcessFunctionDIE(const Module::LookupInfo &lookup_info, DWARFDIE die, + const CompilerDeclContext &parent_decl_ctx, + llvm::function_ref<bool(DWARFDIE die)> callback); + + class DIERefCallbackImpl { + public: + DIERefCallbackImpl(const DWARFIndex &index, + llvm::function_ref<bool(DWARFDIE die)> callback, + llvm::StringRef name); + bool operator()(DIERef ref) const; + bool operator()(const llvm::AppleAcceleratorTable::Entry &entry) const; + + private: + const DWARFIndex &m_index; + SymbolFileDWARF &m_dwarf; + const llvm::function_ref<bool(DWARFDIE die)> m_callback; + const llvm::StringRef m_name; + }; + DIERefCallbackImpl + DIERefCallback(llvm::function_ref<bool(DWARFDIE die)> callback, + llvm::StringRef name = {}) const { + return DIERefCallbackImpl(*this, callback, name); + } + + void ReportInvalidDIERef(DIERef ref, llvm::StringRef name) const; + + /// Implementation of `GetFullyQualifiedType` to check a single entry, + /// shareable with derived classes. + bool + GetFullyQualifiedTypeImpl(const DWARFDeclContext &context, DWARFDIE die, + llvm::function_ref<bool(DWARFDIE die)> callback); +}; +} // namespace dwarf +} // namespace lldb_private::plugin + +#endif // LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_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..4f3a3f544653 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFTypeUnit.cpp @@ -0,0 +1,25 @@ +//===-- DWARFTypeUnit.cpp -------------------------------------------------===// +// +// 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; +using namespace lldb_private::plugin::dwarf; + +void DWARFTypeUnit::Dump(Stream *s) const { + s->Format("{0:x16}: Type Unit: length = {1:x8}, version = {2:x4}, " + "abbr_offset = {3:x8}, addr_size = {4:x2} (next CU at " + "[{5:x16}])\n", + GetOffset(), (uint32_t)GetLength(), GetVersion(), + (uint32_t)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..8c1f932d8c7f --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFTypeUnit.h @@ -0,0 +1,45 @@ +//===-- 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 LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_DWARFTYPEUNIT_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_DWARFTYPEUNIT_H + +#include "DWARFUnit.h" +#include "llvm/Support/Error.h" + +namespace llvm { +class DWARFAbbreviationDeclarationSet; +} // namespace llvm + +namespace lldb_private::plugin { +namespace dwarf { +class DWARFTypeUnit : public DWARFUnit { +public: + void BuildAddressRangeTable(DWARFDebugAranges *debug_aranges) override {} + + void Dump(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 llvm::DWARFUnitHeader &header, + const llvm::DWARFAbbreviationDeclarationSet &abbrevs, + DIERef::Section section, bool is_dwo) + : DWARFUnit(dwarf, uid, header, abbrevs, section, is_dwo) {} + + friend class DWARFUnit; +}; +} // namespace dwarf +} // namespace lldb_private::plugin + +#endif // LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_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..66a762bf9b68 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp @@ -0,0 +1,1088 @@ +//===-- DWARFUnit.cpp -----------------------------------------------------===// +// +// 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/Symbol/ObjectFile.h" +#include "lldb/Utility/LLDBAssert.h" +#include "lldb/Utility/StreamString.h" +#include "lldb/Utility/Timer.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugAbbrev.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h" +#include "llvm/Object/Error.h" + +#include "DWARFCompileUnit.h" +#include "DWARFDebugAranges.h" +#include "DWARFDebugInfo.h" +#include "DWARFTypeUnit.h" +#include "LogChannelDWARF.h" +#include "SymbolFileDWARFDwo.h" +#include <optional> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::dwarf; +using namespace lldb_private::plugin::dwarf; + +extern int g_verbose; + +DWARFUnit::DWARFUnit(SymbolFileDWARF &dwarf, lldb::user_id_t uid, + const llvm::DWARFUnitHeader &header, + const llvm::DWARFAbbreviationDeclarationSet &abbrevs, + DIERef::Section section, bool is_dwo) + : UserID(uid), m_dwarf(dwarf), m_header(header), m_abbrevs(&abbrevs), + m_cancel_scopes(false), m_section(section), m_is_dwo(is_dwo), + m_has_parsed_non_skeleton_unit(false), m_dwo_id(header.getDWOId()) {} + +DWARFUnit::~DWARFUnit() = default; + +// Parses first DIE of a compile unit, excluding DWO. +void DWARFUnit::ExtractUnitDIENoDwoIfNeeded() { + { + 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 + + ElapsedTime elapsed(m_dwarf.GetDebugInfoParseTimeRef()); + + // 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 first DIE of a compile unit including DWO. +void DWARFUnit::ExtractUnitDIEIfNeeded() { + ExtractUnitDIENoDwoIfNeeded(); + + if (m_has_parsed_non_skeleton_unit) + return; + + m_has_parsed_non_skeleton_unit = true; + m_dwo_error.Clear(); + + if (!m_dwo_id) + return; // No DWO file. + + std::shared_ptr<SymbolFileDWARFDwo> dwo_symbol_file = + m_dwarf.GetDwoSymbolFileForCompileUnit(*this, m_first_die); + if (!dwo_symbol_file) + return; + + DWARFUnit *dwo_cu = dwo_symbol_file->GetDWOCompileUnitForHash(*m_dwo_id); + + if (!dwo_cu) { + SetDwoError(Status::createWithFormat( + "unable to load .dwo file from \"{0}\" due to ID ({1:x16}) mismatch " + "for skeleton DIE at {2:x8}", + dwo_symbol_file->GetObjectFile()->GetFileSpec().GetPath().c_str(), + *m_dwo_id, m_first_die.GetOffset())); + return; // Can't fetch the compile unit from the dwo file. + } + // If the skeleton compile unit gets its unit DIE parsed first, then this + // will fill in the DWO file's back pointer to this skeleton compile unit. + // If the DWO files get parsed on their own first the skeleton back link + // can be done manually in DWARFUnit::GetSkeletonCompileUnit() which will + // do a reverse lookup and cache the result. + dwo_cu->SetSkeletonUnit(this); + + DWARFBaseDIE dwo_cu_die = dwo_cu->GetUnitDIEOnly(); + if (!dwo_cu_die.IsValid()) { + // Can't fetch the compile unit DIE from the dwo file. + SetDwoError(Status::createWithFormat( + "unable to extract compile unit DIE from .dwo file for skeleton " + "DIE at {0:x16}", + m_first_die.GetOffset())); + return; + } + + // 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 (m_addr_base) + dwo_cu->SetAddrBase(*m_addr_base); + else if (m_gnu_addr_base) + dwo_cu->SetAddrBase(*m_gnu_addr_base); + + if (GetVersion() <= 4 && m_gnu_ranges_base) + dwo_cu->SetRangesBase(*m_gnu_ranges_base); + else if (dwo_symbol_file->GetDWARFContext() + .getOrLoadRngListsData() + .GetByteSize() > 0) + dwo_cu->SetRangesBase(llvm::DWARFListTableHeader::getHeaderSize(DWARF32)); + + if (GetVersion() >= 5 && + dwo_symbol_file->GetDWARFContext().getOrLoadLocListsData().GetByteSize() > + 0) + dwo_cu->SetLoclistsBase(llvm::DWARFListTableHeader::getHeaderSize(DWARF32)); + + dwo_cu->SetBaseAddress(GetBaseAddress()); + + m_dwo = std::shared_ptr<DWARFUnit>(std::move(dwo_symbol_file), dwo_cu); +} + +// 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); + + ElapsedTime elapsed(m_dwarf.GetDebugInfoParseTimeRef()); + LLDB_SCOPED_TIMERF( + "%s", + llvm::formatv("{0:x16}: DWARFUnit::ExtractDIEsIfNeeded()", GetOffset()) + .str() + .c_str()); + + // 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) { + 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()) { + // The last die cannot have children (if it did, it wouldn't be the last + // one). This only makes a difference for malformed dwarf that does not have + // a terminating null die. + m_die_array.back().SetHasChildren(false); + + 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) + m_dwo->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). +void DWARFUnit::SetDwoStrOffsetsBase() { + lldb::offset_t baseOffset = 0; + + if (const llvm::DWARFUnitIndex::Entry *entry = m_header.getIndexEntry()) { + if (const auto *contribution = + entry->getContribution(llvm::DW_SECT_STR_OFFSETS)) + baseOffset = contribution->getOffset(); + else + return; + } + + if (GetVersion() >= 5) { + const DWARFDataExtractor &strOffsets = + 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; + } + + SetStrOffsetsBase(baseOffset); +} + +std::optional<uint64_t> DWARFUnit::GetDWOId() { + ExtractUnitDIENoDwoIfNeeded(); + return m_dwo_id; +} + +// m_die_array_mutex must be already held as read/write. +void DWARFUnit::AddUnitDIE(const DWARFDebugInfoEntry &cu_die) { + DWARFAttributes attributes = cu_die.GetAttributes(this); + + // Extract DW_AT_addr_base first, as other attributes may need it. + for (size_t i = 0; i < attributes.Size(); ++i) { + if (attributes.AttributeAtIndex(i) != DW_AT_addr_base) + continue; + DWARFFormValue form_value; + if (attributes.ExtractFormValueAtIndex(i, form_value)) { + SetAddrBase(form_value.Unsigned()); + break; + } + } + + for (size_t i = 0; i < attributes.Size(); ++i) { + dw_attr_t attr = attributes.AttributeAtIndex(i); + DWARFFormValue form_value; + if (!attributes.ExtractFormValueAtIndex(i, form_value)) + continue; + switch (attr) { + default: + break; + case DW_AT_loclists_base: + SetLoclistsBase(form_value.Unsigned()); + break; + case DW_AT_rnglists_base: + SetRangesBase(form_value.Unsigned()); + 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: + m_gnu_addr_base = form_value.Unsigned(); + break; + case DW_AT_GNU_ranges_base: + m_gnu_ranges_base = form_value.Unsigned(); + break; + case DW_AT_GNU_dwo_id: + m_dwo_id = form_value.Unsigned(); + break; + } + } + + if (m_is_dwo) { + m_has_parsed_non_skeleton_unit = true; + SetDwoStrOffsetsBase(); + return; + } +} + +size_t DWARFUnit::GetDebugInfoSize() const { + return GetLengthByteSize() + GetLength() - GetHeaderByteSize(); +} + +const llvm::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() { + ExtractUnitDIENoDwoIfNeeded(); + return m_line_table_offset; +} + +void DWARFUnit::SetAddrBase(dw_addr_t addr_base) { m_addr_base = addr_base; } + +// Parse the rangelist table header, including the optional array of offsets +// following it (DWARF v5 and later). +template <typename ListTableType> +static llvm::Expected<ListTableType> +ParseListTableHeader(const llvm::DWARFDataExtractor &data, uint64_t offset, + DwarfFormat format) { + // We are expected to be called with Offset 0 or pointing just past the table + // header. Correct Offset in the latter case so that it points to the start + // of the header. + if (offset == 0) { + // This means DW_AT_rnglists_base is missing and therefore DW_FORM_rnglistx + // cannot be handled. Returning a default-constructed ListTableType allows + // DW_FORM_sec_offset to be supported. + return ListTableType(); + } + + uint64_t HeaderSize = llvm::DWARFListTableHeader::getHeaderSize(format); + if (offset < HeaderSize) + return llvm::createStringError(std::errc::invalid_argument, + "did not detect a valid" + " list table with base = 0x%" PRIx64 "\n", + offset); + offset -= HeaderSize; + ListTableType Table; + if (llvm::Error E = Table.extractHeaderAndOffsets(data, &offset)) + return std::move(E); + return Table; +} + +void DWARFUnit::SetLoclistsBase(dw_addr_t loclists_base) { + uint64_t offset = 0; + if (const llvm::DWARFUnitIndex::Entry *entry = m_header.getIndexEntry()) { + const auto *contribution = entry->getContribution(llvm::DW_SECT_LOCLISTS); + if (!contribution) { + GetSymbolFileDWARF().GetObjectFile()->GetModule()->ReportError( + "Failed to find location list contribution for CU with DWO Id " + "{0:x16}", + *GetDWOId()); + return; + } + offset += contribution->getOffset(); + } + m_loclists_base = loclists_base; + + uint64_t header_size = llvm::DWARFListTableHeader::getHeaderSize(DWARF32); + if (loclists_base < header_size) + return; + + m_loclist_table_header.emplace(".debug_loclists", "locations"); + offset += loclists_base - header_size; + if (llvm::Error E = m_loclist_table_header->extract( + m_dwarf.GetDWARFContext().getOrLoadLocListsData().GetAsLLVMDWARF(), + &offset)) { + GetSymbolFileDWARF().GetObjectFile()->GetModule()->ReportError( + "Failed to extract location list table at offset {0:x16} (location " + "list base: {1:x16}): {2}", + offset, loclists_base, toString(std::move(E)).c_str()); + } +} + +std::unique_ptr<llvm::DWARFLocationTable> +DWARFUnit::GetLocationTable(const DataExtractor &data) const { + llvm::DWARFDataExtractor llvm_data( + data.GetData(), data.GetByteOrder() == lldb::eByteOrderLittle, + data.GetAddressByteSize()); + + if (m_is_dwo || GetVersion() >= 5) + return std::make_unique<llvm::DWARFDebugLoclists>(llvm_data, GetVersion()); + return std::make_unique<llvm::DWARFDebugLoc>(llvm_data); +} + +DWARFDataExtractor DWARFUnit::GetLocationData() const { + DWARFContext &Ctx = GetSymbolFileDWARF().GetDWARFContext(); + const DWARFDataExtractor &data = + GetVersion() >= 5 ? Ctx.getOrLoadLocListsData() : Ctx.getOrLoadLocData(); + if (const llvm::DWARFUnitIndex::Entry *entry = m_header.getIndexEntry()) { + if (const auto *contribution = entry->getContribution( + GetVersion() >= 5 ? llvm::DW_SECT_LOCLISTS : llvm::DW_SECT_EXT_LOC)) + return DWARFDataExtractor(data, contribution->getOffset(), + contribution->getLength32()); + return DWARFDataExtractor(); + } + return data; +} + +DWARFDataExtractor DWARFUnit::GetRnglistData() const { + DWARFContext &Ctx = GetSymbolFileDWARF().GetDWARFContext(); + const DWARFDataExtractor &data = Ctx.getOrLoadRngListsData(); + if (const llvm::DWARFUnitIndex::Entry *entry = m_header.getIndexEntry()) { + if (const auto *contribution = + entry->getContribution(llvm::DW_SECT_RNGLISTS)) + return DWARFDataExtractor(data, contribution->getOffset(), + contribution->getLength32()); + GetSymbolFileDWARF().GetObjectFile()->GetModule()->ReportError( + "Failed to find range list contribution for CU with signature {0:x16}", + entry->getSignature()); + + return DWARFDataExtractor(); + } + return data; +} + +void DWARFUnit::SetRangesBase(dw_addr_t ranges_base) { + lldbassert(!m_rnglist_table_done); + + m_ranges_base = ranges_base; +} + +const std::optional<llvm::DWARFDebugRnglistTable> & +DWARFUnit::GetRnglistTable() { + if (GetVersion() >= 5 && !m_rnglist_table_done) { + m_rnglist_table_done = true; + if (auto table_or_error = + ParseListTableHeader<llvm::DWARFDebugRnglistTable>( + GetRnglistData().GetAsLLVMDWARF(), m_ranges_base, DWARF32)) + m_rnglist_table = std::move(table_or_error.get()); + else + GetSymbolFileDWARF().GetObjectFile()->GetModule()->ReportError( + "Failed to extract range list table at offset {0:x16}: {1}", + m_ranges_base, toString(table_or_error.takeError()).c_str()); + } + return m_rnglist_table; +} + +// This function is called only for DW_FORM_rnglistx. +llvm::Expected<uint64_t> DWARFUnit::GetRnglistOffset(uint32_t Index) { + if (!GetRnglistTable()) + return llvm::createStringError(std::errc::invalid_argument, + "missing or invalid range list table"); + if (!m_ranges_base) + return llvm::createStringError( + std::errc::invalid_argument, + llvm::formatv("DW_FORM_rnglistx cannot be used without " + "DW_AT_rnglists_base for CU at {0:x16}", + GetOffset()) + .str() + .c_str()); + if (std::optional<uint64_t> off = GetRnglistTable()->getOffsetEntry( + GetRnglistData().GetAsLLVM(), Index)) + return *off + m_ranges_base; + return llvm::createStringError( + std::errc::invalid_argument, + "invalid range list table index %u; OffsetEntryCount is %u, " + "DW_AT_rnglists_base is %" PRIu64, + Index, GetRnglistTable()->getOffsetEntryCount(), m_ranges_base); +} + +void DWARFUnit::SetStrOffsetsBase(dw_offset_t str_offsets_base) { + m_str_offsets_base = str_offsets_base; +} + +dw_addr_t DWARFUnit::ReadAddressFromDebugAddrSection(uint32_t index) const { + uint32_t index_size = GetAddressByteSize(); + dw_offset_t addr_base = GetAddrBase(); + dw_addr_t offset = addr_base + static_cast<dw_addr_t>(index) * index_size; + const DWARFDataExtractor &data = + m_dwarf.GetDWARFContext().getOrLoadAddrData(); + if (data.ValidOffsetForDataOfSize(offset, index_size)) + return data.GetMaxU64_unchecked(&offset, index_size); + return LLDB_INVALID_ADDRESS; +} + +// 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 && !m_dwo->m_cancel_scopes) + m_dwo->ClearDIEsRWLocked(); +} + +lldb::ByteOrder DWARFUnit::GetByteOrder() const { + return m_dwarf.GetObjectFile()->GetByteOrder(); +} + +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) + return DWARFDIE(); // Not found + + if (!ContainsDIEOffset(die_offset)) { + GetSymbolFileDWARF().GetObjectFile()->GetModule()->ReportError( + "GetDIE for DIE {0:x16} is outside of its CU {0:x16}", die_offset, + GetOffset()); + return DWARFDIE(); // Not found + } + + 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 && die_offset == (*pos).GetOffset()) + return DWARFDIE(this, &(*pos)); + return DWARFDIE(); // Not found +} + +llvm::StringRef DWARFUnit::PeekDIEName(dw_offset_t die_offset) { + DWARFDebugInfoEntry die; + if (!die.Extract(GetData(), *this, &die_offset)) + return llvm::StringRef(); + + // Does die contain a DW_AT_Name? + if (const char *name = + die.GetAttributeValueAsString(this, DW_AT_name, nullptr)) + return name; + + // Does its DW_AT_specification or DW_AT_abstract_origin contain an AT_Name? + for (auto attr : {DW_AT_specification, DW_AT_abstract_origin}) { + DWARFFormValue form_value; + if (!die.GetAttributeValue(this, attr, form_value)) + continue; + auto [unit, offset] = form_value.ReferencedUnitAndOffset(); + if (unit) + if (auto name = unit->PeekDIEName(offset); !name.empty()) + return name; + } + + return llvm::StringRef(); +} + +DWARFUnit &DWARFUnit::GetNonSkeletonUnit() { + ExtractUnitDIEIfNeeded(); + if (m_dwo) + return *m_dwo; + return *this; +} + +uint8_t DWARFUnit::GetAddressByteSize(const DWARFUnit *cu) { + if (cu) + return cu->GetAddressByteSize(); + return DWARFUnit::GetDefaultAddressSize(); +} + +uint8_t DWARFUnit::GetDefaultAddressSize() { return 4; } + +DWARFCompileUnit *DWARFUnit::GetSkeletonUnit() { + if (m_skeleton_unit == nullptr && IsDWOUnit()) { + SymbolFileDWARFDwo *dwo = + llvm::dyn_cast_or_null<SymbolFileDWARFDwo>(&GetSymbolFileDWARF()); + // Do a reverse lookup if the skeleton compile unit wasn't set. + if (dwo) + m_skeleton_unit = dwo->GetBaseSymbolFile().GetSkeletonUnit(this); + } + return llvm::dyn_cast_or_null<DWARFCompileUnit>(m_skeleton_unit); +} + +void DWARFUnit::SetSkeletonUnit(DWARFUnit *skeleton_unit) { + // If someone is re-setting the skeleton compile unit backlink, make sure + // it is setting it to a valid value when it wasn't valid, or if the + // value in m_skeleton_unit was valid, it should be the same value. + assert(skeleton_unit); + assert(m_skeleton_unit == nullptr || m_skeleton_unit == skeleton_unit); + m_skeleton_unit = skeleton_unit; +} + +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) + return GetProducerVersion() >= llvm::VersionTuple(425, 0, 13); + // Assume all other compilers didn't have incorrect ObjC bitfield info. + return true; +} + +void DWARFUnit::ParseProducerInfo() { + m_producer = eProducerOther; + const DWARFDebugInfoEntry *die = GetUnitDIEPtrOnly(); + if (!die) + return; + + llvm::StringRef producer( + die->GetAttributeValueAsString(this, DW_AT_producer, nullptr)); + if (producer.empty()) + return; + + static const RegularExpression g_swiftlang_version_regex( + llvm::StringRef(R"(swiftlang-([0-9]+\.[0-9]+\.[0-9]+(\.[0-9]+)?))")); + static const RegularExpression g_clang_version_regex( + llvm::StringRef(R"(clang-([0-9]+\.[0-9]+\.[0-9]+(\.[0-9]+)?))")); + static const RegularExpression g_llvm_gcc_regex( + llvm::StringRef(R"(4\.[012]\.[01] )" + R"(\(Based on Apple Inc\. build [0-9]+\) )" + R"(\(LLVM build [\.0-9]+\)$)")); + + llvm::SmallVector<llvm::StringRef, 3> matches; + if (g_swiftlang_version_regex.Execute(producer, &matches)) { + m_producer_version.tryParse(matches[1]); + m_producer = eProducerSwift; + } else if (producer.contains("clang")) { + if (g_clang_version_regex.Execute(producer, &matches)) + m_producer_version.tryParse(matches[1]); + m_producer = eProducerClang; + } else if (producer.contains("GNU")) { + m_producer = eProducerGCC; + } else if (g_llvm_gcc_regex.Execute(producer)) { + m_producer = eProducerLLVMGCC; + } +} + +DWARFProducer DWARFUnit::GetProducer() { + if (m_producer == eProducerInvalid) + ParseProducerInfo(); + return m_producer; +} + +llvm::VersionTuple DWARFUnit::GetProducerVersion() { + if (m_producer_version.empty()) + ParseProducerInfo(); + return m_producer_version; +} + +uint64_t DWARFUnit::GetDWARFLanguageType() { + if (m_language_type) + return *m_language_type; + + const DWARFDebugInfoEntry *die = GetUnitDIEPtrOnly(); + if (!die) + m_language_type = 0; + else + m_language_type = 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) { + if (!path_from_dwarf.contains(':')) + return 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.starts_with("\\") || path.starts_with("/"))) + return path_from_dwarf; + + 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).value_or(FileSpec::Style::native); + m_comp_dir = 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).value_or(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(bool load_all_debug_info) { + if (load_all_debug_info) + ExtractUnitDIEIfNeeded(); + if (m_dwo) + return &llvm::cast<SymbolFileDWARFDwo>(m_dwo->GetSymbolFileDWARF()); + return nullptr; +} + +const DWARFDebugAranges &DWARFUnit::GetFunctionAranges() { + if (m_func_aranges_up == nullptr) { + m_func_aranges_up = std::make_unique<DWARFDebugAranges>(); + const DWARFDebugInfoEntry *die = DIEPtr(); + if (die) + die->BuildFunctionAddressRangeTable(this, m_func_aranges_up.get()); + + if (m_dwo) { + const DWARFDebugInfoEntry *dwo_die = m_dwo->DIEPtr(); + if (dwo_die) + dwo_die->BuildFunctionAddressRangeTable(m_dwo.get(), + m_func_aranges_up.get()); + } + + const bool minimize = false; + m_func_aranges_up->Sort(minimize); + } + return *m_func_aranges_up; +} + +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)); + + DWARFContext &context = dwarf.GetDWARFContext(); + + // FIXME: Either properly map between DIERef::Section and + // llvm::DWARFSectionKind or switch to llvm's definition entirely. + llvm::DWARFSectionKind section_kind_llvm = + section == DIERef::Section::DebugInfo + ? llvm::DWARFSectionKind::DW_SECT_INFO + : llvm::DWARFSectionKind::DW_SECT_EXT_TYPES; + + llvm::DWARFDataExtractor debug_info_llvm = debug_info.GetAsLLVMDWARF(); + llvm::DWARFUnitHeader header; + if (llvm::Error extract_err = header.extract( + context.GetAsLLVM(), debug_info_llvm, offset_ptr, section_kind_llvm)) + return std::move(extract_err); + + if (context.isDwo()) { + const llvm::DWARFUnitIndex::Entry *entry = nullptr; + const llvm::DWARFUnitIndex &index = header.isTypeUnit() + ? context.GetAsLLVM().getTUIndex() + : context.GetAsLLVM().getCUIndex(); + if (index) { + if (header.isTypeUnit()) + entry = index.getFromHash(header.getTypeHash()); + else if (auto dwo_id = header.getDWOId()) + entry = index.getFromHash(*dwo_id); + } + if (!entry) + entry = index.getFromOffset(header.getOffset()); + if (entry) + if (llvm::Error err = header.applyIndexEntry(entry)) + return std::move(err); + } + + const llvm::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( + header.getAbbrOffset()); + if (!abbr_offset_OK) + return llvm::make_error<llvm::object::GenericBinaryError>( + "Abbreviation offset for unit is not valid"); + + llvm::Expected<const llvm::DWARFAbbreviationDeclarationSet *> abbrevs_or_err = + abbr->getAbbreviationDeclarationSet(header.getAbbrOffset()); + if (!abbrevs_or_err) + return abbrevs_or_err.takeError(); + + const llvm::DWARFAbbreviationDeclarationSet *abbrevs = *abbrevs_or_err; + if (!abbrevs) + return llvm::make_error<llvm::object::GenericBinaryError>( + "No abbrev exists at the specified offset."); + + bool is_dwo = dwarf.GetDWARFContext().isDwo(); + if (header.isTypeUnit()) + return DWARFUnitSP( + new DWARFTypeUnit(dwarf, uid, header, *abbrevs, section, is_dwo)); + return DWARFUnitSP( + new DWARFCompileUnit(dwarf, uid, header, *abbrevs, section, is_dwo)); +} + +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."); +} + +std::optional<uint64_t> +DWARFUnit::GetStringOffsetSectionItem(uint32_t index) const { + offset_t offset = GetStrOffsetsBase() + index * 4; + return m_dwarf.GetDWARFContext().getOrLoadStrOffsetsData().GetU32(&offset); +} + +llvm::Expected<DWARFRangeList> +DWARFUnit::FindRnglistFromOffset(dw_offset_t offset) { + if (GetVersion() <= 4) { + const DWARFDebugRanges *debug_ranges = m_dwarf.GetDebugRanges(); + if (!debug_ranges) + return llvm::make_error<llvm::object::GenericBinaryError>( + "No debug_ranges section"); + return debug_ranges->FindRanges(this, offset); + } + + if (!GetRnglistTable()) + return llvm::createStringError(std::errc::invalid_argument, + "missing or invalid range list table"); + + llvm::DWARFDataExtractor data = GetRnglistData().GetAsLLVMDWARF(); + + // As DW_AT_rnglists_base may be missing we need to call setAddressSize. + data.setAddressSize(m_header.getAddressByteSize()); + auto range_list_or_error = GetRnglistTable()->findList(data, offset); + if (!range_list_or_error) + return range_list_or_error.takeError(); + + llvm::Expected<llvm::DWARFAddressRangesVector> llvm_ranges = + range_list_or_error->getAbsoluteRanges( + llvm::object::SectionedAddress{GetBaseAddress()}, + GetAddressByteSize(), [&](uint32_t index) { + uint32_t index_size = GetAddressByteSize(); + dw_offset_t addr_base = GetAddrBase(); + lldb::offset_t offset = + addr_base + static_cast<lldb::offset_t>(index) * index_size; + return llvm::object::SectionedAddress{ + m_dwarf.GetDWARFContext().getOrLoadAddrData().GetMaxU64( + &offset, index_size)}; + }); + if (!llvm_ranges) + return llvm_ranges.takeError(); + + DWARFRangeList ranges; + for (const llvm::DWARFAddressRange &llvm_range : *llvm_ranges) { + ranges.Append(DWARFRangeList::Entry(llvm_range.LowPC, + llvm_range.HighPC - llvm_range.LowPC)); + } + ranges.Sort(); + return ranges; +} + +llvm::Expected<DWARFRangeList> DWARFUnit::FindRnglistFromIndex(uint32_t index) { + llvm::Expected<uint64_t> maybe_offset = GetRnglistOffset(index); + if (!maybe_offset) + return maybe_offset.takeError(); + return FindRnglistFromOffset(*maybe_offset); +} + +bool DWARFUnit::HasAny(llvm::ArrayRef<dw_tag_t> tags) { + ExtractUnitDIEIfNeeded(); + if (m_dwo) + return m_dwo->HasAny(tags); + + for (const auto &die : m_die_array) { + for (const auto tag : tags) { + if (tag == die.Tag()) + return true; + } + } + return false; +} 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..85c37971ced8 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h @@ -0,0 +1,377 @@ +//===-- 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 LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_DWARFUNIT_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_DWARFUNIT_H + +#include "DWARFDIE.h" +#include "DWARFDebugInfoEntry.h" +#include "lldb/Utility/XcodeSDK.h" +#include "lldb/lldb-enumerations.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugAbbrev.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugRnglists.h" +#include "llvm/Support/RWMutex.h" +#include <atomic> +#include <optional> + +namespace lldb_private::plugin { +namespace dwarf { +class DWARFUnit; +class DWARFCompileUnit; +class NameToDIE; +class SymbolFileDWARF; +class SymbolFileDWARFDwo; + +typedef std::shared_ptr<DWARFUnit> DWARFUnitSP; + +enum DWARFProducer { + eProducerInvalid = 0, + eProducerClang, + eProducerGCC, + eProducerLLVMGCC, + eProducerSwift, + eProducerOther +}; + +class DWARFUnit : public 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 DWARFDataExtractor &debug_info, DIERef::Section section, + lldb::offset_t *offset_ptr); + virtual ~DWARFUnit(); + + bool IsDWOUnit() { return m_is_dwo; } + /// Get the DWO ID from the DWARFUnitHeader for DWARF5, or from the unit DIE's + /// DW_AT_dwo_id or DW_AT_GNU_dwo_id for DWARF4 and earlier. + std::optional<uint64_t> GetDWOId(); + /// Get the DWO ID from the DWARFUnitHeader only. DWARF5 skeleton units have + /// the DWO ID in the compile unit header and we sometimes only want to access + /// this cheap value without causing the more expensive attribute fetches that + /// GetDWOId() uses. + std::optional<uint64_t> GetHeaderDWOId() { return m_header.getDWOId(); } + void ExtractUnitDIEIfNeeded(); + void ExtractUnitDIENoDwoIfNeeded(); + void ExtractDIEsIfNeeded(); + + class ScopedExtractDIEs { + DWARFUnit *m_cu; + + public: + bool m_clear_dies = false; + ScopedExtractDIEs(DWARFUnit &cu); + ~ScopedExtractDIEs(); + ScopedExtractDIEs(const ScopedExtractDIEs &) = delete; + const ScopedExtractDIEs &operator=(const ScopedExtractDIEs &) = delete; + ScopedExtractDIEs(ScopedExtractDIEs &&rhs); + ScopedExtractDIEs &operator=(ScopedExtractDIEs &&rhs); + }; + ScopedExtractDIEs ExtractDIEsScoped(); + + bool Verify(Stream *s) const; + virtual void Dump(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 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. + dw_offset_t GetLength() const { return m_header.getLength(); } + uint16_t GetVersion() const { return m_header.getVersion(); } + const llvm::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.value_or(0); } + 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 SetLoclistsBase(dw_addr_t loclists_base); + void SetRangesBase(dw_addr_t ranges_base); + void SetStrOffsetsBase(dw_offset_t str_offsets_base); + virtual void BuildAddressRangeTable(DWARFDebugAranges *debug_aranges) = 0; + + dw_addr_t ReadAddressFromDebugAddrSection(uint32_t index) const; + + lldb::ByteOrder GetByteOrder() const; + + const DWARFDebugAranges &GetFunctionAranges(); + + void SetBaseAddress(dw_addr_t base_addr); + + DWARFBaseDIE GetUnitDIEOnly() { return {this, GetUnitDIEPtrOnly()}; } + + DWARFDIE DIE() { return DWARFDIE(this, DIEPtr()); } + + DWARFDIE GetDIE(dw_offset_t die_offset); + + /// Returns the AT_Name of the DIE at `die_offset`, if it exists, without + /// parsing the entire compile unit. An empty is string is returned upon + /// error or if the attribute is not present. + llvm::StringRef PeekDIEName(dw_offset_t die_offset); + + DWARFUnit &GetNonSkeletonUnit(); + + static uint8_t GetAddressByteSize(const DWARFUnit *cu); + + static uint8_t GetDefaultAddressSize(); + + lldb_private::CompileUnit *GetLLDBCompUnit() const { return m_lldb_cu; } + + void SetLLDBCompUnit(lldb_private::CompileUnit *cu) { m_lldb_cu = cu; } + + /// Get the skeleton compile unit for a DWO file. + /// + /// We need to keep track of the skeleton compile unit for a DWO file so + /// we can access it. Sometimes this value is cached when the skeleton + /// compile unit is first parsed, but if a .dwp file parses all of the + /// DWARFUnits in the file, the skeleton compile unit might not have been + /// parsed yet, to there might not be a backlink. This accessor handles + /// both cases correctly and avoids crashes. + DWARFCompileUnit *GetSkeletonUnit(); + + void SetSkeletonUnit(DWARFUnit *skeleton_unit); + + 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(); + + llvm::VersionTuple GetProducerVersion(); + + uint64_t GetDWARFLanguageType(); + + bool GetIsOptimized(); + + const FileSpec &GetCompilationDirectory(); + const FileSpec &GetAbsolutePath(); + FileSpec GetFile(size_t file_idx); + FileSpec::Style GetPathStyle(); + + SymbolFileDWARFDwo *GetDwoSymbolFile(bool load_all_debug_info = true); + + 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(); } + /// Note that this check only works for DWARF5+. + bool IsSkeletonUnit() const { + return GetUnitType() == llvm::dwarf::DW_UT_skeleton; + } + + std::optional<uint64_t> GetStringOffsetSectionItem(uint32_t index) const; + + /// 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); + + /// 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); + + /// Return a rangelist's offset based on an index. The index designates + /// an entry in the rangelist table's offset array and is supplied by + /// DW_FORM_rnglistx. + llvm::Expected<uint64_t> GetRnglistOffset(uint32_t Index); + + std::optional<uint64_t> GetLoclistOffset(uint32_t Index) { + if (!m_loclist_table_header) + return std::nullopt; + + std::optional<uint64_t> Offset = m_loclist_table_header->getOffsetEntry( + m_dwarf.GetDWARFContext().getOrLoadLocListsData().GetAsLLVM(), Index); + if (!Offset) + return std::nullopt; + return *Offset + m_loclists_base; + } + + /// Return the location table for parsing the given location list data. The + /// format is chosen according to the unit type. Never returns null. + std::unique_ptr<llvm::DWARFLocationTable> + GetLocationTable(const DataExtractor &data) const; + + DWARFDataExtractor GetLocationData() const; + + /// Returns true if any DIEs in the unit match any DW_TAG values in \a tags. + /// + /// \param[in] tags + /// An array of dw_tag_t values to check all abbrevitions for. + /// + /// \returns + /// True if any DIEs match any tag in \a tags, false otherwise. + bool HasAny(llvm::ArrayRef<dw_tag_t> tags); + + /// Get the fission .dwo file specific error for this compile unit. + /// + /// The skeleton compile unit only can have a DWO error. Any other type + /// of DWARFUnit will not have a valid DWO error. + /// + /// \returns + /// A valid DWO error if there is a problem with anything in the + /// locating or parsing inforamtion in the .dwo file + const Status &GetDwoError() const { return m_dwo_error; } + + /// Set the fission .dwo file specific error for this compile unit. + /// + /// This helps tracks issues that arise when trying to locate or parse a + /// .dwo file. Things like a missing .dwo file, DWO ID mismatch, and other + /// .dwo errors can be stored in each compile unit so the issues can be + /// communicated to the user. + void SetDwoError(const Status &error) { m_dwo_error = error; } + +protected: + DWARFUnit(SymbolFileDWARF &dwarf, lldb::user_id_t uid, + const llvm::DWARFUnitHeader &header, + const llvm::DWARFAbbreviationDeclarationSet &abbrevs, + DIERef::Section section, bool is_dwo); + + llvm::Error ExtractHeader(SymbolFileDWARF &dwarf, + const DWARFDataExtractor &data, + lldb::offset_t *offset_ptr); + + // Get the DWARF unit DWARF debug information entry. Parse the single DIE + // if needed. + const DWARFDebugInfoEntry *GetUnitDIEPtrOnly() { + ExtractUnitDIENoDwoIfNeeded(); + // m_first_die_mutex is not required as m_first_die is never cleared. + if (!m_first_die) + return nullptr; + 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 nullptr; + return &m_die_array[0]; + } + + const std::optional<llvm::DWARFDebugRnglistTable> &GetRnglistTable(); + + DWARFDataExtractor GetRnglistData() const; + + SymbolFileDWARF &m_dwarf; + std::shared_ptr<DWARFUnit> m_dwo; + llvm::DWARFUnitHeader m_header; + const llvm::DWARFAbbreviationDeclarationSet *m_abbrevs = nullptr; + lldb_private::CompileUnit *m_lldb_cu = nullptr; + // If this is a DWO file, we have a backlink to our skeleton compile unit. + DWARFUnit *m_skeleton_unit = 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; + llvm::VersionTuple m_producer_version; + std::optional<uint64_t> m_language_type; + LazyBool m_is_optimized = eLazyBoolCalculate; + std::optional<FileSpec> m_comp_dir; + std::optional<FileSpec> m_file_spec; + std::optional<dw_addr_t> m_addr_base; ///< Value of DW_AT_addr_base. + dw_addr_t m_loclists_base = 0; ///< Value of DW_AT_loclists_base. + dw_addr_t m_ranges_base = 0; ///< Value of DW_AT_rnglists_base. + std::optional<uint64_t> m_gnu_addr_base; + std::optional<uint64_t> m_gnu_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. + + std::optional<llvm::DWARFDebugRnglistTable> m_rnglist_table; + bool m_rnglist_table_done = false; + std::optional<llvm::DWARFListTableHeader> m_loclist_table_header; + + const DIERef::Section m_section; + bool m_is_dwo; + bool m_has_parsed_non_skeleton_unit; + /// Value of DW_AT_GNU_dwo_id (v4) or dwo_id from CU header (v5). + std::optional<uint64_t> m_dwo_id; + /// If we get an error when trying to load a .dwo file, save that error here. + /// Errors include .dwo/.dwp file not found, or the .dwp/.dwp file was found + /// but DWO ID doesn't match, etc. + Status m_dwo_error; + +private: + void ParseProducerInfo(); + void ExtractDIEsRWLocked(); + void ClearDIEsRWLocked(); + + void AddUnitDIE(const DWARFDebugInfoEntry &cu_die); + void SetDwoStrOffsetsBase(); + + void ComputeCompDirAndGuessPathStyle(); + void ComputeAbsolutePath(); + + DWARFUnit(const DWARFUnit &) = delete; + const DWARFUnit &operator=(const DWARFUnit &) = delete; +}; +} // namespace dwarf +} // namespace lldb_private::plugin + +#endif // LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_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..7e66b3dccf97 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.cpp @@ -0,0 +1,504 @@ +//===-- DebugNamesDWARFIndex.cpp ------------------------------------------===// +// +// 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/LogChannelDWARF.h" +#include "Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.h" +#include "lldb/Core/Module.h" +#include "lldb/Utility/RegularExpression.h" +#include "lldb/Utility/Stream.h" +#include "llvm/ADT/Sequence.h" +#include <optional> + +using namespace lldb_private; +using namespace lldb; +using namespace lldb_private::dwarf; +using namespace lldb_private::plugin::dwarf; + +llvm::Expected<std::unique_ptr<DebugNamesDWARFIndex>> +DebugNamesDWARFIndex::Create(Module &module, DWARFDataExtractor debug_names, + DWARFDataExtractor debug_str, + SymbolFileDWARF &dwarf) { + auto index_up = std::make_unique<DebugNames>(debug_names.GetAsLLVMDWARF(), + 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, dwarf)); +} + +llvm::DenseSet<uint64_t> +DebugNamesDWARFIndex::GetTypeUnitSignatures(const DebugNames &debug_names) { + llvm::DenseSet<uint64_t> result; + for (const DebugNames::NameIndex &ni : debug_names) { + const uint32_t num_tus = ni.getForeignTUCount(); + for (uint32_t tu = 0; tu < num_tus; ++tu) + result.insert(ni.getForeignTUSignature(tu)); + } + return result; +} + +llvm::DenseSet<dw_offset_t> +DebugNamesDWARFIndex::GetUnits(const DebugNames &debug_names) { + llvm::DenseSet<dw_offset_t> result; + for (const DebugNames::NameIndex &ni : debug_names) { + const uint32_t num_cus = ni.getCUCount(); + for (uint32_t cu = 0; cu < num_cus; ++cu) + result.insert(ni.getCUOffset(cu)); + const uint32_t num_tus = ni.getLocalTUCount(); + for (uint32_t tu = 0; tu < num_tus; ++tu) + result.insert(ni.getLocalTUOffset(tu)); + } + return result; +} + +std::optional<DWARFTypeUnit *> +DebugNamesDWARFIndex::GetForeignTypeUnit(const DebugNames::Entry &entry) const { + std::optional<uint64_t> type_sig = entry.getForeignTUTypeSignature(); + if (!type_sig.has_value()) + return std::nullopt; + + // Ask the entry for the skeleton compile unit offset and fetch the .dwo + // file from it and get the type unit by signature from there. If we find + // the type unit in the .dwo file, we don't need to check that the + // DW_AT_dwo_name matches because each .dwo file can have its own type unit. + std::optional<uint64_t> cu_offset = entry.getRelatedCUOffset(); + if (!cu_offset) + return nullptr; // Return NULL, this is a type unit, but couldn't find it. + + DWARFUnit *cu = + m_debug_info.GetUnitAtOffset(DIERef::Section::DebugInfo, *cu_offset); + if (!cu) + return nullptr; // Return NULL, this is a type unit, but couldn't find it. + + auto dwp_sp = m_debug_info.GetDwpSymbolFile(); + if (!dwp_sp) { + // No .dwp file, we need to load the .dwo file. + DWARFUnit &dwo_cu = cu->GetNonSkeletonUnit(); + // We don't need the check if the type unit matches the .dwo file if we have + // a .dwo file (not a .dwp), so we can just return the value here. + if (!dwo_cu.IsDWOUnit()) + return nullptr; // We weren't able to load the .dwo file. + return dwo_cu.GetSymbolFileDWARF().DebugInfo().GetTypeUnitForHash( + *type_sig); + } + // We have a .dwp file, just get the type unit from there. We need to verify + // that the type unit that ended up in the final .dwp file is the right type + // unit. Type units have signatures which are the same across multiple .dwo + // files, but only one of those type units will end up in the .dwp file. The + // contents of type units for the same type can be different in different .dwo + // files, which means the DIE offsets might not be the same between two + // different type units. So we need to determine if this accelerator table + // matches the type unit that ended up in the .dwp file. If it doesn't match, + // then we need to ignore this accelerator table entry as the type unit that + // is in the .dwp file will have its own index. In order to determine if the + // type unit that ended up in a .dwp file matches this DebugNames::Entry, we + // need to find the skeleton compile unit for this entry. + DWARFTypeUnit *foreign_tu = dwp_sp->DebugInfo().GetTypeUnitForHash(*type_sig); + if (!foreign_tu) + return nullptr; // Return NULL, this is a type unit, but couldn't find it. + + DWARFBaseDIE cu_die = cu->GetUnitDIEOnly(); + DWARFBaseDIE tu_die = foreign_tu->GetUnitDIEOnly(); + llvm::StringRef cu_dwo_name = + cu_die.GetAttributeValueAsString(DW_AT_dwo_name, nullptr); + llvm::StringRef tu_dwo_name = + tu_die.GetAttributeValueAsString(DW_AT_dwo_name, nullptr); + if (cu_dwo_name == tu_dwo_name) + return foreign_tu; // We found a match! + return nullptr; // Return NULL, this is a type unit, but couldn't find it. +} + +DWARFUnit * +DebugNamesDWARFIndex::GetNonSkeletonUnit(const DebugNames::Entry &entry) const { + + if (std::optional<DWARFTypeUnit *> foreign_tu = GetForeignTypeUnit(entry)) + return foreign_tu.value(); + + // Look for a DWARF unit offset (CU offset or local TU offset) as they are + // both offsets into the .debug_info section. + std::optional<uint64_t> unit_offset = entry.getCUOffset(); + if (!unit_offset) + unit_offset = entry.getLocalTUOffset(); + if (unit_offset) { + if (DWARFUnit *cu = m_debug_info.GetUnitAtOffset(DIERef::Section::DebugInfo, + *unit_offset)) + return &cu->GetNonSkeletonUnit(); + } + return nullptr; +} + +DWARFDIE DebugNamesDWARFIndex::GetDIE(const DebugNames::Entry &entry) const { + DWARFUnit *unit = GetNonSkeletonUnit(entry); + std::optional<uint64_t> die_offset = entry.getDIEUnitOffset(); + if (!unit || !die_offset) + return DWARFDIE(); + if (DWARFDIE die = unit->GetDIE(unit->GetOffset() + *die_offset)) + return die; + + m_module.ReportErrorIfModifyDetected( + "the DWARF debug information has been modified (bad offset {0:x} in " + "debug_names section)\n", + *die_offset); + return DWARFDIE(); +} + +bool DebugNamesDWARFIndex::ProcessEntry( + const DebugNames::Entry &entry, + llvm::function_ref<bool(DWARFDIE die)> callback) { + DWARFDIE die = GetDIE(entry); + if (!die) + return true; + // Clang used to erroneously emit index entries for declaration DIEs in case + // when the definition is in a type unit (llvm.org/pr77696). + if (die.IsStructUnionOrClass() && + die.GetAttributeValueAsUnsigned(DW_AT_declaration, 0)) + return true; + return callback(die); +} + +void DebugNamesDWARFIndex::MaybeLogLookupError(llvm::Error error, + const DebugNames::NameIndex &ni, + llvm::StringRef name) { + // Ignore SentinelErrors, log everything else. + LLDB_LOG_ERROR( + GetLog(DWARFLog::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, llvm::function_ref<bool(DWARFDIE die)> callback) { + for (const DebugNames::Entry &entry : + m_debug_names_up->equal_range(basename.GetStringRef())) { + if (entry.tag() != DW_TAG_variable) + continue; + + if (!ProcessEntry(entry, callback)) + return; + } + + m_fallback.GetGlobalVariables(basename, callback); +} + +void DebugNamesDWARFIndex::GetGlobalVariables( + const RegularExpression ®ex, + llvm::function_ref<bool(DWARFDIE die)> callback) { + for (const DebugNames::NameIndex &ni: *m_debug_names_up) { + for (DebugNames::NameTableEntry nte: ni) { + Mangled mangled_name(nte.getString()); + if (!mangled_name.NameMatches(regex)) + continue; + + uint64_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 (!ProcessEntry(*entry_or, callback)) + return; + } + MaybeLogLookupError(entry_or.takeError(), ni, nte.getString()); + } + } + + m_fallback.GetGlobalVariables(regex, callback); +} + +void DebugNamesDWARFIndex::GetGlobalVariables( + DWARFUnit &cu, llvm::function_ref<bool(DWARFDIE die)> callback) { + uint64_t cu_offset = cu.GetOffset(); + bool found_entry_for_cu = false; + for (const DebugNames::NameIndex &ni : *m_debug_names_up) { + // Check if this name index contains an entry for the given CU. + bool cu_matches = false; + for (uint32_t i = 0; i < ni.getCUCount(); ++i) { + if (ni.getCUOffset(i) == cu_offset) { + cu_matches = true; + break; + } + } + if (!cu_matches) + continue; + + for (DebugNames::NameTableEntry nte : ni) { + uint64_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; + + found_entry_for_cu = true; + if (!ProcessEntry(*entry_or, callback)) + return; + } + MaybeLogLookupError(entry_or.takeError(), ni, nte.getString()); + } + } + // If no name index for that particular CU was found, fallback to + // creating the manual index. + if (!found_entry_for_cu) + m_fallback.GetGlobalVariables(cu, callback); +} + +void DebugNamesDWARFIndex::GetCompleteObjCClass( + ConstString class_name, bool must_be_implementation, + llvm::function_ref<bool(DWARFDIE die)> callback) { + // Keep a list of incomplete types as fallback for when we don't find the + // complete type. + std::vector<DWARFDIE> 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; + + DWARFDIE die = GetDIE(entry); + if (!die) { + // Report invalid + continue; + } + DWARFUnit *cu = die.GetCU(); + if (!cu->Supports_DW_AT_APPLE_objc_complete_type()) { + incomplete_types.push_back(die); + continue; + } + + if (die.GetAttributeValueAsUnsigned(DW_AT_APPLE_objc_complete_type, 0)) { + // If we find the complete version we're done. + callback(die); + return; + } + incomplete_types.push_back(die); + } + + for (DWARFDIE die : incomplete_types) + if (!callback(die)) + return; + + m_fallback.GetCompleteObjCClass(class_name, must_be_implementation, callback); +} + +namespace { +using Entry = llvm::DWARFDebugNames::Entry; + +/// If `entry` and all of its parents have an `IDX_parent`, use that information +/// to build and return a list of at most `max_parents` parent Entries. +/// `entry` itself is not included in the list. +/// If any parent does not have an `IDX_parent`, or the Entry data is corrupted, +/// nullopt is returned. +std::optional<llvm::SmallVector<Entry, 4>> +getParentChain(Entry entry, uint32_t max_parents) { + llvm::SmallVector<Entry, 4> parent_entries; + + do { + if (!entry.hasParentInformation()) + return std::nullopt; + + llvm::Expected<std::optional<Entry>> parent = entry.getParentDIEEntry(); + if (!parent) { + // Bad data. + LLDB_LOG_ERROR( + GetLog(DWARFLog::Lookups), parent.takeError(), + "Failed to extract parent entry from a non-empty IDX_parent"); + return std::nullopt; + } + + // Last parent in the chain. + if (!parent->has_value()) + break; + + parent_entries.push_back(**parent); + entry = **parent; + } while (parent_entries.size() < max_parents); + + return parent_entries; +} +} // namespace + +void DebugNamesDWARFIndex::GetFullyQualifiedType( + const DWARFDeclContext &context, + llvm::function_ref<bool(DWARFDIE die)> callback) { + if (context.GetSize() == 0) + return; + + llvm::StringRef leaf_name = context[0].name; + llvm::SmallVector<llvm::StringRef> parent_names; + for (auto idx : llvm::seq<int>(1, context.GetSize())) + parent_names.emplace_back(context[idx].name); + + // For each entry, grab its parent chain and check if we have a match. + for (const DebugNames::Entry &entry : + m_debug_names_up->equal_range(leaf_name)) { + if (!isType(entry.tag())) + continue; + + // If we get a NULL foreign_tu back, the entry doesn't match the type unit + // in the .dwp file, or we were not able to load the .dwo file or the DWO ID + // didn't match. + std::optional<DWARFTypeUnit *> foreign_tu = GetForeignTypeUnit(entry); + if (foreign_tu && foreign_tu.value() == nullptr) + continue; + + // Grab at most one extra parent, subsequent parents are not necessary to + // test equality. + std::optional<llvm::SmallVector<Entry, 4>> parent_chain = + getParentChain(entry, parent_names.size() + 1); + + if (!parent_chain) { + // Fallback: use the base class implementation. + if (!ProcessEntry(entry, [&](DWARFDIE die) { + return GetFullyQualifiedTypeImpl(context, die, callback); + })) + return; + continue; + } + + if (SameParentChain(parent_names, *parent_chain) && + !ProcessEntry(entry, callback)) + return; + } +} + +bool DebugNamesDWARFIndex::SameParentChain( + llvm::ArrayRef<llvm::StringRef> parent_names, + llvm::ArrayRef<DebugNames::Entry> parent_entries) const { + + if (parent_entries.size() != parent_names.size()) + return false; + + auto SameAsEntryATName = [this](llvm::StringRef name, + const DebugNames::Entry &entry) { + // Peek at the AT_name of `entry` and test equality to `name`. + auto maybe_dieoffset = entry.getDIEUnitOffset(); + if (!maybe_dieoffset) + return false; + DWARFUnit *unit = GetNonSkeletonUnit(entry); + if (!unit) + return false; + return name == unit->PeekDIEName(unit->GetOffset() + *maybe_dieoffset); + }; + + // If the AT_name of any parent fails to match the expected name, we don't + // have a match. + for (auto [parent_name, parent_entry] : + llvm::zip_equal(parent_names, parent_entries)) + if (!SameAsEntryATName(parent_name, parent_entry)) + return false; + return true; +} + +void DebugNamesDWARFIndex::GetTypes( + ConstString name, llvm::function_ref<bool(DWARFDIE die)> callback) { + for (const DebugNames::Entry &entry : + m_debug_names_up->equal_range(name.GetStringRef())) { + if (isType(entry.tag())) { + if (!ProcessEntry(entry, callback)) + return; + } + } + + m_fallback.GetTypes(name, callback); +} + +void DebugNamesDWARFIndex::GetTypes( + const DWARFDeclContext &context, + llvm::function_ref<bool(DWARFDIE die)> callback) { + auto name = context[0].name; + for (const DebugNames::Entry &entry : m_debug_names_up->equal_range(name)) { + if (entry.tag() == context[0].tag) { + if (!ProcessEntry(entry, callback)) + return; + } + } + + m_fallback.GetTypes(context, callback); +} + +void DebugNamesDWARFIndex::GetNamespaces( + ConstString name, llvm::function_ref<bool(DWARFDIE die)> callback) { + for (const DebugNames::Entry &entry : + m_debug_names_up->equal_range(name.GetStringRef())) { + lldb_private::dwarf::Tag entry_tag = entry.tag(); + if (entry_tag == DW_TAG_namespace || + entry_tag == DW_TAG_imported_declaration) { + if (!ProcessEntry(entry, callback)) + return; + } + } + + m_fallback.GetNamespaces(name, callback); +} + +void DebugNamesDWARFIndex::GetFunctions( + const Module::LookupInfo &lookup_info, SymbolFileDWARF &dwarf, + const CompilerDeclContext &parent_decl_ctx, + llvm::function_ref<bool(DWARFDIE die)> callback) { + ConstString name = lookup_info.GetLookupName(); + std::set<DWARFDebugInfoEntry *> seen; + 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 (DWARFDIE die = GetDIE(entry)) { + if (!ProcessFunctionDIE(lookup_info, die, parent_decl_ctx, + [&](DWARFDIE die) { + if (!seen.insert(die.GetDIE()).second) + return true; + return callback(die); + })) + return; + } + } + + m_fallback.GetFunctions(lookup_info, dwarf, parent_decl_ctx, callback); +} + +void DebugNamesDWARFIndex::GetFunctions( + const RegularExpression ®ex, + llvm::function_ref<bool(DWARFDIE die)> callback) { + for (const DebugNames::NameIndex &ni: *m_debug_names_up) { + for (DebugNames::NameTableEntry nte: ni) { + if (!regex.Execute(nte.getString())) + continue; + + uint64_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; + + if (!ProcessEntry(*entry_or, callback)) + return; + } + MaybeLogLookupError(entry_or.takeError(), ni, nte.getString()); + } + } + + m_fallback.GetFunctions(regex, callback); +} + +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..cb15c1d4f994 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.h @@ -0,0 +1,133 @@ +//===-- 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_SOURCE_PLUGINS_SYMBOLFILE_DWARF_DEBUGNAMESDWARFINDEX_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_DEBUGNAMESDWARFINDEX_H + +#include "Plugins/SymbolFile/DWARF/DWARFIndex.h" +#include "Plugins/SymbolFile/DWARF/ManualDWARFIndex.h" +#include "Plugins/SymbolFile/DWARF/SymbolFileDWARF.h" +#include "lldb/Utility/ConstString.h" +#include "llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h" +#include <optional> + +namespace lldb_private::plugin { +namespace dwarf { +class DebugNamesDWARFIndex : public DWARFIndex { +public: + static llvm::Expected<std::unique_ptr<DebugNamesDWARFIndex>> + Create(Module &module, DWARFDataExtractor debug_names, + DWARFDataExtractor debug_str, SymbolFileDWARF &dwarf); + + void Preload() override { m_fallback.Preload(); } + + void + GetGlobalVariables(ConstString basename, + llvm::function_ref<bool(DWARFDIE die)> callback) override; + void + GetGlobalVariables(const RegularExpression ®ex, + llvm::function_ref<bool(DWARFDIE die)> callback) override; + void + GetGlobalVariables(DWARFUnit &cu, + llvm::function_ref<bool(DWARFDIE die)> callback) override; + void + GetObjCMethods(ConstString class_name, + llvm::function_ref<bool(DWARFDIE die)> callback) override {} + void GetCompleteObjCClass( + ConstString class_name, bool must_be_implementation, + llvm::function_ref<bool(DWARFDIE die)> callback) override; + + /// Uses DWARF5's IDX_parent fields, when available, to speed up this query. + void GetFullyQualifiedType( + const DWARFDeclContext &context, + llvm::function_ref<bool(DWARFDIE die)> callback) override; + void GetTypes(ConstString name, + llvm::function_ref<bool(DWARFDIE die)> callback) override; + void GetTypes(const DWARFDeclContext &context, + llvm::function_ref<bool(DWARFDIE die)> callback) override; + void GetNamespaces(ConstString name, + llvm::function_ref<bool(DWARFDIE die)> callback) override; + void GetFunctions(const Module::LookupInfo &lookup_info, + SymbolFileDWARF &dwarf, + const CompilerDeclContext &parent_decl_ctx, + llvm::function_ref<bool(DWARFDIE die)> callback) override; + void GetFunctions(const RegularExpression ®ex, + llvm::function_ref<bool(DWARFDIE die)> callback) 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, + SymbolFileDWARF &dwarf) + : DWARFIndex(module), m_debug_info(dwarf.DebugInfo()), + 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, dwarf, GetUnits(*m_debug_names_up), + GetTypeUnitSignatures(*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; + + DWARFUnit *GetNonSkeletonUnit(const DebugNames::Entry &entry) const; + DWARFDIE GetDIE(const DebugNames::Entry &entry) const; + + /// Checks if an entry is a foreign TU and fetch the type unit. + /// + /// This function checks if the DebugNames::Entry refers to a foreign TU and + /// returns an optional with a value of the \a entry is a foreign type unit + /// entry. A valid pointer will be returned if this entry is from a .dwo file + /// or if it is from a .dwp file and it matches the type unit's originating + /// .dwo file by verifying that the DW_TAG_type_unit DIE has a DW_AT_dwo_name + /// that matches the DWO name from the originating skeleton compile unit. + /// + /// \param[in] entry + /// The accelerator table entry to check. + /// + /// \returns + /// A std::optional that has a value if this entry represents a foreign type + /// unit. If the pointer is valid, then we were able to find and match the + /// entry to the type unit in the .dwo or .dwp file. The returned value can + /// have a valid, yet contain NULL in the following cases: + /// - we were not able to load the .dwo file (missing or DWO ID mismatch) + /// - we were able to load the .dwp file, but the type units DWO name + /// doesn't match the originating skeleton compile unit's entry + /// Returns std::nullopt if this entry is not a foreign type unit entry. + std::optional<DWARFTypeUnit *> + GetForeignTypeUnit(const DebugNames::Entry &entry) const; + + bool ProcessEntry(const DebugNames::Entry &entry, + llvm::function_ref<bool(DWARFDIE die)> callback); + + /// Returns true if `parent_entries` have identical names to `parent_names`. + bool SameParentChain(llvm::ArrayRef<llvm::StringRef> parent_names, + llvm::ArrayRef<DebugNames::Entry> parent_entries) const; + + static void MaybeLogLookupError(llvm::Error error, + const DebugNames::NameIndex &ni, + llvm::StringRef name); + + static llvm::DenseSet<dw_offset_t> GetUnits(const DebugNames &debug_names); + static llvm::DenseSet<uint64_t> + GetTypeUnitSignatures(const DebugNames &debug_names); +}; + +} // namespace dwarf +} // namespace lldb_private::plugin + +#endif // LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_DEBUGNAMESDWARFINDEX_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..795355b57a06 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/LogChannelDWARF.cpp @@ -0,0 +1,38 @@ +//===-- LogChannelDWARF.cpp -----------------------------------------------===// +// +// 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 struct/union/class type completions"}, + DWARFLog::TypeCompletion}, + {{"info"}, {"log the parsing of .debug_info"}, DWARFLog::DebugInfo}, + {{"line"}, {"log the parsing of .debug_line"}, DWARFLog::DebugLine}, + {{"lookups"}, + {"log any lookups that happen by name, regex, or address"}, + DWARFLog::Lookups}, + {{"map"}, + {"log insertions of object files into DWARF debug maps"}, + DWARFLog::DebugMap}, + {{"split"}, {"log split DWARF related activities"}, DWARFLog::SplitDwarf}, +}; + +static Log::Channel g_channel(g_categories, DWARFLog::DebugInfo); + +template <> Log::Channel &lldb_private::LogChannelFor<DWARFLog>() { + return g_channel; +} + +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..7f254a1162bd --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/LogChannelDWARF.h @@ -0,0 +1,37 @@ +//===-- 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 LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_LOGCHANNELDWARF_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_LOGCHANNELDWARF_H + +#include "lldb/Utility/Log.h" +#include "llvm/ADT/BitmaskEnum.h" + +namespace lldb_private { + +enum class DWARFLog : Log::MaskType { + DebugInfo = Log::ChannelFlag<0>, + DebugLine = Log::ChannelFlag<1>, + DebugMap = Log::ChannelFlag<2>, + Lookups = Log::ChannelFlag<3>, + TypeCompletion = Log::ChannelFlag<4>, + SplitDwarf = Log::ChannelFlag<5>, + LLVM_MARK_AS_BITMASK_ENUM(TypeCompletion) +}; +LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE(); + +class LogChannelDWARF { +public: + static void Initialize(); + static void Terminate(); +}; + +template <> Log::Channel &LogChannelFor<DWARFLog>(); +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_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..d581d3773ab2 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp @@ -0,0 +1,740 @@ +//===-- ManualDWARFIndex.cpp ----------------------------------------------===// +// +// 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/DataFileCache.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/Progress.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Utility/DataEncoder.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/Stream.h" +#include "lldb/Utility/Timer.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/ThreadPool.h" +#include <optional> + +using namespace lldb_private; +using namespace lldb; +using namespace lldb_private::dwarf; +using namespace lldb_private::plugin::dwarf; + +void ManualDWARFIndex::Index() { + if (m_indexed) + return; + m_indexed = true; + + ElapsedTime elapsed(m_index_time); + LLDB_SCOPED_TIMERF("%p", static_cast<void *>(m_dwarf)); + if (LoadFromCache()) { + m_dwarf->SetDebugInfoIndexWasLoadedFromCache(); + return; + } + + DWARFDebugInfo &main_info = m_dwarf->DebugInfo(); + SymbolFileDWARFDwo *dwp_dwarf = m_dwarf->GetDwpSymbolFile().get(); + DWARFDebugInfo *dwp_info = dwp_dwarf ? &dwp_dwarf->DebugInfo() : nullptr; + + std::vector<DWARFUnit *> units_to_index; + units_to_index.reserve(main_info.GetNumUnits() + + (dwp_info ? dwp_info->GetNumUnits() : 0)); + + // Process all units in the main file, as well as any type units in the dwp + // file. Type units in dwo files are handled when we reach the dwo file in + // IndexUnit. + for (size_t U = 0; U < main_info.GetNumUnits(); ++U) { + DWARFUnit *unit = main_info.GetUnitAtIndex(U); + if (unit && m_units_to_avoid.count(unit->GetOffset()) == 0) + units_to_index.push_back(unit); + } + if (dwp_info && dwp_info->ContainsTypeUnits()) { + for (size_t U = 0; U < dwp_info->GetNumUnits(); ++U) { + if (auto *tu = + llvm::dyn_cast<DWARFTypeUnit>(dwp_info->GetUnitAtIndex(U))) { + if (!m_type_sigs_to_avoid.contains(tu->GetTypeHash())) + units_to_index.push_back(tu); + } + } + } + + if (units_to_index.empty()) + return; + + StreamString module_desc; + m_module.GetDescription(module_desc.AsRawOstream(), + lldb::eDescriptionLevelBrief); + + // Include 2 passes per unit to index for extracting DIEs from the unit and + // indexing the unit, and then 8 extra entries for finalizing each index set. + const uint64_t total_progress = units_to_index.size() * 2 + 8; + Progress progress("Manually indexing DWARF", module_desc.GetData(), + total_progress); + + 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<std::optional<DWARFUnit::ScopedExtractDIEs>> clear_cu_dies( + units_to_index.size()); + auto parser_fn = [&](size_t cu_idx) { + IndexUnit(*units_to_index[cu_idx], dwp_dwarf, sets[cu_idx]); + progress.Increment(); + }; + + auto extract_fn = [&](size_t cu_idx) { + clear_cu_dies[cu_idx] = units_to_index[cu_idx]->ExtractDIEsScoped(); + progress.Increment(); + }; + + // Share one thread pool across operations to avoid the overhead of + // recreating the threads. + llvm::ThreadPoolTaskGroup task_group(Debugger::GetThreadPool()); + + // 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. + for (size_t i = 0; i < units_to_index.size(); ++i) + task_group.async(extract_fn, i); + task_group.wait(); + + // Now create a task runner that can index each DWARF unit in a + // separate thread so we can index quickly. + for (size_t i = 0; i < units_to_index.size(); ++i) + task_group.async(parser_fn, i); + task_group.wait(); + + auto finalize_fn = [this, &sets, &progress](NameToDIE(IndexSet::*index)) { + NameToDIE &result = m_set.*index; + for (auto &set : sets) + result.Append(set.*index); + result.Finalize(); + progress.Increment(); + }; + + task_group.async(finalize_fn, &IndexSet::function_basenames); + task_group.async(finalize_fn, &IndexSet::function_fullnames); + task_group.async(finalize_fn, &IndexSet::function_methods); + task_group.async(finalize_fn, &IndexSet::function_selectors); + task_group.async(finalize_fn, &IndexSet::objc_class_selectors); + task_group.async(finalize_fn, &IndexSet::globals); + task_group.async(finalize_fn, &IndexSet::types); + task_group.async(finalize_fn, &IndexSet::namespaces); + task_group.wait(); + + SaveToCache(); +} + +void ManualDWARFIndex::IndexUnit(DWARFUnit &unit, SymbolFileDWARFDwo *dwp, + IndexSet &set) { + Log *log = GetLog(DWARFLog::Lookups); + + if (log) { + m_module.LogMessage( + log, "ManualDWARFIndex::IndexUnit for unit at .debug_info[{0:x16}]", + unit.GetOffset()); + } + + const LanguageType cu_language = SymbolFileDWARF::GetLanguage(unit); + + // First check if the unit has a DWO ID. If it does then we only want to index + // the .dwo file or nothing at all. If we have a compile unit where we can't + // locate the .dwo/.dwp file we don't want to index anything from the skeleton + // compile unit because it is usally has no children unless + // -fsplit-dwarf-inlining was used at compile time. This option will add a + // copy of all DW_TAG_subprogram and any contained DW_TAG_inline_subroutine + // DIEs so that symbolication will still work in the absence of the .dwo/.dwp + // file, but the functions have no return types and all arguments and locals + // have been removed. So we don't want to index any of these hacked up + // function types. Types can still exist in the skeleton compile unit DWARF + // though as some functions have template parameter types and other things + // that cause extra copies of types to be included, but we should find these + // types in the .dwo file only as methods could have return types removed and + // we don't have to index incomplete types from the skeleton compile unit. + if (unit.GetDWOId()) { + // Index the .dwo or dwp instead of the skeleton unit. + if (SymbolFileDWARFDwo *dwo_symbol_file = unit.GetDwoSymbolFile()) { + // Type units in a dwp file are indexed separately, so we just need to + // process the split unit here. However, if the split unit is in a dwo + // file, then we need to process type units here. + if (dwo_symbol_file == dwp) { + IndexUnitImpl(unit.GetNonSkeletonUnit(), cu_language, set); + } else { + 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); + } + return; + } + // This was a DWARF5 skeleton CU and the .dwo file couldn't be located. + if (unit.GetVersion() >= 5 && unit.IsSkeletonUnit()) + return; + + // Either this is a DWARF 4 + fission CU with the .dwo file + // missing, or it's a -gmodules pch or pcm. Try to detect the + // latter by checking whether the first DIE is a DW_TAG_module. + // If it's a pch/pcm, continue indexing it. + if (unit.GetDIE(unit.GetFirstDIEOffset()).GetFirstChild().Tag() != + llvm::dwarf::DW_TAG_module) + return; + } + // We have a normal compile unit which we want to index. + IndexUnitImpl(unit, 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_imported_declaration: + 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; + } + + const char *name = nullptr; + const char *mangled_cstr = nullptr; + bool is_declaration = false; + bool has_address = false; + bool has_location_or_const_value = false; + bool is_global_or_static_variable = false; + + DWARFFormValue specification_die_form; + DWARFAttributes attributes = die.GetAttributes(&unit); + for (size_t i = 0; i < attributes.Size(); ++i) { + dw_attr_t attr = attributes.AttributeAtIndex(i); + DWARFFormValue form_value; + switch (attr) { + default: + break; + 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_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; + is_global_or_static_variable = die.IsGlobalOrStaticScopeVariable(); + + 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) { + std::optional<const ObjCLanguage::MethodName> objc_method = + ObjCLanguage::MethodName::Create(name, true); + if (objc_method) { + 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().c_str()); + 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: + case DW_TAG_imported_declaration: + 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, llvm::function_ref<bool(DWARFDIE die)> callback) { + Index(); + m_set.globals.Find(basename, + DIERefCallback(callback, basename.GetStringRef())); +} + +void ManualDWARFIndex::GetGlobalVariables( + const RegularExpression ®ex, + llvm::function_ref<bool(DWARFDIE die)> callback) { + Index(); + m_set.globals.Find(regex, DIERefCallback(callback, regex.GetText())); +} + +void ManualDWARFIndex::GetGlobalVariables( + DWARFUnit &unit, llvm::function_ref<bool(DWARFDIE die)> callback) { + Index(); + m_set.globals.FindAllEntriesForUnit(unit, DIERefCallback(callback)); +} + +void ManualDWARFIndex::GetObjCMethods( + ConstString class_name, llvm::function_ref<bool(DWARFDIE die)> callback) { + Index(); + m_set.objc_class_selectors.Find( + class_name, DIERefCallback(callback, class_name.GetStringRef())); +} + +void ManualDWARFIndex::GetCompleteObjCClass( + ConstString class_name, bool must_be_implementation, + llvm::function_ref<bool(DWARFDIE die)> callback) { + Index(); + m_set.types.Find(class_name, + DIERefCallback(callback, class_name.GetStringRef())); +} + +void ManualDWARFIndex::GetTypes( + ConstString name, llvm::function_ref<bool(DWARFDIE die)> callback) { + Index(); + m_set.types.Find(name, DIERefCallback(callback, name.GetStringRef())); +} + +void ManualDWARFIndex::GetTypes( + const DWARFDeclContext &context, + llvm::function_ref<bool(DWARFDIE die)> callback) { + Index(); + auto name = context[0].name; + m_set.types.Find(ConstString(name), + DIERefCallback(callback, llvm::StringRef(name))); +} + +void ManualDWARFIndex::GetNamespaces( + ConstString name, llvm::function_ref<bool(DWARFDIE die)> callback) { + Index(); + m_set.namespaces.Find(name, DIERefCallback(callback, name.GetStringRef())); +} + +void ManualDWARFIndex::GetFunctions( + const Module::LookupInfo &lookup_info, SymbolFileDWARF &dwarf, + const CompilerDeclContext &parent_decl_ctx, + llvm::function_ref<bool(DWARFDIE die)> callback) { + Index(); + ConstString name = lookup_info.GetLookupName(); + FunctionNameType name_type_mask = lookup_info.GetNameTypeMask(); + + if (name_type_mask & eFunctionNameTypeFull) { + if (!m_set.function_fullnames.Find( + name, DIERefCallback( + [&](DWARFDIE die) { + if (!SymbolFileDWARF::DIEInDeclContext(parent_decl_ctx, + die)) + return true; + return callback(die); + }, + name.GetStringRef()))) + return; + } + if (name_type_mask & eFunctionNameTypeBase) { + if (!m_set.function_basenames.Find( + name, DIERefCallback( + [&](DWARFDIE die) { + if (!SymbolFileDWARF::DIEInDeclContext(parent_decl_ctx, + die)) + return true; + return callback(die); + }, + name.GetStringRef()))) + return; + } + + if (name_type_mask & eFunctionNameTypeMethod && !parent_decl_ctx.IsValid()) { + if (!m_set.function_methods.Find( + name, DIERefCallback(callback, name.GetStringRef()))) + return; + } + + if (name_type_mask & eFunctionNameTypeSelector && + !parent_decl_ctx.IsValid()) { + if (!m_set.function_selectors.Find( + name, DIERefCallback(callback, name.GetStringRef()))) + return; + } +} + +void ManualDWARFIndex::GetFunctions( + const RegularExpression ®ex, + llvm::function_ref<bool(DWARFDIE die)> callback) { + Index(); + + if (!m_set.function_basenames.Find(regex, + DIERefCallback(callback, regex.GetText()))) + return; + if (!m_set.function_fullnames.Find(regex, + DIERefCallback(callback, regex.GetText()))) + return; +} + +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); +} + +constexpr llvm::StringLiteral kIdentifierManualDWARFIndex("DIDX"); +// Define IDs for the different tables when encoding and decoding the +// ManualDWARFIndex NameToDIE objects so we can avoid saving any empty maps. +enum DataID { + kDataIDFunctionBasenames = 1u, + kDataIDFunctionFullnames, + kDataIDFunctionMethods, + kDataIDFunctionSelectors, + kDataIDFunctionObjcClassSelectors, + kDataIDGlobals, + kDataIDTypes, + kDataIDNamespaces, + kDataIDEnd = 255u, + +}; + +// Version 2 changes the encoding of DIERef objects used in the DWARF manual +// index name tables. See DIERef class for details. +constexpr uint32_t CURRENT_CACHE_VERSION = 2; + +bool ManualDWARFIndex::IndexSet::Decode(const DataExtractor &data, + lldb::offset_t *offset_ptr) { + StringTableReader strtab; + // We now decode the string table for all strings in the data cache file. + if (!strtab.Decode(data, offset_ptr)) + return false; + + llvm::StringRef identifier((const char *)data.GetData(offset_ptr, 4), 4); + if (identifier != kIdentifierManualDWARFIndex) + return false; + const uint32_t version = data.GetU32(offset_ptr); + if (version != CURRENT_CACHE_VERSION) + return false; + + bool done = false; + while (!done) { + switch (data.GetU8(offset_ptr)) { + default: + // If we got here, this is not expected, we expect the data IDs to match + // one of the values from the DataID enumeration. + return false; + case kDataIDFunctionBasenames: + if (!function_basenames.Decode(data, offset_ptr, strtab)) + return false; + break; + case kDataIDFunctionFullnames: + if (!function_fullnames.Decode(data, offset_ptr, strtab)) + return false; + break; + case kDataIDFunctionMethods: + if (!function_methods.Decode(data, offset_ptr, strtab)) + return false; + break; + case kDataIDFunctionSelectors: + if (!function_selectors.Decode(data, offset_ptr, strtab)) + return false; + break; + case kDataIDFunctionObjcClassSelectors: + if (!objc_class_selectors.Decode(data, offset_ptr, strtab)) + return false; + break; + case kDataIDGlobals: + if (!globals.Decode(data, offset_ptr, strtab)) + return false; + break; + case kDataIDTypes: + if (!types.Decode(data, offset_ptr, strtab)) + return false; + break; + case kDataIDNamespaces: + if (!namespaces.Decode(data, offset_ptr, strtab)) + return false; + break; + case kDataIDEnd: + // We got to the end of our NameToDIE encodings. + done = true; + break; + } + } + // Success! + return true; +} + +void ManualDWARFIndex::IndexSet::Encode(DataEncoder &encoder) const { + ConstStringTable strtab; + + // Encoder the DWARF index into a separate encoder first. This allows us + // gather all of the strings we willl need in "strtab" as we will need to + // write the string table out before the symbol table. + DataEncoder index_encoder(encoder.GetByteOrder(), + encoder.GetAddressByteSize()); + + index_encoder.AppendData(kIdentifierManualDWARFIndex); + // Encode the data version. + index_encoder.AppendU32(CURRENT_CACHE_VERSION); + + if (!function_basenames.IsEmpty()) { + index_encoder.AppendU8(kDataIDFunctionBasenames); + function_basenames.Encode(index_encoder, strtab); + } + if (!function_fullnames.IsEmpty()) { + index_encoder.AppendU8(kDataIDFunctionFullnames); + function_fullnames.Encode(index_encoder, strtab); + } + if (!function_methods.IsEmpty()) { + index_encoder.AppendU8(kDataIDFunctionMethods); + function_methods.Encode(index_encoder, strtab); + } + if (!function_selectors.IsEmpty()) { + index_encoder.AppendU8(kDataIDFunctionSelectors); + function_selectors.Encode(index_encoder, strtab); + } + if (!objc_class_selectors.IsEmpty()) { + index_encoder.AppendU8(kDataIDFunctionObjcClassSelectors); + objc_class_selectors.Encode(index_encoder, strtab); + } + if (!globals.IsEmpty()) { + index_encoder.AppendU8(kDataIDGlobals); + globals.Encode(index_encoder, strtab); + } + if (!types.IsEmpty()) { + index_encoder.AppendU8(kDataIDTypes); + types.Encode(index_encoder, strtab); + } + if (!namespaces.IsEmpty()) { + index_encoder.AppendU8(kDataIDNamespaces); + namespaces.Encode(index_encoder, strtab); + } + index_encoder.AppendU8(kDataIDEnd); + + // Now that all strings have been gathered, we will emit the string table. + strtab.Encode(encoder); + // Followed by the symbol table data. + encoder.AppendData(index_encoder.GetData()); +} + +bool ManualDWARFIndex::Decode(const DataExtractor &data, + lldb::offset_t *offset_ptr, + bool &signature_mismatch) { + signature_mismatch = false; + CacheSignature signature; + if (!signature.Decode(data, offset_ptr)) + return false; + if (CacheSignature(m_dwarf->GetObjectFile()) != signature) { + signature_mismatch = true; + return false; + } + IndexSet set; + if (!set.Decode(data, offset_ptr)) + return false; + m_set = std::move(set); + return true; +} + +bool ManualDWARFIndex::Encode(DataEncoder &encoder) const { + CacheSignature signature(m_dwarf->GetObjectFile()); + if (!signature.Encode(encoder)) + return false; + m_set.Encode(encoder); + return true; +} + +std::string ManualDWARFIndex::GetCacheKey() { + std::string key; + llvm::raw_string_ostream strm(key); + // DWARF Index can come from different object files for the same module. A + // module can have one object file as the main executable and might have + // another object file in a separate symbol file, or we might have a .dwo file + // that claims its module is the main executable. + ObjectFile *objfile = m_dwarf->GetObjectFile(); + strm << objfile->GetModule()->GetCacheKey() << "-dwarf-index-" + << llvm::format_hex(objfile->GetCacheHash(), 10); + return strm.str(); +} + +bool ManualDWARFIndex::LoadFromCache() { + DataFileCache *cache = Module::GetIndexCache(); + if (!cache) + return false; + ObjectFile *objfile = m_dwarf->GetObjectFile(); + if (!objfile) + return false; + std::unique_ptr<llvm::MemoryBuffer> mem_buffer_up = + cache->GetCachedData(GetCacheKey()); + if (!mem_buffer_up) + return false; + DataExtractor data(mem_buffer_up->getBufferStart(), + mem_buffer_up->getBufferSize(), + endian::InlHostByteOrder(), + objfile->GetAddressByteSize()); + bool signature_mismatch = false; + lldb::offset_t offset = 0; + const bool result = Decode(data, &offset, signature_mismatch); + if (signature_mismatch) + cache->RemoveCacheFile(GetCacheKey()); + return result; +} + +void ManualDWARFIndex::SaveToCache() { + DataFileCache *cache = Module::GetIndexCache(); + if (!cache) + return; // Caching is not enabled. + ObjectFile *objfile = m_dwarf->GetObjectFile(); + if (!objfile) + return; + DataEncoder file(endian::InlHostByteOrder(), objfile->GetAddressByteSize()); + // Encode will return false if the object file doesn't have anything to make + // a signature from. + if (Encode(file)) { + if (cache->SetCachedData(GetCacheKey(), file.GetData())) + m_dwarf->SetDebugInfoIndexWasSavedToCache(); + } +} 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..d8c4a22ab21f --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.h @@ -0,0 +1,183 @@ +//===-- 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_SOURCE_PLUGINS_SYMBOLFILE_DWARF_MANUALDWARFINDEX_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_MANUALDWARFINDEX_H + +#include "Plugins/SymbolFile/DWARF/DWARFIndex.h" +#include "Plugins/SymbolFile/DWARF/NameToDIE.h" +#include "llvm/ADT/DenseSet.h" + +namespace lldb_private::plugin { +namespace dwarf { +class DWARFDebugInfo; +class SymbolFileDWARFDwo; + +class ManualDWARFIndex : public DWARFIndex { +public: + ManualDWARFIndex(Module &module, SymbolFileDWARF &dwarf, + llvm::DenseSet<dw_offset_t> units_to_avoid = {}, + llvm::DenseSet<uint64_t> type_sigs_to_avoid = {}) + : DWARFIndex(module), m_dwarf(&dwarf), + m_units_to_avoid(std::move(units_to_avoid)), + m_type_sigs_to_avoid(std::move(type_sigs_to_avoid)) {} + + void Preload() override { Index(); } + + void + GetGlobalVariables(ConstString basename, + llvm::function_ref<bool(DWARFDIE die)> callback) override; + void + GetGlobalVariables(const RegularExpression ®ex, + llvm::function_ref<bool(DWARFDIE die)> callback) override; + void + GetGlobalVariables(DWARFUnit &unit, + llvm::function_ref<bool(DWARFDIE die)> callback) override; + void GetObjCMethods(ConstString class_name, + llvm::function_ref<bool(DWARFDIE die)> callback) override; + void GetCompleteObjCClass( + ConstString class_name, bool must_be_implementation, + llvm::function_ref<bool(DWARFDIE die)> callback) override; + void GetTypes(ConstString name, + llvm::function_ref<bool(DWARFDIE die)> callback) override; + void GetTypes(const DWARFDeclContext &context, + llvm::function_ref<bool(DWARFDIE die)> callback) override; + void GetNamespaces(ConstString name, + llvm::function_ref<bool(DWARFDIE die)> callback) override; + void GetFunctions(const Module::LookupInfo &lookup_info, + SymbolFileDWARF &dwarf, + const CompilerDeclContext &parent_decl_ctx, + llvm::function_ref<bool(DWARFDIE die)> callback) override; + void GetFunctions(const RegularExpression ®ex, + llvm::function_ref<bool(DWARFDIE die)> callback) override; + + void Dump(Stream &s) override; + + // Make IndexSet public so we can unit test the encoding and decoding logic. + struct IndexSet { + NameToDIE function_basenames; + NameToDIE function_fullnames; + NameToDIE function_methods; + NameToDIE function_selectors; + NameToDIE objc_class_selectors; + NameToDIE globals; + NameToDIE types; + NameToDIE namespaces; + bool Decode(const DataExtractor &data, lldb::offset_t *offset_ptr); + void Encode(DataEncoder &encoder) const; + bool operator==(const IndexSet &rhs) const { + return function_basenames == rhs.function_basenames && + function_fullnames == rhs.function_fullnames && + function_methods == rhs.function_methods && + function_selectors == rhs.function_selectors && + objc_class_selectors == rhs.objc_class_selectors && + globals == rhs.globals && types == rhs.types && + namespaces == rhs.namespaces; + } + }; + +private: + void Index(); + + /// Decode a serialized version of this object from data. + /// + /// \param data + /// The decoder object that references the serialized data. + /// + /// \param offset_ptr + /// A pointer that contains the offset from which the data will be decoded + /// from that gets updated as data gets decoded. + /// + /// \param strtab + /// All strings in cache files are put into string tables for efficiency + /// and cache file size reduction. Strings are stored as uint32_t string + /// table offsets in the cache data. + bool Decode(const DataExtractor &data, lldb::offset_t *offset_ptr, + bool &signature_mismatch); + + /// Encode this object into a data encoder object. + /// + /// This allows this object to be serialized to disk. + /// + /// \param encoder + /// A data encoder object that serialized bytes will be encoded into. + /// + /// \param strtab + /// All strings in cache files are put into string tables for efficiency + /// and cache file size reduction. Strings are stored as uint32_t string + /// table offsets in the cache data. + /// + /// \return + /// True if the symbol table's object file can generate a valid signature + /// and all data for the symbol table was encoded, false otherwise. + bool Encode(DataEncoder &encoder) const; + + /// Get the cache key string for this symbol table. + /// + /// The cache key must start with the module's cache key and is followed + /// by information that indicates this key is for caching the symbol table + /// contents and should also include the has of the object file. A module can + /// be represented by an ObjectFile object for the main executable, but can + /// also have a symbol file that is from the same or a different object file. + /// This means we might have two symbol tables cached in the index cache, one + /// for the main executable and one for the symbol file. + /// + /// \return + /// The unique cache key used to save and retrieve data from the index + /// cache. + std::string GetCacheKey(); + + /// Save the symbol table data out into a cache. + /// + /// The symbol table will only be saved to a cache file if caching is enabled. + /// + /// We cache the contents of the symbol table since symbol tables in LLDB take + /// some time to initialize. This is due to the many sources for data that are + /// used to create a symbol table: + /// - standard symbol table + /// - dynamic symbol table (ELF) + /// - compressed debug info sections + /// - unwind information + /// - function pointers found in runtimes for global constructor/destructors + /// - other sources. + /// All of the above sources are combined and one symbol table results after + /// all sources have been considered. + void SaveToCache(); + + /// Load the symbol table from the index cache. + /// + /// Quickly load the finalized symbol table from the index cache. This saves + /// time when the debugger starts up. The index cache file for the symbol + /// table has the modification time set to the same time as the main module. + /// If the cache file exists and the modification times match, we will load + /// the symbol table from the serlized cache file. + /// + /// \return + /// True if the symbol table was successfully loaded from the index cache, + /// false if the symbol table wasn't cached or was out of date. + bool LoadFromCache(); + + void IndexUnit(DWARFUnit &unit, SymbolFileDWARFDwo *dwp, IndexSet &set); + + static void IndexUnitImpl(DWARFUnit &unit, + const lldb::LanguageType cu_language, + IndexSet &set); + + /// The DWARF file which we are indexing. + SymbolFileDWARF *m_dwarf; + /// Which dwarf units should we skip while building the index. + llvm::DenseSet<dw_offset_t> m_units_to_avoid; + llvm::DenseSet<uint64_t> m_type_sigs_to_avoid; + + IndexSet m_set; + bool m_indexed = false; +}; +} // namespace dwarf +} // namespace lldb_private::plugin + +#endif // LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_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..44d90648700c --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/NameToDIE.cpp @@ -0,0 +1,151 @@ +//===-- NameToDIE.cpp -----------------------------------------------------===// +// +// 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/Core/DataFileCache.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/Utility/DataEncoder.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/RegularExpression.h" +#include "lldb/Utility/Stream.h" +#include "lldb/Utility/StreamString.h" +#include <optional> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::plugin::dwarf; + +void NameToDIE::Finalize() { + m_map.Sort(std::less<DIERef>()); + m_map.SizeToFit(); +} + +void NameToDIE::Insert(ConstString name, const DIERef &die_ref) { + m_map.Append(name, die_ref); +} + +bool NameToDIE::Find(ConstString name, + llvm::function_ref<bool(DIERef ref)> callback) const { + for (const auto &entry : m_map.equal_range(name)) + if (!callback(entry.value)) + return false; + return true; +} + +bool NameToDIE::Find(const RegularExpression ®ex, + llvm::function_ref<bool(DIERef ref)> callback) const { + for (const auto &entry : m_map) + if (regex.Execute(entry.cstring.GetCString())) { + if (!callback(entry.value)) + return false; + } + return true; +} + +void NameToDIE::FindAllEntriesForUnit( + DWARFUnit &s_unit, llvm::function_ref<bool(DIERef ref)> callback) const { + const DWARFUnit &ns_unit = s_unit.GetNonSkeletonUnit(); + const uint32_t size = m_map.GetSize(); + for (uint32_t i = 0; i < size; ++i) { + const DIERef &die_ref = m_map.GetValueAtIndexUnchecked(i); + if (ns_unit.GetSymbolFileDWARF().GetFileIndex() == die_ref.file_index() && + ns_unit.GetDebugSection() == die_ref.section() && + ns_unit.GetOffset() <= die_ref.die_offset() && + die_ref.die_offset() < ns_unit.GetNextUnitOffset()) { + if (!callback(die_ref)) + return; + } + } +} + +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)); + } +} + +constexpr llvm::StringLiteral kIdentifierNameToDIE("N2DI"); + +bool NameToDIE::Decode(const DataExtractor &data, lldb::offset_t *offset_ptr, + const StringTableReader &strtab) { + m_map.Clear(); + llvm::StringRef identifier((const char *)data.GetData(offset_ptr, 4), 4); + if (identifier != kIdentifierNameToDIE) + return false; + const uint32_t count = data.GetU32(offset_ptr); + m_map.Reserve(count); + for (uint32_t i = 0; i < count; ++i) { + llvm::StringRef str(strtab.Get(data.GetU32(offset_ptr))); + // No empty strings allowed in the name to DIE maps. + if (str.empty()) + return false; + if (std::optional<DIERef> die_ref = DIERef::Decode(data, offset_ptr)) + m_map.Append(ConstString(str), *die_ref); + else + return false; + } + // We must sort the UniqueCStringMap after decoding it since it is a vector + // of UniqueCStringMap::Entry objects which contain a ConstString and type T. + // ConstString objects are sorted by "const char *" and then type T and + // the "const char *" are point values that will depend on the order in which + // ConstString objects are created and in which of the 256 string pools they + // are created in. So after we decode all of the entries, we must sort the + // name map to ensure name lookups succeed. If we encode and decode within + // the same process we wouldn't need to sort, so unit testing didn't catch + // this issue when first checked in. + m_map.Sort(std::less<DIERef>()); + return true; +} + +void NameToDIE::Encode(DataEncoder &encoder, ConstStringTable &strtab) const { + encoder.AppendData(kIdentifierNameToDIE); + encoder.AppendU32(m_map.GetSize()); + for (const auto &entry : m_map) { + // Make sure there are no empty strings. + assert((bool)entry.cstring); + encoder.AppendU32(strtab.Add(entry.cstring)); + entry.value.Encode(encoder); + } +} + +bool NameToDIE::operator==(const NameToDIE &rhs) const { + const size_t size = m_map.GetSize(); + if (size != rhs.m_map.GetSize()) + return false; + for (size_t i = 0; i < size; ++i) { + if (m_map.GetCStringAtIndex(i) != rhs.m_map.GetCStringAtIndex(i)) + return false; + if (m_map.GetValueRefAtIndexUnchecked(i) != + rhs.m_map.GetValueRefAtIndexUnchecked(i)) + return false; + } + return true; +} 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..90eac1fa3733 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/NameToDIE.h @@ -0,0 +1,94 @@ +//===-- 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 LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_NAMETODIE_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_NAMETODIE_H + +#include <functional> + +#include "DIERef.h" +#include "lldb/Core/UniqueCStringMap.h" +#include "lldb/Core/dwarf.h" +#include "lldb/lldb-defines.h" + +namespace lldb_private::plugin { +namespace dwarf { +class DWARFUnit; + +class NameToDIE { +public: + NameToDIE() : m_map() {} + + ~NameToDIE() = default; + + void Dump(Stream *s); + + void Insert(ConstString name, const DIERef &die_ref); + + void Append(const NameToDIE &other); + + void Finalize(); + + bool Find(ConstString name, + llvm::function_ref<bool(DIERef ref)> callback) const; + + bool Find(const RegularExpression ®ex, + llvm::function_ref<bool(DIERef ref)> callback) const; + + /// \a unit must be the skeleton unit if possible, not GetNonSkeletonUnit(). + void + FindAllEntriesForUnit(DWARFUnit &unit, + llvm::function_ref<bool(DIERef ref)> callback) const; + + void + ForEach(std::function<bool(ConstString name, const DIERef &die_ref)> const + &callback) const; + + /// Decode a serialized version of this object from data. + /// + /// \param data + /// The decoder object that references the serialized data. + /// + /// \param offset_ptr + /// A pointer that contains the offset from which the data will be decoded + /// from that gets updated as data gets decoded. + /// + /// \param strtab + /// All strings in cache files are put into string tables for efficiency + /// and cache file size reduction. Strings are stored as uint32_t string + /// table offsets in the cache data. + bool Decode(const DataExtractor &data, lldb::offset_t *offset_ptr, + const StringTableReader &strtab); + + /// Encode this object into a data encoder object. + /// + /// This allows this object to be serialized to disk. + /// + /// \param encoder + /// A data encoder object that serialized bytes will be encoded into. + /// + /// \param strtab + /// All strings in cache files are put into string tables for efficiency + /// and cache file size reduction. Strings are stored as uint32_t string + /// table offsets in the cache data. + void Encode(DataEncoder &encoder, ConstStringTable &strtab) const; + + /// Used for unit testing the encoding and decoding. + bool operator==(const NameToDIE &rhs) const; + + bool IsEmpty() const { return m_map.IsEmpty(); } + + void Clear() { m_map.Clear(); } + +protected: + UniqueCStringMap<DIERef> m_map; +}; +} // namespace dwarf +} // namespace lldb_private::plugin + +#endif // LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_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..7cd3a33c7de5 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp @@ -0,0 +1,4484 @@ +//===-- SymbolFileDWARF.cpp -----------------------------------------------===// +// +// 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/DebugInfo/DWARF/DWARFDebugLoc.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/FileUtilities.h" +#include "llvm/Support/Format.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/Progress.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/Value.h" +#include "lldb/Utility/ArchSpec.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/RegularExpression.h" +#include "lldb/Utility/Scalar.h" +#include "lldb/Utility/StreamString.h" +#include "lldb/Utility/StructuredData.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 "Plugins/ExpressionParser/Clang/ClangUtil.h" +#include "Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.h" +#include "Plugins/TypeSystem/Clang/TypeSystemClang.h" +#include "lldb/Symbol/Block.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/ObjectFile.h" +#include "lldb/Symbol/SymbolFile.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 "DWARFDebugAranges.h" +#include "DWARFDebugInfo.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 "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugAbbrev.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/FormatVariadic.h" + +#include <algorithm> +#include <map> +#include <memory> +#include <optional> + +#include <cctype> +#include <cstring> + +//#define ENABLE_DEBUG_PRINTF // COMMENT OUT THIS LINE PRIOR TO CHECKIN + +#ifdef ENABLE_DEBUG_PRINTF +#include <cstdio> +#define DEBUG_PRINTF(fmt, ...) printf(fmt, __VA_ARGS__) +#else +#define DEBUG_PRINTF(fmt, ...) +#endif + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::dwarf; +using namespace lldb_private::plugin::dwarf; + +LLDB_PLUGIN_DEFINE(SymbolFileDWARF) + +char SymbolFileDWARF::ID; + +namespace { + +#define LLDB_PROPERTIES_symbolfiledwarf +#include "SymbolFileDWARFProperties.inc" + +enum { +#define LLDB_PROPERTIES_symbolfiledwarf +#include "SymbolFileDWARFPropertiesEnum.inc" +}; + +class PluginProperties : public Properties { +public: + static llvm::StringRef GetSettingName() { + return SymbolFileDWARF::GetPluginNameStatic(); + } + + PluginProperties() { + m_collection_sp = std::make_shared<OptionValueProperties>(GetSettingName()); + m_collection_sp->Initialize(g_symbolfiledwarf_properties); + } + + bool IgnoreFileIndexes() const { + return GetPropertyAtIndexAs<bool>(ePropertyIgnoreIndexes, false); + } +}; + +} // namespace + +bool IsStructOrClassTag(llvm::dwarf::Tag Tag) { + return Tag == llvm::dwarf::Tag::DW_TAG_class_type || + Tag == llvm::dwarf::Tag::DW_TAG_structure_type; +} + +static PluginProperties &GetGlobalPluginProperties() { + static PluginProperties g_settings; + return g_settings; +} + +static const llvm::DWARFDebugLine::LineTable * +ParseLLVMLineTable(DWARFContext &context, llvm::DWARFDebugLine &line, + dw_offset_t line_offset, dw_offset_t unit_offset) { + Log *log = GetLog(DWARFLog::DebugInfo); + + llvm::DWARFDataExtractor data = context.getOrLoadLineData().GetAsLLVMDWARF(); + llvm::DWARFContext &ctx = context.GetAsLLVM(); + llvm::Expected<const llvm::DWARFDebugLine::LineTable *> line_table = + line.getOrParseLineTable( + data, line_offset, ctx, nullptr, [&](llvm::Error e) { + LLDB_LOG_ERROR( + log, std::move(e), + "SymbolFileDWARF::ParseLineTable failed to parse: {0}"); + }); + + if (!line_table) { + LLDB_LOG_ERROR(log, line_table.takeError(), + "SymbolFileDWARF::ParseLineTable failed to parse: {0}"); + return nullptr; + } + return *line_table; +} + +static bool ParseLLVMLineTablePrologue(DWARFContext &context, + llvm::DWARFDebugLine::Prologue &prologue, + dw_offset_t line_offset, + dw_offset_t unit_offset) { + Log *log = GetLog(DWARFLog::DebugInfo); + bool success = true; + llvm::DWARFDataExtractor data = context.getOrLoadLineData().GetAsLLVMDWARF(); + llvm::DWARFContext &ctx = context.GetAsLLVM(); + uint64_t offset = line_offset; + llvm::Error error = prologue.parse( + data, &offset, + [&](llvm::Error e) { + success = false; + LLDB_LOG_ERROR(log, std::move(e), + "SymbolFileDWARF::ParseSupportFiles failed to parse " + "line table prologue: {0}"); + }, + ctx, nullptr); + if (error) { + LLDB_LOG_ERROR(log, std::move(error), + "SymbolFileDWARF::ParseSupportFiles failed to parse line " + "table prologue: {0}"); + return false; + } + return success; +} + +static std::optional<std::string> +GetFileByIndex(const llvm::DWARFDebugLine::Prologue &prologue, size_t idx, + llvm::StringRef compile_dir, FileSpec::Style style) { + // Try to get an absolute path first. + std::string abs_path; + auto absolute = llvm::DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath; + if (prologue.getFileNameByIndex(idx, compile_dir, absolute, abs_path, style)) + return std::move(abs_path); + + // Otherwise ask for a relative path. + std::string rel_path; + auto relative = llvm::DILineInfoSpecifier::FileLineInfoKind::RawValue; + if (!prologue.getFileNameByIndex(idx, compile_dir, relative, rel_path, style)) + return {}; + return std::move(rel_path); +} + +static void ParseSupportFilesFromPrologue( + SupportFileList &support_files, const lldb::ModuleSP &module, + const llvm::DWARFDebugLine::Prologue &prologue, FileSpec::Style style, + llvm::StringRef compile_dir = {}) { + // Handle the case where there are no files first to avoid having to special + // case this later. + if (prologue.FileNames.empty()) + return; + + // Before DWARF v5, the line table indexes were one based. + const bool is_one_based = prologue.getVersion() < 5; + const size_t file_names = prologue.FileNames.size(); + const size_t first_file_idx = is_one_based ? 1 : 0; + const size_t last_file_idx = is_one_based ? file_names : file_names - 1; + + // Add a dummy entry to ensure the support file list indices match those we + // get from the debug info and line tables. + if (is_one_based) + support_files.Append(FileSpec()); + + for (size_t idx = first_file_idx; idx <= last_file_idx; ++idx) { + std::string remapped_file; + if (auto file_path = GetFileByIndex(prologue, idx, compile_dir, style)) { + auto entry = prologue.getFileNameEntry(idx); + auto source = entry.Source.getAsCString(); + if (!source) + consumeError(source.takeError()); + else { + llvm::StringRef source_ref(*source); + if (!source_ref.empty()) { + /// Wrap a path for an in-DWARF source file. Lazily write it + /// to disk when Materialize() is called. + struct LazyDWARFSourceFile : public SupportFile { + LazyDWARFSourceFile(const FileSpec &fs, llvm::StringRef source, + FileSpec::Style style) + : SupportFile(fs), source(source), style(style) {} + FileSpec tmp_file; + /// The file contents buffer. + llvm::StringRef source; + /// Deletes the temporary file at the end. + std::unique_ptr<llvm::FileRemover> remover; + FileSpec::Style style; + + /// Write the file contents to a temporary file. + const FileSpec &Materialize() override { + if (tmp_file) + return tmp_file; + llvm::SmallString<0> name; + int fd; + auto orig_name = m_file_spec.GetFilename().GetStringRef(); + auto ec = llvm::sys::fs::createTemporaryFile( + "", llvm::sys::path::filename(orig_name, style), fd, name); + if (ec || fd <= 0) { + LLDB_LOG(GetLog(DWARFLog::DebugInfo), + "Could not create temporary file"); + return tmp_file; + } + remover = std::make_unique<llvm::FileRemover>(name); + NativeFile file(fd, File::eOpenOptionWriteOnly, true); + size_t num_bytes = source.size(); + file.Write(source.data(), num_bytes); + tmp_file.SetPath(name); + return tmp_file; + } + }; + support_files.Append(std::make_unique<LazyDWARFSourceFile>( + FileSpec(*file_path), *source, style)); + continue; + } + } + if (auto remapped = module->RemapSourceFile(llvm::StringRef(*file_path))) + remapped_file = *remapped; + else + remapped_file = std::move(*file_path); + } + + Checksum checksum; + if (prologue.ContentTypes.HasMD5) { + const llvm::DWARFDebugLine::FileNameEntry &file_name_entry = + prologue.getFileNameEntry(idx); + checksum = file_name_entry.Checksum; + } + + // Unconditionally add an entry, so the indices match up. + support_files.EmplaceBack(FileSpec(remapped_file, style), checksum); + } +} + +void SymbolFileDWARF::Initialize() { + LogChannelDWARF::Initialize(); + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), CreateInstance, + DebuggerInitialize); + SymbolFileDWARFDebugMap::Initialize(); +} + +void SymbolFileDWARF::DebuggerInitialize(Debugger &debugger) { + if (!PluginManager::GetSettingForSymbolFilePlugin( + debugger, PluginProperties::GetSettingName())) { + const bool is_global_setting = true; + PluginManager::CreateSettingForSymbolFilePlugin( + debugger, GetGlobalPluginProperties().GetValueProperties(), + "Properties for the dwarf symbol-file plug-in.", is_global_setting); + } +} + +void SymbolFileDWARF::Terminate() { + SymbolFileDWARFDebugMap::Terminate(); + PluginManager::UnregisterPlugin(CreateInstance); + LogChannelDWARF::Terminate(); +} + +llvm::StringRef SymbolFileDWARF::GetPluginDescriptionStatic() { + return "DWARF and DWARF3 debug symbol file reader."; +} + +SymbolFile *SymbolFileDWARF::CreateInstance(ObjectFileSP objfile_sp) { + return new SymbolFileDWARF(std::move(objfile_sp), + /*dwo_section_list*/ nullptr); +} + +TypeList &SymbolFileDWARF::GetTypeList() { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + if (SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile()) + return debug_map_symfile->GetTypeList(); + return SymbolFileCommon::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; + default: + break; + } + + if (add_type) { + const bool assert_not_being_parsed = true; + Type *type = ResolveTypeUID(die, assert_not_being_parsed); + if (type) + type_set.insert(type); + } + } + + for (DWARFDIE child_die : die.children()) { + GetTypes(child_die, min_die_offset, max_die_offset, type_mask, type_set); + } + } +} + +void SymbolFileDWARF::GetTypes(SymbolContextScope *sc_scope, + TypeClass type_mask, TypeList &type_list) + +{ + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + TypeSet type_set; + + CompileUnit *comp_unit = nullptr; + if (sc_scope) + comp_unit = sc_scope->CalculateSymbolContextCompileUnit(); + + const auto &get = [&](DWARFUnit *unit) { + if (!unit) + return; + unit = &unit->GetNonSkeletonUnit(); + GetTypes(unit->DIE(), unit->GetOffset(), unit->GetNextUnitOffset(), + type_mask, type_set); + }; + if (comp_unit) { + get(GetDWARFCompileUnit(comp_unit)); + } else { + DWARFDebugInfo &info = DebugInfo(); + const size_t num_cus = info.GetNumUnits(); + for (size_t cu_idx = 0; cu_idx < num_cus; ++cu_idx) + get(info.GetUnitAtIndex(cu_idx)); + } + + std::set<CompilerType> compiler_type_set; + 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()); + } + } +} + +// 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; + default: + break; + } + } + return DWARFDIE(); +} + +SymbolFileDWARF::SymbolFileDWARF(ObjectFileSP objfile_sp, + SectionList *dwo_section_list) + : SymbolFileCommon(std::move(objfile_sp)), m_debug_map_module_wp(), + m_debug_map_symfile(nullptr), + m_context(m_objfile_sp->GetModule()->GetSectionList(), dwo_section_list), + m_fetched_external_modules(false), + m_supports_DW_AT_APPLE_objc_complete_type(eLazyBoolCalculate) {} + +SymbolFileDWARF::~SymbolFileDWARF() = default; + +static ConstString GetDWARFMachOSegmentName() { + static ConstString g_dwarf_section_name("__DWARF"); + return g_dwarf_section_name; +} + +llvm::DenseMap<lldb::opaque_compiler_type_t, DIERef> & +SymbolFileDWARF::GetForwardDeclCompilerTypeToDIE() { + if (SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile()) + return debug_map_symfile->GetForwardDeclCompilerTypeToDIE(); + return m_forward_decl_compiler_type_to_die; +} + +UniqueDWARFASTTypeMap &SymbolFileDWARF::GetUniqueDWARFASTTypeMap() { + SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile(); + if (debug_map_symfile) + return debug_map_symfile->GetUniqueDWARFASTTypeMap(); + else + return m_unique_ast_type_map; +} + +llvm::Expected<lldb::TypeSystemSP> +SymbolFileDWARF::GetTypeSystemForLanguage(LanguageType language) { + if (SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile()) + return debug_map_symfile->GetTypeSystemForLanguage(language); + + auto type_system_or_err = + m_objfile_sp->GetModule()->GetTypeSystemForLanguage(language); + if (type_system_or_err) + if (auto ts = *type_system_or_err) + ts->SetSymbolFile(this); + return type_system_or_err; +} + +void SymbolFileDWARF::InitializeObject() { + Log *log = GetLog(DWARFLog::DebugInfo); + + InitializeFirstCodeAddress(); + + if (!GetGlobalPluginProperties().IgnoreFileIndexes()) { + StreamString module_desc; + GetObjectFile()->GetModule()->GetDescription(module_desc.AsRawOstream(), + lldb::eDescriptionLevelBrief); + 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); + + if (apple_names.GetByteSize() > 0 || apple_namespaces.GetByteSize() > 0 || + apple_types.GetByteSize() > 0 || apple_objc.GetByteSize() > 0) { + 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) { + Progress progress("Loading DWARF5 index", module_desc.GetData()); + llvm::Expected<std::unique_ptr<DebugNamesDWARFIndex>> index_or = + DebugNamesDWARFIndex::Create(*GetObjectFile()->GetModule(), + debug_names, + m_context.getOrLoadStrData(), *this); + 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 = + std::make_unique<ManualDWARFIndex>(*GetObjectFile()->GetModule(), *this); +} + +void SymbolFileDWARF::InitializeFirstCodeAddress() { + InitializeFirstCodeAddressRecursive( + *m_objfile_sp->GetModule()->GetSectionList()); + if (m_first_code_address == LLDB_INVALID_ADDRESS) + m_first_code_address = 0; +} + +void SymbolFileDWARF::InitializeFirstCodeAddressRecursive( + const lldb_private::SectionList §ion_list) { + for (SectionSP section_sp : section_list) { + if (section_sp->GetChildren().GetSize() > 0) { + InitializeFirstCodeAddressRecursive(section_sp->GetChildren()); + } else if (section_sp->GetType() == eSectionTypeCode) { + m_first_code_address = + std::min(m_first_code_address, section_sp->GetFileAddress()); + } + } +} + +bool SymbolFileDWARF::SupportedVersion(uint16_t version) { + return version >= 2 && version <= 5; +} + +static std::set<dw_form_t> +GetUnsupportedForms(llvm::DWARFDebugAbbrev *debug_abbrev) { + if (!debug_abbrev) + return {}; + + std::set<dw_form_t> unsupported_forms; + for (const auto &[_, decl_set] : *debug_abbrev) + for (const auto &decl : decl_set) + for (const auto &attr : decl.attributes()) + if (!DWARFFormValue::FormIsSupported(attr.Form)) + unsupported_forms.insert(attr.Form); + + return unsupported_forms; +} + +uint32_t SymbolFileDWARF::CalculateAbilities() { + uint32_t abilities = 0; + if (m_objfile_sp != nullptr) { + const Section *section = nullptr; + const SectionList *section_list = m_objfile_sp->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(); + + llvm::DWARFDebugAbbrev *abbrev = DebugAbbrev(); + std::set<dw_form_t> unsupported_forms = GetUnsupportedForms(abbrev); + if (!unsupported_forms.empty()) { + StreamString error; + error.Printf("unsupported DW_FORM value%s:", + unsupported_forms.size() > 1 ? "s" : ""); + for (auto form : unsupported_forms) + error.Printf(" %#x", form); + m_objfile_sp->GetModule()->ReportWarning("{0}", error.GetString()); + return 0; + } + + section = + section_list->FindSectionByType(eSectionTypeDWARFDebugLine, true) + .get(); + if (section) + debug_line_file_size = section->GetFileSize(); + } else { + llvm::StringRef symfile_dir = + m_objfile_sp->GetFileSpec().GetDirectory().GetStringRef(); + if (symfile_dir.contains_insensitive(".dsym")) { + if (m_objfile_sp->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_objfile_sp->GetModule()->ReportWarning( + "empty dSYM file detected, dSYM was created with an " + "executable with no debug info."); + } + } + } + } + + constexpr uint64_t MaxDebugInfoSize = (1ull) << DW_DIE_OFFSET_MAX_BITSIZE; + if (debug_info_file_size >= MaxDebugInfoSize) { + m_objfile_sp->GetModule()->ReportWarning( + "SymbolFileDWARF can't load this DWARF. It's larger then {0:x+16}", + MaxDebugInfoSize); + return 0; + } + + 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; +} + +void SymbolFileDWARF::LoadSectionData(lldb::SectionType sect_type, + DWARFDataExtractor &data) { + ModuleSP module_sp(m_objfile_sp->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_objfile_sp->ReadSectionData(section_sp.get(), data); +} + +llvm::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; + + ElapsedTime elapsed(m_parse_time); + auto abbr = + std::make_unique<llvm::DWARFDebugAbbrev>(debug_abbrev_data.GetAsLLVM()); + llvm::Error error = abbr->parse(); + if (error) { + Log *log = GetLog(DWARFLog::DebugInfo); + 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(); +} + +DWARFDebugInfo &SymbolFileDWARF::DebugInfo() { + llvm::call_once(m_info_once_flag, [&] { + LLDB_SCOPED_TIMER(); + + m_info = std::make_unique<DWARFDebugInfo>(*this, m_context); + }); + return *m_info; +} + +DWARFCompileUnit *SymbolFileDWARF::GetDWARFCompileUnit(CompileUnit *comp_unit) { + if (!comp_unit) + return nullptr; + + // The compile unit ID is the index of the DWARF unit. + DWARFUnit *dwarf_cu = DebugInfo().GetUnitAtIndex(comp_unit->GetID()); + if (dwarf_cu && dwarf_cu->GetLLDBCompUnit() == nullptr) + dwarf_cu->SetLLDBCompUnit(comp_unit); + + // It must be DWARFCompileUnit when it created a CompileUnit. + return llvm::cast_or_null<DWARFCompileUnit>(dwarf_cu); +} + +DWARFDebugRanges *SymbolFileDWARF::GetDebugRanges() { + if (!m_ranges) { + LLDB_SCOPED_TIMER(); + + if (m_context.getOrLoadRangesData().GetByteSize() > 0) + m_ranges = std::make_unique<DWARFDebugRanges>(); + + if (m_ranges) + m_ranges->Extract(m_context); + } + return m_ranges.get(); +} + +/// Make an absolute path out of \p file_spec and remap it using the +/// module's source remapping dictionary. +static void MakeAbsoluteAndRemap(FileSpec &file_spec, DWARFUnit &dwarf_cu, + const ModuleSP &module_sp) { + if (!file_spec) + return; + // 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. + file_spec.MakeAbsolute(dwarf_cu.GetCompilationDirectory()); + + if (auto remapped_file = module_sp->RemapSourceFile(file_spec.GetPath())) + file_spec.SetFile(*remapped_file, FileSpec::Style::native); +} + +/// Return the DW_AT_(GNU_)dwo_name. +static const char *GetDWOName(DWARFCompileUnit &dwarf_cu, + const DWARFDebugInfoEntry &cu_die) { + const char *dwo_name = + cu_die.GetAttributeValueAsString(&dwarf_cu, DW_AT_GNU_dwo_name, nullptr); + if (!dwo_name) + dwo_name = + cu_die.GetAttributeValueAsString(&dwarf_cu, DW_AT_dwo_name, nullptr); + return dwo_name; +} + +lldb::CompUnitSP SymbolFileDWARF::ParseCompileUnit(DWARFCompileUnit &dwarf_cu) { + CompUnitSP cu_sp; + CompileUnit *comp_unit = dwarf_cu.GetLLDBCompUnit(); + 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 (GetDebugMapSymfile()) { + // Let the debug map create the compile unit + cu_sp = m_debug_map_symfile->GetCompileUnit(this, dwarf_cu); + dwarf_cu.SetLLDBCompUnit(cu_sp.get()); + } else { + ModuleSP module_sp(m_objfile_sp->GetModule()); + if (module_sp) { + auto initialize_cu = [&](lldb::SupportFileSP support_file_sp, + LanguageType cu_language, + SupportFileList &&support_files = {}) { + BuildCuTranslationTable(); + cu_sp = std::make_shared<CompileUnit>( + module_sp, &dwarf_cu, support_file_sp, + *GetDWARFUnitIndex(dwarf_cu.GetID()), cu_language, + eLazyBoolCalculate, std::move(support_files)); + + dwarf_cu.SetLLDBCompUnit(cu_sp.get()); + + SetCompileUnitAtIndex(dwarf_cu.GetID(), cu_sp); + }; + + auto lazy_initialize_cu = [&]() { + // If the version is < 5, we can't do lazy initialization. + if (dwarf_cu.GetVersion() < 5) + return false; + + // If there is no DWO, there is no reason to initialize + // lazily; we will do eager initialization in that case. + if (GetDebugMapSymfile()) + return false; + const DWARFBaseDIE cu_die = dwarf_cu.GetUnitDIEOnly(); + if (!cu_die) + return false; + if (!GetDWOName(dwarf_cu, *cu_die.GetDIE())) + return false; + + // With DWARFv5 we can assume that the first support + // file is also the name of the compile unit. This + // allows us to avoid loading the non-skeleton unit, + // which may be in a separate DWO file. + SupportFileList support_files; + if (!ParseSupportFiles(dwarf_cu, module_sp, support_files)) + return false; + if (support_files.GetSize() == 0) + return false; + initialize_cu(support_files.GetSupportFileAtIndex(0), + eLanguageTypeUnknown, std::move(support_files)); + return true; + }; + + if (!lazy_initialize_cu()) { + // Eagerly initialize compile unit + const DWARFBaseDIE cu_die = + dwarf_cu.GetNonSkeletonUnit().GetUnitDIEOnly(); + if (cu_die) { + LanguageType cu_language = SymbolFileDWARF::LanguageTypeFromDWARF( + dwarf_cu.GetDWARFLanguageType()); + + FileSpec cu_file_spec(cu_die.GetName(), dwarf_cu.GetPathStyle()); + + // Path needs to be remapped in this case. In the support files + // case ParseSupportFiles takes care of the remapping. + MakeAbsoluteAndRemap(cu_file_spec, dwarf_cu, module_sp); + + initialize_cu(std::make_shared<SupportFile>(cu_file_spec), + cu_language); + } + } + } + } + } + return cu_sp; +} + +void SymbolFileDWARF::BuildCuTranslationTable() { + if (!m_lldb_cu_to_dwarf_unit.empty()) + return; + + DWARFDebugInfo &info = DebugInfo(); + 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); + } + } +} + +std::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 std::nullopt; + return m_lldb_cu_to_dwarf_unit[cu_idx]; +} + +uint32_t SymbolFileDWARF::CalculateNumCompileUnits() { + BuildCuTranslationTable(); + return m_lldb_cu_to_dwarf_unit.empty() ? DebugInfo().GetNumUnits() + : m_lldb_cu_to_dwarf_unit.size(); +} + +CompUnitSP SymbolFileDWARF::ParseCompileUnitAtIndex(uint32_t cu_idx) { + ASSERT_MODULE_LOCK(this); + if (std::optional<uint32_t> dwarf_idx = GetDWARFUnitIndex(cu_idx)) { + if (auto *dwarf_cu = llvm::cast_or_null<DWARFCompileUnit>( + DebugInfo().GetUnitAtIndex(*dwarf_idx))) + return ParseCompileUnit(*dwarf_cu); + } + return {}; +} + +Function *SymbolFileDWARF::ParseFunction(CompileUnit &comp_unit, + const DWARFDIE &die) { + ASSERT_MODULE_LOCK(this); + if (!die.IsValid()) + return nullptr; + + auto type_system_or_err = GetTypeSystemForLanguage(GetLanguage(*die.GetCU())); + if (auto err = type_system_or_err.takeError()) { + LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), std::move(err), + "Unable to parse function: {0}"); + return nullptr; + } + auto ts = *type_system_or_err; + if (!ts) + return nullptr; + DWARFASTParser *dwarf_ast = ts->GetDWARFParser(); + if (!dwarf_ast) + return nullptr; + + DWARFRangeList ranges = die.GetDIE()->GetAttributeAddressRanges( + die.GetCU(), /*check_hi_lo_pc=*/true); + if (ranges.IsEmpty()) + return nullptr; + + // Union of all ranges in the function DIE (if the function is + // discontiguous) + lldb::addr_t lowest_func_addr = ranges.GetMinRangeBase(0); + lldb::addr_t highest_func_addr = ranges.GetMaxRangeEnd(0); + if (lowest_func_addr == LLDB_INVALID_ADDRESS || + lowest_func_addr >= highest_func_addr || + lowest_func_addr < m_first_code_address) + return nullptr; + + ModuleSP module_sp(die.GetModule()); + AddressRange func_range; + func_range.GetBaseAddress().ResolveAddressUsingFileSections( + lowest_func_addr, module_sp->GetSectionList()); + if (!func_range.GetBaseAddress().IsValid()) + return nullptr; + + func_range.SetByteSize(highest_func_addr - lowest_func_addr); + if (!FixupAddress(func_range.GetBaseAddress())) + return nullptr; + + return dwarf_ast->ParseFunctionFromDWARF(comp_unit, die, func_range); +} + +ConstString +SymbolFileDWARF::ConstructFunctionDemangledName(const DWARFDIE &die) { + ASSERT_MODULE_LOCK(this); + if (!die.IsValid()) { + return ConstString(); + } + + auto type_system_or_err = GetTypeSystemForLanguage(GetLanguage(*die.GetCU())); + if (auto err = type_system_or_err.takeError()) { + LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), std::move(err), + "Unable to construct demangled name for function: {0}"); + return ConstString(); + } + + auto ts = *type_system_or_err; + if (!ts) { + LLDB_LOG(GetLog(LLDBLog::Symbols), "Type system no longer live"); + return ConstString(); + } + DWARFASTParser *dwarf_ast = ts->GetDWARFParser(); + if (!dwarf_ast) + return ConstString(); + + return dwarf_ast->ConstructDemangledNameFromDWARF(die); +} + +lldb::addr_t SymbolFileDWARF::FixupAddress(lldb::addr_t file_addr) { + SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile(); + if (debug_map_symfile) + return debug_map_symfile->LinkOSOFileAddress(this, file_addr); + return file_addr; +} + +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) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + DWARFUnit *dwarf_cu = GetDWARFCompileUnit(&comp_unit); + if (dwarf_cu) + return GetLanguage(dwarf_cu->GetNonSkeletonUnit()); + else + return eLanguageTypeUnknown; +} + +XcodeSDK SymbolFileDWARF::ParseXcodeSDK(CompileUnit &comp_unit) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + DWARFUnit *dwarf_cu = GetDWARFCompileUnit(&comp_unit); + if (!dwarf_cu) + return {}; + const DWARFBaseDIE cu_die = dwarf_cu->GetNonSkeletonUnit().GetUnitDIEOnly(); + if (!cu_die) + return {}; + const char *sdk = cu_die.GetAttributeValueAsString(DW_AT_APPLE_sdk, nullptr); + if (!sdk) + return {}; + const char *sysroot = + cu_die.GetAttributeValueAsString(DW_AT_LLVM_sysroot, ""); + // Register the sysroot path remapping with the module belonging to + // the CU as well as the one belonging to the symbol file. The two + // would be different if this is an OSO object and module is the + // corresponding debug map, in which case both should be updated. + ModuleSP module_sp = comp_unit.GetModule(); + if (module_sp) + module_sp->RegisterXcodeSDK(sdk, sysroot); + + ModuleSP local_module_sp = m_objfile_sp->GetModule(); + if (local_module_sp && local_module_sp != module_sp) + local_module_sp->RegisterXcodeSDK(sdk, sysroot); + + return {sdk}; +} + +size_t SymbolFileDWARF::ParseFunctions(CompileUnit &comp_unit) { + LLDB_SCOPED_TIMER(); + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + DWARFUnit *dwarf_cu = GetDWARFCompileUnit(&comp_unit); + if (!dwarf_cu) + return 0; + + size_t functions_added = 0; + dwarf_cu = &dwarf_cu->GetNonSkeletonUnit(); + for (DWARFDebugInfoEntry &entry : dwarf_cu->dies()) { + if (entry.Tag() != DW_TAG_subprogram) + continue; + + DWARFDIE die(dwarf_cu, &entry); + if (comp_unit.FindFunctionByUID(die.GetID())) + continue; + if (ParseFunction(comp_unit, die)) + ++functions_added; + } + // FixupTypes(); + return functions_added; +} + +bool SymbolFileDWARF::ForEachExternalModule( + CompileUnit &comp_unit, + llvm::DenseSet<lldb_private::SymbolFile *> &visited_symbol_files, + llvm::function_ref<bool(Module &)> lambda) { + // Only visit each symbol file once. + if (!visited_symbol_files.insert(this).second) + return false; + + UpdateExternalModuleListIfNeeded(); + for (auto &p : m_external_type_modules) { + ModuleSP module = p.second; + if (!module) + continue; + + // Invoke the action and potentially early-exit. + if (lambda(*module)) + return true; + + for (std::size_t i = 0; i < module->GetNumCompileUnits(); ++i) { + auto cu = module->GetCompileUnitAtIndex(i); + bool early_exit = cu->ForEachExternalModule(visited_symbol_files, lambda); + if (early_exit) + return true; + } + } + return false; +} + +bool SymbolFileDWARF::ParseSupportFiles(CompileUnit &comp_unit, + SupportFileList &support_files) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + DWARFUnit *dwarf_cu = GetDWARFCompileUnit(&comp_unit); + if (!dwarf_cu) + return false; + + if (!ParseSupportFiles(*dwarf_cu, comp_unit.GetModule(), support_files)) + return false; + + return true; +} + +bool SymbolFileDWARF::ParseSupportFiles(DWARFUnit &dwarf_cu, + const ModuleSP &module, + SupportFileList &support_files) { + + dw_offset_t offset = dwarf_cu.GetLineTableOffset(); + if (offset == DW_INVALID_OFFSET) + return false; + + ElapsedTime elapsed(m_parse_time); + llvm::DWARFDebugLine::Prologue prologue; + if (!ParseLLVMLineTablePrologue(m_context, prologue, offset, + dwarf_cu.GetOffset())) + return false; + + std::string comp_dir = dwarf_cu.GetCompilationDirectory().GetPath(); + ParseSupportFilesFromPrologue(support_files, module, prologue, + dwarf_cu.GetPathStyle(), comp_dir); + return true; +} + +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); + if (const SupportFileList *support_files = GetTypeUnitSupportFiles(tu)) + return support_files->GetFileSpecAtIndex(file_idx); + return {}; +} + +const SupportFileList * +SymbolFileDWARF::GetTypeUnitSupportFiles(DWARFTypeUnit &tu) { + static SupportFileList 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 nullptr; + + // 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); + std::unique_ptr<SupportFileList> &list = iter_bool.first->second; + if (iter_bool.second) { + list = std::make_unique<SupportFileList>(); + uint64_t line_table_offset = offset; + llvm::DWARFDataExtractor data = + m_context.getOrLoadLineData().GetAsLLVMDWARF(); + llvm::DWARFContext &ctx = m_context.GetAsLLVM(); + llvm::DWARFDebugLine::Prologue prologue; + auto report = [](llvm::Error error) { + Log *log = GetLog(DWARFLog::DebugInfo); + LLDB_LOG_ERROR(log, std::move(error), + "SymbolFileDWARF::GetTypeUnitSupportFiles failed to parse " + "the line table prologue: {0}"); + }; + ElapsedTime elapsed(m_parse_time); + llvm::Error error = prologue.parse(data, &line_table_offset, report, ctx); + if (error) + report(std::move(error)); + else + ParseSupportFilesFromPrologue(*list, GetObjectFile()->GetModule(), + prologue, tu.GetPathStyle()); + } + return list.get(); +} + +bool SymbolFileDWARF::ParseIsOptimized(CompileUnit &comp_unit) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + DWARFUnit *dwarf_cu = GetDWARFCompileUnit(&comp_unit); + if (dwarf_cu) + return dwarf_cu->GetNonSkeletonUnit().GetIsOptimized(); + return false; +} + +bool SymbolFileDWARF::ParseImportedModules( + const lldb_private::SymbolContext &sc, + std::vector<SourceModule> &imported_modules) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + 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.children()) { + 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)) { + FileSpec include_spec(include_path, dwarf_cu->GetPathStyle()); + MakeAbsoluteAndRemap(include_spec, *dwarf_cu, + m_objfile_sp->GetModule()); + module.search_path = ConstString(include_spec.GetPath()); + } + if (const char *sysroot = dwarf_cu->DIE().GetAttributeValueAsString( + DW_AT_LLVM_sysroot, nullptr)) + module.sysroot = ConstString(sysroot); + imported_modules.push_back(module); + } + } + return true; +} + +bool SymbolFileDWARF::ParseLineTable(CompileUnit &comp_unit) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + if (comp_unit.GetLineTable() != nullptr) + return true; + + DWARFUnit *dwarf_cu = GetDWARFCompileUnit(&comp_unit); + if (!dwarf_cu) + return false; + + dw_offset_t offset = dwarf_cu->GetLineTableOffset(); + if (offset == DW_INVALID_OFFSET) + return false; + + ElapsedTime elapsed(m_parse_time); + llvm::DWARFDebugLine line; + const llvm::DWARFDebugLine::LineTable *line_table = + ParseLLVMLineTable(m_context, line, offset, dwarf_cu->GetOffset()); + + if (!line_table) + return false; + + // FIXME: Rather than parsing the whole line table and then copying it over + // into LLDB, we should explore using a callback to populate the line table + // while we parse to reduce memory usage. + std::vector<std::unique_ptr<LineSequence>> sequences; + // The Sequences view contains only valid line sequences. Don't iterate over + // the Rows directly. + for (const llvm::DWARFDebugLine::Sequence &seq : line_table->Sequences) { + // Ignore line sequences that do not start after the first code address. + // All addresses generated in a sequence are incremental so we only need + // to check the first one of the sequence. Check the comment at the + // m_first_code_address declaration for more details on this. + if (seq.LowPC < m_first_code_address) + continue; + std::unique_ptr<LineSequence> sequence = + LineTable::CreateLineSequenceContainer(); + for (unsigned idx = seq.FirstRowIndex; idx < seq.LastRowIndex; ++idx) { + const llvm::DWARFDebugLine::Row &row = line_table->Rows[idx]; + LineTable::AppendLineEntryToSequence( + sequence.get(), row.Address.Address, row.Line, row.Column, row.File, + row.IsStmt, row.BasicBlock, row.PrologueEnd, row.EpilogueBegin, + row.EndSequence); + } + sequences.push_back(std::move(sequence)); + } + + std::unique_ptr<LineTable> line_table_up = + std::make_unique<LineTable>(&comp_unit, std::move(sequences)); + + if (SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile()) { + // 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; +} + +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; + + ElapsedTime elapsed(m_parse_time); + 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) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + + 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; + + std::optional<int> decl_file; + std::optional<int> decl_line; + std::optional<int> decl_column; + std::optional<int> call_file; + std::optional<int> call_line; + std::optional<int> call_column; + 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( + "{0:x8}: adding range [{1:x16}-{2:x16}) which has a base " + "that is less than the function's low PC {3:x16}. 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 || decl_line || decl_column) + decl_up = std::make_unique<Declaration>( + comp_unit.GetSupportFiles().GetFileSpecAtIndex( + decl_file ? *decl_file : 0), + decl_line ? *decl_line : 0, decl_column ? *decl_column : 0); + + std::unique_ptr<Declaration> call_up; + if (call_file || call_line || call_column) + call_up = std::make_unique<Declaration>( + comp_unit.GetSupportFiles().GetFileSpecAtIndex( + call_file ? *call_file : 0), + call_line ? *call_line : 0, call_column ? *call_column : 0); + + 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.children()) { + 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) { + auto *type_system = decl_ctx.GetTypeSystem(); + if (type_system != nullptr) + type_system->GetDWARFParser()->EnsureAllDIEsInDeclContextHaveBeenParsed( + decl_ctx); +} + +DWARFDIE +SymbolFileDWARF::GetDIE(lldb::user_id_t uid) { return GetDIE(DIERef(uid)); } + +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 GetDecl(die); + 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 GetDeclContext(die); + return CompilerDeclContext(); +} + +CompilerDeclContext +SymbolFileDWARF::GetDeclContextContainingUID(lldb::user_id_t type_uid) { + 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 GetContainingDeclContext(die); + return CompilerDeclContext(); +} + +std::vector<CompilerContext> +SymbolFileDWARF::GetCompilerContextForUID(lldb::user_id_t type_uid) { + 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 {}; +} + +Type *SymbolFileDWARF::ResolveTypeUID(lldb::user_id_t type_uid) { + 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; +} + +std::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 std::nullopt; +} + +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 = GetLog(DWARFLog::DebugInfo); + if (log) + GetObjectFile()->GetModule()->LogMessage( + log, + "SymbolFileDWARF::ResolveTypeUID (die = {0:x16}) {1} ({2}) '{3}'", + die.GetOffset(), DW_TAG_value_to_name(die.Tag()), die.Tag(), + 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 = {0:x16}) {1} ({2}) " + "'{3}' resolve parent forward type for {4:x16})", + die.GetOffset(), DW_TAG_value_to_name(die.Tag()), die.Tag(), + 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::HasForwardDeclForCompilerType( + const CompilerType &compiler_type) { + CompilerType compiler_type_no_qualifiers = + ClangUtil::RemoveFastQualifiers(compiler_type); + if (GetForwardDeclCompilerTypeToDIE().count( + compiler_type_no_qualifiers.GetOpaqueQualType())) { + return true; + } + auto type_system = compiler_type.GetTypeSystem(); + auto clang_type_system = type_system.dyn_cast_or_null<TypeSystemClang>(); + 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()); + auto clang_type_system = + compiler_type.GetTypeSystem().dyn_cast_or_null<TypeSystemClang>(); + 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 = GetForwardDeclCompilerTypeToDIE().find( + compiler_type_no_qualifiers.GetOpaqueQualType()); + if (die_it == GetForwardDeclCompilerTypeToDIE().end()) { + // We have already resolved this type... + return true; + } + + DWARFDIE decl_die = GetDIE(die_it->getSecond()); + // Once we start resolving this type, remove it from the forward + // declaration map in case anyone's child members or other types require this + // type to get resolved. + GetForwardDeclCompilerTypeToDIE().erase(die_it); + DWARFDIE def_die = FindDefinitionDIE(decl_die); + if (!def_die) { + SymbolFileDWARFDebugMap *debug_map_symfile = 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... + def_die = debug_map_symfile->FindDefinitionDIE(decl_die); + } + } + if (!def_die) { + // If we don't have definition DIE, CompleteTypeFromDWARF will forcefully + // complete this type. + def_die = decl_die; + } + + DWARFASTParser *dwarf_ast = GetDWARFParser(*def_die.GetCU()); + if (!dwarf_ast) + return false; + Type *type = GetDIEToType().lookup(decl_die.GetDIE()); + if (decl_die != def_die) { + GetDIEToType()[def_die.GetDIE()] = type; + DWARFASTParserClang *ast_parser = + static_cast<DWARFASTParserClang *>(dwarf_ast); + ast_parser->MapDeclDIEToDefDIE(decl_die, def_die); + } + + Log *log = GetLog(DWARFLog::DebugInfo | DWARFLog::TypeCompletion); + if (log) + GetObjectFile()->GetModule()->LogMessageVerboseBacktrace( + log, "{0:x8}: {1} ({2}) '{3}' resolving forward declaration...", + def_die.GetID(), DW_TAG_value_to_name(def_die.Tag()), def_die.Tag(), + type->GetName().AsCString()); + assert(compiler_type); + return dwarf_ast->CompleteTypeFromDWARF(def_die, type, compiler_type); +} + +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: {0:x16}: {1} ({2}) {3}", + die.GetOffset(), DW_TAG_value_to_name(die.Tag()), die.Tag(), + die.GetName()); + + } else + return type; + } + return nullptr; +} + +CompileUnit * +SymbolFileDWARF::GetCompUnitForDWARFCompUnit(DWARFCompileUnit &dwarf_cu) { + + if (dwarf_cu.IsDWOUnit()) { + DWARFCompileUnit *non_dwo_cu = dwarf_cu.GetSkeletonUnit(); + assert(non_dwo_cu); + return non_dwo_cu->GetSymbolFileDWARF().GetCompUnitForDWARFCompUnit( + *non_dwo_cu); + } + // Check if the symbol vendor already knows about this compile unit? + CompileUnit *lldb_cu = dwarf_cu.GetLLDBCompUnit(); + if (lldb_cu) + return lldb_cu; + // 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(); +} + +void SymbolFileDWARF::GetObjCMethods( + ConstString class_name, llvm::function_ref<bool(DWARFDIE die)> callback) { + m_index->GetObjCMethods(class_name, callback); +} + +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::GetExternalModule(ConstString name) { + UpdateExternalModuleListIfNeeded(); + const auto &pos = m_external_type_modules.find(name); + if (pos == m_external_type_modules.end()) + return lldb::ModuleSP(); + return pos->second; +} + +SymbolFileDWARF *SymbolFileDWARF::GetDIERefSymbolFile(const DIERef &die_ref) { + // 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. + + std::optional<uint32_t> file_index = die_ref.file_index(); + + // If the file index matches, then we have the right SymbolFileDWARF already. + // This will work for both .dwo file and DWARF in .o files for mac. Also if + // both the file indexes are invalid, then we have a match. + if (GetFileIndex() == file_index) + return this; + + if (file_index) { + // We have a SymbolFileDWARFDebugMap, so let it find the right file + if (SymbolFileDWARFDebugMap *debug_map = GetDebugMapSymfile()) + return debug_map->GetSymbolFileByOSOIndex(*file_index); + + // Handle the .dwp file case correctly + if (*file_index == DIERef::k_file_index_mask) + return GetDwpSymbolFile().get(); // DWP case + + // Handle the .dwo file case correctly + return DebugInfo().GetUnitAtIndex(*die_ref.file_index()) + ->GetDwoSymbolFile(); // DWO case + } + return this; +} + +DWARFDIE +SymbolFileDWARF::GetDIE(const DIERef &die_ref) { + if (die_ref.die_offset() == DW_INVALID_OFFSET) + return DWARFDIE(); + + // 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()); + SymbolFileDWARF *symbol_file = GetDIERefSymbolFile(die_ref); + if (symbol_file) + return symbol_file->DebugInfo().GetDIE(die_ref.section(), + die_ref.die_offset()); + return DWARFDIE(); +} + +/// Return the DW_AT_(GNU_)dwo_id. +static std::optional<uint64_t> GetDWOId(DWARFCompileUnit &dwarf_cu, + const DWARFDebugInfoEntry &cu_die) { + std::optional<uint64_t> dwo_id = + cu_die.GetAttributeValueAsOptionalUnsigned(&dwarf_cu, DW_AT_GNU_dwo_id); + if (dwo_id) + return dwo_id; + return cu_die.GetAttributeValueAsOptionalUnsigned(&dwarf_cu, DW_AT_dwo_id); +} + +std::optional<uint64_t> SymbolFileDWARF::GetDWOId() { + if (GetNumCompileUnits() == 1) { + if (auto comp_unit = GetCompileUnitAtIndex(0)) + if (DWARFCompileUnit *cu = GetDWARFCompileUnit(comp_unit.get())) + if (DWARFDebugInfoEntry *cu_die = cu->DIE().GetDIE()) + return ::GetDWOId(*cu, *cu_die); + } + return {}; +} + +DWARFUnit *SymbolFileDWARF::GetSkeletonUnit(DWARFUnit *dwo_unit) { + return DebugInfo().GetSkeletonUnit(dwo_unit); +} + +std::shared_ptr<SymbolFileDWARFDwo> +SymbolFileDWARF::GetDwoSymbolFileForCompileUnit( + DWARFUnit &unit, const DWARFDebugInfoEntry &cu_die) { + // If this is a Darwin-style debug map (non-.dSYM) symbol file, + // never attempt to load ELF-style DWO files since the -gmodules + // support uses the same DWO mechanism to specify full debug info + // files for modules. This is handled in + // UpdateExternalModuleListIfNeeded(). + if (GetDebugMapSymfile()) + return nullptr; + + DWARFCompileUnit *dwarf_cu = llvm::dyn_cast<DWARFCompileUnit>(&unit); + // Only compile units can be split into two parts and we should only + // look for a DWO file if there is a valid DWO ID. + if (!dwarf_cu || !dwarf_cu->GetDWOId().has_value()) + return nullptr; + + const char *dwo_name = GetDWOName(*dwarf_cu, cu_die); + if (!dwo_name) { + unit.SetDwoError(Status::createWithFormat( + "missing DWO name in skeleton DIE {0:x16}", cu_die.GetOffset())); + return nullptr; + } + + if (std::shared_ptr<SymbolFileDWARFDwo> dwp_sp = GetDwpSymbolFile()) + return dwp_sp; + + FileSpec dwo_file(dwo_name); + FileSystem::Instance().Resolve(dwo_file); + bool found = false; + + const FileSpecList &debug_file_search_paths = + Target::GetDefaultDebugFileSearchPaths(); + size_t num_search_paths = debug_file_search_paths.GetSize(); + + // It's relative, e.g. "foo.dwo", but we just to happen to be right next to + // it. Or it's absolute. + found = FileSystem::Instance().Exists(dwo_file); + + const char *comp_dir = + cu_die.GetAttributeValueAsString(dwarf_cu, DW_AT_comp_dir, nullptr); + if (!found) { + // It could be a relative path that also uses DW_AT_COMP_DIR. + if (comp_dir) { + dwo_file.SetFile(comp_dir, FileSpec::Style::native); + if (!dwo_file.IsRelative()) { + FileSystem::Instance().Resolve(dwo_file); + dwo_file.AppendPathComponent(dwo_name); + found = FileSystem::Instance().Exists(dwo_file); + } else { + FileSpecList dwo_paths; + + // if DW_AT_comp_dir is relative, it should be relative to the location + // of the executable, not to the location from which the debugger was + // launched. + FileSpec relative_to_binary = dwo_file; + relative_to_binary.PrependPathComponent( + m_objfile_sp->GetFileSpec().GetDirectory().GetStringRef()); + FileSystem::Instance().Resolve(relative_to_binary); + relative_to_binary.AppendPathComponent(dwo_name); + dwo_paths.Append(relative_to_binary); + + // Or it's relative to one of the user specified debug directories. + for (size_t idx = 0; idx < num_search_paths; ++idx) { + FileSpec dirspec = debug_file_search_paths.GetFileSpecAtIndex(idx); + dirspec.AppendPathComponent(comp_dir); + FileSystem::Instance().Resolve(dirspec); + if (!FileSystem::Instance().IsDirectory(dirspec)) + continue; + + dirspec.AppendPathComponent(dwo_name); + dwo_paths.Append(dirspec); + } + + size_t num_possible = dwo_paths.GetSize(); + for (size_t idx = 0; idx < num_possible && !found; ++idx) { + FileSpec dwo_spec = dwo_paths.GetFileSpecAtIndex(idx); + if (FileSystem::Instance().Exists(dwo_spec)) { + dwo_file = dwo_spec; + found = true; + } + } + } + } else { + Log *log = GetLog(LLDBLog::Symbols); + LLDB_LOGF(log, + "unable to locate relative .dwo debug file \"%s\" for " + "skeleton DIE 0x%016" PRIx64 " without valid DW_AT_comp_dir " + "attribute", + dwo_name, cu_die.GetOffset()); + } + } + + if (!found) { + // Try adding the DW_AT_dwo_name ( e.g. "c/d/main-main.dwo"), and just the + // filename ("main-main.dwo") to binary dir and search paths. + FileSpecList dwo_paths; + FileSpec dwo_name_spec(dwo_name); + llvm::StringRef filename_only = dwo_name_spec.GetFilename(); + + FileSpec binary_directory( + m_objfile_sp->GetFileSpec().GetDirectory().GetStringRef()); + FileSystem::Instance().Resolve(binary_directory); + + if (dwo_name_spec.IsRelative()) { + FileSpec dwo_name_binary_directory(binary_directory); + dwo_name_binary_directory.AppendPathComponent(dwo_name); + dwo_paths.Append(dwo_name_binary_directory); + } + + FileSpec filename_binary_directory(binary_directory); + filename_binary_directory.AppendPathComponent(filename_only); + dwo_paths.Append(filename_binary_directory); + + for (size_t idx = 0; idx < num_search_paths; ++idx) { + FileSpec dirspec = debug_file_search_paths.GetFileSpecAtIndex(idx); + FileSystem::Instance().Resolve(dirspec); + if (!FileSystem::Instance().IsDirectory(dirspec)) + continue; + + FileSpec dwo_name_dirspec(dirspec); + dwo_name_dirspec.AppendPathComponent(dwo_name); + dwo_paths.Append(dwo_name_dirspec); + + FileSpec filename_dirspec(dirspec); + filename_dirspec.AppendPathComponent(filename_only); + dwo_paths.Append(filename_dirspec); + } + + size_t num_possible = dwo_paths.GetSize(); + for (size_t idx = 0; idx < num_possible && !found; ++idx) { + FileSpec dwo_spec = dwo_paths.GetFileSpecAtIndex(idx); + if (FileSystem::Instance().Exists(dwo_spec)) { + dwo_file = dwo_spec; + found = true; + } + } + } + + if (!found) { + FileSpec error_dwo_path(dwo_name); + FileSystem::Instance().Resolve(error_dwo_path); + if (error_dwo_path.IsRelative() && comp_dir != nullptr) { + error_dwo_path.PrependPathComponent(comp_dir); + FileSystem::Instance().Resolve(error_dwo_path); + } + unit.SetDwoError(Status::createWithFormat( + "unable to locate .dwo debug file \"{0}\" for skeleton DIE " + "{1:x16}", + error_dwo_path.GetPath().c_str(), cu_die.GetOffset())); + + if (m_dwo_warning_issued.test_and_set(std::memory_order_relaxed) == false) { + GetObjectFile()->GetModule()->ReportWarning( + "unable to locate separate debug file (dwo, dwp). Debugging will be " + "degraded."); + } + 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) { + unit.SetDwoError(Status::createWithFormat( + "unable to load object file for .dwo debug file \"{0}\" for " + "unit DIE {1:x16}", + dwo_name, cu_die.GetOffset())); + return nullptr; + } + + return std::make_shared<SymbolFileDWARFDwo>(*this, dwo_obj_file, + dwarf_cu->GetID()); +} + +void SymbolFileDWARF::UpdateExternalModuleListIfNeeded() { + if (m_fetched_external_modules) + return; + m_fetched_external_modules = true; + DWARFDebugInfo &debug_info = DebugInfo(); + + // Follow DWO skeleton unit breadcrumbs. + const uint32_t num_compile_units = GetNumCompileUnits(); + for (uint32_t cu_idx = 0; cu_idx < num_compile_units; ++cu_idx) { + auto *dwarf_cu = + llvm::dyn_cast<DWARFCompileUnit>(debug_info.GetUnitAtIndex(cu_idx)); + if (!dwarf_cu) + continue; + + const DWARFBaseDIE die = dwarf_cu->GetUnitDIEOnly(); + if (!die || die.HasChildren() || !die.GetDIE()) + continue; + + const char *name = die.GetAttributeValueAsString(DW_AT_name, nullptr); + if (!name) + continue; + + ConstString const_name(name); + ModuleSP &module_sp = m_external_type_modules[const_name]; + if (module_sp) + continue; + + const char *dwo_path = GetDWOName(*dwarf_cu, *die.GetDIE()); + if (!dwo_path) + continue; + + 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_objfile_sp->GetModule()->GetArchitecture(); + + // When LLDB loads "external" modules it looks at the presence of + // DW_AT_dwo_name. However, when the already created module + // (corresponding to .dwo itself) is being processed, it will see + // the presence of DW_AT_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_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_objfile_sp->GetFileSpec().GetFileNameExtension() == ".dwo" && + llvm::StringRef(m_objfile_sp->GetFileSpec().GetPath()) + .ends_with(dwo_module_spec.GetFileSpec().GetPath())) { + continue; + } + + Status error = ModuleList::GetSharedModule(dwo_module_spec, module_sp, + nullptr, nullptr, nullptr); + if (!module_sp) { + GetObjectFile()->GetModule()->ReportWarning( + "{0:x16}: unable to locate module needed for external types: " + "{1}\nerror: {2}\nDebugging will be degraded due to missing " + "types. Rebuilding the project will regenerate the needed " + "module files.", + die.GetOffset(), dwo_module_spec.GetFileSpec().GetPath().c_str(), + error.AsCString("unknown error")); + continue; + } + + // Verify the DWO hash. + // FIXME: Technically "0" is a valid hash. + std::optional<uint64_t> dwo_id = ::GetDWOId(*dwarf_cu, *die.GetDIE()); + if (!dwo_id) + continue; + + auto *dwo_symfile = + llvm::dyn_cast_or_null<SymbolFileDWARF>(module_sp->GetSymbolFile()); + if (!dwo_symfile) + continue; + std::optional<uint64_t> dwo_dwo_id = dwo_symfile->GetDWOId(); + if (!dwo_dwo_id) + continue; + + if (dwo_id != dwo_dwo_id) { + GetObjectFile()->GetModule()->ReportWarning( + "{0:x16}: Module {1} is out-of-date (hash mismatch). Type " + "information " + "from this module may be incomplete or inconsistent with the rest of " + "the program. Rebuilding the project will regenerate the needed " + "module files.", + die.GetOffset(), dwo_module_spec.GetFileSpec().GetPath().c_str()); + } + } +} + +SymbolFileDWARF::GlobalVariableMap &SymbolFileDWARF::GetGlobalAranges() { + if (!m_global_aranges_up) { + m_global_aranges_up = std::make_unique<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 DWARFExpressionList &location = + var_sp->LocationExpressionList(); + ExecutionContext exe_ctx; + llvm::Expected<Value> location_result = location.Evaluate( + &exe_ctx, nullptr, LLDB_INVALID_ADDRESS, nullptr, nullptr); + if (location_result) { + if (location_result->GetValueType() == + Value::ValueType::FileAddress) { + 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(nullptr).value_or(0); + m_global_aranges_up->Append(GlobalVariableMap::Entry( + file_addr, byte_size, var_sp.get())); + } + } else { + LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), + location_result.takeError(), + "location expression failed to execute: {0}"); + } + } + } + } + } + } + } + m_global_aranges_up->Sort(); + } + return *m_global_aranges_up; +} + +void SymbolFileDWARF::ResolveFunctionAndBlock(lldb::addr_t file_vm_addr, + bool lookup_block, + SymbolContext &sc) { + assert(sc.comp_unit); + DWARFCompileUnit &cu = + GetDWARFCompileUnit(sc.comp_unit)->GetNonSkeletonUnit(); + DWARFDIE function_die = 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 && lookup_block) + block_die = function_die.LookupDeepestBlock(file_vm_addr); + } + + if (!sc.function || !lookup_block) + return; + + Block &block = sc.function->GetBlock(true); + if (block_die) + sc.block = block.FindBlockByID(block_die.GetID()); + else + sc.block = block.FindBlockByID(function_die.GetID()); +} + +uint32_t SymbolFileDWARF::ResolveSymbolContext(const Address &so_addr, + SymbolContextItem resolve_scope, + SymbolContext &sc) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + LLDB_SCOPED_TIMERF("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(); + const DWARFDebugAranges &aranges = debug_info.GetCompileUnitAranges(); + 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)) { + ResolveFunctionAndBlock(file_vm_addr, + resolve_scope & eSymbolContextBlock, sc); + if (sc.function) + resolved |= eSymbolContextFunction; + 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.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( + "{0:x16}: compile unit {1} failed to create a valid " + "lldb_private::CompileUnit class.", + cu_offset, cu_idx); + } + } + } + } + return resolved; +} + +uint32_t SymbolFileDWARF::ResolveSymbolContext( + const SourceLocationSpec &src_location_spec, + SymbolContextItem resolve_scope, SymbolContextList &sc_list) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + const bool check_inlines = src_location_spec.GetCheckInlines(); + 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; + + bool file_spec_matches_cu_file_spec = FileSpec::Match( + src_location_spec.GetFileSpec(), dc_cu->GetPrimaryFile()); + if (check_inlines || file_spec_matches_cu_file_spec) { + dc_cu->ResolveSymbolContext(src_location_spec, resolve_scope, sc_list); + if (!check_inlines) + break; + } + } + } + return sc_list.GetSize() - prev_size; +} + +void SymbolFileDWARF::PreloadSymbols() { + // Get the symbol table for the symbol file prior to taking the module lock + // so that it is available without needing to take the module lock. The DWARF + // indexing might end up needing to relocate items when DWARF sections are + // loaded as they might end up getting the section contents which can call + // ObjectFileELF::RelocateSection() which in turn will ask for the symbol + // table and can cause deadlocks. + GetSymtab(); + 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.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(); + auto type_system_or_err = GetTypeSystemForLanguage( + decl_ctx_type_system->GetMinimumLanguage(nullptr)); + if (auto err = type_system_or_err.takeError()) { + LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), std::move(err), + "Unable to match namespace decl using TypeSystem: {0}"); + return false; + } + + if (decl_ctx_type_system == type_system_or_err->get()) + return true; // The type systems match, return true + + // The namespace AST was valid, and it does not match... + Log *log = GetLog(DWARFLog::Lookups); + + if (log) + GetObjectFile()->GetModule()->LogMessage( + log, "Valid namespace does not match symbol file"); + + return false; +} + +void SymbolFileDWARF::FindGlobalVariables( + ConstString name, const CompilerDeclContext &parent_decl_ctx, + uint32_t max_matches, VariableList &variables) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + Log *log = GetLog(DWARFLog::Lookups); + + if (log) + GetObjectFile()->GetModule()->LogMessage( + log, + "SymbolFileDWARF::FindGlobalVariables (name=\"{0}\", " + "parent_decl_ctx={1:p}, max_matches={2}, variables)", + name.GetCString(), static_cast<const void *>(&parent_decl_ctx), + max_matches); + + if (!DeclContextMatchesThisSymbolFile(parent_decl_ctx)) + return; + + // 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 = Mangled::GetManglingScheme(name.GetStringRef()) != + Mangled::eManglingSchemeNone; + + if (!CPlusPlusLanguage::ExtractContextAndIdentifier(name.GetCString(), + context, basename)) + basename = name.GetStringRef(); + + // Loop invariant: Variables up to this index have been checked for context + // matches. + uint32_t pruned_idx = original_size; + + SymbolContext sc; + m_index->GetGlobalVariables(ConstString(basename), [&](DWARFDIE die) { + if (!sc.module_sp) + sc.module_sp = m_objfile_sp->GetModule(); + assert(sc.module_sp); + + if (die.Tag() != DW_TAG_variable) + return true; + + auto *dwarf_cu = llvm::dyn_cast<DWARFCompileUnit>(die.GetCU()); + if (!dwarf_cu) + return true; + sc.comp_unit = GetCompUnitForDWARFCompUnit(*dwarf_cu); + + if (parent_decl_ctx) { + if (DWARFASTParser *dwarf_ast = GetDWARFParser(*die.GetCU())) { + CompilerDeclContext actual_parent_decl_ctx = + dwarf_ast->GetDeclContextContainingUIDFromDWARF(die); + + /// If the actual namespace is inline (i.e., had a DW_AT_export_symbols) + /// and a child (possibly through other layers of inline namespaces) + /// of the namespace referred to by 'basename', allow the lookup to + /// succeed. + if (!actual_parent_decl_ctx || + (actual_parent_decl_ctx != parent_decl_ctx && + !parent_decl_ctx.IsContainedInLookup(actual_parent_decl_ctx))) + return true; + } + } + + ParseAndAppendGlobalVariable(sc, die, 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); + } + + return variables.GetSize() - original_size < max_matches; + }); + + // 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=\"{0}\", " + "parent_decl_ctx={1:p}, max_matches={2}, variables) => {3}", + name.GetCString(), static_cast<const void *>(&parent_decl_ctx), + max_matches, num_matches); + } +} + +void SymbolFileDWARF::FindGlobalVariables(const RegularExpression ®ex, + uint32_t max_matches, + VariableList &variables) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + Log *log = GetLog(DWARFLog::Lookups); + + if (log) { + GetObjectFile()->GetModule()->LogMessage( + log, + "SymbolFileDWARF::FindGlobalVariables (regex=\"{0}\", " + "max_matches={1}, variables)", + regex.GetText().str().c_str(), max_matches); + } + + // Remember how many variables are in the list before we search. + const uint32_t original_size = variables.GetSize(); + + SymbolContext sc; + m_index->GetGlobalVariables(regex, [&](DWARFDIE die) { + if (!sc.module_sp) + sc.module_sp = m_objfile_sp->GetModule(); + assert(sc.module_sp); + + DWARFCompileUnit *dwarf_cu = llvm::dyn_cast<DWARFCompileUnit>(die.GetCU()); + if (!dwarf_cu) + return true; + sc.comp_unit = GetCompUnitForDWARFCompUnit(*dwarf_cu); + + ParseAndAppendGlobalVariable(sc, die, variables); + + return variables.GetSize() - original_size < max_matches; + }); +} + +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(); + } + + sc_list.Append(sc); + return true; + } + + return false; +} + +bool SymbolFileDWARF::DIEInDeclContext(const CompilerDeclContext &decl_ctx, + const DWARFDIE &die, + bool only_root_namespaces) { + // 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.IsValid()) { + // ...But if we are only checking root decl contexts, confirm that the + // 'die' is a top-level context. + if (only_root_namespaces) + return die.GetParent().Tag() == llvm::dwarf::DW_TAG_compile_unit; + + return true; + } + + if (die) { + if (DWARFASTParser *dwarf_ast = GetDWARFParser(*die.GetCU())) { + if (CompilerDeclContext actual_decl_ctx = + dwarf_ast->GetDeclContextContainingUIDFromDWARF(die)) + return decl_ctx.IsContainedInLookup(actual_decl_ctx); + } + } + return false; +} + +void SymbolFileDWARF::FindFunctions(const Module::LookupInfo &lookup_info, + const CompilerDeclContext &parent_decl_ctx, + bool include_inlines, + SymbolContextList &sc_list) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + ConstString name = lookup_info.GetLookupName(); + FunctionNameType name_type_mask = lookup_info.GetNameTypeMask(); + + // eFunctionNameTypeAuto should be pre-resolved by a call to + // Module::LookupInfo::LookupInfo() + assert((name_type_mask & eFunctionNameTypeAuto) == 0); + + Log *log = GetLog(DWARFLog::Lookups); + + if (log) { + GetObjectFile()->GetModule()->LogMessage( + log, + "SymbolFileDWARF::FindFunctions (name=\"{0}\", name_type_mask={1:x}, " + "sc_list)", + name.GetCString(), name_type_mask); + } + + if (!DeclContextMatchesThisSymbolFile(parent_decl_ctx)) + return; + + // If name is empty then we won't find anything. + if (name.IsEmpty()) + return; + + // 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; + + m_index->GetFunctions(lookup_info, *this, parent_decl_ctx, [&](DWARFDIE die) { + if (resolved_dies.insert(die.GetDIE()).second) + ResolveFunction(die, include_inlines, sc_list); + return true; + }); + // With -gsimple-template-names, a templated type's DW_AT_name will not + // contain the template parameters. Try again stripping '<' and anything + // after, filtering out entries with template parameters that don't match. + { + const llvm::StringRef name_ref = name.GetStringRef(); + auto it = name_ref.find('<'); + if (it != llvm::StringRef::npos) { + const llvm::StringRef name_no_template_params = name_ref.slice(0, it); + + Module::LookupInfo no_tp_lookup_info(lookup_info); + no_tp_lookup_info.SetLookupName(ConstString(name_no_template_params)); + m_index->GetFunctions(no_tp_lookup_info, *this, parent_decl_ctx, + [&](DWARFDIE die) { + if (resolved_dies.insert(die.GetDIE()).second) + ResolveFunction(die, include_inlines, sc_list); + return true; + }); + } + } + + // 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=\"{0}\", " + "name_type_mask={1:x}, include_inlines={2:d}, sc_list) => {3}", + name.GetCString(), name_type_mask, include_inlines, num_matches); + } +} + +void SymbolFileDWARF::FindFunctions(const RegularExpression ®ex, + bool include_inlines, + SymbolContextList &sc_list) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + LLDB_SCOPED_TIMERF("SymbolFileDWARF::FindFunctions (regex = '%s')", + regex.GetText().str().c_str()); + + Log *log = GetLog(DWARFLog::Lookups); + + if (log) { + GetObjectFile()->GetModule()->LogMessage( + log, "SymbolFileDWARF::FindFunctions (regex=\"{0}\", sc_list)", + regex.GetText().str().c_str()); + } + + llvm::DenseSet<const DWARFDebugInfoEntry *> resolved_dies; + m_index->GetFunctions(regex, [&](DWARFDIE die) { + if (resolved_dies.insert(die.GetDIE()).second) + ResolveFunction(die, include_inlines, sc_list); + return true; + }); +} + +void SymbolFileDWARF::GetMangledNamesForFunction( + const std::string &scope_qualified_name, + std::vector<ConstString> &mangled_names) { + DWARFDebugInfo &info = DebugInfo(); + uint32_t 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 (DIERef die_ref : + m_function_scope_qualified_name_map.lookup(scope_qualified_name)) { + DWARFDIE die = GetDIE(die_ref); + mangled_names.push_back(ConstString(die.GetMangledName())); + } +} + +/// Split a name up into a basename and template parameters. +static bool SplitTemplateParams(llvm::StringRef fullname, + llvm::StringRef &basename, + llvm::StringRef &template_params) { + auto it = fullname.find('<'); + if (it == llvm::StringRef::npos) { + basename = fullname; + template_params = llvm::StringRef(); + return false; + } + basename = fullname.slice(0, it); + template_params = fullname.slice(it, fullname.size()); + return true; +} + +static bool UpdateCompilerContextForSimpleTemplateNames(TypeQuery &match) { + // We need to find any names in the context that have template parameters + // and strip them so the context can be matched when -gsimple-template-names + // is being used. Returns true if any of the context items were updated. + bool any_context_updated = false; + for (auto &context : match.GetContextRef()) { + llvm::StringRef basename, params; + if (SplitTemplateParams(context.name.GetStringRef(), basename, params)) { + context.name = ConstString(basename); + any_context_updated = true; + } + } + return any_context_updated; +} + +uint64_t SymbolFileDWARF::GetDebugInfoSize(bool load_all_debug_info) { + DWARFDebugInfo &info = DebugInfo(); + uint32_t num_comp_units = info.GetNumUnits(); + + uint64_t debug_info_size = SymbolFileCommon::GetDebugInfoSize(); + // In dwp scenario, debug info == skeleton debug info + dwp debug info. + if (std::shared_ptr<SymbolFileDWARFDwo> dwp_sp = GetDwpSymbolFile()) + return debug_info_size + dwp_sp->GetDebugInfoSize(); + + // In dwo scenario, debug info == skeleton debug info + all dwo debug info. + for (uint32_t i = 0; i < num_comp_units; i++) { + DWARFUnit *cu = info.GetUnitAtIndex(i); + if (cu == nullptr) + continue; + + SymbolFileDWARFDwo *dwo = cu->GetDwoSymbolFile(load_all_debug_info); + if (dwo) + debug_info_size += dwo->GetDebugInfoSize(); + } + return debug_info_size; +} + +void SymbolFileDWARF::FindTypes(const TypeQuery &query, TypeResults &results) { + + // Make sure we haven't already searched this SymbolFile before. + if (results.AlreadySearched(this)) + return; + + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + + bool have_index_match = false; + m_index->GetTypes(query.GetTypeBasename(), [&](DWARFDIE die) { + // Check the language, but only if we have a language filter. + if (query.HasLanguage()) { + if (!query.LanguageMatches(GetLanguageFamily(*die.GetCU()))) + return true; // Keep iterating over index types, language mismatch. + } + + // Check the context matches + std::vector<lldb_private::CompilerContext> die_context; + if (query.GetModuleSearch()) + die_context = die.GetDeclContext(); + else + die_context = die.GetTypeLookupContext(); + assert(!die_context.empty()); + if (!query.ContextMatches(die_context)) + return true; // Keep iterating over index types, context mismatch. + + // Try to resolve the type. + if (Type *matching_type = ResolveType(die, true, true)) { + if (matching_type->IsTemplateType()) { + // We have to watch out for case where we lookup a type by basename and + // it matches a template with simple template names. Like looking up + // "Foo" and if we have simple template names then we will match + // "Foo<int>" and "Foo<double>" because all the DWARF has is "Foo" in + // the accelerator tables. The main case we see this in is when the + // expression parser is trying to parse "Foo<int>" and it will first do + // a lookup on just "Foo". We verify the type basename matches before + // inserting the type in the results. + auto CompilerTypeBasename = + matching_type->GetForwardCompilerType().GetTypeName(true); + if (CompilerTypeBasename != query.GetTypeBasename()) + return true; // Keep iterating over index types, basename mismatch. + } + have_index_match = true; + results.InsertUnique(matching_type->shared_from_this()); + } + return !results.Done(query); // Keep iterating if we aren't done. + }); + + if (results.Done(query)) + return; + + // With -gsimple-template-names, a templated type's DW_AT_name will not + // contain the template parameters. Try again stripping '<' and anything + // after, filtering out entries with template parameters that don't match. + if (!have_index_match) { + // Create a type matcher with a compiler context that is tuned for + // -gsimple-template-names. We will use this for the index lookup and the + // context matching, but will use the original "match" to insert matches + // into if things match. The "match_simple" has a compiler context with + // all template parameters removed to allow the names and context to match. + // The UpdateCompilerContextForSimpleTemplateNames(...) will return true if + // it trims any context items down by removing template parameter names. + TypeQuery query_simple(query); + if (UpdateCompilerContextForSimpleTemplateNames(query_simple)) { + + // Copy our match's context and update the basename we are looking for + // so we can use this only to compare the context correctly. + m_index->GetTypes(query_simple.GetTypeBasename(), [&](DWARFDIE die) { + // Check the language, but only if we have a language filter. + if (query.HasLanguage()) { + if (!query.LanguageMatches(GetLanguageFamily(*die.GetCU()))) + return true; // Keep iterating over index types, language mismatch. + } + + // Check the context matches + std::vector<lldb_private::CompilerContext> die_context; + if (query.GetModuleSearch()) + die_context = die.GetDeclContext(); + else + die_context = die.GetTypeLookupContext(); + assert(!die_context.empty()); + if (!query_simple.ContextMatches(die_context)) + return true; // Keep iterating over index types, context mismatch. + + // Try to resolve the type. + if (Type *matching_type = ResolveType(die, true, true)) { + ConstString name = matching_type->GetQualifiedName(); + // We have found a type that still might not match due to template + // parameters. If we create a new TypeQuery that uses the new type's + // fully qualified name, we can find out if this type matches at all + // context levels. We can't use just the "match_simple" context + // because all template parameters were stripped off. The fully + // qualified name of the type will have the template parameters and + // will allow us to make sure it matches correctly. + TypeQuery die_query(name.GetStringRef(), + TypeQueryOptions::e_exact_match); + if (!query.ContextMatches(die_query.GetContextRef())) + return true; // Keep iterating over index types, context mismatch. + + results.InsertUnique(matching_type->shared_from_this()); + } + return !results.Done(query); // Keep iterating if we aren't done. + }); + if (results.Done(query)) + return; + } + } + + // Next search through the reachable Clang modules. This only applies for + // DWARF objects compiled with -gmodules that haven't been processed by + // dsymutil. + UpdateExternalModuleListIfNeeded(); + + for (const auto &pair : m_external_type_modules) { + if (ModuleSP external_module_sp = pair.second) { + external_module_sp->FindTypes(query, results); + if (results.Done(query)) + return; + } + } +} + +CompilerDeclContext +SymbolFileDWARF::FindNamespace(ConstString name, + const CompilerDeclContext &parent_decl_ctx, + bool only_root_namespaces) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + Log *log = GetLog(DWARFLog::Lookups); + + if (log) { + GetObjectFile()->GetModule()->LogMessage( + log, "SymbolFileDWARF::FindNamespace (sc, name=\"{0}\")", + name.GetCString()); + } + + CompilerDeclContext namespace_decl_ctx; + + if (!DeclContextMatchesThisSymbolFile(parent_decl_ctx)) + return namespace_decl_ctx; + + m_index->GetNamespaces(name, [&](DWARFDIE die) { + if (!DIEInDeclContext(parent_decl_ctx, die, only_root_namespaces)) + return true; // The containing decl contexts don't match + + DWARFASTParser *dwarf_ast = GetDWARFParser(*die.GetCU()); + if (!dwarf_ast) + return true; + + namespace_decl_ctx = dwarf_ast->GetDeclContextForUIDFromDWARF(die); + return !namespace_decl_ctx.IsValid(); + }); + + if (log && namespace_decl_ctx) { + GetObjectFile()->GetModule()->LogMessage( + log, + "SymbolFileDWARF::FindNamespace (sc, name=\"{0}\") => " + "CompilerDeclContext({1:p}/{2:p}) \"{3}\"", + 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) { + // Get the original shared pointer for this type + 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_objfile_sp) { + Symtab *symtab = m_objfile_sp->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; + + m_index->GetCompleteObjCClass( + type_name, must_be_implementation, [&](DWARFDIE type_die) { + // Don't try and resolve the DIE we are looking for with the DIE + // itself! + if (type_die == die || !IsStructOrClassTag(type_die.Tag())) + return true; + + if (must_be_implementation && + type_die.Supports_DW_AT_APPLE_objc_complete_type()) { + const bool try_resolving_type = type_die.GetAttributeValueAsUnsigned( + DW_AT_APPLE_objc_complete_type, 0); + if (!try_resolving_type) + return true; + } + + Type *resolved_type = ResolveType(type_die, false, true); + if (!resolved_type || resolved_type == DIE_IS_BEING_PARSED) + return true; + + DEBUG_PRINTF( + "resolved 0x%8.8" PRIx64 " from %s to 0x%8.8" PRIx64 + " (cu 0x%8.8" PRIx64 ")\n", + die.GetID(), + m_objfile_sp->GetFileSpec().GetFilename().AsCString("<Unknown>"), + type_die.GetID(), type_cu->GetID()); + + if (die) + GetDIEToType()[die.GetDIE()] = resolved_type; + type_sp = resolved_type->shared_from_this(); + return false; + }); + return type_sp; +} + +DWARFDIE +SymbolFileDWARF::FindDefinitionDIE(const DWARFDIE &die) { + const char *name = die.GetName(); + if (!name) + return {}; + if (!die.GetAttributeValueAsUnsigned(DW_AT_declaration, 0)) + return die; + + Progress progress(llvm::formatv( + "Searching definition DIE in {0}: '{1}'", + GetObjectFile()->GetFileSpec().GetFilename().GetString(), name)); + + const dw_tag_t tag = die.Tag(); + + Log *log = GetLog(DWARFLog::TypeCompletion | DWARFLog::Lookups); + if (log) { + GetObjectFile()->GetModule()->LogMessage( + log, + "SymbolFileDWARF::FindDefinitionDIE(tag={0} " + "({1}), name='{2}')", + DW_TAG_value_to_name(tag), tag, name); + } + + // 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 = GetLanguage(*die.GetCU()); + TypeSystemSP type_system = nullptr; + if (language != eLanguageTypeUnknown) { + auto type_system_or_err = GetTypeSystemForLanguage(language); + if (auto err = type_system_or_err.takeError()) { + LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), std::move(err), + "Cannot get TypeSystem for language {1}: {0}", + Language::GetNameForLanguageType(language)); + } else { + type_system = *type_system_or_err; + } + } + + // See comments below about -gsimple-template-names for why we attempt to + // compute missing template parameter names. + std::vector<std::string> template_params; + DWARFDeclContext die_dwarf_decl_ctx; + DWARFASTParser *dwarf_ast = + type_system ? type_system->GetDWARFParser() : nullptr; + for (DWARFDIE ctx_die = die; ctx_die && !isUnitType(ctx_die.Tag()); + ctx_die = ctx_die.GetParentDeclContextDIE()) { + die_dwarf_decl_ctx.AppendDeclContext(ctx_die.Tag(), ctx_die.GetName()); + template_params.push_back( + (ctx_die.IsStructUnionOrClass() && dwarf_ast) + ? dwarf_ast->GetDIEClassTemplateParams(ctx_die) + : ""); + } + const bool any_template_params = llvm::any_of( + template_params, [](llvm::StringRef p) { return !p.empty(); }); + + auto die_matches = [&](DWARFDIE type_die) { + // Resolve the type if both have the same tag or {class, struct} tags. + const bool tag_matches = + type_die.Tag() == tag || + (IsStructOrClassTag(type_die.Tag()) && IsStructOrClassTag(tag)); + if (!tag_matches) + return false; + if (any_template_params) { + size_t pos = 0; + for (DWARFDIE ctx_die = type_die; ctx_die && !isUnitType(ctx_die.Tag()) && + pos < template_params.size(); + ctx_die = ctx_die.GetParentDeclContextDIE(), ++pos) { + if (template_params[pos].empty()) + continue; + if (template_params[pos] != + dwarf_ast->GetDIEClassTemplateParams(ctx_die)) + return false; + } + if (pos != template_params.size()) + return false; + } + return true; + }; + DWARFDIE result; + m_index->GetFullyQualifiedType(die_dwarf_decl_ctx, [&](DWARFDIE type_die) { + // Make sure type_die's language 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(GetLanguage(*type_die.GetCU()))) + return true; + + if (!die_matches(type_die)) { + if (log) { + GetObjectFile()->GetModule()->LogMessage( + log, + "SymbolFileDWARF::FindDefinitionDIE(tag={0} ({1}), " + "name='{2}') ignoring die={3:x16} ({4})", + DW_TAG_value_to_name(tag), tag, name, type_die.GetOffset(), + type_die.GetName()); + } + return true; + } + + if (log) { + DWARFDeclContext type_dwarf_decl_ctx = type_die.GetDWARFDeclContext(); + GetObjectFile()->GetModule()->LogMessage( + log, + "SymbolFileDWARF::FindDefinitionTypeDIE(tag={0} ({1}), name='{2}') " + "trying die={3:x16} ({4})", + DW_TAG_value_to_name(tag), tag, name, type_die.GetOffset(), + type_dwarf_decl_ctx.GetQualifiedName()); + } + + result = type_die; + return false; + }); + return result; +} + +TypeSP SymbolFileDWARF::ParseType(const SymbolContext &sc, const DWARFDIE &die, + bool *type_is_new_ptr) { + if (!die) + return {}; + + auto type_system_or_err = GetTypeSystemForLanguage(GetLanguage(*die.GetCU())); + if (auto err = type_system_or_err.takeError()) { + LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), std::move(err), + "Unable to parse type: {0}"); + return {}; + } + auto ts = *type_system_or_err; + if (!ts) + return {}; + + DWARFASTParser *dwarf_ast = ts->GetDWARFParser(); + if (!dwarf_ast) + return {}; + + TypeSP type_sp = dwarf_ast->ParseTypeFromDWARF(sc, die, type_is_new_ptr); + if (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.GetDIERef()); + } + } + } + + 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) { + const dw_tag_t tag = die.Tag(); + bool type_is_new = false; + + Tag dwarf_tag = static_cast<Tag>(tag); + + // TODO: Currently ParseTypeFromDWARF(...) which is called by ParseType(...) + // does not handle DW_TAG_subrange_type. It is not clear if this is a bug or + // not. + if (isType(dwarf_tag) && tag != DW_TAG_subrange_type) + ParseType(sc, die, &type_is_new); + + 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) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + 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 = DIERef(func.GetID()).die_offset(); + DWARFDIE function_die = + dwarf_cu->GetNonSkeletonUnit().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) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + 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) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + if (sc.comp_unit != nullptr) { + if (sc.function) { + DWARFDIE function_die = GetDIE(sc.function->GetID()); + + dw_addr_t func_lo_pc = LLDB_INVALID_ADDRESS; + DWARFRangeList ranges = function_die.GetDIE()->GetAttributeAddressRanges( + function_die.GetCU(), /*check_hi_lo_pc=*/true); + if (!ranges.IsEmpty()) + func_lo_pc = ranges.GetMinRangeBase(0); + if (func_lo_pc != LLDB_INVALID_ADDRESS) { + const size_t num_variables = + ParseVariablesInFunctionContext(sc, function_die, func_lo_pc); + + // 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 = DebugInfo().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); + + m_index->GetGlobalVariables(*dwarf_cu, [&](DWARFDIE die) { + VariableSP var_sp(ParseVariableDIECached(sc, die)); + if (var_sp) { + variables->AddVariableIfUnique(var_sp); + ++vars_added; + } + return true; + }); + } + return vars_added; + } + } + return 0; +} + +VariableSP SymbolFileDWARF::ParseVariableDIECached(const SymbolContext &sc, + const DWARFDIE &die) { + if (!die) + return nullptr; + + DIEToVariableSP &die_to_variable = die.GetDWARF()->GetDIEToVariable(); + + VariableSP var_sp = die_to_variable[die.GetDIE()]; + if (var_sp) + return var_sp; + + var_sp = ParseVariableDIE(sc, die, LLDB_INVALID_ADDRESS); + if (var_sp) { + die_to_variable[die.GetDIE()] = var_sp; + if (DWARFDIE spec_die = die.GetReferencedDIE(DW_AT_specification)) + die_to_variable[spec_die.GetDIE()] = var_sp; + } + return var_sp; +} + +/// Creates a DWARFExpressionList from an DW_AT_location form_value. +static DWARFExpressionList GetExprListFromAtLocation(DWARFFormValue form_value, + ModuleSP module, + const DWARFDIE &die, + const addr_t func_low_pc) { + if (DWARFFormValue::IsBlockForm(form_value.Form())) { + const DWARFDataExtractor &data = die.GetData(); + + uint64_t block_offset = form_value.BlockData() - data.GetDataStart(); + uint64_t block_length = form_value.Unsigned(); + return DWARFExpressionList( + module, DataExtractor(data, block_offset, block_length), die.GetCU()); + } + + DWARFExpressionList location_list(module, DWARFExpression(), die.GetCU()); + DataExtractor data = die.GetCU()->GetLocationData(); + dw_offset_t offset = form_value.Unsigned(); + if (form_value.Form() == DW_FORM_loclistx) + offset = die.GetCU()->GetLoclistOffset(offset).value_or(-1); + if (data.ValidOffset(offset)) { + data = DataExtractor(data, offset, data.GetByteSize() - offset); + const DWARFUnit *dwarf_cu = form_value.GetUnit(); + if (DWARFExpression::ParseDWARFLocationList(dwarf_cu, data, &location_list)) + location_list.SetFuncFileAddress(func_low_pc); + } + + return location_list; +} + +/// Creates a DWARFExpressionList from an DW_AT_const_value. This is either a +/// block form, or a string, or a data form. For data forms, this returns an +/// empty list, as we cannot initialize it properly without a SymbolFileType. +static DWARFExpressionList +GetExprListFromAtConstValue(DWARFFormValue form_value, ModuleSP module, + const DWARFDIE &die) { + const DWARFDataExtractor &debug_info_data = die.GetData(); + if (DWARFFormValue::IsBlockForm(form_value.Form())) { + // Retrieve the value as a block expression. + uint64_t block_offset = + form_value.BlockData() - debug_info_data.GetDataStart(); + uint64_t block_length = form_value.Unsigned(); + return DWARFExpressionList( + module, DataExtractor(debug_info_data, block_offset, block_length), + die.GetCU()); + } + if (const char *str = form_value.AsCString()) + return DWARFExpressionList(module, + DataExtractor(str, strlen(str) + 1, + die.GetCU()->GetByteOrder(), + die.GetCU()->GetAddressByteSize()), + die.GetCU()); + return DWARFExpressionList(module, DWARFExpression(), die.GetCU()); +} + +/// Global variables that are not initialized may have their address set to +/// zero. Since multiple variables may have this address, we cannot apply the +/// OSO relink address approach we normally use. +/// However, the executable will have a matching symbol with a good address; +/// this function attempts to find the correct address by looking into the +/// executable's symbol table. If it succeeds, the expr_list is updated with +/// the new address and the executable's symbol is returned. +static Symbol *fixupExternalAddrZeroVariable( + SymbolFileDWARFDebugMap &debug_map_symfile, llvm::StringRef name, + DWARFExpressionList &expr_list, const DWARFDIE &die) { + ObjectFile *debug_map_objfile = debug_map_symfile.GetObjectFile(); + if (!debug_map_objfile) + return nullptr; + + Symtab *debug_map_symtab = debug_map_objfile->GetSymtab(); + if (!debug_map_symtab) + return nullptr; + Symbol *exe_symbol = debug_map_symtab->FindFirstSymbolWithNameAndType( + ConstString(name), eSymbolTypeData, Symtab::eDebugYes, + Symtab::eVisibilityExtern); + if (!exe_symbol || !exe_symbol->ValueIsAddress()) + return nullptr; + const addr_t exe_file_addr = exe_symbol->GetAddressRef().GetFileAddress(); + if (exe_file_addr == LLDB_INVALID_ADDRESS) + return nullptr; + + DWARFExpression *location = expr_list.GetMutableExpressionAtAddress(); + if (location->Update_DW_OP_addr(die.GetCU(), exe_file_addr)) + return exe_symbol; + return nullptr; +} + +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); + + if (!die) + return nullptr; + + 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)) + return nullptr; + + DWARFAttributes attributes = die.GetAttributes(); + const char *name = nullptr; + const char *mangled = nullptr; + Declaration decl; + DWARFFormValue type_die_form; + bool is_external = false; + bool is_artificial = false; + DWARFFormValue const_value_form, location_form; + Variable::RangeList scope_ranges; + + for (size_t i = 0; i < attributes.Size(); ++i) { + dw_attr_t attr = attributes.AttributeAtIndex(i); + DWARFFormValue form_value; + + if (!attributes.ExtractFormValueAtIndex(i, form_value)) + continue; + switch (attr) { + case DW_AT_decl_file: + decl.SetFile( + attributes.CompileUnitAtIndex(i)->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_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: + const_value_form = form_value; + break; + case DW_AT_location: + location_form = form_value; + break; + case DW_AT_start_scope: + // TODO: Implement this. + break; + case DW_AT_artificial: + is_artificial = form_value.Boolean(); + break; + case DW_AT_declaration: + case DW_AT_description: + case DW_AT_endianity: + case DW_AT_segment: + case DW_AT_specification: + case DW_AT_visibility: + default: + case DW_AT_abstract_origin: + case DW_AT_sibling: + break; + } + } + + // Prefer DW_AT_location over DW_AT_const_value. Both can be emitted e.g. + // for static constexpr member variables -- DW_AT_const_value and + // DW_AT_location will both be present in the DIE defining the member. + bool location_is_const_value_data = + const_value_form.IsValid() && !location_form.IsValid(); + + DWARFExpressionList location_list = [&] { + if (location_form.IsValid()) + return GetExprListFromAtLocation(location_form, module, die, func_low_pc); + if (const_value_form.IsValid()) + return GetExprListFromAtConstValue(const_value_form, module, die); + return DWARFExpressionList(module, DWARFExpression(), die.GetCU()); + }(); + + const DWARFDIE parent_context_die = GetDeclContextDIEContainingDIE(die); + const DWARFDIE sc_parent_die = GetParentSymbolContextDIE(die); + const dw_tag_t parent_tag = sc_parent_die.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; + 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(GetLanguage(*die.GetCU()))) + mangled = die.GetDWARFDeclContext() + .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 has_explicit_location = location_form.IsValid(); + bool is_static_lifetime = + has_explicit_mangled || + (has_explicit_location && !location_list.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; + const DWARFExpression* location = location_list.GetAlwaysValidExpr(); + if (location) + location_DW_OP_addr = + location->GetLocation_DW_OP_addr(location_form.GetUnit(), op_error); + if (op_error) { + StreamString strm; + location->DumpLocation(&strm, eDescriptionLevelFull, nullptr); + GetObjectFile()->GetModule()->ReportError( + "{0:x16}: {1} ({2}) has an invalid location: {3}", die.GetOffset(), + DW_TAG_value_to_name(die.Tag()), die.Tag(), 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 object file so the relocated address can be + // found there. + location_list.SetModule(debug_map_symfile->GetObjectFile()->GetModule()); + + if (is_static_lifetime) { + if (is_external) + scope = eValueTypeVariableGlobal; + else + scope = eValueTypeVariableStatic; + + if (debug_map_symfile) { + bool linked_oso_file_addr = false; + + if (is_external && location_DW_OP_addr == 0) { + if (Symbol *exe_symbol = fixupExternalAddrZeroVariable( + *debug_map_symfile, mangled ? mangled : name, location_list, + die)) { + 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 + DWARFExpression *location = + location_list.GetMutableExpressionAtAddress(); + location->Update_DW_OP_addr(die.GetCU(), exe_file_addr); + } else { + // Variable didn't make it into the final executable + return nullptr; + } + } + } + } else { + if (location_is_const_value_data && + die.GetDIE()->IsGlobalOrStaticScopeVariable()) + scope = eValueTypeVariableStatic; + else { + scope = eValueTypeVariableLocal; + if (debug_map_symfile) { + // We need to check for TLS addresses that we need to fixup + if (location_list.ContainsThreadLocalStorage()) { + location_list.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) { + // 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 nullptr; + } + + auto type_sp = std::make_shared<SymbolFileType>( + *this, type_die_form.Reference().GetID()); + + bool use_type_size_for_value = + location_is_const_value_data && + DWARFFormValue::IsDataForm(const_value_form.Form()); + if (use_type_size_for_value && type_sp->GetType()) { + DWARFExpression *location = location_list.GetMutableExpressionAtAddress(); + location->UpdateValue(const_value_form.Unsigned(), + type_sp->GetType()->GetByteSize(nullptr).value_or(0), + die.GetCU()->GetAddressByteSize()); + } + + return std::make_shared<Variable>( + die.GetID(), name, mangled, type_sp, scope, symbol_context_scope, + scope_ranges, &decl, location_list, is_external, is_artificial, + location_is_const_value_data, is_static_member); +} + +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(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; + default: + 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.children()) { + DWARFDIE result_die = + FindBlockContainingSpecification(child_die, spec_block_die_offset); + if (result_die) + return result_die; + } + } + + return DWARFDIE(); +} + +void SymbolFileDWARF::ParseAndAppendGlobalVariable( + const SymbolContext &sc, const DWARFDIE &die, + VariableList &cc_variable_list) { + if (!die) + return; + + dw_tag_t tag = die.Tag(); + if (tag != DW_TAG_variable && tag != DW_TAG_constant) + return; + + // Check to see if we have already parsed this variable or constant? + VariableSP var_sp = GetDIEToVariable()[die.GetDIE()]; + if (var_sp) { + cc_variable_list.AddVariableIfUnique(var_sp); + return; + } + + // We haven't parsed the variable yet, lets do that now. Also, let us include + // the variable in the relevant compilation unit's variable list, if it + // exists. + VariableListSP variable_list_sp; + DWARFDIE sc_parent_die = GetParentSymbolContextDIE(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); + } else { + GetObjectFile()->GetModule()->ReportError( + "parent {0:x8} {1} ({2}) with no valid compile unit in " + "symbol context for {3:x8} {4} ({5}).\n", + sc_parent_die.GetID(), DW_TAG_value_to_name(sc_parent_die.Tag()), + sc_parent_die.Tag(), die.GetID(), DW_TAG_value_to_name(die.Tag()), + die.Tag()); + return; + } + break; + + default: + GetObjectFile()->GetModule()->ReportError( + "didn't find appropriate parent DIE for variable list for {0:x8} " + "{1} ({2}).\n", + die.GetID(), DW_TAG_value_to_name(die.Tag()), die.Tag()); + return; + } + + var_sp = ParseVariableDIECached(sc, die); + if (!var_sp) + return; + + cc_variable_list.AddVariableIfUnique(var_sp); + if (variable_list_sp) + variable_list_sp->AddVariableIfUnique(var_sp); +} + +DIEArray +SymbolFileDWARF::MergeBlockAbstractParameters(const DWARFDIE &block_die, + DIEArray &&variable_dies) { + // DW_TAG_inline_subroutine objects may omit DW_TAG_formal_parameter in + // instances of the function when they are unused (i.e., the parameter's + // location list would be empty). The current DW_TAG_inline_subroutine may + // refer to another DW_TAG_subprogram that might actually have the definitions + // of the parameters and we need to include these so they show up in the + // variables for this function (for example, in a stack trace). Let us try to + // find the abstract subprogram that might contain the parameter definitions + // and merge with the concrete parameters. + + // Nothing to merge if the block is not an inlined function. + if (block_die.Tag() != DW_TAG_inlined_subroutine) { + return std::move(variable_dies); + } + + // Nothing to merge if the block does not have abstract parameters. + DWARFDIE abs_die = block_die.GetReferencedDIE(DW_AT_abstract_origin); + if (!abs_die || abs_die.Tag() != DW_TAG_subprogram || + !abs_die.HasChildren()) { + return std::move(variable_dies); + } + + // For each abstract parameter, if we have its concrete counterpart, insert + // it. Otherwise, insert the abstract parameter. + DIEArray::iterator concrete_it = variable_dies.begin(); + DWARFDIE abstract_child = abs_die.GetFirstChild(); + DIEArray merged; + bool did_merge_abstract = false; + for (; abstract_child; abstract_child = abstract_child.GetSibling()) { + if (abstract_child.Tag() == DW_TAG_formal_parameter) { + if (concrete_it == variable_dies.end() || + GetDIE(*concrete_it).Tag() != DW_TAG_formal_parameter) { + // We arrived at the end of the concrete parameter list, so all + // the remaining abstract parameters must have been omitted. + // Let us insert them to the merged list here. + merged.push_back(*abstract_child.GetDIERef()); + did_merge_abstract = true; + continue; + } + + DWARFDIE origin_of_concrete = + GetDIE(*concrete_it).GetReferencedDIE(DW_AT_abstract_origin); + if (origin_of_concrete == abstract_child) { + // The current abstract parameter is the origin of the current + // concrete parameter, just push the concrete parameter. + merged.push_back(*concrete_it); + ++concrete_it; + } else { + // Otherwise, the parameter must have been omitted from the concrete + // function, so insert the abstract one. + merged.push_back(*abstract_child.GetDIERef()); + did_merge_abstract = true; + } + } + } + + // Shortcut if no merging happened. + if (!did_merge_abstract) + return std::move(variable_dies); + + // We inserted all the abstract parameters (or their concrete counterparts). + // Let us insert all the remaining concrete variables to the merged list. + // During the insertion, let us check there are no remaining concrete + // formal parameters. If that's the case, then just bailout from the merge - + // the variable list is malformed. + for (; concrete_it != variable_dies.end(); ++concrete_it) { + if (GetDIE(*concrete_it).Tag() == DW_TAG_formal_parameter) { + return std::move(variable_dies); + } + merged.push_back(*concrete_it); + } + return merged; +} + +size_t SymbolFileDWARF::ParseVariablesInFunctionContext( + const SymbolContext &sc, const DWARFDIE &die, + const lldb::addr_t func_low_pc) { + if (!die || !sc.function) + return 0; + + DIEArray dummy_block_variables; // The recursive call should not add anything + // to this vector because |die| should be a + // subprogram, so all variables will be added + // to the subprogram's list. + return ParseVariablesInFunctionContextRecursive(sc, die, func_low_pc, + dummy_block_variables); +} + +// This method parses all the variables in the blocks in the subtree of |die|, +// and inserts them to the variable list for all the nested blocks. +// The uninserted variables for the current block are accumulated in +// |accumulator|. +size_t SymbolFileDWARF::ParseVariablesInFunctionContextRecursive( + const lldb_private::SymbolContext &sc, const DWARFDIE &die, + lldb::addr_t func_low_pc, DIEArray &accumulator) { + size_t vars_added = 0; + dw_tag_t tag = die.Tag(); + + if ((tag == DW_TAG_variable) || (tag == DW_TAG_constant) || + (tag == DW_TAG_formal_parameter)) { + accumulator.push_back(*die.GetDIERef()); + } + + switch (tag) { + case DW_TAG_subprogram: + case DW_TAG_inlined_subroutine: + case DW_TAG_lexical_block: { + // If we start a new block, compute a new block variable list and recurse. + Block *block = + sc.function->GetBlock(/*can_create=*/true).FindBlockByID(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()), die.GetOffset()); + if (concrete_block_die) + block = sc.function->GetBlock(/*can_create=*/true) + .FindBlockByID(concrete_block_die.GetID()); + } + + if (block == nullptr) + return 0; + + const bool can_create = false; + VariableListSP block_variable_list_sp = + block->GetBlockVariableList(can_create); + if (block_variable_list_sp.get() == nullptr) { + block_variable_list_sp = std::make_shared<VariableList>(); + block->SetVariableList(block_variable_list_sp); + } + + DIEArray block_variables; + for (DWARFDIE child = die.GetFirstChild(); child; + child = child.GetSibling()) { + vars_added += ParseVariablesInFunctionContextRecursive( + sc, child, func_low_pc, block_variables); + } + block_variables = + MergeBlockAbstractParameters(die, std::move(block_variables)); + vars_added += PopulateBlockVariableList(*block_variable_list_sp, sc, + block_variables, func_low_pc); + break; + } + + default: + // Recurse to children with the same variable accumulator. + for (DWARFDIE child = die.GetFirstChild(); child; + child = child.GetSibling()) { + vars_added += ParseVariablesInFunctionContextRecursive( + sc, child, func_low_pc, accumulator); + } + break; + } + + return vars_added; +} + +size_t SymbolFileDWARF::PopulateBlockVariableList( + VariableList &variable_list, const lldb_private::SymbolContext &sc, + llvm::ArrayRef<DIERef> variable_dies, lldb::addr_t func_low_pc) { + // Parse the variable DIEs and insert them to the list. + for (auto &die : variable_dies) { + if (VariableSP var_sp = ParseVariableDIE(sc, GetDIE(die), func_low_pc)) { + variable_list.AddVariableIfUnique(var_sp); + } + } + return variable_dies.size(); +} + +/// Collect call site parameters in a DW_TAG_call_site DIE. +static CallSiteParameterArray +CollectCallSiteParameters(ModuleSP module, DWARFDIE call_site_die) { + CallSiteParameterArray parameters; + for (DWARFDIE child : call_site_die.children()) { + if (child.Tag() != DW_TAG_call_site_parameter && + child.Tag() != DW_TAG_GNU_call_site_parameter) + continue; + + std::optional<DWARFExpressionList> LocationInCallee; + std::optional<DWARFExpressionList> LocationInCaller; + + DWARFAttributes attributes = child.GetAttributes(); + + // Parse the location at index \p attr_index within this call site parameter + // DIE, or return std::nullopt on failure. + auto parse_simple_location = + [&](int attr_index) -> std::optional<DWARFExpressionList> { + DWARFFormValue form_value; + if (!attributes.ExtractFormValueAtIndex(attr_index, form_value)) + return {}; + if (!DWARFFormValue::IsBlockForm(form_value.Form())) + return {}; + auto data = child.GetData(); + uint64_t block_offset = form_value.BlockData() - data.GetDataStart(); + uint64_t block_length = form_value.Unsigned(); + return DWARFExpressionList( + module, DataExtractor(data, block_offset, block_length), + child.GetCU()); + }; + + for (size_t i = 0; i < attributes.Size(); ++i) { + dw_attr_t attr = attributes.AttributeAtIndex(i); + if (attr == DW_AT_location) + LocationInCallee = parse_simple_location(i); + if (attr == DW_AT_call_value || attr == DW_AT_GNU_call_site_value) + LocationInCaller = parse_simple_location(i); + } + + if (LocationInCallee && LocationInCaller) { + CallSiteParameter param = {*LocationInCallee, *LocationInCaller}; + parameters.push_back(param); + } + } + return parameters; +} + +/// Collect call graph edges present in a function DIE. +std::vector<std::unique_ptr<lldb_private::CallEdge>> +SymbolFileDWARF::CollectCallEdges(ModuleSP module, 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. + bool has_call_edges = + function_die.GetAttributeValueAsUnsigned(DW_AT_call_all_calls, 0) || + function_die.GetAttributeValueAsUnsigned(DW_AT_GNU_all_call_sites, 0); + if (!has_call_edges) + return {}; + + Log *log = GetLog(LLDBLog::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<std::unique_ptr<CallEdge>> call_edges; + for (DWARFDIE child : function_die.children()) { + if (child.Tag() != DW_TAG_call_site && child.Tag() != DW_TAG_GNU_call_site) + continue; + + std::optional<DWARFDIE> call_origin; + std::optional<DWARFExpressionList> call_target; + addr_t return_pc = LLDB_INVALID_ADDRESS; + addr_t call_inst_pc = LLDB_INVALID_ADDRESS; + addr_t low_pc = LLDB_INVALID_ADDRESS; + bool tail_call = false; + + // Second DW_AT_low_pc may come from DW_TAG_subprogram referenced by + // DW_TAG_GNU_call_site's DW_AT_abstract_origin overwriting our 'low_pc'. + // So do not inherit attributes from DW_AT_abstract_origin. + DWARFAttributes attributes = child.GetAttributes(DWARFDIE::Recurse::no); + for (size_t i = 0; i < attributes.Size(); ++i) { + DWARFFormValue form_value; + if (!attributes.ExtractFormValueAtIndex(i, form_value)) { + LLDB_LOG(log, "CollectCallEdges: Could not extract TAG_call_site form"); + break; + } + + dw_attr_t attr = attributes.AttributeAtIndex(i); + + if (attr == DW_AT_call_tail_call || attr == DW_AT_GNU_tail_call) + tail_call = form_value.Boolean(); + + // Extract DW_AT_call_origin (the call target's DIE). + if (attr == DW_AT_call_origin || attr == DW_AT_abstract_origin) { + call_origin = form_value.Reference(); + if (!call_origin->IsValid()) { + LLDB_LOG(log, "CollectCallEdges: Invalid call origin in {0}", + function_die.GetPubname()); + break; + } + } + + if (attr == DW_AT_low_pc) + low_pc = form_value.Address(); + + // 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. + if (attr == DW_AT_call_return_pc) + return_pc = form_value.Address(); + + // Extract DW_AT_call_pc (the PC at the call/branch instruction). It + // should only ever be unavailable for non-tail calls, in which case use + // LLDB_INVALID_ADDRESS. + if (attr == DW_AT_call_pc) + call_inst_pc = form_value.Address(); + + // Extract DW_AT_call_target (the location of the address of the indirect + // call). + if (attr == DW_AT_call_target || attr == DW_AT_GNU_call_site_target) { + if (!DWARFFormValue::IsBlockForm(form_value.Form())) { + LLDB_LOG(log, + "CollectCallEdges: AT_call_target does not have block form"); + break; + } + + auto data = child.GetData(); + uint64_t block_offset = form_value.BlockData() - data.GetDataStart(); + uint64_t block_length = form_value.Unsigned(); + call_target = DWARFExpressionList( + module, DataExtractor(data, block_offset, block_length), + child.GetCU()); + } + } + if (!call_origin && !call_target) { + LLDB_LOG(log, "CollectCallEdges: call site without any call target"); + continue; + } + + addr_t caller_address; + CallEdge::AddrType caller_address_type; + if (return_pc != LLDB_INVALID_ADDRESS) { + caller_address = return_pc; + caller_address_type = CallEdge::AddrType::AfterCall; + } else if (low_pc != LLDB_INVALID_ADDRESS) { + caller_address = low_pc; + caller_address_type = CallEdge::AddrType::AfterCall; + } else if (call_inst_pc != LLDB_INVALID_ADDRESS) { + caller_address = call_inst_pc; + caller_address_type = CallEdge::AddrType::Call; + } else { + LLDB_LOG(log, "CollectCallEdges: No caller address"); + continue; + } + // Adjust any PC forms. It needs to be fixed up if the main executable + // contains a debug map (i.e. pointers to object files), because we need a + // file address relative to the executable's text section. + caller_address = FixupAddress(caller_address); + + // Extract call site parameters. + CallSiteParameterArray parameters = + CollectCallSiteParameters(module, child); + + std::unique_ptr<CallEdge> edge; + if (call_origin) { + LLDB_LOG(log, + "CollectCallEdges: Found call origin: {0} (retn-PC: {1:x}) " + "(call-PC: {2:x})", + call_origin->GetPubname(), return_pc, call_inst_pc); + edge = std::make_unique<DirectCallEdge>( + call_origin->GetMangledName(), caller_address_type, caller_address, + tail_call, std::move(parameters)); + } else { + if (log) { + StreamString call_target_desc; + call_target->GetDescription(&call_target_desc, eDescriptionLevelBrief, + nullptr); + LLDB_LOG(log, "CollectCallEdges: Found indirect call target: {0}", + call_target_desc.GetString()); + } + edge = std::make_unique<IndirectCallEdge>( + *call_target, caller_address_type, caller_address, tail_call, + std::move(parameters)); + } + + if (log && parameters.size()) { + for (const CallSiteParameter ¶m : parameters) { + StreamString callee_loc_desc, caller_loc_desc; + param.LocationInCallee.GetDescription(&callee_loc_desc, + eDescriptionLevelBrief, nullptr); + param.LocationInCaller.GetDescription(&caller_loc_desc, + eDescriptionLevelBrief, nullptr); + LLDB_LOG(log, "CollectCallEdges: \tparam: {0} => {1}", + callee_loc_desc.GetString(), caller_loc_desc.GetString()); + } + } + + call_edges.push_back(std::move(edge)); + } + return call_edges; +} + +std::vector<std::unique_ptr<lldb_private::CallEdge>> +SymbolFileDWARF::ParseCallEdgesInFunction(lldb_private::UserID func_id) { + // ParseCallEdgesInFunction must be called at the behest of an exclusively + // locked lldb::Function instance. Storage for parsed call edges is owned by + // the lldb::Function instance: locking at the SymbolFile level would be too + // late, because the act of storing results from ParseCallEdgesInFunction + // would be racy. + DWARFDIE func_die = GetDIE(func_id.GetID()); + if (func_die.IsValid()) + return CollectCallEdges(GetObjectFile()->GetModule(), func_die); + return {}; +} + +void SymbolFileDWARF::Dump(lldb_private::Stream &s) { + SymbolFileCommon::Dump(s); + m_index->Dump(s); +} + +void SymbolFileDWARF::DumpClangAST(Stream &s) { + auto ts_or_err = GetTypeSystemForLanguage(eLanguageTypeC_plus_plus); + if (!ts_or_err) + return; + auto ts = *ts_or_err; + TypeSystemClang *clang = llvm::dyn_cast_or_null<TypeSystemClang>(ts.get()); + if (!clang) + return; + clang->Dump(s.AsRawOstream()); +} + +bool SymbolFileDWARF::GetSeparateDebugInfo(StructuredData::Dictionary &d, + bool errors_only) { + StructuredData::Array separate_debug_info_files; + DWARFDebugInfo &info = DebugInfo(); + const size_t num_cus = info.GetNumUnits(); + for (size_t cu_idx = 0; cu_idx < num_cus; cu_idx++) { + DWARFUnit *unit = info.GetUnitAtIndex(cu_idx); + DWARFCompileUnit *dwarf_cu = llvm::dyn_cast<DWARFCompileUnit>(unit); + if (dwarf_cu == nullptr) + continue; + + // Check if this is a DWO unit by checking if it has a DWO ID. + // NOTE: it seems that `DWARFUnit::IsDWOUnit` is always false? + if (!dwarf_cu->GetDWOId().has_value()) + continue; + + StructuredData::DictionarySP dwo_data = + std::make_shared<StructuredData::Dictionary>(); + const uint64_t dwo_id = dwarf_cu->GetDWOId().value(); + dwo_data->AddIntegerItem("dwo_id", dwo_id); + + if (const DWARFBaseDIE die = dwarf_cu->GetUnitDIEOnly()) { + const char *dwo_name = GetDWOName(*dwarf_cu, *die.GetDIE()); + if (dwo_name) { + dwo_data->AddStringItem("dwo_name", dwo_name); + } else { + dwo_data->AddStringItem("error", "missing dwo name"); + } + + const char *comp_dir = die.GetDIE()->GetAttributeValueAsString( + dwarf_cu, DW_AT_comp_dir, nullptr); + if (comp_dir) { + dwo_data->AddStringItem("comp_dir", comp_dir); + } + } else { + dwo_data->AddStringItem( + "error", + llvm::formatv("unable to get unit DIE for DWARFUnit at {0:x}", + dwarf_cu->GetOffset()) + .str()); + } + + // If we have a DWO symbol file, that means we were able to successfully + // load it. + SymbolFile *dwo_symfile = dwarf_cu->GetDwoSymbolFile(); + if (dwo_symfile) { + dwo_data->AddStringItem( + "resolved_dwo_path", + dwo_symfile->GetObjectFile()->GetFileSpec().GetPath()); + } else { + dwo_data->AddStringItem("error", + dwarf_cu->GetDwoError().AsCString("unknown")); + } + dwo_data->AddBooleanItem("loaded", dwo_symfile != nullptr); + if (!errors_only || dwo_data->HasKey("error")) + separate_debug_info_files.AddItem(dwo_data); + } + + d.AddStringItem("type", "dwo"); + d.AddStringItem("symfile", GetMainObjectFile()->GetFileSpec().GetPath()); + d.AddItem("separate-debug-info-files", + std::make_shared<StructuredData::Array>( + std::move(separate_debug_info_files))); + return true; +} + +SymbolFileDWARFDebugMap *SymbolFileDWARF::GetDebugMapSymfile() { + if (m_debug_map_symfile == nullptr) { + lldb::ModuleSP module_sp(m_debug_map_module_wp.lock()); + if (module_sp) { + m_debug_map_symfile = llvm::cast<SymbolFileDWARFDebugMap>( + module_sp->GetSymbolFile()->GetBackingSymbolFile()); + } + } + return m_debug_map_symfile; +} + +const std::shared_ptr<SymbolFileDWARFDwo> &SymbolFileDWARF::GetDwpSymbolFile() { + llvm::call_once(m_dwp_symfile_once_flag, [this]() { + // Create a list of files to try and append .dwp to. + FileSpecList symfiles; + // Append the module's object file path. + const FileSpec module_fspec = m_objfile_sp->GetModule()->GetFileSpec(); + symfiles.Append(module_fspec); + // Append the object file for this SymbolFile only if it is different from + // the module's file path. Our main module could be "a.out", our symbol file + // could be "a.debug" and our ".dwp" file might be "a.debug.dwp" instead of + // "a.out.dwp". + const FileSpec symfile_fspec(m_objfile_sp->GetFileSpec()); + if (symfile_fspec != module_fspec) { + symfiles.Append(symfile_fspec); + } else { + // If we don't have a separate debug info file, then try stripping the + // extension. The main module could be "a.debug" and the .dwp file could + // be "a.dwp" instead of "a.debug.dwp". + ConstString filename_no_ext = + module_fspec.GetFileNameStrippingExtension(); + if (filename_no_ext != module_fspec.GetFilename()) { + FileSpec module_spec_no_ext(module_fspec); + module_spec_no_ext.SetFilename(filename_no_ext); + symfiles.Append(module_spec_no_ext); + } + } + Log *log = GetLog(DWARFLog::SplitDwarf); + FileSpecList search_paths = Target::GetDefaultDebugFileSearchPaths(); + ModuleSpec module_spec; + module_spec.GetFileSpec() = m_objfile_sp->GetFileSpec(); + for (const auto &symfile : symfiles.files()) { + module_spec.GetSymbolFileSpec() = + FileSpec(symfile.GetPath() + ".dwp", symfile.GetPathStyle()); + LLDB_LOG(log, "Searching for DWP using: \"{0}\"", + module_spec.GetSymbolFileSpec()); + FileSpec dwp_filespec = + PluginManager::LocateExecutableSymbolFile(module_spec, search_paths); + if (FileSystem::Instance().Exists(dwp_filespec)) { + LLDB_LOG(log, "Found DWP file: \"{0}\"", dwp_filespec); + DataBufferSP dwp_file_data_sp; + lldb::offset_t dwp_file_data_offset = 0; + ObjectFileSP dwp_obj_file = ObjectFile::FindPlugin( + GetObjectFile()->GetModule(), &dwp_filespec, 0, + FileSystem::Instance().GetByteSize(dwp_filespec), dwp_file_data_sp, + dwp_file_data_offset); + if (dwp_obj_file) { + m_dwp_symfile = std::make_shared<SymbolFileDWARFDwo>( + *this, dwp_obj_file, DIERef::k_file_index_mask); + break; + } + } + } + if (!m_dwp_symfile) { + LLDB_LOG(log, "Unable to locate for DWP file for: \"{0}\"", + m_objfile_sp->GetModule()->GetFileSpec()); + } + }); + return m_dwp_symfile; +} + +llvm::Expected<lldb::TypeSystemSP> +SymbolFileDWARF::GetTypeSystem(DWARFUnit &unit) { + return unit.GetSymbolFileDWARF().GetTypeSystemForLanguage(GetLanguage(unit)); +} + +DWARFASTParser *SymbolFileDWARF::GetDWARFParser(DWARFUnit &unit) { + auto type_system_or_err = GetTypeSystem(unit); + if (auto err = type_system_or_err.takeError()) { + LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), std::move(err), + "Unable to get DWARFASTParser: {0}"); + return nullptr; + } + if (auto ts = *type_system_or_err) + return ts->GetDWARFParser(); + return nullptr; +} + +CompilerDecl SymbolFileDWARF::GetDecl(const DWARFDIE &die) { + if (DWARFASTParser *dwarf_ast = GetDWARFParser(*die.GetCU())) + return dwarf_ast->GetDeclForUIDFromDWARF(die); + return CompilerDecl(); +} + +CompilerDeclContext SymbolFileDWARF::GetDeclContext(const DWARFDIE &die) { + if (DWARFASTParser *dwarf_ast = GetDWARFParser(*die.GetCU())) + return dwarf_ast->GetDeclContextForUIDFromDWARF(die); + return CompilerDeclContext(); +} + +CompilerDeclContext +SymbolFileDWARF::GetContainingDeclContext(const DWARFDIE &die) { + if (DWARFASTParser *dwarf_ast = GetDWARFParser(*die.GetCU())) + return dwarf_ast->GetDeclContextContainingUIDFromDWARF(die); + return CompilerDeclContext(); +} + +LanguageType SymbolFileDWARF::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; + default: + return static_cast<LanguageType>(val); + } +} + +LanguageType SymbolFileDWARF::GetLanguage(DWARFUnit &unit) { + return LanguageTypeFromDWARF(unit.GetDWARFLanguageType()); +} + +LanguageType SymbolFileDWARF::GetLanguageFamily(DWARFUnit &unit) { + auto lang = (llvm::dwarf::SourceLanguage)unit.GetDWARFLanguageType(); + if (llvm::dwarf::isCPlusPlus(lang)) + lang = DW_LANG_C_plus_plus; + return LanguageTypeFromDWARF(lang); +} + +StatsDuration::Duration SymbolFileDWARF::GetDebugInfoIndexTime() { + if (m_index) + return m_index->GetIndexTime(); + return {}; +} + +Status SymbolFileDWARF::CalculateFrameVariableError(StackFrame &frame) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + CompileUnit *cu = frame.GetSymbolContext(eSymbolContextCompUnit).comp_unit; + if (!cu) + return Status(); + + DWARFCompileUnit *dwarf_cu = GetDWARFCompileUnit(cu); + if (!dwarf_cu) + return Status(); + + // Check if we have a skeleton compile unit that had issues trying to load + // its .dwo/.dwp file. First pares the Unit DIE to make sure we see any .dwo + // related errors. + dwarf_cu->ExtractUnitDIEIfNeeded(); + const Status &dwo_error = dwarf_cu->GetDwoError(); + if (dwo_error.Fail()) + return dwo_error; + + // Don't return an error for assembly files as they typically don't have + // varaible information. + if (dwarf_cu->GetDWARFLanguageType() == DW_LANG_Mips_Assembler) + return Status(); + + // Check if this compile unit has any variable DIEs. If it doesn't then there + // is not variable information for the entire compile unit. + if (dwarf_cu->HasAny({DW_TAG_variable, DW_TAG_formal_parameter})) + return Status(); + + return Status("no variable information is available in debug info for this " + "compile unit"); +} + +void SymbolFileDWARF::GetCompileOptions( + std::unordered_map<lldb::CompUnitSP, lldb_private::Args> &args) { + + const uint32_t num_compile_units = GetNumCompileUnits(); + + for (uint32_t cu_idx = 0; cu_idx < num_compile_units; ++cu_idx) { + lldb::CompUnitSP comp_unit = GetCompileUnitAtIndex(cu_idx); + if (!comp_unit) + continue; + + DWARFUnit *dwarf_cu = GetDWARFCompileUnit(comp_unit.get()); + if (!dwarf_cu) + continue; + + const DWARFBaseDIE die = dwarf_cu->GetUnitDIEOnly(); + if (!die) + continue; + + const char *flags = die.GetAttributeValueAsString(DW_AT_APPLE_flags, NULL); + + if (!flags) + continue; + args.insert({comp_unit, Args(flags)}); + } +} 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..4967b37d753a --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h @@ -0,0 +1,565 @@ +//===-- 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 LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_SYMBOLFILEDWARF_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_SYMBOLFILEDWARF_H + +#include <list> +#include <map> +#include <mutex> +#include <optional> +#include <unordered_map> +#include <vector> + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SetVector.h" +#include "llvm/Support/Threading.h" + +#include "lldb/Core/UniqueCStringMap.h" +#include "lldb/Core/dwarf.h" +#include "lldb/Expression/DWARFExpressionList.h" +#include "lldb/Symbol/DebugMacros.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/SymbolFile.h" +#include "lldb/Target/Statistics.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/Utility/Flags.h" +#include "lldb/Utility/RangeMap.h" +#include "lldb/Utility/StructuredData.h" +#include "lldb/lldb-private.h" + +#include "DWARFContext.h" +#include "DWARFDataExtractor.h" +#include "DWARFDefines.h" +#include "DWARFIndex.h" +#include "UniqueDWARFASTType.h" + +class DWARFASTParserClang; + +namespace llvm { +class DWARFDebugAbbrev; +} // namespace llvm + +namespace lldb_private::plugin { +namespace dwarf { +// Forward Declarations for this DWARF plugin +class DebugMapModule; +class DWARFCompileUnit; +class DWARFDebugAranges; +class DWARFDebugInfo; +class DWARFDebugInfoEntry; +class DWARFDebugLine; +class DWARFDebugRanges; +class DWARFDeclContext; +class DWARFFormValue; +class DWARFTypeUnit; +class SymbolFileDWARFDebugMap; +class SymbolFileDWARFDwo; +class SymbolFileDWARFDwp; + +#define DIE_IS_BEING_PARSED ((lldb_private::Type *)1) + +class SymbolFileDWARF : public SymbolFileCommon { + /// LLVM RTTI support. + static char ID; + +public: + /// LLVM RTTI support. + /// \{ + bool isA(const void *ClassID) const override { + return ClassID == &ID || SymbolFileCommon::isA(ClassID); + } + static bool classof(const SymbolFile *obj) { return obj->isA(&ID); } + /// \} + + friend class SymbolFileDWARFDebugMap; + friend class SymbolFileDWARFDwo; + friend class DebugMapModule; + friend class DWARFCompileUnit; + friend class DWARFDIE; + friend class DWARFASTParser; + + // Static Functions + static void Initialize(); + + static void Terminate(); + + static void DebuggerInitialize(Debugger &debugger); + + static llvm::StringRef GetPluginNameStatic() { return "dwarf"; } + + static llvm::StringRef GetPluginDescriptionStatic(); + + static SymbolFile *CreateInstance(lldb::ObjectFileSP objfile_sp); + + // Constructors and Destructors + + SymbolFileDWARF(lldb::ObjectFileSP objfile_sp, SectionList *dwo_section_list); + + ~SymbolFileDWARF() override; + + uint32_t CalculateAbilities() override; + + void InitializeObject() override; + + // Compile Unit function calls + + lldb::LanguageType ParseLanguage(CompileUnit &comp_unit) override; + + XcodeSDK ParseXcodeSDK(CompileUnit &comp_unit) override; + + size_t ParseFunctions(CompileUnit &comp_unit) override; + + bool ParseLineTable(CompileUnit &comp_unit) override; + + bool ParseDebugMacros(CompileUnit &comp_unit) override; + + bool ForEachExternalModule(CompileUnit &, llvm::DenseSet<SymbolFile *> &, + llvm::function_ref<bool(Module &)>) override; + + bool ParseSupportFiles(CompileUnit &comp_unit, + SupportFileList &support_files) override; + + bool ParseIsOptimized(CompileUnit &comp_unit) override; + + size_t ParseTypes(CompileUnit &comp_unit) override; + + bool + ParseImportedModules(const SymbolContext &sc, + std::vector<SourceModule> &imported_modules) override; + + size_t ParseBlocksRecursive(Function &func) override; + + size_t ParseVariablesForContext(const SymbolContext &sc) override; + + std::optional<ArrayInfo> + GetDynamicArrayInfoForUID(lldb::user_id_t type_uid, + const ExecutionContext *exe_ctx) override; + + bool CompleteType(CompilerType &compiler_type) override; + + Type *ResolveType(const DWARFDIE &die, bool assert_not_being_parsed = true, + bool resolve_function_context = false); + + CompilerDecl GetDeclForUID(lldb::user_id_t uid) override; + + CompilerDeclContext GetDeclContextForUID(lldb::user_id_t uid) override; + + CompilerDeclContext GetDeclContextContainingUID(lldb::user_id_t uid) override; + + std::vector<CompilerContext> + GetCompilerContextForUID(lldb::user_id_t uid) override; + + void ParseDeclsForContext(CompilerDeclContext decl_ctx) override; + + uint32_t ResolveSymbolContext(const Address &so_addr, + lldb::SymbolContextItem resolve_scope, + SymbolContext &sc) override; + + Status CalculateFrameVariableError(StackFrame &frame) override; + + uint32_t ResolveSymbolContext(const SourceLocationSpec &src_location_spec, + lldb::SymbolContextItem resolve_scope, + SymbolContextList &sc_list) override; + + void FindGlobalVariables(ConstString name, + const CompilerDeclContext &parent_decl_ctx, + uint32_t max_matches, + VariableList &variables) override; + + void FindGlobalVariables(const RegularExpression ®ex, uint32_t max_matches, + VariableList &variables) override; + + void FindFunctions(const Module::LookupInfo &lookup_info, + const CompilerDeclContext &parent_decl_ctx, + bool include_inlines, SymbolContextList &sc_list) override; + + void FindFunctions(const RegularExpression ®ex, bool include_inlines, + SymbolContextList &sc_list) override; + + void + GetMangledNamesForFunction(const std::string &scope_qualified_name, + std::vector<ConstString> &mangled_names) override; + + uint64_t GetDebugInfoSize(bool load_all_debug_info = false) override; + + void FindTypes(const lldb_private::TypeQuery &match, + lldb_private::TypeResults &results) override; + + void GetTypes(SymbolContextScope *sc_scope, lldb::TypeClass type_mask, + TypeList &type_list) override; + + llvm::Expected<lldb::TypeSystemSP> + GetTypeSystemForLanguage(lldb::LanguageType language) override; + + CompilerDeclContext FindNamespace(ConstString name, + const CompilerDeclContext &parent_decl_ctx, + bool only_root_namespaces) override; + + void PreloadSymbols() override; + + std::recursive_mutex &GetModuleMutex() const override; + + // PluginInterface protocol + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } + + llvm::DWARFDebugAbbrev *DebugAbbrev(); + + DWARFDebugInfo &DebugInfo(); + + DWARFDebugRanges *GetDebugRanges(); + + static bool SupportedVersion(uint16_t version); + + DWARFDIE + GetDeclContextDIEContainingDIE(const DWARFDIE &die); + + bool HasForwardDeclForCompilerType(const CompilerType &compiler_type); + + CompileUnit *GetCompUnitForDWARFCompUnit(DWARFCompileUnit &dwarf_cu); + + virtual void GetObjCMethods(ConstString class_name, + llvm::function_ref<bool(DWARFDIE die)> callback); + + bool Supports_DW_AT_APPLE_objc_complete_type(DWARFUnit *cu); + + DebugMacrosSP ParseDebugMacros(lldb::offset_t *offset); + + static DWARFDIE GetParentSymbolContextDIE(const DWARFDIE &die); + + lldb::ModuleSP GetExternalModule(ConstString name); + + typedef std::map<ConstString, lldb::ModuleSP> ExternalTypeModuleMap; + + /// Return the list of Clang modules imported by this SymbolFile. + const ExternalTypeModuleMap &getExternalTypeModules() const { + return m_external_type_modules; + } + + /// Given a DIERef, find the correct SymbolFileDWARF. + /// + /// A DIERef contains a file index that can uniquely identify a N_OSO file for + /// DWARF in .o files on mac, or a .dwo or .dwp file index for split DWARF. + /// Calling this function will find the correct symbol file to use so that + /// further lookups can be done on the correct symbol file so that the DIE + /// offset makes sense in the DIERef. + virtual SymbolFileDWARF *GetDIERefSymbolFile(const DIERef &die_ref); + + virtual DWARFDIE GetDIE(const DIERef &die_ref); + + DWARFDIE GetDIE(lldb::user_id_t uid); + + std::shared_ptr<SymbolFileDWARFDwo> + GetDwoSymbolFileForCompileUnit(DWARFUnit &dwarf_cu, + const DWARFDebugInfoEntry &cu_die); + + /// If this is a DWARF object with a single CU, return its DW_AT_dwo_id. + std::optional<uint64_t> GetDWOId(); + + /// Given a DWO DWARFUnit, find the corresponding skeleton DWARFUnit + /// in the main symbol file. DWP files can have their DWARFUnits + /// parsed without the skeleton compile units having been parsed, so + /// sometimes we need to find the skeleton compile unit for a DWO + /// DWARFUnit so we can fill in this link. Currently unless the + /// skeleton compile unit has been parsed _and_ the Unit DIE has been + /// parsed, the DWO unit will not have a backward link setup correctly + /// which was causing crashes due to an assertion that was firing + /// in SymbolFileDWARF::GetCompUnitForDWARFCompUnit(). + DWARFUnit *GetSkeletonUnit(DWARFUnit *dwo_unit); + + static bool DIEInDeclContext(const CompilerDeclContext &parent_decl_ctx, + const DWARFDIE &die, + bool only_root_namespaces = false); + + std::vector<std::unique_ptr<CallEdge>> + ParseCallEdgesInFunction(UserID func_id) override; + + void Dump(Stream &s) override; + + void DumpClangAST(Stream &s) override; + + /// List separate dwo files. + bool GetSeparateDebugInfo(StructuredData::Dictionary &d, + bool errors_only) override; + + DWARFContext &GetDWARFContext() { return m_context; } + + const std::shared_ptr<SymbolFileDWARFDwo> &GetDwpSymbolFile(); + + FileSpec GetFile(DWARFUnit &unit, size_t file_idx); + + static llvm::Expected<lldb::TypeSystemSP> GetTypeSystem(DWARFUnit &unit); + + static DWARFASTParser *GetDWARFParser(DWARFUnit &unit); + + // CompilerDecl related functions + + static CompilerDecl GetDecl(const DWARFDIE &die); + + static CompilerDeclContext GetDeclContext(const DWARFDIE &die); + + static CompilerDeclContext GetContainingDeclContext(const DWARFDIE &die); + + static lldb::LanguageType LanguageTypeFromDWARF(uint64_t val); + + static lldb::LanguageType GetLanguage(DWARFUnit &unit); + /// Same as GetLanguage() but reports all C++ versions as C++ (no version). + static lldb::LanguageType GetLanguageFamily(DWARFUnit &unit); + + StatsDuration::Duration GetDebugInfoParseTime() override { + return m_parse_time; + } + StatsDuration::Duration GetDebugInfoIndexTime() override; + + StatsDuration &GetDebugInfoParseTimeRef() { return m_parse_time; } + + virtual lldb::offset_t + GetVendorDWARFOpcodeSize(const DataExtractor &data, + const lldb::offset_t data_offset, + const uint8_t op) const { + return LLDB_INVALID_OFFSET; + } + + virtual bool ParseVendorDWARFOpcode(uint8_t op, const DataExtractor &opcodes, + lldb::offset_t &offset, + std::vector<Value> &stack) const { + return false; + } + + ConstString ConstructFunctionDemangledName(const DWARFDIE &die); + + std::optional<uint64_t> GetFileIndex() const { return m_file_index; } + void SetFileIndex(std::optional<uint64_t> file_index) { + m_file_index = file_index; + } + + typedef llvm::DenseMap<const DWARFDebugInfoEntry *, Type *> DIEToTypePtr; + + virtual DIEToTypePtr &GetDIEToType() { return m_die_to_type; } + + virtual llvm::DenseMap<lldb::opaque_compiler_type_t, DIERef> & + GetForwardDeclCompilerTypeToDIE(); + + typedef llvm::DenseMap<const DWARFDebugInfoEntry *, lldb::VariableSP> + DIEToVariableSP; + + virtual DIEToVariableSP &GetDIEToVariable() { return m_die_to_variable_sp; } + + virtual UniqueDWARFASTTypeMap &GetUniqueDWARFASTTypeMap(); + + bool ClassOrStructIsVirtual(const DWARFDIE &die); + + SymbolFileDWARFDebugMap *GetDebugMapSymfile(); + + virtual DWARFDIE FindDefinitionDIE(const DWARFDIE &die); + + virtual lldb::TypeSP FindCompleteObjCDefinitionTypeForDIE( + const DWARFDIE &die, ConstString type_name, bool must_be_implementation); + + Type *ResolveTypeUID(lldb::user_id_t type_uid) override; + + Type *ResolveTypeUID(const DWARFDIE &die, bool assert_not_being_parsed); + + Type *ResolveTypeUID(const DIERef &die_ref); + + /// Returns the DWARFIndex for this symbol, if it exists. + DWARFIndex *getIndex() { return m_index.get(); } + +protected: + SymbolFileDWARF(const SymbolFileDWARF &) = delete; + const SymbolFileDWARF &operator=(const SymbolFileDWARF &) = delete; + + virtual void LoadSectionData(lldb::SectionType sect_type, + DWARFDataExtractor &data); + + bool DeclContextMatchesThisSymbolFile(const CompilerDeclContext &decl_ctx); + + uint32_t CalculateNumCompileUnits() override; + + lldb::CompUnitSP ParseCompileUnitAtIndex(uint32_t index) override; + + TypeList &GetTypeList() override; + + lldb::CompUnitSP ParseCompileUnit(DWARFCompileUnit &dwarf_cu); + + virtual DWARFCompileUnit *GetDWARFCompileUnit(CompileUnit *comp_unit); + + DWARFUnit *GetNextUnparsedDWARFCompileUnit(DWARFUnit *prev_cu); + + bool GetFunction(const DWARFDIE &die, SymbolContext &sc); + + Function *ParseFunction(CompileUnit &comp_unit, const DWARFDIE &die); + + size_t ParseBlocksRecursive(CompileUnit &comp_unit, Block *parent_block, + const DWARFDIE &die, + lldb::addr_t subprogram_low_pc, uint32_t depth); + + size_t ParseTypes(const SymbolContext &sc, const DWARFDIE &die, + bool parse_siblings, bool parse_children); + + lldb::TypeSP ParseType(const SymbolContext &sc, const DWARFDIE &die, + bool *type_is_new); + + bool ParseSupportFiles(DWARFUnit &dwarf_cu, const lldb::ModuleSP &module, + SupportFileList &support_files); + + lldb::VariableSP ParseVariableDIE(const SymbolContext &sc, + const DWARFDIE &die, + const lldb::addr_t func_low_pc); + lldb::VariableSP ParseVariableDIECached(const SymbolContext &sc, + const DWARFDIE &die); + + void ParseAndAppendGlobalVariable(const SymbolContext &sc, + const DWARFDIE &die, + VariableList &cc_variable_list); + + size_t ParseVariablesInFunctionContext(const SymbolContext &sc, + const DWARFDIE &die, + const lldb::addr_t func_low_pc); + + size_t ParseVariablesInFunctionContextRecursive(const SymbolContext &sc, + const DWARFDIE &die, + lldb::addr_t func_low_pc, + DIEArray &accumulator); + + size_t PopulateBlockVariableList(VariableList &variable_list, + const SymbolContext &sc, + llvm::ArrayRef<DIERef> variable_dies, + lldb::addr_t func_low_pc); + + DIEArray MergeBlockAbstractParameters(const DWARFDIE &block_die, + DIEArray &&variable_dies); + + // Given a die_offset, figure out the symbol context representing that die. + bool ResolveFunction(const DWARFDIE &die, bool include_inlines, + SymbolContextList &sc_list); + + /// Resolve functions and (possibly) blocks for the given file address and a + /// compile unit. The compile unit comes from the sc argument and it must be + /// set. The results of the lookup (if any) are written back to the symbol + /// context. + void ResolveFunctionAndBlock(lldb::addr_t file_vm_addr, bool lookup_block, + SymbolContext &sc); + + Symbol *GetObjCClassSymbol(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; + } + + 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); + + bool ClassContainsSelector(const DWARFDIE &class_die, ConstString selector); + + /// Parse call site entries (DW_TAG_call_site), including any nested call site + /// parameters (DW_TAG_call_site_parameter). + std::vector<std::unique_ptr<CallEdge>> + CollectCallEdges(lldb::ModuleSP module, DWARFDIE function_die); + + /// If this symbol file is linked to by a debug map (see + /// SymbolFileDWARFDebugMap), and \p file_addr is a file address relative to + /// an object file, adjust \p file_addr so that it is relative to the main + /// binary. Returns the adjusted address, or \p file_addr if no adjustment is + /// needed, on success and LLDB_INVALID_ADDRESS otherwise. + lldb::addr_t FixupAddress(lldb::addr_t file_addr); + + bool FixupAddress(Address &addr); + + typedef llvm::SetVector<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 RangeDataVector<lldb::addr_t, lldb::addr_t, Variable *> + GlobalVariableMap; + + GlobalVariableMap &GetGlobalAranges(); + + void UpdateExternalModuleListIfNeeded(); + + void BuildCuTranslationTable(); + std::optional<uint32_t> GetDWARFUnitIndex(uint32_t cu_idx); + + void FindDwpSymbolFile(); + + const SupportFileList *GetTypeUnitSupportFiles(DWARFTypeUnit &tu); + + void InitializeFirstCodeAddressRecursive(const SectionList §ion_list); + + void InitializeFirstCodeAddress(); + + void + GetCompileOptions(std::unordered_map<lldb::CompUnitSP, Args> &args) override; + + lldb::ModuleWP m_debug_map_module_wp; + SymbolFileDWARFDebugMap *m_debug_map_symfile; + + llvm::once_flag m_dwp_symfile_once_flag; + std::shared_ptr<SymbolFileDWARFDwo> m_dwp_symfile; + + DWARFContext m_context; + + llvm::once_flag m_info_once_flag; + std::unique_ptr<DWARFDebugInfo> m_info; + + std::unique_ptr<llvm::DWARFDebugAbbrev> m_abbr; + std::unique_ptr<GlobalVariableMap> m_global_aranges_up; + + typedef std::unordered_map<lldb::offset_t, DebugMacrosSP> DebugMacrosMap; + DebugMacrosMap m_debug_macros_map; + + ExternalTypeModuleMap m_external_type_modules; + std::unique_ptr<DWARFIndex> m_index; + bool m_fetched_external_modules : 1; + LazyBool m_supports_DW_AT_APPLE_objc_complete_type; + + typedef std::set<DIERef> DIERefSet; + typedef llvm::StringMap<DIERefSet> NameToOffsetMap; + NameToOffsetMap m_function_scope_qualified_name_map; + std::unique_ptr<DWARFDebugRanges> m_ranges; + UniqueDWARFASTTypeMap m_unique_ast_type_map; + // A map from DIE to lldb_private::Type. For record type, the key might be + // either declaration DIE or definition DIE. + DIEToTypePtr m_die_to_type; + DIEToVariableSP m_die_to_variable_sp; + // A map from CompilerType to the struct/class/union/enum DIE (might be a + // declaration or a definition) that is used to construct it. + llvm::DenseMap<lldb::opaque_compiler_type_t, DIERef> + m_forward_decl_compiler_type_to_die; + llvm::DenseMap<dw_offset_t, std::unique_ptr<SupportFileList>> + m_type_unit_support_files; + std::vector<uint32_t> m_lldb_cu_to_dwarf_unit; + /// DWARF does not provide a good way for traditional (concatenating) linkers + /// to invalidate debug info describing dead-stripped code. These linkers will + /// keep the debug info but resolve any addresses referring to such code as + /// zero (BFD) or a small positive integer (zero + relocation addend -- GOLD). + /// Try to filter out this debug info by comparing it to the lowest code + /// address in the module. + lldb::addr_t m_first_code_address = LLDB_INVALID_ADDRESS; + StatsDuration m_parse_time; + std::atomic_flag m_dwo_warning_issued = ATOMIC_FLAG_INIT; + /// If this DWARF file a .DWO file or a DWARF .o file on mac when + /// no dSYM file is being used, this file index will be set to a + /// valid value that can be used in DIERef objects which will contain + /// an index that identifies the .DWO or .o file. + std::optional<uint64_t> m_file_index; +}; +} // namespace dwarf +} // namespace lldb_private::plugin + +#endif // LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_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..64cde16433ef --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp @@ -0,0 +1,1596 @@ +//===-- SymbolFileDWARFDebugMap.cpp ---------------------------------------===// +// +// 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 "DWARFCompileUnit.h" +#include "DWARFDebugAranges.h" +#include "DWARFDebugInfo.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/StreamString.h" +#include "lldb/Utility/StructuredData.h" +#include "lldb/Utility/Timer.h" + +//#define DEBUG_OSO_DMAP // DO NOT CHECKIN WITH THIS NOT COMMENTED OUT + +#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/ADT/STLExtras.h" +#include "llvm/Support/ScopedPrinter.h" + +#include "lldb/Target/StackFrame.h" + +#include "LogChannelDWARF.h" +#include "SymbolFileDWARF.h" +#include "lldb/lldb-private-enumerations.h" + +#include <memory> +#include <optional> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::plugin::dwarf; + +char SymbolFileDWARFDebugMap::ID; + +// Subclass lldb_private::Module so we can intercept the +// "Module::GetObjectFile()" (so we can fixup the object file sections) and +// also for "Module::GetSymbolFile()" (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 = GetLog(DWARFLog::DebugMap); + LLDB_LOGF( + log, + "%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(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(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; +} + +namespace lldb_private::plugin { +namespace dwarf { +class DebugMapModule : public Module { +public: + DebugMapModule(const ModuleSP &exe_module_sp, uint32_t cu_idx, + const FileSpec &file_spec, const ArchSpec &arch, + 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; + + SymbolFile * + GetSymbolFile(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 ? m_symfile_up->GetSymbolFile() : nullptr; + + 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); + if (SymbolFile *symfile = + Module::GetSymbolFile(can_create, feedback_strm)) { + // 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(symfile); + + if (!oso_symfile) + return nullptr; + + ObjectFile *exe_objfile = exe_module_sp->GetObjectFile(); + SymbolFile *exe_symfile = exe_module_sp->GetSymbolFile(); + + if (exe_objfile && exe_symfile) { + 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->SetFileIndex((uint64_t)m_cu_idx); + } + return symfile; + } + } + } + return nullptr; + } + +protected: + ModuleWP m_exe_module_wp; + const uint32_t m_cu_idx; +}; +} // namespace dwarf +} // namespace lldb_private::plugin + +void SymbolFileDWARFDebugMap::Initialize() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), CreateInstance); +} + +void SymbolFileDWARFDebugMap::Terminate() { + PluginManager::UnregisterPlugin(CreateInstance); +} + +llvm::StringRef SymbolFileDWARFDebugMap::GetPluginDescriptionStatic() { + return "DWARF and DWARF3 debug symbol file reader (debug map)."; +} + +SymbolFile *SymbolFileDWARFDebugMap::CreateInstance(ObjectFileSP objfile_sp) { + return new SymbolFileDWARFDebugMap(std::move(objfile_sp)); +} + +SymbolFileDWARFDebugMap::SymbolFileDWARFDebugMap(ObjectFileSP objfile_sp) + : SymbolFileCommon(std::move(objfile_sp)), m_flags(), m_compile_unit_infos(), + m_func_indexes(), m_glob_indexes(), + m_supports_DW_AT_APPLE_objc_complete_type(eLazyBoolCalculate) {} + +SymbolFileDWARFDebugMap::~SymbolFileDWARFDebugMap() = default; + +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_objfile_sp->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_objfile_sp->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_objfile_sp->GetSymtab(); + if (!symtab) + return; + + Log *log = GetLog(DWARFLog::DebugMap); + + 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) + return; + + 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 : + llvm::concat<uint32_t>(m_func_indexes, 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 <= i || sibling_idx == UINT32_MAX) { + m_objfile_sp->GetModule()->ReportError( + "N_SO in symbol with UID {0} 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(); + + LLDB_LOGF(log, "Initialized OSO 0x%8.8x: file=%s", i, + oso_symbol->GetName().GetCString()); + } + } else { + if (oso_symbol == nullptr) + m_objfile_sp->GetModule()->ReportError( + "N_OSO symbol[{0}] can't be found, please file a bug and " + "attach " + "the binary listed in this error", + oso_idx); + else if (so_symbol == nullptr) + m_objfile_sp->GetModule()->ReportError( + "N_SO not found for N_OSO symbol[{0}], please file a bug and " + "attach the binary listed in this error", + oso_idx); + else if (so_symbol->GetType() != eSymbolTypeSourceFile) + m_objfile_sp->GetModule()->ReportError( + "N_SO has incorrect symbol type ({0}) for N_OSO " + "symbol[{1}], " + "please file a bug and attach the binary listed in this error", + so_symbol->GetType(), oso_idx); + else if (oso_symbol->GetType() != eSymbolTypeSourceFile) + m_objfile_sp->GetModule()->ReportError( + "N_OSO has incorrect symbol type ({0}) for N_OSO " + "symbol[{1}], " + "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)); + // A timestamp of 0 means that the linker was in deterministic mode. In + // that case, we should skip the check against the filesystem last + // modification timestamp, since it will never match. + if (comp_unit_info->oso_mod_time != llvm::sys::TimePoint<>() && + oso_mod_time != comp_unit_info->oso_mod_time) { + comp_unit_info->oso_load_error.SetErrorStringWithFormat( + "debug map object file \"%s\" changed (actual: 0x%8.8x, debug " + "map: 0x%8.8x) since this executable was linked, debug info " + "will not be loaded", oso_file.GetPath().c_str(), + (uint32_t)llvm::sys::toTimeT(oso_mod_time), + (uint32_t)llvm::sys::toTimeT(comp_unit_info->oso_mod_time)); + obj_file->GetModule()->ReportError( + "{0}", comp_unit_info->oso_load_error.AsCString()); + return nullptr; + } + + } else { + const bool must_exist = true; + + if (!ObjectFile::SplitArchivePathWithObject(oso_path, oso_file, + oso_object, must_exist)) { + comp_unit_info->oso_load_error.SetErrorStringWithFormat( + "debug map object file \"%s\" containing debug info does not " + "exist, debug info will not be loaded", + comp_unit_info->oso_path.GetCString()); + 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_objfile_sp->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, 0, + oso_object ? comp_unit_info->oso_mod_time : llvm::sys::TimePoint<>()); + + if (oso_object && !comp_unit_info->oso_sp->module_sp->GetObjectFile() && + FileSystem::Instance().Exists(oso_file)) { + // If we are loading a .o file from a .a file the "oso_object" will + // have a valid value name and if the .a file exists, either the .o + // file didn't exist in the .a file or the mod time didn't match. + comp_unit_info->oso_load_error.SetErrorStringWithFormat( + "\"%s\" object from the \"%s\" archive: " + "either the .o file doesn't exist in the archive or the " + "modification time (0x%8.8x) of the .o file doesn't match", + oso_object.AsCString(), oso_file.GetPath().c_str(), + (uint32_t)llvm::sys::toTimeT(comp_unit_info->oso_mod_time)); + } + } + } + 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 static_cast<SymbolFileDWARF *>(sym_file); + return nullptr; +} + +SymbolFileDWARF *SymbolFileDWARFDebugMap::GetSymbolFileByCompUnitInfo( + CompileUnitInfo *comp_unit_info) { + if (Module *oso_module = GetModuleByCompUnitInfo(comp_unit_info)) + return GetSymbolFileAsSymbolFileDWARF(oso_module->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::CalculateNumCompileUnits() { + 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) { + auto &cu_info = m_compile_unit_infos[cu_idx]; + Module *oso_module = GetModuleByCompUnitInfo(&cu_info); + 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. + lldb::user_id_t cu_id = 0; + cu_info.compile_units_sps.push_back(std::make_shared<CompileUnit>( + m_objfile_sp->GetModule(), nullptr, + std::make_shared<SupportFile>(so_file_spec), cu_id, + eLanguageTypeUnknown, eLazyBoolCalculate)); + cu_info.id_to_index_map.insert({0, 0}); + SetCompileUnitAtIndex(cu_idx, cu_info.compile_units_sps[0]); + // If there's a symbol file also register all the extra compile units. + if (SymbolFileDWARF *oso_symfile = + GetSymbolFileByCompUnitInfo(&cu_info)) { + auto num_dwarf_units = oso_symfile->DebugInfo().GetNumUnits(); + for (size_t i = 0; i < num_dwarf_units; ++i) { + auto *dwarf_unit = oso_symfile->DebugInfo().GetUnitAtIndex(i); + if (auto *dwarf_cu = llvm::dyn_cast<DWARFCompileUnit>(dwarf_unit)) { + // The "main" one was already registered. + if (dwarf_cu->GetID() == 0) + continue; + cu_info.compile_units_sps.push_back(std::make_shared<CompileUnit>( + m_objfile_sp->GetModule(), nullptr, + std::make_shared<SupportFile>(so_file_spec), + dwarf_cu->GetID(), eLanguageTypeUnknown, eLazyBoolCalculate)); + cu_info.id_to_index_map.insert( + {dwarf_cu->GetID(), cu_info.compile_units_sps.size() - 1}); + } + } + } + } + } + if (!cu_info.compile_units_sps.empty()) + comp_unit_sp = cu_info.compile_units_sps[0]; + } + + 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) { + auto &id_to_index_map = m_compile_unit_infos[i].id_to_index_map; + + auto it = id_to_index_map.find(comp_unit.GetID()); + if (it != id_to_index_map.end() && + &comp_unit == + m_compile_unit_infos[i].compile_units_sps[it->getSecond()].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) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + SymbolFileDWARF *oso_dwarf = GetSymbolFile(comp_unit); + if (oso_dwarf) + return oso_dwarf->ParseLanguage(comp_unit); + return eLanguageTypeUnknown; +} + +XcodeSDK SymbolFileDWARFDebugMap::ParseXcodeSDK(CompileUnit &comp_unit) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + SymbolFileDWARF *oso_dwarf = GetSymbolFile(comp_unit); + if (oso_dwarf) + return oso_dwarf->ParseXcodeSDK(comp_unit); + return {}; +} + +llvm::SmallSet<lldb::LanguageType, 4> +SymbolFileDWARFDebugMap::ParseAllLanguages( + lldb_private::CompileUnit &comp_unit) { + llvm::SmallSet<lldb::LanguageType, 4> langs; + auto *info = GetCompUnitInfo(comp_unit); + for (auto &comp_unit : info->compile_units_sps) { + langs.insert(comp_unit->GetLanguage()); + } + return langs; +} + +size_t SymbolFileDWARFDebugMap::ParseFunctions(CompileUnit &comp_unit) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + SymbolFileDWARF *oso_dwarf = GetSymbolFile(comp_unit); + if (oso_dwarf) + return oso_dwarf->ParseFunctions(comp_unit); + return 0; +} + +bool SymbolFileDWARFDebugMap::ParseLineTable(CompileUnit &comp_unit) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + SymbolFileDWARF *oso_dwarf = GetSymbolFile(comp_unit); + if (oso_dwarf) + return oso_dwarf->ParseLineTable(comp_unit); + return false; +} + +bool SymbolFileDWARFDebugMap::ParseDebugMacros(CompileUnit &comp_unit) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + SymbolFileDWARF *oso_dwarf = GetSymbolFile(comp_unit); + if (oso_dwarf) + return oso_dwarf->ParseDebugMacros(comp_unit); + return false; +} + +bool SymbolFileDWARFDebugMap::ForEachExternalModule( + CompileUnit &comp_unit, + llvm::DenseSet<lldb_private::SymbolFile *> &visited_symbol_files, + llvm::function_ref<bool(Module &)> f) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + SymbolFileDWARF *oso_dwarf = GetSymbolFile(comp_unit); + if (oso_dwarf) + return oso_dwarf->ForEachExternalModule(comp_unit, visited_symbol_files, f); + return false; +} + +bool SymbolFileDWARFDebugMap::ParseSupportFiles( + CompileUnit &comp_unit, SupportFileList &support_files) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + 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) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + 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) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + SymbolFileDWARF *oso_dwarf = GetSymbolFile(sc); + if (oso_dwarf) + return oso_dwarf->ParseImportedModules(sc, imported_modules); + return false; +} + +size_t SymbolFileDWARFDebugMap::ParseBlocksRecursive(Function &func) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + 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) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + SymbolFileDWARF *oso_dwarf = GetSymbolFile(comp_unit); + if (oso_dwarf) + return oso_dwarf->ParseTypes(comp_unit); + return 0; +} + +size_t +SymbolFileDWARFDebugMap::ParseVariablesForContext(const SymbolContext &sc) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + SymbolFileDWARF *oso_dwarf = GetSymbolFile(sc); + if (oso_dwarf) + return oso_dwarf->ParseVariablesForContext(sc); + return 0; +} + +Type *SymbolFileDWARFDebugMap::ResolveTypeUID(lldb::user_id_t type_uid) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + 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; +} + +std::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 std::nullopt; +} + +bool SymbolFileDWARFDebugMap::CompleteType(CompilerType &compiler_type) { + bool success = false; + if (compiler_type) { + ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) { + if (oso_dwarf->HasForwardDeclForCompilerType(compiler_type)) { + oso_dwarf->CompleteType(compiler_type); + success = true; + return IterationAction::Stop; + } + return IterationAction::Continue; + }); + } + return success; +} + +uint32_t +SymbolFileDWARFDebugMap::ResolveSymbolContext(const Address &exe_so_addr, + SymbolContextItem resolve_scope, + SymbolContext &sc) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + uint32_t resolved_flags = 0; + Symtab *symtab = m_objfile_sp->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)) { + if (SymbolFile *sym_file = oso_module->GetSymbolFile()) { + resolved_flags |= sym_file->ResolveSymbolContext( + oso_so_addr, resolve_scope, sc); + } else { + ObjectFile *obj_file = GetObjectFile(); + LLDB_LOG(GetLog(DWARFLog::DebugMap), + "Failed to get symfile for OSO: {0} in module: {1}", + oso_module->GetFileSpec(), + obj_file ? obj_file->GetFileSpec() + : FileSpec("unknown")); + } + } + } + } + } + } + } + return resolved_flags; +} + +uint32_t SymbolFileDWARFDebugMap::ResolveSymbolContext( + const SourceLocationSpec &src_location_spec, + SymbolContextItem resolve_scope, SymbolContextList &sc_list) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + 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 = src_location_spec.GetCheckInlines(); + + if (!resolve) { + FileSpec so_file_spec; + if (GetFileSpecForSO(i, so_file_spec)) + resolve = + FileSpec::Match(src_location_spec.GetFileSpec(), so_file_spec); + } + if (resolve) { + SymbolFileDWARF *oso_dwarf = GetSymbolFileByOSOIndex(i); + if (oso_dwarf) + oso_dwarf->ResolveSymbolContext(src_location_spec, resolve_scope, + sc_list); + } + } + return sc_list.GetSize() - initial; +} + +void 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 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) { + oso_dwarf->FindGlobalVariables(name, parent_decl_ctx, max_matches, + variables); + if (variables.GetSize() > max_matches) + break; + } + } + } +} + +void SymbolFileDWARFDebugMap::FindGlobalVariables( + ConstString name, const CompilerDeclContext &parent_decl_ctx, + uint32_t max_matches, VariableList &variables) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + uint32_t total_matches = 0; + + ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) { + const uint32_t old_size = variables.GetSize(); + oso_dwarf->FindGlobalVariables(name, parent_decl_ctx, max_matches, + variables); + const uint32_t oso_matches = variables.GetSize() - old_size; + if (oso_matches > 0) { + total_matches += oso_matches; + + // Are we getting all matches? + if (max_matches == UINT32_MAX) + return IterationAction::Continue; // Yep, continue getting everything + + // If we have found enough matches, lets get out + if (max_matches >= total_matches) + return IterationAction::Stop; + + // Update the max matches for any subsequent calls to find globals in any + // other object files with DWARF + max_matches -= oso_matches; + } + + return IterationAction::Continue; + }); +} + +void SymbolFileDWARFDebugMap::FindGlobalVariables( + const RegularExpression ®ex, uint32_t max_matches, + VariableList &variables) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + uint32_t total_matches = 0; + ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) { + const uint32_t old_size = variables.GetSize(); + oso_dwarf->FindGlobalVariables(regex, max_matches, variables); + + const uint32_t oso_matches = variables.GetSize() - old_size; + if (oso_matches > 0) { + total_matches += oso_matches; + + // Are we getting all matches? + if (max_matches == UINT32_MAX) + return IterationAction::Continue; // Yep, continue getting everything + + // If we have found enough matches, lets get out + if (max_matches >= total_matches) + return IterationAction::Stop; + + // Update the max matches for any subsequent calls to find globals in any + // other object files with DWARF + max_matches -= oso_matches; + } + + return IterationAction::Continue; + }); +} + +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; + } +} + +void SymbolFileDWARFDebugMap::FindFunctions( + const Module::LookupInfo &lookup_info, + const CompilerDeclContext &parent_decl_ctx, bool include_inlines, + SymbolContextList &sc_list) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + LLDB_SCOPED_TIMERF("SymbolFileDWARFDebugMap::FindFunctions (name = %s)", + lookup_info.GetLookupName().GetCString()); + + ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) { + uint32_t sc_idx = sc_list.GetSize(); + oso_dwarf->FindFunctions(lookup_info, parent_decl_ctx, include_inlines, + sc_list); + if (!sc_list.IsEmpty()) { + RemoveFunctionsWithModuleNotEqualTo(m_objfile_sp->GetModule(), sc_list, + sc_idx); + } + return IterationAction::Continue; + }); +} + +void SymbolFileDWARFDebugMap::FindFunctions(const RegularExpression ®ex, + bool include_inlines, + SymbolContextList &sc_list) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + LLDB_SCOPED_TIMERF("SymbolFileDWARFDebugMap::FindFunctions (regex = '%s')", + regex.GetText().str().c_str()); + + ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) { + uint32_t sc_idx = sc_list.GetSize(); + + oso_dwarf->FindFunctions(regex, include_inlines, sc_list); + if (!sc_list.IsEmpty()) { + RemoveFunctionsWithModuleNotEqualTo(m_objfile_sp->GetModule(), sc_list, + sc_idx); + } + return IterationAction::Continue; + }); +} + +void SymbolFileDWARFDebugMap::GetTypes(SymbolContextScope *sc_scope, + lldb::TypeClass type_mask, + TypeList &type_list) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + LLDB_SCOPED_TIMERF("SymbolFileDWARFDebugMap::GetTypes (type_mask = 0x%8.8x)", + type_mask); + + 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) { + oso_dwarf->GetTypes(sc_scope, type_mask, type_list); + return IterationAction::Continue; + }); + } +} + +std::vector<std::unique_ptr<lldb_private::CallEdge>> +SymbolFileDWARFDebugMap::ParseCallEdgesInFunction( + lldb_private::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 {}; +} + +DWARFDIE SymbolFileDWARFDebugMap::FindDefinitionDIE(const DWARFDIE &die) { + DWARFDIE result; + ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) { + result = oso_dwarf->FindDefinitionDIE(die); + return result ? IterationAction::Stop : IterationAction::Continue; + }); + return result; +} + +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) { + 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 IterationAction::Stop; + } + return IterationAction::Continue; + }); + } + 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_objfile_sp->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) { + type_sp = oso_dwarf->FindCompleteObjCDefinitionTypeForDIE( + die, type_name, must_be_implementation); + return type_sp ? IterationAction::Stop : IterationAction::Continue; + }); + + return type_sp; + } + return TypeSP(); +} + +void SymbolFileDWARFDebugMap::FindTypes(const TypeQuery &query, + TypeResults &results) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) { + oso_dwarf->FindTypes(query, results); + return results.Done(query) ? IterationAction::Stop + : IterationAction::Continue; + }); +} + +CompilerDeclContext SymbolFileDWARFDebugMap::FindNamespace( + lldb_private::ConstString name, const CompilerDeclContext &parent_decl_ctx, + bool only_root_namespaces) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + CompilerDeclContext matching_namespace; + + ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) { + matching_namespace = + oso_dwarf->FindNamespace(name, parent_decl_ctx, only_root_namespaces); + + return matching_namespace ? IterationAction::Stop + : IterationAction::Continue; + }); + + return matching_namespace; +} + +void SymbolFileDWARFDebugMap::DumpClangAST(Stream &s) { + ForEachSymbolFile([&s](SymbolFileDWARF *oso_dwarf) { + oso_dwarf->DumpClangAST(s); + // The underlying assumption is that DumpClangAST(...) will obtain the + // AST from the underlying TypeSystem and therefore we only need to do + // this once and can stop after the first iteration hence we return true. + return IterationAction::Stop; + }); +} + +bool SymbolFileDWARFDebugMap::GetSeparateDebugInfo( + lldb_private::StructuredData::Dictionary &d, bool errors_only) { + StructuredData::Array separate_debug_info_files; + const uint32_t cu_count = GetNumCompileUnits(); + for (uint32_t cu_idx = 0; cu_idx < cu_count; ++cu_idx) { + const auto &info = m_compile_unit_infos[cu_idx]; + StructuredData::DictionarySP oso_data = + std::make_shared<StructuredData::Dictionary>(); + oso_data->AddStringItem("so_file", info.so_file.GetPath()); + oso_data->AddStringItem("oso_path", info.oso_path); + oso_data->AddIntegerItem("oso_mod_time", + (uint32_t)llvm::sys::toTimeT(info.oso_mod_time)); + + bool loaded_successfully = false; + if (GetModuleByOSOIndex(cu_idx)) { + // If we have a valid pointer to the module, we successfully + // loaded the oso if there are no load errors. + if (!info.oso_load_error.Fail()) { + loaded_successfully = true; + } + } + if (!loaded_successfully) { + oso_data->AddStringItem("error", info.oso_load_error.AsCString()); + } + oso_data->AddBooleanItem("loaded", loaded_successfully); + if (!errors_only || oso_data->HasKey("error")) + separate_debug_info_files.AddItem(oso_data); + } + + d.AddStringItem("type", "oso"); + d.AddStringItem("symfile", GetMainObjectFile()->GetFileSpec().GetPath()); + d.AddItem("separate-debug-info-files", + std::make_shared<StructuredData::Array>( + std::move(separate_debug_info_files))); + return true; +} + +lldb::CompUnitSP +SymbolFileDWARFDebugMap::GetCompileUnit(SymbolFileDWARF *oso_dwarf, DWARFCompileUnit &dwarf_cu) { + 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_units_sps.empty()) + ParseCompileUnitAtIndex(cu_idx); + + auto &id_to_index_map = m_compile_unit_infos[cu_idx].id_to_index_map; + auto it = id_to_index_map.find(dwarf_cu.GetID()); + if (it != id_to_index_map.end()) + return m_compile_unit_infos[cu_idx] + .compile_units_sps[it->getSecond()]; + } + } + } + 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_units_sps.empty()) { + assert(m_compile_unit_infos[cu_idx].compile_units_sps[0].get() == + cu_sp.get()); + } else { + assert(cu_sp->GetID() == 0 && + "Setting first compile unit but with id different than 0!"); + auto &compile_units_sps = m_compile_unit_infos[cu_idx].compile_units_sps; + compile_units_sps.push_back(cu_sp); + m_compile_unit_infos[cu_idx].id_to_index_map.insert( + {cu_sp->GetID(), compile_units_sps.size() - 1}); + + SetCompileUnitAtIndex(cu_idx, cu_sp); + } + } + } + } +} + +CompilerDeclContext +SymbolFileDWARFDebugMap::GetDeclContextForUID(lldb::user_id_t type_uid) { + const uint64_t oso_idx = GetOSOIndexFromUserID(type_uid); + if (SymbolFileDWARF *oso_dwarf = GetSymbolFileByOSOIndex(oso_idx)) + return oso_dwarf->GetDeclContextForUID(type_uid); + return {}; +} + +CompilerDeclContext +SymbolFileDWARFDebugMap::GetDeclContextContainingUID(lldb::user_id_t type_uid) { + const uint64_t oso_idx = GetOSOIndexFromUserID(type_uid); + if (SymbolFileDWARF *oso_dwarf = GetSymbolFileByOSOIndex(oso_idx)) + return oso_dwarf->GetDeclContextContainingUID(type_uid); + return {}; +} + +std::vector<CompilerContext> +SymbolFileDWARFDebugMap::GetCompilerContextForUID(lldb::user_id_t type_uid) { + const uint64_t oso_idx = GetOSOIndexFromUserID(type_uid); + if (SymbolFileDWARF *oso_dwarf = GetSymbolFileByOSOIndex(oso_idx)) + return oso_dwarf->GetCompilerContextForUID(type_uid); + return {}; +} + +void SymbolFileDWARFDebugMap::ParseDeclsForContext( + lldb_private::CompilerDeclContext decl_ctx) { + ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) { + oso_dwarf->ParseDeclsForContext(decl_ctx); + return IterationAction::Continue; + }); +} + +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->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->GetFileIndex(), + entry->GetRangeBase(), + entry->GetRangeEnd()); + num_line_entries_added++; + } + } + } + } + return num_line_entries_added; +} + +ModuleList SymbolFileDWARFDebugMap::GetDebugInfoModules() { + ModuleList oso_modules; + ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) { + ObjectFile *oso_objfile = oso_dwarf->GetObjectFile(); + if (oso_objfile) { + ModuleSP module_sp = oso_objfile->GetModule(); + if (module_sp) + oso_modules.Append(module_sp); + } + return IterationAction::Continue; + }); + return oso_modules; +} + +Status SymbolFileDWARFDebugMap::CalculateFrameVariableError(StackFrame &frame) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + + // We need to make sure that our PC value from the frame matches the module + // for this object file since we will lookup the PC file address in the debug + // map below. + Address pc_addr = frame.GetFrameCodeAddress(); + if (pc_addr.GetModule() == m_objfile_sp->GetModule()) { + Symtab *symtab = m_objfile_sp->GetSymtab(); + if (symtab) { + const DebugMap::Entry *debug_map_entry = + m_debug_map.FindEntryThatContains(pc_addr.GetFileAddress()); + if (debug_map_entry) { + Symbol *symbol = + symtab->SymbolAtIndex(debug_map_entry->data.GetExeSymbolIndex()); + if (symbol) { + uint32_t oso_idx = 0; + CompileUnitInfo *comp_unit_info = + GetCompileUnitInfoForSymbolWithID(symbol->GetID(), &oso_idx); + if (comp_unit_info) { + Module *oso_module = GetModuleByCompUnitInfo(comp_unit_info); + if (oso_module) { + // Check the .o file's DWARF in case it has an error to display. + SymbolFile *oso_sym_file = oso_module->GetSymbolFile(); + if (oso_sym_file) + return oso_sym_file->GetFrameVariableError(frame); + } + // If we don't have a valid OSO module here, then something went + // wrong as we have a symbol for the address in the debug map, but + // we weren't able to open the .o file. Display an appropriate + // error + if (comp_unit_info->oso_load_error.Fail()) + return comp_unit_info->oso_load_error; + else + return Status("unable to load debug map object file \"%s\" " + "exist, debug info will not be loaded", + comp_unit_info->oso_path.GetCString()); + } + } + } + } + } + return Status(); +} + +void SymbolFileDWARFDebugMap::GetCompileOptions( + std::unordered_map<lldb::CompUnitSP, lldb_private::Args> &args) { + + ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) { + oso_dwarf->GetCompileOptions(args); + return IterationAction::Continue; + }); +} 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..34cb52e5b601 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h @@ -0,0 +1,398 @@ +//===-- 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 LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_SYMBOLFILEDWARFDEBUGMAP_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_SYMBOLFILEDWARFDEBUGMAP_H + +#include "DIERef.h" +#include "lldb/Symbol/SymbolFile.h" +#include "lldb/Utility/RangeMap.h" +#include "llvm/Support/Chrono.h" +#include <bitset> +#include <map> +#include <optional> +#include <vector> + +#include "UniqueDWARFASTType.h" +#include "lldb/Utility/StructuredData.h" +#include "lldb/lldb-private-enumerations.h" + +class DWARFASTParserClang; + +namespace lldb_private::plugin { +namespace dwarf { +class SymbolFileDWARF; +class DWARFCompileUnit; +class DWARFDebugAranges; +class DWARFDeclContext; + +class SymbolFileDWARFDebugMap : public SymbolFileCommon { + /// LLVM RTTI support. + static char ID; + +public: + /// LLVM RTTI support. + /// \{ + bool isA(const void *ClassID) const override { + return ClassID == &ID || SymbolFileCommon::isA(ClassID); + } + static bool classof(const SymbolFile *obj) { return obj->isA(&ID); } + /// \} + + // Static Functions + static void Initialize(); + + static void Terminate(); + + static llvm::StringRef GetPluginNameStatic() { return "dwarf-debugmap"; } + + static llvm::StringRef GetPluginDescriptionStatic(); + + static SymbolFile *CreateInstance(lldb::ObjectFileSP objfile_sp); + + // Constructors and Destructors + SymbolFileDWARFDebugMap(lldb::ObjectFileSP objfile_sp); + ~SymbolFileDWARFDebugMap() override; + + uint32_t CalculateAbilities() override; + void InitializeObject() override; + + // Compile Unit function calls + lldb::LanguageType ParseLanguage(CompileUnit &comp_unit) override; + XcodeSDK ParseXcodeSDK(CompileUnit &comp_unit) override; + llvm::SmallSet<lldb::LanguageType, 4> + ParseAllLanguages(CompileUnit &comp_unit) override; + size_t ParseFunctions(CompileUnit &comp_unit) override; + bool ParseLineTable(CompileUnit &comp_unit) override; + bool ParseDebugMacros(CompileUnit &comp_unit) override; + + bool ForEachExternalModule(CompileUnit &, llvm::DenseSet<SymbolFile *> &, + llvm::function_ref<bool(Module &)>) override; + + bool ParseSupportFiles(CompileUnit &comp_unit, + SupportFileList &support_files) override; + + bool ParseIsOptimized(CompileUnit &comp_unit) override; + + size_t ParseTypes(CompileUnit &comp_unit) override; + + bool + ParseImportedModules(const SymbolContext &sc, + std::vector<SourceModule> &imported_modules) override; + size_t ParseBlocksRecursive(Function &func) override; + size_t ParseVariablesForContext(const SymbolContext &sc) override; + + Type *ResolveTypeUID(lldb::user_id_t type_uid) override; + std::optional<ArrayInfo> + GetDynamicArrayInfoForUID(lldb::user_id_t type_uid, + const ExecutionContext *exe_ctx) override; + + CompilerDeclContext GetDeclContextForUID(lldb::user_id_t uid) override; + CompilerDeclContext GetDeclContextContainingUID(lldb::user_id_t uid) override; + std::vector<CompilerContext> + GetCompilerContextForUID(lldb::user_id_t uid) override; + void ParseDeclsForContext(CompilerDeclContext decl_ctx) override; + + bool CompleteType(CompilerType &compiler_type) override; + uint32_t ResolveSymbolContext(const Address &so_addr, + lldb::SymbolContextItem resolve_scope, + SymbolContext &sc) override; + uint32_t ResolveSymbolContext(const SourceLocationSpec &src_location_spec, + lldb::SymbolContextItem resolve_scope, + SymbolContextList &sc_list) override; + + Status CalculateFrameVariableError(StackFrame &frame) override; + + void FindGlobalVariables(ConstString name, + const CompilerDeclContext &parent_decl_ctx, + uint32_t max_matches, + VariableList &variables) override; + void FindGlobalVariables(const RegularExpression ®ex, uint32_t max_matches, + VariableList &variables) override; + void FindFunctions(const Module::LookupInfo &lookup_info, + const CompilerDeclContext &parent_decl_ctx, + bool include_inlines, SymbolContextList &sc_list) override; + void FindFunctions(const RegularExpression ®ex, bool include_inlines, + SymbolContextList &sc_list) override; + void FindTypes(const lldb_private::TypeQuery &match, + lldb_private::TypeResults &results) override; + CompilerDeclContext FindNamespace(ConstString name, + const CompilerDeclContext &parent_decl_ctx, + bool only_root_namespaces) override; + void GetTypes(SymbolContextScope *sc_scope, lldb::TypeClass type_mask, + TypeList &type_list) override; + std::vector<std::unique_ptr<CallEdge>> + ParseCallEdgesInFunction(UserID func_id) override; + + void DumpClangAST(Stream &s) override; + + /// List separate oso files. + bool GetSeparateDebugInfo(StructuredData::Dictionary &d, + bool errors_only) override; + + // PluginInterface protocol + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } + + // Statistics overrides. + ModuleList GetDebugInfoModules() override; + + void + GetCompileOptions(std::unordered_map<lldb::CompUnitSP, Args> &args) 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 RangeDataVector<lldb::addr_t, lldb::addr_t, lldb::addr_t> + FileRangeMap; + + // Class specific types + struct CompileUnitInfo { + FileSpec so_file; + ConstString oso_path; + llvm::sys::TimePoint<> oso_mod_time; + Status oso_load_error; + OSOInfoSP oso_sp; + /// The compile units that an object file contains. + llvm::SmallVector<lldb::CompUnitSP, 2> compile_units_sps; + /// A map from the compile unit ID to its index in the vector. + llvm::SmallDenseMap<uint64_t, uint64_t, 2> id_to_index_map; + uint32_t first_symbol_index = UINT32_MAX; + uint32_t last_symbol_index = UINT32_MAX; + uint32_t first_symbol_id = UINT32_MAX; + uint32_t last_symbol_id = UINT32_MAX; + FileRangeMap file_range_map; + bool file_range_map_valid = false; + + CompileUnitInfo() = default; + + const FileRangeMap &GetFileRangeMap(SymbolFileDWARFDebugMap *exe_symfile); + }; + + // Protected Member Functions + void InitOSO(); + + /// This function actually returns the number of object files, which may be + /// less than the actual number of compile units, since an object file may + /// contain more than one compile unit. SymbolFileDWARFDebugMap looks up the + /// number of compile units by reading the nlist symbol table, which + /// currently, on macOS, only reports one compile unit per object file, and + /// there's no efficient way to calculate the actual number of compile units + /// upfront. + uint32_t CalculateNumCompileUnits() override; + + /// This function actually returns the first compile unit the object file at + /// the given index contains. + lldb::CompUnitSP ParseCompileUnitAtIndex(uint32_t index) override; + + static uint32_t GetOSOIndexFromUserID(lldb::user_id_t uid) { + std::optional<uint32_t> OsoNum = DIERef(uid).file_index(); + lldbassert(OsoNum && "Invalid OSO Index"); + return *OsoNum; + } + + static SymbolFileDWARF *GetSymbolFileAsSymbolFileDWARF(SymbolFile *sym_file); + + bool GetFileSpecForSO(uint32_t oso_idx, FileSpec &file_spec); + + CompileUnitInfo *GetCompUnitInfo(const SymbolContext &sc); + CompileUnitInfo *GetCompUnitInfo(const CompileUnit &comp_unit); + + size_t GetCompUnitInfosForModule(const Module *oso_module, + std::vector<CompileUnitInfo *> &cu_infos); + + Module *GetModuleByCompUnitInfo(CompileUnitInfo *comp_unit_info); + + Module *GetModuleByOSOIndex(uint32_t oso_idx); + + ObjectFile *GetObjectFileByCompUnitInfo(CompileUnitInfo *comp_unit_info); + + ObjectFile *GetObjectFileByOSOIndex(uint32_t oso_idx); + + uint32_t GetCompUnitInfoIndex(const CompileUnitInfo *comp_unit_info); + + SymbolFileDWARF *GetSymbolFile(const SymbolContext &sc); + SymbolFileDWARF *GetSymbolFile(const CompileUnit &comp_unit); + + SymbolFileDWARF *GetSymbolFileByCompUnitInfo(CompileUnitInfo *comp_unit_info); + + SymbolFileDWARF *GetSymbolFileByOSOIndex(uint32_t oso_idx); + + /// If closure returns \ref IterationAction::Continue, iteration + /// continues. Otherwise, iteration terminates. + void + ForEachSymbolFile(std::function<IterationAction(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) == IterationAction::Stop) + 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); + + void + PrivateFindGlobalVariables(ConstString name, + const CompilerDeclContext &parent_decl_ctx, + const std::vector<uint32_t> &name_symbol_indexes, + uint32_t max_matches, VariableList &variables); + + void SetCompileUnit(SymbolFileDWARF *oso_dwarf, + const lldb::CompUnitSP &cu_sp); + + /// Returns the compile unit associated with the dwarf compile unit. This may + /// be one of the extra compile units an object file contains which isn't + /// reachable by ParseCompileUnitAtIndex(uint32_t). + lldb::CompUnitSP GetCompileUnit(SymbolFileDWARF *oso_dwarf, + DWARFCompileUnit &dwarf_cu); + + CompileUnitInfo *GetCompileUnitInfo(SymbolFileDWARF *oso_dwarf); + + DWARFDIE FindDefinitionDIE(const DWARFDIE &die); + + bool Supports_DW_AT_APPLE_objc_complete_type(SymbolFileDWARF *skip_dwarf_oso); + + lldb::TypeSP FindCompleteObjCDefinitionTypeForDIE( + const DWARFDIE &die, ConstString type_name, bool must_be_implementation); + + llvm::DenseMap<lldb::opaque_compiler_type_t, DIERef> & + GetForwardDeclCompilerTypeToDIE() { + return m_forward_decl_compiler_type_to_die; + } + + UniqueDWARFASTTypeMap &GetUniqueDWARFASTTypeMap() { + return m_unique_ast_type_map; + } + + // OSOEntry + class OSOEntry { + public: + OSOEntry() = default; + + 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 = UINT32_MAX; + lldb::addr_t m_oso_file_addr = LLDB_INVALID_ADDRESS; + }; + + typedef 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<ConstString, llvm::sys::TimePoint<>>, OSOInfoSP> m_oso_map; + // A map from CompilerType to the struct/class/union/enum DIE (might be a + // declaration or a definition) that is used to construct it. + llvm::DenseMap<lldb::opaque_compiler_type_t, DIERef> + m_forward_decl_compiler_type_to_die; + UniqueDWARFASTTypeMap m_unique_ast_type_map; + 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(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] line_table + /// A pointer to the line table. + /// + /// \return + /// Returns a valid line table full of linked addresses, or NULL + /// if none of the line table addresses exist in the main + /// executable. + LineTable *LinkOSOLineTable(SymbolFileDWARF *oso_symfile, + LineTable *line_table); + + size_t AddOSOARanges(SymbolFileDWARF *dwarf2Data, + DWARFDebugAranges *debug_aranges); +}; +} // namespace dwarf +} // namespace lldb_private::plugin + +#endif // LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_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..49632e1d8911 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.cpp @@ -0,0 +1,180 @@ +//===-- SymbolFileDWARFDwo.cpp --------------------------------------------===// +// +// 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" +#include <optional> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::plugin::dwarf; + +char SymbolFileDWARFDwo::ID; + +SymbolFileDWARFDwo::SymbolFileDWARFDwo(SymbolFileDWARF &base_symbol_file, + ObjectFileSP objfile, uint32_t id) + : SymbolFileDWARF(objfile, objfile->GetSectionList( + /*update_module_section_list*/ false)), + m_base_symbol_file(base_symbol_file) { + SetFileIndex(id); + + // Parsing of the dwarf unit index is not thread-safe, so we need to prime it + // to enable subsequent concurrent lookups. + m_context.GetAsLLVM().getCUIndex(); +} + +DWARFCompileUnit *SymbolFileDWARFDwo::GetDWOCompileUnitForHash(uint64_t hash) { + if (const llvm::DWARFUnitIndex &index = m_context.GetAsLLVM().getCUIndex()) { + if (const llvm::DWARFUnitIndex::Entry *entry = index.getFromHash(hash)) { + if (auto *unit_contrib = entry->getContribution()) + return llvm::dyn_cast_or_null<DWARFCompileUnit>( + DebugInfo().GetUnitAtOffset(DIERef::Section::DebugInfo, + unit_contrib->getOffset())); + } + return nullptr; + } + + DWARFCompileUnit *cu = FindSingleCompileUnit(); + if (!cu) + return nullptr; + std::optional<uint64_t> dwo_id = cu->GetDWOId(); + if (!dwo_id || hash != *dwo_id) + return nullptr; + return cu; +} + +DWARFCompileUnit *SymbolFileDWARFDwo::FindSingleCompileUnit() { + DWARFDebugInfo &debug_info = DebugInfo(); + + // 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; +} + +lldb::offset_t SymbolFileDWARFDwo::GetVendorDWARFOpcodeSize( + const lldb_private::DataExtractor &data, const lldb::offset_t data_offset, + const uint8_t op) const { + return GetBaseSymbolFile().GetVendorDWARFOpcodeSize(data, data_offset, op); +} + +uint64_t SymbolFileDWARFDwo::GetDebugInfoSize(bool load_all_debug_info) { + // Directly get debug info from current dwo object file's section list + // instead of asking SymbolFileCommon::GetDebugInfo() which parses from + // owning module which is wrong. + SectionList *section_list = + m_objfile_sp->GetSectionList(/*update_module_section_list=*/false); + if (section_list) + return section_list->GetDebugInfoSize(); + return 0; +} + +bool SymbolFileDWARFDwo::ParseVendorDWARFOpcode( + uint8_t op, const lldb_private::DataExtractor &opcodes, + lldb::offset_t &offset, std::vector<lldb_private::Value> &stack) const { + return GetBaseSymbolFile().ParseVendorDWARFOpcode(op, opcodes, offset, stack); +} + +SymbolFileDWARF::DIEToTypePtr &SymbolFileDWARFDwo::GetDIEToType() { + return GetBaseSymbolFile().GetDIEToType(); +} + +SymbolFileDWARF::DIEToVariableSP &SymbolFileDWARFDwo::GetDIEToVariable() { + return GetBaseSymbolFile().GetDIEToVariable(); +} + +llvm::DenseMap<lldb::opaque_compiler_type_t, DIERef> & +SymbolFileDWARFDwo::GetForwardDeclCompilerTypeToDIE() { + return GetBaseSymbolFile().GetForwardDeclCompilerTypeToDIE(); +} + +void SymbolFileDWARFDwo::GetObjCMethods( + lldb_private::ConstString class_name, + llvm::function_ref<bool(DWARFDIE die)> callback) { + GetBaseSymbolFile().GetObjCMethods(class_name, callback); +} + +UniqueDWARFASTTypeMap &SymbolFileDWARFDwo::GetUniqueDWARFASTTypeMap() { + return GetBaseSymbolFile().GetUniqueDWARFASTTypeMap(); +} + +DWARFDIE SymbolFileDWARFDwo::FindDefinitionDIE(const DWARFDIE &die) { + return GetBaseSymbolFile().FindDefinitionDIE(die); +} + +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); +} + +llvm::Expected<lldb::TypeSystemSP> +SymbolFileDWARFDwo::GetTypeSystemForLanguage(LanguageType language) { + return GetBaseSymbolFile().GetTypeSystemForLanguage(language); +} + +DWARFDIE +SymbolFileDWARFDwo::GetDIE(const DIERef &die_ref) { + if (die_ref.file_index() == GetFileIndex()) + return DebugInfo().GetDIE(die_ref.section(), die_ref.die_offset()); + return GetBaseSymbolFile().GetDIE(die_ref); +} + +void SymbolFileDWARFDwo::FindGlobalVariables( + ConstString name, const CompilerDeclContext &parent_decl_ctx, + uint32_t max_matches, VariableList &variables) { + GetBaseSymbolFile().FindGlobalVariables(name, parent_decl_ctx, max_matches, + variables); +} + +bool SymbolFileDWARFDwo::GetDebugInfoIndexWasLoadedFromCache() const { + return GetBaseSymbolFile().GetDebugInfoIndexWasLoadedFromCache(); +} +void SymbolFileDWARFDwo::SetDebugInfoIndexWasLoadedFromCache() { + GetBaseSymbolFile().SetDebugInfoIndexWasLoadedFromCache(); +} +bool SymbolFileDWARFDwo::GetDebugInfoIndexWasSavedToCache() const { + return GetBaseSymbolFile().GetDebugInfoIndexWasSavedToCache(); +} +void SymbolFileDWARFDwo::SetDebugInfoIndexWasSavedToCache() { + GetBaseSymbolFile().SetDebugInfoIndexWasSavedToCache(); +} +bool SymbolFileDWARFDwo::GetDebugInfoHadFrameVariableErrors() const { + return GetBaseSymbolFile().GetDebugInfoHadFrameVariableErrors(); +} +void SymbolFileDWARFDwo::SetDebugInfoHadFrameVariableErrors() { + return GetBaseSymbolFile().SetDebugInfoHadFrameVariableErrors(); +} + +SymbolFileDWARF * +SymbolFileDWARFDwo::GetDIERefSymbolFile(const DIERef &die_ref) { + return GetBaseSymbolFile().GetDIERefSymbolFile(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..15c28fefd81f --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.h @@ -0,0 +1,98 @@ +//===-- 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 LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_SYMBOLFILEDWARFDWO_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_SYMBOLFILEDWARFDWO_H + +#include "SymbolFileDWARF.h" +#include <optional> + +namespace lldb_private::plugin { +namespace dwarf { +class SymbolFileDWARFDwo : public SymbolFileDWARF { + /// LLVM RTTI support. + static char ID; + +public: + /// LLVM RTTI support. + /// \{ + bool isA(const void *ClassID) const override { + return ClassID == &ID || SymbolFileDWARF::isA(ClassID); + } + static bool classof(const SymbolFile *obj) { return obj->isA(&ID); } + /// \} + + SymbolFileDWARFDwo(SymbolFileDWARF &m_base_symbol_file, + lldb::ObjectFileSP objfile, uint32_t id); + + ~SymbolFileDWARFDwo() override = default; + + DWARFCompileUnit *GetDWOCompileUnitForHash(uint64_t hash); + + void GetObjCMethods(ConstString class_name, + llvm::function_ref<bool(DWARFDIE die)> callback) override; + + llvm::Expected<lldb::TypeSystemSP> + GetTypeSystemForLanguage(lldb::LanguageType language) override; + + DWARFDIE + GetDIE(const DIERef &die_ref) override; + + lldb::offset_t GetVendorDWARFOpcodeSize(const DataExtractor &data, + const lldb::offset_t data_offset, + const uint8_t op) const override; + + uint64_t GetDebugInfoSize(bool load_all_debug_info = false) override; + + bool ParseVendorDWARFOpcode(uint8_t op, const DataExtractor &opcodes, + lldb::offset_t &offset, + std::vector<Value> &stack) const override; + + void FindGlobalVariables(ConstString name, + const CompilerDeclContext &parent_decl_ctx, + uint32_t max_matches, + VariableList &variables) override; + + SymbolFileDWARF &GetBaseSymbolFile() const { return m_base_symbol_file; } + + bool GetDebugInfoIndexWasLoadedFromCache() const override; + void SetDebugInfoIndexWasLoadedFromCache() override; + bool GetDebugInfoIndexWasSavedToCache() const override; + void SetDebugInfoIndexWasSavedToCache() override; + bool GetDebugInfoHadFrameVariableErrors() const override; + void SetDebugInfoHadFrameVariableErrors() override; + + SymbolFileDWARF *GetDIERefSymbolFile(const DIERef &die_ref) override; + +protected: + DIEToTypePtr &GetDIEToType() override; + + DIEToVariableSP &GetDIEToVariable() override; + + llvm::DenseMap<lldb::opaque_compiler_type_t, DIERef> & + GetForwardDeclCompilerTypeToDIE() override; + + UniqueDWARFASTTypeMap &GetUniqueDWARFASTTypeMap() override; + + DWARFDIE FindDefinitionDIE(const DWARFDIE &die) override; + + lldb::TypeSP + FindCompleteObjCDefinitionTypeForDIE(const DWARFDIE &die, + ConstString type_name, + bool must_be_implementation) override; + + /// If this file contains exactly one compile unit, this function will return + /// it. Otherwise it returns nullptr. + DWARFCompileUnit *FindSingleCompileUnit(); + + SymbolFileDWARF &m_base_symbol_file; +}; +} // namespace dwarf +} // namespace lldb_private::plugin + +#endif // LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_SYMBOLFILEDWARFDWO_H diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFProperties.td b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFProperties.td new file mode 100644 index 000000000000..2f1ce88808b7 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFProperties.td @@ -0,0 +1,8 @@ +include "../../../../include/lldb/Core/PropertiesBase.td" + +let Definition = "symbolfiledwarf" in { + def IgnoreIndexes: Property<"ignore-file-indexes", "Boolean">, + Global, + DefaultFalse, + Desc<"Ignore indexes present in the object files and always index DWARF manually.">; +} 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..3d201e96f92c --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/UniqueDWARFASTType.cpp @@ -0,0 +1,87 @@ +//===-- UniqueDWARFASTType.cpp --------------------------------------------===// +// +// 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/Core/Declaration.h" + +using namespace lldb_private::dwarf; +using namespace lldb_private::plugin::dwarf; + +static bool IsStructOrClassTag(llvm::dwarf::Tag Tag) { + return Tag == llvm::dwarf::Tag::DW_TAG_class_type || + Tag == llvm::dwarf::Tag::DW_TAG_structure_type; +} + +UniqueDWARFASTType *UniqueDWARFASTTypeList::Find( + const DWARFDIE &die, const lldb_private::Declaration &decl, + const int32_t byte_size, bool is_forward_declaration) { + for (UniqueDWARFASTType &udt : m_collection) { + // Make sure the tags match + if (udt.m_die.Tag() == die.Tag() || (IsStructOrClassTag(udt.m_die.Tag()) && + IsStructOrClassTag(die.Tag()))) { + // If they are not both definition DIEs or both declaration DIEs, then + // don't check for byte size and declaration location, because declaration + // DIEs usually don't have those info. + bool matching_size_declaration = + udt.m_is_forward_declaration != is_forward_declaration + ? true + : (udt.m_byte_size < 0 || byte_size < 0 || + udt.m_byte_size == byte_size) && + udt.m_declaration == decl; + if (!matching_size_declaration) + continue; + // 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 || + (IsStructOrClassTag(parent_arg_tag) && + IsStructOrClassTag(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; + default: + break; + } + } + parent_arg_die = parent_arg_die.GetParent(); + parent_pos_die = parent_pos_die.GetParent(); + } + + if (match) { + return &udt; + } + } + } + return nullptr; +} 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..9215484fa2ea --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/UniqueDWARFASTType.h @@ -0,0 +1,107 @@ +//===-- 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_SOURCE_PLUGINS_SYMBOLFILE_DWARF_UNIQUEDWARFASTTYPE_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_UNIQUEDWARFASTTYPE_H + +#include <vector> + +#include "llvm/ADT/DenseMap.h" + +#include "DWARFDIE.h" +#include "lldb/Core/Declaration.h" +#include "lldb/Symbol/Type.h" + +namespace lldb_private::plugin { +namespace dwarf { +class UniqueDWARFASTType { +public: + // Constructors and Destructors + UniqueDWARFASTType() : m_type_sp(), m_die(), m_declaration() {} + + 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), + m_is_forward_declaration(rhs.m_is_forward_declaration) {} + + ~UniqueDWARFASTType() = default; + + // This UniqueDWARFASTType might be created from declaration, update its info + // to definition DIE. + void UpdateToDefDIE(const DWARFDIE &def_die, Declaration &declaration, + int32_t byte_size) { + // Need to update Type ID to refer to the definition DIE, because + // it's used in DWARFASTParserClang::ParseCXXMethod to determine if we need + // to copy cxx method types from a declaration DIE to this definition DIE. + m_type_sp->SetID(def_die.GetID()); + if (declaration.IsValid()) + m_declaration = declaration; + if (byte_size) + m_byte_size = byte_size; + m_is_forward_declaration = false; + } + + lldb::TypeSP m_type_sp; + DWARFDIE m_die; + Declaration m_declaration; + int32_t m_byte_size = -1; + // True if the m_die is a forward declaration DIE. + bool m_is_forward_declaration = true; +}; + +class UniqueDWARFASTTypeList { +public: + UniqueDWARFASTTypeList() : m_collection() {} + + ~UniqueDWARFASTTypeList() = default; + + uint32_t GetSize() { return (uint32_t)m_collection.size(); } + + void Append(const UniqueDWARFASTType &entry) { + m_collection.push_back(entry); + } + + UniqueDWARFASTType *Find(const DWARFDIE &die, const Declaration &decl, + const int32_t byte_size, + bool is_forward_declaration); + +protected: + typedef std::vector<UniqueDWARFASTType> collection; + collection m_collection; +}; + +class UniqueDWARFASTTypeMap { +public: + UniqueDWARFASTTypeMap() : m_collection() {} + + ~UniqueDWARFASTTypeMap() = default; + + void Insert(ConstString name, const UniqueDWARFASTType &entry) { + m_collection[name.GetCString()].Append(entry); + } + + UniqueDWARFASTType *Find(ConstString name, const DWARFDIE &die, + const Declaration &decl, const int32_t byte_size, + bool is_forward_declaration) { + const char *unique_name_cstr = name.GetCString(); + collection::iterator pos = m_collection.find(unique_name_cstr); + if (pos != m_collection.end()) { + return pos->second.Find(die, decl, byte_size, is_forward_declaration); + } + return nullptr; + } + +protected: + // A unique name string should be used + typedef llvm::DenseMap<const char *, UniqueDWARFASTTypeList> collection; + collection m_collection; +}; +} // namespace dwarf +} // namespace lldb_private::plugin + +#endif // LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_UNIQUEDWARFASTTYPE_H diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/JSON/SymbolFileJSON.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/JSON/SymbolFileJSON.cpp new file mode 100644 index 000000000000..1f3ca27417e4 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/JSON/SymbolFileJSON.cpp @@ -0,0 +1,105 @@ +//===-- SymbolFileJSON.cpp ----------------------------------------------===// +// +// 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 "SymbolFileJSON.h" + +#include "Plugins/ObjectFile/JSON/ObjectFileJSON.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/Symtab.h" +#include "lldb/Symbol/TypeList.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/RegularExpression.h" +#include "lldb/Utility/Timer.h" +#include "llvm/Support/MemoryBuffer.h" + +#include <memory> +#include <optional> + +using namespace llvm; +using namespace lldb; +using namespace lldb_private; + +LLDB_PLUGIN_DEFINE(SymbolFileJSON) + +char SymbolFileJSON::ID; + +SymbolFileJSON::SymbolFileJSON(lldb::ObjectFileSP objfile_sp) + : SymbolFileCommon(std::move(objfile_sp)) {} + +void SymbolFileJSON::Initialize() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), CreateInstance); +} + +void SymbolFileJSON::Terminate() { + PluginManager::UnregisterPlugin(CreateInstance); +} + +llvm::StringRef SymbolFileJSON::GetPluginDescriptionStatic() { + return "Reads debug symbols from a JSON symbol table."; +} + +SymbolFile *SymbolFileJSON::CreateInstance(ObjectFileSP objfile_sp) { + return new SymbolFileJSON(std::move(objfile_sp)); +} + +uint32_t SymbolFileJSON::CalculateAbilities() { + if (!m_objfile_sp || !llvm::isa<ObjectFileJSON>(*m_objfile_sp)) + return 0; + + return GlobalVariables | Functions; +} + +uint32_t SymbolFileJSON::ResolveSymbolContext(const Address &so_addr, + SymbolContextItem resolve_scope, + SymbolContext &sc) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + if (m_objfile_sp->GetSymtab() == nullptr) + return 0; + + uint32_t resolved_flags = 0; + if (resolve_scope & eSymbolContextSymbol) { + sc.symbol = m_objfile_sp->GetSymtab()->FindSymbolContainingFileAddress( + so_addr.GetFileAddress()); + if (sc.symbol) + resolved_flags |= eSymbolContextSymbol; + } + return resolved_flags; +} + +CompUnitSP SymbolFileJSON::ParseCompileUnitAtIndex(uint32_t idx) { return {}; } + +void SymbolFileJSON::GetTypes(SymbolContextScope *sc_scope, TypeClass type_mask, + lldb_private::TypeList &type_list) {} + +void SymbolFileJSON::AddSymbols(Symtab &symtab) { + if (!m_objfile_sp) + return; + + Symtab *json_symtab = m_objfile_sp->GetSymtab(); + if (!json_symtab) + return; + + if (&symtab == json_symtab) + return; + + // Merge the two symbol tables. + const size_t num_new_symbols = json_symtab->GetNumSymbols(); + for (size_t i = 0; i < num_new_symbols; ++i) { + Symbol *s = json_symtab->SymbolAtIndex(i); + symtab.AddSymbol(*s); + } + symtab.Finalize(); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/JSON/SymbolFileJSON.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/JSON/SymbolFileJSON.h new file mode 100644 index 000000000000..3dd33b3dc82f --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/JSON/SymbolFileJSON.h @@ -0,0 +1,110 @@ +//===-- SymbolFileJSON.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_SOURCE_PLUGINS_SYMBOLFILE_JSON_SYMBOLFILEJSON_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_JSON_SYMBOLFILEJSON_H + +#include <map> +#include <optional> +#include <vector> + +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/SymbolFile.h" + +namespace lldb_private { + +class SymbolFileJSON : public lldb_private::SymbolFileCommon { + /// LLVM RTTI support. + static char ID; + +public: + /// LLVM RTTI support. + /// \{ + bool isA(const void *ClassID) const override { + return ClassID == &ID || SymbolFileCommon::isA(ClassID); + } + static bool classof(const SymbolFile *obj) { return obj->isA(&ID); } + /// \} + + SymbolFileJSON(lldb::ObjectFileSP objfile_sp); + + static void Initialize(); + + static void Terminate(); + + static llvm::StringRef GetPluginNameStatic() { return "JSON"; } + + static llvm::StringRef GetPluginDescriptionStatic(); + + static lldb_private::SymbolFile * + CreateInstance(lldb::ObjectFileSP objfile_sp); + + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } + + uint32_t CalculateAbilities() override; + + lldb::LanguageType ParseLanguage(CompileUnit &comp_unit) override { + return lldb::eLanguageTypeUnknown; + } + + size_t ParseFunctions(CompileUnit &comp_unit) override { return 0; } + + bool ParseLineTable(CompileUnit &comp_unit) override { return false; } + + bool ParseDebugMacros(CompileUnit &comp_unit) override { return false; } + + bool ParseSupportFiles(CompileUnit &comp_unit, + SupportFileList &support_files) override { + return false; + } + + size_t ParseTypes(CompileUnit &cu) override { return 0; } + + bool ParseImportedModules( + const SymbolContext &sc, + std::vector<lldb_private::SourceModule> &imported_modules) override { + return false; + } + + size_t ParseBlocksRecursive(Function &func) override { return 0; } + + size_t ParseVariablesForContext(const SymbolContext &sc) override { + return 0; + } + + uint32_t CalculateNumCompileUnits() override { return 0; } + + lldb::CompUnitSP ParseCompileUnitAtIndex(uint32_t index) override; + + Type *ResolveTypeUID(lldb::user_id_t type_uid) override { return nullptr; } + std::optional<ArrayInfo> GetDynamicArrayInfoForUID( + lldb::user_id_t type_uid, + const lldb_private::ExecutionContext *exe_ctx) override { + return std::nullopt; + } + + bool CompleteType(CompilerType &compiler_type) override { return false; } + + uint32_t ResolveSymbolContext(const lldb_private::Address &so_addr, + lldb::SymbolContextItem resolve_scope, + lldb_private::SymbolContext &sc) override; + + void GetTypes(lldb_private::SymbolContextScope *sc_scope, + lldb::TypeClass type_mask, + lldb_private::TypeList &type_list) override; + + void AddSymbols(Symtab &symtab) override; + +private: + lldb::addr_t GetBaseFileAddress(); + + std::vector<std::pair<uint64_t, std::string>> m_symbols; +}; +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_SYMBOLFILE_JSON_SYMBOLFILEJSON_H diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/CodeViewRegisterMapping.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/CodeViewRegisterMapping.cpp new file mode 100644 index 000000000000..3c10d4aa314b --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/CodeViewRegisterMapping.cpp @@ -0,0 +1,748 @@ +//===-- CodeViewRegisterMapping.cpp ---------------------------------------===// +// +// 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 "CodeViewRegisterMapping.h" + +#include "lldb/lldb-defines.h" + +#include "Plugins/Process/Utility/lldb-arm64-register-enums.h" +#include "Plugins/Process/Utility/lldb-x86-register-enums.h" + +using namespace lldb_private; + +static const uint32_t g_code_view_to_lldb_registers_arm64[] = { + LLDB_INVALID_REGNUM, // NONE + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + gpr_w0_arm64, // ARM64_W0, 10) + gpr_w1_arm64, // ARM64_W1, 11) + gpr_w2_arm64, // ARM64_W2, 12) + gpr_w3_arm64, // ARM64_W3, 13) + gpr_w4_arm64, // ARM64_W4, 14) + gpr_w5_arm64, // ARM64_W5, 15) + gpr_w6_arm64, // ARM64_W6, 16) + gpr_w7_arm64, // ARM64_W7, 17) + gpr_w8_arm64, // ARM64_W8, 18) + gpr_w9_arm64, // ARM64_W9, 19) + gpr_w10_arm64, // ARM64_W10, 20) + gpr_w11_arm64, // ARM64_W11, 21) + gpr_w12_arm64, // ARM64_W12, 22) + gpr_w13_arm64, // ARM64_W13, 23) + gpr_w14_arm64, // ARM64_W14, 24) + gpr_w15_arm64, // ARM64_W15, 25) + gpr_w16_arm64, // ARM64_W16, 26) + gpr_w17_arm64, // ARM64_W17, 27) + gpr_w18_arm64, // ARM64_W18, 28) + gpr_w19_arm64, // ARM64_W19, 29) + gpr_w20_arm64, // ARM64_W20, 30) + gpr_w21_arm64, // ARM64_W21, 31) + gpr_w22_arm64, // ARM64_W22, 32) + gpr_w23_arm64, // ARM64_W23, 33) + gpr_w24_arm64, // ARM64_W24, 34) + gpr_w25_arm64, // ARM64_W25, 35) + gpr_w26_arm64, // ARM64_W26, 36) + gpr_w27_arm64, // ARM64_W27, 37) + gpr_w28_arm64, // ARM64_W28, 38) + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + gpr_x0_arm64, // ARM64_X0, 50) + gpr_x1_arm64, // ARM64_X1, 51) + gpr_x2_arm64, // ARM64_X2, 52) + gpr_x3_arm64, // ARM64_X3, 53) + gpr_x4_arm64, // ARM64_X4, 54) + gpr_x5_arm64, // ARM64_X5, 55) + gpr_x6_arm64, // ARM64_X6, 56) + gpr_x7_arm64, // ARM64_X7, 57) + gpr_x8_arm64, // ARM64_X8, 58) + gpr_x9_arm64, // ARM64_X9, 59) + gpr_x10_arm64, // ARM64_X10, 60) + gpr_x11_arm64, // ARM64_X11, 61) + gpr_x12_arm64, // ARM64_X12, 62) + gpr_x13_arm64, // ARM64_X13, 63) + gpr_x14_arm64, // ARM64_X14, 64) + gpr_x15_arm64, // ARM64_X15, 65) + gpr_x16_arm64, // ARM64_X16, 66) + gpr_x17_arm64, // ARM64_X17, 67) + gpr_x18_arm64, // ARM64_X18, 68) + gpr_x19_arm64, // ARM64_X19, 69) + gpr_x20_arm64, // ARM64_X20, 70) + gpr_x21_arm64, // ARM64_X21, 71) + gpr_x22_arm64, // ARM64_X22, 72) + gpr_x23_arm64, // ARM64_X23, 73) + gpr_x24_arm64, // ARM64_X24, 74) + gpr_x25_arm64, // ARM64_X25, 75) + gpr_x26_arm64, // ARM64_X26, 76) + gpr_x27_arm64, // ARM64_X27, 77) + gpr_x28_arm64, // ARM64_X28, 78) + gpr_fp_arm64, // ARM64_FP, 79) + gpr_lr_arm64, // ARM64_LR, 80) + gpr_sp_arm64, // ARM64_SP, 81) + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + gpr_cpsr_arm64, // ARM64_NZCV, 90) + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + fpu_s0_arm64, // (ARM64_S0, 100) + fpu_s1_arm64, // (ARM64_S1, 101) + fpu_s2_arm64, // (ARM64_S2, 102) + fpu_s3_arm64, // (ARM64_S3, 103) + fpu_s4_arm64, // (ARM64_S4, 104) + fpu_s5_arm64, // (ARM64_S5, 105) + fpu_s6_arm64, // (ARM64_S6, 106) + fpu_s7_arm64, // (ARM64_S7, 107) + fpu_s8_arm64, // (ARM64_S8, 108) + fpu_s9_arm64, // (ARM64_S9, 109) + fpu_s10_arm64, // (ARM64_S10, 110) + fpu_s11_arm64, // (ARM64_S11, 111) + fpu_s12_arm64, // (ARM64_S12, 112) + fpu_s13_arm64, // (ARM64_S13, 113) + fpu_s14_arm64, // (ARM64_S14, 114) + fpu_s15_arm64, // (ARM64_S15, 115) + fpu_s16_arm64, // (ARM64_S16, 116) + fpu_s17_arm64, // (ARM64_S17, 117) + fpu_s18_arm64, // (ARM64_S18, 118) + fpu_s19_arm64, // (ARM64_S19, 119) + fpu_s20_arm64, // (ARM64_S20, 120) + fpu_s21_arm64, // (ARM64_S21, 121) + fpu_s22_arm64, // (ARM64_S22, 122) + fpu_s23_arm64, // (ARM64_S23, 123) + fpu_s24_arm64, // (ARM64_S24, 124) + fpu_s25_arm64, // (ARM64_S25, 125) + fpu_s26_arm64, // (ARM64_S26, 126) + fpu_s27_arm64, // (ARM64_S27, 127) + fpu_s28_arm64, // (ARM64_S28, 128) + fpu_s29_arm64, // (ARM64_S29, 129) + fpu_s30_arm64, // (ARM64_S30, 130) + fpu_s31_arm64, // (ARM64_S31, 131) + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + fpu_d0_arm64, // (ARM64_D0, 140) + fpu_d1_arm64, // (ARM64_D1, 141) + fpu_d2_arm64, // (ARM64_D2, 142) + fpu_d3_arm64, // (ARM64_D3, 143) + fpu_d4_arm64, // (ARM64_D4, 144) + fpu_d5_arm64, // (ARM64_D5, 145) + fpu_d6_arm64, // (ARM64_D6, 146) + fpu_d7_arm64, // (ARM64_D7, 147) + fpu_d8_arm64, // (ARM64_D8, 148) + fpu_d9_arm64, // (ARM64_D9, 149) + fpu_d10_arm64, // (ARM64_D10, 150) + fpu_d11_arm64, // (ARM64_D11, 151) + fpu_d12_arm64, // (ARM64_D12, 152) + fpu_d13_arm64, // (ARM64_D13, 153) + fpu_d14_arm64, // (ARM64_D14, 154) + fpu_d15_arm64, // (ARM64_D15, 155) + fpu_d16_arm64, // (ARM64_D16, 156) + fpu_d17_arm64, // (ARM64_D17, 157) + fpu_d18_arm64, // (ARM64_D18, 158) + fpu_d19_arm64, // (ARM64_D19, 159) + fpu_d20_arm64, // (ARM64_D20, 160) + fpu_d21_arm64, // (ARM64_D21, 161) + fpu_d22_arm64, // (ARM64_D22, 162) + fpu_d23_arm64, // (ARM64_D23, 163) + fpu_d24_arm64, // (ARM64_D24, 164) + fpu_d25_arm64, // (ARM64_D25, 165) + fpu_d26_arm64, // (ARM64_D26, 166) + fpu_d27_arm64, // (ARM64_D27, 167) + fpu_d28_arm64, // (ARM64_D28, 168) + fpu_d29_arm64, // (ARM64_D29, 169) + fpu_d30_arm64, // (ARM64_D30, 170) + fpu_d31_arm64, // (ARM64_D31, 171) + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + fpu_v0_arm64, // (ARM64_Q0, 180) + fpu_v1_arm64, // (ARM64_Q1, 181) + fpu_v2_arm64, // (ARM64_Q2, 182) + fpu_v3_arm64, // (ARM64_Q3, 183) + fpu_v4_arm64, // (ARM64_Q4, 184) + fpu_v5_arm64, // (ARM64_Q5, 185) + fpu_v6_arm64, // (ARM64_Q6, 186) + fpu_v7_arm64, // (ARM64_Q7, 187) + fpu_v8_arm64, // (ARM64_Q8, 188) + fpu_v9_arm64, // (ARM64_Q9, 189) + fpu_v10_arm64, // (ARM64_Q10, 190) + fpu_v11_arm64, // (ARM64_Q11, 191) + fpu_v12_arm64, // (ARM64_Q12, 192) + fpu_v13_arm64, // (ARM64_Q13, 193) + fpu_v14_arm64, // (ARM64_Q14, 194) + fpu_v15_arm64, // (ARM64_Q15, 195) + fpu_v16_arm64, // (ARM64_Q16, 196) + fpu_v17_arm64, // (ARM64_Q17, 197) + fpu_v18_arm64, // (ARM64_Q18, 198) + fpu_v19_arm64, // (ARM64_Q19, 199) + fpu_v20_arm64, // (ARM64_Q20, 200) + fpu_v21_arm64, // (ARM64_Q21, 201) + fpu_v22_arm64, // (ARM64_Q22, 202) + fpu_v23_arm64, // (ARM64_Q23, 203) + fpu_v24_arm64, // (ARM64_Q24, 204) + fpu_v25_arm64, // (ARM64_Q25, 205) + fpu_v26_arm64, // (ARM64_Q26, 206) + fpu_v27_arm64, // (ARM64_Q27, 207) + fpu_v28_arm64, // (ARM64_Q28, 208) + fpu_v29_arm64, // (ARM64_Q29, 209) + fpu_v30_arm64, // (ARM64_Q30, 210) + fpu_v31_arm64, // (ARM64_Q31, 211) + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + fpu_fpsr_arm64 // ARM64_FPSR, 220) +}; + +static const uint32_t g_code_view_to_lldb_registers_x86[] = { + LLDB_INVALID_REGNUM, // NONE + lldb_al_i386, // AL + lldb_cl_i386, // CL + lldb_dl_i386, // DL + lldb_bl_i386, // BL + lldb_ah_i386, // AH + lldb_ch_i386, // CH + lldb_dh_i386, // DH + lldb_bh_i386, // BH + lldb_ax_i386, // AX + lldb_cx_i386, // CX + lldb_dx_i386, // DX + lldb_bx_i386, // BX + lldb_sp_i386, // SP + lldb_bp_i386, // BP + lldb_si_i386, // SI + lldb_di_i386, // DI + lldb_eax_i386, // EAX + lldb_ecx_i386, // ECX + lldb_edx_i386, // EDX + lldb_ebx_i386, // EBX + lldb_esp_i386, // ESP + lldb_ebp_i386, // EBP + lldb_esi_i386, // ESI + lldb_edi_i386, // EDI + lldb_es_i386, // ES + lldb_cs_i386, // CS + lldb_ss_i386, // SS + lldb_ds_i386, // DS + lldb_fs_i386, // FS + lldb_gs_i386, // GS + LLDB_INVALID_REGNUM, // IP + LLDB_INVALID_REGNUM, // FLAGS + lldb_eip_i386, // EIP + lldb_eflags_i386, // EFLAGS + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, // TEMP + LLDB_INVALID_REGNUM, // TEMPH + LLDB_INVALID_REGNUM, // QUOTE + LLDB_INVALID_REGNUM, // PCDR3 + LLDB_INVALID_REGNUM, // PCDR4 + LLDB_INVALID_REGNUM, // PCDR5 + LLDB_INVALID_REGNUM, // PCDR6 + LLDB_INVALID_REGNUM, // PCDR7 + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, // CR0 + LLDB_INVALID_REGNUM, // CR1 + LLDB_INVALID_REGNUM, // CR2 + LLDB_INVALID_REGNUM, // CR3 + LLDB_INVALID_REGNUM, // CR4 + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + lldb_dr0_i386, // DR0 + lldb_dr1_i386, // DR1 + lldb_dr2_i386, // DR2 + lldb_dr3_i386, // DR3 + lldb_dr4_i386, // DR4 + lldb_dr5_i386, // DR5 + lldb_dr6_i386, // DR6 + lldb_dr7_i386, // DR7 + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, // GDTR + LLDB_INVALID_REGNUM, // GDTL + LLDB_INVALID_REGNUM, // IDTR + LLDB_INVALID_REGNUM, // IDTL + LLDB_INVALID_REGNUM, // LDTR + LLDB_INVALID_REGNUM, // TR + LLDB_INVALID_REGNUM, // PSEUDO1 + LLDB_INVALID_REGNUM, // PSEUDO2 + LLDB_INVALID_REGNUM, // PSEUDO3 + LLDB_INVALID_REGNUM, // PSEUDO4 + LLDB_INVALID_REGNUM, // PSEUDO5 + LLDB_INVALID_REGNUM, // PSEUDO6 + LLDB_INVALID_REGNUM, // PSEUDO7 + LLDB_INVALID_REGNUM, // PSEUDO8 + LLDB_INVALID_REGNUM, // PSEUDO9 + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + lldb_st0_i386, // ST0 + lldb_st1_i386, // ST1 + lldb_st2_i386, // ST2 + lldb_st3_i386, // ST3 + lldb_st4_i386, // ST4 + lldb_st5_i386, // ST5 + lldb_st6_i386, // ST6 + lldb_st7_i386, // ST7 + LLDB_INVALID_REGNUM, // CTRL + LLDB_INVALID_REGNUM, // STAT + LLDB_INVALID_REGNUM, // TAG + LLDB_INVALID_REGNUM, // FPIP + LLDB_INVALID_REGNUM, // FPCS + LLDB_INVALID_REGNUM, // FPDO + LLDB_INVALID_REGNUM, // FPDS + LLDB_INVALID_REGNUM, // ISEM + LLDB_INVALID_REGNUM, // FPEIP + LLDB_INVALID_REGNUM, // FPEDO + lldb_mm0_i386, // MM0 + lldb_mm1_i386, // MM1 + lldb_mm2_i386, // MM2 + lldb_mm3_i386, // MM3 + lldb_mm4_i386, // MM4 + lldb_mm5_i386, // MM5 + lldb_mm6_i386, // MM6 + lldb_mm7_i386, // MM7 + lldb_xmm0_i386, // XMM0 + lldb_xmm1_i386, // XMM1 + lldb_xmm2_i386, // XMM2 + lldb_xmm3_i386, // XMM3 + lldb_xmm4_i386, // XMM4 + lldb_xmm5_i386, // XMM5 + lldb_xmm6_i386, // XMM6 + lldb_xmm7_i386 // XMM7 +}; + +static const uint32_t g_code_view_to_lldb_registers_x86_64[] = { + LLDB_INVALID_REGNUM, // NONE + lldb_al_x86_64, // AL + lldb_cl_x86_64, // CL + lldb_dl_x86_64, // DL + lldb_bl_x86_64, // BL + lldb_ah_x86_64, // AH + lldb_ch_x86_64, // CH + lldb_dh_x86_64, // DH + lldb_bh_x86_64, // BH + lldb_ax_x86_64, // AX + lldb_cx_x86_64, // CX + lldb_dx_x86_64, // DX + lldb_bx_x86_64, // BX + lldb_sp_x86_64, // SP + lldb_bp_x86_64, // BP + lldb_si_x86_64, // SI + lldb_di_x86_64, // DI + lldb_eax_x86_64, // EAX + lldb_ecx_x86_64, // ECX + lldb_edx_x86_64, // EDX + lldb_ebx_x86_64, // EBX + lldb_esp_x86_64, // ESP + lldb_ebp_x86_64, // EBP + lldb_esi_x86_64, // ESI + lldb_edi_x86_64, // EDI + lldb_es_x86_64, // ES + lldb_cs_x86_64, // CS + lldb_ss_x86_64, // SS + lldb_ds_x86_64, // DS + lldb_fs_x86_64, // FS + lldb_gs_x86_64, // GS + LLDB_INVALID_REGNUM, // IP + LLDB_INVALID_REGNUM, // FLAGS + LLDB_INVALID_REGNUM, // EIP + LLDB_INVALID_REGNUM, // EFLAGS + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, // TEMP + LLDB_INVALID_REGNUM, // TEMPH + LLDB_INVALID_REGNUM, // QUOTE + LLDB_INVALID_REGNUM, // PCDR3 + LLDB_INVALID_REGNUM, // PCDR4 + LLDB_INVALID_REGNUM, // PCDR5 + LLDB_INVALID_REGNUM, // PCDR6 + LLDB_INVALID_REGNUM, // PCDR7 + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, // CR0 + LLDB_INVALID_REGNUM, // CR1 + LLDB_INVALID_REGNUM, // CR2 + LLDB_INVALID_REGNUM, // CR3 + LLDB_INVALID_REGNUM, // CR4 + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + lldb_dr0_x86_64, // DR0 + lldb_dr1_x86_64, // DR1 + lldb_dr2_x86_64, // DR2 + lldb_dr3_x86_64, // DR3 + lldb_dr4_x86_64, // DR4 + lldb_dr5_x86_64, // DR5 + lldb_dr6_x86_64, // DR6 + lldb_dr7_x86_64, // DR7 + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, // GDTR + LLDB_INVALID_REGNUM, // GDTL + LLDB_INVALID_REGNUM, // IDTR + LLDB_INVALID_REGNUM, // IDTL + LLDB_INVALID_REGNUM, // LDTR + LLDB_INVALID_REGNUM, // TR + LLDB_INVALID_REGNUM, // PSEUDO1 + LLDB_INVALID_REGNUM, // PSEUDO2 + LLDB_INVALID_REGNUM, // PSEUDO3 + LLDB_INVALID_REGNUM, // PSEUDO4 + LLDB_INVALID_REGNUM, // PSEUDO5 + LLDB_INVALID_REGNUM, // PSEUDO6 + LLDB_INVALID_REGNUM, // PSEUDO7 + LLDB_INVALID_REGNUM, // PSEUDO8 + LLDB_INVALID_REGNUM, // PSEUDO9 + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + lldb_st0_x86_64, // ST0 + lldb_st1_x86_64, // ST1 + lldb_st2_x86_64, // ST2 + lldb_st3_x86_64, // ST3 + lldb_st4_x86_64, // ST4 + lldb_st5_x86_64, // ST5 + lldb_st6_x86_64, // ST6 + lldb_st7_x86_64, // ST7 + LLDB_INVALID_REGNUM, // CTRL + LLDB_INVALID_REGNUM, // STAT + LLDB_INVALID_REGNUM, // TAG + LLDB_INVALID_REGNUM, // FPIP + LLDB_INVALID_REGNUM, // FPCS + LLDB_INVALID_REGNUM, // FPDO + LLDB_INVALID_REGNUM, // FPDS + LLDB_INVALID_REGNUM, // ISEM + LLDB_INVALID_REGNUM, // FPEIP + LLDB_INVALID_REGNUM, // FPEDO + lldb_mm0_x86_64, // MM0 + lldb_mm1_x86_64, // MM1 + lldb_mm2_x86_64, // MM2 + lldb_mm3_x86_64, // MM3 + lldb_mm4_x86_64, // MM4 + lldb_mm5_x86_64, // MM5 + lldb_mm6_x86_64, // MM6 + lldb_mm7_x86_64, // MM7 + lldb_xmm0_x86_64, // XMM0 + lldb_xmm1_x86_64, // XMM1 + lldb_xmm2_x86_64, // XMM2 + lldb_xmm3_x86_64, // XMM3 + lldb_xmm4_x86_64, // XMM4 + lldb_xmm5_x86_64, // XMM5 + lldb_xmm6_x86_64, // XMM6 + lldb_xmm7_x86_64, // XMM7 + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, + lldb_mxcsr_x86_64, // MXCSR + LLDB_INVALID_REGNUM, // EDXEAX + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, // EMM0L + LLDB_INVALID_REGNUM, // EMM1L + LLDB_INVALID_REGNUM, // EMM2L + LLDB_INVALID_REGNUM, // EMM3L + LLDB_INVALID_REGNUM, // EMM4L + LLDB_INVALID_REGNUM, // EMM5L + LLDB_INVALID_REGNUM, // EMM6L + LLDB_INVALID_REGNUM, // EMM7L + LLDB_INVALID_REGNUM, // EMM0H + LLDB_INVALID_REGNUM, // EMM1H + LLDB_INVALID_REGNUM, // EMM2H + LLDB_INVALID_REGNUM, // EMM3H + LLDB_INVALID_REGNUM, // EMM4H + LLDB_INVALID_REGNUM, // EMM5H + LLDB_INVALID_REGNUM, // EMM6H + LLDB_INVALID_REGNUM, // EMM7H + LLDB_INVALID_REGNUM, // MM00 + LLDB_INVALID_REGNUM, // MM01 + LLDB_INVALID_REGNUM, // MM10 + LLDB_INVALID_REGNUM, // MM11 + LLDB_INVALID_REGNUM, // MM20 + LLDB_INVALID_REGNUM, // MM21 + LLDB_INVALID_REGNUM, // MM30 + LLDB_INVALID_REGNUM, // MM31 + LLDB_INVALID_REGNUM, // MM40 + LLDB_INVALID_REGNUM, // MM41 + LLDB_INVALID_REGNUM, // MM50 + LLDB_INVALID_REGNUM, // MM51 + LLDB_INVALID_REGNUM, // MM60 + LLDB_INVALID_REGNUM, // MM61 + LLDB_INVALID_REGNUM, // MM70 + LLDB_INVALID_REGNUM, // MM71 + lldb_xmm8_x86_64, // XMM8 + lldb_xmm9_x86_64, // XMM9 + lldb_xmm10_x86_64, // XMM10 + lldb_xmm11_x86_64, // XMM11 + lldb_xmm12_x86_64, // XMM12 + lldb_xmm13_x86_64, // XMM13 + lldb_xmm14_x86_64, // XMM14 + lldb_xmm15_x86_64, // XMM15 + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, + lldb_sil_x86_64, // SIL + lldb_dil_x86_64, // DIL + lldb_bpl_x86_64, // BPL + lldb_spl_x86_64, // SPL + lldb_rax_x86_64, // RAX + lldb_rbx_x86_64, // RBX + lldb_rcx_x86_64, // RCX + lldb_rdx_x86_64, // RDX + lldb_rsi_x86_64, // RSI + lldb_rdi_x86_64, // RDI + lldb_rbp_x86_64, // RBP + lldb_rsp_x86_64, // RSP + lldb_r8_x86_64, // R8 + lldb_r9_x86_64, // R9 + lldb_r10_x86_64, // R10 + lldb_r11_x86_64, // R11 + lldb_r12_x86_64, // R12 + lldb_r13_x86_64, // R13 + lldb_r14_x86_64, // R14 + lldb_r15_x86_64, // R15 + lldb_r8l_x86_64, // R8B + lldb_r9l_x86_64, // R9B + lldb_r10l_x86_64, // R10B + lldb_r11l_x86_64, // R11B + lldb_r12l_x86_64, // R12B + lldb_r13l_x86_64, // R13B + lldb_r14l_x86_64, // R14B + lldb_r15l_x86_64, // R15B + lldb_r8w_x86_64, // R8W + lldb_r9w_x86_64, // R9W + lldb_r10w_x86_64, // R10W + lldb_r11w_x86_64, // R11W + lldb_r12w_x86_64, // R12W + lldb_r13w_x86_64, // R13W + lldb_r14w_x86_64, // R14W + lldb_r15w_x86_64, // R15W + lldb_r8d_x86_64, // R8D + lldb_r9d_x86_64, // R9D + lldb_r10d_x86_64, // R10D + lldb_r11d_x86_64, // R11D + lldb_r12d_x86_64, // R12D + lldb_r13d_x86_64, // R13D + lldb_r14d_x86_64, // R14D + lldb_r15d_x86_64, // R15D + lldb_ymm0_x86_64, // AMD64_YMM0 + lldb_ymm1_x86_64, // AMD64_YMM1 + lldb_ymm2_x86_64, // AMD64_YMM2 + lldb_ymm3_x86_64, // AMD64_YMM3 + lldb_ymm4_x86_64, // AMD64_YMM4 + lldb_ymm5_x86_64, // AMD64_YMM5 + lldb_ymm6_x86_64, // AMD64_YMM6 + lldb_ymm7_x86_64, // AMD64_YMM7 + lldb_ymm8_x86_64, // AMD64_YMM8 + lldb_ymm9_x86_64, // AMD64_YMM9 + lldb_ymm10_x86_64, // AMD64_YMM10 + lldb_ymm11_x86_64, // AMD64_YMM11 + lldb_ymm12_x86_64, // AMD64_YMM12 + lldb_ymm13_x86_64, // AMD64_YMM13 + lldb_ymm14_x86_64, // AMD64_YMM14 + lldb_ymm15_x86_64, // AMD64_YMM15 + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + lldb_bnd0_x86_64, // BND0 + lldb_bnd1_x86_64, // BND1 + lldb_bnd2_x86_64 // BND2 +}; + +uint32_t lldb_private::npdb::GetLLDBRegisterNumber( + llvm::Triple::ArchType arch_type, llvm::codeview::RegisterId register_id) { + switch (arch_type) { + case llvm::Triple::aarch64: + if (static_cast<uint16_t>(register_id) < + sizeof(g_code_view_to_lldb_registers_arm64) / + sizeof(g_code_view_to_lldb_registers_arm64[0])) + return g_code_view_to_lldb_registers_arm64[static_cast<uint16_t>( + register_id)]; + + return LLDB_INVALID_REGNUM; + case llvm::Triple::x86: + if (static_cast<uint16_t>(register_id) < + sizeof(g_code_view_to_lldb_registers_x86) / + sizeof(g_code_view_to_lldb_registers_x86[0])) + return g_code_view_to_lldb_registers_x86[static_cast<uint16_t>( + register_id)]; + + switch (register_id) { + case llvm::codeview::RegisterId::MXCSR: + return lldb_mxcsr_i386; + case llvm::codeview::RegisterId::BND0: + return lldb_bnd0_i386; + case llvm::codeview::RegisterId::BND1: + return lldb_bnd1_i386; + case llvm::codeview::RegisterId::BND2: + return lldb_bnd2_i386; + default: + return LLDB_INVALID_REGNUM; + } + case llvm::Triple::x86_64: + if (static_cast<uint16_t>(register_id) < + sizeof(g_code_view_to_lldb_registers_x86_64) / + sizeof(g_code_view_to_lldb_registers_x86_64[0])) + return g_code_view_to_lldb_registers_x86_64[static_cast<uint16_t>( + register_id)]; + + return LLDB_INVALID_REGNUM; + default: + return LLDB_INVALID_REGNUM; + } +} + +uint32_t +lldb_private::npdb::GetRegisterSize(llvm::codeview::RegisterId register_id) { + switch(register_id) { + case llvm::codeview::RegisterId::AL: + case llvm::codeview::RegisterId::BL: + case llvm::codeview::RegisterId::CL: + case llvm::codeview::RegisterId::DL: + case llvm::codeview::RegisterId::AH: + case llvm::codeview::RegisterId::BH: + case llvm::codeview::RegisterId::CH: + case llvm::codeview::RegisterId::DH: + case llvm::codeview::RegisterId::SIL: + case llvm::codeview::RegisterId::DIL: + case llvm::codeview::RegisterId::BPL: + case llvm::codeview::RegisterId::SPL: + case llvm::codeview::RegisterId::R8B: + case llvm::codeview::RegisterId::R9B: + case llvm::codeview::RegisterId::R10B: + case llvm::codeview::RegisterId::R11B: + case llvm::codeview::RegisterId::R12B: + case llvm::codeview::RegisterId::R13B: + case llvm::codeview::RegisterId::R14B: + case llvm::codeview::RegisterId::R15B: + return 1; + case llvm::codeview::RegisterId::AX: + case llvm::codeview::RegisterId::BX: + case llvm::codeview::RegisterId::CX: + case llvm::codeview::RegisterId::DX: + case llvm::codeview::RegisterId::SP: + case llvm::codeview::RegisterId::BP: + case llvm::codeview::RegisterId::SI: + case llvm::codeview::RegisterId::DI: + case llvm::codeview::RegisterId::R8W: + case llvm::codeview::RegisterId::R9W: + case llvm::codeview::RegisterId::R10W: + case llvm::codeview::RegisterId::R11W: + case llvm::codeview::RegisterId::R12W: + case llvm::codeview::RegisterId::R13W: + case llvm::codeview::RegisterId::R14W: + case llvm::codeview::RegisterId::R15W: + return 2; + case llvm::codeview::RegisterId::EAX: + case llvm::codeview::RegisterId::EBX: + case llvm::codeview::RegisterId::ECX: + case llvm::codeview::RegisterId::EDX: + case llvm::codeview::RegisterId::ESP: + case llvm::codeview::RegisterId::EBP: + case llvm::codeview::RegisterId::ESI: + case llvm::codeview::RegisterId::EDI: + case llvm::codeview::RegisterId::R8D: + case llvm::codeview::RegisterId::R9D: + case llvm::codeview::RegisterId::R10D: + case llvm::codeview::RegisterId::R11D: + case llvm::codeview::RegisterId::R12D: + case llvm::codeview::RegisterId::R13D: + case llvm::codeview::RegisterId::R14D: + case llvm::codeview::RegisterId::R15D: + return 4; + case llvm::codeview::RegisterId::RAX: + case llvm::codeview::RegisterId::RBX: + case llvm::codeview::RegisterId::RCX: + case llvm::codeview::RegisterId::RDX: + case llvm::codeview::RegisterId::RSI: + case llvm::codeview::RegisterId::RDI: + case llvm::codeview::RegisterId::RBP: + case llvm::codeview::RegisterId::RSP: + case llvm::codeview::RegisterId::R8: + case llvm::codeview::RegisterId::R9: + case llvm::codeview::RegisterId::R10: + case llvm::codeview::RegisterId::R11: + case llvm::codeview::RegisterId::R12: + case llvm::codeview::RegisterId::R13: + case llvm::codeview::RegisterId::R14: + case llvm::codeview::RegisterId::R15: + return 8; + case llvm::codeview::RegisterId::XMM0: + case llvm::codeview::RegisterId::XMM1: + case llvm::codeview::RegisterId::XMM2: + case llvm::codeview::RegisterId::XMM3: + case llvm::codeview::RegisterId::XMM4: + case llvm::codeview::RegisterId::XMM5: + case llvm::codeview::RegisterId::XMM6: + case llvm::codeview::RegisterId::XMM7: + case llvm::codeview::RegisterId::XMM8: + case llvm::codeview::RegisterId::XMM9: + case llvm::codeview::RegisterId::XMM10: + case llvm::codeview::RegisterId::XMM11: + case llvm::codeview::RegisterId::XMM12: + case llvm::codeview::RegisterId::XMM13: + case llvm::codeview::RegisterId::XMM14: + case llvm::codeview::RegisterId::XMM15: + return 16; + default: + return 0; + } +} diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/CodeViewRegisterMapping.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/CodeViewRegisterMapping.h new file mode 100644 index 000000000000..4717fdcf11e3 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/CodeViewRegisterMapping.h @@ -0,0 +1,25 @@ +//===-- CodeViewRegisterMapping.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_SOURCE_PLUGINS_SYMBOLFILE_NATIVEPDB_CODEVIEWREGISTERMAPPING_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_NATIVEPDB_CODEVIEWREGISTERMAPPING_H + +#include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/TargetParser/Triple.h" + +namespace lldb_private { +namespace npdb { + +uint32_t GetLLDBRegisterNumber(llvm::Triple::ArchType arch_type, + llvm::codeview::RegisterId register_id); +uint32_t GetRegisterSize(llvm::codeview::RegisterId register_id); + +} // namespace npdb +} // namespace lldb_private + +#endif diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/CompileUnitIndex.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/CompileUnitIndex.cpp new file mode 100644 index 000000000000..25d04f999ad6 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/CompileUnitIndex.cpp @@ -0,0 +1,248 @@ +//===-- CompileUnitIndex.cpp ----------------------------------------------===// +// +// 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 "CompileUnitIndex.h" + +#include "PdbIndex.h" +#include "PdbUtil.h" + +#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" +#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h" +#include "llvm/DebugInfo/CodeView/TypeDeserializer.h" +#include "llvm/DebugInfo/MSF/MappedBlockStream.h" +#include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptor.h" +#include "llvm/DebugInfo/PDB/Native/DbiStream.h" +#include "llvm/DebugInfo/PDB/Native/InfoStream.h" +#include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h" +#include "llvm/DebugInfo/PDB/Native/NamedStreamMap.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" +#include "llvm/Support/Path.h" + +#include "lldb/Utility/LLDBAssert.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::npdb; +using namespace llvm::codeview; +using namespace llvm::pdb; + +static bool IsMainFile(llvm::StringRef main, llvm::StringRef other) { + if (main == other) + return true; + + // If the files refer to the local file system, we can just ask the file + // system if they're equivalent. But if the source isn't present on disk + // then we still want to try. + if (llvm::sys::fs::equivalent(main, other)) + return true; + + llvm::SmallString<64> normalized(other); + llvm::sys::path::native(normalized); + return main.equals_insensitive(normalized); +} + +static void ParseCompile3(const CVSymbol &sym, CompilandIndexItem &cci) { + cci.m_compile_opts.emplace(); + llvm::cantFail( + SymbolDeserializer::deserializeAs<Compile3Sym>(sym, *cci.m_compile_opts)); +} + +static void ParseObjname(const CVSymbol &sym, CompilandIndexItem &cci) { + cci.m_obj_name.emplace(); + llvm::cantFail( + SymbolDeserializer::deserializeAs<ObjNameSym>(sym, *cci.m_obj_name)); +} + +static void ParseBuildInfo(PdbIndex &index, const CVSymbol &sym, + CompilandIndexItem &cci) { + BuildInfoSym bis(SymbolRecordKind::BuildInfoSym); + llvm::cantFail(SymbolDeserializer::deserializeAs<BuildInfoSym>(sym, bis)); + + // S_BUILDINFO just points to an LF_BUILDINFO in the IPI stream. Let's do + // a little extra work to pull out the LF_BUILDINFO. + LazyRandomTypeCollection &types = index.ipi().typeCollection(); + std::optional<CVType> cvt = types.tryGetType(bis.BuildId); + + if (!cvt || cvt->kind() != LF_BUILDINFO) + return; + + BuildInfoRecord bir; + llvm::cantFail(TypeDeserializer::deserializeAs<BuildInfoRecord>(*cvt, bir)); + cci.m_build_info.assign(bir.ArgIndices.begin(), bir.ArgIndices.end()); +} + +static void ParseExtendedInfo(PdbIndex &index, CompilandIndexItem &item) { + const CVSymbolArray &syms = item.m_debug_stream.getSymbolArray(); + + // This is a private function, it shouldn't be called if the information + // has already been parsed. + lldbassert(!item.m_obj_name); + lldbassert(!item.m_compile_opts); + lldbassert(item.m_build_info.empty()); + + // We're looking for 3 things. S_COMPILE3, S_OBJNAME, and S_BUILDINFO. + int found = 0; + for (const CVSymbol &sym : syms) { + switch (sym.kind()) { + case S_COMPILE3: + ParseCompile3(sym, item); + break; + case S_OBJNAME: + ParseObjname(sym, item); + break; + case S_BUILDINFO: + ParseBuildInfo(index, sym, item); + break; + default: + continue; + } + if (++found >= 3) + break; + } +} + +static void ParseInlineeLineTableForCompileUnit(CompilandIndexItem &item) { + for (const auto &ss : item.m_debug_stream.getSubsectionsArray()) { + if (ss.kind() != DebugSubsectionKind::InlineeLines) + continue; + + DebugInlineeLinesSubsectionRef inlinee_lines; + llvm::BinaryStreamReader reader(ss.getRecordData()); + if (llvm::Error error = inlinee_lines.initialize(reader)) { + consumeError(std::move(error)); + continue; + } + + for (const InlineeSourceLine &Line : inlinee_lines) { + item.m_inline_map[Line.Header->Inlinee] = Line; + } + } +} + +CompilandIndexItem::CompilandIndexItem( + PdbCompilandId id, llvm::pdb::ModuleDebugStreamRef debug_stream, + llvm::pdb::DbiModuleDescriptor descriptor) + : m_id(id), m_debug_stream(std::move(debug_stream)), + m_module_descriptor(std::move(descriptor)) {} + +CompilandIndexItem &CompileUnitIndex::GetOrCreateCompiland(uint16_t modi) { + auto result = m_comp_units.try_emplace(modi, nullptr); + if (!result.second) + return *result.first->second; + + // Find the module list and load its debug information stream and cache it + // since we need to use it for almost all interesting operations. + const DbiModuleList &modules = m_index.dbi().modules(); + llvm::pdb::DbiModuleDescriptor descriptor = modules.getModuleDescriptor(modi); + uint16_t stream = descriptor.getModuleStreamIndex(); + std::unique_ptr<llvm::msf::MappedBlockStream> stream_data = + m_index.pdb().createIndexedStream(stream); + + + std::unique_ptr<CompilandIndexItem>& cci = result.first->second; + + if (!stream_data) { + llvm::pdb::ModuleDebugStreamRef debug_stream(descriptor, nullptr); + cci = std::make_unique<CompilandIndexItem>(PdbCompilandId{ modi }, debug_stream, std::move(descriptor)); + return *cci; + } + + llvm::pdb::ModuleDebugStreamRef debug_stream(descriptor, + std::move(stream_data)); + + cantFail(debug_stream.reload()); + + cci = std::make_unique<CompilandIndexItem>( + PdbCompilandId{modi}, std::move(debug_stream), std::move(descriptor)); + ParseExtendedInfo(m_index, *cci); + ParseInlineeLineTableForCompileUnit(*cci); + + auto strings = m_index.pdb().getStringTable(); + if (strings) { + cci->m_strings.initialize(cci->m_debug_stream.getSubsectionsArray()); + cci->m_strings.setStrings(strings->getStringTable()); + } else { + consumeError(strings.takeError()); + } + + // We want the main source file to always comes first. Note that we can't + // just push_back the main file onto the front because `GetMainSourceFile` + // computes it in such a way that it doesn't own the resulting memory. So we + // have to iterate the module file list comparing each one to the main file + // name until we find it, and we can cache that one since the memory is backed + // by a contiguous chunk inside the mapped PDB. + llvm::SmallString<64> main_file = GetMainSourceFile(*cci); + std::string s = std::string(main_file.str()); + llvm::sys::path::native(main_file); + + uint32_t file_count = modules.getSourceFileCount(modi); + cci->m_file_list.reserve(file_count); + bool found_main_file = false; + for (llvm::StringRef file : modules.source_files(modi)) { + if (!found_main_file && IsMainFile(main_file, file)) { + cci->m_file_list.insert(cci->m_file_list.begin(), file); + found_main_file = true; + continue; + } + cci->m_file_list.push_back(file); + } + + return *cci; +} + +const CompilandIndexItem *CompileUnitIndex::GetCompiland(uint16_t modi) const { + auto iter = m_comp_units.find(modi); + if (iter == m_comp_units.end()) + return nullptr; + return iter->second.get(); +} + +CompilandIndexItem *CompileUnitIndex::GetCompiland(uint16_t modi) { + auto iter = m_comp_units.find(modi); + if (iter == m_comp_units.end()) + return nullptr; + return iter->second.get(); +} + +llvm::SmallString<64> +CompileUnitIndex::GetMainSourceFile(const CompilandIndexItem &item) const { + // LF_BUILDINFO contains a list of arg indices which point to LF_STRING_ID + // records in the IPI stream. The order of the arg indices is as follows: + // [0] - working directory where compiler was invoked. + // [1] - absolute path to compiler binary + // [2] - source file name + // [3] - path to compiler generated PDB (the /Zi PDB, although this entry gets + // added even when using /Z7) + // [4] - full command line invocation. + // + // We need to form the path [0]\[2] to generate the full path to the main + // file.source + if (item.m_build_info.size() < 3) + return {""}; + + LazyRandomTypeCollection &types = m_index.ipi().typeCollection(); + + StringIdRecord working_dir; + StringIdRecord file_name; + CVType dir_cvt = types.getType(item.m_build_info[0]); + CVType file_cvt = types.getType(item.m_build_info[2]); + llvm::cantFail( + TypeDeserializer::deserializeAs<StringIdRecord>(dir_cvt, working_dir)); + llvm::cantFail( + TypeDeserializer::deserializeAs<StringIdRecord>(file_cvt, file_name)); + + llvm::sys::path::Style style = working_dir.String.starts_with("/") + ? llvm::sys::path::Style::posix + : llvm::sys::path::Style::windows; + if (llvm::sys::path::is_absolute(file_name.String, style)) + return file_name.String; + + llvm::SmallString<64> absolute_path = working_dir.String; + llvm::sys::path::append(absolute_path, file_name.String); + return absolute_path; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/CompileUnitIndex.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/CompileUnitIndex.h new file mode 100644 index 000000000000..3e6766a204dd --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/CompileUnitIndex.h @@ -0,0 +1,108 @@ +//===-- CompileUnitIndex.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_SOURCE_PLUGINS_SYMBOLFILE_NATIVEPDB_COMPILEUNITINDEX_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_NATIVEPDB_COMPILEUNITINDEX_H + +#include "lldb/Utility/RangeMap.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/IntervalMap.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/DebugInfo/CodeView/DebugInlineeLinesSubsection.h" +#include "llvm/DebugInfo/CodeView/StringsAndChecksums.h" +#include "llvm/DebugInfo/CodeView/SymbolRecord.h" +#include "llvm/DebugInfo/CodeView/TypeIndex.h" +#include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptor.h" +#include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h" +#include "llvm/DebugInfo/PDB/PDBTypes.h" + +#include "PdbSymUid.h" + +#include <map> +#include <memory> +#include <optional> + +namespace lldb_private { + +namespace npdb { +class PdbIndex; + +/// Represents a single compile unit. This class is useful for collecting the +/// important accessors and information about a compile unit from disparate +/// parts of the PDB into a single place, simplifying acess to compile unit +/// information for the callers. +struct CompilandIndexItem { + CompilandIndexItem(PdbCompilandId m_id, + llvm::pdb::ModuleDebugStreamRef debug_stream, + llvm::pdb::DbiModuleDescriptor descriptor); + + // index of this compile unit. + PdbCompilandId m_id; + + // debug stream. + llvm::pdb::ModuleDebugStreamRef m_debug_stream; + + // dbi module descriptor. + llvm::pdb::DbiModuleDescriptor m_module_descriptor; + + llvm::codeview::StringsAndChecksumsRef m_strings; + + // List of files which contribute to this compiland. + std::vector<llvm::StringRef> m_file_list; + + // Maps virtual address to global symbol id, which can then be used to + // locate the exact compile unit and offset of the symbol. Note that this + // is intentionally an ordered map so that we can find all symbols up to a + // given starting address. + std::map<lldb::addr_t, PdbSymUid> m_symbols_by_va; + + // S_COMPILE3 sym describing compilation settings for the module. + std::optional<llvm::codeview::Compile3Sym> m_compile_opts; + + // S_OBJNAME sym describing object name. + std::optional<llvm::codeview::ObjNameSym> m_obj_name; + + // LF_BUILDINFO sym describing source file name, working directory, + // command line, etc. This usually contains exactly 5 items which + // are references to other strings. + llvm::SmallVector<llvm::codeview::TypeIndex, 5> m_build_info; + + // Inlinee lines table in this compile unit. + std::map<llvm::codeview::TypeIndex, llvm::codeview::InlineeSourceLine> + m_inline_map; + + // It's the line table parsed from DEBUG_S_LINES sections, mapping the file + // address range to file index and source line number. + using GlobalLineTable = + lldb_private::RangeDataVector<lldb::addr_t, uint32_t, + std::pair<uint32_t, uint32_t>>; + GlobalLineTable m_global_line_table; +}; + +/// Indexes information about all compile units. This is really just a map of +/// global compile unit index to |CompilandIndexItem| structures. +class CompileUnitIndex { + PdbIndex &m_index; + llvm::DenseMap<uint16_t, std::unique_ptr<CompilandIndexItem>> m_comp_units; + +public: + explicit CompileUnitIndex(PdbIndex &index) : m_index(index) {} + + CompilandIndexItem &GetOrCreateCompiland(uint16_t modi); + + const CompilandIndexItem *GetCompiland(uint16_t modi) const; + + CompilandIndexItem *GetCompiland(uint16_t modi); + + llvm::SmallString<64> GetMainSourceFile(const CompilandIndexItem &item) const; +}; +} // namespace npdb +} // namespace lldb_private + +#endif diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/DWARFLocationExpression.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/DWARFLocationExpression.cpp new file mode 100644 index 000000000000..75adf7302f00 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/DWARFLocationExpression.cpp @@ -0,0 +1,300 @@ +//===-- DWARFLocationExpression.cpp ---------------------------------------===// +// +// 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 "DWARFLocationExpression.h" + +#include "lldb/Core/Module.h" +#include "lldb/Core/Section.h" +#include "lldb/Expression/DWARFExpression.h" +#include "lldb/Utility/ArchSpec.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/StreamBuffer.h" + +#include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/DebugInfo/CodeView/TypeDeserializer.h" +#include "llvm/DebugInfo/CodeView/TypeIndex.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" +#include "llvm/Support/Endian.h" + +#include "PdbUtil.h" +#include "CodeViewRegisterMapping.h" +#include "PdbFPOProgramToDWARFExpression.h" +#include <optional> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::npdb; +using namespace llvm::codeview; +using namespace llvm::pdb; + +uint32_t GetGenericRegisterNumber(llvm::codeview::RegisterId register_id) { + if (register_id == llvm::codeview::RegisterId::VFRAME) + return LLDB_REGNUM_GENERIC_FP; + + return LLDB_INVALID_REGNUM; +} + +static uint32_t GetRegisterNumber(llvm::Triple::ArchType arch_type, + llvm::codeview::RegisterId register_id, + RegisterKind ®ister_kind) { + register_kind = eRegisterKindLLDB; + uint32_t reg_num = GetLLDBRegisterNumber(arch_type, register_id); + if (reg_num != LLDB_INVALID_REGNUM) + return reg_num; + + register_kind = eRegisterKindGeneric; + return GetGenericRegisterNumber(register_id); +} + +static bool IsSimpleTypeSignedInteger(SimpleTypeKind kind) { + switch (kind) { + case SimpleTypeKind::Int128: + case SimpleTypeKind::Int64: + case SimpleTypeKind::Int64Quad: + case SimpleTypeKind::Int32: + case SimpleTypeKind::Int32Long: + case SimpleTypeKind::Int16: + case SimpleTypeKind::Int16Short: + case SimpleTypeKind::Float128: + case SimpleTypeKind::Float80: + case SimpleTypeKind::Float64: + case SimpleTypeKind::Float32: + case SimpleTypeKind::Float16: + case SimpleTypeKind::NarrowCharacter: + case SimpleTypeKind::SignedCharacter: + case SimpleTypeKind::SByte: + return true; + default: + return false; + } +} + +static std::pair<size_t, bool> GetIntegralTypeInfo(TypeIndex ti, + TpiStream &tpi) { + if (ti.isSimple()) { + SimpleTypeKind stk = ti.getSimpleKind(); + return {GetTypeSizeForSimpleKind(stk), IsSimpleTypeSignedInteger(stk)}; + } + + CVType cvt = tpi.getType(ti); + switch (cvt.kind()) { + case LF_MODIFIER: { + ModifierRecord mfr; + llvm::cantFail(TypeDeserializer::deserializeAs<ModifierRecord>(cvt, mfr)); + return GetIntegralTypeInfo(mfr.ModifiedType, tpi); + } + case LF_POINTER: { + PointerRecord pr; + llvm::cantFail(TypeDeserializer::deserializeAs<PointerRecord>(cvt, pr)); + return GetIntegralTypeInfo(pr.ReferentType, tpi); + } + case LF_ENUM: { + EnumRecord er; + llvm::cantFail(TypeDeserializer::deserializeAs<EnumRecord>(cvt, er)); + return GetIntegralTypeInfo(er.UnderlyingType, tpi); + } + default: + assert(false && "Type is not integral!"); + return {0, false}; + } +} + +template <typename StreamWriter> +static DWARFExpression MakeLocationExpressionInternal(lldb::ModuleSP module, + StreamWriter &&writer) { + const ArchSpec &architecture = module->GetArchitecture(); + ByteOrder byte_order = architecture.GetByteOrder(); + uint32_t address_size = architecture.GetAddressByteSize(); + uint32_t byte_size = architecture.GetDataByteSize(); + if (byte_order == eByteOrderInvalid || address_size == 0) + return DWARFExpression(); + + RegisterKind register_kind = eRegisterKindDWARF; + StreamBuffer<32> stream(Stream::eBinary, address_size, byte_order); + + if (!writer(stream, register_kind)) + return DWARFExpression(); + + DataBufferSP buffer = + std::make_shared<DataBufferHeap>(stream.GetData(), stream.GetSize()); + DataExtractor extractor(buffer, byte_order, address_size, byte_size); + DWARFExpression result(extractor); + result.SetRegisterKind(register_kind); + + return result; +} + +static bool MakeRegisterBasedLocationExpressionInternal( + Stream &stream, llvm::codeview::RegisterId reg, RegisterKind ®ister_kind, + std::optional<int32_t> relative_offset, lldb::ModuleSP module) { + uint32_t reg_num = GetRegisterNumber(module->GetArchitecture().GetMachine(), + reg, register_kind); + if (reg_num == LLDB_INVALID_REGNUM) + return false; + + if (reg_num > 31) { + llvm::dwarf::LocationAtom base = + relative_offset ? llvm::dwarf::DW_OP_bregx : llvm::dwarf::DW_OP_regx; + stream.PutHex8(base); + stream.PutULEB128(reg_num); + } else { + llvm::dwarf::LocationAtom base = + relative_offset ? llvm::dwarf::DW_OP_breg0 : llvm::dwarf::DW_OP_reg0; + stream.PutHex8(base + reg_num); + } + + if (relative_offset) + stream.PutSLEB128(*relative_offset); + + return true; +} + +static DWARFExpression MakeRegisterBasedLocationExpressionInternal( + llvm::codeview::RegisterId reg, std::optional<int32_t> relative_offset, + lldb::ModuleSP module) { + return MakeLocationExpressionInternal( + module, [&](Stream &stream, RegisterKind ®ister_kind) -> bool { + return MakeRegisterBasedLocationExpressionInternal( + stream, reg, register_kind, relative_offset, module); + }); +} + +DWARFExpression lldb_private::npdb::MakeEnregisteredLocationExpression( + llvm::codeview::RegisterId reg, lldb::ModuleSP module) { + return MakeRegisterBasedLocationExpressionInternal(reg, std::nullopt, module); +} + +DWARFExpression lldb_private::npdb::MakeRegRelLocationExpression( + llvm::codeview::RegisterId reg, int32_t offset, lldb::ModuleSP module) { + return MakeRegisterBasedLocationExpressionInternal(reg, offset, module); +} + +static bool EmitVFrameEvaluationDWARFExpression( + llvm::StringRef program, llvm::Triple::ArchType arch_type, Stream &stream) { + // VFrame value always stored in $TO pseudo-register + return TranslateFPOProgramToDWARFExpression(program, "$T0", arch_type, + stream); +} + +DWARFExpression lldb_private::npdb::MakeVFrameRelLocationExpression( + llvm::StringRef fpo_program, int32_t offset, lldb::ModuleSP module) { + return MakeLocationExpressionInternal( + module, [&](Stream &stream, RegisterKind ®ister_kind) -> bool { + const ArchSpec &architecture = module->GetArchitecture(); + + if (!EmitVFrameEvaluationDWARFExpression(fpo_program, architecture.GetMachine(), + stream)) + return false; + + stream.PutHex8(llvm::dwarf::DW_OP_consts); + stream.PutSLEB128(offset); + stream.PutHex8(llvm::dwarf::DW_OP_plus); + + register_kind = eRegisterKindLLDB; + + return true; + }); +} + +DWARFExpression lldb_private::npdb::MakeGlobalLocationExpression( + uint16_t section, uint32_t offset, ModuleSP module) { + assert(section > 0); + assert(module); + + return MakeLocationExpressionInternal( + module, [&](Stream &stream, RegisterKind ®ister_kind) -> bool { + stream.PutHex8(llvm::dwarf::DW_OP_addr); + + SectionList *section_list = module->GetSectionList(); + assert(section_list); + + auto section_ptr = section_list->FindSectionByID(section); + if (!section_ptr) + return false; + + stream.PutMaxHex64(section_ptr->GetFileAddress() + offset, + stream.GetAddressByteSize(), stream.GetByteOrder()); + + return true; + }); +} + +DWARFExpression lldb_private::npdb::MakeConstantLocationExpression( + TypeIndex underlying_ti, TpiStream &tpi, const llvm::APSInt &constant, + ModuleSP module) { + const ArchSpec &architecture = module->GetArchitecture(); + uint32_t address_size = architecture.GetAddressByteSize(); + + size_t size = 0; + bool is_signed = false; + std::tie(size, is_signed) = GetIntegralTypeInfo(underlying_ti, tpi); + + union { + llvm::support::little64_t I; + llvm::support::ulittle64_t U; + } Value; + + std::shared_ptr<DataBufferHeap> buffer = std::make_shared<DataBufferHeap>(); + buffer->SetByteSize(size); + + llvm::ArrayRef<uint8_t> bytes; + if (is_signed) { + Value.I = constant.getSExtValue(); + } else { + Value.U = constant.getZExtValue(); + } + + bytes = llvm::ArrayRef(reinterpret_cast<const uint8_t *>(&Value), 8) + .take_front(size); + buffer->CopyData(bytes.data(), size); + DataExtractor extractor(buffer, lldb::eByteOrderLittle, address_size); + DWARFExpression result(extractor); + return result; +} + +DWARFExpression +lldb_private::npdb::MakeEnregisteredLocationExpressionForComposite( + const std::map<uint64_t, MemberValLocation> &offset_to_location, + std::map<uint64_t, size_t> &offset_to_size, size_t total_size, + lldb::ModuleSP module) { + return MakeLocationExpressionInternal( + module, [&](Stream &stream, RegisterKind ®ister_kind) -> bool { + size_t cur_offset = 0; + bool is_simple_type = offset_to_size.empty(); + // Iterate through offset_to_location because offset_to_size might be + // empty if the variable is a simple type. + for (const auto &offset_loc : offset_to_location) { + if (cur_offset < offset_loc.first) { + stream.PutHex8(llvm::dwarf::DW_OP_piece); + stream.PutULEB128(offset_loc.first - cur_offset); + cur_offset = offset_loc.first; + } + MemberValLocation loc = offset_loc.second; + std::optional<int32_t> offset = + loc.is_at_reg ? std::nullopt + : std::optional<int32_t>(loc.reg_offset); + if (!MakeRegisterBasedLocationExpressionInternal( + stream, (RegisterId)loc.reg_id, register_kind, offset, + module)) + return false; + if (!is_simple_type) { + stream.PutHex8(llvm::dwarf::DW_OP_piece); + stream.PutULEB128(offset_to_size[offset_loc.first]); + cur_offset = offset_loc.first + offset_to_size[offset_loc.first]; + } + } + // For simple type, it specifies the byte size of the value described by + // the previous dwarf expr. For udt, it's the remaining byte size at end + // of a struct. + if (total_size > cur_offset) { + stream.PutHex8(llvm::dwarf::DW_OP_piece); + stream.PutULEB128(total_size - cur_offset); + } + return true; + }); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/DWARFLocationExpression.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/DWARFLocationExpression.h new file mode 100644 index 000000000000..2f12d8bf0dd7 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/DWARFLocationExpression.h @@ -0,0 +1,57 @@ +//===-- DWARFLocationExpression.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_SOURCE_PLUGINS_SYMBOLFILE_NATIVEPDB_DWARFLOCATIONEXPRESSION_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_NATIVEPDB_DWARFLOCATIONEXPRESSION_H + +#include "lldb/lldb-forward.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/DebugInfo/CodeView/CodeView.h" +#include <map> + +namespace llvm { +class APSInt; +class StringRef; +namespace codeview { +class TypeIndex; +} +namespace pdb { +class TpiStream; +} +} // namespace llvm +namespace lldb_private { +namespace npdb { +struct MemberValLocation { + uint16_t reg_id; + uint16_t reg_offset; + bool is_at_reg = true; +}; + +DWARFExpression +MakeEnregisteredLocationExpression(llvm::codeview::RegisterId reg, + lldb::ModuleSP module); + +DWARFExpression MakeRegRelLocationExpression(llvm::codeview::RegisterId reg, + int32_t offset, + lldb::ModuleSP module); +DWARFExpression MakeVFrameRelLocationExpression(llvm::StringRef fpo_program, + int32_t offset, + lldb::ModuleSP module); +DWARFExpression MakeGlobalLocationExpression(uint16_t section, uint32_t offset, + lldb::ModuleSP module); +DWARFExpression MakeConstantLocationExpression( + llvm::codeview::TypeIndex underlying_ti, llvm::pdb::TpiStream &tpi, + const llvm::APSInt &constant, lldb::ModuleSP module); +DWARFExpression MakeEnregisteredLocationExpressionForComposite( + const std::map<uint64_t, MemberValLocation> &offset_to_location, + std::map<uint64_t, size_t> &offset_to_size, size_t total_size, + lldb::ModuleSP module); +} // namespace npdb +} // namespace lldb_private + +#endif diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp new file mode 100644 index 000000000000..b79d3e63f72b --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp @@ -0,0 +1,1454 @@ +#include "PdbAstBuilder.h" + +#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" +#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" +#include "llvm/DebugInfo/CodeView/RecordName.h" +#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h" +#include "llvm/DebugInfo/CodeView/SymbolRecord.h" +#include "llvm/DebugInfo/CodeView/SymbolRecordHelpers.h" +#include "llvm/DebugInfo/CodeView/TypeDeserializer.h" +#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h" +#include "llvm/DebugInfo/PDB/Native/DbiStream.h" +#include "llvm/DebugInfo/PDB/Native/PublicsStream.h" +#include "llvm/DebugInfo/PDB/Native/SymbolStream.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" +#include "llvm/Demangle/MicrosoftDemangle.h" + +#include "PdbUtil.h" +#include "Plugins/ExpressionParser/Clang/ClangASTMetadata.h" +#include "Plugins/ExpressionParser/Clang/ClangUtil.h" +#include "Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.h" +#include "Plugins/TypeSystem/Clang/TypeSystemClang.h" +#include "SymbolFileNativePDB.h" +#include "UdtRecordCompleter.h" +#include "lldb/Core/Module.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Utility/LLDBAssert.h" +#include <optional> +#include <string_view> + +using namespace lldb_private; +using namespace lldb_private::npdb; +using namespace llvm::codeview; +using namespace llvm::pdb; + +namespace { +struct CreateMethodDecl : public TypeVisitorCallbacks { + CreateMethodDecl(PdbIndex &m_index, TypeSystemClang &m_clang, + TypeIndex func_type_index, + clang::FunctionDecl *&function_decl, + lldb::opaque_compiler_type_t parent_ty, + llvm::StringRef proc_name, CompilerType func_ct) + : m_index(m_index), m_clang(m_clang), func_type_index(func_type_index), + function_decl(function_decl), parent_ty(parent_ty), + proc_name(proc_name), func_ct(func_ct) {} + PdbIndex &m_index; + TypeSystemClang &m_clang; + TypeIndex func_type_index; + clang::FunctionDecl *&function_decl; + lldb::opaque_compiler_type_t parent_ty; + llvm::StringRef proc_name; + CompilerType func_ct; + + llvm::Error visitKnownMember(CVMemberRecord &cvr, + OverloadedMethodRecord &overloaded) override { + TypeIndex method_list_idx = overloaded.MethodList; + + CVType method_list_type = m_index.tpi().getType(method_list_idx); + assert(method_list_type.kind() == LF_METHODLIST); + + MethodOverloadListRecord method_list; + llvm::cantFail(TypeDeserializer::deserializeAs<MethodOverloadListRecord>( + method_list_type, method_list)); + + for (const OneMethodRecord &method : method_list.Methods) { + if (method.getType().getIndex() == func_type_index.getIndex()) + AddMethod(overloaded.Name, method.getAccess(), method.getOptions(), + method.Attrs); + } + + return llvm::Error::success(); + } + + llvm::Error visitKnownMember(CVMemberRecord &cvr, + OneMethodRecord &record) override { + AddMethod(record.getName(), record.getAccess(), record.getOptions(), + record.Attrs); + return llvm::Error::success(); + } + + void AddMethod(llvm::StringRef name, MemberAccess access, + MethodOptions options, MemberAttributes attrs) { + if (name != proc_name || function_decl) + return; + lldb::AccessType access_type = TranslateMemberAccess(access); + bool is_virtual = attrs.isVirtual(); + bool is_static = attrs.isStatic(); + bool is_artificial = (options & MethodOptions::CompilerGenerated) == + MethodOptions::CompilerGenerated; + function_decl = m_clang.AddMethodToCXXRecordType( + parent_ty, proc_name, + /*mangled_name=*/nullptr, func_ct, /*access=*/access_type, + /*is_virtual=*/is_virtual, /*is_static=*/is_static, + /*is_inline=*/false, /*is_explicit=*/false, + /*is_attr_used=*/false, /*is_artificial=*/is_artificial); + } +}; +} // namespace + +static clang::TagTypeKind TranslateUdtKind(const TagRecord &cr) { + switch (cr.Kind) { + case TypeRecordKind::Class: + return clang::TagTypeKind::Class; + case TypeRecordKind::Struct: + return clang::TagTypeKind::Struct; + case TypeRecordKind::Union: + return clang::TagTypeKind::Union; + case TypeRecordKind::Interface: + return clang::TagTypeKind::Interface; + case TypeRecordKind::Enum: + return clang::TagTypeKind::Enum; + default: + lldbassert(false && "Invalid tag record kind!"); + return clang::TagTypeKind::Struct; + } +} + +static bool IsCVarArgsFunction(llvm::ArrayRef<TypeIndex> args) { + if (args.empty()) + return false; + return args.back() == TypeIndex::None(); +} + +static bool +AnyScopesHaveTemplateParams(llvm::ArrayRef<llvm::ms_demangle::Node *> scopes) { + for (llvm::ms_demangle::Node *n : scopes) { + auto *idn = static_cast<llvm::ms_demangle::IdentifierNode *>(n); + if (idn->TemplateParams) + return true; + } + return false; +} + +static std::optional<clang::CallingConv> +TranslateCallingConvention(llvm::codeview::CallingConvention conv) { + using CC = llvm::codeview::CallingConvention; + switch (conv) { + + case CC::NearC: + case CC::FarC: + return clang::CallingConv::CC_C; + case CC::NearPascal: + case CC::FarPascal: + return clang::CallingConv::CC_X86Pascal; + case CC::NearFast: + case CC::FarFast: + return clang::CallingConv::CC_X86FastCall; + case CC::NearStdCall: + case CC::FarStdCall: + return clang::CallingConv::CC_X86StdCall; + case CC::ThisCall: + return clang::CallingConv::CC_X86ThisCall; + case CC::NearVector: + return clang::CallingConv::CC_X86VectorCall; + default: + return std::nullopt; + } +} + +static bool IsAnonymousNamespaceName(llvm::StringRef name) { + return name == "`anonymous namespace'" || name == "`anonymous-namespace'"; +} + +PdbAstBuilder::PdbAstBuilder(TypeSystemClang &clang) : m_clang(clang) {} + +lldb_private::CompilerDeclContext PdbAstBuilder::GetTranslationUnitDecl() { + return ToCompilerDeclContext(*m_clang.GetTranslationUnitDecl()); +} + +std::pair<clang::DeclContext *, std::string> +PdbAstBuilder::CreateDeclInfoForType(const TagRecord &record, TypeIndex ti) { + SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( + m_clang.GetSymbolFile()->GetBackingSymbolFile()); + // FIXME: Move this to GetDeclContextContainingUID. + if (!record.hasUniqueName()) + return CreateDeclInfoForUndecoratedName(record.Name); + + llvm::ms_demangle::Demangler demangler; + std::string_view sv(record.UniqueName.begin(), record.UniqueName.size()); + llvm::ms_demangle::TagTypeNode *ttn = demangler.parseTagUniqueName(sv); + if (demangler.Error) + return {m_clang.GetTranslationUnitDecl(), std::string(record.UniqueName)}; + + llvm::ms_demangle::IdentifierNode *idn = + ttn->QualifiedName->getUnqualifiedIdentifier(); + std::string uname = idn->toString(llvm::ms_demangle::OF_NoTagSpecifier); + + llvm::ms_demangle::NodeArrayNode *name_components = + ttn->QualifiedName->Components; + llvm::ArrayRef<llvm::ms_demangle::Node *> scopes(name_components->Nodes, + name_components->Count - 1); + + clang::DeclContext *context = m_clang.GetTranslationUnitDecl(); + + // If this type doesn't have a parent type in the debug info, then the best we + // can do is to say that it's either a series of namespaces (if the scope is + // non-empty), or the translation unit (if the scope is empty). + std::optional<TypeIndex> parent_index = pdb->GetParentType(ti); + if (!parent_index) { + if (scopes.empty()) + return {context, uname}; + + // If there is no parent in the debug info, but some of the scopes have + // template params, then this is a case of bad debug info. See, for + // example, llvm.org/pr39607. We don't want to create an ambiguity between + // a NamespaceDecl and a CXXRecordDecl, so instead we create a class at + // global scope with the fully qualified name. + if (AnyScopesHaveTemplateParams(scopes)) + return {context, std::string(record.Name)}; + + for (llvm::ms_demangle::Node *scope : scopes) { + auto *nii = static_cast<llvm::ms_demangle::NamedIdentifierNode *>(scope); + std::string str = nii->toString(); + context = GetOrCreateNamespaceDecl(str.c_str(), *context); + } + return {context, uname}; + } + + // Otherwise, all we need to do is get the parent type of this type and + // recurse into our lazy type creation / AST reconstruction logic to get an + // LLDB TypeSP for the parent. This will cause the AST to automatically get + // the right DeclContext created for any parent. + clang::QualType parent_qt = GetOrCreateType(*parent_index); + if (parent_qt.isNull()) + return {nullptr, ""}; + + context = clang::TagDecl::castToDeclContext(parent_qt->getAsTagDecl()); + return {context, uname}; +} + +static bool isLocalVariableType(SymbolKind K) { + switch (K) { + case S_REGISTER: + case S_REGREL32: + case S_LOCAL: + return true; + default: + break; + } + return false; +} + +clang::Decl *PdbAstBuilder::GetOrCreateSymbolForId(PdbCompilandSymId id) { + SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( + m_clang.GetSymbolFile()->GetBackingSymbolFile()); + PdbIndex &index = pdb->GetIndex(); + CVSymbol cvs = index.ReadSymbolRecord(id); + + if (isLocalVariableType(cvs.kind())) { + clang::DeclContext *scope = GetParentDeclContext(id); + if (!scope) + return nullptr; + clang::Decl *scope_decl = clang::Decl::castFromDeclContext(scope); + PdbCompilandSymId scope_id = + PdbSymUid(m_decl_to_status[scope_decl].uid).asCompilandSym(); + return GetOrCreateVariableDecl(scope_id, id); + } + + switch (cvs.kind()) { + case S_GPROC32: + case S_LPROC32: + return GetOrCreateFunctionDecl(id); + case S_GDATA32: + case S_LDATA32: + case S_GTHREAD32: + case S_CONSTANT: + // global variable + return nullptr; + case S_BLOCK32: + return GetOrCreateBlockDecl(id); + case S_INLINESITE: + return GetOrCreateInlinedFunctionDecl(id); + default: + return nullptr; + } +} + +std::optional<CompilerDecl> +PdbAstBuilder::GetOrCreateDeclForUid(PdbSymUid uid) { + if (clang::Decl *result = TryGetDecl(uid)) + return ToCompilerDecl(*result); + + clang::Decl *result = nullptr; + switch (uid.kind()) { + case PdbSymUidKind::CompilandSym: + result = GetOrCreateSymbolForId(uid.asCompilandSym()); + break; + case PdbSymUidKind::Type: { + clang::QualType qt = GetOrCreateType(uid.asTypeSym()); + if (qt.isNull()) + return std::nullopt; + if (auto *tag = qt->getAsTagDecl()) { + result = tag; + break; + } + return std::nullopt; + } + default: + return std::nullopt; + } + + if (!result) + return std::nullopt; + m_uid_to_decl[toOpaqueUid(uid)] = result; + return ToCompilerDecl(*result); +} + +clang::DeclContext *PdbAstBuilder::GetOrCreateDeclContextForUid(PdbSymUid uid) { + if (uid.kind() == PdbSymUidKind::CompilandSym) { + if (uid.asCompilandSym().offset == 0) + return FromCompilerDeclContext(GetTranslationUnitDecl()); + } + auto option = GetOrCreateDeclForUid(uid); + if (!option) + return nullptr; + clang::Decl *decl = FromCompilerDecl(*option); + if (!decl) + return nullptr; + + return clang::Decl::castToDeclContext(decl); +} + +std::pair<clang::DeclContext *, std::string> +PdbAstBuilder::CreateDeclInfoForUndecoratedName(llvm::StringRef name) { + SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( + m_clang.GetSymbolFile()->GetBackingSymbolFile()); + PdbIndex &index = pdb->GetIndex(); + MSVCUndecoratedNameParser parser(name); + llvm::ArrayRef<MSVCUndecoratedNameSpecifier> specs = parser.GetSpecifiers(); + + auto *context = FromCompilerDeclContext(GetTranslationUnitDecl()); + + llvm::StringRef uname = specs.back().GetBaseName(); + specs = specs.drop_back(); + if (specs.empty()) + return {context, std::string(name)}; + + llvm::StringRef scope_name = specs.back().GetFullName(); + + // It might be a class name, try that first. + std::vector<TypeIndex> types = index.tpi().findRecordsByName(scope_name); + while (!types.empty()) { + clang::QualType qt = GetOrCreateType(types.back()); + if (qt.isNull()) + continue; + clang::TagDecl *tag = qt->getAsTagDecl(); + if (tag) + return {clang::TagDecl::castToDeclContext(tag), std::string(uname)}; + types.pop_back(); + } + + // If that fails, treat it as a series of namespaces. + for (const MSVCUndecoratedNameSpecifier &spec : specs) { + std::string ns_name = spec.GetBaseName().str(); + context = GetOrCreateNamespaceDecl(ns_name.c_str(), *context); + } + return {context, std::string(uname)}; +} + +clang::DeclContext *PdbAstBuilder::GetParentDeclContext(PdbSymUid uid) { + // We must do this *without* calling GetOrCreate on the current uid, as + // that would be an infinite recursion. + SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( + m_clang.GetSymbolFile()->GetBackingSymbolFile()); + PdbIndex& index = pdb->GetIndex(); + switch (uid.kind()) { + case PdbSymUidKind::CompilandSym: { + std::optional<PdbCompilandSymId> scope = + pdb->FindSymbolScope(uid.asCompilandSym()); + if (scope) + return GetOrCreateDeclContextForUid(*scope); + + CVSymbol sym = index.ReadSymbolRecord(uid.asCompilandSym()); + return CreateDeclInfoForUndecoratedName(getSymbolName(sym)).first; + } + case PdbSymUidKind::Type: { + // It could be a namespace, class, or global. We don't support nested + // functions yet. Anyway, we just need to consult the parent type map. + PdbTypeSymId type_id = uid.asTypeSym(); + std::optional<TypeIndex> parent_index = pdb->GetParentType(type_id.index); + if (!parent_index) + return FromCompilerDeclContext(GetTranslationUnitDecl()); + return GetOrCreateDeclContextForUid(PdbTypeSymId(*parent_index)); + } + case PdbSymUidKind::FieldListMember: + // In this case the parent DeclContext is the one for the class that this + // member is inside of. + break; + case PdbSymUidKind::GlobalSym: { + // If this refers to a compiland symbol, just recurse in with that symbol. + // The only other possibilities are S_CONSTANT and S_UDT, in which case we + // need to parse the undecorated name to figure out the scope, then look + // that up in the TPI stream. If it's found, it's a type, othewrise it's + // a series of namespaces. + // FIXME: do this. + CVSymbol global = index.ReadSymbolRecord(uid.asGlobalSym()); + switch (global.kind()) { + case SymbolKind::S_GDATA32: + case SymbolKind::S_LDATA32: + return CreateDeclInfoForUndecoratedName(getSymbolName(global)).first;; + case SymbolKind::S_PROCREF: + case SymbolKind::S_LPROCREF: { + ProcRefSym ref{global.kind()}; + llvm::cantFail( + SymbolDeserializer::deserializeAs<ProcRefSym>(global, ref)); + PdbCompilandSymId cu_sym_id{ref.modi(), ref.SymOffset}; + return GetParentDeclContext(cu_sym_id); + } + case SymbolKind::S_CONSTANT: + case SymbolKind::S_UDT: + return CreateDeclInfoForUndecoratedName(getSymbolName(global)).first; + default: + break; + } + break; + } + default: + break; + } + return FromCompilerDeclContext(GetTranslationUnitDecl()); +} + +bool PdbAstBuilder::CompleteType(clang::QualType qt) { + if (qt.isNull()) + return false; + clang::TagDecl *tag = qt->getAsTagDecl(); + if (qt->isArrayType()) { + const clang::Type *element_type = qt->getArrayElementTypeNoTypeQual(); + tag = element_type->getAsTagDecl(); + } + if (!tag) + return false; + + return CompleteTagDecl(*tag); +} + +bool PdbAstBuilder::CompleteTagDecl(clang::TagDecl &tag) { + // If this is not in our map, it's an error. + auto status_iter = m_decl_to_status.find(&tag); + lldbassert(status_iter != m_decl_to_status.end()); + + // If it's already complete, just return. + DeclStatus &status = status_iter->second; + if (status.resolved) + return true; + + PdbTypeSymId type_id = PdbSymUid(status.uid).asTypeSym(); + PdbIndex &index = static_cast<SymbolFileNativePDB *>( + m_clang.GetSymbolFile()->GetBackingSymbolFile()) + ->GetIndex(); + lldbassert(IsTagRecord(type_id, index.tpi())); + + clang::QualType tag_qt = m_clang.getASTContext().getTypeDeclType(&tag); + TypeSystemClang::SetHasExternalStorage(tag_qt.getAsOpaquePtr(), false); + + TypeIndex tag_ti = type_id.index; + CVType cvt = index.tpi().getType(tag_ti); + if (cvt.kind() == LF_MODIFIER) + tag_ti = LookThroughModifierRecord(cvt); + + PdbTypeSymId best_ti = GetBestPossibleDecl(tag_ti, index.tpi()); + cvt = index.tpi().getType(best_ti.index); + lldbassert(IsTagRecord(cvt)); + + if (IsForwardRefUdt(cvt)) { + // If we can't find a full decl for this forward ref anywhere in the debug + // info, then we have no way to complete it. + return false; + } + + TypeIndex field_list_ti = GetFieldListIndex(cvt); + CVType field_list_cvt = index.tpi().getType(field_list_ti); + if (field_list_cvt.kind() != LF_FIELDLIST) + return false; + FieldListRecord field_list; + if (llvm::Error error = TypeDeserializer::deserializeAs<FieldListRecord>( + field_list_cvt, field_list)) + llvm::consumeError(std::move(error)); + + // Visit all members of this class, then perform any finalization necessary + // to complete the class. + CompilerType ct = ToCompilerType(tag_qt); + UdtRecordCompleter completer(best_ti, ct, tag, *this, index, m_decl_to_status, + m_cxx_record_map); + llvm::Error error = + llvm::codeview::visitMemberRecordStream(field_list.Data, completer); + completer.complete(); + + m_decl_to_status[&tag].resolved = true; + if (error) { + llvm::consumeError(std::move(error)); + return false; + } + return true; +} + +clang::QualType PdbAstBuilder::CreateSimpleType(TypeIndex ti) { + if (ti == TypeIndex::NullptrT()) + return GetBasicType(lldb::eBasicTypeNullPtr); + + if (ti.getSimpleMode() != SimpleTypeMode::Direct) { + clang::QualType direct_type = GetOrCreateType(ti.makeDirect()); + if (direct_type.isNull()) + return {}; + return m_clang.getASTContext().getPointerType(direct_type); + } + + if (ti.getSimpleKind() == SimpleTypeKind::NotTranslated) + return {}; + + lldb::BasicType bt = GetCompilerTypeForSimpleKind(ti.getSimpleKind()); + if (bt == lldb::eBasicTypeInvalid) + return {}; + + return GetBasicType(bt); +} + +clang::QualType PdbAstBuilder::CreatePointerType(const PointerRecord &pointer) { + clang::QualType pointee_type = GetOrCreateType(pointer.ReferentType); + + // This can happen for pointers to LF_VTSHAPE records, which we shouldn't + // create in the AST. + if (pointee_type.isNull()) + return {}; + + if (pointer.isPointerToMember()) { + MemberPointerInfo mpi = pointer.getMemberInfo(); + clang::QualType class_type = GetOrCreateType(mpi.ContainingType); + if (class_type.isNull()) + return {}; + if (clang::TagDecl *tag = class_type->getAsTagDecl()) { + clang::MSInheritanceAttr::Spelling spelling; + switch (mpi.Representation) { + case llvm::codeview::PointerToMemberRepresentation::SingleInheritanceData: + case llvm::codeview::PointerToMemberRepresentation:: + SingleInheritanceFunction: + spelling = + clang::MSInheritanceAttr::Spelling::Keyword_single_inheritance; + break; + case llvm::codeview::PointerToMemberRepresentation:: + MultipleInheritanceData: + case llvm::codeview::PointerToMemberRepresentation:: + MultipleInheritanceFunction: + spelling = + clang::MSInheritanceAttr::Spelling::Keyword_multiple_inheritance; + break; + case llvm::codeview::PointerToMemberRepresentation:: + VirtualInheritanceData: + case llvm::codeview::PointerToMemberRepresentation:: + VirtualInheritanceFunction: + spelling = + clang::MSInheritanceAttr::Spelling::Keyword_virtual_inheritance; + break; + case llvm::codeview::PointerToMemberRepresentation::Unknown: + spelling = + clang::MSInheritanceAttr::Spelling::Keyword_unspecified_inheritance; + break; + default: + spelling = clang::MSInheritanceAttr::Spelling::SpellingNotCalculated; + break; + } + tag->addAttr(clang::MSInheritanceAttr::CreateImplicit( + m_clang.getASTContext(), spelling)); + } + return m_clang.getASTContext().getMemberPointerType( + pointee_type, class_type.getTypePtr()); + } + + clang::QualType pointer_type; + if (pointer.getMode() == PointerMode::LValueReference) + pointer_type = m_clang.getASTContext().getLValueReferenceType(pointee_type); + else if (pointer.getMode() == PointerMode::RValueReference) + pointer_type = m_clang.getASTContext().getRValueReferenceType(pointee_type); + else + pointer_type = m_clang.getASTContext().getPointerType(pointee_type); + + if ((pointer.getOptions() & PointerOptions::Const) != PointerOptions::None) + pointer_type.addConst(); + + if ((pointer.getOptions() & PointerOptions::Volatile) != PointerOptions::None) + pointer_type.addVolatile(); + + if ((pointer.getOptions() & PointerOptions::Restrict) != PointerOptions::None) + pointer_type.addRestrict(); + + return pointer_type; +} + +clang::QualType +PdbAstBuilder::CreateModifierType(const ModifierRecord &modifier) { + clang::QualType unmodified_type = GetOrCreateType(modifier.ModifiedType); + if (unmodified_type.isNull()) + return {}; + + if ((modifier.Modifiers & ModifierOptions::Const) != ModifierOptions::None) + unmodified_type.addConst(); + if ((modifier.Modifiers & ModifierOptions::Volatile) != ModifierOptions::None) + unmodified_type.addVolatile(); + + return unmodified_type; +} + +clang::QualType PdbAstBuilder::CreateRecordType(PdbTypeSymId id, + const TagRecord &record) { + clang::DeclContext *context = nullptr; + std::string uname; + std::tie(context, uname) = CreateDeclInfoForType(record, id.index); + if (!context) + return {}; + + clang::TagTypeKind ttk = TranslateUdtKind(record); + lldb::AccessType access = (ttk == clang::TagTypeKind::Class) + ? lldb::eAccessPrivate + : lldb::eAccessPublic; + + ClangASTMetadata metadata; + metadata.SetUserID(toOpaqueUid(id)); + metadata.SetIsDynamicCXXType(false); + + CompilerType ct = m_clang.CreateRecordType( + context, OptionalClangModuleID(), access, uname, llvm::to_underlying(ttk), + lldb::eLanguageTypeC_plus_plus, &metadata); + + lldbassert(ct.IsValid()); + + TypeSystemClang::StartTagDeclarationDefinition(ct); + + // Even if it's possible, don't complete it at this point. Just mark it + // forward resolved, and if/when LLDB needs the full definition, it can + // ask us. + clang::QualType result = + clang::QualType::getFromOpaquePtr(ct.GetOpaqueQualType()); + + TypeSystemClang::SetHasExternalStorage(result.getAsOpaquePtr(), true); + return result; +} + +clang::Decl *PdbAstBuilder::TryGetDecl(PdbSymUid uid) const { + auto iter = m_uid_to_decl.find(toOpaqueUid(uid)); + if (iter != m_uid_to_decl.end()) + return iter->second; + return nullptr; +} + +clang::NamespaceDecl * +PdbAstBuilder::GetOrCreateNamespaceDecl(const char *name, + clang::DeclContext &context) { + return m_clang.GetUniqueNamespaceDeclaration( + IsAnonymousNamespaceName(name) ? nullptr : name, &context, + OptionalClangModuleID()); +} + +clang::BlockDecl * +PdbAstBuilder::GetOrCreateBlockDecl(PdbCompilandSymId block_id) { + if (clang::Decl *decl = TryGetDecl(block_id)) + return llvm::dyn_cast<clang::BlockDecl>(decl); + + clang::DeclContext *scope = GetParentDeclContext(block_id); + + clang::BlockDecl *block_decl = + m_clang.CreateBlockDeclaration(scope, OptionalClangModuleID()); + m_uid_to_decl.insert({toOpaqueUid(block_id), block_decl}); + + DeclStatus status; + status.resolved = true; + status.uid = toOpaqueUid(block_id); + m_decl_to_status.insert({block_decl, status}); + + return block_decl; +} + +clang::VarDecl *PdbAstBuilder::CreateVariableDecl(PdbSymUid uid, CVSymbol sym, + clang::DeclContext &scope) { + VariableInfo var_info = GetVariableNameInfo(sym); + clang::QualType qt = GetOrCreateType(var_info.type); + if (qt.isNull()) + return nullptr; + + clang::VarDecl *var_decl = m_clang.CreateVariableDeclaration( + &scope, OptionalClangModuleID(), var_info.name.str().c_str(), qt); + + m_uid_to_decl[toOpaqueUid(uid)] = var_decl; + DeclStatus status; + status.resolved = true; + status.uid = toOpaqueUid(uid); + m_decl_to_status.insert({var_decl, status}); + return var_decl; +} + +clang::VarDecl * +PdbAstBuilder::GetOrCreateVariableDecl(PdbCompilandSymId scope_id, + PdbCompilandSymId var_id) { + if (clang::Decl *decl = TryGetDecl(var_id)) + return llvm::dyn_cast<clang::VarDecl>(decl); + + clang::DeclContext *scope = GetOrCreateDeclContextForUid(scope_id); + if (!scope) + return nullptr; + + SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( + m_clang.GetSymbolFile()->GetBackingSymbolFile()); + PdbIndex &index = pdb->GetIndex(); + CVSymbol sym = index.ReadSymbolRecord(var_id); + return CreateVariableDecl(PdbSymUid(var_id), sym, *scope); +} + +clang::VarDecl *PdbAstBuilder::GetOrCreateVariableDecl(PdbGlobalSymId var_id) { + if (clang::Decl *decl = TryGetDecl(var_id)) + return llvm::dyn_cast<clang::VarDecl>(decl); + + SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( + m_clang.GetSymbolFile()->GetBackingSymbolFile()); + PdbIndex &index = pdb->GetIndex(); + CVSymbol sym = index.ReadSymbolRecord(var_id); + auto context = FromCompilerDeclContext(GetTranslationUnitDecl()); + return CreateVariableDecl(PdbSymUid(var_id), sym, *context); +} + +clang::TypedefNameDecl * +PdbAstBuilder::GetOrCreateTypedefDecl(PdbGlobalSymId id) { + if (clang::Decl *decl = TryGetDecl(id)) + return llvm::dyn_cast<clang::TypedefNameDecl>(decl); + + SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( + m_clang.GetSymbolFile()->GetBackingSymbolFile()); + PdbIndex &index = pdb->GetIndex(); + CVSymbol sym = index.ReadSymbolRecord(id); + lldbassert(sym.kind() == S_UDT); + UDTSym udt = llvm::cantFail(SymbolDeserializer::deserializeAs<UDTSym>(sym)); + + clang::DeclContext *scope = GetParentDeclContext(id); + + PdbTypeSymId real_type_id{udt.Type, false}; + clang::QualType qt = GetOrCreateType(real_type_id); + if (qt.isNull() || !scope) + return nullptr; + + std::string uname = std::string(DropNameScope(udt.Name)); + + CompilerType ct = ToCompilerType(qt).CreateTypedef( + uname.c_str(), ToCompilerDeclContext(*scope), 0); + clang::TypedefNameDecl *tnd = m_clang.GetAsTypedefDecl(ct); + DeclStatus status; + status.resolved = true; + status.uid = toOpaqueUid(id); + m_decl_to_status.insert({tnd, status}); + return tnd; +} + +clang::QualType PdbAstBuilder::GetBasicType(lldb::BasicType type) { + CompilerType ct = m_clang.GetBasicType(type); + return clang::QualType::getFromOpaquePtr(ct.GetOpaqueQualType()); +} + +clang::QualType PdbAstBuilder::CreateType(PdbTypeSymId type) { + if (type.index.isSimple()) + return CreateSimpleType(type.index); + + SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( + m_clang.GetSymbolFile()->GetBackingSymbolFile()); + PdbIndex &index = pdb->GetIndex(); + CVType cvt = index.tpi().getType(type.index); + + if (cvt.kind() == LF_MODIFIER) { + ModifierRecord modifier; + llvm::cantFail( + TypeDeserializer::deserializeAs<ModifierRecord>(cvt, modifier)); + return CreateModifierType(modifier); + } + + if (cvt.kind() == LF_POINTER) { + PointerRecord pointer; + llvm::cantFail( + TypeDeserializer::deserializeAs<PointerRecord>(cvt, pointer)); + return CreatePointerType(pointer); + } + + if (IsTagRecord(cvt)) { + CVTagRecord tag = CVTagRecord::create(cvt); + if (tag.kind() == CVTagRecord::Union) + return CreateRecordType(type.index, tag.asUnion()); + if (tag.kind() == CVTagRecord::Enum) + return CreateEnumType(type.index, tag.asEnum()); + return CreateRecordType(type.index, tag.asClass()); + } + + if (cvt.kind() == LF_ARRAY) { + ArrayRecord ar; + llvm::cantFail(TypeDeserializer::deserializeAs<ArrayRecord>(cvt, ar)); + return CreateArrayType(ar); + } + + if (cvt.kind() == LF_PROCEDURE) { + ProcedureRecord pr; + llvm::cantFail(TypeDeserializer::deserializeAs<ProcedureRecord>(cvt, pr)); + return CreateFunctionType(pr.ArgumentList, pr.ReturnType, pr.CallConv); + } + + if (cvt.kind() == LF_MFUNCTION) { + MemberFunctionRecord mfr; + llvm::cantFail( + TypeDeserializer::deserializeAs<MemberFunctionRecord>(cvt, mfr)); + return CreateFunctionType(mfr.ArgumentList, mfr.ReturnType, mfr.CallConv); + } + + return {}; +} + +clang::QualType PdbAstBuilder::GetOrCreateType(PdbTypeSymId type) { + if (type.index.isNoneType()) + return {}; + + lldb::user_id_t uid = toOpaqueUid(type); + auto iter = m_uid_to_type.find(uid); + if (iter != m_uid_to_type.end()) + return iter->second; + + SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( + m_clang.GetSymbolFile()->GetBackingSymbolFile()); + PdbIndex &index = pdb->GetIndex(); + PdbTypeSymId best_type = GetBestPossibleDecl(type, index.tpi()); + + clang::QualType qt; + if (best_type.index != type.index) { + // This is a forward decl. Call GetOrCreate on the full decl, then map the + // forward decl id to the full decl QualType. + clang::QualType qt = GetOrCreateType(best_type); + if (qt.isNull()) + return {}; + m_uid_to_type[toOpaqueUid(type)] = qt; + return qt; + } + + // This is either a full decl, or a forward decl with no matching full decl + // in the debug info. + qt = CreateType(type); + if (qt.isNull()) + return {}; + + m_uid_to_type[toOpaqueUid(type)] = qt; + if (IsTagRecord(type, index.tpi())) { + clang::TagDecl *tag = qt->getAsTagDecl(); + lldbassert(m_decl_to_status.count(tag) == 0); + + DeclStatus &status = m_decl_to_status[tag]; + status.uid = uid; + status.resolved = false; + } + return qt; +} + +clang::FunctionDecl * +PdbAstBuilder::CreateFunctionDecl(PdbCompilandSymId func_id, + llvm::StringRef func_name, TypeIndex func_ti, + CompilerType func_ct, uint32_t param_count, + clang::StorageClass func_storage, + bool is_inline, clang::DeclContext *parent) { + clang::FunctionDecl *function_decl = nullptr; + if (parent->isRecord()) { + SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( + m_clang.GetSymbolFile()->GetBackingSymbolFile()); + PdbIndex &index = pdb->GetIndex(); + clang::QualType parent_qt = llvm::cast<clang::TypeDecl>(parent) + ->getTypeForDecl() + ->getCanonicalTypeInternal(); + lldb::opaque_compiler_type_t parent_opaque_ty = + ToCompilerType(parent_qt).GetOpaqueQualType(); + // FIXME: Remove this workaround. + auto iter = m_cxx_record_map.find(parent_opaque_ty); + if (iter != m_cxx_record_map.end()) { + if (iter->getSecond().contains({func_name, func_ct})) { + return nullptr; + } + } + + CVType cvt = index.tpi().getType(func_ti); + MemberFunctionRecord func_record(static_cast<TypeRecordKind>(cvt.kind())); + llvm::cantFail(TypeDeserializer::deserializeAs<MemberFunctionRecord>( + cvt, func_record)); + TypeIndex class_index = func_record.getClassType(); + + CVType parent_cvt = index.tpi().getType(class_index); + TagRecord tag_record = CVTagRecord::create(parent_cvt).asTag(); + // If it's a forward reference, try to get the real TypeIndex. + if (tag_record.isForwardRef()) { + llvm::Expected<TypeIndex> eti = + index.tpi().findFullDeclForForwardRef(class_index); + if (eti) { + tag_record = CVTagRecord::create(index.tpi().getType(*eti)).asTag(); + } + } + if (!tag_record.FieldList.isSimple()) { + CVType field_list_cvt = index.tpi().getType(tag_record.FieldList); + FieldListRecord field_list; + if (llvm::Error error = TypeDeserializer::deserializeAs<FieldListRecord>( + field_list_cvt, field_list)) + llvm::consumeError(std::move(error)); + CreateMethodDecl process(index, m_clang, func_ti, function_decl, + parent_opaque_ty, func_name, func_ct); + if (llvm::Error err = visitMemberRecordStream(field_list.Data, process)) + llvm::consumeError(std::move(err)); + } + + if (!function_decl) { + function_decl = m_clang.AddMethodToCXXRecordType( + parent_opaque_ty, func_name, + /*mangled_name=*/nullptr, func_ct, + /*access=*/lldb::AccessType::eAccessPublic, + /*is_virtual=*/false, /*is_static=*/false, + /*is_inline=*/false, /*is_explicit=*/false, + /*is_attr_used=*/false, /*is_artificial=*/false); + } + m_cxx_record_map[parent_opaque_ty].insert({func_name, func_ct}); + } else { + function_decl = m_clang.CreateFunctionDeclaration( + parent, OptionalClangModuleID(), func_name, func_ct, func_storage, + is_inline); + CreateFunctionParameters(func_id, *function_decl, param_count); + } + return function_decl; +} + +clang::FunctionDecl * +PdbAstBuilder::GetOrCreateInlinedFunctionDecl(PdbCompilandSymId inlinesite_id) { + SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( + m_clang.GetSymbolFile()->GetBackingSymbolFile()); + PdbIndex &index = pdb->GetIndex(); + CompilandIndexItem *cii = + index.compilands().GetCompiland(inlinesite_id.modi); + CVSymbol sym = cii->m_debug_stream.readSymbolAtOffset(inlinesite_id.offset); + InlineSiteSym inline_site(static_cast<SymbolRecordKind>(sym.kind())); + cantFail(SymbolDeserializer::deserializeAs<InlineSiteSym>(sym, inline_site)); + + // Inlinee is the id index to the function id record that is inlined. + PdbTypeSymId func_id(inline_site.Inlinee, true); + // Look up the function decl by the id index to see if we have created a + // function decl for a different inlinesite that refers the same function. + if (clang::Decl *decl = TryGetDecl(func_id)) + return llvm::dyn_cast<clang::FunctionDecl>(decl); + clang::FunctionDecl *function_decl = + CreateFunctionDeclFromId(func_id, inlinesite_id); + if (function_decl == nullptr) + return nullptr; + + // Use inline site id in m_decl_to_status because it's expected to be a + // PdbCompilandSymId so that we can parse local variables info after it. + uint64_t inlinesite_uid = toOpaqueUid(inlinesite_id); + DeclStatus status; + status.resolved = true; + status.uid = inlinesite_uid; + m_decl_to_status.insert({function_decl, status}); + // Use the index in IPI stream as uid in m_uid_to_decl, because index in IPI + // stream are unique and there could be multiple inline sites (different ids) + // referring the same inline function. This avoid creating multiple same + // inline function delcs. + uint64_t func_uid = toOpaqueUid(func_id); + lldbassert(m_uid_to_decl.count(func_uid) == 0); + m_uid_to_decl[func_uid] = function_decl; + return function_decl; +} + +clang::FunctionDecl * +PdbAstBuilder::CreateFunctionDeclFromId(PdbTypeSymId func_tid, + PdbCompilandSymId func_sid) { + lldbassert(func_tid.is_ipi); + SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( + m_clang.GetSymbolFile()->GetBackingSymbolFile()); + PdbIndex &index = pdb->GetIndex(); + CVType func_cvt = index.ipi().getType(func_tid.index); + llvm::StringRef func_name; + TypeIndex func_ti; + clang::DeclContext *parent = nullptr; + switch (func_cvt.kind()) { + case LF_MFUNC_ID: { + MemberFuncIdRecord mfr; + cantFail( + TypeDeserializer::deserializeAs<MemberFuncIdRecord>(func_cvt, mfr)); + func_name = mfr.getName(); + func_ti = mfr.getFunctionType(); + PdbTypeSymId class_type_id(mfr.ClassType, false); + parent = GetOrCreateDeclContextForUid(class_type_id); + break; + } + case LF_FUNC_ID: { + FuncIdRecord fir; + cantFail(TypeDeserializer::deserializeAs<FuncIdRecord>(func_cvt, fir)); + func_name = fir.getName(); + func_ti = fir.getFunctionType(); + parent = FromCompilerDeclContext(GetTranslationUnitDecl()); + if (!fir.ParentScope.isNoneType()) { + CVType parent_cvt = index.ipi().getType(fir.ParentScope); + if (parent_cvt.kind() == LF_STRING_ID) { + StringIdRecord sir; + cantFail( + TypeDeserializer::deserializeAs<StringIdRecord>(parent_cvt, sir)); + parent = GetOrCreateNamespaceDecl(sir.String.data(), *parent); + } + } + break; + } + default: + lldbassert(false && "Invalid function id type!"); + } + clang::QualType func_qt = GetOrCreateType(func_ti); + if (func_qt.isNull() || !parent) + return nullptr; + CompilerType func_ct = ToCompilerType(func_qt); + uint32_t param_count = + llvm::cast<clang::FunctionProtoType>(func_qt)->getNumParams(); + return CreateFunctionDecl(func_sid, func_name, func_ti, func_ct, param_count, + clang::SC_None, true, parent); +} + +clang::FunctionDecl * +PdbAstBuilder::GetOrCreateFunctionDecl(PdbCompilandSymId func_id) { + if (clang::Decl *decl = TryGetDecl(func_id)) + return llvm::dyn_cast<clang::FunctionDecl>(decl); + + clang::DeclContext *parent = GetParentDeclContext(PdbSymUid(func_id)); + if (!parent) + return nullptr; + std::string context_name; + if (clang::NamespaceDecl *ns = llvm::dyn_cast<clang::NamespaceDecl>(parent)) { + context_name = ns->getQualifiedNameAsString(); + } else if (clang::TagDecl *tag = llvm::dyn_cast<clang::TagDecl>(parent)) { + context_name = tag->getQualifiedNameAsString(); + } + + SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( + m_clang.GetSymbolFile()->GetBackingSymbolFile()); + PdbIndex &index = pdb->GetIndex(); + CVSymbol cvs = index.ReadSymbolRecord(func_id); + ProcSym proc(static_cast<SymbolRecordKind>(cvs.kind())); + llvm::cantFail(SymbolDeserializer::deserializeAs<ProcSym>(cvs, proc)); + + PdbTypeSymId type_id(proc.FunctionType); + clang::QualType qt = GetOrCreateType(type_id); + if (qt.isNull()) + return nullptr; + + clang::StorageClass storage = clang::SC_None; + if (proc.Kind == SymbolRecordKind::ProcSym) + storage = clang::SC_Static; + + const clang::FunctionProtoType *func_type = + llvm::dyn_cast<clang::FunctionProtoType>(qt); + + CompilerType func_ct = ToCompilerType(qt); + + llvm::StringRef proc_name = proc.Name; + proc_name.consume_front(context_name); + proc_name.consume_front("::"); + clang::FunctionDecl *function_decl = + CreateFunctionDecl(func_id, proc_name, proc.FunctionType, func_ct, + func_type->getNumParams(), storage, false, parent); + if (function_decl == nullptr) + return nullptr; + + lldbassert(m_uid_to_decl.count(toOpaqueUid(func_id)) == 0); + m_uid_to_decl[toOpaqueUid(func_id)] = function_decl; + DeclStatus status; + status.resolved = true; + status.uid = toOpaqueUid(func_id); + m_decl_to_status.insert({function_decl, status}); + + return function_decl; +} + +void PdbAstBuilder::CreateFunctionParameters(PdbCompilandSymId func_id, + clang::FunctionDecl &function_decl, + uint32_t param_count) { + SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( + m_clang.GetSymbolFile()->GetBackingSymbolFile()); + PdbIndex &index = pdb->GetIndex(); + CompilandIndexItem *cii = index.compilands().GetCompiland(func_id.modi); + CVSymbolArray scope = + cii->m_debug_stream.getSymbolArrayForScope(func_id.offset); + + scope.drop_front(); + auto begin = scope.begin(); + auto end = scope.end(); + std::vector<clang::ParmVarDecl *> params; + for (uint32_t i = 0; i < param_count && begin != end;) { + uint32_t record_offset = begin.offset(); + CVSymbol sym = *begin++; + + TypeIndex param_type; + llvm::StringRef param_name; + switch (sym.kind()) { + case S_REGREL32: { + RegRelativeSym reg(SymbolRecordKind::RegRelativeSym); + cantFail(SymbolDeserializer::deserializeAs<RegRelativeSym>(sym, reg)); + param_type = reg.Type; + param_name = reg.Name; + break; + } + case S_REGISTER: { + RegisterSym reg(SymbolRecordKind::RegisterSym); + cantFail(SymbolDeserializer::deserializeAs<RegisterSym>(sym, reg)); + param_type = reg.Index; + param_name = reg.Name; + break; + } + case S_LOCAL: { + LocalSym local(SymbolRecordKind::LocalSym); + cantFail(SymbolDeserializer::deserializeAs<LocalSym>(sym, local)); + if ((local.Flags & LocalSymFlags::IsParameter) == LocalSymFlags::None) + continue; + param_type = local.Type; + param_name = local.Name; + break; + } + case S_BLOCK32: + case S_INLINESITE: + case S_INLINESITE2: + // All parameters should come before the first block/inlinesite. If that + // isn't the case, then perhaps this is bad debug info that doesn't + // contain information about all parameters. + return; + default: + continue; + } + + PdbCompilandSymId param_uid(func_id.modi, record_offset); + clang::QualType qt = GetOrCreateType(param_type); + if (qt.isNull()) + return; + + CompilerType param_type_ct = m_clang.GetType(qt); + clang::ParmVarDecl *param = m_clang.CreateParameterDeclaration( + &function_decl, OptionalClangModuleID(), param_name.str().c_str(), + param_type_ct, clang::SC_None, true); + lldbassert(m_uid_to_decl.count(toOpaqueUid(param_uid)) == 0); + + m_uid_to_decl[toOpaqueUid(param_uid)] = param; + params.push_back(param); + ++i; + } + + if (!params.empty() && params.size() == param_count) + m_clang.SetFunctionParameters(&function_decl, params); +} + +clang::QualType PdbAstBuilder::CreateEnumType(PdbTypeSymId id, + const EnumRecord &er) { + clang::DeclContext *decl_context = nullptr; + std::string uname; + std::tie(decl_context, uname) = CreateDeclInfoForType(er, id.index); + if (!decl_context) + return {}; + + clang::QualType underlying_type = GetOrCreateType(er.UnderlyingType); + if (underlying_type.isNull()) + return {}; + + Declaration declaration; + CompilerType enum_ct = m_clang.CreateEnumerationType( + uname, decl_context, OptionalClangModuleID(), declaration, + ToCompilerType(underlying_type), er.isScoped()); + + TypeSystemClang::StartTagDeclarationDefinition(enum_ct); + TypeSystemClang::SetHasExternalStorage(enum_ct.GetOpaqueQualType(), true); + + return clang::QualType::getFromOpaquePtr(enum_ct.GetOpaqueQualType()); +} + +clang::QualType PdbAstBuilder::CreateArrayType(const ArrayRecord &ar) { + clang::QualType element_type = GetOrCreateType(ar.ElementType); + + SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( + m_clang.GetSymbolFile()->GetBackingSymbolFile()); + PdbIndex &index = pdb->GetIndex(); + uint64_t element_size = GetSizeOfType({ar.ElementType}, index.tpi()); + if (element_type.isNull() || element_size == 0) + return {}; + uint64_t element_count = ar.Size / element_size; + + CompilerType array_ct = m_clang.CreateArrayType(ToCompilerType(element_type), + element_count, false); + return clang::QualType::getFromOpaquePtr(array_ct.GetOpaqueQualType()); +} + +clang::QualType PdbAstBuilder::CreateFunctionType( + TypeIndex args_type_idx, TypeIndex return_type_idx, + llvm::codeview::CallingConvention calling_convention) { + SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( + m_clang.GetSymbolFile()->GetBackingSymbolFile()); + PdbIndex &index = pdb->GetIndex(); + TpiStream &stream = index.tpi(); + CVType args_cvt = stream.getType(args_type_idx); + ArgListRecord args; + llvm::cantFail( + TypeDeserializer::deserializeAs<ArgListRecord>(args_cvt, args)); + + llvm::ArrayRef<TypeIndex> arg_indices = llvm::ArrayRef(args.ArgIndices); + bool is_variadic = IsCVarArgsFunction(arg_indices); + if (is_variadic) + arg_indices = arg_indices.drop_back(); + + std::vector<CompilerType> arg_types; + arg_types.reserve(arg_indices.size()); + + for (TypeIndex arg_index : arg_indices) { + clang::QualType arg_type = GetOrCreateType(arg_index); + if (arg_type.isNull()) + continue; + arg_types.push_back(ToCompilerType(arg_type)); + } + + clang::QualType return_type = GetOrCreateType(return_type_idx); + if (return_type.isNull()) + return {}; + + std::optional<clang::CallingConv> cc = + TranslateCallingConvention(calling_convention); + if (!cc) + return {}; + + CompilerType return_ct = ToCompilerType(return_type); + CompilerType func_sig_ast_type = m_clang.CreateFunctionType( + return_ct, arg_types.data(), arg_types.size(), is_variadic, 0, *cc); + + return clang::QualType::getFromOpaquePtr( + func_sig_ast_type.GetOpaqueQualType()); +} + +static bool isTagDecl(clang::DeclContext &context) { + return llvm::isa<clang::TagDecl>(&context); +} + +static bool isFunctionDecl(clang::DeclContext &context) { + return llvm::isa<clang::FunctionDecl>(&context); +} + +static bool isBlockDecl(clang::DeclContext &context) { + return llvm::isa<clang::BlockDecl>(&context); +} + +void PdbAstBuilder::ParseNamespace(clang::DeclContext &context) { + clang::NamespaceDecl *ns = llvm::dyn_cast<clang::NamespaceDecl>(&context); + if (m_parsed_namespaces.contains(ns)) + return; + std::string qname = ns->getQualifiedNameAsString(); + SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( + m_clang.GetSymbolFile()->GetBackingSymbolFile()); + PdbIndex &index = pdb->GetIndex(); + TypeIndex ti{index.tpi().TypeIndexBegin()}; + for (const CVType &cvt : index.tpi().typeArray()) { + PdbTypeSymId tid{ti}; + ++ti; + + if (!IsTagRecord(cvt)) + continue; + + CVTagRecord tag = CVTagRecord::create(cvt); + + // Call CreateDeclInfoForType unconditionally so that the namespace info + // gets created. But only call CreateRecordType if the namespace name + // matches. + clang::DeclContext *context = nullptr; + std::string uname; + std::tie(context, uname) = CreateDeclInfoForType(tag.asTag(), tid.index); + if (!context || !context->isNamespace()) + continue; + + clang::NamespaceDecl *ns = llvm::cast<clang::NamespaceDecl>(context); + llvm::StringRef ns_name = ns->getName(); + if (ns_name.starts_with(qname)) { + ns_name = ns_name.drop_front(qname.size()); + if (ns_name.starts_with("::")) + GetOrCreateType(tid); + } + } + ParseAllFunctionsAndNonLocalVars(); + m_parsed_namespaces.insert(ns); +} + +void PdbAstBuilder::ParseAllTypes() { + llvm::call_once(m_parse_all_types, [this]() { + SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( + m_clang.GetSymbolFile()->GetBackingSymbolFile()); + PdbIndex &index = pdb->GetIndex(); + TypeIndex ti{index.tpi().TypeIndexBegin()}; + for (const CVType &cvt : index.tpi().typeArray()) { + PdbTypeSymId tid{ti}; + ++ti; + + if (!IsTagRecord(cvt)) + continue; + + GetOrCreateType(tid); + } + }); +} + +void PdbAstBuilder::ParseAllFunctionsAndNonLocalVars() { + llvm::call_once(m_parse_functions_and_non_local_vars, [this]() { + SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( + m_clang.GetSymbolFile()->GetBackingSymbolFile()); + PdbIndex &index = pdb->GetIndex(); + uint32_t module_count = index.dbi().modules().getModuleCount(); + for (uint16_t modi = 0; modi < module_count; ++modi) { + CompilandIndexItem &cii = index.compilands().GetOrCreateCompiland(modi); + const CVSymbolArray &symbols = cii.m_debug_stream.getSymbolArray(); + auto iter = symbols.begin(); + while (iter != symbols.end()) { + PdbCompilandSymId sym_id{modi, iter.offset()}; + + switch (iter->kind()) { + case S_GPROC32: + case S_LPROC32: + GetOrCreateFunctionDecl(sym_id); + iter = symbols.at(getScopeEndOffset(*iter)); + break; + case S_GDATA32: + case S_GTHREAD32: + case S_LDATA32: + case S_LTHREAD32: + GetOrCreateVariableDecl(PdbCompilandSymId(modi, 0), sym_id); + ++iter; + break; + default: + ++iter; + continue; + } + } + } + }); +} + +static CVSymbolArray skipFunctionParameters(clang::Decl &decl, + const CVSymbolArray &symbols) { + clang::FunctionDecl *func_decl = llvm::dyn_cast<clang::FunctionDecl>(&decl); + if (!func_decl) + return symbols; + unsigned int params = func_decl->getNumParams(); + if (params == 0) + return symbols; + + CVSymbolArray result = symbols; + + while (!result.empty()) { + if (params == 0) + return result; + + CVSymbol sym = *result.begin(); + result.drop_front(); + + if (!isLocalVariableType(sym.kind())) + continue; + + --params; + } + return result; +} + +void PdbAstBuilder::ParseBlockChildren(PdbCompilandSymId block_id) { + SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( + m_clang.GetSymbolFile()->GetBackingSymbolFile()); + PdbIndex &index = pdb->GetIndex(); + CVSymbol sym = index.ReadSymbolRecord(block_id); + lldbassert(sym.kind() == S_GPROC32 || sym.kind() == S_LPROC32 || + sym.kind() == S_BLOCK32 || sym.kind() == S_INLINESITE); + CompilandIndexItem &cii = + index.compilands().GetOrCreateCompiland(block_id.modi); + CVSymbolArray symbols = + cii.m_debug_stream.getSymbolArrayForScope(block_id.offset); + + // Function parameters should already have been created when the function was + // parsed. + if (sym.kind() == S_GPROC32 || sym.kind() == S_LPROC32) + symbols = + skipFunctionParameters(*m_uid_to_decl[toOpaqueUid(block_id)], symbols); + + symbols.drop_front(); + auto begin = symbols.begin(); + while (begin != symbols.end()) { + PdbCompilandSymId child_sym_id(block_id.modi, begin.offset()); + GetOrCreateSymbolForId(child_sym_id); + if (begin->kind() == S_BLOCK32 || begin->kind() == S_INLINESITE) { + ParseBlockChildren(child_sym_id); + begin = symbols.at(getScopeEndOffset(*begin)); + } + ++begin; + } +} + +void PdbAstBuilder::ParseDeclsForSimpleContext(clang::DeclContext &context) { + + clang::Decl *decl = clang::Decl::castFromDeclContext(&context); + lldbassert(decl); + + auto iter = m_decl_to_status.find(decl); + lldbassert(iter != m_decl_to_status.end()); + + if (auto *tag = llvm::dyn_cast<clang::TagDecl>(&context)) { + CompleteTagDecl(*tag); + return; + } + + if (isFunctionDecl(context) || isBlockDecl(context)) { + PdbCompilandSymId block_id = PdbSymUid(iter->second.uid).asCompilandSym(); + ParseBlockChildren(block_id); + } +} + +void PdbAstBuilder::ParseDeclsForContext(clang::DeclContext &context) { + // Namespaces aren't explicitly represented in the debug info, and the only + // way to parse them is to parse all type info, demangling every single type + // and trying to reconstruct the DeclContext hierarchy this way. Since this + // is an expensive operation, we have to special case it so that we do other + // work (such as parsing the items that appear within the namespaces) at the + // same time. + if (context.isTranslationUnit()) { + ParseAllTypes(); + ParseAllFunctionsAndNonLocalVars(); + return; + } + + if (context.isNamespace()) { + ParseNamespace(context); + return; + } + + if (isTagDecl(context) || isFunctionDecl(context) || isBlockDecl(context)) { + ParseDeclsForSimpleContext(context); + return; + } +} + +CompilerDecl PdbAstBuilder::ToCompilerDecl(clang::Decl &decl) { + return m_clang.GetCompilerDecl(&decl); +} + +CompilerType PdbAstBuilder::ToCompilerType(clang::QualType qt) { + return {m_clang.weak_from_this(), qt.getAsOpaquePtr()}; +} + +CompilerDeclContext +PdbAstBuilder::ToCompilerDeclContext(clang::DeclContext &context) { + return m_clang.CreateDeclContext(&context); +} + +clang::Decl * PdbAstBuilder::FromCompilerDecl(CompilerDecl decl) { + return ClangUtil::GetDecl(decl); +} + +clang::DeclContext * +PdbAstBuilder::FromCompilerDeclContext(CompilerDeclContext context) { + return static_cast<clang::DeclContext *>(context.GetOpaqueDeclContext()); +} + +void PdbAstBuilder::Dump(Stream &stream) { + m_clang.Dump(stream.AsRawOstream()); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.h new file mode 100644 index 000000000000..b7cad30c69c0 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.h @@ -0,0 +1,159 @@ +//===-- PdbAstBuilder.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_SOURCE_PLUGINS_SYMBOLFILE_NATIVEPDB_PDBASTBUILDER_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_NATIVEPDB_PDBASTBUILDER_H + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Threading.h" + +#include "Plugins/ExpressionParser/Clang/ClangASTImporter.h" + +#include "PdbIndex.h" +#include "PdbSymUid.h" +#include <optional> + +namespace clang { +class TagDecl; +class DeclContext; +class Decl; +class QualType; +class FunctionDecl; +class NamespaceDecl; +} // namespace clang + +namespace llvm { +namespace codeview { +class ProcSym; +} +} // namespace llvm + +namespace lldb_private { +class ClangASTImporter; +class ObjectFile; + +namespace npdb { +class PdbIndex; +struct VariableInfo; + +struct DeclStatus { + DeclStatus() = default; + DeclStatus(lldb::user_id_t uid, bool resolved) + : uid(uid), resolved(resolved) {} + lldb::user_id_t uid = 0; + bool resolved = false; +}; + +class PdbAstBuilder { +public: + // Constructors and Destructors + PdbAstBuilder(TypeSystemClang &clang); + + lldb_private::CompilerDeclContext GetTranslationUnitDecl(); + + std::optional<lldb_private::CompilerDecl> + GetOrCreateDeclForUid(PdbSymUid uid); + clang::DeclContext *GetOrCreateDeclContextForUid(PdbSymUid uid); + clang::DeclContext *GetParentDeclContext(PdbSymUid uid); + + clang::FunctionDecl *GetOrCreateFunctionDecl(PdbCompilandSymId func_id); + clang::FunctionDecl * + GetOrCreateInlinedFunctionDecl(PdbCompilandSymId inlinesite_id); + clang::BlockDecl *GetOrCreateBlockDecl(PdbCompilandSymId block_id); + clang::VarDecl *GetOrCreateVariableDecl(PdbCompilandSymId scope_id, + PdbCompilandSymId var_id); + clang::VarDecl *GetOrCreateVariableDecl(PdbGlobalSymId var_id); + clang::TypedefNameDecl *GetOrCreateTypedefDecl(PdbGlobalSymId id); + void ParseDeclsForContext(clang::DeclContext &context); + + clang::QualType GetBasicType(lldb::BasicType type); + clang::QualType GetOrCreateType(PdbTypeSymId type); + + bool CompleteTagDecl(clang::TagDecl &tag); + bool CompleteType(clang::QualType qt); + + CompilerDecl ToCompilerDecl(clang::Decl &decl); + CompilerType ToCompilerType(clang::QualType qt); + CompilerDeclContext ToCompilerDeclContext(clang::DeclContext &context); + clang::Decl *FromCompilerDecl(CompilerDecl decl); + clang::DeclContext *FromCompilerDeclContext(CompilerDeclContext context); + + TypeSystemClang &clang() { return m_clang; } + ClangASTImporter &GetClangASTImporter() { return m_importer; } + + void Dump(Stream &stream); + +private: + clang::Decl *TryGetDecl(PdbSymUid uid) const; + + using TypeIndex = llvm::codeview::TypeIndex; + + clang::QualType + CreatePointerType(const llvm::codeview::PointerRecord &pointer); + clang::QualType + CreateModifierType(const llvm::codeview::ModifierRecord &modifier); + clang::QualType CreateArrayType(const llvm::codeview::ArrayRecord &array); + clang::QualType CreateRecordType(PdbTypeSymId id, + const llvm::codeview::TagRecord &record); + clang::QualType CreateEnumType(PdbTypeSymId id, + const llvm::codeview::EnumRecord &record); + clang::QualType + CreateFunctionType(TypeIndex args_type_idx, TypeIndex return_type_idx, + llvm::codeview::CallingConvention calling_convention); + clang::QualType CreateType(PdbTypeSymId type); + + void CreateFunctionParameters(PdbCompilandSymId func_id, + clang::FunctionDecl &function_decl, + uint32_t param_count); + clang::Decl *GetOrCreateSymbolForId(PdbCompilandSymId id); + clang::VarDecl *CreateVariableDecl(PdbSymUid uid, + llvm::codeview::CVSymbol sym, + clang::DeclContext &scope); + clang::NamespaceDecl *GetOrCreateNamespaceDecl(const char *name, + clang::DeclContext &context); + clang::FunctionDecl *CreateFunctionDeclFromId(PdbTypeSymId func_tid, + PdbCompilandSymId func_sid); + clang::FunctionDecl * + CreateFunctionDecl(PdbCompilandSymId func_id, llvm::StringRef func_name, + TypeIndex func_ti, CompilerType func_ct, + uint32_t param_count, clang::StorageClass func_storage, + bool is_inline, clang::DeclContext *parent); + void ParseNamespace(clang::DeclContext &parent); + void ParseAllTypes(); + void ParseAllFunctionsAndNonLocalVars(); + void ParseDeclsForSimpleContext(clang::DeclContext &context); + void ParseBlockChildren(PdbCompilandSymId block_id); + + std::pair<clang::DeclContext *, std::string> + CreateDeclInfoForType(const llvm::codeview::TagRecord &record, TypeIndex ti); + std::pair<clang::DeclContext *, std::string> + CreateDeclInfoForUndecoratedName(llvm::StringRef uname); + clang::QualType CreateSimpleType(TypeIndex ti); + + TypeSystemClang &m_clang; + + ClangASTImporter m_importer; + llvm::once_flag m_parse_functions_and_non_local_vars; + llvm::once_flag m_parse_all_types; + llvm::DenseMap<clang::Decl *, DeclStatus> m_decl_to_status; + llvm::DenseMap<lldb::user_id_t, clang::Decl *> m_uid_to_decl; + llvm::DenseMap<lldb::user_id_t, clang::QualType> m_uid_to_type; + + // From class/struct's opaque_compiler_type_t to a set containing the pairs of + // method's name and CompilerType. + llvm::DenseMap<lldb::opaque_compiler_type_t, + llvm::SmallSet<std::pair<llvm::StringRef, CompilerType>, 8>> + m_cxx_record_map; + llvm::DenseSet<clang::NamespaceDecl *> m_parsed_namespaces; +}; + +} // namespace npdb +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_SYMBOLFILE_NATIVEPDB_PDBASTBUILDER_H diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbFPOProgramToDWARFExpression.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbFPOProgramToDWARFExpression.cpp new file mode 100644 index 000000000000..f28509acbf79 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbFPOProgramToDWARFExpression.cpp @@ -0,0 +1,103 @@ +//===-- PdbFPOProgramToDWARFExpression.cpp --------------------------------===// +// +// 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 "PdbFPOProgramToDWARFExpression.h" +#include "CodeViewRegisterMapping.h" + +#include "lldb/Symbol/PostfixExpression.h" +#include "lldb/Utility/LLDBAssert.h" +#include "lldb/Utility/Stream.h" +#include "llvm/ADT/DenseMap.h" + +#include "llvm/ADT/StringExtras.h" +#include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/DebugInfo/CodeView/EnumTables.h" +#include "llvm/Support/ScopedPrinter.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::postfix; + +static uint32_t ResolveLLDBRegisterNum(llvm::StringRef reg_name, llvm::Triple::ArchType arch_type) { + // lookup register name to get lldb register number + llvm::codeview::CPUType cpu_type; + switch (arch_type) { + case llvm::Triple::ArchType::aarch64: + cpu_type = llvm::codeview::CPUType::ARM64; + break; + + default: + cpu_type = llvm::codeview::CPUType::X64; + break; + } + + llvm::ArrayRef<llvm::EnumEntry<uint16_t>> register_names = + llvm::codeview::getRegisterNames(cpu_type); + auto it = llvm::find_if( + register_names, + [®_name](const llvm::EnumEntry<uint16_t> ®ister_entry) { + return reg_name.compare_insensitive(register_entry.Name) == 0; + }); + + if (it == register_names.end()) + return LLDB_INVALID_REGNUM; + + auto reg_id = static_cast<llvm::codeview::RegisterId>(it->Value); + return npdb::GetLLDBRegisterNumber(arch_type, reg_id); +} + +static Node *ResolveFPOProgram(llvm::StringRef program, + llvm::StringRef register_name, + llvm::Triple::ArchType arch_type, + llvm::BumpPtrAllocator &alloc) { + std::vector<std::pair<llvm::StringRef, Node *>> parsed = + postfix::ParseFPOProgram(program, alloc); + + for (auto it = parsed.begin(), end = parsed.end(); it != end; ++it) { + // Emplace valid dependent subtrees to make target assignment independent + // from predecessors. Resolve all other SymbolNodes as registers. + bool success = + ResolveSymbols(it->second, [&](SymbolNode &symbol) -> Node * { + for (const auto &pair : llvm::make_range(parsed.begin(), it)) { + if (pair.first == symbol.GetName()) + return pair.second; + } + + uint32_t reg_num = + ResolveLLDBRegisterNum(symbol.GetName().drop_front(1), arch_type); + + if (reg_num == LLDB_INVALID_REGNUM) + return nullptr; + + return MakeNode<RegisterNode>(alloc, reg_num); + }); + if (!success) + return nullptr; + + if (it->first == register_name) { + // found target assignment program - no need to parse further + return it->second; + } + } + + return nullptr; +} + +bool lldb_private::npdb::TranslateFPOProgramToDWARFExpression( + llvm::StringRef program, llvm::StringRef register_name, + llvm::Triple::ArchType arch_type, Stream &stream) { + llvm::BumpPtrAllocator node_alloc; + Node *target_program = + ResolveFPOProgram(program, register_name, arch_type, node_alloc); + if (target_program == nullptr) { + return false; + } + + ToDWARF(*target_program, stream); + return true; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbFPOProgramToDWARFExpression.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbFPOProgramToDWARFExpression.h new file mode 100644 index 000000000000..f6849f2083cc --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbFPOProgramToDWARFExpression.h @@ -0,0 +1,28 @@ +//===-- PdbFPOProgramToDWARFExpression.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_SOURCE_PLUGINS_SYMBOLFILE_NATIVEPDB_PDBFPOPROGRAMTODWARFEXPRESSION_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_NATIVEPDB_PDBFPOPROGRAMTODWARFEXPRESSION_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/TargetParser/Triple.h" + +namespace lldb_private { +class Stream; + +namespace npdb { + +bool TranslateFPOProgramToDWARFExpression(llvm::StringRef program, + llvm::StringRef register_name, + llvm::Triple::ArchType arch_type, + lldb_private::Stream &stream); + +} // namespace npdb +} // namespace lldb_private + +#endif diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbIndex.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbIndex.cpp new file mode 100644 index 000000000000..ea778fc6cca6 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbIndex.cpp @@ -0,0 +1,196 @@ +//===-- PdbIndex.cpp ------------------------------------------------------===// +// +// 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 "PdbIndex.h" +#include "PdbUtil.h" + +#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h" +#include "llvm/DebugInfo/PDB/Native/DbiStream.h" +#include "llvm/DebugInfo/PDB/Native/GlobalsStream.h" +#include "llvm/DebugInfo/PDB/Native/ISectionContribVisitor.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/PublicsStream.h" +#include "llvm/DebugInfo/PDB/Native/SymbolStream.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" +#include "llvm/Object/COFF.h" +#include "llvm/Support/Error.h" + +#include "lldb/Utility/LLDBAssert.h" +#include "lldb/lldb-defines.h" +#include <optional> + +using namespace lldb_private; +using namespace lldb_private::npdb; +using namespace llvm::codeview; +using namespace llvm::pdb; + +PdbIndex::PdbIndex() : m_cus(*this), m_va_to_modi(m_allocator) {} + +#define ASSIGN_PTR_OR_RETURN(result_ptr, expr) \ + { \ + auto expected_result = expr; \ + if (!expected_result) \ + return expected_result.takeError(); \ + result_ptr = &expected_result.get(); \ + } + +llvm::Expected<std::unique_ptr<PdbIndex>> +PdbIndex::create(llvm::pdb::PDBFile *file) { + lldbassert(file); + + std::unique_ptr<PdbIndex> result(new PdbIndex()); + ASSIGN_PTR_OR_RETURN(result->m_dbi, file->getPDBDbiStream()); + ASSIGN_PTR_OR_RETURN(result->m_tpi, file->getPDBTpiStream()); + ASSIGN_PTR_OR_RETURN(result->m_ipi, file->getPDBIpiStream()); + ASSIGN_PTR_OR_RETURN(result->m_info, file->getPDBInfoStream()); + ASSIGN_PTR_OR_RETURN(result->m_publics, file->getPDBPublicsStream()); + ASSIGN_PTR_OR_RETURN(result->m_globals, file->getPDBGlobalsStream()); + ASSIGN_PTR_OR_RETURN(result->m_symrecords, file->getPDBSymbolStream()); + + result->m_tpi->buildHashMap(); + + result->m_file = file; + + return std::move(result); +} + +lldb::addr_t PdbIndex::MakeVirtualAddress(uint16_t segment, + uint32_t offset) const { + uint32_t max_section = dbi().getSectionHeaders().size(); + // Segment indices are 1-based. + // If this is an absolute symbol, it's indicated by the magic section index + // |max_section+1|. In this case, the offset is meaningless, so just return. + if (segment == 0 || segment > max_section) + return LLDB_INVALID_ADDRESS; + + const llvm::object::coff_section &cs = dbi().getSectionHeaders()[segment - 1]; + return m_load_address + static_cast<lldb::addr_t>(cs.VirtualAddress) + + static_cast<lldb::addr_t>(offset); +} + +std::optional<uint16_t> PdbIndex::GetModuleIndexForAddr(uint16_t segment, + uint32_t offset) const { + return GetModuleIndexForVa(MakeVirtualAddress(segment, offset)); +} + +std::optional<uint16_t> PdbIndex::GetModuleIndexForVa(lldb::addr_t va) const { + auto iter = m_va_to_modi.find(va); + if (iter == m_va_to_modi.end()) + return std::nullopt; + + return iter.value(); +} + +void PdbIndex::ParseSectionContribs() { + class Visitor : public ISectionContribVisitor { + PdbIndex &m_ctx; + llvm::IntervalMap<uint64_t, uint16_t> &m_imap; + + public: + Visitor(PdbIndex &ctx, llvm::IntervalMap<uint64_t, uint16_t> &imap) + : m_ctx(ctx), m_imap(imap) {} + + void visit(const SectionContrib &C) override { + if (C.Size == 0) + return; + + uint64_t va = m_ctx.MakeVirtualAddress(C.ISect, C.Off); + if (va == LLDB_INVALID_ADDRESS) + return; + uint64_t end = va + C.Size; + // IntervalMap's start and end represent a closed range, not a half-open + // range, so we have to subtract 1. + m_imap.insert(va, end - 1, C.Imod); + } + void visit(const SectionContrib2 &C) override { visit(C.Base); } + }; + Visitor v(*this, m_va_to_modi); + dbi().visitSectionContributions(v); +} + +void PdbIndex::BuildAddrToSymbolMap(CompilandIndexItem &cci) { + lldbassert(cci.m_symbols_by_va.empty() && + "Addr to symbol map is already built!"); + uint16_t modi = cci.m_id.modi; + const CVSymbolArray &syms = cci.m_debug_stream.getSymbolArray(); + for (auto iter = syms.begin(); iter != syms.end(); ++iter) { + if (!SymbolHasAddress(*iter)) + continue; + + SegmentOffset so = GetSegmentAndOffset(*iter); + lldb::addr_t va = MakeVirtualAddress(so.segment, so.offset); + if (va == LLDB_INVALID_ADDRESS) + continue; + + PdbCompilandSymId cu_sym_id(modi, iter.offset()); + + // It's rare, but we could have multiple symbols with the same address + // because of identical comdat folding. Right now, the first one will win. + cci.m_symbols_by_va.insert(std::make_pair(va, PdbSymUid(cu_sym_id))); + } +} + +std::vector<SymbolAndUid> PdbIndex::FindSymbolsByVa(lldb::addr_t va) { + std::vector<SymbolAndUid> result; + + std::optional<uint16_t> modi = GetModuleIndexForVa(va); + if (!modi) + return result; + + CompilandIndexItem &cci = compilands().GetOrCreateCompiland(*modi); + if (cci.m_symbols_by_va.empty()) + BuildAddrToSymbolMap(cci); + + // The map is sorted by starting address of the symbol. So for example + // we could (in theory) have this situation + // + // [------------------] + // [----------] + // [-----------] + // [-------------] + // [----] + // [-----] + // ^ Address we're searching for + // In order to find this, we use the upper_bound of the key value which would + // be the first symbol whose starting address is higher than the element we're + // searching for. + + auto ub = cci.m_symbols_by_va.upper_bound(va); + + for (auto iter = cci.m_symbols_by_va.begin(); iter != ub; ++iter) { + PdbCompilandSymId cu_sym_id = iter->second.asCompilandSym(); + CVSymbol sym = ReadSymbolRecord(cu_sym_id); + + SegmentOffsetLength sol; + if (SymbolIsCode(sym)) + sol = GetSegmentOffsetAndLength(sym); + else + sol.so = GetSegmentAndOffset(sym); + + lldb::addr_t start = MakeVirtualAddress(sol.so.segment, sol.so.offset); + if (start == LLDB_INVALID_ADDRESS) + continue; + + lldb::addr_t end = start + sol.length; + if (va >= start && va < end) + result.push_back({std::move(sym), iter->second}); + } + + return result; +} + +CVSymbol PdbIndex::ReadSymbolRecord(PdbCompilandSymId cu_sym) const { + const CompilandIndexItem *cci = compilands().GetCompiland(cu_sym.modi); + auto iter = cci->m_debug_stream.getSymbolArray().at(cu_sym.offset); + lldbassert(iter != cci->m_debug_stream.getSymbolArray().end()); + return *iter; +} + +CVSymbol PdbIndex::ReadSymbolRecord(PdbGlobalSymId global) const { + return symrecords().readRecord(global.offset); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbIndex.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbIndex.h new file mode 100644 index 000000000000..796aa4c8dfd1 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbIndex.h @@ -0,0 +1,159 @@ +//===-- PdbIndex.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_SOURCE_PLUGINS_SYMBOLFILE_NATIVEPDB_PDBINDEX_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_NATIVEPDB_PDBINDEX_H + +#include "lldb/lldb-types.h" +#include "llvm/ADT/IntervalMap.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/PDBTypes.h" + +#include "CompileUnitIndex.h" +#include "PdbSymUid.h" + +#include <map> +#include <memory> +#include <optional> + +namespace llvm { +namespace pdb { +class DbiStream; +class TpiStream; +class InfoStream; +class PublicsStream; +class GlobalsStream; +class SymbolStream; +} // namespace pdb +} // namespace llvm + +namespace lldb_private { +namespace npdb { +struct SegmentOffset; + +/// PdbIndex - Lazy access to the important parts of a PDB file. +/// +/// This is a layer on top of LLVM's native PDB support libraries which cache +/// certain data when it is accessed the first time. The entire PDB file is +/// mapped into memory, and the underlying support libraries vend out memory +/// that is always backed by the file, so it is safe to hold StringRefs and +/// ArrayRefs into the backing memory as long as the PdbIndex instance is +/// alive. +class PdbIndex { + + /// The underlying PDB file. + llvm::pdb::PDBFile *m_file = nullptr; + + /// The DBI stream. This contains general high level information about the + /// features present in the PDB file, compile units (such as the information + /// necessary to locate full symbol information for each compile unit), + /// section contributions, and other data which is not specifically symbol or + /// type records. + llvm::pdb::DbiStream *m_dbi = nullptr; + + /// TPI (types) and IPI (indices) streams. These are both in the exact same + /// format with different data. Most type records are stored in the TPI + /// stream but certain specific types of records are stored in the IPI stream. + /// The IPI stream records can refer to the records in the TPI stream, but not + /// the other way around. + llvm::pdb::TpiStream *m_tpi = nullptr; + llvm::pdb::TpiStream *m_ipi = nullptr; + + /// This is called the "PDB Stream" in the Microsoft reference implementation. + /// It contains information about the structure of the file, as well as fields + /// used to match EXE and PDB. + llvm::pdb::InfoStream *m_info = nullptr; + + /// Publics stream. Is actually a serialized hash table where the keys are + /// addresses of symbols in the executable, and values are a record containing + /// mangled names and an index which can be used to locate more detailed info + /// about the symbol in the Symbol Records stream. The publics stream only + /// contains info about externally visible symbols. + llvm::pdb::PublicsStream *m_publics = nullptr; + + /// Globals stream. Contrary to its name, this does not contain information + /// about all "global variables" or "global functions". Rather, it is the + /// "global symbol table", i.e. it contains information about *every* symbol + /// in the executable. It is a hash table keyed on name, whose values are + /// indices into the symbol records stream to find the full record. + llvm::pdb::GlobalsStream *m_globals = nullptr; + + /// Symbol records stream. The publics and globals stream refer to records + /// in this stream. For some records, like constants and typedefs, the + /// complete record lives in this stream. For other symbol types, such as + /// functions, data, and other things that have been materialied into a + /// specific compile unit, the records here simply provide a reference + /// necessary to locate the full information. + llvm::pdb::SymbolStream *m_symrecords = nullptr; + + /// Index of all compile units, mapping identifier to |CompilandIndexItem| + /// instance. + CompileUnitIndex m_cus; + + /// An allocator for the interval maps + llvm::IntervalMap<lldb::addr_t, uint32_t>::Allocator m_allocator; + + /// Maps virtual address to module index + llvm::IntervalMap<lldb::addr_t, uint16_t> m_va_to_modi; + + /// The address at which the program has been loaded into memory. + lldb::addr_t m_load_address = 0; + + PdbIndex(); + + void BuildAddrToSymbolMap(CompilandIndexItem &cci); + +public: + static llvm::Expected<std::unique_ptr<PdbIndex>> create(llvm::pdb::PDBFile *); + + void SetLoadAddress(lldb::addr_t addr) { m_load_address = addr; } + lldb::addr_t GetLoadAddress() const { return m_load_address; } + void ParseSectionContribs(); + + llvm::pdb::PDBFile &pdb() { return *m_file; } + const llvm::pdb::PDBFile &pdb() const { return *m_file; } + + llvm::pdb::DbiStream &dbi() { return *m_dbi; } + const llvm::pdb::DbiStream &dbi() const { return *m_dbi; } + + llvm::pdb::TpiStream &tpi() { return *m_tpi; } + const llvm::pdb::TpiStream &tpi() const { return *m_tpi; } + + llvm::pdb::TpiStream &ipi() { return *m_ipi; } + const llvm::pdb::TpiStream &ipi() const { return *m_ipi; } + + llvm::pdb::InfoStream &info() { return *m_info; } + const llvm::pdb::InfoStream &info() const { return *m_info; } + + llvm::pdb::PublicsStream &publics() { return *m_publics; } + const llvm::pdb::PublicsStream &publics() const { return *m_publics; } + + llvm::pdb::GlobalsStream &globals() { return *m_globals; } + const llvm::pdb::GlobalsStream &globals() const { return *m_globals; } + + llvm::pdb::SymbolStream &symrecords() { return *m_symrecords; } + const llvm::pdb::SymbolStream &symrecords() const { return *m_symrecords; } + + CompileUnitIndex &compilands() { return m_cus; } + const CompileUnitIndex &compilands() const { return m_cus; } + + lldb::addr_t MakeVirtualAddress(uint16_t segment, uint32_t offset) const; + + std::vector<SymbolAndUid> FindSymbolsByVa(lldb::addr_t va); + + llvm::codeview::CVSymbol ReadSymbolRecord(PdbCompilandSymId cu_sym) const; + llvm::codeview::CVSymbol ReadSymbolRecord(PdbGlobalSymId global) const; + + std::optional<uint16_t> GetModuleIndexForAddr(uint16_t segment, + uint32_t offset) const; + std::optional<uint16_t> GetModuleIndexForVa(lldb::addr_t va) const; +}; +} // namespace npdb +} // namespace lldb_private + +#endif diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbSymUid.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbSymUid.cpp new file mode 100644 index 000000000000..67397d707110 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbSymUid.cpp @@ -0,0 +1,160 @@ +//===-- PdbSymUid.cpp -----------------------------------------------------===// +// +// 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 "PdbSymUid.h" + +using namespace lldb_private; +using namespace lldb_private::npdb; +using namespace llvm::codeview; + +namespace { +struct GenericIdRepr { + uint64_t tag : 4; + uint64_t data : 60; +}; + +struct CompilandIdRepr { + uint64_t tag : 4; + uint64_t modi : 16; + uint64_t unused : 44; +}; + +struct CompilandSymIdRepr { + uint64_t tag : 4; + uint64_t modi : 16; + uint64_t offset : 32; + uint64_t unused : 12; +}; + +struct GlobalSymIdRepr { + uint64_t tag : 4; + uint64_t offset : 32; + uint64_t pub : 1; + uint64_t unused : 27; +}; + +struct TypeSymIdRepr { + uint64_t tag : 4; + uint64_t index : 32; + uint64_t ipi : 1; + uint64_t unused : 27; +}; + +struct FieldListMemberIdRepr { + uint64_t tag : 4; + uint64_t index : 32; + uint64_t offset : 16; + uint64_t unused : 12; +}; + +static_assert(sizeof(CompilandIdRepr) == 8, "Invalid structure size!"); +static_assert(sizeof(CompilandSymIdRepr) == 8, "Invalid structure size!"); +static_assert(sizeof(GlobalSymIdRepr) == 8, "Invalid structure size!"); +static_assert(sizeof(TypeSymIdRepr) == 8, "Invalid structure size!"); +static_assert(sizeof(FieldListMemberIdRepr) == 8, "Invalid structure size!"); +} // namespace + +template <typename OutT, typename InT> static OutT repr_cast(const InT &value) { + OutT result; + ::memcpy(&result, &value, sizeof(value)); + return result; +} + +PdbSymUid::PdbSymUid(const PdbCompilandId &cid) { + CompilandIdRepr repr; + ::memset(&repr, 0, sizeof(repr)); + repr.modi = cid.modi; + repr.tag = static_cast<uint64_t>(PdbSymUidKind::Compiland); + m_repr = repr_cast<uint64_t>(repr); +} + +PdbSymUid::PdbSymUid(const PdbCompilandSymId &csid) { + CompilandSymIdRepr repr; + ::memset(&repr, 0, sizeof(repr)); + repr.modi = csid.modi; + repr.offset = csid.offset; + repr.tag = static_cast<uint64_t>(PdbSymUidKind::CompilandSym); + m_repr = repr_cast<uint64_t>(repr); +} + +PdbSymUid::PdbSymUid(const PdbGlobalSymId &gsid) { + GlobalSymIdRepr repr; + ::memset(&repr, 0, sizeof(repr)); + repr.pub = gsid.is_public; + repr.offset = gsid.offset; + repr.tag = static_cast<uint64_t>(PdbSymUidKind::GlobalSym); + m_repr = repr_cast<uint64_t>(repr); +} + +PdbSymUid::PdbSymUid(const PdbTypeSymId &tsid) { + TypeSymIdRepr repr; + ::memset(&repr, 0, sizeof(repr)); + repr.index = tsid.index.getIndex(); + repr.ipi = tsid.is_ipi; + repr.tag = static_cast<uint64_t>(PdbSymUidKind::Type); + m_repr = repr_cast<uint64_t>(repr); +} + +PdbSymUid::PdbSymUid(const PdbFieldListMemberId &flmid) { + FieldListMemberIdRepr repr; + ::memset(&repr, 0, sizeof(repr)); + repr.index = flmid.index.getIndex(); + repr.offset = flmid.offset; + repr.tag = static_cast<uint64_t>(PdbSymUidKind::FieldListMember); + m_repr = repr_cast<uint64_t>(repr); +} + +PdbSymUidKind PdbSymUid::kind() const { + GenericIdRepr generic = repr_cast<GenericIdRepr>(m_repr); + return static_cast<PdbSymUidKind>(generic.tag); +} + +PdbCompilandId PdbSymUid::asCompiland() const { + assert(kind() == PdbSymUidKind::Compiland); + auto repr = repr_cast<CompilandIdRepr>(m_repr); + PdbCompilandId result; + result.modi = repr.modi; + return result; +} + +PdbCompilandSymId PdbSymUid::asCompilandSym() const { + assert(kind() == PdbSymUidKind::CompilandSym); + auto repr = repr_cast<CompilandSymIdRepr>(m_repr); + PdbCompilandSymId result; + result.modi = repr.modi; + result.offset = repr.offset; + return result; +} + +PdbGlobalSymId PdbSymUid::asGlobalSym() const { + assert(kind() == PdbSymUidKind::GlobalSym || + kind() == PdbSymUidKind::PublicSym); + auto repr = repr_cast<GlobalSymIdRepr>(m_repr); + PdbGlobalSymId result; + result.is_public = repr.pub; + result.offset = repr.offset; + return result; +} + +PdbTypeSymId PdbSymUid::asTypeSym() const { + assert(kind() == PdbSymUidKind::Type); + auto repr = repr_cast<TypeSymIdRepr>(m_repr); + PdbTypeSymId result; + result.index.setIndex(repr.index); + result.is_ipi = repr.ipi; + return result; +} + +PdbFieldListMemberId PdbSymUid::asFieldListMember() const { + assert(kind() == PdbSymUidKind::FieldListMember); + auto repr = repr_cast<FieldListMemberIdRepr>(m_repr); + PdbFieldListMemberId result; + result.index.setIndex(repr.index); + result.offset = repr.offset; + return result; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbSymUid.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbSymUid.h new file mode 100644 index 000000000000..3accd38d710e --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbSymUid.h @@ -0,0 +1,125 @@ +//===-- PdbSymUid.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 +// +//===----------------------------------------------------------------------===// +// A unique identification scheme for Pdb records. +// The scheme is to partition a 64-bit integer into an 8-bit tag field, which +// will contain some value from the PDB_SymType enumeration. The format of the +// other 48-bits depend on the tag, but must be sufficient to locate the +// corresponding entry in the underlying PDB file quickly. For example, for +// a compile unit, we use 2 bytes to represent the index, which allows fast +// access to the compile unit's information. +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_SYMBOLFILE_NATIVEPDB_PDBSYMUID_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_NATIVEPDB_PDBSYMUID_H + +#include "llvm/DebugInfo/CodeView/SymbolRecord.h" +#include "llvm/DebugInfo/PDB/PDBTypes.h" +#include "llvm/Support/Compiler.h" + +#include "lldb/Utility/LLDBAssert.h" +#include "lldb/lldb-types.h" + +namespace lldb_private { +namespace npdb { + +enum class PdbSymUidKind : uint8_t { + Compiland, + CompilandSym, + PublicSym, + GlobalSym, + Type, + FieldListMember +}; + +struct PdbCompilandId { + // 0-based index of module in PDB + uint16_t modi; +}; + +struct PdbCompilandSymId { + PdbCompilandSymId() = default; + PdbCompilandSymId(uint16_t modi, uint32_t offset) + : modi(modi), offset(offset) {} + // 0-based index of module in PDB + uint16_t modi = 0; + + // Offset of symbol's record in module stream. This is + // offset by 4 from the CVSymbolArray's notion of offset + // due to the debug magic at the beginning of the stream. + uint32_t offset = 0; +}; + +struct PdbGlobalSymId { + PdbGlobalSymId() = default; + PdbGlobalSymId(uint32_t offset, bool is_public) + : offset(offset), is_public(is_public) {} + + // Offset of symbol's record in globals or publics stream. + uint32_t offset = 0; + + // True if this symbol is in the public stream, false if it's in the globals + // stream. + bool is_public = false; +}; + +struct PdbTypeSymId { + PdbTypeSymId() = default; + PdbTypeSymId(llvm::codeview::TypeIndex index, bool is_ipi = false) + : index(index), is_ipi(is_ipi) {} + + // The index of the of the type in the TPI or IPI stream. + llvm::codeview::TypeIndex index; + + // True if this symbol comes from the IPI stream, false if it's from the TPI + // stream. + bool is_ipi = false; +}; + +struct PdbFieldListMemberId { + // The TypeIndex of the LF_FIELDLIST record. + llvm::codeview::TypeIndex index; + + // The offset from the beginning of the LF_FIELDLIST record to this record. + uint16_t offset = 0; +}; + +class PdbSymUid { + uint64_t m_repr = 0; + +public: + PdbSymUid() = default; + PdbSymUid(uint64_t repr) : m_repr(repr) {} + PdbSymUid(const PdbCompilandId &cid); + PdbSymUid(const PdbCompilandSymId &csid); + PdbSymUid(const PdbGlobalSymId &gsid); + PdbSymUid(const PdbTypeSymId &tsid); + PdbSymUid(const PdbFieldListMemberId &flmid); + + uint64_t toOpaqueId() const { return m_repr; } + + PdbSymUidKind kind() const; + + PdbCompilandId asCompiland() const; + PdbCompilandSymId asCompilandSym() const; + PdbGlobalSymId asGlobalSym() const; + PdbTypeSymId asTypeSym() const; + PdbFieldListMemberId asFieldListMember() const; +}; + +template <typename T> uint64_t toOpaqueUid(const T &cid) { + return PdbSymUid(cid).toOpaqueId(); +} + +struct SymbolAndUid { + llvm::codeview::CVSymbol sym; + PdbSymUid uid; +}; +} // namespace npdb +} // namespace lldb_private + +#endif diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp new file mode 100644 index 000000000000..888bd89a7262 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp @@ -0,0 +1,1120 @@ +//===-- PdbUtil.cpp -------------------------------------------------------===// +// +// 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 "PdbUtil.h" + +#include "DWARFLocationExpression.h" +#include "PdbIndex.h" +#include "PdbSymUid.h" + +#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" +#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h" +#include "llvm/DebugInfo/CodeView/TypeDeserializer.h" +#include "llvm/DebugInfo/PDB/Native/DbiStream.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" + +#include "Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.h" +#include "Plugins/SymbolFile/NativePDB/CodeViewRegisterMapping.h" +#include "lldb/Symbol/Block.h" +#include "lldb/Utility/LLDBAssert.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/lldb-enumerations.h" + +using namespace lldb_private; +using namespace lldb_private::npdb; +using namespace llvm::codeview; +using namespace llvm::pdb; + +// The returned range list is guaranteed to be sorted and no overlaps between +// adjacent ranges because fields in LocalVariableAddrGap are unsigned integers. +static Variable::RangeList +MakeRangeList(const PdbIndex &index, const LocalVariableAddrRange &range, + llvm::ArrayRef<LocalVariableAddrGap> gaps) { + lldb::addr_t start = + index.MakeVirtualAddress(range.ISectStart, range.OffsetStart); + if (start == LLDB_INVALID_ADDRESS) + return {}; + lldb::addr_t end = start + range.Range; + + Variable::RangeList result; + while (!gaps.empty()) { + const LocalVariableAddrGap &gap = gaps.front(); + lldb::addr_t gap_start = start + gap.GapStartOffset; + result.Append(start, gap_start - start); + start = gap_start + gap.Range; + gaps = gaps.drop_front(); + } + + result.Append(start, end - start); + return result; +} + +namespace { +struct MemberLocations { + std::map<uint64_t, MemberValLocation> offset_to_location; + DWARFExpression expr; + bool is_dwarf = false; + + MemberLocations() = default; + MemberLocations(const DWARFExpression &expr) : expr(expr), is_dwarf(true) {} + MemberLocations(uint64_t offset, const MemberValLocation &member_loc) { + insert(offset, member_loc); + } + + void insert(uint64_t offset, const MemberValLocation &member_loc) { + offset_to_location[offset] = member_loc; + } + + struct Comparator { + public: + bool operator()(const MemberLocations &, const MemberLocations &) const { + return false; + } + }; +}; + +// A range map with address ranges to a map of pair of offset and locaitons. +typedef RangeDataVector<lldb::addr_t, lldb::addr_t, MemberLocations, 0, + MemberLocations::Comparator> + RangeMap; + +void AddMemberLocationRanges(RangeMap &location_map, uint64_t offset, + MemberValLocation member_loc, + const Variable::RangeList &ranges) { + RangeMap new_location_map; + auto add_overlap_region = [&](lldb::addr_t base, lldb::addr_t end, + RangeMap::Entry *entry) { + RangeMap::Entry overlap_region = {base, end - base, entry->data}; + overlap_region.data.insert(offset, member_loc); + new_location_map.Append(overlap_region); + }; + + for (const auto &range : ranges) { + lldb::addr_t base = range.GetRangeBase(); + lldb::addr_t end = range.GetRangeEnd(); + uint32_t base_idx = location_map.FindEntryIndexThatContainsOrFollows(base); + while (auto *entry = location_map.GetMutableEntryAtIndex(base_idx)) { + if (base >= end || entry->base >= end) + break; + if (entry->data.is_dwarf) + base = entry->GetRangeEnd(); + else { + lldb::addr_t entry_end = entry->GetRangeEnd(); + if (base > entry->base) { + if (end < entry_end) + new_location_map.Append({end, entry_end - end, entry->data}); + add_overlap_region(base, end < entry_end ? end : entry_end, entry); + entry->SetRangeEnd(base); + } else if (base < entry->base) { + new_location_map.Append( + {base, entry->base - base, {offset, member_loc}}); + if (entry_end == end) + entry->data.insert(offset, member_loc); + else { + add_overlap_region(entry->base, end, entry); + entry->ShrinkFront(end - entry->base); + } + } else { + if (end < entry_end) { + new_location_map.Append({end, entry_end, entry->data}); + entry->SetRangeEnd(end); + } + entry->data.insert(offset, member_loc); + } + base = entry_end; + } + ++base_idx; + } + if (base >= end) + continue; + new_location_map.Append({base, end - base, {offset, member_loc}}); + } + for (const auto &entry : new_location_map) + location_map.Append(entry); + if (!new_location_map.IsEmpty()) + location_map.Sort(); +} + +void AddDwarfRange(RangeMap &location_map, const DWARFExpression &expr, + const Variable::RangeList &ranges) { + if (!expr.IsValid()) + return; + RangeMap new_location_map; + for (const auto &range : ranges) { + lldb::addr_t base = range.GetRangeBase(); + lldb::addr_t end = range.GetRangeEnd(); + uint32_t base_idx = location_map.FindEntryIndexThatContains(base); + uint32_t end_idx = location_map.FindEntryIndexThatContains(end - 1); + // range is within an entry. + if (base_idx == end_idx && base_idx != UINT32_MAX) { + auto *entry = location_map.GetMutableEntryAtIndex(base_idx); + if (base > entry->base) { + new_location_map.Append({entry->base, base - entry->base, entry->data}); + entry->ShrinkFront(base - entry->base); + } + if (end == entry->GetRangeEnd()) + entry->data = expr; + else { + entry->ShrinkFront(end - base); + new_location_map.Append({base, end - base, expr}); + } + continue; + } + base_idx = location_map.FindEntryIndexThatContainsOrFollows(base); + if (auto *entry = location_map.GetMutableEntryAtIndex(base_idx)) { + if (entry->Contains(base) && entry->base != base) { + entry->SetRangeEnd(base); + ++base_idx; + } + } + end_idx = location_map.FindEntryIndexThatContainsOrFollows(end - 1); + if (auto *entry = location_map.GetMutableEntryAtIndex(end_idx)) { + if (entry->Contains(end - 1)) { + if (entry->GetRangeEnd() == end) + ++end_idx; + else + entry->ShrinkFront(end - entry->base); + } + } + + if (end_idx == UINT32_MAX) + end_idx = location_map.GetSize(); + // Erase existing ranges covered by new range. + location_map.Erase(base_idx, end_idx); + new_location_map.Append({base, end - base, expr}); + } + + for (const auto &entry : new_location_map) + location_map.Append(entry); + location_map.Sort(); +} +} // namespace + +CVTagRecord CVTagRecord::create(CVType type) { + assert(IsTagRecord(type) && "type is not a tag record!"); + switch (type.kind()) { + case LF_CLASS: + case LF_STRUCTURE: + case LF_INTERFACE: { + ClassRecord cr; + llvm::cantFail(TypeDeserializer::deserializeAs<ClassRecord>(type, cr)); + return CVTagRecord(std::move(cr)); + } + case LF_UNION: { + UnionRecord ur; + llvm::cantFail(TypeDeserializer::deserializeAs<UnionRecord>(type, ur)); + return CVTagRecord(std::move(ur)); + } + case LF_ENUM: { + EnumRecord er; + llvm::cantFail(TypeDeserializer::deserializeAs<EnumRecord>(type, er)); + return CVTagRecord(std::move(er)); + } + default: + llvm_unreachable("Unreachable!"); + } +} + +CVTagRecord::CVTagRecord(ClassRecord &&c) + : cvclass(std::move(c)), + m_kind(cvclass.Kind == TypeRecordKind::Struct ? Struct : Class) {} +CVTagRecord::CVTagRecord(UnionRecord &&u) + : cvunion(std::move(u)), m_kind(Union) {} +CVTagRecord::CVTagRecord(EnumRecord &&e) : cvenum(std::move(e)), m_kind(Enum) {} + +PDB_SymType lldb_private::npdb::CVSymToPDBSym(SymbolKind kind) { + switch (kind) { + case S_COMPILE3: + case S_OBJNAME: + return PDB_SymType::CompilandDetails; + case S_ENVBLOCK: + return PDB_SymType::CompilandEnv; + case S_THUNK32: + case S_TRAMPOLINE: + return PDB_SymType::Thunk; + case S_COFFGROUP: + return PDB_SymType::CoffGroup; + case S_EXPORT: + return PDB_SymType::Export; + case S_LPROC32: + case S_GPROC32: + case S_LPROC32_DPC: + return PDB_SymType::Function; + case S_PUB32: + return PDB_SymType::PublicSymbol; + case S_INLINESITE: + return PDB_SymType::InlineSite; + case S_LOCAL: + case S_BPREL32: + case S_REGREL32: + case S_MANCONSTANT: + case S_CONSTANT: + case S_LDATA32: + case S_GDATA32: + case S_LMANDATA: + case S_GMANDATA: + case S_LTHREAD32: + case S_GTHREAD32: + return PDB_SymType::Data; + case S_BLOCK32: + return PDB_SymType::Block; + case S_LABEL32: + return PDB_SymType::Label; + case S_CALLSITEINFO: + return PDB_SymType::CallSite; + case S_HEAPALLOCSITE: + return PDB_SymType::HeapAllocationSite; + case S_CALLEES: + return PDB_SymType::Callee; + case S_CALLERS: + return PDB_SymType::Caller; + default: + lldbassert(false && "Invalid symbol record kind!"); + } + return PDB_SymType::None; +} + +PDB_SymType lldb_private::npdb::CVTypeToPDBType(TypeLeafKind kind) { + switch (kind) { + case LF_ARRAY: + return PDB_SymType::ArrayType; + case LF_ARGLIST: + return PDB_SymType::FunctionSig; + case LF_BCLASS: + return PDB_SymType::BaseClass; + case LF_BINTERFACE: + return PDB_SymType::BaseInterface; + case LF_CLASS: + case LF_STRUCTURE: + case LF_INTERFACE: + case LF_UNION: + return PDB_SymType::UDT; + case LF_POINTER: + return PDB_SymType::PointerType; + case LF_ENUM: + return PDB_SymType::Enum; + case LF_PROCEDURE: + return PDB_SymType::FunctionSig; + case LF_BITFIELD: + return PDB_SymType::BuiltinType; + default: + lldbassert(false && "Invalid type record kind!"); + } + return PDB_SymType::None; +} + +bool lldb_private::npdb::SymbolHasAddress(const CVSymbol &sym) { + switch (sym.kind()) { + case S_GPROC32: + case S_LPROC32: + case S_GPROC32_ID: + case S_LPROC32_ID: + case S_LPROC32_DPC: + case S_LPROC32_DPC_ID: + case S_THUNK32: + case S_TRAMPOLINE: + case S_COFFGROUP: + case S_BLOCK32: + case S_LABEL32: + case S_CALLSITEINFO: + case S_HEAPALLOCSITE: + case S_LDATA32: + case S_GDATA32: + case S_LMANDATA: + case S_GMANDATA: + case S_LTHREAD32: + case S_GTHREAD32: + return true; + default: + return false; + } +} + +bool lldb_private::npdb::SymbolIsCode(const CVSymbol &sym) { + switch (sym.kind()) { + case S_GPROC32: + case S_LPROC32: + case S_GPROC32_ID: + case S_LPROC32_ID: + case S_LPROC32_DPC: + case S_LPROC32_DPC_ID: + case S_THUNK32: + case S_TRAMPOLINE: + case S_COFFGROUP: + case S_BLOCK32: + return true; + default: + return false; + } +} + +template <typename RecordT> RecordT createRecord(const CVSymbol &sym) { + RecordT record(static_cast<SymbolRecordKind>(sym.kind())); + cantFail(SymbolDeserializer::deserializeAs<RecordT>(sym, record)); + return record; +} + +template <typename RecordT> +static SegmentOffset GetSegmentAndOffset(const CVSymbol &sym) { + RecordT record = createRecord<RecordT>(sym); + return {record.Segment, record.CodeOffset}; +} + +template <> +SegmentOffset GetSegmentAndOffset<TrampolineSym>(const CVSymbol &sym) { + TrampolineSym record = createRecord<TrampolineSym>(sym); + return {record.ThunkSection, record.ThunkOffset}; +} + +template <> SegmentOffset GetSegmentAndOffset<Thunk32Sym>(const CVSymbol &sym) { + Thunk32Sym record = createRecord<Thunk32Sym>(sym); + return {record.Segment, record.Offset}; +} + +template <> +SegmentOffset GetSegmentAndOffset<CoffGroupSym>(const CVSymbol &sym) { + CoffGroupSym record = createRecord<CoffGroupSym>(sym); + return {record.Segment, record.Offset}; +} + +template <> SegmentOffset GetSegmentAndOffset<DataSym>(const CVSymbol &sym) { + DataSym record = createRecord<DataSym>(sym); + return {record.Segment, record.DataOffset}; +} + +template <> +SegmentOffset GetSegmentAndOffset<ThreadLocalDataSym>(const CVSymbol &sym) { + ThreadLocalDataSym record = createRecord<ThreadLocalDataSym>(sym); + return {record.Segment, record.DataOffset}; +} + +SegmentOffset lldb_private::npdb::GetSegmentAndOffset(const CVSymbol &sym) { + switch (sym.kind()) { + case S_GPROC32: + case S_LPROC32: + case S_GPROC32_ID: + case S_LPROC32_ID: + case S_LPROC32_DPC: + case S_LPROC32_DPC_ID: + return ::GetSegmentAndOffset<ProcSym>(sym); + case S_THUNK32: + return ::GetSegmentAndOffset<Thunk32Sym>(sym); + break; + case S_TRAMPOLINE: + return ::GetSegmentAndOffset<TrampolineSym>(sym); + break; + case S_COFFGROUP: + return ::GetSegmentAndOffset<CoffGroupSym>(sym); + break; + case S_BLOCK32: + return ::GetSegmentAndOffset<BlockSym>(sym); + break; + case S_LABEL32: + return ::GetSegmentAndOffset<LabelSym>(sym); + break; + case S_CALLSITEINFO: + return ::GetSegmentAndOffset<CallSiteInfoSym>(sym); + break; + case S_HEAPALLOCSITE: + return ::GetSegmentAndOffset<HeapAllocationSiteSym>(sym); + break; + case S_LDATA32: + case S_GDATA32: + case S_LMANDATA: + case S_GMANDATA: + return ::GetSegmentAndOffset<DataSym>(sym); + break; + case S_LTHREAD32: + case S_GTHREAD32: + return ::GetSegmentAndOffset<ThreadLocalDataSym>(sym); + break; + default: + lldbassert(false && "Record does not have a segment/offset!"); + } + return {0, 0}; +} + +template <typename RecordT> +SegmentOffsetLength GetSegmentOffsetAndLength(const CVSymbol &sym) { + RecordT record = createRecord<RecordT>(sym); + return {record.Segment, record.CodeOffset, record.CodeSize}; +} + +template <> +SegmentOffsetLength +GetSegmentOffsetAndLength<TrampolineSym>(const CVSymbol &sym) { + TrampolineSym record = createRecord<TrampolineSym>(sym); + return {record.ThunkSection, record.ThunkOffset, record.Size}; +} + +template <> +SegmentOffsetLength GetSegmentOffsetAndLength<Thunk32Sym>(const CVSymbol &sym) { + Thunk32Sym record = createRecord<Thunk32Sym>(sym); + return SegmentOffsetLength{record.Segment, record.Offset, record.Length}; +} + +template <> +SegmentOffsetLength +GetSegmentOffsetAndLength<CoffGroupSym>(const CVSymbol &sym) { + CoffGroupSym record = createRecord<CoffGroupSym>(sym); + return SegmentOffsetLength{record.Segment, record.Offset, record.Size}; +} + +SegmentOffsetLength +lldb_private::npdb::GetSegmentOffsetAndLength(const CVSymbol &sym) { + switch (sym.kind()) { + case S_GPROC32: + case S_LPROC32: + case S_GPROC32_ID: + case S_LPROC32_ID: + case S_LPROC32_DPC: + case S_LPROC32_DPC_ID: + return ::GetSegmentOffsetAndLength<ProcSym>(sym); + case S_THUNK32: + return ::GetSegmentOffsetAndLength<Thunk32Sym>(sym); + break; + case S_TRAMPOLINE: + return ::GetSegmentOffsetAndLength<TrampolineSym>(sym); + break; + case S_COFFGROUP: + return ::GetSegmentOffsetAndLength<CoffGroupSym>(sym); + break; + case S_BLOCK32: + return ::GetSegmentOffsetAndLength<BlockSym>(sym); + break; + default: + lldbassert(false && "Record does not have a segment/offset/length triple!"); + } + return {0, 0, 0}; +} + +bool lldb_private::npdb::IsForwardRefUdt(CVType cvt) { + ClassRecord cr; + UnionRecord ur; + EnumRecord er; + switch (cvt.kind()) { + case LF_CLASS: + case LF_STRUCTURE: + case LF_INTERFACE: + llvm::cantFail(TypeDeserializer::deserializeAs<ClassRecord>(cvt, cr)); + return cr.isForwardRef(); + case LF_UNION: + llvm::cantFail(TypeDeserializer::deserializeAs<UnionRecord>(cvt, ur)); + return ur.isForwardRef(); + case LF_ENUM: + llvm::cantFail(TypeDeserializer::deserializeAs<EnumRecord>(cvt, er)); + return er.isForwardRef(); + default: + return false; + } +} + +bool lldb_private::npdb::IsTagRecord(llvm::codeview::CVType cvt) { + switch (cvt.kind()) { + case LF_CLASS: + case LF_STRUCTURE: + case LF_UNION: + case LF_ENUM: + return true; + default: + return false; + } +} + +bool lldb_private::npdb::IsClassStructUnion(llvm::codeview::CVType cvt) { + switch (cvt.kind()) { + case LF_CLASS: + case LF_STRUCTURE: + case LF_UNION: + return true; + default: + return false; + } +} + +bool lldb_private::npdb::IsForwardRefUdt(const PdbTypeSymId &id, + TpiStream &tpi) { + if (id.is_ipi || id.index.isSimple()) + return false; + return IsForwardRefUdt(tpi.getType(id.index)); +} + +bool lldb_private::npdb::IsTagRecord(const PdbTypeSymId &id, TpiStream &tpi) { + if (id.is_ipi || id.index.isSimple()) + return false; + return IsTagRecord(tpi.getType(id.index)); +} + +lldb::AccessType +lldb_private::npdb::TranslateMemberAccess(MemberAccess access) { + switch (access) { + case MemberAccess::Private: + return lldb::eAccessPrivate; + case MemberAccess::Protected: + return lldb::eAccessProtected; + case MemberAccess::Public: + return lldb::eAccessPublic; + case MemberAccess::None: + return lldb::eAccessNone; + } + llvm_unreachable("unreachable"); +} + +TypeIndex lldb_private::npdb::GetFieldListIndex(CVType cvt) { + switch (cvt.kind()) { + case LF_CLASS: + case LF_STRUCTURE: + case LF_INTERFACE: { + ClassRecord cr; + cantFail(TypeDeserializer::deserializeAs<ClassRecord>(cvt, cr)); + return cr.FieldList; + } + case LF_UNION: { + UnionRecord ur; + cantFail(TypeDeserializer::deserializeAs<UnionRecord>(cvt, ur)); + return ur.FieldList; + } + case LF_ENUM: { + EnumRecord er; + cantFail(TypeDeserializer::deserializeAs<EnumRecord>(cvt, er)); + return er.FieldList; + } + default: + llvm_unreachable("Unreachable!"); + } +} + +TypeIndex lldb_private::npdb::LookThroughModifierRecord(CVType modifier) { + lldbassert(modifier.kind() == LF_MODIFIER); + ModifierRecord mr; + llvm::cantFail(TypeDeserializer::deserializeAs<ModifierRecord>(modifier, mr)); + return mr.ModifiedType; +} + +llvm::StringRef lldb_private::npdb::DropNameScope(llvm::StringRef name) { + return MSVCUndecoratedNameParser::DropScope(name); +} + +VariableInfo lldb_private::npdb::GetVariableNameInfo(CVSymbol sym) { + VariableInfo result = {}; + + if (sym.kind() == S_REGREL32) { + RegRelativeSym reg(SymbolRecordKind::RegRelativeSym); + cantFail(SymbolDeserializer::deserializeAs<RegRelativeSym>(sym, reg)); + result.type = reg.Type; + result.name = reg.Name; + return result; + } + + if (sym.kind() == S_REGISTER) { + RegisterSym reg(SymbolRecordKind::RegisterSym); + cantFail(SymbolDeserializer::deserializeAs<RegisterSym>(sym, reg)); + result.type = reg.Index; + result.name = reg.Name; + return result; + } + + if (sym.kind() == S_LOCAL) { + LocalSym local(SymbolRecordKind::LocalSym); + cantFail(SymbolDeserializer::deserializeAs<LocalSym>(sym, local)); + result.type = local.Type; + result.name = local.Name; + result.is_param = + ((local.Flags & LocalSymFlags::IsParameter) != LocalSymFlags::None); + return result; + } + + if (sym.kind() == S_GDATA32 || sym.kind() == S_LDATA32) { + DataSym data(SymbolRecordKind::DataSym); + cantFail(SymbolDeserializer::deserializeAs<DataSym>(sym, data)); + result.type = data.Type; + result.name = data.Name; + return result; + } + + if (sym.kind() == S_GTHREAD32 || sym.kind() == S_LTHREAD32) { + ThreadLocalDataSym data(SymbolRecordKind::ThreadLocalDataSym); + cantFail(SymbolDeserializer::deserializeAs<ThreadLocalDataSym>(sym, data)); + result.type = data.Type; + result.name = data.Name; + return result; + } + + if (sym.kind() == S_CONSTANT) { + ConstantSym constant(SymbolRecordKind::ConstantSym); + cantFail(SymbolDeserializer::deserializeAs<ConstantSym>(sym, constant)); + result.type = constant.Type; + result.name = constant.Name; + return result; + } + + lldbassert(false && "Invalid variable record kind!"); + return {}; +} + +static llvm::FixedStreamArray<FrameData>::Iterator +GetCorrespondingFrameData(lldb::addr_t load_addr, + const DebugFrameDataSubsectionRef &fpo_data, + const Variable::RangeList &ranges) { + lldbassert(!ranges.IsEmpty()); + + // assume that all variable ranges correspond to one frame data + using RangeListEntry = Variable::RangeList::Entry; + const RangeListEntry &range = ranges.GetEntryRef(0); + + auto it = fpo_data.begin(); + + // start by searching first frame data range containing variable range + for (; it != fpo_data.end(); ++it) { + RangeListEntry fd_range(load_addr + it->RvaStart, it->CodeSize); + + if (fd_range.Contains(range)) { + break; + } + } + + // then first most nested entry that still contains variable range + auto found = it; + for (; it != fpo_data.end(); ++it) { + RangeListEntry fd_range(load_addr + it->RvaStart, it->CodeSize); + + if (!fd_range.Contains(range)) { + break; + } + found = it; + } + + return found; +} + +static bool GetFrameDataProgram(PdbIndex &index, + const Variable::RangeList &ranges, + llvm::StringRef &out_program) { + const DebugFrameDataSubsectionRef &new_fpo_data = + index.dbi().getNewFpoRecords(); + + auto frame_data_it = + GetCorrespondingFrameData(index.GetLoadAddress(), new_fpo_data, ranges); + if (frame_data_it == new_fpo_data.end()) + return false; + + auto strings = index.pdb().getStringTable(); + if (!strings) { + consumeError(strings.takeError()); + return false; + } + out_program = cantFail(strings->getStringForID(frame_data_it->FrameFunc)); + return true; +} + +static RegisterId GetBaseFrameRegister(PdbIndex &index, + PdbCompilandSymId frame_proc_id, + bool is_parameter) { + CVSymbol frame_proc_cvs = index.ReadSymbolRecord(frame_proc_id); + if (frame_proc_cvs.kind() != S_FRAMEPROC) + return RegisterId::NONE; + + FrameProcSym frame_proc(SymbolRecordKind::FrameProcSym); + cantFail(SymbolDeserializer::deserializeAs<FrameProcSym>(frame_proc_cvs, + frame_proc)); + + CPUType cpu_type = index.compilands() + .GetCompiland(frame_proc_id.modi) + ->m_compile_opts->Machine; + + return is_parameter ? frame_proc.getParamFramePtrReg(cpu_type) + : frame_proc.getLocalFramePtrReg(cpu_type); +} + +VariableInfo lldb_private::npdb::GetVariableLocationInfo( + PdbIndex &index, PdbCompilandSymId var_id, Block &func_block, + lldb::ModuleSP module) { + + CVSymbol sym = index.ReadSymbolRecord(var_id); + + VariableInfo result = GetVariableNameInfo(sym); + + if (sym.kind() == S_REGREL32) { + RegRelativeSym reg(SymbolRecordKind::RegRelativeSym); + cantFail(SymbolDeserializer::deserializeAs<RegRelativeSym>(sym, reg)); + result.location = DWARFExpressionList( + module, MakeRegRelLocationExpression(reg.Register, reg.Offset, module), + nullptr); + return result; + } + + if (sym.kind() == S_REGISTER) { + RegisterSym reg(SymbolRecordKind::RegisterSym); + cantFail(SymbolDeserializer::deserializeAs<RegisterSym>(sym, reg)); + result.location = DWARFExpressionList( + module, MakeEnregisteredLocationExpression(reg.Register, module), + nullptr); + return result; + } + + if (sym.kind() == S_LOCAL) { + LocalSym local(SymbolRecordKind::LocalSym); + if (llvm::Error error = + SymbolDeserializer::deserializeAs<LocalSym>(sym, local)) { + llvm::consumeError(std::move(error)); + return result; + } + + PdbCompilandSymId loc_specifier_id(var_id.modi, + var_id.offset + sym.RecordData.size()); + CVSymbol loc_specifier_cvs; + // Only used for S_DEFRANGE_FRAMEPOINTER_REL. + RegisterId base_reg = RegisterId::NONE; + size_t type_size = GetSizeOfType(result.type, index.tpi()); + // A map from offset of a field in parent to size of the field. + std::map<uint64_t, size_t> offset_to_size; + + // When overlaps happens, always prefer the one that doesn't split the value + // into multiple locations and the location parsed first is perfered. + RangeMap location_map; + + // Iterate through all location records after S_LOCAL. They describe the + // value of this variable at different locations. + bool finished = false; + while (!finished) { + loc_specifier_cvs = index.ReadSymbolRecord(loc_specifier_id); + switch (loc_specifier_cvs.kind()) { + case S_DEFRANGE_FRAMEPOINTER_REL: { + DefRangeFramePointerRelSym loc( + SymbolRecordKind::DefRangeFramePointerRelSym); + if (llvm::Error error = + SymbolDeserializer::deserializeAs<DefRangeFramePointerRelSym>( + loc_specifier_cvs, loc)) { + llvm::consumeError(std::move(error)); + return result; + } + Variable::RangeList raw_ranges = + MakeRangeList(index, loc.Range, loc.Gaps); + if (base_reg == RegisterId::NONE) { + PdbCompilandSymId func_scope_id = + PdbSymUid(func_block.GetID()).asCompilandSym(); + CVSymbol func_block_cvs = index.ReadSymbolRecord(func_scope_id); + lldbassert(func_block_cvs.kind() == S_GPROC32 || + func_block_cvs.kind() == S_LPROC32); + PdbCompilandSymId frame_proc_id(func_scope_id.modi, + func_scope_id.offset + + func_block_cvs.length()); + base_reg = + GetBaseFrameRegister(index, frame_proc_id, result.is_param); + if (base_reg == RegisterId::NONE) + break; + } + DWARFExpression expr; + if (base_reg == RegisterId::VFRAME) { + llvm::StringRef program; + if (GetFrameDataProgram(index, raw_ranges, program)) + expr = MakeVFrameRelLocationExpression(program, loc.Hdr.Offset, + module); + else { + // invalid variable + } + } else + expr = MakeRegRelLocationExpression(base_reg, loc.Hdr.Offset, module); + AddDwarfRange(location_map, expr, raw_ranges); + break; + } + case S_DEFRANGE_REGISTER: { + DefRangeRegisterSym loc(SymbolRecordKind::DefRangeRegisterSym); + if (llvm::Error error = + SymbolDeserializer::deserializeAs<DefRangeRegisterSym>( + loc_specifier_cvs, loc)) { + llvm::consumeError(std::move(error)); + return result; + } + RegisterId reg_id = (RegisterId)(uint16_t)loc.Hdr.Register; + Variable::RangeList raw_ranges = + MakeRangeList(index, loc.Range, loc.Gaps); + DWARFExpression expr = + MakeEnregisteredLocationExpression(reg_id, module); + AddDwarfRange(location_map, expr, raw_ranges); + break; + } + case S_DEFRANGE_REGISTER_REL: { + DefRangeRegisterRelSym loc(SymbolRecordKind::DefRangeRegisterRelSym); + if (llvm::Error error = + SymbolDeserializer::deserializeAs<DefRangeRegisterRelSym>( + loc_specifier_cvs, loc)) { + llvm::consumeError(std::move(error)); + return result; + } + Variable::RangeList raw_ranges = + MakeRangeList(index, loc.Range, loc.Gaps); + RegisterId reg_id = (RegisterId)(uint16_t)loc.Hdr.Register; + DWARFExpression expr; + if (reg_id == RegisterId::VFRAME) { + llvm::StringRef program; + if (GetFrameDataProgram(index, raw_ranges, program)) + expr = MakeVFrameRelLocationExpression( + program, loc.Hdr.BasePointerOffset, module); + else { + // invalid variable + } + } else { + expr = MakeRegRelLocationExpression(reg_id, loc.Hdr.BasePointerOffset, + module); + } + // FIXME: If it's UDT, we need to know the size of the value in byte. + if (!loc.hasSpilledUDTMember()) + AddDwarfRange(location_map, expr, raw_ranges); + break; + } + case S_DEFRANGE_SUBFIELD_REGISTER: { + DefRangeSubfieldRegisterSym loc( + SymbolRecordKind::DefRangeSubfieldRegisterSym); + if (llvm::Error error = + SymbolDeserializer::deserializeAs<DefRangeSubfieldRegisterSym>( + loc_specifier_cvs, loc)) { + llvm::consumeError(std::move(error)); + return result; + } + + Variable::RangeList ranges = MakeRangeList(index, loc.Range, loc.Gaps); + uint32_t reg_size = + GetRegisterSize((RegisterId)(uint16_t)loc.Hdr.Register); + if (reg_size == 0) + break; + offset_to_size[loc.Hdr.OffsetInParent] = reg_size; + AddMemberLocationRanges(location_map, loc.Hdr.OffsetInParent, + {loc.Hdr.Register, 0, true}, ranges); + break; + } + // FIXME: Handle other kinds. LLVM only generates the 4 types of records + // above. MSVC generates other location types. + case S_DEFRANGE: + case S_DEFRANGE_SUBFIELD: + case S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE: + break; + default: + finished = true; + break; + } + loc_specifier_id = PdbCompilandSymId( + loc_specifier_id.modi, + loc_specifier_id.offset + loc_specifier_cvs.RecordData.size()); + } + for (const auto &entry : location_map) { + DWARFExpression dwarf_expr = + entry.data.is_dwarf ? entry.data.expr + : MakeEnregisteredLocationExpressionForComposite( + entry.data.offset_to_location, + offset_to_size, type_size, module); + + result.location.AddExpression(entry.GetRangeBase(), entry.GetRangeEnd(), + dwarf_expr); + } + return result; + } + llvm_unreachable("Symbol is not a local variable!"); + return result; +} + +lldb::BasicType +lldb_private::npdb::GetCompilerTypeForSimpleKind(SimpleTypeKind kind) { + switch (kind) { + case SimpleTypeKind::Boolean128: + case SimpleTypeKind::Boolean16: + case SimpleTypeKind::Boolean32: + case SimpleTypeKind::Boolean64: + case SimpleTypeKind::Boolean8: + return lldb::eBasicTypeBool; + case SimpleTypeKind::Byte: + case SimpleTypeKind::UnsignedCharacter: + return lldb::eBasicTypeUnsignedChar; + case SimpleTypeKind::NarrowCharacter: + return lldb::eBasicTypeChar; + case SimpleTypeKind::SignedCharacter: + case SimpleTypeKind::SByte: + return lldb::eBasicTypeSignedChar; + case SimpleTypeKind::Character16: + return lldb::eBasicTypeChar16; + case SimpleTypeKind::Character32: + return lldb::eBasicTypeChar32; + case SimpleTypeKind::Character8: + return lldb::eBasicTypeChar8; + case SimpleTypeKind::Complex80: + return lldb::eBasicTypeLongDoubleComplex; + case SimpleTypeKind::Complex64: + return lldb::eBasicTypeDoubleComplex; + case SimpleTypeKind::Complex32: + return lldb::eBasicTypeFloatComplex; + case SimpleTypeKind::Float128: + case SimpleTypeKind::Float80: + return lldb::eBasicTypeLongDouble; + case SimpleTypeKind::Float64: + return lldb::eBasicTypeDouble; + case SimpleTypeKind::Float32: + return lldb::eBasicTypeFloat; + case SimpleTypeKind::Float16: + return lldb::eBasicTypeHalf; + case SimpleTypeKind::Int128: + return lldb::eBasicTypeInt128; + case SimpleTypeKind::Int64: + case SimpleTypeKind::Int64Quad: + return lldb::eBasicTypeLongLong; + case SimpleTypeKind::Int32: + return lldb::eBasicTypeInt; + case SimpleTypeKind::Int16: + case SimpleTypeKind::Int16Short: + return lldb::eBasicTypeShort; + case SimpleTypeKind::UInt128: + return lldb::eBasicTypeUnsignedInt128; + case SimpleTypeKind::UInt64: + case SimpleTypeKind::UInt64Quad: + return lldb::eBasicTypeUnsignedLongLong; + case SimpleTypeKind::HResult: + case SimpleTypeKind::UInt32: + return lldb::eBasicTypeUnsignedInt; + case SimpleTypeKind::UInt16: + case SimpleTypeKind::UInt16Short: + return lldb::eBasicTypeUnsignedShort; + case SimpleTypeKind::Int32Long: + return lldb::eBasicTypeLong; + case SimpleTypeKind::UInt32Long: + return lldb::eBasicTypeUnsignedLong; + case SimpleTypeKind::Void: + return lldb::eBasicTypeVoid; + case SimpleTypeKind::WideCharacter: + return lldb::eBasicTypeWChar; + default: + return lldb::eBasicTypeInvalid; + } +} + +size_t lldb_private::npdb::GetTypeSizeForSimpleKind(SimpleTypeKind kind) { + switch (kind) { + case SimpleTypeKind::Boolean128: + case SimpleTypeKind::Int128: + case SimpleTypeKind::UInt128: + case SimpleTypeKind::Float128: + return 16; + case SimpleTypeKind::Complex80: + case SimpleTypeKind::Float80: + return 10; + case SimpleTypeKind::Boolean64: + case SimpleTypeKind::Complex64: + case SimpleTypeKind::UInt64: + case SimpleTypeKind::UInt64Quad: + case SimpleTypeKind::Float64: + case SimpleTypeKind::Int64: + case SimpleTypeKind::Int64Quad: + return 8; + case SimpleTypeKind::Boolean32: + case SimpleTypeKind::Character32: + case SimpleTypeKind::Complex32: + case SimpleTypeKind::Float32: + case SimpleTypeKind::Int32: + case SimpleTypeKind::Int32Long: + case SimpleTypeKind::UInt32Long: + case SimpleTypeKind::HResult: + case SimpleTypeKind::UInt32: + return 4; + case SimpleTypeKind::Boolean16: + case SimpleTypeKind::Character16: + case SimpleTypeKind::Float16: + case SimpleTypeKind::Int16: + case SimpleTypeKind::Int16Short: + case SimpleTypeKind::UInt16: + case SimpleTypeKind::UInt16Short: + case SimpleTypeKind::WideCharacter: + return 2; + case SimpleTypeKind::Boolean8: + case SimpleTypeKind::Byte: + case SimpleTypeKind::UnsignedCharacter: + case SimpleTypeKind::NarrowCharacter: + case SimpleTypeKind::SignedCharacter: + case SimpleTypeKind::SByte: + case SimpleTypeKind::Character8: + return 1; + case SimpleTypeKind::Void: + default: + return 0; + } +} + +PdbTypeSymId lldb_private::npdb::GetBestPossibleDecl(PdbTypeSymId id, + TpiStream &tpi) { + if (id.index.isSimple()) + return id; + + CVType cvt = tpi.getType(id.index); + + // Only tag records have a best and a worst record. + if (!IsTagRecord(cvt)) + return id; + + // Tag records that are not forward decls are full decls, hence they are the + // best. + if (!IsForwardRefUdt(cvt)) + return id; + + return llvm::cantFail(tpi.findFullDeclForForwardRef(id.index)); +} + +template <typename RecordType> static size_t GetSizeOfTypeInternal(CVType cvt) { + RecordType record; + llvm::cantFail(TypeDeserializer::deserializeAs<RecordType>(cvt, record)); + return record.getSize(); +} + +size_t lldb_private::npdb::GetSizeOfType(PdbTypeSymId id, + llvm::pdb::TpiStream &tpi) { + if (id.index.isSimple()) { + switch (id.index.getSimpleMode()) { + case SimpleTypeMode::Direct: + return GetTypeSizeForSimpleKind(id.index.getSimpleKind()); + case SimpleTypeMode::NearPointer32: + case SimpleTypeMode::FarPointer32: + return 4; + case SimpleTypeMode::NearPointer64: + return 8; + case SimpleTypeMode::NearPointer128: + return 16; + default: + break; + } + return 0; + } + + TypeIndex index = id.index; + if (IsForwardRefUdt(index, tpi)) + index = llvm::cantFail(tpi.findFullDeclForForwardRef(index)); + + CVType cvt = tpi.getType(index); + switch (cvt.kind()) { + case LF_MODIFIER: + return GetSizeOfType({LookThroughModifierRecord(cvt)}, tpi); + case LF_ENUM: { + EnumRecord record; + llvm::cantFail(TypeDeserializer::deserializeAs<EnumRecord>(cvt, record)); + return GetSizeOfType({record.UnderlyingType}, tpi); + } + case LF_POINTER: + return GetSizeOfTypeInternal<PointerRecord>(cvt); + case LF_ARRAY: + return GetSizeOfTypeInternal<ArrayRecord>(cvt); + case LF_CLASS: + case LF_STRUCTURE: + case LF_INTERFACE: + return GetSizeOfTypeInternal<ClassRecord>(cvt); + case LF_UNION: + return GetSizeOfTypeInternal<UnionRecord>(cvt); + case LF_BITFIELD: { + BitFieldRecord record; + llvm::cantFail(TypeDeserializer::deserializeAs<BitFieldRecord>(cvt, record)); + return GetSizeOfType({record.Type}, tpi); + } + default: + break; + } + return 0; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.h new file mode 100644 index 000000000000..1f888f4de1fe --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.h @@ -0,0 +1,157 @@ +//===-- PdbUtil.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_SOURCE_PLUGINS_SYMBOLFILE_NATIVEPDB_PDBUTIL_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_NATIVEPDB_PDBUTIL_H + +#include "lldb/Expression/DWARFExpression.h" +#include "lldb/Symbol/Variable.h" +#include "lldb/lldb-enumerations.h" + +#include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/DebugInfo/CodeView/SymbolRecord.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/DebugInfo/PDB/PDBTypes.h" + +#include "PdbSymUid.h" + +#include <tuple> +#include <utility> + +namespace llvm { +namespace pdb { +class TpiStream; +} +} // namespace llvm + +namespace lldb_private { +namespace npdb { + +class PdbIndex; + +struct CVTagRecord { + enum Kind { Class, Struct, Union, Enum }; + + static CVTagRecord create(llvm::codeview::CVType type); + + Kind kind() const { return m_kind; } + + const llvm::codeview::TagRecord &asTag() const { + if (m_kind == Struct || m_kind == Class) + return cvclass; + if (m_kind == Enum) + return cvenum; + return cvunion; + } + + const llvm::codeview::ClassRecord &asClass() const { + assert(m_kind == Struct || m_kind == Class); + return cvclass; + } + + const llvm::codeview::EnumRecord &asEnum() const { + assert(m_kind == Enum); + return cvenum; + } + + const llvm::codeview::UnionRecord &asUnion() const { + assert(m_kind == Union); + return cvunion; + } + + llvm::StringRef name() const { + if (m_kind == Struct || m_kind == Union) + return cvclass.Name; + if (m_kind == Enum) + return cvenum.Name; + return cvunion.Name; + } + +private: + CVTagRecord(llvm::codeview::ClassRecord &&c); + CVTagRecord(llvm::codeview::UnionRecord &&u); + CVTagRecord(llvm::codeview::EnumRecord &&e); + union { + llvm::codeview::ClassRecord cvclass; + llvm::codeview::EnumRecord cvenum; + llvm::codeview::UnionRecord cvunion; + }; + Kind m_kind; +}; + +struct SegmentOffset { + SegmentOffset() = default; + SegmentOffset(uint16_t s, uint32_t o) : segment(s), offset(o) {} + uint16_t segment = 0; + uint32_t offset = 0; +}; + +struct SegmentOffsetLength { + SegmentOffsetLength() = default; + SegmentOffsetLength(uint16_t s, uint32_t o, uint32_t l) + : so(s, o), length(l) {} + SegmentOffset so; + uint32_t length = 0; +}; + +struct VariableInfo { + llvm::StringRef name; + llvm::codeview::TypeIndex type; + DWARFExpressionList location; + bool is_param; +}; + +llvm::pdb::PDB_SymType CVSymToPDBSym(llvm::codeview::SymbolKind kind); +llvm::pdb::PDB_SymType CVTypeToPDBType(llvm::codeview::TypeLeafKind kind); + +bool SymbolHasAddress(const llvm::codeview::CVSymbol &sym); +bool SymbolIsCode(const llvm::codeview::CVSymbol &sym); + +SegmentOffset GetSegmentAndOffset(const llvm::codeview::CVSymbol &sym); +SegmentOffsetLength +GetSegmentOffsetAndLength(const llvm::codeview::CVSymbol &sym); + +template <typename RecordT> bool IsValidRecord(const RecordT &sym) { + return true; +} + +inline bool IsValidRecord(const llvm::codeview::ProcRefSym &sym) { + // S_PROCREF symbols have 1-based module indices. + return sym.Module > 0; +} + +bool IsForwardRefUdt(llvm::codeview::CVType cvt); +bool IsTagRecord(llvm::codeview::CVType cvt); +bool IsClassStructUnion(llvm::codeview::CVType cvt); + +bool IsForwardRefUdt(const PdbTypeSymId &id, llvm::pdb::TpiStream &tpi); +bool IsTagRecord(const PdbTypeSymId &id, llvm::pdb::TpiStream &tpi); + +lldb::AccessType TranslateMemberAccess(llvm::codeview::MemberAccess access); +llvm::codeview::TypeIndex GetFieldListIndex(llvm::codeview::CVType cvt); +llvm::codeview::TypeIndex +LookThroughModifierRecord(llvm::codeview::CVType modifier); + +llvm::StringRef DropNameScope(llvm::StringRef name); + +VariableInfo GetVariableNameInfo(llvm::codeview::CVSymbol symbol); +VariableInfo GetVariableLocationInfo(PdbIndex &index, PdbCompilandSymId var_id, + Block &func_block, lldb::ModuleSP module); + +size_t GetTypeSizeForSimpleKind(llvm::codeview::SimpleTypeKind kind); +lldb::BasicType +GetCompilerTypeForSimpleKind(llvm::codeview::SimpleTypeKind kind); + +PdbTypeSymId GetBestPossibleDecl(PdbTypeSymId id, llvm::pdb::TpiStream &tpi); + +size_t GetSizeOfType(PdbTypeSymId id, llvm::pdb::TpiStream &tpi); + +} // namespace npdb +} // namespace lldb_private + +#endif diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp new file mode 100644 index 000000000000..7fded6a31a3a --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp @@ -0,0 +1,2336 @@ +//===-- SymbolFileNativePDB.cpp -------------------------------------------===// +// +// 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 "SymbolFileNativePDB.h" + +#include "Plugins/ExpressionParser/Clang/ClangUtil.h" +#include "Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.h" +#include "Plugins/ObjectFile/PDB/ObjectFilePDB.h" +#include "Plugins/TypeSystem/Clang/TypeSystemClang.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/LineTable.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/SymbolVendor.h" +#include "lldb/Symbol/Variable.h" +#include "lldb/Symbol/VariableList.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" + +#include "llvm/DebugInfo/CodeView/CVRecord.h" +#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" +#include "llvm/DebugInfo/CodeView/DebugLinesSubsection.h" +#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" +#include "llvm/DebugInfo/CodeView/RecordName.h" +#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h" +#include "llvm/DebugInfo/CodeView/SymbolRecordHelpers.h" +#include "llvm/DebugInfo/CodeView/TypeDeserializer.h" +#include "llvm/DebugInfo/PDB/Native/DbiStream.h" +#include "llvm/DebugInfo/PDB/Native/GlobalsStream.h" +#include "llvm/DebugInfo/PDB/Native/InfoStream.h" +#include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h" +#include "llvm/DebugInfo/PDB/Native/NativeSession.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/SymbolStream.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" +#include "llvm/DebugInfo/PDB/PDB.h" +#include "llvm/DebugInfo/PDB/PDBTypes.h" +#include "llvm/Demangle/MicrosoftDemangle.h" +#include "llvm/Object/COFF.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/MemoryBuffer.h" + +#include "DWARFLocationExpression.h" +#include "PdbSymUid.h" +#include "PdbUtil.h" +#include "UdtRecordCompleter.h" +#include <optional> +#include <string_view> + +using namespace lldb; +using namespace lldb_private; +using namespace npdb; +using namespace llvm::codeview; +using namespace llvm::pdb; + +char SymbolFileNativePDB::ID; + +static lldb::LanguageType TranslateLanguage(PDB_Lang lang) { + switch (lang) { + case PDB_Lang::Cpp: + return lldb::LanguageType::eLanguageTypeC_plus_plus; + case PDB_Lang::C: + return lldb::LanguageType::eLanguageTypeC; + case PDB_Lang::Swift: + return lldb::LanguageType::eLanguageTypeSwift; + case PDB_Lang::Rust: + return lldb::LanguageType::eLanguageTypeRust; + case PDB_Lang::ObjC: + return lldb::LanguageType::eLanguageTypeObjC; + case PDB_Lang::ObjCpp: + return lldb::LanguageType::eLanguageTypeObjC_plus_plus; + default: + return lldb::LanguageType::eLanguageTypeUnknown; + } +} + +static std::unique_ptr<PDBFile> +loadMatchingPDBFile(std::string exe_path, llvm::BumpPtrAllocator &allocator) { + // Try to find a matching PDB for an EXE. + using namespace llvm::object; + auto expected_binary = createBinary(exe_path); + + // If the file isn't a PE/COFF executable, fail. + if (!expected_binary) { + llvm::consumeError(expected_binary.takeError()); + return nullptr; + } + OwningBinary<Binary> binary = std::move(*expected_binary); + + // TODO: Avoid opening the PE/COFF binary twice by reading this information + // directly from the lldb_private::ObjectFile. + auto *obj = llvm::dyn_cast<llvm::object::COFFObjectFile>(binary.getBinary()); + if (!obj) + return nullptr; + const llvm::codeview::DebugInfo *pdb_info = nullptr; + + // If it doesn't have a debug directory, fail. + llvm::StringRef pdb_file; + if (llvm::Error e = obj->getDebugPDBInfo(pdb_info, pdb_file)) { + consumeError(std::move(e)); + return nullptr; + } + + // If the file doesn't exist, perhaps the path specified at build time + // doesn't match the PDB's current location, so check the location of the + // executable. + if (!FileSystem::Instance().Exists(pdb_file)) { + const auto exe_dir = FileSpec(exe_path).CopyByRemovingLastPathComponent(); + const auto pdb_name = FileSpec(pdb_file).GetFilename().GetCString(); + pdb_file = exe_dir.CopyByAppendingPathComponent(pdb_name).GetPathAsConstString().GetStringRef(); + } + + // If the file is not a PDB or if it doesn't have a matching GUID, fail. + auto pdb = ObjectFilePDB::loadPDBFile(std::string(pdb_file), allocator); + if (!pdb) + return nullptr; + + auto expected_info = pdb->getPDBInfoStream(); + if (!expected_info) { + llvm::consumeError(expected_info.takeError()); + return nullptr; + } + llvm::codeview::GUID guid; + memcpy(&guid, pdb_info->PDB70.Signature, 16); + + if (expected_info->getGuid() != guid) + return nullptr; + return pdb; +} + +static bool IsFunctionPrologue(const CompilandIndexItem &cci, + lldb::addr_t addr) { + // FIXME: Implement this. + return false; +} + +static bool IsFunctionEpilogue(const CompilandIndexItem &cci, + lldb::addr_t addr) { + // FIXME: Implement this. + return false; +} + +static llvm::StringRef GetSimpleTypeName(SimpleTypeKind kind) { + switch (kind) { + case SimpleTypeKind::Boolean128: + case SimpleTypeKind::Boolean16: + case SimpleTypeKind::Boolean32: + case SimpleTypeKind::Boolean64: + case SimpleTypeKind::Boolean8: + return "bool"; + case SimpleTypeKind::Byte: + case SimpleTypeKind::UnsignedCharacter: + return "unsigned char"; + case SimpleTypeKind::NarrowCharacter: + return "char"; + case SimpleTypeKind::SignedCharacter: + case SimpleTypeKind::SByte: + return "signed char"; + case SimpleTypeKind::Character16: + return "char16_t"; + case SimpleTypeKind::Character32: + return "char32_t"; + case SimpleTypeKind::Character8: + return "char8_t"; + case SimpleTypeKind::Complex80: + case SimpleTypeKind::Complex64: + case SimpleTypeKind::Complex32: + return "complex"; + case SimpleTypeKind::Float128: + case SimpleTypeKind::Float80: + return "long double"; + case SimpleTypeKind::Float64: + return "double"; + case SimpleTypeKind::Float32: + return "float"; + case SimpleTypeKind::Float16: + return "single"; + case SimpleTypeKind::Int128: + return "__int128"; + case SimpleTypeKind::Int64: + case SimpleTypeKind::Int64Quad: + return "int64_t"; + case SimpleTypeKind::Int32: + return "int"; + case SimpleTypeKind::Int16: + return "short"; + case SimpleTypeKind::UInt128: + return "unsigned __int128"; + case SimpleTypeKind::UInt64: + case SimpleTypeKind::UInt64Quad: + return "uint64_t"; + case SimpleTypeKind::HResult: + return "HRESULT"; + case SimpleTypeKind::UInt32: + return "unsigned"; + case SimpleTypeKind::UInt16: + case SimpleTypeKind::UInt16Short: + return "unsigned short"; + case SimpleTypeKind::Int32Long: + return "long"; + case SimpleTypeKind::UInt32Long: + return "unsigned long"; + case SimpleTypeKind::Void: + return "void"; + case SimpleTypeKind::WideCharacter: + return "wchar_t"; + default: + return ""; + } +} + +static bool IsClassRecord(TypeLeafKind kind) { + switch (kind) { + case LF_STRUCTURE: + case LF_CLASS: + case LF_INTERFACE: + return true; + default: + return false; + } +} + +static std::optional<CVTagRecord> +GetNestedTagDefinition(const NestedTypeRecord &Record, + const CVTagRecord &parent, TpiStream &tpi) { + // An LF_NESTTYPE is essentially a nested typedef / using declaration, but it + // is also used to indicate the primary definition of a nested class. That is + // to say, if you have: + // struct A { + // struct B {}; + // using C = B; + // }; + // Then in the debug info, this will appear as: + // LF_STRUCTURE `A::B` [type index = N] + // LF_STRUCTURE `A` + // LF_NESTTYPE [name = `B`, index = N] + // LF_NESTTYPE [name = `C`, index = N] + // In order to accurately reconstruct the decl context hierarchy, we need to + // know which ones are actual definitions and which ones are just aliases. + + // If it's a simple type, then this is something like `using foo = int`. + if (Record.Type.isSimple()) + return std::nullopt; + + CVType cvt = tpi.getType(Record.Type); + + if (!IsTagRecord(cvt)) + return std::nullopt; + + // If it's an inner definition, then treat whatever name we have here as a + // single component of a mangled name. So we can inject it into the parent's + // mangled name to see if it matches. + CVTagRecord child = CVTagRecord::create(cvt); + std::string qname = std::string(parent.asTag().getUniqueName()); + if (qname.size() < 4 || child.asTag().getUniqueName().size() < 4) + return std::nullopt; + + // qname[3] is the tag type identifier (struct, class, union, etc). Since the + // inner tag type is not necessarily the same as the outer tag type, re-write + // it to match the inner tag type. + qname[3] = child.asTag().getUniqueName()[3]; + std::string piece; + if (qname[3] == 'W') + piece = "4"; + piece += Record.Name; + piece.push_back('@'); + qname.insert(4, std::move(piece)); + if (qname != child.asTag().UniqueName) + return std::nullopt; + + return std::move(child); +} + +void SymbolFileNativePDB::Initialize() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), CreateInstance, + DebuggerInitialize); +} + +void SymbolFileNativePDB::Terminate() { + PluginManager::UnregisterPlugin(CreateInstance); +} + +void SymbolFileNativePDB::DebuggerInitialize(Debugger &debugger) {} + +llvm::StringRef SymbolFileNativePDB::GetPluginDescriptionStatic() { + return "Microsoft PDB debug symbol cross-platform file reader."; +} + +SymbolFile *SymbolFileNativePDB::CreateInstance(ObjectFileSP objfile_sp) { + return new SymbolFileNativePDB(std::move(objfile_sp)); +} + +SymbolFileNativePDB::SymbolFileNativePDB(ObjectFileSP objfile_sp) + : SymbolFileCommon(std::move(objfile_sp)) {} + +SymbolFileNativePDB::~SymbolFileNativePDB() = default; + +uint32_t SymbolFileNativePDB::CalculateAbilities() { + uint32_t abilities = 0; + if (!m_objfile_sp) + return 0; + + if (!m_index) { + // Lazily load and match the PDB file, but only do this once. + PDBFile *pdb_file; + if (auto *pdb = llvm::dyn_cast<ObjectFilePDB>(m_objfile_sp.get())) { + pdb_file = &pdb->GetPDBFile(); + } else { + m_file_up = loadMatchingPDBFile(m_objfile_sp->GetFileSpec().GetPath(), + m_allocator); + pdb_file = m_file_up.get(); + } + + if (!pdb_file) + return 0; + + auto expected_index = PdbIndex::create(pdb_file); + if (!expected_index) { + llvm::consumeError(expected_index.takeError()); + return 0; + } + m_index = std::move(*expected_index); + } + if (!m_index) + return 0; + + // We don't especially have to be precise here. We only distinguish between + // stripped and not stripped. + abilities = kAllAbilities; + + if (m_index->dbi().isStripped()) + abilities &= ~(Blocks | LocalVariables); + return abilities; +} + +void SymbolFileNativePDB::InitializeObject() { + m_obj_load_address = m_objfile_sp->GetModule() + ->GetObjectFile() + ->GetBaseAddress() + .GetFileAddress(); + m_index->SetLoadAddress(m_obj_load_address); + m_index->ParseSectionContribs(); + + auto ts_or_err = m_objfile_sp->GetModule()->GetTypeSystemForLanguage( + lldb::eLanguageTypeC_plus_plus); + if (auto err = ts_or_err.takeError()) { + LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), std::move(err), + "Failed to initialize: {0}"); + } else { + if (auto ts = *ts_or_err) + ts->SetSymbolFile(this); + BuildParentMap(); + } +} + +uint32_t SymbolFileNativePDB::CalculateNumCompileUnits() { + const DbiModuleList &modules = m_index->dbi().modules(); + uint32_t count = modules.getModuleCount(); + if (count == 0) + return count; + + // The linker can inject an additional "dummy" compilation unit into the + // PDB. Ignore this special compile unit for our purposes, if it is there. + // It is always the last one. + DbiModuleDescriptor last = modules.getModuleDescriptor(count - 1); + if (last.getModuleName() == "* Linker *") + --count; + return count; +} + +Block &SymbolFileNativePDB::CreateBlock(PdbCompilandSymId block_id) { + CompilandIndexItem *cii = m_index->compilands().GetCompiland(block_id.modi); + CVSymbol sym = cii->m_debug_stream.readSymbolAtOffset(block_id.offset); + CompUnitSP comp_unit = GetOrCreateCompileUnit(*cii); + lldb::user_id_t opaque_block_uid = toOpaqueUid(block_id); + BlockSP child_block = std::make_shared<Block>(opaque_block_uid); + auto ts_or_err = GetTypeSystemForLanguage(comp_unit->GetLanguage()); + if (auto err = ts_or_err.takeError()) + return *child_block; + auto ts = *ts_or_err; + if (!ts) + return *child_block; + PdbAstBuilder* ast_builder = ts->GetNativePDBParser(); + + switch (sym.kind()) { + case S_GPROC32: + case S_LPROC32: { + // This is a function. It must be global. Creating the Function entry + // for it automatically creates a block for it. + FunctionSP func = GetOrCreateFunction(block_id, *comp_unit); + if (func) { + Block &block = func->GetBlock(false); + if (block.GetNumRanges() == 0) + block.AddRange(Block::Range(0, func->GetAddressRange().GetByteSize())); + return block; + } + break; + } + case S_BLOCK32: { + // This is a block. Its parent is either a function or another block. In + // either case, its parent can be viewed as a block (e.g. a function + // contains 1 big block. So just get the parent block and add this block + // to it. + BlockSym block(static_cast<SymbolRecordKind>(sym.kind())); + cantFail(SymbolDeserializer::deserializeAs<BlockSym>(sym, block)); + lldbassert(block.Parent != 0); + PdbCompilandSymId parent_id(block_id.modi, block.Parent); + Block &parent_block = GetOrCreateBlock(parent_id); + Function *func = parent_block.CalculateSymbolContextFunction(); + lldbassert(func); + lldb::addr_t block_base = + m_index->MakeVirtualAddress(block.Segment, block.CodeOffset); + lldb::addr_t func_base = + func->GetAddressRange().GetBaseAddress().GetFileAddress(); + if (block_base >= func_base) + child_block->AddRange(Block::Range(block_base - func_base, block.CodeSize)); + else { + GetObjectFile()->GetModule()->ReportError( + "S_BLOCK32 at modi: {0:d} offset: {1:d}: adding range " + "[{2:x16}-{3:x16}) 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_id.modi, block_id.offset, block_base, + block_base + block.CodeSize, func_base); + } + parent_block.AddChild(child_block); + ast_builder->GetOrCreateBlockDecl(block_id); + m_blocks.insert({opaque_block_uid, child_block}); + break; + } + case S_INLINESITE: { + // This ensures line table is parsed first so we have inline sites info. + comp_unit->GetLineTable(); + + std::shared_ptr<InlineSite> inline_site = m_inline_sites[opaque_block_uid]; + Block &parent_block = GetOrCreateBlock(inline_site->parent_id); + parent_block.AddChild(child_block); + ast_builder->GetOrCreateInlinedFunctionDecl(block_id); + // Copy ranges from InlineSite to Block. + for (size_t i = 0; i < inline_site->ranges.GetSize(); ++i) { + auto *entry = inline_site->ranges.GetEntryAtIndex(i); + child_block->AddRange( + Block::Range(entry->GetRangeBase(), entry->GetByteSize())); + } + child_block->FinalizeRanges(); + + // Get the inlined function callsite info. + Declaration &decl = inline_site->inline_function_info->GetDeclaration(); + Declaration &callsite = inline_site->inline_function_info->GetCallSite(); + child_block->SetInlinedFunctionInfo( + inline_site->inline_function_info->GetName().GetCString(), nullptr, + &decl, &callsite); + m_blocks.insert({opaque_block_uid, child_block}); + break; + } + default: + lldbassert(false && "Symbol is not a block!"); + } + + return *child_block; +} + +lldb::FunctionSP SymbolFileNativePDB::CreateFunction(PdbCompilandSymId func_id, + CompileUnit &comp_unit) { + const CompilandIndexItem *cci = + m_index->compilands().GetCompiland(func_id.modi); + lldbassert(cci); + CVSymbol sym_record = cci->m_debug_stream.readSymbolAtOffset(func_id.offset); + + lldbassert(sym_record.kind() == S_LPROC32 || sym_record.kind() == S_GPROC32); + SegmentOffsetLength sol = GetSegmentOffsetAndLength(sym_record); + + auto file_vm_addr = + m_index->MakeVirtualAddress(sol.so.segment, sol.so.offset); + if (file_vm_addr == LLDB_INVALID_ADDRESS || file_vm_addr == 0) + return nullptr; + + AddressRange func_range(file_vm_addr, sol.length, + comp_unit.GetModule()->GetSectionList()); + if (!func_range.GetBaseAddress().IsValid()) + return nullptr; + + ProcSym proc(static_cast<SymbolRecordKind>(sym_record.kind())); + cantFail(SymbolDeserializer::deserializeAs<ProcSym>(sym_record, proc)); + if (proc.FunctionType == TypeIndex::None()) + return nullptr; + TypeSP func_type = GetOrCreateType(proc.FunctionType); + if (!func_type) + return nullptr; + + PdbTypeSymId sig_id(proc.FunctionType, false); + Mangled mangled(proc.Name); + FunctionSP func_sp = std::make_shared<Function>( + &comp_unit, toOpaqueUid(func_id), toOpaqueUid(sig_id), mangled, + func_type.get(), func_range); + + comp_unit.AddFunction(func_sp); + + auto ts_or_err = GetTypeSystemForLanguage(comp_unit.GetLanguage()); + if (auto err = ts_or_err.takeError()) + return func_sp; + auto ts = *ts_or_err; + if (!ts) + return func_sp; + ts->GetNativePDBParser()->GetOrCreateFunctionDecl(func_id); + + return func_sp; +} + +CompUnitSP +SymbolFileNativePDB::CreateCompileUnit(const CompilandIndexItem &cci) { + lldb::LanguageType lang = + cci.m_compile_opts ? TranslateLanguage(cci.m_compile_opts->getLanguage()) + : lldb::eLanguageTypeUnknown; + + LazyBool optimized = eLazyBoolNo; + if (cci.m_compile_opts && cci.m_compile_opts->hasOptimizations()) + optimized = eLazyBoolYes; + + llvm::SmallString<64> source_file_name = + m_index->compilands().GetMainSourceFile(cci); + FileSpec fs(llvm::sys::path::convert_to_slash( + source_file_name, llvm::sys::path::Style::windows_backslash)); + + CompUnitSP cu_sp = std::make_shared<CompileUnit>( + m_objfile_sp->GetModule(), nullptr, std::make_shared<SupportFile>(fs), + toOpaqueUid(cci.m_id), lang, optimized); + + SetCompileUnitAtIndex(cci.m_id.modi, cu_sp); + return cu_sp; +} + +lldb::TypeSP SymbolFileNativePDB::CreateModifierType(PdbTypeSymId type_id, + const ModifierRecord &mr, + CompilerType ct) { + TpiStream &stream = m_index->tpi(); + + std::string name; + if (mr.ModifiedType.isSimple()) + name = std::string(GetSimpleTypeName(mr.ModifiedType.getSimpleKind())); + else + name = computeTypeName(stream.typeCollection(), mr.ModifiedType); + Declaration decl; + lldb::TypeSP modified_type = GetOrCreateType(mr.ModifiedType); + + return MakeType(toOpaqueUid(type_id), ConstString(name), + modified_type->GetByteSize(nullptr), nullptr, + LLDB_INVALID_UID, Type::eEncodingIsUID, decl, ct, + Type::ResolveState::Full); +} + +lldb::TypeSP +SymbolFileNativePDB::CreatePointerType(PdbTypeSymId type_id, + const llvm::codeview::PointerRecord &pr, + CompilerType ct) { + TypeSP pointee = GetOrCreateType(pr.ReferentType); + if (!pointee) + return nullptr; + + if (pr.isPointerToMember()) { + MemberPointerInfo mpi = pr.getMemberInfo(); + GetOrCreateType(mpi.ContainingType); + } + + Declaration decl; + return MakeType(toOpaqueUid(type_id), ConstString(), pr.getSize(), nullptr, + LLDB_INVALID_UID, Type::eEncodingIsUID, decl, ct, + Type::ResolveState::Full); +} + +lldb::TypeSP SymbolFileNativePDB::CreateSimpleType(TypeIndex ti, + CompilerType ct) { + uint64_t uid = toOpaqueUid(PdbTypeSymId(ti, false)); + if (ti == TypeIndex::NullptrT()) { + Declaration decl; + return MakeType(uid, ConstString("std::nullptr_t"), 0, nullptr, + LLDB_INVALID_UID, Type::eEncodingIsUID, decl, ct, + Type::ResolveState::Full); + } + + if (ti.getSimpleMode() != SimpleTypeMode::Direct) { + TypeSP direct_sp = GetOrCreateType(ti.makeDirect()); + uint32_t pointer_size = 0; + switch (ti.getSimpleMode()) { + case SimpleTypeMode::FarPointer32: + case SimpleTypeMode::NearPointer32: + pointer_size = 4; + break; + case SimpleTypeMode::NearPointer64: + pointer_size = 8; + break; + default: + // 128-bit and 16-bit pointers unsupported. + return nullptr; + } + Declaration decl; + return MakeType(uid, ConstString(), pointer_size, nullptr, LLDB_INVALID_UID, + Type::eEncodingIsUID, decl, ct, Type::ResolveState::Full); + } + + if (ti.getSimpleKind() == SimpleTypeKind::NotTranslated) + return nullptr; + + size_t size = GetTypeSizeForSimpleKind(ti.getSimpleKind()); + llvm::StringRef type_name = GetSimpleTypeName(ti.getSimpleKind()); + + Declaration decl; + return MakeType(uid, ConstString(type_name), size, nullptr, LLDB_INVALID_UID, + Type::eEncodingIsUID, decl, ct, Type::ResolveState::Full); +} + +static std::string GetUnqualifiedTypeName(const TagRecord &record) { + if (!record.hasUniqueName()) { + MSVCUndecoratedNameParser parser(record.Name); + llvm::ArrayRef<MSVCUndecoratedNameSpecifier> specs = parser.GetSpecifiers(); + + return std::string(specs.back().GetBaseName()); + } + + llvm::ms_demangle::Demangler demangler; + std::string_view sv(record.UniqueName.begin(), record.UniqueName.size()); + llvm::ms_demangle::TagTypeNode *ttn = demangler.parseTagUniqueName(sv); + if (demangler.Error) + return std::string(record.Name); + + llvm::ms_demangle::IdentifierNode *idn = + ttn->QualifiedName->getUnqualifiedIdentifier(); + return idn->toString(); +} + +lldb::TypeSP +SymbolFileNativePDB::CreateClassStructUnion(PdbTypeSymId type_id, + const TagRecord &record, + size_t size, CompilerType ct) { + + std::string uname = GetUnqualifiedTypeName(record); + + // FIXME: Search IPI stream for LF_UDT_MOD_SRC_LINE. + Declaration decl; + return MakeType(toOpaqueUid(type_id), ConstString(uname), size, nullptr, + LLDB_INVALID_UID, Type::eEncodingIsUID, decl, ct, + Type::ResolveState::Forward); +} + +lldb::TypeSP SymbolFileNativePDB::CreateTagType(PdbTypeSymId type_id, + const ClassRecord &cr, + CompilerType ct) { + return CreateClassStructUnion(type_id, cr, cr.getSize(), ct); +} + +lldb::TypeSP SymbolFileNativePDB::CreateTagType(PdbTypeSymId type_id, + const UnionRecord &ur, + CompilerType ct) { + return CreateClassStructUnion(type_id, ur, ur.getSize(), ct); +} + +lldb::TypeSP SymbolFileNativePDB::CreateTagType(PdbTypeSymId type_id, + const EnumRecord &er, + CompilerType ct) { + std::string uname = GetUnqualifiedTypeName(er); + + Declaration decl; + TypeSP underlying_type = GetOrCreateType(er.UnderlyingType); + + return MakeType(toOpaqueUid(type_id), ConstString(uname), + underlying_type->GetByteSize(nullptr), nullptr, + LLDB_INVALID_UID, lldb_private::Type::eEncodingIsUID, decl, + ct, lldb_private::Type::ResolveState::Forward); +} + +TypeSP SymbolFileNativePDB::CreateArrayType(PdbTypeSymId type_id, + const ArrayRecord &ar, + CompilerType ct) { + TypeSP element_type = GetOrCreateType(ar.ElementType); + + Declaration decl; + TypeSP array_sp = + MakeType(toOpaqueUid(type_id), ConstString(), ar.Size, nullptr, + LLDB_INVALID_UID, lldb_private::Type::eEncodingIsUID, decl, ct, + lldb_private::Type::ResolveState::Full); + array_sp->SetEncodingType(element_type.get()); + return array_sp; +} + +TypeSP SymbolFileNativePDB::CreateFunctionType(PdbTypeSymId type_id, + const MemberFunctionRecord &mfr, + CompilerType ct) { + Declaration decl; + return MakeType(toOpaqueUid(type_id), ConstString(), 0, nullptr, + LLDB_INVALID_UID, lldb_private::Type::eEncodingIsUID, decl, + ct, lldb_private::Type::ResolveState::Full); +} + +TypeSP SymbolFileNativePDB::CreateProcedureType(PdbTypeSymId type_id, + const ProcedureRecord &pr, + CompilerType ct) { + Declaration decl; + return MakeType(toOpaqueUid(type_id), ConstString(), 0, nullptr, + LLDB_INVALID_UID, lldb_private::Type::eEncodingIsUID, decl, + ct, lldb_private::Type::ResolveState::Full); +} + +TypeSP SymbolFileNativePDB::CreateType(PdbTypeSymId type_id, CompilerType ct) { + if (type_id.index.isSimple()) + return CreateSimpleType(type_id.index, ct); + + TpiStream &stream = type_id.is_ipi ? m_index->ipi() : m_index->tpi(); + CVType cvt = stream.getType(type_id.index); + + if (cvt.kind() == LF_MODIFIER) { + ModifierRecord modifier; + llvm::cantFail( + TypeDeserializer::deserializeAs<ModifierRecord>(cvt, modifier)); + return CreateModifierType(type_id, modifier, ct); + } + + if (cvt.kind() == LF_POINTER) { + PointerRecord pointer; + llvm::cantFail( + TypeDeserializer::deserializeAs<PointerRecord>(cvt, pointer)); + return CreatePointerType(type_id, pointer, ct); + } + + if (IsClassRecord(cvt.kind())) { + ClassRecord cr; + llvm::cantFail(TypeDeserializer::deserializeAs<ClassRecord>(cvt, cr)); + return CreateTagType(type_id, cr, ct); + } + + if (cvt.kind() == LF_ENUM) { + EnumRecord er; + llvm::cantFail(TypeDeserializer::deserializeAs<EnumRecord>(cvt, er)); + return CreateTagType(type_id, er, ct); + } + + if (cvt.kind() == LF_UNION) { + UnionRecord ur; + llvm::cantFail(TypeDeserializer::deserializeAs<UnionRecord>(cvt, ur)); + return CreateTagType(type_id, ur, ct); + } + + if (cvt.kind() == LF_ARRAY) { + ArrayRecord ar; + llvm::cantFail(TypeDeserializer::deserializeAs<ArrayRecord>(cvt, ar)); + return CreateArrayType(type_id, ar, ct); + } + + if (cvt.kind() == LF_PROCEDURE) { + ProcedureRecord pr; + llvm::cantFail(TypeDeserializer::deserializeAs<ProcedureRecord>(cvt, pr)); + return CreateProcedureType(type_id, pr, ct); + } + if (cvt.kind() == LF_MFUNCTION) { + MemberFunctionRecord mfr; + llvm::cantFail(TypeDeserializer::deserializeAs<MemberFunctionRecord>(cvt, mfr)); + return CreateFunctionType(type_id, mfr, ct); + } + + return nullptr; +} + +TypeSP SymbolFileNativePDB::CreateAndCacheType(PdbTypeSymId type_id) { + // If they search for a UDT which is a forward ref, try and resolve the full + // decl and just map the forward ref uid to the full decl record. + std::optional<PdbTypeSymId> full_decl_uid; + if (IsForwardRefUdt(type_id, m_index->tpi())) { + auto expected_full_ti = + m_index->tpi().findFullDeclForForwardRef(type_id.index); + if (!expected_full_ti) + llvm::consumeError(expected_full_ti.takeError()); + else if (*expected_full_ti != type_id.index) { + full_decl_uid = PdbTypeSymId(*expected_full_ti, false); + + // It's possible that a lookup would occur for the full decl causing it + // to be cached, then a second lookup would occur for the forward decl. + // We don't want to create a second full decl, so make sure the full + // decl hasn't already been cached. + auto full_iter = m_types.find(toOpaqueUid(*full_decl_uid)); + if (full_iter != m_types.end()) { + TypeSP result = full_iter->second; + // Map the forward decl to the TypeSP for the full decl so we can take + // the fast path next time. + m_types[toOpaqueUid(type_id)] = result; + return result; + } + } + } + + PdbTypeSymId best_decl_id = full_decl_uid ? *full_decl_uid : type_id; + auto ts_or_err = GetTypeSystemForLanguage(lldb::eLanguageTypeC_plus_plus); + if (auto err = ts_or_err.takeError()) + return nullptr; + auto ts = *ts_or_err; + if (!ts) + return nullptr; + + PdbAstBuilder* ast_builder = ts->GetNativePDBParser(); + clang::QualType qt = ast_builder->GetOrCreateType(best_decl_id); + if (qt.isNull()) + return nullptr; + + TypeSP result = CreateType(best_decl_id, ast_builder->ToCompilerType(qt)); + if (!result) + return nullptr; + + uint64_t best_uid = toOpaqueUid(best_decl_id); + m_types[best_uid] = result; + // If we had both a forward decl and a full decl, make both point to the new + // type. + if (full_decl_uid) + m_types[toOpaqueUid(type_id)] = result; + + return result; +} + +TypeSP SymbolFileNativePDB::GetOrCreateType(PdbTypeSymId type_id) { + // We can't use try_emplace / overwrite here because the process of creating + // a type could create nested types, which could invalidate iterators. So + // we have to do a 2-phase lookup / insert. + auto iter = m_types.find(toOpaqueUid(type_id)); + if (iter != m_types.end()) + return iter->second; + + TypeSP type = CreateAndCacheType(type_id); + if (type) + GetTypeList().Insert(type); + return type; +} + +VariableSP SymbolFileNativePDB::CreateGlobalVariable(PdbGlobalSymId var_id) { + CVSymbol sym = m_index->symrecords().readRecord(var_id.offset); + if (sym.kind() == S_CONSTANT) + return CreateConstantSymbol(var_id, sym); + + lldb::ValueType scope = eValueTypeInvalid; + TypeIndex ti; + llvm::StringRef name; + lldb::addr_t addr = 0; + uint16_t section = 0; + uint32_t offset = 0; + bool is_external = false; + switch (sym.kind()) { + case S_GDATA32: + is_external = true; + [[fallthrough]]; + case S_LDATA32: { + DataSym ds(sym.kind()); + llvm::cantFail(SymbolDeserializer::deserializeAs<DataSym>(sym, ds)); + ti = ds.Type; + scope = (sym.kind() == S_GDATA32) ? eValueTypeVariableGlobal + : eValueTypeVariableStatic; + name = ds.Name; + section = ds.Segment; + offset = ds.DataOffset; + addr = m_index->MakeVirtualAddress(ds.Segment, ds.DataOffset); + break; + } + case S_GTHREAD32: + is_external = true; + [[fallthrough]]; + case S_LTHREAD32: { + ThreadLocalDataSym tlds(sym.kind()); + llvm::cantFail( + SymbolDeserializer::deserializeAs<ThreadLocalDataSym>(sym, tlds)); + ti = tlds.Type; + name = tlds.Name; + section = tlds.Segment; + offset = tlds.DataOffset; + addr = m_index->MakeVirtualAddress(tlds.Segment, tlds.DataOffset); + scope = eValueTypeVariableThreadLocal; + break; + } + default: + llvm_unreachable("unreachable!"); + } + + CompUnitSP comp_unit; + std::optional<uint16_t> modi = m_index->GetModuleIndexForVa(addr); + if (!modi) { + return nullptr; + } + + CompilandIndexItem &cci = m_index->compilands().GetOrCreateCompiland(*modi); + comp_unit = GetOrCreateCompileUnit(cci); + + Declaration decl; + PdbTypeSymId tid(ti, false); + SymbolFileTypeSP type_sp = + std::make_shared<SymbolFileType>(*this, toOpaqueUid(tid)); + Variable::RangeList ranges; + auto ts_or_err = GetTypeSystemForLanguage(comp_unit->GetLanguage()); + if (auto err = ts_or_err.takeError()) + return nullptr; + auto ts = *ts_or_err; + if (!ts) + return nullptr; + + ts->GetNativePDBParser()->GetOrCreateVariableDecl(var_id); + + ModuleSP module_sp = GetObjectFile()->GetModule(); + DWARFExpressionList location( + module_sp, MakeGlobalLocationExpression(section, offset, module_sp), + nullptr); + + std::string global_name("::"); + global_name += name; + bool artificial = false; + bool location_is_constant_data = false; + bool static_member = false; + VariableSP var_sp = std::make_shared<Variable>( + toOpaqueUid(var_id), name.str().c_str(), global_name.c_str(), type_sp, + scope, comp_unit.get(), ranges, &decl, location, is_external, artificial, + location_is_constant_data, static_member); + + return var_sp; +} + +lldb::VariableSP +SymbolFileNativePDB::CreateConstantSymbol(PdbGlobalSymId var_id, + const CVSymbol &cvs) { + TpiStream &tpi = m_index->tpi(); + ConstantSym constant(cvs.kind()); + + llvm::cantFail(SymbolDeserializer::deserializeAs<ConstantSym>(cvs, constant)); + std::string global_name("::"); + global_name += constant.Name; + PdbTypeSymId tid(constant.Type, false); + SymbolFileTypeSP type_sp = + std::make_shared<SymbolFileType>(*this, toOpaqueUid(tid)); + + Declaration decl; + Variable::RangeList ranges; + ModuleSP module = GetObjectFile()->GetModule(); + DWARFExpressionList location(module, + MakeConstantLocationExpression( + constant.Type, tpi, constant.Value, module), + nullptr); + + bool external = false; + bool artificial = false; + bool location_is_constant_data = true; + bool static_member = false; + VariableSP var_sp = std::make_shared<Variable>( + toOpaqueUid(var_id), constant.Name.str().c_str(), global_name.c_str(), + type_sp, eValueTypeVariableGlobal, module.get(), ranges, &decl, location, + external, artificial, location_is_constant_data, static_member); + return var_sp; +} + +VariableSP +SymbolFileNativePDB::GetOrCreateGlobalVariable(PdbGlobalSymId var_id) { + auto emplace_result = m_global_vars.try_emplace(toOpaqueUid(var_id), nullptr); + if (emplace_result.second) { + if (VariableSP var_sp = CreateGlobalVariable(var_id)) + emplace_result.first->second = var_sp; + else + return nullptr; + } + + return emplace_result.first->second; +} + +lldb::TypeSP SymbolFileNativePDB::GetOrCreateType(TypeIndex ti) { + return GetOrCreateType(PdbTypeSymId(ti, false)); +} + +FunctionSP SymbolFileNativePDB::GetOrCreateFunction(PdbCompilandSymId func_id, + CompileUnit &comp_unit) { + auto emplace_result = m_functions.try_emplace(toOpaqueUid(func_id), nullptr); + if (emplace_result.second) + emplace_result.first->second = CreateFunction(func_id, comp_unit); + + return emplace_result.first->second; +} + +CompUnitSP +SymbolFileNativePDB::GetOrCreateCompileUnit(const CompilandIndexItem &cci) { + + auto emplace_result = + m_compilands.try_emplace(toOpaqueUid(cci.m_id), nullptr); + if (emplace_result.second) + emplace_result.first->second = CreateCompileUnit(cci); + + lldbassert(emplace_result.first->second); + return emplace_result.first->second; +} + +Block &SymbolFileNativePDB::GetOrCreateBlock(PdbCompilandSymId block_id) { + auto iter = m_blocks.find(toOpaqueUid(block_id)); + if (iter != m_blocks.end()) + return *iter->second; + + return CreateBlock(block_id); +} + +void SymbolFileNativePDB::ParseDeclsForContext( + lldb_private::CompilerDeclContext decl_ctx) { + TypeSystem* ts_or_err = decl_ctx.GetTypeSystem(); + if (!ts_or_err) + return; + PdbAstBuilder* ast_builder = ts_or_err->GetNativePDBParser(); + clang::DeclContext *context = ast_builder->FromCompilerDeclContext(decl_ctx); + if (!context) + return; + ast_builder->ParseDeclsForContext(*context); +} + +lldb::CompUnitSP SymbolFileNativePDB::ParseCompileUnitAtIndex(uint32_t index) { + if (index >= GetNumCompileUnits()) + return CompUnitSP(); + lldbassert(index < UINT16_MAX); + if (index >= UINT16_MAX) + return nullptr; + + CompilandIndexItem &item = m_index->compilands().GetOrCreateCompiland(index); + + return GetOrCreateCompileUnit(item); +} + +lldb::LanguageType SymbolFileNativePDB::ParseLanguage(CompileUnit &comp_unit) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + PdbSymUid uid(comp_unit.GetID()); + lldbassert(uid.kind() == PdbSymUidKind::Compiland); + + CompilandIndexItem *item = + m_index->compilands().GetCompiland(uid.asCompiland().modi); + lldbassert(item); + if (!item->m_compile_opts) + return lldb::eLanguageTypeUnknown; + + return TranslateLanguage(item->m_compile_opts->getLanguage()); +} + +void SymbolFileNativePDB::AddSymbols(Symtab &symtab) {} + +size_t SymbolFileNativePDB::ParseFunctions(CompileUnit &comp_unit) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + PdbSymUid uid{comp_unit.GetID()}; + lldbassert(uid.kind() == PdbSymUidKind::Compiland); + uint16_t modi = uid.asCompiland().modi; + CompilandIndexItem &cii = m_index->compilands().GetOrCreateCompiland(modi); + + size_t count = comp_unit.GetNumFunctions(); + const CVSymbolArray &syms = cii.m_debug_stream.getSymbolArray(); + for (auto iter = syms.begin(); iter != syms.end(); ++iter) { + if (iter->kind() != S_LPROC32 && iter->kind() != S_GPROC32) + continue; + + PdbCompilandSymId sym_id{modi, iter.offset()}; + + FunctionSP func = GetOrCreateFunction(sym_id, comp_unit); + } + + size_t new_count = comp_unit.GetNumFunctions(); + lldbassert(new_count >= count); + return new_count - count; +} + +static bool NeedsResolvedCompileUnit(uint32_t resolve_scope) { + // If any of these flags are set, we need to resolve the compile unit. + uint32_t flags = eSymbolContextCompUnit; + flags |= eSymbolContextVariable; + flags |= eSymbolContextFunction; + flags |= eSymbolContextBlock; + flags |= eSymbolContextLineEntry; + return (resolve_scope & flags) != 0; +} + +uint32_t SymbolFileNativePDB::ResolveSymbolContext( + const Address &addr, SymbolContextItem resolve_scope, SymbolContext &sc) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + uint32_t resolved_flags = 0; + lldb::addr_t file_addr = addr.GetFileAddress(); + + if (NeedsResolvedCompileUnit(resolve_scope)) { + std::optional<uint16_t> modi = m_index->GetModuleIndexForVa(file_addr); + if (!modi) + return 0; + CompUnitSP cu_sp = GetCompileUnitAtIndex(*modi); + if (!cu_sp) + return 0; + + sc.comp_unit = cu_sp.get(); + resolved_flags |= eSymbolContextCompUnit; + } + + if (resolve_scope & eSymbolContextFunction || + resolve_scope & eSymbolContextBlock) { + lldbassert(sc.comp_unit); + std::vector<SymbolAndUid> matches = m_index->FindSymbolsByVa(file_addr); + // Search the matches in reverse. This way if there are multiple matches + // (for example we are 3 levels deep in a nested scope) it will find the + // innermost one first. + for (const auto &match : llvm::reverse(matches)) { + if (match.uid.kind() != PdbSymUidKind::CompilandSym) + continue; + + PdbCompilandSymId csid = match.uid.asCompilandSym(); + CVSymbol cvs = m_index->ReadSymbolRecord(csid); + PDB_SymType type = CVSymToPDBSym(cvs.kind()); + if (type != PDB_SymType::Function && type != PDB_SymType::Block) + continue; + if (type == PDB_SymType::Function) { + sc.function = GetOrCreateFunction(csid, *sc.comp_unit).get(); + if (sc.function) { + Block &block = sc.function->GetBlock(true); + addr_t func_base = + sc.function->GetAddressRange().GetBaseAddress().GetFileAddress(); + addr_t offset = file_addr - func_base; + sc.block = block.FindInnermostBlockByOffset(offset); + } + } + + if (type == PDB_SymType::Block) { + Block &block = GetOrCreateBlock(csid); + sc.function = block.CalculateSymbolContextFunction(); + if (sc.function) { + sc.function->GetBlock(true); + addr_t func_base = + sc.function->GetAddressRange().GetBaseAddress().GetFileAddress(); + addr_t offset = file_addr - func_base; + sc.block = block.FindInnermostBlockByOffset(offset); + } + } + if (sc.function) + resolved_flags |= eSymbolContextFunction; + if (sc.block) + resolved_flags |= eSymbolContextBlock; + break; + } + } + + if (resolve_scope & eSymbolContextLineEntry) { + lldbassert(sc.comp_unit); + if (auto *line_table = sc.comp_unit->GetLineTable()) { + if (line_table->FindLineEntryByAddress(addr, sc.line_entry)) + resolved_flags |= eSymbolContextLineEntry; + } + } + + return resolved_flags; +} + +uint32_t SymbolFileNativePDB::ResolveSymbolContext( + const SourceLocationSpec &src_location_spec, + lldb::SymbolContextItem resolve_scope, SymbolContextList &sc_list) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + 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 *cu = ParseCompileUnitAtIndex(cu_idx).get(); + if (!cu) + continue; + + bool file_spec_matches_cu_file_spec = FileSpec::Match( + src_location_spec.GetFileSpec(), cu->GetPrimaryFile()); + if (file_spec_matches_cu_file_spec) { + cu->ResolveSymbolContext(src_location_spec, resolve_scope, sc_list); + break; + } + } + } + return sc_list.GetSize() - prev_size; +} + +bool SymbolFileNativePDB::ParseLineTable(CompileUnit &comp_unit) { + // Unfortunately LLDB is set up to parse the entire compile unit line table + // all at once, even if all it really needs is line info for a specific + // function. In the future it would be nice if it could set the sc.m_function + // member, and we could only get the line info for the function in question. + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + PdbSymUid cu_id(comp_unit.GetID()); + lldbassert(cu_id.kind() == PdbSymUidKind::Compiland); + uint16_t modi = cu_id.asCompiland().modi; + CompilandIndexItem *cii = m_index->compilands().GetCompiland(modi); + lldbassert(cii); + + // Parse DEBUG_S_LINES subsections first, then parse all S_INLINESITE records + // in this CU. Add line entries into the set first so that if there are line + // entries with same addres, the later is always more accurate than the + // former. + std::set<LineTable::Entry, LineTableEntryComparator> line_set; + + // This is basically a copy of the .debug$S subsections from all original COFF + // object files merged together with address relocations applied. We are + // looking for all DEBUG_S_LINES subsections. + for (const DebugSubsectionRecord &dssr : + cii->m_debug_stream.getSubsectionsArray()) { + if (dssr.kind() != DebugSubsectionKind::Lines) + continue; + + DebugLinesSubsectionRef lines; + llvm::BinaryStreamReader reader(dssr.getRecordData()); + if (auto EC = lines.initialize(reader)) { + llvm::consumeError(std::move(EC)); + return false; + } + + const LineFragmentHeader *lfh = lines.header(); + uint64_t virtual_addr = + m_index->MakeVirtualAddress(lfh->RelocSegment, lfh->RelocOffset); + if (virtual_addr == LLDB_INVALID_ADDRESS) + continue; + + for (const LineColumnEntry &group : lines) { + llvm::Expected<uint32_t> file_index_or_err = + GetFileIndex(*cii, group.NameIndex); + if (!file_index_or_err) + continue; + uint32_t file_index = file_index_or_err.get(); + lldbassert(!group.LineNumbers.empty()); + CompilandIndexItem::GlobalLineTable::Entry line_entry( + LLDB_INVALID_ADDRESS, 0); + for (const LineNumberEntry &entry : group.LineNumbers) { + LineInfo cur_info(entry.Flags); + + if (cur_info.isAlwaysStepInto() || cur_info.isNeverStepInto()) + continue; + + uint64_t addr = virtual_addr + entry.Offset; + + bool is_statement = cur_info.isStatement(); + bool is_prologue = IsFunctionPrologue(*cii, addr); + bool is_epilogue = IsFunctionEpilogue(*cii, addr); + + uint32_t lno = cur_info.getStartLine(); + + LineTable::Entry new_entry(addr, lno, 0, file_index, is_statement, false, + is_prologue, is_epilogue, false); + // Terminal entry has lower precedence than new entry. + auto iter = line_set.find(new_entry); + if (iter != line_set.end() && iter->is_terminal_entry) + line_set.erase(iter); + line_set.insert(new_entry); + + if (line_entry.GetRangeBase() != LLDB_INVALID_ADDRESS) { + line_entry.SetRangeEnd(addr); + cii->m_global_line_table.Append(line_entry); + } + line_entry.SetRangeBase(addr); + line_entry.data = {file_index, lno}; + } + LineInfo last_line(group.LineNumbers.back().Flags); + line_set.emplace(virtual_addr + lfh->CodeSize, last_line.getEndLine(), 0, + file_index, false, false, false, false, true); + + if (line_entry.GetRangeBase() != LLDB_INVALID_ADDRESS) { + line_entry.SetRangeEnd(virtual_addr + lfh->CodeSize); + cii->m_global_line_table.Append(line_entry); + } + } + } + + cii->m_global_line_table.Sort(); + + // Parse all S_INLINESITE in this CU. + const CVSymbolArray &syms = cii->m_debug_stream.getSymbolArray(); + for (auto iter = syms.begin(); iter != syms.end();) { + if (iter->kind() != S_LPROC32 && iter->kind() != S_GPROC32) { + ++iter; + continue; + } + + uint32_t record_offset = iter.offset(); + CVSymbol func_record = + cii->m_debug_stream.readSymbolAtOffset(record_offset); + SegmentOffsetLength sol = GetSegmentOffsetAndLength(func_record); + addr_t file_vm_addr = + m_index->MakeVirtualAddress(sol.so.segment, sol.so.offset); + if (file_vm_addr == LLDB_INVALID_ADDRESS) + continue; + + AddressRange func_range(file_vm_addr, sol.length, + comp_unit.GetModule()->GetSectionList()); + Address func_base = func_range.GetBaseAddress(); + PdbCompilandSymId func_id{modi, record_offset}; + + // Iterate all S_INLINESITEs in the function. + auto parse_inline_sites = [&](SymbolKind kind, PdbCompilandSymId id) { + if (kind != S_INLINESITE) + return false; + + ParseInlineSite(id, func_base); + + for (const auto &line_entry : + m_inline_sites[toOpaqueUid(id)]->line_entries) { + // If line_entry is not terminal entry, remove previous line entry at + // the same address and insert new one. Terminal entry inside an inline + // site might not be terminal entry for its parent. + if (!line_entry.is_terminal_entry) + line_set.erase(line_entry); + line_set.insert(line_entry); + } + // No longer useful after adding to line_set. + m_inline_sites[toOpaqueUid(id)]->line_entries.clear(); + return true; + }; + ParseSymbolArrayInScope(func_id, parse_inline_sites); + // Jump to the end of the function record. + iter = syms.at(getScopeEndOffset(func_record)); + } + + cii->m_global_line_table.Clear(); + + // Add line entries in line_set to line_table. + auto line_table = std::make_unique<LineTable>(&comp_unit); + std::unique_ptr<LineSequence> sequence( + line_table->CreateLineSequenceContainer()); + for (const auto &line_entry : line_set) { + line_table->AppendLineEntryToSequence( + sequence.get(), line_entry.file_addr, line_entry.line, + line_entry.column, line_entry.file_idx, + line_entry.is_start_of_statement, line_entry.is_start_of_basic_block, + line_entry.is_prologue_end, line_entry.is_epilogue_begin, + line_entry.is_terminal_entry); + } + line_table->InsertSequence(sequence.get()); + + if (line_table->GetSize() == 0) + return false; + + comp_unit.SetLineTable(line_table.release()); + return true; +} + +bool SymbolFileNativePDB::ParseDebugMacros(CompileUnit &comp_unit) { + // PDB doesn't contain information about macros + return false; +} + +llvm::Expected<uint32_t> +SymbolFileNativePDB::GetFileIndex(const CompilandIndexItem &cii, + uint32_t file_id) { + if (!cii.m_strings.hasChecksums() || !cii.m_strings.hasStrings()) + return llvm::make_error<RawError>(raw_error_code::no_entry); + + const auto &checksums = cii.m_strings.checksums().getArray(); + const auto &strings = cii.m_strings.strings(); + // Indices in this structure are actually offsets of records in the + // DEBUG_S_FILECHECKSUMS subsection. Those entries then have an index + // into the global PDB string table. + auto iter = checksums.at(file_id); + if (iter == checksums.end()) + return llvm::make_error<RawError>(raw_error_code::no_entry); + + llvm::Expected<llvm::StringRef> efn = strings.getString(iter->FileNameOffset); + if (!efn) { + return efn.takeError(); + } + + // LLDB wants the index of the file in the list of support files. + auto fn_iter = llvm::find(cii.m_file_list, *efn); + if (fn_iter != cii.m_file_list.end()) + return std::distance(cii.m_file_list.begin(), fn_iter); + return llvm::make_error<RawError>(raw_error_code::no_entry); +} + +bool SymbolFileNativePDB::ParseSupportFiles(CompileUnit &comp_unit, + SupportFileList &support_files) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + PdbSymUid cu_id(comp_unit.GetID()); + lldbassert(cu_id.kind() == PdbSymUidKind::Compiland); + CompilandIndexItem *cci = + m_index->compilands().GetCompiland(cu_id.asCompiland().modi); + lldbassert(cci); + + for (llvm::StringRef f : cci->m_file_list) { + FileSpec::Style style = + f.starts_with("/") ? FileSpec::Style::posix : FileSpec::Style::windows; + FileSpec spec(f, style); + support_files.Append(spec); + } + return true; +} + +bool SymbolFileNativePDB::ParseImportedModules( + const SymbolContext &sc, std::vector<SourceModule> &imported_modules) { + // PDB does not yet support module debug info + return false; +} + +void SymbolFileNativePDB::ParseInlineSite(PdbCompilandSymId id, + Address func_addr) { + lldb::user_id_t opaque_uid = toOpaqueUid(id); + if (m_inline_sites.contains(opaque_uid)) + return; + + addr_t func_base = func_addr.GetFileAddress(); + CompilandIndexItem *cii = m_index->compilands().GetCompiland(id.modi); + CVSymbol sym = cii->m_debug_stream.readSymbolAtOffset(id.offset); + CompUnitSP comp_unit = GetOrCreateCompileUnit(*cii); + + InlineSiteSym inline_site(static_cast<SymbolRecordKind>(sym.kind())); + cantFail(SymbolDeserializer::deserializeAs<InlineSiteSym>(sym, inline_site)); + PdbCompilandSymId parent_id(id.modi, inline_site.Parent); + + std::shared_ptr<InlineSite> inline_site_sp = + std::make_shared<InlineSite>(parent_id); + + // Get the inlined function declaration info. + auto iter = cii->m_inline_map.find(inline_site.Inlinee); + if (iter == cii->m_inline_map.end()) + return; + InlineeSourceLine inlinee_line = iter->second; + + const SupportFileList &files = comp_unit->GetSupportFiles(); + FileSpec decl_file; + llvm::Expected<uint32_t> file_index_or_err = + GetFileIndex(*cii, inlinee_line.Header->FileID); + if (!file_index_or_err) + return; + uint32_t file_offset = file_index_or_err.get(); + decl_file = files.GetFileSpecAtIndex(file_offset); + uint32_t decl_line = inlinee_line.Header->SourceLineNum; + std::unique_ptr<Declaration> decl_up = + std::make_unique<Declaration>(decl_file, decl_line); + + // Parse range and line info. + uint32_t code_offset = 0; + int32_t line_offset = 0; + std::optional<uint32_t> code_offset_base; + std::optional<uint32_t> code_offset_end; + std::optional<int32_t> cur_line_offset; + std::optional<int32_t> next_line_offset; + std::optional<uint32_t> next_file_offset; + + bool is_terminal_entry = false; + bool is_start_of_statement = true; + // The first instruction is the prologue end. + bool is_prologue_end = true; + + auto update_code_offset = [&](uint32_t code_delta) { + if (!code_offset_base) + code_offset_base = code_offset; + else if (!code_offset_end) + code_offset_end = *code_offset_base + code_delta; + }; + auto update_line_offset = [&](int32_t line_delta) { + line_offset += line_delta; + if (!code_offset_base || !cur_line_offset) + cur_line_offset = line_offset; + else + next_line_offset = line_offset; + ; + }; + auto update_file_offset = [&](uint32_t offset) { + if (!code_offset_base) + file_offset = offset; + else + next_file_offset = offset; + }; + + for (auto &annot : inline_site.annotations()) { + switch (annot.OpCode) { + case BinaryAnnotationsOpCode::CodeOffset: + case BinaryAnnotationsOpCode::ChangeCodeOffset: + case BinaryAnnotationsOpCode::ChangeCodeOffsetBase: + code_offset += annot.U1; + update_code_offset(annot.U1); + break; + case BinaryAnnotationsOpCode::ChangeLineOffset: + update_line_offset(annot.S1); + break; + case BinaryAnnotationsOpCode::ChangeCodeLength: + update_code_offset(annot.U1); + code_offset += annot.U1; + is_terminal_entry = true; + break; + case BinaryAnnotationsOpCode::ChangeCodeOffsetAndLineOffset: + code_offset += annot.U1; + update_code_offset(annot.U1); + update_line_offset(annot.S1); + break; + case BinaryAnnotationsOpCode::ChangeCodeLengthAndCodeOffset: + code_offset += annot.U2; + update_code_offset(annot.U2); + update_code_offset(annot.U1); + code_offset += annot.U1; + is_terminal_entry = true; + break; + case BinaryAnnotationsOpCode::ChangeFile: + update_file_offset(annot.U1); + break; + default: + break; + } + + // Add range if current range is finished. + if (code_offset_base && code_offset_end && cur_line_offset) { + inline_site_sp->ranges.Append(RangeSourceLineVector::Entry( + *code_offset_base, *code_offset_end - *code_offset_base, + decl_line + *cur_line_offset)); + // Set base, end, file offset and line offset for next range. + if (next_file_offset) + file_offset = *next_file_offset; + if (next_line_offset) { + cur_line_offset = next_line_offset; + next_line_offset = std::nullopt; + } + code_offset_base = is_terminal_entry ? std::nullopt : code_offset_end; + code_offset_end = next_file_offset = std::nullopt; + } + if (code_offset_base && cur_line_offset) { + if (is_terminal_entry) { + LineTable::Entry line_entry( + func_base + *code_offset_base, decl_line + *cur_line_offset, 0, + file_offset, false, false, false, false, true); + inline_site_sp->line_entries.push_back(line_entry); + } else { + LineTable::Entry line_entry(func_base + *code_offset_base, + decl_line + *cur_line_offset, 0, + file_offset, is_start_of_statement, false, + is_prologue_end, false, false); + inline_site_sp->line_entries.push_back(line_entry); + is_prologue_end = false; + is_start_of_statement = false; + } + } + if (is_terminal_entry) + is_start_of_statement = true; + is_terminal_entry = false; + } + + inline_site_sp->ranges.Sort(); + + // Get the inlined function callsite info. + std::unique_ptr<Declaration> callsite_up; + if (!inline_site_sp->ranges.IsEmpty()) { + auto *entry = inline_site_sp->ranges.GetEntryAtIndex(0); + addr_t base_offset = entry->GetRangeBase(); + if (cii->m_debug_stream.readSymbolAtOffset(parent_id.offset).kind() == + S_INLINESITE) { + // Its parent is another inline site, lookup parent site's range vector + // for callsite line. + ParseInlineSite(parent_id, func_base); + std::shared_ptr<InlineSite> parent_site = + m_inline_sites[toOpaqueUid(parent_id)]; + FileSpec &parent_decl_file = + parent_site->inline_function_info->GetDeclaration().GetFile(); + if (auto *parent_entry = + parent_site->ranges.FindEntryThatContains(base_offset)) { + callsite_up = + std::make_unique<Declaration>(parent_decl_file, parent_entry->data); + } + } else { + // Its parent is a function, lookup global line table for callsite. + if (auto *entry = cii->m_global_line_table.FindEntryThatContains( + func_base + base_offset)) { + const FileSpec &callsite_file = + files.GetFileSpecAtIndex(entry->data.first); + callsite_up = + std::make_unique<Declaration>(callsite_file, entry->data.second); + } + } + } + + // Get the inlined function name. + CVType inlinee_cvt = m_index->ipi().getType(inline_site.Inlinee); + std::string inlinee_name; + if (inlinee_cvt.kind() == LF_MFUNC_ID) { + MemberFuncIdRecord mfr; + cantFail( + TypeDeserializer::deserializeAs<MemberFuncIdRecord>(inlinee_cvt, mfr)); + LazyRandomTypeCollection &types = m_index->tpi().typeCollection(); + inlinee_name.append(std::string(types.getTypeName(mfr.ClassType))); + inlinee_name.append("::"); + inlinee_name.append(mfr.getName().str()); + } else if (inlinee_cvt.kind() == LF_FUNC_ID) { + FuncIdRecord fir; + cantFail(TypeDeserializer::deserializeAs<FuncIdRecord>(inlinee_cvt, fir)); + TypeIndex parent_idx = fir.getParentScope(); + if (!parent_idx.isNoneType()) { + LazyRandomTypeCollection &ids = m_index->ipi().typeCollection(); + inlinee_name.append(std::string(ids.getTypeName(parent_idx))); + inlinee_name.append("::"); + } + inlinee_name.append(fir.getName().str()); + } + inline_site_sp->inline_function_info = std::make_shared<InlineFunctionInfo>( + inlinee_name.c_str(), llvm::StringRef(), decl_up.get(), + callsite_up.get()); + + m_inline_sites[opaque_uid] = inline_site_sp; +} + +size_t SymbolFileNativePDB::ParseBlocksRecursive(Function &func) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + PdbCompilandSymId func_id = PdbSymUid(func.GetID()).asCompilandSym(); + // After we iterate through inline sites inside the function, we already get + // all the info needed, removing from the map to save memory. + std::set<uint64_t> remove_uids; + auto parse_blocks = [&](SymbolKind kind, PdbCompilandSymId id) { + if (kind == S_GPROC32 || kind == S_LPROC32 || kind == S_BLOCK32 || + kind == S_INLINESITE) { + GetOrCreateBlock(id); + if (kind == S_INLINESITE) + remove_uids.insert(toOpaqueUid(id)); + return true; + } + return false; + }; + size_t count = ParseSymbolArrayInScope(func_id, parse_blocks); + for (uint64_t uid : remove_uids) { + m_inline_sites.erase(uid); + } + return count; +} + +size_t SymbolFileNativePDB::ParseSymbolArrayInScope( + PdbCompilandSymId parent_id, + llvm::function_ref<bool(SymbolKind, PdbCompilandSymId)> fn) { + CompilandIndexItem *cii = m_index->compilands().GetCompiland(parent_id.modi); + CVSymbolArray syms = + cii->m_debug_stream.getSymbolArrayForScope(parent_id.offset); + + size_t count = 1; + for (auto iter = syms.begin(); iter != syms.end(); ++iter) { + PdbCompilandSymId child_id(parent_id.modi, iter.offset()); + if (fn(iter->kind(), child_id)) + ++count; + } + + return count; +} + +void SymbolFileNativePDB::DumpClangAST(Stream &s) { + auto ts_or_err = GetTypeSystemForLanguage(eLanguageTypeC_plus_plus); + if (!ts_or_err) + return; + auto ts = *ts_or_err; + TypeSystemClang *clang = llvm::dyn_cast_or_null<TypeSystemClang>(ts.get()); + if (!clang) + return; + clang->GetNativePDBParser()->Dump(s); +} + +void SymbolFileNativePDB::FindGlobalVariables( + ConstString name, const CompilerDeclContext &parent_decl_ctx, + uint32_t max_matches, VariableList &variables) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + using SymbolAndOffset = std::pair<uint32_t, llvm::codeview::CVSymbol>; + + std::vector<SymbolAndOffset> results = m_index->globals().findRecordsByName( + name.GetStringRef(), m_index->symrecords()); + for (const SymbolAndOffset &result : results) { + switch (result.second.kind()) { + case SymbolKind::S_GDATA32: + case SymbolKind::S_LDATA32: + case SymbolKind::S_GTHREAD32: + case SymbolKind::S_LTHREAD32: + case SymbolKind::S_CONSTANT: { + PdbGlobalSymId global(result.first, false); + if (VariableSP var = GetOrCreateGlobalVariable(global)) + variables.AddVariable(var); + break; + } + default: + continue; + } + } +} + +void SymbolFileNativePDB::FindFunctions( + const Module::LookupInfo &lookup_info, + const CompilerDeclContext &parent_decl_ctx, bool include_inlines, + SymbolContextList &sc_list) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + ConstString name = lookup_info.GetLookupName(); + FunctionNameType name_type_mask = lookup_info.GetNameTypeMask(); + if (name_type_mask & eFunctionNameTypeFull) + name = lookup_info.GetName(); + + // For now we only support lookup by method name or full name. + if (!(name_type_mask & eFunctionNameTypeFull || + name_type_mask & eFunctionNameTypeMethod)) + return; + + using SymbolAndOffset = std::pair<uint32_t, llvm::codeview::CVSymbol>; + + std::vector<SymbolAndOffset> matches = m_index->globals().findRecordsByName( + name.GetStringRef(), m_index->symrecords()); + for (const SymbolAndOffset &match : matches) { + if (match.second.kind() != S_PROCREF && match.second.kind() != S_LPROCREF) + continue; + ProcRefSym proc(match.second.kind()); + cantFail(SymbolDeserializer::deserializeAs<ProcRefSym>(match.second, proc)); + + if (!IsValidRecord(proc)) + continue; + + CompilandIndexItem &cci = + m_index->compilands().GetOrCreateCompiland(proc.modi()); + SymbolContext sc; + + sc.comp_unit = GetOrCreateCompileUnit(cci).get(); + PdbCompilandSymId func_id(proc.modi(), proc.SymOffset); + sc.function = GetOrCreateFunction(func_id, *sc.comp_unit).get(); + + sc_list.Append(sc); + } +} + +void SymbolFileNativePDB::FindFunctions(const RegularExpression ®ex, + bool include_inlines, + SymbolContextList &sc_list) {} + +void SymbolFileNativePDB::FindTypes(const lldb_private::TypeQuery &query, + lldb_private::TypeResults &results) { + + // Make sure we haven't already searched this SymbolFile before. + if (results.AlreadySearched(this)) + return; + + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + + std::vector<TypeIndex> matches = + m_index->tpi().findRecordsByName(query.GetTypeBasename().GetStringRef()); + + for (TypeIndex type_idx : matches) { + TypeSP type_sp = GetOrCreateType(type_idx); + if (!type_sp) + continue; + + // We resolved a type. Get the fully qualified name to ensure it matches. + ConstString name = type_sp->GetQualifiedName(); + TypeQuery type_match(name.GetStringRef(), TypeQueryOptions::e_exact_match); + if (query.ContextMatches(type_match.GetContextRef())) { + results.InsertUnique(type_sp); + if (results.Done(query)) + return; + } + } +} + +void SymbolFileNativePDB::FindTypesByName(llvm::StringRef name, + uint32_t max_matches, + TypeMap &types) { + + std::vector<TypeIndex> matches = m_index->tpi().findRecordsByName(name); + if (max_matches > 0 && max_matches < matches.size()) + matches.resize(max_matches); + + for (TypeIndex ti : matches) { + TypeSP type = GetOrCreateType(ti); + if (!type) + continue; + + types.Insert(type); + } +} + +size_t SymbolFileNativePDB::ParseTypes(CompileUnit &comp_unit) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + // Only do the full type scan the first time. + if (m_done_full_type_scan) + return 0; + + const size_t old_count = GetTypeList().GetSize(); + LazyRandomTypeCollection &types = m_index->tpi().typeCollection(); + + // First process the entire TPI stream. + for (auto ti = types.getFirst(); ti; ti = types.getNext(*ti)) { + TypeSP type = GetOrCreateType(*ti); + if (type) + (void)type->GetFullCompilerType(); + } + + // Next look for S_UDT records in the globals stream. + for (const uint32_t gid : m_index->globals().getGlobalsTable()) { + PdbGlobalSymId global{gid, false}; + CVSymbol sym = m_index->ReadSymbolRecord(global); + if (sym.kind() != S_UDT) + continue; + + UDTSym udt = llvm::cantFail(SymbolDeserializer::deserializeAs<UDTSym>(sym)); + bool is_typedef = true; + if (IsTagRecord(PdbTypeSymId{udt.Type, false}, m_index->tpi())) { + CVType cvt = m_index->tpi().getType(udt.Type); + llvm::StringRef name = CVTagRecord::create(cvt).name(); + if (name == udt.Name) + is_typedef = false; + } + + if (is_typedef) + GetOrCreateTypedef(global); + } + + const size_t new_count = GetTypeList().GetSize(); + + m_done_full_type_scan = true; + + return new_count - old_count; +} + +size_t +SymbolFileNativePDB::ParseVariablesForCompileUnit(CompileUnit &comp_unit, + VariableList &variables) { + PdbSymUid sym_uid(comp_unit.GetID()); + lldbassert(sym_uid.kind() == PdbSymUidKind::Compiland); + return 0; +} + +VariableSP SymbolFileNativePDB::CreateLocalVariable(PdbCompilandSymId scope_id, + PdbCompilandSymId var_id, + bool is_param) { + ModuleSP module = GetObjectFile()->GetModule(); + Block &block = GetOrCreateBlock(scope_id); + // Get function block. + Block *func_block = █ + while (func_block->GetParent()) { + func_block = func_block->GetParent(); + } + Address addr; + func_block->GetStartAddress(addr); + VariableInfo var_info = + GetVariableLocationInfo(*m_index, var_id, *func_block, module); + Function *func = func_block->CalculateSymbolContextFunction(); + if (!func) + return nullptr; + // Use empty dwarf expr if optimized away so that it won't be filtered out + // when lookuping local variables in this scope. + if (!var_info.location.IsValid()) + var_info.location = DWARFExpressionList(module, DWARFExpression(), nullptr); + var_info.location.SetFuncFileAddress( + func->GetAddressRange().GetBaseAddress().GetFileAddress()); + CompilandIndexItem *cii = m_index->compilands().GetCompiland(var_id.modi); + CompUnitSP comp_unit_sp = GetOrCreateCompileUnit(*cii); + TypeSP type_sp = GetOrCreateType(var_info.type); + if (!type_sp) + return nullptr; + std::string name = var_info.name.str(); + Declaration decl; + SymbolFileTypeSP sftype = + std::make_shared<SymbolFileType>(*this, type_sp->GetID()); + + is_param |= var_info.is_param; + ValueType var_scope = + is_param ? eValueTypeVariableArgument : eValueTypeVariableLocal; + bool external = false; + bool artificial = false; + bool location_is_constant_data = false; + bool static_member = false; + Variable::RangeList scope_ranges; + VariableSP var_sp = std::make_shared<Variable>( + toOpaqueUid(var_id), name.c_str(), name.c_str(), sftype, var_scope, + &block, scope_ranges, &decl, var_info.location, external, artificial, + location_is_constant_data, static_member); + if (!is_param) { + auto ts_or_err = GetTypeSystemForLanguage(comp_unit_sp->GetLanguage()); + if (auto err = ts_or_err.takeError()) + return nullptr; + auto ts = *ts_or_err; + if (!ts) + return nullptr; + + ts->GetNativePDBParser()->GetOrCreateVariableDecl(scope_id, var_id); + } + m_local_variables[toOpaqueUid(var_id)] = var_sp; + return var_sp; +} + +VariableSP SymbolFileNativePDB::GetOrCreateLocalVariable( + PdbCompilandSymId scope_id, PdbCompilandSymId var_id, bool is_param) { + auto iter = m_local_variables.find(toOpaqueUid(var_id)); + if (iter != m_local_variables.end()) + return iter->second; + + return CreateLocalVariable(scope_id, var_id, is_param); +} + +TypeSP SymbolFileNativePDB::CreateTypedef(PdbGlobalSymId id) { + CVSymbol sym = m_index->ReadSymbolRecord(id); + lldbassert(sym.kind() == SymbolKind::S_UDT); + + UDTSym udt = llvm::cantFail(SymbolDeserializer::deserializeAs<UDTSym>(sym)); + + TypeSP target_type = GetOrCreateType(udt.Type); + + auto ts_or_err = GetTypeSystemForLanguage(lldb::eLanguageTypeC_plus_plus); + if (auto err = ts_or_err.takeError()) + return nullptr; + auto ts = *ts_or_err; + if (!ts) + return nullptr; + + ts->GetNativePDBParser()->GetOrCreateTypedefDecl(id); + + Declaration decl; + return MakeType( + toOpaqueUid(id), ConstString(udt.Name), target_type->GetByteSize(nullptr), + nullptr, target_type->GetID(), lldb_private::Type::eEncodingIsTypedefUID, + decl, target_type->GetForwardCompilerType(), + lldb_private::Type::ResolveState::Forward); +} + +TypeSP SymbolFileNativePDB::GetOrCreateTypedef(PdbGlobalSymId id) { + auto iter = m_types.find(toOpaqueUid(id)); + if (iter != m_types.end()) + return iter->second; + + return CreateTypedef(id); +} + +size_t SymbolFileNativePDB::ParseVariablesForBlock(PdbCompilandSymId block_id) { + Block &block = GetOrCreateBlock(block_id); + + size_t count = 0; + + CompilandIndexItem *cii = m_index->compilands().GetCompiland(block_id.modi); + CVSymbol sym = cii->m_debug_stream.readSymbolAtOffset(block_id.offset); + uint32_t params_remaining = 0; + switch (sym.kind()) { + case S_GPROC32: + case S_LPROC32: { + ProcSym proc(static_cast<SymbolRecordKind>(sym.kind())); + cantFail(SymbolDeserializer::deserializeAs<ProcSym>(sym, proc)); + CVType signature = m_index->tpi().getType(proc.FunctionType); + if (signature.kind() == LF_PROCEDURE) { + ProcedureRecord sig; + if (llvm::Error e = TypeDeserializer::deserializeAs<ProcedureRecord>( + signature, sig)) { + llvm::consumeError(std::move(e)); + return 0; + } + params_remaining = sig.getParameterCount(); + } else if (signature.kind() == LF_MFUNCTION) { + MemberFunctionRecord sig; + if (llvm::Error e = TypeDeserializer::deserializeAs<MemberFunctionRecord>( + signature, sig)) { + llvm::consumeError(std::move(e)); + return 0; + } + params_remaining = sig.getParameterCount(); + } else + return 0; + break; + } + case S_BLOCK32: + break; + case S_INLINESITE: + break; + default: + lldbassert(false && "Symbol is not a block!"); + return 0; + } + + VariableListSP variables = block.GetBlockVariableList(false); + if (!variables) { + variables = std::make_shared<VariableList>(); + block.SetVariableList(variables); + } + + CVSymbolArray syms = limitSymbolArrayToScope( + cii->m_debug_stream.getSymbolArray(), block_id.offset); + + // Skip the first record since it's a PROC32 or BLOCK32, and there's + // no point examining it since we know it's not a local variable. + syms.drop_front(); + auto iter = syms.begin(); + auto end = syms.end(); + + while (iter != end) { + uint32_t record_offset = iter.offset(); + CVSymbol variable_cvs = *iter; + PdbCompilandSymId child_sym_id(block_id.modi, record_offset); + ++iter; + + // If this is a block or inline site, recurse into its children and then + // skip it. + if (variable_cvs.kind() == S_BLOCK32 || + variable_cvs.kind() == S_INLINESITE) { + uint32_t block_end = getScopeEndOffset(variable_cvs); + count += ParseVariablesForBlock(child_sym_id); + iter = syms.at(block_end); + continue; + } + + bool is_param = params_remaining > 0; + VariableSP variable; + switch (variable_cvs.kind()) { + case S_REGREL32: + case S_REGISTER: + case S_LOCAL: + variable = GetOrCreateLocalVariable(block_id, child_sym_id, is_param); + if (is_param) + --params_remaining; + if (variable) + variables->AddVariableIfUnique(variable); + break; + default: + break; + } + } + + // Pass false for set_children, since we call this recursively so that the + // children will call this for themselves. + block.SetDidParseVariables(true, false); + + return count; +} + +size_t SymbolFileNativePDB::ParseVariablesForContext(const SymbolContext &sc) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + lldbassert(sc.function || sc.comp_unit); + + VariableListSP variables; + if (sc.block) { + PdbSymUid block_id(sc.block->GetID()); + + size_t count = ParseVariablesForBlock(block_id.asCompilandSym()); + return count; + } + + if (sc.function) { + PdbSymUid block_id(sc.function->GetID()); + + size_t count = ParseVariablesForBlock(block_id.asCompilandSym()); + return count; + } + + if (sc.comp_unit) { + variables = sc.comp_unit->GetVariableList(false); + if (!variables) { + variables = std::make_shared<VariableList>(); + sc.comp_unit->SetVariableList(variables); + } + return ParseVariablesForCompileUnit(*sc.comp_unit, *variables); + } + + llvm_unreachable("Unreachable!"); +} + +CompilerDecl SymbolFileNativePDB::GetDeclForUID(lldb::user_id_t uid) { + auto ts_or_err = GetTypeSystemForLanguage(lldb::eLanguageTypeC_plus_plus); + if (auto err = ts_or_err.takeError()) + return CompilerDecl(); + auto ts = *ts_or_err; + if (!ts) + return {}; + + if (auto decl = ts->GetNativePDBParser()->GetOrCreateDeclForUid(uid)) + return *decl; + return CompilerDecl(); +} + +CompilerDeclContext +SymbolFileNativePDB::GetDeclContextForUID(lldb::user_id_t uid) { + auto ts_or_err = GetTypeSystemForLanguage(lldb::eLanguageTypeC_plus_plus); + if (auto err = ts_or_err.takeError()) + return {}; + auto ts = *ts_or_err; + if (!ts) + return {}; + + PdbAstBuilder *ast_builder = ts->GetNativePDBParser(); + clang::DeclContext *context = + ast_builder->GetOrCreateDeclContextForUid(PdbSymUid(uid)); + if (!context) + return {}; + + return ast_builder->ToCompilerDeclContext(*context); +} + +CompilerDeclContext +SymbolFileNativePDB::GetDeclContextContainingUID(lldb::user_id_t uid) { + auto ts_or_err = GetTypeSystemForLanguage(lldb::eLanguageTypeC_plus_plus); + if (auto err = ts_or_err.takeError()) + return CompilerDeclContext(); + auto ts = *ts_or_err; + if (!ts) + return {}; + + PdbAstBuilder *ast_builder = ts->GetNativePDBParser(); + clang::DeclContext *context = ast_builder->GetParentDeclContext(PdbSymUid(uid)); + if (!context) + return CompilerDeclContext(); + return ast_builder->ToCompilerDeclContext(*context); +} + +Type *SymbolFileNativePDB::ResolveTypeUID(lldb::user_id_t type_uid) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + auto iter = m_types.find(type_uid); + // lldb should not be passing us non-sensical type uids. the only way it + // could have a type uid in the first place is if we handed it out, in which + // case we should know about the type. However, that doesn't mean we've + // instantiated it yet. We can vend out a UID for a future type. So if the + // type doesn't exist, let's instantiate it now. + if (iter != m_types.end()) + return &*iter->second; + + PdbSymUid uid(type_uid); + lldbassert(uid.kind() == PdbSymUidKind::Type); + PdbTypeSymId type_id = uid.asTypeSym(); + if (type_id.index.isNoneType()) + return nullptr; + + TypeSP type_sp = CreateAndCacheType(type_id); + if (!type_sp) + return nullptr; + return &*type_sp; +} + +std::optional<SymbolFile::ArrayInfo> +SymbolFileNativePDB::GetDynamicArrayInfoForUID( + lldb::user_id_t type_uid, const lldb_private::ExecutionContext *exe_ctx) { + return std::nullopt; +} + +bool SymbolFileNativePDB::CompleteType(CompilerType &compiler_type) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + auto ts = compiler_type.GetTypeSystem(); + auto clang_type_system = ts.dyn_cast_or_null<TypeSystemClang>(); + if (!clang_type_system) + return false; + + PdbAstBuilder *ast_builder = + static_cast<PdbAstBuilder *>(clang_type_system->GetNativePDBParser()); + if (ast_builder && + ast_builder->GetClangASTImporter().CanImport(compiler_type)) + return ast_builder->GetClangASTImporter().CompleteType(compiler_type); + clang::QualType qt = + clang::QualType::getFromOpaquePtr(compiler_type.GetOpaqueQualType()); + + return ast_builder->CompleteType(qt); +} + +void SymbolFileNativePDB::GetTypes(lldb_private::SymbolContextScope *sc_scope, + TypeClass type_mask, + lldb_private::TypeList &type_list) {} + +CompilerDeclContext SymbolFileNativePDB::FindNamespace( + ConstString name, const CompilerDeclContext &parent_decl_ctx, bool) { + return {}; +} + +llvm::Expected<lldb::TypeSystemSP> +SymbolFileNativePDB::GetTypeSystemForLanguage(lldb::LanguageType language) { + auto type_system_or_err = + m_objfile_sp->GetModule()->GetTypeSystemForLanguage(language); + if (type_system_or_err) + if (auto ts = *type_system_or_err) + ts->SetSymbolFile(this); + return type_system_or_err; +} + +uint64_t SymbolFileNativePDB::GetDebugInfoSize(bool load_all_debug_info) { + // PDB files are a separate file that contains all debug info. + return m_index->pdb().getFileSize(); +} + +void SymbolFileNativePDB::BuildParentMap() { + LazyRandomTypeCollection &types = m_index->tpi().typeCollection(); + + llvm::DenseMap<TypeIndex, TypeIndex> forward_to_full; + llvm::DenseMap<TypeIndex, TypeIndex> full_to_forward; + + struct RecordIndices { + TypeIndex forward; + TypeIndex full; + }; + + llvm::StringMap<RecordIndices> record_indices; + + for (auto ti = types.getFirst(); ti; ti = types.getNext(*ti)) { + CVType type = types.getType(*ti); + if (!IsTagRecord(type)) + continue; + + CVTagRecord tag = CVTagRecord::create(type); + + RecordIndices &indices = record_indices[tag.asTag().getUniqueName()]; + if (tag.asTag().isForwardRef()) + indices.forward = *ti; + else + indices.full = *ti; + + if (indices.full != TypeIndex::None() && + indices.forward != TypeIndex::None()) { + forward_to_full[indices.forward] = indices.full; + full_to_forward[indices.full] = indices.forward; + } + + // We're looking for LF_NESTTYPE records in the field list, so ignore + // forward references (no field list), and anything without a nested class + // (since there won't be any LF_NESTTYPE records). + if (tag.asTag().isForwardRef() || !tag.asTag().containsNestedClass()) + continue; + + struct ProcessTpiStream : public TypeVisitorCallbacks { + ProcessTpiStream(PdbIndex &index, TypeIndex parent, + const CVTagRecord &parent_cvt, + llvm::DenseMap<TypeIndex, TypeIndex> &parents) + : index(index), parents(parents), parent(parent), + parent_cvt(parent_cvt) {} + + PdbIndex &index; + llvm::DenseMap<TypeIndex, TypeIndex> &parents; + + unsigned unnamed_type_index = 1; + TypeIndex parent; + const CVTagRecord &parent_cvt; + + llvm::Error visitKnownMember(CVMemberRecord &CVR, + NestedTypeRecord &Record) override { + std::string unnamed_type_name; + if (Record.Name.empty()) { + unnamed_type_name = + llvm::formatv("<unnamed-type-$S{0}>", unnamed_type_index).str(); + Record.Name = unnamed_type_name; + ++unnamed_type_index; + } + std::optional<CVTagRecord> tag = + GetNestedTagDefinition(Record, parent_cvt, index.tpi()); + if (!tag) + return llvm::ErrorSuccess(); + + parents[Record.Type] = parent; + return llvm::ErrorSuccess(); + } + }; + + CVType field_list_cvt = m_index->tpi().getType(tag.asTag().FieldList); + ProcessTpiStream process(*m_index, *ti, tag, m_parent_types); + FieldListRecord field_list; + if (llvm::Error error = TypeDeserializer::deserializeAs<FieldListRecord>( + field_list_cvt, field_list)) + llvm::consumeError(std::move(error)); + if (llvm::Error error = visitMemberRecordStream(field_list.Data, process)) + llvm::consumeError(std::move(error)); + } + + // Now that we know the forward -> full mapping of all type indices, we can + // re-write all the indices. At the end of this process, we want a mapping + // consisting of fwd -> full and full -> full for all child -> parent indices. + // We can re-write the values in place, but for the keys, we must save them + // off so that we don't modify the map in place while also iterating it. + std::vector<TypeIndex> full_keys; + std::vector<TypeIndex> fwd_keys; + for (auto &entry : m_parent_types) { + TypeIndex key = entry.first; + TypeIndex value = entry.second; + + auto iter = forward_to_full.find(value); + if (iter != forward_to_full.end()) + entry.second = iter->second; + + iter = forward_to_full.find(key); + if (iter != forward_to_full.end()) + fwd_keys.push_back(key); + else + full_keys.push_back(key); + } + for (TypeIndex fwd : fwd_keys) { + TypeIndex full = forward_to_full[fwd]; + TypeIndex parent_idx = m_parent_types[fwd]; + m_parent_types[full] = parent_idx; + } + for (TypeIndex full : full_keys) { + TypeIndex fwd = full_to_forward[full]; + m_parent_types[fwd] = m_parent_types[full]; + } +} + +std::optional<PdbCompilandSymId> +SymbolFileNativePDB::FindSymbolScope(PdbCompilandSymId id) { + CVSymbol sym = m_index->ReadSymbolRecord(id); + if (symbolOpensScope(sym.kind())) { + // If this exact symbol opens a scope, we can just directly access its + // parent. + id.offset = getScopeParentOffset(sym); + // Global symbols have parent offset of 0. Return std::nullopt to indicate + // this. + if (id.offset == 0) + return std::nullopt; + return id; + } + + // Otherwise we need to start at the beginning and iterate forward until we + // reach (or pass) this particular symbol + CompilandIndexItem &cii = m_index->compilands().GetOrCreateCompiland(id.modi); + const CVSymbolArray &syms = cii.m_debug_stream.getSymbolArray(); + + auto begin = syms.begin(); + auto end = syms.at(id.offset); + std::vector<PdbCompilandSymId> scope_stack; + + while (begin != end) { + if (begin.offset() > id.offset) { + // We passed it. We couldn't even find this symbol record. + lldbassert(false && "Invalid compiland symbol id!"); + return std::nullopt; + } + + // We haven't found the symbol yet. Check if we need to open or close the + // scope stack. + if (symbolOpensScope(begin->kind())) { + // We can use the end offset of the scope to determine whether or not + // we can just outright skip this entire scope. + uint32_t scope_end = getScopeEndOffset(*begin); + if (scope_end < id.offset) { + begin = syms.at(scope_end); + } else { + // The symbol we're looking for is somewhere in this scope. + scope_stack.emplace_back(id.modi, begin.offset()); + } + } else if (symbolEndsScope(begin->kind())) { + scope_stack.pop_back(); + } + ++begin; + } + if (scope_stack.empty()) + return std::nullopt; + // We have a match! Return the top of the stack + return scope_stack.back(); +} + +std::optional<llvm::codeview::TypeIndex> +SymbolFileNativePDB::GetParentType(llvm::codeview::TypeIndex ti) { + auto parent_iter = m_parent_types.find(ti); + if (parent_iter == m_parent_types.end()) + return std::nullopt; + return parent_iter->second; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h new file mode 100644 index 000000000000..669c44aa131e --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h @@ -0,0 +1,286 @@ +//===-- SymbolFileNativePDB.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_SOURCE_PLUGINS_SYMBOLFILE_NATIVEPDB_SYMBOLFILENATIVEPDB_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_NATIVEPDB_SYMBOLFILENATIVEPDB_H + +#include "lldb/Symbol/LineTable.h" +#include "lldb/Symbol/SymbolFile.h" + +#include "llvm/ADT/DenseMap.h" +#include "llvm/DebugInfo/CodeView/CVRecord.h" +#include "llvm/DebugInfo/CodeView/SymbolRecord.h" +#include "llvm/DebugInfo/PDB/PDBTypes.h" + +#include "CompileUnitIndex.h" +#include "PdbIndex.h" +#include "PdbAstBuilder.h" +#include <optional> + +namespace clang { +class TagDecl; +} + +namespace llvm { +namespace codeview { +class ClassRecord; +class EnumRecord; +class ModifierRecord; +class PointerRecord; +struct UnionRecord; +} // namespace codeview +} // namespace llvm + +namespace lldb_private { + +namespace npdb { + +class SymbolFileNativePDB : public SymbolFileCommon { + friend class UdtRecordCompleter; + + /// LLVM RTTI support. + static char ID; + +public: + /// LLVM RTTI support. + /// \{ + bool isA(const void *ClassID) const override { + return ClassID == &ID || SymbolFileCommon::isA(ClassID); + } + static bool classof(const SymbolFile *obj) { return obj->isA(&ID); } + /// \} + + // Static Functions + static void Initialize(); + + static void Terminate(); + + static void DebuggerInitialize(Debugger &debugger); + + static llvm::StringRef GetPluginNameStatic() { return "native-pdb"; } + + static llvm::StringRef GetPluginDescriptionStatic(); + + static SymbolFile *CreateInstance(lldb::ObjectFileSP objfile_sp); + + // Constructors and Destructors + SymbolFileNativePDB(lldb::ObjectFileSP objfile_sp); + + ~SymbolFileNativePDB() override; + + uint32_t CalculateAbilities() override; + + void InitializeObject() override; + + uint64_t GetDebugInfoSize(bool load_all_debug_info = false) override; + + // Compile Unit function calls + + void + ParseDeclsForContext(lldb_private::CompilerDeclContext decl_ctx) 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, + SupportFileList &support_files) override; + size_t ParseTypes(lldb_private::CompileUnit &comp_unit) override; + + bool ParseImportedModules( + const SymbolContext &sc, + std::vector<lldb_private::SourceModule> &imported_modules) override; + + size_t ParseBlocksRecursive(Function &func) override; + + void FindGlobalVariables(ConstString name, + const CompilerDeclContext &parent_decl_ctx, + uint32_t max_matches, + VariableList &variables) override; + + size_t ParseVariablesForContext(const SymbolContext &sc) override; + + void AddSymbols(Symtab &symtab) override; + + CompilerDecl GetDeclForUID(lldb::user_id_t uid) override; + CompilerDeclContext GetDeclContextForUID(lldb::user_id_t uid) override; + CompilerDeclContext GetDeclContextContainingUID(lldb::user_id_t uid) override; + Type *ResolveTypeUID(lldb::user_id_t type_uid) override; + std::optional<ArrayInfo> GetDynamicArrayInfoForUID( + lldb::user_id_t type_uid, + const lldb_private::ExecutionContext *exe_ctx) override; + + bool CompleteType(CompilerType &compiler_type) override; + uint32_t ResolveSymbolContext(const Address &so_addr, + lldb::SymbolContextItem resolve_scope, + SymbolContext &sc) override; + uint32_t ResolveSymbolContext(const SourceLocationSpec &src_location_spec, + lldb::SymbolContextItem resolve_scope, + SymbolContextList &sc_list) override; + + void GetTypes(SymbolContextScope *sc_scope, lldb::TypeClass type_mask, + TypeList &type_list) override; + + void FindFunctions(const Module::LookupInfo &lookup_info, + const CompilerDeclContext &parent_decl_ctx, + bool include_inlines, SymbolContextList &sc_list) override; + + void FindFunctions(const RegularExpression ®ex, bool include_inlines, + SymbolContextList &sc_list) override; + + std::optional<PdbCompilandSymId> FindSymbolScope(PdbCompilandSymId id); + + void FindTypes(const lldb_private::TypeQuery &match, + lldb_private::TypeResults &results) override; + + llvm::Expected<lldb::TypeSystemSP> + GetTypeSystemForLanguage(lldb::LanguageType language) override; + + CompilerDeclContext FindNamespace(ConstString name, + const CompilerDeclContext &parent_decl_ctx, + bool only_root_namespaces) override; + + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } + + llvm::pdb::PDBFile &GetPDBFile() { return m_index->pdb(); } + const llvm::pdb::PDBFile &GetPDBFile() const { return m_index->pdb(); } + + PdbIndex &GetIndex() { return *m_index; }; + + void DumpClangAST(Stream &s) override; + + std::optional<llvm::codeview::TypeIndex> + GetParentType(llvm::codeview::TypeIndex ti); + +private: + struct LineTableEntryComparator { + bool operator()(const lldb_private::LineTable::Entry &lhs, + const lldb_private::LineTable::Entry &rhs) const { + return lhs.file_addr < rhs.file_addr; + } + }; + + // From address range relative to function base to source line number. + using RangeSourceLineVector = + lldb_private::RangeDataVector<uint32_t, uint32_t, int32_t>; + // InlineSite contains information in a S_INLINESITE record. + struct InlineSite { + PdbCompilandSymId parent_id; + std::shared_ptr<InlineFunctionInfo> inline_function_info; + RangeSourceLineVector ranges; + std::vector<lldb_private::LineTable::Entry> line_entries; + InlineSite(PdbCompilandSymId parent_id) : parent_id(parent_id){}; + }; + + void BuildParentMap(); + + uint32_t CalculateNumCompileUnits() override; + + lldb::CompUnitSP ParseCompileUnitAtIndex(uint32_t index) override; + + void FindTypesByName(llvm::StringRef name, uint32_t max_matches, + TypeMap &types); + + lldb::TypeSP CreateModifierType(PdbTypeSymId type_id, + const llvm::codeview::ModifierRecord &mr, + CompilerType ct); + lldb::TypeSP CreatePointerType(PdbTypeSymId type_id, + const llvm::codeview::PointerRecord &pr, + CompilerType ct); + lldb::TypeSP CreateSimpleType(llvm::codeview::TypeIndex ti, CompilerType ct); + lldb::TypeSP CreateTagType(PdbTypeSymId type_id, + const llvm::codeview::ClassRecord &cr, + CompilerType ct); + lldb::TypeSP CreateTagType(PdbTypeSymId type_id, + const llvm::codeview::EnumRecord &er, + CompilerType ct); + lldb::TypeSP CreateTagType(PdbTypeSymId type_id, + const llvm::codeview::UnionRecord &ur, + CompilerType ct); + lldb::TypeSP CreateArrayType(PdbTypeSymId type_id, + const llvm::codeview::ArrayRecord &ar, + CompilerType ct); + lldb::TypeSP CreateFunctionType(PdbTypeSymId type_id, + const llvm::codeview::MemberFunctionRecord &pr, + CompilerType ct); + lldb::TypeSP CreateProcedureType(PdbTypeSymId type_id, + const llvm::codeview::ProcedureRecord &pr, + CompilerType ct); + lldb::TypeSP CreateClassStructUnion(PdbTypeSymId type_id, + const llvm::codeview::TagRecord &record, + size_t size, CompilerType ct); + + lldb::FunctionSP GetOrCreateFunction(PdbCompilandSymId func_id, + CompileUnit &comp_unit); + lldb::CompUnitSP GetOrCreateCompileUnit(const CompilandIndexItem &cci); + lldb::TypeSP GetOrCreateType(PdbTypeSymId type_id); + lldb::TypeSP GetOrCreateType(llvm::codeview::TypeIndex ti); + lldb::VariableSP GetOrCreateGlobalVariable(PdbGlobalSymId var_id); + Block &GetOrCreateBlock(PdbCompilandSymId block_id); + lldb::VariableSP GetOrCreateLocalVariable(PdbCompilandSymId scope_id, + PdbCompilandSymId var_id, + bool is_param); + lldb::TypeSP GetOrCreateTypedef(PdbGlobalSymId id); + + lldb::FunctionSP CreateFunction(PdbCompilandSymId func_id, + CompileUnit &comp_unit); + Block &CreateBlock(PdbCompilandSymId block_id); + lldb::VariableSP CreateLocalVariable(PdbCompilandSymId scope_id, + PdbCompilandSymId var_id, bool is_param); + lldb::TypeSP CreateTypedef(PdbGlobalSymId id); + lldb::CompUnitSP CreateCompileUnit(const CompilandIndexItem &cci); + lldb::TypeSP CreateType(PdbTypeSymId type_id, CompilerType ct); + lldb::TypeSP CreateAndCacheType(PdbTypeSymId type_id); + lldb::VariableSP CreateGlobalVariable(PdbGlobalSymId var_id); + lldb::VariableSP CreateConstantSymbol(PdbGlobalSymId var_id, + const llvm::codeview::CVSymbol &cvs); + size_t ParseVariablesForCompileUnit(CompileUnit &comp_unit, + VariableList &variables); + size_t ParseVariablesForBlock(PdbCompilandSymId block_id); + + llvm::Expected<uint32_t> GetFileIndex(const CompilandIndexItem &cii, + uint32_t file_id); + + size_t ParseSymbolArrayInScope( + PdbCompilandSymId parent, + llvm::function_ref<bool(llvm::codeview::SymbolKind, PdbCompilandSymId)> + fn); + + void ParseInlineSite(PdbCompilandSymId inline_site_id, Address func_addr); + + llvm::BumpPtrAllocator m_allocator; + + lldb::addr_t m_obj_load_address = 0; + bool m_done_full_type_scan = false; + // UID for anonymous union and anonymous struct as they don't have entities in + // pdb debug info. + lldb::user_id_t anonymous_id = LLDB_INVALID_UID - 1; + + std::unique_ptr<llvm::pdb::PDBFile> m_file_up; + std::unique_ptr<PdbIndex> m_index; + + llvm::DenseMap<lldb::user_id_t, lldb::VariableSP> m_global_vars; + llvm::DenseMap<lldb::user_id_t, lldb::VariableSP> m_local_variables; + llvm::DenseMap<lldb::user_id_t, lldb::BlockSP> m_blocks; + llvm::DenseMap<lldb::user_id_t, lldb::FunctionSP> m_functions; + llvm::DenseMap<lldb::user_id_t, lldb::CompUnitSP> m_compilands; + llvm::DenseMap<lldb::user_id_t, lldb::TypeSP> m_types; + llvm::DenseMap<lldb::user_id_t, std::shared_ptr<InlineSite>> m_inline_sites; + llvm::DenseMap<llvm::codeview::TypeIndex, llvm::codeview::TypeIndex> + m_parent_types; +}; + +} // namespace npdb +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_SYMBOLFILE_NATIVEPDB_SYMBOLFILENATIVEPDB_H diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp new file mode 100644 index 000000000000..17c5f6118603 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp @@ -0,0 +1,501 @@ +#include "UdtRecordCompleter.h" + +#include "PdbAstBuilder.h" +#include "PdbIndex.h" +#include "PdbSymUid.h" +#include "PdbUtil.h" + +#include "Plugins/ExpressionParser/Clang/ClangASTImporter.h" +#include "Plugins/ExpressionParser/Clang/ClangASTMetadata.h" +#include "Plugins/ExpressionParser/Clang/ClangUtil.h" +#include "Plugins/TypeSystem/Clang/TypeSystemClang.h" +#include "SymbolFileNativePDB.h" +#include "lldb/Core/Address.h" +#include "lldb/Symbol/Type.h" +#include "lldb/Utility/LLDBAssert.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/lldb-enumerations.h" +#include "lldb/lldb-forward.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h" +#include "llvm/DebugInfo/CodeView/TypeDeserializer.h" +#include "llvm/DebugInfo/CodeView/TypeIndex.h" +#include "llvm/DebugInfo/PDB/Native/GlobalsStream.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" +#include "llvm/DebugInfo/PDB/PDBTypes.h" +#include <optional> + +using namespace llvm::codeview; +using namespace llvm::pdb; +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::npdb; + +using Error = llvm::Error; + +UdtRecordCompleter::UdtRecordCompleter( + PdbTypeSymId id, CompilerType &derived_ct, clang::TagDecl &tag_decl, + PdbAstBuilder &ast_builder, PdbIndex &index, + llvm::DenseMap<clang::Decl *, DeclStatus> &decl_to_status, + llvm::DenseMap<lldb::opaque_compiler_type_t, + llvm::SmallSet<std::pair<llvm::StringRef, CompilerType>, 8>> + &cxx_record_map) + : m_id(id), m_derived_ct(derived_ct), m_tag_decl(tag_decl), + m_ast_builder(ast_builder), m_index(index), + m_decl_to_status(decl_to_status), m_cxx_record_map(cxx_record_map) { + CVType cvt = m_index.tpi().getType(m_id.index); + switch (cvt.kind()) { + case LF_ENUM: + m_cvr.er.Options = ClassOptions::None; + llvm::cantFail(TypeDeserializer::deserializeAs<EnumRecord>(cvt, m_cvr.er)); + break; + case LF_UNION: + m_cvr.ur.Options = ClassOptions::None; + llvm::cantFail(TypeDeserializer::deserializeAs<UnionRecord>(cvt, m_cvr.ur)); + m_layout.bit_size = m_cvr.ur.getSize() * 8; + m_record.record.kind = Member::Union; + break; + case LF_CLASS: + case LF_STRUCTURE: + m_cvr.cr.Options = ClassOptions::None; + llvm::cantFail(TypeDeserializer::deserializeAs<ClassRecord>(cvt, m_cvr.cr)); + m_layout.bit_size = m_cvr.cr.getSize() * 8; + m_record.record.kind = Member::Struct; + break; + default: + llvm_unreachable("unreachable!"); + } +} + +clang::QualType UdtRecordCompleter::AddBaseClassForTypeIndex( + llvm::codeview::TypeIndex ti, llvm::codeview::MemberAccess access, + std::optional<uint64_t> vtable_idx) { + PdbTypeSymId type_id(ti); + clang::QualType qt = m_ast_builder.GetOrCreateType(type_id); + + CVType udt_cvt = m_index.tpi().getType(ti); + + std::unique_ptr<clang::CXXBaseSpecifier> base_spec = + m_ast_builder.clang().CreateBaseClassSpecifier( + qt.getAsOpaquePtr(), TranslateMemberAccess(access), + vtable_idx.has_value(), udt_cvt.kind() == LF_CLASS); + if (!base_spec) + return {}; + + m_bases.push_back( + std::make_pair(vtable_idx.value_or(0), std::move(base_spec))); + + return qt; +} + +void UdtRecordCompleter::AddMethod(llvm::StringRef name, TypeIndex type_idx, + MemberAccess access, MethodOptions options, + MemberAttributes attrs) { + clang::QualType method_qt = + m_ast_builder.GetOrCreateType(PdbTypeSymId(type_idx)); + if (method_qt.isNull()) + return; + CompilerType method_ct = m_ast_builder.ToCompilerType(method_qt); + TypeSystemClang::RequireCompleteType(method_ct); + lldb::opaque_compiler_type_t derived_opaque_ty = + m_derived_ct.GetOpaqueQualType(); + auto iter = m_cxx_record_map.find(derived_opaque_ty); + if (iter != m_cxx_record_map.end()) { + if (iter->getSecond().contains({name, method_ct})) { + return; + } + } + + lldb::AccessType access_type = TranslateMemberAccess(access); + bool is_artificial = (options & MethodOptions::CompilerGenerated) == + MethodOptions::CompilerGenerated; + m_ast_builder.clang().AddMethodToCXXRecordType( + derived_opaque_ty, name.data(), nullptr, method_ct, + access_type, attrs.isVirtual(), attrs.isStatic(), false, false, false, + is_artificial); + + m_cxx_record_map[derived_opaque_ty].insert({name, method_ct}); +} + +Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr, + BaseClassRecord &base) { + clang::QualType base_qt = + AddBaseClassForTypeIndex(base.Type, base.getAccess()); + + if (base_qt.isNull()) + return llvm::Error::success(); + auto decl = + m_ast_builder.clang().GetAsCXXRecordDecl(base_qt.getAsOpaquePtr()); + lldbassert(decl); + + auto offset = clang::CharUnits::fromQuantity(base.getBaseOffset()); + m_layout.base_offsets.insert(std::make_pair(decl, offset)); + + return llvm::Error::success(); +} + +Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr, + VirtualBaseClassRecord &base) { + AddBaseClassForTypeIndex(base.BaseType, base.getAccess(), base.VTableIndex); + + return Error::success(); +} + +Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr, + ListContinuationRecord &cont) { + return Error::success(); +} + +Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr, + VFPtrRecord &vfptr) { + return Error::success(); +} + +Error UdtRecordCompleter::visitKnownMember( + CVMemberRecord &cvr, StaticDataMemberRecord &static_data_member) { + clang::QualType member_type = + m_ast_builder.GetOrCreateType(PdbTypeSymId(static_data_member.Type)); + if (member_type.isNull()) + return llvm::Error::success(); + + CompilerType member_ct = m_ast_builder.ToCompilerType(member_type); + + lldb::AccessType access = + TranslateMemberAccess(static_data_member.getAccess()); + auto decl = TypeSystemClang::AddVariableToRecordType( + m_derived_ct, static_data_member.Name, member_ct, access); + + // Static constant members may be a const[expr] declaration. + // Query the symbol's value as the variable initializer if valid. + if (member_ct.IsConst() && member_ct.IsCompleteType()) { + std::string qual_name = decl->getQualifiedNameAsString(); + + auto results = + m_index.globals().findRecordsByName(qual_name, m_index.symrecords()); + + for (const auto &result : results) { + if (result.second.kind() == SymbolKind::S_CONSTANT) { + ConstantSym constant(SymbolRecordKind::ConstantSym); + cantFail(SymbolDeserializer::deserializeAs<ConstantSym>(result.second, + constant)); + + clang::QualType qual_type = decl->getType(); + unsigned type_width = decl->getASTContext().getIntWidth(qual_type); + unsigned constant_width = constant.Value.getBitWidth(); + + if (qual_type->isIntegralOrEnumerationType()) { + if (type_width >= constant_width) { + TypeSystemClang::SetIntegerInitializerForVariable( + decl, constant.Value.extOrTrunc(type_width)); + } else { + LLDB_LOG(GetLog(LLDBLog::AST), + "Class '{0}' has a member '{1}' of type '{2}' ({3} bits) " + "which resolves to a wider constant value ({4} bits). " + "Ignoring constant.", + m_derived_ct.GetTypeName(), static_data_member.Name, + member_ct.GetTypeName(), type_width, constant_width); + } + } else { + lldb::BasicType basic_type_enum = member_ct.GetBasicTypeEnumeration(); + switch (basic_type_enum) { + case lldb::eBasicTypeFloat: + case lldb::eBasicTypeDouble: + case lldb::eBasicTypeLongDouble: + if (type_width == constant_width) { + TypeSystemClang::SetFloatingInitializerForVariable( + decl, basic_type_enum == lldb::eBasicTypeFloat + ? llvm::APFloat(constant.Value.bitsToFloat()) + : llvm::APFloat(constant.Value.bitsToDouble())); + decl->setConstexpr(true); + } else { + LLDB_LOG( + GetLog(LLDBLog::AST), + "Class '{0}' has a member '{1}' of type '{2}' ({3} bits) " + "which resolves to a constant value of mismatched width " + "({4} bits). Ignoring constant.", + m_derived_ct.GetTypeName(), static_data_member.Name, + member_ct.GetTypeName(), type_width, constant_width); + } + break; + default: + break; + } + } + break; + } + } + } + + // FIXME: Add a PdbSymUid namespace for field list members and update + // the m_uid_to_decl map with this decl. + return Error::success(); +} + +Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr, + NestedTypeRecord &nested) { + return Error::success(); +} + +Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr, + DataMemberRecord &data_member) { + + uint64_t offset = data_member.FieldOffset * 8; + uint32_t bitfield_width = 0; + + TypeIndex ti(data_member.Type); + if (!ti.isSimple()) { + CVType cvt = m_index.tpi().getType(ti); + if (cvt.kind() == LF_BITFIELD) { + BitFieldRecord bfr; + llvm::cantFail(TypeDeserializer::deserializeAs<BitFieldRecord>(cvt, bfr)); + offset += bfr.BitOffset; + bitfield_width = bfr.BitSize; + ti = bfr.Type; + } + } + + clang::QualType member_qt = m_ast_builder.GetOrCreateType(PdbTypeSymId(ti)); + if (member_qt.isNull()) + return Error::success(); + TypeSystemClang::RequireCompleteType(m_ast_builder.ToCompilerType(member_qt)); + lldb::AccessType access = TranslateMemberAccess(data_member.getAccess()); + size_t field_size = + bitfield_width ? bitfield_width : GetSizeOfType(ti, m_index.tpi()) * 8; + if (field_size == 0) + return Error::success(); + m_record.CollectMember(data_member.Name, offset, field_size, member_qt, access, + bitfield_width); + return Error::success(); +} + +Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr, + OneMethodRecord &one_method) { + AddMethod(one_method.Name, one_method.Type, one_method.getAccess(), + one_method.getOptions(), one_method.Attrs); + + return Error::success(); +} + +Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr, + OverloadedMethodRecord &overloaded) { + TypeIndex method_list_idx = overloaded.MethodList; + + CVType method_list_type = m_index.tpi().getType(method_list_idx); + assert(method_list_type.kind() == LF_METHODLIST); + + MethodOverloadListRecord method_list; + llvm::cantFail(TypeDeserializer::deserializeAs<MethodOverloadListRecord>( + method_list_type, method_list)); + + for (const OneMethodRecord &method : method_list.Methods) + AddMethod(overloaded.Name, method.Type, method.getAccess(), + method.getOptions(), method.Attrs); + + return Error::success(); +} + +Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr, + EnumeratorRecord &enumerator) { + Declaration decl; + llvm::StringRef name = DropNameScope(enumerator.getName()); + + m_ast_builder.clang().AddEnumerationValueToEnumerationType( + m_derived_ct, decl, name.str().c_str(), enumerator.Value); + return Error::success(); +} + +void UdtRecordCompleter::complete() { + // Ensure the correct order for virtual bases. + llvm::stable_sort(m_bases, llvm::less_first()); + + std::vector<std::unique_ptr<clang::CXXBaseSpecifier>> bases; + bases.reserve(m_bases.size()); + for (auto &ib : m_bases) + bases.push_back(std::move(ib.second)); + + TypeSystemClang &clang = m_ast_builder.clang(); + // 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) { + TypeSystemClang::RequireCompleteType( + clang.GetType(type_source_info->getType())); + } + } + + clang.TransferBaseClasses(m_derived_ct.GetOpaqueQualType(), std::move(bases)); + + clang.AddMethodOverridesForCXXRecordType(m_derived_ct.GetOpaqueQualType()); + FinishRecord(); + TypeSystemClang::BuildIndirectFields(m_derived_ct); + TypeSystemClang::CompleteTagDeclarationDefinition(m_derived_ct); + + if (auto *record_decl = llvm::dyn_cast<clang::CXXRecordDecl>(&m_tag_decl)) { + m_ast_builder.GetClangASTImporter().SetRecordLayout(record_decl, m_layout); + } +} + +uint64_t +UdtRecordCompleter::AddMember(TypeSystemClang &clang, Member *field, + uint64_t bit_offset, CompilerType parent_ct, + ClangASTImporter::LayoutInfo &parent_layout, + clang::DeclContext *parent_decl_ctx) { + SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( + clang.GetSymbolFile()->GetBackingSymbolFile()); + clang::FieldDecl *field_decl = nullptr; + uint64_t bit_size = 0; + switch (field->kind) { + case Member::Field: { + field_decl = TypeSystemClang::AddFieldToRecordType( + parent_ct, field->name, m_ast_builder.ToCompilerType(field->qt), + field->access, field->bitfield_width); + bit_size = field->bit_size; + break; + }; + case Member::Struct: + case Member::Union: { + clang::TagTypeKind kind = field->kind == Member::Struct + ? clang::TagTypeKind::Struct + : clang::TagTypeKind::Union; + ClangASTMetadata metadata; + metadata.SetUserID(pdb->anonymous_id); + metadata.SetIsDynamicCXXType(false); + CompilerType record_ct = clang.CreateRecordType( + parent_decl_ctx, OptionalClangModuleID(), lldb::eAccessPublic, "", + llvm::to_underlying(kind), lldb::eLanguageTypeC_plus_plus, &metadata); + TypeSystemClang::StartTagDeclarationDefinition(record_ct); + ClangASTImporter::LayoutInfo layout; + clang::DeclContext *decl_ctx = clang.GetDeclContextForType(record_ct); + for (const auto &member : field->fields) { + uint64_t member_offset = field->kind == Member::Struct + ? member->bit_offset - field->base_offset + : 0; + uint64_t member_bit_size = AddMember(clang, member.get(), member_offset, + record_ct, layout, decl_ctx); + if (field->kind == Member::Struct) + bit_size = std::max(bit_size, member_offset + member_bit_size); + else + bit_size = std::max(bit_size, member_bit_size); + } + layout.bit_size = bit_size; + TypeSystemClang::CompleteTagDeclarationDefinition(record_ct); + clang::RecordDecl *record_decl = clang.GetAsRecordDecl(record_ct); + m_ast_builder.GetClangASTImporter().SetRecordLayout(record_decl, layout); + field_decl = TypeSystemClang::AddFieldToRecordType( + parent_ct, "", record_ct, lldb::eAccessPublic, 0); + // Mark this record decl as completed. + DeclStatus status; + status.resolved = true; + status.uid = pdb->anonymous_id--; + m_decl_to_status.insert({record_decl, status}); + break; + }; + } + // FIXME: Add a PdbSymUid namespace for field list members and update + // the m_uid_to_decl map with this decl. + parent_layout.field_offsets.insert({field_decl, bit_offset}); + return bit_size; +} + +void UdtRecordCompleter::FinishRecord() { + TypeSystemClang &clang = m_ast_builder.clang(); + clang::DeclContext *decl_ctx = + m_ast_builder.GetOrCreateDeclContextForUid(m_id); + m_record.ConstructRecord(); + // Maybe we should check the construsted record size with the size in pdb. If + // they mismatch, it might be pdb has fields info missing. + for (const auto &field : m_record.record.fields) { + AddMember(clang, field.get(), field->bit_offset, m_derived_ct, m_layout, + decl_ctx); + } +} + +void UdtRecordCompleter::Record::CollectMember( + llvm::StringRef name, uint64_t offset, uint64_t field_size, + clang::QualType qt, lldb::AccessType access, uint64_t bitfield_width) { + fields_map[offset].push_back(std::make_unique<Member>( + name, offset, field_size, qt, access, bitfield_width)); + if (start_offset > offset) + start_offset = offset; +} + +void UdtRecordCompleter::Record::ConstructRecord() { + // For anonymous unions in a struct, msvc generated pdb doesn't have the + // entity for that union. So, we need to construct anonymous union and struct + // based on field offsets. The final AST is likely not matching the exact + // original AST, but the memory layout is preseved. + // After we collecting all fields in visitKnownMember, we have all fields in + // increasing offset order in m_fields. Since we are iterating in increase + // offset order, if the current offset is equal to m_start_offset, we insert + // it as direct field of top level record. If the current offset is greater + // than m_start_offset, we should be able to find a field in end_offset_map + // whose end offset is less than or equal to current offset. (if not, it might + // be missing field info. We will ignore the field in this case. e.g. Field A + // starts at 0 with size 4 bytes, and Field B starts at 2 with size 4 bytes. + // Normally, there must be something which ends at/before 2.) Then we will + // append current field to the end of parent record. If parent is struct, we + // can just grow it. If parent is a field, it's a field inside an union. We + // convert it into an anonymous struct containing old field and new field. + + // The end offset to a vector of field/struct that ends at the offset. + std::map<uint64_t, std::vector<Member *>> end_offset_map; + for (auto &pair : fields_map) { + uint64_t offset = pair.first; + auto &fields = pair.second; + lldbassert(offset >= start_offset); + Member *parent = &record; + if (offset > start_offset) { + // Find the field with largest end offset that is <= offset. If it's less + // than offset, it indicates there are padding bytes between end offset + // and offset. + lldbassert(!end_offset_map.empty()); + auto iter = end_offset_map.lower_bound(offset); + if (iter == end_offset_map.end()) + --iter; + else if (iter->first > offset) { + if (iter == end_offset_map.begin()) + continue; + --iter; + } + if (iter->second.empty()) + continue; + parent = iter->second.back(); + iter->second.pop_back(); + } + // If it's a field, then the field is inside a union, so we can safely + // increase its size by converting it to a struct to hold multiple fields. + if (parent->kind == Member::Field) + parent->ConvertToStruct(); + + if (fields.size() == 1) { + uint64_t end_offset = offset + fields.back()->bit_size; + parent->fields.push_back(std::move(fields.back())); + if (parent->kind == Member::Struct) { + end_offset_map[end_offset].push_back(parent); + } else { + lldbassert(parent == &record && + "If parent is union, it must be the top level record."); + end_offset_map[end_offset].push_back(parent->fields.back().get()); + } + } else { + if (parent->kind == Member::Struct) { + parent->fields.push_back(std::make_unique<Member>(Member::Union)); + parent = parent->fields.back().get(); + parent->bit_offset = offset; + } else { + lldbassert(parent == &record && + "If parent is union, it must be the top level record."); + } + for (auto &field : fields) { + int64_t bit_size = field->bit_size; + parent->fields.push_back(std::move(field)); + end_offset_map[offset + bit_size].push_back( + parent->fields.back().get()); + } + } + } +} diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.h new file mode 100644 index 000000000000..e6e91d0f2c3e --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.h @@ -0,0 +1,146 @@ +//===-- UdtRecordCompleter.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_SOURCE_PLUGINS_SYMBOLFILE_NATIVEPDB_UDTRECORDCOMPLETER_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_NATIVEPDB_UDTRECORDCOMPLETER_H + +#include "PdbAstBuilder.h" +#include "PdbSymUid.h" +#include "Plugins/ExpressionParser/Clang/ClangASTImporter.h" +#include "llvm/DebugInfo/CodeView/CVRecord.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h" +#include <optional> + +namespace clang { +class CXXBaseSpecifier; +class QualType; +class TagDecl; +} // namespace clang + +namespace llvm { +namespace pdb { +class TpiStream; +class GlobalsStream; +} +} // namespace llvm + +namespace lldb_private { +class Type; +class CompilerType; +namespace npdb { +class PdbAstBuilder; +class PdbIndex; + +class UdtRecordCompleter : public llvm::codeview::TypeVisitorCallbacks { + using IndexedBase = + std::pair<uint64_t, std::unique_ptr<clang::CXXBaseSpecifier>>; + + union UdtTagRecord { + UdtTagRecord() {} + llvm::codeview::UnionRecord ur; + llvm::codeview::ClassRecord cr; + llvm::codeview::EnumRecord er; + } m_cvr; + + PdbTypeSymId m_id; + CompilerType &m_derived_ct; + clang::TagDecl &m_tag_decl; + PdbAstBuilder &m_ast_builder; + PdbIndex &m_index; + std::vector<IndexedBase> m_bases; + ClangASTImporter::LayoutInfo m_layout; + llvm::DenseMap<clang::Decl *, DeclStatus> &m_decl_to_status; + llvm::DenseMap<lldb::opaque_compiler_type_t, + llvm::SmallSet<std::pair<llvm::StringRef, CompilerType>, 8>> + &m_cxx_record_map; + +public: + UdtRecordCompleter( + PdbTypeSymId id, CompilerType &derived_ct, clang::TagDecl &tag_decl, + PdbAstBuilder &ast_builder, PdbIndex &index, + llvm::DenseMap<clang::Decl *, DeclStatus> &decl_to_status, + llvm::DenseMap<lldb::opaque_compiler_type_t, + llvm::SmallSet<std::pair<llvm::StringRef, CompilerType>, + 8>> &cxx_record_map); + +#define MEMBER_RECORD(EnumName, EnumVal, Name) \ + llvm::Error visitKnownMember(llvm::codeview::CVMemberRecord &CVR, \ + llvm::codeview::Name##Record &Record) override; +#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) +#include "llvm/DebugInfo/CodeView/CodeViewTypes.def" + + struct Member; + using MemberUP = std::unique_ptr<Member>; + + struct Member { + enum Kind { Field, Struct, Union } kind; + // Following are only used for field. + llvm::StringRef name; + uint64_t bit_offset; + uint64_t bit_size; + clang::QualType qt; + lldb::AccessType access; + uint32_t bitfield_width; + // Following are Only used for struct or union. + uint64_t base_offset; + llvm::SmallVector<MemberUP, 1> fields; + + Member() = default; + Member(Kind kind) + : kind(kind), name(), bit_offset(0), bit_size(0), qt(), + access(lldb::eAccessPublic), bitfield_width(0), base_offset(0) {} + Member(llvm::StringRef name, uint64_t bit_offset, uint64_t bit_size, + clang::QualType qt, lldb::AccessType access, uint32_t bitfield_width) + : kind(Field), name(name), bit_offset(bit_offset), bit_size(bit_size), + qt(qt), access(access), bitfield_width(bitfield_width), + base_offset(0) {} + void ConvertToStruct() { + kind = Struct; + base_offset = bit_offset; + fields.push_back(std::make_unique<Member>(name, bit_offset, bit_size, qt, + access, bitfield_width)); + name = llvm::StringRef(); + qt = clang::QualType(); + access = lldb::eAccessPublic; + bit_offset = bit_size = bitfield_width = 0; + } + }; + + struct Record { + // Top level record. + Member record; + uint64_t start_offset = UINT64_MAX; + std::map<uint64_t, llvm::SmallVector<MemberUP, 1>> fields_map; + void CollectMember(llvm::StringRef name, uint64_t offset, + uint64_t field_size, clang::QualType qt, + lldb::AccessType access, uint64_t bitfield_width); + void ConstructRecord(); + }; + void complete(); + +private: + Record m_record; + clang::QualType AddBaseClassForTypeIndex( + llvm::codeview::TypeIndex ti, llvm::codeview::MemberAccess access, + std::optional<uint64_t> vtable_idx = std::optional<uint64_t>()); + void AddMethod(llvm::StringRef name, llvm::codeview::TypeIndex type_idx, + llvm::codeview::MemberAccess access, + llvm::codeview::MethodOptions options, + llvm::codeview::MemberAttributes attrs); + void FinishRecord(); + uint64_t AddMember(TypeSystemClang &clang, Member *field, uint64_t bit_offset, + CompilerType parent_ct, + ClangASTImporter::LayoutInfo &parent_layout, + clang::DeclContext *decl_ctx); +}; + +} // namespace npdb +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_SYMBOLFILE_NATIVEPDB_UDTRECORDCOMPLETER_H diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/PDBASTParser.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/PDBASTParser.cpp new file mode 100644 index 000000000000..d656ca3facf7 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/PDBASTParser.cpp @@ -0,0 +1,1455 @@ +//===-- PDBASTParser.cpp --------------------------------------------------===// +// +// 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 "PDBASTParser.h" + +#include "SymbolFilePDB.h" + +#include "clang/AST/CharUnits.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" + +#include "Plugins/ExpressionParser/Clang/ClangASTMetadata.h" +#include "Plugins/ExpressionParser/Clang/ClangUtil.h" +#include "Plugins/TypeSystem/Clang/TypeSystemClang.h" +#include "lldb/Core/Declaration.h" +#include "lldb/Core/Module.h" +#include "lldb/Symbol/SymbolFile.h" +#include "lldb/Symbol/TypeMap.h" +#include "lldb/Symbol/TypeSystem.h" +#include "lldb/Utility/LLDBLog.h" +#include "llvm/DebugInfo/PDB/ConcreteSymbolEnumerator.h" +#include "llvm/DebugInfo/PDB/IPDBLineNumber.h" +#include "llvm/DebugInfo/PDB/IPDBSourceFile.h" +#include "llvm/DebugInfo/PDB/PDBSymbol.h" +#include "llvm/DebugInfo/PDB/PDBSymbolData.h" +#include "llvm/DebugInfo/PDB/PDBSymbolFunc.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeArray.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeBaseClass.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeBuiltin.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeFunctionArg.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeFunctionSig.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypePointer.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeTypedef.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h" + +#include "Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.h" +#include <optional> + +using namespace lldb; +using namespace lldb_private; +using namespace llvm::pdb; + +static int TranslateUdtKind(PDB_UdtType pdb_kind) { + switch (pdb_kind) { + case PDB_UdtType::Class: + return llvm::to_underlying(clang::TagTypeKind::Class); + case PDB_UdtType::Struct: + return llvm::to_underlying(clang::TagTypeKind::Struct); + case PDB_UdtType::Union: + return llvm::to_underlying(clang::TagTypeKind::Union); + case PDB_UdtType::Interface: + return llvm::to_underlying(clang::TagTypeKind::Interface); + } + llvm_unreachable("unsuported PDB UDT type"); +} + +static lldb::Encoding TranslateBuiltinEncoding(PDB_BuiltinType type) { + switch (type) { + case PDB_BuiltinType::Float: + return lldb::eEncodingIEEE754; + case PDB_BuiltinType::Int: + case PDB_BuiltinType::Long: + case PDB_BuiltinType::Char: + return lldb::eEncodingSint; + case PDB_BuiltinType::Bool: + case PDB_BuiltinType::Char16: + case PDB_BuiltinType::Char32: + case PDB_BuiltinType::UInt: + case PDB_BuiltinType::ULong: + case PDB_BuiltinType::HResult: + case PDB_BuiltinType::WCharT: + return lldb::eEncodingUint; + default: + return lldb::eEncodingInvalid; + } +} + +static lldb::Encoding TranslateEnumEncoding(PDB_VariantType type) { + switch (type) { + case PDB_VariantType::Int8: + case PDB_VariantType::Int16: + case PDB_VariantType::Int32: + case PDB_VariantType::Int64: + return lldb::eEncodingSint; + + case PDB_VariantType::UInt8: + case PDB_VariantType::UInt16: + case PDB_VariantType::UInt32: + case PDB_VariantType::UInt64: + return lldb::eEncodingUint; + + default: + break; + } + + return lldb::eEncodingSint; +} + +static CompilerType +GetBuiltinTypeForPDBEncodingAndBitSize(TypeSystemClang &clang_ast, + const PDBSymbolTypeBuiltin &pdb_type, + Encoding encoding, uint32_t width) { + clang::ASTContext &ast = clang_ast.getASTContext(); + + switch (pdb_type.getBuiltinType()) { + default: + break; + case PDB_BuiltinType::None: + return CompilerType(); + case PDB_BuiltinType::Void: + return clang_ast.GetBasicType(eBasicTypeVoid); + case PDB_BuiltinType::Char: + return clang_ast.GetBasicType(eBasicTypeChar); + case PDB_BuiltinType::Bool: + return clang_ast.GetBasicType(eBasicTypeBool); + case PDB_BuiltinType::Long: + if (width == ast.getTypeSize(ast.LongTy)) + return CompilerType(clang_ast.weak_from_this(), + ast.LongTy.getAsOpaquePtr()); + if (width == ast.getTypeSize(ast.LongLongTy)) + return CompilerType(clang_ast.weak_from_this(), + ast.LongLongTy.getAsOpaquePtr()); + break; + case PDB_BuiltinType::ULong: + if (width == ast.getTypeSize(ast.UnsignedLongTy)) + return CompilerType(clang_ast.weak_from_this(), + ast.UnsignedLongTy.getAsOpaquePtr()); + if (width == ast.getTypeSize(ast.UnsignedLongLongTy)) + return CompilerType(clang_ast.weak_from_this(), + ast.UnsignedLongLongTy.getAsOpaquePtr()); + break; + case PDB_BuiltinType::WCharT: + if (width == ast.getTypeSize(ast.WCharTy)) + return CompilerType(clang_ast.weak_from_this(), + ast.WCharTy.getAsOpaquePtr()); + break; + case PDB_BuiltinType::Char16: + return CompilerType(clang_ast.weak_from_this(), + ast.Char16Ty.getAsOpaquePtr()); + case PDB_BuiltinType::Char32: + return CompilerType(clang_ast.weak_from_this(), + ast.Char32Ty.getAsOpaquePtr()); + case PDB_BuiltinType::Float: + // Note: types `long double` and `double` have same bit size in MSVC and + // there is no information in the PDB to distinguish them. So when falling + // back to default search, the compiler type of `long double` will be + // represented by the one generated for `double`. + break; + } + // If there is no match on PDB_BuiltinType, fall back to default search by + // encoding and width only + return clang_ast.GetBuiltinTypeForEncodingAndBitSize(encoding, width); +} + +static ConstString GetPDBBuiltinTypeName(const PDBSymbolTypeBuiltin &pdb_type, + CompilerType &compiler_type) { + PDB_BuiltinType kind = pdb_type.getBuiltinType(); + switch (kind) { + default: + break; + case PDB_BuiltinType::Currency: + return ConstString("CURRENCY"); + case PDB_BuiltinType::Date: + return ConstString("DATE"); + case PDB_BuiltinType::Variant: + return ConstString("VARIANT"); + case PDB_BuiltinType::Complex: + return ConstString("complex"); + case PDB_BuiltinType::Bitfield: + return ConstString("bitfield"); + case PDB_BuiltinType::BSTR: + return ConstString("BSTR"); + case PDB_BuiltinType::HResult: + return ConstString("HRESULT"); + case PDB_BuiltinType::BCD: + return ConstString("BCD"); + case PDB_BuiltinType::Char16: + return ConstString("char16_t"); + case PDB_BuiltinType::Char32: + return ConstString("char32_t"); + case PDB_BuiltinType::None: + return ConstString("..."); + } + return compiler_type.GetTypeName(); +} + +static bool AddSourceInfoToDecl(const PDBSymbol &symbol, Declaration &decl) { + auto &raw_sym = symbol.getRawSymbol(); + auto first_line_up = raw_sym.getSrcLineOnTypeDefn(); + + if (!first_line_up) { + auto lines_up = symbol.getSession().findLineNumbersByAddress( + raw_sym.getVirtualAddress(), raw_sym.getLength()); + if (!lines_up) + return false; + first_line_up = lines_up->getNext(); + if (!first_line_up) + return false; + } + uint32_t src_file_id = first_line_up->getSourceFileId(); + auto src_file_up = symbol.getSession().getSourceFileById(src_file_id); + if (!src_file_up) + return false; + + FileSpec spec(src_file_up->getFileName()); + decl.SetFile(spec); + decl.SetColumn(first_line_up->getColumnNumber()); + decl.SetLine(first_line_up->getLineNumber()); + return true; +} + +static AccessType TranslateMemberAccess(PDB_MemberAccess access) { + switch (access) { + case PDB_MemberAccess::Private: + return eAccessPrivate; + case PDB_MemberAccess::Protected: + return eAccessProtected; + case PDB_MemberAccess::Public: + return eAccessPublic; + } + return eAccessNone; +} + +static AccessType GetDefaultAccessibilityForUdtKind(PDB_UdtType udt_kind) { + switch (udt_kind) { + case PDB_UdtType::Struct: + case PDB_UdtType::Union: + return eAccessPublic; + case PDB_UdtType::Class: + case PDB_UdtType::Interface: + return eAccessPrivate; + } + llvm_unreachable("unsupported PDB UDT type"); +} + +static AccessType GetAccessibilityForUdt(const PDBSymbolTypeUDT &udt) { + AccessType access = TranslateMemberAccess(udt.getAccess()); + if (access != lldb::eAccessNone || !udt.isNested()) + return access; + + auto parent = udt.getClassParent(); + if (!parent) + return lldb::eAccessNone; + + auto parent_udt = llvm::dyn_cast<PDBSymbolTypeUDT>(parent.get()); + if (!parent_udt) + return lldb::eAccessNone; + + return GetDefaultAccessibilityForUdtKind(parent_udt->getUdtKind()); +} + +static clang::MSInheritanceAttr::Spelling +GetMSInheritance(const PDBSymbolTypeUDT &udt) { + int base_count = 0; + bool has_virtual = false; + + auto bases_enum = udt.findAllChildren<PDBSymbolTypeBaseClass>(); + if (bases_enum) { + while (auto base = bases_enum->getNext()) { + base_count++; + has_virtual |= base->isVirtualBaseClass(); + } + } + + if (has_virtual) + return clang::MSInheritanceAttr::Keyword_virtual_inheritance; + if (base_count > 1) + return clang::MSInheritanceAttr::Keyword_multiple_inheritance; + return clang::MSInheritanceAttr::Keyword_single_inheritance; +} + +static std::unique_ptr<llvm::pdb::PDBSymbol> +GetClassOrFunctionParent(const llvm::pdb::PDBSymbol &symbol) { + const IPDBSession &session = symbol.getSession(); + const IPDBRawSymbol &raw = symbol.getRawSymbol(); + auto tag = symbol.getSymTag(); + + // For items that are nested inside of a class, return the class that it is + // nested inside of. + // Note that only certain items can be nested inside of classes. + switch (tag) { + case PDB_SymType::Function: + case PDB_SymType::Data: + case PDB_SymType::UDT: + case PDB_SymType::Enum: + case PDB_SymType::FunctionSig: + case PDB_SymType::Typedef: + case PDB_SymType::BaseClass: + case PDB_SymType::VTable: { + auto class_parent_id = raw.getClassParentId(); + if (auto class_parent = session.getSymbolById(class_parent_id)) + return class_parent; + break; + } + default: + break; + } + + // Otherwise, if it is nested inside of a function, return the function. + // Note that only certain items can be nested inside of functions. + switch (tag) { + case PDB_SymType::Block: + case PDB_SymType::Data: { + auto lexical_parent_id = raw.getLexicalParentId(); + auto lexical_parent = session.getSymbolById(lexical_parent_id); + if (!lexical_parent) + return nullptr; + + auto lexical_parent_tag = lexical_parent->getSymTag(); + if (lexical_parent_tag == PDB_SymType::Function) + return lexical_parent; + if (lexical_parent_tag == PDB_SymType::Exe) + return nullptr; + + return GetClassOrFunctionParent(*lexical_parent); + } + default: + return nullptr; + } +} + +static clang::NamedDecl * +GetDeclFromContextByName(const clang::ASTContext &ast, + const clang::DeclContext &decl_context, + llvm::StringRef name) { + clang::IdentifierInfo &ident = ast.Idents.get(name); + clang::DeclarationName decl_name = ast.DeclarationNames.getIdentifier(&ident); + clang::DeclContext::lookup_result result = decl_context.lookup(decl_name); + if (result.empty()) + return nullptr; + + return *result.begin(); +} + +static bool IsAnonymousNamespaceName(llvm::StringRef name) { + return name == "`anonymous namespace'" || name == "`anonymous-namespace'"; +} + +static clang::CallingConv TranslateCallingConvention(PDB_CallingConv pdb_cc) { + switch (pdb_cc) { + case llvm::codeview::CallingConvention::NearC: + return clang::CC_C; + case llvm::codeview::CallingConvention::NearStdCall: + return clang::CC_X86StdCall; + case llvm::codeview::CallingConvention::NearFast: + return clang::CC_X86FastCall; + case llvm::codeview::CallingConvention::ThisCall: + return clang::CC_X86ThisCall; + case llvm::codeview::CallingConvention::NearVector: + return clang::CC_X86VectorCall; + case llvm::codeview::CallingConvention::NearPascal: + return clang::CC_X86Pascal; + default: + assert(false && "Unknown calling convention"); + return clang::CC_C; + } +} + +PDBASTParser::PDBASTParser(lldb_private::TypeSystemClang &ast) : m_ast(ast) {} + +PDBASTParser::~PDBASTParser() = default; + +// DebugInfoASTParser interface + +lldb::TypeSP PDBASTParser::CreateLLDBTypeFromPDBType(const PDBSymbol &type) { + Declaration decl; + switch (type.getSymTag()) { + case PDB_SymType::BaseClass: { + auto symbol_file = m_ast.GetSymbolFile(); + if (!symbol_file) + return nullptr; + + auto ty = symbol_file->ResolveTypeUID(type.getRawSymbol().getTypeId()); + return ty ? ty->shared_from_this() : nullptr; + } break; + case PDB_SymType::UDT: { + auto udt = llvm::dyn_cast<PDBSymbolTypeUDT>(&type); + assert(udt); + + // Note that, unnamed UDT being typedef-ed is generated as a UDT symbol + // other than a Typedef symbol in PDB. For example, + // typedef union { short Row; short Col; } Union; + // is generated as a named UDT in PDB: + // union Union { short Row; short Col; } + // Such symbols will be handled here. + + // Some UDT with trival ctor has zero length. Just ignore. + if (udt->getLength() == 0) + return nullptr; + + // Ignore unnamed-tag UDTs. + std::string name = + std::string(MSVCUndecoratedNameParser::DropScope(udt->getName())); + if (name.empty()) + return nullptr; + + auto decl_context = GetDeclContextContainingSymbol(type); + + // Check if such an UDT already exists in the current context. + // This may occur with const or volatile types. There are separate type + // symbols in PDB for types with const or volatile modifiers, but we need + // to create only one declaration for them all. + Type::ResolveState type_resolve_state; + CompilerType clang_type = + m_ast.GetTypeForIdentifier<clang::CXXRecordDecl>(name, decl_context); + if (!clang_type.IsValid()) { + auto access = GetAccessibilityForUdt(*udt); + + auto tag_type_kind = TranslateUdtKind(udt->getUdtKind()); + + ClangASTMetadata metadata; + metadata.SetUserID(type.getSymIndexId()); + metadata.SetIsDynamicCXXType(false); + + clang_type = m_ast.CreateRecordType( + decl_context, OptionalClangModuleID(), access, name, tag_type_kind, + lldb::eLanguageTypeC_plus_plus, &metadata); + assert(clang_type.IsValid()); + + auto record_decl = + m_ast.GetAsCXXRecordDecl(clang_type.GetOpaqueQualType()); + assert(record_decl); + m_uid_to_decl[type.getSymIndexId()] = record_decl; + + auto inheritance_attr = clang::MSInheritanceAttr::CreateImplicit( + m_ast.getASTContext(), GetMSInheritance(*udt)); + record_decl->addAttr(inheritance_attr); + + TypeSystemClang::StartTagDeclarationDefinition(clang_type); + + auto children = udt->findAllChildren(); + if (!children || children->getChildCount() == 0) { + // PDB does not have symbol of forwarder. We assume we get an udt w/o + // any fields. Just complete it at this point. + TypeSystemClang::CompleteTagDeclarationDefinition(clang_type); + + TypeSystemClang::SetHasExternalStorage(clang_type.GetOpaqueQualType(), + false); + + type_resolve_state = Type::ResolveState::Full; + } else { + // Add the type to the forward declarations. It will help us to avoid + // an endless recursion in CompleteTypeFromUdt function. + m_forward_decl_to_uid[record_decl] = type.getSymIndexId(); + + TypeSystemClang::SetHasExternalStorage(clang_type.GetOpaqueQualType(), + true); + + type_resolve_state = Type::ResolveState::Forward; + } + } else + type_resolve_state = Type::ResolveState::Forward; + + if (udt->isConstType()) + clang_type = clang_type.AddConstModifier(); + + if (udt->isVolatileType()) + clang_type = clang_type.AddVolatileModifier(); + + AddSourceInfoToDecl(type, decl); + return m_ast.GetSymbolFile()->MakeType( + type.getSymIndexId(), ConstString(name), udt->getLength(), nullptr, + LLDB_INVALID_UID, lldb_private::Type::eEncodingIsUID, decl, clang_type, + type_resolve_state); + } break; + case PDB_SymType::Enum: { + auto enum_type = llvm::dyn_cast<PDBSymbolTypeEnum>(&type); + assert(enum_type); + + std::string name = + std::string(MSVCUndecoratedNameParser::DropScope(enum_type->getName())); + auto decl_context = GetDeclContextContainingSymbol(type); + uint64_t bytes = enum_type->getLength(); + + // Check if such an enum already exists in the current context + CompilerType ast_enum = + m_ast.GetTypeForIdentifier<clang::EnumDecl>(name, decl_context); + if (!ast_enum.IsValid()) { + auto underlying_type_up = enum_type->getUnderlyingType(); + if (!underlying_type_up) + return nullptr; + + lldb::Encoding encoding = + TranslateBuiltinEncoding(underlying_type_up->getBuiltinType()); + // FIXME: Type of underlying builtin is always `Int`. We correct it with + // the very first enumerator's encoding if any. + auto first_child = enum_type->findOneChild<PDBSymbolData>(); + if (first_child) + encoding = TranslateEnumEncoding(first_child->getValue().Type); + + CompilerType builtin_type; + if (bytes > 0) + builtin_type = GetBuiltinTypeForPDBEncodingAndBitSize( + m_ast, *underlying_type_up, encoding, bytes * 8); + else + builtin_type = m_ast.GetBasicType(eBasicTypeInt); + + // FIXME: PDB does not have information about scoped enumeration (Enum + // Class). Set it false for now. + bool isScoped = false; + + ast_enum = m_ast.CreateEnumerationType(name, decl_context, + OptionalClangModuleID(), decl, + builtin_type, isScoped); + + auto enum_decl = TypeSystemClang::GetAsEnumDecl(ast_enum); + assert(enum_decl); + m_uid_to_decl[type.getSymIndexId()] = enum_decl; + + auto enum_values = enum_type->findAllChildren<PDBSymbolData>(); + if (enum_values) { + while (auto enum_value = enum_values->getNext()) { + if (enum_value->getDataKind() != PDB_DataKind::Constant) + continue; + AddEnumValue(ast_enum, *enum_value); + } + } + + if (TypeSystemClang::StartTagDeclarationDefinition(ast_enum)) + TypeSystemClang::CompleteTagDeclarationDefinition(ast_enum); + } + + if (enum_type->isConstType()) + ast_enum = ast_enum.AddConstModifier(); + + if (enum_type->isVolatileType()) + ast_enum = ast_enum.AddVolatileModifier(); + + AddSourceInfoToDecl(type, decl); + return m_ast.GetSymbolFile()->MakeType( + type.getSymIndexId(), ConstString(name), bytes, nullptr, + LLDB_INVALID_UID, lldb_private::Type::eEncodingIsUID, decl, ast_enum, + lldb_private::Type::ResolveState::Full); + } break; + case PDB_SymType::Typedef: { + auto type_def = llvm::dyn_cast<PDBSymbolTypeTypedef>(&type); + assert(type_def); + + SymbolFile *symbol_file = m_ast.GetSymbolFile(); + if (!symbol_file) + return nullptr; + + lldb_private::Type *target_type = + symbol_file->ResolveTypeUID(type_def->getTypeId()); + if (!target_type) + return nullptr; + + std::string name = + std::string(MSVCUndecoratedNameParser::DropScope(type_def->getName())); + auto decl_ctx = GetDeclContextContainingSymbol(type); + + // Check if such a typedef already exists in the current context + CompilerType ast_typedef = + m_ast.GetTypeForIdentifier<clang::TypedefNameDecl>(name, decl_ctx); + if (!ast_typedef.IsValid()) { + CompilerType target_ast_type = target_type->GetFullCompilerType(); + + ast_typedef = target_ast_type.CreateTypedef( + name.c_str(), m_ast.CreateDeclContext(decl_ctx), 0); + if (!ast_typedef) + return nullptr; + + auto typedef_decl = TypeSystemClang::GetAsTypedefDecl(ast_typedef); + assert(typedef_decl); + m_uid_to_decl[type.getSymIndexId()] = typedef_decl; + } + + if (type_def->isConstType()) + ast_typedef = ast_typedef.AddConstModifier(); + + if (type_def->isVolatileType()) + ast_typedef = ast_typedef.AddVolatileModifier(); + + AddSourceInfoToDecl(type, decl); + std::optional<uint64_t> size; + if (type_def->getLength()) + size = type_def->getLength(); + return m_ast.GetSymbolFile()->MakeType( + type_def->getSymIndexId(), ConstString(name), size, nullptr, + target_type->GetID(), lldb_private::Type::eEncodingIsTypedefUID, decl, + ast_typedef, lldb_private::Type::ResolveState::Full); + } break; + case PDB_SymType::Function: + case PDB_SymType::FunctionSig: { + std::string name; + PDBSymbolTypeFunctionSig *func_sig = nullptr; + if (auto pdb_func = llvm::dyn_cast<PDBSymbolFunc>(&type)) { + if (pdb_func->isCompilerGenerated()) + return nullptr; + + auto sig = pdb_func->getSignature(); + if (!sig) + return nullptr; + func_sig = sig.release(); + // Function type is named. + name = std::string( + MSVCUndecoratedNameParser::DropScope(pdb_func->getName())); + } else if (auto pdb_func_sig = + llvm::dyn_cast<PDBSymbolTypeFunctionSig>(&type)) { + func_sig = const_cast<PDBSymbolTypeFunctionSig *>(pdb_func_sig); + } else + llvm_unreachable("Unexpected PDB symbol!"); + + auto arg_enum = func_sig->getArguments(); + uint32_t num_args = arg_enum->getChildCount(); + std::vector<CompilerType> arg_list; + + bool is_variadic = func_sig->isCVarArgs(); + // Drop last variadic argument. + if (is_variadic) + --num_args; + for (uint32_t arg_idx = 0; arg_idx < num_args; arg_idx++) { + auto arg = arg_enum->getChildAtIndex(arg_idx); + if (!arg) + break; + + SymbolFile *symbol_file = m_ast.GetSymbolFile(); + if (!symbol_file) + return nullptr; + + lldb_private::Type *arg_type = + symbol_file->ResolveTypeUID(arg->getSymIndexId()); + // If there's some error looking up one of the dependent types of this + // function signature, bail. + if (!arg_type) + return nullptr; + CompilerType arg_ast_type = arg_type->GetFullCompilerType(); + arg_list.push_back(arg_ast_type); + } + lldbassert(arg_list.size() <= num_args); + + auto pdb_return_type = func_sig->getReturnType(); + SymbolFile *symbol_file = m_ast.GetSymbolFile(); + if (!symbol_file) + return nullptr; + + lldb_private::Type *return_type = + symbol_file->ResolveTypeUID(pdb_return_type->getSymIndexId()); + // If there's some error looking up one of the dependent types of this + // function signature, bail. + if (!return_type) + return nullptr; + CompilerType return_ast_type = return_type->GetFullCompilerType(); + uint32_t type_quals = 0; + if (func_sig->isConstType()) + type_quals |= clang::Qualifiers::Const; + if (func_sig->isVolatileType()) + type_quals |= clang::Qualifiers::Volatile; + auto cc = TranslateCallingConvention(func_sig->getCallingConvention()); + CompilerType func_sig_ast_type = + m_ast.CreateFunctionType(return_ast_type, arg_list.data(), + arg_list.size(), is_variadic, type_quals, cc); + + AddSourceInfoToDecl(type, decl); + return m_ast.GetSymbolFile()->MakeType( + type.getSymIndexId(), ConstString(name), std::nullopt, nullptr, + LLDB_INVALID_UID, lldb_private::Type::eEncodingIsUID, decl, + func_sig_ast_type, lldb_private::Type::ResolveState::Full); + } break; + case PDB_SymType::ArrayType: { + auto array_type = llvm::dyn_cast<PDBSymbolTypeArray>(&type); + assert(array_type); + uint32_t num_elements = array_type->getCount(); + uint32_t element_uid = array_type->getElementTypeId(); + std::optional<uint64_t> bytes; + if (uint64_t size = array_type->getLength()) + bytes = size; + + SymbolFile *symbol_file = m_ast.GetSymbolFile(); + if (!symbol_file) + return nullptr; + + // If array rank > 0, PDB gives the element type at N=0. So element type + // will parsed in the order N=0, N=1,..., N=rank sequentially. + lldb_private::Type *element_type = symbol_file->ResolveTypeUID(element_uid); + if (!element_type) + return nullptr; + + CompilerType element_ast_type = element_type->GetForwardCompilerType(); + // If element type is UDT, it needs to be complete. + if (TypeSystemClang::IsCXXClassType(element_ast_type) && + !element_ast_type.GetCompleteType()) { + if (TypeSystemClang::StartTagDeclarationDefinition(element_ast_type)) { + TypeSystemClang::CompleteTagDeclarationDefinition(element_ast_type); + } else { + // We are not able to start definition. + return nullptr; + } + } + CompilerType array_ast_type = m_ast.CreateArrayType( + element_ast_type, num_elements, /*is_gnu_vector*/ false); + TypeSP type_sp = m_ast.GetSymbolFile()->MakeType( + array_type->getSymIndexId(), ConstString(), bytes, nullptr, + LLDB_INVALID_UID, lldb_private::Type::eEncodingIsUID, decl, + array_ast_type, lldb_private::Type::ResolveState::Full); + type_sp->SetEncodingType(element_type); + return type_sp; + } break; + case PDB_SymType::BuiltinType: { + auto *builtin_type = llvm::dyn_cast<PDBSymbolTypeBuiltin>(&type); + assert(builtin_type); + PDB_BuiltinType builtin_kind = builtin_type->getBuiltinType(); + if (builtin_kind == PDB_BuiltinType::None) + return nullptr; + + std::optional<uint64_t> bytes; + if (uint64_t size = builtin_type->getLength()) + bytes = size; + Encoding encoding = TranslateBuiltinEncoding(builtin_kind); + CompilerType builtin_ast_type = GetBuiltinTypeForPDBEncodingAndBitSize( + m_ast, *builtin_type, encoding, bytes.value_or(0) * 8); + + if (builtin_type->isConstType()) + builtin_ast_type = builtin_ast_type.AddConstModifier(); + + if (builtin_type->isVolatileType()) + builtin_ast_type = builtin_ast_type.AddVolatileModifier(); + + auto type_name = GetPDBBuiltinTypeName(*builtin_type, builtin_ast_type); + + return m_ast.GetSymbolFile()->MakeType( + builtin_type->getSymIndexId(), type_name, bytes, nullptr, + LLDB_INVALID_UID, lldb_private::Type::eEncodingIsUID, decl, + builtin_ast_type, lldb_private::Type::ResolveState::Full); + } break; + case PDB_SymType::PointerType: { + auto *pointer_type = llvm::dyn_cast<PDBSymbolTypePointer>(&type); + assert(pointer_type); + + SymbolFile *symbol_file = m_ast.GetSymbolFile(); + if (!symbol_file) + return nullptr; + + Type *pointee_type = symbol_file->ResolveTypeUID( + pointer_type->getPointeeType()->getSymIndexId()); + if (!pointee_type) + return nullptr; + + if (pointer_type->isPointerToDataMember() || + pointer_type->isPointerToMemberFunction()) { + auto class_parent_uid = pointer_type->getRawSymbol().getClassParentId(); + auto class_parent_type = symbol_file->ResolveTypeUID(class_parent_uid); + assert(class_parent_type); + + CompilerType pointer_ast_type; + pointer_ast_type = TypeSystemClang::CreateMemberPointerType( + class_parent_type->GetLayoutCompilerType(), + pointee_type->GetForwardCompilerType()); + assert(pointer_ast_type); + + return m_ast.GetSymbolFile()->MakeType( + pointer_type->getSymIndexId(), ConstString(), + pointer_type->getLength(), nullptr, LLDB_INVALID_UID, + lldb_private::Type::eEncodingIsUID, decl, pointer_ast_type, + lldb_private::Type::ResolveState::Forward); + } + + CompilerType pointer_ast_type; + pointer_ast_type = pointee_type->GetFullCompilerType(); + if (pointer_type->isReference()) + pointer_ast_type = pointer_ast_type.GetLValueReferenceType(); + else if (pointer_type->isRValueReference()) + pointer_ast_type = pointer_ast_type.GetRValueReferenceType(); + else + pointer_ast_type = pointer_ast_type.GetPointerType(); + + if (pointer_type->isConstType()) + pointer_ast_type = pointer_ast_type.AddConstModifier(); + + if (pointer_type->isVolatileType()) + pointer_ast_type = pointer_ast_type.AddVolatileModifier(); + + if (pointer_type->isRestrictedType()) + pointer_ast_type = pointer_ast_type.AddRestrictModifier(); + + return m_ast.GetSymbolFile()->MakeType( + pointer_type->getSymIndexId(), ConstString(), pointer_type->getLength(), + nullptr, LLDB_INVALID_UID, lldb_private::Type::eEncodingIsUID, decl, + pointer_ast_type, lldb_private::Type::ResolveState::Full); + } break; + default: + break; + } + return nullptr; +} + +bool PDBASTParser::CompleteTypeFromPDB( + lldb_private::CompilerType &compiler_type) { + if (GetClangASTImporter().CanImport(compiler_type)) + return GetClangASTImporter().CompleteType(compiler_type); + + // Remove the type from the forward declarations to avoid + // an endless recursion for types like a linked list. + clang::CXXRecordDecl *record_decl = + m_ast.GetAsCXXRecordDecl(compiler_type.GetOpaqueQualType()); + auto uid_it = m_forward_decl_to_uid.find(record_decl); + if (uid_it == m_forward_decl_to_uid.end()) + return true; + + auto symbol_file = static_cast<SymbolFilePDB *>( + m_ast.GetSymbolFile()->GetBackingSymbolFile()); + if (!symbol_file) + return false; + + std::unique_ptr<PDBSymbol> symbol = + symbol_file->GetPDBSession().getSymbolById(uid_it->getSecond()); + if (!symbol) + return false; + + m_forward_decl_to_uid.erase(uid_it); + + TypeSystemClang::SetHasExternalStorage(compiler_type.GetOpaqueQualType(), + false); + + switch (symbol->getSymTag()) { + case PDB_SymType::UDT: { + auto udt = llvm::dyn_cast<PDBSymbolTypeUDT>(symbol.get()); + if (!udt) + return false; + + return CompleteTypeFromUDT(*symbol_file, compiler_type, *udt); + } + default: + llvm_unreachable("not a forward clang type decl!"); + } +} + +clang::Decl * +PDBASTParser::GetDeclForSymbol(const llvm::pdb::PDBSymbol &symbol) { + uint32_t sym_id = symbol.getSymIndexId(); + auto it = m_uid_to_decl.find(sym_id); + if (it != m_uid_to_decl.end()) + return it->second; + + auto symbol_file = static_cast<SymbolFilePDB *>( + m_ast.GetSymbolFile()->GetBackingSymbolFile()); + if (!symbol_file) + return nullptr; + + // First of all, check if the symbol is a member of a class. Resolve the full + // class type and return the declaration from the cache if so. + auto tag = symbol.getSymTag(); + if (tag == PDB_SymType::Data || tag == PDB_SymType::Function) { + const IPDBSession &session = symbol.getSession(); + const IPDBRawSymbol &raw = symbol.getRawSymbol(); + + auto class_parent_id = raw.getClassParentId(); + if (std::unique_ptr<PDBSymbol> class_parent = + session.getSymbolById(class_parent_id)) { + auto class_parent_type = symbol_file->ResolveTypeUID(class_parent_id); + if (!class_parent_type) + return nullptr; + + CompilerType class_parent_ct = class_parent_type->GetFullCompilerType(); + + // Look a declaration up in the cache after completing the class + clang::Decl *decl = m_uid_to_decl.lookup(sym_id); + if (decl) + return decl; + + // A declaration was not found in the cache. It means that the symbol + // has the class parent, but the class doesn't have the symbol in its + // children list. + if (auto func = llvm::dyn_cast_or_null<PDBSymbolFunc>(&symbol)) { + // Try to find a class child method with the same RVA and use its + // declaration if found. + if (uint32_t rva = func->getRelativeVirtualAddress()) { + if (std::unique_ptr<ConcreteSymbolEnumerator<PDBSymbolFunc>> + methods_enum = + class_parent->findAllChildren<PDBSymbolFunc>()) { + while (std::unique_ptr<PDBSymbolFunc> method = + methods_enum->getNext()) { + if (method->getRelativeVirtualAddress() == rva) { + decl = m_uid_to_decl.lookup(method->getSymIndexId()); + if (decl) + break; + } + } + } + } + + // If no class methods with the same RVA were found, then create a new + // method. It is possible for template methods. + if (!decl) + decl = AddRecordMethod(*symbol_file, class_parent_ct, *func); + } + + if (decl) + m_uid_to_decl[sym_id] = decl; + + return decl; + } + } + + // If we are here, then the symbol is not belonging to a class and is not + // contained in the cache. So create a declaration for it. + switch (symbol.getSymTag()) { + case PDB_SymType::Data: { + auto data = llvm::dyn_cast<PDBSymbolData>(&symbol); + assert(data); + + auto decl_context = GetDeclContextContainingSymbol(symbol); + assert(decl_context); + + // May be the current context is a class really, but we haven't found + // any class parent. This happens e.g. in the case of class static + // variables - they has two symbols, one is a child of the class when + // another is a child of the exe. So always complete the parent and use + // an existing declaration if possible. + if (auto parent_decl = llvm::dyn_cast_or_null<clang::TagDecl>(decl_context)) + m_ast.GetCompleteDecl(parent_decl); + + std::string name = + std::string(MSVCUndecoratedNameParser::DropScope(data->getName())); + + // Check if the current context already contains the symbol with the name. + clang::Decl *decl = + GetDeclFromContextByName(m_ast.getASTContext(), *decl_context, name); + if (!decl) { + auto type = symbol_file->ResolveTypeUID(data->getTypeId()); + if (!type) + return nullptr; + + decl = m_ast.CreateVariableDeclaration( + decl_context, OptionalClangModuleID(), name.c_str(), + ClangUtil::GetQualType(type->GetLayoutCompilerType())); + } + + m_uid_to_decl[sym_id] = decl; + + return decl; + } + case PDB_SymType::Function: { + auto func = llvm::dyn_cast<PDBSymbolFunc>(&symbol); + assert(func); + + auto decl_context = GetDeclContextContainingSymbol(symbol); + assert(decl_context); + + std::string name = + std::string(MSVCUndecoratedNameParser::DropScope(func->getName())); + + Type *type = symbol_file->ResolveTypeUID(sym_id); + if (!type) + return nullptr; + + auto storage = func->isStatic() ? clang::StorageClass::SC_Static + : clang::StorageClass::SC_None; + + auto decl = m_ast.CreateFunctionDeclaration( + decl_context, OptionalClangModuleID(), name, + type->GetForwardCompilerType(), storage, func->hasInlineAttribute()); + + std::vector<clang::ParmVarDecl *> params; + if (std::unique_ptr<PDBSymbolTypeFunctionSig> sig = func->getSignature()) { + if (std::unique_ptr<ConcreteSymbolEnumerator<PDBSymbolTypeFunctionArg>> + arg_enum = sig->findAllChildren<PDBSymbolTypeFunctionArg>()) { + while (std::unique_ptr<PDBSymbolTypeFunctionArg> arg = + arg_enum->getNext()) { + Type *arg_type = symbol_file->ResolveTypeUID(arg->getTypeId()); + if (!arg_type) + continue; + + clang::ParmVarDecl *param = m_ast.CreateParameterDeclaration( + decl, OptionalClangModuleID(), nullptr, + arg_type->GetForwardCompilerType(), clang::SC_None, true); + if (param) + params.push_back(param); + } + } + } + if (params.size()) + m_ast.SetFunctionParameters(decl, params); + + m_uid_to_decl[sym_id] = decl; + + return decl; + } + default: { + // It's not a variable and not a function, check if it's a type + Type *type = symbol_file->ResolveTypeUID(sym_id); + if (!type) + return nullptr; + + return m_uid_to_decl.lookup(sym_id); + } + } +} + +clang::DeclContext * +PDBASTParser::GetDeclContextForSymbol(const llvm::pdb::PDBSymbol &symbol) { + if (symbol.getSymTag() == PDB_SymType::Function) { + clang::DeclContext *result = + llvm::dyn_cast_or_null<clang::FunctionDecl>(GetDeclForSymbol(symbol)); + + if (result) + m_decl_context_to_uid[result] = symbol.getSymIndexId(); + + return result; + } + + auto symbol_file = static_cast<SymbolFilePDB *>( + m_ast.GetSymbolFile()->GetBackingSymbolFile()); + if (!symbol_file) + return nullptr; + + auto type = symbol_file->ResolveTypeUID(symbol.getSymIndexId()); + if (!type) + return nullptr; + + clang::DeclContext *result = + m_ast.GetDeclContextForType(type->GetForwardCompilerType()); + + if (result) + m_decl_context_to_uid[result] = symbol.getSymIndexId(); + + return result; +} + +clang::DeclContext *PDBASTParser::GetDeclContextContainingSymbol( + const llvm::pdb::PDBSymbol &symbol) { + auto parent = GetClassOrFunctionParent(symbol); + while (parent) { + if (auto parent_context = GetDeclContextForSymbol(*parent)) + return parent_context; + + parent = GetClassOrFunctionParent(*parent); + } + + // We can't find any class or function parent of the symbol. So analyze + // the full symbol name. The symbol may be belonging to a namespace + // or function (or even to a class if it's e.g. a static variable symbol). + + // TODO: Make clang to emit full names for variables in namespaces + // (as MSVC does) + + std::string name(symbol.getRawSymbol().getName()); + MSVCUndecoratedNameParser parser(name); + llvm::ArrayRef<MSVCUndecoratedNameSpecifier> specs = parser.GetSpecifiers(); + if (specs.empty()) + return m_ast.GetTranslationUnitDecl(); + + auto symbol_file = static_cast<SymbolFilePDB *>( + m_ast.GetSymbolFile()->GetBackingSymbolFile()); + if (!symbol_file) + return m_ast.GetTranslationUnitDecl(); + + auto global = symbol_file->GetPDBSession().getGlobalScope(); + if (!global) + return m_ast.GetTranslationUnitDecl(); + + bool has_type_or_function_parent = false; + clang::DeclContext *curr_context = m_ast.GetTranslationUnitDecl(); + for (std::size_t i = 0; i < specs.size() - 1; i++) { + // Check if there is a function or a type with the current context's name. + if (std::unique_ptr<IPDBEnumSymbols> children_enum = global->findChildren( + PDB_SymType::None, specs[i].GetFullName(), NS_CaseSensitive)) { + while (IPDBEnumChildren<PDBSymbol>::ChildTypePtr child = + children_enum->getNext()) { + if (clang::DeclContext *child_context = + GetDeclContextForSymbol(*child)) { + // Note that `GetDeclContextForSymbol' retrieves + // a declaration context for functions and types only, + // so if we are here then `child_context' is guaranteed + // a function or a type declaration context. + has_type_or_function_parent = true; + curr_context = child_context; + } + } + } + + // If there were no functions or types above then retrieve a namespace with + // the current context's name. There can be no namespaces inside a function + // or a type. We check it to avoid fake namespaces such as `__l2': + // `N0::N1::CClass::PrivateFunc::__l2::InnerFuncStruct' + if (!has_type_or_function_parent) { + std::string namespace_name = std::string(specs[i].GetBaseName()); + const char *namespace_name_c_str = + IsAnonymousNamespaceName(namespace_name) ? nullptr + : namespace_name.data(); + clang::NamespaceDecl *namespace_decl = + m_ast.GetUniqueNamespaceDeclaration( + namespace_name_c_str, curr_context, OptionalClangModuleID()); + + m_parent_to_namespaces[curr_context].insert(namespace_decl); + m_namespaces.insert(namespace_decl); + + curr_context = namespace_decl; + } + } + + return curr_context; +} + +void PDBASTParser::ParseDeclsForDeclContext( + const clang::DeclContext *decl_context) { + auto symbol_file = static_cast<SymbolFilePDB *>( + m_ast.GetSymbolFile()->GetBackingSymbolFile()); + if (!symbol_file) + return; + + IPDBSession &session = symbol_file->GetPDBSession(); + auto symbol_up = + session.getSymbolById(m_decl_context_to_uid.lookup(decl_context)); + auto global_up = session.getGlobalScope(); + + PDBSymbol *symbol; + if (symbol_up) + symbol = symbol_up.get(); + else if (global_up) + symbol = global_up.get(); + else + return; + + if (auto children = symbol->findAllChildren()) + while (auto child = children->getNext()) + GetDeclForSymbol(*child); +} + +clang::NamespaceDecl * +PDBASTParser::FindNamespaceDecl(const clang::DeclContext *parent, + llvm::StringRef name) { + NamespacesSet *set; + if (parent) { + auto pit = m_parent_to_namespaces.find(parent); + if (pit == m_parent_to_namespaces.end()) + return nullptr; + + set = &pit->second; + } else { + set = &m_namespaces; + } + assert(set); + + for (clang::NamespaceDecl *namespace_decl : *set) + if (namespace_decl->getName() == name) + return namespace_decl; + + for (clang::NamespaceDecl *namespace_decl : *set) + if (namespace_decl->isAnonymousNamespace()) + return FindNamespaceDecl(namespace_decl, name); + + return nullptr; +} + +bool PDBASTParser::AddEnumValue(CompilerType enum_type, + const PDBSymbolData &enum_value) { + Declaration decl; + Variant v = enum_value.getValue(); + std::string name = + std::string(MSVCUndecoratedNameParser::DropScope(enum_value.getName())); + int64_t raw_value; + switch (v.Type) { + case PDB_VariantType::Int8: + raw_value = v.Value.Int8; + break; + case PDB_VariantType::Int16: + raw_value = v.Value.Int16; + break; + case PDB_VariantType::Int32: + raw_value = v.Value.Int32; + break; + case PDB_VariantType::Int64: + raw_value = v.Value.Int64; + break; + case PDB_VariantType::UInt8: + raw_value = v.Value.UInt8; + break; + case PDB_VariantType::UInt16: + raw_value = v.Value.UInt16; + break; + case PDB_VariantType::UInt32: + raw_value = v.Value.UInt32; + break; + case PDB_VariantType::UInt64: + raw_value = v.Value.UInt64; + break; + default: + return false; + } + CompilerType underlying_type = m_ast.GetEnumerationIntegerType(enum_type); + uint32_t byte_size = m_ast.getASTContext().getTypeSize( + ClangUtil::GetQualType(underlying_type)); + auto enum_constant_decl = m_ast.AddEnumerationValueToEnumerationType( + enum_type, decl, name.c_str(), raw_value, byte_size * 8); + if (!enum_constant_decl) + return false; + + m_uid_to_decl[enum_value.getSymIndexId()] = enum_constant_decl; + + return true; +} + +bool PDBASTParser::CompleteTypeFromUDT( + lldb_private::SymbolFile &symbol_file, + lldb_private::CompilerType &compiler_type, + llvm::pdb::PDBSymbolTypeUDT &udt) { + ClangASTImporter::LayoutInfo layout_info; + layout_info.bit_size = udt.getLength() * 8; + + auto nested_enums = udt.findAllChildren<PDBSymbolTypeUDT>(); + if (nested_enums) + while (auto nested = nested_enums->getNext()) + symbol_file.ResolveTypeUID(nested->getSymIndexId()); + + auto bases_enum = udt.findAllChildren<PDBSymbolTypeBaseClass>(); + if (bases_enum) + AddRecordBases(symbol_file, compiler_type, + TranslateUdtKind(udt.getUdtKind()), *bases_enum, + layout_info); + + auto members_enum = udt.findAllChildren<PDBSymbolData>(); + if (members_enum) + AddRecordMembers(symbol_file, compiler_type, *members_enum, layout_info); + + auto methods_enum = udt.findAllChildren<PDBSymbolFunc>(); + if (methods_enum) + AddRecordMethods(symbol_file, compiler_type, *methods_enum); + + m_ast.AddMethodOverridesForCXXRecordType(compiler_type.GetOpaqueQualType()); + TypeSystemClang::BuildIndirectFields(compiler_type); + TypeSystemClang::CompleteTagDeclarationDefinition(compiler_type); + + clang::CXXRecordDecl *record_decl = + m_ast.GetAsCXXRecordDecl(compiler_type.GetOpaqueQualType()); + if (!record_decl) + return static_cast<bool>(compiler_type); + + GetClangASTImporter().SetRecordLayout(record_decl, layout_info); + + return static_cast<bool>(compiler_type); +} + +void PDBASTParser::AddRecordMembers( + lldb_private::SymbolFile &symbol_file, + lldb_private::CompilerType &record_type, + PDBDataSymbolEnumerator &members_enum, + lldb_private::ClangASTImporter::LayoutInfo &layout_info) { + while (auto member = members_enum.getNext()) { + if (member->isCompilerGenerated()) + continue; + + auto member_name = member->getName(); + + auto member_type = symbol_file.ResolveTypeUID(member->getTypeId()); + if (!member_type) + continue; + + auto member_comp_type = member_type->GetLayoutCompilerType(); + if (!member_comp_type.GetCompleteType()) { + symbol_file.GetObjectFile()->GetModule()->ReportError( + ":: Class '{0}' has a member '{1}' of type '{2}' " + "which does not have a complete definition.", + record_type.GetTypeName().GetCString(), member_name.c_str(), + member_comp_type.GetTypeName().GetCString()); + if (TypeSystemClang::StartTagDeclarationDefinition(member_comp_type)) + TypeSystemClang::CompleteTagDeclarationDefinition(member_comp_type); + } + + auto access = TranslateMemberAccess(member->getAccess()); + + switch (member->getDataKind()) { + case PDB_DataKind::Member: { + auto location_type = member->getLocationType(); + + auto bit_size = member->getLength(); + if (location_type == PDB_LocType::ThisRel) + bit_size *= 8; + + auto decl = TypeSystemClang::AddFieldToRecordType( + record_type, member_name.c_str(), member_comp_type, access, bit_size); + if (!decl) + continue; + + m_uid_to_decl[member->getSymIndexId()] = decl; + + auto offset = member->getOffset() * 8; + if (location_type == PDB_LocType::BitField) + offset += member->getBitPosition(); + + layout_info.field_offsets.insert(std::make_pair(decl, offset)); + + break; + } + case PDB_DataKind::StaticMember: { + auto decl = TypeSystemClang::AddVariableToRecordType( + record_type, member_name.c_str(), member_comp_type, access); + if (!decl) + continue; + + // Static constant members may be a const[expr] declaration. + // Query the symbol's value as the variable initializer if valid. + if (member_comp_type.IsConst()) { + auto value = member->getValue(); + if (value.Type == llvm::pdb::Empty) { + LLDB_LOG(GetLog(LLDBLog::AST), + "Class '{0}' has member '{1}' of type '{2}' with an unknown " + "constant size.", + record_type.GetTypeName(), member_name, + member_comp_type.GetTypeName()); + continue; + } + + clang::QualType qual_type = decl->getType(); + unsigned type_width = m_ast.getASTContext().getIntWidth(qual_type); + unsigned constant_width = value.getBitWidth(); + + if (qual_type->isIntegralOrEnumerationType()) { + if (type_width >= constant_width) { + TypeSystemClang::SetIntegerInitializerForVariable( + decl, value.toAPSInt().extOrTrunc(type_width)); + } else { + LLDB_LOG(GetLog(LLDBLog::AST), + "Class '{0}' has a member '{1}' of type '{2}' ({3} bits) " + "which resolves to a wider constant value ({4} bits). " + "Ignoring constant.", + record_type.GetTypeName(), member_name, + member_comp_type.GetTypeName(), type_width, + constant_width); + } + } else { + switch (member_comp_type.GetBasicTypeEnumeration()) { + case lldb::eBasicTypeFloat: + case lldb::eBasicTypeDouble: + case lldb::eBasicTypeLongDouble: + if (type_width == constant_width) { + TypeSystemClang::SetFloatingInitializerForVariable( + decl, value.toAPFloat()); + decl->setConstexpr(true); + } else { + LLDB_LOG(GetLog(LLDBLog::AST), + "Class '{0}' has a member '{1}' of type '{2}' ({3} " + "bits) which resolves to a constant value of mismatched " + "width ({4} bits). Ignoring constant.", + record_type.GetTypeName(), member_name, + member_comp_type.GetTypeName(), type_width, + constant_width); + } + break; + default: + break; + } + } + } + + m_uid_to_decl[member->getSymIndexId()] = decl; + + break; + } + default: + llvm_unreachable("unsupported PDB data kind"); + } + } +} + +void PDBASTParser::AddRecordBases( + lldb_private::SymbolFile &symbol_file, + lldb_private::CompilerType &record_type, int record_kind, + PDBBaseClassSymbolEnumerator &bases_enum, + lldb_private::ClangASTImporter::LayoutInfo &layout_info) const { + std::vector<std::unique_ptr<clang::CXXBaseSpecifier>> base_classes; + + while (auto base = bases_enum.getNext()) { + auto base_type = symbol_file.ResolveTypeUID(base->getTypeId()); + if (!base_type) + continue; + + auto base_comp_type = base_type->GetFullCompilerType(); + if (!base_comp_type.GetCompleteType()) { + symbol_file.GetObjectFile()->GetModule()->ReportError( + ":: Class '{0}' has a base class '{1}' " + "which does not have a complete definition.", + record_type.GetTypeName().GetCString(), + base_comp_type.GetTypeName().GetCString()); + if (TypeSystemClang::StartTagDeclarationDefinition(base_comp_type)) + TypeSystemClang::CompleteTagDeclarationDefinition(base_comp_type); + } + + auto access = TranslateMemberAccess(base->getAccess()); + + auto is_virtual = base->isVirtualBaseClass(); + + std::unique_ptr<clang::CXXBaseSpecifier> base_spec = + m_ast.CreateBaseClassSpecifier( + base_comp_type.GetOpaqueQualType(), access, is_virtual, + record_kind == llvm::to_underlying(clang::TagTypeKind::Class)); + lldbassert(base_spec); + + base_classes.push_back(std::move(base_spec)); + + if (is_virtual) + continue; + + auto decl = m_ast.GetAsCXXRecordDecl(base_comp_type.GetOpaqueQualType()); + if (!decl) + continue; + + auto offset = clang::CharUnits::fromQuantity(base->getOffset()); + layout_info.base_offsets.insert(std::make_pair(decl, offset)); + } + + m_ast.TransferBaseClasses(record_type.GetOpaqueQualType(), + std::move(base_classes)); +} + +void PDBASTParser::AddRecordMethods(lldb_private::SymbolFile &symbol_file, + lldb_private::CompilerType &record_type, + PDBFuncSymbolEnumerator &methods_enum) { + while (std::unique_ptr<PDBSymbolFunc> method = methods_enum.getNext()) + if (clang::CXXMethodDecl *decl = + AddRecordMethod(symbol_file, record_type, *method)) + m_uid_to_decl[method->getSymIndexId()] = decl; +} + +clang::CXXMethodDecl * +PDBASTParser::AddRecordMethod(lldb_private::SymbolFile &symbol_file, + lldb_private::CompilerType &record_type, + const llvm::pdb::PDBSymbolFunc &method) const { + std::string name = + std::string(MSVCUndecoratedNameParser::DropScope(method.getName())); + + Type *method_type = symbol_file.ResolveTypeUID(method.getSymIndexId()); + // MSVC specific __vecDelDtor. + if (!method_type) + return nullptr; + + CompilerType method_comp_type = method_type->GetFullCompilerType(); + if (!method_comp_type.GetCompleteType()) { + symbol_file.GetObjectFile()->GetModule()->ReportError( + ":: Class '{0}' has a method '{1}' whose type cannot be completed.", + record_type.GetTypeName().GetCString(), + method_comp_type.GetTypeName().GetCString()); + if (TypeSystemClang::StartTagDeclarationDefinition(method_comp_type)) + TypeSystemClang::CompleteTagDeclarationDefinition(method_comp_type); + } + + AccessType access = TranslateMemberAccess(method.getAccess()); + if (access == eAccessNone) + access = eAccessPublic; + + // TODO: get mangled name for the method. + return m_ast.AddMethodToCXXRecordType( + record_type.GetOpaqueQualType(), name.c_str(), + /*mangled_name*/ nullptr, method_comp_type, access, method.isVirtual(), + method.isStatic(), method.hasInlineAttribute(), + /*is_explicit*/ false, // FIXME: Need this field in CodeView. + /*is_attr_used*/ false, + /*is_artificial*/ method.isCompilerGenerated()); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/PDBASTParser.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/PDBASTParser.h new file mode 100644 index 000000000000..06f317f4c4d9 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/PDBASTParser.h @@ -0,0 +1,116 @@ +//===-- PDBASTParser.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_SOURCE_PLUGINS_SYMBOLFILE_PDB_PDBASTPARSER_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_PDB_PDBASTPARSER_H + +#include "lldb/lldb-forward.h" + +#include "Plugins/ExpressionParser/Clang/ClangASTImporter.h" + +class SymbolFilePDB; + +namespace clang { +class CharUnits; +class CXXRecordDecl; +class FieldDecl; +class RecordDecl; +} // namespace clang + +namespace lldb_private { +class TypeSystemClang; +class CompilerType; +} // namespace lldb_private + +namespace llvm { +namespace pdb { +template <typename ChildType> class ConcreteSymbolEnumerator; + +class PDBSymbol; +class PDBSymbolData; +class PDBSymbolFunc; +class PDBSymbolTypeBaseClass; +class PDBSymbolTypeBuiltin; +class PDBSymbolTypeUDT; +} // namespace pdb +} // namespace llvm + +class PDBASTParser { +public: + PDBASTParser(lldb_private::TypeSystemClang &ast); + ~PDBASTParser(); + + lldb::TypeSP CreateLLDBTypeFromPDBType(const llvm::pdb::PDBSymbol &type); + bool CompleteTypeFromPDB(lldb_private::CompilerType &compiler_type); + + clang::Decl *GetDeclForSymbol(const llvm::pdb::PDBSymbol &symbol); + + clang::DeclContext * + GetDeclContextForSymbol(const llvm::pdb::PDBSymbol &symbol); + clang::DeclContext * + GetDeclContextContainingSymbol(const llvm::pdb::PDBSymbol &symbol); + + void ParseDeclsForDeclContext(const clang::DeclContext *decl_context); + + clang::NamespaceDecl *FindNamespaceDecl(const clang::DeclContext *parent, + llvm::StringRef name); + + lldb_private::ClangASTImporter &GetClangASTImporter() { + return m_ast_importer; + } + +private: + typedef llvm::DenseMap<clang::CXXRecordDecl *, lldb::user_id_t> + CXXRecordDeclToUidMap; + typedef llvm::DenseMap<lldb::user_id_t, clang::Decl *> UidToDeclMap; + typedef std::set<clang::NamespaceDecl *> NamespacesSet; + typedef llvm::DenseMap<clang::DeclContext *, NamespacesSet> + ParentToNamespacesMap; + typedef llvm::DenseMap<clang::DeclContext *, lldb::user_id_t> + DeclContextToUidMap; + typedef llvm::pdb::ConcreteSymbolEnumerator<llvm::pdb::PDBSymbolData> + PDBDataSymbolEnumerator; + typedef llvm::pdb::ConcreteSymbolEnumerator<llvm::pdb::PDBSymbolTypeBaseClass> + PDBBaseClassSymbolEnumerator; + typedef llvm::pdb::ConcreteSymbolEnumerator<llvm::pdb::PDBSymbolFunc> + PDBFuncSymbolEnumerator; + + bool AddEnumValue(lldb_private::CompilerType enum_type, + const llvm::pdb::PDBSymbolData &data); + bool CompleteTypeFromUDT(lldb_private::SymbolFile &symbol_file, + lldb_private::CompilerType &compiler_type, + llvm::pdb::PDBSymbolTypeUDT &udt); + void + AddRecordMembers(lldb_private::SymbolFile &symbol_file, + lldb_private::CompilerType &record_type, + PDBDataSymbolEnumerator &members_enum, + lldb_private::ClangASTImporter::LayoutInfo &layout_info); + void + AddRecordBases(lldb_private::SymbolFile &symbol_file, + lldb_private::CompilerType &record_type, int record_kind, + PDBBaseClassSymbolEnumerator &bases_enum, + lldb_private::ClangASTImporter::LayoutInfo &layout_info) const; + void AddRecordMethods(lldb_private::SymbolFile &symbol_file, + lldb_private::CompilerType &record_type, + PDBFuncSymbolEnumerator &methods_enum); + clang::CXXMethodDecl * + AddRecordMethod(lldb_private::SymbolFile &symbol_file, + lldb_private::CompilerType &record_type, + const llvm::pdb::PDBSymbolFunc &method) const; + + lldb_private::TypeSystemClang &m_ast; + lldb_private::ClangASTImporter m_ast_importer; + + CXXRecordDeclToUidMap m_forward_decl_to_uid; + UidToDeclMap m_uid_to_decl; + ParentToNamespacesMap m_parent_to_namespaces; + NamespacesSet m_namespaces; + DeclContextToUidMap m_decl_context_to_uid; +}; + +#endif // LLDB_SOURCE_PLUGINS_SYMBOLFILE_PDB_PDBASTPARSER_H diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/PDBLocationToDWARFExpression.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/PDBLocationToDWARFExpression.cpp new file mode 100644 index 000000000000..95add31385df --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/PDBLocationToDWARFExpression.cpp @@ -0,0 +1,182 @@ +//===-- PDBLocationToDWARFExpression.cpp ----------------------------------===// +// +// 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 "PDBLocationToDWARFExpression.h" + +#include "lldb/Core/Section.h" +#include "lldb/Core/dwarf.h" +#include "lldb/Expression/DWARFExpression.h" +#include "lldb/Symbol/Variable.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/StreamBuffer.h" + +#include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/DebugInfo/PDB/IPDBSession.h" +#include "llvm/DebugInfo/PDB/PDBSymbolData.h" + +#include "Plugins/SymbolFile/NativePDB/CodeViewRegisterMapping.h" +#include "Plugins/SymbolFile/NativePDB/PdbFPOProgramToDWARFExpression.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::npdb; +using namespace lldb_private::dwarf; +using namespace llvm::pdb; + +static std::unique_ptr<IPDBFrameData> +GetCorrespondingFrameData(const IPDBSession &session, + const Variable::RangeList &ranges) { + auto enumFrameData = session.getFrameData(); + if (!enumFrameData) + return nullptr; + + std::unique_ptr<IPDBFrameData> found; + while (auto fd = enumFrameData->getNext()) { + Range<lldb::addr_t, lldb::addr_t> fdRange(fd->getVirtualAddress(), + fd->getLengthBlock()); + + for (size_t i = 0; i < ranges.GetSize(); i++) { + auto range = ranges.GetEntryAtIndex(i); + if (!range) + continue; + + if (!range->DoesIntersect(fdRange)) + continue; + + found = std::move(fd); + + break; + } + } + + return found; +} + +static bool EmitVFrameEvaluationDWARFExpression( + llvm::StringRef program, llvm::Triple::ArchType arch_type, Stream &stream) { + // VFrame value always stored in $TO pseudo-register + return TranslateFPOProgramToDWARFExpression(program, "$T0", arch_type, + stream); +} + +DWARFExpression ConvertPDBLocationToDWARFExpression( + ModuleSP module, const PDBSymbolData &symbol, + const Variable::RangeList &ranges, bool &is_constant) { + is_constant = true; + + if (!module) + return DWARFExpression(); + + const ArchSpec &architecture = module->GetArchitecture(); + llvm::Triple::ArchType arch_type = architecture.GetMachine(); + ByteOrder byte_order = architecture.GetByteOrder(); + uint32_t address_size = architecture.GetAddressByteSize(); + uint32_t byte_size = architecture.GetDataByteSize(); + if (byte_order == eByteOrderInvalid || address_size == 0) + return DWARFExpression(); + + RegisterKind register_kind = eRegisterKindDWARF; + StreamBuffer<32> stream(Stream::eBinary, address_size, byte_order); + switch (symbol.getLocationType()) { + case PDB_LocType::Static: + case PDB_LocType::TLS: { + stream.PutHex8(DW_OP_addr); + + SectionList *section_list = module->GetSectionList(); + if (!section_list) + return DWARFExpression(); + + uint32_t section_id = symbol.getAddressSection(); + + auto section = section_list->FindSectionByID(section_id); + if (!section) + return DWARFExpression(); + + uint32_t offset = symbol.getAddressOffset(); + stream.PutMaxHex64(section->GetFileAddress() + offset, address_size, + byte_order); + + is_constant = false; + + break; + } + case PDB_LocType::RegRel: { + uint32_t reg_num; + auto reg_id = symbol.getRegisterId(); + if (reg_id == llvm::codeview::RegisterId::VFRAME) { + if (auto fd = GetCorrespondingFrameData(symbol.getSession(), ranges)) { + if (EmitVFrameEvaluationDWARFExpression(fd->getProgram(), arch_type, + stream)) { + int32_t offset = symbol.getOffset(); + stream.PutHex8(DW_OP_consts); + stream.PutSLEB128(offset); + stream.PutHex8(DW_OP_plus); + + register_kind = eRegisterKindLLDB; + + is_constant = false; + break; + } + } + + register_kind = eRegisterKindGeneric; + reg_num = LLDB_REGNUM_GENERIC_FP; + } else { + register_kind = eRegisterKindLLDB; + reg_num = GetLLDBRegisterNumber(arch_type, reg_id); + if (reg_num == LLDB_INVALID_REGNUM) + return DWARFExpression(); + } + + if (reg_num > 31) { + stream.PutHex8(DW_OP_bregx); + stream.PutULEB128(reg_num); + } else + stream.PutHex8(DW_OP_breg0 + reg_num); + + int32_t offset = symbol.getOffset(); + stream.PutSLEB128(offset); + + is_constant = false; + + break; + } + case PDB_LocType::Enregistered: { + register_kind = eRegisterKindLLDB; + uint32_t reg_num = GetLLDBRegisterNumber(arch_type, symbol.getRegisterId()); + if (reg_num == LLDB_INVALID_REGNUM) + return DWARFExpression(); + + if (reg_num > 31) { + stream.PutHex8(DW_OP_regx); + stream.PutULEB128(reg_num); + } else + stream.PutHex8(DW_OP_reg0 + reg_num); + + is_constant = false; + + break; + } + case PDB_LocType::Constant: { + Variant value = symbol.getValue(); + stream.PutRawBytes(&value.Value, sizeof(value.Value), + endian::InlHostByteOrder()); + break; + } + default: + return DWARFExpression(); + } + + DataBufferSP buffer = + std::make_shared<DataBufferHeap>(stream.GetData(), stream.GetSize()); + DataExtractor extractor(buffer, byte_order, address_size, byte_size); + DWARFExpression result(extractor); + result.SetRegisterKind(register_kind); + + return result; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/PDBLocationToDWARFExpression.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/PDBLocationToDWARFExpression.h new file mode 100644 index 000000000000..fd0fef03e2c8 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/PDBLocationToDWARFExpression.h @@ -0,0 +1,47 @@ +//===-- PDBLocationToDWARFExpression.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_SOURCE_PLUGINS_SYMBOLFILE_PDB_PDBLOCATIONTODWARFEXPRESSION_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_PDB_PDBLOCATIONTODWARFEXPRESSION_H + +#include "lldb/Core/Module.h" +#include "lldb/Symbol/Variable.h" + +namespace lldb_private { +class DWARFExpression; +} + +namespace llvm { +namespace pdb { +class PDBSymbolData; +} +} // namespace llvm + +/// Converts a location information from a PDB symbol to a DWARF expression +/// +/// \param[in] module +/// The module \a symbol belongs to. +/// +/// \param[in] symbol +/// The symbol with a location information to convert. +/// +/// \param[in] ranges +/// Ranges where this variable is valid. +/// +/// \param[out] is_constant +/// Set to \b true if the result expression is a constant value data, +/// and \b false if it is a DWARF bytecode. +/// +/// \return +/// The DWARF expression corresponding to the location data of \a symbol. +lldb_private::DWARFExpression +ConvertPDBLocationToDWARFExpression(lldb::ModuleSP module, + const llvm::pdb::PDBSymbolData &symbol, + const lldb_private::Variable::RangeList &ranges, + bool &is_constant); +#endif diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp new file mode 100644 index 000000000000..9a282acae91f --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp @@ -0,0 +1,2053 @@ +//===-- SymbolFilePDB.cpp -------------------------------------------------===// +// +// 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 "SymbolFilePDB.h" + +#include "PDBASTParser.h" +#include "PDBLocationToDWARFExpression.h" + +#include "clang/Lex/Lexer.h" + +#include "Plugins/TypeSystem/Clang/TypeSystemClang.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/LineTable.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/SymbolVendor.h" +#include "lldb/Symbol/TypeList.h" +#include "lldb/Symbol/TypeMap.h" +#include "lldb/Symbol/Variable.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/RegularExpression.h" + +#include "llvm/DebugInfo/PDB/ConcreteSymbolEnumerator.h" +#include "llvm/DebugInfo/PDB/GenericError.h" +#include "llvm/DebugInfo/PDB/IPDBDataStream.h" +#include "llvm/DebugInfo/PDB/IPDBEnumChildren.h" +#include "llvm/DebugInfo/PDB/IPDBLineNumber.h" +#include "llvm/DebugInfo/PDB/IPDBSectionContrib.h" +#include "llvm/DebugInfo/PDB/IPDBSourceFile.h" +#include "llvm/DebugInfo/PDB/IPDBTable.h" +#include "llvm/DebugInfo/PDB/PDBSymbol.h" +#include "llvm/DebugInfo/PDB/PDBSymbolBlock.h" +#include "llvm/DebugInfo/PDB/PDBSymbolCompiland.h" +#include "llvm/DebugInfo/PDB/PDBSymbolCompilandDetails.h" +#include "llvm/DebugInfo/PDB/PDBSymbolData.h" +#include "llvm/DebugInfo/PDB/PDBSymbolExe.h" +#include "llvm/DebugInfo/PDB/PDBSymbolFunc.h" +#include "llvm/DebugInfo/PDB/PDBSymbolFuncDebugEnd.h" +#include "llvm/DebugInfo/PDB/PDBSymbolFuncDebugStart.h" +#include "llvm/DebugInfo/PDB/PDBSymbolPublicSymbol.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeBuiltin.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeFunctionSig.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeTypedef.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h" + +#include "Plugins/Language/CPlusPlus/CPlusPlusLanguage.h" +#include "Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.h" +#include "Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h" + +#if defined(_WIN32) +#include "llvm/Config/llvm-config.h" +#include <optional> +#endif + +using namespace lldb; +using namespace lldb_private; +using namespace llvm::pdb; + +LLDB_PLUGIN_DEFINE(SymbolFilePDB) + +char SymbolFilePDB::ID; + +namespace { +lldb::LanguageType TranslateLanguage(PDB_Lang lang) { + switch (lang) { + case PDB_Lang::Cpp: + return lldb::LanguageType::eLanguageTypeC_plus_plus; + case PDB_Lang::C: + return lldb::LanguageType::eLanguageTypeC; + case PDB_Lang::Swift: + return lldb::LanguageType::eLanguageTypeSwift; + case PDB_Lang::Rust: + return lldb::LanguageType::eLanguageTypeRust; + case PDB_Lang::ObjC: + return lldb::LanguageType::eLanguageTypeObjC; + case PDB_Lang::ObjCpp: + return lldb::LanguageType::eLanguageTypeObjC_plus_plus; + default: + return lldb::LanguageType::eLanguageTypeUnknown; + } +} + +bool ShouldAddLine(uint32_t requested_line, uint32_t actual_line, + uint32_t addr_length) { + return ((requested_line == 0 || actual_line == requested_line) && + addr_length > 0); +} +} // namespace + +static bool ShouldUseNativeReader() { +#if defined(_WIN32) +#if LLVM_ENABLE_DIA_SDK + llvm::StringRef use_native = ::getenv("LLDB_USE_NATIVE_PDB_READER"); + if (!use_native.equals_insensitive("on") && + !use_native.equals_insensitive("yes") && + !use_native.equals_insensitive("1") && + !use_native.equals_insensitive("true")) + return false; +#endif +#endif + return true; +} + +void SymbolFilePDB::Initialize() { + if (ShouldUseNativeReader()) { + npdb::SymbolFileNativePDB::Initialize(); + } else { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), CreateInstance, + DebuggerInitialize); + } +} + +void SymbolFilePDB::Terminate() { + if (ShouldUseNativeReader()) { + npdb::SymbolFileNativePDB::Terminate(); + } else { + PluginManager::UnregisterPlugin(CreateInstance); + } +} + +void SymbolFilePDB::DebuggerInitialize(lldb_private::Debugger &debugger) {} + +llvm::StringRef SymbolFilePDB::GetPluginDescriptionStatic() { + return "Microsoft PDB debug symbol file reader."; +} + +lldb_private::SymbolFile * +SymbolFilePDB::CreateInstance(ObjectFileSP objfile_sp) { + return new SymbolFilePDB(std::move(objfile_sp)); +} + +SymbolFilePDB::SymbolFilePDB(lldb::ObjectFileSP objfile_sp) + : SymbolFileCommon(std::move(objfile_sp)), m_session_up(), m_global_scope_up() {} + +SymbolFilePDB::~SymbolFilePDB() = default; + +uint32_t SymbolFilePDB::CalculateAbilities() { + uint32_t abilities = 0; + if (!m_objfile_sp) + return 0; + + if (!m_session_up) { + // Lazily load and match the PDB file, but only do this once. + std::string exePath = m_objfile_sp->GetFileSpec().GetPath(); + auto error = loadDataForEXE(PDB_ReaderType::DIA, llvm::StringRef(exePath), + m_session_up); + if (error) { + llvm::consumeError(std::move(error)); + auto module_sp = m_objfile_sp->GetModule(); + if (!module_sp) + return 0; + // See if any symbol file is specified through `--symfile` option. + FileSpec symfile = module_sp->GetSymbolFileFileSpec(); + if (!symfile) + return 0; + error = loadDataForPDB(PDB_ReaderType::DIA, + llvm::StringRef(symfile.GetPath()), m_session_up); + if (error) { + llvm::consumeError(std::move(error)); + return 0; + } + } + } + if (!m_session_up) + return 0; + + auto enum_tables_up = m_session_up->getEnumTables(); + if (!enum_tables_up) + return 0; + while (auto table_up = enum_tables_up->getNext()) { + if (table_up->getItemCount() == 0) + continue; + auto type = table_up->getTableType(); + switch (type) { + case PDB_TableType::Symbols: + // This table represents a store of symbols with types listed in + // PDBSym_Type + abilities |= (CompileUnits | Functions | Blocks | GlobalVariables | + LocalVariables | VariableTypes); + break; + case PDB_TableType::LineNumbers: + abilities |= LineTables; + break; + default: + break; + } + } + return abilities; +} + +void SymbolFilePDB::InitializeObject() { + lldb::addr_t obj_load_address = + m_objfile_sp->GetBaseAddress().GetFileAddress(); + lldbassert(obj_load_address && obj_load_address != LLDB_INVALID_ADDRESS); + m_session_up->setLoadAddress(obj_load_address); + if (!m_global_scope_up) + m_global_scope_up = m_session_up->getGlobalScope(); + lldbassert(m_global_scope_up.get()); +} + +uint32_t SymbolFilePDB::CalculateNumCompileUnits() { + auto compilands = m_global_scope_up->findAllChildren<PDBSymbolCompiland>(); + if (!compilands) + return 0; + + // The linker could link *.dll (compiland language = LINK), or import + // *.dll. For example, a compiland with name `Import:KERNEL32.dll` could be + // found as a child of the global scope (PDB executable). Usually, such + // compilands contain `thunk` symbols in which we are not interested for + // now. However we still count them in the compiland list. If we perform + // any compiland related activity, like finding symbols through + // llvm::pdb::IPDBSession methods, such compilands will all be searched + // automatically no matter whether we include them or not. + uint32_t compile_unit_count = compilands->getChildCount(); + + // The linker can inject an additional "dummy" compilation unit into the + // PDB. Ignore this special compile unit for our purposes, if it is there. + // It is always the last one. + auto last_compiland_up = compilands->getChildAtIndex(compile_unit_count - 1); + lldbassert(last_compiland_up.get()); + std::string name = last_compiland_up->getName(); + if (name == "* Linker *") + --compile_unit_count; + return compile_unit_count; +} + +void SymbolFilePDB::GetCompileUnitIndex( + const llvm::pdb::PDBSymbolCompiland &pdb_compiland, uint32_t &index) { + auto results_up = m_global_scope_up->findAllChildren<PDBSymbolCompiland>(); + if (!results_up) + return; + auto uid = pdb_compiland.getSymIndexId(); + for (uint32_t cu_idx = 0; cu_idx < GetNumCompileUnits(); ++cu_idx) { + auto compiland_up = results_up->getChildAtIndex(cu_idx); + if (!compiland_up) + continue; + if (compiland_up->getSymIndexId() == uid) { + index = cu_idx; + return; + } + } + index = UINT32_MAX; +} + +std::unique_ptr<llvm::pdb::PDBSymbolCompiland> +SymbolFilePDB::GetPDBCompilandByUID(uint32_t uid) { + return m_session_up->getConcreteSymbolById<PDBSymbolCompiland>(uid); +} + +lldb::CompUnitSP SymbolFilePDB::ParseCompileUnitAtIndex(uint32_t index) { + if (index >= GetNumCompileUnits()) + return CompUnitSP(); + + // Assuming we always retrieve same compilands listed in same order through + // `PDBSymbolExe::findAllChildren` method, otherwise using `index` to get a + // compile unit makes no sense. + auto results = m_global_scope_up->findAllChildren<PDBSymbolCompiland>(); + if (!results) + return CompUnitSP(); + auto compiland_up = results->getChildAtIndex(index); + if (!compiland_up) + return CompUnitSP(); + return ParseCompileUnitForUID(compiland_up->getSymIndexId(), index); +} + +lldb::LanguageType SymbolFilePDB::ParseLanguage(CompileUnit &comp_unit) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + auto compiland_up = GetPDBCompilandByUID(comp_unit.GetID()); + if (!compiland_up) + return lldb::eLanguageTypeUnknown; + auto details = compiland_up->findOneChild<PDBSymbolCompilandDetails>(); + if (!details) + return lldb::eLanguageTypeUnknown; + return TranslateLanguage(details->getLanguage()); +} + +lldb_private::Function * +SymbolFilePDB::ParseCompileUnitFunctionForPDBFunc(const PDBSymbolFunc &pdb_func, + CompileUnit &comp_unit) { + if (FunctionSP result = comp_unit.FindFunctionByUID(pdb_func.getSymIndexId())) + return result.get(); + + auto file_vm_addr = pdb_func.getVirtualAddress(); + if (file_vm_addr == LLDB_INVALID_ADDRESS || file_vm_addr == 0) + return nullptr; + + auto func_length = pdb_func.getLength(); + AddressRange func_range = + AddressRange(file_vm_addr, func_length, + GetObjectFile()->GetModule()->GetSectionList()); + if (!func_range.GetBaseAddress().IsValid()) + return nullptr; + + lldb_private::Type *func_type = ResolveTypeUID(pdb_func.getSymIndexId()); + if (!func_type) + return nullptr; + + user_id_t func_type_uid = pdb_func.getSignatureId(); + + Mangled mangled = GetMangledForPDBFunc(pdb_func); + + FunctionSP func_sp = + std::make_shared<Function>(&comp_unit, pdb_func.getSymIndexId(), + func_type_uid, mangled, func_type, func_range); + + comp_unit.AddFunction(func_sp); + + LanguageType lang = ParseLanguage(comp_unit); + auto type_system_or_err = GetTypeSystemForLanguage(lang); + if (auto err = type_system_or_err.takeError()) { + LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), std::move(err), + "Unable to parse PDBFunc: {0}"); + return nullptr; + } + + auto ts = *type_system_or_err; + TypeSystemClang *clang_type_system = + llvm::dyn_cast_or_null<TypeSystemClang>(ts.get()); + if (!clang_type_system) + return nullptr; + clang_type_system->GetPDBParser()->GetDeclForSymbol(pdb_func); + + return func_sp.get(); +} + +size_t SymbolFilePDB::ParseFunctions(CompileUnit &comp_unit) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + size_t func_added = 0; + auto compiland_up = GetPDBCompilandByUID(comp_unit.GetID()); + if (!compiland_up) + return 0; + auto results_up = compiland_up->findAllChildren<PDBSymbolFunc>(); + if (!results_up) + return 0; + while (auto pdb_func_up = results_up->getNext()) { + auto func_sp = comp_unit.FindFunctionByUID(pdb_func_up->getSymIndexId()); + if (!func_sp) { + if (ParseCompileUnitFunctionForPDBFunc(*pdb_func_up, comp_unit)) + ++func_added; + } + } + return func_added; +} + +bool SymbolFilePDB::ParseLineTable(CompileUnit &comp_unit) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + if (comp_unit.GetLineTable()) + return true; + return ParseCompileUnitLineTable(comp_unit, 0); +} + +bool SymbolFilePDB::ParseDebugMacros(CompileUnit &comp_unit) { + // PDB doesn't contain information about macros + return false; +} + +bool SymbolFilePDB::ParseSupportFiles( + CompileUnit &comp_unit, lldb_private::SupportFileList &support_files) { + + // In theory this is unnecessary work for us, because all of this information + // is easily (and quickly) accessible from DebugInfoPDB, so caching it a + // second time seems like a waste. Unfortunately, there's no good way around + // this short of a moderate refactor since SymbolVendor depends on being able + // to cache this list. + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + auto compiland_up = GetPDBCompilandByUID(comp_unit.GetID()); + if (!compiland_up) + return false; + auto files = m_session_up->getSourceFilesForCompiland(*compiland_up); + if (!files || files->getChildCount() == 0) + return false; + + while (auto file = files->getNext()) { + FileSpec spec(file->getFileName(), FileSpec::Style::windows); + support_files.AppendIfUnique(spec); + } + + return true; +} + +bool SymbolFilePDB::ParseImportedModules( + const lldb_private::SymbolContext &sc, + std::vector<SourceModule> &imported_modules) { + // PDB does not yet support module debug info + return false; +} + +static size_t ParseFunctionBlocksForPDBSymbol( + uint64_t func_file_vm_addr, const llvm::pdb::PDBSymbol *pdb_symbol, + lldb_private::Block *parent_block, bool is_top_parent) { + assert(pdb_symbol && parent_block); + + size_t num_added = 0; + switch (pdb_symbol->getSymTag()) { + case PDB_SymType::Block: + case PDB_SymType::Function: { + Block *block = nullptr; + auto &raw_sym = pdb_symbol->getRawSymbol(); + if (auto *pdb_func = llvm::dyn_cast<PDBSymbolFunc>(pdb_symbol)) { + if (pdb_func->hasNoInlineAttribute()) + break; + if (is_top_parent) + block = parent_block; + else + break; + } else if (llvm::isa<PDBSymbolBlock>(pdb_symbol)) { + auto uid = pdb_symbol->getSymIndexId(); + if (parent_block->FindBlockByID(uid)) + break; + if (raw_sym.getVirtualAddress() < func_file_vm_addr) + break; + + auto block_sp = std::make_shared<Block>(pdb_symbol->getSymIndexId()); + parent_block->AddChild(block_sp); + block = block_sp.get(); + } else + llvm_unreachable("Unexpected PDB symbol!"); + + block->AddRange(Block::Range( + raw_sym.getVirtualAddress() - func_file_vm_addr, raw_sym.getLength())); + block->FinalizeRanges(); + ++num_added; + + auto results_up = pdb_symbol->findAllChildren(); + if (!results_up) + break; + while (auto symbol_up = results_up->getNext()) { + num_added += ParseFunctionBlocksForPDBSymbol( + func_file_vm_addr, symbol_up.get(), block, false); + } + } break; + default: + break; + } + return num_added; +} + +size_t SymbolFilePDB::ParseBlocksRecursive(Function &func) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + size_t num_added = 0; + auto uid = func.GetID(); + auto pdb_func_up = m_session_up->getConcreteSymbolById<PDBSymbolFunc>(uid); + if (!pdb_func_up) + return 0; + Block &parent_block = func.GetBlock(false); + num_added = ParseFunctionBlocksForPDBSymbol( + pdb_func_up->getVirtualAddress(), pdb_func_up.get(), &parent_block, true); + return num_added; +} + +size_t SymbolFilePDB::ParseTypes(CompileUnit &comp_unit) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + + size_t num_added = 0; + auto compiland = GetPDBCompilandByUID(comp_unit.GetID()); + if (!compiland) + return 0; + + auto ParseTypesByTagFn = [&num_added, this](const PDBSymbol &raw_sym) { + std::unique_ptr<IPDBEnumSymbols> results; + PDB_SymType tags_to_search[] = {PDB_SymType::Enum, PDB_SymType::Typedef, + PDB_SymType::UDT}; + for (auto tag : tags_to_search) { + results = raw_sym.findAllChildren(tag); + if (!results || results->getChildCount() == 0) + continue; + while (auto symbol = results->getNext()) { + switch (symbol->getSymTag()) { + case PDB_SymType::Enum: + case PDB_SymType::UDT: + case PDB_SymType::Typedef: + break; + default: + continue; + } + + // This should cause the type to get cached and stored in the `m_types` + // lookup. + if (auto type = ResolveTypeUID(symbol->getSymIndexId())) { + // Resolve the type completely to avoid a completion + // (and so a list change, which causes an iterators invalidation) + // during a TypeList dumping + type->GetFullCompilerType(); + ++num_added; + } + } + } + }; + + ParseTypesByTagFn(*compiland); + + // Also parse global types particularly coming from this compiland. + // Unfortunately, PDB has no compiland information for each global type. We + // have to parse them all. But ensure we only do this once. + static bool parse_all_global_types = false; + if (!parse_all_global_types) { + ParseTypesByTagFn(*m_global_scope_up); + parse_all_global_types = true; + } + return num_added; +} + +size_t +SymbolFilePDB::ParseVariablesForContext(const lldb_private::SymbolContext &sc) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + if (!sc.comp_unit) + return 0; + + size_t num_added = 0; + if (sc.function) { + auto pdb_func = m_session_up->getConcreteSymbolById<PDBSymbolFunc>( + sc.function->GetID()); + if (!pdb_func) + return 0; + + num_added += ParseVariables(sc, *pdb_func); + sc.function->GetBlock(false).SetDidParseVariables(true, true); + } else if (sc.comp_unit) { + auto compiland = GetPDBCompilandByUID(sc.comp_unit->GetID()); + if (!compiland) + return 0; + + if (sc.comp_unit->GetVariableList(false)) + return 0; + + auto results = m_global_scope_up->findAllChildren<PDBSymbolData>(); + if (results && results->getChildCount()) { + while (auto result = results->getNext()) { + auto cu_id = GetCompilandId(*result); + // FIXME: We are not able to determine variable's compile unit. + if (cu_id == 0) + continue; + + if (cu_id == sc.comp_unit->GetID()) + num_added += ParseVariables(sc, *result); + } + } + + // FIXME: A `file static` or `global constant` variable appears both in + // compiland's children and global scope's children with unexpectedly + // different symbol's Id making it ambiguous. + + // FIXME: 'local constant', for example, const char var[] = "abc", declared + // in a function scope, can't be found in PDB. + + // Parse variables in this compiland. + num_added += ParseVariables(sc, *compiland); + } + + return num_added; +} + +lldb_private::Type *SymbolFilePDB::ResolveTypeUID(lldb::user_id_t type_uid) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + auto find_result = m_types.find(type_uid); + if (find_result != m_types.end()) + return find_result->second.get(); + + auto type_system_or_err = + GetTypeSystemForLanguage(lldb::eLanguageTypeC_plus_plus); + if (auto err = type_system_or_err.takeError()) { + LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), std::move(err), + "Unable to ResolveTypeUID: {0}"); + return nullptr; + } + + auto ts = *type_system_or_err; + TypeSystemClang *clang_type_system = + llvm::dyn_cast_or_null<TypeSystemClang>(ts.get()); + if (!clang_type_system) + return nullptr; + PDBASTParser *pdb = clang_type_system->GetPDBParser(); + if (!pdb) + return nullptr; + + auto pdb_type = m_session_up->getSymbolById(type_uid); + if (pdb_type == nullptr) + return nullptr; + + lldb::TypeSP result = pdb->CreateLLDBTypeFromPDBType(*pdb_type); + if (result) { + m_types.insert(std::make_pair(type_uid, result)); + } + return result.get(); +} + +std::optional<SymbolFile::ArrayInfo> SymbolFilePDB::GetDynamicArrayInfoForUID( + lldb::user_id_t type_uid, const lldb_private::ExecutionContext *exe_ctx) { + return std::nullopt; +} + +bool SymbolFilePDB::CompleteType(lldb_private::CompilerType &compiler_type) { + std::lock_guard<std::recursive_mutex> guard( + GetObjectFile()->GetModule()->GetMutex()); + + auto type_system_or_err = + GetTypeSystemForLanguage(lldb::eLanguageTypeC_plus_plus); + if (auto err = type_system_or_err.takeError()) { + LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), std::move(err), + "Unable to get dynamic array info for UID: {0}"); + return false; + } + auto ts = *type_system_or_err; + TypeSystemClang *clang_ast_ctx = + llvm::dyn_cast_or_null<TypeSystemClang>(ts.get()); + + if (!clang_ast_ctx) + return false; + + PDBASTParser *pdb = clang_ast_ctx->GetPDBParser(); + if (!pdb) + return false; + + return pdb->CompleteTypeFromPDB(compiler_type); +} + +lldb_private::CompilerDecl SymbolFilePDB::GetDeclForUID(lldb::user_id_t uid) { + auto type_system_or_err = + GetTypeSystemForLanguage(lldb::eLanguageTypeC_plus_plus); + if (auto err = type_system_or_err.takeError()) { + LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), std::move(err), + "Unable to get decl for UID: {0}"); + return CompilerDecl(); + } + auto ts = *type_system_or_err; + TypeSystemClang *clang_ast_ctx = + llvm::dyn_cast_or_null<TypeSystemClang>(ts.get()); + if (!clang_ast_ctx) + return CompilerDecl(); + + PDBASTParser *pdb = clang_ast_ctx->GetPDBParser(); + if (!pdb) + return CompilerDecl(); + + auto symbol = m_session_up->getSymbolById(uid); + if (!symbol) + return CompilerDecl(); + + auto decl = pdb->GetDeclForSymbol(*symbol); + if (!decl) + return CompilerDecl(); + + return clang_ast_ctx->GetCompilerDecl(decl); +} + +lldb_private::CompilerDeclContext +SymbolFilePDB::GetDeclContextForUID(lldb::user_id_t uid) { + auto type_system_or_err = + GetTypeSystemForLanguage(lldb::eLanguageTypeC_plus_plus); + if (auto err = type_system_or_err.takeError()) { + LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), std::move(err), + "Unable to get DeclContext for UID: {0}"); + return CompilerDeclContext(); + } + + auto ts = *type_system_or_err; + TypeSystemClang *clang_ast_ctx = + llvm::dyn_cast_or_null<TypeSystemClang>(ts.get()); + if (!clang_ast_ctx) + return CompilerDeclContext(); + + PDBASTParser *pdb = clang_ast_ctx->GetPDBParser(); + if (!pdb) + return CompilerDeclContext(); + + auto symbol = m_session_up->getSymbolById(uid); + if (!symbol) + return CompilerDeclContext(); + + auto decl_context = pdb->GetDeclContextForSymbol(*symbol); + if (!decl_context) + return GetDeclContextContainingUID(uid); + + return clang_ast_ctx->CreateDeclContext(decl_context); +} + +lldb_private::CompilerDeclContext +SymbolFilePDB::GetDeclContextContainingUID(lldb::user_id_t uid) { + auto type_system_or_err = + GetTypeSystemForLanguage(lldb::eLanguageTypeC_plus_plus); + if (auto err = type_system_or_err.takeError()) { + LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), std::move(err), + "Unable to get DeclContext containing UID: {0}"); + return CompilerDeclContext(); + } + + auto ts = *type_system_or_err; + TypeSystemClang *clang_ast_ctx = + llvm::dyn_cast_or_null<TypeSystemClang>(ts.get()); + if (!clang_ast_ctx) + return CompilerDeclContext(); + + PDBASTParser *pdb = clang_ast_ctx->GetPDBParser(); + if (!pdb) + return CompilerDeclContext(); + + auto symbol = m_session_up->getSymbolById(uid); + if (!symbol) + return CompilerDeclContext(); + + auto decl_context = pdb->GetDeclContextContainingSymbol(*symbol); + assert(decl_context); + + return clang_ast_ctx->CreateDeclContext(decl_context); +} + +void SymbolFilePDB::ParseDeclsForContext( + lldb_private::CompilerDeclContext decl_ctx) { + auto type_system_or_err = + GetTypeSystemForLanguage(lldb::eLanguageTypeC_plus_plus); + if (auto err = type_system_or_err.takeError()) { + LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), std::move(err), + "Unable to parse decls for context: {0}"); + return; + } + + auto ts = *type_system_or_err; + TypeSystemClang *clang_ast_ctx = + llvm::dyn_cast_or_null<TypeSystemClang>(ts.get()); + if (!clang_ast_ctx) + return; + + PDBASTParser *pdb = clang_ast_ctx->GetPDBParser(); + if (!pdb) + return; + + pdb->ParseDeclsForDeclContext( + static_cast<clang::DeclContext *>(decl_ctx.GetOpaqueDeclContext())); +} + +uint32_t +SymbolFilePDB::ResolveSymbolContext(const lldb_private::Address &so_addr, + SymbolContextItem resolve_scope, + lldb_private::SymbolContext &sc) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + uint32_t resolved_flags = 0; + if (resolve_scope & eSymbolContextCompUnit || + resolve_scope & eSymbolContextVariable || + resolve_scope & eSymbolContextFunction || + resolve_scope & eSymbolContextBlock || + resolve_scope & eSymbolContextLineEntry) { + auto cu_sp = GetCompileUnitContainsAddress(so_addr); + if (!cu_sp) { + if (resolved_flags & eSymbolContextVariable) { + // TODO: Resolve variables + } + return 0; + } + sc.comp_unit = cu_sp.get(); + resolved_flags |= eSymbolContextCompUnit; + lldbassert(sc.module_sp == cu_sp->GetModule()); + } + + if (resolve_scope & eSymbolContextFunction || + resolve_scope & eSymbolContextBlock) { + addr_t file_vm_addr = so_addr.GetFileAddress(); + auto symbol_up = + m_session_up->findSymbolByAddress(file_vm_addr, PDB_SymType::Function); + if (symbol_up) { + auto *pdb_func = llvm::dyn_cast<PDBSymbolFunc>(symbol_up.get()); + assert(pdb_func); + auto func_uid = pdb_func->getSymIndexId(); + sc.function = sc.comp_unit->FindFunctionByUID(func_uid).get(); + if (sc.function == nullptr) + sc.function = + ParseCompileUnitFunctionForPDBFunc(*pdb_func, *sc.comp_unit); + if (sc.function) { + resolved_flags |= eSymbolContextFunction; + if (resolve_scope & eSymbolContextBlock) { + auto block_symbol = m_session_up->findSymbolByAddress( + file_vm_addr, PDB_SymType::Block); + auto block_id = block_symbol ? block_symbol->getSymIndexId() + : sc.function->GetID(); + sc.block = sc.function->GetBlock(true).FindBlockByID(block_id); + if (sc.block) + resolved_flags |= eSymbolContextBlock; + } + } + } + } + + if (resolve_scope & eSymbolContextLineEntry) { + if (auto *line_table = sc.comp_unit->GetLineTable()) { + Address addr(so_addr); + if (line_table->FindLineEntryByAddress(addr, sc.line_entry)) + resolved_flags |= eSymbolContextLineEntry; + } + } + + return resolved_flags; +} + +uint32_t SymbolFilePDB::ResolveSymbolContext( + const lldb_private::SourceLocationSpec &src_location_spec, + SymbolContextItem resolve_scope, lldb_private::SymbolContextList &sc_list) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + const size_t old_size = sc_list.GetSize(); + const FileSpec &file_spec = src_location_spec.GetFileSpec(); + const uint32_t line = src_location_spec.GetLine().value_or(0); + if (resolve_scope & lldb::eSymbolContextCompUnit) { + // Locate all compilation units with line numbers referencing the specified + // file. For example, if `file_spec` is <vector>, then this should return + // all source files and header files that reference <vector>, either + // directly or indirectly. + auto compilands = m_session_up->findCompilandsForSourceFile( + file_spec.GetPath(), PDB_NameSearchFlags::NS_CaseInsensitive); + + if (!compilands) + return 0; + + // For each one, either find its previously parsed data or parse it afresh + // and add it to the symbol context list. + while (auto compiland = compilands->getNext()) { + // If we're not checking inlines, then don't add line information for + // this file unless the FileSpec matches. For inline functions, we don't + // have to match the FileSpec since they could be defined in headers + // other than file specified in FileSpec. + if (!src_location_spec.GetCheckInlines()) { + std::string source_file = compiland->getSourceFileFullPath(); + if (source_file.empty()) + continue; + FileSpec this_spec(source_file, FileSpec::Style::windows); + bool need_full_match = !file_spec.GetDirectory().IsEmpty(); + if (FileSpec::Compare(file_spec, this_spec, need_full_match) != 0) + continue; + } + + SymbolContext sc; + auto cu = ParseCompileUnitForUID(compiland->getSymIndexId()); + if (!cu) + continue; + sc.comp_unit = cu.get(); + sc.module_sp = cu->GetModule(); + + // If we were asked to resolve line entries, add all entries to the line + // table that match the requested line (or all lines if `line` == 0). + if (resolve_scope & (eSymbolContextFunction | eSymbolContextBlock | + eSymbolContextLineEntry)) { + bool has_line_table = ParseCompileUnitLineTable(*sc.comp_unit, line); + + if ((resolve_scope & eSymbolContextLineEntry) && !has_line_table) { + // The query asks for line entries, but we can't get them for the + // compile unit. This is not normal for `line` = 0. So just assert + // it. + assert(line && "Couldn't get all line entries!\n"); + + // Current compiland does not have the requested line. Search next. + continue; + } + + if (resolve_scope & (eSymbolContextFunction | eSymbolContextBlock)) { + if (!has_line_table) + continue; + + auto *line_table = sc.comp_unit->GetLineTable(); + lldbassert(line_table); + + uint32_t num_line_entries = line_table->GetSize(); + // Skip the terminal line entry. + --num_line_entries; + + // If `line `!= 0, see if we can resolve function for each line entry + // in the line table. + for (uint32_t line_idx = 0; line && line_idx < num_line_entries; + ++line_idx) { + if (!line_table->GetLineEntryAtIndex(line_idx, sc.line_entry)) + continue; + + auto file_vm_addr = + sc.line_entry.range.GetBaseAddress().GetFileAddress(); + if (file_vm_addr == LLDB_INVALID_ADDRESS || file_vm_addr == 0) + continue; + + auto symbol_up = m_session_up->findSymbolByAddress( + file_vm_addr, PDB_SymType::Function); + if (symbol_up) { + auto func_uid = symbol_up->getSymIndexId(); + sc.function = sc.comp_unit->FindFunctionByUID(func_uid).get(); + if (sc.function == nullptr) { + auto pdb_func = llvm::dyn_cast<PDBSymbolFunc>(symbol_up.get()); + assert(pdb_func); + sc.function = ParseCompileUnitFunctionForPDBFunc(*pdb_func, + *sc.comp_unit); + } + if (sc.function && (resolve_scope & eSymbolContextBlock)) { + Block &block = sc.function->GetBlock(true); + sc.block = block.FindBlockByID(sc.function->GetID()); + } + } + sc_list.Append(sc); + } + } else if (has_line_table) { + // We can parse line table for the compile unit. But no query to + // resolve function or block. We append `sc` to the list anyway. + sc_list.Append(sc); + } + } else { + // No query for line entry, function or block. But we have a valid + // compile unit, append `sc` to the list. + sc_list.Append(sc); + } + } + } + return sc_list.GetSize() - old_size; +} + +std::string SymbolFilePDB::GetMangledForPDBData(const PDBSymbolData &pdb_data) { + // Cache public names at first + if (m_public_names.empty()) + if (auto result_up = + m_global_scope_up->findAllChildren(PDB_SymType::PublicSymbol)) + while (auto symbol_up = result_up->getNext()) + if (auto addr = symbol_up->getRawSymbol().getVirtualAddress()) + m_public_names[addr] = symbol_up->getRawSymbol().getName(); + + // Look up the name in the cache + return m_public_names.lookup(pdb_data.getVirtualAddress()); +} + +VariableSP SymbolFilePDB::ParseVariableForPDBData( + const lldb_private::SymbolContext &sc, + const llvm::pdb::PDBSymbolData &pdb_data) { + VariableSP var_sp; + uint32_t var_uid = pdb_data.getSymIndexId(); + auto result = m_variables.find(var_uid); + if (result != m_variables.end()) + return result->second; + + ValueType scope = eValueTypeInvalid; + bool is_static_member = false; + bool is_external = false; + bool is_artificial = false; + + switch (pdb_data.getDataKind()) { + case PDB_DataKind::Global: + scope = eValueTypeVariableGlobal; + is_external = true; + break; + case PDB_DataKind::Local: + scope = eValueTypeVariableLocal; + break; + case PDB_DataKind::FileStatic: + scope = eValueTypeVariableStatic; + break; + case PDB_DataKind::StaticMember: + is_static_member = true; + scope = eValueTypeVariableStatic; + break; + case PDB_DataKind::Member: + scope = eValueTypeVariableStatic; + break; + case PDB_DataKind::Param: + scope = eValueTypeVariableArgument; + break; + case PDB_DataKind::Constant: + scope = eValueTypeConstResult; + break; + default: + break; + } + + switch (pdb_data.getLocationType()) { + case PDB_LocType::TLS: + scope = eValueTypeVariableThreadLocal; + break; + case PDB_LocType::RegRel: { + // It is a `this` pointer. + if (pdb_data.getDataKind() == PDB_DataKind::ObjectPtr) { + scope = eValueTypeVariableArgument; + is_artificial = true; + } + } break; + default: + break; + } + + Declaration decl; + if (!is_artificial && !pdb_data.isCompilerGenerated()) { + if (auto lines = pdb_data.getLineNumbers()) { + if (auto first_line = lines->getNext()) { + uint32_t src_file_id = first_line->getSourceFileId(); + auto src_file = m_session_up->getSourceFileById(src_file_id); + if (src_file) { + FileSpec spec(src_file->getFileName()); + decl.SetFile(spec); + decl.SetColumn(first_line->getColumnNumber()); + decl.SetLine(first_line->getLineNumber()); + } + } + } + } + + Variable::RangeList ranges; + SymbolContextScope *context_scope = sc.comp_unit; + if (scope == eValueTypeVariableLocal || scope == eValueTypeVariableArgument) { + if (sc.function) { + Block &function_block = sc.function->GetBlock(true); + Block *block = + function_block.FindBlockByID(pdb_data.getLexicalParentId()); + if (!block) + block = &function_block; + + context_scope = block; + + for (size_t i = 0, num_ranges = block->GetNumRanges(); i < num_ranges; + ++i) { + AddressRange range; + if (!block->GetRangeAtIndex(i, range)) + continue; + + ranges.Append(range.GetBaseAddress().GetFileAddress(), + range.GetByteSize()); + } + } + } + + SymbolFileTypeSP type_sp = + std::make_shared<SymbolFileType>(*this, pdb_data.getTypeId()); + + auto var_name = pdb_data.getName(); + auto mangled = GetMangledForPDBData(pdb_data); + auto mangled_cstr = mangled.empty() ? nullptr : mangled.c_str(); + + bool is_constant; + ModuleSP module_sp = GetObjectFile()->GetModule(); + DWARFExpressionList location(module_sp, + ConvertPDBLocationToDWARFExpression( + module_sp, pdb_data, ranges, is_constant), + nullptr); + + var_sp = std::make_shared<Variable>( + var_uid, var_name.c_str(), mangled_cstr, type_sp, scope, context_scope, + ranges, &decl, location, is_external, is_artificial, is_constant, + is_static_member); + + m_variables.insert(std::make_pair(var_uid, var_sp)); + return var_sp; +} + +size_t +SymbolFilePDB::ParseVariables(const lldb_private::SymbolContext &sc, + const llvm::pdb::PDBSymbol &pdb_symbol, + lldb_private::VariableList *variable_list) { + size_t num_added = 0; + + if (auto pdb_data = llvm::dyn_cast<PDBSymbolData>(&pdb_symbol)) { + VariableListSP local_variable_list_sp; + + auto result = m_variables.find(pdb_data->getSymIndexId()); + if (result != m_variables.end()) { + if (variable_list) + variable_list->AddVariableIfUnique(result->second); + } else { + // Prepare right VariableList for this variable. + if (auto lexical_parent = pdb_data->getLexicalParent()) { + switch (lexical_parent->getSymTag()) { + case PDB_SymType::Exe: + assert(sc.comp_unit); + [[fallthrough]]; + case PDB_SymType::Compiland: { + if (sc.comp_unit) { + local_variable_list_sp = sc.comp_unit->GetVariableList(false); + if (!local_variable_list_sp) { + local_variable_list_sp = std::make_shared<VariableList>(); + sc.comp_unit->SetVariableList(local_variable_list_sp); + } + } + } break; + case PDB_SymType::Block: + case PDB_SymType::Function: { + if (sc.function) { + Block *block = sc.function->GetBlock(true).FindBlockByID( + lexical_parent->getSymIndexId()); + if (block) { + local_variable_list_sp = block->GetBlockVariableList(false); + if (!local_variable_list_sp) { + local_variable_list_sp = std::make_shared<VariableList>(); + block->SetVariableList(local_variable_list_sp); + } + } + } + } break; + default: + break; + } + } + + if (local_variable_list_sp) { + if (auto var_sp = ParseVariableForPDBData(sc, *pdb_data)) { + local_variable_list_sp->AddVariableIfUnique(var_sp); + if (variable_list) + variable_list->AddVariableIfUnique(var_sp); + ++num_added; + PDBASTParser *ast = GetPDBAstParser(); + if (ast) + ast->GetDeclForSymbol(*pdb_data); + } + } + } + } + + if (auto results = pdb_symbol.findAllChildren()) { + while (auto result = results->getNext()) + num_added += ParseVariables(sc, *result, variable_list); + } + + return num_added; +} + +void SymbolFilePDB::FindGlobalVariables( + lldb_private::ConstString name, const CompilerDeclContext &parent_decl_ctx, + uint32_t max_matches, lldb_private::VariableList &variables) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + if (!DeclContextMatchesThisSymbolFile(parent_decl_ctx)) + return; + if (name.IsEmpty()) + return; + + auto results = m_global_scope_up->findAllChildren<PDBSymbolData>(); + if (!results) + return; + + uint32_t matches = 0; + size_t old_size = variables.GetSize(); + while (auto result = results->getNext()) { + auto pdb_data = llvm::dyn_cast<PDBSymbolData>(result.get()); + if (max_matches > 0 && matches >= max_matches) + break; + + SymbolContext sc; + sc.module_sp = m_objfile_sp->GetModule(); + lldbassert(sc.module_sp.get()); + + if (name.GetStringRef() != + MSVCUndecoratedNameParser::DropScope(pdb_data->getName())) + continue; + + sc.comp_unit = ParseCompileUnitForUID(GetCompilandId(*pdb_data)).get(); + // FIXME: We are not able to determine the compile unit. + if (sc.comp_unit == nullptr) + continue; + + if (parent_decl_ctx.IsValid() && + GetDeclContextContainingUID(result->getSymIndexId()) != parent_decl_ctx) + continue; + + ParseVariables(sc, *pdb_data, &variables); + matches = variables.GetSize() - old_size; + } +} + +void SymbolFilePDB::FindGlobalVariables( + const lldb_private::RegularExpression ®ex, uint32_t max_matches, + lldb_private::VariableList &variables) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + if (!regex.IsValid()) + return; + auto results = m_global_scope_up->findAllChildren<PDBSymbolData>(); + if (!results) + return; + + uint32_t matches = 0; + size_t old_size = variables.GetSize(); + while (auto pdb_data = results->getNext()) { + if (max_matches > 0 && matches >= max_matches) + break; + + auto var_name = pdb_data->getName(); + if (var_name.empty()) + continue; + if (!regex.Execute(var_name)) + continue; + SymbolContext sc; + sc.module_sp = m_objfile_sp->GetModule(); + lldbassert(sc.module_sp.get()); + + sc.comp_unit = ParseCompileUnitForUID(GetCompilandId(*pdb_data)).get(); + // FIXME: We are not able to determine the compile unit. + if (sc.comp_unit == nullptr) + continue; + + ParseVariables(sc, *pdb_data, &variables); + matches = variables.GetSize() - old_size; + } +} + +bool SymbolFilePDB::ResolveFunction(const llvm::pdb::PDBSymbolFunc &pdb_func, + bool include_inlines, + lldb_private::SymbolContextList &sc_list) { + lldb_private::SymbolContext sc; + sc.comp_unit = ParseCompileUnitForUID(pdb_func.getCompilandId()).get(); + if (!sc.comp_unit) + return false; + sc.module_sp = sc.comp_unit->GetModule(); + sc.function = ParseCompileUnitFunctionForPDBFunc(pdb_func, *sc.comp_unit); + if (!sc.function) + return false; + + sc_list.Append(sc); + return true; +} + +bool SymbolFilePDB::ResolveFunction(uint32_t uid, bool include_inlines, + lldb_private::SymbolContextList &sc_list) { + auto pdb_func_up = m_session_up->getConcreteSymbolById<PDBSymbolFunc>(uid); + if (!pdb_func_up && !(include_inlines && pdb_func_up->hasInlineAttribute())) + return false; + return ResolveFunction(*pdb_func_up, include_inlines, sc_list); +} + +void SymbolFilePDB::CacheFunctionNames() { + if (!m_func_full_names.IsEmpty()) + return; + + std::map<uint64_t, uint32_t> addr_ids; + + if (auto results_up = m_global_scope_up->findAllChildren<PDBSymbolFunc>()) { + while (auto pdb_func_up = results_up->getNext()) { + if (pdb_func_up->isCompilerGenerated()) + continue; + + auto name = pdb_func_up->getName(); + auto demangled_name = pdb_func_up->getUndecoratedName(); + if (name.empty() && demangled_name.empty()) + continue; + + auto uid = pdb_func_up->getSymIndexId(); + if (!demangled_name.empty() && pdb_func_up->getVirtualAddress()) + addr_ids.insert(std::make_pair(pdb_func_up->getVirtualAddress(), uid)); + + if (auto parent = pdb_func_up->getClassParent()) { + + // PDB have symbols for class/struct methods or static methods in Enum + // Class. We won't bother to check if the parent is UDT or Enum here. + m_func_method_names.Append(ConstString(name), uid); + + // To search a method name, like NS::Class:MemberFunc, LLDB searches + // its base name, i.e. MemberFunc by default. Since PDBSymbolFunc does + // not have information of this, we extract base names and cache them + // by our own effort. + llvm::StringRef basename = MSVCUndecoratedNameParser::DropScope(name); + if (!basename.empty()) + m_func_base_names.Append(ConstString(basename), uid); + else { + m_func_base_names.Append(ConstString(name), uid); + } + + if (!demangled_name.empty()) + m_func_full_names.Append(ConstString(demangled_name), uid); + + } else { + // Handle not-method symbols. + + // The function name might contain namespace, or its lexical scope. + llvm::StringRef basename = MSVCUndecoratedNameParser::DropScope(name); + if (!basename.empty()) + m_func_base_names.Append(ConstString(basename), uid); + else + m_func_base_names.Append(ConstString(name), uid); + + if (name == "main") { + m_func_full_names.Append(ConstString(name), uid); + + if (!demangled_name.empty() && name != demangled_name) { + m_func_full_names.Append(ConstString(demangled_name), uid); + m_func_base_names.Append(ConstString(demangled_name), uid); + } + } else if (!demangled_name.empty()) { + m_func_full_names.Append(ConstString(demangled_name), uid); + } else { + m_func_full_names.Append(ConstString(name), uid); + } + } + } + } + + if (auto results_up = + m_global_scope_up->findAllChildren<PDBSymbolPublicSymbol>()) { + while (auto pub_sym_up = results_up->getNext()) { + if (!pub_sym_up->isFunction()) + continue; + auto name = pub_sym_up->getName(); + if (name.empty()) + continue; + + if (CPlusPlusLanguage::IsCPPMangledName(name.c_str())) { + auto vm_addr = pub_sym_up->getVirtualAddress(); + + // PDB public symbol has mangled name for its associated function. + if (vm_addr && addr_ids.find(vm_addr) != addr_ids.end()) { + // Cache mangled name. + m_func_full_names.Append(ConstString(name), addr_ids[vm_addr]); + } + } + } + } + // Sort them before value searching is working properly + m_func_full_names.Sort(); + m_func_full_names.SizeToFit(); + m_func_method_names.Sort(); + m_func_method_names.SizeToFit(); + m_func_base_names.Sort(); + m_func_base_names.SizeToFit(); +} + +void SymbolFilePDB::FindFunctions( + const lldb_private::Module::LookupInfo &lookup_info, + const lldb_private::CompilerDeclContext &parent_decl_ctx, + bool include_inlines, + lldb_private::SymbolContextList &sc_list) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + ConstString name = lookup_info.GetLookupName(); + FunctionNameType name_type_mask = lookup_info.GetNameTypeMask(); + lldbassert((name_type_mask & eFunctionNameTypeAuto) == 0); + + if (name_type_mask & eFunctionNameTypeFull) + name = lookup_info.GetName(); + + if (name_type_mask == eFunctionNameTypeNone) + return; + if (!DeclContextMatchesThisSymbolFile(parent_decl_ctx)) + return; + if (name.IsEmpty()) + return; + + if (name_type_mask & eFunctionNameTypeFull || + name_type_mask & eFunctionNameTypeBase || + name_type_mask & eFunctionNameTypeMethod) { + CacheFunctionNames(); + + std::set<uint32_t> resolved_ids; + auto ResolveFn = [this, &name, parent_decl_ctx, include_inlines, &sc_list, + &resolved_ids](UniqueCStringMap<uint32_t> &Names) { + std::vector<uint32_t> ids; + if (!Names.GetValues(name, ids)) + return; + + for (uint32_t id : ids) { + if (resolved_ids.find(id) != resolved_ids.end()) + continue; + + if (parent_decl_ctx.IsValid() && + GetDeclContextContainingUID(id) != parent_decl_ctx) + continue; + + if (ResolveFunction(id, include_inlines, sc_list)) + resolved_ids.insert(id); + } + }; + if (name_type_mask & eFunctionNameTypeFull) { + ResolveFn(m_func_full_names); + ResolveFn(m_func_base_names); + ResolveFn(m_func_method_names); + } + if (name_type_mask & eFunctionNameTypeBase) + ResolveFn(m_func_base_names); + if (name_type_mask & eFunctionNameTypeMethod) + ResolveFn(m_func_method_names); + } +} + +void SymbolFilePDB::FindFunctions(const lldb_private::RegularExpression ®ex, + bool include_inlines, + lldb_private::SymbolContextList &sc_list) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + if (!regex.IsValid()) + return; + + CacheFunctionNames(); + + std::set<uint32_t> resolved_ids; + auto ResolveFn = [®ex, include_inlines, &sc_list, &resolved_ids, + this](UniqueCStringMap<uint32_t> &Names) { + std::vector<uint32_t> ids; + if (Names.GetValues(regex, ids)) { + for (auto id : ids) { + if (resolved_ids.find(id) == resolved_ids.end()) + if (ResolveFunction(id, include_inlines, sc_list)) + resolved_ids.insert(id); + } + } + }; + ResolveFn(m_func_full_names); + ResolveFn(m_func_base_names); +} + +void SymbolFilePDB::GetMangledNamesForFunction( + const std::string &scope_qualified_name, + std::vector<lldb_private::ConstString> &mangled_names) {} + +void SymbolFilePDB::AddSymbols(lldb_private::Symtab &symtab) { + std::set<lldb::addr_t> sym_addresses; + for (size_t i = 0; i < symtab.GetNumSymbols(); i++) + sym_addresses.insert(symtab.SymbolAtIndex(i)->GetFileAddress()); + + auto results = m_global_scope_up->findAllChildren<PDBSymbolPublicSymbol>(); + if (!results) + return; + + auto section_list = m_objfile_sp->GetSectionList(); + if (!section_list) + return; + + while (auto pub_symbol = results->getNext()) { + auto section_id = pub_symbol->getAddressSection(); + + auto section = section_list->FindSectionByID(section_id); + if (!section) + continue; + + auto offset = pub_symbol->getAddressOffset(); + + auto file_addr = section->GetFileAddress() + offset; + if (sym_addresses.find(file_addr) != sym_addresses.end()) + continue; + sym_addresses.insert(file_addr); + + auto size = pub_symbol->getLength(); + symtab.AddSymbol( + Symbol(pub_symbol->getSymIndexId(), // symID + pub_symbol->getName().c_str(), // name + pub_symbol->isCode() ? eSymbolTypeCode : eSymbolTypeData, // type + true, // external + false, // is_debug + false, // is_trampoline + false, // is_artificial + section, // section_sp + offset, // value + size, // size + size != 0, // size_is_valid + false, // contains_linker_annotations + 0 // flags + )); + } + + symtab.Finalize(); +} + +void SymbolFilePDB::DumpClangAST(Stream &s) { + auto type_system_or_err = + GetTypeSystemForLanguage(lldb::eLanguageTypeC_plus_plus); + if (auto err = type_system_or_err.takeError()) { + LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), std::move(err), + "Unable to dump ClangAST: {0}"); + return; + } + + auto ts = *type_system_or_err; + TypeSystemClang *clang_type_system = + llvm::dyn_cast_or_null<TypeSystemClang>(ts.get()); + if (!clang_type_system) + return; + clang_type_system->Dump(s.AsRawOstream()); +} + +void SymbolFilePDB::FindTypesByRegex( + const lldb_private::RegularExpression ®ex, uint32_t max_matches, + lldb_private::TypeMap &types) { + // When searching by regex, we need to go out of our way to limit the search + // space as much as possible since this searches EVERYTHING in the PDB, + // manually doing regex comparisons. PDB library isn't optimized for regex + // searches or searches across multiple symbol types at the same time, so the + // best we can do is to search enums, then typedefs, then classes one by one, + // and do a regex comparison against each of them. + PDB_SymType tags_to_search[] = {PDB_SymType::Enum, PDB_SymType::Typedef, + PDB_SymType::UDT}; + std::unique_ptr<IPDBEnumSymbols> results; + + uint32_t matches = 0; + + for (auto tag : tags_to_search) { + results = m_global_scope_up->findAllChildren(tag); + if (!results) + continue; + + while (auto result = results->getNext()) { + if (max_matches > 0 && matches >= max_matches) + break; + + std::string type_name; + if (auto enum_type = llvm::dyn_cast<PDBSymbolTypeEnum>(result.get())) + type_name = enum_type->getName(); + else if (auto typedef_type = + llvm::dyn_cast<PDBSymbolTypeTypedef>(result.get())) + type_name = typedef_type->getName(); + else if (auto class_type = llvm::dyn_cast<PDBSymbolTypeUDT>(result.get())) + type_name = class_type->getName(); + else { + // We're looking only for types that have names. Skip symbols, as well + // as unnamed types such as arrays, pointers, etc. + continue; + } + + if (!regex.Execute(type_name)) + continue; + + // This should cause the type to get cached and stored in the `m_types` + // lookup. + if (!ResolveTypeUID(result->getSymIndexId())) + continue; + + auto iter = m_types.find(result->getSymIndexId()); + if (iter == m_types.end()) + continue; + types.Insert(iter->second); + ++matches; + } + } +} + +void SymbolFilePDB::FindTypes(const lldb_private::TypeQuery &query, + lldb_private::TypeResults &type_results) { + + // Make sure we haven't already searched this SymbolFile before. + if (type_results.AlreadySearched(this)) + return; + + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + + std::unique_ptr<IPDBEnumSymbols> results; + llvm::StringRef basename = query.GetTypeBasename().GetStringRef(); + if (basename.empty()) + return; + results = m_global_scope_up->findAllChildren(PDB_SymType::None); + if (!results) + return; + + while (auto result = results->getNext()) { + + switch (result->getSymTag()) { + case PDB_SymType::Enum: + case PDB_SymType::UDT: + case PDB_SymType::Typedef: + break; + default: + // We're looking only for types that have names. Skip symbols, as well + // as unnamed types such as arrays, pointers, etc. + continue; + } + + if (MSVCUndecoratedNameParser::DropScope( + result->getRawSymbol().getName()) != basename) + continue; + + // This should cause the type to get cached and stored in the `m_types` + // lookup. + if (!ResolveTypeUID(result->getSymIndexId())) + continue; + + auto iter = m_types.find(result->getSymIndexId()); + if (iter == m_types.end()) + continue; + // We resolved a type. Get the fully qualified name to ensure it matches. + ConstString name = iter->second->GetQualifiedName(); + TypeQuery type_match(name.GetStringRef(), TypeQueryOptions::e_exact_match); + if (query.ContextMatches(type_match.GetContextRef())) { + type_results.InsertUnique(iter->second); + if (type_results.Done(query)) + return; + } + } +} + +void SymbolFilePDB::GetTypesForPDBSymbol(const llvm::pdb::PDBSymbol &pdb_symbol, + uint32_t type_mask, + TypeCollection &type_collection) { + bool can_parse = false; + switch (pdb_symbol.getSymTag()) { + case PDB_SymType::ArrayType: + can_parse = ((type_mask & eTypeClassArray) != 0); + break; + case PDB_SymType::BuiltinType: + can_parse = ((type_mask & eTypeClassBuiltin) != 0); + break; + case PDB_SymType::Enum: + can_parse = ((type_mask & eTypeClassEnumeration) != 0); + break; + case PDB_SymType::Function: + case PDB_SymType::FunctionSig: + can_parse = ((type_mask & eTypeClassFunction) != 0); + break; + case PDB_SymType::PointerType: + can_parse = ((type_mask & (eTypeClassPointer | eTypeClassBlockPointer | + eTypeClassMemberPointer)) != 0); + break; + case PDB_SymType::Typedef: + can_parse = ((type_mask & eTypeClassTypedef) != 0); + break; + case PDB_SymType::UDT: { + auto *udt = llvm::dyn_cast<PDBSymbolTypeUDT>(&pdb_symbol); + assert(udt); + can_parse = (udt->getUdtKind() != PDB_UdtType::Interface && + ((type_mask & (eTypeClassClass | eTypeClassStruct | + eTypeClassUnion)) != 0)); + } break; + default: + break; + } + + if (can_parse) { + if (auto *type = ResolveTypeUID(pdb_symbol.getSymIndexId())) { + if (!llvm::is_contained(type_collection, type)) + type_collection.push_back(type); + } + } + + auto results_up = pdb_symbol.findAllChildren(); + while (auto symbol_up = results_up->getNext()) + GetTypesForPDBSymbol(*symbol_up, type_mask, type_collection); +} + +void SymbolFilePDB::GetTypes(lldb_private::SymbolContextScope *sc_scope, + TypeClass type_mask, + lldb_private::TypeList &type_list) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + TypeCollection type_collection; + CompileUnit *cu = + sc_scope ? sc_scope->CalculateSymbolContextCompileUnit() : nullptr; + if (cu) { + auto compiland_up = GetPDBCompilandByUID(cu->GetID()); + if (!compiland_up) + return; + GetTypesForPDBSymbol(*compiland_up, type_mask, type_collection); + } else { + for (uint32_t cu_idx = 0; cu_idx < GetNumCompileUnits(); ++cu_idx) { + auto cu_sp = ParseCompileUnitAtIndex(cu_idx); + if (cu_sp) { + if (auto compiland_up = GetPDBCompilandByUID(cu_sp->GetID())) + GetTypesForPDBSymbol(*compiland_up, type_mask, type_collection); + } + } + } + + for (auto type : type_collection) { + type->GetForwardCompilerType(); + type_list.Insert(type->shared_from_this()); + } +} + +llvm::Expected<lldb::TypeSystemSP> +SymbolFilePDB::GetTypeSystemForLanguage(lldb::LanguageType language) { + auto type_system_or_err = + m_objfile_sp->GetModule()->GetTypeSystemForLanguage(language); + if (type_system_or_err) { + if (auto ts = *type_system_or_err) + ts->SetSymbolFile(this); + } + return type_system_or_err; +} + +PDBASTParser *SymbolFilePDB::GetPDBAstParser() { + auto type_system_or_err = + GetTypeSystemForLanguage(lldb::eLanguageTypeC_plus_plus); + if (auto err = type_system_or_err.takeError()) { + LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), std::move(err), + "Unable to get PDB AST parser: {0}"); + return nullptr; + } + + auto ts = *type_system_or_err; + auto *clang_type_system = + llvm::dyn_cast_or_null<TypeSystemClang>(ts.get()); + if (!clang_type_system) + return nullptr; + + return clang_type_system->GetPDBParser(); +} + +lldb_private::CompilerDeclContext +SymbolFilePDB::FindNamespace(lldb_private::ConstString name, + const CompilerDeclContext &parent_decl_ctx, bool) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + auto type_system_or_err = + GetTypeSystemForLanguage(lldb::eLanguageTypeC_plus_plus); + if (auto err = type_system_or_err.takeError()) { + LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), std::move(err), + "Unable to find namespace {1}: {0}", name.AsCString()); + return CompilerDeclContext(); + } + auto ts = *type_system_or_err; + auto *clang_type_system = + llvm::dyn_cast_or_null<TypeSystemClang>(ts.get()); + if (!clang_type_system) + return CompilerDeclContext(); + + PDBASTParser *pdb = clang_type_system->GetPDBParser(); + if (!pdb) + return CompilerDeclContext(); + + clang::DeclContext *decl_context = nullptr; + if (parent_decl_ctx) + decl_context = static_cast<clang::DeclContext *>( + parent_decl_ctx.GetOpaqueDeclContext()); + + auto namespace_decl = + pdb->FindNamespaceDecl(decl_context, name.GetStringRef()); + if (!namespace_decl) + return CompilerDeclContext(); + + return clang_type_system->CreateDeclContext(namespace_decl); +} + +IPDBSession &SymbolFilePDB::GetPDBSession() { return *m_session_up; } + +const IPDBSession &SymbolFilePDB::GetPDBSession() const { + return *m_session_up; +} + +lldb::CompUnitSP SymbolFilePDB::ParseCompileUnitForUID(uint32_t id, + uint32_t index) { + auto found_cu = m_comp_units.find(id); + if (found_cu != m_comp_units.end()) + return found_cu->second; + + auto compiland_up = GetPDBCompilandByUID(id); + if (!compiland_up) + return CompUnitSP(); + + lldb::LanguageType lang; + auto details = compiland_up->findOneChild<PDBSymbolCompilandDetails>(); + if (!details) + lang = lldb::eLanguageTypeC_plus_plus; + else + lang = TranslateLanguage(details->getLanguage()); + + if (lang == lldb::LanguageType::eLanguageTypeUnknown) + return CompUnitSP(); + + std::string path = compiland_up->getSourceFileFullPath(); + if (path.empty()) + return CompUnitSP(); + + // Don't support optimized code for now, DebugInfoPDB does not return this + // information. + LazyBool optimized = eLazyBoolNo; + auto cu_sp = std::make_shared<CompileUnit>(m_objfile_sp->GetModule(), nullptr, + path.c_str(), id, lang, optimized); + + if (!cu_sp) + return CompUnitSP(); + + m_comp_units.insert(std::make_pair(id, cu_sp)); + if (index == UINT32_MAX) + GetCompileUnitIndex(*compiland_up, index); + lldbassert(index != UINT32_MAX); + SetCompileUnitAtIndex(index, cu_sp); + return cu_sp; +} + +bool SymbolFilePDB::ParseCompileUnitLineTable(CompileUnit &comp_unit, + uint32_t match_line) { + auto compiland_up = GetPDBCompilandByUID(comp_unit.GetID()); + if (!compiland_up) + return false; + + // LineEntry needs the *index* of the file into the list of support files + // returned by ParseCompileUnitSupportFiles. But the underlying SDK gives us + // a globally unique idenfitifier in the namespace of the PDB. So, we have + // to do a mapping so that we can hand out indices. + llvm::DenseMap<uint32_t, uint32_t> index_map; + BuildSupportFileIdToSupportFileIndexMap(*compiland_up, index_map); + auto line_table = std::make_unique<LineTable>(&comp_unit); + + // Find contributions to `compiland` from all source and header files. + auto files = m_session_up->getSourceFilesForCompiland(*compiland_up); + if (!files) + return false; + + // For each source and header file, create a LineSequence for contributions + // to the compiland from that file, and add the sequence. + while (auto file = files->getNext()) { + std::unique_ptr<LineSequence> sequence( + line_table->CreateLineSequenceContainer()); + auto lines = m_session_up->findLineNumbers(*compiland_up, *file); + if (!lines) + continue; + int entry_count = lines->getChildCount(); + + uint64_t prev_addr; + uint32_t prev_length; + uint32_t prev_line; + uint32_t prev_source_idx; + + for (int i = 0; i < entry_count; ++i) { + auto line = lines->getChildAtIndex(i); + + uint64_t lno = line->getLineNumber(); + uint64_t addr = line->getVirtualAddress(); + uint32_t length = line->getLength(); + uint32_t source_id = line->getSourceFileId(); + uint32_t col = line->getColumnNumber(); + uint32_t source_idx = index_map[source_id]; + + // There was a gap between the current entry and the previous entry if + // the addresses don't perfectly line up. + bool is_gap = (i > 0) && (prev_addr + prev_length < addr); + + // Before inserting the current entry, insert a terminal entry at the end + // of the previous entry's address range if the current entry resulted in + // a gap from the previous entry. + if (is_gap && ShouldAddLine(match_line, prev_line, prev_length)) { + line_table->AppendLineEntryToSequence( + sequence.get(), prev_addr + prev_length, prev_line, 0, + prev_source_idx, false, false, false, false, true); + + line_table->InsertSequence(sequence.get()); + sequence = line_table->CreateLineSequenceContainer(); + } + + if (ShouldAddLine(match_line, lno, length)) { + bool is_statement = line->isStatement(); + bool is_prologue = false; + bool is_epilogue = false; + auto func = + m_session_up->findSymbolByAddress(addr, PDB_SymType::Function); + if (func) { + auto prologue = func->findOneChild<PDBSymbolFuncDebugStart>(); + if (prologue) + is_prologue = (addr == prologue->getVirtualAddress()); + + auto epilogue = func->findOneChild<PDBSymbolFuncDebugEnd>(); + if (epilogue) + is_epilogue = (addr == epilogue->getVirtualAddress()); + } + + line_table->AppendLineEntryToSequence(sequence.get(), addr, lno, col, + source_idx, is_statement, false, + is_prologue, is_epilogue, false); + } + + prev_addr = addr; + prev_length = length; + prev_line = lno; + prev_source_idx = source_idx; + } + + if (entry_count > 0 && ShouldAddLine(match_line, prev_line, prev_length)) { + // The end is always a terminal entry, so insert it regardless. + line_table->AppendLineEntryToSequence( + sequence.get(), prev_addr + prev_length, prev_line, 0, + prev_source_idx, false, false, false, false, true); + } + + line_table->InsertSequence(sequence.get()); + } + + if (line_table->GetSize()) { + comp_unit.SetLineTable(line_table.release()); + return true; + } + return false; +} + +void SymbolFilePDB::BuildSupportFileIdToSupportFileIndexMap( + const PDBSymbolCompiland &compiland, + llvm::DenseMap<uint32_t, uint32_t> &index_map) const { + // This is a hack, but we need to convert the source id into an index into + // the support files array. We don't want to do path comparisons to avoid + // basename / full path issues that may or may not even be a problem, so we + // use the globally unique source file identifiers. Ideally we could use the + // global identifiers everywhere, but LineEntry currently assumes indices. + auto source_files = m_session_up->getSourceFilesForCompiland(compiland); + if (!source_files) + return; + + int index = 0; + while (auto file = source_files->getNext()) { + uint32_t source_id = file->getUniqueId(); + index_map[source_id] = index++; + } +} + +lldb::CompUnitSP SymbolFilePDB::GetCompileUnitContainsAddress( + const lldb_private::Address &so_addr) { + lldb::addr_t file_vm_addr = so_addr.GetFileAddress(); + if (file_vm_addr == LLDB_INVALID_ADDRESS || file_vm_addr == 0) + return nullptr; + + // If it is a PDB function's vm addr, this is the first sure bet. + if (auto lines = + m_session_up->findLineNumbersByAddress(file_vm_addr, /*Length=*/1)) { + if (auto first_line = lines->getNext()) + return ParseCompileUnitForUID(first_line->getCompilandId()); + } + + // Otherwise we resort to section contributions. + if (auto sec_contribs = m_session_up->getSectionContribs()) { + while (auto section = sec_contribs->getNext()) { + auto va = section->getVirtualAddress(); + if (file_vm_addr >= va && file_vm_addr < va + section->getLength()) + return ParseCompileUnitForUID(section->getCompilandId()); + } + } + return nullptr; +} + +Mangled +SymbolFilePDB::GetMangledForPDBFunc(const llvm::pdb::PDBSymbolFunc &pdb_func) { + Mangled mangled; + auto func_name = pdb_func.getName(); + auto func_undecorated_name = pdb_func.getUndecoratedName(); + std::string func_decorated_name; + + // Seek from public symbols for non-static function's decorated name if any. + // For static functions, they don't have undecorated names and aren't exposed + // in Public Symbols either. + if (!func_undecorated_name.empty()) { + auto result_up = m_global_scope_up->findChildren( + PDB_SymType::PublicSymbol, func_undecorated_name, + PDB_NameSearchFlags::NS_UndecoratedName); + if (result_up) { + while (auto symbol_up = result_up->getNext()) { + // For a public symbol, it is unique. + lldbassert(result_up->getChildCount() == 1); + if (auto *pdb_public_sym = + llvm::dyn_cast_or_null<PDBSymbolPublicSymbol>( + symbol_up.get())) { + if (pdb_public_sym->isFunction()) { + func_decorated_name = pdb_public_sym->getName(); + break; + } + } + } + } + } + if (!func_decorated_name.empty()) { + mangled.SetMangledName(ConstString(func_decorated_name)); + + // For MSVC, format of C function's decorated name depends on calling + // convention. Unfortunately none of the format is recognized by current + // LLDB. For example, `_purecall` is a __cdecl C function. From PDB, + // `__purecall` is retrieved as both its decorated and undecorated name + // (using PDBSymbolFunc::getUndecoratedName method). However `__purecall` + // string is not treated as mangled in LLDB (neither `?` nor `_Z` prefix). + // Mangled::GetDemangledName method will fail internally and caches an + // empty string as its undecorated name. So we will face a contradiction + // here for the same symbol: + // non-empty undecorated name from PDB + // empty undecorated name from LLDB + if (!func_undecorated_name.empty() && mangled.GetDemangledName().IsEmpty()) + mangled.SetDemangledName(ConstString(func_undecorated_name)); + + // LLDB uses several flags to control how a C++ decorated name is + // undecorated for MSVC. See `safeUndecorateName` in Class Mangled. So the + // yielded name could be different from what we retrieve from + // PDB source unless we also apply same flags in getting undecorated + // name through PDBSymbolFunc::getUndecoratedNameEx method. + if (!func_undecorated_name.empty() && + mangled.GetDemangledName() != ConstString(func_undecorated_name)) + mangled.SetDemangledName(ConstString(func_undecorated_name)); + } else if (!func_undecorated_name.empty()) { + mangled.SetDemangledName(ConstString(func_undecorated_name)); + } else if (!func_name.empty()) + mangled.SetValue(ConstString(func_name)); + + return mangled; +} + +bool SymbolFilePDB::DeclContextMatchesThisSymbolFile( + const lldb_private::CompilerDeclContext &decl_ctx) { + if (!decl_ctx.IsValid()) + return true; + + TypeSystem *decl_ctx_type_system = decl_ctx.GetTypeSystem(); + if (!decl_ctx_type_system) + return false; + auto type_system_or_err = GetTypeSystemForLanguage( + decl_ctx_type_system->GetMinimumLanguage(nullptr)); + if (auto err = type_system_or_err.takeError()) { + LLDB_LOG_ERROR( + GetLog(LLDBLog::Symbols), std::move(err), + "Unable to determine if DeclContext matches this symbol file: {0}"); + return false; + } + + if (decl_ctx_type_system == type_system_or_err->get()) + return true; // The type systems match, return true + + return false; +} + +uint32_t SymbolFilePDB::GetCompilandId(const llvm::pdb::PDBSymbolData &data) { + static const auto pred_upper = [](uint32_t lhs, SecContribInfo rhs) { + return lhs < rhs.Offset; + }; + + // Cache section contributions + if (m_sec_contribs.empty()) { + if (auto SecContribs = m_session_up->getSectionContribs()) { + while (auto SectionContrib = SecContribs->getNext()) { + auto comp_id = SectionContrib->getCompilandId(); + if (!comp_id) + continue; + + auto sec = SectionContrib->getAddressSection(); + auto &sec_cs = m_sec_contribs[sec]; + + auto offset = SectionContrib->getAddressOffset(); + auto it = llvm::upper_bound(sec_cs, offset, pred_upper); + + auto size = SectionContrib->getLength(); + sec_cs.insert(it, {offset, size, comp_id}); + } + } + } + + // Check by line number + if (auto Lines = data.getLineNumbers()) { + if (auto FirstLine = Lines->getNext()) + return FirstLine->getCompilandId(); + } + + // Retrieve section + offset + uint32_t DataSection = data.getAddressSection(); + uint32_t DataOffset = data.getAddressOffset(); + if (DataSection == 0) { + if (auto RVA = data.getRelativeVirtualAddress()) + m_session_up->addressForRVA(RVA, DataSection, DataOffset); + } + + if (DataSection) { + // Search by section contributions + auto &sec_cs = m_sec_contribs[DataSection]; + auto it = llvm::upper_bound(sec_cs, DataOffset, pred_upper); + if (it != sec_cs.begin()) { + --it; + if (DataOffset < it->Offset + it->Size) + return it->CompilandId; + } + } else { + // Search in lexical tree + auto LexParentId = data.getLexicalParentId(); + while (auto LexParent = m_session_up->getSymbolById(LexParentId)) { + if (LexParent->getSymTag() == PDB_SymType::Exe) + break; + if (LexParent->getSymTag() == PDB_SymType::Compiland) + return LexParentId; + LexParentId = LexParent->getRawSymbol().getLexicalParentId(); + } + } + + return 0; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.h new file mode 100644 index 000000000000..ea495c575f1f --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.h @@ -0,0 +1,252 @@ +//===-- SymbolFilePDB.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_SOURCE_PLUGINS_SYMBOLFILE_PDB_SYMBOLFILEPDB_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_PDB_SYMBOLFILEPDB_H + +#include "lldb/Core/UniqueCStringMap.h" +#include "lldb/Symbol/SymbolFile.h" +#include "lldb/Symbol/VariableList.h" +#include "lldb/Utility/UserID.h" + +#include "llvm/ADT/DenseMap.h" +#include "llvm/DebugInfo/PDB/IPDBSession.h" +#include "llvm/DebugInfo/PDB/PDB.h" +#include "llvm/DebugInfo/PDB/PDBSymbolExe.h" +#include <optional> + +class PDBASTParser; + +class SymbolFilePDB : public lldb_private::SymbolFileCommon { + /// LLVM RTTI support. + static char ID; + +public: + /// LLVM RTTI support. + /// \{ + bool isA(const void *ClassID) const override { + return ClassID == &ID || SymbolFileCommon::isA(ClassID); + } + static bool classof(const SymbolFile *obj) { return obj->isA(&ID); } + /// \} + + // Static Functions + static void Initialize(); + + static void Terminate(); + + static void DebuggerInitialize(lldb_private::Debugger &debugger); + + static llvm::StringRef GetPluginNameStatic() { return "pdb"; } + + static llvm::StringRef GetPluginDescriptionStatic(); + + static lldb_private::SymbolFile * + CreateInstance(lldb::ObjectFileSP objfile_sp); + + // Constructors and Destructors + SymbolFilePDB(lldb::ObjectFileSP objfile_sp); + + ~SymbolFilePDB() override; + + uint32_t CalculateAbilities() override; + + void InitializeObject() override; + + // Compile Unit function calls + + 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::SupportFileList &support_files) 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; + std::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::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::SourceLocationSpec &src_location_spec, + lldb::SymbolContextItem resolve_scope, + lldb_private::SymbolContextList &sc_list) override; + + void + FindGlobalVariables(lldb_private::ConstString name, + const lldb_private::CompilerDeclContext &parent_decl_ctx, + uint32_t max_matches, + lldb_private::VariableList &variables) override; + + void FindGlobalVariables(const lldb_private::RegularExpression ®ex, + uint32_t max_matches, + lldb_private::VariableList &variables) override; + + void FindFunctions(const lldb_private::Module::LookupInfo &lookup_info, + const lldb_private::CompilerDeclContext &parent_decl_ctx, + bool include_inlines, + lldb_private::SymbolContextList &sc_list) override; + + void FindFunctions(const lldb_private::RegularExpression ®ex, + bool include_inlines, + lldb_private::SymbolContextList &sc_list) override; + + void GetMangledNamesForFunction( + const std::string &scope_qualified_name, + std::vector<lldb_private::ConstString> &mangled_names) override; + + void AddSymbols(lldb_private::Symtab &symtab) override; + void FindTypes(const lldb_private::TypeQuery &match, + lldb_private::TypeResults &results) override; + void FindTypesByRegex(const lldb_private::RegularExpression ®ex, + uint32_t max_matches, lldb_private::TypeMap &types); + + void GetTypes(lldb_private::SymbolContextScope *sc_scope, + lldb::TypeClass type_mask, + lldb_private::TypeList &type_list) override; + + llvm::Expected<lldb::TypeSystemSP> + GetTypeSystemForLanguage(lldb::LanguageType language) override; + + lldb_private::CompilerDeclContext + FindNamespace(lldb_private::ConstString name, + const lldb_private::CompilerDeclContext &parent_decl_ctx, + bool only_root_namespaces) override; + + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } + + llvm::pdb::IPDBSession &GetPDBSession(); + + const llvm::pdb::IPDBSession &GetPDBSession() const; + + void DumpClangAST(lldb_private::Stream &s) override; + +private: + struct SecContribInfo { + uint32_t Offset; + uint32_t Size; + uint32_t CompilandId; + }; + using SecContribsMap = std::map<uint32_t, std::vector<SecContribInfo>>; + + uint32_t CalculateNumCompileUnits() override; + + lldb::CompUnitSP ParseCompileUnitAtIndex(uint32_t index) override; + + lldb::CompUnitSP ParseCompileUnitForUID(uint32_t id, + uint32_t index = UINT32_MAX); + + bool ParseCompileUnitLineTable(lldb_private::CompileUnit &comp_unit, + uint32_t match_line); + + void BuildSupportFileIdToSupportFileIndexMap( + const llvm::pdb::PDBSymbolCompiland &pdb_compiland, + llvm::DenseMap<uint32_t, uint32_t> &index_map) const; + + void FindTypesByName(llvm::StringRef name, + const lldb_private::CompilerDeclContext &parent_decl_ctx, + uint32_t max_matches, lldb_private::TypeMap &types); + + std::string GetMangledForPDBData(const llvm::pdb::PDBSymbolData &pdb_data); + + lldb::VariableSP + ParseVariableForPDBData(const lldb_private::SymbolContext &sc, + const llvm::pdb::PDBSymbolData &pdb_data); + + size_t ParseVariables(const lldb_private::SymbolContext &sc, + const llvm::pdb::PDBSymbol &pdb_data, + lldb_private::VariableList *variable_list = nullptr); + + lldb::CompUnitSP + GetCompileUnitContainsAddress(const lldb_private::Address &so_addr); + + typedef std::vector<lldb_private::Type *> TypeCollection; + + void GetTypesForPDBSymbol(const llvm::pdb::PDBSymbol &pdb_symbol, + uint32_t type_mask, + TypeCollection &type_collection); + + lldb_private::Function * + ParseCompileUnitFunctionForPDBFunc(const llvm::pdb::PDBSymbolFunc &pdb_func, + lldb_private::CompileUnit &comp_unit); + + void GetCompileUnitIndex(const llvm::pdb::PDBSymbolCompiland &pdb_compiland, + uint32_t &index); + + PDBASTParser *GetPDBAstParser(); + + std::unique_ptr<llvm::pdb::PDBSymbolCompiland> + GetPDBCompilandByUID(uint32_t uid); + + lldb_private::Mangled + GetMangledForPDBFunc(const llvm::pdb::PDBSymbolFunc &pdb_func); + + bool ResolveFunction(const llvm::pdb::PDBSymbolFunc &pdb_func, + bool include_inlines, + lldb_private::SymbolContextList &sc_list); + + bool ResolveFunction(uint32_t uid, bool include_inlines, + lldb_private::SymbolContextList &sc_list); + + void CacheFunctionNames(); + + bool DeclContextMatchesThisSymbolFile( + const lldb_private::CompilerDeclContext &decl_ctx); + + uint32_t GetCompilandId(const llvm::pdb::PDBSymbolData &data); + + llvm::DenseMap<uint32_t, lldb::CompUnitSP> m_comp_units; + llvm::DenseMap<uint32_t, lldb::TypeSP> m_types; + llvm::DenseMap<uint32_t, lldb::VariableSP> m_variables; + llvm::DenseMap<uint64_t, std::string> m_public_names; + + SecContribsMap m_sec_contribs; + + std::vector<lldb::TypeSP> m_builtin_types; + std::unique_ptr<llvm::pdb::IPDBSession> m_session_up; + std::unique_ptr<llvm::pdb::PDBSymbolExe> m_global_scope_up; + + lldb_private::UniqueCStringMap<uint32_t> m_func_full_names; + lldb_private::UniqueCStringMap<uint32_t> m_func_base_names; + lldb_private::UniqueCStringMap<uint32_t> m_func_method_names; +}; + +#endif // LLDB_SOURCE_PLUGINS_SYMBOLFILE_PDB_SYMBOLFILEPDB_H diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.cpp new file mode 100644 index 000000000000..8c17017442b1 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.cpp @@ -0,0 +1,258 @@ +//===-- SymbolFileSymtab.cpp ----------------------------------------------===// +// +// 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 "SymbolFileSymtab.h" + +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/Symtab.h" +#include "lldb/Symbol/TypeList.h" +#include "lldb/Utility/RegularExpression.h" +#include "lldb/Utility/Timer.h" + +#include <memory> +#include <optional> + +using namespace lldb; +using namespace lldb_private; + +LLDB_PLUGIN_DEFINE(SymbolFileSymtab) + +char SymbolFileSymtab::ID; + +void SymbolFileSymtab::Initialize() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), CreateInstance); +} + +void SymbolFileSymtab::Terminate() { + PluginManager::UnregisterPlugin(CreateInstance); +} + +llvm::StringRef SymbolFileSymtab::GetPluginDescriptionStatic() { + return "Reads debug symbols from an object file's symbol table."; +} + +SymbolFile *SymbolFileSymtab::CreateInstance(ObjectFileSP objfile_sp) { + return new SymbolFileSymtab(std::move(objfile_sp)); +} + +void SymbolFileSymtab::GetTypes(SymbolContextScope *sc_scope, + TypeClass type_mask, + lldb_private::TypeList &type_list) {} + +SymbolFileSymtab::SymbolFileSymtab(ObjectFileSP objfile_sp) + : SymbolFileCommon(std::move(objfile_sp)), m_source_indexes(), + m_func_indexes(), m_code_indexes(), m_objc_class_name_to_index() {} + +uint32_t SymbolFileSymtab::CalculateAbilities() { + uint32_t abilities = 0; + if (m_objfile_sp) { + const Symtab *symtab = m_objfile_sp->GetSymtab(); + if (symtab) { + // The snippet of code below will get the indexes the module symbol table + // entries that are code, data, or function related (debug info), sort + // them by value (address) and dump the sorted symbols. + if (symtab->AppendSymbolIndexesWithType(eSymbolTypeSourceFile, + m_source_indexes)) { + abilities |= CompileUnits; + } + + if (symtab->AppendSymbolIndexesWithType( + eSymbolTypeCode, Symtab::eDebugYes, Symtab::eVisibilityAny, + m_func_indexes)) { + symtab->SortSymbolIndexesByValue(m_func_indexes, true); + abilities |= Functions; + } + + if (symtab->AppendSymbolIndexesWithType(eSymbolTypeCode, Symtab::eDebugNo, + Symtab::eVisibilityAny, + m_code_indexes)) { + symtab->SortSymbolIndexesByValue(m_code_indexes, true); + abilities |= Functions; + } + + if (symtab->AppendSymbolIndexesWithType(eSymbolTypeData, + m_data_indexes)) { + symtab->SortSymbolIndexesByValue(m_data_indexes, true); + abilities |= GlobalVariables; + } + + lldb_private::Symtab::IndexCollection objc_class_indexes; + if (symtab->AppendSymbolIndexesWithType(eSymbolTypeObjCClass, + objc_class_indexes)) { + symtab->AppendSymbolNamesToMap(objc_class_indexes, true, true, + m_objc_class_name_to_index); + m_objc_class_name_to_index.Sort(); + } + } + } + return abilities; +} + +uint32_t SymbolFileSymtab::CalculateNumCompileUnits() { + // If we don't have any source file symbols we will just have one compile + // unit for the entire object file + if (m_source_indexes.empty()) + return 0; + + // If we have any source file symbols we will logically organize the object + // symbols using these. + return m_source_indexes.size(); +} + +CompUnitSP SymbolFileSymtab::ParseCompileUnitAtIndex(uint32_t idx) { + CompUnitSP cu_sp; + + // If we don't have any source file symbols we will just have one compile + // unit for the entire object file + if (idx < m_source_indexes.size()) { + const Symbol *cu_symbol = + m_objfile_sp->GetSymtab()->SymbolAtIndex(m_source_indexes[idx]); + if (cu_symbol) + cu_sp = std::make_shared<CompileUnit>(m_objfile_sp->GetModule(), nullptr, + cu_symbol->GetName().AsCString(), 0, + eLanguageTypeUnknown, eLazyBoolNo); + } + return cu_sp; +} + +lldb::LanguageType SymbolFileSymtab::ParseLanguage(CompileUnit &comp_unit) { + return eLanguageTypeUnknown; +} + +size_t SymbolFileSymtab::ParseFunctions(CompileUnit &comp_unit) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + size_t num_added = 0; + // We must at least have a valid compile unit + const Symtab *symtab = m_objfile_sp->GetSymtab(); + const Symbol *curr_symbol = nullptr; + const Symbol *next_symbol = nullptr; + // const char *prefix = m_objfile_sp->SymbolPrefix(); + // if (prefix == NULL) + // prefix == ""; + // + // const uint32_t prefix_len = strlen(prefix); + + // If we don't have any source file symbols we will just have one compile + // unit for the entire object file + if (m_source_indexes.empty()) { + // The only time we will have a user ID of zero is when we don't have and + // source file symbols and we declare one compile unit for the entire + // object file + if (!m_func_indexes.empty()) { + } + + if (!m_code_indexes.empty()) { + // StreamFile s(stdout); + // symtab->Dump(&s, m_code_indexes); + + uint32_t idx = 0; // Index into the indexes + const uint32_t num_indexes = m_code_indexes.size(); + for (idx = 0; idx < num_indexes; ++idx) { + uint32_t symbol_idx = m_code_indexes[idx]; + curr_symbol = symtab->SymbolAtIndex(symbol_idx); + if (curr_symbol) { + // Union of all ranges in the function DIE (if the function is + // discontiguous) + AddressRange func_range(curr_symbol->GetAddress(), 0); + if (func_range.GetBaseAddress().IsSectionOffset()) { + uint32_t symbol_size = curr_symbol->GetByteSize(); + if (symbol_size != 0 && !curr_symbol->GetSizeIsSibling()) + func_range.SetByteSize(symbol_size); + else if (idx + 1 < num_indexes) { + next_symbol = symtab->SymbolAtIndex(m_code_indexes[idx + 1]); + if (next_symbol) { + func_range.SetByteSize( + next_symbol->GetAddressRef().GetOffset() - + curr_symbol->GetAddressRef().GetOffset()); + } + } + + FunctionSP func_sp( + new Function(&comp_unit, + symbol_idx, // UserID is the DIE offset + LLDB_INVALID_UID, // We don't have any type info + // for this function + curr_symbol->GetMangled(), // Linker/mangled name + nullptr, // no return type for a code symbol... + func_range)); // first address range + + if (func_sp.get() != nullptr) { + comp_unit.AddFunction(func_sp); + ++num_added; + } + } + } + } + } + } else { + // We assume we + } + return num_added; +} + +size_t SymbolFileSymtab::ParseTypes(CompileUnit &comp_unit) { return 0; } + +bool SymbolFileSymtab::ParseLineTable(CompileUnit &comp_unit) { return false; } + +bool SymbolFileSymtab::ParseDebugMacros(CompileUnit &comp_unit) { + return false; +} + +bool SymbolFileSymtab::ParseSupportFiles(CompileUnit &comp_unit, + SupportFileList &support_files) { + return false; +} + +bool SymbolFileSymtab::ParseImportedModules( + const SymbolContext &sc, std::vector<SourceModule> &imported_modules) { + return false; +} + +size_t SymbolFileSymtab::ParseBlocksRecursive(Function &func) { return 0; } + +size_t SymbolFileSymtab::ParseVariablesForContext(const SymbolContext &sc) { + return 0; +} + +Type *SymbolFileSymtab::ResolveTypeUID(lldb::user_id_t type_uid) { + return nullptr; +} + +std::optional<SymbolFile::ArrayInfo> +SymbolFileSymtab::GetDynamicArrayInfoForUID( + lldb::user_id_t type_uid, const lldb_private::ExecutionContext *exe_ctx) { + return std::nullopt; +} + +bool SymbolFileSymtab::CompleteType(lldb_private::CompilerType &compiler_type) { + return false; +} + +uint32_t SymbolFileSymtab::ResolveSymbolContext(const Address &so_addr, + SymbolContextItem resolve_scope, + SymbolContext &sc) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + if (m_objfile_sp->GetSymtab() == nullptr) + return 0; + + uint32_t resolved_flags = 0; + if (resolve_scope & eSymbolContextSymbol) { + sc.symbol = m_objfile_sp->GetSymtab()->FindSymbolContainingFileAddress( + so_addr.GetFileAddress()); + if (sc.symbol) + resolved_flags |= eSymbolContextSymbol; + } + return resolved_flags; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.h b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.h new file mode 100644 index 000000000000..a36311525334 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.h @@ -0,0 +1,103 @@ +//===-- SymbolFileSymtab.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_SOURCE_PLUGINS_SYMBOLFILE_SYMTAB_SYMBOLFILESYMTAB_H +#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_SYMTAB_SYMBOLFILESYMTAB_H + +#include <map> +#include <optional> +#include <vector> + +#include "lldb/Symbol/SymbolFile.h" +#include "lldb/Symbol/Symtab.h" + +class SymbolFileSymtab : public lldb_private::SymbolFileCommon { + /// LLVM RTTI support. + static char ID; + +public: + /// LLVM RTTI support. + /// \{ + bool isA(const void *ClassID) const override { + return ClassID == &ID || SymbolFileCommon::isA(ClassID); + } + static bool classof(const SymbolFile *obj) { return obj->isA(&ID); } + /// \} + + // Constructors and Destructors + SymbolFileSymtab(lldb::ObjectFileSP objfile_sp); + + // Static Functions + static void Initialize(); + + static void Terminate(); + + static llvm::StringRef GetPluginNameStatic() { return "symtab"; } + + static llvm::StringRef GetPluginDescriptionStatic(); + + static lldb_private::SymbolFile * + CreateInstance(lldb::ObjectFileSP objfile_sp); + + uint32_t CalculateAbilities() override; + + // Compile Unit function calls + 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::SupportFileList &support_files) 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; + std::optional<ArrayInfo> GetDynamicArrayInfoForUID( + lldb::user_id_t type_uid, + const lldb_private::ExecutionContext *exe_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; + + void GetTypes(lldb_private::SymbolContextScope *sc_scope, + lldb::TypeClass type_mask, + lldb_private::TypeList &type_list) override; + + // PluginInterface protocol + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } + +protected: + uint32_t CalculateNumCompileUnits() override; + + lldb::CompUnitSP ParseCompileUnitAtIndex(uint32_t index) override; + + lldb_private::Symtab::IndexCollection m_source_indexes; + lldb_private::Symtab::IndexCollection m_func_indexes; + lldb_private::Symtab::IndexCollection m_code_indexes; + lldb_private::Symtab::IndexCollection m_data_indexes; + lldb_private::Symtab::NameToIndexMap m_objc_class_name_to_index; +}; + +#endif // LLDB_SOURCE_PLUGINS_SYMBOLFILE_SYMTAB_SYMBOLFILESYMTAB_H |