Merge branch 'master' into feat-runtime-core-value-try-from

This commit is contained in:
Ivan Enderlin
2020-02-18 10:01:03 +01:00
committed by GitHub
13 changed files with 153 additions and 208 deletions

View File

@ -2,6 +2,7 @@
## **[Unreleased]**
- [#1228](https://github.com/wasmerio/wasmer/pull/1228) Singlepass cleanup: Resolve several FIXMEs and remove protect_unix.
- [#1218](https://github.com/wasmerio/wasmer/pull/1218) Enable Cranelift verifier in debug mode. Fix bug with table indices being the wrong type.
- [#787](https://github.com/wasmerio/wasmer/pull/787) New crate `wasmer-interface-types` to implement WebAssembly Interface Types.
- [#1213](https://github.com/wasmerio/wasmer/pull/1213) Fixed WASI `fdstat` to detect `isatty` properly.

View File

@ -1 +0,0 @@
<meta http-equiv="refresh" content="0; url=rust/wasmer_runtime/index.html">

View File

@ -0,0 +1,32 @@
<p align="center">
<a href="https://wasmer.io" target="_blank" rel="noopener noreferrer">
<img width="300" src="https://raw.githubusercontent.com/wasmerio/wasmer/master/logo.png" alt="Wasmer logo">
</a>
</p>
<p align="center">
<a href="https://dev.azure.com/wasmerio/wasmer/_build/latest?definitionId=3&branchName=master">
<img src="https://img.shields.io/azure-devops/build/wasmerio/wasmer/3.svg?style=flat-square" alt="Build Status">
</a>
<a href="https://github.com/wasmerio/wasmer/blob/master/LICENSE">
<img src="https://img.shields.io/github/license/wasmerio/wasmer.svg?style=flat-square" alt="License">
</a>
<a href="https://spectrum.chat/wasmer">
<img src="https://withspectrum.github.io/badge/badge.svg" alt="Join the Wasmer Community">
</a>
<a href="https://crates.io/crates/wasmer-interface-types">
<img src="https://img.shields.io/crates/d/wasmer-interface-types.svg?style=flat-square" alt="Number of downloads from crates.io">
</a>
<a href="https://docs.rs/wasmer-interface-types">
<img src="https://docs.rs/wasmer-interface-types/badge.svg" alt="Read our API documentation">
</a>
</p>
# Wasmer Interface Types
Wasmer is a standalone JIT WebAssembly runtime, aiming to be fully
compatible with WASI, Emscripten, Rust and Go. [Learn
more](https://github.com/wasmerio/wasmer).
This crate is an implementation of [the living WebAssembly Interface
Types standard](https://github.com/WebAssembly/interface-types).

View File

@ -51,6 +51,8 @@ pub struct Wasm {
pub(crate) invoke_env: Option<NonNull<c_void>>,
}
impl Kind for Wasm {}
impl Wasm {
/// Create new `Wasm` from given parts.
pub unsafe fn from_raw_parts(
@ -70,7 +72,6 @@ impl Wasm {
/// by the host.
pub struct Host(());
impl Kind for Wasm {}
impl Kind for Host {}
/// Represents a list of WebAssembly values.
@ -110,14 +111,15 @@ pub trait WasmTypeList {
Rets: WasmTypeList;
}
/// Empty trait to specify the kind of `ExternalFunction`: With or
/// Empty trait to specify the kind of `HostFunction`: With or
/// without a `vm::Ctx` argument. See the `ExplicitVmCtx` and the
/// `ImplicitVmCtx` structures.
///
/// This type is never aimed to be used by a user. It is used by the
/// This trait is never aimed to be used by a user. It is used by the
/// trait system to automatically generate an appropriate `wrap`
/// function.
pub trait ExternalFunctionKind {}
#[doc(hidden)]
pub trait HostFunctionKind {}
/// This empty structure indicates that an external function must
/// contain an explicit `vm::Ctx` argument (at first position).
@ -127,8 +129,11 @@ pub trait ExternalFunctionKind {}
/// x + 1
/// }
/// ```
#[doc(hidden)]
pub struct ExplicitVmCtx {}
impl HostFunctionKind for ExplicitVmCtx {}
/// This empty structure indicates that an external function has no
/// `vm::Ctx` argument (at first position). Its signature is:
///
@ -139,14 +144,13 @@ pub struct ExplicitVmCtx {}
/// ```
pub struct ImplicitVmCtx {}
impl ExternalFunctionKind for ExplicitVmCtx {}
impl ExternalFunctionKind for ImplicitVmCtx {}
impl HostFunctionKind for ImplicitVmCtx {}
/// Represents a function that can be converted to a `vm::Func`
/// (function pointer) that can be called within WebAssembly.
pub trait ExternalFunction<Kind, Args, Rets>
pub trait HostFunction<Kind, Args, Rets>
where
Kind: ExternalFunctionKind,
Kind: HostFunctionKind,
Args: WasmTypeList,
Rets: WasmTypeList,
{
@ -227,8 +231,8 @@ where
/// Creates a new `Func`.
pub fn new<F, Kind>(func: F) -> Func<'a, Args, Rets, Host>
where
Kind: ExternalFunctionKind,
F: ExternalFunction<Kind, Args, Rets>,
Kind: HostFunctionKind,
F: HostFunction<Kind, Args, Rets>,
{
let (func, func_env) = func.to_raw();
@ -310,6 +314,7 @@ macro_rules! impl_traits {
where
$( $x: WasmExternType ),*;
#[allow(unused_parens)]
impl< $( $x ),* > WasmTypeList for ( $( $x ),* )
where
$( $x: WasmExternType ),*
@ -380,7 +385,8 @@ macro_rules! impl_traits {
}
}
impl< $( $x, )* Rets, Trap, FN > ExternalFunction<ExplicitVmCtx, ( $( $x ),* ), Rets> for FN
#[allow(unused_parens)]
impl< $( $x, )* Rets, Trap, FN > HostFunction<ExplicitVmCtx, ( $( $x ),* ), Rets> for FN
where
$( $x: WasmExternType, )*
Rets: WasmTypeList,
@ -495,7 +501,8 @@ macro_rules! impl_traits {
}
}
impl< $( $x, )* Rets, Trap, FN > ExternalFunction<ImplicitVmCtx, ( $( $x ),* ), Rets> for FN
#[allow(unused_parens)]
impl< $( $x, )* Rets, Trap, FN > HostFunction<ImplicitVmCtx, ( $( $x ),* ), Rets> for FN
where
$( $x: WasmExternType, )*
Rets: WasmTypeList,
@ -607,6 +614,7 @@ macro_rules! impl_traits {
}
}
#[allow(unused_parens)]
impl<'a $( , $x )*, Rets> Func<'a, ( $( $x ),* ), Rets, Wasm>
where
$( $x: WasmExternType, )*

View File

@ -101,44 +101,57 @@ where
{
/// Type for this `NativeWasmType`.
const TYPE: Type;
/// Convert from u64 bites to self.
fn from_binary(bits: u64) -> Self;
/// Convert self to u64 binary representation.
fn to_binary(self) -> u64;
}
unsafe impl NativeWasmType for i32 {
const TYPE: Type = Type::I32;
fn from_binary(bits: u64) -> Self {
bits as _
}
fn to_binary(self) -> u64 {
self as _
}
}
unsafe impl NativeWasmType for i64 {
const TYPE: Type = Type::I64;
fn from_binary(bits: u64) -> Self {
bits as _
}
fn to_binary(self) -> u64 {
self as _
}
}
unsafe impl NativeWasmType for f32 {
const TYPE: Type = Type::F32;
fn from_binary(bits: u64) -> Self {
f32::from_bits(bits as u32)
}
fn to_binary(self) -> u64 {
self.to_bits() as _
}
}
unsafe impl NativeWasmType for f64 {
const TYPE: Type = Type::F64;
fn from_binary(bits: u64) -> Self {
f64::from_bits(bits)
}
fn to_binary(self) -> u64 {
self.to_bits()
}
@ -151,102 +164,40 @@ where
{
/// Native wasm type for this `WasmExternType`.
type Native: NativeWasmType;
/// Convert from given `Native` type to self.
fn from_native(native: Self::Native) -> Self;
/// Convert self to `Native` type.
fn to_native(self) -> Self::Native;
}
unsafe impl WasmExternType for i8 {
type Native = i32;
macro_rules! wasm_extern_type {
($type:ty => $native_type:ty) => {
unsafe impl WasmExternType for $type {
type Native = $native_type;
fn from_native(native: Self::Native) -> Self {
native as _
}
fn to_native(self) -> Self::Native {
self as _
}
}
};
}
unsafe impl WasmExternType for u8 {
type Native = i32;
fn from_native(native: Self::Native) -> Self {
native as _
}
fn to_native(self) -> Self::Native {
self as _
}
}
unsafe impl WasmExternType for i16 {
type Native = i32;
fn from_native(native: Self::Native) -> Self {
native as _
}
fn to_native(self) -> Self::Native {
self as _
}
}
unsafe impl WasmExternType for u16 {
type Native = i32;
fn from_native(native: Self::Native) -> Self {
native as _
}
fn to_native(self) -> Self::Native {
self as _
}
}
unsafe impl WasmExternType for i32 {
type Native = i32;
fn from_native(native: Self::Native) -> Self {
native
}
fn to_native(self) -> Self::Native {
self
}
}
unsafe impl WasmExternType for u32 {
type Native = i32;
fn from_native(native: Self::Native) -> Self {
native as _
}
fn to_native(self) -> Self::Native {
self as _
}
}
unsafe impl WasmExternType for i64 {
type Native = i64;
fn from_native(native: Self::Native) -> Self {
native
}
fn to_native(self) -> Self::Native {
self
}
}
unsafe impl WasmExternType for u64 {
type Native = i64;
fn from_native(native: Self::Native) -> Self {
native as _
}
fn to_native(self) -> Self::Native {
self as _
}
}
unsafe impl WasmExternType for f32 {
type Native = f32;
fn from_native(native: Self::Native) -> Self {
native
}
fn to_native(self) -> Self::Native {
self
}
}
unsafe impl WasmExternType for f64 {
type Native = f64;
fn from_native(native: Self::Native) -> Self {
native
}
fn to_native(self) -> Self::Native {
self
}
}
wasm_extern_type!(i8 => i32);
wasm_extern_type!(u8 => i32);
wasm_extern_type!(i16 => i32);
wasm_extern_type!(u16 => i32);
wasm_extern_type!(i32 => i32);
wasm_extern_type!(u32 => i32);
wasm_extern_type!(i64 => i64);
wasm_extern_type!(u64 => i64);
wasm_extern_type!(f32 => f32);
wasm_extern_type!(f64 => f64);
// pub trait IntegerAtomic
// where

View File

@ -3,7 +3,6 @@
use crate::emitter_x64::*;
use crate::machine::*;
use crate::protect_unix;
#[cfg(target_arch = "aarch64")]
use dynasmrt::aarch64::Assembler;
#[cfg(target_arch = "x86_64")]
@ -28,7 +27,7 @@ use wasmer_runtime_core::{
},
cache::{Artifact, Error as CacheError},
codegen::*,
fault::raw::register_preservation_trampoline,
fault::{self, raw::register_preservation_trampoline},
loader::CodeMemory,
memory::MemoryType,
module::{ModuleInfo, ModuleInner},
@ -369,6 +368,11 @@ impl RunnableModule for X64ExecutionContext {
}
fn get_trampoline(&self, _: &ModuleInfo, sig_index: SigIndex) -> Option<Wasm> {
// Correctly unwinding from `catch_unsafe_unwind` on hardware exceptions depends
// on the signal handlers being installed. Here we call `ensure_sighandler` "statically"
// outside `invoke()`.
fault::ensure_sighandler();
unsafe extern "C" fn invoke(
_trampoline: Trampoline,
ctx: *mut vm::Ctx,
@ -383,8 +387,9 @@ impl RunnableModule for X64ExecutionContext {
let args =
slice::from_raw_parts(args, num_params_plus_one.unwrap().as_ptr() as usize - 1);
let ret = match protect_unix::call_protected(
let ret = match fault::catch_unsafe_unwind(
|| {
// Puts the arguments onto the stack and calls Wasm entry.
#[cfg(target_arch = "x86_64")]
{
let args_reverse: SmallVec<[u64; 8]> = args.iter().cloned().rev().collect();
@ -395,6 +400,9 @@ impl RunnableModule for X64ExecutionContext {
func.as_ptr(),
)
}
// FIXME: Currently we are doing a hack here to convert between native aarch64 and
// "emulated" x86 ABIs. Ideally, this should be done using handwritten assembly.
#[cfg(target_arch = "aarch64")]
{
struct CallCtx<'a> {
@ -519,7 +527,7 @@ impl RunnableModule for X64ExecutionContext {
true
}
Err(err) => {
*error_out = Some(err.0);
*error_out = Some(err);
false
}
};
@ -545,8 +553,7 @@ impl RunnableModule for X64ExecutionContext {
}
unsafe fn do_early_trap(&self, data: Box<dyn Any + Send>) -> ! {
protect_unix::TRAP_EARLY_DATA.with(|x| x.set(Some(data)));
protect_unix::trigger_trap();
fault::begin_unsafe_unwind(data);
}
fn get_code(&self) -> Option<&[u8]> {
@ -1686,14 +1693,11 @@ impl X64FunctionCode {
Location::GPR(GPR::RSP),
);
// FIXME: Possible dynasm bug. This is a workaround.
// Using RSP as the source/destination operand of a `mov` instruction produces invalid code.
a.emit_mov(Size::S64, Location::GPR(GPR::RSP), Location::GPR(GPR::RCX));
for (i, r) in used_xmms.iter().enumerate() {
a.emit_mov(
Size::S64,
Location::XMM(*r),
Location::Memory(GPR::RCX, (i * 8) as i32),
Location::Memory(GPR::RSP, (i * 8) as i32),
);
}
for r in used_xmms.iter().rev() {
@ -1771,37 +1775,26 @@ impl X64FunctionCode {
}
}
match *param {
// Dynasm bug: RSP in memory operand does not work
Location::Imm64(_) | Location::XMM(_) => {
Location::Imm64(_) => {
// Dummy value slot to be filled with `mov`.
a.emit_push(Size::S64, Location::GPR(GPR::RAX));
// Use R10 as the temporary register here, since it is callee-saved and not
// used by the callback `cb`.
a.emit_mov(Size::S64, *param, Location::GPR(GPR::R10));
a.emit_mov(
Size::S64,
Location::GPR(GPR::RAX),
Location::XMM(XMM::XMM0),
);
a.emit_mov(
Size::S64,
Location::GPR(GPR::RCX),
Location::XMM(XMM::XMM1),
);
a.emit_sub(Size::S64, Location::Imm32(8), Location::GPR(GPR::RSP));
a.emit_mov(Size::S64, Location::GPR(GPR::RSP), Location::GPR(GPR::RCX));
a.emit_mov(Size::S64, *param, Location::GPR(GPR::RAX));
a.emit_mov(
Size::S64,
Location::GPR(GPR::RAX),
Location::Memory(GPR::RCX, 0),
);
a.emit_mov(
Size::S64,
Location::XMM(XMM::XMM0),
Location::GPR(GPR::RAX),
);
a.emit_mov(
Size::S64,
Location::XMM(XMM::XMM1),
Location::GPR(GPR::RCX),
Location::GPR(GPR::R10),
Location::Memory(GPR::RSP, 0),
);
}
Location::XMM(_) => {
// Dummy value slot to be filled with `mov`.
a.emit_push(Size::S64, Location::GPR(GPR::RAX));
// XMM registers can be directly stored to memory.
a.emit_mov(Size::S64, *param, Location::Memory(GPR::RSP, 0));
}
_ => a.emit_push(Size::S64, *param),
}
}
@ -1873,12 +1866,10 @@ impl X64FunctionCode {
// Restore XMMs.
if used_xmms.len() > 0 {
// FIXME: Possible dynasm bug. This is a workaround.
a.emit_mov(Size::S64, Location::GPR(GPR::RSP), Location::GPR(GPR::RDX));
for (i, r) in used_xmms.iter().enumerate() {
a.emit_mov(
Size::S64,
Location::Memory(GPR::RDX, (i * 8) as i32),
Location::Memory(GPR::RSP, (i * 8) as i32),
Location::XMM(*r),
);
}

View File

@ -40,7 +40,6 @@ extern crate smallvec;
mod codegen_x64;
mod emitter_x64;
mod machine;
pub mod protect_unix;
#[cfg(target_arch = "aarch64")]
mod translator_aarch64;

View File

@ -1,49 +0,0 @@
//! Installing signal handlers allows us to handle traps and out-of-bounds memory
//! accesses that occur when runniing WebAssembly.
//!
//! This code is inspired by: https://github.com/pepyakin/wasmtime/commit/625a2b6c0815b21996e111da51b9664feb174622
//!
//! 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 std::any::Any;
use std::cell::Cell;
use wasmer_runtime_core::codegen::BreakpointMap;
use wasmer_runtime_core::fault::{begin_unsafe_unwind, catch_unsafe_unwind, ensure_sighandler};
thread_local! {
pub static TRAP_EARLY_DATA: Cell<Option<Box<dyn Any + Send>>> = Cell::new(None);
}
pub unsafe fn trigger_trap() -> ! {
begin_unsafe_unwind(Box::new(()));
}
pub struct CallProtError(pub Box<dyn Any + Send>);
pub fn call_protected<T>(
f: impl FnOnce() -> T,
breakpoints: Option<BreakpointMap>,
) -> Result<T, CallProtError> {
ensure_sighandler();
unsafe {
let ret = catch_unsafe_unwind(|| f(), breakpoints);
match ret {
Ok(x) => Ok(x),
Err(e) => Err(CallProtError(
if let Some(data) = TRAP_EARLY_DATA.with(|cell| cell.replace(None)) {
data
} else {
e
},
)),
}
}
}
pub unsafe fn throw(payload: Box<dyn Any + Send>) -> ! {
begin_unsafe_unwind(payload);
}

View File

@ -5,7 +5,8 @@
unused_mut,
unused_variables,
unused_unsafe,
unreachable_patterns
unreachable_patterns,
clippy::missing_safety_doc
)]
#![doc(html_favicon_url = "https://wasmer.io/static/icons/favicon.ico")]
#![doc(html_logo_url = "https://avatars3.githubusercontent.com/u/44205449?s=200&v=4")]

View File

@ -36,7 +36,7 @@ use wasmer_runtime_core::vm::Ctx;
/// the fd value of the virtual root
pub const VIRTUAL_ROOT_FD: __wasi_fd_t = 3;
/// all the rights enabled
pub const ALL_RIGHTS: __wasi_rights_t = 0x1FFFFFFF;
pub const ALL_RIGHTS: __wasi_rights_t = 0x1FFF_FFFF;
const STDIN_DEFAULT_RIGHTS: __wasi_rights_t = __WASI_RIGHT_FD_DATASYNC
| __WASI_RIGHT_FD_READ
| __WASI_RIGHT_FD_SYNC
@ -52,7 +52,10 @@ const STDOUT_DEFAULT_RIGHTS: __wasi_rights_t = __WASI_RIGHT_FD_DATASYNC
const STDERR_DEFAULT_RIGHTS: __wasi_rights_t = STDOUT_DEFAULT_RIGHTS;
/// Get WasiState from a Ctx
/// This function is unsafe because it must be called on a WASI Ctx
///
/// # Safety
/// - This function must be called on a `Ctx` that was created with `WasiState`
/// in the data field
pub unsafe fn get_wasi_state(ctx: &mut Ctx) -> &mut WasiState {
&mut *(ctx.data as *mut WasiState)
}
@ -186,7 +189,7 @@ impl WasiFs {
for dir in preopened_dirs {
debug!("Attempting to preopen {}", &dir.to_string_lossy());
// TODO: think about this
let default_rights = 0x1FFFFFFF; // all rights
let default_rights = ALL_RIGHTS;
let cur_dir_metadata = dir.metadata().map_err(|e| {
format!(
"Could not get metadata for file {:?}: {}",
@ -236,7 +239,7 @@ impl WasiFs {
for (alias, real_dir) in mapped_dirs {
debug!("Attempting to open {:?} at {}", real_dir, alias);
// TODO: think about this
let default_rights = 0x1FFFFFFF; // all rights
let default_rights = ALL_RIGHTS;
let cur_dir_metadata = real_dir.metadata().map_err(|e| {
format!(
"Could not get metadata for file {:?}: {}",
@ -428,7 +431,7 @@ impl WasiFs {
// create virtual root
let root_inode = {
let all_rights = 0x1FFFFFFF;
let all_rights = ALL_RIGHTS;
// TODO: make this a list of positive rigths instead of negative ones
// root gets all right for now
let root_rights = all_rights
@ -525,10 +528,15 @@ impl WasiFs {
next
}
/// like create dir all, but it also opens it
/// This function is like create dir all, but it also opens it.
/// Function is unsafe because it may break invariants and hasn't been tested.
/// This is an experimental function and may be removed
// dead code because this is an API for external use
///
/// # Safety
/// - Virtual directories created with this function must not conflict with
/// the standard operation of the WASI filesystem. This is vague and
/// unlikely in pratice. Join the discussion at https://github.com/wasmerio/wasmer/issues/1219
/// for what the newer, safer WASI FS APIs should look like.
#[allow(dead_code)]
pub unsafe fn open_dir_all(
&mut self,
@ -1161,7 +1169,7 @@ impl WasiFs {
stat.st_ino = self.get_next_inode_index();
Ok(self.inodes.insert(InodeVal {
stat: stat,
stat,
is_preopened,
name,
kind,
@ -1210,10 +1218,14 @@ impl WasiFs {
Ok(idx)
}
/// This function is unsafe because it's the caller's responsibility to ensure that
/// all refences to the given inode have been removed from the filesystem
/// Low level function to remove an inode, that is it deletes the WASI FS's
/// knowledge of a file.
///
/// returns the inode if it existed and was removed
/// This function returns the inode if it existed and was removed.
///
/// # Safety
/// - The caller must ensure that all references to the specified inode have
/// been removed from the filesystem.
pub unsafe fn remove_inode(&mut self, inode: Inode) -> Option<InodeVal> {
self.inodes.remove(inode)
}

View File

@ -434,7 +434,7 @@ impl<'de> Deserialize<'de> for HostFile {
}
}
const FIELDS: &'static [&'static str] = &["host_path", "flags"];
const FIELDS: &[&str] = &["host_path", "flags"];
deserializer.deserialize_struct("HostFile", FIELDS, HostFileVisitor)
}
}

View File

@ -1,4 +1,4 @@
#![allow(non_camel_case_types)]
#![allow(non_camel_case_types, clippy::identity_op)]
use crate::ptr::{Array, WasmPtr};
use byteorder::{ReadBytesExt, WriteBytesExt, LE};

View File

@ -30,10 +30,10 @@ pub enum WasiVersion {
}
/// Namespace for the `Snapshot0` version.
const SNAPSHOT0_NAMESPACE: &'static str = "wasi_unstable";
const SNAPSHOT0_NAMESPACE: &str = "wasi_unstable";
/// Namespace for the `Snapshot1` version.
const SNAPSHOT1_NAMESPACE: &'static str = "wasi_snapshot_preview1";
const SNAPSHOT1_NAMESPACE: &str = "wasi_snapshot_preview1";
/// Detect the version of WASI being used based on the import
/// namespaces.