338 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			338 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/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:
 | |
|   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;
 | |
| }
 |