diff options
22 files changed, 820 insertions, 135 deletions
diff --git a/llvm/include/llvm/DebugInfo/CodeView/CVTypeVisitor.h b/llvm/include/llvm/DebugInfo/CodeView/CVTypeVisitor.h index 082ae72aaf4..7641ae6091b 100644 --- a/llvm/include/llvm/DebugInfo/CodeView/CVTypeVisitor.h +++ b/llvm/include/llvm/DebugInfo/CodeView/CVTypeVisitor.h @@ -14,6 +14,7 @@ #include "llvm/DebugInfo/CodeView/RecordIterator.h" #include "llvm/DebugInfo/CodeView/TypeIndex.h" #include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/DebugInfo/CodeView/TypeStream.h" #include "llvm/Support/ErrorOr.h" namespace llvm { @@ -110,6 +111,7 @@ public: /// Visits individual member records of a field list record. Member records do /// not describe their own length, and need special handling. void visitFieldList(TypeLeafKind Leaf, ArrayRef<uint8_t> FieldData) { + auto *DerivedThis = static_cast<Derived *>(this); while (!FieldData.empty()) { const ulittle16_t *LeafPtr; if (!CVTypeVisitor::consumeObject(FieldData, LeafPtr)) @@ -119,7 +121,7 @@ public: default: // Field list records do not describe their own length, so we cannot // continue parsing past an unknown member type. - visitUnknownMember(Leaf); + DerivedThis->visitUnknownMember(Leaf); return parseError(); #define MEMBER_RECORD(EnumName, EnumVal, Name) \ case EnumName: { \ @@ -127,7 +129,7 @@ public: auto Result = Name##Record::deserialize(RK, FieldData); \ if (Result.getError()) \ return parseError(); \ - static_cast<Derived *>(this)->visit##Name(Leaf, *Result); \ + DerivedThis->visit##Name(Leaf, *Result); \ break; \ } #define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) \ diff --git a/llvm/include/llvm/DebugInfo/CodeView/FieldListRecordBuilder.h b/llvm/include/llvm/DebugInfo/CodeView/FieldListRecordBuilder.h index 1ed62487aec..abe385a2d48 100644 --- a/llvm/include/llvm/DebugInfo/CodeView/FieldListRecordBuilder.h +++ b/llvm/include/llvm/DebugInfo/CodeView/FieldListRecordBuilder.h @@ -11,6 +11,7 @@ #define LLVM_DEBUGINFO_CODEVIEW_FIELDLISTRECORDBUILDER_H #include "llvm/DebugInfo/CodeView/ListRecordBuilder.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" namespace llvm { namespace codeview { @@ -46,31 +47,15 @@ private: public: FieldListRecordBuilder(); - void writeBaseClass(MemberAccess Access, TypeIndex Type, uint64_t Offset); - void writeEnumerate(MemberAccess Access, uint64_t Value, StringRef Name); - void writeIndirectVirtualBaseClass(MemberAccess Access, TypeIndex Type, - TypeIndex VirtualBasePointerType, - int64_t VirtualBasePointerOffset, - uint64_t SlotIndex); - void writeMember(MemberAccess Access, TypeIndex Type, uint64_t Offset, - StringRef Name); - void writeOneMethod(MemberAccess Access, MethodKind Kind, - MethodOptions Options, TypeIndex Type, - int32_t VTableSlotOffset, StringRef Name); - void writeOneMethod(const MethodInfo &Method, StringRef Name); - void writeMethod(uint16_t OverloadCount, TypeIndex MethodList, - StringRef Name); - void writeNestedType(TypeIndex Type, StringRef Name); - void writeStaticMember(MemberAccess Access, TypeIndex Type, StringRef Name); - void writeVirtualBaseClass(MemberAccess Access, TypeIndex Type, - TypeIndex VirtualBasePointerType, - int64_t VirtualBasePointerOffset, - uint64_t SlotIndex); - void writeVirtualBaseClass(TypeRecordKind Kind, MemberAccess Access, - TypeIndex Type, TypeIndex VirtualBasePointerType, - int64_t VirtualBasePointerOffset, - uint64_t SlotIndex); - void writeVirtualFunctionTablePointer(TypeIndex Type); + void writeBaseClass(const BaseClassRecord &Record); + void writeEnumerator(const EnumeratorRecord &Record); + void writeDataMember(const DataMemberRecord &Record); + void writeOneMethod(const OneMethodRecord &Record); + void writeOverloadedMethod(const OverloadedMethodRecord &Record); + void writeNestedType(const NestedTypeRecord &Record); + void writeStaticDataMember(const StaticDataMemberRecord &Record); + void writeVirtualBaseClass(const VirtualBaseClassRecord &Record); + void writeVFPtr(const VFPtrRecord &Type); }; } } diff --git a/llvm/include/llvm/DebugInfo/CodeView/ListRecordBuilder.h b/llvm/include/llvm/DebugInfo/CodeView/ListRecordBuilder.h index df0a2e08a41..2467bfd2c29 100644 --- a/llvm/include/llvm/DebugInfo/CodeView/ListRecordBuilder.h +++ b/llvm/include/llvm/DebugInfo/CodeView/ListRecordBuilder.h @@ -28,6 +28,8 @@ protected: public: llvm::StringRef str() { return Builder.str(); } + void reset() { Builder.reset(); } + protected: void finishSubRecord(); diff --git a/llvm/include/llvm/DebugInfo/CodeView/MemoryTypeTableBuilder.h b/llvm/include/llvm/DebugInfo/CodeView/MemoryTypeTableBuilder.h index 0fceac7b436..5a3015c3eea 100644 --- a/llvm/include/llvm/DebugInfo/CodeView/MemoryTypeTableBuilder.h +++ b/llvm/include/llvm/DebugInfo/CodeView/MemoryTypeTableBuilder.h @@ -55,7 +55,7 @@ public: } } -private: +protected: TypeIndex writeRecord(llvm::StringRef Data) override; private: diff --git a/llvm/include/llvm/DebugInfo/CodeView/TypeRecord.h b/llvm/include/llvm/DebugInfo/CodeView/TypeRecord.h index e31ab48cc20..b192ae79314 100644 --- a/llvm/include/llvm/DebugInfo/CodeView/TypeRecord.h +++ b/llvm/include/llvm/DebugInfo/CodeView/TypeRecord.h @@ -30,6 +30,9 @@ using llvm::support::ulittle32_t; /// Equvalent to CV_fldattr_t in cvinfo.h. struct MemberAttributes { ulittle16_t Attrs; + enum { + MethodKindShift = 2, + }; /// Get the access specifier. Valid for any kind of member. MemberAccess getAccess() const { @@ -39,7 +42,8 @@ struct MemberAttributes { /// Indicates if a method is defined with friend, virtual, static, etc. MethodKind getMethodKind() const { return MethodKind( - (unsigned(Attrs) & unsigned(MethodOptions::MethodKindMask)) >> 2); + (unsigned(Attrs) & unsigned(MethodOptions::MethodKindMask)) >> + MethodKindShift); } /// Get the flags that are not included in access control or method @@ -75,6 +79,10 @@ public: PointerToMemberRepresentation Representation) : ContainingType(ContainingType), Representation(Representation) {} + /// Rewrite member type indices with IndexMap. Returns false if a type index + /// is not in the map. + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); + static ErrorOr<MemberPointerInfo> deserialize(ArrayRef<uint8_t> &Data) { const Layout *L = nullptr; if (auto EC = consumeObject(Data, L)) @@ -120,6 +128,10 @@ public: : TypeRecord(TypeRecordKind::Modifier), ModifiedType(ModifiedType), Modifiers(Modifiers) {} + /// Rewrite member type indices with IndexMap. Returns false if a type index + /// is not in the map. + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); + static ErrorOr<ModifierRecord> deserialize(TypeRecordKind Kind, ArrayRef<uint8_t> &Data) { const Layout *L = nullptr; @@ -155,6 +167,10 @@ public: CallConv(CallConv), Options(Options), ParameterCount(ParameterCount), ArgumentList(ArgumentList) {} + /// Rewrite member type indices with IndexMap. Returns false if a type index + /// is not in the map. + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); + static ErrorOr<ProcedureRecord> deserialize(TypeRecordKind Kind, ArrayRef<uint8_t> &Data) { const Layout *L = nullptr; @@ -202,6 +218,10 @@ public: ArgumentList(ArgumentList), ThisPointerAdjustment(ThisPointerAdjustment) {} + /// Rewrite member type indices with IndexMap. Returns false if a type index + /// is not in the map. + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); + static ErrorOr<MemberFunctionRecord> deserialize(TypeRecordKind Kind, ArrayRef<uint8_t> &Data) { const Layout *L = nullptr; @@ -251,8 +271,12 @@ public: : TypeRecord(TypeRecordKind::MemberFuncId), ClassType(ClassType), FunctionType(FunctionType), Name(Name) {} + /// Rewrite member type indices with IndexMap. Returns false if a type index + /// is not in the map. + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); + static ErrorOr<MemberFuncIdRecord> deserialize(TypeRecordKind Kind, - ArrayRef<uint8_t> &Data) { + ArrayRef<uint8_t> &Data) { const Layout *L = nullptr; StringRef Name; CV_DESERIALIZE(Data, L, Name); @@ -281,6 +305,10 @@ public: ArgListRecord(TypeRecordKind Kind, ArrayRef<TypeIndex> Indices) : TypeRecord(Kind), StringIndices(Indices) {} + /// Rewrite member type indices with IndexMap. Returns false if a type index + /// is not in the map. + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); + static ErrorOr<ArgListRecord> deserialize(TypeRecordKind Kind, ArrayRef<uint8_t> &Data) { if (Kind != TypeRecordKind::StringList && Kind != TypeRecordKind::ArgList) @@ -303,7 +331,7 @@ private: // ArgTypes[]: Type indicies of arguments }; - ArrayRef<TypeIndex> StringIndices; + std::vector<TypeIndex> StringIndices; }; // LF_POINTER @@ -330,6 +358,10 @@ public: PtrKind(Kind), Mode(Mode), Options(Options), Size(Size), MemberInfo(Member) {} + /// Rewrite member type indices with IndexMap. Returns false if a type index + /// is not in the map. + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); + static ErrorOr<PointerRecord> deserialize(TypeRecordKind Kind, ArrayRef<uint8_t> &Data) { const Layout *L = nullptr; @@ -421,6 +453,10 @@ public: NestedTypeRecord(TypeIndex Type, StringRef Name) : TypeRecord(TypeRecordKind::NestedType), Type(Type), Name(Name) {} + /// Rewrite member type indices with IndexMap. Returns false if a type index + /// is not in the map. + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); + static ErrorOr<NestedTypeRecord> deserialize(TypeRecordKind Kind, ArrayRef<uint8_t> &Data) { const Layout *L = nullptr; @@ -452,6 +488,10 @@ public: : TypeRecord(TypeRecordKind::Array), ElementType(ElementType), IndexType(IndexType), Size(Size), Name(Name) {} + /// Rewrite member type indices with IndexMap. Returns false if a type index + /// is not in the map. + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); + static ErrorOr<ArrayRecord> deserialize(TypeRecordKind Kind, ArrayRef<uint8_t> &Data) { const Layout *L = nullptr; @@ -489,6 +529,10 @@ protected: FieldList(FieldList), Name(Name), UniqueName(UniqueName) {} public: + /// Rewrite member type indices with IndexMap. Returns false if a type index + /// is not in the map. + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); + static const int HfaKindShift = 11; static const int HfaKindMask = 0x1800; static const int WinRTKindShift = 14; @@ -519,6 +563,10 @@ public: Hfa(Hfa), WinRTKind(WinRTKind), DerivationList(DerivationList), VTableShape(VTableShape), Size(Size) {} + /// Rewrite member type indices with IndexMap. Returns false if a type index + /// is not in the map. + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); + static ErrorOr<ClassRecord> deserialize(TypeRecordKind Kind, ArrayRef<uint8_t> &Data) { uint64_t Size = 0; @@ -629,6 +677,9 @@ public: UniqueName), UnderlyingType(UnderlyingType) {} + /// Rewrite member type indices with IndexMap. Returns false if a type index is not in the map. + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); + static ErrorOr<EnumRecord> deserialize(TypeRecordKind Kind, ArrayRef<uint8_t> &Data) { const Layout *L = nullptr; @@ -679,8 +730,12 @@ public: explicit VFTableShapeRecord(std::vector<VFTableSlotKind> Slots) : TypeRecord(TypeRecordKind::VFTableShape), Slots(Slots) {} + /// Rewrite member type indices with IndexMap. Returns false if a type index + /// is not in the map. + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); + static ErrorOr<VFTableShapeRecord> deserialize(TypeRecordKind Kind, - ArrayRef<uint8_t> &Data) { + ArrayRef<uint8_t> &Data) { const Layout *L = nullptr; if (auto EC = consumeObject(Data, L)) return EC; @@ -733,6 +788,10 @@ public: : TypeRecord(TypeRecordKind::TypeServer2), Guid(Guid), Age(Age), Name(Name) {} + /// Rewrite member type indices with IndexMap. Returns false if a type index + /// is not in the map. + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); + static ErrorOr<TypeServer2Record> deserialize(TypeRecordKind Kind, ArrayRef<uint8_t> &Data) { const Layout *L = nullptr; @@ -766,6 +825,10 @@ public: StringIdRecord(TypeIndex Id, StringRef String) : TypeRecord(TypeRecordKind::StringId), Id(Id), String(String) {} + /// Rewrite member type indices with IndexMap. Returns false if a type index + /// is not in the map. + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); + static ErrorOr<StringIdRecord> deserialize(TypeRecordKind Kind, ArrayRef<uint8_t> &Data) { const Layout *L = nullptr; @@ -796,6 +859,10 @@ public: : TypeRecord(TypeRecordKind::FuncId), ParentScope(ParentScope), FunctionType(FunctionType), Name(Name) {} + /// Rewrite member type indices with IndexMap. Returns false if a type index + /// is not in the map. + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); + static ErrorOr<FuncIdRecord> deserialize(TypeRecordKind Kind, ArrayRef<uint8_t> &Data) { const Layout *L = nullptr; @@ -830,6 +897,10 @@ public: : TypeRecord(TypeRecordKind::UdtSourceLine), UDT(UDT), SourceFile(SourceFile), LineNumber(LineNumber) {} + /// Rewrite member type indices with IndexMap. Returns false if a type index + /// is not in the map. + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); + static ErrorOr<UdtSourceLineRecord> deserialize(TypeRecordKind Kind, ArrayRef<uint8_t> &Data) { const Layout *L = nullptr; @@ -858,7 +929,12 @@ private: class BuildInfoRecord : public TypeRecord { public: BuildInfoRecord(ArrayRef<TypeIndex> ArgIndices) - : TypeRecord(TypeRecordKind::Modifier), ArgIndices(ArgIndices) {} + : TypeRecord(TypeRecordKind::BuildInfo), + ArgIndices(ArgIndices.begin(), ArgIndices.end()) {} + + /// Rewrite member type indices with IndexMap. Returns false if a type index + /// is not in the map. + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); static ErrorOr<BuildInfoRecord> deserialize(TypeRecordKind Kind, ArrayRef<uint8_t> &Data) { @@ -876,7 +952,7 @@ private: ulittle16_t NumArgs; // Number of arguments // ArgTypes[]: Type indicies of arguments }; - ArrayRef<TypeIndex> ArgIndices; + SmallVector<TypeIndex, 4> ArgIndices; }; // LF_VFTABLE @@ -895,6 +971,10 @@ public: CompleteClass(CompleteClass), OverriddenVFTable(OverriddenVFTable), VFPtrOffset(VFPtrOffset), Name(Name), MethodNames(Methods) {} + /// Rewrite member type indices with IndexMap. Returns false if a type index + /// is not in the map. + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); + static ErrorOr<VFTableRecord> deserialize(TypeRecordKind Kind, ArrayRef<uint8_t> &Data) { const Layout *L = nullptr; @@ -943,11 +1023,15 @@ public: Options(Options), Access(Access), VFTableOffset(VFTableOffset), Name(Name) {} + /// Rewrite member type indices with IndexMap. Returns false if a type index + /// is not in the map. + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); + static ErrorOr<OneMethodRecord> deserialize(TypeRecordKind Kind, ArrayRef<uint8_t> &Data) { const Layout *L = nullptr; StringRef Name; - int32_t VFTableOffset = 0; + int32_t VFTableOffset = -1; CV_DESERIALIZE(Data, L, CV_CONDITIONAL_FIELD( VFTableOffset, L->Attrs.isIntroducedVirtual()), @@ -994,17 +1078,19 @@ private: // LF_METHODLIST class MethodOverloadListRecord : public TypeRecord { public: - MethodOverloadListRecord(ArrayRef<OneMethodRecord> Methods) - : TypeRecord(TypeRecordKind::MethodOverloadList), MethodsRef(Methods) {} MethodOverloadListRecord(std::vector<OneMethodRecord> &Methods) : TypeRecord(TypeRecordKind::MethodOverloadList), Methods(Methods) {} + /// Rewrite member type indices with IndexMap. Returns false if a type index + /// is not in the map. + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); + static ErrorOr<MethodOverloadListRecord> deserialize(TypeRecordKind Kind, ArrayRef<uint8_t> &Data) { std::vector<OneMethodRecord> Methods; while (!Data.empty()) { const Layout *L = nullptr; - int32_t VFTableOffset = 0; + int32_t VFTableOffset = -1; CV_DESERIALIZE( Data, L, CV_CONDITIONAL_FIELD(VFTableOffset, L->Attrs.isIntroducedVirtual())); @@ -1019,11 +1105,7 @@ public: return MethodOverloadListRecord(Methods); } - ArrayRef<OneMethodRecord> getMethods() const { - if (!MethodsRef.empty()) - return MethodsRef; - return Methods; - } + ArrayRef<OneMethodRecord> getMethods() const { return Methods; } private: struct Layout { @@ -1035,7 +1117,6 @@ private: // VFTableOffset: int32_t offset in vftable }; - ArrayRef<OneMethodRecord> MethodsRef; std::vector<OneMethodRecord> Methods; }; @@ -1047,6 +1128,10 @@ public: : TypeRecord(TypeRecordKind::OverloadedMethod), NumOverloads(NumOverloads), MethodList(MethodList), Name(Name) {} + /// Rewrite member type indices with IndexMap. Returns false if a type index + /// is not in the map. + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); + static ErrorOr<OverloadedMethodRecord> deserialize(TypeRecordKind Kind, ArrayRef<uint8_t> &Data) { const Layout *L = nullptr; @@ -1080,6 +1165,10 @@ public: : TypeRecord(TypeRecordKind::DataMember), Access(Access), Type(Type), FieldOffset(Offset), Name(Name) {} + /// Rewrite member type indices with IndexMap. Returns false if a type index + /// is not in the map. + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); + static ErrorOr<DataMemberRecord> deserialize(TypeRecordKind Kind, ArrayRef<uint8_t> &Data) { const Layout *L = nullptr; @@ -1116,6 +1205,10 @@ public: : TypeRecord(TypeRecordKind::StaticDataMember), Access(Access), Type(Type), Name(Name) {} + /// Rewrite member type indices with IndexMap. Returns false if a type index + /// is not in the map. + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); + static ErrorOr<StaticDataMemberRecord> deserialize(TypeRecordKind Kind, ArrayRef<uint8_t> &Data) { const Layout *L = nullptr; @@ -1148,6 +1241,10 @@ public: : TypeRecord(TypeRecordKind::Enumerator), Access(Access), Value(Value), Name(Name) {} + /// Rewrite member type indices with IndexMap. Returns false if a type index + /// is not in the map. + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); + static ErrorOr<EnumeratorRecord> deserialize(TypeRecordKind Kind, ArrayRef<uint8_t> &Data) { const Layout *L = nullptr; @@ -1179,6 +1276,11 @@ class VFPtrRecord : public TypeRecord { public: VFPtrRecord(TypeIndex Type) : TypeRecord(TypeRecordKind::VFPtr), Type(Type) {} + + /// Rewrite member type indices with IndexMap. Returns false if a type index + /// is not in the map. + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); + static ErrorOr<VFPtrRecord> deserialize(TypeRecordKind Kind, ArrayRef<uint8_t> &Data) { const Layout *L = nullptr; @@ -1205,6 +1307,10 @@ public: : TypeRecord(TypeRecordKind::BaseClass), Access(Access), Type(Type), Offset(Offset) {} + /// Rewrite member type indices with IndexMap. Returns false if a type index + /// is not in the map. + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); + static ErrorOr<BaseClassRecord> deserialize(TypeRecordKind Kind, ArrayRef<uint8_t> &Data) { const Layout *L = nullptr; @@ -1238,6 +1344,10 @@ public: BaseType(BaseType), VBPtrType(VBPtrType), VBPtrOffset(Offset), VTableIndex(Index) {} + /// Rewrite member type indices with IndexMap. Returns false if a type index + /// is not in the map. + bool remapTypeIndices(ArrayRef<TypeIndex> IndexMap); + static ErrorOr<VirtualBaseClassRecord> deserialize(TypeRecordKind Kind, ArrayRef<uint8_t> &Data) { const Layout *L = nullptr; diff --git a/llvm/include/llvm/DebugInfo/CodeView/TypeRecordBuilder.h b/llvm/include/llvm/DebugInfo/CodeView/TypeRecordBuilder.h index 1f48cf70666..bc10f5a8500 100644 --- a/llvm/include/llvm/DebugInfo/CodeView/TypeRecordBuilder.h +++ b/llvm/include/llvm/DebugInfo/CodeView/TypeRecordBuilder.h @@ -41,11 +41,14 @@ public: void writeEncodedUnsignedInteger(uint64_t Value); void writeNullTerminatedString(const char *Value); void writeNullTerminatedString(StringRef Value); + void writeGuid(StringRef Guid); llvm::StringRef str(); uint64_t size() const { return Stream.tell(); } + void reset() { Buffer.clear(); } + private: llvm::SmallVector<char, 256> Buffer; llvm::raw_svector_ostream Stream; diff --git a/llvm/include/llvm/DebugInfo/CodeView/TypeStreamMerger.h b/llvm/include/llvm/DebugInfo/CodeView/TypeStreamMerger.h new file mode 100644 index 00000000000..0dd49270c15 --- /dev/null +++ b/llvm/include/llvm/DebugInfo/CodeView/TypeStreamMerger.h @@ -0,0 +1,27 @@ +//===- TypeStreamMerger.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_CODEVIEW_TYPESTREAMMERGER_H +#define LLVM_DEBUGINFO_CODEVIEW_TYPESTREAMMERGER_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/DebugInfo/CodeView/TypeTableBuilder.h" + +namespace llvm { +namespace codeview { + +/// Merges one type stream into another. Returns true on success. +bool mergeTypeStreams(TypeTableBuilder &DestStream, + ArrayRef<uint8_t> SrcStream); + +} // end namespace codeview +} // end namespace llvm + +#endif // LLVM_DEBUGINFO_CODEVIEW_TYPESTREAMMERGER_H diff --git a/llvm/include/llvm/DebugInfo/CodeView/TypeTableBuilder.h b/llvm/include/llvm/DebugInfo/CodeView/TypeTableBuilder.h index 4d4f03b54ae..e180390fccf 100644 --- a/llvm/include/llvm/DebugInfo/CodeView/TypeTableBuilder.h +++ b/llvm/include/llvm/DebugInfo/CodeView/TypeTableBuilder.h @@ -41,18 +41,26 @@ public: TypeIndex writeProcedure(const ProcedureRecord &Record); TypeIndex writeMemberFunction(const MemberFunctionRecord &Record); TypeIndex writeArgList(const ArgListRecord &Record); - TypeIndex writeRecord(TypeRecordBuilder &builder); TypeIndex writePointer(const PointerRecord &Record); TypeIndex writeArray(const ArrayRecord &Record); TypeIndex writeClass(const ClassRecord &Record); + TypeIndex writeUnion(const UnionRecord &Record); TypeIndex writeEnum(const EnumRecord &Record); TypeIndex writeBitField(const BitFieldRecord &Record); TypeIndex writeVFTableShape(const VFTableShapeRecord &Record); + TypeIndex writeStringId(const StringIdRecord &Record); + TypeIndex writeVFTable(const VFTableRecord &Record); + TypeIndex writeUdtSourceLine(const UdtSourceLineRecord &Record); + TypeIndex writeFuncId(const FuncIdRecord &Record); + TypeIndex writeMemberFuncId(const MemberFuncIdRecord &Record); + TypeIndex writeBuildInfo(const BuildInfoRecord &Record); + TypeIndex writeMethodOverloadList(const MethodOverloadListRecord &Record); + TypeIndex writeTypeServer2(const TypeServer2Record &Record); TypeIndex writeFieldList(FieldListRecordBuilder &FieldList); - TypeIndex writeMethodList(MethodListRecordBuilder &MethodList); -private: + TypeIndex writeRecord(TypeRecordBuilder &builder); + virtual TypeIndex writeRecord(llvm::StringRef record) = 0; }; } diff --git a/llvm/lib/DebugInfo/CodeView/CMakeLists.txt b/llvm/lib/DebugInfo/CodeView/CMakeLists.txt index cdca673474d..ecddfaf9d7b 100644 --- a/llvm/lib/DebugInfo/CodeView/CMakeLists.txt +++ b/llvm/lib/DebugInfo/CodeView/CMakeLists.txt @@ -6,7 +6,9 @@ add_llvm_library(LLVMDebugInfoCodeView MethodListRecordBuilder.cpp RecordSerialization.cpp TypeDumper.cpp + TypeRecord.cpp TypeRecordBuilder.cpp + TypeStreamMerger.cpp TypeTableBuilder.cpp ADDITIONAL_HEADER_DIRS diff --git a/llvm/lib/DebugInfo/CodeView/FieldListRecordBuilder.cpp b/llvm/lib/DebugInfo/CodeView/FieldListRecordBuilder.cpp index 62bc4c481cb..5f229e3d9f9 100644 --- a/llvm/lib/DebugInfo/CodeView/FieldListRecordBuilder.cpp +++ b/llvm/lib/DebugInfo/CodeView/FieldListRecordBuilder.cpp @@ -15,151 +15,118 @@ using namespace codeview; FieldListRecordBuilder::FieldListRecordBuilder() : ListRecordBuilder(TypeRecordKind::FieldList) {} -void FieldListRecordBuilder::writeBaseClass(MemberAccess Access, TypeIndex Type, - uint64_t Offset) { +void FieldListRecordBuilder::writeBaseClass(const BaseClassRecord &Record) { TypeRecordBuilder &Builder = getBuilder(); Builder.writeTypeRecordKind(TypeRecordKind::BaseClass); - Builder.writeUInt16(static_cast<uint16_t>(Access)); - Builder.writeTypeIndex(Type); - Builder.writeEncodedUnsignedInteger(Offset); + Builder.writeUInt16(static_cast<uint16_t>(Record.getAccess())); + Builder.writeTypeIndex(Record.getBaseType()); + Builder.writeEncodedUnsignedInteger(Record.getBaseOffset()); finishSubRecord(); } -void FieldListRecordBuilder::writeEnumerate(MemberAccess Access, uint64_t Value, - StringRef Name) { +void FieldListRecordBuilder::writeEnumerator(const EnumeratorRecord &Record) { TypeRecordBuilder &Builder = getBuilder(); Builder.writeTypeRecordKind(TypeRecordKind::Enumerator); - Builder.writeUInt16(static_cast<uint16_t>(Access)); - Builder.writeEncodedUnsignedInteger(Value); - Builder.writeNullTerminatedString(Name); + Builder.writeUInt16(static_cast<uint16_t>(Record.getAccess())); + // FIXME: Handle full APInt such as __int128. + Builder.writeEncodedUnsignedInteger(Record.getValue().getZExtValue()); + Builder.writeNullTerminatedString(Record.getName()); finishSubRecord(); } -void FieldListRecordBuilder::writeMember(MemberAccess Access, TypeIndex Type, - uint64_t Offset, StringRef Name) { +void FieldListRecordBuilder::writeDataMember(const DataMemberRecord &Record) { TypeRecordBuilder &Builder = getBuilder(); - Builder.writeTypeRecordKind(TypeRecordKind::DataMember); - Builder.writeUInt16(static_cast<uint16_t>(Access)); - Builder.writeTypeIndex(Type); - Builder.writeEncodedUnsignedInteger(Offset); - Builder.writeNullTerminatedString(Name); + Builder.writeTypeRecordKind(Record.getKind()); + Builder.writeUInt16(static_cast<uint16_t>(Record.getAccess())); + Builder.writeTypeIndex(Record.getType()); + Builder.writeEncodedUnsignedInteger(Record.getFieldOffset()); + Builder.writeNullTerminatedString(Record.getName()); finishSubRecord(); } -void FieldListRecordBuilder::writeMethod(uint16_t OverloadCount, - TypeIndex MethodList, StringRef Name) { +void FieldListRecordBuilder::writeOverloadedMethod( + const OverloadedMethodRecord &Record) { TypeRecordBuilder &Builder = getBuilder(); Builder.writeTypeRecordKind(TypeRecordKind::OverloadedMethod); - Builder.writeUInt16(OverloadCount); - Builder.writeTypeIndex(MethodList); - Builder.writeNullTerminatedString(Name); + Builder.writeUInt16(Record.getNumOverloads()); + Builder.writeTypeIndex(Record.getMethodList()); + Builder.writeNullTerminatedString(Record.getName()); finishSubRecord(); } -void FieldListRecordBuilder::writeOneMethod( - MemberAccess Access, MethodKind Kind, MethodOptions Options, TypeIndex Type, - int32_t VTableSlotOffset, StringRef Name) { +void FieldListRecordBuilder::writeOneMethod(const OneMethodRecord &Record) { TypeRecordBuilder &Builder = getBuilder(); - uint16_t Flags = static_cast<uint16_t>(Access); - Flags |= static_cast<uint16_t>(Kind) << MethodKindShift; - Flags |= static_cast<uint16_t>(Options); + uint16_t Flags = static_cast<uint16_t>(Record.getAccess()); + Flags |= static_cast<uint16_t>(Record.getKind()) << MethodKindShift; + Flags |= static_cast<uint16_t>(Record.getOptions()); Builder.writeTypeRecordKind(TypeRecordKind::OneMethod); Builder.writeUInt16(Flags); - Builder.writeTypeIndex(Type); - switch (Kind) { - case MethodKind::IntroducingVirtual: - case MethodKind::PureIntroducingVirtual: - assert(VTableSlotOffset >= 0); - Builder.writeInt32(VTableSlotOffset); - break; - - default: - assert(VTableSlotOffset == -1); - break; + Builder.writeTypeIndex(Record.getType()); + if (Record.isIntroducingVirtual()) { + assert(Record.getVFTableOffset() >= 0); + Builder.writeInt32(Record.getVFTableOffset()); + } else { + assert(Record.getVFTableOffset() == -1); } - Builder.writeNullTerminatedString(Name); + Builder.writeNullTerminatedString(Record.getName()); finishSubRecord(); } -void FieldListRecordBuilder::writeOneMethod(const MethodInfo &Method, - StringRef Name) { - writeOneMethod(Method.getAccess(), Method.getKind(), Method.getOptions(), - Method.getType(), Method.getVTableSlotOffset(), Name); -} - -void FieldListRecordBuilder::writeNestedType(TypeIndex Type, StringRef Name) { +void FieldListRecordBuilder::writeNestedType(const NestedTypeRecord &Record) { TypeRecordBuilder &Builder = getBuilder(); - Builder.writeTypeRecordKind(TypeRecordKind::NestedType); + Builder.writeTypeRecordKind(Record.getKind()); Builder.writeUInt16(0); - Builder.writeTypeIndex(Type); - Builder.writeNullTerminatedString(Name); + Builder.writeTypeIndex(Record.getNestedType()); + Builder.writeNullTerminatedString(Record.getName()); finishSubRecord(); } -void FieldListRecordBuilder::writeStaticMember(MemberAccess Access, - TypeIndex Type, StringRef Name) { +void FieldListRecordBuilder::writeStaticDataMember( + const StaticDataMemberRecord &Record) { TypeRecordBuilder &Builder = getBuilder(); - Builder.writeTypeRecordKind(TypeRecordKind::StaticDataMember); - Builder.writeUInt16(static_cast<uint16_t>(Access)); - Builder.writeTypeIndex(Type); - Builder.writeNullTerminatedString(Name); + Builder.writeTypeRecordKind(Record.getKind()); + Builder.writeUInt16(static_cast<uint16_t>(Record.getAccess())); + Builder.writeTypeIndex(Record.getType()); + Builder.writeNullTerminatedString(Record.getName()); finishSubRecord(); } -void FieldListRecordBuilder::writeIndirectVirtualBaseClass( - MemberAccess Access, TypeIndex Type, TypeIndex VirtualBasePointerType, - int64_t VirtualBasePointerOffset, uint64_t SlotIndex) { - writeVirtualBaseClass(TypeRecordKind::IndirectVirtualBaseClass, Access, Type, - VirtualBasePointerType, VirtualBasePointerOffset, - SlotIndex); -} - -void FieldListRecordBuilder::writeVirtualBaseClass( - MemberAccess Access, TypeIndex Type, TypeIndex VirtualBasePointerType, - int64_t VirtualBasePointerOffset, uint64_t SlotIndex) { - writeVirtualBaseClass(TypeRecordKind::VirtualBaseClass, Access, Type, - VirtualBasePointerType, VirtualBasePointerOffset, - SlotIndex); -} - void FieldListRecordBuilder::writeVirtualBaseClass( - TypeRecordKind Kind, MemberAccess Access, TypeIndex Type, - TypeIndex VirtualBasePointerType, int64_t VirtualBasePointerOffset, - uint64_t SlotIndex) { + const VirtualBaseClassRecord &Record) { TypeRecordBuilder &Builder = getBuilder(); - Builder.writeTypeRecordKind(Kind); - Builder.writeUInt16(static_cast<uint16_t>(Access)); - Builder.writeTypeIndex(Type); - Builder.writeTypeIndex(VirtualBasePointerType); - Builder.writeEncodedInteger(VirtualBasePointerOffset); - Builder.writeEncodedUnsignedInteger(SlotIndex); + Builder.writeTypeRecordKind(Record.getKind()); + Builder.writeUInt16(static_cast<uint16_t>(Record.getAccess())); + Builder.writeTypeIndex(Record.getBaseType()); + Builder.writeTypeIndex(Record.getVBPtrType()); + Builder.writeEncodedInteger(Record.getVBPtrOffset()); + Builder.writeEncodedUnsignedInteger(Record.getVTableIndex()); finishSubRecord(); } -void FieldListRecordBuilder::writeVirtualFunctionTablePointer(TypeIndex Type) { +void FieldListRecordBuilder::writeVFPtr(const VFPtrRecord &Record) { TypeRecordBuilder &Builder = getBuilder(); Builder.writeTypeRecordKind(TypeRecordKind::VFPtr); Builder.writeUInt16(0); - Builder.writeTypeIndex(Type); + Builder.writeTypeIndex(Record.getType()); finishSubRecord(); } diff --git a/llvm/lib/DebugInfo/CodeView/TypeDumper.cpp b/llvm/lib/DebugInfo/CodeView/TypeDumper.cpp index 6a42fc42433..50a7e6c841b 100644 --- a/llvm/lib/DebugInfo/CodeView/TypeDumper.cpp +++ b/llvm/lib/DebugInfo/CodeView/TypeDumper.cpp @@ -280,6 +280,7 @@ void CVTypeDumperImpl::visitStringId(TypeLeafKind Leaf, // Put this in CVUDTNames so it gets printed with LF_UDT_SRC_LINE. Name = String.getString(); } + void CVTypeDumperImpl::visitArgList(TypeLeafKind Leaf, ArgListRecord &Args) { auto Indices = Args.getIndices(); uint32_t Size = Indices.size(); diff --git a/llvm/lib/DebugInfo/CodeView/TypeRecord.cpp b/llvm/lib/DebugInfo/CodeView/TypeRecord.cpp new file mode 100644 index 00000000000..57cd5b7e264 --- /dev/null +++ b/llvm/lib/DebugInfo/CodeView/TypeRecord.cpp @@ -0,0 +1,191 @@ +//===-- TypeRecord.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/DebugInfo/CodeView/TypeIndex.h" + +using namespace llvm; +using namespace llvm::codeview; + +static bool remapIndex(ArrayRef<TypeIndex> IndexMap, TypeIndex &Idx) { + // Simple types are unchanged. + if (Idx.isSimple()) + return true; + unsigned MapPos = Idx.getIndex() - TypeIndex::FirstNonSimpleIndex; + if (MapPos < IndexMap.size()) { + Idx = IndexMap[MapPos]; + return true; + } + + // This type index is invalid. Remap this to "not translated by cvpack", + // and return failure. + Idx = TypeIndex(SimpleTypeKind::NotTranslated, SimpleTypeMode::Direct); + return false; +} + +bool ModifierRecord::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + return remapIndex(IndexMap, ModifiedType); +} + +bool ProcedureRecord::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + bool Success = true; + Success &= remapIndex(IndexMap, ReturnType); + Success &= remapIndex(IndexMap, ArgumentList); + return Success; +} + +bool MemberFunctionRecord::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + bool Success = true; + Success &= remapIndex(IndexMap, ReturnType); + Success &= remapIndex(IndexMap, ClassType); + Success &= remapIndex(IndexMap, ThisType); + Success &= remapIndex(IndexMap, ArgumentList); + return Success; +} + +bool MemberFuncIdRecord::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + bool Success = true; + Success &= remapIndex(IndexMap, ClassType); + Success &= remapIndex(IndexMap, FunctionType); + return Success; +} + +bool ArgListRecord::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + bool Success = true; + for (TypeIndex &Str : StringIndices) + Success &= remapIndex(IndexMap, Str); + return Success; +} + +bool MemberPointerInfo::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + return remapIndex(IndexMap, ContainingType); +} + +bool PointerRecord::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + bool Success = true; + Success &= remapIndex(IndexMap, ReferentType); + if (isPointerToMember()) + Success &= MemberInfo.remapTypeIndices(IndexMap); + return Success; +} + +bool NestedTypeRecord::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + return remapIndex(IndexMap, Type); +} + +bool ArrayRecord::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + bool Success = true; + Success &= remapIndex(IndexMap, ElementType); + Success &= remapIndex(IndexMap, IndexType); + return Success; +} + +bool TagRecord::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + return remapIndex(IndexMap, FieldList); +} + +bool ClassRecord::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + bool Success = true; + Success &= TagRecord::remapTypeIndices(IndexMap); + Success &= remapIndex(IndexMap, DerivationList); + Success &= remapIndex(IndexMap, VTableShape); + return Success; +} + +bool EnumRecord::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + bool Success = true; + Success &= TagRecord::remapTypeIndices(IndexMap); + Success &= remapIndex(IndexMap, UnderlyingType); + return Success; +} + +bool VFTableShapeRecord::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + return true; +} + +bool TypeServer2Record::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + return true; +} + +bool StringIdRecord::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + return remapIndex(IndexMap, Id); +} + +bool FuncIdRecord::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + bool Success = true; + Success &= remapIndex(IndexMap, ParentScope); + Success &= remapIndex(IndexMap, FunctionType); + return Success; +} + +bool UdtSourceLineRecord::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + bool Success = true; + Success &= remapIndex(IndexMap, UDT); + Success &= remapIndex(IndexMap, SourceFile); + return Success; +} + +bool BuildInfoRecord::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + bool Success = true; + for (TypeIndex &Arg : ArgIndices) + Success &= remapIndex(IndexMap, Arg); + return Success; +} + +bool VFTableRecord::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + bool Success = true; + Success &= remapIndex(IndexMap, CompleteClass); + Success &= remapIndex(IndexMap, OverriddenVFTable); + return Success; +} + +bool OneMethodRecord::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + bool Success = true; + Success &= remapIndex(IndexMap, Type); + return Success; +} + +bool MethodOverloadListRecord::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + bool Success = true; + for (OneMethodRecord &Meth : Methods) + if ((Success = Meth.remapTypeIndices(IndexMap))) + return Success; + return Success; +} + +bool OverloadedMethodRecord::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + return remapIndex(IndexMap, MethodList); +} + +bool DataMemberRecord::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + return remapIndex(IndexMap, Type); +} + +bool StaticDataMemberRecord::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + return remapIndex(IndexMap, Type); +} + +bool EnumeratorRecord::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + return true; +} + +bool VFPtrRecord::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + return remapIndex(IndexMap, Type); +} + +bool BaseClassRecord::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + return remapIndex(IndexMap, Type); +} + +bool VirtualBaseClassRecord::remapTypeIndices(ArrayRef<TypeIndex> IndexMap) { + bool Success = true; + Success &= remapIndex(IndexMap, BaseType); + Success &= remapIndex(IndexMap, VBPtrType); + return Success; +} diff --git a/llvm/lib/DebugInfo/CodeView/TypeRecordBuilder.cpp b/llvm/lib/DebugInfo/CodeView/TypeRecordBuilder.cpp index 3cc56ba2c19..f1c293e39fd 100644 --- a/llvm/lib/DebugInfo/CodeView/TypeRecordBuilder.cpp +++ b/llvm/lib/DebugInfo/CodeView/TypeRecordBuilder.cpp @@ -12,8 +12,8 @@ using namespace llvm; using namespace codeview; -TypeRecordBuilder::TypeRecordBuilder(TypeRecordKind Kind) : Stream(Buffer), - Writer(Stream) { +TypeRecordBuilder::TypeRecordBuilder(TypeRecordKind Kind) + : Stream(Buffer), Writer(Stream) { writeTypeRecordKind(Kind); } @@ -104,6 +104,11 @@ void TypeRecordBuilder::writeNullTerminatedString(StringRef Value) { writeUInt8(0); } +void TypeRecordBuilder::writeGuid(StringRef Guid) { + assert(Guid.size() == 16); + Stream.write(Guid.data(), 16); +} + void TypeRecordBuilder::writeTypeIndex(TypeIndex TypeInd) { writeUInt32(TypeInd.getIndex()); } diff --git a/llvm/lib/DebugInfo/CodeView/TypeStreamMerger.cpp b/llvm/lib/DebugInfo/CodeView/TypeStreamMerger.cpp new file mode 100644 index 00000000000..5ee7cb17000 --- /dev/null +++ b/llvm/lib/DebugInfo/CodeView/TypeStreamMerger.cpp @@ -0,0 +1,144 @@ +//===-- TypeStreamMerger.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/TypeStreamMerger.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" +#include "llvm/DebugInfo/CodeView/FieldListRecordBuilder.h" +#include "llvm/DebugInfo/CodeView/TypeIndex.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/DebugInfo/CodeView/TypeStream.h" +#include "llvm/Support/ScopedPrinter.h" + +using namespace llvm; +using namespace llvm::codeview; + +namespace { + +/// Implementation of CodeView type stream merging. +/// +/// A CodeView type stream is a series of records that reference each other +/// through type indices. A type index is either "simple", meaning it is less +/// than 0x1000 and refers to a builtin type, or it is complex, meaning it +/// refers to a prior type record in the current stream. The type index of a +/// record is equal to the number of records before it in the stream plus +/// 0x1000. +/// +/// Type records are only allowed to use type indices smaller than their own, so +/// a type stream is effectively a topologically sorted DAG. Cycles occuring in +/// the type graph of the source program are resolved with forward declarations +/// of composite types. This class implements the following type stream merging +/// algorithm, which relies on this DAG structure: +/// +/// - Begin with a new empty stream, and a new empty hash table that maps from +/// type record contents to new type index. +/// - For each new type stream, maintain a map from source type index to +/// destination type index. +/// - For each record, copy it and rewrite its type indices to be valid in the +/// destination type stream. +/// - If the new type record is not already present in the destination stream +/// hash table, append it to the destination type stream, assign it the next +/// type index, and update the two hash tables. +/// - If the type record already exists in the destination stream, discard it +/// and update the type index map to forward the source type index to the +/// existing destination type index. +class TypeStreamMerger : public CVTypeVisitor<TypeStreamMerger> { +public: + TypeStreamMerger(TypeTableBuilder &DestStream) : DestStream(DestStream) { + assert(!hadError()); + } + + /// CVTypeVisitor overrides. +#define TYPE_RECORD(EnumName, EnumVal, Name) \ + void visit##Name(TypeLeafKind LeafType, Name##Record &Record); +#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) +#define MEMBER_RECORD(EnumName, EnumVal, Name) \ + void visit##Name(TypeLeafKind LeafType, Name##Record &Record); +#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) +#include "llvm/DebugInfo/CodeView/TypeRecords.def" + + void visitUnknownMember(TypeLeafKind Leaf); + + void visitTypeBegin(TypeLeafKind Leaf, ArrayRef<uint8_t> RecordData); + void visitTypeEnd(TypeLeafKind Leaf, ArrayRef<uint8_t> RecordData); + + void visitFieldList(TypeLeafKind Leaf, ArrayRef<uint8_t> FieldData); + + bool mergeStream(ArrayRef<uint8_t> SrcStream); + +private: + bool hadError() { return FoundBadTypeIndex || CVTypeVisitor::hadError(); } + + bool FoundBadTypeIndex = false; + + FieldListRecordBuilder FieldBuilder; + + TypeTableBuilder &DestStream; + + size_t BeginIndexMapSize = 0; + + /// Map from source type index to destination type index. Indexed by source + /// type index minus 0x1000. + SmallVector<TypeIndex, 0> IndexMap; +}; + +} // end anonymous namespace + +void TypeStreamMerger::visitTypeBegin(TypeLeafKind Leaf, + ArrayRef<uint8_t> RecordData) { + BeginIndexMapSize = IndexMap.size(); +} + +void TypeStreamMerger::visitTypeEnd(TypeLeafKind Leaf, + ArrayRef<uint8_t> RecordData) { + assert(IndexMap.size() == BeginIndexMapSize + 1); +} + +void TypeStreamMerger::visitFieldList(TypeLeafKind Leaf, + ArrayRef<uint8_t> FieldData) { + CVTypeVisitor::visitFieldList(Leaf, FieldData); + IndexMap.push_back(DestStream.writeFieldList(FieldBuilder)); + FieldBuilder.reset(); +} + +#define TYPE_RECORD(EnumName, EnumVal, Name) \ + void TypeStreamMerger::visit##Name(TypeLeafKind LeafType, \ + Name##Record &Record) { \ + FoundBadTypeIndex |= !Record.remapTypeIndices(IndexMap); \ + IndexMap.push_back(DestStream.write##Name(Record)); \ + } +#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) +#define MEMBER_RECORD(EnumName, EnumVal, Name) \ + void TypeStreamMerger::visit##Name(TypeLeafKind LeafType, \ + Name##Record &Record) { \ + FoundBadTypeIndex |= !Record.remapTypeIndices(IndexMap); \ + FieldBuilder.write##Name(Record); \ + } +#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) +#include "llvm/DebugInfo/CodeView/TypeRecords.def" + +void TypeStreamMerger::visitUnknownMember(TypeLeafKind LF) { + // We failed to translate a type. Translate this index as "not translated". + IndexMap.push_back( + TypeIndex(SimpleTypeKind::NotTranslated, SimpleTypeMode::Direct)); + parseError(); +} + +bool TypeStreamMerger::mergeStream(ArrayRef<uint8_t> SrcStream) { + assert(IndexMap.empty()); + visitTypeStream(SrcStream); + IndexMap.clear(); + return !hadError(); +} + +bool llvm::codeview::mergeTypeStreams(TypeTableBuilder &DestStream, + ArrayRef<uint8_t> SrcStream) { + return TypeStreamMerger(DestStream).mergeStream(SrcStream); +} diff --git a/llvm/lib/DebugInfo/CodeView/TypeTableBuilder.cpp b/llvm/lib/DebugInfo/CodeView/TypeTableBuilder.cpp index 39e2a82f40a..3f48f6495f2 100644 --- a/llvm/lib/DebugInfo/CodeView/TypeTableBuilder.cpp +++ b/llvm/lib/DebugInfo/CodeView/TypeTableBuilder.cpp @@ -128,6 +128,23 @@ TypeIndex TypeTableBuilder::writeClass(const ClassRecord &Record) { return writeRecord(Builder); } +TypeIndex TypeTableBuilder::writeUnion(const UnionRecord &Record) { + TypeRecordBuilder Builder(TypeRecordKind::Union); + Builder.writeUInt16(Record.getMemberCount()); + uint16_t Flags = + static_cast<uint16_t>(Record.getOptions()) | + (static_cast<uint16_t>(Record.getHfa()) << ClassRecord::HfaKindShift); + Builder.writeUInt16(Flags); + Builder.writeTypeIndex(Record.getFieldList()); + Builder.writeEncodedUnsignedInteger(Record.getSize()); + Builder.writeNullTerminatedString(Record.getName()); + if ((Record.getOptions() & ClassOptions::HasUniqueName) != + ClassOptions::None) { + Builder.writeNullTerminatedString(Record.getUniqueName()); + } + return writeRecord(Builder); +} + TypeIndex TypeTableBuilder::writeEnum(const EnumRecord &Record) { TypeRecordBuilder Builder(Record.getKind()); @@ -172,6 +189,70 @@ TypeTableBuilder::writeVFTableShape(const VFTableShapeRecord &Record) { return writeRecord(Builder); } +TypeIndex +TypeTableBuilder::writeVFTable(const VFTableRecord &Record) { + TypeRecordBuilder Builder(Record.getKind()); + Builder.writeTypeIndex(Record.getCompleteClass()); + Builder.writeTypeIndex(Record.getOverriddenVTable()); + Builder.writeUInt32(Record.getVFPtrOffset()); + + // Sum up the lengths of the null-terminated names. + size_t NamesLen = Record.getName().size() + 1; + for (StringRef MethodName : Record.getMethodNames()) + NamesLen += MethodName.size() + 1; + + Builder.writeUInt32(NamesLen); + Builder.writeNullTerminatedString(Record.getName()); + for (StringRef MethodName : Record.getMethodNames()) + Builder.writeNullTerminatedString(MethodName); + + return writeRecord(Builder); +} + +TypeIndex TypeTableBuilder::writeStringId(const StringIdRecord &Record) { + TypeRecordBuilder Builder(TypeRecordKind::StringId); + Builder.writeTypeIndex(Record.getId()); + Builder.writeNullTerminatedString(Record.getString()); + return writeRecord(Builder); +} + +TypeIndex +TypeTableBuilder::writeUdtSourceLine(const UdtSourceLineRecord &Record) { + TypeRecordBuilder Builder(Record.getKind()); + Builder.writeTypeIndex(Record.getUDT()); + Builder.writeTypeIndex(Record.getSourceFile()); + Builder.writeUInt32(Record.getLineNumber()); + return writeRecord(Builder); +} + +TypeIndex +TypeTableBuilder::writeFuncId(const FuncIdRecord &Record) { + TypeRecordBuilder Builder(Record.getKind()); + Builder.writeTypeIndex(Record.getParentScope()); + Builder.writeTypeIndex(Record.getFunctionType()); + Builder.writeNullTerminatedString(Record.getName()); + return writeRecord(Builder); +} + +TypeIndex +TypeTableBuilder::writeMemberFuncId(const MemberFuncIdRecord &Record) { + TypeRecordBuilder Builder(Record.getKind()); + Builder.writeTypeIndex(Record.getClassType()); + Builder.writeTypeIndex(Record.getFunctionType()); + Builder.writeNullTerminatedString(Record.getName()); + return writeRecord(Builder); +} + +TypeIndex +TypeTableBuilder::writeBuildInfo(const BuildInfoRecord &Record) { + TypeRecordBuilder Builder(Record.getKind()); + assert(Record.getArgs().size() <= UINT16_MAX); + Builder.writeUInt16(Record.getArgs().size()); + for (TypeIndex Arg : Record.getArgs()) + Builder.writeTypeIndex(Arg); + return writeRecord(Builder); +} + TypeIndex TypeTableBuilder::writeRecord(TypeRecordBuilder &Builder) { return writeRecord(Builder.str()); } @@ -182,9 +263,34 @@ TypeIndex TypeTableBuilder::writeFieldList(FieldListRecordBuilder &FieldList) { return writeRecord(FieldList.str()); } -TypeIndex -TypeTableBuilder::writeMethodList(MethodListRecordBuilder &MethodList) { +TypeIndex TypeTableBuilder::writeMethodOverloadList( + const MethodOverloadListRecord &Record) { + TypeRecordBuilder Builder(Record.getKind()); + for (const OneMethodRecord &Method : Record.getMethods()) { + uint16_t Flags = static_cast<uint16_t>(Method.getAccess()); + Flags |= static_cast<uint16_t>(Method.getKind()) + << MemberAttributes::MethodKindShift; + Flags |= static_cast<uint16_t>(Method.getOptions()); + Builder.writeUInt16(Flags); + Builder.writeUInt16(0); // padding + Builder.writeTypeIndex(Method.getType()); + if (Method.isIntroducingVirtual()) { + assert(Method.getVFTableOffset() >= 0); + Builder.writeInt32(Method.getVFTableOffset()); + } else { + assert(Method.getVFTableOffset() == -1); + } + } + // TODO: Split the list into multiple records if it's longer than 64KB, using // a subrecord of TypeRecordKind::Index to chain the records together. - return writeRecord(MethodList.str()); + return writeRecord(Builder); +} + +TypeIndex TypeTableBuilder::writeTypeServer2(const TypeServer2Record &Record) { + TypeRecordBuilder Builder(Record.getKind()); + Builder.writeGuid(Record.getGuid()); + Builder.writeUInt32(Record.getAge()); + Builder.writeNullTerminatedString(Record.getName()); + return writeRecord(Builder); } diff --git a/llvm/test/tools/llvm-readobj/Inputs/codeview-merging-1.obj b/llvm/test/tools/llvm-readobj/Inputs/codeview-merging-1.obj Binary files differnew file mode 100644 index 00000000000..e51643a136d --- /dev/null +++ b/llvm/test/tools/llvm-readobj/Inputs/codeview-merging-1.obj diff --git a/llvm/test/tools/llvm-readobj/Inputs/codeview-merging-2.obj b/llvm/test/tools/llvm-readobj/Inputs/codeview-merging-2.obj Binary files differnew file mode 100644 index 00000000000..4cf24257dee --- /dev/null +++ b/llvm/test/tools/llvm-readobj/Inputs/codeview-merging-2.obj diff --git a/llvm/test/tools/llvm-readobj/codeview-merging.test b/llvm/test/tools/llvm-readobj/codeview-merging.test new file mode 100644 index 00000000000..60894eff33e --- /dev/null +++ b/llvm/test/tools/llvm-readobj/codeview-merging.test @@ -0,0 +1,65 @@ +# To regenerate t1.obj and t2.obj, run the following: +# $ cat t.cpp +# #ifdef CONFIG1 +# struct A; +# struct B { +# A *a; +# }; +# int f(A *a); +# int g(B *b) { return f(b->a); } +# #else +# struct B; +# struct A { +# B *b; +# }; +# int g(B *b); +# int f(A *a) { return g(a->b); } +# #endif +# $ cl -c -DCONFIG1 -Z7 t.cpp -Fot1.obj && cl -c -Z7 t.cpp -Fot2.obj + +RUN: llvm-readobj -codeview %S/Inputs/codeview-merging-1.obj | FileCheck %s --check-prefix=OBJ1 +RUN: llvm-readobj -codeview %S/Inputs/codeview-merging-2.obj | FileCheck %s --check-prefix=OBJ2 +RUN: llvm-readobj -codeview-merged-types %S/Inputs/codeview-merging-1.obj %S/Inputs/codeview-merging-2.obj | FileCheck %s + +OBJ1: FuncId (0x100D) { +OBJ1-NEXT: TypeLeafKind: LF_FUNC_ID (0x1601) +OBJ1-NEXT: ParentScope: 0x0 +OBJ1-NEXT: FunctionType: int (B*) (0x100C) +OBJ1-NEXT: Name: g +OBJ1-NEXT: } +OBJ1-NEXT: FuncId (0x100E) { +OBJ1-NEXT: TypeLeafKind: LF_FUNC_ID (0x1601) +OBJ1-NEXT: ParentScope: 0x0 +OBJ1-NEXT: FunctionType: int (A*) (0x1003) +OBJ1-NEXT: Name: f +OBJ1-NEXT: } +OBJ1-NOT: FuncId + +OBJ2: FuncId (0x100D) { +OBJ2-NEXT: TypeLeafKind: LF_FUNC_ID (0x1601) +OBJ2-NEXT: ParentScope: 0x0 +OBJ2-NEXT: FunctionType: int (A*) (0x100C) +OBJ2-NEXT: Name: f +OBJ2-NEXT: } + +OBJ2: FuncId (0x1069) { +OBJ2-NEXT: TypeLeafKind: LF_FUNC_ID (0x1601) +OBJ2-NEXT: ParentScope: 0x0 +OBJ2-NEXT: FunctionType: int (B*) (0x1003) +OBJ2-NEXT: Name: g +OBJ2-NEXT: } +OBJ2-NOT: FuncId + +CHECK: FuncId (0x100D) { +CHECK-NEXT: TypeLeafKind: LF_FUNC_ID (0x1601) +CHECK-NEXT: ParentScope: 0x0 +CHECK-NEXT: FunctionType: int (B*) (0x100C) +CHECK-NEXT: Name: g +CHECK-NEXT: } +CHECK-NEXT: FuncId (0x100E) { +CHECK-NEXT: TypeLeafKind: LF_FUNC_ID (0x1601) +CHECK-NEXT: ParentScope: 0x0 +CHECK-NEXT: FunctionType: int (A*) (0x1003) +CHECK-NEXT: Name: f +CHECK-NEXT: } +CHECK-NOT: FuncId diff --git a/llvm/tools/llvm-readobj/CMakeLists.txt b/llvm/tools/llvm-readobj/CMakeLists.txt index 70a7632627d..7477f5b035d 100644 --- a/llvm/tools/llvm-readobj/CMakeLists.txt +++ b/llvm/tools/llvm-readobj/CMakeLists.txt @@ -2,6 +2,7 @@ set(LLVM_LINK_COMPONENTS DebugInfoCodeView Object Support + DebugInfoCodeView ) add_llvm_tool(llvm-readobj diff --git a/llvm/tools/llvm-readobj/COFFDumper.cpp b/llvm/tools/llvm-readobj/COFFDumper.cpp index 6cb543b4251..af3a79d3d1e 100644 --- a/llvm/tools/llvm-readobj/COFFDumper.cpp +++ b/llvm/tools/llvm-readobj/COFFDumper.cpp @@ -25,11 +25,13 @@ #include "llvm/DebugInfo/CodeView/CodeView.h" #include "llvm/DebugInfo/CodeView/Line.h" #include "llvm/DebugInfo/CodeView/RecordSerialization.h" +#include "llvm/DebugInfo/CodeView/MemoryTypeTableBuilder.h" #include "llvm/DebugInfo/CodeView/SymbolRecord.h" #include "llvm/DebugInfo/CodeView/TypeDumper.h" #include "llvm/DebugInfo/CodeView/TypeIndex.h" #include "llvm/DebugInfo/CodeView/TypeRecord.h" #include "llvm/DebugInfo/CodeView/TypeStream.h" +#include "llvm/DebugInfo/CodeView/TypeStreamMerger.h" #include "llvm/Object/COFF.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Support/COFF.h" @@ -71,6 +73,8 @@ public: void printCOFFDirectives() override; void printCOFFBaseReloc() override; void printCodeViewDebugInfo() override; + void + mergeCodeViewTypes(llvm::codeview::MemoryTypeTableBuilder &CVTypes) override; void printStackMap() const override; private: void printSymbol(const SymbolRef &Sym); @@ -1621,6 +1625,25 @@ void COFFDumper::printFileNameForOffset(StringRef Label, uint32_t FileOffset) { W.printHex(Label, getFileNameForFileOffset(FileOffset), FileOffset); } +void COFFDumper::mergeCodeViewTypes(MemoryTypeTableBuilder &CVTypes) { + for (const SectionRef &S : Obj->sections()) { + StringRef SectionName; + error(S.getName(SectionName)); + if (SectionName == ".debug$T") { + StringRef Data; + error(S.getContents(Data)); + unsigned Magic = *reinterpret_cast<const ulittle32_t *>(Data.data()); + if (Magic != 4) + error(object_error::parse_failed); + Data = Data.drop_front(4); + ArrayRef<uint8_t> Bytes(reinterpret_cast<const uint8_t *>(Data.data()), + Data.size()); + if (!mergeTypeStreams(CVTypes, Bytes)) + return error(object_error::parse_failed); + } + } +} + void COFFDumper::printCodeViewTypeSection(StringRef SectionName, const SectionRef &Section) { ListScope D(W, "CodeViewTypes"); @@ -2076,3 +2099,23 @@ void COFFDumper::printStackMap() const { prettyPrintStackMap(llvm::outs(), StackMapV1Parser<support::big>(StackMapContentsArray)); } + +void llvm::dumpCodeViewMergedTypes( + ScopedPrinter &Writer, llvm::codeview::MemoryTypeTableBuilder &CVTypes) { + // Flatten it first, then run our dumper on it. + ListScope S(Writer, "MergedTypeStream"); + SmallString<0> Buf; + CVTypes.ForEachRecord([&](TypeIndex TI, MemoryTypeTableBuilder::Record *R) { + // The record data doesn't include the 16 bit size. + Buf.push_back(R->size() & 0xff); + Buf.push_back((R->size() >> 8) & 0xff); + Buf.append(R->data(), R->data() + R->size()); + }); + CVTypeDumper CVTD(Writer, opts::CodeViewSubsectionBytes); + ArrayRef<uint8_t> BinaryData(reinterpret_cast<const uint8_t *>(Buf.data()), + Buf.size()); + if (!CVTD.dump(BinaryData)) { + Writer.flush(); + error(object_error::parse_failed); + } +} diff --git a/llvm/tools/llvm-readobj/ObjDumper.h b/llvm/tools/llvm-readobj/ObjDumper.h index 318a171d49d..f527ffe08cc 100644 --- a/llvm/tools/llvm-readobj/ObjDumper.h +++ b/llvm/tools/llvm-readobj/ObjDumper.h @@ -18,6 +18,9 @@ namespace object { class COFFImportFile; class ObjectFile; } +namespace codeview { +class MemoryTypeTableBuilder; +}; class ScopedPrinter; @@ -60,6 +63,8 @@ public: virtual void printCOFFDirectives() { } virtual void printCOFFBaseReloc() { } virtual void printCodeViewDebugInfo() { } + virtual void + mergeCodeViewTypes(llvm::codeview::MemoryTypeTableBuilder &CVTypes) {} // Only implemented for MachO. virtual void printMachODataInCode() { } @@ -89,6 +94,9 @@ std::error_code createMachODumper(const object::ObjectFile *Obj, void dumpCOFFImportFile(const object::COFFImportFile *File); +void dumpCodeViewMergedTypes(ScopedPrinter &Writer, + llvm::codeview::MemoryTypeTableBuilder &CVTypes); + } // namespace llvm #endif diff --git a/llvm/tools/llvm-readobj/llvm-readobj.cpp b/llvm/tools/llvm-readobj/llvm-readobj.cpp index c2964c5f5e1..0a48918cf38 100644 --- a/llvm/tools/llvm-readobj/llvm-readobj.cpp +++ b/llvm/tools/llvm-readobj/llvm-readobj.cpp @@ -22,6 +22,7 @@ #include "llvm-readobj.h" #include "Error.h" #include "ObjDumper.h" +#include "llvm/DebugInfo/CodeView/MemoryTypeTableBuilder.h" #include "llvm/Object/Archive.h" #include "llvm/Object/COFFImportFile.h" #include "llvm/Object/ELFObjectFile.h" @@ -144,6 +145,11 @@ namespace opts { cl::opt<bool> CodeView("codeview", cl::desc("Display CodeView debug information")); + // -codeview-merged-types + cl::opt<bool> + CodeViewMergedTypes("codeview-merged-types", + cl::desc("Display the merged CodeView type stream")); + // -codeview-subsection-bytes cl::opt<bool> CodeViewSubsectionBytes( "codeview-subsection-bytes", @@ -296,6 +302,8 @@ static bool isMipsArch(unsigned Arch) { } } +static llvm::codeview::MemoryTypeTableBuilder CVTypes; + /// @brief Creates an format-specific object file dumper. static std::error_code createDumper(const ObjectFile *Obj, ScopedPrinter &Writer, @@ -386,6 +394,8 @@ static void dumpObject(const ObjectFile *Obj) { Dumper->printCOFFBaseReloc(); if (opts::CodeView) Dumper->printCodeViewDebugInfo(); + if (opts::CodeViewMergedTypes) + Dumper->mergeCodeViewTypes(CVTypes); } if (Obj->isMachO()) { if (opts::MachODataInCode) @@ -478,5 +488,10 @@ int main(int argc, const char *argv[]) { std::for_each(opts::InputFilenames.begin(), opts::InputFilenames.end(), dumpInput); + if (opts::CodeViewMergedTypes) { + ScopedPrinter W(outs()); + dumpCodeViewMergedTypes(W, CVTypes); + } + return 0; } |

