| //===-- PdbIndex.cpp ------------------------------------------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "PdbIndex.h" | |
| #include "PdbUtil.h" | |
| #include "llvm/DebugInfo/CodeView/SymbolDeserializer.h" | |
| #include "llvm/DebugInfo/PDB/Native/DbiStream.h" | |
| #include "llvm/DebugInfo/PDB/Native/GlobalsStream.h" | |
| #include "llvm/DebugInfo/PDB/Native/ISectionContribVisitor.h" | |
| #include "llvm/DebugInfo/PDB/Native/PDBFile.h" | |
| #include "llvm/DebugInfo/PDB/Native/PublicsStream.h" | |
| #include "llvm/DebugInfo/PDB/Native/SymbolStream.h" | |
| #include "llvm/DebugInfo/PDB/Native/TpiStream.h" | |
| #include "llvm/Object/COFF.h" | |
| #include "llvm/Support/Error.h" | |
| #include "lldb/lldb-defines.h" | |
| #include <optional> | |
| using namespace lldb_private; | |
| using namespace lldb_private::npdb; | |
| using namespace llvm::codeview; | |
| using namespace llvm::pdb; | |
| PdbIndex::PdbIndex() : m_cus(*this), m_va_to_modi(m_allocator) {} | |
| #define ASSIGN_PTR_OR_RETURN(result_ptr, expr) \ | |
| { \ | |
| auto expected_result = expr; \ | |
| if (!expected_result) \ | |
| return expected_result.takeError(); \ | |
| result_ptr = &expected_result.get(); \ | |
| } | |
| llvm::Expected<std::unique_ptr<PdbIndex>> | |
| PdbIndex::create(llvm::pdb::PDBFile *file) { | |
| assert(file); | |
| std::unique_ptr<PdbIndex> result(new PdbIndex()); | |
| ASSIGN_PTR_OR_RETURN(result->m_dbi, file->getPDBDbiStream()); | |
| ASSIGN_PTR_OR_RETURN(result->m_tpi, file->getPDBTpiStream()); | |
| ASSIGN_PTR_OR_RETURN(result->m_ipi, file->getPDBIpiStream()); | |
| ASSIGN_PTR_OR_RETURN(result->m_info, file->getPDBInfoStream()); | |
| ASSIGN_PTR_OR_RETURN(result->m_publics, file->getPDBPublicsStream()); | |
| ASSIGN_PTR_OR_RETURN(result->m_globals, file->getPDBGlobalsStream()); | |
| ASSIGN_PTR_OR_RETURN(result->m_symrecords, file->getPDBSymbolStream()); | |
| result->m_tpi->buildHashMap(); | |
| result->m_file = file; | |
| return std::move(result); | |
| } | |
| lldb::addr_t PdbIndex::MakeVirtualAddress(uint16_t segment, | |
| uint32_t offset) const { | |
| uint32_t max_section = dbi().getSectionHeaders().size(); | |
| // Segment indices are 1-based. | |
| // If this is an absolute symbol, it's indicated by the magic section index | |
| // |max_section+1|. In this case, the offset is meaningless, so just return. | |
| if (segment == 0 || segment > max_section) | |
| return LLDB_INVALID_ADDRESS; | |
| const llvm::object::coff_section &cs = dbi().getSectionHeaders()[segment - 1]; | |
| return m_load_address + static_cast<lldb::addr_t>(cs.VirtualAddress) + | |
| static_cast<lldb::addr_t>(offset); | |
| } | |
| std::optional<uint16_t> PdbIndex::GetModuleIndexForAddr(uint16_t segment, | |
| uint32_t offset) const { | |
| return GetModuleIndexForVa(MakeVirtualAddress(segment, offset)); | |
| } | |
| std::optional<uint16_t> PdbIndex::GetModuleIndexForVa(lldb::addr_t va) const { | |
| auto iter = m_va_to_modi.find(va); | |
| if (iter == m_va_to_modi.end()) | |
| return std::nullopt; | |
| return iter.value(); | |
| } | |
| void PdbIndex::ParseSectionContribs() { | |
| class Visitor : public ISectionContribVisitor { | |
| PdbIndex &m_ctx; | |
| llvm::IntervalMap<uint64_t, uint16_t> &m_imap; | |
| public: | |
| Visitor(PdbIndex &ctx, llvm::IntervalMap<uint64_t, uint16_t> &imap) | |
| : m_ctx(ctx), m_imap(imap) {} | |
| void visit(const SectionContrib &C) override { | |
| if (C.Size == 0) | |
| return; | |
| uint64_t va = m_ctx.MakeVirtualAddress(C.ISect, C.Off); | |
| if (va == LLDB_INVALID_ADDRESS) | |
| return; | |
| uint64_t end = va + C.Size; | |
| // IntervalMap's start and end represent a closed range, not a half-open | |
| // range, so we have to subtract 1. | |
| m_imap.insert(va, end - 1, C.Imod); | |
| } | |
| void visit(const SectionContrib2 &C) override { visit(C.Base); } | |
| }; | |
| Visitor v(*this, m_va_to_modi); | |
| dbi().visitSectionContributions(v); | |
| } | |
| void PdbIndex::BuildAddrToSymbolMap(CompilandIndexItem &cci) { | |
| assert(cci.m_symbols_by_va.empty() && "Addr to symbol map is already built!"); | |
| uint16_t modi = cci.m_id.modi; | |
| const CVSymbolArray &syms = cci.m_debug_stream.getSymbolArray(); | |
| for (auto iter = syms.begin(); iter != syms.end(); ++iter) { | |
| if (!SymbolHasAddress(*iter)) | |
| continue; | |
| SegmentOffset so = GetSegmentAndOffset(*iter); | |
| lldb::addr_t va = MakeVirtualAddress(so.segment, so.offset); | |
| if (va == LLDB_INVALID_ADDRESS) | |
| continue; | |
| PdbCompilandSymId cu_sym_id(modi, iter.offset()); | |
| // It's rare, but we could have multiple symbols with the same address | |
| // because of identical comdat folding. Right now, the first one will win. | |
| cci.m_symbols_by_va.insert(std::make_pair(va, PdbSymUid(cu_sym_id))); | |
| } | |
| } | |
| std::vector<SymbolAndUid> PdbIndex::FindSymbolsByVa(lldb::addr_t va) { | |
| std::vector<SymbolAndUid> result; | |
| std::optional<uint16_t> modi = GetModuleIndexForVa(va); | |
| if (!modi) | |
| return result; | |
| CompilandIndexItem &cci = compilands().GetOrCreateCompiland(*modi); | |
| if (cci.m_symbols_by_va.empty()) | |
| BuildAddrToSymbolMap(cci); | |
| // The map is sorted by starting address of the symbol. So for example | |
| // we could (in theory) have this situation | |
| // | |
| // [------------------] | |
| // [----------] | |
| // [-----------] | |
| // [-------------] | |
| // [----] | |
| // [-----] | |
| // ^ Address we're searching for | |
| // In order to find this, we use the upper_bound of the key value which would | |
| // be the first symbol whose starting address is higher than the element we're | |
| // searching for. | |
| auto ub = cci.m_symbols_by_va.upper_bound(va); | |
| for (auto iter = cci.m_symbols_by_va.begin(); iter != ub; ++iter) { | |
| PdbCompilandSymId cu_sym_id = iter->second.asCompilandSym(); | |
| CVSymbol sym = ReadSymbolRecord(cu_sym_id); | |
| SegmentOffsetLength sol; | |
| if (SymbolIsCode(sym)) | |
| sol = GetSegmentOffsetAndLength(sym); | |
| else | |
| sol.so = GetSegmentAndOffset(sym); | |
| lldb::addr_t start = MakeVirtualAddress(sol.so.segment, sol.so.offset); | |
| if (start == LLDB_INVALID_ADDRESS) | |
| continue; | |
| lldb::addr_t end = start + sol.length; | |
| if (va >= start && va < end) | |
| result.push_back({std::move(sym), iter->second}); | |
| } | |
| return result; | |
| } | |
| CVSymbol PdbIndex::ReadSymbolRecord(PdbCompilandSymId cu_sym) const { | |
| const CompilandIndexItem *cci = compilands().GetCompiland(cu_sym.modi); | |
| auto iter = cci->m_debug_stream.getSymbolArray().at(cu_sym.offset); | |
| if (iter == cci->m_debug_stream.getSymbolArray().end()) { | |
| assert(false && "missing symbol"); | |
| return {}; | |
| } | |
| return *iter; | |
| } | |
| CVSymbol PdbIndex::ReadSymbolRecord(PdbGlobalSymId global) const { | |
| return symrecords().readRecord(global.offset); | |
| } |