|
| 1 | +//! Implementation of panics backed by libgcc/libunwind (in some form). |
| 2 | +//! |
| 3 | +//! For background on exception handling and stack unwinding please see |
| 4 | +//! "Exception Handling in LLVM" (llvm.org/docs/ExceptionHandling.html) and |
| 5 | +//! documents linked from it. |
| 6 | +//! These are also good reads: |
| 7 | +//! * <https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html> |
| 8 | +//! * <https://monoinfinito.wordpress.com/series/exception-handling-in-c/> |
| 9 | +//! * <https://www.airs.com/blog/index.php?s=exception+frames> |
| 10 | +//! |
| 11 | +//! ## A brief summary |
| 12 | +//! |
| 13 | +//! Exception handling happens in two phases: a search phase and a cleanup |
| 14 | +//! phase. |
| 15 | +//! |
| 16 | +//! In both phases the unwinder walks stack frames from top to bottom using |
| 17 | +//! information from the stack frame unwind sections of the current process's |
| 18 | +//! modules ("module" here refers to an OS module, i.e., an executable or a |
| 19 | +//! dynamic library). |
| 20 | +//! |
| 21 | +//! For each stack frame, it invokes the associated "personality routine", whose |
| 22 | +//! address is also stored in the unwind info section. |
| 23 | +//! |
| 24 | +//! In the search phase, the job of a personality routine is to examine |
| 25 | +//! exception object being thrown, and to decide whether it should be caught at |
| 26 | +//! that stack frame. Once the handler frame has been identified, cleanup phase |
| 27 | +//! begins. |
| 28 | +//! |
| 29 | +//! In the cleanup phase, the unwinder invokes each personality routine again. |
| 30 | +//! This time it decides which (if any) cleanup code needs to be run for |
| 31 | +//! the current stack frame. If so, the control is transferred to a special |
| 32 | +//! branch in the function body, the "landing pad", which invokes destructors, |
| 33 | +//! frees memory, etc. At the end of the landing pad, control is transferred |
| 34 | +//! back to the unwinder and unwinding resumes. |
| 35 | +//! |
| 36 | +//! Once stack has been unwound down to the handler frame level, unwinding stops |
| 37 | +//! and the last personality routine transfers control to the catch block. |
| 38 | +
|
| 39 | +#![allow(nonstandard_style)] |
| 40 | + |
| 41 | +use libc::c_int; |
| 42 | + |
| 43 | +// The following code is based on GCC's C and C++ personality routines. For reference, see: |
| 44 | +// https://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_personality.cc |
| 45 | +// https://github.com/gcc-mirror/gcc/blob/trunk/libgcc/unwind-c.c |
| 46 | + |
| 47 | +cfg_if::cfg_if! { |
| 48 | + if #[cfg(all(target_arch = "arm", not(target_os = "ios"), not(target_os = "netbsd")))] { |
| 49 | + // ARM EHABI personality routine. |
| 50 | + // https://infocenter.arm.com/help/topic/com.arm.doc.ihi0038b/IHI0038B_ehabi.pdf |
| 51 | + // |
| 52 | + // iOS uses the default routine instead since it uses SjLj unwinding. |
| 53 | + #[rustc_std_internal_symbol] |
| 54 | + unsafe extern "C" fn rust_eh_personality(_state: _Unwind_State, |
| 55 | + _exception_object: *mut _Unwind_Exception, |
| 56 | + _context: *mut _Unwind_Context) |
| 57 | + -> _Unwind_Reason_Code { |
| 58 | + crate::do_abort() |
| 59 | + } |
| 60 | + } else { |
| 61 | + cfg_if::cfg_if! { |
| 62 | + if #[cfg(all(windows, target_arch = "x86_64", target_env = "gnu"))] { |
| 63 | + // On x86_64 MinGW targets, the unwinding mechanism is SEH however the unwind |
| 64 | + // handler data (aka LSDA) uses GCC-compatible encoding. |
| 65 | + #[rustc_std_internal_symbol] |
| 66 | + #[allow(nonstandard_style)] |
| 67 | + unsafe extern "C" fn rust_eh_personality(_exceptionRecord: *mut EXCEPTION_RECORD, |
| 68 | + _establisherFrame: LPVOID, |
| 69 | + _contextRecord: *mut CONTEXT, |
| 70 | + _dispatcherContext: *mut DISPATCHER_CONTEXT) |
| 71 | + -> EXCEPTION_DISPOSITION { |
| 72 | + crate::do_abort(); |
| 73 | + } |
| 74 | + } else { |
| 75 | + // The personality routine for most of our targets. |
| 76 | + #[rustc_std_internal_symbol] |
| 77 | + unsafe extern "C" fn rust_eh_personality(_version: c_int, |
| 78 | + _actions: _Unwind_Action, |
| 79 | + _exception_class: _Unwind_Exception_Class, |
| 80 | + _exception_object: *mut _Unwind_Exception, |
| 81 | + _context: *mut _Unwind_Context) |
| 82 | + -> _Unwind_Reason_Code { |
| 83 | + crate::do_abort(); |
| 84 | + } |
| 85 | + } |
| 86 | + } |
| 87 | + } |
| 88 | +} |
| 89 | + |
| 90 | +// Frame unwind info registration |
| 91 | +// |
| 92 | +// Each module's image contains a frame unwind info section (usually |
| 93 | +// ".eh_frame"). When a module is loaded/unloaded into the process, the |
| 94 | +// unwinder must be informed about the location of this section in memory. The |
| 95 | +// methods of achieving that vary by the platform. On some (e.g., Linux), the |
| 96 | +// unwinder can discover unwind info sections on its own (by dynamically |
| 97 | +// enumerating currently loaded modules via the dl_iterate_phdr() API and |
| 98 | +// finding their ".eh_frame" sections); Others, like Windows, require modules |
| 99 | +// to actively register their unwind info sections via unwinder API. |
| 100 | +// |
| 101 | +// This module defines two symbols which are referenced and called from |
| 102 | +// rsbegin.rs to register our information with the GCC runtime. The |
| 103 | +// implementation of stack unwinding is (for now) deferred to libgcc_eh, however |
| 104 | +// Rust crates use these Rust-specific entry points to avoid potential clashes |
| 105 | +// with any GCC runtime. |
| 106 | +#[cfg(all(target_os = "windows", target_arch = "x86", target_env = "gnu"))] |
| 107 | +mod eh_frame_registry { |
| 108 | + extern "C" { |
| 109 | + fn __register_frame_info(eh_frame_begin: *const u8, object: *mut u8); |
| 110 | + fn __deregister_frame_info(eh_frame_begin: *const u8, object: *mut u8); |
| 111 | + } |
| 112 | + |
| 113 | + #[rustc_std_internal_symbol] |
| 114 | + unsafe extern "C" fn rust_eh_register_frames(eh_frame_begin: *const u8, object: *mut u8) { |
| 115 | + __register_frame_info(eh_frame_begin, object); |
| 116 | + } |
| 117 | + |
| 118 | + #[rustc_std_internal_symbol] |
| 119 | + unsafe extern "C" fn rust_eh_unregister_frames(eh_frame_begin: *const u8, object: *mut u8) { |
| 120 | + __deregister_frame_info(eh_frame_begin, object); |
| 121 | + } |
| 122 | +} |
| 123 | + |
| 124 | +use libc::{c_void, uintptr_t}; |
| 125 | + |
| 126 | +#[repr(C)] |
| 127 | +#[derive(Debug, Copy, Clone, PartialEq)] |
| 128 | +enum _Unwind_Reason_Code { |
| 129 | + _URC_NO_REASON = 0, |
| 130 | + _URC_FOREIGN_EXCEPTION_CAUGHT = 1, |
| 131 | + _URC_FATAL_PHASE2_ERROR = 2, |
| 132 | + _URC_FATAL_PHASE1_ERROR = 3, |
| 133 | + _URC_NORMAL_STOP = 4, |
| 134 | + _URC_END_OF_STACK = 5, |
| 135 | + _URC_HANDLER_FOUND = 6, |
| 136 | + _URC_INSTALL_CONTEXT = 7, |
| 137 | + _URC_CONTINUE_UNWIND = 8, |
| 138 | + _URC_FAILURE = 9, // used only by ARM EHABI |
| 139 | +} |
| 140 | + |
| 141 | +type _Unwind_Exception_Class = u64; |
| 142 | +type _Unwind_Word = uintptr_t; |
| 143 | +type _Unwind_Ptr = uintptr_t; |
| 144 | +type _Unwind_Trace_Fn = |
| 145 | + extern "C" fn(ctx: *mut _Unwind_Context, arg: *mut c_void) -> _Unwind_Reason_Code; |
| 146 | + |
| 147 | +enum _Unwind_Exception {} |
| 148 | + |
| 149 | +enum _Unwind_Context {} |
| 150 | + |
| 151 | +type _Unwind_Exception_Cleanup_Fn = |
| 152 | + extern "C" fn(unwind_code: _Unwind_Reason_Code, exception: *mut _Unwind_Exception); |
| 153 | + |
| 154 | +cfg_if::cfg_if! { |
| 155 | +if #[cfg(any(target_os = "ios", target_os = "netbsd", not(target_arch = "arm")))] { |
| 156 | + // Not ARM EHABI |
| 157 | + #[repr(C)] |
| 158 | + #[derive(Copy, Clone, PartialEq)] |
| 159 | + enum _Unwind_Action { |
| 160 | + _UA_SEARCH_PHASE = 1, |
| 161 | + _UA_CLEANUP_PHASE = 2, |
| 162 | + _UA_HANDLER_FRAME = 4, |
| 163 | + _UA_FORCE_UNWIND = 8, |
| 164 | + _UA_END_OF_STACK = 16, |
| 165 | + } |
| 166 | + |
| 167 | +} else { |
| 168 | + // ARM EHABI |
| 169 | + #[repr(C)] |
| 170 | + #[derive(Copy, Clone, PartialEq)] |
| 171 | + enum _Unwind_State { |
| 172 | + _US_VIRTUAL_UNWIND_FRAME = 0, |
| 173 | + _US_UNWIND_FRAME_STARTING = 1, |
| 174 | + _US_UNWIND_FRAME_RESUME = 2, |
| 175 | + _US_ACTION_MASK = 3, |
| 176 | + _US_FORCE_UNWIND = 8, |
| 177 | + _US_END_OF_STACK = 16, |
| 178 | + } |
| 179 | +} |
| 180 | +} // cfg_if! |
| 181 | + |
| 182 | +cfg_if::cfg_if! { |
| 183 | +if #[cfg(all(windows, target_arch = "x86_64", target_env = "gnu"))] { |
| 184 | + // We declare these as opaque types. This is fine since you just need to |
| 185 | + // pass them to _GCC_specific_handler and forget about them. |
| 186 | + enum EXCEPTION_RECORD {} |
| 187 | + type LPVOID = *mut c_void; |
| 188 | + enum CONTEXT {} |
| 189 | + enum DISPATCHER_CONTEXT {} |
| 190 | + type EXCEPTION_DISPOSITION = c_int; |
| 191 | +} |
| 192 | +} // cfg_if! |
0 commit comments