Skip to content

Commit

Permalink
detect: Make FreeBSD feature detection code compatible with 11.3 and …
Browse files Browse the repository at this point in the history
…older
  • Loading branch information
taiki-e committed Jun 26, 2023
1 parent bbfe762 commit 1518f14
Show file tree
Hide file tree
Showing 16 changed files with 263 additions and 42 deletions.
3 changes: 3 additions & 0 deletions .github/.cspell/project-dictionary.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ alcgr
algr
aosp
armasm
Auxinfo
auxv
auxvec
backoff
Expand Down Expand Up @@ -45,6 +46,7 @@ espup
exynos
FIQs
getauxval
getpid
HWCAP
ifunc
includepath
Expand Down Expand Up @@ -138,6 +140,7 @@ uapi
uart
umax
umin
unistd
unparse
usart
uscat
Expand Down
2 changes: 1 addition & 1 deletion src/imp/atomic128/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ Here is the table of targets that support run-time feature detection and the ins
| x86_64 | all (except for sgx) | cpuid | Enabled by default |
| aarch64 | linux | getauxval | Only enabled by default on `*-linux-gnu*`, and `*-linux-musl*"` (default is static linking)/`*-linux-ohos*` (default is dynamic linking) with dynamic linking enabled. |
| aarch64 | android | getauxval | Enabled by default |
| aarch64 | freebsd | elf_aux_info | Enabled by default |
| aarch64 | freebsd | sysctl | Enabled by default |
| aarch64 | openbsd | sysctl | Enabled by default |
| aarch64 | macos | sysctl | Currently only used in tests because FEAT_LSE and FEAT_LSE2 are always available at compile-time. |
| aarch64 | windows | IsProcessorFeaturePresent | Enabled by default |
Expand Down
176 changes: 139 additions & 37 deletions src/imp/atomic128/detect/auxv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,18 +36,9 @@
// aarch64 FreeBSD. However, they do not work on FreeBSD 12 on QEMU (confirmed
// on FreeBSD 12.{2,3,4}), and we got SIGILL (worked on FreeBSD 13 and 14).
//
// So use elf_aux_info instead of mrs like compiler-rt does.
// https://man.freebsd.org/elf_aux_info(3)
// So parse ELF auxiliary vectors instead of mrs like compiler-rt does.
// https://reviews.llvm.org/D109330
//
// elf_aux_info is available on FreeBSD 12.0+ and 11.4+:
// https://github.com/freebsd/freebsd-src/commit/0b08ae2120cdd08c20a2b806e2fcef4d0a36c470
// https://github.com/freebsd/freebsd-src/blob/release/11.4.0/sys/sys/auxv.h
// On FreeBSD, [aarch64 support is available on FreeBSD 11.0+](https://www.freebsd.org/releases/11.0R/relnotes/#hardware-arm),
// but FreeBSD 11 (11.4) was EoL on 2021-09-30, and FreeBSD 11.3 was EoL on 2020-09-30:
// https://www.freebsd.org/security/unsupported
// See also https://github.com/rust-lang/stdarch/pull/611#issuecomment-445464613
//
// # PowerPC64
//
// On PowerPC64, outline-atomics is currently disabled by default mainly for
Expand Down Expand Up @@ -127,42 +118,112 @@ mod os {
mod os {
// core::ffi::c_* (except c_void) requires Rust 1.64, libc will soon require Rust 1.47
#[cfg_attr(test, allow(dead_code))]
#[allow(non_camel_case_types)]
pub(super) mod ffi {
pub(crate) use super::super::c_types::{c_int, c_ulong, c_void};
pub(crate) use super::super::c_types::{c_int, c_long, c_size_t, c_uint, c_ulong, c_void};

// https://github.com/rust-lang/libc/blob/0.2.139/src/unix/mod.rs#LL25
pub(crate) type pid_t = i32;

extern "C" {
// Defined in sys/auxv.h.
// https://man.freebsd.org/elf_aux_info(3)
// https://github.com/freebsd/freebsd-src/blob/deb63adf945d446ed91a9d84124c71f15ae571d1/sys/sys/auxv.h
pub(crate) fn elf_aux_info(aux: c_int, buf: *mut c_void, buf_len: c_int) -> c_int;
// 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;
}

// Defined in sys/sysctl.h.
// https://github.com/freebsd/freebsd-src/blob/4aeb939ecf8b2b6913ae51d828416743bad812ab/sys/sys/sysctl.h
pub(crate) const CTL_KERN: c_int = 1;
pub(crate) const KERN_PROC: c_int = 14;
pub(crate) const KERN_PROC_AUXV: c_int = 36;

// Defined in sys/elf_common.h.
// https://github.com/freebsd/freebsd-src/blob/deb63adf945d446ed91a9d84124c71f15ae571d1/sys/sys/elf_common.h
#[cfg(any(test, target_arch = "aarch64"))]
pub(crate) const AT_HWCAP: c_int = 25;
#[cfg(any(test, target_arch = "powerpc64"))]
pub(crate) const AT_HWCAP2: c_int = 26;
pub(crate) const AT_COUNT: c_size_t = 37;

// https://github.com/freebsd/freebsd-src/blob/deb63adf945d446ed91a9d84124c71f15ae571d1/sys/arm64/include/elf.h#L56-L63
#[derive(Clone, Copy)]
#[repr(C)]
pub(crate) struct Elf_Auxinfo {
pub(crate) a_type: c_long,
pub(crate) a_un: unnamed,
}
#[derive(Clone, Copy)]
#[repr(C)]
pub(crate) union unnamed {
pub(crate) a_val: c_long,
pub(crate) a_ptr: *mut c_void,
pub(crate) a_fcn: Option<unsafe extern "C" fn()>,
}
}

pub(super) fn getauxval(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::<ffi::c_ulong>() as ffi::c_int;
let mut out: ffi::c_ulong = 0;
use core::ptr;

// Adapted from https://github.com/rust-lang/stdarch/blob/a0c30f3e3c75adcd6ee7efc94014ebcead61c507/crates/std_detect/src/detect/os/freebsd/auxvec.rs#L52
//
// This is almost equivalent to what elf_aux_info does.
// https://man.freebsd.org/elf_aux_info(3)
// On FreeBSD, [aarch64 support is available on FreeBSD 11.0+](https://www.freebsd.org/releases/11.0R/relnotes/#hardware-arm),
// but elf_aux_info is available on FreeBSD 12.0+ and 11.4+:
// https://github.com/freebsd/freebsd-src/commit/0b08ae2120cdd08c20a2b806e2fcef4d0a36c470
// https://github.com/freebsd/freebsd-src/blob/release/11.4.0/sys/sys/auxv.h
// so use sysctl instead of elf_aux_info.
// Note that FreeBSD 11 (11.4) was EoL on 2021-09-30, and FreeBSD 11.3 was EoL on 2020-09-30:
// https://www.freebsd.org/security/unsupported
pub(super) fn getauxval(key: ffi::c_int) -> ffi::c_ulong {
let mut auxv: [ffi::Elf_Auxinfo; ffi::AT_COUNT] =
[ffi::Elf_Auxinfo { a_type: 0, a_un: ffi::unnamed { a_val: 0 } }; ffi::AT_COUNT];

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 mib = [ffi::CTL_KERN, ffi::KERN_PROC, ffi::KERN_PROC_AUXV, pid];

#[allow(clippy::cast_possible_truncation)]
// SAFETY:
// - the pointer is valid because we got it from a reference.
// - `OUT_LEN` is the same as the size of `out`.
// - `elf_aux_info` is thread-safe.
unsafe {
let res = ffi::elf_aux_info(
aux,
(&mut out as *mut ffi::c_ulong).cast::<ffi::c_void>(),
OUT_LEN,
);
// If elf_aux_info fails, `out` will be left at zero (which is the proper default value).
debug_assert!(res == 0 || out == 0);
// - `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 {
ffi::sysctl(
mib.as_ptr(),
mib.len() as ffi::c_uint,
auxv.as_mut_ptr().cast::<ffi::c_void>(),
&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 };
}
}
}
out
0
}
}

Expand Down Expand Up @@ -257,6 +318,32 @@ mod tests {
}
}

#[cfg(target_os = "freebsd")]
#[test]
fn test_freebsd() {
use test_helper::sys;
fn getauxval_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::<ffi::c_ulong>() as ffi::c_int;
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`.
// - `elf_aux_info` is thread-safe.
unsafe {
let res = sys::elf_aux_info(
aux,
(&mut out as *mut ffi::c_ulong).cast::<ffi::c_void>(),
OUT_LEN,
);
// If elf_aux_info fails, `out` will be left at zero (which is the proper default value).
debug_assert!(res == 0 || out == 0);
}
out
}
assert_eq!(os::getauxval(ffi::AT_HWCAP), getauxval_elf_aux_info(ffi::AT_HWCAP));
}

// 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 Expand Up @@ -296,13 +383,28 @@ mod tests {
}
#[cfg(target_os = "freebsd")]
{
let mut _elf_aux_info: unsafe extern "C" fn(
ffi::c_int,
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,
ffi::c_int,
) -> ffi::c_int = ffi::elf_aux_info;
_elf_aux_info = libc::elf_aux_info;
_elf_aux_info = sys::elf_aux_info;
*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);
static_assert!(ffi::KERN_PROC == sys::KERN_PROC as _);
static_assert!(ffi::KERN_PROC_AUXV == libc::KERN_PROC_AUXV);
static_assert!(ffi::KERN_PROC_AUXV == sys::KERN_PROC_AUXV as _);
// static_assert!(ffi::AT_COUNT == libc::AT_COUNT); // libc doesn't have this on FreeBSD
static_assert!(ffi::AT_COUNT == sys::AT_COUNT as _);
}
#[cfg(not(target_os = "freebsd"))] // libc doesn't have this on FreeBSD
static_assert!(ffi::AT_HWCAP == libc::AT_HWCAP);
Expand Down
6 changes: 5 additions & 1 deletion tests/helper/src/gen/sys/aarch64_freebsd/mod.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions tests/helper/src/gen/sys/aarch64_freebsd/sys_elf_common.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 17 additions & 0 deletions tests/helper/src/gen/sys/aarch64_freebsd/sys_sysctl.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions tests/helper/src/gen/sys/aarch64_freebsd/unistd.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion tests/helper/src/gen/sys/powerpc64_freebsd/mod.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 17 additions & 0 deletions tests/helper/src/gen/sys/powerpc64_freebsd/sys_sysctl.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions tests/helper/src/gen/sys/powerpc64_freebsd/unistd.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion tests/helper/src/gen/sys/powerpc64le_freebsd/mod.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 17 additions & 0 deletions tests/helper/src/gen/sys/powerpc64le_freebsd/sys_sysctl.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions tests/helper/src/gen/sys/powerpc64le_freebsd/unistd.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 1518f14

Please sign in to comment.