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(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 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(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 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(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 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, 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 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(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 173 std::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