Skip to content

Commit 30bc4ca

Browse files
committed
[libunwind] Disable ZA before resuming from unwinding (Linux)
1 parent 4678f16 commit 30bc4ca

File tree

3 files changed

+85
-16
lines changed

3 files changed

+85
-16
lines changed

libunwind/src/Registers.hpp

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@
2020
#include "libunwind_ext.h"
2121
#include "shadow_stack_unwind.h"
2222

23+
#if __has_include(<sys/auxv.h>)
24+
#include <sys/auxv.h>
25+
#define HAVE_SYS_AUXV_H
26+
#endif
27+
2328
namespace libunwind {
2429

2530
// For emulating 128-bit registers
@@ -1828,6 +1833,7 @@ inline const char *Registers_ppc64::getRegisterName(int regNum) {
18281833
/// process.
18291834
class _LIBUNWIND_HIDDEN Registers_arm64;
18301835
extern "C" void __libunwind_Registers_arm64_jumpto(Registers_arm64 *);
1836+
extern "C" bool __libunwind_Registers_arm64_za_disable();
18311837

18321838
#if defined(_LIBUNWIND_USE_GCS)
18331839
extern "C" void *__libunwind_shstk_get_jump_target() {
@@ -1837,7 +1843,7 @@ extern "C" void *__libunwind_shstk_get_jump_target() {
18371843

18381844
class _LIBUNWIND_HIDDEN Registers_arm64 {
18391845
public:
1840-
Registers_arm64();
1846+
Registers_arm64() = default;
18411847
Registers_arm64(const void *registers);
18421848
Registers_arm64(const Registers_arm64 &);
18431849
Registers_arm64 &operator=(const Registers_arm64 &);
@@ -1855,7 +1861,10 @@ class _LIBUNWIND_HIDDEN Registers_arm64 {
18551861
v128 getVectorRegister(int num) const;
18561862
void setVectorRegister(int num, v128 value);
18571863
static const char *getRegisterName(int num);
1858-
void jumpto() { __libunwind_Registers_arm64_jumpto(this); }
1864+
void jumpto() {
1865+
zaDisable();
1866+
__libunwind_Registers_arm64_jumpto(this);
1867+
}
18591868
static constexpr int lastDwarfRegNum() {
18601869
return _LIBUNWIND_HIGHEST_DWARF_REGISTER_ARM64;
18611870
}
@@ -1908,25 +1917,43 @@ class _LIBUNWIND_HIDDEN Registers_arm64 {
19081917
private:
19091918
uint64_t lazyGetVG() const;
19101919

1920+
void zaDisable() const {
1921+
if (!_misc_registers.__has_sme)
1922+
return;
1923+
if (!__libunwind_Registers_arm64_za_disable())
1924+
_LIBUNWIND_ABORT("SME ZA disable failed");
1925+
}
1926+
1927+
static bool checkHasSME() {
1928+
#if defined(HAVE_SYS_AUXV_H)
1929+
constexpr int hwcap2_sme = (1 << 23);
1930+
unsigned long hwcap2 = getauxval(AT_HWCAP2);
1931+
return (hwcap2 & hwcap2_sme) != 0;
1932+
#endif
1933+
// TODO: Support other platforms.
1934+
return false;
1935+
}
1936+
19111937
struct GPRs {
1912-
uint64_t __x[29]; // x0-x28
1913-
uint64_t __fp; // Frame pointer x29
1914-
uint64_t __lr; // Link register x30
1915-
uint64_t __sp; // Stack pointer x31
1916-
uint64_t __pc; // Program counter
1917-
uint64_t __ra_sign_state; // RA sign state register
1938+
uint64_t __x[29] = {}; // x0-x28
1939+
uint64_t __fp = 0; // Frame pointer x29
1940+
uint64_t __lr = 0; // Link register x30
1941+
uint64_t __sp = 0; // Stack pointer x31
1942+
uint64_t __pc = 0; // Program counter
1943+
uint64_t __ra_sign_state = 0; // RA sign state register
19181944
};
19191945

19201946
struct Misc {
1921-
mutable uint64_t __vg = 0; // Vector Granule
1947+
mutable uint32_t __vg = 0; // Vector Granule
1948+
bool __has_sme = checkHasSME();
19221949
};
19231950

1924-
GPRs _registers;
1951+
GPRs _registers = {};
19251952
// Currently only the lower double in 128-bit vectore registers
19261953
// is perserved during unwinding. We could define new register
19271954
// numbers (> 96) which mean whole vector registers, then this
19281955
// struct would need to change to contain whole vector registers.
1929-
double _vectorHalfRegisters[32];
1956+
double _vectorHalfRegisters[32] = {};
19301957

19311958
// Miscellaneous/virtual registers. These are stored below the GPRs and FPRs
19321959
// 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) {
19711998
return *this;
19721999
}
19732000

1974-
inline Registers_arm64::Registers_arm64() {
1975-
memset(static_cast<void *>(this), 0, sizeof(*this));
1976-
}
1977-
19782001
inline bool Registers_arm64::validRegister(int regNum) const {
19792002
if (regNum == UNW_REG_IP)
19802003
return true;

libunwind/src/UnwindRegistersSave.S

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -827,6 +827,53 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext)
827827
ret
828828
#endif
829829

830+
//
831+
// extern "C" bool __libunwind_Registers_arm64_za_disable()
832+
//
833+
// On return:
834+
// success (true/false) is returned in x0
835+
//
836+
.p2align 2
837+
DEFINE_LIBUNWIND_FUNCTION(__libunwind_Registers_arm64_za_disable)
838+
// If TPIDR2_EL0 is null, the subroutine does nothing.
839+
.inst 0xd53bd0b0 // mrs x16, TPIDR2_EL0
840+
cbz x16, 1f
841+
842+
// If any of the reserved bytes in the first 16 bytes of the TPIDR2 block are
843+
// nonzero, return false (libunwind will then abort).
844+
ldrh w14, [x16, #10]
845+
cbnz w14, 2f
846+
ldr w14, [x16, #12]
847+
cbnz w14, 2f
848+
849+
// If za_save_buffer is NULL, the subroutine does nothing.
850+
ldr x14, [x16]
851+
cbz x14, 1f
852+
853+
// If num_za_save_slices is zero, the subroutine does nothing.
854+
ldrh w14, [x16, #8]
855+
cbz x14, 1f
856+
857+
mov x15, xzr
858+
ldr x16, [x16]
859+
0:
860+
.inst 0xe1206200 // str za[w15,0], [x16]
861+
.inst 0x04305830 // addsvl x16, x16, #1
862+
add x15, x15, #1
863+
cmp x14, x15
864+
b.ne 0b
865+
866+
// * Set TPIDR2_EL0 to null.
867+
.inst 0xd51bd0bf // msr TPIDR2_EL0, xzr
868+
// * Set PSTATE.ZA to 0.
869+
.inst 0xd503447f // smstop za
870+
1:
871+
mov x0, #1
872+
ret
873+
2:
874+
mov x0, #0
875+
ret
876+
830877
#elif defined(__arm__) && !defined(__APPLE__)
831878

832879
#if !defined(__ARM_ARCH_ISA_ARM)

libunwind/src/libunwind.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -514,4 +514,3 @@ bool logDWARF() {
514514
}
515515

516516
#endif // NDEBUG
517-

0 commit comments

Comments
 (0)