From f14267359b9b31deb45c9db311795800c1269454 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Fri, 28 Aug 2026 02:09:59 +0900 Subject: [PATCH] detect: Use weakref instead of dlsym --- .github/.cspell/project-dictionary.txt | 11 +- .github/workflows/ci.yml | 10 +- Cargo.toml | 6 +- build.rs | 63 +++- src/imp/atomic128/aarch64.rs | 62 +--- src/imp/atomic128/mod.rs | 21 +- src/imp/atomic128/powerpc64.rs | 20 +- src/imp/atomic64/arm_linux.rs | 3 +- src/imp/detect/README.md | 39 +-- src/imp/detect/auxv.rs | 447 ++++++++++++------------- src/imp/fallback/mod.rs | 43 +-- src/imp/mod.rs | 21 +- src/lib.rs | 12 + src/utils.rs | 239 +++++++++++++ tools/build.sh | 59 ++-- 15 files changed, 646 insertions(+), 410 deletions(-) diff --git a/.github/.cspell/project-dictionary.txt b/.github/.cspell/project-dictionary.txt index 8af00579b..5e96d35e6 100644 --- a/.github/.cspell/project-dictionary.txt +++ b/.github/.cspell/project-dictionary.txt @@ -15,6 +15,7 @@ atxmega Auxinfo auxv auxvec +balign bitops BOOKE braich @@ -29,6 +30,7 @@ ccmp cdsg cgrjh cgrjl +chericap clgr clgrjh clgrjhe @@ -60,15 +62,16 @@ cxchgweak devkitc devkitm devtmpfs -dlfcn dlsym ecall Espressif espup exynos +fdpic fild fistp FPSR +FUNCDESC getauxval getisax getpid @@ -157,12 +160,16 @@ osxsave outputdir phdr picolibc +popsection poweroff prctl prefetcher PRIMASK +progbits PSRAM pstq +purecap +pushsection quadword rcpc rdinit @@ -229,9 +236,11 @@ wfxt wokwi xadd xchg +XCOFF xgetbv xmegau xmmword +XPLINK xsave xsub zaamo diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c316f7d68..9521be219 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -206,13 +206,13 @@ jobs: # {,no-}outline-atomics # portable_atomic_no_outline_atomics only affects x86_64, AArch64, Arm, powerpc64, and RISC-V Linux. - # outline-atomics is disabled by default on AArch64/powerpc64 musl with static linking, and AIX. + # outline-atomics is disabled by default on AIX. # powerpc64le- (little-endian) is skipped because it is pwr8 by default - run: tools/test.sh -vv --tests ${TARGET:-} ${BUILD_STD:-} env: RUSTDOCFLAGS: ${{ env.RUSTDOCFLAGS }} --cfg portable_atomic_no_outline_atomics RUSTFLAGS: ${{ env.RUSTFLAGS }} --cfg portable_atomic_no_outline_atomics - if: startsWith(matrix.target, 'x86_64') || ((startsWith(matrix.target, 'aarch64') || startsWith(matrix.target, 'arm64')) || startsWith(matrix.target, 'powerpc64-')) && !(contains(matrix.target, '-musl') && matrix.flags == '') || startsWith(matrix.target, 'armv4t') || startsWith(matrix.target, 'armv5te') || matrix.target == 'arm-linux-androideabi' || startsWith(matrix.target, 'riscv') + if: startsWith(matrix.target, 'x86_64') || ((startsWith(matrix.target, 'aarch64') || startsWith(matrix.target, 'arm64')) || startsWith(matrix.target, 'powerpc64-')) || startsWith(matrix.target, 'armv4t') || startsWith(matrix.target, 'armv5te') || matrix.target == 'arm-linux-androideabi' || startsWith(matrix.target, 'riscv') # powerpc64 and RISC-V are skipped because tested below. - run: tools/test.sh -vv --tests ${TARGET:-} ${BUILD_STD:-} env: @@ -508,6 +508,9 @@ jobs: - target: aarch64-unknown-openbsd os: openbsd version: '7.9' + - target: aarch64-unknown-openbsd + os: openbsd + version: '7.5' # no elf_aux_info - target: aarch64-unknown-illumos os: omnios runs-on: ubuntu-24.04 @@ -558,9 +561,10 @@ jobs: if: matrix.toolchain != '' - uses: taiki-e/cross-platform-actions-action@63e3c031895caac4783f1f778f3e2e0e409353b3 # dev # zizmor: ignore[stale-action-refs] env: + OS_VERSION: ${{ matrix.version }} TOOLCHAIN: ${{ matrix.toolchain }} with: - environment_variables: CARGO_INCREMENTAL CARGO_NET_RETRY CARGO_TERM_COLOR RUST_BACKTRACE RUST_TEST_THREADS RUSTDOCFLAGS RUSTFLAGS RUSTUP_MAX_RETRIES PORTABLE_ATOMIC_DENY_WARNINGS TEST_FEATURES TOOLCHAIN + environment_variables: CARGO_INCREMENTAL CARGO_NET_RETRY CARGO_TERM_COLOR RUST_BACKTRACE RUST_TEST_THREADS RUSTDOCFLAGS RUSTFLAGS RUSTUP_MAX_RETRIES PORTABLE_ATOMIC_DENY_WARNINGS TEST_FEATURES OS_VERSION TOOLCHAIN operating_system: ${{ matrix.os }} architecture: ${{ matrix.os == 'omnios' && 'x86_64' || 'aarch64' }} version: ${{ matrix.os == 'omnios' && 'r151058' || matrix.version }} diff --git a/Cargo.toml b/Cargo.toml index 885225016..23cf51009 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -101,8 +101,12 @@ unexpected_cfgs = { level = "warn", check-cfg = [ 'cfg(target_arch,values("loongarch32"))', # 1.89+ https://github.com/rust-lang/rust/pull/142053 'cfg(target_os,values("trusty"))', # 1.82+ https://github.com/rust-lang/rust/pull/129490 'cfg(target_os,values("psx"))', # 1.84+ https://github.com/rust-lang/rust/pull/131168 + 'cfg(target_os,values("cygwin"))', # 1.86+ https://github.com/rust-lang/rust/pull/134999 'cfg(target_os,values("zos"))', # https://github.com/rust-lang/compiler-team/issues/992 'cfg(target_env,values("psx"))', # pre-1.84 https://github.com/rust-lang/rust/pull/131168 + 'cfg(target_abi,values("pauthtest"))', # 1.98+ https://github.com/rust-lang/rust/pull/155722 + 'cfg(target_abi,values("purecap"))', # https://github.com/kent-weak-memory/rust + 'cfg(target_abi,values("fdpic"))', # https://github.com/llvm/llvm-project/issues/146959 'cfg(target_feature,values("lse2","lse128","rcpc3"))', # 1.82+ https://github.com/rust-lang/rust/pull/128192 'cfg(target_feature,values("quadword-atomics"))', # 1.83+ https://github.com/rust-lang/rust/pull/130873 'cfg(target_feature,values("zaamo","zabha"))', # 1.83+ https://github.com/rust-lang/rust/pull/130877 @@ -112,7 +116,7 @@ unexpected_cfgs = { level = "warn", check-cfg = [ 'cfg(target_pointer_width,values("128"))', # Known custom cfgs, excluding those that may be set by build script. # Public APIs, considered unstable unless documented as stable in readme. - 'cfg(portable_atomic_no_outline_atomics,portable_atomic_outline_atomics,portable_atomic_unstable_f16,portable_atomic_unstable_f128)', + 'cfg(portable_atomic_outline_atomics,portable_atomic_unstable_f16,portable_atomic_unstable_f128)', # Not public API. 'cfg(portable_atomic_test_detect_false,portable_atomic_test_no_std_static_assert_ffi,qemu,valgrind,valgrind_cross)', ] } diff --git a/build.rs b/build.rs index 1aeda7391..1869453c7 100644 --- a/build.rs +++ b/build.rs @@ -63,7 +63,7 @@ fn main() { // Custom cfgs set by build script. Not public API. // grep -F 'cargo:rustc-cfg=' build.rs | grep -Ev '^ *//' | sed -E 's/^.*cargo:rustc-cfg=//; s/(=\\)?".*$//' | LC_ALL=C sort -u | tr '\n' ',' | sed -E 's/,$/\n/' println!( - "cargo:rustc-check-cfg=cfg(portable_atomic_atomic_intrinsics,portable_atomic_disable_fiq,portable_atomic_force_amo,portable_atomic_ll_sc_rmw,portable_atomic_llvm_16_or_later,portable_atomic_llvm_17_or_later,portable_atomic_no_asm,portable_atomic_no_asm_maybe_uninit,portable_atomic_no_asm_syscall,portable_atomic_no_atomic_64,portable_atomic_no_atomic_cas,portable_atomic_no_atomic_load_store,portable_atomic_no_atomic_min_max,portable_atomic_no_cfg_target_has_atomic,portable_atomic_no_cmpxchg16b_intrinsic,portable_atomic_no_cmpxchg16b_target_feature,portable_atomic_no_const_mut_refs,portable_atomic_no_const_raw_ptr_deref,portable_atomic_no_const_transmute,portable_atomic_no_core_unwind_safe,portable_atomic_no_diagnostic_namespace,portable_atomic_no_strict_provenance,portable_atomic_no_strict_provenance_atomic_ptr,portable_atomic_no_stronger_failure_ordering,portable_atomic_no_track_caller,portable_atomic_no_unsafe_op_in_unsafe_fn,portable_atomic_old_miri,portable_atomic_pre_llvm_16,portable_atomic_pre_llvm_18,portable_atomic_pre_llvm_20,portable_atomic_s_mode,portable_atomic_sanitize_thread,portable_atomic_target_cpu,portable_atomic_target_feature,portable_atomic_unsafe_assume_privileged,portable_atomic_unsafe_assume_single_core,portable_atomic_unstable_asm,portable_atomic_unstable_asm_experimental_arch,portable_atomic_unstable_cfg_target_has_atomic,portable_atomic_unstable_isa_attribute)" + "cargo:rustc-check-cfg=cfg(portable_atomic_atomic_intrinsics,portable_atomic_disable_fiq,portable_atomic_force_amo,portable_atomic_ll_sc_rmw,portable_atomic_llvm_16_or_later,portable_atomic_llvm_17_or_later,portable_atomic_no_asm,portable_atomic_no_asm_maybe_uninit,portable_atomic_no_asm_syscall,portable_atomic_no_atomic_64,portable_atomic_no_atomic_cas,portable_atomic_no_atomic_load_store,portable_atomic_no_atomic_min_max,portable_atomic_no_cfg_target_has_atomic,portable_atomic_no_cmpxchg16b_intrinsic,portable_atomic_no_cmpxchg16b_target_feature,portable_atomic_no_const_mut_refs,portable_atomic_no_const_raw_ptr_deref,portable_atomic_no_const_transmute,portable_atomic_no_core_unwind_safe,portable_atomic_no_diagnostic_namespace,portable_atomic_no_outline_atomics,portable_atomic_no_strict_provenance,portable_atomic_no_strict_provenance_atomic_ptr,portable_atomic_no_stronger_failure_ordering,portable_atomic_no_target_abi,portable_atomic_no_track_caller,portable_atomic_no_unsafe_op_in_unsafe_fn,portable_atomic_old_miri,portable_atomic_pre_llvm_16,portable_atomic_pre_llvm_18,portable_atomic_pre_llvm_20,portable_atomic_purecap,portable_atomic_s_mode,portable_atomic_sanitize_thread,portable_atomic_target_cpu,portable_atomic_target_feature,portable_atomic_unsafe_assume_privileged,portable_atomic_unsafe_assume_single_core,portable_atomic_unstable_asm,portable_atomic_unstable_asm_experimental_arch,portable_atomic_unstable_cfg_target_has_atomic,portable_atomic_unstable_isa_attribute)" ); // TODO: handle multi-line emit_target_feature_fallback // grep -F 'emit_target_feature_fallback("' build.rs | grep -Ev '^ *//' | sed -E 's/^.*emit_target_feature_fallback\(//; s/",.*$/"/' | LC_ALL=C sort -u | tr '\n' ',' | sed -E 's/,$/\n/' @@ -127,6 +127,19 @@ fn main() { if !version.probe(74, 2023, 8, 23) { println!("cargo:rustc-cfg=portable_atomic_no_asm_maybe_uninit"); } + // #[cfg(target_abi)] stabilized in Rust 1.78 (nightly-2024-02-26): https://github.com/rust-lang/rust/pull/119590 + if !version.probe(78, 2024, 2, 25) { + println!("cargo:rustc-cfg=portable_atomic_no_target_abi"); + if target_arch == "aarch64" + && (env::var("CARGO_CFG_TARGET_ABI") + .unwrap_or_default() + .split(',') + .any(|abi| abi == "purecap") + || target.ends_with("-purecap")) + { + println!("cargo:rustc-cfg=portable_atomic_purecap"); + } + } // #[diagnostic] stabilized in Rust 1.78 (nightly-2024-03-09): https://github.com/rust-lang/rust/pull/119888 if !version.probe(78, 2024, 3, 8) { println!("cargo:rustc-cfg=portable_atomic_no_diagnostic_namespace"); @@ -175,6 +188,13 @@ fn main() { // The part of this feature we use has not been changed since nightly-2020-06-21 // until it was stabilized, so it can safely be enabled in nightly for that period. println!("cargo:rustc-cfg=portable_atomic_unstable_asm"); + if (target_arch == "aarch64"/* test-only: || target_arch == "arm" */) + && (!rustflags.is_allowed_feature("global_asm") || !version.probe(54, 2021, 5, 19)) + { + // weakref macro needs global_asm! and extended_key_value_attributes + // (stabilized in 1.54 (nightly-2021-05-20): https://github.com/rust-lang/rust/pull/83366). + println!("cargo:rustc-cfg=portable_atomic_no_outline_atomics"); + } if (target_arch == "riscv32" || target_arch == "riscv64") && !version.probe(56, 2021, 8, 15) { @@ -226,7 +246,7 @@ fn main() { } } - // feature(cfg_target_has_atomic) stabilized in Rust 1.60 (nightly-2022-02-11): https://github.com/rust-lang/rust/pull/93824 + // cfg(target_has_atomic) stabilized in Rust 1.60 (nightly-2022-02-11): https://github.com/rust-lang/rust/pull/93824 if !version.probe(60, 2022, 2, 10) { if version.nightly && version.probe(40, 2019, 10, 13) @@ -404,6 +424,32 @@ fn main() { if is_apple || rustflags.target_cpu.map_or(false, |cpu| cpu.starts_with("apple-")) { println!("cargo:rustc-cfg=portable_atomic_ll_sc_rmw"); } + + if target_os == "linux" + && env::var("CARGO_CFG_TARGET_ABI") + .unwrap_or_default() + .split(',') + .any(|abi| abi == "pauthtest") + { + let target_env = env::var("CARGO_CFG_TARGET_ENV").unwrap_or_default(); + // The compiler normally rejects crt-static for pauthtest ABI, but check just in case. + // https://github.com/rust-lang/rust/blob/0844f35a32c98882d77e39da8c2a872ec74615cd/compiler/rustc_session/src/session.rs#L1510 + // crt-static is not shown in CARGO_CFG_TARGET_FEATURE when enabled by default, but fine in this case. + if !cfg!(feature = "std") + && target_env == "musl" + && env::var("CARGO_CFG_TARGET_FEATURE") + .unwrap_or_default() + .split(',') + .any(|f| f == "crt-static") + || target_env == "uclibc" + { + // Do not use weakref on pauthtest ABI due to: + // - LLD bug: https://github.com/llvm/llvm-project/issues/173296 + // - There is no good way to recognize `-Z pointer-authentication=-calls` + // Note: There is no builtin targets can reach this. + println!("cargo:rustc-cfg=portable_atomic_no_outline_atomics"); + } + } } "arm" => { // For non-Linux/Android pre-v6 Arm (tier 3) with unsafe_assume_single_core enabled. @@ -633,11 +679,22 @@ fn main() { { // Cell Broadband Engine SPU syscalls support a subset of powerpc64 syscalls with the exact semantics: https://github.com/torvalds/linux/blob/v7.1/arch/powerpc/platforms/cell/spu_callbacks.c#L17 // but have a different syscall sequence: https://github.com/mirror/newlib-cygwin/blob/newlib-4.4.0/libgloss/spu/linux_syscalls.c#L37 - // GCC 10 (https://gcc.gnu.org/gcc-9/changes.html) and LLVM 3.3.0 (https://releases.llvm.org/3.3/docs/ReleaseNotes.html#non-comprehensive-list-of-changes-in-this-release) + // Note: There is no builtin SPU targets. And GCC 10 (https://gcc.gnu.org/gcc-9/changes.html) + // and LLVM 3.3.0 (https://releases.llvm.org/3.3/docs/ReleaseNotes.html#non-comprehensive-list-of-changes-in-this-release) // removed support for SPU, so this is unlikely to be supported in rust-lang/rust: https://github.com/rust-lang/compiler-team/issues/614 // Note: code referring this cfg is currently used only for testing. println!("cargo:rustc-cfg=portable_atomic_no_asm_syscall"); } + if env::var("CARGO_CFG_TARGET_POINTER_WIDTH") + .expect("CARGO_CFG_TARGET_POINTER_WIDTH not set") + == "32" + { + // Do not use weakref on PowerPC64 ILP32 due to LLVM bug: + // - https://github.com/llvm/llvm-project/issues/52826 + // - https://github.com/llvm/llvm-project/issues/169283 + // Note: There is no builtin PowerPC64 ILP32 targets. + println!("cargo:rustc-cfg=portable_atomic_no_outline_atomics"); + } } "s390x" => { let mut arch9_features = false; // z196+ diff --git a/src/imp/atomic128/aarch64.rs b/src/imp/atomic128/aarch64.rs index 0c0d56b24..62eceb581 100644 --- a/src/imp/atomic128/aarch64.rs +++ b/src/imp/atomic128/aarch64.rs @@ -79,8 +79,6 @@ See tests/asm-test/asm/portable-atomic for generated assembly. include!("macros.rs"); -// On musl with static linking, it seems that getauxval is not always available. -// See detect/auxv.rs for more. #[cfg(not(portable_atomic_no_outline_atomics))] #[cfg(any( test, @@ -94,9 +92,9 @@ include!("macros.rs"); target_os = "linux", any( target_env = "gnu", - all(target_env = "musl", any(not(target_feature = "crt-static"), feature = "std")), + target_env = "musl", target_env = "ohos", - all(target_env = "uclibc", not(target_feature = "crt-static")), + target_env = "uclibc", portable_atomic_outline_atomics, ), ), @@ -190,12 +188,9 @@ macro_rules! debug_assert_lse { target_os = "linux", any( target_env = "gnu", - all( - target_env = "musl", - any(not(target_feature = "crt-static"), feature = "std"), - ), + target_env = "musl", target_env = "ohos", - all(target_env = "uclibc", not(target_feature = "crt-static")), + target_env = "uclibc", portable_atomic_outline_atomics, ), ), @@ -230,12 +225,9 @@ macro_rules! debug_assert_lse2 { target_os = "linux", any( target_env = "gnu", - all( - target_env = "musl", - any(not(target_feature = "crt-static"), feature = "std"), - ), + target_env = "musl", target_env = "ohos", - all(target_env = "uclibc", not(target_feature = "crt-static")), + target_env = "uclibc", portable_atomic_outline_atomics, ), ), @@ -274,12 +266,9 @@ macro_rules! debug_assert_lse128 { target_os = "linux", any( target_env = "gnu", - all( - target_env = "musl", - any(not(target_feature = "crt-static"), feature = "std"), - ), + target_env = "musl", target_env = "ohos", - all(target_env = "uclibc", not(target_feature = "crt-static")), + target_env = "uclibc", portable_atomic_outline_atomics, ), ), @@ -318,12 +307,9 @@ macro_rules! debug_assert_rcpc3 { target_os = "linux", any( target_env = "gnu", - all( - target_env = "musl", - any(not(target_feature = "crt-static"), feature = "std"), - ), + target_env = "musl", target_env = "ohos", - all(target_env = "uclibc", not(target_feature = "crt-static")), + target_env = "uclibc", portable_atomic_outline_atomics, ), ), @@ -522,12 +508,9 @@ cfg_sel!({ target_os = "linux", any( target_env = "gnu", - all( - target_env = "musl", - any(not(target_feature = "crt-static"), feature = "std"), - ), + target_env = "musl", target_env = "ohos", - all(target_env = "uclibc", not(target_feature = "crt-static")), + target_env = "uclibc", portable_atomic_outline_atomics, ), ), @@ -909,12 +892,9 @@ cfg_sel!({ target_os = "linux", any( target_env = "gnu", - all( - target_env = "musl", - any(not(target_feature = "crt-static"), feature = "std"), - ), + target_env = "musl", target_env = "ohos", - all(target_env = "uclibc", not(target_feature = "crt-static")), + target_env = "uclibc", portable_atomic_outline_atomics, ), ), @@ -1275,12 +1255,9 @@ unsafe fn atomic_compare_exchange( target_os = "linux", any( target_env = "gnu", - all( - target_env = "musl", - any(not(target_feature = "crt-static"), feature = "std"), - ), + target_env = "musl", target_env = "ohos", - all(target_env = "uclibc", not(target_feature = "crt-static")), + target_env = "uclibc", portable_atomic_outline_atomics, ), ), @@ -1414,12 +1391,9 @@ unsafe fn atomic_compare_exchange( target_os = "linux", any( target_env = "gnu", - all( - target_env = "musl", - any(not(target_feature = "crt-static"), feature = "std"), - ), + target_env = "musl", target_env = "ohos", - all(target_env = "uclibc", not(target_feature = "crt-static")), + target_env = "uclibc", portable_atomic_outline_atomics, ), ), diff --git a/src/imp/atomic128/mod.rs b/src/imp/atomic128/mod.rs index 966467831..22fb40dcb 100644 --- a/src/imp/atomic128/mod.rs +++ b/src/imp/atomic128/mod.rs @@ -47,28 +47,15 @@ pub(super) mod aarch64; all( target_os = "linux", any( - all( - target_env = "gnu", - any(target_endian = "little", not(target_feature = "crt-static")), - ), - all( - target_env = "musl", - any(not(target_feature = "crt-static"), feature = "std"), - ), + target_env = "gnu", + target_env = "musl", target_env = "ohos", - all(target_env = "uclibc", not(target_feature = "crt-static")), + target_env = "uclibc", portable_atomic_outline_atomics, ), ), target_os = "android", - all( - target_os = "freebsd", - any( - target_endian = "little", - not(target_feature = "crt-static"), - portable_atomic_outline_atomics, - ), - ), + target_os = "freebsd", target_os = "openbsd", all( target_os = "aix", diff --git a/src/imp/atomic128/powerpc64.rs b/src/imp/atomic128/powerpc64.rs index 3b409c0da..6e30515cf 100644 --- a/src/imp/atomic128/powerpc64.rs +++ b/src/imp/atomic128/powerpc64.rs @@ -41,8 +41,6 @@ include!("macros.rs"); #[path = "../fallback/outline_atomics.rs"] mod fallback; -// On musl with static linking, it seems that getauxval is not always available. -// See detect/auxv.rs for more. #[cfg(not(portable_atomic_no_outline_atomics))] #[cfg(any( test, @@ -55,25 +53,15 @@ mod fallback; all( target_os = "linux", any( - all( - target_env = "gnu", - any(target_endian = "little", not(target_feature = "crt-static")), - ), - all(target_env = "musl", any(not(target_feature = "crt-static"), feature = "std")), + target_env = "gnu", + target_env = "musl", target_env = "ohos", - all(target_env = "uclibc", not(target_feature = "crt-static")), + target_env = "uclibc", portable_atomic_outline_atomics, ), ), target_os = "android", - all( - target_os = "freebsd", - any( - target_endian = "little", - not(target_feature = "crt-static"), - portable_atomic_outline_atomics, - ), - ), + target_os = "freebsd", target_os = "openbsd", ))] #[path = "../detect/auxv.rs"] diff --git a/src/imp/atomic64/arm_linux.rs b/src/imp/atomic64/arm_linux.rs index 75432c985..fa4731247 100644 --- a/src/imp/atomic64/arm_linux.rs +++ b/src/imp/atomic64/arm_linux.rs @@ -37,7 +37,8 @@ mod fallback; target_env = "gnu", target_env = "musl", target_env = "ohos", - all(target_env = "uclibc", not(target_feature = "crt-static")), + target_env = "uclibc", + portable_atomic_outline_atomics, ), ), target_os = "android", diff --git a/src/imp/detect/README.md b/src/imp/detect/README.md index b0ee5ac2a..a10fa45a6 100644 --- a/src/imp/detect/README.md +++ b/src/imp/detect/README.md @@ -6,30 +6,31 @@ Here is the table of targets that support run-time CPU feature detection and the | target_arch | target_os/target_env | instruction/API | features | note | | ----------- | -------------------- | --------------- | -------- | ---- | -| x86_64 | all (except for sgx) | cpuid | all | Enabled by default | -| aarch64 | linux (gnu/ohos/uclibc) | getauxval | all | Enabled by default (dlsym is used by default if needed for compatibility with older versions) | -| aarch64 | linux (musl) | getauxval | all | Only enabled by default when dynamic linking or `std` feature enabled (both disabled by default, see [auxv.rs](auxv.rs)) | -| aarch64 | android | getauxval | all | Enabled by default | -| aarch64 | freebsd | elf_aux_info | all | Enabled by default | -| aarch64 | netbsd | sysctlbyname | all | Enabled by default | -| aarch64 | openbsd | sysctl | all | Enabled by default | -| aarch64 | macos/ios/tvos/watchos/visionos | sysctlbyname | all | Enabled by default | -| aarch64 | illumos | getisax | lse, lse2 | Enabled by default | -| aarch64/arm64ec | windows | IsProcessorFeaturePresent | lse, lse2 | Enabled by default | -| aarch64 | fuchsia | zx_system_get_features | lse | Enabled by default | -| riscv32/riscv64 | linux/android | riscv_hwprobe | all | Enabled by default | -| powerpc64 | linux (gnu/ohos/uclibc) | getauxval | all | Enabled by default (dlsym is used by default if needed for compatibility with older versions) | -| powerpc64 | linux (musl) | getauxval | all | Only enabled by default when dynamic linking or `std` feature enabled (both disabled by default, see [auxv.rs](auxv.rs)) | -| powerpc64 | freebsd | elf_aux_info | all | Enabled by default (dlsym is used by default for compatibility with older versions) | -| powerpc64 | openbsd | elf_aux_info | all | Enabled by default (dlsym is used by default for compatibility with older versions) | +| x86_64 | all (except for sgx) | cpuid+xgetbv | all | | +| aarch64 | linux (gnu/ohos) | getauxval | all | | +| aarch64 | linux (musl) | getauxval | all | weakref is used by default unless dynamic linking or `std` feature enabled (both disabled by default, see [auxv.rs](auxv.rs) for details) | +| aarch64 | linux (uclibc) | getauxval | all | weakref is used by default for compatibility with older versions | +| aarch64 | android | getauxval+__system_property_get | all | | +| aarch64 | freebsd | elf_aux_info | all | | +| aarch64 | netbsd | sysctlbyname | all | | +| aarch64 | openbsd | sysctl | all | | +| aarch64 | macos/ios/tvos/watchos/visionos | sysctlbyname | all | | +| aarch64 | illumos | getisax | lse, lse2 | | +| aarch64/arm64ec | windows | IsProcessorFeaturePresent | lse, lse2 | | +| aarch64 | fuchsia | zx_system_get_features | lse | | +| riscv32/riscv64 | linux/android | riscv_hwprobe | all | | +| powerpc64 | linux (gnu) | getauxval | all | weakref is used on big endian by default for compatibility with older versions | +| powerpc64 | linux (musl) | getauxval | all | weakref is used by default unless dynamic linking or `std` feature enabled (both disabled by default, see [auxv.rs](auxv.rs) for details) | +| powerpc64 | freebsd | elf_aux_info | all | weakref is used on big endian by default for compatibility with older versions | +| powerpc64 | openbsd | elf_aux_info | all | weakref is used by default for compatibility with older versions | | powerpc64 | aix | getsystemcfg | all | Requires LLVM 20+. Disabled by default (see [powerpc64_aix.rs](powerpc64_aix.rs)) | -Run-time detection is enabled by default on most targets and can be disabled with `--cfg portable_atomic_no_outline_atomics`. +Run-time detection is enabled by default unless otherwise noted and can be disabled with `--cfg portable_atomic_no_outline_atomics`. On some targets, run-time detection is disabled by default mainly for compatibility with incomplete build environments or support for it is experimental, and can be enabled by `--cfg portable_atomic_outline_atomics`. (When both cfg are enabled, `*_no_*` cfg is preferred.) -`dlsym` usually not working with static linking, so detection using implementations that use `dlsym` for compatibility will be disabled if static linking is enabled. -You can use `--cfg portable_atomic_outline_atomics` to force the use of non-`dlsym` implementations and enable run-time detection in such an environment. +On some targets, [weakref](https://sourceware.org/binutils/docs/as/Weakref.html) (which is similar to `dlsym` and [unstable `#[linkage = "extern_weak"]`](https://github.com/rust-lang/rust/issues/29603), but async-signal-safe unlike `dlsym` and works on stable and sound unlike `extern_weak`) is used by default if needed for compatibility with older versions. +You can use `--cfg portable_atomic_outline_atomics` to force the use of strong references. For targets not included in the above table, run-time detection is always disabled and works the same as when `--cfg portable_atomic_no_outline_atomics` is set. diff --git a/src/imp/detect/auxv.rs b/src/imp/detect/auxv.rs index cbbb18904..3b4d13b69 100644 --- a/src/imp/detect/auxv.rs +++ b/src/imp/detect/auxv.rs @@ -36,7 +36,7 @@ Supported platforms: Since Rust 1.46, std requires musl 1.1.24+ https://github.com/rust-lang/rust/pull/73089 Since Rust 1.71, std requires musl 1.2.3+ https://blog.rust-lang.org/2023/05/09/Updating-musl-targets Since Rust 1.93, std requires musl 1.2.5+ https://blog.rust-lang.org/2025/12/05/Updating-musl-1.2.5/ - OpenHarmony uses a fork of musl 1.2 https://gitee.com/openharmony/docs/blob/master/en/application-dev/reference/native-lib/musl.md + OpenHarmony uses a fork of musl 1.2 and supports armv7/aarch64/x86_64 https://github.com/openharmony-rs/openharmony-docs/blob/OpenHarmony-v7.0-Beta1/en/application-dev/reference/native-lib/musl.md - uClibc-ng 1.0.43+ (through getauxval) https://github.com/wbx-github/uclibc-ng/commit/d869bb1600942c01a77539128f9ba5b5b55ad647 Not always available on: @@ -98,11 +98,13 @@ may have been fixed in https://github.com/rust-lang/rust/commit/9ed0d11efbec18a1 See also https://github.com/rust-lang/stdarch/pull/1746. So as for musl with static linking, we assume that getauxval is always available also when `std` feature enabled. -On platforms that we cannot assume that getauxval/elf_aux_info is always available, so we use dlsym +On platforms that we cannot assume that getauxval/elf_aux_info is always available, so we use weakref instead of directly calling getauxval/elf_aux_info. (You can force getauxval/elf_aux_info to be -called directly instead of using dlsym by `--cfg portable_atomic_outline_atomics`). +called directly instead of using weakref by `--cfg portable_atomic_outline_atomics`). -Also, note that dlsym usually not working with static linking. +When statically linked, weakref may resolve to null even if the symbol is actually available. +However, unlike dlsym it works if there are other strong references to the same symbol. +(See test_weak_* tests.) # Linux/Android @@ -175,40 +177,6 @@ mod os { #[cfg(not(all(target_arch = "aarch64", target_pointer_width = "32")))] pub(crate) const AT_HWCAP4: c_ulong = 30; - // Defined in dlfcn.h. - // https://gitlab.com/gnutools/glibc/-/blob/glibc-2.44/dlfcn/dlfcn.h - // https://git.musl-libc.org/cgit/musl/tree/include/dlfcn.h?h=v1.2.5 - // https://github.com/wbx-github/uclibc-ng/blob/v1.0.47/include/dlfcn.h - // https://github.com/kernkonzept/l4re-core/blob/r-2026-W07/libc/uclibc-ng/contrib/uclibc/include/dlfcn.h - // https://android.googlesource.com/platform/bionic.git/+/refs/tags/android-16.0.0_r1/libc/include/dlfcn.h - #[cfg(any( - test, - not(any( - all( - target_os = "linux", - any( - all( - target_env = "gnu", - any( - target_arch = "aarch64", - all(target_arch = "powerpc64", target_endian = "little"), - ), - ), - target_env = "musl", - target_env = "ohos", - ), - ), - all(target_os = "android", target_pointer_width = "64"), - portable_atomic_outline_atomics, - )), - ))] - #[cfg(not(all(target_os = "android", target_pointer_width = "32")))] - pub(crate) const RTLD_DEFAULT: *mut c_void = core::ptr::null_mut(); - #[cfg(all(target_os = "android", target_pointer_width = "32"))] - #[allow(clippy::cast_sign_loss)] - pub(crate) const RTLD_DEFAULT: *mut c_void = - crate::utils::ptr::without_provenance_mut(-1_isize as usize); - // Defined in sys/system_properties.h. // https://android.googlesource.com/platform/bionic.git/+/refs/tags/android-16.0.0_r1/libc/include/sys/system_properties.h #[cfg(all(target_arch = "aarch64", target_os = "android"))] @@ -226,7 +194,6 @@ mod os { // https://android.googlesource.com/platform/bionic.git/+/refs/tags/android-16.0.0_r1/libc/include/sys/auxv.h // https://github.com/picolibc/picolibc/blob/1.8.12/libc/include/sys/auxv.h #[cfg(any( - test, all( target_os = "linux", any( @@ -237,7 +204,10 @@ mod os { all(target_arch = "powerpc64", target_endian = "little"), ), ), - target_env = "musl", + all( + target_env = "musl", + any(not(target_feature = "crt-static"), feature = "std"), // see module-level doc + ), target_env = "ohos", ), ), @@ -246,36 +216,6 @@ mod os { ))] pub(crate) fn getauxval(type_: c_ulong) -> c_ulong; - // Defined in dlfcn.h. - // https://man7.org/linux/man-pages/man3/dlsym.3.html - // https://gitlab.com/gnutools/glibc/-/blob/glibc-2.44/dlfcn/dlfcn.h - // https://git.musl-libc.org/cgit/musl/tree/include/dlfcn.h?h=v1.2.5 - // https://github.com/wbx-github/uclibc-ng/blob/v1.0.47/include/dlfcn.h - // https://github.com/kernkonzept/l4re-core/blob/r-2026-W07/libc/uclibc-ng/contrib/uclibc/include/dlfcn.h - // https://android.googlesource.com/platform/bionic.git/+/refs/tags/android-16.0.0_r1/libc/include/dlfcn.h - #[cfg(any( - test, - not(any( - all( - target_os = "linux", - any( - all( - target_env = "gnu", - any( - target_arch = "aarch64", - all(target_arch = "powerpc64", target_endian = "little"), - ), - ), - target_env = "musl", - target_env = "ohos", - ), - ), - all(target_os = "android", target_pointer_width = "64"), - portable_atomic_outline_atomics, - )), - ))] - pub(crate) fn dlsym(handle: *mut c_void, symbol: *const c_char) -> *mut c_void; - // Defined in sys/system_properties.h. // https://android.googlesource.com/platform/bionic.git/+/refs/tags/android-16.0.0_r1/libc/include/sys/system_properties.h #[cfg(all(target_arch = "aarch64", target_os = "android"))] @@ -301,7 +241,10 @@ mod os { all(target_arch = "powerpc64", target_endian = "little"), ), ), - target_env = "musl", + all( + target_env = "musl", + any(not(target_feature = "crt-static"), feature = "std"), // see module-level doc + ), target_env = "ohos", ), ), @@ -314,14 +257,13 @@ mod os { } #[cfg(else)] { - // SAFETY: we passed a valid C string to dlsym, and a pointer returned by dlsym - // is a valid pointer to the function if it is non-null. - let getauxval: GetauxvalTy = unsafe { - let ptr = ffi::dlsym(ffi::RTLD_DEFAULT, c!("getauxval").as_ptr()); - if ptr.is_null() { - return 0; - } - core::mem::transmute::<*mut ffi::c_void, GetauxvalTy>(ptr) + mod weak { + weakref!(getauxval, Option); + } + // SAFETY: we've passed correct getauxval signature to weakref!. + let getauxval = match unsafe { weak::getauxval } { + Some(getauxval) => getauxval, + None => return 0, }; } }); @@ -363,31 +305,6 @@ mod os { #[cfg(test)] #[cfg(not(target_os = "openbsd"))] pub(crate) const AT_HWCAP4: c_int = 39; - - // FreeBSD - // Defined in dlfcn.h. - // https://man.freebsd.org/dlsym(3) - // https://github.com/freebsd/freebsd-src/blob/release/15.1.0/include/dlfcn.h - // OpenBSD - // Defined in dlfcn.h. - // https://man.openbsd.org/dlsym.3 - // https://github.com/openbsd/src/blob/d2e81eb18e50973c87ae2c9c2ba348ff3ff89cf9/include/dlfcn.h - #[cfg(any( - test, - not(any( - all( - target_os = "freebsd", - any( - target_arch = "aarch64", - all(target_arch = "powerpc64", target_endian = "little"), - ), - ), - portable_atomic_outline_atomics, - )), - ))] - #[allow(clippy::cast_sign_loss)] - pub(crate) const RTLD_DEFAULT: *mut c_void = - crate::utils::ptr::without_provenance_mut(-2_isize as usize); }); sys_fn!({ @@ -401,51 +318,25 @@ mod os { // https://man.openbsd.org/elf_aux_info.3 // https://github.com/openbsd/src/blob/d2e81eb18e50973c87ae2c9c2ba348ff3ff89cf9/sys/sys/auxv.h #[cfg(any( - test, - any( - all( - target_os = "freebsd", - any( - target_arch = "aarch64", - all(target_arch = "powerpc64", target_endian = "little"), - ), + all( + target_os = "freebsd", + any( + target_arch = "aarch64", + all(target_arch = "powerpc64", target_endian = "little"), ), - portable_atomic_outline_atomics, ), + portable_atomic_outline_atomics, ))] pub(crate) fn elf_aux_info(aux: c_int, buf: *mut c_void, buf_len: c_int) -> c_int; - - // FreeBSD - // Defined in dlfcn.h. - // https://man.freebsd.org/dlsym(3) - // https://github.com/freebsd/freebsd-src/blob/release/15.1.0/include/dlfcn.h - // OpenBSD - // Defined in dlfcn.h. - // https://man.openbsd.org/dlsym.3 - // https://github.com/openbsd/src/blob/d2e81eb18e50973c87ae2c9c2ba348ff3ff89cf9/include/dlfcn.h - #[cfg(any( - test, - not(any( - all( - target_os = "freebsd", - any( - target_arch = "aarch64", - all(target_arch = "powerpc64", target_endian = "little"), - ), - ), - portable_atomic_outline_atomics, - )), - ))] - pub(crate) fn dlsym(handle: *mut c_void, symbol: *const c_char) -> *mut c_void; } }); } pub(super) type ElfAuxInfoTy = unsafe extern "C" fn(ffi::c_int, *mut ffi::c_void, ffi::c_int) -> ffi::c_int; - pub(super) fn getauxval(aux: ffi::c_int) -> ffi::c_ulong { + pub(super) fn elf_aux_info(aux: ffi::c_int, buf: &mut ffi::c_ulong) -> ffi::c_int { #[allow(clippy::cast_possible_wrap, clippy::cast_possible_truncation)] - const OUT_LEN: ffi::c_int = mem::size_of::() as ffi::c_int; + const BUF_LEN: ffi::c_int = mem::size_of::() as ffi::c_int; cfg_sel!({ #[cfg(any( @@ -464,26 +355,28 @@ mod os { } #[cfg(else)] { - // SAFETY: we passed a valid C string to dlsym, and a pointer returned by dlsym - // is a valid pointer to the function if it is non-null. - let elf_aux_info: ElfAuxInfoTy = unsafe { - let ptr = ffi::dlsym(ffi::RTLD_DEFAULT, c!("elf_aux_info").as_ptr()); - if ptr.is_null() { - return 0; - } - mem::transmute::<*mut ffi::c_void, ElfAuxInfoTy>(ptr) + mod weak { + weakref!(elf_aux_info, Option); + } + // SAFETY: we've passed correct elf_aux_info signature to weakref!. + let elf_aux_info = match unsafe { weak::elf_aux_info } { + Some(elf_aux_info) => elf_aux_info, + // In elf_aux_info, 0 indicates success, so technically this isn't a correct emulation, + // but since our caller doesn't care about this, we return the cheapest one. + None => return 0, }; } }); - let mut out: ffi::c_ulong = 0; // SAFETY: // - the pointer is valid because we got it from a reference. - // - `OUT_LEN` is the same as the size of `out`. + // - `BUF_LEN` is the same as the size of `buf`. // - `elf_aux_info` is thread-safe. - let res = unsafe { - elf_aux_info(aux, (&mut out as *mut ffi::c_ulong).cast::(), OUT_LEN) - }; + unsafe { elf_aux_info(aux, (buf as *mut ffi::c_ulong).cast::(), BUF_LEN) } + } + pub(super) fn getauxval(aux: ffi::c_int) -> ffi::c_ulong { + let mut out: ffi::c_ulong = 0; + let res = elf_aux_info(aux, &mut out); // If elf_aux_info fails, `out` will be left at zero (which is the proper default value). debug_assert!(res == 0 || out == 0); out @@ -775,87 +668,183 @@ mod tests { ); } - #[cfg(any(target_os = "linux", target_os = "android"))] - #[test] - fn test_dlsym_getauxval() { - unsafe { - let ptr = ffi::dlsym(ffi::RTLD_DEFAULT, c!("getauxval").as_ptr()); - if cfg!(target_feature = "crt-static") { - assert!(ptr.is_null()); - } else if cfg!(any( - all( - target_os = "linux", - any(target_env = "gnu", target_env = "musl", target_env = "ohos"), - ), - target_os = "android", - )) { - assert!(!ptr.is_null()); - } else if option_env!("CI").is_some() { - assert!(ptr.is_null()); - } - if ptr.is_null() { - return; - } - let dlsym_getauxval = mem::transmute::<*mut ffi::c_void, os::GetauxvalTy>(ptr); - for &at in &[ffi::AT_HWCAP, ffi::AT_HWCAP2] { - assert_eq!(dlsym_getauxval(at), ffi::getauxval(at)); - } - #[cfg(not(all(target_arch = "aarch64", target_pointer_width = "32")))] - for &at in &[ffi::AT_HWCAP3, ffi::AT_HWCAP4] { - assert_eq!(dlsym_getauxval(at), ffi::getauxval(at)); - } + #[cfg_attr( + not(portable_atomic_no_target_abi), + cfg(not(all(target_arch = "aarch64", target_abi = "pauthtest"))) + )] + mod weak { + use super::*; + + #[cfg(any(target_os = "linux", target_os = "android"))] + weakref!(getauxval, Option); + #[cfg(any(target_os = "linux", target_os = "android"))] + weakref!(getauxval9, Option); // unavailable symbol + #[cfg(any(target_os = "linux", target_os = "android"))] + mod multiple { + // multiple .weakref + use super::os; + weakref!(getauxval, Option); + weakref!(getauxval9, Option); // unavailable symbol } - } - #[cfg(any(target_os = "freebsd", target_os = "openbsd"))] - #[test] - fn test_dlsym_elf_aux_info() { - unsafe { - let ptr = ffi::dlsym(ffi::RTLD_DEFAULT, c!("elf_aux_info").as_ptr()); - if cfg!(target_feature = "crt-static") { - assert!(ptr.is_null()); - } else if cfg!(target_os = "freebsd") || option_env!("CI").is_some() { - assert!(!ptr.is_null()); - } - if ptr.is_null() { - return; - } - let dlsym_elf_aux_info = mem::transmute::<*mut ffi::c_void, os::ElfAuxInfoTy>(ptr); - #[allow(clippy::cast_possible_wrap, clippy::cast_possible_truncation)] - let out_len = mem::size_of::() as ffi::c_int; - for &at in &[ffi::AT_HWCAP, ffi::AT_HWCAP2] { - let mut out: ffi::c_ulong = 0; - let mut dlsym_out: ffi::c_ulong = 0; - assert_eq!( - ffi::elf_aux_info( - at, - (&mut out as *mut ffi::c_ulong).cast::(), - out_len, - ), - dlsym_elf_aux_info( - at, - (&mut dlsym_out as *mut ffi::c_ulong).cast::(), - out_len, + #[cfg(any(target_os = "linux", target_os = "android"))] + #[test] + fn test_weak_getauxval() { + unsafe { + let ptr = libc::dlsym(libc::RTLD_DEFAULT, c!("getauxval").as_ptr()); + let ptr = mem::transmute::<*mut ffi::c_void, Option>(ptr); + let ptr2 = getauxval; + let ptr4 = getauxval9; + let ptr3 = multiple::getauxval; + let ptr5 = multiple::getauxval9; + assert!(ptr4.is_none()); + assert!(ptr5.is_none()); + if cfg!(any( + all( + target_os = "linux", + any(target_env = "gnu", target_env = "musl", target_env = "ohos"), ), - ); - assert_eq!(out, dlsym_out); + target_os = "android", + )) { + assert_eq!(ptr.is_none(), cfg!(target_feature = "crt-static")); + assert!(ptr2.is_some()); + assert!(ptr3.is_some()); + } else if option_env!("CI").is_some() { + assert!(ptr.is_none()); + assert!(ptr2.is_none()); + assert!(ptr3.is_none()); + } + if ptr2.is_none() { + return; + } + #[cfg(not(target_feature = "crt-static"))] + let dlsym_getauxval = ptr.unwrap(); + let weak_getauxval = ptr2.unwrap(); + let weak2_getauxval = ptr3.unwrap(); + for &at in &[ffi::AT_HWCAP, ffi::AT_HWCAP2] { + #[cfg(not(target_feature = "crt-static"))] + assert_eq!(dlsym_getauxval(at), os::getauxval(at)); + assert_eq!(weak_getauxval(at), os::getauxval(at)); + assert_eq!(weak2_getauxval(at), os::getauxval(at)); + } + #[cfg(not(all(target_arch = "aarch64", target_pointer_width = "32")))] + for &at in &[ffi::AT_HWCAP3, ffi::AT_HWCAP4] { + #[cfg(not(target_feature = "crt-static"))] + assert_eq!(dlsym_getauxval(at), os::getauxval(at)); + assert_eq!(weak_getauxval(at), os::getauxval(at)); + assert_eq!(weak2_getauxval(at), os::getauxval(at)); + } } - #[cfg(not(target_os = "openbsd"))] - for &at in &[ffi::AT_HWCAP3, ffi::AT_HWCAP4] { - let mut out: ffi::c_ulong = 0; - let mut dlsym_out: ffi::c_ulong = 0; - assert_eq!( - ffi::elf_aux_info( - at, - (&mut out as *mut ffi::c_ulong).cast::(), - out_len, - ), - dlsym_elf_aux_info( - at, - (&mut dlsym_out as *mut ffi::c_ulong).cast::(), - out_len, - ), - ); - assert_eq!(out, dlsym_out); + } + #[cfg(any(target_os = "freebsd", target_os = "openbsd"))] + weakref!(elf_aux_info, Option); + #[cfg(any(target_os = "freebsd", target_os = "openbsd"))] + weakref!(elf_aux_info9, Option); // unavailable symbol + #[cfg(any(target_os = "freebsd", target_os = "openbsd"))] + mod multiple { + // multiple .weakref + use super::os; + weakref!(elf_aux_info, Option); + weakref!(elf_aux_info9, Option); // unavailable symbol + } + #[cfg(any(target_os = "freebsd", target_os = "openbsd"))] + #[test] + fn test_weak_elf_aux_info() { + unsafe { + let ptr = libc::dlsym(libc::RTLD_DEFAULT, c!("elf_aux_info").as_ptr()); + let ptr = mem::transmute::<*mut ffi::c_void, Option>(ptr); + let ptr2 = elf_aux_info; + let ptr4 = elf_aux_info9; + let ptr3 = multiple::elf_aux_info; + let ptr5 = multiple::elf_aux_info9; + assert!(ptr4.is_none()); + assert!(ptr5.is_none()); + if cfg!(target_os = "freebsd") + || option_env!("CI").is_some() && option_env!("OS_VERSION") != Some("7.5") + { + assert_eq!(ptr.is_none(), cfg!(target_feature = "crt-static")); + assert!(ptr2.is_some()); + assert!(ptr3.is_some()); + } else if option_env!("CI").is_some() { + assert!(ptr.is_none()); + assert!(ptr2.is_none()); + assert!(ptr3.is_none()); + } + if ptr2.is_none() { + return; + } + #[cfg(not(target_feature = "crt-static"))] + let dlsym_elf_aux_info = ptr.unwrap(); + let weak_elf_aux_info = ptr2.unwrap(); + let weak2_elf_aux_info = ptr3.unwrap(); + #[allow(clippy::cast_possible_wrap, clippy::cast_possible_truncation)] + let out_len = mem::size_of::() as ffi::c_int; + for &at in &[ffi::AT_HWCAP, ffi::AT_HWCAP2] { + let mut out: ffi::c_ulong = 0; + let mut dlsym_out: ffi::c_ulong = 0; + #[cfg(not(target_feature = "crt-static"))] + assert_eq!( + os::elf_aux_info(at, &mut out), + dlsym_elf_aux_info( + at, + (&mut dlsym_out as *mut ffi::c_ulong).cast::(), + out_len, + ), + ); + #[cfg(not(target_feature = "crt-static"))] + assert_eq!(out, dlsym_out); + assert_eq!( + os::elf_aux_info(at, &mut out), + weak_elf_aux_info( + at, + (&mut dlsym_out as *mut ffi::c_ulong).cast::(), + out_len, + ), + ); + assert_eq!(out, dlsym_out); + assert_eq!( + os::elf_aux_info(at, &mut out), + weak2_elf_aux_info( + at, + (&mut dlsym_out as *mut ffi::c_ulong).cast::(), + out_len, + ), + ); + assert_eq!(out, dlsym_out); + } + #[cfg(not(target_os = "openbsd"))] + for &at in &[ffi::AT_HWCAP3, ffi::AT_HWCAP4] { + let mut out: ffi::c_ulong = 0; + let mut dlsym_out: ffi::c_ulong = 0; + #[cfg(not(target_feature = "crt-static"))] + assert_eq!( + os::elf_aux_info(at, &mut out), + dlsym_elf_aux_info( + at, + (&mut dlsym_out as *mut ffi::c_ulong).cast::(), + out_len, + ), + ); + #[cfg(not(target_feature = "crt-static"))] + assert_eq!(out, dlsym_out); + assert_eq!( + os::elf_aux_info(at, &mut out), + weak_elf_aux_info( + at, + (&mut dlsym_out as *mut ffi::c_ulong).cast::(), + out_len, + ), + ); + assert_eq!(out, dlsym_out); + assert_eq!( + os::elf_aux_info(at, &mut out), + weak2_elf_aux_info( + at, + (&mut dlsym_out as *mut ffi::c_ulong).cast::(), + out_len, + ), + ); + assert_eq!(out, dlsym_out); + } } } } diff --git a/src/imp/fallback/mod.rs b/src/imp/fallback/mod.rs index b58b4597e..1bc6af460 100644 --- a/src/imp/fallback/mod.rs +++ b/src/imp/fallback/mod.rs @@ -118,6 +118,7 @@ impl Drop for ScFenceGuard { } } +#[rustfmt::skip] macro_rules! atomic { ($atomic_type:ident, $int_type:ident, $align:literal) => { #[repr(C, align($align))] @@ -228,28 +229,15 @@ macro_rules! atomic { all( target_os = "linux", any( - all( - target_env = "gnu", - any(target_endian = "little", not(target_feature = "crt-static")), - ), - all( - target_env = "musl", - any(not(target_feature = "crt-static"), feature = "std"), - ), + target_env = "gnu", + target_env = "musl", target_env = "ohos", - all(target_env = "uclibc", not(target_feature = "crt-static")), + target_env = "uclibc", portable_atomic_outline_atomics, ), ), target_os = "android", - all( - target_os = "freebsd", - any( - target_endian = "little", - not(target_feature = "crt-static"), - portable_atomic_outline_atomics, - ), - ), + target_os = "freebsd", target_os = "openbsd", all( target_os = "aix", @@ -333,28 +321,15 @@ macro_rules! atomic { all( target_os = "linux", any( - all( - target_env = "gnu", - any(target_endian = "little", not(target_feature = "crt-static")), - ), - all( - target_env = "musl", - any(not(target_feature = "crt-static"), feature = "std"), - ), + target_env = "gnu", + target_env = "musl", target_env = "ohos", - all(target_env = "uclibc", not(target_feature = "crt-static")), + target_env = "uclibc", portable_atomic_outline_atomics, ), ), target_os = "android", - all( - target_os = "freebsd", - any( - target_endian = "little", - not(target_feature = "crt-static"), - portable_atomic_outline_atomics, - ), - ), + target_os = "freebsd", target_os = "openbsd", all( target_os = "aix", diff --git a/src/imp/mod.rs b/src/imp/mod.rs index 33c8091eb..166e31fc2 100644 --- a/src/imp/mod.rs +++ b/src/imp/mod.rs @@ -482,28 +482,15 @@ cfg_sel!({ all( target_os = "linux", any( - all( - target_env = "gnu", - any(target_endian = "little", not(target_feature = "crt-static")), - ), - all( - target_env = "musl", - any(not(target_feature = "crt-static"), feature = "std"), - ), + target_env = "gnu", + target_env = "musl", target_env = "ohos", - all(target_env = "uclibc", not(target_feature = "crt-static")), + target_env = "uclibc", portable_atomic_outline_atomics, ), ), target_os = "android", - all( - target_os = "freebsd", - any( - target_endian = "little", - not(target_feature = "crt-static"), - portable_atomic_outline_atomics, - ), - ), + target_os = "freebsd", target_os = "openbsd", all( target_os = "aix", diff --git a/src/lib.rs b/src/lib.rs index 77a8f6c10..494dcecb3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -337,6 +337,7 @@ See also the [`interrupt` module's readme](https://github.com/taiki-e/portable-a // and can safely be enabled for old nightly as long as version detection works. // - cfg(target_has_atomic) // - asm! on AArch64, Arm, RISC-V, x86, x86_64, Arm64EC, s390x, PowerPC64 +// - global_asm! on AArch64, Arm (test-only) // - llvm_asm! on AVR (tier 3) and MSP430 (tier 3) // - #[instruction_set] on non-Linux/Android pre-v6 Arm (tier 3) // This also helps us test that our assembly code works with the minimum external @@ -356,6 +357,17 @@ See also the [`interrupt` module's readme](https://github.com/taiki-e/portable-a ), feature(asm) )] +#![cfg_attr( + all( + portable_atomic_unstable_asm, + not(portable_atomic_no_outline_atomics), + any( + target_arch = "aarch64", + all(target_arch = "arm", test), // test-only + ), + ), + feature(global_asm) +)] #![cfg_attr( all( portable_atomic_unstable_asm_experimental_arch, diff --git a/src/utils.rs b/src/utils.rs index 65ecca849..07efe1d76 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -91,6 +91,230 @@ macro_rules! __asm { asm!($($tt)*) }; } +#[cfg(not(portable_atomic_no_asm))] +#[allow(unused_macros)] +macro_rules! __global_asm { + ($($tt:tt)*) => { + core::arch::global_asm!($($tt)*); + }; +} +#[cfg(portable_atomic_no_asm)] +#[allow(unused_macros)] +macro_rules! __global_asm { + ($($tt:tt)*) => { + global_asm!($($tt)*); + }; +} + +#[cfg(not(portable_atomic_no_outline_atomics))] +#[cfg(any( + target_arch = "aarch64", + all(target_arch = "arm", test), // test-only + all(target_arch = "powerpc64", target_pointer_width = "64"), +))] +// TODO: use cfg(target_object_format = "elf") once stabilized: https://github.com/rust-lang/rust/issues/152586 +#[cfg(not(any( + // rustc -Z unstable-options --print all-target-specs-json | jq -r '. | to_entries[] | if .value."binary-format" then .key else empty end' | LC_ALL=C sort -u + target_vendor = "apple", // Mach-O + windows, // COFF + target_os = "cygwin", // COFF + target_os = "uefi", // COFF + target_os = "aix", // XCOFF + target_os = "zos", // XPLINK + target_arch = "wasm32", // WASM + target_arch = "wasm64", // WASM +)))] +#[allow(unused_macros)] +#[macro_use] +mod elf { + /// Stable and sound `#[linkage = "extern_weak"]` () + /// alternative using .weakref. + /// + /// # Safety + /// + /// The caller of this macro must pass nullable `$ty` with the correct signature. + /// + /// # Notes + /// + /// - An unresolved weak reference is resolved to null. + /// - Usually this only works with dynamic linking (see below for the exception). + /// - Unlike extern_weak: + /// - This works with stable. + /// - This can be soundly used with "fat LTO + other strong reference + symbol resolved to null" case: + /// + /// - Unlike dlsym: + /// - This only works with ELF. + /// - Even if statically linked, this works if there are other strong references to the same symbol. + /// (See test_weak_* tests in auxv.rs.) + /// - This must not be used for internal symbols: + /// + /// Refs: + #[rustfmt::skip] + macro_rules! weakref { + ($name:ident, $ty:ty $(,)?) => { + cfg_sel!({ + // ------------------------------------------------------------- + // CHERI + // #[cfg(target_abi)] stabilized in Rust 1.78, but known Morello port uses 1.72. + // https://github.com/kent-weak-memory/rust/blob/hacking-1.72.1-descended/compiler/rustc_target/src/spec/aarch64_unknown_freebsd_purecap.rs + #[cfg_attr( + portable_atomic_no_target_abi, + cfg(all(target_arch = "aarch64", portable_atomic_purecap)) + )] + #[cfg_attr( + not(portable_atomic_no_target_abi), + cfg(all(target_arch = "aarch64", target_abi = "purecap")) + )] + { + // https://github.com/ARM-software/abi-aa/blob/2025Q4/aaelf64-morello/aaelf64-morello.rst + weakref_slot_asm!($name, $ty, 16, concat!(".chericap ", weakref_name!($name))); + } + // ------------------------------------------------------------- + // ILP32 + // FDPIC ABI is not yet implemented in LLVM https://github.com/llvm/llvm-project/issues/146959 + #[cfg_attr(portable_atomic_no_target_abi, cfg(any(/* always false */)))] + #[cfg_attr( + not(portable_atomic_no_target_abi), + cfg(all(target_pointer_width = "32", target_arch = "arm", target_abi = "fdpic")) + )] + { + // https://github.com/mickael-guene/fdpic_doc + weakref_slot_asm!( + $name, + $ty, + 4, + concat!(".long ", weakref_name!($name), "(FUNCDESC)"), + ); + } + #[cfg(all( + target_pointer_width = "32", + any( + target_arch = "aarch64", + target_arch = "arm", + // LLVM's PowerPC64 ILP32 support is quite incomplete; looking at the reported issues, + // it appears that it is attempting to read a 4-byte TOC entry as an 8-byte entry? + // https://github.com/llvm/llvm-project/issues/52826 + // https://github.com/llvm/llvm-project/issues/169283 + // target_arch = "powerpc64", + ), + ))] + { + // aarch64: ELF32 in https://github.com/ARM-software/abi-aa/blob/2025Q4/aaelf64/aaelf64.rst + // arm: https://github.com/ARM-software/abi-aa/blob/2025Q4/aaelf32/aaelf32.rst + // powerpc64: https://github.com/ps3dev/PSL1GHT/blob/f649a08fd536a9e27c08c7db2d93a2d7ee4c3bbe/ppu/include/ppu-types.h#L91 + weakref_slot_asm!($name, $ty, 4, concat!(".long ", weakref_name!($name))); + } + // ------------------------------------------------------------- + // LP64 + // pauthtest target has been added in Rust 1.98: https://github.com/rust-lang/rust/pull/155722 + #[cfg_attr(portable_atomic_no_target_abi, cfg(any(/* always false */)))] + #[cfg_attr( + not(portable_atomic_no_target_abi), + cfg(all( + target_pointer_width = "64", + target_arch = "aarch64", + target_abi = "pauthtest", + )) + )] + { + // Our build script emits portable_atomic_no_outline_atomics for platforms where + // weakref is used due to: + // - LLD bug: https://github.com/llvm/llvm-project/issues/173296 + // - There is no good way to recognize `-Z pointer-authentication=-calls` + // // https://github.com/ARM-software/abi-aa/blob/2025Q4/pauthabielf64/pauthabielf64.rst + // // https://llvm.org/docs/PointerAuth.html#assembly-representation + // // https://github.com/rust-lang/rust/blob/1ed2df61a19042f231709eb05d032ae9e2cb2084/compiler/rustc_session/src/session.rs#L125 + // weakref_slot_asm!( + // $name, + // $ty, + // 8, + // concat!(".quad ", weakref_name!($name), "@AUTH(ia,0)"), + // ); + compile_error!("internal error: unreachable"); + } + #[cfg(all( + target_pointer_width = "64", + any(target_arch = "aarch64", target_arch = "powerpc64"), + ))] + { + // aarch64: ELF64 in https://github.com/ARM-software/abi-aa/blob/2025Q4/aaelf64/aaelf64.rst + // powerpc64 (ELFv1): https://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi.html + // powerpc64 (ELFv2): https://openpowerfoundation.org/specifications/64bitelfabi/ + weakref_slot_asm!($name, $ty, 8, concat!(".quad ", weakref_name!($name))); + } + // ------------------------------------------------------------- + // Unsupported + #[cfg(else)] + { + compile_error!("internal error: unreachable"); + } + }); + extern "C" { + #[link_name = weakref_slot!($name)] + pub(crate) static $name: $ty; + } + }; + } + macro_rules! weakref_slot_asm { + ($name:ident, $ty:ty, $balign:literal, $value:expr $(,)?) => { + const _: () = static_assert!(core::mem::size_of::<$ty>() == $balign); + __global_asm!( + // Use .weak (for non-LTO) + .ifndef (for LTO) to avoid duplicated definition error. + // See also https://github.com/bytecodealliance/rustix/blob/v1.1.4/src/backend/linux_raw/vdso_wrappers.rs#L424 + // and https://github.com/rust-lang/rust/issues/133974#issuecomment-2525247548. + concat!(".ifndef ", weakref_slot!($name)), + concat!(".weakref ", weakref_name!($name), ", ", stringify!($name)), + concat!( + ".pushsection .data.rel.ro.", + weakref_name!($name), + ",\"aw\",", + type_prefix!(), + "progbits" + ), + concat!(".balign ", stringify!($balign)), + concat!(".weak ", weakref_slot!($name)), + concat!(".hidden ", weakref_slot!($name)), + concat!(".type ", weakref_slot!($name), ", ", type_prefix!(), "object"), + concat!(weakref_slot!($name), ":"), + $value, + concat!(".size ", weakref_slot!($name), ", .-", weakref_slot!($name)), + ".popsection", + ".endif", + ); + }; + } + #[cfg(target_arch = "arm")] + macro_rules! type_prefix { + () => { + "%" + }; + } + #[cfg(not(target_arch = "arm"))] + macro_rules! type_prefix { + () => { + "@" + }; + } + // We include the version number in the symbol to prevent situations such as when there is a + // fork of an older version within the dependency graph that contains a bug, from causing that + // symbol to override the correct implementation in the newer version. + // NB: Increase version number when the implementation changed. + macro_rules! weakref_prefix { + () => { + "__portable_atomic_1_16_0_weakref_" + }; + } + macro_rules! weakref_slot { + ($name:ident) => { + concat!(weakref_prefix!(), "slot_", stringify!($name)) + }; + } + macro_rules! weakref_name { + ($name:ident) => { + concat!(weakref_prefix!(), stringify!($name)) + }; + } +} #[allow(unused_macros)] #[cfg(not(portable_atomic_no_outline_atomics))] @@ -342,6 +566,21 @@ macro_rules! cfg_sel { cfg_sel! {{ $($rest)+ }} )? }; + ({ + #[cfg_attr(portable_atomic_no_target_abi, cfg($cfg1:meta))] + #[cfg_attr(not(portable_atomic_no_target_abi), cfg($cfg2:meta))] + { $($output:tt)* } + $($( $rest:tt )+)? + }) => { + #[cfg_attr(portable_atomic_no_target_abi, cfg($cfg1))] + #[cfg_attr(not(portable_atomic_no_target_abi), cfg($cfg2))] + cfg_sel! {{#[cfg(else)] { $($output)* }}} + $( + #[cfg_attr(portable_atomic_no_target_abi, cfg(not($cfg1)))] + #[cfg_attr(not(portable_atomic_no_target_abi), cfg(not($cfg2)))] + cfg_sel! {{ $($rest)+ }} + )? + }; } // Equivalent to core::hint::cold_path, but compatible with pre-1.95 rustc. diff --git a/tools/build.sh b/tools/build.sh index fc6ce9dc2..e1ea81883 100755 --- a/tools/build.sh +++ b/tools/build.sh @@ -71,6 +71,7 @@ default_targets=( aarch64-unknown-linux-gnu aarch64-unknown-linux-musl aarch64-unknown-linux-uclibc # custom target + aarch64-unknown-linux-pauthtest aarch64-unknown-netbsd aarch64-unknown-openbsd aarch64-unknown-illumos @@ -165,7 +166,8 @@ is_no_std() { # https://github.com/rust-lang/rust/blob/1.84.0/library/std/build.rs#L65 # ESP-IDF and AIX supports std, but they are often broken. # aarch64-unknown-linux-uclibc is a custom target and libc/std currently doesn't support it. - *-none* | *-psp* | *-psx* | *-cuda* | avr* | *-espidf | *-aix | aarch64-unknown-linux-uclibc) return 0 ;; + # https://github.com/rust-lang/rust/blob/1ed2df61a19042f231709eb05d032ae9e2cb2084/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_pauthtest.rs#L13 + *-none* | *-psp* | *-psx* | *-cuda* | avr* | *-espidf | *-aix | *-pauthtest | aarch64-unknown-linux-uclibc) return 0 ;; esac return 1 } @@ -428,16 +430,19 @@ build() { fi # TODO: handle SIGILL and ERR sed -E "${in_place[@]}" 's/^# (.* #build:static_assert_ffi)/\1/g' Cargo.toml - RUSTFLAGS="${target_rustflags} --cfg portable_atomic_test_no_std_static_assert_ffi" \ + cfg_assert_ffi=' --cfg portable_atomic_test_no_std_static_assert_ffi' + case "${target}" in + *-pauthtest) cfg_assert_ffi='' ;; # TODO: test-helper uses std + esac + RUSTFLAGS="${target_rustflags}${cfg_assert_ffi}" \ x_cargo "${args[@]}" --no-dev-deps --features float --manifest-path Cargo.toml "$@" case "${target}" in - # portable_atomic_outline_atomics only affects AArch64 non-glibc-Linux, powerpc64, and RISC-V Linux. + # portable_atomic_outline_atomics only affects AArch64 non-{glibc,ohos}-Linux, AArch64 OpenBSD (test-only), and powerpc64. # powerpc64le- (little-endian) is skipped because it is pwr8 by default - # RISC-V Linux is skipped because outline-atomics is enabled by default. aarch64*-linux-gnu*) ;; - aarch64*-linux-* | powerpc64-*) + aarch64*-linux-* | aarch64*-openbsd* | powerpc64-*) CARGO_TARGET_DIR="${target_dir}/outline-atomics" \ - RUSTFLAGS="${target_rustflags} --cfg portable_atomic_test_no_std_static_assert_ffi --cfg portable_atomic_outline_atomics" \ + RUSTFLAGS="${target_rustflags}${cfg_assert_ffi} --cfg portable_atomic_outline_atomics" \ x_cargo "${args[@]}" --no-dev-deps --features float --manifest-path Cargo.toml "$@" ;; esac @@ -601,9 +606,9 @@ build() { # Check {,no-}outline-atomics case "${target}" in # portable_atomic_no_outline_atomics only affects x86_64, AArch64, Arm, powerpc64, and RISC-V Linux. - # outline-atomics is disabled by default on AArch64/powerpc64 musl with static linking, and AIX. + # outline-atomics is disabled by default on AIX. # powerpc64le- (little-endian) is skipped because it is pwr8 by default - aarch64*-linux-musl* | powerpc64-*-linux-musl* | powerpc64-*-aix) ;; + powerpc64-*-aix) ;; x86_64* | aarch64* | arm* | thumb* | powerpc64-* | riscv*-linux-*) CARGO_TARGET_DIR="${target_dir}/no-outline-atomics" \ RUSTFLAGS="${target_rustflags} --cfg portable_atomic_no_outline_atomics" \ @@ -611,11 +616,10 @@ build() { ;; esac case "${target}" in - # portable_atomic_outline_atomics only affects AArch64 non-glibc-Linux, powerpc64, and RISC-V Linux. + # portable_atomic_outline_atomics only affects AArch64 non-{glibc,ohos}-Linux, AArch64 OpenBSD (test-only), and powerpc64. # powerpc64le- (little-endian) is skipped because it is pwr8 by default - # RISC-V Linux is skipped because outline-atomics is enabled by default. aarch64*-linux-gnu*) ;; - aarch64*-linux-* | powerpc64-*) + aarch64*-linux-* | aarch64*-openbsd* | powerpc64-*) CARGO_TARGET_DIR="${target_dir}/outline-atomics" \ RUSTFLAGS="${target_rustflags} --cfg portable_atomic_outline_atomics" \ x_cargo "${args[@]}" "$@" @@ -626,22 +630,27 @@ build() { esac # Check target features case "${target}" in + # pointer authentication requires dynamic linking. + *-pauthtest) ;; # Only AArch64 and powerpc64 detections are affected by crt-static. aarch64* | powerpc64-*) - # Script to get builtin targets that support crt-static: - # rustc -Z unstable-options --print all-target-specs-json | jq -r '. | to_entries[] | if .value."crt-static-respected" == true then .value.os else empty end' | LC_ALL=C sort -u | tr '\n' '|' | sed -E 's/\|$/\n/g' - if grep -Eq "^target_os=\"(aix|android|freebsd|hurd|linux|nto|redox|teeos|trusty|vxworks|wasi|windows)\"" <<<"${cfgs}"; then - if grep -Eq '^target_feature="crt-static"' <<<"${cfgs}"; then - CARGO_TARGET_DIR="${target_dir}/no-crt-static" \ - RUSTFLAGS="${target_rustflags} -C target-feature=-crt-static" \ - x_cargo "${args[@]}" "$@" - CARGO_TARGET_DIR="${target_dir}/no-crt-static-no-outline-atomics" \ - RUSTFLAGS="${target_rustflags} -C target-feature=-crt-static --cfg portable_atomic_no_outline_atomics" \ - x_cargo "${args[@]}" "$@" - else - CARGO_TARGET_DIR="${target_dir}/crt-static" \ - RUSTFLAGS="${target_rustflags} -C target-feature=+crt-static" \ - x_cargo "${args[@]}" ${no_target_flags+"--target=${target}"} "$@" + # Only musl has cfg(target_feature = "crt-static") dependent code in production. + if [[ "${target}" == *'-linux-musl'* ]] || [[ -n "${TESTS:-}" ]]; then + # Script to get builtin targets that support crt-static: + # rustc -Z unstable-options --print all-target-specs-json | jq -r '. | to_entries[] | if .value."crt-static-respected" == true then .value.os else empty end' | LC_ALL=C sort -u | tr '\n' '|' | sed -E 's/\|$/\n/g' + if grep -Eq "^target_os=\"(aix|android|freebsd|hurd|linux|nto|redox|teeos|trusty|vxworks|wasi|windows)\"" <<<"${cfgs}"; then + if grep -Eq '^target_feature="crt-static"' <<<"${cfgs}"; then + CARGO_TARGET_DIR="${target_dir}/no-crt-static" \ + RUSTFLAGS="${target_rustflags} -C target-feature=-crt-static" \ + x_cargo "${args[@]}" "$@" + CARGO_TARGET_DIR="${target_dir}/no-crt-static-no-outline-atomics" \ + RUSTFLAGS="${target_rustflags} -C target-feature=-crt-static --cfg portable_atomic_no_outline_atomics" \ + x_cargo "${args[@]}" "$@" + else + CARGO_TARGET_DIR="${target_dir}/crt-static" \ + RUSTFLAGS="${target_rustflags} -C target-feature=+crt-static" \ + x_cargo "${args[@]}" ${no_target_flags+"--target=${target}"} "$@" + fi fi fi ;;