LLVM 24.0.0git
PDBFile.cpp
Go to the documentation of this file.
1//===- PDBFile.cpp - Low level interface to a PDB file ----------*- 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
10#include "llvm/ADT/ArrayRef.h"
24#include "llvm/Support/Endian.h"
25#include "llvm/Support/Error.h"
26#include "llvm/Support/Path.h"
27#include <algorithm>
28#include <cassert>
29#include <cstdint>
30
31using namespace llvm;
32using namespace llvm::codeview;
33using namespace llvm::msf;
34using namespace llvm::pdb;
35
36PDBFile::PDBFile(StringRef Path, std::unique_ptr<BinaryStream> PdbFileBuffer,
37 BumpPtrAllocator &Allocator)
38 : FilePath(std::string(Path)), Allocator(Allocator),
39 Buffer(std::move(PdbFileBuffer)) {}
40
41PDBFile::~PDBFile() = default;
42
43StringRef PDBFile::getFilePath() const { return FilePath; }
44
48
49uint32_t PDBFile::getBlockSize() const { return ContainerLayout.SB->BlockSize; }
50
52 return ContainerLayout.SB->FreeBlockMapBlock;
53}
54
56 return ContainerLayout.SB->NumBlocks;
57}
58
60 return ContainerLayout.SB->NumDirectoryBytes;
61}
62
64 return ContainerLayout.SB->BlockMapAddr;
65}
66
67uint32_t PDBFile::getUnknown1() const { return ContainerLayout.SB->Unknown1; }
68
70 return msf::bytesToBlocks(ContainerLayout.SB->NumDirectoryBytes,
71 ContainerLayout.SB->BlockSize);
72}
73
75 return (uint64_t)ContainerLayout.SB->BlockMapAddr *
76 ContainerLayout.SB->BlockSize;
77}
78
80 return ContainerLayout.StreamSizes.size();
81}
82
84 return *llvm::max_element(ContainerLayout.StreamSizes);
85}
86
88 return ContainerLayout.StreamSizes[StreamIndex];
89}
90
93 return ContainerLayout.StreamMap[StreamIndex];
94}
95
96uint64_t PDBFile::getFileSize() const { return Buffer->getLength(); }
97
99 uint32_t NumBytes) const {
100 uint64_t StreamBlockOffset = msf::blockToOffset(BlockIndex, getBlockSize());
101
102 ArrayRef<uint8_t> Result;
103 if (auto EC = Buffer->readBytes(StreamBlockOffset, NumBytes, Result))
104 return std::move(EC);
105 return Result;
106}
107
109 ArrayRef<uint8_t> Data) const {
111 "PDBFile is immutable");
112}
113
115 BinaryStreamReader Reader(*Buffer);
116
117 // Initialize SB.
118 const msf::SuperBlock *SB = nullptr;
119 if (auto EC = Reader.readObject(SB)) {
120 consumeError(std::move(EC));
122 "MSF superblock is missing");
123 }
124
125 if (auto EC = msf::validateSuperBlock(*SB))
126 return EC;
127
128 if (Buffer->getLength() % SB->BlockSize != 0)
130 "File size is not a multiple of block size");
131 ContainerLayout.SB = SB;
132
133 // Initialize Free Page Map.
134 ContainerLayout.FreePageMap.resize(SB->NumBlocks);
135 // The Fpm exists either at block 1 or block 2 of the MSF. However, this
136 // allows for a maximum of getBlockSize() * 8 blocks bits in the Fpm, and
137 // thusly an equal number of total blocks in the file. For a block size
138 // of 4KiB (very common), this would yield 32KiB total blocks in file, for a
139 // maximum file size of 32KiB * 4KiB = 128MiB. Obviously this won't do, so
140 // the Fpm is split across the file at `getBlockSize()` intervals. As a
141 // result, every block whose index is of the form |{1,2} + getBlockSize() * k|
142 // for any non-negative integer k is an Fpm block. In theory, we only really
143 // need to reserve blocks of the form |{1,2} + getBlockSize() * 8 * k|, but
144 // current versions of the MSF format already expect the Fpm to be arranged
145 // at getBlockSize() intervals, so we have to be compatible.
146 // See the function fpmPn() for more information:
147 // https://github.com/Microsoft/microsoft-pdb/blob/master/PDB/msf/msf.cpp#L489
148 auto FpmStream =
149 MappedBlockStream::createFpmStream(ContainerLayout, *Buffer, Allocator);
150 BinaryStreamReader FpmReader(*FpmStream);
151 ArrayRef<uint8_t> FpmBytes;
152 if (auto EC = FpmReader.readBytes(FpmBytes, FpmReader.bytesRemaining()))
153 return EC;
154 uint32_t BlocksRemaining = getBlockCount();
155 uint32_t BI = 0;
156 for (auto Byte : FpmBytes) {
157 uint32_t BlocksThisByte = std::min(BlocksRemaining, 8U);
158 for (uint32_t I = 0; I < BlocksThisByte; ++I) {
159 if (Byte & (1 << I))
160 ContainerLayout.FreePageMap[BI] = true;
161 --BlocksRemaining;
162 ++BI;
163 }
164 }
165
167 if (auto EC = Reader.readArray(ContainerLayout.DirectoryBlocks,
169 return EC;
170
171 return Error::success();
172}
173
175 assert(ContainerLayout.SB);
176 if (DirectoryStream)
177 return Error::success();
178
179 uint32_t NumStreams = 0;
180
181 // Normally you can't use a MappedBlockStream without having fully parsed the
182 // PDB file, because it accesses the directory and various other things, which
183 // is exactly what we are attempting to parse. By specifying a custom
184 // subclass of IPDBStreamData which only accesses the fields that have already
185 // been parsed, we can avoid this and reuse MappedBlockStream.
186 auto DS = MappedBlockStream::createDirectoryStream(ContainerLayout, *Buffer,
187 Allocator);
188 BinaryStreamReader Reader(*DS);
189 if (auto EC = Reader.readInteger(NumStreams))
190 return EC;
191
192 if (auto EC = Reader.readArray(ContainerLayout.StreamSizes, NumStreams))
193 return EC;
194 for (uint32_t I = 0; I < NumStreams; ++I) {
195 uint32_t StreamSize = getStreamByteSize(I);
196 // FIXME: What does StreamSize ~0U mean?
197 uint64_t NumExpectedStreamBlocks =
198 StreamSize == UINT32_MAX
199 ? 0
200 : msf::bytesToBlocks(StreamSize, ContainerLayout.SB->BlockSize);
201
202 // For convenience, we store the block array contiguously. This is because
203 // if someone calls setStreamMap(), it is more convenient to be able to call
204 // it with an ArrayRef instead of setting up a StreamRef. Since the
205 // DirectoryStream is cached in the class and thus lives for the life of the
206 // class, we can be guaranteed that readArray() will return a stable
207 // reference, even if it has to allocate from its internal pool.
209 if (auto EC = Reader.readArray(Blocks, NumExpectedStreamBlocks))
210 return EC;
211 for (uint32_t Block : Blocks) {
212 uint64_t BlockEndOffset =
213 (uint64_t)(Block + 1) * ContainerLayout.SB->BlockSize;
214 if (BlockEndOffset > getFileSize())
216 "Stream block map is corrupt.");
217 }
218 ContainerLayout.StreamMap.push_back(Blocks);
219 }
220
221 // We should have read exactly SB->NumDirectoryBytes bytes.
222 assert(Reader.bytesRemaining() == 0);
223 DirectoryStream = std::move(DS);
224 return Error::success();
225}
226
228 return ContainerLayout.DirectoryBlocks;
229}
230
231std::unique_ptr<MappedBlockStream>
233 if (SN == kInvalidStreamIndex)
234 return nullptr;
235 return MappedBlockStream::createIndexedStream(ContainerLayout, *Buffer, SN,
236 Allocator);
237}
238
240 MSFStreamLayout Result;
241 auto Blocks = getStreamBlockList(StreamIdx);
242 Result.Blocks.assign(Blocks.begin(), Blocks.end());
243 Result.Length = getStreamByteSize(StreamIdx);
244 return Result;
245}
246
250
252 if (!Globals) {
253 auto DbiS = getPDBDbiStream();
254 if (!DbiS)
255 return DbiS.takeError();
256
257 auto GlobalS =
258 safelyCreateIndexedStream(DbiS->getGlobalSymbolStreamIndex());
259 if (!GlobalS)
260 return GlobalS.takeError();
261 auto TempGlobals = std::make_unique<GlobalsStream>(std::move(*GlobalS));
262 if (auto EC = TempGlobals->reload())
263 return std::move(EC);
264 Globals = std::move(TempGlobals);
265 }
266 return *Globals;
267}
268
270 if (!Info) {
272 if (!InfoS)
273 return InfoS.takeError();
274 auto TempInfo = std::make_unique<InfoStream>(std::move(*InfoS));
275 if (auto EC = TempInfo->reload())
276 return std::move(EC);
277 Info = std::move(TempInfo);
278 }
279 return *Info;
280}
281
283 if (!Dbi) {
285 if (!DbiS)
286 return DbiS.takeError();
287 auto TempDbi = std::make_unique<DbiStream>(std::move(*DbiS));
288 if (auto EC = TempDbi->reload(this))
289 return std::move(EC);
290 Dbi = std::move(TempDbi);
291 }
292 return *Dbi;
293}
294
296 if (!Tpi) {
298 if (!TpiS)
299 return TpiS.takeError();
300 auto TempTpi = std::make_unique<TpiStream>(*this, std::move(*TpiS));
301 if (auto EC = TempTpi->reload())
302 return std::move(EC);
303 Tpi = std::move(TempTpi);
304 }
305 return *Tpi;
306}
307
309 if (!Ipi) {
310 if (!hasPDBIpiStream())
312
314 if (!IpiS)
315 return IpiS.takeError();
316 auto TempIpi = std::make_unique<TpiStream>(*this, std::move(*IpiS));
317 if (auto EC = TempIpi->reload())
318 return std::move(EC);
319 Ipi = std::move(TempIpi);
320 }
321 return *Ipi;
322}
323
325 if (!Publics) {
326 auto DbiS = getPDBDbiStream();
327 if (!DbiS)
328 return DbiS.takeError();
329
330 auto PublicS =
331 safelyCreateIndexedStream(DbiS->getPublicSymbolStreamIndex());
332 if (!PublicS)
333 return PublicS.takeError();
334 auto TempPublics = std::make_unique<PublicsStream>(std::move(*PublicS));
335 if (auto EC = TempPublics->reload())
336 return std::move(EC);
337 Publics = std::move(TempPublics);
338 }
339 return *Publics;
340}
341
343 if (!Symbols) {
344 auto DbiS = getPDBDbiStream();
345 if (!DbiS)
346 return DbiS.takeError();
347
348 uint32_t SymbolStreamNum = DbiS->getSymRecordStreamIndex();
349 auto SymbolS = safelyCreateIndexedStream(SymbolStreamNum);
350 if (!SymbolS)
351 return SymbolS.takeError();
352
353 auto TempSymbols = std::make_unique<SymbolStream>(std::move(*SymbolS));
354 if (auto EC = TempSymbols->reload())
355 return std::move(EC);
356 Symbols = std::move(TempSymbols);
357 }
358 return *Symbols;
359}
360
362 if (!Strings) {
363 auto NS = safelyCreateNamedStream("/names");
364 if (!NS)
365 return NS.takeError();
366
367 auto N = std::make_unique<PDBStringTable>();
368 BinaryStreamReader Reader(**NS);
369 if (auto EC = N->reload(Reader))
370 return std::move(EC);
371 assert(Reader.bytesRemaining() == 0);
372 StringTableStream = std::move(*NS);
373 Strings = std::move(N);
374 }
375 return *Strings;
376}
377
379 if (!InjectedSources) {
380 auto IJS = safelyCreateNamedStream("/src/headerblock");
381 if (!IJS)
382 return IJS.takeError();
383
384 auto Strings = getStringTable();
385 if (!Strings)
386 return Strings.takeError();
387
388 auto IJ = std::make_unique<InjectedSourceStream>(std::move(*IJS));
389 if (auto EC = IJ->reload(*Strings))
390 return std::move(EC);
391 InjectedSources = std::move(IJ);
392 }
393 return *InjectedSources;
394}
395
397 if (!Dxc) {
399 if (!MBS)
400 return MBS.takeError();
401 auto StreamSize = getStreamByteSize(StreamDXContainer);
402 ArrayRef<uint8_t> StreamData;
403 auto Error = MBS->get()->readBytes(0, StreamSize, StreamData);
404 if (Error)
405 return Error;
406
407 StringRef Ref(reinterpret_cast<const char *>(StreamData.data()),
408 StreamSize);
409 MemoryBufferRef MemBuf(Ref, "DXContainerStream");
410 auto DXC = object::DXContainer::create(MemBuf);
411 if (!DXC)
412 return DXC.takeError();
413 Dxc = std::make_unique<object::DXContainer>(std::move(*DXC));
414 }
415 return *Dxc;
416}
417
419 auto DbiS = getPDBDbiStream();
420 if (!DbiS)
421 return 0;
422 PDB_Machine Machine = DbiS->getMachineType();
424 return 8;
425 return 4;
426}
427
431
433 auto DbiS = getPDBDbiStream();
434 if (!DbiS) {
435 consumeError(DbiS.takeError());
436 return false;
437 }
438
439 return DbiS->getGlobalSymbolStreamIndex() < getNumStreams();
440}
441
443
445 if (!hasPDBInfoStream())
446 return false;
447
448 if (StreamIPI >= getNumStreams())
449 return false;
450
451 auto &InfoStream = cantFail(const_cast<PDBFile *>(this)->getPDBInfoStream());
453}
454
456 auto DbiS = getPDBDbiStream();
457 if (!DbiS) {
458 consumeError(DbiS.takeError());
459 return false;
460 }
461 return DbiS->getPublicSymbolStreamIndex() < getNumStreams();
462}
463
465 auto DbiS = getPDBDbiStream();
466 if (!DbiS)
467 return false;
468 return DbiS->getSymRecordStreamIndex() < getNumStreams();
469}
470
474
476 auto IS = getPDBInfoStream();
477 if (!IS)
478 return false;
479 Expected<uint32_t> ExpectedNSI = IS->getNamedStreamIndex("/names");
480 if (!ExpectedNSI) {
481 consumeError(ExpectedNSI.takeError());
482 return false;
483 }
484 assert(*ExpectedNSI < getNumStreams());
485 return true;
486}
487
489 auto IS = getPDBInfoStream();
490 if (!IS)
491 return false;
492 Expected<uint32_t> ExpectedNSI = IS->getNamedStreamIndex("/src/headerblock");
493 if (!ExpectedNSI) {
494 consumeError(ExpectedNSI.takeError());
495 return false;
496 }
497 assert(*ExpectedNSI < getNumStreams());
498 return true;
499}
500
501/// Wrapper around MappedBlockStream::createIndexedStream() that checks if a
502/// stream with that index actually exists. If it does not, the return value
503/// will have an MSFError with code msf_error_code::no_stream. Else, the return
504/// value will contain the stream returned by createIndexedStream().
507 if (StreamIndex >= getNumStreams())
508 // This rejects kInvalidStreamIndex with an error as well.
510 return createIndexedStream(StreamIndex);
511}
512
515 auto IS = getPDBInfoStream();
516 if (!IS)
517 return IS.takeError();
518
519 Expected<uint32_t> ExpectedNSI = IS->getNamedStreamIndex(Name);
520 if (!ExpectedNSI)
521 return ExpectedNSI.takeError();
522 uint32_t NameStreamIndex = *ExpectedNSI;
523
524 return safelyCreateIndexedStream(NameStreamIndex);
525}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
#define I(x, y, z)
Definition MD5.cpp:57
Function const char TargetMachine * Machine
Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition ArrayRef.h:40
const T * data() const
Definition ArrayRef.h:138
Provides read only access to a subclass of BinaryStream.
Error readObject(const T *&Dest)
Get a pointer to an object of type T from the underlying stream, as if by memcpy, and store the resul...
LLVM_ABI Error readBytes(ArrayRef< uint8_t > &Buffer, uint32_t Size)
Read Size bytes from the underlying stream at the current offset and and set Buffer to the resulting ...
Error readInteger(T &Dest)
Read an integer of the specified endianness into Dest and update the stream's offset.
Error readArray(ArrayRef< T > &Array, uint32_t NumElements)
Get a reference to a NumElements element array of objects of type T from the underlying stream as if ...
Lightweight error class with error context and mandatory checking.
Definition Error.h:159
static ErrorSuccess success()
Create a success value.
Definition Error.h:336
Tagged union holding either a T or a Error.
Definition Error.h:485
Error takeError()
Take ownership of the stored error.
Definition Error.h:612
Represent a constant reference to a string, i.e.
Definition StringRef.h:56
Describes the layout of a stream in an MSF layout.
Definition MSFCommon.h:78
static std::unique_ptr< MappedBlockStream > createIndexedStream(const MSFLayout &Layout, BinaryStreamRef MsfData, uint32_t StreamIndex, BumpPtrAllocator &Allocator)
static std::unique_ptr< MappedBlockStream > createDirectoryStream(const MSFLayout &Layout, BinaryStreamRef MsfData, BumpPtrAllocator &Allocator)
static std::unique_ptr< MappedBlockStream > createFpmStream(const MSFLayout &Layout, BinaryStreamRef MsfData, BumpPtrAllocator &Allocator)
static LLVM_ABI Expected< DXContainer > create(MemoryBufferRef Object)
LLVM_ABI bool containsIdStream() const
uint32_t getNumDirectoryBlocks() const
Definition PDBFile.cpp:69
Error parseStreamData()
Definition PDBFile.cpp:174
bool hasPDBInjectedSourceStream()
Definition PDBFile.cpp:488
Expected< InjectedSourceStream & > getInjectedSourceStream()
Definition PDBFile.cpp:378
Expected< GlobalsStream & > getPDBGlobalsStream()
Definition PDBFile.cpp:251
uint32_t getPointerSize()
Definition PDBFile.cpp:418
Expected< TpiStream & > getPDBIpiStream()
Definition PDBFile.cpp:308
uint32_t getBlockSize() const override
Definition PDBFile.cpp:49
Expected< std::unique_ptr< msf::MappedBlockStream > > safelyCreateIndexedStream(uint32_t StreamIndex) const
Wrapper around MappedBlockStream::createIndexedStream() that checks if a stream with that index actua...
Definition PDBFile.cpp:506
uint32_t getUnknown1() const
Definition PDBFile.cpp:67
uint32_t getBlockMapIndex() const
Definition PDBFile.cpp:63
Expected< DbiStream & > getPDBDbiStream()
Definition PDBFile.cpp:282
Expected< InfoStream & > getPDBInfoStream()
Definition PDBFile.cpp:269
bool hasPDBTpiStream() const
Definition PDBFile.cpp:471
PDBFile(StringRef Path, std::unique_ptr< BinaryStream > PdbFileBuffer, BumpPtrAllocator &Allocator)
Definition PDBFile.cpp:36
bool hasPDBSymbolStream()
Definition PDBFile.cpp:464
bool hasPDBPublicsStream()
Definition PDBFile.cpp:455
uint32_t getBlockCount() const override
Definition PDBFile.cpp:55
bool hasPDBGlobalsStream()
Definition PDBFile.cpp:432
StringRef getFileDirectory() const
Definition PDBFile.cpp:45
uint32_t getMaxStreamSize() const
Definition PDBFile.cpp:83
msf::MSFStreamLayout getFpmStreamLayout() const
Definition PDBFile.cpp:247
Expected< PublicsStream & > getPDBPublicsStream()
Definition PDBFile.cpp:324
Error setBlockData(uint32_t BlockIndex, uint32_t Offset, ArrayRef< uint8_t > Data) const override
Definition PDBFile.cpp:108
Expected< TpiStream & > getPDBTpiStream()
Definition PDBFile.cpp:295
msf::MSFStreamLayout getStreamLayout(uint32_t StreamIdx) const
Definition PDBFile.cpp:239
bool hasPDBIpiStream() const
Definition PDBFile.cpp:444
Expected< SymbolStream & > getPDBSymbolStream()
Definition PDBFile.cpp:342
uint64_t getBlockMapOffset() const
Definition PDBFile.cpp:74
Error parseFileHeaders()
Definition PDBFile.cpp:114
~PDBFile() override
uint64_t getFileSize() const
Definition PDBFile.cpp:96
Expected< ArrayRef< uint8_t > > getBlockData(uint32_t BlockIndex, uint32_t NumBytes) const override
Definition PDBFile.cpp:98
uint32_t getFreeBlockMapBlock() const
Definition PDBFile.cpp:51
uint32_t getStreamByteSize(uint32_t StreamIndex) const override
Definition PDBFile.cpp:87
uint32_t getNumStreams() const override
Definition PDBFile.cpp:79
bool hasPDBInfoStream() const
Definition PDBFile.cpp:442
StringRef getFilePath() const
Definition PDBFile.cpp:43
ArrayRef< support::ulittle32_t > getStreamBlockList(uint32_t StreamIndex) const override
Definition PDBFile.cpp:92
uint32_t getNumDirectoryBytes() const
Definition PDBFile.cpp:59
bool hasPDBDbiStream() const
Definition PDBFile.cpp:428
Expected< std::unique_ptr< msf::MappedBlockStream > > safelyCreateNamedStream(StringRef Name)
Definition PDBFile.cpp:514
ArrayRef< support::ulittle32_t > getDirectoryBlockArray() const
Definition PDBFile.cpp:227
bool hasPDBStringTable()
Definition PDBFile.cpp:475
Expected< PDBStringTable & > getStringTable()
Definition PDBFile.cpp:361
Expected< object::DXContainer & > getDXContainerStream()
Definition PDBFile.cpp:396
std::unique_ptr< msf::MappedBlockStream > createIndexedStream(uint16_t SN) const
Definition PDBFile.cpp:232
LLVM_ABI Error validateSuperBlock(const SuperBlock &SB)
Definition MSFCommon.cpp:19
uint64_t blockToOffset(uint64_t BlockNumber, uint64_t BlockSize)
Definition MSFCommon.h:136
LLVM_ABI MSFStreamLayout getFpmStreamLayout(const MSFLayout &Msf, bool IncludeUnusedFpmData=false, bool AltFpm=false)
Determine the layout of the FPM stream, given the MSF layout.
Definition MSFCommon.cpp:62
uint64_t bytesToBlocks(uint64_t NumBytes, uint64_t BlockSize)
Definition MSFCommon.h:132
const uint16_t kInvalidStreamIndex
@ StreamDXContainer
LLVM_ABI StringRef parent_path(StringRef path LLVM_LIFETIME_BOUND, Style style=Style::native)
Get parent path.
Definition Path.cpp:478
This is an optimization pass for GlobalISel generic memory operations.
Error make_error(ArgTs &&... Args)
Make a Error instance representing failure using the given error info type.
Definition Error.h:340
@ Ref
The access may reference the value stored in memory.
Definition ModRef.h:32
void cantFail(Error Err, const char *Msg=nullptr)
Report a fatal error if Err is a failure value.
Definition Error.h:769
auto max_element(R &&Range)
Provide wrappers to std::max_element which take ranges instead of having to pass begin/end explicitly...
Definition STLExtras.h:2104
OutputIt move(R &&Range, OutputIt Out)
Provide wrappers to std::move which take ranges instead of having to pass begin/end explicitly.
Definition STLExtras.h:1933
BumpPtrAllocatorImpl<> BumpPtrAllocator
The standard BumpPtrAllocator which just uses the default template parameters.
Definition Allocator.h:391
void consumeError(Error Err)
Consume a Error without doing anything.
Definition Error.h:1106
Implement std::hash so that hash_code can be used in STL containers.
Definition BitVector.h:878
#define N
support::ulittle32_t NumBlocks
Definition MSFCommon.h:43
support::ulittle32_t BlockSize
Definition MSFCommon.h:37