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: |
36 | explicit TypeNameComputer(TypeCollection &Types) : Types(Types) {} |
37 | |
38 | 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 | |
54 | Error TypeNameComputer::visitTypeBegin(CVType &Record) { |
55 | llvm_unreachable("Must call visitTypeBegin with a TypeIndex!" ); |
56 | return Error::success(); |
57 | } |
58 | |
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 | |
66 | Error TypeNameComputer::visitTypeEnd(CVType &CVR) { return Error::success(); } |
67 | |
68 | Error TypeNameComputer::visitKnownRecord(CVType &CVR, |
69 | FieldListRecord &FieldList) { |
70 | Name = "<field list>" ; |
71 | return Error::success(); |
72 | } |
73 | |
74 | Error TypeNameComputer::visitKnownRecord(CVRecord<TypeLeafKind> &CVR, |
75 | StringIdRecord &String) { |
76 | Name = String.getString(); |
77 | return Error::success(); |
78 | } |
79 | |
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(RHS: Types.getTypeName(Index: Indices[I])); |
87 | else |
88 | Name.append(RHS: "<unknown 0x" + utohexstr(X: Indices[I].getIndex()) + ">" ); |
89 | if (I + 1 != Size) |
90 | Name.append(RHS: ", " ); |
91 | } |
92 | Name.push_back(Elt: ')'); |
93 | return Error::success(); |
94 | } |
95 | |
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(RHS: Types.getTypeName(Index: Indices[I])); |
103 | if (I + 1 != Size) |
104 | Name.append(RHS: "\" \"" ); |
105 | } |
106 | Name.push_back(Elt: '\"'); |
107 | return Error::success(); |
108 | } |
109 | |
110 | Error TypeNameComputer::visitKnownRecord(CVType &CVR, ClassRecord &Class) { |
111 | Name = Class.getName(); |
112 | return Error::success(); |
113 | } |
114 | |
115 | Error TypeNameComputer::visitKnownRecord(CVType &CVR, UnionRecord &Union) { |
116 | Name = Union.getName(); |
117 | return Error::success(); |
118 | } |
119 | |
120 | Error TypeNameComputer::visitKnownRecord(CVType &CVR, EnumRecord &Enum) { |
121 | Name = Enum.getName(); |
122 | return Error::success(); |
123 | } |
124 | |
125 | Error TypeNameComputer::visitKnownRecord(CVType &CVR, ArrayRecord &AT) { |
126 | Name = AT.getName(); |
127 | return Error::success(); |
128 | } |
129 | |
130 | Error TypeNameComputer::visitKnownRecord(CVType &CVR, VFTableRecord &VFT) { |
131 | Name = VFT.getName(); |
132 | return Error::success(); |
133 | } |
134 | |
135 | Error TypeNameComputer::visitKnownRecord(CVType &CVR, MemberFuncIdRecord &Id) { |
136 | Name = Id.getName(); |
137 | return Error::success(); |
138 | } |
139 | |
140 | Error TypeNameComputer::visitKnownRecord(CVType &CVR, ProcedureRecord &Proc) { |
141 | StringRef Ret = Types.getTypeName(Index: Proc.getReturnType()); |
142 | StringRef Params = Types.getTypeName(Index: Proc.getArgumentList()); |
143 | Name = formatv(Fmt: "{0} {1}" , Vals&: Ret, Vals&: Params).sstr<256>(); |
144 | return Error::success(); |
145 | } |
146 | |
147 | Error TypeNameComputer::visitKnownRecord(CVType &CVR, |
148 | MemberFunctionRecord &MF) { |
149 | StringRef Ret = Types.getTypeName(Index: MF.getReturnType()); |
150 | StringRef Class = Types.getTypeName(Index: MF.getClassType()); |
151 | StringRef Params = Types.getTypeName(Index: MF.getArgumentList()); |
152 | Name = formatv(Fmt: "{0} {1}::{2}" , Vals&: Ret, Vals&: Class, Vals&: Params).sstr<256>(); |
153 | return Error::success(); |
154 | } |
155 | |
156 | Error TypeNameComputer::visitKnownRecord(CVType &CVR, FuncIdRecord &Func) { |
157 | Name = Func.getName(); |
158 | return Error::success(); |
159 | } |
160 | |
161 | Error TypeNameComputer::visitKnownRecord(CVType &CVR, TypeServer2Record &TS) { |
162 | Name = TS.getName(); |
163 | return Error::success(); |
164 | } |
165 | |
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(Index: Ptr.getReferentType()); |
172 | StringRef Class = Types.getTypeName(Index: MI.getContainingType()); |
173 | Name = formatv(Fmt: "{0} {1}::*" , Vals&: Pointee, Vals&: Class); |
174 | } else { |
175 | Name.append(RHS: Types.getTypeName(Index: Ptr.getReferentType())); |
176 | |
177 | if (Ptr.getMode() == PointerMode::LValueReference) |
178 | Name.append(RHS: "&" ); |
179 | else if (Ptr.getMode() == PointerMode::RValueReference) |
180 | Name.append(RHS: "&&" ); |
181 | else if (Ptr.getMode() == PointerMode::Pointer) |
182 | Name.append(RHS: "*" ); |
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(RHS: " const" ); |
188 | if (Ptr.isVolatile()) |
189 | Name.append(RHS: " volatile" ); |
190 | if (Ptr.isUnaligned()) |
191 | Name.append(RHS: " __unaligned" ); |
192 | if (Ptr.isRestrict()) |
193 | Name.append(RHS: " __restrict" ); |
194 | } |
195 | return Error::success(); |
196 | } |
197 | |
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(RHS: "const " ); |
203 | if (Mods & uint16_t(ModifierOptions::Volatile)) |
204 | Name.append(RHS: "volatile " ); |
205 | if (Mods & uint16_t(ModifierOptions::Unaligned)) |
206 | Name.append(RHS: "__unaligned " ); |
207 | Name.append(RHS: Types.getTypeName(Index: Mod.getModifiedType())); |
208 | return Error::success(); |
209 | } |
210 | |
211 | Error TypeNameComputer::visitKnownRecord(CVType &CVR, |
212 | VFTableShapeRecord &Shape) { |
213 | Name = formatv(Fmt: "<vftable {0} methods>" , Vals: Shape.getEntryCount()); |
214 | return Error::success(); |
215 | } |
216 | |
217 | Error TypeNameComputer::visitKnownRecord( |
218 | CVType &CVR, UdtModSourceLineRecord &ModSourceLine) { |
219 | return Error::success(); |
220 | } |
221 | |
222 | Error TypeNameComputer::visitKnownRecord(CVType &CVR, |
223 | UdtSourceLineRecord &SourceLine) { |
224 | return Error::success(); |
225 | } |
226 | |
227 | Error TypeNameComputer::visitKnownRecord(CVType &CVR, BitFieldRecord &BF) { |
228 | return Error::success(); |
229 | } |
230 | |
231 | Error TypeNameComputer::visitKnownRecord(CVType &CVR, |
232 | MethodOverloadListRecord &Overloads) { |
233 | return Error::success(); |
234 | } |
235 | |
236 | Error TypeNameComputer::visitKnownRecord(CVType &CVR, BuildInfoRecord &BI) { |
237 | return Error::success(); |
238 | } |
239 | |
240 | Error TypeNameComputer::visitKnownRecord(CVType &CVR, LabelRecord &R) { |
241 | return Error::success(); |
242 | } |
243 | |
244 | Error TypeNameComputer::visitKnownRecord(CVType &CVR, |
245 | PrecompRecord &Precomp) { |
246 | return Error::success(); |
247 | } |
248 | |
249 | Error TypeNameComputer::visitKnownRecord(CVType &CVR, |
250 | EndPrecompRecord &EndPrecomp) { |
251 | return Error::success(); |
252 | } |
253 | |
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, Callbacks&: Computer)) { |
259 | consumeError(Err: std::move(EC)); |
260 | return "<unknown UDT>" ; |
261 | } |
262 | return std::string(Computer.name()); |
263 | } |
264 | |
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 | |
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(Err: Mapping.visitSymbolBegin(Record&: Sym)); |
332 | cantFail(Err: Mapping.visitKnownRecord(CVR&: Sym, Record&: Const)); |
333 | cantFail(Err: Mapping.visitSymbolEnd(Record&: Sym)); |
334 | return Const.Name; |
335 | } |
336 | |
337 | int Offset = getSymbolNameOffset(Sym); |
338 | if (Offset == -1) |
339 | return StringRef(); |
340 | |
341 | StringRef StringData = toStringRef(Input: Sym.content()).drop_front(N: Offset); |
342 | return StringData.split(Separator: '\0').first; |
343 | } |
344 | |