Merge remote-tracking branch 'private/master' into feature/dynasm-backend

This commit is contained in:
losfair
2019-02-11 18:38:00 +08:00
31 changed files with 364 additions and 416 deletions

View File

@ -1,6 +1,3 @@
# This appveyor build file is heavily inspired by uutils/coreutils
# https://raw.githubusercontent.com/uutils/coreutils/d0db7bbaa46dabf65b71e3e33b1ed7595aaacc56/.appveyor.yml
branches: branches:
except: except:
- master - master
@ -9,143 +6,27 @@ version: "{build} ~ {branch}"
os: Visual Studio 2017 os: Visual Studio 2017
matrix:
allow_failures:
- CHANNEL: stable
# - ABI: gnu
environment: environment:
matrix: matrix:
# minimum version
# - CHANNEL: 1.31.0
# ARCH: i686
# ABI: msvc
# # "msvc" ABI
# - CHANNEL: stable
# ARCH: i686
# ABI: msvc
# - CHANNEL: stable
# ARCH: x86_64
# ABI: msvc
# - CHANNEL: beta
# ARCH: i686
# ABI: msvc
# - CHANNEL: beta
# ARCH: x86_64
# ABI: msvc
# - CHANNEL: nightly
# ARCH: i686
# ABI: msvc
# - CHANNEL: nightly
# ARCH: x86_64
# ABI: msvc
# # "gnu" ABI
# - CHANNEL: stable
# ARCH: i686
# ABI: gnu
# - CHANNEL: stable
# ARCH: x86_64
# ABI: gnu
# - CHANNEL: beta
# ARCH: i686
# ABI: gnu
# - CHANNEL: beta
# ARCH: x86_64
# ABI: gnu
# - CHANNEL: nightly
# ARCH: i686
# ABI: gnu
# - CHANNEL: nightly
# ARCH: x86_64
# ABI: gnu
# * specific gnu compilers
# - CHANNEL: stable
# ARCH: i686
# ABI: gnu
# MINGW_URL: https://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win32/Personal%20Builds/mingw-builds/4.9.2/threads-win32/dwarf/i686-4.9.2-release-win32-dwarf-rt_v4-rev4.7z/download
# MINGW_ARCHIVE: i686-4.9.2-release-win32-dwarf-rt_v4-rev4.7z
- CHANNEL: stable - CHANNEL: stable
ARCH: x86_64 ARCH: x86_64
ABI: gnu ABI: msvc
MINGW_URL: https://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win64/Personal%20Builds/mingw-builds/7.3.0/threads-posix/seh/x86_64-7.3.0-release-posix-seh-rt_v5-rev0.7z/download#mingw-w64-x86_64-7.3.0-posix-seh.7z TARGET: x86_64-pc-windows-msvc
install: install:
- echo %PATH% - appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe
# force branch checkout (if knowable), then reset to the specific commit ## (can be needed for accurate code coverage info) - rustup-init.exe -yv --default-host %target%
# * this allows later apps to see the branch name using standard `git branch` operations, yet always builds the correct specific commit - set PATH=%PATH%;%USERPROFILE%\.cargo\bin
# * ref: <https://github.com/appveyor/ci/issues/1606>[`@`](https://archive.is/RVpnF)
- if DEFINED APPVEYOR_REPO_BRANCH if /I "%APPVEYOR_REPO_SCM%"=="git" ( git checkout "%APPVEYOR_REPO_BRANCH%" & git reset --hard "%APPVEYOR_REPO_COMMIT%" )
# ensure CWD is project main directory
- cd "%APPVEYOR_BUILD_FOLDER%"
# create a working area
- ps: if ( ! $env:CI_TEMP_DIR ) { $env:CI_TEMP_DIR = "${env:TEMP}\${env:APPVEYOR_JOB_ID}" ; mkdir -force $env:CI_TEMP_DIR | out-null }
# rust installation
- set "TARGET=%ARCH%-pc-windows-%ABI%"
# * install `rust` via `rustup`
- appveyor DownloadFile "https://win.rustup.rs/" -FileName "%CI_TEMP_DIR%\rustup-init.exe"
- call "%CI_TEMP_DIR%\rustup-init.exe" -y --default-toolchain %CHANNEL% --default-host %TARGET% --no-modify-path >NUL
- set "PATH=%PATH%;%USERPROFILE%\.cargo\bin"
- ps: $env:TOOLCHAIN = $(rustup show active-toolchain)
- rename "C:\Program Files\Git\usr\bin\sh.exe" sh2.exe
# * set RUST_BACKTRACE for enhanced error messages
- set RUST_BACKTRACE=1
# * show versions
- rustc -vV - rustc -vV
- cargo -vV - cargo -vV
# finalize FEATURES
- if /i "%CHANNEL%"=="nightly" set "FEATURES=nightly"
# "gnu" ABI setup
# * use the system MinGW/MSYS if we can
- if /i "%ABI%"=="gnu" set MSYS_BINDIR=C:\msys64\usr\bin
- if /i "%ABI%"=="gnu" if /i "%ARCH%"=="i686" set "MSYS_BITS=32"
- if /i "%ABI%"=="gnu" if /i "%ARCH%"=="x86_64" set "MSYS_BITS=64"
- if defined MSYS_BITS set "MSYS_MINGWDIR=C:\msys64\mingw%MSYS_BITS%"
- if defined MSYS_MINGWDIR set "MSYS_BINDIR=C:\msys64\usr\bin"
## * workaround for rust-lang/rust#47048 / rust-lang/rust#53454 ## !maint: remove when resolved
- if /i "%ABI%"=="gnu" if /i "%ARCH%"=="i686" if not DEFINED MINGW_URL set "MINGW_URL=https://downloads.sourceforge.net/project/mingw-w64/Toolchains targetting Win32/Personal Builds/mingw-builds/8.1.0/threads-posix/dwarf/i686-8.1.0-release-posix-dwarf-rt_v6-rev0.7z"
- if /i "%ABI%"=="gnu" if /i "%ARCH%"=="x86_64" if not DEFINED MINGW_URL set "MINGW_URL=https://downloads.sourceforge.net/project/mingw-w64/Toolchains targetting Win64/Personal Builds/mingw-builds/8.1.0/threads-posix/seh/x86_64-8.1.0-release-posix-seh-rt_v6-rev0.7z"
##
# * specific MinGW, if specified
- ps: if ( ! $env:MINGW_ARCHIVE -and $env:MINGW_URL ) { $env:MINGW_ARCHIVE = $($([URI]$env:MINGW_URL).fragment).TrimStart('#') }
- ps: if ( ! $env:MINGW_ARCHIVE -and $env:MINGW_URL ) { $env:MINGW_ARCHIVE = $([URI]$env:MINGW_URL).segments[-1] }
- if defined MINGW_ARCHIVE curl --insecure -fsSL "%MINGW_URL%" -o "%CI_TEMP_DIR%\%MINGW_ARCHIVE%"
- if defined MINGW_ARCHIVE mkdir "%CI_TEMP_DIR%\MinGW" >NUL
- if defined MINGW_ARCHIVE 7z x -y "%CI_TEMP_DIR%\%MINGW_ARCHIVE%" -o"%CI_TEMP_DIR%\MinGW" >NUL
- if defined MINGW_ARCHIVE set "MSYS_MINGWDIR=%CI_TEMP_DIR%\MinGW\mingw%MSYS_BITS%"
- if defined MINGW_ARCHIVE set "MSYS_BINDIR=%MSYS_MINGWDIR%\bin"
# * MinGW/MSYS PATH setup
- if defined MSYS_MINGWDIR set PATH=%MSYS_MINGWDIR%\%ARCH%-w64-mingw32\bin;%MSYS_BINDIR%;%PATH%
## * workaround for rust-lang/rust#47048 / rust-lang/rust#53454 ## !maint: remove when resolved
# ** ref: <https://github.com/rust-lang/rust/issues/47048>, <https://github.com/rust-lang/rust/issues/53454>
# ** egs: <https://github.com/pkgw/tectonic/commit/29686db533d8732d7d97fc94270ed33b77f29295>, <https://github.com/rukai/PF_Sandbox/blob/e842613cf9ff102dfb3fbd87381319e6e6dfe3ae/appveyor.yml>
- if /i "%ABI%"=="gnu" rustup install %CHANNEL%-%ARCH%-pc-windows-msvc
- if /i "%ABI%"=="gnu" rustup default %CHANNEL%-%ARCH%-pc-windows-msvc
- if /i "%ABI%"=="gnu" rustup target add %TARGET%
- if /i "%ABI%"=="gnu" rustup show
- if /i "%ABI%"=="gnu" rustc -vV
- ps: $env:TOOLCHAIN = $(rustup show active-toolchain)
# ** copy libs from gcc toolchain to rust toolchain (more specifically, "crt2.o" and "dllcrt2.o" are needed)
- if defined MSYS_MINGWDIR copy /y "%MSYS_MINGWDIR%\%ARCH%-w64-mingw32\lib\*.o" "%USERPROFILE%\.rustup\toolchains\%TOOLCHAIN%\lib\rustlib\%TARGET%\lib" >NUL
##
- if /i "%ABI%"=="gnu" where gcc
- if /i "%ABI%"=="gnu" gcc --version
# "msvc" ABI setup
- if /i "%ABI%" == "msvc" if /i "%ARCH%" == "i686" call "%VS140COMNTOOLS%\..\..\VC\vcvarsall.bat"
- if /i "%ABI%" == "msvc" if /i "%ARCH%" == "x86_64" call "C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.cmd" /x64
- if /i "%ABI%" == "msvc" if /i "%ARCH%" == "x86_64" call "%VS140COMNTOOLS%\..\..\VC\vcvarsall.bat" x86_amd64
artifacts: artifacts:
- path: target\%TARGET%\debug\wasmer.exe - path: target\debug\wasmer.exe
name: wasmer.exe name: wasmer.exe
build_script: build_script:
- set BUILD_CMD=cargo +%TOOLCHAIN% build --target=%TARGET% - cargo build --verbose
- echo [ %BUILD_CMD% ] & %BUILD_CMD%
test_script: test_script:
- set TEST_CMD=cargo +%TOOLCHAIN% test --target=%TARGET% --no-fail-fast - set RUST_BACKTRACE=1
- echo [ %TEST_CMD% ] & %TEST_CMD% - cargo test --verbose

View File

@ -42,6 +42,7 @@ jobs:
command: | command: |
sudo apt-get install -y cmake sudo apt-get install -y cmake
- run: make test - run: make test
- run: make integration-tests
- save_cache: - save_cache:
paths: paths:
- /usr/local/cargo/registry - /usr/local/cargo/registry

View File

@ -23,8 +23,8 @@ install:
integration-tests: release integration-tests: release
echo "Running Integration Tests" echo "Running Integration Tests"
# Commented for now until we fix emscripten ./integration_tests/lua/test.sh
# ./integration_tests/nginx/test.sh ./integration_tests/nginx/test.sh
lint: lint:
cargo fmt --all -- --check cargo fmt --all -- --check

View File

@ -0,0 +1,9 @@
# `lua` integration test
This starts wasmer with the lua wasm file. The test asserts on
the output of wasmer. Run test with:
```
> ./integration_tests/lua/test.sh
```

15
integration_tests/lua/test.sh Executable file
View File

@ -0,0 +1,15 @@
#! /bin/bash
nohup ./target/release/wasmer run examples/lua.wasm &
sleep 3s
if grep "Lua 5.4.0 Copyright (C) 1994-2018 Lua.org, PUC-Rio" ./nohup.out
then
echo "lua integration test succeeded"
rm ./nohup.out
exit 0
else
echo "lua integration test failed"
rm ./nohup.out
exit -1
fi

View File

@ -16,7 +16,7 @@ use wasmer_runtime_core::{
cache::{Cache, Error as CacheError}, cache::{Cache, Error as CacheError},
}; };
use wasmer_runtime_core::{ use wasmer_runtime_core::{
backend::{Backend, FuncResolver, ProtectedCaller, Token}, backend::{Backend, FuncResolver, ProtectedCaller, Token, UserTrapper},
error::{CompileResult, RuntimeResult}, error::{CompileResult, RuntimeResult},
module::{ModuleInfo, ModuleInner, StringTable}, module::{ModuleInfo, ModuleInner, StringTable},
structures::{Map, TypedIndex}, structures::{Map, TypedIndex},
@ -51,6 +51,10 @@ impl ProtectedCaller for Placeholder {
) -> RuntimeResult<Vec<Value>> { ) -> RuntimeResult<Vec<Value>> {
Ok(vec![]) Ok(vec![])
} }
fn get_early_trapper(&self) -> Box<dyn UserTrapper> {
unimplemented!()
}
} }
/// This contains all of the items in a `ModuleInner` except the `func_resolver`. /// This contains all of the items in a `ModuleInner` except the `func_resolver`.

View File

@ -35,6 +35,13 @@ use wasmer_runtime_core::{
vm, vmcalls, vm, vmcalls,
}; };
extern "C" {
#[cfg(not(target_os = "windows"))]
pub fn __rust_probestack();
#[cfg(all(target_os = "windows", target_pointer_width = "64"))]
pub fn __chkstk();
}
#[allow(dead_code)] #[allow(dead_code)]
pub struct FuncResolverBuilder { pub struct FuncResolverBuilder {
resolver: FuncResolver, resolver: FuncResolver,
@ -215,7 +222,10 @@ impl FuncResolverBuilder {
LibCall::FloorF64 => libcalls::floorf64 as isize, LibCall::FloorF64 => libcalls::floorf64 as isize,
LibCall::TruncF64 => libcalls::truncf64 as isize, LibCall::TruncF64 => libcalls::truncf64 as isize,
LibCall::NearestF64 => libcalls::nearbyintf64 as isize, LibCall::NearestF64 => libcalls::nearbyintf64 as isize,
LibCall::Probestack => libcalls::__rust_probestack as isize, #[cfg(all(target_pointer_width = "64", target_os = "windows"))]
Probestack => __chkstk as isize,
#[cfg(not(target_os = "windows"))]
Probestack => __rust_probestack as isize,
}, },
RelocationType::Intrinsic(ref name) => match name.as_str() { RelocationType::Intrinsic(ref name) => match name.as_str() {
"i32print" => i32_print as isize, "i32print" => i32_print as isize,

View File

@ -2,9 +2,9 @@ use crate::relocation::{TrapData, TrapSink};
use crate::trampoline::Trampolines; use crate::trampoline::Trampolines;
use hashbrown::HashSet; use hashbrown::HashSet;
use libc::c_void; use libc::c_void;
use std::sync::Arc; use std::{cell::Cell, sync::Arc};
use wasmer_runtime_core::{ use wasmer_runtime_core::{
backend::{ProtectedCaller, Token}, backend::{ProtectedCaller, Token, UserTrapper},
error::RuntimeResult, error::RuntimeResult,
export::Context, export::Context,
module::{ExportIndex, ModuleInfo, ModuleInner}, module::{ExportIndex, ModuleInfo, ModuleInner},
@ -24,6 +24,19 @@ pub use self::unix::*;
#[cfg(windows)] #[cfg(windows)]
pub use self::windows::*; pub use self::windows::*;
thread_local! {
pub static TRAP_EARLY_DATA: Cell<Option<String>> = Cell::new(None);
}
pub struct Trapper;
impl UserTrapper for Trapper {
unsafe fn do_early_trap(&self, msg: String) -> ! {
TRAP_EARLY_DATA.with(|cell| cell.set(Some(msg)));
trigger_trap()
}
}
pub struct Caller { pub struct Caller {
func_export_set: HashSet<FuncIndex>, func_export_set: HashSet<FuncIndex>,
handler_data: HandlerData, handler_data: HandlerData,
@ -118,6 +131,10 @@ impl ProtectedCaller for Caller {
}) })
.collect()) .collect())
} }
fn get_early_trapper(&self) -> Box<dyn UserTrapper> {
Box::new(Trapper)
}
} }
fn get_func_from_index( fn get_func_from_index(

View File

@ -9,7 +9,7 @@
//! are very special, the async signal unsafety of Rust's TLS implementation generally does not affect the correctness here //! 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. //! unless you have memory unsafety elsewhere in your code.
//! //!
use crate::relocation::{TrapCode, TrapData, TrapSink}; use crate::relocation::{TrapCode, TrapData};
use crate::signal::HandlerData; use crate::signal::HandlerData;
use libc::{c_int, c_void, siginfo_t}; use libc::{c_int, c_void, siginfo_t};
use nix::sys::signal::{ use nix::sys::signal::{
@ -60,6 +60,12 @@ thread_local! {
pub static CURRENT_EXECUTABLE_BUFFER: Cell<*const c_void> = Cell::new(ptr::null()); pub static CURRENT_EXECUTABLE_BUFFER: Cell<*const c_void> = Cell::new(ptr::null());
} }
pub unsafe fn trigger_trap() -> ! {
let jmp_buf = SETJMP_BUFFER.with(|buf| buf.get());
longjmp(jmp_buf as *mut c_void, 0)
}
pub fn call_protected<T>(handler_data: &HandlerData, f: impl FnOnce() -> T) -> RuntimeResult<T> { pub fn call_protected<T>(handler_data: &HandlerData, f: impl FnOnce() -> T) -> RuntimeResult<T> {
unsafe { unsafe {
let jmp_buf = SETJMP_BUFFER.with(|buf| buf.get()); let jmp_buf = SETJMP_BUFFER.with(|buf| buf.get());
@ -72,54 +78,59 @@ pub fn call_protected<T>(handler_data: &HandlerData, f: impl FnOnce() -> T) -> R
let signum = setjmp(jmp_buf as *mut _); let signum = setjmp(jmp_buf as *mut _);
if signum != 0 { if signum != 0 {
*jmp_buf = prev_jmp_buf; *jmp_buf = prev_jmp_buf;
let (faulting_addr, inst_ptr) = CAUGHT_ADDRESSES.with(|cell| cell.get());
if let Some(TrapData { if let Some(msg) = super::TRAP_EARLY_DATA.with(|cell| cell.replace(None)) {
trapcode, Err(RuntimeError::User { msg })
srcloc: _, } else {
}) = handler_data.lookup(inst_ptr) let (faulting_addr, inst_ptr) = CAUGHT_ADDRESSES.with(|cell| cell.get());
{
Err(match Signal::from_c_int(signum) { if let Some(TrapData {
Ok(SIGILL) => match trapcode { trapcode,
TrapCode::BadSignature => RuntimeError::IndirectCallSignature { srcloc: _,
table: TableIndex::new(0), }) = handler_data.lookup(inst_ptr)
{
Err(match Signal::from_c_int(signum) {
Ok(SIGILL) => match trapcode {
TrapCode::BadSignature => RuntimeError::IndirectCallSignature {
table: TableIndex::new(0),
},
TrapCode::IndirectCallToNull => RuntimeError::IndirectCallToNull {
table: TableIndex::new(0),
},
TrapCode::HeapOutOfBounds => RuntimeError::OutOfBoundsAccess {
memory: MemoryIndex::new(0),
addr: None,
},
TrapCode::TableOutOfBounds => RuntimeError::TableOutOfBounds {
table: TableIndex::new(0),
},
_ => RuntimeError::Unknown {
msg: "unknown trap".to_string(),
},
}, },
TrapCode::IndirectCallToNull => RuntimeError::IndirectCallToNull { Ok(SIGSEGV) | Ok(SIGBUS) => RuntimeError::OutOfBoundsAccess {
table: TableIndex::new(0),
},
TrapCode::HeapOutOfBounds => RuntimeError::OutOfBoundsAccess {
memory: MemoryIndex::new(0), memory: MemoryIndex::new(0),
addr: None, addr: None,
}, },
TrapCode::TableOutOfBounds => RuntimeError::TableOutOfBounds { Ok(SIGFPE) => RuntimeError::IllegalArithmeticOperation,
table: TableIndex::new(0), _ => unimplemented!(),
}, }
_ => RuntimeError::Unknown { .into())
msg: "unknown trap".to_string(), } else {
}, let signal = match Signal::from_c_int(signum) {
}, Ok(SIGFPE) => "floating-point exception",
Ok(SIGSEGV) | Ok(SIGBUS) => RuntimeError::OutOfBoundsAccess { Ok(SIGILL) => "illegal instruction",
memory: MemoryIndex::new(0), Ok(SIGSEGV) => "segmentation violation",
addr: None, Ok(SIGBUS) => "bus error",
}, Err(_) => "error while getting the Signal",
Ok(SIGFPE) => RuntimeError::IllegalArithmeticOperation, _ => "unkown trapped signal",
_ => unimplemented!(), };
// When the trap-handler is fully implemented, this will return more information.
Err(RuntimeError::Unknown {
msg: format!("trap at {:p} - {}", faulting_addr, signal),
}
.into())
} }
.into())
} else {
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",
};
// When the trap-handler is fully implemented, this will return more information.
Err(RuntimeError::Unknown {
msg: format!("trap at {:p} - {}", faulting_addr, signal),
}
.into())
} }
} else { } else {
let ret = f(); // TODO: Switch stack? let ret = f(); // TODO: Switch stack?

View File

@ -4,3 +4,7 @@ use wasmer_runtime_core::error::RuntimeResult;
pub fn call_protected<T>(handler_data: &HandlerData, f: impl FnOnce() -> T) -> RuntimeResult<T> { pub fn call_protected<T>(handler_data: &HandlerData, f: impl FnOnce() -> T) -> RuntimeResult<T> {
unimplemented!("TODO"); unimplemented!("TODO");
} }
pub unsafe fn trigger_trap() -> ! {
unimplemented!("TODO");
}

View File

@ -1,7 +1,6 @@
test_ccall test_ccall
test_demangle_stacks test_demangle_stacks
emscripten_get_compiler_setting emscripten_get_compiler_setting
env
fs_exports fs_exports
getvalue_setvalue getvalue_setvalue
legacy_exported_runtime_numbers legacy_exported_runtime_numbers

View File

@ -1,15 +1,14 @@
/// NOTE: These syscalls only support wasm_32 for now because they take u32 offset /// NOTE: These syscalls only support wasm_32 for now because they take u32 offset
use libc::{ use libc::{
c_int, c_long, getenv, getgrnam as libc_getgrnam, getpwnam as libc_getpwnam, putenv, setenv, c_int, getenv, getgrnam as libc_getgrnam, getpwnam as libc_getpwnam, putenv, setenv, sysconf,
sysconf, unsetenv, unsetenv,
}; };
use std::ffi::CStr; use std::ffi::CStr;
use std::mem; use std::mem;
use std::os::raw::c_char; use std::os::raw::c_char;
use crate::env::call_malloc; use crate::env::call_malloc;
use crate::utils::{allocate_on_stack, copy_cstr_into_wasm, copy_terminated_array_of_cstrs}; use crate::utils::{copy_cstr_into_wasm, copy_terminated_array_of_cstrs};
use crate::EmscriptenData;
use wasmer_runtime_core::vm::Ctx; use wasmer_runtime_core::vm::Ctx;
// #[no_mangle] // #[no_mangle]

View File

@ -9,7 +9,6 @@ use crate::env::call_malloc;
use crate::utils::{copy_cstr_into_wasm, read_string_from_wasm}; use crate::utils::{copy_cstr_into_wasm, read_string_from_wasm};
use wasmer_runtime_core::vm::Ctx; use wasmer_runtime_core::vm::Ctx;
#[link(name = "c")]
extern "C" { extern "C" {
#[link_name = "_putenv"] #[link_name = "_putenv"]
pub fn putenv(s: *const c_char) -> c_int; pub fn putenv(s: *const c_char) -> c_int;

View File

@ -5,14 +5,14 @@ use wasmer_runtime_core::vm::Ctx;
// this cfg_attr will try to link with the legacy lib that does not inline printf // this cfg_attr will try to link with the legacy lib that does not inline printf
// this will allow for compiliation, but will produce a linker error if there is a problem // this will allow for compiliation, but will produce a linker error if there is a problem
// finding printf. // finding printf.
#[cfg_attr( //#[cfg_attr(
all(windows, target_env = "msvc"), // all(windows, target_env = "msvc"),
link(name = "legacy_stdio_definitions", kind = "static-nobundle") // link(name = "legacy_stdio_definitions", kind = "static-nobundle")
)] //)]
extern "C" { //extern "C" {
#[link_name = "printf"] // #[link_name = "printf"]
pub fn _printf(s: *const c_char, ...) -> c_int; // pub fn _printf(s: *const c_char, ...) -> c_int;
} //}
/// putchar /// putchar
pub fn putchar(chr: i32, ctx: &mut Ctx) { pub fn putchar(chr: i32, ctx: &mut Ctx) {
@ -22,8 +22,9 @@ pub fn putchar(chr: i32, ctx: &mut Ctx) {
/// printf /// printf
pub fn printf(memory_offset: i32, extra: i32, ctx: &mut Ctx) -> i32 { pub fn printf(memory_offset: i32, extra: i32, ctx: &mut Ctx) -> i32 {
debug!("emscripten::printf {}, {}", memory_offset, extra); debug!("emscripten::printf {}, {}", memory_offset, extra);
unsafe { // unsafe {
let addr = emscripten_memory_pointer!(ctx.memory(0), memory_offset) as _; // let addr = emscripten_memory_pointer!(ctx.memory(0), memory_offset) as _;
_printf(addr, extra) // _printf(addr, extra)
} // }
-1
} }

View File

@ -7,20 +7,19 @@ use wasmer_runtime_core::vm::Ctx;
pub fn __setjmp(env_addr: u32, ctx: &mut Ctx) -> c_int { pub fn __setjmp(env_addr: u32, ctx: &mut Ctx) -> c_int {
debug!("emscripten::__setjmp (setjmp)"); debug!("emscripten::__setjmp (setjmp)");
unsafe { unsafe {
unimplemented!() // Rather than using the env as the holder of the jump buffer pointer,
// // Rather than using the env as the holder of the jump buffer pointer, // we use the environment address to store the index relative to jumps
// // we use the environment address to store the index relative to jumps // so the address of the jump it's outside the wasm memory itself.
// // so the address of the jump it's outside the wasm memory itself. let jump_index = emscripten_memory_pointer!(ctx.memory(0), env_addr) as *mut i8;
// let jump_index = ctx.memory(0).as_ptr().add(env_addr as usize) as *mut i8; // We create the jump buffer outside of the wasm memory
// // We create the jump buffer outside of the wasm memory let jump_buf: UnsafeCell<[u32; 27]> = UnsafeCell::new([0; 27]);
// let jump_buf: UnsafeCell<[c_int; 27]> = UnsafeCell::new([0; 27]); let jumps = &mut get_emscripten_data(ctx).jumps;
// let jumps = &mut get_emscripten_data(ctx).jumps; let result = setjmp(jump_buf.get() as _);
// let result = setjmp(jump_buf.get() as _); // We set the jump index to be the last value of jumps
// // We set the jump index to be the last value of jumps *jump_index = jumps.len() as _;
// *jump_index = jumps.len() as _; // We hold the reference of the jump buffer
// // We hold the reference of the jump buffer jumps.push(jump_buf);
// jumps.push(jump_buf); result
// result
} }
} }

View File

@ -1,29 +1,20 @@
#[macro_use] #[macro_use]
extern crate wasmer_runtime_core; extern crate wasmer_runtime_core;
use byteorder::{ByteOrder, LittleEndian};
use libc::c_int;
use std::cell::UnsafeCell; use std::cell::UnsafeCell;
use std::{f64, ffi::c_void, fmt, mem, ptr}; use std::{f64, ffi::c_void};
use wasmer_runtime_core::{ use wasmer_runtime_core::{
error::CallResult, error::CallResult,
export::{Context, Export, FuncPointer}, export::Export,
func, func,
global::Global, global::Global,
import::{ImportObject, Namespace}, import::ImportObject,
imports, imports,
memory::Memory, memory::Memory,
table::Table, table::Table,
types::{ types::{ElementType, MemoryDescriptor, TableDescriptor, Value},
ElementType, FuncSig, GlobalDescriptor, MemoryDescriptor, TableDescriptor,
Type::{self, *},
Value,
},
units::Pages, units::Pages,
vm::Ctx, vm::Ctx,
vm::LocalGlobal,
vm::LocalMemory,
vm::LocalTable,
Func, Instance, Module, Func, Instance, Module,
}; };
@ -52,7 +43,7 @@ mod time;
mod utils; mod utils;
mod varargs; mod varargs;
pub use self::storage::align_memory; pub use self::storage::{align_memory, static_alloc};
pub use self::utils::{ pub use self::utils::{
allocate_cstr_on_stack, allocate_on_stack, get_emscripten_memory_size, allocate_cstr_on_stack, allocate_on_stack, get_emscripten_memory_size,
get_emscripten_table_size, is_emscripten_module, get_emscripten_table_size, is_emscripten_module,
@ -70,7 +61,7 @@ const STATIC_BUMP: u32 = 215_536;
// Then the stack. // Then the stack.
// Then 'dynamic' memory for sbrk. // Then 'dynamic' memory for sbrk.
const GLOBAL_BASE: u32 = 1024; const GLOBAL_BASE: u32 = 1024;
const STATIC_BASE: i32 = GLOBAL_BASE as i32; const STATIC_BASE: u32 = GLOBAL_BASE;
fn stacktop(static_bump: u32) -> u32 { fn stacktop(static_bump: u32) -> u32 {
align_memory(dynamictop_ptr(static_bump) + 4) align_memory(dynamictop_ptr(static_bump) + 4)
@ -121,15 +112,6 @@ impl<'a> EmscriptenData<'a> {
} }
} }
// impl<'a> fmt::Debug for EmscriptenData<'a> {
// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// f.debug_struct("EmscriptenData")
// .field("malloc", &(self.malloc as usize))
// .field("free", &(self.free as usize))
// .finish()
// }
// }
pub fn run_emscripten_instance( pub fn run_emscripten_instance(
_module: &Module, _module: &Module,
instance: &mut Instance, instance: &mut Instance,
@ -187,71 +169,28 @@ fn store_module_arguments(path: &str, args: Vec<&str>, ctx: &mut Ctx) -> (u32, u
(argc as u32, argv_offset) (argc as u32, argv_offset)
} }
pub fn emscripten_set_up_memory(memory: &mut Memory) { pub fn emscripten_set_up_memory(memory: &Memory, globals: &EmscriptenGlobalsData) {
let dynamictop_ptr = dynamictop_ptr(STATIC_BUMP) as usize; let dynamictop_ptr = globals.dynamictop_ptr;
let dynamictop_ptr_offset = dynamictop_ptr + mem::size_of::<u32>(); let stack_max = globals.stack_max;
// println!("value = {:?}"); let dynamic_base = align_memory(stack_max);
// We avoid failures of setting the u32 in our memory if it's out of bounds memory.view::<u32>()[(dynamictop_ptr / 4) as usize].set(dynamic_base);
unimplemented!()
// if dynamictop_ptr_offset > memory.len() {
// return; // TODO: We should panic instead?
// }
//
// // debug!("###### dynamic_base = {:?}", dynamic_base(STATIC_BUMP));
// // debug!("###### dynamictop_ptr = {:?}", dynamictop_ptr);
// // debug!("###### dynamictop_ptr_offset = {:?}", dynamictop_ptr_offset);
//
// let mem = &mut memory[dynamictop_ptr..dynamictop_ptr_offset];
// LittleEndian::write_u32(mem, dynamic_base(STATIC_BUMP));
}
macro_rules! mock_external {
($namespace:ident, $name:ident) => {{
fn _mocked_fn() -> i32 {
debug!("emscripten::{} <mock>", stringify!($name));
-1
}
$namespace.insert(
stringify!($name),
Export::Function {
func: unsafe { FuncPointer::new(_mocked_fn as _) },
ctx: Context::Internal,
signature: FuncSig {
params: vec![],
returns: vec![I32],
},
},
);
}};
}
macro_rules! global {
($value:expr) => {{
unsafe {
GlobalPointer::new(
// NOTE: Taking a shortcut here. LocalGlobal is a struct containing just u64.
std::mem::transmute::<&u64, *mut LocalGlobal>(&$value),
)
}
}};
} }
pub struct EmscriptenGlobalsData { pub struct EmscriptenGlobalsData {
abort: u64, abort: u64,
// Env namespace // Env namespace
stacktop: u64, stacktop: u32,
stack_max: u64, stack_max: u32,
dynamictop_ptr: u64, dynamictop_ptr: u32,
memory_base: u64, memory_base: u32,
table_base: u64, table_base: u32,
temp_double_ptr: u64, temp_double_ptr: u32,
// Global namespace // Global namespace
infinity: u64, infinity: f64,
nan: u64, nan: f64,
} }
pub struct EmscriptenGlobals { pub struct EmscriptenGlobals {
@ -265,7 +204,7 @@ pub struct EmscriptenGlobals {
} }
impl EmscriptenGlobals { impl EmscriptenGlobals {
pub fn new(module: &Module) -> Self { pub fn new(module: &Module /*, static_bump: u32 */) -> Self {
let (table_min, table_max) = get_emscripten_table_size(&module); let (table_min, table_max) = get_emscripten_table_size(&module);
let (memory_min, memory_max) = get_emscripten_memory_size(&module); let (memory_min, memory_max) = get_emscripten_memory_size(&module);
@ -275,7 +214,7 @@ impl EmscriptenGlobals {
maximum: memory_max, maximum: memory_max,
shared: false, shared: false,
}; };
let mut memory = Memory::new(memory_type).unwrap(); let memory = Memory::new(memory_type).unwrap();
let table_type = TableDescriptor { let table_type = TableDescriptor {
element: ElementType::Anyfunc, element: ElementType::Anyfunc,
@ -284,24 +223,38 @@ impl EmscriptenGlobals {
}; };
let mut table = Table::new(table_type).unwrap(); let mut table = Table::new(table_type).unwrap();
let memory_base = STATIC_BASE as u64; let data = {
let table_base = 0 as u64; let static_bump = STATIC_BUMP;
let temp_double_ptr = 0 as u64;
let data = EmscriptenGlobalsData {
abort: 0, // TODO review usage
// env
stacktop: stacktop(STATIC_BUMP) as _,
stack_max: stack_max(STATIC_BUMP) as _,
dynamictop_ptr: dynamictop_ptr(STATIC_BUMP) as _,
memory_base: memory_base,
table_base: table_base,
temp_double_ptr: temp_double_ptr,
// global let mut STATIC_TOP = STATIC_BASE + static_bump;
infinity: std::f64::INFINITY.to_bits() as _,
nan: std::f64::NAN.to_bits() as _, let memory_base = STATIC_BASE;
let table_base = 0;
let temp_double_ptr = STATIC_TOP;
STATIC_TOP += 16;
let dynamictop_ptr = static_alloc(&mut STATIC_TOP, 4);
let stacktop = align_memory(STATIC_TOP);
let stack_max = stacktop + TOTAL_STACK;
EmscriptenGlobalsData {
abort: 0,
stacktop,
stack_max,
dynamictop_ptr,
memory_base,
table_base,
temp_double_ptr,
infinity: std::f64::INFINITY,
nan: std::f64::NAN,
}
}; };
emscripten_set_up_memory(&memory, &data);
Self { Self {
data, data,
memory, memory,
@ -313,38 +266,21 @@ impl EmscriptenGlobals {
} }
pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject { pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject {
// use crate::varargs::VarArgs; imports! {
// let mut imports = ImportObject::new();
// let mut env_namespace = Namespace::new();
// let mut asm_namespace = Namespace::new();
// let mut global_namespace = Namespace::new();
// let mut global_math_namespace = Namespace::new();
// // Add globals.
// // NOTE: There is really no need for checks, these globals should always be available.
// // We generate a fake Context that traps on access
// let null_ctx = Context::External(ptr::null_mut());
// env_namespace.insert("memory".to_string(), Export::Memory(globals.memory.clone()));
// env_namespace.insert("table".to_string(), Export::Table(globals.table.clone()));
let import_object = imports! {
"env" => { "env" => {
"memory" => Export::Memory(globals.memory.clone()), "memory" => Export::Memory(globals.memory.clone()),
"table" => Export::Table(globals.table.clone()), "table" => Export::Table(globals.table.clone()),
// Globals // Globals
"STACKTOP" => Global::new(Value::I32(stacktop(STATIC_BUMP) as i32)), "STACKTOP" => Global::new(Value::I32(globals.data.stacktop as i32)),
"STACK_MAX" => Global::new(Value::I32(stack_max(STATIC_BUMP) as i32)), "STACK_MAX" => Global::new(Value::I32(globals.data.stack_max as i32)),
"DYNAMICTOP_PTR" => Global::new(Value::I32(dynamictop_ptr(STATIC_BUMP) as i32)), "DYNAMICTOP_PTR" => Global::new(Value::I32(globals.data.dynamictop_ptr as i32)),
"tableBase" => Global::new(Value::I32(0)), "tableBase" => Global::new(Value::I32(globals.data.table_base as i32)),
"__table_base" => Global::new(Value::I32(0)), "__table_base" => Global::new(Value::I32(globals.data.table_base as i32)),
"ABORT" => Global::new(Value::I32(0)), "ABORT" => Global::new(Value::I32(globals.data.abort as i32)),
"memoryBase" => Global::new(Value::I32(STATIC_BASE)), "memoryBase" => Global::new(Value::I32(globals.data.memory_base as i32)),
"__memory_base" => Global::new(Value::I32(STATIC_BASE)), "__memory_base" => Global::new(Value::I32(globals.data.memory_base as i32)),
"tempDoublePtr" => Global::new(Value::I32(0)), "tempDoublePtr" => Global::new(Value::I32(globals.data.temp_double_ptr as i32)),
// IO // IO
"printf" => func!(crate::io::printf), "printf" => func!(crate::io::printf),
@ -528,18 +464,7 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject
"asm2wasm" => { "asm2wasm" => {
"f64-rem" => func!(crate::math::f64_rem), "f64-rem" => func!(crate::math::f64_rem),
}, },
}; }
// mock_external!(env_namespace, _sched_yield);
// mock_external!(env_namespace, _llvm_stacksave);
// mock_external!(env_namespace, _getgrent);
// mock_external!(env_namespace, _dlerror);
// imports.register("env", env_namespace);
// imports.register("asm2wasm", asm_namespace);
// imports.register("global", global_namespace);
// imports.register("global.Math", global_math_namespace);
import_object
} }
/// The current version of this crate /// The current version of this crate

View File

@ -11,6 +11,16 @@ pub struct StdioCapturer {
stderr_reader: libc::c_int, stderr_reader: libc::c_int,
} }
#[cfg(not(target_os = "windows"))]
use libc::{STDERR_FILENO, STDOUT_FILENO};
#[cfg(target_os = "windows")]
const STDIN_FILENO: libc::c_int = 0;
#[cfg(target_os = "windows")]
const STDOUT_FILENO: libc::c_int = 1;
#[cfg(target_os = "windows")]
const STDERR_FILENO: libc::c_int = 2;
// Implementation inspired in // Implementation inspired in
// https://github.com/rust-lang/rust/blob/7d52cbce6db83e4fc2d8706b4e4b9c7da76cbcf8/src/test/run-pass/issues/issue-30490.rs // https://github.com/rust-lang/rust/blob/7d52cbce6db83e4fc2d8706b4e4b9c7da76cbcf8/src/test/run-pass/issues/issue-30490.rs
// Currently only works in Unix systems (Mac, Linux) // Currently only works in Unix systems (Mac, Linux)
@ -30,14 +40,14 @@ impl StdioCapturer {
} }
pub fn new() -> Self { pub fn new() -> Self {
let stdout_backup = unsafe { libc::dup(libc::STDOUT_FILENO) }; let stdout_backup = unsafe { libc::dup(STDOUT_FILENO) };
let stderr_backup = unsafe { libc::dup(libc::STDERR_FILENO) }; let stderr_backup = unsafe { libc::dup(STDERR_FILENO) };
let (stdout_reader, stdout_writer) = Self::pipe(); let (stdout_reader, stdout_writer) = Self::pipe();
let (stderr_reader, stderr_writer) = Self::pipe(); let (stderr_reader, stderr_writer) = Self::pipe();
assert!(unsafe { libc::dup2(stdout_writer, libc::STDOUT_FILENO) } > -1); assert!(unsafe { libc::dup2(stdout_writer, STDOUT_FILENO) } > -1);
assert!(unsafe { libc::dup2(stderr_writer, libc::STDERR_FILENO) } > -1); assert!(unsafe { libc::dup2(stderr_writer, STDERR_FILENO) } > -1);
// Make sure we close any duplicates of the writer end of the pipe, // Make sure we close any duplicates of the writer end of the pipe,
// otherwise we can get stuck reading from the pipe which has open // otherwise we can get stuck reading from the pipe which has open
@ -57,8 +67,8 @@ impl StdioCapturer {
// The Stdio passed into the Command took over (and closed) std{out, err} // The Stdio passed into the Command took over (and closed) std{out, err}
// so we should restore them as they were. // so we should restore them as they were.
assert!(unsafe { libc::dup2(self.stdout_backup, libc::STDOUT_FILENO) } > -1); assert!(unsafe { libc::dup2(self.stdout_backup, STDOUT_FILENO) } > -1);
assert!(unsafe { libc::dup2(self.stderr_backup, libc::STDERR_FILENO) } > -1); assert!(unsafe { libc::dup2(self.stderr_backup, STDERR_FILENO) } > -1);
let fd = FileDescriptor::new(self.stdout_reader); let fd = FileDescriptor::new(self.stdout_reader);
let mut reader = BufReader::new(fd); let mut reader = BufReader::new(fd);

View File

@ -4,14 +4,9 @@ pub fn align_memory(ptr: u32) -> u32 {
(ptr + 15) & !15 (ptr + 15) & !15
} }
// pub fn static_alloc(size: u32, static_top: &mut u32, memory: &Memory) -> u32 { pub fn static_alloc(static_top: &mut u32, size: u32) -> u32 {
// let old_static_top = *static_top; let old_static_top = *static_top;
// let total_memory = memory.maximum_size() * Memory::PAGE_SIZE; // NOTE: The `4294967280` is a u32 conversion of -16 as gotten from emscripten.
// // NOTE: The `4294967280` is a u32 conversion of -16 as gotten from emscripten. *static_top = (*static_top + size + 15) & 4294967280;
// *static_top = (*static_top + size + 15) & 4294967280; old_static_top
// assert!( }
// *static_top < total_memory,
// "not enough memory for static allocation - increase total_memory!"
// );
// old_static_top
// }

View File

@ -16,48 +16,30 @@ use byteorder::{ByteOrder, LittleEndian};
/// NOTE: TODO: These syscalls only support wasm_32 for now because they assume offsets are u32 /// NOTE: TODO: These syscalls only support wasm_32 for now because they assume offsets are u32
/// Syscall list: https://www.cs.utexas.edu/~bismith/test/syscalls/syscalls32.html /// Syscall list: https://www.cs.utexas.edu/~bismith/test/syscalls/syscalls32.html
use libc::{ use libc::{
accept,
bind,
// ENOTTY, // ENOTTY,
c_char,
c_int, c_int,
c_void, c_void,
chdir, chdir,
// fcntl, setsockopt, getppid // fcntl, setsockopt, getppid
close, close,
connect,
dup2, dup2,
exit, exit,
fstat, fstat,
getpeername,
getpid, getpid,
getsockname,
getsockopt,
// iovec, // iovec,
listen,
lseek, lseek,
mkdir,
off_t,
open, open,
read, read,
// readv, // readv,
recvfrom,
rmdir, rmdir,
// writev, // writev,
sendto,
setsockopt,
sockaddr,
socket,
ssize_t,
stat, stat,
write, write,
EINVAL,
// sockaddr_in, // sockaddr_in,
}; };
use wasmer_runtime_core::vm::Ctx; use wasmer_runtime_core::vm::Ctx;
use super::env; use super::env;
use std::mem;
use std::slice; use std::slice;
// use std::sys::fd::FileDesc; // use std::sys::fd::FileDesc;

View File

@ -1,5 +1,4 @@
use crate::varargs::VarArgs; use crate::varargs::VarArgs;
use byteorder::{ByteOrder, LittleEndian};
/// NOTE: TODO: These syscalls only support wasm_32 for now because they assume offsets are u32 /// NOTE: TODO: These syscalls only support wasm_32 for now because they assume offsets are u32
/// Syscall list: https://www.cs.utexas.edu/~bismith/test/syscalls/syscalls32.html /// Syscall list: https://www.cs.utexas.edu/~bismith/test/syscalls/syscalls32.html
use libc::{ use libc::{
@ -9,39 +8,28 @@ use libc::{
c_char, c_char,
c_int, c_int,
c_void, c_void,
chdir,
chown, chown,
// fcntl, setsockopt, getppid // fcntl, setsockopt, getppid
close,
connect, connect,
dup2, dup2,
exit,
fcntl, fcntl,
fstat,
getgid, getgid,
getpeername, getpeername,
getpid,
getsockname, getsockname,
getsockopt, getsockopt,
gid_t,
in_addr_t, in_addr_t,
in_port_t, in_port_t,
ioctl, ioctl,
// iovec, // iovec,
listen, listen,
lseek,
mkdir, mkdir,
msghdr, msghdr,
off_t,
open,
pid_t, pid_t,
pread, pread,
pwrite, pwrite,
read,
// readv, // readv,
recvfrom, recvfrom,
recvmsg, recvmsg,
rmdir,
// ENOTTY, // ENOTTY,
rusage, rusage,
sa_family_t, sa_family_t,
@ -54,11 +42,8 @@ use libc::{
sockaddr, sockaddr,
socket, socket,
socklen_t, socklen_t,
ssize_t,
stat,
uname, uname,
utsname, utsname,
write,
EINVAL, EINVAL,
// sockaddr_in, // sockaddr_in,
FIOCLEX, FIOCLEX,
@ -71,9 +56,7 @@ use libc::{
}; };
use wasmer_runtime_core::vm::Ctx; use wasmer_runtime_core::vm::Ctx;
use super::env;
use std::mem; use std::mem;
use std::slice;
// Linking to functions that are not provided by rust libc // Linking to functions that are not provided by rust libc
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]

View File

@ -1,16 +1,18 @@
use super::utils::{copy_cstr_into_wasm, write_to_buf}; use super::utils::{copy_cstr_into_wasm, write_to_buf};
use libc::{c_char, c_int, time_t}; use libc::{c_char, c_int};
use std::mem; use std::mem;
use std::time::SystemTime; use std::time::SystemTime;
#[cfg(not(target_os = "windows"))] #[cfg(not(target_os = "windows"))]
use libc::{clockid_t, time as libc_time}; use libc::{clockid_t, time as libc_time};
#[cfg(target_os = "windows")]
use libc::time_t;
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
type clockid_t = c_int; type clockid_t = c_int;
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
#[link(name = "c")]
extern "C" { extern "C" {
#[link_name = "time"] #[link_name = "time"]
pub fn libc_time(s: *const time_t) -> time_t; pub fn libc_time(s: *const time_t) -> time_t;

View File

@ -2,10 +2,8 @@ use super::env;
use super::env::get_emscripten_data; use super::env::get_emscripten_data;
use libc::stat; use libc::stat;
use std::ffi::CStr; use std::ffi::CStr;
use std::ffi::CString;
use std::mem::size_of; use std::mem::size_of;
use std::os::raw::c_char; use std::os::raw::c_char;
use std::os::raw::c_int;
use std::slice; use std::slice;
use wasmer_runtime_core::memory::Memory; use wasmer_runtime_core::memory::Memory;
use wasmer_runtime_core::{ use wasmer_runtime_core::{

View File

@ -1,5 +1,4 @@
#[test] #[test]
#[ignore]
fn test_env() { fn test_env() {
assert_emscripten_output!( assert_emscripten_output!(
"../../emtests/env.wasm", "../../emtests/env.wasm",

View File

@ -83,6 +83,12 @@ pub trait ProtectedCaller: Send + Sync {
vmctx: *mut vm::Ctx, vmctx: *mut vm::Ctx,
_: Token, _: Token,
) -> RuntimeResult<Vec<Value>>; ) -> RuntimeResult<Vec<Value>>;
fn get_early_trapper(&self) -> Box<dyn UserTrapper>;
}
pub trait UserTrapper {
unsafe fn do_early_trap(&self, msg: String) -> !;
} }
pub trait FuncResolver: Send + Sync { pub trait FuncResolver: Send + Sync {

View File

@ -106,6 +106,9 @@ pub enum RuntimeError {
table: TableIndex, table: TableIndex,
}, },
IllegalArithmeticOperation, IllegalArithmeticOperation,
User {
msg: String,
},
Unknown { Unknown {
msg: String, msg: String,
}, },

View File

@ -7,7 +7,6 @@ use crate::{
import::{ImportObject, LikeNamespace}, import::{ImportObject, LikeNamespace},
memory::Memory, memory::Memory,
module::{ExportIndex, Module, ModuleInner}, module::{ExportIndex, Module, ModuleInner},
sig_registry::SigRegistry,
table::Table, table::Table,
typed_func::{Func, Safe, WasmTypeList}, typed_func::{Func, Safe, WasmTypeList},
types::{FuncIndex, FuncSig, GlobalIndex, LocalOrImport, MemoryIndex, TableIndex, Value}, types::{FuncIndex, FuncSig, GlobalIndex, LocalOrImport, MemoryIndex, TableIndex, Value},

View File

@ -3,6 +3,7 @@ use crate::{
error::Result, error::Result,
import::ImportObject, import::ImportObject,
structures::{Map, TypedIndex}, structures::{Map, TypedIndex},
typed_func::EARLY_TRAPPER,
types::{ types::{
FuncIndex, FuncSig, GlobalDescriptor, GlobalIndex, GlobalInit, ImportedFuncIndex, FuncIndex, FuncSig, GlobalDescriptor, GlobalIndex, GlobalInit, ImportedFuncIndex,
ImportedGlobalIndex, ImportedMemoryIndex, ImportedTableIndex, Initializer, ImportedGlobalIndex, ImportedMemoryIndex, ImportedTableIndex, Initializer,
@ -63,6 +64,10 @@ pub struct Module(#[doc(hidden)] pub Arc<ModuleInner>);
impl Module { impl Module {
pub(crate) fn new(inner: Arc<ModuleInner>) -> Self { pub(crate) fn new(inner: Arc<ModuleInner>) -> Self {
unsafe {
EARLY_TRAPPER
.with(|ucell| *ucell.get() = Some(inner.protected_caller.get_early_trapper()));
}
Module(inner) Module(inner)
} }

View File

@ -18,6 +18,32 @@ pub struct Memory {
} }
impl Memory { impl Memory {
pub fn with_size_protect(size: usize, protection: Protect) -> Result<Self, String> {
if size == 0 {
return Ok(Self {
ptr: ptr::null_mut(),
size: 0,
protection,
});
}
let size = round_up_to_page_size(size, page_size::get());
let protect = protection.to_protect_const();
let ptr = unsafe { VirtualAlloc(ptr::null_mut(), size, MEM_RESERVE, protect) };
if ptr.is_null() {
Err("unable to allocate memory".to_string())
} else {
Ok(Self {
ptr: ptr as *mut u8,
size,
protection,
})
}
}
pub fn with_size(size: usize) -> Result<Self, String> { pub fn with_size(size: usize) -> Result<Self, String> {
if size == 0 { if size == 0 {
return Ok(Self { return Ok(Self {

View File

@ -1,11 +1,16 @@
use crate::{ use crate::{
backend::UserTrapper,
error::RuntimeError, error::RuntimeError,
export::{Context, Export, FuncPointer}, export::{Context, Export, FuncPointer},
import::IsExport, import::IsExport,
types::{FuncSig, Type, WasmExternType}, types::{FuncSig, Type, WasmExternType},
vm::Ctx, vm::Ctx,
}; };
use std::{marker::PhantomData, mem, ptr, sync::Arc}; use std::{cell::UnsafeCell, fmt, marker::PhantomData, mem, panic, ptr, sync::Arc};
thread_local! {
pub static EARLY_TRAPPER: UnsafeCell<Option<Box<dyn UserTrapper>>> = UnsafeCell::new(None);
}
pub trait Safeness {} pub trait Safeness {}
pub struct Safe; pub struct Safe;
@ -28,9 +33,44 @@ where
Args: WasmTypeList, Args: WasmTypeList,
Rets: WasmTypeList, Rets: WasmTypeList,
{ {
fn to_raw(self) -> *const (); fn to_raw(&self) -> *const ();
} }
pub trait TrapEarly<Rets>
where
Rets: WasmTypeList,
{
fn report(self) -> Result<Rets, String>;
}
impl<Rets> TrapEarly<Rets> for Rets
where
Rets: WasmTypeList,
{
fn report(self) -> Result<Rets, String> {
Ok(self)
}
}
impl<Rets, E> TrapEarly<Rets> for Result<Rets, E>
where
Rets: WasmTypeList,
E: fmt::Debug,
{
fn report(self) -> Result<Rets, String> {
self.map_err(|err| format!("Error: {:?}", err))
}
}
// pub fn Func<'a, Args, Rets, F>(f: F) -> Func<'a, Args, Rets, Unsafe>
// where
// Args: WasmTypeList,
// Rets: WasmTypeList,
// F: ExternalFunction<Args, Rets>
// {
// Func::new(f)
// }
pub struct Func<'a, Args = (), Rets = (), Safety: Safeness = Safe> { pub struct Func<'a, Args = (), Rets = (), Safety: Safeness = Safe> {
f: *const (), f: *const (),
ctx: *mut Ctx, ctx: *mut Ctx,
@ -143,18 +183,41 @@ macro_rules! impl_traits {
} }
} }
impl< $( $x: WasmExternType, )* Rets: WasmTypeList, FN: Fn( $( $x, )* &mut Ctx) -> Rets> ExternalFunction<($( $x ),*), Rets> for FN { impl< $( $x: WasmExternType, )* Rets: WasmTypeList, Trap: TrapEarly<Rets>, FN: Fn( $( $x, )* &mut Ctx) -> Trap> ExternalFunction<($( $x ),*), Rets> for FN {
#[allow(non_snake_case)] #[allow(non_snake_case)]
fn to_raw(self) -> *const () { fn to_raw(&self) -> *const () {
assert_eq!(mem::size_of::<Self>(), 0, "you cannot use a closure that captures state for `Func`."); assert_eq!(mem::size_of::<Self>(), 0, "you cannot use a closure that captures state for `Func`.");
extern fn wrap<$( $x: WasmExternType, )* Rets: WasmTypeList, FN: Fn( $( $x, )* &mut Ctx) -> Rets>( $( $x: $x, )* ctx: &mut Ctx) -> Rets::CStruct { extern fn wrap<$( $x: WasmExternType, )* Rets: WasmTypeList, Trap: TrapEarly<Rets>, FN: Fn( $( $x, )* &mut Ctx) -> Trap>( $( $x: $x, )* ctx: &mut Ctx) -> Rets::CStruct {
let f: FN = unsafe { mem::transmute_copy(&()) }; let f: FN = unsafe { mem::transmute_copy(&()) };
let rets = f( $( $x, )* ctx);
rets.into_c_struct() let msg = match panic::catch_unwind(panic::AssertUnwindSafe(|| {
f( $( $x, )* ctx).report()
})) {
Ok(Ok(returns)) => return returns.into_c_struct(),
Ok(Err(err)) => err,
Err(err) => {
if let Some(s) = err.downcast_ref::<&str>() {
s.to_string()
} else if let Some(s) = err.downcast_ref::<String>() {
s.clone()
} else {
"a panic occurred, but no additional information is available".to_string()
}
},
};
unsafe {
if let Some(early_trapper) = &*EARLY_TRAPPER.with(|ucell| ucell.get()) {
early_trapper.do_early_trap(msg)
} else {
eprintln!("panic handling not setup");
std::process::exit(1)
}
}
} }
wrap::<$( $x, )* Rets, Self> as *const () wrap::<$( $x, )* Rets, Trap, Self> as *const ()
} }
} }

View File

@ -493,7 +493,7 @@ mod vm_ctx_tests {
fn generate_module() -> ModuleInner { fn generate_module() -> ModuleInner {
use super::Func; use super::Func;
use crate::backend::{Backend, FuncResolver, ProtectedCaller, Token}; use crate::backend::{Backend, FuncResolver, ProtectedCaller, Token, UserTrapper};
use crate::error::RuntimeResult; use crate::error::RuntimeResult;
use crate::types::{FuncIndex, LocalFuncIndex, Value}; use crate::types::{FuncIndex, LocalFuncIndex, Value};
use hashbrown::HashMap; use hashbrown::HashMap;
@ -520,6 +520,9 @@ mod vm_ctx_tests {
) -> RuntimeResult<Vec<Value>> { ) -> RuntimeResult<Vec<Value>> {
Ok(vec![]) Ok(vec![])
} }
fn get_early_trapper(&self) -> Box<dyn UserTrapper> {
unimplemented!()
}
} }
ModuleInner { ModuleInner {

View File

@ -3,7 +3,7 @@ use wabt::wat2wasm;
use wasmer_clif_backend::CraneliftCompiler; use wasmer_clif_backend::CraneliftCompiler;
use wasmer_runtime_core::{ use wasmer_runtime_core::{
cache::Cache, cache::Cache,
error::Result, error,
global::Global, global::Global,
memory::Memory, memory::Memory,
prelude::*, prelude::*,
@ -14,7 +14,7 @@ use wasmer_runtime_core::{
static EXAMPLE_WASM: &'static [u8] = include_bytes!("simple.wasm"); static EXAMPLE_WASM: &'static [u8] = include_bytes!("simple.wasm");
fn main() -> Result<()> { fn main() -> error::Result<()> {
let compiler = CraneliftCompiler::new(); let compiler = CraneliftCompiler::new();
let wasm_binary = wat2wasm(IMPORT_MODULE.as_bytes()).expect("WAST not valid or malformed"); let wasm_binary = wat2wasm(IMPORT_MODULE.as_bytes()).expect("WAST not valid or malformed");
@ -61,14 +61,14 @@ fn main() -> Result<()> {
Ok(()) Ok(())
} }
fn print_num(n: i32, ctx: &mut vm::Ctx) -> i32 { fn print_num(n: i32, ctx: &mut vm::Ctx) -> Result<i32, ()> {
println!("print_num({})", n); println!("print_num({})", n);
let memory: &Memory = ctx.memory(0); let memory: &Memory = ctx.memory(0);
let a: i32 = memory.view()[0].get(); let a: i32 = memory.view()[0].get();
a + n + 1 Ok(a + n + 1)
} }
static IMPORT_MODULE: &str = r#" static IMPORT_MODULE: &str = r#"