1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251
|
#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"
|