LLVM 18.0.0git
PDBFileBuilder.cpp
Go to the documentation of this file.
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
26#include "llvm/Support/CRC.h"
27#include "llvm/Support/Path.h"
28#include "llvm/Support/xxhash.h"
29
30#include <ctime>
31
32using namespace llvm;
33using namespace llvm::codeview;
34using namespace llvm::msf;
35using namespace llvm::pdb;
36using namespace llvm::support;
37
38namespace llvm {
40}
41
43 : Allocator(Allocator), InjectedSourceHashTraits(Strings),
44 InjectedSourceTable(2) {}
45
47
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
57
59 if (!Info)
60 Info = std::make_unique<InfoStreamBuilder>(*Msf, NamedStreams);
61 return *Info;
62}
63
65 if (!Dbi)
66 Dbi = std::make_unique<DbiStreamBuilder>(*Msf);
67 return *Dbi;
68}
69
71 if (!Tpi)
72 Tpi = std::make_unique<TpiStreamBuilder>(*Msf, StreamTPI);
73 return *Tpi;
74}
75
77 if (!Ipi)
78 Ipi = std::make_unique<TpiStreamBuilder>(*Msf, StreamIPI);
79 return *Ipi;
80}
81
83 return Strings;
84}
85
87 if (!Gsi)
88 Gsi = std::make_unique<GSIStreamBuilder>(*Msf);
89 return *Gsi;
90}
91
92Expected<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
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
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;
116
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
131Error PDBFileBuilder::finalizeMsfLayout() {
132
133 if (Ipi && Ipi->getRecordCount() > 0) {
134 // In theory newer PDBs always have an ID stream, but by saying that we're
135 // only going to *really* have an ID stream if there is at least one ID
136 // record, we leave open the opportunity to test older PDBs such as those
137 // that don't have an ID stream.
138 auto &Info = getInfoBuilder();
139 Info.addFeature(PdbRaw_FeatureSig::VC140);
140 }
141
142 uint32_t StringsLen = Strings.calculateSerializedSize();
143
144 Expected<uint32_t> SN = allocateNamedStream("/LinkInfo", 0);
145 if (!SN)
146 return SN.takeError();
147
148 if (Gsi) {
149 if (auto EC = Gsi->finalizeMsfLayout())
150 return EC;
151 if (Dbi) {
152 Dbi->setPublicsStreamIndex(Gsi->getPublicsStreamIndex());
153 Dbi->setGlobalsStreamIndex(Gsi->getGlobalsStreamIndex());
154 Dbi->setSymbolRecordStreamIndex(Gsi->getRecordStreamIndex());
155 }
156 }
157 if (Tpi) {
158 if (auto EC = Tpi->finalizeMsfLayout())
159 return EC;
160 }
161 if (Dbi) {
162 if (auto EC = Dbi->finalizeMsfLayout())
163 return EC;
164 }
165 SN = allocateNamedStream("/names", StringsLen);
166 if (!SN)
167 return SN.takeError();
168
169 if (Ipi) {
170 if (auto EC = Ipi->finalizeMsfLayout())
171 return EC;
172 }
173
174 // Do this last, since it relies on the named stream map being complete, and
175 // that can be updated by previous steps in the finalization.
176 if (Info) {
177 if (auto EC = Info->finalizeMsfLayout())
178 return EC;
179 }
180
181 if (!InjectedSources.empty()) {
182 for (const auto &IS : InjectedSources) {
183 JamCRC CRC(0);
184 CRC.update(arrayRefFromStringRef(IS.Content->getBuffer()));
185
187 ::memset(&Entry, 0, sizeof(SrcHeaderBlockEntry));
188 Entry.Size = sizeof(SrcHeaderBlockEntry);
189 Entry.FileSize = IS.Content->getBufferSize();
190 Entry.FileNI = IS.NameIndex;
191 Entry.VFileNI = IS.VNameIndex;
192 Entry.ObjNI = 1;
193 Entry.IsVirtual = 0;
194 Entry.Version =
196 Entry.CRC = CRC.getCRC();
197 StringRef VName = getStringTableBuilder().getStringForId(IS.VNameIndex);
198 InjectedSourceTable.set_as(VName, std::move(Entry),
199 InjectedSourceHashTraits);
200 }
201
202 uint32_t SrcHeaderBlockSize =
203 sizeof(SrcHeaderBlockHeader) +
204 InjectedSourceTable.calculateSerializedLength();
205 SN = allocateNamedStream("/src/headerblock", SrcHeaderBlockSize);
206 if (!SN)
207 return SN.takeError();
208 for (const auto &IS : InjectedSources) {
209 SN = allocateNamedStream(IS.StreamName, IS.Content->getBufferSize());
210 if (!SN)
211 return SN.takeError();
212 }
213 }
214
215 // Do this last, since it relies on the named stream map being complete, and
216 // that can be updated by previous steps in the finalization.
217 if (Info) {
218 if (auto EC = Info->finalizeMsfLayout())
219 return EC;
220 }
221
222 return Error::success();
223}
224
226 uint32_t SN = 0;
227 if (!NamedStreams.get(Name, SN))
228 return llvm::make_error<pdb::RawError>(raw_error_code::no_stream);
229 return SN;
230}
231
232void PDBFileBuilder::commitSrcHeaderBlock(WritableBinaryStream &MsfBuffer,
233 const msf::MSFLayout &Layout) {
234 assert(!InjectedSourceTable.empty());
235
236 uint32_t SN = cantFail(getNamedStreamIndex("/src/headerblock"));
238 Layout, MsfBuffer, SN, Allocator);
239 BinaryStreamWriter Writer(*Stream);
240
242 ::memset(&Header, 0, sizeof(Header));
243 Header.Version = static_cast<uint32_t>(PdbRaw_SrcHeaderBlockVer::SrcVerOne);
244 Header.Size = Writer.bytesRemaining();
245
246 cantFail(Writer.writeObject(Header));
247 cantFail(InjectedSourceTable.commit(Writer));
248
249 assert(Writer.bytesRemaining() == 0);
250}
251
252void PDBFileBuilder::commitInjectedSources(WritableBinaryStream &MsfBuffer,
253 const msf::MSFLayout &Layout) {
254 if (InjectedSourceTable.empty())
255 return;
256
257 commitSrcHeaderBlock(MsfBuffer, Layout);
258
259 for (const auto &IS : InjectedSources) {
260 uint32_t SN = cantFail(getNamedStreamIndex(IS.StreamName));
261
263 Layout, MsfBuffer, SN, Allocator);
264 BinaryStreamWriter SourceWriter(*SourceStream);
265 assert(SourceWriter.bytesRemaining() == IS.Content->getBufferSize());
266 cantFail(SourceWriter.writeBytes(
267 arrayRefFromStringRef(IS.Content->getBuffer())));
268 }
269}
270
272 assert(!Filename.empty());
273 if (auto EC = finalizeMsfLayout())
274 return EC;
275
276 MSFLayout Layout;
277 Expected<FileBufferByteStream> ExpectedMsfBuffer =
278 Msf->commit(Filename, Layout);
279 if (!ExpectedMsfBuffer)
280 return ExpectedMsfBuffer.takeError();
281 FileBufferByteStream Buffer = std::move(*ExpectedMsfBuffer);
282
283 auto ExpectedSN = getNamedStreamIndex("/names");
284 if (!ExpectedSN)
285 return ExpectedSN.takeError();
286
288 Layout, Buffer, *ExpectedSN, Allocator);
289 BinaryStreamWriter NSWriter(*NS);
290 if (auto EC = Strings.commit(NSWriter))
291 return EC;
292
293 for (const auto &NSE : NamedStreamData) {
294 if (NSE.second.empty())
295 continue;
296
298 Layout, Buffer, NSE.first, Allocator);
299 BinaryStreamWriter NSW(*NS);
300 if (auto EC = NSW.writeBytes(arrayRefFromStringRef(NSE.second)))
301 return EC;
302 }
303
304 if (Info) {
305 if (auto EC = Info->commit(Layout, Buffer))
306 return EC;
307 }
308
309 if (Dbi) {
310 if (auto EC = Dbi->commit(Layout, Buffer))
311 return EC;
312 }
313
314 if (Tpi) {
315 if (auto EC = Tpi->commit(Layout, Buffer))
316 return EC;
317 }
318
319 if (Ipi) {
320 if (auto EC = Ipi->commit(Layout, Buffer))
321 return EC;
322 }
323
324 if (Gsi) {
325 if (auto EC = Gsi->commit(Layout, Buffer))
326 return EC;
327 }
328
329 auto InfoStreamBlocks = Layout.StreamMap[StreamPDB];
330 assert(!InfoStreamBlocks.empty());
331 uint64_t InfoStreamFileOffset =
332 blockToOffset(InfoStreamBlocks.front(), Layout.SB->BlockSize);
333 InfoStreamHeader *H = reinterpret_cast<InfoStreamHeader *>(
334 Buffer.getBufferStart() + InfoStreamFileOffset);
335
336 commitInjectedSources(Buffer, Layout);
337
338 // Set the build id at the very end, after every other byte of the PDB
339 // has been written.
340 if (Info->hashPDBContentsToGUID()) {
341 // Compute a hash of all sections of the output file.
342 uint64_t Digest =
343 xxh3_64bits({Buffer.getBufferStart(), Buffer.getBufferEnd()});
344
345 H->Age = 1;
346
347 memcpy(H->Guid.Guid, &Digest, 8);
348 // xxhash only gives us 8 bytes, so put some fixed data in the other half.
349 memcpy(H->Guid.Guid + 8, "LLD PDB.", 8);
350
351 // Put the hash in the Signature field too.
352 H->Signature = static_cast<uint32_t>(Digest);
353
354 // Return GUID to caller.
355 memcpy(Guid, H->Guid.Guid, 16);
356 } else {
357 H->Age = Info->getAge();
358 H->Guid = Info->getGuid();
359 std::optional<uint32_t> Sig = Info->getSignature();
360 H->Signature = Sig ? *Sig : time(nullptr);
361 }
362
363 return Buffer.commit();
364}
Analysis containing CSE Info
Definition: CSEInfo.cpp:27
std::string Name
uint64_t Size
#define H(x, y, z)
Definition: MD5.cpp:57
Basic Register Allocator
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
This file defines the SmallString class.
This file contains some functions that are useful when dealing with strings.
static const int BlockSize
Definition: TarWriter.cpp:33
Provides write only access to a subclass of WritableBinaryStream.
Error writeBytes(ArrayRef< uint8_t > Buffer)
Write the bytes specified in Buffer to the underlying stream.
Allocate memory in an ever growing pool, as if by bump-pointer.
Definition: Allocator.h:66
size_type count(const_arg_type_t< KeyT > Val) const
Return 1 if the specified key is in the map, 0 otherwise.
Definition: DenseMap.h:151
Lightweight error class with error context and mandatory checking.
Definition: Error.h:160
static ErrorSuccess success()
Create a success value.
Definition: Error.h:334
Tagged union holding either a T or a Error.
Definition: Error.h:474
Error takeError()
Take ownership of the stored error.
Definition: Error.h:601
An implementation of WritableBinaryStream backed by an llvm FileOutputBuffer.
Error commit() override
For buffered streams, commits changes to the backing store.
uint8_t * getBufferEnd() const
Returns a pointer to the end of the buffer.
uint8_t * getBufferStart() const
Returns a pointer to the start of the buffer.
SmallString - A SmallString is just a SmallVector with methods and accessors that make it work better...
Definition: SmallString.h:26
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:50
A BinaryStream which can be read from as well as written to.
Definition: BinaryStream.h:73
static Expected< MSFBuilder > create(BumpPtrAllocator &Allocator, uint32_t BlockSize, uint32_t MinBlockCount=0, bool CanGrow=true)
Create a new MSFBuilder.
Definition: MSFBuilder.cpp:50
static std::unique_ptr< WritableMappedBlockStream > createIndexedStream(const MSFLayout &Layout, WritableBinaryStreamRef MsfData, uint32_t StreamIndex, BumpPtrAllocator &Allocator)
bool set_as(const Key &K, ValueT V, TraitsT &Traits)
Set the entry using a key type that the specified Traits can convert from a real key to an internal k...
Definition: HashTable.h:250
Error commit(BinaryStreamWriter &Writer) const
Definition: HashTable.h:180
bool empty() const
Definition: HashTable.h:208
uint32_t calculateSerializedLength() const
Definition: HashTable.h:153
void set(StringRef Stream, uint32_t StreamNo)
bool get(StringRef Stream, uint32_t &StreamNo) const
TpiStreamBuilder & getTpiBuilder()
msf::MSFBuilder & getMsfBuilder()
PDBStringTableBuilder & getStringTableBuilder()
Error addNamedStream(StringRef Name, StringRef Data)
DbiStreamBuilder & getDbiBuilder()
TpiStreamBuilder & getIpiBuilder()
GSIStreamBuilder & getGsiBuilder()
InfoStreamBuilder & getInfoBuilder()
Error initialize(uint32_t BlockSize)
PDBFileBuilder(BumpPtrAllocator &Allocator)
Error commit(StringRef Filename, codeview::GUID *Guid)
void addInjectedSource(StringRef Name, std::unique_ptr< MemoryBuffer > Buffer)
Expected< uint32_t > getNamedStreamIndex(StringRef Name) const
Error commit(BinaryStreamWriter &Writer) const
StringRef getStringForId(uint32_t Id) const
uint64_t blockToOffset(uint64_t BlockNumber, uint64_t BlockSize)
Definition: MSFCommon.h:135
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
uint64_t xxh3_64bits(ArrayRef< uint8_t > data)
Definition: xxhash.cpp:397
Op::Description Desc
void cantFail(Error Err, const char *Msg=nullptr)
Report a fatal error if Err is a failure value.
Definition: Error.h:749
Description of the encoding of one expression Op.
This represents the 'GUID' type from windows.h.
Definition: GUID.h:21
const SuperBlock * SB
Definition: MSFCommon.h:64
std::vector< ArrayRef< support::ulittle32_t > > StreamMap
Definition: MSFCommon.h:68
support::ulittle32_t BlockSize
Definition: MSFCommon.h:36
The header preceding the global PDB Stream (Stream 1)
Definition: RawTypes.h:304
A single file record entry within the /src/headerblock stream.
Definition: RawTypes.h:331
support::ulittle32_t Size
Definition: RawTypes.h:332
support::ulittle32_t FileNI
Definition: RawTypes.h:336
support::ulittle32_t VFileNI
Definition: RawTypes.h:338
support::ulittle32_t CRC
Definition: RawTypes.h:334
support::ulittle32_t ObjNI
Definition: RawTypes.h:337
support::ulittle32_t FileSize
Definition: RawTypes.h:335
support::ulittle32_t Version
Definition: RawTypes.h:333
The header preceding the /src/headerblock stream.
Definition: RawTypes.h:321