]> git.proxmox.com Git - rustc.git/blame - src/libstd/sys/common/stack.rs
Imported Upstream version 1.3.0+dfsg1
[rustc.git] / src / libstd / sys / common / stack.rs
CommitLineData
1a4d82fc
JJ
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
bd371182 14//! overflow for rust threads. In this scheme, the prologue of all functions are
1a4d82fc
JJ
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
bd371182 20//! functions here are used at the borders of the thread lifetime in order to
1a4d82fc
JJ
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
c34b1796 49pub const RED_ZONE: usize = 20 * 1024;
1a4d82fc
JJ
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"]
57extern 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)]
c34b1796 120pub unsafe fn record_os_managed_stack_bounds(stack_lo: usize, _stack_hi: usize) {
1a4d82fc
JJ
121 record_sp_limit(stack_lo + RED_ZONE);
122}
123
1a4d82fc
JJ
124/// Records the current limit of the stack as specified by `end`.
125///
126/// This is stored in an OS-dependent location, likely inside of the thread
127/// local storage. The location that the limit is stored is a pre-ordained
128/// location because it's where LLVM has emitted code to check.
129///
130/// Note that this cannot be called under normal circumstances. This function is
131/// changing the stack limit, so upon returning any further function calls will
132/// possibly be triggering the morestack logic if you're not careful.
133///
134/// Also note that this and all of the inside functions are all flagged as
135/// "inline(always)" because they're messing around with the stack limits. This
136/// would be unfortunate for the functions themselves to trigger a morestack
137/// invocation (if they were an actual function call).
138#[inline(always)]
c34b1796 139pub unsafe fn record_sp_limit(limit: usize) {
1a4d82fc
JJ
140 return target_record_sp_limit(limit);
141
1a4d82fc
JJ
142 #[cfg(all(target_arch = "x86_64",
143 any(target_os = "macos", target_os = "ios")))]
144 #[inline(always)]
c34b1796 145 unsafe fn target_record_sp_limit(limit: usize) {
1a4d82fc
JJ
146 asm!("movq $$0x60+90*8, %rsi
147 movq $0, %gs:(%rsi)" :: "r"(limit) : "rsi" : "volatile")
148 }
149 #[cfg(all(target_arch = "x86_64", target_os = "linux"))] #[inline(always)]
c34b1796 150 unsafe fn target_record_sp_limit(limit: usize) {
1a4d82fc
JJ
151 asm!("movq $0, %fs:112" :: "r"(limit) :: "volatile")
152 }
153 #[cfg(all(target_arch = "x86_64", target_os = "windows"))] #[inline(always)]
c34b1796 154 unsafe fn target_record_sp_limit(_: usize) {
1a4d82fc
JJ
155 }
156 #[cfg(all(target_arch = "x86_64", target_os = "freebsd"))] #[inline(always)]
c34b1796 157 unsafe fn target_record_sp_limit(limit: usize) {
1a4d82fc
JJ
158 asm!("movq $0, %fs:24" :: "r"(limit) :: "volatile")
159 }
c34b1796
AL
160 #[cfg(all(target_arch = "x86_64", target_os = "dragonfly"))]
161 #[inline(always)]
162 unsafe fn target_record_sp_limit(limit: usize) {
1a4d82fc
JJ
163 asm!("movq $0, %fs:32" :: "r"(limit) :: "volatile")
164 }
165
1a4d82fc
JJ
166 #[cfg(all(target_arch = "x86",
167 any(target_os = "macos", target_os = "ios")))]
168 #[inline(always)]
c34b1796 169 unsafe fn target_record_sp_limit(limit: usize) {
1a4d82fc
JJ
170 asm!("movl $$0x48+90*4, %eax
171 movl $0, %gs:(%eax)" :: "r"(limit) : "eax" : "volatile")
172 }
c1a9b12d 173 #[cfg(all(target_arch = "x86", target_os = "linux"))]
1a4d82fc 174 #[inline(always)]
c34b1796 175 unsafe fn target_record_sp_limit(limit: usize) {
1a4d82fc
JJ
176 asm!("movl $0, %gs:48" :: "r"(limit) :: "volatile")
177 }
178 #[cfg(all(target_arch = "x86", target_os = "windows"))] #[inline(always)]
c34b1796 179 unsafe fn target_record_sp_limit(_: usize) {
1a4d82fc
JJ
180 }
181
62682a34
SL
182 // mips, arm - The implementations are a bit big for inline asm!
183 // They can be found in src/rt/arch/$target_arch/record_sp.S
1a4d82fc
JJ
184 #[cfg(any(target_arch = "mips",
185 target_arch = "mipsel",
186 all(target_arch = "arm", not(target_os = "ios"))))]
187 #[inline(always)]
c34b1796 188 unsafe fn target_record_sp_limit(limit: usize) {
1a4d82fc
JJ
189 use libc::c_void;
190 return record_sp_limit(limit as *const c_void);
191 extern {
192 fn record_sp_limit(limit: *const c_void);
193 }
194 }
195
196 // aarch64 - FIXME(AARCH64): missing...
85aaf69f
SL
197 // powerpc - FIXME(POWERPC): missing...
198 // arm-ios - iOS segmented stack is disabled for now, see related notes
c1a9b12d
SL
199 // openbsd/bitrig/netbsd - no segmented stacks.
200 // x86-freebsd - no segmented stacks.
85aaf69f
SL
201 #[cfg(any(target_arch = "aarch64",
202 target_arch = "powerpc",
203 all(target_arch = "arm", target_os = "ios"),
c1a9b12d 204 all(target_arch = "x86", target_os = "freebsd"),
c34b1796 205 target_os = "bitrig",
c1a9b12d 206 target_os = "netbsd",
85aaf69f 207 target_os = "openbsd"))]
c34b1796 208 unsafe fn target_record_sp_limit(_: usize) {
1a4d82fc
JJ
209 }
210}
211
212/// The counterpart of the function above, this function will fetch the current
213/// stack limit stored in TLS.
214///
215/// Note that all of these functions are meant to be exact counterparts of their
216/// brethren above, except that the operands are reversed.
217///
218/// As with the setter, this function does not have a __morestack header and can
219/// therefore be called in a "we're out of stack" situation.
220#[inline(always)]
c34b1796 221pub unsafe fn get_sp_limit() -> usize {
1a4d82fc
JJ
222 return target_get_sp_limit();
223
1a4d82fc
JJ
224 #[cfg(all(target_arch = "x86_64",
225 any(target_os = "macos", target_os = "ios")))]
226 #[inline(always)]
c34b1796 227 unsafe fn target_get_sp_limit() -> usize {
1a4d82fc
JJ
228 let limit;
229 asm!("movq $$0x60+90*8, %rsi
230 movq %gs:(%rsi), $0" : "=r"(limit) :: "rsi" : "volatile");
231 return limit;
232 }
233 #[cfg(all(target_arch = "x86_64", target_os = "linux"))] #[inline(always)]
c34b1796 234 unsafe fn target_get_sp_limit() -> usize {
1a4d82fc
JJ
235 let limit;
236 asm!("movq %fs:112, $0" : "=r"(limit) ::: "volatile");
237 return limit;
238 }
239 #[cfg(all(target_arch = "x86_64", target_os = "windows"))] #[inline(always)]
c34b1796 240 unsafe fn target_get_sp_limit() -> usize {
1a4d82fc
JJ
241 return 1024;
242 }
243 #[cfg(all(target_arch = "x86_64", target_os = "freebsd"))] #[inline(always)]
c34b1796 244 unsafe fn target_get_sp_limit() -> usize {
1a4d82fc
JJ
245 let limit;
246 asm!("movq %fs:24, $0" : "=r"(limit) ::: "volatile");
247 return limit;
248 }
c34b1796
AL
249 #[cfg(all(target_arch = "x86_64", target_os = "dragonfly"))]
250 #[inline(always)]
251 unsafe fn target_get_sp_limit() -> usize {
1a4d82fc
JJ
252 let limit;
253 asm!("movq %fs:32, $0" : "=r"(limit) ::: "volatile");
254 return limit;
255 }
256
1a4d82fc
JJ
257 #[cfg(all(target_arch = "x86",
258 any(target_os = "macos", target_os = "ios")))]
259 #[inline(always)]
c34b1796 260 unsafe fn target_get_sp_limit() -> usize {
1a4d82fc
JJ
261 let limit;
262 asm!("movl $$0x48+90*4, %eax
263 movl %gs:(%eax), $0" : "=r"(limit) :: "eax" : "volatile");
264 return limit;
265 }
c1a9b12d 266 #[cfg(all(target_arch = "x86", target_os = "linux"))]
1a4d82fc 267 #[inline(always)]
c34b1796 268 unsafe fn target_get_sp_limit() -> usize {
1a4d82fc
JJ
269 let limit;
270 asm!("movl %gs:48, $0" : "=r"(limit) ::: "volatile");
271 return limit;
272 }
273 #[cfg(all(target_arch = "x86", target_os = "windows"))] #[inline(always)]
c34b1796 274 unsafe fn target_get_sp_limit() -> usize {
1a4d82fc
JJ
275 return 1024;
276 }
277
62682a34
SL
278 // mips, arm - The implementations are a bit big for inline asm!
279 // They can be found in src/rt/arch/$target_arch/record_sp.S
1a4d82fc
JJ
280 #[cfg(any(target_arch = "mips",
281 target_arch = "mipsel",
282 all(target_arch = "arm", not(target_os = "ios"))))]
283 #[inline(always)]
c34b1796 284 unsafe fn target_get_sp_limit() -> usize {
1a4d82fc 285 use libc::c_void;
c34b1796 286 return get_sp_limit() as usize;
1a4d82fc
JJ
287 extern {
288 fn get_sp_limit() -> *const c_void;
289 }
290 }
291
292 // aarch64 - FIXME(AARCH64): missing...
85aaf69f 293 // powerpc - FIXME(POWERPC): missing...
c1a9b12d
SL
294 // arm-ios - no segmented stacks.
295 // openbsd/bitrig/netbsd - no segmented stacks.
296 // x86-freebsd - no segmented stacks..
85aaf69f
SL
297 //
298 // This function might be called by runtime though
299 // so it is unsafe to unreachable, let's return a fixed constant.
300 #[cfg(any(target_arch = "aarch64",
301 target_arch = "powerpc",
302 all(target_arch = "arm", target_os = "ios"),
c1a9b12d 303 all(target_arch = "x86", target_os = "freebsd"),
c34b1796 304 target_os = "bitrig",
c1a9b12d 305 target_os = "netbsd",
85aaf69f
SL
306 target_os = "openbsd"))]
307 #[inline(always)]
c34b1796 308 unsafe fn target_get_sp_limit() -> usize {
1a4d82fc
JJ
309 1024
310 }
311}