[libunwind][Apple] Improve test coverage on Apple platforms (#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.
This commit is contained in:
@@ -9,6 +9,9 @@
|
||||
// REQUIRES: target={{aarch64-.+}}
|
||||
// UNSUPPORTED: target={{.*-windows.*}}
|
||||
|
||||
// TODO: investigate this failure.
|
||||
// XFAIL: target={{.*-apple-.*}} && stdlib=system
|
||||
|
||||
#include <libunwind.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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'))
|
||||
|
||||
@@ -10,18 +10,17 @@
|
||||
// REQUIRES: target={{aarch64-.+}}
|
||||
// UNSUPPORTED: target={{.*-windows.*}}
|
||||
|
||||
// TODO: investigate this failure.
|
||||
// XFAIL: target={{.*-apple-.*}} && stdlib=system
|
||||
|
||||
// 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 +28,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 +52,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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
40
libunwind/test/support/func_bounds.h
Normal file
40
libunwind/test/support/func_bounds.h
Normal file
@@ -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
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user