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