• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 {
SegmentInjection__anon7a2a5afb0111::SegmentInjection14   SegmentInjection(TypeLeafKind Kind) { Prefix.RecordKind = Kind; }
15 
16   ContinuationRecord Cont;
17   RecordPrefix Prefix;
18 };
19 } // namespace
20 
addPadding(BinaryStreamWriter & Writer)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(Writer.writeInteger(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 
getTypeLeafKind(ContinuationRecordKind CK)41 static inline TypeLeafKind getTypeLeafKind(ContinuationRecordKind CK) {
42   return (CK == ContinuationRecordKind::FieldList) ? LF_FIELDLIST
43                                                    : LF_METHODLIST;
44 }
45 
ContinuationRecordBuilder()46 ContinuationRecordBuilder::ContinuationRecordBuilder()
47     : SegmentWriter(Buffer), Mapping(SegmentWriter) {}
48 
~ContinuationRecordBuilder()49 ContinuationRecordBuilder::~ContinuationRecordBuilder() {}
50 
begin(ContinuationRecordKind RecordKind)51 void ContinuationRecordBuilder::begin(ContinuationRecordKind RecordKind) {
52   assert(!Kind.hasValue());
53   Kind = RecordKind;
54   Buffer.clear();
55   SegmentWriter.setOffset(0);
56   SegmentOffsets.clear();
57   SegmentOffsets.push_back(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   CVType Type;
70   Type.Type = getTypeLeafKind(RecordKind);
71   cantFail(Mapping.visitTypeBegin(Type));
72 
73   // Seed the first trecord with an appropriate record prefix.
74   RecordPrefix Prefix;
75   Prefix.RecordLen = 0;
76   Prefix.RecordKind = Type.Type;
77   cantFail(SegmentWriter.writeObject(Prefix));
78 }
79 
80 template <typename RecordType>
writeMemberType(RecordType & Record)81 void ContinuationRecordBuilder::writeMemberType(RecordType &Record) {
82   assert(Kind.hasValue());
83 
84   uint32_t OriginalOffset = SegmentWriter.getOffset();
85   CVMemberRecord CVMR;
86   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   cantFail(SegmentWriter.writeEnum(CVMR.Kind));
91 
92   // Let the Mapping handle the rest.
93   cantFail(Mapping.visitMemberBegin(CVMR));
94   cantFail(Mapping.visitKnownMember(CVMR, Record));
95   cantFail(Mapping.visitMemberEnd(CVMR));
96 
97   // Make sure it's padded to 4 bytes.
98   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   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     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 }
123 
getCurrentSegmentLength() const124 uint32_t ContinuationRecordBuilder::getCurrentSegmentLength() const {
125   return SegmentWriter.getOffset() - SegmentOffsets.back();
126 }
127 
insertSegmentEnd(uint32_t Offset)128 void ContinuationRecordBuilder::insertSegmentEnd(uint32_t Offset) {
129   uint32_t SegmentBegin = SegmentOffsets.back();
130   (void)SegmentBegin;
131   assert(Offset > SegmentBegin);
132   assert(Offset - SegmentBegin <= MaxSegmentLength);
133 
134   // We need to make space for the continuation record.  For now we can't fill
135   // out the length or the TypeIndex of the back-reference, but we need the
136   // space to at least be there.
137   Buffer.insert(Offset, InjectedSegmentBytes);
138 
139   uint32_t NewSegmentBegin = Offset + ContinuationLength;
140   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   SegmentWriter.setOffset(SegmentWriter.getLength());
149   assert(SegmentWriter.bytesRemaining() == 0);
150 }
151 
createSegmentRecord(uint32_t OffBegin,uint32_t OffEnd,Optional<TypeIndex> RefersTo)152 CVType ContinuationRecordBuilder::createSegmentRecord(
153     uint32_t OffBegin, uint32_t OffEnd, Optional<TypeIndex> RefersTo) {
154   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   // sizeof(RecordPrefix.Length)
165   RecordPrefix *Prefix = reinterpret_cast<RecordPrefix *>(Data.data());
166   assert(Prefix->RecordKind == Type.Type);
167   Prefix->RecordLen = Data.size() - sizeof(RecordPrefix::RecordLen);
168 
169   if (RefersTo.hasValue()) {
170     auto Continuation = Data.take_back(ContinuationLength);
171     ContinuationRecord *CR =
172         reinterpret_cast<ContinuationRecord *>(Continuation.data());
173     assert(CR->Kind == TypeLeafKind::LF_INDEX);
174     assert(CR->IndexRef == 0xB0C0B0C0);
175     CR->IndexRef = RefersTo->getIndex();
176   }
177 
178   return Type;
179 }
180 
end(TypeIndex Index)181 std::vector<CVType> ContinuationRecordBuilder::end(TypeIndex Index) {
182   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   //    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   //
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   // SegmentOffsets[1]-4: <Type Index of Next Record> (Initially: 0xB0C0B0C0)
207   //
208   // SegmentOffsets[1]:   <Length>                    (Initially: uninitialized)
209   // SegmentOffsets[1]+2: LF_FIELDLIST
210   // SegmentOffsets[1]+4: Member[0]
211   // SegmentOffsets[1]+?: ...
212   // 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   //
217   // ...
218   //
219   // SegmentOffsets[N]:   <Length>                    (Initially: uninitialized)
220   // SegmentOffsets[N]+2: LF_FIELDLIST
221   // SegmentOffsets[N]+4: Member[0]
222   // SegmentOffsets[N]+?: ...
223   // SegmentOffsets[N]+?: Member[t]
224   //
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   std::vector<CVType> Types;
233   Types.reserve(SegmentOffsets.size());
234 
235   auto SO = makeArrayRef(SegmentOffsets);
236 
237   uint32_t End = SegmentWriter.getOffset();
238 
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   return Types;
249 }
250 
251 // Explicitly instantiate the member function for each known type so that we can
252 // implement this in the cpp file.
253 #define TYPE_RECORD(EnumName, EnumVal, Name)
254 #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 #define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
259 #include "llvm/DebugInfo/CodeView/CodeViewTypes.def"
260