From ac8ddbf5fe29d746bd024211cc805f84a5882203 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Mon, 26 Jun 2023 16:10:37 +0900 Subject: [PATCH] [DO NOT MERGE] use asm syscall --- src/imp/atomic128/detect/auxv.rs | 202 +++++++++++++++++++++++++------ 1 file changed, 167 insertions(+), 35 deletions(-) diff --git a/src/imp/atomic128/detect/auxv.rs b/src/imp/atomic128/detect/auxv.rs index 0ff640a4..89e02c81 100644 --- a/src/imp/atomic128/detect/auxv.rs +++ b/src/imp/atomic128/detect/auxv.rs @@ -121,28 +121,132 @@ mod os { #[allow(non_camel_case_types)] pub(super) mod ffi { pub(crate) use super::super::c_types::{c_int, c_long, c_size_t, c_uint, c_ulong, c_void}; + use core::arch::asm; // https://github.com/rust-lang/libc/blob/0.2.139/src/unix/mod.rs#LL25 pub(crate) type pid_t = i32; + // https://github.com/freebsd/freebsd-src/blob/67633329693723734aef003044e13db07f1fbf5f/sys/sys/syscall.h + pub(crate) const SYS_getpid: usize = 20; + pub(crate) const SYS_sysctl: usize = 202; - extern "C" { - // Defined in sys/sysctl.h. - // https://man.freebsd.org/sysctl(3) - // https://github.com/freebsd/freebsd-src/blob/4aeb939ecf8b2b6913ae51d828416743bad812ab/sys/sys/sysctl.h - // https://github.com/rust-lang/libc/blob/0.2.139/src/unix/bsd/freebsdlike/mod.rs#L1670-L1677 - pub(crate) 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, - ) -> c_int; - - // Defined in unistd.h. - // https://man.freebsd.org/getpid(2) - // https://github.com/freebsd/freebsd-src/blob/deb63adf945d446ed91a9d84124c71f15ae571d1/include/unistd.h - pub(crate) fn getpid() -> pid_t; + // https://github.com/freebsd/freebsd-src/blob/main/lib/libc/aarch64/SYS.h + // https://github.com/golang/go/blob/4badad8d477ffd7a6b762c35bc69aed82faface7/src/syscall/asm_freebsd_arm64.s + #[cfg(target_arch = "aarch64")] + #[inline] + pub(crate) fn getpid() -> pid_t { + #[allow(clippy::cast_possible_truncation)] + // SAFETY: calling getpid is safe. + unsafe { + let n = SYS_getpid; + let r: isize; + asm!( + "svc 0", + in("x8") n, + out("x0") r, + options(nostack, readonly), + ); + r as pid_t + } + } + #[cfg(target_arch = "aarch64")] + #[inline] + pub(crate) 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, + ) -> c_int { + #[allow(clippy::cast_possible_truncation)] + // SAFETY: the caller must uphold the safety contract. + unsafe { + let n = SYS_sysctl; + let r: isize; + asm!( + "svc 0", + "b.cc 2f", + // "mov {err}, x0", + "mov x0, #-1", + "2:", + in("x8") n, + inout("x0") name => r, + inout("x1") name_len as usize => _, + in("x2") old_p, + in("x3") old_len_p, + in("x4") new_p, + in("x5") new_len, + options(nostack), + ); + r as c_int + } + } + + // https://github.com/freebsd/freebsd-src/blob/main/lib/libc/powerpc64/SYS.h + #[cfg(target_arch = "powerpc64")] + #[inline] + pub(crate) fn getpid() -> pid_t { + #[allow(clippy::cast_possible_truncation)] + // SAFETY: calling getpid is safe. + unsafe { + let n = SYS_getpid; + let r: isize; + asm!( + "sc", + inout("r0") n => _, + out("r3") r, + out("r4") _, + out("r5") _, + out("r6") _, + out("r7") _, + out("r8") _, + out("r9") _, + out("r10") _, + out("r11") _, + out("r12") _, + out("cr0") _, + options(nostack, readonly), + ); + r as pid_t + } + } + #[cfg(target_arch = "powerpc64")] + #[inline] + pub(crate) 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, + ) -> c_int { + #[allow(clippy::cast_possible_truncation)] + // SAFETY: the caller must uphold the safety contract. + unsafe { + let n = SYS_sysctl; + let r: isize; + asm!( + "sc", + "bns 2f", + // "mr {err}, %r3", + "li %r3, -1", + "2:", + inout("r0") n => _, + inout("r3") name => r, + inout("r4") name_len as usize => _, + inout("r5") old_p => _, + inout("r6") old_len_p => _, + inout("r7") new_p => _, + inout("r8") new_len => _, + out("r9") _, + out("r10") _, + out("r11") _, + out("r12") _, + out("cr0") _, + options(nostack), + ); + r as c_int + } } // Defined in sys/sysctl.h. @@ -194,8 +298,7 @@ mod os { let mut len: ffi::c_size_t = core::mem::size_of_val(&auxv) as ffi::c_size_t; - // SAFETY: calling getpid is safe. - let pid = unsafe { ffi::getpid() }; + let pid = ffi::getpid(); let mib = [ffi::CTL_KERN, ffi::KERN_PROC, ffi::KERN_PROC_AUXV, pid]; #[allow(clippy::cast_possible_truncation)] @@ -322,7 +425,7 @@ mod tests { #[test] fn test_freebsd() { use test_helper::sys; - fn getauxval_elf_aux_info(aux: ffi::c_int) -> ffi::c_ulong { + fn getauxval_libc_elf_aux_info(aux: ffi::c_int) -> ffi::c_ulong { #[allow(clippy::cast_possible_wrap, clippy::cast_possible_truncation)] const OUT_LEN: ffi::c_int = core::mem::size_of::() as ffi::c_int; let mut out: ffi::c_ulong = 0; @@ -341,7 +444,49 @@ mod tests { } out } - assert_eq!(os::getauxval(ffi::AT_HWCAP), getauxval_elf_aux_info(ffi::AT_HWCAP)); + fn getauxval_libc_sysctl(key: ffi::c_int) -> ffi::c_ulong { + use core::{mem, ptr}; + + let mut auxv = + [ffi::Elf_Auxinfo { a_type: 0, a_un: ffi::unnamed { a_val: 0 } }; ffi::AT_COUNT]; + + let mut len = mem::size_of_val(&auxv) as ffi::c_size_t; + + // SAFETY: calling getpid is safe. + let pid = unsafe { sys::getpid() }; + let mib = [ffi::CTL_KERN, ffi::KERN_PROC, ffi::KERN_PROC_AUXV, pid]; + + #[allow(clippy::cast_possible_truncation)] + // SAFETY: + // - `mib.len()` does not exceed the size of `mib`. + // - `out_len` does not exceed the size of `out`. + // - `sysctl` is thread-safe. + let res = unsafe { + sys::sysctl( + mib.as_ptr(), + mib.len() as ffi::c_uint, + auxv.as_mut_ptr().cast::(), + &mut len, + ptr::null_mut(), + 0, + ) + }; + + if res != -1 { + for aux in &auxv { + if aux.a_type == key as ffi::c_long { + // SAFETY: aux.a_un is #[repr(C)] union and all fields have + // the same size and can be safely transmuted to integers. + return unsafe { aux.a_un.a_val as ffi::c_ulong }; + } + } + } + 0 + } + assert_eq!(os::getauxval(ffi::AT_HWCAP), getauxval_libc_elf_aux_info(ffi::AT_HWCAP)); + assert_eq!(os::getauxval(ffi::AT_HWCAP2), getauxval_libc_elf_aux_info(ffi::AT_HWCAP2)); + assert_eq!(os::getauxval(ffi::AT_HWCAP), getauxval_libc_sysctl(ffi::AT_HWCAP)); + assert_eq!(os::getauxval(ffi::AT_HWCAP2), getauxval_libc_sysctl(ffi::AT_HWCAP2)); } // Static assertions for FFI bindings. @@ -384,19 +529,6 @@ mod tests { #[cfg(target_os = "freebsd")] { let _: ffi::pid_t = 0 as libc::pid_t; - let mut _sysctl: unsafe extern "C" fn( - *const ffi::c_int, - ffi::c_uint, - *mut ffi::c_void, - *mut ffi::c_size_t, - *const ffi::c_void, - ffi::c_size_t, - ) -> ffi::c_int = ffi::sysctl; - _sysctl = libc::sysctl; - _sysctl = sys::sysctl; - let mut _getpid: unsafe extern "C" fn() -> ffi::pid_t = ffi::getpid; - _getpid = libc::getpid; - _getpid = sys::getpid; static_assert!(ffi::CTL_KERN == libc::CTL_KERN); static_assert!(ffi::CTL_KERN == sys::CTL_KERN as _); static_assert!(ffi::KERN_PROC == libc::KERN_PROC);