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