Path: blob/main/contrib/llvm-project/llvm/lib/DebugInfo/PDB/Native/PDBFileBuilder.cpp
35293 views
//===- PDBFileBuilder.cpp - PDB File Creation -------------------*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "llvm/DebugInfo/PDB/Native/PDBFileBuilder.h"9#include "llvm/ADT/SmallString.h"10#include "llvm/ADT/StringExtras.h"11#include "llvm/DebugInfo/CodeView/CodeView.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"2930#include <ctime>3132using namespace llvm;33using namespace llvm::codeview;34using namespace llvm::msf;35using namespace llvm::pdb;36using namespace llvm::support;3738namespace llvm {39class WritableBinaryStream;40}4142PDBFileBuilder::PDBFileBuilder(BumpPtrAllocator &Allocator)43: Allocator(Allocator), InjectedSourceHashTraits(Strings),44InjectedSourceTable(2) {}4546PDBFileBuilder::~PDBFileBuilder() = default;4748Error PDBFileBuilder::initialize(uint32_t BlockSize) {49auto ExpectedMsf = MSFBuilder::create(Allocator, BlockSize);50if (!ExpectedMsf)51return ExpectedMsf.takeError();52Msf = std::make_unique<MSFBuilder>(std::move(*ExpectedMsf));53return Error::success();54}5556MSFBuilder &PDBFileBuilder::getMsfBuilder() { return *Msf; }5758InfoStreamBuilder &PDBFileBuilder::getInfoBuilder() {59if (!Info)60Info = std::make_unique<InfoStreamBuilder>(*Msf, NamedStreams);61return *Info;62}6364DbiStreamBuilder &PDBFileBuilder::getDbiBuilder() {65if (!Dbi)66Dbi = std::make_unique<DbiStreamBuilder>(*Msf);67return *Dbi;68}6970TpiStreamBuilder &PDBFileBuilder::getTpiBuilder() {71if (!Tpi)72Tpi = std::make_unique<TpiStreamBuilder>(*Msf, StreamTPI);73return *Tpi;74}7576TpiStreamBuilder &PDBFileBuilder::getIpiBuilder() {77if (!Ipi)78Ipi = std::make_unique<TpiStreamBuilder>(*Msf, StreamIPI);79return *Ipi;80}8182PDBStringTableBuilder &PDBFileBuilder::getStringTableBuilder() {83return Strings;84}8586GSIStreamBuilder &PDBFileBuilder::getGsiBuilder() {87if (!Gsi)88Gsi = std::make_unique<GSIStreamBuilder>(*Msf);89return *Gsi;90}9192Expected<uint32_t> PDBFileBuilder::allocateNamedStream(StringRef Name,93uint32_t Size) {94auto ExpectedStream = Msf->addStream(Size);95if (ExpectedStream)96NamedStreams.set(Name, *ExpectedStream);97return ExpectedStream;98}99100Error PDBFileBuilder::addNamedStream(StringRef Name, StringRef Data) {101Expected<uint32_t> ExpectedIndex = allocateNamedStream(Name, Data.size());102if (!ExpectedIndex)103return ExpectedIndex.takeError();104assert(NamedStreamData.count(*ExpectedIndex) == 0);105NamedStreamData[*ExpectedIndex] = std::string(Data);106return Error::success();107}108109void PDBFileBuilder::addInjectedSource(StringRef Name,110std::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.114SmallString<64> VName;115sys::path::native(Name.lower(), VName, sys::path::Style::windows_backslash);116117uint32_t NI = getStringTableBuilder().insert(Name);118uint32_t VNI = getStringTableBuilder().insert(VName);119120InjectedSourceDescriptor Desc;121Desc.Content = std::move(Buffer);122Desc.NameIndex = NI;123Desc.VNameIndex = VNI;124Desc.StreamName = "/src/files/";125126Desc.StreamName += VName;127128InjectedSources.push_back(std::move(Desc));129}130131Error PDBFileBuilder::finalizeMsfLayout() {132llvm::TimeTraceScope timeScope("MSF layout");133134if (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.139auto &Info = getInfoBuilder();140Info.addFeature(PdbRaw_FeatureSig::VC140);141}142143uint32_t StringsLen = Strings.calculateSerializedSize();144145Expected<uint32_t> SN = allocateNamedStream("/LinkInfo", 0);146if (!SN)147return SN.takeError();148149if (Gsi) {150if (auto EC = Gsi->finalizeMsfLayout())151return EC;152if (Dbi) {153Dbi->setPublicsStreamIndex(Gsi->getPublicsStreamIndex());154Dbi->setGlobalsStreamIndex(Gsi->getGlobalsStreamIndex());155Dbi->setSymbolRecordStreamIndex(Gsi->getRecordStreamIndex());156}157}158if (Tpi) {159if (auto EC = Tpi->finalizeMsfLayout())160return EC;161}162if (Dbi) {163if (auto EC = Dbi->finalizeMsfLayout())164return EC;165}166SN = allocateNamedStream("/names", StringsLen);167if (!SN)168return SN.takeError();169170if (Ipi) {171if (auto EC = Ipi->finalizeMsfLayout())172return EC;173}174175// Do this last, since it relies on the named stream map being complete, and176// that can be updated by previous steps in the finalization.177if (Info) {178if (auto EC = Info->finalizeMsfLayout())179return EC;180}181182if (!InjectedSources.empty()) {183for (const auto &IS : InjectedSources) {184JamCRC CRC(0);185CRC.update(arrayRefFromStringRef(IS.Content->getBuffer()));186187SrcHeaderBlockEntry Entry;188::memset(&Entry, 0, sizeof(SrcHeaderBlockEntry));189Entry.Size = sizeof(SrcHeaderBlockEntry);190Entry.FileSize = IS.Content->getBufferSize();191Entry.FileNI = IS.NameIndex;192Entry.VFileNI = IS.VNameIndex;193Entry.ObjNI = 1;194Entry.IsVirtual = 0;195Entry.Version =196static_cast<uint32_t>(PdbRaw_SrcHeaderBlockVer::SrcVerOne);197Entry.CRC = CRC.getCRC();198StringRef VName = getStringTableBuilder().getStringForId(IS.VNameIndex);199InjectedSourceTable.set_as(VName, std::move(Entry),200InjectedSourceHashTraits);201}202203uint32_t SrcHeaderBlockSize =204sizeof(SrcHeaderBlockHeader) +205InjectedSourceTable.calculateSerializedLength();206SN = allocateNamedStream("/src/headerblock", SrcHeaderBlockSize);207if (!SN)208return SN.takeError();209for (const auto &IS : InjectedSources) {210SN = allocateNamedStream(IS.StreamName, IS.Content->getBufferSize());211if (!SN)212return SN.takeError();213}214}215216// Do this last, since it relies on the named stream map being complete, and217// that can be updated by previous steps in the finalization.218if (Info) {219if (auto EC = Info->finalizeMsfLayout())220return EC;221}222223return Error::success();224}225226Expected<uint32_t> PDBFileBuilder::getNamedStreamIndex(StringRef Name) const {227uint32_t SN = 0;228if (!NamedStreams.get(Name, SN))229return llvm::make_error<pdb::RawError>(raw_error_code::no_stream);230return SN;231}232233void PDBFileBuilder::commitSrcHeaderBlock(WritableBinaryStream &MsfBuffer,234const msf::MSFLayout &Layout) {235assert(!InjectedSourceTable.empty());236237uint32_t SN = cantFail(getNamedStreamIndex("/src/headerblock"));238auto Stream = WritableMappedBlockStream::createIndexedStream(239Layout, MsfBuffer, SN, Allocator);240BinaryStreamWriter Writer(*Stream);241242SrcHeaderBlockHeader Header;243::memset(&Header, 0, sizeof(Header));244Header.Version = static_cast<uint32_t>(PdbRaw_SrcHeaderBlockVer::SrcVerOne);245Header.Size = Writer.bytesRemaining();246247cantFail(Writer.writeObject(Header));248cantFail(InjectedSourceTable.commit(Writer));249250assert(Writer.bytesRemaining() == 0);251}252253void PDBFileBuilder::commitInjectedSources(WritableBinaryStream &MsfBuffer,254const msf::MSFLayout &Layout) {255if (InjectedSourceTable.empty())256return;257258llvm::TimeTraceScope timeScope("Commit injected sources");259commitSrcHeaderBlock(MsfBuffer, Layout);260261for (const auto &IS : InjectedSources) {262uint32_t SN = cantFail(getNamedStreamIndex(IS.StreamName));263264auto SourceStream = WritableMappedBlockStream::createIndexedStream(265Layout, MsfBuffer, SN, Allocator);266BinaryStreamWriter SourceWriter(*SourceStream);267assert(SourceWriter.bytesRemaining() == IS.Content->getBufferSize());268cantFail(SourceWriter.writeBytes(269arrayRefFromStringRef(IS.Content->getBuffer())));270}271}272273Error PDBFileBuilder::commit(StringRef Filename, codeview::GUID *Guid) {274assert(!Filename.empty());275if (auto EC = finalizeMsfLayout())276return EC;277278MSFLayout Layout;279Expected<FileBufferByteStream> ExpectedMsfBuffer =280Msf->commit(Filename, Layout);281if (!ExpectedMsfBuffer)282return ExpectedMsfBuffer.takeError();283FileBufferByteStream Buffer = std::move(*ExpectedMsfBuffer);284285auto ExpectedSN = getNamedStreamIndex("/names");286if (!ExpectedSN)287return ExpectedSN.takeError();288289auto NS = WritableMappedBlockStream::createIndexedStream(290Layout, Buffer, *ExpectedSN, Allocator);291BinaryStreamWriter NSWriter(*NS);292if (auto EC = Strings.commit(NSWriter))293return EC;294295{296llvm::TimeTraceScope timeScope("Named stream data");297for (const auto &NSE : NamedStreamData) {298if (NSE.second.empty())299continue;300301auto NS = WritableMappedBlockStream::createIndexedStream(302Layout, Buffer, NSE.first, Allocator);303BinaryStreamWriter NSW(*NS);304if (auto EC = NSW.writeBytes(arrayRefFromStringRef(NSE.second)))305return EC;306}307}308309if (Info) {310if (auto EC = Info->commit(Layout, Buffer))311return EC;312}313314if (Dbi) {315if (auto EC = Dbi->commit(Layout, Buffer))316return EC;317}318319if (Tpi) {320if (auto EC = Tpi->commit(Layout, Buffer))321return EC;322}323324if (Ipi) {325if (auto EC = Ipi->commit(Layout, Buffer))326return EC;327}328329if (Gsi) {330if (auto EC = Gsi->commit(Layout, Buffer))331return EC;332}333334auto InfoStreamBlocks = Layout.StreamMap[StreamPDB];335assert(!InfoStreamBlocks.empty());336uint64_t InfoStreamFileOffset =337blockToOffset(InfoStreamBlocks.front(), Layout.SB->BlockSize);338InfoStreamHeader *H = reinterpret_cast<InfoStreamHeader *>(339Buffer.getBufferStart() + InfoStreamFileOffset);340341commitInjectedSources(Buffer, Layout);342343// Set the build id at the very end, after every other byte of the PDB344// has been written.345if (Info->hashPDBContentsToGUID()) {346llvm::TimeTraceScope timeScope("Compute build ID");347348// Compute a hash of all sections of the output file.349uint64_t Digest =350xxh3_64bits({Buffer.getBufferStart(), Buffer.getBufferEnd()});351352H->Age = 1;353354memcpy(H->Guid.Guid, &Digest, 8);355// xxhash only gives us 8 bytes, so put some fixed data in the other half.356memcpy(H->Guid.Guid + 8, "LLD PDB.", 8);357358// Put the hash in the Signature field too.359H->Signature = static_cast<uint32_t>(Digest);360361// Return GUID to caller.362memcpy(Guid, H->Guid.Guid, 16);363} else {364H->Age = Info->getAge();365H->Guid = Info->getGuid();366std::optional<uint32_t> Sig = Info->getSignature();367H->Signature = Sig ? *Sig : time(nullptr);368}369370return Buffer.commit();371}372373374