Add support for argc/argv

This commit is contained in:
Steve Akinyemi 2018-12-06 12:32:53 +01:00
parent fbc2fc9c50
commit 2b3c87e80c
6 changed files with 105 additions and 20 deletions

View File

@ -19,7 +19,7 @@ mod utils;
mod varargs; mod varargs;
pub use self::storage::{align_memory, static_alloc}; 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? // TODO: Magic number - how is this calculated?
const TOTAL_STACK: u32 = 5242880; const TOTAL_STACK: u32 = 5242880;

View File

@ -524,7 +524,7 @@ pub extern "C" fn ___syscall192(
"=> addr: {}, len: {}, prot: {}, flags: {}, fd: {}, off: {}", "=> addr: {}, len: {}, prot: {}, flags: {}, fd: {}, off: {}",
addr, len, prot, flags, fd, off addr, len, prot, flags, fd, off
); );
let (memalign, memset) = { let (memalign, memset) = {
let emscripten_data = &instance.emscripten_data.as_ref().unwrap(); let emscripten_data = &instance.emscripten_data.as_ref().unwrap();
(emscripten_data.memalign, emscripten_data.memset) (emscripten_data.memalign, emscripten_data.memset)
@ -829,7 +829,7 @@ pub extern "C" fn ___syscall63(
unsafe { dup2(src, dst) } unsafe { dup2(src, dst) }
} }
// newselect // select
pub extern "C" fn ___syscall142( pub extern "C" fn ___syscall142(
_which: c_int, _which: c_int,
mut varargs: VarArgs, mut varargs: VarArgs,
@ -848,7 +848,7 @@ pub extern "C" fn ___syscall142(
let readfds_ptr = instance.memory_offset_addr(0, readfds as _) as _; let readfds_ptr = instance.memory_offset_addr(0, readfds as _) as _;
let writefds_ptr = instance.memory_offset_addr(0, writefds as _) as _; let writefds_ptr = instance.memory_offset_addr(0, writefds as _) as _;
unsafe { select(nfds, readfds_ptr, writefds_ptr, 0 as _, 0 as _) } unsafe { select(nfds, readfds_ptr, writefds_ptr, 0 as _, 0 as _) }
} }

View File

@ -2,7 +2,7 @@ use byteorder::{ByteOrder, LittleEndian};
use crate::webassembly::module::Module; use crate::webassembly::module::Module;
use crate::webassembly::Instance; use crate::webassembly::Instance;
use libc::stat; use libc::stat;
use std::ffi::CStr; use std::ffi::{CStr, CString};
use std::os::raw::c_char; use std::os::raw::c_char;
use std::slice; use std::slice;
@ -16,7 +16,7 @@ pub fn is_emscripten_module(module: &Module) -> bool {
return false; 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 s = CStr::from_ptr(cstr).to_str().unwrap();
let space_offset = (instance.emscripten_data.as_ref().unwrap().malloc)(s.len() as _, instance); 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; 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 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( pub unsafe fn copy_terminated_array_of_cstrs(
_instance: &mut Instance, _instance: &mut Instance,
cstrs: *mut *mut c_char, cstrs: *mut *mut c_char,

View File

@ -1,4 +1,4 @@
pub mod emscripten; pub mod emscripten;
pub mod host; 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};

View File

@ -7,6 +7,7 @@ use std::io::Read;
use std::path::PathBuf; use std::path::PathBuf;
use std::process::exit; use std::process::exit;
use apis::emscripten::copy_cstr_array_into_wasm;
use structopt::StructOpt; use structopt::StructOpt;
use wasmer::*; use wasmer::*;
@ -28,11 +29,17 @@ enum CLIOptions {
struct Run { struct Run {
#[structopt(short = "d", long = "debug")] #[structopt(short = "d", long = "debug")]
debug: bool, debug: bool,
/// Input file /// Input file
#[structopt(parse(from_os_str))] #[structopt(parse(from_os_str))]
path: PathBuf, path: PathBuf,
/// Application arguments
#[structopt(name = "--", raw(multiple="true"))]
args: Vec<String>,
} }
/// Read the contents of a file /// Read the contents of a file
fn read_file_contents(path: &PathBuf) -> Result<Vec<u8>, io::Error> { fn read_file_contents(path: &PathBuf) -> Result<Vec<u8>, io::Error> {
let mut buffer: Vec<u8> = Vec::new(); let mut buffer: Vec<u8> = Vec::new();
@ -42,18 +49,22 @@ fn read_file_contents(path: &PathBuf) -> Result<Vec<u8>, io::Error> {
} }
/// Execute a WASM/WAT file /// Execute a WASM/WAT file
fn execute_wasm(wasm_path: PathBuf) -> Result<(), String> { fn execute_wasm(options: &Run) -> Result<(), String> {
let mut wasm_binary: Vec<u8> = read_file_contents(&wasm_path).map_err(|err| { let wasm_path = &options.path;
let mut wasm_binary: Vec<u8> = read_file_contents(wasm_path).map_err(|err| {
format!( format!(
"Can't read the file {}: {}", "Can't read the file {}: {}",
wasm_path.as_os_str().to_string_lossy(), wasm_path.as_os_str().to_string_lossy(),
err err
) )
})?; })?;
if !webassembly::utils::is_wasm_binary(&wasm_binary) { if !webassembly::utils::is_wasm_binary(&wasm_binary) {
wasm_binary = wabt::wat2wasm(wasm_binary) wasm_binary = wabt::wat2wasm(wasm_binary)
.map_err(|err| format!("Can't convert from wast to wasm: {:?}", err))?; .map_err(|err| format!("Can't convert from wast to wasm: {:?}", err))?;
} }
// TODO: We should instantiate after compilation, so we provide the // TODO: We should instantiate after compilation, so we provide the
// emscripten environment conditionally based on the module // emscripten environment conditionally based on the module
let import_object = apis::generate_emscripten_env(); 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))?; .map_err(|err| format!("Can't instantiate the WebAssembly module: {}", err))?;
if apis::emscripten::is_emscripten_module(&module) { if apis::emscripten::is_emscripten_module(&module) {
// Emscripten __ATINIT__ // Emscripten __ATINIT__
if let Some(&webassembly::Export::Function(environ_constructor_index)) = module.info.exports.get("___emscripten_environ_constructor") { if let Some(&webassembly::Export::Function(environ_constructor_index)) = module.info.exports.get("___emscripten_environ_constructor") {
debug!("emscripten::___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); get_instance_function!(instance, environ_constructor_index);
call_protected!(___emscripten_environ_constructor(&instance)).map_err(|err| format!("{}", err))?; 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) // TODO: We also need to handle TTY.init() and SOCKFS.root = FS.mount(SOCKFS, {}, null)
let func_index = match module.info.exports.get("_main") { let func_index = match module.info.exports.get("_main") {
Some(&webassembly::Export::Function(index)) => index, Some(&webassembly::Export::Function(index)) => index,
_ => panic!("_main emscripten function not found"), _ => panic!("_main emscripten function not found"),
}; };
let main: extern "C" fn(u32, u32, &webassembly::Instance) = let main: extern "C" fn(u32, u32, &webassembly::Instance) =
get_instance_function!(instance, func_index); 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<String> = 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__ // TODO: We should implement emscripten __ATEXIT__
} else { } else {
let func_index = let func_index =
@ -93,7 +138,7 @@ fn execute_wasm(wasm_path: PathBuf) -> Result<(), String> {
} }
fn run(options: Run) { fn run(options: Run) {
match execute_wasm(options.path.clone()) { match execute_wasm(&options) {
Ok(()) => {} Ok(()) => {}
Err(message) => { Err(message) => {
// let name = options.path.as_os_str().to_string_lossy(); // let name = options.path.as_os_str().to_string_lossy();
@ -110,3 +155,9 @@ fn main() {
CLIOptions::SelfUpdate => update::self_update(), CLIOptions::SelfUpdate => update::self_update(),
} }
} }
fn get_args() {
}

View File

@ -70,7 +70,7 @@ fn get_function_addr(
} }
pub struct EmscriptenData { 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 free: extern "C" fn(i32, &mut Instance),
pub memalign: extern "C" fn (u32, u32, &mut Instance) -> u32, pub memalign: extern "C" fn (u32, u32, &mut Instance) -> u32,
pub memset: extern "C" fn(u32, i32, 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 memalign_export = module.info.exports.get("_memalign");
let memset_export = module.info.exports.get("_memset"); 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 mut malloc_addr = 0 as *const u8;
let malloc_addr = get_function_addr(&malloc_index, &import_functions, &functions); let mut free_addr = 0 as *const u8;
let free_addr = get_function_addr(&free_index, &import_functions, &functions); let mut memalign_addr = 0 as *const u8;
let memalign_addr = get_function_addr(&memalign_index, &import_functions, &functions); let mut memset_addr = 0 as *const u8;
let memset_addr = get_function_addr(&memset_index, &import_functions, &functions);
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 { Some(EmscriptenData {
malloc: mem::transmute(malloc_addr), malloc: mem::transmute(malloc_addr),
free: mem::transmute(free_addr), free: mem::transmute(free_addr),
memalign: mem::transmute(memalign_addr), memalign: mem::transmute(memalign_addr),
memset: mem::transmute(memset_addr), memset: mem::transmute(memset_addr),
}) })
} else {
None
} }
} }
} else { } else {