]> git.lizzy.rs Git - rust.git/blob - src/libstd/sys/common/unwind/mod.rs
ff93d0526b7efaea749dfbd756adf3f69d7c8ab8
[rust.git] / src / libstd / sys / common / unwind / mod.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 //! Implementation of Rust stack unwinding
12 //!
13 //! For background on exception handling and stack unwinding please see
14 //! "Exception Handling in LLVM" (llvm.org/docs/ExceptionHandling.html) and
15 //! documents linked from it.
16 //! These are also good reads:
17 //!     http://mentorembedded.github.io/cxx-abi/abi-eh.html
18 //!     http://monoinfinito.wordpress.com/series/exception-handling-in-c/
19 //!     http://www.airs.com/blog/index.php?s=exception+frames
20 //!
21 //! ## A brief summary
22 //!
23 //! Exception handling happens in two phases: a search phase and a cleanup phase.
24 //!
25 //! In both phases the unwinder walks stack frames from top to bottom using
26 //! information from the stack frame unwind sections of the current process's
27 //! modules ("module" here refers to an OS module, i.e. an executable or a
28 //! dynamic library).
29 //!
30 //! For each stack frame, it invokes the associated "personality routine", whose
31 //! address is also stored in the unwind info section.
32 //!
33 //! In the search phase, the job of a personality routine is to examine exception
34 //! object being thrown, and to decide whether it should be caught at that stack
35 //! frame.  Once the handler frame has been identified, cleanup phase begins.
36 //!
37 //! In the cleanup phase, personality routines invoke cleanup code associated
38 //! with their stack frames (i.e. destructors).  Once stack has been unwound down
39 //! to the handler frame level, unwinding stops and the last personality routine
40 //! transfers control to its catch block.
41 //!
42 //! ## Frame unwind info registration
43 //!
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 #![allow(dead_code)]
61 #![allow(unused_imports)]
62
63 use prelude::v1::*;
64
65 use any::Any;
66 use boxed;
67 use cell::Cell;
68 use cmp;
69 use panicking;
70 use fmt;
71 use intrinsics;
72 use mem;
73 use sync::atomic::{self, Ordering};
74 use sys_common::mutex::Mutex;
75
76 // The actual unwinding implementation is cfg'd here, and we've got two current
77 // implementations. One goes through SEH on Windows and the other goes through
78 // libgcc via the libunwind-like API.
79
80 // i686-pc-windows-msvc
81 #[cfg(all(windows, target_arch = "x86", target_env = "msvc"))]
82 #[path = "seh.rs"] #[doc(hidden)]
83 pub mod imp;
84
85 // x86_64-pc-windows-*
86 #[cfg(all(windows, target_arch = "x86_64"))]
87 #[path = "seh64_gnu.rs"] #[doc(hidden)]
88 pub mod imp;
89
90 // i686-pc-windows-gnu and all others
91 #[cfg(any(unix, all(windows, target_arch = "x86", target_env = "gnu")))]
92 #[path = "gcc.rs"] #[doc(hidden)]
93 pub mod imp;
94
95 pub type Callback = fn(msg: &(Any + Send), file: &'static str, line: u32);
96
97 // Variables used for invoking callbacks when a thread starts to unwind.
98 //
99 // For more information, see below.
100 const MAX_CALLBACKS: usize = 16;
101 static CALLBACKS: [atomic::AtomicUsize; MAX_CALLBACKS] =
102         [atomic::AtomicUsize::new(0), atomic::AtomicUsize::new(0),
103          atomic::AtomicUsize::new(0), atomic::AtomicUsize::new(0),
104          atomic::AtomicUsize::new(0), atomic::AtomicUsize::new(0),
105          atomic::AtomicUsize::new(0), atomic::AtomicUsize::new(0),
106          atomic::AtomicUsize::new(0), atomic::AtomicUsize::new(0),
107          atomic::AtomicUsize::new(0), atomic::AtomicUsize::new(0),
108          atomic::AtomicUsize::new(0), atomic::AtomicUsize::new(0),
109          atomic::AtomicUsize::new(0), atomic::AtomicUsize::new(0)];
110 static CALLBACK_CNT: atomic::AtomicUsize = atomic::AtomicUsize::new(0);
111
112 thread_local! { static PANICKING: Cell<bool> = Cell::new(false) }
113
114 /// Invoke a closure, capturing the cause of panic if one occurs.
115 ///
116 /// This function will return `Ok(())` if the closure did not panic, and will
117 /// return `Err(cause)` if the closure panics. The `cause` returned is the
118 /// object with which panic was originally invoked.
119 ///
120 /// This function also is unsafe for a variety of reasons:
121 ///
122 /// * This is not safe to call in a nested fashion. The unwinding
123 ///   interface for Rust is designed to have at most one try/catch block per
124 ///   thread, not multiple. No runtime checking is currently performed to uphold
125 ///   this invariant, so this function is not safe. A nested try/catch block
126 ///   may result in corruption of the outer try/catch block's state, especially
127 ///   if this is used within a thread itself.
128 ///
129 /// * It is not sound to trigger unwinding while already unwinding. Rust threads
130 ///   have runtime checks in place to ensure this invariant, but it is not
131 ///   guaranteed that a rust thread is in place when invoking this function.
132 ///   Unwinding twice can lead to resource leaks where some destructors are not
133 ///   run.
134 pub unsafe fn try<F: FnOnce()>(f: F) -> Result<(), Box<Any + Send>> {
135     let mut f = Some(f);
136     return inner_try(try_fn::<F>, &mut f as *mut _ as *mut u8);
137
138     // If an inner function were not used here, then this generic function `try`
139     // uses the native symbol `rust_try`, for which the code is statically
140     // linked into the standard library. This means that the DLL for the
141     // standard library must have `rust_try` as an exposed symbol that
142     // downstream crates can link against (because monomorphizations of `try` in
143     // downstream crates will have a reference to the `rust_try` symbol).
144     //
145     // On MSVC this requires the symbol `rust_try` to be tagged with
146     // `dllexport`, but it's easier to not have conditional `src/rt/rust_try.ll`
147     // files and instead just have this non-generic shim the compiler can take
148     // care of exposing correctly.
149     unsafe fn inner_try(f: fn(*mut u8), data: *mut u8)
150                         -> Result<(), Box<Any + Send>> {
151         let prev = PANICKING.with(|s| s.get());
152         PANICKING.with(|s| s.set(false));
153         let ep = intrinsics::try(f, data);
154         PANICKING.with(|s| s.set(prev));
155         if ep.is_null() {
156             Ok(())
157         } else {
158             Err(imp::cleanup(ep))
159         }
160     }
161
162     fn try_fn<F: FnOnce()>(opt_closure: *mut u8) {
163         let opt_closure = opt_closure as *mut Option<F>;
164         unsafe { (*opt_closure).take().unwrap()(); }
165     }
166
167     extern {
168         // Rust's try-catch
169         // When f(...) returns normally, the return value is null.
170         // When f(...) throws, the return value is a pointer to the caught
171         // exception object.
172         fn rust_try(f: extern fn(*mut u8),
173                     data: *mut u8) -> *mut u8;
174     }
175 }
176
177 /// Determines whether the current thread is unwinding because of panic.
178 pub fn panicking() -> bool {
179     PANICKING.with(|s| s.get())
180 }
181
182 // An uninlined, unmangled function upon which to slap yer breakpoints
183 #[inline(never)]
184 #[no_mangle]
185 #[allow(private_no_mangle_fns)]
186 fn rust_panic(cause: Box<Any + Send + 'static>) -> ! {
187     unsafe {
188         imp::panic(cause)
189     }
190 }
191
192 #[cfg(not(test))]
193 /// Entry point of panic from the libcore crate.
194 #[lang = "panic_fmt"]
195 pub extern fn rust_begin_unwind(msg: fmt::Arguments,
196                                 file: &'static str, line: u32) -> ! {
197     begin_unwind_fmt(msg, &(file, line))
198 }
199
200 /// The entry point for unwinding with a formatted message.
201 ///
202 /// This is designed to reduce the amount of code required at the call
203 /// site as much as possible (so that `panic!()` has as low an impact
204 /// on (e.g.) the inlining of other functions as possible), by moving
205 /// the actual formatting into this shared place.
206 #[inline(never)] #[cold]
207 pub fn begin_unwind_fmt(msg: fmt::Arguments, file_line: &(&'static str, u32)) -> ! {
208     use fmt::Write;
209
210     // We do two allocations here, unfortunately. But (a) they're
211     // required with the current scheme, and (b) we don't handle
212     // panic + OOM properly anyway (see comment in begin_unwind
213     // below).
214
215     let mut s = String::new();
216     let _ = s.write_fmt(msg);
217     begin_unwind_inner(Box::new(s), file_line)
218 }
219
220 /// This is the entry point of unwinding for panic!() and assert!().
221 #[inline(never)] #[cold] // avoid code bloat at the call sites as much as possible
222 pub fn begin_unwind<M: Any + Send>(msg: M, file_line: &(&'static str, u32)) -> ! {
223     // Note that this should be the only allocation performed in this code path.
224     // Currently this means that panic!() on OOM will invoke this code path,
225     // but then again we're not really ready for panic on OOM anyway. If
226     // we do start doing this, then we should propagate this allocation to
227     // be performed in the parent of this thread instead of the thread that's
228     // panicking.
229
230     // see below for why we do the `Any` coercion here.
231     begin_unwind_inner(Box::new(msg), file_line)
232 }
233
234 /// The core of the unwinding.
235 ///
236 /// This is non-generic to avoid instantiation bloat in other crates
237 /// (which makes compilation of small crates noticeably slower). (Note:
238 /// we need the `Any` object anyway, we're not just creating it to
239 /// avoid being generic.)
240 ///
241 /// Doing this split took the LLVM IR line counts of `fn main() { panic!()
242 /// }` from ~1900/3700 (-O/no opts) to 180/590.
243 #[inline(never)] #[cold] // this is the slow path, please never inline this
244 fn begin_unwind_inner(msg: Box<Any + Send>,
245                       file_line: &(&'static str, u32)) -> ! {
246     // Make sure the default failure handler is registered before we look at the
247     // callbacks. We also use a raw sys-based mutex here instead of a
248     // `std::sync` one as accessing TLS can cause weird recursive problems (and
249     // we don't need poison checking).
250     unsafe {
251         static LOCK: Mutex = Mutex::new();
252         static mut INIT: bool = false;
253         LOCK.lock();
254         if !INIT {
255             register(panicking::on_panic);
256             INIT = true;
257         }
258         LOCK.unlock();
259     }
260
261     // First, invoke call the user-defined callbacks triggered on thread panic.
262     //
263     // By the time that we see a callback has been registered (by reading
264     // MAX_CALLBACKS), the actual callback itself may have not been stored yet,
265     // so we just chalk it up to a race condition and move on to the next
266     // callback. Additionally, CALLBACK_CNT may briefly be higher than
267     // MAX_CALLBACKS, so we're sure to clamp it as necessary.
268     let callbacks = {
269         let amt = CALLBACK_CNT.load(Ordering::SeqCst);
270         &CALLBACKS[..cmp::min(amt, MAX_CALLBACKS)]
271     };
272     for cb in callbacks {
273         match cb.load(Ordering::SeqCst) {
274             0 => {}
275             n => {
276                 let f: Callback = unsafe { mem::transmute(n) };
277                 let (file, line) = *file_line;
278                 f(&*msg, file, line);
279             }
280         }
281     };
282
283     // Now that we've run all the necessary unwind callbacks, we actually
284     // perform the unwinding.
285     if panicking() {
286         // If a thread panics while it's already unwinding then we
287         // have limited options. Currently our preference is to
288         // just abort. In the future we may consider resuming
289         // unwinding or otherwise exiting the thread cleanly.
290         super::util::dumb_print(format_args!("thread panicked while panicking. \
291                                               aborting."));
292         unsafe { intrinsics::abort() }
293     }
294     PANICKING.with(|s| s.set(true));
295     rust_panic(msg);
296 }
297
298 /// Register a callback to be invoked when a thread unwinds.
299 ///
300 /// This is an unsafe and experimental API which allows for an arbitrary
301 /// callback to be invoked when a thread panics. This callback is invoked on both
302 /// the initial unwinding and a double unwinding if one occurs. Additionally,
303 /// the local `Thread` will be in place for the duration of the callback, and
304 /// the callback must ensure that it remains in place once the callback returns.
305 ///
306 /// Only a limited number of callbacks can be registered, and this function
307 /// returns whether the callback was successfully registered or not. It is not
308 /// currently possible to unregister a callback once it has been registered.
309 pub unsafe fn register(f: Callback) -> bool {
310     match CALLBACK_CNT.fetch_add(1, Ordering::SeqCst) {
311         // The invocation code has knowledge of this window where the count has
312         // been incremented, but the callback has not been stored. We're
313         // guaranteed that the slot we're storing into is 0.
314         n if n < MAX_CALLBACKS => {
315             let prev = CALLBACKS[n].swap(mem::transmute(f), Ordering::SeqCst);
316             rtassert!(prev == 0);
317             true
318         }
319         // If we accidentally bumped the count too high, pull it back.
320         _ => {
321             CALLBACK_CNT.store(MAX_CALLBACKS, Ordering::SeqCst);
322             false
323         }
324     }
325 }