mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-26 12:43:36 +01:00
[CodeView] Hook up CodeViewRecordIO to type serialization path.
Previously support had been added for using CodeViewRecordIO to read (deserialize) CodeView type records. This patch adds support for writing those same records. With this patch, reading and writing of CodeView type records finally uses a single codepath. Differential Revision: https://reviews.llvm.org/D26253 llvm-svn: 286304
This commit is contained in:
parent
e6c0e33913
commit
064bbdf4f2
@ -13,38 +13,40 @@
|
||||
#include "llvm/ADT/APSInt.h"
|
||||
#include "llvm/ADT/Optional.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeIndex.h"
|
||||
#include "llvm/DebugInfo/CodeView/CodeView.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
|
||||
#include "llvm/DebugInfo/MSF/StreamReader.h"
|
||||
#include "llvm/DebugInfo/MSF/StreamWriter.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <type_traits>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace llvm {
|
||||
namespace msf {
|
||||
class StreamReader;
|
||||
class StreamWriter;
|
||||
}
|
||||
namespace codeview {
|
||||
|
||||
class CodeViewRecordIO {
|
||||
struct ActiveRecord {
|
||||
uint16_t Kind;
|
||||
};
|
||||
uint32_t getCurrentOffset() const {
|
||||
return (isWriting()) ? Writer->getOffset() : Reader->getOffset();
|
||||
}
|
||||
|
||||
public:
|
||||
explicit CodeViewRecordIO(msf::StreamReader &Reader) : Reader(&Reader) {}
|
||||
explicit CodeViewRecordIO(msf::StreamWriter &Writer) : Writer(&Writer) {}
|
||||
|
||||
Error beginRecord(uint16_t Kind);
|
||||
Error beginRecord(Optional<uint32_t> MaxLength);
|
||||
Error endRecord();
|
||||
|
||||
Error mapInteger(TypeIndex &TypeInd);
|
||||
|
||||
bool isReading() const { return Reader != nullptr; }
|
||||
bool isWriting() const { return !isReading(); }
|
||||
|
||||
uint32_t maxFieldLength() const;
|
||||
|
||||
template <typename T> Error mapInteger(T &Value) {
|
||||
if (isWriting())
|
||||
return Writer->writeInteger(Value);
|
||||
@ -53,6 +55,9 @@ public:
|
||||
}
|
||||
|
||||
template <typename T> Error mapEnum(T &Value) {
|
||||
if (sizeof(Value) > maxFieldLength())
|
||||
return make_error<CodeViewError>(cv_error_code::insufficient_buffer);
|
||||
|
||||
using U = typename std::underlying_type<T>::type;
|
||||
U X;
|
||||
if (isWriting())
|
||||
@ -124,7 +129,23 @@ private:
|
||||
Error writeEncodedSignedInteger(const int64_t &Value);
|
||||
Error writeEncodedUnsignedInteger(const uint64_t &Value);
|
||||
|
||||
Optional<ActiveRecord> CurrentRecord;
|
||||
struct RecordLimit {
|
||||
uint32_t BeginOffset;
|
||||
Optional<uint32_t> MaxLength;
|
||||
|
||||
Optional<uint32_t> bytesRemaining(uint32_t CurrentOffset) const {
|
||||
if (!MaxLength.hasValue())
|
||||
return None;
|
||||
assert(CurrentOffset >= BeginOffset);
|
||||
|
||||
uint32_t BytesUsed = CurrentOffset - BeginOffset;
|
||||
if (BytesUsed >= *MaxLength)
|
||||
return 0;
|
||||
return *MaxLength - BytesUsed;
|
||||
}
|
||||
};
|
||||
|
||||
SmallVector<RecordLimit, 2> Limits;
|
||||
|
||||
msf::StreamReader *Reader = nullptr;
|
||||
msf::StreamWriter *Writer = nullptr;
|
||||
|
@ -1,67 +0,0 @@
|
||||
//===- FieldListRecordBuilder.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_FIELDLISTRECORDBUILDER_H
|
||||
#define LLVM_DEBUGINFO_CODEVIEW_FIELDLISTRECORDBUILDER_H
|
||||
|
||||
#include "llvm/DebugInfo/CodeView/ListRecordBuilder.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
|
||||
|
||||
namespace llvm {
|
||||
namespace codeview {
|
||||
|
||||
class MethodInfo {
|
||||
public:
|
||||
MethodInfo() : Access(), Kind(), Options(), Type(), VTableSlotOffset(-1) {}
|
||||
|
||||
MethodInfo(MemberAccess Access, MethodKind Kind, MethodOptions Options,
|
||||
TypeIndex Type, int32_t VTableSlotOffset)
|
||||
: Access(Access), Kind(Kind), Options(Options), Type(Type),
|
||||
VTableSlotOffset(VTableSlotOffset) {}
|
||||
|
||||
MemberAccess getAccess() const { return Access; }
|
||||
MethodKind getKind() const { return Kind; }
|
||||
MethodOptions getOptions() const { return Options; }
|
||||
TypeIndex getType() const { return Type; }
|
||||
int32_t getVTableSlotOffset() const { return VTableSlotOffset; }
|
||||
|
||||
private:
|
||||
MemberAccess Access;
|
||||
MethodKind Kind;
|
||||
MethodOptions Options;
|
||||
TypeIndex Type;
|
||||
int32_t VTableSlotOffset;
|
||||
};
|
||||
|
||||
class FieldListRecordBuilder : public ListRecordBuilder {
|
||||
private:
|
||||
FieldListRecordBuilder(const FieldListRecordBuilder &) = delete;
|
||||
void operator=(const FieldListRecordBuilder &) = delete;
|
||||
|
||||
public:
|
||||
FieldListRecordBuilder();
|
||||
|
||||
void reset() { ListRecordBuilder::reset(); }
|
||||
|
||||
void writeMemberType(const BaseClassRecord &Record);
|
||||
void writeMemberType(const EnumeratorRecord &Record);
|
||||
void writeMemberType(const DataMemberRecord &Record);
|
||||
void writeMemberType(const OneMethodRecord &Record);
|
||||
void writeMemberType(const OverloadedMethodRecord &Record);
|
||||
void writeMemberType(const NestedTypeRecord &Record);
|
||||
void writeMemberType(const StaticDataMemberRecord &Record);
|
||||
void writeMemberType(const VirtualBaseClassRecord &Record);
|
||||
void writeMemberType(const VFPtrRecord &Type);
|
||||
|
||||
using ListRecordBuilder::writeMemberType;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -1,65 +0,0 @@
|
||||
//===- ListRecordBuilder.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_LISTRECORDBUILDER_H
|
||||
#define LLVM_DEBUGINFO_CODEVIEW_LISTRECORDBUILDER_H
|
||||
|
||||
#include "llvm/DebugInfo/CodeView/TypeRecordBuilder.h"
|
||||
|
||||
namespace llvm {
|
||||
namespace codeview {
|
||||
class TypeTableBuilder;
|
||||
|
||||
class ListRecordBuilder {
|
||||
private:
|
||||
ListRecordBuilder(const ListRecordBuilder &) = delete;
|
||||
ListRecordBuilder &operator=(const ListRecordBuilder &) = delete;
|
||||
|
||||
protected:
|
||||
const int MethodKindShift = 2;
|
||||
|
||||
explicit ListRecordBuilder(TypeRecordKind Kind);
|
||||
|
||||
public:
|
||||
llvm::StringRef str() { return Builder.str(); }
|
||||
|
||||
void reset() {
|
||||
Builder.reset(Kind);
|
||||
ContinuationOffsets.clear();
|
||||
SubrecordStart = 0;
|
||||
}
|
||||
|
||||
void writeMemberType(const ListContinuationRecord &R);
|
||||
|
||||
/// Writes this list record as a possible sequence of records.
|
||||
TypeIndex writeListRecord(TypeTableBuilder &Table);
|
||||
|
||||
protected:
|
||||
void finishSubRecord();
|
||||
|
||||
TypeRecordBuilder &getBuilder() { return Builder; }
|
||||
|
||||
private:
|
||||
size_t getLastContinuationStart() const {
|
||||
return ContinuationOffsets.empty() ? 0 : ContinuationOffsets.back();
|
||||
}
|
||||
size_t getLastContinuationEnd() const { return Builder.size(); }
|
||||
size_t getLastContinuationSize() const {
|
||||
return getLastContinuationEnd() - getLastContinuationStart();
|
||||
}
|
||||
|
||||
TypeRecordKind Kind;
|
||||
TypeRecordBuilder Builder;
|
||||
SmallVector<size_t, 4> ContinuationOffsets;
|
||||
size_t SubrecordStart = 0;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -1,50 +0,0 @@
|
||||
//===- MemoryTypeTableBuilder.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_MEMORYTYPETABLEBUILDER_H
|
||||
#define LLVM_DEBUGINFO_CODEVIEW_MEMORYTYPETABLEBUILDER_H
|
||||
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeTableBuilder.h"
|
||||
#include <vector>
|
||||
|
||||
namespace llvm {
|
||||
namespace codeview {
|
||||
|
||||
class MemoryTypeTableBuilder : public TypeTableBuilder {
|
||||
public:
|
||||
explicit MemoryTypeTableBuilder(BumpPtrAllocator &Allocator)
|
||||
: RecordStorage(Allocator) {}
|
||||
|
||||
bool empty() const { return Records.empty(); }
|
||||
|
||||
template <typename TFunc> void ForEachRecord(TFunc Func) {
|
||||
uint32_t Index = TypeIndex::FirstNonSimpleIndex;
|
||||
|
||||
for (StringRef R : Records) {
|
||||
Func(TypeIndex(Index), R);
|
||||
++Index;
|
||||
}
|
||||
}
|
||||
|
||||
TypeIndex writeRecord(llvm::StringRef Data) override;
|
||||
|
||||
ArrayRef<StringRef> getRecords() const { return Records; }
|
||||
|
||||
private:
|
||||
std::vector<StringRef> Records;
|
||||
BumpPtrAllocator &RecordStorage;
|
||||
DenseMap<StringRef, TypeIndex> HashedRecords;
|
||||
};
|
||||
|
||||
} // end namespace codeview
|
||||
} // end namespace llvm
|
||||
|
||||
#endif // LLVM_DEBUGINFO_CODEVIEW_MEMORYTYPETABLEBUILDER_H
|
@ -1,35 +0,0 @@
|
||||
//===- MethodListRecordBuilder.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_METHODLISTRECORDBUILDER_H
|
||||
#define LLVM_DEBUGINFO_CODEVIEW_METHODLISTRECORDBUILDER_H
|
||||
|
||||
#include "llvm/DebugInfo/CodeView/ListRecordBuilder.h"
|
||||
|
||||
namespace llvm {
|
||||
namespace codeview {
|
||||
|
||||
class MethodInfo;
|
||||
|
||||
class MethodListRecordBuilder : public ListRecordBuilder {
|
||||
private:
|
||||
MethodListRecordBuilder(const MethodListRecordBuilder &) = delete;
|
||||
MethodListRecordBuilder &operator=(const MethodListRecordBuilder &) = delete;
|
||||
|
||||
public:
|
||||
MethodListRecordBuilder();
|
||||
|
||||
void writeMethod(MemberAccess Access, MethodKind Kind, MethodOptions Options,
|
||||
TypeIndex Type, int32_t VTableSlotOffset);
|
||||
void writeMethod(const MethodInfo &Method);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -32,7 +32,7 @@ using llvm::support::ulittle32_t;
|
||||
enum : unsigned { MaxRecordLength = 0xFF00 };
|
||||
|
||||
struct RecordPrefix {
|
||||
ulittle16_t RecordLen; // Record length, starting from &Leaf.
|
||||
ulittle16_t RecordLen; // Record length, starting from &RecordKind.
|
||||
ulittle16_t RecordKind; // Record kind enum (SymRecordKind or TypeRecordKind)
|
||||
};
|
||||
|
||||
|
@ -76,7 +76,17 @@ class FieldListDeserializer : public TypeVisitorCallbacks {
|
||||
};
|
||||
|
||||
public:
|
||||
explicit FieldListDeserializer(msf::StreamReader &Reader) : Mapping(Reader) {}
|
||||
explicit FieldListDeserializer(msf::StreamReader &Reader) : Mapping(Reader) {
|
||||
CVType FieldList;
|
||||
FieldList.Type = TypeLeafKind::LF_FIELDLIST;
|
||||
consumeError(Mapping.Mapping.visitTypeBegin(FieldList));
|
||||
}
|
||||
|
||||
~FieldListDeserializer() {
|
||||
CVType FieldList;
|
||||
FieldList.Type = TypeLeafKind::LF_FIELDLIST;
|
||||
consumeError(Mapping.Mapping.visitTypeEnd(FieldList));
|
||||
}
|
||||
|
||||
Error visitMemberBegin(CVMemberRecord &Record) override {
|
||||
Mapping.StartOffset = Mapping.Reader.getOffset();
|
||||
|
@ -93,7 +93,7 @@ public:
|
||||
static const uint32_t SimpleModeMask = 0x00000700;
|
||||
|
||||
public:
|
||||
TypeIndex() : Index(0) {}
|
||||
TypeIndex() : Index(static_cast<uint32_t>(SimpleTypeKind::None)) {}
|
||||
explicit TypeIndex(uint32_t Index) : Index(Index) {}
|
||||
explicit TypeIndex(SimpleTypeKind Kind)
|
||||
: Index(static_cast<uint32_t>(Kind)) {}
|
||||
|
@ -701,26 +701,25 @@ public:
|
||||
StringRef Name)
|
||||
: TypeRecord(TypeRecordKind::OneMethod), Type(Type), Attrs(Attrs),
|
||||
VFTableOffset(VFTableOffset), Name(Name) {}
|
||||
OneMethodRecord(TypeIndex Type, MemberAccess Access, MethodKind Kind,
|
||||
OneMethodRecord(TypeIndex Type, MemberAccess Access, MethodKind MK,
|
||||
MethodOptions Options, int32_t VFTableOffset, StringRef Name)
|
||||
: TypeRecord(TypeRecordKind::OneMethod), Type(Type),
|
||||
Attrs(Access, Kind, Options), VFTableOffset(VFTableOffset), Name(Name) {
|
||||
}
|
||||
Attrs(Access, MK, Options), 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);
|
||||
|
||||
TypeIndex getType() const { return Type; }
|
||||
MethodKind getKind() const { return Attrs.getMethodKind(); }
|
||||
MethodKind getMethodKind() const { return Attrs.getMethodKind(); }
|
||||
MethodOptions getOptions() const { return Attrs.getFlags(); }
|
||||
MemberAccess getAccess() const { return Attrs.getAccess(); }
|
||||
int32_t getVFTableOffset() const { return VFTableOffset; }
|
||||
StringRef getName() const { return Name; }
|
||||
|
||||
bool isIntroducingVirtual() const {
|
||||
return getKind() == MethodKind::IntroducingVirtual ||
|
||||
getKind() == MethodKind::PureIntroducingVirtual;
|
||||
return getMethodKind() == MethodKind::IntroducingVirtual ||
|
||||
getMethodKind() == MethodKind::PureIntroducingVirtual;
|
||||
}
|
||||
TypeIndex Type;
|
||||
MemberAttributes Attrs;
|
||||
|
@ -16,10 +16,15 @@
|
||||
#include "llvm/Support/Error.h"
|
||||
|
||||
namespace llvm {
|
||||
namespace msf {
|
||||
class StreamReader;
|
||||
class StreamWriter;
|
||||
}
|
||||
namespace codeview {
|
||||
class TypeRecordMapping : public TypeVisitorCallbacks {
|
||||
public:
|
||||
explicit TypeRecordMapping(msf::StreamReader &Reader) : IO(Reader) {}
|
||||
explicit TypeRecordMapping(msf::StreamWriter &Writer) : IO(Writer) {}
|
||||
|
||||
Error visitTypeBegin(CVType &Record) override;
|
||||
Error visitTypeEnd(CVType &Record) override;
|
||||
@ -37,6 +42,7 @@ public:
|
||||
|
||||
private:
|
||||
Optional<TypeLeafKind> TypeKind;
|
||||
Optional<TypeLeafKind> MemberKind;
|
||||
|
||||
CodeViewRecordIO IO;
|
||||
};
|
||||
|
@ -1,85 +0,0 @@
|
||||
//===- TypeSerializationVisitor.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_TYPESERIALIZATIONVISITOR_H
|
||||
#define LLVM_DEBUGINFO_CODEVIEW_TYPESERIALIZATIONVISITOR_H
|
||||
|
||||
#include "llvm/DebugInfo/CodeView/FieldListRecordBuilder.h"
|
||||
#include "llvm/DebugInfo/CodeView/MemoryTypeTableBuilder.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h"
|
||||
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
|
||||
namespace llvm {
|
||||
namespace codeview {
|
||||
|
||||
class TypeSerializationVisitor : public TypeVisitorCallbacks {
|
||||
public:
|
||||
TypeSerializationVisitor(FieldListRecordBuilder &FieldListBuilder,
|
||||
MemoryTypeTableBuilder &TypeTableBuilder)
|
||||
: FieldListBuilder(FieldListBuilder), TypeTableBuilder(TypeTableBuilder) {
|
||||
}
|
||||
|
||||
virtual Error visitTypeBegin(CVType &Record) override {
|
||||
if (Record.Type == TypeLeafKind::LF_FIELDLIST)
|
||||
FieldListBuilder.reset();
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
virtual Error visitTypeEnd(CVType &Record) override {
|
||||
// Since this visitor's purpose is to serialize the record, fill out the
|
||||
// fields of `Record` with the bytes of the record.
|
||||
if (Record.Type == TypeLeafKind::LF_FIELDLIST) {
|
||||
TypeTableBuilder.writeFieldList(FieldListBuilder);
|
||||
updateCVRecord(Record);
|
||||
}
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
#define TYPE_RECORD(EnumName, EnumVal, Name) \
|
||||
virtual Error visitKnownRecord(CVType &CVR, Name##Record &Record) override { \
|
||||
visitKnownRecordImpl(CVR, Record); \
|
||||
return Error::success(); \
|
||||
}
|
||||
#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
|
||||
#define MEMBER_RECORD(EnumName, EnumVal, Name) \
|
||||
virtual Error visitKnownMember(CVMemberRecord &CVR, Name##Record &Record) \
|
||||
override { \
|
||||
visitMemberRecordImpl(Record); \
|
||||
return Error::success(); \
|
||||
}
|
||||
#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
|
||||
#include "llvm/DebugInfo/CodeView/TypeRecords.def"
|
||||
|
||||
private:
|
||||
void updateCVRecord(CVType &Record) {
|
||||
StringRef S = TypeTableBuilder.getRecords().back();
|
||||
Record.RecordData = ArrayRef<uint8_t>(S.bytes_begin(), S.bytes_end());
|
||||
}
|
||||
template <typename RecordKind>
|
||||
void visitKnownRecordImpl(CVType &CVR, RecordKind &Record) {
|
||||
TypeTableBuilder.writeKnownType(Record);
|
||||
updateCVRecord(CVR);
|
||||
}
|
||||
template <typename RecordKind>
|
||||
void visitMemberRecordImpl(RecordKind &Record) {
|
||||
FieldListBuilder.writeMemberType(Record);
|
||||
}
|
||||
|
||||
void visitKnownRecordImpl(CVType &CVR, FieldListRecord &FieldList) {}
|
||||
|
||||
FieldListRecordBuilder &FieldListBuilder;
|
||||
MemoryTypeTableBuilder &TypeTableBuilder;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
140
include/llvm/DebugInfo/CodeView/TypeSerializer.h
Normal file
140
include/llvm/DebugInfo/CodeView/TypeSerializer.h
Normal file
@ -0,0 +1,140 @@
|
||||
//===- TypeSerializer.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_TYPESERIALIZER_H
|
||||
#define LLVM_DEBUGINFO_CODEVIEW_TYPESERIALIZER_H
|
||||
|
||||
#include "llvm/DebugInfo/CodeView/TypeRecordMapping.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h"
|
||||
#include "llvm/DebugInfo/MSF/ByteStream.h"
|
||||
#include "llvm/DebugInfo/MSF/StreamWriter.h"
|
||||
|
||||
#include "llvm/ADT/Optional.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/ADT/StringMap.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Support/Allocator.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
|
||||
namespace llvm {
|
||||
|
||||
namespace codeview {
|
||||
|
||||
class TypeSerializer : public TypeVisitorCallbacks {
|
||||
struct SubRecord {
|
||||
SubRecord(TypeLeafKind K, uint32_t S) : Kind(K), Size(S) {}
|
||||
|
||||
TypeLeafKind Kind;
|
||||
uint32_t Size = 0;
|
||||
};
|
||||
struct RecordSegment {
|
||||
SmallVector<SubRecord, 16> SubRecords;
|
||||
|
||||
uint32_t length() const {
|
||||
uint32_t L = sizeof(RecordPrefix);
|
||||
for (const auto &R : SubRecords) {
|
||||
L += R.Size;
|
||||
}
|
||||
return L;
|
||||
}
|
||||
};
|
||||
|
||||
typedef SmallVector<MutableArrayRef<uint8_t>, 2> RecordList;
|
||||
|
||||
static constexpr uint8_t ContinuationLength = 8;
|
||||
BumpPtrAllocator &RecordStorage;
|
||||
RecordSegment CurrentSegment;
|
||||
RecordList FieldListSegments;
|
||||
|
||||
TypeIndex LastTypeIndex;
|
||||
Optional<TypeLeafKind> TypeKind;
|
||||
Optional<TypeLeafKind> MemberKind;
|
||||
std::vector<uint8_t> RecordBuffer;
|
||||
msf::MutableByteStream Stream;
|
||||
msf::StreamWriter Writer;
|
||||
TypeRecordMapping Mapping;
|
||||
|
||||
RecordList SeenRecords;
|
||||
StringMap<TypeIndex> HashedRecords;
|
||||
|
||||
bool isInFieldList() const;
|
||||
TypeIndex calcNextTypeIndex() const;
|
||||
TypeIndex incrementTypeIndex();
|
||||
MutableArrayRef<uint8_t> getCurrentSubRecordData();
|
||||
MutableArrayRef<uint8_t> getCurrentRecordData();
|
||||
Error writeRecordPrefix(TypeLeafKind Kind);
|
||||
TypeIndex insertRecordBytesPrivate(MutableArrayRef<uint8_t> Record);
|
||||
|
||||
Expected<MutableArrayRef<uint8_t>>
|
||||
addPadding(MutableArrayRef<uint8_t> Record);
|
||||
|
||||
public:
|
||||
explicit TypeSerializer(BumpPtrAllocator &Storage);
|
||||
|
||||
ArrayRef<MutableArrayRef<uint8_t>> records() const;
|
||||
TypeIndex getLastTypeIndex() const;
|
||||
TypeIndex insertRecordBytes(MutableArrayRef<uint8_t> Record);
|
||||
Expected<TypeIndex> visitTypeEndGetIndex(CVType &Record);
|
||||
|
||||
Error visitTypeBegin(CVType &Record) override;
|
||||
Error visitTypeEnd(CVType &Record) override;
|
||||
Error visitMemberBegin(CVMemberRecord &Record) override;
|
||||
Error visitMemberEnd(CVMemberRecord &Record) override;
|
||||
|
||||
#define TYPE_RECORD(EnumName, EnumVal, Name) \
|
||||
virtual Error visitKnownRecord(CVType &CVR, Name##Record &Record) override { \
|
||||
return visitKnownRecordImpl(CVR, Record); \
|
||||
}
|
||||
#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
|
||||
#define MEMBER_RECORD(EnumName, EnumVal, Name) \
|
||||
Error visitKnownMember(CVMemberRecord &CVR, Name##Record &Record) override { \
|
||||
return visitKnownMemberImpl<Name##Record>(CVR, Record); \
|
||||
}
|
||||
#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
|
||||
#include "llvm/DebugInfo/CodeView/TypeRecords.def"
|
||||
|
||||
private:
|
||||
template <typename RecordKind>
|
||||
Error visitKnownRecordImpl(CVType &CVR, RecordKind &Record) {
|
||||
return Mapping.visitKnownRecord(CVR, Record);
|
||||
}
|
||||
|
||||
template <typename RecordType>
|
||||
Error visitKnownMemberImpl(CVMemberRecord &CVR, RecordType &Record) {
|
||||
assert(CVR.Kind == static_cast<TypeLeafKind>(Record.getKind()));
|
||||
|
||||
if (auto EC = Writer.writeEnum(CVR.Kind))
|
||||
return EC;
|
||||
|
||||
if (auto EC = Mapping.visitKnownMember(CVR, Record))
|
||||
return EC;
|
||||
|
||||
// Get all the data that was just written and is yet to be committed to
|
||||
// the current segment. Then pad it to 4 bytes.
|
||||
MutableArrayRef<uint8_t> ThisRecord = getCurrentSubRecordData();
|
||||
auto ExpectedRecord = addPadding(ThisRecord);
|
||||
if (!ExpectedRecord)
|
||||
return ExpectedRecord.takeError();
|
||||
ThisRecord = *ExpectedRecord;
|
||||
|
||||
CurrentSegment.SubRecords.emplace_back(CVR.Kind, ThisRecord.size());
|
||||
CVR.Data = ThisRecord;
|
||||
|
||||
// Both the last subrecord and the total length of this segment should be
|
||||
// multiples of 4.
|
||||
assert(ThisRecord.size() % 4 == 0);
|
||||
assert(CurrentSegment.length() % 4 == 0);
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -10,64 +10,111 @@
|
||||
#ifndef LLVM_DEBUGINFO_CODEVIEW_TYPETABLEBUILDER_H
|
||||
#define LLVM_DEBUGINFO_CODEVIEW_TYPETABLEBUILDER_H
|
||||
|
||||
#include "llvm/DebugInfo/CodeView/CodeView.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeIndex.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
|
||||
#include "llvm/Support/Compiler.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeSerializer.h"
|
||||
#include "llvm/Support/Allocator.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
|
||||
namespace llvm {
|
||||
|
||||
class StringRef;
|
||||
|
||||
namespace codeview {
|
||||
|
||||
class FieldListRecordBuilder;
|
||||
class MethodListRecordBuilder;
|
||||
class TypeRecordBuilder;
|
||||
|
||||
class TypeTableBuilder {
|
||||
private:
|
||||
TypeTableBuilder(const TypeTableBuilder &) = delete;
|
||||
TypeTableBuilder &operator=(const TypeTableBuilder &) = delete;
|
||||
|
||||
protected:
|
||||
TypeTableBuilder();
|
||||
TypeIndex handleError(llvm::Error EC) const {
|
||||
assert(false && "Couldn't write Type!");
|
||||
llvm::consumeError(std::move(EC));
|
||||
return TypeIndex();
|
||||
}
|
||||
|
||||
BumpPtrAllocator &Allocator;
|
||||
TypeSerializer Serializer;
|
||||
|
||||
public:
|
||||
virtual ~TypeTableBuilder();
|
||||
explicit TypeTableBuilder(BumpPtrAllocator &Allocator)
|
||||
: Allocator(Allocator), Serializer(Allocator) {}
|
||||
|
||||
bool empty() const { return Serializer.records().empty(); }
|
||||
|
||||
BumpPtrAllocator &getAllocator() const { return Allocator; }
|
||||
|
||||
template <typename T> TypeIndex writeKnownType(T &Record) {
|
||||
static_assert(!std::is_same<T, FieldListRecord>::value,
|
||||
"Can't serialize FieldList!");
|
||||
|
||||
CVType Type;
|
||||
Type.Type = static_cast<TypeLeafKind>(Record.getKind());
|
||||
if (auto EC = Serializer.visitTypeBegin(Type))
|
||||
return handleError(std::move(EC));
|
||||
if (auto EC = Serializer.visitKnownRecord(Type, Record))
|
||||
return handleError(std::move(EC));
|
||||
|
||||
auto ExpectedIndex = Serializer.visitTypeEndGetIndex(Type);
|
||||
if (!ExpectedIndex)
|
||||
return handleError(ExpectedIndex.takeError());
|
||||
|
||||
return *ExpectedIndex;
|
||||
}
|
||||
|
||||
TypeIndex writeSerializedRecord(MutableArrayRef<uint8_t> Record) {
|
||||
return Serializer.insertRecordBytes(Record);
|
||||
}
|
||||
|
||||
template <typename TFunc> void ForEachRecord(TFunc Func) {
|
||||
uint32_t Index = TypeIndex::FirstNonSimpleIndex;
|
||||
|
||||
for (auto Record : Serializer.records()) {
|
||||
Func(TypeIndex(Index), Record);
|
||||
++Index;
|
||||
}
|
||||
}
|
||||
|
||||
ArrayRef<MutableArrayRef<uint8_t>> records() const {
|
||||
return Serializer.records();
|
||||
}
|
||||
};
|
||||
|
||||
class FieldListRecordBuilder {
|
||||
TypeTableBuilder &TypeTable;
|
||||
TypeSerializer TempSerializer;
|
||||
CVType Type;
|
||||
|
||||
public:
|
||||
TypeIndex writeKnownType(const ModifierRecord &Record);
|
||||
TypeIndex writeKnownType(const ProcedureRecord &Record);
|
||||
TypeIndex writeKnownType(const MemberFunctionRecord &Record);
|
||||
TypeIndex writeKnownType(const ArgListRecord &Record);
|
||||
TypeIndex writeKnownType(const PointerRecord &Record);
|
||||
TypeIndex writeKnownType(const ArrayRecord &Record);
|
||||
TypeIndex writeKnownType(const ClassRecord &Record);
|
||||
TypeIndex writeKnownType(const UnionRecord &Record);
|
||||
TypeIndex writeKnownType(const EnumRecord &Record);
|
||||
TypeIndex writeKnownType(const BitFieldRecord &Record);
|
||||
TypeIndex writeKnownType(const VFTableShapeRecord &Record);
|
||||
TypeIndex writeKnownType(const StringIdRecord &Record);
|
||||
TypeIndex writeKnownType(const VFTableRecord &Record);
|
||||
TypeIndex writeKnownType(const UdtSourceLineRecord &Record);
|
||||
TypeIndex writeKnownType(const UdtModSourceLineRecord &Record);
|
||||
TypeIndex writeKnownType(const FuncIdRecord &Record);
|
||||
TypeIndex writeKnownType(const MemberFuncIdRecord &Record);
|
||||
TypeIndex writeKnownType(const BuildInfoRecord &Record);
|
||||
TypeIndex writeKnownType(const MethodOverloadListRecord &Record);
|
||||
TypeIndex writeKnownType(const TypeServer2Record &Record);
|
||||
explicit FieldListRecordBuilder(TypeTableBuilder &TypeTable)
|
||||
: TypeTable(TypeTable), TempSerializer(TypeTable.getAllocator()) {
|
||||
Type.Type = TypeLeafKind::LF_FIELDLIST;
|
||||
}
|
||||
|
||||
TypeIndex writeFieldList(FieldListRecordBuilder &FieldList);
|
||||
void begin() {
|
||||
if (auto EC = TempSerializer.visitTypeBegin(Type))
|
||||
consumeError(std::move(EC));
|
||||
}
|
||||
|
||||
TypeIndex writeRecord(TypeRecordBuilder &builder);
|
||||
template <typename T> void writeMemberType(T &Record) {
|
||||
CVMemberRecord CVMR;
|
||||
CVMR.Kind = static_cast<TypeLeafKind>(Record.getKind());
|
||||
if (auto EC = TempSerializer.visitMemberBegin(CVMR))
|
||||
consumeError(std::move(EC));
|
||||
if (auto EC = TempSerializer.visitKnownMember(CVMR, Record))
|
||||
consumeError(std::move(EC));
|
||||
if (auto EC = TempSerializer.visitMemberEnd(CVMR))
|
||||
consumeError(std::move(EC));
|
||||
}
|
||||
|
||||
virtual TypeIndex writeRecord(llvm::StringRef record) = 0;
|
||||
TypeIndex end() {
|
||||
if (auto EC = TempSerializer.visitTypeEnd(Type)) {
|
||||
consumeError(std::move(EC));
|
||||
return TypeIndex();
|
||||
}
|
||||
|
||||
ArrayRef<TypeRecordKind> getRecordKinds() const { return RecordKinds; }
|
||||
|
||||
private:
|
||||
std::vector<TypeRecordKind> RecordKinds;
|
||||
TypeIndex Index;
|
||||
for (auto Record : TempSerializer.records()) {
|
||||
Index = TypeTable.writeSerializedRecord(Record);
|
||||
}
|
||||
return Index;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -74,26 +74,34 @@ public:
|
||||
|
||||
#define TYPE_RECORD(EnumName, EnumVal, Name) \
|
||||
Error visitKnownRecord(CVType &CVR, Name##Record &Record) override { \
|
||||
for (auto Visitor : Pipeline) { \
|
||||
if (auto EC = Visitor->visitKnownRecord(CVR, Record)) \
|
||||
return EC; \
|
||||
} \
|
||||
return Error::success(); \
|
||||
return visitKnownRecordImpl(CVR, Record); \
|
||||
}
|
||||
#define MEMBER_RECORD(EnumName, EnumVal, Name) \
|
||||
Error visitKnownMember(CVMemberRecord &CVMR, Name##Record &Record) \
|
||||
override { \
|
||||
for (auto Visitor : Pipeline) { \
|
||||
if (auto EC = Visitor->visitKnownMember(CVMR, Record)) \
|
||||
return EC; \
|
||||
} \
|
||||
return Error::success(); \
|
||||
return visitKnownMemberImpl(CVMR, Record); \
|
||||
}
|
||||
#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
|
||||
#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
|
||||
#include "llvm/DebugInfo/CodeView/TypeRecords.def"
|
||||
|
||||
private:
|
||||
template <typename T> Error visitKnownRecordImpl(CVType &CVR, T &Record) {
|
||||
for (auto Visitor : Pipeline) {
|
||||
if (auto EC = Visitor->visitKnownRecord(CVR, Record))
|
||||
return EC;
|
||||
}
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Error visitKnownMemberImpl(CVMemberRecord &CVMR, T &Record) {
|
||||
for (auto Visitor : Pipeline) {
|
||||
if (auto EC = Visitor->visitKnownMember(CVMR, Record))
|
||||
return EC;
|
||||
}
|
||||
return Error::success();
|
||||
}
|
||||
std::vector<TypeVisitorCallbacks *> Pipeline;
|
||||
};
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ namespace msf {
|
||||
class StreamWriter {
|
||||
public:
|
||||
StreamWriter() {}
|
||||
StreamWriter(WritableStreamRef Stream);
|
||||
explicit StreamWriter(WritableStreamRef Stream);
|
||||
|
||||
Error writeBytes(ArrayRef<uint8_t> Buffer);
|
||||
Error writeInteger(uint8_t Int);
|
||||
|
@ -15,7 +15,6 @@
|
||||
#include "llvm/ADT/TinyPtrVector.h"
|
||||
#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
|
||||
#include "llvm/DebugInfo/CodeView/CodeView.h"
|
||||
#include "llvm/DebugInfo/CodeView/FieldListRecordBuilder.h"
|
||||
#include "llvm/DebugInfo/CodeView/Line.h"
|
||||
#include "llvm/DebugInfo/CodeView/SymbolRecord.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeDumper.h"
|
||||
@ -222,8 +221,8 @@ TypeIndex CodeViewDebug::getScopeIndex(const DIScope *Scope) {
|
||||
|
||||
// Build the fully qualified name of the scope.
|
||||
std::string ScopeName = getFullyQualifiedName(Scope);
|
||||
TypeIndex TI =
|
||||
TypeTable.writeKnownType(StringIdRecord(TypeIndex(), ScopeName));
|
||||
StringIdRecord SID(TypeIndex(), ScopeName);
|
||||
auto TI = TypeTable.writeKnownType(SID);
|
||||
return recordTypeIndexForDINode(Scope, TI);
|
||||
}
|
||||
|
||||
@ -469,47 +468,47 @@ void CodeViewDebug::emitTypeInformation() {
|
||||
}
|
||||
|
||||
CVTypeDumper CVTD(nullptr, /*PrintRecordBytes=*/false);
|
||||
TypeTable.ForEachRecord(
|
||||
[&](TypeIndex Index, StringRef Record) {
|
||||
if (OS.isVerboseAsm()) {
|
||||
// Emit a block comment describing the type record for readability.
|
||||
SmallString<512> CommentBlock;
|
||||
raw_svector_ostream CommentOS(CommentBlock);
|
||||
ScopedPrinter SP(CommentOS);
|
||||
SP.setPrefix(CommentPrefix);
|
||||
CVTD.setPrinter(&SP);
|
||||
Error E = CVTD.dump({Record.bytes_begin(), Record.bytes_end()});
|
||||
if (E) {
|
||||
logAllUnhandledErrors(std::move(E), errs(), "error: ");
|
||||
llvm_unreachable("produced malformed type record");
|
||||
}
|
||||
// emitRawComment will insert its own tab and comment string before
|
||||
// the first line, so strip off our first one. It also prints its own
|
||||
// newline.
|
||||
OS.emitRawComment(
|
||||
CommentOS.str().drop_front(CommentPrefix.size() - 1).rtrim());
|
||||
} else {
|
||||
TypeTable.ForEachRecord([&](TypeIndex Index, ArrayRef<uint8_t> Record) {
|
||||
if (OS.isVerboseAsm()) {
|
||||
// Emit a block comment describing the type record for readability.
|
||||
SmallString<512> CommentBlock;
|
||||
raw_svector_ostream CommentOS(CommentBlock);
|
||||
ScopedPrinter SP(CommentOS);
|
||||
SP.setPrefix(CommentPrefix);
|
||||
CVTD.setPrinter(&SP);
|
||||
Error E = CVTD.dump(Record);
|
||||
if (E) {
|
||||
logAllUnhandledErrors(std::move(E), errs(), "error: ");
|
||||
llvm_unreachable("produced malformed type record");
|
||||
}
|
||||
// emitRawComment will insert its own tab and comment string before
|
||||
// the first line, so strip off our first one. It also prints its own
|
||||
// newline.
|
||||
OS.emitRawComment(
|
||||
CommentOS.str().drop_front(CommentPrefix.size() - 1).rtrim());
|
||||
} else {
|
||||
#ifndef NDEBUG
|
||||
// Assert that the type data is valid even if we aren't dumping
|
||||
// comments. The MSVC linker doesn't do much type record validation,
|
||||
// so the first link of an invalid type record can succeed while
|
||||
// subsequent links will fail with LNK1285.
|
||||
ByteStream Stream({Record.bytes_begin(), Record.bytes_end()});
|
||||
CVTypeArray Types;
|
||||
StreamReader Reader(Stream);
|
||||
Error E = Reader.readArray(Types, Reader.getLength());
|
||||
if (!E) {
|
||||
TypeVisitorCallbacks C;
|
||||
E = CVTypeVisitor(C).visitTypeStream(Types);
|
||||
}
|
||||
if (E) {
|
||||
logAllUnhandledErrors(std::move(E), errs(), "error: ");
|
||||
llvm_unreachable("produced malformed type record");
|
||||
}
|
||||
// Assert that the type data is valid even if we aren't dumping
|
||||
// comments. The MSVC linker doesn't do much type record validation,
|
||||
// so the first link of an invalid type record can succeed while
|
||||
// subsequent links will fail with LNK1285.
|
||||
ByteStream Stream(Record);
|
||||
CVTypeArray Types;
|
||||
StreamReader Reader(Stream);
|
||||
Error E = Reader.readArray(Types, Reader.getLength());
|
||||
if (!E) {
|
||||
TypeVisitorCallbacks C;
|
||||
E = CVTypeVisitor(C).visitTypeStream(Types);
|
||||
}
|
||||
if (E) {
|
||||
logAllUnhandledErrors(std::move(E), errs(), "error: ");
|
||||
llvm_unreachable("produced malformed type record");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
OS.EmitBinaryData(Record);
|
||||
});
|
||||
}
|
||||
StringRef S(reinterpret_cast<const char *>(Record.data()), Record.size());
|
||||
OS.EmitBinaryData(S);
|
||||
});
|
||||
}
|
||||
|
||||
namespace {
|
||||
@ -1195,8 +1194,8 @@ TypeIndex CodeViewDebug::lowerTypeArray(const DICompositeType *Ty) {
|
||||
(i == 0 && ElementSize == 0) ? Ty->getSizeInBits() / 8 : ElementSize;
|
||||
|
||||
StringRef Name = (i == 0) ? Ty->getName() : "";
|
||||
ElementTypeIndex = TypeTable.writeKnownType(
|
||||
ArrayRecord(ElementTypeIndex, IndexType, ArraySize, Name));
|
||||
ArrayRecord AR(ElementTypeIndex, IndexType, ArraySize, Name);
|
||||
ElementTypeIndex = TypeTable.writeKnownType(AR);
|
||||
}
|
||||
|
||||
(void)PartiallyIncomplete;
|
||||
@ -1421,7 +1420,8 @@ TypeIndex CodeViewDebug::lowerTypeModifier(const DIDerivedType *Ty) {
|
||||
BaseTy = cast<DIDerivedType>(BaseTy)->getBaseType().resolve();
|
||||
}
|
||||
TypeIndex ModifiedTI = getTypeIndex(BaseTy);
|
||||
return TypeTable.writeKnownType(ModifierRecord(ModifiedTI, Mods));
|
||||
ModifierRecord MR(ModifiedTI, Mods);
|
||||
return TypeTable.writeKnownType(MR);
|
||||
}
|
||||
|
||||
TypeIndex CodeViewDebug::lowerTypeFunction(const DISubroutineType *Ty) {
|
||||
@ -1478,9 +1478,10 @@ TypeIndex CodeViewDebug::lowerTypeMemberFunction(const DISubroutineType *Ty,
|
||||
// TODO: Need to use the correct values for:
|
||||
// FunctionOptions
|
||||
// ThisPointerAdjustment.
|
||||
TypeIndex TI = TypeTable.writeKnownType(MemberFunctionRecord(
|
||||
ReturnTypeIndex, ClassType, ThisTypeIndex, CC, FunctionOptions::None,
|
||||
ArgTypeIndices.size(), ArgListIndex, ThisAdjustment));
|
||||
MemberFunctionRecord MFR(ReturnTypeIndex, ClassType, ThisTypeIndex, CC,
|
||||
FunctionOptions::None, ArgTypeIndices.size(),
|
||||
ArgListIndex, ThisAdjustment);
|
||||
TypeIndex TI = TypeTable.writeKnownType(MFR);
|
||||
|
||||
return TI;
|
||||
}
|
||||
@ -1488,7 +1489,9 @@ TypeIndex CodeViewDebug::lowerTypeMemberFunction(const DISubroutineType *Ty,
|
||||
TypeIndex CodeViewDebug::lowerTypeVFTableShape(const DIDerivedType *Ty) {
|
||||
unsigned VSlotCount = Ty->getSizeInBits() / (8 * Asm->MAI->getPointerSize());
|
||||
SmallVector<VFTableSlotKind, 4> Slots(VSlotCount, VFTableSlotKind::Near);
|
||||
return TypeTable.writeKnownType(VFTableShapeRecord(Slots));
|
||||
|
||||
VFTableShapeRecord VFTSR(Slots);
|
||||
return TypeTable.writeKnownType(VFTSR);
|
||||
}
|
||||
|
||||
static MemberAccess translateAccessFlags(unsigned RecordTag, unsigned Flags) {
|
||||
@ -1578,25 +1581,28 @@ TypeIndex CodeViewDebug::lowerTypeEnum(const DICompositeType *Ty) {
|
||||
if (Ty->isForwardDecl()) {
|
||||
CO |= ClassOptions::ForwardReference;
|
||||
} else {
|
||||
FieldListRecordBuilder Fields;
|
||||
FieldListRecordBuilder FLRB(TypeTable);
|
||||
|
||||
FLRB.begin();
|
||||
for (const DINode *Element : Ty->getElements()) {
|
||||
// We assume that the frontend provides all members in source declaration
|
||||
// order, which is what MSVC does.
|
||||
if (auto *Enumerator = dyn_cast_or_null<DIEnumerator>(Element)) {
|
||||
Fields.writeMemberType(EnumeratorRecord(
|
||||
MemberAccess::Public, APSInt::getUnsigned(Enumerator->getValue()),
|
||||
Enumerator->getName()));
|
||||
EnumeratorRecord ER(MemberAccess::Public,
|
||||
APSInt::getUnsigned(Enumerator->getValue()),
|
||||
Enumerator->getName());
|
||||
FLRB.writeMemberType(ER);
|
||||
EnumeratorCount++;
|
||||
}
|
||||
}
|
||||
FTI = TypeTable.writeFieldList(Fields);
|
||||
FTI = FLRB.end();
|
||||
}
|
||||
|
||||
std::string FullName = getFullyQualifiedName(Ty);
|
||||
|
||||
return TypeTable.writeKnownType(EnumRecord(EnumeratorCount, CO, FTI, FullName,
|
||||
Ty->getIdentifier(),
|
||||
getTypeIndex(Ty->getBaseType())));
|
||||
EnumRecord ER(EnumeratorCount, CO, FTI, FullName, Ty->getIdentifier(),
|
||||
getTypeIndex(Ty->getBaseType()));
|
||||
return TypeTable.writeKnownType(ER);
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
@ -1695,9 +1701,9 @@ TypeIndex CodeViewDebug::lowerTypeClass(const DICompositeType *Ty) {
|
||||
ClassOptions CO =
|
||||
ClassOptions::ForwardReference | getCommonClassOptions(Ty);
|
||||
std::string FullName = getFullyQualifiedName(Ty);
|
||||
TypeIndex FwdDeclTI = TypeTable.writeKnownType(
|
||||
ClassRecord(Kind, 0, CO, TypeIndex(), TypeIndex(), TypeIndex(), 0,
|
||||
FullName, Ty->getIdentifier()));
|
||||
ClassRecord CR(Kind, 0, CO, TypeIndex(), TypeIndex(), TypeIndex(), 0,
|
||||
FullName, Ty->getIdentifier());
|
||||
TypeIndex FwdDeclTI = TypeTable.writeKnownType(CR);
|
||||
if (!Ty->isForwardDecl())
|
||||
DeferredCompleteTypes.push_back(Ty);
|
||||
return FwdDeclTI;
|
||||
@ -1721,14 +1727,14 @@ TypeIndex CodeViewDebug::lowerCompleteTypeClass(const DICompositeType *Ty) {
|
||||
|
||||
uint64_t SizeInBytes = Ty->getSizeInBits() / 8;
|
||||
|
||||
TypeIndex ClassTI = TypeTable.writeKnownType(
|
||||
ClassRecord(Kind, FieldCount, CO, FieldTI, TypeIndex(), VShapeTI,
|
||||
SizeInBytes, FullName, Ty->getIdentifier()));
|
||||
ClassRecord CR(Kind, FieldCount, CO, FieldTI, TypeIndex(), VShapeTI,
|
||||
SizeInBytes, FullName, Ty->getIdentifier());
|
||||
TypeIndex ClassTI = TypeTable.writeKnownType(CR);
|
||||
|
||||
TypeTable.writeKnownType(UdtSourceLineRecord(
|
||||
ClassTI, TypeTable.writeKnownType(StringIdRecord(
|
||||
TypeIndex(0x0), getFullFilepath(Ty->getFile()))),
|
||||
Ty->getLine()));
|
||||
StringIdRecord SIDR(TypeIndex(0x0), getFullFilepath(Ty->getFile()));
|
||||
TypeIndex SIDI = TypeTable.writeKnownType(SIDR);
|
||||
UdtSourceLineRecord USLR(ClassTI, SIDI, Ty->getLine());
|
||||
TypeTable.writeKnownType(USLR);
|
||||
|
||||
addToUDTs(Ty, ClassTI);
|
||||
|
||||
@ -1739,8 +1745,8 @@ TypeIndex CodeViewDebug::lowerTypeUnion(const DICompositeType *Ty) {
|
||||
ClassOptions CO =
|
||||
ClassOptions::ForwardReference | getCommonClassOptions(Ty);
|
||||
std::string FullName = getFullyQualifiedName(Ty);
|
||||
TypeIndex FwdDeclTI = TypeTable.writeKnownType(
|
||||
UnionRecord(0, CO, TypeIndex(), 0, FullName, Ty->getIdentifier()));
|
||||
UnionRecord UR(0, CO, TypeIndex(), 0, FullName, Ty->getIdentifier());
|
||||
TypeIndex FwdDeclTI = TypeTable.writeKnownType(UR);
|
||||
if (!Ty->isForwardDecl())
|
||||
DeferredCompleteTypes.push_back(Ty);
|
||||
return FwdDeclTI;
|
||||
@ -1760,13 +1766,14 @@ TypeIndex CodeViewDebug::lowerCompleteTypeUnion(const DICompositeType *Ty) {
|
||||
uint64_t SizeInBytes = Ty->getSizeInBits() / 8;
|
||||
std::string FullName = getFullyQualifiedName(Ty);
|
||||
|
||||
TypeIndex UnionTI = TypeTable.writeKnownType(UnionRecord(
|
||||
FieldCount, CO, FieldTI, SizeInBytes, FullName, Ty->getIdentifier()));
|
||||
UnionRecord UR(FieldCount, CO, FieldTI, SizeInBytes, FullName,
|
||||
Ty->getIdentifier());
|
||||
TypeIndex UnionTI = TypeTable.writeKnownType(UR);
|
||||
|
||||
TypeTable.writeKnownType(UdtSourceLineRecord(
|
||||
UnionTI, TypeTable.writeKnownType(StringIdRecord(
|
||||
TypeIndex(0x0), getFullFilepath(Ty->getFile()))),
|
||||
Ty->getLine()));
|
||||
StringIdRecord SIR(TypeIndex(0x0), getFullFilepath(Ty->getFile()));
|
||||
TypeIndex SIRI = TypeTable.writeKnownType(SIR);
|
||||
UdtSourceLineRecord USLR(UnionTI, SIRI, Ty->getLine());
|
||||
TypeTable.writeKnownType(USLR);
|
||||
|
||||
addToUDTs(Ty, UnionTI);
|
||||
|
||||
@ -1781,7 +1788,8 @@ CodeViewDebug::lowerRecordFieldList(const DICompositeType *Ty) {
|
||||
// list record.
|
||||
unsigned MemberCount = 0;
|
||||
ClassInfo Info = collectClassInfo(Ty);
|
||||
FieldListRecordBuilder Fields;
|
||||
FieldListRecordBuilder FLBR(TypeTable);
|
||||
FLBR.begin();
|
||||
|
||||
// Create base classes.
|
||||
for (const DIDerivedType *I : Info.Inheritance) {
|
||||
@ -1794,16 +1802,19 @@ CodeViewDebug::lowerRecordFieldList(const DICompositeType *Ty) {
|
||||
auto RecordKind = (I->getFlags() & DINode::FlagIndirectVirtualBase) == DINode::FlagIndirectVirtualBase
|
||||
? TypeRecordKind::IndirectVirtualBaseClass
|
||||
: TypeRecordKind::VirtualBaseClass;
|
||||
Fields.writeMemberType(VirtualBaseClassRecord(
|
||||
VirtualBaseClassRecord VBCR(
|
||||
RecordKind, translateAccessFlags(Ty->getTag(), I->getFlags()),
|
||||
getTypeIndex(I->getBaseType()), getVBPTypeIndex(), VBPtrOffset,
|
||||
VBTableIndex));
|
||||
VBTableIndex);
|
||||
|
||||
FLBR.writeMemberType(VBCR);
|
||||
} else {
|
||||
assert(I->getOffsetInBits() % 8 == 0 &&
|
||||
"bases must be on byte boundaries");
|
||||
Fields.writeMemberType(BaseClassRecord(
|
||||
translateAccessFlags(Ty->getTag(), I->getFlags()),
|
||||
getTypeIndex(I->getBaseType()), I->getOffsetInBits() / 8));
|
||||
BaseClassRecord BCR(translateAccessFlags(Ty->getTag(), I->getFlags()),
|
||||
getTypeIndex(I->getBaseType()),
|
||||
I->getOffsetInBits() / 8);
|
||||
FLBR.writeMemberType(BCR);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1816,8 +1827,8 @@ CodeViewDebug::lowerRecordFieldList(const DICompositeType *Ty) {
|
||||
translateAccessFlags(Ty->getTag(), Member->getFlags());
|
||||
|
||||
if (Member->isStaticMember()) {
|
||||
Fields.writeMemberType(
|
||||
StaticDataMemberRecord(Access, MemberBaseType, MemberName));
|
||||
StaticDataMemberRecord SDMR(Access, MemberBaseType, MemberName);
|
||||
FLBR.writeMemberType(SDMR);
|
||||
MemberCount++;
|
||||
continue;
|
||||
}
|
||||
@ -1825,7 +1836,8 @@ CodeViewDebug::lowerRecordFieldList(const DICompositeType *Ty) {
|
||||
// Virtual function pointer member.
|
||||
if ((Member->getFlags() & DINode::FlagArtificial) &&
|
||||
Member->getName().startswith("_vptr$")) {
|
||||
Fields.writeMemberType(VFPtrRecord(getTypeIndex(Member->getBaseType())));
|
||||
VFPtrRecord VFPR(getTypeIndex(Member->getBaseType()));
|
||||
FLBR.writeMemberType(VFPR);
|
||||
MemberCount++;
|
||||
continue;
|
||||
}
|
||||
@ -1840,12 +1852,14 @@ CodeViewDebug::lowerRecordFieldList(const DICompositeType *Ty) {
|
||||
MemberOffsetInBits = CI->getZExtValue() + MemberInfo.BaseOffset;
|
||||
}
|
||||
StartBitOffset -= MemberOffsetInBits;
|
||||
MemberBaseType = TypeTable.writeKnownType(BitFieldRecord(
|
||||
MemberBaseType, Member->getSizeInBits(), StartBitOffset));
|
||||
BitFieldRecord BFR(MemberBaseType, Member->getSizeInBits(),
|
||||
StartBitOffset);
|
||||
MemberBaseType = TypeTable.writeKnownType(BFR);
|
||||
}
|
||||
uint64_t MemberOffsetInBytes = MemberOffsetInBits / 8;
|
||||
Fields.writeMemberType(DataMemberRecord(Access, MemberBaseType,
|
||||
MemberOffsetInBytes, MemberName));
|
||||
DataMemberRecord DMR(Access, MemberBaseType, MemberOffsetInBytes,
|
||||
MemberName);
|
||||
FLBR.writeMemberType(DMR);
|
||||
MemberCount++;
|
||||
}
|
||||
|
||||
@ -1870,23 +1884,23 @@ CodeViewDebug::lowerRecordFieldList(const DICompositeType *Ty) {
|
||||
}
|
||||
assert(Methods.size() > 0 && "Empty methods map entry");
|
||||
if (Methods.size() == 1)
|
||||
Fields.writeMemberType(Methods[0]);
|
||||
FLBR.writeMemberType(Methods[0]);
|
||||
else {
|
||||
TypeIndex MethodList =
|
||||
TypeTable.writeKnownType(MethodOverloadListRecord(Methods));
|
||||
Fields.writeMemberType(
|
||||
OverloadedMethodRecord(Methods.size(), MethodList, Name));
|
||||
MethodOverloadListRecord MOLR(Methods);
|
||||
TypeIndex MethodList = TypeTable.writeKnownType(MOLR);
|
||||
OverloadedMethodRecord OMR(Methods.size(), MethodList, Name);
|
||||
FLBR.writeMemberType(OMR);
|
||||
}
|
||||
}
|
||||
|
||||
// Create nested classes.
|
||||
for (const DICompositeType *Nested : Info.NestedClasses) {
|
||||
NestedTypeRecord R(getTypeIndex(DITypeRef(Nested)), Nested->getName());
|
||||
Fields.writeMemberType(R);
|
||||
FLBR.writeMemberType(R);
|
||||
MemberCount++;
|
||||
}
|
||||
|
||||
TypeIndex FieldTI = TypeTable.writeFieldList(Fields);
|
||||
TypeIndex FieldTI = FLBR.end();
|
||||
return std::make_tuple(FieldTI, Info.VShapeTI, MemberCount,
|
||||
!Info.NestedClasses.empty());
|
||||
}
|
||||
|
@ -20,8 +20,8 @@
|
||||
#include "llvm/CodeGen/AsmPrinter.h"
|
||||
#include "llvm/CodeGen/MachineFunction.h"
|
||||
#include "llvm/CodeGen/MachineModuleInfo.h"
|
||||
#include "llvm/DebugInfo/CodeView/MemoryTypeTableBuilder.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeIndex.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeTableBuilder.h"
|
||||
#include "llvm/IR/DebugInfo.h"
|
||||
#include "llvm/IR/DebugLoc.h"
|
||||
#include "llvm/MC/MCStreamer.h"
|
||||
@ -37,7 +37,7 @@ struct ClassInfo;
|
||||
class LLVM_LIBRARY_VISIBILITY CodeViewDebug : public DebugHandlerBase {
|
||||
MCStreamer &OS;
|
||||
llvm::BumpPtrAllocator Allocator;
|
||||
codeview::MemoryTypeTableBuilder TypeTable;
|
||||
codeview::TypeTableBuilder TypeTable;
|
||||
|
||||
/// Represents the most general definition range.
|
||||
struct LocalVarDefRange {
|
||||
|
@ -4,21 +4,16 @@ add_llvm_library(LLVMDebugInfoCodeView
|
||||
CVSymbolVisitor.cpp
|
||||
CVTypeVisitor.cpp
|
||||
EnumTables.cpp
|
||||
FieldListRecordBuilder.cpp
|
||||
Line.cpp
|
||||
ListRecordBuilder.cpp
|
||||
MemoryTypeTableBuilder.cpp
|
||||
MethodListRecordBuilder.cpp
|
||||
ModuleSubstream.cpp
|
||||
ModuleSubstreamVisitor.cpp
|
||||
RecordSerialization.cpp
|
||||
SymbolDumper.cpp
|
||||
TypeDumper.cpp
|
||||
TypeRecord.cpp
|
||||
TypeRecordBuilder.cpp
|
||||
TypeRecordMapping.cpp
|
||||
TypeSerializer.cpp
|
||||
TypeStreamMerger.cpp
|
||||
TypeTableBuilder.cpp
|
||||
|
||||
ADDITIONAL_HEADER_DIRS
|
||||
${LLVM_MAIN_INCLUDE_DIR}/llvm/DebugInfo/CodeView
|
||||
|
@ -17,24 +17,6 @@
|
||||
using namespace llvm;
|
||||
using namespace llvm::codeview;
|
||||
|
||||
template <typename T>
|
||||
static Expected<CVMemberRecord>
|
||||
deserializeMemberRecord(FieldListDeserializer &Deserializer,
|
||||
msf::StreamReader &Reader, TypeLeafKind Kind) {
|
||||
T MR(static_cast<TypeRecordKind>(Kind));
|
||||
CVMemberRecord CVR;
|
||||
CVR.Kind = Kind;
|
||||
|
||||
if (auto EC = Deserializer.visitMemberBegin(CVR))
|
||||
return std::move(EC);
|
||||
if (auto EC = Deserializer.visitKnownMember(CVR, MR))
|
||||
return std::move(EC);
|
||||
if (auto EC = Deserializer.visitMemberEnd(CVR))
|
||||
return std::move(EC);
|
||||
|
||||
return CVR;
|
||||
}
|
||||
|
||||
CVTypeVisitor::CVTypeVisitor(TypeVisitorCallbacks &Callbacks)
|
||||
: Callbacks(Callbacks) {}
|
||||
|
||||
@ -85,7 +67,8 @@ Error CVTypeVisitor::visitTypeRecord(CVType &Record) {
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error CVTypeVisitor::visitMemberRecord(CVMemberRecord &Record) {
|
||||
static Error visitMemberRecord(CVMemberRecord &Record,
|
||||
TypeVisitorCallbacks &Callbacks) {
|
||||
if (auto EC = Callbacks.visitMemberBegin(Record))
|
||||
return EC;
|
||||
|
||||
@ -113,6 +96,10 @@ Error CVTypeVisitor::visitMemberRecord(CVMemberRecord &Record) {
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error CVTypeVisitor::visitMemberRecord(CVMemberRecord &Record) {
|
||||
return ::visitMemberRecord(Record, Callbacks);
|
||||
}
|
||||
|
||||
/// Visits the type records in Data. Sets the error flag on parse failures.
|
||||
Error CVTypeVisitor::visitTypeStream(const CVTypeArray &Types) {
|
||||
for (auto I : Types) {
|
||||
@ -122,23 +109,6 @@ Error CVTypeVisitor::visitTypeStream(const CVTypeArray &Types) {
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
template <typename MR>
|
||||
static Error visitKnownMember(FieldListDeserializer &Deserializer,
|
||||
msf::StreamReader &Reader, TypeLeafKind Leaf,
|
||||
TypeVisitorCallbacks &Callbacks) {
|
||||
MR Record(static_cast<TypeRecordKind>(Leaf));
|
||||
CVMemberRecord CVR;
|
||||
CVR.Kind = Leaf;
|
||||
|
||||
if (auto EC = Callbacks.visitMemberBegin(CVR))
|
||||
return EC;
|
||||
if (auto EC = Callbacks.visitKnownMember(CVR, Record))
|
||||
return EC;
|
||||
if (auto EC = Callbacks.visitMemberEnd(CVR))
|
||||
return EC;
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error CVTypeVisitor::visitFieldListMemberStream(msf::StreamReader Reader) {
|
||||
FieldListDeserializer Deserializer(Reader);
|
||||
TypeVisitorCallbackPipeline Pipeline;
|
||||
@ -150,25 +120,12 @@ Error CVTypeVisitor::visitFieldListMemberStream(msf::StreamReader Reader) {
|
||||
if (auto EC = Reader.readEnum(Leaf))
|
||||
return EC;
|
||||
|
||||
CVType Record;
|
||||
switch (Leaf) {
|
||||
default:
|
||||
// Field list records do not describe their own length, so we cannot
|
||||
// continue parsing past a type that we don't know how to deserialize.
|
||||
return llvm::make_error<CodeViewError>(
|
||||
cv_error_code::unknown_member_record);
|
||||
#define MEMBER_RECORD(EnumName, EnumVal, Name) \
|
||||
case EnumName: { \
|
||||
if (auto EC = visitKnownMember<Name##Record>(Deserializer, Reader, Leaf, \
|
||||
Pipeline)) \
|
||||
return EC; \
|
||||
break; \
|
||||
}
|
||||
#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) \
|
||||
MEMBER_RECORD(EnumVal, EnumVal, AliasName)
|
||||
#include "llvm/DebugInfo/CodeView/TypeRecords.def"
|
||||
}
|
||||
CVMemberRecord Record;
|
||||
Record.Kind = Leaf;
|
||||
if (auto EC = ::visitMemberRecord(Record, Pipeline))
|
||||
return EC;
|
||||
}
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
|
@ -16,20 +16,39 @@
|
||||
using namespace llvm;
|
||||
using namespace llvm::codeview;
|
||||
|
||||
Error CodeViewRecordIO::beginRecord(uint16_t Kind) {
|
||||
assert(!CurrentRecord.hasValue() && "There is already a record active!");
|
||||
CurrentRecord.emplace();
|
||||
|
||||
CurrentRecord->Kind = Kind;
|
||||
Error CodeViewRecordIO::beginRecord(Optional<uint32_t> MaxLength) {
|
||||
RecordLimit Limit;
|
||||
Limit.MaxLength = MaxLength;
|
||||
Limit.BeginOffset = getCurrentOffset();
|
||||
Limits.push_back(Limit);
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error CodeViewRecordIO::endRecord() {
|
||||
assert(CurrentRecord.hasValue() && "Not in a record!");
|
||||
CurrentRecord.reset();
|
||||
assert(!Limits.empty() && "Not in a record!");
|
||||
Limits.pop_back();
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
uint32_t CodeViewRecordIO::maxFieldLength() const {
|
||||
assert(!Limits.empty() && "Not in a record!");
|
||||
|
||||
// The max length of the next field is the minimum of all lengths that would
|
||||
// be allowed by any of the sub-records we're in. In practice, we can only
|
||||
// ever be at most 1 sub-record deep (in a FieldList), but this works for
|
||||
// the general case.
|
||||
uint32_t Offset = getCurrentOffset();
|
||||
Optional<uint32_t> Min = Limits.front().bytesRemaining(Offset);
|
||||
for (auto X : makeArrayRef(Limits).drop_front()) {
|
||||
Optional<uint32_t> ThisMin = X.bytesRemaining(Offset);
|
||||
if (ThisMin.hasValue())
|
||||
Min = (Min.hasValue()) ? std::min(*Min, *ThisMin) : *ThisMin;
|
||||
}
|
||||
assert(Min.hasValue() && "Every field must have a maximum length!");
|
||||
|
||||
return *Min;
|
||||
}
|
||||
|
||||
Error CodeViewRecordIO::skipPadding() {
|
||||
assert(!isWriting() && "Cannot skip padding while writing!");
|
||||
|
||||
@ -114,7 +133,9 @@ Error CodeViewRecordIO::mapEncodedInteger(APSInt &Value) {
|
||||
|
||||
Error CodeViewRecordIO::mapStringZ(StringRef &Value) {
|
||||
if (isWriting()) {
|
||||
if (auto EC = Writer->writeZeroString(Value))
|
||||
// Truncate if we attempt to write too much.
|
||||
StringRef S = Value.take_front(maxFieldLength() - 1);
|
||||
if (auto EC = Writer->writeZeroString(S))
|
||||
return EC;
|
||||
} else {
|
||||
if (auto EC = Reader->readZeroString(Value))
|
||||
@ -124,6 +145,10 @@ Error CodeViewRecordIO::mapStringZ(StringRef &Value) {
|
||||
}
|
||||
|
||||
Error CodeViewRecordIO::mapGuid(StringRef &Guid) {
|
||||
constexpr uint32_t GuidSize = 16;
|
||||
if (maxFieldLength() < GuidSize)
|
||||
return make_error<CodeViewError>(cv_error_code::insufficient_buffer);
|
||||
|
||||
if (isWriting()) {
|
||||
assert(Guid.size() == 16 && "Invalid Guid Size!");
|
||||
if (auto EC = Writer->writeFixedString(Guid))
|
||||
|
@ -1,132 +0,0 @@
|
||||
//===-- FieldListRecordBuilder.cpp ----------------------------------------===//
|
||||
//
|
||||
// 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/FieldListRecordBuilder.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace codeview;
|
||||
|
||||
FieldListRecordBuilder::FieldListRecordBuilder()
|
||||
: ListRecordBuilder(TypeRecordKind::FieldList) {}
|
||||
|
||||
void FieldListRecordBuilder::writeMemberType(const BaseClassRecord &Record) {
|
||||
TypeRecordBuilder &Builder = getBuilder();
|
||||
|
||||
Builder.writeTypeRecordKind(TypeRecordKind::BaseClass);
|
||||
Builder.writeUInt16(static_cast<uint16_t>(Record.getAccess()));
|
||||
Builder.writeTypeIndex(Record.getBaseType());
|
||||
Builder.writeEncodedUnsignedInteger(Record.getBaseOffset());
|
||||
|
||||
finishSubRecord();
|
||||
}
|
||||
|
||||
void FieldListRecordBuilder::writeMemberType(const EnumeratorRecord &Record) {
|
||||
TypeRecordBuilder &Builder = getBuilder();
|
||||
|
||||
Builder.writeTypeRecordKind(TypeRecordKind::Enumerator);
|
||||
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::writeMemberType(const DataMemberRecord &Record) {
|
||||
TypeRecordBuilder &Builder = getBuilder();
|
||||
|
||||
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::writeMemberType(
|
||||
const OverloadedMethodRecord &Record) {
|
||||
TypeRecordBuilder &Builder = getBuilder();
|
||||
|
||||
Builder.writeTypeRecordKind(TypeRecordKind::OverloadedMethod);
|
||||
Builder.writeUInt16(Record.getNumOverloads());
|
||||
Builder.writeTypeIndex(Record.getMethodList());
|
||||
Builder.writeNullTerminatedString(Record.getName());
|
||||
|
||||
finishSubRecord();
|
||||
}
|
||||
|
||||
void FieldListRecordBuilder::writeMemberType(const OneMethodRecord &Record) {
|
||||
TypeRecordBuilder &Builder = getBuilder();
|
||||
|
||||
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(Record.getType());
|
||||
if (Record.isIntroducingVirtual()) {
|
||||
assert(Record.getVFTableOffset() >= 0);
|
||||
Builder.writeInt32(Record.getVFTableOffset());
|
||||
} else {
|
||||
assert(Record.getVFTableOffset() == -1);
|
||||
}
|
||||
|
||||
Builder.writeNullTerminatedString(Record.getName());
|
||||
|
||||
finishSubRecord();
|
||||
}
|
||||
|
||||
void FieldListRecordBuilder::writeMemberType(const NestedTypeRecord &Record) {
|
||||
TypeRecordBuilder &Builder = getBuilder();
|
||||
|
||||
Builder.writeTypeRecordKind(Record.getKind());
|
||||
Builder.writeUInt16(0);
|
||||
Builder.writeTypeIndex(Record.getNestedType());
|
||||
Builder.writeNullTerminatedString(Record.getName());
|
||||
|
||||
finishSubRecord();
|
||||
}
|
||||
|
||||
void FieldListRecordBuilder::writeMemberType(
|
||||
const StaticDataMemberRecord &Record) {
|
||||
TypeRecordBuilder &Builder = getBuilder();
|
||||
|
||||
Builder.writeTypeRecordKind(Record.getKind());
|
||||
Builder.writeUInt16(static_cast<uint16_t>(Record.getAccess()));
|
||||
Builder.writeTypeIndex(Record.getType());
|
||||
Builder.writeNullTerminatedString(Record.getName());
|
||||
|
||||
finishSubRecord();
|
||||
}
|
||||
|
||||
void FieldListRecordBuilder::writeMemberType(
|
||||
const VirtualBaseClassRecord &Record) {
|
||||
TypeRecordBuilder &Builder = getBuilder();
|
||||
|
||||
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::writeMemberType(const VFPtrRecord &Record) {
|
||||
TypeRecordBuilder &Builder = getBuilder();
|
||||
|
||||
Builder.writeTypeRecordKind(TypeRecordKind::VFPtr);
|
||||
Builder.writeUInt16(0);
|
||||
Builder.writeTypeIndex(Record.getType());
|
||||
|
||||
finishSubRecord();
|
||||
}
|
@ -1,103 +0,0 @@
|
||||
//===-- ListRecordBuilder.cpp ---------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
#include "llvm/DebugInfo/CodeView/ListRecordBuilder.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeTableBuilder.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace codeview;
|
||||
|
||||
ListRecordBuilder::ListRecordBuilder(TypeRecordKind Kind)
|
||||
: Kind(Kind), Builder(Kind) {}
|
||||
|
||||
void ListRecordBuilder::writeMemberType(const ListContinuationRecord &R) {
|
||||
TypeRecordBuilder &Builder = getBuilder();
|
||||
|
||||
assert(getLastContinuationSize() < MaxRecordLength - 8 && "continuation won't fit");
|
||||
|
||||
Builder.writeTypeRecordKind(TypeRecordKind::ListContinuation);
|
||||
Builder.writeUInt16(0);
|
||||
Builder.writeTypeIndex(R.getContinuationIndex());
|
||||
|
||||
// End the current segment manually so that nothing comes after the
|
||||
// continuation.
|
||||
ContinuationOffsets.push_back(Builder.size());
|
||||
SubrecordStart = Builder.size();
|
||||
}
|
||||
|
||||
void ListRecordBuilder::finishSubRecord() {
|
||||
// The type table inserts a 16 bit size field before each list, so factor that
|
||||
// into our alignment padding.
|
||||
uint32_t Remainder =
|
||||
(Builder.size() + 2 * (ContinuationOffsets.size() + 1)) % 4;
|
||||
if (Remainder != 0) {
|
||||
for (int32_t PaddingBytesLeft = 4 - Remainder; PaddingBytesLeft > 0;
|
||||
--PaddingBytesLeft) {
|
||||
Builder.writeUInt8(LF_PAD0 + PaddingBytesLeft);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if this subrecord makes the current segment not fit in 64K minus the
|
||||
// space for a continuation record (8 bytes). If the segment does not fit,
|
||||
// back up and insert a continuation record, sliding the current subrecord
|
||||
// down.
|
||||
if (getLastContinuationSize() > MaxRecordLength - 8) {
|
||||
assert(SubrecordStart != 0 && "can't slide from the start!");
|
||||
SmallString<128> SubrecordCopy(
|
||||
Builder.str().slice(SubrecordStart, Builder.size()));
|
||||
assert(SubrecordCopy.size() < MaxRecordLength - 8 &&
|
||||
"subrecord is too large to slide!");
|
||||
Builder.truncate(SubrecordStart);
|
||||
|
||||
// Write a placeholder continuation record.
|
||||
Builder.writeTypeRecordKind(TypeRecordKind::ListContinuation);
|
||||
Builder.writeUInt16(0);
|
||||
Builder.writeUInt32(0);
|
||||
ContinuationOffsets.push_back(Builder.size());
|
||||
assert(Builder.size() == SubrecordStart + 8 && "wrong continuation size");
|
||||
assert(getLastContinuationSize() < MaxRecordLength && "segment too big");
|
||||
|
||||
// Start a new list record of the appropriate kind, and copy the previous
|
||||
// subrecord into place.
|
||||
Builder.writeTypeRecordKind(Kind);
|
||||
Builder.writeBytes(SubrecordCopy);
|
||||
}
|
||||
|
||||
SubrecordStart = Builder.size();
|
||||
}
|
||||
|
||||
TypeIndex ListRecordBuilder::writeListRecord(TypeTableBuilder &Table) {
|
||||
// Get the continuation segments as a reversed vector of StringRefs for
|
||||
// convenience.
|
||||
SmallVector<StringRef, 1> Segments;
|
||||
StringRef Data = str();
|
||||
size_t LastEnd = 0;
|
||||
for (size_t SegEnd : ContinuationOffsets) {
|
||||
Segments.push_back(Data.slice(LastEnd, SegEnd));
|
||||
LastEnd = SegEnd;
|
||||
}
|
||||
Segments.push_back(Data.slice(LastEnd, Builder.size()));
|
||||
|
||||
// Pop the last record off and emit it directly.
|
||||
StringRef LastRec = Segments.pop_back_val();
|
||||
TypeIndex ContinuationIndex = Table.writeRecord(LastRec);
|
||||
|
||||
// Emit each record with a continuation in reverse order, so that each one
|
||||
// references the previous record.
|
||||
for (StringRef Rec : reverse(Segments)) {
|
||||
assert(*reinterpret_cast<const ulittle16_t *>(Rec.data()) ==
|
||||
unsigned(Kind));
|
||||
ulittle32_t *ContinuationPtr =
|
||||
reinterpret_cast<ulittle32_t *>(const_cast<char *>(Rec.end())) - 1;
|
||||
*ContinuationPtr = ContinuationIndex.getIndex();
|
||||
ContinuationIndex = Table.writeRecord(Rec);
|
||||
}
|
||||
return ContinuationIndex;
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
//===-- MemoryTypeTableBuilder.cpp ----------------------------------------===//
|
||||
//
|
||||
// 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/MemoryTypeTableBuilder.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeIndex.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace codeview;
|
||||
|
||||
TypeIndex MemoryTypeTableBuilder::writeRecord(StringRef Data) {
|
||||
assert(Data.size() <= UINT16_MAX);
|
||||
auto I = HashedRecords.find(Data);
|
||||
if (I != HashedRecords.end()) {
|
||||
return I->second;
|
||||
}
|
||||
|
||||
// The record provided by the user lacks the 2 byte size field prefix and is
|
||||
// not padded to 4 bytes. Ultimately, that is what gets emitted in the object
|
||||
// file, so pad it out now.
|
||||
const int SizeOfRecLen = 2;
|
||||
const int Align = 4;
|
||||
int TotalSize = alignTo(Data.size() + SizeOfRecLen, Align);
|
||||
assert(TotalSize - SizeOfRecLen <= UINT16_MAX);
|
||||
char *Mem =
|
||||
reinterpret_cast<char *>(RecordStorage.Allocate(TotalSize, Align));
|
||||
*reinterpret_cast<ulittle16_t *>(Mem) = uint16_t(TotalSize - SizeOfRecLen);
|
||||
memcpy(Mem + SizeOfRecLen, Data.data(), Data.size());
|
||||
for (int I = Data.size() + SizeOfRecLen; I < TotalSize; ++I)
|
||||
Mem[I] = LF_PAD0 + (TotalSize - I);
|
||||
|
||||
TypeIndex TI(static_cast<uint32_t>(Records.size()) +
|
||||
TypeIndex::FirstNonSimpleIndex);
|
||||
|
||||
// Use only the data supplied by the user as a key to the hash table, so that
|
||||
// future lookups will succeed.
|
||||
HashedRecords.insert(std::make_pair(StringRef(Mem + SizeOfRecLen, Data.size()), TI));
|
||||
Records.push_back(StringRef(Mem, TotalSize));
|
||||
|
||||
return TI;
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
//===-- MethodListRecordBuilder.cpp ---------------------------------------===//
|
||||
//
|
||||
// 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/MethodListRecordBuilder.h"
|
||||
#include "llvm/DebugInfo/CodeView/FieldListRecordBuilder.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace codeview;
|
||||
|
||||
MethodListRecordBuilder::MethodListRecordBuilder()
|
||||
: ListRecordBuilder(TypeRecordKind::MethodOverloadList) {}
|
||||
|
||||
void MethodListRecordBuilder::writeMethod(MemberAccess Access, MethodKind Kind,
|
||||
MethodOptions Options, TypeIndex Type,
|
||||
int32_t VTableSlotOffset) {
|
||||
TypeRecordBuilder &Builder = getBuilder();
|
||||
|
||||
uint16_t Flags = static_cast<uint16_t>(Access);
|
||||
Flags |= static_cast<uint16_t>(Kind) << MethodKindShift;
|
||||
Flags |= static_cast<uint16_t>(Options);
|
||||
|
||||
Builder.writeUInt16(Flags);
|
||||
Builder.writeUInt16(0);
|
||||
Builder.writeTypeIndex(Type);
|
||||
switch (Kind) {
|
||||
case MethodKind::IntroducingVirtual:
|
||||
case MethodKind::PureIntroducingVirtual:
|
||||
assert(VTableSlotOffset >= 0);
|
||||
Builder.writeInt32(VTableSlotOffset);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(VTableSlotOffset == -1);
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO: Fail if too big?
|
||||
}
|
||||
|
||||
void MethodListRecordBuilder::writeMethod(const MethodInfo &Method) {
|
||||
writeMethod(Method.getAccess(), Method.getKind(), Method.getOptions(),
|
||||
Method.getType(), Method.getVTableSlotOffset());
|
||||
}
|
@ -428,7 +428,7 @@ Error CVTypeDumper::visitKnownRecord(CVRecord<TypeLeafKind> &CVR,
|
||||
MethodOverloadListRecord &MethodList) {
|
||||
for (auto &M : MethodList.getMethods()) {
|
||||
ListScope S(*W, "Method");
|
||||
printMemberAttributes(M.getAccess(), M.getKind(), M.getOptions());
|
||||
printMemberAttributes(M.getAccess(), M.getMethodKind(), M.getOptions());
|
||||
printTypeIndex("Type", M.getType());
|
||||
if (M.isIntroducingVirtual())
|
||||
W->printHex("VFTableOffset", M.getVFTableOffset());
|
||||
@ -607,7 +607,7 @@ Error CVTypeDumper::visitKnownMember(CVMemberRecord &CVR,
|
||||
|
||||
Error CVTypeDumper::visitKnownMember(CVMemberRecord &CVR,
|
||||
OneMethodRecord &Method) {
|
||||
MethodKind K = Method.getKind();
|
||||
MethodKind K = Method.getMethodKind();
|
||||
printMemberAttributes(Method.getAccess(), K, Method.getOptions());
|
||||
printTypeIndex("Type", Method.getType());
|
||||
// If virtual, then read the vftable offset.
|
||||
|
@ -1,119 +0,0 @@
|
||||
//===-- TypeRecordBuilder.cpp ---------------------------------------------===//
|
||||
//
|
||||
// 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/TypeRecordBuilder.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace codeview;
|
||||
|
||||
TypeRecordBuilder::TypeRecordBuilder(TypeRecordKind Kind)
|
||||
: Kind(Kind), Stream(Buffer), Writer(Stream) {
|
||||
writeTypeRecordKind(Kind);
|
||||
}
|
||||
|
||||
StringRef TypeRecordBuilder::str() {
|
||||
return StringRef(Buffer.data(), Buffer.size());
|
||||
}
|
||||
|
||||
void TypeRecordBuilder::writeUInt8(uint8_t Value) {
|
||||
Writer.write(Value);
|
||||
}
|
||||
|
||||
void TypeRecordBuilder::writeInt16(int16_t Value) {
|
||||
Writer.write(Value);
|
||||
}
|
||||
|
||||
void TypeRecordBuilder::writeUInt16(uint16_t Value) {
|
||||
Writer.write(Value);
|
||||
}
|
||||
|
||||
void TypeRecordBuilder::writeInt32(int32_t Value) {
|
||||
Writer.write(Value);
|
||||
}
|
||||
|
||||
void TypeRecordBuilder::writeUInt32(uint32_t Value) {
|
||||
Writer.write(Value);
|
||||
}
|
||||
|
||||
void TypeRecordBuilder::writeInt64(int64_t Value) {
|
||||
Writer.write(Value);
|
||||
}
|
||||
|
||||
void TypeRecordBuilder::writeUInt64(uint64_t Value) {
|
||||
Writer.write(Value);
|
||||
}
|
||||
|
||||
void TypeRecordBuilder::writeEncodedInteger(int64_t Value) {
|
||||
if (Value >= 0) {
|
||||
writeEncodedUnsignedInteger(static_cast<uint64_t>(Value));
|
||||
} else {
|
||||
writeEncodedSignedInteger(Value);
|
||||
}
|
||||
}
|
||||
|
||||
void TypeRecordBuilder::writeEncodedSignedInteger(int64_t Value) {
|
||||
if (Value >= std::numeric_limits<int8_t>::min() &&
|
||||
Value <= std::numeric_limits<int8_t>::max()) {
|
||||
writeUInt16(LF_CHAR);
|
||||
writeInt16(static_cast<int8_t>(Value));
|
||||
} else if (Value >= std::numeric_limits<int16_t>::min() &&
|
||||
Value <= std::numeric_limits<int16_t>::max()) {
|
||||
writeUInt16(LF_SHORT);
|
||||
writeInt16(static_cast<int16_t>(Value));
|
||||
} else if (Value >= std::numeric_limits<int32_t>::min() &&
|
||||
Value <= std::numeric_limits<int32_t>::max()) {
|
||||
writeUInt16(LF_LONG);
|
||||
writeInt32(static_cast<int32_t>(Value));
|
||||
} else {
|
||||
writeUInt16(LF_QUADWORD);
|
||||
writeInt64(Value);
|
||||
}
|
||||
}
|
||||
|
||||
void TypeRecordBuilder::writeEncodedUnsignedInteger(uint64_t Value) {
|
||||
if (Value < LF_CHAR) {
|
||||
writeUInt16(static_cast<uint16_t>(Value));
|
||||
} else if (Value <= std::numeric_limits<uint16_t>::max()) {
|
||||
writeUInt16(LF_USHORT);
|
||||
writeUInt16(static_cast<uint16_t>(Value));
|
||||
} else if (Value <= std::numeric_limits<uint32_t>::max()) {
|
||||
writeUInt16(LF_ULONG);
|
||||
writeUInt32(static_cast<uint32_t>(Value));
|
||||
} else {
|
||||
writeUInt16(LF_UQUADWORD);
|
||||
writeUInt64(Value);
|
||||
}
|
||||
}
|
||||
|
||||
void TypeRecordBuilder::writeNullTerminatedString(StringRef Value) {
|
||||
// Usually the null terminated string comes last, so truncate it to avoid a
|
||||
// record larger than MaxNameLength. Don't do this if this is a list record.
|
||||
// Those have special handling to split the record.
|
||||
unsigned MaxNameLength = MaxRecordLength;
|
||||
if (Kind != TypeRecordKind::FieldList &&
|
||||
Kind != TypeRecordKind::MethodOverloadList)
|
||||
MaxNameLength = maxBytesRemaining();
|
||||
assert(MaxNameLength > 0 && "need room for null terminator");
|
||||
Value = Value.take_front(MaxNameLength - 1);
|
||||
Stream.write(Value.data(), Value.size());
|
||||
writeUInt8(0);
|
||||
}
|
||||
|
||||
void TypeRecordBuilder::writeGuid(StringRef Guid) {
|
||||
assert(Guid.size() == 16);
|
||||
Stream.write(Guid.data(), 16);
|
||||
}
|
||||
|
||||
void TypeRecordBuilder::writeTypeIndex(TypeIndex TypeInd) {
|
||||
writeUInt32(TypeInd.getIndex());
|
||||
}
|
||||
|
||||
void TypeRecordBuilder::writeTypeRecordKind(TypeRecordKind Kind) {
|
||||
writeUInt16(static_cast<uint16_t>(Kind));
|
||||
}
|
@ -17,24 +17,6 @@ using namespace llvm::codeview;
|
||||
return EC;
|
||||
|
||||
namespace {
|
||||
struct MapStringZ {
|
||||
Error operator()(CodeViewRecordIO &IO, StringRef &S) const {
|
||||
return IO.mapStringZ(S);
|
||||
}
|
||||
};
|
||||
|
||||
struct MapInteger {
|
||||
template <typename T> Error operator()(CodeViewRecordIO &IO, T &N) const {
|
||||
return IO.mapInteger(N);
|
||||
}
|
||||
};
|
||||
|
||||
struct MapEnum {
|
||||
template <typename T> Error operator()(CodeViewRecordIO &IO, T &N) const {
|
||||
return IO.mapEnum(N);
|
||||
}
|
||||
};
|
||||
|
||||
struct MapOneMethodRecord {
|
||||
explicit MapOneMethodRecord(bool IsFromOverloadList)
|
||||
: IsFromOverloadList(IsFromOverloadList) {}
|
||||
@ -64,35 +46,97 @@ private:
|
||||
|
||||
static Error mapNameAndUniqueName(CodeViewRecordIO &IO, StringRef &Name,
|
||||
StringRef &UniqueName, bool HasUniqueName) {
|
||||
error(IO.mapStringZ(Name));
|
||||
if (HasUniqueName)
|
||||
error(IO.mapStringZ(UniqueName));
|
||||
if (IO.isWriting()) {
|
||||
// Try to be smart about what we write here. We can't write anything too
|
||||
// large, so if we're going to go over the limit, truncate both the name
|
||||
// and unique name by the same amount.
|
||||
uint32_t BytesLeft = IO.maxFieldLength();
|
||||
if (HasUniqueName) {
|
||||
uint32_t BytesNeeded = Name.size() + UniqueName.size() + 2;
|
||||
StringRef N = Name;
|
||||
StringRef U = UniqueName;
|
||||
if (BytesNeeded > BytesLeft) {
|
||||
uint32_t BytesToDrop = (BytesNeeded - BytesLeft);
|
||||
uint32_t DropN = std::min(N.size(), BytesToDrop / 2);
|
||||
uint32_t DropU = std::min(U.size(), BytesToDrop - DropN);
|
||||
|
||||
N = N.drop_back(DropN);
|
||||
U = U.drop_back(DropU);
|
||||
}
|
||||
|
||||
error(IO.mapStringZ(N));
|
||||
error(IO.mapStringZ(U));
|
||||
} else {
|
||||
uint32_t BytesNeeded = Name.size() + 1;
|
||||
StringRef N = Name;
|
||||
if (BytesNeeded > BytesLeft) {
|
||||
uint32_t BytesToDrop = std::min(N.size(), BytesToDrop);
|
||||
N = N.drop_back(BytesToDrop);
|
||||
}
|
||||
error(IO.mapStringZ(N));
|
||||
}
|
||||
} else {
|
||||
error(IO.mapStringZ(Name));
|
||||
if (HasUniqueName)
|
||||
error(IO.mapStringZ(UniqueName));
|
||||
}
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitTypeBegin(CVType &CVR) {
|
||||
error(IO.beginRecord(CVR.Type));
|
||||
assert(!TypeKind.hasValue() && "Already in a type mapping!");
|
||||
assert(!MemberKind.hasValue() && "Already in a member mapping!");
|
||||
|
||||
// FieldList and MethodList records can be any length because they can be
|
||||
// split with continuation records. All other record types cannot be
|
||||
// longer than the maximum record length.
|
||||
Optional<uint32_t> MaxLen;
|
||||
if (CVR.Type != TypeLeafKind::LF_FIELDLIST &&
|
||||
CVR.Type != TypeLeafKind::LF_METHODLIST)
|
||||
MaxLen = MaxRecordLength - sizeof(RecordPrefix);
|
||||
error(IO.beginRecord(MaxLen));
|
||||
TypeKind = CVR.Type;
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitTypeEnd(CVType &Record) {
|
||||
assert(TypeKind.hasValue() && "Not in a type mapping!");
|
||||
assert(!MemberKind.hasValue() && "Still in a member mapping!");
|
||||
|
||||
error(IO.endRecord());
|
||||
|
||||
TypeKind.reset();
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitMemberBegin(CVMemberRecord &Record) {
|
||||
assert(TypeKind.hasValue() && "Not in a type mapping!");
|
||||
assert(!MemberKind.hasValue() && "Already in a member mapping!");
|
||||
|
||||
// The largest possible subrecord is one in which there is a record prefix,
|
||||
// followed by the subrecord, followed by a continuation, and that entire
|
||||
// sequence spaws `MaxRecordLength` bytes. So the record's length is
|
||||
// calculated as follows.
|
||||
constexpr uint32_t ContinuationLength = 8;
|
||||
error(IO.beginRecord(MaxRecordLength - sizeof(RecordPrefix) -
|
||||
ContinuationLength));
|
||||
|
||||
MemberKind = Record.Kind;
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitMemberEnd(CVMemberRecord &Record) {
|
||||
assert(TypeKind.hasValue() && "Not in a type mapping!");
|
||||
assert(MemberKind.hasValue() && "Not in a member mapping!");
|
||||
|
||||
if (!IO.isWriting()) {
|
||||
if (auto EC = IO.skipPadding())
|
||||
return EC;
|
||||
}
|
||||
|
||||
MemberKind.reset();
|
||||
error(IO.endRecord());
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
@ -129,7 +173,9 @@ Error TypeRecordMapping::visitKnownRecord(CVType &CVR,
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownRecord(CVType &CVR, ArgListRecord &Record) {
|
||||
error(IO.mapVectorN<uint32_t>(Record.StringIndices, MapInteger()));
|
||||
error(IO.mapVectorN<uint32_t>(
|
||||
Record.StringIndices,
|
||||
[](CodeViewRecordIO &IO, TypeIndex &N) { return IO.mapInteger(N); }));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
@ -245,7 +291,9 @@ Error TypeRecordMapping::visitKnownRecord(CVType &CVR, VFTableRecord &Record) {
|
||||
NamesLen += Name.size() + 1;
|
||||
}
|
||||
error(IO.mapInteger(NamesLen));
|
||||
error(IO.mapVectorTail(Record.MethodNames, MapStringZ()));
|
||||
error(IO.mapVectorTail(
|
||||
Record.MethodNames,
|
||||
[](CodeViewRecordIO &IO, StringRef &S) { return IO.mapStringZ(S); }));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
@ -295,7 +343,9 @@ Error TypeRecordMapping::visitKnownRecord(CVType &CVR,
|
||||
|
||||
Error TypeRecordMapping::visitKnownRecord(CVType &CVR,
|
||||
BuildInfoRecord &Record) {
|
||||
error(IO.mapVectorN<uint16_t>(Record.ArgIndices, MapInteger()));
|
||||
error(IO.mapVectorN<uint16_t>(
|
||||
Record.ArgIndices,
|
||||
[](CodeViewRecordIO &IO, TypeIndex &N) { return IO.mapInteger(N); }));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
243
lib/DebugInfo/CodeView/TypeSerializer.cpp
Normal file
243
lib/DebugInfo/CodeView/TypeSerializer.cpp
Normal file
@ -0,0 +1,243 @@
|
||||
//===- TypeSerialzier.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/TypeSerializer.h"
|
||||
|
||||
#include "llvm/DebugInfo/MSF/StreamWriter.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::codeview;
|
||||
|
||||
bool TypeSerializer::isInFieldList() const {
|
||||
return TypeKind.hasValue() && *TypeKind == TypeLeafKind::LF_FIELDLIST;
|
||||
}
|
||||
|
||||
TypeIndex TypeSerializer::calcNextTypeIndex() const {
|
||||
if (LastTypeIndex.isNoneType())
|
||||
return TypeIndex(TypeIndex::FirstNonSimpleIndex);
|
||||
else
|
||||
return TypeIndex(LastTypeIndex.getIndex() + 1);
|
||||
}
|
||||
|
||||
TypeIndex TypeSerializer::incrementTypeIndex() {
|
||||
TypeIndex Previous = LastTypeIndex;
|
||||
LastTypeIndex = calcNextTypeIndex();
|
||||
return Previous;
|
||||
}
|
||||
|
||||
MutableArrayRef<uint8_t> TypeSerializer::getCurrentSubRecordData() {
|
||||
assert(isInFieldList());
|
||||
return getCurrentRecordData().drop_front(CurrentSegment.length());
|
||||
}
|
||||
|
||||
MutableArrayRef<uint8_t> TypeSerializer::getCurrentRecordData() {
|
||||
return MutableArrayRef<uint8_t>(RecordBuffer).take_front(Writer.getOffset());
|
||||
}
|
||||
|
||||
Error TypeSerializer::writeRecordPrefix(TypeLeafKind Kind) {
|
||||
RecordPrefix Prefix;
|
||||
Prefix.RecordKind = Kind;
|
||||
Prefix.RecordLen = 0;
|
||||
if (auto EC = Writer.writeObject(Prefix))
|
||||
return EC;
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
TypeIndex
|
||||
TypeSerializer::insertRecordBytesPrivate(MutableArrayRef<uint8_t> Record) {
|
||||
assert(Record.size() % 4 == 0 && "Record is not aligned to 4 bytes!");
|
||||
|
||||
StringRef S(reinterpret_cast<const char *>(Record.data()), Record.size());
|
||||
|
||||
TypeIndex NextTypeIndex = calcNextTypeIndex();
|
||||
auto Result = HashedRecords.try_emplace(S, NextTypeIndex);
|
||||
if (Result.second) {
|
||||
LastTypeIndex = NextTypeIndex;
|
||||
SeenRecords.push_back(Record);
|
||||
}
|
||||
return Result.first->getValue();
|
||||
}
|
||||
|
||||
Expected<MutableArrayRef<uint8_t>>
|
||||
TypeSerializer::addPadding(MutableArrayRef<uint8_t> Record) {
|
||||
uint32_t Align = Record.size() % 4;
|
||||
if (Align == 0)
|
||||
return Record;
|
||||
|
||||
int PaddingBytes = 4 - Align;
|
||||
int N = PaddingBytes;
|
||||
while (PaddingBytes > 0) {
|
||||
uint8_t Pad = static_cast<uint8_t>(LF_PAD0 + PaddingBytes);
|
||||
if (auto EC = Writer.writeInteger(Pad))
|
||||
return std::move(EC);
|
||||
--PaddingBytes;
|
||||
}
|
||||
return MutableArrayRef<uint8_t>(Record.data(), Record.size() + N);
|
||||
}
|
||||
|
||||
TypeSerializer::TypeSerializer(BumpPtrAllocator &Storage)
|
||||
: RecordStorage(Storage), LastTypeIndex(),
|
||||
RecordBuffer(MaxRecordLength * 2), Stream(RecordBuffer), Writer(Stream),
|
||||
Mapping(Writer) {
|
||||
// RecordBuffer needs to be able to hold enough data so that if we are 1
|
||||
// byte short of MaxRecordLen, and then we try to write MaxRecordLen bytes,
|
||||
// we won't overflow.
|
||||
}
|
||||
|
||||
ArrayRef<MutableArrayRef<uint8_t>> TypeSerializer::records() const {
|
||||
return SeenRecords;
|
||||
}
|
||||
|
||||
TypeIndex TypeSerializer::getLastTypeIndex() const { return LastTypeIndex; }
|
||||
|
||||
TypeIndex TypeSerializer::insertRecordBytes(MutableArrayRef<uint8_t> Record) {
|
||||
assert(!TypeKind.hasValue() && "Already in a type mapping!");
|
||||
assert(Writer.getOffset() == 0 && "Stream has data already!");
|
||||
|
||||
return insertRecordBytesPrivate(Record);
|
||||
}
|
||||
|
||||
Error TypeSerializer::visitTypeBegin(CVType &Record) {
|
||||
assert(!TypeKind.hasValue() && "Already in a type mapping!");
|
||||
assert(Writer.getOffset() == 0 && "Stream has data already!");
|
||||
|
||||
if (auto EC = writeRecordPrefix(Record.kind()))
|
||||
return EC;
|
||||
|
||||
TypeKind = Record.kind();
|
||||
if (auto EC = Mapping.visitTypeBegin(Record))
|
||||
return EC;
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Expected<TypeIndex> TypeSerializer::visitTypeEndGetIndex(CVType &Record) {
|
||||
assert(TypeKind.hasValue() && "Not in a type mapping!");
|
||||
if (auto EC = Mapping.visitTypeEnd(Record))
|
||||
return std::move(EC);
|
||||
|
||||
// Update the record's length and fill out the CVType members to point to
|
||||
// the stable memory holding the record's data.
|
||||
auto ThisRecordData = getCurrentRecordData();
|
||||
auto ExpectedData = addPadding(ThisRecordData);
|
||||
if (!ExpectedData)
|
||||
return ExpectedData.takeError();
|
||||
ThisRecordData = *ExpectedData;
|
||||
|
||||
RecordPrefix *Prefix =
|
||||
reinterpret_cast<RecordPrefix *>(ThisRecordData.data());
|
||||
Prefix->RecordLen = ThisRecordData.size() - sizeof(uint16_t);
|
||||
|
||||
uint8_t *Copy = RecordStorage.Allocate<uint8_t>(ThisRecordData.size());
|
||||
::memcpy(Copy, ThisRecordData.data(), ThisRecordData.size());
|
||||
ThisRecordData = MutableArrayRef<uint8_t>(Copy, ThisRecordData.size());
|
||||
Record = CVType(*TypeKind, ThisRecordData);
|
||||
TypeIndex InsertedTypeIndex = insertRecordBytesPrivate(ThisRecordData);
|
||||
|
||||
// Write out each additional segment in reverse order, and update each
|
||||
// record's continuation index to point to the previous one.
|
||||
for (auto X : reverse(FieldListSegments)) {
|
||||
auto CIBytes = X.take_back(sizeof(uint32_t));
|
||||
support::ulittle32_t *CI =
|
||||
reinterpret_cast<support::ulittle32_t *>(CIBytes.data());
|
||||
assert(*CI == 0xB0C0B0C0 && "Invalid TypeIndex placeholder");
|
||||
*CI = InsertedTypeIndex.getIndex();
|
||||
InsertedTypeIndex = insertRecordBytesPrivate(X);
|
||||
}
|
||||
|
||||
TypeKind.reset();
|
||||
Writer.setOffset(0);
|
||||
FieldListSegments.clear();
|
||||
CurrentSegment.SubRecords.clear();
|
||||
|
||||
return InsertedTypeIndex;
|
||||
}
|
||||
|
||||
Error TypeSerializer::visitTypeEnd(CVType &Record) {
|
||||
auto ExpectedIndex = visitTypeEndGetIndex(Record);
|
||||
if (!ExpectedIndex)
|
||||
return ExpectedIndex.takeError();
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeSerializer::visitMemberBegin(CVMemberRecord &Record) {
|
||||
assert(isInFieldList() && "Not in a field list!");
|
||||
assert(!MemberKind.hasValue() && "Already in a member record!");
|
||||
MemberKind = Record.Kind;
|
||||
|
||||
if (auto EC = Mapping.visitMemberBegin(Record))
|
||||
return EC;
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeSerializer::visitMemberEnd(CVMemberRecord &Record) {
|
||||
if (auto EC = Mapping.visitMemberEnd(Record))
|
||||
return EC;
|
||||
|
||||
// Check if this subrecord makes the current segment not fit in 64K minus
|
||||
// the space for a continuation record (8 bytes). If the segment does not
|
||||
// fit, insert a continuation record.
|
||||
if (Writer.getOffset() > MaxRecordLength - ContinuationLength) {
|
||||
MutableArrayRef<uint8_t> Data = getCurrentRecordData();
|
||||
SubRecord LastSubRecord = CurrentSegment.SubRecords.back();
|
||||
uint32_t CopySize = CurrentSegment.length() - LastSubRecord.Size;
|
||||
auto CopyData = Data.take_front(CopySize);
|
||||
auto LeftOverData = Data.drop_front(CopySize);
|
||||
assert(LastSubRecord.Size == LeftOverData.size());
|
||||
|
||||
// Allocate stable storage for the record and copy the old record plus
|
||||
// continuation over.
|
||||
uint16_t LengthWithSize = CopySize + ContinuationLength;
|
||||
assert(LengthWithSize <= MaxRecordLength);
|
||||
RecordPrefix *Prefix = reinterpret_cast<RecordPrefix *>(CopyData.data());
|
||||
Prefix->RecordLen = LengthWithSize - sizeof(uint16_t);
|
||||
|
||||
uint8_t *SegmentBytes = RecordStorage.Allocate<uint8_t>(LengthWithSize);
|
||||
auto SavedSegment = MutableArrayRef<uint8_t>(SegmentBytes, LengthWithSize);
|
||||
msf::MutableByteStream CS(SavedSegment);
|
||||
msf::StreamWriter CW(CS);
|
||||
if (auto EC = CW.writeBytes(CopyData))
|
||||
return EC;
|
||||
if (auto EC = CW.writeEnum(TypeLeafKind::LF_INDEX))
|
||||
return EC;
|
||||
if (auto EC = CW.writeInteger(uint16_t(0)))
|
||||
return EC;
|
||||
if (auto EC = CW.writeInteger(uint32_t(0xB0C0B0C0)))
|
||||
return EC;
|
||||
FieldListSegments.push_back(SavedSegment);
|
||||
|
||||
// Write a new placeholder record prefix to mark the start of this new
|
||||
// top-level record.
|
||||
Writer.setOffset(0);
|
||||
if (auto EC = writeRecordPrefix(TypeLeafKind::LF_FIELDLIST))
|
||||
return EC;
|
||||
|
||||
// Then move over the subrecord that overflowed the old segment to the
|
||||
// beginning of this segment. Note that we have to use memmove here
|
||||
// instead of Writer.writeBytes(), because the new and old locations
|
||||
// could overlap.
|
||||
::memmove(Stream.data().data() + sizeof(RecordPrefix), LeftOverData.data(),
|
||||
LeftOverData.size());
|
||||
// And point the segment writer at the end of that subrecord.
|
||||
Writer.setOffset(LeftOverData.size() + sizeof(RecordPrefix));
|
||||
|
||||
CurrentSegment.SubRecords.clear();
|
||||
CurrentSegment.SubRecords.push_back(LastSubRecord);
|
||||
}
|
||||
|
||||
// Update the CVMemberRecord since we may have shifted around or gotten
|
||||
// padded.
|
||||
Record.Data = getCurrentSubRecordData();
|
||||
|
||||
MemberKind.reset();
|
||||
return Error::success();
|
||||
}
|
@ -11,10 +11,10 @@
|
||||
#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/TypeDeserializer.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeIndex.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeTableBuilder.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
@ -54,7 +54,8 @@ namespace {
|
||||
/// existing destination type index.
|
||||
class TypeStreamMerger : public TypeVisitorCallbacks {
|
||||
public:
|
||||
TypeStreamMerger(TypeTableBuilder &DestStream) : DestStream(DestStream) {
|
||||
TypeStreamMerger(TypeTableBuilder &DestStream)
|
||||
: DestStream(DestStream), FieldListBuilder(DestStream) {
|
||||
assert(!hadError());
|
||||
}
|
||||
|
||||
@ -94,7 +95,7 @@ private:
|
||||
template <typename RecordType>
|
||||
Error visitKnownMemberRecordImpl(RecordType &Record) {
|
||||
FoundBadTypeIndex |= !Record.remapTypeIndices(IndexMap);
|
||||
FieldBuilder.writeMemberType(Record);
|
||||
FieldListBuilder.writeMemberType(Record);
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
@ -102,9 +103,10 @@ private:
|
||||
|
||||
bool FoundBadTypeIndex = false;
|
||||
|
||||
FieldListRecordBuilder FieldBuilder;
|
||||
BumpPtrAllocator Allocator;
|
||||
|
||||
TypeTableBuilder &DestStream;
|
||||
FieldListRecordBuilder FieldListBuilder;
|
||||
|
||||
bool IsInFieldList{false};
|
||||
size_t BeginIndexMapSize = 0;
|
||||
@ -120,6 +122,7 @@ Error TypeStreamMerger::visitTypeBegin(CVRecord<TypeLeafKind> &Rec) {
|
||||
if (Rec.Type == TypeLeafKind::LF_FIELDLIST) {
|
||||
assert(!IsInFieldList);
|
||||
IsInFieldList = true;
|
||||
FieldListBuilder.begin();
|
||||
} else
|
||||
BeginIndexMapSize = IndexMap.size();
|
||||
return Error::success();
|
||||
@ -127,8 +130,8 @@ Error TypeStreamMerger::visitTypeBegin(CVRecord<TypeLeafKind> &Rec) {
|
||||
|
||||
Error TypeStreamMerger::visitTypeEnd(CVRecord<TypeLeafKind> &Rec) {
|
||||
if (Rec.Type == TypeLeafKind::LF_FIELDLIST) {
|
||||
IndexMap.push_back(DestStream.writeFieldList(FieldBuilder));
|
||||
FieldBuilder.reset();
|
||||
TypeIndex Index = FieldListBuilder.end();
|
||||
IndexMap.push_back(Index);
|
||||
IsInFieldList = false;
|
||||
}
|
||||
return Error::success();
|
||||
|
@ -1,300 +0,0 @@
|
||||
//===-- TypeTableBuilder.cpp ----------------------------------------------===//
|
||||
//
|
||||
// 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/TypeTableBuilder.h"
|
||||
#include "llvm/DebugInfo/CodeView/FieldListRecordBuilder.h"
|
||||
#include "llvm/DebugInfo/CodeView/MethodListRecordBuilder.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeIndex.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeRecordBuilder.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace codeview;
|
||||
|
||||
TypeTableBuilder::TypeTableBuilder() {}
|
||||
|
||||
TypeTableBuilder::~TypeTableBuilder() {}
|
||||
|
||||
TypeIndex TypeTableBuilder::writeKnownType(const ModifierRecord &Record) {
|
||||
TypeRecordBuilder Builder(Record.getKind());
|
||||
|
||||
Builder.writeTypeIndex(Record.getModifiedType());
|
||||
Builder.writeUInt16(static_cast<uint16_t>(Record.getModifiers()));
|
||||
|
||||
return writeRecord(Builder);
|
||||
}
|
||||
|
||||
TypeIndex TypeTableBuilder::writeKnownType(const ProcedureRecord &Record) {
|
||||
TypeRecordBuilder Builder(Record.getKind());
|
||||
|
||||
Builder.writeTypeIndex(Record.getReturnType());
|
||||
Builder.writeUInt8(static_cast<uint8_t>(Record.getCallConv()));
|
||||
Builder.writeUInt8(static_cast<uint8_t>(Record.getOptions()));
|
||||
Builder.writeUInt16(Record.getParameterCount());
|
||||
Builder.writeTypeIndex(Record.getArgumentList());
|
||||
|
||||
return writeRecord(Builder);
|
||||
}
|
||||
|
||||
TypeIndex TypeTableBuilder::writeKnownType(const MemberFunctionRecord &Record) {
|
||||
TypeRecordBuilder Builder(Record.getKind());
|
||||
|
||||
Builder.writeTypeIndex(Record.getReturnType());
|
||||
Builder.writeTypeIndex(Record.getClassType());
|
||||
Builder.writeTypeIndex(Record.getThisType());
|
||||
Builder.writeUInt8(static_cast<uint8_t>(Record.getCallConv()));
|
||||
Builder.writeUInt8(static_cast<uint8_t>(Record.getOptions()));
|
||||
Builder.writeUInt16(Record.getParameterCount());
|
||||
Builder.writeTypeIndex(Record.getArgumentList());
|
||||
Builder.writeInt32(Record.getThisPointerAdjustment());
|
||||
|
||||
return writeRecord(Builder);
|
||||
}
|
||||
|
||||
TypeIndex TypeTableBuilder::writeKnownType(const ArgListRecord &Record) {
|
||||
TypeRecordBuilder Builder(Record.getKind());
|
||||
|
||||
Builder.writeUInt32(Record.getIndices().size());
|
||||
for (TypeIndex TI : Record.getIndices()) {
|
||||
Builder.writeTypeIndex(TI);
|
||||
}
|
||||
|
||||
return writeRecord(Builder);
|
||||
}
|
||||
|
||||
TypeIndex TypeTableBuilder::writeKnownType(const PointerRecord &Record) {
|
||||
TypeRecordBuilder Builder(Record.getKind());
|
||||
|
||||
Builder.writeTypeIndex(Record.getReferentType());
|
||||
Builder.writeUInt32(Record.Attrs);
|
||||
|
||||
if (Record.isPointerToMember()) {
|
||||
const MemberPointerInfo &M = Record.getMemberInfo();
|
||||
Builder.writeTypeIndex(M.getContainingType());
|
||||
Builder.writeUInt16(static_cast<uint16_t>(M.getRepresentation()));
|
||||
}
|
||||
|
||||
return writeRecord(Builder);
|
||||
}
|
||||
|
||||
static void writeNameAndUniqueName(TypeRecordBuilder &Builder, ClassOptions CO,
|
||||
StringRef Name, StringRef UniqueName) {
|
||||
// Truncate the names to half the remaining record length.
|
||||
unsigned MaxNameLength = Builder.maxBytesRemaining() / 2;
|
||||
Name = Name.take_front(MaxNameLength - 1);
|
||||
UniqueName = UniqueName.take_front(MaxNameLength - 1);
|
||||
|
||||
Builder.writeNullTerminatedString(Name);
|
||||
if ((CO & ClassOptions::HasUniqueName) != ClassOptions::None) {
|
||||
Builder.writeNullTerminatedString(UniqueName);
|
||||
}
|
||||
}
|
||||
|
||||
TypeIndex TypeTableBuilder::writeKnownType(const ArrayRecord &Record) {
|
||||
TypeRecordBuilder Builder(Record.getKind());
|
||||
|
||||
Builder.writeTypeIndex(Record.getElementType());
|
||||
Builder.writeTypeIndex(Record.getIndexType());
|
||||
Builder.writeEncodedUnsignedInteger(Record.getSize());
|
||||
Builder.writeNullTerminatedString(Record.getName());
|
||||
|
||||
return writeRecord(Builder);
|
||||
}
|
||||
|
||||
TypeIndex TypeTableBuilder::writeKnownType(const ClassRecord &Record) {
|
||||
assert((Record.getKind() == TypeRecordKind::Struct) ||
|
||||
(Record.getKind() == TypeRecordKind::Class) ||
|
||||
(Record.getKind() == TypeRecordKind::Interface));
|
||||
|
||||
TypeRecordBuilder Builder(Record.getKind());
|
||||
|
||||
Builder.writeUInt16(Record.getMemberCount());
|
||||
uint16_t Flags =
|
||||
static_cast<uint16_t>(Record.getOptions()) |
|
||||
(static_cast<uint16_t>(Record.getHfa()) << ClassRecord::HfaKindShift) |
|
||||
(static_cast<uint16_t>(Record.getWinRTKind())
|
||||
<< ClassRecord::WinRTKindShift);
|
||||
Builder.writeUInt16(Flags);
|
||||
Builder.writeTypeIndex(Record.getFieldList());
|
||||
Builder.writeTypeIndex(Record.getDerivationList());
|
||||
Builder.writeTypeIndex(Record.getVTableShape());
|
||||
Builder.writeEncodedUnsignedInteger(Record.getSize());
|
||||
writeNameAndUniqueName(Builder, Record.getOptions(), Record.getName(),
|
||||
Record.getUniqueName());
|
||||
|
||||
return writeRecord(Builder);
|
||||
}
|
||||
|
||||
TypeIndex TypeTableBuilder::writeKnownType(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());
|
||||
writeNameAndUniqueName(Builder, Record.getOptions(), Record.getName(),
|
||||
Record.getUniqueName());
|
||||
return writeRecord(Builder);
|
||||
}
|
||||
|
||||
TypeIndex TypeTableBuilder::writeKnownType(const EnumRecord &Record) {
|
||||
TypeRecordBuilder Builder(Record.getKind());
|
||||
|
||||
Builder.writeUInt16(Record.getMemberCount());
|
||||
Builder.writeUInt16(static_cast<uint16_t>(Record.getOptions()));
|
||||
Builder.writeTypeIndex(Record.getUnderlyingType());
|
||||
Builder.writeTypeIndex(Record.getFieldList());
|
||||
writeNameAndUniqueName(Builder, Record.getOptions(), Record.getName(),
|
||||
Record.getUniqueName());
|
||||
|
||||
return writeRecord(Builder);
|
||||
}
|
||||
|
||||
TypeIndex TypeTableBuilder::writeKnownType(const BitFieldRecord &Record) {
|
||||
TypeRecordBuilder Builder(Record.getKind());
|
||||
|
||||
Builder.writeTypeIndex(Record.getType());
|
||||
Builder.writeUInt8(Record.getBitSize());
|
||||
Builder.writeUInt8(Record.getBitOffset());
|
||||
|
||||
return writeRecord(Builder);
|
||||
}
|
||||
|
||||
TypeIndex TypeTableBuilder::writeKnownType(const VFTableShapeRecord &Record) {
|
||||
TypeRecordBuilder Builder(Record.getKind());
|
||||
|
||||
ArrayRef<VFTableSlotKind> Slots = Record.getSlots();
|
||||
|
||||
Builder.writeUInt16(Slots.size());
|
||||
for (size_t SlotIndex = 0; SlotIndex < Slots.size(); SlotIndex += 2) {
|
||||
uint8_t Byte = static_cast<uint8_t>(Slots[SlotIndex]) << 4;
|
||||
if ((SlotIndex + 1) < Slots.size()) {
|
||||
Byte |= static_cast<uint8_t>(Slots[SlotIndex + 1]);
|
||||
}
|
||||
Builder.writeUInt8(Byte);
|
||||
}
|
||||
|
||||
return writeRecord(Builder);
|
||||
}
|
||||
|
||||
TypeIndex TypeTableBuilder::writeKnownType(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;
|
||||
|
||||
// FIXME: Avoid creating a record longer than MaxRecordLength.
|
||||
Builder.writeUInt32(NamesLen);
|
||||
Builder.writeNullTerminatedString(Record.getName());
|
||||
for (StringRef MethodName : Record.getMethodNames())
|
||||
Builder.writeNullTerminatedString(MethodName);
|
||||
|
||||
return writeRecord(Builder);
|
||||
}
|
||||
|
||||
TypeIndex TypeTableBuilder::writeKnownType(const StringIdRecord &Record) {
|
||||
TypeRecordBuilder Builder(TypeRecordKind::StringId);
|
||||
Builder.writeTypeIndex(Record.getId());
|
||||
Builder.writeNullTerminatedString(Record.getString());
|
||||
return writeRecord(Builder);
|
||||
}
|
||||
|
||||
TypeIndex TypeTableBuilder::writeKnownType(const UdtSourceLineRecord &Record) {
|
||||
TypeRecordBuilder Builder(Record.getKind());
|
||||
Builder.writeTypeIndex(Record.getUDT());
|
||||
Builder.writeTypeIndex(Record.getSourceFile());
|
||||
Builder.writeUInt32(Record.getLineNumber());
|
||||
return writeRecord(Builder);
|
||||
}
|
||||
|
||||
TypeIndex
|
||||
TypeTableBuilder::writeKnownType(const UdtModSourceLineRecord &Record) {
|
||||
TypeRecordBuilder Builder(Record.getKind());
|
||||
Builder.writeTypeIndex(Record.getUDT());
|
||||
Builder.writeTypeIndex(Record.getSourceFile());
|
||||
Builder.writeUInt32(Record.getLineNumber());
|
||||
Builder.writeUInt16(Record.getModule());
|
||||
return writeRecord(Builder);
|
||||
}
|
||||
|
||||
TypeIndex TypeTableBuilder::writeKnownType(const FuncIdRecord &Record) {
|
||||
TypeRecordBuilder Builder(Record.getKind());
|
||||
Builder.writeTypeIndex(Record.getParentScope());
|
||||
Builder.writeTypeIndex(Record.getFunctionType());
|
||||
Builder.writeNullTerminatedString(Record.getName());
|
||||
return writeRecord(Builder);
|
||||
}
|
||||
|
||||
TypeIndex TypeTableBuilder::writeKnownType(const MemberFuncIdRecord &Record) {
|
||||
TypeRecordBuilder Builder(Record.getKind());
|
||||
Builder.writeTypeIndex(Record.getClassType());
|
||||
Builder.writeTypeIndex(Record.getFunctionType());
|
||||
Builder.writeNullTerminatedString(Record.getName());
|
||||
return writeRecord(Builder);
|
||||
}
|
||||
|
||||
TypeIndex TypeTableBuilder::writeKnownType(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) {
|
||||
TypeIndex I = writeRecord(Builder.str());
|
||||
RecordKinds.push_back(Builder.kind());
|
||||
return I;
|
||||
}
|
||||
|
||||
TypeIndex TypeTableBuilder::writeFieldList(FieldListRecordBuilder &FieldList) {
|
||||
TypeIndex I = FieldList.writeListRecord(*this);
|
||||
RecordKinds.push_back(TypeRecordKind::FieldList);
|
||||
return I;
|
||||
}
|
||||
|
||||
TypeIndex
|
||||
TypeTableBuilder::writeKnownType(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(Builder);
|
||||
}
|
||||
|
||||
TypeIndex TypeTableBuilder::writeKnownType(const TypeServer2Record &Record) {
|
||||
TypeRecordBuilder Builder(Record.getKind());
|
||||
Builder.writeGuid(Record.getGuid());
|
||||
Builder.writeUInt32(Record.getAge());
|
||||
Builder.writeNullTerminatedString(Record.getName());
|
||||
return writeRecord(Builder);
|
||||
}
|
@ -18,7 +18,7 @@
|
||||
#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h"
|
||||
#include "llvm/DebugInfo/CodeView/SymbolVisitorCallbackPipeline.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeSerializationVisitor.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeSerializer.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h"
|
||||
#include "llvm/DebugInfo/PDB/PDBExtras.h"
|
||||
#include "llvm/DebugInfo/PDB/PDBTypes.h"
|
||||
@ -244,8 +244,7 @@ void MappingContextTraits<PdbTpiRecord, pdb::yaml::SerializationContext>::
|
||||
pdb::yaml::SerializationContext &Context) {
|
||||
codeview::TypeVisitorCallbackPipeline Pipeline;
|
||||
codeview::TypeDeserializer Deserializer;
|
||||
codeview::TypeSerializationVisitor Serializer(Context.FieldListBuilder,
|
||||
Context.TypeTableBuilder);
|
||||
codeview::TypeSerializer Serializer(Context.Allocator);
|
||||
pdb::TpiHashUpdater Hasher;
|
||||
|
||||
if (IO.outputting()) {
|
||||
@ -255,6 +254,11 @@ void MappingContextTraits<PdbTpiRecord, pdb::yaml::SerializationContext>::
|
||||
} else {
|
||||
// For Yaml to PDB, extract from the high level record type, then write it
|
||||
// to bytes.
|
||||
|
||||
// This might be interpreted as a hack, but serializing FieldList
|
||||
// sub-records requires having access to the same serializer being used by
|
||||
// the FieldList itself.
|
||||
Context.ActiveSerializer = &Serializer;
|
||||
Pipeline.addCallbackToPipeline(Context.Dumper);
|
||||
Pipeline.addCallbackToPipeline(Serializer);
|
||||
Pipeline.addCallbackToPipeline(Hasher);
|
||||
@ -262,4 +266,5 @@ void MappingContextTraits<PdbTpiRecord, pdb::yaml::SerializationContext>::
|
||||
|
||||
codeview::CVTypeVisitor Visitor(Pipeline);
|
||||
consumeError(Visitor.visitTypeRecord(Obj.Record));
|
||||
Context.ActiveSerializer = nullptr;
|
||||
}
|
||||
|
@ -12,10 +12,12 @@
|
||||
|
||||
#include "PdbYaml.h"
|
||||
#include "YamlTypeDumper.h"
|
||||
#include "llvm/DebugInfo/CodeView/FieldListRecordBuilder.h"
|
||||
#include "llvm/DebugInfo/CodeView/MemoryTypeTableBuilder.h"
|
||||
#include "llvm/Support/Allocator.h"
|
||||
|
||||
namespace llvm {
|
||||
namespace codeview {
|
||||
class TypeSerializer;
|
||||
}
|
||||
namespace yaml {
|
||||
class IO;
|
||||
}
|
||||
@ -24,10 +26,11 @@ namespace pdb {
|
||||
namespace yaml {
|
||||
struct SerializationContext {
|
||||
explicit SerializationContext(llvm::yaml::IO &IO, BumpPtrAllocator &Allocator)
|
||||
: Dumper(IO, *this), TypeTableBuilder(Allocator) {}
|
||||
: Dumper(IO, *this), Allocator(Allocator) {}
|
||||
|
||||
codeview::yaml::YamlTypeDumperCallbacks Dumper;
|
||||
codeview::MemoryTypeTableBuilder TypeTableBuilder;
|
||||
codeview::FieldListRecordBuilder FieldListBuilder;
|
||||
BumpPtrAllocator &Allocator;
|
||||
codeview::TypeSerializer *ActiveSerializer = nullptr;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@
|
||||
#include "llvm/DebugInfo/CodeView/EnumTables.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeSerializationVisitor.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeSerializer.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h"
|
||||
#include "llvm/DebugInfo/PDB/Raw/TpiHashing.h"
|
||||
|
||||
@ -540,15 +540,27 @@ void llvm::codeview::yaml::YamlTypeDumperCallbacks::visitKnownRecordImpl(
|
||||
// which will recurse back to the standard handler for top-level fields
|
||||
// (top-level and member fields all have the exact same Yaml syntax so use
|
||||
// the same parser).
|
||||
//
|
||||
// If we are not outputting, then the array contains no data starting out,
|
||||
// and is instead populated from the sequence represented by the yaml --
|
||||
// again, using the same logic that we use for top-level records.
|
||||
FieldListRecordSplitter Splitter(FieldListRecords);
|
||||
CVTypeVisitor V(Splitter);
|
||||
consumeError(V.visitFieldListMemberStream(FieldList.Data));
|
||||
YamlIO.mapRequired("FieldList", FieldListRecords, Context);
|
||||
} else {
|
||||
// If we are not outputting, then the array contains no data starting out,
|
||||
// and is instead populated from the sequence represented by the yaml --
|
||||
// again, using the same logic that we use for top-level records.
|
||||
assert(Context.ActiveSerializer && "There is no active serializer!");
|
||||
codeview::TypeVisitorCallbackPipeline Pipeline;
|
||||
pdb::TpiHashUpdater Hasher;
|
||||
|
||||
// For Yaml to PDB, dump it (to fill out the record fields from the Yaml)
|
||||
// then serialize those fields to bytes, then update their hashes.
|
||||
Pipeline.addCallbackToPipeline(Context.Dumper);
|
||||
Pipeline.addCallbackToPipeline(*Context.ActiveSerializer);
|
||||
Pipeline.addCallbackToPipeline(Hasher);
|
||||
|
||||
codeview::CVTypeVisitor Visitor(Pipeline);
|
||||
YamlIO.mapRequired("FieldList", FieldListRecords, Visitor);
|
||||
}
|
||||
YamlIO.mapRequired("FieldList", FieldListRecords, Context);
|
||||
}
|
||||
|
||||
namespace llvm {
|
||||
@ -558,31 +570,30 @@ struct MappingContextTraits<pdb::yaml::PdbTpiFieldListRecord,
|
||||
pdb::yaml::SerializationContext> {
|
||||
static void mapping(IO &IO, pdb::yaml::PdbTpiFieldListRecord &Obj,
|
||||
pdb::yaml::SerializationContext &Context) {
|
||||
assert(IO.outputting());
|
||||
codeview::TypeVisitorCallbackPipeline Pipeline;
|
||||
|
||||
msf::ByteStream Data(Obj.Record.Data);
|
||||
msf::StreamReader FieldReader(Data);
|
||||
codeview::FieldListDeserializer Deserializer(FieldReader);
|
||||
codeview::TypeSerializationVisitor Serializer(Context.FieldListBuilder,
|
||||
Context.TypeTableBuilder);
|
||||
pdb::TpiHashUpdater Hasher;
|
||||
|
||||
if (IO.outputting()) {
|
||||
// For PDB to Yaml, deserialize into a high level record type, then dump
|
||||
// it.
|
||||
Pipeline.addCallbackToPipeline(Deserializer);
|
||||
Pipeline.addCallbackToPipeline(Context.Dumper);
|
||||
} else {
|
||||
// For Yaml to PDB, extract from the high level record type, then write it
|
||||
// to bytes.
|
||||
Pipeline.addCallbackToPipeline(Context.Dumper);
|
||||
Pipeline.addCallbackToPipeline(Serializer);
|
||||
Pipeline.addCallbackToPipeline(Hasher);
|
||||
}
|
||||
// For PDB to Yaml, deserialize into a high level record type, then dump
|
||||
// it.
|
||||
Pipeline.addCallbackToPipeline(Deserializer);
|
||||
Pipeline.addCallbackToPipeline(Context.Dumper);
|
||||
|
||||
codeview::CVTypeVisitor Visitor(Pipeline);
|
||||
consumeError(Visitor.visitMemberRecord(Obj.Record));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct MappingContextTraits<pdb::yaml::PdbTpiFieldListRecord,
|
||||
codeview::CVTypeVisitor> {
|
||||
static void mapping(IO &IO, pdb::yaml::PdbTpiFieldListRecord &Obj,
|
||||
codeview::CVTypeVisitor &Visitor) {
|
||||
consumeError(Visitor.visitMemberRecord(Obj.Record));
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,6 @@
|
||||
#define LLVM_TOOLS_LLVMPDBDUMP_YAMLTYPEDUMPER_H
|
||||
|
||||
#include "llvm/DebugInfo/CodeView/CodeView.h"
|
||||
#include "llvm/DebugInfo/CodeView/MemoryTypeTableBuilder.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h"
|
||||
#include "llvm/Support/YAMLTraits.h"
|
||||
|
||||
|
@ -24,7 +24,6 @@
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
#include "llvm/DebugInfo/CodeView/CodeView.h"
|
||||
#include "llvm/DebugInfo/CodeView/Line.h"
|
||||
#include "llvm/DebugInfo/CodeView/MemoryTypeTableBuilder.h"
|
||||
#include "llvm/DebugInfo/CodeView/RecordSerialization.h"
|
||||
#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h"
|
||||
#include "llvm/DebugInfo/CodeView/SymbolDumpDelegate.h"
|
||||
@ -34,6 +33,7 @@
|
||||
#include "llvm/DebugInfo/CodeView/TypeIndex.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeStreamMerger.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeTableBuilder.h"
|
||||
#include "llvm/DebugInfo/MSF/ByteStream.h"
|
||||
#include "llvm/Object/COFF.h"
|
||||
#include "llvm/Object/ObjectFile.h"
|
||||
@ -79,8 +79,7 @@ public:
|
||||
void printCOFFBaseReloc() override;
|
||||
void printCOFFDebugDirectory() override;
|
||||
void printCodeViewDebugInfo() override;
|
||||
void
|
||||
mergeCodeViewTypes(llvm::codeview::MemoryTypeTableBuilder &CVTypes) override;
|
||||
void mergeCodeViewTypes(llvm::codeview::TypeTableBuilder &CVTypes) override;
|
||||
void printStackMap() const override;
|
||||
private:
|
||||
void printSymbol(const SymbolRef &Sym);
|
||||
@ -1063,7 +1062,7 @@ void COFFDumper::printFileNameForOffset(StringRef Label, uint32_t FileOffset) {
|
||||
W.printHex(Label, getFileNameForFileOffset(FileOffset), FileOffset);
|
||||
}
|
||||
|
||||
void COFFDumper::mergeCodeViewTypes(MemoryTypeTableBuilder &CVTypes) {
|
||||
void COFFDumper::mergeCodeViewTypes(TypeTableBuilder &CVTypes) {
|
||||
for (const SectionRef &S : Obj->sections()) {
|
||||
StringRef SectionName;
|
||||
error(S.getName(SectionName));
|
||||
@ -1545,12 +1544,12 @@ void COFFDumper::printStackMap() const {
|
||||
StackMapV2Parser<support::big>(StackMapContentsArray));
|
||||
}
|
||||
|
||||
void llvm::dumpCodeViewMergedTypes(
|
||||
ScopedPrinter &Writer, llvm::codeview::MemoryTypeTableBuilder &CVTypes) {
|
||||
void llvm::dumpCodeViewMergedTypes(ScopedPrinter &Writer,
|
||||
llvm::codeview::TypeTableBuilder &CVTypes) {
|
||||
// Flatten it first, then run our dumper on it.
|
||||
ListScope S(Writer, "MergedTypeStream");
|
||||
SmallString<0> Buf;
|
||||
CVTypes.ForEachRecord([&](TypeIndex TI, StringRef Record) {
|
||||
CVTypes.ForEachRecord([&](TypeIndex TI, ArrayRef<uint8_t> Record) {
|
||||
Buf.append(Record.begin(), Record.end());
|
||||
});
|
||||
CVTypeDumper CVTD(&Writer, opts::CodeViewSubsectionBytes);
|
||||
|
@ -19,7 +19,7 @@ class COFFImportFile;
|
||||
class ObjectFile;
|
||||
}
|
||||
namespace codeview {
|
||||
class MemoryTypeTableBuilder;
|
||||
class TypeTableBuilder;
|
||||
}
|
||||
|
||||
class ScopedPrinter;
|
||||
@ -65,8 +65,7 @@ public:
|
||||
virtual void printCOFFBaseReloc() { }
|
||||
virtual void printCOFFDebugDirectory() { }
|
||||
virtual void printCodeViewDebugInfo() { }
|
||||
virtual void
|
||||
mergeCodeViewTypes(llvm::codeview::MemoryTypeTableBuilder &CVTypes) {}
|
||||
virtual void mergeCodeViewTypes(llvm::codeview::TypeTableBuilder &CVTypes) {}
|
||||
|
||||
// Only implemented for MachO.
|
||||
virtual void printMachODataInCode() { }
|
||||
@ -97,7 +96,7 @@ std::error_code createMachODumper(const object::ObjectFile *Obj,
|
||||
void dumpCOFFImportFile(const object::COFFImportFile *File);
|
||||
|
||||
void dumpCodeViewMergedTypes(ScopedPrinter &Writer,
|
||||
llvm::codeview::MemoryTypeTableBuilder &CVTypes);
|
||||
llvm::codeview::TypeTableBuilder &CVTypes);
|
||||
|
||||
} // namespace llvm
|
||||
|
||||
|
@ -22,7 +22,7 @@
|
||||
#include "llvm-readobj.h"
|
||||
#include "Error.h"
|
||||
#include "ObjDumper.h"
|
||||
#include "llvm/DebugInfo/CodeView/MemoryTypeTableBuilder.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeTableBuilder.h"
|
||||
#include "llvm/Object/Archive.h"
|
||||
#include "llvm/Object/COFFImportFile.h"
|
||||
#include "llvm/Object/ELFObjectFile.h"
|
||||
@ -332,14 +332,14 @@ static bool isMipsArch(unsigned Arch) {
|
||||
}
|
||||
}
|
||||
namespace {
|
||||
struct TypeTableBuilder {
|
||||
TypeTableBuilder() : Allocator(), Builder(Allocator) {}
|
||||
struct ReadObjTypeTableBuilder {
|
||||
ReadObjTypeTableBuilder() : Allocator(), Builder(Allocator) {}
|
||||
|
||||
llvm::BumpPtrAllocator Allocator;
|
||||
llvm::codeview::MemoryTypeTableBuilder Builder;
|
||||
llvm::codeview::TypeTableBuilder Builder;
|
||||
};
|
||||
}
|
||||
static TypeTableBuilder CVTypes;
|
||||
static ReadObjTypeTableBuilder CVTypes;
|
||||
|
||||
/// @brief Creates an format-specific object file dumper.
|
||||
static std::error_code createDumper(const ObjectFile *Obj,
|
||||
|
Loading…
Reference in New Issue
Block a user