From 9a738fd61d3006796d518ba751f5bb632f65edb6 Mon Sep 17 00:00:00 2001 From: Brian Anderson <banderson@mozilla.com> Date: Tue, 6 Dec 2011 16:26:47 -0800 Subject: [PATCH] rt: Various tweaks to make __morestack unwinding work on linux When unwinding through __morestack the stack limit in the TLS is invalidated and must be reset. Instead of actually landing at __morestack we're just going to make all our Rust landing pads call upcall_reset_stack_limit, which will find the stack segment that corresponds to the current stack pointer and put the limit in the TLS. Also massively expand the stack segment red zone to make more room for the dynamic linker. Will fix in the future. --- src/comp/back/upcall.rs | 6 ++++-- src/comp/middle/trans.rs | 5 +++++ src/rt/arch/i386/record_sp.S | 7 +++++++ src/rt/arch/x86_64/morestack.S | 4 ++++ src/rt/arch/x86_64/record_sp.S | 7 +++++++ src/rt/rust_task.cpp | 27 +++++++++++++++++++++++++-- src/rt/rust_task.h | 1 + src/rt/rust_upcall.cpp | 9 +++++++++ src/rt/rustrt.def.in | 1 + src/test/run-fail/morestack3.rs | 4 ++-- 10 files changed, 65 insertions(+), 6 deletions(-) diff --git a/src/comp/back/upcall.rs b/src/comp/back/upcall.rs index 419a7fdc34c..71f33ccb372 100644 --- a/src/comp/back/upcall.rs +++ b/src/comp/back/upcall.rs @@ -27,7 +27,8 @@ type upcalls = dynastack_free: ValueRef, alloc_c_stack: ValueRef, call_shim_on_c_stack: ValueRef, - rust_personality: ValueRef}; + rust_personality: ValueRef, + reset_stack_limit: ValueRef}; fn declare_upcalls(targ_cfg: @session::config, _tn: type_names, @@ -89,7 +90,8 @@ fn declare_upcalls(targ_cfg: @session::config, // arguments: void *args, void *fn_ptr [T_ptr(T_i8()), T_ptr(T_i8())], int_t), - rust_personality: d("rust_personality", [], T_i32()) + rust_personality: d("rust_personality", [], T_i32()), + reset_stack_limit: dv("reset_stack_limit", []) }; } // diff --git a/src/comp/middle/trans.rs b/src/comp/middle/trans.rs index 6804434bb4c..41df1b28edd 100644 --- a/src/comp/middle/trans.rs +++ b/src/comp/middle/trans.rs @@ -3904,6 +3904,11 @@ fn trans_landing_pad(bcx: @block_ctxt, // The landing pad block is a cleanup SetCleanup(bcx, llpad); + // Because we may have unwound across a stack boundary, we must call into + // the runtime to figure out which stack segment we are on and place the + // stack limit back into the TLS. + Call(bcx, bcx_ccx(bcx).upcalls.reset_stack_limit, []); + // FIXME: This seems like a very naive and redundant way to generate the // landing pads, as we're re-generating all in-scope cleanups for each // function call. Probably good optimization opportunities here. diff --git a/src/rt/arch/i386/record_sp.S b/src/rt/arch/i386/record_sp.S index 153225e8187..3f299de5d15 100644 --- a/src/rt/arch/i386/record_sp.S +++ b/src/rt/arch/i386/record_sp.S @@ -2,11 +2,14 @@ #if defined(__APPLE__) || defined(_WIN32) #define RECORD_SP _record_sp +#define GET_SP _get_sp #else #define RECORD_SP record_sp +#define GET_SP get_sp #endif .globl RECORD_SP +.globl GET_SP #if defined(__linux__) RECORD_SP: @@ -25,3 +28,7 @@ RECORD_SP: ret #endif #endif + +GET_SP: + movl %esp, %eax + ret \ No newline at end of file diff --git a/src/rt/arch/x86_64/morestack.S b/src/rt/arch/x86_64/morestack.S index ef0edcee720..535b6190f1c 100644 --- a/src/rt/arch/x86_64/morestack.S +++ b/src/rt/arch/x86_64/morestack.S @@ -132,6 +132,10 @@ MORESTACK: addq $8, %rsp popq %rbp +#ifdef __linux__ + .cfi_restore %rbp + .cfi_def_cfa %rsp, 8 +#endif ret #if defined(__ELF__) diff --git a/src/rt/arch/x86_64/record_sp.S b/src/rt/arch/x86_64/record_sp.S index e8284c5fac9..415f6685655 100644 --- a/src/rt/arch/x86_64/record_sp.S +++ b/src/rt/arch/x86_64/record_sp.S @@ -2,11 +2,14 @@ #if defined(__APPLE__) || defined(_WIN32) #define RECORD_SP _record_sp +#define GET_SP _get_sp #else #define RECORD_SP record_sp +#define GET_SP get_sp #endif .globl RECORD_SP +.globl GET_SP #if defined(__linux__) RECORD_SP: @@ -23,3 +26,7 @@ RECORD_SP: ret #endif #endif + +GET_SP: + movq %rsp, %rax + ret diff --git a/src/rt/rust_task.cpp b/src/rt/rust_task.cpp index b540225243f..888c9ac701a 100644 --- a/src/rt/rust_task.cpp +++ b/src/rt/rust_task.cpp @@ -18,11 +18,11 @@ // to the rt, compiler and dynamic linker for running small functions // FIXME: We want this to be 128 but need to slim the red zone calls down #ifdef __i386__ -#define RED_ZONE_SIZE 2048 +#define RED_ZONE_SIZE 65536 #endif #ifdef __x86_64__ -#define RED_ZONE_SIZE 2048 +#define RED_ZONE_SIZE 65536 #endif // Stack size @@ -613,6 +613,29 @@ rust_task::record_stack_limit() { "Stack size must be greater than LIMIT_OFFSET"); record_sp(stk->data + LIMIT_OFFSET + RED_ZONE_SIZE); } + +extern "C" uintptr_t get_sp(); + +/* +Called by landing pads during unwinding to figure out which +stack segment we are currently running on, delete the others, +and record the stack limit (which was not restored when unwinding +through __morestack). + */ +void +rust_task::reset_stack_limit() { + uintptr_t sp = get_sp(); + // Not positive these bounds for sp are correct. + // I think that the first possible value for esp on a new + // stack is stk->limit, which points one word in front of + // the first work to be pushed onto a new stack. + while (sp <= (uintptr_t)stk->data || stk->limit < sp) { + del_stk(this, stk); + A(sched, stk != NULL, "Failed to find the current stack"); + } + record_stack_limit(); +} + // // Local Variables: // mode: C++ diff --git a/src/rt/rust_task.h b/src/rt/rust_task.h index 00a9fc277dc..47472801510 100644 --- a/src/rt/rust_task.h +++ b/src/rt/rust_task.h @@ -200,6 +200,7 @@ rust_task : public kernel_owned<rust_task>, rust_cond void *new_stack(size_t stk_sz, void *args_addr, size_t args_sz); void del_stack(); void record_stack_limit(); + void reset_stack_limit(); }; // diff --git a/src/rt/rust_upcall.cpp b/src/rt/rust_upcall.cpp index 653db471fc0..5a4c37c500c 100644 --- a/src/rt/rust_upcall.cpp +++ b/src/rt/rust_upcall.cpp @@ -259,6 +259,15 @@ upcall_del_stack() { task->del_stack(); } +// Landing pads need to call this to insert the +// correct limit into TLS. +// NB: This must be called on the Rust stack +extern "C" CDECL void +upcall_reset_stack_limit() { + rust_task *task = rust_scheduler::get_task(); + task->reset_stack_limit(); +} + extern "C" _Unwind_Reason_Code __gxx_personality_v0(int version, _Unwind_Action actions, diff --git a/src/rt/rustrt.def.in b/src/rt/rustrt.def.in index ba1a9c1403d..306c750c8fb 100644 --- a/src/rt/rustrt.def.in +++ b/src/rt/rustrt.def.in @@ -69,6 +69,7 @@ upcall_vec_push upcall_call_shim_on_c_stack upcall_new_stack upcall_del_stack +upcall_reset_stack_limit asm_call_on_stack rust_uv_default_loop rust_uv_loop_new diff --git a/src/test/run-fail/morestack3.rs b/src/test/run-fail/morestack3.rs index 9fdd0326b66..9796017a8eb 100644 --- a/src/test/run-fail/morestack3.rs +++ b/src/test/run-fail/morestack3.rs @@ -25,10 +25,10 @@ resource and_then_get_big_again(_i: @int) { getbig(i - 1); } } - getbig(1000); + getbig(100); } fn main() { rustrt::set_min_stack(256u); - std::task::spawn(1000, getbig_and_fail); + std::task::spawn(100, getbig_and_fail); } \ No newline at end of file