Use __morestack to detect stack overflow

This commit resumes management of the stack boundaries and limits when switching
between tasks. This additionally leverages the __morestack function to run code
on "stack overflow". The current behavior is to abort the process, but this is
probably not the best behavior in the long term (for deails, see the comment I
wrote up in the stack exhaustion routine).
This commit is contained in:
Alex Crichton 2013-10-17 01:40:33 -07:00
parent d773a024a2
commit 6d8330afb6
57 changed files with 500 additions and 1551 deletions

View File

@ -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

View File

@ -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))

View File

@ -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() {

View File

@ -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,

View File

@ -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);

View File

@ -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 std::sys::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;
}
}
}

View File

@ -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> {

View File

@ -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() {

View File

@ -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

View File

@ -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();

View File

@ -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);
}

View File

@ -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]

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -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
bl rust_stack_exhausted@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
// Return
pop {r6, fp, lr}
mov pc, lr
// the above function ensures that it never returns
.fnend

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -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
// re-align the stack
subl $12,%esp
calll EXHAUSTED
// the exhaustion function guarantees that it can't return
// 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
// 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

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -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)
lw $25, %call16(rust_stack_exhausted)($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
// the above function make sure that we never get here
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)
jalr $25
nop
// Restore the return value
lw $2, 16($29)
lw $3, 20($29)
addiu $29, $29, 24
lw $31, 8($29)
lw $30, 4($29)
lw $23, 0($29)
addiu $29, $29, 12
jr $31
nop
.end __morestack
.cfi_endproc

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -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
// re-align the stack
subq $8, %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)
// kill this program
call EXHAUSTED
// 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
#ifdef __APPLE__
call UPCALL_DEL_STACK
#endif
#ifdef __linux__
call UPCALL_DEL_STACK@PLT
#endif
#ifdef __FreeBSD__
call UPCALL_DEL_STACK@PLT
#endif
popq %rax // Restore the return value
popq %rbp
ret
// the exhaustion function guarantees that it can't return
.cfi_endproc
#else
MORESTACK:
ret
#endif

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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;
}

View File

@ -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();

View File

@ -9,7 +9,6 @@
// except according to those terms.
#[crate_type = "lib"];
#[no_std];
static private: int = 0;
pub static public: int = 0;

View File

@ -10,8 +10,6 @@
// aux-build:static_priv_by_default.rs
#[no_std];
extern mod static_priv_by_default;
mod child {

View File

@ -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
}

View File

@ -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