//===-- PdbIndex.cpp --------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "PdbIndex.h" #include "PdbUtil.h" #include "llvm/DebugInfo/CodeView/SymbolDeserializer.h" #include "llvm/DebugInfo/PDB/Native/DbiStream.h" #include "llvm/DebugInfo/PDB/Native/GlobalsStream.h" #include "llvm/DebugInfo/PDB/Native/ISectionContribVisitor.h" #include "llvm/DebugInfo/PDB/Native/PDBFile.h" #include "llvm/DebugInfo/PDB/Native/PublicsStream.h" #include "llvm/DebugInfo/PDB/Native/SymbolStream.h" #include "llvm/DebugInfo/PDB/Native/TpiStream.h" #include "llvm/Object/COFF.h" #include "llvm/Support/Error.h" #include "lldb/Utility/LLDBAssert.h" #include "lldb/lldb-defines.h" using namespace lldb_private; using namespace lldb_private::npdb; using namespace llvm::codeview; using namespace llvm::pdb; PdbIndex::PdbIndex() : m_cus(*this), m_va_to_modi(m_allocator) {} #define ASSIGN_PTR_OR_RETURN(result_ptr, expr) \ { \ auto expected_result = expr; \ if (!expected_result) \ return expected_result.takeError(); \ result_ptr = &expected_result.get(); \ } llvm::Expected> PdbIndex::create(std::unique_ptr file) { lldbassert(file); std::unique_ptr result(new PdbIndex()); ASSIGN_PTR_OR_RETURN(result->m_dbi, file->getPDBDbiStream()); ASSIGN_PTR_OR_RETURN(result->m_tpi, file->getPDBTpiStream()); ASSIGN_PTR_OR_RETURN(result->m_ipi, file->getPDBIpiStream()); ASSIGN_PTR_OR_RETURN(result->m_info, file->getPDBInfoStream()); ASSIGN_PTR_OR_RETURN(result->m_publics, file->getPDBPublicsStream()); ASSIGN_PTR_OR_RETURN(result->m_globals, file->getPDBGlobalsStream()); ASSIGN_PTR_OR_RETURN(result->m_symrecords, file->getPDBSymbolStream()); result->m_tpi->buildHashMap(); result->m_file = std::move(file); return std::move(result); } lldb::addr_t PdbIndex::MakeVirtualAddress(uint16_t segment, uint32_t offset) const { // Segment indices are 1-based. lldbassert(segment > 0); uint32_t max_section = dbi().getSectionHeaders().size(); lldbassert(segment <= max_section + 1); // If this is an absolute symbol, it's indicated by the magic section index // |max_section+1|. In this case, the offset is meaningless, so just return. if (segment == max_section + 1) return LLDB_INVALID_ADDRESS; const llvm::object::coff_section &cs = dbi().getSectionHeaders()[segment - 1]; return m_load_address + static_cast(cs.VirtualAddress) + static_cast(offset); } lldb::addr_t PdbIndex::MakeVirtualAddress(const SegmentOffset &so) const { return MakeVirtualAddress(so.segment, so.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() { class Visitor : public ISectionContribVisitor { PdbIndex &m_ctx; llvm::IntervalMap &m_imap; public: Visitor(PdbIndex &ctx, llvm::IntervalMap &imap) : m_ctx(ctx), m_imap(imap) {} void visit(const SectionContrib &C) override { if (C.Size == 0) return; uint64_t va = m_ctx.MakeVirtualAddress(C.ISect, C.Off); uint64_t end = va + C.Size; // IntervalMap's start and end represent a closed range, not a half-open // range, so we have to subtract 1. m_imap.insert(va, end - 1, C.Imod); } void visit(const SectionContrib2 &C) override { visit(C.Base); } }; Visitor v(*this, m_va_to_modi); dbi().visitSectionContributions(v); } void PdbIndex::BuildAddrToSymbolMap(CompilandIndexItem &cci) { lldbassert(cci.m_symbols_by_va.empty() && "Addr to symbol map is already built!"); uint16_t modi = cci.m_id.modi; const CVSymbolArray &syms = cci.m_debug_stream.getSymbolArray(); for (auto iter = syms.begin(); iter != syms.end(); ++iter) { if (!SymbolHasAddress(*iter)) continue; SegmentOffset so = GetSegmentAndOffset(*iter); lldb::addr_t va = MakeVirtualAddress(so); PdbCompilandSymId cu_sym_id(modi, iter.offset()); // If the debug info is incorrect, we could have multiple symbols with the // same address. So use try_emplace instead of insert, and the first one // will win. cci.m_symbols_by_va.insert(std::make_pair(va, PdbSymUid(cu_sym_id))); } } std::vector 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) { PdbCompilandSymId cu_sym_id = iter->second.asCompilandSym(); CVSymbol sym = ReadSymbolRecord(cu_sym_id); SegmentOffsetLength sol; if (SymbolIsCode(sym)) sol = GetSegmentOffsetAndLength(sym); else sol.so = GetSegmentAndOffset(sym); lldb::addr_t start = MakeVirtualAddress(sol.so); lldb::addr_t end = start + sol.length; if (va >= start && va < end) result.push_back({std::move(sym), iter->second}); } return result; } CVSymbol PdbIndex::ReadSymbolRecord(PdbCompilandSymId cu_sym) const { // We need to subtract 4 here to adjust for the codeview debug magic // at the beginning of the debug info stream. const CompilandIndexItem *cci = compilands().GetCompiland(cu_sym.modi); auto iter = cci->m_debug_stream.getSymbolArray().at(cu_sym.offset); lldbassert(iter != cci->m_debug_stream.getSymbolArray().end()); return *iter; } CVSymbol PdbIndex::ReadSymbolRecord(PdbGlobalSymId global) const { return symrecords().readRecord(global.offset); }