1*0b57cec5SDimitry Andric #include "llvm/DebugInfo/CodeView/ContinuationRecordBuilder.h"
2*0b57cec5SDimitry Andric
3*0b57cec5SDimitry Andric using namespace llvm;
4*0b57cec5SDimitry Andric using namespace llvm::codeview;
5*0b57cec5SDimitry Andric
6*0b57cec5SDimitry Andric namespace {
7*0b57cec5SDimitry Andric struct ContinuationRecord {
8*0b57cec5SDimitry Andric ulittle16_t Kind{uint16_t(TypeLeafKind::LF_INDEX)};
9*0b57cec5SDimitry Andric ulittle16_t Size{0};
10*0b57cec5SDimitry Andric ulittle32_t IndexRef{0xB0C0B0C0};
11*0b57cec5SDimitry Andric };
12*0b57cec5SDimitry Andric
13*0b57cec5SDimitry Andric struct SegmentInjection {
SegmentInjection__anon16c127ee0111::SegmentInjection14*0b57cec5SDimitry Andric SegmentInjection(TypeLeafKind Kind) { Prefix.RecordKind = Kind; }
15*0b57cec5SDimitry Andric
16*0b57cec5SDimitry Andric ContinuationRecord Cont;
17*0b57cec5SDimitry Andric RecordPrefix Prefix;
18*0b57cec5SDimitry Andric };
19*0b57cec5SDimitry Andric } // namespace
20*0b57cec5SDimitry Andric
addPadding(BinaryStreamWriter & Writer)21*0b57cec5SDimitry Andric static void addPadding(BinaryStreamWriter &Writer) {
22*0b57cec5SDimitry Andric uint32_t Align = Writer.getOffset() % 4;
23*0b57cec5SDimitry Andric if (Align == 0)
24*0b57cec5SDimitry Andric return;
25*0b57cec5SDimitry Andric
26*0b57cec5SDimitry Andric int PaddingBytes = 4 - Align;
27*0b57cec5SDimitry Andric while (PaddingBytes > 0) {
28*0b57cec5SDimitry Andric uint8_t Pad = static_cast<uint8_t>(LF_PAD0 + PaddingBytes);
29*0b57cec5SDimitry Andric cantFail(Writer.writeInteger(Pad));
30*0b57cec5SDimitry Andric --PaddingBytes;
31*0b57cec5SDimitry Andric }
32*0b57cec5SDimitry Andric }
33*0b57cec5SDimitry Andric
34*0b57cec5SDimitry Andric static SegmentInjection InjectFieldList(TypeLeafKind::LF_FIELDLIST);
35*0b57cec5SDimitry Andric static SegmentInjection InjectMethodOverloadList(TypeLeafKind::LF_METHODLIST);
36*0b57cec5SDimitry Andric
37*0b57cec5SDimitry Andric static constexpr uint32_t ContinuationLength = sizeof(ContinuationRecord);
38*0b57cec5SDimitry Andric static constexpr uint32_t MaxSegmentLength =
39*0b57cec5SDimitry Andric MaxRecordLength - ContinuationLength;
40*0b57cec5SDimitry Andric
getTypeLeafKind(ContinuationRecordKind CK)41*0b57cec5SDimitry Andric static inline TypeLeafKind getTypeLeafKind(ContinuationRecordKind CK) {
42*0b57cec5SDimitry Andric return (CK == ContinuationRecordKind::FieldList) ? LF_FIELDLIST
43*0b57cec5SDimitry Andric : LF_METHODLIST;
44*0b57cec5SDimitry Andric }
45*0b57cec5SDimitry Andric
ContinuationRecordBuilder()46*0b57cec5SDimitry Andric ContinuationRecordBuilder::ContinuationRecordBuilder()
47*0b57cec5SDimitry Andric : SegmentWriter(Buffer), Mapping(SegmentWriter) {}
48*0b57cec5SDimitry Andric
49*0b57cec5SDimitry Andric ContinuationRecordBuilder::~ContinuationRecordBuilder() = default;
50*0b57cec5SDimitry Andric
begin(ContinuationRecordKind RecordKind)51*0b57cec5SDimitry Andric void ContinuationRecordBuilder::begin(ContinuationRecordKind RecordKind) {
52*0b57cec5SDimitry Andric assert(!Kind);
53*0b57cec5SDimitry Andric Kind = RecordKind;
54*0b57cec5SDimitry Andric Buffer.clear();
55*0b57cec5SDimitry Andric SegmentWriter.setOffset(0);
56*0b57cec5SDimitry Andric SegmentOffsets.clear();
57*0b57cec5SDimitry Andric SegmentOffsets.push_back(0);
58*0b57cec5SDimitry Andric assert(SegmentWriter.getOffset() == 0);
59*0b57cec5SDimitry Andric assert(SegmentWriter.getLength() == 0);
60*0b57cec5SDimitry Andric
61*0b57cec5SDimitry Andric const SegmentInjection *FLI =
62*0b57cec5SDimitry Andric (RecordKind == ContinuationRecordKind::FieldList)
63*0b57cec5SDimitry Andric ? &InjectFieldList
64*0b57cec5SDimitry Andric : &InjectMethodOverloadList;
65*0b57cec5SDimitry Andric const uint8_t *FLIB = reinterpret_cast<const uint8_t *>(FLI);
66*0b57cec5SDimitry Andric InjectedSegmentBytes =
67*0b57cec5SDimitry Andric ArrayRef<uint8_t>(FLIB, FLIB + sizeof(SegmentInjection));
68*0b57cec5SDimitry Andric
69*0b57cec5SDimitry Andric // Seed the first record with an appropriate record prefix.
70*0b57cec5SDimitry Andric RecordPrefix Prefix(getTypeLeafKind(RecordKind));
71*0b57cec5SDimitry Andric CVType Type(&Prefix, sizeof(Prefix));
72*0b57cec5SDimitry Andric cantFail(Mapping.visitTypeBegin(Type));
73*0b57cec5SDimitry Andric
74*0b57cec5SDimitry Andric cantFail(SegmentWriter.writeObject(Prefix));
75*0b57cec5SDimitry Andric }
76*0b57cec5SDimitry Andric
77*0b57cec5SDimitry Andric template <typename RecordType>
writeMemberType(RecordType & Record)78*0b57cec5SDimitry Andric void ContinuationRecordBuilder::writeMemberType(RecordType &Record) {
79*0b57cec5SDimitry Andric assert(Kind);
80*0b57cec5SDimitry Andric
81*0b57cec5SDimitry Andric uint32_t OriginalOffset = SegmentWriter.getOffset();
82*0b57cec5SDimitry Andric CVMemberRecord CVMR;
83*0b57cec5SDimitry Andric CVMR.Kind = static_cast<TypeLeafKind>(Record.getKind());
84*0b57cec5SDimitry Andric
85*0b57cec5SDimitry Andric // Member Records aren't length-prefixed, they only have a 2-byte TypeLeafKind
86*0b57cec5SDimitry Andric // at the beginning.
87*0b57cec5SDimitry Andric cantFail(SegmentWriter.writeEnum(CVMR.Kind));
88*0b57cec5SDimitry Andric
89*0b57cec5SDimitry Andric // Let the Mapping handle the rest.
90*0b57cec5SDimitry Andric cantFail(Mapping.visitMemberBegin(CVMR));
91*0b57cec5SDimitry Andric cantFail(Mapping.visitKnownMember(CVMR, Record));
92*0b57cec5SDimitry Andric cantFail(Mapping.visitMemberEnd(CVMR));
93*0b57cec5SDimitry Andric
94*0b57cec5SDimitry Andric // Make sure it's padded to 4 bytes.
95*0b57cec5SDimitry Andric addPadding(SegmentWriter);
96*0b57cec5SDimitry Andric assert(getCurrentSegmentLength() % 4 == 0);
97*0b57cec5SDimitry Andric
98*0b57cec5SDimitry Andric // The maximum length of a single segment is 64KB minus the size to insert a
99*0b57cec5SDimitry Andric // continuation. So if we are over that, inject a continuation between the
100*0b57cec5SDimitry Andric // previous member and the member that was just written, then end the previous
101*0b57cec5SDimitry Andric // segment after the continuation and begin a new one with the just-written
102*0b57cec5SDimitry Andric // member.
103*0b57cec5SDimitry Andric if (getCurrentSegmentLength() > MaxSegmentLength) {
104*0b57cec5SDimitry Andric // We need to inject some bytes before the member we just wrote but after
105*0b57cec5SDimitry Andric // the previous member. Save off the length of the member we just wrote so
106*0b57cec5SDimitry Andric // that we can do validate it.
107*0b57cec5SDimitry Andric uint32_t MemberLength = SegmentWriter.getOffset() - OriginalOffset;
108*0b57cec5SDimitry Andric (void) MemberLength;
109*0b57cec5SDimitry Andric insertSegmentEnd(OriginalOffset);
110*0b57cec5SDimitry Andric // Since this member now becomes a new top-level record, it should have
111*0b57cec5SDimitry Andric // gotten a RecordPrefix injected, and that RecordPrefix + the member we
112*0b57cec5SDimitry Andric // just wrote should now constitute the entirety of the current "new"
113*0b57cec5SDimitry Andric // segment.
114*0b57cec5SDimitry Andric assert(getCurrentSegmentLength() == MemberLength + sizeof(RecordPrefix));
115*0b57cec5SDimitry Andric }
116*0b57cec5SDimitry Andric
117*0b57cec5SDimitry Andric assert(getCurrentSegmentLength() % 4 == 0);
118*0b57cec5SDimitry Andric assert(getCurrentSegmentLength() <= MaxSegmentLength);
119*0b57cec5SDimitry Andric }
120*0b57cec5SDimitry Andric
getCurrentSegmentLength() const121*0b57cec5SDimitry Andric uint32_t ContinuationRecordBuilder::getCurrentSegmentLength() const {
122*0b57cec5SDimitry Andric return SegmentWriter.getOffset() - SegmentOffsets.back();
123*0b57cec5SDimitry Andric }
124*0b57cec5SDimitry Andric
insertSegmentEnd(uint32_t Offset)125*0b57cec5SDimitry Andric void ContinuationRecordBuilder::insertSegmentEnd(uint32_t Offset) {
126*0b57cec5SDimitry Andric uint32_t SegmentBegin = SegmentOffsets.back();
127*0b57cec5SDimitry Andric (void)SegmentBegin;
128*0b57cec5SDimitry Andric assert(Offset > SegmentBegin);
129*0b57cec5SDimitry Andric assert(Offset - SegmentBegin <= MaxSegmentLength);
130*0b57cec5SDimitry Andric
131*0b57cec5SDimitry Andric // We need to make space for the continuation record. For now we can't fill
132*0b57cec5SDimitry Andric // out the length or the TypeIndex of the back-reference, but we need the
133*0b57cec5SDimitry Andric // space to at least be there.
134*0b57cec5SDimitry Andric Buffer.insert(Offset, InjectedSegmentBytes);
135*0b57cec5SDimitry Andric
136*0b57cec5SDimitry Andric uint32_t NewSegmentBegin = Offset + ContinuationLength;
137*0b57cec5SDimitry Andric uint32_t SegmentLength = NewSegmentBegin - SegmentOffsets.back();
138*0b57cec5SDimitry Andric (void) SegmentLength;
139*0b57cec5SDimitry Andric
140*0b57cec5SDimitry Andric assert(SegmentLength % 4 == 0);
141*0b57cec5SDimitry Andric assert(SegmentLength <= MaxRecordLength);
142*0b57cec5SDimitry Andric SegmentOffsets.push_back(NewSegmentBegin);
143*0b57cec5SDimitry Andric
144*0b57cec5SDimitry Andric // Seek to the end so that we can keep writing against the new segment.
145*0b57cec5SDimitry Andric SegmentWriter.setOffset(SegmentWriter.getLength());
146*0b57cec5SDimitry Andric assert(SegmentWriter.bytesRemaining() == 0);
147*0b57cec5SDimitry Andric }
148*0b57cec5SDimitry Andric
createSegmentRecord(uint32_t OffBegin,uint32_t OffEnd,std::optional<TypeIndex> RefersTo)149*0b57cec5SDimitry Andric CVType ContinuationRecordBuilder::createSegmentRecord(
150*0b57cec5SDimitry Andric uint32_t OffBegin, uint32_t OffEnd, std::optional<TypeIndex> RefersTo) {
151*0b57cec5SDimitry Andric assert(OffEnd - OffBegin <= USHRT_MAX);
152*0b57cec5SDimitry Andric
153*0b57cec5SDimitry Andric MutableArrayRef<uint8_t> Data = Buffer.data();
154*0b57cec5SDimitry Andric Data = Data.slice(OffBegin, OffEnd - OffBegin);
155*0b57cec5SDimitry Andric
156*0b57cec5SDimitry Andric // Write the length to the RecordPrefix, making sure it does not include
157*0b57cec5SDimitry Andric // sizeof(RecordPrefix.Length)
158*0b57cec5SDimitry Andric RecordPrefix *Prefix = reinterpret_cast<RecordPrefix *>(Data.data());
159*0b57cec5SDimitry Andric Prefix->RecordLen = Data.size() - sizeof(RecordPrefix::RecordLen);
160*0b57cec5SDimitry Andric
161*0b57cec5SDimitry Andric if (RefersTo) {
162*0b57cec5SDimitry Andric auto Continuation = Data.take_back(ContinuationLength);
163*0b57cec5SDimitry Andric ContinuationRecord *CR =
164*0b57cec5SDimitry Andric reinterpret_cast<ContinuationRecord *>(Continuation.data());
165*0b57cec5SDimitry Andric assert(CR->Kind == TypeLeafKind::LF_INDEX);
166*0b57cec5SDimitry Andric assert(CR->IndexRef == 0xB0C0B0C0);
167*0b57cec5SDimitry Andric CR->IndexRef = RefersTo->getIndex();
168*0b57cec5SDimitry Andric }
169*0b57cec5SDimitry Andric
170*0b57cec5SDimitry Andric return CVType(Data);
171*0b57cec5SDimitry Andric }
172*0b57cec5SDimitry Andric
end(TypeIndex Index)173*0b57cec5SDimitry Andric std::vector<CVType> ContinuationRecordBuilder::end(TypeIndex Index) {
174*0b57cec5SDimitry Andric RecordPrefix Prefix(getTypeLeafKind(*Kind));
175*0b57cec5SDimitry Andric CVType Type(&Prefix, sizeof(Prefix));
176*0b57cec5SDimitry Andric cantFail(Mapping.visitTypeEnd(Type));
177*0b57cec5SDimitry Andric
178*0b57cec5SDimitry Andric // We're now done, and we have a series of segments each beginning at an
179*0b57cec5SDimitry Andric // offset specified in the SegmentOffsets array. We now need to iterate
180*0b57cec5SDimitry Andric // over each segment and post-process them in the following two ways:
181*0b57cec5SDimitry Andric // 1) Each top-level record has a RecordPrefix whose type is either
182*0b57cec5SDimitry Andric // LF_FIELDLIST or LF_METHODLIST, but the Length field is still 0.
183*0b57cec5SDimitry Andric // Those should all be set to the correct length now.
184*0b57cec5SDimitry Andric // 2) Each continuation record has an IndexRef field which we set to the
185*0b57cec5SDimitry Andric // magic value 0xB0C0B0C0. Now that the caller has told us the TypeIndex
186*0b57cec5SDimitry Andric // they want this sequence to start from, we can go through and update
187*0b57cec5SDimitry Andric // each one.
188*0b57cec5SDimitry Andric //
189*0b57cec5SDimitry Andric // Logically, the sequence of records we've built up looks like this:
190*0b57cec5SDimitry Andric //
191*0b57cec5SDimitry Andric // SegmentOffsets[0]: <Length> (Initially: uninitialized)
192*0b57cec5SDimitry Andric // SegmentOffsets[0]+2: LF_FIELDLIST
193*0b57cec5SDimitry Andric // SegmentOffsets[0]+4: Member[0]
194*0b57cec5SDimitry Andric // SegmentOffsets[0]+?: ...
195*0b57cec5SDimitry Andric // SegmentOffsets[0]+?: Member[4]
196*0b57cec5SDimitry Andric // SegmentOffsets[1]-8: LF_INDEX
197*0b57cec5SDimitry Andric // SegmentOffsets[1]-6: 0
198*0b57cec5SDimitry Andric // SegmentOffsets[1]-4: <Type Index of Next Record> (Initially: 0xB0C0B0C0)
199*0b57cec5SDimitry Andric //
200*0b57cec5SDimitry Andric // SegmentOffsets[1]: <Length> (Initially: uninitialized)
201*0b57cec5SDimitry Andric // SegmentOffsets[1]+2: LF_FIELDLIST
202*0b57cec5SDimitry Andric // SegmentOffsets[1]+4: Member[0]
203*0b57cec5SDimitry Andric // SegmentOffsets[1]+?: ...
204*0b57cec5SDimitry Andric // SegmentOffsets[1]+?: Member[s]
205*0b57cec5SDimitry Andric // SegmentOffsets[2]-8: LF_INDEX
206*0b57cec5SDimitry Andric // SegmentOffsets[2]-6: 0
207*0b57cec5SDimitry Andric // SegmentOffsets[2]-4: <Type Index of Next Record> (Initially: 0xB0C0B0C0)
208*0b57cec5SDimitry Andric //
209*0b57cec5SDimitry Andric // ...
210*0b57cec5SDimitry Andric //
211*0b57cec5SDimitry Andric // SegmentOffsets[N]: <Length> (Initially: uninitialized)
212*0b57cec5SDimitry Andric // SegmentOffsets[N]+2: LF_FIELDLIST
213*0b57cec5SDimitry Andric // SegmentOffsets[N]+4: Member[0]
214*0b57cec5SDimitry Andric // SegmentOffsets[N]+?: ...
215*0b57cec5SDimitry Andric // SegmentOffsets[N]+?: Member[t]
216*0b57cec5SDimitry Andric //
217*0b57cec5SDimitry Andric // And this is the way we have laid them out in the serialization buffer. But
218*0b57cec5SDimitry Andric // we cannot actually commit them to the underlying stream this way, due to
219*0b57cec5SDimitry Andric // the topological sorting requirement of a type stream (specifically,
220*0b57cec5SDimitry Andric // TypeIndex references can only point backwards, not forwards). So the
221*0b57cec5SDimitry Andric // sequence that we return to the caller contains the records in reverse
222*0b57cec5SDimitry Andric // order, which is the proper order for committing the serialized records.
223*0b57cec5SDimitry Andric
224*0b57cec5SDimitry Andric std::vector<CVType> Types;
225*0b57cec5SDimitry Andric Types.reserve(SegmentOffsets.size());
226*0b57cec5SDimitry Andric
227*0b57cec5SDimitry Andric ArrayRef SO = SegmentOffsets;
228*0b57cec5SDimitry Andric
229*0b57cec5SDimitry Andric uint32_t End = SegmentWriter.getOffset();
230*0b57cec5SDimitry Andric
231*0b57cec5SDimitry Andric std::optional<TypeIndex> RefersTo;
232*0b57cec5SDimitry Andric for (uint32_t Offset : reverse(SO)) {
233*0b57cec5SDimitry Andric Types.push_back(createSegmentRecord(Offset, End, RefersTo));
234*0b57cec5SDimitry Andric
235*0b57cec5SDimitry Andric End = Offset;
236*0b57cec5SDimitry Andric RefersTo = Index++;
237*0b57cec5SDimitry Andric }
238*0b57cec5SDimitry Andric
239*0b57cec5SDimitry Andric Kind.reset();
240*0b57cec5SDimitry Andric return Types;
241*0b57cec5SDimitry Andric }
242*0b57cec5SDimitry Andric
243*0b57cec5SDimitry Andric // Explicitly instantiate the member function for each known type so that we can
244*0b57cec5SDimitry Andric // implement this in the cpp file.
245*0b57cec5SDimitry Andric #define TYPE_RECORD(EnumName, EnumVal, Name)
246*0b57cec5SDimitry Andric #define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
247*0b57cec5SDimitry Andric #define MEMBER_RECORD(EnumName, EnumVal, Name) \
248*0b57cec5SDimitry Andric template void llvm::codeview::ContinuationRecordBuilder::writeMemberType( \
249*0b57cec5SDimitry Andric Name##Record &Record);
250*0b57cec5SDimitry Andric #define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
251*0b57cec5SDimitry Andric #include "llvm/DebugInfo/CodeView/CodeViewTypes.def"
252