]> git.lizzy.rs Git - rust.git/blob - src/libstd/rt/unwind.rs
be05bfceaac3e1da8024b8db55b0330d101712c3
[rust.git] / src / libstd / rt / unwind.rs
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 fmt;
62 use intrinsics;
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 string::String;
75 use task::TaskResult;
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 = mem::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: || = mem::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(mem::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                         mem::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     #[cfg(not(stage0))]
217     extern fn 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     #[lang="eh_personality"]
231     #[no_mangle] // so we can reference it by name from middle/trans/base.rs
232     #[cfg(stage0)]
233     pub extern "C" fn rust_eh_personality(
234         version: c_int,
235         actions: uw::_Unwind_Action,
236         exception_class: uw::_Unwind_Exception_Class,
237         ue_header: *uw::_Unwind_Exception,
238         context: *uw::_Unwind_Context
239     ) -> uw::_Unwind_Reason_Code
240     {
241         unsafe {
242             __gcc_personality_v0(version, actions, exception_class, ue_header,
243                                  context)
244         }
245     }
246
247     #[no_mangle] // referenced from rust_try.ll
248     pub extern "C" fn rust_eh_personality_catch(
249         version: c_int,
250         actions: uw::_Unwind_Action,
251         exception_class: uw::_Unwind_Exception_Class,
252         ue_header: *uw::_Unwind_Exception,
253         context: *uw::_Unwind_Context
254     ) -> uw::_Unwind_Reason_Code
255     {
256         if (actions as c_int & uw::_UA_SEARCH_PHASE as c_int) != 0 { // search phase
257             uw::_URC_HANDLER_FOUND // catch!
258         }
259         else { // cleanup phase
260             unsafe {
261                  __gcc_personality_v0(version, actions, exception_class, ue_header,
262                                       context)
263             }
264         }
265     }
266 }
267
268 // ARM EHABI uses a slightly different personality routine signature,
269 // but otherwise works the same.
270 #[cfg(target_arch = "arm", not(test))]
271 #[allow(visible_private_types)]
272 pub mod eabi {
273     use uw = rt::libunwind;
274     use libc::c_int;
275
276     extern "C" {
277         fn __gcc_personality_v0(state: uw::_Unwind_State,
278                                 ue_header: *uw::_Unwind_Exception,
279                                 context: *uw::_Unwind_Context)
280             -> uw::_Unwind_Reason_Code;
281     }
282
283     #[lang="eh_personality"]
284     #[cfg(not(stage0))]
285     extern "C" fn eh_personality(
286         state: uw::_Unwind_State,
287         ue_header: *uw::_Unwind_Exception,
288         context: *uw::_Unwind_Context
289     ) -> uw::_Unwind_Reason_Code
290     {
291         unsafe {
292             __gcc_personality_v0(state, ue_header, context)
293         }
294     }
295
296     #[lang="eh_personality"]
297     #[no_mangle] // so we can reference it by name from middle/trans/base.rs
298     #[cfg(stage0)]
299     pub extern "C" fn rust_eh_personality(
300         state: uw::_Unwind_State,
301         ue_header: *uw::_Unwind_Exception,
302         context: *uw::_Unwind_Context
303     ) -> uw::_Unwind_Reason_Code
304     {
305         unsafe {
306             __gcc_personality_v0(state, ue_header, context)
307         }
308     }
309
310     #[no_mangle] // referenced from rust_try.ll
311     pub extern "C" fn rust_eh_personality_catch(
312         state: uw::_Unwind_State,
313         ue_header: *uw::_Unwind_Exception,
314         context: *uw::_Unwind_Context
315     ) -> uw::_Unwind_Reason_Code
316     {
317         if (state as c_int & uw::_US_ACTION_MASK as c_int)
318                            == uw::_US_VIRTUAL_UNWIND_FRAME as c_int { // search phase
319             uw::_URC_HANDLER_FOUND // catch!
320         }
321         else { // cleanup phase
322             unsafe {
323                  __gcc_personality_v0(state, ue_header, context)
324             }
325         }
326     }
327 }
328
329 // Entry point of failure from the libcore crate
330 #[cfg(not(test), not(stage0))]
331 #[lang = "begin_unwind"]
332 pub extern fn rust_begin_unwind(msg: &fmt::Arguments,
333                                 file: &'static str, line: uint) -> ! {
334     begin_unwind_fmt(msg, file, line)
335 }
336
337 #[no_mangle]
338 #[cfg(not(test), stage0)]
339 pub extern fn rust_begin_unwind(msg: &fmt::Arguments,
340                                 file: &'static str, line: uint) -> ! {
341     begin_unwind_fmt(msg, file, line)
342 }
343
344 /// The entry point for unwinding with a formatted message.
345 ///
346 /// This is designed to reduce the amount of code required at the call
347 /// site as much as possible (so that `fail!()` has as low an impact
348 /// on (e.g.) the inlining of other functions as possible), by moving
349 /// the actual formatting into this shared place.
350 #[inline(never)] #[cold]
351 pub fn begin_unwind_fmt(msg: &fmt::Arguments, file: &'static str,
352                         line: uint) -> ! {
353     // We do two allocations here, unfortunately. But (a) they're
354     // required with the current scheme, and (b) we don't handle
355     // failure + OOM properly anyway (see comment in begin_unwind
356     // below).
357     begin_unwind_inner(box fmt::format_strbuf(msg), file, line)
358 }
359
360 /// This is the entry point of unwinding for fail!() and assert!().
361 #[inline(never)] #[cold] // avoid code bloat at the call sites as much as possible
362 pub fn begin_unwind<M: Any + Send>(msg: M, file: &'static str, line: uint) -> ! {
363     // Note that this should be the only allocation performed in this code path.
364     // Currently this means that fail!() on OOM will invoke this code path,
365     // but then again we're not really ready for failing on OOM anyway. If
366     // we do start doing this, then we should propagate this allocation to
367     // be performed in the parent of this task instead of the task that's
368     // failing.
369
370     // see below for why we do the `Any` coercion here.
371     begin_unwind_inner(box msg, file, line)
372 }
373
374
375 /// The core of the unwinding.
376 ///
377 /// This is non-generic to avoid instantiation bloat in other crates
378 /// (which makes compilation of small crates noticably slower). (Note:
379 /// we need the `Any` object anyway, we're not just creating it to
380 /// avoid being generic.)
381 ///
382 /// Do this split took the LLVM IR line counts of `fn main() { fail!()
383 /// }` from ~1900/3700 (-O/no opts) to 180/590.
384 #[inline(never)] #[cold] // this is the slow path, please never inline this
385 fn begin_unwind_inner(msg: Box<Any:Send>,
386                       file: &'static str,
387                       line: uint) -> ! {
388     let mut task;
389     {
390         let msg_s = match msg.as_ref::<&'static str>() {
391             Some(s) => *s,
392             None => match msg.as_ref::<String>() {
393                 Some(s) => s.as_slice(),
394                 None => "Box<Any>",
395             }
396         };
397
398         // It is assumed that all reasonable rust code will have a local task at
399         // all times. This means that this `try_take` will succeed almost all of
400         // the time. There are border cases, however, when the runtime has
401         // *almost* set up the local task, but hasn't quite gotten there yet. In
402         // order to get some better diagnostics, we print on failure and
403         // immediately abort the whole process if there is no local task
404         // available.
405         let opt_task: Option<Box<Task>> = Local::try_take();
406         task = match opt_task {
407             Some(t) => t,
408             None => {
409                 rterrln!("failed at '{}', {}:{}", msg_s, file, line);
410                 if backtrace::log_enabled() {
411                     let mut err = ::rt::util::Stderr;
412                     let _err = backtrace::write(&mut err);
413                 } else {
414                     rterrln!("run with `RUST_BACKTRACE=1` to see a backtrace");
415                 }
416                 unsafe { intrinsics::abort() }
417             }
418         };
419
420         // See comments in io::stdio::with_task_stdout as to why we have to be
421         // careful when using an arbitrary I/O handle from the task. We
422         // essentially need to dance to make sure when a task is in TLS when
423         // running user code.
424         let name = task.name.take();
425         {
426             let n = name.as_ref().map(|n| n.as_slice()).unwrap_or("<unnamed>");
427
428             match task.stderr.take() {
429                 Some(mut stderr) => {
430                     Local::put(task);
431                     // FIXME: what to do when the task printing fails?
432                     let _err = write!(stderr,
433                                       "task '{}' failed at '{}', {}:{}\n",
434                                       n, msg_s, file, line);
435                     if backtrace::log_enabled() {
436                         let _err = backtrace::write(stderr);
437                     }
438                     task = Local::take();
439
440                     match mem::replace(&mut task.stderr, Some(stderr)) {
441                         Some(prev) => {
442                             Local::put(task);
443                             drop(prev);
444                             task = Local::take();
445                         }
446                         None => {}
447                     }
448                 }
449                 None => {
450                     rterrln!("task '{}' failed at '{}', {}:{}", n, msg_s,
451                              file, line);
452                     if backtrace::log_enabled() {
453                         let mut err = ::rt::util::Stderr;
454                         let _err = backtrace::write(&mut err);
455                     }
456                 }
457             }
458         }
459         task.name = name;
460
461         if task.unwinder.unwinding {
462             // If a task fails while it's already unwinding then we
463             // have limited options. Currently our preference is to
464             // just abort. In the future we may consider resuming
465             // unwinding or otherwise exiting the task cleanly.
466             rterrln!("task failed during unwinding (double-failure - total drag!)")
467             rterrln!("rust must abort now. so sorry.");
468
469             // Don't print the backtrace twice (it would have already been
470             // printed if logging was enabled).
471             if !backtrace::log_enabled() {
472                 let mut err = ::rt::util::Stderr;
473                 let _err = backtrace::write(&mut err);
474             }
475             unsafe { intrinsics::abort() }
476         }
477     }
478
479     // The unwinder won't actually use the task at all, so we put the task back
480     // into TLS right before we invoke the unwinder, but this means we need an
481     // unsafe reference back to the unwinder once it's in TLS.
482     Local::put(task);
483     unsafe {
484         let task: *mut Task = Local::unsafe_borrow();
485         (*task).unwinder.begin_unwind(msg);
486     }
487 }