Skip to content

Commit d757d1e

Browse files
john-brawn-armllvmbot
authored andcommitted
[libunwind] Add GCS support for AArch64 (llvm#99335)
AArch64 GCS (Guarded Control Stack) is similar enough to CET that we can re-use the existing code that is guarded by _LIBUNWIND_USE_CET, so long as we also add defines to locate the GCS stack and pop the entries from it. We also need the jumpto function to exit using br instead of ret, to prevent it from popping the GCS stack. GCS support is enabled using the LIBUNWIND_ENABLE_GCS cmake option. This enables -mbranch-protection=standard, which enables GCS. For the places we need to use GCS instructions we use the target attribute, as there's not a command-line option to enable a specific architecture extension. (cherry picked from commit b32aac4)
1 parent 4fd6b32 commit d757d1e

10 files changed

+75
-7
lines changed

libunwind/CMakeLists.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ if (LIBUNWIND_BUILD_32_BITS)
3737
endif()
3838

3939
option(LIBUNWIND_ENABLE_CET "Build libunwind with CET enabled." OFF)
40+
option(LIBUNWIND_ENABLE_GCS "Build libunwind with GCS enabled." OFF)
4041
option(LIBUNWIND_ENABLE_ASSERTIONS "Enable assertions independent of build mode." ON)
4142
option(LIBUNWIND_ENABLE_PEDANTIC "Compile with pedantic enabled." ON)
4243
option(LIBUNWIND_ENABLE_WERROR "Fail and stop if a warning is triggered." OFF)
@@ -188,6 +189,13 @@ if (LIBUNWIND_ENABLE_CET)
188189
endif()
189190
endif()
190191

192+
if (LIBUNWIND_ENABLE_GCS)
193+
add_compile_flags_if_supported(-mbranch-protection=standard)
194+
if (NOT CXX_SUPPORTS_MBRANCH_PROTECTION_EQ_STANDARD_FLAG)
195+
message(SEND_ERROR "Compiler doesn't support GCS -mbranch-protection option!")
196+
endif()
197+
endif()
198+
191199
if (WIN32)
192200
# The headers lack matching dllexport attributes (_LIBUNWIND_EXPORT);
193201
# silence the warning instead of cluttering the headers (which aren't

libunwind/src/Registers.hpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1815,6 +1815,13 @@ inline const char *Registers_ppc64::getRegisterName(int regNum) {
18151815
/// process.
18161816
class _LIBUNWIND_HIDDEN Registers_arm64;
18171817
extern "C" void __libunwind_Registers_arm64_jumpto(Registers_arm64 *);
1818+
1819+
#if defined(_LIBUNWIND_USE_GCS)
1820+
extern "C" void *__libunwind_cet_get_jump_target() {
1821+
return reinterpret_cast<void *>(&__libunwind_Registers_arm64_jumpto);
1822+
}
1823+
#endif
1824+
18181825
class _LIBUNWIND_HIDDEN Registers_arm64 {
18191826
public:
18201827
Registers_arm64();

libunwind/src/UnwindCursor.hpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -471,7 +471,7 @@ class _LIBUNWIND_HIDDEN AbstractUnwindCursor {
471471
}
472472
#endif
473473

474-
#if defined(_LIBUNWIND_USE_CET)
474+
#if defined(_LIBUNWIND_USE_CET) || defined(_LIBUNWIND_USE_GCS)
475475
virtual void *get_registers() {
476476
_LIBUNWIND_ABORT("get_registers not implemented");
477477
}
@@ -954,7 +954,7 @@ class UnwindCursor : public AbstractUnwindCursor{
954954
virtual uintptr_t getDataRelBase();
955955
#endif
956956

957-
#if defined(_LIBUNWIND_USE_CET)
957+
#if defined(_LIBUNWIND_USE_CET) || defined(_LIBUNWIND_USE_GCS)
958958
virtual void *get_registers() { return &_registers; }
959959
#endif
960960

@@ -3005,7 +3005,7 @@ bool UnwindCursor<A, R>::isReadableAddr(const pint_t addr) const {
30053005
}
30063006
#endif
30073007

3008-
#if defined(_LIBUNWIND_USE_CET)
3008+
#if defined(_LIBUNWIND_USE_CET) || defined(_LIBUNWIND_USE_GCS)
30093009
extern "C" void *__libunwind_cet_get_registers(unw_cursor_t *cursor) {
30103010
AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;
30113011
return co->get_registers();

libunwind/src/UnwindLevel1.c

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
// _LIBUNWIND_POP_CET_SSP is used to adjust CET shadow stack pointer and we
4545
// directly jump to __libunwind_Registers_x86/x86_64_jumpto instead of using
4646
// a regular function call to avoid pushing to CET shadow stack again.
47-
#if !defined(_LIBUNWIND_USE_CET)
47+
#if !defined(_LIBUNWIND_USE_CET) && !defined(_LIBUNWIND_USE_GCS)
4848
#define __unw_phase2_resume(cursor, fn) \
4949
do { \
5050
(void)fn; \
@@ -72,6 +72,19 @@
7272
__asm__ volatile("jmpq *%%rdx\n\t" :: "D"(cetRegContext), \
7373
"d"(cetJumpAddress)); \
7474
} while (0)
75+
#elif defined(_LIBUNWIND_TARGET_AARCH64)
76+
#define __cet_ss_step_size 8
77+
#define __unw_phase2_resume(cursor, fn) \
78+
do { \
79+
_LIBUNWIND_POP_CET_SSP((fn)); \
80+
void *cetRegContext = __libunwind_cet_get_registers((cursor)); \
81+
void *cetJumpAddress = __libunwind_cet_get_jump_target(); \
82+
__asm__ volatile("mov x0, %0\n\t" \
83+
"br %1\n\t" \
84+
: \
85+
: "r"(cetRegContext), "r"(cetJumpAddress) \
86+
: "x0"); \
87+
} while (0)
7588
#endif
7689

7790
static _Unwind_Reason_Code
@@ -170,6 +183,10 @@ unwind_phase1(unw_context_t *uc, unw_cursor_t *cursor, _Unwind_Exception *except
170183
}
171184
extern int __unw_step_stage2(unw_cursor_t *);
172185

186+
#if defined(_LIBUNWIND_USE_GCS)
187+
// Enable the GCS target feature to permit gcspop instructions to be used.
188+
__attribute__((target("gcs")))
189+
#endif
173190
static _Unwind_Reason_Code
174191
unwind_phase2(unw_context_t *uc, unw_cursor_t *cursor, _Unwind_Exception *exception_object) {
175192
__unw_init_local(cursor, uc);
@@ -180,8 +197,12 @@ unwind_phase2(unw_context_t *uc, unw_cursor_t *cursor, _Unwind_Exception *except
180197
// uc is initialized by __unw_getcontext in the parent frame. The first stack
181198
// frame walked is unwind_phase2.
182199
unsigned framesWalked = 1;
183-
#ifdef _LIBUNWIND_USE_CET
200+
#if defined(_LIBUNWIND_USE_CET)
184201
unsigned long shadowStackTop = _get_ssp();
202+
#elif defined(_LIBUNWIND_USE_GCS)
203+
unsigned long shadowStackTop = 0;
204+
if (__chkfeat(_CHKFEAT_GCS))
205+
shadowStackTop = (unsigned long)__gcspr();
185206
#endif
186207
// Walk each frame until we reach where search phase said to stop.
187208
while (true) {
@@ -238,7 +259,7 @@ unwind_phase2(unw_context_t *uc, unw_cursor_t *cursor, _Unwind_Exception *except
238259
// against return address stored in CET shadow stack, if the 2 addresses don't
239260
// match, it means return address in normal stack has been corrupted, we return
240261
// _URC_FATAL_PHASE2_ERROR.
241-
#ifdef _LIBUNWIND_USE_CET
262+
#if defined(_LIBUNWIND_USE_CET) || defined(_LIBUNWIND_USE_GCS)
242263
if (shadowStackTop != 0) {
243264
unw_word_t retInNormalStack;
244265
__unw_get_reg(cursor, UNW_REG_IP, &retInNormalStack);
@@ -306,6 +327,10 @@ unwind_phase2(unw_context_t *uc, unw_cursor_t *cursor, _Unwind_Exception *except
306327
return _URC_FATAL_PHASE2_ERROR;
307328
}
308329

330+
#if defined(_LIBUNWIND_USE_GCS)
331+
// Enable the GCS target feature to permit gcspop instructions to be used.
332+
__attribute__((target("gcs")))
333+
#endif
309334
static _Unwind_Reason_Code
310335
unwind_phase2_forced(unw_context_t *uc, unw_cursor_t *cursor,
311336
_Unwind_Exception *exception_object,

libunwind/src/UnwindRegistersRestore.S

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -680,7 +680,7 @@ DEFINE_LIBUNWIND_FUNCTION(__libunwind_Registers_arm64_jumpto)
680680
ldr x16, [x0, #0x0F8]
681681
ldp x0, x1, [x0, #0x000] // restore x0,x1
682682
mov sp,x16 // restore sp
683-
ret x30 // jump to pc
683+
br x30 // jump to pc
684684

685685
#elif defined(__arm__) && !defined(__APPLE__)
686686

libunwind/src/cet_unwind.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,24 @@
3535
} while (0)
3636
#endif
3737

38+
// On AArch64 we use _LIBUNWIND_USE_GCS to indicate that GCS is supported. We
39+
// need to guard any use of GCS instructions with __chkfeat though, as GCS may
40+
// not be enabled.
41+
#if defined(_LIBUNWIND_TARGET_AARCH64) && defined(__ARM_FEATURE_GCS_DEFAULT)
42+
#define _LIBUNWIND_USE_GCS 1
43+
#include <arm_acle.h>
44+
45+
#define _LIBUNWIND_POP_CET_SSP(x) \
46+
do { \
47+
if (__chkfeat(_CHKFEAT_GCS)) { \
48+
unsigned tmp = (x); \
49+
while (tmp--) \
50+
__gcspopm(); \
51+
} \
52+
} while (0)
53+
54+
#endif
55+
3856
extern void *__libunwind_cet_get_registers(unw_cursor_t *);
3957
extern void *__libunwind_cet_get_jump_target(void);
4058

libunwind/test/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ macro(pythonize_bool var)
99
endmacro()
1010

1111
pythonize_bool(LIBUNWIND_ENABLE_CET)
12+
pythonize_bool(LIBUNWIND_ENABLE_GCS)
1213
pythonize_bool(LIBUNWIND_ENABLE_THREADS)
1314
pythonize_bool(LIBUNWIND_USES_ARM_EHABI)
1415

libunwind/test/configs/llvm-libunwind-merged.cfg.in

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ link_flags = []
1111
if @LIBUNWIND_ENABLE_CET@:
1212
compile_flags.append('-fcf-protection=full')
1313

14+
if @LIBUNWIND_ENABLE_GCS@:
15+
compile_flags.append('-mbranch-protection=standard')
16+
1417
# On ELF platforms, link tests with -Wl,--export-dynamic if supported by the linker.
1518
if len('@CMAKE_EXE_EXPORTS_CXX_FLAG@'):
1619
link_flags.append('@CMAKE_EXE_EXPORTS_CXX_FLAG@')

libunwind/test/configs/llvm-libunwind-shared.cfg.in

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ link_flags = []
1010
if @LIBUNWIND_ENABLE_CET@:
1111
compile_flags.append('-fcf-protection=full')
1212

13+
if @LIBUNWIND_ENABLE_GCS@:
14+
compile_flags.append('-mbranch-protection=standard')
15+
1316
# On ELF platforms, link tests with -Wl,--export-dynamic if supported by the linker.
1417
if len('@CMAKE_EXE_EXPORTS_CXX_FLAG@'):
1518
link_flags.append('@CMAKE_EXE_EXPORTS_CXX_FLAG@')

libunwind/test/configs/llvm-libunwind-static.cfg.in

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ if @LIBUNWIND_ENABLE_THREADS@:
1313
if @LIBUNWIND_ENABLE_CET@:
1414
compile_flags.append('-fcf-protection=full')
1515

16+
if @LIBUNWIND_ENABLE_GCS@:
17+
compile_flags.append('-mbranch-protection=standard')
18+
1619
# On ELF platforms, link tests with -Wl,--export-dynamic if supported by the linker.
1720
if len('@CMAKE_EXE_EXPORTS_CXX_FLAG@'):
1821
link_flags.append('@CMAKE_EXE_EXPORTS_CXX_FLAG@')

0 commit comments

Comments
 (0)