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