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