[libunwind] [libunwind] Fix exposure of UNW_AARCH64_RA_SIGN_STATE through _Unwind_GetGR (PR #209947)
Nathan Corbyn via cfe-commits
cfe-commits at lists.llvm.org
Thu Jul 16 09:59:35 PDT 2026
================
@@ -0,0 +1,164 @@
+// -*- 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
+}
+
+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
----------------
cofibrant wrote:
Is this asserting that the state is uninitialised? I don't understand where `2` comes from
https://github.com/llvm/llvm-project/pull/209947
More information about the cfe-commits
mailing list