1use std::ffi::c_void;
38
39use libunwind::{self as uw};
40use wasmer_types::TagIndex;
41
42use crate::{InternalStoreHandle, StoreHandle, StoreObjects, VMContext, VMExceptionRef};
43
44use super::dwarf::eh::{self, EHAction, EHContext};
45
46static CANARY: u8 = 0;
50const WASMER_EXCEPTION_CLASS: uw::_Unwind_Exception_Class = u64::from_ne_bytes(*b"WMERWASM");
51
52const CATCH_ALL_TAG_VALUE: i32 = i32::MAX;
53const NO_MATCH_FOUND_TAG_VALUE: i32 = i32::MAX - 1;
57
58#[repr(C)]
59pub struct UwExceptionWrapper {
60 pub _uwe: uw::_Unwind_Exception,
61 pub canary: *const u8,
62 pub exnref: u32,
63
64 pub current_frame_info: Option<Box<CurrentFrameInfo>>,
66}
67
68#[repr(C)]
69pub struct CurrentFrameInfo {
70 pub catch_tags: Vec<u32>,
71 pub has_catch_all: bool,
72}
73
74impl UwExceptionWrapper {
75 pub fn new(exnref: u32) -> Self {
76 Self {
77 _uwe: uw::_Unwind_Exception {
78 exception_class: WASMER_EXCEPTION_CLASS,
79 exception_cleanup: Some(deallocate_exception),
80 private_1: core::ptr::null::<u8>() as usize as _,
81 private_2: 0,
82 },
83 canary: &CANARY,
84 exnref,
85 current_frame_info: None,
86 }
87 }
88}
89
90#[cfg(target_arch = "x86_64")]
91const UNWIND_DATA_REG: (i32, i32) = (0, 1); #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
94const UNWIND_DATA_REG: (i32, i32) = (0, 1); #[cfg(any(target_arch = "riscv64", target_arch = "riscv32"))]
97const UNWIND_DATA_REG: (i32, i32) = (10, 11); #[cfg(target_arch = "loongarch64")]
100const UNWIND_DATA_REG: (i32, i32) = (4, 5); #[cfg(target_arch = "powerpc64")]
103const UNWIND_DATA_REG: (i32, i32) = (3, 4); macro_rules! log {
106 ($e: expr) => {
107 if false {
108 eprintln!($e)
109 }
110
111 };
112
113 ($($e: expr),*) => {
114 if false {
115 eprintln!($($e),*)
116 }
117
118 };
119}
120
121#[unsafe(no_mangle)]
122pub unsafe extern "C" fn wasmer_eh_personality(
128 version: std::ffi::c_int,
129 actions: uw::_Unwind_Action,
130 exception_class: uw::_Unwind_Exception_Class,
131 exception_object: *mut uw::_Unwind_Exception,
132 context: *mut uw::_Unwind_Context,
133) -> uw::_Unwind_Reason_Code {
134 unsafe {
135 if version != 1 {
136 return uw::_Unwind_Reason_Code__URC_FATAL_PHASE1_ERROR;
137 }
138
139 let uw_exc = std::mem::transmute::<*mut uw::_Unwind_Exception, *mut UwExceptionWrapper>(
140 exception_object,
141 );
142
143 if exception_class != WASMER_EXCEPTION_CLASS {
144 return uw::_Unwind_Reason_Code__URC_CONTINUE_UNWIND;
145 }
146
147 let eh_action = match find_eh_action(context) {
148 Ok(action) => action,
149 Err(_) => {
150 return uw::_Unwind_Reason_Code__URC_FATAL_PHASE1_ERROR;
151 }
152 };
153
154 let is_catch_specific_or_all = matches!(&eh_action, EHAction::CatchSpecificOrAll { .. });
155 log!("[wasmer][eh] stage1 action: {:?}", eh_action);
156
157 if actions as i32 & uw::_Unwind_Action__UA_SEARCH_PHASE as i32 != 0 {
158 match eh_action {
159 EHAction::None => uw::_Unwind_Reason_Code__URC_CONTINUE_UNWIND,
160 EHAction::CatchAll { .. }
161 | EHAction::CatchSpecific { .. }
162 | EHAction::CatchSpecificOrAll { .. } => uw::_Unwind_Reason_Code__URC_HANDLER_FOUND,
163 EHAction::Terminate => uw::_Unwind_Reason_Code__URC_FATAL_PHASE1_ERROR,
164 }
165 } else {
166 match eh_action {
169 EHAction::None => uw::_Unwind_Reason_Code__URC_CONTINUE_UNWIND,
170 EHAction::CatchAll { lpad } => {
171 uw::_Unwind_SetGR(context, UNWIND_DATA_REG.0, uw_exc as _);
172 uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, 0);
174 uw::_Unwind_SetIP(context, lpad as usize as _);
175 uw::_Unwind_Reason_Code__URC_INSTALL_CONTEXT
176 }
177 EHAction::CatchSpecific { lpad, tags }
178 | EHAction::CatchSpecificOrAll { lpad, tags } => {
179 log!(
180 "[wasmer][eh] stage1 catch tags={:?} catch_all={}",
181 tags,
182 is_catch_specific_or_all
183 );
184 (*uw_exc).current_frame_info = Some(Box::new(CurrentFrameInfo {
185 catch_tags: tags,
186 has_catch_all: is_catch_specific_or_all,
187 }));
188 uw::_Unwind_SetGR(context, UNWIND_DATA_REG.0, uw_exc as _);
189 uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, 1);
191 uw::_Unwind_SetIP(context, lpad as usize as _);
192 uw::_Unwind_Reason_Code__URC_INSTALL_CONTEXT
193 }
194 EHAction::Terminate => uw::_Unwind_Reason_Code__URC_FATAL_PHASE2_ERROR,
195 }
196 }
197 }
198}
199
200#[unsafe(no_mangle)]
201pub unsafe extern "C" fn wasmer_eh_personality2(
208 vmctx: *mut VMContext,
209 exception_object: *mut UwExceptionWrapper,
210) -> i32 {
211 unsafe {
212 let Some(current_frame_info) = (*exception_object).current_frame_info.take() else {
213 unreachable!("wasmer_eh_personality2 called without current_frame_info");
215 };
216
217 log!(
218 "[wasmer][eh] stage2 catch_tags={:?} has_catch_all={}",
219 current_frame_info.catch_tags,
220 current_frame_info.has_catch_all
221 );
222
223 let instance = (*vmctx).instance();
224 let exn = super::exn_obj_from_exnref(vmctx, (*exception_object).exnref);
225
226 for tag in current_frame_info.catch_tags {
227 log!("[wasmer][eh] stage2 checking tag {}", tag);
228 let unique_tag = instance.shared_tag_ptr(TagIndex::from_u32(tag)).index();
229 if unique_tag == (*exn).tag_index() {
230 return tag as i32;
231 }
232 }
233
234 if current_frame_info.has_catch_all {
235 log!("[wasmer][eh] stage2 falling back to catch-all");
236 CATCH_ALL_TAG_VALUE
237 } else {
238 log!("[wasmer][eh] stage2 found no match");
239 NO_MATCH_FOUND_TAG_VALUE
240 }
241 }
242}
243
244unsafe fn find_eh_action(context: *mut uw::_Unwind_Context) -> Result<EHAction, ()> {
245 unsafe {
246 let lsda = uw::_Unwind_GetLanguageSpecificData(context) as *const u8;
247 let mut ip_before_instr: std::ffi::c_int = 0;
248 let ip = uw::_Unwind_GetIPInfo(context, &mut ip_before_instr);
249 let eh_context = EHContext {
250 ip: if ip_before_instr != 0 {
255 ip as _
256 } else {
257 ip.wrapping_sub(1) as _
258 },
259 func_start: uw::_Unwind_GetRegionStart(context) as *const _,
260 get_text_start: &|| uw::_Unwind_GetTextRelBase(context) as *const _,
261 get_data_start: &|| uw::_Unwind_GetDataRelBase(context) as *const _,
262 };
263 eh::find_eh_action(lsda, &eh_context)
264 }
265}
266
267pub unsafe fn read_exnref(exception: *mut c_void) -> u32 {
268 if exception.is_null() {
269 0
270 } else {
271 unsafe { (*(exception as *mut UwExceptionWrapper)).exnref }
272 }
273}
274
275pub unsafe fn throw(ctx: &StoreObjects, exnref: u32) -> ! {
279 unsafe {
280 log!("[wasmer][eh] throw resolved exnref={exnref}");
281 if exnref == 0 {
282 crate::raise_lib_trap(crate::Trap::lib(
283 wasmer_types::TrapCode::UninitializedExnRef,
284 ))
285 }
286
287 let exception = Box::new(UwExceptionWrapper::new(exnref));
288 let exception_ptr = Box::into_raw(exception);
289
290 match uw::_Unwind_RaiseException(exception_ptr as *mut libunwind::_Unwind_Exception) {
291 libunwind::_Unwind_Reason_Code__URC_END_OF_STACK => {
292 delete_exception(exception_ptr as *mut c_void);
293
294 let exnref = VMExceptionRef(StoreHandle::from_internal(
295 ctx.id(),
296 InternalStoreHandle::from_index(exnref as usize).unwrap(),
297 ));
298 log!(
299 "[wasmer][eh] throw -> URC_END_OF_STACK (personality={:p})",
300 wasmer_eh_personality as *const ()
301 );
302 crate::raise_lib_trap(crate::Trap::uncaught_exception(exnref, ctx))
303 }
304 other => {
305 log!("[wasmer][eh] throw -> unexpected code {:?}", other);
306 unreachable!()
307 }
308 }
309 }
310}
311
312pub unsafe fn delete_exception(exception: *mut c_void) {
313 unsafe {
314 if !exception.is_null() {
315 uw::_Unwind_DeleteException(exception as *mut uw::_Unwind_Exception);
316 }
317 }
318}
319
320unsafe extern "C" fn deallocate_exception(
321 _: uw::_Unwind_Reason_Code,
322 exception: *mut uw::_Unwind_Exception,
323) {
324 unsafe {
325 let exception = Box::from_raw(exception as *mut UwExceptionWrapper);
326 drop(exception);
327 }
328}