From c3a3ad83fa466d007848924a17713f55d46b0a4a Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Tue, 28 Nov 2017 16:54:51 +0300 Subject: [PATCH] Some changes --- pwasm-emscripten/.gitignore | 1 + pwasm-emscripten/Cargo.toml | 7 + pwasm-emscripten/src/lib.rs | 258 ++++++++++++++++++++++++++++++++++++ src/interpreter/program.rs | 29 ++-- 4 files changed, 275 insertions(+), 20 deletions(-) create mode 100644 pwasm-emscripten/.gitignore create mode 100644 pwasm-emscripten/Cargo.toml create mode 100644 pwasm-emscripten/src/lib.rs diff --git a/pwasm-emscripten/.gitignore b/pwasm-emscripten/.gitignore new file mode 100644 index 0000000..eb5a316 --- /dev/null +++ b/pwasm-emscripten/.gitignore @@ -0,0 +1 @@ +target diff --git a/pwasm-emscripten/Cargo.toml b/pwasm-emscripten/Cargo.toml new file mode 100644 index 0000000..01d0e95 --- /dev/null +++ b/pwasm-emscripten/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "pwasm-emscripten" +version = "0.1.0" +authors = ["Sergey Pepyakin "] + +[dependencies] +parity-wasm = { path = ".." } diff --git a/pwasm-emscripten/src/lib.rs b/pwasm-emscripten/src/lib.rs new file mode 100644 index 0000000..3bb0b8b --- /dev/null +++ b/pwasm-emscripten/src/lib.rs @@ -0,0 +1,258 @@ +extern crate parity_wasm; + +//This module provides some of the simplest exports +//from the Emscripten runtime, such as `STACKTOP` or `abort`. + +use std::sync::{Arc, Weak}; +use parity_wasm::builder::module; +use parity_wasm::elements::{ExportEntry, Internal, ValueType}; +use parity_wasm::interpreter::Error; +use parity_wasm::interpreter::{native_module, UserDefinedElements, UserFunctionDescriptor, UserFunctionExecutor}; +use parity_wasm::interpreter::{CallerContext, ModuleInstance, ModuleInstanceInterface}; +use parity_wasm::interpreter::RuntimeValue; +use parity_wasm::interpreter::ProgramInstance; +use parity_wasm::interpreter::{VariableInstance, VariableType}; + +/// Memory address, at which stack begins. +const DEFAULT_STACK_TOP: u32 = 256 * 1024; +/// Memory, allocated for stack. +const DEFAULT_TOTAL_STACK: u32 = 5 * 1024 * 1024; +/// Total memory, allocated by default. +const DEFAULT_TOTAL_MEMORY: u32 = 16 * 1024 * 1024; +/// Whether memory can be enlarged, or not. +const DEFAULT_ALLOW_MEMORY_GROWTH: bool = true; +/// Default tableBase variable value. +const DEFAULT_TABLE_BASE: u32 = 0; +/// Default tableBase variable value. +const DEFAULT_MEMORY_BASE: u32 = 0; + +/// Default table size. +const DEFAULT_TABLE_SIZE: u32 = 64; + +/// Index of default memory. +const INDEX_MEMORY: u32 = 0; + +/// Index of default table. +const INDEX_TABLE: u32 = 0; + +const LINEAR_MEMORY_PAGE_SIZE: u32 = 64 * 1024; + +/// Emscripten environment parameters. +pub struct EnvParams { + /// Stack size in bytes. + pub total_stack: u32, + /// Total memory size in bytes. + pub total_memory: u32, + /// Allow memory growth. + pub allow_memory_growth: bool, + /// Table size. + pub table_size: u32, + /// Static reserve, if any + pub static_size: Option, +} + +struct EmscriptenFunctionExecutor { + abort_global: Arc, + total_mem_global: Arc, +} + +impl<'a> UserFunctionExecutor for EmscriptenFunctionExecutor { + fn execute( + &mut self, + name: &str, + context: CallerContext, + ) -> Result, Error> { + match name { + "_abort" | "abort" => { + self.abort_global.set(RuntimeValue::I32(1))?; + Err(Error::Trap("abort".into()).into()) + } + "assert" => { + let condition = context.value_stack.pop_as::()?; + if condition == 0 { + self.abort_global.set(RuntimeValue::I32(1))?; + Err(Error::Trap("assertion failed".into())) + } else { + Ok(None) + } + } + "enlargeMemory" => { + // TODO: support memory enlarge + Ok(Some(RuntimeValue::I32(0))) + } + "getTotalMemory" => { + let total_memory = self.total_mem_global.get(); + Ok(Some(total_memory)) + } + _ => Err(Error::Trap("not implemented".into()).into()), + } + } +} + +pub fn env_module(params: EnvParams) -> Result, Error> { + debug_assert!(params.total_stack < params.total_memory); + debug_assert!((params.total_stack % LINEAR_MEMORY_PAGE_SIZE) == 0); + debug_assert!((params.total_memory % LINEAR_MEMORY_PAGE_SIZE) == 0); + + let stack_top = params.static_size.unwrap_or(DEFAULT_STACK_TOP); + + // Build module with defined memory and table, + // instantiate it and wrap into an Arc. + let instance = { + let builder = module() + // memory regions + .memory() + .with_min(params.total_memory / LINEAR_MEMORY_PAGE_SIZE) + .with_max(params.max_memory().map(|m| m / LINEAR_MEMORY_PAGE_SIZE)) + .build() + .with_export(ExportEntry::new("memory".into(), Internal::Memory(INDEX_MEMORY))) + // tables + .table() + .with_min(params.table_size) + .build() + .with_export(ExportEntry::new("table".into(), Internal::Table(INDEX_TABLE))); + let mut instance = ModuleInstance::new(Weak::default(), "env".into(), builder.build())?; + instance.instantiate(None)?; + Arc::new(instance) + }; + + let abort_global = Arc::new( + VariableInstance::new( + false, + VariableType::I32, + RuntimeValue::I32(0) + ).unwrap() + ); + let total_mem_global = Arc::new( + VariableInstance::new( + false, + VariableType::I32, + RuntimeValue::I32(params.total_memory as i32), + ).unwrap(), + ); + + let function_executor = EmscriptenFunctionExecutor { + abort_global: Arc::clone(&abort_global), + total_mem_global: Arc::clone(&total_mem_global), + }; + + const SIGNATURES: &'static [UserFunctionDescriptor] = &[ + UserFunctionDescriptor::Static("_abort", &[], None), + UserFunctionDescriptor::Static("abort", &[], None), + UserFunctionDescriptor::Static("assert", &[ValueType::I32], None), + UserFunctionDescriptor::Static("enlargeMemory", &[], Some(ValueType::I32)), + UserFunctionDescriptor::Static("getTotalMemory", &[], Some(ValueType::I32)), + ]; + + let elements = UserDefinedElements { + executor: Some(function_executor), + globals: vec![ + ( + "STACK_BASE".into(), + Arc::new( + VariableInstance::new( + false, + VariableType::I32, + RuntimeValue::I32(stack_top as i32), + ).unwrap(), + ), + ), + ( + "STACKTOP".into(), + Arc::new( + VariableInstance::new( + false, + VariableType::I32, + RuntimeValue::I32(stack_top as i32), + ).unwrap(), + ), + ), + ( + "STACK_MAX".into(), + Arc::new( + VariableInstance::new( + false, + VariableType::I32, + RuntimeValue::I32((stack_top + params.total_stack) as i32), + ).unwrap(), + ), + ), + ( + "DYNAMIC_BASE".into(), + Arc::new( + VariableInstance::new( + false, + VariableType::I32, + RuntimeValue::I32((stack_top + params.total_stack) as i32), + ).unwrap(), + ), + ), + ( + "DYNAMICTOP_PTR".into(), + Arc::new( + VariableInstance::new( + false, + VariableType::I32, + RuntimeValue::I32((stack_top + params.total_stack) as i32), + ).unwrap(), + ), + ), + ( + "EXITSTATUS".into(), + Arc::new( + VariableInstance::new(false, VariableType::I32, RuntimeValue::I32(0)).unwrap(), + ), + ), + ( + "tableBase".into(), + Arc::new( + VariableInstance::new( + false, + VariableType::I32, + RuntimeValue::I32(DEFAULT_TABLE_BASE as i32), + ).unwrap(), + ), + ), + ( + "memoryBase".into(), + Arc::new( + VariableInstance::new( + false, + VariableType::I32, + RuntimeValue::I32(DEFAULT_MEMORY_BASE as i32), + ).unwrap(), + ), + ), + ("TOTAL_MEMORY".into(), total_mem_global), + ("ABORT".into(), abort_global), + ].into_iter() + .collect(), + functions: ::std::borrow::Cow::from(SIGNATURES), + }; + + Ok(native_module(instance, elements)?) +} + +impl Default for EnvParams { + fn default() -> Self { + EnvParams { + total_stack: DEFAULT_TOTAL_STACK, + total_memory: DEFAULT_TOTAL_MEMORY, + allow_memory_growth: DEFAULT_ALLOW_MEMORY_GROWTH, + table_size: DEFAULT_TABLE_SIZE, + static_size: None, + } + } +} + +impl EnvParams { + fn max_memory(&self) -> Option { + if self.allow_memory_growth { None } else { Some(self.total_memory) } + } +} + +pub fn program_with_emscripten(params: EnvParams) -> ProgramInstance { + let program = ProgramInstance::new(); + program.insert_loaded_module("env", env_module(params).unwrap()).unwrap(); + program +} diff --git a/src/interpreter/program.rs b/src/interpreter/program.rs index 0173b99..7615fc8 100644 --- a/src/interpreter/program.rs +++ b/src/interpreter/program.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use parking_lot::RwLock; use elements::Module; use interpreter::Error; -use interpreter::emscripten::{EmscriptenParams, env_module}; +use interpreter::emscripten::{self, env_module}; use interpreter::module::{ModuleInstance, ModuleInstanceInterface}; /// Program instance. Program is a set of instantiated modules. @@ -29,10 +29,12 @@ impl ProgramInstance { /// Create new program instance with added Emscripten's `env` module. /// /// You can specify desired environment params. Or you can just pass `Default::default()`. - pub fn with_emscripten_env(params: EmscriptenParams) -> Result { - Ok(ProgramInstance { - essence: Arc::new(ProgramInstanceEssence::with_env_params(params)?), - }) + pub fn with_emscripten_env(params: emscripten::EnvParams) -> Result { + let instance = ProgramInstance { + essence: Arc::new(ProgramInstanceEssence::new()), + }; + // instance.essence.modules.write().insert("env".into(), env_module(params)?); + Ok(instance) } /// Instantiate module with validation. @@ -47,9 +49,9 @@ impl ProgramInstance { } /// Insert instantiated module. - pub fn insert_loaded_module(&self, name: &str, module_instance: Arc) -> Result, Error> { + pub fn insert_loaded_module(&self, name: &str, module_instance: Arc) -> Result, Error> { // replace existing module with the same name with new one - self.essence.modules.write().insert(name.into(), module_instance.clone()); + self.essence.modules.write().insert(name.into(), Arc::clone(&module_instance)); Ok(module_instance) } @@ -67,19 +69,6 @@ impl ProgramInstanceEssence { } } - pub fn with_env_params(env_params: EmscriptenParams) -> Result { - let env_mod = env_module(env_params)?; - Ok(ProgramInstanceEssence::with_env_module(env_mod)) - } - - pub fn with_env_module(env_module: Arc) -> Self { - let mut modules = HashMap::new(); - modules.insert("env".into(), env_module); - ProgramInstanceEssence { - modules: RwLock::new(modules), - } - } - /// Get module reference. pub fn module(&self, name: &str) -> Option> { self.modules.read().get(name).cloned()