diff --git a/src/common/mmap/common.rs b/src/common/mmap/common.rs deleted file mode 100644 index c4b020def..000000000 --- a/src/common/mmap/common.rs +++ /dev/null @@ -1,17 +0,0 @@ -/// Round `size` up to the nearest multiple of `page_size`. -pub fn round_up_to_page_size(size: usize, page_size: usize) -> usize { - (size + (page_size - 1)) & !(page_size - 1) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_round_up_to_page_size() { - assert_eq!(round_up_to_page_size(0, 4096), 0); - assert_eq!(round_up_to_page_size(1, 4096), 4096); - assert_eq!(round_up_to_page_size(4096, 4096), 4096); - assert_eq!(round_up_to_page_size(4097, 4096), 8192); - } -} diff --git a/src/common/mmap/mod.rs b/src/common/mmap/mod.rs deleted file mode 100644 index b72a09b45..000000000 --- a/src/common/mmap/mod.rs +++ /dev/null @@ -1,13 +0,0 @@ -//! A cross-platform Rust API for memory mapped buffers. -// TODO: Refactor this into it's own lib -mod common; - -#[cfg(windows)] -mod windows; -#[cfg(windows)] -pub use self::windows::Mmap; - -#[cfg(unix)] -mod unix; -#[cfg(unix)] -pub use self::unix::Mmap; diff --git a/src/common/mmap/unix.rs b/src/common/mmap/unix.rs deleted file mode 100644 index 64071f6f0..000000000 --- a/src/common/mmap/unix.rs +++ /dev/null @@ -1,95 +0,0 @@ -//! Low-level abstraction for allocating and managing zero-filled pages -//! of memory. - -use errno; -use libc; -use region; -use std::ptr; -use std::slice; -use std::string::String; - -use super::common::round_up_to_page_size; - -/// A simple struct consisting of a page-aligned pointer to page-aligned -/// and initially-zeroed memory and a length. -#[derive(Debug)] -pub struct Mmap { - ptr: *mut u8, - len: usize, -} - -impl Mmap { - /// Construct a new empty instance of `Mmap`. - pub fn new() -> Self { - Self { - ptr: ptr::null_mut(), - len: 0, - } - } - - /// Create a new `Mmap` pointing to at least `size` bytes of memory, - /// suitably sized and aligned for memory protection. - #[cfg(not(target_os = "windows"))] - pub fn with_size(size: usize) -> Result { - // Mmap may return EINVAL if the size is zero, so just - // special-case that. - if size == 0 { - return Ok(Self::new()); - } - - let page_size = region::page::size(); - let alloc_size = round_up_to_page_size(size, page_size); - let ptr = unsafe { - libc::mmap( - ptr::null_mut(), - alloc_size, - libc::PROT_NONE, - libc::MAP_PRIVATE | libc::MAP_ANON, - -1, - 0, - ) - }; - if ptr as isize == -1isize { - Err(errno::errno().to_string()) - } else { - Ok(Self { - ptr: ptr as *mut u8, - len: alloc_size, - }) - } - } - - /// Return the allocated memory as a slice of u8. - pub fn as_slice(&self) -> &[u8] { - unsafe { slice::from_raw_parts(self.ptr, self.len) } - } - - /// Return the allocated memory as a mutable slice of u8. - pub fn as_mut_slice(&mut self) -> &mut [u8] { - unsafe { slice::from_raw_parts_mut(self.ptr, self.len) } - } - - /// Return the allocated memory as a pointer to u8. - pub fn as_ptr(&self) -> *const u8 { - self.ptr - } - - /// Return the allocated memory as a mutable pointer to u8. - pub fn as_mut_ptr(&mut self) -> *mut u8 { - self.ptr - } - - /// Return the lengthof the allocated memory. - pub fn len(&self) -> usize { - self.len - } -} - -impl Drop for Mmap { - fn drop(&mut self) { - if !self.ptr.is_null() { - let r = unsafe { libc::munmap(self.ptr as *mut libc::c_void, self.len) }; - assert_eq!(r, 0, "munmap failed: {}", errno::errno()); - } - } -} diff --git a/src/common/mmap/windows.rs b/src/common/mmap/windows.rs deleted file mode 100644 index 52fe80be6..000000000 --- a/src/common/mmap/windows.rs +++ /dev/null @@ -1,88 +0,0 @@ -//! Low-level abstraction for allocating and managing zero-filled pages -//! of memory. - -use errno; -use region; -use std::ptr; -use std::slice; -use std::string::String; -use winapi::um::memoryapi::{VirtualAlloc, VirtualFree}; -use winapi::um::winnt::{MEM_COMMIT, MEM_RELEASE, MEM_RESERVE, PAGE_NOACCESS}; - -use super::common::round_up_to_page_size; - -/// A simple struct consisting of a page-aligned pointer to page-aligned -/// and initially-zeroed memory and a length. -#[derive(Debug)] -pub struct Mmap { - ptr: *mut u8, - len: usize, -} - -impl Mmap { - /// Construct a new empty instance of `Mmap`. - pub fn new() -> Self { - Self { - ptr: ptr::null_mut(), - len: 0, - } - } - - /// Create a new `Mmap` pointing to at least `size` bytes of memory, - /// suitably sized and aligned for memory protection. - pub fn with_size(size: usize) -> Result { - let page_size = region::page::size(); - - // VirtualAlloc always rounds up to the next multiple of the page size - let ptr = unsafe { - VirtualAlloc( - ptr::null_mut(), - size, - MEM_COMMIT | MEM_RESERVE, - PAGE_NOACCESS, - ) - }; - if !ptr.is_null() { - Ok(Self { - ptr: ptr as *mut u8, - len: round_up_to_page_size(size, page_size), - }) - } else { - Err(errno::errno().to_string()) - } - } - - /// Return the allocated memory as a slice of u8. - pub fn as_slice(&self) -> &[u8] { - unsafe { slice::from_raw_parts(self.ptr, self.len) } - } - - /// Return the allocated memory as a mutable slice of u8. - pub fn as_mut_slice(&mut self) -> &mut [u8] { - unsafe { slice::from_raw_parts_mut(self.ptr, self.len) } - } - - /// Return the allocated memory as a pointer to u8. - pub fn as_ptr(&self) -> *const u8 { - self.ptr - } - - /// Return the allocated memory as a mutable pointer to u8. - pub fn as_mut_ptr(&mut self) -> *mut u8 { - self.ptr - } - - /// Return the lengthof the allocated memory. - pub fn len(&self) -> usize { - self.len - } -} - -impl Drop for Mmap { - fn drop(&mut self) { - if !self.ptr.is_null() { - let r = unsafe { VirtualFree(self.ptr, self.len, MEM_RELEASE) }; - assert_eq!(r, 0); - } - } -} diff --git a/src/common/mod.rs b/src/common/mod.rs deleted file mode 100644 index 2e4a760a8..000000000 --- a/src/common/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod mmap; -pub mod slice; diff --git a/src/common/slice.rs b/src/common/slice.rs deleted file mode 100644 index 2c720a7c1..000000000 --- a/src/common/slice.rs +++ /dev/null @@ -1,106 +0,0 @@ -use std::ops::{Index, IndexMut}; -use std::ptr::NonNull; - -#[derive(Copy, Clone, Debug)] -#[repr(transparent)] -pub struct UncheckedSlice { - ptr: NonNull, -} - -impl UncheckedSlice { - #[inline] - pub fn get_unchecked(&self, index: usize) -> &T { - let ptr = self.ptr.as_ptr(); - unsafe { &*ptr.add(index) } - } - - #[inline] - pub fn get_unchecked_mut(&mut self, index: usize) -> &mut T { - let ptr = self.ptr.as_ptr(); - unsafe { &mut *(ptr.add(index) as *mut _) } - } - - pub unsafe fn dangling() -> UncheckedSlice { - UncheckedSlice { - ptr: NonNull::dangling(), - } - } - - pub fn as_ptr(&self) -> *const T { - self.ptr.as_ptr() - } - - pub fn as_mut_ptr(&mut self) -> *mut T { - self.ptr.as_ptr() - } -} - -impl<'a, T> From<&'a [T]> for UncheckedSlice { - fn from(slice: &[T]) -> UncheckedSlice { - let ptr: NonNull<[T]> = slice.into(); - UncheckedSlice { ptr: ptr.cast() } - } -} - -#[derive(Debug)] -#[repr(C)] -pub struct BoundedSlice { - pub data: UncheckedSlice, - pub len: usize, -} - -impl BoundedSlice { - pub fn get(&self, index: usize) -> Option<&T> { - if index < self.len { - Some(self.data.get_unchecked(index)) - } else { - None - } - } - - pub fn get_mut(&mut self, index: usize) -> Option<&mut T> { - if index < self.len { - Some(self.data.get_unchecked_mut(index)) - } else { - None - } - } - - #[inline] - pub fn len(&self) -> usize { - self.len - } - - // TODO: Needs refactor. Take LinearMemory as argument. - // I've tried that but it gives cryptic error. - pub fn new(slice: &[T], size: usize) -> BoundedSlice { - BoundedSlice { - data: slice.into(), - len: size, - } - } -} - -impl Index for BoundedSlice { - type Output = T; - fn index(&self, index: usize) -> &T { - self.get(index) - .unwrap_or_else(|| panic!("index: {} was out of bounds.", index)) - } -} - -impl IndexMut for BoundedSlice { - fn index_mut(&mut self, index: usize) -> &mut T { - self.get_mut(index) - .unwrap_or_else(|| panic!("index: {} was out of bounds.", index)) - } -} - -impl<'a, T> From<&'a [T]> for BoundedSlice { - fn from(slice: &[T]) -> BoundedSlice { - BoundedSlice { - data: slice.into(), - len: slice.len(), - } - } -} diff --git a/src/lib.rs b/src/lib.rs index 71b9e00e8..be0d11244 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,17 +1,8 @@ -extern crate libc; -extern crate region; extern crate wasmer_runtime; // extern crate wasmer_emscripten; -pub extern crate nix; // re-exported for usage in macros -#[cfg(windows)] -extern crate winapi; - #[macro_use] mod macros; #[macro_use] -pub mod recovery; -pub mod common; -pub mod sighandler; pub mod update; pub mod webassembly; diff --git a/src/macros.rs b/src/macros.rs index f94e6c662..f69d2dca5 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1,24 +1,3 @@ -/// Retrieve a WebAssembly function given a Instance and a FuncIndex -/// Example: -/// let func: fn(i32) -> i32 = get_instance_function!(instance, func_index); -#[macro_export] -macro_rules! get_instance_function { - ($instance:expr, $func_index:expr) => {{ - use std::mem; - let func_addr = $instance.get_function_pointer($func_index); - unsafe { mem::transmute(func_addr) } - }}; -} - -#[macro_export] -macro_rules! include_wast2wasm_bytes { - ($x:expr) => {{ - use wabt::wat2wasm; - const WAST_BYTES: &[u8] = include_bytes!($x); - wat2wasm(WAST_BYTES.to_vec()).expect(&format!("Can't convert {} file to wasm", $x)) - }}; -} - #[macro_export] macro_rules! debug { ($fmt:expr) => (if cfg!(any(debug_assertions, feature="debug")) { println!(concat!("Wasmer::", $fmt)) }); diff --git a/src/recovery.rs b/src/recovery.rs deleted file mode 100644 index f1e4734ac..000000000 --- a/src/recovery.rs +++ /dev/null @@ -1,122 +0,0 @@ -//! When a WebAssembly module triggers any traps, we perform recovery here. -//! -//! This module uses TLS (thread-local storage) to track recovery information. Since the four signals we're handling -//! are very special, the async signal unsafety of Rust's TLS implementation generally does not affect the correctness here -//! unless you have memory unsafety elsewhere in your code. - -use crate::sighandler::install_sighandler; -use nix::libc::siginfo_t; -use nix::sys::signal::{Signal, SIGBUS, SIGFPE, SIGILL, SIGSEGV}; -use std::cell::{Cell, UnsafeCell}; -use std::sync::Once; - -extern "C" { - pub fn setjmp(env: *mut ::nix::libc::c_void) -> ::nix::libc::c_int; - fn longjmp(env: *mut ::nix::libc::c_void, val: ::nix::libc::c_int) -> !; -} - -const SETJMP_BUFFER_LEN: usize = 27; -pub static SIGHANDLER_INIT: Once = Once::new(); - -thread_local! { - pub static SETJMP_BUFFER: UnsafeCell<[::nix::libc::c_int; SETJMP_BUFFER_LEN]> = UnsafeCell::new([0; SETJMP_BUFFER_LEN]); - pub static CAUGHT_ADDRESS: Cell = Cell::new(0); -} - -// We need a macro since the arguments we will provide to the funciton -// (and the return value) are not fixed to just one case: f(x) -> y -// but multiple: f(x) -> y, f(a,b) -> c, ... -// And right now it's impossible to handle with Rust function type system - -/// Calls a WebAssembly function with longjmp receiver installed. If a non-WebAssembly function is passed in, -/// the behavior of call_protected is undefined. -#[macro_export] -macro_rules! call_protected { - ($x:expr) => { - unsafe { - use crate::recovery::{setjmp, CAUGHT_ADDRESS, SETJMP_BUFFER, SIGHANDLER_INIT}; - use crate::sighandler::install_sighandler; - use crate::webassembly::ErrorKind; - - use crate::nix::sys::signal::{Signal, SIGBUS, SIGFPE, SIGILL, SIGSEGV}; - - let jmp_buf = SETJMP_BUFFER.with(|buf| buf.get()); - let prev_jmp_buf = *jmp_buf; - - SIGHANDLER_INIT.call_once(|| { - install_sighandler(); - }); - - let signum = setjmp(jmp_buf as *mut ::nix::libc::c_void); - if signum != 0 { - *jmp_buf = prev_jmp_buf; - let addr = CAUGHT_ADDRESS.with(|cell| cell.get()); - - let signal = match Signal::from_c_int(signum) { - Ok(SIGFPE) => "floating-point exception", - Ok(SIGILL) => "illegal instruction", - Ok(SIGSEGV) => "segmentation violation", - Ok(SIGBUS) => "bus error", - Err(_) => "error while getting the Signal", - _ => "unkown trapped signal", - }; - Err(ErrorKind::RuntimeError(format!( - "trap at {:#x} - {}", - addr, signal - ))) - } else { - let ret = $x; // TODO: Switch stack? - *jmp_buf = prev_jmp_buf; - Ok(ret) - } - } - }; -} - -pub fn call_protected(f: impl FnOnce() -> T) -> Result { - unsafe { - let jmp_buf = SETJMP_BUFFER.with(|buf| buf.get()); - let prev_jmp_buf = *jmp_buf; - - SIGHANDLER_INIT.call_once(|| { - install_sighandler(); - }); - - let signum = setjmp(jmp_buf as *mut ::nix::libc::c_void); - if signum != 0 { - *jmp_buf = prev_jmp_buf; - let addr = CAUGHT_ADDRESS.with(|cell| cell.get()); - - let signal = match Signal::from_c_int(signum) { - Ok(SIGFPE) => "floating-point exception", - Ok(SIGILL) => "illegal instruction", - Ok(SIGSEGV) => "segmentation violation", - Ok(SIGBUS) => "bus error", - Err(_) => "error while getting the Signal", - _ => "unkown trapped signal", - }; - Err(format!("trap at {:#x} - {}", addr, signal)) - } else { - let ret = f(); // TODO: Switch stack? - *jmp_buf = prev_jmp_buf; - Ok(ret) - } - } -} - -/// Unwinds to last protected_call. -pub unsafe fn do_unwind(signum: i32, siginfo: *mut siginfo_t) -> ! { - // Since do_unwind is only expected to get called from WebAssembly code which doesn't hold any host resources (locks etc.) - // itself, accessing TLS here is safe. In case any other code calls this, it often indicates a memory safety bug and you should - // temporarily disable the signal handlers to debug it. - - let jmp_buf = SETJMP_BUFFER.with(|buf| buf.get()); - if *jmp_buf == [0; SETJMP_BUFFER_LEN] { - ::std::process::abort(); - } - // We only target macos at the moment as other ones might not have si_addr field - #[cfg(target_os = "macos")] - CAUGHT_ADDRESS.with(|cell| cell.set((*siginfo).si_addr as _)); - - longjmp(jmp_buf as *mut ::nix::libc::c_void, signum) -} diff --git a/src/sighandler.rs b/src/sighandler.rs deleted file mode 100644 index 21b28c0ff..000000000 --- a/src/sighandler.rs +++ /dev/null @@ -1,33 +0,0 @@ -//! We install signal handlers to handle WebAssembly traps within -//! our Rust code. Otherwise we will have errors that stop the Rust process -//! such as `process didn't exit successfully: ... (signal: 8, SIGFPE: erroneous arithmetic operation)` -//! -//! Please read more about this here: https://github.com/CraneStation/wasmtime/issues/15 -//! This code is inspired by: https://github.com/pepyakin/wasmtime/commit/625a2b6c0815b21996e111da51b9664feb174622 -use super::recovery; -use nix::libc::{c_void, siginfo_t}; -use nix::sys::signal::{ - sigaction, SaFlags, SigAction, SigHandler, SigSet, SIGBUS, SIGFPE, SIGILL, SIGSEGV, -}; - -pub unsafe fn install_sighandler() { - let sa = SigAction::new( - SigHandler::SigAction(signal_trap_handler), - SaFlags::SA_ONSTACK, - SigSet::empty(), - ); - sigaction(SIGFPE, &sa).unwrap(); - sigaction(SIGILL, &sa).unwrap(); - sigaction(SIGSEGV, &sa).unwrap(); - sigaction(SIGBUS, &sa).unwrap(); -} - -extern "C" fn signal_trap_handler( - signum: ::nix::libc::c_int, - siginfo: *mut siginfo_t, - _ucontext: *mut c_void, -) { - unsafe { - recovery::do_unwind(signum, siginfo); - } -}