xref: /llvm-project/llvm/lib/DebugInfo/CodeView/ContinuationRecordBuilder.cpp (revision 38818b60c58c76ba89b990978cdfd2d7b6799260)
16900de1dSZachary Turner #include "llvm/DebugInfo/CodeView/ContinuationRecordBuilder.h"
26900de1dSZachary Turner 
36900de1dSZachary Turner using namespace llvm;
46900de1dSZachary Turner using namespace llvm::codeview;
56900de1dSZachary Turner 
66900de1dSZachary Turner namespace {
76900de1dSZachary Turner struct ContinuationRecord {
86900de1dSZachary Turner   ulittle16_t Kind{uint16_t(TypeLeafKind::LF_INDEX)};
96900de1dSZachary Turner   ulittle16_t Size{0};
106900de1dSZachary Turner   ulittle32_t IndexRef{0xB0C0B0C0};
116900de1dSZachary Turner };
126900de1dSZachary Turner 
136900de1dSZachary Turner struct SegmentInjection {
SegmentInjection__anonbe5e98eb0111::SegmentInjection146900de1dSZachary Turner   SegmentInjection(TypeLeafKind Kind) { Prefix.RecordKind = Kind; }
156900de1dSZachary Turner 
166900de1dSZachary Turner   ContinuationRecord Cont;
176900de1dSZachary Turner   RecordPrefix Prefix;
186900de1dSZachary Turner };
196900de1dSZachary Turner } // namespace
206900de1dSZachary Turner 
addPadding(BinaryStreamWriter & Writer)216900de1dSZachary Turner static void addPadding(BinaryStreamWriter &Writer) {
226900de1dSZachary Turner   uint32_t Align = Writer.getOffset() % 4;
236900de1dSZachary Turner   if (Align == 0)
246900de1dSZachary Turner     return;
256900de1dSZachary Turner 
266900de1dSZachary Turner   int PaddingBytes = 4 - Align;
276900de1dSZachary Turner   while (PaddingBytes > 0) {
286900de1dSZachary Turner     uint8_t Pad = static_cast<uint8_t>(LF_PAD0 + PaddingBytes);
296900de1dSZachary Turner     cantFail(Writer.writeInteger(Pad));
306900de1dSZachary Turner     --PaddingBytes;
316900de1dSZachary Turner   }
326900de1dSZachary Turner }
336900de1dSZachary Turner 
346900de1dSZachary Turner static SegmentInjection InjectFieldList(TypeLeafKind::LF_FIELDLIST);
356900de1dSZachary Turner static SegmentInjection InjectMethodOverloadList(TypeLeafKind::LF_METHODLIST);
366900de1dSZachary Turner 
376900de1dSZachary Turner static constexpr uint32_t ContinuationLength = sizeof(ContinuationRecord);
386900de1dSZachary Turner static constexpr uint32_t MaxSegmentLength =
396900de1dSZachary Turner     MaxRecordLength - ContinuationLength;
406900de1dSZachary Turner 
getTypeLeafKind(ContinuationRecordKind CK)416900de1dSZachary Turner static inline TypeLeafKind getTypeLeafKind(ContinuationRecordKind CK) {
426900de1dSZachary Turner   return (CK == ContinuationRecordKind::FieldList) ? LF_FIELDLIST
436900de1dSZachary Turner                                                    : LF_METHODLIST;
446900de1dSZachary Turner }
456900de1dSZachary Turner 
ContinuationRecordBuilder()466900de1dSZachary Turner ContinuationRecordBuilder::ContinuationRecordBuilder()
476900de1dSZachary Turner     : SegmentWriter(Buffer), Mapping(SegmentWriter) {}
486900de1dSZachary Turner 
493a3cb929SKazu Hirata ContinuationRecordBuilder::~ContinuationRecordBuilder() = default;
506900de1dSZachary Turner 
begin(ContinuationRecordKind RecordKind)516900de1dSZachary Turner void ContinuationRecordBuilder::begin(ContinuationRecordKind RecordKind) {
525413bf1bSKazu Hirata   assert(!Kind);
536900de1dSZachary Turner   Kind = RecordKind;
546900de1dSZachary Turner   Buffer.clear();
556900de1dSZachary Turner   SegmentWriter.setOffset(0);
566900de1dSZachary Turner   SegmentOffsets.clear();
576900de1dSZachary Turner   SegmentOffsets.push_back(0);
586900de1dSZachary Turner   assert(SegmentWriter.getOffset() == 0);
596900de1dSZachary Turner   assert(SegmentWriter.getLength() == 0);
606900de1dSZachary Turner 
616900de1dSZachary Turner   const SegmentInjection *FLI =
626900de1dSZachary Turner       (RecordKind == ContinuationRecordKind::FieldList)
636900de1dSZachary Turner           ? &InjectFieldList
646900de1dSZachary Turner           : &InjectMethodOverloadList;
656900de1dSZachary Turner   const uint8_t *FLIB = reinterpret_cast<const uint8_t *>(FLI);
666900de1dSZachary Turner   InjectedSegmentBytes =
676900de1dSZachary Turner       ArrayRef<uint8_t>(FLIB, FLIB + sizeof(SegmentInjection));
686900de1dSZachary Turner 
69e10d0041SReid Kleckner   // Seed the first record with an appropriate record prefix.
70e10d0041SReid Kleckner   RecordPrefix Prefix(getTypeLeafKind(RecordKind));
71e10d0041SReid Kleckner   CVType Type(&Prefix, sizeof(Prefix));
726900de1dSZachary Turner   cantFail(Mapping.visitTypeBegin(Type));
736900de1dSZachary Turner 
746900de1dSZachary Turner   cantFail(SegmentWriter.writeObject(Prefix));
756900de1dSZachary Turner }
766900de1dSZachary Turner 
776900de1dSZachary Turner template <typename RecordType>
writeMemberType(RecordType & Record)786900de1dSZachary Turner void ContinuationRecordBuilder::writeMemberType(RecordType &Record) {
795413bf1bSKazu Hirata   assert(Kind);
806900de1dSZachary Turner 
816900de1dSZachary Turner   uint32_t OriginalOffset = SegmentWriter.getOffset();
826900de1dSZachary Turner   CVMemberRecord CVMR;
836900de1dSZachary Turner   CVMR.Kind = static_cast<TypeLeafKind>(Record.getKind());
846900de1dSZachary Turner 
856900de1dSZachary Turner   // Member Records aren't length-prefixed, they only have a 2-byte TypeLeafKind
866900de1dSZachary Turner   // at the beginning.
876900de1dSZachary Turner   cantFail(SegmentWriter.writeEnum(CVMR.Kind));
886900de1dSZachary Turner 
896900de1dSZachary Turner   // Let the Mapping handle the rest.
906900de1dSZachary Turner   cantFail(Mapping.visitMemberBegin(CVMR));
916900de1dSZachary Turner   cantFail(Mapping.visitKnownMember(CVMR, Record));
926900de1dSZachary Turner   cantFail(Mapping.visitMemberEnd(CVMR));
936900de1dSZachary Turner 
946900de1dSZachary Turner   // Make sure it's padded to 4 bytes.
956900de1dSZachary Turner   addPadding(SegmentWriter);
966900de1dSZachary Turner   assert(getCurrentSegmentLength() % 4 == 0);
976900de1dSZachary Turner 
986900de1dSZachary Turner   // The maximum length of a single segment is 64KB minus the size to insert a
996900de1dSZachary Turner   // continuation.  So if we are over that, inject a continuation between the
1006900de1dSZachary Turner   // previous member and the member that was just written, then end the previous
1016900de1dSZachary Turner   // segment after the continuation and begin a new one with the just-written
1026900de1dSZachary Turner   // member.
1036900de1dSZachary Turner   if (getCurrentSegmentLength() > MaxSegmentLength) {
1046900de1dSZachary Turner     // We need to inject some bytes before the member we just wrote but after
1056900de1dSZachary Turner     // the previous member.  Save off the length of the member we just wrote so
10659c84774SZarko Todorovski     // that we can do validate it.
1076900de1dSZachary Turner     uint32_t MemberLength = SegmentWriter.getOffset() - OriginalOffset;
108bba7f862SRafael Espindola     (void) MemberLength;
1096900de1dSZachary Turner     insertSegmentEnd(OriginalOffset);
1106900de1dSZachary Turner     // Since this member now becomes a new top-level record, it should have
1116900de1dSZachary Turner     // gotten a RecordPrefix injected, and that RecordPrefix + the member we
1126900de1dSZachary Turner     // just wrote should now constitute the entirety of the current "new"
1136900de1dSZachary Turner     // segment.
1146900de1dSZachary Turner     assert(getCurrentSegmentLength() == MemberLength + sizeof(RecordPrefix));
1156900de1dSZachary Turner   }
1166900de1dSZachary Turner 
1176900de1dSZachary Turner   assert(getCurrentSegmentLength() % 4 == 0);
1186900de1dSZachary Turner   assert(getCurrentSegmentLength() <= MaxSegmentLength);
1196900de1dSZachary Turner }
1206900de1dSZachary Turner 
getCurrentSegmentLength() const1216900de1dSZachary Turner uint32_t ContinuationRecordBuilder::getCurrentSegmentLength() const {
1226900de1dSZachary Turner   return SegmentWriter.getOffset() - SegmentOffsets.back();
1236900de1dSZachary Turner }
1246900de1dSZachary Turner 
insertSegmentEnd(uint32_t Offset)1256900de1dSZachary Turner void ContinuationRecordBuilder::insertSegmentEnd(uint32_t Offset) {
1266900de1dSZachary Turner   uint32_t SegmentBegin = SegmentOffsets.back();
127bba7f862SRafael Espindola   (void)SegmentBegin;
1286900de1dSZachary Turner   assert(Offset > SegmentBegin);
1296900de1dSZachary Turner   assert(Offset - SegmentBegin <= MaxSegmentLength);
1306900de1dSZachary Turner 
1316900de1dSZachary Turner   // We need to make space for the continuation record.  For now we can't fill
1326900de1dSZachary Turner   // out the length or the TypeIndex of the back-reference, but we need the
1336900de1dSZachary Turner   // space to at least be there.
1346900de1dSZachary Turner   Buffer.insert(Offset, InjectedSegmentBytes);
1356900de1dSZachary Turner 
1366900de1dSZachary Turner   uint32_t NewSegmentBegin = Offset + ContinuationLength;
1376900de1dSZachary Turner   uint32_t SegmentLength = NewSegmentBegin - SegmentOffsets.back();
138bba7f862SRafael Espindola   (void) SegmentLength;
1396900de1dSZachary Turner 
1406900de1dSZachary Turner   assert(SegmentLength % 4 == 0);
1416900de1dSZachary Turner   assert(SegmentLength <= MaxRecordLength);
1426900de1dSZachary Turner   SegmentOffsets.push_back(NewSegmentBegin);
1436900de1dSZachary Turner 
1446900de1dSZachary Turner   // Seek to the end so that we can keep writing against the new segment.
1456900de1dSZachary Turner   SegmentWriter.setOffset(SegmentWriter.getLength());
1466900de1dSZachary Turner   assert(SegmentWriter.bytesRemaining() == 0);
1476900de1dSZachary Turner }
1486900de1dSZachary Turner 
createSegmentRecord(uint32_t OffBegin,uint32_t OffEnd,std::optional<TypeIndex> RefersTo)1496900de1dSZachary Turner CVType ContinuationRecordBuilder::createSegmentRecord(
15089fab98eSFangrui Song     uint32_t OffBegin, uint32_t OffEnd, std::optional<TypeIndex> RefersTo) {
1516900de1dSZachary Turner   assert(OffEnd - OffBegin <= USHRT_MAX);
1526900de1dSZachary Turner 
1536900de1dSZachary Turner   MutableArrayRef<uint8_t> Data = Buffer.data();
1546900de1dSZachary Turner   Data = Data.slice(OffBegin, OffEnd - OffBegin);
1556900de1dSZachary Turner 
1566900de1dSZachary Turner   // Write the length to the RecordPrefix, making sure it does not include
1576900de1dSZachary Turner   // sizeof(RecordPrefix.Length)
1586900de1dSZachary Turner   RecordPrefix *Prefix = reinterpret_cast<RecordPrefix *>(Data.data());
1596900de1dSZachary Turner   Prefix->RecordLen = Data.size() - sizeof(RecordPrefix::RecordLen);
1606900de1dSZachary Turner 
161e0e687a6SKazu Hirata   if (RefersTo) {
1626900de1dSZachary Turner     auto Continuation = Data.take_back(ContinuationLength);
1636900de1dSZachary Turner     ContinuationRecord *CR =
1646900de1dSZachary Turner         reinterpret_cast<ContinuationRecord *>(Continuation.data());
1656900de1dSZachary Turner     assert(CR->Kind == TypeLeafKind::LF_INDEX);
1666900de1dSZachary Turner     assert(CR->IndexRef == 0xB0C0B0C0);
1676900de1dSZachary Turner     CR->IndexRef = RefersTo->getIndex();
1686900de1dSZachary Turner   }
1696900de1dSZachary Turner 
170e10d0041SReid Kleckner   return CVType(Data);
1716900de1dSZachary Turner }
1726900de1dSZachary Turner 
end(TypeIndex Index)1736900de1dSZachary Turner std::vector<CVType> ContinuationRecordBuilder::end(TypeIndex Index) {
174e10d0041SReid Kleckner   RecordPrefix Prefix(getTypeLeafKind(*Kind));
175e10d0041SReid Kleckner   CVType Type(&Prefix, sizeof(Prefix));
1766900de1dSZachary Turner   cantFail(Mapping.visitTypeEnd(Type));
1776900de1dSZachary Turner 
1786900de1dSZachary Turner   // We're now done, and we have a series of segments each beginning at an
1796900de1dSZachary Turner   // offset specified in the SegmentOffsets array.  We now need to iterate
1806900de1dSZachary Turner   // over each segment and post-process them in the following two ways:
1816900de1dSZachary Turner   // 1) Each top-level record has a RecordPrefix whose type is either
1826900de1dSZachary Turner   //    LF_FIELDLIST or LF_METHODLIST, but the Length field is still 0.
1836900de1dSZachary Turner   //    Those should all be set to the correct length now.
1846900de1dSZachary Turner   // 2) Each continuation record has an IndexRef field which we set to the
1856900de1dSZachary Turner   //    magic value 0xB0C0B0C0.  Now that the caller has told us the TypeIndex
1866900de1dSZachary Turner   //    they want this sequence to start from, we can go through and update
1876900de1dSZachary Turner   //    each one.
1886900de1dSZachary Turner   //
1896900de1dSZachary Turner   // Logically, the sequence of records we've built up looks like this:
1906900de1dSZachary Turner   //
1916900de1dSZachary Turner   // SegmentOffsets[0]:   <Length>                    (Initially: uninitialized)
1926900de1dSZachary Turner   // SegmentOffsets[0]+2: LF_FIELDLIST
1936900de1dSZachary Turner   // SegmentOffsets[0]+4: Member[0]
1946900de1dSZachary Turner   // SegmentOffsets[0]+?: ...
1956900de1dSZachary Turner   // SegmentOffsets[0]+?: Member[4]
1966900de1dSZachary Turner   // SegmentOffsets[1]-8: LF_INDEX
1976900de1dSZachary Turner   // SegmentOffsets[1]-6: 0
1986900de1dSZachary Turner   // SegmentOffsets[1]-4: <Type Index of Next Record> (Initially: 0xB0C0B0C0)
1996900de1dSZachary Turner   //
2006900de1dSZachary Turner   // SegmentOffsets[1]:   <Length>                    (Initially: uninitialized)
2016900de1dSZachary Turner   // SegmentOffsets[1]+2: LF_FIELDLIST
2026900de1dSZachary Turner   // SegmentOffsets[1]+4: Member[0]
2036900de1dSZachary Turner   // SegmentOffsets[1]+?: ...
2046900de1dSZachary Turner   // SegmentOffsets[1]+?: Member[s]
2056900de1dSZachary Turner   // SegmentOffsets[2]-8: LF_INDEX
2066900de1dSZachary Turner   // SegmentOffsets[2]-6: 0
2076900de1dSZachary Turner   // SegmentOffsets[2]-4: <Type Index of Next Record> (Initially: 0xB0C0B0C0)
2086900de1dSZachary Turner   //
2096900de1dSZachary Turner   // ...
2106900de1dSZachary Turner   //
2116900de1dSZachary Turner   // SegmentOffsets[N]:   <Length>                    (Initially: uninitialized)
2126900de1dSZachary Turner   // SegmentOffsets[N]+2: LF_FIELDLIST
2136900de1dSZachary Turner   // SegmentOffsets[N]+4: Member[0]
2146900de1dSZachary Turner   // SegmentOffsets[N]+?: ...
2156900de1dSZachary Turner   // SegmentOffsets[N]+?: Member[t]
2166900de1dSZachary Turner   //
2176900de1dSZachary Turner   // And this is the way we have laid them out in the serialization buffer.  But
2186900de1dSZachary Turner   // we cannot actually commit them to the underlying stream this way, due to
2196900de1dSZachary Turner   // the topological sorting requirement of a type stream (specifically,
2206900de1dSZachary Turner   // TypeIndex references can only point backwards, not forwards).  So the
2216900de1dSZachary Turner   // sequence that we return to the caller contains the records in reverse
2226900de1dSZachary Turner   // order, which is the proper order for committing the serialized records.
2236900de1dSZachary Turner 
2246900de1dSZachary Turner   std::vector<CVType> Types;
2256900de1dSZachary Turner   Types.reserve(SegmentOffsets.size());
2266900de1dSZachary Turner 
227*38818b60Sserge-sans-paille   ArrayRef SO = SegmentOffsets;
2286900de1dSZachary Turner 
2296900de1dSZachary Turner   uint32_t End = SegmentWriter.getOffset();
2306900de1dSZachary Turner 
23189fab98eSFangrui Song   std::optional<TypeIndex> RefersTo;
2326900de1dSZachary Turner   for (uint32_t Offset : reverse(SO)) {
2336900de1dSZachary Turner     Types.push_back(createSegmentRecord(Offset, End, RefersTo));
2346900de1dSZachary Turner 
2356900de1dSZachary Turner     End = Offset;
2366900de1dSZachary Turner     RefersTo = Index++;
2376900de1dSZachary Turner   }
2386900de1dSZachary Turner 
2396900de1dSZachary Turner   Kind.reset();
2406900de1dSZachary Turner   return Types;
2416900de1dSZachary Turner }
2426900de1dSZachary Turner 
2436900de1dSZachary Turner // Explicitly instantiate the member function for each known type so that we can
2446900de1dSZachary Turner // implement this in the cpp file.
2456900de1dSZachary Turner #define TYPE_RECORD(EnumName, EnumVal, Name)
2466900de1dSZachary Turner #define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
2476900de1dSZachary Turner #define MEMBER_RECORD(EnumName, EnumVal, Name)                                 \
2486900de1dSZachary Turner   template void llvm::codeview::ContinuationRecordBuilder::writeMemberType(    \
2496900de1dSZachary Turner       Name##Record &Record);
2506900de1dSZachary Turner #define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
2516900de1dSZachary Turner #include "llvm/DebugInfo/CodeView/CodeViewTypes.def"
252