[AArch64][COFF] Branch-protection=standard/pac-ret means "b-key" on Windows (#203989)

On AArch64 Windows the `B-key` is the only supported key for userspace,
let's make "standard" and "pac-ret" options generate "b-key".

Fixes: #203852
(cherry picked from commit 68f703f3e58a52c41b39dfc654a675560b6c5614)
This commit is contained in:
Daniel Kiss authored and Douglas Yung committed 2026-07-17 07:48:53 +00:00
1 parent 66ba3bdaa2
commit 47e2df730a
17 files changed
+112 -66

No files matched your search

+3
View File
@@ -1041,6 +1041,9 @@ latest release, please see the [Clang Web Site](https://clang.llvm.org) or the
- Hisilicon hip12 core (hip12).
- NVIDIA Rigel core (rigel).
- On AArch64 Windows targets, `-mbranch-protection=standard` and `-mbranch-protection=pac-ret`
now uses the B-key by default.
#### Android Support
#### Windows Support
+30
View File
@@ -2774,6 +2774,36 @@ are listed below.
only. This only applies to the AArch64 architecture.
```
```{eval-rst}
.. option:: -mbranch-protection=features
Select the branch protection features to use for ARM and AArch64 targets.
``features`` can be ``none``, ``standard``, or a ``+``-separated list of
``bti``, ``gcs``, and ``pac-ret`` with optional ``pac-ret`` modifiers.
``none`` is the default and turns off all types of branch protection.
``standard`` turns on all branch protection features that are valid for the
target platform. If a feature has additional tuning options, ``standard``
sets them to their standard level.
``pac-ret`` turns on return address signing to its standard level: signing
functions that save the return address to memory (non-leaf functions
practically always do this) using the A-key. On AArch64 Windows targets,
``pac-ret`` uses the B-key by default.
``+leaf`` extends the ``pac-ret`` signing to include leaf functions.
``+b-key`` can be used to sign functions with the B-key instead of the A-key
on AArch64. ``b-key`` is accepted but ignored for ARM targets.
``+pc`` enables the PAuth-LR form of return address signing.
``bti`` turns on the Branch Target Identification mechanism.
``gcs`` turns on Guarded Control Stack compatible code generation.
```
```{eval-rst}
.. option:: -mcompact-branches=[values]
+2 -1
View File
@@ -254,7 +254,8 @@ bool AArch64TargetInfo::validateBranchProtection(StringRef Spec, StringRef,
const LangOptions &LO,
StringRef &Err) const {
llvm::ARM::ParsedBranchProtection PBP;
if (!llvm::ARM::parseBranchProtection(Spec, PBP, Err, HasPAuthLR))
if (!llvm::ARM::parseBranchProtection(Spec, PBP, Err, getTriple(),
HasPAuthLR))
return false;
// GCS is currently untested with ptrauth-returns, but enabling this could be
+1 -1
View File
@@ -375,7 +375,7 @@ bool ARMTargetInfo::validateBranchProtection(StringRef Spec, StringRef Arch,
const LangOptions &LO,
StringRef &Err) const {
llvm::ARM::ParsedBranchProtection PBP;
if (!llvm::ARM::parseBranchProtection(Spec, PBP, Err))
if (!llvm::ARM::parseBranchProtection(Spec, PBP, Err, getTriple()))
return false;
if (!isBranchProtectionSupportedArch(Arch))
+1 -1
View File
@@ -1445,7 +1445,7 @@ static void CollectARMPACBTIOptions(const ToolChain &TC, const ArgList &Args,
if (llvm::any_of(CmdArgs, isPAuthLR))
EnablePAuthLR = true;
}
if (!llvm::ARM::parseBranchProtection(A->getValue(), PBP, DiagMsg,
if (!llvm::ARM::parseBranchProtection(A->getValue(), PBP, DiagMsg, Triple,
EnablePAuthLR))
D.Diag(diag::err_drv_unsupported_option_argument)
<< A->getSpelling() << DiagMsg;
@@ -13,6 +13,8 @@
// RUN: | FileCheck %s --check-prefix=CHECK
// RUN: %clang_cc1 -triple aarch64 -emit-llvm -target-cpu generic -target-feature +v8.5a -mguarded-control-stack -mbranch-target-enforce -mbranch-protection-pauth-lr -msign-return-address=all -msign-return-address-key=a_key %s -o - \
// RUN: | FileCheck %s --check-prefix=CHECK
// RUN: %clang_cc1 -triple aarch64-windows-msvc -emit-llvm -target-cpu generic -target-feature +v8.5a %s -o - \
// RUN: | FileCheck %s --check-prefix=WIN
__attribute__ ((target("branch-protection=none")))
void none() {}
@@ -21,6 +23,7 @@ void none() {}
__attribute__ ((target("branch-protection=standard")))
void std() {}
// CHECK: define{{.*}} void @std() #[[#STD:]]
// WIN: define{{.*}} void @std() #[[#WINSTD:]]
__attribute__ ((target("branch-protection=bti")))
void btionly() {}
@@ -29,6 +32,7 @@ void btionly() {}
__attribute__ ((target("branch-protection=pac-ret")))
void paconly() {}
// CHECK: define{{.*}} void @paconly() #[[#PAC:]]
// WIN: define{{.*}} void @paconly() #[[#WINPAC:]]
__attribute__ ((target("branch-protection=pac-ret+bti")))
void pacbti0() {}
@@ -82,10 +86,12 @@ void gcs() {}
// CHECK-DAG: attributes #[[#NONE]] = { {{.*}}
// CHECK-DAG: attributes #[[#STD]] = { {{.*}} "branch-target-enforcement" "guarded-control-stack" {{.*}} "sign-return-address"="non-leaf" "sign-return-address-key"="a_key"
// WIN-DAG: attributes #[[#WINSTD]] = { {{.*}} "branch-target-enforcement" "guarded-control-stack" {{.*}} "sign-return-address"="non-leaf" "sign-return-address-key"="b_key"
// CHECK-DAG: attributes #[[#BTI]] = { {{.*}} "branch-target-enforcement"
// CHECK-DAG: attributes #[[#PAC]] = { {{.*}} "sign-return-address"="non-leaf" "sign-return-address-key"="a_key"
// WIN-DAG: attributes #[[#WINPAC]] = { {{.*}} "sign-return-address"="non-leaf" "sign-return-address-key"="b_key"
// CHECK-DAG: attributes #[[#PACLEAF]] = { {{.*}} "sign-return-address"="all" "sign-return-address-key"="a_key"
@@ -6,6 +6,8 @@
// RUN: %clang -target aarch64-none-elf -S -emit-llvm -o - -mbranch-protection=pac-ret+leaf %s | FileCheck %s --check-prefix=CHECK --check-prefix=ALL
// RUN: %clang -target aarch64-none-elf -S -emit-llvm -o - -mbranch-protection=pac-ret+b-key %s | FileCheck %s --check-prefix=CHECK --check-prefix=B-KEY
// RUN: %clang -target aarch64-none-elf -S -emit-llvm -o - -mbranch-protection=bti %s | FileCheck %s --check-prefix=CHECK --check-prefix=BTE
// RUN: %clang -target aarch64-windows-msvc -S -emit-llvm -o - -mbranch-protection=pac-ret %s | FileCheck %s --check-prefix=CHECK --check-prefix=WIN-PAC
// RUN: %clang -target aarch64-windows-msvc -S -emit-llvm -o - -mbranch-protection=standard %s | FileCheck %s --check-prefix=CHECK --check-prefix=WIN-STD
// REQUIRES: aarch64-registered-target
@@ -21,6 +23,8 @@
// PART: attributes #[[#ATTR]] = { {{.*}} "sign-return-address-key"="a_key"
// B-KEY: attributes #[[#ATTR]] = { {{.*}} "sign-return-address-key"="b_key"
// BTE: attributes #[[#ATTR]] = { {{.*}} "branch-target-enforcement"
// WIN-PAC: attributes #[[#ATTR]] = { {{.*}} "sign-return-address"="non-leaf" "sign-return-address-key"="b_key"
// WIN-STD: attributes #[[#ATTR]] = { {{.*}} "branch-target-enforcement" {{.*}} "guarded-control-stack" {{.*}} "sign-return-address"="non-leaf" "sign-return-address-key"="b_key"
// Check module attributes
@@ -30,23 +34,34 @@
// PART-NOT: !"branch-target-enforcement"
// BTE: !{i32 8, !"branch-target-enforcement", i32 2}
// B-KEY-NOT: !"branch-target-enforcement"
// WIN-PAC-NOT: !"branch-target-enforcement"
// WIN-STD: !{i32 8, !"branch-target-enforcement", i32 2}
// WIN-PAC-NOT: !"guarded-control-stack"
// WIN-STD: !{i32 8, !"guarded-control-stack", i32 2}
// NONE-NOT: !"sign-return-address"
// ALL: !{i32 8, !"sign-return-address", i32 2}
// PART: !{i32 8, !"sign-return-address", i32 2}
// BTE-NOT: !"sign-return-address"
// B-KEY: !{i32 8, !"sign-return-address", i32 2}
// WIN-PAC: !{i32 8, !"sign-return-address", i32 2}
// WIN-STD: !{i32 8, !"sign-return-address", i32 2}
// NONE-NOT: !"sign-return-address-all"
// ALL: !{i32 8, !"sign-return-address-all", i32 2}
// PART-NOT: !"sign-return-address-all"
// BTE-NOT: !"sign-return-address-all"
// B-KEY-NOT: !"sign-return-address-all"
// WIN-PAC-NOT: !"sign-return-address-all"
// WIN-STD-NOT: !"sign-return-address-all"
// NONE-NOT: !"sign-return-address-with-bkey"
// ALL-NOT: !"sign-return-address-with-bkey"
// PART-NOT: !"sign-return-address-with-bkey"
// BTE-NOT: !"sign-return-address-with-bkey"
// B-KEY: !{i32 8, !"sign-return-address-with-bkey", i32 2}
// WIN-PAC: !{i32 8, !"sign-return-address-with-bkey", i32 2}
// WIN-STD: !{i32 8, !"sign-return-address-with-bkey", i32 2}
void foo() {}
@@ -13,6 +13,9 @@
// RUN: %clang --target=aarch64 -c %s -### -mbranch-protection=standard 2>&1 | \
// RUN: FileCheck %s --check-prefix=RA-NON-LEAF --check-prefix=KEY-A --check-prefix=BTE-ON --check-prefix=GCS-ON --check-prefix=WARN
// RUN: %clang --target=aarch64-windows-msvc -c %s -### -mbranch-protection=standard 2>&1 | \
// RUN: FileCheck %s --check-prefix=RA-NON-LEAF --check-prefix=KEY-B --check-prefix=BTE-ON --check-prefix=GCS-ON --check-prefix=WARN
// If the -msign-return-address and -mbranch-protection are both used, the
// right-most one controls return address signing.
// RUN: %clang --target=aarch64 -c %s -### -msign-return-address=non-leaf -mbranch-protection=none 2>&1 | \
@@ -37,6 +40,7 @@
// RA-ALL: "-msign-return-address=all"
// KEY-A: "-msign-return-address-key=a_key"
// KEY-B: "-msign-return-address-key=b_key"
// KEY-NOT: "-msign-return-address-key"
// BTE-OFF-NOT: "-mbranch-target-enforce"
@@ -541,6 +541,7 @@
// RUN: %clang -target arm64-none-linux-gnu -march=armv8-a -mbranch-protection=pac-ret+b-key -x c -E -dM %s -o - | FileCheck -check-prefix=CHECK-PAUTH-BKEY %s
// RUN: %clang -target arm64-none-linux-gnu -march=armv8-a -mbranch-protection=pac-ret+leaf -x c -E -dM %s -o - | FileCheck -check-prefix=CHECK-PAUTH-ALL %s
// RUN: %clang -target arm64-none-linux-gnu -march=armv8-a -mbranch-protection=pac-ret+leaf+b-key -x c -E -dM %s -o - | FileCheck -check-prefix=CHECK-PAUTH-BKEY-ALL %s
// RUN: %clang -target aarch64-windows-msvc -march=armv8-a -mbranch-protection=standard -x c -E -dM %s -o - | FileCheck -check-prefix=CHECK-BRANCH-PROTECTION-STANDARD-WINDOWS %s
//
// Note: PAUTH-OFF - pac-ret is disabled
// CPU-NOPAUTH - FEAT_PAUTH support is disabled (but pac-ret can still use HINT-encoded instructions)
@@ -553,6 +554,9 @@
// CHECK-PAUTH-BKEY-ALL: #define __ARM_FEATURE_PAC_DEFAULT 6
// CHECK-CPU-PAUTH: #define __ARM_FEATURE_PAUTH 1
// CHECK-CPU-NOPAUTH-NOT: __ARM_FEATURE_PAUTH
// CHECK-BRANCH-PROTECTION-STANDARD-WINDOWS-DAG: #define __ARM_FEATURE_BTI_DEFAULT 1
// CHECK-BRANCH-PROTECTION-STANDARD-WINDOWS-DAG: #define __ARM_FEATURE_GCS_DEFAULT 1
// CHECK-BRANCH-PROTECTION-STANDARD-WINDOWS-DAG: #define __ARM_FEATURE_PAC_DEFAULT 2
// ================== Check Branch Target Identification (BTI).
// RUN: %clang -target arm64-none-linux-gnu -march=armv8-a -x c -E -dM %s -o - | FileCheck -check-prefix=CHECK-BTI-OFF %s
+2
View File
@@ -203,6 +203,8 @@ Makes programs 10x faster by doing Special New Thing.
instead of the ABI names FP and LR. Note that LLVM IR produced by Clang
always uses the ABI names, but other frontends may not.
([#167783](https://github.com/llvm/llvm-project/pull/167783))
* On AArch64 Windows targets, return address signing now uses the B-key by
default because Windows unwind information only supports B-key signing.
### Changes to the AMDGPU Backend
@@ -15,6 +15,7 @@
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Compiler.h"
#include "llvm/TargetParser/Triple.h"
namespace llvm {
namespace ARM {
@@ -47,7 +48,8 @@ struct ParsedBranchProtection {
};
LLVM_ABI bool parseBranchProtection(StringRef Spec, ParsedBranchProtection &PBP,
StringRef &Err, bool EnablePAuthLR = false);
StringRef &Err, const llvm::Triple &Triple,
bool EnablePAuthLR = false);
} // namespace ARM
} // namespace llvm
@@ -18,6 +18,7 @@
#include "AArch64Subtarget.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DiagnosticInfo.h"
#include "llvm/IR/Metadata.h"
#include "llvm/IR/Module.h"
#include "llvm/MC/MCAsmInfo.h"
@@ -93,6 +94,12 @@ static bool ShouldSignWithBKey(const Function &F, const AArch64Subtarget &STI) {
const StringRef Key =
F.getFnAttribute("sign-return-address-key").getValueAsString();
assert(Key == "a_key" || Key == "b_key");
if (STI.getTargetTriple().isOSWindows() && Key == "a_key" &&
GetSignReturnAddress(F) != SignReturnAddress::None) {
F.getContext().diagnose(DiagnosticInfoUnsupported{
F, "A-key return address signing is unsupported on AArch64 Windows"});
return true;
}
return Key == "b_key";
}
@@ -175,6 +175,8 @@ void AArch64PointerAuthImpl::signLR(MachineFunction &MF,
}
if (!EmitCFI && NeedsWinCFI) {
assert(UseBKey &&
"Windows SEH PAC unwind info only supports B-key signing");
BuildMI(MBB, MBBI, DL, TII->get(AArch64::SEH_PACSignLR))
.setMIFlag(MachineInstr::FrameSetup);
}
@@ -336,6 +338,8 @@ void AArch64PointerAuthImpl::authenticateLR(
}
if (NeedsWinCFI) {
assert(UseBKey &&
"Windows SEH PAC unwind info only supports B-key signing");
BuildMI(MBB, MBBI, DL, TII->get(AArch64::SEH_PACSignLR))
.setMIFlag(MachineInstr::FrameDestroy);
}
@@ -141,7 +141,8 @@ ARM::EndianKind ARM::parseArchEndian(StringRef Arch) {
// returned in `PBP`. Returns false in error, with `Err` containing
// an erroneous part of the spec.
bool ARM::parseBranchProtection(StringRef Spec, ParsedBranchProtection &PBP,
StringRef &Err, bool EnablePAuthLR) {
StringRef &Err, const llvm::Triple &Triple,
bool EnablePAuthLR) {
PBP = {"none", "a_key", false, false, false};
if (Spec == "none")
return true; // defaults are ok
@@ -151,6 +152,8 @@ bool ARM::parseBranchProtection(StringRef Spec, ParsedBranchProtection &PBP,
PBP.BranchTargetEnforcement = true;
PBP.GuardedControlStack = true;
PBP.BranchProtectionPAuthLR = EnablePAuthLR;
if (Triple.isAArch64() && Triple.isOSWindows())
PBP.Key = "b_key";
return true;
}
@@ -164,6 +167,9 @@ bool ARM::parseBranchProtection(StringRef Spec, ParsedBranchProtection &PBP,
}
if (Opt == "pac-ret") {
PBP.Scope = "non-leaf";
if (Triple.isAArch64() && Triple.isOSWindows())
PBP.Key = "b_key";
for (; I + 1 != E; ++I) {
StringRef PACOpt = Opts[I + 1].trim();
if (PACOpt == "leaf")
@@ -5,14 +5,21 @@
; v9.5-A is not expected to change codegen without -mbranch-protection=+pc, so reuse DWARFCFI-V83A.
; RUN: llc -mtriple=aarch64 -mattr=v9.5a < %s | FileCheck --check-prefixes=CHECK,DWARFCFI,DWARFCFI-V83A %s
; RUN: llc -mtriple=aarch64-windows < %s | FileCheck --check-prefixes=CHECK,WINCFI,WINCFI-COMPAT %s
; RUN: llc -mtriple=aarch64-windows -mattr=v8.3a < %s | FileCheck --check-prefixes=CHECK,WINCFI,WINCFI-V83A %s
; RUN: sed -e '/^define i32 @leaf_sign_all_a_key(/,/^}/d' -e '/^define i32 @leaf_sign_all_a_key_bti(/,/^}/d' %s > %t.win-valid.ll
; RUN: llc -mtriple=aarch64-windows < %t.win-valid.ll | FileCheck --check-prefixes=CHECK,WINCFI,WINCFI-COMPAT %s
; RUN: llc -mtriple=aarch64-windows -mattr=v8.3a < %t.win-valid.ll | FileCheck --check-prefixes=CHECK,WINCFI,WINCFI-V83A %s
; RUN: sed -n -e '/^define i32 @leaf_sign_all_a_key(/,/^}/p' %s | not llc -mtriple=aarch64-windows -filetype=null 2>&1 | FileCheck --check-prefix=ERR-AKEY %s
; Make sure no errors are detected when emitting SEH opcodes.
; Errors are only checked for when generating a binary, so emit a dummy object
; file and make sure llc produces zero exit code.
; RUN: llc -mtriple=aarch64-windows -o %t.dummy.o --filetype=obj < %s
; RUN: llc -mtriple=aarch64-windows -mattr=v8.3a -o %t.dummy.o --filetype=obj < %s
; RUN: llc -mtriple=aarch64-windows -o %t.dummy.o --filetype=obj < %t.win-valid.ll
; RUN: llc -mtriple=aarch64-windows -mattr=v8.3a -o %t.dummy.o --filetype=obj < %t.win-valid.ll
; ERR-AKEY: error:
; ERR-AKEY-SAME: in function leaf_sign_all_a_key
; ERR-AKEY-SAME: A-key return address signing is unsupported on AArch64 Windows
define i32 @leaf(i32 %x) {
; CHECK-LABEL: leaf:
@@ -649,34 +656,6 @@ define i32 @leaf_sign_all_a_key(i32 %x) "sign-return-address"="all" "sign-return
; DWARFCFI-V83A-NEXT: paciasp
; DWARFCFI-V83A-NEXT: .cfi_negate_ra_state
; DWARFCFI-V83A-NEXT: retaa
;
; WINCFI-COMPAT-LABEL: leaf_sign_all_a_key:
; WINCFI-COMPAT: .seh_proc leaf_sign_all_a_key
; WINCFI-COMPAT-NEXT: // %bb.0:
; WINCFI-COMPAT-NEXT: hint #25
; WINCFI-COMPAT-NEXT: .seh_pac_sign_lr
; WINCFI-COMPAT-NEXT: .seh_endprologue
; WINCFI-COMPAT-NEXT: .seh_startepilogue
; WINCFI-COMPAT-NEXT: hint #29
; WINCFI-COMPAT-NEXT: .seh_pac_sign_lr
; WINCFI-COMPAT-NEXT: .seh_endepilogue
; WINCFI-COMPAT-NEXT: ret
; WINCFI-COMPAT-NEXT: .seh_endfunclet
; WINCFI-COMPAT-NEXT: .seh_endproc
;
; WINCFI-V83A-LABEL: leaf_sign_all_a_key:
; WINCFI-V83A: .seh_proc leaf_sign_all_a_key
; WINCFI-V83A-NEXT: // %bb.0:
; WINCFI-V83A-NEXT: paciasp
; WINCFI-V83A-NEXT: .seh_pac_sign_lr
; WINCFI-V83A-NEXT: .seh_endprologue
; WINCFI-V83A-NEXT: .seh_startepilogue
; WINCFI-V83A-NEXT: autiasp
; WINCFI-V83A-NEXT: .seh_pac_sign_lr
; WINCFI-V83A-NEXT: .seh_endepilogue
; WINCFI-V83A-NEXT: ret
; WINCFI-V83A-NEXT: .seh_endfunclet
; WINCFI-V83A-NEXT: .seh_endproc
ret i32 %x
}
@@ -764,34 +743,6 @@ define i32 @leaf_sign_all_a_key_bti(i32 %x) "sign-return-address"="all" "sign-re
; DWARFCFI-V83A-NEXT: paciasp
; DWARFCFI-V83A-NEXT: .cfi_negate_ra_state
; DWARFCFI-V83A-NEXT: retaa
;
; WINCFI-COMPAT-LABEL: leaf_sign_all_a_key_bti:
; WINCFI-COMPAT: .seh_proc leaf_sign_all_a_key_bti
; WINCFI-COMPAT-NEXT: // %bb.0:
; WINCFI-COMPAT-NEXT: hint #25
; WINCFI-COMPAT-NEXT: .seh_pac_sign_lr
; WINCFI-COMPAT-NEXT: .seh_endprologue
; WINCFI-COMPAT-NEXT: .seh_startepilogue
; WINCFI-COMPAT-NEXT: hint #29
; WINCFI-COMPAT-NEXT: .seh_pac_sign_lr
; WINCFI-COMPAT-NEXT: .seh_endepilogue
; WINCFI-COMPAT-NEXT: ret
; WINCFI-COMPAT-NEXT: .seh_endfunclet
; WINCFI-COMPAT-NEXT: .seh_endproc
;
; WINCFI-V83A-LABEL: leaf_sign_all_a_key_bti:
; WINCFI-V83A: .seh_proc leaf_sign_all_a_key_bti
; WINCFI-V83A-NEXT: // %bb.0:
; WINCFI-V83A-NEXT: paciasp
; WINCFI-V83A-NEXT: .seh_pac_sign_lr
; WINCFI-V83A-NEXT: .seh_endprologue
; WINCFI-V83A-NEXT: .seh_startepilogue
; WINCFI-V83A-NEXT: autiasp
; WINCFI-V83A-NEXT: .seh_pac_sign_lr
; WINCFI-V83A-NEXT: .seh_endepilogue
; WINCFI-V83A-NEXT: ret
; WINCFI-V83A-NEXT: .seh_endfunclet
; WINCFI-V83A-NEXT: .seh_endproc
ret i32 %x
}
@@ -0,0 +1,11 @@
; RUN: not llc -mtriple=aarch64-windows-msvc -filetype=null %s 2>&1 | FileCheck %s
; CHECK: error:
; CHECK-SAME: A-key return address signing is unsupported on AArch64 Windows
define void @a_key() "sign-return-address"="non-leaf" "sign-return-address-key"="a_key" {
call void @callee()
ret void
}
declare void @callee()
+1 -1
View File
@@ -1,6 +1,6 @@
; RUN: llc < %s -mtriple=aarch64-windows -aarch64-min-jump-table-entries=4 | FileCheck %s
define dso_local i32 @func(i32 %in) "sign-return-address"="non-leaf" "sign-return-address-key"="a_key" "branch-target-enforcement" {
define dso_local i32 @func(i32 %in) "sign-return-address"="non-leaf" "sign-return-address-key"="b_key" "branch-target-enforcement" {
entry:
call void asm sideeffect "", "~{x19}"()
switch i32 %in, label %def [