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