xref: /llvm-project/llvm/lib/DebugInfo/PDB/Native/PDBFileBuilder.cpp (revision a6fb536e5b5c98d3ca46fbd7ea21893f633d21d8)
1 //===- PDBFileBuilder.cpp - PDB File Creation -------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "llvm/DebugInfo/PDB/Native/PDBFileBuilder.h"
11 
12 #include "llvm/ADT/BitVector.h"
13 
14 #include "llvm/DebugInfo/MSF/MSFBuilder.h"
15 #include "llvm/DebugInfo/PDB/GenericError.h"
16 #include "llvm/DebugInfo/PDB/Native/DbiStream.h"
17 #include "llvm/DebugInfo/PDB/Native/DbiStreamBuilder.h"
18 #include "llvm/DebugInfo/PDB/Native/GSIStreamBuilder.h"
19 #include "llvm/DebugInfo/PDB/Native/InfoStream.h"
20 #include "llvm/DebugInfo/PDB/Native/InfoStreamBuilder.h"
21 #include "llvm/DebugInfo/PDB/Native/PDBStringTableBuilder.h"
22 #include "llvm/DebugInfo/PDB/Native/RawError.h"
23 #include "llvm/DebugInfo/PDB/Native/TpiStream.h"
24 #include "llvm/DebugInfo/PDB/Native/TpiStreamBuilder.h"
25 #include "llvm/Support/BinaryStream.h"
26 #include "llvm/Support/BinaryStreamWriter.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) {}
36 
37 PDBFileBuilder::~PDBFileBuilder() {}
38 
39 Error PDBFileBuilder::initialize(uint32_t BlockSize) {
40   auto ExpectedMsf = MSFBuilder::create(Allocator, BlockSize);
41   if (!ExpectedMsf)
42     return ExpectedMsf.takeError();
43   Msf = llvm::make_unique<MSFBuilder>(std::move(*ExpectedMsf));
44   return Error::success();
45 }
46 
47 MSFBuilder &PDBFileBuilder::getMsfBuilder() { return *Msf; }
48 
49 InfoStreamBuilder &PDBFileBuilder::getInfoBuilder() {
50   if (!Info)
51     Info = llvm::make_unique<InfoStreamBuilder>(*Msf, NamedStreams);
52   return *Info;
53 }
54 
55 DbiStreamBuilder &PDBFileBuilder::getDbiBuilder() {
56   if (!Dbi)
57     Dbi = llvm::make_unique<DbiStreamBuilder>(*Msf);
58   return *Dbi;
59 }
60 
61 TpiStreamBuilder &PDBFileBuilder::getTpiBuilder() {
62   if (!Tpi)
63     Tpi = llvm::make_unique<TpiStreamBuilder>(*Msf, StreamTPI);
64   return *Tpi;
65 }
66 
67 TpiStreamBuilder &PDBFileBuilder::getIpiBuilder() {
68   if (!Ipi)
69     Ipi = llvm::make_unique<TpiStreamBuilder>(*Msf, StreamIPI);
70   return *Ipi;
71 }
72 
73 PDBStringTableBuilder &PDBFileBuilder::getStringTableBuilder() {
74   return Strings;
75 }
76 
77 GSIStreamBuilder &PDBFileBuilder::getGsiBuilder() {
78   if (!Gsi)
79     Gsi = llvm::make_unique<GSIStreamBuilder>(*Msf);
80   return *Gsi;
81 }
82 
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 
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] = Data;
97   return Error::success();
98 }
99 
100 Expected<msf::MSFLayout> PDBFileBuilder::finalizeMsfLayout() {
101 
102   if (Ipi && Ipi->getRecordCount() > 0) {
103     // In theory newer PDBs always have an ID stream, but by saying that we're
104     // only going to *really* have an ID stream if there is at least one ID
105     // record, we leave open the opportunity to test older PDBs such as those
106     // that don't have an ID stream.
107     auto &Info = getInfoBuilder();
108     Info.addFeature(PdbRaw_FeatureSig::VC140);
109   }
110 
111   uint32_t StringsLen = Strings.calculateSerializedSize();
112 
113   Expected<uint32_t> SN = allocateNamedStream("/LinkInfo", 0);
114   if (!SN)
115     return SN.takeError();
116 
117   if (Gsi) {
118     if (auto EC = Gsi->finalizeMsfLayout())
119       return std::move(EC);
120     if (Dbi) {
121       Dbi->setPublicsStreamIndex(Gsi->getPublicsStreamIndex());
122       Dbi->setGlobalsStreamIndex(Gsi->getGlobalsStreamIndex());
123       Dbi->setSymbolRecordStreamIndex(Gsi->getRecordStreamIdx());
124     }
125   }
126   if (Tpi) {
127     if (auto EC = Tpi->finalizeMsfLayout())
128       return std::move(EC);
129   }
130   if (Dbi) {
131     if (auto EC = Dbi->finalizeMsfLayout())
132       return std::move(EC);
133   }
134   SN = allocateNamedStream("/names", StringsLen);
135   if (!SN)
136     return SN.takeError();
137 
138   if (Ipi) {
139     if (auto EC = Ipi->finalizeMsfLayout())
140       return std::move(EC);
141   }
142 
143   // Do this last, since it relies on the named stream map being complete, and
144   // that can be updated by previous steps in the finalization.
145   if (Info) {
146     if (auto EC = Info->finalizeMsfLayout())
147       return std::move(EC);
148   }
149 
150   return Msf->build();
151 }
152 
153 Expected<uint32_t> PDBFileBuilder::getNamedStreamIndex(StringRef Name) const {
154   uint32_t SN = 0;
155   if (!NamedStreams.get(Name, SN))
156     return llvm::make_error<pdb::RawError>(raw_error_code::no_stream);
157   return SN;
158 }
159 
160 void PDBFileBuilder::commitFpm(WritableBinaryStream &MsfBuffer,
161                                const MSFLayout &Layout) {
162   auto FpmStream =
163       WritableMappedBlockStream::createFpmStream(Layout, MsfBuffer, Allocator);
164 
165   // We only need to create the alt fpm stream so that it gets initialized.
166   WritableMappedBlockStream::createFpmStream(Layout, MsfBuffer, Allocator,
167                                              true);
168 
169   uint32_t BI = 0;
170   BinaryStreamWriter FpmWriter(*FpmStream);
171   while (BI < Layout.SB->NumBlocks) {
172     uint8_t ThisByte = 0;
173     for (uint32_t I = 0; I < 8; ++I) {
174       bool IsFree =
175           (BI < Layout.SB->NumBlocks) ? Layout.FreePageMap.test(BI) : true;
176       uint8_t Mask = uint8_t(IsFree) << I;
177       ThisByte |= Mask;
178       ++BI;
179     }
180     cantFail(FpmWriter.writeObject(ThisByte));
181   }
182   assert(FpmWriter.bytesRemaining() == 0);
183 }
184 
185 Error PDBFileBuilder::commit(StringRef Filename) {
186   assert(!Filename.empty());
187   auto ExpectedLayout = finalizeMsfLayout();
188   if (!ExpectedLayout)
189     return ExpectedLayout.takeError();
190   auto &Layout = *ExpectedLayout;
191 
192   uint64_t Filesize = Layout.SB->BlockSize * Layout.SB->NumBlocks;
193   auto OutFileOrError = FileOutputBuffer::create(Filename, Filesize);
194   if (auto E = OutFileOrError.takeError())
195     return E;
196   FileOutputBuffer *FOB = OutFileOrError->get();
197 
198   FileBufferByteStream Buffer(std::move(*OutFileOrError),
199                               llvm::support::little);
200   BinaryStreamWriter Writer(Buffer);
201 
202   if (auto EC = Writer.writeObject(*Layout.SB))
203     return EC;
204 
205   commitFpm(Buffer, Layout);
206 
207   uint32_t BlockMapOffset =
208       msf::blockToOffset(Layout.SB->BlockMapAddr, Layout.SB->BlockSize);
209   Writer.setOffset(BlockMapOffset);
210   if (auto EC = Writer.writeArray(Layout.DirectoryBlocks))
211     return EC;
212 
213   auto DirStream = WritableMappedBlockStream::createDirectoryStream(
214       Layout, Buffer, Allocator);
215   BinaryStreamWriter DW(*DirStream);
216   if (auto EC = DW.writeInteger<uint32_t>(Layout.StreamSizes.size()))
217     return EC;
218 
219   if (auto EC = DW.writeArray(Layout.StreamSizes))
220     return EC;
221 
222   for (const auto &Blocks : Layout.StreamMap) {
223     if (auto EC = DW.writeArray(Blocks))
224       return EC;
225   }
226 
227   auto ExpectedSN = getNamedStreamIndex("/names");
228   if (!ExpectedSN)
229     return ExpectedSN.takeError();
230 
231   auto NS = WritableMappedBlockStream::createIndexedStream(
232       Layout, Buffer, *ExpectedSN, Allocator);
233   BinaryStreamWriter NSWriter(*NS);
234   if (auto EC = Strings.commit(NSWriter))
235     return EC;
236 
237   for (const auto &NSE : NamedStreamData) {
238     if (NSE.second.empty())
239       continue;
240 
241     auto NS = WritableMappedBlockStream::createIndexedStream(
242         Layout, Buffer, NSE.first, Allocator);
243     BinaryStreamWriter NSW(*NS);
244     if (auto EC = NSW.writeBytes(arrayRefFromStringRef(NSE.second)))
245       return EC;
246   }
247 
248   if (Info) {
249     if (auto EC = Info->commit(Layout, Buffer))
250       return EC;
251   }
252 
253   if (Dbi) {
254     if (auto EC = Dbi->commit(Layout, Buffer))
255       return EC;
256   }
257 
258   if (Tpi) {
259     if (auto EC = Tpi->commit(Layout, Buffer))
260       return EC;
261   }
262 
263   if (Ipi) {
264     if (auto EC = Ipi->commit(Layout, Buffer))
265       return EC;
266   }
267 
268   if (Gsi) {
269     if (auto EC = Gsi->commit(Layout, Buffer))
270       return EC;
271   }
272 
273   auto InfoStreamBlocks = Layout.StreamMap[StreamPDB];
274   assert(!InfoStreamBlocks.empty());
275   uint64_t InfoStreamFileOffset =
276       blockToOffset(InfoStreamBlocks.front(), Layout.SB->BlockSize);
277   InfoStreamHeader *H = reinterpret_cast<InfoStreamHeader *>(
278       FOB->getBufferStart() + InfoStreamFileOffset);
279 
280   // Set the build id at the very end, after every other byte of the PDB
281   // has been written.
282   // FIXME: Use a hash of the PDB rather than time(nullptr) for the signature.
283   H->Age = Info->getAge();
284   H->Guid = Info->getGuid();
285   Optional<uint32_t> Sig = Info->getSignature();
286   H->Signature = Sig.hasValue() ? *Sig : time(nullptr);
287 
288   return Buffer.commit();
289 }
290