summaryrefslogtreecommitdiffstats
path: root/library/std
diff options
context:
space:
mode:
Diffstat (limited to 'library/std')
-rw-r--r--library/std/Cargo.toml6
-rw-r--r--library/std/build.rs11
-rw-r--r--library/std/src/collections/hash/map.rs2
-rw-r--r--library/std/src/collections/hash/set.rs2
-rw-r--r--library/std/src/fs.rs19
-rw-r--r--library/std/src/fs/tests.rs86
-rw-r--r--library/std/src/io/buffered/bufreader.rs25
-rw-r--r--library/std/src/io/copy.rs74
-rw-r--r--library/std/src/io/copy/tests.rs12
-rw-r--r--library/std/src/io/impls.rs32
-rw-r--r--library/std/src/io/mod.rs67
-rw-r--r--library/std/src/io/readbuf.rs317
-rw-r--r--library/std/src/io/readbuf/tests.rs175
-rw-r--r--library/std/src/io/stdio.rs2
-rw-r--r--library/std/src/lib.rs7
-rw-r--r--library/std/src/net/udp.rs21
-rw-r--r--library/std/src/os/aix/fs.rs348
-rw-r--r--library/std/src/os/aix/mod.rs6
-rw-r--r--library/std/src/os/aix/raw.rs9
-rw-r--r--library/std/src/os/fd/owned.rs6
-rw-r--r--library/std/src/os/freebsd/fs.rs10
-rw-r--r--library/std/src/os/ios/fs.rs6
-rw-r--r--library/std/src/os/linux/fs.rs9
-rw-r--r--library/std/src/os/macos/fs.rs6
-rw-r--r--library/std/src/os/mod.rs2
-rw-r--r--library/std/src/os/unix/mod.rs2
-rw-r--r--library/std/src/os/watchos/fs.rs6
-rw-r--r--library/std/src/os/windows/fs.rs6
-rw-r--r--library/std/src/panicking.rs60
-rw-r--r--library/std/src/process.rs16
-rw-r--r--library/std/src/rt.rs1
-rw-r--r--library/std/src/sync/once.rs2
-rw-r--r--library/std/src/sync/once_lock.rs43
-rw-r--r--library/std/src/sync/rwlock.rs4
-rw-r--r--library/std/src/sys/common/mod.rs1
-rw-r--r--library/std/src/sys/hermit/net.rs6
-rw-r--r--library/std/src/sys/hermit/thread_local_dtor.rs14
-rw-r--r--library/std/src/sys/personality/dwarf/eh.rs131
-rw-r--r--library/std/src/sys/personality/gcc.rs11
-rw-r--r--library/std/src/sys/sgx/abi/usercalls/alloc.rs207
-rw-r--r--library/std/src/sys/sgx/waitqueue/mod.rs26
-rw-r--r--library/std/src/sys/solid/net.rs11
-rw-r--r--library/std/src/sys/solid/thread_local_dtor.rs15
-rw-r--r--library/std/src/sys/uefi/alloc.rs22
-rw-r--r--library/std/src/sys/uefi/args.rs158
-rw-r--r--library/std/src/sys/uefi/helpers.rs7
-rw-r--r--library/std/src/sys/uefi/mod.rs2
-rw-r--r--library/std/src/sys/uefi/stdio.rs162
-rw-r--r--library/std/src/sys/unix/args.rs1
-rw-r--r--library/std/src/sys/unix/env.rs11
-rw-r--r--library/std/src/sys/unix/fd.rs24
-rw-r--r--library/std/src/sys/unix/fs.rs59
-rw-r--r--library/std/src/sys/unix/mod.rs3
-rw-r--r--library/std/src/sys/unix/net.rs21
-rw-r--r--library/std/src/sys/unix/os.rs36
-rw-r--r--library/std/src/sys/unix/process/mod.rs9
-rw-r--r--library/std/src/sys/unix/process/process_common.rs1
-rw-r--r--library/std/src/sys/unix/process/process_common/tests.rs33
-rw-r--r--library/std/src/sys/unix/process/process_unix.rs5
-rw-r--r--library/std/src/sys/unix/process/process_unsupported.rs56
-rw-r--r--library/std/src/sys/unix/process/process_unsupported/wait_status.rs84
-rw-r--r--library/std/src/sys/unix/process/process_unsupported/wait_status/tests.rs36
-rw-r--r--library/std/src/sys/unix/rand.rs125
-rw-r--r--library/std/src/sys/unix/stack_overflow.rs14
-rw-r--r--library/std/src/sys/unix/thread.rs6
-rw-r--r--library/std/src/sys/unix/thread_local_dtor.rs67
-rw-r--r--library/std/src/sys/unix/time.rs298
-rw-r--r--library/std/src/sys/wasi/mod.rs115
-rw-r--r--library/std/src/sys/windows/api.rs157
-rw-r--r--library/std/src/sys/windows/c.rs4
-rw-r--r--library/std/src/sys/windows/c/windows_sys.lst10
-rw-r--r--library/std/src/sys/windows/c/windows_sys.rs65
-rw-r--r--library/std/src/sys/windows/cmath.rs6
-rw-r--r--library/std/src/sys/windows/fs.rs79
-rw-r--r--library/std/src/sys/windows/io.rs4
-rw-r--r--library/std/src/sys/windows/mod.rs16
-rw-r--r--library/std/src/sys/windows/net.rs14
-rw-r--r--library/std/src/sys/windows/os.rs6
-rw-r--r--library/std/src/sys/windows/process.rs5
-rw-r--r--library/std/src/sys/windows/stack_overflow.rs4
-rw-r--r--library/std/src/sys/windows/stdio.rs3
-rw-r--r--library/std/src/sys/windows/thread.rs15
-rw-r--r--library/std/src/sys/windows/thread_local_key.rs19
-rw-r--r--library/std/src/sys/windows/time.rs38
-rw-r--r--library/std/src/sys_common/net.rs4
-rw-r--r--library/std/src/sys_common/thread_local_dtor.rs17
-rw-r--r--library/std/src/thread/local.rs6
-rw-r--r--library/std/src/thread/mod.rs18
-rw-r--r--library/std/src/time.rs7
-rw-r--r--library/std/tests/switch-stdout.rs24
90 files changed, 2441 insertions, 1257 deletions
diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml
index 965132bde..f666b1888 100644
--- a/library/std/Cargo.toml
+++ b/library/std/Cargo.toml
@@ -17,8 +17,8 @@ cfg-if = { version = "1.0", features = ['rustc-dep-of-std'] }
panic_unwind = { path = "../panic_unwind", optional = true }
panic_abort = { path = "../panic_abort" }
core = { path = "../core", public = true }
-libc = { version = "0.2.148", default-features = false, features = ['rustc-dep-of-std'], public = true }
-compiler_builtins = { version = "0.1.100" }
+libc = { version = "0.2.150", default-features = false, features = ['rustc-dep-of-std'], public = true }
+compiler_builtins = { version = "0.1.103" }
profiler_builtins = { path = "../profiler_builtins", optional = true }
unwind = { path = "../unwind" }
hashbrown = { version = "0.14", default-features = false, features = ['rustc-dep-of-std'] }
@@ -72,7 +72,7 @@ llvm-libunwind = ["unwind/llvm-libunwind"]
system-llvm-libunwind = ["unwind/system-llvm-libunwind"]
# Make panics and failed asserts immediately abort without formatting any message
-panic_immediate_abort = ["core/panic_immediate_abort"]
+panic_immediate_abort = ["core/panic_immediate_abort", "alloc/panic_immediate_abort"]
# Enable std_detect default features for stdarch/crates/std_detect:
# https://github.com/rust-lang/stdarch/blob/master/crates/std_detect/Cargo.toml
diff --git a/library/std/build.rs b/library/std/build.rs
index 36516978b..ad0a82eab 100644
--- a/library/std/build.rs
+++ b/library/std/build.rs
@@ -3,17 +3,11 @@ use std::env;
fn main() {
println!("cargo:rerun-if-changed=build.rs");
let target = env::var("TARGET").expect("TARGET was not set");
- if target.contains("freebsd") {
- if env::var("RUST_STD_FREEBSD_12_ABI").is_ok() {
- println!("cargo:rustc-cfg=freebsd12");
- } else if env::var("RUST_STD_FREEBSD_13_ABI").is_ok() {
- println!("cargo:rustc-cfg=freebsd12");
- println!("cargo:rustc-cfg=freebsd13");
- }
- } else if target.contains("linux")
+ if target.contains("linux")
|| target.contains("netbsd")
|| target.contains("dragonfly")
|| target.contains("openbsd")
+ || target.contains("freebsd")
|| target.contains("solaris")
|| target.contains("illumos")
|| target.contains("apple-darwin")
@@ -36,6 +30,7 @@ fn main() {
|| target.contains("solid")
|| target.contains("nintendo-3ds")
|| target.contains("vita")
+ || target.contains("aix")
|| target.contains("nto")
|| target.contains("xous")
|| target.contains("hurd")
diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs
index be173a7ac..4d109285d 100644
--- a/library/std/src/collections/hash/map.rs
+++ b/library/std/src/collections/hash/map.rs
@@ -24,7 +24,7 @@ use crate::sys;
/// reasonable best-effort is made to generate this seed from a high quality,
/// secure source of randomness provided by the host without blocking the
/// program. Because of this, the randomness of the seed depends on the output
-/// quality of the system's random number generator when the seed is created.
+/// quality of the system's random number coroutine when the seed is created.
/// In particular, seeds generated when the system's entropy pool is abnormally
/// low such as during system boot may be of a lower quality.
///
diff --git a/library/std/src/collections/hash/set.rs b/library/std/src/collections/hash/set.rs
index 6d85b26af..6a87f6e5f 100644
--- a/library/std/src/collections/hash/set.rs
+++ b/library/std/src/collections/hash/set.rs
@@ -144,7 +144,7 @@ impl<T> HashSet<T, RandomState> {
#[must_use]
#[stable(feature = "rust1", since = "1.0.0")]
pub fn with_capacity(capacity: usize) -> HashSet<T, RandomState> {
- HashSet { base: base::HashSet::with_capacity_and_hasher(capacity, Default::default()) }
+ HashSet::with_capacity_and_hasher(capacity, Default::default())
}
}
diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs
index 73cce35ac..4310e1083 100644
--- a/library/std/src/fs.rs
+++ b/library/std/src/fs.rs
@@ -184,11 +184,12 @@ pub struct DirEntry(fs_imp::DirEntry);
/// ```
#[derive(Clone, Debug)]
#[stable(feature = "rust1", since = "1.0.0")]
+#[cfg_attr(not(test), rustc_diagnostic_item = "FsOpenOptions")]
pub struct OpenOptions(fs_imp::OpenOptions);
/// Representation of the various timestamps on a file.
#[derive(Copy, Clone, Debug, Default)]
-#[unstable(feature = "file_set_times", issue = "98245")]
+#[stable(feature = "file_set_times", since = "1.75.0")]
pub struct FileTimes(fs_imp::FileTimes);
/// Representation of the various permissions on a file.
@@ -201,6 +202,7 @@ pub struct FileTimes(fs_imp::FileTimes);
/// [`PermissionsExt`]: crate::os::unix::fs::PermissionsExt
#[derive(Clone, PartialEq, Eq, Debug)]
#[stable(feature = "rust1", since = "1.0.0")]
+#[cfg_attr(not(test), rustc_diagnostic_item = "FsPermissions")]
pub struct Permissions(fs_imp::FilePermissions);
/// A structure representing a type of file with accessors for each file type.
@@ -674,8 +676,6 @@ impl File {
/// # Examples
///
/// ```no_run
- /// #![feature(file_set_times)]
- ///
/// fn main() -> std::io::Result<()> {
/// use std::fs::{self, File, FileTimes};
///
@@ -688,7 +688,7 @@ impl File {
/// Ok(())
/// }
/// ```
- #[unstable(feature = "file_set_times", issue = "98245")]
+ #[stable(feature = "file_set_times", since = "1.75.0")]
#[doc(alias = "futimens")]
#[doc(alias = "futimes")]
#[doc(alias = "SetFileTime")]
@@ -699,7 +699,7 @@ impl File {
/// Changes the modification time of the underlying file.
///
/// This is an alias for `set_times(FileTimes::new().set_modified(time))`.
- #[unstable(feature = "file_set_times", issue = "98245")]
+ #[stable(feature = "file_set_times", since = "1.75.0")]
#[inline]
pub fn set_modified(&self, time: SystemTime) -> io::Result<()> {
self.set_times(FileTimes::new().set_modified(time))
@@ -1413,20 +1413,20 @@ impl FileTimes {
/// Create a new `FileTimes` with no times set.
///
/// Using the resulting `FileTimes` in [`File::set_times`] will not modify any timestamps.
- #[unstable(feature = "file_set_times", issue = "98245")]
+ #[stable(feature = "file_set_times", since = "1.75.0")]
pub fn new() -> Self {
Self::default()
}
/// Set the last access time of a file.
- #[unstable(feature = "file_set_times", issue = "98245")]
+ #[stable(feature = "file_set_times", since = "1.75.0")]
pub fn set_accessed(mut self, t: SystemTime) -> Self {
self.0.set_accessed(t.into_inner());
self
}
/// Set the last modified time of a file.
- #[unstable(feature = "file_set_times", issue = "98245")]
+ #[stable(feature = "file_set_times", since = "1.75.0")]
pub fn set_modified(mut self, t: SystemTime) -> Self {
self.0.set_modified(t.into_inner());
self
@@ -1440,7 +1440,7 @@ impl AsInnerMut<fs_imp::FileTimes> for FileTimes {
}
// For implementing OS extension traits in `std::os`
-#[unstable(feature = "file_set_times", issue = "98245")]
+#[stable(feature = "file_set_times", since = "1.75.0")]
impl Sealed for FileTimes {}
impl Permissions {
@@ -2241,6 +2241,7 @@ pub fn canonicalize<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> {
/// ```
#[doc(alias = "mkdir")]
#[stable(feature = "rust1", since = "1.0.0")]
+#[cfg_attr(not(test), rustc_diagnostic_item = "fs_create_dir")]
pub fn create_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
DirBuilder::new().create(path.as_ref())
}
diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs
index d74f0f00e..547a7b705 100644
--- a/library/std/src/fs/tests.rs
+++ b/library/std/src/fs/tests.rs
@@ -1707,3 +1707,89 @@ fn test_file_times() {
assert_eq!(metadata.created().unwrap(), created);
}
}
+
+#[test]
+#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))]
+fn test_file_times_pre_epoch_with_nanos() {
+ #[cfg(target_os = "ios")]
+ use crate::os::ios::fs::FileTimesExt;
+ #[cfg(target_os = "macos")]
+ use crate::os::macos::fs::FileTimesExt;
+ #[cfg(target_os = "tvos")]
+ use crate::os::tvos::fs::FileTimesExt;
+ #[cfg(target_os = "watchos")]
+ use crate::os::watchos::fs::FileTimesExt;
+
+ let tmp = tmpdir();
+ let file = File::create(tmp.join("foo")).unwrap();
+
+ for (accessed, modified, created) in [
+ // The first round is to set filetimes to something we know works, but this time
+ // it's validated with nanoseconds as well which probe the numeric boundary.
+ (
+ SystemTime::UNIX_EPOCH + Duration::new(12345, 1),
+ SystemTime::UNIX_EPOCH + Duration::new(54321, 100_000_000),
+ SystemTime::UNIX_EPOCH + Duration::new(32123, 999_999_999),
+ ),
+ // The second rounds uses pre-epoch dates along with nanoseconds that probe
+ // the numeric boundary.
+ (
+ SystemTime::UNIX_EPOCH - Duration::new(1, 1),
+ SystemTime::UNIX_EPOCH - Duration::new(60, 100_000_000),
+ SystemTime::UNIX_EPOCH - Duration::new(3600, 999_999_999),
+ ),
+ ] {
+ let mut times = FileTimes::new();
+ times = times.set_accessed(accessed).set_modified(modified).set_created(created);
+ file.set_times(times).unwrap();
+
+ let metadata = file.metadata().unwrap();
+ assert_eq!(metadata.accessed().unwrap(), accessed);
+ assert_eq!(metadata.modified().unwrap(), modified);
+ assert_eq!(metadata.created().unwrap(), created);
+ }
+}
+
+#[test]
+#[cfg(windows)]
+fn windows_unix_socket_exists() {
+ use crate::sys::{c, net};
+ use crate::{mem, ptr};
+
+ let tmp = tmpdir();
+ let socket_path = tmp.join("socket");
+
+ // std doesn't currently support Unix sockets on Windows so manually create one here.
+ net::init();
+ unsafe {
+ let socket = c::WSASocketW(
+ c::AF_UNIX as i32,
+ c::SOCK_STREAM,
+ 0,
+ ptr::null_mut(),
+ 0,
+ c::WSA_FLAG_OVERLAPPED | c::WSA_FLAG_NO_HANDLE_INHERIT,
+ );
+ // AF_UNIX is not supported on earlier versions of Windows,
+ // so skip this test if it's unsupported and we're not in CI.
+ if socket == c::INVALID_SOCKET {
+ let error = c::WSAGetLastError();
+ if env::var_os("CI").is_none() && error == c::WSAEAFNOSUPPORT {
+ return;
+ } else {
+ panic!("Creating AF_UNIX socket failed (OS error {error})");
+ }
+ }
+ let mut addr = c::SOCKADDR_UN { sun_family: c::AF_UNIX, sun_path: mem::zeroed() };
+ let bytes = socket_path.as_os_str().as_encoded_bytes();
+ addr.sun_path[..bytes.len()].copy_from_slice(bytes);
+ let len = mem::size_of_val(&addr) as i32;
+ let result = c::bind(socket, ptr::addr_of!(addr).cast::<c::SOCKADDR>(), len);
+ c::closesocket(socket);
+ assert_eq!(result, 0);
+ }
+ // Make sure all ways of testing a file exist work for a Unix socket.
+ assert_eq!(socket_path.exists(), true);
+ assert_eq!(socket_path.try_exists().unwrap(), true);
+ assert_eq!(socket_path.metadata().is_ok(), true);
+}
diff --git a/library/std/src/io/buffered/bufreader.rs b/library/std/src/io/buffered/bufreader.rs
index 7097dfef8..55aafc3db 100644
--- a/library/std/src/io/buffered/bufreader.rs
+++ b/library/std/src/io/buffered/bufreader.rs
@@ -2,7 +2,8 @@ mod buffer;
use crate::fmt;
use crate::io::{
- self, BorrowedCursor, BufRead, IoSliceMut, Read, Seek, SeekFrom, SizeHint, DEFAULT_BUF_SIZE,
+ self, uninlined_slow_read_byte, BorrowedCursor, BufRead, IoSliceMut, Read, Seek, SeekFrom,
+ SizeHint, SpecReadByte, DEFAULT_BUF_SIZE,
};
use buffer::Buffer;
@@ -259,6 +260,22 @@ impl<R: ?Sized + Seek> BufReader<R> {
}
}
+impl<R> SpecReadByte for BufReader<R>
+where
+ Self: Read,
+{
+ #[inline]
+ fn spec_read_byte(&mut self) -> Option<io::Result<u8>> {
+ let mut byte = 0;
+ if self.buf.consume_with(1, |claimed| byte = claimed[0]) {
+ return Some(Ok(byte));
+ }
+
+ // Fallback case, only reached once per buffer refill.
+ uninlined_slow_read_byte(self)
+ }
+}
+
#[stable(feature = "rust1", since = "1.0.0")]
impl<R: ?Sized + Read> Read for BufReader<R> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
@@ -269,10 +286,8 @@ impl<R: ?Sized + Read> Read for BufReader<R> {
self.discard_buffer();
return self.inner.read(buf);
}
- let nread = {
- let mut rem = self.fill_buf()?;
- rem.read(buf)?
- };
+ let mut rem = self.fill_buf()?;
+ let nread = rem.read(buf)?;
self.consume(nread);
Ok(nread)
}
diff --git a/library/std/src/io/copy.rs b/library/std/src/io/copy.rs
index eafd078a7..4d51a719f 100644
--- a/library/std/src/io/copy.rs
+++ b/library/std/src/io/copy.rs
@@ -1,5 +1,7 @@
use super::{BorrowedBuf, BufReader, BufWriter, Read, Result, Write, DEFAULT_BUF_SIZE};
use crate::alloc::Allocator;
+use crate::cmp;
+use crate::cmp::min;
use crate::collections::VecDeque;
use crate::io::IoSlice;
use crate::mem::MaybeUninit;
@@ -254,6 +256,78 @@ impl<I: Write + ?Sized> BufferedWriterSpec for BufWriter<I> {
}
}
+impl<A: Allocator> BufferedWriterSpec for Vec<u8, A> {
+ fn buffer_size(&self) -> usize {
+ cmp::max(DEFAULT_BUF_SIZE, self.capacity() - self.len())
+ }
+
+ fn copy_from<R: Read + ?Sized>(&mut self, reader: &mut R) -> Result<u64> {
+ let mut bytes = 0;
+
+ // avoid inflating empty/small vecs before we have determined that there's anything to read
+ if self.capacity() < DEFAULT_BUF_SIZE {
+ let stack_read_limit = DEFAULT_BUF_SIZE as u64;
+ bytes = stack_buffer_copy(&mut reader.take(stack_read_limit), self)?;
+ // fewer bytes than requested -> EOF reached
+ if bytes < stack_read_limit {
+ return Ok(bytes);
+ }
+ }
+
+ // don't immediately offer the vec's whole spare capacity, otherwise
+ // we might have to fully initialize it if the reader doesn't have a custom read_buf() impl
+ let mut max_read_size = DEFAULT_BUF_SIZE;
+
+ loop {
+ self.reserve(DEFAULT_BUF_SIZE);
+ let mut initialized_spare_capacity = 0;
+
+ loop {
+ let buf = self.spare_capacity_mut();
+ let read_size = min(max_read_size, buf.len());
+ let mut buf = BorrowedBuf::from(&mut buf[..read_size]);
+ // SAFETY: init is either 0 or the init_len from the previous iteration.
+ unsafe {
+ buf.set_init(initialized_spare_capacity);
+ }
+ match reader.read_buf(buf.unfilled()) {
+ Ok(()) => {
+ let bytes_read = buf.len();
+
+ // EOF
+ if bytes_read == 0 {
+ return Ok(bytes);
+ }
+
+ // the reader is returning short reads but it doesn't call ensure_init()
+ if buf.init_len() < buf.capacity() {
+ max_read_size = usize::MAX;
+ }
+ // the reader hasn't returned short reads so far
+ if bytes_read == buf.capacity() {
+ max_read_size *= 2;
+ }
+
+ initialized_spare_capacity = buf.init_len() - bytes_read;
+ bytes += bytes_read as u64;
+ // SAFETY: BorrowedBuf guarantees all of its filled bytes are init
+ // and the number of read bytes can't exceed the spare capacity since
+ // that's what the buffer is borrowing from.
+ unsafe { self.set_len(self.len() + bytes_read) };
+
+ // spare capacity full, reserve more
+ if self.len() == self.capacity() {
+ break;
+ }
+ }
+ Err(e) if e.is_interrupted() => continue,
+ Err(e) => return Err(e),
+ }
+ }
+ }
+ }
+}
+
fn stack_buffer_copy<R: Read + ?Sized, W: Write + ?Sized>(
reader: &mut R,
writer: &mut W,
diff --git a/library/std/src/io/copy/tests.rs b/library/std/src/io/copy/tests.rs
index d9998e87c..af137eaf8 100644
--- a/library/std/src/io/copy/tests.rs
+++ b/library/std/src/io/copy/tests.rs
@@ -81,6 +81,18 @@ fn copy_specializes_bufreader() {
}
#[test]
+fn copy_specializes_to_vec() {
+ let cap = 123456;
+ let mut source = ShortReader { cap, observed_buffer: 0, read_size: 1337 };
+ let mut sink = Vec::new();
+ assert_eq!(cap as u64, io::copy(&mut source, &mut sink).unwrap());
+ assert!(
+ source.observed_buffer > DEFAULT_BUF_SIZE,
+ "expected a large buffer to be provided to the reader"
+ );
+}
+
+#[test]
fn copy_specializes_from_vecdeque() {
let mut source = VecDeque::with_capacity(100 * 1024);
for _ in 0..20 * 1024 {
diff --git a/library/std/src/io/impls.rs b/library/std/src/io/impls.rs
index a7428776d..d8c8d933e 100644
--- a/library/std/src/io/impls.rs
+++ b/library/std/src/io/impls.rs
@@ -475,6 +475,24 @@ impl<A: Allocator> Read for VecDeque<u8, A> {
}
}
+/// BufRead is implemented for `VecDeque<u8>` by reading bytes from the front of the `VecDeque`.
+#[stable(feature = "vecdeque_buf_read", since = "1.75.0")]
+impl<A: Allocator> BufRead for VecDeque<u8, A> {
+ /// Returns the contents of the "front" slice as returned by
+ /// [`as_slices`][`VecDeque::as_slices`]. If the contained byte slices of the `VecDeque` are
+ /// discontiguous, multiple calls to `fill_buf` will be needed to read the entire content.
+ #[inline]
+ fn fill_buf(&mut self) -> io::Result<&[u8]> {
+ let (front, _) = self.as_slices();
+ Ok(front)
+ }
+
+ #[inline]
+ fn consume(&mut self, amt: usize) {
+ self.drain(..amt);
+ }
+}
+
/// Write is implemented for `VecDeque<u8>` by appending to the `VecDeque`, growing it as needed.
#[stable(feature = "vecdeque_read_write", since = "1.63.0")]
impl<A: Allocator> Write for VecDeque<u8, A> {
@@ -510,3 +528,17 @@ impl<A: Allocator> Write for VecDeque<u8, A> {
Ok(())
}
}
+
+#[unstable(feature = "read_buf", issue = "78485")]
+impl<'a> io::Write for core::io::BorrowedCursor<'a> {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ let amt = cmp::min(buf.len(), self.capacity());
+ self.append(&buf[..amt]);
+ Ok(amt)
+ }
+
+ #[inline]
+ fn flush(&mut self) -> io::Result<()> {
+ Ok(())
+ }
+}
diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs
index 604b795cd..7d70a0bac 100644
--- a/library/std/src/io/mod.rs
+++ b/library/std/src/io/mod.rs
@@ -317,6 +317,7 @@ pub use self::stdio::set_output_capture;
#[stable(feature = "is_terminal", since = "1.70.0")]
pub use self::stdio::IsTerminal;
#[unstable(feature = "print_internals", issue = "none")]
+#[doc(hidden)]
pub use self::stdio::{_eprint, _print};
#[stable(feature = "rust1", since = "1.0.0")]
pub use self::{
@@ -329,7 +330,7 @@ pub use self::{
};
#[unstable(feature = "read_buf", issue = "78485")]
-pub use self::readbuf::{BorrowedBuf, BorrowedCursor};
+pub use core::io::{BorrowedBuf, BorrowedCursor};
pub(crate) use error::const_io_error;
mod buffered;
@@ -338,7 +339,6 @@ mod cursor;
mod error;
mod impls;
pub mod prelude;
-mod readbuf;
mod stdio;
mod util;
@@ -513,8 +513,7 @@ pub(crate) fn default_read_exact<R: Read + ?Sized>(this: &mut R, mut buf: &mut [
match this.read(buf) {
Ok(0) => break,
Ok(n) => {
- let tmp = buf;
- buf = &mut tmp[n..];
+ buf = &mut buf[n..];
}
Err(ref e) if e.is_interrupted() => {}
Err(e) => return Err(e),
@@ -1141,10 +1140,10 @@ pub fn read_to_string<R: Read>(mut reader: R) -> Result<String> {
#[repr(transparent)]
pub struct IoSliceMut<'a>(sys::io::IoSliceMut<'a>);
-#[stable(feature = "iovec-send-sync", since = "1.44.0")]
+#[stable(feature = "iovec_send_sync", since = "1.44.0")]
unsafe impl<'a> Send for IoSliceMut<'a> {}
-#[stable(feature = "iovec-send-sync", since = "1.44.0")]
+#[stable(feature = "iovec_send_sync", since = "1.44.0")]
unsafe impl<'a> Sync for IoSliceMut<'a> {}
#[stable(feature = "iovec", since = "1.36.0")]
@@ -1284,10 +1283,10 @@ impl<'a> DerefMut for IoSliceMut<'a> {
#[repr(transparent)]
pub struct IoSlice<'a>(sys::io::IoSlice<'a>);
-#[stable(feature = "iovec-send-sync", since = "1.44.0")]
+#[stable(feature = "iovec_send_sync", since = "1.44.0")]
unsafe impl<'a> Send for IoSlice<'a> {}
-#[stable(feature = "iovec-send-sync", since = "1.44.0")]
+#[stable(feature = "iovec_send_sync", since = "1.44.0")]
unsafe impl<'a> Sync for IoSlice<'a> {}
#[stable(feature = "iovec", since = "1.36.0")]
@@ -1830,6 +1829,7 @@ pub trait Write {
/// }
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
+#[cfg_attr(not(test), rustc_diagnostic_item = "IoSeek")]
pub trait Seek {
/// Seek to an offset, in bytes, in a stream.
///
@@ -2777,23 +2777,55 @@ pub struct Bytes<R> {
impl<R: Read> Iterator for Bytes<R> {
type Item = Result<u8>;
+ // Not `#[inline]`. This function gets inlined even without it, but having
+ // the inline annotation can result in worse code generation. See #116785.
fn next(&mut self) -> Option<Result<u8>> {
- let mut byte = 0;
- loop {
- return match self.inner.read(slice::from_mut(&mut byte)) {
- Ok(0) => None,
- Ok(..) => Some(Ok(byte)),
- Err(ref e) if e.is_interrupted() => continue,
- Err(e) => Some(Err(e)),
- };
- }
+ SpecReadByte::spec_read_byte(&mut self.inner)
}
+ #[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
SizeHint::size_hint(&self.inner)
}
}
+/// For the specialization of `Bytes::next`.
+trait SpecReadByte {
+ fn spec_read_byte(&mut self) -> Option<Result<u8>>;
+}
+
+impl<R> SpecReadByte for R
+where
+ Self: Read,
+{
+ #[inline]
+ default fn spec_read_byte(&mut self) -> Option<Result<u8>> {
+ inlined_slow_read_byte(self)
+ }
+}
+
+/// Read a single byte in a slow, generic way. This is used by the default
+/// `spec_read_byte`.
+#[inline]
+fn inlined_slow_read_byte<R: Read>(reader: &mut R) -> Option<Result<u8>> {
+ let mut byte = 0;
+ loop {
+ return match reader.read(slice::from_mut(&mut byte)) {
+ Ok(0) => None,
+ Ok(..) => Some(Ok(byte)),
+ Err(ref e) if e.is_interrupted() => continue,
+ Err(e) => Some(Err(e)),
+ };
+ }
+}
+
+// Used by `BufReader::spec_read_byte`, for which the `inline(ever)` is
+// important.
+#[inline(never)]
+fn uninlined_slow_read_byte<R: Read>(reader: &mut R) -> Option<Result<u8>> {
+ inlined_slow_read_byte(reader)
+}
+
trait SizeHint {
fn lower_bound(&self) -> usize;
@@ -2893,6 +2925,7 @@ impl<B: BufRead> Iterator for Split<B> {
/// [`lines`]: BufRead::lines
#[stable(feature = "rust1", since = "1.0.0")]
#[derive(Debug)]
+#[cfg_attr(not(test), rustc_diagnostic_item = "IoLines")]
pub struct Lines<B> {
buf: B,
}
diff --git a/library/std/src/io/readbuf.rs b/library/std/src/io/readbuf.rs
deleted file mode 100644
index 034ddd8df..000000000
--- a/library/std/src/io/readbuf.rs
+++ /dev/null
@@ -1,317 +0,0 @@
-#![unstable(feature = "read_buf", issue = "78485")]
-
-#[cfg(test)]
-mod tests;
-
-use crate::fmt::{self, Debug, Formatter};
-use crate::io::{Result, Write};
-use crate::mem::{self, MaybeUninit};
-use crate::{cmp, ptr};
-
-/// A borrowed byte buffer which is incrementally filled and initialized.
-///
-/// This type is a sort of "double cursor". It tracks three regions in the buffer: a region at the beginning of the
-/// buffer that has been logically filled with data, a region that has been initialized at some point but not yet
-/// logically filled, and a region at the end that is fully uninitialized. The filled region is guaranteed to be a
-/// subset of the initialized region.
-///
-/// In summary, the contents of the buffer can be visualized as:
-/// ```not_rust
-/// [ capacity ]
-/// [ filled | unfilled ]
-/// [ initialized | uninitialized ]
-/// ```
-///
-/// A `BorrowedBuf` is created around some existing data (or capacity for data) via a unique reference
-/// (`&mut`). The `BorrowedBuf` can be configured (e.g., using `clear` or `set_init`), but cannot be
-/// directly written. To write into the buffer, use `unfilled` to create a `BorrowedCursor`. The cursor
-/// has write-only access to the unfilled portion of the buffer (you can think of it as a
-/// write-only iterator).
-///
-/// The lifetime `'data` is a bound on the lifetime of the underlying data.
-pub struct BorrowedBuf<'data> {
- /// The buffer's underlying data.
- buf: &'data mut [MaybeUninit<u8>],
- /// The length of `self.buf` which is known to be filled.
- filled: usize,
- /// The length of `self.buf` which is known to be initialized.
- init: usize,
-}
-
-impl Debug for BorrowedBuf<'_> {
- fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
- f.debug_struct("BorrowedBuf")
- .field("init", &self.init)
- .field("filled", &self.filled)
- .field("capacity", &self.capacity())
- .finish()
- }
-}
-
-/// Create a new `BorrowedBuf` from a fully initialized slice.
-impl<'data> From<&'data mut [u8]> for BorrowedBuf<'data> {
- #[inline]
- fn from(slice: &'data mut [u8]) -> BorrowedBuf<'data> {
- let len = slice.len();
-
- BorrowedBuf {
- // SAFETY: initialized data never becoming uninitialized is an invariant of BorrowedBuf
- buf: unsafe { (slice as *mut [u8]).as_uninit_slice_mut().unwrap() },
- filled: 0,
- init: len,
- }
- }
-}
-
-/// Create a new `BorrowedBuf` from an uninitialized buffer.
-///
-/// Use `set_init` if part of the buffer is known to be already initialized.
-impl<'data> From<&'data mut [MaybeUninit<u8>]> for BorrowedBuf<'data> {
- #[inline]
- fn from(buf: &'data mut [MaybeUninit<u8>]) -> BorrowedBuf<'data> {
- BorrowedBuf { buf, filled: 0, init: 0 }
- }
-}
-
-impl<'data> BorrowedBuf<'data> {
- /// Returns the total capacity of the buffer.
- #[inline]
- pub fn capacity(&self) -> usize {
- self.buf.len()
- }
-
- /// Returns the length of the filled part of the buffer.
- #[inline]
- pub fn len(&self) -> usize {
- self.filled
- }
-
- /// Returns the length of the initialized part of the buffer.
- #[inline]
- pub fn init_len(&self) -> usize {
- self.init
- }
-
- /// Returns a shared reference to the filled portion of the buffer.
- #[inline]
- pub fn filled(&self) -> &[u8] {
- // SAFETY: We only slice the filled part of the buffer, which is always valid
- unsafe { MaybeUninit::slice_assume_init_ref(&self.buf[0..self.filled]) }
- }
-
- /// Returns a mutable reference to the filled portion of the buffer.
- #[inline]
- pub fn filled_mut(&mut self) -> &mut [u8] {
- // SAFETY: We only slice the filled part of the buffer, which is always valid
- unsafe { MaybeUninit::slice_assume_init_mut(&mut self.buf[0..self.filled]) }
- }
-
- /// Returns a cursor over the unfilled part of the buffer.
- #[inline]
- pub fn unfilled<'this>(&'this mut self) -> BorrowedCursor<'this> {
- BorrowedCursor {
- start: self.filled,
- // SAFETY: we never assign into `BorrowedCursor::buf`, so treating its
- // lifetime covariantly is safe.
- buf: unsafe {
- mem::transmute::<&'this mut BorrowedBuf<'data>, &'this mut BorrowedBuf<'this>>(self)
- },
- }
- }
-
- /// Clears the buffer, resetting the filled region to empty.
- ///
- /// The number of initialized bytes is not changed, and the contents of the buffer are not modified.
- #[inline]
- pub fn clear(&mut self) -> &mut Self {
- self.filled = 0;
- self
- }
-
- /// Asserts that the first `n` bytes of the buffer are initialized.
- ///
- /// `BorrowedBuf` assumes that bytes are never de-initialized, so this method does nothing when called with fewer
- /// bytes than are already known to be initialized.
- ///
- /// # Safety
- ///
- /// The caller must ensure that the first `n` unfilled bytes of the buffer have already been initialized.
- #[inline]
- pub unsafe fn set_init(&mut self, n: usize) -> &mut Self {
- self.init = cmp::max(self.init, n);
- self
- }
-}
-
-/// A writeable view of the unfilled portion of a [`BorrowedBuf`](BorrowedBuf).
-///
-/// Provides access to the initialized and uninitialized parts of the underlying `BorrowedBuf`.
-/// Data can be written directly to the cursor by using [`append`](BorrowedCursor::append) or
-/// indirectly by getting a slice of part or all of the cursor and writing into the slice. In the
-/// indirect case, the caller must call [`advance`](BorrowedCursor::advance) after writing to inform
-/// the cursor how many bytes have been written.
-///
-/// Once data is written to the cursor, it becomes part of the filled portion of the underlying
-/// `BorrowedBuf` and can no longer be accessed or re-written by the cursor. I.e., the cursor tracks
-/// the unfilled part of the underlying `BorrowedBuf`.
-///
-/// The lifetime `'a` is a bound on the lifetime of the underlying buffer (which means it is a bound
-/// on the data in that buffer by transitivity).
-#[derive(Debug)]
-pub struct BorrowedCursor<'a> {
- /// The underlying buffer.
- // Safety invariant: we treat the type of buf as covariant in the lifetime of `BorrowedBuf` when
- // we create a `BorrowedCursor`. This is only safe if we never replace `buf` by assigning into
- // it, so don't do that!
- buf: &'a mut BorrowedBuf<'a>,
- /// The length of the filled portion of the underlying buffer at the time of the cursor's
- /// creation.
- start: usize,
-}
-
-impl<'a> BorrowedCursor<'a> {
- /// Reborrow this cursor by cloning it with a smaller lifetime.
- ///
- /// Since a cursor maintains unique access to its underlying buffer, the borrowed cursor is
- /// not accessible while the new cursor exists.
- #[inline]
- pub fn reborrow<'this>(&'this mut self) -> BorrowedCursor<'this> {
- BorrowedCursor {
- // SAFETY: we never assign into `BorrowedCursor::buf`, so treating its
- // lifetime covariantly is safe.
- buf: unsafe {
- mem::transmute::<&'this mut BorrowedBuf<'a>, &'this mut BorrowedBuf<'this>>(
- self.buf,
- )
- },
- start: self.start,
- }
- }
-
- /// Returns the available space in the cursor.
- #[inline]
- pub fn capacity(&self) -> usize {
- self.buf.capacity() - self.buf.filled
- }
-
- /// Returns the number of bytes written to this cursor since it was created from a `BorrowedBuf`.
- ///
- /// Note that if this cursor is a reborrowed clone of another, then the count returned is the
- /// count written via either cursor, not the count since the cursor was reborrowed.
- #[inline]
- pub fn written(&self) -> usize {
- self.buf.filled - self.start
- }
-
- /// Returns a shared reference to the initialized portion of the cursor.
- #[inline]
- pub fn init_ref(&self) -> &[u8] {
- // SAFETY: We only slice the initialized part of the buffer, which is always valid
- unsafe { MaybeUninit::slice_assume_init_ref(&self.buf.buf[self.buf.filled..self.buf.init]) }
- }
-
- /// Returns a mutable reference to the initialized portion of the cursor.
- #[inline]
- pub fn init_mut(&mut self) -> &mut [u8] {
- // SAFETY: We only slice the initialized part of the buffer, which is always valid
- unsafe {
- MaybeUninit::slice_assume_init_mut(&mut self.buf.buf[self.buf.filled..self.buf.init])
- }
- }
-
- /// Returns a mutable reference to the uninitialized part of the cursor.
- ///
- /// It is safe to uninitialize any of these bytes.
- #[inline]
- pub fn uninit_mut(&mut self) -> &mut [MaybeUninit<u8>] {
- &mut self.buf.buf[self.buf.init..]
- }
-
- /// Returns a mutable reference to the whole cursor.
- ///
- /// # Safety
- ///
- /// The caller must not uninitialize any bytes in the initialized portion of the cursor.
- #[inline]
- pub unsafe fn as_mut(&mut self) -> &mut [MaybeUninit<u8>] {
- &mut self.buf.buf[self.buf.filled..]
- }
-
- /// Advance the cursor by asserting that `n` bytes have been filled.
- ///
- /// After advancing, the `n` bytes are no longer accessible via the cursor and can only be
- /// accessed via the underlying buffer. I.e., the buffer's filled portion grows by `n` elements
- /// and its unfilled portion (and the capacity of this cursor) shrinks by `n` elements.
- ///
- /// # Safety
- ///
- /// The caller must ensure that the first `n` bytes of the cursor have been properly
- /// initialised.
- #[inline]
- pub unsafe fn advance(&mut self, n: usize) -> &mut Self {
- self.buf.filled += n;
- self.buf.init = cmp::max(self.buf.init, self.buf.filled);
- self
- }
-
- /// Initializes all bytes in the cursor.
- #[inline]
- pub fn ensure_init(&mut self) -> &mut Self {
- let uninit = self.uninit_mut();
- // SAFETY: 0 is a valid value for MaybeUninit<u8> and the length matches the allocation
- // since it is comes from a slice reference.
- unsafe {
- ptr::write_bytes(uninit.as_mut_ptr(), 0, uninit.len());
- }
- self.buf.init = self.buf.capacity();
-
- self
- }
-
- /// Asserts that the first `n` unfilled bytes of the cursor are initialized.
- ///
- /// `BorrowedBuf` assumes that bytes are never de-initialized, so this method does nothing when
- /// called with fewer bytes than are already known to be initialized.
- ///
- /// # Safety
- ///
- /// The caller must ensure that the first `n` bytes of the buffer have already been initialized.
- #[inline]
- pub unsafe fn set_init(&mut self, n: usize) -> &mut Self {
- self.buf.init = cmp::max(self.buf.init, self.buf.filled + n);
- self
- }
-
- /// Appends data to the cursor, advancing position within its buffer.
- ///
- /// # Panics
- ///
- /// Panics if `self.capacity()` is less than `buf.len()`.
- #[inline]
- pub fn append(&mut self, buf: &[u8]) {
- assert!(self.capacity() >= buf.len());
-
- // SAFETY: we do not de-initialize any of the elements of the slice
- unsafe {
- MaybeUninit::write_slice(&mut self.as_mut()[..buf.len()], buf);
- }
-
- // SAFETY: We just added the entire contents of buf to the filled section.
- unsafe {
- self.set_init(buf.len());
- }
- self.buf.filled += buf.len();
- }
-}
-
-impl<'a> Write for BorrowedCursor<'a> {
- fn write(&mut self, buf: &[u8]) -> Result<usize> {
- self.append(buf);
- Ok(buf.len())
- }
-
- #[inline]
- fn flush(&mut self) -> Result<()> {
- Ok(())
- }
-}
diff --git a/library/std/src/io/readbuf/tests.rs b/library/std/src/io/readbuf/tests.rs
deleted file mode 100644
index 89a2f6b22..000000000
--- a/library/std/src/io/readbuf/tests.rs
+++ /dev/null
@@ -1,175 +0,0 @@
-use super::BorrowedBuf;
-use crate::mem::MaybeUninit;
-
-/// Test that BorrowedBuf has the correct numbers when created with new
-#[test]
-fn new() {
- let buf: &mut [_] = &mut [0; 16];
- let mut rbuf: BorrowedBuf<'_> = buf.into();
-
- assert_eq!(rbuf.filled().len(), 0);
- assert_eq!(rbuf.init_len(), 16);
- assert_eq!(rbuf.capacity(), 16);
- assert_eq!(rbuf.unfilled().capacity(), 16);
-}
-
-/// Test that BorrowedBuf has the correct numbers when created with uninit
-#[test]
-fn uninit() {
- let buf: &mut [_] = &mut [MaybeUninit::uninit(); 16];
- let mut rbuf: BorrowedBuf<'_> = buf.into();
-
- assert_eq!(rbuf.filled().len(), 0);
- assert_eq!(rbuf.init_len(), 0);
- assert_eq!(rbuf.capacity(), 16);
- assert_eq!(rbuf.unfilled().capacity(), 16);
-}
-
-#[test]
-fn initialize_unfilled() {
- let buf: &mut [_] = &mut [MaybeUninit::uninit(); 16];
- let mut rbuf: BorrowedBuf<'_> = buf.into();
-
- rbuf.unfilled().ensure_init();
-
- assert_eq!(rbuf.init_len(), 16);
-}
-
-#[test]
-fn advance_filled() {
- let buf: &mut [_] = &mut [0; 16];
- let mut rbuf: BorrowedBuf<'_> = buf.into();
-
- unsafe {
- rbuf.unfilled().advance(1);
- }
-
- assert_eq!(rbuf.filled().len(), 1);
- assert_eq!(rbuf.unfilled().capacity(), 15);
-}
-
-#[test]
-fn clear() {
- let buf: &mut [_] = &mut [255; 16];
- let mut rbuf: BorrowedBuf<'_> = buf.into();
-
- unsafe {
- rbuf.unfilled().advance(16);
- }
-
- assert_eq!(rbuf.filled().len(), 16);
- assert_eq!(rbuf.unfilled().capacity(), 0);
-
- rbuf.clear();
-
- assert_eq!(rbuf.filled().len(), 0);
- assert_eq!(rbuf.unfilled().capacity(), 16);
-
- assert_eq!(rbuf.unfilled().init_ref(), [255; 16]);
-}
-
-#[test]
-fn set_init() {
- let buf: &mut [_] = &mut [MaybeUninit::uninit(); 16];
- let mut rbuf: BorrowedBuf<'_> = buf.into();
-
- unsafe {
- rbuf.set_init(8);
- }
-
- assert_eq!(rbuf.init_len(), 8);
-
- unsafe {
- rbuf.unfilled().advance(4);
- }
-
- unsafe {
- rbuf.set_init(2);
- }
-
- assert_eq!(rbuf.init_len(), 8);
-
- unsafe {
- rbuf.set_init(8);
- }
-
- assert_eq!(rbuf.init_len(), 8);
-}
-
-#[test]
-fn append() {
- let buf: &mut [_] = &mut [MaybeUninit::new(255); 16];
- let mut rbuf: BorrowedBuf<'_> = buf.into();
-
- rbuf.unfilled().append(&[0; 8]);
-
- assert_eq!(rbuf.init_len(), 8);
- assert_eq!(rbuf.filled().len(), 8);
- assert_eq!(rbuf.filled(), [0; 8]);
-
- rbuf.clear();
-
- rbuf.unfilled().append(&[1; 16]);
-
- assert_eq!(rbuf.init_len(), 16);
- assert_eq!(rbuf.filled().len(), 16);
- assert_eq!(rbuf.filled(), [1; 16]);
-}
-
-#[test]
-fn reborrow_written() {
- let buf: &mut [_] = &mut [MaybeUninit::new(0); 32];
- let mut buf: BorrowedBuf<'_> = buf.into();
-
- let mut cursor = buf.unfilled();
- cursor.append(&[1; 16]);
-
- let mut cursor2 = cursor.reborrow();
- cursor2.append(&[2; 16]);
-
- assert_eq!(cursor2.written(), 32);
- assert_eq!(cursor.written(), 32);
-
- assert_eq!(buf.unfilled().written(), 0);
- assert_eq!(buf.init_len(), 32);
- assert_eq!(buf.filled().len(), 32);
- let filled = buf.filled();
- assert_eq!(&filled[..16], [1; 16]);
- assert_eq!(&filled[16..], [2; 16]);
-}
-
-#[test]
-fn cursor_set_init() {
- let buf: &mut [_] = &mut [MaybeUninit::uninit(); 16];
- let mut rbuf: BorrowedBuf<'_> = buf.into();
-
- unsafe {
- rbuf.unfilled().set_init(8);
- }
-
- assert_eq!(rbuf.init_len(), 8);
- assert_eq!(rbuf.unfilled().init_ref().len(), 8);
- assert_eq!(rbuf.unfilled().init_mut().len(), 8);
- assert_eq!(rbuf.unfilled().uninit_mut().len(), 8);
- assert_eq!(unsafe { rbuf.unfilled().as_mut() }.len(), 16);
-
- unsafe {
- rbuf.unfilled().advance(4);
- }
-
- unsafe {
- rbuf.unfilled().set_init(2);
- }
-
- assert_eq!(rbuf.init_len(), 8);
-
- unsafe {
- rbuf.unfilled().set_init(8);
- }
-
- assert_eq!(rbuf.init_len(), 12);
- assert_eq!(rbuf.unfilled().init_ref().len(), 8);
- assert_eq!(rbuf.unfilled().init_mut().len(), 8);
- assert_eq!(rbuf.unfilled().uninit_mut().len(), 4);
- assert_eq!(unsafe { rbuf.unfilled().as_mut() }.len(), 12);
-}
diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs
index 9098d36ee..05b21eeb4 100644
--- a/library/std/src/io/stdio.rs
+++ b/library/std/src/io/stdio.rs
@@ -611,6 +611,7 @@ static STDOUT: OnceLock<ReentrantMutex<RefCell<LineWriter<StdoutRaw>>>> = OnceLo
/// ```
#[must_use]
#[stable(feature = "rust1", since = "1.0.0")]
+#[cfg_attr(not(test), rustc_diagnostic_item = "io_stdout")]
pub fn stdout() -> Stdout {
Stdout {
inner: STDOUT
@@ -847,6 +848,7 @@ pub struct StderrLock<'a> {
/// ```
#[must_use]
#[stable(feature = "rust1", since = "1.0.0")]
+#[cfg_attr(not(test), rustc_diagnostic_item = "io_stderr")]
pub fn stderr() -> Stderr {
// Note that unlike `stdout()` we don't use `at_exit` here to register a
// destructor. Stderr is not buffered, so there's no need to run a
diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs
index f1f0f8b16..425890122 100644
--- a/library/std/src/lib.rs
+++ b/library/std/src/lib.rs
@@ -227,6 +227,7 @@
test(no_crate_inject, attr(deny(warnings))),
test(attr(allow(dead_code, deprecated, unused_variables, unused_mut)))
)]
+#![cfg_attr(not(bootstrap), doc(rust_logo))]
#![doc(cfg_hide(
not(test),
not(any(test, bootstrap)),
@@ -259,7 +260,7 @@
all(target_vendor = "fortanix", target_env = "sgx"),
feature(slice_index_methods, coerce_unsized, sgx_platform)
)]
-#![cfg_attr(windows, feature(round_char_boundary))]
+#![cfg_attr(any(windows, target_os = "uefi"), feature(round_char_boundary))]
#![cfg_attr(target_os = "xous", feature(slice_ptr_len))]
//
// Language features:
@@ -270,6 +271,7 @@
#![feature(allow_internal_unstable)]
#![feature(c_unwind)]
#![feature(cfg_target_thread_local)]
+#![feature(cfi_encoding)]
#![feature(concat_idents)]
#![feature(const_mut_refs)]
#![feature(const_trait_impl)]
@@ -292,6 +294,7 @@
#![feature(needs_panic_runtime)]
#![feature(negative_impls)]
#![feature(never_type)]
+#![feature(no_sanitize)]
#![feature(platform_intrinsics)]
#![feature(prelude_import)]
#![feature(rustc_attrs)]
@@ -307,6 +310,7 @@
// tidy-alphabetical-start
#![feature(char_internals)]
#![feature(core_intrinsics)]
+#![feature(core_io_borrowed_buf)]
#![feature(duration_constants)]
#![feature(error_generic_member_access)]
#![feature(error_in_core)]
@@ -328,7 +332,6 @@
#![feature(panic_can_unwind)]
#![feature(panic_info_message)]
#![feature(panic_internals)]
-#![feature(pointer_byte_offsets)]
#![feature(pointer_is_aligned)]
#![feature(portable_simd)]
#![feature(prelude_2024)]
diff --git a/library/std/src/net/udp.rs b/library/std/src/net/udp.rs
index 227e418b7..60347a11d 100644
--- a/library/std/src/net/udp.rs
+++ b/library/std/src/net/udp.rs
@@ -99,6 +99,16 @@ impl UdpSocket {
///
/// let socket = UdpSocket::bind("127.0.0.1:0").unwrap();
/// ```
+ ///
+ /// Note that `bind` declares the scope of your network connection.
+ /// You can only receive datagrams from and send datagrams to
+ /// participants in that view of the network.
+ /// For instance, binding to a loopback address as in the example
+ /// above will prevent you from sending datagrams to another device
+ /// in your local network.
+ ///
+ /// In order to limit your view of the network the least, `bind` to
+ /// [`Ipv4Addr::UNSPECIFIED`] or [`Ipv6Addr::UNSPECIFIED`].
#[stable(feature = "rust1", since = "1.0.0")]
pub fn bind<A: ToSocketAddrs>(addr: A) -> io::Result<UdpSocket> {
super::each_addr(addr, net_imp::UdpSocket::bind).map(UdpSocket)
@@ -157,7 +167,9 @@ impl UdpSocket {
}
/// Sends data on the socket to the given address. On success, returns the
- /// number of bytes written.
+ /// number of bytes written. Note that the operating system may refuse
+ /// buffers larger than 65507. However, partial writes are not possible
+ /// until buffer sizes above `i32::MAX`.
///
/// Address type can be any implementor of [`ToSocketAddrs`] trait. See its
/// documentation for concrete examples.
@@ -652,12 +664,19 @@ impl UdpSocket {
/// function of a UDP socket is not a useful thing to do: The OS will be
/// unable to determine whether something is listening on the remote
/// address without the application sending data.
+ ///
+ /// If your first `connect` is to a loopback address, subsequent
+ /// `connect`s to non-loopback addresses might fail, depending
+ /// on the platform.
#[stable(feature = "net2_mutators", since = "1.9.0")]
pub fn connect<A: ToSocketAddrs>(&self, addr: A) -> io::Result<()> {
super::each_addr(addr, |addr| self.0.connect(addr))
}
/// Sends data on the socket to the remote address to which it is connected.
+ /// On success, returns the number of bytes written. Note that the operating
+ /// system may refuse buffers larger than 65507. However, partial writes are
+ /// not possible until buffer sizes above `i32::MAX`.
///
/// [`UdpSocket::connect`] will connect this socket to a remote address. This
/// method will fail if the socket is not connected.
diff --git a/library/std/src/os/aix/fs.rs b/library/std/src/os/aix/fs.rs
new file mode 100644
index 000000000..ac9dd45f0
--- /dev/null
+++ b/library/std/src/os/aix/fs.rs
@@ -0,0 +1,348 @@
+//! AIX specific extensions to primitives in the [`std::fs`] module.
+//!
+//! [`std::fs`]: crate::fs
+
+#![stable(feature = "metadata_ext", since = "1.1.0")]
+
+use crate::fs::Metadata;
+use crate::sys_common::AsInner;
+
+/// OS-specific extensions to [`fs::Metadata`].
+///
+/// [`fs::Metadata`]: crate::fs::Metadata
+#[stable(feature = "metadata_ext", since = "1.1.0")]
+pub trait MetadataExt {
+ /// Returns the device ID on which this file resides.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::io;
+ /// use std::os::aix::fs::MetadataExt;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// println!("{}", meta.st_dev());
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_dev(&self) -> u64;
+ /// Returns the inode number.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::io;
+ /// use std::os::aix::fs::MetadataExt;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// println!("{}", meta.st_ino());
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_ino(&self) -> u64;
+ /// Returns the file type and mode.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::io;
+ /// use std::os::aix::fs::MetadataExt;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// println!("{}", meta.st_mode());
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_mode(&self) -> u32;
+ /// Returns the number of hard links to file.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::io;
+ /// use std::os::aix::fs::MetadataExt;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// println!("{}", meta.st_nlink());
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_nlink(&self) -> u64;
+ /// Returns the user ID of the file owner.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::io;
+ /// use std::os::aix::fs::MetadataExt;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// println!("{}", meta.st_uid());
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_uid(&self) -> u32;
+ /// Returns the group ID of the file owner.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::io;
+ /// use std::os::aix::fs::MetadataExt;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// println!("{}", meta.st_gid());
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_gid(&self) -> u32;
+ /// Returns the device ID that this file represents. Only relevant for special file.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::io;
+ /// use std::os::aix::fs::MetadataExt;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// println!("{}", meta.st_rdev());
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_rdev(&self) -> u64;
+ /// Returns the size of the file (if it is a regular file or a symbolic link) in bytes.
+ ///
+ /// The size of a symbolic link is the length of the pathname it contains,
+ /// without a terminating null byte.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::io;
+ /// use std::os::aix::fs::MetadataExt;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// println!("{}", meta.st_size());
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_size(&self) -> u64;
+ /// Returns the last access time of the file, in seconds since Unix Epoch.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::io;
+ /// use std::os::aix::fs::MetadataExt;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// println!("{}", meta.st_atime());
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_atime(&self) -> i64;
+ /// Returns the last access time of the file, in nanoseconds since [`st_atime`].
+ ///
+ /// [`st_atime`]: Self::st_atime
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::io;
+ /// use std::os::aix::fs::MetadataExt;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// println!("{}", meta.st_atime_nsec());
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_atime_nsec(&self) -> i64;
+ /// Returns the last modification time of the file, in seconds since Unix Epoch.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::io;
+ /// use std::os::aix::fs::MetadataExt;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// println!("{}", meta.st_mtime());
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_mtime(&self) -> i64;
+ /// Returns the last modification time of the file, in nanoseconds since [`st_mtime`].
+ ///
+ /// [`st_mtime`]: Self::st_mtime
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::io;
+ /// use std::os::aix::fs::MetadataExt;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// println!("{}", meta.st_mtime_nsec());
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_mtime_nsec(&self) -> i64;
+ /// Returns the last status change time of the file, in seconds since Unix Epoch.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::io;
+ /// use std::os::aix::fs::MetadataExt;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// println!("{}", meta.st_ctime());
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_ctime(&self) -> i64;
+ /// Returns the last status change time of the file, in nanoseconds since [`st_ctime`].
+ ///
+ /// [`st_ctime`]: Self::st_ctime
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::io;
+ /// use std::os::aix::fs::MetadataExt;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// println!("{}", meta.st_ctime_nsec());
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_ctime_nsec(&self) -> i64;
+ /// Returns the "preferred" block size for efficient filesystem I/O.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::io;
+ /// use std::os::aix::fs::MetadataExt;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// println!("{}", meta.st_blksize());
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_blksize(&self) -> u64;
+ /// Returns the number of blocks allocated to the file, 512-byte units.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::io;
+ /// use std::os::aix::fs::MetadataExt;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// println!("{}", meta.st_blocks());
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext2", since = "1.8.0")]
+ fn st_blocks(&self) -> u64;
+}
+
+#[stable(feature = "metadata_ext", since = "1.1.0")]
+impl MetadataExt for Metadata {
+ fn st_dev(&self) -> u64 {
+ self.as_inner().as_inner().st_dev as u64
+ }
+ fn st_ino(&self) -> u64 {
+ self.as_inner().as_inner().st_ino as u64
+ }
+ fn st_mode(&self) -> u32 {
+ self.as_inner().as_inner().st_mode as u32
+ }
+ fn st_nlink(&self) -> u64 {
+ self.as_inner().as_inner().st_nlink as u64
+ }
+ fn st_uid(&self) -> u32 {
+ self.as_inner().as_inner().st_uid as u32
+ }
+ fn st_gid(&self) -> u32 {
+ self.as_inner().as_inner().st_gid as u32
+ }
+ fn st_rdev(&self) -> u64 {
+ self.as_inner().as_inner().st_rdev as u64
+ }
+ fn st_size(&self) -> u64 {
+ self.as_inner().as_inner().st_size as u64
+ }
+ fn st_atime(&self) -> i64 {
+ self.as_inner().as_inner().st_atime.tv_sec as i64
+ }
+ fn st_atime_nsec(&self) -> i64 {
+ self.as_inner().as_inner().st_atime.tv_nsec as i64
+ }
+ fn st_mtime(&self) -> i64 {
+ self.as_inner().as_inner().st_mtime.tv_sec as i64
+ }
+ fn st_mtime_nsec(&self) -> i64 {
+ self.as_inner().as_inner().st_mtime.tv_nsec as i64
+ }
+ fn st_ctime(&self) -> i64 {
+ self.as_inner().as_inner().st_ctime.tv_sec as i64
+ }
+ fn st_ctime_nsec(&self) -> i64 {
+ self.as_inner().as_inner().st_ctime.tv_nsec as i64
+ }
+ fn st_blksize(&self) -> u64 {
+ self.as_inner().as_inner().st_blksize as u64
+ }
+ fn st_blocks(&self) -> u64 {
+ self.as_inner().as_inner().st_blocks as u64
+ }
+}
diff --git a/library/std/src/os/aix/mod.rs b/library/std/src/os/aix/mod.rs
new file mode 100644
index 000000000..7f86a3c77
--- /dev/null
+++ b/library/std/src/os/aix/mod.rs
@@ -0,0 +1,6 @@
+//! AIX specific definitions.
+
+#![stable(feature = "raw_ext", since = "1.1.0")]
+
+pub mod fs;
+pub mod raw;
diff --git a/library/std/src/os/aix/raw.rs b/library/std/src/os/aix/raw.rs
new file mode 100644
index 000000000..b4c8dc72c
--- /dev/null
+++ b/library/std/src/os/aix/raw.rs
@@ -0,0 +1,9 @@
+//! AIX specific raw type definitions.
+
+#![stable(feature = "raw_ext", since = "1.1.0")]
+
+#[stable(feature = "pthread_t", since = "1.8.0")]
+pub use libc::pthread_t;
+
+#[stable(feature = "raw_ext", since = "1.1.0")]
+pub use libc::{blkcnt_t, blksize_t, dev_t, ino_t, mode_t, nlink_t, off_t, stat, time_t};
diff --git a/library/std/src/os/fd/owned.rs b/library/std/src/os/fd/owned.rs
index 81106d6c6..24f2bdcf4 100644
--- a/library/std/src/os/fd/owned.rs
+++ b/library/std/src/os/fd/owned.rs
@@ -97,14 +97,14 @@ impl BorrowedFd<'_> {
// We want to atomically duplicate this file descriptor and set the
// CLOEXEC flag, and currently that's done via F_DUPFD_CLOEXEC. This
// is a POSIX flag that was added to Linux in 2.6.24.
- #[cfg(not(target_os = "espidf"))]
+ #[cfg(not(any(target_os = "espidf", target_os = "vita")))]
let cmd = libc::F_DUPFD_CLOEXEC;
// For ESP-IDF, F_DUPFD is used instead, because the CLOEXEC semantics
// will never be supported, as this is a bare metal framework with
// no capabilities for multi-process execution. While F_DUPFD is also
// not supported yet, it might be (currently it returns ENOSYS).
- #[cfg(target_os = "espidf")]
+ #[cfg(any(target_os = "espidf", target_os = "vita"))]
let cmd = libc::F_DUPFD;
// Avoid using file descriptors below 3 as they are used for stdio
@@ -119,7 +119,7 @@ impl BorrowedFd<'_> {
pub fn try_clone_to_owned(&self) -> crate::io::Result<OwnedFd> {
Err(crate::io::const_io_error!(
crate::io::ErrorKind::Unsupported,
- "operation not supported on WASI yet",
+ "operation not supported on this platform",
))
}
}
diff --git a/library/std/src/os/freebsd/fs.rs b/library/std/src/os/freebsd/fs.rs
index 8db3a950c..5689a82e0 100644
--- a/library/std/src/os/freebsd/fs.rs
+++ b/library/std/src/os/freebsd/fs.rs
@@ -76,12 +76,7 @@ impl MetadataExt for Metadata {
fn as_raw_stat(&self) -> &raw::stat {
// The methods below use libc::stat, so they work fine when libc is built with FreeBSD 12 ABI.
// This method would just return nonsense.
- #[cfg(freebsd12)]
panic!("as_raw_stat not supported with FreeBSD 12 ABI");
- #[cfg(not(freebsd12))]
- unsafe {
- &*(self.as_inner().as_inner() as *const libc::stat as *const raw::stat)
- }
}
fn st_dev(&self) -> u64 {
self.as_inner().as_inner().st_dev as u64
@@ -143,12 +138,7 @@ impl MetadataExt for Metadata {
fn st_flags(&self) -> u32 {
self.as_inner().as_inner().st_flags as u32
}
- #[cfg(freebsd12)]
fn st_lspare(&self) -> u32 {
panic!("st_lspare not supported with FreeBSD 12 ABI");
}
- #[cfg(not(freebsd12))]
- fn st_lspare(&self) -> u32 {
- self.as_inner().as_inner().st_lspare as u32
- }
}
diff --git a/library/std/src/os/ios/fs.rs b/library/std/src/os/ios/fs.rs
index b319527a5..e5df4de0b 100644
--- a/library/std/src/os/ios/fs.rs
+++ b/library/std/src/os/ios/fs.rs
@@ -144,14 +144,14 @@ impl MetadataExt for Metadata {
}
/// OS-specific extensions to [`fs::FileTimes`].
-#[unstable(feature = "file_set_times", issue = "98245")]
+#[stable(feature = "file_set_times", since = "1.75.0")]
pub trait FileTimesExt: Sealed {
/// Set the creation time of a file.
- #[unstable(feature = "file_set_times", issue = "98245")]
+ #[stable(feature = "file_set_times", since = "1.75.0")]
fn set_created(self, t: SystemTime) -> Self;
}
-#[unstable(feature = "file_set_times", issue = "98245")]
+#[stable(feature = "file_set_times", since = "1.75.0")]
impl FileTimesExt for fs::FileTimes {
fn set_created(mut self, t: SystemTime) -> Self {
self.as_inner_mut().set_created(t.into_inner());
diff --git a/library/std/src/os/linux/fs.rs b/library/std/src/os/linux/fs.rs
index 479bbcc17..ab0b2a3ed 100644
--- a/library/std/src/os/linux/fs.rs
+++ b/library/std/src/os/linux/fs.rs
@@ -329,7 +329,14 @@ pub trait MetadataExt {
impl MetadataExt for Metadata {
#[allow(deprecated)]
fn as_raw_stat(&self) -> &raw::stat {
- unsafe { &*(self.as_inner().as_inner() as *const libc::stat64 as *const raw::stat) }
+ #[cfg(target_env = "musl")]
+ unsafe {
+ &*(self.as_inner().as_inner() as *const libc::stat as *const raw::stat)
+ }
+ #[cfg(not(target_env = "musl"))]
+ unsafe {
+ &*(self.as_inner().as_inner() as *const libc::stat64 as *const raw::stat)
+ }
}
fn st_dev(&self) -> u64 {
self.as_inner().as_inner().st_dev as u64
diff --git a/library/std/src/os/macos/fs.rs b/library/std/src/os/macos/fs.rs
index fe82d03d8..573426d1a 100644
--- a/library/std/src/os/macos/fs.rs
+++ b/library/std/src/os/macos/fs.rs
@@ -150,14 +150,14 @@ impl MetadataExt for Metadata {
}
/// OS-specific extensions to [`fs::FileTimes`].
-#[unstable(feature = "file_set_times", issue = "98245")]
+#[stable(feature = "file_set_times", since = "1.75.0")]
pub trait FileTimesExt: Sealed {
/// Set the creation time of a file.
- #[unstable(feature = "file_set_times", issue = "98245")]
+ #[stable(feature = "file_set_times", since = "1.75.0")]
fn set_created(self, t: SystemTime) -> Self;
}
-#[unstable(feature = "file_set_times", issue = "98245")]
+#[stable(feature = "file_set_times", since = "1.75.0")]
impl FileTimesExt for fs::FileTimes {
fn set_created(mut self, t: SystemTime) -> Self {
self.as_inner_mut().set_created(t.into_inner());
diff --git a/library/std/src/os/mod.rs b/library/std/src/os/mod.rs
index 11ad21515..6e11b92b6 100644
--- a/library/std/src/os/mod.rs
+++ b/library/std/src/os/mod.rs
@@ -97,6 +97,8 @@ pub mod wasi;
pub mod windows;
// Others.
+#[cfg(target_os = "aix")]
+pub mod aix;
#[cfg(target_os = "android")]
pub mod android;
#[cfg(target_os = "dragonfly")]
diff --git a/library/std/src/os/unix/mod.rs b/library/std/src/os/unix/mod.rs
index 3724e90af..5ba8719e6 100644
--- a/library/std/src/os/unix/mod.rs
+++ b/library/std/src/os/unix/mod.rs
@@ -37,6 +37,8 @@ use crate::os::linux as platform;
#[cfg(not(doc))]
mod platform {
+ #[cfg(target_os = "aix")]
+ pub use crate::os::aix::*;
#[cfg(target_os = "android")]
pub use crate::os::android::*;
#[cfg(target_os = "dragonfly")]
diff --git a/library/std/src/os/watchos/fs.rs b/library/std/src/os/watchos/fs.rs
index 2ecc4c68a..ee215dd59 100644
--- a/library/std/src/os/watchos/fs.rs
+++ b/library/std/src/os/watchos/fs.rs
@@ -144,14 +144,14 @@ impl MetadataExt for Metadata {
}
/// OS-specific extensions to [`fs::FileTimes`].
-#[unstable(feature = "file_set_times", issue = "98245")]
+#[stable(feature = "file_set_times", since = "1.75.0")]
pub trait FileTimesExt: Sealed {
/// Set the creation time of a file.
- #[unstable(feature = "file_set_times", issue = "98245")]
+ #[stable(feature = "file_set_times", since = "1.75.0")]
fn set_created(self, t: SystemTime) -> Self;
}
-#[unstable(feature = "file_set_times", issue = "98245")]
+#[stable(feature = "file_set_times", since = "1.75.0")]
impl FileTimesExt for fs::FileTimes {
fn set_created(mut self, t: SystemTime) -> Self {
self.as_inner_mut().set_created(t.into_inner());
diff --git a/library/std/src/os/windows/fs.rs b/library/std/src/os/windows/fs.rs
index 94509e547..1b013d1c1 100644
--- a/library/std/src/os/windows/fs.rs
+++ b/library/std/src/os/windows/fs.rs
@@ -528,14 +528,14 @@ impl FileTypeExt for fs::FileType {
}
/// Windows-specific extensions to [`fs::FileTimes`].
-#[unstable(feature = "file_set_times", issue = "98245")]
+#[stable(feature = "file_set_times", since = "1.75.0")]
pub trait FileTimesExt: Sealed {
/// Set the creation time of a file.
- #[unstable(feature = "file_set_times", issue = "98245")]
+ #[stable(feature = "file_set_times", since = "1.75.0")]
fn set_created(self, t: SystemTime) -> Self;
}
-#[unstable(feature = "file_set_times", issue = "98245")]
+#[stable(feature = "file_set_times", since = "1.75.0")]
impl FileTimesExt for fs::FileTimes {
fn set_created(mut self, t: SystemTime) -> Self {
self.as_inner_mut().set_created(t.into_inner());
diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs
index d7a2baa1f..55f4917a9 100644
--- a/library/std/src/panicking.rs
+++ b/library/std/src/panicking.rs
@@ -295,12 +295,53 @@ fn default_hook(info: &PanicInfo<'_>) {
#[cfg(not(test))]
#[doc(hidden)]
+#[cfg(feature = "panic_immediate_abort")]
+#[unstable(feature = "update_panic_count", issue = "none")]
+pub mod panic_count {
+ /// A reason for forcing an immediate abort on panic.
+ #[derive(Debug)]
+ pub enum MustAbort {
+ AlwaysAbort,
+ PanicInHook,
+ }
+
+ #[inline]
+ pub fn increase(run_panic_hook: bool) -> Option<MustAbort> {
+ None
+ }
+
+ #[inline]
+ pub fn finished_panic_hook() {}
+
+ #[inline]
+ pub fn decrease() {}
+
+ #[inline]
+ pub fn set_always_abort() {}
+
+ // Disregards ALWAYS_ABORT_FLAG
+ #[inline]
+ #[must_use]
+ pub fn get_count() -> usize {
+ 0
+ }
+
+ #[must_use]
+ #[inline]
+ pub fn count_is_zero() -> bool {
+ true
+ }
+}
+
+#[cfg(not(test))]
+#[doc(hidden)]
+#[cfg(not(feature = "panic_immediate_abort"))]
#[unstable(feature = "update_panic_count", issue = "none")]
pub mod panic_count {
use crate::cell::Cell;
use crate::sync::atomic::{AtomicUsize, Ordering};
- pub const ALWAYS_ABORT_FLAG: usize = 1 << (usize::BITS - 1);
+ const ALWAYS_ABORT_FLAG: usize = 1 << (usize::BITS - 1);
/// A reason for forcing an immediate abort on panic.
#[derive(Debug)]
@@ -421,6 +462,13 @@ pub mod panic_count {
pub use realstd::rt::panic_count;
/// Invoke a closure, capturing the cause of an unwinding panic if one occurs.
+#[cfg(feature = "panic_immediate_abort")]
+pub unsafe fn r#try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>> {
+ Ok(f())
+}
+
+/// Invoke a closure, capturing the cause of an unwinding panic if one occurs.
+#[cfg(not(feature = "panic_immediate_abort"))]
pub unsafe fn r#try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>> {
union Data<F, R> {
f: ManuallyDrop<F>,
@@ -755,6 +803,7 @@ fn rust_panic_with_hook(
/// This is the entry point for `resume_unwind`.
/// It just forwards the payload to the panic runtime.
+#[cfg_attr(feature = "panic_immediate_abort", inline)]
pub fn rust_panic_without_hook(payload: Box<dyn Any + Send>) -> ! {
panic_count::increase(false);
@@ -777,7 +826,16 @@ pub fn rust_panic_without_hook(payload: Box<dyn Any + Send>) -> ! {
/// yer breakpoints.
#[inline(never)]
#[cfg_attr(not(test), rustc_std_internal_symbol)]
+#[cfg(not(feature = "panic_immediate_abort"))]
fn rust_panic(msg: &mut dyn PanicPayload) -> ! {
let code = unsafe { __rust_start_panic(msg) };
rtabort!("failed to initiate panic, error {code}")
}
+
+#[cfg_attr(not(test), rustc_std_internal_symbol)]
+#[cfg(feature = "panic_immediate_abort")]
+fn rust_panic(_: &mut dyn PanicPayload) -> ! {
+ unsafe {
+ crate::intrinsics::abort();
+ }
+}
diff --git a/library/std/src/process.rs b/library/std/src/process.rs
index 8c1497613..af6bef1a7 100644
--- a/library/std/src/process.rs
+++ b/library/std/src/process.rs
@@ -526,6 +526,7 @@ impl fmt::Debug for ChildStderr {
/// list_dir.status().expect("process failed to execute");
/// ```
#[stable(feature = "process", since = "1.0.0")]
+#[cfg_attr(not(test), rustc_diagnostic_item = "Command")]
pub struct Command {
inner: imp::Command,
}
@@ -607,7 +608,7 @@ impl Command {
///
/// Note that the argument is not passed through a shell, but given
/// literally to the program. This means that shell syntax like quotes,
- /// escaped characters, word splitting, glob patterns, substitution, etc.
+ /// escaped characters, word splitting, glob patterns, variable substitution, etc.
/// have no effect.
///
/// # Examples
@@ -637,7 +638,7 @@ impl Command {
///
/// Note that the arguments are not passed through a shell, but given
/// literally to the program. This means that shell syntax like quotes,
- /// escaped characters, word splitting, glob patterns, substitution, etc.
+ /// escaped characters, word splitting, glob patterns, variable substitution, etc.
/// have no effect.
///
/// # Examples
@@ -1593,7 +1594,7 @@ impl From<io::Stderr> for Stdio {
pub struct ExitStatus(imp::ExitStatus);
/// The default value is one which indicates successful completion.
-#[stable(feature = "process-exitcode-default", since = "1.73.0")]
+#[stable(feature = "process_exitstatus_default", since = "1.73.0")]
impl Default for ExitStatus {
fn default() -> Self {
// Ideally this would be done by ExitCode::default().into() but that is complicated.
@@ -1959,6 +1960,14 @@ impl ExitCode {
}
}
+/// The default value is [`ExitCode::SUCCESS`]
+#[stable(feature = "process_exitcode_default", since = "1.75.0")]
+impl Default for ExitCode {
+ fn default() -> Self {
+ ExitCode::SUCCESS
+ }
+}
+
#[stable(feature = "process_exitcode", since = "1.61.0")]
impl From<u8> for ExitCode {
/// Construct an `ExitCode` from an arbitrary u8 value.
@@ -2196,6 +2205,7 @@ impl Child {
/// process::exit(0x0100);
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
+#[cfg_attr(not(test), rustc_diagnostic_item = "process_exit")]
pub fn exit(code: i32) -> ! {
crate::rt::cleanup();
crate::sys::os::exit(code)
diff --git a/library/std/src/rt.rs b/library/std/src/rt.rs
index f1eeb75be..5c83f72f3 100644
--- a/library/std/src/rt.rs
+++ b/library/std/src/rt.rs
@@ -155,6 +155,7 @@ fn lang_start_internal(
}
#[cfg(not(test))]
+#[inline(never)]
#[lang = "start"]
fn lang_start<T: crate::process::Termination + 'static>(
main: fn() -> T,
diff --git a/library/std/src/sync/once.rs b/library/std/src/sync/once.rs
index 8c46080e4..2bb4f3f9e 100644
--- a/library/std/src/sync/once.rs
+++ b/library/std/src/sync/once.rs
@@ -125,7 +125,7 @@ impl Once {
///
/// # Panics
///
- /// The closure `f` will only be executed once if this is called
+ /// The closure `f` will only be executed once even if this is called
/// concurrently amongst many threads. If that closure panics, however, then
/// it will *poison* this [`Once`] instance, causing all future invocations of
/// `call_once` to also panic.
diff --git a/library/std/src/sync/once_lock.rs b/library/std/src/sync/once_lock.rs
index e2b7b893c..f49630907 100644
--- a/library/std/src/sync/once_lock.rs
+++ b/library/std/src/sync/once_lock.rs
@@ -126,11 +126,48 @@ impl<T> OnceLock<T> {
#[inline]
#[stable(feature = "once_cell", since = "1.70.0")]
pub fn set(&self, value: T) -> Result<(), T> {
+ match self.try_insert(value) {
+ Ok(_) => Ok(()),
+ Err((_, value)) => Err(value),
+ }
+ }
+
+ /// Sets the contents of this cell to `value` if the cell was empty, then
+ /// returns a reference to it.
+ ///
+ /// May block if another thread is currently attempting to initialize the cell. The cell is
+ /// guaranteed to contain a value when set returns, though not necessarily the one provided.
+ ///
+ /// Returns `Ok(&value)` if the cell was empty and `Err(&current_value, value)` if it was full.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(once_cell_try_insert)]
+ ///
+ /// use std::sync::OnceLock;
+ ///
+ /// static CELL: OnceLock<i32> = OnceLock::new();
+ ///
+ /// fn main() {
+ /// assert!(CELL.get().is_none());
+ ///
+ /// std::thread::spawn(|| {
+ /// assert_eq!(CELL.try_insert(92), Ok(&92));
+ /// }).join().unwrap();
+ ///
+ /// assert_eq!(CELL.try_insert(62), Err((&92, 62)));
+ /// assert_eq!(CELL.get(), Some(&92));
+ /// }
+ /// ```
+ #[inline]
+ #[unstable(feature = "once_cell_try_insert", issue = "116693")]
+ pub fn try_insert(&self, value: T) -> Result<&T, (&T, T)> {
let mut value = Some(value);
- self.get_or_init(|| value.take().unwrap());
+ let res = self.get_or_init(|| value.take().unwrap());
match value {
- None => Ok(()),
- Some(value) => Err(value),
+ None => Ok(res),
+ Some(value) => Err((res, value)),
}
}
diff --git a/library/std/src/sync/rwlock.rs b/library/std/src/sync/rwlock.rs
index 26aaa2414..ac7c800ff 100644
--- a/library/std/src/sync/rwlock.rs
+++ b/library/std/src/sync/rwlock.rs
@@ -380,7 +380,7 @@ impl<T: ?Sized> RwLock<T> {
///
/// If the lock is poisoned, it will remain poisoned until this function is called. This allows
/// recovering from a poisoned state and marking that it has recovered. For example, if the
- /// value is overwritten by a known-good value, then the mutex can be marked as un-poisoned. Or
+ /// value is overwritten by a known-good value, then the lock can be marked as un-poisoned. Or
/// possibly, the value could be inspected to determine if it is in a consistent state, and if
/// so the poison is removed.
///
@@ -397,7 +397,7 @@ impl<T: ?Sized> RwLock<T> {
///
/// let _ = thread::spawn(move || {
/// let _lock = c_lock.write().unwrap();
- /// panic!(); // the mutex gets poisoned
+ /// panic!(); // the lock gets poisoned
/// }).join();
///
/// assert_eq!(lock.is_poisoned(), true);
diff --git a/library/std/src/sys/common/mod.rs b/library/std/src/sys/common/mod.rs
index 2b8782ddf..b35c5d30b 100644
--- a/library/std/src/sys/common/mod.rs
+++ b/library/std/src/sys/common/mod.rs
@@ -12,6 +12,7 @@
pub mod alloc;
pub mod small_c_string;
+#[allow(unused_imports)]
pub mod thread_local;
#[cfg(test)]
diff --git a/library/std/src/sys/hermit/net.rs b/library/std/src/sys/hermit/net.rs
index a564f1698..bd8b493d6 100644
--- a/library/std/src/sys/hermit/net.rs
+++ b/library/std/src/sys/hermit/net.rs
@@ -56,6 +56,12 @@ impl Socket {
unimplemented!()
}
+ pub fn connect(&self, addr: &SocketAddr) -> io::Result<()> {
+ let (addr, len) = addr.into_inner();
+ cvt_r(|| unsafe { netc::connect(self.as_raw_fd(), addr.as_ptr(), len) })?;
+ Ok(())
+ }
+
pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> {
self.set_nonblocking(true)?;
let r = unsafe {
diff --git a/library/std/src/sys/hermit/thread_local_dtor.rs b/library/std/src/sys/hermit/thread_local_dtor.rs
index 613266b95..98adaf4bf 100644
--- a/library/std/src/sys/hermit/thread_local_dtor.rs
+++ b/library/std/src/sys/hermit/thread_local_dtor.rs
@@ -5,23 +5,25 @@
// The this solution works like the implementation of macOS and
// doesn't additional OS support
-use crate::mem;
+use crate::cell::RefCell;
#[thread_local]
-static mut DTORS: Vec<(*mut u8, unsafe extern "C" fn(*mut u8))> = Vec::new();
+static DTORS: RefCell<Vec<(*mut u8, unsafe extern "C" fn(*mut u8))>> = RefCell::new(Vec::new());
pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
- let list = &mut DTORS;
- list.push((t, dtor));
+ match DTORS.try_borrow_mut() {
+ Ok(mut dtors) => dtors.push((t, dtor)),
+ Err(_) => rtabort!("global allocator may not use TLS"),
+ }
}
// every thread call this function to run through all possible destructors
pub unsafe fn run_dtors() {
- let mut list = mem::take(&mut DTORS);
+ let mut list = DTORS.take();
while !list.is_empty() {
for (ptr, dtor) in list {
dtor(ptr);
}
- list = mem::take(&mut DTORS);
+ list = DTORS.take();
}
}
diff --git a/library/std/src/sys/personality/dwarf/eh.rs b/library/std/src/sys/personality/dwarf/eh.rs
index 79624703a..a78084de0 100644
--- a/library/std/src/sys/personality/dwarf/eh.rs
+++ b/library/std/src/sys/personality/dwarf/eh.rs
@@ -1,6 +1,7 @@
//! Parsing of GCC-style Language-Specific Data Area (LSDA)
//! For details see:
//! * <https://refspecs.linuxfoundation.org/LSB_3.0.0/LSB-PDA/LSB-PDA/ehframechpt.html>
+//! * <https://refspecs.linuxfoundation.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/dwarfext.html>
//! * <https://itanium-cxx-abi.github.io/cxx-abi/exceptions.pdf>
//! * <https://www.airs.com/blog/archives/460>
//! * <https://www.airs.com/blog/archives/464>
@@ -37,17 +38,19 @@ pub const DW_EH_PE_indirect: u8 = 0x80;
#[derive(Copy, Clone)]
pub struct EHContext<'a> {
- pub ip: usize, // Current instruction pointer
- pub func_start: usize, // Address of the current function
- pub get_text_start: &'a dyn Fn() -> usize, // Get address of the code section
- pub get_data_start: &'a dyn Fn() -> usize, // Get address of the data section
+ pub ip: *const u8, // Current instruction pointer
+ pub func_start: *const u8, // Pointer to the current function
+ pub get_text_start: &'a dyn Fn() -> *const u8, // Get pointer to the code section
+ pub get_data_start: &'a dyn Fn() -> *const u8, // Get pointer to the data section
}
+/// Landing pad.
+type LPad = *const u8;
pub enum EHAction {
None,
- Cleanup(usize),
- Catch(usize),
- Filter(usize),
+ Cleanup(LPad),
+ Catch(LPad),
+ Filter(LPad),
Terminate,
}
@@ -81,22 +84,24 @@ pub unsafe fn find_eh_action(lsda: *const u8, context: &EHContext<'_>) -> Result
let ip = context.ip;
if !USING_SJLJ_EXCEPTIONS {
+ // read the callsite table
while reader.ptr < action_table {
- let cs_start = read_encoded_pointer(&mut reader, context, call_site_encoding)?;
- let cs_len = read_encoded_pointer(&mut reader, context, call_site_encoding)?;
- let cs_lpad = read_encoded_pointer(&mut reader, context, call_site_encoding)?;
+ // these are offsets rather than pointers;
+ let cs_start = read_encoded_offset(&mut reader, call_site_encoding)?;
+ let cs_len = read_encoded_offset(&mut reader, call_site_encoding)?;
+ let cs_lpad = read_encoded_offset(&mut reader, call_site_encoding)?;
let cs_action_entry = reader.read_uleb128();
// Callsite table is sorted by cs_start, so if we've passed the ip, we
// may stop searching.
- if ip < func_start + cs_start {
+ if ip < func_start.wrapping_add(cs_start) {
break;
}
- if ip < func_start + cs_start + cs_len {
+ if ip < func_start.wrapping_add(cs_start + cs_len) {
if cs_lpad == 0 {
return Ok(EHAction::None);
} else {
- let lpad = lpad_base + cs_lpad;
- return Ok(interpret_cs_action(action_table as *mut u8, cs_action_entry, lpad));
+ let lpad = lpad_base.wrapping_add(cs_lpad);
+ return Ok(interpret_cs_action(action_table, cs_action_entry, lpad));
}
}
}
@@ -106,12 +111,12 @@ pub unsafe fn find_eh_action(lsda: *const u8, context: &EHContext<'_>) -> Result
// SjLj version:
// The "IP" is an index into the call-site table, with two exceptions:
// -1 means 'no-action', and 0 means 'terminate'.
- match ip as isize {
+ match ip.addr() as isize {
-1 => return Ok(EHAction::None),
0 => return Ok(EHAction::Terminate),
_ => (),
}
- let mut idx = ip;
+ let mut idx = ip.addr();
loop {
let cs_lpad = reader.read_uleb128();
let cs_action_entry = reader.read_uleb128();
@@ -119,17 +124,18 @@ pub unsafe fn find_eh_action(lsda: *const u8, context: &EHContext<'_>) -> Result
if idx == 0 {
// Can never have null landing pad for sjlj -- that would have
// been indicated by a -1 call site index.
- let lpad = (cs_lpad + 1) as usize;
- return Ok(interpret_cs_action(action_table as *mut u8, cs_action_entry, lpad));
+ // FIXME(strict provenance)
+ let lpad = ptr::from_exposed_addr((cs_lpad + 1) as usize);
+ return Ok(interpret_cs_action(action_table, cs_action_entry, lpad));
}
}
}
}
unsafe fn interpret_cs_action(
- action_table: *mut u8,
+ action_table: *const u8,
cs_action_entry: u64,
- lpad: usize,
+ lpad: LPad,
) -> EHAction {
if cs_action_entry == 0 {
// If cs_action_entry is 0 then this is a cleanup (Drop::drop). We run these
@@ -138,7 +144,7 @@ unsafe fn interpret_cs_action(
} else {
// If lpad != 0 and cs_action_entry != 0, we have to check ttype_index.
// If ttype_index == 0 under the condition, we take cleanup action.
- let action_record = (action_table as *mut u8).offset(cs_action_entry as isize - 1);
+ let action_record = action_table.offset(cs_action_entry as isize - 1);
let mut action_reader = DwarfReader::new(action_record);
let ttype_index = action_reader.read_sleb128();
if ttype_index == 0 {
@@ -157,22 +163,24 @@ fn round_up(unrounded: usize, align: usize) -> Result<usize, ()> {
if align.is_power_of_two() { Ok((unrounded + align - 1) & !(align - 1)) } else { Err(()) }
}
-unsafe fn read_encoded_pointer(
- reader: &mut DwarfReader,
- context: &EHContext<'_>,
- encoding: u8,
-) -> Result<usize, ()> {
- if encoding == DW_EH_PE_omit {
+/// Read a offset (`usize`) from `reader` whose encoding is described by `encoding`.
+///
+/// `encoding` must be a [DWARF Exception Header Encoding as described by the LSB spec][LSB-dwarf-ext].
+/// In addition the upper ("application") part must be zero.
+///
+/// # Errors
+/// Returns `Err` if `encoding`
+/// * is not a valid DWARF Exception Header Encoding,
+/// * is `DW_EH_PE_omit`, or
+/// * has a non-zero application part.
+///
+/// [LSB-dwarf-ext]: https://refspecs.linuxfoundation.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/dwarfext.html
+unsafe fn read_encoded_offset(reader: &mut DwarfReader, encoding: u8) -> Result<usize, ()> {
+ if encoding == DW_EH_PE_omit || encoding & 0xF0 != 0 {
return Err(());
}
-
- // DW_EH_PE_aligned implies it's an absolute pointer value
- if encoding == DW_EH_PE_aligned {
- reader.ptr = reader.ptr.with_addr(round_up(reader.ptr.addr(), mem::size_of::<usize>())?);
- return Ok(reader.read::<usize>());
- }
-
- let mut result = match encoding & 0x0F {
+ let result = match encoding & 0x0F {
+ // despite the name, LLVM also uses absptr for offsets instead of pointers
DW_EH_PE_absptr => reader.read::<usize>(),
DW_EH_PE_uleb128 => reader.read_uleb128() as usize,
DW_EH_PE_udata2 => reader.read::<u16>() as usize,
@@ -184,25 +192,66 @@ unsafe fn read_encoded_pointer(
DW_EH_PE_sdata8 => reader.read::<i64>() as usize,
_ => return Err(()),
};
+ Ok(result)
+}
+
+/// Read a pointer from `reader` whose encoding is described by `encoding`.
+///
+/// `encoding` must be a [DWARF Exception Header Encoding as described by the LSB spec][LSB-dwarf-ext].
+///
+/// # Errors
+/// Returns `Err` if `encoding`
+/// * is not a valid DWARF Exception Header Encoding,
+/// * is `DW_EH_PE_omit`, or
+/// * combines `DW_EH_PE_absptr` or `DW_EH_PE_aligned` application part with an integer encoding
+/// (not `DW_EH_PE_absptr`) in the value format part.
+///
+/// [LSB-dwarf-ext]: https://refspecs.linuxfoundation.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/dwarfext.html
+unsafe fn read_encoded_pointer(
+ reader: &mut DwarfReader,
+ context: &EHContext<'_>,
+ encoding: u8,
+) -> Result<*const u8, ()> {
+ if encoding == DW_EH_PE_omit {
+ return Err(());
+ }
- result += match encoding & 0x70 {
- DW_EH_PE_absptr => 0,
+ let base_ptr = match encoding & 0x70 {
+ DW_EH_PE_absptr => core::ptr::null(),
// relative to address of the encoded value, despite the name
- DW_EH_PE_pcrel => reader.ptr.expose_addr(),
+ DW_EH_PE_pcrel => reader.ptr,
DW_EH_PE_funcrel => {
- if context.func_start == 0 {
+ if context.func_start.is_null() {
return Err(());
}
context.func_start
}
DW_EH_PE_textrel => (*context.get_text_start)(),
DW_EH_PE_datarel => (*context.get_data_start)(),
+ // aligned means the value is aligned to the size of a pointer
+ DW_EH_PE_aligned => {
+ reader.ptr =
+ reader.ptr.with_addr(round_up(reader.ptr.addr(), mem::size_of::<*const u8>())?);
+ core::ptr::null()
+ }
_ => return Err(()),
};
+ let mut ptr = if base_ptr.is_null() {
+ // any value encoding other than absptr would be nonsensical here;
+ // there would be no source of pointer provenance
+ if encoding & 0x0F != DW_EH_PE_absptr {
+ return Err(());
+ }
+ reader.read::<*const u8>()
+ } else {
+ let offset = read_encoded_offset(reader, encoding & 0x0F)?;
+ base_ptr.wrapping_add(offset)
+ };
+
if encoding & DW_EH_PE_indirect != 0 {
- result = *ptr::from_exposed_addr::<usize>(result);
+ ptr = *(ptr.cast::<*const u8>());
}
- Ok(result)
+ Ok(ptr)
}
diff --git a/library/std/src/sys/personality/gcc.rs b/library/std/src/sys/personality/gcc.rs
index e477a0cd7..6f3171311 100644
--- a/library/std/src/sys/personality/gcc.rs
+++ b/library/std/src/sys/personality/gcc.rs
@@ -38,7 +38,6 @@
use super::dwarf::eh::{self, EHAction, EHContext};
use crate::ffi::c_int;
-use libc::uintptr_t;
use unwind as uw;
// Register ids were lifted from LLVM's TargetLowering::getExceptionPointerRegister()
@@ -95,7 +94,7 @@ const UNWIND_DATA_REG: (i32, i32) = (4, 5); // a0, a1
cfg_if::cfg_if! {
if #[cfg(all(target_arch = "arm", not(target_os = "ios"), not(target_os = "tvos"), not(target_os = "watchos"), not(target_os = "netbsd")))] {
// ARM EHABI personality routine.
- // https://infocenter.arm.com/help/topic/com.arm.doc.ihi0038b/IHI0038B_ehabi.pdf
+ // https://web.archive.org/web/20190728160938/https://infocenter.arm.com/help/topic/com.arm.doc.ihi0038b/IHI0038B_ehabi.pdf
//
// iOS uses the default routine instead since it uses SjLj unwinding.
#[lang = "eh_personality"]
@@ -160,9 +159,9 @@ cfg_if::cfg_if! {
uw::_Unwind_SetGR(
context,
UNWIND_DATA_REG.0,
- exception_object as uintptr_t,
+ exception_object as uw::_Unwind_Ptr,
);
- uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, 0);
+ uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, core::ptr::null());
uw::_Unwind_SetIP(context, lpad);
return uw::_URC_INSTALL_CONTEXT;
}
@@ -222,9 +221,9 @@ cfg_if::cfg_if! {
uw::_Unwind_SetGR(
context,
UNWIND_DATA_REG.0,
- exception_object as uintptr_t,
+ exception_object.cast(),
);
- uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, 0);
+ uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, core::ptr::null());
uw::_Unwind_SetIP(context, lpad);
uw::_URC_INSTALL_CONTEXT
}
diff --git a/library/std/src/sys/sgx/abi/usercalls/alloc.rs b/library/std/src/sys/sgx/abi/usercalls/alloc.rs
index 01505e944..817c33b66 100644
--- a/library/std/src/sys/sgx/abi/usercalls/alloc.rs
+++ b/library/std/src/sys/sgx/abi/usercalls/alloc.rs
@@ -3,6 +3,8 @@
use crate::arch::asm;
use crate::cell::UnsafeCell;
use crate::cmp;
+use crate::convert::TryInto;
+use crate::intrinsics;
use crate::mem;
use crate::ops::{CoerceUnsized, Deref, DerefMut, Index, IndexMut};
use crate::ptr::{self, NonNull};
@@ -306,20 +308,35 @@ where
}
}
-// Split a memory region ptr..ptr + len into three parts:
-// +--------+
-// | small0 | Chunk smaller than 8 bytes
-// +--------+
-// | big | Chunk 8-byte aligned, and size a multiple of 8 bytes
-// +--------+
-// | small1 | Chunk smaller than 8 bytes
-// +--------+
-fn region_as_aligned_chunks(ptr: *const u8, len: usize) -> (usize, usize, usize) {
- let small0_size = if ptr.is_aligned_to(8) { 0 } else { 8 - ptr.addr() % 8 };
- let small1_size = (len - small0_size) % 8;
- let big_size = len - small0_size - small1_size;
-
- (small0_size, big_size, small1_size)
+/// Divide the slice `(ptr, len)` into three parts, where the middle part is
+/// aligned to `u64`.
+///
+/// The return values `(prefix_len, mid_len, suffix_len)` add back up to `len`.
+/// The return values are such that the memory region `(ptr + prefix_len,
+/// mid_len)` is the largest possible region where `ptr + prefix_len` is aligned
+/// to `u64` and `mid_len` is a multiple of the byte size of `u64`. This means
+/// that `prefix_len` and `suffix_len` are guaranteed to be less than the byte
+/// size of `u64`, and that `(ptr, prefix_len)` and `(ptr + prefix_len +
+/// mid_len, suffix_len)` don't straddle an alignment boundary.
+// Standard Rust functions such as `<[u8]>::align_to::<u64>` and
+// `<*const u8>::align_offset` aren't _guaranteed_ to compute the largest
+// possible middle region, and as such can't be used.
+fn u64_align_to_guaranteed(ptr: *const u8, mut len: usize) -> (usize, usize, usize) {
+ const QWORD_SIZE: usize = mem::size_of::<u64>();
+
+ let offset = ptr as usize % QWORD_SIZE;
+
+ let prefix_len = if intrinsics::unlikely(offset > 0) { QWORD_SIZE - offset } else { 0 };
+
+ len = match len.checked_sub(prefix_len) {
+ Some(remaining_len) => remaining_len,
+ None => return (len, 0, 0),
+ };
+
+ let suffix_len = len % QWORD_SIZE;
+ len -= suffix_len;
+
+ (prefix_len, len, suffix_len)
}
unsafe fn copy_quadwords(src: *const u8, dst: *mut u8, len: usize) {
@@ -352,7 +369,13 @@ unsafe fn copy_quadwords(src: *const u8, dst: *mut u8, len: usize) {
/// - https://www.intel.com/content/www/us/en/security-center/advisory/intel-sa-00615.html
/// - https://www.intel.com/content/www/us/en/developer/articles/technical/software-security-guidance/technical-documentation/processor-mmio-stale-data-vulnerabilities.html#inpage-nav-3-2-2
pub(crate) unsafe fn copy_to_userspace(src: *const u8, dst: *mut u8, len: usize) {
- unsafe fn copy_bytewise_to_userspace(src: *const u8, dst: *mut u8, len: usize) {
+ /// Like `ptr::copy(src, dst, len)`, except it uses the Intel-recommended
+ /// instruction sequence for unaligned writes.
+ unsafe fn write_bytewise_to_userspace(src: *const u8, dst: *mut u8, len: usize) {
+ if intrinsics::likely(len == 0) {
+ return;
+ }
+
unsafe {
let mut seg_sel: u16 = 0;
for off in 0..len {
@@ -380,41 +403,15 @@ pub(crate) unsafe fn copy_to_userspace(src: *const u8, dst: *mut u8, len: usize)
assert!(!src.addr().overflowing_add(len).1);
assert!(!dst.addr().overflowing_add(len).1);
- if len < 8 {
- // Can't align on 8 byte boundary: copy safely byte per byte
- unsafe {
- copy_bytewise_to_userspace(src, dst, len);
- }
- } else if len % 8 == 0 && dst.is_aligned_to(8) {
- // Copying 8-byte aligned quadwords: copy quad word per quad word
- unsafe {
- copy_quadwords(src, dst, len);
- }
- } else {
- // Split copies into three parts:
- // +--------+
- // | small0 | Chunk smaller than 8 bytes
- // +--------+
- // | big | Chunk 8-byte aligned, and size a multiple of 8 bytes
- // +--------+
- // | small1 | Chunk smaller than 8 bytes
- // +--------+
- let (small0_size, big_size, small1_size) = region_as_aligned_chunks(dst, len);
+ unsafe {
+ let (len1, len2, len3) = u64_align_to_guaranteed(dst, len);
+ let (src1, dst1) = (src, dst);
+ let (src2, dst2) = (src1.add(len1), dst1.add(len1));
+ let (src3, dst3) = (src2.add(len2), dst2.add(len2));
- unsafe {
- // Copy small0
- copy_bytewise_to_userspace(src, dst, small0_size);
-
- // Copy big
- let big_src = src.add(small0_size);
- let big_dst = dst.add(small0_size);
- copy_quadwords(big_src, big_dst, big_size);
-
- // Copy small1
- let small1_src = src.add(big_size + small0_size);
- let small1_dst = dst.add(big_size + small0_size);
- copy_bytewise_to_userspace(small1_src, small1_dst, small1_size);
- }
+ write_bytewise_to_userspace(src1, dst1, len1);
+ copy_quadwords(src2, dst2, len2);
+ write_bytewise_to_userspace(src3, dst3, len3);
}
}
@@ -434,45 +431,33 @@ pub(crate) unsafe fn copy_to_userspace(src: *const u8, dst: *mut u8, len: usize)
/// - https://www.intel.com/content/www/us/en/security-center/advisory/intel-sa-00657.html
/// - https://www.intel.com/content/www/us/en/developer/articles/technical/software-security-guidance/advisory-guidance/stale-data-read-from-xapic.html
pub(crate) unsafe fn copy_from_userspace(src: *const u8, dst: *mut u8, len: usize) {
- // Copies memory region `src..src + len` to the enclave at `dst`. The source memory region
- // is:
- // - strictly less than 8 bytes in size and may be
- // - located at a misaligned memory location
- fn copy_misaligned_chunk_to_enclave(src: *const u8, dst: *mut u8, len: usize) {
- let mut tmp_buff = [0u8; 16];
+ /// Like `ptr::copy(src, dst, len)`, except it uses only u64-aligned reads.
+ ///
+ /// # Safety
+ /// The source memory region must not straddle an alignment boundary.
+ unsafe fn read_misaligned_from_userspace(src: *const u8, dst: *mut u8, len: usize) {
+ if intrinsics::likely(len == 0) {
+ return;
+ }
unsafe {
- // Compute an aligned memory region to read from
- // +--------+ <-- aligned_src + aligned_len (8B-aligned)
- // | pad1 |
- // +--------+ <-- src + len (misaligned)
- // | |
- // | |
- // | |
- // +--------+ <-- src (misaligned)
- // | pad0 |
- // +--------+ <-- aligned_src (8B-aligned)
- let pad0_size = src as usize % 8;
- let aligned_src = src.sub(pad0_size);
-
- let pad1_size = 8 - (src.add(len) as usize % 8);
- let aligned_len = pad0_size + len + pad1_size;
-
- debug_assert!(len < 8);
- debug_assert_eq!(aligned_src as usize % 8, 0);
- debug_assert_eq!(aligned_len % 8, 0);
- debug_assert!(aligned_len <= 16);
-
- // Copy the aligned buffer to a temporary buffer
- // Note: copying from a slightly different memory location is a bit odd. In this case it
- // can't lead to page faults or inadvertent copying from the enclave as we only ensured
- // that the `src` pointer is aligned at an 8 byte boundary. As pages are 4096 bytes
- // aligned, `aligned_src` must be on the same page as `src`. A similar argument can be made
- // for `src + len`
- copy_quadwords(aligned_src as _, tmp_buff.as_mut_ptr(), aligned_len);
-
- // Copy the correct parts of the temporary buffer to the destination
- ptr::copy(tmp_buff.as_ptr().add(pad0_size), dst, len);
+ let offset: usize;
+ let data: u64;
+ // doing a memory read that's potentially out of bounds for `src`,
+ // this isn't supported by Rust, so have to use assembly
+ asm!("
+ movl {src:e}, {offset:e}
+ andl $7, {offset:e}
+ andq $-8, {src}
+ movq ({src}), {dst}
+ ",
+ src = inout(reg) src => _,
+ offset = out(reg) offset,
+ dst = out(reg) data,
+ options(nostack, att_syntax, readonly, pure)
+ );
+ let data = data.to_le_bytes();
+ ptr::copy_nonoverlapping(data.as_ptr().add(offset), dst, len);
}
}
@@ -480,41 +465,19 @@ pub(crate) unsafe fn copy_from_userspace(src: *const u8, dst: *mut u8, len: usiz
assert!(!dst.is_null());
assert!(is_user_range(src, len));
assert!(is_enclave_range(dst, len));
- assert!(!(src as usize).overflowing_add(len + 8).1);
- assert!(!(dst as usize).overflowing_add(len + 8).1);
+ assert!(len < isize::MAX as usize);
+ assert!(!(src as usize).overflowing_add(len).1);
+ assert!(!(dst as usize).overflowing_add(len).1);
- if len < 8 {
- copy_misaligned_chunk_to_enclave(src, dst, len);
- } else if len % 8 == 0 && src as usize % 8 == 0 {
- // Copying 8-byte aligned quadwords: copy quad word per quad word
- unsafe {
- copy_quadwords(src, dst, len);
- }
- } else {
- // Split copies into three parts:
- // +--------+
- // | small0 | Chunk smaller than 8 bytes
- // +--------+
- // | big | Chunk 8-byte aligned, and size a multiple of 8 bytes
- // +--------+
- // | small1 | Chunk smaller than 8 bytes
- // +--------+
- let (small0_size, big_size, small1_size) = region_as_aligned_chunks(dst, len);
+ unsafe {
+ let (len1, len2, len3) = u64_align_to_guaranteed(src, len);
+ let (src1, dst1) = (src, dst);
+ let (src2, dst2) = (src1.add(len1), dst1.add(len1));
+ let (src3, dst3) = (src2.add(len2), dst2.add(len2));
- unsafe {
- // Copy small0
- copy_misaligned_chunk_to_enclave(src, dst, small0_size);
-
- // Copy big
- let big_src = src.add(small0_size);
- let big_dst = dst.add(small0_size);
- copy_quadwords(big_src, big_dst, big_size);
-
- // Copy small1
- let small1_src = src.add(big_size + small0_size);
- let small1_dst = dst.add(big_size + small0_size);
- copy_misaligned_chunk_to_enclave(small1_src, small1_dst, small1_size);
- }
+ read_misaligned_from_userspace(src1, dst1, len1);
+ copy_quadwords(src2, dst2, len2);
+ read_misaligned_from_userspace(src3, dst3, len3);
}
}
@@ -609,9 +572,9 @@ where
/// Copies the value from user memory into enclave memory.
pub fn to_enclave(&self) -> T {
unsafe {
- let mut data: T = mem::MaybeUninit::uninit().assume_init();
- copy_from_userspace(self.0.get() as _, &mut data as *mut T as _, mem::size_of::<T>());
- data
+ let mut data = mem::MaybeUninit::uninit();
+ copy_from_userspace(self.0.get() as _, data.as_mut_ptr() as _, mem::size_of::<T>());
+ data.assume_init()
}
}
}
diff --git a/library/std/src/sys/sgx/waitqueue/mod.rs b/library/std/src/sys/sgx/waitqueue/mod.rs
index 5e1d859ee..25eca61d6 100644
--- a/library/std/src/sys/sgx/waitqueue/mod.rs
+++ b/library/std/src/sys/sgx/waitqueue/mod.rs
@@ -18,6 +18,7 @@ mod unsafe_list;
use crate::num::NonZeroUsize;
use crate::ops::{Deref, DerefMut};
+use crate::panic::{self, AssertUnwindSafe};
use crate::time::Duration;
use super::abi::thread;
@@ -147,7 +148,8 @@ impl WaitQueue {
/// Adds the calling thread to the `WaitVariable`'s wait queue, then wait
/// until a wakeup event.
///
- /// This function does not return until this thread has been awoken.
+ /// This function does not return until this thread has been awoken. When `before_wait` panics,
+ /// this function will abort.
pub fn wait<T, F: FnOnce()>(mut guard: SpinMutexGuard<'_, WaitVariable<T>>, before_wait: F) {
// very unsafe: check requirements of UnsafeList::push
unsafe {
@@ -157,8 +159,13 @@ impl WaitQueue {
}));
let entry = guard.queue.inner.push(&mut entry);
drop(guard);
- before_wait();
+ if let Err(_e) = panic::catch_unwind(AssertUnwindSafe(|| before_wait())) {
+ rtabort!("Panic before wait on wakeup event")
+ }
while !entry.lock().wake {
+ // `entry.wake` is only set in `notify_one` and `notify_all` functions. Both ensure
+ // the entry is removed from the queue _before_ setting this bool. There are no
+ // other references to `entry`.
// don't panic, this would invalidate `entry` during unwinding
let eventset = rtunwrap!(Ok, usercalls::wait(EV_UNPARK, WAIT_INDEFINITE));
rtassert!(eventset & EV_UNPARK == EV_UNPARK);
@@ -169,6 +176,7 @@ impl WaitQueue {
/// Adds the calling thread to the `WaitVariable`'s wait queue, then wait
/// until a wakeup event or timeout. If event was observed, returns true.
/// If not, it will remove the calling thread from the wait queue.
+ /// When `before_wait` panics, this function will abort.
pub fn wait_timeout<T, F: FnOnce()>(
lock: &SpinMutex<WaitVariable<T>>,
timeout: Duration,
@@ -181,9 +189,13 @@ impl WaitQueue {
wake: false,
}));
let entry_lock = lock.lock().queue.inner.push(&mut entry);
- before_wait();
+ if let Err(_e) = panic::catch_unwind(AssertUnwindSafe(|| before_wait())) {
+ rtabort!("Panic before wait on wakeup event or timeout")
+ }
usercalls::wait_timeout(EV_UNPARK, timeout, || entry_lock.lock().wake);
- // acquire the wait queue's lock first to avoid deadlock.
+ // acquire the wait queue's lock first to avoid deadlock
+ // and ensure no other function can simultaneously access the list
+ // (e.g., `notify_one` or `notify_all`)
let mut guard = lock.lock();
let success = entry_lock.lock().wake;
if !success {
@@ -204,8 +216,8 @@ impl WaitQueue {
) -> Result<WaitGuard<'_, T>, SpinMutexGuard<'_, WaitVariable<T>>> {
// SAFETY: lifetime of the pop() return value is limited to the map
// closure (The closure return value is 'static). The underlying
- // stack frame won't be freed until after the WaitGuard created below
- // is dropped.
+ // stack frame won't be freed until after the lock on the queue is released
+ // (i.e., `guard` is dropped).
unsafe {
let tcs = guard.queue.inner.pop().map(|entry| -> Tcs {
let mut entry_guard = entry.lock();
@@ -231,7 +243,7 @@ impl WaitQueue {
) -> Result<WaitGuard<'_, T>, SpinMutexGuard<'_, WaitVariable<T>>> {
// SAFETY: lifetime of the pop() return values are limited to the
// while loop body. The underlying stack frames won't be freed until
- // after the WaitGuard created below is dropped.
+ // after the lock on the queue is released (i.e., `guard` is dropped).
unsafe {
let mut count = 0;
while let Some(entry) = guard.queue.inner.pop() {
diff --git a/library/std/src/sys/solid/net.rs b/library/std/src/sys/solid/net.rs
index 6adced787..1eae0fc06 100644
--- a/library/std/src/sys/solid/net.rs
+++ b/library/std/src/sys/solid/net.rs
@@ -233,12 +233,15 @@ impl Socket {
}
}
+ pub fn connect(&self, addr: &SocketAddr) -> io::Result<()> {
+ let (addr, len) = addr.into_inner();
+ cvt(unsafe { netc::connect(self.0.raw(), addr.as_ptr(), len) })?;
+ Ok(())
+ }
+
pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> {
self.set_nonblocking(true)?;
- let r = unsafe {
- let (addr, len) = addr.into_inner();
- cvt(netc::connect(self.0.raw(), addr.as_ptr(), len))
- };
+ let r = self.connect(addr);
self.set_nonblocking(false)?;
match r {
diff --git a/library/std/src/sys/solid/thread_local_dtor.rs b/library/std/src/sys/solid/thread_local_dtor.rs
index bad14bb37..26918a4fc 100644
--- a/library/std/src/sys/solid/thread_local_dtor.rs
+++ b/library/std/src/sys/solid/thread_local_dtor.rs
@@ -4,14 +4,13 @@
// Simplify dtor registration by using a list of destructors.
use super::{abi, itron::task};
-use crate::cell::Cell;
-use crate::mem;
+use crate::cell::{Cell, RefCell};
#[thread_local]
static REGISTERED: Cell<bool> = Cell::new(false);
#[thread_local]
-static mut DTORS: Vec<(*mut u8, unsafe extern "C" fn(*mut u8))> = Vec::new();
+static DTORS: RefCell<Vec<(*mut u8, unsafe extern "C" fn(*mut u8))>> = RefCell::new(Vec::new());
pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
if !REGISTERED.get() {
@@ -22,18 +21,20 @@ pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
REGISTERED.set(true);
}
- let list = unsafe { &mut DTORS };
- list.push((t, dtor));
+ match DTORS.try_borrow_mut() {
+ Ok(mut dtors) => dtors.push((t, dtor)),
+ Err(_) => rtabort!("global allocator may not use TLS"),
+ }
}
pub unsafe fn run_dtors() {
- let mut list = mem::take(unsafe { &mut DTORS });
+ let mut list = DTORS.take();
while !list.is_empty() {
for (ptr, dtor) in list {
unsafe { dtor(ptr) };
}
- list = mem::take(unsafe { &mut DTORS });
+ list = DTORS.take();
}
}
diff --git a/library/std/src/sys/uefi/alloc.rs b/library/std/src/sys/uefi/alloc.rs
index 789e3cbd8..ad3904d82 100644
--- a/library/std/src/sys/uefi/alloc.rs
+++ b/library/std/src/sys/uefi/alloc.rs
@@ -1,13 +1,17 @@
//! Global Allocator for UEFI.
//! Uses [r-efi-alloc](https://crates.io/crates/r-efi-alloc)
-use crate::alloc::{GlobalAlloc, Layout, System};
+use r_efi::protocols::loaded_image;
-const MEMORY_TYPE: u32 = r_efi::efi::LOADER_DATA;
+use crate::alloc::{GlobalAlloc, Layout, System};
+use crate::sync::OnceLock;
+use crate::sys::uefi::helpers;
#[stable(feature = "alloc_system_type", since = "1.28.0")]
unsafe impl GlobalAlloc for System {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
+ static EFI_MEMORY_TYPE: OnceLock<u32> = OnceLock::new();
+
// Return null pointer if boot services are not available
if crate::os::uefi::env::boot_services().is_none() {
return crate::ptr::null_mut();
@@ -15,8 +19,20 @@ unsafe impl GlobalAlloc for System {
// If boot services is valid then SystemTable is not null.
let system_table = crate::os::uefi::env::system_table().as_ptr().cast();
+
+ // Each loaded image has an image handle that supports `EFI_LOADED_IMAGE_PROTOCOL`. Thus, this
+ // will never fail.
+ let mem_type = EFI_MEMORY_TYPE.get_or_init(|| {
+ let protocol = helpers::image_handle_protocol::<loaded_image::Protocol>(
+ loaded_image::PROTOCOL_GUID,
+ )
+ .unwrap();
+ // Gives allocations the memory type that the data sections were loaded as.
+ unsafe { (*protocol.as_ptr()).image_data_type }
+ });
+
// The caller must ensure non-0 layout
- unsafe { r_efi_alloc::raw::alloc(system_table, layout, MEMORY_TYPE) }
+ unsafe { r_efi_alloc::raw::alloc(system_table, layout, *mem_type) }
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
diff --git a/library/std/src/sys/uefi/args.rs b/library/std/src/sys/uefi/args.rs
new file mode 100644
index 000000000..4ff7be748
--- /dev/null
+++ b/library/std/src/sys/uefi/args.rs
@@ -0,0 +1,158 @@
+use r_efi::protocols::loaded_image;
+
+use crate::env::current_exe;
+use crate::ffi::OsString;
+use crate::fmt;
+use crate::iter::Iterator;
+use crate::mem::size_of;
+use crate::sys::uefi::helpers;
+use crate::vec;
+
+pub struct Args {
+ parsed_args_list: vec::IntoIter<OsString>,
+}
+
+pub fn args() -> Args {
+ let lazy_current_exe = || Vec::from([current_exe().map(Into::into).unwrap_or_default()]);
+
+ // Each loaded image has an image handle that supports `EFI_LOADED_IMAGE_PROTOCOL`. Thus, this
+ // will never fail.
+ let protocol =
+ helpers::image_handle_protocol::<loaded_image::Protocol>(loaded_image::PROTOCOL_GUID)
+ .unwrap();
+
+ let lp_size = unsafe { (*protocol.as_ptr()).load_options_size } as usize;
+ // Break if we are sure that it cannot be UTF-16
+ if lp_size < size_of::<u16>() || lp_size % size_of::<u16>() != 0 {
+ return Args { parsed_args_list: lazy_current_exe().into_iter() };
+ }
+ let lp_size = lp_size / size_of::<u16>();
+
+ let lp_cmd_line = unsafe { (*protocol.as_ptr()).load_options as *const u16 };
+ if !lp_cmd_line.is_aligned() {
+ return Args { parsed_args_list: lazy_current_exe().into_iter() };
+ }
+ let lp_cmd_line = unsafe { crate::slice::from_raw_parts(lp_cmd_line, lp_size) };
+
+ Args {
+ parsed_args_list: parse_lp_cmd_line(lp_cmd_line)
+ .unwrap_or_else(lazy_current_exe)
+ .into_iter(),
+ }
+}
+
+impl fmt::Debug for Args {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.parsed_args_list.as_slice().fmt(f)
+ }
+}
+
+impl Iterator for Args {
+ type Item = OsString;
+
+ fn next(&mut self) -> Option<OsString> {
+ self.parsed_args_list.next()
+ }
+
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ self.parsed_args_list.size_hint()
+ }
+}
+
+impl ExactSizeIterator for Args {
+ fn len(&self) -> usize {
+ self.parsed_args_list.len()
+ }
+}
+
+impl DoubleEndedIterator for Args {
+ fn next_back(&mut self) -> Option<OsString> {
+ self.parsed_args_list.next_back()
+ }
+}
+
+/// Implements the UEFI command-line argument parsing algorithm.
+///
+/// This implementation is based on what is defined in Section 3.4 of
+/// [UEFI Shell Specification](https://uefi.org/sites/default/files/resources/UEFI_Shell_Spec_2_0.pdf)
+///
+/// Return None in the following cases:
+/// - Invalid UTF-16 (unpaired surrogate)
+/// - Empty/improper arguments
+fn parse_lp_cmd_line(code_units: &[u16]) -> Option<Vec<OsString>> {
+ const QUOTE: char = '"';
+ const SPACE: char = ' ';
+ const CARET: char = '^';
+ const NULL: char = '\0';
+
+ let mut ret_val = Vec::new();
+ let mut code_units_iter = char::decode_utf16(code_units.iter().cloned()).peekable();
+
+ // The executable name at the beginning is special.
+ let mut in_quotes = false;
+ let mut cur = String::new();
+ while let Some(w) = code_units_iter.next() {
+ let w = w.ok()?;
+ match w {
+ // break on NULL
+ NULL => break,
+ // A quote mark always toggles `in_quotes` no matter what because
+ // there are no escape characters when parsing the executable name.
+ QUOTE => in_quotes = !in_quotes,
+ // If not `in_quotes` then whitespace ends argv[0].
+ SPACE if !in_quotes => break,
+ // In all other cases the code unit is taken literally.
+ _ => cur.push(w),
+ }
+ }
+
+ // If exe name is missing, the cli args are invalid
+ if cur.is_empty() {
+ return None;
+ }
+
+ ret_val.push(OsString::from(cur));
+ // Skip whitespace.
+ while code_units_iter.next_if_eq(&Ok(SPACE)).is_some() {}
+
+ // Parse the arguments according to these rules:
+ // * All code units are taken literally except space, quote and caret.
+ // * When not `in_quotes`, space separate arguments. Consecutive spaces are
+ // treated as a single separator.
+ // * A space `in_quotes` is taken literally.
+ // * A quote toggles `in_quotes` mode unless it's escaped. An escaped quote is taken literally.
+ // * A quote can be escaped if preceded by caret.
+ // * A caret can be escaped if preceded by caret.
+ let mut cur = String::new();
+ let mut in_quotes = false;
+ while let Some(w) = code_units_iter.next() {
+ let w = w.ok()?;
+ match w {
+ // break on NULL
+ NULL => break,
+ // If not `in_quotes`, a space or tab ends the argument.
+ SPACE if !in_quotes => {
+ ret_val.push(OsString::from(&cur[..]));
+ cur.truncate(0);
+
+ // Skip whitespace.
+ while code_units_iter.next_if_eq(&Ok(SPACE)).is_some() {}
+ }
+ // Caret can escape quotes or carets
+ CARET if in_quotes => {
+ if let Some(x) = code_units_iter.next() {
+ cur.push(x.ok()?);
+ }
+ }
+ // If quote then flip `in_quotes`
+ QUOTE => in_quotes = !in_quotes,
+ // Everything else is always taken literally.
+ _ => cur.push(w),
+ }
+ }
+ // Push the final argument, if any.
+ if !cur.is_empty() || in_quotes {
+ ret_val.push(OsString::from(cur));
+ }
+ Some(ret_val)
+}
diff --git a/library/std/src/sys/uefi/helpers.rs b/library/std/src/sys/uefi/helpers.rs
index 126661bfc..9837cc89f 100644
--- a/library/std/src/sys/uefi/helpers.rs
+++ b/library/std/src/sys/uefi/helpers.rs
@@ -139,3 +139,10 @@ pub(crate) unsafe fn close_event(evt: NonNull<crate::ffi::c_void>) -> io::Result
if r.is_error() { Err(crate::io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) }
}
+
+/// Get the Protocol for current system handle.
+/// Note: Some protocols need to be manually freed. It is the callers responsibility to do so.
+pub(crate) fn image_handle_protocol<T>(protocol_guid: Guid) -> Option<NonNull<T>> {
+ let system_handle = uefi::env::try_image_handle()?;
+ open_protocol(system_handle, protocol_guid).ok()
+}
diff --git a/library/std/src/sys/uefi/mod.rs b/library/std/src/sys/uefi/mod.rs
index 9a10395af..4edc00e3e 100644
--- a/library/std/src/sys/uefi/mod.rs
+++ b/library/std/src/sys/uefi/mod.rs
@@ -13,7 +13,6 @@
//! [`OsString`]: crate::ffi::OsString
pub mod alloc;
-#[path = "../unsupported/args.rs"]
pub mod args;
#[path = "../unix/cmath.rs"]
pub mod cmath;
@@ -36,7 +35,6 @@ pub mod path;
pub mod pipe;
#[path = "../unsupported/process.rs"]
pub mod process;
-#[path = "../unsupported/stdio.rs"]
pub mod stdio;
#[path = "../unsupported/thread.rs"]
pub mod thread;
diff --git a/library/std/src/sys/uefi/stdio.rs b/library/std/src/sys/uefi/stdio.rs
new file mode 100644
index 000000000..a533d8a05
--- /dev/null
+++ b/library/std/src/sys/uefi/stdio.rs
@@ -0,0 +1,162 @@
+use crate::io;
+use crate::iter::Iterator;
+use crate::mem::MaybeUninit;
+use crate::os::uefi;
+use crate::ptr::NonNull;
+
+const MAX_BUFFER_SIZE: usize = 8192;
+
+pub struct Stdin;
+pub struct Stdout;
+pub struct Stderr;
+
+impl Stdin {
+ pub const fn new() -> Stdin {
+ Stdin
+ }
+}
+
+impl io::Read for Stdin {
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ let st: NonNull<r_efi::efi::SystemTable> = uefi::env::system_table().cast();
+ let stdin = unsafe { (*st.as_ptr()).con_in };
+
+ // Try reading any pending data
+ let inp = match read_key_stroke(stdin) {
+ Ok(x) => x,
+ Err(e) if e == r_efi::efi::Status::NOT_READY => {
+ // Wait for keypress for new data
+ wait_stdin(stdin)?;
+ read_key_stroke(stdin).map_err(|x| io::Error::from_raw_os_error(x.as_usize()))?
+ }
+ Err(e) => {
+ return Err(io::Error::from_raw_os_error(e.as_usize()));
+ }
+ };
+
+ // Check if the key is printiable character
+ if inp.scan_code != 0x00 {
+ return Err(io::const_io_error!(io::ErrorKind::Interrupted, "Special Key Press"));
+ }
+
+ // SAFETY: Iterator will have only 1 character since we are reading only 1 Key
+ // SAFETY: This character will always be UCS-2 and thus no surrogates.
+ let ch: char = char::decode_utf16([inp.unicode_char]).next().unwrap().unwrap();
+ if ch.len_utf8() > buf.len() {
+ return Ok(0);
+ }
+
+ ch.encode_utf8(buf);
+
+ Ok(ch.len_utf8())
+ }
+}
+
+impl Stdout {
+ pub const fn new() -> Stdout {
+ Stdout
+ }
+}
+
+impl io::Write for Stdout {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ let st: NonNull<r_efi::efi::SystemTable> = uefi::env::system_table().cast();
+ let stdout = unsafe { (*st.as_ptr()).con_out };
+
+ write(stdout, buf)
+ }
+
+ 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<usize> {
+ let st: NonNull<r_efi::efi::SystemTable> = uefi::env::system_table().cast();
+ let stderr = unsafe { (*st.as_ptr()).std_err };
+
+ write(stderr, buf)
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ Ok(())
+ }
+}
+
+// UCS-2 character should occupy 3 bytes at most in UTF-8
+pub const STDIN_BUF_SIZE: usize = 3;
+
+pub fn is_ebadf(_err: &io::Error) -> bool {
+ true
+}
+
+pub fn panic_output() -> Option<impl io::Write> {
+ uefi::env::try_system_table().map(|_| Stderr::new())
+}
+
+fn write(
+ protocol: *mut r_efi::protocols::simple_text_output::Protocol,
+ buf: &[u8],
+) -> io::Result<usize> {
+ let mut utf16 = [0; MAX_BUFFER_SIZE / 2];
+
+ // Get valid UTF-8 buffer
+ let utf8 = match crate::str::from_utf8(buf) {
+ Ok(x) => x,
+ Err(e) => unsafe { crate::str::from_utf8_unchecked(&buf[..e.valid_up_to()]) },
+ };
+ // Clip UTF-8 buffer to max UTF-16 buffer we support
+ let utf8 = &utf8[..utf8.floor_char_boundary(utf16.len() - 1)];
+
+ for (i, ch) in utf8.encode_utf16().enumerate() {
+ utf16[i] = ch;
+ }
+
+ unsafe { simple_text_output(protocol, &mut utf16) }?;
+
+ Ok(utf8.len())
+}
+
+unsafe fn simple_text_output(
+ protocol: *mut r_efi::protocols::simple_text_output::Protocol,
+ buf: &mut [u16],
+) -> io::Result<()> {
+ let res = unsafe { ((*protocol).output_string)(protocol, buf.as_mut_ptr()) };
+ if res.is_error() { Err(io::Error::from_raw_os_error(res.as_usize())) } else { Ok(()) }
+}
+
+fn wait_stdin(stdin: *mut r_efi::protocols::simple_text_input::Protocol) -> io::Result<()> {
+ let boot_services: NonNull<r_efi::efi::BootServices> =
+ uefi::env::boot_services().unwrap().cast();
+ let wait_for_event = unsafe { (*boot_services.as_ptr()).wait_for_event };
+ let wait_for_key_event = unsafe { (*stdin).wait_for_key };
+
+ let r = {
+ let mut x: usize = 0;
+ (wait_for_event)(1, [wait_for_key_event].as_mut_ptr(), &mut x)
+ };
+ if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) }
+}
+
+fn read_key_stroke(
+ stdin: *mut r_efi::protocols::simple_text_input::Protocol,
+) -> Result<r_efi::protocols::simple_text_input::InputKey, r_efi::efi::Status> {
+ let mut input_key: MaybeUninit<r_efi::protocols::simple_text_input::InputKey> =
+ MaybeUninit::uninit();
+
+ let r = unsafe { ((*stdin).read_key_stroke)(stdin, input_key.as_mut_ptr()) };
+
+ if r.is_error() {
+ Err(r)
+ } else {
+ let input_key = unsafe { input_key.assume_init() };
+ Ok(input_key)
+ }
+}
diff --git a/library/std/src/sys/unix/args.rs b/library/std/src/sys/unix/args.rs
index 19334e2af..2da17fabc 100644
--- a/library/std/src/sys/unix/args.rs
+++ b/library/std/src/sys/unix/args.rs
@@ -70,6 +70,7 @@ impl DoubleEndedIterator for Args {
target_os = "redox",
target_os = "vxworks",
target_os = "horizon",
+ target_os = "aix",
target_os = "nto",
target_os = "hurd",
))]
diff --git a/library/std/src/sys/unix/env.rs b/library/std/src/sys/unix/env.rs
index c6d8578a6..3bb492fa9 100644
--- a/library/std/src/sys/unix/env.rs
+++ b/library/std/src/sys/unix/env.rs
@@ -261,3 +261,14 @@ pub mod os {
pub const EXE_SUFFIX: &str = "";
pub const EXE_EXTENSION: &str = "";
}
+
+#[cfg(target_os = "aix")]
+pub mod os {
+ pub const FAMILY: &str = "unix";
+ pub const OS: &str = "aix";
+ pub const DLL_PREFIX: &str = "lib";
+ pub const DLL_SUFFIX: &str = ".a";
+ pub const DLL_EXTENSION: &str = "a";
+ pub const EXE_SUFFIX: &str = "";
+ pub const EXE_EXTENSION: &str = "";
+}
diff --git a/library/std/src/sys/unix/fd.rs b/library/std/src/sys/unix/fd.rs
index 6c4f40842..bf1fb3123 100644
--- a/library/std/src/sys/unix/fd.rs
+++ b/library/std/src/sys/unix/fd.rs
@@ -126,9 +126,17 @@ impl FileDesc {
}
pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
- #[cfg(not(any(target_os = "linux", target_os = "android", target_os = "hurd")))]
+ #[cfg(not(any(
+ all(target_os = "linux", not(target_env = "musl")),
+ target_os = "android",
+ target_os = "hurd"
+ )))]
use libc::pread as pread64;
- #[cfg(any(target_os = "linux", target_os = "android", target_os = "hurd"))]
+ #[cfg(any(
+ all(target_os = "linux", not(target_env = "musl")),
+ target_os = "android",
+ target_os = "hurd"
+ ))]
use libc::pread64;
unsafe {
@@ -285,9 +293,17 @@ impl FileDesc {
}
pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
- #[cfg(not(any(target_os = "linux", target_os = "android", target_os = "hurd")))]
+ #[cfg(not(any(
+ all(target_os = "linux", not(target_env = "musl")),
+ target_os = "android",
+ target_os = "hurd"
+ )))]
use libc::pwrite as pwrite64;
- #[cfg(any(target_os = "linux", target_os = "android", target_os = "hurd"))]
+ #[cfg(any(
+ all(target_os = "linux", not(target_env = "musl")),
+ target_os = "android",
+ target_os = "hurd"
+ ))]
use libc::pwrite64;
unsafe {
diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs
index 764e1f257..40eb910fd 100644
--- a/library/std/src/sys/unix/fs.rs
+++ b/library/std/src/sys/unix/fs.rs
@@ -40,13 +40,17 @@ use libc::{c_int, mode_t};
))]
use libc::c_char;
#[cfg(any(
- target_os = "linux",
+ all(target_os = "linux", not(target_env = "musl")),
target_os = "emscripten",
target_os = "android",
- target_os = "hurd",
+ target_os = "hurd"
))]
use libc::dirfd;
-#[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "hurd"))]
+#[cfg(any(
+ all(target_os = "linux", not(target_env = "musl")),
+ target_os = "emscripten",
+ target_os = "hurd"
+))]
use libc::fstatat64;
#[cfg(any(
target_os = "android",
@@ -54,11 +58,13 @@ use libc::fstatat64;
target_os = "fuchsia",
target_os = "redox",
target_os = "illumos",
+ target_os = "aix",
target_os = "nto",
target_os = "vita",
+ all(target_os = "linux", target_env = "musl"),
))]
use libc::readdir as readdir64;
-#[cfg(any(target_os = "linux", target_os = "hurd"))]
+#[cfg(any(all(target_os = "linux", not(target_env = "musl")), target_os = "hurd"))]
use libc::readdir64;
#[cfg(any(target_os = "emscripten", target_os = "l4re"))]
use libc::readdir64_r;
@@ -71,6 +77,7 @@ use libc::readdir64_r;
target_os = "l4re",
target_os = "fuchsia",
target_os = "redox",
+ target_os = "aix",
target_os = "nto",
target_os = "vita",
target_os = "hurd",
@@ -82,7 +89,7 @@ use libc::{
lstat as lstat64, off64_t, open as open64, stat as stat64,
};
#[cfg(not(any(
- target_os = "linux",
+ all(target_os = "linux", not(target_env = "musl")),
target_os = "emscripten",
target_os = "l4re",
target_os = "android",
@@ -93,7 +100,7 @@ use libc::{
lstat as lstat64, off_t as off64_t, open as open64, stat as stat64,
};
#[cfg(any(
- target_os = "linux",
+ all(target_os = "linux", not(target_env = "musl")),
target_os = "emscripten",
target_os = "l4re",
target_os = "hurd"
@@ -288,6 +295,7 @@ unsafe impl Sync for Dir {}
target_os = "illumos",
target_os = "fuchsia",
target_os = "redox",
+ target_os = "aix",
target_os = "nto",
target_os = "vita",
target_os = "hurd",
@@ -311,6 +319,7 @@ pub struct DirEntry {
target_os = "illumos",
target_os = "fuchsia",
target_os = "redox",
+ target_os = "aix",
target_os = "nto",
target_os = "vita",
target_os = "hurd",
@@ -320,8 +329,9 @@ struct dirent64_min {
#[cfg(not(any(
target_os = "solaris",
target_os = "illumos",
+ target_os = "aix",
target_os = "nto",
- target_os = "vita"
+ target_os = "vita",
)))]
d_type: u8,
}
@@ -333,6 +343,7 @@ struct dirent64_min {
target_os = "illumos",
target_os = "fuchsia",
target_os = "redox",
+ target_os = "aix",
target_os = "nto",
target_os = "vita",
target_os = "hurd",
@@ -464,7 +475,22 @@ impl FileAttr {
}
}
-#[cfg(not(any(target_os = "netbsd", target_os = "nto")))]
+#[cfg(target_os = "aix")]
+impl FileAttr {
+ pub fn modified(&self) -> io::Result<SystemTime> {
+ Ok(SystemTime::new(self.stat.st_mtime.tv_sec as i64, self.stat.st_mtime.tv_nsec as i64))
+ }
+
+ pub fn accessed(&self) -> io::Result<SystemTime> {
+ Ok(SystemTime::new(self.stat.st_atime.tv_sec as i64, self.stat.st_atime.tv_nsec as i64))
+ }
+
+ pub fn created(&self) -> io::Result<SystemTime> {
+ Ok(SystemTime::new(self.stat.st_ctime.tv_sec as i64, self.stat.st_ctime.tv_nsec as i64))
+ }
+}
+
+#[cfg(not(any(target_os = "netbsd", target_os = "nto", target_os = "aix")))]
impl FileAttr {
#[cfg(not(any(
target_os = "vxworks",
@@ -671,6 +697,7 @@ impl Iterator for ReadDir {
target_os = "fuchsia",
target_os = "redox",
target_os = "illumos",
+ target_os = "aix",
target_os = "nto",
target_os = "vita",
target_os = "hurd",
@@ -748,6 +775,7 @@ impl Iterator for ReadDir {
#[cfg(not(any(
target_os = "solaris",
target_os = "illumos",
+ target_os = "aix",
target_os = "nto",
)))]
d_type: *offset_ptr!(entry_ptr, d_type) as u8,
@@ -772,6 +800,7 @@ impl Iterator for ReadDir {
target_os = "fuchsia",
target_os = "redox",
target_os = "illumos",
+ target_os = "aix",
target_os = "nto",
target_os = "vita",
target_os = "hurd",
@@ -829,10 +858,10 @@ impl DirEntry {
#[cfg(all(
any(
- target_os = "linux",
+ all(target_os = "linux", not(target_env = "musl")),
target_os = "emscripten",
target_os = "android",
- target_os = "hurd",
+ target_os = "hurd"
),
not(miri)
))]
@@ -858,7 +887,7 @@ impl DirEntry {
#[cfg(any(
not(any(
- target_os = "linux",
+ all(target_os = "linux", not(target_env = "musl")),
target_os = "emscripten",
target_os = "android",
target_os = "hurd",
@@ -874,6 +903,7 @@ impl DirEntry {
target_os = "illumos",
target_os = "haiku",
target_os = "vxworks",
+ target_os = "aix",
target_os = "nto",
target_os = "vita",
))]
@@ -886,6 +916,7 @@ impl DirEntry {
target_os = "illumos",
target_os = "haiku",
target_os = "vxworks",
+ target_os = "aix",
target_os = "nto",
target_os = "vita",
)))]
@@ -920,6 +951,7 @@ impl DirEntry {
target_os = "espidf",
target_os = "horizon",
target_os = "vita",
+ target_os = "aix",
target_os = "nto",
target_os = "hurd",
))]
@@ -977,6 +1009,7 @@ impl DirEntry {
target_os = "illumos",
target_os = "fuchsia",
target_os = "redox",
+ target_os = "aix",
target_os = "nto",
target_os = "vita",
target_os = "hurd",
@@ -991,6 +1024,7 @@ impl DirEntry {
target_os = "illumos",
target_os = "fuchsia",
target_os = "redox",
+ target_os = "aix",
target_os = "nto",
target_os = "vita",
target_os = "hurd",
@@ -1391,6 +1425,7 @@ impl FromInner<FileDesc> for File {
}
impl AsFd for File {
+ #[inline]
fn as_fd(&self) -> BorrowedFd<'_> {
self.0.as_fd()
}
@@ -2025,6 +2060,7 @@ mod remove_dir_impl {
target_os = "illumos",
target_os = "haiku",
target_os = "vxworks",
+ target_os = "aix",
))]
fn is_dir(_ent: &DirEntry) -> Option<bool> {
None
@@ -2035,6 +2071,7 @@ mod remove_dir_impl {
target_os = "illumos",
target_os = "haiku",
target_os = "vxworks",
+ target_os = "aix",
)))]
fn is_dir(ent: &DirEntry) -> Option<bool> {
match ent.entry.d_type {
diff --git a/library/std/src/sys/unix/mod.rs b/library/std/src/sys/unix/mod.rs
index 3edafde71..4b28f6feb 100644
--- a/library/std/src/sys/unix/mod.rs
+++ b/library/std/src/sys/unix/mod.rs
@@ -241,6 +241,7 @@ pub unsafe fn cleanup() {
#[cfg(target_os = "android")]
pub use crate::sys::android::signal;
+#[allow(unused_imports)]
#[cfg(not(target_os = "android"))]
pub use libc::signal;
@@ -278,6 +279,7 @@ pub fn decode_error_kind(errno: i32) -> ErrorKind {
libc::ENETUNREACH => NetworkUnreachable,
libc::ENOTCONN => NotConnected,
libc::ENOTDIR => NotADirectory,
+ #[cfg(not(target_os = "aix"))]
libc::ENOTEMPTY => DirectoryNotEmpty,
libc::EPIPE => BrokenPipe,
libc::EROFS => ReadOnlyFilesystem,
@@ -413,7 +415,6 @@ cfg_if::cfg_if! {
} else if #[cfg(any(target_os = "ios", target_os = "tvos", target_os = "watchos"))] {
#[link(name = "System")]
#[link(name = "objc")]
- #[link(name = "Security", kind = "framework")]
#[link(name = "Foundation", kind = "framework")]
extern "C" {}
} else if #[cfg(target_os = "fuchsia")] {
diff --git a/library/std/src/sys/unix/net.rs b/library/std/src/sys/unix/net.rs
index f450d708d..ec861f9cb 100644
--- a/library/std/src/sys/unix/net.rs
+++ b/library/std/src/sys/unix/net.rs
@@ -6,6 +6,7 @@ use crate::net::{Shutdown, SocketAddr};
use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
use crate::str;
use crate::sys::fd::FileDesc;
+use crate::sys::unix::IsMinusOne;
use crate::sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr};
use crate::sys_common::{AsInner, FromInner, IntoInner};
use crate::time::{Duration, Instant};
@@ -103,7 +104,7 @@ impl Socket {
}
}
- #[cfg(not(any(target_os = "vxworks", target_os = "vita")))]
+ #[cfg(not(target_os = "vxworks"))]
pub fn new_pair(fam: c_int, ty: c_int) -> io::Result<(Socket, Socket)> {
unsafe {
let mut fds = [0, 0];
@@ -135,11 +136,27 @@ impl Socket {
}
}
- #[cfg(any(target_os = "vxworks", target_os = "vita"))]
+ #[cfg(target_os = "vxworks")]
pub fn new_pair(_fam: c_int, _ty: c_int) -> io::Result<(Socket, Socket)> {
unimplemented!()
}
+ pub fn connect(&self, addr: &SocketAddr) -> io::Result<()> {
+ let (addr, len) = addr.into_inner();
+ loop {
+ let result = unsafe { libc::connect(self.as_raw_fd(), addr.as_ptr(), len) };
+ if result.is_minus_one() {
+ let err = crate::sys::os::errno();
+ match err {
+ libc::EINTR => continue,
+ libc::EISCONN => return Ok(()),
+ _ => return Err(io::Error::from_raw_os_error(err)),
+ }
+ }
+ return Ok(());
+ }
+ }
+
pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> {
self.set_nonblocking(true)?;
let r = unsafe {
diff --git a/library/std/src/sys/unix/os.rs b/library/std/src/sys/unix/os.rs
index 01ff375d2..dc3c037c0 100644
--- a/library/std/src/sys/unix/os.rs
+++ b/library/std/src/sys/unix/os.rs
@@ -74,6 +74,7 @@ extern "C" {
link_name = "__error"
)]
#[cfg_attr(target_os = "haiku", link_name = "_errnop")]
+ #[cfg_attr(target_os = "aix", link_name = "_Errno")]
fn errno_location() -> *mut c_int;
}
@@ -254,6 +255,41 @@ impl StdError for JoinPathsError {
}
}
+#[cfg(target_os = "aix")]
+pub fn current_exe() -> io::Result<PathBuf> {
+ use crate::io::ErrorKind;
+
+ #[cfg(test)]
+ use realstd::env;
+
+ #[cfg(not(test))]
+ use crate::env;
+
+ let exe_path = env::args().next().ok_or(io::const_io_error!(
+ ErrorKind::NotFound,
+ "an executable path was not found because no arguments were provided through argv"
+ ))?;
+ let path = PathBuf::from(exe_path);
+ if path.is_absolute() {
+ return path.canonicalize();
+ }
+ // Search PWD to infer current_exe.
+ if let Some(pstr) = path.to_str() && pstr.contains("/") {
+ return getcwd().map(|cwd| cwd.join(path))?.canonicalize();
+ }
+ // Search PATH to infer current_exe.
+ if let Some(p) = getenv(OsStr::from_bytes("PATH".as_bytes())) {
+ for search_path in split_paths(&p) {
+ let pb = search_path.join(&path);
+ if pb.is_file() && let Ok(metadata) = crate::fs::metadata(&pb) &&
+ metadata.permissions().mode() & 0o111 != 0 {
+ return pb.canonicalize();
+ }
+ }
+ }
+ Err(io::const_io_error!(ErrorKind::NotFound, "an executable path was not found"))
+}
+
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
pub fn current_exe() -> io::Result<PathBuf> {
unsafe {
diff --git a/library/std/src/sys/unix/process/mod.rs b/library/std/src/sys/unix/process/mod.rs
index 0cf163d9f..074f0a105 100644
--- a/library/std/src/sys/unix/process/mod.rs
+++ b/library/std/src/sys/unix/process/mod.rs
@@ -1,11 +1,13 @@
pub use self::process_common::{Command, CommandArgs, ExitCode, Stdio, StdioPipes};
pub use self::process_inner::{ExitStatus, ExitStatusError, Process};
pub use crate::ffi::OsString as EnvKey;
-pub use crate::sys_common::process::CommandEnvs;
#[cfg_attr(any(target_os = "espidf", target_os = "horizon"), allow(unused))]
mod process_common;
+#[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))]
+mod process_unsupported;
+
cfg_if::cfg_if! {
if #[cfg(target_os = "fuchsia")] {
#[path = "process_fuchsia.rs"]
@@ -15,8 +17,9 @@ cfg_if::cfg_if! {
#[path = "process_vxworks.rs"]
mod process_inner;
} else if #[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))] {
- #[path = "process_unsupported.rs"]
- mod process_inner;
+ mod process_inner {
+ pub use super::process_unsupported::*;
+ }
} else {
#[path = "process_unix.rs"]
mod process_inner;
diff --git a/library/std/src/sys/unix/process/process_common.rs b/library/std/src/sys/unix/process/process_common.rs
index 1ca11a7f9..bac32d9e6 100644
--- a/library/std/src/sys/unix/process/process_common.rs
+++ b/library/std/src/sys/unix/process/process_common.rs
@@ -75,6 +75,7 @@ cfg_if::cfg_if! {
return 0;
}
} else {
+ #[allow(unused_imports)]
pub use libc::{sigemptyset, sigaddset};
}
}
diff --git a/library/std/src/sys/unix/process/process_common/tests.rs b/library/std/src/sys/unix/process/process_common/tests.rs
index 03631e4e3..4e41efc90 100644
--- a/library/std/src/sys/unix/process/process_common/tests.rs
+++ b/library/std/src/sys/unix/process/process_common/tests.rs
@@ -159,3 +159,36 @@ fn test_program_kind() {
);
}
}
+
+// Test that Rust std handles wait status values (`ExitStatus`) the way that Unix does,
+// at least for the values which represent a Unix exit status (`ExitCode`).
+// Should work on every #[cfg(unix)] platform. However:
+#[cfg(not(any(
+ // Fuchsia is not Unix and has totally broken std::os::unix.
+ // https://github.com/rust-lang/rust/issues/58590#issuecomment-836535609
+ target_os = "fuchsia",
+)))]
+#[test]
+fn unix_exit_statuses() {
+ use crate::num::NonZeroI32;
+ use crate::os::unix::process::ExitStatusExt;
+ use crate::process::*;
+
+ for exit_code in 0..=0xff {
+ // FIXME impl From<ExitCode> for ExitStatus and then test that here too;
+ // the two ExitStatus values should be the same
+ let raw_wait_status = exit_code << 8;
+ let exit_status = ExitStatus::from_raw(raw_wait_status);
+
+ assert_eq!(exit_status.code(), Some(exit_code));
+
+ if let Ok(nz) = NonZeroI32::try_from(exit_code) {
+ assert!(!exit_status.success());
+ let es_error = exit_status.exit_ok().unwrap_err();
+ assert_eq!(es_error.code().unwrap(), i32::from(nz));
+ } else {
+ assert!(exit_status.success());
+ assert_eq!(exit_status.exit_ok(), Ok(()));
+ }
+ }
+}
diff --git a/library/std/src/sys/unix/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs
index 564f8c482..72aca4e66 100644
--- a/library/std/src/sys/unix/process/process_unix.rs
+++ b/library/std/src/sys/unix/process/process_unix.rs
@@ -1074,3 +1074,8 @@ impl crate::os::linux::process::ChildExt for crate::process::Child {
#[cfg(test)]
#[path = "process_unix/tests.rs"]
mod tests;
+
+// See [`process_unsupported_wait_status::compare_with_linux`];
+#[cfg(all(test, target_os = "linux"))]
+#[path = "process_unsupported/wait_status.rs"]
+mod process_unsupported_wait_status;
diff --git a/library/std/src/sys/unix/process/process_unsupported.rs b/library/std/src/sys/unix/process/process_unsupported.rs
index 8e0b971af..2fbb31922 100644
--- a/library/std/src/sys/unix/process/process_unsupported.rs
+++ b/library/std/src/sys/unix/process/process_unsupported.rs
@@ -55,68 +55,20 @@ impl Process {
}
}
-#[derive(PartialEq, Eq, Clone, Copy, Debug, Default)]
-pub struct ExitStatus(c_int);
-
-impl ExitStatus {
- #[cfg_attr(target_os = "horizon", allow(unused))]
- pub fn success(&self) -> bool {
- self.code() == Some(0)
- }
-
- pub fn exit_ok(&self) -> Result<(), ExitStatusError> {
- Err(ExitStatusError(1.try_into().unwrap()))
- }
-
- pub fn code(&self) -> Option<i32> {
- None
- }
-
- pub fn signal(&self) -> Option<i32> {
- None
- }
-
- pub fn core_dumped(&self) -> bool {
- false
- }
-
- pub fn stopped_signal(&self) -> Option<i32> {
- None
- }
-
- pub fn continued(&self) -> bool {
- false
- }
-
- pub fn into_raw(&self) -> c_int {
- 0
- }
-}
-
-/// Converts a raw `c_int` to a type-safe `ExitStatus` by wrapping it without copying.
-impl From<c_int> for ExitStatus {
- fn from(a: c_int) -> ExitStatus {
- ExitStatus(a as i32)
- }
-}
-
-impl fmt::Display for ExitStatus {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "exit code: {}", self.0)
- }
-}
+mod wait_status;
+pub use wait_status::ExitStatus;
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct ExitStatusError(NonZero_c_int);
impl Into<ExitStatus> for ExitStatusError {
fn into(self) -> ExitStatus {
- ExitStatus(self.0.into())
+ ExitStatus::from(c_int::from(self.0))
}
}
impl ExitStatusError {
pub fn code(self) -> Option<NonZeroI32> {
- ExitStatus(self.0.into()).code().map(|st| st.try_into().unwrap())
+ ExitStatus::from(c_int::from(self.0)).code().map(|st| st.try_into().unwrap())
}
}
diff --git a/library/std/src/sys/unix/process/process_unsupported/wait_status.rs b/library/std/src/sys/unix/process/process_unsupported/wait_status.rs
new file mode 100644
index 000000000..72b7ae18c
--- /dev/null
+++ b/library/std/src/sys/unix/process/process_unsupported/wait_status.rs
@@ -0,0 +1,84 @@
+//! Emulated wait status for non-Unix #[cfg(unix) platforms
+//!
+//! Separate module to facilitate testing against a real Unix implementation.
+use core::ffi::NonZero_c_int;
+
+use crate::ffi::c_int;
+use crate::fmt;
+
+use super::ExitStatusError;
+
+/// Emulated wait status for use by `process_unsupported.rs`
+///
+/// Uses the "traditional unix" encoding. For use on platfors which are `#[cfg(unix)]`
+/// but do not actually support subprocesses at all.
+///
+/// These platforms aren't Unix, but are simply pretending to be for porting convenience.
+/// So, we provide a faithful pretence here.
+#[derive(PartialEq, Eq, Clone, Copy, Debug, Default)]
+pub struct ExitStatus {
+ wait_status: c_int,
+}
+
+/// Converts a raw `c_int` to a type-safe `ExitStatus` by wrapping it
+impl From<c_int> for ExitStatus {
+ fn from(wait_status: c_int) -> ExitStatus {
+ ExitStatus { wait_status }
+ }
+}
+
+impl fmt::Display for ExitStatus {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "emulated wait status: {}", self.wait_status)
+ }
+}
+
+impl ExitStatus {
+ pub fn code(&self) -> Option<i32> {
+ // Linux and FreeBSD both agree that values linux 0x80
+ // count as "WIFEXITED" even though this is quite mad.
+ // Likewise the macros disregard all the high bits, so are happy to declare
+ // out-of-range values to be WIFEXITED, WIFSTOPPED, etc.
+ let w = self.wait_status;
+ if (w & 0x7f) == 0 { Some((w & 0xff00) >> 8) } else { None }
+ }
+
+ #[allow(unused)]
+ pub fn exit_ok(&self) -> Result<(), ExitStatusError> {
+ // This assumes that WIFEXITED(status) && WEXITSTATUS==0 corresponds to status==0. This is
+ // true on all actual versions of Unix, is widely assumed, and is specified in SuS
+ // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html. If it is not
+ // true for a platform pretending to be Unix, the tests (our doctests, and also
+ // process_unix/tests.rs) will spot it. `ExitStatusError::code` assumes this too.
+ match NonZero_c_int::try_from(self.wait_status) {
+ /* was nonzero */ Ok(failure) => Err(ExitStatusError(failure)),
+ /* was zero, couldn't convert */ Err(_) => Ok(()),
+ }
+ }
+
+ pub fn signal(&self) -> Option<i32> {
+ let signal = self.wait_status & 0x007f;
+ if signal > 0 && signal < 0x7f { Some(signal) } else { None }
+ }
+
+ pub fn core_dumped(&self) -> bool {
+ self.signal().is_some() && (self.wait_status & 0x80) != 0
+ }
+
+ pub fn stopped_signal(&self) -> Option<i32> {
+ let w = self.wait_status;
+ if (w & 0xff) == 0x7f { Some((w & 0xff00) >> 8) } else { None }
+ }
+
+ pub fn continued(&self) -> bool {
+ self.wait_status == 0xffff
+ }
+
+ pub fn into_raw(&self) -> c_int {
+ self.wait_status
+ }
+}
+
+#[cfg(test)]
+#[path = "wait_status/tests.rs"] // needed because of strange layout of process_unsupported
+mod tests;
diff --git a/library/std/src/sys/unix/process/process_unsupported/wait_status/tests.rs b/library/std/src/sys/unix/process/process_unsupported/wait_status/tests.rs
new file mode 100644
index 000000000..5132eab10
--- /dev/null
+++ b/library/std/src/sys/unix/process/process_unsupported/wait_status/tests.rs
@@ -0,0 +1,36 @@
+// Note that tests in this file are run on Linux as well as on platforms using process_unsupported
+
+// Test that our emulation exactly matches Linux
+//
+// This test runs *on Linux* but it tests
+// the implementation used on non-Unix `#[cfg(unix)]` platforms.
+//
+// I.e. we're using Linux as a proxy for "trad unix".
+#[cfg(target_os = "linux")]
+#[test]
+fn compare_with_linux() {
+ use super::ExitStatus as Emulated;
+ use crate::os::unix::process::ExitStatusExt as _;
+ use crate::process::ExitStatus as Real;
+
+ // Check that we handle out-of-range values similarly, too.
+ for wstatus in -0xf_ffff..0xf_ffff {
+ let emulated = Emulated::from(wstatus);
+ let real = Real::from_raw(wstatus);
+
+ macro_rules! compare { { $method:ident } => {
+ assert_eq!(
+ emulated.$method(),
+ real.$method(),
+ "{wstatus:#x}.{}()",
+ stringify!($method),
+ );
+ } }
+ compare!(code);
+ compare!(signal);
+ compare!(core_dumped);
+ compare!(stopped_signal);
+ compare!(continued);
+ compare!(into_raw);
+ }
+}
diff --git a/library/std/src/sys/unix/rand.rs b/library/std/src/sys/unix/rand.rs
index fbf158f56..2825d1677 100644
--- a/library/std/src/sys/unix/rand.rs
+++ b/library/std/src/sys/unix/rand.rs
@@ -62,18 +62,15 @@ mod imp {
unsafe { getrandom(buf.as_mut_ptr().cast(), buf.len(), libc::GRND_NONBLOCK) }
}
- #[cfg(any(target_os = "espidf", target_os = "horizon"))]
+ #[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "freebsd"))]
fn getrandom(buf: &mut [u8]) -> libc::ssize_t {
- unsafe { libc::getrandom(buf.as_mut_ptr().cast(), buf.len(), 0) }
- }
-
- #[cfg(target_os = "freebsd")]
- fn getrandom(buf: &mut [u8]) -> libc::ssize_t {
- // FIXME: using the above when libary std's libc is updated
+ #[cfg(not(target_os = "freebsd"))]
+ use libc::getrandom;
+ #[cfg(target_os = "freebsd")]
extern "C" {
fn getrandom(
- buffer: *mut libc::c_void,
- length: libc::size_t,
+ buf: *mut libc::c_void,
+ buflen: libc::size_t,
flags: libc::c_uint,
) -> libc::ssize_t;
}
@@ -154,40 +151,65 @@ mod imp {
}
}
-#[cfg(target_os = "macos")]
+#[cfg(target_vendor = "apple")]
mod imp {
- use crate::fs::File;
- use crate::io::Read;
- use crate::sys::os::errno;
- use crate::sys::weak::weak;
+ use crate::io;
use libc::{c_int, c_void, size_t};
- fn getentropy_fill_bytes(v: &mut [u8]) -> bool {
- weak!(fn getentropy(*mut c_void, size_t) -> c_int);
-
- getentropy
- .get()
- .map(|f| {
- // getentropy(2) permits a maximum buffer size of 256 bytes
- for s in v.chunks_mut(256) {
- let ret = unsafe { f(s.as_mut_ptr() as *mut c_void, s.len()) };
- if ret == -1 {
- panic!("unexpected getentropy error: {}", errno());
- }
- }
- true
- })
- .unwrap_or(false)
+ #[inline(always)]
+ fn random_failure() -> ! {
+ panic!("unexpected random generation error: {}", io::Error::last_os_error());
}
- pub fn fill_bytes(v: &mut [u8]) {
- if getentropy_fill_bytes(v) {
- return;
+ #[cfg(target_os = "macos")]
+ fn getentropy_fill_bytes(v: &mut [u8]) {
+ extern "C" {
+ fn getentropy(bytes: *mut c_void, count: size_t) -> c_int;
}
- // for older macos which doesn't support getentropy
- let mut file = File::open("/dev/urandom").expect("failed to open /dev/urandom");
- file.read_exact(v).expect("failed to read /dev/urandom")
+ // getentropy(2) permits a maximum buffer size of 256 bytes
+ for s in v.chunks_mut(256) {
+ let ret = unsafe { getentropy(s.as_mut_ptr().cast(), s.len()) };
+ if ret == -1 {
+ random_failure()
+ }
+ }
+ }
+
+ #[cfg(not(target_os = "macos"))]
+ fn ccrandom_fill_bytes(v: &mut [u8]) {
+ extern "C" {
+ fn CCRandomGenerateBytes(bytes: *mut c_void, count: size_t) -> c_int;
+ }
+
+ let ret = unsafe { CCRandomGenerateBytes(v.as_mut_ptr().cast(), v.len()) };
+ if ret == -1 {
+ random_failure()
+ }
+ }
+
+ pub fn fill_bytes(v: &mut [u8]) {
+ // All supported versions of macOS (10.12+) support getentropy.
+ //
+ // `getentropy` is measurably faster (via Divan) then the other alternatives so its preferred
+ // when usable.
+ #[cfg(target_os = "macos")]
+ getentropy_fill_bytes(v);
+
+ // On Apple platforms, `CCRandomGenerateBytes` and `SecRandomCopyBytes` simply
+ // call into `CCRandomCopyBytes` with `kCCRandomDefault`. `CCRandomCopyBytes`
+ // manages a CSPRNG which is seeded from the kernel's CSPRNG and which runs on
+ // its own thread accessed via GCD. This seems needlessly heavyweight for our purposes
+ // so we only use it on non-Mac OSes where the better entrypoints are blocked.
+ //
+ // `CCRandomGenerateBytes` is used instead of `SecRandomCopyBytes` because the former is accessible
+ // via `libSystem` (libc) while the other needs to link to `Security.framework`.
+ //
+ // Note that while `getentropy` has a available attribute in the macOS headers, the lack
+ // of a header in the iOS (and others) SDK means that its can cause app store rejections.
+ // Just use `CCRandomGenerateBytes` instead.
+ #[cfg(not(target_os = "macos"))]
+ ccrandom_fill_bytes(v);
}
}
@@ -206,36 +228,7 @@ mod imp {
}
}
-// On iOS and MacOS `SecRandomCopyBytes` calls `CCRandomCopyBytes` with
-// `kCCRandomDefault`. `CCRandomCopyBytes` manages a CSPRNG which is seeded
-// from `/dev/random` and which runs on its own thread accessed via GCD.
-// This seems needlessly heavyweight for the purposes of generating two u64s
-// once per thread in `hashmap_random_keys`. Therefore `SecRandomCopyBytes` is
-// only used on iOS where direct access to `/dev/urandom` is blocked by the
-// sandbox.
-#[cfg(any(target_os = "ios", target_os = "tvos", target_os = "watchos"))]
-mod imp {
- use crate::io;
- use crate::ptr;
- use libc::{c_int, size_t};
-
- enum SecRandom {}
-
- #[allow(non_upper_case_globals)]
- const kSecRandomDefault: *const SecRandom = ptr::null();
-
- extern "C" {
- fn SecRandomCopyBytes(rnd: *const SecRandom, count: size_t, bytes: *mut u8) -> c_int;
- }
-
- pub fn fill_bytes(v: &mut [u8]) {
- let ret = unsafe { SecRandomCopyBytes(kSecRandomDefault, v.len(), v.as_mut_ptr()) };
- if ret == -1 {
- panic!("couldn't generate random bytes: {}", io::Error::last_os_error());
- }
- }
-}
-
+// FIXME: once the 10.x release becomes the minimum, this can be dropped for simplification.
#[cfg(target_os = "netbsd")]
mod imp {
use crate::ptr;
diff --git a/library/std/src/sys/unix/stack_overflow.rs b/library/std/src/sys/unix/stack_overflow.rs
index 73c530786..3dbab4cc4 100644
--- a/library/std/src/sys/unix/stack_overflow.rs
+++ b/library/std/src/sys/unix/stack_overflow.rs
@@ -134,9 +134,19 @@ mod imp {
// OpenBSD requires this flag for stack mapping
// otherwise the said mapping will fail as a no-op on most systems
// and has a different meaning on FreeBSD
- #[cfg(any(target_os = "openbsd", target_os = "netbsd", target_os = "linux",))]
+ #[cfg(any(
+ target_os = "openbsd",
+ target_os = "netbsd",
+ target_os = "linux",
+ target_os = "dragonfly",
+ ))]
let flags = MAP_PRIVATE | MAP_ANON | libc::MAP_STACK;
- #[cfg(not(any(target_os = "openbsd", target_os = "netbsd", target_os = "linux",)))]
+ #[cfg(not(any(
+ target_os = "openbsd",
+ target_os = "netbsd",
+ target_os = "linux",
+ target_os = "dragonfly",
+ )))]
let flags = MAP_PRIVATE | MAP_ANON;
let stackp =
mmap64(ptr::null_mut(), SIGSTKSZ + page_size(), PROT_READ | PROT_WRITE, flags, -1, 0);
diff --git a/library/std/src/sys/unix/thread.rs b/library/std/src/sys/unix/thread.rs
index 311ed9502..29db9468e 100644
--- a/library/std/src/sys/unix/thread.rs
+++ b/library/std/src/sys/unix/thread.rs
@@ -207,7 +207,9 @@ impl Thread {
pub fn set_name(name: &CStr) {
unsafe {
let thread_self = libc::find_thread(ptr::null_mut());
- libc::rename_thread(thread_self, name.as_ptr());
+ let res = libc::rename_thread(thread_self, name.as_ptr());
+ // We have no good way of propagating errors here, but in debug-builds let's check that this actually worked.
+ debug_assert_eq!(res, libc::B_OK);
}
}
@@ -218,6 +220,7 @@ impl Thread {
target_os = "redox",
target_os = "vxworks",
target_os = "hurd",
+ target_os = "aix",
))]
pub fn set_name(_name: &CStr) {
// Newlib, Emscripten, and VxWorks have no way to set a thread name.
@@ -317,6 +320,7 @@ pub fn available_parallelism() -> io::Result<NonZeroUsize> {
target_os = "macos",
target_os = "solaris",
target_os = "illumos",
+ target_os = "aix",
))] {
#[allow(unused_assignments)]
#[allow(unused_mut)]
diff --git a/library/std/src/sys/unix/thread_local_dtor.rs b/library/std/src/sys/unix/thread_local_dtor.rs
index fba2a676f..06399e8a2 100644
--- a/library/std/src/sys/unix/thread_local_dtor.rs
+++ b/library/std/src/sys/unix/thread_local_dtor.rs
@@ -11,28 +11,47 @@
// 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.
+#[allow(unexpected_cfgs)]
#[cfg(any(target_os = "linux", target_os = "fuchsia", target_os = "redox", target_os = "hurd"))]
+// FIXME: The Rust compiler currently omits weakly function definitions (i.e.,
+// __cxa_thread_atexit_impl) and its metadata from LLVM IR.
+#[no_sanitize(cfi, kcfi)]
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;
+ /// This is necessary because the __cxa_thread_atexit_impl implementation
+ /// std links to by default may be a C or C++ implementation that was not
+ /// compiled using the Clang integer normalization option.
+ #[cfg(not(sanitizer_cfi_normalize_integers))]
+ #[cfi_encoding = "i"]
+ #[repr(transparent)]
+ pub struct c_int(pub libc::c_int);
+
extern "C" {
#[linkage = "extern_weak"]
static __dso_handle: *mut u8;
#[linkage = "extern_weak"]
- static __cxa_thread_atexit_impl: *const libc::c_void;
+ static __cxa_thread_atexit_impl: Option<
+ extern "C" fn(
+ unsafe extern "C" fn(*mut libc::c_void),
+ *mut libc::c_void,
+ *mut libc::c_void,
+ ) -> c_int,
+ >;
}
- if !__cxa_thread_atexit_impl.is_null() {
- type F = unsafe extern "C" fn(
- dtor: unsafe extern "C" fn(*mut u8),
- arg: *mut u8,
- dso_handle: *mut u8,
- ) -> libc::c_int;
- mem::transmute::<*const libc::c_void, F>(__cxa_thread_atexit_impl)(
- dtor,
- t,
- &__dso_handle as *const _ as *mut _,
- );
+
+ if let Some(f) = __cxa_thread_atexit_impl {
+ unsafe {
+ f(
+ mem::transmute::<
+ unsafe extern "C" fn(*mut u8),
+ unsafe extern "C" fn(*mut libc::c_void),
+ >(dtor),
+ t.cast(),
+ &__dso_handle as *const _ as *mut _,
+ );
+ }
return;
}
register_dtor_fallback(t, dtor);
@@ -48,17 +67,16 @@ 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(any(target_os = "macos", target_os = "ios", target_os = "watchos"))]
+#[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos", target_os = "tvos"))]
pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
- use crate::cell::Cell;
- use crate::mem;
+ use crate::cell::{Cell, RefCell};
use crate::ptr;
#[thread_local]
static REGISTERED: Cell<bool> = Cell::new(false);
#[thread_local]
- static mut DTORS: Vec<(*mut u8, unsafe extern "C" fn(*mut u8))> = Vec::new();
+ static DTORS: RefCell<Vec<(*mut u8, unsafe extern "C" fn(*mut u8))>> = RefCell::new(Vec::new());
if !REGISTERED.get() {
_tlv_atexit(run_dtors, ptr::null_mut());
@@ -69,21 +87,28 @@ pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
fn _tlv_atexit(dtor: unsafe extern "C" fn(*mut u8), arg: *mut u8);
}
- let list = &mut DTORS;
- list.push((t, dtor));
+ match DTORS.try_borrow_mut() {
+ Ok(mut dtors) => dtors.push((t, dtor)),
+ Err(_) => rtabort!("global allocator may not use TLS"),
+ }
unsafe extern "C" fn run_dtors(_: *mut u8) {
- let mut list = mem::take(&mut DTORS);
+ let mut list = DTORS.take();
while !list.is_empty() {
for (ptr, dtor) in list {
dtor(ptr);
}
- list = mem::take(&mut DTORS);
+ list = DTORS.take();
}
}
}
-#[cfg(any(target_os = "vxworks", target_os = "horizon", target_os = "emscripten"))]
+#[cfg(any(
+ target_os = "vxworks",
+ target_os = "horizon",
+ target_os = "emscripten",
+ target_os = "aix"
+))]
#[cfg_attr(target_family = "wasm", allow(unused))] // might remain unused depending on target details (e.g. wasm32-unknown-emscripten)
pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
use crate::sys_common::thread_local_dtor::register_dtor_fallback;
diff --git a/library/std/src/sys/unix/time.rs b/library/std/src/sys/unix/time.rs
index 4fe61b284..f2e86a4fb 100644
--- a/library/std/src/sys/unix/time.rs
+++ b/library/std/src/sys/unix/time.rs
@@ -1,8 +1,6 @@
use crate::fmt;
use crate::time::Duration;
-pub use self::inner::Instant;
-
const NSEC_PER_SEC: u64 = 1_000_000_000;
pub const UNIX_EPOCH: SystemTime = SystemTime { t: Timespec::zero() };
#[allow(dead_code)] // Used for pthread condvar timeouts
@@ -40,6 +38,10 @@ impl SystemTime {
SystemTime { t: Timespec::new(tv_sec, tv_nsec) }
}
+ pub fn now() -> SystemTime {
+ SystemTime { t: Timespec::now(libc::CLOCK_REALTIME) }
+ }
+
pub fn sub_time(&self, other: &SystemTime) -> Result<Duration, Duration> {
self.t.sub_timespec(&other.t)
}
@@ -74,11 +76,65 @@ impl Timespec {
}
const fn new(tv_sec: i64, tv_nsec: i64) -> Timespec {
+ // On Apple OS, dates before epoch are represented differently than on other
+ // Unix platforms: e.g. 1/10th of a second before epoch is represented as `seconds=-1`
+ // and `nanoseconds=100_000_000` on other platforms, but is `seconds=0` and
+ // `nanoseconds=-900_000_000` on Apple OS.
+ //
+ // To compensate, we first detect this special case by checking if both
+ // seconds and nanoseconds are in range, and then correct the value for seconds
+ // and nanoseconds to match the common unix representation.
+ //
+ // Please note that Apple OS nonetheless accepts the standard unix format when
+ // setting file times, which makes this compensation round-trippable and generally
+ // transparent.
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "tvos",
+ target_os = "watchos"
+ ))]
+ let (tv_sec, tv_nsec) =
+ if (tv_sec <= 0 && tv_sec > i64::MIN) && (tv_nsec < 0 && tv_nsec > -1_000_000_000) {
+ (tv_sec - 1, tv_nsec + 1_000_000_000)
+ } else {
+ (tv_sec, tv_nsec)
+ };
assert!(tv_nsec >= 0 && tv_nsec < NSEC_PER_SEC as i64);
// SAFETY: The assert above checks tv_nsec is within the valid range
Timespec { tv_sec, tv_nsec: unsafe { Nanoseconds(tv_nsec as u32) } }
}
+ pub fn now(clock: libc::clockid_t) -> Timespec {
+ use crate::mem::MaybeUninit;
+ use crate::sys::cvt;
+
+ // Try to use 64-bit time in preparation for Y2038.
+ #[cfg(all(
+ target_os = "linux",
+ target_env = "gnu",
+ target_pointer_width = "32",
+ not(target_arch = "riscv32")
+ ))]
+ {
+ use crate::sys::weak::weak;
+
+ // __clock_gettime64 was added to 32-bit arches in glibc 2.34,
+ // and it handles both vDSO calls and ENOSYS fallbacks itself.
+ weak!(fn __clock_gettime64(libc::clockid_t, *mut __timespec64) -> libc::c_int);
+
+ if let Some(clock_gettime64) = __clock_gettime64.get() {
+ let mut t = MaybeUninit::uninit();
+ cvt(unsafe { clock_gettime64(clock, t.as_mut_ptr()) }).unwrap();
+ return Timespec::from(unsafe { t.assume_init() });
+ }
+ }
+
+ let mut t = MaybeUninit::uninit();
+ cvt(unsafe { libc::clock_gettime(clock, t.as_mut_ptr()) }).unwrap();
+ Timespec::from(unsafe { t.assume_init() })
+ }
+
pub fn sub_timespec(&self, other: &Timespec) -> Result<Duration, Duration> {
if self >= other {
// NOTE(eddyb) two aspects of this `if`-`else` are required for LLVM
@@ -216,209 +272,59 @@ impl From<__timespec64> for Timespec {
}
}
-#[cfg(any(
- all(target_os = "macos", any(not(target_arch = "aarch64"))),
- target_os = "ios",
- target_os = "watchos",
- target_os = "tvos"
-))]
-mod inner {
- use crate::sync::atomic::{AtomicU64, Ordering};
- use crate::sys::cvt;
- use crate::sys_common::mul_div_u64;
- use crate::time::Duration;
-
- use super::{SystemTime, Timespec, NSEC_PER_SEC};
-
- #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
- pub struct Instant {
- t: u64,
- }
-
- #[repr(C)]
- #[derive(Copy, Clone)]
- struct mach_timebase_info {
- numer: u32,
- denom: u32,
- }
- type mach_timebase_info_t = *mut mach_timebase_info;
- type kern_return_t = libc::c_int;
-
- impl Instant {
- pub fn now() -> Instant {
- extern "C" {
- fn mach_absolute_time() -> u64;
- }
- Instant { t: unsafe { mach_absolute_time() } }
- }
-
- pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
- let diff = self.t.checked_sub(other.t)?;
- let info = info();
- let nanos = mul_div_u64(diff, info.numer as u64, info.denom as u64);
- Some(Duration::new(nanos / NSEC_PER_SEC, (nanos % NSEC_PER_SEC) as u32))
- }
-
- pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> {
- Some(Instant { t: self.t.checked_add(checked_dur2intervals(other)?)? })
- }
-
- pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
- Some(Instant { t: self.t.checked_sub(checked_dur2intervals(other)?)? })
- }
- }
-
- impl SystemTime {
- pub fn now() -> SystemTime {
- use crate::ptr;
-
- let mut s = libc::timeval { tv_sec: 0, tv_usec: 0 };
- cvt(unsafe { libc::gettimeofday(&mut s, ptr::null_mut()) }).unwrap();
- return SystemTime::from(s);
- }
- }
-
- impl From<libc::timeval> for Timespec {
- fn from(t: libc::timeval) -> Timespec {
- Timespec::new(t.tv_sec as i64, 1000 * t.tv_usec as i64)
- }
- }
-
- impl From<libc::timeval> for SystemTime {
- fn from(t: libc::timeval) -> SystemTime {
- SystemTime { t: Timespec::from(t) }
- }
- }
-
- fn checked_dur2intervals(dur: &Duration) -> Option<u64> {
- let nanos =
- dur.as_secs().checked_mul(NSEC_PER_SEC)?.checked_add(dur.subsec_nanos() as u64)?;
- let info = info();
- Some(mul_div_u64(nanos, info.denom as u64, info.numer as u64))
- }
-
- fn info() -> mach_timebase_info {
- // INFO_BITS conceptually is an `Option<mach_timebase_info>`. We can do
- // this in 64 bits because we know 0 is never a valid value for the
- // `denom` field.
- //
- // Encoding this as a single `AtomicU64` allows us to use `Relaxed`
- // operations, as we are only interested in the effects on a single
- // memory location.
- static INFO_BITS: AtomicU64 = AtomicU64::new(0);
-
- // If a previous thread has initialized `INFO_BITS`, use it.
- let info_bits = INFO_BITS.load(Ordering::Relaxed);
- if info_bits != 0 {
- return info_from_bits(info_bits);
- }
-
- // ... otherwise learn for ourselves ...
- extern "C" {
- fn mach_timebase_info(info: mach_timebase_info_t) -> kern_return_t;
- }
-
- let mut info = info_from_bits(0);
- unsafe {
- mach_timebase_info(&mut info);
- }
- INFO_BITS.store(info_to_bits(info), Ordering::Relaxed);
- info
- }
-
- #[inline]
- fn info_to_bits(info: mach_timebase_info) -> u64 {
- ((info.denom as u64) << 32) | (info.numer as u64)
- }
-
- #[inline]
- fn info_from_bits(bits: u64) -> mach_timebase_info {
- mach_timebase_info { numer: bits as u32, denom: (bits >> 32) as u32 }
- }
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct Instant {
+ t: Timespec,
}
-#[cfg(not(any(
- all(target_os = "macos", any(not(target_arch = "aarch64"))),
- target_os = "ios",
- target_os = "watchos",
- target_os = "tvos"
-)))]
-mod inner {
- use crate::fmt;
- use crate::mem::MaybeUninit;
- use crate::sys::cvt;
- use crate::time::Duration;
-
- use super::{SystemTime, Timespec};
-
- #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
- pub struct Instant {
- t: Timespec,
+impl Instant {
+ pub fn now() -> Instant {
+ // https://www.manpagez.com/man/3/clock_gettime/
+ //
+ // CLOCK_UPTIME_RAW clock that increments monotonically, in the same man-
+ // ner as CLOCK_MONOTONIC_RAW, but that does not incre-
+ // ment while the system is asleep. The returned value
+ // is identical to the result of mach_absolute_time()
+ // after the appropriate mach_timebase conversion is
+ // applied.
+ //
+ // Instant on macos was historically implemented using mach_absolute_time;
+ // we preserve this value domain out of an abundance of caution.
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "watchos",
+ target_os = "tvos"
+ ))]
+ const clock_id: libc::clockid_t = libc::CLOCK_UPTIME_RAW;
+ #[cfg(not(any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "watchos",
+ target_os = "tvos"
+ )))]
+ const clock_id: libc::clockid_t = libc::CLOCK_MONOTONIC;
+ Instant { t: Timespec::now(clock_id) }
}
- impl Instant {
- pub fn now() -> Instant {
- #[cfg(target_os = "macos")]
- const clock_id: libc::clockid_t = libc::CLOCK_UPTIME_RAW;
- #[cfg(not(target_os = "macos"))]
- const clock_id: libc::clockid_t = libc::CLOCK_MONOTONIC;
- Instant { t: Timespec::now(clock_id) }
- }
-
- pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
- self.t.sub_timespec(&other.t).ok()
- }
-
- pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> {
- Some(Instant { t: self.t.checked_add_duration(other)? })
- }
-
- pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
- Some(Instant { t: self.t.checked_sub_duration(other)? })
- }
+ pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
+ self.t.sub_timespec(&other.t).ok()
}
- impl fmt::Debug for Instant {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_struct("Instant")
- .field("tv_sec", &self.t.tv_sec)
- .field("tv_nsec", &self.t.tv_nsec.0)
- .finish()
- }
+ pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> {
+ Some(Instant { t: self.t.checked_add_duration(other)? })
}
- impl SystemTime {
- pub fn now() -> SystemTime {
- SystemTime { t: Timespec::now(libc::CLOCK_REALTIME) }
- }
+ pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
+ Some(Instant { t: self.t.checked_sub_duration(other)? })
}
+}
- impl Timespec {
- pub fn now(clock: libc::clockid_t) -> Timespec {
- // Try to use 64-bit time in preparation for Y2038.
- #[cfg(all(
- target_os = "linux",
- target_env = "gnu",
- target_pointer_width = "32",
- not(target_arch = "riscv32")
- ))]
- {
- use crate::sys::weak::weak;
-
- // __clock_gettime64 was added to 32-bit arches in glibc 2.34,
- // and it handles both vDSO calls and ENOSYS fallbacks itself.
- weak!(fn __clock_gettime64(libc::clockid_t, *mut super::__timespec64) -> libc::c_int);
-
- if let Some(clock_gettime64) = __clock_gettime64.get() {
- let mut t = MaybeUninit::uninit();
- cvt(unsafe { clock_gettime64(clock, t.as_mut_ptr()) }).unwrap();
- return Timespec::from(unsafe { t.assume_init() });
- }
- }
-
- let mut t = MaybeUninit::uninit();
- cvt(unsafe { libc::clock_gettime(clock, t.as_mut_ptr()) }).unwrap();
- Timespec::from(unsafe { t.assume_init() })
- }
+impl fmt::Debug for Instant {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Instant")
+ .field("tv_sec", &self.t.tv_sec)
+ .field("tv_nsec", &self.t.tv_nsec.0)
+ .finish()
}
}
diff --git a/library/std/src/sys/wasi/mod.rs b/library/std/src/sys/wasi/mod.rs
index 5cbb5cb65..5919cc506 100644
--- a/library/std/src/sys/wasi/mod.rs
+++ b/library/std/src/sys/wasi/mod.rs
@@ -82,31 +82,99 @@ pub fn is_interrupted(errno: i32) -> bool {
}
pub fn decode_error_kind(errno: i32) -> std_io::ErrorKind {
- use std_io::ErrorKind::*;
- if errno > u16::MAX as i32 || errno < 0 {
- return Uncategorized;
+ use std_io::ErrorKind;
+
+ let Ok(errno) = u16::try_from(errno) else {
+ return ErrorKind::Uncategorized;
+ };
+
+ macro_rules! match_errno {
+ ($($($errno:ident)|+ => $errkind:ident),*, _ => $wildcard:ident $(,)?) => {
+ match errno {
+ $(e if $(e == ::wasi::$errno.raw())||+ => ErrorKind::$errkind),*,
+ _ => ErrorKind::$wildcard,
+ }
+ };
}
- match errno {
- e if e == wasi::ERRNO_CONNREFUSED.raw().into() => ConnectionRefused,
- e if e == wasi::ERRNO_CONNRESET.raw().into() => ConnectionReset,
- e if e == wasi::ERRNO_PERM.raw().into() || e == wasi::ERRNO_ACCES.raw().into() => {
- PermissionDenied
- }
- e if e == wasi::ERRNO_PIPE.raw().into() => BrokenPipe,
- e if e == wasi::ERRNO_NOTCONN.raw().into() => NotConnected,
- e if e == wasi::ERRNO_CONNABORTED.raw().into() => ConnectionAborted,
- e if e == wasi::ERRNO_ADDRNOTAVAIL.raw().into() => AddrNotAvailable,
- e if e == wasi::ERRNO_ADDRINUSE.raw().into() => AddrInUse,
- e if e == wasi::ERRNO_NOENT.raw().into() => NotFound,
- e if e == wasi::ERRNO_INTR.raw().into() => Interrupted,
- e if e == wasi::ERRNO_INVAL.raw().into() => InvalidInput,
- e if e == wasi::ERRNO_TIMEDOUT.raw().into() => TimedOut,
- e if e == wasi::ERRNO_EXIST.raw().into() => AlreadyExists,
- e if e == wasi::ERRNO_AGAIN.raw().into() => WouldBlock,
- e if e == wasi::ERRNO_NOSYS.raw().into() => Unsupported,
- e if e == wasi::ERRNO_NOMEM.raw().into() => OutOfMemory,
- _ => Uncategorized,
+ match_errno! {
+ ERRNO_2BIG => ArgumentListTooLong,
+ ERRNO_ACCES => PermissionDenied,
+ ERRNO_ADDRINUSE => AddrInUse,
+ ERRNO_ADDRNOTAVAIL => AddrNotAvailable,
+ ERRNO_AFNOSUPPORT => Unsupported,
+ ERRNO_AGAIN => WouldBlock,
+ // ALREADY => "connection already in progress",
+ // BADF => "bad file descriptor",
+ // BADMSG => "bad message",
+ ERRNO_BUSY => ResourceBusy,
+ // CANCELED => "operation canceled",
+ // CHILD => "no child processes",
+ ERRNO_CONNABORTED => ConnectionAborted,
+ ERRNO_CONNREFUSED => ConnectionRefused,
+ ERRNO_CONNRESET => ConnectionReset,
+ ERRNO_DEADLK => Deadlock,
+ // DESTADDRREQ => "destination address required",
+ ERRNO_DOM => InvalidInput,
+ // DQUOT => /* reserved */,
+ ERRNO_EXIST => AlreadyExists,
+ // FAULT => "bad address",
+ ERRNO_FBIG => FileTooLarge,
+ ERRNO_HOSTUNREACH => HostUnreachable,
+ // IDRM => "identifier removed",
+ // ILSEQ => "illegal byte sequence",
+ // INPROGRESS => "operation in progress",
+ ERRNO_INTR => Interrupted,
+ ERRNO_INVAL => InvalidInput,
+ ERRNO_IO => Uncategorized,
+ // ISCONN => "socket is connected",
+ ERRNO_ISDIR => IsADirectory,
+ ERRNO_LOOP => FilesystemLoop,
+ // MFILE => "file descriptor value too large",
+ ERRNO_MLINK => TooManyLinks,
+ // MSGSIZE => "message too large",
+ // MULTIHOP => /* reserved */,
+ ERRNO_NAMETOOLONG => InvalidFilename,
+ ERRNO_NETDOWN => NetworkDown,
+ // NETRESET => "connection aborted by network",
+ ERRNO_NETUNREACH => NetworkUnreachable,
+ // NFILE => "too many files open in system",
+ // NOBUFS => "no buffer space available",
+ ERRNO_NODEV => NotFound,
+ ERRNO_NOENT => NotFound,
+ // NOEXEC => "executable file format error",
+ // NOLCK => "no locks available",
+ // NOLINK => /* reserved */,
+ ERRNO_NOMEM => OutOfMemory,
+ // NOMSG => "no message of the desired type",
+ // NOPROTOOPT => "protocol not available",
+ ERRNO_NOSPC => StorageFull,
+ ERRNO_NOSYS => Unsupported,
+ ERRNO_NOTCONN => NotConnected,
+ ERRNO_NOTDIR => NotADirectory,
+ ERRNO_NOTEMPTY => DirectoryNotEmpty,
+ // NOTRECOVERABLE => "state not recoverable",
+ // NOTSOCK => "not a socket",
+ ERRNO_NOTSUP => Unsupported,
+ // NOTTY => "inappropriate I/O control operation",
+ ERRNO_NXIO => NotFound,
+ // OVERFLOW => "value too large to be stored in data type",
+ // OWNERDEAD => "previous owner died",
+ ERRNO_PERM => PermissionDenied,
+ ERRNO_PIPE => BrokenPipe,
+ // PROTO => "protocol error",
+ ERRNO_PROTONOSUPPORT => Unsupported,
+ // PROTOTYPE => "protocol wrong type for socket",
+ // RANGE => "result too large",
+ ERRNO_ROFS => ReadOnlyFilesystem,
+ ERRNO_SPIPE => NotSeekable,
+ ERRNO_SRCH => NotFound,
+ // STALE => /* reserved */,
+ ERRNO_TIMEDOUT => TimedOut,
+ ERRNO_TXTBSY => ResourceBusy,
+ ERRNO_XDEV => CrossesDevices,
+ ERRNO_NOTCAPABLE => PermissionDenied,
+ _ => Uncategorized,
}
}
@@ -124,6 +192,7 @@ pub fn hashmap_random_keys() -> (u64, u64) {
return ret;
}
+#[inline]
fn err2io(err: wasi::Errno) -> std_io::Error {
std_io::Error::from_raw_os_error(err.raw().into())
}
diff --git a/library/std/src/sys/windows/api.rs b/library/std/src/sys/windows/api.rs
new file mode 100644
index 000000000..e9f0bbfbe
--- /dev/null
+++ b/library/std/src/sys/windows/api.rs
@@ -0,0 +1,157 @@
+//! # Safe(r) wrappers around Windows API functions.
+//!
+//! This module contains fairly thin wrappers around Windows API functions,
+//! aimed at centralising safety instead of having unsafe blocks spread
+//! throughout higher level code. This makes it much easier to audit FFI safety.
+//!
+//! Not all functions can be made completely safe without more context but in
+//! such cases we should still endeavour to reduce the caller's burden of safety
+//! as much as possible.
+//!
+//! ## Guidelines for wrappers
+//!
+//! Items here should be named similarly to their raw Windows API name, except
+//! that they follow Rust's case conventions. E.g. function names are
+//! lower_snake_case. The idea here is that it should be easy for a Windows
+//! C/C++ programmer to identify the underlying function that's being wrapped
+//! while not looking too out of place in Rust code.
+//!
+//! Every use of an `unsafe` block must have a related SAFETY comment, even if
+//! it's trivially safe (for example, see `get_last_error`). Public unsafe
+//! functions must document what the caller has to do to call them safely.
+//!
+//! Avoid unchecked `as` casts. For integers, either assert that the integer
+//! is in range or use `try_into` instead. For pointers, prefer to use
+//! `ptr.cast::<Type>()` when possible.
+//!
+//! This module must only depend on core and not on std types as the eventual
+//! hope is to have std depend on sys and not the other way around.
+//! However, some amount of glue code may currently be necessary so such code
+//! should go in sys/windows/mod.rs rather than here. See `IoResult` as an example.
+
+use core::ffi::c_void;
+use core::ptr::addr_of;
+
+use super::c;
+
+/// Helper method for getting the size of `T` as a u32.
+/// Errors at compile time if the size would overflow.
+///
+/// While a type larger than u32::MAX is unlikely, it is possible if only because of a bug.
+/// However, one key motivation for this function is to avoid the temptation to
+/// use frequent `as` casts. This is risky because they are too powerful.
+/// For example, the following will compile today:
+///
+/// `std::mem::size_of::<u64> as u32`
+///
+/// Note that `size_of` is never actually called, instead a function pointer is
+/// converted to a `u32`. Clippy would warn about this but, alas, it's not run
+/// on the standard library.
+const fn win32_size_of<T: Sized>() -> u32 {
+ // Const assert that the size is less than u32::MAX.
+ // Uses a trait to workaround restriction on using generic types in inner items.
+ trait Win32SizeOf: Sized {
+ const WIN32_SIZE_OF: u32 = {
+ let size = core::mem::size_of::<Self>();
+ assert!(size <= u32::MAX as usize);
+ size as u32
+ };
+ }
+ impl<T: Sized> Win32SizeOf for T {}
+
+ T::WIN32_SIZE_OF
+}
+
+/// The `SetFileInformationByHandle` function takes a generic parameter by
+/// making the user specify the type (class), a pointer to the data and its
+/// size. This trait allows attaching that information to a Rust type so that
+/// [`set_file_information_by_handle`] can be called safely.
+///
+/// This trait is designed so that it can support variable sized types.
+/// However, currently Rust's std only uses fixed sized structures.
+///
+/// # Safety
+///
+/// * `as_ptr` must return a pointer to memory that is readable up to `size` bytes.
+/// * `CLASS` must accurately reflect the type pointed to by `as_ptr`. E.g.
+/// the `FILE_BASIC_INFO` structure has the class `FileBasicInfo`.
+pub unsafe trait SetFileInformation {
+ /// The type of information to set.
+ const CLASS: i32;
+ /// A pointer to the file information to set.
+ fn as_ptr(&self) -> *const c_void;
+ /// The size of the type pointed to by `as_ptr`.
+ fn size(&self) -> u32;
+}
+/// Helper trait for implementing `SetFileInformation` for statically sized types.
+unsafe trait SizedSetFileInformation: Sized {
+ const CLASS: i32;
+}
+unsafe impl<T: SizedSetFileInformation> SetFileInformation for T {
+ const CLASS: i32 = T::CLASS;
+ fn as_ptr(&self) -> *const c_void {
+ addr_of!(*self).cast::<c_void>()
+ }
+ fn size(&self) -> u32 {
+ win32_size_of::<Self>()
+ }
+}
+
+// SAFETY: FILE_BASIC_INFO, FILE_END_OF_FILE_INFO, FILE_ALLOCATION_INFO,
+// FILE_DISPOSITION_INFO, FILE_DISPOSITION_INFO_EX and FILE_IO_PRIORITY_HINT_INFO
+// are all plain `repr(C)` structs that only contain primitive types.
+// The given information classes correctly match with the struct.
+unsafe impl SizedSetFileInformation for c::FILE_BASIC_INFO {
+ const CLASS: i32 = c::FileBasicInfo;
+}
+unsafe impl SizedSetFileInformation for c::FILE_END_OF_FILE_INFO {
+ const CLASS: i32 = c::FileEndOfFileInfo;
+}
+unsafe impl SizedSetFileInformation for c::FILE_ALLOCATION_INFO {
+ const CLASS: i32 = c::FileAllocationInfo;
+}
+unsafe impl SizedSetFileInformation for c::FILE_DISPOSITION_INFO {
+ const CLASS: i32 = c::FileDispositionInfo;
+}
+unsafe impl SizedSetFileInformation for c::FILE_DISPOSITION_INFO_EX {
+ const CLASS: i32 = c::FileDispositionInfoEx;
+}
+unsafe impl SizedSetFileInformation for c::FILE_IO_PRIORITY_HINT_INFO {
+ const CLASS: i32 = c::FileIoPriorityHintInfo;
+}
+
+#[inline]
+pub fn set_file_information_by_handle<T: SetFileInformation>(
+ handle: c::HANDLE,
+ info: &T,
+) -> Result<(), WinError> {
+ unsafe fn set_info(
+ handle: c::HANDLE,
+ class: i32,
+ info: *const c_void,
+ size: u32,
+ ) -> Result<(), WinError> {
+ let result = c::SetFileInformationByHandle(handle, class, info, size);
+ (result != 0).then_some(()).ok_or_else(|| get_last_error())
+ }
+ // SAFETY: The `SetFileInformation` trait ensures that this is safe.
+ unsafe { set_info(handle, T::CLASS, info.as_ptr(), info.size()) }
+}
+
+/// Gets the error from the last function.
+/// This must be called immediately after the function that sets the error to
+/// avoid the risk of another function overwriting it.
+pub fn get_last_error() -> WinError {
+ // SAFETY: This just returns a thread-local u32 and has no other effects.
+ unsafe { WinError { code: c::GetLastError() } }
+}
+
+/// An error code as returned by [`get_last_error`].
+///
+/// This is usually a 16-bit Win32 error code but may be a 32-bit HRESULT or NTSTATUS.
+/// Check the documentation of the Windows API function being called for expected errors.
+#[derive(Clone, Copy, PartialEq, Eq)]
+#[repr(transparent)]
+pub struct WinError {
+ pub code: u32,
+}
diff --git a/library/std/src/sys/windows/c.rs b/library/std/src/sys/windows/c.rs
index f3637cbb9..a349e24b0 100644
--- a/library/std/src/sys/windows/c.rs
+++ b/library/std/src/sys/windows/c.rs
@@ -46,6 +46,10 @@ pub use FD_SET as fd_set;
pub use LINGER as linger;
pub use TIMEVAL as timeval;
+// https://learn.microsoft.com/en-us/cpp/c-runtime-library/exit-success-exit-failure?view=msvc-170
+pub const EXIT_SUCCESS: u32 = 0;
+pub const EXIT_FAILURE: u32 = 1;
+
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() };
diff --git a/library/std/src/sys/windows/c/windows_sys.lst b/library/std/src/sys/windows/c/windows_sys.lst
index 0aca37e2d..38bf15b7c 100644
--- a/library/std/src/sys/windows/c/windows_sys.lst
+++ b/library/std/src/sys/windows/c/windows_sys.lst
@@ -1964,6 +1964,7 @@ Windows.Win32.Networking.WinSock.ADDRESS_FAMILY
Windows.Win32.Networking.WinSock.ADDRINFOA
Windows.Win32.Networking.WinSock.AF_INET
Windows.Win32.Networking.WinSock.AF_INET6
+Windows.Win32.Networking.WinSock.AF_UNIX
Windows.Win32.Networking.WinSock.AF_UNSPEC
Windows.Win32.Networking.WinSock.bind
Windows.Win32.Networking.WinSock.closesocket
@@ -2058,6 +2059,7 @@ Windows.Win32.Networking.WinSock.SOCK_RDM
Windows.Win32.Networking.WinSock.SOCK_SEQPACKET
Windows.Win32.Networking.WinSock.SOCK_STREAM
Windows.Win32.Networking.WinSock.SOCKADDR
+Windows.Win32.Networking.WinSock.SOCKADDR_UN
Windows.Win32.Networking.WinSock.SOCKET
Windows.Win32.Networking.WinSock.SOCKET_ERROR
Windows.Win32.Networking.WinSock.SOL_SOCKET
@@ -2222,6 +2224,7 @@ Windows.Win32.Storage.FileSystem.FILE_ACCESS_RIGHTS
Windows.Win32.Storage.FileSystem.FILE_ADD_FILE
Windows.Win32.Storage.FileSystem.FILE_ADD_SUBDIRECTORY
Windows.Win32.Storage.FileSystem.FILE_ALL_ACCESS
+Windows.Win32.Storage.FileSystem.FILE_ALLOCATION_INFO
Windows.Win32.Storage.FileSystem.FILE_APPEND_DATA
Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_ARCHIVE
Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_COMPRESSED
@@ -2282,6 +2285,7 @@ Windows.Win32.Storage.FileSystem.FILE_GENERIC_READ
Windows.Win32.Storage.FileSystem.FILE_GENERIC_WRITE
Windows.Win32.Storage.FileSystem.FILE_ID_BOTH_DIR_INFO
Windows.Win32.Storage.FileSystem.FILE_INFO_BY_HANDLE_CLASS
+Windows.Win32.Storage.FileSystem.FILE_IO_PRIORITY_HINT_INFO
Windows.Win32.Storage.FileSystem.FILE_LIST_DIRECTORY
Windows.Win32.Storage.FileSystem.FILE_NAME_NORMALIZED
Windows.Win32.Storage.FileSystem.FILE_NAME_OPENED
@@ -2503,9 +2507,12 @@ Windows.Win32.System.Threading.CREATE_SEPARATE_WOW_VDM
Windows.Win32.System.Threading.CREATE_SHARED_WOW_VDM
Windows.Win32.System.Threading.CREATE_SUSPENDED
Windows.Win32.System.Threading.CREATE_UNICODE_ENVIRONMENT
+Windows.Win32.System.Threading.CREATE_WAITABLE_TIMER_HIGH_RESOLUTION
+Windows.Win32.System.Threading.CREATE_WAITABLE_TIMER_MANUAL_RESET
Windows.Win32.System.Threading.CreateEventW
Windows.Win32.System.Threading.CreateProcessW
Windows.Win32.System.Threading.CreateThread
+Windows.Win32.System.Threading.CreateWaitableTimerExW
Windows.Win32.System.Threading.DEBUG_ONLY_THIS_PROCESS
Windows.Win32.System.Threading.DEBUG_PROCESS
Windows.Win32.System.Threading.DeleteProcThreadAttributeList
@@ -2542,6 +2549,7 @@ Windows.Win32.System.Threading.REALTIME_PRIORITY_CLASS
Windows.Win32.System.Threading.ReleaseSRWLockExclusive
Windows.Win32.System.Threading.ReleaseSRWLockShared
Windows.Win32.System.Threading.SetThreadStackGuarantee
+Windows.Win32.System.Threading.SetWaitableTimer
Windows.Win32.System.Threading.Sleep
Windows.Win32.System.Threading.SleepConditionVariableSRW
Windows.Win32.System.Threading.SleepEx
@@ -2568,6 +2576,8 @@ Windows.Win32.System.Threading.TerminateProcess
Windows.Win32.System.Threading.THREAD_CREATE_RUN_IMMEDIATELY
Windows.Win32.System.Threading.THREAD_CREATE_SUSPENDED
Windows.Win32.System.Threading.THREAD_CREATION_FLAGS
+Windows.Win32.System.Threading.TIMER_ALL_ACCESS
+Windows.Win32.System.Threading.TIMER_MODIFY_STATE
Windows.Win32.System.Threading.TLS_OUT_OF_INDEXES
Windows.Win32.System.Threading.TlsAlloc
Windows.Win32.System.Threading.TlsFree
diff --git a/library/std/src/sys/windows/c/windows_sys.rs b/library/std/src/sys/windows/c/windows_sys.rs
index 851d15915..e0509e6a5 100644
--- a/library/std/src/sys/windows/c/windows_sys.rs
+++ b/library/std/src/sys/windows/c/windows_sys.rs
@@ -152,6 +152,15 @@ extern "system" {
}
#[link(name = "kernel32")]
extern "system" {
+ pub fn CreateWaitableTimerExW(
+ lptimerattributes: *const SECURITY_ATTRIBUTES,
+ lptimername: PCWSTR,
+ dwflags: u32,
+ dwdesiredaccess: u32,
+ ) -> HANDLE;
+}
+#[link(name = "kernel32")]
+extern "system" {
pub fn DeleteFileW(lpfilename: PCWSTR) -> BOOL;
}
#[link(name = "kernel32")]
@@ -509,6 +518,17 @@ extern "system" {
}
#[link(name = "kernel32")]
extern "system" {
+ pub fn SetWaitableTimer(
+ htimer: HANDLE,
+ lpduetime: *const i64,
+ lperiod: i32,
+ pfncompletionroutine: PTIMERAPCROUTINE,
+ lpargtocompletionroutine: *const ::core::ffi::c_void,
+ fresume: BOOL,
+ ) -> BOOL;
+}
+#[link(name = "kernel32")]
+extern "system" {
pub fn Sleep(dwmilliseconds: u32) -> ();
}
#[link(name = "kernel32")]
@@ -847,6 +867,7 @@ impl ::core::clone::Clone for ADDRINFOA {
}
pub const AF_INET: ADDRESS_FAMILY = 2u16;
pub const AF_INET6: ADDRESS_FAMILY = 23u16;
+pub const AF_UNIX: u16 = 1u16;
pub const AF_UNSPEC: ADDRESS_FAMILY = 0u16;
pub const ALL_PROCESSOR_GROUPS: u32 = 65535u32;
#[repr(C)]
@@ -1164,6 +1185,8 @@ 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 CREATE_WAITABLE_TIMER_HIGH_RESOLUTION: u32 = 2u32;
+pub const CREATE_WAITABLE_TIMER_MANUAL_RESET: u32 = 1u32;
pub const CSTR_EQUAL: COMPARESTRING_RESULT = 2i32;
pub const CSTR_GREATER_THAN: COMPARESTRING_RESULT = 3i32;
pub const CSTR_LESS_THAN: COMPARESTRING_RESULT = 1i32;
@@ -3106,6 +3129,16 @@ impl ::core::clone::Clone for FILETIME {
pub type FILE_ACCESS_RIGHTS = u32;
pub const FILE_ADD_FILE: FILE_ACCESS_RIGHTS = 2u32;
pub const FILE_ADD_SUBDIRECTORY: FILE_ACCESS_RIGHTS = 4u32;
+#[repr(C)]
+pub struct FILE_ALLOCATION_INFO {
+ pub AllocationSize: i64,
+}
+impl ::core::marker::Copy for FILE_ALLOCATION_INFO {}
+impl ::core::clone::Clone for FILE_ALLOCATION_INFO {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
pub const FILE_ALL_ACCESS: FILE_ACCESS_RIGHTS = 2032127u32;
pub const FILE_APPEND_DATA: FILE_ACCESS_RIGHTS = 4u32;
pub const FILE_ATTRIBUTE_ARCHIVE: FILE_FLAGS_AND_ATTRIBUTES = 32u32;
@@ -3247,6 +3280,16 @@ impl ::core::clone::Clone for FILE_ID_BOTH_DIR_INFO {
}
}
pub type FILE_INFO_BY_HANDLE_CLASS = i32;
+#[repr(C)]
+pub struct FILE_IO_PRIORITY_HINT_INFO {
+ pub PriorityHint: PRIORITY_HINT,
+}
+impl ::core::marker::Copy for FILE_IO_PRIORITY_HINT_INFO {}
+impl ::core::clone::Clone for FILE_IO_PRIORITY_HINT_INFO {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
pub const FILE_LIST_DIRECTORY: FILE_ACCESS_RIGHTS = 1u32;
pub const FILE_NAME_NORMALIZED: GETFINALPATHNAMEBYHANDLE_FLAGS = 0u32;
pub const FILE_NAME_OPENED: GETFINALPATHNAMEBYHANDLE_FLAGS = 8u32;
@@ -3752,6 +3795,7 @@ pub const PIPE_SERVER_END: NAMED_PIPE_MODE = 1u32;
pub const PIPE_TYPE_BYTE: NAMED_PIPE_MODE = 0u32;
pub const PIPE_TYPE_MESSAGE: NAMED_PIPE_MODE = 4u32;
pub const PIPE_WAIT: NAMED_PIPE_MODE = 0u32;
+pub type PRIORITY_HINT = i32;
pub type PROCESSOR_ARCHITECTURE = u16;
pub type PROCESS_CREATION_FLAGS = u32;
#[repr(C)]
@@ -3774,6 +3818,13 @@ pub const PROFILE_SERVER: PROCESS_CREATION_FLAGS = 1073741824u32;
pub const PROFILE_USER: PROCESS_CREATION_FLAGS = 268435456u32;
pub const PROGRESS_CONTINUE: u32 = 0u32;
pub type PSTR = *mut u8;
+pub type PTIMERAPCROUTINE = ::core::option::Option<
+ unsafe extern "system" fn(
+ lpargtocompletionroutine: *const ::core::ffi::c_void,
+ dwtimerlowvalue: u32,
+ dwtimerhighvalue: u32,
+ ) -> (),
+>;
pub type PWSTR = *mut u16;
pub const READ_CONTROL: FILE_ACCESS_RIGHTS = 131072u32;
pub const REALTIME_PRIORITY_CLASS: PROCESS_CREATION_FLAGS = 256u32;
@@ -3813,6 +3864,17 @@ impl ::core::clone::Clone for SOCKADDR {
*self
}
}
+#[repr(C)]
+pub struct SOCKADDR_UN {
+ pub sun_family: ADDRESS_FAMILY,
+ pub sun_path: [u8; 108],
+}
+impl ::core::marker::Copy for SOCKADDR_UN {}
+impl ::core::clone::Clone for SOCKADDR_UN {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
pub type SOCKET = usize;
pub const SOCKET_ERROR: i32 = -1i32;
pub const SOCK_DGRAM: WINSOCK_SOCKET_TYPE = 2i32;
@@ -3910,6 +3972,7 @@ pub type SYMBOLIC_LINK_FLAGS = u32;
pub const SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE: SYMBOLIC_LINK_FLAGS = 2u32;
pub const SYMBOLIC_LINK_FLAG_DIRECTORY: SYMBOLIC_LINK_FLAGS = 1u32;
pub const SYMLINK_FLAG_RELATIVE: u32 = 1u32;
+pub type SYNCHRONIZATION_ACCESS_RIGHTS = u32;
pub const SYNCHRONIZE: FILE_ACCESS_RIGHTS = 1048576u32;
#[repr(C)]
pub struct SYSTEM_INFO {
@@ -3956,6 +4019,8 @@ pub const TCP_NODELAY: i32 = 1i32;
pub const THREAD_CREATE_RUN_IMMEDIATELY: THREAD_CREATION_FLAGS = 0u32;
pub const THREAD_CREATE_SUSPENDED: THREAD_CREATION_FLAGS = 4u32;
pub type THREAD_CREATION_FLAGS = u32;
+pub const TIMER_ALL_ACCESS: SYNCHRONIZATION_ACCESS_RIGHTS = 2031619u32;
+pub const TIMER_MODIFY_STATE: SYNCHRONIZATION_ACCESS_RIGHTS = 2u32;
#[repr(C)]
pub struct TIMEVAL {
pub tv_sec: i32,
diff --git a/library/std/src/sys/windows/cmath.rs b/library/std/src/sys/windows/cmath.rs
index 1b2a86f3c..36578d5a3 100644
--- a/library/std/src/sys/windows/cmath.rs
+++ b/library/std/src/sys/windows/cmath.rs
@@ -1,6 +1,6 @@
#![cfg(not(test))]
-use libc::{c_double, c_float, c_int};
+use core::ffi::{c_double, c_float, c_int};
extern "C" {
pub fn acos(n: c_double) -> c_double;
@@ -33,7 +33,7 @@ pub use self::shims::*;
#[cfg(not(all(target_env = "msvc", target_arch = "x86")))]
mod shims {
- use libc::c_float;
+ use core::ffi::c_float;
extern "C" {
pub fn acosf(n: c_float) -> c_float;
@@ -52,7 +52,7 @@ mod shims {
// back to f32. While not precisely correct should be "correct enough" for now.
#[cfg(all(target_env = "msvc", target_arch = "x86"))]
mod shims {
- use libc::c_float;
+ use core::ffi::c_float;
#[inline]
pub unsafe fn acosf(n: c_float) -> c_float {
diff --git a/library/std/src/sys/windows/fs.rs b/library/std/src/sys/windows/fs.rs
index 21a65bc25..d7e36b9a3 100644
--- a/library/std/src/sys/windows/fs.rs
+++ b/library/std/src/sys/windows/fs.rs
@@ -16,8 +16,10 @@ use crate::sys::{c, cvt, Align8};
use crate::sys_common::{AsInner, FromInner, IntoInner};
use crate::thread;
+use core::ffi::c_void;
+
use super::path::maybe_verbatim;
-use super::to_u16s;
+use super::{api, to_u16s, IoResult};
pub struct File {
handle: Handle,
@@ -121,7 +123,7 @@ impl Iterator for ReadDir {
let mut wfd = mem::zeroed();
loop {
if c::FindNextFileW(self.handle.0, &mut wfd) == 0 {
- if c::GetLastError() == c::ERROR_NO_MORE_FILES {
+ if api::get_last_error().code == c::ERROR_NO_MORE_FILES {
return None;
} else {
return Some(Err(Error::last_os_error()));
@@ -316,17 +318,8 @@ impl File {
}
pub fn truncate(&self, size: u64) -> io::Result<()> {
- let mut info = c::FILE_END_OF_FILE_INFO { EndOfFile: size as c::LARGE_INTEGER };
- let size = mem::size_of_val(&info);
- cvt(unsafe {
- c::SetFileInformationByHandle(
- self.handle.as_raw_handle(),
- c::FileEndOfFileInfo,
- &mut info as *mut _ as *mut _,
- size as c::DWORD,
- )
- })?;
- Ok(())
+ let info = c::FILE_END_OF_FILE_INFO { EndOfFile: size as i64 };
+ api::set_file_information_by_handle(self.handle.as_raw_handle(), &info).io_result()
}
#[cfg(not(target_vendor = "uwp"))]
@@ -371,7 +364,7 @@ impl File {
cvt(c::GetFileInformationByHandleEx(
self.handle.as_raw_handle(),
c::FileBasicInfo,
- &mut info as *mut _ as *mut libc::c_void,
+ &mut info as *mut _ as *mut c_void,
size as c::DWORD,
))?;
let mut attr = FileAttr {
@@ -399,7 +392,7 @@ impl File {
cvt(c::GetFileInformationByHandleEx(
self.handle.as_raw_handle(),
c::FileStandardInfo,
- &mut info as *mut _ as *mut libc::c_void,
+ &mut info as *mut _ as *mut c_void,
size as c::DWORD,
))?;
attr.file_size = info.AllocationSize as u64;
@@ -563,23 +556,14 @@ impl File {
}
pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> {
- let mut info = c::FILE_BASIC_INFO {
+ let info = c::FILE_BASIC_INFO {
CreationTime: 0,
LastAccessTime: 0,
LastWriteTime: 0,
ChangeTime: 0,
FileAttributes: perm.attrs,
};
- let size = mem::size_of_val(&info);
- cvt(unsafe {
- c::SetFileInformationByHandle(
- self.handle.as_raw_handle(),
- c::FileBasicInfo,
- &mut info as *mut _ as *mut _,
- size as c::DWORD,
- )
- })?;
- Ok(())
+ api::set_file_information_by_handle(self.handle.as_raw_handle(), &info).io_result()
}
pub fn set_times(&self, times: FileTimes) -> io::Result<()> {
@@ -624,7 +608,7 @@ impl File {
cvt(c::GetFileInformationByHandleEx(
self.handle.as_raw_handle(),
c::FileBasicInfo,
- &mut info as *mut _ as *mut libc::c_void,
+ &mut info as *mut _ as *mut c_void,
size as c::DWORD,
))?;
Ok(info)
@@ -639,38 +623,20 @@ impl File {
/// If the operation is not supported for this filesystem or OS version
/// then errors will be `ERROR_NOT_SUPPORTED` or `ERROR_INVALID_PARAMETER`.
fn posix_delete(&self) -> io::Result<()> {
- let mut info = c::FILE_DISPOSITION_INFO_EX {
+ let info = c::FILE_DISPOSITION_INFO_EX {
Flags: c::FILE_DISPOSITION_FLAG_DELETE
| c::FILE_DISPOSITION_FLAG_POSIX_SEMANTICS
| c::FILE_DISPOSITION_FLAG_IGNORE_READONLY_ATTRIBUTE,
};
- let size = mem::size_of_val(&info);
- cvt(unsafe {
- c::SetFileInformationByHandle(
- self.handle.as_raw_handle(),
- c::FileDispositionInfoEx,
- &mut info as *mut _ as *mut _,
- size as c::DWORD,
- )
- })?;
- Ok(())
+ api::set_file_information_by_handle(self.handle.as_raw_handle(), &info).io_result()
}
/// Delete a file using win32 semantics. The file won't actually be deleted
/// until all file handles are closed. However, marking a file for deletion
/// will prevent anyone from opening a new handle to the file.
fn win32_delete(&self) -> io::Result<()> {
- let mut info = c::FILE_DISPOSITION_INFO { DeleteFile: c::TRUE as _ };
- let size = mem::size_of_val(&info);
- cvt(unsafe {
- c::SetFileInformationByHandle(
- self.handle.as_raw_handle(),
- c::FileDispositionInfo,
- &mut info as *mut _ as *mut _,
- size as c::DWORD,
- )
- })?;
- Ok(())
+ let info = c::FILE_DISPOSITION_INFO { DeleteFile: c::TRUE as _ };
+ api::set_file_information_by_handle(self.handle.as_raw_handle(), &info).io_result()
}
/// Fill the given buffer with as many directory entries as will fit.
@@ -1064,6 +1030,14 @@ impl DirBuilder {
}
pub fn readdir(p: &Path) -> io::Result<ReadDir> {
+ // We push a `*` to the end of the path which cause the empty path to be
+ // treated as the current directory. So, for consistency with other platforms,
+ // we explicitly error on the empty path.
+ if p.as_os_str().is_empty() {
+ // Return an error code consistent with other ways of opening files.
+ // E.g. fs::metadata or File::open.
+ return Err(io::Error::from_raw_os_error(c::ERROR_PATH_NOT_FOUND as i32));
+ }
let root = p.to_path_buf();
let star = p.join("*");
let path = maybe_verbatim(&star)?;
@@ -1513,6 +1487,13 @@ pub fn try_exists(path: &Path) -> io::Result<bool> {
// as the file existing.
_ if e.raw_os_error() == Some(c::ERROR_SHARING_VIOLATION as i32) => Ok(true),
+ // `ERROR_CANT_ACCESS_FILE` means that a file exists but that the
+ // reparse point could not be handled by `CreateFile`.
+ // This can happen for special files such as:
+ // * Unix domain sockets which you need to `connect` to
+ // * App exec links which require using `CreateProcess`
+ _ if e.raw_os_error() == Some(c::ERROR_CANT_ACCESS_FILE as i32) => Ok(true),
+
// Other errors such as `ERROR_ACCESS_DENIED` may indicate that the
// file exists. However, these types of errors are usually more
// permanent so we report them here.
diff --git a/library/std/src/sys/windows/io.rs b/library/std/src/sys/windows/io.rs
index fc9856cae..9b540ee07 100644
--- a/library/std/src/sys/windows/io.rs
+++ b/library/std/src/sys/windows/io.rs
@@ -3,7 +3,7 @@ use crate::mem::size_of;
use crate::os::windows::io::{AsHandle, AsRawHandle, BorrowedHandle};
use crate::slice;
use crate::sys::c;
-use libc;
+use core::ffi::c_void;
#[derive(Copy, Clone)]
#[repr(transparent)]
@@ -136,7 +136,7 @@ unsafe fn msys_tty_on(handle: c::HANDLE) -> bool {
let res = c::GetFileInformationByHandleEx(
handle,
c::FileNameInfo,
- &mut name_info as *mut _ as *mut libc::c_void,
+ &mut name_info as *mut _ as *mut c_void,
size_of::<FILE_NAME_INFO>() as u32,
);
if res == 0 {
diff --git a/library/std/src/sys/windows/mod.rs b/library/std/src/sys/windows/mod.rs
index b609ad247..c4e56e13b 100644
--- a/library/std/src/sys/windows/mod.rs
+++ b/library/std/src/sys/windows/mod.rs
@@ -44,6 +44,18 @@ cfg_if::cfg_if! {
}
}
+mod api;
+
+/// Map a Result<T, WinError> to io::Result<T>.
+trait IoResult<T> {
+ fn io_result(self) -> crate::io::Result<T>;
+}
+impl<T> IoResult<T> for Result<T, api::WinError> {
+ fn io_result(self) -> crate::io::Result<T> {
+ self.map_err(|e| crate::io::Error::from_raw_os_error(e.code as i32))
+ }
+}
+
// 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) {
@@ -241,11 +253,11 @@ where
// not an actual error.
c::SetLastError(0);
let k = match f1(buf.as_mut_ptr().cast::<u16>(), n as c::DWORD) {
- 0 if c::GetLastError() == 0 => 0,
+ 0 if api::get_last_error().code == 0 => 0,
0 => return Err(crate::io::Error::last_os_error()),
n => n,
} as usize;
- if k == n && c::GetLastError() == c::ERROR_INSUFFICIENT_BUFFER {
+ if k == n && api::get_last_error().code == c::ERROR_INSUFFICIENT_BUFFER {
n = n.saturating_mul(2).min(c::DWORD::MAX as usize);
} else if k > n {
n = k;
diff --git a/library/std/src/sys/windows/net.rs b/library/std/src/sys/windows/net.rs
index abdcab424..c29b86366 100644
--- a/library/std/src/sys/windows/net.rs
+++ b/library/std/src/sys/windows/net.rs
@@ -15,7 +15,7 @@ use crate::sys_common::net;
use crate::sys_common::{AsInner, FromInner, IntoInner};
use crate::time::Duration;
-use libc::{c_int, c_long, c_ulong, c_ushort};
+use core::ffi::{c_int, c_long, c_ulong, c_ushort};
pub type wrlen_t = i32;
@@ -140,13 +140,15 @@ impl Socket {
}
}
+ pub fn connect(&self, addr: &SocketAddr) -> io::Result<()> {
+ let (addr, len) = addr.into_inner();
+ let result = unsafe { c::connect(self.as_raw(), addr.as_ptr(), len) };
+ cvt(result).map(drop)
+ }
+
pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> {
self.set_nonblocking(true)?;
- let result = {
- let (addr, len) = addr.into_inner();
- let result = unsafe { c::connect(self.as_raw(), addr.as_ptr(), len) };
- cvt(result).map(drop)
- };
+ let result = self.connect(addr);
self.set_nonblocking(false)?;
match result {
diff --git a/library/std/src/sys/windows/os.rs b/library/std/src/sys/windows/os.rs
index 58afca088..8cc905101 100644
--- a/library/std/src/sys/windows/os.rs
+++ b/library/std/src/sys/windows/os.rs
@@ -17,10 +17,10 @@ use crate::ptr;
use crate::slice;
use crate::sys::{c, cvt};
-use super::to_u16s;
+use super::{api, to_u16s};
pub fn errno() -> i32 {
- unsafe { c::GetLastError() as i32 }
+ api::get_last_error().code as i32
}
/// Gets a detailed string description for the given error number.
@@ -336,7 +336,7 @@ fn home_dir_crt() -> Option<PathBuf> {
super::fill_utf16_buf(
|buf, mut sz| {
match c::GetUserProfileDirectoryW(token, buf, &mut sz) {
- 0 if c::GetLastError() != c::ERROR_INSUFFICIENT_BUFFER => 0,
+ 0 if api::get_last_error().code != c::ERROR_INSUFFICIENT_BUFFER => 0,
0 => sz,
_ => sz - 1, // sz includes the null terminator
}
diff --git a/library/std/src/sys/windows/process.rs b/library/std/src/sys/windows/process.rs
index cd5bf7f15..f4078d359 100644
--- a/library/std/src/sys/windows/process.rs
+++ b/library/std/src/sys/windows/process.rs
@@ -19,8 +19,7 @@ use crate::path::{Path, PathBuf};
use crate::ptr;
use crate::sync::Mutex;
use crate::sys::args::{self, Arg};
-use crate::sys::c;
-use crate::sys::c::NonZeroDWORD;
+use crate::sys::c::{self, NonZeroDWORD, EXIT_FAILURE, EXIT_SUCCESS};
use crate::sys::cvt;
use crate::sys::fs::{File, OpenOptions};
use crate::sys::handle::Handle;
@@ -30,7 +29,7 @@ use crate::sys::stdio;
use crate::sys_common::process::{CommandEnv, CommandEnvs};
use crate::sys_common::IntoInner;
-use libc::{c_void, EXIT_FAILURE, EXIT_SUCCESS};
+use core::ffi::c_void;
////////////////////////////////////////////////////////////////////////////////
// Command
diff --git a/library/std/src/sys/windows/stack_overflow.rs b/library/std/src/sys/windows/stack_overflow.rs
index 0caf0a317..627763da8 100644
--- a/library/std/src/sys/windows/stack_overflow.rs
+++ b/library/std/src/sys/windows/stack_overflow.rs
@@ -3,6 +3,8 @@
use crate::sys::c;
use crate::thread;
+use super::api;
+
pub struct Handler;
impl Handler {
@@ -10,7 +12,7 @@ impl Handler {
// This API isn't available on XP, so don't panic in that case and just
// pray it works out ok.
if c::SetThreadStackGuarantee(&mut 0x5000) == 0
- && c::GetLastError() as u32 != c::ERROR_CALL_NOT_IMPLEMENTED as u32
+ && api::get_last_error().code != c::ERROR_CALL_NOT_IMPLEMENTED
{
panic!("failed to reserve stack space for exception handling");
}
diff --git a/library/std/src/sys/windows/stdio.rs b/library/std/src/sys/windows/stdio.rs
index 3fcaaa508..a9ff909aa 100644
--- a/library/std/src/sys/windows/stdio.rs
+++ b/library/std/src/sys/windows/stdio.rs
@@ -9,6 +9,7 @@ use crate::str;
use crate::sys::c;
use crate::sys::cvt;
use crate::sys::handle::Handle;
+use crate::sys::windows::api;
use core::str::utf8_char_width;
#[cfg(test)]
@@ -369,7 +370,7 @@ fn read_u16s(handle: c::HANDLE, buf: &mut [MaybeUninit<u16>]) -> io::Result<usiz
// ReadConsoleW returns success with ERROR_OPERATION_ABORTED for Ctrl-C or Ctrl-Break.
// Explicitly check for that case here and try again.
- if amount == 0 && unsafe { c::GetLastError() } == c::ERROR_OPERATION_ABORTED {
+ if amount == 0 && api::get_last_error().code == c::ERROR_OPERATION_ABORTED {
continue;
}
break;
diff --git a/library/std/src/sys/windows/thread.rs b/library/std/src/sys/windows/thread.rs
index 18cecb656..1fe744935 100644
--- a/library/std/src/sys/windows/thread.rs
+++ b/library/std/src/sys/windows/thread.rs
@@ -10,8 +10,9 @@ use crate::sys::stack_overflow;
use crate::sys_common::FromInner;
use crate::time::Duration;
-use libc::c_void;
+use core::ffi::c_void;
+use super::time::WaitableTimer;
use super::to_u16s;
pub const DEFAULT_MIN_STACK_SIZE: usize = 2 * 1024 * 1024;
@@ -87,7 +88,17 @@ impl Thread {
}
pub fn sleep(dur: Duration) {
- unsafe { c::Sleep(super::dur2timeout(dur)) }
+ fn high_precision_sleep(dur: Duration) -> Result<(), ()> {
+ let timer = WaitableTimer::high_resolution()?;
+ timer.set(dur)?;
+ timer.wait()
+ }
+ // Attempt to use high-precision sleep (Windows 10, version 1803+).
+ // On error fallback to the standard `Sleep` function.
+ // Also preserves the zero duration behaviour of `Sleep`.
+ if dur.is_zero() || high_precision_sleep(dur).is_err() {
+ unsafe { c::Sleep(super::dur2timeout(dur)) }
+ }
}
pub fn handle(&self) -> &Handle {
diff --git a/library/std/src/sys/windows/thread_local_key.rs b/library/std/src/sys/windows/thread_local_key.rs
index 036d96596..5eee4a966 100644
--- a/library/std/src/sys/windows/thread_local_key.rs
+++ b/library/std/src/sys/windows/thread_local_key.rs
@@ -16,14 +16,19 @@ static HAS_DTORS: AtomicBool = AtomicBool::new(false);
// Using a per-thread list avoids the problems in synchronizing global state.
#[thread_local]
#[cfg(target_thread_local)]
-static mut DESTRUCTORS: Vec<(*mut u8, unsafe extern "C" fn(*mut u8))> = Vec::new();
+static DESTRUCTORS: crate::cell::RefCell<Vec<(*mut u8, unsafe extern "C" fn(*mut u8))>> =
+ crate::cell::RefCell::new(Vec::new());
// Ensure this can never be inlined because otherwise this may break in dylibs.
// See #44391.
#[inline(never)]
#[cfg(target_thread_local)]
pub unsafe fn register_keyless_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
- DESTRUCTORS.push((t, dtor));
+ match DESTRUCTORS.try_borrow_mut() {
+ Ok(mut dtors) => dtors.push((t, dtor)),
+ Err(_) => rtabort!("global allocator may not use TLS"),
+ }
+
HAS_DTORS.store(true, Relaxed);
}
@@ -37,11 +42,17 @@ unsafe fn run_keyless_dtors() {
// the case that this loop always terminates because we provide the
// guarantee that a TLS key cannot be set after it is flagged for
// destruction.
- while let Some((ptr, dtor)) = DESTRUCTORS.pop() {
+ loop {
+ // Use a let-else binding to ensure the `RefCell` guard is dropped
+ // immediately. Otherwise, a panic would occur if a TLS destructor
+ // tries to access the list.
+ let Some((ptr, dtor)) = DESTRUCTORS.borrow_mut().pop() else {
+ break;
+ };
(dtor)(ptr);
}
// We're done so free the memory.
- DESTRUCTORS = Vec::new();
+ DESTRUCTORS.replace(Vec::new());
}
type Key = c::DWORD;
diff --git a/library/std/src/sys/windows/time.rs b/library/std/src/sys/windows/time.rs
index b8209a854..bece48e79 100644
--- a/library/std/src/sys/windows/time.rs
+++ b/library/std/src/sys/windows/time.rs
@@ -1,11 +1,13 @@
use crate::cmp::Ordering;
use crate::fmt;
use crate::mem;
+use crate::ptr::{null, null_mut};
use crate::sys::c;
use crate::sys_common::IntoInner;
use crate::time::Duration;
use core::hash::{Hash, Hasher};
+use core::ops::Neg;
const NANOS_PER_SEC: u64 = 1_000_000_000;
const INTERVALS_PER_SEC: u64 = NANOS_PER_SEC / 100;
@@ -222,3 +224,39 @@ mod perf_counter {
qpc_value
}
}
+
+/// A timer you can wait on.
+pub(super) struct WaitableTimer {
+ handle: c::HANDLE,
+}
+impl WaitableTimer {
+ /// Create a high-resolution timer. Will fail before Windows 10, version 1803.
+ pub fn high_resolution() -> Result<Self, ()> {
+ let handle = unsafe {
+ c::CreateWaitableTimerExW(
+ null(),
+ null(),
+ c::CREATE_WAITABLE_TIMER_HIGH_RESOLUTION,
+ c::TIMER_ALL_ACCESS,
+ )
+ };
+ if handle != null_mut() { Ok(Self { handle }) } else { Err(()) }
+ }
+ pub fn set(&self, duration: Duration) -> Result<(), ()> {
+ // Convert the Duration to a format similar to FILETIME.
+ // Negative values are relative times whereas positive values are absolute.
+ // Therefore we negate the relative duration.
+ let time = checked_dur2intervals(&duration).ok_or(())?.neg();
+ let result = unsafe { c::SetWaitableTimer(self.handle, &time, 0, None, null(), c::FALSE) };
+ if result != 0 { Ok(()) } else { Err(()) }
+ }
+ pub fn wait(&self) -> Result<(), ()> {
+ let result = unsafe { c::WaitForSingleObject(self.handle, c::INFINITE) };
+ if result != c::WAIT_FAILED { Ok(()) } else { Err(()) }
+ }
+}
+impl Drop for WaitableTimer {
+ fn drop(&mut self) {
+ unsafe { c::CloseHandle(self.handle) };
+ }
+}
diff --git a/library/std/src/sys_common/net.rs b/library/std/src/sys_common/net.rs
index 4f5b17dea..8712bd2ec 100644
--- a/library/std/src/sys_common/net.rs
+++ b/library/std/src/sys_common/net.rs
@@ -226,9 +226,7 @@ impl TcpStream {
init();
let sock = Socket::new(addr, c::SOCK_STREAM)?;
-
- let (addr, len) = addr.into_inner();
- cvt_r(|| unsafe { c::connect(sock.as_raw(), addr.as_ptr(), len) })?;
+ sock.connect(addr)?;
Ok(TcpStream { inner: sock })
}
diff --git a/library/std/src/sys_common/thread_local_dtor.rs b/library/std/src/sys_common/thread_local_dtor.rs
index 844946eda..98382fc6a 100644
--- a/library/std/src/sys_common/thread_local_dtor.rs
+++ b/library/std/src/sys_common/thread_local_dtor.rs
@@ -13,6 +13,7 @@
#![unstable(feature = "thread_local_internals", issue = "none")]
#![allow(dead_code)]
+use crate::cell::RefCell;
use crate::ptr;
use crate::sys_common::thread_local_key::StaticKey;
@@ -28,17 +29,23 @@ pub unsafe fn register_dtor_fallback(t: *mut u8, dtor: unsafe extern "C" fn(*mut
// flagged for destruction.
static DTORS: StaticKey = StaticKey::new(Some(run_dtors));
- type List = Vec<(*mut u8, unsafe extern "C" fn(*mut u8))>;
+ // FIXME(joboet): integrate RefCell into pointer to avoid infinite recursion
+ // when the global allocator tries to register a destructor and just panic
+ // instead.
+ type List = RefCell<Vec<(*mut u8, unsafe extern "C" fn(*mut u8))>>;
if DTORS.get().is_null() {
- let v: Box<List> = Box::new(Vec::new());
+ let v: Box<List> = Box::new(RefCell::new(Vec::new()));
DTORS.set(Box::into_raw(v) as *mut u8);
}
- let list: &mut List = &mut *(DTORS.get() as *mut List);
- list.push((t, dtor));
+ let list = &*(DTORS.get() as *const List);
+ match list.try_borrow_mut() {
+ Ok(mut dtors) => dtors.push((t, dtor)),
+ Err(_) => rtabort!("global allocator may not use TLS"),
+ }
unsafe extern "C" fn run_dtors(mut ptr: *mut u8) {
while !ptr.is_null() {
- let list: Box<List> = Box::from_raw(ptr as *mut List);
+ let list = Box::from_raw(ptr as *mut List).into_inner();
for (ptr, dtor) in list.into_iter() {
dtor(ptr);
}
diff --git a/library/std/src/thread/local.rs b/library/std/src/thread/local.rs
index 09994e47f..def94acd4 100644
--- a/library/std/src/thread/local.rs
+++ b/library/std/src/thread/local.rs
@@ -29,9 +29,9 @@ use crate::fmt;
/// within a thread, and values that implement [`Drop`] get destructed when a
/// thread exits. Some caveats apply, which are explained below.
///
-/// A `LocalKey`'s initializer cannot recursively depend on itself, and using
-/// a `LocalKey` in this way will cause the initializer to infinitely recurse
-/// on the first call to `with`.
+/// A `LocalKey`'s initializer cannot recursively depend on itself. Using a
+/// `LocalKey` in this way may cause panics, aborts or infinite recursion on
+/// the first call to `with`.
///
/// # Examples
///
diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs
index 7b26068c2..4097eb554 100644
--- a/library/std/src/thread/mod.rs
+++ b/library/std/src/thread/mod.rs
@@ -545,6 +545,15 @@ impl Builder {
scope_data.increment_num_running_threads();
}
+ let main = Box::new(main);
+ // SAFETY: dynamic size and alignment of the Box remain the same. See below for why the
+ // lifetime change is justified.
+ #[cfg(bootstrap)]
+ let main =
+ unsafe { mem::transmute::<Box<dyn FnOnce() + 'a>, Box<dyn FnOnce() + 'static>>(main) };
+ #[cfg(not(bootstrap))]
+ let main = unsafe { Box::from_raw(Box::into_raw(main) as *mut (dyn FnOnce() + 'static)) };
+
Ok(JoinInner {
// SAFETY:
//
@@ -559,14 +568,7 @@ impl Builder {
// Similarly, the `sys` implementation must guarantee that no references to the closure
// exist after the thread has terminated, which is signaled by `Thread::join`
// returning.
- native: unsafe {
- imp::Thread::new(
- stack_size,
- mem::transmute::<Box<dyn FnOnce() + 'a>, Box<dyn FnOnce() + 'static>>(
- Box::new(main),
- ),
- )?
- },
+ native: unsafe { imp::Thread::new(stack_size, main)? },
thread: my_thread,
packet: my_packet,
})
diff --git a/library/std/src/time.rs b/library/std/src/time.rs
index 005d8c767..91c010ef2 100644
--- a/library/std/src/time.rs
+++ b/library/std/src/time.rs
@@ -111,7 +111,7 @@ pub use core::time::TryFromFloatSecsError;
/// |-----------|----------------------------------------------------------------------|
/// | SGX | [`insecure_time` usercall]. More information on [timekeeping in SGX] |
/// | UNIX | [clock_gettime (Monotonic Clock)] |
-/// | Darwin | [mach_absolute_time] |
+/// | Darwin | [clock_gettime (Monotonic Clock)] |
/// | VXWorks | [clock_gettime (Monotonic Clock)] |
/// | SOLID | `get_tim` |
/// | WASI | [__wasi_clock_time_get (Monotonic Clock)] |
@@ -123,7 +123,6 @@ pub use core::time::TryFromFloatSecsError;
/// [timekeeping in SGX]: https://edp.fortanix.com/docs/concepts/rust-std/#codestdtimecode
/// [__wasi_clock_time_get (Monotonic Clock)]: https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/docs.md#clock_time_get
/// [clock_gettime (Monotonic Clock)]: https://linux.die.net/man/3/clock_gettime
-/// [mach_absolute_time]: https://developer.apple.com/library/archive/documentation/Darwin/Conceptual/KernelProgramming/services/services.html
///
/// **Disclaimer:** These system calls might change over time.
///
@@ -153,6 +152,7 @@ pub use core::time::TryFromFloatSecsError;
///
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[stable(feature = "time2", since = "1.8.0")]
+#[cfg_attr(not(test), rustc_diagnostic_item = "Instant")]
pub struct Instant(time::Instant);
/// A measurement of the system clock, useful for talking to
@@ -223,7 +223,7 @@ pub struct Instant(time::Instant);
/// |-----------|----------------------------------------------------------------------|
/// | SGX | [`insecure_time` usercall]. More information on [timekeeping in SGX] |
/// | UNIX | [clock_gettime (Realtime Clock)] |
-/// | Darwin | [gettimeofday] |
+/// | Darwin | [clock_gettime (Realtime Clock)] |
/// | VXWorks | [clock_gettime (Realtime Clock)] |
/// | SOLID | `SOLID_RTC_ReadTime` |
/// | WASI | [__wasi_clock_time_get (Realtime Clock)] |
@@ -232,7 +232,6 @@ pub struct Instant(time::Instant);
/// [currently]: crate::io#platform-specific-behavior
/// [`insecure_time` usercall]: https://edp.fortanix.com/docs/api/fortanix_sgx_abi/struct.Usercalls.html#method.insecure_time
/// [timekeeping in SGX]: https://edp.fortanix.com/docs/concepts/rust-std/#codestdtimecode
-/// [gettimeofday]: https://man7.org/linux/man-pages/man2/gettimeofday.2.html
/// [clock_gettime (Realtime Clock)]: https://linux.die.net/man/3/clock_gettime
/// [__wasi_clock_time_get (Realtime Clock)]: https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/docs.md#clock_time_get
/// [GetSystemTimePreciseAsFileTime]: https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemtimepreciseasfiletime
diff --git a/library/std/tests/switch-stdout.rs b/library/std/tests/switch-stdout.rs
index 2605664d2..27f3e8a9b 100644
--- a/library/std/tests/switch-stdout.rs
+++ b/library/std/tests/switch-stdout.rs
@@ -5,32 +5,48 @@ use std::io::{Read, Write};
mod common;
+#[cfg(windows)]
+use std::os::windows::io::OwnedHandle;
+
#[cfg(unix)]
-fn switch_stdout_to(file: File) {
+use std::os::fd::OwnedFd;
+
+#[cfg(unix)]
+fn switch_stdout_to(file: OwnedFd) -> OwnedFd {
use std::os::unix::prelude::*;
extern "C" {
+ fn dup(old: i32) -> i32;
fn dup2(old: i32, new: i32) -> i32;
}
unsafe {
+ let orig_fd = dup(1);
+ assert_ne!(orig_fd, -1);
+ let res = OwnedFd::from_raw_fd(orig_fd);
assert_eq!(dup2(file.as_raw_fd(), 1), 1);
+ res
}
}
#[cfg(windows)]
-fn switch_stdout_to(file: File) {
+fn switch_stdout_to(file: OwnedHandle) -> OwnedHandle {
use std::os::windows::prelude::*;
extern "system" {
+ fn GetStdHandle(nStdHandle: u32) -> *mut u8;
fn SetStdHandle(nStdHandle: u32, handle: *mut u8) -> i32;
}
const STD_OUTPUT_HANDLE: u32 = (-11i32) as u32;
+ const INVALID_HANDLE_VALUE: *mut u8 = !0 as *mut u8;
unsafe {
+ let orig_hdl = GetStdHandle(STD_OUTPUT_HANDLE);
+ assert!(!orig_hdl.is_null() && orig_hdl != INVALID_HANDLE_VALUE);
let rc = SetStdHandle(STD_OUTPUT_HANDLE, file.into_raw_handle() as *mut _);
assert!(rc != 0);
+ OwnedHandle::from_raw_handle(orig_hdl as _)
}
}
@@ -43,10 +59,12 @@ fn switch_stdout() {
let mut stdout = std::io::stdout();
stdout.write(b"foo\n").unwrap();
stdout.flush().unwrap();
- switch_stdout_to(f);
+ let orig_hdl = switch_stdout_to(f.into());
stdout.write(b"bar\n").unwrap();
stdout.flush().unwrap();
+ switch_stdout_to(orig_hdl);
+
let mut contents = String::new();
File::open(&path).unwrap().read_to_string(&mut contents).unwrap();
assert_eq!(contents, "bar\n");