[libunwind] Add GCS support for AArch64 (#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 b32aac4358c1f6639de7c453656cd74fbab75d71)
This commit is contained in:
parent
64b8514e6c
commit
7e7e8125cf
@ -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)
|
||||
@ -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
|
||||
|
@ -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_GCS)
|
||||
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();
|
||||
|
@ -471,7 +471,7 @@ public:
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(_LIBUNWIND_USE_CET)
|
||||
#if defined(_LIBUNWIND_USE_CET) || defined(_LIBUNWIND_USE_GCS)
|
||||
virtual void *get_registers() {
|
||||
_LIBUNWIND_ABORT("get_registers not implemented");
|
||||
}
|
||||
@ -954,7 +954,7 @@ public:
|
||||
virtual uintptr_t getDataRelBase();
|
||||
#endif
|
||||
|
||||
#if defined(_LIBUNWIND_USE_CET)
|
||||
#if defined(_LIBUNWIND_USE_CET) || defined(_LIBUNWIND_USE_GCS)
|
||||
virtual void *get_registers() { return &_registers; }
|
||||
#endif
|
||||
|
||||
@ -3005,7 +3005,7 @@ bool UnwindCursor<A, R>::isReadableAddr(const pint_t addr) const {
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(_LIBUNWIND_USE_CET)
|
||||
#if defined(_LIBUNWIND_USE_CET) || defined(_LIBUNWIND_USE_GCS)
|
||||
extern "C" void *__libunwind_cet_get_registers(unw_cursor_t *cursor) {
|
||||
AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;
|
||||
return co->get_registers();
|
||||
|
@ -44,7 +44,7 @@
|
||||
// _LIBUNWIND_POP_CET_SSP is used to adjust CET shadow stack pointer and we
|
||||
// directly jump to __libunwind_Registers_x86/x86_64_jumpto instead of using
|
||||
// a regular function call to avoid pushing to CET shadow stack again.
|
||||
#if !defined(_LIBUNWIND_USE_CET)
|
||||
#if !defined(_LIBUNWIND_USE_CET) && !defined(_LIBUNWIND_USE_GCS)
|
||||
#define __unw_phase2_resume(cursor, fn) \
|
||||
do { \
|
||||
(void)fn; \
|
||||
@ -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
|
||||
@ -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_GCS)
|
||||
// Enable the GCS target feature to permit gcspop 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);
|
||||
@ -180,8 +197,12 @@ unwind_phase2(unw_context_t *uc, unw_cursor_t *cursor, _Unwind_Exception *except
|
||||
// uc is initialized by __unw_getcontext in the parent frame. The first stack
|
||||
// frame walked is unwind_phase2.
|
||||
unsigned framesWalked = 1;
|
||||
#ifdef _LIBUNWIND_USE_CET
|
||||
#if defined(_LIBUNWIND_USE_CET)
|
||||
unsigned long shadowStackTop = _get_ssp();
|
||||
#elif defined(_LIBUNWIND_USE_GCS)
|
||||
unsigned long shadowStackTop = 0;
|
||||
if (__chkfeat(_CHKFEAT_GCS))
|
||||
shadowStackTop = (unsigned long)__gcspr();
|
||||
#endif
|
||||
// Walk each frame until we reach where search phase said to stop.
|
||||
while (true) {
|
||||
@ -238,7 +259,7 @@ unwind_phase2(unw_context_t *uc, unw_cursor_t *cursor, _Unwind_Exception *except
|
||||
// against return address stored in CET shadow stack, if the 2 addresses don't
|
||||
// match, it means return address in normal stack has been corrupted, we return
|
||||
// _URC_FATAL_PHASE2_ERROR.
|
||||
#ifdef _LIBUNWIND_USE_CET
|
||||
#if defined(_LIBUNWIND_USE_CET) || defined(_LIBUNWIND_USE_GCS)
|
||||
if (shadowStackTop != 0) {
|
||||
unw_word_t retInNormalStack;
|
||||
__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
|
||||
return _URC_FATAL_PHASE2_ERROR;
|
||||
}
|
||||
|
||||
#if defined(_LIBUNWIND_USE_GCS)
|
||||
// Enable the GCS target feature to permit gcspop 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,
|
||||
|
@ -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__)
|
||||
|
||||
|
@ -35,6 +35,24 @@
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
// On AArch64 we use _LIBUNWIND_USE_GCS 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_GCS 1
|
||||
#include <arm_acle.h>
|
||||
|
||||
#define _LIBUNWIND_POP_CET_SSP(x) \
|
||||
do { \
|
||||
if (__chkfeat(_CHKFEAT_GCS)) { \
|
||||
unsigned 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);
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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@')
|
||||
|
@ -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@')
|
||||
|
@ -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@')
|
||||
|
Loading…
x
Reference in New Issue
Block a user