Skip to content
This repository was archived by the owner on Nov 1, 2021. It is now read-only.

Commit fa210fc

Browse files
committed
Do not register incompatible C++ destructors with __cxa_atexit
Summary: For a static object with a nontrivial destructor, clang generates an initializer function (__cxx_global_var_init) which registers that object's destructor using __cxa_atexit. However some ABIs (ARM, WebAssembly) use destructors that return 'this' instead of having void return (which does not match the signature of function pointers passed to __cxa_atexit). This results in undefined behavior when the destructors are called. All the calling conventions I know of on ARM can tolerate this, but WebAssembly requires the signatures of indirect calls to match the called function. This patch disables that direct registration of destructors for ABIs that have this-returning destructors. Subscribers: aemerson, jfb, cfe-commits, dschuff Differential Revision: http://reviews.llvm.org/D19275 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@269085 91177308-0d34-0410-b5e6-96231b3b80d8
1 parent e0b839e commit fa210fc

File tree

3 files changed

+42
-4
lines changed

3 files changed

+42
-4
lines changed

lib/CodeGen/CGDeclCXX.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,11 +86,15 @@ static void EmitDeclDestroy(CodeGenFunction &CGF, const VarDecl &D,
8686
llvm::Constant *function;
8787
llvm::Constant *argument;
8888

89-
// Special-case non-array C++ destructors, where there's a function
90-
// with the right signature that we can just call.
89+
// Special-case non-array C++ destructors, if they have the right signature
90+
// that can be directly registered with __cxa_atexit. If __cxa_atexit is
91+
// disabled via a flag, a different helper function is generated anyway.
9192
const CXXRecordDecl *record = nullptr;
9293
if (dtorKind == QualType::DK_cxx_destructor &&
93-
(record = type->getAsCXXRecordDecl())) {
94+
(record = type->getAsCXXRecordDecl()) &&
95+
(!CGM.getCXXABI().HasThisReturn(
96+
GlobalDecl(record->getDestructor(), Dtor_Complete)) ||
97+
!CGM.getCodeGenOpts().CXAAtExit)) {
9498
assert(!record->hasTrivialDestructor());
9599
CXXDestructorDecl *dtor = record->getDestructor();
96100

test/CodeGenCXX/runtimecc.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,13 @@ namespace test0 {
2222
A global;
2323
// CHECK-LABEL: define internal void @__cxx_global_var_init()
2424
// CHECK: call [[A]]* @_ZN5test01AC1Ev([[A]]* @_ZN5test06globalE)
25-
// CHECK-NEXT: call i32 @__cxa_atexit(void (i8*)* bitcast ([[A]]* ([[A]]*)* @_ZN5test01AD1Ev to void (i8*)*), i8* bitcast ([[A]]* @_ZN5test06globalE to i8*), i8* @__dso_handle) [[NOUNWIND:#[0-9]+]]
25+
// CHECK-NEXT: call i32 @__cxa_atexit(void (i8*)* @__cxx_global_array_dtor, i8* null, i8* @__dso_handle) [[NOUNWIND:#[0-9]+]]
2626
// CHECK-NEXT: ret void
27+
28+
// CHECK-LABEL: define internal void @__cxx_global_array_dtor(i8*)
29+
// CHECK: call [[A]]* @_ZN5test01AD1Ev([[A]]* @_ZN5test06globalE)
30+
// CHECK-NEXT: ret void
31+
2732
}
2833

2934
// CHECK: declare i32 @__cxa_atexit(void (i8*)*, i8*, i8*) [[NOUNWIND]]

test/CodeGenCXX/static-destructor.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// RUN: %clang_cc1 %s -triple=x86_64-pc-linux -emit-llvm -o - | FileCheck --check-prefix=X86 %s
2+
// RUN: %clang_cc1 %s -triple=wasm32 -emit-llvm -o - | FileCheck --check-prefix=WASM %s
3+
4+
// Test that destructors are not passed directly to __cxa_atexit when their
5+
// signatures do not match the type of its first argument.
6+
// e.g. ARM and WebAssembly have destructors that return this instead of void.
7+
8+
9+
class Foo {
10+
public:
11+
~Foo() {
12+
}
13+
};
14+
15+
Foo global;
16+
17+
// X86 destructors have void return, and are registered directly with __cxa_atexit.
18+
// X86: define internal void @__cxx_global_var_init()
19+
// X86-NEXT: entry:
20+
// X86-NEXT: %0 = call i32 @__cxa_atexit(void (i8*)* bitcast (void (%class.Foo*)* @_ZN3FooD1Ev to void (i8*)*), i8* getelementptr inbounds (%class.Foo, %class.Foo* @global, i32 0, i32 0), i8* @__dso_handle)
21+
22+
// Wasm destructors return this, and use a wrapper function, which is registered
23+
// with __cxa_atexit.
24+
// WASM: define internal void @__cxx_global_var_init()
25+
// WASM-NEXT: entry:
26+
// WASM-NEXT: %0 = call i32 @__cxa_atexit(void (i8*)* @__cxx_global_array_dtor, i8* null, i8* @__dso_handle)
27+
28+
// WASM: define internal void @__cxx_global_array_dtor(i8*)
29+
// WASM: %call = call %class.Foo* @_ZN3FooD1Ev(%class.Foo* @global)

0 commit comments

Comments
 (0)