[AArch64] PAC/BTI code generation for LLVM generated functions
PAC/BTI-related codegen in the AArch64 backend is controlled by a set of LLVM IR function attributes, added to the function by Clang, based on command-line options and GCC-style function attributes. However, functions, generated in the LLVM middle end (for example, asan.module.ctor or __llvm_gcov_write_out) do not get any attributes and the backend incorrectly does not do any PAC/BTI code generation. This patch record the default state of PAC/BTI codegen in a set of LLVM IR module-level attributes, based on command-line options: * "sign-return-address", with non-zero value means generate code to sign return addresses (PAC-RET), zero value means disable PAC-RET. * "sign-return-address-all", with non-zero value means enable PAC-RET for all functions, zero value means enable PAC-RET only for functions, which spill LR. * "sign-return-address-with-bkey", with non-zero value means use B-key for signing, zero value mean use A-key. This set of attributes are always added for AArch64 targets (as opposed, for example, to interpreting a missing attribute as having a value 0) in order to be able to check for conflicts when combining module attributed during LTO. Module-level attributes are overridden by function level attributes. All the decision making about whether to not to generate PAC and/or BTI code is factored out into AArch64FunctionInfo, there shouldn't be any places left, other than AArch64FunctionInfo, which directly examine PAC/BTI attributes, except AArch64AsmPrinter.cpp, which is/will-be handled by a separate patch. Differential Revision: https://reviews.llvm.org/D85649
This commit is contained in:
parent
f11f382523
commit
a88c722e68
|
|
@ -421,22 +421,6 @@ llvm::Function *CodeGenModule::CreateGlobalInitOrCleanUpFunction(
|
|||
!isInSanitizerBlacklist(SanitizerKind::ShadowCallStack, Fn, Loc))
|
||||
Fn->addFnAttr(llvm::Attribute::ShadowCallStack);
|
||||
|
||||
auto RASignKind = getLangOpts().getSignReturnAddressScope();
|
||||
if (RASignKind != LangOptions::SignReturnAddressScopeKind::None) {
|
||||
Fn->addFnAttr("sign-return-address",
|
||||
RASignKind == LangOptions::SignReturnAddressScopeKind::All
|
||||
? "all"
|
||||
: "non-leaf");
|
||||
auto RASignKey = getLangOpts().getSignReturnAddressKey();
|
||||
Fn->addFnAttr("sign-return-address-key",
|
||||
RASignKey == LangOptions::SignReturnAddressKeyKind::AKey
|
||||
? "a_key"
|
||||
: "b_key");
|
||||
}
|
||||
|
||||
if (getLangOpts().BranchTargetEnforcement)
|
||||
Fn->addFnAttr("branch-target-enforcement");
|
||||
|
||||
return Fn;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -590,6 +590,23 @@ void CodeGenModule::Release() {
|
|||
1);
|
||||
}
|
||||
|
||||
if (Arch == llvm::Triple::aarch64 || Arch == llvm::Triple::aarch64_32 ||
|
||||
Arch == llvm::Triple::aarch64_be) {
|
||||
getModule().addModuleFlag(llvm::Module::Error,
|
||||
"branch-target-enforcement",
|
||||
LangOpts.BranchTargetEnforcement);
|
||||
|
||||
getModule().addModuleFlag(llvm::Module::Error, "sign-return-address",
|
||||
LangOpts.hasSignReturnAddress());
|
||||
|
||||
getModule().addModuleFlag(llvm::Module::Error, "sign-return-address-all",
|
||||
LangOpts.isSignReturnAddressScopeAll());
|
||||
|
||||
getModule().addModuleFlag(llvm::Module::Error,
|
||||
"sign-return-address-with-bkey",
|
||||
!LangOpts.isSignReturnAddressWithAKey());
|
||||
}
|
||||
|
||||
if (LangOpts.CUDAIsDevice && getTriple().isNVPTX()) {
|
||||
// Indicate whether __nvvm_reflect should be configured to flush denormal
|
||||
// floating point values to 0. (This corresponds to its "__CUDA_FTZ"
|
||||
|
|
|
|||
|
|
@ -5521,40 +5521,33 @@ public:
|
|||
if (!FD)
|
||||
return;
|
||||
|
||||
LangOptions::SignReturnAddressScopeKind Scope =
|
||||
CGM.getLangOpts().getSignReturnAddressScope();
|
||||
LangOptions::SignReturnAddressKeyKind Key =
|
||||
CGM.getLangOpts().getSignReturnAddressKey();
|
||||
bool BranchTargetEnforcement = CGM.getLangOpts().BranchTargetEnforcement;
|
||||
if (const auto *TA = FD->getAttr<TargetAttr>()) {
|
||||
ParsedTargetAttr Attr = TA->parse();
|
||||
if (!Attr.BranchProtection.empty()) {
|
||||
TargetInfo::BranchProtectionInfo BPI;
|
||||
StringRef Error;
|
||||
(void)CGM.getTarget().validateBranchProtection(Attr.BranchProtection,
|
||||
BPI, Error);
|
||||
assert(Error.empty());
|
||||
Scope = BPI.SignReturnAddr;
|
||||
Key = BPI.SignKey;
|
||||
BranchTargetEnforcement = BPI.BranchTargetEnforcement;
|
||||
}
|
||||
}
|
||||
const auto *TA = FD->getAttr<TargetAttr>();
|
||||
if (TA == nullptr)
|
||||
return;
|
||||
|
||||
ParsedTargetAttr Attr = TA->parse();
|
||||
if (Attr.BranchProtection.empty())
|
||||
return;
|
||||
|
||||
TargetInfo::BranchProtectionInfo BPI;
|
||||
StringRef Error;
|
||||
(void)CGM.getTarget().validateBranchProtection(Attr.BranchProtection,
|
||||
BPI, Error);
|
||||
assert(Error.empty());
|
||||
|
||||
auto *Fn = cast<llvm::Function>(GV);
|
||||
if (Scope != LangOptions::SignReturnAddressScopeKind::None) {
|
||||
Fn->addFnAttr("sign-return-address",
|
||||
Scope == LangOptions::SignReturnAddressScopeKind::All
|
||||
? "all"
|
||||
: "non-leaf");
|
||||
static const char *SignReturnAddrStr[] = {"none", "non-leaf", "all"};
|
||||
Fn->addFnAttr("sign-return-address", SignReturnAddrStr[static_cast<int>(BPI.SignReturnAddr)]);
|
||||
|
||||
if (BPI.SignReturnAddr != LangOptions::SignReturnAddressScopeKind::None) {
|
||||
Fn->addFnAttr("sign-return-address-key",
|
||||
Key == LangOptions::SignReturnAddressKeyKind::AKey
|
||||
BPI.SignKey == LangOptions::SignReturnAddressKeyKind::AKey
|
||||
? "a_key"
|
||||
: "b_key");
|
||||
}
|
||||
|
||||
if (BranchTargetEnforcement)
|
||||
Fn->addFnAttr("branch-target-enforcement");
|
||||
Fn->addFnAttr("branch-target-enforcement",
|
||||
BPI.BranchTargetEnforcement ? "true" : "false");
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,81 +1,63 @@
|
|||
// REQUIRES: aarch64-registered-target
|
||||
// RUN: %clang_cc1 -triple aarch64-unknown-unknown-eabi -emit-llvm -target-cpu generic -target-feature +v8.5a %s -o - \
|
||||
// RUN: | FileCheck %s --check-prefix=CHECK --check-prefix=NO-OVERRIDE
|
||||
// RUN: %clang_cc1 -triple aarch64-unknown-unknown-eabi -emit-llvm -target-cpu generic -target-feature +v8.5a %s -o - \
|
||||
// RUN: -msign-return-address=non-leaf -msign-return-address-key=a_key -mbranch-target-enforce \
|
||||
// RUN: | FileCheck %s --check-prefix=CHECK --check-prefix=OVERRIDE
|
||||
|
||||
void missing() {}
|
||||
// NO-OVERRIDE: define void @missing() #[[#NONE:]]
|
||||
// OVERRIDE: define void @missing() #[[#STD:]]
|
||||
// RUN: | FileCheck %s --check-prefix=CHECK
|
||||
|
||||
__attribute__ ((target("branch-protection=none")))
|
||||
void none() {}
|
||||
// NO-OVERRIDE: define void @none() #[[#NONE]]
|
||||
// OVERRIDE: define void @none() #[[#NONE:]]
|
||||
// CHECK: define void @none() #[[#NONE:]]
|
||||
|
||||
__attribute__ ((target("branch-protection=standard")))
|
||||
void std() {}
|
||||
// NO-OVERRIDE: define void @std() #[[#STD:]]
|
||||
// OVERRIDE: define void @std() #[[#STD]]
|
||||
// CHECK: define void @std() #[[#STD:]]
|
||||
|
||||
__attribute__ ((target("branch-protection=bti")))
|
||||
void btionly() {}
|
||||
// NO-OVERRIDE: define void @btionly() #[[#BTI:]]
|
||||
// OVERRIDE: define void @btionly() #[[#BTI:]]
|
||||
// CHECK: define void @btionly() #[[#BTI:]]
|
||||
|
||||
__attribute__ ((target("branch-protection=pac-ret")))
|
||||
void paconly() {}
|
||||
// NO-OVERRIDE: define void @paconly() #[[#PAC:]]
|
||||
// OVERRIDE: define void @paconly() #[[#PAC:]]
|
||||
// CHECK: define void @paconly() #[[#PAC:]]
|
||||
|
||||
__attribute__ ((target("branch-protection=pac-ret+bti")))
|
||||
void pacbti0() {}
|
||||
// NO-OVERRIDE: define void @pacbti0() #[[#PACBTI:]]
|
||||
// OVERRIDE: define void @pacbti0() #[[#PACBTI:]]
|
||||
// CHECK: define void @pacbti0() #[[#PACBTI:]]
|
||||
|
||||
__attribute__ ((target("branch-protection=bti+pac-ret")))
|
||||
void pacbti1() {}
|
||||
// NO-OVERRIDE: define void @pacbti1() #[[#PACBTI]]
|
||||
// OVERRIDE: define void @pacbti1() #[[#PACBTI]]
|
||||
// CHECK: define void @pacbti1() #[[#PACBTI]]
|
||||
|
||||
__attribute__ ((target("branch-protection=pac-ret+leaf")))
|
||||
void leaf() {}
|
||||
// NO-OVERRIDE: define void @leaf() #[[#PACLEAF:]]
|
||||
// OVERRIDE: define void @leaf() #[[#PACLEAF:]]
|
||||
// CHECK: define void @leaf() #[[#PACLEAF:]]
|
||||
|
||||
__attribute__ ((target("branch-protection=pac-ret+b-key")))
|
||||
void bkey() {}
|
||||
// NO-OVERRIDE: define void @bkey() #[[#PACBKEY:]]
|
||||
// OVERRIDE: define void @bkey() #[[#PACBKEY:]]
|
||||
// CHECK: define void @bkey() #[[#PACBKEY:]]
|
||||
|
||||
__attribute__ ((target("branch-protection=pac-ret+b-key+leaf")))
|
||||
void bkeyleaf0() {}
|
||||
// NO-OVERRIDE: define void @bkeyleaf0() #[[#PACBKEYLEAF:]]
|
||||
// OVERRIDE: define void @bkeyleaf0() #[[#PACBKEYLEAF:]]
|
||||
// CHECK: define void @bkeyleaf0() #[[#PACBKEYLEAF:]]
|
||||
|
||||
__attribute__ ((target("branch-protection=pac-ret+leaf+b-key")))
|
||||
void bkeyleaf1() {}
|
||||
// NO-OVERRIDE: define void @bkeyleaf1() #[[#PACBKEYLEAF]]
|
||||
// OVERRIDE: define void @bkeyleaf1() #[[#PACBKEYLEAF]]
|
||||
// CHECK: define void @bkeyleaf1() #[[#PACBKEYLEAF]]
|
||||
|
||||
__attribute__ ((target("branch-protection=pac-ret+leaf+bti")))
|
||||
void btileaf() {}
|
||||
// NO-OVERRIDE: define void @btileaf() #[[#BTIPACLEAF:]]
|
||||
// OVERRIDE: define void @btileaf() #[[#BTIPACLEAF:]]
|
||||
// CHECK: define void @btileaf() #[[#BTIPACLEAF:]]
|
||||
|
||||
// CHECK-DAG: attributes #[[#NONE]]
|
||||
// CHECK-DAG: attributes #[[#NONE]] = { {{.*}} "branch-target-enforcement"="false" {{.*}} "sign-return-address"="none"
|
||||
|
||||
// CHECK-DAG: attributes #[[#STD]] = { {{.*}} "branch-target-enforcement" {{.*}} "sign-return-address"="non-leaf" "sign-return-address-key"="a_key"
|
||||
// CHECK-DAG: attributes #[[#STD]] = { {{.*}} "branch-target-enforcement"="true" {{.*}} "sign-return-address"="non-leaf" "sign-return-address-key"="a_key"
|
||||
|
||||
// CHECK-DAG: attributes #[[#BTI]] = { {{.*}}"branch-target-enforcement"
|
||||
// CHECK-DAG: attributes #[[#BTI]] = { {{.*}} "branch-target-enforcement"="true" {{.*}} "sign-return-address"="none"
|
||||
|
||||
// CHECK-DAG: attributes #[[#PAC]] = { {{.*}} "sign-return-address"="non-leaf" "sign-return-address-key"="a_key"
|
||||
// CHECK-DAG: attributes #[[#PAC]] = { {{.*}} "branch-target-enforcement"="false" {{.*}} "sign-return-address"="non-leaf" "sign-return-address-key"="a_key"
|
||||
|
||||
// CHECK-DAG: attributes #[[#PACLEAF]] = { {{.*}} "sign-return-address"="all" "sign-return-address-key"="a_key"
|
||||
// CHECK-DAG: attributes #[[#PACLEAF]] = { {{.*}} "branch-target-enforcement"="false" {{.*}}"sign-return-address"="all" "sign-return-address-key"="a_key"
|
||||
|
||||
// CHECK-DAG: attributes #[[#PACBKEY]] = { {{.*}} "sign-return-address"="non-leaf" "sign-return-address-key"="b_key"
|
||||
// CHECK-DAG: attributes #[[#PACBKEY]] = { {{.*}}"branch-target-enforcement"="false" {{.*}} "sign-return-address"="non-leaf" "sign-return-address-key"="b_key"
|
||||
|
||||
// CHECK-DAG: attributes #[[#PACBKEYLEAF]] = { {{.*}} "sign-return-address"="all" "sign-return-address-key"="b_key"
|
||||
// CHECK-DAG: attributes #[[#PACBKEYLEAF]] = { {{.*}} "branch-target-enforcement"="false" {{.*}}"sign-return-address"="all" "sign-return-address-key"="b_key"
|
||||
|
||||
// CHECK-DAG: attributes #[[#BTIPACLEAF]] = { {{.*}}"branch-target-enforcement" {{.*}} "sign-return-address"="all" "sign-return-address-key"="a_key"
|
||||
// CHECK-DAG: attributes #[[#BTIPACLEAF]] = { {{.*}}"branch-target-enforcement"="true" {{.*}} "sign-return-address"="all" "sign-return-address-key"="a_key"
|
||||
|
|
|
|||
|
|
@ -1,27 +1,46 @@
|
|||
// RUN: %clang -target aarch64-arm-none-eabi -march=armv8.3-a -S -emit-llvm -o - -msign-return-address=none %s | FileCheck %s --check-prefix=CHECK --check-prefix=NONE
|
||||
// RUN: %clang -target aarch64-arm-none-eabi -march=armv8.2-a -S -emit-llvm -o - -msign-return-address=all %s | FileCheck %s --check-prefix=CHECK --check-prefix=ALL --check-prefix=A-KEY
|
||||
// RUN: %clang -target aarch64-arm-none-eabi -march=armv8.3-a -S -emit-llvm -o - -msign-return-address=all %s | FileCheck %s --check-prefix=CHECK --check-prefix=ALL --check-prefix=A-KEY
|
||||
// RUN: %clang -target aarch64-arm-none-eabi -march=armv8.3-a -S -emit-llvm -o - -msign-return-address=non-leaf %s | FileCheck %s --check-prefix=CHECK --check-prefix=PARTIAL --check-prefix=A-KEY
|
||||
// RUN: %clang -target aarch64-arm-none-eabi -march=armv8.3-a -S -emit-llvm -o - -msign-return-address=all %s | FileCheck %s --check-prefix=CHECK --check-prefix=ALL --check-prefix=A-KEY
|
||||
// RUN: %clang -target aarch64-arm-none-eabi -march=armv8.4-a -S -emit-llvm -o - -msign-return-address=all %s | FileCheck %s --check-prefix=CHECK --check-prefix=ALL --check-prefix=A-KEY
|
||||
// RUN: %clang -target aarch64-arm-none-eabi -march=armv8.3-a -S -emit-llvm -o - -mbranch-protection=pac-ret+b-key %s | FileCheck %s --check-prefix=CHECK --check-prefix=PARTIAL --check-prefix=B-KEY
|
||||
// RUN: %clang -target aarch64-arm-none-eabi -march=armv8.3-a -S -emit-llvm -o - -mbranch-protection=pac-ret+b-key+leaf %s | FileCheck %s --check-prefix=CHECK --check-prefix=ALL --check-prefix=B-KEY
|
||||
// RUN: %clang -target aarch64-arm-none-eabi -march=armv8.3-a -S -emit-llvm -o - -mbranch-protection=bti %s | FileCheck %s --check-prefix=CHECK --check-prefix=BTE
|
||||
// RUN: %clang -target aarch64-arm-none-eabi -S -emit-llvm -o - -msign-return-address=none %s | FileCheck %s --check-prefix=CHECK --check-prefix=NONE
|
||||
// RUN: %clang -target aarch64-arm-none-eabi -S -emit-llvm -o - -msign-return-address=all %s | FileCheck %s --check-prefix=CHECK --check-prefix=ALL
|
||||
// RUN: %clang -target aarch64-arm-none-eabi -S -emit-llvm -o - -msign-return-address=non-leaf %s | FileCheck %s --check-prefix=CHECK --check-prefix=PART
|
||||
|
||||
// RUN: %clang -target aarch64-arm-none-eabi -S -emit-llvm -o - -mbranch-protection=none %s | FileCheck %s --check-prefix=CHECK --check-prefix=NONE
|
||||
// RUN: %clang -target aarch64-arm-none-eabi -S -emit-llvm -o - -mbranch-protection=pac-ret+leaf %s | FileCheck %s --check-prefix=CHECK --check-prefix=ALL
|
||||
// RUN: %clang -target aarch64-arm-none-eabi -S -emit-llvm -o - -mbranch-protection=pac-ret+b-key %s | FileCheck %s --check-prefix=CHECK --check-prefix=B-KEY
|
||||
// RUN: %clang -target aarch64-arm-none-eabi -S -emit-llvm -o - -mbranch-protection=bti %s | FileCheck %s --check-prefix=CHECK --check-prefix=BTE
|
||||
|
||||
// REQUIRES: aarch64-registered-target
|
||||
|
||||
// CHECK: @foo() #[[ATTR:[0-9]*]]
|
||||
//
|
||||
// NONE-NOT: "sign-return-address"={{.*}}
|
||||
// Check there are no branch protection function attributes
|
||||
|
||||
// PARTIAL: "sign-return-address"="non-leaf"
|
||||
// CHECK-LABEL: @foo() #[[#ATTR:]]
|
||||
|
||||
// ALL: "sign-return-address"="all"
|
||||
// CHECK-NOT: attributes #[[#ATTR]] = { {{.*}} "sign-return-address"
|
||||
// CHECK-NOT: attributes #[[#ATTR]] = { {{.*}} "sign-return-address-key"
|
||||
// CHECK-NOT: attributes #[[#ATTR]] = { {{.*}} "branch-target-enforcement"
|
||||
|
||||
// BTE: "branch-target-enforcement"
|
||||
// Check module attributes
|
||||
|
||||
// A-KEY: "sign-return-address-key"="a_key"
|
||||
// NONE: !{i32 1, !"branch-target-enforcement", i32 0}
|
||||
// ALL: !{i32 1, !"branch-target-enforcement", i32 0}
|
||||
// PART: !{i32 1, !"branch-target-enforcement", i32 0}
|
||||
// BTE: !{i32 1, !"branch-target-enforcement", i32 1}
|
||||
// B-KEY: !{i32 1, !"branch-target-enforcement", i32 0}
|
||||
|
||||
// B-KEY: "sign-return-address-key"="b_key"
|
||||
// NONE: !{i32 1, !"sign-return-address", i32 0}
|
||||
// ALL: !{i32 1, !"sign-return-address", i32 1}
|
||||
// PART: !{i32 1, !"sign-return-address", i32 1}
|
||||
// BTE: !{i32 1, !"sign-return-address", i32 0}
|
||||
// B-KEY: !{i32 1, !"sign-return-address", i32 1}
|
||||
|
||||
// NONE: !{i32 1, !"sign-return-address-all", i32 0}
|
||||
// ALL: !{i32 1, !"sign-return-address-all", i32 1}
|
||||
// PART: !{i32 1, !"sign-return-address-all", i32 0}
|
||||
// BTE: !{i32 1, !"sign-return-address-all", i32 0}
|
||||
// B-KEY: !{i32 1, !"sign-return-address-all", i32 0}
|
||||
|
||||
// NONE: !{i32 1, !"sign-return-address-with-bkey", i32 0}
|
||||
// ALL: !{i32 1, !"sign-return-address-with-bkey", i32 0}
|
||||
// PART: !{i32 1, !"sign-return-address-with-bkey", i32 0}
|
||||
// BTE: !{i32 1, !"sign-return-address-with-bkey", i32 0}
|
||||
// B-KEY: !{i32 1, !"sign-return-address-with-bkey", i32 1}
|
||||
|
||||
void foo() {}
|
||||
|
|
|
|||
|
|
@ -1,41 +0,0 @@
|
|||
// RUN: %clang -target aarch64-arm-none-eabi -S -emit-llvm -o - -msign-return-address=none %s | \
|
||||
// RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-NONE
|
||||
// RUN: %clang -target aarch64-arm-none-eabi -S -emit-llvm -o - -msign-return-address=non-leaf %s | \
|
||||
// RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-PARTIAL --check-prefix=CHECK-A-KEY
|
||||
// RUN: %clang -target aarch64-arm-none-eabi -S -emit-llvm -o - -msign-return-address=all %s | \
|
||||
// RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-ALL --check-prefix=CHECK-A-KEY
|
||||
|
||||
// RUN: %clang -target aarch64-arm-none-eabi -S -emit-llvm -o - -mbranch-protection=none %s | \
|
||||
// RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-NONE
|
||||
// RUN: %clang -target aarch64-arm-none-eabi -S -emit-llvm -o - -mbranch-protection=standard %s | \
|
||||
// RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-PARTIAL --check-prefix=CHECK-A-KEY --check-prefix=CHECK-BTE
|
||||
// RUN: %clang -target aarch64-arm-none-eabi -S -emit-llvm -o - -mbranch-protection=pac-ret %s | \
|
||||
// RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-PARTIAL --check-prefix=CHECK-A-KEY
|
||||
// RUN: %clang -target aarch64-arm-none-eabi -S -emit-llvm -o - -mbranch-protection=pac-ret+leaf %s | \
|
||||
// RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-ALL --check-prefix=CHECK-A-KEY
|
||||
// RUN: %clang -target aarch64-arm-none-eabi -S -emit-llvm -o - -mbranch-protection=pac-ret+b-key %s | \
|
||||
// RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-PARTIAL --check-prefix=CHECK-B-KEY
|
||||
// RUN: %clang -target aarch64-arm-none-eabi -S -emit-llvm -o - -mbranch-protection=pac-ret+b-key+leaf %s | \
|
||||
// RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-ALL --check-prefix=CHECK-B-KEY
|
||||
// RUN: %clang -target aarch64-arm-none-eabi -S -emit-llvm -o - -mbranch-protection=bti %s | \
|
||||
// RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-BTE
|
||||
// RUN: %clang -target aarch64-arm-none-eabi -S -emit-llvm -o - -mbranch-protection=pac-ret+b-key+leaf+bti %s | \
|
||||
// RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-ALL --check-prefix=CHECK-B-KEY --check-prefix=BTE
|
||||
|
||||
struct Foo {
|
||||
Foo() {}
|
||||
~Foo() {}
|
||||
};
|
||||
|
||||
Foo f;
|
||||
|
||||
// CHECK: @llvm.global_ctors {{.*}}i32 65535, void ()* @[[CTOR_FN:.*]], i8* null
|
||||
|
||||
// CHECK: @[[CTOR_FN]]() #[[ATTR:[0-9]*]]
|
||||
|
||||
// CHECK-NONE-NOT: "sign-return-address"={{.*}}
|
||||
// CHECK-PARTIAL: "sign-return-address"="non-leaf"
|
||||
// CHECK-ALL: "sign-return-address"="all"
|
||||
// CHECK-A-KEY: "sign-return-address-key"="a_key"
|
||||
// CHECK-B-KEY: "sign-return-address-key"="b_key"
|
||||
// CHECK-BTE: "branch-target-enforcement"
|
||||
|
|
@ -16,6 +16,7 @@
|
|||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "AArch64MachineFunctionInfo.h"
|
||||
#include "AArch64Subtarget.h"
|
||||
#include "llvm/CodeGen/MachineFunctionPass.h"
|
||||
#include "llvm/CodeGen/MachineInstrBuilder.h"
|
||||
|
|
@ -57,13 +58,13 @@ FunctionPass *llvm::createAArch64BranchTargetsPass() {
|
|||
}
|
||||
|
||||
bool AArch64BranchTargets::runOnMachineFunction(MachineFunction &MF) {
|
||||
const Function &F = MF.getFunction();
|
||||
if (!F.hasFnAttribute("branch-target-enforcement"))
|
||||
if (!MF.getInfo<AArch64FunctionInfo>()->branchTargetEnforcement())
|
||||
return false;
|
||||
|
||||
LLVM_DEBUG(
|
||||
dbgs() << "********** AArch64 Branch Targets **********\n"
|
||||
<< "********** Function: " << MF.getName() << '\n');
|
||||
const Function &F = MF.getFunction();
|
||||
|
||||
// LLVM does not consider basic blocks which are the targets of jump tables
|
||||
// to be address-taken (the address can't escape anywhere else), but they are
|
||||
|
|
|
|||
|
|
@ -375,31 +375,6 @@ MachineBasicBlock::iterator AArch64FrameLowering::eliminateCallFramePseudoInstr(
|
|||
return MBB.erase(I);
|
||||
}
|
||||
|
||||
static bool ShouldSignReturnAddress(MachineFunction &MF) {
|
||||
// The function should be signed in the following situations:
|
||||
// - sign-return-address=all
|
||||
// - sign-return-address=non-leaf and the functions spills the LR
|
||||
|
||||
const Function &F = MF.getFunction();
|
||||
if (!F.hasFnAttribute("sign-return-address"))
|
||||
return false;
|
||||
|
||||
StringRef Scope = F.getFnAttribute("sign-return-address").getValueAsString();
|
||||
if (Scope.equals("none"))
|
||||
return false;
|
||||
|
||||
if (Scope.equals("all"))
|
||||
return true;
|
||||
|
||||
assert(Scope.equals("non-leaf") && "Expected all, none or non-leaf");
|
||||
|
||||
for (const auto &Info : MF.getFrameInfo().getCalleeSavedInfo())
|
||||
if (Info.getReg() == AArch64::LR)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Convenience function to create a DWARF expression for
|
||||
// Expr + NumBytes + NumVGScaledBytes * AArch64::VG
|
||||
static void appendVGScaledOffsetExpr(SmallVectorImpl<char> &Expr,
|
||||
|
|
@ -1007,17 +982,6 @@ static void adaptForLdStOpt(MachineBasicBlock &MBB,
|
|||
//
|
||||
}
|
||||
|
||||
static bool ShouldSignWithAKey(MachineFunction &MF) {
|
||||
const Function &F = MF.getFunction();
|
||||
if (!F.hasFnAttribute("sign-return-address-key"))
|
||||
return true;
|
||||
|
||||
const StringRef Key =
|
||||
F.getFnAttribute("sign-return-address-key").getValueAsString();
|
||||
assert(Key.equals_lower("a_key") || Key.equals_lower("b_key"));
|
||||
return Key.equals_lower("a_key");
|
||||
}
|
||||
|
||||
static bool needsWinCFI(const MachineFunction &MF) {
|
||||
const Function &F = MF.getFunction();
|
||||
return MF.getTarget().getMCAsmInfo()->usesWindowsCFI() &&
|
||||
|
|
@ -1070,15 +1034,16 @@ void AArch64FrameLowering::emitPrologue(MachineFunction &MF,
|
|||
// to determine the end of the prologue.
|
||||
DebugLoc DL;
|
||||
|
||||
if (ShouldSignReturnAddress(MF)) {
|
||||
if (ShouldSignWithAKey(MF))
|
||||
BuildMI(MBB, MBBI, DL, TII->get(AArch64::PACIASP))
|
||||
.setMIFlag(MachineInstr::FrameSetup);
|
||||
else {
|
||||
const auto &MFnI = *MF.getInfo<AArch64FunctionInfo>();
|
||||
if (MFnI.shouldSignReturnAddress()) {
|
||||
if (MFnI.shouldSignWithBKey()) {
|
||||
BuildMI(MBB, MBBI, DL, TII->get(AArch64::EMITBKEY))
|
||||
.setMIFlag(MachineInstr::FrameSetup);
|
||||
BuildMI(MBB, MBBI, DL, TII->get(AArch64::PACIBSP))
|
||||
.setMIFlag(MachineInstr::FrameSetup);
|
||||
} else {
|
||||
BuildMI(MBB, MBBI, DL, TII->get(AArch64::PACIASP))
|
||||
.setMIFlag(MachineInstr::FrameSetup);
|
||||
}
|
||||
|
||||
unsigned CFIIndex =
|
||||
|
|
@ -1510,7 +1475,8 @@ void AArch64FrameLowering::emitPrologue(MachineFunction &MF,
|
|||
|
||||
static void InsertReturnAddressAuth(MachineFunction &MF,
|
||||
MachineBasicBlock &MBB) {
|
||||
if (!ShouldSignReturnAddress(MF))
|
||||
const auto &MFI = *MF.getInfo<AArch64FunctionInfo>();
|
||||
if (!MFI.shouldSignReturnAddress())
|
||||
return;
|
||||
const AArch64Subtarget &Subtarget = MF.getSubtarget<AArch64Subtarget>();
|
||||
const TargetInstrInfo *TII = Subtarget.getInstrInfo();
|
||||
|
|
@ -1527,13 +1493,13 @@ static void InsertReturnAddressAuth(MachineFunction &MF,
|
|||
if (Subtarget.hasV8_3aOps() && MBBI != MBB.end() &&
|
||||
MBBI->getOpcode() == AArch64::RET_ReallyLR) {
|
||||
BuildMI(MBB, MBBI, DL,
|
||||
TII->get(ShouldSignWithAKey(MF) ? AArch64::RETAA : AArch64::RETAB))
|
||||
TII->get(MFI.shouldSignWithBKey() ? AArch64::RETAB : AArch64::RETAA))
|
||||
.copyImplicitOps(*MBBI);
|
||||
MBB.erase(MBBI);
|
||||
} else {
|
||||
BuildMI(
|
||||
MBB, MBBI, DL,
|
||||
TII->get(ShouldSignWithAKey(MF) ? AArch64::AUTIASP : AArch64::AUTIBSP))
|
||||
TII->get(MFI.shouldSignWithBKey() ? AArch64::AUTIBSP : AArch64::AUTIASP))
|
||||
.setMIFlag(MachineInstr::FrameDestroy);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "AArch64MachineFunctionInfo.h"
|
||||
#include "AArch64TargetMachine.h"
|
||||
#include "MCTargetDesc/AArch64AddressingModes.h"
|
||||
#include "llvm/ADT/APSInt.h"
|
||||
|
|
|
|||
|
|
@ -5842,84 +5842,20 @@ AArch64InstrInfo::findRegisterToSaveLRTo(const outliner::Candidate &C) const {
|
|||
static bool
|
||||
outliningCandidatesSigningScopeConsensus(const outliner::Candidate &a,
|
||||
const outliner::Candidate &b) {
|
||||
const Function &Fa = a.getMF()->getFunction();
|
||||
const Function &Fb = b.getMF()->getFunction();
|
||||
const auto &MFIa = a.getMF()->getInfo<AArch64FunctionInfo>();
|
||||
const auto &MFIb = b.getMF()->getInfo<AArch64FunctionInfo>();
|
||||
|
||||
// If none of the functions have the "sign-return-address" attribute their
|
||||
// signing behaviour is equal
|
||||
if (!Fa.hasFnAttribute("sign-return-address") &&
|
||||
!Fb.hasFnAttribute("sign-return-address")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If both functions have the "sign-return-address" attribute their signing
|
||||
// behaviour is equal, if the values of the attributes are equal
|
||||
if (Fa.hasFnAttribute("sign-return-address") &&
|
||||
Fb.hasFnAttribute("sign-return-address")) {
|
||||
StringRef ScopeA =
|
||||
Fa.getFnAttribute("sign-return-address").getValueAsString();
|
||||
StringRef ScopeB =
|
||||
Fb.getFnAttribute("sign-return-address").getValueAsString();
|
||||
return ScopeA.equals(ScopeB);
|
||||
}
|
||||
|
||||
// If function B doesn't have the "sign-return-address" attribute but A does,
|
||||
// the functions' signing behaviour is equal if A's value for
|
||||
// "sign-return-address" is "none" and vice versa.
|
||||
if (Fa.hasFnAttribute("sign-return-address")) {
|
||||
StringRef ScopeA =
|
||||
Fa.getFnAttribute("sign-return-address").getValueAsString();
|
||||
return ScopeA.equals("none");
|
||||
}
|
||||
|
||||
if (Fb.hasFnAttribute("sign-return-address")) {
|
||||
StringRef ScopeB =
|
||||
Fb.getFnAttribute("sign-return-address").getValueAsString();
|
||||
return ScopeB.equals("none");
|
||||
}
|
||||
|
||||
llvm_unreachable("Unkown combination of sign-return-address attributes");
|
||||
return MFIa->shouldSignReturnAddress(false) == MFIb->shouldSignReturnAddress(false) &&
|
||||
MFIa->shouldSignReturnAddress(true) == MFIb->shouldSignReturnAddress(true);
|
||||
}
|
||||
|
||||
static bool
|
||||
outliningCandidatesSigningKeyConsensus(const outliner::Candidate &a,
|
||||
const outliner::Candidate &b) {
|
||||
const Function &Fa = a.getMF()->getFunction();
|
||||
const Function &Fb = b.getMF()->getFunction();
|
||||
const auto &MFIa = a.getMF()->getInfo<AArch64FunctionInfo>();
|
||||
const auto &MFIb = b.getMF()->getInfo<AArch64FunctionInfo>();
|
||||
|
||||
// If none of the functions have the "sign-return-address-key" attribute
|
||||
// their keys are equal
|
||||
if (!Fa.hasFnAttribute("sign-return-address-key") &&
|
||||
!Fb.hasFnAttribute("sign-return-address-key")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If both functions have the "sign-return-address-key" attribute their
|
||||
// keys are equal if the values of "sign-return-address-key" are equal
|
||||
if (Fa.hasFnAttribute("sign-return-address-key") &&
|
||||
Fb.hasFnAttribute("sign-return-address-key")) {
|
||||
StringRef KeyA =
|
||||
Fa.getFnAttribute("sign-return-address-key").getValueAsString();
|
||||
StringRef KeyB =
|
||||
Fb.getFnAttribute("sign-return-address-key").getValueAsString();
|
||||
return KeyA.equals(KeyB);
|
||||
}
|
||||
|
||||
// If B doesn't have the "sign-return-address-key" attribute, both keys are
|
||||
// equal, if function a has the default key (a_key)
|
||||
if (Fa.hasFnAttribute("sign-return-address-key")) {
|
||||
StringRef KeyA =
|
||||
Fa.getFnAttribute("sign-return-address-key").getValueAsString();
|
||||
return KeyA.equals_lower("a_key");
|
||||
}
|
||||
|
||||
if (Fb.hasFnAttribute("sign-return-address-key")) {
|
||||
StringRef KeyB =
|
||||
Fb.getFnAttribute("sign-return-address-key").getValueAsString();
|
||||
return KeyB.equals_lower("a_key");
|
||||
}
|
||||
|
||||
llvm_unreachable("Unkown combination of sign-return-address-key attributes");
|
||||
return MFIa->shouldSignWithBKey() == MFIb->shouldSignWithBKey();
|
||||
}
|
||||
|
||||
static bool outliningCandidatesV8_3OpsConsensus(const outliner::Candidate &a,
|
||||
|
|
@ -5975,9 +5911,10 @@ outliner::OutlinedFunction AArch64InstrInfo::getOutliningCandidateInfo(
|
|||
// v8.3a RET can be replaced by RETAA/RETAB and no AUT instruction is
|
||||
// necessary. However, at this point we don't know if the outlined function
|
||||
// will have a RET instruction so we assume the worst.
|
||||
const Function &FCF = FirstCand.getMF()->getFunction();
|
||||
const TargetRegisterInfo &TRI = getRegisterInfo();
|
||||
if (FCF.hasFnAttribute("sign-return-address")) {
|
||||
if (FirstCand.getMF()
|
||||
->getInfo<AArch64FunctionInfo>()
|
||||
->shouldSignReturnAddress(true)) {
|
||||
// One PAC and one AUT instructions
|
||||
NumBytesToCreateFrame += 8;
|
||||
|
||||
|
|
@ -6106,7 +6043,7 @@ outliner::OutlinedFunction AArch64InstrInfo::getOutliningCandidateInfo(
|
|||
NumBytesToCreateFrame += 4;
|
||||
|
||||
bool HasBTI = any_of(RepeatedSequenceLocs, [](outliner::Candidate &C) {
|
||||
return C.getMF()->getFunction().hasFnAttribute("branch-target-enforcement");
|
||||
return C.getMF()->getInfo<AArch64FunctionInfo>()->branchTargetEnforcement();
|
||||
});
|
||||
|
||||
// We check to see if CFI Instructions are present, and if they are
|
||||
|
|
@ -6811,27 +6748,11 @@ void AArch64InstrInfo::buildOutlinedFrame(
|
|||
// If a bunch of candidates reach this point they must agree on their return
|
||||
// address signing. It is therefore enough to just consider the signing
|
||||
// behaviour of one of them
|
||||
const Function &CF = OF.Candidates.front().getMF()->getFunction();
|
||||
bool ShouldSignReturnAddr = false;
|
||||
if (CF.hasFnAttribute("sign-return-address")) {
|
||||
StringRef Scope =
|
||||
CF.getFnAttribute("sign-return-address").getValueAsString();
|
||||
if (Scope.equals("all"))
|
||||
ShouldSignReturnAddr = true;
|
||||
else if (Scope.equals("non-leaf") && !IsLeafFunction)
|
||||
ShouldSignReturnAddr = true;
|
||||
}
|
||||
const auto &MFI = *OF.Candidates.front().getMF()->getInfo<AArch64FunctionInfo>();
|
||||
bool ShouldSignReturnAddr = MFI.shouldSignReturnAddress(!IsLeafFunction);
|
||||
|
||||
// a_key is the default
|
||||
bool ShouldSignReturnAddrWithAKey = true;
|
||||
if (CF.hasFnAttribute("sign-return-address-key")) {
|
||||
const StringRef Key =
|
||||
CF.getFnAttribute("sign-return-address-key").getValueAsString();
|
||||
// Key can either be a_key or b_key
|
||||
assert((Key.equals_lower("a_key") || Key.equals_lower("b_key")) &&
|
||||
"Return address signing key must be either a_key or b_key");
|
||||
ShouldSignReturnAddrWithAKey = Key.equals_lower("a_key");
|
||||
}
|
||||
bool ShouldSignReturnAddrWithAKey = !MFI.shouldSignWithBKey();
|
||||
|
||||
// If this is a tail call outlined function, then there's already a return.
|
||||
if (OF.FrameConstructionID == MachineOutlinerTailCall ||
|
||||
|
|
|
|||
|
|
@ -593,8 +593,8 @@ let RecomputePerFunction = 1 in {
|
|||
// Avoid generating STRQro if it is slow, unless we're optimizing for code size.
|
||||
def UseSTRQro : Predicate<"!Subtarget->isSTRQroSlow() || shouldOptForSize(MF)">;
|
||||
|
||||
def UseBTI : Predicate<[{ MF->getFunction().hasFnAttribute("branch-target-enforcement") }]>;
|
||||
def NotUseBTI : Predicate<[{ !MF->getFunction().hasFnAttribute("branch-target-enforcement") }]>;
|
||||
def UseBTI : Predicate<[{ MF->getInfo<AArch64FunctionInfo>()->branchTargetEnforcement() }]>;
|
||||
def NotUseBTI : Predicate<[{ !MF->getInfo<AArch64FunctionInfo>()->branchTargetEnforcement() }]>;
|
||||
|
||||
def SLSBLRMitigation : Predicate<[{ MF->getSubtarget<AArch64Subtarget>().hardenSlsBlr() }]>;
|
||||
def NoSLSBLRMitigation : Predicate<[{ !MF->getSubtarget<AArch64Subtarget>().hardenSlsBlr() }]>;
|
||||
|
|
|
|||
|
|
@ -14,6 +14,9 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "AArch64MachineFunctionInfo.h"
|
||||
#include "AArch64InstrInfo.h"
|
||||
#include <llvm/IR/Metadata.h>
|
||||
#include <llvm/IR/Module.h>
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
|
|
@ -30,3 +33,82 @@ void AArch64FunctionInfo::initializeBaseYamlFields(
|
|||
if (YamlMFI.HasRedZone.hasValue())
|
||||
HasRedZone = YamlMFI.HasRedZone;
|
||||
}
|
||||
|
||||
static std::pair<bool, bool> GetSignReturnAddress(const Function &F) {
|
||||
// The function should be signed in the following situations:
|
||||
// - sign-return-address=all
|
||||
// - sign-return-address=non-leaf and the functions spills the LR
|
||||
if (!F.hasFnAttribute("sign-return-address")) {
|
||||
const Module &M = *F.getParent();
|
||||
if (const auto *Sign = mdconst::extract_or_null<ConstantInt>(
|
||||
M.getModuleFlag("sign-return-address"))) {
|
||||
if (Sign->getZExtValue()) {
|
||||
if (const auto *All = mdconst::extract_or_null<ConstantInt>(
|
||||
M.getModuleFlag("sign-return-address-all")))
|
||||
return {true, All->getZExtValue()};
|
||||
return {true, false};
|
||||
}
|
||||
}
|
||||
return {false, false};
|
||||
}
|
||||
|
||||
StringRef Scope = F.getFnAttribute("sign-return-address").getValueAsString();
|
||||
if (Scope.equals("none"))
|
||||
return {false, false};
|
||||
|
||||
if (Scope.equals("all"))
|
||||
return {true, true};
|
||||
|
||||
assert(Scope.equals("non-leaf"));
|
||||
return {true, false};
|
||||
}
|
||||
|
||||
static bool ShouldSignWithBKey(const Function &F) {
|
||||
if (!F.hasFnAttribute("sign-return-address-key")) {
|
||||
if (const auto *BKey = mdconst::extract_or_null<ConstantInt>(
|
||||
F.getParent()->getModuleFlag("sign-return-address-with-bkey")))
|
||||
return BKey->getZExtValue();
|
||||
return false;
|
||||
}
|
||||
|
||||
const StringRef Key =
|
||||
F.getFnAttribute("sign-return-address-key").getValueAsString();
|
||||
assert(Key.equals_lower("a_key") || Key.equals_lower("b_key"));
|
||||
return Key.equals_lower("b_key");
|
||||
}
|
||||
|
||||
AArch64FunctionInfo::AArch64FunctionInfo(MachineFunction &MF) : MF(MF) {
|
||||
// If we already know that the function doesn't have a redzone, set
|
||||
// HasRedZone here.
|
||||
if (MF.getFunction().hasFnAttribute(Attribute::NoRedZone))
|
||||
HasRedZone = false;
|
||||
|
||||
const Function &F = MF.getFunction();
|
||||
std::tie(SignReturnAddress, SignReturnAddressAll) = GetSignReturnAddress(F);
|
||||
SignWithBKey = ShouldSignWithBKey(F);
|
||||
|
||||
if (!F.hasFnAttribute("branch-target-enforcement")) {
|
||||
if (const auto *BTE = mdconst::extract_or_null<ConstantInt>(
|
||||
F.getParent()->getModuleFlag("branch-target-enforcement")))
|
||||
BranchTargetEnforcement = BTE->getZExtValue();
|
||||
return;
|
||||
}
|
||||
|
||||
const StringRef BTIEnable = F.getFnAttribute("branch-target-enforcement").getValueAsString();
|
||||
assert(BTIEnable.equals_lower("true") || BTIEnable.equals_lower("false"));
|
||||
BranchTargetEnforcement = BTIEnable.equals_lower("true");
|
||||
}
|
||||
|
||||
bool AArch64FunctionInfo::shouldSignReturnAddress(bool SpillsLR) const {
|
||||
if (!SignReturnAddress)
|
||||
return false;
|
||||
if (SignReturnAddressAll)
|
||||
return true;
|
||||
return SpillsLR;
|
||||
}
|
||||
|
||||
bool AArch64FunctionInfo::shouldSignReturnAddress() const {
|
||||
return shouldSignReturnAddress(llvm::any_of(
|
||||
MF.getFrameInfo().getCalleeSavedInfo(),
|
||||
[](const auto &Info) { return Info.getReg() == AArch64::LR; }));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,9 @@ class MachineInstr;
|
|||
/// AArch64FunctionInfo - This class is derived from MachineFunctionInfo and
|
||||
/// contains private AArch64-specific information for each MachineFunction.
|
||||
class AArch64FunctionInfo final : public MachineFunctionInfo {
|
||||
/// Backreference to the machine function.
|
||||
MachineFunction &MF;
|
||||
|
||||
/// Number of bytes of arguments this function has on the stack. If the callee
|
||||
/// is expected to restore the argument stack this should be a multiple of 16,
|
||||
/// all usable during a tail call.
|
||||
|
|
@ -138,17 +141,24 @@ class AArch64FunctionInfo final : public MachineFunctionInfo {
|
|||
// CalleeSavedStackSize) to the address of the frame record.
|
||||
int CalleeSaveBaseToFrameRecordOffset = 0;
|
||||
|
||||
/// SignReturnAddress is true if PAC-RET is enabled for the function with
|
||||
/// defaults being sign non-leaf functions only, with the B key.
|
||||
bool SignReturnAddress = false;
|
||||
|
||||
/// SignReturnAddressAll modifies the default PAC-RET mode to signing leaf
|
||||
/// functions as well.
|
||||
bool SignReturnAddressAll = false;
|
||||
|
||||
/// SignWithBKey modifies the default PAC-RET mode to signing with the B key.
|
||||
bool SignWithBKey = false;
|
||||
|
||||
/// BranchTargetEnforcement enables placing BTI instructions at potential
|
||||
/// indirect branch destinations.
|
||||
bool BranchTargetEnforcement = false;
|
||||
|
||||
public:
|
||||
AArch64FunctionInfo() = default;
|
||||
explicit AArch64FunctionInfo(MachineFunction &MF);
|
||||
|
||||
explicit AArch64FunctionInfo(MachineFunction &MF) {
|
||||
(void)MF;
|
||||
|
||||
// If we already know that the function doesn't have a redzone, set
|
||||
// HasRedZone here.
|
||||
if (MF.getFunction().hasFnAttribute(Attribute::NoRedZone))
|
||||
HasRedZone = false;
|
||||
}
|
||||
void initializeBaseYamlFields(const yaml::AArch64FunctionInfo &YamlMFI);
|
||||
|
||||
unsigned getBytesInStackArgArea() const { return BytesInStackArgArea; }
|
||||
|
|
@ -347,6 +357,13 @@ public:
|
|||
CalleeSaveBaseToFrameRecordOffset = Offset;
|
||||
}
|
||||
|
||||
bool shouldSignReturnAddress() const;
|
||||
bool shouldSignReturnAddress(bool SpillsLR) const;
|
||||
|
||||
bool shouldSignWithBKey() const { return SignWithBKey; }
|
||||
|
||||
bool branchTargetEnforcement() const { return BranchTargetEnforcement; }
|
||||
|
||||
private:
|
||||
// Hold the lists of LOHs.
|
||||
MILOHContainer LOHContainerSet;
|
||||
|
|
|
|||
|
|
@ -789,7 +789,7 @@ static unsigned getCallOpcode(const MachineFunction &CallerF, bool IsIndirect,
|
|||
|
||||
// When BTI is enabled, we need to use TCRETURNriBTI to make sure that we use
|
||||
// x16 or x17.
|
||||
if (CallerF.getFunction().hasFnAttribute("branch-target-enforcement"))
|
||||
if (CallerF.getInfo<AArch64FunctionInfo>()->branchTargetEnforcement())
|
||||
return AArch64::TCRETURNriBTI;
|
||||
|
||||
return AArch64::TCRETURNri;
|
||||
|
|
@ -809,7 +809,7 @@ bool AArch64CallLowering::lowerTailCall(
|
|||
|
||||
// TODO: Right now, regbankselect doesn't know how to handle the rtcGPR64
|
||||
// register class. Until we can do that, we should fall back here.
|
||||
if (F.hasFnAttribute("branch-target-enforcement")) {
|
||||
if (MF.getInfo<AArch64FunctionInfo>()->branchTargetEnforcement()) {
|
||||
LLVM_DEBUG(
|
||||
dbgs() << "Cannot lower indirect tail calls with BTI enabled yet.\n");
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ entry:
|
|||
ret void
|
||||
}
|
||||
|
||||
define void @bti_enabled(void ()* %p) "branch-target-enforcement" {
|
||||
define void @bti_enabled(void ()* %p) "branch-target-enforcement"="true" {
|
||||
entry:
|
||||
tail call void %p()
|
||||
; CHECK: br {{x16|x17}}
|
||||
|
|
|
|||
|
|
@ -3,29 +3,29 @@
|
|||
target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
|
||||
target triple = "aarch64-arm-none-eabi"
|
||||
|
||||
define hidden i32 @simple_external() "branch-target-enforcement" {
|
||||
define hidden i32 @simple_external() "branch-target-enforcement"="true" {
|
||||
entry:
|
||||
ret i32 0
|
||||
}
|
||||
|
||||
define internal i32 @simple_internal() "branch-target-enforcement" {
|
||||
define internal i32 @simple_internal() "branch-target-enforcement"="true" {
|
||||
entry:
|
||||
ret i32 0
|
||||
}
|
||||
|
||||
define hidden i32 @ptr_auth() "branch-target-enforcement" {
|
||||
define hidden i32 @ptr_auth() "branch-target-enforcement"="true" {
|
||||
entry:
|
||||
tail call void asm sideeffect "", "~{lr}"()
|
||||
ret i32 0
|
||||
}
|
||||
|
||||
define hidden i32 @ptr_auth_b() "branch-target-enforcement" {
|
||||
define hidden i32 @ptr_auth_b() "branch-target-enforcement"="true" {
|
||||
entry:
|
||||
tail call void asm sideeffect "", "~{lr}"()
|
||||
ret i32 0
|
||||
}
|
||||
|
||||
define hidden i32 @jump_table(i32 %a) "branch-target-enforcement" {
|
||||
define hidden i32 @jump_table(i32 %a) "branch-target-enforcement"="true" {
|
||||
entry:
|
||||
switch i32 %a, label %sw.epilog [
|
||||
i32 1, label %sw.bb
|
||||
|
|
@ -61,7 +61,7 @@
|
|||
|
||||
@label_address.addr = internal unnamed_addr global i8* blockaddress(@label_address, %return), align 8
|
||||
|
||||
define hidden i32 @label_address() "branch-target-enforcement" {
|
||||
define hidden i32 @label_address() "branch-target-enforcement"="true" {
|
||||
entry:
|
||||
%0 = load i8*, i8** @label_address.addr, align 8
|
||||
indirectbr i8* %0, [label %return, label %lab2]
|
||||
|
|
@ -79,7 +79,7 @@
|
|||
ret i32 %merge2
|
||||
}
|
||||
|
||||
define hidden i32 @label_address_entry() "branch-target-enforcement" {
|
||||
define hidden i32 @label_address_entry() "branch-target-enforcement"="true" {
|
||||
entry:
|
||||
%0 = load i8*, i8** @label_address.addr, align 8
|
||||
indirectbr i8* %0, [label %return, label %lab2]
|
||||
|
|
@ -97,7 +97,7 @@
|
|||
ret i32 %merge2
|
||||
}
|
||||
|
||||
define hidden i32 @debug_ptr_auth() "branch-target-enforcement" {
|
||||
define hidden i32 @debug_ptr_auth() "branch-target-enforcement"="true" {
|
||||
entry:
|
||||
tail call void asm sideeffect "", "~{lr}"()
|
||||
ret i32 0
|
||||
|
|
|
|||
|
|
@ -61,4 +61,4 @@ declare dso_local void @e(...) local_unnamed_addr #0
|
|||
|
||||
declare dso_local i64 @llvm.aarch64.space(i32, i64) local_unnamed_addr #0
|
||||
|
||||
attributes #0 = { nounwind "branch-target-enforcement" "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="generic" "target-features"="+neon,+v8.5a" "unsafe-fp-math"="false" "use-soft-float"="false" }
|
||||
attributes #0 = { nounwind "branch-target-enforcement"="true" "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="generic" "target-features"="+neon,+v8.5a" "unsafe-fp-math"="false" "use-soft-float"="false" }
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
define void @f2() #0 { ret void }
|
||||
define void @f3() #0 { ret void }
|
||||
define void @f4() #0 { ret void }
|
||||
attributes #0 = { minsize noredzone "branch-target-enforcement" }
|
||||
attributes #0 = { minsize noredzone "branch-target-enforcement"="true" }
|
||||
...
|
||||
---
|
||||
name: f1
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
--- |
|
||||
@g = hidden local_unnamed_addr global i32 0, align 4
|
||||
|
||||
define hidden void @bar(void ()* nocapture %f) "branch-target-enforcement" {
|
||||
define hidden void @bar(void ()* nocapture %f) "branch-target-enforcement"="true" {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
@g = hidden global i32 0, align 4
|
||||
|
||||
define hidden void @foo() minsize "branch-target-enforcement" {
|
||||
define hidden void @foo() minsize "branch-target-enforcement"="true" {
|
||||
entry:
|
||||
; CHECK: hint #34
|
||||
; CHECK: b OUTLINED_FUNCTION_0
|
||||
|
|
@ -13,7 +13,7 @@ entry:
|
|||
ret void
|
||||
}
|
||||
|
||||
define hidden void @bar() minsize "branch-target-enforcement" {
|
||||
define hidden void @bar() minsize "branch-target-enforcement"="true" {
|
||||
entry:
|
||||
; CHECK: hint #34
|
||||
; CHECK: b OUTLINED_FUNCTION_0
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
; RUN: llvm-readelf --notes - | FileCheck %s --check-prefix=OBJ
|
||||
@x = common dso_local global i32 0, align 4
|
||||
|
||||
attributes #0 = { "branch-target-enforcement" }
|
||||
attributes #0 = { "branch-target-enforcement"="true" }
|
||||
|
||||
; Both attributes present in a file with no functions.
|
||||
; ASM: .word 3221225472
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ entry:
|
|||
ret i32 0
|
||||
}
|
||||
|
||||
attributes #0 = { "branch-target-enforcement" }
|
||||
attributes #0 = { "branch-target-enforcement"="true" }
|
||||
|
||||
; BTI attribute present
|
||||
; ASM: .word 3221225472
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ entry:
|
|||
ret i32 0
|
||||
}
|
||||
|
||||
attributes #0 = { "branch-target-enforcement" "sign-return-address"="non-leaf" }
|
||||
attributes #0 = { "branch-target-enforcement"="true" "sign-return-address"="non-leaf" }
|
||||
|
||||
; Both attribute present
|
||||
; ASM: .word 3221225472
|
||||
|
|
|
|||
|
|
@ -13,9 +13,9 @@ entry:
|
|||
ret i32 0
|
||||
}
|
||||
|
||||
attributes #0 = { "branch-target-enforcement" "sign-return-address"="non-leaf" }
|
||||
attributes #0 = { "branch-target-enforcement"="true" "sign-return-address"="non-leaf" }
|
||||
|
||||
attributes #1 = { "branch-target-enforcement" }
|
||||
attributes #1 = { "branch-target-enforcement"="true" }
|
||||
|
||||
; Only the common atttribute (BTI)
|
||||
; ASM: .word 3221225472
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ entry:
|
|||
ret i32 0
|
||||
}
|
||||
|
||||
attributes #0 = { "branch-target-enforcement" "sign-return-address"="non-leaf" }
|
||||
attributes #0 = { "branch-target-enforcement"="true" "sign-return-address"="non-leaf" }
|
||||
|
||||
attributes #1 = { "sign-return-address"="all" }
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ entry:
|
|||
|
||||
attributes #0 = { "sign-return-address"="non-leaf" }
|
||||
|
||||
attributes #1 = { "branch-target-enforcement" }
|
||||
attributes #1 = { "branch-target-enforcement"="true" }
|
||||
|
||||
; No common attribute, no note section
|
||||
; ASM: warning: not setting BTI in feature flags
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ entry:
|
|||
|
||||
declare dso_local i32 @g()
|
||||
|
||||
attributes #0 = { "branch-target-enforcement" }
|
||||
attributes #0 = { "branch-target-enforcement"="true" }
|
||||
|
||||
; Declarations don't prevent setting BTI
|
||||
; ASM: .word 3221225472
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
;; RUN: llc %s -o -| FileCheck %s
|
||||
target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
|
||||
target triple = "aarch64-unknown-linux"
|
||||
|
||||
@llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 1, void ()* @asan.module_ctor, i8* null }]
|
||||
|
||||
define dso_local i32 @f() #0 {
|
||||
entry:
|
||||
ret i32 0
|
||||
}
|
||||
;; CHECK-LABEL: f:
|
||||
;; CHECK: hint #34
|
||||
|
||||
declare void @__asan_init()
|
||||
declare void @__asan_version_mismatch_check_v8()
|
||||
|
||||
define internal void @asan.module_ctor() {
|
||||
call void @__asan_init()
|
||||
call void @__asan_version_mismatch_check_v8()
|
||||
ret void
|
||||
}
|
||||
;; CHECK-LABEL: asan.module_ctor:
|
||||
;; CHECK: hint #34
|
||||
|
||||
attributes #0 = { noinline nounwind optnone sanitize_address uwtable "branch-target-enforcement"="true" }
|
||||
|
||||
!llvm.module.flags = !{!0, !1, !2, !3, !4}
|
||||
|
||||
!0 = !{i32 1, !"wchar_size", i32 4}
|
||||
!1 = !{i32 4, !"branch-target-enforcement", i32 1}
|
||||
!2 = !{i32 4, !"sign-return-address", i32 0}
|
||||
!3 = !{i32 4, !"sign-return-address-all", i32 0}
|
||||
!4 = !{i32 4, !"sign-return-address-with-bkey", i32 0}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
;; RUN: llc --mattr=+v8.3a %s -o - | FileCheck %s
|
||||
target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
|
||||
target triple = "aarch64-unknown-linux"
|
||||
|
||||
@__llvm_gcov_ctr = internal global [1 x i64] zeroinitializer
|
||||
@0 = private unnamed_addr constant [7 x i8] c"m.gcda\00", align 1
|
||||
@llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 0, void ()* @__llvm_gcov_init, i8* null }]
|
||||
|
||||
define dso_local i32 @f() local_unnamed_addr #0 {
|
||||
entry:
|
||||
ret i32 0
|
||||
}
|
||||
;; CHECK-LABEL: f:
|
||||
;; CHECK: pacibsp
|
||||
|
||||
declare void @llvm_gcda_start_file(i8*, i32, i32) local_unnamed_addr
|
||||
|
||||
declare void @llvm_gcda_emit_function(i32, i32, i32) local_unnamed_addr
|
||||
|
||||
declare void @llvm_gcda_emit_arcs(i32, i64*) local_unnamed_addr
|
||||
|
||||
declare void @llvm_gcda_summary_info() local_unnamed_addr
|
||||
|
||||
declare void @llvm_gcda_end_file() local_unnamed_addr
|
||||
|
||||
define internal void @__llvm_gcov_writeout() unnamed_addr #1 {
|
||||
entry:
|
||||
tail call void @llvm_gcda_start_file(i8* getelementptr inbounds ([7 x i8], [7 x i8]* @0, i64 0, i64 0), i32 875575338, i32 0)
|
||||
tail call void @llvm_gcda_emit_function(i32 0, i32 0, i32 0)
|
||||
tail call void @llvm_gcda_emit_arcs(i32 1, i64* getelementptr inbounds ([1 x i64], [1 x i64]* @__llvm_gcov_ctr, i64 0, i64 0))
|
||||
tail call void @llvm_gcda_summary_info()
|
||||
tail call void @llvm_gcda_end_file()
|
||||
ret void
|
||||
}
|
||||
;; CHECK-LABEL: __llvm_gcov_writeout:
|
||||
;; CHECK: .cfi_b_key_frame
|
||||
;; CHECK-NEXT: pacibsp
|
||||
;; CHECK-NEXT: .cfi_negate_ra_state
|
||||
|
||||
define internal void @__llvm_gcov_reset() unnamed_addr #2 {
|
||||
entry:
|
||||
store i64 0, i64* getelementptr inbounds ([1 x i64], [1 x i64]* @__llvm_gcov_ctr, i64 0, i64 0), align 8
|
||||
ret void
|
||||
}
|
||||
;; CHECK-LABEL: __llvm_gcov_reset:
|
||||
;; CHECK: pacibsp
|
||||
|
||||
declare void @llvm_gcov_init(void ()*, void ()*) local_unnamed_addr
|
||||
|
||||
define internal void @__llvm_gcov_init() unnamed_addr #1 {
|
||||
entry:
|
||||
tail call void @llvm_gcov_init(void ()* nonnull @__llvm_gcov_writeout, void ()* nonnull @__llvm_gcov_reset)
|
||||
ret void
|
||||
}
|
||||
;; CHECK-LABEL: __llvm_gcov_init:
|
||||
;; CHECK: .cfi_b_key_frame
|
||||
;; CHECK-NEXT: pacibsp
|
||||
;; CHECK-NEXT: .cfi_negate_ra_state
|
||||
|
||||
attributes #0 = { norecurse nounwind readnone "sign-return-address"="all" "sign-return-address-key"="b_key" }
|
||||
attributes #1 = { noinline }
|
||||
attributes #2 = { nofree noinline norecurse nounwind writeonly }
|
||||
|
||||
!llvm.module.flags = !{!0, !1, !2, !3, !4, !5}
|
||||
|
||||
!0 = !{i32 2, !"Debug Info Version", i32 3}
|
||||
!1 = !{i32 1, !"wchar_size", i32 4}
|
||||
!2 = !{i32 1, !"branch-target-enforcement", i32 0}
|
||||
!3 = !{i32 1, !"sign-return-address", i32 1}
|
||||
!4 = !{i32 1, !"sign-return-address-all", i32 1}
|
||||
!5 = !{i32 1, !"sign-return-address-with-bkey", i32 1}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
;; RUN: llc -mtriple=aarch64-eabi -mattr=+v8.5a %s -o - | FileCheck %s
|
||||
|
||||
declare i32 @g(i32) #5
|
||||
|
||||
define i32 @f0(i32 %x) #0 {
|
||||
entry:
|
||||
%call = tail call i32 @g(i32 %x) #5
|
||||
%add = add nsw i32 %call, 1
|
||||
ret i32 %add
|
||||
}
|
||||
;; CHECK-LABEL: f0:
|
||||
;; CHECK-NOT: bti
|
||||
;; CHECK-NOT: pacia
|
||||
;; CHECK-NOT: reta
|
||||
|
||||
define i32 @f1(i32 %x) #1 {
|
||||
entry:
|
||||
%call = tail call i32 @g(i32 %x) #5
|
||||
%add = add nsw i32 %call, 1
|
||||
ret i32 %add
|
||||
}
|
||||
;; CHECK-LABEL: f1:
|
||||
;; CHECK: bti c
|
||||
;; CHECK-NOT: reta
|
||||
|
||||
define i32 @f2(i32 %x) #2 {
|
||||
entry:
|
||||
%call = tail call i32 @g(i32 %x) #5
|
||||
%add = add nsw i32 %call, 1
|
||||
ret i32 %add
|
||||
}
|
||||
;; CHECK-LABEL: f2:
|
||||
;; CHECK: paciasp
|
||||
;; CHECK: retaa
|
||||
|
||||
define i32 @f3(i32 %x) #3 {
|
||||
entry:
|
||||
%call = tail call i32 @g(i32 %x) #5
|
||||
%add = add nsw i32 %call, 1
|
||||
ret i32 %add
|
||||
}
|
||||
;; CHECK-LABEL: f3:
|
||||
;; CHECK: pacibsp
|
||||
;; CHECK: retab
|
||||
|
||||
define i32 @f4(i32 %x) #4 {
|
||||
entry:
|
||||
ret i32 1
|
||||
}
|
||||
;; CHECK-LABEL: f4:
|
||||
;; CHECK: paciasp
|
||||
;; CHECK: retaa
|
||||
|
||||
define i32 @f5(i32 %x) #5 {
|
||||
entry:
|
||||
%call = tail call i32 @g(i32 %x) #5
|
||||
%add = add nsw i32 %call, 1
|
||||
ret i32 %add
|
||||
}
|
||||
;; CHECK-LABEL: f5:
|
||||
;; CHECK: paciasp
|
||||
;; CHECK: retaa
|
||||
|
||||
attributes #0 = { nounwind "branch-target-enforcement"="false" "sign-return-address"="none" }
|
||||
attributes #1 = { nounwind "branch-target-enforcement"="true" "sign-return-address"="none" }
|
||||
attributes #2 = { nounwind "branch-target-enforcement"="false" "sign-return-address"="non-leaf" "sign-return-address-key"="a_key" }
|
||||
attributes #3 = { nounwind "branch-target-enforcement"="false" "sign-return-address"="non-leaf" "sign-return-address-key"="b_key" }
|
||||
attributes #4 = { nounwind "branch-target-enforcement"="false" "sign-return-address"="all" "sign-return-address-key"="a_key" }
|
||||
attributes #5 = { nounwind }
|
||||
|
||||
!llvm.module.flags = !{!0, !1, !2, !3, !4}
|
||||
|
||||
!0 = !{i32 1, !"wchar_size", i32 4}
|
||||
!1 = !{i32 1, !"branch-target-enforcement", i32 1}
|
||||
!2 = !{i32 1, !"sign-return-address", i32 1}
|
||||
!3 = !{i32 1, !"sign-return-address-all", i32 0}
|
||||
!4 = !{i32 1, !"sign-return-address-with-bkey", i32 0}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
; RUN: llc -mtriple=aarch64 %s -o - | FileCheck %s
|
||||
|
||||
define void @f0() "patchable-function-entry"="0" "branch-target-enforcement" {
|
||||
define void @f0() "patchable-function-entry"="0" "branch-target-enforcement"="true" {
|
||||
; CHECK-LABEL: f0:
|
||||
; CHECK-NEXT: .Lfunc_begin0:
|
||||
; CHECK: // %bb.0:
|
||||
|
|
@ -12,7 +12,7 @@ define void @f0() "patchable-function-entry"="0" "branch-target-enforcement" {
|
|||
|
||||
;; -fpatchable-function-entry=1 -mbranch-protection=bti
|
||||
;; For M=0, place the label .Lpatch0 after the initial BTI.
|
||||
define void @f1() "patchable-function-entry"="1" "branch-target-enforcement" {
|
||||
define void @f1() "patchable-function-entry"="1" "branch-target-enforcement"="true" {
|
||||
; CHECK-LABEL: f1:
|
||||
; CHECK-NEXT: .Lfunc_begin1:
|
||||
; CHECK-NEXT: .cfi_startproc
|
||||
|
|
@ -28,7 +28,7 @@ define void @f1() "patchable-function-entry"="1" "branch-target-enforcement" {
|
|||
}
|
||||
|
||||
;; -fpatchable-function-entry=2,1 -mbranch-protection=bti
|
||||
define void @f2_1() "patchable-function-entry"="1" "patchable-function-prefix"="1" "branch-target-enforcement" {
|
||||
define void @f2_1() "patchable-function-entry"="1" "patchable-function-prefix"="1" "branch-target-enforcement"="true" {
|
||||
; CHECK-LABEL: .type f2_1,@function
|
||||
; CHECK-NEXT: .Ltmp0:
|
||||
; CHECK-NEXT: nop
|
||||
|
|
@ -50,7 +50,7 @@ define void @f2_1() "patchable-function-entry"="1" "patchable-function-prefix"="
|
|||
;; -fpatchable-function-entry=1 -mbranch-protection=bti
|
||||
;; For M=0, don't create .Lpatch0 if the initial instruction is not BTI,
|
||||
;; even if other basic blocks may have BTI.
|
||||
define internal void @f1i(i64 %v) "patchable-function-entry"="1" "branch-target-enforcement" {
|
||||
define internal void @f1i(i64 %v) "patchable-function-entry"="1" "branch-target-enforcement"="true" {
|
||||
; CHECK-LABEL: f1i:
|
||||
; CHECK-NEXT: .Lfunc_begin3:
|
||||
; CHECK: // %bb.0:
|
||||
|
|
|
|||
Loading…
Reference in New Issue