1 //===- DbiStreamBuilder.cpp - PDB Dbi Stream 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/DbiStreamBuilder.h"
11 #include "llvm/ADT/ArrayRef.h"
12 #include "llvm/BinaryFormat/COFF.h"
13 #include "llvm/DebugInfo/CodeView/DebugFrameDataSubsection.h"
14 #include "llvm/DebugInfo/MSF/MSFBuilder.h"
15 #include "llvm/DebugInfo/MSF/MappedBlockStream.h"
16 #include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptorBuilder.h"
17 #include "llvm/DebugInfo/PDB/Native/DbiStream.h"
18 #include "llvm/DebugInfo/PDB/Native/RawError.h"
19 #include "llvm/Object/COFF.h"
20 #include "llvm/Support/BinaryStreamWriter.h"
23 using namespace llvm::codeview
;
24 using namespace llvm::msf
;
25 using namespace llvm::pdb
;
27 DbiStreamBuilder::DbiStreamBuilder(msf::MSFBuilder
&Msf
)
28 : Msf(Msf
), Allocator(Msf
.getAllocator()), Age(1), BuildNumber(0),
29 PdbDllVersion(0), PdbDllRbld(0), Flags(0), MachineType(PDB_Machine::x86
),
32 DbiStreamBuilder::~DbiStreamBuilder() {}
34 void DbiStreamBuilder::setVersionHeader(PdbRaw_DbiVer V
) { VerHeader
= V
; }
36 void DbiStreamBuilder::setAge(uint32_t A
) { Age
= A
; }
38 void DbiStreamBuilder::setBuildNumber(uint16_t B
) { BuildNumber
= B
; }
40 void DbiStreamBuilder::setBuildNumber(uint8_t Major
, uint8_t Minor
) {
41 BuildNumber
= (uint16_t(Major
) << DbiBuildNo::BuildMajorShift
) &
42 DbiBuildNo::BuildMajorMask
;
43 BuildNumber
|= (uint16_t(Minor
) << DbiBuildNo::BuildMinorShift
) &
44 DbiBuildNo::BuildMinorMask
;
45 BuildNumber
|= DbiBuildNo::NewVersionFormatMask
;
48 void DbiStreamBuilder::setPdbDllVersion(uint16_t V
) { PdbDllVersion
= V
; }
50 void DbiStreamBuilder::setPdbDllRbld(uint16_t R
) { PdbDllRbld
= R
; }
52 void DbiStreamBuilder::setFlags(uint16_t F
) { Flags
= F
; }
54 void DbiStreamBuilder::setMachineType(PDB_Machine M
) { MachineType
= M
; }
56 void DbiStreamBuilder::setMachineType(COFF::MachineTypes M
) {
57 // These enums are mirrors of each other, so we can just cast the value.
58 MachineType
= static_cast<pdb::PDB_Machine
>(static_cast<unsigned>(M
));
61 void DbiStreamBuilder::setSectionMap(ArrayRef
<SecMapEntry
> SecMap
) {
65 void DbiStreamBuilder::setGlobalsStreamIndex(uint32_t Index
) {
66 GlobalsStreamIndex
= Index
;
69 void DbiStreamBuilder::setSymbolRecordStreamIndex(uint32_t Index
) {
70 SymRecordStreamIndex
= Index
;
73 void DbiStreamBuilder::setPublicsStreamIndex(uint32_t Index
) {
74 PublicsStreamIndex
= Index
;
77 void DbiStreamBuilder::addNewFpoData(const codeview::FrameData
&FD
) {
78 if (!NewFpoData
.hasValue())
79 NewFpoData
.emplace(false);
81 NewFpoData
->addFrameData(FD
);
84 void DbiStreamBuilder::addOldFpoData(const object::FpoData
&FD
) {
85 OldFpoData
.push_back(FD
);
88 Error
DbiStreamBuilder::addDbgStream(pdb::DbgHeaderType Type
,
89 ArrayRef
<uint8_t> Data
) {
90 assert(Type
!= DbgHeaderType::NewFPO
&&
91 "NewFPO data should be written via addFrameData()!");
93 DbgStreams
[(int)Type
].emplace();
94 DbgStreams
[(int)Type
]->Size
= Data
.size();
95 DbgStreams
[(int)Type
]->WriteFn
= [Data
](BinaryStreamWriter
&Writer
) {
96 return Writer
.writeArray(Data
);
98 return Error::success();
101 uint32_t DbiStreamBuilder::addECName(StringRef Name
) {
102 return ECNamesBuilder
.insert(Name
);
105 uint32_t DbiStreamBuilder::calculateSerializedLength() const {
106 // For now we only support serializing the header.
107 return sizeof(DbiStreamHeader
) + calculateFileInfoSubstreamSize() +
108 calculateModiSubstreamSize() + calculateSectionContribsStreamSize() +
109 calculateSectionMapStreamSize() + calculateDbgStreamsSize() +
110 ECNamesBuilder
.calculateSerializedSize();
113 Expected
<DbiModuleDescriptorBuilder
&>
114 DbiStreamBuilder::addModuleInfo(StringRef ModuleName
) {
115 uint32_t Index
= ModiList
.size();
117 std::make_unique
<DbiModuleDescriptorBuilder
>(ModuleName
, Index
, Msf
));
118 return *ModiList
.back();
121 Error
DbiStreamBuilder::addModuleSourceFile(DbiModuleDescriptorBuilder
&Module
,
123 uint32_t Index
= SourceFileNames
.size();
124 SourceFileNames
.insert(std::make_pair(File
, Index
));
125 Module
.addSourceFile(File
);
126 return Error::success();
129 Expected
<uint32_t> DbiStreamBuilder::getSourceFileNameIndex(StringRef File
) {
130 auto NameIter
= SourceFileNames
.find(File
);
131 if (NameIter
== SourceFileNames
.end())
132 return make_error
<RawError
>(raw_error_code::no_entry
,
133 "The specified source file was not found");
134 return NameIter
->getValue();
137 uint32_t DbiStreamBuilder::calculateModiSubstreamSize() const {
139 for (const auto &M
: ModiList
)
140 Size
+= M
->calculateSerializedLength();
144 uint32_t DbiStreamBuilder::calculateSectionContribsStreamSize() const {
145 if (SectionContribs
.empty())
147 return sizeof(enum PdbRaw_DbiSecContribVer
) +
148 sizeof(SectionContribs
[0]) * SectionContribs
.size();
151 uint32_t DbiStreamBuilder::calculateSectionMapStreamSize() const {
152 if (SectionMap
.empty())
154 return sizeof(SecMapHeader
) + sizeof(SecMapEntry
) * SectionMap
.size();
157 uint32_t DbiStreamBuilder::calculateNamesOffset() const {
159 Offset
+= sizeof(ulittle16_t
); // NumModules
160 Offset
+= sizeof(ulittle16_t
); // NumSourceFiles
161 Offset
+= ModiList
.size() * sizeof(ulittle16_t
); // ModIndices
162 Offset
+= ModiList
.size() * sizeof(ulittle16_t
); // ModFileCounts
163 uint32_t NumFileInfos
= 0;
164 for (const auto &M
: ModiList
)
165 NumFileInfos
+= M
->source_files().size();
166 Offset
+= NumFileInfos
* sizeof(ulittle32_t
); // FileNameOffsets
170 uint32_t DbiStreamBuilder::calculateFileInfoSubstreamSize() const {
171 uint32_t Size
= calculateNamesOffset();
172 Size
+= calculateNamesBufferSize();
173 return alignTo(Size
, sizeof(uint32_t));
176 uint32_t DbiStreamBuilder::calculateNamesBufferSize() const {
178 for (const auto &F
: SourceFileNames
) {
179 Size
+= F
.getKeyLength() + 1; // Names[I];
184 uint32_t DbiStreamBuilder::calculateDbgStreamsSize() const {
185 return DbgStreams
.size() * sizeof(uint16_t);
188 Error
DbiStreamBuilder::generateFileInfoSubstream() {
189 uint32_t Size
= calculateFileInfoSubstreamSize();
190 auto Data
= Allocator
.Allocate
<uint8_t>(Size
);
191 uint32_t NamesOffset
= calculateNamesOffset();
193 FileInfoBuffer
= MutableBinaryByteStream(MutableArrayRef
<uint8_t>(Data
, Size
),
194 llvm::support::little
);
196 WritableBinaryStreamRef MetadataBuffer
=
197 WritableBinaryStreamRef(FileInfoBuffer
).keep_front(NamesOffset
);
198 BinaryStreamWriter
MetadataWriter(MetadataBuffer
);
200 uint16_t ModiCount
= std::min
<uint32_t>(UINT16_MAX
, ModiList
.size());
201 uint16_t FileCount
= std::min
<uint32_t>(UINT16_MAX
, SourceFileNames
.size());
202 if (auto EC
= MetadataWriter
.writeInteger(ModiCount
)) // NumModules
204 if (auto EC
= MetadataWriter
.writeInteger(FileCount
)) // NumSourceFiles
206 for (uint16_t I
= 0; I
< ModiCount
; ++I
) {
207 if (auto EC
= MetadataWriter
.writeInteger(I
)) // Mod Indices
210 for (const auto &MI
: ModiList
) {
211 FileCount
= static_cast<uint16_t>(MI
->source_files().size());
212 if (auto EC
= MetadataWriter
.writeInteger(FileCount
)) // Mod File Counts
216 // Before writing the FileNameOffsets array, write the NamesBuffer array.
217 // A side effect of this is that this will actually compute the various
218 // file name offsets, so we can then go back and write the FileNameOffsets
219 // array to the other substream.
220 NamesBuffer
= WritableBinaryStreamRef(FileInfoBuffer
).drop_front(NamesOffset
);
221 BinaryStreamWriter
NameBufferWriter(NamesBuffer
);
222 for (auto &Name
: SourceFileNames
) {
223 Name
.second
= NameBufferWriter
.getOffset();
224 if (auto EC
= NameBufferWriter
.writeCString(Name
.getKey()))
228 for (const auto &MI
: ModiList
) {
229 for (StringRef Name
: MI
->source_files()) {
230 auto Result
= SourceFileNames
.find(Name
);
231 if (Result
== SourceFileNames
.end())
232 return make_error
<RawError
>(raw_error_code::no_entry
,
233 "The source file was not found.");
234 if (auto EC
= MetadataWriter
.writeInteger(Result
->second
))
239 if (auto EC
= NameBufferWriter
.padToAlignment(sizeof(uint32_t)))
242 if (NameBufferWriter
.bytesRemaining() > 0)
243 return make_error
<RawError
>(raw_error_code::invalid_format
,
244 "The names buffer contained unexpected data.");
246 if (MetadataWriter
.bytesRemaining() > sizeof(uint32_t))
247 return make_error
<RawError
>(
248 raw_error_code::invalid_format
,
249 "The metadata buffer contained unexpected data.");
251 return Error::success();
254 Error
DbiStreamBuilder::finalize() {
256 return Error::success();
258 for (auto &MI
: ModiList
)
261 if (auto EC
= generateFileInfoSubstream())
264 DbiStreamHeader
*H
= Allocator
.Allocate
<DbiStreamHeader
>();
265 ::memset(H
, 0, sizeof(DbiStreamHeader
));
266 H
->VersionHeader
= *VerHeader
;
267 H
->VersionSignature
= -1;
269 H
->BuildNumber
= BuildNumber
;
271 H
->PdbDllRbld
= PdbDllRbld
;
272 H
->PdbDllVersion
= PdbDllVersion
;
273 H
->MachineType
= static_cast<uint16_t>(MachineType
);
275 H
->ECSubstreamSize
= ECNamesBuilder
.calculateSerializedSize();
276 H
->FileInfoSize
= FileInfoBuffer
.getLength();
277 H
->ModiSubstreamSize
= calculateModiSubstreamSize();
278 H
->OptionalDbgHdrSize
= DbgStreams
.size() * sizeof(uint16_t);
279 H
->SecContrSubstreamSize
= calculateSectionContribsStreamSize();
280 H
->SectionMapSize
= calculateSectionMapStreamSize();
281 H
->TypeServerSize
= 0;
282 H
->SymRecordStreamIndex
= SymRecordStreamIndex
;
283 H
->PublicSymbolStreamIndex
= PublicsStreamIndex
;
284 H
->MFCTypeServerIndex
= 0; // Not sure what this is, but link.exe writes 0.
285 H
->GlobalSymbolStreamIndex
= GlobalsStreamIndex
;
288 return Error::success();
291 Error
DbiStreamBuilder::finalizeMsfLayout() {
292 if (NewFpoData
.hasValue()) {
293 DbgStreams
[(int)DbgHeaderType::NewFPO
].emplace();
294 DbgStreams
[(int)DbgHeaderType::NewFPO
]->Size
=
295 NewFpoData
->calculateSerializedSize();
296 DbgStreams
[(int)DbgHeaderType::NewFPO
]->WriteFn
=
297 [this](BinaryStreamWriter
&Writer
) {
298 return NewFpoData
->commit(Writer
);
302 if (!OldFpoData
.empty()) {
303 DbgStreams
[(int)DbgHeaderType::FPO
].emplace();
304 DbgStreams
[(int)DbgHeaderType::FPO
]->Size
=
305 sizeof(object::FpoData
) * OldFpoData
.size();
306 DbgStreams
[(int)DbgHeaderType::FPO
]->WriteFn
=
307 [this](BinaryStreamWriter
&Writer
) {
308 return Writer
.writeArray(makeArrayRef(OldFpoData
));
312 for (auto &S
: DbgStreams
) {
315 auto ExpectedIndex
= Msf
.addStream(S
->Size
);
317 return ExpectedIndex
.takeError();
318 S
->StreamNumber
= *ExpectedIndex
;
321 for (auto &MI
: ModiList
) {
322 if (auto EC
= MI
->finalizeMsfLayout())
326 uint32_t Length
= calculateSerializedLength();
327 if (auto EC
= Msf
.setStreamSize(StreamDBI
, Length
))
329 return Error::success();
332 static uint16_t toSecMapFlags(uint32_t Flags
) {
334 if (Flags
& COFF::IMAGE_SCN_MEM_READ
)
335 Ret
|= static_cast<uint16_t>(OMFSegDescFlags::Read
);
336 if (Flags
& COFF::IMAGE_SCN_MEM_WRITE
)
337 Ret
|= static_cast<uint16_t>(OMFSegDescFlags::Write
);
338 if (Flags
& COFF::IMAGE_SCN_MEM_EXECUTE
)
339 Ret
|= static_cast<uint16_t>(OMFSegDescFlags::Execute
);
340 if (Flags
& COFF::IMAGE_SCN_MEM_EXECUTE
)
341 Ret
|= static_cast<uint16_t>(OMFSegDescFlags::Execute
);
342 if (!(Flags
& COFF::IMAGE_SCN_MEM_16BIT
))
343 Ret
|= static_cast<uint16_t>(OMFSegDescFlags::AddressIs32Bit
);
345 // This seems always 1.
346 Ret
|= static_cast<uint16_t>(OMFSegDescFlags::IsSelector
);
351 // A utility function to create a Section Map for a given list of COFF sections.
353 // A Section Map seem to be a copy of a COFF section list in other format.
354 // I don't know why a PDB file contains both a COFF section header and
355 // a Section Map, but it seems it must be present in a PDB.
356 std::vector
<SecMapEntry
> DbiStreamBuilder::createSectionMap(
357 ArrayRef
<llvm::object::coff_section
> SecHdrs
) {
358 std::vector
<SecMapEntry
> Ret
;
361 auto Add
= [&]() -> SecMapEntry
& {
363 auto &Entry
= Ret
.back();
364 memset(&Entry
, 0, sizeof(Entry
));
366 Entry
.Frame
= Idx
+ 1;
368 // We don't know the meaning of these fields yet.
369 Entry
.SecName
= UINT16_MAX
;
370 Entry
.ClassName
= UINT16_MAX
;
375 for (auto &Hdr
: SecHdrs
) {
377 Entry
.Flags
= toSecMapFlags(Hdr
.Characteristics
);
378 Entry
.SecByteLength
= Hdr
.VirtualSize
;
382 // The last entry is for absolute symbols.
384 Entry
.Flags
= static_cast<uint16_t>(OMFSegDescFlags::AddressIs32Bit
) |
385 static_cast<uint16_t>(OMFSegDescFlags::IsAbsoluteAddress
);
386 Entry
.SecByteLength
= UINT32_MAX
;
391 Error
DbiStreamBuilder::commit(const msf::MSFLayout
&Layout
,
392 WritableBinaryStreamRef MsfBuffer
) {
393 if (auto EC
= finalize())
396 auto DbiS
= WritableMappedBlockStream::createIndexedStream(
397 Layout
, MsfBuffer
, StreamDBI
, Allocator
);
399 BinaryStreamWriter
Writer(*DbiS
);
400 if (auto EC
= Writer
.writeObject(*Header
))
403 for (auto &M
: ModiList
) {
404 if (auto EC
= M
->commit(Writer
, Layout
, MsfBuffer
))
408 if (!SectionContribs
.empty()) {
409 if (auto EC
= Writer
.writeEnum(DbiSecContribVer60
))
411 if (auto EC
= Writer
.writeArray(makeArrayRef(SectionContribs
)))
415 if (!SectionMap
.empty()) {
416 ulittle16_t Size
= static_cast<ulittle16_t
>(SectionMap
.size());
417 SecMapHeader SMHeader
= {Size
, Size
};
418 if (auto EC
= Writer
.writeObject(SMHeader
))
420 if (auto EC
= Writer
.writeArray(SectionMap
))
424 if (auto EC
= Writer
.writeStreamRef(FileInfoBuffer
))
427 if (auto EC
= ECNamesBuilder
.commit(Writer
))
430 for (auto &Stream
: DbgStreams
) {
431 uint16_t StreamNumber
= kInvalidStreamIndex
;
432 if (Stream
.hasValue())
433 StreamNumber
= Stream
->StreamNumber
;
434 if (auto EC
= Writer
.writeInteger(StreamNumber
))
438 for (auto &Stream
: DbgStreams
) {
439 if (!Stream
.hasValue())
441 assert(Stream
->StreamNumber
!= kInvalidStreamIndex
);
443 auto WritableStream
= WritableMappedBlockStream::createIndexedStream(
444 Layout
, MsfBuffer
, Stream
->StreamNumber
, Allocator
);
445 BinaryStreamWriter
DbgStreamWriter(*WritableStream
);
447 if (auto EC
= Stream
->WriteFn(DbgStreamWriter
))
451 if (Writer
.bytesRemaining() > 0)
452 return make_error
<RawError
>(raw_error_code::invalid_format
,
453 "Unexpected bytes found in DBI Stream");
454 return Error::success();