https://github.com/jroelofs created https://github.com/llvm/llvm-project/pull/186423
Introduces a macro abstraction around capturing the bounds of a function, which many platforms handle subtly differently (Mach-O, and ELF, for example). Also introduce an arm64[^-]* -> aarch64 available feature, to enable more tests that would otherwise be excluded on Apple platforms, whose target triples tend to take the form e.g. 'arm64-apple-macosx', rather than 'aarch64-apple-macosx'. Third, we implement the has-sme check using the appropriate sysctl, as getauxval is not available on Darwin platforms. >From af77b7ea0ade29a59f68ce3295e16a7ded1d87c7 Mon Sep 17 00:00:00 2001 From: Jon Roelofs <[email protected]> Date: Fri, 13 Mar 2026 08:23:44 -0700 Subject: [PATCH] [libunwind][Apple] Improve test coverage on Apple platforms Introduces a macro abstraction around capturing the bounds of a function, which many platforms handle subtly differently (Mach-O, and ELF, for example). Also introduce an arm64[^-]* -> aarch64 available feature, to enable more tests that would otherwise be excluded on Apple platforms, whose target triples tend to take the form e.g. 'arm64-apple-macosx', rather than 'aarch64-apple-macosx'. Third, we implement the has-sme check using the appropriate sysctl, as getauxval is not available on Darwin platforms. --- libunwind/test/aarch64_za_unwind.pass.cpp | 15 +++++++- libunwind/test/configs/cmake-bridge.cfg.in | 9 +++++ libunwind/test/floatregister.pass.cpp | 14 +++----- libunwind/test/forceunwind.pass.cpp | 15 +++----- libunwind/test/signal_unwind.pass.cpp | 15 +++----- libunwind/test/support/func_bounds.h | 40 +++++++++++++++++++++ libunwind/test/unwind_leaffunction.pass.cpp | 15 +++----- 7 files changed, 83 insertions(+), 40 deletions(-) create mode 100644 libunwind/test/support/func_bounds.h diff --git a/libunwind/test/aarch64_za_unwind.pass.cpp b/libunwind/test/aarch64_za_unwind.pass.cpp index 9f6b106a21fec..a8f616ad17677 100644 --- a/libunwind/test/aarch64_za_unwind.pass.cpp +++ b/libunwind/test/aarch64_za_unwind.pass.cpp @@ -14,18 +14,31 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#if defined(__APPLE__) +#include <sys/sysctl.h> +#else #include <sys/auxv.h> +#endif // Basic test of unwinding with SME lazy saves. This tests libunwind disables ZA // (and commits a lazy save of ZA) before resuming from unwinding. // Note: This test requires SME (and is setup to pass on targets without SME). - +#if defined(__APPLE__) +static bool checkHasSME() { + int has_sme = 0; + size_t size = sizeof(has_sme); + if (!sysctlbyname("hw.optional.arm.FEAT_SME", &has_sme, &size, NULL, 0)) + return false; + return has_sme != 0; +} +#else static bool checkHasSME() { constexpr int hwcap2_sme = (1 << 23); unsigned long hwcap2 = getauxval(AT_HWCAP2); return (hwcap2 & hwcap2_sme) != 0; } +#endif struct TPIDR2Block { void *za_save_buffer; diff --git a/libunwind/test/configs/cmake-bridge.cfg.in b/libunwind/test/configs/cmake-bridge.cfg.in index e40497bfa9976..cbb39873b68c3 100644 --- a/libunwind/test/configs/cmake-bridge.cfg.in +++ b/libunwind/test/configs/cmake-bridge.cfg.in @@ -15,6 +15,7 @@ import os, site site.addsitedir(os.path.join('@LIBUNWIND_LIBCXX_PATH@', 'utils')) import libcxx.test.format from lit.util import which +import re as _re # Basic configuration of the test suite config.name = os.path.basename('@LIBUNWIND_TEST_CONFIG@') @@ -29,6 +30,14 @@ if @LIBUNWIND_USES_ARM_EHABI@: if not @LIBUNWIND_ENABLE_THREADS@: config.available_features.add('libunwind-no-threads') +# Apple uses "arm64" and "arm64e" in target triples where the canonical LLVM +# name is "aarch64". REQUIRES directives like `REQUIRES: target={{aarch64-.+}}` +# would otherwise never match on any Apple AArch64 target. Add a normalised +# alias so those directives work correctly. +_norm_triple = _re.sub(r'^arm64([^-]*)', 'aarch64', config.target_triple) +if _norm_triple != config.target_triple: + config.available_features.add('target={}'.format(_norm_triple)) + # Add substitutions for bootstrapping the test suite configuration config.substitutions.append(('%{install-prefix}', '@LIBUNWIND_TESTING_INSTALL_PREFIX@')) config.substitutions.append(('%{include}', '@LIBUNWIND_TESTING_INSTALL_PREFIX@/include')) diff --git a/libunwind/test/floatregister.pass.cpp b/libunwind/test/floatregister.pass.cpp index 6be3e1f3f7385..48e1b7797a885 100644 --- a/libunwind/test/floatregister.pass.cpp +++ b/libunwind/test/floatregister.pass.cpp @@ -12,16 +12,12 @@ // Basic test for float registers number are accepted. +#include "support/func_bounds.h" #include <stdlib.h> #include <string.h> #include <unwind.h> -// Using __attribute__((section("main_func"))) is ELF specific, but then -// this entire test is marked as requiring Linux, so we should be good. -// -// We don't use dladdr() because on musl it's a no-op when statically linked. -extern char __start_main_func; -extern char __stop_main_func; +FUNC_BOUNDS_DECL(main_func); _Unwind_Reason_Code frame_handler(struct _Unwind_Context *ctx, void *arg) { (void)arg; @@ -29,8 +25,8 @@ _Unwind_Reason_Code frame_handler(struct _Unwind_Context *ctx, void *arg) { // Unwind until the main is reached, above frames depend on the platform and // architecture. uintptr_t ip = _Unwind_GetIP(ctx); - if (ip >= (uintptr_t)&__start_main_func && - ip < (uintptr_t)&__stop_main_func) { + if (ip >= (uintptr_t)FUNC_START(main_func) && + ip < (uintptr_t)FUNC_END(main_func)) { _Exit(0); } @@ -53,7 +49,7 @@ __attribute__((noinline)) void foo() { _Unwind_Backtrace(frame_handler, NULL); } -__attribute__((section("main_func"))) int main(int, char **) { +FUNC_ATTR(main_func) int main(int, char **) { foo(); return -2; } diff --git a/libunwind/test/forceunwind.pass.cpp b/libunwind/test/forceunwind.pass.cpp index e5437c31a0f65..fd61912acb734 100644 --- a/libunwind/test/forceunwind.pass.cpp +++ b/libunwind/test/forceunwind.pass.cpp @@ -7,7 +7,6 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: target={{.*-apple.*}} // UNSUPPORTED: target={{.*-aix.*}} // UNSUPPORTED: target={{.*-windows.*}} @@ -18,6 +17,7 @@ // See libcxxabi/test/forced_unwind* tests too. #undef NDEBUG +#include "support/func_bounds.h" #include <assert.h> #include <signal.h> #include <stdint.h> @@ -28,12 +28,7 @@ #include <unistd.h> #include <unwind.h> -// Using __attribute__((section("main_func"))) is Linux specific, but then -// this entire test is marked as requiring Linux, so we should be good. -// -// We don't use dladdr() because on musl it's a no-op when statically linked. -extern char __start_main_func; -extern char __stop_main_func; +FUNC_BOUNDS_DECL(main_func); void foo(); _Unwind_Exception ex; @@ -52,8 +47,8 @@ _Unwind_Reason_Code stop(int version, _Unwind_Action actions, // Unwind until the main is reached, above frames depend on the platform and // architecture. uintptr_t ip = _Unwind_GetIP(context); - if (ip >= (uintptr_t)&__start_main_func && - ip < (uintptr_t)&__stop_main_func) { + if (ip >= (uintptr_t)FUNC_START(main_func) && + ip < (uintptr_t)FUNC_END(main_func)) { _Exit(0); } @@ -74,7 +69,7 @@ __attribute__((noinline)) void foo() { _Unwind_ForcedUnwind(e, stop, (void *)&foo); } -__attribute__((section("main_func"))) int main(int, char **) { +FUNC_ATTR(main_func) int main(int, char **) { foo(); return -2; } diff --git a/libunwind/test/signal_unwind.pass.cpp b/libunwind/test/signal_unwind.pass.cpp index ca50f83964c11..f8798bc3eabeb 100644 --- a/libunwind/test/signal_unwind.pass.cpp +++ b/libunwind/test/signal_unwind.pass.cpp @@ -10,7 +10,6 @@ // Ensure that the unwinder can cope with the signal handler. // REQUIRES: target={{(aarch64|loongarch64|riscv64|s390x|x86_64)-.+}} // UNSUPPORTED: target={{.*-windows.*}} -// UNSUPPORTED: target={{.*-apple.*}} // TODO: Figure out why this fails with Memory Sanitizer. // XFAIL: msan @@ -23,6 +22,7 @@ // XFAIL: target={{.*}}-musl #undef NDEBUG +#include "support/func_bounds.h" #include <assert.h> #include <signal.h> #include <stdio.h> @@ -32,12 +32,7 @@ #include <unistd.h> #include <unwind.h> -// Using __attribute__((section("main_func"))) is ELF specific, but then -// this entire test is marked as requiring Linux, so we should be good. -// -// We don't use dladdr() because on musl it's a no-op when statically linked. -extern char __start_main_func; -extern char __stop_main_func; +FUNC_BOUNDS_DECL(main_func); _Unwind_Reason_Code frame_handler(struct _Unwind_Context* ctx, void* arg) { (void)arg; @@ -45,8 +40,8 @@ _Unwind_Reason_Code frame_handler(struct _Unwind_Context* ctx, void* arg) { // Unwind until the main is reached, above frames depend on the platform and // architecture. uintptr_t ip = _Unwind_GetIP(ctx); - if (ip >= (uintptr_t)&__start_main_func && - ip < (uintptr_t)&__stop_main_func) { + if (ip >= (uintptr_t)FUNC_START(main_func) && + ip < (uintptr_t)FUNC_END(main_func)) { _Exit(0); } @@ -59,7 +54,7 @@ void signal_handler(int signum) { _Exit(-1); } -__attribute__((section("main_func"))) int main(int, char **) { +FUNC_ATTR(main_func) int main(int, char **) { signal(SIGUSR1, signal_handler); kill(getpid(), SIGUSR1); return -2; diff --git a/libunwind/test/support/func_bounds.h b/libunwind/test/support/func_bounds.h new file mode 100644 index 0000000000000..b6d9d25280d66 --- /dev/null +++ b/libunwind/test/support/func_bounds.h @@ -0,0 +1,40 @@ +// -*- 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 +// +//===----------------------------------------------------------------------===// +// +// Utilities for locating the address range of a function. +// +// On MachO targets the linker synthesises section$start$SEG$sect / +// section$end$SEG$sect symbols; __asm() is used to bind them to valid C +// identifiers without the leading '_' implied by the Darwin User Label Prefix. +// +// On ELF targets the linker synthesises __start_<section> / __stop_<section> +// symbols for any section whose name is a valid C identifier. +// We don't use dladdr() because on musl it's a no-op when statically linked. + +#ifndef LIBUNWIND_TEST_SUPPORT_FUNC_BOUNDS_H +#define LIBUNWIND_TEST_SUPPORT_FUNC_BOUNDS_H + +#ifdef __APPLE__ +#define FUNC_BOUNDS_DECL(name) \ + extern char name##_start __asm("section$start$__TEXT$__" #name); \ + extern char name##_end __asm("section$end$__TEXT$__" #name) +#define FUNC_ATTR(name) \ + __attribute__((section("__TEXT,__" #name ",regular,pure_instructions"))) +#define FUNC_START(name) (&name##_start) +#define FUNC_END(name) (&name##_end) +#else +#define FUNC_BOUNDS_DECL(name) \ + extern char __start_##name; \ + extern char __stop_##name +#define FUNC_ATTR(name) __attribute__((section(#name))) +#define FUNC_START(name) (&__start_##name) +#define FUNC_END(name) (&__stop_##name) +#endif + +#endif // LIBUNWIND_TEST_SUPPORT_FUNC_BOUNDS_H diff --git a/libunwind/test/unwind_leaffunction.pass.cpp b/libunwind/test/unwind_leaffunction.pass.cpp index af791a6b2ed31..a709493604e3c 100644 --- a/libunwind/test/unwind_leaffunction.pass.cpp +++ b/libunwind/test/unwind_leaffunction.pass.cpp @@ -10,7 +10,6 @@ // Ensure that leaf function can be unwund. // REQUIRES: target={{(aarch64|loongarch64|riscv64|s390x|x86_64)-.+}} // UNSUPPORTED: target={{.*-windows.*}} -// UNSUPPORTED: target={{.*-apple.*}} // TODO: Figure out why this fails with Memory Sanitizer. // XFAIL: msan @@ -23,6 +22,7 @@ // XFAIL: target={{.*}}-musl #undef NDEBUG +#include "support/func_bounds.h" #include <assert.h> #include <signal.h> #include <stdio.h> @@ -32,12 +32,7 @@ #include <unistd.h> #include <unwind.h> -// Using __attribute__((section("main_func"))) is ELF specific, but then -// this entire test is marked as requiring Linux, so we should be good. -// -// We don't use dladdr() because on musl it's a no-op when statically linked. -extern char __start_main_func; -extern char __stop_main_func; +FUNC_BOUNDS_DECL(main_func); _Unwind_Reason_Code frame_handler(struct _Unwind_Context* ctx, void* arg) { (void)arg; @@ -45,8 +40,8 @@ _Unwind_Reason_Code frame_handler(struct _Unwind_Context* ctx, void* arg) { // Unwind until the main is reached, above frames depend on the platform and // architecture. uintptr_t ip = _Unwind_GetIP(ctx); - if (ip >= (uintptr_t)&__start_main_func && - ip < (uintptr_t)&__stop_main_func) { + if (ip >= (uintptr_t)FUNC_START(main_func) && + ip < (uintptr_t)FUNC_END(main_func)) { _Exit(0); } @@ -72,7 +67,7 @@ __attribute__((noinline)) void crashing_leaf_func(int do_trap) { __builtin_trap(); } -__attribute__((section("main_func"))) int main(int, char **) { +FUNC_ATTR(main_func) int main(int, char **) { signal(SIGTRAP, signal_handler); signal(SIGILL, signal_handler); crashing_leaf_func(1); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
