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