(index<- ) ./libstd/rt/stack.rs
git branch: * master 5200215 auto merge of #14035 : alexcrichton/rust/experimental, r=huonw
modified: Fri May 9 13:02:28 2014
1 // Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 //! Rust stack-limit management
12 //!
13 //! Currently Rust uses a segmented-stack-like scheme in order to detect stack
14 //! overflow for rust tasks. In this scheme, the prologue of all functions are
15 //! preceded with a check to see whether the current stack limits are being
16 //! exceeded.
17 //!
18 //! This module provides the functionality necessary in order to manage these
19 //! stack limits (which are stored in platform-specific locations). The
20 //! functions here are used at the borders of the task lifetime in order to
21 //! manage these limits.
22 //!
23 //! This function is an unstable module because this scheme for stack overflow
24 //! detection is not guaranteed to continue in the future. Usage of this module
25 //! is discouraged unless absolutely necessary.
26
27 pub static RED_ZONE: uint = 20 * 1024;
28
29 /// This function is invoked from rust's current __morestack function. Segmented
30 /// stacks are currently not enabled as segmented stacks, but rather one giant
31 /// stack segment. This means that whenever we run out of stack, we want to
32 /// truly consider it to be stack overflow rather than allocating a new stack.
33 #[no_mangle] // - this is called from C code
34 #[no_split_stack] // - it would be sad for this function to trigger __morestack
35 #[doc(hidden)] // - Function must be `pub` to get exported, but it's
36 // irrelevant for documentation purposes.
37 #[cfg(not(test))] // in testing, use the original libstd's version
38 pub extern "C" fn rust_stack_exhausted() {
39 use option::{Option, None, Some};
40 use owned::Box;
41 use rt::local::Local;
42 use rt::task::Task;
43 use str::Str;
44 use intrinsics;
45
46 unsafe {
47 // We're calling this function because the stack just ran out. We need
48 // to call some other rust functions, but if we invoke the functions
49 // right now it'll just trigger this handler being called again. In
50 // order to alleviate this, we move the stack limit to be inside of the
51 // red zone that was allocated for exactly this reason.
52 let limit = get_sp_limit();
53 record_sp_limit(limit - RED_ZONE / 2);
54
55 // This probably isn't the best course of action. Ideally one would want
56 // to unwind the stack here instead of just aborting the entire process.
57 // This is a tricky problem, however. There's a few things which need to
58 // be considered:
59 //
60 // 1. We're here because of a stack overflow, yet unwinding will run
61 // destructors and hence arbitrary code. What if that code overflows
62 // the stack? One possibility is to use the above allocation of an
63 // extra 10k to hope that we don't hit the limit, and if we do then
64 // abort the whole program. Not the best, but kind of hard to deal
65 // with unless we want to switch stacks.
66 //
67 // 2. LLVM will optimize functions based on whether they can unwind or
68 // not. It will flag functions with 'nounwind' if it believes that
69 // the function cannot trigger unwinding, but if we do unwind on
70 // stack overflow then it means that we could unwind in any function
71 // anywhere. We would have to make sure that LLVM only places the
72 // nounwind flag on functions which don't call any other functions.
73 //
74 // 3. The function that overflowed may have owned arguments. These
75 // arguments need to have their destructors run, but we haven't even
76 // begun executing the function yet, so unwinding will not run the
77 // any landing pads for these functions. If this is ignored, then
78 // the arguments will just be leaked.
79 //
80 // Exactly what to do here is a very delicate topic, and is possibly
81 // still up in the air for what exactly to do. Some relevant issues:
82 //
83 // #3555 - out-of-stack failure leaks arguments
84 // #3695 - should there be a stack limit?
85 // #9855 - possible strategies which could be taken
86 // #9854 - unwinding on windows through __morestack has never worked
87 // #2361 - possible implementation of not using landing pads
88
89 let task: Option<Box<Task>> = Local::try_take();
90 let name = match task {
91 Some(ref task) => {
92 task.name.as_ref().map(|n| n.as_slice())
93 }
94 None => None
95 };
96 let name = name.unwrap_or("<unknown>");
97
98 // See the message below for why this is not emitted to the
99 // task's logger. This has the additional conundrum of the
100 // logger may not be initialized just yet, meaning that an FFI
101 // call would happen to initialized it (calling out to libuv),
102 // and the FFI call needs 2MB of stack when we just ran out.
103 rterrln!("task '{}' has overflowed its stack", name);
104
105 intrinsics::abort();
106 }
107 }
108
109 #[inline(always)]
110 pub unsafe fn record_stack_bounds(stack_lo: uint, stack_hi: uint) {
111 // When the old runtime had segmented stacks, it used a calculation that was
112 // "limit + RED_ZONE + FUDGE". The red zone was for things like dynamic
113 // symbol resolution, llvm function calls, etc. In theory this red zone
114 // value is 0, but it matters far less when we have gigantic stacks because
115 // we don't need to be so exact about our stack budget. The "fudge factor"
116 // was because LLVM doesn't emit a stack check for functions < 256 bytes in
117 // size. Again though, we have giant stacks, so we round all these
118 // calculations up to the nice round number of 20k.
119 record_sp_limit(stack_lo + RED_ZONE);
120
121 return target_record_stack_bounds(stack_lo, stack_hi);
122
123 #[cfg(not(windows))] #[cfg(not(target_arch = "x86_64"))] #[inline(always)]
124 unsafe fn target_record_stack_bounds(_stack_lo: uint, _stack_hi: uint) {}
125 #[cfg(windows, target_arch = "x86_64")] #[inline(always)]
126 unsafe fn target_record_stack_bounds(stack_lo: uint, stack_hi: uint) {
127 // Windows compiles C functions which may check the stack bounds. This
128 // means that if we want to perform valid FFI on windows, then we need
129 // to ensure that the stack bounds are what they truly are for this
130 // task. More info can be found at:
131 // https://github.com/mozilla/rust/issues/3445#issuecomment-26114839
132 //
133 // stack range is at TIB: %gs:0x08 (top) and %gs:0x10 (bottom)
134 asm!("mov $0, %gs:0x08" :: "r"(stack_hi) :: "volatile");
135 asm!("mov $0, %gs:0x10" :: "r"(stack_lo) :: "volatile");
136 }
137 }
138
139 /// Records the current limit of the stack as specified by `end`.
140 ///
141 /// This is stored in an OS-dependent location, likely inside of the thread
142 /// local storage. The location that the limit is stored is a pre-ordained
143 /// location because it's where LLVM has emitted code to check.
144 ///
145 /// Note that this cannot be called under normal circumstances. This function is
146 /// changing the stack limit, so upon returning any further function calls will
147 /// possibly be triggering the morestack logic if you're not careful.
148 ///
149 /// Also note that this and all of the inside functions are all flagged as
150 /// "inline(always)" because they're messing around with the stack limits. This
151 /// would be unfortunate for the functions themselves to trigger a morestack
152 /// invocation (if they were an actual function call).
153 #[inline(always)]
154 pub unsafe fn record_sp_limit(limit: uint) {
155 return target_record_sp_limit(limit);
156
157 // x86-64
158 #[cfg(target_arch = "x86_64", target_os = "macos")] #[inline(always)]
159 unsafe fn target_record_sp_limit(limit: uint) {
160 asm!("movq $$0x60+90*8, %rsi
161 movq $0, %gs:(%rsi)" :: "r"(limit) : "rsi" : "volatile")
162 }
163 #[cfg(target_arch = "x86_64", target_os = "linux")] #[inline(always)]
164 unsafe fn target_record_sp_limit(limit: uint) {
165 asm!("movq $0, %fs:112" :: "r"(limit) :: "volatile")
166 }
167 #[cfg(target_arch = "x86_64", target_os = "win32")] #[inline(always)]
168 unsafe fn target_record_sp_limit(limit: uint) {
169 // see: http://en.wikipedia.org/wiki/Win32_Thread_Information_Block
170 // store this inside of the "arbitrary data slot", but double the size
171 // because this is 64 bit instead of 32 bit
172 asm!("movq $0, %gs:0x28" :: "r"(limit) :: "volatile")
173 }
174 #[cfg(target_arch = "x86_64", target_os = "freebsd")] #[inline(always)]
175 unsafe fn target_record_sp_limit(limit: uint) {
176 asm!("movq $0, %fs:24" :: "r"(limit) :: "volatile")
177 }
178
179 // x86
180 #[cfg(target_arch = "x86", target_os = "macos")] #[inline(always)]
181 unsafe fn target_record_sp_limit(limit: uint) {
182 asm!("movl $$0x48+90*4, %eax
183 movl $0, %gs:(%eax)" :: "r"(limit) : "eax" : "volatile")
184 }
185 #[cfg(target_arch = "x86", target_os = "linux")]
186 #[cfg(target_arch = "x86", target_os = "freebsd")] #[inline(always)]
187 unsafe fn target_record_sp_limit(limit: uint) {
188 asm!("movl $0, %gs:48" :: "r"(limit) :: "volatile")
189 }
190 #[cfg(target_arch = "x86", target_os = "win32")] #[inline(always)]
191 unsafe fn target_record_sp_limit(limit: uint) {
192 // see: http://en.wikipedia.org/wiki/Win32_Thread_Information_Block
193 // store this inside of the "arbitrary data slot"
194 asm!("movl $0, %fs:0x14" :: "r"(limit) :: "volatile")
195 }
196
197 // mips, arm - Some brave soul can port these to inline asm, but it's over
198 // my head personally
199 #[cfg(target_arch = "mips")]
200 #[cfg(target_arch = "arm")] #[inline(always)]
201 unsafe fn target_record_sp_limit(limit: uint) {
202 use libc::c_void;
203 return record_sp_limit(limit as *c_void);
204 extern {
205 fn record_sp_limit(limit: *c_void);
206 }
207 }
208 }
209
210 /// The counterpart of the function above, this function will fetch the current
211 /// stack limit stored in TLS.
212 ///
213 /// Note that all of these functions are meant to be exact counterparts of their
214 /// brethren above, except that the operands are reversed.
215 ///
216 /// As with the setter, this function does not have a __morestack header and can
217 /// therefore be called in a "we're out of stack" situation.
218 #[inline(always)]
219 pub unsafe fn get_sp_limit() -> uint {
220 return target_get_sp_limit();
221
222 // x86-64
223 #[cfg(target_arch = "x86_64", target_os = "macos")] #[inline(always)]
224 unsafe fn target_get_sp_limit() -> uint {
225 let limit;
226 asm!("movq $$0x60+90*8, %rsi
227 movq %gs:(%rsi), $0" : "=r"(limit) :: "rsi" : "volatile");
228 return limit;
229 }
230 #[cfg(target_arch = "x86_64", target_os = "linux")] #[inline(always)]
231 unsafe fn target_get_sp_limit() -> uint {
232 let limit;
233 asm!("movq %fs:112, $0" : "=r"(limit) ::: "volatile");
234 return limit;
235 }
236 #[cfg(target_arch = "x86_64", target_os = "win32")] #[inline(always)]
237 unsafe fn target_get_sp_limit() -> uint {
238 let limit;
239 asm!("movq %gs:0x28, $0" : "=r"(limit) ::: "volatile");
240 return limit;
241 }
242 #[cfg(target_arch = "x86_64", target_os = "freebsd")] #[inline(always)]
243 unsafe fn target_get_sp_limit() -> uint {
244 let limit;
245 asm!("movq %fs:24, $0" : "=r"(limit) ::: "volatile");
246 return limit;
247 }
248
249 // x86
250 #[cfg(target_arch = "x86", target_os = "macos")] #[inline(always)]
251 unsafe fn target_get_sp_limit() -> uint {
252 let limit;
253 asm!("movl $$0x48+90*4, %eax
254 movl %gs:(%eax), $0" : "=r"(limit) :: "eax" : "volatile");
255 return limit;
256 }
257 #[cfg(target_arch = "x86", target_os = "linux")]
258 #[cfg(target_arch = "x86", target_os = "freebsd")] #[inline(always)]
259 unsafe fn target_get_sp_limit() -> uint {
260 let limit;
261 asm!("movl %gs:48, $0" : "=r"(limit) ::: "volatile");
262 return limit;
263 }
264 #[cfg(target_arch = "x86", target_os = "win32")] #[inline(always)]
265 unsafe fn target_get_sp_limit() -> uint {
266 let limit;
267 asm!("movl %fs:0x14, $0" : "=r"(limit) ::: "volatile");
268 return limit;
269 }
270
271 // mips, arm - Some brave soul can port these to inline asm, but it's over
272 // my head personally
273 #[cfg(target_arch = "mips")]
274 #[cfg(target_arch = "arm")] #[inline(always)]
275 unsafe fn target_get_sp_limit() -> uint {
276 use libc::c_void;
277 return get_sp_limit() as uint;
278 extern {
279 fn get_sp_limit() -> *c_void;
280 }
281 }
282 }