[codeview] Add type stream merging prototype

Summary:
This code is intended to be used as part of LLD's PDB writing. Until
that exists, this is exposed via llvm-readobj for testing purposes.

Type stream merging uses the following algorithm:

- 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.

Reviewers: zturner, ruiu

Subscribers: llvm-commits

Differential Revision: http://reviews.llvm.org/D20122

llvm-svn: 269521
This commit is contained in:
Reid Kleckner 2016-05-14 00:02:53 +00:00
parent 32b2897af6
commit 0b269748a6
22 changed files with 820 additions and 135 deletions

View File

@ -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) \

View File

@ -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);
};
}
}

View File

@ -28,6 +28,8 @@ protected:
public:
llvm::StringRef str() { return Builder.str(); }
void reset() { Builder.reset(); }
protected:
void finishSubRecord();

View File

@ -55,7 +55,7 @@ public:
}
}
private:
protected:
TypeIndex writeRecord(llvm::StringRef Data) override;
private:

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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;
};
}

View File

@ -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

View File

@ -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();
}

View File

@ -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();

View File

@ -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;
}

View File

@ -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());
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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

View File

@ -2,6 +2,7 @@ set(LLVM_LINK_COMPONENTS
DebugInfoCodeView
Object
Support
DebugInfoCodeView
)
add_llvm_tool(llvm-readobj

View File

@ -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);
}
}

View File

@ -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

View File

@ -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;
}