]> git.proxmox.com Git - rustc.git/blame - src/stdsimd/stdsimd/arch/detect/os/linux/auxvec.rs
New upstream version 1.33.0+dfsg1
[rustc.git] / src / stdsimd / stdsimd / arch / detect / os / linux / auxvec.rs
CommitLineData
83c7162d
XL
1//! Parses ELF auxiliary vectors.
2#![cfg_attr(not(target_arch = "aarch64"), allow(dead_code))]
3
4use mem;
5use prelude::v1::*;
6use fs::File;
7use io::Read;
8
9/// Key to access the CPU Hardware capabilities bitfield.
8faf50e0 10pub(crate) const AT_HWCAP: usize = 16;
83c7162d
XL
11/// Key to access the CPU Hardware capabilities 2 bitfield.
12#[cfg(any(target_arch = "arm", target_arch = "powerpc64"))]
8faf50e0 13pub(crate) const AT_HWCAP2: usize = 26;
83c7162d
XL
14
15/// Cache HWCAP bitfields of the ELF Auxiliary Vector.
16///
17/// If an entry cannot be read all the bits in the bitfield are set to zero.
18/// This should be interpreted as all the features being disabled.
19#[derive(Debug, Copy, Clone)]
8faf50e0 20pub(crate) struct AuxVec {
83c7162d
XL
21 pub hwcap: usize,
22 #[cfg(any(target_arch = "arm", target_arch = "powerpc64"))]
23 pub hwcap2: usize,
24}
25
26/// ELF Auxiliary Vector
27///
28/// The auxiliary vector is a memory region in a running ELF program's stack
29/// composed of (key: usize, value: usize) pairs.
30///
31/// The keys used in the aux vector are platform dependent. For Linux, they are
32/// defined in [linux/auxvec.h][auxvec_h]. The hardware capabilities of a given
33/// CPU can be queried with the `AT_HWCAP` and `AT_HWCAP2` keys.
34///
35/// There is no perfect way of reading the auxiliary vector.
36///
37/// - If the `getauxval` is dynamically linked to this binary, it will be used.
38/// - Otherwise, try to read `/proc/self/auxv`.
39/// - If that fails, this function returns an error.
40///
41/// Note that run-time feature detection is not invoked for features that can
42/// be detected at compile-time. Also note that if this function returns an
43/// error, cpuinfo still can (and will) be used to try to perform run-time
44/// feature detecton on some platforms.
45///
46/// For more information about when `getauxval` is available check the great
47/// [`auxv` crate documentation][auxv_docs].
48///
49/// [auxvec_h]: https://github.com/torvalds/linux/blob/master/include/uapi/linux/auxvec.h
50/// [auxv_docs]: https://docs.rs/auxv/0.3.3/auxv/
8faf50e0 51pub(crate) fn auxv() -> Result<AuxVec, ()> {
83c7162d
XL
52 // Try to call a dynamically-linked getauxval function.
53 if let Ok(hwcap) = getauxval(AT_HWCAP) {
54 // Targets with only AT_HWCAP:
55 #[cfg(any(target_arch = "aarch64", target_arch = "mips",
56 target_arch = "mips64"))]
57 {
58 if hwcap != 0 {
59 return Ok(AuxVec { hwcap });
60 }
61 }
62
63 // Targets with AT_HWCAP and AT_HWCAP2:
64 #[cfg(any(target_arch = "arm", target_arch = "powerpc64"))]
65 {
66 if let Ok(hwcap2) = getauxval(AT_HWCAP2) {
67 if hwcap != 0 && hwcap2 != 0 {
68 return Ok(AuxVec { hwcap, hwcap2 });
69 }
70 }
71 }
72 drop(hwcap);
73 }
74 // If calling getauxval fails, try to read the auxiliary vector from
75 // its file:
76 auxv_from_file("/proc/self/auxv")
77}
78
79/// Tries to read the `key` from the auxiliary vector by calling the
80/// dynamically-linked `getauxval` function. If the function is not linked,
81/// this function return `Err`.
82fn getauxval(key: usize) -> Result<usize, ()> {
83 use libc;
84 pub type F = unsafe extern "C" fn(usize) -> usize;
85 unsafe {
86 let ptr = libc::dlsym(
87 libc::RTLD_DEFAULT,
88 "getauxval\0".as_ptr() as *const _,
89 );
90 if ptr.is_null() {
91 return Err(());
92 }
93
94 let ffi_getauxval: F = mem::transmute(ptr);
95 Ok(ffi_getauxval(key))
96 }
97}
98
99/// Tries to read the auxiliary vector from the `file`. If this fails, this
100/// function returns `Err`.
101fn auxv_from_file(file: &str) -> Result<AuxVec, ()> {
102 let mut file = File::open(file).map_err(|_| ())?;
103
104 // See https://github.com/torvalds/linux/blob/v3.19/include/uapi/linux/auxvec.h
105 //
106 // The auxiliary vector contains at most 32 (key,value) fields: from
107 // `AT_EXECFN = 31` to `AT_NULL = 0`. That is, a buffer of
108 // 2*32 `usize` elements is enough to read the whole vector.
109 let mut buf = [0_usize; 64];
110 {
111 let raw: &mut [u8; 64 * mem::size_of::<usize>()] =
112 unsafe { mem::transmute(&mut buf) };
113 file.read(raw).map_err(|_| ())?;
114 }
115 auxv_from_buf(&buf)
116}
117
118/// Tries to interpret the `buffer` as an auxiliary vector. If that fails, this
119/// function returns `Err`.
120fn auxv_from_buf(buf: &[usize; 64]) -> Result<AuxVec, ()> {
121 // Targets with only AT_HWCAP:
122 #[cfg(any(target_arch = "aarch64", target_arch = "mips",
123 target_arch = "mips64"))]
124 {
125 for el in buf.chunks(2) {
126 match el[0] {
127 AT_HWCAP => return Ok(AuxVec { hwcap: el[1] }),
128 _ => (),
129 }
130 }
131 }
132 // Targets with AT_HWCAP and AT_HWCAP2:
133 #[cfg(any(target_arch = "arm", target_arch = "powerpc64"))]
134 {
135 let mut hwcap = None;
136 let mut hwcap2 = None;
137 for el in buf.chunks(2) {
138 match el[0] {
139 AT_HWCAP => hwcap = Some(el[1]),
140 AT_HWCAP2 => hwcap2 = Some(el[1]),
141 _ => (),
142 }
143 }
144
145 if let (Some(hwcap), Some(hwcap2)) = (hwcap, hwcap2) {
146 return Ok(AuxVec { hwcap, hwcap2 });
147 }
148 }
149 drop(buf);
150 Err(())
151}
152
153#[cfg(test)]
154mod tests {
155 extern crate auxv as auxv_crate;
156 use super::*;
157
158 // Reads the Auxiliary Vector key from /proc/self/auxv
159 // using the auxv crate.
160 fn auxv_crate_getprocfs(key: usize) -> Option<usize> {
161 use self::auxv_crate::AuxvType;
162 use self::auxv_crate::procfs::search_procfs_auxv;
163 let k = key as AuxvType;
164 match search_procfs_auxv(&[k]) {
165 Ok(v) => Some(v[&k] as usize),
166 Err(_) => None,
167 }
168 }
169
170 // Reads the Auxiliary Vector key from getauxval()
171 // using the auxv crate.
172 #[cfg(not(any(target_arch = "mips", target_arch = "mips64")))]
173 fn auxv_crate_getauxval(key: usize) -> Option<usize> {
174 use self::auxv_crate::AuxvType;
175 use self::auxv_crate::getauxval::Getauxval;
176 let q = auxv_crate::getauxval::NativeGetauxval {};
177 match q.getauxval(key as AuxvType) {
178 Ok(v) => Some(v as usize),
179 Err(_) => None,
180 }
181 }
182
183 // FIXME: on mips/mips64 getauxval returns 0, and /proc/self/auxv
184 // does not always contain the AT_HWCAP key under qemu.
185 #[cfg(not(any(target_arch = "mips", target_arch = "mips64", target_arch = "powerpc")))]
186 #[test]
187 fn auxv_crate() {
188 let v = auxv();
189 if let Some(hwcap) = auxv_crate_getauxval(AT_HWCAP) {
190 let rt_hwcap = v.expect("failed to find hwcap key").hwcap;
191 assert_eq!(rt_hwcap, hwcap);
192 }
193
194 // Targets with AT_HWCAP and AT_HWCAP2:
195 #[cfg(any(target_arch = "arm", target_arch = "powerpc64"))]
196 {
197 if let Some(hwcap2) = auxv_crate_getauxval(AT_HWCAP2) {
198 let rt_hwcap2 = v.expect("failed to find hwcap2 key").hwcap2;
199 assert_eq!(rt_hwcap2, hwcap2);
200 }
201 }
202 }
203
204 #[test]
205 fn auxv_dump() {
206 if let Ok(auxvec) = auxv() {
207 println!("{:?}", auxvec);
208 } else {
209 println!("both getauxval() and reading /proc/self/auxv failed!");
210 }
211 }
212
213 cfg_if! {
214 if #[cfg(target_arch = "arm")] {
215 #[test]
216 fn linux_rpi3() {
217 let v = auxv_from_file(
218 "../../stdsimd/arch/detect/test_data/linux-rpi3.auxv",
219 ).unwrap();
220 assert_eq!(v.hwcap, 4174038);
221 assert_eq!(v.hwcap2, 16);
222 }
223
224 #[test]
225 #[should_panic]
226 fn linux_macos_vb() {
227 let _ = auxv_from_file(
228 "../../stdsimd/arch/detect/test_data/macos-virtualbox-linux-x86-4850HQ.auxv"
229 ).unwrap();
230 // this file is incomplete (contains hwcap but not hwcap2), we
231 // want to fall back to /proc/cpuinfo in this case, so
232 // reading should fail. assert_eq!(v.hwcap, 126614527);
233 // assert_eq!(v.hwcap2, 0);
234 }
235 } else if #[cfg(target_arch = "aarch64")] {
236 #[test]
237 fn linux_x64() {
238 let v = auxv_from_file(
239 "../../stdsimd/arch/detect/test_data/linux-x64-i7-6850k.auxv",
240 ).unwrap();
241 assert_eq!(v.hwcap, 3219913727);
242 }
243 }
244 }
245
246 #[test]
247 fn auxv_dump_procfs() {
248 if let Ok(auxvec) = auxv_from_file("/proc/self/auxv") {
249 println!("{:?}", auxvec);
250 } else {
251 println!("reading /proc/self/auxv failed!");
252 }
253 }
254
255 #[test]
256 fn auxv_crate_procfs() {
257 let v = auxv();
258 if let Some(hwcap) = auxv_crate_getprocfs(AT_HWCAP) {
259 assert_eq!(v.unwrap().hwcap, hwcap);
260 }
261
262 // Targets with AT_HWCAP and AT_HWCAP2:
263 #[cfg(any(target_arch = "arm", target_arch = "powerpc64"))]
264 {
265 if let Some(hwcap2) = auxv_crate_getprocfs(AT_HWCAP2) {
266 assert_eq!(v.unwrap().hwcap2, hwcap2);
267 }
268 }
269 }
270}