From 2b3c87e80c5658616bdf4ffda5a007f5b85d1cd5 Mon Sep 17 00:00:00 2001 From: Steve Akinyemi Date: Thu, 6 Dec 2018 12:32:53 +0100 Subject: [PATCH] Add support for argc/argv --- src/apis/emscripten/mod.rs | 2 +- src/apis/emscripten/syscalls.rs | 6 ++-- src/apis/emscripten/utils.rs | 22 ++++++++++-- src/apis/mod.rs | 2 +- src/bin/wasmer.rs | 59 ++++++++++++++++++++++++++++++--- src/webassembly/instance.rs | 34 ++++++++++++++----- 6 files changed, 105 insertions(+), 20 deletions(-) diff --git a/src/apis/emscripten/mod.rs b/src/apis/emscripten/mod.rs index ad6165211..81de9d7ca 100644 --- a/src/apis/emscripten/mod.rs +++ b/src/apis/emscripten/mod.rs @@ -19,7 +19,7 @@ mod utils; mod varargs; pub use self::storage::{align_memory, static_alloc}; -pub use self::utils::is_emscripten_module; +pub use self::utils::{is_emscripten_module, copy_cstr_array_into_wasm}; // TODO: Magic number - how is this calculated? const TOTAL_STACK: u32 = 5242880; diff --git a/src/apis/emscripten/syscalls.rs b/src/apis/emscripten/syscalls.rs index 6325755fa..8ada6854d 100644 --- a/src/apis/emscripten/syscalls.rs +++ b/src/apis/emscripten/syscalls.rs @@ -524,7 +524,7 @@ pub extern "C" fn ___syscall192( "=> addr: {}, len: {}, prot: {}, flags: {}, fd: {}, off: {}", addr, len, prot, flags, fd, off ); - + let (memalign, memset) = { let emscripten_data = &instance.emscripten_data.as_ref().unwrap(); (emscripten_data.memalign, emscripten_data.memset) @@ -829,7 +829,7 @@ pub extern "C" fn ___syscall63( unsafe { dup2(src, dst) } } -// newselect +// select pub extern "C" fn ___syscall142( _which: c_int, mut varargs: VarArgs, @@ -848,7 +848,7 @@ pub extern "C" fn ___syscall142( 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 _) } } diff --git a/src/apis/emscripten/utils.rs b/src/apis/emscripten/utils.rs index 7e12c11e2..7e67643b1 100644 --- a/src/apis/emscripten/utils.rs +++ b/src/apis/emscripten/utils.rs @@ -2,7 +2,7 @@ use byteorder::{ByteOrder, LittleEndian}; use crate::webassembly::module::Module; use crate::webassembly::Instance; use libc::stat; -use std::ffi::CStr; +use std::ffi::{CStr, CString}; use std::os::raw::c_char; use std::slice; @@ -16,7 +16,7 @@ pub fn is_emscripten_module(module: &Module) -> bool { return false; } -pub unsafe fn copy_cstr_into_wasm(instance: &mut Instance, cstr: *const c_char) -> u32 { +pub unsafe fn copy_cstr_into_wasm(instance: &Instance, cstr: *const c_char) -> u32 { let s = CStr::from_ptr(cstr).to_str().unwrap(); let space_offset = (instance.emscripten_data.as_ref().unwrap().malloc)(s.len() as _, instance); let raw_memory = instance.memory_offset_addr(0, space_offset as _) as *mut u8; @@ -28,6 +28,24 @@ pub unsafe fn copy_cstr_into_wasm(instance: &mut Instance, cstr: *const c_char) space_offset } +pub unsafe fn copy_cstr_array_into_wasm(array_count: u32, array: *mut *mut c_char, instance: &Instance) -> u32 { + let array_offset = (instance.emscripten_data.as_ref().unwrap().malloc)(array_count as _, instance); + + let array_addr = instance.memory_offset_addr(0, array_offset as _) as *mut u32; + for i in 0..array_count { + let offset = copy_cstr_into_wasm( + instance, + *array.offset(i as isize) + ); + *array_addr.offset(i as isize) = offset; + } + + // let first_arg_addr = instance.memory_offset_addr(0, *array_addr.offset(0) as _) as *const i8; + // debug!("###### argv[0] = {:?}", CStr::from_ptr(first_arg_addr)); + + array_offset +} + pub unsafe fn copy_terminated_array_of_cstrs( _instance: &mut Instance, cstrs: *mut *mut c_char, diff --git a/src/apis/mod.rs b/src/apis/mod.rs index d96282fb8..588b2fc1f 100644 --- a/src/apis/mod.rs +++ b/src/apis/mod.rs @@ -1,4 +1,4 @@ pub mod emscripten; pub mod host; -pub use self::emscripten::{align_memory, generate_emscripten_env, is_emscripten_module}; +pub use self::emscripten::{align_memory, generate_emscripten_env, is_emscripten_module, copy_cstr_array_into_wasm}; diff --git a/src/bin/wasmer.rs b/src/bin/wasmer.rs index fe1a80bb4..7abe5ed9a 100644 --- a/src/bin/wasmer.rs +++ b/src/bin/wasmer.rs @@ -7,6 +7,7 @@ use std::io::Read; use std::path::PathBuf; use std::process::exit; +use apis::emscripten::copy_cstr_array_into_wasm; use structopt::StructOpt; use wasmer::*; @@ -28,11 +29,17 @@ enum CLIOptions { struct Run { #[structopt(short = "d", long = "debug")] debug: bool, + /// Input file #[structopt(parse(from_os_str))] path: PathBuf, + + /// Application arguments + #[structopt(name = "--", raw(multiple="true"))] + args: Vec, } + /// Read the contents of a file fn read_file_contents(path: &PathBuf) -> Result, io::Error> { let mut buffer: Vec = Vec::new(); @@ -42,18 +49,22 @@ fn read_file_contents(path: &PathBuf) -> Result, io::Error> { } /// Execute a WASM/WAT file -fn execute_wasm(wasm_path: PathBuf) -> Result<(), String> { - let mut wasm_binary: Vec = read_file_contents(&wasm_path).map_err(|err| { +fn execute_wasm(options: &Run) -> Result<(), String> { + let wasm_path = &options.path; + + let mut wasm_binary: Vec = read_file_contents(wasm_path).map_err(|err| { format!( "Can't read the file {}: {}", wasm_path.as_os_str().to_string_lossy(), err ) })?; + if !webassembly::utils::is_wasm_binary(&wasm_binary) { wasm_binary = wabt::wat2wasm(wasm_binary) .map_err(|err| format!("Can't convert from wast to wasm: {:?}", err))?; } + // TODO: We should instantiate after compilation, so we provide the // emscripten environment conditionally based on the module let import_object = apis::generate_emscripten_env(); @@ -62,6 +73,7 @@ fn execute_wasm(wasm_path: PathBuf) -> Result<(), String> { .map_err(|err| format!("Can't instantiate the WebAssembly module: {}", err))?; if apis::emscripten::is_emscripten_module(&module) { + // Emscripten __ATINIT__ if let Some(&webassembly::Export::Function(environ_constructor_index)) = module.info.exports.get("___emscripten_environ_constructor") { debug!("emscripten::___emscripten_environ_constructor"); @@ -69,14 +81,47 @@ fn execute_wasm(wasm_path: PathBuf) -> Result<(), String> { get_instance_function!(instance, environ_constructor_index); call_protected!(___emscripten_environ_constructor(&instance)).map_err(|err| format!("{}", err))?; }; + // TODO: We also need to handle TTY.init() and SOCKFS.root = FS.mount(SOCKFS, {}, null) let func_index = match module.info.exports.get("_main") { Some(&webassembly::Export::Function(index)) => index, _ => panic!("_main emscripten function not found"), }; + let main: extern "C" fn(u32, u32, &webassembly::Instance) = get_instance_function!(instance, func_index); - return call_protected!(main(0, 0, &instance)).map_err(|err| format!("{}", err)); + + // Application Arguments + let mut arg_values: Vec = Vec::new(); + let mut arg_addrs: Vec<*const u8> = Vec::new(); + let arg_length = options.args.len() + 1; + + arg_values.reserve_exact(arg_length); + arg_addrs.reserve_exact(arg_length); + + // Push name of wasm file + arg_values.push(format!("{}\0", options.path.to_str().unwrap())); + arg_addrs.push(arg_values[0].as_ptr()); + + // Push additional arguments + for (i, arg) in options.args.iter().enumerate() { + arg_values.push(format!("{}\0", arg)); + arg_addrs.push(arg_values[i + 1].as_ptr()); + } + + // Get argument count and pointer to addresses + let argv = arg_addrs.as_ptr() as *mut *mut i8; + let argc = arg_length as u32; + + // Copy the the arguments into the wasm memory and get offset + let argv_offset = unsafe { + copy_cstr_array_into_wasm(argc, argv, &instance) + }; + + debug!("argc = {:?}", argc); + debug!("argv = {:?}", arg_addrs); + + return call_protected!(main(argc, argv_offset, &instance)).map_err(|err| format!("{}", err)); // TODO: We should implement emscripten __ATEXIT__ } else { let func_index = @@ -93,7 +138,7 @@ fn execute_wasm(wasm_path: PathBuf) -> Result<(), String> { } fn run(options: Run) { - match execute_wasm(options.path.clone()) { + match execute_wasm(&options) { Ok(()) => {} Err(message) => { // let name = options.path.as_os_str().to_string_lossy(); @@ -110,3 +155,9 @@ fn main() { CLIOptions::SelfUpdate => update::self_update(), } } + + + +fn get_args() { + +} diff --git a/src/webassembly/instance.rs b/src/webassembly/instance.rs index 92d1a8c4b..8c02d6190 100644 --- a/src/webassembly/instance.rs +++ b/src/webassembly/instance.rs @@ -70,7 +70,7 @@ fn get_function_addr( } pub struct EmscriptenData { - pub malloc: extern "C" fn(i32, &mut Instance) -> u32, + pub malloc: extern "C" fn(i32, &Instance) -> u32, pub free: extern "C" fn(i32, &mut Instance), pub memalign: extern "C" fn (u32, u32, &mut Instance) -> u32, pub memset: extern "C" fn(u32, i32, u32, &mut Instance) -> u32, @@ -551,20 +551,36 @@ impl Instance { let memalign_export = module.info.exports.get("_memalign"); let memset_export = module.info.exports.get("_memset"); - if let (Some(Export::Function(malloc_index)), Some(Export::Function(free_index)), Some(Export::Function(memalign_index)), Some(Export::Function(memset_index))) = (malloc_export, free_export, memalign_export, memset_export) { - let malloc_addr = get_function_addr(&malloc_index, &import_functions, &functions); - let free_addr = get_function_addr(&free_index, &import_functions, &functions); - let memalign_addr = get_function_addr(&memalign_index, &import_functions, &functions); - let memset_addr = get_function_addr(&memset_index, &import_functions, &functions); - + let mut malloc_addr = 0 as *const u8; + let mut free_addr = 0 as *const u8; + let mut memalign_addr = 0 as *const u8; + let mut memset_addr = 0 as *const u8; + + if malloc_export.is_none() && free_export.is_none() && memalign_export.is_none() && memset_export.is_none() { + None + } else { + if let Some(Export::Function(malloc_index)) = malloc_export { + malloc_addr = get_function_addr(&malloc_index, &import_functions, &functions); + } + + if let Some(Export::Function(free_index)) = free_export { + free_addr = get_function_addr(&free_index, &import_functions, &functions); + } + + if let Some(Export::Function(memalign_index)) = memalign_export { + memalign_addr = get_function_addr(&memalign_index, &import_functions, &functions); + } + + if let Some(Export::Function(memset_index)) = memset_export { + memset_addr = get_function_addr(&memset_index, &import_functions, &functions); + } + Some(EmscriptenData { malloc: mem::transmute(malloc_addr), free: mem::transmute(free_addr), memalign: mem::transmute(memalign_addr), memset: mem::transmute(memset_addr), }) - } else { - None } } } else {