1 //===- TpiStreamBuilder.cpp - -------------------------------------------===//
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/TpiStreamBuilder.h"
10 #include "llvm/ADT/ArrayRef.h"
11 #include "llvm/ADT/STLExtras.h"
12 #include "llvm/DebugInfo/CodeView/TypeIndex.h"
13 #include "llvm/DebugInfo/CodeView/TypeRecord.h"
14 #include "llvm/DebugInfo/MSF/MSFBuilder.h"
15 #include "llvm/DebugInfo/MSF/MappedBlockStream.h"
16 #include "llvm/DebugInfo/PDB/Native/PDBFile.h"
17 #include "llvm/DebugInfo/PDB/Native/RawError.h"
18 #include "llvm/DebugInfo/PDB/Native/RawTypes.h"
19 #include "llvm/Support/Allocator.h"
20 #include "llvm/Support/BinaryByteStream.h"
21 #include "llvm/Support/BinaryStreamArray.h"
22 #include "llvm/Support/BinaryStreamReader.h"
23 #include "llvm/Support/BinaryStreamWriter.h"
24 #include "llvm/Support/Endian.h"
25 #include "llvm/Support/Error.h"
30 using namespace llvm::msf
;
31 using namespace llvm::pdb
;
32 using namespace llvm::support
;
34 TpiStreamBuilder::TpiStreamBuilder(MSFBuilder
&Msf
, uint32_t StreamIdx
)
35 : Msf(Msf
), Allocator(Msf
.getAllocator()), Header(nullptr), Idx(StreamIdx
) {
38 TpiStreamBuilder::~TpiStreamBuilder() = default;
40 void TpiStreamBuilder::setVersionHeader(PdbRaw_TpiVer Version
) {
44 void TpiStreamBuilder::addTypeRecord(ArrayRef
<uint8_t> Record
,
45 Optional
<uint32_t> Hash
) {
46 // If we just crossed an 8KB threshold, add a type index offset.
47 size_t NewSize
= TypeRecordBytes
+ Record
.size();
48 constexpr size_t EightKB
= 8 * 1024;
49 if (NewSize
/ EightKB
> TypeRecordBytes
/ EightKB
|| TypeRecords
.empty()) {
50 TypeIndexOffsets
.push_back(
51 {codeview::TypeIndex(codeview::TypeIndex::FirstNonSimpleIndex
+
53 ulittle32_t(TypeRecordBytes
)});
55 TypeRecordBytes
= NewSize
;
57 TypeRecords
.push_back(Record
);
59 TypeHashes
.push_back(*Hash
);
62 Error
TpiStreamBuilder::finalize() {
64 return Error::success();
66 TpiStreamHeader
*H
= Allocator
.Allocate
<TpiStreamHeader
>();
68 uint32_t Count
= TypeRecords
.size();
70 H
->Version
= VerHeader
;
71 H
->HeaderSize
= sizeof(TpiStreamHeader
);
72 H
->TypeIndexBegin
= codeview::TypeIndex::FirstNonSimpleIndex
;
73 H
->TypeIndexEnd
= H
->TypeIndexBegin
+ Count
;
74 H
->TypeRecordBytes
= TypeRecordBytes
;
76 H
->HashStreamIndex
= HashStreamIndex
;
77 H
->HashAuxStreamIndex
= kInvalidStreamIndex
;
78 H
->HashKeySize
= sizeof(ulittle32_t
);
79 H
->NumHashBuckets
= MaxTpiHashBuckets
- 1;
81 // Recall that hash values go into a completely different stream identified by
82 // the `HashStreamIndex` field of the `TpiStreamHeader`. Therefore, the data
83 // begins at offset 0 of this independent stream.
84 H
->HashValueBuffer
.Off
= 0;
85 H
->HashValueBuffer
.Length
= calculateHashBufferSize();
87 // We never write any adjustments into our PDBs, so this is usually some
88 // offset with zero length.
89 H
->HashAdjBuffer
.Off
= H
->HashValueBuffer
.Off
+ H
->HashValueBuffer
.Length
;
90 H
->HashAdjBuffer
.Length
= 0;
92 H
->IndexOffsetBuffer
.Off
= H
->HashAdjBuffer
.Off
+ H
->HashAdjBuffer
.Length
;
93 H
->IndexOffsetBuffer
.Length
= calculateIndexOffsetSize();
96 return Error::success();
99 uint32_t TpiStreamBuilder::calculateSerializedLength() {
100 return sizeof(TpiStreamHeader
) + TypeRecordBytes
;
103 uint32_t TpiStreamBuilder::calculateHashBufferSize() const {
104 assert((TypeRecords
.size() == TypeHashes
.size() || TypeHashes
.empty()) &&
105 "either all or no type records should have hashes");
106 return TypeHashes
.size() * sizeof(ulittle32_t
);
109 uint32_t TpiStreamBuilder::calculateIndexOffsetSize() const {
110 return TypeIndexOffsets
.size() * sizeof(codeview::TypeIndexOffset
);
113 Error
TpiStreamBuilder::finalizeMsfLayout() {
114 uint32_t Length
= calculateSerializedLength();
115 if (auto EC
= Msf
.setStreamSize(Idx
, Length
))
118 uint32_t HashStreamSize
=
119 calculateHashBufferSize() + calculateIndexOffsetSize();
121 if (HashStreamSize
== 0)
122 return Error::success();
124 auto ExpectedIndex
= Msf
.addStream(HashStreamSize
);
126 return ExpectedIndex
.takeError();
127 HashStreamIndex
= *ExpectedIndex
;
128 if (!TypeHashes
.empty()) {
129 ulittle32_t
*H
= Allocator
.Allocate
<ulittle32_t
>(TypeHashes
.size());
130 MutableArrayRef
<ulittle32_t
> HashBuffer(H
, TypeHashes
.size());
131 for (uint32_t I
= 0; I
< TypeHashes
.size(); ++I
) {
132 HashBuffer
[I
] = TypeHashes
[I
] % (MaxTpiHashBuckets
- 1);
134 ArrayRef
<uint8_t> Bytes(
135 reinterpret_cast<const uint8_t *>(HashBuffer
.data()),
136 calculateHashBufferSize());
138 std::make_unique
<BinaryByteStream
>(Bytes
, llvm::support::little
);
140 return Error::success();
143 Error
TpiStreamBuilder::commit(const msf::MSFLayout
&Layout
,
144 WritableBinaryStreamRef Buffer
) {
145 if (auto EC
= finalize())
148 auto InfoS
= WritableMappedBlockStream::createIndexedStream(Layout
, Buffer
,
151 BinaryStreamWriter
Writer(*InfoS
);
152 if (auto EC
= Writer
.writeObject(*Header
))
155 for (auto Rec
: TypeRecords
) {
156 assert(!Rec
.empty()); // An empty record will not write anything, but it
157 // would shift all offsets from here on.
158 if (auto EC
= Writer
.writeBytes(Rec
))
162 if (HashStreamIndex
!= kInvalidStreamIndex
) {
163 auto HVS
= WritableMappedBlockStream::createIndexedStream(
164 Layout
, Buffer
, HashStreamIndex
, Allocator
);
165 BinaryStreamWriter
HW(*HVS
);
166 if (HashValueStream
) {
167 if (auto EC
= HW
.writeStreamRef(*HashValueStream
))
171 for (auto &IndexOffset
: TypeIndexOffsets
) {
172 if (auto EC
= HW
.writeObject(IndexOffset
))
177 return Error::success();