Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improved Emscripten debugging + optipng #422

Merged
merged 13 commits into from
May 6, 2019
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ All PRs to the Wasmer repository must add to this file.
Blocks of changes will separated by version increments.

## **[Unreleased]**
- [#422](https://github.com/wasmerio/wasmer/pull/422) Improved Emscripten functions to run optipng and pngquant compiled to wasm
- [#409](https://github.com/wasmerio/wasmer/pull/409) Improved Emscripten functions to run JavascriptCore compiled to wasm
- [#399](https://github.com/wasmerio/wasmer/pull/399) Add example of using a plugin extended from WASI
- [#397](https://github.com/wasmerio/wasmer/pull/397) Fix WASI fs abstraction to work on Windows
Expand Down
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ rustc_version = "0.2.3"

[features]
default = ["fast-tests", "wasi"]
debug = ["wasmer-clif-backend/debug", "wasmer-runtime-core/debug"]
debug = ["wasmer-runtime-core/debug"]
extra-debug = ["wasmer-clif-backend/debug", "wasmer-runtime-core/debug"]
# This feature will allow cargo test to run much faster
fast-tests = []
"backend:llvm" = ["wasmer-llvm-backend"]
Expand Down
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -84,5 +84,8 @@ production-release:
debug-release:
cargo build --release --features "debug"

extra-debug-release:
cargo build --release --features "extra-debug"

publish-release:
ghr -t ${GITHUB_TOKEN} -u ${CIRCLE_PROJECT_USERNAME} -r ${CIRCLE_PROJECT_REPONAME} -c ${CIRCLE_SHA1} -delete ${VERSION} ./artifacts/
9 changes: 9 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# WebAssembly Examples

In this directory you can find a set of different examples that can run on the Wasmer WebAssembly runtime:

* Cowsay (WASI ABI) [[source-code](https://github.com/wapm-packages/cowsay)] [[wapm-package](https://wapm.io/package/cowsay)]
* Nginx (Emscripten ABI) [[source-code](https://github.com/wapm-packages/nginx)] [[wapm-package](https://wapm.io/package/nginx)]
* Lua (Emscripten ABI) [[source-code](https://github.com/wapm-packages/lua)] [[wapm-package](https://wapm.io/package/lua)]
* PHP (Emscripten ABI) [[source-code](https://github.com/wapm-packages/php)] [[wapm-package](https://wapm.io/package/php)]
* SQLite (Emscripten ABI) [[source-code](https://github.com/wapm-packages/sqlite)] [[wapm-package](https://wapm.io/package/sqlite)]
13 changes: 10 additions & 3 deletions lib/emscripten/src/emscripten_target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ pub fn _dladdr(_ctx: &mut Ctx, _a: i32, _b: i32) -> i32 {
0
}
pub fn _pthread_attr_init(_ctx: &mut Ctx, _a: i32) -> i32 {
debug!("emscripten::_pthread_attr_init");
debug!("emscripten::_pthread_attr_init({})", _a);
0
}
pub fn _pthread_attr_destroy(_ctx: &mut Ctx, _a: i32) -> i32 {
Expand All @@ -68,7 +68,10 @@ pub fn _pthread_attr_getstack(
_stacksize: i32,
_other: i32,
) -> i32 {
debug!("emscripten::_pthread_attr_getstack");
debug!(
"emscripten::_pthread_attr_getstack({}, {}, {})",
_stackaddr, _stacksize, _other
);
// TODO: Translate from Emscripten
// HEAP32[stackaddr >> 2] = STACK_BASE;
// HEAP32[stacksize >> 2] = TOTAL_STACK;
Expand All @@ -87,7 +90,7 @@ pub fn _pthread_getspecific(_ctx: &mut Ctx, _a: i32) -> i32 {
0
}
pub fn _pthread_getattr_np(_ctx: &mut Ctx, _thread: i32, _attr: i32) -> i32 {
debug!("emscripten::_pthread_getattr_np");
debug!("emscripten::_pthread_getattr_np({}, {})", _thread, _attr);
0
}
pub fn _pthread_setspecific(_ctx: &mut Ctx, _a: i32, _b: i32) -> i32 {
Expand Down Expand Up @@ -732,6 +735,10 @@ pub fn invoke_vj(ctx: &mut Ctx, index: i32, a1: i32, a2: i32) {
panic!("dyn_call_vj is set to None");
}
}
pub fn invoke_vjji(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32, a4: i32, a5: i32) {
debug!("emscripten::invoke_vjji");
invoke_no_return!(ctx, dyn_call_vjji, index, a1, a2, a3, a4, a5)
}
pub fn invoke_vij(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32) {
debug!("emscripten::invoke_vij");
if let Some(dyn_call_vij) = &get_emscripten_data(ctx).dyn_call_vij {
Expand Down
42 changes: 36 additions & 6 deletions lib/emscripten/src/env/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,21 +60,51 @@ pub fn ___build_environment(ctx: &mut Ctx, environ: c_int) {
const MAX_ENV_VALUES: u32 = 64;
const TOTAL_ENV_SIZE: u32 = 1024;
let environment = emscripten_memory_pointer!(ctx.memory(0), environ) as *mut c_int;
unsafe {
let (mut pool_offset, env_ptr, mut pool_ptr) = unsafe {
let (pool_offset, _pool_slice): (u32, &mut [u8]) =
allocate_on_stack(ctx, TOTAL_ENV_SIZE as u32);
let (env_offset, _env_slice): (u32, &mut [u8]) =
allocate_on_stack(ctx, (MAX_ENV_VALUES * 4) as u32);
let env_ptr = emscripten_memory_pointer!(ctx.memory(0), env_offset) as *mut c_int;
let mut _pool_ptr = emscripten_memory_pointer!(ctx.memory(0), pool_offset) as *mut c_int;
let pool_ptr = emscripten_memory_pointer!(ctx.memory(0), pool_offset) as *mut u8;
*env_ptr = pool_offset as i32;
*environment = env_offset as i32;

// *env_ptr = 0;
(pool_offset, env_ptr, pool_ptr)
};
// unsafe {
// *env_ptr = 0;
// };

// *env_ptr = 0;
let default_vars = vec![
["USER", "web_user"],
["LOGNAME", "web_user"],
["PATH", "/"],
["PWD", "/"],
["HOME", "/home/web_user"],
["LANG", "C.UTF-8"],
["_", "thisProgram"],
];
let mut strings = vec![];
let mut total_size = 0;
for [key, val] in &default_vars {
let line = key.to_string() + "=" + val;
total_size += line.len();
strings.push(line);
}
if total_size as u32 > TOTAL_ENV_SIZE {
panic!("Environment size exceeded TOTAL_ENV_SIZE!");
}
unsafe {
for (i, s) in strings.iter().enumerate() {
for (j, c) in s.chars().enumerate() {
debug_assert!(c < u8::max_value() as char);
*pool_ptr.add(j) = c as u8;
}
*env_ptr.add(i * 4) = pool_offset as i32;
pool_offset += s.len() as u32 + 1;
pool_ptr = pool_ptr.add(s.len() + 1);
}
*env_ptr.add(strings.len() * 4) = 0;
}
}

pub fn ___assert_fail(_ctx: &mut Ctx, _a: c_int, _b: c_int, _c: c_int, _d: c_int) {
Expand Down
2 changes: 1 addition & 1 deletion lib/emscripten/src/jmp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use wasmer_runtime_core::vm::Ctx;
/// setjmp
pub fn __setjmp(ctx: &mut Ctx, _env_addr: u32) -> c_int {
debug!("emscripten::__setjmp (setjmp)");
abort_with_message(ctx, "missing function: _longjmp");
abort_with_message(ctx, "missing function: _setjmp");
unreachable!()
// unsafe {
// // Rather than using the env as the holder of the jump buffer pointer,
Expand Down
61 changes: 36 additions & 25 deletions lib/emscripten/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ pub struct EmscriptenData<'a> {
pub dyn_call_viijiii: Option<Func<'a, (i32, i32, i32, i32, i32, i32, i32, i32)>>,
pub dyn_call_viijj: Option<Func<'a, (i32, i32, i32, i32, i32, i32, i32)>>,
pub dyn_call_vj: Option<Func<'a, (i32, i32, i32)>>,
pub dyn_call_vjji: Option<Func<'a, (i32, i32, i32, i32, i32, i32)>>,
pub dyn_call_vij: Option<Func<'a, (i32, i32, i32, i32)>>,
pub dyn_call_viji: Option<Func<'a, (i32, i32, i32, i32, i32)>>,
pub dyn_call_vijiii: Option<Func<'a, (i32, i32, i32, i32, i32, i32, i32)>>,
Expand All @@ -146,11 +147,7 @@ impl<'a> EmscriptenData<'a> {
pub fn new(instance: &'a mut Instance) -> EmscriptenData<'a> {
let malloc = instance.func("_malloc").unwrap();
let free = instance.func("_free").unwrap();
let memalign = if let Ok(func) = instance.func("_memalign") {
Some(func)
} else {
None
};
let memalign = instance.func("_memalign").ok();
let memset = instance.func("_memset").unwrap();
let stack_alloc = instance.func("stackAlloc").unwrap();

Expand Down Expand Up @@ -198,6 +195,7 @@ impl<'a> EmscriptenData<'a> {
let dyn_call_viijiii = instance.func("dynCall_viijiii").ok();
let dyn_call_viijj = instance.func("dynCall_viijj").ok();
let dyn_call_vj = instance.func("dynCall_vj").ok();
let dyn_call_vjji = instance.func("dynCall_vjji").ok();
let dyn_call_vij = instance.func("dynCall_vij").ok();
let dyn_call_viji = instance.func("dynCall_viji").ok();
let dyn_call_vijiii = instance.func("dynCall_vijiii").ok();
Expand Down Expand Up @@ -261,6 +259,7 @@ impl<'a> EmscriptenData<'a> {
dyn_call_viijiii,
dyn_call_viijj,
dyn_call_vj,
dyn_call_vjji,
dyn_call_vij,
dyn_call_viji,
dyn_call_vijiii,
Expand All @@ -282,6 +281,7 @@ pub fn run_emscripten_instance(
instance: &mut Instance,
path: &str,
args: Vec<&str>,
entrypoint: Option<String>,
) -> CallResult<()> {
let mut data = EmscriptenData::new(instance);
let data_ptr = &mut data as *mut _ as *mut c_void;
Expand All @@ -299,45 +299,54 @@ pub fn run_emscripten_instance(

// println!("running emscripten instance");

let main_func = instance.dyn_func("_main")?;
let num_params = main_func.signature().params().len();
let _result = match num_params {
2 => {
let (argc, argv) = store_module_arguments(instance.context_mut(), path, args);
instance.call("_main", &[Value::I32(argc as i32), Value::I32(argv as i32)])?;
}
0 => {
instance.call("_main", &[])?;
}
_ => panic!(
"The emscripten main function has received an incorrect number of params {}",
num_params
),
};
if let Some(ep) = entrypoint {
debug!("Running entry point: {}", &ep);
let ep_fn = instance.dyn_func(&ep)?;
let arg = unsafe { allocate_cstr_on_stack(instance.context_mut(), args[0]).0 };
//let (argc, argv) = store_module_arguments(instance.context_mut(), args);
instance.call(&ep, &[Value::I32(arg as i32)])?;
} else {
let main_func = instance.dyn_func("_main")?;
let num_params = main_func.signature().params().len();
let _result = match num_params {
2 => {
let mut new_args = vec![path];
new_args.extend(args);
let (argc, argv) = store_module_arguments(instance.context_mut(), new_args);
instance.call("_main", &[Value::I32(argc as i32), Value::I32(argv as i32)])?;
}
0 => {
instance.call("_main", &[])?;
}
_ => panic!(
"The emscripten main function has received an incorrect number of params {}",
num_params
),
};
}

// TODO atexit for emscripten
// println!("{:?}", data);
Ok(())
}

fn store_module_arguments(ctx: &mut Ctx, path: &str, args: Vec<&str>) -> (u32, u32) {
fn store_module_arguments(ctx: &mut Ctx, args: Vec<&str>) -> (u32, u32) {
let argc = args.len() + 1;

let mut args_slice = vec![0; argc];
args_slice[0] = unsafe { allocate_cstr_on_stack(ctx, path).0 };
for (slot, arg) in args_slice[1..argc].iter_mut().zip(args.iter()) {
for (slot, arg) in args_slice[0..argc].iter_mut().zip(args.iter()) {
*slot = unsafe { allocate_cstr_on_stack(ctx, &arg).0 };
}

let (argv_offset, argv_slice): (_, &mut [u32]) =
unsafe { allocate_on_stack(ctx, ((argc + 1) * 4) as u32) };
unsafe { allocate_on_stack(ctx, ((argc) * 4) as u32) };
assert!(!argv_slice.is_empty());
for (slot, arg) in argv_slice[0..argc].iter_mut().zip(args_slice.iter()) {
*slot = *arg
}
argv_slice[argc] = 0;

(argc as u32, argv_offset)
(argc as u32 - 1, argv_offset)
}

pub fn emscripten_set_up_memory(memory: &Memory, globals: &EmscriptenGlobalsData) {
Expand Down Expand Up @@ -597,6 +606,7 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject
"___syscall272" => func!(crate::syscalls::___syscall272),
"___syscall295" => func!(crate::syscalls::___syscall295),
"___syscall300" => func!(crate::syscalls::___syscall300),
"___syscall320" => func!(crate::syscalls::___syscall320),
"___syscall324" => func!(crate::syscalls::___syscall324),
"___syscall330" => func!(crate::syscalls::___syscall330),
"___syscall334" => func!(crate::syscalls::___syscall334),
Expand Down Expand Up @@ -718,6 +728,7 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject
"invoke_v" => func!(crate::emscripten_target::invoke_v),
"invoke_vi" => func!(crate::emscripten_target::invoke_vi),
"invoke_vj" => func!(crate::emscripten_target::invoke_vj),
"invoke_vjji" => func!(crate::emscripten_target::invoke_vjji),
"invoke_vii" => func!(crate::emscripten_target::invoke_vii),
"invoke_viii" => func!(crate::emscripten_target::invoke_viii),
"invoke_viiii" => func!(crate::emscripten_target::invoke_viiii),
Expand Down
7 changes: 5 additions & 2 deletions lib/emscripten/src/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,11 @@ pub fn _emscripten_memcpy_big(ctx: &mut Ctx, dest: u32, src: u32, len: u32) -> u

/// emscripten: _emscripten_get_heap_size
pub fn _emscripten_get_heap_size(ctx: &mut Ctx) -> u32 {
debug!("emscripten::_emscripten_get_heap_size",);
ctx.memory(0).size().bytes().0 as u32
debug!("emscripten::_emscripten_get_heap_size");
let result = ctx.memory(0).size().bytes().0 as u32;
debug!("=> {}", result);

result
}

// From emscripten implementation
Expand Down
2 changes: 1 addition & 1 deletion lib/emscripten/src/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ pub fn _raise(_ctx: &mut Ctx, _one: i32) -> i32 {
}

pub fn _sem_init(_ctx: &mut Ctx, _one: i32, _two: i32, _three: i32) -> i32 {
debug!("emscripten::_sem_init");
debug!("emscripten::_sem_init: {}, {}, {}", _one, _two, _three);
0
}

Expand Down
45 changes: 38 additions & 7 deletions lib/emscripten/src/syscalls/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,13 @@ use libc::{
off_t,
// open,
read,
rename,
// sockaddr_in,
// readv,
rmdir,
// writev,
stat,
write,
// sockaddr_in,
};
use wasmer_runtime_core::vm::Ctx;

Expand Down Expand Up @@ -118,9 +119,23 @@ pub fn ___syscall20(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 {
unsafe { getpid() }
}

pub fn ___syscall38(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 {
debug!("emscripten::___syscall38");
-1
// rename
pub fn ___syscall38(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> i32 {
debug!("emscripten::___syscall38 (rename)");
let old_path_addr: u32 = varargs.get(ctx);
let new_path_addr: u32 = varargs.get(ctx);
let old_path = emscripten_memory_pointer!(ctx.memory(0), old_path_addr) as *const i8;
let new_path = emscripten_memory_pointer!(ctx.memory(0), new_path_addr) as *const i8;
let result = unsafe { rename(old_path, new_path) };
unsafe {
debug!(
"=> old_path: {}, new_path: {}, result: {}",
std::ffi::CStr::from_ptr(old_path).to_str().unwrap(),
std::ffi::CStr::from_ptr(new_path).to_str().unwrap(),
result
);
}
result
}

// rmdir
Expand Down Expand Up @@ -246,12 +261,22 @@ pub fn ___syscall192(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in
if fd == -1 {
let ptr = env::call_memalign(ctx, 16384, len);
if ptr == 0 {
return -1;
// ENOMEM
return -12;
}
let real_ptr = emscripten_memory_pointer!(ctx.memory(0), ptr) as *const u8;
env::call_memset(ctx, ptr, 0, len);
ptr as _
for i in 0..(len as usize) {
unsafe {
assert_eq!(*real_ptr.add(i), 0);
}
}
debug!("=> ptr: {}", ptr);
return ptr as i32;
} else {
-1
unimplemented!("temp during dev");
// return ENODEV
return -19;
}
}

Expand Down Expand Up @@ -461,6 +486,12 @@ pub fn ___syscall300(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 {
-1
}

// utimensat
pub fn ___syscall320(ctx: &mut Ctx, _which: c_int, mut _varargs: VarArgs) -> c_int {
debug!("emscripten::___syscall320 (utimensat), {}", _which);
0
}

pub fn ___syscall334(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 {
debug!("emscripten::___syscall334");
-1
Expand Down
Loading