about summary refs log tree commit diff
path: root/library/std/src/sys
diff options
context:
space:
mode:
Diffstat (limited to 'library/std/src/sys')
-rw-r--r--library/std/src/sys/alloc/unix.rs1
-rw-r--r--library/std/src/sys/dbg.rs229
-rw-r--r--library/std/src/sys/mod.rs1
-rw-r--r--library/std/src/sys/os_str/bytes.rs3
-rw-r--r--library/std/src/sys/os_str/wtf8.rs3
-rw-r--r--library/std/src/sys/pal/hermit/mod.rs6
-rw-r--r--library/std/src/sys/pal/hermit/net.rs4
-rw-r--r--library/std/src/sys/pal/hermit/thread.rs1
-rw-r--r--library/std/src/sys/pal/hermit/time.rs6
-rw-r--r--library/std/src/sys/pal/unix/args.rs1
-rw-r--r--library/std/src/sys/pal/unix/env.rs11
-rw-r--r--library/std/src/sys/pal/unix/fd.rs42
-rw-r--r--library/std/src/sys/pal/unix/fs.rs27
-rw-r--r--library/std/src/sys/pal/unix/mod.rs3
-rw-r--r--library/std/src/sys/pal/unix/net.rs8
-rw-r--r--library/std/src/sys/pal/unix/os.rs6
-rw-r--r--library/std/src/sys/pal/unix/process/mod.rs6
-rw-r--r--library/std/src/sys/pal/unix/process/process_fuchsia.rs4
-rw-r--r--library/std/src/sys/pal/unix/process/process_unix.rs12
-rw-r--r--library/std/src/sys/pal/unix/stack_overflow.rs4
-rw-r--r--library/std/src/sys/pal/unix/thread.rs28
-rw-r--r--library/std/src/sys/pal/wasip2/mod.rs1
-rw-r--r--library/std/src/sys/pal/wasip2/net.rs379
-rw-r--r--library/std/src/sys/pal/windows/api.rs3
-rw-r--r--library/std/src/sys/pal/windows/fs.rs36
-rw-r--r--library/std/src/sys/pal/windows/futex.rs2
-rw-r--r--library/std/src/sys/pal/windows/io.rs2
-rw-r--r--library/std/src/sys/pal/windows/net.rs2
-rw-r--r--library/std/src/sys/pal/windows/pipe.rs2
-rw-r--r--library/std/src/sys/pal/windows/process.rs6
-rw-r--r--library/std/src/sys/personality/mod.rs2
-rw-r--r--library/std/src/sys/random/mod.rs1
-rw-r--r--library/std/src/sys/sync/condvar/mod.rs8
-rw-r--r--library/std/src/sys/sync/condvar/pthread.rs38
-rw-r--r--library/std/src/sys/sync/condvar/sgx.rs29
-rw-r--r--library/std/src/sys/sync/condvar/teeos.rs101
-rw-r--r--library/std/src/sys/sync/mod.rs3
-rw-r--r--library/std/src/sys/sync/mutex/mod.rs2
-rw-r--r--library/std/src/sys/sync/mutex/pthread.rs81
-rw-r--r--library/std/src/sys/sync/mutex/sgx.rs28
-rw-r--r--library/std/src/sys/sync/once_box.rs82
-rw-r--r--library/std/src/sys/sync/rwlock/queue.rs6
-rw-r--r--library/std/src/sys/sync/rwlock/teeos.rs8
-rw-r--r--library/std/src/sys/sync/thread_parking/pthread.rs11
-rw-r--r--library/std/src/sys/sync/thread_parking/windows7.rs2
-rw-r--r--library/std/src/sys/thread_local/destructors/linux_like.rs2
-rw-r--r--library/std/src/sys/thread_local/guard/apple.rs1
-rw-r--r--library/std/src/sys/thread_local/guard/key.rs39
-rw-r--r--library/std/src/sys/thread_local/guard/solid.rs5
-rw-r--r--library/std/src/sys/thread_local/guard/windows.rs8
-rw-r--r--library/std/src/sys/thread_local/key/xous.rs2
-rw-r--r--library/std/src/sys/thread_local/mod.rs48
-rw-r--r--library/std/src/sys/thread_local/native/mod.rs31
-rw-r--r--library/std/src/sys/thread_local/os.rs34
-rw-r--r--library/std/src/sys/thread_local/statik.rs33
55 files changed, 899 insertions, 545 deletions
diff --git a/library/std/src/sys/alloc/unix.rs b/library/std/src/sys/alloc/unix.rs
index 266b69cdc1e..1af9d766290 100644
--- a/library/std/src/sys/alloc/unix.rs
+++ b/library/std/src/sys/alloc/unix.rs
@@ -71,6 +71,7 @@ cfg_if::cfg_if! {
         }
     } else {
         #[inline]
+        #[cfg_attr(target_os = "vxworks", allow(unused_unsafe))]
         unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 {
             let mut out = ptr::null_mut();
             // We prefer posix_memalign over aligned_alloc since it is more widely available, and
diff --git a/library/std/src/sys/dbg.rs b/library/std/src/sys/dbg.rs
deleted file mode 100644
index 383ed84cfa2..00000000000
--- a/library/std/src/sys/dbg.rs
+++ /dev/null
@@ -1,229 +0,0 @@
-//! Debugging aids.
-
-/// Presence of a debugger. The debugger being concerned
-/// is expected to use the OS API to debug this process.
-#[derive(Copy, Clone, Debug)]
-#[allow(unused)]
-pub(crate) enum DebuggerPresence {
-    /// The debugger is attached to this process.
-    Detected,
-    /// The debugger is not attached to this process.
-    NotDetected,
-}
-
-#[cfg(target_os = "windows")]
-mod os {
-    use super::DebuggerPresence;
-
-    #[link(name = "kernel32")]
-    extern "system" {
-        fn IsDebuggerPresent() -> i32;
-    }
-
-    pub(super) fn is_debugger_present() -> Option<DebuggerPresence> {
-        // SAFETY: No state is shared between threads. The call reads
-        // a field from the Thread Environment Block using the OS API
-        // as required by the documentation.
-        if unsafe { IsDebuggerPresent() } != 0 {
-            Some(DebuggerPresence::Detected)
-        } else {
-            Some(DebuggerPresence::NotDetected)
-        }
-    }
-}
-
-#[cfg(any(target_vendor = "apple", target_os = "freebsd"))]
-mod os {
-    use libc::{CTL_KERN, KERN_PROC, KERN_PROC_PID, c_int, sysctl};
-
-    use super::DebuggerPresence;
-    use crate::io::{Cursor, Read, Seek, SeekFrom};
-    use crate::process;
-
-    const P_TRACED: i32 = 0x00000800;
-
-    // The assumption is that the kernel structures available to the
-    // user space may not shrink or repurpose the existing fields over
-    // time. The kernels normally adhere to that for the backward
-    // compatibility of the user space.
-
-    // The macOS 14.5 SDK comes with a header `MacOSX14.5.sdk/usr/include/sys/sysctl.h`
-    // that defines `struct kinfo_proc` be of `648` bytes on the 64-bit system. That has
-    // not changed since macOS 10.13 (released in 2017) at least, validated by building
-    // a C program in XCode while changing the build target. Apple provides this example
-    // for reference: https://developer.apple.com/library/archive/qa/qa1361/_index.html.
-    #[cfg(target_vendor = "apple")]
-    const KINFO_PROC_SIZE: usize = if cfg!(target_pointer_width = "64") { 648 } else { 492 };
-    #[cfg(target_vendor = "apple")]
-    const KINFO_PROC_FLAGS_OFFSET: u64 = if cfg!(target_pointer_width = "64") { 32 } else { 16 };
-
-    // Works for FreeBSD stable (13.3, 13.4) and current (14.0, 14.1).
-    // The size of the structure has stayed the same for a long time,
-    // at least since 2005:
-    // https://lists.freebsd.org/pipermail/freebsd-stable/2005-November/019899.html
-    #[cfg(target_os = "freebsd")]
-    const KINFO_PROC_SIZE: usize = if cfg!(target_pointer_width = "64") { 1088 } else { 768 };
-    #[cfg(target_os = "freebsd")]
-    const KINFO_PROC_FLAGS_OFFSET: u64 = if cfg!(target_pointer_width = "64") { 368 } else { 296 };
-
-    pub(super) fn is_debugger_present() -> Option<DebuggerPresence> {
-        debug_assert_ne!(KINFO_PROC_SIZE, 0);
-
-        let mut flags = [0u8; 4]; // `ki_flag` under FreeBSD and `p_flag` under macOS.
-        let mut mib = [CTL_KERN, KERN_PROC, KERN_PROC_PID, process::id() as c_int];
-        let mut info_size = KINFO_PROC_SIZE;
-        let mut kinfo_proc = [0u8; KINFO_PROC_SIZE];
-
-        // SAFETY: No state is shared with other threads. The sysctl call
-        // is safe according to the documentation.
-        if unsafe {
-            sysctl(
-                mib.as_mut_ptr(),
-                mib.len() as u32,
-                kinfo_proc.as_mut_ptr().cast(),
-                &mut info_size,
-                core::ptr::null_mut(),
-                0,
-            )
-        } != 0
-        {
-            return None;
-        }
-        debug_assert_eq!(info_size, KINFO_PROC_SIZE);
-
-        let mut reader = Cursor::new(kinfo_proc);
-        reader.seek(SeekFrom::Start(KINFO_PROC_FLAGS_OFFSET)).ok()?;
-        reader.read_exact(&mut flags).ok()?;
-        // Just in case, not limiting this to the little-endian systems.
-        let flags = i32::from_ne_bytes(flags);
-
-        if flags & P_TRACED != 0 {
-            Some(DebuggerPresence::Detected)
-        } else {
-            Some(DebuggerPresence::NotDetected)
-        }
-    }
-}
-
-#[cfg(target_os = "linux")]
-mod os {
-    use super::DebuggerPresence;
-    use crate::fs::File;
-    use crate::io::Read;
-
-    pub(super) fn is_debugger_present() -> Option<DebuggerPresence> {
-        // This function is crafted with the following goals:
-        // * Memory efficiency: It avoids crashing the panicking process due to
-        //   out-of-memory (OOM) conditions by not using large heap buffers or
-        //   allocating significant stack space, which could lead to stack overflow.
-        // * Minimal binary size: The function uses a minimal set of facilities
-        //   from the standard library to avoid increasing the resulting binary size.
-        //
-        // To achieve these goals, the function does not use `[std::io::BufReader]`
-        // and instead reads the file byte by byte using a sliding window approach.
-        // It's important to note that the "/proc/self/status" pseudo-file is synthesized
-        // by the Virtual File System (VFS), meaning it is not read from a slow or
-        // non-volatile storage medium so buffering might not be as beneficial because
-        // all data is read from memory, though this approach does incur a syscall for
-        // each byte read.
-        //
-        // We cannot make assumptions about the file size or the position of the
-        // target prefix ("TracerPid:"), so the function does not use
-        // `[std::fs::read_to_string]` thus not employing UTF-8 to ASCII checking,
-        // conversion, or parsing as we're looking for an ASCII prefix.
-        //
-        // These condiderations make the function deviate from the familiar concise pattern
-        // of searching for a string in a text file.
-
-        fn read_byte(file: &mut File) -> Option<u8> {
-            let mut buffer = [0];
-            file.read_exact(&mut buffer).ok()?;
-            Some(buffer[0])
-        }
-
-        // The ASCII prefix of the datum we're interested in.
-        const TRACER_PID: &[u8] = b"TracerPid:\t";
-
-        let mut file = File::open("/proc/self/status").ok()?;
-        let mut matched = 0;
-
-        // Look for the `TRACER_PID` prefix.
-        while let Some(byte) = read_byte(&mut file) {
-            if byte == TRACER_PID[matched] {
-                matched += 1;
-                if matched == TRACER_PID.len() {
-                    break;
-                }
-            } else {
-                matched = 0;
-            }
-        }
-
-        // Was the prefix found?
-        if matched != TRACER_PID.len() {
-            return None;
-        }
-
-        // It was; get the ASCII representation of the first digit
-        // of the PID. That is enough to see if there is a debugger
-        // attached as the kernel does not pad the PID on the left
-        // with the leading zeroes.
-        let byte = read_byte(&mut file)?;
-        if byte.is_ascii_digit() && byte != b'0' {
-            Some(DebuggerPresence::Detected)
-        } else {
-            Some(DebuggerPresence::NotDetected)
-        }
-    }
-}
-
-#[cfg(not(any(
-    target_os = "windows",
-    target_vendor = "apple",
-    target_os = "freebsd",
-    target_os = "linux"
-)))]
-mod os {
-    pub(super) fn is_debugger_present() -> Option<super::DebuggerPresence> {
-        None
-    }
-}
-
-/// Detect the debugger presence.
-///
-/// The code does not try to detect the debugger at all costs (e.g., when anti-debugger
-/// tricks are at play), it relies on the interfaces provided by the OS.
-///
-/// Return value:
-/// * `None`: it's not possible to conclude whether the debugger is attached to this
-///    process or not. When checking for the presence of the debugger, the detection logic
-///    encountered an issue, such as the OS API throwing an error or the feature not being
-///    implemented.
-/// * `Some(DebuggerPresence::Detected)`: yes, the debugger is attached
-///   to this process.
-/// * `Some(DebuggerPresence::NotDetected)`: no, the debugger is not
-///    attached to this process.
-pub(crate) fn is_debugger_present() -> Option<DebuggerPresence> {
-    if cfg!(miri) { None } else { os::is_debugger_present() }
-}
-
-/// Execute the breakpoint instruction if the debugger presence is detected.
-/// Useful for breaking into the debugger without the need to set a breakpoint
-/// in the debugger.
-///
-/// Note that there is a race between attaching or detaching the debugger, and running the
-/// breakpoint instruction. This is nonetheless memory-safe, like [`crate::process::abort`]
-/// is. In case the debugger is attached and the function is about
-/// to run the breakpoint instruction yet right before that the debugger detaches, the
-/// process will crash due to running the breakpoint instruction and the debugger not
-/// handling the trap exception.
-pub(crate) fn breakpoint_if_debugging() -> Option<DebuggerPresence> {
-    let debugger_present = is_debugger_present();
-    if let Some(DebuggerPresence::Detected) = debugger_present {
-        // SAFETY: Executing the breakpoint instruction. No state is shared
-        // or modified by this code.
-        unsafe { core::intrinsics::breakpoint() };
-    }
-
-    debugger_present
-}
diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs
index df25b84fbbe..f17dd47dece 100644
--- a/library/std/src/sys/mod.rs
+++ b/library/std/src/sys/mod.rs
@@ -11,7 +11,6 @@ mod personality;
 pub mod anonymous_pipe;
 pub mod backtrace;
 pub mod cmath;
-pub mod dbg;
 pub mod exit_guard;
 pub mod os_str;
 pub mod path;
diff --git a/library/std/src/sys/os_str/bytes.rs b/library/std/src/sys/os_str/bytes.rs
index 992767211d0..8e0609fe48c 100644
--- a/library/std/src/sys/os_str/bytes.rs
+++ b/library/std/src/sys/os_str/bytes.rs
@@ -2,7 +2,6 @@
 //! systems: just a `Vec<u8>`/`[u8]`.
 
 use core::clone::CloneToUninit;
-use core::ptr::addr_of_mut;
 
 use crate::borrow::Cow;
 use crate::collections::TryReserveError;
@@ -355,6 +354,6 @@ unsafe impl CloneToUninit for Slice {
     #[cfg_attr(debug_assertions, track_caller)]
     unsafe fn clone_to_uninit(&self, dst: *mut Self) {
         // SAFETY: we're just a wrapper around [u8]
-        unsafe { self.inner.clone_to_uninit(addr_of_mut!((*dst).inner)) }
+        unsafe { self.inner.clone_to_uninit(&raw mut (*dst).inner) }
     }
 }
diff --git a/library/std/src/sys/os_str/wtf8.rs b/library/std/src/sys/os_str/wtf8.rs
index 6fbbec7a945..b3834388df6 100644
--- a/library/std/src/sys/os_str/wtf8.rs
+++ b/library/std/src/sys/os_str/wtf8.rs
@@ -1,7 +1,6 @@
 //! The underlying OsString/OsStr implementation on Windows is a
 //! wrapper around the "WTF-8" encoding; see the `wtf8` module for more.
 use core::clone::CloneToUninit;
-use core::ptr::addr_of_mut;
 
 use crate::borrow::Cow;
 use crate::collections::TryReserveError;
@@ -278,6 +277,6 @@ unsafe impl CloneToUninit for Slice {
     #[cfg_attr(debug_assertions, track_caller)]
     unsafe fn clone_to_uninit(&self, dst: *mut Self) {
         // SAFETY: we're just a wrapper around Wtf8
-        unsafe { self.inner.clone_to_uninit(addr_of_mut!((*dst).inner)) }
+        unsafe { self.inner.clone_to_uninit(&raw mut (*dst).inner) }
     }
 }
diff --git a/library/std/src/sys/pal/hermit/mod.rs b/library/std/src/sys/pal/hermit/mod.rs
index f49ef947174..b62afb40a61 100644
--- a/library/std/src/sys/pal/hermit/mod.rs
+++ b/library/std/src/sys/pal/hermit/mod.rs
@@ -92,7 +92,11 @@ pub unsafe extern "C" fn runtime_entry(
     unsafe {
         crate::sys::thread_local::destructors::run();
     }
-    unsafe { hermit_abi::exit(result) }
+    crate::rt::thread_cleanup();
+
+    unsafe {
+        hermit_abi::exit(result);
+    }
 }
 
 #[inline]
diff --git a/library/std/src/sys/pal/hermit/net.rs b/library/std/src/sys/pal/hermit/net.rs
index 416469c0037..d9baa091a23 100644
--- a/library/std/src/sys/pal/hermit/net.rs
+++ b/library/std/src/sys/pal/hermit/net.rs
@@ -192,7 +192,7 @@ impl Socket {
                 buf.as_mut_ptr(),
                 buf.len(),
                 flags,
-                core::ptr::addr_of_mut!(storage) as *mut _,
+                (&raw mut storage) as *mut _,
                 &mut addrlen,
             )
         })?;
@@ -298,7 +298,7 @@ impl Socket {
             netc::ioctl(
                 self.as_raw_fd(),
                 netc::FIONBIO,
-                core::ptr::addr_of_mut!(nonblocking) as *mut core::ffi::c_void,
+                (&raw mut nonblocking) as *mut core::ffi::c_void,
             )
         })
         .map(drop)
diff --git a/library/std/src/sys/pal/hermit/thread.rs b/library/std/src/sys/pal/hermit/thread.rs
index 4c0c0919f47..41f2c3e2123 100644
--- a/library/std/src/sys/pal/hermit/thread.rs
+++ b/library/std/src/sys/pal/hermit/thread.rs
@@ -53,6 +53,7 @@ impl Thread {
 
                 // run all destructors
                 crate::sys::thread_local::destructors::run();
+                crate::rt::thread_cleanup();
             }
         }
     }
diff --git a/library/std/src/sys/pal/hermit/time.rs b/library/std/src/sys/pal/hermit/time.rs
index 99166b15602..e0b6eb76b03 100644
--- a/library/std/src/sys/pal/hermit/time.rs
+++ b/library/std/src/sys/pal/hermit/time.rs
@@ -107,8 +107,7 @@ pub struct Instant(Timespec);
 impl Instant {
     pub fn now() -> Instant {
         let mut time: Timespec = Timespec::zero();
-        let _ =
-            unsafe { hermit_abi::clock_gettime(CLOCK_MONOTONIC, core::ptr::addr_of_mut!(time.t)) };
+        let _ = unsafe { hermit_abi::clock_gettime(CLOCK_MONOTONIC, &raw mut time.t) };
 
         Instant(time)
     }
@@ -209,8 +208,7 @@ impl SystemTime {
 
     pub fn now() -> SystemTime {
         let mut time: Timespec = Timespec::zero();
-        let _ =
-            unsafe { hermit_abi::clock_gettime(CLOCK_REALTIME, core::ptr::addr_of_mut!(time.t)) };
+        let _ = unsafe { hermit_abi::clock_gettime(CLOCK_REALTIME, &raw mut time.t) };
 
         SystemTime(time)
     }
diff --git a/library/std/src/sys/pal/unix/args.rs b/library/std/src/sys/pal/unix/args.rs
index a943e3a581a..8438a61e90f 100644
--- a/library/std/src/sys/pal/unix/args.rs
+++ b/library/std/src/sys/pal/unix/args.rs
@@ -113,6 +113,7 @@ impl DoubleEndedIterator for Args {
     target_os = "nto",
     target_os = "hurd",
     target_os = "rtems",
+    target_os = "nuttx",
 ))]
 mod imp {
     use crate::ffi::c_char;
diff --git a/library/std/src/sys/pal/unix/env.rs b/library/std/src/sys/pal/unix/env.rs
index b2d399b8791..2aee0b5d460 100644
--- a/library/std/src/sys/pal/unix/env.rs
+++ b/library/std/src/sys/pal/unix/env.rs
@@ -283,3 +283,14 @@ pub mod os {
     pub const EXE_SUFFIX: &str = "";
     pub const EXE_EXTENSION: &str = "";
 }
+
+#[cfg(target_os = "nuttx")]
+pub mod os {
+    pub const FAMILY: &str = "unix";
+    pub const OS: &str = "nuttx";
+    pub const DLL_PREFIX: &str = "lib";
+    pub const DLL_SUFFIX: &str = ".so";
+    pub const DLL_EXTENSION: &str = "so";
+    pub const EXE_SUFFIX: &str = "";
+    pub const EXE_EXTENSION: &str = "";
+}
diff --git a/library/std/src/sys/pal/unix/fd.rs b/library/std/src/sys/pal/unix/fd.rs
index 135042779ad..6a28799ca55 100644
--- a/library/std/src/sys/pal/unix/fd.rs
+++ b/library/std/src/sys/pal/unix/fd.rs
@@ -98,7 +98,12 @@ impl FileDesc {
         Ok(ret as usize)
     }
 
-    #[cfg(not(any(target_os = "espidf", target_os = "horizon", target_os = "vita")))]
+    #[cfg(not(any(
+        target_os = "espidf",
+        target_os = "horizon",
+        target_os = "vita",
+        target_os = "nuttx"
+    )))]
     pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
         let ret = cvt(unsafe {
             libc::readv(
@@ -110,14 +115,24 @@ impl FileDesc {
         Ok(ret as usize)
     }
 
-    #[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))]
+    #[cfg(any(
+        target_os = "espidf",
+        target_os = "horizon",
+        target_os = "vita",
+        target_os = "nuttx"
+    ))]
     pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
         io::default_read_vectored(|b| self.read(b), bufs)
     }
 
     #[inline]
     pub fn is_read_vectored(&self) -> bool {
-        cfg!(not(any(target_os = "espidf", target_os = "horizon", target_os = "vita")))
+        cfg!(not(any(
+            target_os = "espidf",
+            target_os = "horizon",
+            target_os = "vita",
+            target_os = "nuttx"
+        )))
     }
 
     pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
@@ -297,7 +312,12 @@ impl FileDesc {
         Ok(ret as usize)
     }
 
-    #[cfg(not(any(target_os = "espidf", target_os = "horizon", target_os = "vita")))]
+    #[cfg(not(any(
+        target_os = "espidf",
+        target_os = "horizon",
+        target_os = "vita",
+        target_os = "nuttx"
+    )))]
     pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
         let ret = cvt(unsafe {
             libc::writev(
@@ -309,14 +329,24 @@ impl FileDesc {
         Ok(ret as usize)
     }
 
-    #[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))]
+    #[cfg(any(
+        target_os = "espidf",
+        target_os = "horizon",
+        target_os = "vita",
+        target_os = "nuttx"
+    ))]
     pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
         io::default_write_vectored(|b| self.write(b), bufs)
     }
 
     #[inline]
     pub fn is_write_vectored(&self) -> bool {
-        cfg!(not(any(target_os = "espidf", target_os = "horizon", target_os = "vita")))
+        cfg!(not(any(
+            target_os = "espidf",
+            target_os = "horizon",
+            target_os = "vita",
+            target_os = "nuttx"
+        )))
     }
 
     #[cfg_attr(target_os = "vxworks", allow(unused_unsafe))]
diff --git a/library/std/src/sys/pal/unix/fs.rs b/library/std/src/sys/pal/unix/fs.rs
index dade6d6bbae..39aabf0b2d6 100644
--- a/library/std/src/sys/pal/unix/fs.rs
+++ b/library/std/src/sys/pal/unix/fs.rs
@@ -479,6 +479,7 @@ impl FileAttr {
         target_os = "vita",
         target_os = "hurd",
         target_os = "rtems",
+        target_os = "nuttx",
     )))]
     pub fn modified(&self) -> io::Result<SystemTime> {
         #[cfg(target_pointer_width = "32")]
@@ -501,7 +502,7 @@ impl FileAttr {
         SystemTime::new(self.stat.st_mtime as i64, 0)
     }
 
-    #[cfg(any(target_os = "horizon", target_os = "hurd"))]
+    #[cfg(any(target_os = "horizon", target_os = "hurd", target_os = "nuttx"))]
     pub fn modified(&self) -> io::Result<SystemTime> {
         SystemTime::new(self.stat.st_mtim.tv_sec as i64, self.stat.st_mtim.tv_nsec as i64)
     }
@@ -513,6 +514,7 @@ impl FileAttr {
         target_os = "vita",
         target_os = "hurd",
         target_os = "rtems",
+        target_os = "nuttx",
     )))]
     pub fn accessed(&self) -> io::Result<SystemTime> {
         #[cfg(target_pointer_width = "32")]
@@ -535,7 +537,7 @@ impl FileAttr {
         SystemTime::new(self.stat.st_atime as i64, 0)
     }
 
-    #[cfg(any(target_os = "horizon", target_os = "hurd"))]
+    #[cfg(any(target_os = "horizon", target_os = "hurd", target_os = "nuttx"))]
     pub fn accessed(&self) -> io::Result<SystemTime> {
         SystemTime::new(self.stat.st_atim.tv_sec as i64, self.stat.st_atim.tv_nsec as i64)
     }
@@ -738,7 +740,7 @@ impl Iterator for ReadDir {
                 //
                 // Like for uninitialized contents, converting entry_ptr to `&dirent64`
                 // would not be legal. However, unique to dirent64 is that we don't even
-                // get to use `addr_of!((*entry_ptr).d_name)` because that operation
+                // get to use `&raw const (*entry_ptr).d_name` because that operation
                 // requires the full extent of *entry_ptr to be in bounds of the same
                 // allocation, which is not necessarily the case here.
                 //
@@ -752,7 +754,7 @@ impl Iterator for ReadDir {
                         } else {
                             #[allow(deref_nullptr)]
                             {
-                                ptr::addr_of!((*ptr::null::<dirent64>()).$field)
+                                &raw const (*ptr::null::<dirent64>()).$field
                             }
                         }
                     }};
@@ -866,6 +868,7 @@ impl Drop for Dir {
             target_os = "horizon",
             target_os = "vxworks",
             target_os = "rtems",
+            target_os = "nuttx",
         )))]
         {
             let fd = unsafe { libc::dirfd(self.0) };
@@ -1000,6 +1003,13 @@ impl DirEntry {
         self.entry.d_fileno as u64
     }
 
+    #[cfg(target_os = "nuttx")]
+    pub fn ino(&self) -> u64 {
+        // Leave this 0 for now, as NuttX does not provide an inode number
+        // in its directory entries.
+        0
+    }
+
     #[cfg(any(
         target_os = "netbsd",
         target_os = "openbsd",
@@ -1327,7 +1337,8 @@ impl File {
             target_os = "redox",
             target_os = "espidf",
             target_os = "horizon",
-            target_os = "vxworks"
+            target_os = "vxworks",
+            target_os = "nuttx",
         )))]
         let to_timespec = |time: Option<SystemTime>| match time {
             Some(time) if let Some(ts) = time.t.to_timespec() => Ok(ts),
@@ -1342,7 +1353,7 @@ impl File {
             None => Ok(libc::timespec { tv_sec: 0, tv_nsec: libc::UTIME_OMIT as _ }),
         };
         cfg_if::cfg_if! {
-            if #[cfg(any(target_os = "redox", target_os = "espidf", target_os = "horizon", target_os = "vxworks"))] {
+            if #[cfg(any(target_os = "redox", target_os = "espidf", target_os = "horizon", target_os = "vxworks", target_os = "nuttx"))] {
                 // Redox doesn't appear to support `UTIME_OMIT`.
                 // ESP-IDF and HorizonOS do not support `futimens` at all and the behavior for those OS is therefore
                 // the same as for Redox.
@@ -1374,7 +1385,7 @@ impl File {
                 }
                 cvt(unsafe { libc::fsetattrlist(
                     self.as_raw_fd(),
-                    core::ptr::addr_of!(attrlist).cast::<libc::c_void>().cast_mut(),
+                    (&raw const attrlist).cast::<libc::c_void>().cast_mut(),
                     buf.as_ptr().cast::<libc::c_void>().cast_mut(),
                     num_times * mem::size_of::<libc::timespec>(),
                     0
@@ -1933,7 +1944,7 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
         libc::copyfile_state_get(
             state.0,
             libc::COPYFILE_STATE_COPIED as u32,
-            core::ptr::addr_of_mut!(bytes_copied) as *mut libc::c_void,
+            (&raw mut bytes_copied) as *mut libc::c_void,
         )
     })?;
     Ok(bytes_copied as u64)
diff --git a/library/std/src/sys/pal/unix/mod.rs b/library/std/src/sys/pal/unix/mod.rs
index 0d63b1119d5..4fe18daa204 100644
--- a/library/std/src/sys/pal/unix/mod.rs
+++ b/library/std/src/sys/pal/unix/mod.rs
@@ -222,6 +222,7 @@ static ON_BROKEN_PIPE_FLAG_USED: crate::sync::atomic::AtomicBool =
     target_os = "horizon",
     target_os = "vxworks",
     target_os = "vita",
+    target_os = "nuttx",
 )))]
 pub(crate) fn on_broken_pipe_flag_used() -> bool {
     ON_BROKEN_PIPE_FLAG_USED.load(crate::sync::atomic::Ordering::Relaxed)
@@ -425,7 +426,7 @@ cfg_if::cfg_if! {
     }
 }
 
-#[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))]
+#[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita", target_os = "nuttx"))]
 mod unsupported {
     use crate::io;
 
diff --git a/library/std/src/sys/pal/unix/net.rs b/library/std/src/sys/pal/unix/net.rs
index e98232ba89a..6a67bb0a101 100644
--- a/library/std/src/sys/pal/unix/net.rs
+++ b/library/std/src/sys/pal/unix/net.rs
@@ -38,19 +38,19 @@ pub fn cvt_gai(err: c_int) -> io::Result<()> {
     // We may need to trigger a glibc workaround. See on_resolver_failure() for details.
     on_resolver_failure();
 
-    #[cfg(not(target_os = "espidf"))]
+    #[cfg(not(any(target_os = "espidf", target_os = "nuttx")))]
     if err == libc::EAI_SYSTEM {
         return Err(io::Error::last_os_error());
     }
 
-    #[cfg(not(target_os = "espidf"))]
+    #[cfg(not(any(target_os = "espidf", target_os = "nuttx")))]
     let detail = unsafe {
         // We can't always expect a UTF-8 environment. When we don't get that luxury,
         // it's better to give a low-quality error message than none at all.
         CStr::from_ptr(libc::gai_strerror(err)).to_string_lossy()
     };
 
-    #[cfg(target_os = "espidf")]
+    #[cfg(any(target_os = "espidf", target_os = "nuttx"))]
     let detail = "";
 
     Err(io::Error::new(
@@ -329,7 +329,7 @@ impl Socket {
                 buf.as_mut_ptr() as *mut c_void,
                 buf.len(),
                 flags,
-                core::ptr::addr_of_mut!(storage) as *mut _,
+                (&raw mut storage) as *mut _,
                 &mut addrlen,
             )
         })?;
diff --git a/library/std/src/sys/pal/unix/os.rs b/library/std/src/sys/pal/unix/os.rs
index 503f8915256..f983d174ed6 100644
--- a/library/std/src/sys/pal/unix/os.rs
+++ b/library/std/src/sys/pal/unix/os.rs
@@ -48,6 +48,7 @@ extern "C" {
             target_os = "openbsd",
             target_os = "android",
             target_os = "redox",
+            target_os = "nuttx",
             target_env = "newlib"
         ),
         link_name = "__errno"
@@ -399,6 +400,7 @@ pub fn current_exe() -> io::Result<PathBuf> {
     target_os = "linux",
     target_os = "hurd",
     target_os = "android",
+    target_os = "nuttx",
     target_os = "emscripten"
 ))]
 pub fn current_exe() -> io::Result<PathBuf> {
@@ -610,7 +612,7 @@ pub unsafe fn environ() -> *mut *const *const c_char {
     extern "C" {
         static mut environ: *const *const c_char;
     }
-    ptr::addr_of_mut!(environ)
+    &raw mut environ
 }
 
 static ENV_LOCK: RwLock<()> = RwLock::new(());
@@ -717,6 +719,7 @@ pub fn home_dir() -> Option<PathBuf> {
         target_os = "espidf",
         target_os = "horizon",
         target_os = "vita",
+        target_os = "nuttx",
         all(target_vendor = "apple", not(target_os = "macos")),
     ))]
     unsafe fn fallback() -> Option<OsString> {
@@ -730,6 +733,7 @@ pub fn home_dir() -> Option<PathBuf> {
         target_os = "espidf",
         target_os = "horizon",
         target_os = "vita",
+        target_os = "nuttx",
         all(target_vendor = "apple", not(target_os = "macos")),
     )))]
     unsafe fn fallback() -> Option<OsString> {
diff --git a/library/std/src/sys/pal/unix/process/mod.rs b/library/std/src/sys/pal/unix/process/mod.rs
index 074f0a105e3..2751d51c44d 100644
--- a/library/std/src/sys/pal/unix/process/mod.rs
+++ b/library/std/src/sys/pal/unix/process/mod.rs
@@ -2,10 +2,10 @@ 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;
 
-#[cfg_attr(any(target_os = "espidf", target_os = "horizon"), allow(unused))]
+#[cfg_attr(any(target_os = "espidf", target_os = "horizon", target_os = "nuttx"), allow(unused))]
 mod process_common;
 
-#[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))]
+#[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita", target_os = "nuttx"))]
 mod process_unsupported;
 
 cfg_if::cfg_if! {
@@ -16,7 +16,7 @@ cfg_if::cfg_if! {
     } else if #[cfg(target_os = "vxworks")] {
         #[path = "process_vxworks.rs"]
         mod process_inner;
-    } else if #[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))] {
+    } else if #[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita", target_os = "nuttx"))] {
         mod process_inner {
             pub use super::process_unsupported::*;
         }
diff --git a/library/std/src/sys/pal/unix/process/process_fuchsia.rs b/library/std/src/sys/pal/unix/process/process_fuchsia.rs
index 34ff464aa37..5d0110cf55d 100644
--- a/library/std/src/sys/pal/unix/process/process_fuchsia.rs
+++ b/library/std/src/sys/pal/unix/process/process_fuchsia.rs
@@ -178,7 +178,7 @@ impl Process {
             zx_cvt(zx_object_get_info(
                 self.handle.raw(),
                 ZX_INFO_PROCESS,
-                core::ptr::addr_of_mut!(proc_info) as *mut libc::c_void,
+                (&raw mut proc_info) as *mut libc::c_void,
                 mem::size_of::<zx_info_process_t>(),
                 &mut actual,
                 &mut avail,
@@ -215,7 +215,7 @@ impl Process {
             zx_cvt(zx_object_get_info(
                 self.handle.raw(),
                 ZX_INFO_PROCESS,
-                core::ptr::addr_of_mut!(proc_info) as *mut libc::c_void,
+                (&raw mut proc_info) as *mut libc::c_void,
                 mem::size_of::<zx_info_process_t>(),
                 &mut actual,
                 &mut avail,
diff --git a/library/std/src/sys/pal/unix/process/process_unix.rs b/library/std/src/sys/pal/unix/process/process_unix.rs
index d812aa0e02c..5d30f388da1 100644
--- a/library/std/src/sys/pal/unix/process/process_unix.rs
+++ b/library/std/src/sys/pal/unix/process/process_unix.rs
@@ -788,15 +788,15 @@ impl Command {
             let mut iov = [IoSlice::new(b"")];
             let mut msg: libc::msghdr = mem::zeroed();
 
-            msg.msg_iov = core::ptr::addr_of_mut!(iov) as *mut _;
+            msg.msg_iov = (&raw mut iov) as *mut _;
             msg.msg_iovlen = 1;
 
             // only attach cmsg if we successfully acquired the pidfd
             if pidfd >= 0 {
                 msg.msg_controllen = mem::size_of_val(&cmsg.buf) as _;
-                msg.msg_control = core::ptr::addr_of_mut!(cmsg.buf) as *mut _;
+                msg.msg_control = (&raw mut cmsg.buf) as *mut _;
 
-                let hdr = CMSG_FIRSTHDR(core::ptr::addr_of_mut!(msg) as *mut _);
+                let hdr = CMSG_FIRSTHDR((&raw mut msg) as *mut _);
                 (*hdr).cmsg_level = SOL_SOCKET;
                 (*hdr).cmsg_type = SCM_RIGHTS;
                 (*hdr).cmsg_len = CMSG_LEN(SCM_MSG_LEN as _) as _;
@@ -838,17 +838,17 @@ impl Command {
 
             let mut msg: libc::msghdr = mem::zeroed();
 
-            msg.msg_iov = core::ptr::addr_of_mut!(iov) as *mut _;
+            msg.msg_iov = (&raw mut iov) as *mut _;
             msg.msg_iovlen = 1;
             msg.msg_controllen = mem::size_of::<Cmsg>() as _;
-            msg.msg_control = core::ptr::addr_of_mut!(cmsg) as *mut _;
+            msg.msg_control = (&raw mut cmsg) as *mut _;
 
             match cvt_r(|| libc::recvmsg(sock.as_raw(), &mut msg, libc::MSG_CMSG_CLOEXEC)) {
                 Err(_) => return -1,
                 Ok(_) => {}
             }
 
-            let hdr = CMSG_FIRSTHDR(core::ptr::addr_of_mut!(msg) as *mut _);
+            let hdr = CMSG_FIRSTHDR((&raw mut msg) as *mut _);
             if hdr.is_null()
                 || (*hdr).cmsg_level != SOL_SOCKET
                 || (*hdr).cmsg_type != SCM_RIGHTS
diff --git a/library/std/src/sys/pal/unix/stack_overflow.rs b/library/std/src/sys/pal/unix/stack_overflow.rs
index e0a0d0973c6..ac0858e1de8 100644
--- a/library/std/src/sys/pal/unix/stack_overflow.rs
+++ b/library/std/src/sys/pal/unix/stack_overflow.rs
@@ -426,8 +426,8 @@ mod imp {
             match sysctlbyname.get() {
                 Some(fcn) if unsafe {
                     fcn(oid.as_ptr(),
-                        ptr::addr_of_mut!(guard).cast(),
-                        ptr::addr_of_mut!(size),
+                        (&raw mut guard).cast(),
+                        &raw mut size,
                         ptr::null_mut(),
                         0) == 0
                 } => guard,
diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/pal/unix/thread.rs
index 7fe9b6c3e52..04024661836 100644
--- a/library/std/src/sys/pal/unix/thread.rs
+++ b/library/std/src/sys/pal/unix/thread.rs
@@ -117,13 +117,15 @@ impl Thread {
     pub fn set_name(name: &CStr) {
         const PR_SET_NAME: libc::c_int = 15;
         unsafe {
-            libc::prctl(
+            let res = libc::prctl(
                 PR_SET_NAME,
                 name.as_ptr(),
                 0 as libc::c_ulong,
                 0 as libc::c_ulong,
                 0 as libc::c_ulong,
             );
+            // We have no good way of propagating errors here, but in debug-builds let's check that this actually worked.
+            debug_assert_eq!(res, 0);
         }
     }
 
@@ -140,7 +142,12 @@ impl Thread {
         }
     }
 
-    #[cfg(any(target_os = "freebsd", target_os = "dragonfly", target_os = "openbsd"))]
+    #[cfg(any(
+        target_os = "freebsd",
+        target_os = "dragonfly",
+        target_os = "openbsd",
+        target_os = "nuttx"
+    ))]
     pub fn set_name(name: &CStr) {
         unsafe {
             libc::pthread_set_name_np(libc::pthread_self(), name.as_ptr());
@@ -253,7 +260,7 @@ impl Thread {
                     tv_nsec: nsecs,
                 };
                 secs -= ts.tv_sec as u64;
-                let ts_ptr = core::ptr::addr_of_mut!(ts);
+                let ts_ptr = &raw mut ts;
                 if libc::nanosleep(ts_ptr, ts_ptr) == -1 {
                     assert_eq!(os::errno(), libc::EINTR);
                     secs += ts.tv_sec as u64;
@@ -442,8 +449,8 @@ pub fn available_parallelism() -> io::Result<NonZero<usize>> {
                     libc::sysctl(
                         mib.as_mut_ptr(),
                         2,
-                        core::ptr::addr_of_mut!(cpus) as *mut _,
-                        core::ptr::addr_of_mut!(cpus_size) as *mut _,
+                        (&raw mut cpus) as *mut _,
+                        (&raw mut cpus_size) as *mut _,
                         ptr::null_mut(),
                         0,
                     )
@@ -517,7 +524,7 @@ mod cgroups {
     use crate::borrow::Cow;
     use crate::ffi::OsString;
     use crate::fs::{File, exists};
-    use crate::io::{BufRead, BufReader, Read};
+    use crate::io::{BufRead, Read};
     use crate::os::unix::ffi::OsStringExt;
     use crate::path::{Path, PathBuf};
     use crate::str::from_utf8;
@@ -690,7 +697,7 @@ mod cgroups {
     /// If the cgroupfs is a bind mount then `group_path` is adjusted to skip
     /// over the already-included prefix
     fn find_mountpoint(group_path: &Path) -> Option<(Cow<'static, str>, &Path)> {
-        let mut reader = BufReader::new(File::open("/proc/self/mountinfo").ok()?);
+        let mut reader = File::open_buffered("/proc/self/mountinfo").ok()?;
         let mut line = String::with_capacity(256);
         loop {
             line.clear();
@@ -747,12 +754,15 @@ unsafe fn min_stack_size(attr: *const libc::pthread_attr_t) -> usize {
 }
 
 // No point in looking up __pthread_get_minstack() on non-glibc platforms.
-#[cfg(all(not(all(target_os = "linux", target_env = "gnu")), not(target_os = "netbsd")))]
+#[cfg(all(
+    not(all(target_os = "linux", target_env = "gnu")),
+    not(any(target_os = "netbsd", target_os = "nuttx"))
+))]
 unsafe fn min_stack_size(_: *const libc::pthread_attr_t) -> usize {
     libc::PTHREAD_STACK_MIN
 }
 
-#[cfg(target_os = "netbsd")]
+#[cfg(any(target_os = "netbsd", target_os = "nuttx"))]
 unsafe fn min_stack_size(_: *const libc::pthread_attr_t) -> usize {
     static STACK: crate::sync::OnceLock<usize> = crate::sync::OnceLock::new();
 
diff --git a/library/std/src/sys/pal/wasip2/mod.rs b/library/std/src/sys/pal/wasip2/mod.rs
index 17b26543bd7..320712fdcc9 100644
--- a/library/std/src/sys/pal/wasip2/mod.rs
+++ b/library/std/src/sys/pal/wasip2/mod.rs
@@ -20,7 +20,6 @@ pub mod futex;
 #[path = "../wasi/io.rs"]
 pub mod io;
 
-#[path = "../wasi/net.rs"]
 pub mod net;
 #[path = "../wasi/os.rs"]
 pub mod os;
diff --git a/library/std/src/sys/pal/wasip2/net.rs b/library/std/src/sys/pal/wasip2/net.rs
new file mode 100644
index 00000000000..c40eb229ba9
--- /dev/null
+++ b/library/std/src/sys/pal/wasip2/net.rs
@@ -0,0 +1,379 @@
+#![deny(unsafe_op_in_unsafe_fn)]
+
+use libc::{c_int, c_void, size_t};
+
+use super::fd::WasiFd;
+use crate::ffi::CStr;
+use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut};
+use crate::net::{Shutdown, SocketAddr};
+use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
+use crate::sys::unsupported;
+use crate::sys_common::net::{TcpListener, getsockopt, setsockopt, sockaddr_to_addr};
+use crate::sys_common::{AsInner, FromInner, IntoInner};
+use crate::time::{Duration, Instant};
+use crate::{cmp, mem, str};
+
+pub extern crate libc as netc;
+
+#[allow(non_camel_case_types)]
+pub type wrlen_t = size_t;
+
+#[doc(hidden)]
+pub trait IsMinusOne {
+    fn is_minus_one(&self) -> bool;
+}
+
+macro_rules! impl_is_minus_one {
+    ($($t:ident)*) => ($(impl IsMinusOne for $t {
+        fn is_minus_one(&self) -> bool {
+            *self == -1
+        }
+    })*)
+}
+
+impl_is_minus_one! { i8 i16 i32 i64 isize }
+
+pub fn cvt<T: IsMinusOne>(t: T) -> crate::io::Result<T> {
+    if t.is_minus_one() { Err(crate::io::Error::last_os_error()) } else { Ok(t) }
+}
+
+pub fn cvt_r<T, F>(mut f: F) -> crate::io::Result<T>
+where
+    T: IsMinusOne,
+    F: FnMut() -> T,
+{
+    loop {
+        match cvt(f()) {
+            Err(ref e) if e.is_interrupted() => {}
+            other => return other,
+        }
+    }
+}
+
+pub fn cvt_gai(err: c_int) -> io::Result<()> {
+    if err == 0 {
+        return Ok(());
+    }
+
+    if err == netc::EAI_SYSTEM {
+        return Err(io::Error::last_os_error());
+    }
+
+    let detail = unsafe {
+        str::from_utf8(CStr::from_ptr(netc::gai_strerror(err)).to_bytes()).unwrap().to_owned()
+    };
+
+    Err(io::Error::new(
+        io::ErrorKind::Uncategorized,
+        &format!("failed to lookup address information: {detail}")[..],
+    ))
+}
+
+pub fn init() {}
+
+pub struct Socket(WasiFd);
+
+impl Socket {
+    pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result<Socket> {
+        let fam = match *addr {
+            SocketAddr::V4(..) => netc::AF_INET,
+            SocketAddr::V6(..) => netc::AF_INET6,
+        };
+        Socket::new_raw(fam, ty)
+    }
+
+    pub fn new_raw(fam: c_int, ty: c_int) -> io::Result<Socket> {
+        let fd = cvt(unsafe { netc::socket(fam, ty, 0) })?;
+        Ok(unsafe { Self::from_raw_fd(fd) })
+    }
+
+    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 = self.connect(addr);
+        self.set_nonblocking(false)?;
+
+        match r {
+            Ok(_) => return Ok(()),
+            // there's no ErrorKind for EINPROGRESS
+            Err(ref e) if e.raw_os_error() == Some(netc::EINPROGRESS) => {}
+            Err(e) => return Err(e),
+        }
+
+        let mut pollfd = netc::pollfd { fd: self.as_raw_fd(), events: netc::POLLOUT, revents: 0 };
+
+        if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 {
+            return Err(io::Error::ZERO_TIMEOUT);
+        }
+
+        let start = Instant::now();
+
+        loop {
+            let elapsed = start.elapsed();
+            if elapsed >= timeout {
+                return Err(io::const_io_error!(io::ErrorKind::TimedOut, "connection timed out"));
+            }
+
+            let timeout = timeout - elapsed;
+            let mut timeout = timeout
+                .as_secs()
+                .saturating_mul(1_000)
+                .saturating_add(timeout.subsec_nanos() as u64 / 1_000_000);
+            if timeout == 0 {
+                timeout = 1;
+            }
+
+            let timeout = cmp::min(timeout, c_int::MAX as u64) as c_int;
+
+            match unsafe { netc::poll(&mut pollfd, 1, timeout) } {
+                -1 => {
+                    let err = io::Error::last_os_error();
+                    if !err.is_interrupted() {
+                        return Err(err);
+                    }
+                }
+                0 => {}
+                _ => {
+                    // WASI poll does not return  POLLHUP or POLLERR in revents. Check if the
+                    // connnection actually succeeded and return ok only when the socket is
+                    // ready and no errors were found.
+                    if let Some(e) = self.take_error()? {
+                        return Err(e);
+                    }
+
+                    return Ok(());
+                }
+            }
+        }
+    }
+
+    pub fn accept(
+        &self,
+        storage: *mut netc::sockaddr,
+        len: *mut netc::socklen_t,
+    ) -> io::Result<Socket> {
+        let fd = cvt_r(|| unsafe { netc::accept(self.as_raw_fd(), storage, len) })?;
+        Ok(unsafe { Self::from_raw_fd(fd) })
+    }
+
+    pub fn duplicate(&self) -> io::Result<Socket> {
+        unsupported()
+    }
+
+    fn recv_with_flags(&self, mut buf: BorrowedCursor<'_>, flags: c_int) -> io::Result<()> {
+        let ret = cvt(unsafe {
+            netc::recv(
+                self.as_raw_fd(),
+                buf.as_mut().as_mut_ptr() as *mut c_void,
+                buf.capacity(),
+                flags,
+            )
+        })?;
+        unsafe {
+            buf.advance_unchecked(ret as usize);
+        }
+        Ok(())
+    }
+
+    pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
+        let mut buf = BorrowedBuf::from(buf);
+        self.recv_with_flags(buf.unfilled(), 0)?;
+        Ok(buf.len())
+    }
+
+    pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
+        let mut buf = BorrowedBuf::from(buf);
+        self.recv_with_flags(buf.unfilled(), netc::MSG_PEEK)?;
+        Ok(buf.len())
+    }
+
+    pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> {
+        self.recv_with_flags(buf, 0)
+    }
+
+    pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+        io::default_read_vectored(|b| self.read(b), bufs)
+    }
+
+    #[inline]
+    pub fn is_read_vectored(&self) -> bool {
+        false
+    }
+
+    fn recv_from_with_flags(
+        &self,
+        buf: &mut [u8],
+        flags: c_int,
+    ) -> io::Result<(usize, SocketAddr)> {
+        let mut storage: netc::sockaddr_storage = unsafe { mem::zeroed() };
+        let mut addrlen = mem::size_of_val(&storage) as netc::socklen_t;
+
+        let n = cvt(unsafe {
+            netc::recvfrom(
+                self.as_raw_fd(),
+                buf.as_mut_ptr() as *mut c_void,
+                buf.len(),
+                flags,
+                core::ptr::addr_of_mut!(storage) as *mut _,
+                &mut addrlen,
+            )
+        })?;
+        Ok((n as usize, sockaddr_to_addr(&storage, addrlen as usize)?))
+    }
+
+    pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
+        self.recv_from_with_flags(buf, 0)
+    }
+
+    pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
+        self.recv_from_with_flags(buf, netc::MSG_PEEK)
+    }
+
+    fn write(&self, buf: &[u8]) -> io::Result<usize> {
+        let len = cmp::min(buf.len(), <wrlen_t>::MAX as usize) as wrlen_t;
+        let ret = cvt(unsafe {
+            netc::send(self.as_raw(), buf.as_ptr() as *const c_void, len, netc::MSG_NOSIGNAL)
+        })?;
+        Ok(ret as usize)
+    }
+
+    pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+        io::default_write_vectored(|b| self.write(b), bufs)
+    }
+
+    #[inline]
+    pub fn is_write_vectored(&self) -> bool {
+        false
+    }
+
+    pub fn set_timeout(&self, dur: Option<Duration>, kind: c_int) -> io::Result<()> {
+        let timeout = match dur {
+            Some(dur) => {
+                if dur.as_secs() == 0 && dur.subsec_nanos() == 0 {
+                    return Err(io::Error::ZERO_TIMEOUT);
+                }
+
+                let secs = dur.as_secs().try_into().unwrap_or(netc::time_t::MAX);
+                let mut timeout = netc::timeval {
+                    tv_sec: secs,
+                    tv_usec: dur.subsec_micros() as netc::suseconds_t,
+                };
+                if timeout.tv_sec == 0 && timeout.tv_usec == 0 {
+                    timeout.tv_usec = 1;
+                }
+                timeout
+            }
+            None => netc::timeval { tv_sec: 0, tv_usec: 0 },
+        };
+        setsockopt(self, netc::SOL_SOCKET, kind, timeout)
+    }
+
+    pub fn timeout(&self, kind: c_int) -> io::Result<Option<Duration>> {
+        let raw: netc::timeval = getsockopt(self, netc::SOL_SOCKET, kind)?;
+        if raw.tv_sec == 0 && raw.tv_usec == 0 {
+            Ok(None)
+        } else {
+            let sec = raw.tv_sec as u64;
+            let nsec = (raw.tv_usec as u32) * 1000;
+            Ok(Some(Duration::new(sec, nsec)))
+        }
+    }
+
+    pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
+        let how = match how {
+            Shutdown::Write => netc::SHUT_WR,
+            Shutdown::Read => netc::SHUT_RD,
+            Shutdown::Both => netc::SHUT_RDWR,
+        };
+        cvt(unsafe { netc::shutdown(self.as_raw_fd(), how) })?;
+        Ok(())
+    }
+
+    pub fn set_linger(&self, _linger: Option<Duration>) -> io::Result<()> {
+        unsupported()
+    }
+
+    pub fn linger(&self) -> io::Result<Option<Duration>> {
+        unsupported()
+    }
+
+    pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> {
+        setsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY, nodelay as c_int)
+    }
+
+    pub fn nodelay(&self) -> io::Result<bool> {
+        let raw: c_int = getsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY)?;
+        Ok(raw != 0)
+    }
+
+    pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
+        let mut nonblocking = nonblocking as c_int;
+        cvt(unsafe { netc::ioctl(self.as_raw_fd(), netc::FIONBIO, &mut nonblocking) }).map(drop)
+    }
+
+    pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+        let raw: c_int = getsockopt(self, netc::SOL_SOCKET, netc::SO_ERROR)?;
+        if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) }
+    }
+
+    // This is used by sys_common code to abstract over Windows and Unix.
+    pub fn as_raw(&self) -> RawFd {
+        self.as_raw_fd()
+    }
+}
+
+impl AsInner<WasiFd> for Socket {
+    #[inline]
+    fn as_inner(&self) -> &WasiFd {
+        &self.0
+    }
+}
+
+impl IntoInner<WasiFd> for Socket {
+    fn into_inner(self) -> WasiFd {
+        self.0
+    }
+}
+
+impl FromInner<WasiFd> for Socket {
+    fn from_inner(inner: WasiFd) -> Socket {
+        Socket(inner)
+    }
+}
+
+impl AsFd for Socket {
+    fn as_fd(&self) -> BorrowedFd<'_> {
+        self.0.as_fd()
+    }
+}
+
+impl AsRawFd for Socket {
+    #[inline]
+    fn as_raw_fd(&self) -> RawFd {
+        self.0.as_raw_fd()
+    }
+}
+
+impl IntoRawFd for Socket {
+    fn into_raw_fd(self) -> RawFd {
+        self.0.into_raw_fd()
+    }
+}
+
+impl FromRawFd for Socket {
+    unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
+        unsafe { Self(FromRawFd::from_raw_fd(raw_fd)) }
+    }
+}
+
+impl AsInner<Socket> for TcpListener {
+    #[inline]
+    fn as_inner(&self) -> &Socket {
+        &self.socket()
+    }
+}
diff --git a/library/std/src/sys/pal/windows/api.rs b/library/std/src/sys/pal/windows/api.rs
index 9e336ff2d47..ebe207fde93 100644
--- a/library/std/src/sys/pal/windows/api.rs
+++ b/library/std/src/sys/pal/windows/api.rs
@@ -30,7 +30,6 @@
 //! should go in sys/pal/windows/mod.rs rather than here. See `IoResult` as an example.
 
 use core::ffi::c_void;
-use core::ptr::addr_of;
 
 use super::c;
 
@@ -186,7 +185,7 @@ unsafe trait SizedSetFileInformation: Sized {
 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>()
+        (&raw const *self).cast::<c_void>()
     }
     fn size(&self) -> u32 {
         win32_size_of::<Self>()
diff --git a/library/std/src/sys/pal/windows/fs.rs b/library/std/src/sys/pal/windows/fs.rs
index be26356bb40..aab471e28ea 100644
--- a/library/std/src/sys/pal/windows/fs.rs
+++ b/library/std/src/sys/pal/windows/fs.rs
@@ -1,5 +1,3 @@
-use core::ptr::addr_of;
-
 use super::api::{self, WinError};
 use super::{IoResult, to_u16s};
 use crate::borrow::Cow;
@@ -325,7 +323,7 @@ impl File {
                     let result = c::SetFileInformationByHandle(
                         handle.as_raw_handle(),
                         c::FileEndOfFileInfo,
-                        ptr::addr_of!(eof).cast::<c_void>(),
+                        (&raw const eof).cast::<c_void>(),
                         mem::size_of::<c::FILE_END_OF_FILE_INFO>() as u32,
                     );
                     if result == 0 {
@@ -364,7 +362,7 @@ impl File {
                 cvt(c::GetFileInformationByHandleEx(
                     self.handle.as_raw_handle(),
                     c::FileAttributeTagInfo,
-                    ptr::addr_of_mut!(attr_tag).cast(),
+                    (&raw mut attr_tag).cast(),
                     mem::size_of::<c::FILE_ATTRIBUTE_TAG_INFO>().try_into().unwrap(),
                 ))?;
                 if attr_tag.FileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 {
@@ -396,7 +394,7 @@ impl File {
             cvt(c::GetFileInformationByHandleEx(
                 self.handle.as_raw_handle(),
                 c::FileBasicInfo,
-                core::ptr::addr_of_mut!(info) as *mut c_void,
+                (&raw mut info) as *mut c_void,
                 size as u32,
             ))?;
             let mut attr = FileAttr {
@@ -428,7 +426,7 @@ impl File {
             cvt(c::GetFileInformationByHandleEx(
                 self.handle.as_raw_handle(),
                 c::FileStandardInfo,
-                core::ptr::addr_of_mut!(info) as *mut c_void,
+                (&raw mut info) as *mut c_void,
                 size as u32,
             ))?;
             attr.file_size = info.AllocationSize as u64;
@@ -438,7 +436,7 @@ impl File {
                 cvt(c::GetFileInformationByHandleEx(
                     self.handle.as_raw_handle(),
                     c::FileAttributeTagInfo,
-                    ptr::addr_of_mut!(attr_tag).cast(),
+                    (&raw mut attr_tag).cast(),
                     mem::size_of::<c::FILE_ATTRIBUTE_TAG_INFO>().try_into().unwrap(),
                 ))?;
                 if attr_tag.FileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 {
@@ -545,22 +543,20 @@ impl File {
         unsafe {
             let (path_buffer, subst_off, subst_len, relative) = match (*buf).ReparseTag {
                 c::IO_REPARSE_TAG_SYMLINK => {
-                    let info: *mut c::SYMBOLIC_LINK_REPARSE_BUFFER =
-                        ptr::addr_of_mut!((*buf).rest).cast();
+                    let info: *mut c::SYMBOLIC_LINK_REPARSE_BUFFER = (&raw mut (*buf).rest).cast();
                     assert!(info.is_aligned());
                     (
-                        ptr::addr_of_mut!((*info).PathBuffer).cast::<u16>(),
+                        (&raw mut (*info).PathBuffer).cast::<u16>(),
                         (*info).SubstituteNameOffset / 2,
                         (*info).SubstituteNameLength / 2,
                         (*info).Flags & c::SYMLINK_FLAG_RELATIVE != 0,
                     )
                 }
                 c::IO_REPARSE_TAG_MOUNT_POINT => {
-                    let info: *mut c::MOUNT_POINT_REPARSE_BUFFER =
-                        ptr::addr_of_mut!((*buf).rest).cast();
+                    let info: *mut c::MOUNT_POINT_REPARSE_BUFFER = (&raw mut (*buf).rest).cast();
                     assert!(info.is_aligned());
                     (
-                        ptr::addr_of_mut!((*info).PathBuffer).cast::<u16>(),
+                        (&raw mut (*info).PathBuffer).cast::<u16>(),
                         (*info).SubstituteNameOffset / 2,
                         (*info).SubstituteNameLength / 2,
                         false,
@@ -643,7 +639,7 @@ impl File {
             cvt(c::GetFileInformationByHandleEx(
                 self.handle.as_raw_handle(),
                 c::FileBasicInfo,
-                core::ptr::addr_of_mut!(info) as *mut c_void,
+                (&raw mut info) as *mut c_void,
                 size as u32,
             ))?;
             Ok(info)
@@ -790,11 +786,11 @@ impl<'a> Iterator for DirBuffIter<'a> {
             // it does not seem that reality is so kind, and assuming this
             // caused crashes in some cases (https://github.com/rust-lang/rust/issues/104530)
             // presumably, this can be blamed on buggy filesystem drivers, but who knows.
-            let next_entry = ptr::addr_of!((*info).NextEntryOffset).read_unaligned() as usize;
-            let length = ptr::addr_of!((*info).FileNameLength).read_unaligned() as usize;
-            let attrs = ptr::addr_of!((*info).FileAttributes).read_unaligned();
+            let next_entry = (&raw const (*info).NextEntryOffset).read_unaligned() as usize;
+            let length = (&raw const (*info).FileNameLength).read_unaligned() as usize;
+            let attrs = (&raw const (*info).FileAttributes).read_unaligned();
             let name = from_maybe_unaligned(
-                ptr::addr_of!((*info).FileName).cast::<u16>(),
+                (&raw const (*info).FileName).cast::<u16>(),
                 length / size_of::<u16>(),
             );
             let is_directory = (attrs & c::FILE_ATTRIBUTE_DIRECTORY) != 0;
@@ -1326,7 +1322,7 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
             pfrom.as_ptr(),
             pto.as_ptr(),
             Some(callback),
-            core::ptr::addr_of_mut!(size) as *mut _,
+            (&raw mut size) as *mut _,
             ptr::null_mut(),
             0,
         )
@@ -1405,7 +1401,7 @@ pub fn junction_point(original: &Path, link: &Path) -> io::Result<()> {
         cvt(c::DeviceIoControl(
             d.as_raw_handle(),
             c::FSCTL_SET_REPARSE_POINT,
-            addr_of!(header).cast::<c_void>(),
+            (&raw const header).cast::<c_void>(),
             data_len as u32 + 8,
             ptr::null_mut(),
             0,
diff --git a/library/std/src/sys/pal/windows/futex.rs b/library/std/src/sys/pal/windows/futex.rs
index f16a9f534a3..4d6c4df9a5a 100644
--- a/library/std/src/sys/pal/windows/futex.rs
+++ b/library/std/src/sys/pal/windows/futex.rs
@@ -57,7 +57,7 @@ pub fn wait_on_address<W: Waitable>(
     unsafe {
         let addr = ptr::from_ref(address).cast::<c_void>();
         let size = mem::size_of::<W>();
-        let compare_addr = ptr::addr_of!(compare).cast::<c_void>();
+        let compare_addr = (&raw const compare).cast::<c_void>();
         let timeout = timeout.map(dur2timeout).unwrap_or(c::INFINITE);
         c::WaitOnAddress(addr, compare_addr, size, timeout) == c::TRUE
     }
diff --git a/library/std/src/sys/pal/windows/io.rs b/library/std/src/sys/pal/windows/io.rs
index 785a3f6768b..1e7d02908f6 100644
--- a/library/std/src/sys/pal/windows/io.rs
+++ b/library/std/src/sys/pal/windows/io.rs
@@ -122,7 +122,7 @@ fn msys_tty_on(handle: BorrowedHandle<'_>) -> bool {
         c::GetFileInformationByHandleEx(
             handle.as_raw_handle(),
             c::FileNameInfo,
-            core::ptr::addr_of_mut!(name_info) as *mut c_void,
+            (&raw mut name_info) as *mut c_void,
             size_of::<FILE_NAME_INFO>() as u32,
         )
     };
diff --git a/library/std/src/sys/pal/windows/net.rs b/library/std/src/sys/pal/windows/net.rs
index 61a4504cf65..fd62d1f407c 100644
--- a/library/std/src/sys/pal/windows/net.rs
+++ b/library/std/src/sys/pal/windows/net.rs
@@ -390,7 +390,7 @@ impl Socket {
                 buf.as_mut_ptr() as *mut _,
                 length,
                 flags,
-                core::ptr::addr_of_mut!(storage) as *mut _,
+                (&raw mut storage) as *mut _,
                 &mut addrlen,
             )
         };
diff --git a/library/std/src/sys/pal/windows/pipe.rs b/library/std/src/sys/pal/windows/pipe.rs
index d8200ef9ca4..a8f6617c9dc 100644
--- a/library/std/src/sys/pal/windows/pipe.rs
+++ b/library/std/src/sys/pal/windows/pipe.rs
@@ -375,7 +375,7 @@ impl AnonPipe {
         let mut overlapped: c::OVERLAPPED = unsafe { crate::mem::zeroed() };
         // `hEvent` is unused by `ReadFileEx` and `WriteFileEx`.
         // Therefore the documentation suggests using it to smuggle a pointer to the callback.
-        overlapped.hEvent = core::ptr::addr_of_mut!(async_result) as *mut _;
+        overlapped.hEvent = (&raw mut async_result) as *mut _;
 
         // Asynchronous read of the pipe.
         // If successful, `callback` will be called once it completes.
diff --git a/library/std/src/sys/pal/windows/process.rs b/library/std/src/sys/pal/windows/process.rs
index 93a6c45ce30..95b51e704f9 100644
--- a/library/std/src/sys/pal/windows/process.rs
+++ b/library/std/src/sys/pal/windows/process.rs
@@ -368,10 +368,10 @@ impl Command {
                 StartupInfo: si,
                 lpAttributeList: proc_thread_attribute_list.0.as_mut_ptr() as _,
             };
-            si_ptr = core::ptr::addr_of_mut!(si_ex) as _;
+            si_ptr = (&raw mut si_ex) as _;
         } else {
             si.cb = mem::size_of::<c::STARTUPINFOW>() as u32;
-            si_ptr = core::ptr::addr_of_mut!(si) as _;
+            si_ptr = (&raw mut si) as _;
         }
 
         unsafe {
@@ -953,7 +953,7 @@ fn make_proc_thread_attribute_list(
     // It's theoretically possible for the attribute count to exceed a u32 value.
     // Therefore, we ensure that we don't add more attributes than the buffer was initialized for.
     for (&attribute, value) in attributes.iter().take(attribute_count as usize) {
-        let value_ptr = core::ptr::addr_of!(*value.data) as _;
+        let value_ptr = (&raw const *value.data) as _;
         cvt(unsafe {
             c::UpdateProcThreadAttribute(
                 proc_thread_attribute_list.0.as_mut_ptr() as _,
diff --git a/library/std/src/sys/personality/mod.rs b/library/std/src/sys/personality/mod.rs
index 68085d026c4..9754e840d15 100644
--- a/library/std/src/sys/personality/mod.rs
+++ b/library/std/src/sys/personality/mod.rs
@@ -31,7 +31,7 @@ cfg_if::cfg_if! {
         target_os = "psp",
         target_os = "xous",
         target_os = "solid_asp3",
-        all(target_family = "unix", not(target_os = "espidf"), not(target_os = "l4re"), not(target_os = "rtems")),
+        all(target_family = "unix", not(target_os = "espidf"), not(target_os = "l4re"), not(target_os = "rtems"), not(target_os = "nuttx")),
         all(target_vendor = "fortanix", target_env = "sgx"),
     ))] {
         mod gcc;
diff --git a/library/std/src/sys/random/mod.rs b/library/std/src/sys/random/mod.rs
index 16fb8c64c9b..d625814d15b 100644
--- a/library/std/src/sys/random/mod.rs
+++ b/library/std/src/sys/random/mod.rs
@@ -42,6 +42,7 @@ cfg_if::cfg_if! {
         target_os = "hurd",
         target_os = "l4re",
         target_os = "nto",
+        target_os = "nuttx",
     ))] {
         mod unix_legacy;
         pub use unix_legacy::fill_bytes;
diff --git a/library/std/src/sys/sync/condvar/mod.rs b/library/std/src/sys/sync/condvar/mod.rs
index 6849cacf88e..d0c998a5597 100644
--- a/library/std/src/sys/sync/condvar/mod.rs
+++ b/library/std/src/sys/sync/condvar/mod.rs
@@ -12,7 +12,10 @@ cfg_if::cfg_if! {
     ))] {
         mod futex;
         pub use futex::Condvar;
-    } else if #[cfg(target_family = "unix")] {
+    } else if #[cfg(any(
+        target_family = "unix",
+        target_os = "teeos",
+    ))] {
         mod pthread;
         pub use pthread::Condvar;
     } else if #[cfg(all(target_os = "windows", target_vendor = "win7"))] {
@@ -24,9 +27,6 @@ cfg_if::cfg_if! {
     } else if #[cfg(target_os = "solid_asp3")] {
         mod itron;
         pub use itron::Condvar;
-    } else if #[cfg(target_os = "teeos")] {
-        mod teeos;
-        pub use teeos::Condvar;
     } else if #[cfg(target_os = "xous")] {
         mod xous;
         pub use xous::Condvar;
diff --git a/library/std/src/sys/sync/condvar/pthread.rs b/library/std/src/sys/sync/condvar/pthread.rs
index 5b5e7770b06..986cd0cb7d1 100644
--- a/library/std/src/sys/sync/condvar/pthread.rs
+++ b/library/std/src/sys/sync/condvar/pthread.rs
@@ -2,31 +2,25 @@ use crate::cell::UnsafeCell;
 use crate::ptr;
 use crate::sync::atomic::AtomicPtr;
 use crate::sync::atomic::Ordering::Relaxed;
-use crate::sys::sync::{Mutex, mutex};
+use crate::sys::sync::{Mutex, OnceBox};
 #[cfg(not(target_os = "nto"))]
 use crate::sys::time::TIMESPEC_MAX;
 #[cfg(target_os = "nto")]
 use crate::sys::time::TIMESPEC_MAX_CAPPED;
-use crate::sys_common::lazy_box::{LazyBox, LazyInit};
 use crate::time::Duration;
 
 struct AllocatedCondvar(UnsafeCell<libc::pthread_cond_t>);
 
 pub struct Condvar {
-    inner: LazyBox<AllocatedCondvar>,
+    inner: OnceBox<AllocatedCondvar>,
     mutex: AtomicPtr<libc::pthread_mutex_t>,
 }
 
-#[inline]
-fn raw(c: &Condvar) -> *mut libc::pthread_cond_t {
-    c.inner.0.get()
-}
-
 unsafe impl Send for AllocatedCondvar {}
 unsafe impl Sync for AllocatedCondvar {}
 
-impl LazyInit for AllocatedCondvar {
-    fn init() -> Box<Self> {
+impl AllocatedCondvar {
+    fn new() -> Box<Self> {
         let condvar = Box::new(AllocatedCondvar(UnsafeCell::new(libc::PTHREAD_COND_INITIALIZER)));
 
         cfg_if::cfg_if! {
@@ -37,7 +31,7 @@ impl LazyInit for AllocatedCondvar {
                 target_vendor = "apple",
             ))] {
                 // `pthread_condattr_setclock` is unfortunately not supported on these platforms.
-            } else if #[cfg(any(target_os = "espidf", target_os = "horizon"))] {
+            } else if #[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "teeos"))] {
                 // NOTE: ESP-IDF's PTHREAD_COND_INITIALIZER support is not released yet
                 // So on that platform, init() should always be called
                 // Moreover, that platform does not have pthread_condattr_setclock support,
@@ -82,7 +76,11 @@ impl Drop for AllocatedCondvar {
 
 impl Condvar {
     pub const fn new() -> Condvar {
-        Condvar { inner: LazyBox::new(), mutex: AtomicPtr::new(ptr::null_mut()) }
+        Condvar { inner: OnceBox::new(), mutex: AtomicPtr::new(ptr::null_mut()) }
+    }
+
+    fn get(&self) -> *mut libc::pthread_cond_t {
+        self.inner.get_or_init(AllocatedCondvar::new).0.get()
     }
 
     #[inline]
@@ -98,21 +96,21 @@ impl Condvar {
 
     #[inline]
     pub fn notify_one(&self) {
-        let r = unsafe { libc::pthread_cond_signal(raw(self)) };
+        let r = unsafe { libc::pthread_cond_signal(self.get()) };
         debug_assert_eq!(r, 0);
     }
 
     #[inline]
     pub fn notify_all(&self) {
-        let r = unsafe { libc::pthread_cond_broadcast(raw(self)) };
+        let r = unsafe { libc::pthread_cond_broadcast(self.get()) };
         debug_assert_eq!(r, 0);
     }
 
     #[inline]
     pub unsafe fn wait(&self, mutex: &Mutex) {
-        let mutex = mutex::raw(mutex);
+        let mutex = mutex.get_assert_locked();
         self.verify(mutex);
-        let r = libc::pthread_cond_wait(raw(self), mutex);
+        let r = libc::pthread_cond_wait(self.get(), mutex);
         debug_assert_eq!(r, 0);
     }
 
@@ -129,7 +127,7 @@ impl Condvar {
     pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool {
         use crate::sys::time::Timespec;
 
-        let mutex = mutex::raw(mutex);
+        let mutex = mutex.get_assert_locked();
         self.verify(mutex);
 
         #[cfg(not(target_os = "nto"))]
@@ -144,7 +142,7 @@ impl Condvar {
             .and_then(|t| t.to_timespec_capped())
             .unwrap_or(TIMESPEC_MAX_CAPPED);
 
-        let r = libc::pthread_cond_timedwait(raw(self), mutex, &timeout);
+        let r = libc::pthread_cond_timedwait(self.get(), mutex, &timeout);
         assert!(r == libc::ETIMEDOUT || r == 0);
         r == 0
     }
@@ -162,7 +160,7 @@ impl Condvar {
         use crate::sys::time::SystemTime;
         use crate::time::Instant;
 
-        let mutex = mutex::raw(mutex);
+        let mutex = mutex.get_assert_locked();
         self.verify(mutex);
 
         // OSX implementation of `pthread_cond_timedwait` is buggy
@@ -188,7 +186,7 @@ impl Condvar {
             .and_then(|t| t.to_timespec())
             .unwrap_or(TIMESPEC_MAX);
 
-        let r = libc::pthread_cond_timedwait(raw(self), mutex, &timeout);
+        let r = libc::pthread_cond_timedwait(self.get(), mutex, &timeout);
         debug_assert!(r == libc::ETIMEDOUT || r == 0);
 
         // ETIMEDOUT is not a totally reliable method of determining timeout due
diff --git a/library/std/src/sys/sync/condvar/sgx.rs b/library/std/src/sys/sync/condvar/sgx.rs
index ecb5872f60d..e60715e4b59 100644
--- a/library/std/src/sys/sync/condvar/sgx.rs
+++ b/library/std/src/sys/sync/condvar/sgx.rs
@@ -1,44 +1,39 @@
 use crate::sys::pal::waitqueue::{SpinMutex, WaitQueue, WaitVariable};
-use crate::sys::sync::Mutex;
-use crate::sys_common::lazy_box::{LazyBox, LazyInit};
+use crate::sys::sync::{Mutex, OnceBox};
 use crate::time::Duration;
 
-/// FIXME: `UnsafeList` is not movable.
-struct AllocatedCondvar(SpinMutex<WaitVariable<()>>);
-
 pub struct Condvar {
-    inner: LazyBox<AllocatedCondvar>,
-}
-
-impl LazyInit for AllocatedCondvar {
-    fn init() -> Box<Self> {
-        Box::new(AllocatedCondvar(SpinMutex::new(WaitVariable::new(()))))
-    }
+    // FIXME: `UnsafeList` is not movable.
+    inner: OnceBox<SpinMutex<WaitVariable<()>>>,
 }
 
 impl Condvar {
     pub const fn new() -> Condvar {
-        Condvar { inner: LazyBox::new() }
+        Condvar { inner: OnceBox::new() }
+    }
+
+    fn get(&self) -> &SpinMutex<WaitVariable<()>> {
+        self.inner.get_or_init(|| Box::new(SpinMutex::new(WaitVariable::new(()))))
     }
 
     #[inline]
     pub fn notify_one(&self) {
-        let _ = WaitQueue::notify_one(self.inner.0.lock());
+        let _ = WaitQueue::notify_one(self.get().lock());
     }
 
     #[inline]
     pub fn notify_all(&self) {
-        let _ = WaitQueue::notify_all(self.inner.0.lock());
+        let _ = WaitQueue::notify_all(self.get().lock());
     }
 
     pub unsafe fn wait(&self, mutex: &Mutex) {
-        let guard = self.inner.0.lock();
+        let guard = self.get().lock();
         WaitQueue::wait(guard, || unsafe { mutex.unlock() });
         mutex.lock()
     }
 
     pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool {
-        let success = WaitQueue::wait_timeout(&self.inner.0, dur, || unsafe { mutex.unlock() });
+        let success = WaitQueue::wait_timeout(self.get(), dur, || unsafe { mutex.unlock() });
         mutex.lock();
         success
     }
diff --git a/library/std/src/sys/sync/condvar/teeos.rs b/library/std/src/sys/sync/condvar/teeos.rs
deleted file mode 100644
index 943867cd761..00000000000
--- a/library/std/src/sys/sync/condvar/teeos.rs
+++ /dev/null
@@ -1,101 +0,0 @@
-use crate::cell::UnsafeCell;
-use crate::ptr;
-use crate::sync::atomic::AtomicPtr;
-use crate::sync::atomic::Ordering::Relaxed;
-use crate::sys::sync::mutex::{self, Mutex};
-use crate::sys::time::TIMESPEC_MAX;
-use crate::sys_common::lazy_box::{LazyBox, LazyInit};
-use crate::time::Duration;
-
-extern "C" {
-    pub fn pthread_cond_timedwait(
-        cond: *mut libc::pthread_cond_t,
-        lock: *mut libc::pthread_mutex_t,
-        adstime: *const libc::timespec,
-    ) -> libc::c_int;
-}
-
-struct AllocatedCondvar(UnsafeCell<libc::pthread_cond_t>);
-
-pub struct Condvar {
-    inner: LazyBox<AllocatedCondvar>,
-    mutex: AtomicPtr<libc::pthread_mutex_t>,
-}
-
-#[inline]
-fn raw(c: &Condvar) -> *mut libc::pthread_cond_t {
-    c.inner.0.get()
-}
-
-unsafe impl Send for AllocatedCondvar {}
-unsafe impl Sync for AllocatedCondvar {}
-
-impl LazyInit for AllocatedCondvar {
-    fn init() -> Box<Self> {
-        let condvar = Box::new(AllocatedCondvar(UnsafeCell::new(libc::PTHREAD_COND_INITIALIZER)));
-
-        let r = unsafe { libc::pthread_cond_init(condvar.0.get(), crate::ptr::null()) };
-        assert_eq!(r, 0);
-
-        condvar
-    }
-}
-
-impl Drop for AllocatedCondvar {
-    #[inline]
-    fn drop(&mut self) {
-        let r = unsafe { libc::pthread_cond_destroy(self.0.get()) };
-        debug_assert_eq!(r, 0);
-    }
-}
-
-impl Condvar {
-    pub const fn new() -> Condvar {
-        Condvar { inner: LazyBox::new(), mutex: AtomicPtr::new(ptr::null_mut()) }
-    }
-
-    #[inline]
-    fn verify(&self, mutex: *mut libc::pthread_mutex_t) {
-        match self.mutex.compare_exchange(ptr::null_mut(), mutex, Relaxed, Relaxed) {
-            Ok(_) => {}                // Stored the address
-            Err(n) if n == mutex => {} // Lost a race to store the same address
-            _ => panic!("attempted to use a condition variable with two mutexes"),
-        }
-    }
-
-    #[inline]
-    pub fn notify_one(&self) {
-        let r = unsafe { libc::pthread_cond_signal(raw(self)) };
-        debug_assert_eq!(r, 0);
-    }
-
-    #[inline]
-    pub fn notify_all(&self) {
-        let r = unsafe { libc::pthread_cond_broadcast(raw(self)) };
-        debug_assert_eq!(r, 0);
-    }
-
-    #[inline]
-    pub unsafe fn wait(&self, mutex: &Mutex) {
-        let mutex = unsafe { mutex::raw(mutex) };
-        self.verify(mutex);
-        let r = unsafe { libc::pthread_cond_wait(raw(self), mutex) };
-        debug_assert_eq!(r, 0);
-    }
-
-    pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool {
-        use crate::sys::time::Timespec;
-
-        let mutex = unsafe { mutex::raw(mutex) };
-        self.verify(mutex);
-
-        let timeout = Timespec::now(libc::CLOCK_MONOTONIC)
-            .checked_add_duration(&dur)
-            .and_then(|t| t.to_timespec())
-            .unwrap_or(TIMESPEC_MAX);
-
-        let r = unsafe { pthread_cond_timedwait(raw(self), mutex, &timeout) };
-        assert!(r == libc::ETIMEDOUT || r == 0);
-        r == 0
-    }
-}
diff --git a/library/std/src/sys/sync/mod.rs b/library/std/src/sys/sync/mod.rs
index 52fac5902a2..0691e967851 100644
--- a/library/std/src/sys/sync/mod.rs
+++ b/library/std/src/sys/sync/mod.rs
@@ -1,11 +1,14 @@
 mod condvar;
 mod mutex;
 mod once;
+mod once_box;
 mod rwlock;
 mod thread_parking;
 
 pub use condvar::Condvar;
 pub use mutex::Mutex;
 pub use once::{Once, OnceState};
+#[allow(unused)] // Only used on some platforms.
+use once_box::OnceBox;
 pub use rwlock::RwLock;
 pub use thread_parking::Parker;
diff --git a/library/std/src/sys/sync/mutex/mod.rs b/library/std/src/sys/sync/mutex/mod.rs
index 73d9bd273de..360df3fc4b5 100644
--- a/library/std/src/sys/sync/mutex/mod.rs
+++ b/library/std/src/sys/sync/mutex/mod.rs
@@ -19,7 +19,7 @@ cfg_if::cfg_if! {
         target_os = "teeos",
     ))] {
         mod pthread;
-        pub use pthread::{Mutex, raw};
+        pub use pthread::Mutex;
     } else if #[cfg(all(target_os = "windows", target_vendor = "win7"))] {
         mod windows7;
         pub use windows7::{Mutex, raw};
diff --git a/library/std/src/sys/sync/mutex/pthread.rs b/library/std/src/sys/sync/mutex/pthread.rs
index 1c407bc2537..87c95f45f96 100644
--- a/library/std/src/sys/sync/mutex/pthread.rs
+++ b/library/std/src/sys/sync/mutex/pthread.rs
@@ -2,24 +2,19 @@ use crate::cell::UnsafeCell;
 use crate::io::Error;
 use crate::mem::{MaybeUninit, forget};
 use crate::sys::cvt_nz;
-use crate::sys_common::lazy_box::{LazyBox, LazyInit};
+use crate::sys::sync::OnceBox;
 
 struct AllocatedMutex(UnsafeCell<libc::pthread_mutex_t>);
 
 pub struct Mutex {
-    inner: LazyBox<AllocatedMutex>,
-}
-
-#[inline]
-pub unsafe fn raw(m: &Mutex) -> *mut libc::pthread_mutex_t {
-    m.inner.0.get()
+    inner: OnceBox<AllocatedMutex>,
 }
 
 unsafe impl Send for AllocatedMutex {}
 unsafe impl Sync for AllocatedMutex {}
 
-impl LazyInit for AllocatedMutex {
-    fn init() -> Box<Self> {
+impl AllocatedMutex {
+    fn new() -> Box<Self> {
         let mutex = Box::new(AllocatedMutex(UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER)));
 
         // Issue #33770
@@ -60,24 +55,6 @@ impl LazyInit for AllocatedMutex {
 
         mutex
     }
-
-    fn destroy(mutex: Box<Self>) {
-        // We're not allowed to pthread_mutex_destroy a locked mutex,
-        // so check first if it's unlocked.
-        if unsafe { libc::pthread_mutex_trylock(mutex.0.get()) == 0 } {
-            unsafe { libc::pthread_mutex_unlock(mutex.0.get()) };
-            drop(mutex);
-        } else {
-            // The mutex is locked. This happens if a MutexGuard is leaked.
-            // In this case, we just leak the Mutex too.
-            forget(mutex);
-        }
-    }
-
-    fn cancel_init(_: Box<Self>) {
-        // In this case, we can just drop it without any checks,
-        // since it cannot have been locked yet.
-    }
 }
 
 impl Drop for AllocatedMutex {
@@ -99,11 +76,33 @@ impl Drop for AllocatedMutex {
 impl Mutex {
     #[inline]
     pub const fn new() -> Mutex {
-        Mutex { inner: LazyBox::new() }
+        Mutex { inner: OnceBox::new() }
+    }
+
+    /// Gets access to the pthread mutex under the assumption that the mutex is
+    /// locked.
+    ///
+    /// This allows skipping the initialization check, as the mutex can only be
+    /// locked if it is already initialized, and allows relaxing the ordering
+    /// on the pointer load, since the allocation cannot have been modified
+    /// since the `lock` and the lock must have occurred on the current thread.
+    ///
+    /// # Safety
+    /// Causes undefined behaviour if the mutex is not locked.
+    #[inline]
+    pub(crate) unsafe fn get_assert_locked(&self) -> *mut libc::pthread_mutex_t {
+        unsafe { self.inner.get_unchecked().0.get() }
     }
 
     #[inline]
-    pub unsafe fn lock(&self) {
+    fn get(&self) -> *mut libc::pthread_mutex_t {
+        // If initialization fails, the mutex is destroyed. This is always sound,
+        // however, as the mutex cannot have been locked yet.
+        self.inner.get_or_init(AllocatedMutex::new).0.get()
+    }
+
+    #[inline]
+    pub fn lock(&self) {
         #[cold]
         #[inline(never)]
         fn fail(r: i32) -> ! {
@@ -111,7 +110,7 @@ impl Mutex {
             panic!("failed to lock mutex: {error}");
         }
 
-        let r = libc::pthread_mutex_lock(raw(self));
+        let r = unsafe { libc::pthread_mutex_lock(self.get()) };
         // As we set the mutex type to `PTHREAD_MUTEX_NORMAL` above, we expect
         // the lock call to never fail. Unfortunately however, some platforms
         // (Solaris) do not conform to the standard, and instead always provide
@@ -126,13 +125,29 @@ impl Mutex {
 
     #[inline]
     pub unsafe fn unlock(&self) {
-        let r = libc::pthread_mutex_unlock(raw(self));
+        let r = libc::pthread_mutex_unlock(self.get_assert_locked());
         debug_assert_eq!(r, 0);
     }
 
     #[inline]
-    pub unsafe fn try_lock(&self) -> bool {
-        libc::pthread_mutex_trylock(raw(self)) == 0
+    pub fn try_lock(&self) -> bool {
+        unsafe { libc::pthread_mutex_trylock(self.get()) == 0 }
+    }
+}
+
+impl Drop for Mutex {
+    fn drop(&mut self) {
+        let Some(mutex) = self.inner.take() else { return };
+        // We're not allowed to pthread_mutex_destroy a locked mutex,
+        // so check first if it's unlocked.
+        if unsafe { libc::pthread_mutex_trylock(mutex.0.get()) == 0 } {
+            unsafe { libc::pthread_mutex_unlock(mutex.0.get()) };
+            drop(mutex);
+        } else {
+            // The mutex is locked. This happens if a MutexGuard is leaked.
+            // In this case, we just leak the Mutex too.
+            forget(mutex);
+        }
     }
 }
 
diff --git a/library/std/src/sys/sync/mutex/sgx.rs b/library/std/src/sys/sync/mutex/sgx.rs
index 65d1e880f7b..8529e857970 100644
--- a/library/std/src/sys/sync/mutex/sgx.rs
+++ b/library/std/src/sys/sync/mutex/sgx.rs
@@ -1,28 +1,24 @@
 use crate::sys::pal::waitqueue::{SpinMutex, WaitQueue, WaitVariable, try_lock_or_false};
-use crate::sys_common::lazy_box::{LazyBox, LazyInit};
-
-/// FIXME: `UnsafeList` is not movable.
-struct AllocatedMutex(SpinMutex<WaitVariable<bool>>);
+use crate::sys::sync::OnceBox;
 
 pub struct Mutex {
-    inner: LazyBox<AllocatedMutex>,
-}
-
-impl LazyInit for AllocatedMutex {
-    fn init() -> Box<Self> {
-        Box::new(AllocatedMutex(SpinMutex::new(WaitVariable::new(false))))
-    }
+    // FIXME: `UnsafeList` is not movable.
+    inner: OnceBox<SpinMutex<WaitVariable<bool>>>,
 }
 
 // Implementation according to “Operating Systems: Three Easy Pieces”, chapter 28
 impl Mutex {
     pub const fn new() -> Mutex {
-        Mutex { inner: LazyBox::new() }
+        Mutex { inner: OnceBox::new() }
+    }
+
+    fn get(&self) -> &SpinMutex<WaitVariable<bool>> {
+        self.inner.get_or_init(|| Box::new(SpinMutex::new(WaitVariable::new(false))))
     }
 
     #[inline]
     pub fn lock(&self) {
-        let mut guard = self.inner.0.lock();
+        let mut guard = self.get().lock();
         if *guard.lock_var() {
             // Another thread has the lock, wait
             WaitQueue::wait(guard, || {})
@@ -35,7 +31,9 @@ impl Mutex {
 
     #[inline]
     pub unsafe fn unlock(&self) {
-        let guard = self.inner.0.lock();
+        // SAFETY: the mutex was locked by the current thread, so it has been
+        // initialized already.
+        let guard = unsafe { self.inner.get_unchecked().lock() };
         if let Err(mut guard) = WaitQueue::notify_one(guard) {
             // No other waiters, unlock
             *guard.lock_var_mut() = false;
@@ -46,7 +44,7 @@ impl Mutex {
 
     #[inline]
     pub fn try_lock(&self) -> bool {
-        let mut guard = try_lock_or_false!(self.inner.0);
+        let mut guard = try_lock_or_false!(self.get());
         if *guard.lock_var() {
             // Another thread has the lock
             false
diff --git a/library/std/src/sys/sync/once_box.rs b/library/std/src/sys/sync/once_box.rs
new file mode 100644
index 00000000000..1422b5a1721
--- /dev/null
+++ b/library/std/src/sys/sync/once_box.rs
@@ -0,0 +1,82 @@
+//! A racily-initialized alternative to `OnceLock<Box<T>>`.
+//!
+//! This is used to implement synchronization primitives that need allocation,
+//! like the pthread versions.
+
+#![allow(dead_code)] // Only used on some platforms.
+
+use crate::mem::replace;
+use crate::ptr::null_mut;
+use crate::sync::atomic::AtomicPtr;
+use crate::sync::atomic::Ordering::{AcqRel, Acquire, Relaxed};
+
+pub(crate) struct OnceBox<T> {
+    ptr: AtomicPtr<T>,
+}
+
+impl<T> OnceBox<T> {
+    #[inline]
+    pub const fn new() -> Self {
+        Self { ptr: AtomicPtr::new(null_mut()) }
+    }
+
+    /// Gets access to the value, assuming it is already initialized and this
+    /// initialization has been observed by the current thread.
+    ///
+    /// Since all modifications to the pointer have already been observed, the
+    /// pointer load in this function can be performed with relaxed ordering,
+    /// potentially allowing the optimizer to turn code like this:
+    /// ```rust, ignore
+    /// once_box.get_or_init(|| Box::new(42));
+    /// unsafe { once_box.get_unchecked() }
+    /// ```
+    /// into
+    /// ```rust, ignore
+    /// once_box.get_or_init(|| Box::new(42))
+    /// ```
+    ///
+    /// # Safety
+    /// This causes undefined behaviour if the assumption above is violated.
+    #[inline]
+    pub unsafe fn get_unchecked(&self) -> &T {
+        unsafe { &*self.ptr.load(Relaxed) }
+    }
+
+    #[inline]
+    pub fn get_or_init(&self, f: impl FnOnce() -> Box<T>) -> &T {
+        let ptr = self.ptr.load(Acquire);
+        match unsafe { ptr.as_ref() } {
+            Some(val) => val,
+            None => self.initialize(f),
+        }
+    }
+
+    #[inline]
+    pub fn take(&mut self) -> Option<Box<T>> {
+        let ptr = replace(self.ptr.get_mut(), null_mut());
+        if !ptr.is_null() { Some(unsafe { Box::from_raw(ptr) }) } else { None }
+    }
+
+    #[cold]
+    fn initialize(&self, f: impl FnOnce() -> Box<T>) -> &T {
+        let new_ptr = Box::into_raw(f());
+        match self.ptr.compare_exchange(null_mut(), new_ptr, AcqRel, Acquire) {
+            Ok(_) => unsafe { &*new_ptr },
+            Err(ptr) => {
+                // Lost the race to another thread.
+                // Drop the value we created, and use the one from the other thread instead.
+                drop(unsafe { Box::from_raw(new_ptr) });
+                unsafe { &*ptr }
+            }
+        }
+    }
+}
+
+unsafe impl<T: Send> Send for OnceBox<T> {}
+unsafe impl<T: Send + Sync> Sync for OnceBox<T> {}
+
+impl<T> Drop for OnceBox<T> {
+    fn drop(&mut self) {
+        self.take();
+    }
+}
diff --git a/library/std/src/sys/sync/rwlock/queue.rs b/library/std/src/sys/sync/rwlock/queue.rs
index 0e658328c2e..733f51cae8c 100644
--- a/library/std/src/sys/sync/rwlock/queue.rs
+++ b/library/std/src/sys/sync/rwlock/queue.rs
@@ -113,7 +113,7 @@ use crate::mem;
 use crate::ptr::{self, NonNull, null_mut, without_provenance_mut};
 use crate::sync::atomic::Ordering::{AcqRel, Acquire, Relaxed, Release};
 use crate::sync::atomic::{AtomicBool, AtomicPtr};
-use crate::thread::{self, Thread};
+use crate::thread::{self, Thread, ThreadId};
 
 // Locking uses exponential backoff. `SPIN_COUNT` indicates how many times the
 // locking operation will be retried.
@@ -200,7 +200,9 @@ impl Node {
     fn prepare(&mut self) {
         // Fall back to creating an unnamed `Thread` handle to allow locking in
         // TLS destructors.
-        self.thread.get_or_init(|| thread::try_current().unwrap_or_else(Thread::new_unnamed));
+        self.thread.get_or_init(|| {
+            thread::try_current().unwrap_or_else(|| Thread::new_unnamed(ThreadId::new()))
+        });
         self.completed = AtomicBool::new(false);
     }
 
diff --git a/library/std/src/sys/sync/rwlock/teeos.rs b/library/std/src/sys/sync/rwlock/teeos.rs
index ef9b1ab5154..76343022383 100644
--- a/library/std/src/sys/sync/rwlock/teeos.rs
+++ b/library/std/src/sys/sync/rwlock/teeos.rs
@@ -14,22 +14,22 @@ impl RwLock {
 
     #[inline]
     pub fn read(&self) {
-        unsafe { self.inner.lock() };
+        self.inner.lock()
     }
 
     #[inline]
     pub fn try_read(&self) -> bool {
-        unsafe { self.inner.try_lock() }
+        self.inner.try_lock()
     }
 
     #[inline]
     pub fn write(&self) {
-        unsafe { self.inner.lock() };
+        self.inner.lock()
     }
 
     #[inline]
     pub unsafe fn try_write(&self) -> bool {
-        unsafe { self.inner.try_lock() }
+        self.inner.try_lock()
     }
 
     #[inline]
diff --git a/library/std/src/sys/sync/thread_parking/pthread.rs b/library/std/src/sys/sync/thread_parking/pthread.rs
index c64600e9e29..5f195d0bb0c 100644
--- a/library/std/src/sys/sync/thread_parking/pthread.rs
+++ b/library/std/src/sys/sync/thread_parking/pthread.rs
@@ -3,7 +3,6 @@
 use crate::cell::UnsafeCell;
 use crate::marker::PhantomPinned;
 use crate::pin::Pin;
-use crate::ptr::addr_of_mut;
 use crate::sync::atomic::AtomicUsize;
 use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release};
 #[cfg(not(target_os = "nto"))]
@@ -101,8 +100,8 @@ impl Parker {
         // This could lead to undefined behaviour when deadlocking. This is avoided
         // by not deadlocking. Note in particular the unlocking operation before any
         // panic, as code after the panic could try to park again.
-        addr_of_mut!((*parker).state).write(AtomicUsize::new(EMPTY));
-        addr_of_mut!((*parker).lock).write(UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER));
+        (&raw mut (*parker).state).write(AtomicUsize::new(EMPTY));
+        (&raw mut (*parker).lock).write(UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER));
 
         cfg_if::cfg_if! {
             if #[cfg(any(
@@ -112,9 +111,9 @@ impl Parker {
                 target_os = "vita",
                 target_vendor = "apple",
             ))] {
-                addr_of_mut!((*parker).cvar).write(UnsafeCell::new(libc::PTHREAD_COND_INITIALIZER));
+                (&raw mut (*parker).cvar).write(UnsafeCell::new(libc::PTHREAD_COND_INITIALIZER));
             } else if #[cfg(any(target_os = "espidf", target_os = "horizon"))] {
-                let r = libc::pthread_cond_init(addr_of_mut!((*parker).cvar).cast(), crate::ptr::null());
+                let r = libc::pthread_cond_init((&raw mut (*parker).cvar).cast(), crate::ptr::null());
                 assert_eq!(r, 0);
             } else {
                 use crate::mem::MaybeUninit;
@@ -123,7 +122,7 @@ impl Parker {
                 assert_eq!(r, 0);
                 let r = libc::pthread_condattr_setclock(attr.as_mut_ptr(), libc::CLOCK_MONOTONIC);
                 assert_eq!(r, 0);
-                let r = libc::pthread_cond_init(addr_of_mut!((*parker).cvar).cast(), attr.as_ptr());
+                let r = libc::pthread_cond_init((&raw mut (*parker).cvar).cast(), attr.as_ptr());
                 assert_eq!(r, 0);
                 let r = libc::pthread_condattr_destroy(attr.as_mut_ptr());
                 assert_eq!(r, 0);
diff --git a/library/std/src/sys/sync/thread_parking/windows7.rs b/library/std/src/sys/sync/thread_parking/windows7.rs
index cdd59757fe2..8f7e66c46ef 100644
--- a/library/std/src/sys/sync/thread_parking/windows7.rs
+++ b/library/std/src/sys/sync/thread_parking/windows7.rs
@@ -178,7 +178,7 @@ impl Parker {
     }
 
     fn ptr(&self) -> *const c_void {
-        core::ptr::addr_of!(self.state).cast::<c_void>()
+        (&raw const self.state).cast::<c_void>()
     }
 }
 
diff --git a/library/std/src/sys/thread_local/destructors/linux_like.rs b/library/std/src/sys/thread_local/destructors/linux_like.rs
index c381be0bf8c..f473dc4d79d 100644
--- a/library/std/src/sys/thread_local/destructors/linux_like.rs
+++ b/library/std/src/sys/thread_local/destructors/linux_like.rs
@@ -47,7 +47,7 @@ pub unsafe fn register(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
                     dtor,
                 ),
                 t.cast(),
-                core::ptr::addr_of!(__dso_handle) as *mut _,
+                (&raw const __dso_handle) as *mut _,
             );
         }
     } else {
diff --git a/library/std/src/sys/thread_local/guard/apple.rs b/library/std/src/sys/thread_local/guard/apple.rs
index 6c27f7ae35c..fa25b116622 100644
--- a/library/std/src/sys/thread_local/guard/apple.rs
+++ b/library/std/src/sys/thread_local/guard/apple.rs
@@ -26,6 +26,7 @@ pub fn enable() {
     unsafe extern "C" fn run_dtors(_: *mut u8) {
         unsafe {
             destructors::run();
+            crate::rt::thread_cleanup();
         }
     }
 }
diff --git a/library/std/src/sys/thread_local/guard/key.rs b/library/std/src/sys/thread_local/guard/key.rs
index 77575547c14..59581e6f281 100644
--- a/library/std/src/sys/thread_local/guard/key.rs
+++ b/library/std/src/sys/thread_local/guard/key.rs
@@ -3,10 +3,12 @@
 //! that will run all native TLS destructors in the destructor list.
 
 use crate::ptr;
-use crate::sys::thread_local::destructors;
 use crate::sys::thread_local::key::{LazyKey, set};
 
+#[cfg(target_thread_local)]
 pub fn enable() {
+    use crate::sys::thread_local::destructors;
+
     static DTORS: LazyKey = LazyKey::new(Some(run));
 
     // Setting the key value to something other than NULL will result in the
@@ -18,6 +20,41 @@ pub fn enable() {
     unsafe extern "C" fn run(_: *mut u8) {
         unsafe {
             destructors::run();
+            // On platforms with `__cxa_thread_atexit_impl`, `destructors::run`
+            // does nothing on newer systems as the TLS destructors are
+            // registered with the system. But because all of those platforms
+            // call the destructors of TLS keys after the registered ones, this
+            // function will still be run last (at the time of writing).
+            crate::rt::thread_cleanup();
+        }
+    }
+}
+
+/// On platforms with key-based TLS, the system runs the destructors for us.
+/// We still have to make sure that [`crate::rt::thread_cleanup`] is called,
+/// however. This is done by defering the execution of a TLS destructor to
+/// the next round of destruction inside the TLS destructors.
+#[cfg(not(target_thread_local))]
+pub fn enable() {
+    const DEFER: *mut u8 = ptr::without_provenance_mut(1);
+    const RUN: *mut u8 = ptr::without_provenance_mut(2);
+
+    static CLEANUP: LazyKey = LazyKey::new(Some(run));
+
+    unsafe { set(CLEANUP.force(), DEFER) }
+
+    unsafe extern "C" fn run(state: *mut u8) {
+        if state == DEFER {
+            // Make sure that this function is run again in the next round of
+            // TLS destruction. If there is no futher round, there will be leaks,
+            // but that's okay, `thread_cleanup` is not guaranteed to be called.
+            unsafe { set(CLEANUP.force(), RUN) }
+        } else {
+            debug_assert_eq!(state, RUN);
+            // If the state is still RUN in the next round of TLS destruction,
+            // it means that no other TLS destructors defined by this runtime
+            // have been run, as they would have set the state to DEFER.
+            crate::rt::thread_cleanup();
         }
     }
 }
diff --git a/library/std/src/sys/thread_local/guard/solid.rs b/library/std/src/sys/thread_local/guard/solid.rs
index 054b2d561c8..3bcd46c481d 100644
--- a/library/std/src/sys/thread_local/guard/solid.rs
+++ b/library/std/src/sys/thread_local/guard/solid.rs
@@ -19,6 +19,9 @@ pub fn enable() {
     }
 
     unsafe extern "C" fn tls_dtor(_unused: *mut u8) {
-        unsafe { destructors::run() };
+        unsafe {
+            destructors::run();
+            crate::rt::thread_cleanup();
+        }
     }
 }
diff --git a/library/std/src/sys/thread_local/guard/windows.rs b/library/std/src/sys/thread_local/guard/windows.rs
index bf94f7d6e3d..7ee8e695c75 100644
--- a/library/std/src/sys/thread_local/guard/windows.rs
+++ b/library/std/src/sys/thread_local/guard/windows.rs
@@ -80,13 +80,13 @@ pub static CALLBACK: unsafe extern "system" fn(*mut c_void, u32, *mut c_void) =
 
 unsafe extern "system" fn tls_callback(_h: *mut c_void, dw_reason: u32, _pv: *mut c_void) {
     if dw_reason == c::DLL_THREAD_DETACH || dw_reason == c::DLL_PROCESS_DETACH {
-        #[cfg(target_thread_local)]
         unsafe {
+            #[cfg(target_thread_local)]
             super::super::destructors::run();
-        }
-        #[cfg(not(target_thread_local))]
-        unsafe {
+            #[cfg(not(target_thread_local))]
             super::super::key::run_dtors();
+
+            crate::rt::thread_cleanup();
         }
     }
 }
diff --git a/library/std/src/sys/thread_local/key/xous.rs b/library/std/src/sys/thread_local/key/xous.rs
index 295fb848b30..2ab4bba7d8e 100644
--- a/library/std/src/sys/thread_local/key/xous.rs
+++ b/library/std/src/sys/thread_local/key/xous.rs
@@ -212,4 +212,6 @@ unsafe fn run_dtors() {
             unsafe { cur = (*cur).next };
         }
     }
+
+    crate::rt::thread_cleanup();
 }
diff --git a/library/std/src/sys/thread_local/mod.rs b/library/std/src/sys/thread_local/mod.rs
index 3d1b91a7ea0..31d3b439060 100644
--- a/library/std/src/sys/thread_local/mod.rs
+++ b/library/std/src/sys/thread_local/mod.rs
@@ -31,12 +31,15 @@ cfg_if::cfg_if! {
     ))] {
         mod statik;
         pub use statik::{EagerStorage, LazyStorage, thread_local_inner};
+        pub(crate) use statik::{LocalPointer, local_pointer};
     } else if #[cfg(target_thread_local)] {
         mod native;
         pub use native::{EagerStorage, LazyStorage, thread_local_inner};
+        pub(crate) use native::{LocalPointer, local_pointer};
     } else {
         mod os;
         pub use os::{Storage, thread_local_inner};
+        pub(crate) use os::{LocalPointer, local_pointer};
     }
 }
 
@@ -72,36 +75,47 @@ pub(crate) mod destructors {
 }
 
 /// This module provides a way to schedule the execution of the destructor list
-/// on systems without a per-variable destructor system.
-mod guard {
+/// and the [runtime cleanup](crate::rt::thread_cleanup) function. Calling `enable`
+/// should ensure that these functions are called at the right times.
+pub(crate) mod guard {
     cfg_if::cfg_if! {
         if #[cfg(all(target_thread_local, target_vendor = "apple"))] {
             mod apple;
-            pub(super) use apple::enable;
+            pub(crate) use apple::enable;
         } else if #[cfg(target_os = "windows")] {
             mod windows;
-            pub(super) use windows::enable;
+            pub(crate) use windows::enable;
         } else if #[cfg(any(
-            all(target_family = "wasm", target_feature = "atomics"),
+            target_family = "wasm",
+            target_os = "uefi",
+            target_os = "zkvm",
         ))] {
-            pub(super) fn enable() {
-                // FIXME: Right now there is no concept of "thread exit", but
-                // this is likely going to show up at some point in the form of
-                // an exported symbol that the wasm runtime is going to be
-                // expected to call. For now we just leak everything, but if
-                // such a function starts to exist it will probably need to
-                // iterate the destructor list with this function:
+            pub(crate) fn enable() {
+                // FIXME: Right now there is no concept of "thread exit" on
+                // wasm, but this is likely going to show up at some point in
+                // the form of an exported symbol that the wasm runtime is going
+                // to be expected to call. For now we just leak everything, but
+                // if such a function starts to exist it will probably need to
+                // iterate the destructor list with these functions:
+                #[cfg(all(target_family = "wasm", target_feature = "atomics"))]
                 #[allow(unused)]
                 use super::destructors::run;
+                #[allow(unused)]
+                use crate::rt::thread_cleanup;
             }
-        } else if #[cfg(target_os = "hermit")] {
-            pub(super) fn enable() {}
+        } else if #[cfg(any(
+            target_os = "hermit",
+            target_os = "xous",
+        ))] {
+            // `std` is the only runtime, so it just calls the destructor functions
+            // itself when the time comes.
+            pub(crate) fn enable() {}
         } else if #[cfg(target_os = "solid_asp3")] {
             mod solid;
-            pub(super) use solid::enable;
-        } else if #[cfg(all(target_thread_local, not(target_family = "wasm")))] {
+            pub(crate) use solid::enable;
+        } else {
             mod key;
-            pub(super) use key::enable;
+            pub(crate) use key::enable;
         }
     }
 }
diff --git a/library/std/src/sys/thread_local/native/mod.rs b/library/std/src/sys/thread_local/native/mod.rs
index 1cc45fe892d..f498dee0899 100644
--- a/library/std/src/sys/thread_local/native/mod.rs
+++ b/library/std/src/sys/thread_local/native/mod.rs
@@ -29,6 +29,9 @@
 //! eliminates the `Destroyed` state for these values, which can allow more niche
 //! optimizations to occur for the `State` enum. For `Drop` types, `()` is used.
 
+use crate::cell::Cell;
+use crate::ptr;
+
 mod eager;
 mod lazy;
 
@@ -107,3 +110,31 @@ pub macro thread_local_inner {
             $crate::thread::local_impl::thread_local_inner!(@key $t, $($init)*);
     },
 }
+
+#[rustc_macro_transparency = "semitransparent"]
+pub(crate) macro local_pointer {
+    () => {},
+    ($vis:vis static $name:ident; $($rest:tt)*) => {
+        #[thread_local]
+        $vis static $name: $crate::sys::thread_local::LocalPointer = $crate::sys::thread_local::LocalPointer::__new();
+        $crate::sys::thread_local::local_pointer! { $($rest)* }
+    },
+}
+
+pub(crate) struct LocalPointer {
+    p: Cell<*mut ()>,
+}
+
+impl LocalPointer {
+    pub const fn __new() -> LocalPointer {
+        LocalPointer { p: Cell::new(ptr::null_mut()) }
+    }
+
+    pub fn get(&self) -> *mut () {
+        self.p.get()
+    }
+
+    pub fn set(&self, p: *mut ()) {
+        self.p.set(p)
+    }
+}
diff --git a/library/std/src/sys/thread_local/os.rs b/library/std/src/sys/thread_local/os.rs
index f09c396ef0f..26ce3322a16 100644
--- a/library/std/src/sys/thread_local/os.rs
+++ b/library/std/src/sys/thread_local/os.rs
@@ -1,8 +1,8 @@
-use super::abort_on_dtor_unwind;
+use super::key::{Key, LazyKey, get, set};
+use super::{abort_on_dtor_unwind, guard};
 use crate::cell::Cell;
 use crate::marker::PhantomData;
 use crate::ptr;
-use crate::sys::thread_local::key::{Key, LazyKey, get, set};
 
 #[doc(hidden)]
 #[allow_internal_unstable(thread_local_internals)]
@@ -138,5 +138,35 @@ unsafe extern "C" fn destroy_value<T: 'static>(ptr: *mut u8) {
         drop(ptr);
         // SAFETY: `key` is the TLS key `ptr` was stored under.
         unsafe { set(key, ptr::null_mut()) };
+        // Make sure that the runtime cleanup will be performed
+        // after the next round of TLS destruction.
+        guard::enable();
     });
 }
+
+#[rustc_macro_transparency = "semitransparent"]
+pub(crate) macro local_pointer {
+    () => {},
+    ($vis:vis static $name:ident; $($rest:tt)*) => {
+        $vis static $name: $crate::sys::thread_local::LocalPointer = $crate::sys::thread_local::LocalPointer::__new();
+        $crate::sys::thread_local::local_pointer! { $($rest)* }
+    },
+}
+
+pub(crate) struct LocalPointer {
+    key: LazyKey,
+}
+
+impl LocalPointer {
+    pub const fn __new() -> LocalPointer {
+        LocalPointer { key: LazyKey::new(None) }
+    }
+
+    pub fn get(&'static self) -> *mut () {
+        unsafe { get(self.key.force()) as *mut () }
+    }
+
+    pub fn set(&'static self, p: *mut ()) {
+        unsafe { set(self.key.force(), p as *mut u8) }
+    }
+}
diff --git a/library/std/src/sys/thread_local/statik.rs b/library/std/src/sys/thread_local/statik.rs
index a3451ab74e0..ba94caa6690 100644
--- a/library/std/src/sys/thread_local/statik.rs
+++ b/library/std/src/sys/thread_local/statik.rs
@@ -1,7 +1,8 @@
 //! On some targets like wasm there's no threads, so no need to generate
 //! thread locals and we can instead just use plain statics!
 
-use crate::cell::UnsafeCell;
+use crate::cell::{Cell, UnsafeCell};
+use crate::ptr;
 
 #[doc(hidden)]
 #[allow_internal_unstable(thread_local_internals)]
@@ -93,3 +94,33 @@ impl<T> LazyStorage<T> {
 
 // SAFETY: the target doesn't have threads.
 unsafe impl<T> Sync for LazyStorage<T> {}
+
+#[rustc_macro_transparency = "semitransparent"]
+pub(crate) macro local_pointer {
+    () => {},
+    ($vis:vis static $name:ident; $($rest:tt)*) => {
+        $vis static $name: $crate::sys::thread_local::LocalPointer = $crate::sys::thread_local::LocalPointer::__new();
+        $crate::sys::thread_local::local_pointer! { $($rest)* }
+    },
+}
+
+pub(crate) struct LocalPointer {
+    p: Cell<*mut ()>,
+}
+
+impl LocalPointer {
+    pub const fn __new() -> LocalPointer {
+        LocalPointer { p: Cell::new(ptr::null_mut()) }
+    }
+
+    pub fn get(&self) -> *mut () {
+        self.p.get()
+    }
+
+    pub fn set(&self, p: *mut ()) {
+        self.p.set(p)
+    }
+}
+
+// SAFETY: the target doesn't have threads.
+unsafe impl Sync for LocalPointer {}