From 941cb2aa5722c2657e009a77545c1ccdef5132d7 Mon Sep 17 00:00:00 2001 From: Akira Hatanaka Date: Tue, 17 Apr 2018 18:41:52 +0000 Subject: [PATCH] Add a command line option 'fregister_global_dtors_with_atexit' to register destructor functions annotated with __attribute__((destructor)) using __cxa_atexit or atexit. Register destructor functions annotated with __attribute__((destructor)) calling __cxa_atexit in a synthesized constructor function instead of emitting references to the functions in a special section. The primary reason for adding this option is that we are planning to deprecate the __mod_term_funcs section on Darwin in the future. This feature is enabled by default only on Darwin. Users who do not want this can use command line option 'fno_register_global_dtors_with_atexit' to disable it. rdar://problem/33887655 Differential Revision: https://reviews.llvm.org/D45578 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@330199 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Driver/Options.td | 4 ++ include/clang/Frontend/CodeGenOptions.def | 1 + lib/CodeGen/CGDeclCXX.cpp | 3 ++ lib/CodeGen/CodeGenFunction.h | 3 ++ lib/CodeGen/CodeGenModule.cpp | 6 +++ lib/CodeGen/CodeGenModule.h | 6 +++ lib/CodeGen/ItaniumCXXABI.cpp | 50 ++++++++++++++++++++ lib/Driver/ToolChains/Clang.cpp | 5 ++ lib/Frontend/CompilerInvocation.cpp | 2 + test/CodeGen/constructor-attribute.c | 56 +++++++++++++++++++++-- test/Driver/cxa-atexit.cpp | 15 ++++++ test/Driver/rewrite-legacy-objc.m | 6 +-- test/Driver/rewrite-objc.m | 2 +- 13 files changed, 150 insertions(+), 9 deletions(-) diff --git a/include/clang/Driver/Options.td b/include/clang/Driver/Options.td index 5a3a7442ac..09f759e996 100644 --- a/include/clang/Driver/Options.td +++ b/include/clang/Driver/Options.td @@ -1364,6 +1364,8 @@ def fno_threadsafe_statics : Flag<["-"], "fno-threadsafe-statics">, Group, HelpText<"Do not emit code to make initialization of local statics thread safe">; def fno_use_cxa_atexit : Flag<["-"], "fno-use-cxa-atexit">, Group, Flags<[CC1Option]>, HelpText<"Don't use __cxa_atexit for calling destructors">; +def fno_register_global_dtors_with_atexit : Flag<["-"], "fno-register-global-dtors-with-atexit">, Group, + HelpText<"Don't use atexit or __cxa_atexit to register global destructors">; def fno_use_init_array : Flag<["-"], "fno-use-init-array">, Group, Flags<[CC1Option]>, HelpText<"Don't use .init_array instead of .ctors">; def fno_unit_at_a_time : Flag<["-"], "fno-unit-at-a-time">, Group; @@ -1612,6 +1614,8 @@ def funsigned_char : Flag<["-"], "funsigned-char">, Group; def fno_unsigned_char : Flag<["-"], "fno-unsigned-char">; def funwind_tables : Flag<["-"], "funwind-tables">, Group; def fuse_cxa_atexit : Flag<["-"], "fuse-cxa-atexit">, Group; +def fregister_global_dtors_with_atexit : Flag<["-"], "fregister-global-dtors-with-atexit">, Group, Flags<[CC1Option]>, + HelpText<"Use atexit or __cxa_atexit to register global destructors">; def fuse_init_array : Flag<["-"], "fuse-init-array">, Group, Flags<[CC1Option]>, HelpText<"Use .init_array instead of .ctors">; def fno_var_tracking : Flag<["-"], "fno-var-tracking">, Group; diff --git a/include/clang/Frontend/CodeGenOptions.def b/include/clang/Frontend/CodeGenOptions.def index adcbe36fb4..8d710b0c6f 100644 --- a/include/clang/Frontend/CodeGenOptions.def +++ b/include/clang/Frontend/CodeGenOptions.def @@ -43,6 +43,7 @@ CODEGENOPT(CoverageExtraChecksum, 1, 0) ///< Whether we need a second checksum f CODEGENOPT(CoverageNoFunctionNamesInData, 1, 0) ///< Do not include function names in GCDA files. CODEGENOPT(CoverageExitBlockBeforeBody, 1, 0) ///< Whether to emit the exit block before the body blocks in GCNO files. CODEGENOPT(CXAAtExit , 1, 1) ///< Use __cxa_atexit for calling destructors. +CODEGENOPT(RegisterGlobalDtorsWithAtExit, 1, 1) ///< Use atexit or __cxa_atexit to register global destructors. CODEGENOPT(CXXCtorDtorAliases, 1, 0) ///< Emit complete ctors/dtors as linker ///< aliases to base ctors when possible. CODEGENOPT(DataSections , 1, 0) ///< Set when -fdata-sections is enabled. diff --git a/lib/CodeGen/CGDeclCXX.cpp b/lib/CodeGen/CGDeclCXX.cpp index e47a65ee71..5e237d7e0b 100644 --- a/lib/CodeGen/CGDeclCXX.cpp +++ b/lib/CodeGen/CGDeclCXX.cpp @@ -236,7 +236,10 @@ void CodeGenFunction::registerGlobalDtorWithAtExit(const VarDecl &VD, llvm::Constant *addr) { // Create a function which calls the destructor. llvm::Constant *dtorStub = createAtExitStub(VD, dtor, addr); + registerGlobalDtorWithAtExit(dtorStub); +} +void CodeGenFunction::registerGlobalDtorWithAtExit(llvm::Constant *dtorStub) { // extern "C" int atexit(void (*f)(void)); llvm::FunctionType *atexitTy = llvm::FunctionType::get(IntTy, dtorStub->getType(), false); diff --git a/lib/CodeGen/CodeGenFunction.h b/lib/CodeGen/CodeGenFunction.h index 27b0677d49..84118a50c1 100644 --- a/lib/CodeGen/CodeGenFunction.h +++ b/lib/CodeGen/CodeGenFunction.h @@ -3696,6 +3696,9 @@ public: void registerGlobalDtorWithAtExit(const VarDecl &D, llvm::Constant *fn, llvm::Constant *addr); + /// Call atexit() with function dtorStub. + void registerGlobalDtorWithAtExit(llvm::Constant *dtorStub); + /// Emit code in this function to perform a guarded variable /// initialization. Guarded initializations are used when it's not /// possible to prove that an initialization will be done exactly diff --git a/lib/CodeGen/CodeGenModule.cpp b/lib/CodeGen/CodeGenModule.cpp index 42a8cd800f..a21527abba 100644 --- a/lib/CodeGen/CodeGenModule.cpp +++ b/lib/CodeGen/CodeGenModule.cpp @@ -397,6 +397,7 @@ void CodeGenModule::Release() { emitMultiVersionFunctions(); EmitCXXGlobalInitFunc(); EmitCXXGlobalDtorFunc(); + registerGlobalDtorsWithAtExit(); EmitCXXThreadLocalInitFunc(); if (ObjCRuntime) if (llvm::Function *ObjCInitFunction = ObjCRuntime->ModuleInitFunction()) @@ -1026,6 +1027,11 @@ void CodeGenModule::AddGlobalCtor(llvm::Function *Ctor, int Priority, /// AddGlobalDtor - Add a function to the list that will be called /// when the module is unloaded. void CodeGenModule::AddGlobalDtor(llvm::Function *Dtor, int Priority) { + if (CodeGenOpts.RegisterGlobalDtorsWithAtExit) { + DtorsUsingAtExit[Priority].push_back(Dtor); + return; + } + // FIXME: Type coercion of void()* types. GlobalDtors.push_back(Structor(Priority, Dtor, nullptr)); } diff --git a/lib/CodeGen/CodeGenModule.h b/lib/CodeGen/CodeGenModule.h index 5daa4e989d..76af56b56c 100644 --- a/lib/CodeGen/CodeGenModule.h +++ b/lib/CodeGen/CodeGenModule.h @@ -1336,6 +1336,12 @@ private: void checkAliases(); + std::map> DtorsUsingAtExit; + + /// Register functions annotated with __attribute__((destructor)) using + /// __cxa_atexit, if it is available, or atexit otherwise. + void registerGlobalDtorsWithAtExit(); + void emitMultiVersionFunctions(); /// Emit any vtables which we deferred and still have a use for. diff --git a/lib/CodeGen/ItaniumCXXABI.cpp b/lib/CodeGen/ItaniumCXXABI.cpp index 68274ea13a..f92d7ec063 100644 --- a/lib/CodeGen/ItaniumCXXABI.cpp +++ b/lib/CodeGen/ItaniumCXXABI.cpp @@ -34,6 +34,7 @@ #include "llvm/IR/Instructions.h" #include "llvm/IR/Intrinsics.h" #include "llvm/IR/Value.h" +#include "llvm/Support/ScopedPrinter.h" using namespace clang; using namespace CodeGen; @@ -2195,6 +2196,13 @@ static void emitGlobalDtorWithCXAAtExit(CodeGenFunction &CGF, auto *GV = cast(handle->stripPointerCasts()); GV->setVisibility(llvm::GlobalValue::HiddenVisibility); + if (!addr) + // addr is null when we are trying to register a dtor annotated with + // __attribute__((destructor)) in a constructor function. Using null here is + // okay because this argument is just passed back to the destructor + // function. + addr = llvm::Constant::getNullValue(CGF.Int8PtrTy); + llvm::Value *args[] = { llvm::ConstantExpr::getBitCast(dtor, dtorTy), llvm::ConstantExpr::getBitCast(addr, CGF.Int8PtrTy), @@ -2203,6 +2211,48 @@ static void emitGlobalDtorWithCXAAtExit(CodeGenFunction &CGF, CGF.EmitNounwindRuntimeCall(atexit, args); } +void CodeGenModule::registerGlobalDtorsWithAtExit() { + for (const auto I : DtorsUsingAtExit) { + int Priority = I.first; + const llvm::TinyPtrVector &Dtors = I.second; + + // Create a function that registers destructors that have the same priority. + // + // Since constructor functions are run in non-descending order of their + // priorities, destructors are registered in non-descending order of their + // priorities, and since destructor functions are run in the reverse order + // of their registration, destructor functions are run in non-ascending + // order of their priorities. + CodeGenFunction CGF(*this); + std::string GlobalInitFnName = + std::string("__GLOBAL_init_") + llvm::to_string(Priority); + llvm::FunctionType *FTy = llvm::FunctionType::get(VoidTy, false); + llvm::Function *GlobalInitFn = CreateGlobalInitOrDestructFunction( + FTy, GlobalInitFnName, getTypes().arrangeNullaryFunction(), + SourceLocation()); + ASTContext &Ctx = getContext(); + FunctionDecl *FD = FunctionDecl::Create( + Ctx, Ctx.getTranslationUnitDecl(), SourceLocation(), SourceLocation(), + &Ctx.Idents.get(GlobalInitFnName), Ctx.VoidTy, nullptr, SC_Static, + false, false); + CGF.StartFunction(GlobalDecl(FD), getContext().VoidTy, GlobalInitFn, + getTypes().arrangeNullaryFunction(), FunctionArgList(), + SourceLocation(), SourceLocation()); + + for (auto *Dtor : Dtors) { + // Register the destructor function calling __cxa_atexit if it is + // available. Otherwise fall back on calling atexit. + if (getCodeGenOpts().CXAAtExit) + emitGlobalDtorWithCXAAtExit(CGF, Dtor, nullptr, false); + else + CGF.registerGlobalDtorWithAtExit(Dtor); + } + + CGF.FinishFunction(); + AddGlobalCtor(GlobalInitFn, Priority, nullptr); + } +} + /// Register a global destructor as best as we know how. void ItaniumCXXABI::registerGlobalDtor(CodeGenFunction &CGF, const VarDecl &D, diff --git a/lib/Driver/ToolChains/Clang.cpp b/lib/Driver/ToolChains/Clang.cpp index 49a0c4cdc1..f4e87fa1f4 100644 --- a/lib/Driver/ToolChains/Clang.cpp +++ b/lib/Driver/ToolChains/Clang.cpp @@ -4179,6 +4179,11 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, KernelOrKext) CmdArgs.push_back("-fno-use-cxa-atexit"); + if (Args.hasFlag(options::OPT_fregister_global_dtors_with_atexit, + options::OPT_fno_register_global_dtors_with_atexit, + RawTriple.isOSDarwin())) + CmdArgs.push_back("-fregister-global-dtors-with-atexit"); + // -fms-extensions=0 is default. if (Args.hasFlag(options::OPT_fms_extensions, options::OPT_fno_ms_extensions, IsWindowsMSVC)) diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 3ff5fcd14a..469497c485 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -662,6 +662,8 @@ static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK, Opts.AssumeSaneOperatorNew = !Args.hasArg(OPT_fno_assume_sane_operator_new); Opts.ObjCAutoRefCountExceptions = Args.hasArg(OPT_fobjc_arc_exceptions); Opts.CXAAtExit = !Args.hasArg(OPT_fno_use_cxa_atexit); + Opts.RegisterGlobalDtorsWithAtExit = + Args.hasArg(OPT_fregister_global_dtors_with_atexit); Opts.CXXCtorDtorAliases = Args.hasArg(OPT_mconstructor_aliases); Opts.CodeModel = getCodeModel(Args, Diags); Opts.DebugPass = Args.getLastArgValue(OPT_mdebug_pass); diff --git a/test/CodeGen/constructor-attribute.c b/test/CodeGen/constructor-attribute.c index a1f0e604d4..562e57b8b1 100644 --- a/test/CodeGen/constructor-attribute.c +++ b/test/CodeGen/constructor-attribute.c @@ -1,8 +1,39 @@ -// RUN: %clang_cc1 -emit-llvm -o %t %s -// RUN: grep -e "global_ctors.*@A" %t -// RUN: grep -e "global_dtors.*@B" %t -// RUN: grep -e "global_ctors.*@C" %t -// RUN: grep -e "global_dtors.*@D" %t +// RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=WITHOUTATEXIT %s +// RUN: %clang_cc1 -triple x86_64-apple-darwin -fregister-global-dtors-with-atexit -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=CXAATEXIT --check-prefix=WITHATEXIT %s +// RUN: %clang_cc1 -triple x86_64-apple-darwin -fno-use-cxa-atexit -fregister-global-dtors-with-atexit -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=ATEXIT --check-prefix=WITHATEXIT %s + +// WITHOUTATEXIT: global_ctors{{.*}}@A{{.*}}@C +// WITHOUTATEXIT: @llvm.global_dtors = appending global [5 x { i32, void ()*, i8* }]{{.*}}@B{{.*}}@E{{.*}}@F{{.*}}@G{{.*}}@D +// WITHATEXIT: @llvm.global_ctors = appending global [5 x { i32, void ()*, i8* }]{{.*}}i32 65535, void ()* @A,{{.*}}i32 65535, void ()* @C,{{.*}}i32 123, void ()* @__GLOBAL_init_123,{{.*}}i32 789, void ()* @[[GLOBAL_INIT_789:__GLOBAL_init_789.[0-9]+]],{{.*}}i32 65535, void ()* @__GLOBAL_init_65535, +// WITHATEXIT-NOT: global_dtors + +// CHECK: define void @A() +// CHECK: define void @B() +// CHECK: define internal void @E() +// CHECK: define internal void @F() +// CHECK: define internal void @G() +// CHECK: define i32 @__GLOBAL_init_789(i32 %{{.*}}) +// CHECK: define internal void @C() +// CHECK: define internal void @D() +// CHECK: define i32 @main() +// CHECK: define internal i32 @foo() +// WITHOUTATEXIT-NOT: define + +// WITHATEXIT: define internal void @__GLOBAL_init_123(){{.*}}section "__TEXT,__StaticInit,regular,pure_instructions" +// CXAATEXIT: call i32 @__cxa_atexit(void (i8*)* bitcast (void ()* @E to void (i8*)*), i8* null, i8* @__dso_handle) +// CXAATEXIT: call i32 @__cxa_atexit(void (i8*)* bitcast (void ()* @G to void (i8*)*), i8* null, i8* @__dso_handle) +// ATEXIT: call i32 @atexit(void ()* @E) +// ATEXIT: call i32 @atexit(void ()* @G) + +// WITHATEXIT: define internal void @[[GLOBAL_INIT_789]](){{.*}}section "__TEXT,__StaticInit,regular,pure_instructions" +// CXAATEXIT: call i32 @__cxa_atexit(void (i8*)* bitcast (void ()* @F to void (i8*)*), i8* null, i8* @__dso_handle) +// ATEXIT: call i32 @atexit(void ()* @F) + +// WITHATEXIT: define internal void @__GLOBAL_init_65535(){{.*}}section "__TEXT,__StaticInit,regular,pure_instructions" +// CXAATEXIT: call i32 @__cxa_atexit(void (i8*)* bitcast (void ()* @B to void (i8*)*), i8* null, i8* @__dso_handle) +// CXAATEXIT: call i32 @__cxa_atexit(void (i8*)* bitcast (void ()* @D to void (i8*)*), i8* null, i8* @__dso_handle) +// ATEXIT: call i32 @atexit(void ()* @B) +// ATEXIT: call i32 @atexit(void ()* @D) int printf(const char *, ...); @@ -21,6 +52,21 @@ static void C() __attribute__((constructor)); static void D() __attribute__((destructor)); +static __attribute__((destructor(123))) void E() { +} + +static __attribute__((destructor(789))) void F() { +} + +static __attribute__((destructor(123))) void G() { +} + +// Test that this function doesn't collide with the synthesized constructor +// function for destructors with priority 789. +int __GLOBAL_init_789(int a) { + return a * a; +} + static int foo() { return 10; } diff --git a/test/Driver/cxa-atexit.cpp b/test/Driver/cxa-atexit.cpp index 01d4e20e77..56d2552023 100644 --- a/test/Driver/cxa-atexit.cpp +++ b/test/Driver/cxa-atexit.cpp @@ -25,3 +25,18 @@ // CHECK-MTI: "-fno-use-cxa-atexit" // CHECK-MIPS-NOT: "-fno-use-cxa-atexit" +// RUN: %clang -target x86_64-apple-darwin -fregister-global-dtors-with-atexit -fno-register-global-dtors-with-atexit -c -### %s 2>&1 | \ +// RUN: FileCheck --check-prefix=WITHOUTATEXIT %s +// RUN: %clang -target x86_64-apple-darwin -fno-register-global-dtors-with-atexit -fregister-global-dtors-with-atexit -c -### %s 2>&1 | \ +// RUN: FileCheck --check-prefix=WITHATEXIT %s +// RUN: %clang -target x86_64-apple-darwin -c -### %s 2>&1 | \ +// RUN: FileCheck --check-prefix=WITHATEXIT %s +// RUN: %clang -target x86_64-pc-linux-gnu -fregister-global-dtors-with-atexit -fno-register-global-dtors-with-atexit -c -### %s 2>&1 | \ +// RUN: FileCheck --check-prefix=WITHOUTATEXIT %s +// RUN: %clang -target x86_64-pc-linux-gnu -fno-register-global-dtors-with-atexit -fregister-global-dtors-with-atexit -c -### %s 2>&1 | \ +// RUN: FileCheck --check-prefix=WITHATEXIT %s +// RUN: %clang -target x86_64-pc-linux-gnu -c -### %s 2>&1 | \ +// RUN: FileCheck --check-prefix=WITHOUTATEXIT %s + +// WITHATEXIT: -fregister-global-dtors-with-atexit +// WITHOUTATEXIT-NOT: -fregister-global-dtors-with-atexit diff --git a/test/Driver/rewrite-legacy-objc.m b/test/Driver/rewrite-legacy-objc.m index 8ac357c844..37b829e5e5 100644 --- a/test/Driver/rewrite-legacy-objc.m +++ b/test/Driver/rewrite-legacy-objc.m @@ -3,11 +3,11 @@ // TEST0: clang{{.*}}" "-cc1" // TEST0: "-rewrite-objc" // FIXME: CHECK-NOT is broken somehow, it doesn't work here. Check adjacency instead. -// TEST0: "-fmessage-length" "0" "-stack-protector" "1" "-fblocks" "-fencode-extended-block-signature" "-fobjc-runtime=macosx-fragile" "-fno-objc-infer-related-result-type" "-fobjc-exceptions" "-fexceptions" "-fmax-type-align=16" "-fdiagnostics-show-option" +// TEST0: "-fmessage-length" "0" "-stack-protector" "1" "-fblocks" "-fencode-extended-block-signature" "-fregister-global-dtors-with-atexit" "-fobjc-runtime=macosx-fragile" "-fno-objc-infer-related-result-type" "-fobjc-exceptions" "-fexceptions" "-fmax-type-align=16" "-fdiagnostics-show-option" // TEST0: rewrite-legacy-objc.m" // RUN: %clang -no-canonical-prefixes -target i386-apple-macosx10.9.0 -rewrite-legacy-objc %s -o - -### 2>&1 | \ // RUN: FileCheck -check-prefix=TEST1 %s // RUN: %clang -no-canonical-prefixes -target i386-apple-macosx10.6.0 -rewrite-legacy-objc %s -o - -### 2>&1 | \ // RUN: FileCheck -check-prefix=TEST2 %s -// TEST1: "-fmessage-length" "0" "-stack-protector" "1" "-fblocks" "-fencode-extended-block-signature" "-fobjc-runtime=macosx-fragile" "-fobjc-subscripting-legacy-runtime" "-fno-objc-infer-related-result-type" "-fobjc-exceptions" "-fmax-type-align=16" "-fdiagnostics-show-option" -// TEST2: "-fmessage-length" "0" "-stack-protector" "1" "-fblocks" "-fencode-extended-block-signature" "-fobjc-runtime=macosx-fragile" "-fno-objc-infer-related-result-type" "-fobjc-exceptions" "-fmax-type-align=16" "-fdiagnostics-show-option" +// TEST1: "-fmessage-length" "0" "-stack-protector" "1" "-fblocks" "-fencode-extended-block-signature" "-fregister-global-dtors-with-atexit" "-fobjc-runtime=macosx-fragile" "-fobjc-subscripting-legacy-runtime" "-fno-objc-infer-related-result-type" "-fobjc-exceptions" "-fmax-type-align=16" "-fdiagnostics-show-option" +// TEST2: "-fmessage-length" "0" "-stack-protector" "1" "-fblocks" "-fencode-extended-block-signature" "-fregister-global-dtors-with-atexit" "-fobjc-runtime=macosx-fragile" "-fno-objc-infer-related-result-type" "-fobjc-exceptions" "-fmax-type-align=16" "-fdiagnostics-show-option" diff --git a/test/Driver/rewrite-objc.m b/test/Driver/rewrite-objc.m index b8a1ffe8b8..1c6dbcc329 100644 --- a/test/Driver/rewrite-objc.m +++ b/test/Driver/rewrite-objc.m @@ -3,4 +3,4 @@ // TEST0: clang{{.*}}" "-cc1" // TEST0: "-rewrite-objc" // FIXME: CHECK-NOT is broken somehow, it doesn't work here. Check adjacency instead. -// TEST0: "-fmessage-length" "0" "-stack-protector" "1" "-fblocks" "-fencode-extended-block-signature" "-fobjc-runtime=macosx" "-fno-objc-infer-related-result-type" "-fobjc-exceptions" "-fexceptions" "-fmax-type-align=16" "-fdiagnostics-show-option" +// TEST0: "-fmessage-length" "0" "-stack-protector" "1" "-fblocks" "-fencode-extended-block-signature" "-fregister-global-dtors-with-atexit" "-fobjc-runtime=macosx" "-fno-objc-infer-related-result-type" "-fobjc-exceptions" "-fexceptions" "-fmax-type-align=16" "-fdiagnostics-show-option" -- 2.40.0