Skip to content

Commit

Permalink
[libunwind] Add GCS support for AArch64
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
john-brawn-arm committed Jul 17, 2024
1 parent 554febd commit e0f7186
Show file tree
Hide file tree
Showing 9 changed files with 73 additions and 1 deletion.
8 changes: 8 additions & 0 deletions libunwind/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ if (LIBUNWIND_BUILD_32_BITS)
endif()

option(LIBUNWIND_ENABLE_CET "Build libunwind with CET enabled." OFF)
option(LIBUNWIND_ENABLE_GCS "Build libunwind with GCS enabled." OFF)
option(LIBUNWIND_ENABLE_ASSERTIONS "Enable assertions independent of build mode." ON)
option(LIBUNWIND_ENABLE_PEDANTIC "Compile with pedantic enabled." ON)
option(LIBUNWIND_ENABLE_WERROR "Fail and stop if a warning is triggered." OFF)
Expand Down Expand Up @@ -188,6 +189,13 @@ if (LIBUNWIND_ENABLE_CET)
endif()
endif()

if (LIBUNWIND_ENABLE_GCS)
add_compile_flags_if_supported(-mbranch-protection=standard)
if (NOT CXX_SUPPORTS_MBRANCH_PROTECTION_EQ_STANDARD_FLAG)
message(SEND_ERROR "Compiler doesn't support GCS -mbranch-protection option!")
endif()
endif()

if (WIN32)
# The headers lack matching dllexport attributes (_LIBUNWIND_EXPORT);
# silence the warning instead of cluttering the headers (which aren't
Expand Down
7 changes: 7 additions & 0 deletions libunwind/src/Registers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1815,6 +1815,13 @@ inline const char *Registers_ppc64::getRegisterName(int regNum) {
/// process.
class _LIBUNWIND_HIDDEN Registers_arm64;
extern "C" void __libunwind_Registers_arm64_jumpto(Registers_arm64 *);

#if defined(_LIBUNWIND_USE_CET)
extern "C" void *__libunwind_cet_get_jump_target() {
return reinterpret_cast<void *>(&__libunwind_Registers_arm64_jumpto);
}
#endif

class _LIBUNWIND_HIDDEN Registers_arm64 {
public:
Registers_arm64();
Expand Down
28 changes: 28 additions & 0 deletions libunwind/src/UnwindLevel1.c
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,19 @@
__asm__ volatile("jmpq *%%rdx\n\t" :: "D"(cetRegContext), \
"d"(cetJumpAddress)); \
} while (0)
#elif defined(_LIBUNWIND_TARGET_AARCH64)
#define __cet_ss_step_size 8
#define __unw_phase2_resume(cursor, fn) \
do { \
_LIBUNWIND_POP_CET_SSP((fn)); \
void *cetRegContext = __libunwind_cet_get_registers((cursor)); \
void *cetJumpAddress = __libunwind_cet_get_jump_target(); \
__asm__ volatile("mov x0, %0\n\t" \
"br %1\n\t" \
: \
: "r"(cetRegContext), "r"(cetJumpAddress) \
: "x0"); \
} while (0)
#endif

static _Unwind_Reason_Code
Expand Down Expand Up @@ -170,6 +183,10 @@ unwind_phase1(unw_context_t *uc, unw_cursor_t *cursor, _Unwind_Exception *except
}
extern int __unw_step_stage2(unw_cursor_t *);

#if defined(_LIBUNWIND_USE_CET) && defined(_LIBUNWIND_TARGET_AARCH64)
// Enable the GCS target feature to permit GCS instructions to be used.
__attribute__((target("gcs")))
#endif
static _Unwind_Reason_Code
unwind_phase2(unw_context_t *uc, unw_cursor_t *cursor, _Unwind_Exception *exception_object) {
__unw_init_local(cursor, uc);
Expand All @@ -181,7 +198,14 @@ unwind_phase2(unw_context_t *uc, unw_cursor_t *cursor, _Unwind_Exception *except
// frame walked is unwind_phase2.
unsigned framesWalked = 1;
#ifdef _LIBUNWIND_USE_CET
#if defined(_LIBUNWIND_TARGET_I386) || defined(_LIBUNWIND_TARGET_X86_64)
unsigned long shadowStackTop = _get_ssp();
#elif defined(_LIBUNWIND_TARGET_AARCH64)
unsigned long shadowStackTop = 0;
if (__chkfeat(_CHKFEAT_GCS)) {
shadowStackTop = (unsigned long)__gcspr();
}
#endif
#endif
// Walk each frame until we reach where search phase said to stop.
while (true) {
Expand Down Expand Up @@ -306,6 +330,10 @@ unwind_phase2(unw_context_t *uc, unw_cursor_t *cursor, _Unwind_Exception *except
return _URC_FATAL_PHASE2_ERROR;
}

#if defined(_LIBUNWIND_USE_CET) && defined(_LIBUNWIND_TARGET_AARCH64)
// Enable the GCS target feature to permit GCS instructions to be used.
__attribute__((target("gcs")))
#endif
static _Unwind_Reason_Code
unwind_phase2_forced(unw_context_t *uc, unw_cursor_t *cursor,
_Unwind_Exception *exception_object,
Expand Down
2 changes: 1 addition & 1 deletion libunwind/src/UnwindRegistersRestore.S
Original file line number Diff line number Diff line change
Expand Up @@ -680,7 +680,7 @@ DEFINE_LIBUNWIND_FUNCTION(__libunwind_Registers_arm64_jumpto)
ldr x16, [x0, #0x0F8]
ldp x0, x1, [x0, #0x000] // restore x0,x1
mov sp,x16 // restore sp
ret x30 // jump to pc
br x30 // jump to pc

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

Expand Down
19 changes: 19 additions & 0 deletions libunwind/src/cet_unwind.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,25 @@
} while (0)
#endif

// On AArch64 we use _LIBUNWIND_USE_CET to indicate that GCS is supported. We
// need to guard any use of GCS instructions with __chkfeat though, as GCS may
// not be enabled.
#if defined(_LIBUNWIND_TARGET_AARCH64) && defined(__ARM_FEATURE_GCS_DEFAULT)
#define _LIBUNWIND_USE_CET 1
#include <arm_acle.h>

#define _LIBUNWIND_POP_CET_SSP(x) \
do { \
if (__chkfeat(_CHKFEAT_GCS)) { \
unsigned int tmp = (x); \
while (tmp--) { \
__gcspopm(); \
} \
} \
} while (0)

#endif

extern void *__libunwind_cet_get_registers(unw_cursor_t *);
extern void *__libunwind_cet_get_jump_target(void);

Expand Down
1 change: 1 addition & 0 deletions libunwind/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ macro(pythonize_bool var)
endmacro()

pythonize_bool(LIBUNWIND_ENABLE_CET)
pythonize_bool(LIBUNWIND_ENABLE_GCS)
pythonize_bool(LIBUNWIND_ENABLE_THREADS)
pythonize_bool(LIBUNWIND_USES_ARM_EHABI)

Expand Down
3 changes: 3 additions & 0 deletions libunwind/test/configs/llvm-libunwind-merged.cfg.in
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ link_flags = []
if @LIBUNWIND_ENABLE_CET@:
compile_flags.append('-fcf-protection=full')

if @LIBUNWIND_ENABLE_GCS@:
compile_flags.append('-mbranch-protection=standard')

# On ELF platforms, link tests with -Wl,--export-dynamic if supported by the linker.
if len('@CMAKE_EXE_EXPORTS_CXX_FLAG@'):
link_flags.append('@CMAKE_EXE_EXPORTS_CXX_FLAG@')
Expand Down
3 changes: 3 additions & 0 deletions libunwind/test/configs/llvm-libunwind-shared.cfg.in
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ link_flags = []
if @LIBUNWIND_ENABLE_CET@:
compile_flags.append('-fcf-protection=full')

if @LIBUNWIND_ENABLE_GCS@:
compile_flags.append('-mbranch-protection=standard')

# On ELF platforms, link tests with -Wl,--export-dynamic if supported by the linker.
if len('@CMAKE_EXE_EXPORTS_CXX_FLAG@'):
link_flags.append('@CMAKE_EXE_EXPORTS_CXX_FLAG@')
Expand Down
3 changes: 3 additions & 0 deletions libunwind/test/configs/llvm-libunwind-static.cfg.in
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ if @LIBUNWIND_ENABLE_THREADS@:
if @LIBUNWIND_ENABLE_CET@:
compile_flags.append('-fcf-protection=full')

if @LIBUNWIND_ENABLE_GCS@:
compile_flags.append('-mbranch-protection=standard')

# On ELF platforms, link tests with -Wl,--export-dynamic if supported by the linker.
if len('@CMAKE_EXE_EXPORTS_CXX_FLAG@'):
link_flags.append('@CMAKE_EXE_EXPORTS_CXX_FLAG@')
Expand Down

0 comments on commit e0f7186

Please sign in to comment.