Merge pull request #233 from wasmerio/feature/emscripten-for-bundling

implement more emscripten functions
This commit is contained in:
Syrus Akbary 2019-03-06 17:26:58 -08:00 committed by GitHub
commit acf2bc2722
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 197 additions and 0 deletions

2
.gitignore vendored
View File

@ -3,3 +3,5 @@
/artifacts
.DS_Store
.idea
\.vscode

17
lib/emscripten/emtests/test_execvp.c vendored Normal file
View File

@ -0,0 +1,17 @@
#include <stdio.h>
#include <unistd.h>
int main() {
char command[] = "touch";
char arg1[] = "foo.txt";
char* argv[3];
argv[0] = command;
argv[1] = arg1;
argv[2] = 0;
printf("_execvp\n");
int result = execvp(command, argv);
// should not return, and not print this message
printf("error");
return 0;
}

View File

@ -0,0 +1 @@
_execvp

BIN
lib/emscripten/emtests/test_execvp.wasm vendored Normal file

Binary file not shown.

View File

@ -0,0 +1,18 @@
#include <stdio.h>
#include <unistd.h>
int main() {
char command[] = "C:\\Windows\\System32\\cmd.exe";
char arg1[] = "echo";
char arg2[] = "foo";
char* argv[4];
argv[0] = command;
argv[1] = arg1;
argv[2] = arg2;
argv[3] = 0;
printf("_execvp\n");
int result = execvp(command, argv);
// should not return, and not print this message
printf("error");
return 0;
}

Binary file not shown.

13
lib/emscripten/emtests/test_pipe.c vendored Normal file
View File

@ -0,0 +1,13 @@
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main() {
int mypipe[2];
printf("pipe\n");
if (pipe(mypipe)) {
printf("error\n");
}
return 0;
}

1
lib/emscripten/emtests/test_pipe.out vendored Normal file
View File

@ -0,0 +1 @@
pipe

BIN
lib/emscripten/emtests/test_pipe.wasm vendored Normal file

Binary file not shown.

View File

@ -0,0 +1,40 @@
use libc::execvp as libc_execvp;
use std::cell::Cell;
use std::ffi::CString;
use wasmer_runtime_core::vm::Ctx;
pub fn execvp(ctx: &mut Ctx, command_name_offset: u32, argv_offset: u32) -> i32 {
// a single reference to re-use
let emscripten_memory = ctx.memory(0);
// read command name as string
let command_name_string_vec: Vec<u8> = emscripten_memory.view()
[(command_name_offset as usize)..]
.iter()
.map(|cell| cell.get())
.take_while(|&byte| byte != 0)
.collect();
let command_name_string = CString::new(command_name_string_vec).unwrap();
// get the array of args
let mut argv: Vec<*const i8> = emscripten_memory.view()[((argv_offset / 4) as usize)..]
.iter()
.map(|cell: &Cell<u32>| cell.get())
.take_while(|&byte| byte != 0)
.map(|offset| {
let p: *const i8 = (emscripten_memory.view::<u8>()[(offset as usize)..])
.iter()
.map(|cell| cell.as_ptr() as *const i8)
.collect::<Vec<*const i8>>()[0];
p
})
.collect();
// push a nullptr on to the end of the args array
argv.push(std::ptr::null());
// construct raw pointers and hand them to `execvp`
let command_pointer = command_name_string.as_ptr() as *const i8;
let args_pointer = argv.as_ptr();
unsafe { libc_execvp(command_pointer, args_pointer) }
}

View File

@ -0,0 +1,7 @@
use wasmer_runtime_core::vm::Ctx;
// __exit
pub fn exit(_ctx: &mut Ctx, value: i32) {
debug!("emscripten::exit {}", value);
::std::process::exit(value);
}

View File

@ -31,6 +31,8 @@ mod emscripten_target;
mod env;
mod errno;
mod exception;
mod exec;
mod exit;
mod io;
mod jmp;
mod linking;
@ -439,6 +441,12 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject
"___unlock" => func!(crate::lock::___unlock),
"___wait" => func!(crate::lock::___wait),
// exec
"_execvp" => func!(crate::exec::execvp),
// exit
"__exit" => func!(crate::exit::exit),
// Env
"___assert_fail" => func!(crate::env::___assert_fail),
"_getenv" => func!(crate::env::_getenv),
@ -481,6 +489,7 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject
"___syscall39" => func!(crate::syscalls::___syscall39),
"___syscall38" => func!(crate::syscalls::___syscall38),
"___syscall40" => func!(crate::syscalls::___syscall40),
"___syscall42" => func!(crate::syscalls::___syscall42),
"___syscall54" => func!(crate::syscalls::___syscall54),
"___syscall57" => func!(crate::syscalls::___syscall57),
"___syscall60" => func!(crate::syscalls::___syscall60),

View File

@ -40,6 +40,7 @@ use libc::{
use wasmer_runtime_core::vm::Ctx;
use super::env;
use std::cell::Cell;
use std::slice;
// use std::sys::fd::FileDesc;
@ -140,6 +141,33 @@ pub fn ___syscall40(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int
unsafe { rmdir(pathname_addr) }
}
// pipe
pub fn ___syscall42(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int {
debug!("emscripten::___syscall42 (pipe)");
// offset to a file descriptor, which contains a read end and write end, 2 integers
let fd_offset: u32 = varargs.get(ctx);
use std::cell::Cell;
let emscripten_memory = ctx.memory(0);
// convert the file descriptor into a vec with two slots
let mut fd_vec: Vec<c_int> = emscripten_memory.view()[((fd_offset / 4) as usize)..]
.iter()
.map(|pipe_end: &Cell<c_int>| pipe_end.get())
.take(2)
.collect();
// get it as a mutable pointer
let fd_ptr = fd_vec.as_mut_ptr();
// call pipe and store the pointers in this array
#[cfg(target_os = "windows")]
let result: c_int = unsafe { libc::pipe(fd_ptr, 2048, 0) };
#[cfg(not(target_os = "windows"))]
let result: c_int = unsafe { libc::pipe(fd_ptr) };
result
}
pub fn ___syscall60(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 {
debug!("emscripten::___syscall60");
-1

View File

@ -41,3 +41,37 @@ macro_rules! assert_emscripten_output {
);
}};
}
pub fn assert_emscripten_output(wasm_bytes: &[u8], raw_expected_str: &str) {
use wasmer_clif_backend::CraneliftCompiler;
use wasmer_emscripten::{generate_emscripten_env, stdio::StdioCapturer, EmscriptenGlobals};
let module = wasmer_runtime_core::compile_with(&wasm_bytes[..], &CraneliftCompiler::new())
.expect("WASM can't be compiled");
let mut emscripten_globals = EmscriptenGlobals::new(&module);
let import_object = generate_emscripten_env(&mut emscripten_globals);
let mut instance = module
.instantiate(&import_object)
.map_err(|err| format!("Can't instantiate the WebAssembly module: {:?}", err))
.unwrap();
let capturer = StdioCapturer::new();
wasmer_emscripten::run_emscripten_instance(&module, &mut instance, "test", vec![])
.expect("run_emscripten_instance finishes");
let raw_output_string = capturer.end().unwrap().0;
// trim the strings to avoid cross-platform line ending and white space issues
let output = raw_output_string.trim();
let expected_output = raw_expected_str.trim();
let contains_output = output.contains(expected_output);
assert!(
contains_output,
"Output: `{}` does not contain expected output: `{}`",
output, expected_output
);
}

View File

@ -43,6 +43,7 @@ mod test_exceptions_2;
mod test_exceptions_multi;
mod test_exceptions_std;
mod test_exceptions_white_list;
mod test_execvp;
mod test_fast_math;
mod test_flexarray_struct;
mod test_float32_precise;
@ -118,6 +119,7 @@ mod test_nested_struct_varargs;
mod test_nl_types;
mod test_perrar;
mod test_phiundef;
mod test_pipe;
mod test_poll;
mod test_posixtime;
mod test_printf_2;

View File

@ -0,0 +1,17 @@
#[test]
fn test_execvp() {
#[cfg(not(target_os = "windows"))]
assert_emscripten_output!(
"../../emtests/test_execvp.wasm",
"test_execvp",
vec![],
"../../emtests/test_execvp.out"
);
#[cfg(target_os = "windows")]
assert_emscripten_output!(
"../../emtests/test_execvp_windows.wasm",
"test_execvp",
vec![],
"../../emtests/test_execvp.out"
);
}

View File

@ -0,0 +1,8 @@
use crate::emtests::_common::assert_emscripten_output;
#[test]
fn test_pipe() {
let wasm_bytes = include_bytes!("../../emtests/test_pipe.wasm");
let expected_str = include_str!("../../emtests/test_pipe.out");
assert_emscripten_output(wasm_bytes, expected_str);
}