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/CRC.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(arrayRefFromStringRef(IS
.Content
->getBuffer()));
179 SrcHeaderBlockEntry Entry
;
180 ::memset(&Entry
, 0, sizeof(SrcHeaderBlockEntry
));
181 Entry
.Size
= sizeof(SrcHeaderBlockEntry
);
182 Entry
.FileSize
= IS
.Content
->getBufferSize();
183 Entry
.FileNI
= IS
.NameIndex
;
184 Entry
.VFileNI
= IS
.VNameIndex
;
188 static_cast<uint32_t>(PdbRaw_SrcHeaderBlockVer::SrcVerOne
);
189 Entry
.CRC
= CRC
.getCRC();
190 StringRef VName
= getStringTableBuilder().getStringForId(IS
.VNameIndex
);
191 InjectedSourceTable
.set_as(VName
, std::move(Entry
),
192 InjectedSourceHashTraits
);
195 uint32_t SrcHeaderBlockSize
=
196 sizeof(SrcHeaderBlockHeader
) +
197 InjectedSourceTable
.calculateSerializedLength();
198 SN
= allocateNamedStream("/src/headerblock", SrcHeaderBlockSize
);
200 return SN
.takeError();
201 for (const auto &IS
: InjectedSources
) {
202 SN
= allocateNamedStream(IS
.StreamName
, IS
.Content
->getBufferSize());
204 return SN
.takeError();
208 // Do this last, since it relies on the named stream map being complete, and
209 // that can be updated by previous steps in the finalization.
211 if (auto EC
= Info
->finalizeMsfLayout())
215 return Error::success();
218 Expected
<uint32_t> PDBFileBuilder::getNamedStreamIndex(StringRef Name
) const {
220 if (!NamedStreams
.get(Name
, SN
))
221 return llvm::make_error
<pdb::RawError
>(raw_error_code::no_stream
);
225 void PDBFileBuilder::commitSrcHeaderBlock(WritableBinaryStream
&MsfBuffer
,
226 const msf::MSFLayout
&Layout
) {
227 assert(!InjectedSourceTable
.empty());
229 uint32_t SN
= cantFail(getNamedStreamIndex("/src/headerblock"));
230 auto Stream
= WritableMappedBlockStream::createIndexedStream(
231 Layout
, MsfBuffer
, SN
, Allocator
);
232 BinaryStreamWriter
Writer(*Stream
);
234 SrcHeaderBlockHeader Header
;
235 ::memset(&Header
, 0, sizeof(Header
));
236 Header
.Version
= static_cast<uint32_t>(PdbRaw_SrcHeaderBlockVer::SrcVerOne
);
237 Header
.Size
= Writer
.bytesRemaining();
239 cantFail(Writer
.writeObject(Header
));
240 cantFail(InjectedSourceTable
.commit(Writer
));
242 assert(Writer
.bytesRemaining() == 0);
245 void PDBFileBuilder::commitInjectedSources(WritableBinaryStream
&MsfBuffer
,
246 const msf::MSFLayout
&Layout
) {
247 if (InjectedSourceTable
.empty())
250 commitSrcHeaderBlock(MsfBuffer
, Layout
);
252 for (const auto &IS
: InjectedSources
) {
253 uint32_t SN
= cantFail(getNamedStreamIndex(IS
.StreamName
));
255 auto SourceStream
= WritableMappedBlockStream::createIndexedStream(
256 Layout
, MsfBuffer
, SN
, Allocator
);
257 BinaryStreamWriter
SourceWriter(*SourceStream
);
258 assert(SourceWriter
.bytesRemaining() == IS
.Content
->getBufferSize());
259 cantFail(SourceWriter
.writeBytes(
260 arrayRefFromStringRef(IS
.Content
->getBuffer())));
264 Error
PDBFileBuilder::commit(StringRef Filename
, codeview::GUID
*Guid
) {
265 assert(!Filename
.empty());
266 if (auto EC
= finalizeMsfLayout())
270 Expected
<FileBufferByteStream
> ExpectedMsfBuffer
=
271 Msf
->commit(Filename
, Layout
);
272 if (!ExpectedMsfBuffer
)
273 return ExpectedMsfBuffer
.takeError();
274 FileBufferByteStream Buffer
= std::move(*ExpectedMsfBuffer
);
276 auto ExpectedSN
= getNamedStreamIndex("/names");
278 return ExpectedSN
.takeError();
280 auto NS
= WritableMappedBlockStream::createIndexedStream(
281 Layout
, Buffer
, *ExpectedSN
, Allocator
);
282 BinaryStreamWriter
NSWriter(*NS
);
283 if (auto EC
= Strings
.commit(NSWriter
))
286 for (const auto &NSE
: NamedStreamData
) {
287 if (NSE
.second
.empty())
290 auto NS
= WritableMappedBlockStream::createIndexedStream(
291 Layout
, Buffer
, NSE
.first
, Allocator
);
292 BinaryStreamWriter
NSW(*NS
);
293 if (auto EC
= NSW
.writeBytes(arrayRefFromStringRef(NSE
.second
)))
298 if (auto EC
= Info
->commit(Layout
, Buffer
))
303 if (auto EC
= Dbi
->commit(Layout
, Buffer
))
308 if (auto EC
= Tpi
->commit(Layout
, Buffer
))
313 if (auto EC
= Ipi
->commit(Layout
, Buffer
))
318 if (auto EC
= Gsi
->commit(Layout
, Buffer
))
322 auto InfoStreamBlocks
= Layout
.StreamMap
[StreamPDB
];
323 assert(!InfoStreamBlocks
.empty());
324 uint64_t InfoStreamFileOffset
=
325 blockToOffset(InfoStreamBlocks
.front(), Layout
.SB
->BlockSize
);
326 InfoStreamHeader
*H
= reinterpret_cast<InfoStreamHeader
*>(
327 Buffer
.getBufferStart() + InfoStreamFileOffset
);
329 commitInjectedSources(Buffer
, Layout
);
331 // Set the build id at the very end, after every other byte of the PDB
333 if (Info
->hashPDBContentsToGUID()) {
334 // Compute a hash of all sections of the output file.
336 xxHash64({Buffer
.getBufferStart(), Buffer
.getBufferEnd()});
340 memcpy(H
->Guid
.Guid
, &Digest
, 8);
341 // xxhash only gives us 8 bytes, so put some fixed data in the other half.
342 memcpy(H
->Guid
.Guid
+ 8, "LLD PDB.", 8);
344 // Put the hash in the Signature field too.
345 H
->Signature
= static_cast<uint32_t>(Digest
);
347 // Return GUID to caller.
348 memcpy(Guid
, H
->Guid
.Guid
, 16);
350 H
->Age
= Info
->getAge();
351 H
->Guid
= Info
->getGuid();
352 Optional
<uint32_t> Sig
= Info
->getSignature();
353 H
->Signature
= Sig
.hasValue() ? *Sig
: time(nullptr);
356 return Buffer
.commit();