diff --git a/lib/clif-backend/src/signal/windows.rs b/lib/clif-backend/src/signal/windows.rs index 7d3e81d695f..1228db9a7c5 100644 --- a/lib/clif-backend/src/signal/windows.rs +++ b/lib/clif-backend/src/signal/windows.rs @@ -1,6 +1,6 @@ use crate::{ relocation::{TrapCode, TrapData}, - signal::{CallProtError, HandlerData}, + signal::HandlerData, }; use std::{ cell::Cell, @@ -9,6 +9,7 @@ use std::{ }; use wasmer_runtime_core::{ backend::ExceptionCode, + error::InvokeError, typed_func::Trampoline, vm::{Ctx, Func}, }; @@ -32,6 +33,34 @@ thread_local! { pub static CURRENT_EXECUTABLE_BUFFER: Cell<*const c_void> = Cell::new(ptr::null()); } +fn get_signal_name(code: DWORD) -> &'static str { + match code { + EXCEPTION_FLT_DENORMAL_OPERAND + | EXCEPTION_FLT_DIVIDE_BY_ZERO + | EXCEPTION_FLT_INEXACT_RESULT + | EXCEPTION_FLT_INVALID_OPERATION + | EXCEPTION_FLT_OVERFLOW + | EXCEPTION_FLT_STACK_CHECK + | EXCEPTION_FLT_UNDERFLOW => "floating-point exception", + EXCEPTION_ILLEGAL_INSTRUCTION => "illegal instruction", + EXCEPTION_ACCESS_VIOLATION => "segmentation violation", + EXCEPTION_DATATYPE_MISALIGNMENT => "datatype misalignment", + EXCEPTION_BREAKPOINT => "breakpoint", + EXCEPTION_SINGLE_STEP => "single step", + EXCEPTION_ARRAY_BOUNDS_EXCEEDED => "array bounds exceeded", + EXCEPTION_INT_DIVIDE_BY_ZERO => "int div by zero", + EXCEPTION_INT_OVERFLOW => "int overflow", + EXCEPTION_PRIV_INSTRUCTION => "priv instruction", + EXCEPTION_IN_PAGE_ERROR => "in page error", + EXCEPTION_NONCONTINUABLE_EXCEPTION => "non continuable exception", + EXCEPTION_STACK_OVERFLOW => "stack overflow", + EXCEPTION_GUARD_PAGE => "guard page", + EXCEPTION_INVALID_HANDLE => "invalid handle", + EXCEPTION_POSSIBLE_DEADLOCK => "possible deadlock", + _ => "unknown exception code", + } +} + pub fn call_protected( handler_data: &HandlerData, trampoline: Trampoline, @@ -39,7 +68,7 @@ pub fn call_protected( func: NonNull, param_vec: *const u64, return_vec: *mut u64, -) -> Result<(), CallProtError> { +) -> Result<(), InvokeError> { // TODO: trap early // user code error // if let Some(msg) = super::TRAP_EARLY_DATA.with(|cell| cell.replace(None)) { @@ -58,12 +87,8 @@ pub fn call_protected( instruction_pointer, } = result.unwrap_err(); - if let Some(TrapData { - trapcode, - srcloc: _, - }) = handler_data.lookup(instruction_pointer as _) - { - Err(CallProtError(Box::new(match code as DWORD { + if let Some(TrapData { trapcode, srcloc }) = handler_data.lookup(instruction_pointer as _) { + let exception_code = match code as DWORD { EXCEPTION_ACCESS_VIOLATION => ExceptionCode::MemoryOutOfBounds, EXCEPTION_ILLEGAL_INSTRUCTION => match trapcode { TrapCode::BadSignature => ExceptionCode::IncorrectCallIndirectSignature, @@ -71,51 +96,36 @@ pub fn call_protected( TrapCode::HeapOutOfBounds => ExceptionCode::MemoryOutOfBounds, TrapCode::TableOutOfBounds => ExceptionCode::CallIndirectOOB, TrapCode::UnreachableCodeReached => ExceptionCode::Unreachable, - _ => return Err(CallProtError(Box::new("unknown trap code".to_string()))), + _ => { + return Err(InvokeError::UnknownTrapCode { + trap_code: format!("{}", code as DWORD), + srcloc, + }) + } }, EXCEPTION_STACK_OVERFLOW => ExceptionCode::MemoryOutOfBounds, EXCEPTION_INT_DIVIDE_BY_ZERO | EXCEPTION_INT_OVERFLOW => { ExceptionCode::IllegalArithmetic } _ => { - return Err(CallProtError(Box::new( - "unknown exception code".to_string(), - ))) + let signal = get_signal_name(code as DWORD); + return Err(InvokeError::UnknownTrap { + address: exception_address as usize, + signal, + }); } - }))) - } else { - let signal = match code as DWORD { - EXCEPTION_FLT_DENORMAL_OPERAND - | EXCEPTION_FLT_DIVIDE_BY_ZERO - | EXCEPTION_FLT_INEXACT_RESULT - | EXCEPTION_FLT_INVALID_OPERATION - | EXCEPTION_FLT_OVERFLOW - | EXCEPTION_FLT_STACK_CHECK - | EXCEPTION_FLT_UNDERFLOW => "floating-point exception", - EXCEPTION_ILLEGAL_INSTRUCTION => "illegal instruction", - EXCEPTION_ACCESS_VIOLATION => "segmentation violation", - EXCEPTION_DATATYPE_MISALIGNMENT => "datatype misalignment", - EXCEPTION_BREAKPOINT => "breakpoint", - EXCEPTION_SINGLE_STEP => "single step", - EXCEPTION_ARRAY_BOUNDS_EXCEEDED => "array bounds exceeded", - EXCEPTION_INT_DIVIDE_BY_ZERO => "int div by zero", - EXCEPTION_INT_OVERFLOW => "int overflow", - EXCEPTION_PRIV_INSTRUCTION => "priv instruction", - EXCEPTION_IN_PAGE_ERROR => "in page error", - EXCEPTION_NONCONTINUABLE_EXCEPTION => "non continuable exception", - EXCEPTION_STACK_OVERFLOW => "stack overflow", - EXCEPTION_GUARD_PAGE => "guard page", - EXCEPTION_INVALID_HANDLE => "invalid handle", - EXCEPTION_POSSIBLE_DEADLOCK => "possible deadlock", - _ => "unknown exception code", }; + return Err(InvokeError::TrapCode { + srcloc, + code: exception_code, + }); + } else { + let signal = get_signal_name(code as DWORD); - let s = format!( - "unhandled trap at {:x} - code #{:x}: {}", - exception_address, code, signal, - ); - - Err(CallProtError(Box::new(s))) + Err(InvokeError::UnknownTrap { + address: exception_address as usize, + signal, + }) } } diff --git a/lib/runtime-core/src/error.rs b/lib/runtime-core/src/error.rs index f12f52532cf..4b05fd35161 100644 --- a/lib/runtime-core/src/error.rs +++ b/lib/runtime-core/src/error.rs @@ -173,78 +173,38 @@ impl std::fmt::Display for LinkError { impl std::error::Error for LinkError {} -/* -/// This is the error type returned when calling -/// a WebAssembly function. -/// -/// The main way to do this is `Instance.call`. -/// -/// Comparing two `RuntimeError`s always evaluates to false. -pub struct RuntimeError(pub Box); - -impl PartialEq for RuntimeError { - fn eq(&self, _other: &RuntimeError) -> bool { - false - } -} - -impl std::fmt::Display for RuntimeError { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - let data = &*self.0; - if let Some(s) = data.downcast_ref::() { - write!(f, "\"{}\"", s) - } else if let Some(s) = data.downcast_ref::<&str>() { - write!(f, "\"{}\"", s) - } else if let Some(exc_code) = data.downcast_ref::() { - write!(f, "Caught exception of type \"{:?}\".", exc_code) - } else { - write!(f, "unknown error") - } - } -} - -impl std::fmt::Debug for RuntimeError { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "{}", self) - } -} - -impl std::error::Error for RuntimeError {} - -*/ - -/// TODO: +/// An error that happened while invoking a Wasm function. #[derive(Debug)] pub enum InvokeError { - /// Indicates an exceptional circumstance such as a bug that should be reported or - /// a hardware failure. + /// Indicates an exceptional circumstance such as a bug in Wasmer (please file an issue!) + /// or a hardware failure. FailedWithNoError, - - /// TODO: + /// Indicates that a trap occurred that is not known to Wasmer. UnknownTrap { - /// TODO: + /// The address that the trap occured at. address: usize, - /// TODO: + /// The name of the signal. signal: &'static str, }, - /// TODO: + /// A trap that Wasmer knows about occurred. TrapCode { - /// TODO: + /// The type of exception. code: ExceptionCode, - /// TODO: + /// Where in the Wasm file this trap orginated from. srcloc: u32, }, - /// TODO: + /// A trap occurred that Wasmer knows about but it had a trap code that + /// we weren't expecting or that we do not handle. This error may be backend-specific. UnknownTrapCode { - /// TODO: + /// The trap code we saw but did not recognize. trap_code: String, - /// TODO: + /// Where in the Wasm file this trap orginated from. srcloc: u32, }, - /// extra TODO: investigate if this can be a `Box` instead (looks like probably no) - /// TODO: + /// An "early trap" occurred. TODO: document this properly EarlyTrap(Box), - /// Indicates an error that ocurred related to breakpoints. + /// Indicates that a breakpoint was hit. The inner value is dependent upon + /// the middleware or backend being used. Breakpoint(Box), } @@ -257,7 +217,30 @@ impl From for RuntimeError { } } -//impl std::error::Error for InvokeError {} +impl std::error::Error for InvokeError {} + +impl std::fmt::Display for InvokeError { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + InvokeError::FailedWithNoError => write!(f, "Invoke failed with no error"), + InvokeError::UnknownTrap { address, signal } => write!( + f, + "An unknown trap (`{}`) occured at 0x{:X}", + signal, address + ), + InvokeError::TrapCode { code, srcloc } => { + write!(f, "A `{}` trap was thrown at code offset {}", code, srcloc) + } + InvokeError::UnknownTrapCode { trap_code, srcloc } => write!( + f, + "A trap with an unknown trap code (`{}`) was thrown at code offset {}", + trap_code, srcloc + ), + InvokeError::EarlyTrap(rte) => write!(f, "Early trap: {}", rte), + InvokeError::Breakpoint(rte) => write!(f, "Breakpoint hit: {}", rte), + } + } +} /// An `InternalError` is an error that happened inside of Wasmer and is a /// catch-all for errors that would otherwise be returned as @@ -295,8 +278,7 @@ impl std::error::Error for RuntimeError {} impl std::fmt::Display for RuntimeError { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { match self { - // TODO: update invoke error formatting - RuntimeError::InvokeError(ie) => write!(f, "Error when calling invoke: {:?}", ie), + RuntimeError::InvokeError(ie) => write!(f, "Error when calling invoke: {}", ie), RuntimeError::Metering(_) => write!(f, "unknown metering error type"), RuntimeError::InstanceImage(_) => write!( f, @@ -318,14 +300,6 @@ impl std::fmt::Display for RuntimeError { } } -/* -impl From for RuntimeError { - fn from(other: InternalError) -> Self { - RuntimeError(Box::new(other)) - } -} - */ - /// This error type is produced by resolving a wasm function /// given its name. /// diff --git a/lib/runtime-core/src/tiering.rs b/lib/runtime-core/src/tiering.rs index 83ebfd47159..2aa324232f4 100644 --- a/lib/runtime-core/src/tiering.rs +++ b/lib/runtime-core/src/tiering.rs @@ -2,6 +2,7 @@ //! as runtime. use crate::backend::{Compiler, CompilerConfig}; use crate::compile_with_config; +use crate::error::RuntimeError; use crate::fault::{ catch_unsafe_unwind, ensure_sighandler, pop_code_version, push_code_version, with_ctx, }; @@ -223,23 +224,26 @@ pub unsafe fn run_tiering ShellExitOperation>( } }); if let Err(e) = ret { - if let Ok(new_image) = e.downcast::() { + match e { // Tier switch event - if !was_sigint_triggered_fault() && opt_state.outcome.lock().unwrap().is_some() { - resume_image = Some(*new_image); - continue; - } - let op = interactive_shell(InteractiveShellContext { - image: Some(*new_image), - patched: n_versions.get() > 1, - }); - match op { - ShellExitOperation::ContinueWith(new_image) => { - resume_image = Some(new_image); + RuntimeError::InstanceImage(ii_value) => { + let new_image = ii_value.downcast::().unwrap(); + if !was_sigint_triggered_fault() && opt_state.outcome.lock().unwrap().is_some() + { + resume_image = Some(*new_image); + continue; + } + let op = interactive_shell(InteractiveShellContext { + image: Some(*new_image), + patched: n_versions.get() > 1, + }); + match op { + ShellExitOperation::ContinueWith(new_image) => { + resume_image = Some(new_image); + } } } - } else { - return Err("Error while executing WebAssembly".into()); + _ => return Err("Error while executing WebAssembly".into()), } } else { return Ok(()); diff --git a/lib/runtime/examples/call.rs b/lib/runtime/examples/call.rs index 46ccc5aa693..a501d887ed2 100644 --- a/lib/runtime/examples/call.rs +++ b/lib/runtime/examples/call.rs @@ -71,8 +71,11 @@ fn main() -> Result<(), error::Error> { println!("result: {:?}", result); if let Err(e) = result { - if let Ok(exit_code) = e.0.downcast::() { + if let Err(RuntimeError::User(e)) = result { + let exit_code = e.downcast::().unwrap(); println!("exit code: {:?}", exit_code); + } else { + panic!("Found error that wasn't a user error!") } }