]> git.lizzy.rs Git - rust.git/blob - src/libstd/sys/common/stack.rs
Rewrite Condvar::wait_timeout and make it public
[rust.git] / src / libstd / sys / common / stack.rs
1 // Copyright 2013-2015 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 // iOS related notes
28 //
29 // It is possible to implement it using idea from
30 // http://www.opensource.apple.com/source/Libc/Libc-825.40.1/pthreads/pthread_machdep.h
31 //
32 // In short: _pthread_{get,set}_specific_direct allows extremely fast
33 // access, exactly what is required for segmented stack
34 // There is a pool of reserved slots for Apple internal use (0..119)
35 // First dynamic allocated pthread key starts with 257 (on iOS7)
36 // So using slot 149 should be pretty safe ASSUMING space is reserved
37 // for every key < first dynamic key
38 //
39 // There is also an opportunity to steal keys reserved for Garbage Collection
40 // ranges 80..89 and 110..119, especially considering the fact Garbage Collection
41 // never supposed to work on iOS. But as everybody knows it - there is a chance
42 // that those slots will be re-used, like it happened with key 95 (moved from
43 // JavaScriptCore to CoreText)
44 //
45 // Unfortunately Apple rejected patch to LLVM which generated
46 // corresponding prolog, decision was taken to disable segmented
47 // stack support on iOS.
48
49 pub const RED_ZONE: uint = 20 * 1024;
50
51 /// This function is invoked from rust's current __morestack function. Segmented
52 /// stacks are currently not enabled as segmented stacks, but rather one giant
53 /// stack segment. This means that whenever we run out of stack, we want to
54 /// truly consider it to be stack overflow rather than allocating a new stack.
55 #[cfg(not(test))] // in testing, use the original libstd's version
56 #[lang = "stack_exhausted"]
57 extern fn stack_exhausted() {
58     use intrinsics;
59
60     unsafe {
61         // We're calling this function because the stack just ran out. We need
62         // to call some other rust functions, but if we invoke the functions
63         // right now it'll just trigger this handler being called again. In
64         // order to alleviate this, we move the stack limit to be inside of the
65         // red zone that was allocated for exactly this reason.
66         let limit = get_sp_limit();
67         record_sp_limit(limit - RED_ZONE / 2);
68
69         // This probably isn't the best course of action. Ideally one would want
70         // to unwind the stack here instead of just aborting the entire process.
71         // This is a tricky problem, however. There's a few things which need to
72         // be considered:
73         //
74         //  1. We're here because of a stack overflow, yet unwinding will run
75         //     destructors and hence arbitrary code. What if that code overflows
76         //     the stack? One possibility is to use the above allocation of an
77         //     extra 10k to hope that we don't hit the limit, and if we do then
78         //     abort the whole program. Not the best, but kind of hard to deal
79         //     with unless we want to switch stacks.
80         //
81         //  2. LLVM will optimize functions based on whether they can unwind or
82         //     not. It will flag functions with 'nounwind' if it believes that
83         //     the function cannot trigger unwinding, but if we do unwind on
84         //     stack overflow then it means that we could unwind in any function
85         //     anywhere. We would have to make sure that LLVM only places the
86         //     nounwind flag on functions which don't call any other functions.
87         //
88         //  3. The function that overflowed may have owned arguments. These
89         //     arguments need to have their destructors run, but we haven't even
90         //     begun executing the function yet, so unwinding will not run the
91         //     any landing pads for these functions. If this is ignored, then
92         //     the arguments will just be leaked.
93         //
94         // Exactly what to do here is a very delicate topic, and is possibly
95         // still up in the air for what exactly to do. Some relevant issues:
96         //
97         //  #3555 - out-of-stack failure leaks arguments
98         //  #3695 - should there be a stack limit?
99         //  #9855 - possible strategies which could be taken
100         //  #9854 - unwinding on windows through __morestack has never worked
101         //  #2361 - possible implementation of not using landing pads
102
103         ::rt::util::report_overflow();
104
105         intrinsics::abort();
106     }
107 }
108
109 // Windows maintains a record of upper and lower stack bounds in the Thread Information
110 // Block (TIB), and some syscalls do check that addresses which are supposed to be in
111 // the stack, indeed lie between these two values.
112 // (See https://github.com/rust-lang/rust/issues/3445#issuecomment-26114839)
113 //
114 // When using Rust-managed stacks (libgreen), we must maintain these values accordingly.
115 // For OS-managed stacks (libnative), we let the OS manage them for us.
116 //
117 // On all other platforms both variants behave identically.
118
119 #[inline(always)]
120 pub unsafe fn record_os_managed_stack_bounds(stack_lo: uint, _stack_hi: uint) {
121     record_sp_limit(stack_lo + RED_ZONE);
122 }
123
124 #[inline(always)]
125 pub unsafe fn record_rust_managed_stack_bounds(stack_lo: uint, stack_hi: uint) {
126     // When the old runtime had segmented stacks, it used a calculation that was
127     // "limit + RED_ZONE + FUDGE". The red zone was for things like dynamic
128     // symbol resolution, llvm function calls, etc. In theory this red zone
129     // value is 0, but it matters far less when we have gigantic stacks because
130     // we don't need to be so exact about our stack budget. The "fudge factor"
131     // was because LLVM doesn't emit a stack check for functions < 256 bytes in
132     // size. Again though, we have giant stacks, so we round all these
133     // calculations up to the nice round number of 20k.
134     record_sp_limit(stack_lo + RED_ZONE);
135
136     return target_record_stack_bounds(stack_lo, stack_hi);
137
138     #[cfg(not(windows))] #[inline(always)]
139     unsafe fn target_record_stack_bounds(_stack_lo: uint, _stack_hi: uint) {}
140
141     #[cfg(all(windows, target_arch = "x86"))] #[inline(always)]
142     unsafe fn target_record_stack_bounds(stack_lo: uint, stack_hi: uint) {
143         // stack range is at TIB: %fs:0x04 (top) and %fs:0x08 (bottom)
144         asm!("mov $0, %fs:0x04" :: "r"(stack_hi) :: "volatile");
145         asm!("mov $0, %fs:0x08" :: "r"(stack_lo) :: "volatile");
146     }
147     #[cfg(all(windows, target_arch = "x86_64"))] #[inline(always)]
148     unsafe fn target_record_stack_bounds(stack_lo: uint, stack_hi: uint) {
149         // stack range is at TIB: %gs:0x08 (top) and %gs:0x10 (bottom)
150         asm!("mov $0, %gs:0x08" :: "r"(stack_hi) :: "volatile");
151         asm!("mov $0, %gs:0x10" :: "r"(stack_lo) :: "volatile");
152     }
153 }
154
155 /// Records the current limit of the stack as specified by `end`.
156 ///
157 /// This is stored in an OS-dependent location, likely inside of the thread
158 /// local storage. The location that the limit is stored is a pre-ordained
159 /// location because it's where LLVM has emitted code to check.
160 ///
161 /// Note that this cannot be called under normal circumstances. This function is
162 /// changing the stack limit, so upon returning any further function calls will
163 /// possibly be triggering the morestack logic if you're not careful.
164 ///
165 /// Also note that this and all of the inside functions are all flagged as
166 /// "inline(always)" because they're messing around with the stack limits.  This
167 /// would be unfortunate for the functions themselves to trigger a morestack
168 /// invocation (if they were an actual function call).
169 #[inline(always)]
170 pub unsafe fn record_sp_limit(limit: uint) {
171     return target_record_sp_limit(limit);
172
173     // x86-64
174     #[cfg(all(target_arch = "x86_64",
175               any(target_os = "macos", target_os = "ios")))]
176     #[inline(always)]
177     unsafe fn target_record_sp_limit(limit: uint) {
178         asm!("movq $$0x60+90*8, %rsi
179               movq $0, %gs:(%rsi)" :: "r"(limit) : "rsi" : "volatile")
180     }
181     #[cfg(all(target_arch = "x86_64", target_os = "linux"))] #[inline(always)]
182     unsafe fn target_record_sp_limit(limit: uint) {
183         asm!("movq $0, %fs:112" :: "r"(limit) :: "volatile")
184     }
185     #[cfg(all(target_arch = "x86_64", target_os = "windows"))] #[inline(always)]
186     unsafe fn target_record_sp_limit(_: uint) {
187     }
188     #[cfg(all(target_arch = "x86_64", target_os = "freebsd"))] #[inline(always)]
189     unsafe fn target_record_sp_limit(limit: uint) {
190         asm!("movq $0, %fs:24" :: "r"(limit) :: "volatile")
191     }
192     #[cfg(all(target_arch = "x86_64", target_os = "dragonfly"))] #[inline(always)]
193     unsafe fn target_record_sp_limit(limit: uint) {
194         asm!("movq $0, %fs:32" :: "r"(limit) :: "volatile")
195     }
196
197     // x86
198     #[cfg(all(target_arch = "x86",
199               any(target_os = "macos", target_os = "ios")))]
200     #[inline(always)]
201     unsafe fn target_record_sp_limit(limit: uint) {
202         asm!("movl $$0x48+90*4, %eax
203               movl $0, %gs:(%eax)" :: "r"(limit) : "eax" : "volatile")
204     }
205     #[cfg(all(target_arch = "x86",
206               any(target_os = "linux", target_os = "freebsd")))]
207     #[inline(always)]
208     unsafe fn target_record_sp_limit(limit: uint) {
209         asm!("movl $0, %gs:48" :: "r"(limit) :: "volatile")
210     }
211     #[cfg(all(target_arch = "x86", target_os = "windows"))] #[inline(always)]
212     unsafe fn target_record_sp_limit(_: uint) {
213     }
214
215     // mips, arm - Some brave soul can port these to inline asm, but it's over
216     //             my head personally
217     #[cfg(any(target_arch = "mips",
218               target_arch = "mipsel",
219               all(target_arch = "arm", not(target_os = "ios"))))]
220     #[inline(always)]
221     unsafe fn target_record_sp_limit(limit: uint) {
222         use libc::c_void;
223         return record_sp_limit(limit as *const c_void);
224         extern {
225             fn record_sp_limit(limit: *const c_void);
226         }
227     }
228
229     // aarch64 - FIXME(AARCH64): missing...
230     #[cfg(target_arch = "aarch64")]
231     unsafe fn target_record_sp_limit(_: uint) {
232     }
233
234     // iOS segmented stack is disabled for now, see related notes
235     #[cfg(all(target_arch = "arm", target_os = "ios"))] #[inline(always)]
236     unsafe fn target_record_sp_limit(_: uint) {
237     }
238 }
239
240 /// The counterpart of the function above, this function will fetch the current
241 /// stack limit stored in TLS.
242 ///
243 /// Note that all of these functions are meant to be exact counterparts of their
244 /// brethren above, except that the operands are reversed.
245 ///
246 /// As with the setter, this function does not have a __morestack header and can
247 /// therefore be called in a "we're out of stack" situation.
248 #[inline(always)]
249 pub unsafe fn get_sp_limit() -> uint {
250     return target_get_sp_limit();
251
252     // x86-64
253     #[cfg(all(target_arch = "x86_64",
254               any(target_os = "macos", target_os = "ios")))]
255     #[inline(always)]
256     unsafe fn target_get_sp_limit() -> uint {
257         let limit;
258         asm!("movq $$0x60+90*8, %rsi
259               movq %gs:(%rsi), $0" : "=r"(limit) :: "rsi" : "volatile");
260         return limit;
261     }
262     #[cfg(all(target_arch = "x86_64", target_os = "linux"))] #[inline(always)]
263     unsafe fn target_get_sp_limit() -> uint {
264         let limit;
265         asm!("movq %fs:112, $0" : "=r"(limit) ::: "volatile");
266         return limit;
267     }
268     #[cfg(all(target_arch = "x86_64", target_os = "windows"))] #[inline(always)]
269     unsafe fn target_get_sp_limit() -> uint {
270         return 1024;
271     }
272     #[cfg(all(target_arch = "x86_64", target_os = "freebsd"))] #[inline(always)]
273     unsafe fn target_get_sp_limit() -> uint {
274         let limit;
275         asm!("movq %fs:24, $0" : "=r"(limit) ::: "volatile");
276         return limit;
277     }
278     #[cfg(all(target_arch = "x86_64", target_os = "dragonfly"))] #[inline(always)]
279     unsafe fn target_get_sp_limit() -> uint {
280         let limit;
281         asm!("movq %fs:32, $0" : "=r"(limit) ::: "volatile");
282         return limit;
283     }
284
285
286     // x86
287     #[cfg(all(target_arch = "x86",
288               any(target_os = "macos", target_os = "ios")))]
289     #[inline(always)]
290     unsafe fn target_get_sp_limit() -> uint {
291         let limit;
292         asm!("movl $$0x48+90*4, %eax
293               movl %gs:(%eax), $0" : "=r"(limit) :: "eax" : "volatile");
294         return limit;
295     }
296     #[cfg(all(target_arch = "x86",
297               any(target_os = "linux", target_os = "freebsd")))]
298     #[inline(always)]
299     unsafe fn target_get_sp_limit() -> uint {
300         let limit;
301         asm!("movl %gs:48, $0" : "=r"(limit) ::: "volatile");
302         return limit;
303     }
304     #[cfg(all(target_arch = "x86", target_os = "windows"))] #[inline(always)]
305     unsafe fn target_get_sp_limit() -> uint {
306         return 1024;
307     }
308
309     // mips, arm - Some brave soul can port these to inline asm, but it's over
310     //             my head personally
311     #[cfg(any(target_arch = "mips",
312               target_arch = "mipsel",
313               all(target_arch = "arm", not(target_os = "ios"))))]
314     #[inline(always)]
315     unsafe fn target_get_sp_limit() -> uint {
316         use libc::c_void;
317         return get_sp_limit() as uint;
318         extern {
319             fn get_sp_limit() -> *const c_void;
320         }
321     }
322
323     // aarch64 - FIXME(AARCH64): missing...
324     #[cfg(target_arch = "aarch64")]
325     unsafe fn target_get_sp_limit() -> uint {
326         1024
327     }
328
329     // iOS doesn't support segmented stacks yet. This function might
330     // be called by runtime though so it is unsafe to mark it as
331     // unreachable, let's return a fixed constant.
332     #[cfg(all(target_arch = "arm", target_os = "ios"))] #[inline(always)]
333     unsafe fn target_get_sp_limit() -> uint {
334         1024
335     }
336 }