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