Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/llvm/lib/DebugInfo/PDB/Native/PDBFileBuilder.cpp
35293 views
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
374