]>
Commit | Line | Data |
---|---|---|
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 | 49 | pub 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"] | |
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)] | |
c34b1796 | 120 | pub 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 | 139 | pub 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 | 221 | pub 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 | } |