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() {} 50 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> 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 insertSegmentEnd(OriginalOffset); 112 // Since this member now becomes a new top-level record, it should have 113 // gotten a RecordPrefix injected, and that RecordPrefix + the member we 114 // just wrote should now constitute the entirety of the current "new" 115 // segment. 116 assert(getCurrentSegmentLength() == MemberLength + sizeof(RecordPrefix)); 117 } 118 119 assert(getCurrentSegmentLength() % 4 == 0); 120 assert(getCurrentSegmentLength() <= MaxSegmentLength); 121 } 122 123 uint32_t ContinuationRecordBuilder::getCurrentSegmentLength() const { 124 return SegmentWriter.getOffset() - SegmentOffsets.back(); 125 } 126 127 void ContinuationRecordBuilder::insertSegmentEnd(uint32_t Offset) { 128 uint32_t SegmentBegin = SegmentOffsets.back(); 129 assert(Offset > SegmentBegin); 130 assert(Offset - SegmentBegin <= MaxSegmentLength); 131 132 // We need to make space for the continuation record. For now we can't fill 133 // out the length or the TypeIndex of the back-reference, but we need the 134 // space to at least be there. 135 Buffer.insert(Offset, InjectedSegmentBytes); 136 137 uint32_t NewSegmentBegin = Offset + ContinuationLength; 138 uint32_t SegmentLength = NewSegmentBegin - SegmentOffsets.back(); 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, 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 CVType Type; 157 Type.Type = getTypeLeafKind(*Kind); 158 Type.RecordData = Data; 159 160 // Write the length to the RecordPrefix, making sure it does not include 161 // sizeof(RecordPrefix.Length) 162 RecordPrefix *Prefix = reinterpret_cast<RecordPrefix *>(Data.data()); 163 assert(Prefix->RecordKind == Type.Type); 164 Prefix->RecordLen = Data.size() - sizeof(RecordPrefix::RecordLen); 165 166 if (RefersTo.hasValue()) { 167 auto Continuation = Data.take_back(ContinuationLength); 168 ContinuationRecord *CR = 169 reinterpret_cast<ContinuationRecord *>(Continuation.data()); 170 assert(CR->Kind == TypeLeafKind::LF_INDEX); 171 assert(CR->IndexRef == 0xB0C0B0C0); 172 CR->IndexRef = RefersTo->getIndex(); 173 } 174 175 return Type; 176 } 177 178 std::vector<CVType> ContinuationRecordBuilder::end(TypeIndex Index) { 179 CVType Type; 180 Type.Type = getTypeLeafKind(*Kind); 181 cantFail(Mapping.visitTypeEnd(Type)); 182 183 // We're now done, and we have a series of segments each beginning at an 184 // offset specified in the SegmentOffsets array. We now need to iterate 185 // over each segment and post-process them in the following two ways: 186 // 1) Each top-level record has a RecordPrefix whose type is either 187 // LF_FIELDLIST or LF_METHODLIST, but the Length field is still 0. 188 // Those should all be set to the correct length now. 189 // 2) Each continuation record has an IndexRef field which we set to the 190 // magic value 0xB0C0B0C0. Now that the caller has told us the TypeIndex 191 // they want this sequence to start from, we can go through and update 192 // each one. 193 // 194 // Logically, the sequence of records we've built up looks like this: 195 // 196 // SegmentOffsets[0]: <Length> (Initially: uninitialized) 197 // SegmentOffsets[0]+2: LF_FIELDLIST 198 // SegmentOffsets[0]+4: Member[0] 199 // SegmentOffsets[0]+?: ... 200 // SegmentOffsets[0]+?: Member[4] 201 // SegmentOffsets[1]-8: LF_INDEX 202 // SegmentOffsets[1]-6: 0 203 // SegmentOffsets[1]-4: <Type Index of Next Record> (Initially: 0xB0C0B0C0) 204 // 205 // SegmentOffsets[1]: <Length> (Initially: uninitialized) 206 // SegmentOffsets[1]+2: LF_FIELDLIST 207 // SegmentOffsets[1]+4: Member[0] 208 // SegmentOffsets[1]+?: ... 209 // SegmentOffsets[1]+?: Member[s] 210 // SegmentOffsets[2]-8: LF_INDEX 211 // SegmentOffsets[2]-6: 0 212 // SegmentOffsets[2]-4: <Type Index of Next Record> (Initially: 0xB0C0B0C0) 213 // 214 // ... 215 // 216 // SegmentOffsets[N]: <Length> (Initially: uninitialized) 217 // SegmentOffsets[N]+2: LF_FIELDLIST 218 // SegmentOffsets[N]+4: Member[0] 219 // SegmentOffsets[N]+?: ... 220 // SegmentOffsets[N]+?: Member[t] 221 // 222 // And this is the way we have laid them out in the serialization buffer. But 223 // we cannot actually commit them to the underlying stream this way, due to 224 // the topological sorting requirement of a type stream (specifically, 225 // TypeIndex references can only point backwards, not forwards). So the 226 // sequence that we return to the caller contains the records in reverse 227 // order, which is the proper order for committing the serialized records. 228 229 std::vector<CVType> Types; 230 Types.reserve(SegmentOffsets.size()); 231 232 auto SO = makeArrayRef(SegmentOffsets); 233 234 uint32_t End = SegmentWriter.getOffset(); 235 236 Optional<TypeIndex> RefersTo; 237 for (uint32_t Offset : reverse(SO)) { 238 Types.push_back(createSegmentRecord(Offset, End, RefersTo)); 239 240 End = Offset; 241 RefersTo = Index++; 242 } 243 244 Kind.reset(); 245 return Types; 246 } 247 248 // Explicitly instantiate the member function for each known type so that we can 249 // implement this in the cpp file. 250 #define TYPE_RECORD(EnumName, EnumVal, Name) 251 #define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) 252 #define MEMBER_RECORD(EnumName, EnumVal, Name) \ 253 template void llvm::codeview::ContinuationRecordBuilder::writeMemberType( \ 254 Name##Record &Record); 255 #define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) 256 #include "llvm/DebugInfo/CodeView/CodeViewTypes.def" 257