Skip to content

Commit 8d54ded

Browse files
committed
Rework and add test
1 parent 3984605 commit 8d54ded

File tree

3 files changed

+148
-28
lines changed

3 files changed

+148
-28
lines changed

libunwind/src/Registers.hpp

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1829,6 +1829,10 @@ inline const char *Registers_ppc64::getRegisterName(int regNum) {
18291829
class _LIBUNWIND_HIDDEN Registers_arm64;
18301830
extern "C" void __libunwind_Registers_arm64_jumpto(Registers_arm64 *);
18311831

1832+
#if !defined(__APPLE__)
1833+
extern "C" void __attribute__((weak)) __arm_za_disable();
1834+
#endif
1835+
18321836
#if defined(_LIBUNWIND_USE_GCS)
18331837
extern "C" void *__libunwind_shstk_get_jump_target() {
18341838
return reinterpret_cast<void *>(&__libunwind_Registers_arm64_jumpto);
@@ -1855,7 +1859,7 @@ class _LIBUNWIND_HIDDEN Registers_arm64 {
18551859
v128 getVectorRegister(int num) const;
18561860
void setVectorRegister(int num, v128 value);
18571861
static const char *getRegisterName(int num);
1858-
void jumpto() { __libunwind_Registers_arm64_jumpto(this); }
1862+
void jumpto();
18591863
static constexpr int lastDwarfRegNum() {
18601864
return _LIBUNWIND_HIGHEST_DWARF_REGISTER_ARM64;
18611865
}
@@ -1971,6 +1975,32 @@ Registers_arm64::operator=(const Registers_arm64 &other) {
19711975
return *this;
19721976
}
19731977

1978+
void Registers_arm64::jumpto() {
1979+
#if !defined(__APPLE__)
1980+
// The platform must ensure that all the following conditions are true on
1981+
// entry to EH:
1982+
//
1983+
// - PSTATE.SM is 0.
1984+
// - PSTATE.ZA is 0.
1985+
// - TPIDR2_EL0 is null.
1986+
//
1987+
// The first point is ensured by routines for throwing exceptions having a
1988+
// non-streaming interface. TPIDR2_EL0 is set to null and ZA disabled by
1989+
// calling __arm_za_disable.
1990+
//
1991+
// See:
1992+
// https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst#exceptions
1993+
if (__arm_za_disable) {
1994+
__arm_za_disable();
1995+
} else {
1996+
// FIXME: If SME is available and `__arm_za_disable` is not, this should
1997+
// abort.
1998+
_LIBUNWIND_DEBUG_LOG("failed to call __arm_za_disable in %s", __FUNCTION__);
1999+
}
2000+
#endif
2001+
__libunwind_Registers_arm64_jumpto(this);
2002+
}
2003+
19742004
inline Registers_arm64::Registers_arm64() {
19752005
memset(static_cast<void *>(this), 0, sizeof(*this));
19762006
}

libunwind/src/UnwindLevel1.c

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -202,10 +202,6 @@ unwind_phase1(unw_context_t *uc, unw_cursor_t *cursor, _Unwind_Exception *except
202202
}
203203
extern int __unw_step_stage2(unw_cursor_t *);
204204

205-
#if defined(__aarch64__)
206-
extern void __attribute__((weak)) __arm_za_disable(void);
207-
#endif
208-
209205
#if defined(_LIBUNWIND_USE_GCS)
210206
// Enable the GCS target feature to permit gcspop instructions to be used.
211207
__attribute__((target("+gcs")))
@@ -218,29 +214,6 @@ unwind_phase2(unw_context_t *uc, unw_cursor_t *cursor,
218214
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_obj=%p)",
219215
(void *)exception_object);
220216

221-
#if defined(__aarch64__)
222-
// The platform must ensure that all the following conditions are true on
223-
// entry to EH:
224-
//
225-
// - PSTATE.SM is 0.
226-
// - PSTATE.ZA is 0.
227-
// - TPIDR2_EL0 is null.
228-
//
229-
// The first point is ensured by routines for throwing exceptions having a
230-
// non-streaming interface. TPIDR2_EL0 is set to null and ZA disabled by
231-
// calling __arm_za_disable.
232-
//
233-
// See:
234-
// https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst#exceptions
235-
if (__arm_za_disable) {
236-
__arm_za_disable();
237-
} else {
238-
// FIXME: If SME is available and `__arm_za_disable` is not, this should
239-
// abort.
240-
_LIBUNWIND_DEBUG_LOG("failed to call __arm_za_disable in %s", __FUNCTION__);
241-
}
242-
#endif
243-
244217
// uc is initialized by __unw_getcontext in the parent frame. The first stack
245218
// frame walked is unwind_phase2.
246219
unsigned framesWalked = 1;
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// REQUIRES: linux && target={{aarch64-.+}}
10+
11+
#include <libunwind.h>
12+
#include <stdint.h>
13+
#include <stdio.h>
14+
#include <stdlib.h>
15+
#include <string.h>
16+
#include <sys/auxv.h>
17+
18+
// Basic test of unwinding with SME lazy saves. This tests libunwind disables ZA
19+
// (and commits a lazy save of ZA) before resuming from unwinding.
20+
21+
// Note: This test requires SME (and is setup to pass on targets without SME).
22+
23+
static bool checkHasSME() {
24+
constexpr int hwcap2_sme = (1 << 23);
25+
unsigned long hwcap2 = getauxval(AT_HWCAP2);
26+
return (hwcap2 & hwcap2_sme) != 0;
27+
}
28+
29+
struct TPIDR2Block {
30+
void *za_save_buffer;
31+
uint64_t num_save_slices;
32+
};
33+
34+
__attribute__((noinline)) void private_za() {
35+
// Note: Lazy save active on entry to function.
36+
unw_context_t context;
37+
unw_cursor_t cursor;
38+
39+
unw_getcontext(&context);
40+
unw_init_local(&cursor, &context);
41+
unw_step(&cursor);
42+
unw_resume(&cursor);
43+
}
44+
45+
bool isZAOn() {
46+
register uint64_t svcr asm("x20");
47+
asm(".inst 0xd53b4254" : "=r"(svcr));
48+
return (svcr & 0b10) != 0;
49+
}
50+
51+
__attribute__((noinline)) void za_function_with_lazy_save() {
52+
register uint64_t tmp asm("x8");
53+
54+
// SMSTART ZA (should zero ZA)
55+
asm(".inst 0xd503457f");
56+
57+
// RDSVL x8, #1 (read streaming vector length)
58+
asm(".inst 0x04bf5828" : "=r"(tmp));
59+
60+
// Allocate and fill ZA save buffer with 0xAA.
61+
size_t buffer_size = tmp * tmp;
62+
uint8_t *za_save_buffer = (uint8_t *)alloca(buffer_size);
63+
memset(za_save_buffer, 0xAA, buffer_size);
64+
65+
TPIDR2Block block = {za_save_buffer, tmp};
66+
tmp = reinterpret_cast<uint64_t>(&block);
67+
68+
// MRS TPIDR2_EL0, x8 (setup lazy save of ZA)
69+
asm(".inst 0xd51bd0a8" ::"r"(tmp));
70+
71+
// ZA should be on before unwinding.
72+
if (!isZAOn()) {
73+
fprintf(stderr, __FILE__ ": fail (ZA not on before call)\n");
74+
abort();
75+
} else {
76+
fprintf(stderr, __FILE__ ": pass (ZA on before call)\n");
77+
}
78+
79+
private_za();
80+
81+
// ZA should be off after unwinding.
82+
if (isZAOn()) {
83+
fprintf(stderr, __FILE__ ": fail (ZA on after unwinding)\n");
84+
abort();
85+
} else {
86+
fprintf(stderr, __FILE__ ": pass (ZA off after unwinding)\n");
87+
}
88+
89+
// MRS x8, TPIDR2_EL0 (read TPIDR2_EL0)
90+
asm(".inst 0xd53bd0a8" : "=r"(tmp));
91+
// ZA should have been saved (TPIDR2_EL0 zero).
92+
if (tmp != 0) {
93+
fprintf(stderr, __FILE__ ": fail (TPIDR2_EL0 non-null after unwinding)\n");
94+
abort();
95+
} else {
96+
fprintf(stderr, __FILE__ ": pass (TPIDR2_EL0 null after unwinding)\n");
97+
}
98+
99+
// ZA (all zero) should have been saved to the buffer.
100+
for (unsigned i = 0; i < buffer_size; ++i) {
101+
if (za_save_buffer[i] != 0) {
102+
fprintf(stderr,
103+
__FILE__ ": fail (za_save_buffer non-zero after unwinding)\n");
104+
abort();
105+
}
106+
}
107+
fprintf(stderr, __FILE__ ": pass (za_save_buffer zero'd after unwinding)\n");
108+
}
109+
110+
int main(int, char **) {
111+
if (!checkHasSME()) {
112+
fprintf(stderr, __FILE__ ": pass (no SME support)\n");
113+
return 0; // Pass (SME is required for this test to run).
114+
}
115+
za_function_with_lazy_save();
116+
return 0;
117+
}

0 commit comments

Comments
 (0)