v8 152.1.0

Rust bindings to V8
Documentation
// -*- 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;
}