Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Standard exception types for singlepass backend. #1129

Merged
merged 19 commits into from
Jan 20, 2020
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions lib/runtime-core/src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,32 @@ pub struct CompilerConfig {
pub backend_specific_config: Option<BackendCompilerConfig>,
}

/// An exception table for a `RunnableModule`.
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct ExceptionTable {
/// Mappings from offsets in generated machine code to the corresponding exception code.
pub offset_to_code: HashMap<usize, ExceptionCode>,
}

impl ExceptionTable {
pub fn new() -> Self {
Self::default()
}
}

/// The code of an exception.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
pub enum ExceptionCode {
/// An `unreachable` opcode was executed.
Unreachable,

/// An arithmetic exception, e.g. divided by zero.
Arithmetic,

/// Memory access exception, e.g. misaligned/out-of-bound read/write.
Memory,
}

pub trait Compiler {
/// Compiles a `Module` from WebAssembly binary format.
/// The `CompileToken` parameter ensures that this can only
Expand Down Expand Up @@ -144,6 +170,10 @@ pub trait RunnableModule: Send + Sync {
None
}

fn get_exception_table(&self) -> Option<&ExceptionTable> {
None
}

unsafe fn patch_local_function(&self, _idx: usize, _target_address: usize) -> bool {
false
}
Expand Down
3 changes: 3 additions & 0 deletions lib/runtime-core/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! The error module contains the data structures and helper functions used to implement errors that
//! are produced and returned from the wasmer runtime core.
use crate::backend::ExceptionCode;
use crate::types::{FuncSig, GlobalDescriptor, MemoryDescriptor, TableDescriptor, Type};
use core::borrow::Borrow;
use std::any::Any;
Expand Down Expand Up @@ -208,6 +209,8 @@ impl std::fmt::Display for RuntimeError {
write!(f, "\"{}\"", s)
} else if let Some(s) = data.downcast_ref::<&str>() {
write!(f, "\"{}\"", s)
} else if let Some(exc_code) = data.downcast_ref::<ExceptionCode>() {
write!(f, "Caught exception of type \"{:?}\".", exc_code)
} else {
write!(f, "unknown error")
}
Expand Down
29 changes: 28 additions & 1 deletion lib/runtime-core/src/fault.rs
Original file line number Diff line number Diff line change
Expand Up @@ -375,23 +375,50 @@ extern "C" fn signal_trap_handler(
_ => {}
}

// Now we have looked up all possible handler tables but failed to find a handler
// for this exception that allows a normal return.
//
// So here we check whether this exception is caused by a suspend signal, return the
// state image if so, or throw the exception out otherwise.

let ctx: &mut vm::Ctx = &mut **CURRENT_CTX.with(|x| x.get());
let es_image = fault
.read_stack(None)
.expect("fault.read_stack() failed. Broken invariants?");

if is_suspend_signal {
// If this is a suspend signal, we parse the runtime state and return the resulting image.
let image = build_instance_image(ctx, es_image);
unwind_result = Box::new(image);
} else {
// Otherwise, this is a real exception and we just throw it to the caller.
if es_image.frames.len() > 0 {
eprintln!(
"\n{}",
"Wasmer encountered an error while running your WebAssembly program."
);
es_image.print_backtrace_if_needed();
}
// Just let the error propagate otherwise

// Look up the exception tables and try to find an exception code.
let exc_code = CURRENT_CODE_VERSIONS.with(|versions| {
let versions = versions.borrow();
for v in versions.iter() {
if let Some(table) = v.runnable_module.get_exception_table() {
let ip = fault.ip.get();
let end = v.base + v.msm.total_size;
if ip >= v.base && ip < end {
if let Some(exc_code) = table.offset_to_code.get(&(ip - v.base)) {
return Some(*exc_code);
}
}
}
}
None
});
if let Some(code) = exc_code {
unwind_result = Box::new(code);
}
}

true
Expand Down
Loading