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