diff --git a/examples/lua.wasm b/examples/lua.wasm new file mode 100644 index 00000000000..1a424ae532e Binary files /dev/null and b/examples/lua.wasm differ diff --git a/src/apis/emscripten/env.rs b/src/apis/emscripten/env.rs index a79de32554a..fd8ea10b581 100644 --- a/src/apis/emscripten/env.rs +++ b/src/apis/emscripten/env.rs @@ -1,6 +1,6 @@ use super::super::host; /// NOTE: These syscalls only support wasm_32 for now because they take u32 offset -use libc::{c_int, c_long, getgrnam as libc_getgrnam, getpwnam as libc_getpwnam, sysconf}; +use libc::{c_int, c_long, getenv, getgrnam as libc_getgrnam, getpwnam as libc_getpwnam, sysconf}; use std::ffi::CStr; use std::mem; use std::os::raw::c_char; @@ -8,19 +8,21 @@ use std::os::raw::c_char; use super::utils::{copy_cstr_into_wasm, copy_terminated_array_of_cstrs}; use crate::webassembly::Instance; -/// emscripten: _getenv -pub extern "C" fn _getenv(name_ptr: c_int, instance: &mut Instance) -> c_int { - debug!("emscripten::_getenv {}", name_ptr); - let name = unsafe { - let memory_name_ptr = instance.memory_offset_addr(0, name_ptr as usize) as *const c_char; - CStr::from_ptr(memory_name_ptr).to_str().unwrap() - }; - match host::get_env(name, instance) { - Ok(_) => { - unimplemented!(); - } - Err(_) => 0, +// #[no_mangle] +/// emscripten: _getenv // (name: *const char) -> *const c_char; +pub extern "C" fn _getenv(name: c_int, instance: &mut Instance) -> u32 { + debug!("emscripten::_getenv"); + + let name_addr = instance.memory_offset_addr(0, name as usize) as *const c_char; + + debug!("=> name({:?})", unsafe { CStr::from_ptr(name_addr) }); + + let c_str = unsafe { getenv(name_addr) }; + if c_str.is_null() { + return 0; } + + unsafe { copy_cstr_into_wasm(instance, c_str) } } pub extern "C" fn _getpwnam(name_ptr: c_int, instance: &mut Instance) -> c_int { diff --git a/src/apis/emscripten/exception.rs b/src/apis/emscripten/exception.rs new file mode 100644 index 00000000000..8f8c2b96944 --- /dev/null +++ b/src/apis/emscripten/exception.rs @@ -0,0 +1,15 @@ +use super::process::_abort; +use crate::webassembly::Instance; + +/// emscripten: ___cxa_allocate_exception +pub extern "C" fn ___cxa_allocate_exception(size: u32, instance: &mut Instance) -> u32 { + debug!("emscripten::___cxa_allocate_exception"); + (instance.emscripten_data.as_ref().unwrap().malloc)(size as _, instance) +} + +/// emscripten: ___cxa_throw +/// TODO: We don't have support for exceptions yet +pub extern "C" fn ___cxa_throw(ptr: u32, ty: u32, destructor: u32, instance: &mut Instance) { + debug!("emscripten::___cxa_throw"); + _abort(); +} diff --git a/src/apis/emscripten/jmp.rs b/src/apis/emscripten/jmp.rs new file mode 100644 index 00000000000..ead60cf56de --- /dev/null +++ b/src/apis/emscripten/jmp.rs @@ -0,0 +1,41 @@ +use crate::webassembly::Instance; +use libc::{c_int, c_void}; +use std::cell::UnsafeCell; + +/// setjmp +pub extern "C" fn __setjmp(env_addr: u32, instance: &mut Instance) -> c_int { + debug!("emscripten::__setjmp (setjmp)"); + unsafe { + // 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 = instance.memory_offset_addr(0, 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 mut jumps = &mut instance.emscripten_data.as_mut().unwrap().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 + } +} + +/// longjmp +pub extern "C" fn __longjmp(env_addr: u32, val: c_int, instance: &mut Instance) -> ! { + debug!("emscripten::__longjmp (longjmp) {}", val); + unsafe { + // We retrieve the jump index from the env address + let jump_index = instance.memory_offset_addr(0, env_addr as usize) as *mut i8; + let mut jumps = &mut instance.emscripten_data.as_mut().unwrap().jumps; + // We get the real jump buffer from the jumps vector, using the retrieved index + let mut jump_buf = &jumps[*jump_index as usize]; + longjmp(jump_buf.get() as _, val) + }; +} + +extern "C" { + fn setjmp(env: *mut c_void) -> c_int; + fn longjmp(env: *mut c_void, val: c_int) -> !; +} diff --git a/src/apis/emscripten/linking.rs b/src/apis/emscripten/linking.rs new file mode 100644 index 00000000000..32f85ee7a03 --- /dev/null +++ b/src/apis/emscripten/linking.rs @@ -0,0 +1,27 @@ +use crate::webassembly::Instance; + +// TODO: Need to implement. + +/// emscripten: dlopen(filename: *const c_char, flag: c_int) -> *mut c_void +pub extern "C" fn _dlopen(filename: u32, flag: c_int) -> u32 { + debug!("emscripten::_dlopen"); + -1 +} + +/// emscripten: dlclose(handle: *mut c_void) -> c_int +pub extern "C" fn _dlclose(filename: u32) -> u32 { + debug!("emscripten::_dlclose"); + -1 +} + +/// emscripten: dlsym(handle: *mut c_void, symbol: *const c_char) -> *mut c_void +pub extern "C" fn _dlsym(filepath: u32, symbol: u32) -> u32 { + debug!("emscripten::_dlerror"); + -1 +} + +/// emscripten: dlerror() -> *mut c_char +pub extern "C" fn _dlerror() -> u32 { + debug!("emscripten::_dlerror"); + -1 +} diff --git a/src/apis/emscripten/lock.rs b/src/apis/emscripten/lock.rs index ea875fcca27..3485f3b9e3a 100644 --- a/src/apis/emscripten/lock.rs +++ b/src/apis/emscripten/lock.rs @@ -10,3 +10,8 @@ pub extern "C" fn ___lock(which: c_int, varargs: c_int, _instance: &mut Instance pub extern "C" fn ___unlock(which: c_int, varargs: c_int, _instance: &mut Instance) { debug!("emscripten::___unlock {}, {}", which, varargs); } + +// NOTE: Not implemented by Emscripten +pub extern "C" fn ___wait(_which: c_int, _varargs: c_int, _instance: &mut Instance) { + debug!("emscripten::___wait"); +} diff --git a/src/apis/emscripten/math.rs b/src/apis/emscripten/math.rs new file mode 100644 index 00000000000..9f33e5366be --- /dev/null +++ b/src/apis/emscripten/math.rs @@ -0,0 +1,19 @@ +use crate::webassembly::Instance; + +/// emscripten: _llvm_log10_f64 +pub extern "C" fn _llvm_log10_f64(value: f64) -> f64 { + debug!("emscripten::_llvm_log10_f64"); + value.log10() +} + +/// emscripten: _llvm_log2_f64 +pub extern "C" fn _llvm_log2_f64(value: f64) -> f64 { + debug!("emscripten::_llvm_log2_f64"); + value.log2() +} + +// emscripten: f64-rem +pub extern "C" fn f64_rem(x: f64, y: f64) -> f64 { + debug!("emscripten::f64-rem"); + x % y +} diff --git a/src/apis/emscripten/memory.rs b/src/apis/emscripten/memory.rs index 73c4fd510d3..56b2e06f57d 100644 --- a/src/apis/emscripten/memory.rs +++ b/src/apis/emscripten/memory.rs @@ -1,6 +1,6 @@ use super::process::abort_with_message; use crate::webassembly::Instance; -use libc::{c_void, memcpy, size_t}; +use libc::{c_int, c_void, memcpy, size_t}; /// emscripten: _emscripten_memcpy_big pub extern "C" fn _emscripten_memcpy_big( @@ -39,3 +39,10 @@ pub extern "C" fn abort_on_cannot_grow_memory() { debug!("emscripten::abort_on_cannot_grow_memory"); abort_with_message("Cannot enlarge memory arrays!"); } + +/// emscripten: ___map_file +pub extern "C" fn ___map_file() -> c_int { + debug!("emscripten::___map_file"); + // NOTE: TODO: Em returns -1 here as well. May need to implement properly + -1 +} diff --git a/src/apis/emscripten/mod.rs b/src/apis/emscripten/mod.rs index f25b3d71808..413171e6392 100644 --- a/src/apis/emscripten/mod.rs +++ b/src/apis/emscripten/mod.rs @@ -6,8 +6,11 @@ use std::mem; // EMSCRIPTEN APIS mod env; mod errno; +mod exception; mod io; +mod jmp; mod lock; +mod math; mod memory; mod nullfunc; mod process; @@ -108,6 +111,7 @@ pub fn generate_emscripten_env<'a, 'b>() -> ImportObject<&'a str, &'b str> { // Lock import_object.set("env", "___lock", ImportValue::Func(lock::___lock as _)); import_object.set("env", "___unlock", ImportValue::Func(lock::___unlock as _)); + import_object.set("env", "___wait", ImportValue::Func(lock::___wait as _)); // Env import_object.set("env", "_getenv", ImportValue::Func(env::_getenv as _)); import_object.set("env", "_getpwnam", ImportValue::Func(env::_getpwnam as _)); @@ -151,39 +155,54 @@ pub fn generate_emscripten_env<'a, 'b>() -> ImportObject<&'a str, &'b str> { ); import_object.set( "env", - "___syscall54", - ImportValue::Func(syscalls::___syscall54 as _), + "___syscall12", + ImportValue::Func(syscalls::___syscall12 as _), ); import_object.set( "env", - "___syscall140", - ImportValue::Func(syscalls::___syscall140 as _), + "___syscall20", + ImportValue::Func(syscalls::___syscall20 as _), ); import_object.set( "env", - "___syscall145", - ImportValue::Func(syscalls::___syscall145 as _), + "___syscall39", + ImportValue::Func(syscalls::___syscall39 as _), ); import_object.set( "env", - "___syscall146", - ImportValue::Func(syscalls::___syscall146 as _), + "___syscall40", + ImportValue::Func(syscalls::___syscall40 as _), ); import_object.set( "env", - "___syscall221", - ImportValue::Func(syscalls::___syscall221 as _), + "___syscall54", + ImportValue::Func(syscalls::___syscall54 as _), ); import_object.set( "env", - "___syscall20", - ImportValue::Func(syscalls::___syscall20 as _), + "___syscall57", + ImportValue::Func(syscalls::___syscall57 as _), + ); + import_object.set( + "env", + "___syscall63", + ImportValue::Func(syscalls::___syscall63 as _), ); import_object.set( "env", "___syscall64", ImportValue::Func(syscalls::___syscall64 as _), ); + import_object.set( + "env", + "___syscall102", + ImportValue::Func(syscalls::___syscall102 as _), + ); + import_object.set( + "env", + "___syscall114", + ImportValue::Func(syscalls::___syscall114 as _), + ); import_object.set( "env", "___syscall122", @@ -191,23 +210,23 @@ pub fn generate_emscripten_env<'a, 'b>() -> ImportObject<&'a str, &'b str> { ); import_object.set( "env", - "___syscall201", - ImportValue::Func(syscalls::___syscall201 as _), + "___syscall140", + ImportValue::Func(syscalls::___syscall140 as _), ); import_object.set( "env", - "___syscall202", - ImportValue::Func(syscalls::___syscall202 as _), + "___syscall142", + ImportValue::Func(syscalls::___syscall142 as _), ); import_object.set( "env", - "___syscall340", - ImportValue::Func(syscalls::___syscall340 as _), + "___syscall145", + ImportValue::Func(syscalls::___syscall145 as _), ); import_object.set( "env", - "___syscall197", - ImportValue::Func(syscalls::___syscall197 as _), + "___syscall146", + ImportValue::Func(syscalls::___syscall146 as _), ); import_object.set( "env", @@ -221,8 +240,8 @@ pub fn generate_emscripten_env<'a, 'b>() -> ImportObject<&'a str, &'b str> { ); import_object.set( "env", - "___syscall39", - ImportValue::Func(syscalls::___syscall39 as _), + "___syscall192", + ImportValue::Func(syscalls::___syscall192 as _), ); import_object.set( "env", @@ -231,50 +250,39 @@ pub fn generate_emscripten_env<'a, 'b>() -> ImportObject<&'a str, &'b str> { ); import_object.set( "env", - "___syscall212", - ImportValue::Func(syscalls::___syscall212 as _), - ); - import_object.set( - "env", - "___syscall221", - ImportValue::Func(syscalls::___syscall221 as _), - ); - import_object.set( - "env", - "___syscall102", - ImportValue::Func(syscalls::___syscall102 as _), + "___syscall197", + ImportValue::Func(syscalls::___syscall197 as _), ); import_object.set( "env", - "___syscall54", - ImportValue::Func(syscalls::___syscall54 as _), + "___syscall201", + ImportValue::Func(syscalls::___syscall201 as _), ); import_object.set( "env", - "___syscall12", - ImportValue::Func(syscalls::___syscall12 as _), + "___syscall202", + ImportValue::Func(syscalls::___syscall202 as _), ); import_object.set( "env", - "___syscall192", - ImportValue::Func(syscalls::___syscall192 as _), + "___syscall212", + ImportValue::Func(syscalls::___syscall212 as _), ); import_object.set( "env", - "___syscall63", - ImportValue::Func(syscalls::___syscall63 as _), + "___syscall221", + ImportValue::Func(syscalls::___syscall221 as _), ); import_object.set( "env", - "___syscall142", - ImportValue::Func(syscalls::___syscall142 as _), + "___syscall330", + ImportValue::Func(syscalls::___syscall330 as _), ); import_object.set( "env", - "___syscall57", - ImportValue::Func(syscalls::___syscall57 as _), + "___syscall340", + ImportValue::Func(syscalls::___syscall340 as _), ); - // Process import_object.set("env", "abort", ImportValue::Func(process::em_abort as _)); import_object.set("env", "_abort", ImportValue::Func(process::_abort as _)); @@ -283,9 +291,15 @@ pub fn generate_emscripten_env<'a, 'b>() -> ImportObject<&'a str, &'b str> { "abortStackOverflow", ImportValue::Func(process::abort_stack_overflow as _), ); + import_object.set( + "env", + "_llvm_trap", + ImportValue::Func(process::_llvm_trap as _), + ); import_object.set("env", "_fork", ImportValue::Func(process::_fork as _)); import_object.set("env", "_exit", ImportValue::Func(process::_exit as _)); - + import_object.set("env", "_system", ImportValue::Func(process::_system as _)); + import_object.set("env", "_popen", ImportValue::Func(process::_popen as _)); // Signal import_object.set( "env", @@ -329,6 +343,27 @@ pub fn generate_emscripten_env<'a, 'b>() -> ImportObject<&'a str, &'b str> { "getTotalMemory", ImportValue::Func(memory::get_total_memory as _), ); + import_object.set( + "env", + "___map_file", + ImportValue::Func(memory::___map_file as _), + ); + // Exception + import_object.set( + "env", + "___cxa_allocate_exception", + ImportValue::Func(exception::___cxa_allocate_exception as _), + ); + import_object.set( + "env", + "___cxa_allocate_exception", + ImportValue::Func(exception::___cxa_throw as _), + ); + import_object.set( + "env", + "___cxa_throw", + ImportValue::Func(exception::___cxa_throw as _), + ); // NullFuncs import_object.set( "env", @@ -380,6 +415,16 @@ pub fn generate_emscripten_env<'a, 'b>() -> ImportObject<&'a str, &'b str> { "nullFunc_viiii", ImportValue::Func(nullfunc::nullfunc_viiii as _), ); + import_object.set( + "env", + "nullFunc_viiiii", + ImportValue::Func(nullfunc::nullfunc_viiiii as _), + ); + import_object.set( + "env", + "nullFunc_viiiiii", + ImportValue::Func(nullfunc::nullfunc_viiiiii as _), + ); // Time import_object.set( "env", @@ -391,6 +436,13 @@ pub fn generate_emscripten_env<'a, 'b>() -> ImportObject<&'a str, &'b str> { "_clock_gettime", ImportValue::Func(time::_clock_gettime as _), ); + import_object.set( + "env", + "___clock_gettime", + ImportValue::Func(time::___clock_gettime as _), + ); + import_object.set("env", "_clock", ImportValue::Func(time::_clock as _)); + import_object.set("env", "_difftime", ImportValue::Func(time::_difftime as _)); import_object.set("env", "_asctime", ImportValue::Func(time::_asctime as _)); import_object.set( "env", @@ -415,6 +467,21 @@ pub fn generate_emscripten_env<'a, 'b>() -> ImportObject<&'a str, &'b str> { ImportValue::Func(env::_getpagesize as _), ); import_object.set("env", "_sysconf", ImportValue::Func(env::_sysconf as _)); + // Math + import_object.set( + "env", + "_llvm_log10_f64", + ImportValue::Func(math::_llvm_log10_f64 as _), + ); + import_object.set( + "env", + "_llvm_log2_f64", + ImportValue::Func(math::_llvm_log2_f64 as _), + ); + import_object.set("asm2wasm", "f64-rem", ImportValue::Func(math::f64_rem as _)); + + import_object.set("env", "__setjmp", ImportValue::Func(jmp::__setjmp as _)); + import_object.set("env", "__longjmp", ImportValue::Func(jmp::__longjmp as _)); mock_external!(import_object, _waitpid); mock_external!(import_object, _utimes); @@ -458,7 +525,7 @@ pub fn generate_emscripten_env<'a, 'b>() -> ImportObject<&'a str, &'b str> { mock_external!(import_object, ___syscall66); // mock_external!(import_object, ___syscall64); // mock_external!(import_object, ___syscall63); - mock_external!(import_object, ___syscall60); + // mock_external!(import_object, ___syscall60); // mock_external!(import_object, ___syscall54); // mock_external!(import_object, ___syscall39); mock_external!(import_object, ___syscall38); @@ -490,6 +557,10 @@ pub fn generate_emscripten_env<'a, 'b>() -> ImportObject<&'a str, &'b str> { // mock_external!(import_object, ___syscall20); mock_external!(import_object, ___syscall15); mock_external!(import_object, ___syscall10); + mock_external!(import_object, _dlopen); + mock_external!(import_object, _dlclose); + mock_external!(import_object, _dlsym); + mock_external!(import_object, _dlerror); import_object } diff --git a/src/apis/emscripten/nullfunc.rs b/src/apis/emscripten/nullfunc.rs index 6d89d0ab6cc..00f43560bbe 100644 --- a/src/apis/emscripten/nullfunc.rs +++ b/src/apis/emscripten/nullfunc.rs @@ -50,3 +50,13 @@ pub extern "C" fn nullfunc_viiii(x: u32, _instance: &Instance) { debug!("emscripten::nullfunc_viiii {}", x); abort_with_message("Invalid function pointer called with signature 'viiii'. Perhaps this is an invalid value (e.g. caused by calling a virtual method on a NULL pointer)? Or calling a function with an incorrect type, which will fail? (it is worth building your source files with -Werror (warnings are errors), as warnings can indicate undefined behavior which can cause this)"); } + +pub extern "C" fn nullfunc_viiiii(_x: u32, _instance: &Instance) { + debug!("emscripten::nullfunc_viiiii"); + abort_with_message("Invalid function pointer called with signature 'viiiii'. Perhaps this is an invalid value (e.g. caused by calling a virtual method on a NULL pointer)? Or calling a function with an incorrect type, which will fail? (it is worth building your source files with -Werror (warnings are errors), as warnings can indicate undefined behavior which can cause this)"); +} + +pub extern "C" fn nullfunc_viiiiii(_x: u32, _instance: &Instance) { + debug!("emscripten::nullfunc_viiiiii"); + abort_with_message("Invalid function pointer called with signature 'viiiiii'. Perhaps this is an invalid value (e.g. caused by calling a virtual method on a NULL pointer)? Or calling a function with an incorrect type, which will fail? (it is worth building your source files with -Werror (warnings are errors), as warnings can indicate undefined behavior which can cause this)"); +} diff --git a/src/apis/emscripten/process.rs b/src/apis/emscripten/process.rs index 55b8e7b53f3..f3acb32fe18 100644 --- a/src/apis/emscripten/process.rs +++ b/src/apis/emscripten/process.rs @@ -1,4 +1,4 @@ -use libc::{abort, c_char, c_int, exit, pid_t}; +use libc::{abort, c_char, c_int, exit, pid_t, EAGAIN}; use crate::webassembly::Instance; use std::ffi::CStr; @@ -46,3 +46,24 @@ pub extern "C" fn abort_stack_overflow() { // TODO: Message incomplete. Need to finish em runtime data first abort_with_message("Stack overflow! Attempted to allocate some bytes on the stack"); } + +pub extern "C" fn _llvm_trap() { + debug!("emscripten::_llvm_trap"); + abort_with_message("abort!"); +} + +pub extern "C" fn _system() -> c_int { + debug!("emscripten::_system"); + // TODO: May need to change this Em impl to a working version + eprintln!("Can't call external programs"); + return EAGAIN; +} + +pub extern "C" fn _popen() -> c_int { + debug!("emscripten::_popen"); + // TODO: May need to change this Em impl to a working version + eprintln!("Missing function: popen"); + unsafe { + abort(); + } +} diff --git a/src/apis/emscripten/syscalls.rs b/src/apis/emscripten/syscalls.rs index 15f2fee196b..150ff625bbc 100644 --- a/src/apis/emscripten/syscalls.rs +++ b/src/apis/emscripten/syscalls.rs @@ -18,6 +18,7 @@ use libc::{ connect, dup2, exit, + fcntl, fstat, getgid, getpeername, @@ -42,6 +43,9 @@ use libc::{ // readv, recvfrom, recvmsg, + rmdir, + // ENOTTY, + rusage, sa_family_t, // writev, select, @@ -57,16 +61,30 @@ use libc::{ uname, utsname, write, + EINVAL, // sockaddr_in, FIOCLEX, FIONBIO, + F_GETFD, + F_SETFD, SOL_SOCKET, TIOCGWINSZ, }; + use std::mem; use std::slice; // use std::sys::fd::FileDesc; +// Linking to functions that are not provided by rust libc +#[cfg(target_os = "macos")] +#[link(name = "c")] +extern "C" { + pub fn wait4(pid: pid_t, status: *mut c_int, options: c_int, rusage: *mut rusage) -> pid_t; +} + +#[cfg(not(target_os = "macos"))] +use libc::wait4; + // Another conditional constant for name resolution: Macos et iOS use // SO_NOSIGPIPE as a setsockopt flag to disable SIGPIPE emission on socket. // Other platforms do otherwise. @@ -181,15 +199,19 @@ pub extern "C" fn ___syscall39( let pathname: u32 = varargs.get(instance); let mode: u32 = varargs.get(instance); let pathname_addr = instance.memory_offset_addr(0, pathname as usize) as *const i8; - - unsafe { mkdir(pathname_addr, mode as _) }; - 0 + unsafe { mkdir(pathname_addr, mode as _) } } -// getppid -pub extern "C" fn ___syscall64() -> pid_t { - debug!("emscripten::___syscall64 (getppid)"); - unsafe { getpid() } +// rmdir +pub extern "C" fn ___syscall40( + _which: c_int, + mut varargs: VarArgs, + instance: &mut Instance, +) -> c_int { + debug!("emscripten::___syscall40 (rmdir)"); + let pathname: u32 = varargs.get(instance); + let pathname_addr = instance.memory_offset_addr(0, pathname as usize) as *const i8; + unsafe { rmdir(pathname_addr) } } /// ioctl @@ -238,6 +260,38 @@ pub extern "C" fn ___syscall54( } } +// setpgid +pub extern "C" fn ___syscall57( + which: c_int, + mut varargs: VarArgs, + instance: &mut Instance, +) -> c_int { + debug!("emscripten::___syscall57 (setpgid) {}", which); + let pid: i32 = varargs.get(instance); + let pgid: i32 = varargs.get(instance); + unsafe { setpgid(pid, pgid) } +} + +// dup2 +pub extern "C" fn ___syscall63( + which: c_int, + mut varargs: VarArgs, + instance: &mut Instance, +) -> c_int { + debug!("emscripten::___syscall63 (dup2) {}", which); + + let src: i32 = varargs.get(instance); + let dst: i32 = varargs.get(instance); + + unsafe { dup2(src, dst) } +} + +// getppid +pub extern "C" fn ___syscall64() -> pid_t { + debug!("emscripten::___syscall64 (getppid)"); + unsafe { getpid() } +} + // socketcall pub extern "C" fn ___syscall102( which: c_int, @@ -515,6 +569,27 @@ pub extern "C" fn ___syscall102( } } +/// wait4 +pub extern "C" fn ___syscall114( + _which: c_int, + mut varargs: VarArgs, + instance: &mut Instance, +) -> pid_t { + debug!("emscripten::___syscall114 (wait4)"); + let pid: pid_t = varargs.get(instance); + let status: u32 = varargs.get(instance); + let options: c_int = varargs.get(instance); + let rusage: u32 = varargs.get(instance); + let status_addr = instance.memory_offset_addr(0, status as usize) as *mut c_int; + let rusage_addr = instance.memory_offset_addr(0, rusage as usize) as *mut rusage; + let res = unsafe { wait4(pid, status_addr, options, rusage_addr) }; + debug!( + "=> pid: {}, status: {:?}, options: {}, rusage: {:?} = pid: {}", + pid, status_addr, options, rusage_addr, res + ); + res +} + /// uname // NOTE: Wondering if we should return custom utsname, like Emscripten. pub extern "C" fn ___syscall122( @@ -529,6 +604,29 @@ pub extern "C" fn ___syscall122( unsafe { uname(buf_addr) } } +// select +pub extern "C" fn ___syscall142( + which: c_int, + mut varargs: VarArgs, + instance: &mut Instance, +) -> c_int { + debug!("emscripten::___syscall142 (newselect) {}", which); + + let nfds: i32 = varargs.get(instance); + let readfds: u32 = varargs.get(instance); + let writefds: u32 = varargs.get(instance); + let exceptfds: u32 = varargs.get(instance); + let _timeout: i32 = varargs.get(instance); + + assert!(nfds <= 64, "`nfds` must be less than or equal to 64"); + assert!(exceptfds == 0, "`exceptfds` is not supporrted"); + + let readfds_ptr = instance.memory_offset_addr(0, readfds as _) as _; + let writefds_ptr = instance.memory_offset_addr(0, writefds as _) as _; + + unsafe { select(nfds, readfds_ptr, writefds_ptr, 0 as _, 0 as _) } +} + // mmap2 pub extern "C" fn ___syscall192( which: c_int, @@ -814,6 +912,44 @@ pub extern "C" fn ___syscall221( } } +/// dup3 +pub extern "C" fn ___syscall330( + _which: c_int, + mut varargs: VarArgs, + instance: &mut Instance, +) -> pid_t { + // Implementation based on description at https://linux.die.net/man/2/dup3 + debug!("emscripten::___syscall330 (dup3)"); + let oldfd: c_int = varargs.get(instance); + let newfd: c_int = varargs.get(instance); + let flags: c_int = varargs.get(instance); + + if oldfd == newfd { + return EINVAL; + } + + let res = unsafe { dup2(oldfd, newfd) }; + + // Set flags on newfd (https://www.gnu.org/software/libc/manual/html_node/Descriptor-Flags.html) + let mut old_flags = unsafe { fcntl(newfd, F_GETFD, 0) }; + + if old_flags > 0 { + old_flags |= flags; + } else if old_flags == 0 { + old_flags &= !flags; + } + + unsafe { + fcntl(newfd, F_SETFD, old_flags); + } + + debug!( + "=> oldfd: {}, newfd: {}, flags: {} = pid: {}", + oldfd, newfd, flags, res + ); + res +} + // prlimit64 pub extern "C" fn ___syscall340( which: c_int, @@ -840,52 +976,3 @@ pub extern "C" fn ___syscall340( 0 } - -// dup2 -pub extern "C" fn ___syscall63( - which: c_int, - mut varargs: VarArgs, - instance: &mut Instance, -) -> c_int { - debug!("emscripten::___syscall63 (dup2) {}", which); - - let src: i32 = varargs.get(instance); - let dst: i32 = varargs.get(instance); - - unsafe { dup2(src, dst) } -} - -// select -pub extern "C" fn ___syscall142( - which: c_int, - mut varargs: VarArgs, - instance: &mut Instance, -) -> c_int { - debug!("emscripten::___syscall142 (newselect) {}", which); - - let nfds: i32 = varargs.get(instance); - let readfds: u32 = varargs.get(instance); - let writefds: u32 = varargs.get(instance); - let exceptfds: u32 = varargs.get(instance); - let _timeout: i32 = varargs.get(instance); - - assert!(nfds <= 64, "`nfds` must be less than or equal to 64"); - assert!(exceptfds == 0, "`exceptfds` is not supporrted"); - - let readfds_ptr = instance.memory_offset_addr(0, readfds as _) as _; - let writefds_ptr = instance.memory_offset_addr(0, writefds as _) as _; - - unsafe { select(nfds, readfds_ptr, writefds_ptr, 0 as _, 0 as _) } -} - -// setpgid -pub extern "C" fn ___syscall57( - which: c_int, - mut varargs: VarArgs, - instance: &mut Instance, -) -> c_int { - debug!("emscripten::___syscall57 (setpgid) {}", which); - let pid: i32 = varargs.get(instance); - let pgid: i32 = varargs.get(instance); - unsafe { setpgid(pid, pgid) } -} diff --git a/src/apis/emscripten/time.rs b/src/apis/emscripten/time.rs index ce453941136..40c6ba11add 100644 --- a/src/apis/emscripten/time.rs +++ b/src/apis/emscripten/time.rs @@ -58,6 +58,24 @@ pub extern "C" fn _clock_gettime(clk_id: c_int, tp: c_int, instance: &mut Instan 0 } +/// emscripten: ___clock_gettime +pub extern "C" fn ___clock_gettime(clk_id: c_int, tp: c_int, instance: &mut Instance) -> c_int { + debug!("emscripten::___clock_gettime {} {}", clk_id, tp); + _clock_gettime(clk_id, tp, instance) +} + +/// emscripten: _clock +pub extern "C" fn _clock() -> c_int { + debug!("emscripten::_clock"); + 0 // TODO: unimplemented +} + +/// emscripten: _difftime +pub extern "C" fn _difftime(t0: u32, t1: u32) -> c_int { + debug!("emscripten::_difftime"); + (t0 - t1) as _ +} + #[repr(C)] struct guest_tm { pub tm_sec: c_int, // 0 @@ -111,7 +129,7 @@ pub extern "C" fn _asctime(time: u32, instance: &mut Instance) -> u32 { let time_str_ptr = fmt_time(time, instance); copy_cstr_into_wasm(instance, time_str_ptr) - // let c_str = instance.memory_offset_addr(0, time_str_offset as _) as *mut i8; + // let c_str = instance.memory_offset_addr(0, res as _) as *mut i8; // use std::ffi::CStr; // debug!("#### cstr = {:?}", CStr::from_ptr(c_str)); } @@ -129,7 +147,7 @@ pub extern "C" fn _asctime_r(time: u32, buf: u32, instance: &mut Instance) -> u3 let time_str_ptr = fmt_time(time, instance); write_to_buf(time_str_ptr, buf, 26, instance) - // let c_str = instance.memory_offset_addr(0, time_str_offset as _) as *mut i8; + // let c_str = instance.memory_offset_addr(0, res as _) as *mut i8; // use std::ffi::CStr; // debug!("#### cstr = {:?}", CStr::from_ptr(c_str)); } diff --git a/src/apis/emscripten/utils.rs b/src/apis/emscripten/utils.rs index c3714b1d7d1..6e77d7aabce 100644 --- a/src/apis/emscripten/utils.rs +++ b/src/apis/emscripten/utils.rs @@ -27,6 +27,7 @@ pub unsafe fn write_to_buf(string: *const c_char, buf: u32, max: u32, instance: buf } +/// This function expects nullbyte to be appended. pub unsafe fn copy_cstr_into_wasm(instance: &mut Instance, cstr: *const c_char) -> u32 { let s = CStr::from_ptr(cstr).to_str().unwrap(); let cstr_len = s.len(); diff --git a/src/apis/host/posix/env.rs b/src/apis/host/posix/env.rs index b0fff8174db..5943e589ba3 100644 --- a/src/apis/host/posix/env.rs +++ b/src/apis/host/posix/env.rs @@ -1,7 +1,7 @@ use crate::webassembly::Instance; use std::env; -pub extern "C" fn get_env(name: &str, _instance: &mut Instance) -> Result { - debug!("host::get_env({:?})", name); - env::var(name) -} +// pub extern "C" fn get_env(name: &str, _instance: &mut Instance) -> Result { +// debug!("host::get_env({:?})", name); +// env::var(name) +// } diff --git a/src/emtests/syscalls.rs b/src/emtests/syscalls.rs new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/webassembly/instance.rs b/src/webassembly/instance.rs index a958f27c2bd..1692e9cd0e3 100644 --- a/src/webassembly/instance.rs +++ b/src/webassembly/instance.rs @@ -15,7 +15,9 @@ use cranelift_wasm::{FuncIndex, GlobalInit}; use indicatif::{ProgressBar, ProgressStyle}; use rayon::prelude::*; +use libc::c_int; use region; +use std::cell::UnsafeCell; use std::iter::FromIterator; use std::iter::Iterator; use std::mem::size_of; @@ -72,6 +74,7 @@ pub struct EmscriptenData { pub memalign: extern "C" fn(u32, u32, &mut Instance) -> u32, pub memset: extern "C" fn(u32, i32, u32, &mut Instance) -> u32, pub stack_alloc: extern "C" fn(u32, &Instance) -> u32, + pub jumps: Vec>, } impl EmscriptenData { @@ -116,6 +119,7 @@ impl EmscriptenData { memalign: mem::transmute(memalign_addr), memset: mem::transmute(memset_addr), stack_alloc: mem::transmute(stack_alloc_addr), + jumps: Vec::new(), } } }