(index<- ) ./libstd/rt/unwind.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 //! Stack unwinding
12
13 // Implementation of Rust stack unwinding
14 //
15 // For background on exception handling and stack unwinding please see
16 // "Exception Handling in LLVM" (llvm.org/docs/ExceptionHandling.html) and
17 // documents linked from it.
18 // These are also good reads:
19 // http://theofilos.cs.columbia.edu/blog/2013/09/22/base_abi/
20 // http://monoinfinito.wordpress.com/series/exception-handling-in-c/
21 // http://www.airs.com/blog/index.php?s=exception+frames
22 //
23 // ~~~ A brief summary ~~~
24 // Exception handling happens in two phases: a search phase and a cleanup phase.
25 //
26 // In both phases the unwinder walks stack frames from top to bottom using
27 // information from the stack frame unwind sections of the current process's
28 // modules ("module" here refers to an OS module, i.e. an executable or a
29 // dynamic library).
30 //
31 // For each stack frame, it invokes the associated "personality routine", whose
32 // address is also stored in the unwind info section.
33 //
34 // In the search phase, the job of a personality routine is to examine exception
35 // object being thrown, and to decide whether it should be caught at that stack
36 // frame. Once the handler frame has been identified, cleanup phase begins.
37 //
38 // In the cleanup phase, personality routines invoke cleanup code associated
39 // with their stack frames (i.e. destructors). Once stack has been unwound down
40 // to the handler frame level, unwinding stops and the last personality routine
41 // transfers control to its' catch block.
42 //
43 // ~~~ Frame unwind info registration ~~~
44 // Each module has its' own frame unwind info section (usually ".eh_frame"), and
45 // unwinder needs to know about all of them in order for unwinding to be able to
46 // cross module boundaries.
47 //
48 // On some platforms, like Linux, this is achieved by dynamically enumerating
49 // currently loaded modules via the dl_iterate_phdr() API and finding all
50 // .eh_frame sections.
51 //
52 // Others, like Windows, require modules to actively register their unwind info
53 // sections by calling __register_frame_info() API at startup. In the latter
54 // case it is essential that there is only one copy of the unwinder runtime in
55 // the process. This is usually achieved by linking to the dynamic version of
56 // the unwind runtime.
57 //
58 // Currently Rust uses unwind runtime provided by libgcc.
59
60 use any::{Any, AnyRefExt};
61 use cast;
62 use fmt;
63 use kinds::Send;
64 use mem;
65 use option::{Some, None, Option};
66 use owned::Box;
67 use prelude::drop;
68 use ptr::RawPtr;
69 use result::{Err, Ok};
70 use rt::backtrace;
71 use rt::local::Local;
72 use rt::task::Task;
73 use str::Str;
74 use task::TaskResult;
75 use intrinsics;
76
77 use uw = rt::libunwind;
78
79 pub struct Unwinder {
80 unwinding: bool,
81 cause: Option<Box<Any:Send>>
82 }
83
84 impl Unwinder {
85 pub fn new() -> Unwinder {
86 Unwinder {
87 unwinding: false,
88 cause: None,
89 }
90 }
91
92 pub fn unwinding(&self) -> bool {
93 self.unwinding
94 }
95
96 pub fn try(&mut self, f: ||) {
97 use raw::Closure;
98 use libc::{c_void};
99
100 unsafe {
101 let closure: Closure = cast::transmute(f);
102 let ep = rust_try(try_fn, closure.code as *c_void,
103 closure.env as *c_void);
104 if !ep.is_null() {
105 rtdebug!("caught {}", (*ep).exception_class);
106 uw::_Unwind_DeleteException(ep);
107 }
108 }
109
110 extern fn try_fn(code: *c_void, env: *c_void) {
111 unsafe {
112 let closure: || = cast::transmute(Closure {
113 code: code as *(),
114 env: env as *(),
115 });
116 closure();
117 }
118 }
119
120 extern {
121 // Rust's try-catch
122 // When f(...) returns normally, the return value is null.
123 // When f(...) throws, the return value is a pointer to the caught
124 // exception object.
125 fn rust_try(f: extern "C" fn(*c_void, *c_void),
126 code: *c_void,
127 data: *c_void) -> *uw::_Unwind_Exception;
128 }
129 }
130
131 pub fn begin_unwind(&mut self, cause: Box<Any:Send>) -> ! {
132 rtdebug!("begin_unwind()");
133
134 self.unwinding = true;
135 self.cause = Some(cause);
136
137 rust_fail();
138
139 // An uninlined, unmangled function upon which to slap yer breakpoints
140 #[inline(never)]
141 #[no_mangle]
142 fn rust_fail() -> ! {
143 unsafe {
144 let exception = box uw::_Unwind_Exception {
145 exception_class: rust_exception_class(),
146 exception_cleanup: exception_cleanup,
147 private: [0, ..uw::unwinder_private_data_size],
148 };
149 let error = uw::_Unwind_RaiseException(cast::transmute(exception));
150 rtabort!("Could not unwind stack, error = {}", error as int)
151 }
152
153 extern "C" fn exception_cleanup(_unwind_code: uw::_Unwind_Reason_Code,
154 exception: *uw::_Unwind_Exception) {
155 rtdebug!("exception_cleanup()");
156 unsafe {
157 let _: Box<uw::_Unwind_Exception> =
158 cast::transmute(exception);
159 }
160 }
161 }
162 }
163
164 pub fn result(&mut self) -> TaskResult {
165 if self.unwinding {
166 Err(self.cause.take().unwrap())
167 } else {
168 Ok(())
169 }
170 }
171 }
172
173 // Rust's exception class identifier. This is used by personality routines to
174 // determine whether the exception was thrown by their own runtime.
175 fn rust_exception_class() -> uw::_Unwind_Exception_Class {
176 // M O Z \0 R U S T -- vendor, language
177 0x4d4f5a_00_52555354
178 }
179
180 // We could implement our personality routine in pure Rust, however exception
181 // info decoding is tedious. More importantly, personality routines have to
182 // handle various platform quirks, which are not fun to maintain. For this
183 // reason, we attempt to reuse personality routine of the C language:
184 // __gcc_personality_v0.
185 //
186 // Since C does not support exception catching, __gcc_personality_v0 simply
187 // always returns _URC_CONTINUE_UNWIND in search phase, and always returns
188 // _URC_INSTALL_CONTEXT (i.e. "invoke cleanup code") in cleanup phase.
189 //
190 // This is pretty close to Rust's exception handling approach, except that Rust
191 // does have a single "catch-all" handler at the bottom of each task's stack.
192 // So we have two versions:
193 // - rust_eh_personality, used by all cleanup landing pads, which never catches,
194 // so the behavior of __gcc_personality_v0 is perfectly adequate there, and
195 // - rust_eh_personality_catch, used only by rust_try(), which always catches.
196 // This is achieved by overriding the return value in search phase to always
197 // say "catch!".
198
199 #[cfg(not(target_arch = "arm"), not(test))]
200 #[doc(hidden)]
201 #[allow(visible_private_types)]
202 pub mod eabi {
203 use uw = rt::libunwind;
204 use libc::c_int;
205
206 extern "C" {
207 fn __gcc_personality_v0(version: c_int,
208 actions: uw::_Unwind_Action,
209 exception_class: uw::_Unwind_Exception_Class,
210 ue_header: *uw::_Unwind_Exception,
211 context: *uw::_Unwind_Context)
212 -> uw::_Unwind_Reason_Code;
213 }
214
215 #[lang="eh_personality"]
216 #[no_mangle] // so we can reference it by name from middle/trans/base.rs
217 pub extern "C" fn rust_eh_personality(
218 version: c_int,
219 actions: uw::_Unwind_Action,
220 exception_class: uw::_Unwind_Exception_Class,
221 ue_header: *uw::_Unwind_Exception,
222 context: *uw::_Unwind_Context
223 ) -> uw::_Unwind_Reason_Code
224 {
225 unsafe {
226 __gcc_personality_v0(version, actions, exception_class, ue_header,
227 context)
228 }
229 }
230
231 #[no_mangle] // referenced from rust_try.ll
232 pub extern "C" fn rust_eh_personality_catch(
233 version: c_int,
234 actions: uw::_Unwind_Action,
235 exception_class: uw::_Unwind_Exception_Class,
236 ue_header: *uw::_Unwind_Exception,
237 context: *uw::_Unwind_Context
238 ) -> uw::_Unwind_Reason_Code
239 {
240 if (actions as c_int & uw::_UA_SEARCH_PHASE as c_int) != 0 { // search phase
241 uw::_URC_HANDLER_FOUND // catch!
242 }
243 else { // cleanup phase
244 unsafe {
245 __gcc_personality_v0(version, actions, exception_class, ue_header,
246 context)
247 }
248 }
249 }
250 }
251
252 // ARM EHABI uses a slightly different personality routine signature,
253 // but otherwise works the same.
254 #[cfg(target_arch = "arm", not(test))]
255 #[allow(visible_private_types)]
256 pub mod eabi {
257 use uw = rt::libunwind;
258 use libc::c_int;
259
260 extern "C" {
261 fn __gcc_personality_v0(state: uw::_Unwind_State,
262 ue_header: *uw::_Unwind_Exception,
263 context: *uw::_Unwind_Context)
264 -> uw::_Unwind_Reason_Code;
265 }
266
267 #[lang="eh_personality"]
268 #[no_mangle] // so we can reference it by name from middle/trans/base.rs
269 pub extern "C" fn rust_eh_personality(
270 state: uw::_Unwind_State,
271 ue_header: *uw::_Unwind_Exception,
272 context: *uw::_Unwind_Context
273 ) -> uw::_Unwind_Reason_Code
274 {
275 unsafe {
276 __gcc_personality_v0(state, ue_header, context)
277 }
278 }
279
280 #[no_mangle] // referenced from rust_try.ll
281 pub extern "C" fn rust_eh_personality_catch(
282 state: uw::_Unwind_State,
283 ue_header: *uw::_Unwind_Exception,
284 context: *uw::_Unwind_Context
285 ) -> uw::_Unwind_Reason_Code
286 {
287 if (state as c_int & uw::_US_ACTION_MASK as c_int)
288 == uw::_US_VIRTUAL_UNWIND_FRAME as c_int { // search phase
289 uw::_URC_HANDLER_FOUND // catch!
290 }
291 else { // cleanup phase
292 unsafe {
293 __gcc_personality_v0(state, ue_header, context)
294 }
295 }
296 }
297 }
298
299 #[cold]
300 #[no_mangle]
301 #[cfg(not(test))]
302 pub extern fn rust_fail_bounds_check(file: *u8, line: uint,
303 index: uint, len: uint) -> ! {
304 use str::raw::c_str_to_static_slice;
305
306 let msg = format!("index out of bounds: the len is {} but the index is {}",
307 len as uint, index as uint);
308 begin_unwind(msg, unsafe { c_str_to_static_slice(file as *i8) }, line)
309 }
310
311 // Entry point of failure from the libcore crate
312 #[no_mangle]
313 #[cfg(not(test))]
314 pub extern fn rust_begin_unwind(msg: &str, file: &'static str, line: uint) -> ! {
315 use str::StrAllocating;
316 begin_unwind(msg.to_owned(), file, line)
317 }
318
319 /// The entry point for unwinding with a formatted message.
320 ///
321 /// This is designed to reduce the amount of code required at the call
322 /// site as much as possible (so that `fail!()` has as low an impact
323 /// on (e.g.) the inlining of other functions as possible), by moving
324 /// the actual formatting into this shared place.
325 #[inline(never)] #[cold]
326 pub fn begin_unwind_fmt(msg: &fmt::Arguments, file: &'static str, line: uint) -> ! {
327 // We do two allocations here, unfortunately. But (a) they're
328 // required with the current scheme, and (b) we don't handle
329 // failure + OOM properly anyway (see comment in begin_unwind
330 // below).
331 begin_unwind_inner(box fmt::format(msg), file, line)
332 }
333
334 /// This is the entry point of unwinding for fail!() and assert!().
335 #[inline(never)] #[cold] // avoid code bloat at the call sites as much as possible
336 pub fn begin_unwind<M: Any + Send>(msg: M, file: &'static str, line: uint) -> ! {
337 // Note that this should be the only allocation performed in this code path.
338 // Currently this means that fail!() on OOM will invoke this code path,
339 // but then again we're not really ready for failing on OOM anyway. If
340 // we do start doing this, then we should propagate this allocation to
341 // be performed in the parent of this task instead of the task that's
342 // failing.
343
344 // see below for why we do the `Any` coercion here.
345 begin_unwind_inner(box msg, file, line)
346 }
347
348
349 /// The core of the unwinding.
350 ///
351 /// This is non-generic to avoid instantiation bloat in other crates
352 /// (which makes compilation of small crates noticably slower). (Note:
353 /// we need the `Any` object anyway, we're not just creating it to
354 /// avoid being generic.)
355 ///
356 /// Do this split took the LLVM IR line counts of `fn main() { fail!()
357 /// }` from ~1900/3700 (-O/no opts) to 180/590.
358 #[inline(never)] #[cold] // this is the slow path, please never inline this
359 fn begin_unwind_inner(msg: Box<Any:Send>,
360 file: &'static str,
361 line: uint) -> ! {
362 let mut task;
363 {
364 let msg_s = match msg.as_ref::<&'static str>() {
365 Some(s) => *s,
366 None => match msg.as_ref::<~str>() {
367 Some(s) => s.as_slice(),
368 None => "Box<Any>",
369 }
370 };
371
372 // It is assumed that all reasonable rust code will have a local task at
373 // all times. This means that this `try_take` will succeed almost all of
374 // the time. There are border cases, however, when the runtime has
375 // *almost* set up the local task, but hasn't quite gotten there yet. In
376 // order to get some better diagnostics, we print on failure and
377 // immediately abort the whole process if there is no local task
378 // available.
379 let opt_task: Option<Box<Task>> = Local::try_take();
380 task = match opt_task {
381 Some(t) => t,
382 None => {
383 rterrln!("failed at '{}', {}:{}", msg_s, file, line);
384 if backtrace::log_enabled() {
385 let mut err = ::rt::util::Stderr;
386 let _err = backtrace::write(&mut err);
387 } else {
388 rterrln!("run with `RUST_BACKTRACE=1` to see a backtrace");
389 }
390 unsafe { intrinsics::abort() }
391 }
392 };
393
394 // See comments in io::stdio::with_task_stdout as to why we have to be
395 // careful when using an arbitrary I/O handle from the task. We
396 // essentially need to dance to make sure when a task is in TLS when
397 // running user code.
398 let name = task.name.take();
399 {
400 let n = name.as_ref().map(|n| n.as_slice()).unwrap_or("<unnamed>");
401
402 match task.stderr.take() {
403 Some(mut stderr) => {
404 Local::put(task);
405 // FIXME: what to do when the task printing fails?
406 let _err = format_args!(|args| ::fmt::writeln(stderr, args),
407 "task '{}' failed at '{}', {}:{}",
408 n, msg_s, file, line);
409 if backtrace::log_enabled() {
410 let _err = backtrace::write(stderr);
411 }
412 task = Local::take();
413
414 match mem::replace(&mut task.stderr, Some(stderr)) {
415 Some(prev) => {
416 Local::put(task);
417 drop(prev);
418 task = Local::take();
419 }
420 None => {}
421 }
422 }
423 None => {
424 rterrln!("task '{}' failed at '{}', {}:{}", n, msg_s,
425 file, line);
426 if backtrace::log_enabled() {
427 let mut err = ::rt::util::Stderr;
428 let _err = backtrace::write(&mut err);
429 }
430 }
431 }
432 }
433 task.name = name;
434
435 if task.unwinder.unwinding {
436 // If a task fails while it's already unwinding then we
437 // have limited options. Currently our preference is to
438 // just abort. In the future we may consider resuming
439 // unwinding or otherwise exiting the task cleanly.
440 rterrln!("task failed during unwinding (double-failure - total drag!)")
441 rterrln!("rust must abort now. so sorry.");
442
443 // Don't print the backtrace twice (it would have already been
444 // printed if logging was enabled).
445 if !backtrace::log_enabled() {
446 let mut err = ::rt::util::Stderr;
447 let _err = backtrace::write(&mut err);
448 }
449 unsafe { intrinsics::abort() }
450 }
451 }
452
453 // The unwinder won't actually use the task at all, so we put the task back
454 // into TLS right before we invoke the unwinder, but this means we need an
455 // unsafe reference back to the unwinder once it's in TLS.
456 Local::put(task);
457 unsafe {
458 let task: *mut Task = Local::unsafe_borrow();
459 (*task).unwinder.begin_unwind(msg);
460 }
461 }
libstd/rt/unwind.rs:78:1-78:1 -struct- definition:
pub struct Unwinder {
unwinding: bool,
cause: Option<Box<Any:Send>>
references:- 485: pub fn new() -> Unwinder {
86: Unwinder {
87: unwinding: false,
libstd/rt/task.rs:
49: pub storage: LocalStorage,
50: pub unwinder: Unwinder,
51: pub death: Death,
libstd/rt/unwind.rs:358:76-358:76 -fn- definition:
fn begin_unwind_inner(msg: Box<Any:Send>,
file: &'static str,
line: uint) -> ! {
references:- 2330: // below).
331: begin_unwind_inner(box fmt::format(msg), file, line)
332: }
--
344: // see below for why we do the `Any` coercion here.
345: begin_unwind_inner(box msg, file, line)
346: }
libstd/rt/unwind.rs:335:83-335:83 -fn- definition:
pub fn begin_unwind<M: Any + Send>(msg: M, file: &'static str, line: uint) -> ! {
// Note that this should be the only allocation performed in this code path.
// Currently this means that fail!() on OOM will invoke this code path,
references:- 63libstd/macros.rs:
libstd/rt/unwind.rs:325:25-325:25 -fn- definition:
pub fn begin_unwind_fmt(msg: &fmt::Arguments, file: &'static str, line: uint) -> ! {
// We do two allocations here, unfortunately. But (a) they're
// required with the current scheme, and (b) we don't handle
references:- 121libstd/macros.rs: