about summary refs log tree commit diff
path: root/src/libstd/sys/wasi
diff options
context:
space:
mode:
Diffstat (limited to 'src/libstd/sys/wasi')
-rw-r--r--src/libstd/sys/wasi/alloc.rs43
-rw-r--r--src/libstd/sys/wasi/args.rs78
-rw-r--r--src/libstd/sys/wasi/backtrace.rs27
-rw-r--r--src/libstd/sys/wasi/env.rs9
-rw-r--r--src/libstd/sys/wasi/ext/ffi.rs61
-rw-r--r--src/libstd/sys/wasi/ext/mod.rs10
-rw-r--r--src/libstd/sys/wasi/fd.rs322
-rw-r--r--src/libstd/sys/wasi/fs.rs294
-rw-r--r--src/libstd/sys/wasi/io.rs62
-rw-r--r--src/libstd/sys/wasi/mod.rs128
-rw-r--r--src/libstd/sys/wasi/net.rs358
-rw-r--r--src/libstd/sys/wasi/os.rs171
-rw-r--r--src/libstd/sys/wasi/path.rs19
-rw-r--r--src/libstd/sys/wasi/pipe.rs25
-rw-r--r--src/libstd/sys/wasi/process.rs152
-rw-r--r--src/libstd/sys/wasi/stdio.rs74
-rw-r--r--src/libstd/sys/wasi/thread.rs57
-rw-r--r--src/libstd/sys/wasi/time.rs72
18 files changed, 1962 insertions, 0 deletions
diff --git a/src/libstd/sys/wasi/alloc.rs b/src/libstd/sys/wasi/alloc.rs
new file mode 100644
index 00000000000..c8529937bbd
--- /dev/null
+++ b/src/libstd/sys/wasi/alloc.rs
@@ -0,0 +1,43 @@
+use crate::alloc::{GlobalAlloc, Layout, System};
+use crate::ptr;
+use crate::sys_common::alloc::{MIN_ALIGN, realloc_fallback};
+use libc;
+
+#[stable(feature = "alloc_system_type", since = "1.28.0")]
+unsafe impl GlobalAlloc for System {
+    #[inline]
+    unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
+        if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() {
+            libc::malloc(layout.size()) as *mut u8
+        } else {
+            libc::aligned_alloc(layout.size(), layout.align()) as *mut u8
+        }
+    }
+
+    #[inline]
+    unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
+        if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() {
+            libc::calloc(layout.size(), 1) as *mut u8
+        } else {
+            let ptr = self.alloc(layout.clone());
+            if !ptr.is_null() {
+                ptr::write_bytes(ptr, 0, layout.size());
+            }
+            ptr
+        }
+    }
+
+    #[inline]
+    unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
+        libc::free(ptr as *mut libc::c_void)
+    }
+
+    #[inline]
+    unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
+        if layout.align() <= MIN_ALIGN && layout.align() <= new_size {
+            libc::realloc(ptr as *mut libc::c_void, new_size) as *mut u8
+        } else {
+            realloc_fallback(self, ptr, layout, new_size)
+        }
+    }
+}
diff --git a/src/libstd/sys/wasi/args.rs b/src/libstd/sys/wasi/args.rs
new file mode 100644
index 00000000000..20558a8042d
--- /dev/null
+++ b/src/libstd/sys/wasi/args.rs
@@ -0,0 +1,78 @@
+use crate::any::Any;
+use crate::ffi::CStr;
+use crate::ffi::OsString;
+use crate::marker::PhantomData;
+use crate::os::wasi::ffi::OsStringExt;
+use crate::ptr;
+use crate::vec;
+
+static mut ARGC: isize = 0;
+static mut ARGV: *const *const u8 = ptr::null();
+
+#[cfg(not(target_feature = "atomics"))]
+pub unsafe fn args_lock() -> impl Any {
+    // No need for a lock if we're single-threaded, but this function will need
+    // to get implemented for multi-threaded scenarios
+}
+
+pub unsafe fn init(argc: isize, argv: *const *const u8) {
+    let _guard = args_lock();
+    ARGC = argc;
+    ARGV = argv;
+}
+
+pub unsafe fn cleanup() {
+    let _guard = args_lock();
+    ARGC = 0;
+    ARGV = ptr::null();
+}
+
+pub struct Args {
+    iter: vec::IntoIter<OsString>,
+    _dont_send_or_sync_me: PhantomData<*mut ()>,
+}
+
+/// Returns the command line arguments
+pub fn args() -> Args {
+    unsafe {
+        let _guard = args_lock();
+        let args = (0..ARGC)
+            .map(|i| {
+                let cstr = CStr::from_ptr(*ARGV.offset(i) as *const libc::c_char);
+                OsStringExt::from_vec(cstr.to_bytes().to_vec())
+            })
+            .collect::<Vec<_>>();
+        Args {
+            iter: args.into_iter(),
+            _dont_send_or_sync_me: PhantomData,
+        }
+    }
+}
+
+impl Args {
+    pub fn inner_debug(&self) -> &[OsString] {
+        self.iter.as_slice()
+    }
+}
+
+impl Iterator for Args {
+    type Item = OsString;
+    fn next(&mut self) -> Option<OsString> {
+        self.iter.next()
+    }
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        self.iter.size_hint()
+    }
+}
+
+impl ExactSizeIterator for Args {
+    fn len(&self) -> usize {
+        self.iter.len()
+    }
+}
+
+impl DoubleEndedIterator for Args {
+    fn next_back(&mut self) -> Option<OsString> {
+        self.iter.next_back()
+    }
+}
diff --git a/src/libstd/sys/wasi/backtrace.rs b/src/libstd/sys/wasi/backtrace.rs
new file mode 100644
index 00000000000..7d56b298997
--- /dev/null
+++ b/src/libstd/sys/wasi/backtrace.rs
@@ -0,0 +1,27 @@
+use crate::io;
+use crate::sys::unsupported;
+use crate::sys_common::backtrace::Frame;
+
+pub struct BacktraceContext;
+
+pub fn unwind_backtrace(_frames: &mut [Frame])
+    -> io::Result<(usize, BacktraceContext)>
+{
+    unsupported()
+}
+
+pub fn resolve_symname<F>(_frame: Frame,
+                          _callback: F,
+                          _: &BacktraceContext) -> io::Result<()>
+    where F: FnOnce(Option<&str>) -> io::Result<()>
+{
+    unsupported()
+}
+
+pub fn foreach_symbol_fileline<F>(_: Frame,
+                                  _: F,
+                                  _: &BacktraceContext) -> io::Result<bool>
+    where F: FnMut(&[u8], u32) -> io::Result<()>
+{
+    unsupported()
+}
diff --git a/src/libstd/sys/wasi/env.rs b/src/libstd/sys/wasi/env.rs
new file mode 100644
index 00000000000..730e356d7fe
--- /dev/null
+++ b/src/libstd/sys/wasi/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 = ".wasm";
+    pub const DLL_EXTENSION: &str = "wasm";
+    pub const EXE_SUFFIX: &str = ".wasm";
+    pub const EXE_EXTENSION: &str = "wasm";
+}
diff --git a/src/libstd/sys/wasi/ext/ffi.rs b/src/libstd/sys/wasi/ext/ffi.rs
new file mode 100644
index 00000000000..07b93dd143f
--- /dev/null
+++ b/src/libstd/sys/wasi/ext/ffi.rs
@@ -0,0 +1,61 @@
+//! WASI-specific extension to the primitives in the `std::ffi` module
+
+#![stable(feature = "rust1", since = "1.0.0")]
+
+use crate::ffi::{OsStr, OsString};
+use crate::mem;
+use crate::sys::os_str::Buf;
+use crate::sys_common::{FromInner, IntoInner, AsInner};
+
+/// WASI-specific extensions to [`OsString`].
+///
+/// [`OsString`]: ../../../../std/ffi/struct.OsString.html
+#[stable(feature = "rust1", since = "1.0.0")]
+pub trait OsStringExt {
+    /// Creates an `OsString` from a byte vector.
+    #[stable(feature = "rust1", since = "1.0.0")]
+    fn from_vec(vec: Vec<u8>) -> Self;
+
+    /// Yields the underlying byte vector of this `OsString`.
+    #[stable(feature = "rust1", since = "1.0.0")]
+    fn into_vec(self) -> Vec<u8>;
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl OsStringExt for OsString {
+    fn from_vec(vec: Vec<u8>) -> OsString {
+        FromInner::from_inner(Buf { inner: vec })
+    }
+    fn into_vec(self) -> Vec<u8> {
+        self.into_inner().inner
+    }
+}
+
+/// WASI-specific extensions to [`OsStr`].
+///
+/// [`OsStr`]: ../../../../std/ffi/struct.OsStr.html
+#[stable(feature = "rust1", since = "1.0.0")]
+pub trait OsStrExt {
+    #[stable(feature = "rust1", since = "1.0.0")]
+    /// Creates an [`OsStr`] from a byte slice.
+    ///
+    /// [`OsStr`]: ../../../ffi/struct.OsStr.html
+    fn from_bytes(slice: &[u8]) -> &Self;
+
+    /// Gets the underlying byte view of the [`OsStr`] slice.
+    ///
+    /// [`OsStr`]: ../../../ffi/struct.OsStr.html
+    #[stable(feature = "rust1", since = "1.0.0")]
+    fn as_bytes(&self) -> &[u8];
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl OsStrExt for OsStr {
+    fn from_bytes(slice: &[u8]) -> &OsStr {
+        unsafe { mem::transmute(slice) }
+    }
+    fn as_bytes(&self) -> &[u8] {
+        &self.as_inner().inner
+    }
+}
+
diff --git a/src/libstd/sys/wasi/ext/mod.rs b/src/libstd/sys/wasi/ext/mod.rs
new file mode 100644
index 00000000000..877b9ed89d8
--- /dev/null
+++ b/src/libstd/sys/wasi/ext/mod.rs
@@ -0,0 +1,10 @@
+pub mod ffi;
+
+/// A prelude for conveniently writing platform-specific code.
+///
+/// Includes all extension traits, and some important type definitions.
+#[stable(feature = "rust1", since = "1.0.0")]
+pub mod prelude {
+    #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")]
+    pub use crate::sys::ext::ffi::{OsStringExt, OsStrExt};
+}
diff --git a/src/libstd/sys/wasi/fd.rs b/src/libstd/sys/wasi/fd.rs
new file mode 100644
index 00000000000..1e05e701759
--- /dev/null
+++ b/src/libstd/sys/wasi/fd.rs
@@ -0,0 +1,322 @@
+#![allow(dead_code)]
+
+use crate::io::{self, IoVec, IoVecMut, SeekFrom};
+use crate::mem;
+use crate::net::Shutdown;
+use crate::sys::cvt_wasi;
+use libc::{self, c_char, c_void};
+
+pub struct WasiFd {
+    fd: libc::__wasi_fd_t,
+}
+
+// FIXME: these should probably all be fancier structs, builders, enums, etc
+pub type LookupFlags = u32;
+pub type FdFlags = u16;
+pub type Advice = u8;
+pub type Rights = u64;
+pub type Oflags = u16;
+pub type DirCookie = u64;
+pub type Timestamp = u64;
+pub type FstFlags = u16;
+pub type RiFlags = u16;
+pub type RoFlags = u16;
+pub type SiFlags = u16;
+
+fn iovec(a: &mut [IoVecMut]) -> (*const libc::__wasi_iovec_t, usize) {
+    assert_eq!(
+        mem::size_of::<IoVecMut>(),
+        mem::size_of::<libc::__wasi_iovec_t>()
+    );
+    assert_eq!(
+        mem::align_of::<IoVecMut>(),
+        mem::align_of::<libc::__wasi_iovec_t>()
+    );
+    (a.as_ptr() as *const libc::__wasi_iovec_t, a.len())
+}
+
+fn ciovec(a: &[IoVec]) -> (*const libc::__wasi_ciovec_t, usize) {
+    assert_eq!(
+        mem::size_of::<IoVec>(),
+        mem::size_of::<libc::__wasi_ciovec_t>()
+    );
+    assert_eq!(
+        mem::align_of::<IoVec>(),
+        mem::align_of::<libc::__wasi_ciovec_t>()
+    );
+    (a.as_ptr() as *const libc::__wasi_ciovec_t, a.len())
+}
+
+impl WasiFd {
+    pub unsafe fn from_raw(fd: libc::__wasi_fd_t) -> WasiFd {
+        WasiFd { fd }
+    }
+
+    pub fn datasync(&self) -> io::Result<()> {
+        cvt_wasi(unsafe { libc::__wasi_fd_datasync(self.fd) })
+    }
+
+    pub fn pread(&self, bufs: &mut [IoVecMut], offset: u64) -> io::Result<usize> {
+        let mut read = 0;
+        let (ptr, len) = iovec(bufs);
+        cvt_wasi(unsafe { libc::__wasi_fd_pread(self.fd, ptr, len, offset, &mut read) })?;
+        Ok(read)
+    }
+
+    pub fn pwrite(&self, bufs: &[IoVec], offset: u64) -> io::Result<usize> {
+        let mut read = 0;
+        let (ptr, len) = ciovec(bufs);
+        cvt_wasi(unsafe { libc::__wasi_fd_pwrite(self.fd, ptr, len, offset, &mut read) })?;
+        Ok(read)
+    }
+
+    pub fn read(&self, bufs: &mut [IoVecMut]) -> io::Result<usize> {
+        let mut read = 0;
+        let (ptr, len) = iovec(bufs);
+        cvt_wasi(unsafe { libc::__wasi_fd_read(self.fd, ptr, len, &mut read) })?;
+        Ok(read)
+    }
+
+    pub fn write(&self, bufs: &[IoVec]) -> io::Result<usize> {
+        let mut read = 0;
+        let (ptr, len) = ciovec(bufs);
+        cvt_wasi(unsafe { libc::__wasi_fd_write(self.fd, ptr, len, &mut read) })?;
+        Ok(read)
+    }
+
+    pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> {
+        let (whence, offset) = match pos {
+            SeekFrom::Start(pos) => (libc::__WASI_WHENCE_SET, pos as i64),
+            SeekFrom::End(pos) => (libc::__WASI_WHENCE_END, pos),
+            SeekFrom::Current(pos) => (libc::__WASI_WHENCE_CUR, pos),
+        };
+        let mut pos = 0;
+        cvt_wasi(unsafe { libc::__wasi_fd_seek(self.fd, offset, whence, &mut pos) })?;
+        Ok(pos)
+    }
+
+    pub fn tell(&self) -> io::Result<u64> {
+        let mut pos = 0;
+        cvt_wasi(unsafe { libc::__wasi_fd_tell(self.fd, &mut pos) })?;
+        Ok(pos)
+    }
+
+    // FIXME: __wasi_fd_fdstat_get
+
+    pub fn set_flags(&self, flags: FdFlags) -> io::Result<()> {
+        cvt_wasi(unsafe { libc::__wasi_fd_fdstat_set_flags(self.fd, flags) })
+    }
+
+    pub fn set_rights(&self, base: Rights, inheriting: Rights) -> io::Result<()> {
+        cvt_wasi(unsafe { libc::__wasi_fd_fdstat_set_rights(self.fd, base, inheriting) })
+    }
+
+    pub fn sync(&self) -> io::Result<()> {
+        cvt_wasi(unsafe { libc::__wasi_fd_sync(self.fd) })
+    }
+
+    pub fn advise(&self, offset: u64, len: u64, advice: Advice) -> io::Result<()> {
+        cvt_wasi(unsafe { libc::__wasi_fd_advise(self.fd, offset, len, advice as u8) })
+    }
+
+    pub fn allocate(&self, offset: u64, len: u64) -> io::Result<()> {
+        cvt_wasi(unsafe { libc::__wasi_fd_allocate(self.fd, offset, len) })
+    }
+
+    pub fn crate_directory(&self, path: &[u8]) -> io::Result<()> {
+        cvt_wasi(unsafe {
+            libc::__wasi_path_create_directory(self.fd, path.as_ptr() as *const c_char, path.len())
+        })
+    }
+
+    pub fn link(
+        &self,
+        old_flags: LookupFlags,
+        old_path: &[u8],
+        new_fd: &WasiFd,
+        new_path: &[u8],
+    ) -> io::Result<()> {
+        cvt_wasi(unsafe {
+            libc::__wasi_path_link(
+                self.fd,
+                old_flags,
+                old_path.as_ptr() as *const c_char,
+                old_path.len(),
+                new_fd.fd,
+                new_path.as_ptr() as *const c_char,
+                new_path.len(),
+            )
+        })
+    }
+
+    pub fn open(
+        &self,
+        dirflags: LookupFlags,
+        path: &[u8],
+        oflags: Oflags,
+        fs_rights_base: Rights,
+        fs_rights_inheriting: Rights,
+        fs_flags: FdFlags,
+    ) -> io::Result<WasiFd> {
+        unsafe {
+            let mut fd = 0;
+            cvt_wasi(libc::__wasi_path_open(
+                self.fd,
+                dirflags,
+                path.as_ptr() as *const c_char,
+                path.len(),
+                oflags,
+                fs_rights_base,
+                fs_rights_inheriting,
+                fs_flags,
+                &mut fd,
+            ))?;
+            Ok(WasiFd::from_raw(fd))
+        }
+    }
+
+    pub fn readdir(&self, buf: &mut [u8], cookie: DirCookie) -> io::Result<usize> {
+        let mut used = 0;
+        cvt_wasi(unsafe {
+            libc::__wasi_fd_readdir(
+                self.fd,
+                buf.as_mut_ptr() as *mut c_void,
+                buf.len(),
+                cookie,
+                &mut used,
+            )
+        })?;
+        Ok(used)
+    }
+
+    pub fn readlink(&self, path: &[u8], buf: &mut [u8]) -> io::Result<usize> {
+        let mut used = 0;
+        cvt_wasi(unsafe {
+            libc::__wasi_path_readlink(
+                self.fd,
+                path.as_ptr() as *const c_char,
+                path.len(),
+                buf.as_mut_ptr() as *mut c_char,
+                buf.len(),
+                &mut used,
+            )
+        })?;
+        Ok(used)
+    }
+
+    pub fn rename(&self, old_path: &[u8], new_fd: &WasiFd, new_path: &[u8]) -> io::Result<()> {
+        cvt_wasi(unsafe {
+            libc::__wasi_path_rename(
+                self.fd,
+                old_path.as_ptr() as *const c_char,
+                old_path.len(),
+                new_fd.fd,
+                new_path.as_ptr() as *const c_char,
+                new_path.len(),
+            )
+        })
+    }
+
+    // FIXME: __wasi_fd_filestat_get
+
+    pub fn filestat_set_times(
+        &self,
+        atim: Timestamp,
+        mtim: Timestamp,
+        fstflags: FstFlags,
+    ) -> io::Result<()> {
+        cvt_wasi(unsafe { libc::__wasi_fd_filestat_set_times(self.fd, atim, mtim, fstflags) })
+    }
+
+    pub fn filestat_set_size(&self, size: u64) -> io::Result<()> {
+        cvt_wasi(unsafe { libc::__wasi_fd_filestat_set_size(self.fd, size) })
+    }
+
+    // FIXME: __wasi_path_filestat_get
+
+    pub fn path_filestat_set_times(
+        &self,
+        flags: LookupFlags,
+        path: &[u8],
+        atim: Timestamp,
+        mtim: Timestamp,
+        fstflags: FstFlags,
+    ) -> io::Result<()> {
+        cvt_wasi(unsafe {
+            libc::__wasi_path_filestat_set_times(
+                self.fd,
+                flags,
+                path.as_ptr() as *const c_char,
+                path.len(),
+                atim,
+                mtim,
+                fstflags,
+            )
+        })
+    }
+
+    pub fn symlink(&self, old_path: &[u8], new_path: &[u8]) -> io::Result<()> {
+        cvt_wasi(unsafe {
+            libc::__wasi_path_symlink(
+                old_path.as_ptr() as *const c_char,
+                old_path.len(),
+                self.fd,
+                new_path.as_ptr() as *const c_char,
+                new_path.len(),
+            )
+        })
+    }
+
+    pub fn unlink_file(&self, path: &[u8]) -> io::Result<()> {
+        cvt_wasi(unsafe {
+            libc::__wasi_path_unlink_file(self.fd, path.as_ptr() as *const c_char, path.len())
+        })
+    }
+
+    pub fn remove_directory(&self, path: &[u8]) -> io::Result<()> {
+        cvt_wasi(unsafe {
+            libc::__wasi_path_remove_directory(self.fd, path.as_ptr() as *const c_char, path.len())
+        })
+    }
+
+    pub fn sock_recv(
+        &self,
+        ri_data: &mut [IoVecMut],
+        ri_flags: RiFlags,
+    ) -> io::Result<(usize, RoFlags)> {
+        let mut ro_datalen = 0;
+        let mut ro_flags = 0;
+        let (ptr, len) = iovec(ri_data);
+        cvt_wasi(unsafe {
+            libc::__wasi_sock_recv(self.fd, ptr, len, ri_flags, &mut ro_datalen, &mut ro_flags)
+        })?;
+        Ok((ro_datalen, ro_flags))
+    }
+
+    pub fn sock_send(&self, si_data: &[IoVec], si_flags: SiFlags) -> io::Result<usize> {
+        let mut so_datalen = 0;
+        let (ptr, len) = ciovec(si_data);
+        cvt_wasi(unsafe { libc::__wasi_sock_send(self.fd, ptr, len, si_flags, &mut so_datalen) })?;
+        Ok(so_datalen)
+    }
+
+    pub fn sock_shutdown(&self, how: Shutdown) -> io::Result<()> {
+        let how = match how {
+            Shutdown::Read => libc::__WASI_SHUT_RD,
+            Shutdown::Write => libc::__WASI_SHUT_WR,
+            Shutdown::Both => libc::__WASI_SHUT_WR | libc::__WASI_SHUT_RD,
+        };
+        cvt_wasi(unsafe { libc::__wasi_sock_shutdown(self.fd, how) })?;
+        Ok(())
+    }
+}
+
+impl Drop for WasiFd {
+    fn drop(&mut self) {
+        unsafe {
+            // FIXME: can we handle the return code here even though we can't on
+            // unix?
+            libc::__wasi_fd_close(self.fd);
+        }
+    }
+}
diff --git a/src/libstd/sys/wasi/fs.rs b/src/libstd/sys/wasi/fs.rs
new file mode 100644
index 00000000000..485d2c87fbd
--- /dev/null
+++ b/src/libstd/sys/wasi/fs.rs
@@ -0,0 +1,294 @@
+use crate::ffi::OsString;
+use crate::fmt;
+use crate::hash::{Hash, Hasher};
+use crate::io::{self, SeekFrom};
+use crate::path::{Path, PathBuf};
+use crate::sys::time::SystemTime;
+use crate::sys::{unsupported, Void};
+
+pub struct File(Void);
+
+pub struct FileAttr(Void);
+
+pub struct ReadDir(Void);
+
+pub struct DirEntry(Void);
+
+#[derive(Clone, Debug)]
+pub struct OpenOptions { }
+
+pub struct FilePermissions(Void);
+
+pub struct FileType(Void);
+
+#[derive(Debug)]
+pub struct DirBuilder { }
+
+impl FileAttr {
+    pub fn size(&self) -> u64 {
+        match self.0 {}
+    }
+
+    pub fn perm(&self) -> FilePermissions {
+        match self.0 {}
+    }
+
+    pub fn file_type(&self) -> FileType {
+        match self.0 {}
+    }
+
+    pub fn modified(&self) -> io::Result<SystemTime> {
+        match self.0 {}
+    }
+
+    pub fn accessed(&self) -> io::Result<SystemTime> {
+        match self.0 {}
+    }
+
+    pub fn created(&self) -> io::Result<SystemTime> {
+        match self.0 {}
+    }
+}
+
+impl Clone for FileAttr {
+    fn clone(&self) -> FileAttr {
+        match self.0 {}
+    }
+}
+
+impl FilePermissions {
+    pub fn readonly(&self) -> bool {
+        match self.0 {}
+    }
+
+    pub fn set_readonly(&mut self, _readonly: bool) {
+        match self.0 {}
+    }
+}
+
+impl Clone for FilePermissions {
+    fn clone(&self) -> FilePermissions {
+        match self.0 {}
+    }
+}
+
+impl PartialEq for FilePermissions {
+    fn eq(&self, _other: &FilePermissions) -> bool {
+        match self.0 {}
+    }
+}
+
+impl Eq for FilePermissions {
+}
+
+impl fmt::Debug for FilePermissions {
+    fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
+        match self.0 {}
+    }
+}
+
+impl FileType {
+    pub fn is_dir(&self) -> bool {
+        match self.0 {}
+    }
+
+    pub fn is_file(&self) -> bool {
+        match self.0 {}
+    }
+
+    pub fn is_symlink(&self) -> bool {
+        match self.0 {}
+    }
+}
+
+impl Clone for FileType {
+    fn clone(&self) -> FileType {
+        match self.0 {}
+    }
+}
+
+impl Copy for FileType {}
+
+impl PartialEq for FileType {
+    fn eq(&self, _other: &FileType) -> bool {
+        match self.0 {}
+    }
+}
+
+impl Eq for FileType {
+}
+
+impl Hash for FileType {
+    fn hash<H: Hasher>(&self, _h: &mut H) {
+        match self.0 {}
+    }
+}
+
+impl fmt::Debug for FileType {
+    fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
+        match self.0 {}
+    }
+}
+
+impl fmt::Debug for ReadDir {
+    fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
+        match self.0 {}
+    }
+}
+
+impl Iterator for ReadDir {
+    type Item = io::Result<DirEntry>;
+
+    fn next(&mut self) -> Option<io::Result<DirEntry>> {
+        match self.0 {}
+    }
+}
+
+impl DirEntry {
+    pub fn path(&self) -> PathBuf {
+        match self.0 {}
+    }
+
+    pub fn file_name(&self) -> OsString {
+        match self.0 {}
+    }
+
+    pub fn metadata(&self) -> io::Result<FileAttr> {
+        match self.0 {}
+    }
+
+    pub fn file_type(&self) -> io::Result<FileType> {
+        match self.0 {}
+    }
+}
+
+impl OpenOptions {
+    pub fn new() -> OpenOptions {
+        OpenOptions { }
+    }
+
+    pub fn read(&mut self, _read: bool) { }
+    pub fn write(&mut self, _write: bool) { }
+    pub fn append(&mut self, _append: bool) { }
+    pub fn truncate(&mut self, _truncate: bool) { }
+    pub fn create(&mut self, _create: bool) { }
+    pub fn create_new(&mut self, _create_new: bool) { }
+}
+
+impl File {
+    pub fn open(_path: &Path, _opts: &OpenOptions) -> io::Result<File> {
+        unsupported()
+    }
+
+    pub fn file_attr(&self) -> io::Result<FileAttr> {
+        match self.0 {}
+    }
+
+    pub fn fsync(&self) -> io::Result<()> {
+        match self.0 {}
+    }
+
+    pub fn datasync(&self) -> io::Result<()> {
+        match self.0 {}
+    }
+
+    pub fn truncate(&self, _size: u64) -> io::Result<()> {
+        match self.0 {}
+    }
+
+    pub fn read(&self, _buf: &mut [u8]) -> io::Result<usize> {
+        match self.0 {}
+    }
+
+    pub fn write(&self, _buf: &[u8]) -> io::Result<usize> {
+        match self.0 {}
+    }
+
+    pub fn flush(&self) -> io::Result<()> {
+        match self.0 {}
+    }
+
+    pub fn seek(&self, _pos: SeekFrom) -> io::Result<u64> {
+        match self.0 {}
+    }
+
+    pub fn duplicate(&self) -> io::Result<File> {
+        match self.0 {}
+    }
+
+    pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> {
+        match self.0 {}
+    }
+
+    pub fn diverge(&self) -> ! {
+        match self.0 {}
+    }
+}
+
+impl DirBuilder {
+    pub fn new() -> DirBuilder {
+        DirBuilder { }
+    }
+
+    pub fn mkdir(&self, _p: &Path) -> io::Result<()> {
+        unsupported()
+    }
+}
+
+impl fmt::Debug for File {
+    fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
+        match self.0 {}
+    }
+}
+
+pub fn readdir(_p: &Path) -> io::Result<ReadDir> {
+    unsupported()
+}
+
+pub fn unlink(_p: &Path) -> io::Result<()> {
+    unsupported()
+}
+
+pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> {
+    unsupported()
+}
+
+pub fn set_perm(_p: &Path, perm: FilePermissions) -> io::Result<()> {
+    match perm.0 {}
+}
+
+pub fn rmdir(_p: &Path) -> io::Result<()> {
+    unsupported()
+}
+
+pub fn remove_dir_all(_path: &Path) -> io::Result<()> {
+    unsupported()
+}
+
+pub fn readlink(_p: &Path) -> io::Result<PathBuf> {
+    unsupported()
+}
+
+pub fn symlink(_src: &Path, _dst: &Path) -> io::Result<()> {
+    unsupported()
+}
+
+pub fn link(_src: &Path, _dst: &Path) -> io::Result<()> {
+    unsupported()
+}
+
+pub fn stat(_p: &Path) -> io::Result<FileAttr> {
+    unsupported()
+}
+
+pub fn lstat(_p: &Path) -> io::Result<FileAttr> {
+    unsupported()
+}
+
+pub fn canonicalize(_p: &Path) -> io::Result<PathBuf> {
+    unsupported()
+}
+
+pub fn copy(_from: &Path, _to: &Path) -> io::Result<u64> {
+    unsupported()
+}
diff --git a/src/libstd/sys/wasi/io.rs b/src/libstd/sys/wasi/io.rs
new file mode 100644
index 00000000000..5f0315d279e
--- /dev/null
+++ b/src/libstd/sys/wasi/io.rs
@@ -0,0 +1,62 @@
+use crate::marker::PhantomData;
+use crate::slice;
+
+use libc::{__wasi_ciovec_t, __wasi_iovec_t, c_void};
+
+#[repr(transparent)]
+pub struct IoVec<'a> {
+    vec: __wasi_ciovec_t,
+    _p: PhantomData<&'a [u8]>,
+}
+
+impl<'a> IoVec<'a> {
+    #[inline]
+    pub fn new(buf: &'a [u8]) -> IoVec<'a> {
+        IoVec {
+            vec: __wasi_ciovec_t {
+                buf: buf.as_ptr() as *const c_void,
+                buf_len: buf.len(),
+            },
+            _p: PhantomData,
+        }
+    }
+
+    #[inline]
+    pub fn as_slice(&self) -> &[u8] {
+        unsafe {
+            slice::from_raw_parts(self.vec.buf as *const u8, self.vec.buf_len)
+        }
+    }
+}
+
+pub struct IoVecMut<'a> {
+    vec: __wasi_iovec_t,
+    _p: PhantomData<&'a mut [u8]>,
+}
+
+impl<'a> IoVecMut<'a> {
+    #[inline]
+    pub fn new(buf: &'a mut [u8]) -> IoVecMut<'a> {
+        IoVecMut {
+            vec: __wasi_iovec_t {
+                buf: buf.as_mut_ptr() as *mut c_void,
+                buf_len: buf.len()
+            },
+            _p: PhantomData,
+        }
+    }
+
+    #[inline]
+    pub fn as_slice(&self) -> &[u8] {
+        unsafe {
+            slice::from_raw_parts(self.vec.buf as *const u8, self.vec.buf_len)
+        }
+    }
+
+    #[inline]
+    pub fn as_mut_slice(&mut self) -> &mut [u8] {
+        unsafe {
+            slice::from_raw_parts_mut(self.vec.buf as *mut u8, self.vec.buf_len)
+        }
+    }
+}
diff --git a/src/libstd/sys/wasi/mod.rs b/src/libstd/sys/wasi/mod.rs
new file mode 100644
index 00000000000..a9bb0151d05
--- /dev/null
+++ b/src/libstd/sys/wasi/mod.rs
@@ -0,0 +1,128 @@
+//! System bindings for the wasm/web platform
+//!
+//! This module contains the facade (aka platform-specific) implementations of
+//! OS level functionality for wasm. Note that this wasm is *not* the emscripten
+//! wasm, so we have no runtime here.
+//!
+//! This is all super highly experimental and not actually intended for
+//! wide/production use yet, it's still all in the experimental category. This
+//! will likely change over time.
+//!
+//! Currently all functions here are basically stubs that immediately return
+//! errors. The hope is that with a portability lint we can turn actually just
+//! remove all this and just omit parts of the standard library if we're
+//! compiling for wasm. That way it's a compile time error for something that's
+//! guaranteed to be a runtime error!
+
+use libc;
+use crate::io::{Error, ErrorKind};
+use crate::mem;
+use crate::os::raw::c_char;
+
+pub mod alloc;
+pub mod args;
+#[cfg(feature = "backtrace")]
+pub mod backtrace;
+#[path = "../wasm/cmath.rs"]
+pub mod cmath;
+#[path = "../wasm/condvar.rs"]
+pub mod condvar;
+pub mod env;
+pub mod fd;
+pub mod fs;
+#[path = "../wasm/memchr.rs"]
+pub mod memchr;
+#[path = "../wasm/mutex.rs"]
+pub mod mutex;
+pub mod net;
+pub mod io;
+pub mod os;
+pub use crate::sys_common::os_str_bytes as os_str;
+pub mod path;
+pub mod pipe;
+pub mod process;
+#[path = "../wasm/rwlock.rs"]
+pub mod rwlock;
+#[path = "../wasm/stack_overflow.rs"]
+pub mod stack_overflow;
+pub mod stdio;
+pub mod thread;
+#[path = "../wasm/thread_local.rs"]
+pub mod thread_local;
+pub mod time;
+pub mod ext;
+
+#[cfg(not(test))]
+pub fn init() {
+}
+
+pub fn unsupported<T>() -> crate::io::Result<T> {
+    Err(unsupported_err())
+}
+
+pub fn unsupported_err() -> Error {
+    Error::new(ErrorKind::Other, "operation not supported on wasm yet")
+}
+
+pub fn decode_error_kind(_code: i32) -> ErrorKind {
+    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 unsafe fn abort_internal() -> ! {
+    libc::abort()
+}
+
+pub fn hashmap_random_keys() -> (u64, u64) {
+    let mut ret = (0u64, 0u64);
+    unsafe {
+        let base = &mut ret as *mut (u64, u64) as *mut libc::c_void;
+        let len = mem::size_of_val(&ret);
+        cvt_wasi(libc::__wasi_random_get(base, len)).unwrap();
+    }
+    return ret
+}
+
+#[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(Error::last_os_error())
+    } else {
+        Ok(t)
+    }
+}
+
+pub fn cvt_wasi(r: u16) -> crate::io::Result<()> {
+    if r != libc::__WASI_ESUCCESS {
+        Err(Error::from_raw_os_error(r as i32))
+    } else {
+        Ok(())
+    }
+}
diff --git a/src/libstd/sys/wasi/net.rs b/src/libstd/sys/wasi/net.rs
new file mode 100644
index 00000000000..af9a22f5b7a
--- /dev/null
+++ b/src/libstd/sys/wasi/net.rs
@@ -0,0 +1,358 @@
+use crate::fmt;
+use crate::io::{self, IoVec, IoVecMut};
+use crate::net::{SocketAddr, Shutdown, Ipv4Addr, Ipv6Addr};
+use crate::time::Duration;
+use crate::sys::{unsupported, Void};
+use crate::convert::TryFrom;
+
+pub struct TcpStream(Void);
+
+impl TcpStream {
+    pub fn connect(_: io::Result<&SocketAddr>) -> io::Result<TcpStream> {
+        unsupported()
+    }
+
+    pub fn connect_timeout(_: &SocketAddr, _: Duration) -> io::Result<TcpStream> {
+        unsupported()
+    }
+
+    pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> {
+        match self.0 {}
+    }
+
+    pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> {
+        match self.0 {}
+    }
+
+    pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
+        match self.0 {}
+    }
+
+    pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
+        match self.0 {}
+    }
+
+    pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> {
+        match self.0 {}
+    }
+
+    pub fn read(&self, _: &mut [u8]) -> io::Result<usize> {
+        match self.0 {}
+    }
+
+    pub fn read_vectored(&self, _: &mut [IoVecMut<'_>]) -> io::Result<usize> {
+        match self.0 {}
+    }
+
+    pub fn write(&self, _: &[u8]) -> io::Result<usize> {
+        match self.0 {}
+    }
+
+    pub fn write_vectored(&self, _: &[IoVec<'_>]) -> io::Result<usize> {
+        match self.0 {}
+    }
+
+    pub fn peer_addr(&self) -> io::Result<SocketAddr> {
+        match self.0 {}
+    }
+
+    pub fn socket_addr(&self) -> io::Result<SocketAddr> {
+        match self.0 {}
+    }
+
+    pub fn shutdown(&self, _: Shutdown) -> io::Result<()> {
+        match self.0 {}
+    }
+
+    pub fn duplicate(&self) -> io::Result<TcpStream> {
+        match self.0 {}
+    }
+
+    pub fn set_nodelay(&self, _: bool) -> io::Result<()> {
+        match self.0 {}
+    }
+
+    pub fn nodelay(&self) -> io::Result<bool> {
+        match self.0 {}
+    }
+
+    pub fn set_ttl(&self, _: u32) -> io::Result<()> {
+        match self.0 {}
+    }
+
+    pub fn ttl(&self) -> io::Result<u32> {
+        match self.0 {}
+    }
+
+    pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+        match self.0 {}
+    }
+
+    pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
+        match self.0 {}
+    }
+}
+
+impl fmt::Debug for TcpStream {
+    fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
+        match self.0 {}
+    }
+}
+
+pub struct TcpListener(Void);
+
+impl TcpListener {
+    pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<TcpListener> {
+        unsupported()
+    }
+
+    pub fn socket_addr(&self) -> io::Result<SocketAddr> {
+        match self.0 {}
+    }
+
+    pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> {
+        match self.0 {}
+    }
+
+    pub fn duplicate(&self) -> io::Result<TcpListener> {
+        match self.0 {}
+    }
+
+    pub fn set_ttl(&self, _: u32) -> io::Result<()> {
+        match self.0 {}
+    }
+
+    pub fn ttl(&self) -> io::Result<u32> {
+        match self.0 {}
+    }
+
+    pub fn set_only_v6(&self, _: bool) -> io::Result<()> {
+        match self.0 {}
+    }
+
+    pub fn only_v6(&self) -> io::Result<bool> {
+        match self.0 {}
+    }
+
+    pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+        match self.0 {}
+    }
+
+    pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
+        match self.0 {}
+    }
+}
+
+impl fmt::Debug for TcpListener {
+    fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
+        match self.0 {}
+    }
+}
+
+pub struct UdpSocket(Void);
+
+impl UdpSocket {
+    pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<UdpSocket> {
+        unsupported()
+    }
+
+    pub fn peer_addr(&self) -> io::Result<SocketAddr> {
+        match self.0 {}
+    }
+
+    pub fn socket_addr(&self) -> io::Result<SocketAddr> {
+        match self.0 {}
+    }
+
+    pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
+        match self.0 {}
+    }
+
+    pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
+        match self.0 {}
+    }
+
+    pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result<usize> {
+        match self.0 {}
+    }
+
+    pub fn duplicate(&self) -> io::Result<UdpSocket> {
+        match self.0 {}
+    }
+
+    pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> {
+        match self.0 {}
+    }
+
+    pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> {
+        match self.0 {}
+    }
+
+    pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
+        match self.0 {}
+    }
+
+    pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
+        match self.0 {}
+    }
+
+    pub fn set_broadcast(&self, _: bool) -> io::Result<()> {
+        match self.0 {}
+    }
+
+    pub fn broadcast(&self) -> io::Result<bool> {
+        match self.0 {}
+    }
+
+    pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> {
+        match self.0 {}
+    }
+
+    pub fn multicast_loop_v4(&self) -> io::Result<bool> {
+        match self.0 {}
+    }
+
+    pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> {
+        match self.0 {}
+    }
+
+    pub fn multicast_ttl_v4(&self) -> io::Result<u32> {
+        match self.0 {}
+    }
+
+    pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> {
+        match self.0 {}
+    }
+
+    pub fn multicast_loop_v6(&self) -> io::Result<bool> {
+        match self.0 {}
+    }
+
+    pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr)
+                         -> io::Result<()> {
+        match self.0 {}
+    }
+
+    pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32)
+                         -> io::Result<()> {
+        match self.0 {}
+    }
+
+    pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr)
+                          -> io::Result<()> {
+        match self.0 {}
+    }
+
+    pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32)
+                          -> io::Result<()> {
+        match self.0 {}
+    }
+
+    pub fn set_ttl(&self, _: u32) -> io::Result<()> {
+        match self.0 {}
+    }
+
+    pub fn ttl(&self) -> io::Result<u32> {
+        match self.0 {}
+    }
+
+    pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+        match self.0 {}
+    }
+
+    pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
+        match self.0 {}
+    }
+
+    pub fn recv(&self, _: &mut [u8]) -> io::Result<usize> {
+        match self.0 {}
+    }
+
+    pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> {
+        match self.0 {}
+    }
+
+    pub fn send(&self, _: &[u8]) -> io::Result<usize> {
+        match self.0 {}
+    }
+
+    pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> {
+        match self.0 {}
+    }
+}
+
+impl fmt::Debug for UdpSocket {
+    fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
+        match self.0 {}
+    }
+}
+
+pub struct LookupHost(Void);
+
+impl LookupHost {
+    pub fn port(&self) -> u16 {
+        match self.0 {}
+    }
+}
+
+impl Iterator for LookupHost {
+    type Item = SocketAddr;
+    fn next(&mut self) -> Option<SocketAddr> {
+        match self.0 {}
+    }
+}
+
+impl<'a> TryFrom<&'a str> for LookupHost {
+    type Error = io::Error;
+
+    fn try_from(_v: &'a str) -> io::Result<LookupHost> {
+        unsupported()
+    }
+}
+
+impl<'a> TryFrom<(&'a str, u16)> for LookupHost {
+    type Error = io::Error;
+
+    fn try_from(_v: (&'a str, u16)) -> io::Result<LookupHost> {
+        unsupported()
+    }
+}
+
+#[allow(nonstandard_style)]
+pub mod netc {
+    pub const AF_INET: u8 = 0;
+    pub const AF_INET6: u8 = 1;
+    pub type sa_family_t = u8;
+
+    #[derive(Copy, Clone)]
+    pub struct in_addr {
+        pub s_addr: u32,
+    }
+
+    #[derive(Copy, Clone)]
+    pub struct sockaddr_in {
+        pub sin_family: sa_family_t,
+        pub sin_port: u16,
+        pub sin_addr: in_addr,
+    }
+
+    #[derive(Copy, Clone)]
+    pub struct in6_addr {
+        pub s6_addr: [u8; 16],
+    }
+
+    #[derive(Copy, Clone)]
+    pub struct sockaddr_in6 {
+        pub sin6_family: sa_family_t,
+        pub sin6_port: u16,
+        pub sin6_addr: in6_addr,
+        pub sin6_flowinfo: u32,
+        pub sin6_scope_id: u32,
+    }
+
+    #[derive(Copy, Clone)]
+    pub struct sockaddr {
+    }
+
+    pub type socklen_t = usize;
+}
diff --git a/src/libstd/sys/wasi/os.rs b/src/libstd/sys/wasi/os.rs
new file mode 100644
index 00000000000..de18e4ba950
--- /dev/null
+++ b/src/libstd/sys/wasi/os.rs
@@ -0,0 +1,171 @@
+use crate::any::Any;
+use crate::error::Error as StdError;
+use crate::ffi::{OsString, OsStr, CString, CStr};
+use crate::fmt;
+use crate::io;
+use crate::marker::PhantomData;
+use crate::os::wasi::prelude::*;
+use crate::path::{self, PathBuf};
+use crate::ptr;
+use crate::str;
+use crate::sys::memchr;
+use crate::sys::{cvt, unsupported, Void};
+use crate::vec;
+
+#[cfg(not(target_feature = "atomics"))]
+pub unsafe fn env_lock() -> impl Any {
+    // No need for a lock if we're single-threaded, but this function will need
+    // to get implemented for multi-threaded scenarios
+}
+
+pub fn errno() -> i32 {
+    extern {
+        #[thread_local]
+        static errno: libc::c_int;
+    }
+
+    unsafe { errno as i32 }
+}
+
+pub fn error_string(_errno: i32) -> String {
+    "operation failed".to_string()
+}
+
+pub fn getcwd() -> io::Result<PathBuf> {
+    unsupported()
+}
+
+pub fn chdir(_: &path::Path) -> io::Result<()> {
+    unsupported()
+}
+
+pub struct SplitPaths<'a>(&'a Void);
+
+pub fn split_paths(_unparsed: &OsStr) -> SplitPaths {
+    panic!("unsupported")
+}
+
+impl<'a> Iterator for SplitPaths<'a> {
+    type Item = PathBuf;
+    fn next(&mut self) -> Option<PathBuf> {
+        match *self.0 {}
+    }
+}
+
+#[derive(Debug)]
+pub struct JoinPathsError;
+
+pub fn join_paths<I, T>(_paths: I) -> Result<OsString, JoinPathsError>
+    where I: Iterator<Item=T>, T: AsRef<OsStr>
+{
+    Err(JoinPathsError)
+}
+
+impl fmt::Display for JoinPathsError {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        "not supported on wasm yet".fmt(f)
+    }
+}
+
+impl StdError for JoinPathsError {
+    fn description(&self) -> &str {
+        "not supported on wasm yet"
+    }
+}
+
+pub fn current_exe() -> io::Result<PathBuf> {
+    unsupported()
+}
+
+pub struct Env {
+    iter: vec::IntoIter<(OsString, OsString)>,
+    _dont_send_or_sync_me: PhantomData<*mut ()>,
+}
+
+impl Iterator for Env {
+    type Item = (OsString, OsString);
+    fn next(&mut self) -> Option<(OsString, OsString)> { self.iter.next() }
+    fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() }
+}
+
+
+pub fn env() -> Env {
+    unsafe {
+        let _guard = env_lock();
+        let mut environ = libc::environ;
+        let mut result = Vec::new();
+        while environ != ptr::null_mut() && *environ != ptr::null_mut() {
+            if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) {
+                result.push(key_value);
+            }
+            environ = environ.offset(1);
+        }
+        return Env {
+            iter: result.into_iter(),
+            _dont_send_or_sync_me: PhantomData,
+        }
+    }
+
+    // See src/libstd/sys/unix/os.rs, same as that
+    fn parse(input: &[u8]) -> Option<(OsString, OsString)> {
+        if input.is_empty() {
+            return None;
+        }
+        let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1);
+        pos.map(|p| (
+            OsStringExt::from_vec(input[..p].to_vec()),
+            OsStringExt::from_vec(input[p+1..].to_vec()),
+        ))
+    }
+}
+
+pub fn getenv(k: &OsStr) -> io::Result<Option<OsString>> {
+    let k = CString::new(k.as_bytes())?;
+    unsafe {
+        let _guard = env_lock();
+        let s = libc::getenv(k.as_ptr()) as *const libc::c_char;
+        let ret = if s.is_null() {
+            None
+        } else {
+            Some(OsStringExt::from_vec(CStr::from_ptr(s).to_bytes().to_vec()))
+        };
+        Ok(ret)
+    }
+}
+
+pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
+    let k = CString::new(k.as_bytes())?;
+    let v = CString::new(v.as_bytes())?;
+
+    unsafe {
+        let _guard = env_lock();
+        cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(|_| ())
+    }
+}
+
+pub fn unsetenv(n: &OsStr) -> io::Result<()> {
+    let nbuf = CString::new(n.as_bytes())?;
+
+    unsafe {
+        let _guard = env_lock();
+        cvt(libc::unsetenv(nbuf.as_ptr())).map(|_| ())
+    }
+}
+
+pub fn temp_dir() -> PathBuf {
+    panic!("no filesystem on wasm")
+}
+
+pub fn home_dir() -> Option<PathBuf> {
+    None
+}
+
+pub fn exit(code: i32) -> ! {
+    unsafe {
+        libc::exit(code)
+    }
+}
+
+pub fn getpid() -> u32 {
+    panic!("unsupported");
+}
diff --git a/src/libstd/sys/wasi/path.rs b/src/libstd/sys/wasi/path.rs
new file mode 100644
index 00000000000..5c062e7c97c
--- /dev/null
+++ b/src/libstd/sys/wasi/path.rs
@@ -0,0 +1,19 @@
+use crate::path::Prefix;
+use crate::ffi::OsStr;
+
+#[inline]
+pub fn is_sep_byte(b: u8) -> bool {
+    b == b'/'
+}
+
+#[inline]
+pub fn is_verbatim_sep(b: u8) -> bool {
+    b == b'/'
+}
+
+pub fn parse_prefix(_: &OsStr) -> Option<Prefix> {
+    None
+}
+
+pub const MAIN_SEP_STR: &str = "/";
+pub const MAIN_SEP: char = '/';
diff --git a/src/libstd/sys/wasi/pipe.rs b/src/libstd/sys/wasi/pipe.rs
new file mode 100644
index 00000000000..2582b993b60
--- /dev/null
+++ b/src/libstd/sys/wasi/pipe.rs
@@ -0,0 +1,25 @@
+use crate::io;
+use crate::sys::Void;
+
+pub struct AnonPipe(Void);
+
+impl AnonPipe {
+    pub fn read(&self, _buf: &mut [u8]) -> io::Result<usize> {
+        match self.0 {}
+    }
+
+    pub fn write(&self, _buf: &[u8]) -> io::Result<usize> {
+        match self.0 {}
+    }
+
+    pub fn diverge(&self) -> ! {
+        match self.0 {}
+    }
+}
+
+pub fn read2(p1: AnonPipe,
+             _v1: &mut Vec<u8>,
+             _p2: AnonPipe,
+             _v2: &mut Vec<u8>) -> io::Result<()> {
+    match p1.0 {}
+}
diff --git a/src/libstd/sys/wasi/process.rs b/src/libstd/sys/wasi/process.rs
new file mode 100644
index 00000000000..c49daaa1632
--- /dev/null
+++ b/src/libstd/sys/wasi/process.rs
@@ -0,0 +1,152 @@
+use crate::ffi::OsStr;
+use crate::fmt;
+use crate::io;
+use crate::sys::fs::File;
+use crate::sys::pipe::AnonPipe;
+use crate::sys::{unsupported, Void};
+use crate::sys_common::process::{CommandEnv, DefaultEnvKey};
+
+////////////////////////////////////////////////////////////////////////////////
+// Command
+////////////////////////////////////////////////////////////////////////////////
+
+pub struct Command {
+    env: CommandEnv<DefaultEnvKey>
+}
+
+// passed back to std::process with the pipes connected to the child, if any
+// were requested
+pub struct StdioPipes {
+    pub stdin: Option<AnonPipe>,
+    pub stdout: Option<AnonPipe>,
+    pub stderr: Option<AnonPipe>,
+}
+
+pub enum Stdio {
+    Inherit,
+    Null,
+    MakePipe,
+}
+
+impl Command {
+    pub fn new(_program: &OsStr) -> Command {
+        Command {
+            env: Default::default()
+        }
+    }
+
+    pub fn arg(&mut self, _arg: &OsStr) {
+    }
+
+    pub fn env_mut(&mut self) -> &mut CommandEnv<DefaultEnvKey> {
+        &mut self.env
+    }
+
+    pub fn cwd(&mut self, _dir: &OsStr) {
+    }
+
+    pub fn stdin(&mut self, _stdin: Stdio) {
+    }
+
+    pub fn stdout(&mut self, _stdout: Stdio) {
+    }
+
+    pub fn stderr(&mut self, _stderr: Stdio) {
+    }
+
+    pub fn spawn(&mut self, _default: Stdio, _needs_stdin: bool)
+        -> io::Result<(Process, StdioPipes)> {
+        unsupported()
+    }
+}
+
+impl From<AnonPipe> for Stdio {
+    fn from(pipe: AnonPipe) -> Stdio {
+        pipe.diverge()
+    }
+}
+
+impl From<File> for Stdio {
+    fn from(file: File) -> Stdio {
+        file.diverge()
+    }
+}
+
+impl fmt::Debug for Command {
+    fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
+        Ok(())
+    }
+}
+
+pub struct ExitStatus(Void);
+
+impl ExitStatus {
+    pub fn success(&self) -> bool {
+        match self.0 {}
+    }
+
+    pub fn code(&self) -> Option<i32> {
+        match self.0 {}
+    }
+}
+
+impl Clone for ExitStatus {
+    fn clone(&self) -> ExitStatus {
+        match self.0 {}
+    }
+}
+
+impl Copy for ExitStatus {}
+
+impl PartialEq for ExitStatus {
+    fn eq(&self, _other: &ExitStatus) -> bool {
+        match self.0 {}
+    }
+}
+
+impl Eq for ExitStatus {
+}
+
+impl fmt::Debug for ExitStatus {
+    fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
+        match self.0 {}
+    }
+}
+
+impl fmt::Display for ExitStatus {
+    fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
+        match self.0 {}
+    }
+}
+
+#[derive(PartialEq, Eq, Clone, Copy, Debug)]
+pub struct ExitCode(bool);
+
+impl ExitCode {
+    pub const SUCCESS: ExitCode = ExitCode(false);
+    pub const FAILURE: ExitCode = ExitCode(true);
+
+    pub fn as_i32(&self) -> i32 {
+        self.0 as i32
+    }
+}
+
+pub struct Process(Void);
+
+impl Process {
+    pub fn id(&self) -> u32 {
+        match self.0 {}
+    }
+
+    pub fn kill(&mut self) -> io::Result<()> {
+        match self.0 {}
+    }
+
+    pub fn wait(&mut self) -> io::Result<ExitStatus> {
+        match self.0 {}
+    }
+
+    pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
+        match self.0 {}
+    }
+}
diff --git a/src/libstd/sys/wasi/stdio.rs b/src/libstd/sys/wasi/stdio.rs
new file mode 100644
index 00000000000..f6a4958897d
--- /dev/null
+++ b/src/libstd/sys/wasi/stdio.rs
@@ -0,0 +1,74 @@
+use crate::io;
+use crate::libc;
+use crate::sys::cvt;
+
+pub struct Stdin;
+pub struct Stdout;
+pub struct Stderr;
+
+impl Stdin {
+    pub fn new() -> io::Result<Stdin> {
+        Ok(Stdin)
+    }
+
+    pub fn read(&self, data: &mut [u8]) -> io::Result<usize> {
+        let amt = cvt(unsafe {
+            libc::read(libc::STDIN_FILENO, data.as_mut_ptr() as *mut _, data.len())
+        })?;
+        Ok(amt as usize)
+    }
+}
+
+impl Stdout {
+    pub fn new() -> io::Result<Stdout> {
+        Ok(Stdout)
+    }
+
+    pub fn write(&self, data: &[u8]) -> io::Result<usize> {
+        let amt = cvt(unsafe {
+            libc::write(libc::STDOUT_FILENO, data.as_ptr() as *const _, data.len())
+        })?;
+        Ok(amt as usize)
+    }
+
+    pub fn flush(&self) -> io::Result<()> {
+        Ok(())
+    }
+}
+
+impl Stderr {
+    pub fn new() -> io::Result<Stderr> {
+        Ok(Stderr)
+    }
+
+    pub fn write(&self, data: &[u8]) -> io::Result<usize> {
+        let amt = cvt(unsafe {
+            libc::write(libc::STDERR_FILENO, data.as_ptr() as *const _, data.len())
+        })?;
+        Ok(amt as usize)
+    }
+
+    pub fn flush(&self) -> io::Result<()> {
+        Ok(())
+    }
+}
+
+impl io::Write for Stderr {
+    fn write(&mut self, data: &[u8]) -> io::Result<usize> {
+        (&*self).write(data)
+    }
+
+    fn flush(&mut self) -> io::Result<()> {
+        (&*self).flush()
+    }
+}
+
+pub const STDIN_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE;
+
+pub fn is_ebadf(err: &io::Error) -> bool {
+    err.raw_os_error() == Some(libc::__WASI_EBADF as i32)
+}
+
+pub fn panic_output() -> Option<impl io::Write> {
+    Stderr::new().ok()
+}
diff --git a/src/libstd/sys/wasi/thread.rs b/src/libstd/sys/wasi/thread.rs
new file mode 100644
index 00000000000..9d3c6ac59d1
--- /dev/null
+++ b/src/libstd/sys/wasi/thread.rs
@@ -0,0 +1,57 @@
+use crate::boxed::FnBox;
+use crate::cmp;
+use crate::ffi::CStr;
+use crate::io;
+use crate::sys::cvt;
+use crate::sys::{unsupported, Void};
+use crate::time::Duration;
+use libc;
+
+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 FnBox()>)
+        -> io::Result<Thread>
+    {
+        unsupported()
+    }
+
+    pub fn yield_now() {
+        let ret = unsafe { libc::__wasi_sched_yield() };
+        debug_assert_eq!(ret, 0);
+    }
+
+    pub fn set_name(_name: &CStr) {
+        // nope
+    }
+
+    pub fn sleep(dur: Duration) {
+        let mut secs = dur.as_secs();
+        let mut nsecs = dur.subsec_nanos() as i32;
+
+        unsafe {
+            while secs > 0 || nsecs > 0 {
+                let mut ts = libc::timespec {
+                    tv_sec: cmp::min(libc::time_t::max_value() as u64, secs) as libc::time_t,
+                    tv_nsec: nsecs,
+                };
+                secs -= ts.tv_sec as u64;
+                cvt(libc::nanosleep(&ts, &mut ts)).unwrap();
+                nsecs = 0;
+            }
+        }
+    }
+
+    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/wasi/time.rs b/src/libstd/sys/wasi/time.rs
new file mode 100644
index 00000000000..e1b92e7c5a7
--- /dev/null
+++ b/src/libstd/sys/wasi/time.rs
@@ -0,0 +1,72 @@
+use crate::time::Duration;
+use crate::mem;
+use crate::sys::cvt_wasi;
+use libc;
+
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
+pub struct Instant(Duration);
+
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
+pub struct SystemTime(Duration);
+
+pub const UNIX_EPOCH: SystemTime = SystemTime(Duration::from_secs(0));
+
+fn current_time(clock: u32) -> Duration {
+    unsafe {
+        let mut ts = mem::zeroed();
+        cvt_wasi(libc::__wasi_clock_time_get(
+            clock,
+            1, // precision... seems ignored though?
+            &mut ts,
+        )).unwrap();
+        Duration::new(
+            (ts / 1_000_000_000) as u64,
+            (ts % 1_000_000_000) as u32,
+        )
+    }
+}
+
+impl Instant {
+    pub fn now() -> Instant {
+        Instant(current_time(libc::__WASI_CLOCK_MONOTONIC))
+    }
+
+    pub const fn zero() -> Instant {
+        Instant(Duration::from_secs(0))
+    }
+
+    pub fn actually_monotonic() -> bool {
+        true
+    }
+
+    pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
+        self.0.checked_sub(other.0)
+    }
+
+    pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> {
+        Some(Instant(self.0.checked_add(*other)?))
+    }
+
+    pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
+        Some(Instant(self.0.checked_sub(*other)?))
+    }
+}
+
+impl SystemTime {
+    pub fn now() -> SystemTime {
+        SystemTime(current_time(libc::__WASI_CLOCK_REALTIME))
+    }
+
+    pub fn sub_time(&self, other: &SystemTime)
+                    -> Result<Duration, Duration> {
+        self.0.checked_sub(other.0).ok_or_else(|| other.0 - self.0)
+    }
+
+    pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> {
+        Some(SystemTime(self.0.checked_add(*other)?))
+    }
+
+    pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> {
+        Some(SystemTime(self.0.checked_sub(*other)?))
+    }
+}