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