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