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
18using namespace llvm;
19using namespace llvm::codeview;
20
21namespace {
22class 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
30public:
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
49Error TypeNameComputer::visitTypeBegin(CVType &Record) {
50  llvm_unreachable("Must call visitTypeBegin with a TypeIndex!");
51  return Error::success();
52}
53
54Error 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
61Error TypeNameComputer::visitTypeEnd(CVType &CVR) { return Error::success(); }
62
63Error TypeNameComputer::visitKnownRecord(CVType &CVR,
64                                         FieldListRecord &FieldList) {
65  Name = "<field list>";
66  return Error::success();
67}
68
69Error TypeNameComputer::visitKnownRecord(CVRecord<TypeLeafKind> &CVR,
70                                         StringIdRecord &String) {
71  Name = String.getString();
72  return Error::success();
73}
74
75Error 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
90Error 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
104Error TypeNameComputer::visitKnownRecord(CVType &CVR, ClassRecord &Class) {
105  Name = Class.getName();
106  return Error::success();
107}
108
109Error TypeNameComputer::visitKnownRecord(CVType &CVR, UnionRecord &Union) {
110  Name = Union.getName();
111  return Error::success();
112}
113
114Error TypeNameComputer::visitKnownRecord(CVType &CVR, EnumRecord &Enum) {
115  Name = Enum.getName();
116  return Error::success();
117}
118
119Error TypeNameComputer::visitKnownRecord(CVType &CVR, ArrayRecord &AT) {
120  Name = AT.getName();
121  return Error::success();
122}
123
124Error TypeNameComputer::visitKnownRecord(CVType &CVR, VFTableRecord &VFT) {
125  Name = VFT.getName();
126  return Error::success();
127}
128
129Error TypeNameComputer::visitKnownRecord(CVType &CVR, MemberFuncIdRecord &Id) {
130  Name = Id.getName();
131  return Error::success();
132}
133
134Error 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
141Error 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
150Error TypeNameComputer::visitKnownRecord(CVType &CVR, FuncIdRecord &Func) {
151  Name = Func.getName();
152  return Error::success();
153}
154
155Error TypeNameComputer::visitKnownRecord(CVType &CVR, TypeServer2Record &TS) {
156  Name = TS.getName();
157  return Error::success();
158}
159
160Error 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
192Error 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
205Error TypeNameComputer::visitKnownRecord(CVType &CVR,
206                                         VFTableShapeRecord &Shape) {
207  Name = formatv("<vftable {0} methods>", Shape.getEntryCount());
208  return Error::success();
209}
210
211Error TypeNameComputer::visitKnownRecord(
212    CVType &CVR, UdtModSourceLineRecord &ModSourceLine) {
213  return Error::success();
214}
215
216Error TypeNameComputer::visitKnownRecord(CVType &CVR,
217                                         UdtSourceLineRecord &SourceLine) {
218  return Error::success();
219}
220
221Error TypeNameComputer::visitKnownRecord(CVType &CVR, BitFieldRecord &BF) {
222  return Error::success();
223}
224
225Error TypeNameComputer::visitKnownRecord(CVType &CVR,
226                                         MethodOverloadListRecord &Overloads) {
227  return Error::success();
228}
229
230Error TypeNameComputer::visitKnownRecord(CVType &CVR, BuildInfoRecord &BI) {
231  return Error::success();
232}
233
234Error TypeNameComputer::visitKnownRecord(CVType &CVR, LabelRecord &R) {
235  return Error::success();
236}
237
238Error TypeNameComputer::visitKnownRecord(CVType &CVR,
239                                         PrecompRecord &Precomp) {
240  return Error::success();
241}
242
243Error TypeNameComputer::visitKnownRecord(CVType &CVR,
244                                         EndPrecompRecord &EndPrecomp) {
245  return Error::success();
246}
247
248std::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
259static 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
317StringRef 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