1 //===- RecordName.cpp ----------------------------------------- *- C++ --*-===//
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
7 //===----------------------------------------------------------------------===//
9 #include "llvm/DebugInfo/CodeView/RecordName.h"
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"
24 using namespace llvm::codeview
;
27 class TypeNameComputer
: public TypeVisitorCallbacks
{
28 /// The type collection. Used to calculate names of nested types.
29 TypeCollection
&Types
;
30 TypeIndex CurrentTypeIndex
= TypeIndex::None();
32 /// Name of the current type. Only valid before visitTypeEnd.
33 SmallString
<256> Name
;
36 explicit TypeNameComputer(TypeCollection
&Types
) : Types(Types
) {}
38 StringRef
name() const { return Name
; }
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
;
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"
54 Error
TypeNameComputer::visitTypeBegin(CVType
&Record
) {
55 llvm_unreachable("Must call visitTypeBegin with a TypeIndex!");
56 return Error::success();
59 Error
TypeNameComputer::visitTypeBegin(CVType
&Record
, TypeIndex Index
) {
60 // Reset Name to the empty string. If the visitor sets it, we know it.
62 CurrentTypeIndex
= Index
;
63 return Error::success();
66 Error
TypeNameComputer::visitTypeEnd(CVType
&CVR
) { return Error::success(); }
68 Error
TypeNameComputer::visitKnownRecord(CVType
&CVR
,
69 FieldListRecord
&FieldList
) {
70 Name
= "<field list>";
71 return Error::success();
74 Error
TypeNameComputer::visitKnownRecord(CVRecord
<TypeLeafKind
> &CVR
,
75 StringIdRecord
&String
) {
76 Name
= String
.getString();
77 return Error::success();
80 Error
TypeNameComputer::visitKnownRecord(CVType
&CVR
, ArgListRecord
&Args
) {
81 auto Indices
= Args
.getIndices();
82 uint32_t Size
= Indices
.size();
84 for (uint32_t I
= 0; I
< Size
; ++I
) {
85 if (Indices
[I
] < CurrentTypeIndex
)
86 Name
.append(Types
.getTypeName(Indices
[I
]));
88 Name
.append("<unknown 0x" + utohexstr(Indices
[I
].getIndex()) + ">");
93 return Error::success();
96 Error
TypeNameComputer::visitKnownRecord(CVType
&CVR
,
97 StringListRecord
&Strings
) {
98 auto Indices
= Strings
.getIndices();
99 uint32_t Size
= Indices
.size();
101 for (uint32_t I
= 0; I
< Size
; ++I
) {
102 Name
.append(Types
.getTypeName(Indices
[I
]));
104 Name
.append("\" \"");
106 Name
.push_back('\"');
107 return Error::success();
110 Error
TypeNameComputer::visitKnownRecord(CVType
&CVR
, ClassRecord
&Class
) {
111 Name
= Class
.getName();
112 return Error::success();
115 Error
TypeNameComputer::visitKnownRecord(CVType
&CVR
, UnionRecord
&Union
) {
116 Name
= Union
.getName();
117 return Error::success();
120 Error
TypeNameComputer::visitKnownRecord(CVType
&CVR
, EnumRecord
&Enum
) {
121 Name
= Enum
.getName();
122 return Error::success();
125 Error
TypeNameComputer::visitKnownRecord(CVType
&CVR
, ArrayRecord
&AT
) {
127 return Error::success();
130 Error
TypeNameComputer::visitKnownRecord(CVType
&CVR
, VFTableRecord
&VFT
) {
131 Name
= VFT
.getName();
132 return Error::success();
135 Error
TypeNameComputer::visitKnownRecord(CVType
&CVR
, MemberFuncIdRecord
&Id
) {
137 return Error::success();
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();
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();
156 Error
TypeNameComputer::visitKnownRecord(CVType
&CVR
, FuncIdRecord
&Func
) {
157 Name
= Func
.getName();
158 return Error::success();
161 Error
TypeNameComputer::visitKnownRecord(CVType
&CVR
, TypeServer2Record
&TS
) {
163 return Error::success();
166 Error
TypeNameComputer::visitKnownRecord(CVType
&CVR
, PointerRecord
&Ptr
) {
168 if (Ptr
.isPointerToMember()) {
169 const MemberPointerInfo
&MI
= Ptr
.getMemberInfo();
171 StringRef Pointee
= Types
.getTypeName(Ptr
.getReferentType());
172 StringRef Class
= Types
.getTypeName(MI
.getContainingType());
173 Name
= formatv("{0} {1}::*", Pointee
, Class
);
175 Name
.append(Types
.getTypeName(Ptr
.getReferentType()));
177 if (Ptr
.getMode() == PointerMode::LValueReference
)
179 else if (Ptr
.getMode() == PointerMode::RValueReference
)
181 else if (Ptr
.getMode() == PointerMode::Pointer
)
184 // Qualifiers in pointer records apply to the pointer, not the pointee, so
185 // they go on the right.
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");
195 return Error::success();
198 Error
TypeNameComputer::visitKnownRecord(CVType
&CVR
, ModifierRecord
&Mod
) {
199 uint16_t Mods
= static_cast<uint16_t>(Mod
.getModifiers());
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();
211 Error
TypeNameComputer::visitKnownRecord(CVType
&CVR
,
212 VFTableShapeRecord
&Shape
) {
213 Name
= formatv("<vftable {0} methods>", Shape
.getEntryCount());
214 return Error::success();
217 Error
TypeNameComputer::visitKnownRecord(
218 CVType
&CVR
, UdtModSourceLineRecord
&ModSourceLine
) {
219 return Error::success();
222 Error
TypeNameComputer::visitKnownRecord(CVType
&CVR
,
223 UdtSourceLineRecord
&SourceLine
) {
224 return Error::success();
227 Error
TypeNameComputer::visitKnownRecord(CVType
&CVR
, BitFieldRecord
&BF
) {
228 return Error::success();
231 Error
TypeNameComputer::visitKnownRecord(CVType
&CVR
,
232 MethodOverloadListRecord
&Overloads
) {
233 return Error::success();
236 Error
TypeNameComputer::visitKnownRecord(CVType
&CVR
, BuildInfoRecord
&BI
) {
237 return Error::success();
240 Error
TypeNameComputer::visitKnownRecord(CVType
&CVR
, LabelRecord
&R
) {
241 return Error::success();
244 Error
TypeNameComputer::visitKnownRecord(CVType
&CVR
,
245 PrecompRecord
&Precomp
) {
246 return Error::success();
249 Error
TypeNameComputer::visitKnownRecord(CVType
&CVR
,
250 EndPrecompRecord
&EndPrecomp
) {
251 return Error::success();
254 std::string
llvm::codeview::computeTypeName(TypeCollection
&Types
,
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>";
262 return std::string(Computer
.name());
265 static int getSymbolNameOffset(CVSymbol Sym
) {
266 switch (Sym
.kind()) {
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
:
276 case SymbolKind::S_THUNK32
:
279 case SymbolKind::S_SECTION
:
282 case SymbolKind::S_COFFGROUP
:
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
:
297 // See RegisterSym and LocalSym
298 case SymbolKind::S_REGISTER
:
299 case SymbolKind::S_LOCAL
:
302 case SymbolKind::S_BLOCK32
:
305 case SymbolKind::S_LABEL32
:
307 // See ObjNameSym, ExportSym, and UDTSym
308 case SymbolKind::S_OBJNAME
:
309 case SymbolKind::S_EXPORT
:
310 case SymbolKind::S_UDT
:
313 case SymbolKind::S_BPREL32
:
315 // See UsingNamespaceSym
316 case SymbolKind::S_UNAMESPACE
:
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
));
337 int Offset
= getSymbolNameOffset(Sym
);
341 StringRef StringData
= toStringRef(Sym
.content()).drop_front(Offset
);
342 return StringData
.split('\0').first
;