diff --git a/library/std/src/sys/env/wasi.rs b/library/std/src/sys/env/wasi.rs index c970aac182604..6b892376fd0cf 100644 --- a/library/std/src/sys/env/wasi.rs +++ b/library/std/src/sys/env/wasi.rs @@ -1,11 +1,16 @@ use core::slice::memchr; pub use super::common::Env; -use crate::ffi::{CStr, OsStr, OsString}; +use crate::ffi::{CStr, OsStr, OsString, c_char}; use crate::io; use crate::os::wasi::prelude::*; use crate::sys::helpers::run_with_cstr; -use crate::sys::pal::os::{cvt, libc}; +use crate::sys::pal::cvt; + +// This is not available yet in libc. +unsafe extern "C" { + fn __wasilibc_get_environ() -> *mut *mut c_char; +} cfg_select! { target_feature = "atomics" => { @@ -37,7 +42,7 @@ pub fn env() -> Env { // Use `__wasilibc_get_environ` instead of `environ` here so that we // don't require wasi-libc to eagerly initialize the environment // variables. - let mut environ = libc::__wasilibc_get_environ(); + let mut environ = __wasilibc_get_environ(); let mut result = Vec::new(); if !environ.is_null() { diff --git a/library/std/src/sys/net/connection/socket/unix.rs b/library/std/src/sys/net/connection/socket/unix.rs index 5e20c0ffdfa6a..f3c860804de03 100644 --- a/library/std/src/sys/net/connection/socket/unix.rs +++ b/library/std/src/sys/net/connection/socket/unix.rs @@ -684,7 +684,7 @@ fn on_resolver_failure() { use crate::sys; // If the version fails to parse, we treat it the same as "not glibc". - if let Some(version) = sys::os::glibc_version() { + if let Some(version) = sys::pal::conf::glibc_version() { if version < (2, 26) { unsafe { libc::res_init() }; } diff --git a/library/std/src/sys/pal/solid/error.rs b/library/std/src/sys/pal/solid/error.rs index 3e85cdb3c1d9f..a737abcc04eda 100644 --- a/library/std/src/sys/pal/solid/error.rs +++ b/library/std/src/sys/pal/solid/error.rs @@ -3,6 +3,14 @@ use super::{abi, itron}; use crate::io; use crate::sys::net; +// SOLID directly maps `errno`s to μITRON error codes. +impl SolidError { + #[inline] + pub(crate) fn as_io_error(self) -> crate::io::Error { + crate::io::Error::from_raw_os_error(self.as_raw()) + } +} + /// Describe the specified SOLID error code. Returns `None` if it's an /// undefined error code. /// diff --git a/library/std/src/sys/pal/solid/os.rs b/library/std/src/sys/pal/solid/os.rs index 4a07d240d2e66..79bb91b179969 100644 --- a/library/std/src/sys/pal/solid/os.rs +++ b/library/std/src/sys/pal/solid/os.rs @@ -1,16 +1,8 @@ -use super::{itron, unsupported}; +use super::unsupported; use crate::ffi::{OsStr, OsString}; use crate::path::{self, PathBuf}; use crate::{fmt, io}; -// `solid` directly maps `errno`s to μITRON error codes. -impl itron::error::ItronError { - #[inline] - pub(crate) fn as_io_error(self) -> crate::io::Error { - crate::io::Error::from_raw_os_error(self.as_raw()) - } -} - pub fn getcwd() -> io::Result { unsupported() } diff --git a/library/std/src/sys/pal/teeos/conf.rs b/library/std/src/sys/pal/teeos/conf.rs new file mode 100644 index 0000000000000..1d13a283c458b --- /dev/null +++ b/library/std/src/sys/pal/teeos/conf.rs @@ -0,0 +1,5 @@ +// Hardcoded to return 4096, since `sysconf` is only implemented as a stub. +pub fn page_size() -> usize { + // unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize }; + 4096 +} diff --git a/library/std/src/sys/pal/teeos/mod.rs b/library/std/src/sys/pal/teeos/mod.rs index f76c26d3c966c..7d2ecdbf7ec4b 100644 --- a/library/std/src/sys/pal/teeos/mod.rs +++ b/library/std/src/sys/pal/teeos/mod.rs @@ -6,6 +6,7 @@ #![allow(unused_variables)] #![allow(dead_code)] +pub mod conf; pub mod os; #[path = "../unix/time.rs"] pub mod time; diff --git a/library/std/src/sys/pal/teeos/os.rs b/library/std/src/sys/pal/teeos/os.rs index c09b84f42bab9..c86fa555e4117 100644 --- a/library/std/src/sys/pal/teeos/os.rs +++ b/library/std/src/sys/pal/teeos/os.rs @@ -7,12 +7,6 @@ use crate::ffi::{OsStr, OsString}; use crate::path::PathBuf; use crate::{fmt, io, path}; -// Hardcoded to return 4096, since `sysconf` is only implemented as a stub. -pub fn page_size() -> usize { - // unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize }; - 4096 -} - // Everything below are stubs and copied from unsupported.rs pub fn getcwd() -> io::Result { diff --git a/library/std/src/sys/pal/unix/conf.rs b/library/std/src/sys/pal/unix/conf.rs new file mode 100644 index 0000000000000..a97173a1a35a1 --- /dev/null +++ b/library/std/src/sys/pal/unix/conf.rs @@ -0,0 +1,97 @@ +#[cfg(test)] +mod tests; + +#[cfg(not(target_os = "espidf"))] +pub fn page_size() -> usize { + unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize } +} + +/// Returns the value for [`confstr(key, ...)`][posix_confstr]. Currently only +/// used on Darwin, but should work on any unix (in case we need to get +/// `_CS_PATH` or `_CS_V[67]_ENV` in the future). +/// +/// [posix_confstr]: +/// https://pubs.opengroup.org/onlinepubs/9699919799/functions/confstr.html +// +// FIXME: Support `confstr` in Miri. +#[cfg(all(target_vendor = "apple", not(miri)))] +pub fn confstr( + key: crate::ffi::c_int, + size_hint: Option, +) -> crate::io::Result { + use crate::ffi::OsString; + use crate::io; + use crate::os::unix::ffi::OsStringExt; + + let mut buf: Vec = Vec::with_capacity(0); + let mut bytes_needed_including_nul = size_hint + .unwrap_or_else(|| { + // Treat "None" as "do an extra call to get the length". In theory + // we could move this into the loop below, but it's hard to do given + // that it isn't 100% clear if it's legal to pass 0 for `len` when + // the buffer isn't null. + unsafe { libc::confstr(key, core::ptr::null_mut(), 0) } + }) + .max(1); + // If the value returned by `confstr` is greater than the len passed into + // it, then the value was truncated, meaning we need to retry. Note that + // while `confstr` results don't seem to change for a process, it's unclear + // if this is guaranteed anywhere, so looping does seem required. + while bytes_needed_including_nul > buf.capacity() { + // We write into the spare capacity of `buf`. This lets us avoid + // changing buf's `len`, which both simplifies `reserve` computation, + // allows working with `Vec` instead of `Vec>`, and + // may avoid a copy, since the Vec knows that none of the bytes are needed + // when reallocating (well, in theory anyway). + buf.reserve(bytes_needed_including_nul); + // `confstr` returns + // - 0 in the case of errors: we break and return an error. + // - The number of bytes written, iff the provided buffer is enough to + // hold the entire value: we break and return the data in `buf`. + // - Otherwise, the number of bytes needed (including nul): we go + // through the loop again. + bytes_needed_including_nul = + unsafe { libc::confstr(key, buf.as_mut_ptr().cast(), buf.capacity()) }; + } + // `confstr` returns 0 in the case of an error. + if bytes_needed_including_nul == 0 { + return Err(io::Error::last_os_error()); + } + // Safety: `confstr(..., buf.as_mut_ptr(), buf.capacity())` returned a + // non-zero value, meaning `bytes_needed_including_nul` bytes were + // initialized. + unsafe { + buf.set_len(bytes_needed_including_nul); + // Remove the NUL-terminator. + let last_byte = buf.pop(); + // ... and smoke-check that it *was* a NUL-terminator. + assert_eq!(last_byte, Some(0), "`confstr` provided a string which wasn't nul-terminated"); + }; + Ok(OsString::from_vec(buf)) +} + +#[cfg(all(target_os = "linux", target_env = "gnu"))] +pub fn glibc_version() -> Option<(usize, usize)> { + use crate::ffi::CStr; + + unsafe extern "C" { + fn gnu_get_libc_version() -> *const libc::c_char; + } + let version_cstr = unsafe { CStr::from_ptr(gnu_get_libc_version()) }; + if let Ok(version_str) = version_cstr.to_str() { + parse_glibc_version(version_str) + } else { + None + } +} + +/// Returns Some((major, minor)) if the string is a valid "x.y" version, +/// ignoring any extra dot-separated parts. Otherwise return None. +#[cfg(all(target_os = "linux", target_env = "gnu"))] +fn parse_glibc_version(version: &str) -> Option<(usize, usize)> { + let mut parsed_ints = version.split('.').map(str::parse::).fuse(); + match (parsed_ints.next(), parsed_ints.next()) { + (Some(Ok(major)), Some(Ok(minor))) => Some((major, minor)), + _ => None, + } +} diff --git a/library/std/src/sys/pal/unix/os/tests.rs b/library/std/src/sys/pal/unix/conf/tests.rs similarity index 100% rename from library/std/src/sys/pal/unix/os/tests.rs rename to library/std/src/sys/pal/unix/conf/tests.rs diff --git a/library/std/src/sys/pal/unix/mod.rs b/library/std/src/sys/pal/unix/mod.rs index 0fbf37fda7fbf..9931b4de0b9bf 100644 --- a/library/std/src/sys/pal/unix/mod.rs +++ b/library/std/src/sys/pal/unix/mod.rs @@ -2,6 +2,7 @@ use crate::io; +pub mod conf; #[cfg(target_os = "fuchsia")] pub mod fuchsia; pub mod futex; diff --git a/library/std/src/sys/pal/unix/os.rs b/library/std/src/sys/pal/unix/os.rs index d11282682d08d..f69614c2077cd 100644 --- a/library/std/src/sys/pal/unix/os.rs +++ b/library/std/src/sys/pal/unix/os.rs @@ -2,9 +2,6 @@ #![allow(unused_imports)] // lots of cfg code here -#[cfg(test)] -mod tests; - use libc::{c_char, c_int, c_void}; use crate::ffi::{CStr, OsStr, OsString}; @@ -396,75 +393,15 @@ pub fn current_exe() -> io::Result { if !path.is_absolute() { getcwd().map(|cwd| cwd.join(path)) } else { Ok(path) } } -#[cfg(not(target_os = "espidf"))] -pub fn page_size() -> usize { - unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize } -} - -// Returns the value for [`confstr(key, ...)`][posix_confstr]. Currently only -// used on Darwin, but should work on any unix (in case we need to get -// `_CS_PATH` or `_CS_V[67]_ENV` in the future). -// -// [posix_confstr]: -// https://pubs.opengroup.org/onlinepubs/9699919799/functions/confstr.html -// -// FIXME: Support `confstr` in Miri. -#[cfg(all(target_vendor = "apple", not(miri)))] -fn confstr(key: c_int, size_hint: Option) -> io::Result { - let mut buf: Vec = Vec::with_capacity(0); - let mut bytes_needed_including_nul = size_hint - .unwrap_or_else(|| { - // Treat "None" as "do an extra call to get the length". In theory - // we could move this into the loop below, but it's hard to do given - // that it isn't 100% clear if it's legal to pass 0 for `len` when - // the buffer isn't null. - unsafe { libc::confstr(key, core::ptr::null_mut(), 0) } - }) - .max(1); - // If the value returned by `confstr` is greater than the len passed into - // it, then the value was truncated, meaning we need to retry. Note that - // while `confstr` results don't seem to change for a process, it's unclear - // if this is guaranteed anywhere, so looping does seem required. - while bytes_needed_including_nul > buf.capacity() { - // We write into the spare capacity of `buf`. This lets us avoid - // changing buf's `len`, which both simplifies `reserve` computation, - // allows working with `Vec` instead of `Vec>`, and - // may avoid a copy, since the Vec knows that none of the bytes are needed - // when reallocating (well, in theory anyway). - buf.reserve(bytes_needed_including_nul); - // `confstr` returns - // - 0 in the case of errors: we break and return an error. - // - The number of bytes written, iff the provided buffer is enough to - // hold the entire value: we break and return the data in `buf`. - // - Otherwise, the number of bytes needed (including nul): we go - // through the loop again. - bytes_needed_including_nul = - unsafe { libc::confstr(key, buf.as_mut_ptr().cast::(), buf.capacity()) }; - } - // `confstr` returns 0 in the case of an error. - if bytes_needed_including_nul == 0 { - return Err(io::Error::last_os_error()); - } - // Safety: `confstr(..., buf.as_mut_ptr(), buf.capacity())` returned a - // non-zero value, meaning `bytes_needed_including_nul` bytes were - // initialized. - unsafe { - buf.set_len(bytes_needed_including_nul); - // Remove the NUL-terminator. - let last_byte = buf.pop(); - // ... and smoke-check that it *was* a NUL-terminator. - assert_eq!(last_byte, Some(0), "`confstr` provided a string which wasn't nul-terminated"); - }; - Ok(OsString::from_vec(buf)) -} - #[cfg(all(target_vendor = "apple", not(miri)))] fn darwin_temp_dir() -> PathBuf { - confstr(libc::_CS_DARWIN_USER_TEMP_DIR, Some(64)).map(PathBuf::from).unwrap_or_else(|_| { - // It failed for whatever reason (there are several possible reasons), - // so return the global one. - PathBuf::from("/tmp") - }) + crate::sys::pal::conf::confstr(libc::_CS_DARWIN_USER_TEMP_DIR, Some(64)) + .map(PathBuf::from) + .unwrap_or_else(|_| { + // It failed for whatever reason (there are several possible reasons), + // so return the global one. + PathBuf::from("/tmp") + }) } pub fn temp_dir() -> PathBuf { @@ -532,27 +469,3 @@ pub fn home_dir() -> Option { } } } - -#[cfg(all(target_os = "linux", target_env = "gnu"))] -pub fn glibc_version() -> Option<(usize, usize)> { - unsafe extern "C" { - fn gnu_get_libc_version() -> *const libc::c_char; - } - let version_cstr = unsafe { CStr::from_ptr(gnu_get_libc_version()) }; - if let Ok(version_str) = version_cstr.to_str() { - parse_glibc_version(version_str) - } else { - None - } -} - -// Returns Some((major, minor)) if the string is a valid "x.y" version, -// ignoring any extra dot-separated parts. Otherwise return None. -#[cfg(all(target_os = "linux", target_env = "gnu"))] -fn parse_glibc_version(version: &str) -> Option<(usize, usize)> { - let mut parsed_ints = version.split('.').map(str::parse::).fuse(); - match (parsed_ints.next(), parsed_ints.next()) { - (Some(Ok(major)), Some(Ok(minor))) => Some((major, minor)), - _ => None, - } -} diff --git a/library/std/src/sys/pal/unix/stack_overflow.rs b/library/std/src/sys/pal/unix/stack_overflow.rs index 040fda75a5080..632f619655b37 100644 --- a/library/std/src/sys/pal/unix/stack_overflow.rs +++ b/library/std/src/sys/pal/unix/stack_overflow.rs @@ -70,7 +70,7 @@ mod imp { use super::thread_info::{delete_current_info, set_current_info, with_current_info}; use crate::ops::Range; use crate::sync::atomic::{Atomic, AtomicBool, AtomicPtr, AtomicUsize, Ordering}; - use crate::sys::pal::unix::os; + use crate::sys::pal::unix::conf; use crate::{io, mem, ptr}; // Signal handler for the SIGSEGV and SIGBUS handlers. We've got guard pages @@ -150,7 +150,7 @@ mod imp { /// Must be called only once #[forbid(unsafe_op_in_unsafe_fn)] pub unsafe fn init() { - PAGE_SIZE.store(os::page_size(), Ordering::Relaxed); + PAGE_SIZE.store(conf::page_size(), Ordering::Relaxed); let mut guard_page_range = unsafe { install_main_guard() }; diff --git a/library/std/src/sys/pal/wasi/conf.rs b/library/std/src/sys/pal/wasi/conf.rs new file mode 100644 index 0000000000000..cf76ae733358e --- /dev/null +++ b/library/std/src/sys/pal/wasi/conf.rs @@ -0,0 +1,4 @@ +#[allow(dead_code)] +pub fn page_size() -> usize { + unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize } +} diff --git a/library/std/src/sys/pal/wasi/helpers.rs b/library/std/src/sys/pal/wasi/helpers.rs deleted file mode 100644 index 4f2eb1148f0b3..0000000000000 --- a/library/std/src/sys/pal/wasi/helpers.rs +++ /dev/null @@ -1,11 +0,0 @@ -#![forbid(unsafe_op_in_unsafe_fn)] - -pub fn abort_internal() -> ! { - unsafe { libc::abort() } -} - -#[inline] -#[cfg(target_env = "p1")] -pub(crate) fn err2io(err: wasi::Errno) -> crate::io::Error { - crate::io::Error::from_raw_os_error(err.raw().into()) -} diff --git a/library/std/src/sys/pal/wasi/mod.rs b/library/std/src/sys/pal/wasi/mod.rs index 9b49db9af6b05..66d91078a5d54 100644 --- a/library/std/src/sys/pal/wasi/mod.rs +++ b/library/std/src/sys/pal/wasi/mod.rs @@ -4,34 +4,64 @@ //! OS level functionality for WASI. Currently this includes both WASIp1 and //! WASIp2. +use crate::io; + +pub mod conf; #[allow(unused)] #[path = "../wasm/atomics/futex.rs"] pub mod futex; - pub mod os; pub mod stack_overflow; #[path = "../unix/time.rs"] pub mod time; +#[cfg(not(target_env = "p1"))] +mod cabi_realloc; + #[path = "../unsupported/common.rs"] #[deny(unsafe_op_in_unsafe_fn)] -#[allow(unused)] +#[expect(dead_code)] mod common; +pub use common::{cleanup, init, unsupported}; -pub use common::*; +pub fn abort_internal() -> ! { + unsafe { libc::abort() } +} -mod helpers; +#[inline] +#[cfg(target_env = "p1")] +pub(crate) fn err2io(err: wasi::Errno) -> crate::io::Error { + crate::io::Error::from_raw_os_error(err.raw().into()) +} -// The following exports are listed individually to work around Rust's glob -// import conflict rules. If we glob export `helpers` and `common` together, -// then the compiler complains about conflicts. +#[doc(hidden)] +pub trait IsMinusOne { + fn is_minus_one(&self) -> bool; +} -pub(crate) use helpers::abort_internal; -#[cfg(target_env = "p1")] -pub(crate) use helpers::err2io; -#[cfg(not(target_env = "p1"))] -pub use os::IsMinusOne; -pub use os::{cvt, cvt_r}; +macro_rules! impl_is_minus_one { + ($($t:ident)*) => ($(impl IsMinusOne for $t { + fn is_minus_one(&self) -> bool { + *self == -1 + } + })*) +} -#[cfg(not(target_env = "p1"))] -mod cabi_realloc; +impl_is_minus_one! { i8 i16 i32 i64 isize } + +pub fn cvt(t: T) -> io::Result { + if t.is_minus_one() { Err(io::Error::last_os_error()) } else { Ok(t) } +} + +pub fn cvt_r(mut f: F) -> io::Result +where + T: IsMinusOne, + F: FnMut() -> T, +{ + loop { + match cvt(f()) { + Err(ref e) if e.is_interrupted() => {} + other => return other, + } + } +} diff --git a/library/std/src/sys/pal/wasi/os.rs b/library/std/src/sys/pal/wasi/os.rs index c8f3ddf692bcc..b1c26acc68cb9 100644 --- a/library/std/src/sys/pal/wasi/os.rs +++ b/library/std/src/sys/pal/wasi/os.rs @@ -8,17 +8,6 @@ use crate::sys::helpers::run_path_with_cstr; use crate::sys::unsupported; use crate::{fmt, io}; -// Add a few symbols not in upstream `libc` just yet. -pub mod libc { - pub use libc::*; - - unsafe extern "C" { - pub fn getcwd(buf: *mut c_char, size: size_t) -> *mut c_char; - pub fn chdir(dir: *const c_char) -> c_int; - pub fn __wasilibc_get_environ() -> *mut *mut c_char; - } -} - pub fn getcwd() -> io::Result { let mut buf = Vec::with_capacity(512); loop { @@ -89,11 +78,6 @@ pub fn current_exe() -> io::Result { unsupported() } -#[allow(dead_code)] -pub fn page_size() -> usize { - unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize } -} - pub fn temp_dir() -> PathBuf { panic!("no filesystem on wasm") } @@ -101,35 +85,3 @@ pub fn temp_dir() -> PathBuf { pub fn home_dir() -> Option { None } - -#[doc(hidden)] -pub trait IsMinusOne { - fn is_minus_one(&self) -> bool; -} - -macro_rules! impl_is_minus_one { - ($($t:ident)*) => ($(impl IsMinusOne for $t { - fn is_minus_one(&self) -> bool { - *self == -1 - } - })*) -} - -impl_is_minus_one! { i8 i16 i32 i64 isize } - -pub fn cvt(t: T) -> io::Result { - if t.is_minus_one() { Err(io::Error::last_os_error()) } else { Ok(t) } -} - -pub fn cvt_r(mut f: F) -> io::Result -where - T: IsMinusOne, - F: FnMut() -> T, -{ - loop { - match cvt(f()) { - Err(ref e) if e.is_interrupted() => {} - other => return other, - } - } -} diff --git a/library/std/src/sys/process/unix/unix.rs b/library/std/src/sys/process/unix/unix.rs index 82ff94fb1e030..a68be2543bc23 100644 --- a/library/std/src/sys/process/unix/unix.rs +++ b/library/std/src/sys/process/unix/unix.rs @@ -537,7 +537,7 @@ impl Command { // Only glibc 2.24+ posix_spawn() supports returning ENOENT directly. #[cfg(all(target_os = "linux", target_env = "gnu"))] { - if let Some(version) = sys::os::glibc_version() { + if let Some(version) = sys::pal::conf::glibc_version() { if version < (2, 24) { return Ok(None); } diff --git a/library/std/src/sys/thread/teeos.rs b/library/std/src/sys/thread/teeos.rs index 5e71f757eaa4b..aa679e780c18c 100644 --- a/library/std/src/sys/thread/teeos.rs +++ b/library/std/src/sys/thread/teeos.rs @@ -1,5 +1,5 @@ use crate::mem::{self, ManuallyDrop}; -use crate::sys::os; +use crate::sys::pal::conf; use crate::thread::ThreadInit; use crate::time::Duration; use crate::{cmp, io, ptr}; @@ -52,7 +52,7 @@ impl Thread { // multiple of the system page size. Because it's definitely // >= PTHREAD_STACK_MIN, it must be an alignment issue. // Round up to the nearest page and try again. - let page_size = os::page_size(); + let page_size = conf::page_size(); let stack_size = (stack_size + page_size - 1) & (-(page_size as isize - 1) as usize - 1); assert_eq!(unsafe { libc::pthread_attr_setstacksize(&mut attr, stack_size) }, 0); diff --git a/library/std/src/sys/thread/unix.rs b/library/std/src/sys/thread/unix.rs index 22f9bfef5a383..5d4eabc226ed7 100644 --- a/library/std/src/sys/thread/unix.rs +++ b/library/std/src/sys/thread/unix.rs @@ -76,7 +76,7 @@ impl Thread { // multiple of the system page size. Because it's definitely // >= PTHREAD_STACK_MIN, it must be an alignment issue. // Round up to the nearest page and try again. - let page_size = sys::os::page_size(); + let page_size = sys::pal::conf::page_size(); let stack_size = (stack_size + page_size - 1) & (-(page_size as isize - 1) as usize - 1);