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:
except:
- master
@ -9,143 +6,27 @@ version: "{build} ~ {branch}"
os: Visual Studio 2017
matrix:
allow_failures:
- CHANNEL: stable
# - ABI: gnu
environment:
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
ARCH: x86_64
ABI: gnu
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
ABI: msvc
TARGET: x86_64-pc-windows-msvc
install:
- echo %PATH%
# force branch checkout (if knowable), then reset to the specific commit ## (can be needed for accurate code coverage info)
# * this allows later apps to see the branch name using standard `git branch` operations, yet always builds the correct specific commit
# * 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
- appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe
- rustup-init.exe -yv --default-host %target%
- set PATH=%PATH%;%USERPROFILE%\.cargo\bin
- rustc -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:
- path: target\%TARGET%\debug\wasmer.exe
- path: target\debug\wasmer.exe
name: wasmer.exe
build_script:
- set BUILD_CMD=cargo +%TOOLCHAIN% build --target=%TARGET%
- echo [ %BUILD_CMD% ] & %BUILD_CMD%
- cargo build --verbose
test_script:
- set TEST_CMD=cargo +%TOOLCHAIN% test --target=%TARGET% --no-fail-fast
- echo [ %TEST_CMD% ] & %TEST_CMD%
- set RUST_BACKTRACE=1
- cargo test --verbose

View File

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

View File

@ -23,8 +23,8 @@ install:
integration-tests: release
echo "Running Integration Tests"
# Commented for now until we fix emscripten
# ./integration_tests/nginx/test.sh
./integration_tests/lua/test.sh
./integration_tests/nginx/test.sh
lint:
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},
};
use wasmer_runtime_core::{
backend::{Backend, FuncResolver, ProtectedCaller, Token},
backend::{Backend, FuncResolver, ProtectedCaller, Token, UserTrapper},
error::{CompileResult, RuntimeResult},
module::{ModuleInfo, ModuleInner, StringTable},
structures::{Map, TypedIndex},
@ -51,6 +51,10 @@ impl ProtectedCaller for Placeholder {
) -> RuntimeResult<Vec<Value>> {
Ok(vec![])
}
fn get_early_trapper(&self) -> Box<dyn UserTrapper> {
unimplemented!()
}
}
/// 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,
};
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)]
pub struct FuncResolverBuilder {
resolver: FuncResolver,
@ -215,7 +222,10 @@ impl FuncResolverBuilder {
LibCall::FloorF64 => libcalls::floorf64 as isize,
LibCall::TruncF64 => libcalls::truncf64 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() {
"i32print" => i32_print as isize,

View File

@ -2,9 +2,9 @@ use crate::relocation::{TrapData, TrapSink};
use crate::trampoline::Trampolines;
use hashbrown::HashSet;
use libc::c_void;
use std::sync::Arc;
use std::{cell::Cell, sync::Arc};
use wasmer_runtime_core::{
backend::{ProtectedCaller, Token},
backend::{ProtectedCaller, Token, UserTrapper},
error::RuntimeResult,
export::Context,
module::{ExportIndex, ModuleInfo, ModuleInner},
@ -24,6 +24,19 @@ pub use self::unix::*;
#[cfg(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 {
func_export_set: HashSet<FuncIndex>,
handler_data: HandlerData,
@ -118,6 +131,10 @@ impl ProtectedCaller for Caller {
})
.collect())
}
fn get_early_trapper(&self) -> Box<dyn UserTrapper> {
Box::new(Trapper)
}
}
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
//! 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 libc::{c_int, c_void, siginfo_t};
use nix::sys::signal::{
@ -60,6 +60,12 @@ thread_local! {
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> {
unsafe {
let jmp_buf = SETJMP_BUFFER.with(|buf| buf.get());
@ -72,6 +78,10 @@ pub fn call_protected<T>(handler_data: &HandlerData, f: impl FnOnce() -> T) -> R
let signum = setjmp(jmp_buf as *mut _);
if signum != 0 {
*jmp_buf = prev_jmp_buf;
if let Some(msg) = super::TRAP_EARLY_DATA.with(|cell| cell.replace(None)) {
Err(RuntimeError::User { msg })
} else {
let (faulting_addr, inst_ptr) = CAUGHT_ADDRESSES.with(|cell| cell.get());
if let Some(TrapData {
@ -121,6 +131,7 @@ pub fn call_protected<T>(handler_data: &HandlerData, f: impl FnOnce() -> T) -> R
}
.into())
}
}
} else {
let ret = f(); // TODO: Switch stack?
*jmp_buf = prev_jmp_buf;

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> {
unimplemented!("TODO");
}
pub unsafe fn trigger_trap() -> ! {
unimplemented!("TODO");
}

View File

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

View File

@ -1,15 +1,14 @@
/// NOTE: These syscalls only support wasm_32 for now because they take u32 offset
use libc::{
c_int, c_long, getenv, getgrnam as libc_getgrnam, getpwnam as libc_getpwnam, putenv, setenv,
sysconf, unsetenv,
c_int, getenv, getgrnam as libc_getgrnam, getpwnam as libc_getpwnam, putenv, setenv, sysconf,
unsetenv,
};
use std::ffi::CStr;
use std::mem;
use std::os::raw::c_char;
use crate::env::call_malloc;
use crate::utils::{allocate_on_stack, copy_cstr_into_wasm, copy_terminated_array_of_cstrs};
use crate::EmscriptenData;
use crate::utils::{copy_cstr_into_wasm, copy_terminated_array_of_cstrs};
use wasmer_runtime_core::vm::Ctx;
// #[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 wasmer_runtime_core::vm::Ctx;
#[link(name = "c")]
extern "C" {
#[link_name = "_putenv"]
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 will allow for compiliation, but will produce a linker error if there is a problem
// finding printf.
#[cfg_attr(
all(windows, target_env = "msvc"),
link(name = "legacy_stdio_definitions", kind = "static-nobundle")
)]
extern "C" {
#[link_name = "printf"]
pub fn _printf(s: *const c_char, ...) -> c_int;
}
//#[cfg_attr(
// all(windows, target_env = "msvc"),
// link(name = "legacy_stdio_definitions", kind = "static-nobundle")
//)]
//extern "C" {
// #[link_name = "printf"]
// pub fn _printf(s: *const c_char, ...) -> c_int;
//}
/// putchar
pub fn putchar(chr: i32, ctx: &mut Ctx) {
@ -22,8 +22,9 @@ pub fn putchar(chr: i32, ctx: &mut Ctx) {
/// printf
pub fn printf(memory_offset: i32, extra: i32, ctx: &mut Ctx) -> i32 {
debug!("emscripten::printf {}, {}", memory_offset, extra);
unsafe {
let addr = emscripten_memory_pointer!(ctx.memory(0), memory_offset) as _;
_printf(addr, extra)
}
// unsafe {
// let addr = emscripten_memory_pointer!(ctx.memory(0), memory_offset) as _;
// _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 {
debug!("emscripten::__setjmp (setjmp)");
unsafe {
unimplemented!()
// // 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
// // so the address of the jump it's outside the wasm memory itself.
// 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
// let jump_buf: UnsafeCell<[c_int; 27]> = UnsafeCell::new([0; 27]);
// let jumps = &mut get_emscripten_data(ctx).jumps;
// let result = setjmp(jump_buf.get() as _);
// // We set the jump index to be the last value of jumps
// *jump_index = jumps.len() as _;
// // We hold the reference of the jump buffer
// jumps.push(jump_buf);
// result
// 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
// 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;
// We create the jump buffer outside of the wasm memory
let jump_buf: UnsafeCell<[u32; 27]> = UnsafeCell::new([0; 27]);
let jumps = &mut get_emscripten_data(ctx).jumps;
let result = setjmp(jump_buf.get() as _);
// We set the jump index to be the last value of jumps
*jump_index = jumps.len() as _;
// We hold the reference of the jump buffer
jumps.push(jump_buf);
result
}
}

View File

@ -1,29 +1,20 @@
#[macro_use]
extern crate wasmer_runtime_core;
use byteorder::{ByteOrder, LittleEndian};
use libc::c_int;
use std::cell::UnsafeCell;
use std::{f64, ffi::c_void, fmt, mem, ptr};
use std::{f64, ffi::c_void};
use wasmer_runtime_core::{
error::CallResult,
export::{Context, Export, FuncPointer},
export::Export,
func,
global::Global,
import::{ImportObject, Namespace},
import::ImportObject,
imports,
memory::Memory,
table::Table,
types::{
ElementType, FuncSig, GlobalDescriptor, MemoryDescriptor, TableDescriptor,
Type::{self, *},
Value,
},
types::{ElementType, MemoryDescriptor, TableDescriptor, Value},
units::Pages,
vm::Ctx,
vm::LocalGlobal,
vm::LocalMemory,
vm::LocalTable,
Func, Instance, Module,
};
@ -52,7 +43,7 @@ mod time;
mod utils;
mod varargs;
pub use self::storage::align_memory;
pub use self::storage::{align_memory, static_alloc};
pub use self::utils::{
allocate_cstr_on_stack, allocate_on_stack, get_emscripten_memory_size,
get_emscripten_table_size, is_emscripten_module,
@ -70,7 +61,7 @@ const STATIC_BUMP: u32 = 215_536;
// Then the stack.
// Then 'dynamic' memory for sbrk.
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 {
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(
_module: &Module,
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)
}
pub fn emscripten_set_up_memory(memory: &mut Memory) {
let dynamictop_ptr = dynamictop_ptr(STATIC_BUMP) as usize;
let dynamictop_ptr_offset = dynamictop_ptr + mem::size_of::<u32>();
pub fn emscripten_set_up_memory(memory: &Memory, globals: &EmscriptenGlobalsData) {
let dynamictop_ptr = globals.dynamictop_ptr;
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
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),
)
}
}};
memory.view::<u32>()[(dynamictop_ptr / 4) as usize].set(dynamic_base);
}
pub struct EmscriptenGlobalsData {
abort: u64,
// Env namespace
stacktop: u64,
stack_max: u64,
dynamictop_ptr: u64,
memory_base: u64,
table_base: u64,
temp_double_ptr: u64,
stacktop: u32,
stack_max: u32,
dynamictop_ptr: u32,
memory_base: u32,
table_base: u32,
temp_double_ptr: u32,
// Global namespace
infinity: u64,
nan: u64,
infinity: f64,
nan: f64,
}
pub struct EmscriptenGlobals {
@ -265,7 +204,7 @@ pub struct 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 (memory_min, memory_max) = get_emscripten_memory_size(&module);
@ -275,7 +214,7 @@ impl EmscriptenGlobals {
maximum: memory_max,
shared: false,
};
let mut memory = Memory::new(memory_type).unwrap();
let memory = Memory::new(memory_type).unwrap();
let table_type = TableDescriptor {
element: ElementType::Anyfunc,
@ -284,24 +223,38 @@ impl EmscriptenGlobals {
};
let mut table = Table::new(table_type).unwrap();
let memory_base = STATIC_BASE as u64;
let table_base = 0 as u64;
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,
let data = {
let static_bump = STATIC_BUMP;
// global
infinity: std::f64::INFINITY.to_bits() as _,
nan: std::f64::NAN.to_bits() as _,
let mut STATIC_TOP = STATIC_BASE + static_bump;
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 {
data,
memory,
@ -313,38 +266,21 @@ impl EmscriptenGlobals {
}
pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject {
// use crate::varargs::VarArgs;
// 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! {
imports! {
"env" => {
"memory" => Export::Memory(globals.memory.clone()),
"table" => Export::Table(globals.table.clone()),
// Globals
"STACKTOP" => Global::new(Value::I32(stacktop(STATIC_BUMP) as i32)),
"STACK_MAX" => Global::new(Value::I32(stack_max(STATIC_BUMP) as i32)),
"DYNAMICTOP_PTR" => Global::new(Value::I32(dynamictop_ptr(STATIC_BUMP) as i32)),
"tableBase" => Global::new(Value::I32(0)),
"__table_base" => Global::new(Value::I32(0)),
"ABORT" => Global::new(Value::I32(0)),
"memoryBase" => Global::new(Value::I32(STATIC_BASE)),
"__memory_base" => Global::new(Value::I32(STATIC_BASE)),
"tempDoublePtr" => Global::new(Value::I32(0)),
"STACKTOP" => Global::new(Value::I32(globals.data.stacktop as i32)),
"STACK_MAX" => Global::new(Value::I32(globals.data.stack_max as i32)),
"DYNAMICTOP_PTR" => Global::new(Value::I32(globals.data.dynamictop_ptr as i32)),
"tableBase" => Global::new(Value::I32(globals.data.table_base as i32)),
"__table_base" => Global::new(Value::I32(globals.data.table_base as i32)),
"ABORT" => Global::new(Value::I32(globals.data.abort as i32)),
"memoryBase" => Global::new(Value::I32(globals.data.memory_base as i32)),
"__memory_base" => Global::new(Value::I32(globals.data.memory_base as i32)),
"tempDoublePtr" => Global::new(Value::I32(globals.data.temp_double_ptr as i32)),
// IO
"printf" => func!(crate::io::printf),
@ -528,18 +464,7 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject
"asm2wasm" => {
"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

View File

@ -11,6 +11,16 @@ pub struct StdioCapturer {
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
// https://github.com/rust-lang/rust/blob/7d52cbce6db83e4fc2d8706b4e4b9c7da76cbcf8/src/test/run-pass/issues/issue-30490.rs
// Currently only works in Unix systems (Mac, Linux)
@ -30,14 +40,14 @@ impl StdioCapturer {
}
pub fn new() -> Self {
let stdout_backup = unsafe { libc::dup(libc::STDOUT_FILENO) };
let stderr_backup = unsafe { libc::dup(libc::STDERR_FILENO) };
let stdout_backup = unsafe { libc::dup(STDOUT_FILENO) };
let stderr_backup = unsafe { libc::dup(STDERR_FILENO) };
let (stdout_reader, stdout_writer) = Self::pipe();
let (stderr_reader, stderr_writer) = Self::pipe();
assert!(unsafe { libc::dup2(stdout_writer, libc::STDOUT_FILENO) } > -1);
assert!(unsafe { libc::dup2(stderr_writer, libc::STDERR_FILENO) } > -1);
assert!(unsafe { libc::dup2(stdout_writer, STDOUT_FILENO) } > -1);
assert!(unsafe { libc::dup2(stderr_writer, STDERR_FILENO) } > -1);
// 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
@ -57,8 +67,8 @@ impl StdioCapturer {
// The Stdio passed into the Command took over (and closed) std{out, err}
// so we should restore them as they were.
assert!(unsafe { libc::dup2(self.stdout_backup, libc::STDOUT_FILENO) } > -1);
assert!(unsafe { libc::dup2(self.stderr_backup, libc::STDERR_FILENO) } > -1);
assert!(unsafe { libc::dup2(self.stdout_backup, STDOUT_FILENO) } > -1);
assert!(unsafe { libc::dup2(self.stderr_backup, STDERR_FILENO) } > -1);
let fd = FileDescriptor::new(self.stdout_reader);
let mut reader = BufReader::new(fd);

View File

@ -4,14 +4,9 @@ pub fn align_memory(ptr: u32) -> u32 {
(ptr + 15) & !15
}
// pub fn static_alloc(size: u32, static_top: &mut u32, memory: &Memory) -> u32 {
// 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.
// *static_top = (*static_top + size + 15) & 4294967280;
// assert!(
// *static_top < total_memory,
// "not enough memory for static allocation - increase total_memory!"
// );
// old_static_top
// }
pub fn static_alloc(static_top: &mut u32, size: u32) -> u32 {
let old_static_top = *static_top;
// NOTE: The `4294967280` is a u32 conversion of -16 as gotten from emscripten.
*static_top = (*static_top + size + 15) & 4294967280;
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
/// Syscall list: https://www.cs.utexas.edu/~bismith/test/syscalls/syscalls32.html
use libc::{
accept,
bind,
// ENOTTY,
c_char,
c_int,
c_void,
chdir,
// fcntl, setsockopt, getppid
close,
connect,
dup2,
exit,
fstat,
getpeername,
getpid,
getsockname,
getsockopt,
// iovec,
listen,
lseek,
mkdir,
off_t,
open,
read,
// readv,
recvfrom,
rmdir,
// writev,
sendto,
setsockopt,
sockaddr,
socket,
ssize_t,
stat,
write,
EINVAL,
// sockaddr_in,
};
use wasmer_runtime_core::vm::Ctx;
use super::env;
use std::mem;
use std::slice;
// use std::sys::fd::FileDesc;

View File

@ -1,5 +1,4 @@
use crate::varargs::VarArgs;
use byteorder::{ByteOrder, LittleEndian};
/// 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
use libc::{
@ -9,39 +8,28 @@ use libc::{
c_char,
c_int,
c_void,
chdir,
chown,
// fcntl, setsockopt, getppid
close,
connect,
dup2,
exit,
fcntl,
fstat,
getgid,
getpeername,
getpid,
getsockname,
getsockopt,
gid_t,
in_addr_t,
in_port_t,
ioctl,
// iovec,
listen,
lseek,
mkdir,
msghdr,
off_t,
open,
pid_t,
pread,
pwrite,
read,
// readv,
recvfrom,
recvmsg,
rmdir,
// ENOTTY,
rusage,
sa_family_t,
@ -54,11 +42,8 @@ use libc::{
sockaddr,
socket,
socklen_t,
ssize_t,
stat,
uname,
utsname,
write,
EINVAL,
// sockaddr_in,
FIOCLEX,
@ -71,9 +56,7 @@ use libc::{
};
use wasmer_runtime_core::vm::Ctx;
use super::env;
use std::mem;
use std::slice;
// Linking to functions that are not provided by rust libc
#[cfg(target_os = "macos")]

View File

@ -1,16 +1,18 @@
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::time::SystemTime;
#[cfg(not(target_os = "windows"))]
use libc::{clockid_t, time as libc_time};
#[cfg(target_os = "windows")]
use libc::time_t;
#[cfg(target_os = "windows")]
type clockid_t = c_int;
#[cfg(target_os = "windows")]
#[link(name = "c")]
extern "C" {
#[link_name = "time"]
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 libc::stat;
use std::ffi::CStr;
use std::ffi::CString;
use std::mem::size_of;
use std::os::raw::c_char;
use std::os::raw::c_int;
use std::slice;
use wasmer_runtime_core::memory::Memory;
use wasmer_runtime_core::{

View File

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

View File

@ -83,6 +83,12 @@ pub trait ProtectedCaller: Send + Sync {
vmctx: *mut vm::Ctx,
_: Token,
) -> 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 {

View File

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

View File

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

View File

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

View File

@ -18,6 +18,32 @@ pub struct 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> {
if size == 0 {
return Ok(Self {

View File

@ -1,11 +1,16 @@
use crate::{
backend::UserTrapper,
error::RuntimeError,
export::{Context, Export, FuncPointer},
import::IsExport,
types::{FuncSig, Type, WasmExternType},
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 struct Safe;
@ -28,9 +33,44 @@ where
Args: 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> {
f: *const (),
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)]
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`.");
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 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 {
use super::Func;
use crate::backend::{Backend, FuncResolver, ProtectedCaller, Token};
use crate::backend::{Backend, FuncResolver, ProtectedCaller, Token, UserTrapper};
use crate::error::RuntimeResult;
use crate::types::{FuncIndex, LocalFuncIndex, Value};
use hashbrown::HashMap;
@ -520,6 +520,9 @@ mod vm_ctx_tests {
) -> RuntimeResult<Vec<Value>> {
Ok(vec![])
}
fn get_early_trapper(&self) -> Box<dyn UserTrapper> {
unimplemented!()
}
}
ModuleInner {

View File

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