| // -*- C++ -*- | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // clang-format off | |
| // REQUIRES: target={{aarch64.*}} | |
| // UNSUPPORTED: target={{.*-windows.*}} | |
| // The libSystem unwinder does not correctly read UNW_AARCH64_RA_SIGN_STATE, at | |
| // least through OS version 27.0 | |
| // XFAIL: stdlib=apple-libc++ && target={{.*}}-apple-{{.*}}{{(11|12|13|14|15|26)(\.\d+)*}} | |
| // XFAIL: stdlib=apple-libc++ && target={{.*}}-apple-{{.*}}27.0{{(\.\d+)*}} | |
| // clang-format on | |
| #undef NDEBUG | |
| #include "../src/config.h" | |
| #include "support/func_bounds.h" | |
| #include <assert.h> | |
| #include <inttypes.h> | |
| #include <libunwind.h> | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <sys/types.h> | |
| #include <unistd.h> | |
| #include <unwind.h> | |
| #if defined(__APPLE__) | |
| #include <sys/sysctl.h> | |
| #endif | |
| #if defined(_LIBUNWIND_HAVE_GETAUXVAL) || defined(_LIBUNWIND_HAVE_ELF_AUX_INFO) | |
| #include <sys/auxv.h> | |
| #endif | |
| // Note: This test requires FEAT_PAuth (and is setup to pass on other targets). | |
| #if defined(__APPLE__) | |
| static bool checkHasPAuth() { | |
| int has_pauth = 0; | |
| size_t size = sizeof(has_pauth); | |
| if (sysctlbyname("hw.optional.arm.FEAT_PAuth", &has_pauth, &size, NULL, 0)) | |
| return false; | |
| return has_pauth != 0; | |
| } | |
| #elif defined(_LIBUNWIND_HAVE_GETAUXVAL) | |
| static bool checkHasPAuth() { | |
| constexpr unsigned long hwcap_paca = (1UL << 30); | |
| unsigned long hwcap = getauxval(AT_HWCAP); | |
| return (hwcap & hwcap_paca) != 0; | |
| } | |
| #elif defined(_LIBUNWIND_HAVE_ELF_AUX_INFO) | |
| static bool checkHasPAuth() { | |
| constexpr unsigned long hwcap_paca = (1UL << 30); | |
| unsigned long hwcap = 0; | |
| elf_aux_info(AT_HWCAP, &hwcap, sizeof(hwcap)); | |
| return (hwcap & hwcap_paca) != 0; | |
| } | |
| #else | |
| static bool checkHasPAuth() { | |
| // TODO: Support other platforms. | |
| return false; | |
| } | |
| #endif | |
| FUNC_BOUNDS_DECL(main_func); | |
| static _Unwind_Reason_Code frame_handler(struct _Unwind_Context *ctx, | |
| void *arg) { | |
| fprintf(stderr, "frame_handler\n"); | |
| uint64_t ra_sign_state = | |
| (uint64_t)_Unwind_GetGR(ctx, UNW_AARCH64_RA_SIGN_STATE); | |
| fprintf(stderr, "ra_sign_state = 0x%" PRIx64 "\n", ra_sign_state); | |
| uintptr_t ip = _Unwind_GetIP(ctx); | |
| fprintf(stderr, "UNW_AARCH64_RA_SIGN_STATE @ 0x%" PRIxPTR " = %" PRIu64 "\n", | |
| ip, ra_sign_state); | |
| if (ip >= (uintptr_t)FUNC_START(main_func) && | |
| ip < (uintptr_t)FUNC_END(main_func)) { | |
| // Collect the RA from the callee that will return to main. | |
| *(uint64_t *)arg = ra_sign_state; | |
| // Unwind until main is reached, above frames depend on the platform and | |
| // architecture. | |
| return _URC_END_OF_STACK; | |
| } | |
| #if defined(_LIBUNWIND_TARGET_AARCH64_AUTHENTICATED_UNWINDING) | |
| assert(ra_sign_state == 1 || ra_sign_state == 2); | |
| #endif | |
| return _URC_NO_REASON; | |
| } | |
| __attribute__((noinline)) extern "C" uintptr_t get_main_ra_sign_state() { | |
| uint64_t sign_state = -1; | |
| _Unwind_Backtrace(frame_handler, &sign_state); | |
| fprintf(stderr, "get_main_ra_sign_state: sign_state = 0x%" PRIx64 "\n", | |
| sign_state); | |
| assert((sign_state & 0x3) == sign_state); | |
| return sign_state; | |
| } | |
| __attribute__((noinline)) static uint64_t check_vanilla() { | |
| return get_main_ra_sign_state(); | |
| } | |
| __attribute__((naked, target("pauth"))) static uint64_t check_negate() { | |
| // clang-format off | |
| asm( | |
| #if !defined(__APPLE__) | |
| ".cfi_b_key_frame\n" | |
| #endif | |
| ".cfi_negate_ra_state\n" | |
| "pacibsp\n" | |
| "stp x29, x30, [sp, #-16]!\n" | |
| ".cfi_def_cfa_offset 16\n" | |
| ".cfi_offset x29, -16\n" | |
| ".cfi_offset x30, -8\n" | |
| "bl " SYMBOL_NAME(get_main_ra_sign_state) "\n" | |
| "ldp x29, x30, [sp], #16\n" | |
| ".cfi_def_cfa_offset 0\n" | |
| ".cfi_restore x29\n" | |
| ".cfi_restore x30\n" | |
| ".cfi_negate_ra_state\n" | |
| "retab"); | |
| // clang-format on | |
| } | |
| #if defined(HAVE_CFI_SET_RA_STATE) | |
| __attribute__((naked, target("pauth"))) uint64_t check_set() { | |
| // clang-format off | |
| asm( | |
| #if !defined(__APPLE__) | |
| ".cfi_b_key_frame\n" | |
| #endif | |
| ".cfi_set_ra_state 1, 0\n" | |
| "pacibsp\n" | |
| "stp x29, x30, [sp, #-16]!\n" | |
| ".cfi_def_cfa_offset 16\n" | |
| ".cfi_offset x29, -16\n" | |
| ".cfi_offset x30, -8\n" | |
| "bl " SYMBOL_NAME(get_main_ra_sign_state) "\n" | |
| "ldp x29, x30, [sp], #16\n" | |
| ".cfi_def_cfa_offset 0\n" | |
| ".cfi_restore x29\n" | |
| ".cfi_restore x30\n" | |
| ".cfi_set_ra_state 0, -20\n" | |
| "retab"); | |
| // clang-format on | |
| } | |
| #endif | |
| FUNC_ATTR(main_func) int main(int, char **) { | |
| uint64_t ret; | |
| ret = check_vanilla(); | |
| fprintf(stderr, "check_vanilla: ret = 0x%" PRIx64 "\n", ret); | |
| #if defined(_LIBUNWIND_TARGET_AARCH64_AUTHENTICATED_UNWINDING) | |
| assert(ret == 1 || ret == 2); | |
| #endif | |
| if (!checkHasPAuth()) { | |
| fprintf(stderr, "target does not have FEAT_PAuth\n"); | |
| return 0; | |
| } | |
| ret = check_negate(); | |
| fprintf(stderr, "check_negate: ret = 0x%" PRIx64 "\n", ret); | |
| assert(ret == 1); | |
| #if defined(HAVE_CFI_SET_RA_STATE) | |
| ret = check_set(); | |
| fprintf(stderr, "check_set: ret = 0x%" PRIx64 "\n", ret); | |
| assert(ret == 1); | |
| #endif | |
| fprintf(stderr, "success\n"); | |
| return 0; | |
| } |