#![allow(dead_code)]
pub(crate) const AT_NULL: usize = 0;
pub(crate) const AT_HWCAP: usize = 16;
#[cfg(any(
target_arch = "aarch64",
target_arch = "arm",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "s390x",
))]
pub(crate) const AT_HWCAP2: usize = 26;
#[derive(Debug, Copy, Clone)]
#[cfg_attr(test, derive(PartialEq))]
pub(crate) struct AuxVec {
pub hwcap: usize,
#[cfg(any(
target_arch = "aarch64",
target_arch = "arm",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "s390x",
))]
pub hwcap2: usize,
}
pub(crate) fn auxv() -> Result<AuxVec, ()> {
if let Ok(hwcap) = getauxval(AT_HWCAP) {
#[cfg(any(
target_arch = "riscv32",
target_arch = "riscv64",
target_arch = "mips",
target_arch = "mips64",
target_arch = "loongarch64",
))]
{
if hwcap != 0 {
return Ok(AuxVec { hwcap });
}
}
#[cfg(any(
target_arch = "aarch64",
target_arch = "arm",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "s390x",
))]
{
if let Ok(hwcap2) = getauxval(AT_HWCAP2) {
if hwcap != 0 || hwcap2 != 0 {
return Ok(AuxVec { hwcap, hwcap2 });
}
}
}
let _ = hwcap;
}
#[cfg(feature = "std_detect_file_io")]
{
auxv_from_file("/proc/self/auxv")
}
#[cfg(not(feature = "std_detect_file_io"))]
{
Err(())
}
}
fn getauxval(key: usize) -> Result<usize, ()> {
type F = unsafe extern "C" fn(libc::c_ulong) -> libc::c_ulong;
cfg_if::cfg_if! {
if #[cfg(all(
feature = "std_detect_dlsym_getauxval",
not(all(
target_os = "linux",
any(target_env = "gnu", target_env = "musl", target_env = "ohos"),
)),
not(target_os = "android"),
))] {
let ffi_getauxval: F = unsafe {
let ptr = libc::dlsym(libc::RTLD_DEFAULT, c"getauxval".as_ptr());
if ptr.is_null() {
return Err(());
}
core::mem::transmute(ptr)
};
} else {
let ffi_getauxval: F = libc::getauxval;
}
}
Ok(unsafe { ffi_getauxval(key as libc::c_ulong) as usize })
}
#[cfg(feature = "std_detect_file_io")]
pub(super) fn auxv_from_file(file: &str) -> Result<AuxVec, ()> {
let file = super::read_file(file)?;
let len = file.len();
let mut buf = alloc::vec![0_usize; 1 + len / core::mem::size_of::<usize>()];
unsafe {
core::ptr::copy_nonoverlapping(file.as_ptr(), buf.as_mut_ptr() as *mut u8, len);
}
auxv_from_buf(&buf)
}
#[cfg(feature = "std_detect_file_io")]
fn auxv_from_buf(buf: &[usize]) -> Result<AuxVec, ()> {
#[cfg(any(
target_arch = "riscv32",
target_arch = "riscv64",
target_arch = "mips",
target_arch = "mips64",
target_arch = "loongarch64",
))]
{
for el in buf.chunks(2) {
match el[0] {
AT_NULL => break,
AT_HWCAP => return Ok(AuxVec { hwcap: el[1] }),
_ => (),
}
}
}
#[cfg(any(
target_arch = "aarch64",
target_arch = "arm",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "s390x",
))]
{
let mut hwcap = None;
let mut hwcap2 = 0;
for el in buf.chunks(2) {
match el[0] {
AT_NULL => break,
AT_HWCAP => hwcap = Some(el[1]),
AT_HWCAP2 => hwcap2 = el[1],
_ => (),
}
}
if let Some(hwcap) = hwcap {
return Ok(AuxVec { hwcap, hwcap2 });
}
}
let _ = buf;
Err(())
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(any(
target_arch = "arm",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "s390x",
))]
#[test]
fn auxv_crate() {
let v = auxv();
if let Ok(hwcap) = getauxval(AT_HWCAP) {
let rt_hwcap = v.expect("failed to find hwcap key").hwcap;
assert_eq!(rt_hwcap, hwcap);
}
#[cfg(any(
target_arch = "aarch64",
target_arch = "arm",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "s390x",
))]
{
if let Ok(hwcap2) = getauxval(AT_HWCAP2) {
let rt_hwcap2 = v.expect("failed to find hwcap2 key").hwcap2;
assert_eq!(rt_hwcap2, hwcap2);
}
}
}
#[test]
fn auxv_dump() {
if let Ok(auxvec) = auxv() {
println!("{:?}", auxvec);
} else {
println!("both getauxval() and reading /proc/self/auxv failed!");
}
}
#[cfg(feature = "std_detect_file_io")]
cfg_if::cfg_if! {
if #[cfg(target_arch = "arm")] {
#[test]
fn linux_rpi3() {
let file = concat!(env!("CARGO_MANIFEST_DIR"), "/src/detect/test_data/linux-rpi3.auxv");
println!("file: {file}");
let v = auxv_from_file(file).unwrap();
assert_eq!(v.hwcap, 4174038);
assert_eq!(v.hwcap2, 16);
}
#[test]
fn linux_macos_vb() {
let file = concat!(env!("CARGO_MANIFEST_DIR"), "/src/detect/test_data/macos-virtualbox-linux-x86-4850HQ.auxv");
println!("file: {file}");
let v = auxv_from_file(file).unwrap();
assert_eq!(v.hwcap, 126614527);
assert_eq!(v.hwcap2, 0);
}
} else if #[cfg(target_arch = "aarch64")] {
#[cfg(target_endian = "little")]
#[test]
fn linux_artificial_aarch64() {
let file = concat!(env!("CARGO_MANIFEST_DIR"), "/src/detect/test_data/linux-artificial-aarch64.auxv");
println!("file: {file}");
let v = auxv_from_file(file).unwrap();
assert_eq!(v.hwcap, 0x0123456789abcdef);
assert_eq!(v.hwcap2, 0x02468ace13579bdf);
}
#[cfg(target_endian = "little")]
#[test]
fn linux_no_hwcap2_aarch64() {
let file = concat!(env!("CARGO_MANIFEST_DIR"), "/src/detect/test_data/linux-no-hwcap2-aarch64.auxv");
println!("file: {file}");
let v = auxv_from_file(file).unwrap();
assert_ne!(v.hwcap, 0);
assert_eq!(v.hwcap2, 0);
}
}
}
#[test]
#[cfg(feature = "std_detect_file_io")]
fn auxv_dump_procfs() {
if let Ok(auxvec) = auxv_from_file("/proc/self/auxv") {
println!("{:?}", auxvec);
} else {
println!("reading /proc/self/auxv failed!");
}
}
#[cfg(any(
target_arch = "aarch64",
target_arch = "arm",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "s390x",
))]
#[test]
#[cfg(feature = "std_detect_file_io")]
fn auxv_crate_procfs() {
if let Ok(procfs_auxv) = auxv_from_file("/proc/self/auxv") {
assert_eq!(auxv().unwrap(), procfs_auxv);
}
}
}