Skip to content

Commit 37386af

Browse files
committed
linux_android: Use direct syscall on x86-64 to support x86_64-*-linux-none.
Remove the last libc dependency from `linux_android`, as a step towards supporting x86_64-unknown-linux-none. This requires bumping the MSRV to 1.59.
1 parent f9f7af9 commit 37386af

File tree

8 files changed

+100
-21
lines changed

8 files changed

+100
-21
lines changed

.clippy.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
msrv = "1.57"
1+
msrv = "1.59"

.github/workflows/tests.yml

+5-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ jobs:
4444
strategy:
4545
matrix:
4646
os: [ubuntu-22.04, windows-2022]
47-
toolchain: [nightly, beta, stable, 1.57]
47+
toolchain: [nightly, beta, stable, 1.59]
4848
# Only Test macOS on stable to reduce macOS CI jobs
4949
include:
5050
# x86_64-apple-darwin.
@@ -61,6 +61,8 @@ jobs:
6161
- uses: Swatinem/rust-cache@v2
6262
- run: cargo test
6363
- run: cargo test --features=std
64+
# This is assumed to be the same code path as x86_64-*-linux-none, since
65+
# we don't/can't run tests for that target yet.
6466
- run: cargo test --features=linux_disable_fallback
6567
- run: cargo test --features=custom # custom should do nothing here
6668
- if: ${{ matrix.toolchain == 'nightly' }}
@@ -361,6 +363,8 @@ jobs:
361363
features: ["rdrand"]
362364
- target: i686-unknown-hurd-gnu
363365
features: ["std"]
366+
- target: x86_64-unknown-linux-none
367+
features: ["rdrand"]
364368
steps:
365369
- uses: actions/checkout@v3
366370
- uses: dtolnay/rust-toolchain@nightly # Required to build libcore

Cargo.toml

+6-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
name = "getrandom"
33
version = "0.2.15" # Also update html_root_url in lib.rs when bumping this
44
edition = "2021"
5-
rust-version = "1.57" # Sync .clippy.toml, tests.yml, and README.md.
5+
rust-version = "1.59" # Sync .clippy.toml, tests.yml, and README.md.
66
authors = ["The Rand Project Developers"]
77
license = "MIT OR Apache-2.0"
88
description = "A small cross-platform library for retrieving random data from system source"
@@ -18,7 +18,11 @@ cfg-if = "1"
1818
compiler_builtins = { version = "0.1", optional = true }
1919
core = { version = "1.0", optional = true, package = "rustc-std-workspace-core" }
2020

21-
[target.'cfg(unix)'.dependencies]
21+
# XXX: Additionally, we don't use libc when feature `linux_disable_fallback` is
22+
# enabled on `x86_64-{*-linux-*,linux-android}`, but we can't express this. In
23+
# that case, we require libc to be built, and we force it to be linked, but we
24+
# don't actually use anything from it.
25+
[target.'cfg(all(unix, not(all(target_arch = "x86_64", target_os = "linux", target_env = ""))))'.dependencies]
2226
libc = { version = "0.2.154", default-features = false }
2327

2428
[target.'cfg(target_os = "wasi")'.dependencies]

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ crate features, WASM support and Custom RNGs see the
5252

5353
## Minimum Supported Rust Version
5454

55-
This crate requires Rust 1.57.0 or later.
55+
This crate requires Rust 1.59.0 or later.
5656

5757
## Platform Support
5858

src/lib.rs

+8-3
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,12 @@ cfg_if! {
259259
mod util_libc;
260260
#[path = "getrandom.rs"] mod imp;
261261
} else if #[cfg(all(
262-
not(feature = "linux_disable_fallback"),
262+
// Always treat feature="linux_disable_fallback" identically to
263+
// all(target_os = "linux", target_env="") to ensure the code paths are
264+
// the same. This is important because we can't run tests for
265+
// *-*-linux-none (yet). Note that target_env="" for common Android
266+
// targets.
267+
not(any(feature = "linux_disable_fallback", all(target_os = "linux", target_env=""))),
263268
any(
264269
// Rust supports Android API level 19 (KitKat) [0] and the next upgrade targets
265270
// level 21 (Lollipop) [1], while `getrandom(2)` was added only in
@@ -304,8 +309,8 @@ cfg_if! {
304309
mod linux_android;
305310
#[path = "linux_android_with_fallback.rs"] mod imp;
306311
} else if #[cfg(any(target_os = "android", target_os = "linux"))] {
307-
mod util_libc;
308-
#[path = "linux_android.rs"] mod imp;
312+
mod linux_android;
313+
use linux_android as imp;
309314
} else if #[cfg(target_os = "solaris")] {
310315
mod util_libc;
311316
#[path = "solaris.rs"] mod imp;

src/linux_android.rs

+69-12
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,75 @@ pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
66
sys_fill_exact(dest, getrandom_syscall)
77
}
88

9+
// The value of `EINTR` is not architecture-specific. It is checked against
10+
// `libc::EINTR` by linux_android_with_fallback.rs.
11+
pub const EINTR: i32 = 4;
12+
913
// Also used by linux_android_with_fallback to check if the syscall is available.
10-
pub fn getrandom_syscall(buf: &mut [MaybeUninit<u8>]) -> Result<usize, Error> {
11-
use crate::util_libc::last_os_error;
14+
cfg_if! {
15+
// TODO: Expand inilne assembly to other architectures to avoid depending
16+
// on libc on Linux.
17+
if #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] {
18+
type Word = u64;
19+
type IWord = i64;
20+
21+
// TODO(MSRV(1.78 feature(target_abi))): Enable this and remove `target_pointer_width`
22+
// restriction above.
23+
//
24+
// #[cfg(target_abi = "x32")]
25+
// const __X32_SYSCALL_BIT: Word = 0x40000000;
26+
//
27+
// #[cfg(target_abi = "x832")]
28+
// #[allow(non_upper_case_globals)]
29+
// pub const SYS_getrandom: IWord = 318 | __X32_SYSCALL_BIT;
30+
31+
// #[cfg(not(target_abi = "x832"))]
32+
#[allow(non_upper_case_globals)]
33+
pub const SYS_getrandom: IWord = 318;
34+
35+
pub fn getrandom_syscall(buf: &mut [MaybeUninit<u8>]) -> Result<usize, Error> {
36+
// Clamp request length to word size; no-op on regular (non-x32) x86_64.
37+
let buflen: Word = Word::try_from(buf.len()).unwrap_or(Word::MAX);
38+
let mut ret: IWord;
39+
let flags = 0;
40+
unsafe {
41+
core::arch::asm!(
42+
"syscall",
43+
inout("rax") SYS_getrandom => ret,
44+
in("rdi") buf.as_mut_ptr(),
45+
in("rsi") buflen,
46+
in("rdx") flags,
47+
lateout("rcx") _,
48+
lateout("r11") _,
49+
options(nostack),
50+
);
51+
}
52+
match Word::try_from(ret) {
53+
Ok(written) => {
54+
const _:() = assert!(core::mem::size_of::<Word>() <= core::mem::size_of::<usize>());
55+
Ok(written as usize)
56+
},
57+
Err(_) => {
58+
Err(u32::try_from(ret.unsigned_abs()).map_or(
59+
Error::UNEXPECTED, Error::from_os_error))
60+
}
61+
}
62+
}
63+
} else {
64+
use crate::util_libc::last_os_error;
65+
pub use libc::SYS_getrandom;
1266

13-
let ret: libc::c_long = unsafe {
14-
libc::syscall(
15-
libc::SYS_getrandom,
16-
buf.as_mut_ptr().cast::<core::ffi::c_void>(),
17-
buf.len(),
18-
0,
19-
)
20-
};
21-
const _: () = assert!(core::mem::size_of::<libc::c_long>() == core::mem::size_of::<isize>());
22-
usize::try_from(ret as isize).map_err(|_| last_os_error())
67+
pub fn getrandom_syscall(buf: &mut [MaybeUninit<u8>]) -> Result<usize, Error> {
68+
let ret: libc::c_long = unsafe {
69+
libc::syscall(
70+
SYS_getrandom,
71+
buf.as_mut_ptr().cast::<core::ffi::c_void>(),
72+
buf.len(),
73+
0,
74+
)
75+
};
76+
const _:() = assert!(core::mem::size_of::<libc::c_long>() == core::mem::size_of::<isize>());
77+
usize::try_from(ret as isize).map_err(|_| last_os_error())
78+
}
79+
}
2380
}

src/linux_android_with_fallback.rs

+3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
use crate::{lazy::LazyBool, linux_android, use_file, Error};
33
use core::mem::MaybeUninit;
44

5+
const _: () = assert!(linux_android::EINTR == libc::EINTR);
6+
const _: () = assert!(linux_android::SYS_getrandom == libc::SYS_getrandom);
7+
58
pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
69
// getrandom(2) was introduced in Linux 3.17
710
static HAS_GETRANDOM: LazyBool = LazyBool::new();

src/util_unix.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,18 @@ pub fn sys_fill_exact(
99
mut buf: &mut [MaybeUninit<u8>],
1010
sys_fill: impl Fn(&mut [MaybeUninit<u8>]) -> Result<usize, Error>,
1111
) -> Result<(), Error> {
12+
// Avoid depending on libc for Linux/Android.
13+
#[cfg(any(target_os = "android", target_os = "linux"))]
14+
use crate::linux_android::EINTR;
15+
#[cfg(not(any(target_os = "android", target_os = "linux")))]
16+
use libc::EINTR;
17+
1218
while !buf.is_empty() {
1319
match sys_fill(buf) {
1420
Ok(res) if res > 0 => buf = buf.get_mut(res..).ok_or(Error::UNEXPECTED)?,
1521
Err(err) => {
1622
// We should try again if the call was interrupted.
17-
if err.raw_os_error() != Some(libc::EINTR) {
23+
if err.raw_os_error() != Some(EINTR) {
1824
return Err(err);
1925
}
2026
}

0 commit comments

Comments
 (0)