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 
27 using namespace lldb_private;
28 using namespace lldb_private::npdb;
29 using namespace llvm::codeview;
30 using namespace llvm::pdb;
31 
PdbIndex()32 PdbIndex::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 
42 llvm::Expected<std::unique_ptr<PdbIndex>>
create(llvm::pdb::PDBFile * file)43 PdbIndex::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 
MakeVirtualAddress(uint16_t segment,uint32_t offset) const62 lldb::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 
GetModuleIndexForAddr(uint16_t segment,uint32_t offset) const76 std::optional<uint16_t> PdbIndex::GetModuleIndexForAddr(uint16_t segment,
77                                                         uint32_t offset) const {
78   return GetModuleIndexForVa(MakeVirtualAddress(segment, offset));
79 }
80 
GetModuleIndexForVa(lldb::addr_t va) const81 std::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 
ParseSectionContribs()89 void 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 
BuildAddrToSymbolMap(CompilandIndexItem & cci)116 void 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 
FindSymbolsByVa(lldb::addr_t va)138 std::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 
ReadSymbolRecord(PdbCompilandSymId cu_sym) const187 CVSymbol 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 
ReadSymbolRecord(PdbGlobalSymId global) const194 CVSymbol PdbIndex::ReadSymbolRecord(PdbGlobalSymId global) const {
195   return symrecords().readRecord(global.offset);
196 }
197