253 lines · cpp
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} // namespace20 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_FIELDLIST43 : 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 ? &InjectFieldList64 : &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 TypeLeafKind86 // 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 a99 // continuation. So if we are over that, inject a continuation between the100 // previous member and the member that was just written, then end the previous101 // segment after the continuation and begin a new one with the just-written102 // member.103 if (getCurrentSegmentLength() > MaxSegmentLength) {104 // We need to inject some bytes before the member we just wrote but after105 // the previous member. Save off the length of the member we just wrote so106 // 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 have111 // gotten a RecordPrefix injected, and that RecordPrefix + the member we112 // 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 fill132 // out the length or the TypeIndex of the back-reference, but we need the133 // 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 include157 // 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 an179 // offset specified in the SegmentOffsets array. We now need to iterate180 // over each segment and post-process them in the following two ways:181 // 1) Each top-level record has a RecordPrefix whose type is either182 // 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 the185 // magic value 0xB0C0B0C0. Now that the caller has told us the TypeIndex186 // they want this sequence to start from, we can go through and update187 // 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_FIELDLIST193 // SegmentOffsets[0]+4: Member[0]194 // SegmentOffsets[0]+?: ...195 // SegmentOffsets[0]+?: Member[4]196 // SegmentOffsets[1]-8: LF_INDEX197 // SegmentOffsets[1]-6: 0198 // SegmentOffsets[1]-4: <Type Index of Next Record> (Initially: 0xB0C0B0C0)199 //200 // SegmentOffsets[1]: <Length> (Initially: uninitialized)201 // SegmentOffsets[1]+2: LF_FIELDLIST202 // SegmentOffsets[1]+4: Member[0]203 // SegmentOffsets[1]+?: ...204 // SegmentOffsets[1]+?: Member[s]205 // SegmentOffsets[2]-8: LF_INDEX206 // SegmentOffsets[2]-6: 0207 // SegmentOffsets[2]-4: <Type Index of Next Record> (Initially: 0xB0C0B0C0)208 //209 // ...210 //211 // SegmentOffsets[N]: <Length> (Initially: uninitialized)212 // SegmentOffsets[N]+2: LF_FIELDLIST213 // 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. But218 // we cannot actually commit them to the underlying stream this way, due to219 // the topological sorting requirement of a type stream (specifically,220 // TypeIndex references can only point backwards, not forwards). So the221 // sequence that we return to the caller contains the records in reverse222 // 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 can244// 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 LLVM_ABI void \249 llvm::codeview::ContinuationRecordBuilder::writeMemberType( \250 Name##Record &Record);251#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)252#include "llvm/DebugInfo/CodeView/CodeViewTypes.def"253