]>
Commit | Line | Data |
---|---|---|
62682a34 SL |
1 | // Copyright 2015 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 | ||
a7813a04 XL |
11 | //! Implementation of panics backed by libgcc/libunwind (in some form) |
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 | |
24 | //! 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 | |
35 | //! exception object being thrown, and to decide whether it should be caught at | |
36 | //! that stack frame. Once the handler frame has been identified, cleanup phase | |
37 | //! begins. | |
38 | //! | |
39 | //! In the cleanup phase, the unwinder invokes each personality routine again. | |
40 | //! This time it decides which (if any) cleanup code needs to be run for | |
41 | //! the current stack frame. If so, the control is transferred to a special | |
42 | //! branch in the function body, the "landing pad", which invokes destructors, | |
43 | //! frees memory, etc. At the end of the landing pad, control is transferred | |
44 | //! back to the unwinder and unwinding resumes. | |
45 | //! | |
46 | //! Once stack has been unwound down to the handler frame level, unwinding stops | |
47 | //! and the last personality routine transfers control to the catch block. | |
48 | //! | |
49 | //! ## `eh_personality` and `eh_unwind_resume` | |
50 | //! | |
51 | //! These language items are used by the compiler when generating unwind info. | |
52 | //! The first one is the personality routine described above. The second one | |
53 | //! allows compilation target to customize the process of resuming unwind at the | |
54 | //! end of the landing pads. `eh_unwind_resume` is used only if | |
55 | //! `custom_unwind_resume` flag in the target options is set. | |
56 | ||
c1a9b12d SL |
57 | #![allow(private_no_mangle_fns)] |
58 | ||
a7813a04 | 59 | use core::any::Any; |
5bcae85e | 60 | use core::ptr; |
a7813a04 | 61 | use alloc::boxed::Box; |
62682a34 | 62 | |
a7813a04 | 63 | use unwind as uw; |
5bcae85e SL |
64 | use libc::{c_int, uintptr_t}; |
65 | use dwarf::eh::{self, EHContext, EHAction}; | |
62682a34 | 66 | |
a7813a04 | 67 | #[repr(C)] |
62682a34 | 68 | struct Exception { |
a7813a04 XL |
69 | _uwe: uw::_Unwind_Exception, |
70 | cause: Option<Box<Any + Send>>, | |
62682a34 SL |
71 | } |
72 | ||
a7813a04 XL |
73 | pub unsafe fn panic(data: Box<Any + Send>) -> u32 { |
74 | let exception = Box::new(Exception { | |
75 | _uwe: uw::_Unwind_Exception { | |
62682a34 SL |
76 | exception_class: rust_exception_class(), |
77 | exception_cleanup: exception_cleanup, | |
78 | private: [0; uw::unwinder_private_data_size], | |
79 | }, | |
80 | cause: Some(data), | |
a7813a04 | 81 | }); |
62682a34 | 82 | let exception_param = Box::into_raw(exception) as *mut uw::_Unwind_Exception; |
a7813a04 | 83 | return uw::_Unwind_RaiseException(exception_param) as u32; |
62682a34 | 84 | |
3157f602 XL |
85 | extern "C" fn exception_cleanup(_unwind_code: uw::_Unwind_Reason_Code, |
86 | exception: *mut uw::_Unwind_Exception) { | |
62682a34 SL |
87 | unsafe { |
88 | let _: Box<Exception> = Box::from_raw(exception as *mut Exception); | |
89 | } | |
90 | } | |
91 | } | |
92 | ||
7453a54e | 93 | pub fn payload() -> *mut u8 { |
5bcae85e | 94 | ptr::null_mut() |
7453a54e SL |
95 | } |
96 | ||
a7813a04 | 97 | pub unsafe fn cleanup(ptr: *mut u8) -> Box<Any + Send> { |
62682a34 | 98 | let my_ep = ptr as *mut Exception; |
62682a34 SL |
99 | let cause = (*my_ep).cause.take(); |
100 | uw::_Unwind_DeleteException(ptr as *mut _); | |
101 | cause.unwrap() | |
102 | } | |
103 | ||
104 | // Rust's exception class identifier. This is used by personality routines to | |
105 | // determine whether the exception was thrown by their own runtime. | |
106 | fn rust_exception_class() -> uw::_Unwind_Exception_Class { | |
107 | // M O Z \0 R U S T -- vendor, language | |
108 | 0x4d4f5a_00_52555354 | |
109 | } | |
110 | ||
62682a34 | 111 | |
5bcae85e SL |
112 | // Register ids were lifted from LLVM's TargetLowering::getExceptionPointerRegister() |
113 | // and TargetLowering::getExceptionSelectorRegister() for each architecture, | |
114 | // then mapped to DWARF register numbers via register definition tables | |
115 | // (typically <arch>RegisterInfo.td, search for "DwarfRegNum"). | |
116 | // See also http://llvm.org/docs/WritingAnLLVMBackend.html#defining-a-register. | |
62682a34 | 117 | |
5bcae85e SL |
118 | #[cfg(target_arch = "x86")] |
119 | const UNWIND_DATA_REG: (i32, i32) = (0, 2); // EAX, EDX | |
62682a34 | 120 | |
5bcae85e SL |
121 | #[cfg(target_arch = "x86_64")] |
122 | const UNWIND_DATA_REG: (i32, i32) = (0, 1); // RAX, RDX | |
62682a34 | 123 | |
5bcae85e SL |
124 | #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] |
125 | const UNWIND_DATA_REG: (i32, i32) = (0, 1); // R0, R1 / X0, X1 | |
62682a34 | 126 | |
9e0c209e | 127 | #[cfg(any(target_arch = "mips", target_arch = "mipsel", target_arch = "mips64"))] |
5bcae85e SL |
128 | const UNWIND_DATA_REG: (i32, i32) = (4, 5); // A0, A1 |
129 | ||
130 | #[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))] | |
131 | const UNWIND_DATA_REG: (i32, i32) = (3, 4); // R3, R4 / X3, X4 | |
132 | ||
9e0c209e SL |
133 | #[cfg(target_arch = "s390x")] |
134 | const UNWIND_DATA_REG: (i32, i32) = (6, 7); // R6, R7 | |
135 | ||
5bcae85e SL |
136 | // The following code is based on GCC's C and C++ personality routines. For reference, see: |
137 | // https://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_personality.cc | |
138 | // https://github.com/gcc-mirror/gcc/blob/trunk/libgcc/unwind-c.c | |
139 | ||
140 | // The personality routine for most of our targets, except ARM, which has a slightly different ABI | |
141 | // (however, iOS goes here as it uses SjLj unwinding). Also, the 64-bit Windows implementation | |
142 | // lives in seh64_gnu.rs | |
143 | #[cfg(all(any(target_os = "ios", not(target_arch = "arm"))))] | |
144 | #[lang = "eh_personality"] | |
145 | #[no_mangle] | |
146 | #[allow(unused)] | |
147 | unsafe extern "C" fn rust_eh_personality(version: c_int, | |
148 | actions: uw::_Unwind_Action, | |
149 | exception_class: uw::_Unwind_Exception_Class, | |
150 | exception_object: *mut uw::_Unwind_Exception, | |
151 | context: *mut uw::_Unwind_Context) | |
152 | -> uw::_Unwind_Reason_Code { | |
153 | if version != 1 { | |
154 | return uw::_URC_FATAL_PHASE1_ERROR; | |
155 | } | |
156 | let eh_action = find_eh_action(context); | |
157 | if actions as i32 & uw::_UA_SEARCH_PHASE as i32 != 0 { | |
158 | match eh_action { | |
159 | EHAction::None | EHAction::Cleanup(_) => return uw::_URC_CONTINUE_UNWIND, | |
160 | EHAction::Catch(_) => return uw::_URC_HANDLER_FOUND, | |
161 | EHAction::Terminate => return uw::_URC_FATAL_PHASE1_ERROR, | |
162 | } | |
163 | } else { | |
164 | match eh_action { | |
165 | EHAction::None => return uw::_URC_CONTINUE_UNWIND, | |
166 | EHAction::Cleanup(lpad) | EHAction::Catch(lpad) => { | |
167 | uw::_Unwind_SetGR(context, UNWIND_DATA_REG.0, exception_object as uintptr_t); | |
168 | uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, 0); | |
169 | uw::_Unwind_SetIP(context, lpad); | |
170 | return uw::_URC_INSTALL_CONTEXT; | |
171 | } | |
172 | EHAction::Terminate => return uw::_URC_FATAL_PHASE2_ERROR, | |
62682a34 SL |
173 | } |
174 | } | |
175 | } | |
176 | ||
5bcae85e SL |
177 | // ARM EHABI personality routine. |
178 | // http://infocenter.arm.com/help/topic/com.arm.doc.ihi0038b/IHI0038B_ehabi.pdf | |
179 | #[cfg(all(target_arch = "arm", not(target_os = "ios")))] | |
180 | #[lang = "eh_personality"] | |
181 | #[no_mangle] | |
182 | unsafe extern "C" fn rust_eh_personality(state: uw::_Unwind_State, | |
183 | exception_object: *mut uw::_Unwind_Exception, | |
184 | context: *mut uw::_Unwind_Context) | |
185 | -> uw::_Unwind_Reason_Code { | |
186 | let state = state as c_int; | |
187 | let action = state & uw::_US_ACTION_MASK as c_int; | |
188 | let search_phase = if action == uw::_US_VIRTUAL_UNWIND_FRAME as c_int { | |
189 | // Backtraces on ARM will call the personality routine with | |
190 | // state == _US_VIRTUAL_UNWIND_FRAME | _US_FORCE_UNWIND. In those cases | |
191 | // we want to continue unwinding the stack, otherwise all our backtraces | |
192 | // would end at __rust_try | |
193 | if state & uw::_US_FORCE_UNWIND as c_int != 0 { | |
194 | return continue_unwind(exception_object, context) | |
195 | } | |
196 | true | |
197 | } else if action == uw::_US_UNWIND_FRAME_STARTING as c_int { | |
198 | false | |
199 | } else if action == uw::_US_UNWIND_FRAME_RESUME as c_int { | |
200 | return continue_unwind(exception_object, context); | |
201 | } else { | |
202 | return uw::_URC_FAILURE; | |
203 | }; | |
62682a34 | 204 | |
5bcae85e SL |
205 | // The DWARF unwinder assumes that _Unwind_Context holds things like the function |
206 | // and LSDA pointers, however ARM EHABI places them into the exception object. | |
207 | // To preserve signatures of functions like _Unwind_GetLanguageSpecificData(), which | |
208 | // take only the context pointer, GCC personality routines stash a pointer to exception_object | |
209 | // in the context, using location reserved for ARM's "scratch register" (r12). | |
210 | uw::_Unwind_SetGR(context, uw::UNWIND_POINTER_REG, exception_object as uw::_Unwind_Ptr); | |
211 | // ...A more principled approach would be to provide the full definition of ARM's | |
212 | // _Unwind_Context in our libunwind bindings and fetch the required data from there directly, | |
213 | // bypassing DWARF compatibility functions. | |
62682a34 | 214 | |
5bcae85e SL |
215 | let eh_action = find_eh_action(context); |
216 | if search_phase { | |
217 | match eh_action { | |
218 | EHAction::None | | |
219 | EHAction::Cleanup(_) => return continue_unwind(exception_object, context), | |
220 | EHAction::Catch(_) => return uw::_URC_HANDLER_FOUND, | |
221 | EHAction::Terminate => return uw::_URC_FAILURE, | |
222 | } | |
223 | } else { | |
224 | match eh_action { | |
225 | EHAction::None => return continue_unwind(exception_object, context), | |
226 | EHAction::Cleanup(lpad) | EHAction::Catch(lpad) => { | |
227 | uw::_Unwind_SetGR(context, UNWIND_DATA_REG.0, exception_object as uintptr_t); | |
228 | uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, 0); | |
229 | uw::_Unwind_SetIP(context, lpad); | |
230 | return uw::_URC_INSTALL_CONTEXT; | |
231 | } | |
232 | EHAction::Terminate => return uw::_URC_FAILURE, | |
233 | } | |
62682a34 SL |
234 | } |
235 | ||
5bcae85e SL |
236 | // On ARM EHABI the personality routine is responsible for actually |
237 | // unwinding a single stack frame before returning (ARM EHABI Sec. 6.1). | |
238 | unsafe fn continue_unwind(exception_object: *mut uw::_Unwind_Exception, | |
239 | context: *mut uw::_Unwind_Context) | |
240 | -> uw::_Unwind_Reason_Code { | |
241 | if __gnu_unwind_frame(exception_object, context) == uw::_URC_NO_REASON { | |
242 | uw::_URC_CONTINUE_UNWIND | |
3157f602 | 243 | } else { |
5bcae85e | 244 | uw::_URC_FAILURE |
62682a34 SL |
245 | } |
246 | } | |
5bcae85e | 247 | // defined in libgcc |
3157f602 | 248 | extern "C" { |
5bcae85e | 249 | fn __gnu_unwind_frame(exception_object: *mut uw::_Unwind_Exception, |
62682a34 | 250 | context: *mut uw::_Unwind_Context) |
3157f602 | 251 | -> uw::_Unwind_Reason_Code; |
62682a34 | 252 | } |
5bcae85e | 253 | } |
62682a34 | 254 | |
5bcae85e SL |
255 | unsafe fn find_eh_action(context: *mut uw::_Unwind_Context) -> EHAction { |
256 | let lsda = uw::_Unwind_GetLanguageSpecificData(context) as *const u8; | |
257 | let mut ip_before_instr: c_int = 0; | |
258 | let ip = uw::_Unwind_GetIPInfo(context, &mut ip_before_instr); | |
259 | let eh_context = EHContext { | |
260 | // The return address points 1 byte past the call instruction, | |
261 | // which could be in the next IP range in LSDA range table. | |
262 | ip: if ip_before_instr != 0 { ip } else { ip - 1 }, | |
263 | func_start: uw::_Unwind_GetRegionStart(context), | |
264 | get_text_start: &|| uw::_Unwind_GetTextRelBase(context), | |
265 | get_data_start: &|| uw::_Unwind_GetDataRelBase(context), | |
266 | }; | |
267 | eh::find_eh_action(lsda, &eh_context) | |
268 | } | |
62682a34 | 269 | |
92a42be0 | 270 | // See docs in the `unwind` module. |
a7813a04 | 271 | #[cfg(all(target_os="windows", target_arch = "x86", target_env="gnu"))] |
92a42be0 SL |
272 | #[lang = "eh_unwind_resume"] |
273 | #[unwind] | |
3157f602 | 274 | unsafe extern "C" fn rust_eh_unwind_resume(panic_ctx: *mut u8) -> ! { |
92a42be0 SL |
275 | uw::_Unwind_Resume(panic_ctx as *mut uw::_Unwind_Exception); |
276 | } | |
277 | ||
a7813a04 XL |
278 | // Frame unwind info registration |
279 | // | |
280 | // Each module's image contains a frame unwind info section (usually | |
281 | // ".eh_frame"). When a module is loaded/unloaded into the process, the | |
282 | // unwinder must be informed about the location of this section in memory. The | |
283 | // methods of achieving that vary by the platform. On some (e.g. Linux), the | |
284 | // unwinder can discover unwind info sections on its own (by dynamically | |
285 | // enumerating currently loaded modules via the dl_iterate_phdr() API and | |
286 | // finding their ".eh_frame" sections); Others, like Windows, require modules | |
287 | // to actively register their unwind info sections via unwinder API. | |
288 | // | |
289 | // This module defines two symbols which are referenced and called from | |
290 | // rsbegin.rs to reigster our information with the GCC runtime. The | |
291 | // implementation of stack unwinding is (for now) deferred to libgcc_eh, however | |
292 | // Rust crates use these Rust-specific entry points to avoid potential clashes | |
293 | // with any GCC runtime. | |
92a42be0 SL |
294 | #[cfg(all(target_os="windows", target_arch = "x86", target_env="gnu"))] |
295 | pub mod eh_frame_registry { | |
92a42be0 | 296 | #[link(name = "gcc_eh")] |
7453a54e | 297 | #[cfg(not(cargobuild))] |
3157f602 | 298 | extern "C" {} |
7453a54e | 299 | |
3157f602 | 300 | extern "C" { |
92a42be0 SL |
301 | fn __register_frame_info(eh_frame_begin: *const u8, object: *mut u8); |
302 | fn __deregister_frame_info(eh_frame_begin: *const u8, object: *mut u8); | |
303 | } | |
a7813a04 | 304 | |
92a42be0 | 305 | #[no_mangle] |
3157f602 | 306 | pub unsafe extern "C" fn rust_eh_register_frames(eh_frame_begin: *const u8, object: *mut u8) { |
92a42be0 SL |
307 | __register_frame_info(eh_frame_begin, object); |
308 | } | |
a7813a04 | 309 | |
92a42be0 | 310 | #[no_mangle] |
3157f602 XL |
311 | pub unsafe extern "C" fn rust_eh_unregister_frames(eh_frame_begin: *const u8, |
312 | object: *mut u8) { | |
92a42be0 SL |
313 | __deregister_frame_info(eh_frame_begin, object); |
314 | } | |
315 | } |