summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--llvm/include/llvm/DebugInfo/CodeView/CVTypeVisitor.h6
-rw-r--r--llvm/include/llvm/DebugInfo/CodeView/FieldListRecordBuilder.h35
-rw-r--r--llvm/include/llvm/DebugInfo/CodeView/ListRecordBuilder.h2
-rw-r--r--llvm/include/llvm/DebugInfo/CodeView/MemoryTypeTableBuilder.h2
-rw-r--r--llvm/include/llvm/DebugInfo/CodeView/TypeRecord.h142
-rw-r--r--llvm/include/llvm/DebugInfo/CodeView/TypeRecordBuilder.h3
-rw-r--r--llvm/include/llvm/DebugInfo/CodeView/TypeStreamMerger.h27
-rw-r--r--llvm/include/llvm/DebugInfo/CodeView/TypeTableBuilder.h14
-rw-r--r--llvm/lib/DebugInfo/CodeView/CMakeLists.txt2
-rw-r--r--llvm/lib/DebugInfo/CodeView/FieldListRecordBuilder.cpp133
-rw-r--r--llvm/lib/DebugInfo/CodeView/TypeDumper.cpp1
-rw-r--r--llvm/lib/DebugInfo/CodeView/TypeRecord.cpp191
-rw-r--r--llvm/lib/DebugInfo/CodeView/TypeRecordBuilder.cpp9
-rw-r--r--llvm/lib/DebugInfo/CodeView/TypeStreamMerger.cpp144
-rw-r--r--llvm/lib/DebugInfo/CodeView/TypeTableBuilder.cpp112
-rw-r--r--llvm/test/tools/llvm-readobj/Inputs/codeview-merging-1.objbin0 -> 8515 bytes
-rw-r--r--llvm/test/tools/llvm-readobj/Inputs/codeview-merging-2.objbin0 -> 8507 bytes
-rw-r--r--llvm/test/tools/llvm-readobj/codeview-merging.test65
-rw-r--r--llvm/tools/llvm-readobj/CMakeLists.txt1
-rw-r--r--llvm/tools/llvm-readobj/COFFDumper.cpp43
-rw-r--r--llvm/tools/llvm-readobj/ObjDumper.h8
-rw-r--r--llvm/tools/llvm-readobj/llvm-readobj.cpp15
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
new file mode 100644
index 00000000000..e51643a136d
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/Inputs/codeview-merging-1.obj
Binary files differ
diff --git a/llvm/test/tools/llvm-readobj/Inputs/codeview-merging-2.obj b/llvm/test/tools/llvm-readobj/Inputs/codeview-merging-2.obj
new file mode 100644
index 00000000000..4cf24257dee
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/Inputs/codeview-merging-2.obj
Binary files differ
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;
}
OpenPOWER on IntegriCloud