1317017Sdim//===- PDBFileBuilder.cpp - PDB File Creation -------------------*- C++ -*-===//
2317017Sdim//
3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4353358Sdim// See https://llvm.org/LICENSE.txt for license information.
5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6317017Sdim//
7317017Sdim//===----------------------------------------------------------------------===//
8317017Sdim
9317017Sdim#include "llvm/DebugInfo/PDB/Native/PDBFileBuilder.h"
10317017Sdim
11317017Sdim#include "llvm/ADT/BitVector.h"
12317017Sdim
13317017Sdim#include "llvm/DebugInfo/MSF/MSFBuilder.h"
14317017Sdim#include "llvm/DebugInfo/PDB/Native/DbiStream.h"
15317017Sdim#include "llvm/DebugInfo/PDB/Native/DbiStreamBuilder.h"
16327952Sdim#include "llvm/DebugInfo/PDB/Native/GSIStreamBuilder.h"
17317017Sdim#include "llvm/DebugInfo/PDB/Native/InfoStream.h"
18317017Sdim#include "llvm/DebugInfo/PDB/Native/InfoStreamBuilder.h"
19317778Sdim#include "llvm/DebugInfo/PDB/Native/PDBStringTableBuilder.h"
20317017Sdim#include "llvm/DebugInfo/PDB/Native/RawError.h"
21317017Sdim#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
22317017Sdim#include "llvm/DebugInfo/PDB/Native/TpiStreamBuilder.h"
23317017Sdim#include "llvm/Support/BinaryStream.h"
24317017Sdim#include "llvm/Support/BinaryStreamWriter.h"
25360784Sdim#include "llvm/Support/CRC.h"
26341825Sdim#include "llvm/Support/Path.h"
27344779Sdim#include "llvm/Support/xxhash.h"
28317017Sdim
29317017Sdimusing namespace llvm;
30317017Sdimusing namespace llvm::codeview;
31317017Sdimusing namespace llvm::msf;
32317017Sdimusing namespace llvm::pdb;
33317017Sdimusing namespace llvm::support;
34317017Sdim
35317017SdimPDBFileBuilder::PDBFileBuilder(BumpPtrAllocator &Allocator)
36341825Sdim    : Allocator(Allocator), InjectedSourceHashTraits(Strings),
37353358Sdim      InjectedSourceTable(2) {}
38317017Sdim
39320970SdimPDBFileBuilder::~PDBFileBuilder() {}
40320970Sdim
41317017SdimError PDBFileBuilder::initialize(uint32_t BlockSize) {
42317017Sdim  auto ExpectedMsf = MSFBuilder::create(Allocator, BlockSize);
43317017Sdim  if (!ExpectedMsf)
44317017Sdim    return ExpectedMsf.takeError();
45360784Sdim  Msf = std::make_unique<MSFBuilder>(std::move(*ExpectedMsf));
46317017Sdim  return Error::success();
47317017Sdim}
48317017Sdim
49317017SdimMSFBuilder &PDBFileBuilder::getMsfBuilder() { return *Msf; }
50317017Sdim
51317017SdimInfoStreamBuilder &PDBFileBuilder::getInfoBuilder() {
52317017Sdim  if (!Info)
53360784Sdim    Info = std::make_unique<InfoStreamBuilder>(*Msf, NamedStreams);
54317017Sdim  return *Info;
55317017Sdim}
56317017Sdim
57317017SdimDbiStreamBuilder &PDBFileBuilder::getDbiBuilder() {
58317017Sdim  if (!Dbi)
59360784Sdim    Dbi = std::make_unique<DbiStreamBuilder>(*Msf);
60317017Sdim  return *Dbi;
61317017Sdim}
62317017Sdim
63317017SdimTpiStreamBuilder &PDBFileBuilder::getTpiBuilder() {
64317017Sdim  if (!Tpi)
65360784Sdim    Tpi = std::make_unique<TpiStreamBuilder>(*Msf, StreamTPI);
66317017Sdim  return *Tpi;
67317017Sdim}
68317017Sdim
69317017SdimTpiStreamBuilder &PDBFileBuilder::getIpiBuilder() {
70317017Sdim  if (!Ipi)
71360784Sdim    Ipi = std::make_unique<TpiStreamBuilder>(*Msf, StreamIPI);
72317017Sdim  return *Ipi;
73317017Sdim}
74317017Sdim
75317778SdimPDBStringTableBuilder &PDBFileBuilder::getStringTableBuilder() {
76317778Sdim  return Strings;
77317778Sdim}
78317017Sdim
79327952SdimGSIStreamBuilder &PDBFileBuilder::getGsiBuilder() {
80327952Sdim  if (!Gsi)
81360784Sdim    Gsi = std::make_unique<GSIStreamBuilder>(*Msf);
82327952Sdim  return *Gsi;
83320970Sdim}
84320970Sdim
85341825SdimExpected<uint32_t> PDBFileBuilder::allocateNamedStream(StringRef Name,
86341825Sdim                                                       uint32_t Size) {
87317017Sdim  auto ExpectedStream = Msf->addStream(Size);
88341825Sdim  if (ExpectedStream)
89341825Sdim    NamedStreams.set(Name, *ExpectedStream);
90341825Sdim  return ExpectedStream;
91341825Sdim}
92341825Sdim
93341825SdimError PDBFileBuilder::addNamedStream(StringRef Name, StringRef Data) {
94341825Sdim  Expected<uint32_t> ExpectedIndex = allocateNamedStream(Name, Data.size());
95341825Sdim  if (!ExpectedIndex)
96341825Sdim    return ExpectedIndex.takeError();
97341825Sdim  assert(NamedStreamData.count(*ExpectedIndex) == 0);
98341825Sdim  NamedStreamData[*ExpectedIndex] = Data;
99317017Sdim  return Error::success();
100317017Sdim}
101317017Sdim
102341825Sdimvoid PDBFileBuilder::addInjectedSource(StringRef Name,
103341825Sdim                                       std::unique_ptr<MemoryBuffer> Buffer) {
104341825Sdim  // Stream names must be exact matches, since they get looked up in a hash
105341825Sdim  // table and the hash value is dependent on the exact contents of the string.
106341825Sdim  // link.exe lowercases a path and converts / to \, so we must do the same.
107341825Sdim  SmallString<64> VName;
108341825Sdim  sys::path::native(Name.lower(), VName);
109320041Sdim
110341825Sdim  uint32_t NI = getStringTableBuilder().insert(Name);
111341825Sdim  uint32_t VNI = getStringTableBuilder().insert(VName);
112341825Sdim
113341825Sdim  InjectedSourceDescriptor Desc;
114341825Sdim  Desc.Content = std::move(Buffer);
115341825Sdim  Desc.NameIndex = NI;
116341825Sdim  Desc.VNameIndex = VNI;
117341825Sdim  Desc.StreamName = "/src/files/";
118341825Sdim
119341825Sdim  Desc.StreamName += VName;
120341825Sdim
121341825Sdim  InjectedSources.push_back(std::move(Desc));
122341825Sdim}
123341825Sdim
124341825SdimError PDBFileBuilder::finalizeMsfLayout() {
125341825Sdim
126320041Sdim  if (Ipi && Ipi->getRecordCount() > 0) {
127320041Sdim    // In theory newer PDBs always have an ID stream, but by saying that we're
128320041Sdim    // only going to *really* have an ID stream if there is at least one ID
129320041Sdim    // record, we leave open the opportunity to test older PDBs such as those
130320041Sdim    // that don't have an ID stream.
131320041Sdim    auto &Info = getInfoBuilder();
132320041Sdim    Info.addFeature(PdbRaw_FeatureSig::VC140);
133320041Sdim  }
134320041Sdim
135317778Sdim  uint32_t StringsLen = Strings.calculateSerializedSize();
136317017Sdim
137341825Sdim  Expected<uint32_t> SN = allocateNamedStream("/LinkInfo", 0);
138341825Sdim  if (!SN)
139341825Sdim    return SN.takeError();
140317017Sdim
141341825Sdim  if (Gsi) {
142341825Sdim    if (auto EC = Gsi->finalizeMsfLayout())
143341825Sdim      return EC;
144341825Sdim    if (Dbi) {
145341825Sdim      Dbi->setPublicsStreamIndex(Gsi->getPublicsStreamIndex());
146341825Sdim      Dbi->setGlobalsStreamIndex(Gsi->getGlobalsStreamIndex());
147341825Sdim      Dbi->setSymbolRecordStreamIndex(Gsi->getRecordStreamIdx());
148341825Sdim    }
149317017Sdim  }
150341825Sdim  if (Tpi) {
151341825Sdim    if (auto EC = Tpi->finalizeMsfLayout())
152341825Sdim      return EC;
153341825Sdim  }
154317017Sdim  if (Dbi) {
155317017Sdim    if (auto EC = Dbi->finalizeMsfLayout())
156341825Sdim      return EC;
157317017Sdim  }
158341825Sdim  SN = allocateNamedStream("/names", StringsLen);
159341825Sdim  if (!SN)
160341825Sdim    return SN.takeError();
161341825Sdim
162317017Sdim  if (Ipi) {
163317017Sdim    if (auto EC = Ipi->finalizeMsfLayout())
164341825Sdim      return EC;
165317017Sdim  }
166341825Sdim
167341825Sdim  // Do this last, since it relies on the named stream map being complete, and
168341825Sdim  // that can be updated by previous steps in the finalization.
169341825Sdim  if (Info) {
170341825Sdim    if (auto EC = Info->finalizeMsfLayout())
171341825Sdim      return EC;
172341825Sdim  }
173341825Sdim
174341825Sdim  if (!InjectedSources.empty()) {
175341825Sdim    for (const auto &IS : InjectedSources) {
176341825Sdim      JamCRC CRC(0);
177360784Sdim      CRC.update(arrayRefFromStringRef(IS.Content->getBuffer()));
178341825Sdim
179341825Sdim      SrcHeaderBlockEntry Entry;
180341825Sdim      ::memset(&Entry, 0, sizeof(SrcHeaderBlockEntry));
181341825Sdim      Entry.Size = sizeof(SrcHeaderBlockEntry);
182341825Sdim      Entry.FileSize = IS.Content->getBufferSize();
183341825Sdim      Entry.FileNI = IS.NameIndex;
184341825Sdim      Entry.VFileNI = IS.VNameIndex;
185341825Sdim      Entry.ObjNI = 1;
186341825Sdim      Entry.IsVirtual = 0;
187341825Sdim      Entry.Version =
188341825Sdim          static_cast<uint32_t>(PdbRaw_SrcHeaderBlockVer::SrcVerOne);
189341825Sdim      Entry.CRC = CRC.getCRC();
190341825Sdim      StringRef VName = getStringTableBuilder().getStringForId(IS.VNameIndex);
191353358Sdim      InjectedSourceTable.set_as(VName, std::move(Entry),
192353358Sdim                                 InjectedSourceHashTraits);
193320970Sdim    }
194341825Sdim
195341825Sdim    uint32_t SrcHeaderBlockSize =
196341825Sdim        sizeof(SrcHeaderBlockHeader) +
197341825Sdim        InjectedSourceTable.calculateSerializedLength();
198341825Sdim    SN = allocateNamedStream("/src/headerblock", SrcHeaderBlockSize);
199341825Sdim    if (!SN)
200341825Sdim      return SN.takeError();
201341825Sdim    for (const auto &IS : InjectedSources) {
202341825Sdim      SN = allocateNamedStream(IS.StreamName, IS.Content->getBufferSize());
203341825Sdim      if (!SN)
204341825Sdim        return SN.takeError();
205341825Sdim    }
206320970Sdim  }
207317017Sdim
208341825Sdim  // Do this last, since it relies on the named stream map being complete, and
209341825Sdim  // that can be updated by previous steps in the finalization.
210341825Sdim  if (Info) {
211341825Sdim    if (auto EC = Info->finalizeMsfLayout())
212341825Sdim      return EC;
213341825Sdim  }
214341825Sdim
215341825Sdim  return Error::success();
216317017Sdim}
217317017Sdim
218317778SdimExpected<uint32_t> PDBFileBuilder::getNamedStreamIndex(StringRef Name) const {
219317778Sdim  uint32_t SN = 0;
220317778Sdim  if (!NamedStreams.get(Name, SN))
221317778Sdim    return llvm::make_error<pdb::RawError>(raw_error_code::no_stream);
222317778Sdim  return SN;
223317778Sdim}
224317778Sdim
225341825Sdimvoid PDBFileBuilder::commitSrcHeaderBlock(WritableBinaryStream &MsfBuffer,
226341825Sdim                                          const msf::MSFLayout &Layout) {
227341825Sdim  assert(!InjectedSourceTable.empty());
228327952Sdim
229341825Sdim  uint32_t SN = cantFail(getNamedStreamIndex("/src/headerblock"));
230341825Sdim  auto Stream = WritableMappedBlockStream::createIndexedStream(
231341825Sdim      Layout, MsfBuffer, SN, Allocator);
232341825Sdim  BinaryStreamWriter Writer(*Stream);
233327952Sdim
234341825Sdim  SrcHeaderBlockHeader Header;
235341825Sdim  ::memset(&Header, 0, sizeof(Header));
236341825Sdim  Header.Version = static_cast<uint32_t>(PdbRaw_SrcHeaderBlockVer::SrcVerOne);
237341825Sdim  Header.Size = Writer.bytesRemaining();
238327952Sdim
239341825Sdim  cantFail(Writer.writeObject(Header));
240341825Sdim  cantFail(InjectedSourceTable.commit(Writer));
241317017Sdim
242341825Sdim  assert(Writer.bytesRemaining() == 0);
243341825Sdim}
244317017Sdim
245341825Sdimvoid PDBFileBuilder::commitInjectedSources(WritableBinaryStream &MsfBuffer,
246341825Sdim                                           const msf::MSFLayout &Layout) {
247341825Sdim  if (InjectedSourceTable.empty())
248341825Sdim    return;
249327952Sdim
250341825Sdim  commitSrcHeaderBlock(MsfBuffer, Layout);
251327952Sdim
252341825Sdim  for (const auto &IS : InjectedSources) {
253341825Sdim    uint32_t SN = cantFail(getNamedStreamIndex(IS.StreamName));
254317017Sdim
255341825Sdim    auto SourceStream = WritableMappedBlockStream::createIndexedStream(
256341825Sdim        Layout, MsfBuffer, SN, Allocator);
257341825Sdim    BinaryStreamWriter SourceWriter(*SourceStream);
258341825Sdim    assert(SourceWriter.bytesRemaining() == IS.Content->getBufferSize());
259341825Sdim    cantFail(SourceWriter.writeBytes(
260341825Sdim        arrayRefFromStringRef(IS.Content->getBuffer())));
261341825Sdim  }
262341825Sdim}
263317017Sdim
264344779SdimError PDBFileBuilder::commit(StringRef Filename, codeview::GUID *Guid) {
265341825Sdim  assert(!Filename.empty());
266341825Sdim  if (auto EC = finalizeMsfLayout())
267317017Sdim    return EC;
268317017Sdim
269341825Sdim  MSFLayout Layout;
270344779Sdim  Expected<FileBufferByteStream> ExpectedMsfBuffer =
271344779Sdim      Msf->commit(Filename, Layout);
272341825Sdim  if (!ExpectedMsfBuffer)
273341825Sdim    return ExpectedMsfBuffer.takeError();
274341825Sdim  FileBufferByteStream Buffer = std::move(*ExpectedMsfBuffer);
275317017Sdim
276317778Sdim  auto ExpectedSN = getNamedStreamIndex("/names");
277317778Sdim  if (!ExpectedSN)
278317778Sdim    return ExpectedSN.takeError();
279317017Sdim
280319547Sdim  auto NS = WritableMappedBlockStream::createIndexedStream(
281319547Sdim      Layout, Buffer, *ExpectedSN, Allocator);
282317017Sdim  BinaryStreamWriter NSWriter(*NS);
283317017Sdim  if (auto EC = Strings.commit(NSWriter))
284317017Sdim    return EC;
285317017Sdim
286341825Sdim  for (const auto &NSE : NamedStreamData) {
287341825Sdim    if (NSE.second.empty())
288341825Sdim      continue;
289341825Sdim
290341825Sdim    auto NS = WritableMappedBlockStream::createIndexedStream(
291341825Sdim        Layout, Buffer, NSE.first, Allocator);
292341825Sdim    BinaryStreamWriter NSW(*NS);
293341825Sdim    if (auto EC = NSW.writeBytes(arrayRefFromStringRef(NSE.second)))
294341825Sdim      return EC;
295341825Sdim  }
296341825Sdim
297317017Sdim  if (Info) {
298317017Sdim    if (auto EC = Info->commit(Layout, Buffer))
299317017Sdim      return EC;
300317017Sdim  }
301317017Sdim
302317017Sdim  if (Dbi) {
303317017Sdim    if (auto EC = Dbi->commit(Layout, Buffer))
304317017Sdim      return EC;
305317017Sdim  }
306317017Sdim
307317017Sdim  if (Tpi) {
308317017Sdim    if (auto EC = Tpi->commit(Layout, Buffer))
309317017Sdim      return EC;
310317017Sdim  }
311317017Sdim
312317017Sdim  if (Ipi) {
313317017Sdim    if (auto EC = Ipi->commit(Layout, Buffer))
314317017Sdim      return EC;
315317017Sdim  }
316317017Sdim
317327952Sdim  if (Gsi) {
318327952Sdim    if (auto EC = Gsi->commit(Layout, Buffer))
319320970Sdim      return EC;
320320970Sdim  }
321320970Sdim
322341825Sdim  auto InfoStreamBlocks = Layout.StreamMap[StreamPDB];
323341825Sdim  assert(!InfoStreamBlocks.empty());
324341825Sdim  uint64_t InfoStreamFileOffset =
325341825Sdim      blockToOffset(InfoStreamBlocks.front(), Layout.SB->BlockSize);
326341825Sdim  InfoStreamHeader *H = reinterpret_cast<InfoStreamHeader *>(
327341825Sdim      Buffer.getBufferStart() + InfoStreamFileOffset);
328341825Sdim
329341825Sdim  commitInjectedSources(Buffer, Layout);
330341825Sdim
331341825Sdim  // Set the build id at the very end, after every other byte of the PDB
332341825Sdim  // has been written.
333344779Sdim  if (Info->hashPDBContentsToGUID()) {
334344779Sdim    // Compute a hash of all sections of the output file.
335344779Sdim    uint64_t Digest =
336344779Sdim        xxHash64({Buffer.getBufferStart(), Buffer.getBufferEnd()});
337341825Sdim
338344779Sdim    H->Age = 1;
339344779Sdim
340344779Sdim    memcpy(H->Guid.Guid, &Digest, 8);
341344779Sdim    // xxhash only gives us 8 bytes, so put some fixed data in the other half.
342344779Sdim    memcpy(H->Guid.Guid + 8, "LLD PDB.", 8);
343344779Sdim
344344779Sdim    // Put the hash in the Signature field too.
345344779Sdim    H->Signature = static_cast<uint32_t>(Digest);
346344779Sdim
347344779Sdim    // Return GUID to caller.
348344779Sdim    memcpy(Guid, H->Guid.Guid, 16);
349344779Sdim  } else {
350344779Sdim    H->Age = Info->getAge();
351344779Sdim    H->Guid = Info->getGuid();
352344779Sdim    Optional<uint32_t> Sig = Info->getSignature();
353344779Sdim    H->Signature = Sig.hasValue() ? *Sig : time(nullptr);
354344779Sdim  }
355344779Sdim
356317017Sdim  return Buffer.commit();
357317017Sdim}
358