252 lines
		
	
	
		
			9.2 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			252 lines
		
	
	
		
			9.2 KiB
		
	
	
	
		
			C++
		
	
	
	
| #include "llvm/DebugInfo/CodeView/ContinuationRecordBuilder.h"
 | |
| 
 | |
| using namespace llvm;
 | |
| using namespace llvm::codeview;
 | |
| 
 | |
| namespace {
 | |
| struct ContinuationRecord {
 | |
|   ulittle16_t Kind{uint16_t(TypeLeafKind::LF_INDEX)};
 | |
|   ulittle16_t Size{0};
 | |
|   ulittle32_t IndexRef{0xB0C0B0C0};
 | |
| };
 | |
| 
 | |
| struct SegmentInjection {
 | |
|   SegmentInjection(TypeLeafKind Kind) { Prefix.RecordKind = Kind; }
 | |
| 
 | |
|   ContinuationRecord Cont;
 | |
|   RecordPrefix Prefix;
 | |
| };
 | |
| } // namespace
 | |
| 
 | |
| static void addPadding(BinaryStreamWriter &Writer) {
 | |
|   uint32_t Align = Writer.getOffset() % 4;
 | |
|   if (Align == 0)
 | |
|     return;
 | |
| 
 | |
|   int PaddingBytes = 4 - Align;
 | |
|   while (PaddingBytes > 0) {
 | |
|     uint8_t Pad = static_cast<uint8_t>(LF_PAD0 + PaddingBytes);
 | |
|     cantFail(Writer.writeInteger(Pad));
 | |
|     --PaddingBytes;
 | |
|   }
 | |
| }
 | |
| 
 | |
| static SegmentInjection InjectFieldList(TypeLeafKind::LF_FIELDLIST);
 | |
| static SegmentInjection InjectMethodOverloadList(TypeLeafKind::LF_METHODLIST);
 | |
| 
 | |
| static constexpr uint32_t ContinuationLength = sizeof(ContinuationRecord);
 | |
| static constexpr uint32_t MaxSegmentLength =
 | |
|     MaxRecordLength - ContinuationLength;
 | |
| 
 | |
| static inline TypeLeafKind getTypeLeafKind(ContinuationRecordKind CK) {
 | |
|   return (CK == ContinuationRecordKind::FieldList) ? LF_FIELDLIST
 | |
|                                                    : LF_METHODLIST;
 | |
| }
 | |
| 
 | |
| ContinuationRecordBuilder::ContinuationRecordBuilder()
 | |
|     : SegmentWriter(Buffer), Mapping(SegmentWriter) {}
 | |
| 
 | |
| ContinuationRecordBuilder::~ContinuationRecordBuilder() {}
 | |
| 
 | |
| void ContinuationRecordBuilder::begin(ContinuationRecordKind RecordKind) {
 | |
|   assert(!Kind.hasValue());
 | |
|   Kind = RecordKind;
 | |
|   Buffer.clear();
 | |
|   SegmentWriter.setOffset(0);
 | |
|   SegmentOffsets.clear();
 | |
|   SegmentOffsets.push_back(0);
 | |
|   assert(SegmentWriter.getOffset() == 0);
 | |
|   assert(SegmentWriter.getLength() == 0);
 | |
| 
 | |
|   const SegmentInjection *FLI =
 | |
|       (RecordKind == ContinuationRecordKind::FieldList)
 | |
|           ? &InjectFieldList
 | |
|           : &InjectMethodOverloadList;
 | |
|   const uint8_t *FLIB = reinterpret_cast<const uint8_t *>(FLI);
 | |
|   InjectedSegmentBytes =
 | |
|       ArrayRef<uint8_t>(FLIB, FLIB + sizeof(SegmentInjection));
 | |
| 
 | |
|   // Seed the first record with an appropriate record prefix.
 | |
|   RecordPrefix Prefix(getTypeLeafKind(RecordKind));
 | |
|   CVType Type(&Prefix, sizeof(Prefix));
 | |
|   cantFail(Mapping.visitTypeBegin(Type));
 | |
| 
 | |
|   cantFail(SegmentWriter.writeObject(Prefix));
 | |
| }
 | |
| 
 | |
| template <typename RecordType>
 | |
| void ContinuationRecordBuilder::writeMemberType(RecordType &Record) {
 | |
|   assert(Kind.hasValue());
 | |
| 
 | |
|   uint32_t OriginalOffset = SegmentWriter.getOffset();
 | |
|   CVMemberRecord CVMR;
 | |
|   CVMR.Kind = static_cast<TypeLeafKind>(Record.getKind());
 | |
| 
 | |
|   // Member Records aren't length-prefixed, they only have a 2-byte TypeLeafKind
 | |
|   // at the beginning.
 | |
|   cantFail(SegmentWriter.writeEnum(CVMR.Kind));
 | |
| 
 | |
|   // Let the Mapping handle the rest.
 | |
|   cantFail(Mapping.visitMemberBegin(CVMR));
 | |
|   cantFail(Mapping.visitKnownMember(CVMR, Record));
 | |
|   cantFail(Mapping.visitMemberEnd(CVMR));
 | |
| 
 | |
|   // Make sure it's padded to 4 bytes.
 | |
|   addPadding(SegmentWriter);
 | |
|   assert(getCurrentSegmentLength() % 4 == 0);
 | |
| 
 | |
|   // The maximum length of a single segment is 64KB minus the size to insert a
 | |
|   // continuation.  So if we are over that, inject a continuation between the
 | |
|   // previous member and the member that was just written, then end the previous
 | |
|   // segment after the continuation and begin a new one with the just-written
 | |
|   // member.
 | |
|   if (getCurrentSegmentLength() > MaxSegmentLength) {
 | |
|     // We need to inject some bytes before the member we just wrote but after
 | |
|     // the previous member.  Save off the length of the member we just wrote so
 | |
|     // that we can do some sanity checking on it.
 | |
|     uint32_t MemberLength = SegmentWriter.getOffset() - OriginalOffset;
 | |
|     (void) MemberLength;
 | |
|     insertSegmentEnd(OriginalOffset);
 | |
|     // Since this member now becomes a new top-level record, it should have
 | |
|     // gotten a RecordPrefix injected, and that RecordPrefix + the member we
 | |
|     // just wrote should now constitute the entirety of the current "new"
 | |
|     // segment.
 | |
|     assert(getCurrentSegmentLength() == MemberLength + sizeof(RecordPrefix));
 | |
|   }
 | |
| 
 | |
|   assert(getCurrentSegmentLength() % 4 == 0);
 | |
|   assert(getCurrentSegmentLength() <= MaxSegmentLength);
 | |
| }
 | |
| 
 | |
| uint32_t ContinuationRecordBuilder::getCurrentSegmentLength() const {
 | |
|   return SegmentWriter.getOffset() - SegmentOffsets.back();
 | |
| }
 | |
| 
 | |
| void ContinuationRecordBuilder::insertSegmentEnd(uint32_t Offset) {
 | |
|   uint32_t SegmentBegin = SegmentOffsets.back();
 | |
|   (void)SegmentBegin;
 | |
|   assert(Offset > SegmentBegin);
 | |
|   assert(Offset - SegmentBegin <= MaxSegmentLength);
 | |
| 
 | |
|   // We need to make space for the continuation record.  For now we can't fill
 | |
|   // out the length or the TypeIndex of the back-reference, but we need the
 | |
|   // space to at least be there.
 | |
|   Buffer.insert(Offset, InjectedSegmentBytes);
 | |
| 
 | |
|   uint32_t NewSegmentBegin = Offset + ContinuationLength;
 | |
|   uint32_t SegmentLength = NewSegmentBegin - SegmentOffsets.back();
 | |
|   (void) SegmentLength;
 | |
| 
 | |
|   assert(SegmentLength % 4 == 0);
 | |
|   assert(SegmentLength <= MaxRecordLength);
 | |
|   SegmentOffsets.push_back(NewSegmentBegin);
 | |
| 
 | |
|   // Seek to the end so that we can keep writing against the new segment.
 | |
|   SegmentWriter.setOffset(SegmentWriter.getLength());
 | |
|   assert(SegmentWriter.bytesRemaining() == 0);
 | |
| }
 | |
| 
 | |
| CVType ContinuationRecordBuilder::createSegmentRecord(
 | |
|     uint32_t OffBegin, uint32_t OffEnd, Optional<TypeIndex> RefersTo) {
 | |
|   assert(OffEnd - OffBegin <= USHRT_MAX);
 | |
| 
 | |
|   MutableArrayRef<uint8_t> Data = Buffer.data();
 | |
|   Data = Data.slice(OffBegin, OffEnd - OffBegin);
 | |
| 
 | |
|   // Write the length to the RecordPrefix, making sure it does not include
 | |
|   // sizeof(RecordPrefix.Length)
 | |
|   RecordPrefix *Prefix = reinterpret_cast<RecordPrefix *>(Data.data());
 | |
|   Prefix->RecordLen = Data.size() - sizeof(RecordPrefix::RecordLen);
 | |
| 
 | |
|   if (RefersTo.hasValue()) {
 | |
|     auto Continuation = Data.take_back(ContinuationLength);
 | |
|     ContinuationRecord *CR =
 | |
|         reinterpret_cast<ContinuationRecord *>(Continuation.data());
 | |
|     assert(CR->Kind == TypeLeafKind::LF_INDEX);
 | |
|     assert(CR->IndexRef == 0xB0C0B0C0);
 | |
|     CR->IndexRef = RefersTo->getIndex();
 | |
|   }
 | |
| 
 | |
|   return CVType(Data);
 | |
| }
 | |
| 
 | |
| std::vector<CVType> ContinuationRecordBuilder::end(TypeIndex Index) {
 | |
|   RecordPrefix Prefix(getTypeLeafKind(*Kind));
 | |
|   CVType Type(&Prefix, sizeof(Prefix));
 | |
|   cantFail(Mapping.visitTypeEnd(Type));
 | |
| 
 | |
|   // We're now done, and we have a series of segments each beginning at an
 | |
|   // offset specified in the SegmentOffsets array.  We now need to iterate
 | |
|   // over each segment and post-process them in the following two ways:
 | |
|   // 1) Each top-level record has a RecordPrefix whose type is either
 | |
|   //    LF_FIELDLIST or LF_METHODLIST, but the Length field is still 0.
 | |
|   //    Those should all be set to the correct length now.
 | |
|   // 2) Each continuation record has an IndexRef field which we set to the
 | |
|   //    magic value 0xB0C0B0C0.  Now that the caller has told us the TypeIndex
 | |
|   //    they want this sequence to start from, we can go through and update
 | |
|   //    each one.
 | |
|   //
 | |
|   // Logically, the sequence of records we've built up looks like this:
 | |
|   //
 | |
|   // SegmentOffsets[0]:   <Length>                    (Initially: uninitialized)
 | |
|   // SegmentOffsets[0]+2: LF_FIELDLIST
 | |
|   // SegmentOffsets[0]+4: Member[0]
 | |
|   // SegmentOffsets[0]+?: ...
 | |
|   // SegmentOffsets[0]+?: Member[4]
 | |
|   // SegmentOffsets[1]-8: LF_INDEX
 | |
|   // SegmentOffsets[1]-6: 0
 | |
|   // SegmentOffsets[1]-4: <Type Index of Next Record> (Initially: 0xB0C0B0C0)
 | |
|   //
 | |
|   // SegmentOffsets[1]:   <Length>                    (Initially: uninitialized)
 | |
|   // SegmentOffsets[1]+2: LF_FIELDLIST
 | |
|   // SegmentOffsets[1]+4: Member[0]
 | |
|   // SegmentOffsets[1]+?: ...
 | |
|   // SegmentOffsets[1]+?: Member[s]
 | |
|   // SegmentOffsets[2]-8: LF_INDEX
 | |
|   // SegmentOffsets[2]-6: 0
 | |
|   // SegmentOffsets[2]-4: <Type Index of Next Record> (Initially: 0xB0C0B0C0)
 | |
|   //
 | |
|   // ...
 | |
|   //
 | |
|   // SegmentOffsets[N]:   <Length>                    (Initially: uninitialized)
 | |
|   // SegmentOffsets[N]+2: LF_FIELDLIST
 | |
|   // SegmentOffsets[N]+4: Member[0]
 | |
|   // SegmentOffsets[N]+?: ...
 | |
|   // SegmentOffsets[N]+?: Member[t]
 | |
|   //
 | |
|   // And this is the way we have laid them out in the serialization buffer.  But
 | |
|   // we cannot actually commit them to the underlying stream this way, due to
 | |
|   // the topological sorting requirement of a type stream (specifically,
 | |
|   // TypeIndex references can only point backwards, not forwards).  So the
 | |
|   // sequence that we return to the caller contains the records in reverse
 | |
|   // order, which is the proper order for committing the serialized records.
 | |
| 
 | |
|   std::vector<CVType> Types;
 | |
|   Types.reserve(SegmentOffsets.size());
 | |
| 
 | |
|   auto SO = makeArrayRef(SegmentOffsets);
 | |
| 
 | |
|   uint32_t End = SegmentWriter.getOffset();
 | |
| 
 | |
|   Optional<TypeIndex> RefersTo;
 | |
|   for (uint32_t Offset : reverse(SO)) {
 | |
|     Types.push_back(createSegmentRecord(Offset, End, RefersTo));
 | |
| 
 | |
|     End = Offset;
 | |
|     RefersTo = Index++;
 | |
|   }
 | |
| 
 | |
|   Kind.reset();
 | |
|   return Types;
 | |
| }
 | |
| 
 | |
| // Explicitly instantiate the member function for each known type so that we can
 | |
| // implement this in the cpp file.
 | |
| #define TYPE_RECORD(EnumName, EnumVal, Name)
 | |
| #define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
 | |
| #define MEMBER_RECORD(EnumName, EnumVal, Name)                                 \
 | |
|   template void llvm::codeview::ContinuationRecordBuilder::writeMemberType(    \
 | |
|       Name##Record &Record);
 | |
| #define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
 | |
| #include "llvm/DebugInfo/CodeView/CodeViewTypes.def"
 |