1 //===- RecordName.cpp ----------------------------------------- *- C++ --*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "llvm/DebugInfo/CodeView/RecordName.h" 10 11 #include "llvm/ADT/SmallString.h" 12 #include "llvm/ADT/StringExtras.h" 13 #include "llvm/DebugInfo/CodeView/CVSymbolVisitor.h" 14 #include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" 15 #include "llvm/DebugInfo/CodeView/SymbolRecordMapping.h" 16 #include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h" 17 #include "llvm/Support/FormatVariadic.h" 18 19 using namespace llvm; 20 using namespace llvm::codeview; 21 22 namespace { 23 class TypeNameComputer : public TypeVisitorCallbacks { 24 /// The type collection. Used to calculate names of nested types. 25 TypeCollection &Types; 26 TypeIndex CurrentTypeIndex = TypeIndex::None(); 27 28 /// Name of the current type. Only valid before visitTypeEnd. 29 SmallString<256> Name; 30 31 public: 32 explicit TypeNameComputer(TypeCollection &Types) : Types(Types) {} 33 34 StringRef name() const { return Name; } 35 36 /// Paired begin/end actions for all types. Receives all record data, 37 /// including the fixed-length record prefix. 38 Error visitTypeBegin(CVType &Record) override; 39 Error visitTypeBegin(CVType &Record, TypeIndex Index) override; 40 Error visitTypeEnd(CVType &Record) override; 41 42 #define TYPE_RECORD(EnumName, EnumVal, Name) \ 43 Error visitKnownRecord(CVType &CVR, Name##Record &Record) override; 44 #define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) 45 #define MEMBER_RECORD(EnumName, EnumVal, Name) 46 #include "llvm/DebugInfo/CodeView/CodeViewTypes.def" 47 }; 48 } // namespace 49 50 Error TypeNameComputer::visitTypeBegin(CVType &Record) { 51 llvm_unreachable("Must call visitTypeBegin with a TypeIndex!"); 52 return Error::success(); 53 } 54 55 Error TypeNameComputer::visitTypeBegin(CVType &Record, TypeIndex Index) { 56 // Reset Name to the empty string. If the visitor sets it, we know it. 57 Name = ""; 58 CurrentTypeIndex = Index; 59 return Error::success(); 60 } 61 62 Error TypeNameComputer::visitTypeEnd(CVType &CVR) { return Error::success(); } 63 64 Error TypeNameComputer::visitKnownRecord(CVType &CVR, 65 FieldListRecord &FieldList) { 66 Name = "<field list>"; 67 return Error::success(); 68 } 69 70 Error TypeNameComputer::visitKnownRecord(CVRecord<TypeLeafKind> &CVR, 71 StringIdRecord &String) { 72 Name = String.getString(); 73 return Error::success(); 74 } 75 76 Error TypeNameComputer::visitKnownRecord(CVType &CVR, ArgListRecord &Args) { 77 auto Indices = Args.getIndices(); 78 uint32_t Size = Indices.size(); 79 Name = "("; 80 for (uint32_t I = 0; I < Size; ++I) { 81 if (Indices[I] < CurrentTypeIndex) 82 Name.append(Types.getTypeName(Indices[I])); 83 else 84 Name.append("<unknown 0x" + utohexstr(Indices[I].getIndex()) + ">"); 85 if (I + 1 != Size) 86 Name.append(", "); 87 } 88 Name.push_back(')'); 89 return Error::success(); 90 } 91 92 Error TypeNameComputer::visitKnownRecord(CVType &CVR, 93 StringListRecord &Strings) { 94 auto Indices = Strings.getIndices(); 95 uint32_t Size = Indices.size(); 96 Name = "\""; 97 for (uint32_t I = 0; I < Size; ++I) { 98 Name.append(Types.getTypeName(Indices[I])); 99 if (I + 1 != Size) 100 Name.append("\" \""); 101 } 102 Name.push_back('\"'); 103 return Error::success(); 104 } 105 106 Error TypeNameComputer::visitKnownRecord(CVType &CVR, ClassRecord &Class) { 107 Name = Class.getName(); 108 return Error::success(); 109 } 110 111 Error TypeNameComputer::visitKnownRecord(CVType &CVR, UnionRecord &Union) { 112 Name = Union.getName(); 113 return Error::success(); 114 } 115 116 Error TypeNameComputer::visitKnownRecord(CVType &CVR, EnumRecord &Enum) { 117 Name = Enum.getName(); 118 return Error::success(); 119 } 120 121 Error TypeNameComputer::visitKnownRecord(CVType &CVR, ArrayRecord &AT) { 122 Name = AT.getName(); 123 return Error::success(); 124 } 125 126 Error TypeNameComputer::visitKnownRecord(CVType &CVR, VFTableRecord &VFT) { 127 Name = VFT.getName(); 128 return Error::success(); 129 } 130 131 Error TypeNameComputer::visitKnownRecord(CVType &CVR, MemberFuncIdRecord &Id) { 132 Name = Id.getName(); 133 return Error::success(); 134 } 135 136 Error TypeNameComputer::visitKnownRecord(CVType &CVR, ProcedureRecord &Proc) { 137 StringRef Ret = Types.getTypeName(Proc.getReturnType()); 138 StringRef Params = Types.getTypeName(Proc.getArgumentList()); 139 Name = formatv("{0} {1}", Ret, Params).sstr<256>(); 140 return Error::success(); 141 } 142 143 Error TypeNameComputer::visitKnownRecord(CVType &CVR, 144 MemberFunctionRecord &MF) { 145 StringRef Ret = Types.getTypeName(MF.getReturnType()); 146 StringRef Class = Types.getTypeName(MF.getClassType()); 147 StringRef Params = Types.getTypeName(MF.getArgumentList()); 148 Name = formatv("{0} {1}::{2}", Ret, Class, Params).sstr<256>(); 149 return Error::success(); 150 } 151 152 Error TypeNameComputer::visitKnownRecord(CVType &CVR, FuncIdRecord &Func) { 153 Name = Func.getName(); 154 return Error::success(); 155 } 156 157 Error TypeNameComputer::visitKnownRecord(CVType &CVR, TypeServer2Record &TS) { 158 Name = TS.getName(); 159 return Error::success(); 160 } 161 162 Error TypeNameComputer::visitKnownRecord(CVType &CVR, PointerRecord &Ptr) { 163 164 if (Ptr.isPointerToMember()) { 165 const MemberPointerInfo &MI = Ptr.getMemberInfo(); 166 167 StringRef Pointee = Types.getTypeName(Ptr.getReferentType()); 168 StringRef Class = Types.getTypeName(MI.getContainingType()); 169 Name = formatv("{0} {1}::*", Pointee, Class); 170 } else { 171 Name.append(Types.getTypeName(Ptr.getReferentType())); 172 173 if (Ptr.getMode() == PointerMode::LValueReference) 174 Name.append("&"); 175 else if (Ptr.getMode() == PointerMode::RValueReference) 176 Name.append("&&"); 177 else if (Ptr.getMode() == PointerMode::Pointer) 178 Name.append("*"); 179 180 // Qualifiers in pointer records apply to the pointer, not the pointee, so 181 // they go on the right. 182 if (Ptr.isConst()) 183 Name.append(" const"); 184 if (Ptr.isVolatile()) 185 Name.append(" volatile"); 186 if (Ptr.isUnaligned()) 187 Name.append(" __unaligned"); 188 if (Ptr.isRestrict()) 189 Name.append(" __restrict"); 190 } 191 return Error::success(); 192 } 193 194 Error TypeNameComputer::visitKnownRecord(CVType &CVR, ModifierRecord &Mod) { 195 uint16_t Mods = static_cast<uint16_t>(Mod.getModifiers()); 196 197 if (Mods & uint16_t(ModifierOptions::Const)) 198 Name.append("const "); 199 if (Mods & uint16_t(ModifierOptions::Volatile)) 200 Name.append("volatile "); 201 if (Mods & uint16_t(ModifierOptions::Unaligned)) 202 Name.append("__unaligned "); 203 Name.append(Types.getTypeName(Mod.getModifiedType())); 204 return Error::success(); 205 } 206 207 Error TypeNameComputer::visitKnownRecord(CVType &CVR, 208 VFTableShapeRecord &Shape) { 209 Name = formatv("<vftable {0} methods>", Shape.getEntryCount()); 210 return Error::success(); 211 } 212 213 Error TypeNameComputer::visitKnownRecord( 214 CVType &CVR, UdtModSourceLineRecord &ModSourceLine) { 215 return Error::success(); 216 } 217 218 Error TypeNameComputer::visitKnownRecord(CVType &CVR, 219 UdtSourceLineRecord &SourceLine) { 220 return Error::success(); 221 } 222 223 Error TypeNameComputer::visitKnownRecord(CVType &CVR, BitFieldRecord &BF) { 224 return Error::success(); 225 } 226 227 Error TypeNameComputer::visitKnownRecord(CVType &CVR, 228 MethodOverloadListRecord &Overloads) { 229 return Error::success(); 230 } 231 232 Error TypeNameComputer::visitKnownRecord(CVType &CVR, BuildInfoRecord &BI) { 233 return Error::success(); 234 } 235 236 Error TypeNameComputer::visitKnownRecord(CVType &CVR, LabelRecord &R) { 237 return Error::success(); 238 } 239 240 Error TypeNameComputer::visitKnownRecord(CVType &CVR, 241 PrecompRecord &Precomp) { 242 return Error::success(); 243 } 244 245 Error TypeNameComputer::visitKnownRecord(CVType &CVR, 246 EndPrecompRecord &EndPrecomp) { 247 return Error::success(); 248 } 249 250 std::string llvm::codeview::computeTypeName(TypeCollection &Types, 251 TypeIndex Index) { 252 TypeNameComputer Computer(Types); 253 CVType Record = Types.getType(Index); 254 if (auto EC = visitTypeRecord(Record, Index, Computer)) { 255 consumeError(std::move(EC)); 256 return "<unknown UDT>"; 257 } 258 return std::string(Computer.name()); 259 } 260 261 static int getSymbolNameOffset(CVSymbol Sym) { 262 switch (Sym.kind()) { 263 // See ProcSym 264 case SymbolKind::S_GPROC32: 265 case SymbolKind::S_LPROC32: 266 case SymbolKind::S_GPROC32_ID: 267 case SymbolKind::S_LPROC32_ID: 268 case SymbolKind::S_LPROC32_DPC: 269 case SymbolKind::S_LPROC32_DPC_ID: 270 return 35; 271 // See Thunk32Sym 272 case SymbolKind::S_THUNK32: 273 return 21; 274 // See SectionSym 275 case SymbolKind::S_SECTION: 276 return 16; 277 // See CoffGroupSym 278 case SymbolKind::S_COFFGROUP: 279 return 14; 280 // See PublicSym32, FileStaticSym, RegRelativeSym, DataSym, ThreadLocalDataSym 281 case SymbolKind::S_PUB32: 282 case SymbolKind::S_FILESTATIC: 283 case SymbolKind::S_REGREL32: 284 case SymbolKind::S_GDATA32: 285 case SymbolKind::S_LDATA32: 286 case SymbolKind::S_LMANDATA: 287 case SymbolKind::S_GMANDATA: 288 case SymbolKind::S_LTHREAD32: 289 case SymbolKind::S_GTHREAD32: 290 case SymbolKind::S_PROCREF: 291 case SymbolKind::S_LPROCREF: 292 return 10; 293 // See RegisterSym and LocalSym 294 case SymbolKind::S_REGISTER: 295 case SymbolKind::S_LOCAL: 296 return 6; 297 // See BlockSym 298 case SymbolKind::S_BLOCK32: 299 return 18; 300 // See LabelSym 301 case SymbolKind::S_LABEL32: 302 return 7; 303 // See ObjNameSym, ExportSym, and UDTSym 304 case SymbolKind::S_OBJNAME: 305 case SymbolKind::S_EXPORT: 306 case SymbolKind::S_UDT: 307 return 4; 308 // See BPRelativeSym 309 case SymbolKind::S_BPREL32: 310 return 8; 311 // See UsingNamespaceSym 312 case SymbolKind::S_UNAMESPACE: 313 return 0; 314 default: 315 return -1; 316 } 317 } 318 319 StringRef llvm::codeview::getSymbolName(CVSymbol Sym) { 320 if (Sym.kind() == SymbolKind::S_CONSTANT) { 321 // S_CONSTANT is preceded by an APSInt, which has a variable length. So we 322 // have to do a full deserialization. 323 BinaryStreamReader Reader(Sym.content(), llvm::support::little); 324 // The container doesn't matter for single records. 325 SymbolRecordMapping Mapping(Reader, CodeViewContainer::ObjectFile); 326 ConstantSym Const(SymbolKind::S_CONSTANT); 327 cantFail(Mapping.visitSymbolBegin(Sym)); 328 cantFail(Mapping.visitKnownRecord(Sym, Const)); 329 cantFail(Mapping.visitSymbolEnd(Sym)); 330 return Const.Name; 331 } 332 333 int Offset = getSymbolNameOffset(Sym); 334 if (Offset == -1) 335 return StringRef(); 336 337 StringRef StringData = toStringRef(Sym.content()).drop_front(Offset); 338 return StringData.split('\0').first; 339 } 340