brintos

brintos / llvm-project-archived public Read only

0
0
Text · 11.4 KiB · 77fa9d3 Raw
372 lines · cpp
1//===- PDBFileBuilder.cpp - PDB File Creation -------------------*- 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/PDBFileBuilder.h"10#include "llvm/ADT/SmallString.h"11#include "llvm/ADT/StringExtras.h"12#include "llvm/DebugInfo/CodeView/GUID.h"13#include "llvm/DebugInfo/MSF/MSFBuilder.h"14#include "llvm/DebugInfo/MSF/MSFCommon.h"15#include "llvm/DebugInfo/MSF/MappedBlockStream.h"16#include "llvm/DebugInfo/PDB/Native/DbiStreamBuilder.h"17#include "llvm/DebugInfo/PDB/Native/GSIStreamBuilder.h"18#include "llvm/DebugInfo/PDB/Native/InfoStreamBuilder.h"19#include "llvm/DebugInfo/PDB/Native/PDBStringTableBuilder.h"20#include "llvm/DebugInfo/PDB/Native/RawConstants.h"21#include "llvm/DebugInfo/PDB/Native/RawError.h"22#include "llvm/DebugInfo/PDB/Native/RawTypes.h"23#include "llvm/DebugInfo/PDB/Native/TpiStreamBuilder.h"24#include "llvm/Support/BinaryStreamWriter.h"25#include "llvm/Support/CRC.h"26#include "llvm/Support/Path.h"27#include "llvm/Support/TimeProfiler.h"28#include "llvm/Support/xxhash.h"29 30#include <ctime>31 32using namespace llvm;33using namespace llvm::codeview;34using namespace llvm::msf;35using namespace llvm::pdb;36using namespace llvm::support;37 38namespace llvm {39class WritableBinaryStream;40}41 42PDBFileBuilder::PDBFileBuilder(BumpPtrAllocator &Allocator)43    : Allocator(Allocator), InjectedSourceHashTraits(Strings),44      InjectedSourceTable(2) {}45 46PDBFileBuilder::~PDBFileBuilder() = default;47 48Error PDBFileBuilder::initialize(uint32_t BlockSize) {49  auto ExpectedMsf = MSFBuilder::create(Allocator, BlockSize);50  if (!ExpectedMsf)51    return ExpectedMsf.takeError();52  Msf = std::make_unique<MSFBuilder>(std::move(*ExpectedMsf));53  return Error::success();54}55 56MSFBuilder &PDBFileBuilder::getMsfBuilder() { return *Msf; }57 58InfoStreamBuilder &PDBFileBuilder::getInfoBuilder() {59  if (!Info)60    Info = std::make_unique<InfoStreamBuilder>(*Msf, NamedStreams);61  return *Info;62}63 64DbiStreamBuilder &PDBFileBuilder::getDbiBuilder() {65  if (!Dbi)66    Dbi = std::make_unique<DbiStreamBuilder>(*Msf);67  return *Dbi;68}69 70TpiStreamBuilder &PDBFileBuilder::getTpiBuilder() {71  if (!Tpi)72    Tpi = std::make_unique<TpiStreamBuilder>(*Msf, StreamTPI);73  return *Tpi;74}75 76TpiStreamBuilder &PDBFileBuilder::getIpiBuilder() {77  if (!Ipi)78    Ipi = std::make_unique<TpiStreamBuilder>(*Msf, StreamIPI);79  return *Ipi;80}81 82PDBStringTableBuilder &PDBFileBuilder::getStringTableBuilder() {83  return Strings;84}85 86GSIStreamBuilder &PDBFileBuilder::getGsiBuilder() {87  if (!Gsi)88    Gsi = std::make_unique<GSIStreamBuilder>(*Msf);89  return *Gsi;90}91 92Expected<uint32_t> PDBFileBuilder::allocateNamedStream(StringRef Name,93                                                       uint32_t Size) {94  auto ExpectedStream = Msf->addStream(Size);95  if (ExpectedStream)96    NamedStreams.set(Name, *ExpectedStream);97  return ExpectedStream;98}99 100Error PDBFileBuilder::addNamedStream(StringRef Name, StringRef Data) {101  Expected<uint32_t> ExpectedIndex = allocateNamedStream(Name, Data.size());102  if (!ExpectedIndex)103    return ExpectedIndex.takeError();104  assert(NamedStreamData.count(*ExpectedIndex) == 0);105  NamedStreamData[*ExpectedIndex] = std::string(Data);106  return Error::success();107}108 109void PDBFileBuilder::addInjectedSource(StringRef Name,110                                       std::unique_ptr<MemoryBuffer> Buffer) {111  // Stream names must be exact matches, since they get looked up in a hash112  // table and the hash value is dependent on the exact contents of the string.113  // link.exe lowercases a path and converts / to \, so we must do the same.114  SmallString<64> VName;115  sys::path::native(Name.lower(), VName, sys::path::Style::windows_backslash);116 117  uint32_t NI = getStringTableBuilder().insert(Name);118  uint32_t VNI = getStringTableBuilder().insert(VName);119 120  InjectedSourceDescriptor Desc;121  Desc.Content = std::move(Buffer);122  Desc.NameIndex = NI;123  Desc.VNameIndex = VNI;124  Desc.StreamName = "/src/files/";125 126  Desc.StreamName += VName;127 128  InjectedSources.push_back(std::move(Desc));129}130 131Error PDBFileBuilder::finalizeMsfLayout() {132  llvm::TimeTraceScope timeScope("MSF layout");133 134  if (Ipi && Ipi->getRecordCount() > 0) {135    // In theory newer PDBs always have an ID stream, but by saying that we're136    // only going to *really* have an ID stream if there is at least one ID137    // record, we leave open the opportunity to test older PDBs such as those138    // that don't have an ID stream.139    auto &Info = getInfoBuilder();140    Info.addFeature(PdbRaw_FeatureSig::VC140);141  }142 143  uint32_t StringsLen = Strings.calculateSerializedSize();144 145  Expected<uint32_t> SN = allocateNamedStream("/LinkInfo", 0);146  if (!SN)147    return SN.takeError();148 149  if (Gsi) {150    if (auto EC = Gsi->finalizeMsfLayout())151      return EC;152    if (Dbi) {153      Dbi->setPublicsStreamIndex(Gsi->getPublicsStreamIndex());154      Dbi->setGlobalsStreamIndex(Gsi->getGlobalsStreamIndex());155      Dbi->setSymbolRecordStreamIndex(Gsi->getRecordStreamIndex());156    }157  }158  if (Tpi) {159    if (auto EC = Tpi->finalizeMsfLayout())160      return EC;161  }162  if (Dbi) {163    if (auto EC = Dbi->finalizeMsfLayout())164      return EC;165  }166  SN = allocateNamedStream("/names", StringsLen);167  if (!SN)168    return SN.takeError();169 170  if (Ipi) {171    if (auto EC = Ipi->finalizeMsfLayout())172      return EC;173  }174 175  // Do this last, since it relies on the named stream map being complete, and176  // that can be updated by previous steps in the finalization.177  if (Info) {178    if (auto EC = Info->finalizeMsfLayout())179      return EC;180  }181 182  if (!InjectedSources.empty()) {183    for (const auto &IS : InjectedSources) {184      JamCRC CRC(0);185      CRC.update(arrayRefFromStringRef(IS.Content->getBuffer()));186 187      SrcHeaderBlockEntry Entry;188      ::memset(&Entry, 0, sizeof(SrcHeaderBlockEntry));189      Entry.Size = sizeof(SrcHeaderBlockEntry);190      Entry.FileSize = IS.Content->getBufferSize();191      Entry.FileNI = IS.NameIndex;192      Entry.VFileNI = IS.VNameIndex;193      Entry.ObjNI = 1;194      Entry.IsVirtual = 0;195      Entry.Version =196          static_cast<uint32_t>(PdbRaw_SrcHeaderBlockVer::SrcVerOne);197      Entry.CRC = CRC.getCRC();198      StringRef VName = getStringTableBuilder().getStringForId(IS.VNameIndex);199      InjectedSourceTable.set_as(VName, std::move(Entry),200                                 InjectedSourceHashTraits);201    }202 203    uint32_t SrcHeaderBlockSize =204        sizeof(SrcHeaderBlockHeader) +205        InjectedSourceTable.calculateSerializedLength();206    SN = allocateNamedStream("/src/headerblock", SrcHeaderBlockSize);207    if (!SN)208      return SN.takeError();209    for (const auto &IS : InjectedSources) {210      SN = allocateNamedStream(IS.StreamName, IS.Content->getBufferSize());211      if (!SN)212        return SN.takeError();213    }214  }215 216  // Do this last, since it relies on the named stream map being complete, and217  // that can be updated by previous steps in the finalization.218  if (Info) {219    if (auto EC = Info->finalizeMsfLayout())220      return EC;221  }222 223  return Error::success();224}225 226Expected<uint32_t> PDBFileBuilder::getNamedStreamIndex(StringRef Name) const {227  uint32_t SN = 0;228  if (!NamedStreams.get(Name, SN))229    return llvm::make_error<pdb::RawError>(raw_error_code::no_stream);230  return SN;231}232 233void PDBFileBuilder::commitSrcHeaderBlock(WritableBinaryStream &MsfBuffer,234                                          const msf::MSFLayout &Layout) {235  assert(!InjectedSourceTable.empty());236 237  uint32_t SN = cantFail(getNamedStreamIndex("/src/headerblock"));238  auto Stream = WritableMappedBlockStream::createIndexedStream(239      Layout, MsfBuffer, SN, Allocator);240  BinaryStreamWriter Writer(*Stream);241 242  SrcHeaderBlockHeader Header;243  ::memset(&Header, 0, sizeof(Header));244  Header.Version = static_cast<uint32_t>(PdbRaw_SrcHeaderBlockVer::SrcVerOne);245  Header.Size = Writer.bytesRemaining();246 247  cantFail(Writer.writeObject(Header));248  cantFail(InjectedSourceTable.commit(Writer));249 250  assert(Writer.bytesRemaining() == 0);251}252 253void PDBFileBuilder::commitInjectedSources(WritableBinaryStream &MsfBuffer,254                                           const msf::MSFLayout &Layout) {255  if (InjectedSourceTable.empty())256    return;257 258  llvm::TimeTraceScope timeScope("Commit injected sources");259  commitSrcHeaderBlock(MsfBuffer, Layout);260 261  for (const auto &IS : InjectedSources) {262    uint32_t SN = cantFail(getNamedStreamIndex(IS.StreamName));263 264    auto SourceStream = WritableMappedBlockStream::createIndexedStream(265        Layout, MsfBuffer, SN, Allocator);266    BinaryStreamWriter SourceWriter(*SourceStream);267    assert(SourceWriter.bytesRemaining() == IS.Content->getBufferSize());268    cantFail(SourceWriter.writeBytes(269        arrayRefFromStringRef(IS.Content->getBuffer())));270  }271}272 273Error PDBFileBuilder::commit(StringRef Filename, codeview::GUID *Guid) {274  assert(!Filename.empty());275  if (auto EC = finalizeMsfLayout())276    return EC;277 278  MSFLayout Layout;279  Expected<FileBufferByteStream> ExpectedMsfBuffer =280      Msf->commit(Filename, Layout);281  if (!ExpectedMsfBuffer)282    return ExpectedMsfBuffer.takeError();283  FileBufferByteStream Buffer = std::move(*ExpectedMsfBuffer);284 285  auto ExpectedSN = getNamedStreamIndex("/names");286  if (!ExpectedSN)287    return ExpectedSN.takeError();288 289  auto NS = WritableMappedBlockStream::createIndexedStream(290      Layout, Buffer, *ExpectedSN, Allocator);291  BinaryStreamWriter NSWriter(*NS);292  if (auto EC = Strings.commit(NSWriter))293    return EC;294 295  {296    llvm::TimeTraceScope timeScope("Named stream data");297    for (const auto &NSE : NamedStreamData) {298      if (NSE.second.empty())299        continue;300 301      auto NS = WritableMappedBlockStream::createIndexedStream(302          Layout, Buffer, NSE.first, Allocator);303      BinaryStreamWriter NSW(*NS);304      if (auto EC = NSW.writeBytes(arrayRefFromStringRef(NSE.second)))305        return EC;306    }307  }308 309  if (Info) {310    if (auto EC = Info->commit(Layout, Buffer))311      return EC;312  }313 314  if (Dbi) {315    if (auto EC = Dbi->commit(Layout, Buffer))316      return EC;317  }318 319  if (Tpi) {320    if (auto EC = Tpi->commit(Layout, Buffer))321      return EC;322  }323 324  if (Ipi) {325    if (auto EC = Ipi->commit(Layout, Buffer))326      return EC;327  }328 329  if (Gsi) {330    if (auto EC = Gsi->commit(Layout, Buffer))331      return EC;332  }333 334  auto InfoStreamBlocks = Layout.StreamMap[StreamPDB];335  assert(!InfoStreamBlocks.empty());336  uint64_t InfoStreamFileOffset =337      blockToOffset(InfoStreamBlocks.front(), Layout.SB->BlockSize);338  InfoStreamHeader *H = reinterpret_cast<InfoStreamHeader *>(339      Buffer.getBufferStart() + InfoStreamFileOffset);340 341  commitInjectedSources(Buffer, Layout);342 343  // Set the build id at the very end, after every other byte of the PDB344  // has been written.345  if (Info->hashPDBContentsToGUID()) {346    llvm::TimeTraceScope timeScope("Compute build ID");347 348    // Compute a hash of all sections of the output file.349    uint64_t Digest =350        xxh3_64bits({Buffer.getBufferStart(), Buffer.getBufferEnd()});351 352    H->Age = 1;353 354    memcpy(H->Guid.Guid, &Digest, 8);355    // xxhash only gives us 8 bytes, so put some fixed data in the other half.356    memcpy(H->Guid.Guid + 8, "LLD PDB.", 8);357 358    // Put the hash in the Signature field too.359    H->Signature = static_cast<uint32_t>(Digest);360 361    // Return GUID to caller.362    memcpy(Guid, H->Guid.Guid, 16);363  } else {364    H->Age = Info->getAge();365    H->Guid = Info->getGuid();366    std::optional<uint32_t> Sig = Info->getSignature();367    H->Signature = Sig ? *Sig : time(nullptr);368  }369 370  return Buffer.commit();371}372