Index: lldb/lit/SymbolFile/NativePDB/Inputs/breakpoints.lldbinit =================================================================== --- /dev/null +++ lldb/lit/SymbolFile/NativePDB/Inputs/breakpoints.lldbinit @@ -0,0 +1,5 @@ +break set -n main +break set -n OvlGlobalFn +break set -n StaticFn +break list +quit Index: lldb/lit/SymbolFile/NativePDB/Inputs/disassembly.lldbinit =================================================================== --- /dev/null +++ lldb/lit/SymbolFile/NativePDB/Inputs/disassembly.lldbinit @@ -0,0 +1,2 @@ +disassemble --flavor=intel -m -n main +quit Index: lldb/lit/SymbolFile/NativePDB/Inputs/source-list.lldbinit =================================================================== --- /dev/null +++ lldb/lit/SymbolFile/NativePDB/Inputs/source-list.lldbinit @@ -0,0 +1,3 @@ +source list -n main +source list -n OvlGlobalFn +quit Index: lldb/lit/SymbolFile/NativePDB/disassembly.cpp =================================================================== --- /dev/null +++ lldb/lit/SymbolFile/NativePDB/disassembly.cpp @@ -0,0 +1,38 @@ +// clang-format off + +// Test that we can show disassembly and source. +// RUN: clang-cl /Z7 /GS- /GR- /c %s /Fo%t.obj +// RUN: lld-link /DEBUG /nodefaultlib /entry:main /OUT:%t.exe /PDB:%t.pdb %t.obj +// RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb -f %t.exe -s \ +// RUN: %p/Inputs/disassembly.lldbinit | FileCheck %s + +// Some context lines before +// the function. + +int foo() { return 42; } + +int main(int argc, char **argv) { + foo(); + return 0; +} + + +// CHECK: (lldb) disassemble --flavor=intel -m -n main +// CHECK: 12 int foo() { return 42; } +// CHECK-NEXT: 13 +// CHECK-NEXT: ** 14 int main(int argc, char **argv) { +// CHECK: disassembly.cpp.tmp.exe`main: +// CHECK-NEXT: disassembly.cpp.tmp.exe[0x140001010] <+0>: sub rsp, 0x38 +// CHECK-NEXT: disassembly.cpp.tmp.exe[0x140001014] <+4>: mov dword ptr [rsp + 0x34], 0x0 +// CHECK-NEXT: disassembly.cpp.tmp.exe[0x14000101c] <+12>: mov qword ptr [rsp + 0x28], rdx +// CHECK-NEXT: disassembly.cpp.tmp.exe[0x140001021] <+17>: mov dword ptr [rsp + 0x24], ecx +// CHECK: ** 15 foo(); +// CHECK: disassembly.cpp.tmp.exe[0x140001025] <+21>: call 0x140001000 ; foo at disassembly.cpp:12 +// CHECK-NEXT: disassembly.cpp.tmp.exe[0x14000102a] <+26>: xor ecx, ecx +// CHECK-NEXT: disassembly.cpp.tmp.exe[0x14000102c] <+28>: mov dword ptr [rsp + 0x20], eax +// CHECK: ** 16 return 0; +// CHECK-NEXT: 17 } +// CHECK-NEXT: 18 +// CHECK: disassembly.cpp.tmp.exe[0x140001030] <+32>: mov eax, ecx +// CHECK-NEXT: disassembly.cpp.tmp.exe[0x140001032] <+34>: add rsp, 0x38 +// CHECK-NEXT: disassembly.cpp.tmp.exe[0x140001036] <+38>: ret Index: lldb/lit/SymbolFile/NativePDB/lit.local.cfg =================================================================== --- /dev/null +++ lldb/lit/SymbolFile/NativePDB/lit.local.cfg @@ -0,0 +1 @@ +config.suffixes = ['.test', '.cpp'] Index: lldb/lit/SymbolFile/NativePDB/simple-breakpoints.cpp =================================================================== --- /dev/null +++ lldb/lit/SymbolFile/NativePDB/simple-breakpoints.cpp @@ -0,0 +1,60 @@ +// clang-format off + +// Test that we can set simple breakpoints using PDB on any platform. +// RUN: clang-cl /Z7 /GS- /GR- /c %s /Fo%t.obj +// RUN: lld-link /DEBUG /nodefaultlib /entry:main /OUT:%t.exe /PDB:%t.pdb %t.obj +// RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb -f %t.exe -s \ +// RUN: %p/Inputs/breakpoints.lldbinit | FileCheck %s + +// Use different indentation style for each overload so that the starting +// line is in a different place. +int OvlGlobalFn(int X) { + return X + 42; +} + +int OvlGlobalFn(int X, int Y) { return X + Y + 42; } + +int OvlGlobalFn(int X, int Y, int Z) +{ + return X + Y + Z + 42; +} + +static int StaticFn(int X) { + return X + 42; +} + +int main(int argc, char **argv) { + // Make sure they don't get optimized out. + // Note the comments here, we want to make sure the line number reported + // for the breakpoint is the first actual line of code. + int Result = OvlGlobalFn(argc) + OvlGlobalFn(argc, argc) + + OvlGlobalFn(argc, argc, argc) + StaticFn(argc); + return Result; +} + + +// CHECK: (lldb) target create "{{.*}}simple-breakpoints.cpp.tmp.exe" +// CHECK: Current executable set to '{{.*}}simple-breakpoints.cpp.tmp.exe' (x86_64). +// CHECK: (lldb) break set -n main +// CHECK: Breakpoint 1: where = simple-breakpoints.cpp.tmp.exe`main + 21 +// CHECK-SAME: at simple-breakpoints.cpp:30 +// CHECK: (lldb) break set -n OvlGlobalFn +// CHECK: Breakpoint 2: 3 locations. +// CHECK: (lldb) break set -n StaticFn +// CHECK: Breakpoint 3: where = simple-breakpoints.cpp.tmp.exe`StaticFn + 5 +// CHECK-SAME: at simple-breakpoints.cpp:23 +// CHECK: (lldb) break list +// CHECK: Current breakpoints: +// CHECK: 1: name = 'main', locations = 1 +// CHECK: 1.1: where = simple-breakpoints.cpp.tmp.exe`main + 21 +// CHECK-SAME: at simple-breakpoints.cpp:30 +// CHECK: 2: name = 'OvlGlobalFn', locations = 3 +// CHECK: 2.1: where = simple-breakpoints.cpp.tmp.exe`OvlGlobalFn + 5 +// CHECK-SAME: at simple-breakpoints.cpp:12 +// CHECK: 2.2: where = simple-breakpoints.cpp.tmp.exe`OvlGlobalFn +// CHECK-SAME: at simple-breakpoints.cpp:15 +// CHECK: 2.3: where = simple-breakpoints.cpp.tmp.exe`OvlGlobalFn + 17 +// CHECK-SAME: at simple-breakpoints.cpp:19 +// CHECK: 3: name = 'StaticFn', locations = 1 +// CHECK: 3.1: where = simple-breakpoints.cpp.tmp.exe`StaticFn + 5 +// CHECK-SAME: at simple-breakpoints.cpp:23 Index: lldb/lit/SymbolFile/NativePDB/source-list.cpp =================================================================== --- /dev/null +++ lldb/lit/SymbolFile/NativePDB/source-list.cpp @@ -0,0 +1,42 @@ +// clang-format off + +// Test that we can set display source of functions. +// RUN: clang-cl /Z7 /GS- /GR- /c %s /Fo%t.obj +// RUN: lld-link /DEBUG /nodefaultlib /entry:main /OUT:%t.exe /PDB:%t.pdb %t.obj +// RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb -f %t.exe -s \ +// RUN: %p/Inputs/source-list.lldbinit | FileCheck %s + + + +// Some context lines before +// the function. + + +int main(int argc, char **argv) { + // Here are some comments. + // That we should print when listing source. + return 0; +} + +// Some context lines after +// the function. + +// check lines go at the end so that line numbers stay stable when +// changing this file. + +// CHECK: (lldb) source list -n main +// CHECK: File: D:\src\llvm-mono\lldb\lit\SymbolFile\NativePDB\source-list.cpp +// CHECK: 10 +// CHECK: 11 // Some context lines before +// CHECK: 12 // the function. +// CHECK: 13 +// CHECK: 14 +// CHECK: 15 int main(int argc, char **argv) { +// CHECK: 16 // Here are some comments. +// CHECK: 17 // That we should print when listing source. +// CHECK: 18 return 0; +// CHECK: 19 } +// CHECK: 20 +// CHECK: 21 // Some context lines after +// CHECK: 22 // the function. +// CHECK: 23 Index: lldb/lit/lit.cfg =================================================================== --- lldb/lit/lit.cfg +++ lldb/lit/lit.cfg @@ -64,6 +64,8 @@ config.test_source_root) lldbmi = lit.util.which('lldb-mi', lldb_tools_dir) +if lldbmi: + config.available_features.add('lldb-mi') if not os.path.exists(config.cc): config.cc = lit.util.which(config.cc, config.environment['PATH']) @@ -90,7 +92,8 @@ config.substitutions.append(('%cc', config.cc)) config.substitutions.append(('%cxx', config.cxx)) -config.substitutions.append(('%lldbmi', lldbmi + " --synchronous")) +if lldbmi: + config.substitutions.append(('%lldbmi', lldbmi + " --synchronous")) config.substitutions.append(('%lldb', lldb)) if debugserver is not None: Index: lldb/source/Plugins/SymbolFile/CMakeLists.txt =================================================================== --- lldb/source/Plugins/SymbolFile/CMakeLists.txt +++ lldb/source/Plugins/SymbolFile/CMakeLists.txt @@ -1,3 +1,4 @@ add_subdirectory(DWARF) add_subdirectory(Symtab) +add_subdirectory(NativePDB) add_subdirectory(PDB) Index: lldb/source/Plugins/SymbolFile/NativePDB/CMakeLists.txt =================================================================== --- /dev/null +++ lldb/source/Plugins/SymbolFile/NativePDB/CMakeLists.txt @@ -0,0 +1,16 @@ +add_lldb_library(lldbPluginSymbolFileNativePDB PLUGIN + CompileUnitIndex.cpp + PdbIndex.cpp + PdbUtil.cpp + SymbolFileNativePDB.cpp + + LINK_LIBS + clangAST + clangLex + lldbCore + lldbSymbol + lldbUtility + LINK_COMPONENTS + DebugInfoPDB + Support + ) Index: lldb/source/Plugins/SymbolFile/NativePDB/CompileUnitIndex.h =================================================================== --- /dev/null +++ lldb/source/Plugins/SymbolFile/NativePDB/CompileUnitIndex.h @@ -0,0 +1,93 @@ +//===-- 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 +#include + +namespace lldb_private { + +namespace npdb { +class PdbIndex; + +struct CompilandIndexItem { + CompilandIndexItem(PdbSymUid uid, + llvm::pdb::ModuleDebugStreamRef debug_stream, + llvm::pdb::DbiModuleDescriptor descriptor); + + // uid of this compile unit. + PdbSymUid m_uid; + + // 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 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 m_symbols_by_va; + + // S_COMPILE3 sym describing compilation settings for the module. + llvm::Optional m_compile_opts; + + // S_OBJNAME sym describing object name. + llvm::Optional 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 m_build_info; +}; + +class CompileUnitIndex { + PdbIndex &m_index; + llvm::DenseMap> + m_comp_units; + +public: + explicit CompileUnitIndex(PdbIndex &index) : m_index(index) {} + + CompilandIndexItem &GetOrCreateCompiland(uint16_t modi); + CompilandIndexItem &GetOrCreateCompiland(PdbSymUid compiland_uid); + + const CompilandIndexItem *GetCompiland(uint16_t modi) const; + const CompilandIndexItem *GetCompiland(PdbSymUid compiland_uid) const; + + CompilandIndexItem *GetCompiland(uint16_t modi); + CompilandIndexItem *GetCompiland(PdbSymUid compiland_uid); + + llvm::SmallString<64> GetMainSourceFile(const CompilandIndexItem &item) const; +}; +} // namespace npdb +} // namespace lldb_private + +#endif Index: lldb/source/Plugins/SymbolFile/NativePDB/CompileUnitIndex.cpp =================================================================== --- /dev/null +++ lldb/source/Plugins/SymbolFile/NativePDB/CompileUnitIndex.cpp @@ -0,0 +1,224 @@ +//===-- 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" + +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; + + // FIXME: If we ever want to support PDB debug info for non-Windows systems + // the following check will be wrong, but we need a way to store the host + // information in the PDB. + llvm::SmallString<64> normalized(other); + llvm::sys::path::native(normalized, llvm::sys::path::Style::windows); + return main.equals_lower(normalized); +} + +static void ParseCompile3(const CVSymbol &sym, CompilandIndexItem &cci) { + cci.m_compile_opts.emplace(); + llvm::cantFail( + SymbolDeserializer::deserializeAs(sym, *cci.m_compile_opts)); +} + +static void ParseObjname(const CVSymbol &sym, CompilandIndexItem &cci) { + cci.m_obj_name.emplace(); + llvm::cantFail( + SymbolDeserializer::deserializeAs(sym, *cci.m_obj_name)); +} + +static void ParseBuildInfo(PdbIndex &index, const CVSymbol &sym, + CompilandIndexItem &cci) { + BuildInfoSym bis(SymbolRecordKind::BuildInfoSym); + llvm::cantFail(SymbolDeserializer::deserializeAs(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 cvt = types.tryGetType(bis.BuildId); + + if (!cvt || cvt->kind() != LF_BUILDINFO) + return; + + BuildInfoRecord bir; + llvm::cantFail(TypeDeserializer::deserializeAs(*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(); + + if (item.m_obj_name && item.m_compile_opts) + return; + + // 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( + PdbSymUid uid, llvm::pdb::ModuleDebugStreamRef debug_stream, + llvm::pdb::DbiModuleDescriptor descriptor) + : m_uid(uid), m_debug_stream(std::move(debug_stream)), + m_module_descriptor(std::move(descriptor)) {} + +CompilandIndexItem &CompileUnitIndex::GetOrCreateCompiland(uint16_t modi) { + PdbSymUid uid = PdbSymUid::makeCompilandId(modi); + return GetOrCreateCompiland(uid); +} + +CompilandIndexItem & +CompileUnitIndex::GetOrCreateCompiland(PdbSymUid compiland_uid) { + auto result = m_comp_units.try_emplace(compiland_uid.toOpaqueId(), 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(); + uint16_t modi = compiland_uid.asCompiland().modi; + llvm::pdb::DbiModuleDescriptor descriptor = modules.getModuleDescriptor(modi); + uint16_t stream = descriptor.getModuleStreamIndex(); + std::unique_ptr stream_data = + m_index.pdb().createIndexedStream(stream); + llvm::pdb::ModuleDebugStreamRef debug_stream(descriptor, + std::move(stream_data)); + cantFail(debug_stream.reload()); + + std::unique_ptr &cci = result.first->second; + + cci = llvm::make_unique( + compiland_uid, 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); + llvm::sys::path::native(main_file, llvm::sys::path::Style::windows); + + 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 { + return GetCompiland(PdbSymUid::makeCompilandId(modi)); +} + +const CompilandIndexItem * +CompileUnitIndex::GetCompiland(PdbSymUid compiland_uid) const { + auto iter = m_comp_units.find(compiland_uid.toOpaqueId()); + if (iter == m_comp_units.end()) + return nullptr; + return iter->second.get(); +} + +CompilandIndexItem *CompileUnitIndex::GetCompiland(uint16_t modi) { + return GetCompiland(PdbSymUid::makeCompilandId(modi)); +} + +CompilandIndexItem *CompileUnitIndex::GetCompiland(PdbSymUid compiland_uid) { + auto iter = m_comp_units.find(compiland_uid.toOpaqueId()); + 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(dir_cvt, working_dir)); + llvm::cantFail( + TypeDeserializer::deserializeAs(file_cvt, file_name)); + + llvm::SmallString<64> absolute_path = working_dir.String; + llvm::sys::path::append(absolute_path, file_name.String); + return absolute_path; +} Index: lldb/source/Plugins/SymbolFile/NativePDB/PdbIndex.h =================================================================== --- /dev/null +++ lldb/source/Plugins/SymbolFile/NativePDB/PdbIndex.h @@ -0,0 +1,156 @@ +//===-- 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 +#include + +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 { + +/// 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 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; + + /// 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; + llvm::pdb::TpiStream *m_ipi; + + /// 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; + + /// 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; + + /// 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; + + /// 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; + + CompileUnitIndex m_cus; + + /// An allocator for the interval maps + llvm::IntervalMap::Allocator m_allocator; + + /// Maps virtual address to module index + llvm::IntervalMap 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 std::unique_ptr create(std::unique_ptr); + + 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; + + std::vector FindSymbolsByVa(lldb::addr_t va); + + llvm::codeview::CVSymbol ReadSymbolRecord(PdbCuSymId cu_sym) const; + + llvm::Optional GetModuleIndexForAddr(uint16_t segment, + uint32_t offset) const; + llvm::Optional GetModuleIndexForVa(lldb::addr_t va) const; +}; +} // namespace npdb +} // namespace lldb_private + +#endif Index: lldb/source/Plugins/SymbolFile/NativePDB/PdbIndex.cpp =================================================================== --- /dev/null +++ lldb/source/Plugins/SymbolFile/NativePDB/PdbIndex.cpp @@ -0,0 +1,231 @@ +//===-- 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/Object/COFF.h" +#include "llvm/Support/Error.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) {} + +std::unique_ptr +PdbIndex::create(std::unique_ptr file) { + assert(file); + auto ExpectedDBI = file->getPDBDbiStream(); + if (!ExpectedDBI) { + llvm::consumeError(ExpectedDBI.takeError()); + return nullptr; + } + + auto ExpectedTPI = file->getPDBTpiStream(); + if (!ExpectedTPI) { + llvm::consumeError(ExpectedTPI.takeError()); + return nullptr; + } + + auto ExpectedIPI = file->getPDBIpiStream(); + if (!ExpectedIPI) { + llvm::consumeError(ExpectedIPI.takeError()); + return nullptr; + } + + auto ExpectedPublics = file->getPDBPublicsStream(); + if (!ExpectedPublics) { + llvm::consumeError(ExpectedPublics.takeError()); + return nullptr; + } + + auto ExpectedGlobals = file->getPDBGlobalsStream(); + if (!ExpectedGlobals) { + llvm::consumeError(ExpectedGlobals.takeError()); + return nullptr; + } + + auto ExpectedSymRecords = file->getPDBSymbolStream(); + if (!ExpectedSymRecords) { + llvm::consumeError(ExpectedSymRecords.takeError()); + return nullptr; + } + + auto ExpectedInfo = file->getPDBInfoStream(); + if (!ExpectedInfo) { + llvm::consumeError(ExpectedInfo.takeError()); + return nullptr; + } + + std::unique_ptr result(new PdbIndex()); + result->m_file = std::move(file); + result->m_dbi = &ExpectedDBI.get(); + result->m_tpi = &ExpectedTPI.get(); + result->m_ipi = &ExpectedIPI.get(); + result->m_info = &ExpectedInfo.get(); + result->m_publics = &ExpectedPublics.get(); + result->m_globals = &ExpectedGlobals.get(); + result->m_symrecords = &ExpectedSymRecords.get(); + + return std::move(result); +} + +lldb::addr_t PdbIndex::MakeVirtualAddress(uint16_t segment, + uint32_t offset) const { + // Segment indices are 1-based. + assert(segment > 0); + + uint32_t max_section = dbi().getSectionHeaders().size(); + assert(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(cs.VirtualAddress) + + static_cast(offset); +} + +llvm::Optional +PdbIndex::GetModuleIndexForAddr(uint16_t segment, uint32_t offset) const { + return GetModuleIndexForVa(MakeVirtualAddress(segment, offset)); +} + +llvm::Optional 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() { + struct Visitor : public ISectionContribVisitor { + Visitor(PdbIndex &ctx, llvm::IntervalMap &imap) + : m_ctx(ctx), m_imap(imap) {} + + void visit(const SectionContrib &C) override { + 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); } + + PdbIndex &m_ctx; + llvm::IntervalMap &m_imap; + }; + Visitor v(*this, m_va_to_modi); + dbi().visitSectionContributions(v); +} + +void PdbIndex::BuildAddrToSymbolMap(CompilandIndexItem &cci) { + assert(cci.m_symbols_by_va.empty() && "Addr to symbol map is already built!"); + uint16_t modi = cci.m_uid.asCompiland().modi; + const CVSymbolArray &syms = cci.m_debug_stream.getSymbolArray(); + for (auto iter = syms.begin(); iter != syms.end(); ++iter) { + if (!SymbolHasAddress(*iter)) + continue; + + uint16_t segment = 0; + uint32_t offset = 0; + std::tie(segment, offset) = GetSegmentAndOffset(*iter); + lldb::addr_t va = MakeVirtualAddress(segment, offset); + + // We need to add 4 here to adjust for the codeview debug magic + // at the beginning of the debug info stream. + uint32_t sym_offset = iter.offset() + 4; + PdbSymUid cu_sym_uid = + PdbSymUid::makeCuSymId(CVSymToPDBSym(iter->kind()), modi, sym_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. + auto emplace_result = cci.m_symbols_by_va.try_emplace(va, cu_sym_uid); + (void)emplace_result; + + // The odds of an error in some function such as GetSegmentAndOffset or + // MakeVirtualAddress are much higher than the odds of encountering bad + // debug info, so assert that this item was inserted in the map as opposed + // to having already been there. + assert(emplace_result.second); + } +} + +std::vector PdbIndex::FindSymbolsByVa(lldb::addr_t va) { + std::vector result; + + llvm::Optional 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) { + const PdbCuSymId &cu_sym_id = iter->second.asCuSym(); + CVSymbol sym = ReadSymbolRecord(cu_sym_id); + + uint16_t segment = 0; + uint32_t offset = 0; + uint32_t length = 0; + if (SymbolIsCode(sym)) + std::tie(segment, offset, length) = GetSegmentOffsetAndLength(sym); + else + std::tie(segment, offset) = GetSegmentAndOffset(sym); + + lldb::addr_t start = MakeVirtualAddress(segment, offset); + lldb::addr_t end = start + length; + if (va >= start && va < end) + result.push_back({std::move(sym), iter->second}); + } + + return result; +} + +CVSymbol PdbIndex::ReadSymbolRecord(PdbCuSymId cu_sym) const { + // We need to subtract 4 here to adjust for the codeview debug magic + // at the beginning of the debug info stream. + PdbSymUid cuid = PdbSymUid::makeCompilandId(cu_sym.modi); + const CompilandIndexItem *cci = compilands().GetCompiland(cuid); + auto iter = cci->m_debug_stream.getSymbolArray().at(cu_sym.offset - 4); + assert(iter != cci->m_debug_stream.getSymbolArray().end()); + return *iter; +} Index: lldb/source/Plugins/SymbolFile/NativePDB/PdbSymUid.h =================================================================== --- /dev/null +++ lldb/source/Plugins/SymbolFile/NativePDB/PdbSymUid.h @@ -0,0 +1,200 @@ +//===-- 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 "lldb/lldb-types.h" +#include "llvm/DebugInfo/PDB/PDBTypes.h" +#include "llvm/Support/Compiler.h" + +namespace lldb_private { +namespace npdb { + +// **important** - All concrete id types must have the 1-byte tag field at +// the beginning so that the types are all layout-compatible with each +// other, which is necessary in order to be able to safely access the tag +// member through any union member. + +struct PdbCompilandId { + uint64_t tag : 8; // PDB_SymType::Compiland + uint64_t modi : 16; // 0-based index of module in PDB + uint64_t unused : 32; +}; +struct PdbCuSymId { + uint64_t tag : 8; // PDB_SymType::Data, Function, Block, etc. + uint64_t + offset : 32; // 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. + uint64_t modi : 16; // 0-based index of module in PDB +}; +struct PdbTypeSymId { + uint64_t tag : 8; // PDB_SymType::FunctionSig, Enum, PointerType, etc. + uint64_t is_ipi : 8; // 1 if this value is from the IPI stream, 0 for TPI. + uint64_t unused : 16; + uint64_t index : 32; // codeview::TypeIndex +}; + +static_assert(sizeof(PdbCompilandId) == 8, "invalid uid size"); +static_assert(sizeof(PdbCuSymId) == 8, "invalid uid size"); +static_assert(std::is_standard_layout::value, + "type is not standard layout!"); +static_assert(std::is_standard_layout::value, + "type is not standard layout!"); + +class PdbSymUid { + union { + PdbCompilandId comp_id; + PdbCuSymId cu_sym; + PdbTypeSymId type_sym; + } m_uid; + + PdbSymUid() { ::memset(&m_uid, 0, sizeof(m_uid)); } + +public: + static bool isTypeSym(llvm::pdb::PDB_SymType tag) { + switch (tag) { + case llvm::pdb::PDB_SymType::ArrayType: + case llvm::pdb::PDB_SymType::BaseClass: + case llvm::pdb::PDB_SymType::BaseInterface: + case llvm::pdb::PDB_SymType::BuiltinType: + case llvm::pdb::PDB_SymType::CustomType: + case llvm::pdb::PDB_SymType::Enum: + case llvm::pdb::PDB_SymType::FunctionArg: + case llvm::pdb::PDB_SymType::FunctionSig: + case llvm::pdb::PDB_SymType::Typedef: + case llvm::pdb::PDB_SymType::VectorType: + case llvm::pdb::PDB_SymType::VTableShape: + case llvm::pdb::PDB_SymType::PointerType: + case llvm::pdb::PDB_SymType::UDT: + return true; + default: + return false; + } + } + + static bool isCuSym(llvm::pdb::PDB_SymType tag) { + switch (tag) { + case llvm::pdb::PDB_SymType::Block: + case llvm::pdb::PDB_SymType::Callee: + case llvm::pdb::PDB_SymType::Caller: + case llvm::pdb::PDB_SymType::CallSite: + case llvm::pdb::PDB_SymType::CoffGroup: + case llvm::pdb::PDB_SymType::CompilandDetails: + case llvm::pdb::PDB_SymType::CompilandEnv: + case llvm::pdb::PDB_SymType::Custom: + case llvm::pdb::PDB_SymType::Data: + case llvm::pdb::PDB_SymType::Function: + case llvm::pdb::PDB_SymType::Inlinee: + case llvm::pdb::PDB_SymType::InlineSite: + case llvm::pdb::PDB_SymType::Label: + case llvm::pdb::PDB_SymType::Thunk: + return true; + default: + return false; + } + } + + static PdbSymUid makeCuSymId(llvm::codeview::ProcRefSym sym) { + return makeCuSymId(llvm::pdb::PDB_SymType::Function, sym.Module - 1, + sym.SymOffset); + } + + static PdbSymUid makeCuSymId(llvm::pdb::PDB_SymType type, uint16_t modi, + uint32_t offset) { + assert(isCuSym(type)); + + PdbSymUid uid; + uid.m_uid.cu_sym.modi = modi; + uid.m_uid.cu_sym.offset = offset; + uid.m_uid.cu_sym.tag = static_cast(type); + return uid; + } + + static PdbSymUid makeCompilandId(llvm::codeview::ProcRefSym sym) { + // S_PROCREF symbols are 1-based + assert(sym.Module > 0); + return makeCompilandId(sym.Module - 1); + } + + static PdbSymUid makeCompilandId(uint16_t modi) { + PdbSymUid uid; + uid.m_uid.comp_id.modi = modi; + uid.m_uid.cu_sym.tag = + static_cast(llvm::pdb::PDB_SymType::Compiland); + return uid; + } + + static PdbSymUid makeTypeSymId(llvm::pdb::PDB_SymType type, + llvm::codeview::TypeIndex index, bool is_ipi) { + assert(isTypeSym(type)); + + PdbSymUid uid; + uid.m_uid.type_sym.tag = static_cast(type); + uid.m_uid.type_sym.index = index.getIndex(); + uid.m_uid.type_sym.is_ipi = static_cast(is_ipi); + return uid; + } + + static PdbSymUid fromOpaqueId(uint64_t value) { + PdbSymUid result; + ::memcpy(&result.m_uid, &value, sizeof(value)); + return result; + } + + uint64_t toOpaqueId() const { + uint64_t result; + ::memcpy(&result, &m_uid, sizeof(m_uid)); + return result; + } + + bool isPubSym() const { + return tag() == llvm::pdb::PDB_SymType::PublicSymbol; + } + bool isCompiland() const { + return tag() == llvm::pdb::PDB_SymType::Compiland; + } + + llvm::pdb::PDB_SymType tag() const { + return static_cast(m_uid.comp_id.tag); + } + + const PdbCompilandId &asCompiland() const { + assert(tag() == llvm::pdb::PDB_SymType::Compiland); + return m_uid.comp_id; + } + + const PdbCuSymId &asCuSym() const { + assert(isCuSym(tag())); + return m_uid.cu_sym; + } + + const PdbTypeSymId &asTypeSym() const { + assert(isTypeSym(tag())); + return m_uid.type_sym; + } +}; + +struct SymbolAndUid { + llvm::codeview::CVSymbol sym; + PdbSymUid uid; +}; +} // namespace npdb +} // namespace lldb_private + +#endif Index: lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.h =================================================================== --- /dev/null +++ lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.h @@ -0,0 +1,43 @@ +//===-- 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 "llvm/DebugInfo/CodeView/SymbolRecord.h" +#include "llvm/DebugInfo/PDB/PDBTypes.h" + +#include +#include + +namespace lldb_private { +namespace npdb { +llvm::pdb::PDB_SymType CVSymToPDBSym(llvm::codeview::SymbolKind kind); + +bool SymbolHasAddress(const llvm::codeview::CVSymbol &sym); +bool SymbolIsCode(const llvm::codeview::CVSymbol &sym); + +std::pair +GetSegmentAndOffset(const llvm::codeview::CVSymbol &sym); +std::tuple +GetSegmentOffsetAndLength(const llvm::codeview::CVSymbol &sym); + +template 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; +} + +} // namespace npdb +} // namespace lldb_private + +#endif Index: lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp =================================================================== --- /dev/null +++ lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp @@ -0,0 +1,266 @@ +//===-- 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 "llvm/DebugInfo/CodeView/SymbolDeserializer.h" + +using namespace lldb_private; +using namespace lldb_private::npdb; +using namespace llvm::codeview; +using namespace llvm::pdb; + +llvm::pdb::PDB_SymType +lldb_private::npdb::CVSymToPDBSym(llvm::codeview::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: + assert(false && "Invalid symbol record kind!"); + } + return PDB_SymType::None; +} + +bool lldb_private::npdb::SymbolHasAddress(const llvm::codeview::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 llvm::codeview::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 RecordT createRecord(const CVSymbol &sym) { + RecordT record(static_cast(sym.kind())); + cantFail(SymbolDeserializer::deserializeAs(sym, record)); + return record; +} + +template +static std::pair GetSegmentAndOffset(const CVSymbol &sym) { + RecordT record = createRecord(sym); + return {record.Segment, record.CodeOffset}; +} + +template <> +std::pair +GetSegmentAndOffset(const CVSymbol &sym) { + TrampolineSym record = createRecord(sym); + return {record.ThunkSection, record.ThunkOffset}; +} + +template <> +std::pair +GetSegmentAndOffset(const CVSymbol &sym) { + Thunk32Sym record = createRecord(sym); + return {record.Segment, record.Offset}; +} + +template <> +std::pair +GetSegmentAndOffset(const CVSymbol &sym) { + CoffGroupSym record = createRecord(sym); + return {record.Segment, record.Offset}; +} + +template <> +std::pair +GetSegmentAndOffset(const CVSymbol &sym) { + DataSym record = createRecord(sym); + return {record.Segment, record.DataOffset}; +} + +template <> +std::pair +GetSegmentAndOffset(const CVSymbol &sym) { + ThreadLocalDataSym record = createRecord(sym); + return {record.Segment, record.DataOffset}; +} + +std::pair +lldb_private::npdb::GetSegmentAndOffset(const llvm::codeview::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(sym); + case S_THUNK32: + return ::GetSegmentAndOffset(sym); + break; + case S_TRAMPOLINE: + return ::GetSegmentAndOffset(sym); + break; + case S_COFFGROUP: + return ::GetSegmentAndOffset(sym); + break; + case S_BLOCK32: + return ::GetSegmentAndOffset(sym); + break; + case S_LABEL32: + return ::GetSegmentAndOffset(sym); + break; + case S_CALLSITEINFO: + return ::GetSegmentAndOffset(sym); + break; + case S_HEAPALLOCSITE: + return ::GetSegmentAndOffset(sym); + break; + case S_LDATA32: + case S_GDATA32: + case S_LMANDATA: + case S_GMANDATA: + return ::GetSegmentAndOffset(sym); + break; + case S_LTHREAD32: + case S_GTHREAD32: + return ::GetSegmentAndOffset(sym); + break; + default: + assert(false && "Record does not have a segment/offset!"); + } + return {0, 0}; +} + +template +static std::tuple +GetSegmentOffsetAndLength(const CVSymbol &sym) { + RecordT record = createRecord(sym); + return {record.Segment, record.CodeOffset, record.CodeSize}; +} + +template <> +std::tuple +GetSegmentOffsetAndLength(const CVSymbol &sym) { + TrampolineSym record = createRecord(sym); + return {record.ThunkSection, record.ThunkOffset, record.Size}; +} + +template <> +std::tuple +GetSegmentOffsetAndLength(const CVSymbol &sym) { + Thunk32Sym record = createRecord(sym); + return {record.Segment, record.Offset, record.Length}; +} + +template <> +std::tuple +GetSegmentOffsetAndLength(const CVSymbol &sym) { + CoffGroupSym record = createRecord(sym); + return {record.Segment, record.Offset, record.Size}; +} + +std::tuple +lldb_private::npdb::GetSegmentOffsetAndLength( + const llvm::codeview::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(sym); + case S_THUNK32: + return ::GetSegmentOffsetAndLength(sym); + break; + case S_TRAMPOLINE: + return ::GetSegmentOffsetAndLength(sym); + break; + case S_COFFGROUP: + return ::GetSegmentOffsetAndLength(sym); + break; + case S_BLOCK32: + return ::GetSegmentOffsetAndLength(sym); + break; + default: + assert(false && "Record does not have a segment/offset/length triple!"); + } + return {0, 0, 0}; +} Index: lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h =================================================================== --- /dev/null +++ lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h @@ -0,0 +1,181 @@ +//===-- 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_PDB_SymbolFileNativePDB_h_ +#define lldb_Plugins_SymbolFile_PDB_SymbolFileNativePDB_h_ + +#include "lldb/Core/UniqueCStringMap.h" +#include "lldb/Symbol/SymbolFile.h" +#include "lldb/Symbol/VariableList.h" +#include "lldb/Utility/UserID.h" + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/Optional.h" +#include "llvm/DebugInfo/CodeView/CVRecord.h" +#include "llvm/DebugInfo/CodeView/StringsAndChecksums.h" +#include "llvm/DebugInfo/CodeView/SymbolRecord.h" +#include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h" +#include "llvm/DebugInfo/PDB/PDBTypes.h" + +#include "CompileUnitIndex.h" +#include "PdbIndex.h" + +#include + +namespace llvm { +namespace pdb { +class PDBFile; +class PDBSymbol; +class PDBSymbolCompiland; +class PDBSymbolData; +class PDBSymbolFunc; + +class DbiStream; +class TpiStream; +class TpiStream; +class InfoStream; +class PublicsStream; +class GlobalsStream; +class SymbolStream; +class ModuleDebugStreamRef; +} // namespace pdb +} // namespace llvm + +namespace lldb_private { +namespace npdb { + +class SymbolFileNativePDB : public lldb_private::SymbolFile { +public: + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void Initialize(); + + static void Terminate(); + + static void DebuggerInitialize(lldb_private::Debugger &debugger); + + static lldb_private::ConstString GetPluginNameStatic(); + + static const char *GetPluginDescriptionStatic(); + + static lldb_private::SymbolFile * + CreateInstance(lldb_private::ObjectFile *obj_file); + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + SymbolFileNativePDB(lldb_private::ObjectFile *ofile); + + ~SymbolFileNativePDB() override; + + uint32_t CalculateAbilities() override; + + void InitializeObject() override; + + //------------------------------------------------------------------ + // Compile Unit function calls + //------------------------------------------------------------------ + + uint32_t GetNumCompileUnits() override; + + lldb::CompUnitSP ParseCompileUnitAtIndex(uint32_t index) override; + + lldb::LanguageType + ParseCompileUnitLanguage(const lldb_private::SymbolContext &sc) override; + + size_t + ParseCompileUnitFunctions(const lldb_private::SymbolContext &sc) override; + + bool + ParseCompileUnitLineTable(const lldb_private::SymbolContext &sc) override; + + bool + ParseCompileUnitDebugMacros(const lldb_private::SymbolContext &sc) override; + + bool ParseCompileUnitSupportFiles( + const lldb_private::SymbolContext &sc, + lldb_private::FileSpecList &support_files) override; + + bool ParseImportedModules( + const lldb_private::SymbolContext &sc, + std::vector &imported_modules) override; + + size_t ParseFunctionBlocks(const lldb_private::SymbolContext &sc) override; + + size_t ParseTypes(const lldb_private::SymbolContext &sc) override { + return 0; + } + size_t + ParseVariablesForContext(const lldb_private::SymbolContext &sc) override { + return 0; + } + lldb_private::Type *ResolveTypeUID(lldb::user_id_t type_uid) override { + return nullptr; + } + bool CompleteType(lldb_private::CompilerType &compiler_type) override { + return false; + } + uint32_t ResolveSymbolContext(const lldb_private::Address &so_addr, + uint32_t resolve_scope, + lldb_private::SymbolContext &sc) override; + + virtual size_t GetTypes(lldb_private::SymbolContextScope *sc_scope, + uint32_t type_mask, + lldb_private::TypeList &type_list) override { + return 0; + } + + uint32_t + FindFunctions(const lldb_private::ConstString &name, + const lldb_private::CompilerDeclContext *parent_decl_ctx, + uint32_t name_type_mask, bool include_inlines, bool append, + lldb_private::SymbolContextList &sc_list) override; + + uint32_t FindFunctions(const lldb_private::RegularExpression ®ex, + bool include_inlines, bool append, + lldb_private::SymbolContextList &sc_list) override; + + lldb_private::TypeSystem * + GetTypeSystemForLanguage(lldb::LanguageType language) override; + + lldb_private::CompilerDeclContext FindNamespace( + const lldb_private::SymbolContext &sc, + const lldb_private::ConstString &name, + const lldb_private::CompilerDeclContext *parent_decl_ctx) override; + + lldb_private::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(); } + +private: + lldb::FunctionSP GetOrCreateFunction(PdbSymUid func_uid, + const SymbolContext &sc); + lldb::CompUnitSP GetOrCreateCompileUnit(const CompilandIndexItem &cci); + + lldb::FunctionSP CreateFunction(PdbSymUid func_uid, const SymbolContext &sc); + lldb::CompUnitSP CreateCompileUnit(const CompilandIndexItem &cci); + + llvm::BumpPtrAllocator m_allocator; + + lldb::addr_t m_obj_load_address = 0; + + std::unique_ptr m_index; + + llvm::DenseMap m_functions; + llvm::DenseMap m_compilands; +}; + +} // namespace npdb +} // namespace lldb_private + +#endif // lldb_Plugins_SymbolFile_PDB_SymbolFilePDB_h_ Index: lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp =================================================================== --- /dev/null +++ lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp @@ -0,0 +1,617 @@ +//===-- 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/Lex/Lexer.h" + +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/LineTable.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/SymbolVendor.h" + +#include "llvm/DebugInfo/CodeView/CVRecord.h" +#include "llvm/DebugInfo/CodeView/DebugLinesSubsection.h" +#include "llvm/DebugInfo/CodeView/RecordName.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/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/PDBTypes.h" +#include "llvm/Object/COFF.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/MemoryBuffer.h" + +#include "PdbSymUid.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 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 loadPDBFile(std::string PdbPath, + llvm::BumpPtrAllocator &Allocator) { + llvm::ErrorOr> ErrorOrBuffer = + llvm::MemoryBuffer::getFile(PdbPath, /*FileSize=*/-1, + /*RequiresNullTerminator=*/false); + if (!ErrorOrBuffer) + return nullptr; + std::unique_ptr Buffer = std::move(*ErrorOrBuffer); + + llvm::StringRef Path = Buffer->getBufferIdentifier(); + auto Stream = llvm::make_unique( + std::move(Buffer), llvm::support::little); + + auto File = llvm::make_unique(Path, std::move(Stream), Allocator); + if (auto EC = File->parseFileHeaders()) + return nullptr; + if (auto EC = File->parseStreamData()) + return nullptr; + + return std::move(File); +} + +static std::unique_ptr +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 = std::move(*expected_binary); + + auto *obj = llvm::dyn_cast(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 pdb = loadPDBFile(pdb_file, allocator); + 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 std::move(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; +} + +void SymbolFileNativePDB::Initialize() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), CreateInstance, + DebuggerInitialize); +} + +void SymbolFileNativePDB::Terminate() { + PluginManager::UnregisterPlugin(CreateInstance); +} + +void SymbolFileNativePDB::DebuggerInitialize(lldb_private::Debugger &debugger) { +} + +lldb_private::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."; +} + +lldb_private::SymbolFile * +SymbolFileNativePDB::CreateInstance(lldb_private::ObjectFile *obj_file) { + return new SymbolFileNativePDB(obj_file); +} + +SymbolFileNativePDB::SymbolFileNativePDB(lldb_private::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 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; + + m_index = PdbIndex::create(std::move(file_up)); + } + 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(); +} + +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; +} + +lldb::FunctionSP SymbolFileNativePDB::CreateFunction(PdbSymUid func_uid, + const SymbolContext &sc) { + assert(func_uid.tag() == PDB_SymType::Function); + + uint16_t segment = 0; + uint32_t offset = 0; + uint32_t length = 0; + PdbSymUid cuid = PdbSymUid::makeCompilandId(func_uid.asCuSym().modi); + + const CompilandIndexItem *cci = m_index->compilands().GetCompiland(cuid); + assert(cci); + CVSymbol sym_record = + cci->m_debug_stream.readSymbolAtOffset(func_uid.asCuSym().offset); + + assert(sym_record.kind() == S_LPROC32 || sym_record.kind() == S_GPROC32); + std::tie(segment, offset, length) = GetSegmentOffsetAndLength(sym_record); + + auto file_vm_addr = m_index->MakeVirtualAddress(segment, offset); + if (file_vm_addr == LLDB_INVALID_ADDRESS || file_vm_addr == 0) + return nullptr; + + AddressRange func_range(file_vm_addr, length, sc.module_sp->GetSectionList()); + if (!func_range.GetBaseAddress().IsValid()) + return nullptr; + + lldb_private::Type *func_type = nullptr; + + // FIXME: Resolve types and mangled names. + PdbSymUid sig_uid = + PdbSymUid::makeTypeSymId(PDB_SymType::FunctionSig, TypeIndex{0}, false); + Mangled mangled(getSymbolName(sym_record)); + + FunctionSP func_sp = std::make_shared( + sc.comp_unit, func_uid.toOpaqueId(), sig_uid.toOpaqueId(), mangled, + func_type, func_range); + + sc.comp_unit->AddFunction(func_sp); + 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::StringRef source_file_name = + m_index->compilands().GetMainSourceFile(cci); + lldb_private::FileSpec fs(source_file_name, false); + + CompUnitSP cu_sp = + std::make_shared(m_obj_file->GetModule(), nullptr, fs, + cci.m_uid.toOpaqueId(), lang, optimized); + + const PdbCompilandId &cuid = cci.m_uid.asCompiland(); + m_obj_file->GetModule()->GetSymbolVendor()->SetCompileUnitAtIndex(cuid.modi, + cu_sp); + return cu_sp; +} + +FunctionSP SymbolFileNativePDB::GetOrCreateFunction(PdbSymUid func_uid, + const SymbolContext &sc) { + assert(func_uid.tag() == PDB_SymType::Function); + auto emplace_result = m_functions.try_emplace(func_uid.toOpaqueId(), nullptr); + if (emplace_result.second) + emplace_result.first->second = CreateFunction(func_uid, sc); + + assert(emplace_result.first->second); + return emplace_result.first->second; +} + +CompUnitSP +SymbolFileNativePDB::GetOrCreateCompileUnit(const CompilandIndexItem &cci) { + auto emplace_result = + m_compilands.try_emplace(cci.m_uid.toOpaqueId(), nullptr); + if (emplace_result.second) + emplace_result.first->second = CreateCompileUnit(cci); + + assert(emplace_result.first->second); + return emplace_result.first->second; +} + +lldb::CompUnitSP SymbolFileNativePDB::ParseCompileUnitAtIndex(uint32_t index) { + if (index >= GetNumCompileUnits()) + return CompUnitSP(); + assert(index < UINT16_MAX); + if (index >= UINT16_MAX) + return nullptr; + + CompilandIndexItem &item = m_index->compilands().GetOrCreateCompiland(index); + + return GetOrCreateCompileUnit(item); +} + +lldb::LanguageType SymbolFileNativePDB::ParseCompileUnitLanguage( + const lldb_private::SymbolContext &sc) { + // What fields should I expect to be filled out on the SymbolContext? Is it + // safe to assume that `sc.comp_unit` is valid? + if (!sc.comp_unit) + return lldb::eLanguageTypeUnknown; + PdbSymUid uid = PdbSymUid::fromOpaqueId(sc.comp_unit->GetID()); + assert(uid.tag() == PDB_SymType::Compiland); + + CompilandIndexItem *item = m_index->compilands().GetCompiland(uid); + assert(item); + if (!item->m_compile_opts) + return lldb::eLanguageTypeUnknown; + + return TranslateLanguage(item->m_compile_opts->getLanguage()); +} + +size_t SymbolFileNativePDB::ParseCompileUnitFunctions( + const lldb_private::SymbolContext &sc) { + lldbassert(sc.comp_unit); + return false; +} + +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 lldb_private::Address &addr, + uint32_t resolve_scope, + lldb_private::SymbolContext &sc) { + uint32_t resolved_flags = 0; + lldb::addr_t file_addr = addr.GetFileAddress(); + + if (NeedsResolvedCompileUnit(resolve_scope)) { + llvm::Optional modi = m_index->GetModuleIndexForVa(file_addr); + if (!modi) + return 0; + PdbSymUid cuid = PdbSymUid::makeCompilandId(*modi); + CompilandIndexItem *cci = m_index->compilands().GetCompiland(cuid); + if (!cci) + return 0; + + sc.comp_unit = GetOrCreateCompileUnit(*cci).get(); + resolved_flags |= eSymbolContextCompUnit; + } + + if (resolve_scope & eSymbolContextFunction) { + assert(sc.comp_unit); + std::vector matches = m_index->FindSymbolsByVa(file_addr); + for (const auto &match : matches) { + if (match.uid.tag() != PDB_SymType::Function) + continue; + sc.function = GetOrCreateFunction(match.uid, sc).get(); + } + resolved_flags |= eSymbolContextFunction; + } + + if (resolve_scope & eSymbolContextLineEntry) { + assert(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; +} + +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 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::ParseCompileUnitLineTable( + const lldb_private::SymbolContext &sc) { + // 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. + lldbassert(sc.comp_unit); + PdbSymUid cu_id = PdbSymUid::fromOpaqueId(sc.comp_unit->GetID()); + assert(cu_id.isCompiland()); + CompilandIndexItem *cci = m_index->compilands().GetCompiland(cu_id); + assert(cci); + auto line_table = llvm::make_unique(sc.comp_unit); + + llvm::StringMap file_index_lookup; + + // This is basically a copy of the .debug$S subsections from the original COFF + // object file. 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 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. + llvm::StringRef file_name = *efn; + auto fn_iter = llvm::find(cci->m_file_list, *efn); + assert(fn_iter != cci->m_file_list.end()); + uint32_t file_index = std::distance(cci->m_file_list.begin(), fn_iter); + + std::unique_ptr sequence( + line_table->CreateLineSequenceContainer()); + assert(!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; + + sc.comp_unit->SetLineTable(line_table.release()); + return true; +} + +bool SymbolFileNativePDB::ParseCompileUnitDebugMacros( + const lldb_private::SymbolContext &sc) { + // PDB doesn't contain information about macros + return false; +} + +bool SymbolFileNativePDB::ParseCompileUnitSupportFiles( + const lldb_private::SymbolContext &sc, + lldb_private::FileSpecList &support_files) { + lldbassert(sc.comp_unit); + + PdbSymUid comp_uid = PdbSymUid::fromOpaqueId(sc.comp_unit->GetID()); + assert(comp_uid.tag() == PDB_SymType::Compiland); + + const CompilandIndexItem *cci = m_index->compilands().GetCompiland(comp_uid); + assert(cci); + + for (llvm::StringRef f : cci->m_file_list) { + FileSpec spec(f, false, FileSpec::Style::windows); + support_files.Append(spec); + } + + return true; +} + +bool SymbolFileNativePDB::ParseImportedModules( + const lldb_private::SymbolContext &sc, + std::vector &imported_modules) { + // PDB does not yet support module debug info + return false; +} + +size_t SymbolFileNativePDB::ParseFunctionBlocks( + const lldb_private::SymbolContext &sc) { + lldbassert(sc.comp_unit && sc.function); + return 0; +} + +uint32_t SymbolFileNativePDB::FindFunctions( + const lldb_private::ConstString &name, + const lldb_private::CompilerDeclContext *parent_decl_ctx, + uint32_t name_type_mask, bool include_inlines, bool append, + lldb_private::SymbolContextList &sc_list) { + // For now we only support lookup by method name. + if (!(name_type_mask & eFunctionNameTypeMethod)) + return 0; + + using SymbolAndOffset = std::pair; + + std::vector 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(match.second, proc)); + + if (!IsValidRecord(proc)) + continue; + + PdbSymUid cuid = PdbSymUid::makeCompilandId(proc); + CompilandIndexItem &cci = m_index->compilands().GetOrCreateCompiland(cuid); + lldb_private::SymbolContext sc; + + sc.comp_unit = GetOrCreateCompileUnit(cci).get(); + sc.module_sp = sc.comp_unit->GetModule(); + PdbSymUid func_uid = PdbSymUid::makeCuSymId(proc); + sc.function = GetOrCreateFunction(func_uid, sc).get(); + + sc_list.Append(sc); + } + + return sc_list.GetSize(); +} + +uint32_t +SymbolFileNativePDB::FindFunctions(const lldb_private::RegularExpression ®ex, + bool include_inlines, bool append, + lldb_private::SymbolContextList &sc_list) { + return 0; +} + +lldb_private::CompilerDeclContext SymbolFileNativePDB::FindNamespace( + const lldb_private::SymbolContext &sc, + const lldb_private::ConstString &name, + const lldb_private::CompilerDeclContext *parent_decl_ctx) { + return {}; +} + +lldb_private::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; +} + +lldb_private::ConstString SymbolFileNativePDB::GetPluginName() { + static ConstString g_name("pdb"); + return g_name; +} + +uint32_t SymbolFileNativePDB::GetPluginVersion() { return 1; } Index: lldb/source/Plugins/SymbolFile/PDB/CMakeLists.txt =================================================================== --- lldb/source/Plugins/SymbolFile/PDB/CMakeLists.txt +++ lldb/source/Plugins/SymbolFile/PDB/CMakeLists.txt @@ -9,6 +9,7 @@ lldbCore lldbSymbol lldbUtility + lldbPluginSymbolFileNativePDB LINK_COMPONENTS DebugInfoPDB Support Index: lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp =================================================================== --- lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp +++ lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp @@ -46,6 +46,7 @@ #include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h" #include "Plugins/Language/CPlusPlus/CPlusPlusLanguage.h" // For IsCPPMangledName +#include "Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h" #include "Plugins/SymbolFile/PDB/PDBASTParser.h" #include "Plugins/SymbolFile/PDB/PDBLocationToDWARFExpression.h" @@ -74,14 +75,31 @@ } } // namespace +static bool ShouldUseNativeReader() { +#if !defined(_WIN32) + return true; +#endif + llvm::StringRef use_native = ::getenv("LLDB_USE_NATIVE_PDB_READER"); + return use_native.equals_lower("on") || use_native.equals_lower("yes") || + use_native.equals_lower("1") || use_native.equals_lower("true"); +} + void SymbolFilePDB::Initialize() { - PluginManager::RegisterPlugin(GetPluginNameStatic(), - GetPluginDescriptionStatic(), CreateInstance, - DebuggerInitialize); + if (ShouldUseNativeReader()) { + npdb::SymbolFileNativePDB::Initialize(); + } else { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), CreateInstance, + DebuggerInitialize); + } } void SymbolFilePDB::Terminate() { - PluginManager::UnregisterPlugin(CreateInstance); + if (ShouldUseNativeReader()) { + npdb::SymbolFileNativePDB::Terminate(); + } else { + PluginManager::UnregisterPlugin(CreateInstance); + } } void SymbolFilePDB::DebuggerInitialize(lldb_private::Debugger &debugger) {} Index: llvm/test/DebugInfo/PDB/pdbdump-global-lookup.test =================================================================== --- llvm/test/DebugInfo/PDB/pdbdump-global-lookup.test +++ llvm/test/DebugInfo/PDB/pdbdump-global-lookup.test @@ -10,23 +10,23 @@ CHECK: Global Symbols CHECK-NEXT: ============================================================ -CHECK-NEXT: Global Name `main` -CHECK-NEXT: 344 | S_PROCREF [size = 20] `main` -CHECK-NEXT: module = 1, sum name = 0, offset = 780 -CHECK-NEXT: Global Name `operator delete` -CHECK-NEXT: 228 | S_PROCREF [size = 32] `operator delete` -CHECK-NEXT: module = 1, sum name = 0, offset = 200 -CHECK-NEXT: 196 | S_PROCREF [size = 32] `operator delete` -CHECK-NEXT: module = 1, sum name = 0, offset = 52 -CHECK-NEXT: Global Name `abcdefg` -CHECK-NEXT: (no matching records found) - +CHECK-NEXT: Global Name `main` +CHECK-NEXT: 344 | S_PROCREF [size = 20] `main` +CHECK-NEXT: module = 1, sum name = 0, offset = 780 +CHECK-NEXT: Global Name `operator delete` +CHECK-NEXT: 228 | S_PROCREF [size = 32] `operator delete` +CHECK-NEXT: module = 1, sum name = 0, offset = 200 +CHECK-NEXT: 196 | S_PROCREF [size = 32] `operator delete` +CHECK-NEXT: module = 1, sum name = 0, offset = 52 +CHECK-NEXT: Global Name `abcdefg` +CHECK-NEXT: (no matching records found) + LASTBUCKET: Global Symbols LASTBUCKET-NEXT: ============================================================ -LASTBUCKET-NEXT: Global Name `OvlGlobalFn` -LASTBUCKET-NEXT: 316 | S_PROCREF [size = 28] `OvlGlobalFn` -LASTBUCKET-NEXT: module = 1, sum name = 0, offset = 608 -LASTBUCKET-NEXT: 288 | S_PROCREF [size = 28] `OvlGlobalFn` -LASTBUCKET-NEXT: module = 1, sum name = 0, offset = 464 -LASTBUCKET-NEXT: 260 | S_PROCREF [size = 28] `OvlGlobalFn` -LASTBUCKET-NEXT: module = 1, sum name = 0, offset = 348 +LASTBUCKET-NEXT: Global Name `OvlGlobalFn` +LASTBUCKET-NEXT: 316 | S_PROCREF [size = 28] `OvlGlobalFn` +LASTBUCKET-NEXT: module = 1, sum name = 0, offset = 608 +LASTBUCKET-NEXT: 288 | S_PROCREF [size = 28] `OvlGlobalFn` +LASTBUCKET-NEXT: module = 1, sum name = 0, offset = 464 +LASTBUCKET-NEXT: 260 | S_PROCREF [size = 28] `OvlGlobalFn` +LASTBUCKET-NEXT: module = 1, sum name = 0, offset = 348