Skip to content

Commit 4bd0c48

Browse files
[DebugInfo] Add symbol for debugger with VTable information.
The IR now includes a global variable for the debugger that holds the address of the vtable. Now every class that contains virtual functions, has a static member (marked as artificial) that identifies where that vtable is loaded in memory. The unmangled name is '_vtable$'. This new symbol will allow a debugger to easily associate classes with the physical location of their VTables using only the DWARF information. Previously, this had to be done by searching for ELF symbols with matching names; something that was time-consuming and error-prone in certain edge cases.
1 parent c020191 commit 4bd0c48

16 files changed

+755
-43
lines changed

clang/lib/CodeGen/CGDebugInfo.cpp

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2518,6 +2518,59 @@ StringRef CGDebugInfo::getVTableName(const CXXRecordDecl *RD) {
25182518
return internString("_vptr$", RD->getNameAsString());
25192519
}
25202520

2521+
// Emit symbol for the debugger that points to the vtable address for
2522+
// the given class. The symbol is named as '_vtable$'.
2523+
// The debugger does not need to know any details about the contents of the
2524+
// vtable as it can work this out using its knowledge of the ABI and the
2525+
// existing information in the DWARF. The type is assumed to be 'void *'.
2526+
void CGDebugInfo::emitVTableSymbol(llvm::GlobalVariable *VTable,
2527+
const CXXRecordDecl *RD) {
2528+
ASTContext &Context = CGM.getContext();
2529+
SmallString<64> Buffer;
2530+
Twine SymbolName = internString("_vtable$");
2531+
StringRef SymbolNameRef = SymbolName.toStringRef(Buffer);
2532+
DeclContext *DC = static_cast<DeclContext *>(const_cast<CXXRecordDecl *>(RD));
2533+
SourceLocation Loc;
2534+
QualType VoidPtr = Context.getPointerType(Context.VoidTy);
2535+
2536+
// We deal with two different contexts:
2537+
// - The type for the variable, which is part of the class that has the
2538+
// vtable, is placed in the context of the DICompositeType metadata.
2539+
// - The DIGlobalVariable for the vtable is put in the DICompileUnitScope.
2540+
2541+
// The created non-member should be mark as 'artificial'. It will be
2542+
// placed it inside the scope of the C++ class/structure.
2543+
llvm::DIScope *DContext = getContextDescriptor(cast<Decl>(DC), TheCU);
2544+
auto *Ctxt = cast<llvm::DICompositeType>(DContext);
2545+
llvm::DIFile *Unit = getOrCreateFile(Loc);
2546+
llvm::DIType *VTy = getOrCreateType(VoidPtr, Unit);
2547+
llvm::DINode::DIFlags Flags = getAccessFlag(AccessSpecifier::AS_private, RD);
2548+
auto Tag = CGM.getCodeGenOpts().DwarfVersion >= 5
2549+
? llvm::dwarf::DW_TAG_variable
2550+
: llvm::dwarf::DW_TAG_member;
2551+
llvm::DIDerivedType *OldDT = DBuilder.createStaticMemberType(
2552+
Ctxt, SymbolNameRef, Unit, /*LineNumber=*/0, VTy, Flags,
2553+
/*Val=*/nullptr, Tag);
2554+
llvm::DIDerivedType *DT =
2555+
static_cast<llvm::DIDerivedType *>(DBuilder.createArtificialType(OldDT));
2556+
2557+
// Use the same vtable pointer to global alignment for the symbol.
2558+
LangAS AS = CGM.GetGlobalVarAddressSpace(nullptr);
2559+
unsigned PAlign = CGM.getItaniumVTableContext().isRelativeLayout()
2560+
? 32
2561+
: CGM.getTarget().getPointerAlign(AS);
2562+
2563+
// The global variable is in the CU scope, and links back to the type it's
2564+
// "within" via the declaration field.
2565+
llvm::DIGlobalVariableExpression *GVE =
2566+
DBuilder.createGlobalVariableExpression(
2567+
TheCU, SymbolNameRef, VTable->getName(), Unit, /*LineNo=*/0,
2568+
getOrCreateType(VoidPtr, Unit), VTable->hasLocalLinkage(),
2569+
/*isDefined=*/true, nullptr, DT, /*TemplateParameters=*/nullptr,
2570+
PAlign);
2571+
VTable->addDebugInfo(GVE);
2572+
}
2573+
25212574
StringRef CGDebugInfo::getDynamicInitializerName(const VarDecl *VD,
25222575
DynamicInitKind StubKind,
25232576
llvm::Function *InitFn) {

clang/lib/CodeGen/CGDebugInfo.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -636,6 +636,9 @@ class CGDebugInfo {
636636
StringRef Category,
637637
StringRef FailureMsg);
638638

639+
/// Emit symbol for debugger that holds the pointer to the vtable.
640+
void emitVTableSymbol(llvm::GlobalVariable *VTable, const CXXRecordDecl *RD);
641+
639642
private:
640643
/// Emit call to llvm.dbg.declare for a variable declaration.
641644
/// Returns a pointer to the DILocalVariable associated with the

clang/lib/CodeGen/ItaniumCXXABI.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2059,6 +2059,10 @@ void ItaniumCXXABI::emitVTableDefinitions(CodeGenVTables &CGVT,
20592059
if (!VTable->isDSOLocal())
20602060
CGVT.GenerateRelativeVTableAlias(VTable, VTable->getName());
20612061
}
2062+
2063+
// Emit symbol for debugger only if requested debug info.
2064+
if (CGDebugInfo *DI = CGM.getModuleDebugInfo())
2065+
DI->emitVTableSymbol(VTable, RD);
20622066
}
20632067

20642068
bool ItaniumCXXABI::isVirtualOffsetNeededForVTableField(
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#include "vtable-debug-info-inheritance-simple-base.h"
2+
3+
void NSP::CBase::zero() {}
4+
int NSP::CBase::one() { return 1; }
5+
int NSP::CBase::two() { return 2; };
6+
int NSP::CBase::three() { return 3; }
7+
8+
#ifdef SYMBOL_AT_FILE_SCOPE
9+
static NSP::CBase Base;
10+
#else
11+
void fooBase() {
12+
NSP::CBase Base;
13+
}
14+
#endif
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#ifndef BASE_H
2+
#define BASE_H
3+
4+
namespace NSP {
5+
struct CBase {
6+
unsigned B = 1;
7+
virtual void zero();
8+
virtual int one();
9+
virtual int two();
10+
virtual int three();
11+
};
12+
}
13+
14+
extern void fooBase();
15+
#endif
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#include "vtable-debug-info-inheritance-simple-derived.h"
2+
3+
void CDerived::zero() {}
4+
int CDerived::two() { return 22; };
5+
int CDerived::three() { return 33; }
6+
7+
#ifdef SYMBOL_AT_FILE_SCOPE
8+
static CDerived Derived;
9+
#else
10+
void fooDerived() {
11+
CDerived Derived;
12+
}
13+
#endif
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#include "vtable-debug-info-inheritance-simple-base.h"
2+
3+
#ifndef DERIVED_H
4+
#define DERIVED_H
5+
6+
struct CDerived : NSP::CBase {
7+
unsigned D = 2;
8+
void zero() override;
9+
int two() override;
10+
int three() override;
11+
};
12+
13+
extern void fooDerived();
14+
#endif

clang/test/CodeGenCXX/debug-info-class.cpp

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -122,14 +122,6 @@ int main(int argc, char **argv) {
122122
// CHECK-SAME: ){{$}}
123123

124124
// CHECK: ![[INT:[0-9]+]] = !DIBasicType(name: "int"
125-
// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "foo"
126-
// CHECK: !DICompositeType(tag: DW_TAG_class_type, name: "bar"
127-
// CHECK: !DICompositeType(tag: DW_TAG_union_type, name: "baz"
128-
// CHECK: !DICompositeType(tag: DW_TAG_class_type, name: "B"
129-
// CHECK-NOT: DIFlagFwdDecl
130-
// CHECK-SAME: ){{$}}
131-
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "_vptr$B",
132-
// CHECK-SAME: DIFlagArtificial
133125

134126
// CHECK: [[C:![0-9]*]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "C",
135127
// CHECK-NOT: DIFlagFwdDecl
@@ -145,6 +137,20 @@ int main(int argc, char **argv) {
145137
// CHECK-SAME: DIFlagStaticMember
146138
// CHECK: [[C_DTOR]] = !DISubprogram(name: "~C"
147139

140+
// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "K"
141+
// CHECK-SAME: identifier: "_ZTS1K"
142+
// CHECK-SAME: ){{$}}
143+
144+
// CHECK: !DICompositeType(tag: DW_TAG_class_type, name: "B"
145+
// CHECK-NOT: DIFlagFwdDecl
146+
// CHECK-SAME: ){{$}}
147+
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "_vptr$B",
148+
// CHECK-SAME: DIFlagArtificial
149+
150+
// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "foo"
151+
// CHECK: !DICompositeType(tag: DW_TAG_class_type, name: "bar"
152+
// CHECK: !DICompositeType(tag: DW_TAG_union_type, name: "baz"
153+
148154
// CHECK: [[D:![0-9]+]] = !DICompositeType(tag: DW_TAG_structure_type, name: "D"
149155
// CHECK-SAME: size:
150156
// CHECK-SAME: DIFlagFwdDecl
@@ -156,10 +162,6 @@ int main(int argc, char **argv) {
156162
// CHECK-NOT: identifier:
157163
// CHECK-SAME: ){{$}}
158164

159-
// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "K"
160-
// CHECK-SAME: identifier: "_ZTS1K"
161-
// CHECK-SAME: ){{$}}
162-
163165
// CHECK: [[L:![0-9]+]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "L"
164166
// CHECK-SAME: ){{$}}
165167
// CHECK: [[L_FUNC_DECL:![0-9]*]] = !DISubprogram(name: "func",{{.*}} scope: [[L]]

clang/test/CodeGenCXX/debug-info-template-member.cpp

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -22,29 +22,6 @@ inline int add3(int x) {
2222
// CHECK: [[X]] = !DIGlobalVariableExpression(var: [[XV:.*]], expr: !DIExpression())
2323
// CHECK: [[XV]] = distinct !DIGlobalVariable(name: "x",
2424
// CHECK-SAME: type: ![[OUTER_FOO_INNER_ID:[0-9]+]]
25-
//
26-
// CHECK: {{![0-9]+}} = distinct !DIGlobalVariable(
27-
// CHECK-SAME: name: "var"
28-
// CHECK-SAME: templateParams: {{![0-9]+}}
29-
// CHECK: !DITemplateTypeParameter(name: "T", type: [[TY:![0-9]+]])
30-
// CHECK: {{![0-9]+}} = distinct !DIGlobalVariable(
31-
// CHECK-SAME: name: "var"
32-
// CHECK-SAME: templateParams: {{![0-9]+}}
33-
// CHECK: !DITemplateTypeParameter(name: "T", type: {{![0-9]+}})
34-
// CHECK: {{![0-9]+}} = distinct !DIGlobalVariable(
35-
// CHECK-SAME: name: "varray"
36-
// CHECK-SAME: templateParams: {{![0-9]+}}
37-
// CHECK: !DITemplateValueParameter(name: "N", type: [[TY]], value: i32 1)
38-
39-
// CHECK: ![[OUTER_FOO_INNER_ID:[0-9]*]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "inner"{{.*}}, identifier:
40-
// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "foo"
41-
// CHECK-SAME: elements: [[FOO_MEM:![0-9]*]]
42-
// CHECK-SAME: identifier: "_ZTS3foo"
43-
// CHECK: [[FOO_MEM]] = !{[[FOO_FUNC:![0-9]*]]}
44-
// CHECK: [[FOO_FUNC]] = !DISubprogram(name: "func", linkageName: "_ZN3foo4funcEN5outerIS_E5innerE",
45-
// CHECK-SAME: type: [[FOO_FUNC_TYPE:![0-9]*]]
46-
// CHECK: [[FOO_FUNC_TYPE]] = !DISubroutineType(types: [[FOO_FUNC_PARAMS:![0-9]*]])
47-
// CHECK: [[FOO_FUNC_PARAMS]] = !{null, !{{[0-9]*}}, ![[OUTER_FOO_INNER_ID]]}
4825

4926
// CHECK: [[C:![0-9]*]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "MyClass"
5027
// CHECK-SAME: elements: [[C_MEM:![0-9]*]]
@@ -55,9 +32,6 @@ inline int add3(int x) {
5532

5633
// CHECK: [[C_FUNC]] = !DISubprogram(name: "func",{{.*}} line: 9,
5734

58-
// CHECK: !DISubprogram(name: "add<2>"
59-
// CHECK-SAME: scope: [[C]]
60-
//
6135
// CHECK: [[VIRT_TEMP:![0-9]+]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "virt<elem>"
6236
// CHECK-SAME: elements: [[VIRT_MEM:![0-9]*]]
6337
// CHECK-SAME: vtableHolder: [[VIRT_TEMP]]
@@ -74,6 +48,32 @@ inline int add3(int x) {
7448
// CHECK: [[VIRT_TEMP_PARAM]] = !{[[VIRT_T:![0-9]*]]}
7549
// CHECK: [[VIRT_T]] = !DITemplateTypeParameter(name: "T", type: [[ELEM]])
7650

51+
// CHECK: {{![0-9]+}} = distinct !DIGlobalVariable(
52+
// CHECK-SAME: name: "var"
53+
// CHECK-SAME: templateParams: {{![0-9]+}}
54+
// CHECK: !DITemplateTypeParameter(name: "T", type: [[TY:![0-9]+]])
55+
// CHECK: {{![0-9]+}} = distinct !DIGlobalVariable(
56+
// CHECK-SAME: name: "var"
57+
// CHECK-SAME: templateParams: {{![0-9]+}}
58+
// CHECK: !DITemplateTypeParameter(name: "T", type: {{![0-9]+}})
59+
// CHECK: {{![0-9]+}} = distinct !DIGlobalVariable(
60+
// CHECK-SAME: name: "varray"
61+
// CHECK-SAME: templateParams: {{![0-9]+}}
62+
// CHECK: !DITemplateValueParameter(name: "N", type: [[TY]], value: i32 1)
63+
64+
// CHECK: ![[OUTER_FOO_INNER_ID:[0-9]*]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "inner"{{.*}}, identifier:
65+
// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "foo"
66+
// CHECK-SAME: elements: [[FOO_MEM:![0-9]*]]
67+
// CHECK-SAME: identifier: "_ZTS3foo"
68+
// CHECK: [[FOO_MEM]] = !{[[FOO_FUNC:![0-9]*]]}
69+
// CHECK: [[FOO_FUNC]] = !DISubprogram(name: "func", linkageName: "_ZN3foo4funcEN5outerIS_E5innerE",
70+
// CHECK-SAME: type: [[FOO_FUNC_TYPE:![0-9]*]]
71+
// CHECK: [[FOO_FUNC_TYPE]] = !DISubroutineType(types: [[FOO_FUNC_PARAMS:![0-9]*]])
72+
// CHECK: [[FOO_FUNC_PARAMS]] = !{null, !{{[0-9]*}}, ![[OUTER_FOO_INNER_ID]]}
73+
74+
// CHECK: !DISubprogram(name: "add<2>"
75+
// CHECK-SAME: scope: [[C]]
76+
7777
template<typename T>
7878
struct outer {
7979
struct inner {
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// REQUIRES: target={{x86_64.*-linux.*}}
2+
3+
// Diamond inheritance case:
4+
// For CBase, CLeft, CRight and CDerived we check:
5+
// - Generation of their vtables (including attributes).
6+
// - Generation of their '_vtable$' data members:
7+
// * Correct scope and attributes
8+
9+
namespace NSP {
10+
struct CBase {
11+
int B = 0;
12+
virtual char fooBase() { return 'B'; }
13+
};
14+
}
15+
16+
namespace NSP_1 {
17+
struct CLeft : NSP::CBase {
18+
int M1 = 1;
19+
char fooBase() override { return 'O'; };
20+
virtual int fooLeft() { return 1; }
21+
};
22+
}
23+
24+
namespace NSP_2 {
25+
struct CRight : NSP::CBase {
26+
int M2 = 2;
27+
char fooBase() override { return 'T'; };
28+
virtual int fooRight() { return 2; }
29+
};
30+
}
31+
32+
struct CDerived : NSP_1::CLeft, NSP_2::CRight {
33+
int D = 3;
34+
char fooBase() override { return 'D'; };
35+
int fooDerived() { return 3; };
36+
};
37+
38+
int main() {
39+
NSP::CBase Base;
40+
NSP_1::CLeft Left;
41+
NSP_2::CRight Right;
42+
CDerived Derived;
43+
44+
return 0;
45+
}
46+
47+
// RUN: %clang --target=x86_64-linux -Xclang -disable-O0-optnone -Xclang -disable-llvm-passes -emit-llvm -S -g %s -o - | FileCheck %s
48+
49+
// CHECK: $_ZTVN3NSP5CBaseE = comdat any
50+
// CHECK: $_ZTVN5NSP_15CLeftE = comdat any
51+
// CHECK: $_ZTVN5NSP_26CRightE = comdat any
52+
// CHECK: $_ZTV8CDerived = comdat any
53+
54+
// CHECK: @_ZTVN3NSP5CBaseE = linkonce_odr {{dso_local|hidden}} unnamed_addr constant {{.*}}, comdat, align 8, !dbg [[BASE_VTABLE_VAR:![0-9]*]]
55+
// CHECK: @_ZTVN5NSP_15CLeftE = linkonce_odr {{dso_local|hidden}} unnamed_addr constant {{.*}}, comdat, align 8, !dbg [[LEFT_VTABLE_VAR:![0-9]*]]
56+
// CHECK: @_ZTVN5NSP_26CRightE = linkonce_odr {{dso_local|hidden}} unnamed_addr constant {{.*}}, comdat, align 8, !dbg [[RIGHT_VTABLE_VAR:![0-9]*]]
57+
// CHECK: @_ZTV8CDerived = linkonce_odr {{dso_local|hidden}} unnamed_addr constant {{.*}}, comdat, align 8, !dbg [[DERIVED_VTABLE_VAR:![0-9]*]]
58+
59+
// CHECK: [[BASE_VTABLE_VAR]] = !DIGlobalVariableExpression(var: [[BASE_VTABLE:![0-9]*]], expr: !DIExpression())
60+
// CHECK-NEXT: [[BASE_VTABLE]] = distinct !DIGlobalVariable(name: "_vtable$", linkageName: "_ZTVN3NSP5CBaseE"
61+
62+
// CHECK: [[LEFT_VTABLE_VAR]] = !DIGlobalVariableExpression(var: [[LEFT_VTABLE:![0-9]*]], expr: !DIExpression())
63+
// CHECK-NEXT: [[LEFT_VTABLE]] = distinct !DIGlobalVariable(name: "_vtable$", linkageName: "_ZTVN5NSP_15CLeftE"
64+
65+
// CHECK: [[TYPE:![0-9]*]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64)
66+
67+
// CHECK: !DIDerivedType(tag: DW_TAG_variable, name: "_vtable$", scope: [[LEFT:![0-9]*]], file: {{.*}}, baseType: [[TYPE]], flags: DIFlagPrivate | DIFlagArtificial | DIFlagStaticMember)
68+
69+
// CHECK: [[LEFT]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "CLeft"
70+
71+
// CHECK: [[BASE:![0-9]*]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "CBase"
72+
73+
// CHECK: [[RIGHT_VTABLE_VAR]] = !DIGlobalVariableExpression(var: [[RIGHT_VTABLE:![0-9]*]], expr: !DIExpression())
74+
// CHECK-NEXT: [[RIGHT_VTABLE]] = distinct !DIGlobalVariable(name: "_vtable$", linkageName: "_ZTVN5NSP_26CRightE"
75+
76+
// CHECK: !DIDerivedType(tag: DW_TAG_variable, name: "_vtable$", scope: [[RIGHT:![0-9]*]], file: {{.*}}, baseType: [[TYPE]], flags: DIFlagPrivate | DIFlagArtificial | DIFlagStaticMember)
77+
78+
// CHECK: [[RIGHT]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "CRight"
79+
80+
// CHECK: [[DERIVED_VTABLE_VAR]] = !DIGlobalVariableExpression(var: [[DERIVED_VTABLE:![0-9]*]], expr: !DIExpression())
81+
// CHECK-NEXT: [[DERIVED_VTABLE]] = distinct !DIGlobalVariable(name: "_vtable$", linkageName: "_ZTV8CDerived"
82+
83+
// CHECK: !DIDerivedType(tag: DW_TAG_variable, name: "_vtable$", scope: [[DERIVED:![0-9]*]], file: {{.*}}, baseType: [[TYPE]], flags: DIFlagPrivate | DIFlagArtificial | DIFlagStaticMember)
84+
85+
// CHECK: [[DERIVED]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "CDerived"
86+
87+
// CHECK: !DIDerivedType(tag: DW_TAG_variable, name: "_vtable$", scope: [[BASE]], file: {{.*}}, baseType: [[TYPE]], flags: DIFlagPrivate | DIFlagArtificial | DIFlagStaticMember)

0 commit comments

Comments
 (0)