[PATCH] D19275: Do not register incompatible C++ destructors with __cxa_atexit

Derek Schuff via cfe-commits cfe-commits at lists.llvm.org
Tue Apr 19 11:16:11 PDT 2016


dschuff created this revision.
dschuff added a subscriber: cfe-commits.
Herald added subscribers: dschuff, jfb, aemerson.

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.

http://reviews.llvm.org/D19275

Files:
  lib/CodeGen/CGDeclCXX.cpp
  test/CodeGenCXX/runtimecc.cpp
  test/CodeGenCXX/static-destructor.cpp

Index: test/CodeGenCXX/static-destructor.cpp
===================================================================
--- /dev/null
+++ test/CodeGenCXX/static-destructor.cpp
@@ -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)
Index: test/CodeGenCXX/runtimecc.cpp
===================================================================
--- test/CodeGenCXX/runtimecc.cpp
+++ test/CodeGenCXX/runtimecc.cpp
@@ -22,8 +22,13 @@
   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]]
Index: lib/CodeGen/CGDeclCXX.cpp
===================================================================
--- lib/CodeGen/CGDeclCXX.cpp
+++ lib/CodeGen/CGDeclCXX.cpp
@@ -86,11 +86,15 @@
   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();
 


-------------- next part --------------
A non-text attachment was scrubbed...
Name: D19275.54228.patch
Type: text/x-patch
Size: 3429 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20160419/f26c15bc/attachment.bin>


More information about the cfe-commits mailing list