wasmer_vm/libcalls/eh/
gcc.rs1use libunwind as uw;
2
3use super::dwarf::eh::{self, EHAction, EHContext};
4
5static CANARY: u8 = 0;
9const WASMER_EXCEPTION_CLASS: uw::_Unwind_Exception_Class = u64::from_ne_bytes(*b"WMERWASM");
10
11#[repr(C)]
12pub struct UwExceptionWrapper {
13 pub _uwe: uw::_Unwind_Exception,
14 pub canary: *const u8,
15 pub cause: Box<dyn std::any::Any + Send>,
16}
17
18impl UwExceptionWrapper {
19 pub fn new(tag: u64, data_ptr: usize, data_size: u64) -> Self {
20 Self {
21 _uwe: uw::_Unwind_Exception {
22 exception_class: WASMER_EXCEPTION_CLASS,
23 exception_cleanup: None,
24 private_1: core::ptr::null::<u8>() as usize as _,
25 private_2: 0,
26 },
27 canary: &CANARY,
28 cause: Box::new(WasmerException {
29 tag,
30 data_ptr,
31 data_size,
32 }),
33 }
34 }
35}
36
37#[repr(C)]
38#[derive(Debug, thiserror::Error, Clone)]
39#[error("Uncaught exception in wasm code!")]
40pub struct WasmerException {
41 pub tag: u64,
42 pub data_ptr: usize,
43 pub data_size: u64,
44}
45
46impl WasmerException {
47 pub fn new(tag: u64, data_ptr: usize, data_size: u64) -> Self {
48 Self {
49 tag,
50 data_ptr,
51 data_size,
52 }
53 }
54}
55
56#[cfg(target_arch = "x86_64")]
57const UNWIND_DATA_REG: (i32, i32) = (0, 1); #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
60const UNWIND_DATA_REG: (i32, i32) = (0, 1); #[cfg(any(target_arch = "riscv64", target_arch = "riscv32"))]
63const UNWIND_DATA_REG: (i32, i32) = (10, 11); #[cfg(target_arch = "loongarch64")]
66const UNWIND_DATA_REG: (i32, i32) = (4, 5); #[no_mangle]
69pub unsafe extern "C" fn wasmer_eh_personality(
75 version: std::ffi::c_int,
76 actions: uw::_Unwind_Action,
77 exception_class: uw::_Unwind_Exception_Class,
78 exception_object: *mut uw::_Unwind_Exception,
79 context: *mut uw::_Unwind_Context,
80) -> uw::_Unwind_Reason_Code {
81 unsafe {
82 if version != 1 {
83 return uw::_Unwind_Reason_Code__URC_FATAL_PHASE1_ERROR;
84 }
85
86 let uw_exc = std::mem::transmute::<*mut uw::_Unwind_Exception, *mut UwExceptionWrapper>(
87 exception_object,
88 );
89
90 if exception_class != WASMER_EXCEPTION_CLASS {
91 return uw::_Unwind_Reason_Code__URC_CONTINUE_UNWIND;
92 }
93
94 let wasmer_exc = (*uw_exc).cause.downcast_ref::<WasmerException>();
95 let wasmer_exc = match wasmer_exc {
96 Some(e) => e,
97 None => {
98 return uw::_Unwind_Reason_Code__URC_CONTINUE_UNWIND;
99 }
100 };
101
102 let eh_action = match find_eh_action(context, wasmer_exc.tag) {
103 Ok(action) => action,
104 Err(_) => {
105 return uw::_Unwind_Reason_Code__URC_FATAL_PHASE1_ERROR;
106 }
107 };
108
109 if actions as i32 & uw::_Unwind_Action__UA_SEARCH_PHASE as i32 != 0 {
110 match eh_action {
111 EHAction::None | EHAction::Cleanup(_) => {
112 uw::_Unwind_Reason_Code__URC_CONTINUE_UNWIND
113 }
114 EHAction::Catch { .. } | EHAction::Filter { .. } => {
115 uw::_Unwind_Reason_Code__URC_HANDLER_FOUND
116 }
117 EHAction::Terminate => uw::_Unwind_Reason_Code__URC_FATAL_PHASE1_ERROR,
118 }
119 } else {
120 match eh_action {
121 EHAction::None => uw::_Unwind_Reason_Code__URC_CONTINUE_UNWIND,
122 EHAction::Filter { .. }
124 if actions as i32 & uw::_Unwind_Action__UA_FORCE_UNWIND as i32 != 0 =>
125 {
126 uw::_Unwind_Reason_Code__URC_CONTINUE_UNWIND
127 }
128 EHAction::Cleanup(lpad) => {
129 uw::_Unwind_SetGR(context, UNWIND_DATA_REG.0, uw_exc as _);
130 uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, 0);
131 uw::_Unwind_SetIP(context, lpad as usize as _);
132 uw::_Unwind_Reason_Code__URC_INSTALL_CONTEXT
133 }
134 EHAction::Catch { lpad, tag } | EHAction::Filter { lpad, tag } => {
135 uw::_Unwind_SetGR(context, UNWIND_DATA_REG.0, uw_exc as _);
136 #[allow(trivial_numeric_casts)]
137 uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, tag as _);
138 uw::_Unwind_SetIP(context, lpad as usize as _);
139 uw::_Unwind_Reason_Code__URC_INSTALL_CONTEXT
140 }
141 EHAction::Terminate => uw::_Unwind_Reason_Code__URC_FATAL_PHASE2_ERROR,
142 }
143 }
144 }
145}
146
147unsafe fn find_eh_action(context: *mut uw::_Unwind_Context, tag: u64) -> Result<EHAction, ()> {
148 unsafe {
149 let lsda = uw::_Unwind_GetLanguageSpecificData(context) as *const u8;
150 let mut ip_before_instr: std::ffi::c_int = 0;
151 let ip = uw::_Unwind_GetIPInfo(context, &mut ip_before_instr);
152 let eh_context = EHContext {
153 ip: if ip_before_instr != 0 {
158 ip as _
159 } else {
160 ip.wrapping_sub(1) as _
161 },
162 func_start: uw::_Unwind_GetRegionStart(context) as *const _,
163 get_text_start: &|| uw::_Unwind_GetTextRelBase(context) as *const _,
164 get_data_start: &|| uw::_Unwind_GetDataRelBase(context) as *const _,
165 tag,
166 };
167 eh::find_eh_action(lsda, &eh_context)
168 }
169}
170
171pub unsafe fn throw(tag: u64, data_ptr: usize, data_size: u64) -> ! {
172 let exception = Box::new(UwExceptionWrapper::new(tag, data_ptr, data_size));
173 let exception_param = Box::into_raw(exception) as *mut libunwind::_Unwind_Exception;
174
175 match uw::_Unwind_RaiseException(exception_param) {
176 libunwind::_Unwind_Reason_Code__URC_END_OF_STACK => {
177 crate::raise_lib_trap(crate::Trap::lib(wasmer_types::TrapCode::UncaughtException))
178 }
179 _ => {
180 unreachable!()
181 }
182 }
183}
184
185pub unsafe fn rethrow(exc: *mut UwExceptionWrapper) -> ! {
186 if exc.is_null() {
187 panic!();
188 }
189
190 match uw::_Unwind_Resume_or_Rethrow(std::mem::transmute::<
191 *mut UwExceptionWrapper,
192 *mut libunwind::_Unwind_Exception,
193 >(exc))
194 {
195 libunwind::_Unwind_Reason_Code__URC_END_OF_STACK => {
196 crate::raise_lib_trap(crate::Trap::lib(wasmer_types::TrapCode::UncaughtException))
197 }
198 _ => unreachable!(),
199 }
200}