Skip to content

Commit 3652dc0

Browse files
authored
On 32-bit x86, add more strategies for the syscall mechanism. (#979)
* On 32-bit x86, add more strategies for the syscall mechanism. By default, rustix uses `libc` to read aux records, to get the sysinfo information needed to locate the vsyscall function. If that's disabled, add more fallback paths: Add a way to use `PR_GET_AUXV` even when "alloc" is not available, by using a statically-sized buffer and being prepared to proceed to the next fallback if the buffer isn't big enough. And, if `PR_GET_AUXV` and /proc/self/auxv both fail, fall back to using `int 0x80`. * Disable the riscv32imc-esp-espidf test for now. * Disable armv7-sony-vita-newlibeabihf for now too.
1 parent c4ac6b6 commit 3652dc0

File tree

2 files changed

+98
-38
lines changed

2 files changed

+98
-38
lines changed

.github/workflows/main.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -213,10 +213,12 @@ jobs:
213213
# Omit --all-targets on haiku because not all the tests build yet.
214214
- run: cargo check -Z build-std --target x86_64-unknown-haiku --features=all-apis
215215
- run: cargo check -Z build-std --target x86_64-uwp-windows-msvc --all-targets --features=all-apis
216-
- run: cargo check -Z build-std --target=riscv32imc-esp-espidf --features=all-apis
216+
# Temporarily disable riscv32imc-esp-espidf due to std using SOMAXCONN.
217+
#- run: cargo check -Z build-std --target=riscv32imc-esp-espidf --features=all-apis
217218
- run: cargo check -Z build-std --target=aarch64-unknown-nto-qnx710 --features=all-apis
218219
- run: cargo check -Z build-std --target=x86_64-pc-nto-qnx710 --features=all-apis
219-
- run: cargo check -Z build-std --target=armv7-sony-vita-newlibeabihf --features=all-apis
220+
# Temporarily disable armv7-sony-vita-newlibeabihf due to std using SOMAXCONN.
221+
#- run: cargo check -Z build-std --target=armv7-sony-vita-newlibeabihf --features=all-apis
220222
# `std` doesn't appear to build on AIX yet, so test in `no_std` mode.
221223
- run: cargo check -Zbuild-std=core,alloc --target=powerpc64-ibm-aix --features=all-apis --no-default-features
222224
# Disable MIPS entirely for now as it fails with errors like

src/backend/linux_raw/param/auxv.rs

Lines changed: 94 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
//! This uses raw pointers to locate and read the kernel-provided auxv array.
66
#![allow(unsafe_code)]
77

8+
use super::super::conv::{c_int, pass_usize, ret_usize};
89
use crate::backend::c;
910
use crate::fd::OwnedFd;
1011
#[cfg(feature = "param")]
@@ -27,6 +28,8 @@ use linux_raw_sys::general::{
2728
use linux_raw_sys::general::{
2829
AT_EGID, AT_ENTRY, AT_EUID, AT_GID, AT_PHDR, AT_PHENT, AT_PHNUM, AT_RANDOM, AT_SECURE, AT_UID,
2930
};
31+
#[cfg(feature = "alloc")]
32+
use {alloc::borrow::Cow, alloc::vec};
3033

3134
#[cfg(feature = "param")]
3235
#[inline]
@@ -120,12 +123,19 @@ pub(crate) fn exe_phdrs() -> (*const c::c_void, usize, usize) {
120123

121124
/// `AT_SYSINFO_EHDR` isn't present on all platforms in all configurations, so
122125
/// if we don't see it, this function returns a null pointer.
126+
///
127+
/// And, this function returns a null pointer, rather than panicking, if the
128+
/// auxv records can't be read.
123129
#[inline]
124130
pub(in super::super) fn sysinfo_ehdr() -> *const Elf_Ehdr {
125131
let mut ehdr = SYSINFO_EHDR.load(Relaxed);
126132

127133
if ehdr.is_null() {
128-
init_auxv();
134+
// Use `maybe_init_auxv` to to read the aux vectors if it can, but do
135+
// nothing if it can't. If it can't, then we'll get a null pointer
136+
// here, which our callers are prepared to deal with.
137+
maybe_init_auxv();
138+
129139
ehdr = SYSINFO_EHDR.load(Relaxed);
130140
}
131141

@@ -177,74 +187,122 @@ static ENTRY: AtomicUsize = AtomicUsize::new(0);
177187
#[cfg(feature = "runtime")]
178188
static RANDOM: AtomicPtr<[u8; 16]> = AtomicPtr::new(null_mut());
179189

180-
#[cfg(feature = "alloc")]
181-
fn pr_get_auxv() -> crate::io::Result<Vec<u8>> {
182-
use super::super::conv::{c_int, pass_usize, ret_usize};
183-
const PR_GET_AUXV: c::c_int = 0x4155_5856;
184-
let mut buffer = alloc::vec![0u8; 512];
190+
const PR_GET_AUXV: c::c_int = 0x4155_5856;
191+
192+
/// Use Linux >= 6.4's `PR_GET_AUXV` to read the aux records, into a provided
193+
/// statically-sized buffer. Return:
194+
/// - `Ok(...)` if the buffer is big enough.
195+
/// - `Err(Ok(len))` if we need a buffer of length `len`.
196+
/// - `Err(Err(err))` if we failed with `err`.
197+
#[cold]
198+
fn pr_get_auxv_static(buffer: &mut [u8; 512]) -> Result<&mut [u8], crate::io::Result<usize>> {
185199
let len = unsafe {
186200
ret_usize(syscall_always_asm!(
187201
__NR_prctl,
188202
c_int(PR_GET_AUXV),
189-
buffer.as_ptr(),
203+
buffer.as_mut_ptr(),
190204
pass_usize(buffer.len()),
191205
pass_usize(0),
192206
pass_usize(0)
193-
))?
207+
))
208+
.map_err(Err)?
194209
};
195210
if len <= buffer.len() {
196-
buffer.truncate(len);
197-
return Ok(buffer);
211+
return Ok(&mut buffer[..len]);
198212
}
199-
buffer.resize(len, 0);
213+
Err(Ok(len))
214+
}
215+
216+
/// Use Linux >= 6.4's `PR_GET_AUXV` to read the aux records, using a provided
217+
/// statically-sized buffer if possible, or a dynamically allocated buffer
218+
/// otherwise. Return:
219+
/// - Ok(...) on success.
220+
/// - Err(err) on failure.
221+
#[cfg(feature = "alloc")]
222+
#[cold]
223+
fn pr_get_auxv_dynamic(buffer: &mut [u8; 512]) -> crate::io::Result<Cow<'_, [u8]>> {
224+
// First try use the static buffer.
225+
let len = match pr_get_auxv_static(buffer) {
226+
Ok(buffer) => return Ok(Cow::Borrowed(buffer)),
227+
Err(Ok(len)) => len,
228+
Err(Err(err)) => return Err(err),
229+
};
230+
231+
// If that indicates it needs a bigger buffer, allocate one.
232+
let mut buffer = vec![0u8; len];
200233
let len = unsafe {
201234
ret_usize(syscall_always_asm!(
202235
__NR_prctl,
203236
c_int(PR_GET_AUXV),
204-
buffer.as_ptr(),
237+
buffer.as_mut_ptr(),
205238
pass_usize(buffer.len()),
206239
pass_usize(0),
207240
pass_usize(0)
208241
))?
209242
};
210243
assert_eq!(len, buffer.len());
211-
return Ok(buffer);
244+
Ok(Cow::Owned(buffer))
245+
}
246+
247+
/// Read the auxv records and initialize the various static variables. Panic
248+
/// if an error is encountered.
249+
#[cold]
250+
fn init_auxv() {
251+
init_auxv_impl().unwrap();
252+
}
253+
254+
/// Like `init_auxv`, but don't panic if an error is encountered. The caller
255+
/// must be prepared for initialization to be skipped.
256+
#[cold]
257+
fn maybe_init_auxv() {
258+
if let Ok(()) = init_auxv_impl() {
259+
return;
260+
}
212261
}
213262

214263
/// If we don't have "use-explicitly-provided-auxv" or "use-libc-auxv", we
215264
/// read the aux vector via the `prctl` `PR_GET_AUXV`, with a fallback to
216265
/// /proc/self/auxv for kernels that don't support `PR_GET_AUXV`.
217266
#[cold]
218-
fn init_auxv() {
267+
fn init_auxv_impl() -> Result<(), ()> {
268+
let mut buffer = [0u8; 512];
269+
270+
// If we don't have "alloc", just try to read into our statically-sized
271+
// buffer. This might fail due to the buffer being insufficient; we're
272+
// prepared to cope, though we may do suboptimal things.
273+
#[cfg(not(feature = "alloc"))]
274+
let result = pr_get_auxv_static(&mut buffer);
275+
276+
// If we do have "alloc" then read into our statically-sized buffer if
277+
// it fits, or fall back to a dynamically-allocated buffer.
219278
#[cfg(feature = "alloc")]
220-
{
221-
match pr_get_auxv() {
222-
Ok(buffer) => {
223-
// SAFETY: We assume the kernel returns a valid auxv.
224-
unsafe {
225-
init_from_aux_iter(AuxPointer(buffer.as_ptr().cast())).unwrap();
226-
}
227-
return;
228-
}
229-
Err(_) => {
230-
// Fall back to /proc/self/auxv on error.
231-
}
279+
let result = pr_get_auxv_dynamic(&mut buffer);
280+
281+
if let Ok(buffer) = result {
282+
// SAFETY: We assume the kernel returns a valid auxv.
283+
unsafe {
284+
init_from_aux_iter(AuxPointer(buffer.as_ptr().cast())).unwrap();
232285
}
286+
return Ok(());
233287
}
234288

235-
// Open "/proc/self/auxv", either because we trust "/proc", or because
236-
// we're running inside QEMU and `proc_self_auxv`'s extra checking foils
237-
// QEMU's emulation so we need to do a plain open to get the right
238-
// auxv records.
239-
let file = crate::fs::open("/proc/self/auxv", OFlags::RDONLY, Mode::empty()).unwrap();
289+
// If `PR_GET_AUXV` is unavailable, or if we don't have "alloc" and
290+
// the aux records don't fit in our static buffer, then fall back to trying
291+
// to open "/proc/self/auxv". We don't use `proc_self_fd` because its extra
292+
// checking breaks on QEMU.
293+
if let Ok(file) = crate::fs::open("/proc/self/auxv", OFlags::RDONLY, Mode::empty()) {
294+
#[cfg(feature = "alloc")]
295+
init_from_auxv_file(file).unwrap();
240296

241-
#[cfg(feature = "alloc")]
242-
init_from_auxv_file(file).unwrap();
297+
#[cfg(not(feature = "alloc"))]
298+
unsafe {
299+
init_from_aux_iter(AuxFile(file)).unwrap();
300+
}
243301

244-
#[cfg(not(feature = "alloc"))]
245-
unsafe {
246-
init_from_aux_iter(AuxFile(file)).unwrap();
302+
return Ok(());
247303
}
304+
305+
Err(())
248306
}
249307

250308
/// Process auxv entries from the open file `auxv`.

0 commit comments

Comments
 (0)