mirror of
https://github.com/fluencelabs/wasmer
synced 2025-06-23 13:41:32 +00:00
Merge branch 'master' into fix/imported-functions-must-be-send
This commit is contained in:
@ -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"
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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")
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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[..],
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
Reference in New Issue
Block a user