[clang] [AARCH64] Support TPIDR_EL0 and TPIDRRO_EL0 as stack protector sysregs (PR #188054)
Ard Biesheuvel via cfe-commits
cfe-commits at lists.llvm.org
Mon Mar 23 08:40:55 PDT 2026
https://github.com/ardbiesheuvel created https://github.com/llvm/llvm-project/pull/188054
Even though the command line option suggests that arbitrary system registers may be chosen, the sysreg option for the stack protector guard currently only permits SP_EL0, as this is what the Linux kernel uses.
While it makes no sense to permit arbitrary system registers here (which usually have side effects), there is a desire to switch to TPIDR_EL0 or TPIDRRO_EL0 from the Linux side, both of which are part of the base v8.0 AArch64 ISA, and can hold arbitrary 64-bit values without side effects.
So add TPIDR_EL0 and TPIDDRO_EL0 to the set of accepted arguments for the -mstack-protected-guard-reg= command line option. For good measure, add TPIDR_EL1, TPIDR_EL2, FAR_EL1 and FAR_EL2 as well, all of which could potentially be useful to privileged software such as the Linux kernel to stash a per-thread pointer to the stack protector guard value.
>From 48a1e7e4641efe93866673d4a113aa48b25b9e7d Mon Sep 17 00:00:00 2001
From: Ard Biesheuvel <ardb at kernel.org>
Date: Mon, 23 Mar 2026 15:38:52 +0100
Subject: [PATCH] [AARCH64] Support TPIDR_EL0 and TPIDRRO_EL0 as stack
protector sysregs
Even though the command line option suggests that arbitrary system
registers may be chosen, the sysreg option for the stack protector guard
currently only permits SP_EL0, as this is what the Linux kernel uses.
While it makes no sense to permit arbitrary system registers here (which
usually have side effects), there is a desire to switch to TPIDR_EL0 or
TPIDRRO_EL0 from the Linux side, both of which are part of the base v8.0
AArch64 ISA, and can hold arbitrary 64-bit values without side effects.
So add TPIDR_EL0 and TPIDDRO_EL0 to the set of accepted arguments for
the -mstack-protected-guard-reg= command line option. For good measure,
add TPIDR_EL1, TPIDR_EL2, FAR_EL1 and FAR_EL2 as well, all of which
could potentially be useful to privileged software such as the Linux
kernel to stash a per-thread pointer to the stack protector guard value.
Signed-off-by: Ard Biesheuvel <ardb at kernel.org>
---
clang/lib/Driver/ToolChains/Clang.cpp | 9 ++++++--
clang/test/CodeGen/stack-protector-guard.c | 26 +++++++++++++++++-----
clang/test/Driver/stack-protector-guard.c | 7 +++++-
3 files changed, 34 insertions(+), 8 deletions(-)
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 6416baf9126ff..36b93b0f50583 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -3588,8 +3588,13 @@ static void RenderSSPOptions(const Driver &D, const ToolChain &TC,
<< A->getOption().getName() << Value << "fs gs";
return;
}
- if (EffectiveTriple.isAArch64() && Value != "sp_el0") {
- D.Diag(diag::err_drv_invalid_value) << A->getOption().getName() << Value;
+ if (EffectiveTriple.isAArch64() &&
+ (Value != "sp_el0" && Value != "tpidrro_el0" && Value != "tpidr_el0" &&
+ Value != "tpidr_el1" && Value != "tpidr_el2" && Value != "far_el1" &&
+ Value != "far_el2")) {
+ D.Diag(diag::err_drv_invalid_value_with_suggestion)
+ << A->getOption().getName() << Value
+ << "sp_el0 tpidrro_el0 tpidr_el0 tpidr_el1 tpidr_el2 far_el1 far_el2";
return;
}
if (EffectiveTriple.isRISCV() && Value != "tp") {
diff --git a/clang/test/CodeGen/stack-protector-guard.c b/clang/test/CodeGen/stack-protector-guard.c
index 0a4da13eed95c..49358da3f558e 100644
--- a/clang/test/CodeGen/stack-protector-guard.c
+++ b/clang/test/CodeGen/stack-protector-guard.c
@@ -8,7 +8,13 @@
// RUN: -mstack-protector-guard-offset=1024 -emit-llvm %s -o - | FileCheck %s
// RUN: %clang_cc1 -mstack-protector-guard=sysreg -triple aarch64-linux-gnu \
// RUN: -mstack-protector-guard-offset=1024 -mstack-protector-guard-reg=sp_el0 \
-// RUN: -emit-llvm %s -o - | FileCheck %s --check-prefix=AARCH64
+// RUN: -emit-llvm %s -o - | FileCheck %s --check-prefix=AARCH64-SP
+// RUN: %clang_cc1 -mstack-protector-guard=sysreg -triple aarch64-linux-gnu \
+// RUN: -mstack-protector-guard-offset=1024 -mstack-protector-guard-reg=tpidr_el0 \
+// RUN: -emit-llvm %s -o - | FileCheck %s --check-prefix=AARCH64-TPIDR
+// RUN: %clang_cc1 -mstack-protector-guard=sysreg -triple aarch64-linux-gnu \
+// RUN: -mstack-protector-guard-offset=1024 -mstack-protector-guard-reg=tpidrro_el0 \
+// RUN: -emit-llvm %s -o - | FileCheck %s --check-prefix=AARCH64-TPIDRRO
// RUN: %clang_cc1 -mstack-protector-guard=tls -triple riscv64-unknown-elf \
// RUN: -mstack-protector-guard-offset=44 -mstack-protector-guard-reg=tp \
// RUN: -emit-llvm %s -o - | FileCheck %s --check-prefix=RISCV
@@ -28,10 +34,20 @@ void bar(int x) {
// CHECK: [[ATTR1]] = !{i32 1, !"stack-protector-guard", !"sysreg"}
// CHECK: [[ATTR2]] = !{i32 1, !"stack-protector-guard-offset", i32 1024}
-// AARCH64: !llvm.module.flags = !{{{.*}}[[ATTR1:![0-9]+]], [[ATTR2:![0-9]+]], [[ATTR3:![0-9]+]]}
-// AARCH64: [[ATTR1]] = !{i32 1, !"stack-protector-guard", !"sysreg"}
-// AARCH64: [[ATTR2]] = !{i32 1, !"stack-protector-guard-reg", !"sp_el0"}
-// AARCH64: [[ATTR3]] = !{i32 1, !"stack-protector-guard-offset", i32 1024}
+// AARCH64-SP: !llvm.module.flags = !{{{.*}}[[ATTR1:![0-9]+]], [[ATTR2:![0-9]+]], [[ATTR3:![0-9]+]]}
+// AARCH64-SP: [[ATTR1]] = !{i32 1, !"stack-protector-guard", !"sysreg"}
+// AARCH64-SP: [[ATTR2]] = !{i32 1, !"stack-protector-guard-reg", !"sp_el0"}
+// AARCH64-SP: [[ATTR3]] = !{i32 1, !"stack-protector-guard-offset", i32 1024}
+
+// AARCH64-TPIDR: !llvm.module.flags = !{{{.*}}[[ATTR1:![0-9]+]], [[ATTR2:![0-9]+]], [[ATTR3:![0-9]+]]}
+// AARCH64-TPIDR: [[ATTR1]] = !{i32 1, !"stack-protector-guard", !"sysreg"}
+// AARCH64-TPIDR: [[ATTR2]] = !{i32 1, !"stack-protector-guard-reg", !"tpidr_el0"}
+// AARCH64-TPIDR: [[ATTR3]] = !{i32 1, !"stack-protector-guard-offset", i32 1024}
+
+// AARCH64-TPIDRRO: !llvm.module.flags = !{{{.*}}[[ATTR1:![0-9]+]], [[ATTR2:![0-9]+]], [[ATTR3:![0-9]+]]}
+// AARCH64-TPIDRRO: [[ATTR1]] = !{i32 1, !"stack-protector-guard", !"sysreg"}
+// AARCH64-TPIDRRO: [[ATTR2]] = !{i32 1, !"stack-protector-guard-reg", !"tpidrro_el0"}
+// AARCH64-TPIDRRO: [[ATTR3]] = !{i32 1, !"stack-protector-guard-offset", i32 1024}
// RISCV: !llvm.module.flags = !{{{.*}}[[ATTR1:![0-9]+]], [[ATTR2:![0-9]+]], [[ATTR3:![0-9]+]], [[ATTR4:![0-9]+]]}
// RISCV: [[ATTR1]] = !{i32 1, !"stack-protector-guard", !"tls"}
diff --git a/clang/test/Driver/stack-protector-guard.c b/clang/test/Driver/stack-protector-guard.c
index 666c83079e519..3b12a57f867e8 100644
--- a/clang/test/Driver/stack-protector-guard.c
+++ b/clang/test/Driver/stack-protector-guard.c
@@ -81,10 +81,15 @@
// RUN: -mstack-protector-guard-reg=foo \
// RUN: -mstack-protector-guard-offset=0 %s 2>&1 | \
// RUN: FileCheck -check-prefix=INVALID-REG-AARCH64 %s
+// RUN: %clang -### -target aarch64-linux-gnu -mstack-protector-guard=sysreg \
+// RUN: -mstack-protector-guard-reg=tpidr_el0 \
+// RUN: -mstack-protector-guard-offset=0 %s 2>&1 | \
+// RUN: FileCheck -check-prefix=CHECK-AARCH64-TPIDR %s
// CHECK-AARCH64: "-cc1" {{.*}}"-mstack-protector-guard=sysreg" "-mstack-protector-guard-offset=0" "-mstack-protector-guard-reg=sp_el0"
// INVALID-VALUE-AARCH64: error: invalid value 'tls' in 'mstack-protector-guard=', expected one of: sysreg global
-// INVALID-REG-AARCH64: error: invalid value 'foo' in 'mstack-protector-guard-reg='
+// INVALID-REG-AARCH64: error: invalid value 'foo' in 'mstack-protector-guard-reg=', expected one of: sp_el0 tpidrro_el0 tpidr_el0 tpidr_el1 tpidr_el2 far_el1 far_el2
+// CHECK-AARCH64-TPIDR: "-cc1" {{.*}}"-mstack-protector-guard=sysreg" "-mstack-protector-guard-offset=0" "-mstack-protector-guard-reg=tpidr_el0"
// RUN: %clang -### -target riscv64-unknown-elf -mstack-protector-guard=tls -mstack-protector-guard-offset=24 -mstack-protector-guard-reg=tp %s 2>&1 | \
// RUN: FileCheck -v -check-prefix=CHECK-TLS-RISCV %s
More information about the cfe-commits
mailing list