mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-25 12:12:47 +01:00
[Debuginfo][COFF] Minimal serialization support for precompiled types records
This change adds support for the LF_PRECOMP and LF_ENDPRECOMP records required to read/write Microsoft precompiled types .objs. See https://en.wikipedia.org/wiki/Precompiled_header#Microsoft_Visual_C_and_C++ This also adds handling for the .debug$P section, which is actually a .debug$T section in disguise, found only in precompiled .objs. Differential Revision: https://reviews.llvm.org/D45283 llvm-svn: 329613
This commit is contained in:
parent
8fce7e82b9
commit
325264aa91
@ -22,8 +22,8 @@
|
||||
namespace llvm {
|
||||
namespace codeview {
|
||||
|
||||
/// Distinguishes individual records in .debug$T section or PDB type stream. The
|
||||
/// documentation and headers talk about this as the "leaf" type.
|
||||
/// Distinguishes individual records in .debug$T or .debug$P section or PDB type
|
||||
/// stream. The documentation and headers talk about this as the "leaf" type.
|
||||
enum class TypeRecordKind : uint16_t {
|
||||
#define TYPE_RECORD(lf_ename, value, name) name = value,
|
||||
#include "CodeViewTypes.def"
|
||||
|
@ -87,6 +87,8 @@ TYPE_RECORD(LF_UDT_MOD_SRC_LINE, 0x1607, UdtModSourceLine)
|
||||
|
||||
TYPE_RECORD(LF_METHODLIST, 0x1206, MethodOverloadList)
|
||||
|
||||
TYPE_RECORD(LF_PRECOMP, 0x1509, Precomp)
|
||||
TYPE_RECORD(LF_ENDPRECOMP, 0x0014, EndPrecomp)
|
||||
|
||||
// 16 bit type records.
|
||||
CV_TYPE(LF_MODIFIER_16t, 0x0001)
|
||||
@ -106,7 +108,6 @@ CV_TYPE(LF_NOTTRAN, 0x0010)
|
||||
CV_TYPE(LF_DIMARRAY_16t, 0x0011)
|
||||
CV_TYPE(LF_VFTPATH_16t, 0x0012)
|
||||
CV_TYPE(LF_PRECOMP_16t, 0x0013)
|
||||
CV_TYPE(LF_ENDPRECOMP, 0x0014)
|
||||
CV_TYPE(LF_OEM_16t, 0x0015)
|
||||
CV_TYPE(LF_TYPESERVER_ST, 0x0016)
|
||||
|
||||
@ -181,7 +182,6 @@ CV_TYPE(LF_MANAGED_ST, 0x140f)
|
||||
CV_TYPE(LF_ST_MAX, 0x1500)
|
||||
CV_TYPE(LF_TYPESERVER, 0x1501)
|
||||
CV_TYPE(LF_DIMARRAY, 0x1508)
|
||||
CV_TYPE(LF_PRECOMP, 0x1509)
|
||||
CV_TYPE(LF_ALIAS, 0x150a)
|
||||
CV_TYPE(LF_DEFARG, 0x150b)
|
||||
CV_TYPE(LF_FRIENDFCN, 0x150c)
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -67,6 +67,7 @@ struct Section {
|
||||
yaml::BinaryRef SectionData;
|
||||
std::vector<CodeViewYAML::YAMLDebugSubsection> DebugS;
|
||||
std::vector<CodeViewYAML::LeafRecord> DebugT;
|
||||
std::vector<CodeViewYAML::LeafRecord> DebugP;
|
||||
Optional<CodeViewYAML::DebugHSection> DebugH;
|
||||
std::vector<Relocation> Relocations;
|
||||
StringRef Name;
|
||||
|
@ -47,7 +47,7 @@ struct DebugHSection {
|
||||
std::vector<GlobalHash> Hashes;
|
||||
};
|
||||
|
||||
DebugHSection fromDebugH(ArrayRef<uint8_t> DebugT);
|
||||
DebugHSection fromDebugH(ArrayRef<uint8_t> DebugH);
|
||||
ArrayRef<uint8_t> toDebugH(const DebugHSection &DebugH,
|
||||
BumpPtrAllocator &Alloc);
|
||||
|
||||
|
@ -51,8 +51,10 @@ struct LeafRecord {
|
||||
static Expected<LeafRecord> fromCodeViewRecord(codeview::CVType Type);
|
||||
};
|
||||
|
||||
std::vector<LeafRecord> fromDebugT(ArrayRef<uint8_t> DebugT);
|
||||
ArrayRef<uint8_t> toDebugT(ArrayRef<LeafRecord>, BumpPtrAllocator &Alloc);
|
||||
std::vector<LeafRecord> fromDebugT(ArrayRef<uint8_t> DebugTorP,
|
||||
StringRef SectionName);
|
||||
ArrayRef<uint8_t> toDebugT(ArrayRef<LeafRecord>, BumpPtrAllocator &Alloc,
|
||||
StringRef SectionName);
|
||||
|
||||
} // end namespace CodeViewYAML
|
||||
|
||||
|
@ -524,7 +524,7 @@ void CodeViewDebug::emitTypeInformation() {
|
||||
if (TypeTable.empty())
|
||||
return;
|
||||
|
||||
// Start the .debug$T section with 0x4.
|
||||
// Start the .debug$T or .debug$P section with 0x4.
|
||||
OS.SwitchSection(Asm->getObjFileLowering().getCOFFDebugTypesSection());
|
||||
emitCodeViewMagicVersion();
|
||||
|
||||
|
@ -239,7 +239,8 @@ class LLVM_LIBRARY_VISIBILITY CodeViewDebug : public DebugHandlerBase {
|
||||
}
|
||||
|
||||
/// Emit the magic version number at the start of a CodeView type or symbol
|
||||
/// section. Appears at the front of every .debug$S or .debug$T section.
|
||||
/// section. Appears at the front of every .debug$S or .debug$T or .debug$P
|
||||
/// section.
|
||||
void emitCodeViewMagicVersion();
|
||||
|
||||
void emitTypeInformation();
|
||||
|
@ -1,323 +1,333 @@
|
||||
//===- RecordName.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/RecordName.h"
|
||||
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
#include "llvm/DebugInfo/CodeView/CVSymbolVisitor.h"
|
||||
#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
|
||||
#include "llvm/DebugInfo/CodeView/SymbolRecordMapping.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h"
|
||||
#include "llvm/Support/FormatVariadic.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::codeview;
|
||||
|
||||
namespace {
|
||||
class TypeNameComputer : public TypeVisitorCallbacks {
|
||||
/// The type collection. Used to calculate names of nested types.
|
||||
TypeCollection &Types;
|
||||
TypeIndex CurrentTypeIndex = TypeIndex::None();
|
||||
|
||||
/// Name of the current type. Only valid before visitTypeEnd.
|
||||
SmallString<256> Name;
|
||||
|
||||
public:
|
||||
explicit TypeNameComputer(TypeCollection &Types) : Types(Types) {}
|
||||
|
||||
StringRef name() const { return Name; }
|
||||
|
||||
/// Paired begin/end actions for all types. Receives all record data,
|
||||
/// including the fixed-length record prefix.
|
||||
Error visitTypeBegin(CVType &Record) override;
|
||||
Error visitTypeBegin(CVType &Record, TypeIndex Index) override;
|
||||
Error visitTypeEnd(CVType &Record) override;
|
||||
|
||||
#define TYPE_RECORD(EnumName, EnumVal, Name) \
|
||||
Error visitKnownRecord(CVType &CVR, Name##Record &Record) override;
|
||||
#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
|
||||
#define MEMBER_RECORD(EnumName, EnumVal, Name)
|
||||
#include "llvm/DebugInfo/CodeView/CodeViewTypes.def"
|
||||
};
|
||||
} // namespace
|
||||
|
||||
Error TypeNameComputer::visitTypeBegin(CVType &Record) {
|
||||
llvm_unreachable("Must call visitTypeBegin with a TypeIndex!");
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitTypeBegin(CVType &Record, TypeIndex Index) {
|
||||
// Reset Name to the empty string. If the visitor sets it, we know it.
|
||||
Name = "";
|
||||
CurrentTypeIndex = Index;
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitTypeEnd(CVType &CVR) { return Error::success(); }
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR,
|
||||
FieldListRecord &FieldList) {
|
||||
Name = "<field list>";
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVRecord<TypeLeafKind> &CVR,
|
||||
StringIdRecord &String) {
|
||||
Name = String.getString();
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR, ArgListRecord &Args) {
|
||||
auto Indices = Args.getIndices();
|
||||
uint32_t Size = Indices.size();
|
||||
Name = "(";
|
||||
for (uint32_t I = 0; I < Size; ++I) {
|
||||
assert(Indices[I] < CurrentTypeIndex);
|
||||
|
||||
Name.append(Types.getTypeName(Indices[I]));
|
||||
if (I + 1 != Size)
|
||||
Name.append(", ");
|
||||
}
|
||||
Name.push_back(')');
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR,
|
||||
StringListRecord &Strings) {
|
||||
auto Indices = Strings.getIndices();
|
||||
uint32_t Size = Indices.size();
|
||||
Name = "\"";
|
||||
for (uint32_t I = 0; I < Size; ++I) {
|
||||
Name.append(Types.getTypeName(Indices[I]));
|
||||
if (I + 1 != Size)
|
||||
Name.append("\" \"");
|
||||
}
|
||||
Name.push_back('\"');
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR, ClassRecord &Class) {
|
||||
Name = Class.getName();
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR, UnionRecord &Union) {
|
||||
Name = Union.getName();
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR, EnumRecord &Enum) {
|
||||
Name = Enum.getName();
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR, ArrayRecord &AT) {
|
||||
Name = AT.getName();
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR, VFTableRecord &VFT) {
|
||||
Name = VFT.getName();
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR, MemberFuncIdRecord &Id) {
|
||||
Name = Id.getName();
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR, ProcedureRecord &Proc) {
|
||||
StringRef Ret = Types.getTypeName(Proc.getReturnType());
|
||||
StringRef Params = Types.getTypeName(Proc.getArgumentList());
|
||||
Name = formatv("{0} {1}", Ret, Params).sstr<256>();
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR,
|
||||
MemberFunctionRecord &MF) {
|
||||
StringRef Ret = Types.getTypeName(MF.getReturnType());
|
||||
StringRef Class = Types.getTypeName(MF.getClassType());
|
||||
StringRef Params = Types.getTypeName(MF.getArgumentList());
|
||||
Name = formatv("{0} {1}::{2}", Ret, Class, Params).sstr<256>();
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR, FuncIdRecord &Func) {
|
||||
Name = Func.getName();
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR, TypeServer2Record &TS) {
|
||||
Name = TS.getName();
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR, PointerRecord &Ptr) {
|
||||
|
||||
if (Ptr.isPointerToMember()) {
|
||||
const MemberPointerInfo &MI = Ptr.getMemberInfo();
|
||||
|
||||
StringRef Pointee = Types.getTypeName(Ptr.getReferentType());
|
||||
StringRef Class = Types.getTypeName(MI.getContainingType());
|
||||
Name = formatv("{0} {1}::*", Pointee, Class);
|
||||
} else {
|
||||
Name.append(Types.getTypeName(Ptr.getReferentType()));
|
||||
|
||||
if (Ptr.getMode() == PointerMode::LValueReference)
|
||||
Name.append("&");
|
||||
else if (Ptr.getMode() == PointerMode::RValueReference)
|
||||
Name.append("&&");
|
||||
else if (Ptr.getMode() == PointerMode::Pointer)
|
||||
Name.append("*");
|
||||
|
||||
// Qualifiers in pointer records apply to the pointer, not the pointee, so
|
||||
// they go on the right.
|
||||
if (Ptr.isConst())
|
||||
Name.append(" const");
|
||||
if (Ptr.isVolatile())
|
||||
Name.append(" volatile");
|
||||
if (Ptr.isUnaligned())
|
||||
Name.append(" __unaligned");
|
||||
if (Ptr.isRestrict())
|
||||
Name.append(" __restrict");
|
||||
}
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR, ModifierRecord &Mod) {
|
||||
uint16_t Mods = static_cast<uint16_t>(Mod.getModifiers());
|
||||
|
||||
if (Mods & uint16_t(ModifierOptions::Const))
|
||||
Name.append("const ");
|
||||
if (Mods & uint16_t(ModifierOptions::Volatile))
|
||||
Name.append("volatile ");
|
||||
if (Mods & uint16_t(ModifierOptions::Unaligned))
|
||||
Name.append("__unaligned ");
|
||||
Name.append(Types.getTypeName(Mod.getModifiedType()));
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR,
|
||||
VFTableShapeRecord &Shape) {
|
||||
Name = formatv("<vftable {0} methods>", Shape.getEntryCount());
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(
|
||||
CVType &CVR, UdtModSourceLineRecord &ModSourceLine) {
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR,
|
||||
UdtSourceLineRecord &SourceLine) {
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR, BitFieldRecord &BF) {
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR,
|
||||
MethodOverloadListRecord &Overloads) {
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR, BuildInfoRecord &BI) {
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR, LabelRecord &R) {
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
std::string llvm::codeview::computeTypeName(TypeCollection &Types,
|
||||
TypeIndex Index) {
|
||||
TypeNameComputer Computer(Types);
|
||||
CVType Record = Types.getType(Index);
|
||||
if (auto EC = visitTypeRecord(Record, Index, Computer)) {
|
||||
consumeError(std::move(EC));
|
||||
return "<unknown UDT>";
|
||||
}
|
||||
return Computer.name();
|
||||
}
|
||||
|
||||
static int getSymbolNameOffset(CVSymbol Sym) {
|
||||
switch (Sym.kind()) {
|
||||
// See ProcSym
|
||||
case SymbolKind::S_GPROC32:
|
||||
case SymbolKind::S_LPROC32:
|
||||
case SymbolKind::S_GPROC32_ID:
|
||||
case SymbolKind::S_LPROC32_ID:
|
||||
case SymbolKind::S_LPROC32_DPC:
|
||||
case SymbolKind::S_LPROC32_DPC_ID:
|
||||
return 35;
|
||||
// See Thunk32Sym
|
||||
case SymbolKind::S_THUNK32:
|
||||
return 21;
|
||||
// See SectionSym
|
||||
case SymbolKind::S_SECTION:
|
||||
return 16;
|
||||
// See CoffGroupSym
|
||||
case SymbolKind::S_COFFGROUP:
|
||||
return 14;
|
||||
// See PublicSym32, FileStaticSym, RegRelativeSym, DataSym, ThreadLocalDataSym
|
||||
case SymbolKind::S_PUB32:
|
||||
case SymbolKind::S_FILESTATIC:
|
||||
case SymbolKind::S_REGREL32:
|
||||
case SymbolKind::S_GDATA32:
|
||||
case SymbolKind::S_LDATA32:
|
||||
case SymbolKind::S_LMANDATA:
|
||||
case SymbolKind::S_GMANDATA:
|
||||
case SymbolKind::S_LTHREAD32:
|
||||
case SymbolKind::S_GTHREAD32:
|
||||
return 10;
|
||||
// See RegisterSym and LocalSym
|
||||
case SymbolKind::S_REGISTER:
|
||||
case SymbolKind::S_LOCAL:
|
||||
return 6;
|
||||
// See BlockSym
|
||||
case SymbolKind::S_BLOCK32:
|
||||
return 18;
|
||||
// See LabelSym
|
||||
case SymbolKind::S_LABEL32:
|
||||
return 7;
|
||||
// See ObjNameSym, ExportSym, and UDTSym
|
||||
case SymbolKind::S_OBJNAME:
|
||||
case SymbolKind::S_EXPORT:
|
||||
case SymbolKind::S_UDT:
|
||||
return 4;
|
||||
// See BPRelativeSym
|
||||
case SymbolKind::S_BPREL32:
|
||||
return 8;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
StringRef llvm::codeview::getSymbolName(CVSymbol Sym) {
|
||||
if (Sym.kind() == SymbolKind::S_CONSTANT) {
|
||||
// S_CONSTANT is preceded by an APSInt, which has a variable length. So we
|
||||
// have to do a full deserialization.
|
||||
BinaryStreamReader Reader(Sym.content(), llvm::support::little);
|
||||
// The container doesn't matter for single records.
|
||||
SymbolRecordMapping Mapping(Reader, CodeViewContainer::ObjectFile);
|
||||
ConstantSym Const(SymbolKind::S_CONSTANT);
|
||||
cantFail(Mapping.visitSymbolBegin(Sym));
|
||||
cantFail(Mapping.visitKnownRecord(Sym, Const));
|
||||
cantFail(Mapping.visitSymbolEnd(Sym));
|
||||
return Const.Name;
|
||||
}
|
||||
|
||||
int Offset = getSymbolNameOffset(Sym);
|
||||
if (Offset == -1)
|
||||
return StringRef();
|
||||
|
||||
StringRef StringData = toStringRef(Sym.content()).drop_front(Offset);
|
||||
return StringData.split('\0').first;
|
||||
}
|
||||
//===- RecordName.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/RecordName.h"
|
||||
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
#include "llvm/DebugInfo/CodeView/CVSymbolVisitor.h"
|
||||
#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
|
||||
#include "llvm/DebugInfo/CodeView/SymbolRecordMapping.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h"
|
||||
#include "llvm/Support/FormatVariadic.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::codeview;
|
||||
|
||||
namespace {
|
||||
class TypeNameComputer : public TypeVisitorCallbacks {
|
||||
/// The type collection. Used to calculate names of nested types.
|
||||
TypeCollection &Types;
|
||||
TypeIndex CurrentTypeIndex = TypeIndex::None();
|
||||
|
||||
/// Name of the current type. Only valid before visitTypeEnd.
|
||||
SmallString<256> Name;
|
||||
|
||||
public:
|
||||
explicit TypeNameComputer(TypeCollection &Types) : Types(Types) {}
|
||||
|
||||
StringRef name() const { return Name; }
|
||||
|
||||
/// Paired begin/end actions for all types. Receives all record data,
|
||||
/// including the fixed-length record prefix.
|
||||
Error visitTypeBegin(CVType &Record) override;
|
||||
Error visitTypeBegin(CVType &Record, TypeIndex Index) override;
|
||||
Error visitTypeEnd(CVType &Record) override;
|
||||
|
||||
#define TYPE_RECORD(EnumName, EnumVal, Name) \
|
||||
Error visitKnownRecord(CVType &CVR, Name##Record &Record) override;
|
||||
#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName)
|
||||
#define MEMBER_RECORD(EnumName, EnumVal, Name)
|
||||
#include "llvm/DebugInfo/CodeView/CodeViewTypes.def"
|
||||
};
|
||||
} // namespace
|
||||
|
||||
Error TypeNameComputer::visitTypeBegin(CVType &Record) {
|
||||
llvm_unreachable("Must call visitTypeBegin with a TypeIndex!");
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitTypeBegin(CVType &Record, TypeIndex Index) {
|
||||
// Reset Name to the empty string. If the visitor sets it, we know it.
|
||||
Name = "";
|
||||
CurrentTypeIndex = Index;
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitTypeEnd(CVType &CVR) { return Error::success(); }
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR,
|
||||
FieldListRecord &FieldList) {
|
||||
Name = "<field list>";
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVRecord<TypeLeafKind> &CVR,
|
||||
StringIdRecord &String) {
|
||||
Name = String.getString();
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR, ArgListRecord &Args) {
|
||||
auto Indices = Args.getIndices();
|
||||
uint32_t Size = Indices.size();
|
||||
Name = "(";
|
||||
for (uint32_t I = 0; I < Size; ++I) {
|
||||
assert(Indices[I] < CurrentTypeIndex);
|
||||
|
||||
Name.append(Types.getTypeName(Indices[I]));
|
||||
if (I + 1 != Size)
|
||||
Name.append(", ");
|
||||
}
|
||||
Name.push_back(')');
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR,
|
||||
StringListRecord &Strings) {
|
||||
auto Indices = Strings.getIndices();
|
||||
uint32_t Size = Indices.size();
|
||||
Name = "\"";
|
||||
for (uint32_t I = 0; I < Size; ++I) {
|
||||
Name.append(Types.getTypeName(Indices[I]));
|
||||
if (I + 1 != Size)
|
||||
Name.append("\" \"");
|
||||
}
|
||||
Name.push_back('\"');
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR, ClassRecord &Class) {
|
||||
Name = Class.getName();
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR, UnionRecord &Union) {
|
||||
Name = Union.getName();
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR, EnumRecord &Enum) {
|
||||
Name = Enum.getName();
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR, ArrayRecord &AT) {
|
||||
Name = AT.getName();
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR, VFTableRecord &VFT) {
|
||||
Name = VFT.getName();
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR, MemberFuncIdRecord &Id) {
|
||||
Name = Id.getName();
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR, ProcedureRecord &Proc) {
|
||||
StringRef Ret = Types.getTypeName(Proc.getReturnType());
|
||||
StringRef Params = Types.getTypeName(Proc.getArgumentList());
|
||||
Name = formatv("{0} {1}", Ret, Params).sstr<256>();
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR,
|
||||
MemberFunctionRecord &MF) {
|
||||
StringRef Ret = Types.getTypeName(MF.getReturnType());
|
||||
StringRef Class = Types.getTypeName(MF.getClassType());
|
||||
StringRef Params = Types.getTypeName(MF.getArgumentList());
|
||||
Name = formatv("{0} {1}::{2}", Ret, Class, Params).sstr<256>();
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR, FuncIdRecord &Func) {
|
||||
Name = Func.getName();
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR, TypeServer2Record &TS) {
|
||||
Name = TS.getName();
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR, PointerRecord &Ptr) {
|
||||
|
||||
if (Ptr.isPointerToMember()) {
|
||||
const MemberPointerInfo &MI = Ptr.getMemberInfo();
|
||||
|
||||
StringRef Pointee = Types.getTypeName(Ptr.getReferentType());
|
||||
StringRef Class = Types.getTypeName(MI.getContainingType());
|
||||
Name = formatv("{0} {1}::*", Pointee, Class);
|
||||
} else {
|
||||
Name.append(Types.getTypeName(Ptr.getReferentType()));
|
||||
|
||||
if (Ptr.getMode() == PointerMode::LValueReference)
|
||||
Name.append("&");
|
||||
else if (Ptr.getMode() == PointerMode::RValueReference)
|
||||
Name.append("&&");
|
||||
else if (Ptr.getMode() == PointerMode::Pointer)
|
||||
Name.append("*");
|
||||
|
||||
// Qualifiers in pointer records apply to the pointer, not the pointee, so
|
||||
// they go on the right.
|
||||
if (Ptr.isConst())
|
||||
Name.append(" const");
|
||||
if (Ptr.isVolatile())
|
||||
Name.append(" volatile");
|
||||
if (Ptr.isUnaligned())
|
||||
Name.append(" __unaligned");
|
||||
if (Ptr.isRestrict())
|
||||
Name.append(" __restrict");
|
||||
}
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR, ModifierRecord &Mod) {
|
||||
uint16_t Mods = static_cast<uint16_t>(Mod.getModifiers());
|
||||
|
||||
if (Mods & uint16_t(ModifierOptions::Const))
|
||||
Name.append("const ");
|
||||
if (Mods & uint16_t(ModifierOptions::Volatile))
|
||||
Name.append("volatile ");
|
||||
if (Mods & uint16_t(ModifierOptions::Unaligned))
|
||||
Name.append("__unaligned ");
|
||||
Name.append(Types.getTypeName(Mod.getModifiedType()));
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR,
|
||||
VFTableShapeRecord &Shape) {
|
||||
Name = formatv("<vftable {0} methods>", Shape.getEntryCount());
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(
|
||||
CVType &CVR, UdtModSourceLineRecord &ModSourceLine) {
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR,
|
||||
UdtSourceLineRecord &SourceLine) {
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR, BitFieldRecord &BF) {
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR,
|
||||
MethodOverloadListRecord &Overloads) {
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR, BuildInfoRecord &BI) {
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR, LabelRecord &R) {
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR,
|
||||
PrecompRecord &Precomp) {
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeNameComputer::visitKnownRecord(CVType &CVR,
|
||||
EndPrecompRecord &EndPrecomp) {
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
std::string llvm::codeview::computeTypeName(TypeCollection &Types,
|
||||
TypeIndex Index) {
|
||||
TypeNameComputer Computer(Types);
|
||||
CVType Record = Types.getType(Index);
|
||||
if (auto EC = visitTypeRecord(Record, Index, Computer)) {
|
||||
consumeError(std::move(EC));
|
||||
return "<unknown UDT>";
|
||||
}
|
||||
return Computer.name();
|
||||
}
|
||||
|
||||
static int getSymbolNameOffset(CVSymbol Sym) {
|
||||
switch (Sym.kind()) {
|
||||
// See ProcSym
|
||||
case SymbolKind::S_GPROC32:
|
||||
case SymbolKind::S_LPROC32:
|
||||
case SymbolKind::S_GPROC32_ID:
|
||||
case SymbolKind::S_LPROC32_ID:
|
||||
case SymbolKind::S_LPROC32_DPC:
|
||||
case SymbolKind::S_LPROC32_DPC_ID:
|
||||
return 35;
|
||||
// See Thunk32Sym
|
||||
case SymbolKind::S_THUNK32:
|
||||
return 21;
|
||||
// See SectionSym
|
||||
case SymbolKind::S_SECTION:
|
||||
return 16;
|
||||
// See CoffGroupSym
|
||||
case SymbolKind::S_COFFGROUP:
|
||||
return 14;
|
||||
// See PublicSym32, FileStaticSym, RegRelativeSym, DataSym, ThreadLocalDataSym
|
||||
case SymbolKind::S_PUB32:
|
||||
case SymbolKind::S_FILESTATIC:
|
||||
case SymbolKind::S_REGREL32:
|
||||
case SymbolKind::S_GDATA32:
|
||||
case SymbolKind::S_LDATA32:
|
||||
case SymbolKind::S_LMANDATA:
|
||||
case SymbolKind::S_GMANDATA:
|
||||
case SymbolKind::S_LTHREAD32:
|
||||
case SymbolKind::S_GTHREAD32:
|
||||
return 10;
|
||||
// See RegisterSym and LocalSym
|
||||
case SymbolKind::S_REGISTER:
|
||||
case SymbolKind::S_LOCAL:
|
||||
return 6;
|
||||
// See BlockSym
|
||||
case SymbolKind::S_BLOCK32:
|
||||
return 18;
|
||||
// See LabelSym
|
||||
case SymbolKind::S_LABEL32:
|
||||
return 7;
|
||||
// See ObjNameSym, ExportSym, and UDTSym
|
||||
case SymbolKind::S_OBJNAME:
|
||||
case SymbolKind::S_EXPORT:
|
||||
case SymbolKind::S_UDT:
|
||||
return 4;
|
||||
// See BPRelativeSym
|
||||
case SymbolKind::S_BPREL32:
|
||||
return 8;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
StringRef llvm::codeview::getSymbolName(CVSymbol Sym) {
|
||||
if (Sym.kind() == SymbolKind::S_CONSTANT) {
|
||||
// S_CONSTANT is preceded by an APSInt, which has a variable length. So we
|
||||
// have to do a full deserialization.
|
||||
BinaryStreamReader Reader(Sym.content(), llvm::support::little);
|
||||
// The container doesn't matter for single records.
|
||||
SymbolRecordMapping Mapping(Reader, CodeViewContainer::ObjectFile);
|
||||
ConstantSym Const(SymbolKind::S_CONSTANT);
|
||||
cantFail(Mapping.visitSymbolBegin(Sym));
|
||||
cantFail(Mapping.visitKnownRecord(Sym, Const));
|
||||
cantFail(Mapping.visitSymbolEnd(Sym));
|
||||
return Const.Name;
|
||||
}
|
||||
|
||||
int Offset = getSymbolNameOffset(Sym);
|
||||
if (Offset == -1)
|
||||
return StringRef();
|
||||
|
||||
StringRef StringData = toStringRef(Sym.content()).drop_front(Offset);
|
||||
return StringData.split('\0').first;
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,482 +1,497 @@
|
||||
//===- TypeRecordMapping.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/TypeRecordMapping.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::codeview;
|
||||
|
||||
#define error(X) \
|
||||
if (auto EC = X) \
|
||||
return EC;
|
||||
|
||||
namespace {
|
||||
struct MapOneMethodRecord {
|
||||
explicit MapOneMethodRecord(bool IsFromOverloadList)
|
||||
: IsFromOverloadList(IsFromOverloadList) {}
|
||||
|
||||
Error operator()(CodeViewRecordIO &IO, OneMethodRecord &Method) const {
|
||||
error(IO.mapInteger(Method.Attrs.Attrs));
|
||||
if (IsFromOverloadList) {
|
||||
uint16_t Padding = 0;
|
||||
error(IO.mapInteger(Padding));
|
||||
}
|
||||
error(IO.mapInteger(Method.Type));
|
||||
if (Method.isIntroducingVirtual()) {
|
||||
error(IO.mapInteger(Method.VFTableOffset));
|
||||
} else if (!IO.isWriting())
|
||||
Method.VFTableOffset = -1;
|
||||
|
||||
if (!IsFromOverloadList)
|
||||
error(IO.mapStringZ(Method.Name));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
private:
|
||||
bool IsFromOverloadList;
|
||||
};
|
||||
}
|
||||
|
||||
static Error mapNameAndUniqueName(CodeViewRecordIO &IO, StringRef &Name,
|
||||
StringRef &UniqueName, bool HasUniqueName) {
|
||||
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.
|
||||
size_t BytesLeft = IO.maxFieldLength();
|
||||
if (HasUniqueName) {
|
||||
size_t BytesNeeded = Name.size() + UniqueName.size() + 2;
|
||||
StringRef N = Name;
|
||||
StringRef U = UniqueName;
|
||||
if (BytesNeeded > BytesLeft) {
|
||||
size_t BytesToDrop = (BytesNeeded - BytesLeft);
|
||||
size_t DropN = std::min(N.size(), BytesToDrop / 2);
|
||||
size_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 {
|
||||
// Cap the length of the string at however many bytes we have available,
|
||||
// plus one for the required null terminator.
|
||||
auto N = StringRef(Name).take_front(BytesLeft - 1);
|
||||
error(IO.mapStringZ(N));
|
||||
}
|
||||
} else {
|
||||
error(IO.mapStringZ(Name));
|
||||
if (HasUniqueName)
|
||||
error(IO.mapStringZ(UniqueName));
|
||||
}
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitTypeBegin(CVType &CVR) {
|
||||
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();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownRecord(CVType &CVR, ModifierRecord &Record) {
|
||||
error(IO.mapInteger(Record.ModifiedType));
|
||||
error(IO.mapEnum(Record.Modifiers));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownRecord(CVType &CVR,
|
||||
ProcedureRecord &Record) {
|
||||
error(IO.mapInteger(Record.ReturnType));
|
||||
error(IO.mapEnum(Record.CallConv));
|
||||
error(IO.mapEnum(Record.Options));
|
||||
error(IO.mapInteger(Record.ParameterCount));
|
||||
error(IO.mapInteger(Record.ArgumentList));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownRecord(CVType &CVR,
|
||||
MemberFunctionRecord &Record) {
|
||||
error(IO.mapInteger(Record.ReturnType));
|
||||
error(IO.mapInteger(Record.ClassType));
|
||||
error(IO.mapInteger(Record.ThisType));
|
||||
error(IO.mapEnum(Record.CallConv));
|
||||
error(IO.mapEnum(Record.Options));
|
||||
error(IO.mapInteger(Record.ParameterCount));
|
||||
error(IO.mapInteger(Record.ArgumentList));
|
||||
error(IO.mapInteger(Record.ThisPointerAdjustment));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownRecord(CVType &CVR, ArgListRecord &Record) {
|
||||
error(IO.mapVectorN<uint32_t>(
|
||||
Record.ArgIndices,
|
||||
[](CodeViewRecordIO &IO, TypeIndex &N) { return IO.mapInteger(N); }));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownRecord(CVType &CVR,
|
||||
StringListRecord &Record) {
|
||||
error(IO.mapVectorN<uint32_t>(
|
||||
Record.StringIndices,
|
||||
[](CodeViewRecordIO &IO, TypeIndex &N) { return IO.mapInteger(N); }));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownRecord(CVType &CVR, PointerRecord &Record) {
|
||||
error(IO.mapInteger(Record.ReferentType));
|
||||
error(IO.mapInteger(Record.Attrs));
|
||||
|
||||
if (Record.isPointerToMember()) {
|
||||
if (!IO.isWriting())
|
||||
Record.MemberInfo.emplace();
|
||||
|
||||
MemberPointerInfo &M = *Record.MemberInfo;
|
||||
error(IO.mapInteger(M.ContainingType));
|
||||
error(IO.mapEnum(M.Representation));
|
||||
}
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownRecord(CVType &CVR, ArrayRecord &Record) {
|
||||
error(IO.mapInteger(Record.ElementType));
|
||||
error(IO.mapInteger(Record.IndexType));
|
||||
error(IO.mapEncodedInteger(Record.Size));
|
||||
error(IO.mapStringZ(Record.Name));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownRecord(CVType &CVR, ClassRecord &Record) {
|
||||
assert((CVR.Type == TypeLeafKind::LF_STRUCTURE) ||
|
||||
(CVR.Type == TypeLeafKind::LF_CLASS) ||
|
||||
(CVR.Type == TypeLeafKind::LF_INTERFACE));
|
||||
|
||||
error(IO.mapInteger(Record.MemberCount));
|
||||
error(IO.mapEnum(Record.Options));
|
||||
error(IO.mapInteger(Record.FieldList));
|
||||
error(IO.mapInteger(Record.DerivationList));
|
||||
error(IO.mapInteger(Record.VTableShape));
|
||||
error(IO.mapEncodedInteger(Record.Size));
|
||||
error(mapNameAndUniqueName(IO, Record.Name, Record.UniqueName,
|
||||
Record.hasUniqueName()));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownRecord(CVType &CVR, UnionRecord &Record) {
|
||||
error(IO.mapInteger(Record.MemberCount));
|
||||
error(IO.mapEnum(Record.Options));
|
||||
error(IO.mapInteger(Record.FieldList));
|
||||
error(IO.mapEncodedInteger(Record.Size));
|
||||
error(mapNameAndUniqueName(IO, Record.Name, Record.UniqueName,
|
||||
Record.hasUniqueName()));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownRecord(CVType &CVR, EnumRecord &Record) {
|
||||
error(IO.mapInteger(Record.MemberCount));
|
||||
error(IO.mapEnum(Record.Options));
|
||||
error(IO.mapInteger(Record.UnderlyingType));
|
||||
error(IO.mapInteger(Record.FieldList));
|
||||
error(mapNameAndUniqueName(IO, Record.Name, Record.UniqueName,
|
||||
Record.hasUniqueName()));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownRecord(CVType &CVR, BitFieldRecord &Record) {
|
||||
error(IO.mapInteger(Record.Type));
|
||||
error(IO.mapInteger(Record.BitSize));
|
||||
error(IO.mapInteger(Record.BitOffset));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownRecord(CVType &CVR,
|
||||
VFTableShapeRecord &Record) {
|
||||
uint16_t Size;
|
||||
if (IO.isWriting()) {
|
||||
ArrayRef<VFTableSlotKind> Slots = Record.getSlots();
|
||||
Size = Slots.size();
|
||||
error(IO.mapInteger(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]);
|
||||
}
|
||||
error(IO.mapInteger(Byte));
|
||||
}
|
||||
} else {
|
||||
error(IO.mapInteger(Size));
|
||||
for (uint16_t I = 0; I < Size; I += 2) {
|
||||
uint8_t Byte;
|
||||
error(IO.mapInteger(Byte));
|
||||
Record.Slots.push_back(static_cast<VFTableSlotKind>(Byte & 0xF));
|
||||
if ((I + 1) < Size)
|
||||
Record.Slots.push_back(static_cast<VFTableSlotKind>(Byte >> 4));
|
||||
}
|
||||
}
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownRecord(CVType &CVR, VFTableRecord &Record) {
|
||||
error(IO.mapInteger(Record.CompleteClass));
|
||||
error(IO.mapInteger(Record.OverriddenVFTable));
|
||||
error(IO.mapInteger(Record.VFPtrOffset));
|
||||
uint32_t NamesLen = 0;
|
||||
if (IO.isWriting()) {
|
||||
for (auto Name : Record.MethodNames)
|
||||
NamesLen += Name.size() + 1;
|
||||
}
|
||||
error(IO.mapInteger(NamesLen));
|
||||
error(IO.mapVectorTail(
|
||||
Record.MethodNames,
|
||||
[](CodeViewRecordIO &IO, StringRef &S) { return IO.mapStringZ(S); }));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownRecord(CVType &CVR, StringIdRecord &Record) {
|
||||
error(IO.mapInteger(Record.Id));
|
||||
error(IO.mapStringZ(Record.String));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownRecord(CVType &CVR,
|
||||
UdtSourceLineRecord &Record) {
|
||||
error(IO.mapInteger(Record.UDT));
|
||||
error(IO.mapInteger(Record.SourceFile));
|
||||
error(IO.mapInteger(Record.LineNumber));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownRecord(CVType &CVR,
|
||||
UdtModSourceLineRecord &Record) {
|
||||
error(IO.mapInteger(Record.UDT));
|
||||
error(IO.mapInteger(Record.SourceFile));
|
||||
error(IO.mapInteger(Record.LineNumber));
|
||||
error(IO.mapInteger(Record.Module));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownRecord(CVType &CVR, FuncIdRecord &Record) {
|
||||
error(IO.mapInteger(Record.ParentScope));
|
||||
error(IO.mapInteger(Record.FunctionType));
|
||||
error(IO.mapStringZ(Record.Name));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownRecord(CVType &CVR,
|
||||
MemberFuncIdRecord &Record) {
|
||||
error(IO.mapInteger(Record.ClassType));
|
||||
error(IO.mapInteger(Record.FunctionType));
|
||||
error(IO.mapStringZ(Record.Name));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownRecord(CVType &CVR,
|
||||
BuildInfoRecord &Record) {
|
||||
error(IO.mapVectorN<uint16_t>(
|
||||
Record.ArgIndices,
|
||||
[](CodeViewRecordIO &IO, TypeIndex &N) { return IO.mapInteger(N); }));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownRecord(CVType &CVR,
|
||||
MethodOverloadListRecord &Record) {
|
||||
// TODO: Split the list into multiple records if it's longer than 64KB, using
|
||||
// a subrecord of TypeRecordKind::Index to chain the records together.
|
||||
error(IO.mapVectorTail(Record.Methods, MapOneMethodRecord(true)));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownRecord(CVType &CVR,
|
||||
FieldListRecord &Record) {
|
||||
error(IO.mapByteVectorTail(Record.Data));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownRecord(CVType &CVR,
|
||||
TypeServer2Record &Record) {
|
||||
error(IO.mapGuid(Record.Guid));
|
||||
error(IO.mapInteger(Record.Age));
|
||||
error(IO.mapStringZ(Record.Name));
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownRecord(CVType &CVR, LabelRecord &Record) {
|
||||
error(IO.mapEnum(Record.Mode));
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR,
|
||||
BaseClassRecord &Record) {
|
||||
error(IO.mapInteger(Record.Attrs.Attrs));
|
||||
error(IO.mapInteger(Record.Type));
|
||||
error(IO.mapEncodedInteger(Record.Offset));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR,
|
||||
EnumeratorRecord &Record) {
|
||||
error(IO.mapInteger(Record.Attrs.Attrs));
|
||||
|
||||
// FIXME: Handle full APInt such as __int128.
|
||||
error(IO.mapEncodedInteger(Record.Value));
|
||||
error(IO.mapStringZ(Record.Name));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR,
|
||||
DataMemberRecord &Record) {
|
||||
error(IO.mapInteger(Record.Attrs.Attrs));
|
||||
error(IO.mapInteger(Record.Type));
|
||||
error(IO.mapEncodedInteger(Record.FieldOffset));
|
||||
error(IO.mapStringZ(Record.Name));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR,
|
||||
OverloadedMethodRecord &Record) {
|
||||
error(IO.mapInteger(Record.NumOverloads));
|
||||
error(IO.mapInteger(Record.MethodList));
|
||||
error(IO.mapStringZ(Record.Name));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR,
|
||||
OneMethodRecord &Record) {
|
||||
const bool IsFromOverloadList = (TypeKind == LF_METHODLIST);
|
||||
MapOneMethodRecord Mapper(IsFromOverloadList);
|
||||
return Mapper(IO, Record);
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR,
|
||||
NestedTypeRecord &Record) {
|
||||
uint16_t Padding = 0;
|
||||
error(IO.mapInteger(Padding));
|
||||
error(IO.mapInteger(Record.Type));
|
||||
error(IO.mapStringZ(Record.Name));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR,
|
||||
StaticDataMemberRecord &Record) {
|
||||
|
||||
error(IO.mapInteger(Record.Attrs.Attrs));
|
||||
error(IO.mapInteger(Record.Type));
|
||||
error(IO.mapStringZ(Record.Name));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR,
|
||||
VirtualBaseClassRecord &Record) {
|
||||
|
||||
error(IO.mapInteger(Record.Attrs.Attrs));
|
||||
error(IO.mapInteger(Record.BaseType));
|
||||
error(IO.mapInteger(Record.VBPtrType));
|
||||
error(IO.mapEncodedInteger(Record.VBPtrOffset));
|
||||
error(IO.mapEncodedInteger(Record.VTableIndex));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR,
|
||||
VFPtrRecord &Record) {
|
||||
uint16_t Padding = 0;
|
||||
error(IO.mapInteger(Padding));
|
||||
error(IO.mapInteger(Record.Type));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR,
|
||||
ListContinuationRecord &Record) {
|
||||
uint16_t Padding = 0;
|
||||
error(IO.mapInteger(Padding));
|
||||
error(IO.mapInteger(Record.ContinuationIndex));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
//===- TypeRecordMapping.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/TypeRecordMapping.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::codeview;
|
||||
|
||||
#define error(X) \
|
||||
if (auto EC = X) \
|
||||
return EC;
|
||||
|
||||
namespace {
|
||||
struct MapOneMethodRecord {
|
||||
explicit MapOneMethodRecord(bool IsFromOverloadList)
|
||||
: IsFromOverloadList(IsFromOverloadList) {}
|
||||
|
||||
Error operator()(CodeViewRecordIO &IO, OneMethodRecord &Method) const {
|
||||
error(IO.mapInteger(Method.Attrs.Attrs));
|
||||
if (IsFromOverloadList) {
|
||||
uint16_t Padding = 0;
|
||||
error(IO.mapInteger(Padding));
|
||||
}
|
||||
error(IO.mapInteger(Method.Type));
|
||||
if (Method.isIntroducingVirtual()) {
|
||||
error(IO.mapInteger(Method.VFTableOffset));
|
||||
} else if (!IO.isWriting())
|
||||
Method.VFTableOffset = -1;
|
||||
|
||||
if (!IsFromOverloadList)
|
||||
error(IO.mapStringZ(Method.Name));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
private:
|
||||
bool IsFromOverloadList;
|
||||
};
|
||||
}
|
||||
|
||||
static Error mapNameAndUniqueName(CodeViewRecordIO &IO, StringRef &Name,
|
||||
StringRef &UniqueName, bool HasUniqueName) {
|
||||
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.
|
||||
size_t BytesLeft = IO.maxFieldLength();
|
||||
if (HasUniqueName) {
|
||||
size_t BytesNeeded = Name.size() + UniqueName.size() + 2;
|
||||
StringRef N = Name;
|
||||
StringRef U = UniqueName;
|
||||
if (BytesNeeded > BytesLeft) {
|
||||
size_t BytesToDrop = (BytesNeeded - BytesLeft);
|
||||
size_t DropN = std::min(N.size(), BytesToDrop / 2);
|
||||
size_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 {
|
||||
// Cap the length of the string at however many bytes we have available,
|
||||
// plus one for the required null terminator.
|
||||
auto N = StringRef(Name).take_front(BytesLeft - 1);
|
||||
error(IO.mapStringZ(N));
|
||||
}
|
||||
} else {
|
||||
error(IO.mapStringZ(Name));
|
||||
if (HasUniqueName)
|
||||
error(IO.mapStringZ(UniqueName));
|
||||
}
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitTypeBegin(CVType &CVR) {
|
||||
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();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownRecord(CVType &CVR, ModifierRecord &Record) {
|
||||
error(IO.mapInteger(Record.ModifiedType));
|
||||
error(IO.mapEnum(Record.Modifiers));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownRecord(CVType &CVR,
|
||||
ProcedureRecord &Record) {
|
||||
error(IO.mapInteger(Record.ReturnType));
|
||||
error(IO.mapEnum(Record.CallConv));
|
||||
error(IO.mapEnum(Record.Options));
|
||||
error(IO.mapInteger(Record.ParameterCount));
|
||||
error(IO.mapInteger(Record.ArgumentList));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownRecord(CVType &CVR,
|
||||
MemberFunctionRecord &Record) {
|
||||
error(IO.mapInteger(Record.ReturnType));
|
||||
error(IO.mapInteger(Record.ClassType));
|
||||
error(IO.mapInteger(Record.ThisType));
|
||||
error(IO.mapEnum(Record.CallConv));
|
||||
error(IO.mapEnum(Record.Options));
|
||||
error(IO.mapInteger(Record.ParameterCount));
|
||||
error(IO.mapInteger(Record.ArgumentList));
|
||||
error(IO.mapInteger(Record.ThisPointerAdjustment));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownRecord(CVType &CVR, ArgListRecord &Record) {
|
||||
error(IO.mapVectorN<uint32_t>(
|
||||
Record.ArgIndices,
|
||||
[](CodeViewRecordIO &IO, TypeIndex &N) { return IO.mapInteger(N); }));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownRecord(CVType &CVR,
|
||||
StringListRecord &Record) {
|
||||
error(IO.mapVectorN<uint32_t>(
|
||||
Record.StringIndices,
|
||||
[](CodeViewRecordIO &IO, TypeIndex &N) { return IO.mapInteger(N); }));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownRecord(CVType &CVR, PointerRecord &Record) {
|
||||
error(IO.mapInteger(Record.ReferentType));
|
||||
error(IO.mapInteger(Record.Attrs));
|
||||
|
||||
if (Record.isPointerToMember()) {
|
||||
if (!IO.isWriting())
|
||||
Record.MemberInfo.emplace();
|
||||
|
||||
MemberPointerInfo &M = *Record.MemberInfo;
|
||||
error(IO.mapInteger(M.ContainingType));
|
||||
error(IO.mapEnum(M.Representation));
|
||||
}
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownRecord(CVType &CVR, ArrayRecord &Record) {
|
||||
error(IO.mapInteger(Record.ElementType));
|
||||
error(IO.mapInteger(Record.IndexType));
|
||||
error(IO.mapEncodedInteger(Record.Size));
|
||||
error(IO.mapStringZ(Record.Name));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownRecord(CVType &CVR, ClassRecord &Record) {
|
||||
assert((CVR.Type == TypeLeafKind::LF_STRUCTURE) ||
|
||||
(CVR.Type == TypeLeafKind::LF_CLASS) ||
|
||||
(CVR.Type == TypeLeafKind::LF_INTERFACE));
|
||||
|
||||
error(IO.mapInteger(Record.MemberCount));
|
||||
error(IO.mapEnum(Record.Options));
|
||||
error(IO.mapInteger(Record.FieldList));
|
||||
error(IO.mapInteger(Record.DerivationList));
|
||||
error(IO.mapInteger(Record.VTableShape));
|
||||
error(IO.mapEncodedInteger(Record.Size));
|
||||
error(mapNameAndUniqueName(IO, Record.Name, Record.UniqueName,
|
||||
Record.hasUniqueName()));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownRecord(CVType &CVR, UnionRecord &Record) {
|
||||
error(IO.mapInteger(Record.MemberCount));
|
||||
error(IO.mapEnum(Record.Options));
|
||||
error(IO.mapInteger(Record.FieldList));
|
||||
error(IO.mapEncodedInteger(Record.Size));
|
||||
error(mapNameAndUniqueName(IO, Record.Name, Record.UniqueName,
|
||||
Record.hasUniqueName()));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownRecord(CVType &CVR, EnumRecord &Record) {
|
||||
error(IO.mapInteger(Record.MemberCount));
|
||||
error(IO.mapEnum(Record.Options));
|
||||
error(IO.mapInteger(Record.UnderlyingType));
|
||||
error(IO.mapInteger(Record.FieldList));
|
||||
error(mapNameAndUniqueName(IO, Record.Name, Record.UniqueName,
|
||||
Record.hasUniqueName()));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownRecord(CVType &CVR, BitFieldRecord &Record) {
|
||||
error(IO.mapInteger(Record.Type));
|
||||
error(IO.mapInteger(Record.BitSize));
|
||||
error(IO.mapInteger(Record.BitOffset));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownRecord(CVType &CVR,
|
||||
VFTableShapeRecord &Record) {
|
||||
uint16_t Size;
|
||||
if (IO.isWriting()) {
|
||||
ArrayRef<VFTableSlotKind> Slots = Record.getSlots();
|
||||
Size = Slots.size();
|
||||
error(IO.mapInteger(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]);
|
||||
}
|
||||
error(IO.mapInteger(Byte));
|
||||
}
|
||||
} else {
|
||||
error(IO.mapInteger(Size));
|
||||
for (uint16_t I = 0; I < Size; I += 2) {
|
||||
uint8_t Byte;
|
||||
error(IO.mapInteger(Byte));
|
||||
Record.Slots.push_back(static_cast<VFTableSlotKind>(Byte & 0xF));
|
||||
if ((I + 1) < Size)
|
||||
Record.Slots.push_back(static_cast<VFTableSlotKind>(Byte >> 4));
|
||||
}
|
||||
}
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownRecord(CVType &CVR, VFTableRecord &Record) {
|
||||
error(IO.mapInteger(Record.CompleteClass));
|
||||
error(IO.mapInteger(Record.OverriddenVFTable));
|
||||
error(IO.mapInteger(Record.VFPtrOffset));
|
||||
uint32_t NamesLen = 0;
|
||||
if (IO.isWriting()) {
|
||||
for (auto Name : Record.MethodNames)
|
||||
NamesLen += Name.size() + 1;
|
||||
}
|
||||
error(IO.mapInteger(NamesLen));
|
||||
error(IO.mapVectorTail(
|
||||
Record.MethodNames,
|
||||
[](CodeViewRecordIO &IO, StringRef &S) { return IO.mapStringZ(S); }));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownRecord(CVType &CVR, StringIdRecord &Record) {
|
||||
error(IO.mapInteger(Record.Id));
|
||||
error(IO.mapStringZ(Record.String));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownRecord(CVType &CVR,
|
||||
UdtSourceLineRecord &Record) {
|
||||
error(IO.mapInteger(Record.UDT));
|
||||
error(IO.mapInteger(Record.SourceFile));
|
||||
error(IO.mapInteger(Record.LineNumber));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownRecord(CVType &CVR,
|
||||
UdtModSourceLineRecord &Record) {
|
||||
error(IO.mapInteger(Record.UDT));
|
||||
error(IO.mapInteger(Record.SourceFile));
|
||||
error(IO.mapInteger(Record.LineNumber));
|
||||
error(IO.mapInteger(Record.Module));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownRecord(CVType &CVR, FuncIdRecord &Record) {
|
||||
error(IO.mapInteger(Record.ParentScope));
|
||||
error(IO.mapInteger(Record.FunctionType));
|
||||
error(IO.mapStringZ(Record.Name));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownRecord(CVType &CVR,
|
||||
MemberFuncIdRecord &Record) {
|
||||
error(IO.mapInteger(Record.ClassType));
|
||||
error(IO.mapInteger(Record.FunctionType));
|
||||
error(IO.mapStringZ(Record.Name));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownRecord(CVType &CVR,
|
||||
BuildInfoRecord &Record) {
|
||||
error(IO.mapVectorN<uint16_t>(
|
||||
Record.ArgIndices,
|
||||
[](CodeViewRecordIO &IO, TypeIndex &N) { return IO.mapInteger(N); }));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownRecord(CVType &CVR,
|
||||
MethodOverloadListRecord &Record) {
|
||||
// TODO: Split the list into multiple records if it's longer than 64KB, using
|
||||
// a subrecord of TypeRecordKind::Index to chain the records together.
|
||||
error(IO.mapVectorTail(Record.Methods, MapOneMethodRecord(true)));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownRecord(CVType &CVR,
|
||||
FieldListRecord &Record) {
|
||||
error(IO.mapByteVectorTail(Record.Data));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownRecord(CVType &CVR,
|
||||
TypeServer2Record &Record) {
|
||||
error(IO.mapGuid(Record.Guid));
|
||||
error(IO.mapInteger(Record.Age));
|
||||
error(IO.mapStringZ(Record.Name));
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownRecord(CVType &CVR, LabelRecord &Record) {
|
||||
error(IO.mapEnum(Record.Mode));
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR,
|
||||
BaseClassRecord &Record) {
|
||||
error(IO.mapInteger(Record.Attrs.Attrs));
|
||||
error(IO.mapInteger(Record.Type));
|
||||
error(IO.mapEncodedInteger(Record.Offset));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR,
|
||||
EnumeratorRecord &Record) {
|
||||
error(IO.mapInteger(Record.Attrs.Attrs));
|
||||
|
||||
// FIXME: Handle full APInt such as __int128.
|
||||
error(IO.mapEncodedInteger(Record.Value));
|
||||
error(IO.mapStringZ(Record.Name));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR,
|
||||
DataMemberRecord &Record) {
|
||||
error(IO.mapInteger(Record.Attrs.Attrs));
|
||||
error(IO.mapInteger(Record.Type));
|
||||
error(IO.mapEncodedInteger(Record.FieldOffset));
|
||||
error(IO.mapStringZ(Record.Name));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR,
|
||||
OverloadedMethodRecord &Record) {
|
||||
error(IO.mapInteger(Record.NumOverloads));
|
||||
error(IO.mapInteger(Record.MethodList));
|
||||
error(IO.mapStringZ(Record.Name));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR,
|
||||
OneMethodRecord &Record) {
|
||||
const bool IsFromOverloadList = (TypeKind == LF_METHODLIST);
|
||||
MapOneMethodRecord Mapper(IsFromOverloadList);
|
||||
return Mapper(IO, Record);
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR,
|
||||
NestedTypeRecord &Record) {
|
||||
uint16_t Padding = 0;
|
||||
error(IO.mapInteger(Padding));
|
||||
error(IO.mapInteger(Record.Type));
|
||||
error(IO.mapStringZ(Record.Name));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR,
|
||||
StaticDataMemberRecord &Record) {
|
||||
|
||||
error(IO.mapInteger(Record.Attrs.Attrs));
|
||||
error(IO.mapInteger(Record.Type));
|
||||
error(IO.mapStringZ(Record.Name));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR,
|
||||
VirtualBaseClassRecord &Record) {
|
||||
|
||||
error(IO.mapInteger(Record.Attrs.Attrs));
|
||||
error(IO.mapInteger(Record.BaseType));
|
||||
error(IO.mapInteger(Record.VBPtrType));
|
||||
error(IO.mapEncodedInteger(Record.VBPtrOffset));
|
||||
error(IO.mapEncodedInteger(Record.VTableIndex));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR,
|
||||
VFPtrRecord &Record) {
|
||||
uint16_t Padding = 0;
|
||||
error(IO.mapInteger(Padding));
|
||||
error(IO.mapInteger(Record.Type));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR,
|
||||
ListContinuationRecord &Record) {
|
||||
uint16_t Padding = 0;
|
||||
error(IO.mapInteger(Padding));
|
||||
error(IO.mapInteger(Record.ContinuationIndex));
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownRecord(CVType &CVR,
|
||||
PrecompRecord &Precomp) {
|
||||
error(IO.mapInteger(Precomp.StartTypeIndex));
|
||||
error(IO.mapInteger(Precomp.TypesCount));
|
||||
error(IO.mapInteger(Precomp.Signature));
|
||||
error(IO.mapStringZ(Precomp.PrecompFilePath));
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TypeRecordMapping::visitKnownRecord(CVType &CVR,
|
||||
EndPrecompRecord &EndPrecomp) {
|
||||
error(IO.mapInteger(EndPrecomp.Signature));
|
||||
return Error::success();
|
||||
}
|
||||
|
@ -562,14 +562,16 @@ void MappingTraits<COFFYAML::Section>::mapping(IO &IO, COFFYAML::Section &Sec) {
|
||||
IO.mapOptional("VirtualSize", Sec.Header.VirtualSize, 0U);
|
||||
IO.mapOptional("Alignment", Sec.Alignment, 0U);
|
||||
|
||||
// If this is a .debug$S .debug$T, or .debug$H section parse the semantic
|
||||
// representation of the symbols/types. If it is any other kind of section,
|
||||
// just deal in raw bytes.
|
||||
// If this is a .debug$S .debug$T .debug$P, or .debug$H section parse the
|
||||
// semantic representation of the symbols/types. If it is any other kind
|
||||
// of section, just deal in raw bytes.
|
||||
IO.mapOptional("SectionData", Sec.SectionData);
|
||||
if (Sec.Name == ".debug$S")
|
||||
IO.mapOptional("Subsections", Sec.DebugS);
|
||||
else if (Sec.Name == ".debug$T")
|
||||
IO.mapOptional("Types", Sec.DebugT);
|
||||
else if (Sec.Name == ".debug$P")
|
||||
IO.mapOptional("PrecompTypes", Sec.DebugP);
|
||||
else if (Sec.Name == ".debug$H")
|
||||
IO.mapOptional("GlobalHashes", Sec.DebugH);
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
BIN
test/DebugInfo/Inputs/precomp-a.obj
Normal file
BIN
test/DebugInfo/Inputs/precomp-a.obj
Normal file
Binary file not shown.
BIN
test/DebugInfo/Inputs/precomp.obj
Normal file
BIN
test/DebugInfo/Inputs/precomp.obj
Normal file
Binary file not shown.
57
test/DebugInfo/precomp.test
Normal file
57
test/DebugInfo/precomp.test
Normal file
@ -0,0 +1,57 @@
|
||||
|
||||
RUN: rm -rf %t1/
|
||||
RUN: mkdir %t1
|
||||
RUN: obj2yaml %S/Inputs/precomp-a.obj > %t1/precomp-a.yaml
|
||||
RUN: obj2yaml %S/Inputs/precomp.obj > %t1/precomp.yaml
|
||||
RUN: yaml2obj %t1/precomp-a.yaml > %t1/a.obj
|
||||
RUN: yaml2obj %t1/precomp.yaml > %t1/precomp.obj
|
||||
RUN: llvm-readobj -codeview %t1/a.obj | FileCheck %s -check-prefix PRECOMP
|
||||
RUN: llvm-readobj -codeview %t1/precomp.obj | FileCheck %s -check-prefix ENDPRECOMP
|
||||
RUN: llvm-pdbutil dump -types %t1/a.obj | FileCheck %s -check-prefix PDB-PRECOMP
|
||||
RUN: llvm-pdbutil dump -types %t1/precomp.obj | FileCheck %s -check-prefix PDB-ENDPRECOMP
|
||||
|
||||
ENDPRECOMP: CodeViewTypes [
|
||||
ENDPRECOMP-NEXT: Section: .debug$P (3)
|
||||
ENDPRECOMP: EndPrecomp (0x1407) {
|
||||
ENDPRECOMP-NEXT: TypeLeafKind: LF_ENDPRECOMP (0x14)
|
||||
ENDPRECOMP-NEXT: Signature: 0x1116980E
|
||||
ENDPRECOMP-NEXT: }
|
||||
|
||||
PRECOMP: CodeViewTypes [
|
||||
PRECOMP-NEXT: Section: .debug$T (3)
|
||||
PRECOMP-NEXT: Magic: 0x4
|
||||
PRECOMP-NEXT: Precomp (0x1000) {
|
||||
PRECOMP-NEXT: TypeLeafKind: LF_PRECOMP (0x1509)
|
||||
PRECOMP-NEXT: StartIndex: 0x1000
|
||||
PRECOMP-NEXT: Count: 0x407
|
||||
PRECOMP-NEXT: Signature: 0x1116980E
|
||||
|
||||
PDB-PRECOMP: Types (.debug$T)
|
||||
PDB-PRECOMP-NEXT: ============================================================
|
||||
PDB-PRECOMP-NEXT: Showing 0 records
|
||||
PDB-PRECOMP-NEXT: 0x1000 | LF_PRECOMP [size = 60] start index = 0x1000, types count = 0x407, signature = 0x1116980E, precomp path = f:\svn\lld\test\coff\precomp\precomp.obj
|
||||
|
||||
PDB-ENDPRECOMP: Precompiled Types (.debug$P)
|
||||
PDB-ENDPRECOMP-NEXT: ============================================================
|
||||
PDB-ENDPRECOMP-NEXT: Showing 0 records
|
||||
PDB-ENDPRECOMP: 0x1407 | LF_ENDPRECOMP [size = 8] signature = 0x1116980E
|
||||
|
||||
# // precomp.h
|
||||
# #pragma once
|
||||
# int Function(char A);
|
||||
#
|
||||
# // precomp.cpp
|
||||
# // cl.exe precomp.cpp /Z7 /Ycprecomp.h /c
|
||||
# #include "precomp.h"
|
||||
#
|
||||
# // a.cpp
|
||||
# #include "precomp.h"
|
||||
# int main(void) {
|
||||
# Function('a');
|
||||
# return 0;
|
||||
# }
|
||||
#
|
||||
# // cl.exe a.cpp /Z7 /Yuprecomp.h /c
|
||||
#
|
||||
# // obj2yaml precomp.obj >precomp-precomp.yaml
|
||||
# // obj2yaml a.obj >precomp-a.yaml
|
@ -95,7 +95,8 @@ static inline bool isDebugSSection(object::SectionRef Section,
|
||||
|
||||
static bool isDebugTSection(SectionRef Section, CVTypeArray &Types) {
|
||||
BinaryStreamReader Reader;
|
||||
if (!isCodeViewDebugSubsection(Section, ".debug$T", Reader))
|
||||
if (!isCodeViewDebugSubsection(Section, ".debug$T", Reader) &&
|
||||
!isCodeViewDebugSubsection(Section, ".debug$P", Reader))
|
||||
return false;
|
||||
cantFail(Reader.readArray(Types, Reader.bytesRemaining()));
|
||||
return true;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -170,7 +170,11 @@ void COFFDumper::dumpSections(unsigned NumSections) {
|
||||
if (NewYAMLSection.Name == ".debug$S")
|
||||
NewYAMLSection.DebugS = CodeViewYAML::fromDebugS(sectionData, SC);
|
||||
else if (NewYAMLSection.Name == ".debug$T")
|
||||
NewYAMLSection.DebugT = CodeViewYAML::fromDebugT(sectionData);
|
||||
NewYAMLSection.DebugT = CodeViewYAML::fromDebugT(sectionData,
|
||||
NewYAMLSection.Name);
|
||||
else if (NewYAMLSection.Name == ".debug$P")
|
||||
NewYAMLSection.DebugP = CodeViewYAML::fromDebugT(sectionData,
|
||||
NewYAMLSection.Name);
|
||||
else if (NewYAMLSection.Name == ".debug$H")
|
||||
NewYAMLSection.DebugH = CodeViewYAML::fromDebugH(sectionData);
|
||||
|
||||
|
@ -233,7 +233,10 @@ static bool layoutCOFF(COFFParser &CP) {
|
||||
}
|
||||
} else if (S.Name == ".debug$T") {
|
||||
if (S.SectionData.binary_size() == 0)
|
||||
S.SectionData = CodeViewYAML::toDebugT(S.DebugT, CP.Allocator);
|
||||
S.SectionData = CodeViewYAML::toDebugT(S.DebugT, CP.Allocator, S.Name);
|
||||
} else if (S.Name == ".debug$P") {
|
||||
if (S.SectionData.binary_size() == 0)
|
||||
S.SectionData = CodeViewYAML::toDebugT(S.DebugP, CP.Allocator, S.Name);
|
||||
} else if (S.Name == ".debug$H") {
|
||||
if (S.DebugH.hasValue() && S.SectionData.binary_size() == 0)
|
||||
S.SectionData = CodeViewYAML::toDebugH(*S.DebugH, CP.Allocator);
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user