include!("common.rs");
use self::os::ffi;
#[cfg(any(target_os = "linux", target_os = "android"))]
mod os {
#[cfg_attr(test, allow(dead_code))]
pub(super) mod ffi {
pub(crate) use crate::utils::ffi::c_ulong;
#[allow(unused_imports)]
pub(crate) use crate::utils::ffi::{c_char, c_int, c_void};
sys_const!({
pub(crate) const AT_HWCAP: c_ulong = 16;
#[cfg(any(
test,
all(target_arch = "aarch64", target_pointer_width = "64"),
target_arch = "powerpc64",
))]
pub(crate) const AT_HWCAP2: c_ulong = 26;
#[cfg(test)]
#[cfg(not(all(target_arch = "aarch64", target_pointer_width = "32")))]
pub(crate) const AT_HWCAP3: c_ulong = 29;
#[cfg(test)]
#[cfg(not(all(target_arch = "aarch64", target_pointer_width = "32")))]
pub(crate) const AT_HWCAP4: c_ulong = 30;
#[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);
#[cfg(all(target_arch = "aarch64", target_os = "android"))]
pub(crate) const PROP_VALUE_MAX: c_int = 92;
});
sys_fn!({
extern "C" {
#[cfg(any(
test,
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 getauxval(type_: c_ulong) -> c_ulong;
#[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;
#[cfg(all(target_arch = "aarch64", target_os = "android"))]
pub(crate) fn __system_property_get(
name: *const c_char,
value: *mut c_char,
) -> c_int;
}
});
}
pub(super) type GetauxvalTy = unsafe extern "C" fn(ffi::c_ulong) -> ffi::c_ulong;
pub(super) fn getauxval(type_: ffi::c_ulong) -> ffi::c_ulong {
cfg_sel!({
#[cfg(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,
))]
{
let getauxval: GetauxvalTy = ffi::getauxval;
}
#[cfg(else)]
{
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)
};
}
});
unsafe { getauxval(type_) }
}
}
#[cfg(any(target_os = "freebsd", target_os = "openbsd"))]
mod os {
use core::mem;
#[cfg_attr(test, allow(dead_code))]
pub(super) mod ffi {
#[allow(unused_imports)]
pub(crate) use crate::utils::ffi::c_char;
pub(crate) use crate::utils::ffi::{c_int, c_ulong, c_void};
sys_const!({
pub(crate) const AT_HWCAP: c_int = 25;
#[cfg(any(
test,
all(target_os = "freebsd", target_arch = "aarch64", target_pointer_width = "64"),
target_arch = "powerpc64",
))]
pub(crate) const AT_HWCAP2: c_int = 26;
#[cfg(test)]
#[cfg(not(target_os = "openbsd"))]
pub(crate) const AT_HWCAP3: c_int = 38;
#[cfg(test)]
#[cfg(not(target_os = "openbsd"))]
pub(crate) const AT_HWCAP4: c_int = 39;
#[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 = -2_isize as usize as *mut c_void;
});
sys_fn!({
extern "C" {
#[cfg(any(
test,
any(
all(
target_os = "freebsd",
any(
target_arch = "aarch64",
all(target_arch = "powerpc64", target_endian = "little"),
),
),
portable_atomic_outline_atomics,
),
))]
pub(crate) fn elf_aux_info(aux: c_int, buf: *mut c_void, buf_len: c_int) -> c_int;
#[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 {
#[allow(clippy::cast_possible_wrap, clippy::cast_possible_truncation)]
const OUT_LEN: ffi::c_int = mem::size_of::<ffi::c_ulong>() as ffi::c_int;
cfg_sel!({
#[cfg(any(
all(
target_os = "freebsd",
any(
target_arch = "aarch64",
all(target_arch = "powerpc64", target_endian = "little"),
),
),
portable_atomic_outline_atomics,
))]
{
let elf_aux_info: ElfAuxInfoTy = ffi::elf_aux_info;
}
#[cfg(else)]
{
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)
};
}
});
let mut out: ffi::c_ulong = 0;
let res = unsafe {
elf_aux_info(aux, (&mut out as *mut ffi::c_ulong).cast::<ffi::c_void>(), OUT_LEN)
};
debug_assert!(res == 0 || out == 0);
out
}
}
use self::arch::_detect;
#[cfg(target_arch = "aarch64")]
mod arch {
use super::{CpuInfo, CpuInfoFlag, ffi, os};
sys_const!({
pub(crate) const HWCAP_ATOMICS: ffi::c_ulong = 1 << 8;
#[cfg(test)]
pub(crate) const HWCAP_CPUID: ffi::c_ulong = 1 << 11;
#[cfg(test)]
pub(crate) const HWCAP_LRCPC: ffi::c_ulong = 1 << 15;
pub(crate) const HWCAP_USCAT: ffi::c_ulong = 1 << 25;
#[cfg(test)]
pub(crate) const HWCAP_ILRCPC: ffi::c_ulong = 1 << 26;
#[cfg(not(target_os = "openbsd"))]
#[cfg(target_pointer_width = "64")]
pub(crate) const HWCAP2_LRCPC3: ffi::c_ulong = 1 << 46;
#[cfg(not(target_os = "openbsd"))]
#[cfg(target_pointer_width = "64")]
pub(crate) const HWCAP2_LSE128: ffi::c_ulong = 1 << 47;
#[cfg(test)]
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(target_pointer_width = "64")]
pub(crate) const HWCAP3_LSFE: ffi::c_ulong = 1 << 2;
});
#[cold]
pub(super) fn _detect(info: &mut CpuInfo) {
#[cfg(target_os = "android")]
{
let mut arch = [0_u8; ffi::PROP_VALUE_MAX as usize];
let len = unsafe {
ffi::__system_property_get(
c!("ro.arch").as_ptr(),
arch.as_mut_ptr().cast::<ffi::c_char>(),
)
};
if len > 0 && arch.starts_with(b"exynos9810") {
return;
}
}
macro_rules! check {
($x:ident, $flag:ident, $bit:ident) => {
if $x & $bit != 0 {
info.set(CpuInfoFlag::$flag);
}
};
}
let hwcap = os::getauxval(ffi::AT_HWCAP);
check!(hwcap, lse, HWCAP_ATOMICS);
check!(hwcap, lse2, HWCAP_USCAT);
#[cfg(test)]
check!(hwcap, rcpc, HWCAP_LRCPC);
#[cfg(test)]
check!(hwcap, rcpc2, HWCAP_ILRCPC);
#[cfg(test)]
check!(hwcap, cpuid, HWCAP_CPUID);
#[cfg(not(target_os = "openbsd"))]
#[cfg(target_pointer_width = "64")]
{
let hwcap2 = os::getauxval(ffi::AT_HWCAP2);
check!(hwcap2, rcpc3, HWCAP2_LRCPC3);
check!(hwcap2, lse128, HWCAP2_LSE128);
#[cfg(test)]
#[cfg(any(target_os = "linux", target_os = "android"))]
{
let hwcap3 = os::getauxval(ffi::AT_HWCAP3);
check!(hwcap3, lsfe, HWCAP3_LSFE);
}
}
}
}
#[cfg(target_arch = "arm")]
mod arch {
use super::{CpuInfo, CpuInfoFlag, ffi, os};
sys_const!({
#[cfg(test)]
pub(crate) const HWCAP_LPAE: ffi::c_ulong = 1 << 20;
});
#[cold]
pub(crate) fn _detect(info: &mut CpuInfo) {
macro_rules! check {
($x:ident, $flag:ident, $($bit:ident) ||+) => {
if $x & ($($bit) |+) != 0 {
info.set(CpuInfoFlag::$flag);
}
};
($x:ident, $flag:ident, $($bit:ident) &&+) => {
if $x & ($($bit) |+) == ($($bit) |+) {
info.set(CpuInfoFlag::$flag);
}
};
}
let hwcap = os::getauxval(ffi::AT_HWCAP);
#[cfg(test)]
check!(hwcap, lpae, HWCAP_LPAE);
}
}
#[cfg(target_arch = "powerpc64")]
mod arch {
use super::{CpuInfo, CpuInfoFlag, ffi, os};
sys_const!({
pub(crate) const PPC_FEATURE_BOOKE: ffi::c_ulong = 0x00008000;
pub(crate) const PPC_FEATURE2_ARCH_2_07: ffi::c_ulong = 0x80000000;
pub(crate) const PPC_FEATURE2_ARCH_3_00: ffi::c_ulong = 0x00800000;
pub(crate) const PPC_FEATURE2_ARCH_3_1: ffi::c_ulong = 0x00040000;
});
#[cold]
pub(super) fn _detect(info: &mut CpuInfo) {
let hwcap = os::getauxval(ffi::AT_HWCAP);
if hwcap & PPC_FEATURE_BOOKE != 0 {
return;
}
let hwcap2 = os::getauxval(ffi::AT_HWCAP2);
let isa_2_07_or_later =
PPC_FEATURE2_ARCH_2_07 | PPC_FEATURE2_ARCH_3_00 | PPC_FEATURE2_ARCH_3_1;
if hwcap2 & isa_2_07_or_later != 0 {
info.set(CpuInfoFlag::quadword_atomics);
}
}
}
#[allow(
clippy::alloc_instead_of_core,
clippy::std_instead_of_alloc,
clippy::std_instead_of_core,
clippy::undocumented_unsafe_blocks,
clippy::wildcard_imports
)]
#[cfg(test)]
mod tests {
use std::mem;
use super::*;
#[allow(clippy::cast_sign_loss)]
#[cfg(all(target_arch = "aarch64", target_os = "android"))]
#[test]
fn test_android() {
use std::{slice, str};
unsafe {
let mut arch = [1; ffi::PROP_VALUE_MAX as usize];
let len = ffi::__system_property_get(
c!("ro.arch").as_ptr(),
arch.as_mut_ptr().cast::<ffi::c_char>(),
);
assert!(len >= 0);
test_helper::eprintln_nocapture!("ro.arch=raw={:?},len={}", arch, len);
test_helper::eprintln_nocapture!(
"ro.arch={:?}",
str::from_utf8(slice::from_raw_parts(arch.as_ptr(), len as usize)).unwrap()
);
}
}
#[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(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::<ffi::c_ulong>() 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::<ffi::c_void>(),
out_len,
),
dlsym_elf_aux_info(
at,
(&mut dlsym_out as *mut ffi::c_ulong).cast::<ffi::c_void>(),
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;
assert_eq!(
ffi::elf_aux_info(
at,
(&mut out as *mut ffi::c_ulong).cast::<ffi::c_void>(),
out_len,
),
dlsym_elf_aux_info(
at,
(&mut dlsym_out as *mut ffi::c_ulong).cast::<ffi::c_void>(),
out_len,
),
);
assert_eq!(out, dlsym_out);
}
}
}
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(not(all(target_arch = "aarch64", target_pointer_width = "32")))]
#[cfg_attr(target_arch = "arm", rustversion::nightly)] #[cfg_attr(target_arch = "powerpc64", rustversion::since(1.92))] #[test]
fn test_alternative() {
#[cfg(not(portable_atomic_no_asm))]
use std::arch::asm;
use std::{str, vec};
#[cfg(target_pointer_width = "32")]
use sys::Elf32_auxv_t as Elf_auxv_t;
#[cfg(target_pointer_width = "64")]
use sys::Elf64_auxv_t as Elf_auxv_t;
use test_helper::sys;
use crate::utils::{RegISize, RegSize, ffi::*};
fn getauxval_pr_get_auxv_no_libc(type_: c_ulong) -> Result<c_ulong, c_int> {
unsafe fn prctl_get_auxv(out: *mut c_void, len: usize) -> Result<usize, c_int> {
let number = sys::__NR_prctl as RegSize;
let arg1 = sys::PR_GET_AUXV as RegSize;
let arg2 = ptr_reg!(out);
let arg3 = len as RegSize;
let r: RegISize;
unsafe {
#[cfg(target_arch = "aarch64")]
asm!(
"svc 0",
in("x8") number,
inout("x0") arg1 => r,
in("x1") arg2,
in("x2") arg3,
in("x3") 0_u64,
in("x4") 0_u64,
options(nostack, preserves_flags),
);
#[cfg(all(target_arch = "arm", not(target_feature = "thumb-mode")))]
asm!(
"svc 0",
in("r7") number,
inout("r0") arg1 => r,
in("r1") arg2,
in("r2") arg3,
in("r3") 0_u32,
in("r4") 0_u32,
options(nostack, preserves_flags),
);
#[cfg(all(target_arch = "arm", target_feature = "thumb-mode"))]
asm!(
"mov {tmp}, r7",
"mov r7, {number}",
"svc 0",
"mov r7, {tmp}",
number = in(reg) number,
tmp = out(reg) _,
inout("r0") arg1 => r,
in("r1") arg2,
in("r2") arg3,
in("r3") 0_u32,
in("r4") 0_u32,
options(nostack, preserves_flags),
);
#[cfg(target_arch = "powerpc64")]
asm!(
"sc",
"bns+ 2f",
"neg %r3, %r3",
"2:",
inout("r0") number => _,
inout("r3") arg1 => r,
inout("r4") arg2 => _,
inout("r5") arg3 => _,
inout("r6") 0_u64 => _,
inout("r7") 0_u64 => _,
out("r8") _,
out("r9") _,
out("r10") _,
out("r11") _,
out("r12") _,
out("cr0") _,
out("ctr") _,
out("xer") _,
options(nostack, preserves_flags),
);
}
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
if (r as c_int) < 0 { Err(r as c_int) } else { Ok(r as usize) }
}
let mut auxv = vec![unsafe { mem::zeroed::<Elf_auxv_t>() }; 38];
let old_len = auxv.len() * mem::size_of::<Elf_auxv_t>();
let _len = unsafe { prctl_get_auxv(auxv.as_mut_ptr().cast::<c_void>(), old_len)? };
for aux in &auxv {
if aux.a_type == type_ {
return Ok(unsafe { aux.a_un.a_val });
}
}
Err(0)
}
fn getauxval_pr_get_auxv_libc(type_: c_ulong) -> Result<c_ulong, c_int> {
unsafe fn prctl_get_auxv(out: *mut c_void, len: usize) -> Result<usize, c_int> {
#[allow(clippy::cast_possible_wrap)]
let r = unsafe { libc::prctl(sys::PR_GET_AUXV as c_int, out, len, 0, 0) };
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
if (r as c_int) < 0 { Err(r as c_int) } else { Ok(r as usize) }
}
let mut auxv = vec![unsafe { mem::zeroed::<Elf_auxv_t>() }; 38];
let old_len = auxv.len() * mem::size_of::<Elf_auxv_t>();
let _len = unsafe { prctl_get_auxv(auxv.as_mut_ptr().cast::<c_void>(), old_len)? };
for aux in &auxv {
if aux.a_type == type_ {
return Ok(unsafe { aux.a_un.a_val });
}
}
Err(0)
}
unsafe {
let mut u = mem::zeroed();
assert_eq!(libc::uname(&mut u), 0);
let release = std::ffi::CStr::from_ptr(u.release.as_ptr());
let release = str::from_utf8(release.to_bytes()).unwrap();
let mut digits = release.split('.');
let major = digits.next().unwrap().parse::<u32>().unwrap();
let minor = digits.next().unwrap().parse::<u32>().unwrap();
if (major, minor) < (6, 4) || cfg!(qemu) {
std::eprintln!("kernel version: {}.{} (no pr_get_auxv)", major, minor);
for &at in &[ffi::AT_HWCAP, ffi::AT_HWCAP2, ffi::AT_HWCAP3, ffi::AT_HWCAP4] {
assert_eq!(getauxval_pr_get_auxv_libc(at).unwrap_err(), -1);
assert_eq!(getauxval_pr_get_auxv_no_libc(at).unwrap_err(), -libc::EINVAL);
}
} else {
std::eprintln!("kernel version: {}.{} (has pr_get_auxv)", major, minor);
for &at in &[ffi::AT_HWCAP, ffi::AT_HWCAP2] {
if cfg!(all(valgrind, target_arch = "powerpc64")) {
assert_eq!(getauxval_pr_get_auxv_libc(at).unwrap_err(), -1);
assert_eq!(getauxval_pr_get_auxv_no_libc(at).unwrap_err(), -libc::EINVAL);
} else if cfg!(all(valgrind, target_arch = "aarch64"))
|| cfg!(all(valgrind, target_arch = "arm")) && at == ffi::AT_HWCAP2
{
assert_ne!(os::getauxval(at), getauxval_pr_get_auxv_libc(at).unwrap());
assert_ne!(os::getauxval(at), getauxval_pr_get_auxv_no_libc(at).unwrap());
} else {
assert_eq!(os::getauxval(at), getauxval_pr_get_auxv_libc(at).unwrap());
assert_eq!(os::getauxval(at), getauxval_pr_get_auxv_no_libc(at).unwrap());
}
}
for &at in &[ffi::AT_HWCAP3, ffi::AT_HWCAP4] {
assert_eq!(
os::getauxval(at),
getauxval_pr_get_auxv_libc(at).unwrap_or_default()
);
assert_eq!(
os::getauxval(at),
getauxval_pr_get_auxv_no_libc(at).unwrap_or_default()
);
}
}
}
}
#[allow(clippy::cast_possible_wrap)]
#[cfg(target_os = "freebsd")]
#[test]
fn test_alternative() {
#[cfg(not(portable_atomic_no_asm))]
use std::arch::asm;
use std::ptr;
use test_helper::sys;
use crate::utils::{RegISize, RegSize, ffi::*};
fn getauxval_sysctl_libc(type_: ffi::c_int) -> Result<ffi::c_ulong, c_int> {
let mut auxv: [sys::Elf_Auxinfo; sys::AT_COUNT as usize] = unsafe { mem::zeroed() };
let mut len = mem::size_of_val(&auxv) as c_size_t;
let pid = unsafe { libc::getpid() };
let mib = [
sys::CTL_KERN as c_int,
sys::KERN_PROC as c_int,
sys::KERN_PROC_AUXV as c_int,
pid,
];
#[allow(clippy::cast_possible_truncation)]
let res = unsafe {
libc::sysctl(
mib.as_ptr(),
mib.len() as c_uint,
auxv.as_mut_ptr().cast::<c_void>(),
&mut len,
ptr::null_mut(),
0,
)
};
if res == -1 {
return Err(res);
}
for aux in &auxv {
#[allow(clippy::cast_sign_loss)]
if aux.a_type == type_ as c_long {
return Ok(unsafe { aux.a_un.a_val as c_ulong });
}
}
Err(0)
}
fn getauxval_sysctl_no_libc(type_: ffi::c_int) -> Result<ffi::c_ulong, c_int> {
#[allow(non_camel_case_types)]
type pid_t = c_int;
#[inline]
fn getpid() -> pid_t {
let n = sys::SYS_getpid as RegSize;
let r: RegISize;
unsafe {
#[cfg(target_arch = "aarch64")]
asm!(
"svc 0",
in("x8") n,
out("x0") r,
options(nostack, readonly),
);
#[cfg(target_arch = "powerpc64")]
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") _,
out("ctr") _,
out("xer") _,
options(nostack, preserves_flags, readonly),
);
}
#[allow(clippy::cast_possible_truncation)]
{
r as pid_t
}
}
#[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> {
let mut n = sys::SYS___sysctl as RegSize;
let arg1 = ptr_reg!(name);
let arg2 = name_len as RegSize;
let arg3 = ptr_reg!(old_p);
let arg4 = ptr_reg!(old_len_p);
let arg5 = ptr_reg!(new_p);
let arg6 = new_len as RegSize;
let r: RegISize;
unsafe {
#[cfg(target_arch = "aarch64")]
asm!(
"svc 0",
"b.cc 2f",
"mov x8, x0",
"mov x0, #-1",
"2:",
inout("x8") n,
inout("x0") arg1 => r,
inout("x1") arg2 => _,
in("x2") arg3,
in("x3") arg4,
in("x4") arg5,
in("x5") arg6,
options(nostack),
);
#[cfg(target_arch = "powerpc64")]
asm!(
"sc",
"bns+ 2f",
"mr %r0, %r3",
"li %r3, -1",
"2:",
inout("r0") n,
inout("r3") arg1 => r,
inout("r4") arg2 => _,
inout("r5") arg3 => _,
inout("r6") arg4 => _,
inout("r7") arg5 => _,
inout("r8") arg6 => _,
out("r9") _,
out("r10") _,
out("r11") _,
out("r12") _,
out("cr0") _,
out("ctr") _,
out("xer") _,
options(nostack, preserves_flags),
);
}
#[allow(clippy::cast_possible_truncation)]
if r as c_int == -1 { Err(n as c_int) } else { Ok(r as c_int) }
}
let mut auxv: [sys::Elf_Auxinfo; sys::AT_COUNT as usize] = unsafe { mem::zeroed() };
let mut len = mem::size_of_val(&auxv) as c_size_t;
let pid = getpid();
let mib = [
sys::CTL_KERN as c_int,
sys::KERN_PROC as c_int,
sys::KERN_PROC_AUXV as c_int,
pid,
];
#[allow(clippy::cast_possible_truncation)]
unsafe {
sysctl(
mib.as_ptr(),
mib.len() as c_uint,
auxv.as_mut_ptr().cast::<c_void>(),
&mut len,
ptr::null_mut(),
0,
)?;
}
for aux in &auxv {
#[allow(clippy::cast_sign_loss)]
if aux.a_type == type_ as c_long {
return Ok(unsafe { aux.a_un.a_val as c_ulong });
}
}
Err(0)
}
let hwcap2_else = |e| if cfg!(target_arch = "aarch64") { 0 } else { panic!("{:?}", e) };
let at = ffi::AT_HWCAP;
assert_eq!(os::getauxval(at), getauxval_sysctl_libc(at).unwrap());
assert_eq!(os::getauxval(at), getauxval_sysctl_no_libc(at).unwrap());
let at = ffi::AT_HWCAP2;
assert_eq!(os::getauxval(at), getauxval_sysctl_libc(at).unwrap_or_else(hwcap2_else));
assert_eq!(os::getauxval(at), getauxval_sysctl_no_libc(at).unwrap_or_else(hwcap2_else));
for &at in &[ffi::AT_HWCAP3, ffi::AT_HWCAP4] {
assert_eq!(os::getauxval(at), getauxval_sysctl_libc(at).unwrap_or_default());
assert_eq!(os::getauxval(at), getauxval_sysctl_no_libc(at).unwrap_or_default());
}
}
}