On Windows 7, my WASM module has different behavior depending on the compilation mode. In debug, it runs successfully while on release it "segfaults". (On Ubuntu 16.04, it finishes successfully in both modes).
To get code:
git clone https://github.com/slipher/wasmer-bug
cd wasmer-bug
git checkout another_bug
Debug mode output:
cargo build && target\debug\sandbox cgame.wasm ->
instantiated
print_str
static inited
Release mode output:
cargo build --release && target\release\sandbox cgame.wasm ->
instantiated
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: RuntimeError: WebAssembly trap occurred during runtime: memory out-of-bounds access', src\libcore\result.rs:1165:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
I just checked that the bug also happens on Windows 10. So no one can make the excuse that Windows 7 is deprecated :stuck_out_tongue:
Hahah, thanks for checking! We've been a bit busy with other things, but we just got a remote Windows system set up that I should be able to reproduce this with today!
Okay so I spent some time finding where the trap is happening on Windows -- it looks like it's happening when zeroing out memory before starting execution... I haven't been actively working on it since I made that discovery but it's still on my short-list of things to fix. All the ideas I tested didn't appear to be the source of the issue.
Rust does print warnings about non-FFI safe types when compiling Wasmer on Windows, but those seem to be false positives for our case (the warning is triggered because if you follow pointers far enough there are types that are not FFI safe, but the trap-handling bit of C++ code we use on Windows does not do that, so that shouldn't be the issue)
Could possibly somehow be related to https://github.com/wasmerio/wasmer/issues/1179 (caused by https://github.com/bytecodealliance/cranelift/issues/1366 ). That one caused some extremely weird issues for us until we applied a terrifying workaround. Zeroing memory seems like something that could involve SIMD registers.
I've just manually tested, adapting the code from the repo to the Wasmer 1.0 API:
extern crate wasmer;
use wasmer::{Store, Instance, Module, Function, imports};
fn emscripten_saveSetjmp(_env: i32, _label: i32, _table: i32, _size: i32)-> i32 {
panic!("setjmp called");
}
fn emscripten_testSetjmp(_id: i32, _table: i32, _size: i32)-> i32 {
panic!("testSetjmp called");
}
fn emscripten_roundf(x: f32) -> f32 {
panic!("roundf called");
}
fn emscripten_invoke_vi(_0: i32, _1: i32) {
panic!("wtf");
}
fn emscripten_invoke_iii(_0: i32, _1: i32, _2: i32) -> i32 {
panic!("what is this shit");
}
fn emscripten_setTempRet0(_: i32) {
panic!("setTempRet0");
}
fn emscripten_getTempRet0() -> i32 {
panic!("getTempRet0");
}
fn env_time(_: i32) -> i32 {
panic!("called env_time");
}
fn env_ctime(_:i32)->i32 {
panic!("called env_ctime");
}
fn env__exit(_:i32) {
panic!("called env__exit");
}
fn env_localtime(_:i32)->i32 {
panic!("called env_localtime");
}
fn env_emscripten_longjmp(_:i32,_:i32) {
panic!("called env_emscripten_longjmp");
}
fn env_invoke_viiii(_:i32,_:i32,_:i32,_:i32,_:i32) {
panic!("called env_invoke_viiii");
}
fn env_invoke_iiiii(_:i32,_:i32,_:i32,_:i32,_:i32) -> i32 {
panic!("called env_invoke_iiiii");
}
fn env_invoke_iiii(_:i32,_:i32,_:i32,_:i32)->i32 {
panic!("called env_invoke_iiii");
}
fn wsp1_fd_close(_:i32)->i32 {
panic!("called wsp1_fd_close");
}
fn wsp1_proc_exit(_:i32) {
panic!("called wsp1_proc_exit");
}
fn env_syscall221(_:i32,_:i32)->i32 {
panic!("called env_syscall221");
}
fn env_syscall54(_:i32,_:i32)->i32 {
panic!("called env_syscall54");
}
fn env_syscall5(_:i32,_:i32)->i32 {
panic!("called env_syscall5");
}
fn wsp1_fd_write(_:i32,_:i32,_:i32,_:i32)->i32 {
panic!("called wsp1_fd_write");
}
fn env_clock_gettime(_:i32,_:i32)->i32 {
panic!("called env_clock_gettime");
}
fn print_str(ptr: u32, len: u32) {
eprintln!("print_str");
}
fn handle_sync_message(data: u32, size: u32, replybuf: u32, bufsize: u32) -> u32 {
panic!("handle_sync_message");
}
fn w_fd_read(_0: i32, _1: i32, _2: i32, _3: i32) -> i32 {
panic!("fd_read");
}
fn w_fd_seek(_0: i32, _1: i64, _2: i32, _3: i32) -> i32 {
panic!("fd_seek");
}
fn main() {
let args: Vec<String> = std::env::args().collect();
if args.len() != 2 {
panic!("bad args");
}
let binary: Vec<u8> = std::fs::read(&args[1]).unwrap();
let store = Store::default();
let mut import_object = imports! {
"env" => {
"saveSetjmp" => Function::new_native(&store, emscripten_saveSetjmp),
"testSetjmp" => Function::new_native(&store, emscripten_testSetjmp),
"roundf" => Function::new_native(&store, emscripten_roundf),
"invoke_vi" => Function::new_native(&store, emscripten_invoke_vi),
"invoke_iii" => Function::new_native(&store, emscripten_invoke_iii),
"setTempRet0" => Function::new_native(&store, emscripten_setTempRet0),
"getTempRet0" => Function::new_native(&store, emscripten_getTempRet0),
"time" => Function::new_native(&store, env_time),
"ctime" => Function::new_native(&store, env_ctime),
"_exit" => Function::new_native(&store, env__exit),
"localtime" => Function::new_native(&store, env_localtime),
"emscripten_longjmp" => Function::new_native(&store, env_emscripten_longjmp),
"invoke_viiii" => Function::new_native(&store, env_invoke_viiii),
"invoke_iiiii" => Function::new_native(&store, env_invoke_iiiii),
"invoke_iiii" => Function::new_native(&store, env_invoke_iiii),
"__syscall5" => Function::new_native(&store, env_syscall5),
"__syscall221" => Function::new_native(&store, env_syscall221),
"__syscall54" => Function::new_native(&store, env_syscall54),
"clock_gettime" => Function::new_native(&store, env_clock_gettime),
"WasmLog" => Function::new_native(&store, print_str),
"WasmSendMsg" => Function::new_native(&store, handle_sync_message),
},
"wasi_snapshot_preview1" => {
"fd_read" => Function::new_native(&store, w_fd_read),
"fd_seek" => Function::new_native(&store, w_fd_seek),
"fd_write" => Function::new_native(&store, wsp1_fd_write),
"fd_close" => Function::new_native(&store, wsp1_fd_close),
"proc_exit" => Function::new_native(&store, wsp1_proc_exit),
},
};
let module = Module::new(&store, &binary).unwrap();
let instance = Instance::new(&module, &import_object).unwrap();
eprintln!("instantiated");
instance.exports.get_function("_start").unwrap().call(&[]).unwrap();
eprintln!("static inited");
}
And it works properly in both debug and release mode.
$ cargo run cgame.wasm
instantiated
print_str
static inited
$ cargo run --release cgame.wasm
instantiated
print_str
static inited
Closing the issue
Most helpful comment
Could possibly somehow be related to https://github.com/wasmerio/wasmer/issues/1179 (caused by https://github.com/bytecodealliance/cranelift/issues/1366 ). That one caused some extremely weird issues for us until we applied a terrifying workaround. Zeroing memory seems like something that could involve SIMD registers.