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