1//===- NativeSession.cpp - Native implementation of IPDBSession -*- C++ -*-===//
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 "llvm/DebugInfo/PDB/Native/NativeSession.h"
10
11#include "llvm/ADT/SmallString.h"
12#include "llvm/BinaryFormat/Magic.h"
13#include "llvm/DebugInfo/MSF/MappedBlockStream.h"
14#include "llvm/DebugInfo/PDB/IPDBEnumChildren.h"
15#include "llvm/DebugInfo/PDB/IPDBSourceFile.h"
16#include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptor.h"
17#include "llvm/DebugInfo/PDB/Native/DbiModuleList.h"
18#include "llvm/DebugInfo/PDB/Native/DbiStream.h"
19#include "llvm/DebugInfo/PDB/Native/ISectionContribVisitor.h"
20#include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h"
21#include "llvm/DebugInfo/PDB/Native/NativeEnumInjectedSources.h"
22#include "llvm/DebugInfo/PDB/Native/NativeExeSymbol.h"
23#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
24#include "llvm/DebugInfo/PDB/Native/RawConstants.h"
25#include "llvm/DebugInfo/PDB/Native/RawError.h"
26#include "llvm/DebugInfo/PDB/Native/RawTypes.h"
27#include "llvm/DebugInfo/PDB/Native/SymbolCache.h"
28#include "llvm/DebugInfo/PDB/PDBSymbol.h"
29#include "llvm/DebugInfo/PDB/PDBSymbolCompiland.h"
30#include "llvm/DebugInfo/PDB/PDBSymbolExe.h"
31#include "llvm/Object/Binary.h"
32#include "llvm/Object/COFF.h"
33#include "llvm/Support/Allocator.h"
34#include "llvm/Support/BinaryByteStream.h"
35#include "llvm/Support/BinaryStreamArray.h"
36#include "llvm/Support/Error.h"
37#include "llvm/Support/ErrorOr.h"
38#include "llvm/Support/MemoryBuffer.h"
39#include "llvm/Support/Path.h"
40
41#include <cassert>
42#include <memory>
43#include <utility>
44
45using namespace llvm;
46using namespace llvm::msf;
47using namespace llvm::pdb;
48
49namespace llvm {
50namespace codeview {
51union DebugInfo;
52}
53} // namespace llvm
54
55static DbiStream *getDbiStreamPtr(PDBFile &File) {
56 Expected<DbiStream &> DbiS = File.getPDBDbiStream();
57 if (DbiS)
58 return &DbiS.get();
59
60 consumeError(Err: DbiS.takeError());
61 return nullptr;
62}
63
64NativeSession::NativeSession(std::unique_ptr<PDBFile> PdbFile,
65 std::unique_ptr<BumpPtrAllocator> Allocator)
66 : Pdb(std::move(PdbFile)), Allocator(std::move(Allocator)),
67 Cache(*this, getDbiStreamPtr(File&: *Pdb)), AddrToModuleIndex(IMapAllocator) {}
68
69NativeSession::~NativeSession() = default;
70
71Error NativeSession::createFromPdb(std::unique_ptr<MemoryBuffer> Buffer,
72 std::unique_ptr<IPDBSession> &Session) {
73 StringRef Path = Buffer->getBufferIdentifier();
74 auto Stream = std::make_unique<MemoryBufferByteStream>(
75 args: std::move(Buffer), args: llvm::endianness::little);
76
77 auto Allocator = std::make_unique<BumpPtrAllocator>();
78 auto File = std::make_unique<PDBFile>(args&: Path, args: std::move(Stream), args&: *Allocator);
79 if (auto EC = File->parseFileHeaders())
80 return EC;
81 if (auto EC = File->parseStreamData())
82 return EC;
83
84 Session =
85 std::make_unique<NativeSession>(args: std::move(File), args: std::move(Allocator));
86
87 return Error::success();
88}
89
90static Error validatePdbMagic(StringRef PdbPath) {
91 file_magic Magic;
92 if (auto EC = identify_magic(path: PdbPath, result&: Magic))
93 return make_error<RawError>(Args&: EC);
94
95 if (Magic != file_magic::pdb)
96 return make_error<RawError>(
97 Args: raw_error_code::invalid_format,
98 Args: "The input file did not contain the pdb file magic.");
99
100 return Error::success();
101}
102
103static Expected<std::unique_ptr<PDBFile>>
104loadPdbFile(StringRef PdbPath, std::unique_ptr<BumpPtrAllocator> &Allocator) {
105 ErrorOr<std::unique_ptr<MemoryBuffer>> ErrorOrBuffer =
106 MemoryBuffer::getFile(Filename: PdbPath, /*IsText=*/false,
107 /*RequiresNullTerminator=*/false);
108 if (!ErrorOrBuffer)
109 return make_error<RawError>(Args: ErrorOrBuffer.getError());
110 std::unique_ptr<llvm::MemoryBuffer> Buffer = std::move(*ErrorOrBuffer);
111
112 PdbPath = Buffer->getBufferIdentifier();
113 if (auto EC = validatePdbMagic(PdbPath))
114 return std::move(EC);
115
116 auto Stream = std::make_unique<MemoryBufferByteStream>(
117 args: std::move(Buffer), args: llvm::endianness::little);
118
119 auto File = std::make_unique<PDBFile>(args&: PdbPath, args: std::move(Stream), args&: *Allocator);
120 if (auto EC = File->parseFileHeaders())
121 return std::move(EC);
122
123 if (auto EC = File->parseStreamData())
124 return std::move(EC);
125
126 return std::move(File);
127}
128
129Error NativeSession::createFromPdbPath(StringRef PdbPath,
130 std::unique_ptr<IPDBSession> &Session) {
131 auto Allocator = std::make_unique<BumpPtrAllocator>();
132 auto PdbFile = loadPdbFile(PdbPath, Allocator);
133 if (!PdbFile)
134 return PdbFile.takeError();
135
136 Session = std::make_unique<NativeSession>(args: std::move(PdbFile.get()),
137 args: std::move(Allocator));
138 return Error::success();
139}
140
141static Expected<std::string> getPdbPathFromExe(StringRef ExePath) {
142 Expected<object::OwningBinary<object::Binary>> BinaryFile =
143 object::createBinary(Path: ExePath);
144 if (!BinaryFile)
145 return BinaryFile.takeError();
146
147 const object::COFFObjectFile *ObjFile =
148 dyn_cast<object::COFFObjectFile>(Val: BinaryFile->getBinary());
149 if (!ObjFile)
150 return make_error<RawError>(Args: raw_error_code::invalid_format);
151
152 StringRef PdbPath;
153 const llvm::codeview::DebugInfo *PdbInfo = nullptr;
154 if (Error E = ObjFile->getDebugPDBInfo(Info&: PdbInfo, PDBFileName&: PdbPath))
155 return std::move(E);
156
157 return std::string(PdbPath);
158}
159
160Error NativeSession::createFromExe(StringRef ExePath,
161 std::unique_ptr<IPDBSession> &Session) {
162 Expected<std::string> PdbPath = getPdbPathFromExe(ExePath);
163 if (!PdbPath)
164 return PdbPath.takeError();
165
166 if (auto EC = validatePdbMagic(PdbPath: PdbPath.get()))
167 return EC;
168
169 auto Allocator = std::make_unique<BumpPtrAllocator>();
170 auto File = loadPdbFile(PdbPath: PdbPath.get(), Allocator);
171 if (!File)
172 return File.takeError();
173
174 Session = std::make_unique<NativeSession>(args: std::move(File.get()),
175 args: std::move(Allocator));
176
177 return Error::success();
178}
179
180Expected<std::string>
181NativeSession::searchForPdb(const PdbSearchOptions &Opts) {
182 Expected<std::string> PathOrErr = getPdbPathFromExe(ExePath: Opts.ExePath);
183 if (!PathOrErr)
184 return PathOrErr.takeError();
185 StringRef PathFromExe = PathOrErr.get();
186 sys::path::Style Style = PathFromExe.starts_with(Prefix: "/")
187 ? sys::path::Style::posix
188 : sys::path::Style::windows;
189 StringRef PdbName = sys::path::filename(path: PathFromExe, style: Style);
190
191 // Check if pdb exists in the executable directory.
192 SmallString<128> PdbPath = StringRef(Opts.ExePath);
193 sys::path::remove_filename(path&: PdbPath);
194 sys::path::append(path&: PdbPath, a: PdbName);
195
196 auto Allocator = std::make_unique<BumpPtrAllocator>();
197
198 if (auto File = loadPdbFile(PdbPath, Allocator))
199 return std::string(PdbPath);
200 else
201 consumeError(Err: File.takeError());
202
203 // Check path that was in the executable.
204 if (auto File = loadPdbFile(PdbPath: PathFromExe, Allocator))
205 return std::string(PathFromExe);
206 else
207 return File.takeError();
208
209 return make_error<RawError>(Args: "PDB not found");
210}
211
212uint64_t NativeSession::getLoadAddress() const { return LoadAddress; }
213
214bool NativeSession::setLoadAddress(uint64_t Address) {
215 LoadAddress = Address;
216 return true;
217}
218
219std::unique_ptr<PDBSymbolExe> NativeSession::getGlobalScope() {
220 return PDBSymbol::createAs<PDBSymbolExe>(PDBSession: *this, RawSymbol&: getNativeGlobalScope());
221}
222
223std::unique_ptr<PDBSymbol>
224NativeSession::getSymbolById(SymIndexId SymbolId) const {
225 return Cache.getSymbolById(SymbolId);
226}
227
228bool NativeSession::addressForVA(uint64_t VA, uint32_t &Section,
229 uint32_t &Offset) const {
230 uint32_t RVA = VA - getLoadAddress();
231 return addressForRVA(RVA, Section, Offset);
232}
233
234bool NativeSession::addressForRVA(uint32_t RVA, uint32_t &Section,
235 uint32_t &Offset) const {
236 Section = 0;
237 Offset = 0;
238
239 auto Dbi = Pdb->getPDBDbiStream();
240 if (!Dbi)
241 return false;
242
243 if ((int32_t)RVA < 0)
244 return true;
245
246 Offset = RVA;
247 for (; Section < Dbi->getSectionHeaders().size(); ++Section) {
248 auto &Sec = Dbi->getSectionHeaders()[Section];
249 if (RVA < Sec.VirtualAddress)
250 return true;
251 Offset = RVA - Sec.VirtualAddress;
252 }
253 return true;
254}
255
256std::unique_ptr<PDBSymbol>
257NativeSession::findSymbolByAddress(uint64_t Address, PDB_SymType Type) {
258 if (AddrToModuleIndex.empty())
259 parseSectionContribs();
260
261 return Cache.findSymbolByVA(VA: Address, Type);
262}
263
264std::unique_ptr<PDBSymbol> NativeSession::findSymbolByRVA(uint32_t RVA,
265 PDB_SymType Type) {
266 return findSymbolByAddress(Address: getLoadAddress() + RVA, Type);
267}
268
269std::unique_ptr<PDBSymbol>
270NativeSession::findSymbolBySectOffset(uint32_t Sect, uint32_t Offset,
271 PDB_SymType Type) {
272 return findSymbolByAddress(Address: getVAFromSectOffset(Section: Sect, Offset), Type);
273}
274
275std::unique_ptr<IPDBEnumLineNumbers>
276NativeSession::findLineNumbers(const PDBSymbolCompiland &Compiland,
277 const IPDBSourceFile &File) const {
278 return nullptr;
279}
280
281std::unique_ptr<IPDBEnumLineNumbers>
282NativeSession::findLineNumbersByAddress(uint64_t Address,
283 uint32_t Length) const {
284 return Cache.findLineNumbersByVA(VA: Address, Length);
285}
286
287std::unique_ptr<IPDBEnumLineNumbers>
288NativeSession::findLineNumbersByRVA(uint32_t RVA, uint32_t Length) const {
289 return Cache.findLineNumbersByVA(VA: getLoadAddress() + RVA, Length);
290}
291
292std::unique_ptr<IPDBEnumLineNumbers>
293NativeSession::findLineNumbersBySectOffset(uint32_t Section, uint32_t Offset,
294 uint32_t Length) const {
295 uint64_t VA = getVAFromSectOffset(Section, Offset);
296 return Cache.findLineNumbersByVA(VA, Length);
297}
298
299std::unique_ptr<IPDBEnumSourceFiles>
300NativeSession::findSourceFiles(const PDBSymbolCompiland *Compiland,
301 StringRef Pattern,
302 PDB_NameSearchFlags Flags) const {
303 return nullptr;
304}
305
306std::unique_ptr<IPDBSourceFile>
307NativeSession::findOneSourceFile(const PDBSymbolCompiland *Compiland,
308 StringRef Pattern,
309 PDB_NameSearchFlags Flags) const {
310 return nullptr;
311}
312
313std::unique_ptr<IPDBEnumChildren<PDBSymbolCompiland>>
314NativeSession::findCompilandsForSourceFile(StringRef Pattern,
315 PDB_NameSearchFlags Flags) const {
316 return nullptr;
317}
318
319std::unique_ptr<PDBSymbolCompiland>
320NativeSession::findOneCompilandForSourceFile(StringRef Pattern,
321 PDB_NameSearchFlags Flags) const {
322 return nullptr;
323}
324
325std::unique_ptr<IPDBEnumSourceFiles> NativeSession::getAllSourceFiles() const {
326 return nullptr;
327}
328
329std::unique_ptr<IPDBEnumSourceFiles> NativeSession::getSourceFilesForCompiland(
330 const PDBSymbolCompiland &Compiland) const {
331 return nullptr;
332}
333
334std::unique_ptr<IPDBSourceFile>
335NativeSession::getSourceFileById(uint32_t FileId) const {
336 return Cache.getSourceFileById(FileId);
337}
338
339std::unique_ptr<IPDBEnumDataStreams> NativeSession::getDebugStreams() const {
340 return nullptr;
341}
342
343std::unique_ptr<IPDBEnumTables> NativeSession::getEnumTables() const {
344 return nullptr;
345}
346
347std::unique_ptr<IPDBEnumInjectedSources>
348NativeSession::getInjectedSources() const {
349 auto ISS = Pdb->getInjectedSourceStream();
350 if (!ISS) {
351 consumeError(Err: ISS.takeError());
352 return nullptr;
353 }
354 auto Strings = Pdb->getStringTable();
355 if (!Strings) {
356 consumeError(Err: Strings.takeError());
357 return nullptr;
358 }
359 return std::make_unique<NativeEnumInjectedSources>(args&: *Pdb, args&: *ISS, args&: *Strings);
360}
361
362std::unique_ptr<IPDBEnumSectionContribs>
363NativeSession::getSectionContribs() const {
364 return nullptr;
365}
366
367std::unique_ptr<IPDBEnumFrameData>
368NativeSession::getFrameData() const {
369 return nullptr;
370}
371
372void NativeSession::initializeExeSymbol() {
373 if (ExeSymbol == 0)
374 ExeSymbol = Cache.createSymbol<NativeExeSymbol>();
375}
376
377NativeExeSymbol &NativeSession::getNativeGlobalScope() const {
378 const_cast<NativeSession &>(*this).initializeExeSymbol();
379
380 return Cache.getNativeSymbolById<NativeExeSymbol>(SymbolId: ExeSymbol);
381}
382
383uint32_t NativeSession::getRVAFromSectOffset(uint32_t Section,
384 uint32_t Offset) const {
385 if (Section <= 0)
386 return 0;
387
388 auto Dbi = getDbiStreamPtr(File&: *Pdb);
389 if (!Dbi)
390 return 0;
391
392 uint32_t MaxSection = Dbi->getSectionHeaders().size();
393 if (Section > MaxSection + 1)
394 Section = MaxSection + 1;
395 auto &Sec = Dbi->getSectionHeaders()[Section - 1];
396 return Sec.VirtualAddress + Offset;
397}
398
399uint64_t NativeSession::getVAFromSectOffset(uint32_t Section,
400 uint32_t Offset) const {
401 return LoadAddress + getRVAFromSectOffset(Section, Offset);
402}
403
404bool NativeSession::moduleIndexForVA(uint64_t VA, uint16_t &ModuleIndex) const {
405 ModuleIndex = 0;
406 auto Iter = AddrToModuleIndex.find(x: VA);
407 if (Iter.valid() && !IMap::KeyTraits::startLess(x: VA, a: Iter.start())) {
408 ModuleIndex = Iter.value();
409 return true;
410 }
411 return false;
412}
413
414bool NativeSession::moduleIndexForSectOffset(uint32_t Sect, uint32_t Offset,
415 uint16_t &ModuleIndex) const {
416 return moduleIndexForVA(VA: getVAFromSectOffset(Section: Sect, Offset), ModuleIndex);
417}
418
419#ifndef NDEBUG
420void NativeSession::checkSymbolRange(uint64_t Start, uint64_t Stop) const {
421 auto Iter = AddrToModuleIndex.find(Start);
422 assert(Iter.valid() && !IMap::KeyTraits::startLess(Start, Iter.start()) &&
423 !IMap::KeyTraits::stopLess(Iter.stop(), Stop - 1) &&
424 "Symbol range is not within a single contiguous module range");
425}
426#endif
427
428void NativeSession::parseSectionContribs() {
429 auto Dbi = Pdb->getPDBDbiStream();
430 if (!Dbi)
431 return;
432
433 class Visitor : public ISectionContribVisitor {
434 NativeSession &Session;
435 IMap &AddrMap;
436
437 public:
438 Visitor(NativeSession &Session, IMap &AddrMap)
439 : Session(Session), AddrMap(AddrMap) {}
440 void visit(const SectionContrib &C) override {
441 if (C.Size == 0)
442 return;
443
444 uint64_t VA = Session.getVAFromSectOffset(Section: C.ISect, Offset: C.Off);
445 uint64_t End = VA + C.Size;
446
447 // Ignore overlapping sections based on the assumption that a valid
448 // PDB file should not have overlaps.
449 if (!AddrMap.overlaps(a: VA, b: End))
450 AddrMap.insert(a: VA, b: End, y: C.Imod);
451 }
452 void visit(const SectionContrib2 &C) override { visit(C: C.Base); }
453 };
454
455 Visitor V(*this, AddrToModuleIndex);
456 Dbi->visitSectionContributions(Visitor&: V);
457}
458
459Expected<ModuleDebugStreamRef>
460NativeSession::getModuleDebugStream(uint32_t Index) const {
461 auto *Dbi = getDbiStreamPtr(File&: *Pdb);
462 assert(Dbi && "Dbi stream not present");
463
464 DbiModuleDescriptor Modi = Dbi->modules().getModuleDescriptor(Modi: Index);
465
466 uint16_t ModiStream = Modi.getModuleStreamIndex();
467 if (ModiStream == kInvalidStreamIndex)
468 return make_error<RawError>(Args: "Module stream not present");
469
470 std::unique_ptr<msf::MappedBlockStream> ModStreamData =
471 Pdb->createIndexedStream(SN: ModiStream);
472
473 ModuleDebugStreamRef ModS(Modi, std::move(ModStreamData));
474 if (auto EC = ModS.reload())
475 return std::move(EC);
476
477 return std::move(ModS);
478}
479