Rollup merge of #147024 - brad0:std_detect_openbsd_elf_aux_info, r=Mark-Simulacrum

std_detect: Support run-time detection on OpenBSD using elf_aux_info
This commit is contained in:
Matthias Krüger 2025-11-08 15:42:20 +01:00 committed by GitHub
commit da171df13c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 107 additions and 5 deletions

View file

@ -66,10 +66,12 @@ crate from working on applications in which `std` is not available.
* FreeBSD:
* `arm32`, `powerpc64`: `std_detect` supports these on FreeBSD by querying ELF
auxiliary vectors using `sysctl`.
auxiliary vectors using `elf_aux_info`.
* `arm64`: run-time feature detection is implemented by directly querying `mrs`.
* OpenBSD:
* `powerpc64`: `std_detect` supports these on OpenBSD by querying ELF auxiliary
vectors using `elf_aux_info`.
* `arm64`: run-time feature detection is implemented by querying `sysctl`.
* Windows:

View file

@ -61,11 +61,12 @@ cfg_select! {
#[path = "os/freebsd/mod.rs"]
mod os;
}
all(target_os = "openbsd", target_arch = "aarch64", feature = "libc") => {
all(target_os = "openbsd", feature = "libc") => {
#[allow(dead_code)] // we don't use code that calls the mrs instruction.
#[cfg(target_arch = "aarch64")]
#[path = "os/aarch64.rs"]
mod aarch64;
#[path = "os/openbsd/aarch64.rs"]
#[path = "os/openbsd/mod.rs"]
mod os;
}
all(target_os = "windows", any(target_arch = "aarch64", target_arch = "arm64ec")) => {

View file

@ -0,0 +1,54 @@
//! Parses ELF auxiliary vectors.
#![cfg_attr(
any(target_arch = "aarch64", target_arch = "powerpc64", target_arch = "riscv64"),
allow(dead_code)
)]
/// Cache HWCAP bitfields of the ELF Auxiliary Vector.
///
/// If an entry cannot be read all the bits in the bitfield are set to zero.
/// This should be interpreted as all the features being disabled.
#[derive(Debug, Copy, Clone)]
pub(crate) struct AuxVec {
pub hwcap: usize,
pub hwcap2: usize,
}
/// ELF Auxiliary Vector
///
/// The auxiliary vector is a memory region in a running ELF program's stack
/// composed of (key: usize, value: usize) pairs.
///
/// The keys used in the aux vector are platform dependent. For OpenBSD, they are
/// defined in [machine/elf.h][elfh]. The hardware capabilities of a given CPU
/// can be queried with the `AT_HWCAP` and `AT_HWCAP2` keys.
///
/// Note that run-time feature detection is not invoked for features that can
/// be detected at compile-time.
///
/// [elf.h]: https://github.com/openbsd/src/blob/master/sys/arch/arm64/include/elf.h
/// [elf.h]: https://github.com/openbsd/src/blob/master/sys/arch/powerpc64/include/elf.h
pub(crate) fn auxv() -> Result<AuxVec, ()> {
let hwcap = archauxv(libc::AT_HWCAP);
let hwcap2 = archauxv(libc::AT_HWCAP2);
// Zero could indicate that no features were detected, but it's also used to
// indicate an error. In particular, on many platforms AT_HWCAP2 will be
// legitimately zero, since it contains the most recent feature flags.
if hwcap != 0 || hwcap2 != 0 {
return Ok(AuxVec { hwcap, hwcap2 });
}
Err(())
}
/// Tries to read the `key` from the auxiliary vector.
fn archauxv(key: libc::c_int) -> usize {
const OUT_LEN: libc::c_int = core::mem::size_of::<libc::c_ulong>() as libc::c_int;
let mut out: libc::c_ulong = 0;
unsafe {
let res =
libc::elf_aux_info(key, &mut out as *mut libc::c_ulong as *mut libc::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 as usize
}

View file

@ -0,0 +1,21 @@
//! Run-time feature detection on OpenBSD
mod auxvec;
cfg_select! {
target_arch = "aarch64" => {
mod aarch64;
pub(crate) use self::aarch64::detect_features;
}
target_arch = "powerpc64" => {
mod powerpc;
pub(crate) use self::powerpc::detect_features;
}
_ => {
use crate::detect::cache;
/// Performs run-time feature detection.
pub(crate) fn detect_features() -> cache::Initializer {
cache::Initializer::default()
}
}
}

View file

@ -0,0 +1,21 @@
//! Run-time feature detection for PowerPC on OpenBSD.
use super::auxvec;
use crate::detect::{Feature, cache};
pub(crate) fn detect_features() -> cache::Initializer {
let mut value = cache::Initializer::default();
let enable_feature = |value: &mut cache::Initializer, f, enable| {
if enable {
value.set(f as u32);
}
};
if let Ok(auxv) = auxvec::auxv() {
enable_feature(&mut value, Feature::altivec, auxv.hwcap & 0x10000000 != 0);
enable_feature(&mut value, Feature::vsx, auxv.hwcap & 0x00000080 != 0);
enable_feature(&mut value, Feature::power8, auxv.hwcap2 & 0x80000000 != 0);
return value;
}
value
}

View file

@ -319,8 +319,11 @@ fn powerpc_linux() {
}
#[test]
#[cfg(all(target_arch = "powerpc64", any(target_os = "linux", target_os = "freebsd"),))]
fn powerpc64_linux_or_freebsd() {
#[cfg(all(
target_arch = "powerpc64",
any(target_os = "linux", target_os = "freebsd", target_os = "openbsd"),
))]
fn powerpc64_linux_or_bsd() {
println!("altivec: {}", is_powerpc64_feature_detected!("altivec"));
println!("vsx: {}", is_powerpc64_feature_detected!("vsx"));
println!("power8: {}", is_powerpc64_feature_detected!("power8"));