brintos

brintos / llvm-project-archived public Read only

0
0
Text · 16.1 KiB · 0232cae Raw
507 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8 9#include "llvm/DebugInfo/PDB/Native/PDBFile.h"10#include "llvm/ADT/ArrayRef.h"11#include "llvm/DebugInfo/MSF/MSFCommon.h"12#include "llvm/DebugInfo/MSF/MappedBlockStream.h"13#include "llvm/DebugInfo/PDB/Native/DbiStream.h"14#include "llvm/DebugInfo/PDB/Native/GlobalsStream.h"15#include "llvm/DebugInfo/PDB/Native/InfoStream.h"16#include "llvm/DebugInfo/PDB/Native/InjectedSourceStream.h"17#include "llvm/DebugInfo/PDB/Native/PDBStringTable.h"18#include "llvm/DebugInfo/PDB/Native/PublicsStream.h"19#include "llvm/DebugInfo/PDB/Native/RawError.h"20#include "llvm/DebugInfo/PDB/Native/SymbolStream.h"21#include "llvm/DebugInfo/PDB/Native/TpiStream.h"22#include "llvm/Support/BinaryStream.h"23#include "llvm/Support/BinaryStreamArray.h"24#include "llvm/Support/BinaryStreamReader.h"25#include "llvm/Support/Endian.h"26#include "llvm/Support/Error.h"27#include "llvm/Support/Path.h"28#include <algorithm>29#include <cassert>30#include <cstdint>31 32using namespace llvm;33using namespace llvm::codeview;34using namespace llvm::msf;35using namespace llvm::pdb;36 37namespace {38typedef FixedStreamArray<support::ulittle32_t> ulittle_array;39} // end anonymous namespace40 41PDBFile::PDBFile(StringRef Path, std::unique_ptr<BinaryStream> PdbFileBuffer,42                 BumpPtrAllocator &Allocator)43    : FilePath(std::string(Path)), Allocator(Allocator),44      Buffer(std::move(PdbFileBuffer)) {}45 46PDBFile::~PDBFile() = default;47 48StringRef PDBFile::getFilePath() const { return FilePath; }49 50StringRef PDBFile::getFileDirectory() const {51  return sys::path::parent_path(FilePath);52}53 54uint32_t PDBFile::getBlockSize() const { return ContainerLayout.SB->BlockSize; }55 56uint32_t PDBFile::getFreeBlockMapBlock() const {57  return ContainerLayout.SB->FreeBlockMapBlock;58}59 60uint32_t PDBFile::getBlockCount() const {61  return ContainerLayout.SB->NumBlocks;62}63 64uint32_t PDBFile::getNumDirectoryBytes() const {65  return ContainerLayout.SB->NumDirectoryBytes;66}67 68uint32_t PDBFile::getBlockMapIndex() const {69  return ContainerLayout.SB->BlockMapAddr;70}71 72uint32_t PDBFile::getUnknown1() const { return ContainerLayout.SB->Unknown1; }73 74uint32_t PDBFile::getNumDirectoryBlocks() const {75  return msf::bytesToBlocks(ContainerLayout.SB->NumDirectoryBytes,76                            ContainerLayout.SB->BlockSize);77}78 79uint64_t PDBFile::getBlockMapOffset() const {80  return (uint64_t)ContainerLayout.SB->BlockMapAddr *81         ContainerLayout.SB->BlockSize;82}83 84uint32_t PDBFile::getNumStreams() const {85  return ContainerLayout.StreamSizes.size();86}87 88uint32_t PDBFile::getMaxStreamSize() const {89  return *llvm::max_element(ContainerLayout.StreamSizes);90}91 92uint32_t PDBFile::getStreamByteSize(uint32_t StreamIndex) const {93  return ContainerLayout.StreamSizes[StreamIndex];94}95 96ArrayRef<support::ulittle32_t>97PDBFile::getStreamBlockList(uint32_t StreamIndex) const {98  return ContainerLayout.StreamMap[StreamIndex];99}100 101uint64_t PDBFile::getFileSize() const { return Buffer->getLength(); }102 103Expected<ArrayRef<uint8_t>> PDBFile::getBlockData(uint32_t BlockIndex,104                                                  uint32_t NumBytes) const {105  uint64_t StreamBlockOffset = msf::blockToOffset(BlockIndex, getBlockSize());106 107  ArrayRef<uint8_t> Result;108  if (auto EC = Buffer->readBytes(StreamBlockOffset, NumBytes, Result))109    return std::move(EC);110  return Result;111}112 113Error PDBFile::setBlockData(uint32_t BlockIndex, uint32_t Offset,114                            ArrayRef<uint8_t> Data) const {115  return make_error<RawError>(raw_error_code::not_writable,116                              "PDBFile is immutable");117}118 119Error PDBFile::parseFileHeaders() {120  BinaryStreamReader Reader(*Buffer);121 122  // Initialize SB.123  const msf::SuperBlock *SB = nullptr;124  if (auto EC = Reader.readObject(SB)) {125    consumeError(std::move(EC));126    return make_error<RawError>(raw_error_code::corrupt_file,127                                "MSF superblock is missing");128  }129 130  if (auto EC = msf::validateSuperBlock(*SB))131    return EC;132 133  if (Buffer->getLength() % SB->BlockSize != 0)134    return make_error<RawError>(raw_error_code::corrupt_file,135                                "File size is not a multiple of block size");136  ContainerLayout.SB = SB;137 138  // Initialize Free Page Map.139  ContainerLayout.FreePageMap.resize(SB->NumBlocks);140  // The Fpm exists either at block 1 or block 2 of the MSF.  However, this141  // allows for a maximum of getBlockSize() * 8 blocks bits in the Fpm, and142  // thusly an equal number of total blocks in the file.  For a block size143  // of 4KiB (very common), this would yield 32KiB total blocks in file, for a144  // maximum file size of 32KiB * 4KiB = 128MiB.  Obviously this won't do, so145  // the Fpm is split across the file at `getBlockSize()` intervals.  As a146  // result, every block whose index is of the form |{1,2} + getBlockSize() * k|147  // for any non-negative integer k is an Fpm block.  In theory, we only really148  // need to reserve blocks of the form |{1,2} + getBlockSize() * 8 * k|, but149  // current versions of the MSF format already expect the Fpm to be arranged150  // at getBlockSize() intervals, so we have to be compatible.151  // See the function fpmPn() for more information:152  // https://github.com/Microsoft/microsoft-pdb/blob/master/PDB/msf/msf.cpp#L489153  auto FpmStream =154      MappedBlockStream::createFpmStream(ContainerLayout, *Buffer, Allocator);155  BinaryStreamReader FpmReader(*FpmStream);156  ArrayRef<uint8_t> FpmBytes;157  if (auto EC = FpmReader.readBytes(FpmBytes, FpmReader.bytesRemaining()))158    return EC;159  uint32_t BlocksRemaining = getBlockCount();160  uint32_t BI = 0;161  for (auto Byte : FpmBytes) {162    uint32_t BlocksThisByte = std::min(BlocksRemaining, 8U);163    for (uint32_t I = 0; I < BlocksThisByte; ++I) {164      if (Byte & (1 << I))165        ContainerLayout.FreePageMap[BI] = true;166      --BlocksRemaining;167      ++BI;168    }169  }170 171  Reader.setOffset(getBlockMapOffset());172  if (auto EC = Reader.readArray(ContainerLayout.DirectoryBlocks,173                                 getNumDirectoryBlocks()))174    return EC;175 176  return Error::success();177}178 179Error PDBFile::parseStreamData() {180  assert(ContainerLayout.SB);181  if (DirectoryStream)182    return Error::success();183 184  uint32_t NumStreams = 0;185 186  // Normally you can't use a MappedBlockStream without having fully parsed the187  // PDB file, because it accesses the directory and various other things, which188  // is exactly what we are attempting to parse.  By specifying a custom189  // subclass of IPDBStreamData which only accesses the fields that have already190  // been parsed, we can avoid this and reuse MappedBlockStream.191  auto DS = MappedBlockStream::createDirectoryStream(ContainerLayout, *Buffer,192                                                     Allocator);193  BinaryStreamReader Reader(*DS);194  if (auto EC = Reader.readInteger(NumStreams))195    return EC;196 197  if (auto EC = Reader.readArray(ContainerLayout.StreamSizes, NumStreams))198    return EC;199  for (uint32_t I = 0; I < NumStreams; ++I) {200    uint32_t StreamSize = getStreamByteSize(I);201    // FIXME: What does StreamSize ~0U mean?202    uint64_t NumExpectedStreamBlocks =203        StreamSize == UINT32_MAX204            ? 0205            : msf::bytesToBlocks(StreamSize, ContainerLayout.SB->BlockSize);206 207    // For convenience, we store the block array contiguously.  This is because208    // if someone calls setStreamMap(), it is more convenient to be able to call209    // it with an ArrayRef instead of setting up a StreamRef.  Since the210    // DirectoryStream is cached in the class and thus lives for the life of the211    // class, we can be guaranteed that readArray() will return a stable212    // reference, even if it has to allocate from its internal pool.213    ArrayRef<support::ulittle32_t> Blocks;214    if (auto EC = Reader.readArray(Blocks, NumExpectedStreamBlocks))215      return EC;216    for (uint32_t Block : Blocks) {217      uint64_t BlockEndOffset =218          (uint64_t)(Block + 1) * ContainerLayout.SB->BlockSize;219      if (BlockEndOffset > getFileSize())220        return make_error<RawError>(raw_error_code::corrupt_file,221                                    "Stream block map is corrupt.");222    }223    ContainerLayout.StreamMap.push_back(Blocks);224  }225 226  // We should have read exactly SB->NumDirectoryBytes bytes.227  assert(Reader.bytesRemaining() == 0);228  DirectoryStream = std::move(DS);229  return Error::success();230}231 232ArrayRef<support::ulittle32_t> PDBFile::getDirectoryBlockArray() const {233  return ContainerLayout.DirectoryBlocks;234}235 236std::unique_ptr<MappedBlockStream>237PDBFile::createIndexedStream(uint16_t SN) const {238  if (SN == kInvalidStreamIndex)239    return nullptr;240  return MappedBlockStream::createIndexedStream(ContainerLayout, *Buffer, SN,241                                                Allocator);242}243 244MSFStreamLayout PDBFile::getStreamLayout(uint32_t StreamIdx) const {245  MSFStreamLayout Result;246  auto Blocks = getStreamBlockList(StreamIdx);247  Result.Blocks.assign(Blocks.begin(), Blocks.end());248  Result.Length = getStreamByteSize(StreamIdx);249  return Result;250}251 252msf::MSFStreamLayout PDBFile::getFpmStreamLayout() const {253  return msf::getFpmStreamLayout(ContainerLayout);254}255 256Expected<GlobalsStream &> PDBFile::getPDBGlobalsStream() {257  if (!Globals) {258    auto DbiS = getPDBDbiStream();259    if (!DbiS)260      return DbiS.takeError();261 262    auto GlobalS =263        safelyCreateIndexedStream(DbiS->getGlobalSymbolStreamIndex());264    if (!GlobalS)265      return GlobalS.takeError();266    auto TempGlobals = std::make_unique<GlobalsStream>(std::move(*GlobalS));267    if (auto EC = TempGlobals->reload())268      return std::move(EC);269    Globals = std::move(TempGlobals);270  }271  return *Globals;272}273 274Expected<InfoStream &> PDBFile::getPDBInfoStream() {275  if (!Info) {276    auto InfoS = safelyCreateIndexedStream(StreamPDB);277    if (!InfoS)278      return InfoS.takeError();279    auto TempInfo = std::make_unique<InfoStream>(std::move(*InfoS));280    if (auto EC = TempInfo->reload())281      return std::move(EC);282    Info = std::move(TempInfo);283  }284  return *Info;285}286 287Expected<DbiStream &> PDBFile::getPDBDbiStream() {288  if (!Dbi) {289    auto DbiS = safelyCreateIndexedStream(StreamDBI);290    if (!DbiS)291      return DbiS.takeError();292    auto TempDbi = std::make_unique<DbiStream>(std::move(*DbiS));293    if (auto EC = TempDbi->reload(this))294      return std::move(EC);295    Dbi = std::move(TempDbi);296  }297  return *Dbi;298}299 300Expected<TpiStream &> PDBFile::getPDBTpiStream() {301  if (!Tpi) {302    auto TpiS = safelyCreateIndexedStream(StreamTPI);303    if (!TpiS)304      return TpiS.takeError();305    auto TempTpi = std::make_unique<TpiStream>(*this, std::move(*TpiS));306    if (auto EC = TempTpi->reload())307      return std::move(EC);308    Tpi = std::move(TempTpi);309  }310  return *Tpi;311}312 313Expected<TpiStream &> PDBFile::getPDBIpiStream() {314  if (!Ipi) {315    if (!hasPDBIpiStream())316      return make_error<RawError>(raw_error_code::no_stream);317 318    auto IpiS = safelyCreateIndexedStream(StreamIPI);319    if (!IpiS)320      return IpiS.takeError();321    auto TempIpi = std::make_unique<TpiStream>(*this, std::move(*IpiS));322    if (auto EC = TempIpi->reload())323      return std::move(EC);324    Ipi = std::move(TempIpi);325  }326  return *Ipi;327}328 329Expected<PublicsStream &> PDBFile::getPDBPublicsStream() {330  if (!Publics) {331    auto DbiS = getPDBDbiStream();332    if (!DbiS)333      return DbiS.takeError();334 335    auto PublicS =336        safelyCreateIndexedStream(DbiS->getPublicSymbolStreamIndex());337    if (!PublicS)338      return PublicS.takeError();339    auto TempPublics = std::make_unique<PublicsStream>(std::move(*PublicS));340    if (auto EC = TempPublics->reload())341      return std::move(EC);342    Publics = std::move(TempPublics);343  }344  return *Publics;345}346 347Expected<SymbolStream &> PDBFile::getPDBSymbolStream() {348  if (!Symbols) {349    auto DbiS = getPDBDbiStream();350    if (!DbiS)351      return DbiS.takeError();352 353    uint32_t SymbolStreamNum = DbiS->getSymRecordStreamIndex();354    auto SymbolS = safelyCreateIndexedStream(SymbolStreamNum);355    if (!SymbolS)356      return SymbolS.takeError();357 358    auto TempSymbols = std::make_unique<SymbolStream>(std::move(*SymbolS));359    if (auto EC = TempSymbols->reload())360      return std::move(EC);361    Symbols = std::move(TempSymbols);362  }363  return *Symbols;364}365 366Expected<PDBStringTable &> PDBFile::getStringTable() {367  if (!Strings) {368    auto NS = safelyCreateNamedStream("/names");369    if (!NS)370      return NS.takeError();371 372    auto N = std::make_unique<PDBStringTable>();373    BinaryStreamReader Reader(**NS);374    if (auto EC = N->reload(Reader))375      return std::move(EC);376    assert(Reader.bytesRemaining() == 0);377    StringTableStream = std::move(*NS);378    Strings = std::move(N);379  }380  return *Strings;381}382 383Expected<InjectedSourceStream &> PDBFile::getInjectedSourceStream() {384  if (!InjectedSources) {385    auto IJS = safelyCreateNamedStream("/src/headerblock");386    if (!IJS)387      return IJS.takeError();388 389    auto Strings = getStringTable();390    if (!Strings)391      return Strings.takeError();392 393    auto IJ = std::make_unique<InjectedSourceStream>(std::move(*IJS));394    if (auto EC = IJ->reload(*Strings))395      return std::move(EC);396    InjectedSources = std::move(IJ);397  }398  return *InjectedSources;399}400 401uint32_t PDBFile::getPointerSize() {402  auto DbiS = getPDBDbiStream();403  if (!DbiS)404    return 0;405  PDB_Machine Machine = DbiS->getMachineType();406  if (Machine == PDB_Machine::Amd64)407    return 8;408  return 4;409}410 411bool PDBFile::hasPDBDbiStream() const {412  return StreamDBI < getNumStreams() && getStreamByteSize(StreamDBI) > 0;413}414 415bool PDBFile::hasPDBGlobalsStream() {416  auto DbiS = getPDBDbiStream();417  if (!DbiS) {418    consumeError(DbiS.takeError());419    return false;420  }421 422  return DbiS->getGlobalSymbolStreamIndex() < getNumStreams();423}424 425bool PDBFile::hasPDBInfoStream() const { return StreamPDB < getNumStreams(); }426 427bool PDBFile::hasPDBIpiStream() const {428  if (!hasPDBInfoStream())429    return false;430 431  if (StreamIPI >= getNumStreams())432    return false;433 434  auto &InfoStream = cantFail(const_cast<PDBFile *>(this)->getPDBInfoStream());435  return InfoStream.containsIdStream();436}437 438bool PDBFile::hasPDBPublicsStream() {439  auto DbiS = getPDBDbiStream();440  if (!DbiS) {441    consumeError(DbiS.takeError());442    return false;443  }444  return DbiS->getPublicSymbolStreamIndex() < getNumStreams();445}446 447bool PDBFile::hasPDBSymbolStream() {448  auto DbiS = getPDBDbiStream();449  if (!DbiS)450    return false;451  return DbiS->getSymRecordStreamIndex() < getNumStreams();452}453 454bool PDBFile::hasPDBTpiStream() const { return StreamTPI < getNumStreams(); }455 456bool PDBFile::hasPDBStringTable() {457  auto IS = getPDBInfoStream();458  if (!IS)459    return false;460  Expected<uint32_t> ExpectedNSI = IS->getNamedStreamIndex("/names");461  if (!ExpectedNSI) {462    consumeError(ExpectedNSI.takeError());463    return false;464  }465  assert(*ExpectedNSI < getNumStreams());466  return true;467}468 469bool PDBFile::hasPDBInjectedSourceStream() {470  auto IS = getPDBInfoStream();471  if (!IS)472    return false;473  Expected<uint32_t> ExpectedNSI = IS->getNamedStreamIndex("/src/headerblock");474  if (!ExpectedNSI) {475    consumeError(ExpectedNSI.takeError());476    return false;477  }478  assert(*ExpectedNSI < getNumStreams());479  return true;480}481 482/// Wrapper around MappedBlockStream::createIndexedStream() that checks if a483/// stream with that index actually exists.  If it does not, the return value484/// will have an MSFError with code msf_error_code::no_stream.  Else, the return485/// value will contain the stream returned by createIndexedStream().486Expected<std::unique_ptr<MappedBlockStream>>487PDBFile::safelyCreateIndexedStream(uint32_t StreamIndex) const {488  if (StreamIndex >= getNumStreams())489    // This rejects kInvalidStreamIndex with an error as well.490    return make_error<RawError>(raw_error_code::no_stream);491  return createIndexedStream(StreamIndex);492}493 494Expected<std::unique_ptr<MappedBlockStream>>495PDBFile::safelyCreateNamedStream(StringRef Name) {496  auto IS = getPDBInfoStream();497  if (!IS)498    return IS.takeError();499 500  Expected<uint32_t> ExpectedNSI = IS->getNamedStreamIndex(Name);501  if (!ExpectedNSI)502    return ExpectedNSI.takeError();503  uint32_t NameStreamIndex = *ExpectedNSI;504 505  return safelyCreateIndexedStream(NameStreamIndex);506}507