Skip to content

Commit

Permalink
Merge pull request wasmerio#37 from wasmerio/feature/pass-arguments-t…
Browse files Browse the repository at this point in the history
…o-wasm-application

Pass arguments to wasm application
  • Loading branch information
syrusakbary authored Dec 7, 2018
2 parents fbc2fc9 + a29b480 commit 3c730d4
Show file tree
Hide file tree
Showing 6 changed files with 154 additions and 45 deletions.
37 changes: 14 additions & 23 deletions src/apis/emscripten/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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));
}
Expand All @@ -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",
Expand All @@ -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 _));
Expand Down
6 changes: 3 additions & 3 deletions src/apis/emscripten/syscalls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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,
Expand All @@ -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 _) }
}

Expand Down
30 changes: 27 additions & 3 deletions src/apis/emscripten/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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,
Expand Down
78 changes: 73 additions & 5 deletions src/bin/wasmer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;
Expand All @@ -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();
Expand All @@ -42,41 +49,51 @@ 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");
let ___emscripten_environ_constructor: extern "C" fn(&webassembly::Instance) =
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 =
Expand All @@ -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();
Expand All @@ -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)
// }
44 changes: 34 additions & 10 deletions src/webassembly/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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())));
Expand Down Expand Up @@ -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 {
Expand Down
4 changes: 3 additions & 1 deletion src/webassembly/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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();
Expand Down

0 comments on commit 3c730d4

Please sign in to comment.