aboutsummaryrefslogtreecommitdiff
path: root/lib/DebugInfo/PDB/Raw/PDBFile.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/DebugInfo/PDB/Raw/PDBFile.cpp')
-rw-r--r--lib/DebugInfo/PDB/Raw/PDBFile.cpp365
1 files changed, 365 insertions, 0 deletions
diff --git a/lib/DebugInfo/PDB/Raw/PDBFile.cpp b/lib/DebugInfo/PDB/Raw/PDBFile.cpp
new file mode 100644
index 000000000000..95016753dc14
--- /dev/null
+++ b/lib/DebugInfo/PDB/Raw/PDBFile.cpp
@@ -0,0 +1,365 @@
+//===- PDBFile.cpp - Low level interface to a PDB file ----------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/DebugInfo/PDB/Raw/PDBFile.h"
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/DebugInfo/CodeView/StreamArray.h"
+#include "llvm/DebugInfo/CodeView/StreamInterface.h"
+#include "llvm/DebugInfo/CodeView/StreamReader.h"
+#include "llvm/DebugInfo/CodeView/StreamWriter.h"
+#include "llvm/DebugInfo/PDB/Raw/DbiStream.h"
+#include "llvm/DebugInfo/PDB/Raw/DirectoryStreamData.h"
+#include "llvm/DebugInfo/PDB/Raw/IndexedStreamData.h"
+#include "llvm/DebugInfo/PDB/Raw/InfoStream.h"
+#include "llvm/DebugInfo/PDB/Raw/NameHashTable.h"
+#include "llvm/DebugInfo/PDB/Raw/PublicsStream.h"
+#include "llvm/DebugInfo/PDB/Raw/RawError.h"
+#include "llvm/DebugInfo/PDB/Raw/SymbolStream.h"
+#include "llvm/DebugInfo/PDB/Raw/TpiStream.h"
+#include "llvm/Support/Endian.h"
+#include "llvm/Support/FileOutputBuffer.h"
+#include "llvm/Support/MemoryBuffer.h"
+
+using namespace llvm;
+using namespace llvm::codeview;
+using namespace llvm::pdb;
+
+namespace {
+typedef FixedStreamArray<support::ulittle32_t> ulittle_array;
+}
+
+PDBFile::PDBFile(std::unique_ptr<StreamInterface> PdbFileBuffer)
+ : Buffer(std::move(PdbFileBuffer)), SB(nullptr) {}
+
+PDBFile::~PDBFile() {}
+
+uint32_t PDBFile::getBlockSize() const { return SB->BlockSize; }
+
+uint32_t PDBFile::getFreeBlockMapBlock() const { return SB->FreeBlockMapBlock; }
+
+uint32_t PDBFile::getBlockCount() const { return SB->NumBlocks; }
+
+uint32_t PDBFile::getNumDirectoryBytes() const { return SB->NumDirectoryBytes; }
+
+uint32_t PDBFile::getBlockMapIndex() const { return SB->BlockMapAddr; }
+
+uint32_t PDBFile::getUnknown1() const { return SB->Unknown1; }
+
+uint32_t PDBFile::getNumDirectoryBlocks() const {
+ return msf::bytesToBlocks(SB->NumDirectoryBytes, SB->BlockSize);
+}
+
+uint64_t PDBFile::getBlockMapOffset() const {
+ return (uint64_t)SB->BlockMapAddr * SB->BlockSize;
+}
+
+uint32_t PDBFile::getNumStreams() const { return StreamSizes.size(); }
+
+uint32_t PDBFile::getStreamByteSize(uint32_t StreamIndex) const {
+ return StreamSizes[StreamIndex];
+}
+
+ArrayRef<support::ulittle32_t>
+PDBFile::getStreamBlockList(uint32_t StreamIndex) const {
+ return StreamMap[StreamIndex];
+}
+
+uint32_t PDBFile::getFileSize() const { return Buffer->getLength(); }
+
+Expected<ArrayRef<uint8_t>> PDBFile::getBlockData(uint32_t BlockIndex,
+ uint32_t NumBytes) const {
+ uint64_t StreamBlockOffset = msf::blockToOffset(BlockIndex, getBlockSize());
+
+ ArrayRef<uint8_t> Result;
+ if (auto EC = Buffer->readBytes(StreamBlockOffset, NumBytes, Result))
+ return std::move(EC);
+ return Result;
+}
+
+Error PDBFile::setBlockData(uint32_t BlockIndex, uint32_t Offset,
+ ArrayRef<uint8_t> Data) const {
+ if (Offset >= getBlockSize())
+ return make_error<RawError>(
+ raw_error_code::invalid_block_address,
+ "setBlockData attempted to write out of block bounds.");
+ if (Data.size() > getBlockSize() - Offset)
+ return make_error<RawError>(
+ raw_error_code::invalid_block_address,
+ "setBlockData attempted to write out of block bounds.");
+
+ uint64_t StreamBlockOffset = msf::blockToOffset(BlockIndex, getBlockSize());
+ StreamBlockOffset += Offset;
+ return Buffer->writeBytes(StreamBlockOffset, Data);
+}
+
+Error PDBFile::parseFileHeaders() {
+ StreamReader Reader(*Buffer);
+
+ if (auto EC = Reader.readObject(SB)) {
+ consumeError(std::move(EC));
+ return make_error<RawError>(raw_error_code::corrupt_file,
+ "Does not contain superblock");
+ }
+
+ if (auto EC = setSuperBlock(SB))
+ return EC;
+
+ Reader.setOffset(getBlockMapOffset());
+ if (auto EC = Reader.readArray(DirectoryBlocks, getNumDirectoryBlocks()))
+ return EC;
+
+ return Error::success();
+}
+
+Error PDBFile::parseStreamData() {
+ assert(SB);
+ if (DirectoryStream)
+ return Error::success();
+
+ uint32_t NumStreams = 0;
+
+ // Normally you can't use a MappedBlockStream without having fully parsed the
+ // PDB file, because it accesses the directory and various other things, which
+ // is exactly what we are attempting to parse. By specifying a custom
+ // subclass of IPDBStreamData which only accesses the fields that have already
+ // been parsed, we can avoid this and reuse MappedBlockStream.
+ auto DS = MappedBlockStream::createDirectoryStream(*this);
+ if (!DS)
+ return DS.takeError();
+ StreamReader Reader(**DS);
+ if (auto EC = Reader.readInteger(NumStreams))
+ return EC;
+
+ if (auto EC = Reader.readArray(StreamSizes, NumStreams))
+ return EC;
+ for (uint32_t I = 0; I < NumStreams; ++I) {
+ uint32_t StreamSize = getStreamByteSize(I);
+ // FIXME: What does StreamSize ~0U mean?
+ uint64_t NumExpectedStreamBlocks =
+ StreamSize == UINT32_MAX ? 0 : msf::bytesToBlocks(StreamSize,
+ SB->BlockSize);
+
+ // For convenience, we store the block array contiguously. This is because
+ // if someone calls setStreamMap(), it is more convenient to be able to call
+ // it with an ArrayRef instead of setting up a StreamRef. Since the
+ // DirectoryStream is cached in the class and thus lives for the life of the
+ // class, we can be guaranteed that readArray() will return a stable
+ // reference, even if it has to allocate from its internal pool.
+ ArrayRef<support::ulittle32_t> Blocks;
+ if (auto EC = Reader.readArray(Blocks, NumExpectedStreamBlocks))
+ return EC;
+ for (uint32_t Block : Blocks) {
+ uint64_t BlockEndOffset = (uint64_t)(Block + 1) * SB->BlockSize;
+ if (BlockEndOffset > getFileSize())
+ return make_error<RawError>(raw_error_code::corrupt_file,
+ "Stream block map is corrupt.");
+ }
+ StreamMap.push_back(Blocks);
+ }
+
+ // We should have read exactly SB->NumDirectoryBytes bytes.
+ assert(Reader.bytesRemaining() == 0);
+ DirectoryStream = std::move(*DS);
+ return Error::success();
+}
+
+llvm::ArrayRef<support::ulittle32_t> PDBFile::getDirectoryBlockArray() const {
+ return DirectoryBlocks;
+}
+
+Expected<InfoStream &> PDBFile::getPDBInfoStream() {
+ if (!Info) {
+ auto InfoS = MappedBlockStream::createIndexedStream(StreamPDB, *this);
+ if (!InfoS)
+ return InfoS.takeError();
+ auto TempInfo = llvm::make_unique<InfoStream>(std::move(*InfoS));
+ if (auto EC = TempInfo->reload())
+ return std::move(EC);
+ Info = std::move(TempInfo);
+ }
+ return *Info;
+}
+
+Expected<DbiStream &> PDBFile::getPDBDbiStream() {
+ if (!Dbi) {
+ auto DbiS = MappedBlockStream::createIndexedStream(StreamDBI, *this);
+ if (!DbiS)
+ return DbiS.takeError();
+ auto TempDbi = llvm::make_unique<DbiStream>(*this, std::move(*DbiS));
+ if (auto EC = TempDbi->reload())
+ return std::move(EC);
+ Dbi = std::move(TempDbi);
+ }
+ return *Dbi;
+}
+
+Expected<TpiStream &> PDBFile::getPDBTpiStream() {
+ if (!Tpi) {
+ auto TpiS = MappedBlockStream::createIndexedStream(StreamTPI, *this);
+ if (!TpiS)
+ return TpiS.takeError();
+ auto TempTpi = llvm::make_unique<TpiStream>(*this, std::move(*TpiS));
+ if (auto EC = TempTpi->reload())
+ return std::move(EC);
+ Tpi = std::move(TempTpi);
+ }
+ return *Tpi;
+}
+
+Expected<TpiStream &> PDBFile::getPDBIpiStream() {
+ if (!Ipi) {
+ auto IpiS = MappedBlockStream::createIndexedStream(StreamIPI, *this);
+ if (!IpiS)
+ return IpiS.takeError();
+ auto TempIpi = llvm::make_unique<TpiStream>(*this, std::move(*IpiS));
+ if (auto EC = TempIpi->reload())
+ return std::move(EC);
+ Ipi = std::move(TempIpi);
+ }
+ return *Ipi;
+}
+
+Expected<PublicsStream &> PDBFile::getPDBPublicsStream() {
+ if (!Publics) {
+ auto DbiS = getPDBDbiStream();
+ if (!DbiS)
+ return DbiS.takeError();
+
+ uint32_t PublicsStreamNum = DbiS->getPublicSymbolStreamIndex();
+
+ auto PublicS =
+ MappedBlockStream::createIndexedStream(PublicsStreamNum, *this);
+ if (!PublicS)
+ return PublicS.takeError();
+ auto TempPublics =
+ llvm::make_unique<PublicsStream>(*this, std::move(*PublicS));
+ if (auto EC = TempPublics->reload())
+ return std::move(EC);
+ Publics = std::move(TempPublics);
+ }
+ return *Publics;
+}
+
+Expected<SymbolStream &> PDBFile::getPDBSymbolStream() {
+ if (!Symbols) {
+ auto DbiS = getPDBDbiStream();
+ if (!DbiS)
+ return DbiS.takeError();
+
+ uint32_t SymbolStreamNum = DbiS->getSymRecordStreamIndex();
+
+ auto SymbolS =
+ MappedBlockStream::createIndexedStream(SymbolStreamNum, *this);
+ if (!SymbolS)
+ return SymbolS.takeError();
+ auto TempSymbols = llvm::make_unique<SymbolStream>(std::move(*SymbolS));
+ if (auto EC = TempSymbols->reload())
+ return std::move(EC);
+ Symbols = std::move(TempSymbols);
+ }
+ return *Symbols;
+}
+
+Expected<NameHashTable &> PDBFile::getStringTable() {
+ if (!StringTable || !StringTableStream) {
+ auto IS = getPDBInfoStream();
+ if (!IS)
+ return IS.takeError();
+
+ uint32_t NameStreamIndex = IS->getNamedStreamIndex("/names");
+
+ if (NameStreamIndex == 0)
+ return make_error<RawError>(raw_error_code::no_stream);
+ if (NameStreamIndex >= getNumStreams())
+ return make_error<RawError>(raw_error_code::no_stream);
+
+ auto NS = MappedBlockStream::createIndexedStream(NameStreamIndex, *this);
+ if (!NS)
+ return NS.takeError();
+
+ StreamReader Reader(**NS);
+ auto N = llvm::make_unique<NameHashTable>();
+ if (auto EC = N->load(Reader))
+ return std::move(EC);
+ StringTable = std::move(N);
+ StringTableStream = std::move(*NS);
+ }
+ return *StringTable;
+}
+
+Error PDBFile::setSuperBlock(const msf::SuperBlock *Block) {
+ if (auto EC = msf::validateSuperBlock(*Block))
+ return EC;
+
+ if (Buffer->getLength() % SB->BlockSize != 0)
+ return make_error<RawError>(raw_error_code::corrupt_file,
+ "File size is not a multiple of block size");
+
+ SB = Block;
+ return Error::success();
+}
+
+Error PDBFile::commit() {
+ StreamWriter Writer(*Buffer);
+
+ if (auto EC = Writer.writeObject(*SB))
+ return EC;
+ Writer.setOffset(getBlockMapOffset());
+ if (auto EC = Writer.writeArray(DirectoryBlocks))
+ return EC;
+
+ auto DS = MappedBlockStream::createDirectoryStream(*this);
+ if (!DS)
+ return DS.takeError();
+ auto DirStream = std::move(*DS);
+ StreamWriter DW(*DirStream);
+ if (auto EC = DW.writeInteger(this->getNumStreams()))
+ return EC;
+
+ if (auto EC = DW.writeArray(StreamSizes))
+ return EC;
+
+ for (const auto &Blocks : StreamMap) {
+ if (auto EC = DW.writeArray(Blocks))
+ return EC;
+ }
+
+ if (Info) {
+ if (auto EC = Info->commit())
+ return EC;
+ }
+
+ if (Dbi) {
+ if (auto EC = Dbi->commit())
+ return EC;
+ }
+
+ if (Symbols) {
+ if (auto EC = Symbols->commit())
+ return EC;
+ }
+
+ if (Publics) {
+ if (auto EC = Publics->commit())
+ return EC;
+ }
+
+ if (Tpi) {
+ if (auto EC = Tpi->commit())
+ return EC;
+ }
+
+ if (Ipi) {
+ if (auto EC = Ipi->commit())
+ return EC;
+ }
+
+ return Buffer->commit();
+}