197 lines · cpp
1//===-- PdbIndex.cpp ------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "PdbIndex.h"10#include "PdbUtil.h"11 12#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h"13#include "llvm/DebugInfo/PDB/Native/DbiStream.h"14#include "llvm/DebugInfo/PDB/Native/GlobalsStream.h"15#include "llvm/DebugInfo/PDB/Native/ISectionContribVisitor.h"16#include "llvm/DebugInfo/PDB/Native/PDBFile.h"17#include "llvm/DebugInfo/PDB/Native/PublicsStream.h"18#include "llvm/DebugInfo/PDB/Native/SymbolStream.h"19#include "llvm/DebugInfo/PDB/Native/TpiStream.h"20#include "llvm/Object/COFF.h"21#include "llvm/Support/Error.h"22 23#include "lldb/Utility/LLDBAssert.h"24#include "lldb/lldb-defines.h"25#include <optional>26 27using namespace lldb_private;28using namespace lldb_private::npdb;29using namespace llvm::codeview;30using namespace llvm::pdb;31 32PdbIndex::PdbIndex() : m_cus(*this), m_va_to_modi(m_allocator) {}33 34#define ASSIGN_PTR_OR_RETURN(result_ptr, expr) \35 { \36 auto expected_result = expr; \37 if (!expected_result) \38 return expected_result.takeError(); \39 result_ptr = &expected_result.get(); \40 }41 42llvm::Expected<std::unique_ptr<PdbIndex>>43PdbIndex::create(llvm::pdb::PDBFile *file) {44 lldbassert(file);45 46 std::unique_ptr<PdbIndex> result(new PdbIndex());47 ASSIGN_PTR_OR_RETURN(result->m_dbi, file->getPDBDbiStream());48 ASSIGN_PTR_OR_RETURN(result->m_tpi, file->getPDBTpiStream());49 ASSIGN_PTR_OR_RETURN(result->m_ipi, file->getPDBIpiStream());50 ASSIGN_PTR_OR_RETURN(result->m_info, file->getPDBInfoStream());51 ASSIGN_PTR_OR_RETURN(result->m_publics, file->getPDBPublicsStream());52 ASSIGN_PTR_OR_RETURN(result->m_globals, file->getPDBGlobalsStream());53 ASSIGN_PTR_OR_RETURN(result->m_symrecords, file->getPDBSymbolStream());54 55 result->m_tpi->buildHashMap();56 57 result->m_file = file;58 59 return std::move(result);60}61 62lldb::addr_t PdbIndex::MakeVirtualAddress(uint16_t segment,63 uint32_t offset) const {64 uint32_t max_section = dbi().getSectionHeaders().size();65 // Segment indices are 1-based.66 // If this is an absolute symbol, it's indicated by the magic section index67 // |max_section+1|. In this case, the offset is meaningless, so just return.68 if (segment == 0 || segment > max_section)69 return LLDB_INVALID_ADDRESS;70 71 const llvm::object::coff_section &cs = dbi().getSectionHeaders()[segment - 1];72 return m_load_address + static_cast<lldb::addr_t>(cs.VirtualAddress) +73 static_cast<lldb::addr_t>(offset);74}75 76std::optional<uint16_t> PdbIndex::GetModuleIndexForAddr(uint16_t segment,77 uint32_t offset) const {78 return GetModuleIndexForVa(MakeVirtualAddress(segment, offset));79}80 81std::optional<uint16_t> PdbIndex::GetModuleIndexForVa(lldb::addr_t va) const {82 auto iter = m_va_to_modi.find(va);83 if (iter == m_va_to_modi.end())84 return std::nullopt;85 86 return iter.value();87}88 89void PdbIndex::ParseSectionContribs() {90 class Visitor : public ISectionContribVisitor {91 PdbIndex &m_ctx;92 llvm::IntervalMap<uint64_t, uint16_t> &m_imap;93 94 public:95 Visitor(PdbIndex &ctx, llvm::IntervalMap<uint64_t, uint16_t> &imap)96 : m_ctx(ctx), m_imap(imap) {}97 98 void visit(const SectionContrib &C) override {99 if (C.Size == 0)100 return;101 102 uint64_t va = m_ctx.MakeVirtualAddress(C.ISect, C.Off);103 if (va == LLDB_INVALID_ADDRESS)104 return;105 uint64_t end = va + C.Size;106 // IntervalMap's start and end represent a closed range, not a half-open107 // range, so we have to subtract 1.108 m_imap.insert(va, end - 1, C.Imod);109 }110 void visit(const SectionContrib2 &C) override { visit(C.Base); }111 };112 Visitor v(*this, m_va_to_modi);113 dbi().visitSectionContributions(v);114}115 116void PdbIndex::BuildAddrToSymbolMap(CompilandIndexItem &cci) {117 lldbassert(cci.m_symbols_by_va.empty() &&118 "Addr to symbol map is already built!");119 uint16_t modi = cci.m_id.modi;120 const CVSymbolArray &syms = cci.m_debug_stream.getSymbolArray();121 for (auto iter = syms.begin(); iter != syms.end(); ++iter) {122 if (!SymbolHasAddress(*iter))123 continue;124 125 SegmentOffset so = GetSegmentAndOffset(*iter);126 lldb::addr_t va = MakeVirtualAddress(so.segment, so.offset);127 if (va == LLDB_INVALID_ADDRESS)128 continue;129 130 PdbCompilandSymId cu_sym_id(modi, iter.offset());131 132 // It's rare, but we could have multiple symbols with the same address133 // because of identical comdat folding. Right now, the first one will win.134 cci.m_symbols_by_va.insert(std::make_pair(va, PdbSymUid(cu_sym_id)));135 }136}137 138std::vector<SymbolAndUid> PdbIndex::FindSymbolsByVa(lldb::addr_t va) {139 std::vector<SymbolAndUid> result;140 141 std::optional<uint16_t> modi = GetModuleIndexForVa(va);142 if (!modi)143 return result;144 145 CompilandIndexItem &cci = compilands().GetOrCreateCompiland(*modi);146 if (cci.m_symbols_by_va.empty())147 BuildAddrToSymbolMap(cci);148 149 // The map is sorted by starting address of the symbol. So for example150 // we could (in theory) have this situation151 //152 // [------------------]153 // [----------]154 // [-----------]155 // [-------------]156 // [----]157 // [-----]158 // ^ Address we're searching for159 // In order to find this, we use the upper_bound of the key value which would160 // be the first symbol whose starting address is higher than the element we're161 // searching for.162 163 auto ub = cci.m_symbols_by_va.upper_bound(va);164 165 for (auto iter = cci.m_symbols_by_va.begin(); iter != ub; ++iter) {166 PdbCompilandSymId cu_sym_id = iter->second.asCompilandSym();167 CVSymbol sym = ReadSymbolRecord(cu_sym_id);168 169 SegmentOffsetLength sol;170 if (SymbolIsCode(sym))171 sol = GetSegmentOffsetAndLength(sym);172 else173 sol.so = GetSegmentAndOffset(sym);174 175 lldb::addr_t start = MakeVirtualAddress(sol.so.segment, sol.so.offset);176 if (start == LLDB_INVALID_ADDRESS)177 continue;178 179 lldb::addr_t end = start + sol.length;180 if (va >= start && va < end)181 result.push_back({std::move(sym), iter->second});182 }183 184 return result;185}186 187CVSymbol PdbIndex::ReadSymbolRecord(PdbCompilandSymId cu_sym) const {188 const CompilandIndexItem *cci = compilands().GetCompiland(cu_sym.modi);189 auto iter = cci->m_debug_stream.getSymbolArray().at(cu_sym.offset);190 lldbassert(iter != cci->m_debug_stream.getSymbolArray().end());191 return *iter;192}193 194CVSymbol PdbIndex::ReadSymbolRecord(PdbGlobalSymId global) const {195 return symrecords().readRecord(global.offset);196}197