diff --git a/mk/platform.mk b/mk/platform.mk index 8f9714e62d5b..7ba8f77ef980 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 347c32f17206..8a11d3553f45 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)) $$(JEMALLOC_LIB_$(1)) diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index e28d6a6899c8..c6cc3092c11e 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 9c83f7322045..09dd2f21a3b0 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 1d9f37a2e877..712bf66bdaf2 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 853cc08a0ba7..f4616a8e1837 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(ptr: *mut T, count: int) -> *mut T { use std::sys::size_of; (ptr as int + count * (size_of::() 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 8785dcca7bdb..96a0069e8515 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 5b8406551204..c02e7fe90131 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 0a4622bc65ee..c636a1690370 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 d5278975d8df..1b1e4e7d426a 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(""); + + 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(""); - 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 8b64fda21364..e774b81da350 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 cd89d09ffc0a..ddb104240f2c 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 3350a040f530..000000000000 --- 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 7d90668aad53..000000000000 --- 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*)®s == (void*)this); - memset(®s, 0, sizeof(regs)); -} - -void context::swap(context &out) -{ - swap_registers(&out.regs, ®s); -} - -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 54f0df7de314..000000000000 --- a/src/rt/arch/arm/context.h +++ /dev/null @@ -1,44 +0,0 @@ -// -*- mode: c++ -*- -// xfail-license - -#ifndef CONTEXT_H -#define CONTEXT_H - -#include -#include -#include -//#include - -#include "vg/memcheck.h" - -template -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 77ec9d5182a1..000000000000 --- 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 c8a3e916a371..000000000000 --- 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 f0ec3f4b7a51..219f0962d771 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 3c5c7644bebf..6900444c0fe2 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 0d1c24e0fb74..000000000000 --- 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 cd7988476077..000000000000 --- 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 or the MIT license -// , 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 eeee7a4e715e..000000000000 --- 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 94e6f0418d07..000000000000 --- 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 or the MIT license -// , 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*)®s == (void*)this); -} - -void context::swap(context &out) -{ - swap_registers(&out.regs, ®s); -} - -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)®s.r - (intptr_t)®s); - - 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 33352b4a5562..000000000000 --- 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - - -#ifndef CONTEXT_H -#define CONTEXT_H - -#include -#include -#include - -#include "vg/memcheck.h" - -template -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 e5a59d664b0d..000000000000 --- 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 or the MIT license -// , 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 1953170301c5..000000000000 --- 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 or the MIT license -// , 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 9598f14579f7..25f907f479ca 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 85c02049edd5..000000000000 --- 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 or the MIT license -// , 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 4f4c84c81757..000000000000 --- 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 or the MIT license -// , 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 cdcdc07db555..000000000000 --- 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 e1e5776bc1a6..000000000000 --- 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 or the MIT license -// , 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*)®s == (void*)this); - memset(®s, 0, sizeof(regs)); -} - -void context::swap(context &out) -{ - swap_registers(&out.regs, ®s); -} - -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 5e2364437ec2..000000000000 --- 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#ifndef CONTEXT_H -#define CONTEXT_H - -#include -#include -#include -//#include - -#include "vg/memcheck.h" - -template -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 da2f515999fd..000000000000 --- 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 or the MIT license -// , 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 b48c1d4e732a..000000000000 --- 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 or the MIT license -// , 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 e534ac059133..9cb6ece80ce3 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 a88fefead049..a6dfa04edbbd 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 2f38e1507ad0..000000000000 --- 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 or the MIT license -// , 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 cd7988476077..000000000000 --- 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 or the MIT license -// , 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 857fe91c9141..a53b1c2d7372 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 dbee5bcdc90a..000000000000 --- 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 6a265dff7615..000000000000 --- 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 or the MIT license -// , 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*)®s == (void*)this); -} - -void context::swap(context &out) -{ - swap_registers(&out.regs, ®s); -} - -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 b768a1fb7b08..000000000000 --- 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - - -#ifndef CONTEXT_H -#define CONTEXT_H - -#include -#include -#include -#include - -#include "vg/memcheck.h" - -template -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 37247d1dfdc8..000000000000 --- 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 or the MIT license -// , 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 18ef77dbba63..000000000000 --- 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 or the MIT license -// , 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 b718c9121c57..d248d79d1214 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 cff47ac378af..25160ca68a6b 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 764927759fe6..000000000000 --- 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 or the MIT license -// , 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 9750e22e9453..755235a9138d 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 @@ -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 7ec2dda9cd40..000000000000 --- 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 or the MIT license -// , 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 - -class rust_gpr_base { -public: - // Returns the value of a register by number. - inline uintptr_t &get(uint32_t i) { - return reinterpret_cast(this)[i]; - } - - // Sets the value of a register by number. - inline void set(uint32_t i, uintptr_t val) { - reinterpret_cast(this)[i] = val; - } -}; - - -#endif diff --git a/src/rt/rust_test_helpers.cpp b/src/rt/rust_test_helpers.cpp index f10a1f36938a..3c6e2d68c2fe 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 fb9934c76011..aa545c3cdba0 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 824642fc435d..a78153523d25 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 212d237698e9..cad87e514b5c 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 d46ccf299e82..e6fe8615a962 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 f447a6c547c7..24a2b561a8b0 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 ca1221e7432f..8dd05cd00da2 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() {} -#[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::(); //~^ 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 104e33b7488e..f69f53cecd47 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