1use libunwind as uw;
38use wasmer_types::TagIndex;
39
40use crate::VMContext;
41
42use super::dwarf::eh::{self, EHAction, EHContext};
43
44static CANARY: u8 = 0;
48const WASMER_EXCEPTION_CLASS: uw::_Unwind_Exception_Class = u64::from_ne_bytes(*b"WMERWASM");
49
50const CATCH_ALL_TAG_VALUE: i32 = i32::MAX;
51const NO_MATCH_FOUND_TAG_VALUE: i32 = i32::MAX - 1;
55
56#[repr(C)]
57pub struct UwExceptionWrapper {
58 pub _uwe: uw::_Unwind_Exception,
59 pub canary: *const u8,
60 pub cause: Box<dyn std::any::Any + Send>,
61
62 pub current_frame_info: Option<Box<CurrentFrameInfo>>,
64}
65
66#[repr(C)]
67pub struct CurrentFrameInfo {
68 pub exception_tag: u32,
69 pub catch_tags: Vec<u32>,
70 pub has_catch_all: bool,
71}
72
73impl UwExceptionWrapper {
74 pub fn new(tag: u32, data_ptr: usize, data_size: u64) -> Self {
75 Self {
76 _uwe: uw::_Unwind_Exception {
77 exception_class: WASMER_EXCEPTION_CLASS,
78 exception_cleanup: None,
79 private_1: core::ptr::null::<u8>() as usize as _,
80 private_2: 0,
81 },
82 canary: &CANARY,
83 cause: Box::new(WasmerException {
84 tag,
85 data_ptr,
86 data_size,
87 }),
88 current_frame_info: None,
89 }
90 }
91}
92
93#[repr(C)]
94#[derive(Debug, thiserror::Error, Clone)]
95#[error("Uncaught exception in wasm code!")]
96pub struct WasmerException {
97 pub tag: u32,
99 pub data_ptr: usize,
100 pub data_size: u64,
101}
102
103impl WasmerException {
104 pub fn new(tag: u32, data_ptr: usize, data_size: u64) -> Self {
105 Self {
106 tag,
107 data_ptr,
108 data_size,
109 }
110 }
111}
112
113#[cfg(target_arch = "x86_64")]
114const UNWIND_DATA_REG: (i32, i32) = (0, 1); #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
117const UNWIND_DATA_REG: (i32, i32) = (0, 1); #[cfg(any(target_arch = "riscv64", target_arch = "riscv32"))]
120const UNWIND_DATA_REG: (i32, i32) = (10, 11); #[cfg(target_arch = "loongarch64")]
123const UNWIND_DATA_REG: (i32, i32) = (4, 5); #[unsafe(no_mangle)]
126pub unsafe extern "C" fn wasmer_eh_personality(
132 version: std::ffi::c_int,
133 actions: uw::_Unwind_Action,
134 exception_class: uw::_Unwind_Exception_Class,
135 exception_object: *mut uw::_Unwind_Exception,
136 context: *mut uw::_Unwind_Context,
137) -> uw::_Unwind_Reason_Code {
138 unsafe {
139 if version != 1 {
140 return uw::_Unwind_Reason_Code__URC_FATAL_PHASE1_ERROR;
141 }
142
143 let uw_exc = std::mem::transmute::<*mut uw::_Unwind_Exception, *mut UwExceptionWrapper>(
144 exception_object,
145 );
146
147 if exception_class != WASMER_EXCEPTION_CLASS {
148 return uw::_Unwind_Reason_Code__URC_CONTINUE_UNWIND;
149 }
150
151 let wasmer_exc = (*uw_exc).cause.downcast_ref::<WasmerException>();
152 let wasmer_exc = match wasmer_exc {
153 Some(e) => e,
154 None => {
155 return uw::_Unwind_Reason_Code__URC_CONTINUE_UNWIND;
156 }
157 };
158
159 let eh_action = match find_eh_action(context) {
160 Ok(action) => action,
161 Err(_) => {
162 return uw::_Unwind_Reason_Code__URC_FATAL_PHASE1_ERROR;
163 }
164 };
165
166 if actions as i32 & uw::_Unwind_Action__UA_SEARCH_PHASE as i32 != 0 {
167 match eh_action {
168 EHAction::None => uw::_Unwind_Reason_Code__URC_CONTINUE_UNWIND,
169 EHAction::CatchAll { .. }
170 | EHAction::CatchSpecific { .. }
171 | EHAction::CatchSpecificOrAll { .. } => uw::_Unwind_Reason_Code__URC_HANDLER_FOUND,
172 EHAction::Terminate => uw::_Unwind_Reason_Code__URC_FATAL_PHASE1_ERROR,
173 }
174 } else {
175 let has_catch_all = matches!(eh_action, EHAction::CatchSpecificOrAll { .. });
178
179 match eh_action {
180 EHAction::None => uw::_Unwind_Reason_Code__URC_CONTINUE_UNWIND,
181 EHAction::CatchAll { lpad } => {
182 uw::_Unwind_SetGR(context, UNWIND_DATA_REG.0, uw_exc as _);
183 uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, 0);
185 uw::_Unwind_SetIP(context, lpad as usize as _);
186 uw::_Unwind_Reason_Code__URC_INSTALL_CONTEXT
187 }
188 EHAction::CatchSpecific { lpad, tags }
189 | EHAction::CatchSpecificOrAll { lpad, tags } => {
190 (*uw_exc).current_frame_info = Some(Box::new(CurrentFrameInfo {
191 exception_tag: wasmer_exc.tag,
192 catch_tags: tags,
193 has_catch_all,
194 }));
195 uw::_Unwind_SetGR(context, UNWIND_DATA_REG.0, uw_exc as _);
196 uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, 1);
198 uw::_Unwind_SetIP(context, lpad as usize as _);
199 uw::_Unwind_Reason_Code__URC_INSTALL_CONTEXT
200 }
201 EHAction::Terminate => uw::_Unwind_Reason_Code__URC_FATAL_PHASE2_ERROR,
202 }
203 }
204 }
205}
206
207#[unsafe(no_mangle)]
208pub unsafe extern "C" fn wasmer_eh_personality2(
215 vmctx: *mut VMContext,
216 exception_object: *mut UwExceptionWrapper,
217) -> i32 {
218 unsafe {
219 let Some(current_frame_info) = (*exception_object).current_frame_info.take() else {
220 unreachable!("wasmer_eh_personality2 called without current_frame_info");
222 };
223
224 let instance = (*vmctx).instance();
225 for tag in current_frame_info.catch_tags {
226 let unique_tag = instance.shared_tag_ptr(TagIndex::from_u32(tag)).index();
227 if unique_tag == current_frame_info.exception_tag {
228 return tag as i32;
229 }
230 }
231
232 if current_frame_info.has_catch_all {
233 CATCH_ALL_TAG_VALUE
234 } else {
235 NO_MATCH_FOUND_TAG_VALUE
236 }
237 }
238}
239
240unsafe fn find_eh_action(context: *mut uw::_Unwind_Context) -> Result<EHAction, ()> {
241 unsafe {
242 let lsda = uw::_Unwind_GetLanguageSpecificData(context) as *const u8;
243 let mut ip_before_instr: std::ffi::c_int = 0;
244 let ip = uw::_Unwind_GetIPInfo(context, &mut ip_before_instr);
245 let eh_context = EHContext {
246 ip: if ip_before_instr != 0 {
251 ip as _
252 } else {
253 ip.wrapping_sub(1) as _
254 },
255 func_start: uw::_Unwind_GetRegionStart(context) as *const _,
256 get_text_start: &|| uw::_Unwind_GetTextRelBase(context) as *const _,
257 get_data_start: &|| uw::_Unwind_GetDataRelBase(context) as *const _,
258 };
259 eh::find_eh_action(lsda, &eh_context)
260 }
261}
262
263pub unsafe fn throw(tag: u32, vmctx: *mut VMContext, data_ptr: usize, data_size: u64) -> ! {
264 unsafe {
265 let unique_tag = (*vmctx)
267 .instance()
268 .shared_tag_ptr(TagIndex::from_u32(tag))
269 .index();
270
271 let exception = Box::new(UwExceptionWrapper::new(unique_tag, data_ptr, data_size));
272 let exception_param = Box::into_raw(exception) as *mut libunwind::_Unwind_Exception;
273
274 match uw::_Unwind_RaiseException(exception_param) {
275 libunwind::_Unwind_Reason_Code__URC_END_OF_STACK => {
276 crate::raise_lib_trap(crate::Trap::lib(wasmer_types::TrapCode::UncaughtException))
277 }
278 _ => {
279 unreachable!()
280 }
281 }
282 }
283}
284
285pub unsafe fn rethrow(exc: *mut UwExceptionWrapper) -> ! {
286 unsafe {
287 if exc.is_null() {
288 panic!();
289 }
290
291 match uw::_Unwind_Resume_or_Rethrow(std::mem::transmute::<
292 *mut UwExceptionWrapper,
293 *mut libunwind::_Unwind_Exception,
294 >(exc))
295 {
296 libunwind::_Unwind_Reason_Code__URC_END_OF_STACK => {
297 crate::raise_lib_trap(crate::Trap::lib(wasmer_types::TrapCode::UncaughtException))
298 }
299 _ => unreachable!(),
300 }
301 }
302}