about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--mk/platform.mk3
-rw-r--r--mk/rt.mk9
-rw-r--r--src/librustc/middle/trans/base.rs1
-rw-r--r--src/librustc/middle/trans/foreign.rs5
-rw-r--r--src/librustc/rustc.rs8
-rw-r--r--src/libstd/rt/context.rs283
-rw-r--r--src/libstd/rt/crate_map.rs2
-rw-r--r--src/libstd/rt/env.rs2
-rw-r--r--src/libstd/rt/sched.rs2
-rw-r--r--src/libstd/rt/task.rs102
-rw-r--r--src/libstd/rt/thread.rs43
-rw-r--r--src/libstd/rt/thread_local_storage.rs45
-rw-r--r--src/rt/arch/arm/ccall.S26
-rw-r--r--src/rt/arch/arm/context.cpp39
-rw-r--r--src/rt/arch/arm/context.h44
-rw-r--r--src/rt/arch/arm/gpr.cpp16
-rw-r--r--src/rt/arch/arm/gpr.h23
-rw-r--r--src/rt/arch/arm/morestack.S41
-rw-r--r--src/rt/arch/arm/record_sp.S5
-rw-r--r--src/rt/arch/arm/regs.h21
-rw-r--r--src/rt/arch/arm/sp.h29
-rw-r--r--src/rt/arch/i386/ccall.S52
-rw-r--r--src/rt/arch/i386/context.cpp82
-rw-r--r--src/rt/arch/i386/context.h53
-rw-r--r--src/rt/arch/i386/gpr.cpp22
-rw-r--r--src/rt/arch/i386/gpr.h31
-rw-r--r--src/rt/arch/i386/morestack.S174
-rw-r--r--src/rt/arch/i386/regs.h12
-rw-r--r--src/rt/arch/i386/sp.h71
-rw-r--r--src/rt/arch/mips/ccall.S42
-rw-r--r--src/rt/arch/mips/context.cpp49
-rw-r--r--src/rt/arch/mips/context.h51
-rw-r--r--src/rt/arch/mips/gpr.cpp31
-rw-r--r--src/rt/arch/mips/gpr.h32
-rw-r--r--src/rt/arch/mips/morestack.S55
-rw-r--r--src/rt/arch/mips/record_sp.S12
-rw-r--r--src/rt/arch/mips/regs.h18
-rw-r--r--src/rt/arch/mips/sp.h29
-rw-r--r--src/rt/arch/x86_64/_context.S12
-rw-r--r--src/rt/arch/x86_64/ccall.S59
-rw-r--r--src/rt/arch/x86_64/context.cpp45
-rw-r--r--src/rt/arch/x86_64/context.h52
-rw-r--r--src/rt/arch/x86_64/gpr.cpp24
-rw-r--r--src/rt/arch/x86_64/gpr.h32
-rw-r--r--src/rt/arch/x86_64/morestack.S98
-rw-r--r--src/rt/arch/x86_64/regs.h2
-rw-r--r--src/rt/arch/x86_64/sp.h79
-rw-r--r--src/rt/rust_builtin.cpp25
-rw-r--r--src/rt/rust_gpr_base.h33
-rw-r--r--src/rt/rust_test_helpers.cpp1
-rw-r--r--src/rt/rustrt.def.in1
-rw-r--r--src/rt/sync/rust_thread.cpp9
-rw-r--r--src/rt/sync/rust_thread.h2
-rw-r--r--src/test/auxiliary/static_priv_by_default.rs1
-rw-r--r--src/test/compile-fail/static-priv-by-default.rs2
-rw-r--r--src/test/compile-fail/xcrate-private-by-default.rs7
-rw-r--r--src/test/run-pass/no-std-xcrate.rs2
57 files changed, 500 insertions, 1551 deletions
diff --git a/mk/platform.mk b/mk/platform.mk
index 8f9714e62d5..7ba8f77ef98 100644
--- a/mk/platform.mk
+++ b/mk/platform.mk
@@ -507,7 +507,8 @@ define CFG_MAKE_TOOLCHAIN
 
   # For the ARM and MIPS crosses, use the toolchain assembler
   # XXX: We should be able to use the LLVM assembler
-  CFG_ASSEMBLE_$(1)=$$(CC_$(1)) $$(CFG_DEPEND_FLAGS) $$(2) -c -o $$(1)
+  CFG_ASSEMBLE_$(1)=$$(CC_$(1)) $$(CFG_GCCISH_CFLAGS_$(1)) \
+		    $$(CFG_DEPEND_FLAGS) $$(2) -c -o $$(1)
 
   endif
 
diff --git a/mk/rt.mk b/mk/rt.mk
index f210b5ab389..2fb325e8348 100644
--- a/mk/rt.mk
+++ b/mk/rt.mk
@@ -91,8 +91,6 @@ RUNTIME_CXXS_$(1)_$(2) := \
               rt/miniz.cpp \
               rt/memory_region.cpp \
               rt/boxed_region.cpp \
-              rt/arch/$$(HOST_$(1))/context.cpp \
-              rt/arch/$$(HOST_$(1))/gpr.cpp \
               rt/rust_android_dummy.cpp \
               rt/rust_test_helpers.cpp
 
@@ -106,7 +104,6 @@ RUNTIME_CS_$(1)_$(2) := rt/sundown/src/autolink.c \
 			rt/sundown/html/html.c
 
 RUNTIME_S_$(1)_$(2) := rt/arch/$$(HOST_$(1))/_context.S \
-			rt/arch/$$(HOST_$(1))/ccall.S \
 			rt/arch/$$(HOST_$(1))/record_sp.S
 
 RT_BUILD_DIR_$(1)_$(2) := $$(RT_OUTPUT_DIR_$(1))/stage$(2)
@@ -122,7 +119,7 @@ RUNTIME_OBJS_$(1)_$(2) := $$(RUNTIME_CXXS_$(1)_$(2):rt/%.cpp=$$(RT_BUILD_DIR_$(1
                      $$(RUNTIME_S_$(1)_$(2):rt/%.S=$$(RT_BUILD_DIR_$(1)_$(2))/%.o)
 ALL_OBJ_FILES += $$(RUNTIME_OBJS_$(1)_$(2))
 
-MORESTACK_OBJ_$(1)_$(2) := $$(RT_BUILD_DIR_$(1)_$(2))/arch/$$(HOST_$(1))/morestack.o
+MORESTACK_OBJS_$(1)_$(2) := $$(RT_BUILD_DIR_$(1)_$(2))/arch/$$(HOST_$(1))/morestack.o
 ALL_OBJ_FILES += $$(MORESTACK_OBJS_$(1)_$(2))
 
 $$(RT_BUILD_DIR_$(1)_$(2))/%.o: rt/%.cpp $$(MKFILE_DEPS)
@@ -140,9 +137,9 @@ $$(RT_BUILD_DIR_$(1)_$(2))/%.o: rt/%.S  $$(MKFILE_DEPS) \
 	@$$(call E, compile: $$@)
 	$$(Q)$$(call CFG_ASSEMBLE_$(1),$$@,$$<)
 
-$$(RT_BUILD_DIR_$(1)_$(2))/arch/$$(HOST_$(1))/libmorestack.a: $$(MORESTACK_OBJ_$(1)_$(2))
+$$(RT_BUILD_DIR_$(1)_$(2))/arch/$$(HOST_$(1))/libmorestack.a: $$(MORESTACK_OBJS_$(1)_$(2))
 	@$$(call E, link: $$@)
-	$$(Q)$(AR_$(1)) rcs $$@ $$<
+	$$(Q)$(AR_$(1)) rcs $$@ $$^
 
 $$(RT_BUILD_DIR_$(1)_$(2))/$(CFG_RUNTIME_$(1)): $$(RUNTIME_OBJS_$(1)_$(2)) $$(MKFILE_DEPS) \
                         $$(RUNTIME_DEF_$(1)_$(2)) $$(LIBUV_LIB_$(1))
diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs
index e28d6a6899c..c6cc3092c11 100644
--- a/src/librustc/middle/trans/base.rs
+++ b/src/librustc/middle/trans/base.rs
@@ -2226,6 +2226,7 @@ pub fn trans_item(ccx: @mut CrateContext, item: &ast::item) {
                              [path_name(item.ident)]),
                 decl,
                 body,
+                item.attrs,
                 llfndecl,
                 item.id);
         } else if !generics.is_type_parameterized() {
diff --git a/src/librustc/middle/trans/foreign.rs b/src/librustc/middle/trans/foreign.rs
index 9c83f732204..09dd2f21a3b 100644
--- a/src/librustc/middle/trans/foreign.rs
+++ b/src/librustc/middle/trans/foreign.rs
@@ -386,6 +386,7 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: @mut CrateContext,
                                       path: &ast_map::path,
                                       decl: &ast::fn_decl,
                                       body: &ast::Block,
+                                      attrs: &[ast::Attribute],
                                       llwrapfn: ValueRef,
                                       id: ast::NodeId) {
     let _icx = push_ctxt("foreign::build_foreign_fn");
@@ -393,7 +394,7 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: @mut CrateContext,
 
     unsafe { // unsafe because we call LLVM operations
         // Build up the Rust function (`foo0` above).
-        let llrustfn = build_rust_fn(ccx, path, decl, body, id);
+        let llrustfn = build_rust_fn(ccx, path, decl, body, attrs, id);
 
         // Build up the foreign wrapper (`foo` above).
         return build_wrap_fn(ccx, llrustfn, llwrapfn, &tys);
@@ -403,6 +404,7 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: @mut CrateContext,
                      path: &ast_map::path,
                      decl: &ast::fn_decl,
                      body: &ast::Block,
+                     attrs: &[ast::Attribute],
                      id: ast::NodeId)
                      -> ValueRef {
         let _icx = push_ctxt("foreign::foreign::build_rust_fn");
@@ -434,6 +436,7 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: @mut CrateContext,
                t.repr(tcx));
 
         let llfndecl = base::decl_internal_rust_fn(ccx, f.sig.inputs, f.sig.output, ps);
+        base::set_llvm_fn_attrs(attrs, llfndecl);
         base::trans_fn(ccx,
                        (*path).clone(),
                        decl,
diff --git a/src/librustc/rustc.rs b/src/librustc/rustc.rs
index 1d9f37a2e87..712bf66bdaf 100644
--- a/src/librustc/rustc.rs
+++ b/src/librustc/rustc.rs
@@ -326,8 +326,12 @@ pub fn monitor(f: ~fn(@diagnostic::Emitter)) {
     use std::comm::*;
 
     // XXX: This is a hack for newsched since it doesn't support split stacks.
-    // rustc needs a lot of stack!
-    static STACK_SIZE: uint = 6000000;
+    // rustc needs a lot of stack! When optimizations are disabled, it needs
+    // even *more* stack than usual as well.
+    #[cfg(rtopt)]
+    static STACK_SIZE: uint = 6000000;  // 6MB
+    #[cfg(not(rtopt))]
+    static STACK_SIZE: uint = 20000000; // 20MB
 
     let (p, ch) = stream();
     let ch = SharedChan::new(ch);
diff --git a/src/libstd/rt/context.rs b/src/libstd/rt/context.rs
index 222f9a44b17..7f7545ca230 100644
--- a/src/libstd/rt/context.rs
+++ b/src/libstd/rt/context.rs
@@ -11,9 +11,12 @@
 use option::*;
 use super::stack::StackSegment;
 use libc::c_void;
+use uint;
 use cast::{transmute, transmute_mut_unsafe,
            transmute_region, transmute_mut_region};
 
+pub static RED_ZONE: uint = 20 * 1024;
+
 // FIXME #7761: Registers is boxed so that it is 16-byte aligned, for storing
 // SSE regs.  It would be marginally better not to do this. In C++ we
 // use an attribute on a struct.
@@ -24,14 +27,17 @@ pub struct Context {
     /// The context entry point, saved here for later destruction
     start: Option<~~fn()>,
     /// Hold the registers while the task or scheduler is suspended
-    regs: ~Registers
+    regs: ~Registers,
+    /// Lower bound and upper bound for the stack
+    stack_bounds: Option<(uint, uint)>,
 }
 
 impl Context {
     pub fn empty() -> Context {
         Context {
             start: None,
-            regs: new_regs()
+            regs: new_regs(),
+            stack_bounds: None,
         }
     }
 
@@ -47,7 +53,6 @@ impl Context {
 
         let fp: *c_void = task_start_wrapper as *c_void;
         let argp: *c_void = unsafe { transmute::<&~fn(), *c_void>(&*start) };
-        let stack_base: *uint = stack.start();
         let sp: *uint = stack.end();
         let sp: *mut uint = unsafe { transmute_mut_unsafe(sp) };
         // Save and then immediately load the current context,
@@ -57,11 +62,23 @@ impl Context {
             swap_registers(transmute_mut_region(&mut *regs), transmute_region(&*regs));
         };
 
-        initialize_call_frame(&mut *regs, fp, argp, sp, stack_base);
+        initialize_call_frame(&mut *regs, fp, argp, sp);
 
+        // Scheduler tasks don't have a stack in the "we allocated it" sense,
+        // but rather they run on pthreads stacks. We have complete control over
+        // them in terms of the code running on them (and hopefully they don't
+        // overflow). Additionally, their coroutine stacks are listed as being
+        // zero-length, so that's how we detect what's what here.
+        let stack_base: *uint = stack.start();
+        let bounds = if sp as uint == stack_base as uint {
+            None
+        } else {
+            Some((stack_base as uint, sp as uint))
+        };
         return Context {
             start: Some(start),
-            regs: regs
+            regs: regs,
+            stack_bounds: bounds,
         }
     }
 
@@ -79,8 +96,25 @@ impl Context {
         let in_regs: &Registers = match in_context {
             &Context { regs: ~ref r, _ } => r
         };
-        rtdebug!("doing raw swap");
-        unsafe { swap_registers(out_regs, in_regs) };
+
+        rtdebug!("noting the stack limit and doing raw swap");
+
+        unsafe {
+            // Right before we switch to the new context, set the new context's
+            // stack limit in the OS-specified TLS slot. This also  means that
+            // we cannot call any more rust functions after record_stack_bounds
+            // returns because they would all likely fail due to the limit being
+            // invalid for the current task. Lucky for us `swap_registers` is a
+            // C function so we don't have to worry about that!
+            match in_context.stack_bounds {
+                Some((lo, hi)) => record_stack_bounds(lo, hi),
+                // If we're going back to one of the original contexts or
+                // something that's possibly not a "normal task", then reset
+                // the stack limit to 0 to make morestack never fail
+                None => record_stack_bounds(0, uint::max_value),
+            }
+            swap_registers(out_regs, in_regs)
+        }
     }
 }
 
@@ -89,6 +123,29 @@ extern {
     fn swap_registers(out_regs: *mut Registers, in_regs: *Registers);
 }
 
+// Register contexts used in various architectures
+//
+// These structures all represent a context of one task throughout its
+// execution. Each struct is a representation of the architecture's register
+// set. When swapping between tasks, these register sets are used to save off
+// the current registers into one struct, and load them all from another.
+//
+// Note that this is only used for context switching, which means that some of
+// the registers may go unused. For example, for architectures with
+// callee/caller saved registers, the context will only reflect the callee-saved
+// registers. This is because the caller saved registers are already stored
+// elsewhere on the stack (if it was necessary anyway).
+//
+// Additionally, there may be fields on various architectures which are unused
+// entirely because they only reflect what is theoretically possible for a
+// "complete register set" to show, but user-space cannot alter these registers.
+// An example of this would be the segment selectors for x86.
+//
+// These structures/functions are roughly in-sync with the source files inside
+// of src/rt/arch/$arch. The only currently used function from those folders is
+// the `swap_registers` function, but that's only because for now segmented
+// stacks are disabled.
+
 #[cfg(target_arch = "x86")]
 struct Registers {
     eax: u32, ebx: u32, ecx: u32, edx: u32,
@@ -109,7 +166,7 @@ fn new_regs() -> ~Registers {
 
 #[cfg(target_arch = "x86")]
 fn initialize_call_frame(regs: &mut Registers, fptr: *c_void, arg: *c_void,
-                         sp: *mut uint, _stack_base: *uint) {
+                         sp: *mut uint) {
 
     let sp = align_down(sp);
     let sp = mut_offset(sp, -4);
@@ -125,6 +182,8 @@ fn initialize_call_frame(regs: &mut Registers, fptr: *c_void, arg: *c_void,
     regs.ebp = 0;
 }
 
+// windows requires saving more registers (both general and XMM), so the windows
+// register context must be larger.
 #[cfg(windows, target_arch = "x86_64")]
 type Registers = [uint, ..34];
 #[cfg(not(windows), target_arch = "x86_64")]
@@ -137,29 +196,14 @@ fn new_regs() -> ~Registers { ~([0, .. 22]) }
 
 #[cfg(target_arch = "x86_64")]
 fn initialize_call_frame(regs: &mut Registers, fptr: *c_void, arg: *c_void,
-                         sp: *mut uint, stack_base: *uint) {
+                         sp: *mut uint) {
 
-    // Redefinitions from regs.h
+    // Redefinitions from rt/arch/x86_64/regs.h
     static RUSTRT_ARG0: uint = 3;
     static RUSTRT_RSP: uint = 1;
     static RUSTRT_IP: uint = 8;
     static RUSTRT_RBP: uint = 2;
 
-    #[cfg(windows)]
-    fn initialize_tib(regs: &mut Registers, sp: *mut uint, stack_base: *uint) {
-        // Redefinitions from regs.h
-        static RUSTRT_ST1: uint = 11; // stack bottom
-        static RUSTRT_ST2: uint = 12; // stack top
-        regs[RUSTRT_ST1] = sp as uint;
-        regs[RUSTRT_ST2] = stack_base as uint;
-    }
-    #[cfg(not(windows))]
-    fn initialize_tib(_: &mut Registers, _: *mut uint, _: *uint) {
-    }
-
-    // Win64 manages stack range at TIB: %gs:0x08 (top) and %gs:0x10 (bottom)
-    initialize_tib(regs, sp, stack_base);
-
     let sp = align_down(sp);
     let sp = mut_offset(sp, -1);
 
@@ -167,9 +211,9 @@ fn initialize_call_frame(regs: &mut Registers, fptr: *c_void, arg: *c_void,
     unsafe { *sp = 0; }
 
     rtdebug!("creating call frame");
-    rtdebug!("fptr {}", fptr as uint);
-    rtdebug!("arg {}", arg as uint);
-    rtdebug!("sp {}", sp as uint);
+    rtdebug!("fptr {}", fptr);
+    rtdebug!("arg {}", arg);
+    rtdebug!("sp {}", sp);
 
     regs[RUSTRT_ARG0] = arg as uint;
     regs[RUSTRT_RSP] = sp as uint;
@@ -187,7 +231,7 @@ fn new_regs() -> ~Registers { ~([0, .. 32]) }
 
 #[cfg(target_arch = "arm")]
 fn initialize_call_frame(regs: &mut Registers, fptr: *c_void, arg: *c_void,
-                         sp: *mut uint, _stack_base: *uint) {
+                         sp: *mut uint) {
     let sp = align_down(sp);
     // sp of arm eabi is 8-byte aligned
     let sp = mut_offset(sp, -2);
@@ -208,7 +252,7 @@ fn new_regs() -> ~Registers { ~([0, .. 32]) }
 
 #[cfg(target_arch = "mips")]
 fn initialize_call_frame(regs: &mut Registers, fptr: *c_void, arg: *c_void,
-                         sp: *mut uint, _stack_base: *uint) {
+                         sp: *mut uint) {
     let sp = align_down(sp);
     // sp of mips o32 is 8-byte aligned
     let sp = mut_offset(sp, -2);
@@ -236,3 +280,182 @@ pub fn mut_offset<T>(ptr: *mut T, count: int) -> *mut T {
     use mem::size_of;
     (ptr as int + count * (size_of::<T>() as int)) as *mut T
 }
+
+#[inline(always)]
+pub unsafe fn record_stack_bounds(stack_lo: uint, stack_hi: uint) {
+    // When the old runtime had segmented stacks, it used a calculation that was
+    // "limit + RED_ZONE + FUDGE". The red zone was for things like dynamic
+    // symbol resolution, llvm function calls, etc. In theory this red zone
+    // value is 0, but it matters far less when we have gigantic stacks because
+    // we don't need to be so exact about our stack budget. The "fudge factor"
+    // was because LLVM doesn't emit a stack check for functions < 256 bytes in
+    // size. Again though, we have giant stacks, so we round all these
+    // calculations up to the nice round number of 20k.
+    record_sp_limit(stack_lo + RED_ZONE);
+
+    return target_record_stack_bounds(stack_lo, stack_hi);
+
+    #[cfg(not(windows))] #[cfg(not(target_arch = "x86_64"))] #[inline(always)]
+    unsafe fn target_record_stack_bounds(_stack_lo: uint, _stack_hi: uint) {}
+    #[cfg(windows, target_arch = "x86_64")] #[inline(always)]
+    unsafe fn target_record_stack_bounds(stack_lo: uint, stack_hi: uint) {
+        // Windows compiles C functions which may check the stack bounds. This
+        // means that if we want to perform valid FFI on windows, then we need
+        // to ensure that the stack bounds are what they truly are for this
+        // task. More info can be found at:
+        //   https://github.com/mozilla/rust/issues/3445#issuecomment-26114839
+        //
+        // stack range is at TIB: %gs:0x08 (top) and %gs:0x10 (bottom)
+        asm!("mov $0, %gs:0x08" :: "r"(stack_lo) :: "volatile");
+        asm!("mov $0, %gs:0x10" :: "r"(stack_hi) :: "volatile");
+    }
+}
+
+/// Records the current limit of the stack as specified by `end`.
+///
+/// This is stored in an OS-dependent location, likely inside of the thread
+/// local storage. The location that the limit is stored is a pre-ordained
+/// location because it's where LLVM has emitted code to check.
+///
+/// Note that this cannot be called under normal circumstances. This function is
+/// changing the stack limit, so upon returning any further function calls will
+/// possibly be triggering the morestack logic if you're not careful.
+///
+/// Also note that this and all of the inside functions are all flagged as
+/// "inline(always)" because they're messing around with the stack limits.  This
+/// would be unfortunate for the functions themselves to trigger a morestack
+/// invocation (if they were an actual function call).
+#[inline(always)]
+pub unsafe fn record_sp_limit(limit: uint) {
+    return target_record_sp_limit(limit);
+
+    // x86-64
+    #[cfg(target_arch = "x86_64", target_os = "macos")] #[inline(always)]
+    unsafe fn target_record_sp_limit(limit: uint) {
+        asm!("movq $$0x60+90*8, %rsi
+              movq $0, %gs:(%rsi)" :: "r"(limit) : "rsi" : "volatile")
+    }
+    #[cfg(target_arch = "x86_64", target_os = "linux")] #[inline(always)]
+    unsafe fn target_record_sp_limit(limit: uint) {
+        asm!("movq $0, %fs:112" :: "r"(limit) :: "volatile")
+    }
+    #[cfg(target_arch = "x86_64", target_os = "win32")] #[inline(always)]
+    unsafe fn target_record_sp_limit(limit: uint) {
+        // see: http://en.wikipedia.org/wiki/Win32_Thread_Information_Block
+        // store this inside of the "arbitrary data slot", but double the size
+        // because this is 64 bit instead of 32 bit
+        asm!("movq $0, %gs:0x28" :: "r"(limit) :: "volatile")
+    }
+    #[cfg(target_arch = "x86_64", target_os = "freebsd")] #[inline(always)]
+    unsafe fn target_record_sp_limit(limit: uint) {
+        asm!("movq $0, %fs:24" :: "r"(limit) :: "volatile")
+    }
+
+    // x86
+    #[cfg(target_arch = "x86", target_os = "macos")] #[inline(always)]
+    unsafe fn target_record_sp_limit(limit: uint) {
+        asm!("movl $$0x48+90*4, %eax
+              movl $0, %gs:(%eax)" :: "r"(limit) : "eax" : "volatile")
+    }
+    #[cfg(target_arch = "x86", target_os = "linux")]
+    #[cfg(target_arch = "x86", target_os = "freebsd")] #[inline(always)]
+    unsafe fn target_record_sp_limit(limit: uint) {
+        asm!("movl $0, %gs:48" :: "r"(limit) :: "volatile")
+    }
+    #[cfg(target_arch = "x86", target_os = "win32")] #[inline(always)]
+    unsafe fn target_record_sp_limit(limit: uint) {
+        // see: http://en.wikipedia.org/wiki/Win32_Thread_Information_Block
+        // store this inside of the "arbitrary data slot"
+        asm!("movl $0, %fs:0x14" :: "r"(limit) :: "volatile")
+    }
+
+    // mips, arm - Some brave soul can port these to inline asm, but it's over
+    //             my head personally
+    #[cfg(target_arch = "mips")]
+    #[cfg(target_arch = "arm")] #[inline(always)]
+    unsafe fn target_record_sp_limit(limit: uint) {
+        return record_sp_limit(limit as *c_void);
+        extern {
+            #[rust_stack]
+            fn record_sp_limit(limit: *c_void);
+        }
+    }
+}
+
+/// The counterpart of the function above, this function will fetch the current
+/// stack limit stored in TLS.
+///
+/// Note that all of these functions are meant to be exact counterparts of their
+/// brethren above, except that the operands are reversed.
+///
+/// As with the setter, this function does not have a __morestack header and can
+/// therefore be called in a "we're out of stack" situation.
+#[inline(always)]
+// NOTE: after the next snapshot, can remove the initialization before inline
+//       assembly due to an improvement in how it's handled, then this specific
+//       allow directive should get removed.
+#[allow(dead_assignment)]
+pub unsafe fn get_sp_limit() -> uint {
+    return target_get_sp_limit();
+
+    // x86-64
+    #[cfg(target_arch = "x86_64", target_os = "macos")] #[inline(always)]
+    unsafe fn target_get_sp_limit() -> uint {
+        let mut limit: uint = 0;
+        asm!("movq $$0x60+90*8, %rsi
+              movq %gs:(%rsi), $0" : "=r"(limit) :: "rsi" : "volatile");
+        return limit;
+    }
+    #[cfg(target_arch = "x86_64", target_os = "linux")] #[inline(always)]
+    unsafe fn target_get_sp_limit() -> uint {
+        let mut limit: uint = 0;
+        asm!("movq %fs:112, $0" : "=r"(limit) ::: "volatile");
+        return limit;
+    }
+    #[cfg(target_arch = "x86_64", target_os = "win32")] #[inline(always)]
+    unsafe fn target_get_sp_limit() -> uint {
+        let mut limit: uint = 0;
+        asm!("movq %gs:0x28, $0" : "=r"(limit) ::: "volatile");
+        return limit;
+    }
+    #[cfg(target_arch = "x86_64", target_os = "freebsd")] #[inline(always)]
+    unsafe fn target_get_sp_limit() -> uint {
+        let mut limit: uint = 0;
+        asm!("movq %fs:24, $0" : "=r"(limit) ::: "volatile");
+        return limit;
+    }
+
+    // x86
+    #[cfg(target_arch = "x86", target_os = "macos")] #[inline(always)]
+    unsafe fn target_get_sp_limit() -> uint {
+        let mut limit: uint = 0;
+        asm!("movl $$0x48+90*4, %eax
+              movl %gs:(%eax), $0" : "=r"(limit) :: "eax" : "volatile");
+        return limit;
+    }
+    #[cfg(target_arch = "x86", target_os = "linux")]
+    #[cfg(target_arch = "x86", target_os = "freebsd")] #[inline(always)]
+    unsafe fn target_get_sp_limit() -> uint {
+        let mut limit: uint = 0;
+        asm!("movl %gs:48, $0" : "=r"(limit) ::: "volatile");
+        return limit;
+    }
+    #[cfg(target_arch = "x86", target_os = "win32")] #[inline(always)]
+    unsafe fn target_get_sp_limit() -> uint {
+        let mut limit: uint = 0;
+        asm!("movl %fs:0x14, $0" : "=r"(limit) ::: "volatile");
+        return limit;
+    }
+
+    // mips, arm - Some brave soul can port these to inline asm, but it's over
+    //             my head personally
+    #[cfg(target_arch = "mips")]
+    #[cfg(target_arch = "arm")] #[inline(always)]
+    unsafe fn target_get_sp_limit() -> uint {
+        return get_sp_limit() as uint;
+        extern {
+            #[rust_stack]
+            fn get_sp_limit() -> *c_void;
+        }
+    }
+}
diff --git a/src/libstd/rt/crate_map.rs b/src/libstd/rt/crate_map.rs
index 8785dcca7bd..96a0069e851 100644
--- a/src/libstd/rt/crate_map.rs
+++ b/src/libstd/rt/crate_map.rs
@@ -17,7 +17,7 @@ use vec::ImmutableVector;
 // and instead look them up at runtime, which we need to resolve
 // the crate_map properly.
 #[cfg(target_os = "macos")]
-#[link_args = "-undefined dynamic_lookup"]
+#[link_args = "-Wl,-U,__rust_crate_map_toplevel"]
 extern {}
 
 pub struct ModEntry<'self> {
diff --git a/src/libstd/rt/env.rs b/src/libstd/rt/env.rs
index 5b840655120..c02e7fe9013 100644
--- a/src/libstd/rt/env.rs
+++ b/src/libstd/rt/env.rs
@@ -17,7 +17,7 @@ use os;
 // Note that these are all accessed without any synchronization.
 // They are expected to be initialized once then left alone.
 
-static mut MIN_STACK: uint = 2000000;
+static mut MIN_STACK: uint = 4000000;
 static mut DEBUG_BORROW: bool = false;
 
 pub fn init() {
diff --git a/src/libstd/rt/sched.rs b/src/libstd/rt/sched.rs
index 7724f58153e..93ac308df3a 100644
--- a/src/libstd/rt/sched.rs
+++ b/src/libstd/rt/sched.rs
@@ -173,7 +173,7 @@ impl Scheduler {
 
         // Now that we have an empty task struct for the scheduler
         // task, put it in TLS.
-        Local::put::(sched_task);
+        Local::put(sched_task);
 
         // Before starting our first task, make sure the idle callback
         // is active. As we do not start in the sleep state this is
diff --git a/src/libstd/rt/task.rs b/src/libstd/rt/task.rs
index 889d9bb3156..28c38ac9b53 100644
--- a/src/libstd/rt/task.rs
+++ b/src/libstd/rt/task.rs
@@ -29,6 +29,7 @@ use rt::logging::StdErrLogger;
 use super::local_heap::LocalHeap;
 use rt::sched::{Scheduler, SchedHandle};
 use rt::stack::{StackSegment, StackPool};
+use rt::context;
 use rt::context::Context;
 use unstable::finally::Finally;
 use task::spawn::Taskgroup;
@@ -465,6 +466,80 @@ impl Unwinder {
     }
 }
 
+/// This function is invoked from rust's current __morestack function. Segmented
+/// stacks are currently not enabled as segmented stacks, but rather one giant
+/// stack segment. This means that whenever we run out of stack, we want to
+/// truly consider it to be stack overflow rather than allocating a new stack.
+#[no_mangle]      // - this is called from C code
+#[no_split_stack] // - it would be sad for this function to trigger __morestack
+#[doc(hidden)] // XXX: this function shouldn't have to be `pub` to get exported
+               //      so it can be linked against, we should have a better way
+               //      of specifying that.
+pub extern "C" fn rust_stack_exhausted() {
+    use rt::in_green_task_context;
+    use rt::task::Task;
+    use rt::local::Local;
+    use rt::logging::Logger;
+    use unstable::intrinsics;
+
+    unsafe {
+        // We're calling this function because the stack just ran out. We need
+        // to call some other rust functions, but if we invoke the functions
+        // right now it'll just trigger this handler being called again. In
+        // order to alleviate this, we move the stack limit to be inside of the
+        // red zone that was allocated for exactly this reason.
+        let limit = context::get_sp_limit();
+        context::record_sp_limit(limit - context::RED_ZONE / 2);
+
+        // This probably isn't the best course of action. Ideally one would want
+        // to unwind the stack here instead of just aborting the entire process.
+        // This is a tricky problem, however. There's a few things which need to
+        // be considered:
+        //
+        //  1. We're here because of a stack overflow, yet unwinding will run
+        //     destructors and hence arbitrary code. What if that code overflows
+        //     the stack? One possibility is to use the above allocation of an
+        //     extra 10k to hope that we don't hit the limit, and if we do then
+        //     abort the whole program. Not the best, but kind of hard to deal
+        //     with unless we want to switch stacks.
+        //
+        //  2. LLVM will optimize functions based on whether they can unwind or
+        //     not. It will flag functions with 'nounwind' if it believes that
+        //     the function cannot trigger unwinding, but if we do unwind on
+        //     stack overflow then it means that we could unwind in any function
+        //     anywhere. We would have to make sure that LLVM only places the
+        //     nounwind flag on functions which don't call any other functions.
+        //
+        //  3. The function that overflowed may have owned arguments. These
+        //     arguments need to have their destructors run, but we haven't even
+        //     begun executing the function yet, so unwinding will not run the
+        //     any landing pads for these functions. If this is ignored, then
+        //     the arguments will just be leaked.
+        //
+        // Exactly what to do here is a very delicate topic, and is possibly
+        // still up in the air for what exactly to do. Some relevant issues:
+        //
+        //  #3555 - out-of-stack failure leaks arguments
+        //  #3695 - should there be a stack limit?
+        //  #9855 - possible strategies which could be taken
+        //  #9854 - unwinding on windows through __morestack has never worked
+        //  #2361 - possible implementation of not using landing pads
+
+        if in_green_task_context() {
+            do Local::borrow |task: &mut Task| {
+                let n = task.name.as_ref().map(|n| n.as_slice()).unwrap_or("<unnamed>");
+
+                format_args!(|args| { task.logger.log(args) },
+                             "task '{}' has overflowed its stack", n);
+            }
+        } else {
+            rterrln!("stack overflow in non-task context");
+        }
+
+        intrinsics::abort();
+    }
+}
+
 /// This is the entry point of unwinding for things like lang items and such.
 /// The arguments are normally generated by the compiler.
 pub fn begin_unwind(msg: *c_char, file: *c_char, line: size_t) -> ! {
@@ -481,22 +556,33 @@ pub fn begin_unwind(msg: *c_char, file: *c_char, line: size_t) -> ! {
         let msg = match msg.as_str() {
             Some(s) => s, None => rtabort!("message wasn't utf8?")
         };
-        let file = match file.as_str() {
-            Some(s) => s, None => rtabort!("message wasn't utf8?")
-        };
 
         if in_green_task_context() {
             // Be careful not to allocate in this block, if we're failing we may
             // have been failing due to a lack of memory in the first place...
             do Local::borrow |task: &mut Task| {
                 let n = task.name.as_ref().map(|n| n.as_slice()).unwrap_or("<unnamed>");
-                format_args!(|args| { task.logger.log(args) },
-                             "task '{}' failed at '{}', {}:{}",
-                             n, msg, file, line);
+
+                match file.as_str() {
+                    Some(file) => {
+                        format_args!(|args| { task.logger.log(args) },
+                                     "task '{}' failed at '{}', {}:{}",
+                                     n, msg, file, line);
+                    }
+                    None => {
+                        format_args!(|args| { task.logger.log(args) },
+                                     "task '{}' failed at '{}'", n, msg);
+                    }
+                }
             }
         } else {
-            rterrln!("failed in non-task context at '{}', {}:{}",
-                     msg, file, line as int);
+            match file.as_str() {
+                Some(file) => {
+                    rterrln!("failed in non-task context at '{}', {}:{}",
+                             msg, file, line as int);
+                }
+                None => rterrln!("failed in non-task context at '{}'", msg),
+            }
         }
 
         let task: *mut Task = Local::unsafe_borrow();
diff --git a/src/libstd/rt/thread.rs b/src/libstd/rt/thread.rs
index 8b64fda2136..e774b81da35 100644
--- a/src/libstd/rt/thread.rs
+++ b/src/libstd/rt/thread.rs
@@ -8,8 +8,11 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use cast;
 use libc;
 use ops::Drop;
+use unstable::raw;
+use uint;
 
 #[allow(non_camel_case_types)] // runtime type
 type raw_thread = libc::c_void;
@@ -17,21 +20,38 @@ type raw_thread = libc::c_void;
 pub struct Thread {
     main: ~fn(),
     raw_thread: *raw_thread,
-    joined: bool
+    joined: bool,
 }
 
 impl Thread {
+    #[fixed_stack_segment] #[inline(never)]
     pub fn start(main: ~fn()) -> Thread {
-        fn substart(main: &~fn()) -> *raw_thread {
-            #[fixed_stack_segment]; #[inline(never)];
-
-            unsafe { rust_raw_thread_start(main) }
+        // This is the starting point of rust os threads. The first thing we do
+        // is make sure that we don't trigger __morestack (also why this has a
+        // no_split_stack annotation), and then we re-build the main function
+        // and invoke it from there.
+        #[no_split_stack]
+        extern "C" fn thread_start(code: *(), env: *()) {
+            use rt::context;
+            unsafe {
+                context::record_stack_bounds(0, uint::max_value);
+                let f: &fn() = cast::transmute(raw::Closure {
+                    code: code,
+                    env: env,
+                });
+                f();
+            }
         }
-        let raw = substart(&main);
+
+        let raw_thread = unsafe {
+            let c: raw::Closure = cast::transmute_copy(&main);
+            let raw::Closure { code, env } = c;
+            rust_raw_thread_start(thread_start, code, env)
+        };
         Thread {
             main: main,
-            raw_thread: raw,
-            joined: false
+            raw_thread: raw_thread,
+            joined: false,
         }
     }
 
@@ -55,7 +75,8 @@ impl Drop for Thread {
 }
 
 extern {
-    pub fn rust_raw_thread_start(f: &(~fn())) -> *raw_thread;
-    pub fn rust_raw_thread_join(thread: *raw_thread);
-    pub fn rust_raw_thread_delete(thread: *raw_thread);
+    fn rust_raw_thread_start(f: extern "C" fn(*(), *()),
+                             code: *(), env: *()) -> *raw_thread;
+    fn rust_raw_thread_join(thread: *raw_thread);
+    fn rust_raw_thread_delete(thread: *raw_thread);
 }
diff --git a/src/libstd/rt/thread_local_storage.rs b/src/libstd/rt/thread_local_storage.rs
index cd89d09ffc0..ddb104240f2 100644
--- a/src/libstd/rt/thread_local_storage.rs
+++ b/src/libstd/rt/thread_local_storage.rs
@@ -27,15 +27,11 @@ pub unsafe fn create(key: &mut Key) {
 }
 
 #[cfg(unix)]
-#[fixed_stack_segment]
-#[inline(never)]
 pub unsafe fn set(key: Key, value: *mut c_void) {
     assert_eq!(0, pthread_setspecific(key, value));
 }
 
 #[cfg(unix)]
-#[fixed_stack_segment]
-#[inline(never)]
 pub unsafe fn get(key: Key) -> *mut c_void {
     pthread_getspecific(key)
 }
@@ -53,8 +49,21 @@ type pthread_key_t = ::libc::c_uint;
 #[cfg(unix)]
 extern {
     fn pthread_key_create(key: *mut pthread_key_t, dtor: *u8) -> c_int;
-    fn pthread_setspecific(key: pthread_key_t, value: *mut c_void) -> c_int;
+
+    // This function is a very cheap operation on both osx and unix. On osx, it
+    // turns out it's just three instructions, and on unix it's a cheap function
+    // which only uses a very small amount of stack.
+    //
+    // This is not marked as such because we think it has a small stack, but
+    // rather we would like to be able to fetch information from
+    // thread-local-storage when a task is running very low on its stack budget.
+    // For example, this is invoked whenever stack overflow is detected, and we
+    // obviously have very little budget to deal with (certainly not anything
+    // close to a fixed_stack_segment)
+    #[rust_stack]
     fn pthread_getspecific(key: pthread_key_t) -> *mut c_void;
+    #[rust_stack]
+    fn pthread_setspecific(key: pthread_key_t, value: *mut c_void) -> c_int;
 }
 
 #[cfg(windows)]
@@ -70,31 +79,37 @@ pub unsafe fn create(key: &mut Key) {
 }
 
 #[cfg(windows)]
-#[fixed_stack_segment]
-#[inline(never)]
 pub unsafe fn set(key: Key, value: *mut c_void) {
     assert!(0 != TlsSetValue(key, value))
 }
 
 #[cfg(windows)]
-#[fixed_stack_segment]
-#[inline(never)]
 pub unsafe fn get(key: Key) -> *mut c_void {
     TlsGetValue(key)
 }
 
 #[cfg(windows, target_arch = "x86")]
 extern "stdcall" {
-       fn TlsAlloc() -> DWORD;
-       fn TlsSetValue(dwTlsIndex: DWORD, lpTlsvalue: LPVOID) -> BOOL;
-       fn TlsGetValue(dwTlsIndex: DWORD) -> LPVOID;
+    fn TlsAlloc() -> DWORD;
+
+    // See the reasoning in pthread_getspecific as to why this has the
+    // 'rust_stack' attribute, as this function was also verified to only
+    // require a small amount of stack.
+    #[rust_stack]
+    fn TlsGetValue(dwTlsIndex: DWORD) -> LPVOID;
+    #[rust_stack]
+    fn TlsSetValue(dwTlsIndex: DWORD, lpTlsvalue: LPVOID) -> BOOL;
 }
 
 #[cfg(windows, target_arch = "x86_64")]
 extern {
-       fn TlsAlloc() -> DWORD;
-       fn TlsSetValue(dwTlsIndex: DWORD, lpTlsvalue: LPVOID) -> BOOL;
-       fn TlsGetValue(dwTlsIndex: DWORD) -> LPVOID;
+    fn TlsAlloc() -> DWORD;
+
+    // See above.
+    #[rust_stack]
+    fn TlsGetValue(dwTlsIndex: DWORD) -> LPVOID;
+    #[rust_stack]
+    fn TlsSetValue(dwTlsIndex: DWORD, lpTlsvalue: LPVOID) -> BOOL;
 }
 
 #[test]
diff --git a/src/rt/arch/arm/ccall.S b/src/rt/arch/arm/ccall.S
deleted file mode 100644
index 3350a040f53..00000000000
--- a/src/rt/arch/arm/ccall.S
+++ /dev/null
@@ -1,26 +0,0 @@
-// Mark stack as non-executable
-#if defined(__linux__) && defined(__ELF__)
-.section	.note.GNU-stack, "", %progbits
-#endif
-
-.text
-.code 32
-.arm
-.align
-
-.globl __morestack
-.hidden __morestack
-.type __morestack, %function
-__morestack:
-	.fnstart
-	.save {r4, fp, lr}
-	push {r4, fp, lr}
-    .movsp r4
-	mov r4, sp
-	mov sp, r2
-	mov fp, sp
-	blx r1
-	mov sp, r4
-	pop {r4, fp, lr}
-	mov pc, lr
-	.fnend
diff --git a/src/rt/arch/arm/context.cpp b/src/rt/arch/arm/context.cpp
deleted file mode 100644
index 7d90668aad5..00000000000
--- a/src/rt/arch/arm/context.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-// xfail-license
-
-#include "context.h"
-#include "../../rust_globals.h"
-
-extern "C" void CDECL swap_registers(registers_t *oregs,
-                                     registers_t *regs)
-asm ("swap_registers");
-
-context::context()
-{
-    assert((void*)&regs == (void*)this);
-    memset(&regs, 0, sizeof(regs));
-}
-
-void context::swap(context &out)
-{
-    swap_registers(&out.regs, &regs);
-}
-
-void context::call(void *f, void *arg, void *stack)
-{
-  // Get the current context, which we will then modify to call the
-  // given function.
-  swap(*this);
-
-  // set up the stack
-  uint32_t *sp = ( uint32_t *)stack;
-  sp = align_down(sp);
-  // The final return address. 0 indicates the bottom of the stack
-  // sp of arm eabi is 8-byte aligned
-  sp -= 2;
-  *sp = 0;
-
-  regs.data[0] = ( uint32_t )arg; // r0
-  regs.data[13] = ( uint32_t )sp; //#52 sp, r13
-  regs.data[14] = ( uint32_t )f;  //#60 pc, r15 --> lr,
-  // Last base pointer on the stack should be 0
-}
diff --git a/src/rt/arch/arm/context.h b/src/rt/arch/arm/context.h
deleted file mode 100644
index 54f0df7de31..00000000000
--- a/src/rt/arch/arm/context.h
+++ /dev/null
@@ -1,44 +0,0 @@
-// -*- mode: c++ -*-
-// xfail-license
-
-#ifndef CONTEXT_H
-#define CONTEXT_H
-
-#include <cstdlib>
-#include <inttypes.h>
-#include <stdint.h>
-//#include <xmmintrin.h>
-
-#include "vg/memcheck.h"
-
-template<typename T>
-T align_down(T sp)
-{
-    // There is no platform we care about that needs more than a
-    // 16-byte alignment.
-    return (T)((uint32_t)sp & ~(16 - 1));
-}
-
-// The struct in which we store the saved data.  This is mostly the
-// volatile registers and instruction pointer, but it also includes
-// RCX/RDI which are used to pass arguments.  The indices for each
-// register are found in "regs.h".  Note that the alignment must be
-// 16 bytes so that SSE instructions can be used.
-#include "regs.h"
-struct registers_t {
-    uint32_t data[RUSTRT_MAX];
-} __attribute__((aligned(16)));
-
-class context {
-public:
-    registers_t regs;
-
-    context();
-
-    context *next;
-
-    void swap(context &out);
-    void call(void *f, void *arg, void *sp);
-};
-
-#endif
diff --git a/src/rt/arch/arm/gpr.cpp b/src/rt/arch/arm/gpr.cpp
deleted file mode 100644
index 77ec9d5182a..00000000000
--- a/src/rt/arch/arm/gpr.cpp
+++ /dev/null
@@ -1,16 +0,0 @@
-// xfail-license
-
-#include "gpr.h"
-
-#define LOAD(rn) do { \
-    uintptr_t tmp; \
-    asm("mov %%" #rn ",%0" : "=r" (tmp) :); \
-    this->rn = tmp; \
-} while (0)
-
-void rust_gpr::load() {
-    LOAD(r0); LOAD(r1); LOAD(r2); LOAD(r3);
-    LOAD(r4); LOAD(r5); LOAD(r6); LOAD(r7);
-    LOAD(r8);  LOAD(r9);  LOAD(r10); LOAD(r11);
-    LOAD(r12); LOAD(r13); LOAD(r14); LOAD(r15);
-}
diff --git a/src/rt/arch/arm/gpr.h b/src/rt/arch/arm/gpr.h
deleted file mode 100644
index c8a3e916a37..00000000000
--- a/src/rt/arch/arm/gpr.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// xfail-license
-// General-purpose registers. This structure is used during stack crawling.
-
-#ifndef GPR_H
-#define GPR_H
-
-#include "rust_gpr_base.h"
-
-class rust_gpr : public rust_gpr_base {
-public:
-    uintptr_t r0, r1, r2, r3, r4, r5, r6, r7;
-    uintptr_t  r8,  r9, r10, r11, r12, r13, r14, r15;
-
-    inline uintptr_t get_fp() { return r11; }
-    inline uintptr_t get_ip() { return r12; }
-
-    inline void set_fp(uintptr_t new_fp) { r11 = new_fp; }
-    inline void set_ip(uintptr_t new_ip) { r12 = new_ip; }
-
-    void load();
-};
-
-#endif
diff --git a/src/rt/arch/arm/morestack.S b/src/rt/arch/arm/morestack.S
index f0ec3f4b7a5..219f0962d77 100644
--- a/src/rt/arch/arm/morestack.S
+++ b/src/rt/arch/arm/morestack.S
@@ -3,13 +3,14 @@
 .section	.note.GNU-stack, "", %progbits
 #endif
 
+/* See i386/morestack.S for the lengthy, general explanation. */
+
 .text
 .code 32
 .arm
 .align
 
-.global upcall_new_stack
-.global upcall_del_stack
+.global rust_stack_exhausted
 .global __morestack
 .hidden __morestack
 
@@ -32,40 +33,8 @@ __morestack:
     // Save argument registers of the original function
     push {r0, r1, r2, r3, lr}
 
-    mov r0, r4         // The amount of stack needed
-    add r1, fp, #20    // Address of stack arguments
-    mov r2, r5         // Size of stack arguments
-
     // Create new stack
-    bl upcall_new_stack@plt
-
-    // Hold new stack pointer
-    mov r5, r0
-
-    // Pop the saved arguments
-    pop {r0, r1, r2, r3, lr}
-
-    // Grab the return pointer
-    add r4, lr, #16    // Skip past the return
-    mov sp, r5         // Swich to the new stack
-    mov lr, pc
-    mov pc, r4         // Call the original function
-
-    // Switch back to rust stack
-    mov sp, r6
-
-    // Save return value
-	mov r4, r0
-	mov r5, r1
-
-    // Remove the new allocated stack
-    bl upcall_del_stack@plt
-
-    // Restore return value
-	mov r0, r4
-	mov r1, r5
+    bl rust_stack_exhausted@plt
 
-    // Return
-    pop {r6, fp, lr}
-    mov pc, lr
+    // the above function ensures that it never returns
     .fnend
diff --git a/src/rt/arch/arm/record_sp.S b/src/rt/arch/arm/record_sp.S
index 3c5c7644beb..6900444c0fe 100644
--- a/src/rt/arch/arm/record_sp.S
+++ b/src/rt/arch/arm/record_sp.S
@@ -11,7 +11,6 @@
 
 .globl record_sp_limit
 .globl get_sp_limit
-.globl get_sp
 
 record_sp_limit:
 	// First, try to read TLS address from coprocessor
@@ -46,7 +45,3 @@ get_sp_limit:
 
 	ldr r0, [r3]
 	mov pc, lr
-
-get_sp:
-	mov r0, sp
-	mov pc, lr
diff --git a/src/rt/arch/arm/regs.h b/src/rt/arch/arm/regs.h
deleted file mode 100644
index 0d1c24e0fb7..00000000000
--- a/src/rt/arch/arm/regs.h
+++ /dev/null
@@ -1,21 +0,0 @@
-// xfail-license
-
-#define RUSTRT_RBX   0
-#define RUSTRT_RSP   1
-#define RUSTRT_RBP   2
-// RCX on Windows, RDI elsewhere
-#define RUSTRT_ARG0  3
-#define RUSTRT_R12   4
-#define RUSTRT_R13   5
-#define RUSTRT_R14   6
-#define RUSTRT_R15   7
-#define RUSTRT_IP    8
-
-#define RUSTRT_MAX  32
-
-// ARG0 is the register in which the first argument goes.
-// Naturally this depends on your operating system.
-#   define RUSTRT_ARG0_S r0
-#   define RUSTRT_ARG1_S r1
-#   define RUSTRT_ARG2_S r2
-#   define RUSTRT_ARG3_S r3
diff --git a/src/rt/arch/arm/sp.h b/src/rt/arch/arm/sp.h
deleted file mode 100644
index cd798847607..00000000000
--- a/src/rt/arch/arm/sp.h
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2012 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.
-
-// Getting the stack pointer and getting/setting sp limit.
-
-#ifndef SP_H
-#define SP_H
-
-#include "../../rust_globals.h"
-
-// Gets a pointer to the vicinity of the current stack pointer
-extern "C" uintptr_t get_sp();
-
-// Gets the pointer to the end of the Rust stack from a platform-
-// specific location in the thread control block
-extern "C" CDECL uintptr_t get_sp_limit();
-
-// Records the pointer to the end of the Rust stack in a platform-
-// specific location in the thread control block
-extern "C" CDECL void record_sp_limit(void *limit);
-
-#endif
diff --git a/src/rt/arch/i386/ccall.S b/src/rt/arch/i386/ccall.S
deleted file mode 100644
index eeee7a4e715..00000000000
--- a/src/rt/arch/i386/ccall.S
+++ /dev/null
@@ -1,52 +0,0 @@
-// Mark stack as non-executable
-#if defined(__linux__) && defined(__ELF__)
-.section	.note.GNU-stack, "", @progbits
-#endif
-
-/*
-	The function for switching to the C stack.  It is called
-	__morestack because gdb allows any frame with that name to
-	move the stack pointer to a different stack, which it usually
-	considers an error.
-*/
-
-	.text
-
-#if defined(__APPLE__) || defined(__WIN32__)
-.globl ___morestack
-___morestack:
-#else
-.globl __morestack
-.hidden __morestack
-__morestack:
-#endif
-
-#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__WIN32__)
-	.cfi_startproc
-#endif
-
-	pushl %ebp
-
-#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__WIN32__)
-	.cfi_def_cfa_offset 8
-	.cfi_offset %ebp, -8
-#endif
-
-	movl %esp,%ebp          // save esp
-
-#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__WIN32__)
-	.cfi_def_cfa_register %ebp
-#endif
-
-	movl 16(%ebp),%esp      // load new esp
-	subl $12,%esp           // maintain 16-byte alignment
-	pushl 8(%ebp)           // push ptr to argument block
-	calll *12(%ebp)
-	movl %ebp,%esp          // would like to use "leave" but it's slower
-	popl %ebp
-
-	ret
-
-#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__WIN32__)
-	.cfi_endproc
-#endif
diff --git a/src/rt/arch/i386/context.cpp b/src/rt/arch/i386/context.cpp
deleted file mode 100644
index 94e6f0418d0..00000000000
--- a/src/rt/arch/i386/context.cpp
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright 2012 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.
-
-
-#include "context.h"
-#include "../../rust_globals.h"
-
-extern "C" uint32_t CDECL swap_registers(registers_t *oregs,
-                                         registers_t *regs);
-
-context::context()
-{
-    assert((void*)&regs == (void*)this);
-}
-
-void context::swap(context &out)
-{
-  swap_registers(&out.regs, &regs);
-}
-
-void context::call(void *f, void *arg, void *stack) {
-  // Get the current context, which we will then modify to call the
-  // given function.
-  swap(*this);
-
-  // set up the trampoline frame
-  uint32_t *sp = (uint32_t *)stack;
-
-  // Shift the stack pointer so the alignment works out right.
-  sp = align_down(sp) - 3;
-  *--sp = (uint32_t)arg;
-  // The final return address. 0 indicates the bottom of the stack
-  *--sp = 0;
-
-  regs.esp = (uint32_t)sp;
-  regs.eip = (uint32_t)f;
-
-  // Last base pointer on the stack should be 0
-  regs.ebp = 0;
-}
-
-#if 0
-// This is some useful code to check how the registers struct got
-// layed out in memory.
-int main() {
-  registers_t regs;
-
-  printf("Register offsets\n");
-
-#define REG(r) \
-  printf("  %6s: +%ld\n", #r, (intptr_t)&regs.r - (intptr_t)&regs);
-
-  REG(eax);
-  REG(ebx);
-  REG(ecx);
-  REG(edx);
-  REG(ebp);
-  REG(esi);
-  REG(edi);
-  REG(esp);
-
-  REG(cs);
-  REG(ds);
-  REG(ss);
-  REG(es);
-  REG(fs);
-  REG(gs);
-
-  REG(eflags);
-
-  REG(eip);
-
-  return 0;
-}
-#endif
diff --git a/src/rt/arch/i386/context.h b/src/rt/arch/i386/context.h
deleted file mode 100644
index 33352b4a556..00000000000
--- a/src/rt/arch/i386/context.h
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2012 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.
-
-
-#ifndef CONTEXT_H
-#define CONTEXT_H
-
-#include <cstdlib>
-#include <inttypes.h>
-#include <stdint.h>
-
-#include "vg/memcheck.h"
-
-template<typename T>
-T align_down(T sp)
-{
-    // There is no platform we care about that needs more than a
-    // 16-byte alignment.
-    return (T)((uint32_t)sp & ~(16 - 1));
-}
-
-struct registers_t {
-  // general purpose registers
-  uint32_t eax, ebx, ecx, edx, ebp, esi, edi, esp;
-
-  // segment registers
-  uint16_t cs, ds, ss, es, fs, gs;
-
-  uint32_t eflags;
-
-  uint32_t eip;
-} __attribute__((aligned(16)));
-
-class context {
-public:
-  registers_t regs;
-
-  context();
-
-  context *next;
-
-  void swap(context &out);
-  void call(void *f, void *arg, void *sp);
-};
-
-#endif
diff --git a/src/rt/arch/i386/gpr.cpp b/src/rt/arch/i386/gpr.cpp
deleted file mode 100644
index e5a59d664b0..00000000000
--- a/src/rt/arch/i386/gpr.cpp
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2012 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.
-
-#include "gpr.h"
-
-#define LOAD(rn) do { \
-    uintptr_t tmp; \
-    asm("movl %%" #rn ",%0" : "=r" (tmp) :); \
-    this->rn = tmp; \
-} while (0)
-
-void rust_gpr::load() {
-    LOAD(eax); LOAD(ebx); LOAD(ecx); LOAD(edx);
-    LOAD(esi); LOAD(edi); LOAD(ebp); LOAD(esi);
-}
diff --git a/src/rt/arch/i386/gpr.h b/src/rt/arch/i386/gpr.h
deleted file mode 100644
index 1953170301c..00000000000
--- a/src/rt/arch/i386/gpr.h
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2012 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.
-
-// General-purpose registers. This structure is used during stack crawling.
-
-#ifndef GPR_H
-#define GPR_H
-
-#include "rust_gpr_base.h"
-
-class rust_gpr : public rust_gpr_base {
-public:
-    uintptr_t eax, ebx, ecx, edx, esi, edi, ebp, eip;
-
-    inline uintptr_t get_fp() { return ebp; }
-    inline uintptr_t get_ip() { return eip; }
-
-    inline void set_fp(uintptr_t new_fp) { ebp = new_fp; }
-    inline void set_ip(uintptr_t new_ip) { eip = new_ip; }
-
-    void load();
-};
-
-#endif
diff --git a/src/rt/arch/i386/morestack.S b/src/rt/arch/i386/morestack.S
index 9598f14579f..25f907f479c 100644
--- a/src/rt/arch/i386/morestack.S
+++ b/src/rt/arch/i386/morestack.S
@@ -6,29 +6,27 @@
 /*
 	__morestack
 
-	This function implements stack growth using the mechanism
-	devised by Ian Lance Taylor for gccgo, described here:
+        This function is normally used to implement stack growth using the
+        mechanism devised by Ian Lance Taylor for gccgo, described here:
 
 	http://gcc.gnu.org/wiki/SplitStacks
 
-	The Rust stack is composed of a linked list of stack segments,
-	and each stack segment contains two parts: the work area,
-	where Rust functions are allowed to execute; and the red zone,
-	where no Rust code can execute, but where short runtime
-	functions (including __morestack), the dynamic linker, signal
-	handlers, and the unwinder can run.
+        Each Rust function contains an LLVM-generated prologue that compares the
+        stack space required for the current function to the space remaining in
+        the current stack segment, maintained in a platform-specific TLS slot.
+        The stack limit is strategically maintained by the Rust runtime so that
+        it is always in place whenever a Rust function is running.
 
-	Each Rust function contains an LLVM-generated prologue that
-	compares the stack space required for the current function to
-	the space remaining in the current stack segment,
-	maintained in a platform-specific TLS slot.  The stack limit
-	is strategically maintained by the Rust runtime so that it is
-	always in place whenever a Rust function is running.
+        In Rust, however, we currently do not use __morestack for stack growth
+        purposes.  Rather each task has one large stack segment. When this
+        __morestack function is run, we interpret this as a "stack overflow"
+        event rather than an event requiring an allocation of a new stack.
 
-	When there is not enough room to run the function, the function
-	prologue makes a call to __morestack to allocate a new stack
-	segment, copy any stack-based arguments to it, switch stacks,
-	then resume execution of the original function.
+        In the early days, this implementation did indeed have all of the fiddly
+        bits in order to manage split stacks in the sense of always growing
+        stacks. For posterity, the implementation can be found at commit
+        c8e77d5586aed50821e0b9361b2e24c96ade816c if we ever need to refer back
+        to it.
 
 	-- The __morestack calling convention --
 
@@ -72,30 +70,20 @@
 .text
 
 #if defined(__APPLE__)
-#define RUST_GET_TASK           L_rust_get_task$stub
-#define UPCALL_NEW_STACK        L_upcall_new_stack$stub
-#define UPCALL_DEL_STACK        L_upcall_del_stack$stub
 #define MORESTACK               ___morestack
+#define EXHAUSTED               _rust_stack_exhausted
 #else
 #if defined(__linux__) || defined(__FreeBSD__)
-#define UPCALL_NEW_STACK        upcall_new_stack
-#define UPCALL_DEL_STACK        upcall_del_stack
-#define RUST_GET_TASK           rust_get_task
 #define MORESTACK               __morestack
+#define EXHAUSTED               rust_stack_exhausted
 #else
-#define UPCALL_NEW_STACK        _upcall_new_stack
-#define UPCALL_DEL_STACK        _upcall_del_stack
-#define RUST_GET_TASK           _rust_get_task
 #define MORESTACK               ___morestack
+#define EXHAUSTED               _rust_stack_exhausted
 #endif
 #endif
 
-#ifndef __APPLE__
-.globl UPCALL_NEW_STACK
-.globl UPCALL_DEL_STACK
-.globl RUST_GET_TASK
-#endif
 .globl MORESTACK
+.globl EXHAUSTED
 
 // FIXME: What about __WIN32__?
 #if defined(__linux__) || defined(__FreeBSD__)
@@ -111,9 +99,7 @@
 #endif
 
 MORESTACK:
-#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__WIN32__)
 	.cfi_startproc
-#endif
 
 	// This base pointer setup differs from most in that we are
 	// telling the unwinder to consider the Canonical Frame
@@ -129,129 +115,21 @@ MORESTACK:
 	// would normally be, accounting for the two arguments to
 	// __morestack, and an extra return address.
 
+        // FIXME(#9854) these cfi directives don't work on windows.
+
 	pushl %ebp
-#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__WIN32__)
 	// The CFA is 20 bytes above the register that it is
 	// associated with for this frame (which will be %ebp)
 	.cfi_def_cfa_offset 20
 	// %ebp is -20 bytes from the CFA
 	.cfi_offset %ebp, -20
-#endif
 	movl %esp, %ebp
-#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__WIN32__)
 	// Calculate the CFA as an offset from %ebp
 	.cfi_def_cfa_register %ebp
-#endif
-
-	// NB: This can be called with the fastcc convention so we
-	// have to preserve any argument registers
-
-	// NB: __morestack is called misaligned by 4 bytes, i.e.
-	// subl $4, %esp would get us to a normal alignment
-
-	subl $28,%esp
-
-	// Save fastcc arguments
-	movl %ecx, 16(%esp)
-	movl %edx, 12(%esp)
-
-	// FIXME (1388): it's possible we also need to save/restore some
-	// SSE2 registers here, if floats-go-in-regs on x86+SSE2. Unclear.
-
-	// FIXME (1226): main is compiled with the split-stack prologue,
-	// causing it to call __morestack, so we have to jump back out
-	calll RUST_GET_TASK
-	testl %eax,%eax
-	jz .L$bail
-
-	// The arguments to upcall_new_stack
-
-	// The size of the stack arguments to copy to the new stack,
-	// and of the arguments to __morestack
-	movl 40(%esp),%eax
-	movl %eax,8(%esp)
-	// The address of the stack arguments to the original function
-	leal 48(%esp),%eax
-	movl %eax,4(%esp)
-	// The amount of stack needed for the original function,
-	// the other argument to __morestack
-	movl 36(%esp),%eax // The amount of stack needed
-	movl %eax,(%esp)
-
-	call UPCALL_NEW_STACK
-
-	// Save the address of the new stack
-	movl %eax, (%esp)
-
-	// Grab the __morestack return pointer
-	movl 32(%esp),%eax
-	// Skip past the ret instruction in the parent fn
-	inc  %eax
-
-	// Restore the fastcc arguments to the original function
-	movl 16(%esp), %ecx
-	movl 12(%esp), %edx
-
-        // Switch stacks
-	movl (%esp),%esp
-        // Re-enter the function that called us
-	call *%eax
-
-	// Now the function that called us has returned, so we need to
-	// delete the old stack space
 
-	// Switch back to the rust stack
-	movl %ebp, %esp
+        // re-align the stack
+        subl $12,%esp
+        calll EXHAUSTED
+        // the exhaustion function guarantees that it can't return
 
-	// Realign stack - remember that __morestack was called misaligned
-	subl $12, %esp
-
-	// Save the return value of the function we allocated space for
-	movl %edx, 4(%esp)
-	movl %eax, (%esp)
-
-	call UPCALL_DEL_STACK
-
-	// And restore it
-	movl (%esp), %eax
-	movl 4(%esp), %edx
-
-	addl $12,%esp
-
-	popl %ebp
-
-	retl $8
-
-.L$bail:
-	movl 32(%esp),%eax
-	inc %eax
-
-	addl $44, %esp
-	popl %ebp
-	addl $4+8,%esp
-
-	jmpl *%eax
-
-#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__WIN32__)
 	.cfi_endproc
-#endif
-
-#ifdef __APPLE__
-
-.section __IMPORT,__jump_table,symbol_stubs,pure_instructions+self_modifying_code,5
-
-	// Linker will replace the hlts (the ascii) with jmp
-L_rust_get_task$stub:
-	.indirect_symbol _rust_get_task
-	.ascii	 "\364\364\364\364\364"
-
-L_upcall_new_stack$stub:
-	.indirect_symbol _upcall_new_stack
-	.ascii	 "\364\364\364\364\364"
-
-L_upcall_del_stack$stub:
-	.indirect_symbol _upcall_del_stack
-	.ascii	 "\364\364\364\364\364"
-
-	.subsections_via_symbols
-#endif
diff --git a/src/rt/arch/i386/regs.h b/src/rt/arch/i386/regs.h
deleted file mode 100644
index 85c02049edd..00000000000
--- a/src/rt/arch/i386/regs.h
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright 2012 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 file is not used by i386, but we keep it here so all
-// architectures have the same set of header files.
diff --git a/src/rt/arch/i386/sp.h b/src/rt/arch/i386/sp.h
deleted file mode 100644
index 4f4c84c8175..00000000000
--- a/src/rt/arch/i386/sp.h
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright 2012 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.
-
-// Getting the stack pointer and getting/setting sp limit.
-
-#ifndef SP_H
-#define SP_H
-
-#include "../../rust_globals.h"
-
-// Gets a pointer to the vicinity of the current stack pointer
-extern "C" ALWAYS_INLINE uintptr_t get_sp() {
-    uintptr_t sp;
-    asm volatile (
-        "movl %%esp, %0"
-        : "=m"(sp));
-    return sp;
-}
-
-// Gets the pointer to the end of the Rust stack from a platform-
-// specific location in the thread control block
-extern "C" CDECL ALWAYS_INLINE uintptr_t get_sp_limit() {
-    uintptr_t limit;
-
-#if defined(__linux__) || defined(__FreeBSD__)
-    asm volatile (
-        "movl %%gs:48, %0"
-        : "=r"(limit));
-#elif defined(__APPLE__)
-    asm volatile (
-        "movl $0x48+90*4, %%ecx\n\t"
-        "movl %%gs:(%%ecx), %0"
-        :  "=r"(limit)
-        :: "ecx");
-#elif defined(_WIN32)
-    asm volatile (
-        "movl %%fs:0x14, %0"
-        : "=r"(limit));
-#endif
-
-    return limit;
-}
-
-// Records the pointer to the end of the Rust stack in a platform-
-// specific location in the thread control block
-extern "C" CDECL ALWAYS_INLINE void record_sp_limit(void *limit) {
-#if defined(__linux__) || defined(__FreeBSD__)
-    asm volatile (
-        "movl %0, %%gs:48"
-        :: "r"(limit));
-#elif defined(__APPLE__)
-    asm volatile (
-        "movl $0x48+90*4, %%eax\n\t"
-        "movl %0, %%gs:(%%eax)"
-        :: "r"(limit)
-        :  "eax");
-#elif defined(_WIN32)
-    asm volatile (
-        "movl %0, %%fs:0x14"
-        :: "r"(limit));
-#endif
-}
-
-#endif
diff --git a/src/rt/arch/mips/ccall.S b/src/rt/arch/mips/ccall.S
deleted file mode 100644
index cdcdc07db55..00000000000
--- a/src/rt/arch/mips/ccall.S
+++ /dev/null
@@ -1,42 +0,0 @@
-// Mark stack as non-executable
-#if defined(__linux__) && defined(__ELF__)
-.section	.note.GNU-stack, "", @progbits
-#endif
-
-.text
-
-.align 2
-.globl __morestack
-.hidden __morestack
-.cfi_startproc
-.set nomips16
-.ent __morestack
-__morestack:
-        .set noreorder
-        .set nomacro
-
-        addiu $29, $29, -8
-        sw $31, 4($29)
-        sw $30, 0($29)
-
-        .cfi_def_cfa_offset 8
-        .cfi_offset 31, -4
-        .cfi_offset 30, -8
-
-        move $30, $29
-        .cfi_def_cfa_register 30
-
-        move $29, $6
-        move $25, $5
-        jalr $25
-        nop
-        move $29, $30
-
-        lw $30, 0($29)
-        lw $31, 4($29)
-        addiu $29, $29, 8
-
-        jr $31
-        nop
-.end __morestack
-.cfi_endproc
diff --git a/src/rt/arch/mips/context.cpp b/src/rt/arch/mips/context.cpp
deleted file mode 100644
index e1e5776bc1a..00000000000
--- a/src/rt/arch/mips/context.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2012 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.
-
-#include "context.h"
-#include "../../rust_globals.h"
-
-extern "C" void CDECL swap_registers(registers_t *oregs,
-                                     registers_t *regs)
-asm ("swap_registers");
-
-context::context()
-{
-    assert((void*)&regs == (void*)this);
-    memset(&regs, 0, sizeof(regs));
-}
-
-void context::swap(context &out)
-{
-    swap_registers(&out.regs, &regs);
-}
-
-void context::call(void *f, void *arg, void *stack)
-{
-  // Get the current context, which we will then modify to call the
-  // given function.
-  swap(*this);
-
-  // set up the stack
-  uint32_t *sp = (uint32_t *)stack;
-  sp = align_down(sp);
-  // The final return address. 0 indicates the bottom of the stack
-  // sp of mips o32 is 8-byte aligned
-  sp -= 2;
-  *sp = 0;
-
-  regs.data[4] = (uint32_t)arg;
-  regs.data[29] = (uint32_t)sp;
-  regs.data[25] = (uint32_t)f;
-  regs.data[31] = (uint32_t)f;
-
-  // Last base pointer on the stack should be 0
-}
diff --git a/src/rt/arch/mips/context.h b/src/rt/arch/mips/context.h
deleted file mode 100644
index 5e2364437ec..00000000000
--- a/src/rt/arch/mips/context.h
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2012 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.
-
-#ifndef CONTEXT_H
-#define CONTEXT_H
-
-#include <cstdlib>
-#include <inttypes.h>
-#include <stdint.h>
-//#include <xmmintrin.h>
-
-#include "vg/memcheck.h"
-
-template<typename T>
-T align_down(T sp)
-{
-    // There is no platform we care about that needs more than a
-    // 16-byte alignment.
-    return (T)((uint32_t)sp & ~(16 - 1));
-}
-
-// The struct in which we store the saved data.  This is mostly the
-// volatile registers and instruction pointer, but it also includes
-// RCX/RDI which are used to pass arguments.  The indices for each
-// register are found in "regs.h".  Note that the alignment must be
-// 16 bytes so that SSE instructions can be used.
-#include "regs.h"
-struct registers_t {
-    uint32_t data[RUSTRT_MAX];
-} __attribute__((aligned(16)));
-
-class context {
-public:
-    registers_t regs;
-
-    context();
-
-    context *next;
-
-    void swap(context &out);
-    void call(void *f, void *arg, void *sp);
-};
-
-#endif
diff --git a/src/rt/arch/mips/gpr.cpp b/src/rt/arch/mips/gpr.cpp
deleted file mode 100644
index da2f515999f..00000000000
--- a/src/rt/arch/mips/gpr.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2012 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.
-
-#include "gpr.h"
-
-#define LOAD(n) do { \
-    uintptr_t tmp; \
-    asm(".set noat; move %0, $" #n : "=r" (tmp) :); \
-    this->r##n = tmp; \
-} while (0)
-
-void rust_gpr::load() {
-              LOAD(1); LOAD(2); LOAD(3);
-    LOAD(4); LOAD(5); LOAD(6); LOAD(7);
-
-    LOAD(8); LOAD(9); LOAD(10); LOAD(11);
-    LOAD(12); LOAD(13); LOAD(14); LOAD(15);
-
-    LOAD(16); LOAD(17); LOAD(18); LOAD(19);
-    LOAD(20); LOAD(21); LOAD(22); LOAD(23);
-
-    LOAD(24); LOAD(25); LOAD(26); LOAD(27);
-    LOAD(28); LOAD(29); LOAD(30); LOAD(31);
-}
diff --git a/src/rt/arch/mips/gpr.h b/src/rt/arch/mips/gpr.h
deleted file mode 100644
index b48c1d4e732..00000000000
--- a/src/rt/arch/mips/gpr.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2012 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.
-
-#ifndef GPR_H
-#define GPR_H
-
-#include "rust_gpr_base.h"
-
-class rust_gpr : public rust_gpr_base {
-public:
-    uintptr_t r0, r1, r2, r3, r4, r5, r6, r7;
-    uintptr_t r8,  r9, r10, r11, r12, r13, r14, r15;
-    uintptr_t r16, r17, r18, r19, r20, r21, r22, r23;
-    uintptr_t r24, r25, r26, r27, r28, r29, r30, r31;
-
-    inline uintptr_t get_fp() { return r30; }
-    inline uintptr_t get_ip() { return r31; }
-
-    inline void set_fp(uintptr_t new_fp) { r30 = new_fp; }
-    inline void set_ip(uintptr_t new_ip) { r31 = new_ip; }
-
-    void load();
-};
-
-#endif
diff --git a/src/rt/arch/mips/morestack.S b/src/rt/arch/mips/morestack.S
index e534ac05913..9cb6ece80ce 100644
--- a/src/rt/arch/mips/morestack.S
+++ b/src/rt/arch/mips/morestack.S
@@ -3,10 +3,11 @@
 .section        .note.GNU-stack, "", @progbits
 #endif
 
+/* See i386/morestack.S for the lengthy, general explanation. */
+
 .text
 
-.globl upcall_new_stack
-.globl upcall_del_stack
+.globl rust_stack_exhausted
 .globl __morestack
 
 .hidden __morestack
@@ -18,6 +19,10 @@ __morestack:
         .set noreorder
         .set nomacro
 
+        // n.b. most of this is probably unnecessary. I know very little mips
+        //      assembly, and I didn't have anything to test on, so I wasn't
+        //      brave enough to try to trim this down.
+
         addiu $29, $29, -12
         sw $31, 8($29)
         sw $30, 4($29)
@@ -45,53 +50,11 @@ __morestack:
         move $6, $15     // The amount of stack needed
 
         move $28, $23
-        lw $25, %call16(upcall_new_stack)($23)
-        jalr $25
-        nop
-
-        // Pop the saved arguments
-        lw $4, 16($29)
-        lw $5, 20($29)
-        lw $6, 24($29)
-        lw $7, 28($29)
-        addiu $29, $29, 32
-
-        lw $24, 8($30)     // Grab the return pointer.
-        addiu $24, $24, 12 // Skip past the `lw`, `jr`, `addiu` in our parent frame
-        move $29, $2       // Switch to the new stack.
-
-        // for PIC
-        lw $2, 12($30)
-        lw $25, 16($30)
-
-        move $28, $23
-        jalr $24           // Reenter the caller function
-        nop
-
-        // Switch back to the rust stack
-        move $29, $30
-
-        // Save the return value
-        addiu $29, $29, -24
-        sw $2, 16($29)
-        sw $3, 20($29)
-
-        move $28, $23
-        lw $25, %call16(upcall_del_stack)($23)
+        lw $25, %call16(rust_stack_exhausted)($23)
         jalr $25
         nop
 
-        // Restore the return value
-        lw $2, 16($29)
-        lw $3, 20($29)
-        addiu $29, $29, 24
+        // the above function make sure that we never get here
 
-        lw $31, 8($29)
-        lw $30, 4($29)
-        lw $23, 0($29)
-        addiu $29, $29, 12
-
-        jr $31
-        nop
 .end __morestack
 .cfi_endproc
diff --git a/src/rt/arch/mips/record_sp.S b/src/rt/arch/mips/record_sp.S
index a88fefead04..a6dfa04edbb 100644
--- a/src/rt/arch/mips/record_sp.S
+++ b/src/rt/arch/mips/record_sp.S
@@ -38,15 +38,3 @@ get_sp_limit:
         jr $31
         nop
 .end get_sp_limit
-
-.globl get_sp
-.align 2
-.set nomips16
-.ent get_sp
-get_sp:
-        .set noreorder
-        .set nomacro
-        move $2, $29
-        jr $31
-        nop
-.end get_sp
diff --git a/src/rt/arch/mips/regs.h b/src/rt/arch/mips/regs.h
deleted file mode 100644
index 2f38e1507ad..00000000000
--- a/src/rt/arch/mips/regs.h
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2012 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.
-
-#define RUSTRT_MAX  32
-
-// ARG0 is the register in which the first argument goes.
-// Naturally this depends on your operating system.
-#define RUSTRT_ARG0_S r4
-#define RUSTRT_ARG1_S r5
-#define RUSTRT_ARG2_S r6
-#define RUSTRT_ARG3_S r7
diff --git a/src/rt/arch/mips/sp.h b/src/rt/arch/mips/sp.h
deleted file mode 100644
index cd798847607..00000000000
--- a/src/rt/arch/mips/sp.h
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2012 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.
-
-// Getting the stack pointer and getting/setting sp limit.
-
-#ifndef SP_H
-#define SP_H
-
-#include "../../rust_globals.h"
-
-// Gets a pointer to the vicinity of the current stack pointer
-extern "C" uintptr_t get_sp();
-
-// Gets the pointer to the end of the Rust stack from a platform-
-// specific location in the thread control block
-extern "C" CDECL uintptr_t get_sp_limit();
-
-// Records the pointer to the end of the Rust stack in a platform-
-// specific location in the thread control block
-extern "C" CDECL void record_sp_limit(void *limit);
-
-#endif
diff --git a/src/rt/arch/x86_64/_context.S b/src/rt/arch/x86_64/_context.S
index 857fe91c914..a53b1c2d737 100644
--- a/src/rt/arch/x86_64/_context.S
+++ b/src/rt/arch/x86_64/_context.S
@@ -89,12 +89,6 @@ SWAP_REGISTERS:
 #if defined(__MINGW32__) || defined(_WINDOWS)
         mov %rdi, (RUSTRT_RDI*8)(ARG0)
         mov %rsi, (RUSTRT_RSI*8)(ARG0)
-
-        // Save stack range
-        mov %gs:0x08, %r8
-        mov %r8, (RUSTRT_ST1*8)(ARG0)
-        mov %gs:0x10, %r9
-        mov %r9, (RUSTRT_ST2*8)(ARG0)
 #endif
 
         // Save 0th argument register:
@@ -134,12 +128,6 @@ SWAP_REGISTERS:
 #if defined(__MINGW32__) || defined(_WINDOWS)
         mov (RUSTRT_RDI*8)(ARG1), %rdi
         mov (RUSTRT_RSI*8)(ARG1), %rsi
-
-        // Restore stack range
-        mov (RUSTRT_ST1*8)(ARG1), %r8
-        mov %r8, %gs:0x08
-        mov (RUSTRT_ST2*8)(ARG1), %r9
-        mov %r9, %gs:0x10
 #endif
 
         // Restore 0th argument register:
diff --git a/src/rt/arch/x86_64/ccall.S b/src/rt/arch/x86_64/ccall.S
deleted file mode 100644
index dbee5bcdc90..00000000000
--- a/src/rt/arch/x86_64/ccall.S
+++ /dev/null
@@ -1,59 +0,0 @@
-// Mark stack as non-executable
-#if defined(__linux__) && defined(__ELF__)
-.section	.note.GNU-stack, "", @progbits
-#endif
-
-/*
-	The function for switching to the C stack.  It is called
-	__morestack because gdb allows any frame with that name to
-	move the stack pointer to a different stack, which it usually
-	considers an error.
-*/
-
-#include "regs.h"
-
-#define ARG0 RUSTRT_ARG0_S
-#define ARG1 RUSTRT_ARG1_S
-#define ARG2 RUSTRT_ARG2_S
-
-        .text
-
-#if defined(__APPLE__)
-.globl ___morestack
-.private_extern MORESTACK
-___morestack:
-#elif defined(_WIN32)
-.globl __morestack
-__morestack:
-#else
-.globl __morestack
-.hidden __morestack
-__morestack:
-#endif
-
-#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)
-	.cfi_startproc
-#endif
-
-	push %rbp
-#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)
-	.cfi_def_cfa_offset 16
-	.cfi_offset %rbp, -16
-#endif
-
-	mov %rsp,%rbp          // save rsp
-
-#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)
-	.cfi_def_cfa_register %rbp
-#endif
-
-	mov ARG2,%rsp          // switch stack
-	call *ARG1             // invoke target address
-	mov %rbp,%rsp
-	pop %rbp
-
-	ret
-
-#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)
-	.cfi_endproc
-#endif
diff --git a/src/rt/arch/x86_64/context.cpp b/src/rt/arch/x86_64/context.cpp
deleted file mode 100644
index 6a265dff761..00000000000
--- a/src/rt/arch/x86_64/context.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2012 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.
-
-
-#include "context.h"
-#include "../../rust_globals.h"
-
-extern "C" void CDECL swap_registers(registers_t *oregs,
-                                     registers_t *regs);
-
-context::context()
-{
-    assert((void*)&regs == (void*)this);
-}
-
-void context::swap(context &out)
-{
-    swap_registers(&out.regs, &regs);
-}
-
-void context::call(void *f, void *arg, void *stack) {
-  // Get the current context, which we will then modify to call the
-  // given function.
-  swap(*this);
-
-  // set up the stack
-  uint64_t *sp = (uint64_t *)stack;
-  sp = align_down(sp);
-  // The final return address. 0 indicates the bottom of the stack
-  *--sp = 0;
-
-  regs.data[RUSTRT_ARG0] = (uint64_t)arg;
-  regs.data[RUSTRT_RSP] = (uint64_t)sp;
-  regs.data[RUSTRT_IP] = (uint64_t)f;
-
-  // Last base pointer on the stack should be 0
-  regs.data[RUSTRT_RBP] = 0;
-}
diff --git a/src/rt/arch/x86_64/context.h b/src/rt/arch/x86_64/context.h
deleted file mode 100644
index b768a1fb7b0..00000000000
--- a/src/rt/arch/x86_64/context.h
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2012 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.
-
-
-#ifndef CONTEXT_H
-#define CONTEXT_H
-
-#include <cstdlib>
-#include <inttypes.h>
-#include <stdint.h>
-#include <xmmintrin.h>
-
-#include "vg/memcheck.h"
-
-template<typename T>
-T align_down(T sp)
-{
-    // There is no platform we care about that needs more than a
-    // 16-byte alignment.
-    return (T)((uint64_t)sp & ~(16 - 1));
-}
-
-// The struct in which we store the saved data.  This is mostly the
-// volatile registers and instruction pointer, but it also includes
-// RCX/RDI which are used to pass arguments.  The indices for each
-// register are found in "regs.h".  Note that the alignment must be
-// 16 bytes so that SSE instructions can be used.
-#include "regs.h"
-struct registers_t {
-    uint64_t data[RUSTRT_MAX];
-} __attribute__((aligned(16)));
-
-class context {
-public:
-    registers_t regs;
-
-    context();
-
-    context *next;
-
-    void swap(context &out);
-    void call(void *f, void *arg, void *sp);
-};
-
-#endif
diff --git a/src/rt/arch/x86_64/gpr.cpp b/src/rt/arch/x86_64/gpr.cpp
deleted file mode 100644
index 37247d1dfdc..00000000000
--- a/src/rt/arch/x86_64/gpr.cpp
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2012 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.
-
-#include "gpr.h"
-
-#define LOAD(rn) do { \
-    uintptr_t tmp; \
-    asm("movq %%" #rn ",%0" : "=r" (tmp) :); \
-    this->rn = tmp; \
-} while (0)
-
-void rust_gpr::load() {
-    LOAD(rax); LOAD(rbx); LOAD(rcx); LOAD(rdx);
-    LOAD(rsi); LOAD(rdi); LOAD(rbp); LOAD(rsi);
-    LOAD(r8);  LOAD(r9);  LOAD(r10); LOAD(r11);
-    LOAD(r12); LOAD(r13); LOAD(r14); LOAD(r15);
-}
diff --git a/src/rt/arch/x86_64/gpr.h b/src/rt/arch/x86_64/gpr.h
deleted file mode 100644
index 18ef77dbba6..00000000000
--- a/src/rt/arch/x86_64/gpr.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2012 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.
-
-// General-purpose registers. This structure is used during stack crawling.
-
-#ifndef GPR_H
-#define GPR_H
-
-#include "rust_gpr_base.h"
-
-class rust_gpr : public rust_gpr_base {
-public:
-    uintptr_t rax, rbx, rcx, rdx, rsi, rdi, rbp, rip;
-    uintptr_t  r8,  r9, r10, r11, r12, r13, r14, r15;
-
-    inline uintptr_t get_fp() { return rbp; }
-    inline uintptr_t get_ip() { return rip; }
-
-    inline void set_fp(uintptr_t new_fp) { rbp = new_fp; }
-    inline void set_ip(uintptr_t new_ip) { rip = new_ip; }
-
-    void load();
-};
-
-#endif
diff --git a/src/rt/arch/x86_64/morestack.S b/src/rt/arch/x86_64/morestack.S
index b718c9121c5..d248d79d121 100644
--- a/src/rt/arch/x86_64/morestack.S
+++ b/src/rt/arch/x86_64/morestack.S
@@ -3,27 +3,23 @@
 .section	.note.GNU-stack, "", @progbits
 #endif
 
-/*
-	__morestack
-
-	See i386/morestack.S for the lengthy, general explanation.
-*/
+/* See i386/morestack.S for the lengthy, general explanation. */
 
 .text
 
 #if defined(__APPLE__)
-#define UPCALL_NEW_STACK        _upcall_new_stack
-#define UPCALL_DEL_STACK        _upcall_del_stack
 #define MORESTACK               ___morestack
 #else
-#define UPCALL_NEW_STACK        upcall_new_stack
-#define UPCALL_DEL_STACK        upcall_del_stack
 #define MORESTACK               __morestack
 #endif
 
-.globl UPCALL_NEW_STACK
-.globl UPCALL_DEL_STACK
-.globl MORESTACK
+#if defined(__APPLE__)
+#define EXHAUSTED               _rust_stack_exhausted
+#elif defined(__linux__) || defined(__FreeBSD__)
+#define EXHAUSTED               rust_stack_exhausted@PLT
+#else
+#define EXHAUSTED               rust_stack_exhausted
+#endif
 
 #if defined(__linux__) || defined(__FreeBSD__)
 	.hidden MORESTACK
@@ -37,8 +33,7 @@
 	.type MORESTACK,@function
 #endif
 
-
-#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)
+.globl MORESTACK
 MORESTACK:
 	.cfi_startproc
 
@@ -54,77 +49,12 @@ MORESTACK:
 	// Calculate the CFA as on offset from %ebp
 	.cfi_def_cfa_register %rbp
 
-        subq $56, %rsp
-
-	// Save argument registers of the original function
-	movq %rdi,       (%rsp)
-	movq %rsi,      8(%rsp)
-	movq %rdx,     16(%rsp)
-	movq %rcx,     24(%rsp)
-	movq %r8,      32(%rsp)
-	movq %r9,      40(%rsp)
-
-	// Calculate the address of the stack arguments.
-	// We have the base pointer, __morestack's return address,
-	// and __morestack's caller's return address to skip
-	movq %rbp, %rax
-	addq $24, %rax  // Base pointer, return address x2
-
-	// The arguments to __morestack are passed in %r10 & %r11
-
-	movq %r11, %rdx // Size of stack arguments
-	movq %rax, %rsi // Address of stack arguments
-	movq %r10, %rdi // The amount of stack needed
-
-#ifdef __APPLE__
-	call UPCALL_NEW_STACK
-#endif
-#ifdef __linux__
-	call UPCALL_NEW_STACK@PLT
-#endif
-#ifdef __FreeBSD__
-	call UPCALL_NEW_STACK@PLT
-#endif
-
-	// Pop the saved arguments
-	movq      (%rsp), %rdi
-	movq     8(%rsp), %rsi
-	movq    16(%rsp), %rdx
-	movq    24(%rsp), %rcx
-	movq    32(%rsp), %r8
-	movq    40(%rsp), %r9
-
-	addq $56, %rsp
-
-        movq 8(%rbp),%r10       // Grab the return pointer.
-        incq %r10               // Skip past the `ret` in our parent frame
-        movq %rax,%rsp          // Switch to the new stack.
-
-        call *%r10              // Reenter the caller function
-
-	// Switch back to the rust stack
-	movq %rbp, %rsp
-
-	// Save the return value
-	pushq %rax
+        // re-align the stack
+        subq $8, %rsp
 
-#ifdef __APPLE__
-	call UPCALL_DEL_STACK
-#endif
-#ifdef __linux__
-	call UPCALL_DEL_STACK@PLT
-#endif
-#ifdef __FreeBSD__
-	call UPCALL_DEL_STACK@PLT
-#endif
+        // kill this program
+        call EXHAUSTED
 
-	popq %rax // Restore the return value
-	popq %rbp
-	ret
+        // the exhaustion function guarantees that it can't return
 
 	.cfi_endproc
-
-#else
-MORESTACK:
-	ret
-#endif
diff --git a/src/rt/arch/x86_64/regs.h b/src/rt/arch/x86_64/regs.h
index cff47ac378a..25160ca68a6 100644
--- a/src/rt/arch/x86_64/regs.h
+++ b/src/rt/arch/x86_64/regs.h
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+// This is loosely kept in sync with src/libstd/rt/context.rs
+
 #define RUSTRT_RBX   0
 #define RUSTRT_RSP   1
 #define RUSTRT_RBP   2
diff --git a/src/rt/arch/x86_64/sp.h b/src/rt/arch/x86_64/sp.h
deleted file mode 100644
index 764927759fe..00000000000
--- a/src/rt/arch/x86_64/sp.h
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright 2012 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.
-
-// Getting the stack pointer and getting/setting sp limit.
-
-#ifndef SP_H
-#define SP_H
-
-#include "../../rust_globals.h"
-
-// Gets a pointer to the vicinity of the current stack pointer
-extern "C" ALWAYS_INLINE uintptr_t get_sp() {
-    uintptr_t sp;
-    asm volatile (
-        "movq %%rsp, %0"
-        : "=m"(sp));
-    return sp;
-}
-
-// Gets the pointer to the end of the Rust stack from a platform-
-// specific location in the thread control block
-extern "C" CDECL ALWAYS_INLINE uintptr_t get_sp_limit() {
-    uintptr_t limit;
-
-#if defined(__linux__)
-    asm volatile (
-        "movq %%fs:112, %0"
-        : "=r"(limit));
-#elif defined(__APPLE__)
-    asm volatile (
-        "movq $0x60+90*8, %%rsi\n\t"
-        "movq %%gs:(%%rsi), %0"
-        :  "=r"(limit)
-        :: "rsi");
-#elif defined(__FreeBSD__)
-    asm volatile (
-        "movq %%fs:24, %0"
-        : "=r"(limit));
-#elif defined(_WIN64)
-    asm volatile (
-        "movq %%gs:0x28, %0"
-        : "=r"(limit));
-#endif
-
-    return limit;
-}
-
-// Records the pointer to the end of the Rust stack in a platform-
-// specific location in the thread control block
-extern "C" CDECL ALWAYS_INLINE void record_sp_limit(void *limit) {
-#if defined(__linux__)
-    asm volatile (
-        "movq %0, %%fs:112"
-        :: "r"(limit));
-#elif defined(__APPLE__)
-    asm volatile (
-        "movq $0x60+90*8, %%rsi\n\t"
-        "movq %0, %%gs:(%%rsi)"
-        :: "r"(limit)
-        :  "rsi");
-#elif defined(__FreeBSD__)
-    asm volatile (
-        "movq %0, %%fs:24"
-        :: "r"(limit));
-#elif defined(_WIN64)
-    asm volatile (
-        "movq %0, %%gs:0x28"
-        :: "r"(limit));
-#endif
-}
-
-#endif
diff --git a/src/rt/rust_builtin.cpp b/src/rt/rust_builtin.cpp
index 9750e22e945..755235a9138 100644
--- a/src/rt/rust_builtin.cpp
+++ b/src/rt/rust_builtin.cpp
@@ -16,7 +16,6 @@
 #include "memory_region.h"
 #include "boxed_region.h"
 #include "vg/valgrind.h"
-#include "sp.h"
 
 #include <time.h>
 
@@ -340,22 +339,26 @@ rust_unlock_little_lock(lock_and_signal *lock) {
     lock->unlock();
 }
 
+typedef void(startfn)(void*, void*);
+
 class raw_thread: public rust_thread {
 public:
-    fn_env_pair fn;
+    startfn *raw_start;
+    void *rust_fn;
+    void *rust_env;
 
-    raw_thread(fn_env_pair fn) : fn(fn) { }
+    raw_thread(startfn *raw_start, void *rust_fn, void *rust_env)
+        : raw_start(raw_start), rust_fn(rust_fn), rust_env(rust_env) { }
 
     virtual void run() {
-        record_sp_limit(0);
-        fn.f(fn.env, NULL);
+        raw_start(rust_fn, rust_env);
     }
 };
 
 extern "C" raw_thread*
-rust_raw_thread_start(fn_env_pair *fn) {
-    assert(fn);
-    raw_thread *thread = new raw_thread(*fn);
+rust_raw_thread_start(startfn *raw_start, void *rust_start, void *rust_env) {
+    assert(raw_start && rust_start);
+    raw_thread *thread = new raw_thread(raw_start, rust_start, rust_env);
     thread->start();
     return thread;
 }
@@ -552,12 +555,6 @@ rust_get_global_args_ptr() {
     return &global_args_ptr;
 }
 
-// Used by i386 __morestack
-extern "C" CDECL uintptr_t
-rust_get_task() {
-    return 0;
-}
-
 static lock_and_signal env_lock;
 
 extern "C" CDECL void
diff --git a/src/rt/rust_gpr_base.h b/src/rt/rust_gpr_base.h
deleted file mode 100644
index 7ec2dda9cd4..00000000000
--- a/src/rt/rust_gpr_base.h
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2012 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.
-
-// Base class for architecture-specific general-purpose registers. This
-// structure is used during stack crawling.
-
-#ifndef GPR_BASE_H
-#define GPR_BASE_H
-
-#include <stdint.h>
-
-class rust_gpr_base {
-public:
-    // Returns the value of a register by number.
-    inline uintptr_t &get(uint32_t i) {
-        return reinterpret_cast<uintptr_t *>(this)[i];
-    }
-
-    // Sets the value of a register by number.
-    inline void set(uint32_t i, uintptr_t val) {
-        reinterpret_cast<uintptr_t *>(this)[i] = val;
-    }
-};
-
-
-#endif
diff --git a/src/rt/rust_test_helpers.cpp b/src/rt/rust_test_helpers.cpp
index f10a1f36938..3c6e2d68c2f 100644
--- a/src/rt/rust_test_helpers.cpp
+++ b/src/rt/rust_test_helpers.cpp
@@ -11,7 +11,6 @@
 // Helper functions used only in tests
 
 #include "rust_util.h"
-#include "sync/rust_thread.h"
 #include "sync/lock_and_signal.h"
 
 // These functions are used in the unit tests for C ABI calls.
diff --git a/src/rt/rustrt.def.in b/src/rt/rustrt.def.in
index fb9934c7601..aa545c3cdba 100644
--- a/src/rt/rustrt.def.in
+++ b/src/rt/rustrt.def.in
@@ -181,7 +181,6 @@ rust_get_global_args_ptr
 rust_take_global_args_lock
 rust_drop_global_args_lock
 rust_get_test_int
-rust_get_task
 rust_uv_get_loop_from_getaddrinfo_req
 rust_uv_spawn
 rust_uv_process_kill
diff --git a/src/rt/sync/rust_thread.cpp b/src/rt/sync/rust_thread.cpp
index 824642fc435..a78153523d2 100644
--- a/src/rt/sync/rust_thread.cpp
+++ b/src/rt/sync/rust_thread.cpp
@@ -14,11 +14,7 @@
 
 const size_t default_stack_sz = 1024*1024;
 
-rust_thread::rust_thread() : thread(0), stack_sz(default_stack_sz) {
-}
-
-rust_thread::rust_thread(size_t stack_sz)
-  : thread(0), stack_sz(stack_sz) {
+rust_thread::rust_thread() : thread(0) {
 }
 
 rust_thread::~rust_thread() {
@@ -40,10 +36,11 @@ rust_thread_start(void *ptr) {
 void
 rust_thread::start() {
 #if defined(__WIN32__)
-   thread = CreateThread(NULL, stack_sz, rust_thread_start, this, 0, NULL);
+   thread = CreateThread(NULL, default_stack_sz, rust_thread_start, this, 0, NULL);
 #else
    // PTHREAD_STACK_MIN of some system is larger than default size
    // so we check stack_sz to prevent assertion failure.
+   size_t stack_sz = default_stack_sz;
    if (stack_sz < PTHREAD_STACK_MIN) {
       stack_sz = PTHREAD_STACK_MIN;
    }
diff --git a/src/rt/sync/rust_thread.h b/src/rt/sync/rust_thread.h
index 212d237698e..cad87e514b5 100644
--- a/src/rt/sync/rust_thread.h
+++ b/src/rt/sync/rust_thread.h
@@ -23,11 +23,9 @@ class rust_thread {
 #else
     pthread_t thread;
 #endif
-    size_t stack_sz;
  public:
 
     rust_thread();
-    rust_thread(size_t stack_sz);
     virtual ~rust_thread();
 
     void start();
diff --git a/src/test/auxiliary/static_priv_by_default.rs b/src/test/auxiliary/static_priv_by_default.rs
index d46ccf299e8..e6fe8615a96 100644
--- a/src/test/auxiliary/static_priv_by_default.rs
+++ b/src/test/auxiliary/static_priv_by_default.rs
@@ -9,7 +9,6 @@
 // except according to those terms.
 
 #[crate_type = "lib"];
-#[no_std];
 
 static private: int = 0;
 pub static public: int = 0;
diff --git a/src/test/compile-fail/static-priv-by-default.rs b/src/test/compile-fail/static-priv-by-default.rs
index f447a6c547c..24a2b561a8b 100644
--- a/src/test/compile-fail/static-priv-by-default.rs
+++ b/src/test/compile-fail/static-priv-by-default.rs
@@ -10,8 +10,6 @@
 
 // aux-build:static_priv_by_default.rs
 
-#[no_std];
-
 extern mod static_priv_by_default;
 
 mod child {
diff --git a/src/test/compile-fail/xcrate-private-by-default.rs b/src/test/compile-fail/xcrate-private-by-default.rs
index ca1221e7432..8dd05cd00da 100644
--- a/src/test/compile-fail/xcrate-private-by-default.rs
+++ b/src/test/compile-fail/xcrate-private-by-default.rs
@@ -10,14 +10,11 @@
 
 // aux-build:static_priv_by_default.rs
 
-#[no_std]; // helps if debugging resolve
-
 extern mod static_priv_by_default;
 
 fn foo<T>() {}
 
-#[start]
-fn main(_: int, _: **u8) -> int {
+fn main() {
     // Actual public items should be public
     static_priv_by_default::a;
     static_priv_by_default::b;
@@ -49,6 +46,4 @@ fn main(_: int, _: **u8) -> int {
     //~^ ERROR: struct `c` is private
     foo::<static_priv_by_default::foo::d>();
     //~^ ERROR: type `d` is private
-
-    3
 }
diff --git a/src/test/run-pass/no-std-xcrate.rs b/src/test/run-pass/no-std-xcrate.rs
index 104e33b7488..f69f53cecd4 100644
--- a/src/test/run-pass/no-std-xcrate.rs
+++ b/src/test/run-pass/no-std-xcrate.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// xfail-fast
+// xfail-test #9839
 // aux-build:no_std_crate.rs
 
 // This tests that crates which link to std can also be linked to crates with