[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:
Alexandre Ganea 2018-04-09 20:17:56 +00:00
parent 69a2e18b4a
commit d9e96741c4
21 changed files with 4384 additions and 4203 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

Binary file not shown.

Binary file not shown.

View 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

View File

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

View File

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

View File

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