| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,229 @@ | ||
| //===-- 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; | ||
|
|
||
| // 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<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( | ||
| 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<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>( | ||
| 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<StringIdRecord>(dir_cvt, working_dir)); | ||
| llvm::cantFail( | ||
| TypeDeserializer::deserializeAs<StringIdRecord>(file_cvt, file_name)); | ||
|
|
||
| llvm::SmallString<64> absolute_path = working_dir.String; | ||
| llvm::sys::path::append(absolute_path, file_name.String); | ||
| return absolute_path; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,99 @@ | ||
| //===-- 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(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<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<lldb::user_id_t, std::unique_ptr<CompilandIndexItem>> | ||
| 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 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,202 @@ | ||
| //===-- 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/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_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 { | ||
| 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_uid.asCompiland().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); | ||
|
|
||
| // 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. | ||
| lldbassert(emplace_result.second); | ||
| } | ||
| } | ||
|
|
||
| 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) { | ||
| const PdbCuSymId &cu_sym_id = iter->second.asCuSym(); | ||
| 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(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); | ||
| lldbassert(iter != cci->m_debug_stream.getSymbolArray().end()); | ||
| return *iter; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,161 @@ | ||
| //===-- 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(PdbCuSymId cu_sym) 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 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,202 @@ | ||
| //===-- 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/PDB/PDBTypes.h" | ||
| #include "llvm/Support/Compiler.h" | ||
|
|
||
| #include "lldb/Utility/LLDBAssert.h" | ||
| #include "lldb/lldb-types.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<PdbCompilandId>::value, | ||
| "type is not standard layout!"); | ||
| static_assert(std::is_standard_layout<PdbCuSymId>::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) { | ||
| lldbassert(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<uint8_t>(type); | ||
| return uid; | ||
| } | ||
|
|
||
| static PdbSymUid makeCompilandId(llvm::codeview::ProcRefSym sym) { | ||
| // S_PROCREF symbols are 1-based | ||
| lldbassert(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<uint8_t>(llvm::pdb::PDB_SymType::Compiland); | ||
| return uid; | ||
| } | ||
|
|
||
| static PdbSymUid makeTypeSymId(llvm::pdb::PDB_SymType type, | ||
| llvm::codeview::TypeIndex index, bool is_ipi) { | ||
| lldbassert(isTypeSym(type)); | ||
|
|
||
| PdbSymUid uid; | ||
| uid.m_uid.type_sym.tag = static_cast<uint8_t>(type); | ||
| uid.m_uid.type_sym.index = index.getIndex(); | ||
| uid.m_uid.type_sym.is_ipi = static_cast<uint8_t>(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<llvm::pdb::PDB_SymType>(m_uid.comp_id.tag); | ||
| } | ||
|
|
||
| const PdbCompilandId &asCompiland() const { | ||
| lldbassert(tag() == llvm::pdb::PDB_SymType::Compiland); | ||
| return m_uid.comp_id; | ||
| } | ||
|
|
||
| const PdbCuSymId &asCuSym() const { | ||
| lldbassert(isCuSym(tag())); | ||
| return m_uid.cu_sym; | ||
| } | ||
|
|
||
| const PdbTypeSymId &asTypeSym() const { | ||
| lldbassert(isTypeSym(tag())); | ||
| return m_uid.type_sym; | ||
| } | ||
| }; | ||
|
|
||
| struct SymbolAndUid { | ||
| llvm::codeview::CVSymbol sym; | ||
| PdbSymUid uid; | ||
| }; | ||
| } // namespace npdb | ||
| } // namespace lldb_private | ||
|
|
||
| #endif |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,258 @@ | ||
| //===-- 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" | ||
|
|
||
| #include "lldb/Utility/LLDBAssert.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: | ||
| lldbassert(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 <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 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<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 {record.Segment, record.Offset, record.Length}; | ||
| } | ||
|
|
||
| template <> | ||
| SegmentOffsetLength | ||
| GetSegmentOffsetAndLength<CoffGroupSym>(const CVSymbol &sym) { | ||
| CoffGroupSym record = createRecord<CoffGroupSym>(sym); | ||
| return {record.Segment, record.Offset, record.Size}; | ||
| } | ||
|
|
||
| SegmentOffsetLength 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<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}; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| //===-- 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 <tuple> | ||
| #include <utility> | ||
|
|
||
| namespace lldb_private { | ||
| namespace npdb { | ||
|
|
||
| struct SegmentOffset { | ||
| uint16_t segment = 0; | ||
| uint32_t offset = 0; | ||
| }; | ||
|
|
||
| struct SegmentOffsetLength { | ||
| SegmentOffset so; | ||
| uint32_t length = 0; | ||
| }; | ||
|
|
||
| llvm::pdb::PDB_SymType CVSymToPDBSym(llvm::codeview::SymbolKind 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; | ||
| } | ||
|
|
||
| } // namespace npdb | ||
| } // namespace lldb_private | ||
|
|
||
| #endif |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 <unordered_map> | ||
|
|
||
| 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<lldb_private::ConstString> &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<PdbIndex> m_index; | ||
|
|
||
| llvm::DenseMap<lldb::user_id_t, lldb::FunctionSP> m_functions; | ||
| llvm::DenseMap<lldb::user_id_t, lldb::CompUnitSP> m_compilands; | ||
| }; | ||
|
|
||
| } // namespace npdb | ||
| } // namespace lldb_private | ||
|
|
||
| #endif // lldb_Plugins_SymbolFile_PDB_SymbolFilePDB_h_ |