diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 0ebab141b187d..0b660e3daaf81 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -4610,12 +4610,6 @@ void CodeGenModule::emitMultiVersionFunctions() { } llvm::Function *ResolverFunc = cast(ResolverConstant); - ResolverFunc->setLinkage(getMultiversionLinkage(*this, GD)); - - if (!ResolverFunc->hasLocalLinkage() && supportsCOMDAT()) - ResolverFunc->setComdat( - getModule().getOrInsertComdat(ResolverFunc->getName())); - const TargetInfo &TI = getTarget(); llvm::stable_sort( Options, [&TI](const CodeGenFunction::FMVResolverOption &LHS, @@ -4624,6 +4618,11 @@ void CodeGenModule::emitMultiVersionFunctions() { }); CodeGenFunction CGF(*this); CGF.EmitMultiVersionResolver(ResolverFunc, Options); + + setMultiVersionResolverAttributes(ResolverFunc, GD); + if (!ResolverFunc->hasLocalLinkage() && supportsCOMDAT()) + ResolverFunc->setComdat( + getModule().getOrInsertComdat(ResolverFunc->getName())); } // Ensure that any additions to the deferred decls list caused by emitting a @@ -4674,7 +4673,7 @@ void CodeGenModule::emitCPUDispatchDefinition(GlobalDecl GD) { auto *ResolverFunc = cast(GetOrCreateLLVMFunction( ResolverName, ResolverType, ResolverGD, /*ForVTable=*/false)); - ResolverFunc->setLinkage(getMultiversionLinkage(*this, GD)); + if (supportsCOMDAT()) ResolverFunc->setComdat( getModule().getOrInsertComdat(ResolverFunc->getName())); @@ -4740,6 +4739,7 @@ void CodeGenModule::emitCPUDispatchDefinition(GlobalDecl GD) { CodeGenFunction CGF(*this); CGF.EmitMultiVersionResolver(ResolverFunc, Options); + setMultiVersionResolverAttributes(ResolverFunc, GD); if (getTarget().supportsIFunc()) { llvm::GlobalValue::LinkageTypes Linkage = getMultiversionLinkage(*this, GD); @@ -4858,6 +4858,26 @@ llvm::Constant *CodeGenModule::GetOrCreateMultiVersionResolver(GlobalDecl GD) { return Resolver; } +void CodeGenModule::setMultiVersionResolverAttributes(llvm::Function *Resolver, + GlobalDecl GD) { + const NamedDecl *D = dyn_cast_or_null(GD.getDecl()); + Resolver->setLinkage(getMultiversionLinkage(*this, GD)); + + // Function body has to be emitted before calling setGlobalVisibility + // for Resolver to be considered as definition. + setGlobalVisibility(Resolver, D); + + setDSOLocal(Resolver); + + // Set the default target-specific attributes, such as PAC and BTI ones on + // AArch64. Not passing Decl to prevent setting unrelated attributes, + // as Resolver can be shared by multiple declarations. + // FIXME Some targets may require a non-null D to set some attributes + // (such as "stackrealign" on X86, even when it is requested via + // "-mstackrealign" command line option). + getTargetCodeGenInfo().setTargetAttributes(/*D=*/nullptr, Resolver, *this); +} + bool CodeGenModule::shouldDropDLLAttribute(const Decl *D, const llvm::GlobalValue *GV) const { auto SC = GV->getDLLStorageClass(); diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h index 8b1ac2d976c5e..3971b296b3f80 100644 --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -1851,6 +1851,15 @@ class CodeGenModule : public CodeGenTypeCache { // that feature and for a regular function (llvm::GlobalValue) otherwise. llvm::Constant *GetOrCreateMultiVersionResolver(GlobalDecl GD); + // Set attributes to a resolver function generated by Clang. + // GD is either the cpu_dispatch declaration or an arbitrarily chosen + // function declaration that triggered the implicit generation of this + // resolver function. + // + /// NOTE: This should only be called for definitions. + void setMultiVersionResolverAttributes(llvm::Function *Resolver, + GlobalDecl GD); + // In scenarios where a function is not known to be a multiversion function // until a later declaration, it is sometimes necessary to change the // previously created mangled name to align with requirements of whatever diff --git a/clang/test/CodeGen/AArch64/ptrauth-fmv.c b/clang/test/CodeGen/AArch64/ptrauth-fmv.c new file mode 100644 index 0000000000000..3b60ea7412f1b --- /dev/null +++ b/clang/test/CodeGen/AArch64/ptrauth-fmv.c @@ -0,0 +1,42 @@ +// RUN: %clang_cc1 -triple aarch64-linux-gnu -mbranch-target-enforce -msign-return-address=all -emit-llvm %s -o - | FileCheck --check-prefixes=CHECK,BTI-SIGNRA %s +// RUN: %clang_cc1 -triple arm64-apple-ios -mbranch-target-enforce -msign-return-address=all -emit-llvm %s -o - | FileCheck --check-prefixes=CHECK,BTI-SIGNRA %s +// RUN: %clang_cc1 -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-returns -fptrauth-auth-traps -emit-llvm %s -o - | FileCheck --check-prefixes=CHECK,PAUTHTEST %s +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-returns -fptrauth-auth-traps -emit-llvm %s -o - | FileCheck --check-prefixes=CHECK,PAUTHTEST %s + +// Check that both multi-versioned functions themselves and corresponding +// resolvers generated by Clang have the correct PAC/BTI attributes. + +int __attribute__((target_clones("crc", "default"))) global_target_clones(void) { return 0; } + +int __attribute__((target_version("crc"))) global_target_version(void) { return 0; } +int __attribute__((target_version("default"))) global_target_version(void) { return 0; } + +static int __attribute__((target_clones("crc", "default"))) static_target_clones(void) { return 0; } + +static int __attribute__((target_version("crc"))) static_target_version(void) { return 0; } +static int __attribute__((target_version("default"))) static_target_version(void) { return 0; } + +// Force emission of static_* functions. +void *get_ptr1(void) { return static_target_clones; } +void *get_ptr2(void) { return static_target_version; } + +// CHECK-DAG: define{{( dso_local)?}} i32 @global_target_clones._Mcrc() #[[ATTR_CRC:[0-9]+]] +// CHECK-DAG: define{{( dso_local)?}} i32 @global_target_clones.default() #[[ATTR_DEFAULT:[0-9]+]] +// CHECK-DAG: define weak_odr ptr @global_target_clones.resolver() #[[ATTR_RESOLVER:[0-9]+]] +// CHECK-DAG: define{{( dso_local)?}} i32 @global_target_version._Mcrc() #[[ATTR_CRC]] +// CHECK-DAG: define{{( dso_local)?}} i32 @global_target_version.default() #[[ATTR_DEFAULT]] +// CHECK-DAG: define weak_odr ptr @global_target_version.resolver() #[[ATTR_RESOLVER]] + +// CHECK-DAG: define internal i32 @static_target_clones._Mcrc() #[[ATTR_CRC:[0-9]+]] +// CHECK-DAG: define internal i32 @static_target_clones.default() #[[ATTR_DEFAULT:[0-9]+]] +// CHECK-DAG: define internal ptr @static_target_clones.resolver() #[[ATTR_RESOLVER:[0-9]+]] +// CHECK-DAG: define internal i32 @static_target_version._Mcrc() #[[ATTR_CRC]] +// CHECK-DAG: define internal i32 @static_target_version.default() #[[ATTR_DEFAULT]] +// CHECK-DAG: define internal ptr @static_target_version.resolver() #[[ATTR_RESOLVER]] + +// BTI-SIGNRA-DAG: attributes #[[ATTR_CRC]] = { {{.*}}"branch-target-enforcement" {{.*}}"sign-return-address"="all" "sign-return-address-key"="a_key"{{.*}} } +// BTI-SIGNRA-DAG: attributes #[[ATTR_RESOLVER]] = { {{.*}}"branch-target-enforcement" {{.*}}"sign-return-address"="all" "sign-return-address-key"="a_key"{{.*}} } +// BTI-SIGNRA-DAG: attributes #[[ATTR_DEFAULT]] = { {{.*}}"branch-target-enforcement" {{.*}}"sign-return-address"="all" "sign-return-address-key"="a_key"{{.*}} } +// PAUTHTEST-DAG: attributes #[[ATTR_CRC]] = { {{.*}}"ptrauth-auth-traps" "ptrauth-calls" "ptrauth-returns"{{.*}} } +// PAUTHTEST-DAG: attributes #[[ATTR_RESOLVER]] = { {{.*}}"ptrauth-auth-traps" "ptrauth-calls" "ptrauth-returns"{{.*}} } +// PAUTHTEST-DAG: attributes #[[ATTR_DEFAULT]] = { {{.*}}"ptrauth-auth-traps" "ptrauth-calls" "ptrauth-returns"{{.*}} } diff --git a/clang/test/CodeGen/AArch64/resolver-attributes.c b/clang/test/CodeGen/AArch64/resolver-attributes.c new file mode 100644 index 0000000000000..6e4497cdc8611 --- /dev/null +++ b/clang/test/CodeGen/AArch64/resolver-attributes.c @@ -0,0 +1,62 @@ +// RUN: %clang_cc1 -triple aarch64-linux-gnu -mbranch-target-enforce -emit-llvm %s -o - | FileCheck --check-prefixes=BTI,ELF %s +// RUN: %clang_cc1 -triple arm64-apple-ios -mbranch-target-enforce -emit-llvm %s -o - | FileCheck --check-prefixes=BTI %s +// RUN: %clang_cc1 -triple aarch64-linux-gnu -emit-llvm %s -o - | FileCheck --check-prefixes=NOBTI,ELF %s +// RUN: %clang_cc1 -triple arm64-apple-ios -emit-llvm %s -o - | FileCheck --check-prefixes=NOBTI %s +// RUN: %clang_cc1 -triple aarch64-linux-gnu -fvisibility=hidden -emit-llvm %s -o - | FileCheck --check-prefixes=HIDDEN,ELF %s +// RUN: %clang_cc1 -triple arm64-apple-ios -fvisibility=hidden -emit-llvm %s -o - | FileCheck --check-prefixes=HIDDEN %s + +// Check that the resolver functions generated by Clang have the correct attributes. +// In these test cases, branch-target-enforcement is used as an example of +// target-specific attribute that has to be set on every function by default. + +// FIXME: `cpu_specific`/`cpu_dispatch` and `target` attributes cannot be +// tested on AArch64. + +__attribute__((target_clones("crc", "default"))) +int global_target_clones(void) { return 0; } + +__attribute__((target_version("crc"))) int global_target_version(void) { return 0; } +__attribute__((target_version("default"))) int global_target_version(void) { return 0; } + +__attribute__((target_clones("crc", "default"))) +static int static_target_clones(void) { return 0; } + +__attribute__((target_version("crc"))) static int static_target_version(void) { return 0; } +__attribute__((target_version("default"))) static int static_target_version(void) { return 0; } + +// Force emission of static_* functions. +void *get_ptr1(void) { return static_target_clones; } +void *get_ptr2(void) { return static_target_version; } + +#ifdef __ELF__ +// Make sure target-specific attributes can be overriden as needed for +// non-autogenerated resolver functions. +// Note that since there is only a single definition of ifunc_resolver, it +// is not itself a multi-versioned function, even though it has target(...) +// attribute. +int ifunc_func(void) { return 0; } +__attribute__((target("branch-protection=bti"))) void *ifunc_resolver(void) { return ifunc_func; } +__attribute__((ifunc("ifunc_resolver"))) int ifunc(void); +#endif + +// ELF: define{{.*}} ptr @ifunc_resolver() #[[ATTR_IFUNC_RESOLVER:[0-9]+]] + +// BTI: define weak_odr ptr @global_target_clones.resolver() #[[ATTR_RESOLVER:[0-9]+]] +// BTI: define weak_odr ptr @global_target_version.resolver() #[[ATTR_RESOLVER]] +// BTI: define internal ptr @static_target_clones.resolver() #[[ATTR_RESOLVER]] +// BTI: define internal ptr @static_target_version.resolver() #[[ATTR_RESOLVER]] + +// In NOBTI case, no attribute groups are assigned to the resolver functions: +// NOBTI: define weak_odr ptr @global_target_clones.resolver(){{( comdat)?}} { +// NOBTI: define weak_odr ptr @global_target_version.resolver(){{( comdat)?}} { +// NOBTI: define internal ptr @static_target_clones.resolver() { +// NOBTI: define internal ptr @static_target_version.resolver() { + +// HIDDEN: define weak_odr hidden ptr @global_target_clones.resolver(){{( comdat)?}} { +// HIDDEN: define weak_odr hidden ptr @global_target_version.resolver(){{( comdat)?}} { +// HIDDEN: define internal ptr @static_target_clones.resolver() { +// HIDDEN: define internal ptr @static_target_version.resolver() { + +// ELF: attributes #[[ATTR_IFUNC_RESOLVER]] = { {{.*}}"branch-target-enforcement"{{.*}} } + +// BTI: attributes #[[ATTR_RESOLVER]] = { {{.*}}"branch-target-enforcement"{{.*}} }