//===- 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/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 = ""; return Error::success(); } Error TypeNameComputer::visitKnownRecord(CVRecord &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(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("", 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 ""; } 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: 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; }