forked from OSchip/llvm-project
Resubmit "[pdb] Change type visitor pattern to be dynamic."
There was a regression introduced during type stream merging when visiting a field list record. This has been fixed in this patch. llvm-svn: 272929
This commit is contained in:
parent
5a07687a8e
commit
01ee3dae04
|
@ -11,151 +11,31 @@
|
||||||
#define LLVM_DEBUGINFO_CODEVIEW_CVTYPEVISITOR_H
|
#define LLVM_DEBUGINFO_CODEVIEW_CVTYPEVISITOR_H
|
||||||
|
|
||||||
#include "llvm/DebugInfo/CodeView/CVRecord.h"
|
#include "llvm/DebugInfo/CodeView/CVRecord.h"
|
||||||
#include "llvm/DebugInfo/CodeView/CodeView.h"
|
|
||||||
#include "llvm/DebugInfo/CodeView/TypeIndex.h"
|
|
||||||
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
|
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
|
||||||
#include "llvm/Support/ErrorOr.h"
|
#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h"
|
||||||
|
#include "llvm/Support/Error.h"
|
||||||
|
|
||||||
namespace llvm {
|
namespace llvm {
|
||||||
namespace codeview {
|
namespace codeview {
|
||||||
|
|
||||||
template <typename Derived>
|
|
||||||
class CVTypeVisitor {
|
class CVTypeVisitor {
|
||||||
public:
|
public:
|
||||||
CVTypeVisitor() {}
|
explicit CVTypeVisitor(TypeVisitorCallbacks &Callbacks);
|
||||||
|
|
||||||
bool hadError() const { return HadError; }
|
Error visitTypeRecord(const CVRecord<TypeLeafKind> &Record);
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
bool consumeObject(ArrayRef<uint8_t> &Data, const T *&Res) {
|
|
||||||
if (Data.size() < sizeof(*Res)) {
|
|
||||||
HadError = true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
Res = reinterpret_cast<const T *>(Data.data());
|
|
||||||
Data = Data.drop_front(sizeof(*Res));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Actions to take on known types. By default, they do nothing. Visit methods
|
|
||||||
/// for member records take the FieldData by non-const reference and are
|
|
||||||
/// expected to consume the trailing bytes used by the field.
|
|
||||||
/// FIXME: Make the visitor interpret the trailing bytes so that clients don't
|
|
||||||
/// need to.
|
|
||||||
#define TYPE_RECORD(EnumName, EnumVal, Name) \
|
|
||||||
void visit##Name(Name##Record &Record) {}
|
|
||||||
#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
|
|
||||||
#define MEMBER_RECORD(EnumName, EnumVal, Name) \
|
|
||||||
void visit##Name(Name##Record &Record) {}
|
|
||||||
#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
|
|
||||||
#include "TypeRecords.def"
|
|
||||||
|
|
||||||
void visitTypeRecord(const CVRecord<TypeLeafKind> &Record) {
|
|
||||||
ArrayRef<uint8_t> LeafData = Record.Data;
|
|
||||||
auto *DerivedThis = static_cast<Derived *>(this);
|
|
||||||
DerivedThis->visitTypeBegin(Record);
|
|
||||||
switch (Record.Type) {
|
|
||||||
default:
|
|
||||||
DerivedThis->visitUnknownType(Record);
|
|
||||||
break;
|
|
||||||
case LF_FIELDLIST:
|
|
||||||
DerivedThis->visitFieldList(Record.Type, LeafData);
|
|
||||||
break;
|
|
||||||
#define TYPE_RECORD(EnumName, EnumVal, Name) \
|
|
||||||
case EnumName: { \
|
|
||||||
TypeRecordKind RK = static_cast<TypeRecordKind>(EnumName); \
|
|
||||||
auto Result = Name##Record::deserialize(RK, LeafData); \
|
|
||||||
if (Result.getError()) \
|
|
||||||
return parseError(); \
|
|
||||||
DerivedThis->visit##Name(*Result); \
|
|
||||||
break; \
|
|
||||||
}
|
|
||||||
#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) \
|
|
||||||
TYPE_RECORD(EnumVal, EnumVal, AliasName)
|
|
||||||
#define MEMBER_RECORD(EnumName, EnumVal, Name)
|
|
||||||
#include "TypeRecords.def"
|
|
||||||
}
|
|
||||||
DerivedThis->visitTypeEnd(Record);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Visits the type records in Data. Sets the error flag on parse failures.
|
/// Visits the type records in Data. Sets the error flag on parse failures.
|
||||||
void visitTypeStream(const CVTypeArray &Types) {
|
Error visitTypeStream(const CVTypeArray &Types);
|
||||||
for (const auto &I : Types) {
|
|
||||||
visitTypeRecord(I);
|
|
||||||
if (hadError())
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Action to take on unknown types. By default, they are ignored.
|
Error skipPadding(ArrayRef<uint8_t> &Data);
|
||||||
void visitUnknownType(const CVRecord<TypeLeafKind> &Record) {}
|
|
||||||
|
|
||||||
/// Paired begin/end actions for all types. Receives all record data,
|
|
||||||
/// including the fixed-length record prefix.
|
|
||||||
void visitTypeBegin(const CVRecord<TypeLeafKind> &Record) {}
|
|
||||||
void visitTypeEnd(const CVRecord<TypeLeafKind> &Record) {}
|
|
||||||
|
|
||||||
ArrayRef<uint8_t> skipPadding(ArrayRef<uint8_t> Data) {
|
|
||||||
if (Data.empty())
|
|
||||||
return Data;
|
|
||||||
uint8_t Leaf = Data.front();
|
|
||||||
if (Leaf < LF_PAD0)
|
|
||||||
return Data;
|
|
||||||
// Leaf is greater than 0xf0. We should advance by the number of bytes in
|
|
||||||
// the low 4 bits.
|
|
||||||
unsigned BytesToAdvance = Leaf & 0x0F;
|
|
||||||
if (Data.size() < BytesToAdvance) {
|
|
||||||
parseError();
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
return Data.drop_front(BytesToAdvance);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Visits individual member records of a field list record. Member records do
|
/// Visits individual member records of a field list record. Member records do
|
||||||
/// not describe their own length, and need special handling.
|
/// not describe their own length, and need special handling.
|
||||||
void visitFieldList(TypeLeafKind Leaf, ArrayRef<uint8_t> FieldData) {
|
Error visitFieldList(const CVRecord<TypeLeafKind> &Record);
|
||||||
auto *DerivedThis = static_cast<Derived *>(this);
|
|
||||||
while (!FieldData.empty()) {
|
|
||||||
const ulittle16_t *LeafPtr;
|
|
||||||
if (!CVTypeVisitor::consumeObject(FieldData, LeafPtr))
|
|
||||||
return;
|
|
||||||
TypeLeafKind Leaf = TypeLeafKind(unsigned(*LeafPtr));
|
|
||||||
switch (Leaf) {
|
|
||||||
default:
|
|
||||||
// Field list records do not describe their own length, so we cannot
|
|
||||||
// continue parsing past an unknown member type.
|
|
||||||
DerivedThis->visitUnknownMember(Leaf);
|
|
||||||
return parseError();
|
|
||||||
#define MEMBER_RECORD(EnumName, EnumVal, Name) \
|
|
||||||
case EnumName: { \
|
|
||||||
TypeRecordKind RK = static_cast<TypeRecordKind>(EnumName); \
|
|
||||||
auto Result = Name##Record::deserialize(RK, FieldData); \
|
|
||||||
if (Result.getError()) \
|
|
||||||
return parseError(); \
|
|
||||||
DerivedThis->visit##Name(*Result); \
|
|
||||||
break; \
|
|
||||||
}
|
|
||||||
#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) \
|
|
||||||
MEMBER_RECORD(EnumVal, EnumVal, AliasName)
|
|
||||||
#include "TypeRecords.def"
|
|
||||||
}
|
|
||||||
FieldData = skipPadding(FieldData);
|
|
||||||
if (hadError())
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Action to take on unknown members. By default, they are ignored. Member
|
|
||||||
/// record parsing cannot recover from an unknown member record, so this
|
|
||||||
/// method is only called at most once per field list record.
|
|
||||||
void visitUnknownMember(TypeLeafKind Leaf) {}
|
|
||||||
|
|
||||||
/// Helper for returning from a void function when the stream is corrupted.
|
|
||||||
void parseError() { HadError = true; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Whether a type stream parsing error was encountered.
|
/// The interface to the class that gets notified of each visitation.
|
||||||
bool HadError = false;
|
TypeVisitorCallbacks &Callbacks;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // end namespace codeview
|
} // end namespace codeview
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include "llvm/ADT/StringSet.h"
|
#include "llvm/ADT/StringSet.h"
|
||||||
#include "llvm/DebugInfo/CodeView/TypeIndex.h"
|
#include "llvm/DebugInfo/CodeView/TypeIndex.h"
|
||||||
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
|
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
|
||||||
|
#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h"
|
||||||
|
|
||||||
namespace llvm {
|
namespace llvm {
|
||||||
class ScopedPrinter;
|
class ScopedPrinter;
|
||||||
|
@ -21,7 +22,7 @@ class ScopedPrinter;
|
||||||
namespace codeview {
|
namespace codeview {
|
||||||
|
|
||||||
/// Dumper for CodeView type streams found in COFF object files and PDB files.
|
/// Dumper for CodeView type streams found in COFF object files and PDB files.
|
||||||
class CVTypeDumper {
|
class CVTypeDumper : public TypeVisitorCallbacks {
|
||||||
public:
|
public:
|
||||||
CVTypeDumper(ScopedPrinter *W, bool PrintRecordBytes)
|
CVTypeDumper(ScopedPrinter *W, bool PrintRecordBytes)
|
||||||
: W(W), PrintRecordBytes(PrintRecordBytes) {}
|
: W(W), PrintRecordBytes(PrintRecordBytes) {}
|
||||||
|
@ -33,17 +34,17 @@ public:
|
||||||
/// and true otherwise. This should be called in order, since the dumper
|
/// and true otherwise. This should be called in order, since the dumper
|
||||||
/// maintains state about previous records which are necessary for cross
|
/// maintains state about previous records which are necessary for cross
|
||||||
/// type references.
|
/// type references.
|
||||||
bool dump(const CVRecord<TypeLeafKind> &Record);
|
Error dump(const CVRecord<TypeLeafKind> &Record);
|
||||||
|
|
||||||
/// Dumps the type records in Types. Returns false if there was a type stream
|
/// Dumps the type records in Types. Returns false if there was a type stream
|
||||||
/// parse error, and true otherwise.
|
/// parse error, and true otherwise.
|
||||||
bool dump(const CVTypeArray &Types);
|
Error dump(const CVTypeArray &Types);
|
||||||
|
|
||||||
/// Dumps the type records in Data. Returns false if there was a type stream
|
/// Dumps the type records in Data. Returns false if there was a type stream
|
||||||
/// parse error, and true otherwise. Use this method instead of the
|
/// parse error, and true otherwise. Use this method instead of the
|
||||||
/// CVTypeArray overload when type records are laid out contiguously in
|
/// CVTypeArray overload when type records are laid out contiguously in
|
||||||
/// memory.
|
/// memory.
|
||||||
bool dump(ArrayRef<uint8_t> Data);
|
Error dump(ArrayRef<uint8_t> Data);
|
||||||
|
|
||||||
/// Gets the type index for the next type record.
|
/// Gets the type index for the next type record.
|
||||||
unsigned getNextTypeIndex() const {
|
unsigned getNextTypeIndex() const {
|
||||||
|
@ -61,11 +62,35 @@ public:
|
||||||
void setPrinter(ScopedPrinter *P);
|
void setPrinter(ScopedPrinter *P);
|
||||||
ScopedPrinter *getPrinter() { return W; }
|
ScopedPrinter *getPrinter() { return W; }
|
||||||
|
|
||||||
|
/// Action to take on unknown types. By default, they are ignored.
|
||||||
|
Error visitUnknownType(const CVRecord<TypeLeafKind> &Record) override;
|
||||||
|
Error visitUnknownMember(const CVRecord<TypeLeafKind> &Record) override;
|
||||||
|
|
||||||
|
/// Paired begin/end actions for all types. Receives all record data,
|
||||||
|
/// including the fixed-length record prefix.
|
||||||
|
Error visitTypeBegin(const CVRecord<TypeLeafKind> &Record) override;
|
||||||
|
Error visitTypeEnd(const CVRecord<TypeLeafKind> &Record) override;
|
||||||
|
|
||||||
|
#define TYPE_RECORD(EnumName, EnumVal, Name) \
|
||||||
|
Error visit##Name(Name##Record &Record) override;
|
||||||
|
#define MEMBER_RECORD(EnumName, EnumVal, Name) \
|
||||||
|
TYPE_RECORD(EnumName, EnumVal, Name)
|
||||||
|
#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
|
||||||
|
#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
|
||||||
|
#include "TypeRecords.def"
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void printMemberAttributes(MemberAttributes Attrs);
|
||||||
|
void printMemberAttributes(MemberAccess Access, MethodKind Kind,
|
||||||
|
MethodOptions Options);
|
||||||
|
|
||||||
ScopedPrinter *W;
|
ScopedPrinter *W;
|
||||||
|
|
||||||
bool PrintRecordBytes = false;
|
bool PrintRecordBytes = false;
|
||||||
|
|
||||||
|
/// Name of the current type. Only valid before visitTypeEnd.
|
||||||
|
StringRef Name;
|
||||||
|
|
||||||
/// All user defined type records in .debug$T live in here. Type indices
|
/// All user defined type records in .debug$T live in here. Type indices
|
||||||
/// greater than 0x1000 are user defined. Subtract 0x1000 from the index to
|
/// greater than 0x1000 are user defined. Subtract 0x1000 from the index to
|
||||||
/// index into this vector.
|
/// index into this vector.
|
||||||
|
|
|
@ -83,6 +83,7 @@ TYPE_RECORD(LF_STRING_ID, 0x1605, StringId)
|
||||||
TYPE_RECORD(LF_UDT_SRC_LINE, 0x1606, UdtSourceLine)
|
TYPE_RECORD(LF_UDT_SRC_LINE, 0x1606, UdtSourceLine)
|
||||||
TYPE_RECORD(LF_UDT_MOD_SRC_LINE, 0x1607, UdtModSourceLine)
|
TYPE_RECORD(LF_UDT_MOD_SRC_LINE, 0x1607, UdtModSourceLine)
|
||||||
|
|
||||||
|
|
||||||
TYPE_RECORD(LF_METHODLIST, 0x1206, MethodOverloadList)
|
TYPE_RECORD(LF_METHODLIST, 0x1206, MethodOverloadList)
|
||||||
|
|
||||||
|
|
||||||
|
@ -195,6 +196,8 @@ CV_TYPE(LF_MODIFIER_EX, 0x1518)
|
||||||
CV_TYPE(LF_VECTOR, 0x151b)
|
CV_TYPE(LF_VECTOR, 0x151b)
|
||||||
CV_TYPE(LF_MATRIX, 0x151c)
|
CV_TYPE(LF_MATRIX, 0x151c)
|
||||||
|
|
||||||
|
// ID leaf records. Subsequent leaf types may be referenced from .debug$S.
|
||||||
|
|
||||||
// Numeric leaf types. These are generally contained in other records, and not
|
// Numeric leaf types. These are generally contained in other records, and not
|
||||||
// encountered in the main type stream.
|
// encountered in the main type stream.
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
//===- TypeVisitorCallbacks.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_TYPEVISITORCALLBACKS_H
|
||||||
|
#define LLVM_DEBUGINFO_CODEVIEW_TYPEVISITORCALLBACKS_H
|
||||||
|
|
||||||
|
#include "llvm/ADT/ArrayRef.h"
|
||||||
|
#include "llvm/DebugInfo/CodeView/CodeView.h"
|
||||||
|
#include "llvm/Support/Error.h"
|
||||||
|
|
||||||
|
namespace llvm {
|
||||||
|
namespace codeview {
|
||||||
|
class TypeVisitorCallbacks {
|
||||||
|
friend class CVTypeVisitor;
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual ~TypeVisitorCallbacks() {}
|
||||||
|
|
||||||
|
/// Action to take on unknown types. By default, they are ignored.
|
||||||
|
virtual Error visitUnknownType(const CVRecord<TypeLeafKind> &Record) {
|
||||||
|
return Error::success();
|
||||||
|
}
|
||||||
|
virtual Error visitUnknownMember(const CVRecord<TypeLeafKind> &Record) {
|
||||||
|
return Error::success();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Paired begin/end actions for all types. Receives all record data,
|
||||||
|
/// including the fixed-length record prefix.
|
||||||
|
virtual Error visitTypeBegin(const CVRecord<TypeLeafKind> &Record) {
|
||||||
|
return Error::success();
|
||||||
|
}
|
||||||
|
virtual Error visitTypeEnd(const CVRecord<TypeLeafKind> &Record) {
|
||||||
|
return Error::success();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Error visitFieldListBegin(const CVRecord<TypeLeafKind> &Record) {
|
||||||
|
return Error::success();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Error visitFieldListEnd(const CVRecord<TypeLeafKind> &Record) {
|
||||||
|
return Error::success();
|
||||||
|
}
|
||||||
|
|
||||||
|
#define TYPE_RECORD(EnumName, EnumVal, Name) \
|
||||||
|
virtual Error visit##Name(Name##Record &Record) { return Error::success(); }
|
||||||
|
#define MEMBER_RECORD(EnumName, EnumVal, Name) \
|
||||||
|
TYPE_RECORD(EnumName, EnumVal, Name)
|
||||||
|
#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
|
||||||
|
#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
|
||||||
|
#include "TypeRecords.def"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -25,6 +25,7 @@ enum class raw_error_code {
|
||||||
index_out_of_bounds,
|
index_out_of_bounds,
|
||||||
invalid_block_address,
|
invalid_block_address,
|
||||||
not_writable,
|
not_writable,
|
||||||
|
invalid_tpi_hash,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Base class for errors originating when parsing raw PDB files
|
/// Base class for errors originating when parsing raw PDB files
|
||||||
|
|
|
@ -322,10 +322,9 @@ void CodeViewDebug::emitTypeInformation() {
|
||||||
ScopedPrinter SP(CommentOS);
|
ScopedPrinter SP(CommentOS);
|
||||||
SP.setPrefix(CommentPrefix);
|
SP.setPrefix(CommentPrefix);
|
||||||
CVTD.setPrinter(&SP);
|
CVTD.setPrinter(&SP);
|
||||||
bool DumpSuccess =
|
Error EC = CVTD.dump({Record.bytes_begin(), Record.bytes_end()});
|
||||||
CVTD.dump({Record.bytes_begin(), Record.bytes_end()});
|
assert(!EC && "produced malformed type record");
|
||||||
(void)DumpSuccess;
|
consumeError(std::move(EC));
|
||||||
assert(DumpSuccess && "produced malformed type record");
|
|
||||||
// emitRawComment will insert its own tab and comment string before
|
// emitRawComment will insert its own tab and comment string before
|
||||||
// the first line, so strip off our first one. It also prints its own
|
// the first line, so strip off our first one. It also prints its own
|
||||||
// newline.
|
// newline.
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
add_llvm_library(LLVMDebugInfoCodeView
|
add_llvm_library(LLVMDebugInfoCodeView
|
||||||
ByteStream.cpp
|
ByteStream.cpp
|
||||||
CodeViewError.cpp
|
CodeViewError.cpp
|
||||||
|
CVTypeVisitor.cpp
|
||||||
EnumTables.cpp
|
EnumTables.cpp
|
||||||
FieldListRecordBuilder.cpp
|
FieldListRecordBuilder.cpp
|
||||||
Line.cpp
|
Line.cpp
|
||||||
|
|
|
@ -0,0 +1,123 @@
|
||||||
|
//===- CVTypeVisitor.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/CVTypeVisitor.h"
|
||||||
|
|
||||||
|
using namespace llvm;
|
||||||
|
using namespace llvm::codeview;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static Error takeObject(ArrayRef<uint8_t> &Data, const T *&Res) {
|
||||||
|
if (Data.size() < sizeof(*Res))
|
||||||
|
return llvm::make_error<CodeViewError>(cv_error_code::insufficient_buffer);
|
||||||
|
Res = reinterpret_cast<const T *>(Data.data());
|
||||||
|
Data = Data.drop_front(sizeof(*Res));
|
||||||
|
return Error::success();
|
||||||
|
}
|
||||||
|
|
||||||
|
CVTypeVisitor::CVTypeVisitor(TypeVisitorCallbacks &Callbacks)
|
||||||
|
: Callbacks(Callbacks) {}
|
||||||
|
|
||||||
|
Error CVTypeVisitor::visitTypeRecord(const CVRecord<TypeLeafKind> &Record) {
|
||||||
|
ArrayRef<uint8_t> LeafData = Record.Data;
|
||||||
|
if (auto EC = Callbacks.visitTypeBegin(Record))
|
||||||
|
return EC;
|
||||||
|
switch (Record.Type) {
|
||||||
|
default:
|
||||||
|
if (auto EC = Callbacks.visitUnknownType(Record))
|
||||||
|
return EC;
|
||||||
|
break;
|
||||||
|
case LF_FIELDLIST:
|
||||||
|
if (auto EC = Callbacks.visitFieldListBegin(Record))
|
||||||
|
return EC;
|
||||||
|
if (auto EC = visitFieldList(Record))
|
||||||
|
return EC;
|
||||||
|
if (auto EC = Callbacks.visitFieldListEnd(Record))
|
||||||
|
return EC;
|
||||||
|
break;
|
||||||
|
#define TYPE_RECORD(EnumName, EnumVal, Name) \
|
||||||
|
case EnumName: { \
|
||||||
|
TypeRecordKind RK = static_cast<TypeRecordKind>(EnumName); \
|
||||||
|
auto Result = Name##Record::deserialize(RK, LeafData); \
|
||||||
|
if (Result.getError()) \
|
||||||
|
return llvm::make_error<CodeViewError>(cv_error_code::corrupt_record); \
|
||||||
|
if (auto EC = Callbacks.visit##Name(*Result)) \
|
||||||
|
return EC; \
|
||||||
|
break; \
|
||||||
|
}
|
||||||
|
#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) \
|
||||||
|
TYPE_RECORD(EnumVal, EnumVal, AliasName)
|
||||||
|
#define MEMBER_RECORD(EnumName, EnumVal, Name)
|
||||||
|
#include "llvm/DebugInfo/CodeView/TypeRecords.def"
|
||||||
|
}
|
||||||
|
if (auto EC = Callbacks.visitTypeEnd(Record))
|
||||||
|
return EC;
|
||||||
|
return Error::success();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Visits the type records in Data. Sets the error flag on parse failures.
|
||||||
|
Error CVTypeVisitor::visitTypeStream(const CVTypeArray &Types) {
|
||||||
|
for (const auto &I : Types) {
|
||||||
|
if (auto EC = visitTypeRecord(I))
|
||||||
|
return EC;
|
||||||
|
}
|
||||||
|
return Error::success();
|
||||||
|
}
|
||||||
|
|
||||||
|
Error CVTypeVisitor::skipPadding(ArrayRef<uint8_t> &Data) {
|
||||||
|
if (Data.empty())
|
||||||
|
return Error::success();
|
||||||
|
uint8_t Leaf = Data.front();
|
||||||
|
if (Leaf < LF_PAD0)
|
||||||
|
return Error::success();
|
||||||
|
// Leaf is greater than 0xf0. We should advance by the number of bytes in
|
||||||
|
// the low 4 bits.
|
||||||
|
unsigned BytesToAdvance = Leaf & 0x0F;
|
||||||
|
if (Data.size() < BytesToAdvance) {
|
||||||
|
return llvm::make_error<CodeViewError>(cv_error_code::corrupt_record,
|
||||||
|
"Invalid padding bytes!");
|
||||||
|
}
|
||||||
|
Data = Data.drop_front(BytesToAdvance);
|
||||||
|
return Error::success();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Visits individual member records of a field list record. Member records do
|
||||||
|
/// not describe their own length, and need special handling.
|
||||||
|
Error CVTypeVisitor::visitFieldList(const CVRecord<TypeLeafKind> &Record) {
|
||||||
|
ArrayRef<uint8_t> RecordData = Record.Data;
|
||||||
|
while (!RecordData.empty()) {
|
||||||
|
const ulittle16_t *LeafPtr;
|
||||||
|
if (auto EC = takeObject(RecordData, LeafPtr))
|
||||||
|
return EC;
|
||||||
|
TypeLeafKind Leaf = TypeLeafKind(unsigned(*LeafPtr));
|
||||||
|
switch (Leaf) {
|
||||||
|
default:
|
||||||
|
// Field list records do not describe their own length, so we cannot
|
||||||
|
// continue parsing past an unknown member type.
|
||||||
|
if (auto EC = Callbacks.visitUnknownMember(Record))
|
||||||
|
return llvm::make_error<CodeViewError>(cv_error_code::corrupt_record);
|
||||||
|
#define MEMBER_RECORD(EnumName, EnumVal, Name) \
|
||||||
|
case EnumName: { \
|
||||||
|
TypeRecordKind RK = static_cast<TypeRecordKind>(EnumName); \
|
||||||
|
auto Result = Name##Record::deserialize(RK, RecordData); \
|
||||||
|
if (Result.getError()) \
|
||||||
|
return llvm::make_error<CodeViewError>(cv_error_code::corrupt_record); \
|
||||||
|
if (auto EC = Callbacks.visit##Name(*Result)) \
|
||||||
|
return EC; \
|
||||||
|
break; \
|
||||||
|
}
|
||||||
|
#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) \
|
||||||
|
MEMBER_RECORD(EnumVal, EnumVal, AliasName)
|
||||||
|
#include "llvm/DebugInfo/CodeView/TypeRecords.def"
|
||||||
|
}
|
||||||
|
if (auto EC = skipPadding(RecordData))
|
||||||
|
return EC;
|
||||||
|
}
|
||||||
|
return Error::success();
|
||||||
|
}
|
|
@ -189,55 +189,6 @@ static const EnumEntry<uint8_t> FunctionOptionEnum[] = {
|
||||||
|
|
||||||
#undef ENUM_ENTRY
|
#undef ENUM_ENTRY
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
/// Use this private dumper implementation to keep implementation details about
|
|
||||||
/// the visitor out of TypeDumper.h.
|
|
||||||
class CVTypeDumperImpl : public CVTypeVisitor<CVTypeDumperImpl> {
|
|
||||||
public:
|
|
||||||
CVTypeDumperImpl(CVTypeDumper &CVTD, ScopedPrinter &W, bool PrintRecordBytes)
|
|
||||||
: CVTD(CVTD), W(W), PrintRecordBytes(PrintRecordBytes) {}
|
|
||||||
|
|
||||||
/// CVTypeVisitor overrides.
|
|
||||||
#define TYPE_RECORD(EnumName, EnumVal, Name) \
|
|
||||||
void visit##Name(Name##Record &Record);
|
|
||||||
#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
|
|
||||||
#define MEMBER_RECORD(EnumName, EnumVal, Name) \
|
|
||||||
void visit##Name(Name##Record &Record);
|
|
||||||
#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
|
|
||||||
#include "llvm/DebugInfo/CodeView/TypeRecords.def"
|
|
||||||
|
|
||||||
void visitUnknownMember(TypeLeafKind Leaf);
|
|
||||||
void visitUnknownType(const CVRecord<TypeLeafKind> &Record);
|
|
||||||
|
|
||||||
void visitTypeBegin(const CVRecord<TypeLeafKind> &Record);
|
|
||||||
void visitTypeEnd(const CVRecord<TypeLeafKind> &Record);
|
|
||||||
|
|
||||||
void printMemberAttributes(MemberAttributes Attrs);
|
|
||||||
void printMemberAttributes(MemberAccess Access, MethodKind Kind,
|
|
||||||
MethodOptions Options);
|
|
||||||
|
|
||||||
private:
|
|
||||||
/// Forwards to the dumper, which holds the persistent state from visitation.
|
|
||||||
StringRef getTypeName(TypeIndex TI) {
|
|
||||||
return CVTD.getTypeName(TI);
|
|
||||||
}
|
|
||||||
|
|
||||||
void printTypeIndex(StringRef FieldName, TypeIndex TI) {
|
|
||||||
CVTD.printTypeIndex(FieldName, TI);
|
|
||||||
}
|
|
||||||
|
|
||||||
CVTypeDumper &CVTD;
|
|
||||||
ScopedPrinter &W;
|
|
||||||
bool PrintRecordBytes = false;
|
|
||||||
|
|
||||||
/// Name of the current type. Only valid before visitTypeEnd.
|
|
||||||
StringRef Name;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // end anonymous namespace
|
|
||||||
|
|
||||||
static StringRef getLeafTypeName(TypeLeafKind LT) {
|
static StringRef getLeafTypeName(TypeLeafKind LT) {
|
||||||
switch (LT) {
|
switch (LT) {
|
||||||
#define TYPE_RECORD(ename, value, name) \
|
#define TYPE_RECORD(ename, value, name) \
|
||||||
|
@ -250,39 +201,44 @@ static StringRef getLeafTypeName(TypeLeafKind LT) {
|
||||||
return "UnknownLeaf";
|
return "UnknownLeaf";
|
||||||
}
|
}
|
||||||
|
|
||||||
void CVTypeDumperImpl::visitTypeBegin(const CVRecord<TypeLeafKind> &Rec) {
|
Error CVTypeDumper::visitTypeBegin(const CVRecord<TypeLeafKind> &Record) {
|
||||||
// Reset Name to the empty string. If the visitor sets it, we know it.
|
// Reset Name to the empty string. If the visitor sets it, we know it.
|
||||||
Name = "";
|
Name = "";
|
||||||
|
|
||||||
W.startLine() << getLeafTypeName(Rec.Type) << " ("
|
W->startLine() << getLeafTypeName(Record.Type) << " ("
|
||||||
<< HexNumber(CVTD.getNextTypeIndex()) << ") {\n";
|
<< HexNumber(getNextTypeIndex()) << ") {\n";
|
||||||
W.indent();
|
W->indent();
|
||||||
W.printEnum("TypeLeafKind", unsigned(Rec.Type), makeArrayRef(LeafTypeNames));
|
W->printEnum("TypeLeafKind", unsigned(Record.Type),
|
||||||
|
makeArrayRef(LeafTypeNames));
|
||||||
|
return Error::success();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CVTypeDumperImpl::visitTypeEnd(const CVRecord<TypeLeafKind> &Rec) {
|
Error CVTypeDumper::visitTypeEnd(const CVRecord<TypeLeafKind> &Record) {
|
||||||
// Always record some name for every type, even if Name is empty. CVUDTNames
|
// Always record some name for every type, even if Name is empty. CVUDTNames
|
||||||
// is indexed by type index, and must have one entry for every type.
|
// is indexed by type index, and must have one entry for every type.
|
||||||
CVTD.recordType(Name);
|
recordType(Name);
|
||||||
if (PrintRecordBytes)
|
|
||||||
W.printBinaryBlock("LeafData", getBytesAsCharacters(Rec.Data));
|
|
||||||
|
|
||||||
W.unindent();
|
if (PrintRecordBytes)
|
||||||
W.startLine() << "}\n";
|
W->printBinaryBlock("LeafData", getBytesAsCharacters(Record.Data));
|
||||||
|
|
||||||
|
W->unindent();
|
||||||
|
W->startLine() << "}\n";
|
||||||
|
return Error::success();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CVTypeDumperImpl::visitStringId(StringIdRecord &String) {
|
Error CVTypeDumper::visitStringId(StringIdRecord &String) {
|
||||||
printTypeIndex("Id", String.getId());
|
printTypeIndex("Id", String.getId());
|
||||||
W.printString("StringData", String.getString());
|
W->printString("StringData", String.getString());
|
||||||
// Put this in CVUDTNames so it gets printed with LF_UDT_SRC_LINE.
|
// Put this in CVUDTNames so it gets printed with LF_UDT_SRC_LINE.
|
||||||
Name = String.getString();
|
Name = String.getString();
|
||||||
|
return Error::success();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CVTypeDumperImpl::visitArgList(ArgListRecord &Args) {
|
Error CVTypeDumper::visitArgList(ArgListRecord &Args) {
|
||||||
auto Indices = Args.getIndices();
|
auto Indices = Args.getIndices();
|
||||||
uint32_t Size = Indices.size();
|
uint32_t Size = Indices.size();
|
||||||
W.printNumber("NumArgs", Size);
|
W->printNumber("NumArgs", Size);
|
||||||
ListScope Arguments(W, "Arguments");
|
ListScope Arguments(*W, "Arguments");
|
||||||
SmallString<256> TypeName("(");
|
SmallString<256> TypeName("(");
|
||||||
for (uint32_t I = 0; I < Size; ++I) {
|
for (uint32_t I = 0; I < Size; ++I) {
|
||||||
printTypeIndex("ArgType", Indices[I]);
|
printTypeIndex("ArgType", Indices[I]);
|
||||||
|
@ -292,77 +248,84 @@ void CVTypeDumperImpl::visitArgList(ArgListRecord &Args) {
|
||||||
TypeName.append(", ");
|
TypeName.append(", ");
|
||||||
}
|
}
|
||||||
TypeName.push_back(')');
|
TypeName.push_back(')');
|
||||||
Name = CVTD.saveName(TypeName);
|
Name = saveName(TypeName);
|
||||||
|
return Error::success();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CVTypeDumperImpl::visitClass(ClassRecord &Class) {
|
Error CVTypeDumper::visitClass(ClassRecord &Class) {
|
||||||
uint16_t Props = static_cast<uint16_t>(Class.getOptions());
|
uint16_t Props = static_cast<uint16_t>(Class.getOptions());
|
||||||
W.printNumber("MemberCount", Class.getMemberCount());
|
W->printNumber("MemberCount", Class.getMemberCount());
|
||||||
W.printFlags("Properties", Props, makeArrayRef(ClassOptionNames));
|
W->printFlags("Properties", Props, makeArrayRef(ClassOptionNames));
|
||||||
printTypeIndex("FieldList", Class.getFieldList());
|
printTypeIndex("FieldList", Class.getFieldList());
|
||||||
printTypeIndex("DerivedFrom", Class.getDerivationList());
|
printTypeIndex("DerivedFrom", Class.getDerivationList());
|
||||||
printTypeIndex("VShape", Class.getVTableShape());
|
printTypeIndex("VShape", Class.getVTableShape());
|
||||||
W.printNumber("SizeOf", Class.getSize());
|
W->printNumber("SizeOf", Class.getSize());
|
||||||
W.printString("Name", Class.getName());
|
W->printString("Name", Class.getName());
|
||||||
if (Props & uint16_t(ClassOptions::HasUniqueName))
|
if (Props & uint16_t(ClassOptions::HasUniqueName))
|
||||||
W.printString("LinkageName", Class.getUniqueName());
|
W->printString("LinkageName", Class.getUniqueName());
|
||||||
Name = Class.getName();
|
Name = Class.getName();
|
||||||
|
return Error::success();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CVTypeDumperImpl::visitUnion(UnionRecord &Union) {
|
Error CVTypeDumper::visitUnion(UnionRecord &Union) {
|
||||||
uint16_t Props = static_cast<uint16_t>(Union.getOptions());
|
uint16_t Props = static_cast<uint16_t>(Union.getOptions());
|
||||||
W.printNumber("MemberCount", Union.getMemberCount());
|
W->printNumber("MemberCount", Union.getMemberCount());
|
||||||
W.printFlags("Properties", Props, makeArrayRef(ClassOptionNames));
|
W->printFlags("Properties", Props, makeArrayRef(ClassOptionNames));
|
||||||
printTypeIndex("FieldList", Union.getFieldList());
|
printTypeIndex("FieldList", Union.getFieldList());
|
||||||
W.printNumber("SizeOf", Union.getSize());
|
W->printNumber("SizeOf", Union.getSize());
|
||||||
W.printString("Name", Union.getName());
|
W->printString("Name", Union.getName());
|
||||||
if (Props & uint16_t(ClassOptions::HasUniqueName))
|
if (Props & uint16_t(ClassOptions::HasUniqueName))
|
||||||
W.printString("LinkageName", Union.getUniqueName());
|
W->printString("LinkageName", Union.getUniqueName());
|
||||||
Name = Union.getName();
|
Name = Union.getName();
|
||||||
|
return Error::success();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CVTypeDumperImpl::visitEnum(EnumRecord &Enum) {
|
Error CVTypeDumper::visitEnum(EnumRecord &Enum) {
|
||||||
W.printNumber("NumEnumerators", Enum.getMemberCount());
|
W->printNumber("NumEnumerators", Enum.getMemberCount());
|
||||||
W.printFlags("Properties", uint16_t(Enum.getOptions()),
|
W->printFlags("Properties", uint16_t(Enum.getOptions()),
|
||||||
makeArrayRef(ClassOptionNames));
|
makeArrayRef(ClassOptionNames));
|
||||||
printTypeIndex("UnderlyingType", Enum.getUnderlyingType());
|
printTypeIndex("UnderlyingType", Enum.getUnderlyingType());
|
||||||
printTypeIndex("FieldListType", Enum.getFieldList());
|
printTypeIndex("FieldListType", Enum.getFieldList());
|
||||||
W.printString("Name", Enum.getName());
|
W->printString("Name", Enum.getName());
|
||||||
Name = Enum.getName();
|
Name = Enum.getName();
|
||||||
|
return Error::success();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CVTypeDumperImpl::visitArray(ArrayRecord &AT) {
|
Error CVTypeDumper::visitArray(ArrayRecord &AT) {
|
||||||
printTypeIndex("ElementType", AT.getElementType());
|
printTypeIndex("ElementType", AT.getElementType());
|
||||||
printTypeIndex("IndexType", AT.getIndexType());
|
printTypeIndex("IndexType", AT.getIndexType());
|
||||||
W.printNumber("SizeOf", AT.getSize());
|
W->printNumber("SizeOf", AT.getSize());
|
||||||
W.printString("Name", AT.getName());
|
W->printString("Name", AT.getName());
|
||||||
Name = AT.getName();
|
Name = AT.getName();
|
||||||
|
return Error::success();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CVTypeDumperImpl::visitVFTable(VFTableRecord &VFT) {
|
Error CVTypeDumper::visitVFTable(VFTableRecord &VFT) {
|
||||||
printTypeIndex("CompleteClass", VFT.getCompleteClass());
|
printTypeIndex("CompleteClass", VFT.getCompleteClass());
|
||||||
printTypeIndex("OverriddenVFTable", VFT.getOverriddenVTable());
|
printTypeIndex("OverriddenVFTable", VFT.getOverriddenVTable());
|
||||||
W.printHex("VFPtrOffset", VFT.getVFPtrOffset());
|
W->printHex("VFPtrOffset", VFT.getVFPtrOffset());
|
||||||
W.printString("VFTableName", VFT.getName());
|
W->printString("VFTableName", VFT.getName());
|
||||||
for (auto N : VFT.getMethodNames())
|
for (auto N : VFT.getMethodNames())
|
||||||
W.printString("MethodName", N);
|
W->printString("MethodName", N);
|
||||||
Name = VFT.getName();
|
Name = VFT.getName();
|
||||||
|
return Error::success();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CVTypeDumperImpl::visitMemberFuncId(MemberFuncIdRecord &Id) {
|
Error CVTypeDumper::visitMemberFuncId(MemberFuncIdRecord &Id) {
|
||||||
printTypeIndex("ClassType", Id.getClassType());
|
printTypeIndex("ClassType", Id.getClassType());
|
||||||
printTypeIndex("FunctionType", Id.getFunctionType());
|
printTypeIndex("FunctionType", Id.getFunctionType());
|
||||||
W.printString("Name", Id.getName());
|
W->printString("Name", Id.getName());
|
||||||
Name = Id.getName();
|
Name = Id.getName();
|
||||||
|
return Error::success();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CVTypeDumperImpl::visitProcedure(ProcedureRecord &Proc) {
|
Error CVTypeDumper::visitProcedure(ProcedureRecord &Proc) {
|
||||||
printTypeIndex("ReturnType", Proc.getReturnType());
|
printTypeIndex("ReturnType", Proc.getReturnType());
|
||||||
W.printEnum("CallingConvention", uint8_t(Proc.getCallConv()),
|
W->printEnum("CallingConvention", uint8_t(Proc.getCallConv()),
|
||||||
makeArrayRef(CallingConventions));
|
makeArrayRef(CallingConventions));
|
||||||
W.printFlags("FunctionOptions", uint8_t(Proc.getOptions()),
|
W->printFlags("FunctionOptions", uint8_t(Proc.getOptions()),
|
||||||
makeArrayRef(FunctionOptionEnum));
|
makeArrayRef(FunctionOptionEnum));
|
||||||
W.printNumber("NumParameters", Proc.getParameterCount());
|
W->printNumber("NumParameters", Proc.getParameterCount());
|
||||||
printTypeIndex("ArgListType", Proc.getArgumentList());
|
printTypeIndex("ArgListType", Proc.getArgumentList());
|
||||||
|
|
||||||
StringRef ReturnTypeName = getTypeName(Proc.getReturnType());
|
StringRef ReturnTypeName = getTypeName(Proc.getReturnType());
|
||||||
|
@ -370,20 +333,21 @@ void CVTypeDumperImpl::visitProcedure(ProcedureRecord &Proc) {
|
||||||
SmallString<256> TypeName(ReturnTypeName);
|
SmallString<256> TypeName(ReturnTypeName);
|
||||||
TypeName.push_back(' ');
|
TypeName.push_back(' ');
|
||||||
TypeName.append(ArgListTypeName);
|
TypeName.append(ArgListTypeName);
|
||||||
Name = CVTD.saveName(TypeName);
|
Name = saveName(TypeName);
|
||||||
|
return Error::success();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CVTypeDumperImpl::visitMemberFunction(MemberFunctionRecord &MF) {
|
Error CVTypeDumper::visitMemberFunction(MemberFunctionRecord &MF) {
|
||||||
printTypeIndex("ReturnType", MF.getReturnType());
|
printTypeIndex("ReturnType", MF.getReturnType());
|
||||||
printTypeIndex("ClassType", MF.getClassType());
|
printTypeIndex("ClassType", MF.getClassType());
|
||||||
printTypeIndex("ThisType", MF.getThisType());
|
printTypeIndex("ThisType", MF.getThisType());
|
||||||
W.printEnum("CallingConvention", uint8_t(MF.getCallConv()),
|
W->printEnum("CallingConvention", uint8_t(MF.getCallConv()),
|
||||||
makeArrayRef(CallingConventions));
|
makeArrayRef(CallingConventions));
|
||||||
W.printFlags("FunctionOptions", uint8_t(MF.getOptions()),
|
W->printFlags("FunctionOptions", uint8_t(MF.getOptions()),
|
||||||
makeArrayRef(FunctionOptionEnum));
|
makeArrayRef(FunctionOptionEnum));
|
||||||
W.printNumber("NumParameters", MF.getParameterCount());
|
W->printNumber("NumParameters", MF.getParameterCount());
|
||||||
printTypeIndex("ArgListType", MF.getArgumentList());
|
printTypeIndex("ArgListType", MF.getArgumentList());
|
||||||
W.printNumber("ThisAdjustment", MF.getThisPointerAdjustment());
|
W->printNumber("ThisAdjustment", MF.getThisPointerAdjustment());
|
||||||
|
|
||||||
StringRef ReturnTypeName = getTypeName(MF.getReturnType());
|
StringRef ReturnTypeName = getTypeName(MF.getReturnType());
|
||||||
StringRef ClassTypeName = getTypeName(MF.getClassType());
|
StringRef ClassTypeName = getTypeName(MF.getClassType());
|
||||||
|
@ -393,52 +357,56 @@ void CVTypeDumperImpl::visitMemberFunction(MemberFunctionRecord &MF) {
|
||||||
TypeName.append(ClassTypeName);
|
TypeName.append(ClassTypeName);
|
||||||
TypeName.append("::");
|
TypeName.append("::");
|
||||||
TypeName.append(ArgListTypeName);
|
TypeName.append(ArgListTypeName);
|
||||||
Name = CVTD.saveName(TypeName);
|
Name = saveName(TypeName);
|
||||||
|
return Error::success();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CVTypeDumperImpl::visitMethodOverloadList(
|
Error CVTypeDumper::visitMethodOverloadList(
|
||||||
MethodOverloadListRecord &MethodList) {
|
MethodOverloadListRecord &MethodList) {
|
||||||
for (auto &M : MethodList.getMethods()) {
|
for (auto &M : MethodList.getMethods()) {
|
||||||
ListScope S(W, "Method");
|
ListScope S(*W, "Method");
|
||||||
printMemberAttributes(M.getAccess(), M.getKind(), M.getOptions());
|
printMemberAttributes(M.getAccess(), M.getKind(), M.getOptions());
|
||||||
printTypeIndex("Type", M.getType());
|
printTypeIndex("Type", M.getType());
|
||||||
if (M.isIntroducingVirtual())
|
if (M.isIntroducingVirtual())
|
||||||
W.printHex("VFTableOffset", M.getVFTableOffset());
|
W->printHex("VFTableOffset", M.getVFTableOffset());
|
||||||
}
|
}
|
||||||
|
return Error::success();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CVTypeDumperImpl::visitFuncId(FuncIdRecord &Func) {
|
Error CVTypeDumper::visitFuncId(FuncIdRecord &Func) {
|
||||||
printTypeIndex("ParentScope", Func.getParentScope());
|
printTypeIndex("ParentScope", Func.getParentScope());
|
||||||
printTypeIndex("FunctionType", Func.getFunctionType());
|
printTypeIndex("FunctionType", Func.getFunctionType());
|
||||||
W.printString("Name", Func.getName());
|
W->printString("Name", Func.getName());
|
||||||
Name = Func.getName();
|
Name = Func.getName();
|
||||||
|
return Error::success();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CVTypeDumperImpl::visitTypeServer2(TypeServer2Record &TS) {
|
Error CVTypeDumper::visitTypeServer2(TypeServer2Record &TS) {
|
||||||
W.printBinary("Signature", TS.getGuid());
|
W->printBinary("Signature", TS.getGuid());
|
||||||
W.printNumber("Age", TS.getAge());
|
W->printNumber("Age", TS.getAge());
|
||||||
W.printString("Name", TS.getName());
|
W->printString("Name", TS.getName());
|
||||||
Name = TS.getName();
|
Name = TS.getName();
|
||||||
|
return Error::success();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CVTypeDumperImpl::visitPointer(PointerRecord &Ptr) {
|
Error CVTypeDumper::visitPointer(PointerRecord &Ptr) {
|
||||||
printTypeIndex("PointeeType", Ptr.getReferentType());
|
printTypeIndex("PointeeType", Ptr.getReferentType());
|
||||||
W.printHex("PointerAttributes", uint32_t(Ptr.getOptions()));
|
W->printHex("PointerAttributes", uint32_t(Ptr.getOptions()));
|
||||||
W.printEnum("PtrType", unsigned(Ptr.getPointerKind()),
|
W->printEnum("PtrType", unsigned(Ptr.getPointerKind()),
|
||||||
makeArrayRef(PtrKindNames));
|
makeArrayRef(PtrKindNames));
|
||||||
W.printEnum("PtrMode", unsigned(Ptr.getMode()), makeArrayRef(PtrModeNames));
|
W->printEnum("PtrMode", unsigned(Ptr.getMode()), makeArrayRef(PtrModeNames));
|
||||||
|
|
||||||
W.printNumber("IsFlat", Ptr.isFlat());
|
W->printNumber("IsFlat", Ptr.isFlat());
|
||||||
W.printNumber("IsConst", Ptr.isConst());
|
W->printNumber("IsConst", Ptr.isConst());
|
||||||
W.printNumber("IsVolatile", Ptr.isVolatile());
|
W->printNumber("IsVolatile", Ptr.isVolatile());
|
||||||
W.printNumber("IsUnaligned", Ptr.isUnaligned());
|
W->printNumber("IsUnaligned", Ptr.isUnaligned());
|
||||||
|
|
||||||
if (Ptr.isPointerToMember()) {
|
if (Ptr.isPointerToMember()) {
|
||||||
const MemberPointerInfo &MI = Ptr.getMemberInfo();
|
const MemberPointerInfo &MI = Ptr.getMemberInfo();
|
||||||
|
|
||||||
printTypeIndex("ClassType", MI.getContainingType());
|
printTypeIndex("ClassType", MI.getContainingType());
|
||||||
W.printEnum("Representation", uint16_t(MI.getRepresentation()),
|
W->printEnum("Representation", uint16_t(MI.getRepresentation()),
|
||||||
makeArrayRef(PtrMemberRepNames));
|
makeArrayRef(PtrMemberRepNames));
|
||||||
|
|
||||||
StringRef PointeeName = getTypeName(Ptr.getReferentType());
|
StringRef PointeeName = getTypeName(Ptr.getReferentType());
|
||||||
StringRef ClassName = getTypeName(MI.getContainingType());
|
StringRef ClassName = getTypeName(MI.getContainingType());
|
||||||
|
@ -446,7 +414,7 @@ void CVTypeDumperImpl::visitPointer(PointerRecord &Ptr) {
|
||||||
TypeName.push_back(' ');
|
TypeName.push_back(' ');
|
||||||
TypeName.append(ClassName);
|
TypeName.append(ClassName);
|
||||||
TypeName.append("::*");
|
TypeName.append("::*");
|
||||||
Name = CVTD.saveName(TypeName);
|
Name = saveName(TypeName);
|
||||||
} else {
|
} else {
|
||||||
SmallString<256> TypeName;
|
SmallString<256> TypeName;
|
||||||
if (Ptr.isConst())
|
if (Ptr.isConst())
|
||||||
|
@ -466,14 +434,15 @@ void CVTypeDumperImpl::visitPointer(PointerRecord &Ptr) {
|
||||||
TypeName.append("*");
|
TypeName.append("*");
|
||||||
|
|
||||||
if (!TypeName.empty())
|
if (!TypeName.empty())
|
||||||
Name = CVTD.saveName(TypeName);
|
Name = saveName(TypeName);
|
||||||
}
|
}
|
||||||
|
return Error::success();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CVTypeDumperImpl::visitModifier(ModifierRecord &Mod) {
|
Error CVTypeDumper::visitModifier(ModifierRecord &Mod) {
|
||||||
uint16_t Mods = static_cast<uint16_t>(Mod.getModifiers());
|
uint16_t Mods = static_cast<uint16_t>(Mod.getModifiers());
|
||||||
printTypeIndex("ModifiedType", Mod.getModifiedType());
|
printTypeIndex("ModifiedType", Mod.getModifiedType());
|
||||||
W.printFlags("Modifiers", Mods, makeArrayRef(TypeModifierNames));
|
W->printFlags("Modifiers", Mods, makeArrayRef(TypeModifierNames));
|
||||||
|
|
||||||
StringRef ModifiedName = getTypeName(Mod.getModifiedType());
|
StringRef ModifiedName = getTypeName(Mod.getModifiedType());
|
||||||
SmallString<256> TypeName;
|
SmallString<256> TypeName;
|
||||||
|
@ -484,146 +453,162 @@ void CVTypeDumperImpl::visitModifier(ModifierRecord &Mod) {
|
||||||
if (Mods & uint16_t(ModifierOptions::Unaligned))
|
if (Mods & uint16_t(ModifierOptions::Unaligned))
|
||||||
TypeName.append("__unaligned ");
|
TypeName.append("__unaligned ");
|
||||||
TypeName.append(ModifiedName);
|
TypeName.append(ModifiedName);
|
||||||
Name = CVTD.saveName(TypeName);
|
Name = saveName(TypeName);
|
||||||
|
return Error::success();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CVTypeDumperImpl::visitBitField(BitFieldRecord &BitField) {
|
Error CVTypeDumper::visitBitField(BitFieldRecord &BitField) {
|
||||||
printTypeIndex("Type", BitField.getType());
|
printTypeIndex("Type", BitField.getType());
|
||||||
W.printNumber("BitSize", BitField.getBitSize());
|
W->printNumber("BitSize", BitField.getBitSize());
|
||||||
W.printNumber("BitOffset", BitField.getBitOffset());
|
W->printNumber("BitOffset", BitField.getBitOffset());
|
||||||
|
return Error::success();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CVTypeDumperImpl::visitVFTableShape(VFTableShapeRecord &Shape) {
|
Error CVTypeDumper::visitVFTableShape(VFTableShapeRecord &Shape) {
|
||||||
W.printNumber("VFEntryCount", Shape.getEntryCount());
|
W->printNumber("VFEntryCount", Shape.getEntryCount());
|
||||||
|
return Error::success();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CVTypeDumperImpl::visitUdtSourceLine(UdtSourceLineRecord &Line) {
|
Error CVTypeDumper::visitUdtSourceLine(UdtSourceLineRecord &Line) {
|
||||||
printTypeIndex("UDT", Line.getUDT());
|
printTypeIndex("UDT", Line.getUDT());
|
||||||
printTypeIndex("SourceFile", Line.getSourceFile());
|
printTypeIndex("SourceFile", Line.getSourceFile());
|
||||||
W.printNumber("LineNumber", Line.getLineNumber());
|
W->printNumber("LineNumber", Line.getLineNumber());
|
||||||
|
return Error::success();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CVTypeDumperImpl::visitUdtModSourceLine(UdtModSourceLineRecord &Line) {
|
Error CVTypeDumper::visitUdtModSourceLine(UdtModSourceLineRecord &Line) {
|
||||||
printTypeIndex("UDT", Line.getUDT());
|
printTypeIndex("UDT", Line.getUDT());
|
||||||
printTypeIndex("SourceFile", Line.getSourceFile());
|
printTypeIndex("SourceFile", Line.getSourceFile());
|
||||||
W.printNumber("LineNumber", Line.getLineNumber());
|
W->printNumber("LineNumber", Line.getLineNumber());
|
||||||
W.printNumber("Module", Line.getModule());
|
W->printNumber("Module", Line.getModule());
|
||||||
|
return Error::success();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CVTypeDumperImpl::visitBuildInfo(BuildInfoRecord &Args) {
|
Error CVTypeDumper::visitBuildInfo(BuildInfoRecord &Args) {
|
||||||
W.printNumber("NumArgs", static_cast<uint32_t>(Args.getArgs().size()));
|
W->printNumber("NumArgs", static_cast<uint32_t>(Args.getArgs().size()));
|
||||||
|
|
||||||
ListScope Arguments(W, "Arguments");
|
ListScope Arguments(*W, "Arguments");
|
||||||
for (auto Arg : Args.getArgs()) {
|
for (auto Arg : Args.getArgs()) {
|
||||||
printTypeIndex("ArgType", Arg);
|
printTypeIndex("ArgType", Arg);
|
||||||
}
|
}
|
||||||
|
return Error::success();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CVTypeDumperImpl::printMemberAttributes(MemberAttributes Attrs) {
|
void CVTypeDumper::printMemberAttributes(MemberAttributes Attrs) {
|
||||||
return printMemberAttributes(Attrs.getAccess(), Attrs.getMethodKind(),
|
return printMemberAttributes(Attrs.getAccess(), Attrs.getMethodKind(),
|
||||||
Attrs.getFlags());
|
Attrs.getFlags());
|
||||||
}
|
}
|
||||||
|
|
||||||
void CVTypeDumperImpl::printMemberAttributes(MemberAccess Access,
|
void CVTypeDumper::printMemberAttributes(MemberAccess Access, MethodKind Kind,
|
||||||
MethodKind Kind,
|
MethodOptions Options) {
|
||||||
MethodOptions Options) {
|
W->printEnum("AccessSpecifier", uint8_t(Access),
|
||||||
W.printEnum("AccessSpecifier", uint8_t(Access),
|
makeArrayRef(MemberAccessNames));
|
||||||
makeArrayRef(MemberAccessNames));
|
|
||||||
// Data members will be vanilla. Don't try to print a method kind for them.
|
// Data members will be vanilla. Don't try to print a method kind for them.
|
||||||
if (Kind != MethodKind::Vanilla)
|
if (Kind != MethodKind::Vanilla)
|
||||||
W.printEnum("MethodKind", unsigned(Kind), makeArrayRef(MemberKindNames));
|
W->printEnum("MethodKind", unsigned(Kind), makeArrayRef(MemberKindNames));
|
||||||
if (Options != MethodOptions::None) {
|
if (Options != MethodOptions::None) {
|
||||||
W.printFlags("MethodOptions", unsigned(Options),
|
W->printFlags("MethodOptions", unsigned(Options),
|
||||||
makeArrayRef(MethodOptionNames));
|
makeArrayRef(MethodOptionNames));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CVTypeDumperImpl::visitUnknownMember(TypeLeafKind Leaf) {
|
Error CVTypeDumper::visitUnknownMember(const CVRecord<TypeLeafKind> &Record) {
|
||||||
W.printHex("UnknownMember", unsigned(Leaf));
|
W->printHex("UnknownMember", unsigned(Record.Type));
|
||||||
|
return Error::success();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CVTypeDumperImpl::visitUnknownType(const CVRecord<TypeLeafKind> &Rec) {
|
Error CVTypeDumper::visitUnknownType(const CVRecord<TypeLeafKind> &Record) {
|
||||||
DictScope S(W, "UnknownType");
|
DictScope S(*W, "UnknownType");
|
||||||
W.printEnum("Kind", uint16_t(Rec.Type), makeArrayRef(LeafTypeNames));
|
W->printEnum("Kind", uint16_t(Record.Type), makeArrayRef(LeafTypeNames));
|
||||||
W.printNumber("Length", uint32_t(Rec.Data.size()));
|
W->printNumber("Length", uint32_t(Record.Data.size()));
|
||||||
|
return Error::success();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CVTypeDumperImpl::visitNestedType(NestedTypeRecord &Nested) {
|
Error CVTypeDumper::visitNestedType(NestedTypeRecord &Nested) {
|
||||||
DictScope S(W, "NestedType");
|
DictScope S(*W, "NestedType");
|
||||||
printTypeIndex("Type", Nested.getNestedType());
|
printTypeIndex("Type", Nested.getNestedType());
|
||||||
W.printString("Name", Nested.getName());
|
W->printString("Name", Nested.getName());
|
||||||
Name = Nested.getName();
|
Name = Nested.getName();
|
||||||
|
return Error::success();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CVTypeDumperImpl::visitOneMethod(OneMethodRecord &Method) {
|
Error CVTypeDumper::visitOneMethod(OneMethodRecord &Method) {
|
||||||
DictScope S(W, "OneMethod");
|
DictScope S(*W, "OneMethod");
|
||||||
MethodKind K = Method.getKind();
|
MethodKind K = Method.getKind();
|
||||||
printMemberAttributes(Method.getAccess(), K, Method.getOptions());
|
printMemberAttributes(Method.getAccess(), K, Method.getOptions());
|
||||||
printTypeIndex("Type", Method.getType());
|
printTypeIndex("Type", Method.getType());
|
||||||
// If virtual, then read the vftable offset.
|
// If virtual, then read the vftable offset.
|
||||||
if (Method.isIntroducingVirtual())
|
if (Method.isIntroducingVirtual())
|
||||||
W.printHex("VFTableOffset", Method.getVFTableOffset());
|
W->printHex("VFTableOffset", Method.getVFTableOffset());
|
||||||
W.printString("Name", Method.getName());
|
W->printString("Name", Method.getName());
|
||||||
Name = Method.getName();
|
Name = Method.getName();
|
||||||
|
return Error::success();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CVTypeDumperImpl::visitOverloadedMethod(OverloadedMethodRecord &Method) {
|
Error CVTypeDumper::visitOverloadedMethod(OverloadedMethodRecord &Method) {
|
||||||
DictScope S(W, "OverloadedMethod");
|
DictScope S(*W, "OverloadedMethod");
|
||||||
W.printHex("MethodCount", Method.getNumOverloads());
|
W->printHex("MethodCount", Method.getNumOverloads());
|
||||||
printTypeIndex("MethodListIndex", Method.getMethodList());
|
printTypeIndex("MethodListIndex", Method.getMethodList());
|
||||||
W.printString("Name", Method.getName());
|
W->printString("Name", Method.getName());
|
||||||
Name = Method.getName();
|
Name = Method.getName();
|
||||||
|
return Error::success();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CVTypeDumperImpl::visitDataMember(DataMemberRecord &Field) {
|
Error CVTypeDumper::visitDataMember(DataMemberRecord &Field) {
|
||||||
DictScope S(W, "DataMember");
|
DictScope S(*W, "DataMember");
|
||||||
printMemberAttributes(Field.getAccess(), MethodKind::Vanilla,
|
printMemberAttributes(Field.getAccess(), MethodKind::Vanilla,
|
||||||
MethodOptions::None);
|
MethodOptions::None);
|
||||||
printTypeIndex("Type", Field.getType());
|
printTypeIndex("Type", Field.getType());
|
||||||
W.printHex("FieldOffset", Field.getFieldOffset());
|
W->printHex("FieldOffset", Field.getFieldOffset());
|
||||||
W.printString("Name", Field.getName());
|
W->printString("Name", Field.getName());
|
||||||
Name = Field.getName();
|
Name = Field.getName();
|
||||||
|
return Error::success();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CVTypeDumperImpl::visitStaticDataMember(StaticDataMemberRecord &Field) {
|
Error CVTypeDumper::visitStaticDataMember(StaticDataMemberRecord &Field) {
|
||||||
DictScope S(W, "StaticDataMember");
|
DictScope S(*W, "StaticDataMember");
|
||||||
printMemberAttributes(Field.getAccess(), MethodKind::Vanilla,
|
printMemberAttributes(Field.getAccess(), MethodKind::Vanilla,
|
||||||
MethodOptions::None);
|
MethodOptions::None);
|
||||||
printTypeIndex("Type", Field.getType());
|
printTypeIndex("Type", Field.getType());
|
||||||
W.printString("Name", Field.getName());
|
W->printString("Name", Field.getName());
|
||||||
Name = Field.getName();
|
Name = Field.getName();
|
||||||
|
return Error::success();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CVTypeDumperImpl::visitVFPtr(VFPtrRecord &VFTable) {
|
Error CVTypeDumper::visitVFPtr(VFPtrRecord &VFTable) {
|
||||||
DictScope S(W, "VFPtr");
|
DictScope S(*W, "VFPtr");
|
||||||
printTypeIndex("Type", VFTable.getType());
|
printTypeIndex("Type", VFTable.getType());
|
||||||
|
return Error::success();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CVTypeDumperImpl::visitEnumerator(EnumeratorRecord &Enum) {
|
Error CVTypeDumper::visitEnumerator(EnumeratorRecord &Enum) {
|
||||||
DictScope S(W, "Enumerator");
|
DictScope S(*W, "Enumerator");
|
||||||
printMemberAttributes(Enum.getAccess(), MethodKind::Vanilla,
|
printMemberAttributes(Enum.getAccess(), MethodKind::Vanilla,
|
||||||
MethodOptions::None);
|
MethodOptions::None);
|
||||||
W.printNumber("EnumValue", Enum.getValue());
|
W->printNumber("EnumValue", Enum.getValue());
|
||||||
W.printString("Name", Enum.getName());
|
W->printString("Name", Enum.getName());
|
||||||
Name = Enum.getName();
|
Name = Enum.getName();
|
||||||
|
return Error::success();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CVTypeDumperImpl::visitBaseClass(BaseClassRecord &Base) {
|
Error CVTypeDumper::visitBaseClass(BaseClassRecord &Base) {
|
||||||
DictScope S(W, "BaseClass");
|
DictScope S(*W, "BaseClass");
|
||||||
printMemberAttributes(Base.getAccess(), MethodKind::Vanilla,
|
printMemberAttributes(Base.getAccess(), MethodKind::Vanilla,
|
||||||
MethodOptions::None);
|
MethodOptions::None);
|
||||||
printTypeIndex("BaseType", Base.getBaseType());
|
printTypeIndex("BaseType", Base.getBaseType());
|
||||||
W.printHex("BaseOffset", Base.getBaseOffset());
|
W->printHex("BaseOffset", Base.getBaseOffset());
|
||||||
|
return Error::success();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CVTypeDumperImpl::visitVirtualBaseClass(VirtualBaseClassRecord &Base) {
|
Error CVTypeDumper::visitVirtualBaseClass(VirtualBaseClassRecord &Base) {
|
||||||
DictScope S(W, "VirtualBaseClass");
|
DictScope S(*W, "VirtualBaseClass");
|
||||||
printMemberAttributes(Base.getAccess(), MethodKind::Vanilla,
|
printMemberAttributes(Base.getAccess(), MethodKind::Vanilla,
|
||||||
MethodOptions::None);
|
MethodOptions::None);
|
||||||
printTypeIndex("BaseType", Base.getBaseType());
|
printTypeIndex("BaseType", Base.getBaseType());
|
||||||
printTypeIndex("VBPtrType", Base.getVBPtrType());
|
printTypeIndex("VBPtrType", Base.getVBPtrType());
|
||||||
W.printHex("VBPtrOffset", Base.getVBPtrOffset());
|
W->printHex("VBPtrOffset", Base.getVBPtrOffset());
|
||||||
W.printHex("VBTableIndex", Base.getVTableIndex());
|
W->printHex("VBTableIndex", Base.getVTableIndex());
|
||||||
|
return Error::success();
|
||||||
}
|
}
|
||||||
|
|
||||||
StringRef CVTypeDumper::getTypeName(TypeIndex TI) {
|
StringRef CVTypeDumper::getTypeName(TypeIndex TI) {
|
||||||
|
@ -663,28 +648,29 @@ void CVTypeDumper::printTypeIndex(StringRef FieldName, TypeIndex TI) {
|
||||||
W->printHex(FieldName, TI.getIndex());
|
W->printHex(FieldName, TI.getIndex());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CVTypeDumper::dump(const CVRecord<TypeLeafKind> &Record) {
|
Error CVTypeDumper::dump(const CVRecord<TypeLeafKind> &Record) {
|
||||||
assert(W && "printer should not be null");
|
assert(W && "printer should not be null");
|
||||||
CVTypeDumperImpl Dumper(*this, *W, PrintRecordBytes);
|
CVTypeVisitor Visitor(*this);
|
||||||
Dumper.visitTypeRecord(Record);
|
|
||||||
return !Dumper.hadError();
|
if (auto EC = Visitor.visitTypeRecord(Record))
|
||||||
|
return EC;
|
||||||
|
return Error::success();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CVTypeDumper::dump(const CVTypeArray &Types) {
|
Error CVTypeDumper::dump(const CVTypeArray &Types) {
|
||||||
assert(W && "printer should not be null");
|
assert(W && "printer should not be null");
|
||||||
CVTypeDumperImpl Dumper(*this, *W, PrintRecordBytes);
|
CVTypeVisitor Visitor(*this);
|
||||||
Dumper.visitTypeStream(Types);
|
if (auto EC = Visitor.visitTypeStream(Types))
|
||||||
return !Dumper.hadError();
|
return EC;
|
||||||
|
return Error::success();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CVTypeDumper::dump(ArrayRef<uint8_t> Data) {
|
Error CVTypeDumper::dump(ArrayRef<uint8_t> Data) {
|
||||||
ByteStream<> Stream(Data);
|
ByteStream<> Stream(Data);
|
||||||
CVTypeArray Types;
|
CVTypeArray Types;
|
||||||
StreamReader Reader(Stream);
|
StreamReader Reader(Stream);
|
||||||
if (auto EC = Reader.readArray(Types, Reader.getLength())) {
|
if (auto EC = Reader.readArray(Types, Reader.getLength()))
|
||||||
consumeError(std::move(EC));
|
return EC;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return dump(Types);
|
return dump(Types);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
#include "llvm/DebugInfo/CodeView/StreamRef.h"
|
#include "llvm/DebugInfo/CodeView/StreamRef.h"
|
||||||
#include "llvm/DebugInfo/CodeView/TypeIndex.h"
|
#include "llvm/DebugInfo/CodeView/TypeIndex.h"
|
||||||
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
|
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
|
||||||
|
#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h"
|
||||||
|
#include "llvm/Support/Error.h"
|
||||||
#include "llvm/Support/ScopedPrinter.h"
|
#include "llvm/Support/ScopedPrinter.h"
|
||||||
|
|
||||||
using namespace llvm;
|
using namespace llvm;
|
||||||
|
@ -49,32 +51,32 @@ namespace {
|
||||||
/// - If the type record already exists in the destination stream, discard it
|
/// - 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
|
/// and update the type index map to forward the source type index to the
|
||||||
/// existing destination type index.
|
/// existing destination type index.
|
||||||
class TypeStreamMerger : public CVTypeVisitor<TypeStreamMerger> {
|
class TypeStreamMerger : public TypeVisitorCallbacks {
|
||||||
public:
|
public:
|
||||||
TypeStreamMerger(TypeTableBuilder &DestStream) : DestStream(DestStream) {
|
TypeStreamMerger(TypeTableBuilder &DestStream) : DestStream(DestStream) {
|
||||||
assert(!hadError());
|
assert(!hadError());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// CVTypeVisitor overrides.
|
/// TypeVisitorCallbacks overrides.
|
||||||
#define TYPE_RECORD(EnumName, EnumVal, Name) \
|
#define TYPE_RECORD(EnumName, EnumVal, Name) \
|
||||||
void visit##Name(Name##Record &Record);
|
Error visit##Name(Name##Record &Record) override;
|
||||||
#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
|
|
||||||
#define MEMBER_RECORD(EnumName, EnumVal, Name) \
|
#define MEMBER_RECORD(EnumName, EnumVal, Name) \
|
||||||
void visit##Name(Name##Record &Record);
|
TYPE_RECORD(EnumName, EnumVal, Name)
|
||||||
|
#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
|
||||||
#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
|
#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
|
||||||
#include "llvm/DebugInfo/CodeView/TypeRecords.def"
|
#include "llvm/DebugInfo/CodeView/TypeRecords.def"
|
||||||
|
|
||||||
void visitUnknownType(const CVRecord<TypeLeafKind> &Record);
|
Error visitUnknownType(const CVRecord<TypeLeafKind> &Record) override;
|
||||||
|
|
||||||
void visitTypeBegin(const CVRecord<TypeLeafKind> &Record);
|
Error visitTypeBegin(const CVRecord<TypeLeafKind> &Record) override;
|
||||||
void visitTypeEnd(const CVRecord<TypeLeafKind> &Record);
|
Error visitTypeEnd(const CVRecord<TypeLeafKind> &Record) override;
|
||||||
|
|
||||||
void visitFieldList(TypeLeafKind Leaf, ArrayRef<uint8_t> FieldData);
|
Error visitFieldListEnd(const CVRecord<TypeLeafKind> &Record) override;
|
||||||
|
|
||||||
bool mergeStream(const CVTypeArray &Types);
|
bool mergeStream(const CVTypeArray &Types);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool hadError() { return FoundBadTypeIndex || CVTypeVisitor::hadError(); }
|
bool hadError() { return FoundBadTypeIndex; }
|
||||||
|
|
||||||
bool FoundBadTypeIndex = false;
|
bool FoundBadTypeIndex = false;
|
||||||
|
|
||||||
|
@ -91,45 +93,52 @@ private:
|
||||||
|
|
||||||
} // end anonymous namespace
|
} // end anonymous namespace
|
||||||
|
|
||||||
void TypeStreamMerger::visitTypeBegin(const CVRecord<TypeLeafKind> &Rec) {
|
Error TypeStreamMerger::visitTypeBegin(const CVRecord<TypeLeafKind> &Rec) {
|
||||||
BeginIndexMapSize = IndexMap.size();
|
BeginIndexMapSize = IndexMap.size();
|
||||||
|
return Error::success();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TypeStreamMerger::visitTypeEnd(const CVRecord<TypeLeafKind> &Rec) {
|
Error TypeStreamMerger::visitTypeEnd(const CVRecord<TypeLeafKind> &Rec) {
|
||||||
assert(IndexMap.size() == BeginIndexMapSize + 1);
|
assert(IndexMap.size() == BeginIndexMapSize + 1);
|
||||||
|
return Error::success();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TypeStreamMerger::visitFieldList(TypeLeafKind Leaf,
|
Error TypeStreamMerger::visitFieldListEnd(const CVRecord<TypeLeafKind> &Rec) {
|
||||||
ArrayRef<uint8_t> FieldData) {
|
|
||||||
CVTypeVisitor::visitFieldList(Leaf, FieldData);
|
|
||||||
IndexMap.push_back(DestStream.writeFieldList(FieldBuilder));
|
IndexMap.push_back(DestStream.writeFieldList(FieldBuilder));
|
||||||
FieldBuilder.reset();
|
FieldBuilder.reset();
|
||||||
|
return Error::success();
|
||||||
}
|
}
|
||||||
|
|
||||||
#define TYPE_RECORD(EnumName, EnumVal, Name) \
|
#define TYPE_RECORD(EnumName, EnumVal, Name) \
|
||||||
void TypeStreamMerger::visit##Name(Name##Record &Record) { \
|
Error TypeStreamMerger::visit##Name(Name##Record &Record) { \
|
||||||
FoundBadTypeIndex |= !Record.remapTypeIndices(IndexMap); \
|
FoundBadTypeIndex |= !Record.remapTypeIndices(IndexMap); \
|
||||||
IndexMap.push_back(DestStream.write##Name(Record)); \
|
IndexMap.push_back(DestStream.write##Name(Record)); \
|
||||||
|
return Error::success(); \
|
||||||
}
|
}
|
||||||
#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
|
#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
|
||||||
#define MEMBER_RECORD(EnumName, EnumVal, Name) \
|
#define MEMBER_RECORD(EnumName, EnumVal, Name) \
|
||||||
void TypeStreamMerger::visit##Name(Name##Record &Record) { \
|
Error TypeStreamMerger::visit##Name(Name##Record &Record) { \
|
||||||
FoundBadTypeIndex |= !Record.remapTypeIndices(IndexMap); \
|
FoundBadTypeIndex |= !Record.remapTypeIndices(IndexMap); \
|
||||||
FieldBuilder.write##Name(Record); \
|
FieldBuilder.write##Name(Record); \
|
||||||
|
return Error::success(); \
|
||||||
}
|
}
|
||||||
#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
|
#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
|
||||||
#include "llvm/DebugInfo/CodeView/TypeRecords.def"
|
#include "llvm/DebugInfo/CodeView/TypeRecords.def"
|
||||||
|
|
||||||
void TypeStreamMerger::visitUnknownType(const CVRecord<TypeLeafKind> &Rec) {
|
Error TypeStreamMerger::visitUnknownType(const CVRecord<TypeLeafKind> &Rec) {
|
||||||
// We failed to translate a type. Translate this index as "not translated".
|
// We failed to translate a type. Translate this index as "not translated".
|
||||||
IndexMap.push_back(
|
IndexMap.push_back(
|
||||||
TypeIndex(SimpleTypeKind::NotTranslated, SimpleTypeMode::Direct));
|
TypeIndex(SimpleTypeKind::NotTranslated, SimpleTypeMode::Direct));
|
||||||
parseError();
|
return llvm::make_error<CodeViewError>(cv_error_code::corrupt_record);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TypeStreamMerger::mergeStream(const CVTypeArray &Types) {
|
bool TypeStreamMerger::mergeStream(const CVTypeArray &Types) {
|
||||||
assert(IndexMap.empty());
|
assert(IndexMap.empty());
|
||||||
visitTypeStream(Types);
|
CVTypeVisitor Visitor(*this);
|
||||||
|
if (auto EC = Visitor.visitTypeStream(Types)) {
|
||||||
|
consumeError(std::move(EC));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
IndexMap.clear();
|
IndexMap.clear();
|
||||||
return !hadError();
|
return !hadError();
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,8 @@ public:
|
||||||
return "The specified block address is not valid.";
|
return "The specified block address is not valid.";
|
||||||
case raw_error_code::not_writable:
|
case raw_error_code::not_writable:
|
||||||
return "The PDB does not support writing.";
|
return "The PDB does not support writing.";
|
||||||
|
case raw_error_code::invalid_tpi_hash:
|
||||||
|
return "The Type record has an invalid hash value.";
|
||||||
}
|
}
|
||||||
llvm_unreachable("Unrecognized raw_error_code");
|
llvm_unreachable("Unrecognized raw_error_code");
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,39 +84,44 @@ template <typename T> static uint32_t getTpiHash(T &Rec) {
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
class TpiHashVerifier : public CVTypeVisitor<TpiHashVerifier> {
|
class TpiHashVerifier : public TypeVisitorCallbacks {
|
||||||
public:
|
public:
|
||||||
TpiHashVerifier(FixedStreamArray<support::ulittle32_t> &HashValues,
|
TpiHashVerifier(FixedStreamArray<support::ulittle32_t> &HashValues,
|
||||||
uint32_t NumHashBuckets)
|
uint32_t NumHashBuckets)
|
||||||
: HashValues(HashValues), NumHashBuckets(NumHashBuckets) {}
|
: HashValues(HashValues), NumHashBuckets(NumHashBuckets) {}
|
||||||
|
|
||||||
void visitUdtSourceLine(UdtSourceLineRecord &Rec) { verifySourceLine(Rec); }
|
Error visitUdtSourceLine(UdtSourceLineRecord &Rec) override {
|
||||||
|
return verifySourceLine(Rec);
|
||||||
void visitUdtModSourceLine(UdtModSourceLineRecord &Rec) {
|
|
||||||
verifySourceLine(Rec);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void visitClass(ClassRecord &Rec) { verify(Rec); }
|
Error visitUdtModSourceLine(UdtModSourceLineRecord &Rec) override {
|
||||||
void visitEnum(EnumRecord &Rec) { verify(Rec); }
|
return verifySourceLine(Rec);
|
||||||
void visitInterface(ClassRecord &Rec) { verify(Rec); }
|
}
|
||||||
void visitStruct(ClassRecord &Rec) { verify(Rec); }
|
|
||||||
void visitUnion(UnionRecord &Rec) { verify(Rec); }
|
|
||||||
|
|
||||||
void visitTypeEnd(const CVRecord<TypeLeafKind> &Record) { ++Index; }
|
Error visitClass(ClassRecord &Rec) override { return verify(Rec); }
|
||||||
|
Error visitEnum(EnumRecord &Rec) override { return verify(Rec); }
|
||||||
|
Error visitUnion(UnionRecord &Rec) override { return verify(Rec); }
|
||||||
|
|
||||||
|
Error visitTypeEnd(const CVRecord<TypeLeafKind> &Record) override {
|
||||||
|
++Index;
|
||||||
|
return Error::success();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template <typename T> void verify(T &Rec) {
|
template <typename T> Error verify(T &Rec) {
|
||||||
uint32_t Hash = getTpiHash(Rec);
|
uint32_t Hash = getTpiHash(Rec);
|
||||||
if (Hash && Hash % NumHashBuckets != HashValues[Index])
|
if (Hash && Hash % NumHashBuckets != HashValues[Index])
|
||||||
parseError();
|
return make_error<RawError>(raw_error_code::invalid_tpi_hash);
|
||||||
|
return Error::success();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T> void verifySourceLine(T &Rec) {
|
template <typename T> Error verifySourceLine(T &Rec) {
|
||||||
char Buf[4];
|
char Buf[4];
|
||||||
support::endian::write32le(Buf, Rec.getUDT().getIndex());
|
support::endian::write32le(Buf, Rec.getUDT().getIndex());
|
||||||
uint32_t Hash = hashStringV1(StringRef(Buf, 4));
|
uint32_t Hash = hashStringV1(StringRef(Buf, 4));
|
||||||
if (Hash % NumHashBuckets != HashValues[Index])
|
if (Hash % NumHashBuckets != HashValues[Index])
|
||||||
parseError();
|
return make_error<RawError>(raw_error_code::invalid_tpi_hash);
|
||||||
|
return Error::success();
|
||||||
}
|
}
|
||||||
|
|
||||||
FixedStreamArray<support::ulittle32_t> HashValues;
|
FixedStreamArray<support::ulittle32_t> HashValues;
|
||||||
|
@ -129,11 +134,8 @@ private:
|
||||||
// Currently we only verify SRC_LINE records.
|
// Currently we only verify SRC_LINE records.
|
||||||
Error TpiStream::verifyHashValues() {
|
Error TpiStream::verifyHashValues() {
|
||||||
TpiHashVerifier Verifier(HashValues, Header->NumHashBuckets);
|
TpiHashVerifier Verifier(HashValues, Header->NumHashBuckets);
|
||||||
Verifier.visitTypeStream(TypeRecords);
|
CVTypeVisitor Visitor(Verifier);
|
||||||
if (Verifier.hadError())
|
return Visitor.visitTypeStream(TypeRecords);
|
||||||
return make_error<RawError>(raw_error_code::corrupt_file,
|
|
||||||
"Corrupt TPI hash table.");
|
|
||||||
return Error::success();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Error TpiStream::reload() {
|
Error TpiStream::reload() {
|
||||||
|
|
|
@ -328,8 +328,10 @@ Error LLVMOutputStyle::dumpTpiStream(uint32_t StreamIdx) {
|
||||||
for (auto &Type : Tpi->types(&HadError)) {
|
for (auto &Type : Tpi->types(&HadError)) {
|
||||||
DictScope DD(P, "");
|
DictScope DD(P, "");
|
||||||
|
|
||||||
if (DumpRecords)
|
if (DumpRecords) {
|
||||||
TD.dump(Type);
|
if (auto EC = TD.dump(Type))
|
||||||
|
return EC;
|
||||||
|
}
|
||||||
|
|
||||||
if (DumpRecordBytes)
|
if (DumpRecordBytes)
|
||||||
P.printBinaryBlock("Bytes", Type.Data);
|
P.printBinaryBlock("Bytes", Type.Data);
|
||||||
|
@ -347,8 +349,10 @@ Error LLVMOutputStyle::dumpTpiStream(uint32_t StreamIdx) {
|
||||||
TD.setPrinter(nullptr);
|
TD.setPrinter(nullptr);
|
||||||
|
|
||||||
bool HadError = false;
|
bool HadError = false;
|
||||||
for (auto &Type : Tpi->types(&HadError))
|
for (auto &Type : Tpi->types(&HadError)) {
|
||||||
TD.dump(Type);
|
if (auto EC = TD.dump(Type))
|
||||||
|
return EC;
|
||||||
|
}
|
||||||
|
|
||||||
TD.setPrinter(OldP);
|
TD.setPrinter(OldP);
|
||||||
dumpTpiHash(P, *Tpi);
|
dumpTpiHash(P, *Tpi);
|
||||||
|
|
|
@ -1108,9 +1108,9 @@ void COFFDumper::printCodeViewTypeSection(StringRef SectionName,
|
||||||
if (Magic != COFF::DEBUG_SECTION_MAGIC)
|
if (Magic != COFF::DEBUG_SECTION_MAGIC)
|
||||||
return error(object_error::parse_failed);
|
return error(object_error::parse_failed);
|
||||||
|
|
||||||
if (!CVTD.dump({Data.bytes_begin(), Data.bytes_end()})) {
|
if (auto EC = CVTD.dump({Data.bytes_begin(), Data.bytes_end()})) {
|
||||||
W.flush();
|
W.flush();
|
||||||
error(object_error::parse_failed);
|
error(llvm::errorToErrorCode(std::move(EC)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1555,8 +1555,8 @@ void llvm::dumpCodeViewMergedTypes(
|
||||||
Buf.append(Record.begin(), Record.end());
|
Buf.append(Record.begin(), Record.end());
|
||||||
});
|
});
|
||||||
CVTypeDumper CVTD(&Writer, opts::CodeViewSubsectionBytes);
|
CVTypeDumper CVTD(&Writer, opts::CodeViewSubsectionBytes);
|
||||||
if (!CVTD.dump({Buf.str().bytes_begin(), Buf.str().bytes_end()})) {
|
if (auto EC = CVTD.dump({Buf.str().bytes_begin(), Buf.str().bytes_end()})) {
|
||||||
Writer.flush();
|
Writer.flush();
|
||||||
error(object_error::parse_failed);
|
error(llvm::errorToErrorCode(std::move(EC)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue