[libunwind] [libunwind] Disable ZA before resuming from unwinding (Linux) (PR #165451)
Benjamin Maxwell via cfe-commits
cfe-commits at lists.llvm.org
Tue Oct 28 11:10:00 PDT 2025
https://github.com/MacDue created https://github.com/llvm/llvm-project/pull/165451
None
>From 30bc4cabc424fb6c451c4dd2c7ddd9c5ed373949 Mon Sep 17 00:00:00 2001
From: Benjamin Maxwell <benjamin.maxwell at arm.com>
Date: Tue, 28 Oct 2025 17:23:23 +0000
Subject: [PATCH] [libunwind] Disable ZA before resuming from unwinding (Linux)
---
libunwind/src/Registers.hpp | 53 +++++++++++++++++++++--------
libunwind/src/UnwindRegistersSave.S | 47 +++++++++++++++++++++++++
libunwind/src/libunwind.cpp | 1 -
3 files changed, 85 insertions(+), 16 deletions(-)
diff --git a/libunwind/src/Registers.hpp b/libunwind/src/Registers.hpp
index 5a5b57835379a..e4530d58f9a04 100644
--- a/libunwind/src/Registers.hpp
+++ b/libunwind/src/Registers.hpp
@@ -20,6 +20,11 @@
#include "libunwind_ext.h"
#include "shadow_stack_unwind.h"
+#if __has_include(<sys/auxv.h>)
+#include <sys/auxv.h>
+#define HAVE_SYS_AUXV_H
+#endif
+
namespace libunwind {
// For emulating 128-bit registers
@@ -1828,6 +1833,7 @@ inline const char *Registers_ppc64::getRegisterName(int regNum) {
/// process.
class _LIBUNWIND_HIDDEN Registers_arm64;
extern "C" void __libunwind_Registers_arm64_jumpto(Registers_arm64 *);
+extern "C" bool __libunwind_Registers_arm64_za_disable();
#if defined(_LIBUNWIND_USE_GCS)
extern "C" void *__libunwind_shstk_get_jump_target() {
@@ -1837,7 +1843,7 @@ extern "C" void *__libunwind_shstk_get_jump_target() {
class _LIBUNWIND_HIDDEN Registers_arm64 {
public:
- Registers_arm64();
+ Registers_arm64() = default;
Registers_arm64(const void *registers);
Registers_arm64(const Registers_arm64 &);
Registers_arm64 &operator=(const Registers_arm64 &);
@@ -1855,7 +1861,10 @@ class _LIBUNWIND_HIDDEN Registers_arm64 {
v128 getVectorRegister(int num) const;
void setVectorRegister(int num, v128 value);
static const char *getRegisterName(int num);
- void jumpto() { __libunwind_Registers_arm64_jumpto(this); }
+ void jumpto() {
+ zaDisable();
+ __libunwind_Registers_arm64_jumpto(this);
+ }
static constexpr int lastDwarfRegNum() {
return _LIBUNWIND_HIGHEST_DWARF_REGISTER_ARM64;
}
@@ -1908,25 +1917,43 @@ class _LIBUNWIND_HIDDEN Registers_arm64 {
private:
uint64_t lazyGetVG() const;
+ void zaDisable() const {
+ if (!_misc_registers.__has_sme)
+ return;
+ if (!__libunwind_Registers_arm64_za_disable())
+ _LIBUNWIND_ABORT("SME ZA disable failed");
+ }
+
+ static bool checkHasSME() {
+#if defined(HAVE_SYS_AUXV_H)
+ constexpr int hwcap2_sme = (1 << 23);
+ unsigned long hwcap2 = getauxval(AT_HWCAP2);
+ return (hwcap2 & hwcap2_sme) != 0;
+#endif
+ // TODO: Support other platforms.
+ return false;
+ }
+
struct GPRs {
- uint64_t __x[29]; // x0-x28
- uint64_t __fp; // Frame pointer x29
- uint64_t __lr; // Link register x30
- uint64_t __sp; // Stack pointer x31
- uint64_t __pc; // Program counter
- uint64_t __ra_sign_state; // RA sign state register
+ uint64_t __x[29] = {}; // x0-x28
+ uint64_t __fp = 0; // Frame pointer x29
+ uint64_t __lr = 0; // Link register x30
+ uint64_t __sp = 0; // Stack pointer x31
+ uint64_t __pc = 0; // Program counter
+ uint64_t __ra_sign_state = 0; // RA sign state register
};
struct Misc {
- mutable uint64_t __vg = 0; // Vector Granule
+ mutable uint32_t __vg = 0; // Vector Granule
+ bool __has_sme = checkHasSME();
};
- GPRs _registers;
+ GPRs _registers = {};
// Currently only the lower double in 128-bit vectore registers
// is perserved during unwinding. We could define new register
// numbers (> 96) which mean whole vector registers, then this
// struct would need to change to contain whole vector registers.
- double _vectorHalfRegisters[32];
+ double _vectorHalfRegisters[32] = {};
// Miscellaneous/virtual registers. These are stored below the GPRs and FPRs
// as they do not correspond to physical registers, so do not need to be
@@ -1971,10 +1998,6 @@ Registers_arm64::operator=(const Registers_arm64 &other) {
return *this;
}
-inline Registers_arm64::Registers_arm64() {
- memset(static_cast<void *>(this), 0, sizeof(*this));
-}
-
inline bool Registers_arm64::validRegister(int regNum) const {
if (regNum == UNW_REG_IP)
return true;
diff --git a/libunwind/src/UnwindRegistersSave.S b/libunwind/src/UnwindRegistersSave.S
index 619a59751151e..1a19c5fffa3af 100644
--- a/libunwind/src/UnwindRegistersSave.S
+++ b/libunwind/src/UnwindRegistersSave.S
@@ -827,6 +827,53 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext)
ret
#endif
+//
+// extern "C" bool __libunwind_Registers_arm64_za_disable()
+//
+// On return:
+// success (true/false) is returned in x0
+//
+ .p2align 2
+DEFINE_LIBUNWIND_FUNCTION(__libunwind_Registers_arm64_za_disable)
+ // If TPIDR2_EL0 is null, the subroutine does nothing.
+ .inst 0xd53bd0b0 // mrs x16, TPIDR2_EL0
+ cbz x16, 1f
+
+ // If any of the reserved bytes in the first 16 bytes of the TPIDR2 block are
+ // nonzero, return false (libunwind will then abort).
+ ldrh w14, [x16, #10]
+ cbnz w14, 2f
+ ldr w14, [x16, #12]
+ cbnz w14, 2f
+
+ // If za_save_buffer is NULL, the subroutine does nothing.
+ ldr x14, [x16]
+ cbz x14, 1f
+
+ // If num_za_save_slices is zero, the subroutine does nothing.
+ ldrh w14, [x16, #8]
+ cbz x14, 1f
+
+ mov x15, xzr
+ ldr x16, [x16]
+0:
+ .inst 0xe1206200 // str za[w15,0], [x16]
+ .inst 0x04305830 // addsvl x16, x16, #1
+ add x15, x15, #1
+ cmp x14, x15
+ b.ne 0b
+
+ // * Set TPIDR2_EL0 to null.
+ .inst 0xd51bd0bf // msr TPIDR2_EL0, xzr
+ // * Set PSTATE.ZA to 0.
+ .inst 0xd503447f // smstop za
+1:
+ mov x0, #1
+ ret
+2:
+ mov x0, #0
+ ret
+
#elif defined(__arm__) && !defined(__APPLE__)
#if !defined(__ARM_ARCH_ISA_ARM)
diff --git a/libunwind/src/libunwind.cpp b/libunwind/src/libunwind.cpp
index 951d87db868bc..c2384becbd310 100644
--- a/libunwind/src/libunwind.cpp
+++ b/libunwind/src/libunwind.cpp
@@ -514,4 +514,3 @@ bool logDWARF() {
}
#endif // NDEBUG
-
More information about the cfe-commits
mailing list