| //===- TypeRecordMapping.cpp ------------------------------------*- C++ -*-===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "llvm/DebugInfo/CodeView/TypeRecordMapping.h" |
| |
| using namespace llvm; |
| using namespace llvm::codeview; |
| |
| #define error(X) \ |
| if (auto EC = X) \ |
| return EC; |
| |
| namespace { |
| struct MapOneMethodRecord { |
| explicit MapOneMethodRecord(bool IsFromOverloadList) |
| : IsFromOverloadList(IsFromOverloadList) {} |
| |
| Error operator()(CodeViewRecordIO &IO, OneMethodRecord &Method) const { |
| error(IO.mapInteger(Method.Attrs.Attrs)); |
| if (IsFromOverloadList) { |
| uint16_t Padding = 0; |
| error(IO.mapInteger(Padding)); |
| } |
| error(IO.mapInteger(Method.Type)); |
| if (Method.isIntroducingVirtual()) { |
| error(IO.mapInteger(Method.VFTableOffset)); |
| } else if (!IO.isWriting()) |
| Method.VFTableOffset = -1; |
| |
| if (!IsFromOverloadList) |
| error(IO.mapStringZ(Method.Name)); |
| |
| return Error::success(); |
| } |
| |
| private: |
| bool IsFromOverloadList; |
| }; |
| } |
| |
| static Error mapNameAndUniqueName(CodeViewRecordIO &IO, StringRef &Name, |
| StringRef &UniqueName, bool HasUniqueName) { |
| if (IO.isWriting()) { |
| // Try to be smart about what we write here. We can't write anything too |
| // large, so if we're going to go over the limit, truncate both the name |
| // and unique name by the same amount. |
| size_t BytesLeft = IO.maxFieldLength(); |
| if (HasUniqueName) { |
| size_t BytesNeeded = Name.size() + UniqueName.size() + 2; |
| StringRef N = Name; |
| StringRef U = UniqueName; |
| if (BytesNeeded > BytesLeft) { |
| size_t BytesToDrop = (BytesNeeded - BytesLeft); |
| size_t DropN = std::min(N.size(), BytesToDrop / 2); |
| size_t DropU = std::min(U.size(), BytesToDrop - DropN); |
| |
| N = N.drop_back(DropN); |
| U = U.drop_back(DropU); |
| } |
| |
| error(IO.mapStringZ(N)); |
| error(IO.mapStringZ(U)); |
| } else { |
| // Cap the length of the string at however many bytes we have available, |
| // plus one for the required null terminator. |
| auto N = StringRef(Name).take_front(BytesLeft - 1); |
| error(IO.mapStringZ(N)); |
| } |
| } else { |
| error(IO.mapStringZ(Name)); |
| if (HasUniqueName) |
| error(IO.mapStringZ(UniqueName)); |
| } |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitTypeBegin(CVType &CVR) { |
| assert(!TypeKind.hasValue() && "Already in a type mapping!"); |
| assert(!MemberKind.hasValue() && "Already in a member mapping!"); |
| |
| // FieldList and MethodList records can be any length because they can be |
| // split with continuation records. All other record types cannot be |
| // longer than the maximum record length. |
| Optional<uint32_t> MaxLen; |
| if (CVR.kind() != TypeLeafKind::LF_FIELDLIST && |
| CVR.kind() != TypeLeafKind::LF_METHODLIST) |
| MaxLen = MaxRecordLength - sizeof(RecordPrefix); |
| error(IO.beginRecord(MaxLen)); |
| TypeKind = CVR.kind(); |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitTypeEnd(CVType &Record) { |
| assert(TypeKind.hasValue() && "Not in a type mapping!"); |
| assert(!MemberKind.hasValue() && "Still in a member mapping!"); |
| |
| error(IO.endRecord()); |
| |
| TypeKind.reset(); |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitMemberBegin(CVMemberRecord &Record) { |
| assert(TypeKind.hasValue() && "Not in a type mapping!"); |
| assert(!MemberKind.hasValue() && "Already in a member mapping!"); |
| |
| // The largest possible subrecord is one in which there is a record prefix, |
| // followed by the subrecord, followed by a continuation, and that entire |
| // sequence spaws `MaxRecordLength` bytes. So the record's length is |
| // calculated as follows. |
| constexpr uint32_t ContinuationLength = 8; |
| error(IO.beginRecord(MaxRecordLength - sizeof(RecordPrefix) - |
| ContinuationLength)); |
| |
| MemberKind = Record.Kind; |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitMemberEnd(CVMemberRecord &Record) { |
| assert(TypeKind.hasValue() && "Not in a type mapping!"); |
| assert(MemberKind.hasValue() && "Not in a member mapping!"); |
| |
| if (!IO.isWriting()) { |
| if (auto EC = IO.skipPadding()) |
| return EC; |
| } |
| |
| MemberKind.reset(); |
| error(IO.endRecord()); |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownRecord(CVType &CVR, ModifierRecord &Record) { |
| error(IO.mapInteger(Record.ModifiedType)); |
| error(IO.mapEnum(Record.Modifiers)); |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownRecord(CVType &CVR, |
| ProcedureRecord &Record) { |
| error(IO.mapInteger(Record.ReturnType)); |
| error(IO.mapEnum(Record.CallConv)); |
| error(IO.mapEnum(Record.Options)); |
| error(IO.mapInteger(Record.ParameterCount)); |
| error(IO.mapInteger(Record.ArgumentList)); |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownRecord(CVType &CVR, |
| MemberFunctionRecord &Record) { |
| error(IO.mapInteger(Record.ReturnType)); |
| error(IO.mapInteger(Record.ClassType)); |
| error(IO.mapInteger(Record.ThisType)); |
| error(IO.mapEnum(Record.CallConv)); |
| error(IO.mapEnum(Record.Options)); |
| error(IO.mapInteger(Record.ParameterCount)); |
| error(IO.mapInteger(Record.ArgumentList)); |
| error(IO.mapInteger(Record.ThisPointerAdjustment)); |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownRecord(CVType &CVR, ArgListRecord &Record) { |
| error(IO.mapVectorN<uint32_t>( |
| Record.ArgIndices, |
| [](CodeViewRecordIO &IO, TypeIndex &N) { return IO.mapInteger(N); })); |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownRecord(CVType &CVR, |
| StringListRecord &Record) { |
| error(IO.mapVectorN<uint32_t>( |
| Record.StringIndices, |
| [](CodeViewRecordIO &IO, TypeIndex &N) { return IO.mapInteger(N); })); |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownRecord(CVType &CVR, PointerRecord &Record) { |
| error(IO.mapInteger(Record.ReferentType)); |
| error(IO.mapInteger(Record.Attrs)); |
| |
| if (Record.isPointerToMember()) { |
| if (!IO.isWriting()) |
| Record.MemberInfo.emplace(); |
| |
| MemberPointerInfo &M = *Record.MemberInfo; |
| error(IO.mapInteger(M.ContainingType)); |
| error(IO.mapEnum(M.Representation)); |
| } |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownRecord(CVType &CVR, ArrayRecord &Record) { |
| error(IO.mapInteger(Record.ElementType)); |
| error(IO.mapInteger(Record.IndexType)); |
| error(IO.mapEncodedInteger(Record.Size)); |
| error(IO.mapStringZ(Record.Name)); |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownRecord(CVType &CVR, ClassRecord &Record) { |
| assert((CVR.kind() == TypeLeafKind::LF_STRUCTURE) || |
| (CVR.kind() == TypeLeafKind::LF_CLASS) || |
| (CVR.kind() == TypeLeafKind::LF_INTERFACE)); |
| |
| error(IO.mapInteger(Record.MemberCount)); |
| error(IO.mapEnum(Record.Options)); |
| error(IO.mapInteger(Record.FieldList)); |
| error(IO.mapInteger(Record.DerivationList)); |
| error(IO.mapInteger(Record.VTableShape)); |
| error(IO.mapEncodedInteger(Record.Size)); |
| error(mapNameAndUniqueName(IO, Record.Name, Record.UniqueName, |
| Record.hasUniqueName())); |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownRecord(CVType &CVR, UnionRecord &Record) { |
| error(IO.mapInteger(Record.MemberCount)); |
| error(IO.mapEnum(Record.Options)); |
| error(IO.mapInteger(Record.FieldList)); |
| error(IO.mapEncodedInteger(Record.Size)); |
| error(mapNameAndUniqueName(IO, Record.Name, Record.UniqueName, |
| Record.hasUniqueName())); |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownRecord(CVType &CVR, EnumRecord &Record) { |
| error(IO.mapInteger(Record.MemberCount)); |
| error(IO.mapEnum(Record.Options)); |
| error(IO.mapInteger(Record.UnderlyingType)); |
| error(IO.mapInteger(Record.FieldList)); |
| error(mapNameAndUniqueName(IO, Record.Name, Record.UniqueName, |
| Record.hasUniqueName())); |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownRecord(CVType &CVR, BitFieldRecord &Record) { |
| error(IO.mapInteger(Record.Type)); |
| error(IO.mapInteger(Record.BitSize)); |
| error(IO.mapInteger(Record.BitOffset)); |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownRecord(CVType &CVR, |
| VFTableShapeRecord &Record) { |
| uint16_t Size; |
| if (IO.isWriting()) { |
| ArrayRef<VFTableSlotKind> Slots = Record.getSlots(); |
| Size = Slots.size(); |
| error(IO.mapInteger(Size)); |
| |
| for (size_t SlotIndex = 0; SlotIndex < Slots.size(); SlotIndex += 2) { |
| uint8_t Byte = static_cast<uint8_t>(Slots[SlotIndex]) << 4; |
| if ((SlotIndex + 1) < Slots.size()) { |
| Byte |= static_cast<uint8_t>(Slots[SlotIndex + 1]); |
| } |
| error(IO.mapInteger(Byte)); |
| } |
| } else { |
| error(IO.mapInteger(Size)); |
| for (uint16_t I = 0; I < Size; I += 2) { |
| uint8_t Byte; |
| error(IO.mapInteger(Byte)); |
| Record.Slots.push_back(static_cast<VFTableSlotKind>(Byte & 0xF)); |
| if ((I + 1) < Size) |
| Record.Slots.push_back(static_cast<VFTableSlotKind>(Byte >> 4)); |
| } |
| } |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownRecord(CVType &CVR, VFTableRecord &Record) { |
| error(IO.mapInteger(Record.CompleteClass)); |
| error(IO.mapInteger(Record.OverriddenVFTable)); |
| error(IO.mapInteger(Record.VFPtrOffset)); |
| uint32_t NamesLen = 0; |
| if (IO.isWriting()) { |
| for (auto Name : Record.MethodNames) |
| NamesLen += Name.size() + 1; |
| } |
| error(IO.mapInteger(NamesLen)); |
| error(IO.mapVectorTail( |
| Record.MethodNames, |
| [](CodeViewRecordIO &IO, StringRef &S) { return IO.mapStringZ(S); })); |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownRecord(CVType &CVR, StringIdRecord &Record) { |
| error(IO.mapInteger(Record.Id)); |
| error(IO.mapStringZ(Record.String)); |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownRecord(CVType &CVR, |
| UdtSourceLineRecord &Record) { |
| error(IO.mapInteger(Record.UDT)); |
| error(IO.mapInteger(Record.SourceFile)); |
| error(IO.mapInteger(Record.LineNumber)); |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownRecord(CVType &CVR, |
| UdtModSourceLineRecord &Record) { |
| error(IO.mapInteger(Record.UDT)); |
| error(IO.mapInteger(Record.SourceFile)); |
| error(IO.mapInteger(Record.LineNumber)); |
| error(IO.mapInteger(Record.Module)); |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownRecord(CVType &CVR, FuncIdRecord &Record) { |
| error(IO.mapInteger(Record.ParentScope)); |
| error(IO.mapInteger(Record.FunctionType)); |
| error(IO.mapStringZ(Record.Name)); |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownRecord(CVType &CVR, |
| MemberFuncIdRecord &Record) { |
| error(IO.mapInteger(Record.ClassType)); |
| error(IO.mapInteger(Record.FunctionType)); |
| error(IO.mapStringZ(Record.Name)); |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownRecord(CVType &CVR, |
| BuildInfoRecord &Record) { |
| error(IO.mapVectorN<uint16_t>( |
| Record.ArgIndices, |
| [](CodeViewRecordIO &IO, TypeIndex &N) { return IO.mapInteger(N); })); |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownRecord(CVType &CVR, |
| MethodOverloadListRecord &Record) { |
| // TODO: Split the list into multiple records if it's longer than 64KB, using |
| // a subrecord of TypeRecordKind::Index to chain the records together. |
| error(IO.mapVectorTail(Record.Methods, MapOneMethodRecord(true))); |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownRecord(CVType &CVR, |
| FieldListRecord &Record) { |
| error(IO.mapByteVectorTail(Record.Data)); |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownRecord(CVType &CVR, |
| TypeServer2Record &Record) { |
| error(IO.mapGuid(Record.Guid)); |
| error(IO.mapInteger(Record.Age)); |
| error(IO.mapStringZ(Record.Name)); |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownRecord(CVType &CVR, LabelRecord &Record) { |
| error(IO.mapEnum(Record.Mode)); |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, |
| BaseClassRecord &Record) { |
| error(IO.mapInteger(Record.Attrs.Attrs)); |
| error(IO.mapInteger(Record.Type)); |
| error(IO.mapEncodedInteger(Record.Offset)); |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, |
| EnumeratorRecord &Record) { |
| error(IO.mapInteger(Record.Attrs.Attrs)); |
| |
| // FIXME: Handle full APInt such as __int128. |
| error(IO.mapEncodedInteger(Record.Value)); |
| error(IO.mapStringZ(Record.Name)); |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, |
| DataMemberRecord &Record) { |
| error(IO.mapInteger(Record.Attrs.Attrs)); |
| error(IO.mapInteger(Record.Type)); |
| error(IO.mapEncodedInteger(Record.FieldOffset)); |
| error(IO.mapStringZ(Record.Name)); |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, |
| OverloadedMethodRecord &Record) { |
| error(IO.mapInteger(Record.NumOverloads)); |
| error(IO.mapInteger(Record.MethodList)); |
| error(IO.mapStringZ(Record.Name)); |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, |
| OneMethodRecord &Record) { |
| const bool IsFromOverloadList = (TypeKind == LF_METHODLIST); |
| MapOneMethodRecord Mapper(IsFromOverloadList); |
| return Mapper(IO, Record); |
| } |
| |
| Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, |
| NestedTypeRecord &Record) { |
| uint16_t Padding = 0; |
| error(IO.mapInteger(Padding)); |
| error(IO.mapInteger(Record.Type)); |
| error(IO.mapStringZ(Record.Name)); |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, |
| StaticDataMemberRecord &Record) { |
| |
| error(IO.mapInteger(Record.Attrs.Attrs)); |
| error(IO.mapInteger(Record.Type)); |
| error(IO.mapStringZ(Record.Name)); |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, |
| VirtualBaseClassRecord &Record) { |
| |
| error(IO.mapInteger(Record.Attrs.Attrs)); |
| error(IO.mapInteger(Record.BaseType)); |
| error(IO.mapInteger(Record.VBPtrType)); |
| error(IO.mapEncodedInteger(Record.VBPtrOffset)); |
| error(IO.mapEncodedInteger(Record.VTableIndex)); |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, |
| VFPtrRecord &Record) { |
| uint16_t Padding = 0; |
| error(IO.mapInteger(Padding)); |
| error(IO.mapInteger(Record.Type)); |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR, |
| ListContinuationRecord &Record) { |
| uint16_t Padding = 0; |
| error(IO.mapInteger(Padding)); |
| error(IO.mapInteger(Record.ContinuationIndex)); |
| |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownRecord(CVType &CVR, |
| PrecompRecord &Precomp) { |
| error(IO.mapInteger(Precomp.StartTypeIndex)); |
| error(IO.mapInteger(Precomp.TypesCount)); |
| error(IO.mapInteger(Precomp.Signature)); |
| error(IO.mapStringZ(Precomp.PrecompFilePath)); |
| return Error::success(); |
| } |
| |
| Error TypeRecordMapping::visitKnownRecord(CVType &CVR, |
| EndPrecompRecord &EndPrecomp) { |
| error(IO.mapInteger(EndPrecomp.Signature)); |
| return Error::success(); |
| } |