From c23a457e72abe608715ac76f076f47dc42af07a5 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 30 May 2024 20:31:44 +0200 Subject: Merging upstream version 1.74.1+dfsg1. Signed-off-by: Daniel Baumann --- library/std/src/sys/common/small_c_string.rs | 2 +- library/std/src/sys/common/tests.rs | 4 +- library/std/src/sys/common/thread_local/mod.rs | 2 +- library/std/src/sys/hermit/mod.rs | 8 +- library/std/src/sys/hermit/net.rs | 10 +- library/std/src/sys/itron/error.rs | 5 + library/std/src/sys/mod.rs | 9 + library/std/src/sys/sgx/mod.rs | 6 + library/std/src/sys/solid/mod.rs | 5 + library/std/src/sys/solid/net.rs | 5 + library/std/src/sys/solid/os.rs | 2 +- library/std/src/sys/uefi/alloc.rs | 33 +++ library/std/src/sys/uefi/env.rs | 9 + library/std/src/sys/uefi/helpers.rs | 141 ++++++++++++ library/std/src/sys/uefi/mod.rs | 244 +++++++++++++++++++++ library/std/src/sys/uefi/os.rs | 237 ++++++++++++++++++++ library/std/src/sys/uefi/path.rs | 25 +++ library/std/src/sys/uefi/tests.rs | 21 ++ library/std/src/sys/unix/alloc.rs | 6 +- library/std/src/sys/unix/args.rs | 1 + library/std/src/sys/unix/env.rs | 11 + library/std/src/sys/unix/fd.rs | 18 +- library/std/src/sys/unix/fs.rs | 62 ++++-- library/std/src/sys/unix/kernel_copy.rs | 4 +- library/std/src/sys/unix/mod.rs | 11 +- library/std/src/sys/unix/net.rs | 9 +- library/std/src/sys/unix/os.rs | 15 +- library/std/src/sys/unix/os_str.rs | 10 +- library/std/src/sys/unix/os_str/tests.rs | 4 +- library/std/src/sys/unix/path.rs | 2 +- library/std/src/sys/unix/pipe.rs | 9 +- library/std/src/sys/unix/process/process_common.rs | 51 ++++- library/std/src/sys/unix/process/process_unix.rs | 15 +- library/std/src/sys/unix/stack_overflow.rs | 2 + library/std/src/sys/unix/thread.rs | 37 +++- library/std/src/sys/unix/thread_local_dtor.rs | 4 +- library/std/src/sys/unix/thread_parking/darwin.rs | 3 +- library/std/src/sys/unix/time.rs | 2 +- library/std/src/sys/unsupported/common.rs | 4 + library/std/src/sys/unsupported/process.rs | 20 ++ library/std/src/sys/wasi/mod.rs | 5 + library/std/src/sys/windows/args.rs | 4 +- library/std/src/sys/windows/c.rs | 6 +- library/std/src/sys/windows/c/windows_sys.lst | 21 +- library/std/src/sys/windows/c/windows_sys.rs | 201 ++++++++++------- library/std/src/sys/windows/handle.rs | 9 +- library/std/src/sys/windows/mod.rs | 5 + library/std/src/sys/windows/net.rs | 38 ++-- library/std/src/sys/windows/os_str.rs | 8 +- library/std/src/sys/windows/path.rs | 28 +-- library/std/src/sys/windows/pipe.rs | 8 +- library/std/src/sys/windows/process.rs | 151 +++++++++++-- library/std/src/sys/xous/alloc.rs | 62 ++++++ library/std/src/sys/xous/locks/condvar.rs | 111 ++++++++++ library/std/src/sys/xous/locks/mod.rs | 7 + library/std/src/sys/xous/locks/mutex.rs | 116 ++++++++++ library/std/src/sys/xous/locks/rwlock.rs | 72 ++++++ library/std/src/sys/xous/mod.rs | 37 ++++ library/std/src/sys/xous/os.rs | 147 +++++++++++++ library/std/src/sys/xous/stdio.rs | 131 +++++++++++ library/std/src/sys/xous/thread.rs | 144 ++++++++++++ library/std/src/sys/xous/thread_local_key.rs | 190 ++++++++++++++++ library/std/src/sys/xous/time.rs | 57 +++++ 63 files changed, 2402 insertions(+), 224 deletions(-) create mode 100644 library/std/src/sys/uefi/alloc.rs create mode 100644 library/std/src/sys/uefi/env.rs create mode 100644 library/std/src/sys/uefi/helpers.rs create mode 100644 library/std/src/sys/uefi/mod.rs create mode 100644 library/std/src/sys/uefi/os.rs create mode 100644 library/std/src/sys/uefi/path.rs create mode 100644 library/std/src/sys/uefi/tests.rs create mode 100644 library/std/src/sys/xous/alloc.rs create mode 100644 library/std/src/sys/xous/locks/condvar.rs create mode 100644 library/std/src/sys/xous/locks/mod.rs create mode 100644 library/std/src/sys/xous/locks/mutex.rs create mode 100644 library/std/src/sys/xous/locks/rwlock.rs create mode 100644 library/std/src/sys/xous/mod.rs create mode 100644 library/std/src/sys/xous/os.rs create mode 100644 library/std/src/sys/xous/stdio.rs create mode 100644 library/std/src/sys/xous/thread.rs create mode 100644 library/std/src/sys/xous/thread_local_key.rs create mode 100644 library/std/src/sys/xous/time.rs (limited to 'library/std/src/sys') diff --git a/library/std/src/sys/common/small_c_string.rs b/library/std/src/sys/common/small_c_string.rs index 963d17a47..af9b18e37 100644 --- a/library/std/src/sys/common/small_c_string.rs +++ b/library/std/src/sys/common/small_c_string.rs @@ -19,7 +19,7 @@ pub fn run_path_with_cstr(path: &Path, f: F) -> io::Result where F: FnOnce(&CStr) -> io::Result, { - run_with_cstr(path.as_os_str().as_os_str_bytes(), f) + run_with_cstr(path.as_os_str().as_encoded_bytes(), f) } #[inline] diff --git a/library/std/src/sys/common/tests.rs b/library/std/src/sys/common/tests.rs index 0a1cbcbe8..32dc18ee1 100644 --- a/library/std/src/sys/common/tests.rs +++ b/library/std/src/sys/common/tests.rs @@ -8,7 +8,7 @@ use core::iter::repeat; fn stack_allocation_works() { let path = Path::new("abc"); let result = run_path_with_cstr(path, |p| { - assert_eq!(p, &*CString::new(path.as_os_str().as_os_str_bytes()).unwrap()); + assert_eq!(p, &*CString::new(path.as_os_str().as_encoded_bytes()).unwrap()); Ok(42) }); assert_eq!(result.unwrap(), 42); @@ -25,7 +25,7 @@ fn heap_allocation_works() { let path = repeat("a").take(384).collect::(); let path = Path::new(&path); let result = run_path_with_cstr(path, |p| { - assert_eq!(p, &*CString::new(path.as_os_str().as_os_str_bytes()).unwrap()); + assert_eq!(p, &*CString::new(path.as_os_str().as_encoded_bytes()).unwrap()); Ok(42) }); assert_eq!(result.unwrap(), 42); diff --git a/library/std/src/sys/common/thread_local/mod.rs b/library/std/src/sys/common/thread_local/mod.rs index 975509bd4..8b2c839f8 100644 --- a/library/std/src/sys/common/thread_local/mod.rs +++ b/library/std/src/sys/common/thread_local/mod.rs @@ -6,7 +6,7 @@ // "static" is for single-threaded platforms where a global static is sufficient. cfg_if::cfg_if! { - if #[cfg(all(target_family = "wasm", not(target_feature = "atomics")))] { + if #[cfg(any(all(target_family = "wasm", not(target_feature = "atomics")), target_os = "uefi"))] { #[doc(hidden)] mod static_local; #[doc(hidden)] diff --git a/library/std/src/sys/hermit/mod.rs b/library/std/src/sys/hermit/mod.rs index c7cb84667..abd7eb353 100644 --- a/library/std/src/sys/hermit/mod.rs +++ b/library/std/src/sys/hermit/mod.rs @@ -101,7 +101,6 @@ pub extern "C" fn __rust_abort() { // SAFETY: must be called only once during runtime initialization. // NOTE: this is not guaranteed to run, for example when Rust code is called externally. pub unsafe fn init(argc: isize, argv: *const *const u8, _sigpipe: u8) { - let _ = net::init(); args::init(argc, argv); } @@ -130,6 +129,11 @@ pub unsafe extern "C" fn runtime_entry( abi::exit(result); } +#[inline] +pub(crate) fn is_interrupted(errno: i32) -> bool { + errno == abi::errno::EINTR +} + pub fn decode_error_kind(errno: i32) -> ErrorKind { match errno { abi::errno::EACCES => ErrorKind::PermissionDenied, @@ -196,7 +200,7 @@ where { loop { match cvt(f()) { - Err(ref e) if e.kind() == ErrorKind::Interrupted => {} + Err(ref e) if e.is_interrupted() => {} other => return other, } } diff --git a/library/std/src/sys/hermit/net.rs b/library/std/src/sys/hermit/net.rs index 8c2d489d6..a564f1698 100644 --- a/library/std/src/sys/hermit/net.rs +++ b/library/std/src/sys/hermit/net.rs @@ -33,13 +33,7 @@ pub fn cvt_gai(err: i32) -> io::Result<()> { )) } -/// Checks whether the HermitCore's socket interface has been started already, and -/// if not, starts it. -pub fn init() { - if unsafe { netc::network_init() } < 0 { - panic!("Unable to initialize network interface"); - } -} +pub fn init() {} #[derive(Debug)] pub struct Socket(FileDesc); @@ -108,7 +102,7 @@ impl Socket { match unsafe { netc::poll(&mut pollfd, 1, timeout) } { -1 => { let err = io::Error::last_os_error(); - if err.kind() != io::ErrorKind::Interrupted { + if !err.is_interrupted() { return Err(err); } } diff --git a/library/std/src/sys/itron/error.rs b/library/std/src/sys/itron/error.rs index 830c60d32..fbc822d4e 100644 --- a/library/std/src/sys/itron/error.rs +++ b/library/std/src/sys/itron/error.rs @@ -79,6 +79,11 @@ pub fn error_name(er: abi::ER) -> Option<&'static str> { } } +#[inline] +pub fn is_interrupted(er: abi::ER) -> bool { + er == abi::E_RLWAI +} + pub fn decode_error_kind(er: abi::ER) -> ErrorKind { match er { // Success diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs index beea3f23c..159ffe7ac 100644 --- a/library/std/src/sys/mod.rs +++ b/library/std/src/sys/mod.rs @@ -44,6 +44,12 @@ cfg_if::cfg_if! { } else if #[cfg(target_family = "wasm")] { mod wasm; pub use self::wasm::*; + } else if #[cfg(target_os = "xous")] { + mod xous; + pub use self::xous::*; + } else if #[cfg(target_os = "uefi")] { + mod uefi; + pub use self::uefi::*; } else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] { mod sgx; pub use self::sgx::*; @@ -110,3 +116,6 @@ pub fn log_wrapper f64>(n: f64, log_fn: F) -> f64 { pub fn log_wrapper f64>(n: f64, log_fn: F) -> f64 { log_fn(n) } + +#[cfg(not(target_os = "uefi"))] +pub type RawOsError = i32; diff --git a/library/std/src/sys/sgx/mod.rs b/library/std/src/sys/sgx/mod.rs index 9865a945b..09d3f7638 100644 --- a/library/std/src/sys/sgx/mod.rs +++ b/library/std/src/sys/sgx/mod.rs @@ -86,6 +86,12 @@ pub fn sgx_ineffective(v: T) -> crate::io::Result { } } +#[inline] +pub fn is_interrupted(code: i32) -> bool { + use fortanix_sgx_abi::Error; + code == Error::Interrupted as _ +} + pub fn decode_error_kind(code: i32) -> ErrorKind { use fortanix_sgx_abi::Error; diff --git a/library/std/src/sys/solid/mod.rs b/library/std/src/sys/solid/mod.rs index 923d27fd9..5af83653c 100644 --- a/library/std/src/sys/solid/mod.rs +++ b/library/std/src/sys/solid/mod.rs @@ -72,6 +72,11 @@ pub fn unsupported_err() -> crate::io::Error { ) } +#[inline] +pub fn is_interrupted(code: i32) -> bool { + net::is_interrupted(code) +} + pub fn decode_error_kind(code: i32) -> crate::io::ErrorKind { error::decode_error_kind(code) } diff --git a/library/std/src/sys/solid/net.rs b/library/std/src/sys/solid/net.rs index 0bd2bc3b9..6adced787 100644 --- a/library/std/src/sys/solid/net.rs +++ b/library/std/src/sys/solid/net.rs @@ -181,6 +181,11 @@ pub(super) fn error_name(er: abi::ER) -> Option<&'static str> { unsafe { CStr::from_ptr(netc::strerror(er)) }.to_str().ok() } +#[inline] +pub fn is_interrupted(er: abi::ER) -> bool { + er == netc::SOLID_NET_ERR_BASE - libc::EINTR +} + pub(super) fn decode_error_kind(er: abi::ER) -> ErrorKind { let errno = netc::SOLID_NET_ERR_BASE - er; match errno as libc::c_int { diff --git a/library/std/src/sys/solid/os.rs b/library/std/src/sys/solid/os.rs index 9f4e66d62..ff81544ba 100644 --- a/library/std/src/sys/solid/os.rs +++ b/library/std/src/sys/solid/os.rs @@ -8,7 +8,7 @@ use crate::os::{ solid::ffi::{OsStrExt, OsStringExt}, }; use crate::path::{self, PathBuf}; -use crate::sync::RwLock; +use crate::sync::{PoisonError, RwLock}; use crate::sys::common::small_c_string::run_with_cstr; use crate::vec; diff --git a/library/std/src/sys/uefi/alloc.rs b/library/std/src/sys/uefi/alloc.rs new file mode 100644 index 000000000..789e3cbd8 --- /dev/null +++ b/library/std/src/sys/uefi/alloc.rs @@ -0,0 +1,33 @@ +//! Global Allocator for UEFI. +//! Uses [r-efi-alloc](https://crates.io/crates/r-efi-alloc) + +use crate::alloc::{GlobalAlloc, Layout, System}; + +const MEMORY_TYPE: u32 = r_efi::efi::LOADER_DATA; + +#[stable(feature = "alloc_system_type", since = "1.28.0")] +unsafe impl GlobalAlloc for System { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + // Return null pointer if boot services are not available + if crate::os::uefi::env::boot_services().is_none() { + return crate::ptr::null_mut(); + } + + // If boot services is valid then SystemTable is not null. + let system_table = crate::os::uefi::env::system_table().as_ptr().cast(); + // The caller must ensure non-0 layout + unsafe { r_efi_alloc::raw::alloc(system_table, layout, MEMORY_TYPE) } + } + + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + // Do nothing if boot services are not available + if crate::os::uefi::env::boot_services().is_none() { + return; + } + + // If boot services is valid then SystemTable is not null. + let system_table = crate::os::uefi::env::system_table().as_ptr().cast(); + // The caller must ensure non-0 layout + unsafe { r_efi_alloc::raw::dealloc(system_table, ptr, layout) } + } +} diff --git a/library/std/src/sys/uefi/env.rs b/library/std/src/sys/uefi/env.rs new file mode 100644 index 000000000..c106d5fed --- /dev/null +++ b/library/std/src/sys/uefi/env.rs @@ -0,0 +1,9 @@ +pub mod os { + pub const FAMILY: &str = ""; + pub const OS: &str = "uefi"; + pub const DLL_PREFIX: &str = ""; + pub const DLL_SUFFIX: &str = ""; + pub const DLL_EXTENSION: &str = ""; + pub const EXE_SUFFIX: &str = ".efi"; + pub const EXE_EXTENSION: &str = "efi"; +} diff --git a/library/std/src/sys/uefi/helpers.rs b/library/std/src/sys/uefi/helpers.rs new file mode 100644 index 000000000..126661bfc --- /dev/null +++ b/library/std/src/sys/uefi/helpers.rs @@ -0,0 +1,141 @@ +//! Contains most of the shared UEFI specific stuff. Some of this might be moved to `std::os::uefi` +//! if needed but no point in adding extra public API when there is not Std support for UEFI in the +//! first place +//! +//! Some Nomenclature +//! * Protocol: +//! - Protocols serve to enable communication between separately built modules, including drivers. +//! - Every protocol has a GUID associated with it. The GUID serves as the name for the protocol. +//! - Protocols are produced and consumed. +//! - More information about protocols can be found [here](https://edk2-docs.gitbook.io/edk-ii-uefi-driver-writer-s-guide/3_foundation/36_protocols_and_handles) + +use r_efi::efi::{self, Guid}; + +use crate::mem::{size_of, MaybeUninit}; +use crate::os::uefi; +use crate::ptr::NonNull; +use crate::{ + io::{self, const_io_error}, + os::uefi::env::boot_services, +}; + +const BOOT_SERVICES_UNAVAILABLE: io::Error = + const_io_error!(io::ErrorKind::Other, "Boot Services are no longer available"); + +/// Locate Handles with a particular Protocol GUID +/// Implemented using `EFI_BOOT_SERVICES.LocateHandles()` +/// +/// Returns an array of [Handles](r_efi::efi::Handle) that support a specified protocol. +pub(crate) fn locate_handles(mut guid: Guid) -> io::Result>> { + fn inner( + guid: &mut Guid, + boot_services: NonNull, + buf_size: &mut usize, + buf: *mut r_efi::efi::Handle, + ) -> io::Result<()> { + let r = unsafe { + ((*boot_services.as_ptr()).locate_handle)( + r_efi::efi::BY_PROTOCOL, + guid, + crate::ptr::null_mut(), + buf_size, + buf, + ) + }; + + if r.is_error() { Err(crate::io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) } + } + + let boot_services = boot_services().ok_or(BOOT_SERVICES_UNAVAILABLE)?.cast(); + let mut buf_len = 0usize; + + // This should always fail since the size of buffer is 0. This call should update the buf_len + // variable with the required buffer length + match inner(&mut guid, boot_services, &mut buf_len, crate::ptr::null_mut()) { + Ok(()) => unreachable!(), + Err(e) => match e.kind() { + io::ErrorKind::FileTooLarge => {} + _ => return Err(e), + }, + } + + // The returned buf_len is in bytes + assert_eq!(buf_len % size_of::(), 0); + let num_of_handles = buf_len / size_of::(); + let mut buf: Vec = Vec::with_capacity(num_of_handles); + match inner(&mut guid, boot_services, &mut buf_len, buf.as_mut_ptr()) { + Ok(()) => { + // This is safe because the call will succeed only if buf_len >= required length. + // Also, on success, the `buf_len` is updated with the size of bufferv (in bytes) written + unsafe { buf.set_len(num_of_handles) }; + Ok(buf.into_iter().filter_map(|x| NonNull::new(x)).collect()) + } + Err(e) => Err(e), + } +} + +/// Open Protocol on a handle. +/// Internally just a call to `EFI_BOOT_SERVICES.OpenProtocol()`. +/// +/// Queries a handle to determine if it supports a specified protocol. If the protocol is +/// supported by the handle, it opens the protocol on behalf of the calling agent. +pub(crate) fn open_protocol( + handle: NonNull, + mut protocol_guid: Guid, +) -> io::Result> { + let boot_services: NonNull = + boot_services().ok_or(BOOT_SERVICES_UNAVAILABLE)?.cast(); + let system_handle = uefi::env::image_handle(); + let mut protocol: MaybeUninit<*mut T> = MaybeUninit::uninit(); + + let r = unsafe { + ((*boot_services.as_ptr()).open_protocol)( + handle.as_ptr(), + &mut protocol_guid, + protocol.as_mut_ptr().cast(), + system_handle.as_ptr(), + crate::ptr::null_mut(), + r_efi::system::OPEN_PROTOCOL_GET_PROTOCOL, + ) + }; + + if r.is_error() { + Err(crate::io::Error::from_raw_os_error(r.as_usize())) + } else { + NonNull::new(unsafe { protocol.assume_init() }) + .ok_or(const_io_error!(io::ErrorKind::Other, "null protocol")) + } +} + +pub(crate) fn create_event( + signal: u32, + tpl: efi::Tpl, + handler: Option, + context: *mut crate::ffi::c_void, +) -> io::Result> { + let boot_services: NonNull = + boot_services().ok_or(BOOT_SERVICES_UNAVAILABLE)?.cast(); + let mut event: r_efi::efi::Event = crate::ptr::null_mut(); + let r = unsafe { + let create_event = (*boot_services.as_ptr()).create_event; + (create_event)(signal, tpl, handler, context, &mut event) + }; + if r.is_error() { + Err(crate::io::Error::from_raw_os_error(r.as_usize())) + } else { + NonNull::new(event).ok_or(const_io_error!(io::ErrorKind::Other, "null protocol")) + } +} + +/// # SAFETY +/// - The supplied event must be valid +pub(crate) unsafe fn close_event(evt: NonNull) -> io::Result<()> { + let boot_services: NonNull = + boot_services().ok_or(BOOT_SERVICES_UNAVAILABLE)?.cast(); + let r = unsafe { + let close_event = (*boot_services.as_ptr()).close_event; + (close_event)(evt.as_ptr()) + }; + + if r.is_error() { Err(crate::io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) } +} diff --git a/library/std/src/sys/uefi/mod.rs b/library/std/src/sys/uefi/mod.rs new file mode 100644 index 000000000..9a10395af --- /dev/null +++ b/library/std/src/sys/uefi/mod.rs @@ -0,0 +1,244 @@ +//! Platform-specific extensions to `std` for UEFI platforms. +//! +//! Provides access to platform-level information on UEFI platforms, and +//! exposes UEFI-specific functions that would otherwise be inappropriate as +//! part of the core `std` library. +//! +//! It exposes more ways to deal with platform-specific strings ([`OsStr`], +//! [`OsString`]), allows to set permissions more granularly, extract low-level +//! file descriptors from files and sockets, and has platform-specific helpers +//! for spawning processes. +//! +//! [`OsStr`]: crate::ffi::OsStr +//! [`OsString`]: crate::ffi::OsString + +pub mod alloc; +#[path = "../unsupported/args.rs"] +pub mod args; +#[path = "../unix/cmath.rs"] +pub mod cmath; +pub mod env; +#[path = "../unsupported/fs.rs"] +pub mod fs; +#[path = "../unsupported/io.rs"] +pub mod io; +#[path = "../unsupported/locks/mod.rs"] +pub mod locks; +#[path = "../unsupported/net.rs"] +pub mod net; +#[path = "../unsupported/once.rs"] +pub mod once; +pub mod os; +#[path = "../windows/os_str.rs"] +pub mod os_str; +pub mod path; +#[path = "../unsupported/pipe.rs"] +pub mod pipe; +#[path = "../unsupported/process.rs"] +pub mod process; +#[path = "../unsupported/stdio.rs"] +pub mod stdio; +#[path = "../unsupported/thread.rs"] +pub mod thread; +#[path = "../unsupported/thread_local_key.rs"] +pub mod thread_local_key; +#[path = "../unsupported/thread_parking.rs"] +pub mod thread_parking; +#[path = "../unsupported/time.rs"] +pub mod time; + +mod helpers; + +#[cfg(test)] +mod tests; + +pub type RawOsError = usize; + +use crate::io as std_io; +use crate::os::uefi; +use crate::ptr::NonNull; +use crate::sync::atomic::{AtomicPtr, Ordering}; + +pub mod memchr { + pub use core::slice::memchr::{memchr, memrchr}; +} + +static EXIT_BOOT_SERVICE_EVENT: AtomicPtr = + AtomicPtr::new(crate::ptr::null_mut()); + +/// # SAFETY +/// - must be called only once during runtime initialization. +/// - argc must be 2. +/// - argv must be &[Handle, *mut SystemTable]. +pub(crate) unsafe fn init(argc: isize, argv: *const *const u8, _sigpipe: u8) { + assert_eq!(argc, 2); + let image_handle = unsafe { NonNull::new(*argv as *mut crate::ffi::c_void).unwrap() }; + let system_table = unsafe { NonNull::new(*argv.add(1) as *mut crate::ffi::c_void).unwrap() }; + unsafe { uefi::env::init_globals(image_handle, system_table) }; + + // Register exit boot services handler + match helpers::create_event( + r_efi::efi::EVT_SIGNAL_EXIT_BOOT_SERVICES, + r_efi::efi::TPL_NOTIFY, + Some(exit_boot_service_handler), + crate::ptr::null_mut(), + ) { + Ok(x) => { + if EXIT_BOOT_SERVICE_EVENT + .compare_exchange( + crate::ptr::null_mut(), + x.as_ptr(), + Ordering::Release, + Ordering::Acquire, + ) + .is_err() + { + abort_internal(); + }; + } + Err(_) => abort_internal(), + } +} + +/// # SAFETY +/// this is not guaranteed to run, for example when the program aborts. +/// - must be called only once during runtime cleanup. +pub unsafe fn cleanup() { + if let Some(exit_boot_service_event) = + NonNull::new(EXIT_BOOT_SERVICE_EVENT.swap(crate::ptr::null_mut(), Ordering::Acquire)) + { + let _ = unsafe { helpers::close_event(exit_boot_service_event) }; + } +} + +#[inline] +pub const fn unsupported() -> std_io::Result { + Err(unsupported_err()) +} + +#[inline] +pub const fn unsupported_err() -> std_io::Error { + std_io::const_io_error!(std_io::ErrorKind::Unsupported, "operation not supported on UEFI",) +} + +pub fn decode_error_kind(code: RawOsError) -> crate::io::ErrorKind { + use crate::io::ErrorKind; + use r_efi::efi::Status; + + match r_efi::efi::Status::from_usize(code) { + Status::ALREADY_STARTED + | Status::COMPROMISED_DATA + | Status::CONNECTION_FIN + | Status::CRC_ERROR + | Status::DEVICE_ERROR + | Status::END_OF_MEDIA + | Status::HTTP_ERROR + | Status::ICMP_ERROR + | Status::INCOMPATIBLE_VERSION + | Status::LOAD_ERROR + | Status::MEDIA_CHANGED + | Status::NO_MAPPING + | Status::NO_MEDIA + | Status::NOT_STARTED + | Status::PROTOCOL_ERROR + | Status::PROTOCOL_UNREACHABLE + | Status::TFTP_ERROR + | Status::VOLUME_CORRUPTED => ErrorKind::Other, + Status::BAD_BUFFER_SIZE | Status::INVALID_LANGUAGE => ErrorKind::InvalidData, + Status::ABORTED => ErrorKind::ConnectionAborted, + Status::ACCESS_DENIED => ErrorKind::PermissionDenied, + Status::BUFFER_TOO_SMALL => ErrorKind::FileTooLarge, + Status::CONNECTION_REFUSED => ErrorKind::ConnectionRefused, + Status::CONNECTION_RESET => ErrorKind::ConnectionReset, + Status::END_OF_FILE => ErrorKind::UnexpectedEof, + Status::HOST_UNREACHABLE => ErrorKind::HostUnreachable, + Status::INVALID_PARAMETER => ErrorKind::InvalidInput, + Status::IP_ADDRESS_CONFLICT => ErrorKind::AddrInUse, + Status::NETWORK_UNREACHABLE => ErrorKind::NetworkUnreachable, + Status::NO_RESPONSE => ErrorKind::HostUnreachable, + Status::NOT_FOUND => ErrorKind::NotFound, + Status::NOT_READY => ErrorKind::ResourceBusy, + Status::OUT_OF_RESOURCES => ErrorKind::OutOfMemory, + Status::SECURITY_VIOLATION => ErrorKind::PermissionDenied, + Status::TIMEOUT => ErrorKind::TimedOut, + Status::UNSUPPORTED => ErrorKind::Unsupported, + Status::VOLUME_FULL => ErrorKind::StorageFull, + Status::WRITE_PROTECTED => ErrorKind::ReadOnlyFilesystem, + _ => ErrorKind::Uncategorized, + } +} + +pub fn abort_internal() -> ! { + if let Some(exit_boot_service_event) = + NonNull::new(EXIT_BOOT_SERVICE_EVENT.load(Ordering::Acquire)) + { + let _ = unsafe { helpers::close_event(exit_boot_service_event) }; + } + + if let (Some(boot_services), Some(handle)) = + (uefi::env::boot_services(), uefi::env::try_image_handle()) + { + let boot_services: NonNull = boot_services.cast(); + let _ = unsafe { + ((*boot_services.as_ptr()).exit)( + handle.as_ptr(), + r_efi::efi::Status::ABORTED, + 0, + crate::ptr::null_mut(), + ) + }; + } + + // In case SystemTable and ImageHandle cannot be reached, use `core::intrinsics::abort` + core::intrinsics::abort(); +} + +// This function is needed by the panic runtime. The symbol is named in +// pre-link args for the target specification, so keep that in sync. +#[cfg(not(test))] +#[no_mangle] +pub extern "C" fn __rust_abort() { + abort_internal(); +} + +#[inline] +pub fn hashmap_random_keys() -> (u64, u64) { + get_random().unwrap() +} + +fn get_random() -> Option<(u64, u64)> { + use r_efi::protocols::rng; + + let mut buf = [0u8; 16]; + let handles = helpers::locate_handles(rng::PROTOCOL_GUID).ok()?; + for handle in handles { + if let Ok(protocol) = helpers::open_protocol::(handle, rng::PROTOCOL_GUID) { + let r = unsafe { + ((*protocol.as_ptr()).get_rng)( + protocol.as_ptr(), + crate::ptr::null_mut(), + buf.len(), + buf.as_mut_ptr(), + ) + }; + if r.is_error() { + continue; + } else { + return Some(( + u64::from_le_bytes(buf[..8].try_into().ok()?), + u64::from_le_bytes(buf[8..].try_into().ok()?), + )); + } + } + } + None +} + +/// Disable access to BootServices if `EVT_SIGNAL_EXIT_BOOT_SERVICES` is signaled +extern "efiapi" fn exit_boot_service_handler(_e: r_efi::efi::Event, _ctx: *mut crate::ffi::c_void) { + uefi::env::disable_boot_services(); +} + +pub fn is_interrupted(_code: RawOsError) -> bool { + false +} diff --git a/library/std/src/sys/uefi/os.rs b/library/std/src/sys/uefi/os.rs new file mode 100644 index 000000000..e6693db68 --- /dev/null +++ b/library/std/src/sys/uefi/os.rs @@ -0,0 +1,237 @@ +use super::{unsupported, RawOsError}; +use crate::error::Error as StdError; +use crate::ffi::{OsStr, OsString}; +use crate::fmt; +use crate::io; +use crate::marker::PhantomData; +use crate::os::uefi; +use crate::path::{self, PathBuf}; +use crate::ptr::NonNull; +use r_efi::efi::Status; + +pub fn errno() -> RawOsError { + 0 +} + +pub fn error_string(errno: RawOsError) -> String { + // Keep the List in Alphabetical Order + // The Messages are taken from UEFI Specification Appendix D - Status Codes + match r_efi::efi::Status::from_usize(errno) { + Status::ABORTED => "The operation was aborted.".to_owned(), + Status::ACCESS_DENIED => "Access was denied.".to_owned(), + Status::ALREADY_STARTED => "The protocol has already been started.".to_owned(), + Status::BAD_BUFFER_SIZE => "The buffer was not the proper size for the request.".to_owned(), + Status::BUFFER_TOO_SMALL => { + "The buffer is not large enough to hold the requested data. The required buffer size is returned in the appropriate parameter when this error occurs.".to_owned() + } + Status::COMPROMISED_DATA => { + "The security status of the data is unknown or compromised and the data must be updated or replaced to restore a valid security status.".to_owned() + } + Status::CONNECTION_FIN => { + "The receiving operation fails because the communication peer has closed the connection and there is no more data in the receive buffer of the instance.".to_owned() + } + Status::CONNECTION_REFUSED => { + "The receiving or transmission operation fails because this connection is refused.".to_owned() + } + Status::CONNECTION_RESET => { + "The connect fails because the connection is reset either by instance itself or the communication peer.".to_owned() + } + Status::CRC_ERROR => "A CRC error was detected.".to_owned(), + Status::DEVICE_ERROR => "The physical device reported an error while attempting the operation.".to_owned() + , + Status::END_OF_FILE => { + "The end of the file was reached.".to_owned() + } + Status::END_OF_MEDIA => { + "Beginning or end of media was reached".to_owned() + } + Status::HOST_UNREACHABLE => { + "The remote host is not reachable.".to_owned() + } + Status::HTTP_ERROR => { + "A HTTP error occurred during the network operation.".to_owned() + } + Status::ICMP_ERROR => { + "An ICMP error occurred during the network operation.".to_owned() + } + Status::INCOMPATIBLE_VERSION => { + "The function encountered an internal version that was incompatible with a version requested by the caller.".to_owned() + } + Status::INVALID_LANGUAGE => { + "The language specified was invalid.".to_owned() + } + Status::INVALID_PARAMETER => { + "A parameter was incorrect.".to_owned() + } + Status::IP_ADDRESS_CONFLICT => { + "There is an address conflict address allocation".to_owned() + } + Status::LOAD_ERROR => { + "The image failed to load.".to_owned() + } + Status::MEDIA_CHANGED => { + "The medium in the device has changed since the last access.".to_owned() + } + Status::NETWORK_UNREACHABLE => { + "The network containing the remote host is not reachable.".to_owned() + } + Status::NO_MAPPING => { + "A mapping to a device does not exist.".to_owned() + } + Status::NO_MEDIA => { + "The device does not contain any medium to perform the operation.".to_owned() + } + Status::NO_RESPONSE => { + "The server was not found or did not respond to the request.".to_owned() + } + Status::NOT_FOUND => "The item was not found.".to_owned(), + Status::NOT_READY => { + "There is no data pending upon return.".to_owned() + } + Status::NOT_STARTED => { + "The protocol has not been started.".to_owned() + } + Status::OUT_OF_RESOURCES => { + "A resource has run out.".to_owned() + } + Status::PROTOCOL_ERROR => { + "A protocol error occurred during the network operation.".to_owned() + } + Status::PROTOCOL_UNREACHABLE => { + "An ICMP protocol unreachable error is received.".to_owned() + } + Status::SECURITY_VIOLATION => { + "The function was not performed due to a security violation.".to_owned() + } + Status::TFTP_ERROR => { + "A TFTP error occurred during the network operation.".to_owned() + } + Status::TIMEOUT => "The timeout time expired.".to_owned(), + Status::UNSUPPORTED => { + "The operation is not supported.".to_owned() + } + Status::VOLUME_FULL => { + "There is no more space on the file system.".to_owned() + } + Status::VOLUME_CORRUPTED => { + "An inconstancy was detected on the file system causing the operating to fail.".to_owned() + } + Status::WRITE_PROTECTED => { + "The device cannot be written to.".to_owned() + } + _ => format!("Status: {}", errno), + } +} + +pub fn getcwd() -> io::Result { + unsupported() +} + +pub fn chdir(_: &path::Path) -> io::Result<()> { + unsupported() +} + +pub struct SplitPaths<'a>(!, PhantomData<&'a ()>); + +pub fn split_paths(_unparsed: &OsStr) -> SplitPaths<'_> { + panic!("unsupported") +} + +impl<'a> Iterator for SplitPaths<'a> { + type Item = PathBuf; + fn next(&mut self) -> Option { + self.0 + } +} + +#[derive(Debug)] +pub struct JoinPathsError; + +pub fn join_paths(_paths: I) -> Result +where + I: Iterator, + T: AsRef, +{ + Err(JoinPathsError) +} + +impl fmt::Display for JoinPathsError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + "not supported on this platform yet".fmt(f) + } +} + +impl StdError for JoinPathsError {} + +pub fn current_exe() -> io::Result { + unsupported() +} + +pub struct Env(!); + +impl Env { + // FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when ::fmt matches ::fmt. + pub fn str_debug(&self) -> impl fmt::Debug + '_ { + let Self(inner) = self; + match *inner {} + } +} + +impl Iterator for Env { + type Item = (OsString, OsString); + fn next(&mut self) -> Option<(OsString, OsString)> { + self.0 + } +} + +impl fmt::Debug for Env { + fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self(inner) = self; + match *inner {} + } +} + +pub fn env() -> Env { + panic!("not supported on this platform") +} + +pub fn getenv(_: &OsStr) -> Option { + None +} + +pub fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> { + Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform")) +} + +pub fn unsetenv(_: &OsStr) -> io::Result<()> { + Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform")) +} + +pub fn temp_dir() -> PathBuf { + panic!("no filesystem on this platform") +} + +pub fn home_dir() -> Option { + None +} + +pub fn exit(code: i32) -> ! { + if let (Some(boot_services), Some(handle)) = + (uefi::env::boot_services(), uefi::env::try_image_handle()) + { + let boot_services: NonNull = boot_services.cast(); + let _ = unsafe { + ((*boot_services.as_ptr()).exit)( + handle.as_ptr(), + Status::from_usize(code as usize), + 0, + crate::ptr::null_mut(), + ) + }; + } + crate::intrinsics::abort() +} + +pub fn getpid() -> u32 { + panic!("no pids on this platform") +} diff --git a/library/std/src/sys/uefi/path.rs b/library/std/src/sys/uefi/path.rs new file mode 100644 index 000000000..106682eee --- /dev/null +++ b/library/std/src/sys/uefi/path.rs @@ -0,0 +1,25 @@ +use super::unsupported; +use crate::ffi::OsStr; +use crate::io; +use crate::path::{Path, PathBuf, Prefix}; + +pub const MAIN_SEP_STR: &str = "\\"; +pub const MAIN_SEP: char = '\\'; + +#[inline] +pub fn is_sep_byte(b: u8) -> bool { + b == b'\\' +} + +#[inline] +pub fn is_verbatim_sep(b: u8) -> bool { + b == b'\\' +} + +pub fn parse_prefix(_p: &OsStr) -> Option> { + None +} + +pub(crate) fn absolute(_path: &Path) -> io::Result { + unsupported() +} diff --git a/library/std/src/sys/uefi/tests.rs b/library/std/src/sys/uefi/tests.rs new file mode 100644 index 000000000..8806eda3a --- /dev/null +++ b/library/std/src/sys/uefi/tests.rs @@ -0,0 +1,21 @@ +use super::alloc::*; + +#[test] +fn align() { + // UEFI ABI specifies that allocation alignment minimum is always 8. So this can be + // statically verified. + assert_eq!(POOL_ALIGNMENT, 8); + + // Loop over allocation-request sizes from 0-256 and alignments from 1-128, and verify + // that in case of overalignment there is at least space for one additional pointer to + // store in the allocation. + for i in 0..256 { + for j in &[1, 2, 4, 8, 16, 32, 64, 128] { + if *j <= 8 { + assert_eq!(align_size(i, *j), i); + } else { + assert!(align_size(i, *j) > i + std::mem::size_of::<*mut ()>()); + } + } + } +} diff --git a/library/std/src/sys/unix/alloc.rs b/library/std/src/sys/unix/alloc.rs index 8604b5398..af0089978 100644 --- a/library/std/src/sys/unix/alloc.rs +++ b/library/std/src/sys/unix/alloc.rs @@ -86,7 +86,11 @@ cfg_if::cfg_if! { } else if #[cfg(target_os = "wasi")] { #[inline] unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 { - libc::aligned_alloc(layout.align(), layout.size()) as *mut u8 + // C11 aligned_alloc requires that the size be a multiple of the alignment. + // Layout already checks that the size rounded up doesn't overflow isize::MAX. + let align = layout.align(); + let size = layout.size().next_multiple_of(align); + libc::aligned_alloc(align, size) as *mut u8 } } else { #[inline] diff --git a/library/std/src/sys/unix/args.rs b/library/std/src/sys/unix/args.rs index eafd6821f..19334e2af 100644 --- a/library/std/src/sys/unix/args.rs +++ b/library/std/src/sys/unix/args.rs @@ -71,6 +71,7 @@ impl DoubleEndedIterator for Args { target_os = "vxworks", target_os = "horizon", target_os = "nto", + target_os = "hurd", ))] mod imp { use super::Args; diff --git a/library/std/src/sys/unix/env.rs b/library/std/src/sys/unix/env.rs index 929e9dae7..c6d8578a6 100644 --- a/library/std/src/sys/unix/env.rs +++ b/library/std/src/sys/unix/env.rs @@ -152,6 +152,17 @@ pub mod os { pub const EXE_EXTENSION: &str = "elf"; } +#[cfg(target_os = "hurd")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "hurd"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + #[cfg(target_os = "vita")] pub mod os { pub const FAMILY: &str = "unix"; diff --git a/library/std/src/sys/unix/fd.rs b/library/std/src/sys/unix/fd.rs index 85e020ae4..6c4f40842 100644 --- a/library/std/src/sys/unix/fd.rs +++ b/library/std/src/sys/unix/fd.rs @@ -13,14 +13,16 @@ use crate::sys_common::{AsInner, FromInner, IntoInner}; target_os = "android", target_os = "linux", target_os = "emscripten", - target_os = "l4re" + target_os = "l4re", + target_os = "hurd", ))] use libc::off64_t; #[cfg(not(any( target_os = "linux", target_os = "emscripten", target_os = "l4re", - target_os = "android" + target_os = "android", + target_os = "hurd", )))] use libc::off_t as off64_t; @@ -124,9 +126,9 @@ impl FileDesc { } pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result { - #[cfg(not(any(target_os = "linux", target_os = "android")))] + #[cfg(not(any(target_os = "linux", target_os = "android", target_os = "hurd")))] use libc::pread as pread64; - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", target_os = "hurd"))] use libc::pread64; unsafe { @@ -160,6 +162,7 @@ impl FileDesc { target_os = "emscripten", target_os = "freebsd", target_os = "fuchsia", + target_os = "hurd", target_os = "illumos", target_os = "linux", target_os = "netbsd", @@ -181,6 +184,7 @@ impl FileDesc { target_os = "emscripten", target_os = "freebsd", target_os = "fuchsia", + target_os = "hurd", target_os = "illumos", target_os = "ios", target_os = "tvos", @@ -281,9 +285,9 @@ impl FileDesc { } pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result { - #[cfg(not(any(target_os = "linux", target_os = "android")))] + #[cfg(not(any(target_os = "linux", target_os = "android", target_os = "hurd")))] use libc::pwrite as pwrite64; - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", target_os = "hurd"))] use libc::pwrite64; unsafe { @@ -301,6 +305,7 @@ impl FileDesc { target_os = "emscripten", target_os = "freebsd", target_os = "fuchsia", + target_os = "hurd", target_os = "illumos", target_os = "linux", target_os = "netbsd", @@ -322,6 +327,7 @@ impl FileDesc { target_os = "emscripten", target_os = "freebsd", target_os = "fuchsia", + target_os = "hurd", target_os = "illumos", target_os = "ios", target_os = "tvos", diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs index a5604c92a..764e1f257 100644 --- a/library/std/src/sys/unix/fs.rs +++ b/library/std/src/sys/unix/fs.rs @@ -39,9 +39,14 @@ use libc::{c_int, mode_t}; all(target_os = "linux", target_env = "gnu") ))] use libc::c_char; -#[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "android"))] +#[cfg(any( + target_os = "linux", + target_os = "emscripten", + target_os = "android", + target_os = "hurd", +))] use libc::dirfd; -#[cfg(any(target_os = "linux", target_os = "emscripten"))] +#[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "hurd"))] use libc::fstatat64; #[cfg(any( target_os = "android", @@ -53,7 +58,7 @@ use libc::fstatat64; target_os = "vita", ))] use libc::readdir as readdir64; -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "hurd"))] use libc::readdir64; #[cfg(any(target_os = "emscripten", target_os = "l4re"))] use libc::readdir64_r; @@ -68,6 +73,7 @@ use libc::readdir64_r; target_os = "redox", target_os = "nto", target_os = "vita", + target_os = "hurd", )))] use libc::readdir_r as readdir64_r; #[cfg(target_os = "android")] @@ -79,13 +85,19 @@ use libc::{ target_os = "linux", target_os = "emscripten", target_os = "l4re", - target_os = "android" + target_os = "android", + target_os = "hurd", )))] use libc::{ dirent as dirent64, fstat as fstat64, ftruncate as ftruncate64, lseek as lseek64, lstat as lstat64, off_t as off64_t, open as open64, stat as stat64, }; -#[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "l4re"))] +#[cfg(any( + target_os = "linux", + target_os = "emscripten", + target_os = "l4re", + target_os = "hurd" +))] use libc::{dirent64, fstat64, ftruncate64, lseek64, lstat64, off64_t, open64, stat64}; pub use crate::sys_common::fs::try_exists; @@ -277,7 +289,8 @@ unsafe impl Sync for Dir {} target_os = "fuchsia", target_os = "redox", target_os = "nto", - target_os = "vita" + target_os = "vita", + target_os = "hurd", ))] pub struct DirEntry { dir: Arc, @@ -300,6 +313,7 @@ pub struct DirEntry { target_os = "redox", target_os = "nto", target_os = "vita", + target_os = "hurd", ))] struct dirent64_min { d_ino: u64, @@ -321,6 +335,7 @@ struct dirent64_min { target_os = "redox", target_os = "nto", target_os = "vita", + target_os = "hurd", )))] pub struct DirEntry { dir: Arc, @@ -455,7 +470,8 @@ impl FileAttr { target_os = "vxworks", target_os = "espidf", target_os = "horizon", - target_os = "vita" + target_os = "vita", + target_os = "hurd", )))] pub fn modified(&self) -> io::Result { #[cfg(target_pointer_width = "32")] @@ -473,7 +489,7 @@ impl FileAttr { Ok(SystemTime::new(self.stat.st_mtime as i64, 0)) } - #[cfg(target_os = "horizon")] + #[cfg(any(target_os = "horizon", target_os = "hurd"))] pub fn modified(&self) -> io::Result { Ok(SystemTime::from(self.stat.st_mtim)) } @@ -482,7 +498,8 @@ impl FileAttr { target_os = "vxworks", target_os = "espidf", target_os = "horizon", - target_os = "vita" + target_os = "vita", + target_os = "hurd", )))] pub fn accessed(&self) -> io::Result { #[cfg(target_pointer_width = "32")] @@ -500,7 +517,7 @@ impl FileAttr { Ok(SystemTime::new(self.stat.st_atime as i64, 0)) } - #[cfg(target_os = "horizon")] + #[cfg(any(target_os = "horizon", target_os = "hurd"))] pub fn accessed(&self) -> io::Result { Ok(SystemTime::from(self.stat.st_atim)) } @@ -656,6 +673,7 @@ impl Iterator for ReadDir { target_os = "illumos", target_os = "nto", target_os = "vita", + target_os = "hurd", ))] fn next(&mut self) -> Option> { if self.end_of_stream { @@ -756,6 +774,7 @@ impl Iterator for ReadDir { target_os = "illumos", target_os = "nto", target_os = "vita", + target_os = "hurd", )))] fn next(&mut self) -> Option> { if self.end_of_stream { @@ -792,7 +811,7 @@ impl Drop for Dir { fn drop(&mut self) { let r = unsafe { libc::closedir(self.0) }; assert!( - r == 0 || crate::io::Error::last_os_error().kind() == crate::io::ErrorKind::Interrupted, + r == 0 || crate::io::Error::last_os_error().is_interrupted(), "unexpected error during closedir: {:?}", crate::io::Error::last_os_error() ); @@ -809,7 +828,12 @@ impl DirEntry { } #[cfg(all( - any(target_os = "linux", target_os = "emscripten", target_os = "android"), + any( + target_os = "linux", + target_os = "emscripten", + target_os = "android", + target_os = "hurd", + ), not(miri) ))] pub fn metadata(&self) -> io::Result { @@ -833,7 +857,12 @@ impl DirEntry { } #[cfg(any( - not(any(target_os = "linux", target_os = "emscripten", target_os = "android")), + not(any( + target_os = "linux", + target_os = "emscripten", + target_os = "android", + target_os = "hurd", + )), miri ))] pub fn metadata(&self) -> io::Result { @@ -892,6 +921,7 @@ impl DirEntry { target_os = "horizon", target_os = "vita", target_os = "nto", + target_os = "hurd", ))] pub fn ino(&self) -> u64 { self.entry.d_ino as u64 @@ -949,6 +979,7 @@ impl DirEntry { target_os = "redox", target_os = "nto", target_os = "vita", + target_os = "hurd", )))] fn name_cstr(&self) -> &CStr { unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()) } @@ -962,6 +993,7 @@ impl DirEntry { target_os = "redox", target_os = "nto", target_os = "vita", + target_os = "hurd", ))] fn name_cstr(&self) -> &CStr { &self.name @@ -1131,6 +1163,7 @@ impl File { target_os = "netbsd", target_os = "openbsd", target_os = "nto", + target_os = "hurd", ))] unsafe fn os_datasync(fd: c_int) -> c_int { libc::fdatasync(fd) @@ -1146,6 +1179,7 @@ impl File { target_os = "openbsd", target_os = "watchos", target_os = "nto", + target_os = "hurd", )))] unsafe fn os_datasync(fd: c_int) -> c_int { libc::fsync(fd) @@ -1456,6 +1490,7 @@ impl fmt::Debug for File { target_os = "linux", target_os = "macos", target_os = "freebsd", + target_os = "hurd", target_os = "netbsd", target_os = "openbsd", target_os = "vxworks" @@ -1477,6 +1512,7 @@ impl fmt::Debug for File { target_os = "linux", target_os = "macos", target_os = "freebsd", + target_os = "hurd", target_os = "netbsd", target_os = "openbsd", target_os = "vxworks" diff --git a/library/std/src/sys/unix/kernel_copy.rs b/library/std/src/sys/unix/kernel_copy.rs index 4d17a1b00..18acd5ecc 100644 --- a/library/std/src/sys/unix/kernel_copy.rs +++ b/library/std/src/sys/unix/kernel_copy.rs @@ -59,9 +59,9 @@ use crate::ptr; use crate::sync::atomic::{AtomicBool, AtomicU8, Ordering}; use crate::sys::cvt; use crate::sys::weak::syscall; -#[cfg(not(all(target_os = "linux", target_env = "gnu")))] +#[cfg(not(any(all(target_os = "linux", target_env = "gnu"), target_os = "hurd")))] use libc::sendfile as sendfile64; -#[cfg(all(target_os = "linux", target_env = "gnu"))] +#[cfg(any(all(target_os = "linux", target_env = "gnu"), target_os = "hurd"))] use libc::sendfile64; use libc::{EBADF, EINVAL, ENOSYS, EOPNOTSUPP, EOVERFLOW, EPERM, EXDEV}; diff --git a/library/std/src/sys/unix/mod.rs b/library/std/src/sys/unix/mod.rs index 77ef086f2..3edafde71 100644 --- a/library/std/src/sys/unix/mod.rs +++ b/library/std/src/sys/unix/mod.rs @@ -204,6 +204,10 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { } if let Some(handler) = handler { rtassert!(signal(libc::SIGPIPE, handler) != libc::SIG_ERR); + #[cfg(target_os = "hurd")] + { + rtassert!(signal(libc::SIGLOST, handler) != libc::SIG_ERR); + } } } } @@ -240,6 +244,11 @@ pub use crate::sys::android::signal; #[cfg(not(target_os = "android"))] pub use libc::signal; +#[inline] +pub(crate) fn is_interrupted(errno: i32) -> bool { + errno == libc::EINTR +} + pub fn decode_error_kind(errno: i32) -> ErrorKind { use ErrorKind::*; match errno as libc::c_int { @@ -315,7 +324,7 @@ where { loop { match cvt(f()) { - Err(ref e) if e.kind() == ErrorKind::Interrupted => {} + Err(ref e) if e.is_interrupted() => {} other => return other, } } diff --git a/library/std/src/sys/unix/net.rs b/library/std/src/sys/unix/net.rs index 7258c222a..f450d708d 100644 --- a/library/std/src/sys/unix/net.rs +++ b/library/std/src/sys/unix/net.rs @@ -75,6 +75,7 @@ impl Socket { target_os = "dragonfly", target_os = "freebsd", target_os = "illumos", + target_os = "hurd", target_os = "linux", target_os = "netbsd", target_os = "openbsd", @@ -102,7 +103,7 @@ impl Socket { } } - #[cfg(not(target_os = "vxworks"))] + #[cfg(not(any(target_os = "vxworks", target_os = "vita")))] pub fn new_pair(fam: c_int, ty: c_int) -> io::Result<(Socket, Socket)> { unsafe { let mut fds = [0, 0]; @@ -114,6 +115,7 @@ impl Socket { target_os = "freebsd", target_os = "illumos", target_os = "linux", + target_os = "hurd", target_os = "netbsd", target_os = "openbsd", target_os = "nto", @@ -133,7 +135,7 @@ impl Socket { } } - #[cfg(target_os = "vxworks")] + #[cfg(any(target_os = "vxworks", target_os = "vita"))] pub fn new_pair(_fam: c_int, _ty: c_int) -> io::Result<(Socket, Socket)> { unimplemented!() } @@ -184,7 +186,7 @@ impl Socket { match unsafe { libc::poll(&mut pollfd, 1, timeout) } { -1 => { let err = io::Error::last_os_error(); - if err.kind() != io::ErrorKind::Interrupted { + if !err.is_interrupted() { return Err(err); } } @@ -220,6 +222,7 @@ impl Socket { target_os = "freebsd", target_os = "illumos", target_os = "linux", + target_os = "hurd", target_os = "netbsd", target_os = "openbsd", ))] { diff --git a/library/std/src/sys/unix/os.rs b/library/std/src/sys/unix/os.rs index 57e1a36da..01ff375d2 100644 --- a/library/std/src/sys/unix/os.rs +++ b/library/std/src/sys/unix/os.rs @@ -46,7 +46,8 @@ extern "C" { target_os = "linux", target_os = "emscripten", target_os = "fuchsia", - target_os = "l4re" + target_os = "l4re", + target_os = "hurd", ), link_name = "__errno_location" )] @@ -121,7 +122,10 @@ pub fn set_errno(e: i32) { pub fn error_string(errno: i32) -> String { extern "C" { #[cfg_attr( - all(any(target_os = "linux", target_env = "newlib"), not(target_env = "ohos")), + all( + any(target_os = "linux", target_os = "hurd", target_env = "newlib"), + not(target_env = "ohos") + ), link_name = "__xpg_strerror_r" )] fn strerror_r(errnum: c_int, buf: *mut c_char, buflen: libc::size_t) -> c_int; @@ -359,7 +363,12 @@ pub fn current_exe() -> io::Result { } } -#[cfg(any(target_os = "linux", target_os = "android", target_os = "emscripten"))] +#[cfg(any( + target_os = "linux", + target_os = "hurd", + target_os = "android", + target_os = "emscripten" +))] pub fn current_exe() -> io::Result { match crate::fs::read_link("/proc/self/exe") { Err(ref e) if e.kind() == io::ErrorKind::NotFound => Err(io::const_io_error!( diff --git a/library/std/src/sys/unix/os_str.rs b/library/std/src/sys/unix/os_str.rs index 463b0a275..7bd2f656a 100644 --- a/library/std/src/sys/unix/os_str.rs +++ b/library/std/src/sys/unix/os_str.rs @@ -97,12 +97,12 @@ impl AsInner<[u8]> for Buf { impl Buf { #[inline] - pub fn into_os_str_bytes(self) -> Vec { + pub fn into_encoded_bytes(self) -> Vec { self.inner } #[inline] - pub unsafe fn from_os_str_bytes_unchecked(s: Vec) -> Self { + pub unsafe fn from_encoded_bytes_unchecked(s: Vec) -> Self { Self { inner: s } } @@ -203,18 +203,18 @@ impl Buf { impl Slice { #[inline] - pub fn as_os_str_bytes(&self) -> &[u8] { + pub fn as_encoded_bytes(&self) -> &[u8] { &self.inner } #[inline] - pub unsafe fn from_os_str_bytes_unchecked(s: &[u8]) -> &Slice { + pub unsafe fn from_encoded_bytes_unchecked(s: &[u8]) -> &Slice { unsafe { mem::transmute(s) } } #[inline] pub fn from_str(s: &str) -> &Slice { - unsafe { Slice::from_os_str_bytes_unchecked(s.as_bytes()) } + unsafe { Slice::from_encoded_bytes_unchecked(s.as_bytes()) } } pub fn to_str(&self) -> Result<&str, crate::str::Utf8Error> { diff --git a/library/std/src/sys/unix/os_str/tests.rs b/library/std/src/sys/unix/os_str/tests.rs index 91bc0e61a..e2a99045e 100644 --- a/library/std/src/sys/unix/os_str/tests.rs +++ b/library/std/src/sys/unix/os_str/tests.rs @@ -2,7 +2,7 @@ use super::*; #[test] fn slice_debug_output() { - let input = unsafe { Slice::from_os_str_bytes_unchecked(b"\xF0hello,\tworld") }; + let input = unsafe { Slice::from_encoded_bytes_unchecked(b"\xF0hello,\tworld") }; let expected = r#""\xF0hello,\tworld""#; let output = format!("{input:?}"); @@ -12,6 +12,6 @@ fn slice_debug_output() { #[test] fn display() { assert_eq!("Hello\u{FFFD}\u{FFFD} There\u{FFFD} Goodbye", unsafe { - Slice::from_os_str_bytes_unchecked(b"Hello\xC0\x80 There\xE6\x83 Goodbye").to_string() + Slice::from_encoded_bytes_unchecked(b"Hello\xC0\x80 There\xE6\x83 Goodbye").to_string() },); } diff --git a/library/std/src/sys/unix/path.rs b/library/std/src/sys/unix/path.rs index 935245f63..837f68d3e 100644 --- a/library/std/src/sys/unix/path.rs +++ b/library/std/src/sys/unix/path.rs @@ -30,7 +30,7 @@ pub(crate) fn absolute(path: &Path) -> io::Result { // Get the components, skipping the redundant leading "." component if it exists. let mut components = path.strip_prefix(".").unwrap_or(path).components(); - let path_os = path.as_os_str().as_os_str_bytes(); + let path_os = path.as_os_str().as_encoded_bytes(); let mut normalized = if path.is_absolute() { // "If a pathname begins with two successive characters, the diff --git a/library/std/src/sys/unix/pipe.rs b/library/std/src/sys/unix/pipe.rs index 938a46bfd..33db24e77 100644 --- a/library/std/src/sys/unix/pipe.rs +++ b/library/std/src/sys/unix/pipe.rs @@ -3,7 +3,7 @@ use crate::mem; use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; use crate::sys::fd::FileDesc; use crate::sys::{cvt, cvt_r}; -use crate::sys_common::IntoInner; +use crate::sys_common::{FromInner, IntoInner}; //////////////////////////////////////////////////////////////////////////////// // Anonymous pipes @@ -21,6 +21,7 @@ pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { if #[cfg(any( target_os = "dragonfly", target_os = "freebsd", + target_os = "hurd", target_os = "linux", target_os = "netbsd", target_os = "openbsd", @@ -158,3 +159,9 @@ impl FromRawFd for AnonPipe { Self(FromRawFd::from_raw_fd(raw_fd)) } } + +impl FromInner for AnonPipe { + fn from_inner(fd: FileDesc) -> Self { + Self(fd) + } +} diff --git a/library/std/src/sys/unix/process/process_common.rs b/library/std/src/sys/unix/process/process_common.rs index 640648e87..1ca11a7f9 100644 --- a/library/std/src/sys/unix/process/process_common.rs +++ b/library/std/src/sys/unix/process/process_common.rs @@ -13,7 +13,7 @@ use crate::sys::fd::FileDesc; use crate::sys::fs::File; use crate::sys::pipe::{self, AnonPipe}; use crate::sys_common::process::{CommandEnv, CommandEnvs}; -use crate::sys_common::IntoInner; +use crate::sys_common::{FromInner, IntoInner}; #[cfg(not(target_os = "fuchsia"))] use crate::sys::fs::OpenOptions; @@ -150,6 +150,7 @@ pub enum Stdio { Null, MakePipe, Fd(FileDesc), + StaticFd(BorrowedFd<'static>), } #[derive(Copy, Clone, Debug, Eq, PartialEq)] @@ -164,9 +165,9 @@ pub enum ProgramKind { impl ProgramKind { fn new(program: &OsStr) -> Self { - if program.as_os_str_bytes().starts_with(b"/") { + if program.as_encoded_bytes().starts_with(b"/") { Self::Absolute - } else if program.as_os_str_bytes().contains(&b'/') { + } else if program.as_encoded_bytes().contains(&b'/') { // If the program has more than one component in it, it is a relative path. Self::Relative } else { @@ -463,6 +464,11 @@ impl Stdio { } } + Stdio::StaticFd(fd) => { + let fd = FileDesc::from_inner(fd.try_clone_to_owned()?); + Ok((ChildStdio::Owned(fd), None)) + } + Stdio::MakePipe => { let (reader, writer) = pipe::anon_pipe()?; let (ours, theirs) = if readable { (writer, reader) } else { (reader, writer) }; @@ -497,6 +503,28 @@ impl From for Stdio { } } +impl From for Stdio { + fn from(_: io::Stdout) -> Stdio { + // This ought really to be is Stdio::StaticFd(input_argument.as_fd()). + // But AsFd::as_fd takes its argument by reference, and yields + // a bounded lifetime, so it's no use here. There is no AsStaticFd. + // + // Additionally AsFd is only implemented for the *locked* versions. + // We don't want to lock them here. (The implications of not locking + // are the same as those for process::Stdio::inherit().) + // + // Arguably the hypothetical AsStaticFd and AsFd<'static> + // should be implemented for io::Stdout, not just for StdoutLocked. + Stdio::StaticFd(unsafe { BorrowedFd::borrow_raw(libc::STDOUT_FILENO) }) + } +} + +impl From for Stdio { + fn from(_: io::Stderr) -> Stdio { + Stdio::StaticFd(unsafe { BorrowedFd::borrow_raw(libc::STDERR_FILENO) }) + } +} + impl ChildStdio { pub fn fd(&self) -> Option { match *self { @@ -558,6 +586,23 @@ impl fmt::Debug for Command { if let Some(ref cwd) = self.cwd { write!(f, "cd {cwd:?} && ")?; } + if self.env.does_clear() { + write!(f, "env -i ")?; + // Altered env vars will be printed next, that should exactly work as expected. + } else { + // Removed env vars need the command to be wrapped in `env`. + let mut any_removed = false; + for (key, value_opt) in self.get_envs() { + if value_opt.is_none() { + if !any_removed { + write!(f, "env ")?; + any_removed = true; + } + write!(f, "-u {} ", key.to_string_lossy())?; + } + } + } + // Altered env vars can just be added in front of the program. for (key, value_opt) in self.get_envs() { if let Some(value) = value_opt { write!(f, "{}={value:?} ", key.to_string_lossy())?; diff --git a/library/std/src/sys/unix/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs index 3963e7f52..564f8c482 100644 --- a/library/std/src/sys/unix/process/process_unix.rs +++ b/library/std/src/sys/unix/process/process_unix.rs @@ -165,7 +165,7 @@ impl Command { assert!(p.wait().is_ok(), "wait() should either return Ok or panic"); return Err(Error::from_raw_os_error(errno)); } - Err(ref e) if e.kind() == ErrorKind::Interrupted => {} + Err(ref e) if e.is_interrupted() => {} Err(e) => { assert!(p.wait().is_ok(), "wait() should either return Ok or panic"); panic!("the CLOEXEC pipe failed: {e:?}") @@ -374,6 +374,13 @@ impl Command { return Err(io::Error::last_os_error()); } } + #[cfg(target_os = "hurd")] + { + let ret = sys::signal(libc::SIGLOST, libc::SIG_DFL); + if ret == libc::SIG_ERR { + return Err(io::Error::last_os_error()); + } + } } } @@ -620,6 +627,10 @@ impl Command { let mut default_set = MaybeUninit::::uninit(); cvt(sigemptyset(default_set.as_mut_ptr()))?; cvt(sigaddset(default_set.as_mut_ptr(), libc::SIGPIPE))?; + #[cfg(target_os = "hurd")] + { + cvt(sigaddset(default_set.as_mut_ptr(), libc::SIGLOST))?; + } cvt_nz(libc::posix_spawnattr_setsigdefault( attrs.0.as_mut_ptr(), default_set.as_ptr(), @@ -993,6 +1004,8 @@ fn signal_string(signal: i32) -> &'static str { target_os = "dragonfly" ))] libc::SIGINFO => " (SIGINFO)", + #[cfg(target_os = "hurd")] + libc::SIGLOST => " (SIGLOST)", _ => "", } } diff --git a/library/std/src/sys/unix/stack_overflow.rs b/library/std/src/sys/unix/stack_overflow.rs index b59d4ba26..73c530786 100644 --- a/library/std/src/sys/unix/stack_overflow.rs +++ b/library/std/src/sys/unix/stack_overflow.rs @@ -32,6 +32,7 @@ impl Drop for Handler { target_os = "macos", target_os = "dragonfly", target_os = "freebsd", + target_os = "hurd", target_os = "solaris", target_os = "illumos", target_os = "netbsd", @@ -193,6 +194,7 @@ mod imp { target_os = "macos", target_os = "dragonfly", target_os = "freebsd", + target_os = "hurd", target_os = "solaris", target_os = "illumos", target_os = "netbsd", diff --git a/library/std/src/sys/unix/thread.rs b/library/std/src/sys/unix/thread.rs index 4f2d9cf36..311ed9502 100644 --- a/library/std/src/sys/unix/thread.rs +++ b/library/std/src/sys/unix/thread.rs @@ -182,6 +182,9 @@ impl Thread { } if let Some(f) = pthread_setname_np.get() { + #[cfg(target_os = "nto")] + let name = truncate_cstr::<{ libc::_NTO_THREAD_NAME_MAX as usize }>(name); + let res = unsafe { f(libc::pthread_self(), name.as_ptr()) }; debug_assert_eq!(res, 0); } @@ -213,7 +216,8 @@ impl Thread { target_os = "l4re", target_os = "emscripten", target_os = "redox", - target_os = "vxworks" + target_os = "vxworks", + target_os = "hurd", ))] pub fn set_name(_name: &CStr) { // Newlib, Emscripten, and VxWorks have no way to set a thread name. @@ -290,6 +294,7 @@ impl Drop for Thread { target_os = "ios", target_os = "tvos", target_os = "watchos", + target_os = "nto", ))] fn truncate_cstr(cstr: &CStr) -> [libc::c_char; MAX_WITH_NUL] { let mut result = [0; MAX_WITH_NUL]; @@ -305,6 +310,7 @@ pub fn available_parallelism() -> io::Result { target_os = "android", target_os = "emscripten", target_os = "fuchsia", + target_os = "hurd", target_os = "ios", target_os = "tvos", target_os = "linux", @@ -312,23 +318,38 @@ pub fn available_parallelism() -> io::Result { target_os = "solaris", target_os = "illumos", ))] { + #[allow(unused_assignments)] + #[allow(unused_mut)] + let mut quota = usize::MAX; + #[cfg(any(target_os = "android", target_os = "linux"))] { - let quota = cgroups::quota().max(1); + quota = cgroups::quota().max(1); let mut set: libc::cpu_set_t = unsafe { mem::zeroed() }; unsafe { if libc::sched_getaffinity(0, mem::size_of::(), &mut set) == 0 { let count = libc::CPU_COUNT(&set) as usize; let count = count.min(quota); - // SAFETY: affinity mask can't be empty and the quota gets clamped to a minimum of 1 - return Ok(NonZeroUsize::new_unchecked(count)); + + // According to sched_getaffinity's API it should always be non-zero, but + // some old MIPS kernels were buggy and zero-initialized the mask if + // none was explicitly set. + // In that case we use the sysconf fallback. + if let Some(count) = NonZeroUsize::new(count) { + return Ok(count) + } } } } match unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) } { -1 => Err(io::Error::last_os_error()), 0 => Err(io::const_io_error!(io::ErrorKind::NotFound, "The number of hardware threads is not known for the target platform")), - cpus => Ok(unsafe { NonZeroUsize::new_unchecked(cpus as usize) }), + cpus => { + let count = cpus as usize; + // Cover the unusual situation where we were able to get the quota but not the affinity mask + let count = count.min(quota); + Ok(unsafe { NonZeroUsize::new_unchecked(count) }) + } } } else if #[cfg(any(target_os = "freebsd", target_os = "dragonfly", target_os = "netbsd"))] { use crate::ptr; @@ -686,6 +707,7 @@ mod cgroups { #[cfg(all( not(target_os = "linux"), not(target_os = "freebsd"), + not(target_os = "hurd"), not(target_os = "macos"), not(target_os = "netbsd"), not(target_os = "openbsd"), @@ -706,6 +728,7 @@ pub mod guard { #[cfg(any( target_os = "linux", target_os = "freebsd", + target_os = "hurd", target_os = "macos", target_os = "netbsd", target_os = "openbsd", @@ -762,6 +785,7 @@ pub mod guard { #[cfg(any( target_os = "android", target_os = "freebsd", + target_os = "hurd", target_os = "linux", target_os = "netbsd", target_os = "l4re" @@ -899,6 +923,7 @@ pub mod guard { #[cfg(any( target_os = "android", target_os = "freebsd", + target_os = "hurd", target_os = "linux", target_os = "netbsd", target_os = "l4re" @@ -930,7 +955,7 @@ pub mod guard { assert_eq!(libc::pthread_attr_getstack(&attr, &mut stackptr, &mut size), 0); let stackaddr = stackptr.addr(); - ret = if cfg!(any(target_os = "freebsd", target_os = "netbsd")) { + ret = if cfg!(any(target_os = "freebsd", target_os = "netbsd", target_os = "hurd")) { Some(stackaddr - guardsize..stackaddr) } else if cfg!(all(target_os = "linux", target_env = "musl")) { Some(stackaddr - guardsize..stackaddr) diff --git a/library/std/src/sys/unix/thread_local_dtor.rs b/library/std/src/sys/unix/thread_local_dtor.rs index 236d2f2ee..fba2a676f 100644 --- a/library/std/src/sys/unix/thread_local_dtor.rs +++ b/library/std/src/sys/unix/thread_local_dtor.rs @@ -11,7 +11,7 @@ // Note, however, that we run on lots older linuxes, as well as cross // compiling from a newer linux to an older linux, so we also have a // fallback implementation to use as well. -#[cfg(any(target_os = "linux", target_os = "fuchsia", target_os = "redox"))] +#[cfg(any(target_os = "linux", target_os = "fuchsia", target_os = "redox", target_os = "hurd"))] pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { use crate::mem; use crate::sys_common::thread_local_dtor::register_dtor_fallback; @@ -48,7 +48,7 @@ pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { // workaround below is to register, via _tlv_atexit, a custom DTOR list once per // thread. thread_local dtors are pushed to the DTOR list without calling // _tlv_atexit. -#[cfg(target_os = "macos")] +#[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))] pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { use crate::cell::Cell; use crate::mem; diff --git a/library/std/src/sys/unix/thread_parking/darwin.rs b/library/std/src/sys/unix/thread_parking/darwin.rs index b709fada3..8231f3cba 100644 --- a/library/std/src/sys/unix/thread_parking/darwin.rs +++ b/library/std/src/sys/unix/thread_parking/darwin.rs @@ -2,8 +2,7 @@ //! //! Darwin actually has futex syscalls (`__ulock_wait`/`__ulock_wake`), but they //! cannot be used in `std` because they are non-public (their use will lead to -//! rejection from the App Store) and because they are only available starting -//! with macOS version 10.12, even though the minimum target version is 10.7. +//! rejection from the App Store). //! //! Therefore, we need to look for other synchronization primitives. Luckily, Darwin //! supports semaphores, which allow us to implement the behaviour we need with diff --git a/library/std/src/sys/unix/time.rs b/library/std/src/sys/unix/time.rs index 17b4130c2..4fe61b284 100644 --- a/library/std/src/sys/unix/time.rs +++ b/library/std/src/sys/unix/time.rs @@ -35,7 +35,7 @@ pub(in crate::sys::unix) struct Timespec { } impl SystemTime { - #[cfg_attr(target_os = "horizon", allow(unused))] + #[cfg_attr(any(target_os = "horizon", target_os = "hurd"), allow(unused))] pub fn new(tv_sec: i64, tv_nsec: i64) -> SystemTime { SystemTime { t: Timespec::new(tv_sec, tv_nsec) } } diff --git a/library/std/src/sys/unsupported/common.rs b/library/std/src/sys/unsupported/common.rs index 5cd9e57de..5c379992b 100644 --- a/library/std/src/sys/unsupported/common.rs +++ b/library/std/src/sys/unsupported/common.rs @@ -23,6 +23,10 @@ pub fn unsupported_err() -> std_io::Error { ) } +pub fn is_interrupted(_code: i32) -> bool { + false +} + pub fn decode_error_kind(_code: i32) -> crate::io::ErrorKind { crate::io::ErrorKind::Uncategorized } diff --git a/library/std/src/sys/unsupported/process.rs b/library/std/src/sys/unsupported/process.rs index 77b675aaa..a639afcc6 100644 --- a/library/std/src/sys/unsupported/process.rs +++ b/library/std/src/sys/unsupported/process.rs @@ -27,6 +27,8 @@ pub struct StdioPipes { pub stderr: Option, } +// FIXME: This should be a unit struct, so we can always construct it +// The value here should be never used, since we cannot spawn processes. pub enum Stdio { Inherit, Null, @@ -87,8 +89,26 @@ impl From for Stdio { } } +impl From for Stdio { + fn from(_: io::Stdout) -> Stdio { + // FIXME: This is wrong. + // Instead, the Stdio we have here should be a unit struct. + panic!("unsupported") + } +} + +impl From for Stdio { + fn from(_: io::Stderr) -> Stdio { + // FIXME: This is wrong. + // Instead, the Stdio we have here should be a unit struct. + panic!("unsupported") + } +} + impl From for Stdio { fn from(_file: File) -> Stdio { + // FIXME: This is wrong. + // Instead, the Stdio we have here should be a unit struct. panic!("unsupported") } } diff --git a/library/std/src/sys/wasi/mod.rs b/library/std/src/sys/wasi/mod.rs index 98517da1d..5cbb5cb65 100644 --- a/library/std/src/sys/wasi/mod.rs +++ b/library/std/src/sys/wasi/mod.rs @@ -76,6 +76,11 @@ cfg_if::cfg_if! { mod common; pub use common::*; +#[inline] +pub fn is_interrupted(errno: i32) -> bool { + errno == wasi::ERRNO_INTR.raw().into() +} + pub fn decode_error_kind(errno: i32) -> std_io::ErrorKind { use std_io::ErrorKind::*; if errno > u16::MAX as i32 || errno < 0 { diff --git a/library/std/src/sys/windows/args.rs b/library/std/src/sys/windows/args.rs index 6b597f499..ee7dba6e5 100644 --- a/library/std/src/sys/windows/args.rs +++ b/library/std/src/sys/windows/args.rs @@ -226,7 +226,7 @@ pub(crate) fn append_arg(cmd: &mut Vec, arg: &Arg, force_quotes: bool) -> i // that it actually gets passed through on the command line or otherwise // it will be dropped entirely when parsed on the other end. ensure_no_nuls(arg)?; - let arg_bytes = arg.as_os_str_bytes(); + let arg_bytes = arg.as_encoded_bytes(); let (quote, escape) = match quote { Quote::Always => (true, true), Quote::Auto => { @@ -298,7 +298,7 @@ pub(crate) fn make_bat_command_line( const SPECIAL: &[u8] = b"\t &()[]{}^=;!'+,`~%|<>"; let force_quotes = match arg { Arg::Regular(arg) if !force_quotes => { - arg.as_os_str_bytes().iter().any(|c| SPECIAL.contains(c)) + arg.as_encoded_bytes().iter().any(|c| SPECIAL.contains(c)) } _ => force_quotes, }; diff --git a/library/std/src/sys/windows/c.rs b/library/std/src/sys/windows/c.rs index d9ccba0e9..f3637cbb9 100644 --- a/library/std/src/sys/windows/c.rs +++ b/library/std/src/sys/windows/c.rs @@ -46,10 +46,6 @@ pub use FD_SET as fd_set; pub use LINGER as linger; pub use TIMEVAL as timeval; -pub type CONDITION_VARIABLE = RTL_CONDITION_VARIABLE; -pub type SRWLOCK = RTL_SRWLOCK; -pub type INIT_ONCE = RTL_RUN_ONCE; - pub const CONDITION_VARIABLE_INIT: CONDITION_VARIABLE = CONDITION_VARIABLE { Ptr: ptr::null_mut() }; pub const SRWLOCK_INIT: SRWLOCK = SRWLOCK { Ptr: ptr::null_mut() }; pub const INIT_ONCE_STATIC_INIT: INIT_ONCE = INIT_ONCE { Ptr: ptr::null_mut() }; @@ -224,7 +220,7 @@ pub unsafe extern "system" fn ReadFileEx( ) -> BOOL { windows_sys::ReadFileEx( hFile.as_raw_handle(), - lpBuffer, + lpBuffer.cast::(), nNumberOfBytesToRead, lpOverlapped, lpCompletionRoutine, diff --git a/library/std/src/sys/windows/c/windows_sys.lst b/library/std/src/sys/windows/c/windows_sys.lst index 631aedd26..0aca37e2d 100644 --- a/library/std/src/sys/windows/c/windows_sys.lst +++ b/library/std/src/sys/windows/c/windows_sys.lst @@ -1,3 +1,6 @@ +--out windows_sys.rs +--config flatten std +--filter // tidy-alphabetical-start Windows.Wdk.Storage.FileSystem.FILE_COMPLETE_IF_OPLOCKED Windows.Wdk.Storage.FileSystem.FILE_CONTAINS_EXTENDED_CREATE_INFORMATION @@ -2108,7 +2111,6 @@ Windows.Win32.Networking.WinSock.WSABASEERR Windows.Win32.Networking.WinSock.WSABUF Windows.Win32.Networking.WinSock.WSACleanup Windows.Win32.Networking.WinSock.WSADATA -Windows.Win32.Networking.WinSock.WSADATA Windows.Win32.Networking.WinSock.WSADuplicateSocketW Windows.Win32.Networking.WinSock.WSAEACCES Windows.Win32.Networking.WinSock.WSAEADDRINUSE @@ -2328,7 +2330,6 @@ Windows.Win32.Storage.FileSystem.FileStandardInfo Windows.Win32.Storage.FileSystem.FileStorageInfo Windows.Win32.Storage.FileSystem.FileStreamInfo Windows.Win32.Storage.FileSystem.FindClose -Windows.Win32.Storage.FileSystem.FindFileHandle Windows.Win32.Storage.FileSystem.FindFirstFileW Windows.Win32.Storage.FileSystem.FindNextFileW Windows.Win32.Storage.FileSystem.FlushFileBuffers @@ -2420,8 +2421,6 @@ Windows.Win32.System.Console.STD_OUTPUT_HANDLE Windows.Win32.System.Console.WriteConsoleW Windows.Win32.System.Diagnostics.Debug.ARM64_NT_NEON128 Windows.Win32.System.Diagnostics.Debug.CONTEXT -Windows.Win32.System.Diagnostics.Debug.CONTEXT -Windows.Win32.System.Diagnostics.Debug.CONTEXT Windows.Win32.System.Diagnostics.Debug.EXCEPTION_RECORD Windows.Win32.System.Diagnostics.Debug.FACILITY_CODE Windows.Win32.System.Diagnostics.Debug.FACILITY_NT_BIT @@ -2435,7 +2434,6 @@ Windows.Win32.System.Diagnostics.Debug.FORMAT_MESSAGE_OPTIONS Windows.Win32.System.Diagnostics.Debug.FormatMessageW Windows.Win32.System.Diagnostics.Debug.M128A Windows.Win32.System.Diagnostics.Debug.XSAVE_FORMAT -Windows.Win32.System.Diagnostics.Debug.XSAVE_FORMAT Windows.Win32.System.Environment.FreeEnvironmentStringsW Windows.Win32.System.Environment.GetCommandLineW Windows.Win32.System.Environment.GetCurrentDirectoryW @@ -2456,7 +2454,6 @@ Windows.Win32.System.Kernel.ExceptionContinueExecution Windows.Win32.System.Kernel.ExceptionContinueSearch Windows.Win32.System.Kernel.ExceptionNestedException Windows.Win32.System.Kernel.FLOATING_SAVE_AREA -Windows.Win32.System.Kernel.FLOATING_SAVE_AREA Windows.Win32.System.Kernel.OBJ_DONT_REPARSE Windows.Win32.System.LibraryLoader.GetModuleFileNameW Windows.Win32.System.LibraryLoader.GetModuleHandleA @@ -2482,6 +2479,7 @@ Windows.Win32.System.SystemInformation.GetSystemTimeAsFileTime Windows.Win32.System.SystemInformation.GetWindowsDirectoryW Windows.Win32.System.SystemInformation.PROCESSOR_ARCHITECTURE Windows.Win32.System.SystemInformation.SYSTEM_INFO +Windows.Win32.System.SystemServices.ALL_PROCESSOR_GROUPS Windows.Win32.System.SystemServices.DLL_PROCESS_DETACH Windows.Win32.System.SystemServices.DLL_THREAD_DETACH Windows.Win32.System.SystemServices.EXCEPTION_MAXIMUM_PARAMETERS @@ -2510,9 +2508,11 @@ Windows.Win32.System.Threading.CreateProcessW Windows.Win32.System.Threading.CreateThread Windows.Win32.System.Threading.DEBUG_ONLY_THIS_PROCESS Windows.Win32.System.Threading.DEBUG_PROCESS +Windows.Win32.System.Threading.DeleteProcThreadAttributeList Windows.Win32.System.Threading.DETACHED_PROCESS Windows.Win32.System.Threading.ExitProcess Windows.Win32.System.Threading.EXTENDED_STARTUPINFO_PRESENT +Windows.Win32.System.Threading.GetActiveProcessorCount Windows.Win32.System.Threading.GetCurrentProcess Windows.Win32.System.Threading.GetCurrentProcessId Windows.Win32.System.Threading.GetCurrentThread @@ -2524,8 +2524,10 @@ Windows.Win32.System.Threading.INFINITE Windows.Win32.System.Threading.INHERIT_CALLER_PRIORITY Windows.Win32.System.Threading.INHERIT_PARENT_AFFINITY Windows.Win32.System.Threading.INIT_ONCE_INIT_FAILED +Windows.Win32.System.Threading.InitializeProcThreadAttributeList Windows.Win32.System.Threading.InitOnceBeginInitialize Windows.Win32.System.Threading.InitOnceComplete +Windows.Win32.System.Threading.LPPROC_THREAD_ATTRIBUTE_LIST Windows.Win32.System.Threading.LPTHREAD_START_ROUTINE Windows.Win32.System.Threading.NORMAL_PRIORITY_CLASS Windows.Win32.System.Threading.OpenProcessToken @@ -2539,9 +2541,6 @@ Windows.Win32.System.Threading.PROFILE_USER Windows.Win32.System.Threading.REALTIME_PRIORITY_CLASS Windows.Win32.System.Threading.ReleaseSRWLockExclusive Windows.Win32.System.Threading.ReleaseSRWLockShared -Windows.Win32.System.Threading.RTL_CONDITION_VARIABLE -Windows.Win32.System.Threading.RTL_RUN_ONCE -Windows.Win32.System.Threading.RTL_SRWLOCK Windows.Win32.System.Threading.SetThreadStackGuarantee Windows.Win32.System.Threading.Sleep Windows.Win32.System.Threading.SleepConditionVariableSRW @@ -2561,6 +2560,7 @@ Windows.Win32.System.Threading.STARTF_USEPOSITION Windows.Win32.System.Threading.STARTF_USESHOWWINDOW Windows.Win32.System.Threading.STARTF_USESIZE Windows.Win32.System.Threading.STARTF_USESTDHANDLES +Windows.Win32.System.Threading.STARTUPINFOEXW Windows.Win32.System.Threading.STARTUPINFOW Windows.Win32.System.Threading.STARTUPINFOW_FLAGS Windows.Win32.System.Threading.SwitchToThread @@ -2575,12 +2575,11 @@ Windows.Win32.System.Threading.TlsGetValue Windows.Win32.System.Threading.TlsSetValue Windows.Win32.System.Threading.TryAcquireSRWLockExclusive Windows.Win32.System.Threading.TryAcquireSRWLockShared +Windows.Win32.System.Threading.UpdateProcThreadAttribute Windows.Win32.System.Threading.WaitForMultipleObjects Windows.Win32.System.Threading.WaitForSingleObject Windows.Win32.System.Threading.WakeAllConditionVariable Windows.Win32.System.Threading.WakeConditionVariable -Windows.Win32.System.WindowsProgramming.IO_STATUS_BLOCK -Windows.Win32.System.WindowsProgramming.OBJECT_ATTRIBUTES Windows.Win32.System.WindowsProgramming.PROGRESS_CONTINUE Windows.Win32.UI.Shell.GetUserProfileDirectoryW // tidy-alphabetical-end diff --git a/library/std/src/sys/windows/c/windows_sys.rs b/library/std/src/sys/windows/c/windows_sys.rs index 023770871..851d15915 100644 --- a/library/std/src/sys/windows/c/windows_sys.rs +++ b/library/std/src/sys/windows/c/windows_sys.rs @@ -4,7 +4,7 @@ // regenerate the bindings. // // ignore-tidy-filelength -// Bindings generated by `windows-bindgen` 0.49.0 +// Bindings generated by `windows-bindgen` 0.51.1 #![allow(non_snake_case, non_upper_case_globals, non_camel_case_types, dead_code, clippy::all)] #[link(name = "advapi32")] @@ -32,11 +32,11 @@ extern "system" { } #[link(name = "kernel32")] extern "system" { - pub fn AcquireSRWLockExclusive(srwlock: *mut RTL_SRWLOCK) -> (); + pub fn AcquireSRWLockExclusive(srwlock: *mut SRWLOCK) -> (); } #[link(name = "kernel32")] extern "system" { - pub fn AcquireSRWLockShared(srwlock: *mut RTL_SRWLOCK) -> (); + pub fn AcquireSRWLockShared(srwlock: *mut SRWLOCK) -> (); } #[link(name = "kernel32")] extern "system" { @@ -155,6 +155,10 @@ extern "system" { pub fn DeleteFileW(lpfilename: PCWSTR) -> BOOL; } #[link(name = "kernel32")] +extern "system" { + pub fn DeleteProcThreadAttributeList(lpattributelist: LPPROC_THREAD_ATTRIBUTE_LIST) -> (); +} +#[link(name = "kernel32")] extern "system" { pub fn DeviceIoControl( hdevice: HANDLE, @@ -185,18 +189,15 @@ extern "system" { } #[link(name = "kernel32")] extern "system" { - pub fn FindClose(hfindfile: FindFileHandle) -> BOOL; + pub fn FindClose(hfindfile: HANDLE) -> BOOL; } #[link(name = "kernel32")] extern "system" { - pub fn FindFirstFileW( - lpfilename: PCWSTR, - lpfindfiledata: *mut WIN32_FIND_DATAW, - ) -> FindFileHandle; + pub fn FindFirstFileW(lpfilename: PCWSTR, lpfindfiledata: *mut WIN32_FIND_DATAW) -> HANDLE; } #[link(name = "kernel32")] extern "system" { - pub fn FindNextFileW(hfindfile: FindFileHandle, lpfindfiledata: *mut WIN32_FIND_DATAW) -> BOOL; + pub fn FindNextFileW(hfindfile: HANDLE, lpfindfiledata: *mut WIN32_FIND_DATAW) -> BOOL; } #[link(name = "kernel32")] extern "system" { @@ -219,6 +220,10 @@ extern "system" { pub fn FreeEnvironmentStringsW(penv: PCWSTR) -> BOOL; } #[link(name = "kernel32")] +extern "system" { + pub fn GetActiveProcessorCount(groupnumber: u16) -> u32; +} +#[link(name = "kernel32")] extern "system" { pub fn GetCommandLineW() -> PCWSTR; } @@ -356,7 +361,7 @@ extern "system" { #[link(name = "kernel32")] extern "system" { pub fn InitOnceBeginInitialize( - lpinitonce: *mut RTL_RUN_ONCE, + lpinitonce: *mut INIT_ONCE, dwflags: u32, fpending: *mut BOOL, lpcontext: *mut *mut ::core::ffi::c_void, @@ -365,12 +370,21 @@ extern "system" { #[link(name = "kernel32")] extern "system" { pub fn InitOnceComplete( - lpinitonce: *mut RTL_RUN_ONCE, + lpinitonce: *mut INIT_ONCE, dwflags: u32, lpcontext: *const ::core::ffi::c_void, ) -> BOOL; } #[link(name = "kernel32")] +extern "system" { + pub fn InitializeProcThreadAttributeList( + lpattributelist: LPPROC_THREAD_ATTRIBUTE_LIST, + dwattributecount: u32, + dwflags: u32, + lpsize: *mut usize, + ) -> BOOL; +} +#[link(name = "kernel32")] extern "system" { pub fn MoveFileExW( lpexistingfilename: PCWSTR, @@ -411,7 +425,7 @@ extern "system" { extern "system" { pub fn ReadFile( hfile: HANDLE, - lpbuffer: *mut ::core::ffi::c_void, + lpbuffer: *mut u8, nnumberofbytestoread: u32, lpnumberofbytesread: *mut u32, lpoverlapped: *mut OVERLAPPED, @@ -421,7 +435,7 @@ extern "system" { extern "system" { pub fn ReadFileEx( hfile: HANDLE, - lpbuffer: *mut ::core::ffi::c_void, + lpbuffer: *mut u8, nnumberofbytestoread: u32, lpoverlapped: *mut OVERLAPPED, lpcompletionroutine: LPOVERLAPPED_COMPLETION_ROUTINE, @@ -429,11 +443,11 @@ extern "system" { } #[link(name = "kernel32")] extern "system" { - pub fn ReleaseSRWLockExclusive(srwlock: *mut RTL_SRWLOCK) -> (); + pub fn ReleaseSRWLockExclusive(srwlock: *mut SRWLOCK) -> (); } #[link(name = "kernel32")] extern "system" { - pub fn ReleaseSRWLockShared(srwlock: *mut RTL_SRWLOCK) -> (); + pub fn ReleaseSRWLockShared(srwlock: *mut SRWLOCK) -> (); } #[link(name = "kernel32")] extern "system" { @@ -500,8 +514,8 @@ extern "system" { #[link(name = "kernel32")] extern "system" { pub fn SleepConditionVariableSRW( - conditionvariable: *mut RTL_CONDITION_VARIABLE, - srwlock: *mut RTL_SRWLOCK, + conditionvariable: *mut CONDITION_VARIABLE, + srwlock: *mut SRWLOCK, dwmilliseconds: u32, flags: u32, ) -> BOOL; @@ -536,11 +550,23 @@ extern "system" { } #[link(name = "kernel32")] extern "system" { - pub fn TryAcquireSRWLockExclusive(srwlock: *mut RTL_SRWLOCK) -> BOOLEAN; + pub fn TryAcquireSRWLockExclusive(srwlock: *mut SRWLOCK) -> BOOLEAN; +} +#[link(name = "kernel32")] +extern "system" { + pub fn TryAcquireSRWLockShared(srwlock: *mut SRWLOCK) -> BOOLEAN; } #[link(name = "kernel32")] extern "system" { - pub fn TryAcquireSRWLockShared(srwlock: *mut RTL_SRWLOCK) -> BOOLEAN; + pub fn UpdateProcThreadAttribute( + lpattributelist: LPPROC_THREAD_ATTRIBUTE_LIST, + dwflags: u32, + attribute: usize, + lpvalue: *const ::core::ffi::c_void, + cbsize: usize, + lppreviousvalue: *mut ::core::ffi::c_void, + lpreturnsize: *const usize, + ) -> BOOL; } #[link(name = "kernel32")] extern "system" { @@ -549,19 +575,19 @@ extern "system" { lphandles: *const HANDLE, bwaitall: BOOL, dwmilliseconds: u32, - ) -> WIN32_ERROR; + ) -> WAIT_EVENT; } #[link(name = "kernel32")] extern "system" { - pub fn WaitForSingleObject(hhandle: HANDLE, dwmilliseconds: u32) -> WIN32_ERROR; + pub fn WaitForSingleObject(hhandle: HANDLE, dwmilliseconds: u32) -> WAIT_EVENT; } #[link(name = "kernel32")] extern "system" { - pub fn WakeAllConditionVariable(conditionvariable: *mut RTL_CONDITION_VARIABLE) -> (); + pub fn WakeAllConditionVariable(conditionvariable: *mut CONDITION_VARIABLE) -> (); } #[link(name = "kernel32")] extern "system" { - pub fn WakeConditionVariable(conditionvariable: *mut RTL_CONDITION_VARIABLE) -> (); + pub fn WakeConditionVariable(conditionvariable: *mut CONDITION_VARIABLE) -> (); } #[link(name = "kernel32")] extern "system" { @@ -822,6 +848,7 @@ impl ::core::clone::Clone for ADDRINFOA { pub const AF_INET: ADDRESS_FAMILY = 2u16; pub const AF_INET6: ADDRESS_FAMILY = 23u16; pub const AF_UNSPEC: ADDRESS_FAMILY = 0u16; +pub const ALL_PROCESSOR_GROUPS: u32 = 65535u32; #[repr(C)] pub union ARM64_NT_NEON128 { pub Anonymous: ARM64_NT_NEON128_0, @@ -874,7 +901,17 @@ impl ::core::clone::Clone for BY_HANDLE_FILE_INFORMATION { } pub const CALLBACK_CHUNK_FINISHED: LPPROGRESS_ROUTINE_CALLBACK_REASON = 0u32; pub const CALLBACK_STREAM_SWITCH: LPPROGRESS_ROUTINE_CALLBACK_REASON = 1u32; -pub type COMPARESTRING_RESULT = u32; +pub type COMPARESTRING_RESULT = i32; +#[repr(C)] +pub struct CONDITION_VARIABLE { + pub Ptr: *mut ::core::ffi::c_void, +} +impl ::core::marker::Copy for CONDITION_VARIABLE {} +impl ::core::clone::Clone for CONDITION_VARIABLE { + fn clone(&self) -> Self { + *self + } +} pub type CONSOLE_MODE = u32; #[repr(C)] pub struct CONSOLE_READCONSOLE_CONTROL { @@ -892,7 +929,7 @@ impl ::core::clone::Clone for CONSOLE_READCONSOLE_CONTROL { #[repr(C)] #[cfg(target_arch = "aarch64")] pub struct CONTEXT { - pub ContextFlags: u32, + pub ContextFlags: CONTEXT_FLAGS, pub Cpsr: u32, pub Anonymous: CONTEXT_0, pub Sp: u64, @@ -979,7 +1016,7 @@ pub struct CONTEXT { pub P4Home: u64, pub P5Home: u64, pub P6Home: u64, - pub ContextFlags: u32, + pub ContextFlags: CONTEXT_FLAGS, pub MxCsr: u32, pub SegCs: u16, pub SegDs: u16, @@ -1075,7 +1112,7 @@ impl ::core::clone::Clone for CONTEXT_0_0 { #[repr(C)] #[cfg(target_arch = "x86")] pub struct CONTEXT { - pub ContextFlags: u32, + pub ContextFlags: CONTEXT_FLAGS, pub Dr0: u32, pub Dr1: u32, pub Dr2: u32, @@ -1109,6 +1146,7 @@ impl ::core::clone::Clone for CONTEXT { *self } } +pub type CONTEXT_FLAGS = u32; pub const CP_UTF8: u32 = 65001u32; pub const CREATE_ALWAYS: FILE_CREATION_DISPOSITION = 2u32; pub const CREATE_BREAKAWAY_FROM_JOB: PROCESS_CREATION_FLAGS = 16777216u32; @@ -1126,9 +1164,9 @@ pub const CREATE_SEPARATE_WOW_VDM: PROCESS_CREATION_FLAGS = 2048u32; pub const CREATE_SHARED_WOW_VDM: PROCESS_CREATION_FLAGS = 4096u32; pub const CREATE_SUSPENDED: PROCESS_CREATION_FLAGS = 4u32; pub const CREATE_UNICODE_ENVIRONMENT: PROCESS_CREATION_FLAGS = 1024u32; -pub const CSTR_EQUAL: COMPARESTRING_RESULT = 2u32; -pub const CSTR_GREATER_THAN: COMPARESTRING_RESULT = 3u32; -pub const CSTR_LESS_THAN: COMPARESTRING_RESULT = 1u32; +pub const CSTR_EQUAL: COMPARESTRING_RESULT = 2i32; +pub const CSTR_GREATER_THAN: COMPARESTRING_RESULT = 3i32; +pub const CSTR_LESS_THAN: COMPARESTRING_RESULT = 1i32; pub const DEBUG_ONLY_THIS_PROCESS: PROCESS_CREATION_FLAGS = 2u32; pub const DEBUG_PROCESS: PROCESS_CREATION_FLAGS = 1u32; pub const DELETE: FILE_ACCESS_RIGHTS = 65536u32; @@ -3344,7 +3382,6 @@ pub const FileRenameInfoEx: FILE_INFO_BY_HANDLE_CLASS = 22i32; pub const FileStandardInfo: FILE_INFO_BY_HANDLE_CLASS = 1i32; pub const FileStorageInfo: FILE_INFO_BY_HANDLE_CLASS = 16i32; pub const FileStreamInfo: FILE_INFO_BY_HANDLE_CLASS = 7i32; -pub type FindFileHandle = *mut ::core::ffi::c_void; pub type GENERIC_ACCESS_RIGHTS = u32; pub const GENERIC_ALL: GENERIC_ACCESS_RIGHTS = 268435456u32; pub const GENERIC_EXECUTE: GENERIC_ACCESS_RIGHTS = 536870912u32; @@ -3358,6 +3395,12 @@ pub struct GUID { pub data3: u16, pub data4: [u8; 8], } +impl ::core::marker::Copy for GUID {} +impl ::core::clone::Clone for GUID { + fn clone(&self) -> Self { + *self + } +} impl GUID { pub const fn from_u128(uuid: u128) -> Self { Self { @@ -3368,12 +3411,6 @@ impl GUID { } } } -impl ::core::marker::Copy for GUID {} -impl ::core::clone::Clone for GUID { - fn clone(&self) -> Self { - *self - } -} pub type HANDLE = *mut ::core::ffi::c_void; pub type HANDLE_FLAGS = u32; pub const HANDLE_FLAG_INHERIT: HANDLE_FLAGS = 1u32; @@ -3406,6 +3443,16 @@ impl ::core::clone::Clone for IN6_ADDR_0 { pub const INFINITE: u32 = 4294967295u32; pub const INHERIT_CALLER_PRIORITY: PROCESS_CREATION_FLAGS = 131072u32; pub const INHERIT_PARENT_AFFINITY: PROCESS_CREATION_FLAGS = 65536u32; +#[repr(C)] +pub union INIT_ONCE { + pub Ptr: *mut ::core::ffi::c_void, +} +impl ::core::marker::Copy for INIT_ONCE {} +impl ::core::clone::Clone for INIT_ONCE { + fn clone(&self) -> Self { + *self + } +} pub const INIT_ONCE_INIT_FAILED: u32 = 4u32; pub const INVALID_FILE_ATTRIBUTES: u32 = 4294967295u32; pub const INVALID_HANDLE_VALUE: HANDLE = ::core::ptr::invalid_mut(-1i32 as _); @@ -3567,6 +3614,7 @@ pub type LPOVERLAPPED_COMPLETION_ROUTINE = ::core::option::Option< lpoverlapped: *mut OVERLAPPED, ) -> (), >; +pub type LPPROC_THREAD_ATTRIBUTE_LIST = *mut ::core::ffi::c_void; pub type LPPROGRESS_ROUTINE = ::core::option::Option< unsafe extern "system" fn( totalfilesize: i64, @@ -3633,10 +3681,10 @@ pub type NTSTATUS = i32; pub struct OBJECT_ATTRIBUTES { pub Length: u32, pub RootDirectory: HANDLE, - pub ObjectName: *mut UNICODE_STRING, + pub ObjectName: *const UNICODE_STRING, pub Attributes: u32, - pub SecurityDescriptor: *mut ::core::ffi::c_void, - pub SecurityQualityOfService: *mut ::core::ffi::c_void, + pub SecurityDescriptor: *const ::core::ffi::c_void, + pub SecurityQualityOfService: *const ::core::ffi::c_void, } impl ::core::marker::Copy for OBJECT_ATTRIBUTES {} impl ::core::clone::Clone for OBJECT_ATTRIBUTES { @@ -3686,8 +3734,8 @@ pub type PCSTR = *const u8; pub type PCWSTR = *const u16; pub type PIO_APC_ROUTINE = ::core::option::Option< unsafe extern "system" fn( - apccontext: *const ::core::ffi::c_void, - iostatusblock: *const IO_STATUS_BLOCK, + apccontext: *mut ::core::ffi::c_void, + iostatusblock: *mut IO_STATUS_BLOCK, reserved: u32, ) -> (), >; @@ -3729,36 +3777,6 @@ pub type PSTR = *mut u8; pub type PWSTR = *mut u16; pub const READ_CONTROL: FILE_ACCESS_RIGHTS = 131072u32; pub const REALTIME_PRIORITY_CLASS: PROCESS_CREATION_FLAGS = 256u32; -#[repr(C)] -pub struct RTL_CONDITION_VARIABLE { - pub Ptr: *mut ::core::ffi::c_void, -} -impl ::core::marker::Copy for RTL_CONDITION_VARIABLE {} -impl ::core::clone::Clone for RTL_CONDITION_VARIABLE { - fn clone(&self) -> Self { - *self - } -} -#[repr(C)] -pub union RTL_RUN_ONCE { - pub Ptr: *mut ::core::ffi::c_void, -} -impl ::core::marker::Copy for RTL_RUN_ONCE {} -impl ::core::clone::Clone for RTL_RUN_ONCE { - fn clone(&self) -> Self { - *self - } -} -#[repr(C)] -pub struct RTL_SRWLOCK { - pub Ptr: *mut ::core::ffi::c_void, -} -impl ::core::marker::Copy for RTL_SRWLOCK {} -impl ::core::clone::Clone for RTL_SRWLOCK { - fn clone(&self) -> Self { - *self - } -} pub const SD_BOTH: WINSOCK_SHUTDOWN_HOW = 2i32; pub const SD_RECEIVE: WINSOCK_SHUTDOWN_HOW = 0i32; pub const SD_SEND: WINSOCK_SHUTDOWN_HOW = 1i32; @@ -3795,10 +3813,7 @@ impl ::core::clone::Clone for SOCKADDR { *self } } -#[cfg(target_pointer_width = "32")] -pub type SOCKET = u32; -#[cfg(target_pointer_width = "64")] -pub type SOCKET = u64; +pub type SOCKET = usize; pub const SOCKET_ERROR: i32 = -1i32; pub const SOCK_DGRAM: WINSOCK_SOCKET_TYPE = 2i32; pub const SOCK_RAW: WINSOCK_SOCKET_TYPE = 3i32; @@ -3812,6 +3827,16 @@ pub const SO_LINGER: i32 = 128i32; pub const SO_RCVTIMEO: i32 = 4102i32; pub const SO_SNDTIMEO: i32 = 4101i32; pub const SPECIFIC_RIGHTS_ALL: FILE_ACCESS_RIGHTS = 65535u32; +#[repr(C)] +pub struct SRWLOCK { + pub Ptr: *mut ::core::ffi::c_void, +} +impl ::core::marker::Copy for SRWLOCK {} +impl ::core::clone::Clone for SRWLOCK { + fn clone(&self) -> Self { + *self + } +} pub const STACK_SIZE_PARAM_IS_A_RESERVATION: THREAD_CREATION_FLAGS = 65536u32; pub const STANDARD_RIGHTS_ALL: FILE_ACCESS_RIGHTS = 2031616u32; pub const STANDARD_RIGHTS_EXECUTE: FILE_ACCESS_RIGHTS = 131072u32; @@ -3833,6 +3858,17 @@ pub const STARTF_USESHOWWINDOW: STARTUPINFOW_FLAGS = 1u32; pub const STARTF_USESIZE: STARTUPINFOW_FLAGS = 2u32; pub const STARTF_USESTDHANDLES: STARTUPINFOW_FLAGS = 256u32; #[repr(C)] +pub struct STARTUPINFOEXW { + pub StartupInfo: STARTUPINFOW, + pub lpAttributeList: LPPROC_THREAD_ATTRIBUTE_LIST, +} +impl ::core::marker::Copy for STARTUPINFOEXW {} +impl ::core::clone::Clone for STARTUPINFOEXW { + fn clone(&self) -> Self { + *self + } +} +#[repr(C)] pub struct STARTUPINFOW { pub cb: u32, pub lpReserved: PWSTR, @@ -3971,12 +4007,13 @@ impl ::core::clone::Clone for UNICODE_STRING { pub const VOLUME_NAME_DOS: GETFINALPATHNAMEBYHANDLE_FLAGS = 0u32; pub const VOLUME_NAME_GUID: GETFINALPATHNAMEBYHANDLE_FLAGS = 1u32; pub const VOLUME_NAME_NONE: GETFINALPATHNAMEBYHANDLE_FLAGS = 4u32; -pub const WAIT_ABANDONED: WIN32_ERROR = 128u32; -pub const WAIT_ABANDONED_0: WIN32_ERROR = 128u32; -pub const WAIT_FAILED: WIN32_ERROR = 4294967295u32; -pub const WAIT_IO_COMPLETION: WIN32_ERROR = 192u32; -pub const WAIT_OBJECT_0: WIN32_ERROR = 0u32; -pub const WAIT_TIMEOUT: WIN32_ERROR = 258u32; +pub const WAIT_ABANDONED: WAIT_EVENT = 128u32; +pub const WAIT_ABANDONED_0: WAIT_EVENT = 128u32; +pub type WAIT_EVENT = u32; +pub const WAIT_FAILED: WAIT_EVENT = 4294967295u32; +pub const WAIT_IO_COMPLETION: WAIT_EVENT = 192u32; +pub const WAIT_OBJECT_0: WAIT_EVENT = 0u32; +pub const WAIT_TIMEOUT: WAIT_EVENT = 258u32; pub const WC_ERR_INVALID_CHARS: u32 = 128u32; pub type WIN32_ERROR = u32; #[repr(C)] diff --git a/library/std/src/sys/windows/handle.rs b/library/std/src/sys/windows/handle.rs index 84c1fbde3..56d0d6c08 100644 --- a/library/std/src/sys/windows/handle.rs +++ b/library/std/src/sys/windows/handle.rs @@ -143,13 +143,8 @@ impl Handle { ) -> io::Result> { let len = cmp::min(buf.len(), ::MAX as usize) as c::DWORD; let mut amt = 0; - let res = cvt(c::ReadFile( - self.as_raw_handle(), - buf.as_ptr() as c::LPVOID, - len, - &mut amt, - overlapped, - )); + let res = + cvt(c::ReadFile(self.as_raw_handle(), buf.as_mut_ptr(), len, &mut amt, overlapped)); match res { Ok(_) => Ok(Some(amt as usize)), Err(e) => { diff --git a/library/std/src/sys/windows/mod.rs b/library/std/src/sys/windows/mod.rs index bcc172b0f..b609ad247 100644 --- a/library/std/src/sys/windows/mod.rs +++ b/library/std/src/sys/windows/mod.rs @@ -60,6 +60,11 @@ pub unsafe fn cleanup() { net::cleanup(); } +#[inline] +pub fn is_interrupted(_errno: i32) -> bool { + false +} + pub fn decode_error_kind(errno: i32) -> ErrorKind { use ErrorKind::*; diff --git a/library/std/src/sys/windows/net.rs b/library/std/src/sys/windows/net.rs index 1ae42cb7e..abdcab424 100644 --- a/library/std/src/sys/windows/net.rs +++ b/library/std/src/sys/windows/net.rs @@ -117,7 +117,7 @@ impl Socket { }; if socket != c::INVALID_SOCKET { - unsafe { Ok(Self::from_raw_socket(socket)) } + unsafe { Ok(Self::from_raw(socket)) } } else { let error = unsafe { c::WSAGetLastError() }; @@ -133,7 +133,7 @@ impl Socket { } unsafe { - let socket = Self::from_raw_socket(socket); + let socket = Self::from_raw(socket); socket.0.set_no_inherit()?; Ok(socket) } @@ -144,7 +144,7 @@ impl Socket { self.set_nonblocking(true)?; let result = { let (addr, len) = addr.into_inner(); - let result = unsafe { c::connect(self.as_raw_socket(), addr.as_ptr(), len) }; + let result = unsafe { c::connect(self.as_raw(), addr.as_ptr(), len) }; cvt(result).map(drop) }; self.set_nonblocking(false)?; @@ -170,7 +170,7 @@ impl Socket { let fds = { let mut fds = unsafe { mem::zeroed::() }; fds.fd_count = 1; - fds.fd_array[0] = self.as_raw_socket(); + fds.fd_array[0] = self.as_raw(); fds }; @@ -202,11 +202,11 @@ impl Socket { } pub fn accept(&self, storage: *mut c::SOCKADDR, len: *mut c_int) -> io::Result { - let socket = unsafe { c::accept(self.as_raw_socket(), storage, len) }; + let socket = unsafe { c::accept(self.as_raw(), storage, len) }; match socket { c::INVALID_SOCKET => Err(last_error()), - _ => unsafe { Ok(Self::from_raw_socket(socket)) }, + _ => unsafe { Ok(Self::from_raw(socket)) }, } } @@ -218,9 +218,8 @@ impl Socket { // On unix when a socket is shut down all further reads return 0, so we // do the same on windows to map a shut down socket to returning EOF. let length = cmp::min(buf.capacity(), i32::MAX as usize) as i32; - let result = unsafe { - c::recv(self.as_raw_socket(), buf.as_mut().as_mut_ptr() as *mut _, length, flags) - }; + let result = + unsafe { c::recv(self.as_raw(), buf.as_mut().as_mut_ptr() as *mut _, length, flags) }; match result { c::SOCKET_ERROR => { @@ -257,7 +256,7 @@ impl Socket { let mut flags = 0; let result = unsafe { c::WSARecv( - self.as_raw_socket(), + self.as_raw(), bufs.as_mut_ptr() as *mut c::WSABUF, length, &mut nread, @@ -305,7 +304,7 @@ impl Socket { // do the same on windows to map a shut down socket to returning EOF. let result = unsafe { c::recvfrom( - self.as_raw_socket(), + self.as_raw(), buf.as_mut_ptr() as *mut _, length, flags, @@ -341,7 +340,7 @@ impl Socket { let mut nwritten = 0; let result = unsafe { c::WSASend( - self.as_raw_socket(), + self.as_raw(), bufs.as_ptr() as *const c::WSABUF as *mut _, length, &mut nwritten, @@ -392,14 +391,14 @@ impl Socket { Shutdown::Read => c::SD_RECEIVE, Shutdown::Both => c::SD_BOTH, }; - let result = unsafe { c::shutdown(self.as_raw_socket(), how) }; + let result = unsafe { c::shutdown(self.as_raw(), how) }; cvt(result).map(drop) } pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { let mut nonblocking = nonblocking as c_ulong; let result = - unsafe { c::ioctlsocket(self.as_raw_socket(), c::FIONBIO as c_int, &mut nonblocking) }; + unsafe { c::ioctlsocket(self.as_raw(), c::FIONBIO as c_int, &mut nonblocking) }; cvt(result).map(drop) } @@ -433,8 +432,15 @@ impl Socket { } // This is used by sys_common code to abstract over Windows and Unix. - pub fn as_raw(&self) -> RawSocket { - self.as_inner().as_raw_socket() + pub fn as_raw(&self) -> c::SOCKET { + debug_assert_eq!(mem::size_of::(), mem::size_of::()); + debug_assert_eq!(mem::align_of::(), mem::align_of::()); + self.as_inner().as_raw_socket() as c::SOCKET + } + pub unsafe fn from_raw(raw: c::SOCKET) -> Self { + debug_assert_eq!(mem::size_of::(), mem::size_of::()); + debug_assert_eq!(mem::align_of::(), mem::align_of::()); + Self::from_raw_socket(raw as RawSocket) } } diff --git a/library/std/src/sys/windows/os_str.rs b/library/std/src/sys/windows/os_str.rs index 4708657a9..237854fac 100644 --- a/library/std/src/sys/windows/os_str.rs +++ b/library/std/src/sys/windows/os_str.rs @@ -64,12 +64,12 @@ impl fmt::Display for Slice { impl Buf { #[inline] - pub fn into_os_str_bytes(self) -> Vec { + pub fn into_encoded_bytes(self) -> Vec { self.inner.into_bytes() } #[inline] - pub unsafe fn from_os_str_bytes_unchecked(s: Vec) -> Self { + pub unsafe fn from_encoded_bytes_unchecked(s: Vec) -> Self { Self { inner: Wtf8Buf::from_bytes_unchecked(s) } } @@ -162,12 +162,12 @@ impl Buf { impl Slice { #[inline] - pub fn as_os_str_bytes(&self) -> &[u8] { + pub fn as_encoded_bytes(&self) -> &[u8] { self.inner.as_bytes() } #[inline] - pub unsafe fn from_os_str_bytes_unchecked(s: &[u8]) -> &Slice { + pub unsafe fn from_encoded_bytes_unchecked(s: &[u8]) -> &Slice { mem::transmute(Wtf8::from_bytes_unchecked(s)) } diff --git a/library/std/src/sys/windows/path.rs b/library/std/src/sys/windows/path.rs index c9c2d10e6..8c0e07b35 100644 --- a/library/std/src/sys/windows/path.rs +++ b/library/std/src/sys/windows/path.rs @@ -22,12 +22,12 @@ pub fn is_verbatim_sep(b: u8) -> bool { /// Returns true if `path` looks like a lone filename. pub(crate) fn is_file_name(path: &OsStr) -> bool { - !path.as_os_str_bytes().iter().copied().any(is_sep_byte) + !path.as_encoded_bytes().iter().copied().any(is_sep_byte) } pub(crate) fn has_trailing_slash(path: &OsStr) -> bool { - let is_verbatim = path.as_os_str_bytes().starts_with(br"\\?\"); + let is_verbatim = path.as_encoded_bytes().starts_with(br"\\?\"); let is_separator = if is_verbatim { is_verbatim_sep } else { is_sep_byte }; - if let Some(&c) = path.as_os_str_bytes().last() { is_separator(c) } else { false } + if let Some(&c) = path.as_encoded_bytes().last() { is_separator(c) } else { false } } /// Appends a suffix to a path. @@ -49,7 +49,7 @@ impl<'a, const LEN: usize> PrefixParser<'a, LEN> { fn get_prefix(path: &OsStr) -> [u8; LEN] { let mut prefix = [0; LEN]; // SAFETY: Only ASCII characters are modified. - for (i, &ch) in path.as_os_str_bytes().iter().take(LEN).enumerate() { + for (i, &ch) in path.as_encoded_bytes().iter().take(LEN).enumerate() { prefix[i] = if ch == b'/' { b'\\' } else { ch }; } prefix @@ -82,7 +82,7 @@ impl<'a> PrefixParserSlice<'a, '_> { } fn prefix_bytes(&self) -> &'a [u8] { - &self.path.as_os_str_bytes()[..self.index] + &self.path.as_encoded_bytes()[..self.index] } fn finish(self) -> &'a OsStr { @@ -90,7 +90,7 @@ impl<'a> PrefixParserSlice<'a, '_> { // &[u8] and back. This is safe to do because (1) we only look at ASCII // contents of the encoding and (2) new &OsStr values are produced only // from ASCII-bounded slices of existing &OsStr values. - unsafe { OsStr::from_os_str_bytes_unchecked(&self.path.as_os_str_bytes()[self.index..]) } + unsafe { OsStr::from_encoded_bytes_unchecked(&self.path.as_encoded_bytes()[self.index..]) } } } @@ -162,7 +162,7 @@ fn parse_drive(path: &OsStr) -> Option { drive.is_ascii_alphabetic() } - match path.as_os_str_bytes() { + match path.as_encoded_bytes() { [drive, b':', ..] if is_valid_drive_letter(drive) => Some(drive.to_ascii_uppercase()), _ => None, } @@ -171,7 +171,7 @@ fn parse_drive(path: &OsStr) -> Option { // Parses a drive prefix exactly, e.g. "C:" fn parse_drive_exact(path: &OsStr) -> Option { // only parse two bytes: the drive letter and the drive separator - if path.as_os_str_bytes().get(2).map(|&x| is_sep_byte(x)).unwrap_or(true) { + if path.as_encoded_bytes().get(2).map(|&x| is_sep_byte(x)).unwrap_or(true) { parse_drive(path) } else { None @@ -185,15 +185,15 @@ fn parse_drive_exact(path: &OsStr) -> Option { fn parse_next_component(path: &OsStr, verbatim: bool) -> (&OsStr, &OsStr) { let separator = if verbatim { is_verbatim_sep } else { is_sep_byte }; - match path.as_os_str_bytes().iter().position(|&x| separator(x)) { + match path.as_encoded_bytes().iter().position(|&x| separator(x)) { Some(separator_start) => { let separator_end = separator_start + 1; - let component = &path.as_os_str_bytes()[..separator_start]; + let component = &path.as_encoded_bytes()[..separator_start]; // Panic safe // The max `separator_end` is `bytes.len()` and `bytes[bytes.len()..]` is a valid index. - let path = &path.as_os_str_bytes()[separator_end..]; + let path = &path.as_encoded_bytes()[separator_end..]; // SAFETY: `path` is a valid wtf8 encoded slice and each of the separators ('/', '\') // is encoded in a single byte, therefore `bytes[separator_start]` and @@ -201,8 +201,8 @@ fn parse_next_component(path: &OsStr, verbatim: bool) -> (&OsStr, &OsStr) { // `bytes[..separator_start]` and `bytes[separator_end..]` are valid wtf8 slices. unsafe { ( - OsStr::from_os_str_bytes_unchecked(component), - OsStr::from_os_str_bytes_unchecked(path), + OsStr::from_encoded_bytes_unchecked(component), + OsStr::from_encoded_bytes_unchecked(path), ) } } @@ -323,7 +323,7 @@ pub(crate) fn absolute(path: &Path) -> io::Result { // Verbatim paths should not be modified. if prefix.map(|x| x.is_verbatim()).unwrap_or(false) { // NULs in verbatim paths are rejected for consistency. - if path.as_os_str_bytes().contains(&0) { + if path.as_encoded_bytes().contains(&0) { return Err(io::const_io_error!( io::ErrorKind::InvalidInput, "strings passed to WinAPI cannot contain NULs", diff --git a/library/std/src/sys/windows/pipe.rs b/library/std/src/sys/windows/pipe.rs index d07147ecc..7624e746f 100644 --- a/library/std/src/sys/windows/pipe.rs +++ b/library/std/src/sys/windows/pipe.rs @@ -12,7 +12,7 @@ use crate::sys::c; use crate::sys::fs::{File, OpenOptions}; use crate::sys::handle::Handle; use crate::sys::hashmap_random_keys; -use crate::sys_common::IntoInner; +use crate::sys_common::{FromInner, IntoInner}; //////////////////////////////////////////////////////////////////////////////// // Anonymous pipes @@ -28,6 +28,12 @@ impl IntoInner for AnonPipe { } } +impl FromInner for AnonPipe { + fn from_inner(inner: Handle) -> AnonPipe { + Self { inner } + } +} + pub struct Pipes { pub ours: AnonPipe, pub theirs: AnonPipe, diff --git a/library/std/src/sys/windows/process.rs b/library/std/src/sys/windows/process.rs index 2dd0c67ac..cd5bf7f15 100644 --- a/library/std/src/sys/windows/process.rs +++ b/library/std/src/sys/windows/process.rs @@ -11,6 +11,7 @@ use crate::ffi::{OsStr, OsString}; use crate::fmt; use crate::io::{self, Error, ErrorKind}; use crate::mem; +use crate::mem::MaybeUninit; use crate::num::NonZeroI32; use crate::os::windows::ffi::{OsStrExt, OsStringExt}; use crate::os::windows::io::{AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, IntoRawHandle}; @@ -166,10 +167,12 @@ pub struct Command { stdout: Option, stderr: Option, force_quotes_enabled: bool, + proc_thread_attributes: BTreeMap, } pub enum Stdio { Inherit, + InheritSpecific { from_stdio_id: c::DWORD }, Null, MakePipe, Pipe(AnonPipe), @@ -195,6 +198,7 @@ impl Command { stdout: None, stderr: None, force_quotes_enabled: false, + proc_thread_attributes: Default::default(), } } @@ -245,6 +249,17 @@ impl Command { self.cwd.as_ref().map(|cwd| Path::new(cwd)) } + pub unsafe fn raw_attribute( + &mut self, + attribute: usize, + value: T, + ) { + self.proc_thread_attributes.insert( + attribute, + ProcThreadAttributeValue { size: mem::size_of::(), data: Box::new(value) }, + ); + } + pub fn spawn( &mut self, default: Stdio, @@ -308,7 +323,6 @@ impl Command { let stderr = stderr.to_handle(c::STD_ERROR_HANDLE, &mut pipes.stderr)?; let mut si = zeroed_startupinfo(); - si.cb = mem::size_of::() as c::DWORD; // If at least one of stdin, stdout or stderr are set (i.e. are non null) // then set the `hStd` fields in `STARTUPINFO`. @@ -322,6 +336,27 @@ impl Command { si.hStdError = stderr.as_raw_handle(); } + let si_ptr: *mut c::STARTUPINFOW; + + let mut proc_thread_attribute_list; + let mut si_ex; + + if !self.proc_thread_attributes.is_empty() { + si.cb = mem::size_of::() as u32; + flags |= c::EXTENDED_STARTUPINFO_PRESENT; + + proc_thread_attribute_list = + make_proc_thread_attribute_list(&self.proc_thread_attributes)?; + si_ex = c::STARTUPINFOEXW { + StartupInfo: si, + lpAttributeList: proc_thread_attribute_list.0.as_mut_ptr() as _, + }; + si_ptr = &mut si_ex as *mut _ as _; + } else { + si.cb = mem::size_of::() as c::DWORD; + si_ptr = &mut si as *mut _ as _; + } + unsafe { cvt(c::CreateProcessW( program.as_ptr(), @@ -332,7 +367,7 @@ impl Command { flags, envp, dirp, - &si, + si_ptr, &mut pi, )) }?; @@ -395,7 +430,7 @@ fn resolve_exe<'a>( // Test if the file name has the `exe` extension. // This does a case-insensitive `ends_with`. let has_exe_suffix = if exe_path.len() >= EXE_SUFFIX.len() { - exe_path.as_os_str_bytes()[exe_path.len() - EXE_SUFFIX.len()..] + exe_path.as_encoded_bytes()[exe_path.len() - EXE_SUFFIX.len()..] .eq_ignore_ascii_case(EXE_SUFFIX.as_bytes()) } else { false @@ -425,7 +460,7 @@ fn resolve_exe<'a>( // From the `CreateProcessW` docs: // > If the file name does not contain an extension, .exe is appended. // Note that this rule only applies when searching paths. - let has_extension = exe_path.as_os_str_bytes().contains(&b'.'); + let has_extension = exe_path.as_encoded_bytes().contains(&b'.'); // Search the directories given by `search_paths`. let result = search_paths(parent_paths, child_paths, |mut path| { @@ -521,17 +556,19 @@ fn program_exists(path: &Path) -> Option> { impl Stdio { fn to_handle(&self, stdio_id: c::DWORD, pipe: &mut Option) -> io::Result { - match *self { - Stdio::Inherit => match stdio::get_handle(stdio_id) { - Ok(io) => unsafe { - let io = Handle::from_raw_handle(io); - let ret = io.duplicate(0, true, c::DUPLICATE_SAME_ACCESS); - io.into_raw_handle(); - ret - }, - // If no stdio handle is available, then propagate the null value. - Err(..) => unsafe { Ok(Handle::from_raw_handle(ptr::null_mut())) }, + let use_stdio_id = |stdio_id| match stdio::get_handle(stdio_id) { + Ok(io) => unsafe { + let io = Handle::from_raw_handle(io); + let ret = io.duplicate(0, true, c::DUPLICATE_SAME_ACCESS); + io.into_raw_handle(); + ret }, + // If no stdio handle is available, then propagate the null value. + Err(..) => unsafe { Ok(Handle::from_raw_handle(ptr::null_mut())) }, + }; + match *self { + Stdio::Inherit => use_stdio_id(stdio_id), + Stdio::InheritSpecific { from_stdio_id } => use_stdio_id(from_stdio_id), Stdio::MakePipe => { let ours_readable = stdio_id != c::STD_INPUT_HANDLE; @@ -579,6 +616,18 @@ impl From for Stdio { } } +impl From for Stdio { + fn from(_: io::Stdout) -> Stdio { + Stdio::InheritSpecific { from_stdio_id: c::STD_OUTPUT_HANDLE } + } +} + +impl From for Stdio { + fn from(_: io::Stderr) -> Stdio { + Stdio::InheritSpecific { from_stdio_id: c::STD_ERROR_HANDLE } + } +} + //////////////////////////////////////////////////////////////////////////////// // Processes //////////////////////////////////////////////////////////////////////////////// @@ -831,6 +880,80 @@ fn make_dirp(d: Option<&OsString>) -> io::Result<(*const u16, Vec)> { } } +struct ProcThreadAttributeList(Box<[MaybeUninit]>); + +impl Drop for ProcThreadAttributeList { + fn drop(&mut self) { + let lp_attribute_list = self.0.as_mut_ptr() as _; + unsafe { c::DeleteProcThreadAttributeList(lp_attribute_list) } + } +} + +/// Wrapper around the value data to be used as a Process Thread Attribute. +struct ProcThreadAttributeValue { + data: Box, + size: usize, +} + +fn make_proc_thread_attribute_list( + attributes: &BTreeMap, +) -> io::Result { + // To initialize our ProcThreadAttributeList, we need to determine + // how many bytes to allocate for it. The Windows API simplifies this process + // by allowing us to call `InitializeProcThreadAttributeList` with + // a null pointer to retrieve the required size. + let mut required_size = 0; + let Ok(attribute_count) = attributes.len().try_into() else { + return Err(io::const_io_error!( + ErrorKind::InvalidInput, + "maximum number of ProcThreadAttributes exceeded", + )); + }; + unsafe { + c::InitializeProcThreadAttributeList( + ptr::null_mut(), + attribute_count, + 0, + &mut required_size, + ) + }; + + let mut proc_thread_attribute_list = ProcThreadAttributeList( + vec![MaybeUninit::uninit(); required_size as usize].into_boxed_slice(), + ); + + // Once we've allocated the necessary memory, it's safe to invoke + // `InitializeProcThreadAttributeList` to properly initialize the list. + cvt(unsafe { + c::InitializeProcThreadAttributeList( + proc_thread_attribute_list.0.as_mut_ptr() as *mut _, + attribute_count, + 0, + &mut required_size, + ) + })?; + + // # Add our attributes to the buffer. + // It's theoretically possible for the attribute count to exceed a u32 value. + // Therefore, we ensure that we don't add more attributes than the buffer was initialized for. + for (&attribute, value) in attributes.iter().take(attribute_count as usize) { + let value_ptr = &*value.data as *const (dyn Send + Sync) as _; + cvt(unsafe { + c::UpdateProcThreadAttribute( + proc_thread_attribute_list.0.as_mut_ptr() as _, + 0, + attribute, + value_ptr, + value.size, + ptr::null_mut(), + ptr::null_mut(), + ) + })?; + } + + Ok(proc_thread_attribute_list) +} + pub struct CommandArgs<'a> { iter: crate::slice::Iter<'a, Arg>, } diff --git a/library/std/src/sys/xous/alloc.rs b/library/std/src/sys/xous/alloc.rs new file mode 100644 index 000000000..b3a3e691e --- /dev/null +++ b/library/std/src/sys/xous/alloc.rs @@ -0,0 +1,62 @@ +use crate::alloc::{GlobalAlloc, Layout, System}; + +static mut DLMALLOC: dlmalloc::Dlmalloc = dlmalloc::Dlmalloc::new(); + +#[stable(feature = "alloc_system_type", since = "1.28.0")] +unsafe impl GlobalAlloc for System { + #[inline] + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + // SAFETY: DLMALLOC access is guaranteed to be safe because the lock gives us unique and non-reentrant access. + // Calling malloc() is safe because preconditions on this function match the trait method preconditions. + let _lock = lock::lock(); + unsafe { DLMALLOC.malloc(layout.size(), layout.align()) } + } + + #[inline] + unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { + // SAFETY: DLMALLOC access is guaranteed to be safe because the lock gives us unique and non-reentrant access. + // Calling calloc() is safe because preconditions on this function match the trait method preconditions. + let _lock = lock::lock(); + unsafe { DLMALLOC.calloc(layout.size(), layout.align()) } + } + + #[inline] + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + // SAFETY: DLMALLOC access is guaranteed to be safe because the lock gives us unique and non-reentrant access. + // Calling free() is safe because preconditions on this function match the trait method preconditions. + let _lock = lock::lock(); + unsafe { DLMALLOC.free(ptr, layout.size(), layout.align()) } + } + + #[inline] + unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + // SAFETY: DLMALLOC access is guaranteed to be safe because the lock gives us unique and non-reentrant access. + // Calling realloc() is safe because preconditions on this function match the trait method preconditions. + let _lock = lock::lock(); + unsafe { DLMALLOC.realloc(ptr, layout.size(), layout.align(), new_size) } + } +} + +mod lock { + use crate::sync::atomic::{AtomicI32, Ordering::SeqCst}; + + static LOCKED: AtomicI32 = AtomicI32::new(0); + + pub struct DropLock; + + pub fn lock() -> DropLock { + loop { + if LOCKED.swap(1, SeqCst) == 0 { + return DropLock; + } + crate::os::xous::ffi::do_yield(); + } + } + + impl Drop for DropLock { + fn drop(&mut self) { + let r = LOCKED.swap(0, SeqCst); + debug_assert_eq!(r, 1); + } + } +} diff --git a/library/std/src/sys/xous/locks/condvar.rs b/library/std/src/sys/xous/locks/condvar.rs new file mode 100644 index 000000000..1bb38dfa3 --- /dev/null +++ b/library/std/src/sys/xous/locks/condvar.rs @@ -0,0 +1,111 @@ +use super::mutex::Mutex; +use crate::os::xous::ffi::{blocking_scalar, scalar}; +use crate::os::xous::services::ticktimer_server; +use crate::sync::Mutex as StdMutex; +use crate::time::Duration; + +// The implementation is inspired by Andrew D. Birrell's paper +// "Implementing Condition Variables with Semaphores" + +pub struct Condvar { + counter: StdMutex, +} + +unsafe impl Send for Condvar {} +unsafe impl Sync for Condvar {} + +impl Condvar { + #[inline] + #[rustc_const_stable(feature = "const_locks", since = "1.63.0")] + pub const fn new() -> Condvar { + Condvar { counter: StdMutex::new(0) } + } + + pub fn notify_one(&self) { + let mut counter = self.counter.lock().unwrap(); + if *counter <= 0 { + return; + } else { + *counter -= 1; + } + let result = blocking_scalar( + ticktimer_server(), + crate::os::xous::services::TicktimerScalar::NotifyCondition(self.index(), 1).into(), + ); + drop(counter); + result.expect("failure to send NotifyCondition command"); + } + + pub fn notify_all(&self) { + let mut counter = self.counter.lock().unwrap(); + if *counter <= 0 { + return; + } + let result = blocking_scalar( + ticktimer_server(), + crate::os::xous::services::TicktimerScalar::NotifyCondition(self.index(), *counter) + .into(), + ); + *counter = 0; + drop(counter); + + result.expect("failure to send NotifyCondition command"); + } + + fn index(&self) -> usize { + self as *const Condvar as usize + } + + pub unsafe fn wait(&self, mutex: &Mutex) { + let mut counter = self.counter.lock().unwrap(); + *counter += 1; + unsafe { mutex.unlock() }; + drop(counter); + + let result = blocking_scalar( + ticktimer_server(), + crate::os::xous::services::TicktimerScalar::WaitForCondition(self.index(), 0).into(), + ); + unsafe { mutex.lock() }; + + result.expect("Ticktimer: failure to send WaitForCondition command"); + } + + pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { + let mut counter = self.counter.lock().unwrap(); + *counter += 1; + unsafe { mutex.unlock() }; + drop(counter); + + let mut millis = dur.as_millis() as usize; + if millis == 0 { + millis = 1; + } + + let result = blocking_scalar( + ticktimer_server(), + crate::os::xous::services::TicktimerScalar::WaitForCondition(self.index(), millis) + .into(), + ); + unsafe { mutex.lock() }; + + let result = result.expect("Ticktimer: failure to send WaitForCondition command")[0] == 0; + + // If we awoke due to a timeout, decrement the wake count, as that would not have + // been done in the `notify()` call. + if !result { + *self.counter.lock().unwrap() -= 1; + } + result + } +} + +impl Drop for Condvar { + fn drop(&mut self) { + scalar( + ticktimer_server(), + crate::os::xous::services::TicktimerScalar::FreeCondition(self.index()).into(), + ) + .ok(); + } +} diff --git a/library/std/src/sys/xous/locks/mod.rs b/library/std/src/sys/xous/locks/mod.rs new file mode 100644 index 000000000..f3c5c5d9f --- /dev/null +++ b/library/std/src/sys/xous/locks/mod.rs @@ -0,0 +1,7 @@ +mod condvar; +mod mutex; +mod rwlock; + +pub use condvar::*; +pub use mutex::*; +pub use rwlock::*; diff --git a/library/std/src/sys/xous/locks/mutex.rs b/library/std/src/sys/xous/locks/mutex.rs new file mode 100644 index 000000000..ea51776d5 --- /dev/null +++ b/library/std/src/sys/xous/locks/mutex.rs @@ -0,0 +1,116 @@ +use crate::os::xous::ffi::{blocking_scalar, do_yield, scalar}; +use crate::os::xous::services::ticktimer_server; +use crate::sync::atomic::{AtomicBool, AtomicUsize, Ordering::Relaxed, Ordering::SeqCst}; + +pub struct Mutex { + /// The "locked" value indicates how many threads are waiting on this + /// Mutex. Possible values are: + /// 0: The lock is unlocked + /// 1: The lock is locked and uncontended + /// >=2: The lock is locked and contended + /// + /// A lock is "contended" when there is more than one thread waiting + /// for a lock, or it is locked for long periods of time. Rather than + /// spinning, these locks send a Message to the ticktimer server + /// requesting that they be woken up when a lock is unlocked. + locked: AtomicUsize, + + /// Whether this Mutex ever was contended, and therefore made a trip + /// to the ticktimer server. If this was never set, then we were never + /// on the slow path and can skip deregistering the mutex. + contended: AtomicBool, +} + +impl Mutex { + #[inline] + #[rustc_const_stable(feature = "const_locks", since = "1.63.0")] + pub const fn new() -> Mutex { + Mutex { locked: AtomicUsize::new(0), contended: AtomicBool::new(false) } + } + + fn index(&self) -> usize { + self as *const Mutex as usize + } + + #[inline] + pub unsafe fn lock(&self) { + // Try multiple times to acquire the lock without resorting to the ticktimer + // server. For locks that are held for a short amount of time, this will + // result in the ticktimer server never getting invoked. The `locked` value + // will be either 0 or 1. + for _attempts in 0..3 { + if unsafe { self.try_lock() } { + return; + } + do_yield(); + } + + // Try one more time to lock. If the lock is released between the previous code and + // here, then the inner `locked` value will be 1 at the end of this. If it was not + // locked, then the value will be more than 1, for example if there are multiple other + // threads waiting on this lock. + if unsafe { self.try_lock_or_poison() } { + return; + } + + // When this mutex is dropped, we will need to deregister it with the server. + self.contended.store(true, Relaxed); + + // The lock is now "contended". When the lock is released, a Message will get sent to the + // ticktimer server to wake it up. Note that this may already have happened, so the actual + // value of `lock` may be anything (0, 1, 2, ...). + blocking_scalar( + ticktimer_server(), + crate::os::xous::services::TicktimerScalar::LockMutex(self.index()).into(), + ) + .expect("failure to send LockMutex command"); + } + + #[inline] + pub unsafe fn unlock(&self) { + let prev = self.locked.fetch_sub(1, SeqCst); + + // If the previous value was 1, then this was a "fast path" unlock, so no + // need to involve the Ticktimer server + if prev == 1 { + return; + } + + // If it was 0, then something has gone seriously wrong and the counter + // has just wrapped around. + if prev == 0 { + panic!("mutex lock count underflowed"); + } + + // Unblock one thread that is waiting on this message. + scalar( + ticktimer_server(), + crate::os::xous::services::TicktimerScalar::UnlockMutex(self.index()).into(), + ) + .expect("failure to send UnlockMutex command"); + } + + #[inline] + pub unsafe fn try_lock(&self) -> bool { + self.locked.compare_exchange(0, 1, SeqCst, SeqCst).is_ok() + } + + #[inline] + pub unsafe fn try_lock_or_poison(&self) -> bool { + self.locked.fetch_add(1, SeqCst) == 0 + } +} + +impl Drop for Mutex { + fn drop(&mut self) { + // If there was Mutex contention, then we involved the ticktimer. Free + // the resources associated with this Mutex as it is deallocated. + if self.contended.load(Relaxed) { + scalar( + ticktimer_server(), + crate::os::xous::services::TicktimerScalar::FreeMutex(self.index()).into(), + ) + .ok(); + } + } +} diff --git a/library/std/src/sys/xous/locks/rwlock.rs b/library/std/src/sys/xous/locks/rwlock.rs new file mode 100644 index 000000000..618da758a --- /dev/null +++ b/library/std/src/sys/xous/locks/rwlock.rs @@ -0,0 +1,72 @@ +use crate::os::xous::ffi::do_yield; +use crate::sync::atomic::{AtomicIsize, Ordering::SeqCst}; + +pub struct RwLock { + /// The "mode" value indicates how many threads are waiting on this + /// Mutex. Possible values are: + /// -1: The lock is locked for writing + /// 0: The lock is unlocked + /// >=1: The lock is locked for reading + /// + /// This currently spins waiting for the lock to be freed. An + /// optimization would be to involve the ticktimer server to + /// coordinate unlocks. + mode: AtomicIsize, +} + +unsafe impl Send for RwLock {} +unsafe impl Sync for RwLock {} + +impl RwLock { + #[inline] + #[rustc_const_stable(feature = "const_locks", since = "1.63.0")] + pub const fn new() -> RwLock { + RwLock { mode: AtomicIsize::new(0) } + } + + #[inline] + pub unsafe fn read(&self) { + while !unsafe { self.try_read() } { + do_yield(); + } + } + + #[inline] + pub unsafe fn try_read(&self) -> bool { + // Non-atomically determine the current value. + let current = self.mode.load(SeqCst); + + // If it's currently locked for writing, then we cannot read. + if current < 0 { + return false; + } + + // Attempt to lock. If the `current` value has changed, then this + // operation will fail and we will not obtain the lock even if we + // could potentially keep it. + let new = current + 1; + self.mode.compare_exchange(current, new, SeqCst, SeqCst).is_ok() + } + + #[inline] + pub unsafe fn write(&self) { + while !unsafe { self.try_write() } { + do_yield(); + } + } + + #[inline] + pub unsafe fn try_write(&self) -> bool { + self.mode.compare_exchange(0, -1, SeqCst, SeqCst).is_ok() + } + + #[inline] + pub unsafe fn read_unlock(&self) { + self.mode.fetch_sub(1, SeqCst); + } + + #[inline] + pub unsafe fn write_unlock(&self) { + assert_eq!(self.mode.compare_exchange(-1, 0, SeqCst, SeqCst), Ok(-1)); + } +} diff --git a/library/std/src/sys/xous/mod.rs b/library/std/src/sys/xous/mod.rs new file mode 100644 index 000000000..6d5c218d1 --- /dev/null +++ b/library/std/src/sys/xous/mod.rs @@ -0,0 +1,37 @@ +#![deny(unsafe_op_in_unsafe_fn)] + +pub mod alloc; +#[path = "../unsupported/args.rs"] +pub mod args; +#[path = "../unix/cmath.rs"] +pub mod cmath; +#[path = "../unsupported/env.rs"] +pub mod env; +#[path = "../unsupported/fs.rs"] +pub mod fs; +#[path = "../unsupported/io.rs"] +pub mod io; +pub mod locks; +#[path = "../unsupported/net.rs"] +pub mod net; +#[path = "../unsupported/once.rs"] +pub mod once; +pub mod os; +#[path = "../unix/os_str.rs"] +pub mod os_str; +#[path = "../unix/path.rs"] +pub mod path; +#[path = "../unsupported/pipe.rs"] +pub mod pipe; +#[path = "../unsupported/process.rs"] +pub mod process; +pub mod stdio; +pub mod thread; +pub mod thread_local_key; +#[path = "../unsupported/thread_parking.rs"] +pub mod thread_parking; +pub mod time; + +#[path = "../unsupported/common.rs"] +mod common; +pub use common::*; diff --git a/library/std/src/sys/xous/os.rs b/library/std/src/sys/xous/os.rs new file mode 100644 index 000000000..3d19fa4b3 --- /dev/null +++ b/library/std/src/sys/xous/os.rs @@ -0,0 +1,147 @@ +use super::unsupported; +use crate::error::Error as StdError; +use crate::ffi::{OsStr, OsString}; +use crate::fmt; +use crate::io; +use crate::marker::PhantomData; +use crate::os::xous::ffi::Error as XousError; +use crate::path::{self, PathBuf}; + +#[cfg(not(test))] +mod c_compat { + use crate::os::xous::ffi::exit; + extern "C" { + fn main() -> u32; + } + + #[no_mangle] + pub extern "C" fn abort() { + exit(1); + } + + #[no_mangle] + pub extern "C" fn _start() { + exit(unsafe { main() }); + } + + // This function is needed by the panic runtime. The symbol is named in + // pre-link args for the target specification, so keep that in sync. + #[no_mangle] + // NB. used by both libunwind and libpanic_abort + pub extern "C" fn __rust_abort() -> ! { + exit(101); + } +} + +pub fn errno() -> i32 { + 0 +} + +pub fn error_string(errno: i32) -> String { + Into::::into(errno).to_string() +} + +pub fn getcwd() -> io::Result { + unsupported() +} + +pub fn chdir(_: &path::Path) -> io::Result<()> { + unsupported() +} + +pub struct SplitPaths<'a>(!, PhantomData<&'a ()>); + +pub fn split_paths(_unparsed: &OsStr) -> SplitPaths<'_> { + panic!("unsupported") +} + +impl<'a> Iterator for SplitPaths<'a> { + type Item = PathBuf; + fn next(&mut self) -> Option { + self.0 + } +} + +#[derive(Debug)] +pub struct JoinPathsError; + +pub fn join_paths(_paths: I) -> Result +where + I: Iterator, + T: AsRef, +{ + Err(JoinPathsError) +} + +impl fmt::Display for JoinPathsError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + "not supported on this platform yet".fmt(f) + } +} + +impl StdError for JoinPathsError { + #[allow(deprecated)] + fn description(&self) -> &str { + "not supported on this platform yet" + } +} + +pub fn current_exe() -> io::Result { + unsupported() +} + +pub struct Env(!); + +impl Env { + // FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when ::fmt matches ::fmt. + pub fn str_debug(&self) -> impl fmt::Debug + '_ { + let Self(inner) = self; + match *inner {} + } +} + +impl fmt::Debug for Env { + fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self(inner) = self; + match *inner {} + } +} + +impl Iterator for Env { + type Item = (OsString, OsString); + fn next(&mut self) -> Option<(OsString, OsString)> { + self.0 + } +} + +pub fn env() -> Env { + panic!("not supported on this platform") +} + +pub fn getenv(_: &OsStr) -> Option { + None +} + +pub fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> { + Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform")) +} + +pub fn unsetenv(_: &OsStr) -> io::Result<()> { + Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform")) +} + +pub fn temp_dir() -> PathBuf { + panic!("no filesystem on this platform") +} + +pub fn home_dir() -> Option { + None +} + +pub fn exit(code: i32) -> ! { + crate::os::xous::ffi::exit(code as u32); +} + +pub fn getpid() -> u32 { + panic!("no pids on this platform") +} diff --git a/library/std/src/sys/xous/stdio.rs b/library/std/src/sys/xous/stdio.rs new file mode 100644 index 000000000..2ac694641 --- /dev/null +++ b/library/std/src/sys/xous/stdio.rs @@ -0,0 +1,131 @@ +use crate::io; + +pub struct Stdin; +pub struct Stdout {} +pub struct Stderr; + +use crate::os::xous::ffi::{lend, try_lend, try_scalar, Connection}; +use crate::os::xous::services::{log_server, try_connect, LogScalar}; + +impl Stdin { + pub const fn new() -> Stdin { + Stdin + } +} + +impl io::Read for Stdin { + fn read(&mut self, _buf: &mut [u8]) -> io::Result { + Ok(0) + } +} + +impl Stdout { + pub const fn new() -> Stdout { + Stdout {} + } +} + +impl io::Write for Stdout { + fn write(&mut self, buf: &[u8]) -> io::Result { + #[repr(align(4096))] + struct LendBuffer([u8; 4096]); + let mut lend_buffer = LendBuffer([0u8; 4096]); + let connection = log_server(); + for chunk in buf.chunks(lend_buffer.0.len()) { + for (dest, src) in lend_buffer.0.iter_mut().zip(chunk) { + *dest = *src; + } + lend(connection, 1, &lend_buffer.0, 0, chunk.len()).unwrap(); + } + Ok(buf.len()) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +impl Stderr { + pub const fn new() -> Stderr { + Stderr + } +} + +impl io::Write for Stderr { + fn write(&mut self, buf: &[u8]) -> io::Result { + #[repr(align(4096))] + struct LendBuffer([u8; 4096]); + let mut lend_buffer = LendBuffer([0u8; 4096]); + let connection = log_server(); + for chunk in buf.chunks(lend_buffer.0.len()) { + for (dest, src) in lend_buffer.0.iter_mut().zip(chunk) { + *dest = *src; + } + lend(connection, 1, &lend_buffer.0, 0, chunk.len()).unwrap(); + } + Ok(buf.len()) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +pub const STDIN_BUF_SIZE: usize = 0; + +pub fn is_ebadf(_err: &io::Error) -> bool { + true +} + +#[derive(Copy, Clone)] +pub struct PanicWriter { + log: Connection, + gfx: Option, +} + +impl io::Write for PanicWriter { + fn write(&mut self, s: &[u8]) -> core::result::Result { + for c in s.chunks(core::mem::size_of::() * 4) { + // Text is grouped into 4x `usize` words. The id is 1100 plus + // the number of characters in this message. + // Ignore errors since we're already panicking. + try_scalar(self.log, LogScalar::AppendPanicMessage(&c).into()).ok(); + } + + // Serialize the text to the graphics panic handler, only if we were able + // to acquire a connection to it. Text length is encoded in the `valid` field, + // the data itself in the buffer. Typically several messages are require to + // fully transmit the entire panic message. + if let Some(gfx) = self.gfx { + #[repr(C, align(4096))] + struct Request([u8; 4096]); + let mut request = Request([0u8; 4096]); + for (&s, d) in s.iter().zip(request.0.iter_mut()) { + *d = s; + } + try_lend(gfx, 0 /* AppendPanicText */, &request.0, 0, s.len()).ok(); + } + Ok(s.len()) + } + + // Tests show that this does not seem to be reliably called at the end of a panic + // print, so, we can't rely on this to e.g. trigger a graphics update. + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +pub fn panic_output() -> Option { + // Generally this won't fail because every server has already connected, so + // this is likely to succeed. + let log = log_server(); + + // Send the "We're panicking" message (1000). + try_scalar(log, LogScalar::BeginPanic.into()).ok(); + + // This is will fail in the case that the connection table is full, or if the + // graphics server is not running. Most servers do not already have this connection. + let gfx = try_connect("panic-to-screen!"); + + Some(PanicWriter { log, gfx }) +} diff --git a/library/std/src/sys/xous/thread.rs b/library/std/src/sys/xous/thread.rs new file mode 100644 index 000000000..78c68de7b --- /dev/null +++ b/library/std/src/sys/xous/thread.rs @@ -0,0 +1,144 @@ +use crate::ffi::CStr; +use crate::io; +use crate::num::NonZeroUsize; +use crate::os::xous::ffi::{ + blocking_scalar, create_thread, do_yield, join_thread, map_memory, update_memory_flags, + MemoryFlags, Syscall, ThreadId, +}; +use crate::os::xous::services::{ticktimer_server, TicktimerScalar}; +use crate::time::Duration; +use core::arch::asm; + +pub struct Thread { + tid: ThreadId, +} + +pub const DEFAULT_MIN_STACK_SIZE: usize = 131072; +const MIN_STACK_SIZE: usize = 4096; +pub const GUARD_PAGE_SIZE: usize = 4096; + +impl Thread { + // unsafe: see thread::Builder::spawn_unchecked for safety requirements + pub unsafe fn new(stack: usize, p: Box) -> io::Result { + let p = Box::into_raw(Box::new(p)); + let mut stack_size = crate::cmp::max(stack, MIN_STACK_SIZE); + + if (stack_size & 4095) != 0 { + stack_size = (stack_size + 4095) & !4095; + } + + // Allocate the whole thing, then divide it up after the fact. This ensures that + // even if there's a context switch during this function, the whole stack plus + // guard pages will remain contiguous. + let stack_plus_guard_pages: &mut [u8] = unsafe { + map_memory( + None, + None, + GUARD_PAGE_SIZE + stack_size + GUARD_PAGE_SIZE, + MemoryFlags::R | MemoryFlags::W | MemoryFlags::X, + ) + } + .map_err(|code| io::Error::from_raw_os_error(code as i32))?; + + // No access to this page. Note: Write-only pages are illegal, and will + // cause an access violation. + unsafe { + update_memory_flags(&mut stack_plus_guard_pages[0..GUARD_PAGE_SIZE], MemoryFlags::W) + .map_err(|code| io::Error::from_raw_os_error(code as i32))? + }; + + // No access to this page. Note: Write-only pages are illegal, and will + // cause an access violation. + unsafe { + update_memory_flags( + &mut stack_plus_guard_pages[(GUARD_PAGE_SIZE + stack_size)..], + MemoryFlags::W, + ) + .map_err(|code| io::Error::from_raw_os_error(code as i32))? + }; + + let guard_page_pre = stack_plus_guard_pages.as_ptr() as usize; + let tid = create_thread( + thread_start as *mut usize, + &mut stack_plus_guard_pages[GUARD_PAGE_SIZE..(stack_size + GUARD_PAGE_SIZE)], + p as usize, + guard_page_pre, + stack_size, + 0, + ) + .map_err(|code| io::Error::from_raw_os_error(code as i32))?; + + extern "C" fn thread_start(main: *mut usize, guard_page_pre: usize, stack_size: usize) { + unsafe { + // Finally, let's run some code. + Box::from_raw(main as *mut Box)(); + } + + // Destroy TLS, which will free the TLS page and call the destructor for + // any thread local storage. + unsafe { + crate::sys::thread_local_key::destroy_tls(); + } + + // Deallocate the stack memory, along with the guard pages. Afterwards, + // exit the thread by returning to the magic address 0xff80_3000usize, + // which tells the kernel to deallocate this thread. + let mapped_memory_base = guard_page_pre; + let mapped_memory_length = GUARD_PAGE_SIZE + stack_size + GUARD_PAGE_SIZE; + unsafe { + asm!( + "ecall", + "ret", + in("a0") Syscall::UnmapMemory as usize, + in("a1") mapped_memory_base, + in("a2") mapped_memory_length, + in("ra") 0xff80_3000usize, + options(nomem, nostack, noreturn) + ); + } + } + + Ok(Thread { tid }) + } + + pub fn yield_now() { + do_yield(); + } + + pub fn set_name(_name: &CStr) { + // nope + } + + pub fn sleep(dur: Duration) { + // Because the sleep server works on units of `usized milliseconds`, split + // the messages up into these chunks. This means we may run into issues + // if you try to sleep a thread for more than 49 days on a 32-bit system. + let mut millis = dur.as_millis(); + while millis > 0 { + let sleep_duration = + if millis > (usize::MAX as _) { usize::MAX } else { millis as usize }; + blocking_scalar(ticktimer_server(), TicktimerScalar::SleepMs(sleep_duration).into()) + .expect("failed to send message to ticktimer server"); + millis -= sleep_duration as u128; + } + } + + pub fn join(self) { + join_thread(self.tid).unwrap(); + } +} + +pub fn available_parallelism() -> io::Result { + // We're unicore right now. + Ok(unsafe { NonZeroUsize::new_unchecked(1) }) +} + +pub mod guard { + pub type Guard = !; + pub unsafe fn current() -> Option { + None + } + pub unsafe fn init() -> Option { + None + } +} diff --git a/library/std/src/sys/xous/thread_local_key.rs b/library/std/src/sys/xous/thread_local_key.rs new file mode 100644 index 000000000..3771ea657 --- /dev/null +++ b/library/std/src/sys/xous/thread_local_key.rs @@ -0,0 +1,190 @@ +use crate::mem::ManuallyDrop; +use crate::ptr; +use crate::sync::atomic::AtomicPtr; +use crate::sync::atomic::AtomicUsize; +use crate::sync::atomic::Ordering::SeqCst; +use core::arch::asm; + +use crate::os::xous::ffi::{map_memory, unmap_memory, MemoryFlags}; + +/// Thread Local Storage +/// +/// Currently, we are limited to 1023 TLS entries. The entries +/// live in a page of memory that's unique per-process, and is +/// stored in the `$tp` register. If this register is 0, then +/// TLS has not been initialized and thread cleanup can be skipped. +/// +/// The index into this register is the `key`. This key is identical +/// between all threads, but indexes a different offset within this +/// pointer. +pub type Key = usize; + +pub type Dtor = unsafe extern "C" fn(*mut u8); + +const TLS_MEMORY_SIZE: usize = 4096; + +/// TLS keys start at `1` to mimic POSIX. +static TLS_KEY_INDEX: AtomicUsize = AtomicUsize::new(1); + +fn tls_ptr_addr() -> *mut usize { + let mut tp: usize; + unsafe { + asm!( + "mv {}, tp", + out(reg) tp, + ); + } + core::ptr::from_exposed_addr_mut::(tp) +} + +/// Create an area of memory that's unique per thread. This area will +/// contain all thread local pointers. +fn tls_ptr() -> *mut usize { + let mut tp = tls_ptr_addr(); + + // If the TP register is `0`, then this thread hasn't initialized + // its TLS yet. Allocate a new page to store this memory. + if tp.is_null() { + tp = unsafe { + map_memory( + None, + None, + TLS_MEMORY_SIZE / core::mem::size_of::(), + MemoryFlags::R | MemoryFlags::W, + ) + } + .expect("Unable to allocate memory for thread local storage") + .as_mut_ptr(); + + unsafe { + // Key #0 is currently unused. + (tp).write_volatile(0); + + // Set the thread's `$tp` register + asm!( + "mv tp, {}", + in(reg) tp as usize, + ); + } + } + tp +} + +/// Allocate a new TLS key. These keys are shared among all threads. +fn tls_alloc() -> usize { + TLS_KEY_INDEX.fetch_add(1, SeqCst) +} + +#[inline] +pub unsafe fn create(dtor: Option) -> Key { + let key = tls_alloc(); + if let Some(f) = dtor { + unsafe { register_dtor(key, f) }; + } + key +} + +#[inline] +pub unsafe fn set(key: Key, value: *mut u8) { + assert!((key < 1022) && (key >= 1)); + unsafe { tls_ptr().add(key).write_volatile(value as usize) }; +} + +#[inline] +pub unsafe fn get(key: Key) -> *mut u8 { + assert!((key < 1022) && (key >= 1)); + core::ptr::from_exposed_addr_mut::(unsafe { tls_ptr().add(key).read_volatile() }) +} + +#[inline] +pub unsafe fn destroy(_key: Key) { + panic!("can't destroy keys on Xous"); +} + +// ------------------------------------------------------------------------- +// Dtor registration (stolen from Windows) +// +// Xous has no native support for running destructors so we manage our own +// list of destructors to keep track of how to destroy keys. We then install a +// callback later to get invoked whenever a thread exits, running all +// appropriate destructors. +// +// Currently unregistration from this list is not supported. A destructor can be +// registered but cannot be unregistered. There's various simplifying reasons +// for doing this, the big ones being: +// +// 1. Currently we don't even support deallocating TLS keys, so normal operation +// doesn't need to deallocate a destructor. +// 2. There is no point in time where we know we can unregister a destructor +// because it could always be getting run by some remote thread. +// +// Typically processes have a statically known set of TLS keys which is pretty +// small, and we'd want to keep this memory alive for the whole process anyway +// really. +// +// Perhaps one day we can fold the `Box` here into a static allocation, +// expanding the `StaticKey` structure to contain not only a slot for the TLS +// key but also a slot for the destructor queue on windows. An optimization for +// another day! + +static DTORS: AtomicPtr = AtomicPtr::new(ptr::null_mut()); + +struct Node { + dtor: Dtor, + key: Key, + next: *mut Node, +} + +unsafe fn register_dtor(key: Key, dtor: Dtor) { + let mut node = ManuallyDrop::new(Box::new(Node { key, dtor, next: ptr::null_mut() })); + + let mut head = DTORS.load(SeqCst); + loop { + node.next = head; + match DTORS.compare_exchange(head, &mut **node, SeqCst, SeqCst) { + Ok(_) => return, // nothing to drop, we successfully added the node to the list + Err(cur) => head = cur, + } + } +} + +pub unsafe fn destroy_tls() { + let tp = tls_ptr_addr(); + + // If the pointer address is 0, then this thread has no TLS. + if tp.is_null() { + return; + } + unsafe { run_dtors() }; + + // Finally, free the TLS array + unsafe { + unmap_memory(core::slice::from_raw_parts_mut( + tp, + TLS_MEMORY_SIZE / core::mem::size_of::(), + )) + .unwrap() + }; +} + +unsafe fn run_dtors() { + let mut any_run = true; + for _ in 0..5 { + if !any_run { + break; + } + any_run = false; + let mut cur = DTORS.load(SeqCst); + while !cur.is_null() { + let ptr = unsafe { get((*cur).key) }; + + if !ptr.is_null() { + unsafe { set((*cur).key, ptr::null_mut()) }; + unsafe { ((*cur).dtor)(ptr as *mut _) }; + any_run = true; + } + + unsafe { cur = (*cur).next }; + } + } +} diff --git a/library/std/src/sys/xous/time.rs b/library/std/src/sys/xous/time.rs new file mode 100644 index 000000000..4e4ae67ef --- /dev/null +++ b/library/std/src/sys/xous/time.rs @@ -0,0 +1,57 @@ +use crate::os::xous::ffi::blocking_scalar; +use crate::os::xous::services::{ + systime_server, ticktimer_server, SystimeScalar::GetUtcTimeMs, TicktimerScalar::ElapsedMs, +}; +use crate::time::Duration; + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] +pub struct Instant(Duration); + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] +pub struct SystemTime(Duration); + +pub const UNIX_EPOCH: SystemTime = SystemTime(Duration::from_secs(0)); + +impl Instant { + pub fn now() -> Instant { + let result = blocking_scalar(ticktimer_server(), ElapsedMs.into()) + .expect("failed to request elapsed_ms"); + let lower = result[0]; + let upper = result[1]; + Instant { 0: Duration::from_millis(lower as u64 | (upper as u64) << 32) } + } + + pub fn checked_sub_instant(&self, other: &Instant) -> Option { + self.0.checked_sub(other.0) + } + + pub fn checked_add_duration(&self, other: &Duration) -> Option { + self.0.checked_add(*other).map(Instant) + } + + pub fn checked_sub_duration(&self, other: &Duration) -> Option { + self.0.checked_sub(*other).map(Instant) + } +} + +impl SystemTime { + pub fn now() -> SystemTime { + let result = blocking_scalar(systime_server(), GetUtcTimeMs.into()) + .expect("failed to request utc time in ms"); + let lower = result[0]; + let upper = result[1]; + SystemTime { 0: Duration::from_millis((upper as u64) << 32 | lower as u64) } + } + + pub fn sub_time(&self, other: &SystemTime) -> Result { + self.0.checked_sub(other.0).ok_or_else(|| other.0 - self.0) + } + + pub fn checked_add_duration(&self, other: &Duration) -> Option { + Some(SystemTime(self.0.checked_add(*other)?)) + } + + pub fn checked_sub_duration(&self, other: &Duration) -> Option { + Some(SystemTime(self.0.checked_sub(*other)?)) + } +} -- cgit v1.2.3