Merge pull request #37 from wasmerio/feature/pass-arguments-to-wasm-application

Pass arguments to wasm application
This commit is contained in:
Syrus Akbary
2018-12-06 19:52:09 -08:00
committed by GitHub
6 changed files with 154 additions and 45 deletions

View File

@@ -19,14 +19,14 @@ 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, allocate_on_stack, allocate_cstr_on_stack};
// TODO: Magic number - how is this calculated?
const TOTAL_STACK: u32 = 5242880;
// TODO: Magic number stolen from the generated JS - how is this calculated?
// TODO: Magic number - how is this calculated?
const DYNAMICTOP_PTR_DIFF: u32 = 1088;
const STATIC_BUMP: u32 = 215536; // TODO: make this variable
// TODO: make this variable
const STATIC_BUMP: u32 = 215536;
fn stacktop(static_bump: u32) -> u32 {
align_memory(dynamictop_ptr(static_bump) + 4)
@@ -54,10 +54,18 @@ pub fn emscripten_set_up_memory(memory: &mut LinearMemory) {
let dynamictop_ptr = dynamictop_ptr(STATIC_BUMP) as usize;
let dynamictop_ptr_offset = dynamictop_ptr + mem::size_of::<u32>();
// println!("value = {:?}");
// We avoid failures of setting the u32 in our memory if it's out of bounds
if dynamictop_ptr_offset > memory.len() {
return;
return; // TODO: We should panic instead?
}
// debug!("###### dynamic_base = {:?}", dynamic_base(STATIC_BUMP));
// debug!("###### dynamictop_ptr = {:?}", dynamictop_ptr);
// debug!("###### dynamictop_ptr_offset = {:?}", dynamictop_ptr_offset);
let mem = &mut memory[dynamictop_ptr..dynamictop_ptr_offset];
LittleEndian::write_u32(mem, dynamic_base(STATIC_BUMP));
}
@@ -74,23 +82,7 @@ macro_rules! mock_external {
pub fn generate_emscripten_env<'a, 'b>() -> ImportObject<&'a str, &'b str> {
let mut import_object = ImportObject::new();
// Global
import_object.set(
"env",
"global1",
ImportValue::Global(24), // TODO
);
import_object.set(
"env",
"global2",
ImportValue::Global(50), // TODO
);
import_object.set(
"env",
"global3",
ImportValue::Global(67), // TODO
);
// Globals
import_object.set(
"env",
"STACKTOP",
@@ -107,7 +99,6 @@ pub fn generate_emscripten_env<'a, 'b>() -> ImportObject<&'a str, &'b str> {
ImportValue::Global(dynamictop_ptr(STATIC_BUMP) as _),
);
import_object.set("env", "tableBase", ImportValue::Global(0));
// Print functions
import_object.set("env", "printf", ImportValue::Func(io::printf as _));
import_object.set("env", "putchar", ImportValue::Func(io::putchar as _));

View File

@@ -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 _) }
}

View File

@@ -2,9 +2,10 @@ 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;
use std::mem::size_of;
/// We check if a provided module is an Emscripten generated one
pub fn is_emscripten_module(module: &Module) -> bool {
@@ -18,16 +19,39 @@ pub fn is_emscripten_module(module: &Module) -> bool {
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 space_offset = (instance.emscripten_data.as_ref().unwrap().malloc)(s.len() as _, instance);
let cstr_len = s.len();
let space_offset = (instance.emscripten_data.as_ref().unwrap().malloc)((cstr_len as i32) + 1, instance);
let raw_memory = instance.memory_offset_addr(0, space_offset as _) as *mut u8;
let slice = slice::from_raw_parts_mut(raw_memory, s.len());
let slice = slice::from_raw_parts_mut(raw_memory, cstr_len);
for (byte, loc) in s.bytes().zip(slice.iter_mut()) {
*loc = byte;
}
*raw_memory.add(cstr_len) = 0;
space_offset
}
pub unsafe fn allocate_on_stack<'a, T: Copy>(count: u32, instance: &'a Instance) -> (u32, &'a mut [T]) {
let offset = (instance.emscripten_data.as_ref().unwrap().stack_alloc)(count * (size_of::<T>() as u32), instance);
let addr = instance.memory_offset_addr(0, offset as _) as *mut T;
let slice = slice::from_raw_parts_mut(addr, count as usize);
(offset, slice)
}
pub unsafe fn allocate_cstr_on_stack<'a>(s: &str, instance: &'a Instance) -> (u32, &'a [u8]) {
let (offset, slice) = allocate_on_stack((s.len() + 1) as u32, instance);
use std::iter;
for (byte, loc) in s.bytes().chain(iter::once(0)).zip(slice.iter_mut()) {
*loc = byte;
}
(offset, slice)
}
pub unsafe fn copy_terminated_array_of_cstrs(
_instance: &mut Instance,
cstrs: *mut *mut c_char,

View File

@@ -7,6 +7,7 @@ use std::io::Read;
use std::path::PathBuf;
use std::process::exit;
use apis::emscripten::{allocate_on_stack, allocate_cstr_on_stack};
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<String>,
}
/// Read the contents of a file
fn read_file_contents(path: &PathBuf) -> Result<Vec<u8>, io::Error> {
let mut buffer: Vec<u8> = Vec::new();
@@ -42,26 +49,31 @@ fn read_file_contents(path: &PathBuf) -> Result<Vec<u8>, io::Error> {
}
/// Execute a WASM/WAT file
fn execute_wasm(wasm_path: PathBuf) -> Result<(), String> {
let mut wasm_binary: Vec<u8> = 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<u8> = 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();
let webassembly::ResultObject { module, instance } =
let webassembly::ResultObject { module, mut instance } =
webassembly::instantiate(wasm_binary, import_object)
.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,19 @@ 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));
let (argc, argv) = store_module_arguments(options, &mut instance);
return call_protected!(main(argc, argv, &instance)).map_err(|err| format!("{}", err));
// TODO: We should implement emscripten __ATEXIT__
} else {
let func_index =
@@ -93,7 +110,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 +127,54 @@ fn main() {
CLIOptions::SelfUpdate => update::self_update(),
}
}
fn store_module_arguments(options: &Run, instance: &mut webassembly::Instance) -> (u32, u32) {
let argc = options.args.len() + 1;
let (argv_offset, argv_slice): (_, &mut [u32]) = unsafe { allocate_on_stack(((argc + 1) * 4) as u32, instance) };
assert!(argv_slice.len() >= 1);
argv_slice[0] = unsafe { allocate_cstr_on_stack(options.path.to_str().unwrap(), instance).0 };
for (slot, arg) in argv_slice[1..argc].iter_mut().zip(options.args.iter()) {
*slot = unsafe { allocate_cstr_on_stack(&arg, instance).0 };
}
argv_slice[argc] = 0;
(argc as u32, argv_offset)
}
// fn get_module_arguments(options: &Run, instance: &mut webassembly::Instance) -> (u32, u32) {
// // 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);
// (argc, argv_offset)
// }

View File

@@ -70,10 +70,11 @@ 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,
pub stack_alloc: extern "C" fn (u32, &Instance) -> u32,
}
impl fmt::Debug for EmscriptenData {
@@ -265,7 +266,7 @@ impl Instance {
func
// unimplemented!()
}).collect();
if let Some(ref progress_bar) = progress_bar_option {
progress_bar.set_style(ProgressStyle::default_bar()
.template(&format!("{} {{msg}}", style("[{elapsed_precise}]").bold().dim())));
@@ -550,21 +551,44 @@ impl Instance {
let free_export = module.info.exports.get("_free");
let memalign_export = module.info.exports.get("_memalign");
let memset_export = module.info.exports.get("_memset");
let stack_alloc_export = module.info.exports.get("stackAlloc");
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;
let mut stack_alloc_addr = 0 as _;
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);
}
if let Some(Export::Function(stack_alloc_index)) = stack_alloc_export {
stack_alloc_addr = get_function_addr(&stack_alloc_index, &import_functions, &functions);
}
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);
Some(EmscriptenData {
malloc: mem::transmute(malloc_addr),
free: mem::transmute(free_addr),
memalign: mem::transmute(memalign_addr),
memset: mem::transmute(memset_addr),
stack_alloc: mem::transmute(stack_alloc_addr),
})
} else {
None
}
}
} else {

View File

@@ -9,7 +9,7 @@ use cranelift_codegen::cursor::FuncCursor;
use cranelift_codegen::ir::immediates::{Imm64, Offset32};
use cranelift_codegen::ir::types::*;
use cranelift_codegen::ir::{
self, AbiParam, ArgumentPurpose, ExtFuncData, ExternalName, FuncRef, InstBuilder, Signature,
self, AbiParam, ArgumentPurpose, ExtFuncData, ExternalName, FuncRef, InstBuilder, Signature, TrapCode,
};
use cranelift_codegen::isa::{CallConv, TargetFrontendConfig};
use cranelift_entity::{EntityRef, PrimaryMap};
@@ -532,6 +532,8 @@ impl<'environment> FuncEnvironmentTrait for FuncEnvironment<'environment> {
mflags.set_aligned();
let func_ptr = pos.ins().load(ptr, mflags, entry_addr, 0);
pos.ins().trapz(func_ptr, TrapCode::IndirectCallToNull);
// Build a value list for the indirect call instruction containing the callee, call_args,
// and the vmctx parameter.
let mut args = ir::ValueList::default();