340 lines
10 KiB
C++
340 lines
10 KiB
C++
//===- RecordName.cpp ----------------------------------------- *- C++ --*-===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/DebugInfo/CodeView/RecordName.h"
|
|
|
|
#include "llvm/ADT/SmallString.h"
|
|
#include "llvm/ADT/StringExtras.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) {
|
|
if (Indices[I] < CurrentTypeIndex)
|
|
Name.append(Types.getTypeName(Indices[I]));
|
|
else
|
|
Name.append("<unknown 0x" + utohexstr(Indices[I].getIndex()) + ">");
|
|
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 std::string(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:
|
|
case SymbolKind::S_PROCREF:
|
|
case SymbolKind::S_LPROCREF:
|
|
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;
|
|
// See UsingNamespaceSym
|
|
case SymbolKind::S_UNAMESPACE:
|
|
return 0;
|
|
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;
|
|
}
|