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-exception 6// 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 index 67 // |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-open 107 // 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 address 133 // 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 example 150 // we could (in theory) have this situation 151 // 152 // [------------------] 153 // [----------] 154 // [-----------] 155 // [-------------] 156 // [----] 157 // [-----] 158 // ^ Address we're searching for 159 // In order to find this, we use the upper_bound of the key value which would 160 // be the first symbol whose starting address is higher than the element we're 161 // 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 else 173 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