Skip to content

Commit

Permalink
detect: Clean up alternative impl tests
Browse files Browse the repository at this point in the history
  • Loading branch information
taiki-e committed Oct 1, 2024
1 parent c1c652e commit 21b838c
Show file tree
Hide file tree
Showing 6 changed files with 255 additions and 65 deletions.
2 changes: 2 additions & 0 deletions .github/.cspell/project-dictionary.txt
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@ dlfcn
dlsym
DWCAS
ecall
EINVAL
elems
ENOENT
espup
evbmips
exynos
Expand Down
3 changes: 1 addition & 2 deletions src/imp/detect/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ Here is the table of targets that support run-time CPU feature detection and the
| aarch64 | illumos | getisax | lse, lse2 | Disabled by default |
| aarch64/arm64ec | windows | IsProcessorFeaturePresent | lse | Enabled by default |
| aarch64 | fuchsia | zx_system_get_features | lse | Enabled by default |
| riscv32 | linux | riscv_hwprobe | all | Disabled by default |
| riscv64 | linux | riscv_hwprobe | all | Disabled by default |
| riscv32/riscv64 | linux/android | riscv_hwprobe | all | Disabled by default |
| powerpc64 | linux | getauxval | all | Disabled by default |
| powerpc64 | freebsd | elf_aux_info | all | Disabled by default |
| powerpc64 | openbsd | elf_aux_info | all | Disabled by default |
Expand Down
6 changes: 3 additions & 3 deletions src/imp/detect/aarch64_aa64reg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,7 @@ mod tests {
#[allow(clippy::cast_possible_wrap)]
#[cfg(target_os = "netbsd")]
#[test]
fn test_netbsd() {
fn test_alternative() {
use c_types::*;
use imp::ffi;
#[cfg(not(portable_atomic_no_asm))]
Expand All @@ -371,7 +371,7 @@ mod tests {
// much as Linux does (It may actually be stable enough, though: https://lists.llvm.org/pipermail/llvm-dev/2019-June/133393.html).
//
// This is currently used only for testing.
unsafe fn sysctl_cpu_id_asm_syscall(name: &[&[u8]]) -> Result<AA64Reg, c_int> {
unsafe fn sysctl_cpu_id_no_libc(name: &[&[u8]]) -> Result<AA64Reg, c_int> {
// https://github.com/golang/go/blob/4badad8d477ffd7a6b762c35bc69aed82faface7/src/syscall/asm_netbsd_arm64.s
#[inline]
unsafe fn sysctl(
Expand Down Expand Up @@ -489,7 +489,7 @@ mod tests {
unsafe {
assert_eq!(
imp::sysctl_cpu_id(b"machdep.cpu0.cpu_id\0").unwrap(),
sysctl_cpu_id_asm_syscall(&[b"machdep", b"cpu0", b"cpu_id"]).unwrap()
sysctl_cpu_id_no_libc(&[b"machdep", b"cpu0", b"cpu_id"]).unwrap()
);
}
}
Expand Down
138 changes: 137 additions & 1 deletion src/imp/detect/aarch64_apple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ mod ffi {

extern "C" {
// https://developer.apple.com/documentation/kernel/1387446-sysctlbyname
// https://github.com/apple-oss-distributions/xnu/blob/5c2921b07a2480ab43ec66f5b9e41cb872bc554f/bsd/sys/sysctl.h
// https://github.com/apple-oss-distributions/xnu/blob/8d741a5de7ff4191bf97d57b9f54c2f6d4a15585/bsd/sys/sysctl.h
pub(crate) fn sysctlbyname(
name: *const c_char,
old_p: *mut c_void,
Expand Down Expand Up @@ -122,6 +122,142 @@ mod tests {
}
}

#[cfg(target_pointer_width = "64")]
#[test]
fn test_alternative() {
use c_types::*;
#[cfg(not(portable_atomic_no_asm))]
use std::arch::asm;
use std::mem;
use test_helper::{libc, sys};
// Call syscall using asm instead of libc.
// Note that macOS does not guarantee the stability of raw syscall.
// (And they actually changed it: https://go-review.googlesource.com/c/go/+/25495)
//
// This is currently used only for testing.
unsafe fn sysctlbyname32_no_libc(name: &[u8]) -> Result<u32, c_int> {
// https://github.com/apple-oss-distributions/xnu/blob/8d741a5de7ff4191bf97d57b9f54c2f6d4a15585/bsd/kern/syscalls.master#L298
#[inline]
unsafe fn sysctl(
name: *const c_int,
name_len: c_uint,
old_p: *mut c_void,
old_len_p: *mut c_size_t,
new_p: *const c_void,
new_len: c_size_t,
) -> Result<c_int, c_int> {
// https://github.com/apple-oss-distributions/xnu/blob/8d741a5de7ff4191bf97d57b9f54c2f6d4a15585/osfmk/mach/i386/syscall_sw.h#L158
#[inline(always)]
fn syscall_construct_unix(n: u64) -> u64 {
const SYSCALL_CLASS_UNIX: u64 = 2;
const SYSCALL_CLASS_SHIFT: u64 = 24;
const SYSCALL_CLASS_MASK: u64 = 0xFF << SYSCALL_CLASS_SHIFT;
const SYSCALL_NUMBER_MASK: u64 = !SYSCALL_CLASS_MASK;
(SYSCALL_CLASS_UNIX << SYSCALL_CLASS_SHIFT) | (SYSCALL_NUMBER_MASK & n)
}
#[allow(clippy::cast_possible_truncation)]
// SAFETY: the caller must uphold the safety contract.
unsafe {
// https://github.com/apple-oss-distributions/xnu/blob/8d741a5de7ff4191bf97d57b9f54c2f6d4a15585/bsd/kern/syscalls.master#L4
let mut n = syscall_construct_unix(202);
let r: i64;
asm!(
"svc 0",
"b.cc 2f",
"mov x16, x0",
"mov x0, #-1",
"2:",
inout("x16") n,
inout("x0") ptr_reg!(name) => r,
inout("x1") name_len as u64 => _,
in("x2") ptr_reg!(old_p),
in("x3") ptr_reg!(old_len_p),
in("x4") ptr_reg!(new_p),
in("x5") new_len as u64,
options(nostack),
);
if r as c_int == -1 {
Err(n as c_int)
} else {
Ok(r as c_int)
}
}
}
// https://github.com/apple-oss-distributions/Libc/blob/af11da5ca9d527ea2f48bb7efbd0f0f2a4ea4812/gen/FreeBSD/sysctlbyname.c
unsafe fn sysctlbyname(
name: &[u8],
old_p: *mut c_void,
old_len_p: *mut c_size_t,
new_p: *mut c_void,
new_len: c_size_t,
) -> Result<c_int, c_int> {
let mut real_oid: [c_int; sys::CTL_MAXNAME as usize + 2] = unsafe { mem::zeroed() };

// Note that this is undocumented API.
// Although FreeBSD defined it in sys/sysctl.h since https://github.com/freebsd/freebsd-src/commit/382e01c8dc7f328f46c61c82a29222f432f510f7
let mut name2oid_oid: [c_int; 2] = [0, 3];

let mut oid_len = mem::size_of_val(&real_oid);
unsafe {
sysctl(
name2oid_oid.as_mut_ptr(),
2,
real_oid.as_mut_ptr().cast::<c_void>(),
&mut oid_len,
name.as_ptr().cast::<c_void>() as *mut c_void,
name.len() - 1,
)?
};
oid_len /= mem::size_of::<c_int>();
unsafe {
sysctl(real_oid.as_mut_ptr(), oid_len as u32, old_p, old_len_p, new_p, new_len)
}
}

const OUT_LEN: ffi::c_size_t = core::mem::size_of::<u32>() as ffi::c_size_t;

debug_assert_eq!(name.last(), Some(&0), "{:?}", name);
debug_assert_eq!(name.iter().filter(|&&v| v == 0).count(), 1, "{:?}", name);

let mut out = 0_u32;
let mut out_len = OUT_LEN;
// SAFETY:
// - the caller must guarantee that `name` a valid C string.
// - `out_len` does not exceed the size of `out`.
// - `sysctlbyname` is thread-safe.
let res = unsafe {
sysctlbyname(
name,
(&mut out as *mut u32).cast::<ffi::c_void>(),
&mut out_len,
ptr::null_mut(),
0,
)?
};
debug_assert_eq!(res, 0);
debug_assert_eq!(out_len, OUT_LEN);
Ok(out)
}

for name in [
&b"hw.optional.armv8_1_atomics\0"[..],
b"hw.optional.arm.FEAT_LSE\0",
b"hw.optional.arm.FEAT_LSE2\0",
b"hw.optional.arm.FEAT_LSE128\0",
b"hw.optional.arm.FEAT_LRCPC\0",
b"hw.optional.arm.FEAT_LRCPC2\0",
b"hw.optional.arm.FEAT_LRCPC3\0",
] {
unsafe {
if let Some(res) = sysctlbyname32(name) {
assert_eq!(res, sysctlbyname32_no_libc(name).unwrap());
} else {
assert_eq!(sysctlbyname32_no_libc(name).unwrap_err(), libc::ENOENT);
}
}
}
}

// Static assertions for FFI bindings.
// This checks that FFI bindings defined in this crate, FFI bindings defined
// in libc, and FFI bindings generated for the platform's latest header file
Expand Down
Loading

0 comments on commit 21b838c

Please sign in to comment.