diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs
index 9625984195bd2..94c70c4f267b1 100644
--- a/library/std/src/io/mod.rs
+++ b/library/std/src/io/mod.rs
@@ -293,6 +293,10 @@ mod util;
 
 const DEFAULT_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE;
 
+pub(crate) fn cleanup() {
+    stdio::cleanup()
+}
+
 struct Guard<'a> {
     buf: &'a mut Vec<u8>,
     len: usize,
diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs
index c2e0b24ba8327..2b0d2b7e0be23 100644
--- a/library/std/src/io/stdio.rs
+++ b/library/std/src/io/stdio.rs
@@ -13,7 +13,6 @@ use crate::pin::Pin;
 use crate::sync::atomic::{AtomicBool, Ordering};
 use crate::sync::{Arc, Mutex, MutexGuard};
 use crate::sys::stdio;
-use crate::sys_common;
 use crate::sys_common::remutex::{ReentrantMutex, ReentrantMutexGuard};
 
 type LocalStream = Arc<Mutex<Vec<u8>>>;
@@ -508,6 +507,8 @@ pub struct StdoutLock<'a> {
     inner: ReentrantMutexGuard<'a, RefCell<LineWriter<StdoutRaw>>>,
 }
 
+static STDOUT: SyncOnceCell<ReentrantMutex<RefCell<LineWriter<StdoutRaw>>>> = SyncOnceCell::new();
+
 /// Constructs a new handle to the standard output of the current process.
 ///
 /// Each handle returned is a reference to a shared global buffer whose access
@@ -549,34 +550,28 @@ pub struct StdoutLock<'a> {
 /// ```
 #[stable(feature = "rust1", since = "1.0.0")]
 pub fn stdout() -> Stdout {
-    static INSTANCE: SyncOnceCell<ReentrantMutex<RefCell<LineWriter<StdoutRaw>>>> =
-        SyncOnceCell::new();
-
-    fn cleanup() {
-        if let Some(instance) = INSTANCE.get() {
-            // Flush the data and disable buffering during shutdown
-            // by replacing the line writer by one with zero
-            // buffering capacity.
-            // We use try_lock() instead of lock(), because someone
-            // might have leaked a StdoutLock, which would
-            // otherwise cause a deadlock here.
-            if let Some(lock) = Pin::static_ref(instance).try_lock() {
-                *lock.borrow_mut() = LineWriter::with_capacity(0, stdout_raw());
-            }
-        }
-    }
-
     Stdout {
-        inner: Pin::static_ref(&INSTANCE).get_or_init_pin(
-            || unsafe {
-                let _ = sys_common::at_exit(cleanup);
-                ReentrantMutex::new(RefCell::new(LineWriter::new(stdout_raw())))
-            },
+        inner: Pin::static_ref(&STDOUT).get_or_init_pin(
+            || unsafe { ReentrantMutex::new(RefCell::new(LineWriter::new(stdout_raw()))) },
             |mutex| unsafe { mutex.init() },
         ),
     }
 }
 
+pub fn cleanup() {
+    if let Some(instance) = STDOUT.get() {
+        // Flush the data and disable buffering during shutdown
+        // by replacing the line writer by one with zero
+        // buffering capacity.
+        // We use try_lock() instead of lock(), because someone
+        // might have leaked a StdoutLock, which would
+        // otherwise cause a deadlock here.
+        if let Some(lock) = Pin::static_ref(instance).try_lock() {
+            *lock.borrow_mut() = LineWriter::with_capacity(0, stdout_raw());
+        }
+    }
+}
+
 impl Stdout {
     /// Locks this handle to the standard output stream, returning a writable
     /// guard.
diff --git a/library/std/src/process.rs b/library/std/src/process.rs
index 931b3b600a302..b45c620fd0b06 100644
--- a/library/std/src/process.rs
+++ b/library/std/src/process.rs
@@ -1749,7 +1749,7 @@ impl Child {
 /// [platform-specific behavior]: #platform-specific-behavior
 #[stable(feature = "rust1", since = "1.0.0")]
 pub fn exit(code: i32) -> ! {
-    crate::sys_common::cleanup();
+    crate::sys_common::rt::cleanup();
     crate::sys::os::exit(code)
 }
 
diff --git a/library/std/src/rt.rs b/library/std/src/rt.rs
index 45af9f68a0f6b..1e19aff51f8d6 100644
--- a/library/std/src/rt.rs
+++ b/library/std/src/rt.rs
@@ -26,33 +26,16 @@ fn lang_start_internal(
     argv: *const *const u8,
 ) -> isize {
     use crate::panic;
-    use crate::sys;
     use crate::sys_common;
-    use crate::sys_common::thread_info;
-    use crate::thread::Thread;
 
-    sys::init();
+    // SAFETY: Only called once during runtime initialization.
+    unsafe { sys_common::rt::init(argc, argv) };
 
-    unsafe {
-        let main_guard = sys::thread::guard::init();
-        sys::stack_overflow::init();
+    let exit_code = panic::catch_unwind(main);
 
-        // Next, set up the current Thread with the guard information we just
-        // created. Note that this isn't necessary in general for new threads,
-        // but we just do this to name the main thread and to give it correct
-        // info about the stack bounds.
-        let thread = Thread::new(Some("main".to_owned()));
-        thread_info::set(main_guard, thread);
+    sys_common::rt::cleanup();
 
-        // Store our args if necessary in a squirreled away location
-        sys::args::init(argc, argv);
-
-        // Let's run some code!
-        let exit_code = panic::catch_unwind(main);
-
-        sys_common::cleanup();
-        exit_code.unwrap_or(101) as isize
-    }
+    exit_code.unwrap_or(101) as isize
 }
 
 #[cfg(not(test))]
diff --git a/library/std/src/sys/hermit/mod.rs b/library/std/src/sys/hermit/mod.rs
index 56497162c0333..a70d1db7ca672 100644
--- a/library/std/src/sys/hermit/mod.rs
+++ b/library/std/src/sys/hermit/mod.rs
@@ -37,7 +37,6 @@ pub mod pipe;
 #[path = "../unsupported/process.rs"]
 pub mod process;
 pub mod rwlock;
-pub mod stack_overflow;
 pub mod stdio;
 pub mod thread;
 pub mod thread_local_dtor;
@@ -96,9 +95,17 @@ pub extern "C" fn __rust_abort() {
     abort_internal();
 }
 
-#[cfg(not(test))]
-pub fn init() {
+// SAFETY: must be called only once during runtime initialization.
+// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
+pub unsafe fn init(argc: isize, argv: *const *const u8) {
     let _ = net::init();
+    args::init(argc, argv);
+}
+
+// SAFETY: must be called only once during runtime cleanup.
+// NOTE: this is not guaranteed to run, for example when the program aborts.
+pub unsafe fn cleanup() {
+    args::cleanup();
 }
 
 #[cfg(not(test))]
diff --git a/library/std/src/sys/hermit/stack_overflow.rs b/library/std/src/sys/hermit/stack_overflow.rs
deleted file mode 100644
index 121fe42011da5..0000000000000
--- a/library/std/src/sys/hermit/stack_overflow.rs
+++ /dev/null
@@ -1,5 +0,0 @@
-#[inline]
-pub unsafe fn init() {}
-
-#[inline]
-pub unsafe fn cleanup() {}
diff --git a/library/std/src/sys/sgx/args.rs b/library/std/src/sys/sgx/args.rs
index 2d2e692ec7d35..7f484b6d136fc 100644
--- a/library/std/src/sys/sgx/args.rs
+++ b/library/std/src/sys/sgx/args.rs
@@ -22,8 +22,6 @@ pub unsafe fn init(argc: isize, argv: *const *const u8) {
     }
 }
 
-pub unsafe fn cleanup() {}
-
 pub fn args() -> Args {
     let args = unsafe { (ARGS.load(Ordering::Relaxed) as *const ArgsStore).as_ref() };
     if let Some(args) = args { Args(args.iter()) } else { Args([].iter()) }
diff --git a/library/std/src/sys/sgx/mod.rs b/library/std/src/sys/sgx/mod.rs
index d6a5683073309..059d6cb5ba131 100644
--- a/library/std/src/sys/sgx/mod.rs
+++ b/library/std/src/sys/sgx/mod.rs
@@ -32,7 +32,6 @@ pub mod pipe;
 #[path = "../unsupported/process.rs"]
 pub mod process;
 pub mod rwlock;
-pub mod stack_overflow;
 pub mod stdio;
 pub mod thread;
 pub mod thread_local_key;
@@ -40,8 +39,17 @@ pub mod time;
 
 pub use crate::sys_common::os_str_bytes as os_str;
 
-#[cfg(not(test))]
-pub fn init() {}
+// SAFETY: must be called only once during runtime initialization.
+// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
+pub unsafe fn init(argc: isize, argv: *const *const u8) {
+    unsafe {
+        args::init(argc, argv);
+    }
+}
+
+// SAFETY: must be called only once during runtime cleanup.
+// NOTE: this is not guaranteed to run, for example when the program aborts.
+pub unsafe fn cleanup() {}
 
 /// This function is used to implement functionality that simply doesn't exist.
 /// Programs relying on this functionality will need to deal with the error.
diff --git a/library/std/src/sys/sgx/stack_overflow.rs b/library/std/src/sys/sgx/stack_overflow.rs
deleted file mode 100644
index b96652a8330e9..0000000000000
--- a/library/std/src/sys/sgx/stack_overflow.rs
+++ /dev/null
@@ -1,4 +0,0 @@
-#[cfg_attr(test, allow(dead_code))]
-pub unsafe fn init() {}
-
-pub unsafe fn cleanup() {}
diff --git a/library/std/src/sys/unix/mod.rs b/library/std/src/sys/unix/mod.rs
index 1316835a89d12..a0ee69c2f72dd 100644
--- a/library/std/src/sys/unix/mod.rs
+++ b/library/std/src/sys/unix/mod.rs
@@ -44,14 +44,13 @@ pub mod time;
 
 pub use crate::sys_common::os_str_bytes as os_str;
 
-#[cfg(not(test))]
-pub fn init() {
+// SAFETY: must be called only once during runtime initialization.
+// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
+pub unsafe fn init(argc: isize, argv: *const *const u8) {
     // The standard streams might be closed on application startup. To prevent
     // std::io::{stdin, stdout,stderr} objects from using other unrelated file
     // resources opened later, we reopen standards streams when they are closed.
-    unsafe {
-        sanitize_standard_fds();
-    }
+    sanitize_standard_fds();
 
     // By default, some platforms will send a *signal* when an EPIPE error
     // would otherwise be delivered. This runtime doesn't install a SIGPIPE
@@ -60,26 +59,24 @@ pub fn init() {
     //
     // Hence, we set SIGPIPE to ignore when the program starts up in order
     // to prevent this problem.
-    unsafe {
-        reset_sigpipe();
-    }
+    reset_sigpipe();
+
+    stack_overflow::init();
+    args::init(argc, argv);
 
-    cfg_if::cfg_if! {
-        if #[cfg(miri)] {
-            // The standard fds are always available in Miri.
-            unsafe fn sanitize_standard_fds() {}
-        } else if #[cfg(not(any(
-            target_os = "emscripten",
-            target_os = "fuchsia",
-            target_os = "vxworks",
-            // The poll on Darwin doesn't set POLLNVAL for closed fds.
-            target_os = "macos",
-            target_os = "ios",
-            target_os = "redox",
-        )))] {
-            // In the case when all file descriptors are open, the poll has been
-            // observed to perform better than fcntl (on GNU/Linux).
-            unsafe fn sanitize_standard_fds() {
+    unsafe fn sanitize_standard_fds() {
+        #[cfg(not(miri))]
+        // The standard fds are always available in Miri.
+        cfg_if::cfg_if! {
+            if #[cfg(not(any(
+                target_os = "emscripten",
+                target_os = "fuchsia",
+                target_os = "vxworks",
+                // The poll on Darwin doesn't set POLLNVAL for closed fds.
+                target_os = "macos",
+                target_os = "ios",
+                target_os = "redox",
+            )))] {
                 use crate::sys::os::errno;
                 let pfds: &mut [_] = &mut [
                     libc::pollfd { fd: 0, events: 0, revents: 0 },
@@ -104,9 +101,7 @@ pub fn init() {
                         libc::abort();
                     }
                 }
-            }
-        } else if #[cfg(any(target_os = "macos", target_os = "ios", target_os = "redox"))] {
-            unsafe fn sanitize_standard_fds() {
+            } else if #[cfg(any(target_os = "macos", target_os = "ios", target_os = "redox"))] {
                 use crate::sys::os::errno;
                 for fd in 0..3 {
                     if libc::fcntl(fd, libc::F_GETFD) == -1 && errno() == libc::EBADF {
@@ -116,17 +111,20 @@ pub fn init() {
                     }
                 }
             }
-        } else {
-            unsafe fn sanitize_standard_fds() {}
         }
     }
 
-    #[cfg(not(any(target_os = "emscripten", target_os = "fuchsia")))]
     unsafe fn reset_sigpipe() {
+        #[cfg(not(any(target_os = "emscripten", target_os = "fuchsia")))]
         assert!(signal(libc::SIGPIPE, libc::SIG_IGN) != libc::SIG_ERR);
     }
-    #[cfg(any(target_os = "emscripten", target_os = "fuchsia"))]
-    unsafe fn reset_sigpipe() {}
+}
+
+// SAFETY: must be called only once during runtime cleanup.
+// NOTE: this is not guaranteed to run, for example when the program aborts.
+pub unsafe fn cleanup() {
+    args::cleanup();
+    stack_overflow::cleanup();
 }
 
 #[cfg(target_os = "android")]
diff --git a/library/std/src/sys/unsupported/args.rs b/library/std/src/sys/unsupported/args.rs
index 71d0c5fa13e18..cdb474b5b1556 100644
--- a/library/std/src/sys/unsupported/args.rs
+++ b/library/std/src/sys/unsupported/args.rs
@@ -1,8 +1,5 @@
 use crate::ffi::OsString;
 
-pub unsafe fn init(_argc: isize, _argv: *const *const u8) {}
-pub unsafe fn cleanup() {}
-
 pub struct Args {}
 
 pub fn args() -> Args {
diff --git a/library/std/src/sys/unsupported/common.rs b/library/std/src/sys/unsupported/common.rs
index 0ef84c84ee877..6e72a7c632ed0 100644
--- a/library/std/src/sys/unsupported/common.rs
+++ b/library/std/src/sys/unsupported/common.rs
@@ -10,8 +10,13 @@ pub use crate::sys_common::os_str_bytes as os_str;
 // spec definition?
 use crate::os::raw::c_char;
 
-#[cfg(not(test))]
-pub fn init() {}
+// SAFETY: must be called only once during runtime initialization.
+// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
+pub unsafe fn init(_argc: isize, _argv: *const *const u8) {}
+
+// SAFETY: must be called only once during runtime cleanup.
+// NOTE: this is not guaranteed to run, for example when the program aborts.
+pub unsafe fn cleanup() {}
 
 pub fn unsupported<T>() -> std_io::Result<T> {
     Err(unsupported_err())
diff --git a/library/std/src/sys/unsupported/mod.rs b/library/std/src/sys/unsupported/mod.rs
index d9efdec33d937..32ca68ef15b5b 100644
--- a/library/std/src/sys/unsupported/mod.rs
+++ b/library/std/src/sys/unsupported/mod.rs
@@ -15,7 +15,6 @@ pub mod path;
 pub mod pipe;
 pub mod process;
 pub mod rwlock;
-pub mod stack_overflow;
 pub mod stdio;
 pub mod thread;
 #[cfg(target_thread_local)]
diff --git a/library/std/src/sys/unsupported/stack_overflow.rs b/library/std/src/sys/unsupported/stack_overflow.rs
deleted file mode 100644
index 32555394cd5a5..0000000000000
--- a/library/std/src/sys/unsupported/stack_overflow.rs
+++ /dev/null
@@ -1,3 +0,0 @@
-pub unsafe fn init() {}
-
-pub unsafe fn cleanup() {}
diff --git a/library/std/src/sys/wasi/args.rs b/library/std/src/sys/wasi/args.rs
index 9a27218e1fb70..004d47960f7e0 100644
--- a/library/std/src/sys/wasi/args.rs
+++ b/library/std/src/sys/wasi/args.rs
@@ -5,10 +5,6 @@ use crate::marker::PhantomData;
 use crate::os::wasi::ffi::OsStrExt;
 use crate::vec;
 
-pub unsafe fn init(_argc: isize, _argv: *const *const u8) {}
-
-pub unsafe fn cleanup() {}
-
 pub struct Args {
     iter: vec::IntoIter<OsString>,
     _dont_send_or_sync_me: PhantomData<*mut ()>,
diff --git a/library/std/src/sys/wasi/mod.rs b/library/std/src/sys/wasi/mod.rs
index b7b640b174fa9..2584d35b6ef8a 100644
--- a/library/std/src/sys/wasi/mod.rs
+++ b/library/std/src/sys/wasi/mod.rs
@@ -42,8 +42,6 @@ pub mod pipe;
 pub mod process;
 #[path = "../unsupported/rwlock.rs"]
 pub mod rwlock;
-#[path = "../unsupported/stack_overflow.rs"]
-pub mod stack_overflow;
 pub mod stdio;
 pub mod thread;
 #[path = "../unsupported/thread_local_dtor.rs"]
diff --git a/library/std/src/sys/wasm/args.rs b/library/std/src/sys/wasm/args.rs
index 3b6557ae3257f..3672227bf6680 100644
--- a/library/std/src/sys/wasm/args.rs
+++ b/library/std/src/sys/wasm/args.rs
@@ -2,12 +2,6 @@ use crate::ffi::OsString;
 use crate::marker::PhantomData;
 use crate::vec;
 
-pub unsafe fn init(_argc: isize, _argv: *const *const u8) {
-    // On wasm these should always be null, so there's nothing for us to do here
-}
-
-pub unsafe fn cleanup() {}
-
 pub fn args() -> Args {
     Args { iter: Vec::new().into_iter(), _dont_send_or_sync_me: PhantomData }
 }
diff --git a/library/std/src/sys/wasm/mod.rs b/library/std/src/sys/wasm/mod.rs
index 82683c0f624cf..8705910c73a81 100644
--- a/library/std/src/sys/wasm/mod.rs
+++ b/library/std/src/sys/wasm/mod.rs
@@ -35,8 +35,6 @@ pub mod path;
 pub mod pipe;
 #[path = "../unsupported/process.rs"]
 pub mod process;
-#[path = "../unsupported/stack_overflow.rs"]
-pub mod stack_overflow;
 #[path = "../unsupported/stdio.rs"]
 pub mod stdio;
 pub mod thread;
diff --git a/library/std/src/sys/windows/args.rs b/library/std/src/sys/windows/args.rs
index bcc2ea9ae00f0..feddb1ea78e63 100644
--- a/library/std/src/sys/windows/args.rs
+++ b/library/std/src/sys/windows/args.rs
@@ -14,10 +14,6 @@ use crate::vec;
 
 use core::iter;
 
-pub unsafe fn init(_argc: isize, _argv: *const *const u8) {}
-
-pub unsafe fn cleanup() {}
-
 pub fn args() -> Args {
     unsafe {
         let lp_cmd_line = c::GetCommandLineW();
diff --git a/library/std/src/sys/windows/mod.rs b/library/std/src/sys/windows/mod.rs
index 973301af2d992..ddb6ac5f55c0d 100644
--- a/library/std/src/sys/windows/mod.rs
+++ b/library/std/src/sys/windows/mod.rs
@@ -49,8 +49,17 @@ cfg_if::cfg_if! {
     }
 }
 
-#[cfg(not(test))]
-pub fn init() {}
+// SAFETY: must be called only once during runtime initialization.
+// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
+pub unsafe fn init(_argc: isize, _argv: *const *const u8) {
+    stack_overflow::init();
+}
+
+// SAFETY: must be called only once during runtime cleanup.
+// NOTE: this is not guaranteed to run, for example when the program aborts.
+pub unsafe fn cleanup() {
+    net::cleanup();
+}
 
 pub fn decode_error_kind(errno: i32) -> ErrorKind {
     match errno as c::DWORD {
diff --git a/library/std/src/sys/windows/net.rs b/library/std/src/sys/windows/net.rs
index ad04afc0b6d8a..1ad13254c0846 100644
--- a/library/std/src/sys/windows/net.rs
+++ b/library/std/src/sys/windows/net.rs
@@ -9,7 +9,7 @@ use crate::sync::Once;
 use crate::sys;
 use crate::sys::c;
 use crate::sys_common::net;
-use crate::sys_common::{self, AsInner, FromInner, IntoInner};
+use crate::sys_common::{AsInner, FromInner, IntoInner};
 use crate::time::Duration;
 
 use libc::{c_int, c_long, c_ulong, c_void};
@@ -26,23 +26,28 @@ pub mod netc {
 
 pub struct Socket(c::SOCKET);
 
+static INIT: Once = Once::new();
+
 /// Checks whether the Windows socket interface has been started already, and
 /// if not, starts it.
 pub fn init() {
-    static START: Once = Once::new();
-
-    START.call_once(|| unsafe {
+    INIT.call_once(|| unsafe {
         let mut data: c::WSADATA = mem::zeroed();
         let ret = c::WSAStartup(
             0x202, // version 2.2
             &mut data,
         );
         assert_eq!(ret, 0);
+    });
+}
 
-        let _ = sys_common::at_exit(|| {
+pub fn cleanup() {
+    if INIT.is_completed() {
+        // only close the socket interface if it has actually been started
+        unsafe {
             c::WSACleanup();
-        });
-    });
+        }
+    }
 }
 
 /// Returns the last error from the Windows socket interface.
diff --git a/library/std/src/sys/windows/stack_overflow.rs b/library/std/src/sys/windows/stack_overflow.rs
index 187ad4e66c3ef..39efb778207fc 100644
--- a/library/std/src/sys/windows/stack_overflow.rs
+++ b/library/std/src/sys/windows/stack_overflow.rs
@@ -37,5 +37,3 @@ pub unsafe fn init() {
     // Set the thread stack guarantee for the main thread.
     let _h = Handler::new();
 }
-
-pub unsafe fn cleanup() {}
diff --git a/library/std/src/sys/windows/stack_overflow_uwp.rs b/library/std/src/sys/windows/stack_overflow_uwp.rs
index e7236cf359cd5..afdf7f566ae51 100644
--- a/library/std/src/sys/windows/stack_overflow_uwp.rs
+++ b/library/std/src/sys/windows/stack_overflow_uwp.rs
@@ -9,5 +9,3 @@ impl Handler {
 }
 
 pub unsafe fn init() {}
-
-pub unsafe fn cleanup() {}
diff --git a/library/std/src/sys_common/at_exit_imp.rs b/library/std/src/sys_common/at_exit_imp.rs
deleted file mode 100644
index 90d5d3a78987f..0000000000000
--- a/library/std/src/sys_common/at_exit_imp.rs
+++ /dev/null
@@ -1,74 +0,0 @@
-//! Implementation of running at_exit routines
-//!
-//! Documentation can be found on the `rt::at_exit` function.
-
-use crate::mem;
-use crate::ptr;
-use crate::sys_common::mutex::StaticMutex;
-
-type Queue = Vec<Box<dyn FnOnce()>>;
-
-// NB these are specifically not types from `std::sync` as they currently rely
-// on poisoning and this module needs to operate at a lower level than requiring
-// the thread infrastructure to be in place (useful on the borders of
-// initialization/destruction).
-// It is UB to attempt to acquire this mutex reentrantly!
-static LOCK: StaticMutex = StaticMutex::new();
-static mut QUEUE: *mut Queue = ptr::null_mut();
-
-const DONE: *mut Queue = 1_usize as *mut _;
-
-// The maximum number of times the cleanup routines will be run. While running
-// the at_exit closures new ones may be registered, and this count is the number
-// of times the new closures will be allowed to register successfully. After
-// this number of iterations all new registrations will return `false`.
-const ITERS: usize = 10;
-
-unsafe fn init() -> bool {
-    if QUEUE.is_null() {
-        let state: Box<Queue> = box Vec::new();
-        QUEUE = Box::into_raw(state);
-    } else if QUEUE == DONE {
-        // can't re-init after a cleanup
-        return false;
-    }
-
-    true
-}
-
-pub fn cleanup() {
-    for i in 1..=ITERS {
-        unsafe {
-            let queue = {
-                let _guard = LOCK.lock();
-                mem::replace(&mut QUEUE, if i == ITERS { DONE } else { ptr::null_mut() })
-            };
-
-            // make sure we're not recursively cleaning up
-            assert!(queue != DONE);
-
-            // If we never called init, not need to cleanup!
-            if !queue.is_null() {
-                let queue: Box<Queue> = Box::from_raw(queue);
-                for to_run in *queue {
-                    // We are not holding any lock, so reentrancy is fine.
-                    to_run();
-                }
-            }
-        }
-    }
-}
-
-pub fn push(f: Box<dyn FnOnce()>) -> bool {
-    unsafe {
-        let _guard = LOCK.lock();
-        if init() {
-            // We are just moving `f` around, not calling it.
-            // There is no possibility of reentrancy here.
-            (*QUEUE).push(f);
-            true
-        } else {
-            false
-        }
-    }
-}
diff --git a/library/std/src/sys_common/mod.rs b/library/std/src/sys_common/mod.rs
index 23a3a0e907dcf..449ce509d2587 100644
--- a/library/std/src/sys_common/mod.rs
+++ b/library/std/src/sys_common/mod.rs
@@ -20,35 +20,6 @@
 #[cfg(test)]
 mod tests;
 
-use crate::sync::Once;
-use crate::sys;
-
-macro_rules! rtabort {
-    ($($t:tt)*) => (crate::sys_common::util::abort(format_args!($($t)*)))
-}
-
-macro_rules! rtassert {
-    ($e:expr) => {
-        if !$e {
-            rtabort!(concat!("assertion failed: ", stringify!($e)));
-        }
-    };
-}
-
-#[allow(unused_macros)] // not used on all platforms
-macro_rules! rtunwrap {
-    ($ok:ident, $e:expr) => {
-        match $e {
-            $ok(v) => v,
-            ref err => {
-                let err = err.as_ref().map(drop); // map Ok/Some which might not be Debug
-                rtabort!(concat!("unwrap failed: ", stringify!($e), " = {:?}"), err)
-            }
-        }
-    };
-}
-
-pub mod at_exit_imp;
 pub mod backtrace;
 pub mod bytestring;
 pub mod condvar;
@@ -62,6 +33,8 @@ pub mod os_str_bytes;
 pub mod poison;
 pub mod process;
 pub mod remutex;
+#[macro_use]
+pub mod rt;
 pub mod rwlock;
 pub mod thread;
 pub mod thread_info;
@@ -109,30 +82,6 @@ pub trait FromInner<Inner> {
     fn from_inner(inner: Inner) -> Self;
 }
 
-/// Enqueues a procedure to run when the main thread exits.
-///
-/// Currently these closures are only run once the main *Rust* thread exits.
-/// Once the `at_exit` handlers begin running, more may be enqueued, but not
-/// infinitely so. Eventually a handler registration will be forced to fail.
-///
-/// Returns `Ok` if the handler was successfully registered, meaning that the
-/// closure will be run once the main thread exits. Returns `Err` to indicate
-/// that the closure could not be registered, meaning that it is not scheduled
-/// to be run.
-pub fn at_exit<F: FnOnce() + Send + 'static>(f: F) -> Result<(), ()> {
-    if at_exit_imp::push(Box::new(f)) { Ok(()) } else { Err(()) }
-}
-
-/// One-time runtime cleanup.
-pub fn cleanup() {
-    static CLEANUP: Once = Once::new();
-    CLEANUP.call_once(|| unsafe {
-        sys::args::cleanup();
-        sys::stack_overflow::cleanup();
-        at_exit_imp::cleanup();
-    });
-}
-
 // Computes (value*numer)/denom without overflow, as long as both
 // (numer*denom) and the overall result fit into i64 (which is the case
 // for our time conversions).
diff --git a/library/std/src/sys_common/rt.rs b/library/std/src/sys_common/rt.rs
new file mode 100644
index 0000000000000..c70f2ecc04e3a
--- /dev/null
+++ b/library/std/src/sys_common/rt.rs
@@ -0,0 +1,64 @@
+#![deny(unsafe_op_in_unsafe_fn)]
+
+use crate::sync::Once;
+use crate::sys;
+use crate::sys_common::thread_info;
+use crate::thread::Thread;
+
+// One-time runtime initialization.
+// Runs before `main`.
+// SAFETY: must be called only once during runtime initialization.
+// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
+#[cfg_attr(test, allow(dead_code))]
+pub unsafe fn init(argc: isize, argv: *const *const u8) {
+    unsafe {
+        sys::init(argc, argv);
+
+        let main_guard = sys::thread::guard::init();
+        // Next, set up the current Thread with the guard information we just
+        // created. Note that this isn't necessary in general for new threads,
+        // but we just do this to name the main thread and to give it correct
+        // info about the stack bounds.
+        let thread = Thread::new(Some("main".to_owned()));
+        thread_info::set(main_guard, thread);
+    }
+}
+
+// One-time runtime cleanup.
+// Runs after `main` or at program exit.
+// NOTE: this is not guaranteed to run, for example when the program aborts.
+#[cfg_attr(test, allow(dead_code))]
+pub fn cleanup() {
+    static CLEANUP: Once = Once::new();
+    CLEANUP.call_once(|| unsafe {
+        // Flush stdout and disable buffering.
+        crate::io::cleanup();
+        // SAFETY: Only called once during runtime cleanup.
+        sys::cleanup();
+    });
+}
+
+macro_rules! rtabort {
+    ($($t:tt)*) => (crate::sys_common::util::abort(format_args!($($t)*)))
+}
+
+macro_rules! rtassert {
+    ($e:expr) => {
+        if !$e {
+            rtabort!(concat!("assertion failed: ", stringify!($e)));
+        }
+    };
+}
+
+#[allow(unused_macros)] // not used on all platforms
+macro_rules! rtunwrap {
+    ($ok:ident, $e:expr) => {
+        match $e {
+            $ok(v) => v,
+            ref err => {
+                let err = err.as_ref().map(drop); // map Ok/Some which might not be Debug
+                rtabort!(concat!("unwrap failed: ", stringify!($e), " = {:?}"), err)
+            }
+        }
+    };
+}