-
Notifications
You must be signed in to change notification settings - Fork 15.2k
[clang][Fuchsia] Have global dtors use llvm.global_dtors over atexit #115788
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
@llvm/pr-subscribers-clang-codegen @llvm/pr-subscribers-clang Author: None (PiJoules) ChangesFull diff: https://github.com/llvm/llvm-project/pull/115788.diff 2 Files Affected:
diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp
index 9b3c2f1b2af677..7dda27e02e9ea1 100644
--- a/clang/lib/CodeGen/ItaniumCXXABI.cpp
+++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp
@@ -518,6 +518,14 @@ class FuchsiaCXXABI final : public ItaniumCXXABI {
explicit FuchsiaCXXABI(CodeGen::CodeGenModule &CGM)
: ItaniumCXXABI(CGM) {}
+ // Rather than using the defaul [__cxa_]atexit, instead use llvm.global_dtors
+ // which will result in .fini_array being used.
+ void registerGlobalDtor(CodeGenFunction &CGF, const VarDecl &D,
+ llvm::FunctionCallee dtor,
+ llvm::Constant *addr) override {
+ return CGM.AddCXXDtorEntry(dtor, addr);
+ }
+
private:
bool constructorsAndDestructorsReturnThis() const override { return true; }
};
diff --git a/clang/test/CodeGenCXX/fuchsia-global-dtor.cpp b/clang/test/CodeGenCXX/fuchsia-global-dtor.cpp
new file mode 100644
index 00000000000000..ff3066059af91d
--- /dev/null
+++ b/clang/test/CodeGenCXX/fuchsia-global-dtor.cpp
@@ -0,0 +1,25 @@
+/// Global destructors targetting Fuchsia should not use [__cxa_]atexit. Instead
+/// they should be invoked through llvm.global_dtors.
+
+// RUN: %clang_cc1 %s -triple aarch64-unknown-fuchsia -emit-llvm -o - | FileCheck %s
+
+// CHECK-NOT: atexit
+
+// CHECK: @llvm.global_dtors = appending global [1 x { i32, ptr, ptr }]
+// CHECK-SAME: [{ i32, ptr, ptr } { i32 {{.*}}, ptr [[MODULE_DTOR:@.*]], ptr {{.*}} }]
+
+// CHECK: define internal void [[MODULE_DTOR]]() {{.*}}{
+// CHECK-NEXT: entry:
+// CHECK-NEXT: %0 = call ptr @_ZN1AD1Ev(ptr @DestroyFirst)
+// CHECK-NEXT: %1 = call ptr @_ZN1AD1Ev(ptr @DestroySecond)
+// CHECK-NEXT: %2 = call ptr @_ZN1AD1Ev(ptr @DestroyThird)
+// CHECK-NEXT: ret void
+// CHECK-NEXT: }
+
+struct A {
+ ~A() {}
+};
+
+A DestroyThird;
+A DestroySecond;
+A DestroyFirst;
|
efriedma-quic
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure we can just unconditionally call the destructor. We need to ensure the constructor runs first. If a constructor throws an exception or calls exit(), not all constructors will run before we start cleaning up. This works out naturally with atexit... but if you use global_dtors, I think you end up calling the destructor on an uninitialized object.
|
Also, I'm not sure objects are destroyed in the correct order. As far as I know, fini_array existed at the time the Itanium ABI was written; I assume it intentionally was not used because there were issues ensuring the correct objects are destroyed in the correct order. |
Ah yeah you're right. I didn't consider this. Since we're willing to make ABI changes, my colleague @mysterymath came up with another idea that might help address this:
An alternative approach might be using a lookup table with a decrementing pointer: With either of these, I think we guarantee that we only call the dtors of globals that were successfully constructed even if a ctor calls exit or throws an exception.
Do you mean for the test case I added? I think they should be destroyed in reverse order. |
|
To ensure we don't destroy objects that weren't constructed, you can use a guard variable of some sort, sure. I'm not quite sure what the goal is, here; are you trying to improve performance, or are you worried about the function pointers for security? No, I mean for objects in different translation units. I think the same translation unit works like you think. And different translation units maybe works for non-inline variables, assuming the loader iterates in the right direction? I think you might run into issues with inline variables? Not sure if this patch actually changes behavior for them. |
|
(Sorry I think I accidentally edited your last comment rather than adding a new one)
Fewer atexit calls means fewer calls to malloc since the number of callbacks can dynamically grow.
Good points, I'll have to write more tests for this |
The scenario that brought this up was With an approach like this, such constructors wouldn't need to be generated; the little state machine in the module constructor function would take it as granted that such "constructors" executed successfully. We'd probably need some additional mechanism in Clang's data structures to keep track of the relationship between these trivial constructors and their destructors though. |
No description provided.