Merge branch 'master' into fix/imported-functions-must-be-send

This commit is contained in:
Mark McCaskey
2020-01-21 09:47:46 -08:00
committed by GitHub
29 changed files with 1421 additions and 844 deletions

View File

@ -13,11 +13,11 @@ edition = "2018"
nix = "0.15"
page_size = "0.4"
wasmparser = "0.45.0"
parking_lot = "0.9"
parking_lot = "0.10.0"
lazy_static = "1.4"
errno = "0.2"
libc = "0.2.60"
hex = "0.3"
hex = "0.4"
smallvec = "0.6"
bincode = "1.1"
@ -36,8 +36,8 @@ version = "1.0"
version = "0.11"
[dependencies.serde-bench]
version = "0.0.7"
[dependencies.blake2b_simd]
version = "0.5"
[dependencies.blake3]
version = "0.1.0"
[dependencies.digest]
version = "0.8"
@ -45,7 +45,7 @@ version = "0.8"
winapi = { version = "0.3", features = ["memoryapi"] }
[build-dependencies]
blake2b_simd = "0.5"
blake3 = "0.1.0"
rustc_version = "0.2"
cc = "1.0"

View File

@ -1,13 +1,12 @@
use blake2b_simd::blake2bp;
use std::{env, fs, io::Write, path::PathBuf};
const WASMER_VERSION: &'static str = env!("CARGO_PKG_VERSION");
fn main() {
let mut state = blake2bp::State::new();
state.update(WASMER_VERSION.as_bytes());
let mut hasher = blake3::Hasher::new();
hasher.update(WASMER_VERSION.as_bytes());
let hasher = state.finalize();
let hasher = hasher.finalize();
let hash_string = hasher.to_hex().as_str().to_owned();
let crate_dir = env::var("OUT_DIR").unwrap();

View File

@ -76,9 +76,18 @@ impl Default for MemoryBoundCheckMode {
}
/// Controls which experimental features will be enabled.
/// Features usually have a corresponding [WebAssembly proposal][wasm-props].
///
/// [wasm-props]: https://github.com/WebAssembly/proposals
#[derive(Debug, Default)]
pub struct Features {
/// Whether support for the [SIMD proposal][simd-prop] is enabled.
///
/// [simd-prop]: https://github.com/webassembly/simd
pub simd: bool,
/// Whether support for the [threads proposal][threads-prop] is enabled.
///
/// [threads-prop]: https://github.com/webassembly/threads
pub threads: bool,
}
@ -113,6 +122,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
@ -144,6 +179,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
}

View File

@ -3,7 +3,6 @@
//! and loaded to allow skipping compilation and fast startup.
use crate::{module::ModuleInfo, sys::Memory};
use blake2b_simd::blake2bp;
use std::{io, mem, slice};
/// Indicates the invalid type of invalid cache file
@ -46,10 +45,8 @@ impl From<io::Error> for Error {
///
/// [`Cache`]: trait.Cache.html
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
// WasmHash is made up of two 32 byte arrays instead of a 64 byte array
// because derive only works on fixed sized arrays size 32 or below
// TODO: fix this when this gets fixed by improved const generics
pub struct WasmHash([u8; 32], [u8; 32]);
// WasmHash is made up of a 32 byte array
pub struct WasmHash([u8; 32]);
impl WasmHash {
/// Hash a wasm module.
@ -58,18 +55,8 @@ impl WasmHash {
/// This does no verification that the supplied data
/// is, in fact, a wasm module.
pub fn generate(wasm: &[u8]) -> Self {
let mut first_part = [0u8; 32];
let mut second_part = [0u8; 32];
let mut state = blake2bp::State::new();
state.update(wasm);
let hasher = state.finalize();
let generic_array = hasher.as_bytes();
first_part.copy_from_slice(&generic_array[0..32]);
second_part.copy_from_slice(&generic_array[32..64]);
WasmHash(first_part, second_part)
let hash = blake3::hash(wasm);
WasmHash(hash.into())
}
/// Create the hexadecimal representation of the
@ -86,26 +73,20 @@ impl WasmHash {
e
))
})?;
if bytes.len() != 64 {
if bytes.len() != 32 {
return Err(Error::DeserializeError(
"Prehashed keys must deserialze into exactly 64 bytes".to_string(),
"Prehashed keys must deserialze into exactly 32 bytes".to_string(),
));
}
use std::convert::TryInto;
Ok(WasmHash(
bytes[0..32].try_into().map_err(|e| {
Error::DeserializeError(format!("Could not get first 32 bytes: {}", e))
})?,
bytes[32..64].try_into().map_err(|e| {
Error::DeserializeError(format!("Could not get last 32 bytes: {}", e))
})?,
))
Ok(WasmHash(bytes[0..32].try_into().map_err(|e| {
Error::DeserializeError(format!("Could not get first 32 bytes: {}", e))
})?))
}
pub(crate) fn into_array(self) -> [u8; 64] {
let mut total = [0u8; 64];
pub(crate) fn into_array(self) -> [u8; 32] {
let mut total = [0u8; 32];
total[0..32].copy_from_slice(&self.0);
total[32..64].copy_from_slice(&self.1);
total
}
}

View File

@ -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;
@ -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")
}

View File

@ -375,15 +375,23 @@ 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{}",
@ -391,7 +399,26 @@ extern "C" fn signal_trap_handler(
);
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

View File

@ -173,7 +173,7 @@ pub fn validate_and_report_errors_with_features(
}
}
/// Creates a new module from the given cache `Artifact` for the specified compiler backend
/// Creates a new module from the given cache [`Artifact`] for the specified compiler backend
pub unsafe fn load_cache_with(
cache: Artifact,
compiler: &dyn backend::Compiler,

View File

@ -627,9 +627,11 @@ pub mod x64 {
use crate::vm::Ctx;
use std::any::Any;
#[allow(clippy::cast_ptr_alignment)]
unsafe fn compute_vmctx_deref(vmctx: *const Ctx, seq: &[usize]) -> u64 {
let mut ptr = &vmctx as *const *const Ctx as *const u8;
for x in seq {
debug_assert!(ptr.align_offset(std::mem::align_of::<*const u8>()) == 0);
ptr = (*(ptr as *const *const u8)).offset(*x as isize);
}
ptr as usize as u64

View File

@ -1,85 +0,0 @@
#[derive(Debug, Clone)]
enum MonoVecInner<T> {
None,
Inline(T),
Heap(Vec<T>),
}
/// A type that can hold zero items,
/// one item, or many items.
#[derive(Debug, Clone)]
pub struct MonoVec<T> {
inner: MonoVecInner<T>,
}
impl<T> MonoVec<T> {
pub fn new() -> Self {
Self {
inner: MonoVecInner::None,
}
}
pub fn new_inline(item: T) -> Self {
Self {
inner: MonoVecInner::Inline(item),
}
}
pub fn with_capacity(capacity: usize) -> Self {
match capacity {
0 | 1 => Self::new(),
_ => Self {
inner: MonoVecInner::Heap(Vec::with_capacity(capacity)),
},
}
}
pub fn push(&mut self, item: T) {
let uninit = MonoVecInner::None;
let prev = mem::replace(&mut self.inner, uninit);
let next = match prev {
MonoVecInner::None => MonoVecInner::Inline(item),
MonoVecInner::Inline(previous_item) => MonoVecInner::Heap(vec![previous_item, item]),
MonoVecInner::Heap(mut v) => {
v.push(item);
MonoVecInner::Heap(v)
}
};
let uninit = mem::replace(&mut self.inner, next);
mem::forget(uninit);
}
pub fn pop(&mut self) -> Option<T> {
match self.inner {
MonoVecInner::None => None,
MonoVecInner::Inline(ref mut item) => {
let uninit = unsafe { mem::zeroed() };
let item = mem::replace(item, uninit);
let uninit = mem::replace(&mut self.inner, MonoVecInner::None);
mem::forget(uninit);
Some(item)
}
MonoVecInner::Heap(ref mut v) => v.pop(),
}
}
pub fn as_slice(&self) -> &[T] {
match self.inner {
MonoVecInner::None => unsafe {
slice::from_raw_parts(mem::align_of::<T>() as *const T, 0)
},
MonoVecInner::Inline(ref item) => slice::from_ref(item),
MonoVecInner::Heap(ref v) => &v[..],
}
}
pub fn as_slice_mut(&mut self) -> &mut [T] {
match self.inner {
MonoVecInner::None => unsafe {
slice::from_raw_parts_mut(mem::align_of::<T>() as *mut T, 0)
},
MonoVecInner::Inline(ref mut item) => slice::from_mut(item),
MonoVecInner::Heap(ref mut v) => &mut v[..],
}
}
}

View File

@ -54,11 +54,14 @@ pub struct Ctx {
/// This is intended to be user-supplied, per-instance
/// contextual data. There are currently some issue with it,
/// notably that it cannot be set before running the `start`
/// function in a WebAssembly module.
/// function in a WebAssembly module. Additionally, the `data`
/// field may be taken by another ABI implementation that the user
/// wishes to use in addition to their own, such as WASI. This issue is
/// being discussed at [#1111](https://github.com/wasmerio/wasmer/pull/1111).
///
/// [#219](https://github.com/wasmerio/wasmer/pull/219) fixes that
/// issue, as well as allowing the user to have *per-function*
/// context, instead of just per-instance.
/// Alternatively, per-function data can be used if the function in the
/// [`ImportObject`] is a closure. This cannot duplicate data though,
/// so if data may be shared if the [`ImportObject`] is reused.
pub data: *mut c_void,
/// If there's a function set in this field, it gets called
@ -567,6 +570,7 @@ pub struct FuncCtx {
impl FuncCtx {
/// Offset to the `vmctx` field.
#[allow(clippy::erasing_op)]
pub const fn offset_vmctx() -> u8 {
0 * (mem::size_of::<usize>() as u8)
}