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(va: MakeVirtualAddress(segment, offset));
79}
80
81std::optional<uint16_t> PdbIndex::GetModuleIndexForVa(lldb::addr_t va) const {
82 auto iter = m_va_to_modi.find(x: 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(segment: C.ISect, offset: 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(a: va, b: end - 1, y: C.Imod);
109 }
110 void visit(const SectionContrib2 &C) override { visit(C: C.Base); }
111 };
112 Visitor v(*this, m_va_to_modi);
113 dbi().visitSectionContributions(Visitor&: 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(sym: *iter))
123 continue;
124
125 SegmentOffset so = GetSegmentAndOffset(sym: *iter);
126 lldb::addr_t va = MakeVirtualAddress(segment: so.segment, offset: 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(x: std::make_pair(x&: va, y: 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: *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(x: 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: 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(segment: sol.so.segment, offset: 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(x: {.sym: std::move(sym), .uid: iter->second});
182 }
183
184 return result;
185}
186
187CVSymbol PdbIndex::ReadSymbolRecord(PdbCompilandSymId cu_sym) const {
188 const CompilandIndexItem *cci = compilands().GetCompiland(modi: cu_sym.modi);
189 auto iter = cci->m_debug_stream.getSymbolArray().at(Offset: 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(Offset: global.offset);
196}
197

source code of lldb/source/Plugins/SymbolFile/NativePDB/PdbIndex.cpp