1 //===- PDBFileBuilder.cpp - PDB File Creation -------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 #include "llvm/DebugInfo/PDB/Native/PDBFileBuilder.h"
11 #include "llvm/ADT/BitVector.h"
13 #include "llvm/DebugInfo/MSF/MSFBuilder.h"
14 #include "llvm/DebugInfo/PDB/Native/DbiStream.h"
15 #include "llvm/DebugInfo/PDB/Native/DbiStreamBuilder.h"
16 #include "llvm/DebugInfo/PDB/Native/GSIStreamBuilder.h"
17 #include "llvm/DebugInfo/PDB/Native/InfoStream.h"
18 #include "llvm/DebugInfo/PDB/Native/InfoStreamBuilder.h"
19 #include "llvm/DebugInfo/PDB/Native/PDBStringTableBuilder.h"
20 #include "llvm/DebugInfo/PDB/Native/RawError.h"
21 #include "llvm/DebugInfo/PDB/Native/TpiStream.h"
22 #include "llvm/DebugInfo/PDB/Native/TpiStreamBuilder.h"
23 #include "llvm/Support/BinaryStream.h"
24 #include "llvm/Support/BinaryStreamWriter.h"
25 #include "llvm/Support/JamCRC.h"
26 #include "llvm/Support/Path.h"
27 #include "llvm/Support/xxhash.h"
30 using namespace llvm::codeview
;
31 using namespace llvm::msf
;
32 using namespace llvm::pdb
;
33 using namespace llvm::support
;
35 PDBFileBuilder::PDBFileBuilder(BumpPtrAllocator
&Allocator
)
36 : Allocator(Allocator
), InjectedSourceHashTraits(Strings
),
37 InjectedSourceTable(2) {}
39 PDBFileBuilder::~PDBFileBuilder() {}
41 Error
PDBFileBuilder::initialize(uint32_t BlockSize
) {
42 auto ExpectedMsf
= MSFBuilder::create(Allocator
, BlockSize
);
44 return ExpectedMsf
.takeError();
45 Msf
= std::make_unique
<MSFBuilder
>(std::move(*ExpectedMsf
));
46 return Error::success();
49 MSFBuilder
&PDBFileBuilder::getMsfBuilder() { return *Msf
; }
51 InfoStreamBuilder
&PDBFileBuilder::getInfoBuilder() {
53 Info
= std::make_unique
<InfoStreamBuilder
>(*Msf
, NamedStreams
);
57 DbiStreamBuilder
&PDBFileBuilder::getDbiBuilder() {
59 Dbi
= std::make_unique
<DbiStreamBuilder
>(*Msf
);
63 TpiStreamBuilder
&PDBFileBuilder::getTpiBuilder() {
65 Tpi
= std::make_unique
<TpiStreamBuilder
>(*Msf
, StreamTPI
);
69 TpiStreamBuilder
&PDBFileBuilder::getIpiBuilder() {
71 Ipi
= std::make_unique
<TpiStreamBuilder
>(*Msf
, StreamIPI
);
75 PDBStringTableBuilder
&PDBFileBuilder::getStringTableBuilder() {
79 GSIStreamBuilder
&PDBFileBuilder::getGsiBuilder() {
81 Gsi
= std::make_unique
<GSIStreamBuilder
>(*Msf
);
85 Expected
<uint32_t> PDBFileBuilder::allocateNamedStream(StringRef Name
,
87 auto ExpectedStream
= Msf
->addStream(Size
);
89 NamedStreams
.set(Name
, *ExpectedStream
);
90 return ExpectedStream
;
93 Error
PDBFileBuilder::addNamedStream(StringRef Name
, StringRef Data
) {
94 Expected
<uint32_t> ExpectedIndex
= allocateNamedStream(Name
, Data
.size());
96 return ExpectedIndex
.takeError();
97 assert(NamedStreamData
.count(*ExpectedIndex
) == 0);
98 NamedStreamData
[*ExpectedIndex
] = Data
;
99 return Error::success();
102 void PDBFileBuilder::addInjectedSource(StringRef Name
,
103 std::unique_ptr
<MemoryBuffer
> Buffer
) {
104 // Stream names must be exact matches, since they get looked up in a hash
105 // table and the hash value is dependent on the exact contents of the string.
106 // link.exe lowercases a path and converts / to \, so we must do the same.
107 SmallString
<64> VName
;
108 sys::path::native(Name
.lower(), VName
);
110 uint32_t NI
= getStringTableBuilder().insert(Name
);
111 uint32_t VNI
= getStringTableBuilder().insert(VName
);
113 InjectedSourceDescriptor Desc
;
114 Desc
.Content
= std::move(Buffer
);
116 Desc
.VNameIndex
= VNI
;
117 Desc
.StreamName
= "/src/files/";
119 Desc
.StreamName
+= VName
;
121 InjectedSources
.push_back(std::move(Desc
));
124 Error
PDBFileBuilder::finalizeMsfLayout() {
126 if (Ipi
&& Ipi
->getRecordCount() > 0) {
127 // In theory newer PDBs always have an ID stream, but by saying that we're
128 // only going to *really* have an ID stream if there is at least one ID
129 // record, we leave open the opportunity to test older PDBs such as those
130 // that don't have an ID stream.
131 auto &Info
= getInfoBuilder();
132 Info
.addFeature(PdbRaw_FeatureSig::VC140
);
135 uint32_t StringsLen
= Strings
.calculateSerializedSize();
137 Expected
<uint32_t> SN
= allocateNamedStream("/LinkInfo", 0);
139 return SN
.takeError();
142 if (auto EC
= Gsi
->finalizeMsfLayout())
145 Dbi
->setPublicsStreamIndex(Gsi
->getPublicsStreamIndex());
146 Dbi
->setGlobalsStreamIndex(Gsi
->getGlobalsStreamIndex());
147 Dbi
->setSymbolRecordStreamIndex(Gsi
->getRecordStreamIdx());
151 if (auto EC
= Tpi
->finalizeMsfLayout())
155 if (auto EC
= Dbi
->finalizeMsfLayout())
158 SN
= allocateNamedStream("/names", StringsLen
);
160 return SN
.takeError();
163 if (auto EC
= Ipi
->finalizeMsfLayout())
167 // Do this last, since it relies on the named stream map being complete, and
168 // that can be updated by previous steps in the finalization.
170 if (auto EC
= Info
->finalizeMsfLayout())
174 if (!InjectedSources
.empty()) {
175 for (const auto &IS
: InjectedSources
) {
177 CRC
.update(makeArrayRef(IS
.Content
->getBufferStart(),
178 IS
.Content
->getBufferSize()));
180 SrcHeaderBlockEntry Entry
;
181 ::memset(&Entry
, 0, sizeof(SrcHeaderBlockEntry
));
182 Entry
.Size
= sizeof(SrcHeaderBlockEntry
);
183 Entry
.FileSize
= IS
.Content
->getBufferSize();
184 Entry
.FileNI
= IS
.NameIndex
;
185 Entry
.VFileNI
= IS
.VNameIndex
;
189 static_cast<uint32_t>(PdbRaw_SrcHeaderBlockVer::SrcVerOne
);
190 Entry
.CRC
= CRC
.getCRC();
191 StringRef VName
= getStringTableBuilder().getStringForId(IS
.VNameIndex
);
192 InjectedSourceTable
.set_as(VName
, std::move(Entry
),
193 InjectedSourceHashTraits
);
196 uint32_t SrcHeaderBlockSize
=
197 sizeof(SrcHeaderBlockHeader
) +
198 InjectedSourceTable
.calculateSerializedLength();
199 SN
= allocateNamedStream("/src/headerblock", SrcHeaderBlockSize
);
201 return SN
.takeError();
202 for (const auto &IS
: InjectedSources
) {
203 SN
= allocateNamedStream(IS
.StreamName
, IS
.Content
->getBufferSize());
205 return SN
.takeError();
209 // Do this last, since it relies on the named stream map being complete, and
210 // that can be updated by previous steps in the finalization.
212 if (auto EC
= Info
->finalizeMsfLayout())
216 return Error::success();
219 Expected
<uint32_t> PDBFileBuilder::getNamedStreamIndex(StringRef Name
) const {
221 if (!NamedStreams
.get(Name
, SN
))
222 return llvm::make_error
<pdb::RawError
>(raw_error_code::no_stream
);
226 void PDBFileBuilder::commitSrcHeaderBlock(WritableBinaryStream
&MsfBuffer
,
227 const msf::MSFLayout
&Layout
) {
228 assert(!InjectedSourceTable
.empty());
230 uint32_t SN
= cantFail(getNamedStreamIndex("/src/headerblock"));
231 auto Stream
= WritableMappedBlockStream::createIndexedStream(
232 Layout
, MsfBuffer
, SN
, Allocator
);
233 BinaryStreamWriter
Writer(*Stream
);
235 SrcHeaderBlockHeader Header
;
236 ::memset(&Header
, 0, sizeof(Header
));
237 Header
.Version
= static_cast<uint32_t>(PdbRaw_SrcHeaderBlockVer::SrcVerOne
);
238 Header
.Size
= Writer
.bytesRemaining();
240 cantFail(Writer
.writeObject(Header
));
241 cantFail(InjectedSourceTable
.commit(Writer
));
243 assert(Writer
.bytesRemaining() == 0);
246 void PDBFileBuilder::commitInjectedSources(WritableBinaryStream
&MsfBuffer
,
247 const msf::MSFLayout
&Layout
) {
248 if (InjectedSourceTable
.empty())
251 commitSrcHeaderBlock(MsfBuffer
, Layout
);
253 for (const auto &IS
: InjectedSources
) {
254 uint32_t SN
= cantFail(getNamedStreamIndex(IS
.StreamName
));
256 auto SourceStream
= WritableMappedBlockStream::createIndexedStream(
257 Layout
, MsfBuffer
, SN
, Allocator
);
258 BinaryStreamWriter
SourceWriter(*SourceStream
);
259 assert(SourceWriter
.bytesRemaining() == IS
.Content
->getBufferSize());
260 cantFail(SourceWriter
.writeBytes(
261 arrayRefFromStringRef(IS
.Content
->getBuffer())));
265 Error
PDBFileBuilder::commit(StringRef Filename
, codeview::GUID
*Guid
) {
266 assert(!Filename
.empty());
267 if (auto EC
= finalizeMsfLayout())
271 Expected
<FileBufferByteStream
> ExpectedMsfBuffer
=
272 Msf
->commit(Filename
, Layout
);
273 if (!ExpectedMsfBuffer
)
274 return ExpectedMsfBuffer
.takeError();
275 FileBufferByteStream Buffer
= std::move(*ExpectedMsfBuffer
);
277 auto ExpectedSN
= getNamedStreamIndex("/names");
279 return ExpectedSN
.takeError();
281 auto NS
= WritableMappedBlockStream::createIndexedStream(
282 Layout
, Buffer
, *ExpectedSN
, Allocator
);
283 BinaryStreamWriter
NSWriter(*NS
);
284 if (auto EC
= Strings
.commit(NSWriter
))
287 for (const auto &NSE
: NamedStreamData
) {
288 if (NSE
.second
.empty())
291 auto NS
= WritableMappedBlockStream::createIndexedStream(
292 Layout
, Buffer
, NSE
.first
, Allocator
);
293 BinaryStreamWriter
NSW(*NS
);
294 if (auto EC
= NSW
.writeBytes(arrayRefFromStringRef(NSE
.second
)))
299 if (auto EC
= Info
->commit(Layout
, Buffer
))
304 if (auto EC
= Dbi
->commit(Layout
, Buffer
))
309 if (auto EC
= Tpi
->commit(Layout
, Buffer
))
314 if (auto EC
= Ipi
->commit(Layout
, Buffer
))
319 if (auto EC
= Gsi
->commit(Layout
, Buffer
))
323 auto InfoStreamBlocks
= Layout
.StreamMap
[StreamPDB
];
324 assert(!InfoStreamBlocks
.empty());
325 uint64_t InfoStreamFileOffset
=
326 blockToOffset(InfoStreamBlocks
.front(), Layout
.SB
->BlockSize
);
327 InfoStreamHeader
*H
= reinterpret_cast<InfoStreamHeader
*>(
328 Buffer
.getBufferStart() + InfoStreamFileOffset
);
330 commitInjectedSources(Buffer
, Layout
);
332 // Set the build id at the very end, after every other byte of the PDB
334 if (Info
->hashPDBContentsToGUID()) {
335 // Compute a hash of all sections of the output file.
337 xxHash64({Buffer
.getBufferStart(), Buffer
.getBufferEnd()});
341 memcpy(H
->Guid
.Guid
, &Digest
, 8);
342 // xxhash only gives us 8 bytes, so put some fixed data in the other half.
343 memcpy(H
->Guid
.Guid
+ 8, "LLD PDB.", 8);
345 // Put the hash in the Signature field too.
346 H
->Signature
= static_cast<uint32_t>(Digest
);
348 // Return GUID to caller.
349 memcpy(Guid
, H
->Guid
.Guid
, 16);
351 H
->Age
= Info
->getAge();
352 H
->Guid
= Info
->getGuid();
353 Optional
<uint32_t> Sig
= Info
->getSignature();
354 H
->Signature
= Sig
.hasValue() ? *Sig
: time(nullptr);
357 return Buffer
.commit();