blob: 4c407890245a005811d63973863a4897c4494ceb [file] [edit]
// -*- 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
// 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;
}