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