about summary refs log tree commit diff
path: root/src/libstd
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2020-07-17 00:09:49 +0000
committerbors <bors@rust-lang.org>2020-07-17 00:09:49 +0000
commit8534be72fc3b9c5f2f2dc4e4ee7b651a008e9a3e (patch)
tree9b6f6432be9c4a78fd4677f7a3125f3eb93e1a0a /src/libstd
parent5c9e5df3a097e094641f16dab501ab1c4da10e9f (diff)
parent5bb9bef79577b9629b12800dcdae1d8fd52998c0 (diff)
downloadrust-8534be72fc3b9c5f2f2dc4e4ee7b651a008e9a3e.tar.gz
rust-8534be72fc3b9c5f2f2dc4e4ee7b651a008e9a3e.zip
Auto merge of #74422 - Manishearth:rollup-7mfrf6g, r=Manishearth
Rollup of 8 pull requests

Successful merges:

 - #73101 (Resolve items for cross-crate imports relative to the original module)
 - #73269 (Enable some timeouts in SGX platform)
 - #74033 (Add build support for Cargo's build-std feature.)
 - #74351 (Do not render unstable items for rustc doc)
 - #74357 (Some `Symbol` related improvements)
 - #74371 (Improve ayu rustdoc theme)
 - #74386 (Add RISC-V GNU/Linux to src/tools/build-manifest as a host platform)
 - #74398 (Clean up E0723 explanation)

Failed merges:

r? @ghost
Diffstat (limited to 'src/libstd')
-rw-r--r--src/libstd/build.rs24
-rw-r--r--src/libstd/env.rs77
-rw-r--r--src/libstd/lib.rs9
-rw-r--r--src/libstd/sync/condvar.rs4
-rw-r--r--src/libstd/sync/mpsc/mod.rs9
-rw-r--r--src/libstd/sys/mod.rs3
-rw-r--r--src/libstd/sys/sgx/abi/usercalls/mod.rs92
-rw-r--r--src/libstd/sys/sgx/condvar.rs6
-rw-r--r--src/libstd/sys/sgx/mod.rs9
-rw-r--r--src/libstd/sys/sgx/thread.rs4
-rw-r--r--src/libstd/sys/sgx/waitqueue.rs129
-rw-r--r--src/libstd/sys/unsupported/alloc.rs22
-rw-r--r--src/libstd/sys/unsupported/args.rs38
-rw-r--r--src/libstd/sys/unsupported/cmath.rs (renamed from src/libstd/sys/wasm/cmath.rs)0
-rw-r--r--src/libstd/sys/unsupported/common.rs48
-rw-r--r--src/libstd/sys/unsupported/condvar.rs (renamed from src/libstd/sys/wasm/condvar.rs)4
-rw-r--r--src/libstd/sys/unsupported/env.rs9
-rw-r--r--src/libstd/sys/unsupported/fs.rs (renamed from src/libstd/sys/wasm/fs.rs)0
-rw-r--r--src/libstd/sys/unsupported/io.rs (renamed from src/libstd/sys/wasm/io.rs)0
-rw-r--r--src/libstd/sys/unsupported/mod.rs24
-rw-r--r--src/libstd/sys/unsupported/mutex.rs (renamed from src/libstd/sys/wasm/mutex.rs)7
-rw-r--r--src/libstd/sys/unsupported/net.rs (renamed from src/libstd/sys/wasm/net.rs)0
-rw-r--r--src/libstd/sys/unsupported/os.rs (renamed from src/libstd/sys/wasm/os.rs)21
-rw-r--r--src/libstd/sys/unsupported/path.rs (renamed from src/libstd/sys/wasm/path.rs)0
-rw-r--r--src/libstd/sys/unsupported/pipe.rs (renamed from src/libstd/sys/wasm/pipe.rs)0
-rw-r--r--src/libstd/sys/unsupported/process.rs (renamed from src/libstd/sys/wasm/process.rs)0
-rw-r--r--src/libstd/sys/unsupported/rwlock.rs (renamed from src/libstd/sys/wasm/rwlock.rs)2
-rw-r--r--src/libstd/sys/unsupported/stack_overflow.rs (renamed from src/libstd/sys/wasm/stack_overflow.rs)0
-rw-r--r--src/libstd/sys/unsupported/stdio.rs (renamed from src/libstd/sys/wasm/stdio.rs)0
-rw-r--r--src/libstd/sys/unsupported/thread.rs41
-rw-r--r--src/libstd/sys/unsupported/thread_local_dtor.rs (renamed from src/libstd/sys/wasm/thread_local_dtor.rs)0
-rw-r--r--src/libstd/sys/unsupported/thread_local_key.rs (renamed from src/libstd/sys/wasm/thread_local_key.rs)10
-rw-r--r--src/libstd/sys/unsupported/time.rs (renamed from src/libstd/sys/wasm/time.rs)4
-rw-r--r--src/libstd/sys/wasi/mod.rs45
-rw-r--r--src/libstd/sys/wasm/memchr.rs1
-rw-r--r--src/libstd/sys/wasm/mod.rs63
-rw-r--r--src/libstd/sys_common/mod.rs11
-rw-r--r--src/libstd/sys_common/mutex.rs1
-rw-r--r--src/libstd/thread/mod.rs3
39 files changed, 488 insertions, 232 deletions
diff --git a/src/libstd/build.rs b/src/libstd/build.rs
index 743a1778fbd..eb2753d6245 100644
--- a/src/libstd/build.rs
+++ b/src/libstd/build.rs
@@ -62,5 +62,29 @@ fn main() {
         }
         println!("cargo:rustc-link-lib=c");
         println!("cargo:rustc-link-lib=compiler_rt");
+    } else if (target.contains("sgx") && target.contains("fortanix"))
+        || target.contains("hermit")
+        || target.contains("l4re")
+        || target.contains("redox")
+        || target.contains("haiku")
+        || target.contains("vxworks")
+        || target.contains("wasm32")
+        || target.contains("asmjs")
+    {
+        // These platforms don't have any special requirements.
+    } else {
+        // This is for Cargo's build-std support, to mark std as unstable for
+        // typically no_std platforms.
+        // This covers:
+        // - os=none ("bare metal" targets)
+        // - mipsel-sony-psp
+        // - nvptx64-nvidia-cuda
+        // - avr-unknown-unknown
+        // - tvos (aarch64-apple-tvos, x86_64-apple-tvos)
+        // - uefi (x86_64-unknown-uefi, i686-unknown-uefi)
+        // - JSON targets
+        // - Any new targets that have not been explicitly added above.
+        println!("cargo:rustc-cfg=feature=\"restricted-std\"");
     }
+    println!("cargo:rustc-env=STD_ENV_ARCH={}", env::var("CARGO_CFG_TARGET_ARCH").unwrap());
 }
diff --git a/src/libstd/env.rs b/src/libstd/env.rs
index 97c20ca9459..6489e0709cb 100644
--- a/src/libstd/env.rs
+++ b/src/libstd/env.rs
@@ -882,7 +882,7 @@ pub mod consts {
     /// - s390x
     /// - sparc64
     #[stable(feature = "env", since = "1.0.0")]
-    pub const ARCH: &str = super::arch::ARCH;
+    pub const ARCH: &str = env!("STD_ENV_ARCH");
 
     /// The family of the operating system. Example value is `unix`.
     ///
@@ -966,81 +966,6 @@ pub mod consts {
     pub const EXE_EXTENSION: &str = os::EXE_EXTENSION;
 }
 
-#[cfg(target_arch = "x86")]
-mod arch {
-    pub const ARCH: &str = "x86";
-}
-
-#[cfg(target_arch = "x86_64")]
-mod arch {
-    pub const ARCH: &str = "x86_64";
-}
-
-#[cfg(target_arch = "arm")]
-mod arch {
-    pub const ARCH: &str = "arm";
-}
-
-#[cfg(target_arch = "aarch64")]
-mod arch {
-    pub const ARCH: &str = "aarch64";
-}
-
-#[cfg(target_arch = "mips")]
-mod arch {
-    pub const ARCH: &str = "mips";
-}
-
-#[cfg(target_arch = "mips64")]
-mod arch {
-    pub const ARCH: &str = "mips64";
-}
-
-#[cfg(target_arch = "powerpc")]
-mod arch {
-    pub const ARCH: &str = "powerpc";
-}
-
-#[cfg(target_arch = "powerpc64")]
-mod arch {
-    pub const ARCH: &str = "powerpc64";
-}
-
-#[cfg(target_arch = "s390x")]
-mod arch {
-    pub const ARCH: &str = "s390x";
-}
-
-#[cfg(target_arch = "sparc64")]
-mod arch {
-    pub const ARCH: &str = "sparc64";
-}
-
-#[cfg(target_arch = "le32")]
-mod arch {
-    pub const ARCH: &str = "le32";
-}
-
-#[cfg(target_arch = "asmjs")]
-mod arch {
-    pub const ARCH: &str = "asmjs";
-}
-
-#[cfg(target_arch = "wasm32")]
-mod arch {
-    pub const ARCH: &str = "wasm32";
-}
-
-#[cfg(target_arch = "hexagon")]
-mod arch {
-    pub const ARCH: &'static str = "hexagon";
-}
-
-#[cfg(target_arch = "riscv64")]
-mod arch {
-    pub const ARCH: &'static str = "riscv64";
-}
-
 #[cfg(test)]
 mod tests {
     use super::*;
diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs
index cbc24009a94..b5ba0da7ae5 100644
--- a/src/libstd/lib.rs
+++ b/src/libstd/lib.rs
@@ -198,7 +198,8 @@
 //! [primitive types]: ../book/ch03-02-data-types.html
 //! [rust-discord]: https://discord.gg/rust-lang
 
-#![stable(feature = "rust1", since = "1.0.0")]
+#![cfg_attr(not(feature = "restricted-std"), stable(feature = "rust1", since = "1.0.0"))]
+#![cfg_attr(feature = "restricted-std", unstable(feature = "restricted_std", issue = "none"))]
 #![doc(
     html_root_url = "https://doc.rust-lang.org/nightly/",
     html_playground_url = "https://play.rust-lang.org/",
@@ -554,3 +555,9 @@ include!("primitive_docs.rs");
 // the rustdoc documentation for the existing keywords. Using `include!`
 // because rustdoc only looks for these modules at the crate level.
 include!("keyword_docs.rs");
+
+// This is required to avoid an unstable error when `restricted-std` is not
+// enabled. The use of #![feature(restricted_std)] in rustc-std-workspace-std
+// is unconditional, so the unstable feature needs to be defined somewhere.
+#[cfg_attr(not(feature = "restricted-std"), unstable(feature = "restricted_std", issue = "none"))]
+mod __restricted_std_workaround {}
diff --git a/src/libstd/sync/condvar.rs b/src/libstd/sync/condvar.rs
index 2250c0d4203..9b90bfd68b5 100644
--- a/src/libstd/sync/condvar.rs
+++ b/src/libstd/sync/condvar.rs
@@ -694,7 +694,6 @@ mod tests {
 
     #[test]
     #[cfg_attr(target_os = "emscripten", ignore)]
-    #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31
     fn wait_timeout_wait() {
         let m = Arc::new(Mutex::new(()));
         let c = Arc::new(Condvar::new());
@@ -714,7 +713,6 @@ mod tests {
 
     #[test]
     #[cfg_attr(target_os = "emscripten", ignore)]
-    #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31
     fn wait_timeout_while_wait() {
         let m = Arc::new(Mutex::new(()));
         let c = Arc::new(Condvar::new());
@@ -739,7 +737,6 @@ mod tests {
 
     #[test]
     #[cfg_attr(target_os = "emscripten", ignore)]
-    #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31
     fn wait_timeout_while_wake() {
         let pair = Arc::new((Mutex::new(false), Condvar::new()));
         let pair_copy = pair.clone();
@@ -763,7 +760,6 @@ mod tests {
 
     #[test]
     #[cfg_attr(target_os = "emscripten", ignore)]
-    #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31
     fn wait_timeout_wake() {
         let m = Arc::new(Mutex::new(()));
         let c = Arc::new(Condvar::new());
diff --git a/src/libstd/sync/mpsc/mod.rs b/src/libstd/sync/mpsc/mod.rs
index d6cc811154f..3ff50e9f213 100644
--- a/src/libstd/sync/mpsc/mod.rs
+++ b/src/libstd/sync/mpsc/mod.rs
@@ -2088,7 +2088,6 @@ mod tests {
     }
 
     #[test]
-    #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31
     fn oneshot_single_thread_recv_timeout() {
         let (tx, rx) = channel();
         tx.send(()).unwrap();
@@ -2099,7 +2098,6 @@ mod tests {
     }
 
     #[test]
-    #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31
     fn stress_recv_timeout_two_threads() {
         let (tx, rx) = channel();
         let stress = stress_factor() + 100;
@@ -2130,7 +2128,6 @@ mod tests {
     }
 
     #[test]
-    #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31
     fn recv_timeout_upgrade() {
         let (tx, rx) = channel::<()>();
         let timeout = Duration::from_millis(1);
@@ -2142,7 +2139,6 @@ mod tests {
     }
 
     #[test]
-    #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31
     fn stress_recv_timeout_shared() {
         let (tx, rx) = channel();
         let stress = stress_factor() + 100;
@@ -2173,7 +2169,6 @@ mod tests {
     }
 
     #[test]
-    #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31
     fn very_long_recv_timeout_wont_panic() {
         let (tx, rx) = channel::<()>();
         let join_handle = thread::spawn(move || rx.recv_timeout(Duration::from_secs(u64::MAX)));
@@ -2195,7 +2190,6 @@ mod tests {
     }
 
     #[test]
-    #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31
     fn shared_recv_timeout() {
         let (tx, rx) = channel();
         let total = 5;
@@ -2425,7 +2419,6 @@ mod sync_tests {
     }
 
     #[test]
-    #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31
     fn recv_timeout() {
         let (tx, rx) = sync_channel::<i32>(1);
         assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Err(RecvTimeoutError::Timeout));
@@ -2517,7 +2510,6 @@ mod sync_tests {
     }
 
     #[test]
-    #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31
     fn stress_recv_timeout_two_threads() {
         let (tx, rx) = sync_channel::<i32>(0);
 
@@ -2543,7 +2535,6 @@ mod sync_tests {
     }
 
     #[test]
-    #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31
     fn stress_recv_timeout_shared() {
         const AMT: u32 = 1000;
         const NTHREADS: u32 = 8;
diff --git a/src/libstd/sys/mod.rs b/src/libstd/sys/mod.rs
index 875ff1af920..7b5fac922d0 100644
--- a/src/libstd/sys/mod.rs
+++ b/src/libstd/sys/mod.rs
@@ -48,7 +48,8 @@ cfg_if::cfg_if! {
         mod sgx;
         pub use self::sgx::*;
     } else {
-        compile_error!("libstd doesn't compile for this platform yet");
+        mod unsupported;
+        pub use self::unsupported::*;
     }
 }
 
diff --git a/src/libstd/sys/sgx/abi/usercalls/mod.rs b/src/libstd/sys/sgx/abi/usercalls/mod.rs
index ae803ee47a6..73f1b951e74 100644
--- a/src/libstd/sys/sgx/abi/usercalls/mod.rs
+++ b/src/libstd/sys/sgx/abi/usercalls/mod.rs
@@ -1,6 +1,8 @@
 use crate::cmp;
-use crate::io::{Error as IoError, IoSlice, IoSliceMut, Result as IoResult};
-use crate::time::Duration;
+use crate::convert::TryFrom;
+use crate::io::{Error as IoError, ErrorKind, IoSlice, IoSliceMut, Result as IoResult};
+use crate::sys::rand::rdrand64;
+use crate::time::{Duration, Instant};
 
 pub(crate) mod alloc;
 #[macro_use]
@@ -149,10 +151,94 @@ pub fn exit(panic: bool) -> ! {
 
 /// Usercall `wait`. See the ABI documentation for more information.
 #[unstable(feature = "sgx_platform", issue = "56975")]
-pub fn wait(event_mask: u64, timeout: u64) -> IoResult<u64> {
+pub fn wait(event_mask: u64, mut timeout: u64) -> IoResult<u64> {
+    if timeout != WAIT_NO && timeout != WAIT_INDEFINITE {
+        // We don't want people to rely on accuracy of timeouts to make
+        // security decisions in an SGX enclave. That's why we add a random
+        // amount not exceeding +/- 10% to the timeout value to discourage
+        // people from relying on accuracy of timeouts while providing a way
+        // to make things work in other cases. Note that in the SGX threat
+        // model the enclave runner which is serving the wait usercall is not
+        // trusted to ensure accurate timeouts.
+        if let Ok(timeout_signed) = i64::try_from(timeout) {
+            let tenth = timeout_signed / 10;
+            let deviation = (rdrand64() as i64).checked_rem(tenth).unwrap_or(0);
+            timeout = timeout_signed.saturating_add(deviation) as _;
+        }
+    }
     unsafe { raw::wait(event_mask, timeout).from_sgx_result() }
 }
 
+/// This function makes an effort to wait for a non-spurious event at least as
+/// long as `duration`. Note that in general there is no guarantee about accuracy
+/// of time and timeouts in SGX model. The enclave runner serving usercalls may
+/// lie about current time and/or ignore timeout values.
+///
+/// Once the event is observed, `should_wake_up` will be used to determine
+/// whether or not the event was spurious.
+#[unstable(feature = "sgx_platform", issue = "56975")]
+pub fn wait_timeout<F>(event_mask: u64, duration: Duration, should_wake_up: F)
+where
+    F: Fn() -> bool,
+{
+    // Calls the wait usercall and checks the result. Returns true if event was
+    // returned, and false if WouldBlock/TimedOut was returned.
+    // If duration is None, it will use WAIT_NO.
+    fn wait_checked(event_mask: u64, duration: Option<Duration>) -> bool {
+        let timeout = duration.map_or(raw::WAIT_NO, |duration| {
+            cmp::min((u64::MAX - 1) as u128, duration.as_nanos()) as u64
+        });
+        match wait(event_mask, timeout) {
+            Ok(eventset) => {
+                if event_mask == 0 {
+                    rtabort!("expected wait() to return Err, found Ok.");
+                }
+                rtassert!(eventset != 0 && eventset & !event_mask == 0);
+                true
+            }
+            Err(e) => {
+                rtassert!(e.kind() == ErrorKind::TimedOut || e.kind() == ErrorKind::WouldBlock);
+                false
+            }
+        }
+    }
+
+    match wait_checked(event_mask, Some(duration)) {
+        false => return,                    // timed out
+        true if should_wake_up() => return, // woken up
+        true => {}                          // spurious event
+    }
+
+    // Drain all cached events.
+    // Note that `event_mask != 0` is implied if we get here.
+    loop {
+        match wait_checked(event_mask, None) {
+            false => break,                     // no more cached events
+            true if should_wake_up() => return, // woken up
+            true => {}                          // spurious event
+        }
+    }
+
+    // Continue waiting, but take note of time spent waiting so we don't wait
+    // forever. We intentionally don't call `Instant::now()` before this point
+    // to avoid the cost of the `insecure_time` usercall in case there are no
+    // spurious wakeups.
+
+    let start = Instant::now();
+    let mut remaining = duration;
+    loop {
+        match wait_checked(event_mask, Some(remaining)) {
+            false => return,                    // timed out
+            true if should_wake_up() => return, // woken up
+            true => {}                          // spurious event
+        }
+        remaining = match duration.checked_sub(start.elapsed()) {
+            Some(remaining) => remaining,
+            None => break,
+        }
+    }
+}
+
 /// Usercall `send`. See the ABI documentation for more information.
 #[unstable(feature = "sgx_platform", issue = "56975")]
 pub fn send(event_set: u64, tcs: Option<Tcs>) -> IoResult<()> {
diff --git a/src/libstd/sys/sgx/condvar.rs b/src/libstd/sys/sgx/condvar.rs
index 9c5c086184d..ed6dbcf4971 100644
--- a/src/libstd/sys/sgx/condvar.rs
+++ b/src/libstd/sys/sgx/condvar.rs
@@ -31,8 +31,10 @@ impl Condvar {
         mutex.lock()
     }
 
-    pub unsafe fn wait_timeout(&self, _mutex: &Mutex, _dur: Duration) -> bool {
-        rtabort!("timeout not supported in SGX");
+    pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool {
+        let success = WaitQueue::wait_timeout(&self.inner, dur, || mutex.unlock());
+        mutex.lock();
+        success
     }
 
     #[inline]
diff --git a/src/libstd/sys/sgx/mod.rs b/src/libstd/sys/sgx/mod.rs
index a4968ff7d4f..1d32eb25424 100644
--- a/src/libstd/sys/sgx/mod.rs
+++ b/src/libstd/sys/sgx/mod.rs
@@ -137,8 +137,8 @@ pub extern "C" fn __rust_abort() {
     abort_internal();
 }
 
-pub fn hashmap_random_keys() -> (u64, u64) {
-    fn rdrand64() -> u64 {
+pub mod rand {
+    pub fn rdrand64() -> u64 {
         unsafe {
             let mut ret: u64 = 0;
             for _ in 0..10 {
@@ -149,7 +149,10 @@ pub fn hashmap_random_keys() -> (u64, u64) {
             rtabort!("Failed to obtain random data");
         }
     }
-    (rdrand64(), rdrand64())
+}
+
+pub fn hashmap_random_keys() -> (u64, u64) {
+    (self::rand::rdrand64(), self::rand::rdrand64())
 }
 
 pub use crate::sys_common::{AsInner, FromInner, IntoInner};
diff --git a/src/libstd/sys/sgx/thread.rs b/src/libstd/sys/sgx/thread.rs
index 9b515eb82de..5895f70436e 100644
--- a/src/libstd/sys/sgx/thread.rs
+++ b/src/libstd/sys/sgx/thread.rs
@@ -73,8 +73,8 @@ impl Thread {
         // FIXME: could store this pointer in TLS somewhere
     }
 
-    pub fn sleep(_dur: Duration) {
-        rtabort!("can't sleep"); // FIXME
+    pub fn sleep(dur: Duration) {
+        usercalls::wait_timeout(0, dur, || true);
     }
 
     pub fn join(self) {
diff --git a/src/libstd/sys/sgx/waitqueue.rs b/src/libstd/sys/sgx/waitqueue.rs
index 6e50f161b3b..070afa55f30 100644
--- a/src/libstd/sys/sgx/waitqueue.rs
+++ b/src/libstd/sys/sgx/waitqueue.rs
@@ -1,16 +1,17 @@
+//! A simple queue implementation for synchronization primitives.
+//!
+//! This queue is used to implement condition variable and mutexes.
+//!
+//! Users of this API are expected to use the `WaitVariable<T>` type. Since
+//! that type is not `Sync`, it needs to be protected by e.g., a `SpinMutex` to
+//! allow shared access.
+//!
+//! Since userspace may send spurious wake-ups, the wakeup event state is
+//! recorded in the enclave. The wakeup event state is protected by a spinlock.
+//! The queue and associated wait state are stored in a `WaitVariable`.
 use crate::num::NonZeroUsize;
-/// A simple queue implementation for synchronization primitives.
-///
-/// This queue is used to implement condition variable and mutexes.
-///
-/// Users of this API are expected to use the `WaitVariable<T>` type. Since
-/// that type is not `Sync`, it needs to be protected by e.g., a `SpinMutex` to
-/// allow shared access.
-///
-/// Since userspace may send spurious wake-ups, the wakeup event state is
-/// recorded in the enclave. The wakeup event state is protected by a spinlock.
-/// The queue and associated wait state are stored in a `WaitVariable`.
 use crate::ops::{Deref, DerefMut};
+use crate::time::Duration;
 
 use super::abi::thread;
 use super::abi::usercalls;
@@ -158,6 +159,34 @@ impl WaitQueue {
         }
     }
 
+    /// Adds the calling thread to the `WaitVariable`'s wait queue, then wait
+    /// until a wakeup event or timeout. If event was observed, returns true.
+    /// If not, it will remove the calling thread from the wait queue.
+    pub fn wait_timeout<T, F: FnOnce()>(
+        lock: &SpinMutex<WaitVariable<T>>,
+        timeout: Duration,
+        before_wait: F,
+    ) -> bool {
+        // very unsafe: check requirements of UnsafeList::push
+        unsafe {
+            let mut entry = UnsafeListEntry::new(SpinMutex::new(WaitEntry {
+                tcs: thread::current(),
+                wake: false,
+            }));
+            let entry_lock = lock.lock().queue.inner.push(&mut entry);
+            before_wait();
+            usercalls::wait_timeout(EV_UNPARK, timeout, || entry_lock.lock().wake);
+            // acquire the wait queue's lock first to avoid deadlock.
+            let mut guard = lock.lock();
+            let success = entry_lock.lock().wake;
+            if !success {
+                // nobody is waking us up, so remove our entry from the wait queue.
+                guard.queue.inner.remove(&mut entry);
+            }
+            success
+        }
+    }
+
     /// Either find the next waiter on the wait queue, or return the mutex
     /// guard unchanged.
     ///
@@ -325,6 +354,31 @@ mod unsafe_list {
                 Some((*first.as_ptr()).value.as_ref().unwrap())
             }
         }
+
+        /// Removes an entry from the list.
+        ///
+        /// # Safety
+        ///
+        /// The caller must ensure that `entry` has been pushed onto `self`
+        /// prior to this call and has not moved since then.
+        pub unsafe fn remove(&mut self, entry: &mut UnsafeListEntry<T>) {
+            rtassert!(!self.is_empty());
+            // BEFORE:
+            //     /----\ next ---> /-----\ next ---> /----\
+            // ... |prev|           |entry|           |next| ...
+            //     \----/ <--- prev \-----/ <--- prev \----/
+            //
+            // AFTER:
+            //     /----\ next ---> /----\
+            // ... |prev|           |next| ...
+            //     \----/ <--- prev \----/
+            let mut prev = entry.prev;
+            let mut next = entry.next;
+            prev.as_mut().next = next;
+            next.as_mut().prev = prev;
+            entry.next = NonNull::dangling();
+            entry.prev = NonNull::dangling();
+        }
     }
 
     #[cfg(test)]
@@ -355,6 +409,51 @@ mod unsafe_list {
         }
 
         #[test]
+        fn push_remove() {
+            unsafe {
+                let mut node = UnsafeListEntry::new(1234);
+                let mut list = UnsafeList::new();
+                assert_eq!(list.push(&mut node), &1234);
+                list.remove(&mut node);
+                assert_empty(&mut list);
+            }
+        }
+
+        #[test]
+        fn push_remove_pop() {
+            unsafe {
+                let mut node1 = UnsafeListEntry::new(11);
+                let mut node2 = UnsafeListEntry::new(12);
+                let mut node3 = UnsafeListEntry::new(13);
+                let mut node4 = UnsafeListEntry::new(14);
+                let mut node5 = UnsafeListEntry::new(15);
+                let mut list = UnsafeList::new();
+                assert_eq!(list.push(&mut node1), &11);
+                assert_eq!(list.push(&mut node2), &12);
+                assert_eq!(list.push(&mut node3), &13);
+                assert_eq!(list.push(&mut node4), &14);
+                assert_eq!(list.push(&mut node5), &15);
+
+                list.remove(&mut node1);
+                assert_eq!(list.pop().unwrap(), &12);
+                list.remove(&mut node3);
+                assert_eq!(list.pop().unwrap(), &14);
+                list.remove(&mut node5);
+                assert_empty(&mut list);
+
+                assert_eq!(list.push(&mut node1), &11);
+                assert_eq!(list.pop().unwrap(), &11);
+                assert_empty(&mut list);
+
+                assert_eq!(list.push(&mut node3), &13);
+                assert_eq!(list.push(&mut node4), &14);
+                list.remove(&mut node3);
+                list.remove(&mut node4);
+                assert_empty(&mut list);
+            }
+        }
+
+        #[test]
         fn complex_pushes_pops() {
             unsafe {
                 let mut node1 = UnsafeListEntry::new(1234);
@@ -474,7 +573,7 @@ mod spin_mutex {
         use super::*;
         use crate::sync::Arc;
         use crate::thread;
-        use crate::time::{Duration, SystemTime};
+        use crate::time::Duration;
 
         #[test]
         fn sleep() {
@@ -485,11 +584,7 @@ mod spin_mutex {
                 *mutex2.lock() = 1;
             });
 
-            // "sleep" for 50ms
-            // FIXME: https://github.com/fortanix/rust-sgx/issues/31
-            let start = SystemTime::now();
-            let max = Duration::from_millis(50);
-            while start.elapsed().unwrap() < max {}
+            thread::sleep(Duration::from_millis(50));
 
             assert_eq!(*guard, 0);
             drop(guard);
diff --git a/src/libstd/sys/unsupported/alloc.rs b/src/libstd/sys/unsupported/alloc.rs
new file mode 100644
index 00000000000..8d5d0a2f5cc
--- /dev/null
+++ b/src/libstd/sys/unsupported/alloc.rs
@@ -0,0 +1,22 @@
+use crate::alloc::{GlobalAlloc, Layout, System};
+
+#[stable(feature = "alloc_system_type", since = "1.28.0")]
+unsafe impl GlobalAlloc for System {
+    #[inline]
+    unsafe fn alloc(&self, _layout: Layout) -> *mut u8 {
+        0 as *mut u8
+    }
+
+    #[inline]
+    unsafe fn alloc_zeroed(&self, _layout: Layout) -> *mut u8 {
+        0 as *mut u8
+    }
+
+    #[inline]
+    unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {}
+
+    #[inline]
+    unsafe fn realloc(&self, _ptr: *mut u8, _layout: Layout, _new_size: usize) -> *mut u8 {
+        0 as *mut u8
+    }
+}
diff --git a/src/libstd/sys/unsupported/args.rs b/src/libstd/sys/unsupported/args.rs
new file mode 100644
index 00000000000..71d0c5fa13e
--- /dev/null
+++ b/src/libstd/sys/unsupported/args.rs
@@ -0,0 +1,38 @@
+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 {
+    Args {}
+}
+
+impl Args {
+    pub fn inner_debug(&self) -> &[OsString] {
+        &[]
+    }
+}
+
+impl Iterator for Args {
+    type Item = OsString;
+    fn next(&mut self) -> Option<OsString> {
+        None
+    }
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        (0, Some(0))
+    }
+}
+
+impl ExactSizeIterator for Args {
+    fn len(&self) -> usize {
+        0
+    }
+}
+
+impl DoubleEndedIterator for Args {
+    fn next_back(&mut self) -> Option<OsString> {
+        None
+    }
+}
diff --git a/src/libstd/sys/wasm/cmath.rs b/src/libstd/sys/unsupported/cmath.rs
index 304cf906b2a..304cf906b2a 100644
--- a/src/libstd/sys/wasm/cmath.rs
+++ b/src/libstd/sys/unsupported/cmath.rs
diff --git a/src/libstd/sys/unsupported/common.rs b/src/libstd/sys/unsupported/common.rs
new file mode 100644
index 00000000000..80311d26819
--- /dev/null
+++ b/src/libstd/sys/unsupported/common.rs
@@ -0,0 +1,48 @@
+use crate::io as std_io;
+
+pub mod memchr {
+    pub use core::slice::memchr::{memchr, memrchr};
+}
+
+pub use crate::sys_common::os_str_bytes as os_str;
+
+// This is not necessarily correct. May want to consider making it part of the
+// spec definition?
+use crate::os::raw::c_char;
+
+#[cfg(not(test))]
+pub fn init() {}
+
+pub fn unsupported<T>() -> std_io::Result<T> {
+    Err(unsupported_err())
+}
+
+pub fn unsupported_err() -> std_io::Error {
+    std_io::Error::new(std_io::ErrorKind::Other, "operation not supported on this platform")
+}
+
+pub fn decode_error_kind(_code: i32) -> crate::io::ErrorKind {
+    crate::io::ErrorKind::Other
+}
+
+pub fn abort_internal() -> ! {
+    core::intrinsics::abort();
+}
+
+pub fn hashmap_random_keys() -> (u64, u64) {
+    (1, 2)
+}
+
+// This enum is used as the storage for a bunch of types which can't actually
+// exist.
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
+pub enum Void {}
+
+pub unsafe fn strlen(mut s: *const c_char) -> usize {
+    let mut n = 0;
+    while *s != 0 {
+        n += 1;
+        s = s.offset(1);
+    }
+    return n;
+}
diff --git a/src/libstd/sys/wasm/condvar.rs b/src/libstd/sys/unsupported/condvar.rs
index 9fd781c7282..a578eee8ccc 100644
--- a/src/libstd/sys/wasm/condvar.rs
+++ b/src/libstd/sys/unsupported/condvar.rs
@@ -18,11 +18,11 @@ impl Condvar {
     pub unsafe fn notify_all(&self) {}
 
     pub unsafe fn wait(&self, _mutex: &Mutex) {
-        panic!("can't block with web assembly")
+        panic!("condvar wait not supported")
     }
 
     pub unsafe fn wait_timeout(&self, _mutex: &Mutex, _dur: Duration) -> bool {
-        panic!("can't block with web assembly");
+        panic!("condvar wait not supported");
     }
 
     #[inline]
diff --git a/src/libstd/sys/unsupported/env.rs b/src/libstd/sys/unsupported/env.rs
new file mode 100644
index 00000000000..d2efec506c5
--- /dev/null
+++ b/src/libstd/sys/unsupported/env.rs
@@ -0,0 +1,9 @@
+pub mod os {
+    pub const FAMILY: &str = "";
+    pub const OS: &str = "";
+    pub const DLL_PREFIX: &str = "";
+    pub const DLL_SUFFIX: &str = "";
+    pub const DLL_EXTENSION: &str = "";
+    pub const EXE_SUFFIX: &str = "";
+    pub const EXE_EXTENSION: &str = "";
+}
diff --git a/src/libstd/sys/wasm/fs.rs b/src/libstd/sys/unsupported/fs.rs
index ecb5b51cccd..ecb5b51cccd 100644
--- a/src/libstd/sys/wasm/fs.rs
+++ b/src/libstd/sys/unsupported/fs.rs
diff --git a/src/libstd/sys/wasm/io.rs b/src/libstd/sys/unsupported/io.rs
index d5f475b4310..d5f475b4310 100644
--- a/src/libstd/sys/wasm/io.rs
+++ b/src/libstd/sys/unsupported/io.rs
diff --git a/src/libstd/sys/unsupported/mod.rs b/src/libstd/sys/unsupported/mod.rs
new file mode 100644
index 00000000000..87f655eecd5
--- /dev/null
+++ b/src/libstd/sys/unsupported/mod.rs
@@ -0,0 +1,24 @@
+pub mod alloc;
+pub mod args;
+pub mod cmath;
+pub mod condvar;
+pub mod env;
+pub mod fs;
+pub mod io;
+pub mod mutex;
+pub mod net;
+pub mod os;
+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)]
+pub mod thread_local_dtor;
+pub mod thread_local_key;
+pub mod time;
+
+mod common;
+pub use common::*;
diff --git a/src/libstd/sys/wasm/mutex.rs b/src/libstd/sys/unsupported/mutex.rs
index 7aaf1b3a343..9ef8af52eb5 100644
--- a/src/libstd/sys/wasm/mutex.rs
+++ b/src/libstd/sys/unsupported/mutex.rs
@@ -5,9 +5,10 @@ pub struct Mutex {
 }
 
 unsafe impl Send for Mutex {}
-unsafe impl Sync for Mutex {} // no threads on wasm
+unsafe impl Sync for Mutex {} // no threads on this platform
 
 impl Mutex {
+    #[rustc_const_stable(feature = "const_sys_mutex_new", since = "1.0.0")]
     pub const fn new() -> Mutex {
         Mutex { locked: UnsafeCell::new(false) }
     }
@@ -42,8 +43,8 @@ impl Mutex {
     pub unsafe fn destroy(&self) {}
 }
 
-// All empty stubs because wasm has no threads yet, so lock acquisition always
-// succeeds.
+// All empty stubs because this platform does not yet support threads, so lock
+// acquisition always succeeds.
 pub struct ReentrantMutex {}
 
 impl ReentrantMutex {
diff --git a/src/libstd/sys/wasm/net.rs b/src/libstd/sys/unsupported/net.rs
index 5c9f1098f9b..5c9f1098f9b 100644
--- a/src/libstd/sys/wasm/net.rs
+++ b/src/libstd/sys/unsupported/net.rs
diff --git a/src/libstd/sys/wasm/os.rs b/src/libstd/sys/unsupported/os.rs
index 91afdc8a5a0..0615780c242 100644
--- a/src/libstd/sys/wasm/os.rs
+++ b/src/libstd/sys/unsupported/os.rs
@@ -1,10 +1,9 @@
+use super::{unsupported, Void};
 use crate::error::Error as StdError;
 use crate::ffi::{OsStr, OsString};
 use crate::fmt;
 use crate::io;
 use crate::path::{self, PathBuf};
-use crate::str;
-use crate::sys::{unsupported, Void};
 
 pub fn errno() -> i32 {
     0
@@ -48,14 +47,14 @@ where
 
 impl fmt::Display for JoinPathsError {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        "not supported on wasm yet".fmt(f)
+        "not supported on this platform yet".fmt(f)
     }
 }
 
 impl StdError for JoinPathsError {
     #[allow(deprecated)]
     fn description(&self) -> &str {
-        "not supported on wasm yet"
+        "not supported on this platform yet"
     }
 }
 
@@ -73,7 +72,7 @@ impl Iterator for Env {
 }
 
 pub fn env() -> Env {
-    panic!("not supported on web assembly")
+    panic!("not supported on this platform")
 }
 
 pub fn getenv(_: &OsStr) -> io::Result<Option<OsString>> {
@@ -81,15 +80,15 @@ pub fn getenv(_: &OsStr) -> io::Result<Option<OsString>> {
 }
 
 pub fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> {
-    Err(io::Error::new(io::ErrorKind::Other, "cannot set env vars on wasm32-unknown-unknown"))
+    Err(io::Error::new(io::ErrorKind::Other, "cannot set env vars on this platform"))
 }
 
 pub fn unsetenv(_: &OsStr) -> io::Result<()> {
-    Err(io::Error::new(io::ErrorKind::Other, "cannot unset env vars on wasm32-unknown-unknown"))
+    Err(io::Error::new(io::ErrorKind::Other, "cannot unset env vars on this platform"))
 }
 
 pub fn temp_dir() -> PathBuf {
-    panic!("no filesystem on wasm")
+    panic!("no filesystem on this platform")
 }
 
 pub fn home_dir() -> Option<PathBuf> {
@@ -97,11 +96,9 @@ pub fn home_dir() -> Option<PathBuf> {
 }
 
 pub fn exit(_code: i32) -> ! {
-    unsafe {
-        crate::arch::wasm32::unreachable();
-    }
+    crate::intrinsics::abort()
 }
 
 pub fn getpid() -> u32 {
-    panic!("no pids on wasm")
+    panic!("no pids on this platform")
 }
diff --git a/src/libstd/sys/wasm/path.rs b/src/libstd/sys/unsupported/path.rs
index 840a7ae0426..840a7ae0426 100644
--- a/src/libstd/sys/wasm/path.rs
+++ b/src/libstd/sys/unsupported/path.rs
diff --git a/src/libstd/sys/wasm/pipe.rs b/src/libstd/sys/unsupported/pipe.rs
index 10d0925823e..10d0925823e 100644
--- a/src/libstd/sys/wasm/pipe.rs
+++ b/src/libstd/sys/unsupported/pipe.rs
diff --git a/src/libstd/sys/wasm/process.rs b/src/libstd/sys/unsupported/process.rs
index 4702e5c5492..4702e5c5492 100644
--- a/src/libstd/sys/wasm/process.rs
+++ b/src/libstd/sys/unsupported/process.rs
diff --git a/src/libstd/sys/wasm/rwlock.rs b/src/libstd/sys/unsupported/rwlock.rs
index a59944482e9..d37f34ac935 100644
--- a/src/libstd/sys/wasm/rwlock.rs
+++ b/src/libstd/sys/unsupported/rwlock.rs
@@ -5,7 +5,7 @@ pub struct RWLock {
 }
 
 unsafe impl Send for RWLock {}
-unsafe impl Sync for RWLock {} // no threads on wasm
+unsafe impl Sync for RWLock {} // no threads on this platform
 
 impl RWLock {
     pub const fn new() -> RWLock {
diff --git a/src/libstd/sys/wasm/stack_overflow.rs b/src/libstd/sys/unsupported/stack_overflow.rs
index 32555394cd5..32555394cd5 100644
--- a/src/libstd/sys/wasm/stack_overflow.rs
+++ b/src/libstd/sys/unsupported/stack_overflow.rs
diff --git a/src/libstd/sys/wasm/stdio.rs b/src/libstd/sys/unsupported/stdio.rs
index 5a4e4505e93..5a4e4505e93 100644
--- a/src/libstd/sys/wasm/stdio.rs
+++ b/src/libstd/sys/unsupported/stdio.rs
diff --git a/src/libstd/sys/unsupported/thread.rs b/src/libstd/sys/unsupported/thread.rs
new file mode 100644
index 00000000000..20ae309db30
--- /dev/null
+++ b/src/libstd/sys/unsupported/thread.rs
@@ -0,0 +1,41 @@
+use super::{unsupported, Void};
+use crate::ffi::CStr;
+use crate::io;
+use crate::time::Duration;
+
+pub struct Thread(Void);
+
+pub const DEFAULT_MIN_STACK_SIZE: usize = 4096;
+
+impl Thread {
+    // unsafe: see thread::Builder::spawn_unchecked for safety requirements
+    pub unsafe fn new(_stack: usize, _p: Box<dyn FnOnce()>) -> io::Result<Thread> {
+        unsupported()
+    }
+
+    pub fn yield_now() {
+        // do nothing
+    }
+
+    pub fn set_name(_name: &CStr) {
+        // nope
+    }
+
+    pub fn sleep(_dur: Duration) {
+        panic!("can't sleep");
+    }
+
+    pub fn join(self) {
+        match self.0 {}
+    }
+}
+
+pub mod guard {
+    pub type Guard = !;
+    pub unsafe fn current() -> Option<Guard> {
+        None
+    }
+    pub unsafe fn init() -> Option<Guard> {
+        None
+    }
+}
diff --git a/src/libstd/sys/wasm/thread_local_dtor.rs b/src/libstd/sys/unsupported/thread_local_dtor.rs
index 85d66098302..85d66098302 100644
--- a/src/libstd/sys/wasm/thread_local_dtor.rs
+++ b/src/libstd/sys/unsupported/thread_local_dtor.rs
diff --git a/src/libstd/sys/wasm/thread_local_key.rs b/src/libstd/sys/unsupported/thread_local_key.rs
index f8be9863ed5..c31b61cbf56 100644
--- a/src/libstd/sys/wasm/thread_local_key.rs
+++ b/src/libstd/sys/unsupported/thread_local_key.rs
@@ -2,25 +2,25 @@ pub type Key = usize;
 
 #[inline]
 pub unsafe fn create(_dtor: Option<unsafe extern "C" fn(*mut u8)>) -> Key {
-    panic!("should not be used on the wasm target");
+    panic!("should not be used on this target");
 }
 
 #[inline]
 pub unsafe fn set(_key: Key, _value: *mut u8) {
-    panic!("should not be used on the wasm target");
+    panic!("should not be used on this target");
 }
 
 #[inline]
 pub unsafe fn get(_key: Key) -> *mut u8 {
-    panic!("should not be used on the wasm target");
+    panic!("should not be used on this target");
 }
 
 #[inline]
 pub unsafe fn destroy(_key: Key) {
-    panic!("should not be used on the wasm target");
+    panic!("should not be used on this target");
 }
 
 #[inline]
 pub fn requires_synchronized_create() -> bool {
-    panic!("should not be used on the wasm target");
+    panic!("should not be used on this target");
 }
diff --git a/src/libstd/sys/wasm/time.rs b/src/libstd/sys/unsupported/time.rs
index d9edc7fdc44..8aaf1777f24 100644
--- a/src/libstd/sys/wasm/time.rs
+++ b/src/libstd/sys/unsupported/time.rs
@@ -10,7 +10,7 @@ pub const UNIX_EPOCH: SystemTime = SystemTime(Duration::from_secs(0));
 
 impl Instant {
     pub fn now() -> Instant {
-        panic!("time not implemented on wasm32-unknown-unknown")
+        panic!("time not implemented on this platform")
     }
 
     pub const fn zero() -> Instant {
@@ -36,7 +36,7 @@ impl Instant {
 
 impl SystemTime {
     pub fn now() -> SystemTime {
-        panic!("time not implemented on wasm32-unknown-unknown")
+        panic!("time not implemented on this platform")
     }
 
     pub fn sub_time(&self, other: &SystemTime) -> Result<Duration, Duration> {
diff --git a/src/libstd/sys/wasi/mod.rs b/src/libstd/sys/wasi/mod.rs
index 85f5282034f..2704ff484f9 100644
--- a/src/libstd/sys/wasi/mod.rs
+++ b/src/libstd/sys/wasi/mod.rs
@@ -16,21 +16,18 @@
 
 use crate::io as std_io;
 use crate::mem;
-use crate::os::raw::c_char;
 
 pub mod alloc;
 pub mod args;
-#[path = "../wasm/cmath.rs"]
+#[path = "../unsupported/cmath.rs"]
 pub mod cmath;
-#[path = "../wasm/condvar.rs"]
+#[path = "../unsupported/condvar.rs"]
 pub mod condvar;
 pub mod env;
 pub mod fd;
 pub mod fs;
 pub mod io;
-#[path = "../wasm/memchr.rs"]
-pub mod memchr;
-#[path = "../wasm/mutex.rs"]
+#[path = "../unsupported/mutex.rs"]
 pub mod mutex;
 pub mod net;
 pub mod os;
@@ -39,28 +36,22 @@ pub mod ext;
 pub mod path;
 pub mod pipe;
 pub mod process;
-#[path = "../wasm/rwlock.rs"]
+#[path = "../unsupported/rwlock.rs"]
 pub mod rwlock;
-#[path = "../wasm/stack_overflow.rs"]
+#[path = "../unsupported/stack_overflow.rs"]
 pub mod stack_overflow;
 pub mod stdio;
 pub mod thread;
-#[path = "../wasm/thread_local_dtor.rs"]
+#[path = "../unsupported/thread_local_dtor.rs"]
 pub mod thread_local_dtor;
-#[path = "../wasm/thread_local_key.rs"]
+#[path = "../unsupported/thread_local_key.rs"]
 pub mod thread_local_key;
 pub mod time;
 
-#[cfg(not(test))]
-pub fn init() {}
-
-pub fn unsupported<T>() -> std_io::Result<T> {
-    Err(unsupported_err())
-}
-
-pub fn unsupported_err() -> std_io::Error {
-    std_io::Error::new(std_io::ErrorKind::Other, "operation not supported on wasm yet")
-}
+#[path = "../unsupported/common.rs"]
+#[allow(unused)]
+mod common;
+pub use common::*;
 
 pub fn decode_error_kind(errno: i32) -> std_io::ErrorKind {
     use std_io::ErrorKind::*;
@@ -86,20 +77,6 @@ pub fn decode_error_kind(errno: i32) -> std_io::ErrorKind {
     }
 }
 
-// This enum is used as the storage for a bunch of types which can't actually
-// exist.
-#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
-pub enum Void {}
-
-pub unsafe fn strlen(mut s: *const c_char) -> usize {
-    let mut n = 0;
-    while *s != 0 {
-        n += 1;
-        s = s.offset(1);
-    }
-    return n;
-}
-
 pub fn abort_internal() -> ! {
     unsafe { libc::abort() }
 }
diff --git a/src/libstd/sys/wasm/memchr.rs b/src/libstd/sys/wasm/memchr.rs
deleted file mode 100644
index 9967482197e..00000000000
--- a/src/libstd/sys/wasm/memchr.rs
+++ /dev/null
@@ -1 +0,0 @@
-pub use core::slice::memchr::{memchr, memrchr};
diff --git a/src/libstd/sys/wasm/mod.rs b/src/libstd/sys/wasm/mod.rs
index 6939596e52d..3de58904043 100644
--- a/src/libstd/sys/wasm/mod.rs
+++ b/src/libstd/sys/wasm/mod.rs
@@ -14,25 +14,35 @@
 //! compiling for wasm. That way it's a compile time error for something that's
 //! guaranteed to be a runtime error!
 
-use crate::os::raw::c_char;
-
 pub mod alloc;
 pub mod args;
+#[path = "../unsupported/cmath.rs"]
 pub mod cmath;
 pub mod env;
+#[path = "../unsupported/fs.rs"]
 pub mod fs;
+#[path = "../unsupported/io.rs"]
 pub mod io;
-pub mod memchr;
+#[path = "../unsupported/net.rs"]
 pub mod net;
+#[path = "../unsupported/os.rs"]
 pub mod os;
+#[path = "../unsupported/path.rs"]
 pub mod path;
+#[path = "../unsupported/pipe.rs"]
 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;
+#[path = "../unsupported/thread_local_dtor.rs"]
 pub mod thread_local_dtor;
+#[path = "../unsupported/thread_local_key.rs"]
 pub mod thread_local_key;
+#[path = "../unsupported/time.rs"]
 pub mod time;
 
 pub use crate::sys_common::os_str_bytes as os_str;
@@ -46,50 +56,15 @@ cfg_if::cfg_if! {
         #[path = "rwlock_atomics.rs"]
         pub mod rwlock;
     } else {
+        #[path = "../unsupported/condvar.rs"]
         pub mod condvar;
+        #[path = "../unsupported/mutex.rs"]
         pub mod mutex;
+        #[path = "../unsupported/rwlock.rs"]
         pub mod rwlock;
     }
 }
 
-#[cfg(not(test))]
-pub fn init() {}
-
-pub fn unsupported<T>() -> crate::io::Result<T> {
-    Err(unsupported_err())
-}
-
-pub fn unsupported_err() -> crate::io::Error {
-    crate::io::Error::new(crate::io::ErrorKind::Other, "operation not supported on wasm yet")
-}
-
-pub fn decode_error_kind(_code: i32) -> crate::io::ErrorKind {
-    crate::io::ErrorKind::Other
-}
-
-// This enum is used as the storage for a bunch of types which can't actually
-// exist.
-#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
-pub enum Void {}
-
-pub unsafe fn strlen(mut s: *const c_char) -> usize {
-    let mut n = 0;
-    while *s != 0 {
-        n += 1;
-        s = s.offset(1);
-    }
-    return n;
-}
-
-pub fn abort_internal() -> ! {
-    unsafe { crate::arch::wasm32::unreachable() }
-}
-
-// We don't have randomness yet, but I totally used a random number generator to
-// generate these numbers.
-//
-// More seriously though this is just for DOS protection in hash maps. It's ok
-// if we don't do that on wasm just yet.
-pub fn hashmap_random_keys() -> (u64, u64) {
-    (1, 2)
-}
+#[path = "../unsupported/common.rs"]
+mod common;
+pub use common::*;
diff --git a/src/libstd/sys_common/mod.rs b/src/libstd/sys_common/mod.rs
index e57bb267cbd..840f9093e00 100644
--- a/src/libstd/sys_common/mod.rs
+++ b/src/libstd/sys_common/mod.rs
@@ -51,13 +51,9 @@ pub mod condvar;
 pub mod fs;
 pub mod io;
 pub mod mutex;
-#[cfg(any(doc, // see `mod os`, docs are generated for multiple platforms
-          unix,
-          target_os = "redox",
-          target_os = "cloudabi",
-          target_os = "hermit",
-          target_arch = "wasm32",
-          all(target_vendor = "fortanix", target_env = "sgx")))]
+// `doc` is required because `sys/mod.rs` imports `unix/ext/mod.rs` on Windows
+// when generating documentation.
+#[cfg(any(doc, not(windows)))]
 pub mod os_str_bytes;
 pub mod poison;
 pub mod process;
@@ -74,6 +70,7 @@ cfg_if::cfg_if! {
     if #[cfg(any(target_os = "cloudabi",
                  target_os = "l4re",
                  target_os = "hermit",
+                 feature = "restricted-std",
                  all(target_arch = "wasm32", not(target_os = "emscripten")),
                  all(target_vendor = "fortanix", target_env = "sgx")))] {
         pub use crate::sys::net;
diff --git a/src/libstd/sys_common/mutex.rs b/src/libstd/sys_common/mutex.rs
index 899fc6a7235..e66d8994147 100644
--- a/src/libstd/sys_common/mutex.rs
+++ b/src/libstd/sys_common/mutex.rs
@@ -17,6 +17,7 @@ impl Mutex {
     /// Also, until `init` is called, behavior is undefined if this
     /// mutex is ever used reentrantly, i.e., `raw_lock` or `try_lock`
     /// are called by the thread currently holding the lock.
+    #[rustc_const_stable(feature = "const_sys_mutex_new", since = "1.0.0")]
     pub const fn new() -> Mutex {
         Mutex(imp::Mutex::new())
     }
diff --git a/src/libstd/thread/mod.rs b/src/libstd/thread/mod.rs
index d354a9b1842..202867258f1 100644
--- a/src/libstd/thread/mod.rs
+++ b/src/libstd/thread/mod.rs
@@ -1741,7 +1741,6 @@ mod tests {
     }
 
     #[test]
-    #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31
     fn test_park_timeout_unpark_not_called() {
         for _ in 0..10 {
             thread::park_timeout(Duration::from_millis(10));
@@ -1749,7 +1748,6 @@ mod tests {
     }
 
     #[test]
-    #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31
     fn test_park_timeout_unpark_called_other_thread() {
         for _ in 0..10 {
             let th = thread::current();
@@ -1764,7 +1762,6 @@ mod tests {
     }
 
     #[test]
-    #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31
     fn sleep_ms_smoke() {
         thread::sleep(Duration::from_millis(2));
     }