[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:
Momchil Velikov 2020-09-25 11:45:22 +01:00
parent f11f382523
commit a88c722e68
31 changed files with 439 additions and 316 deletions

View File

@ -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;
}

View File

@ -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"

View File

@ -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");
}
};

View File

@ -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"

View File

@ -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() {}

View File

@ -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"

View File

@ -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

View File

@ -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);
}
}

View File

@ -10,6 +10,7 @@
//
//===----------------------------------------------------------------------===//
#include "AArch64MachineFunctionInfo.h"
#include "AArch64TargetMachine.h"
#include "MCTargetDesc/AArch64AddressingModes.h"
#include "llvm/ADT/APSInt.h"

View File

@ -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 ||

View File

@ -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() }]>;

View File

@ -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; }));
}

View File

@ -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;

View File

@ -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;

View File

@ -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}}

View File

@ -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

View File

@ -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" }

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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" }

View File

@ -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

View File

@ -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

View File

@ -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}

View File

@ -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}

View File

@ -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}

View File

@ -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: