diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2019-01-19 10:06:29 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2019-01-19 10:06:29 +0000 |
commit | 94994d372d014ce4c8758b9605d63fae651bd8aa (patch) | |
tree | 51c0b708bd59f205d6b35cb2a8c24d62f0c33d77 /source/Plugins/SymbolFile/NativePDB | |
parent | 39be7ce23363d12ae3e49aeb1fdb2bfeb892e836 (diff) | |
download | src-94994d372d014ce4c8758b9605d63fae651bd8aa.tar.gz src-94994d372d014ce4c8758b9605d63fae651bd8aa.zip |
Vendor import of lldb trunk r351319 (just before the release_80 branchvendor/lldb/lldb-trunk-r351319
Notes
Notes:
svn path=/vendor/lldb/dist/; revision=343181
svn path=/vendor/lldb/lldb-trunk-r351319/; revision=343182; tag=vendor/lldb/lldb-trunk-r351319
Diffstat (limited to 'source/Plugins/SymbolFile/NativePDB')
17 files changed, 6180 insertions, 0 deletions
diff --git a/source/Plugins/SymbolFile/NativePDB/CMakeLists.txt b/source/Plugins/SymbolFile/NativePDB/CMakeLists.txt new file mode 100644 index 000000000000..da2d7fe8108a --- /dev/null +++ b/source/Plugins/SymbolFile/NativePDB/CMakeLists.txt @@ -0,0 +1,21 @@ +add_lldb_library(lldbPluginSymbolFileNativePDB PLUGIN + CompileUnitIndex.cpp + DWARFLocationExpression.cpp + PdbAstBuilder.cpp + PdbIndex.cpp + PdbSymUid.cpp + PdbUtil.cpp + SymbolFileNativePDB.cpp + UdtRecordCompleter.cpp + + LINK_LIBS + clangAST + clangLex + lldbCore + lldbSymbol + lldbUtility + LINK_COMPONENTS + DebugInfoCodeView + DebugInfoPDB + Support + ) diff --git a/source/Plugins/SymbolFile/NativePDB/CompileUnitIndex.cpp b/source/Plugins/SymbolFile/NativePDB/CompileUnitIndex.cpp new file mode 100644 index 000000000000..67ea05767fde --- /dev/null +++ b/source/Plugins/SymbolFile/NativePDB/CompileUnitIndex.cpp @@ -0,0 +1,217 @@ +//===-- CompileUnitIndex.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#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_lower(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(); + llvm::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; + } +} + +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); + llvm::pdb::ModuleDebugStreamRef debug_stream(descriptor, + std::move(stream_data)); + cantFail(debug_stream.reload()); + + std::unique_ptr<CompilandIndexItem> &cci = result.first->second; + + cci = llvm::make_unique<CompilandIndexItem>( + PdbCompilandId{modi}, std::move(debug_stream), std::move(descriptor)); + ParseExtendedInfo(m_index, *cci); + + cci->m_strings.initialize(debug_stream.getSubsectionsArray()); + PDBStringTable &strings = cantFail(m_index.pdb().getStringTable()); + cci->m_strings.setStrings(strings.getStringTable()); + + // 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 = 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.startswith("/") + ? 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/source/Plugins/SymbolFile/NativePDB/CompileUnitIndex.h b/source/Plugins/SymbolFile/NativePDB/CompileUnitIndex.h new file mode 100644 index 000000000000..c965870da44b --- /dev/null +++ b/source/Plugins/SymbolFile/NativePDB/CompileUnitIndex.h @@ -0,0 +1,95 @@ +//===-- CompileUnitIndex.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_PLUGINS_SYMBOLFILENATIVEPDB_COMPILEUNITINDEX_H +#define LLDB_PLUGINS_SYMBOLFILENATIVEPDB_COMPILEUNITINDEX_H + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/IntervalMap.h" +#include "llvm/ADT/Optional.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> + +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. + llvm::Optional<llvm::codeview::Compile3Sym> m_compile_opts; + + // S_OBJNAME sym describing object name. + llvm::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; +}; + +/// 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/source/Plugins/SymbolFile/NativePDB/DWARFLocationExpression.cpp b/source/Plugins/SymbolFile/NativePDB/DWARFLocationExpression.cpp new file mode 100644 index 000000000000..7b62530e4680 --- /dev/null +++ b/source/Plugins/SymbolFile/NativePDB/DWARFLocationExpression.cpp @@ -0,0 +1,673 @@ +//===-- DWARFLocationExpression.cpp -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DWARFLocationExpression.h" + +#include "Plugins/Process/Utility/lldb-x86-register-enums.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/StreamBuffer.h" +#include "lldb/Expression/DWARFExpression.h" +#include "lldb/Utility/ArchSpec.h" +#include "lldb/Utility/DataBufferHeap.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" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::npdb; +using namespace llvm::codeview; +using namespace llvm::pdb; + +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 GetLLDBRegisterNumber(llvm::Triple::ArchType arch_type, + llvm::codeview::RegisterId register_id) { + switch (arch_type) { + 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 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(nullptr); + + RegisterKind register_kind = eRegisterKindDWARF; + StreamBuffer<32> stream(Stream::eBinary, address_size, byte_order); + + if (!writer(stream, register_kind)) + return DWARFExpression(nullptr); + + DataBufferSP buffer = + std::make_shared<DataBufferHeap>(stream.GetData(), stream.GetSize()); + DataExtractor extractor(buffer, byte_order, address_size, byte_size); + DWARFExpression result(module, extractor, nullptr, 0, buffer->GetByteSize()); + result.SetRegisterKind(register_kind); + + return result; +} + +static DWARFExpression MakeRegisterBasedLocationExpressionInternal( + llvm::codeview::RegisterId reg, llvm::Optional<int32_t> relative_offset, + lldb::ModuleSP module) { + return MakeLocationExpressionInternal( + module, [&](Stream &stream, RegisterKind ®ister_kind) -> bool { + 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; + }); +} + +DWARFExpression lldb_private::npdb::MakeEnregisteredLocationExpression( + llvm::codeview::RegisterId reg, lldb::ModuleSP module) { + return MakeRegisterBasedLocationExpressionInternal(reg, llvm::None, module); +} + +DWARFExpression lldb_private::npdb::MakeRegRelLocationExpression( + llvm::codeview::RegisterId reg, int32_t offset, lldb::ModuleSP module) { + return MakeRegisterBasedLocationExpressionInternal(reg, offset, module); +} + +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); + + // Section indices in PDB are 1-based, but in DWARF they are 0-based, so + // we need to subtract 1. + uint32_t section_idx = section - 1; + if (section_idx >= section_list->GetSize()) + return false; + + auto section_ptr = section_list->GetSectionAtIndex(section_idx); + 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::makeArrayRef(reinterpret_cast<const uint8_t *>(&Value), 8) + .take_front(size); + buffer->CopyData(bytes.data(), size); + DataExtractor extractor(buffer, lldb::eByteOrderLittle, address_size); + DWARFExpression result(nullptr, extractor, nullptr, 0, size); + return result; +} diff --git a/source/Plugins/SymbolFile/NativePDB/DWARFLocationExpression.h b/source/Plugins/SymbolFile/NativePDB/DWARFLocationExpression.h new file mode 100644 index 000000000000..670e95ee8e3c --- /dev/null +++ b/source/Plugins/SymbolFile/NativePDB/DWARFLocationExpression.h @@ -0,0 +1,42 @@ +//===-- DWARFLocationExpression.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_PLUGINS_SYMBOLFILE_NATIVEPDB_DWARFLOCATIONEXPRESSION_H +#define LLDB_PLUGINS_SYMBOLFILE_NATIVEPDB_DWARFLOCATIONEXPRESSION_H + +#include "lldb/lldb-forward.h" +#include "llvm/DebugInfo/CodeView/CodeView.h" + +namespace llvm { +class APSInt; +namespace codeview { +class TypeIndex; +} +namespace pdb { +class TpiStream; +} +} // namespace llvm +namespace lldb_private { +namespace npdb { +DWARFExpression +MakeEnregisteredLocationExpression(llvm::codeview::RegisterId reg, + lldb::ModuleSP module); + +DWARFExpression MakeRegRelLocationExpression(llvm::codeview::RegisterId reg, + 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); +} // namespace npdb +} // namespace lldb_private + +#endif diff --git a/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp b/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp new file mode 100644 index 000000000000..8917fd092385 --- /dev/null +++ b/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp @@ -0,0 +1,1348 @@ +#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 "Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.h" +#include "lldb/Core/Module.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/ClangExternalASTSourceCommon.h" +#include "lldb/Symbol/ClangUtil.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Utility/LLDBAssert.h" + +#include "PdbUtil.h" +#include "UdtRecordCompleter.h" + +using namespace lldb_private; +using namespace lldb_private::npdb; +using namespace llvm::codeview; +using namespace llvm::pdb; + +static llvm::Optional<PdbCompilandSymId> FindSymbolScope(PdbIndex &index, + PdbCompilandSymId id) { + CVSymbol sym = 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 llvm::None to indicate + // this. + if (id.offset == 0) + return llvm::None; + return id; + } + + // Otherwise we need to start at the beginning and iterate forward until we + // reach (or pass) this particular symbol + CompilandIndexItem &cii = 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 (id.offset == begin.offset()) { + // We have a match! Return the top of the stack + if (scope_stack.empty()) + return llvm::None; + return scope_stack.back(); + } + if (begin.offset() > id.offset) { + // We passed it. We couldn't even find this symbol record. + lldbassert(false && "Invalid compiland symbol id!"); + return llvm::None; + } + + // 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.modi) { + 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; + } + + return llvm::None; +} + +static clang::TagTypeKind TranslateUdtKind(const TagRecord &cr) { + switch (cr.Kind) { + case TypeRecordKind::Class: + return clang::TTK_Class; + case TypeRecordKind::Struct: + return clang::TTK_Struct; + case TypeRecordKind::Union: + return clang::TTK_Union; + case TypeRecordKind::Interface: + return clang::TTK_Interface; + case TypeRecordKind::Enum: + return clang::TTK_Enum; + default: + lldbassert(false && "Invalid tag record kind!"); + return clang::TTK_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 ClangASTContext &GetClangASTContext(ObjectFile &obj) { + TypeSystem *ts = + obj.GetModule()->GetTypeSystemForLanguage(lldb::eLanguageTypeC_plus_plus); + lldbassert(ts); + return static_cast<ClangASTContext &>(*ts); +} + +static llvm::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 llvm::None; + } +} + +static llvm::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 llvm::None; + + CVType cvt = tpi.getType(Record.Type); + + if (!IsTagRecord(cvt)) + return llvm::None; + + // 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 = parent.asTag().getUniqueName(); + if (qname.size() < 4 || child.asTag().getUniqueName().size() < 4) + return llvm::None; + + // 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 llvm::None; + + return std::move(child); +} + +PdbAstBuilder::PdbAstBuilder(ObjectFile &obj, PdbIndex &index) + : m_index(index), m_clang(GetClangASTContext(obj)) { + BuildParentMap(); +} + +clang::DeclContext &PdbAstBuilder::GetTranslationUnitDecl() { + return *m_clang.GetTranslationUnitDecl(); +} + +std::pair<clang::DeclContext *, std::string> +PdbAstBuilder::CreateDeclInfoForType(const TagRecord &record, TypeIndex ti) { + // FIXME: Move this to GetDeclContextContainingUID. + if (!record.hasUniqueName()) + return CreateDeclInfoForUndecoratedName(record.Name); + + llvm::ms_demangle::Demangler demangler; + StringView sv(record.UniqueName.begin(), record.UniqueName.size()); + llvm::ms_demangle::TagTypeNode *ttn = demangler.parseTagUniqueName(sv); + if (demangler.Error) + return {m_clang.GetTranslationUnitDecl(), 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). + auto parent_iter = m_parent_types.find(ti); + if (parent_iter == m_parent_types.end()) { + 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, record.Name}; + + for (llvm::ms_demangle::Node *scope : scopes) { + auto *nii = static_cast<llvm::ms_demangle::NamedIdentifierNode *>(scope); + std::string str = nii->toString(); + context = m_clang.GetUniqueNamespaceDeclaration(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_iter->second); + + context = clang::TagDecl::castToDeclContext(parent_qt->getAsTagDecl()); + return {context, uname}; +} + +void PdbAstBuilder::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; + } + llvm::Optional<CVTagRecord> tag = + GetNestedTagDefinition(Record, parent_cvt, index.tpi()); + if (!tag) + return llvm::ErrorSuccess(); + + parents[Record.Type] = parent; + return llvm::ErrorSuccess(); + } + }; + + CVType field_list = m_index.tpi().getType(tag.asTag().FieldList); + ProcessTpiStream process(m_index, *ti, tag, m_parent_types); + llvm::Error error = visitMemberRecordStream(field_list.data(), process); + if (error) + 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]; + m_parent_types[full] = m_parent_types[fwd]; + } + for (TypeIndex full : full_keys) { + TypeIndex fwd = full_to_forward[full]; + m_parent_types[fwd] = m_parent_types[full]; + } + + // Now that +} + +static bool isLocalVariableType(SymbolKind K) { + switch (K) { + case S_REGISTER: + case S_REGREL32: + case S_LOCAL: + return true; + default: + break; + } + return false; +} + +static std::string +RenderScopeList(llvm::ArrayRef<llvm::ms_demangle::Node *> nodes) { + lldbassert(!nodes.empty()); + + std::string result = nodes.front()->toString(); + nodes = nodes.drop_front(); + while (!nodes.empty()) { + result += "::"; + result += nodes.front()->toString(llvm::ms_demangle::OF_NoTagSpecifier); + nodes = nodes.drop_front(); + } + return result; +} + +static llvm::Optional<PublicSym32> FindPublicSym(const SegmentOffset &addr, + SymbolStream &syms, + PublicsStream &publics) { + llvm::FixedStreamArray<ulittle32_t> addr_map = publics.getAddressMap(); + auto iter = std::lower_bound( + addr_map.begin(), addr_map.end(), addr, + [&](const ulittle32_t &x, const SegmentOffset &y) { + CVSymbol s1 = syms.readRecord(x); + lldbassert(s1.kind() == S_PUB32); + PublicSym32 p1; + llvm::cantFail(SymbolDeserializer::deserializeAs<PublicSym32>(s1, p1)); + if (p1.Segment < y.segment) + return true; + return p1.Offset < y.offset; + }); + if (iter == addr_map.end()) + return llvm::None; + CVSymbol sym = syms.readRecord(*iter); + lldbassert(sym.kind() == S_PUB32); + PublicSym32 p; + llvm::cantFail(SymbolDeserializer::deserializeAs<PublicSym32>(sym, p)); + if (p.Segment == addr.segment && p.Offset == addr.offset) + return p; + return llvm::None; +} + +clang::Decl *PdbAstBuilder::GetOrCreateSymbolForId(PdbCompilandSymId id) { + CVSymbol cvs = m_index.ReadSymbolRecord(id); + + if (isLocalVariableType(cvs.kind())) { + clang::DeclContext *scope = GetParentDeclContext(id); + clang::Decl *scope_decl = clang::Decl::castFromDeclContext(scope); + PdbCompilandSymId scope_id(id.modi, m_decl_to_status[scope_decl].uid); + 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); + default: + return nullptr; + } +} + +clang::Decl *PdbAstBuilder::GetOrCreateDeclForUid(PdbSymUid uid) { + if (clang::Decl *result = TryGetDecl(uid)) + return 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 (auto *tag = qt->getAsTagDecl()) { + result = tag; + break; + } + return nullptr; + } + default: + return nullptr; + } + m_uid_to_decl[toOpaqueUid(uid)] = result; + return result; +} + +clang::DeclContext *PdbAstBuilder::GetOrCreateDeclContextForUid(PdbSymUid uid) { + if (uid.kind() == PdbSymUidKind::CompilandSym) { + if (uid.asCompilandSym().offset == 0) + return &GetTranslationUnitDecl(); + } + + clang::Decl *decl = GetOrCreateDeclForUid(uid); + if (!decl) + return nullptr; + + return clang::Decl::castToDeclContext(decl); +} + +std::pair<clang::DeclContext *, std::string> +PdbAstBuilder::CreateDeclInfoForUndecoratedName(llvm::StringRef name) { + MSVCUndecoratedNameParser parser(name); + llvm::ArrayRef<MSVCUndecoratedNameSpecifier> specs = parser.GetSpecifiers(); + + clang::DeclContext *context = &GetTranslationUnitDecl(); + + llvm::StringRef uname = specs.back().GetBaseName(); + specs = specs.drop_back(); + if (specs.empty()) + return {context, name}; + + llvm::StringRef scope_name = specs.back().GetFullName(); + + // It might be a class name, try that first. + std::vector<TypeIndex> types = m_index.tpi().findRecordsByName(scope_name); + while (!types.empty()) { + clang::QualType qt = GetOrCreateType(types.back()); + clang::TagDecl *tag = qt->getAsTagDecl(); + if (tag) + return {clang::TagDecl::castToDeclContext(tag), 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 = m_clang.GetUniqueNamespaceDeclaration(ns_name.c_str(), context); + } + return {context, uname}; +} + +clang::DeclContext * +PdbAstBuilder::GetParentDeclContextForSymbol(const CVSymbol &sym) { + if (!SymbolHasAddress(sym)) + return CreateDeclInfoForUndecoratedName(getSymbolName(sym)).first; + SegmentOffset addr = GetSegmentAndOffset(sym); + llvm::Optional<PublicSym32> pub = + FindPublicSym(addr, m_index.symrecords(), m_index.publics()); + if (!pub) + return CreateDeclInfoForUndecoratedName(getSymbolName(sym)).first; + + llvm::ms_demangle::Demangler demangler; + StringView name{pub->Name.begin(), pub->Name.size()}; + llvm::ms_demangle::SymbolNode *node = demangler.parse(name); + if (!node) + return &GetTranslationUnitDecl(); + llvm::ArrayRef<llvm::ms_demangle::Node *> name_components{ + node->Name->Components->Nodes, node->Name->Components->Count - 1}; + + if (!name_components.empty()) { + // Render the current list of scope nodes as a fully qualified name, and + // look it up in the debug info as a type name. If we find something, + // this is a type (which may itself be prefixed by a namespace). If we + // don't, this is a list of namespaces. + std::string qname = RenderScopeList(name_components); + std::vector<TypeIndex> matches = m_index.tpi().findRecordsByName(qname); + while (!matches.empty()) { + clang::QualType qt = GetOrCreateType(matches.back()); + clang::TagDecl *tag = qt->getAsTagDecl(); + if (tag) + return clang::TagDecl::castToDeclContext(tag); + matches.pop_back(); + } + } + + // It's not a type. It must be a series of namespaces. + clang::DeclContext *context = &GetTranslationUnitDecl(); + while (!name_components.empty()) { + std::string ns = name_components.front()->toString(); + context = m_clang.GetUniqueNamespaceDeclaration(ns.c_str(), context); + name_components = name_components.drop_front(); + } + return context; +} + +clang::DeclContext *PdbAstBuilder::GetParentDeclContext(PdbSymUid uid) { + // We must do this *without* calling GetOrCreate on the current uid, as + // that would be an infinite recursion. + switch (uid.kind()) { + case PdbSymUidKind::CompilandSym: { + llvm::Optional<PdbCompilandSymId> scope = + FindSymbolScope(m_index, uid.asCompilandSym()); + if (scope) + return GetOrCreateDeclContextForUid(*scope); + + CVSymbol sym = m_index.ReadSymbolRecord(uid.asCompilandSym()); + return GetParentDeclContextForSymbol(sym); + } + 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(); + auto iter = m_parent_types.find(type_id.index); + if (iter == m_parent_types.end()) + return &GetTranslationUnitDecl(); + return GetOrCreateDeclContextForUid(PdbTypeSymId(iter->second)); + } + 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 = m_index.ReadSymbolRecord(uid.asGlobalSym()); + switch (global.kind()) { + case SymbolKind::S_GDATA32: + case SymbolKind::S_LDATA32: + return GetParentDeclContextForSymbol(global); + 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 &GetTranslationUnitDecl(); +} + +bool PdbAstBuilder::CompleteType(clang::QualType qt) { + clang::TagDecl *tag = qt->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(); + + lldbassert(IsTagRecord(type_id, m_index.tpi())); + + clang::QualType tag_qt = m_clang.getASTContext()->getTypeDeclType(&tag); + ClangASTContext::SetHasExternalStorage(tag_qt.getAsOpaquePtr(), false); + + TypeIndex tag_ti = type_id.index; + CVType cvt = m_index.tpi().getType(tag_ti); + if (cvt.kind() == LF_MODIFIER) + tag_ti = LookThroughModifierRecord(cvt); + + PdbTypeSymId best_ti = GetBestPossibleDecl(tag_ti, m_index.tpi()); + cvt = m_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 = m_index.tpi().getType(field_list_ti); + if (field_list_cvt.kind() != LF_FIELDLIST) + return false; + + // 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, m_index.tpi()); + auto error = + llvm::codeview::visitMemberRecordStream(field_list_cvt.data(), completer); + completer.complete(); + + status.resolved = true; + if (!error) + return true; + + llvm::consumeError(std::move(error)); + return false; +} + +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()); + 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); + + 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); + clang::TagTypeKind ttk = TranslateUdtKind(record); + lldb::AccessType access = + (ttk == clang::TTK_Class) ? lldb::eAccessPrivate : lldb::eAccessPublic; + + ClangASTMetadata metadata; + metadata.SetUserID(toOpaqueUid(id)); + metadata.SetIsDynamicCXXType(false); + + CompilerType ct = + m_clang.CreateRecordType(context, access, uname.c_str(), ttk, + lldb::eLanguageTypeC_plus_plus, &metadata); + + lldbassert(ct.IsValid()); + + ClangASTContext::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()); + + ClangASTContext::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(llvm::StringRef name, + clang::DeclContext &context) { + return m_clang.GetUniqueNamespaceDeclaration(name.str().c_str(), &context); +} + +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); + 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); + + clang::VarDecl *var_decl = m_clang.CreateVariableDeclaration( + &scope, 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); + + CVSymbol sym = m_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); + + CVSymbol sym = m_index.ReadSymbolRecord(var_id); + return CreateVariableDecl(PdbSymUid(var_id), sym, GetTranslationUnitDecl()); +} + +clang::TypedefNameDecl * +PdbAstBuilder::GetOrCreateTypedefDecl(PdbGlobalSymId id) { + if (clang::Decl *decl = TryGetDecl(id)) + return llvm::dyn_cast<clang::TypedefNameDecl>(decl); + + CVSymbol sym = m_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); + + std::string uname = DropNameScope(udt.Name); + + CompilerType ct = m_clang.CreateTypedefType(ToCompilerType(qt), uname.c_str(), + ToCompilerDeclContext(*scope)); + 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); + + CVType cvt = m_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 CreateProcedureType(pr); + } + + return {}; +} + +clang::QualType PdbAstBuilder::GetOrCreateType(PdbTypeSymId type) { + 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; + + PdbTypeSymId best_type = GetBestPossibleDecl(type, m_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); + 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); + m_uid_to_type[toOpaqueUid(type)] = qt; + if (IsTagRecord(type, m_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::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)); + 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(); + } + + CVSymbol cvs = m_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 = m_clang.CreateFunctionDeclaration( + parent, proc_name.str().c_str(), func_ct, storage, false); + + 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}); + + CreateFunctionParameters(func_id, *function_decl, func_type->getNumParams()); + + return function_decl; +} + +void PdbAstBuilder::CreateFunctionParameters(PdbCompilandSymId func_id, + clang::FunctionDecl &function_decl, + uint32_t param_count) { + CompilandIndexItem *cii = m_index.compilands().GetCompiland(func_id.modi); + CVSymbolArray scope = + cii->m_debug_stream.getSymbolArrayForScope(func_id.offset); + + auto begin = scope.begin(); + auto end = scope.end(); + std::vector<clang::ParmVarDecl *> params; + while (begin != end && param_count > 0) { + 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: + // All parameters should come before the first block. 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); + + CompilerType param_type_ct(&m_clang, qt.getAsOpaquePtr()); + clang::ParmVarDecl *param = m_clang.CreateParameterDeclaration( + &function_decl, param_name.str().c_str(), param_type_ct, + clang::SC_None); + lldbassert(m_uid_to_decl.count(toOpaqueUid(param_uid)) == 0); + + m_uid_to_decl[toOpaqueUid(param_uid)] = param; + params.push_back(param); + --param_count; + } + + if (!params.empty()) + m_clang.SetFunctionParameters(&function_decl, params.data(), params.size()); +} + +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); + clang::QualType underlying_type = GetOrCreateType(er.UnderlyingType); + + Declaration declaration; + CompilerType enum_ct = m_clang.CreateEnumerationType( + uname.c_str(), decl_context, declaration, ToCompilerType(underlying_type), + er.isScoped()); + + ClangASTContext::StartTagDeclarationDefinition(enum_ct); + ClangASTContext::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); + + uint64_t element_count = + ar.Size / GetSizeOfType({ar.ElementType}, m_index.tpi()); + + CompilerType array_ct = m_clang.CreateArrayType(ToCompilerType(element_type), + element_count, false); + return clang::QualType::getFromOpaquePtr(array_ct.GetOpaqueQualType()); +} + +clang::QualType +PdbAstBuilder::CreateProcedureType(const ProcedureRecord &proc) { + TpiStream &stream = m_index.tpi(); + CVType args_cvt = stream.getType(proc.ArgumentList); + ArgListRecord args; + llvm::cantFail( + TypeDeserializer::deserializeAs<ArgListRecord>(args_cvt, args)); + + llvm::ArrayRef<TypeIndex> arg_indices = llvm::makeArrayRef(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); + arg_types.push_back(ToCompilerType(arg_type)); + } + + clang::QualType return_type = GetOrCreateType(proc.ReturnType); + + llvm::Optional<clang::CallingConv> cc = + TranslateCallingConvention(proc.CallConv); + 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::dyn_cast<clang::TagDecl>(&context); +} + +static bool isFunctionDecl(clang::DeclContext &context) { + return !!llvm::dyn_cast<clang::FunctionDecl>(&context); +} + +static bool isBlockDecl(clang::DeclContext &context) { + return !!llvm::dyn_cast<clang::BlockDecl>(&context); +} + +void PdbAstBuilder::ParseAllNamespacesPlusChildrenOf( + llvm::Optional<llvm::StringRef> parent) { + TypeIndex ti{m_index.tpi().TypeIndexBegin()}; + for (const CVType &cvt : m_index.tpi().typeArray()) { + PdbTypeSymId tid{ti}; + ++ti; + + if (!IsTagRecord(cvt)) + continue; + + CVTagRecord tag = CVTagRecord::create(cvt); + + if (!parent.hasValue()) { + clang::QualType qt = GetOrCreateType(tid); + CompleteType(qt); + continue; + } + + // 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->isNamespace()) + continue; + + clang::NamespaceDecl *ns = llvm::dyn_cast<clang::NamespaceDecl>(context); + std::string actual_ns = ns->getQualifiedNameAsString(); + if (llvm::StringRef(actual_ns).startswith(*parent)) { + clang::QualType qt = GetOrCreateType(tid); + CompleteType(qt); + continue; + } + } + + uint32_t module_count = m_index.dbi().modules().getModuleCount(); + for (uint16_t modi = 0; modi < module_count; ++modi) { + CompilandIndexItem &cii = m_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) { + CVSymbol sym = m_index.ReadSymbolRecord(block_id); + lldbassert(sym.kind() == S_GPROC32 || sym.kind() == S_LPROC32 || + sym.kind() == S_BLOCK32); + CompilandIndexItem &cii = + m_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); + + 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) { + 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()) { + ParseAllNamespacesPlusChildrenOf(llvm::None); + return; + } + + if (context.isNamespace()) { + clang::NamespaceDecl &ns = *llvm::dyn_cast<clang::NamespaceDecl>(&context); + std::string qname = ns.getQualifiedNameAsString(); + ParseAllNamespacesPlusChildrenOf(llvm::StringRef{qname}); + return; + } + + if (isTagDecl(context) || isFunctionDecl(context) || isBlockDecl(context)) { + ParseDeclsForSimpleContext(context); + return; + } +} + +CompilerDecl PdbAstBuilder::ToCompilerDecl(clang::Decl &decl) { + return {&m_clang, &decl}; +} + +CompilerType PdbAstBuilder::ToCompilerType(clang::QualType qt) { + return {&m_clang, qt.getAsOpaquePtr()}; +} + +CompilerDeclContext +PdbAstBuilder::ToCompilerDeclContext(clang::DeclContext &context) { + return {&m_clang, &context}; +} + +clang::DeclContext * +PdbAstBuilder::FromCompilerDeclContext(CompilerDeclContext context) { + return static_cast<clang::DeclContext *>(context.GetOpaqueDeclContext()); +} + +void PdbAstBuilder::Dump(Stream &stream) { m_clang.Dump(stream); } diff --git a/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.h b/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.h new file mode 100644 index 000000000000..e3c0346f935e --- /dev/null +++ b/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.h @@ -0,0 +1,144 @@ +//===-- PdbAstBuilder.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_PLUGINS_SYMBOLFILE_NATIVEPDB_PDBASTBUILDER_H +#define LLDB_PLUGINS_SYMBOLFILE_NATIVEPDB_PDBASTBUILDER_H + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringRef.h" + +#include "lldb/Symbol/ClangASTImporter.h" + +#include "PdbIndex.h" +#include "PdbSymUid.h" + +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(ObjectFile &obj, PdbIndex &index); + + clang::DeclContext &GetTranslationUnitDecl(); + + clang::Decl *GetOrCreateDeclForUid(PdbSymUid uid); + clang::DeclContext *GetOrCreateDeclContextForUid(PdbSymUid uid); + clang::DeclContext *GetParentDeclContext(PdbSymUid uid); + + clang::NamespaceDecl *GetOrCreateNamespaceDecl(llvm::StringRef name, + clang::DeclContext &context); + clang::FunctionDecl *GetOrCreateFunctionDecl(PdbCompilandSymId func_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::DeclContext *FromCompilerDeclContext(CompilerDeclContext context); + + ClangASTContext &clang() { return m_clang; } + ClangASTImporter &importer() { 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 + CreateProcedureType(const llvm::codeview::ProcedureRecord &proc); + 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::DeclContext * + GetParentDeclContextForSymbol(const llvm::codeview::CVSymbol &sym); + + void ParseAllNamespacesPlusChildrenOf(llvm::Optional<llvm::StringRef> parent); + void ParseDeclsForSimpleContext(clang::DeclContext &context); + void ParseBlockChildren(PdbCompilandSymId block_id); + + void BuildParentMap(); + 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); + + PdbIndex &m_index; + ClangASTContext &m_clang; + + ClangASTImporter m_importer; + + llvm::DenseMap<TypeIndex, TypeIndex> m_parent_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; +}; + +} // namespace npdb +} // namespace lldb_private + +#endif // lldb_Plugins_SymbolFile_PDB_SymbolFilePDB_h_ diff --git a/source/Plugins/SymbolFile/NativePDB/PdbIndex.cpp b/source/Plugins/SymbolFile/NativePDB/PdbIndex.cpp new file mode 100644 index 000000000000..9f5dab6c2e84 --- /dev/null +++ b/source/Plugins/SymbolFile/NativePDB/PdbIndex.cpp @@ -0,0 +1,200 @@ +//===-- PdbIndex.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#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" + +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(std::unique_ptr<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 = std::move(file); + + return std::move(result); +} + +lldb::addr_t PdbIndex::MakeVirtualAddress(uint16_t segment, + uint32_t offset) const { + // Segment indices are 1-based. + lldbassert(segment > 0); + + uint32_t max_section = dbi().getSectionHeaders().size(); + lldbassert(segment <= max_section + 1); + + // 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 == max_section + 1) + 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); +} + +lldb::addr_t PdbIndex::MakeVirtualAddress(const SegmentOffset &so) const { + return MakeVirtualAddress(so.segment, so.offset); +} + +llvm::Optional<uint16_t> +PdbIndex::GetModuleIndexForAddr(uint16_t segment, uint32_t offset) const { + return GetModuleIndexForVa(MakeVirtualAddress(segment, offset)); +} + +llvm::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 llvm::None; + + 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); + 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); + + PdbCompilandSymId cu_sym_id(modi, iter.offset()); + + // If the debug info is incorrect, we could have multiple symbols with the + // same address. So use try_emplace instead of insert, and 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; + + llvm::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); + 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 { + // We need to subtract 4 here to adjust for the codeview debug magic + // at the beginning of the debug info stream. + 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/source/Plugins/SymbolFile/NativePDB/PdbIndex.h b/source/Plugins/SymbolFile/NativePDB/PdbIndex.h new file mode 100644 index 000000000000..839d4e6606e4 --- /dev/null +++ b/source/Plugins/SymbolFile/NativePDB/PdbIndex.h @@ -0,0 +1,162 @@ +//===-- PdbIndex.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_PLUGINS_SYMBOLFILENATIVEPDB_PDBINDEX_H +#define LLDB_PLUGINS_SYMBOLFILENATIVEPDB_PDBINDEX_H + +#include "lldb/lldb-types.h" +#include "llvm/ADT/IntervalMap.h" +#include "llvm/ADT/Optional.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/PDBTypes.h" + +#include "CompileUnitIndex.h" +#include "PdbSymUid.h" + +#include <map> +#include <memory> + +namespace llvm { +namespace pdb { +class DbiStream; +class TpiStream; +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. + std::unique_ptr<llvm::pdb::PDBFile> m_file; + + /// 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(std::unique_ptr<llvm::pdb::PDBFile>); + + void SetLoadAddress(lldb::addr_t addr) { m_load_address = addr; } + 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; + lldb::addr_t MakeVirtualAddress(const SegmentOffset &so) const; + + std::vector<SymbolAndUid> FindSymbolsByVa(lldb::addr_t va); + + llvm::codeview::CVSymbol ReadSymbolRecord(PdbCompilandSymId cu_sym) const; + llvm::codeview::CVSymbol ReadSymbolRecord(PdbGlobalSymId global) const; + + llvm::Optional<uint16_t> GetModuleIndexForAddr(uint16_t segment, + uint32_t offset) const; + llvm::Optional<uint16_t> GetModuleIndexForVa(lldb::addr_t va) const; +}; +} // namespace npdb +} // namespace lldb_private + +#endif diff --git a/source/Plugins/SymbolFile/NativePDB/PdbSymUid.cpp b/source/Plugins/SymbolFile/NativePDB/PdbSymUid.cpp new file mode 100644 index 000000000000..e5424568da47 --- /dev/null +++ b/source/Plugins/SymbolFile/NativePDB/PdbSymUid.cpp @@ -0,0 +1,161 @@ +//===-- PdbSymUid.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#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/source/Plugins/SymbolFile/NativePDB/PdbSymUid.h b/source/Plugins/SymbolFile/NativePDB/PdbSymUid.h new file mode 100644 index 000000000000..1166bee4e327 --- /dev/null +++ b/source/Plugins/SymbolFile/NativePDB/PdbSymUid.h @@ -0,0 +1,126 @@ +//===-- PdbSymUid.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// 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_PLUGINS_SYMBOLFILENATIVEPDB_PDBSYMUID_H +#define LLDB_PLUGINS_SYMBOLFILENATIVEPDB_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/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp b/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp new file mode 100644 index 000000000000..317725dd250e --- /dev/null +++ b/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp @@ -0,0 +1,750 @@ +//===-- PdbUtil.cpp ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PdbUtil.h" + +#include "DWARFLocationExpression.h" +#include "PdbIndex.h" +#include "PdbSymUid.h" + +#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h" +#include "llvm/DebugInfo/CodeView/TypeDeserializer.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" + +#include "Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.h" +#include "lldb/Utility/LLDBAssert.h" +#include "lldb/lldb-enumerations.h" + +using namespace lldb_private; +using namespace lldb_private::npdb; +using namespace llvm::codeview; +using namespace llvm::pdb; + +static Variable::RangeList +MakeRangeList(const PdbIndex &index, const LocalVariableAddrRange &range, + llvm::ArrayRef<LocalVariableAddrGap> gaps) { + lldb::addr_t start = + index.MakeVirtualAddress(range.ISectStart, range.OffsetStart); + lldb::addr_t end = start + range.Range; + + Variable::RangeList result; + while (!gaps.empty()) { + const LocalVariableAddrGap &gap = gaps.front(); + + lldb::addr_t size = gap.GapStartOffset - start; + result.Append(start, size); + start += gap.Range; + gaps = gaps.drop_front(); + } + + result.Append(start, end); + return result; +} + +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; + 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 {}; +} + +VariableInfo lldb_private::npdb::GetVariableLocationInfo( + PdbIndex &index, PdbCompilandSymId var_id, 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 = + MakeRegRelLocationExpression(reg.Register, reg.Offset, module); + result.ranges.emplace(); + return result; + } + + if (sym.kind() == S_REGISTER) { + RegisterSym reg(SymbolRecordKind::RegisterSym); + cantFail(SymbolDeserializer::deserializeAs<RegisterSym>(sym, reg)); + result.location = MakeEnregisteredLocationExpression(reg.Register, module); + result.ranges.emplace(); + return result; + } + + if (sym.kind() == S_LOCAL) { + LocalSym local(SymbolRecordKind::LocalSym); + cantFail(SymbolDeserializer::deserializeAs<LocalSym>(sym, local)); + + PdbCompilandSymId loc_specifier_id(var_id.modi, + var_id.offset + sym.RecordData.size()); + CVSymbol loc_specifier_cvs = index.ReadSymbolRecord(loc_specifier_id); + if (loc_specifier_cvs.kind() == S_DEFRANGE_FRAMEPOINTER_REL) { + DefRangeFramePointerRelSym loc( + SymbolRecordKind::DefRangeFramePointerRelSym); + cantFail(SymbolDeserializer::deserializeAs<DefRangeFramePointerRelSym>( + loc_specifier_cvs, loc)); + // FIXME: The register needs to come from the S_FRAMEPROC symbol. + result.location = + MakeRegRelLocationExpression(RegisterId::RSP, loc.Offset, module); + result.ranges = MakeRangeList(index, loc.Range, loc.Gaps); + } else { + // FIXME: Handle other kinds + } + 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::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: + 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); + default: + break; + } + return 0; +} diff --git a/source/Plugins/SymbolFile/NativePDB/PdbUtil.h b/source/Plugins/SymbolFile/NativePDB/PdbUtil.h new file mode 100644 index 000000000000..570c300b6a2b --- /dev/null +++ b/source/Plugins/SymbolFile/NativePDB/PdbUtil.h @@ -0,0 +1,159 @@ +//===-- PdbUtil.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_PLUGINS_SYMBOLFILENATIVEPDB_PDBUTIL_H +#define LLDB_PLUGINS_SYMBOLFILENATIVEPDB_PDBUTIL_H + +#include "lldb/Expression/DWARFExpression.h" +#include "lldb/Symbol/Variable.h" +#include "lldb/lldb-enumerations.h" + +#include "llvm/ADT/Optional.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; + llvm::Optional<DWARFExpression> location; + llvm::Optional<Variable::RangeList> ranges; +}; + +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, + 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/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp b/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp new file mode 100644 index 000000000000..7e97e2b37724 --- /dev/null +++ b/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp @@ -0,0 +1,1571 @@ +//===-- SymbolFileNativePDB.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "SymbolFileNativePDB.h" + +#include "clang/AST/Attr.h" +#include "clang/AST/CharUnits.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/Type.h" + +#include "Plugins/Language/CPlusPlus/MSVCUndecoratedNameParser.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/StreamBuffer.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/ClangASTImporter.h" +#include "lldb/Symbol/ClangExternalASTSourceCommon.h" +#include "lldb/Symbol/ClangUtil.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 "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/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/SymbolStream.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.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 "PdbAstBuilder.h" +#include "PdbSymUid.h" +#include "PdbUtil.h" +#include "UdtRecordCompleter.h" + +using namespace lldb; +using namespace lldb_private; +using namespace npdb; +using namespace llvm::codeview; +using namespace llvm::pdb; + +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; + default: + return lldb::LanguageType::eLanguageTypeUnknown; + } +} + +static std::unique_ptr<PDBFile> loadPDBFile(std::string PdbPath, + llvm::BumpPtrAllocator &Allocator) { + llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> ErrorOrBuffer = + llvm::MemoryBuffer::getFile(PdbPath, /*FileSize=*/-1, + /*RequiresNullTerminator=*/false); + if (!ErrorOrBuffer) + return nullptr; + std::unique_ptr<llvm::MemoryBuffer> Buffer = std::move(*ErrorOrBuffer); + + llvm::StringRef Path = Buffer->getBufferIdentifier(); + auto Stream = llvm::make_unique<llvm::MemoryBufferByteStream>( + std::move(Buffer), llvm::support::little); + + auto File = llvm::make_unique<PDBFile>(Path, std::move(Stream), Allocator); + if (auto EC = File->parseFileHeaders()) { + llvm::consumeError(std::move(EC)); + return nullptr; + } + if (auto EC = File->parseStreamData()) { + llvm::consumeError(std::move(EC)); + return nullptr; + } + + return File; +} + +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); + + 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; + auto ec = obj->getDebugPDBInfo(pdb_info, pdb_file); + if (ec) + return nullptr; + + // if the file doesn't exist, is not a pdb, or doesn't have a matching guid, + // fail. + llvm::file_magic magic; + ec = llvm::identify_magic(pdb_file, magic); + if (ec || magic != llvm::file_magic::pdb) + return nullptr; + std::unique_ptr<PDBFile> pdb = loadPDBFile(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::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; + } +} + +void SymbolFileNativePDB::Initialize() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), CreateInstance, + DebuggerInitialize); +} + +void SymbolFileNativePDB::Terminate() { + PluginManager::UnregisterPlugin(CreateInstance); +} + +void SymbolFileNativePDB::DebuggerInitialize(Debugger &debugger) {} + +ConstString SymbolFileNativePDB::GetPluginNameStatic() { + static ConstString g_name("native-pdb"); + return g_name; +} + +const char *SymbolFileNativePDB::GetPluginDescriptionStatic() { + return "Microsoft PDB debug symbol cross-platform file reader."; +} + +SymbolFile *SymbolFileNativePDB::CreateInstance(ObjectFile *obj_file) { + return new SymbolFileNativePDB(obj_file); +} + +SymbolFileNativePDB::SymbolFileNativePDB(ObjectFile *object_file) + : SymbolFile(object_file) {} + +SymbolFileNativePDB::~SymbolFileNativePDB() {} + +uint32_t SymbolFileNativePDB::CalculateAbilities() { + uint32_t abilities = 0; + if (!m_obj_file) + return 0; + + if (!m_index) { + // Lazily load and match the PDB file, but only do this once. + std::unique_ptr<PDBFile> file_up = + loadMatchingPDBFile(m_obj_file->GetFileSpec().GetPath(), m_allocator); + + if (!file_up) { + auto module_sp = m_obj_file->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; + file_up = loadPDBFile(symfile.GetPath(), m_allocator); + } + + if (!file_up) + return 0; + + auto expected_index = PdbIndex::create(std::move(file_up)); + 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_obj_file->GetFileOffset(); + m_index->SetLoadAddress(m_obj_load_address); + m_index->ParseSectionContribs(); + + TypeSystem *ts = m_obj_file->GetModule()->GetTypeSystemForLanguage( + lldb::eLanguageTypeC_plus_plus); + if (ts) + ts->SetSymbolFile(this); + + m_ast = llvm::make_unique<PdbAstBuilder>(*m_obj_file, *m_index); +} + +uint32_t SymbolFileNativePDB::GetNumCompileUnits() { + 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); + + if (sym.kind() == S_GPROC32 || sym.kind() == S_LPROC32) { + // This is a function. It must be global. Creating the Function entry for + // it automatically creates a block for it. + CompUnitSP comp_unit = GetOrCreateCompileUnit(*cii); + return GetOrCreateFunction(block_id, *comp_unit)->GetBlock(false); + } + + lldbassert(sym.kind() == 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); + lldb::user_id_t opaque_block_uid = toOpaqueUid(block_id); + BlockSP child_block = std::make_shared<Block>(opaque_block_uid); + parent_block.AddChild(child_block); + + m_ast->GetOrCreateBlockDecl(block_id); + + m_blocks.insert({opaque_block_uid, child_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); + 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); + + m_ast->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(source_file_name); + + CompUnitSP cu_sp = + std::make_shared<CompileUnit>(m_obj_file->GetModule(), nullptr, fs, + toOpaqueUid(cci.m_id), lang, optimized); + + m_obj_file->GetModule()->GetSymbolVendor()->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 = GetSimpleTypeName(mr.ModifiedType.getSimpleKind()); + else + name = computeTypeName(stream.typeCollection(), mr.ModifiedType); + Declaration decl; + lldb::TypeSP modified_type = GetOrCreateType(mr.ModifiedType); + + return std::make_shared<Type>(toOpaqueUid(type_id), this, ConstString(name), + modified_type->GetByteSize(), nullptr, + LLDB_INVALID_UID, Type::eEncodingIsUID, decl, + ct, Type::eResolveStateFull); +} + +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 std::make_shared<Type>(toOpaqueUid(type_id), this, ConstString(), + pr.getSize(), nullptr, LLDB_INVALID_UID, + Type::eEncodingIsUID, decl, ct, + Type::eResolveStateFull); +} + +lldb::TypeSP SymbolFileNativePDB::CreateSimpleType(TypeIndex ti, + CompilerType ct) { + uint64_t uid = toOpaqueUid(PdbTypeSymId(ti, false)); + if (ti == TypeIndex::NullptrT()) { + Declaration decl; + return std::make_shared<Type>( + uid, this, ConstString("std::nullptr_t"), 0, nullptr, LLDB_INVALID_UID, + Type::eEncodingIsUID, decl, ct, Type::eResolveStateFull); + } + + 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 std::make_shared<Type>( + uid, this, ConstString(), pointer_size, nullptr, LLDB_INVALID_UID, + Type::eEncodingIsUID, decl, ct, Type::eResolveStateFull); + } + + if (ti.getSimpleKind() == SimpleTypeKind::NotTranslated) + return nullptr; + + size_t size = GetTypeSizeForSimpleKind(ti.getSimpleKind()); + llvm::StringRef type_name = GetSimpleTypeName(ti.getSimpleKind()); + + Declaration decl; + return std::make_shared<Type>(uid, this, ConstString(type_name), size, + nullptr, LLDB_INVALID_UID, Type::eEncodingIsUID, + decl, ct, Type::eResolveStateFull); +} + +static std::string GetUnqualifiedTypeName(const TagRecord &record) { + if (!record.hasUniqueName()) { + MSVCUndecoratedNameParser parser(record.Name); + llvm::ArrayRef<MSVCUndecoratedNameSpecifier> specs = parser.GetSpecifiers(); + + return specs.back().GetBaseName(); + } + + llvm::ms_demangle::Demangler demangler; + StringView sv(record.UniqueName.begin(), record.UniqueName.size()); + llvm::ms_demangle::TagTypeNode *ttn = demangler.parseTagUniqueName(sv); + if (demangler.Error) + return 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 std::make_shared<Type>(toOpaqueUid(type_id), this, ConstString(uname), + size, nullptr, LLDB_INVALID_UID, + Type::eEncodingIsUID, decl, ct, + Type::eResolveStateForward); +} + +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 std::make_shared<lldb_private::Type>( + toOpaqueUid(type_id), this, ConstString(uname), + underlying_type->GetByteSize(), nullptr, LLDB_INVALID_UID, + lldb_private::Type::eEncodingIsUID, decl, ct, + lldb_private::Type::eResolveStateForward); +} + +TypeSP SymbolFileNativePDB::CreateArrayType(PdbTypeSymId type_id, + const ArrayRecord &ar, + CompilerType ct) { + TypeSP element_type = GetOrCreateType(ar.ElementType); + + Declaration decl; + TypeSP array_sp = std::make_shared<lldb_private::Type>( + toOpaqueUid(type_id), this, ConstString(), ar.Size, nullptr, + LLDB_INVALID_UID, lldb_private::Type::eEncodingIsUID, decl, ct, + lldb_private::Type::eResolveStateFull); + array_sp->SetEncodingType(element_type.get()); + return array_sp; +} + +TypeSP SymbolFileNativePDB::CreateProcedureType(PdbTypeSymId type_id, + const ProcedureRecord &pr, + CompilerType ct) { + Declaration decl; + return std::make_shared<lldb_private::Type>( + toOpaqueUid(type_id), this, ConstString(), 0, nullptr, LLDB_INVALID_UID, + lldb_private::Type::eEncodingIsUID, decl, ct, + lldb_private::Type::eResolveStateFull); +} + +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); + } + + 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. + llvm::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; + + clang::QualType qt = m_ast->GetOrCreateType(best_decl_id); + + TypeSP result = CreateType(best_decl_id, m_ast->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) + m_obj_file->GetModule()->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; + LLVM_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; + LLVM_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; + llvm::Optional<uint16_t> modi = m_index->GetModuleIndexForVa(addr); + if (modi) { + 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; + + m_ast->GetOrCreateVariableDecl(var_id); + + DWARFExpression location = MakeGlobalLocationExpression( + section, offset, GetObjectFile()->GetModule()); + + std::string global_name("::"); + global_name += name; + 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, false, + false); + var_sp->SetLocationIsConstantValueData(false); + + 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(); + DWARFExpression location = MakeConstantLocationExpression( + constant.Type, tpi, constant.Value, module); + + 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, + false, false, false); + var_sp->SetLocationIsConstantValueData(true); + 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) + emplace_result.first->second = CreateGlobalVariable(var_id); + + 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) { + clang::DeclContext *context = m_ast->FromCompilerDeclContext(decl_ctx); + if (!context) + return; + m_ast->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) { + 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) { return; } + +size_t SymbolFileNativePDB::ParseFunctions(CompileUnit &comp_unit) { + 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) { + uint32_t resolved_flags = 0; + lldb::addr_t file_addr = addr.GetFileAddress(); + + if (NeedsResolvedCompileUnit(resolve_scope)) { + llvm::Optional<uint16_t> modi = m_index->GetModuleIndexForVa(file_addr); + if (!modi) + return 0; + CompilandIndexItem *cci = m_index->compilands().GetCompiland(*modi); + if (!cci) + return 0; + + sc.comp_unit = GetOrCreateCompileUnit(*cci).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(); + sc.block = sc.GetFunctionBlock(); + } + + if (type == PDB_SymType::Block) { + sc.block = &GetOrCreateBlock(csid); + sc.function = sc.block->CalculateSymbolContextFunction(); + } + resolved_flags |= eSymbolContextFunction; + 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 FileSpec &file_spec, uint32_t line, bool check_inlines, + lldb::SymbolContextItem resolve_scope, SymbolContextList &sc_list) { + return 0; +} + +static void AppendLineEntryToSequence(LineTable &table, LineSequence &sequence, + const CompilandIndexItem &cci, + lldb::addr_t base_addr, + uint32_t file_number, + const LineFragmentHeader &block, + const LineNumberEntry &cur) { + LineInfo cur_info(cur.Flags); + + if (cur_info.isAlwaysStepInto() || cur_info.isNeverStepInto()) + return; + + uint64_t addr = base_addr + cur.Offset; + + bool is_statement = cur_info.isStatement(); + bool is_prologue = IsFunctionPrologue(cci, addr); + bool is_epilogue = IsFunctionEpilogue(cci, addr); + + uint32_t lno = cur_info.getStartLine(); + + table.AppendLineEntryToSequence(&sequence, addr, lno, 0, file_number, + is_statement, false, is_prologue, is_epilogue, + false); +} + +static void TerminateLineSequence(LineTable &table, + const LineFragmentHeader &block, + lldb::addr_t base_addr, uint32_t file_number, + uint32_t last_line, + std::unique_ptr<LineSequence> seq) { + // The end is always a terminal entry, so insert it regardless. + table.AppendLineEntryToSequence(seq.get(), base_addr + block.CodeSize, + last_line, 0, file_number, false, false, + false, false, true); + table.InsertSequence(seq.release()); +} + +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. + PdbSymUid cu_id(comp_unit.GetID()); + lldbassert(cu_id.kind() == PdbSymUidKind::Compiland); + CompilandIndexItem *cci = + m_index->compilands().GetCompiland(cu_id.asCompiland().modi); + lldbassert(cci); + auto line_table = llvm::make_unique<LineTable>(&comp_unit); + + // 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 : + cci->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); + + const auto &checksums = cci->m_strings.checksums().getArray(); + const auto &strings = cci->m_strings.strings(); + for (const LineColumnEntry &group : lines) { + // 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(group.NameIndex); + if (iter == checksums.end()) + continue; + + llvm::Expected<llvm::StringRef> efn = + strings.getString(iter->FileNameOffset); + if (!efn) { + llvm::consumeError(efn.takeError()); + continue; + } + + // LLDB wants the index of the file in the list of support files. + auto fn_iter = llvm::find(cci->m_file_list, *efn); + lldbassert(fn_iter != cci->m_file_list.end()); + // LLDB support file indices are 1-based. + uint32_t file_index = + 1 + std::distance(cci->m_file_list.begin(), fn_iter); + + std::unique_ptr<LineSequence> sequence( + line_table->CreateLineSequenceContainer()); + lldbassert(!group.LineNumbers.empty()); + + for (const LineNumberEntry &entry : group.LineNumbers) { + AppendLineEntryToSequence(*line_table, *sequence, *cci, virtual_addr, + file_index, *lfh, entry); + } + LineInfo last_line(group.LineNumbers.back().Flags); + TerminateLineSequence(*line_table, *lfh, virtual_addr, file_index, + last_line.getEndLine(), std::move(sequence)); + } + } + + 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; +} + +bool SymbolFileNativePDB::ParseSupportFiles(CompileUnit &comp_unit, + FileSpecList &support_files) { + 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.startswith("/") ? FileSpec::Style::posix : FileSpec::Style::windows; + FileSpec spec(f, style); + support_files.Append(spec); + } + + llvm::SmallString<64> main_source_file = + m_index->compilands().GetMainSourceFile(*cci); + FileSpec::Style style = main_source_file.startswith("/") + ? FileSpec::Style::posix + : FileSpec::Style::windows; + FileSpec spec(main_source_file, style); + support_files.Insert(0, spec); + return true; +} + +bool SymbolFileNativePDB::ParseImportedModules( + const SymbolContext &sc, std::vector<ConstString> &imported_modules) { + // PDB does not yet support module debug info + return false; +} + +size_t SymbolFileNativePDB::ParseBlocksRecursive(Function &func) { + GetOrCreateBlock(PdbSymUid(func.GetID()).asCompilandSym()); + // FIXME: Parse child blocks + return 1; +} + +void SymbolFileNativePDB::DumpClangAST(Stream &s) { m_ast->Dump(s); } + +uint32_t SymbolFileNativePDB::FindGlobalVariables( + const ConstString &name, const CompilerDeclContext *parent_decl_ctx, + uint32_t max_matches, VariableList &variables) { + 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) { + VariableSP var; + 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); + var = GetOrCreateGlobalVariable(global); + variables.AddVariable(var); + break; + } + default: + continue; + } + } + return variables.GetSize(); +} + +uint32_t SymbolFileNativePDB::FindFunctions( + const ConstString &name, const CompilerDeclContext *parent_decl_ctx, + FunctionNameType name_type_mask, bool include_inlines, bool append, + SymbolContextList &sc_list) { + // For now we only support lookup by method name. + if (!(name_type_mask & eFunctionNameTypeMethod)) + return 0; + + 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); + } + + return sc_list.GetSize(); +} + +uint32_t SymbolFileNativePDB::FindFunctions(const RegularExpression ®ex, + bool include_inlines, bool append, + SymbolContextList &sc_list) { + return 0; +} + +uint32_t SymbolFileNativePDB::FindTypes( + const ConstString &name, const CompilerDeclContext *parent_decl_ctx, + bool append, uint32_t max_matches, + llvm::DenseSet<SymbolFile *> &searched_symbol_files, TypeMap &types) { + if (!append) + types.Clear(); + if (!name) + return 0; + + searched_symbol_files.clear(); + searched_symbol_files.insert(this); + + // There is an assumption 'name' is not a regex + size_t match_count = FindTypesByName(name.GetStringRef(), max_matches, types); + + return match_count; +} + +size_t +SymbolFileNativePDB::FindTypes(const std::vector<CompilerContext> &context, + bool append, TypeMap &types) { + return 0; +} + +size_t SymbolFileNativePDB::FindTypesByName(llvm::StringRef name, + uint32_t max_matches, + TypeMap &types) { + + size_t match_count = 0; + 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); + ++match_count; + } + return match_count; +} + +size_t SymbolFileNativePDB::ParseTypes(CompileUnit &comp_unit) { + // Only do the full type scan the first time. + if (m_done_full_type_scan) + return 0; + + size_t old_count = m_obj_file->GetModule()->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); + } + + size_t new_count = m_obj_file->GetModule()->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(); + VariableInfo var_info = GetVariableLocationInfo(*m_index, var_id, module); + if (!var_info.location || !var_info.ranges) + return nullptr; + + CompilandIndexItem *cii = m_index->compilands().GetCompiland(var_id.modi); + CompUnitSP comp_unit_sp = GetOrCreateCompileUnit(*cii); + TypeSP type_sp = GetOrCreateType(var_info.type); + std::string name = var_info.name.str(); + Declaration decl; + SymbolFileTypeSP sftype = + std::make_shared<SymbolFileType>(*this, type_sp->GetID()); + + ValueType var_scope = + is_param ? eValueTypeVariableArgument : eValueTypeVariableLocal; + VariableSP var_sp = std::make_shared<Variable>( + toOpaqueUid(var_id), name.c_str(), name.c_str(), sftype, var_scope, + comp_unit_sp.get(), *var_info.ranges, &decl, *var_info.location, false, + false, false); + + if (!is_param) + m_ast->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); + + (void)m_ast->GetOrCreateTypedefDecl(id); + + Declaration decl; + return std::make_shared<lldb_private::Type>( + toOpaqueUid(id), this, ConstString(udt.Name), target_type->GetByteSize(), + nullptr, target_type->GetID(), lldb_private::Type::eEncodingIsTypedefUID, + decl, target_type->GetForwardCompilerType(), + lldb_private::Type::eResolveStateForward); +} + +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); + ProcedureRecord sig; + cantFail(TypeDeserializer::deserializeAs<ProcedureRecord>(signature, sig)); + params_remaining = sig.getParameterCount(); + break; + } + case S_BLOCK32: + 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, recurse into its children and then skip it. + if (variable_cvs.kind() == S_BLOCK32) { + 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) { + 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) { + clang::Decl *decl = m_ast->GetOrCreateDeclForUid(PdbSymUid(uid)); + + return m_ast->ToCompilerDecl(*decl); +} + +CompilerDeclContext +SymbolFileNativePDB::GetDeclContextForUID(lldb::user_id_t uid) { + clang::DeclContext *context = + m_ast->GetOrCreateDeclContextForUid(PdbSymUid(uid)); + if (!context) + return {}; + + return m_ast->ToCompilerDeclContext(*context); +} + +CompilerDeclContext +SymbolFileNativePDB::GetDeclContextContainingUID(lldb::user_id_t uid) { + clang::DeclContext *context = m_ast->GetParentDeclContext(PdbSymUid(uid)); + return m_ast->ToCompilerDeclContext(*context); +} + +Type *SymbolFileNativePDB::ResolveTypeUID(lldb::user_id_t type_uid) { + 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); + return &*type_sp; +} + +llvm::Optional<SymbolFile::ArrayInfo> +SymbolFileNativePDB::GetDynamicArrayInfoForUID( + lldb::user_id_t type_uid, const lldb_private::ExecutionContext *exe_ctx) { + return llvm::None; +} + + +bool SymbolFileNativePDB::CompleteType(CompilerType &compiler_type) { + clang::QualType qt = + clang::QualType::getFromOpaquePtr(compiler_type.GetOpaqueQualType()); + + return m_ast->CompleteType(qt); +} + +size_t SymbolFileNativePDB::GetTypes(lldb_private::SymbolContextScope *sc_scope, + TypeClass type_mask, + lldb_private::TypeList &type_list) { + return 0; +} + +CompilerDeclContext +SymbolFileNativePDB::FindNamespace(const ConstString &name, + const CompilerDeclContext *parent_decl_ctx) { + return {}; +} + +TypeSystem * +SymbolFileNativePDB::GetTypeSystemForLanguage(lldb::LanguageType language) { + auto type_system = + m_obj_file->GetModule()->GetTypeSystemForLanguage(language); + if (type_system) + type_system->SetSymbolFile(this); + return type_system; +} + +ConstString SymbolFileNativePDB::GetPluginName() { + static ConstString g_name("pdb"); + return g_name; +} + +uint32_t SymbolFileNativePDB::GetPluginVersion() { return 1; } diff --git a/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h b/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h new file mode 100644 index 000000000000..dcf3fe365ef1 --- /dev/null +++ b/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h @@ -0,0 +1,245 @@ +//===-- SymbolFileNativePDB.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_PLUGINS_SYMBOLFILE_NATIVEPDB_SYMBOLFILENATIVEPDB_H +#define LLDB_PLUGINS_SYMBOLFILE_NATIVEPDB_SYMBOLFILENATIVEPDB_H + +#include "lldb/Symbol/ClangASTImporter.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" + +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 { +class ClangASTImporter; + +namespace npdb { +class PdbAstBuilder; + +class SymbolFileNativePDB : public SymbolFile { + friend class UdtRecordCompleter; + +public: + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void Initialize(); + + static void Terminate(); + + static void DebuggerInitialize(Debugger &debugger); + + static ConstString GetPluginNameStatic(); + + static const char *GetPluginDescriptionStatic(); + + static SymbolFile *CreateInstance(ObjectFile *obj_file); + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + SymbolFileNativePDB(ObjectFile *ofile); + + ~SymbolFileNativePDB() override; + + uint32_t CalculateAbilities() override; + + void InitializeObject() override; + + //------------------------------------------------------------------ + // Compile Unit function calls + //------------------------------------------------------------------ + + uint32_t GetNumCompileUnits() override; + + void + ParseDeclsForContext(lldb_private::CompilerDeclContext decl_ctx) override; + + lldb::CompUnitSP ParseCompileUnitAtIndex(uint32_t index) override; + + lldb::LanguageType + ParseLanguage(lldb_private::CompileUnit &comp_unit) override; + + size_t ParseFunctions(lldb_private::CompileUnit &comp_unit) override; + + bool ParseLineTable(lldb_private::CompileUnit &comp_unit) override; + + bool ParseDebugMacros(lldb_private::CompileUnit &comp_unit) override; + + bool ParseSupportFiles(lldb_private::CompileUnit &comp_unit, + FileSpecList &support_files) override; + size_t ParseTypes(lldb_private::CompileUnit &comp_unit) override; + + bool + ParseImportedModules(const SymbolContext &sc, + std::vector<ConstString> &imported_modules) override; + + size_t ParseBlocksRecursive(Function &func) override; + + uint32_t FindGlobalVariables(const 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; + llvm::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 FileSpec &file_spec, uint32_t line, + bool check_inlines, + lldb::SymbolContextItem resolve_scope, + SymbolContextList &sc_list) override; + + size_t GetTypes(SymbolContextScope *sc_scope, lldb::TypeClass type_mask, + TypeList &type_list) override; + + uint32_t FindFunctions(const ConstString &name, + const CompilerDeclContext *parent_decl_ctx, + lldb::FunctionNameType name_type_mask, + bool include_inlines, bool append, + SymbolContextList &sc_list) override; + + uint32_t FindFunctions(const RegularExpression ®ex, bool include_inlines, + bool append, SymbolContextList &sc_list) override; + + uint32_t FindTypes(const ConstString &name, + const CompilerDeclContext *parent_decl_ctx, bool append, + uint32_t max_matches, + llvm::DenseSet<SymbolFile *> &searched_symbol_files, + TypeMap &types) override; + + size_t FindTypes(const std::vector<CompilerContext> &context, bool append, + TypeMap &types) override; + + TypeSystem *GetTypeSystemForLanguage(lldb::LanguageType language) override; + + CompilerDeclContext + FindNamespace(const ConstString &name, + const CompilerDeclContext *parent_decl_ctx) override; + + ConstString GetPluginName() override; + + uint32_t GetPluginVersion() override; + + llvm::pdb::PDBFile &GetPDBFile() { return m_index->pdb(); } + const llvm::pdb::PDBFile &GetPDBFile() const { return m_index->pdb(); } + + void DumpClangAST(Stream &s) override; + +private: + + size_t 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 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::BumpPtrAllocator m_allocator; + + lldb::addr_t m_obj_load_address = 0; + bool m_done_full_type_scan = false; + + std::unique_ptr<PdbIndex> m_index; + + std::unique_ptr<PdbAstBuilder> m_ast; + + 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; +}; + +} // namespace npdb +} // namespace lldb_private + +#endif // lldb_Plugins_SymbolFile_PDB_SymbolFilePDB_h_ diff --git a/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp b/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp new file mode 100644 index 000000000000..239dfbee625d --- /dev/null +++ b/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp @@ -0,0 +1,191 @@ +#include "UdtRecordCompleter.h" + +#include "PdbAstBuilder.h" +#include "PdbIndex.h" +#include "PdbSymUid.h" +#include "PdbUtil.h" + +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/ClangASTImporter.h" +#include "lldb/Symbol/Type.h" +#include "lldb/Utility/LLDBAssert.h" +#include "lldb/lldb-enumerations.h" +#include "lldb/lldb-forward.h" + +#include "llvm/DebugInfo/CodeView/TypeDeserializer.h" +#include "llvm/DebugInfo/CodeView/TypeIndex.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" +#include "llvm/DebugInfo/PDB/PDBTypes.h" + +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, + TpiStream &tpi) + : m_id(id), m_derived_ct(derived_ct), m_tag_decl(tag_decl), + m_ast_builder(ast_builder), m_tpi(tpi) { + CVType cvt = m_tpi.getType(m_id.index); + switch (cvt.kind()) { + case LF_ENUM: + llvm::cantFail(TypeDeserializer::deserializeAs<EnumRecord>(cvt, m_cvr.er)); + break; + case LF_UNION: + llvm::cantFail(TypeDeserializer::deserializeAs<UnionRecord>(cvt, m_cvr.ur)); + break; + case LF_CLASS: + case LF_STRUCTURE: + llvm::cantFail(TypeDeserializer::deserializeAs<ClassRecord>(cvt, m_cvr.cr)); + break; + default: + llvm_unreachable("unreachable!"); + } +} + +clang::QualType UdtRecordCompleter::AddBaseClassForTypeIndex( + llvm::codeview::TypeIndex ti, llvm::codeview::MemberAccess access) { + PdbTypeSymId type_id(ti); + clang::QualType qt = m_ast_builder.GetOrCreateType(type_id); + + CVType udt_cvt = m_tpi.getType(ti); + + std::unique_ptr<clang::CXXBaseSpecifier> base_spec = + m_ast_builder.clang().CreateBaseClassSpecifier( + qt.getAsOpaquePtr(), TranslateMemberAccess(access), false, + udt_cvt.kind() == LF_CLASS); + lldbassert(base_spec); + m_bases.push_back(std::move(base_spec)); + return qt; +} + +Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr, + BaseClassRecord &base) { + clang::QualType base_qt = + AddBaseClassForTypeIndex(base.Type, base.getAccess()); + + 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()); + + // FIXME: Handle virtual base offsets. + 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)); + + m_ast_builder.CompleteType(member_type); + + CompilerType member_ct = m_ast_builder.ToCompilerType(member_type); + + lldb::AccessType access = + TranslateMemberAccess(static_data_member.getAccess()); + ClangASTContext::AddVariableToRecordType( + m_derived_ct, static_data_member.Name, member_ct, access); + + // 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_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)); + m_ast_builder.CompleteType(member_qt); + + lldb::AccessType access = TranslateMemberAccess(data_member.getAccess()); + + clang::FieldDecl *decl = ClangASTContext::AddFieldToRecordType( + m_derived_ct, data_member.Name, m_ast_builder.ToCompilerType(member_qt), + access, bitfield_width); + // FIXME: Add a PdbSymUid namespace for field list members and update + // the m_uid_to_decl map with this decl. + + m_layout.field_offsets.insert(std::make_pair(decl, offset)); + + return Error::success(); +} + +Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr, + OneMethodRecord &one_method) { + return Error::success(); +} + +Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr, + OverloadedMethodRecord &overloaded) { + 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() { + ClangASTContext &clang = m_ast_builder.clang(); + clang.TransferBaseClasses(m_derived_ct.GetOpaqueQualType(), + std::move(m_bases)); + + clang.AddMethodOverridesForCXXRecordType(m_derived_ct.GetOpaqueQualType()); + ClangASTContext::BuildIndirectFields(m_derived_ct); + ClangASTContext::CompleteTagDeclarationDefinition(m_derived_ct); + + if (auto *record_decl = llvm::dyn_cast<clang::CXXRecordDecl>(&m_tag_decl)) { + m_ast_builder.importer().InsertRecordDecl(record_decl, m_layout); + } +} diff --git a/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.h b/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.h new file mode 100644 index 000000000000..469685126e59 --- /dev/null +++ b/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.h @@ -0,0 +1,75 @@ +//===-- SymbolFileNativePDB.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_PLUGINS_SYMBOLFILE_NATIVEPDB_UDTRECORDCOMPLETER_H +#define LLDB_PLUGINS_SYMBOLFILE_NATIVEPDB_UDTRECORDCOMPLETER_H + +#include "lldb/Symbol/ClangASTImporter.h" +#include "llvm/DebugInfo/CodeView/CVRecord.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h" + +#include "PdbSymUid.h" + +namespace clang { +class CXXBaseSpecifier; +class QualType; +class TagDecl; +} // namespace clang + +namespace llvm { +namespace pdb { +class TpiStream; +} +} // namespace llvm + +namespace lldb_private { +class Type; +class CompilerType; +namespace npdb { +class PdbAstBuilder; + +class UdtRecordCompleter : public llvm::codeview::TypeVisitorCallbacks { + 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; + llvm::pdb::TpiStream &m_tpi; + std::vector<std::unique_ptr<clang::CXXBaseSpecifier>> m_bases; + ClangASTImporter::LayoutInfo m_layout; + +public: + UdtRecordCompleter(PdbTypeSymId id, CompilerType &derived_ct, + clang::TagDecl &tag_decl, PdbAstBuilder &ast_builder, + llvm::pdb::TpiStream &tpi); + +#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" + + void complete(); + +private: + clang::QualType AddBaseClassForTypeIndex(llvm::codeview::TypeIndex ti, + llvm::codeview::MemberAccess access); +}; + +} // namespace npdb +} // namespace lldb_private + +#endif // LLDB_PLUGINS_SYMBOLFILE_NATIVEPDB_UDTRECORDCOMPLETER_H |