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