1#include "llvm/DebugInfo/CodeView/ContinuationRecordBuilder.h"
2
3using namespace llvm;
4using namespace llvm::codeview;
5
6namespace {
7struct ContinuationRecord {
8  ulittle16_t Kind{uint16_t(TypeLeafKind::LF_INDEX)};
9  ulittle16_t Size{0};
10  ulittle32_t IndexRef{0xB0C0B0C0};
11};
12
13struct SegmentInjection {
14  SegmentInjection(TypeLeafKind Kind) { Prefix.RecordKind = Kind; }
15
16  ContinuationRecord Cont;
17  RecordPrefix Prefix;
18};
19} // namespace
20
21static 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
34static SegmentInjection InjectFieldList(TypeLeafKind::LF_FIELDLIST);
35static SegmentInjection InjectMethodOverloadList(TypeLeafKind::LF_METHODLIST);
36
37static constexpr uint32_t ContinuationLength = sizeof(ContinuationRecord);
38static constexpr uint32_t MaxSegmentLength =
39    MaxRecordLength - ContinuationLength;
40
41static inline TypeLeafKind getTypeLeafKind(ContinuationRecordKind CK) {
42  return (CK == ContinuationRecordKind::FieldList) ? LF_FIELDLIST
43                                                   : LF_METHODLIST;
44}
45
46ContinuationRecordBuilder::ContinuationRecordBuilder()
47    : SegmentWriter(Buffer), Mapping(SegmentWriter) {}
48
49ContinuationRecordBuilder::~ContinuationRecordBuilder() = default;
50
51void ContinuationRecordBuilder::begin(ContinuationRecordKind RecordKind) {
52  assert(!Kind);
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  // Seed the first record with an appropriate record prefix.
70  RecordPrefix Prefix(getTypeLeafKind(RecordKind));
71  CVType Type(&Prefix, sizeof(Prefix));
72  cantFail(Mapping.visitTypeBegin(Type));
73
74  cantFail(SegmentWriter.writeObject(Prefix));
75}
76
77template <typename RecordType>
78void 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(SegmentWriter.writeEnum(CVMR.Kind));
88
89  // Let the Mapping handle the rest.
90  cantFail(Mapping.visitMemberBegin(CVMR));
91  cantFail(Mapping.visitKnownMember(CVMR, Record));
92  cantFail(Mapping.visitMemberEnd(CVMR));
93
94  // Make sure it's padded to 4 bytes.
95  addPadding(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(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
121uint32_t ContinuationRecordBuilder::getCurrentSegmentLength() const {
122  return SegmentWriter.getOffset() - SegmentOffsets.back();
123}
124
125void 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, 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(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
149CVType 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(OffBegin, 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(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
173std::vector<CVType> ContinuationRecordBuilder::end(TypeIndex Index) {
174  RecordPrefix Prefix(getTypeLeafKind(*Kind));
175  CVType Type(&Prefix, sizeof(Prefix));
176  cantFail(Mapping.visitTypeEnd(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(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(SO)) {
233    Types.push_back(createSegmentRecord(Offset, 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