brintos

brintos / llvm-project-archived public Read only

0
0
Text · 9.2 KiB · 3761263 Raw
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