]> git.lizzy.rs Git - rust.git/blob - src/libstd/rt/unwind.rs
auto merge of #13967 : richo/rust/features/ICE-fails, r=alexcrichton
[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 c_str::CString;
62 use cast;
63 use fmt;
64 use kinds::Send;
65 use mem;
66 use option::{Some, None, Option};
67 use owned::Box;
68 use prelude::drop;
69 use ptr::RawPtr;
70 use result::{Err, Ok};
71 use rt::backtrace;
72 use rt::local::Local;
73 use rt::task::Task;
74 use str::Str;
75 use task::TaskResult;
76 use intrinsics;
77
78 use uw = rt::libunwind;
79
80 pub struct Unwinder {
81     unwinding: bool,
82     cause: Option<Box<Any:Send>>
83 }
84
85 impl Unwinder {
86     pub fn new() -> Unwinder {
87         Unwinder {
88             unwinding: false,
89             cause: None,
90         }
91     }
92
93     pub fn unwinding(&self) -> bool {
94         self.unwinding
95     }
96
97     pub fn try(&mut self, f: ||) {
98         use raw::Closure;
99         use libc::{c_void};
100
101         unsafe {
102             let closure: Closure = cast::transmute(f);
103             let ep = rust_try(try_fn, closure.code as *c_void,
104                               closure.env as *c_void);
105             if !ep.is_null() {
106                 rtdebug!("caught {}", (*ep).exception_class);
107                 uw::_Unwind_DeleteException(ep);
108             }
109         }
110
111         extern fn try_fn(code: *c_void, env: *c_void) {
112             unsafe {
113                 let closure: || = cast::transmute(Closure {
114                     code: code as *(),
115                     env: env as *(),
116                 });
117                 closure();
118             }
119         }
120
121         extern {
122             // Rust's try-catch
123             // When f(...) returns normally, the return value is null.
124             // When f(...) throws, the return value is a pointer to the caught
125             // exception object.
126             fn rust_try(f: extern "C" fn(*c_void, *c_void),
127                         code: *c_void,
128                         data: *c_void) -> *uw::_Unwind_Exception;
129         }
130     }
131
132     pub fn begin_unwind(&mut self, cause: Box<Any:Send>) -> ! {
133         rtdebug!("begin_unwind()");
134
135         self.unwinding = true;
136         self.cause = Some(cause);
137
138         rust_fail();
139
140         // An uninlined, unmangled function upon which to slap yer breakpoints
141         #[inline(never)]
142         #[no_mangle]
143         fn rust_fail() -> ! {
144             unsafe {
145                 let exception = box uw::_Unwind_Exception {
146                     exception_class: rust_exception_class(),
147                     exception_cleanup: exception_cleanup,
148                     private: [0, ..uw::unwinder_private_data_size],
149                 };
150                 let error = uw::_Unwind_RaiseException(cast::transmute(exception));
151                 rtabort!("Could not unwind stack, error = {}", error as int)
152             }
153
154             extern "C" fn exception_cleanup(_unwind_code: uw::_Unwind_Reason_Code,
155                                             exception: *uw::_Unwind_Exception) {
156                 rtdebug!("exception_cleanup()");
157                 unsafe {
158                     let _: Box<uw::_Unwind_Exception> =
159                         cast::transmute(exception);
160                 }
161             }
162         }
163     }
164
165     pub fn result(&mut self) -> TaskResult {
166         if self.unwinding {
167             Err(self.cause.take().unwrap())
168         } else {
169             Ok(())
170         }
171     }
172 }
173
174 // Rust's exception class identifier.  This is used by personality routines to
175 // determine whether the exception was thrown by their own runtime.
176 fn rust_exception_class() -> uw::_Unwind_Exception_Class {
177     // M O Z \0  R U S T -- vendor, language
178     0x4d4f5a_00_52555354
179 }
180
181 // We could implement our personality routine in pure Rust, however exception
182 // info decoding is tedious.  More importantly, personality routines have to
183 // handle various platform quirks, which are not fun to maintain.  For this
184 // reason, we attempt to reuse personality routine of the C language:
185 // __gcc_personality_v0.
186 //
187 // Since C does not support exception catching, __gcc_personality_v0 simply
188 // always returns _URC_CONTINUE_UNWIND in search phase, and always returns
189 // _URC_INSTALL_CONTEXT (i.e. "invoke cleanup code") in cleanup phase.
190 //
191 // This is pretty close to Rust's exception handling approach, except that Rust
192 // does have a single "catch-all" handler at the bottom of each task's stack.
193 // So we have two versions:
194 // - rust_eh_personality, used by all cleanup landing pads, which never catches,
195 //   so the behavior of __gcc_personality_v0 is perfectly adequate there, and
196 // - rust_eh_personality_catch, used only by rust_try(), which always catches.
197 //   This is achieved by overriding the return value in search phase to always
198 //   say "catch!".
199
200 #[cfg(not(target_arch = "arm"), not(test))]
201 #[doc(hidden)]
202 #[allow(visible_private_types)]
203 pub mod eabi {
204     use uw = rt::libunwind;
205     use libc::c_int;
206
207     extern "C" {
208         fn __gcc_personality_v0(version: c_int,
209                                 actions: uw::_Unwind_Action,
210                                 exception_class: uw::_Unwind_Exception_Class,
211                                 ue_header: *uw::_Unwind_Exception,
212                                 context: *uw::_Unwind_Context)
213             -> uw::_Unwind_Reason_Code;
214     }
215
216     #[lang="eh_personality"]
217     #[no_mangle] // so we can reference it by name from middle/trans/base.rs
218     pub extern "C" fn rust_eh_personality(
219         version: c_int,
220         actions: uw::_Unwind_Action,
221         exception_class: uw::_Unwind_Exception_Class,
222         ue_header: *uw::_Unwind_Exception,
223         context: *uw::_Unwind_Context
224     ) -> uw::_Unwind_Reason_Code
225     {
226         unsafe {
227             __gcc_personality_v0(version, actions, exception_class, ue_header,
228                                  context)
229         }
230     }
231
232     #[no_mangle] // referenced from rust_try.ll
233     pub extern "C" fn rust_eh_personality_catch(
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         if (actions as c_int & uw::_UA_SEARCH_PHASE as c_int) != 0 { // search phase
242             uw::_URC_HANDLER_FOUND // catch!
243         }
244         else { // cleanup phase
245             unsafe {
246                  __gcc_personality_v0(version, actions, exception_class, ue_header,
247                                       context)
248             }
249         }
250     }
251 }
252
253 // ARM EHABI uses a slightly different personality routine signature,
254 // but otherwise works the same.
255 #[cfg(target_arch = "arm", not(test))]
256 #[allow(visible_private_types)]
257 pub mod eabi {
258     use uw = rt::libunwind;
259     use libc::c_int;
260
261     extern "C" {
262         fn __gcc_personality_v0(state: uw::_Unwind_State,
263                                 ue_header: *uw::_Unwind_Exception,
264                                 context: *uw::_Unwind_Context)
265             -> uw::_Unwind_Reason_Code;
266     }
267
268     #[lang="eh_personality"]
269     #[no_mangle] // so we can reference it by name from middle/trans/base.rs
270     pub extern "C" fn rust_eh_personality(
271         state: uw::_Unwind_State,
272         ue_header: *uw::_Unwind_Exception,
273         context: *uw::_Unwind_Context
274     ) -> uw::_Unwind_Reason_Code
275     {
276         unsafe {
277             __gcc_personality_v0(state, ue_header, context)
278         }
279     }
280
281     #[no_mangle] // referenced from rust_try.ll
282     pub extern "C" fn rust_eh_personality_catch(
283         state: uw::_Unwind_State,
284         ue_header: *uw::_Unwind_Exception,
285         context: *uw::_Unwind_Context
286     ) -> uw::_Unwind_Reason_Code
287     {
288         if (state as c_int & uw::_US_ACTION_MASK as c_int)
289                            == uw::_US_VIRTUAL_UNWIND_FRAME as c_int { // search phase
290             uw::_URC_HANDLER_FOUND // catch!
291         }
292         else { // cleanup phase
293             unsafe {
294                  __gcc_personality_v0(state, ue_header, context)
295             }
296         }
297     }
298 }
299
300 #[cold]
301 #[lang="fail_"]
302 #[cfg(not(test))]
303 pub fn fail_(expr: *u8, file: *u8, line: uint) -> ! {
304     begin_unwind_raw(expr, file, line);
305 }
306
307 #[cold]
308 #[lang="fail_bounds_check"]
309 #[cfg(not(test))]
310 pub fn fail_bounds_check(file: *u8, line: uint, index: uint, len: uint) -> ! {
311     use c_str::ToCStr;
312
313     let msg = format!("index out of bounds: the len is {} but the index is {}",
314                       len as uint, index as uint);
315     msg.with_c_str(|buf| fail_(buf as *u8, file, line))
316 }
317
318 /// This is the entry point of unwinding for things like lang items and such.
319 /// The arguments are normally generated by the compiler, and need to
320 /// have static lifetimes.
321 #[inline(never)] #[cold] // this is the slow path, please never inline this
322 pub fn begin_unwind_raw(msg: *u8, file: *u8, line: uint) -> ! {
323     use libc::c_char;
324     #[inline]
325     fn static_char_ptr(p: *u8) -> &'static str {
326         let s = unsafe { CString::new(p as *c_char, false) };
327         match s.as_str() {
328             Some(s) => unsafe { cast::transmute::<&str, &'static str>(s) },
329             None => rtabort!("message wasn't utf8?")
330         }
331     }
332
333     let msg = static_char_ptr(msg);
334     let file = static_char_ptr(file);
335
336     begin_unwind(msg, file, line as uint)
337 }
338
339 /// The entry point for unwinding with a formatted message.
340 ///
341 /// This is designed to reduce the amount of code required at the call
342 /// site as much as possible (so that `fail!()` has as low an impact
343 /// on (e.g.) the inlining of other functions as possible), by moving
344 /// the actual formatting into this shared place.
345 #[inline(never)] #[cold]
346 pub fn begin_unwind_fmt(msg: &fmt::Arguments, file: &'static str, line: uint) -> ! {
347     // We do two allocations here, unfortunately. But (a) they're
348     // required with the current scheme, and (b) we don't handle
349     // failure + OOM properly anyway (see comment in begin_unwind
350     // below).
351     begin_unwind_inner(box fmt::format(msg), file, line)
352 }
353
354 /// This is the entry point of unwinding for fail!() and assert!().
355 #[inline(never)] #[cold] // avoid code bloat at the call sites as much as possible
356 pub fn begin_unwind<M: Any + Send>(msg: M, file: &'static str, line: uint) -> ! {
357     // Note that this should be the only allocation performed in this code path.
358     // Currently this means that fail!() on OOM will invoke this code path,
359     // but then again we're not really ready for failing on OOM anyway. If
360     // we do start doing this, then we should propagate this allocation to
361     // be performed in the parent of this task instead of the task that's
362     // failing.
363
364     // see below for why we do the `Any` coercion here.
365     begin_unwind_inner(box msg, file, line)
366 }
367
368
369 /// The core of the unwinding.
370 ///
371 /// This is non-generic to avoid instantiation bloat in other crates
372 /// (which makes compilation of small crates noticably slower). (Note:
373 /// we need the `Any` object anyway, we're not just creating it to
374 /// avoid being generic.)
375 ///
376 /// Do this split took the LLVM IR line counts of `fn main() { fail!()
377 /// }` from ~1900/3700 (-O/no opts) to 180/590.
378 #[inline(never)] #[cold] // this is the slow path, please never inline this
379 fn begin_unwind_inner(msg: Box<Any:Send>,
380                       file: &'static str,
381                       line: uint) -> ! {
382     let mut task;
383     {
384         let msg_s = match msg.as_ref::<&'static str>() {
385             Some(s) => *s,
386             None => match msg.as_ref::<~str>() {
387                 Some(s) => s.as_slice(),
388                 None => "Box<Any>",
389             }
390         };
391
392         // It is assumed that all reasonable rust code will have a local task at
393         // all times. This means that this `try_take` will succeed almost all of
394         // the time. There are border cases, however, when the runtime has
395         // *almost* set up the local task, but hasn't quite gotten there yet. In
396         // order to get some better diagnostics, we print on failure and
397         // immediately abort the whole process if there is no local task
398         // available.
399         let opt_task: Option<Box<Task>> = Local::try_take();
400         task = match opt_task {
401             Some(t) => t,
402             None => {
403                 rterrln!("failed at '{}', {}:{}", msg_s, file, line);
404                 if backtrace::log_enabled() {
405                     let mut err = ::rt::util::Stderr;
406                     let _err = backtrace::write(&mut err);
407                 } else {
408                     rterrln!("run with `RUST_BACKTRACE=1` to see a backtrace");
409                 }
410                 unsafe { intrinsics::abort() }
411             }
412         };
413
414         // See comments in io::stdio::with_task_stdout as to why we have to be
415         // careful when using an arbitrary I/O handle from the task. We
416         // essentially need to dance to make sure when a task is in TLS when
417         // running user code.
418         let name = task.name.take();
419         {
420             let n = name.as_ref().map(|n| n.as_slice()).unwrap_or("<unnamed>");
421
422             match task.stderr.take() {
423                 Some(mut stderr) => {
424                     Local::put(task);
425                     // FIXME: what to do when the task printing fails?
426                     let _err = format_args!(|args| ::fmt::writeln(stderr, args),
427                                             "task '{}' failed at '{}', {}:{}",
428                                             n, msg_s, file, line);
429                     if backtrace::log_enabled() {
430                         let _err = backtrace::write(stderr);
431                     }
432                     task = Local::take();
433
434                     match mem::replace(&mut task.stderr, Some(stderr)) {
435                         Some(prev) => {
436                             Local::put(task);
437                             drop(prev);
438                             task = Local::take();
439                         }
440                         None => {}
441                     }
442                 }
443                 None => {
444                     rterrln!("task '{}' failed at '{}', {}:{}", n, msg_s,
445                              file, line);
446                     if backtrace::log_enabled() {
447                         let mut err = ::rt::util::Stderr;
448                         let _err = backtrace::write(&mut err);
449                     }
450                 }
451             }
452         }
453         task.name = name;
454
455         if task.unwinder.unwinding {
456             // If a task fails while it's already unwinding then we
457             // have limited options. Currently our preference is to
458             // just abort. In the future we may consider resuming
459             // unwinding or otherwise exiting the task cleanly.
460             rterrln!("task failed during unwinding (double-failure - total drag!)")
461             rterrln!("rust must abort now. so sorry.");
462
463             // Don't print the backtrace twice (it would have already been
464             // printed if logging was enabled).
465             if !backtrace::log_enabled() {
466                 let mut err = ::rt::util::Stderr;
467                 let _err = backtrace::write(&mut err);
468             }
469             unsafe { intrinsics::abort() }
470         }
471     }
472
473     // The unwinder won't actually use the task at all, so we put the task back
474     // into TLS right before we invoke the unwinder, but this means we need an
475     // unsafe reference back to the unwinder once it's in TLS.
476     Local::put(task);
477     unsafe {
478         let task: *mut Task = Local::unsafe_borrow();
479         (*task).unwinder.begin_unwind(msg);
480     }
481 }