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 #include "llvm/Support/xxhash.h"
29
30 #include <ctime>
31
32 using namespace llvm;
33 using namespace llvm::codeview;
34 using namespace llvm::msf;
35 using namespace llvm::pdb;
36 using namespace llvm::support;
37
38 namespace llvm {
39 class WritableBinaryStream;
40 }
41
PDBFileBuilder(BumpPtrAllocator & Allocator)42 PDBFileBuilder::PDBFileBuilder(BumpPtrAllocator &Allocator)
43 : Allocator(Allocator), InjectedSourceHashTraits(Strings),
44 InjectedSourceTable(2) {}
45
46 PDBFileBuilder::~PDBFileBuilder() = default;
47
initialize(uint32_t BlockSize)48 Error 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
getMsfBuilder()56 MSFBuilder &PDBFileBuilder::getMsfBuilder() { return *Msf; }
57
getInfoBuilder()58 InfoStreamBuilder &PDBFileBuilder::getInfoBuilder() {
59 if (!Info)
60 Info = std::make_unique<InfoStreamBuilder>(*Msf, NamedStreams);
61 return *Info;
62 }
63
getDbiBuilder()64 DbiStreamBuilder &PDBFileBuilder::getDbiBuilder() {
65 if (!Dbi)
66 Dbi = std::make_unique<DbiStreamBuilder>(*Msf);
67 return *Dbi;
68 }
69
getTpiBuilder()70 TpiStreamBuilder &PDBFileBuilder::getTpiBuilder() {
71 if (!Tpi)
72 Tpi = std::make_unique<TpiStreamBuilder>(*Msf, StreamTPI);
73 return *Tpi;
74 }
75
getIpiBuilder()76 TpiStreamBuilder &PDBFileBuilder::getIpiBuilder() {
77 if (!Ipi)
78 Ipi = std::make_unique<TpiStreamBuilder>(*Msf, StreamIPI);
79 return *Ipi;
80 }
81
getStringTableBuilder()82 PDBStringTableBuilder &PDBFileBuilder::getStringTableBuilder() {
83 return Strings;
84 }
85
getGsiBuilder()86 GSIStreamBuilder &PDBFileBuilder::getGsiBuilder() {
87 if (!Gsi)
88 Gsi = std::make_unique<GSIStreamBuilder>(*Msf);
89 return *Gsi;
90 }
91
allocateNamedStream(StringRef Name,uint32_t Size)92 Expected<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
addNamedStream(StringRef Name,StringRef Data)100 Error 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
addInjectedSource(StringRef Name,std::unique_ptr<MemoryBuffer> Buffer)109 void PDBFileBuilder::addInjectedSource(StringRef Name,
110 std::unique_ptr<MemoryBuffer> Buffer) {
111 // Stream names must be exact matches, since they get looked up in a hash
112 // 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
finalizeMsfLayout()131 Error 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're
136 // only going to *really* have an ID stream if there is at least one ID
137 // record, we leave open the opportunity to test older PDBs such as those
138 // 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, and
176 // 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, and
217 // 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
getNamedStreamIndex(StringRef Name) const226 Expected<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
commitSrcHeaderBlock(WritableBinaryStream & MsfBuffer,const msf::MSFLayout & Layout)233 void 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
commitInjectedSources(WritableBinaryStream & MsfBuffer,const msf::MSFLayout & Layout)253 void 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
commit(StringRef Filename,codeview::GUID * Guid)273 Error 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 PDB
344 // 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