about summary refs log tree commit diff
path: root/src/libstd/sys/sgx
diff options
context:
space:
mode:
authorKonrad Borowski <konrad@borowski.pw>2018-12-23 16:47:11 +0100
committerGitHub <noreply@github.com>2018-12-23 16:47:11 +0100
commit8ac5380ea0204dbdcbc8108d259928b67d5f8ebb (patch)
tree174d912756fc2678af50d46ff457f7504750a975 /src/libstd/sys/sgx
parentb4a306c1e648c84f289c63e984941b7faad10af1 (diff)
parentddab10a692aab2e2984b5c826ed9d78a57e94851 (diff)
downloadrust-8ac5380ea0204dbdcbc8108d259928b67d5f8ebb.tar.gz
rust-8ac5380ea0204dbdcbc8108d259928b67d5f8ebb.zip
Merge branch 'master' into copied
Diffstat (limited to 'src/libstd/sys/sgx')
-rw-r--r--src/libstd/sys/sgx/abi/entry.S335
-rw-r--r--src/libstd/sys/sgx/abi/mem.rs42
-rw-r--r--src/libstd/sys/sgx/abi/mod.rs99
-rw-r--r--src/libstd/sys/sgx/abi/panic.rs58
-rw-r--r--src/libstd/sys/sgx/abi/reloc.rs40
-rw-r--r--src/libstd/sys/sgx/abi/thread.rs20
-rw-r--r--src/libstd/sys/sgx/abi/tls.rs246
-rw-r--r--src/libstd/sys/sgx/abi/usercalls/alloc.rs404
-rw-r--r--src/libstd/sys/sgx/abi/usercalls/mod.rs189
-rw-r--r--src/libstd/sys/sgx/abi/usercalls/raw.rs231
-rw-r--r--src/libstd/sys/sgx/alloc.rs42
-rw-r--r--src/libstd/sys/sgx/args.rs75
-rw-r--r--src/libstd/sys/sgx/backtrace.rs37
-rw-r--r--src/libstd/sys/sgx/cmath.rs41
-rw-r--r--src/libstd/sys/sgx/condvar.rs51
-rw-r--r--src/libstd/sys/sgx/env.rs19
-rw-r--r--src/libstd/sys/sgx/fd.rs58
-rw-r--r--src/libstd/sys/sgx/fs.rs304
-rw-r--r--src/libstd/sys/sgx/memchr.rs11
-rw-r--r--src/libstd/sys/sgx/mod.rs153
-rw-r--r--src/libstd/sys/sgx/mutex.rs150
-rw-r--r--src/libstd/sys/sgx/net.rs415
-rw-r--r--src/libstd/sys/sgx/os.rs147
-rw-r--r--src/libstd/sys/sgx/os_str.rs189
-rw-r--r--src/libstd/sys/sgx/path.rs29
-rw-r--r--src/libstd/sys/sgx/pipe.rs35
-rw-r--r--src/libstd/sys/sgx/process.rs162
-rw-r--r--src/libstd/sys/sgx/rwlock.rs258
-rw-r--r--src/libstd/sys/sgx/stack_overflow.rs23
-rw-r--r--src/libstd/sys/sgx/stdio.rs81
-rw-r--r--src/libstd/sys/sgx/thread.rs100
-rw-r--r--src/libstd/sys/sgx/thread_local.rs38
-rw-r--r--src/libstd/sys/sgx/time.rs57
-rw-r--r--src/libstd/sys/sgx/waitqueue.rs549
34 files changed, 4688 insertions, 0 deletions
diff --git a/src/libstd/sys/sgx/abi/entry.S b/src/libstd/sys/sgx/abi/entry.S
new file mode 100644
index 00000000000..49ede0674ce
--- /dev/null
+++ b/src/libstd/sys/sgx/abi/entry.S
@@ -0,0 +1,335 @@
+/* Copyright 2018 The Rust Project Developers. See the COPYRIGHT     */
+/* file at the top-level directory of this distribution and at       */
+/* http://rust-lang.org/COPYRIGHT.                                   */
+/*                                                                   */
+/* Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or */
+/* http://www.apache.org/licenses/LICENSE-2.0> or the MIT license    */
+/* <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your      */
+/* option. This file may not be copied, modified, or distributed     */
+/* except according to those terms.                                  */
+
+/*  This symbol is used at runtime to figure out the virtual address that the */
+/*  enclave is loaded at. */
+.section absolute
+.global IMAGE_BASE
+IMAGE_BASE:
+
+.section .rodata
+/*  The XSAVE area needs to be a large chunk of readable memory, but since we are */
+/*  going to restore everything to its initial state (XSTATE_BV=0), only certain */
+/*  parts need to have a defined value. In particular: */
+/*  */
+/*    * MXCSR in the legacy area. This register is always restored if RFBM[1] or */
+/*      RFBM[2] is set, regardless of the value of XSTATE_BV */
+/*    * XSAVE header */
+.align 64
+.Lxsave_clear:
+.org .+24
+.Lxsave_mxcsr:
+    .int 0
+
+/*  We can store a bunch of data in the gap between MXCSR and the XSAVE header */
+
+/*  The following symbols point at read-only data that will be filled in by the */
+/*  post-linker. */
+
+/*  When using this macro, don't forget to adjust the linker version script! */
+.macro globvar name:req size:req
+    .global \name
+    .protected \name
+    .align \size
+    .size \name , \size
+    \name :
+        .org .+\size
+.endm
+    /*  The base address (relative to enclave start) of the heap area */
+    globvar HEAP_BASE 8
+    /*  The heap size in bytes */
+    globvar HEAP_SIZE 8
+    /*  Value of the RELA entry in the dynamic table */
+    globvar RELA 8
+    /*  Value of the RELACOUNT entry in the dynamic table */
+    globvar RELACOUNT 8
+    /*  The enclave size in bytes */
+    globvar ENCLAVE_SIZE 8
+    /*  The base address (relative to enclave start) of the enclave configuration area */
+    globvar CFGDATA_BASE 8
+    /*  Non-zero if debugging is enabled, zero otherwise */
+    globvar DEBUG 1
+    /*  The base address (relative to enclave start) of the enclave text section */
+    globvar TEXT_BASE 8
+    /*  The size in bytes of enclacve text section */
+    globvar TEXT_SIZE 8
+    /*  The base address (relative to enclave start) of the enclave EH_FRM_HDR section */
+    globvar EH_FRM_HDR_BASE 8
+    /*  The size in bytes of enclacve EH_FRM_HDR section */
+    globvar EH_FRM_HDR_SIZE 8
+
+.Lreentry_panic_msg:
+    .asciz "Re-entered panicked enclave!"
+.Lreentry_panic_msg_end:
+
+.Lusercall_panic_msg:
+    .asciz "Invalid usercall#!"
+.Lusercall_panic_msg_end:
+
+.org .Lxsave_clear+512
+.Lxsave_header:
+    .int 0, 0 /*  XSTATE_BV */
+    .int 0, 0 /*  XCOMP_BV */
+    .org .+48 /*  reserved bits */
+
+.data
+.Lpanicked:
+    .byte 0
+
+/*  TCS local storage section */
+.equ tcsls_tos,                 0x00 /*  initialized by loader to *offset* from image base to TOS */
+.equ tcsls_flags,               0x08 /*  initialized by loader */
+.equ tcsls_flag_secondary,      0    /*  initialized by loader; 0 = standard TCS, 1 = secondary TCS */
+.equ tcsls_flag_init_once,      1    /*  initialized by loader to 0 */
+/*  14 unused bits */
+.equ tcsls_user_fcw,            0x0a
+.equ tcsls_user_mxcsr,          0x0c
+.equ tcsls_last_rsp,            0x10 /*  initialized by loader to 0 */
+.equ tcsls_panic_last_rsp,      0x18 /*  initialized by loader to 0 */
+.equ tcsls_debug_panic_buf_ptr, 0x20 /*  initialized by loader to 0 */
+.equ tcsls_user_rsp,            0x28
+.equ tcsls_user_retip,          0x30
+.equ tcsls_user_rbp,            0x38
+.equ tcsls_user_r12,            0x40
+.equ tcsls_user_r13,            0x48
+.equ tcsls_user_r14,            0x50
+.equ tcsls_user_r15,            0x58
+.equ tcsls_tls_ptr,             0x60
+.equ tcsls_tcs_addr,            0x68
+
+.macro load_tcsls_flag_secondary_bool reg:req comments:vararg
+    .ifne tcsls_flag_secondary /* to convert to a bool, must be the first bit */
+    .abort
+    .endif
+        mov $(1<<tcsls_flag_secondary),%e\reg
+        and %gs:tcsls_flags,%\reg
+.endm
+
+.text
+.global sgx_entry
+.type sgx_entry,function
+sgx_entry:
+/*  save user registers */
+    mov %rcx,%gs:tcsls_user_retip
+    mov %rsp,%gs:tcsls_user_rsp
+    mov %rbp,%gs:tcsls_user_rbp
+    mov %r12,%gs:tcsls_user_r12
+    mov %r13,%gs:tcsls_user_r13
+    mov %r14,%gs:tcsls_user_r14
+    mov %r15,%gs:tcsls_user_r15
+    mov %rbx,%gs:tcsls_tcs_addr
+    stmxcsr %gs:tcsls_user_mxcsr
+    fnstcw %gs:tcsls_user_fcw
+/*  reset user state */
+    cld /* x86-64 ABI requires DF to be unset at function entry/exit */
+/*  check for debug buffer pointer */
+    testb  $0xff,DEBUG(%rip)
+    jz .Lskip_debug_init
+    mov %r10,%gs:tcsls_debug_panic_buf_ptr
+.Lskip_debug_init:
+/*  check if returning from usercall */
+    mov %gs:tcsls_last_rsp,%r11
+    test %r11,%r11
+    jnz .Lusercall_ret
+/*  setup stack */
+    mov %gs:tcsls_tos,%rsp /*  initially, RSP is not set to the correct value */
+                           /*  here. This is fixed below under "adjust stack". */
+/*  check for thread init */
+    bts $tcsls_flag_init_once,%gs:tcsls_flags
+    jc .Lskip_init
+/*  adjust stack */
+    lea IMAGE_BASE(%rip),%rax
+    add %rax,%rsp
+    mov %rsp,%gs:tcsls_tos
+/*  call tcs_init */
+/*  store caller-saved registers in callee-saved registers */
+    mov %rdi,%rbx
+    mov %rsi,%r12
+    mov %rdx,%r13
+    mov %r8,%r14
+    mov %r9,%r15
+    load_tcsls_flag_secondary_bool di /* RDI = tcs_init() argument: secondary: bool */
+    call tcs_init
+/*  reload caller-saved registers */
+    mov %rbx,%rdi
+    mov %r12,%rsi
+    mov %r13,%rdx
+    mov %r14,%r8
+    mov %r15,%r9
+.Lskip_init:
+/*  check for panic */
+    bt $0,.Lpanicked(%rip)
+    jc .Lreentry_panic
+/*  call into main entry point */
+    load_tcsls_flag_secondary_bool cx /* RCX = entry() argument: secondary: bool */
+    call entry /* RDI, RSI, RDX, R8, R9 passed in from userspace */
+    mov %rax,%rsi  /* RSI = return value */
+    /* NOP: mov %rdx,%rdx */ /*  RDX = return value */
+    xor %rdi,%rdi  /* RDI = normal exit */
+.Lexit:
+/*  clear general purpose register state */
+    /*  RAX overwritten by ENCLU */
+    /*  RBX set later */
+    /*  RCX overwritten by ENCLU */
+    /*  RDX contains return value */
+    /*  RSP set later */
+    /*  RBP set later */
+    /*  RDI contains exit mode */
+    /*  RSI contains return value */
+    xor %r8,%r8
+    xor %r9,%r9
+    xor %r10,%r10
+    xor %r11,%r11
+    /*  R12 ~ R15 set by sgx_exit */
+.Lsgx_exit:
+/*  clear extended register state */
+    mov %rdx, %rcx /*  save RDX */
+    mov $-1, %rax
+    mov %rax, %rdx
+    xrstor .Lxsave_clear(%rip)
+    mov %rcx, %rdx /*  restore RDX */
+/*  clear flags */
+    pushq $0
+    popfq
+/*  restore user registers */
+    mov %gs:tcsls_user_r12,%r12
+    mov %gs:tcsls_user_r13,%r13
+    mov %gs:tcsls_user_r14,%r14
+    mov %gs:tcsls_user_r15,%r15
+    mov %gs:tcsls_user_retip,%rbx
+    mov %gs:tcsls_user_rsp,%rsp
+    mov %gs:tcsls_user_rbp,%rbp
+    fldcw %gs:tcsls_user_fcw
+    ldmxcsr %gs:tcsls_user_mxcsr
+/*  exit enclave */
+    mov $0x4,%eax /*  EEXIT */
+    enclu
+/*  end sgx_entry */
+
+.Lreentry_panic:
+    lea .Lreentry_panic_msg(%rip),%rdi
+    mov $.Lreentry_panic_msg_end-.Lreentry_panic_msg,%esi
+    orq $8,%rsp
+    jmp panic_msg
+
+.Lusercall_panic:
+    lea .Lusercall_panic_msg(%rip),%rdi
+    mov $.Lusercall_panic_msg_end-.Lusercall_panic_msg,%esi
+    orq $8,%rsp
+    jmp panic_msg
+
+.macro push_callee_saved_registers
+    push %r15
+    push %r14
+    push %r13
+    push %r12
+    push %rbp
+    push %rbx
+    sub $8, %rsp
+    fstcw 4(%rsp)
+    stmxcsr (%rsp)
+.endm
+
+.global panic_exit
+panic_exit:
+/* save registers in DEBUG mode, so that debugger can reconstruct the stack */
+    testb $0xff,DEBUG(%rip)
+    jz .Lskip_save_registers
+    push_callee_saved_registers
+    movq %rsp,%gs:tcsls_panic_last_rsp
+.Lskip_save_registers:
+/* set panicked bit */
+    movb $1,.Lpanicked(%rip)
+/* call usercall exit(true) */
+    mov $1,%esi   /*  RSI = usercall() argument: panic = true */
+    xor %rdx,%rdx /*  RDX cleared */
+    movq $usercall_nr_exit,%rdi /*  RDI = usercall exit */
+    jmp .Lexit
+
+/*  This *MUST* be called with 6 parameters, otherwise register information */
+/*  might leak! */
+.global usercall
+usercall:
+    test %rdi,%rdi
+    jle .Lusercall_panic
+/*  save callee-saved state */
+    push_callee_saved_registers
+    movq %rsp,%gs:tcsls_last_rsp
+/*  clear general purpose register state */
+    /*  RAX overwritten by ENCLU */
+    /*  RBX set by sgx_exit */
+    /*  RCX overwritten by ENCLU */
+    /*  RDX contains parameter */
+    /*  RSP set by sgx_exit */
+    /*  RBP set by sgx_exit */
+    /*  RDI contains parameter */
+    /*  RSI contains parameter */
+    /*  R8 contains parameter */
+    /*  R9 contains parameter */
+    xor %r10,%r10
+    xor %r11,%r11
+    /*  R12 ~ R15 set by sgx_exit */
+/*  extended registers/flags cleared by sgx_exit */
+/*  exit */
+    jmp .Lsgx_exit
+.Lusercall_ret:
+    movq $0,%gs:tcsls_last_rsp
+/*  restore callee-saved state, cf. push_callee_saved_registers */
+    mov %r11,%rsp
+    ldmxcsr (%rsp)
+    fldcw 4(%rsp)
+    add $8, %rsp
+    pop %rbx
+    pop %rbp
+    pop %r12
+    pop %r13
+    pop %r14
+    pop %r15
+/*  return */
+    mov %rsi,%rax /*  RAX = return value */
+    /* NOP: mov %rdx,%rdx */ /*  RDX = return value */
+    ret
+
+/*
+The following functions need to be defined externally:
+```
+// Called by entry code when it needs to panic
+extern "C" fn panic_msg(msg: &'static str) -> ! {
+    panic!(msg)
+}
+
+// Called once when a TCS is first entered
+extern "C" fn tcs_init(secondary: bool);
+
+// Standard TCS entrypoint
+extern "C" fn entry(p1: u64, p2: u64, p3: u64, secondary: bool, p4: u64, p5: u64) -> (u64, u64);
+```
+*/
+
+.global get_tcs_addr
+get_tcs_addr:
+    mov %gs:tcsls_tcs_addr,%rax
+    ret
+
+.global get_tls_ptr
+get_tls_ptr:
+    mov %gs:tcsls_tls_ptr,%rax
+    ret
+
+.global set_tls_ptr
+set_tls_ptr:
+    mov %rdi,%gs:tcsls_tls_ptr
+    ret
+
+.global take_debug_panic_buf_ptr
+take_debug_panic_buf_ptr:
+    xor %rax,%rax
+    xchg %gs:tcsls_debug_panic_buf_ptr,%rax
+    ret
diff --git a/src/libstd/sys/sgx/abi/mem.rs b/src/libstd/sys/sgx/abi/mem.rs
new file mode 100644
index 00000000000..bf32c712216
--- /dev/null
+++ b/src/libstd/sys/sgx/abi/mem.rs
@@ -0,0 +1,42 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Do not remove inline: will result in relocation failure
+#[inline(always)]
+pub unsafe fn rel_ptr<T>(offset: u64) -> *const T {
+    (image_base() + offset) as *const T
+}
+
+// Do not remove inline: will result in relocation failure
+#[inline(always)]
+pub unsafe fn rel_ptr_mut<T>(offset: u64) -> *mut T {
+    (image_base() + offset) as *mut T
+}
+
+extern {
+    static ENCLAVE_SIZE: usize;
+}
+
+// Do not remove inline: will result in relocation failure
+// For the same reason we use inline ASM here instead of an extern static to
+// locate the base
+#[inline(always)]
+fn image_base() -> u64 {
+    let base;
+    unsafe { asm!("lea IMAGE_BASE(%rip),$0":"=r"(base)) };
+    base
+}
+
+pub fn is_user_range(p: *const u8, len: usize) -> bool {
+    let start=p as u64;
+    let end=start + (len as u64);
+    end <= image_base() ||
+        start >= image_base() + (unsafe { ENCLAVE_SIZE } as u64) // unsafe ok: link-time constant
+}
diff --git a/src/libstd/sys/sgx/abi/mod.rs b/src/libstd/sys/sgx/abi/mod.rs
new file mode 100644
index 00000000000..069cca3b98e
--- /dev/null
+++ b/src/libstd/sys/sgx/abi/mod.rs
@@ -0,0 +1,99 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use core::sync::atomic::{AtomicUsize, Ordering};
+use io::Write;
+
+// runtime features
+mod reloc;
+mod mem;
+pub(super) mod panic;
+
+// library features
+pub mod thread;
+pub mod tls;
+#[macro_use]
+pub mod usercalls;
+
+global_asm!(concat!(usercalls_asm!(), include_str!("entry.S")));
+
+#[no_mangle]
+unsafe extern "C" fn tcs_init(secondary: bool) {
+    // Be very careful when changing this code: it runs before the binary has been
+    // relocated. Any indirect accesses to symbols will likely fail.
+    const UNINIT: usize = 0;
+    const BUSY: usize = 1;
+    const DONE: usize = 2;
+    // Three-state spin-lock
+    static RELOC_STATE: AtomicUsize = AtomicUsize::new(UNINIT);
+
+    if secondary && RELOC_STATE.load(Ordering::Relaxed) != DONE {
+        panic::panic_msg("Entered secondary TCS before main TCS!")
+    }
+
+    // Try to atomically swap UNINIT with BUSY. The returned state can be:
+    match RELOC_STATE.compare_and_swap(UNINIT, BUSY, Ordering::Acquire) {
+        // This thread just obtained the lock and other threads will observe BUSY
+        UNINIT => {
+            reloc::relocate_elf_rela();
+            RELOC_STATE.store(DONE, Ordering::Release);
+        },
+        // We need to wait until the initialization is done.
+        BUSY => while RELOC_STATE.load(Ordering::Acquire) == BUSY  {
+            ::core::arch::x86_64::_mm_pause()
+        },
+        // Initialization is done.
+        DONE => {},
+        _ => unreachable!()
+    }
+}
+
+// FIXME: this item should only exist if this is linked into an executable
+// (main function exists). If this is a library, the crate author should be
+// able to specify this
+#[no_mangle]
+extern "C" fn entry(p1: u64, p2: u64, p3: u64, secondary: bool, p4: u64, p5: u64) -> (u64, u64) {
+    // FIXME: how to support TLS in library mode?
+    let tls = Box::new(tls::Tls::new());
+    let _tls_guard = unsafe { tls.activate() };
+
+    if secondary {
+        super::thread::Thread::entry();
+
+        (0, 0)
+    } else {
+        extern "C" {
+            fn main(argc: isize, argv: *const *const u8) -> isize;
+        }
+
+        // check entry is being called according to ABI
+        assert_eq!(p3, 0);
+        assert_eq!(p4, 0);
+        assert_eq!(p5, 0);
+
+        unsafe {
+            // The actual types of these arguments are `p1: *const Arg, p2:
+            // usize`. We can't currently customize the argument list of Rust's
+            // main function, so we pass these in as the standard pointer-sized
+            // values in `argc` and `argv`.
+            let ret = main(p2 as _, p1 as _);
+            exit_with_code(ret)
+        }
+    }
+}
+
+pub(super) fn exit_with_code(code: isize) -> ! {
+    if code != 0 {
+        if let Some(mut out) = panic::SgxPanicOutput::new() {
+            let _ = write!(out, "Exited with status code {}", code);
+        }
+    }
+    usercalls::exit(code != 0);
+}
diff --git a/src/libstd/sys/sgx/abi/panic.rs b/src/libstd/sys/sgx/abi/panic.rs
new file mode 100644
index 00000000000..dd9159b9fe2
--- /dev/null
+++ b/src/libstd/sys/sgx/abi/panic.rs
@@ -0,0 +1,58 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use io::{self, Write};
+use slice::from_raw_parts_mut;
+
+extern "C" {
+    fn take_debug_panic_buf_ptr() -> *mut u8;
+    static DEBUG: u8;
+}
+
+pub(crate) struct SgxPanicOutput(Option<&'static mut [u8]>);
+
+impl SgxPanicOutput {
+    pub(crate) fn new() -> Option<Self> {
+        if unsafe { DEBUG == 0 } {
+            None
+        } else {
+            Some(SgxPanicOutput(None))
+        }
+    }
+
+    fn init(&mut self) -> &mut &'static mut [u8] {
+        self.0.get_or_insert_with(|| unsafe {
+            let ptr = take_debug_panic_buf_ptr();
+            if ptr.is_null() {
+                &mut []
+            } else {
+                from_raw_parts_mut(ptr, 1024)
+            }
+        })
+    }
+}
+
+impl Write for SgxPanicOutput {
+    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+        self.init().write(buf)
+    }
+
+    fn flush(&mut self) -> io::Result<()> {
+        self.init().flush()
+    }
+}
+
+#[no_mangle]
+pub extern "C" fn panic_msg(msg: &str) -> ! {
+    let _ = SgxPanicOutput::new().map(|mut out| out.write(msg.as_bytes()));
+    unsafe { panic_exit(); }
+}
+
+extern "C" { pub fn panic_exit() -> !; }
diff --git a/src/libstd/sys/sgx/abi/reloc.rs b/src/libstd/sys/sgx/abi/reloc.rs
new file mode 100644
index 00000000000..2d5e14d6ad1
--- /dev/null
+++ b/src/libstd/sys/sgx/abi/reloc.rs
@@ -0,0 +1,40 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use slice::from_raw_parts;
+use super::mem;
+
+const R_X86_64_RELATIVE: u32 = 8;
+
+#[repr(packed)]
+struct Rela<T> {
+    offset: T,
+    info: T,
+    addend: T,
+}
+
+pub fn relocate_elf_rela() {
+    extern {
+        static RELA: u64;
+        static RELACOUNT: usize;
+    }
+
+    if unsafe { RELACOUNT } == 0 { return }  // unsafe ok: link-time constant
+
+    let relas = unsafe {
+        from_raw_parts::<Rela<u64>>(mem::rel_ptr(RELA), RELACOUNT)  // unsafe ok: link-time constant
+    };
+    for rela in relas {
+        if rela.info != (/*0 << 32 |*/ R_X86_64_RELATIVE as u64) {
+            panic!("Invalid relocation");
+        }
+        unsafe { *mem::rel_ptr_mut::<*const ()>(rela.offset) = mem::rel_ptr(rela.addend) };
+    }
+}
diff --git a/src/libstd/sys/sgx/abi/thread.rs b/src/libstd/sys/sgx/abi/thread.rs
new file mode 100644
index 00000000000..4640b812fea
--- /dev/null
+++ b/src/libstd/sys/sgx/abi/thread.rs
@@ -0,0 +1,20 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use fortanix_sgx_abi::Tcs;
+
+/// Get the ID for the current thread. The ID is guaranteed to be unique among
+/// all currently running threads in the enclave, and it is guaranteed to be
+/// constant for the lifetime of the thread. More specifically for SGX, there
+/// is a one-to-one correspondence of the ID to the address of the TCS.
+pub fn current() -> Tcs {
+    extern "C" { fn get_tcs_addr() -> Tcs; }
+    unsafe { get_tcs_addr() }
+}
diff --git a/src/libstd/sys/sgx/abi/tls.rs b/src/libstd/sys/sgx/abi/tls.rs
new file mode 100644
index 00000000000..ab7822182a5
--- /dev/null
+++ b/src/libstd/sys/sgx/abi/tls.rs
@@ -0,0 +1,246 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
+use ptr;
+use mem;
+use cell::Cell;
+use num::NonZeroUsize;
+use self::sync_bitset::*;
+
+#[cfg(target_pointer_width="64")]
+const USIZE_BITS: usize = 64;
+const TLS_KEYS: usize = 128; // Same as POSIX minimum
+const TLS_KEYS_BITSET_SIZE: usize = (TLS_KEYS + (USIZE_BITS - 1)) / USIZE_BITS;
+
+static TLS_KEY_IN_USE: SyncBitset = SYNC_BITSET_INIT;
+macro_rules! dup {
+    ((* $($exp:tt)*) $($val:tt)*) => (dup!( ($($exp)*) $($val)* $($val)* ));
+    (() $($val:tt)*) => ([$($val),*])
+}
+static TLS_DESTRUCTOR: [AtomicUsize; TLS_KEYS] = dup!((* * * * * * *) ATOMIC_USIZE_INIT);
+
+extern "C" {
+    fn get_tls_ptr() -> *const u8;
+    fn set_tls_ptr(tls: *const u8);
+}
+
+#[derive(Copy, Clone)]
+#[repr(C)]
+pub struct Key(NonZeroUsize);
+
+impl Key {
+    fn to_index(self) -> usize {
+        self.0.get() - 1
+    }
+
+    fn from_index(index: usize) -> Self {
+        Key(NonZeroUsize::new(index + 1).unwrap())
+    }
+
+    pub fn as_usize(self) -> usize {
+        self.0.get()
+    }
+
+    pub fn from_usize(index: usize) -> Self {
+        Key(NonZeroUsize::new(index).unwrap())
+    }
+}
+
+#[repr(C)]
+pub struct Tls {
+    data: [Cell<*mut u8>; TLS_KEYS]
+}
+
+pub struct ActiveTls<'a> {
+    tls: &'a Tls
+}
+
+impl<'a> Drop for ActiveTls<'a> {
+    fn drop(&mut self) {
+        let value_with_destructor = |key: usize| {
+            let ptr = TLS_DESTRUCTOR[key].load(Ordering::Relaxed);
+            unsafe { mem::transmute::<_,Option<unsafe extern fn(*mut u8)>>(ptr) }
+                .map(|dtor| (&self.tls.data[key], dtor))
+        };
+
+        let mut any_non_null_dtor = true;
+        while any_non_null_dtor {
+            any_non_null_dtor = false;
+            for (value, dtor) in TLS_KEY_IN_USE.iter().filter_map(&value_with_destructor) {
+                let value = value.replace(ptr::null_mut());
+                if value != ptr::null_mut() {
+                    any_non_null_dtor = true;
+                    unsafe { dtor(value) }
+                }
+            }
+        }
+    }
+}
+
+impl Tls {
+    pub fn new() -> Tls {
+        Tls { data: dup!((* * * * * * *) (Cell::new(ptr::null_mut()))) }
+    }
+
+    pub unsafe fn activate(&self) -> ActiveTls {
+        set_tls_ptr(self as *const Tls as _);
+        ActiveTls { tls: self }
+    }
+
+    #[allow(unused)]
+    pub unsafe fn activate_persistent(self: Box<Self>) {
+        set_tls_ptr((&*self) as *const Tls as _);
+        mem::forget(self);
+    }
+
+    unsafe fn current<'a>() -> &'a Tls {
+        &*(get_tls_ptr() as *const Tls)
+    }
+
+    pub fn create(dtor: Option<unsafe extern fn(*mut u8)>) -> Key {
+        let index = TLS_KEY_IN_USE.set().expect("TLS limit exceeded");
+        TLS_DESTRUCTOR[index].store(dtor.map_or(0, |f| f as usize), Ordering::Relaxed);
+        Key::from_index(index)
+    }
+
+    pub fn set(key: Key, value: *mut u8) {
+        let index = key.to_index();
+        assert!(TLS_KEY_IN_USE.get(index));
+        unsafe { Self::current() }.data[index].set(value);
+    }
+
+    pub fn get(key: Key) -> *mut u8 {
+        let index = key.to_index();
+        assert!(TLS_KEY_IN_USE.get(index));
+        unsafe { Self::current() }.data[index].get()
+    }
+
+    pub fn destroy(key: Key) {
+        TLS_KEY_IN_USE.clear(key.to_index());
+    }
+}
+
+mod sync_bitset {
+    use sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
+    use iter::{Enumerate, Peekable};
+    use slice::Iter;
+    use super::{TLS_KEYS_BITSET_SIZE, USIZE_BITS};
+
+    /// A bitset that can be used synchronously.
+    pub(super) struct SyncBitset([AtomicUsize; TLS_KEYS_BITSET_SIZE]);
+
+    pub(super) const SYNC_BITSET_INIT: SyncBitset =
+        SyncBitset([ATOMIC_USIZE_INIT, ATOMIC_USIZE_INIT]);
+
+    impl SyncBitset {
+        pub fn get(&self, index: usize) -> bool {
+            let (hi, lo) = Self::split(index);
+            (self.0[hi].load(Ordering::Relaxed) & lo) != 0
+        }
+
+        /// Not atomic.
+        pub fn iter(&self) -> SyncBitsetIter {
+            SyncBitsetIter {
+                iter: self.0.iter().enumerate().peekable(),
+                elem_idx: 0,
+            }
+        }
+
+        pub fn clear(&self, index: usize) {
+            let (hi, lo) = Self::split(index);
+            self.0[hi].fetch_and(!lo, Ordering::Relaxed);
+        }
+
+        /// Set any unset bit. Not atomic. Returns `None` if all bits were
+        /// observed to be set.
+        pub fn set(&self) -> Option<usize> {
+            'elems: for (idx, elem) in self.0.iter().enumerate() {
+                let mut current = elem.load(Ordering::Relaxed);
+                loop {
+                    if 0 == !current {
+                        continue 'elems;
+                    }
+                    let trailing_ones = (!current).trailing_zeros() as usize;
+                    match elem.compare_exchange(
+                        current,
+                        current | (1 << trailing_ones),
+                        Ordering::AcqRel,
+                        Ordering::Relaxed
+                    ) {
+                        Ok(_) => return Some(idx * USIZE_BITS + trailing_ones),
+                        Err(previous) => current = previous,
+                    }
+                }
+            }
+            None
+        }
+
+        fn split(index: usize) -> (usize, usize) {
+            (index / USIZE_BITS, 1 << (index % USIZE_BITS))
+        }
+    }
+
+    pub(super) struct SyncBitsetIter<'a> {
+        iter: Peekable<Enumerate<Iter<'a, AtomicUsize>>>,
+        elem_idx: usize,
+    }
+
+    impl<'a> Iterator for SyncBitsetIter<'a> {
+        type Item = usize;
+
+        fn next(&mut self) -> Option<usize> {
+            self.iter.peek().cloned().and_then(|(idx, elem)| {
+                let elem = elem.load(Ordering::Relaxed);
+                let low_mask = (1 << self.elem_idx) - 1;
+                let next = elem & !low_mask;
+                let next_idx = next.trailing_zeros() as usize;
+                self.elem_idx = next_idx + 1;
+                if self.elem_idx >= 64 {
+                    self.elem_idx = 0;
+                    self.iter.next();
+                }
+                match next_idx {
+                    64 => self.next(),
+                    _ => Some(idx * USIZE_BITS + next_idx),
+                }
+            })
+        }
+    }
+
+    #[cfg(test)]
+    mod tests {
+        use super::*;
+
+        fn test_data(bitset: [usize; 2], bit_indices: &[usize]) {
+            let set = SyncBitset([AtomicUsize::new(bitset[0]), AtomicUsize::new(bitset[1])]);
+            assert_eq!(set.iter().collect::<Vec<_>>(), bit_indices);
+            for &i in bit_indices {
+                assert!(set.get(i));
+            }
+        }
+
+        #[test]
+        fn iter() {
+            test_data([0b0110_1001, 0], &[0, 3, 5, 6]);
+            test_data([0x8000_0000_0000_0000, 0x8000_0000_0000_0001], &[63, 64, 127]);
+            test_data([0, 0], &[]);
+        }
+
+        #[test]
+        fn set_get_clear() {
+            let set = SYNC_BITSET_INIT;
+            let key = set.set().unwrap();
+            assert!(set.get(key));
+            set.clear(key);
+            assert!(!set.get(key));
+        }
+    }
+}
diff --git a/src/libstd/sys/sgx/abi/usercalls/alloc.rs b/src/libstd/sys/sgx/abi/usercalls/alloc.rs
new file mode 100644
index 00000000000..64968a9970c
--- /dev/null
+++ b/src/libstd/sys/sgx/abi/usercalls/alloc.rs
@@ -0,0 +1,404 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![allow(unused)]
+
+use ptr;
+use mem;
+use cell::UnsafeCell;
+use slice;
+use ops::{Deref, DerefMut, Index, IndexMut, CoerceUnsized};
+use slice::SliceIndex;
+
+use fortanix_sgx_abi::*;
+use super::super::mem::is_user_range;
+
+/// A type that can be safely read from or written to userspace.
+///
+/// Non-exhaustive list of specific requirements for reading and writing:
+/// * **Type is `Copy`** (and therefore also not `Drop`). Copies will be
+///   created when copying from/to userspace. Destructors will not be called.
+/// * **No references or Rust-style owned pointers** (`Vec`, `Arc`, etc.). When
+///   reading from userspace, references into enclave memory must not be
+///   created. Also, only enclave memory is considered managed by the Rust
+///   compiler's static analysis. When reading from userspace, there can be no
+///   guarantee that the value correctly adheres to the expectations of the
+///   type. When writing to userspace, memory addresses of data in enclave
+///   memory must not be leaked for confidentiality reasons. `User` and
+///   `UserRef` are also not allowed for the same reasons.
+/// * **No fat pointers.** When reading from userspace, the size or vtable
+///   pointer could be automatically interpreted and used by the code. When
+///   writing to userspace, memory addresses of data in enclave memory (such
+///   as vtable pointers) must not be leaked for confidentiality reasons.
+///
+/// Non-exhaustive list of specific requirements for reading from userspace:
+/// * Any bit pattern is valid for this type (no `enum`s). There can be no
+///   guarantee that the value correctly adheres to the expectations of the
+///   type, so any value must be valid for this type.
+///
+/// Non-exhaustive list of specific requirements for writing to userspace:
+/// * No pointers to enclave memory. Memory addresses of data in enclave memory
+///   must not be leaked for confidentiality reasons.
+/// * No internal padding. Padding might contain previously-initialized secret
+///   data stored at that memory location and must not be leaked for
+///   confidentiality reasons.
+pub unsafe trait UserSafeSized: Copy + Sized {}
+
+unsafe impl UserSafeSized for u8 {}
+unsafe impl<T> UserSafeSized for FifoDescriptor<T> {}
+unsafe impl UserSafeSized for ByteBuffer {}
+unsafe impl UserSafeSized for Usercall {}
+unsafe impl UserSafeSized for Return {}
+unsafe impl<T: UserSafeSized> UserSafeSized for [T; 2] {}
+
+/// A type that can be represented in memory as one or more `UserSafeSized`s.
+pub unsafe trait UserSafe {
+    unsafe fn align_of() -> usize;
+
+    /// NB. This takes a size, not a length!
+    unsafe fn from_raw_sized_unchecked(ptr: *const u8, size: usize) -> *const Self;
+
+    /// NB. This takes a size, not a length!
+    unsafe fn from_raw_sized(ptr: *const u8, size: usize) -> *const Self {
+        let ret = Self::from_raw_sized_unchecked(ptr, size);
+        Self::check_ptr(ret);
+        ret
+    }
+
+    unsafe fn check_ptr(ptr: *const Self) {
+        let is_aligned = |p| -> bool {
+            0 == (p as usize) & (Self::align_of() - 1)
+        };
+
+        assert!(is_aligned(ptr as *const u8));
+        assert!(is_user_range(ptr as _, mem::size_of_val(&*ptr)));
+        assert!(!ptr.is_null());
+    }
+}
+
+unsafe impl<T: UserSafeSized> UserSafe for T {
+    unsafe fn align_of() -> usize {
+        mem::align_of::<T>()
+    }
+
+    unsafe fn from_raw_sized_unchecked(ptr: *const u8, size: usize) -> *const Self {
+        assert_eq!(size, mem::size_of::<T>());
+        ptr as _
+    }
+}
+
+unsafe impl<T: UserSafeSized> UserSafe for [T] {
+    unsafe fn align_of() -> usize {
+        mem::align_of::<T>()
+    }
+
+    unsafe fn from_raw_sized_unchecked(ptr: *const u8, size: usize) -> *const Self {
+        let elem_size = mem::size_of::<T>();
+        assert_eq!(size % elem_size, 0);
+        let len = size / elem_size;
+        slice::from_raw_parts(ptr as _, len)
+    }
+}
+
+/// A reference to some type in userspace memory. `&UserRef<T>` is equivalent
+/// to `&T` in enclave memory. Access to the memory is only allowed by copying
+/// to avoid TOCTTOU issues. After copying, code should make sure to completely
+/// check the value before use.
+pub struct UserRef<T: ?Sized>(UnsafeCell<T>);
+/// An owned type in userspace memory. `User<T>` is equivalent to `Box<T>` in
+/// enclave memory. Access to the memory is only allowed by copying to avoid
+/// TOCTTOU issues. The user memory will be freed when the value is dropped.
+/// After copying, code should make sure to completely check the value before
+/// use.
+pub struct User<T: UserSafe + ?Sized>(*mut UserRef<T>);
+
+impl<T: ?Sized> User<T> where T: UserSafe {
+    // This function returns memory that is practically uninitialized, but is
+    // not considered "unspecified" or "undefined" for purposes of an
+    // optimizing compiler. This is achieved by returning a pointer from
+    // from outside as obtained by `super::alloc`.
+    fn new_uninit_bytes(size: usize) -> Self {
+        unsafe {
+            let ptr = super::alloc(size, T::align_of()).expect("User memory allocation failed");
+            User(T::from_raw_sized(ptr as _, size) as _)
+        }
+    }
+
+    pub fn new_from_enclave(val: &T) -> Self {
+        unsafe {
+            let ret = Self::new_uninit_bytes(mem::size_of_val(val));
+            ptr::copy(
+                val as *const T as *const u8,
+                ret.0 as *mut T as *mut u8,
+                mem::size_of_val(val)
+            );
+            ret
+        }
+    }
+
+    /// Create an owned `User<T>` from a raw pointer. The pointer should be
+    /// freeable with the `free` usercall and the alignment of `T`.
+    ///
+    /// # Panics
+    /// This function panics if:
+    ///
+    /// * The pointer is not aligned
+    /// * The pointer is null
+    /// * The pointed-to range is not in user memory
+    pub unsafe fn from_raw(ptr: *mut T) -> Self {
+        T::check_ptr(ptr);
+        User(ptr as _)
+    }
+
+    /// Convert this value into a raw pointer. The value will no longer be
+    /// automatically freed.
+    pub fn into_raw(self) -> *mut T {
+        let ret = self.0;
+        mem::forget(self);
+        ret as _
+    }
+}
+
+impl<T> User<T> where T: UserSafe {
+    pub fn uninitialized() -> Self {
+        Self::new_uninit_bytes(mem::size_of::<T>())
+    }
+}
+
+impl<T> User<[T]> where [T]: UserSafe {
+    pub fn uninitialized(n: usize) -> Self {
+        Self::new_uninit_bytes(n * mem::size_of::<T>())
+    }
+
+    /// Create an owned `User<[T]>` from a raw thin pointer and a slice length.
+    /// The pointer should be freeable with the `free` usercall and the
+    /// alignment of `T`.
+    ///
+    /// # Panics
+    /// This function panics if:
+    ///
+    /// * The pointer is not aligned
+    /// * The pointer is null
+    /// * The pointed-to range is not in user memory
+    pub unsafe fn from_raw_parts(ptr: *mut T, len: usize) -> Self {
+        User(<[T]>::from_raw_sized(ptr as _, len * mem::size_of::<T>()) as _)
+    }
+}
+
+impl<T: ?Sized> UserRef<T> where T: UserSafe {
+    /// Create a `&UserRef<[T]>` from a raw pointer.
+    ///
+    /// # Panics
+    /// This function panics if:
+    ///
+    /// * The pointer is not aligned
+    /// * The pointer is null
+    /// * The pointed-to range is not in user memory
+    pub unsafe fn from_ptr<'a>(ptr: *const T) -> &'a Self {
+        T::check_ptr(ptr);
+        &*(ptr as *const Self)
+    }
+
+    /// Create a `&mut UserRef<[T]>` from a raw pointer.
+    ///
+    /// # Panics
+    /// This function panics if:
+    ///
+    /// * The pointer is not aligned
+    /// * The pointer is null
+    /// * The pointed-to range is not in user memory
+    pub unsafe fn from_mut_ptr<'a>(ptr: *mut T) -> &'a mut Self {
+        T::check_ptr(ptr);
+        &mut*(ptr as *mut Self)
+    }
+
+    /// # Panics
+    /// This function panics if the destination doesn't have the same size as
+    /// the source. This can happen for dynamically-sized types such as slices.
+    pub fn copy_from_enclave(&mut self, val: &T) {
+        unsafe {
+            assert_eq!(mem::size_of_val(val), mem::size_of_val( &*self.0.get() ));
+            ptr::copy(
+                val as *const T as *const u8,
+                self.0.get() as *mut T as *mut u8,
+                mem::size_of_val(val)
+            );
+        }
+    }
+
+    /// # Panics
+    /// This function panics if the destination doesn't have the same size as
+    /// the source. This can happen for dynamically-sized types such as slices.
+    pub fn copy_to_enclave(&self, dest: &mut T) {
+        unsafe {
+            assert_eq!(mem::size_of_val(dest), mem::size_of_val( &*self.0.get() ));
+            ptr::copy(
+                self.0.get() as *const T as *const u8,
+                dest as *mut T as *mut u8,
+                mem::size_of_val(dest)
+            );
+        }
+    }
+
+    pub fn as_raw_ptr(&self) -> *const T {
+        self as *const _ as _
+    }
+
+    pub fn as_raw_mut_ptr(&mut self) -> *mut T {
+        self as *mut _ as _
+    }
+}
+
+impl<T> UserRef<T> where T: UserSafe {
+    pub fn to_enclave(&self) -> T {
+        unsafe { ptr::read(self.0.get()) }
+    }
+}
+
+impl<T> UserRef<[T]> where [T]: UserSafe {
+    /// Create a `&UserRef<[T]>` from a raw thin pointer and a slice length.
+    ///
+    /// # Panics
+    /// This function panics if:
+    ///
+    /// * The pointer is not aligned
+    /// * The pointer is null
+    /// * The pointed-to range is not in user memory
+    pub unsafe fn from_raw_parts<'a>(ptr: *const T, len: usize) -> &'a Self {
+        &*(<[T]>::from_raw_sized(ptr as _, len * mem::size_of::<T>()) as *const Self)
+    }
+
+    /// Create a `&mut UserRef<[T]>` from a raw thin pointer and a slice length.
+    ///
+    /// # Panics
+    /// This function panics if:
+    ///
+    /// * The pointer is not aligned
+    /// * The pointer is null
+    /// * The pointed-to range is not in user memory
+    pub unsafe fn from_raw_parts_mut<'a>(ptr: *mut T, len: usize) -> &'a mut Self {
+        &mut*(<[T]>::from_raw_sized(ptr as _, len * mem::size_of::<T>()) as *mut Self)
+    }
+
+    pub fn as_ptr(&self) -> *const T {
+        self.0.get() as _
+    }
+
+    pub fn as_mut_ptr(&mut self) -> *mut T {
+        self.0.get() as _
+    }
+
+    pub fn len(&self) -> usize {
+        unsafe { (*self.0.get()).len() }
+    }
+
+    pub fn copy_to_enclave_vec(&self, dest: &mut Vec<T>) {
+        unsafe {
+            if let Some(missing) = self.len().checked_sub(dest.capacity()) {
+                dest.reserve(missing)
+            }
+            dest.set_len(self.len());
+            self.copy_to_enclave(&mut dest[..]);
+        }
+    }
+
+    pub fn to_enclave(&self) -> Vec<T> {
+        let mut ret = Vec::with_capacity(self.len());
+        self.copy_to_enclave_vec(&mut ret);
+        ret
+    }
+
+    pub fn iter(&self) -> Iter<T>
+        where T: UserSafe // FIXME: should be implied by [T]: UserSafe?
+    {
+        unsafe {
+            Iter((&*self.as_raw_ptr()).iter())
+        }
+    }
+
+    pub fn iter_mut(&mut self) -> IterMut<T>
+        where T: UserSafe // FIXME: should be implied by [T]: UserSafe?
+    {
+        unsafe {
+            IterMut((&mut*self.as_raw_mut_ptr()).iter_mut())
+        }
+    }
+}
+
+pub struct Iter<'a, T: 'a + UserSafe>(slice::Iter<'a, T>);
+
+impl<'a, T: UserSafe> Iterator for Iter<'a, T> {
+    type Item = &'a UserRef<T>;
+
+    #[inline]
+    fn next(&mut self) -> Option<Self::Item> {
+        unsafe {
+            self.0.next().map(|e| UserRef::from_ptr(e))
+        }
+    }
+}
+
+pub struct IterMut<'a, T: 'a + UserSafe>(slice::IterMut<'a, T>);
+
+impl<'a, T: UserSafe> Iterator for IterMut<'a, T> {
+    type Item = &'a mut UserRef<T>;
+
+    #[inline]
+    fn next(&mut self) -> Option<Self::Item> {
+        unsafe {
+            self.0.next().map(|e| UserRef::from_mut_ptr(e))
+        }
+    }
+}
+
+impl<T: ?Sized> Deref for User<T> where T: UserSafe {
+    type Target = UserRef<T>;
+
+    fn deref(&self) -> &Self::Target {
+        unsafe { &*self.0 }
+    }
+}
+
+impl<T: ?Sized> DerefMut for User<T> where T: UserSafe {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        unsafe { &mut*self.0 }
+    }
+}
+
+impl<T: ?Sized> Drop for User<T> where T: UserSafe {
+    fn drop(&mut self) {
+        unsafe {
+            let ptr = (*self.0).0.get();
+            super::free(ptr as _, mem::size_of_val(&mut*ptr), T::align_of());
+        }
+    }
+}
+
+impl<T: CoerceUnsized<U>, U> CoerceUnsized<UserRef<U>> for UserRef<T> {}
+
+impl<T, I: SliceIndex<[T]>> Index<I> for UserRef<[T]> where [T]: UserSafe, I::Output: UserSafe {
+    type Output = UserRef<I::Output>;
+
+    #[inline]
+    fn index(&self, index: I) -> &UserRef<I::Output> {
+        unsafe {
+            UserRef::from_ptr(index.index(&*self.as_raw_ptr()))
+        }
+    }
+}
+
+impl<T, I: SliceIndex<[T]>> IndexMut<I> for UserRef<[T]> where [T]: UserSafe, I::Output: UserSafe {
+    #[inline]
+    fn index_mut(&mut self, index: I) -> &mut UserRef<I::Output> {
+        unsafe {
+            UserRef::from_mut_ptr(index.index_mut(&mut*self.as_raw_mut_ptr()))
+        }
+    }
+}
diff --git a/src/libstd/sys/sgx/abi/usercalls/mod.rs b/src/libstd/sys/sgx/abi/usercalls/mod.rs
new file mode 100644
index 00000000000..d1d180e4825
--- /dev/null
+++ b/src/libstd/sys/sgx/abi/usercalls/mod.rs
@@ -0,0 +1,189 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+pub use fortanix_sgx_abi::*;
+
+use io::{Error as IoError, Result as IoResult};
+use time::Duration;
+
+pub mod alloc;
+#[macro_use]
+mod raw;
+
+pub(crate) fn copy_user_buffer(buf: &alloc::UserRef<ByteBuffer>) -> Vec<u8> {
+    unsafe {
+        let buf = buf.to_enclave();
+        alloc::User::from_raw_parts(buf.data as _, buf.len).to_enclave()
+    }
+}
+
+pub fn read(fd: Fd, buf: &mut [u8]) -> IoResult<usize> {
+    unsafe {
+        let mut userbuf = alloc::User::<[u8]>::uninitialized(buf.len());
+        let len = raw::read(fd, userbuf.as_mut_ptr(), userbuf.len()).from_sgx_result()?;
+        userbuf[..len].copy_to_enclave(&mut buf[..len]);
+        Ok(len)
+    }
+}
+
+pub fn write(fd: Fd, buf: &[u8]) -> IoResult<usize> {
+    unsafe {
+        let userbuf = alloc::User::new_from_enclave(buf);
+        raw::write(fd, userbuf.as_ptr(), userbuf.len()).from_sgx_result()
+    }
+}
+
+pub fn flush(fd: Fd) -> IoResult<()> {
+    unsafe { raw::flush(fd).from_sgx_result() }
+}
+
+pub fn close(fd: Fd) {
+    unsafe { raw::close(fd) }
+}
+
+fn string_from_bytebuffer(buf: &alloc::UserRef<ByteBuffer>, usercall: &str, arg: &str) -> String {
+    String::from_utf8(copy_user_buffer(buf))
+        .unwrap_or_else(|_| panic!("Usercall {}: expected {} to be valid UTF-8", usercall, arg))
+}
+
+pub fn bind_stream(addr: &str) -> IoResult<(Fd, String)> {
+    unsafe {
+        let addr_user = alloc::User::new_from_enclave(addr.as_bytes());
+        let mut local = alloc::User::<ByteBuffer>::uninitialized();
+        let fd = raw::bind_stream(
+            addr_user.as_ptr(),
+            addr_user.len(),
+            local.as_raw_mut_ptr()
+        ).from_sgx_result()?;
+        let local = string_from_bytebuffer(&local, "bind_stream", "local_addr");
+        Ok((fd, local))
+    }
+}
+
+pub fn accept_stream(fd: Fd) -> IoResult<(Fd, String, String)> {
+    unsafe {
+        let mut bufs = alloc::User::<[ByteBuffer; 2]>::uninitialized();
+        let mut buf_it = alloc::UserRef::iter_mut(&mut *bufs); // FIXME: can this be done
+                                                               // without forcing coercion?
+        let (local, peer) = (buf_it.next().unwrap(), buf_it.next().unwrap());
+        let fd = raw::accept_stream(
+            fd,
+            local.as_raw_mut_ptr(),
+            peer.as_raw_mut_ptr()
+        ).from_sgx_result()?;
+        let local = string_from_bytebuffer(&local, "accept_stream", "local_addr");
+        let peer = string_from_bytebuffer(&peer, "accept_stream", "peer_addr");
+        Ok((fd, local, peer))
+    }
+}
+
+pub fn connect_stream(addr: &str) -> IoResult<(Fd, String, String)> {
+    unsafe {
+        let addr_user = alloc::User::new_from_enclave(addr.as_bytes());
+        let mut bufs = alloc::User::<[ByteBuffer; 2]>::uninitialized();
+        let mut buf_it = alloc::UserRef::iter_mut(&mut *bufs); // FIXME: can this be done
+                                                               // without forcing coercion?
+        let (local, peer) = (buf_it.next().unwrap(), buf_it.next().unwrap());
+        let fd = raw::connect_stream(
+            addr_user.as_ptr(),
+            addr_user.len(),
+            local.as_raw_mut_ptr(),
+            peer.as_raw_mut_ptr()
+        ).from_sgx_result()?;
+        let local = string_from_bytebuffer(&local, "connect_stream", "local_addr");
+        let peer = string_from_bytebuffer(&peer, "connect_stream", "peer_addr");
+        Ok((fd, local, peer))
+    }
+}
+
+pub fn launch_thread() -> IoResult<()> {
+    unsafe { raw::launch_thread().from_sgx_result() }
+}
+
+pub fn exit(panic: bool) -> ! {
+    unsafe { raw::exit(panic) }
+}
+
+pub fn wait(event_mask: u64, timeout: u64) -> IoResult<u64> {
+    unsafe { raw::wait(event_mask, timeout).from_sgx_result() }
+}
+
+pub fn send(event_set: u64, tcs: Option<Tcs>) -> IoResult<()> {
+    unsafe { raw::send(event_set, tcs).from_sgx_result() }
+}
+
+pub fn insecure_time() -> Duration {
+    let t = unsafe { raw::insecure_time() };
+    Duration::new(t / 1_000_000_000, (t % 1_000_000_000) as _)
+}
+
+pub fn alloc(size: usize, alignment: usize) -> IoResult<*mut u8> {
+    unsafe { raw::alloc(size, alignment).from_sgx_result() }
+}
+
+pub use self::raw::free;
+
+fn check_os_error(err: Result) -> i32 {
+    // FIXME: not sure how to make sure all variants of Error are covered
+    if err == Error::NotFound as _ ||
+       err == Error::PermissionDenied as _ ||
+       err == Error::ConnectionRefused as _ ||
+       err == Error::ConnectionReset as _ ||
+       err == Error::ConnectionAborted as _ ||
+       err == Error::NotConnected as _ ||
+       err == Error::AddrInUse as _ ||
+       err == Error::AddrNotAvailable as _ ||
+       err == Error::BrokenPipe as _ ||
+       err == Error::AlreadyExists as _ ||
+       err == Error::WouldBlock as _ ||
+       err == Error::InvalidInput as _ ||
+       err == Error::InvalidData as _ ||
+       err == Error::TimedOut as _ ||
+       err == Error::WriteZero as _ ||
+       err == Error::Interrupted as _ ||
+       err == Error::Other as _ ||
+       err == Error::UnexpectedEof as _ ||
+       ((Error::UserRangeStart as _)..=(Error::UserRangeEnd as _)).contains(&err)
+    {
+        err
+    } else {
+        panic!("Usercall: returned invalid error value {}", err)
+    }
+}
+
+trait FromSgxResult {
+    type Return;
+
+    fn from_sgx_result(self) -> IoResult<Self::Return>;
+}
+
+impl<T> FromSgxResult for (Result, T) {
+    type Return = T;
+
+    fn from_sgx_result(self) -> IoResult<Self::Return> {
+        if self.0 == RESULT_SUCCESS {
+            Ok(self.1)
+        } else {
+            Err(IoError::from_raw_os_error(check_os_error(self.0)))
+        }
+    }
+}
+
+impl FromSgxResult for Result {
+    type Return = ();
+
+    fn from_sgx_result(self) -> IoResult<Self::Return> {
+        if self == RESULT_SUCCESS {
+            Ok(())
+        } else {
+            Err(IoError::from_raw_os_error(check_os_error(self)))
+        }
+    }
+}
diff --git a/src/libstd/sys/sgx/abi/usercalls/raw.rs b/src/libstd/sys/sgx/abi/usercalls/raw.rs
new file mode 100644
index 00000000000..a28d41c1b74
--- /dev/null
+++ b/src/libstd/sys/sgx/abi/usercalls/raw.rs
@@ -0,0 +1,231 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![allow(unused)]
+
+use fortanix_sgx_abi::*;
+
+use ptr::NonNull;
+
+#[repr(C)]
+struct UsercallReturn(u64, u64);
+
+extern "C" {
+    fn usercall(nr: u64, p1: u64, p2: u64, _ignore: u64, p3: u64, p4: u64) -> UsercallReturn;
+}
+
+unsafe fn do_usercall(nr: u64, p1: u64, p2: u64, p3: u64, p4: u64) -> (u64, u64) {
+    if nr==0 { panic!("Invalid usercall number {}",nr) }
+    let UsercallReturn(a, b) = usercall(nr,p1,p2,0,p3,p4);
+    (a, b)
+}
+
+type Register = u64;
+
+trait RegisterArgument {
+    fn from_register(Register) -> Self;
+    fn into_register(self) -> Register;
+}
+
+trait ReturnValue {
+    fn from_registers(call: &'static str, regs: (Register, Register)) -> Self;
+}
+
+macro_rules! define_usercalls {
+    // Using `$r:tt` because `$r:ty` doesn't match ! in `clobber_diverging`
+    ($(fn $f:ident($($n:ident: $t:ty),*) $(-> $r:tt)*; )*) => {
+        #[repr(C)]
+        #[allow(non_camel_case_types)]
+        enum Usercalls {
+            __enclave_usercalls_invalid,
+            $($f,)*
+        }
+
+        $(enclave_usercalls_internal_define_usercalls!(def fn $f($($n: $t),*) $(-> $r)*);)*
+    };
+}
+
+macro_rules! define_usercalls_asm {
+    ($(fn $f:ident($($n:ident: $t:ty),*) $(-> $r:ty)*; )*) => {
+        macro_rules! usercalls_asm {
+            () => {
+                concat!(
+                    ".equ usercall_nr_LAST, 0\n",
+                    $(
+                    ".equ usercall_nr_", stringify!($f), ", usercall_nr_LAST+1\n",
+                    ".equ usercall_nr_LAST, usercall_nr_", stringify!($f), "\n"
+                    ),*
+                )
+            }
+        }
+    };
+}
+
+macro_rules! define_ra {
+    (< $i:ident > $t:ty) => {
+        impl<$i> RegisterArgument for $t {
+            fn from_register(a: Register) -> Self {
+                a as _
+            }
+            fn into_register(self) -> Register {
+                self as _
+            }
+        }
+    };
+    ($i:ty as $t:ty) => {
+        impl RegisterArgument for $t {
+            fn from_register(a: Register) -> Self {
+                a as $i as _
+            }
+            fn into_register(self) -> Register {
+                self as $i as _
+            }
+        }
+    };
+    ($t:ty) => {
+        impl RegisterArgument for $t {
+            fn from_register(a: Register) -> Self {
+                a as _
+            }
+            fn into_register(self) -> Register {
+                self as _
+            }
+        }
+    };
+}
+
+define_ra!(Register);
+define_ra!(i64);
+define_ra!(u32);
+define_ra!(u32 as i32);
+define_ra!(u16);
+define_ra!(u16 as i16);
+define_ra!(u8);
+define_ra!(u8 as i8);
+define_ra!(usize);
+define_ra!(usize as isize);
+define_ra!(<T> *const T);
+define_ra!(<T> *mut T);
+
+impl RegisterArgument for bool {
+    fn from_register(a: Register) -> bool {
+        if a != 0 {
+            true
+        } else {
+            false
+        }
+    }
+    fn into_register(self) -> Register {
+        self as _
+    }
+}
+
+impl<T: RegisterArgument> RegisterArgument for Option<NonNull<T>> {
+    fn from_register(a: Register) -> Option<NonNull<T>> {
+        NonNull::new(a as _)
+    }
+    fn into_register(self) -> Register {
+        self.map_or(0 as _, NonNull::as_ptr) as _
+    }
+}
+
+impl ReturnValue for ! {
+    fn from_registers(call: &'static str, _regs: (Register, Register)) -> Self {
+        panic!("Usercall {}: did not expect to be re-entered", call);
+    }
+}
+
+impl ReturnValue for () {
+    fn from_registers(call: &'static str, regs: (Register, Register)) -> Self {
+        assert_eq!(regs.0, 0, "Usercall {}: expected {} return value to be 0", call, "1st");
+        assert_eq!(regs.1, 0, "Usercall {}: expected {} return value to be 0", call, "2nd");
+        ()
+    }
+}
+
+impl<T: RegisterArgument> ReturnValue for T {
+    fn from_registers(call: &'static str, regs: (Register, Register)) -> Self {
+        assert_eq!(regs.1, 0, "Usercall {}: expected {} return value to be 0", call, "2nd");
+        T::from_register(regs.0)
+    }
+}
+
+impl<T: RegisterArgument, U: RegisterArgument> ReturnValue for (T, U) {
+    fn from_registers(_call: &'static str, regs: (Register, Register)) -> Self {
+        (
+            T::from_register(regs.0),
+            U::from_register(regs.1)
+        )
+    }
+}
+
+macro_rules! enclave_usercalls_internal_define_usercalls {
+    (def fn $f:ident($n1:ident: $t1:ty, $n2:ident: $t2:ty,
+                     $n3:ident: $t3:ty, $n4:ident: $t4:ty) -> $r:ty) => (
+        #[inline(always)]
+        pub unsafe fn $f($n1: $t1, $n2: $t2, $n3: $t3, $n4: $t4) -> $r {
+            ReturnValue::from_registers(stringify!($f), do_usercall(
+                Usercalls::$f as Register,
+                RegisterArgument::into_register($n1),
+                RegisterArgument::into_register($n2),
+                RegisterArgument::into_register($n3),
+                RegisterArgument::into_register($n4),
+            ))
+        }
+    );
+    (def fn $f:ident($n1:ident: $t1:ty, $n2:ident: $t2:ty, $n3:ident: $t3:ty) -> $r:ty) => (
+        #[inline(always)]
+        pub unsafe fn $f($n1: $t1, $n2: $t2, $n3: $t3) -> $r {
+            ReturnValue::from_registers(stringify!($f), do_usercall(
+                Usercalls::$f as Register,
+                RegisterArgument::into_register($n1),
+                RegisterArgument::into_register($n2),
+                RegisterArgument::into_register($n3),
+                0
+            ))
+        }
+    );
+    (def fn $f:ident($n1:ident: $t1:ty, $n2:ident: $t2:ty) -> $r:ty) => (
+        #[inline(always)]
+        pub unsafe fn $f($n1: $t1, $n2: $t2) -> $r {
+            ReturnValue::from_registers(stringify!($f), do_usercall(
+                Usercalls::$f as Register,
+                RegisterArgument::into_register($n1),
+                RegisterArgument::into_register($n2),
+                0,0
+            ))
+        }
+    );
+    (def fn $f:ident($n1:ident: $t1:ty) -> $r:ty) => (
+        #[inline(always)]
+        pub unsafe fn $f($n1: $t1) -> $r {
+            ReturnValue::from_registers(stringify!($f), do_usercall(
+                Usercalls::$f as Register,
+                RegisterArgument::into_register($n1),
+                0,0,0
+            ))
+        }
+    );
+    (def fn $f:ident() -> $r:ty) => (
+        #[inline(always)]
+        pub unsafe fn $f() -> $r {
+            ReturnValue::from_registers(stringify!($f), do_usercall(
+                Usercalls::$f as Register,
+                0,0,0,0
+            ))
+        }
+    );
+    (def fn $f:ident($($n:ident: $t:ty),*)) => (
+        enclave_usercalls_internal_define_usercalls!(def fn $f($($n: $t),*) -> ());
+    );
+}
+
+invoke_with_usercalls!(define_usercalls);
+invoke_with_usercalls!(define_usercalls_asm);
diff --git a/src/libstd/sys/sgx/alloc.rs b/src/libstd/sys/sgx/alloc.rs
new file mode 100644
index 00000000000..83c20ace89b
--- /dev/null
+++ b/src/libstd/sys/sgx/alloc.rs
@@ -0,0 +1,42 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+extern crate dlmalloc;
+
+use alloc::{GlobalAlloc, Layout, System};
+
+use super::waitqueue::SpinMutex;
+
+// Using a SpinMutex because we never want to exit the enclave waiting for the
+// allocator.
+static DLMALLOC: SpinMutex<dlmalloc::Dlmalloc> = SpinMutex::new(dlmalloc::DLMALLOC_INIT);
+
+#[stable(feature = "alloc_system_type", since = "1.28.0")]
+unsafe impl GlobalAlloc for System {
+    #[inline]
+    unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
+        DLMALLOC.lock().malloc(layout.size(), layout.align())
+    }
+
+    #[inline]
+    unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
+        DLMALLOC.lock().calloc(layout.size(), layout.align())
+    }
+
+    #[inline]
+    unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
+        DLMALLOC.lock().free(ptr, layout.size(), layout.align())
+    }
+
+    #[inline]
+    unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
+        DLMALLOC.lock().realloc(ptr, layout.size(), layout.align(), new_size)
+    }
+}
diff --git a/src/libstd/sys/sgx/args.rs b/src/libstd/sys/sgx/args.rs
new file mode 100644
index 00000000000..8fb35d7ef98
--- /dev/null
+++ b/src/libstd/sys/sgx/args.rs
@@ -0,0 +1,75 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use ffi::OsString;
+use super::abi::usercalls::{copy_user_buffer, alloc, ByteBuffer};
+use sync::atomic::{AtomicUsize, Ordering};
+use sys::os_str::Buf;
+use sys_common::FromInner;
+use slice;
+
+static ARGS: AtomicUsize = AtomicUsize::new(0);
+type ArgsStore = Vec<OsString>;
+
+pub unsafe fn init(argc: isize, argv: *const *const u8) {
+    if argc != 0 {
+        let args = alloc::User::<[ByteBuffer]>::from_raw_parts(argv as _, argc as _);
+        let args = args.iter()
+            .map( |a| OsString::from_inner(Buf { inner: copy_user_buffer(a) }) )
+            .collect::<ArgsStore>();
+        ARGS.store(Box::into_raw(Box::new(args)) as _, Ordering::Relaxed);
+    }
+}
+
+pub unsafe fn cleanup() {
+    let args = ARGS.swap(0, Ordering::Relaxed);
+    if args != 0 {
+        drop(Box::<ArgsStore>::from_raw(args as _))
+    }
+}
+
+pub fn args() -> Args {
+    let args = unsafe { (ARGS.load(Ordering::Relaxed) as *const ArgsStore).as_ref() };
+    if let Some(args) = args {
+        Args(args.iter())
+    } else {
+        Args([].iter())
+    }
+}
+
+pub struct Args(slice::Iter<'static, OsString>);
+
+impl Args {
+    pub fn inner_debug(&self) -> &[OsString] {
+        self.0.as_slice()
+    }
+}
+
+impl Iterator for Args {
+    type Item = OsString;
+    fn next(&mut self) -> Option<OsString> {
+        self.0.next().cloned()
+    }
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        self.0.size_hint()
+    }
+}
+
+impl ExactSizeIterator for Args {
+    fn len(&self) -> usize {
+        self.0.len()
+    }
+}
+
+impl DoubleEndedIterator for Args {
+    fn next_back(&mut self) -> Option<OsString> {
+        self.0.next_back().cloned()
+    }
+}
diff --git a/src/libstd/sys/sgx/backtrace.rs b/src/libstd/sys/sgx/backtrace.rs
new file mode 100644
index 00000000000..ca4a7c9561c
--- /dev/null
+++ b/src/libstd/sys/sgx/backtrace.rs
@@ -0,0 +1,37 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use io;
+use sys::unsupported;
+use 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/sgx/cmath.rs b/src/libstd/sys/sgx/cmath.rs
new file mode 100644
index 00000000000..0c1300f61f8
--- /dev/null
+++ b/src/libstd/sys/sgx/cmath.rs
@@ -0,0 +1,41 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![cfg(not(test))]
+
+// These symbols are all defined in `compiler-builtins`
+extern {
+    pub fn acos(n: f64) -> f64;
+    pub fn acosf(n: f32) -> f32;
+    pub fn asin(n: f64) -> f64;
+    pub fn asinf(n: f32) -> f32;
+    pub fn atan(n: f64) -> f64;
+    pub fn atan2(a: f64, b: f64) -> f64;
+    pub fn atan2f(a: f32, b: f32) -> f32;
+    pub fn atanf(n: f32) -> f32;
+    pub fn cbrt(n: f64) -> f64;
+    pub fn cbrtf(n: f32) -> f32;
+    pub fn cosh(n: f64) -> f64;
+    pub fn coshf(n: f32) -> f32;
+    pub fn expm1(n: f64) -> f64;
+    pub fn expm1f(n: f32) -> f32;
+    pub fn fdim(a: f64, b: f64) -> f64;
+    pub fn fdimf(a: f32, b: f32) -> f32;
+    pub fn hypot(x: f64, y: f64) -> f64;
+    pub fn hypotf(x: f32, y: f32) -> f32;
+    pub fn log1p(n: f64) -> f64;
+    pub fn log1pf(n: f32) -> f32;
+    pub fn sinh(n: f64) -> f64;
+    pub fn sinhf(n: f32) -> f32;
+    pub fn tan(n: f64) -> f64;
+    pub fn tanf(n: f32) -> f32;
+    pub fn tanh(n: f64) -> f64;
+    pub fn tanhf(n: f32) -> f32;
+}
diff --git a/src/libstd/sys/sgx/condvar.rs b/src/libstd/sys/sgx/condvar.rs
new file mode 100644
index 00000000000..940f50f25b8
--- /dev/null
+++ b/src/libstd/sys/sgx/condvar.rs
@@ -0,0 +1,51 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use sys::mutex::Mutex;
+use time::Duration;
+
+use super::waitqueue::{WaitVariable, WaitQueue, SpinMutex};
+
+pub struct Condvar {
+    inner: SpinMutex<WaitVariable<()>>,
+}
+
+impl Condvar {
+    pub const fn new() -> Condvar {
+        Condvar { inner: SpinMutex::new(WaitVariable::new(())) }
+    }
+
+    #[inline]
+    pub unsafe fn init(&mut self) {}
+
+    #[inline]
+    pub unsafe fn notify_one(&self) {
+        let _ = WaitQueue::notify_one(self.inner.lock());
+    }
+
+    #[inline]
+    pub unsafe fn notify_all(&self) {
+        let _ = WaitQueue::notify_all(self.inner.lock());
+    }
+
+    pub unsafe fn wait(&self, mutex: &Mutex) {
+        let guard = self.inner.lock();
+        mutex.unlock();
+        WaitQueue::wait(guard);
+        mutex.lock()
+    }
+
+    pub unsafe fn wait_timeout(&self, _mutex: &Mutex, _dur: Duration) -> bool {
+        panic!("timeout not supported in SGX");
+    }
+
+    #[inline]
+    pub unsafe fn destroy(&self) {}
+}
diff --git a/src/libstd/sys/sgx/env.rs b/src/libstd/sys/sgx/env.rs
new file mode 100644
index 00000000000..146ce02754b
--- /dev/null
+++ b/src/libstd/sys/sgx/env.rs
@@ -0,0 +1,19 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+pub mod os {
+    pub const FAMILY: &'static str = "";
+    pub const OS: &'static str = "";
+    pub const DLL_PREFIX: &'static str = "";
+    pub const DLL_SUFFIX: &'static str = ".sgxs";
+    pub const DLL_EXTENSION: &'static str = "sgxs";
+    pub const EXE_SUFFIX: &'static str = ".sgxs";
+    pub const EXE_EXTENSION: &'static str = "sgxs";
+}
diff --git a/src/libstd/sys/sgx/fd.rs b/src/libstd/sys/sgx/fd.rs
new file mode 100644
index 00000000000..31c4199c6cd
--- /dev/null
+++ b/src/libstd/sys/sgx/fd.rs
@@ -0,0 +1,58 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use fortanix_sgx_abi::Fd;
+
+use io;
+use mem;
+use sys_common::AsInner;
+use super::abi::usercalls;
+
+#[derive(Debug)]
+pub struct FileDesc {
+    fd: Fd,
+}
+
+impl FileDesc {
+    pub fn new(fd: Fd) -> FileDesc {
+        FileDesc { fd: fd }
+    }
+
+    pub fn raw(&self) -> Fd { self.fd }
+
+    /// Extracts the actual filedescriptor without closing it.
+    pub fn into_raw(self) -> Fd {
+        let fd = self.fd;
+        mem::forget(self);
+        fd
+    }
+
+    pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
+        usercalls::read(self.fd, buf)
+    }
+
+    pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
+        usercalls::write(self.fd, buf)
+    }
+
+    pub fn flush(&self) -> io::Result<()> {
+        usercalls::flush(self.fd)
+    }
+}
+
+impl AsInner<Fd> for FileDesc {
+    fn as_inner(&self) -> &Fd { &self.fd }
+}
+
+impl Drop for FileDesc {
+    fn drop(&mut self) {
+        usercalls::close(self.fd)
+    }
+}
diff --git a/src/libstd/sys/sgx/fs.rs b/src/libstd/sys/sgx/fs.rs
new file mode 100644
index 00000000000..1dcea3e8eac
--- /dev/null
+++ b/src/libstd/sys/sgx/fs.rs
@@ -0,0 +1,304 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use ffi::OsString;
+use fmt;
+use hash::{Hash, Hasher};
+use io::{self, SeekFrom};
+use path::{Path, PathBuf};
+use sys::time::SystemTime;
+use 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/sgx/memchr.rs b/src/libstd/sys/sgx/memchr.rs
new file mode 100644
index 00000000000..0998bc5db4c
--- /dev/null
+++ b/src/libstd/sys/sgx/memchr.rs
@@ -0,0 +1,11 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+pub use core::slice::memchr::{memchr, memrchr};
diff --git a/src/libstd/sys/sgx/mod.rs b/src/libstd/sys/sgx/mod.rs
new file mode 100644
index 00000000000..dd6862e908e
--- /dev/null
+++ b/src/libstd/sys/sgx/mod.rs
@@ -0,0 +1,153 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! System bindings for the Fortanix SGX platform
+//!
+//! This module contains the facade (aka platform-specific) implementations of
+//! OS level functionality for Fortanix SGX.
+
+use io;
+use os::raw::c_char;
+use sync::atomic::{AtomicBool, Ordering};
+
+pub mod abi;
+mod waitqueue;
+
+pub mod alloc;
+pub mod args;
+#[cfg(feature = "backtrace")]
+pub mod backtrace;
+pub mod cmath;
+pub mod condvar;
+pub mod env;
+pub mod fd;
+pub mod fs;
+pub mod memchr;
+pub mod mutex;
+pub mod net;
+pub mod os;
+pub mod os_str;
+pub mod path;
+pub mod pipe;
+pub mod process;
+pub mod rwlock;
+pub mod stack_overflow;
+pub mod thread;
+pub mod thread_local;
+pub mod time;
+pub mod stdio;
+
+#[cfg(not(test))]
+pub fn init() {
+}
+
+/// This function is used to implement functionality that simply doesn't exist.
+/// Programs relying on this functionality will need to deal with the error.
+pub fn unsupported<T>() -> io::Result<T> {
+    Err(unsupported_err())
+}
+
+pub fn unsupported_err() -> io::Error {
+    io::Error::new(io::ErrorKind::Other,
+                   "operation not supported on SGX yet")
+}
+
+/// This function is used to implement various functions that doesn't exist,
+/// but the lack of which might not be reason for error. If no error is
+/// returned, the program might very well be able to function normally. This is
+/// what happens when `SGX_INEFFECTIVE_ERROR` is set to `true`. If it is
+/// `false`, the behavior is the same as `unsupported`.
+pub fn sgx_ineffective<T>(v: T) -> io::Result<T> {
+    static SGX_INEFFECTIVE_ERROR: AtomicBool = AtomicBool::new(false);
+    if SGX_INEFFECTIVE_ERROR.load(Ordering::Relaxed) {
+        Err(io::Error::new(io::ErrorKind::Other,
+                       "operation can't be trusted to have any effect on SGX"))
+    } else {
+        Ok(v)
+    }
+}
+
+pub fn decode_error_kind(code: i32) -> io::ErrorKind {
+    use fortanix_sgx_abi::Error;
+
+    // FIXME: not sure how to make sure all variants of Error are covered
+    if code == Error::NotFound as _ {
+        io::ErrorKind::NotFound
+    } else if code == Error::PermissionDenied as _ {
+        io::ErrorKind::PermissionDenied
+    } else if code == Error::ConnectionRefused as _ {
+        io::ErrorKind::ConnectionRefused
+    } else if code == Error::ConnectionReset as _ {
+        io::ErrorKind::ConnectionReset
+    } else if code == Error::ConnectionAborted as _ {
+        io::ErrorKind::ConnectionAborted
+    } else if code == Error::NotConnected as _ {
+        io::ErrorKind::NotConnected
+    } else if code == Error::AddrInUse as _ {
+        io::ErrorKind::AddrInUse
+    } else if code == Error::AddrNotAvailable as _ {
+        io::ErrorKind::AddrNotAvailable
+    } else if code == Error::BrokenPipe as _ {
+        io::ErrorKind::BrokenPipe
+    } else if code == Error::AlreadyExists as _ {
+        io::ErrorKind::AlreadyExists
+    } else if code == Error::WouldBlock as _ {
+        io::ErrorKind::WouldBlock
+    } else if code == Error::InvalidInput as _ {
+        io::ErrorKind::InvalidInput
+    } else if code == Error::InvalidData as _ {
+        io::ErrorKind::InvalidData
+    } else if code == Error::TimedOut as _ {
+        io::ErrorKind::TimedOut
+    } else if code == Error::WriteZero as _ {
+        io::ErrorKind::WriteZero
+    } else if code == Error::Interrupted as _ {
+        io::ErrorKind::Interrupted
+    } else if code == Error::Other as _ {
+        io::ErrorKind::Other
+    } else if code == Error::UnexpectedEof as _ {
+        io::ErrorKind::UnexpectedEof
+    } else {
+        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 unsafe fn abort_internal() -> ! {
+    abi::panic::panic_exit()
+}
+
+pub fn hashmap_random_keys() -> (u64, u64) {
+    fn rdrand64() -> u64 {
+        unsafe {
+            let mut ret: u64 = ::mem::uninitialized();
+            for _ in 0..10 {
+                if ::arch::x86_64::_rdrand64_step(&mut ret) == 1 {
+                    return ret;
+                }
+            }
+            panic!("Failed to obtain random data");
+        }
+    }
+    (rdrand64(), rdrand64())
+}
diff --git a/src/libstd/sys/sgx/mutex.rs b/src/libstd/sys/sgx/mutex.rs
new file mode 100644
index 00000000000..994cf91eea0
--- /dev/null
+++ b/src/libstd/sys/sgx/mutex.rs
@@ -0,0 +1,150 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use fortanix_sgx_abi::Tcs;
+
+use super::abi::thread;
+
+use super::waitqueue::{WaitVariable, WaitQueue, SpinMutex, NotifiedTcs, try_lock_or_false};
+
+pub struct Mutex {
+    inner: SpinMutex<WaitVariable<bool>>,
+}
+
+// Implementation according to “Operating Systems: Three Easy Pieces”, chapter 28
+impl Mutex {
+    pub const fn new() -> Mutex {
+        Mutex { inner: SpinMutex::new(WaitVariable::new(false)) }
+    }
+
+    #[inline]
+    pub unsafe fn init(&mut self) {}
+
+    #[inline]
+    pub unsafe fn lock(&self) {
+        let mut guard = self.inner.lock();
+        if *guard.lock_var() {
+            // Another thread has the lock, wait
+            WaitQueue::wait(guard)
+            // Another thread has passed the lock to us
+        } else {
+            // We are just now obtaining the lock
+            *guard.lock_var_mut() = true;
+        }
+    }
+
+    #[inline]
+    pub unsafe fn unlock(&self) {
+        let guard = self.inner.lock();
+        if let Err(mut guard) = WaitQueue::notify_one(guard) {
+            // No other waiters, unlock
+            *guard.lock_var_mut() = false;
+        } else {
+            // There was a thread waiting, just pass the lock
+        }
+    }
+
+    #[inline]
+    pub unsafe fn try_lock(&self) -> bool {
+        let mut guard = try_lock_or_false!(self.inner);
+        if *guard.lock_var() {
+            // Another thread has the lock
+            false
+        } else {
+            // We are just now obtaining the lock
+            *guard.lock_var_mut() = true;
+            true
+        }
+    }
+
+    #[inline]
+    pub unsafe fn destroy(&self) {}
+}
+
+struct ReentrantLock {
+    owner: Option<Tcs>,
+    count: usize
+}
+
+pub struct ReentrantMutex {
+    inner: SpinMutex<WaitVariable<ReentrantLock>>,
+}
+
+impl ReentrantMutex {
+    pub const fn uninitialized() -> ReentrantMutex {
+        ReentrantMutex {
+            inner: SpinMutex::new(WaitVariable::new(ReentrantLock { owner: None, count: 0 }))
+        }
+    }
+
+    #[inline]
+    pub unsafe fn init(&mut self) {}
+
+    #[inline]
+    pub unsafe fn lock(&self) {
+        let mut guard = self.inner.lock();
+        match guard.lock_var().owner {
+            Some(tcs) if tcs != thread::current() => {
+                // Another thread has the lock, wait
+                WaitQueue::wait(guard);
+                // Another thread has passed the lock to us
+            },
+            _ => {
+                // We are just now obtaining the lock
+                guard.lock_var_mut().owner = Some(thread::current());
+                guard.lock_var_mut().count += 1;
+            },
+        }
+    }
+
+    #[inline]
+    pub unsafe fn unlock(&self) {
+        let mut guard = self.inner.lock();
+        if guard.lock_var().count > 1 {
+            guard.lock_var_mut().count -= 1;
+        } else {
+            match WaitQueue::notify_one(guard) {
+                Err(mut guard) => {
+                    // No other waiters, unlock
+                    guard.lock_var_mut().count = 0;
+                    guard.lock_var_mut().owner = None;
+                },
+                Ok(mut guard) => {
+                    // There was a thread waiting, just pass the lock
+                    if let NotifiedTcs::Single(tcs) = guard.notified_tcs() {
+                        guard.lock_var_mut().owner = Some(tcs)
+                    } else {
+                        unreachable!() // called notify_one
+                    }
+                }
+            }
+        }
+    }
+
+    #[inline]
+    pub unsafe fn try_lock(&self) -> bool {
+        let mut guard = try_lock_or_false!(self.inner);
+        match guard.lock_var().owner {
+            Some(tcs) if tcs != thread::current() => {
+                // Another thread has the lock
+                false
+            },
+            _ => {
+                // We are just now obtaining the lock
+                guard.lock_var_mut().owner = Some(thread::current());
+                guard.lock_var_mut().count += 1;
+                true
+            },
+        }
+    }
+
+    #[inline]
+    pub unsafe fn destroy(&self) {}
+}
diff --git a/src/libstd/sys/sgx/net.rs b/src/libstd/sys/sgx/net.rs
new file mode 100644
index 00000000000..176d230846d
--- /dev/null
+++ b/src/libstd/sys/sgx/net.rs
@@ -0,0 +1,415 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use fmt;
+use io;
+use net::{SocketAddr, Shutdown, Ipv4Addr, Ipv6Addr, ToSocketAddrs};
+use time::Duration;
+use sys::{unsupported, Void, sgx_ineffective};
+use sys::fd::FileDesc;
+use convert::TryFrom;
+use error;
+use sync::Arc;
+
+use super::abi::usercalls;
+
+const DEFAULT_FAKE_TTL: u32 = 64;
+
+#[derive(Debug, Clone)]
+struct Socket {
+    inner: Arc<FileDesc>,
+    local_addr: String,
+}
+
+impl Socket {
+    fn new(fd: usercalls::Fd, local_addr: String) -> Socket {
+        Socket { inner: Arc::new(FileDesc::new(fd)), local_addr }
+    }
+}
+
+#[derive(Debug, Clone)]
+pub struct TcpStream {
+    inner: Socket,
+    peer_addr: String,
+}
+
+fn io_err_to_addr(result: io::Result<&SocketAddr>) -> io::Result<String> {
+    match result {
+        Ok(saddr) => Ok(saddr.to_string()),
+        // need to downcast twice because io::Error::into_inner doesn't return the original
+        // value if the conversion fails
+        Err(e) => if e.get_ref().and_then(|e| e.downcast_ref::<NonIpSockAddr>()).is_some() {
+            Ok(e.into_inner().unwrap().downcast::<NonIpSockAddr>().unwrap().host)
+        } else {
+            Err(e)
+        }
+    }
+}
+
+fn addr_to_sockaddr(addr: &str) -> io::Result<SocketAddr> {
+    // unwrap OK: if an iterator is returned, we're guaranteed to get exactly one entry
+    addr.to_socket_addrs().map(|mut it| it.next().unwrap())
+}
+
+impl TcpStream {
+    pub fn connect(addr: io::Result<&SocketAddr>) -> io::Result<TcpStream> {
+        let addr = io_err_to_addr(addr)?;
+        let (fd, local_addr, peer_addr) = usercalls::connect_stream(&addr)?;
+        Ok(TcpStream { inner: Socket::new(fd, local_addr), peer_addr })
+    }
+
+    pub fn connect_timeout(addr: &SocketAddr, _: Duration) -> io::Result<TcpStream> {
+        Self::connect(Ok(addr)) // FIXME: ignoring timeout
+    }
+
+    pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> {
+        sgx_ineffective(())
+    }
+
+    pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> {
+        sgx_ineffective(())
+    }
+
+    pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
+        sgx_ineffective(None)
+    }
+
+    pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
+        sgx_ineffective(None)
+    }
+
+    pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> {
+        Ok(0)
+    }
+
+    pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
+        self.inner.inner.read(buf)
+    }
+
+    pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
+        self.inner.inner.write(buf)
+    }
+
+    pub fn peer_addr(&self) -> io::Result<SocketAddr> {
+        addr_to_sockaddr(&self.peer_addr)
+    }
+
+    pub fn socket_addr(&self) -> io::Result<SocketAddr> {
+        addr_to_sockaddr(&self.inner.local_addr)
+    }
+
+    pub fn shutdown(&self, _: Shutdown) -> io::Result<()> {
+        sgx_ineffective(())
+    }
+
+    pub fn duplicate(&self) -> io::Result<TcpStream> {
+        Ok(self.clone())
+    }
+
+    pub fn set_nodelay(&self, _: bool) -> io::Result<()> {
+        sgx_ineffective(())
+    }
+
+    pub fn nodelay(&self) -> io::Result<bool> {
+        sgx_ineffective(false)
+    }
+
+    pub fn set_ttl(&self, _: u32) -> io::Result<()> {
+        sgx_ineffective(())
+    }
+
+    pub fn ttl(&self) -> io::Result<u32> {
+        sgx_ineffective(DEFAULT_FAKE_TTL)
+    }
+
+    pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+        Ok(None)
+    }
+
+    pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
+        sgx_ineffective(())
+    }
+}
+
+#[derive(Debug, Clone)]
+pub struct TcpListener {
+    inner: Socket,
+}
+
+impl TcpListener {
+    pub fn bind(addr: io::Result<&SocketAddr>) -> io::Result<TcpListener> {
+        let addr = io_err_to_addr(addr)?;
+        let (fd, local_addr) = usercalls::bind_stream(&addr)?;
+        Ok(TcpListener { inner: Socket::new(fd, local_addr) })
+    }
+
+    pub fn socket_addr(&self) -> io::Result<SocketAddr> {
+        addr_to_sockaddr(&self.inner.local_addr)
+    }
+
+    pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> {
+        let (fd, local_addr, peer_addr) = usercalls::accept_stream(self.inner.inner.raw())?;
+        let ret_peer = addr_to_sockaddr(&peer_addr).unwrap_or_else(|_| ([0; 4], 0).into());
+        Ok((TcpStream { inner: Socket::new(fd, local_addr), peer_addr }, ret_peer))
+    }
+
+    pub fn duplicate(&self) -> io::Result<TcpListener> {
+        Ok(self.clone())
+    }
+
+    pub fn set_ttl(&self, _: u32) -> io::Result<()> {
+        sgx_ineffective(())
+    }
+
+    pub fn ttl(&self) -> io::Result<u32> {
+        sgx_ineffective(DEFAULT_FAKE_TTL)
+    }
+
+    pub fn set_only_v6(&self, _: bool) -> io::Result<()> {
+        sgx_ineffective(())
+    }
+
+    pub fn only_v6(&self) -> io::Result<bool> {
+        sgx_ineffective(false)
+    }
+
+    pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+        Ok(None)
+    }
+
+    pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
+        sgx_ineffective(())
+    }
+}
+
+pub struct UdpSocket(Void);
+
+impl UdpSocket {
+    pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<UdpSocket> {
+        unsupported()
+    }
+
+    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 {}
+    }
+}
+
+#[derive(Debug)]
+pub struct NonIpSockAddr {
+    host: String
+}
+
+impl error::Error for NonIpSockAddr {
+    fn description(&self) -> &str {
+        "Failed to convert address to SocketAddr"
+    }
+}
+
+impl fmt::Display for NonIpSockAddr {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "Failed to convert address to SocketAddr: {}", self.host)
+    }
+}
+
+pub struct LookupHost(Void);
+
+impl LookupHost {
+    fn new(host: String) -> io::Result<LookupHost> {
+        Err(io::Error::new(io::ErrorKind::Other, NonIpSockAddr { host }))
+    }
+
+    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> {
+        LookupHost::new(v.to_owned())
+    }
+}
+
+impl<'a> TryFrom<(&'a str, u16)> for LookupHost {
+    type Error = io::Error;
+
+    fn try_from((host, port): (&'a str, u16)) -> io::Result<LookupHost> {
+        LookupHost::new(format!("{}:{}", host, port))
+    }
+}
+
+#[allow(bad_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/sgx/os.rs b/src/libstd/sys/sgx/os.rs
new file mode 100644
index 00000000000..79ebafe73f9
--- /dev/null
+++ b/src/libstd/sys/sgx/os.rs
@@ -0,0 +1,147 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use fortanix_sgx_abi::{Error, RESULT_SUCCESS};
+
+use error::Error as StdError;
+use ffi::{OsString, OsStr};
+use fmt;
+use io;
+use path::{self, PathBuf};
+use str;
+use sys::{unsupported, Void, sgx_ineffective, decode_error_kind};
+use collections::HashMap;
+use vec;
+use sync::Mutex;
+use sync::atomic::{AtomicUsize, Ordering};
+use sync::Once;
+
+pub fn errno() -> i32 {
+    RESULT_SUCCESS
+}
+
+pub fn error_string(errno: i32) -> String {
+    if errno == RESULT_SUCCESS {
+        "operation succesful".into()
+    } else if ((Error::UserRangeStart as _)..=(Error::UserRangeEnd as _)).contains(&errno) {
+        format!("user-specified error {:08x}", errno)
+    } else {
+        decode_error_kind(errno).as_str().into()
+    }
+}
+
+pub fn getcwd() -> io::Result<PathBuf> {
+    unsupported()
+}
+
+pub fn chdir(_: &path::Path) -> io::Result<()> {
+    sgx_ineffective(())
+}
+
+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 in SGX yet".fmt(f)
+    }
+}
+
+impl StdError for JoinPathsError {
+    fn description(&self) -> &str {
+        "not supported in SGX yet"
+    }
+}
+
+pub fn current_exe() -> io::Result<PathBuf> {
+    unsupported()
+}
+
+static ENV: AtomicUsize = AtomicUsize::new(0);
+static ENV_INIT: Once = Once::new();
+type EnvStore = Mutex<HashMap<OsString, OsString>>;
+
+fn get_env_store() -> Option<&'static EnvStore> {
+    unsafe { (ENV.load(Ordering::Relaxed) as *const EnvStore).as_ref() }
+}
+
+fn create_env_store() -> &'static EnvStore {
+    ENV_INIT.call_once(|| {
+        ENV.store(Box::into_raw(Box::new(EnvStore::default())) as _, Ordering::Relaxed)
+    });
+    unsafe {
+        &*(ENV.load(Ordering::Relaxed) as *const EnvStore)
+    }
+}
+
+pub type Env = vec::IntoIter<(OsString, OsString)>;
+
+pub fn env() -> Env {
+    let clone_to_vec = |map: &HashMap<OsString, OsString>| -> Vec<_> {
+        map.iter().map(|(k, v)| (k.clone(), v.clone()) ).collect()
+    };
+
+    get_env_store()
+        .map(|env| clone_to_vec(&env.lock().unwrap()) )
+        .unwrap_or_default()
+        .into_iter()
+}
+
+pub fn getenv(k: &OsStr) -> io::Result<Option<OsString>> {
+    Ok(get_env_store().and_then(|s| s.lock().unwrap().get(k).cloned() ))
+}
+
+pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
+    let (k, v) = (k.to_owned(), v.to_owned());
+    create_env_store().lock().unwrap().insert(k, v);
+    Ok(())
+}
+
+pub fn unsetenv(k: &OsStr) -> io::Result<()> {
+    if let Some(env) = get_env_store() {
+        env.lock().unwrap().remove(k);
+    }
+    Ok(())
+}
+
+pub fn temp_dir() -> PathBuf {
+    panic!("no filesystem in SGX")
+}
+
+pub fn home_dir() -> Option<PathBuf> {
+    None
+}
+
+pub fn exit(code: i32) -> ! {
+    super::abi::exit_with_code(code as _)
+}
+
+pub fn getpid() -> u32 {
+    panic!("no pids in SGX")
+}
diff --git a/src/libstd/sys/sgx/os_str.rs b/src/libstd/sys/sgx/os_str.rs
new file mode 100644
index 00000000000..9bfb84db209
--- /dev/null
+++ b/src/libstd/sys/sgx/os_str.rs
@@ -0,0 +1,189 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+/// The underlying OsString/OsStr implementation on Unix systems: just
+/// a `Vec<u8>`/`[u8]`.
+
+use borrow::Cow;
+use fmt;
+use str;
+use mem;
+use rc::Rc;
+use sync::Arc;
+use sys_common::{AsInner, IntoInner};
+use sys_common::bytestring::debug_fmt_bytestring;
+use core::str::lossy::Utf8Lossy;
+
+#[derive(Clone, Hash)]
+pub struct Buf {
+    pub inner: Vec<u8>
+}
+
+pub struct Slice {
+    pub inner: [u8]
+}
+
+impl fmt::Debug for Slice {
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+        debug_fmt_bytestring(&self.inner, formatter)
+    }
+}
+
+impl fmt::Display for Slice {
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+        fmt::Display::fmt(&Utf8Lossy::from_bytes(&self.inner), formatter)
+    }
+}
+
+impl fmt::Debug for Buf {
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+        fmt::Debug::fmt(self.as_slice(), formatter)
+    }
+}
+
+impl fmt::Display for Buf {
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+        fmt::Display::fmt(self.as_slice(), formatter)
+    }
+}
+
+impl IntoInner<Vec<u8>> for Buf {
+    fn into_inner(self) -> Vec<u8> {
+        self.inner
+    }
+}
+
+impl AsInner<[u8]> for Buf {
+    fn as_inner(&self) -> &[u8] {
+        &self.inner
+    }
+}
+
+
+impl Buf {
+    pub fn from_string(s: String) -> Buf {
+        Buf { inner: s.into_bytes() }
+    }
+
+    #[inline]
+    pub fn with_capacity(capacity: usize) -> Buf {
+        Buf {
+            inner: Vec::with_capacity(capacity)
+        }
+    }
+
+    #[inline]
+    pub fn clear(&mut self) {
+        self.inner.clear()
+    }
+
+    #[inline]
+    pub fn capacity(&self) -> usize {
+        self.inner.capacity()
+    }
+
+    #[inline]
+    pub fn reserve(&mut self, additional: usize) {
+        self.inner.reserve(additional)
+    }
+
+    #[inline]
+    pub fn reserve_exact(&mut self, additional: usize) {
+        self.inner.reserve_exact(additional)
+    }
+
+    #[inline]
+    pub fn shrink_to_fit(&mut self) {
+        self.inner.shrink_to_fit()
+    }
+
+    #[inline]
+    pub fn shrink_to(&mut self, min_capacity: usize) {
+        self.inner.shrink_to(min_capacity)
+    }
+
+    pub fn as_slice(&self) -> &Slice {
+        unsafe { mem::transmute(&*self.inner) }
+    }
+
+    pub fn into_string(self) -> Result<String, Buf> {
+        String::from_utf8(self.inner).map_err(|p| Buf { inner: p.into_bytes() } )
+    }
+
+    pub fn push_slice(&mut self, s: &Slice) {
+        self.inner.extend_from_slice(&s.inner)
+    }
+
+    #[inline]
+    pub fn into_box(self) -> Box<Slice> {
+        unsafe { mem::transmute(self.inner.into_boxed_slice()) }
+    }
+
+    #[inline]
+    pub fn from_box(boxed: Box<Slice>) -> Buf {
+        let inner: Box<[u8]> = unsafe { mem::transmute(boxed) };
+        Buf { inner: inner.into_vec() }
+    }
+
+    #[inline]
+    pub fn into_arc(&self) -> Arc<Slice> {
+        self.as_slice().into_arc()
+    }
+
+    #[inline]
+    pub fn into_rc(&self) -> Rc<Slice> {
+        self.as_slice().into_rc()
+    }
+}
+
+impl Slice {
+    fn from_u8_slice(s: &[u8]) -> &Slice {
+        unsafe { mem::transmute(s) }
+    }
+
+    pub fn from_str(s: &str) -> &Slice {
+        Slice::from_u8_slice(s.as_bytes())
+    }
+
+    pub fn to_str(&self) -> Option<&str> {
+        str::from_utf8(&self.inner).ok()
+    }
+
+    pub fn to_string_lossy(&self) -> Cow<str> {
+        String::from_utf8_lossy(&self.inner)
+    }
+
+    pub fn to_owned(&self) -> Buf {
+        Buf { inner: self.inner.to_vec() }
+    }
+
+    #[inline]
+    pub fn into_box(&self) -> Box<Slice> {
+        let boxed: Box<[u8]> = self.inner.into();
+        unsafe { mem::transmute(boxed) }
+    }
+
+    pub fn empty_box() -> Box<Slice> {
+        let boxed: Box<[u8]> = Default::default();
+        unsafe { mem::transmute(boxed) }
+    }
+
+    #[inline]
+    pub fn into_arc(&self) -> Arc<Slice> {
+        let arc: Arc<[u8]> = Arc::from(&self.inner);
+        unsafe { Arc::from_raw(Arc::into_raw(arc) as *const Slice) }
+    }
+
+    #[inline]
+    pub fn into_rc(&self) -> Rc<Slice> {
+        let rc: Rc<[u8]> = Rc::from(&self.inner);
+        unsafe { Rc::from_raw(Rc::into_raw(rc) as *const Slice) }
+    }
+}
diff --git a/src/libstd/sys/sgx/path.rs b/src/libstd/sys/sgx/path.rs
new file mode 100644
index 00000000000..afe0c490426
--- /dev/null
+++ b/src/libstd/sys/sgx/path.rs
@@ -0,0 +1,29 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use path::Prefix;
+use 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: &'static str = "/";
+pub const MAIN_SEP: char = '/';
diff --git a/src/libstd/sys/sgx/pipe.rs b/src/libstd/sys/sgx/pipe.rs
new file mode 100644
index 00000000000..6c6cbc14a8a
--- /dev/null
+++ b/src/libstd/sys/sgx/pipe.rs
@@ -0,0 +1,35 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use io;
+use 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/sgx/process.rs b/src/libstd/sys/sgx/process.rs
new file mode 100644
index 00000000000..01a12fba043
--- /dev/null
+++ b/src/libstd/sys/sgx/process.rs
@@ -0,0 +1,162 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use ffi::OsStr;
+use fmt;
+use io;
+use sys::fs::File;
+use sys::pipe::AnonPipe;
+use sys::{unsupported, Void};
+use 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/sgx/rwlock.rs b/src/libstd/sys/sgx/rwlock.rs
new file mode 100644
index 00000000000..d1af98bd4f5
--- /dev/null
+++ b/src/libstd/sys/sgx/rwlock.rs
@@ -0,0 +1,258 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use num::NonZeroUsize;
+use slice;
+use str;
+
+use super::waitqueue::{
+    try_lock_or_false, NotifiedTcs, SpinMutex, SpinMutexGuard, WaitQueue, WaitVariable,
+};
+use mem;
+
+pub struct RWLock {
+    readers: SpinMutex<WaitVariable<Option<NonZeroUsize>>>,
+    writer: SpinMutex<WaitVariable<bool>>,
+}
+
+// Below is to check at compile time, that RWLock has size of 128 bytes.
+#[allow(dead_code)]
+unsafe fn rw_lock_size_assert(r: RWLock) {
+    mem::transmute::<RWLock, [u8; 128]>(r);
+}
+
+//unsafe impl Send for RWLock {}
+//unsafe impl Sync for RWLock {} // FIXME
+
+impl RWLock {
+    pub const fn new() -> RWLock {
+        RWLock {
+            readers: SpinMutex::new(WaitVariable::new(None)),
+            writer: SpinMutex::new(WaitVariable::new(false)),
+        }
+    }
+
+    #[inline]
+    pub unsafe fn read(&self) {
+        let mut rguard = self.readers.lock();
+        let wguard = self.writer.lock();
+        if *wguard.lock_var() || !wguard.queue_empty() {
+            // Another thread has or is waiting for the write lock, wait
+            drop(wguard);
+            WaitQueue::wait(rguard);
+            // Another thread has passed the lock to us
+        } else {
+            // No waiting writers, acquire the read lock
+            *rguard.lock_var_mut() =
+                NonZeroUsize::new(rguard.lock_var().map_or(0, |n| n.get()) + 1);
+        }
+    }
+
+    #[inline]
+    pub unsafe fn try_read(&self) -> bool {
+        let mut rguard = try_lock_or_false!(self.readers);
+        let wguard = try_lock_or_false!(self.writer);
+        if *wguard.lock_var() || !wguard.queue_empty() {
+            // Another thread has or is waiting for the write lock
+            false
+        } else {
+            // No waiting writers, acquire the read lock
+            *rguard.lock_var_mut() =
+                NonZeroUsize::new(rguard.lock_var().map_or(0, |n| n.get()) + 1);
+            true
+        }
+    }
+
+    #[inline]
+    pub unsafe fn write(&self) {
+        let rguard = self.readers.lock();
+        let mut wguard = self.writer.lock();
+        if *wguard.lock_var() || rguard.lock_var().is_some() {
+            // Another thread has the lock, wait
+            drop(rguard);
+            WaitQueue::wait(wguard);
+            // Another thread has passed the lock to us
+        } else {
+            // We are just now obtaining the lock
+            *wguard.lock_var_mut() = true;
+        }
+    }
+
+    #[inline]
+    pub unsafe fn try_write(&self) -> bool {
+        let rguard = try_lock_or_false!(self.readers);
+        let mut wguard = try_lock_or_false!(self.writer);
+        if *wguard.lock_var() || rguard.lock_var().is_some() {
+            // Another thread has the lock
+            false
+        } else {
+            // We are just now obtaining the lock
+            *wguard.lock_var_mut() = true;
+            true
+        }
+    }
+
+    #[inline]
+    unsafe fn __read_unlock(
+        &self,
+        mut rguard: SpinMutexGuard<WaitVariable<Option<NonZeroUsize>>>,
+        wguard: SpinMutexGuard<WaitVariable<bool>>,
+    ) {
+        *rguard.lock_var_mut() = NonZeroUsize::new(rguard.lock_var().unwrap().get() - 1);
+        if rguard.lock_var().is_some() {
+            // There are other active readers
+        } else {
+            if let Ok(mut wguard) = WaitQueue::notify_one(wguard) {
+                // A writer was waiting, pass the lock
+                *wguard.lock_var_mut() = true;
+            } else {
+                // No writers were waiting, the lock is released
+                assert!(rguard.queue_empty());
+            }
+        }
+    }
+
+    #[inline]
+    pub unsafe fn read_unlock(&self) {
+        let rguard = self.readers.lock();
+        let wguard = self.writer.lock();
+        self.__read_unlock(rguard, wguard);
+    }
+
+    #[inline]
+    unsafe fn __write_unlock(
+        &self,
+        rguard: SpinMutexGuard<WaitVariable<Option<NonZeroUsize>>>,
+        wguard: SpinMutexGuard<WaitVariable<bool>>,
+    ) {
+        if let Err(mut wguard) = WaitQueue::notify_one(wguard) {
+            // No writers waiting, release the write lock
+            *wguard.lock_var_mut() = false;
+            if let Ok(mut rguard) = WaitQueue::notify_all(rguard) {
+                // One or more readers were waiting, pass the lock to them
+                if let NotifiedTcs::All { count } = rguard.notified_tcs() {
+                    *rguard.lock_var_mut() = Some(count)
+                } else {
+                    unreachable!() // called notify_all
+                }
+            } else {
+                // No readers waiting, the lock is released
+            }
+        } else {
+            // There was a thread waiting for write, just pass the lock
+        }
+    }
+
+    #[inline]
+    pub unsafe fn write_unlock(&self) {
+        let rguard = self.readers.lock();
+        let wguard = self.writer.lock();
+        self.__write_unlock(rguard, wguard);
+    }
+
+    #[inline]
+    unsafe fn unlock(&self) {
+        let rguard = self.readers.lock();
+        let wguard = self.writer.lock();
+        if *wguard.lock_var() == true {
+            self.__write_unlock(rguard, wguard);
+        } else {
+            self.__read_unlock(rguard, wguard);
+        }
+    }
+
+    #[inline]
+    pub unsafe fn destroy(&self) {}
+}
+
+const EINVAL: i32 = 22;
+
+#[no_mangle]
+pub unsafe extern "C" fn __rust_rwlock_rdlock(p: *mut RWLock) -> i32 {
+    if p.is_null() {
+        return EINVAL;
+    }
+    (*p).read();
+    return 0;
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn __rust_rwlock_wrlock(p: *mut RWLock) -> i32 {
+    if p.is_null() {
+        return EINVAL;
+    }
+    (*p).write();
+    return 0;
+}
+#[no_mangle]
+pub unsafe extern "C" fn __rust_rwlock_unlock(p: *mut RWLock) -> i32 {
+    if p.is_null() {
+        return EINVAL;
+    }
+    (*p).unlock();
+    return 0;
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn __rust_print_err(m: *mut u8, s: i32) {
+    if s < 0 {
+        return;
+    }
+    let buf = slice::from_raw_parts(m as *const u8, s as _);
+    if let Ok(s) = str::from_utf8(&buf[..buf.iter().position(|&b| b == 0).unwrap_or(buf.len())]) {
+        eprint!("{}", s);
+    }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn __rust_abort() {
+    ::sys::abort_internal();
+}
+
+#[cfg(test)]
+mod tests {
+
+    use super::*;
+    use core::array::FixedSizeArray;
+    use mem::MaybeUninit;
+    use {mem, ptr};
+
+    // The below test verifies that the bytes of initialized RWLock are the ones
+    // we use in libunwind.
+    // If they change we need to update src/UnwindRustSgx.h in libunwind.
+    #[test]
+    fn test_c_rwlock_initializer() {
+        const RWLOCK_INIT: &[u8] = &[
+            0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ];
+
+        let mut init = MaybeUninit::<RWLock>::zeroed();
+        init.set(RWLock::new());
+        assert_eq!(
+            mem::transmute::<_, [u8; 128]>(init.into_inner()).as_slice(),
+            RWLOCK_INIT
+        );
+    }
+}
diff --git a/src/libstd/sys/sgx/stack_overflow.rs b/src/libstd/sys/sgx/stack_overflow.rs
new file mode 100644
index 00000000000..0176b748a87
--- /dev/null
+++ b/src/libstd/sys/sgx/stack_overflow.rs
@@ -0,0 +1,23 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+pub struct Handler;
+
+impl Handler {
+    pub unsafe fn new() -> Handler {
+        Handler
+    }
+}
+
+pub unsafe fn init() {
+}
+
+pub unsafe fn cleanup() {
+}
diff --git a/src/libstd/sys/sgx/stdio.rs b/src/libstd/sys/sgx/stdio.rs
new file mode 100644
index 00000000000..13c91195569
--- /dev/null
+++ b/src/libstd/sys/sgx/stdio.rs
@@ -0,0 +1,81 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use fortanix_sgx_abi as abi;
+
+use io;
+use sys::fd::FileDesc;
+
+pub struct Stdin(());
+pub struct Stdout(());
+pub struct Stderr(());
+
+fn with_std_fd<F: FnOnce(&FileDesc) -> R, R>(fd: abi::Fd, f: F) -> R {
+    let fd = FileDesc::new(fd);
+    let ret = f(&fd);
+    fd.into_raw();
+    ret
+}
+
+impl Stdin {
+    pub fn new() -> io::Result<Stdin> { Ok(Stdin(())) }
+
+    pub fn read(&self, data: &mut [u8]) -> io::Result<usize> {
+        with_std_fd(abi::FD_STDIN, |fd| fd.read(data))
+    }
+}
+
+impl Stdout {
+    pub fn new() -> io::Result<Stdout> { Ok(Stdout(())) }
+
+    pub fn write(&self, data: &[u8]) -> io::Result<usize> {
+        with_std_fd(abi::FD_STDOUT, |fd| fd.write(data))
+    }
+
+    pub fn flush(&self) -> io::Result<()> {
+        with_std_fd(abi::FD_STDOUT, |fd| fd.flush())
+    }
+}
+
+impl Stderr {
+    pub fn new() -> io::Result<Stderr> { Ok(Stderr(())) }
+
+    pub fn write(&self, data: &[u8]) -> io::Result<usize> {
+        with_std_fd(abi::FD_STDERR, |fd| fd.write(data))
+    }
+
+    pub fn flush(&self) -> io::Result<()> {
+        with_std_fd(abi::FD_STDERR, |fd| fd.flush())
+    }
+}
+
+// FIXME: right now this raw stderr handle is used in a few places because
+//        std::io::stderr_raw isn't exposed, but once that's exposed this impl
+//        should go away
+impl io::Write for Stderr {
+    fn write(&mut self, data: &[u8]) -> io::Result<usize> {
+        Stderr::write(self, data)
+    }
+
+    fn flush(&mut self) -> io::Result<()> {
+        Stderr::flush(self)
+    }
+}
+
+pub const STDIN_BUF_SIZE: usize = ::sys_common::io::DEFAULT_BUF_SIZE;
+
+pub fn is_ebadf(err: &io::Error) -> bool {
+    // FIXME: Rust normally maps Unix EBADF to `Other`
+    err.raw_os_error() == Some(abi::Error::BrokenPipe as _)
+}
+
+pub fn panic_output() -> Option<impl io::Write> {
+    super::abi::panic::SgxPanicOutput::new()
+}
diff --git a/src/libstd/sys/sgx/thread.rs b/src/libstd/sys/sgx/thread.rs
new file mode 100644
index 00000000000..9f3c4536cb5
--- /dev/null
+++ b/src/libstd/sys/sgx/thread.rs
@@ -0,0 +1,100 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use boxed::FnBox;
+use ffi::CStr;
+use io;
+use time::Duration;
+
+use super::abi::usercalls;
+
+pub struct Thread(task_queue::JoinHandle);
+
+pub const DEFAULT_MIN_STACK_SIZE: usize = 4096;
+
+mod task_queue {
+    use sync::{Mutex, MutexGuard, Once};
+    use sync::mpsc;
+    use boxed::FnBox;
+
+    pub type JoinHandle = mpsc::Receiver<()>;
+
+    pub(super) struct Task {
+        p: Box<dyn FnBox()>,
+        done: mpsc::Sender<()>,
+    }
+
+    impl Task {
+        pub(super) fn new(p: Box<dyn FnBox()>) -> (Task, JoinHandle) {
+            let (done, recv) = mpsc::channel();
+            (Task { p, done }, recv)
+        }
+
+        pub(super) fn run(self) {
+            (self.p)();
+            let _ = self.done.send(());
+        }
+    }
+
+    static TASK_QUEUE_INIT: Once = Once::new();
+    static mut TASK_QUEUE: Option<Mutex<Vec<Task>>> = None;
+
+    pub(super) fn lock() -> MutexGuard<'static, Vec<Task>> {
+        unsafe {
+            TASK_QUEUE_INIT.call_once(|| TASK_QUEUE = Some(Default::default()) );
+            TASK_QUEUE.as_ref().unwrap().lock().unwrap()
+        }
+    }
+}
+
+impl Thread {
+    // unsafe: see thread::Builder::spawn_unchecked for safety requirements
+    pub unsafe fn new(_stack: usize, p: Box<dyn FnBox()>)
+        -> io::Result<Thread>
+    {
+        let mut queue_lock = task_queue::lock();
+        usercalls::launch_thread()?;
+        let (task, handle) = task_queue::Task::new(p);
+        queue_lock.push(task);
+        Ok(Thread(handle))
+    }
+
+    pub(super) fn entry() {
+        let mut guard = task_queue::lock();
+        let task = guard.pop().expect("Thread started but no tasks pending");
+        drop(guard); // make sure to not hold the task queue lock longer than necessary
+        task.run()
+    }
+
+    pub fn yield_now() {
+        assert_eq!(
+            usercalls::wait(0, usercalls::WAIT_NO).unwrap_err().kind(),
+            io::ErrorKind::WouldBlock
+        );
+    }
+
+    pub fn set_name(_name: &CStr) {
+        // FIXME: could store this pointer in TLS somewhere
+    }
+
+    pub fn sleep(_dur: Duration) {
+        panic!("can't sleep"); // FIXME
+    }
+
+    pub fn join(self) {
+        let _ = self.0.recv();
+    }
+}
+
+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/sgx/thread_local.rs b/src/libstd/sys/sgx/thread_local.rs
new file mode 100644
index 00000000000..3b628bae4fb
--- /dev/null
+++ b/src/libstd/sys/sgx/thread_local.rs
@@ -0,0 +1,38 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use super::abi::tls::{Tls, Key as AbiKey};
+
+pub type Key = usize;
+
+#[inline]
+pub unsafe fn create(dtor: Option<unsafe extern fn(*mut u8)>) -> Key {
+    Tls::create(dtor).as_usize()
+}
+
+#[inline]
+pub unsafe fn set(key: Key, value: *mut u8) {
+    Tls::set(AbiKey::from_usize(key), value)
+}
+
+#[inline]
+pub unsafe fn get(key: Key) -> *mut u8 {
+    Tls::get(AbiKey::from_usize(key))
+}
+
+#[inline]
+pub unsafe fn destroy(key: Key) {
+    Tls::destroy(AbiKey::from_usize(key))
+}
+
+#[inline]
+pub fn requires_synchronized_create() -> bool {
+    false
+}
diff --git a/src/libstd/sys/sgx/time.rs b/src/libstd/sys/sgx/time.rs
new file mode 100644
index 00000000000..196e1a97fc4
--- /dev/null
+++ b/src/libstd/sys/sgx/time.rs
@@ -0,0 +1,57 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use time::Duration;
+use super::abi::usercalls;
+
+#[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));
+
+impl Instant {
+    pub fn now() -> Instant {
+        Instant(usercalls::insecure_time())
+    }
+
+    pub fn sub_instant(&self, other: &Instant) -> Duration {
+        self.0 - 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(usercalls::insecure_time())
+    }
+
+    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)?))
+    }
+}
diff --git a/src/libstd/sys/sgx/waitqueue.rs b/src/libstd/sys/sgx/waitqueue.rs
new file mode 100644
index 00000000000..ef0def13eee
--- /dev/null
+++ b/src/libstd/sys/sgx/waitqueue.rs
@@ -0,0 +1,549 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+/// 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 ops::{Deref, DerefMut};
+use num::NonZeroUsize;
+
+use fortanix_sgx_abi::{Tcs, EV_UNPARK, WAIT_INDEFINITE};
+use super::abi::usercalls;
+use super::abi::thread;
+
+use self::unsafe_list::{UnsafeList, UnsafeListEntry};
+pub use self::spin_mutex::{SpinMutex, SpinMutexGuard, try_lock_or_false};
+
+/// An queue entry in a `WaitQueue`.
+struct WaitEntry {
+    /// TCS address of the thread that is waiting
+    tcs: Tcs,
+    /// Whether this thread has been notified to be awoken
+    wake: bool
+}
+
+/// Data stored with a `WaitQueue` alongside it. This ensures accesses to the
+/// queue and the data are synchronized, since the type itself is not `Sync`.
+///
+/// Consumers of this API should use a synchronization primitive for shared
+/// access, such as `SpinMutex`.
+#[derive(Default)]
+pub struct WaitVariable<T> {
+    queue: WaitQueue,
+    lock: T
+}
+
+impl<T> WaitVariable<T> {
+    pub const fn new(var: T) -> Self {
+        WaitVariable {
+            queue: WaitQueue::new(),
+            lock: var
+        }
+    }
+
+    pub fn queue_empty(&self) -> bool {
+        self.queue.is_empty()
+    }
+
+    pub fn lock_var(&self) -> &T {
+        &self.lock
+    }
+
+    pub fn lock_var_mut(&mut self) -> &mut T {
+        &mut self.lock
+    }
+}
+
+#[derive(Copy, Clone)]
+pub enum NotifiedTcs {
+    Single(Tcs),
+    All { count: NonZeroUsize }
+}
+
+/// An RAII guard that will notify a set of target threads as well as unlock
+/// a mutex on drop.
+pub struct WaitGuard<'a, T: 'a> {
+    mutex_guard: Option<SpinMutexGuard<'a, WaitVariable<T>>>,
+    notified_tcs: NotifiedTcs
+}
+
+/// A queue of threads that are waiting on some synchronization primitive.
+///
+/// `UnsafeList` entries are allocated on the waiting thread's stack. This
+/// avoids any global locking that might happen in the heap allocator. This is
+/// safe because the waiting thread will not return from that stack frame until
+/// after it is notified. The notifying thread ensures to clean up any
+/// references to the list entries before sending the wakeup event.
+pub struct WaitQueue {
+    // We use an inner Mutex here to protect the data in the face of spurious
+    // wakeups.
+    inner: UnsafeList<SpinMutex<WaitEntry>>,
+}
+unsafe impl Send for WaitQueue {}
+
+impl Default for WaitQueue {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
+impl<'a, T> WaitGuard<'a, T> {
+    /// Returns which TCSes will be notified when this guard drops.
+    pub fn notified_tcs(&self) -> NotifiedTcs {
+        self.notified_tcs
+    }
+}
+
+impl<'a, T> Deref for WaitGuard<'a, T> {
+    type Target = SpinMutexGuard<'a, WaitVariable<T>>;
+
+    fn deref(&self) -> &Self::Target {
+        self.mutex_guard.as_ref().unwrap()
+    }
+}
+
+impl<'a, T> DerefMut for WaitGuard<'a, T> {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        self.mutex_guard.as_mut().unwrap()
+    }
+}
+
+impl<'a, T> Drop for WaitGuard<'a, T> {
+    fn drop(&mut self) {
+        drop(self.mutex_guard.take());
+        let target_tcs = match self.notified_tcs {
+            NotifiedTcs::Single(tcs) => Some(tcs),
+            NotifiedTcs::All { .. } => None
+        };
+        usercalls::send(EV_UNPARK, target_tcs).unwrap();
+    }
+}
+
+impl WaitQueue {
+    pub const fn new() -> Self {
+        WaitQueue {
+            inner: UnsafeList::new()
+        }
+    }
+
+    pub fn is_empty(&self) -> bool {
+        self.inner.is_empty()
+    }
+
+    /// Add the calling thread to the WaitVariable's wait queue, then wait
+    /// until a wakeup event.
+    ///
+    /// This function does not return until this thread has been awoken.
+    pub fn wait<T>(mut guard: SpinMutexGuard<WaitVariable<T>>) {
+        unsafe {
+            let mut entry = UnsafeListEntry::new(SpinMutex::new(WaitEntry {
+                tcs: thread::current(),
+                wake: false
+            }));
+            let entry = guard.queue.inner.push(&mut entry);
+            drop(guard);
+            while !entry.lock().wake {
+                assert_eq!(
+                    usercalls::wait(EV_UNPARK, WAIT_INDEFINITE).unwrap() & EV_UNPARK,
+                    EV_UNPARK
+                );
+            }
+        }
+    }
+
+    /// Either find the next waiter on the wait queue, or return the mutex
+    /// guard unchanged.
+    ///
+    /// If a waiter is found, a `WaitGuard` is returned which will notify the
+    /// waiter when it is dropped.
+    pub fn notify_one<T>(mut guard: SpinMutexGuard<WaitVariable<T>>)
+        -> Result<WaitGuard<T>, SpinMutexGuard<WaitVariable<T>>>
+    {
+        unsafe {
+            if let Some(entry) = guard.queue.inner.pop() {
+                let mut entry_guard = entry.lock();
+                let tcs = entry_guard.tcs;
+                entry_guard.wake = true;
+                drop(entry);
+                Ok(WaitGuard {
+                    mutex_guard: Some(guard),
+                    notified_tcs: NotifiedTcs::Single(tcs)
+                })
+            } else {
+                Err(guard)
+            }
+        }
+    }
+
+    /// Either find any and all waiters on the wait queue, or return the mutex
+    /// guard unchanged.
+    ///
+    /// If at least one waiter is found, a `WaitGuard` is returned which will
+    /// notify all waiters when it is dropped.
+    pub fn notify_all<T>(mut guard: SpinMutexGuard<WaitVariable<T>>)
+        -> Result<WaitGuard<T>, SpinMutexGuard<WaitVariable<T>>>
+    {
+        unsafe {
+            let mut count = 0;
+            while let Some(entry) = guard.queue.inner.pop() {
+                count += 1;
+                let mut entry_guard = entry.lock();
+                entry_guard.wake = true;
+            }
+            if let Some(count) = NonZeroUsize::new(count) {
+                Ok(WaitGuard {
+                    mutex_guard: Some(guard),
+                    notified_tcs: NotifiedTcs::All { count }
+                })
+            } else {
+                Err(guard)
+            }
+        }
+    }
+}
+
+/// A doubly-linked list where callers are in charge of memory allocation
+/// of the nodes in the list.
+mod unsafe_list {
+    use ptr::NonNull;
+    use mem;
+
+    pub struct UnsafeListEntry<T> {
+        next: NonNull<UnsafeListEntry<T>>,
+        prev: NonNull<UnsafeListEntry<T>>,
+        value: Option<T>
+    }
+
+    impl<T> UnsafeListEntry<T> {
+        fn dummy() -> Self {
+            UnsafeListEntry {
+                next: NonNull::dangling(),
+                prev: NonNull::dangling(),
+                value: None
+            }
+        }
+
+        pub fn new(value: T) -> Self {
+            UnsafeListEntry {
+                value: Some(value),
+                ..Self::dummy()
+            }
+        }
+    }
+
+    pub struct UnsafeList<T> {
+        head_tail: NonNull<UnsafeListEntry<T>>,
+        head_tail_entry: Option<UnsafeListEntry<T>>,
+    }
+
+    impl<T> UnsafeList<T> {
+        pub const fn new() -> Self {
+            unsafe {
+                UnsafeList {
+                    head_tail: NonNull::new_unchecked(1 as _),
+                    head_tail_entry: None
+                }
+            }
+        }
+
+        unsafe fn init(&mut self) {
+            if self.head_tail_entry.is_none() {
+                self.head_tail_entry = Some(UnsafeListEntry::dummy());
+                self.head_tail = NonNull::new_unchecked(self.head_tail_entry.as_mut().unwrap());
+                self.head_tail.as_mut().next = self.head_tail;
+                self.head_tail.as_mut().prev = self.head_tail;
+            }
+        }
+
+        pub fn is_empty(&self) -> bool {
+            unsafe {
+                if self.head_tail_entry.is_some() {
+                    let first = self.head_tail.as_ref().next;
+                    if first == self.head_tail {
+                        // ,-------> /---------\ next ---,
+                        // |         |head_tail|         |
+                        // `--- prev \---------/ <-------`
+                        assert_eq!(self.head_tail.as_ref().prev, first);
+                        true
+                    } else {
+                        false
+                    }
+                } else {
+                    true
+                }
+            }
+        }
+
+        /// Pushes an entry onto the back of the list.
+        ///
+        /// # Safety
+        ///
+        /// The entry must remain allocated until the entry is removed from the
+        /// list AND the caller who popped is done using the entry.
+        pub unsafe fn push<'a>(&mut self, entry: &'a mut UnsafeListEntry<T>) -> &'a T {
+            self.init();
+
+            // BEFORE:
+            //     /---------\ next ---> /---------\
+            // ... |prev_tail|           |head_tail| ...
+            //     \---------/ <--- prev \---------/
+            //
+            // AFTER:
+            //     /---------\ next ---> /-----\ next ---> /---------\
+            // ... |prev_tail|           |entry|           |head_tail| ...
+            //     \---------/ <--- prev \-----/ <--- prev \---------/
+            let mut entry = NonNull::new_unchecked(entry);
+            let mut prev_tail = mem::replace(&mut self.head_tail.as_mut().prev, entry);
+            entry.as_mut().prev = prev_tail;
+            entry.as_mut().next = self.head_tail;
+            prev_tail.as_mut().next = entry;
+            (*entry.as_ptr()).value.as_ref().unwrap()
+        }
+
+        /// Pops an entry from the front of the list.
+        ///
+        /// # Safety
+        ///
+        /// The caller must make sure to synchronize ending the borrow of the
+        /// return value and deallocation of the containing entry.
+        pub unsafe fn pop<'a>(&mut self) -> Option<&'a T> {
+            self.init();
+
+            if self.is_empty() {
+                None
+            } else {
+                // BEFORE:
+                //     /---------\ next ---> /-----\ next ---> /------\
+                // ... |head_tail|           |first|           |second| ...
+                //     \---------/ <--- prev \-----/ <--- prev \------/
+                //
+                // AFTER:
+                //     /---------\ next ---> /------\
+                // ... |head_tail|           |second| ...
+                //     \---------/ <--- prev \------/
+                let mut first = self.head_tail.as_mut().next;
+                let mut second = first.as_mut().next;
+                self.head_tail.as_mut().next = second;
+                second.as_mut().prev = self.head_tail;
+                first.as_mut().next = NonNull::dangling();
+                first.as_mut().prev = NonNull::dangling();
+                Some((*first.as_ptr()).value.as_ref().unwrap())
+            }
+        }
+    }
+
+    #[cfg(test)]
+    mod tests {
+        use super::*;
+        use cell::Cell;
+
+        unsafe fn assert_empty<T>(list: &mut UnsafeList<T>) {
+            assert!(list.pop().is_none(), "assertion failed: list is not empty");
+        }
+
+        #[test]
+        fn init_empty() {
+            unsafe {
+                assert_empty(&mut UnsafeList::<i32>::new());
+            }
+        }
+
+        #[test]
+        fn push_pop() {
+            unsafe {
+                let mut node = UnsafeListEntry::new(1234);
+                let mut list = UnsafeList::new();
+                assert_eq!(list.push(&mut node), &1234);
+                assert_eq!(list.pop().unwrap(), &1234);
+                assert_empty(&mut list);
+            }
+        }
+
+        #[test]
+        fn complex_pushes_pops() {
+            unsafe {
+                let mut node1 = UnsafeListEntry::new(1234);
+                let mut node2 = UnsafeListEntry::new(4567);
+                let mut node3 = UnsafeListEntry::new(9999);
+                let mut node4 = UnsafeListEntry::new(8642);
+                let mut list = UnsafeList::new();
+                list.push(&mut node1);
+                list.push(&mut node2);
+                assert_eq!(list.pop().unwrap(), &1234);
+                list.push(&mut node3);
+                assert_eq!(list.pop().unwrap(), &4567);
+                assert_eq!(list.pop().unwrap(), &9999);
+                assert_empty(&mut list);
+                list.push(&mut node4);
+                assert_eq!(list.pop().unwrap(), &8642);
+                assert_empty(&mut list);
+            }
+        }
+
+        #[test]
+        fn cell() {
+            unsafe {
+                let mut node = UnsafeListEntry::new(Cell::new(0));
+                let mut list = UnsafeList::new();
+                let noderef = list.push(&mut node);
+                assert_eq!(noderef.get(), 0);
+                list.pop().unwrap().set(1);
+                assert_empty(&mut list);
+                assert_eq!(noderef.get(), 1);
+            }
+        }
+    }
+}
+
+/// Trivial spinlock-based implementation of `sync::Mutex`.
+// FIXME: Perhaps use Intel TSX to avoid locking?
+mod spin_mutex {
+    use cell::UnsafeCell;
+    use sync::atomic::{AtomicBool, Ordering, spin_loop_hint};
+    use ops::{Deref, DerefMut};
+
+    #[derive(Default)]
+    pub struct SpinMutex<T> {
+        value: UnsafeCell<T>,
+        lock: AtomicBool,
+    }
+
+    unsafe impl<T: Send> Send for SpinMutex<T> {}
+    unsafe impl<T: Send> Sync for SpinMutex<T> {}
+
+    pub struct SpinMutexGuard<'a, T: 'a> {
+        mutex: &'a SpinMutex<T>,
+    }
+
+    impl<'a, T> !Send for SpinMutexGuard<'a, T> {}
+    unsafe impl<'a, T: Sync> Sync for SpinMutexGuard<'a, T> {}
+
+    impl<T> SpinMutex<T> {
+        pub const fn new(value: T) -> Self {
+            SpinMutex {
+                value: UnsafeCell::new(value),
+                lock: AtomicBool::new(false)
+            }
+        }
+
+        #[inline(always)]
+        pub fn lock(&self) -> SpinMutexGuard<T> {
+            loop {
+                match self.try_lock() {
+                    None => while self.lock.load(Ordering::Relaxed) {
+                        spin_loop_hint()
+                    },
+                    Some(guard) => return guard
+                }
+            }
+        }
+
+        #[inline(always)]
+        pub fn try_lock(&self) -> Option<SpinMutexGuard<T>> {
+            if !self.lock.compare_and_swap(false, true, Ordering::Acquire) {
+                Some(SpinMutexGuard {
+                    mutex: self,
+                })
+            } else {
+                None
+            }
+        }
+    }
+
+    pub macro try_lock_or_false {
+        ($e:expr) => {
+            if let Some(v) = $e.try_lock() {
+                v
+            } else {
+                return false
+            }
+        }
+    }
+
+    impl<'a, T> Deref for SpinMutexGuard<'a, T> {
+        type Target = T;
+
+        fn deref(&self) -> &T {
+            unsafe {
+                &*self.mutex.value.get()
+            }
+        }
+    }
+
+    impl<'a, T> DerefMut for SpinMutexGuard<'a, T> {
+        fn deref_mut(&mut self) -> &mut T {
+            unsafe {
+                &mut*self.mutex.value.get()
+            }
+        }
+    }
+
+    impl<'a, T> Drop for SpinMutexGuard<'a, T> {
+        fn drop(&mut self) {
+            self.mutex.lock.store(false, Ordering::Release)
+        }
+    }
+
+    #[cfg(test)]
+    mod tests {
+        #![allow(deprecated)]
+
+        use super::*;
+        use sync::Arc;
+        use thread;
+
+        #[test]
+        fn sleep() {
+            let mutex = Arc::new(SpinMutex::<i32>::default());
+            let mutex2 = mutex.clone();
+            let guard = mutex.lock();
+            let t1 = thread::spawn(move || {
+                *mutex2.lock() = 1;
+            });
+            thread::sleep_ms(50);
+            assert_eq!(*guard, 0);
+            drop(guard);
+            t1.join().unwrap();
+            assert_eq!(*mutex.lock(), 1);
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use sync::Arc;
+    use thread;
+
+    #[test]
+    fn queue() {
+        let wq = Arc::new(SpinMutex::<WaitVariable<()>>::default());
+        let wq2 = wq.clone();
+
+        let locked = wq.lock();
+
+        let t1 = thread::spawn(move || {
+            assert!(WaitQueue::notify_one(wq2.lock()).is_none())
+        });
+
+        WaitQueue::wait(locked);
+
+        t1.join().unwrap();
+    }
+}