]>
Commit | Line | Data |
---|---|---|
62682a34 SL |
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: | |
c1a9b12d | 17 | //! http://mentorembedded.github.io/cxx-abi/abi-eh.html |
62682a34 SL |
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 | //! | |
92a42be0 SL |
37 | //! In the cleanup phase, the unwinder invokes each personality routine again. |
38 | //! This time it decides which (if any) cleanup code needs to be run for | |
39 | //! the current stack frame. If so, the control is transferred to a special branch | |
40 | //! in the function body, the "landing pad", which invokes destructors, frees memory, | |
41 | //! etc. At the end of the landing pad, control is transferred back to the unwinder | |
42 | //! and unwinding resumes. | |
62682a34 | 43 | //! |
92a42be0 SL |
44 | //! Once stack has been unwound down to the handler frame level, unwinding stops |
45 | //! and the last personality routine transfers control to the catch block. | |
62682a34 | 46 | //! |
92a42be0 | 47 | //! ## `eh_personality` and `eh_unwind_resume` |
62682a34 | 48 | //! |
92a42be0 SL |
49 | //! These language items are used by the compiler when generating unwind info. |
50 | //! The first one is the personality routine described above. The second one | |
51 | //! allows compilation target to customize the process of resuming unwind at the | |
52 | //! end of the landing pads. `eh_unwind_resume` is used only if `custom_unwind_resume` | |
53 | //! flag in the target options is set. | |
62682a34 | 54 | //! |
92a42be0 | 55 | //! ## Frame unwind info registration |
62682a34 | 56 | //! |
92a42be0 SL |
57 | //! Each module's image contains a frame unwind info section (usually ".eh_frame"). |
58 | //! When a module is loaded/unloaded into the process, the unwinder must be informed | |
59 | //! about the location of this section in memory. The methods of achieving that vary | |
60 | //! by the platform. | |
61 | //! On some (e.g. Linux), the unwinder can discover unwind info sections on its own | |
62 | //! (by dynamically enumerating currently loaded modules via the dl_iterate_phdr() API | |
63 | //! and finding their ".eh_frame" sections); | |
64 | //! Others, like Windows, require modules to actively register their unwind info | |
65 | //! sections via unwinder API (see `rust_eh_register_frames`/`rust_eh_unregister_frames`). | |
62682a34 SL |
66 | |
67 | #![allow(dead_code)] | |
68 | #![allow(unused_imports)] | |
69 | ||
70 | use prelude::v1::*; | |
71 | ||
72 | use any::Any; | |
73 | use boxed; | |
62682a34 | 74 | use cmp; |
b039eaaf | 75 | use panicking::{self,PANIC_COUNT}; |
62682a34 SL |
76 | use fmt; |
77 | use intrinsics; | |
62682a34 SL |
78 | use mem; |
79 | use sync::atomic::{self, Ordering}; | |
80 | use sys_common::mutex::Mutex; | |
81 | ||
82 | // The actual unwinding implementation is cfg'd here, and we've got two current | |
83 | // implementations. One goes through SEH on Windows and the other goes through | |
84 | // libgcc via the libunwind-like API. | |
c1a9b12d | 85 | |
e9174d1e SL |
86 | // i686-pc-windows-msvc |
87 | #[cfg(all(windows, target_arch = "x86", target_env = "msvc"))] | |
c1a9b12d SL |
88 | #[path = "seh.rs"] #[doc(hidden)] |
89 | pub mod imp; | |
90 | ||
b039eaaf SL |
91 | // stage0: i686-pc-windows-gnu |
92 | #[cfg(all(stage0, windows, target_arch = "x86_64", target_env = "gnu"))] | |
93 | #[path = "seh64_gnu.rs"] #[doc(hidden)] | |
94 | pub mod imp; | |
95 | ||
96 | // stage0: x86_64-pc-windows-msvc | |
97 | #[cfg(all(stage0, windows, target_arch = "x86_64", target_env = "msvc"))] | |
98 | #[path = "seh.rs"] #[doc(hidden)] | |
99 | pub mod imp; | |
100 | ||
e9174d1e | 101 | // x86_64-pc-windows-* |
b039eaaf | 102 | #[cfg(all(not(stage0), windows, target_arch = "x86_64"))] |
c1a9b12d | 103 | #[path = "seh64_gnu.rs"] #[doc(hidden)] |
62682a34 | 104 | pub mod imp; |
c1a9b12d SL |
105 | |
106 | // i686-pc-windows-gnu and all others | |
e9174d1e | 107 | #[cfg(any(unix, all(windows, target_arch = "x86", target_env = "gnu")))] |
c1a9b12d | 108 | #[path = "gcc.rs"] #[doc(hidden)] |
62682a34 SL |
109 | pub mod imp; |
110 | ||
62682a34 SL |
111 | /// Invoke a closure, capturing the cause of panic if one occurs. |
112 | /// | |
113 | /// This function will return `Ok(())` if the closure did not panic, and will | |
114 | /// return `Err(cause)` if the closure panics. The `cause` returned is the | |
115 | /// object with which panic was originally invoked. | |
116 | /// | |
117 | /// This function also is unsafe for a variety of reasons: | |
118 | /// | |
119 | /// * This is not safe to call in a nested fashion. The unwinding | |
120 | /// interface for Rust is designed to have at most one try/catch block per | |
121 | /// thread, not multiple. No runtime checking is currently performed to uphold | |
122 | /// this invariant, so this function is not safe. A nested try/catch block | |
123 | /// may result in corruption of the outer try/catch block's state, especially | |
124 | /// if this is used within a thread itself. | |
125 | /// | |
126 | /// * It is not sound to trigger unwinding while already unwinding. Rust threads | |
127 | /// have runtime checks in place to ensure this invariant, but it is not | |
128 | /// guaranteed that a rust thread is in place when invoking this function. | |
129 | /// Unwinding twice can lead to resource leaks where some destructors are not | |
130 | /// run. | |
131 | pub unsafe fn try<F: FnOnce()>(f: F) -> Result<(), Box<Any + Send>> { | |
132 | let mut f = Some(f); | |
c1a9b12d | 133 | return inner_try(try_fn::<F>, &mut f as *mut _ as *mut u8); |
62682a34 SL |
134 | |
135 | // If an inner function were not used here, then this generic function `try` | |
136 | // uses the native symbol `rust_try`, for which the code is statically | |
137 | // linked into the standard library. This means that the DLL for the | |
138 | // standard library must have `rust_try` as an exposed symbol that | |
139 | // downstream crates can link against (because monomorphizations of `try` in | |
140 | // downstream crates will have a reference to the `rust_try` symbol). | |
141 | // | |
142 | // On MSVC this requires the symbol `rust_try` to be tagged with | |
143 | // `dllexport`, but it's easier to not have conditional `src/rt/rust_try.ll` | |
144 | // files and instead just have this non-generic shim the compiler can take | |
145 | // care of exposing correctly. | |
c1a9b12d | 146 | unsafe fn inner_try(f: fn(*mut u8), data: *mut u8) |
62682a34 | 147 | -> Result<(), Box<Any + Send>> { |
b039eaaf SL |
148 | PANIC_COUNT.with(|s| { |
149 | let prev = s.get(); | |
150 | s.set(0); | |
151 | let ep = intrinsics::try(f, data); | |
152 | s.set(prev); | |
153 | if ep.is_null() { | |
154 | Ok(()) | |
155 | } else { | |
156 | Err(imp::cleanup(ep)) | |
157 | } | |
158 | }) | |
62682a34 SL |
159 | } |
160 | ||
c1a9b12d | 161 | fn try_fn<F: FnOnce()>(opt_closure: *mut u8) { |
62682a34 SL |
162 | let opt_closure = opt_closure as *mut Option<F>; |
163 | unsafe { (*opt_closure).take().unwrap()(); } | |
164 | } | |
165 | ||
166 | extern { | |
167 | // Rust's try-catch | |
168 | // When f(...) returns normally, the return value is null. | |
169 | // When f(...) throws, the return value is a pointer to the caught | |
170 | // exception object. | |
c1a9b12d SL |
171 | fn rust_try(f: extern fn(*mut u8), |
172 | data: *mut u8) -> *mut u8; | |
62682a34 SL |
173 | } |
174 | } | |
175 | ||
176 | /// Determines whether the current thread is unwinding because of panic. | |
177 | pub fn panicking() -> bool { | |
b039eaaf | 178 | PANIC_COUNT.with(|s| s.get() != 0) |
62682a34 SL |
179 | } |
180 | ||
181 | // An uninlined, unmangled function upon which to slap yer breakpoints | |
182 | #[inline(never)] | |
183 | #[no_mangle] | |
184 | #[allow(private_no_mangle_fns)] | |
185 | fn rust_panic(cause: Box<Any + Send + 'static>) -> ! { | |
62682a34 SL |
186 | unsafe { |
187 | imp::panic(cause) | |
188 | } | |
189 | } | |
190 | ||
191 | #[cfg(not(test))] | |
192 | /// Entry point of panic from the libcore crate. | |
193 | #[lang = "panic_fmt"] | |
e9174d1e | 194 | #[unwind] |
62682a34 SL |
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. | |
92a42be0 SL |
206 | #[unstable(feature = "libstd_sys_internals", |
207 | reason = "used by the panic! macro", | |
208 | issue = "0")] | |
62682a34 SL |
209 | #[inline(never)] #[cold] |
210 | pub fn begin_unwind_fmt(msg: fmt::Arguments, file_line: &(&'static str, u32)) -> ! { | |
211 | use fmt::Write; | |
212 | ||
213 | // We do two allocations here, unfortunately. But (a) they're | |
214 | // required with the current scheme, and (b) we don't handle | |
215 | // panic + OOM properly anyway (see comment in begin_unwind | |
216 | // below). | |
217 | ||
218 | let mut s = String::new(); | |
219 | let _ = s.write_fmt(msg); | |
220 | begin_unwind_inner(Box::new(s), file_line) | |
221 | } | |
222 | ||
223 | /// This is the entry point of unwinding for panic!() and assert!(). | |
92a42be0 SL |
224 | #[unstable(feature = "libstd_sys_internals", |
225 | reason = "used by the panic! macro", | |
226 | issue = "0")] | |
62682a34 SL |
227 | #[inline(never)] #[cold] // avoid code bloat at the call sites as much as possible |
228 | pub fn begin_unwind<M: Any + Send>(msg: M, file_line: &(&'static str, u32)) -> ! { | |
229 | // Note that this should be the only allocation performed in this code path. | |
230 | // Currently this means that panic!() on OOM will invoke this code path, | |
231 | // but then again we're not really ready for panic on OOM anyway. If | |
232 | // we do start doing this, then we should propagate this allocation to | |
233 | // be performed in the parent of this thread instead of the thread that's | |
234 | // panicking. | |
235 | ||
236 | // see below for why we do the `Any` coercion here. | |
237 | begin_unwind_inner(Box::new(msg), file_line) | |
238 | } | |
239 | ||
240 | /// The core of the unwinding. | |
241 | /// | |
242 | /// This is non-generic to avoid instantiation bloat in other crates | |
243 | /// (which makes compilation of small crates noticeably slower). (Note: | |
244 | /// we need the `Any` object anyway, we're not just creating it to | |
245 | /// avoid being generic.) | |
246 | /// | |
247 | /// Doing this split took the LLVM IR line counts of `fn main() { panic!() | |
248 | /// }` from ~1900/3700 (-O/no opts) to 180/590. | |
249 | #[inline(never)] #[cold] // this is the slow path, please never inline this | |
250 | fn begin_unwind_inner(msg: Box<Any + Send>, | |
251 | file_line: &(&'static str, u32)) -> ! { | |
b039eaaf | 252 | let (file, line) = *file_line; |
62682a34 | 253 | |
b039eaaf SL |
254 | // First, invoke the default panic handler. |
255 | panicking::on_panic(&*msg, file, line); | |
62682a34 | 256 | |
b039eaaf | 257 | // Finally, perform the unwinding. |
62682a34 SL |
258 | rust_panic(msg); |
259 | } |