]> granicus.if.org Git - clang/commitdiff
Do not register incompatible C++ destructors with __cxa_atexit
authorDerek Schuff <dschuff@google.com>
Tue, 10 May 2016 17:44:46 +0000 (17:44 +0000)
committerDerek Schuff <dschuff@google.com>
Tue, 10 May 2016 17:44:46 +0000 (17:44 +0000)
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

lib/CodeGen/CGDeclCXX.cpp
test/CodeGenCXX/runtimecc.cpp
test/CodeGenCXX/static-destructor.cpp [new file with mode: 0644]

index 3dc02dd25b410d9f48bfff0f6d0b73e90cf66e69..47db544004271d253b37ed2e1db86fb1d1d943c8 100644 (file)
@@ -86,11 +86,15 @@ static void EmitDeclDestroy(CodeGenFunction &CGF, const VarDecl &D,
   llvm::Constant *function;
   llvm::Constant *argument;
 
-  // Special-case non-array C++ destructors, where there's a function
-  // with the right signature that we can just call.
+  // Special-case non-array C++ destructors, if they have the right signature
+  // that can be directly registered with __cxa_atexit. If __cxa_atexit is
+  // disabled via a flag, a different helper function is generated anyway.
   const CXXRecordDecl *record = nullptr;
   if (dtorKind == QualType::DK_cxx_destructor &&
-      (record = type->getAsCXXRecordDecl())) {
+      (record = type->getAsCXXRecordDecl()) &&
+      (!CGM.getCXXABI().HasThisReturn(
+          GlobalDecl(record->getDestructor(), Dtor_Complete)) ||
+       !CGM.getCodeGenOpts().CXAAtExit)) {
     assert(!record->hasTrivialDestructor());
     CXXDestructorDecl *dtor = record->getDestructor();
 
index ad6dc85c360d689bf055c1eee01bcd13bac15712..1aac5ffa716596c3617896b116f9a86c0de9fdb0 100644 (file)
@@ -22,8 +22,13 @@ namespace test0 {
   A global;
 // CHECK-LABEL:    define internal void @__cxx_global_var_init()
 // CHECK:      call [[A]]* @_ZN5test01AC1Ev([[A]]* @_ZN5test06globalE)
-// 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]+]]
+// CHECK-NEXT: call i32 @__cxa_atexit(void (i8*)* @__cxx_global_array_dtor, i8* null, i8* @__dso_handle) [[NOUNWIND:#[0-9]+]]
 // CHECK-NEXT: ret void
+
+// CHECK-LABEL: define internal void @__cxx_global_array_dtor(i8*)
+// CHECK: call [[A]]* @_ZN5test01AD1Ev([[A]]* @_ZN5test06globalE)
+// CHECK-NEXT: ret void
+
 }
 
 // CHECK: declare i32 @__cxa_atexit(void (i8*)*, i8*, i8*) [[NOUNWIND]]
diff --git a/test/CodeGenCXX/static-destructor.cpp b/test/CodeGenCXX/static-destructor.cpp
new file mode 100644 (file)
index 0000000..8b18993
--- /dev/null
@@ -0,0 +1,29 @@
+// RUN: %clang_cc1 %s -triple=x86_64-pc-linux -emit-llvm -o - | FileCheck --check-prefix=X86 %s
+// RUN: %clang_cc1 %s -triple=wasm32 -emit-llvm -o - | FileCheck --check-prefix=WASM %s
+
+// Test that destructors are not passed directly to __cxa_atexit when their
+// signatures do not match the type of its first argument.
+// e.g. ARM and WebAssembly have destructors that return this instead of void.
+
+
+class Foo {
+ public:
+  ~Foo() {
+  }
+};
+
+Foo global;
+
+// X86 destructors have void return, and are registered directly with __cxa_atexit.
+// X86: define internal void @__cxx_global_var_init()
+// X86-NEXT: entry:
+// 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)
+
+// Wasm destructors return this, and use a wrapper function, which is registered
+// with __cxa_atexit.
+// WASM: define internal void @__cxx_global_var_init()
+// WASM-NEXT: entry:
+// WASM-NEXT: %0 = call i32 @__cxa_atexit(void (i8*)* @__cxx_global_array_dtor, i8* null, i8* @__dso_handle)
+
+// WASM: define internal void @__cxx_global_array_dtor(i8*)
+// WASM: %call = call %class.Foo* @_ZN3FooD1Ev(%class.Foo* @global)