mirror of
https://github.com/fluencelabs/wasmer
synced 2025-07-30 23:02:06 +00:00
Merge pull request #37 from wasmerio/feature/pass-arguments-to-wasm-application
Pass arguments to wasm application
This commit is contained in:
@@ -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 _));
|
||||
|
@@ -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 _) }
|
||||
}
|
||||
|
||||
|
@@ -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,
|
||||
|
@@ -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)
|
||||
// }
|
||||
|
@@ -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 {
|
||||
|
@@ -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();
|
||||
|
Reference in New Issue
Block a user