mirror of
https://github.com/pezkuwichain/wasm-instrument.git
synced 2026-06-11 20:01:05 +00:00
Merge branch 'master' of github.com:NikVolf/wasm-tools
This commit is contained in:
+11
-11
@@ -5,18 +5,18 @@ use std::env;
|
||||
|
||||
fn main() {
|
||||
|
||||
wasm_utils::init_log();
|
||||
wasm_utils::init_log();
|
||||
|
||||
let args = env::args().collect::<Vec<_>>();
|
||||
if args.len() != 3 {
|
||||
println!("Usage: {} input_file.wasm output_file.wasm", args[0]);
|
||||
return;
|
||||
}
|
||||
let args = env::args().collect::<Vec<_>>();
|
||||
if args.len() != 3 {
|
||||
println!("Usage: {} input_file.wasm output_file.wasm", args[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
let module = wasm_utils::externalize(
|
||||
parity_wasm::deserialize_file(&args[1]).expect("Module to deserialize ok"),
|
||||
vec!["_free", "_malloc"],
|
||||
);
|
||||
let module = wasm_utils::externalize(
|
||||
parity_wasm::deserialize_file(&args[1]).expect("Module to deserialize ok"),
|
||||
vec!["_free", "_malloc"],
|
||||
);
|
||||
|
||||
parity_wasm::serialize_to_file(&args[2], module).expect("Module to serialize ok");
|
||||
parity_wasm::serialize_to_file(&args[2], module).expect("Module to serialize ok");
|
||||
}
|
||||
|
||||
+10
-10
@@ -5,18 +5,18 @@ use std::env;
|
||||
|
||||
fn main() {
|
||||
|
||||
wasm_utils::init_log();
|
||||
wasm_utils::init_log();
|
||||
|
||||
let args = env::args().collect::<Vec<_>>();
|
||||
if args.len() != 3 {
|
||||
println!("Usage: {} input_file.wasm output_file.wasm", args[0]);
|
||||
return;
|
||||
}
|
||||
let args = env::args().collect::<Vec<_>>();
|
||||
if args.len() != 3 {
|
||||
println!("Usage: {} input_file.wasm output_file.wasm", args[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
// Loading module
|
||||
let module = parity_wasm::deserialize_file(&args[1]).expect("Module deserialization to succeed");
|
||||
// Loading module
|
||||
let module = parity_wasm::deserialize_file(&args[1]).expect("Module deserialization to succeed");
|
||||
|
||||
let result = wasm_utils::inject_gas_counter(module);
|
||||
let result = wasm_utils::inject_gas_counter(module);
|
||||
|
||||
parity_wasm::serialize_to_file(&args[2], result).expect("Module serialization to succeed")
|
||||
parity_wasm::serialize_to_file(&args[2], result).expect("Module serialization to succeed")
|
||||
}
|
||||
|
||||
+13
-13
@@ -2,26 +2,26 @@ use parity_wasm::interpreter::{self, ModuleInstance};
|
||||
use runtime::Runtime;
|
||||
|
||||
pub struct Arena {
|
||||
pub runtime: Runtime,
|
||||
pub runtime: Runtime,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Error;
|
||||
|
||||
impl Arena {
|
||||
pub fn alloc(&self, size: u32) -> Result<u32, Error> {
|
||||
// todo: maybe use unsafe cell since it has nothing to do with threads
|
||||
let previous_top = self.runtime.env().dynamic_top.get();
|
||||
self.runtime.env().dynamic_top.set(previous_top + size);
|
||||
Ok(previous_top)
|
||||
}
|
||||
pub fn alloc(&self, size: u32) -> Result<u32, Error> {
|
||||
// todo: maybe use unsafe cell since it has nothing to do with threads
|
||||
let previous_top = self.runtime.env().dynamic_top.get();
|
||||
self.runtime.env().dynamic_top.set(previous_top + size);
|
||||
Ok(previous_top)
|
||||
}
|
||||
}
|
||||
|
||||
impl interpreter::UserFunctionInterface for Arena {
|
||||
fn call(&mut self, _module: &ModuleInstance, context: interpreter::CallerContext) -> Result<Option<interpreter::RuntimeValue>, interpreter::Error> {
|
||||
let amount = context.value_stack.pop_as::<i32>()?;
|
||||
self.alloc(amount as u32)
|
||||
.map(|val| Some((val as i32).into()))
|
||||
.map_err(|e| interpreter::Error::Trap(format!("Allocator failure: {}", "todo: format arg")))
|
||||
}
|
||||
fn call(&mut self, _module: &ModuleInstance, context: interpreter::CallerContext) -> Result<Option<interpreter::RuntimeValue>, interpreter::Error> {
|
||||
let amount = context.value_stack.pop_as::<i32>()?;
|
||||
self.alloc(amount as u32)
|
||||
.map(|val| Some((val as i32).into()))
|
||||
.map_err(|e| interpreter::Error::Trap(format!("Allocator failure: {}", "todo: format arg")))
|
||||
}
|
||||
}
|
||||
@@ -1,82 +1,69 @@
|
||||
use parity_wasm::interpreter::{self, ModuleInstanceInterface};
|
||||
use {alloc, runtime};
|
||||
use parity_wasm::interpreter;
|
||||
use runtime;
|
||||
|
||||
use {DEFAULT_MEMORY_INDEX, WasmMemoryPtr};
|
||||
use WasmMemoryPtr;
|
||||
|
||||
fn write_u32(dst: &mut [u8], val: u32) {
|
||||
dst[0] = (val & 0x000000ff) as u8;
|
||||
dst[1] = ((val & 0x0000ff00) >> 8) as u8;
|
||||
dst[2] = ((val & 0x00ff0000) >> 16) as u8;
|
||||
dst[3] = ((val & 0xff000000) >> 24) as u8;
|
||||
dst[0] = (val & 0x000000ff) as u8;
|
||||
dst[1] = ((val & 0x0000ff00) >> 8) as u8;
|
||||
dst[2] = ((val & 0x00ff0000) >> 16) as u8;
|
||||
dst[3] = ((val & 0xff000000) >> 24) as u8;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
Allocator(alloc::Error),
|
||||
Interpreter(interpreter::Error),
|
||||
Allocator(runtime::ErrorAlloc),
|
||||
Interpreter(interpreter::Error),
|
||||
}
|
||||
|
||||
impl From<alloc::Error> for Error {
|
||||
fn from(err: alloc::Error) -> Self {
|
||||
Error::Allocator(err)
|
||||
}
|
||||
impl From<runtime::ErrorAlloc> for Error {
|
||||
fn from(err: runtime::ErrorAlloc) -> Self {
|
||||
Error::Allocator(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<interpreter::Error> for Error {
|
||||
fn from(err: interpreter::Error) -> Self {
|
||||
Error::Interpreter(err)
|
||||
}
|
||||
fn from(err: interpreter::Error) -> Self {
|
||||
Error::Interpreter(err)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(
|
||||
env: &interpreter::ModuleInstanceInterface,
|
||||
runtime: &runtime::Runtime,
|
||||
input: &[u8],
|
||||
memory: &interpreter::MemoryInstance,
|
||||
runtime: &mut runtime::Runtime,
|
||||
input: &[u8],
|
||||
) -> Result<WasmMemoryPtr, Error> {
|
||||
let mut input_ptr_slc = [0u8; 4];
|
||||
let mut input_length = [0u8; 4];
|
||||
let mut input_ptr_slc = [0u8; 4];
|
||||
let mut input_length = [0u8; 4];
|
||||
|
||||
let allocator = runtime.allocator();
|
||||
let descriptor_ptr = runtime.alloc(16)?;
|
||||
|
||||
let descriptor_ptr = allocator.alloc(16)?;
|
||||
println!("descriptor_ptr: {}", descriptor_ptr);
|
||||
|
||||
println!("descriptor_ptr: {}", descriptor_ptr);
|
||||
let memory = env.memory(DEFAULT_MEMORY_INDEX)?;
|
||||
if input.len() > 0 {
|
||||
let input_ptr = runtime.alloc(input.len() as u32)?;
|
||||
write_u32(&mut input_ptr_slc, input_ptr);
|
||||
write_u32(&mut input_length, input.len() as u32);
|
||||
memory.set(input_ptr, input)?;
|
||||
println!("input_ptr: {}", input_ptr);
|
||||
} else {
|
||||
write_u32(&mut input_ptr_slc, 0);
|
||||
write_u32(&mut input_length, 0);
|
||||
}
|
||||
|
||||
if input.len() > 0 {
|
||||
let input_ptr = allocator.alloc(input.len() as u32)?;
|
||||
write_u32(&mut input_ptr_slc, input_ptr);
|
||||
write_u32(&mut input_length, input.len() as u32);
|
||||
memory.set(input_ptr, input)?;
|
||||
println!("input_ptr: {}", input_ptr);
|
||||
} else {
|
||||
write_u32(&mut input_ptr_slc, 0);
|
||||
write_u32(&mut input_length, 0);
|
||||
}
|
||||
memory.set(descriptor_ptr, &input_ptr_slc)?;
|
||||
memory.set(descriptor_ptr+4, &input_length)?;
|
||||
|
||||
memory.set(descriptor_ptr, &input_ptr_slc)?;
|
||||
memory.set(descriptor_ptr+4, &input_length)?;
|
||||
// zero result ptr/len
|
||||
memory.set(descriptor_ptr+8, &[0u8; 4])?;
|
||||
memory.set(descriptor_ptr+12, &[0u8; 4])?;
|
||||
|
||||
// zero result ptr/len
|
||||
memory.set(descriptor_ptr+8, &[0u8; 4])?;
|
||||
memory.set(descriptor_ptr+12, &[0u8; 4])?;
|
||||
println!("descriptor: {:?}", memory.get(descriptor_ptr, 16));
|
||||
|
||||
println!("descriptor: {:?}", memory.get(descriptor_ptr, 16));
|
||||
|
||||
Ok(descriptor_ptr as i32)
|
||||
Ok(descriptor_ptr as i32)
|
||||
}
|
||||
|
||||
fn read_u32(slc: &[u8]) -> u32 {
|
||||
use std::ops::Shl;
|
||||
(slc[0] as u32) + (slc[1] as u32).shl(8) + (slc[2] as u32).shl(16) + (slc[3] as u32).shl(24)
|
||||
}
|
||||
|
||||
// pub fn retrieve(env:
|
||||
// env: &interpreter::ModuleInstanceInterface,
|
||||
// runtime: &runtime::Runtime,
|
||||
// descriptor_ptr: u32,
|
||||
// result_ptr: u32,
|
||||
// ) -> Result<Vec<u8>, Error> {
|
||||
// let memory = env.memory(DEFAULT_MEMORY_INDEX)?;
|
||||
|
||||
// }
|
||||
fn _read_u32(slc: &[u8]) -> u32 {
|
||||
use std::ops::Shl;
|
||||
(slc[0] as u32) + (slc[1] as u32).shl(8) + (slc[2] as u32).shl(16) + (slc[3] as u32).shl(24)
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
use parity_wasm::interpreter::{self, ModuleInstance};
|
||||
use runtime::Runtime;
|
||||
|
||||
pub struct GasCounter {
|
||||
pub runtime: Runtime,
|
||||
}
|
||||
|
||||
impl interpreter::UserFunctionInterface for GasCounter {
|
||||
fn call(&mut self, _module: &ModuleInstance, context: interpreter::CallerContext) -> Result<Option<interpreter::RuntimeValue>, interpreter::Error> {
|
||||
let prev = self.runtime.env().gas_counter.get();
|
||||
let update = context.value_stack.pop_as::<i32>()? as u64;
|
||||
if prev + update > self.runtime.env().gas_limit {
|
||||
// exceeds gas
|
||||
Err(interpreter::Error::Trap(format!("Gas exceeds limits of {}", self.runtime.env().gas_limit)))
|
||||
} else {
|
||||
self.runtime.env().gas_counter.set(prev + update);
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
+70
-64
@@ -1,19 +1,17 @@
|
||||
/*
|
||||
|
||||
Rust contract demo runner
|
||||
Rust contract demo runner
|
||||
|
||||
*/
|
||||
|
||||
extern crate parity_wasm;
|
||||
extern crate wasm_utils;
|
||||
|
||||
mod alloc;
|
||||
mod storage;
|
||||
mod call_args;
|
||||
mod runtime;
|
||||
mod gas_counter;
|
||||
|
||||
use std::env;
|
||||
use std::sync::Arc;
|
||||
use parity_wasm::interpreter::{self, ModuleInstanceInterface};
|
||||
use parity_wasm::elements;
|
||||
|
||||
@@ -21,73 +19,81 @@ pub const DEFAULT_MEMORY_INDEX: interpreter::ItemIndex = interpreter::ItemIndex:
|
||||
pub type WasmMemoryPtr = i32;
|
||||
|
||||
fn main() {
|
||||
// First, load wasm contract as a module
|
||||
wasm_utils::init_log();
|
||||
// First, load wasm contract as a module
|
||||
wasm_utils::init_log();
|
||||
|
||||
let args = env::args().collect::<Vec<_>>();
|
||||
if args.len() != 2 {
|
||||
println!("Usage: {} contract.wasm", args[0]);
|
||||
return;
|
||||
}
|
||||
let args = env::args().collect::<Vec<_>>();
|
||||
if args.len() != 2 {
|
||||
println!("Usage: {} contract.wasm", args[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
let module = parity_wasm::deserialize_file(&args[1]).expect("Module deserialization to succeed");
|
||||
let module = parity_wasm::deserialize_file(&args[1]).expect("Module deserialization to succeed");
|
||||
|
||||
// Second, create runtime and program instance
|
||||
let runtime = runtime::Runtime::with_params(
|
||||
5*1024*1024, // default stack space
|
||||
65536, // runner arbitrary gas limit
|
||||
);
|
||||
let program = parity_wasm::interpreter::ProgramInstance::new()
|
||||
.expect("Program instance to be created");
|
||||
|
||||
let mut user_functions = interpreter::UserFunctions::new();
|
||||
user_functions.insert("gas".to_owned(),
|
||||
interpreter::UserFunction {
|
||||
params: vec![elements::ValueType::I32],
|
||||
result: None,
|
||||
closure: Box::new(runtime.gas_counter()),
|
||||
}
|
||||
);
|
||||
user_functions.insert("_malloc".to_owned(),
|
||||
interpreter::UserFunction {
|
||||
params: vec![elements::ValueType::I32],
|
||||
result: Some(elements::ValueType::I32),
|
||||
closure: Box::new(runtime.allocator()),
|
||||
}
|
||||
);
|
||||
user_functions.insert("_storage_read".to_owned(),
|
||||
interpreter::UserFunction {
|
||||
params: vec![elements::ValueType::I32, elements::ValueType::I32],
|
||||
result: Some(elements::ValueType::I32),
|
||||
closure: Box::new(runtime.storage().reader()),
|
||||
}
|
||||
);
|
||||
user_functions.insert("_storage_write".to_owned(),
|
||||
interpreter::UserFunction {
|
||||
params: vec![elements::ValueType::I32, elements::ValueType::I32],
|
||||
result: Some(elements::ValueType::I32),
|
||||
closure: Box::new(runtime.storage().writer()),
|
||||
}
|
||||
);
|
||||
runtime::user_trap(&mut user_functions, "_emscripten_memcpy_big");
|
||||
runtime::user_trap(&mut user_functions, "invoke_vii");
|
||||
runtime::user_noop(&mut user_functions, "_free");
|
||||
// Add module to the programm
|
||||
let module_instance = program.add_module("contract", module).expect("Module to be added successfully");
|
||||
|
||||
let program = parity_wasm::interpreter::ProgramInstance::with_functions(user_functions)
|
||||
.expect("Program instance to be created");
|
||||
{
|
||||
let env_instance = program.module("env").expect("env module to exist");
|
||||
let env_memory = env_instance.memory(interpreter::ItemIndex::Internal(0))
|
||||
.expect("liner memory to exist");
|
||||
|
||||
// Add module to the programm
|
||||
let module_instance = program.add_module("contract", module).expect("Module to be added successfully");
|
||||
// Second, create runtime and program instance
|
||||
let mut runtime = runtime::Runtime::with_params(
|
||||
env_memory.clone(), // memory shared ptr
|
||||
5*1024*1024, // default stack space
|
||||
65536, // runner arbitrary gas limit
|
||||
);
|
||||
|
||||
// Initialize call descriptor
|
||||
let descriptor = call_args::init(
|
||||
&*program.module("env").expect("env module to exist"),
|
||||
&runtime,
|
||||
&[3u8; 128],
|
||||
).expect("call descriptor initialization to succeed");
|
||||
// Initialize call descriptor
|
||||
let descriptor = call_args::init(
|
||||
&*env_memory,
|
||||
&mut runtime,
|
||||
&[3u8; 128],
|
||||
).expect("call descriptor initialization to succeed");
|
||||
|
||||
// Invoke _call method of the module
|
||||
let return_ptr = module_instance.execute_export("_call", vec![descriptor.into()])
|
||||
.expect("_call to execute successfully")
|
||||
.expect("_call function to return result ptr");
|
||||
// create native env module with native add && sub implementations
|
||||
let functions = interpreter::UserFunctions {
|
||||
executor: &mut runtime,
|
||||
functions: vec![
|
||||
interpreter::UserFunction {
|
||||
name: "_storage_read".to_owned(),
|
||||
params: vec![elements::ValueType::I32, elements::ValueType::I32],
|
||||
result: Some(elements::ValueType::I32),
|
||||
},
|
||||
interpreter::UserFunction {
|
||||
name: "_storage_write".to_owned(),
|
||||
params: vec![elements::ValueType::I32, elements::ValueType::I32],
|
||||
result: Some(elements::ValueType::I32),
|
||||
},
|
||||
interpreter::UserFunction {
|
||||
name: "_malloc".to_owned(),
|
||||
params: vec![elements::ValueType::I32],
|
||||
result: Some(elements::ValueType::I32),
|
||||
},
|
||||
interpreter::UserFunction {
|
||||
name: "gas".to_owned(),
|
||||
params: vec![elements::ValueType::I32],
|
||||
result: None,
|
||||
},
|
||||
interpreter::UserFunction {
|
||||
name: "_free".to_owned(),
|
||||
params: vec![elements::ValueType::I32],
|
||||
result: None,
|
||||
},
|
||||
],
|
||||
};
|
||||
let native_env_instance = Arc::new(interpreter::env_native_module(env_instance, functions).unwrap());
|
||||
|
||||
// ???
|
||||
// Form ExecutionParams (payload + env link)
|
||||
let params = interpreter::ExecutionParams::with_external("env".into(), native_env_instance)
|
||||
.add_argument(interpreter::RuntimeValue::I32(descriptor));
|
||||
|
||||
module_instance.execute_export("_call", params)
|
||||
.expect("_call to execute successfully")
|
||||
.expect("_call function to return result ptr");
|
||||
}
|
||||
}
|
||||
+150
-77
@@ -1,92 +1,165 @@
|
||||
use std::sync::Arc;
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use parity_wasm::{interpreter, elements};
|
||||
use {alloc, gas_counter, storage};
|
||||
use parity_wasm::interpreter;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct RuntimeEnv {
|
||||
pub gas_counter: Cell<u64>,
|
||||
pub gas_limit: u64,
|
||||
pub dynamic_top: Cell<u32>,
|
||||
pub storage: RefCell<HashMap<storage::StorageKey, storage::StorageValue>>,
|
||||
#[derive(Hash, PartialEq, Eq, Debug)]
|
||||
pub struct StorageKey([u8; 32]);
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct StorageValue([u8; 32]);
|
||||
|
||||
struct ErrorStorage;
|
||||
|
||||
impl StorageKey {
|
||||
// todo: deal with memory views
|
||||
fn from_mem(vec: Vec<u8>) -> Result<Self, ErrorStorage> {
|
||||
if vec.len() != 32 { return Err(ErrorStorage); }
|
||||
let mut result = StorageKey([0u8; 32]);
|
||||
result.0.copy_from_slice(&vec[0..32]);
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
pub struct Runtime(Arc<RuntimeEnv>);
|
||||
impl StorageValue {
|
||||
// todo: deal with memory views
|
||||
// todo: deal with variable-length values when it comes
|
||||
fn from_mem(vec: Vec<u8>) -> Result<Self, ErrorStorage> {
|
||||
if vec.len() != 32 { return Err(ErrorStorage); }
|
||||
let mut result = StorageValue([0u8; 32]);
|
||||
result.0.copy_from_slice(&vec[0..32]);
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn as_slice(&self) -> &[u8] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Runtime {
|
||||
gas_counter: u64,
|
||||
gas_limit: u64,
|
||||
dynamic_top: u32,
|
||||
storage: HashMap<StorageKey, StorageValue>,
|
||||
memory: Arc<interpreter::MemoryInstance>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ErrorAlloc;
|
||||
|
||||
impl Runtime {
|
||||
pub fn with_params(stack_space: u32, gas_limit: u64) -> Runtime {
|
||||
Runtime(Arc::new(RuntimeEnv {
|
||||
gas_counter: Cell::new(0),
|
||||
gas_limit: gas_limit,
|
||||
dynamic_top: Cell::new(stack_space),
|
||||
storage: RefCell::new(HashMap::new()),
|
||||
}))
|
||||
}
|
||||
pub fn with_params(memory: Arc<interpreter::MemoryInstance>, stack_space: u32, gas_limit: u64) -> Runtime {
|
||||
Runtime {
|
||||
gas_counter: 0,
|
||||
gas_limit: gas_limit,
|
||||
dynamic_top: stack_space,
|
||||
storage: HashMap::new(),
|
||||
memory: memory,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn allocator(&self) -> alloc::Arena {
|
||||
alloc::Arena {
|
||||
runtime: self.clone(),
|
||||
}
|
||||
}
|
||||
pub fn storage_write(&mut self, context: interpreter::CallerContext)
|
||||
-> Result<Option<interpreter::RuntimeValue>, interpreter::Error>
|
||||
{
|
||||
let val_ptr = context.value_stack.pop_as::<i32>()?;
|
||||
let key_ptr = context.value_stack.pop_as::<i32>()?;
|
||||
|
||||
pub fn gas_counter(&self) -> gas_counter::GasCounter {
|
||||
gas_counter::GasCounter {
|
||||
runtime: self.clone(),
|
||||
}
|
||||
}
|
||||
let key = StorageKey::from_mem(self.memory.get(key_ptr as u32, 32)?)
|
||||
.map_err(|_| interpreter::Error::Trap("Memory access violation".to_owned()))?;
|
||||
let val = StorageValue::from_mem(self.memory.get(val_ptr as u32, 32)?)
|
||||
.map_err(|_| interpreter::Error::Trap("Memory access violation".to_owned()))?;
|
||||
|
||||
pub fn storage(&self) -> storage::Storage {
|
||||
storage::Storage::new(self.clone())
|
||||
}
|
||||
println!("write storage {:?} = {:?}", key, val);
|
||||
|
||||
pub fn env(&self) -> &RuntimeEnv {
|
||||
&*self.0
|
||||
}
|
||||
self.storage.insert(key, val);
|
||||
|
||||
Ok(Some(0i32.into()))
|
||||
}
|
||||
|
||||
pub fn storage_read(&mut self, context: interpreter::CallerContext)
|
||||
-> Result<Option<interpreter::RuntimeValue>, interpreter::Error>
|
||||
{
|
||||
// arguments passed are in backward order (since it is stack)
|
||||
let val_ptr = context.value_stack.pop_as::<i32>()?;
|
||||
let key_ptr = context.value_stack.pop_as::<i32>()?;
|
||||
|
||||
let key = StorageKey::from_mem(self.memory.get(key_ptr as u32, 32)?)
|
||||
.map_err(|_| interpreter::Error::Trap("Memory access violation".to_owned()))?;
|
||||
let empty = StorageValue([0u8; 32]);
|
||||
let val = self.storage.get(&key).unwrap_or(&empty);
|
||||
|
||||
self.memory.set(val_ptr as u32, val.as_slice())?;
|
||||
|
||||
println!("read storage {:?} (evaluated as {:?})", key, val);
|
||||
|
||||
Ok(Some(0.into()))
|
||||
}
|
||||
|
||||
pub fn malloc(&mut self, context: interpreter::CallerContext)
|
||||
-> Result<Option<interpreter::RuntimeValue>, interpreter::Error>
|
||||
{
|
||||
let amount = context.value_stack.pop_as::<i32>()? as u32;
|
||||
let previous_top = self.dynamic_top;
|
||||
self.dynamic_top = previous_top + amount;
|
||||
Ok(Some((previous_top as i32).into()))
|
||||
}
|
||||
|
||||
pub fn alloc(&mut self, amount: u32) -> Result<u32, ErrorAlloc> {
|
||||
let previous_top = self.dynamic_top;
|
||||
self.dynamic_top = previous_top + amount;
|
||||
Ok(previous_top.into())
|
||||
}
|
||||
|
||||
fn gas(&mut self, context: interpreter::CallerContext)
|
||||
-> Result<Option<interpreter::RuntimeValue>, interpreter::Error>
|
||||
{
|
||||
let prev = self.gas_counter;
|
||||
let update = context.value_stack.pop_as::<i32>()? as u64;
|
||||
if prev + update > self.gas_limit {
|
||||
// exceeds gas
|
||||
Err(interpreter::Error::Trap(format!("Gas exceeds limits of {}", self.gas_limit)))
|
||||
} else {
|
||||
self.gas_counter = prev + update;
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
fn user_trap(&mut self, _context: interpreter::CallerContext)
|
||||
-> Result<Option<interpreter::RuntimeValue>, interpreter::Error>
|
||||
{
|
||||
Err(interpreter::Error::Trap("unknown trap".to_owned()))
|
||||
}
|
||||
|
||||
fn user_noop(&mut self,
|
||||
_context: interpreter::CallerContext
|
||||
) -> Result<Option<interpreter::RuntimeValue>, interpreter::Error> {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn user_trap(funcs: &mut interpreter::UserFunctions, func_name: &str) {
|
||||
let func_str = func_name.to_owned();
|
||||
funcs.insert(func_str.clone(),
|
||||
interpreter::UserFunction {
|
||||
params: vec![],
|
||||
result: Some(elements::ValueType::I32),
|
||||
closure: Box::new(UserTrap(func_str)),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
struct UserTrap(String);
|
||||
|
||||
impl interpreter::UserFunctionInterface for UserTrap {
|
||||
fn call(&mut self,
|
||||
_module: &interpreter::ModuleInstance,
|
||||
_context: interpreter::CallerContext
|
||||
) -> Result<Option<interpreter::RuntimeValue>, interpreter::Error> {
|
||||
Err(interpreter::Error::Trap(self.0.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
struct UserNoop;
|
||||
|
||||
pub fn user_noop(funcs: &mut interpreter::UserFunctions, func_name: &str) {
|
||||
let func_str = func_name.to_owned();
|
||||
funcs.insert(func_str.clone(),
|
||||
interpreter::UserFunction {
|
||||
params: vec![],
|
||||
result: None,
|
||||
closure: Box::new(UserNoop),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
impl interpreter::UserFunctionInterface for UserNoop {
|
||||
fn call(&mut self,
|
||||
_module: &interpreter::ModuleInstance,
|
||||
_context: interpreter::CallerContext
|
||||
) -> Result<Option<interpreter::RuntimeValue>, interpreter::Error> {
|
||||
Ok(None)
|
||||
}
|
||||
impl interpreter::UserFunctionExecutor for Runtime {
|
||||
fn execute(&mut self, name: &str, context: interpreter::CallerContext)
|
||||
-> Result<Option<interpreter::RuntimeValue>, interpreter::Error>
|
||||
{
|
||||
match name {
|
||||
"_malloc" => {
|
||||
self.malloc(context)
|
||||
},
|
||||
"_free" => {
|
||||
self.user_noop(context)
|
||||
},
|
||||
"_storage_read" => {
|
||||
self.storage_read(context)
|
||||
},
|
||||
"_storage_write" => {
|
||||
self.storage_write(context)
|
||||
},
|
||||
"gas" => {
|
||||
self.gas(context)
|
||||
},
|
||||
_ => {
|
||||
self.user_trap(context)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,118 +0,0 @@
|
||||
use parity_wasm::interpreter::{self, ItemIndex, ModuleInstanceInterface};
|
||||
use std::sync::Arc;
|
||||
|
||||
use DEFAULT_MEMORY_INDEX;
|
||||
use runtime::Runtime;
|
||||
|
||||
#[derive(Hash, PartialEq, Eq, Debug)]
|
||||
pub struct StorageKey([u8; 32]);
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct StorageValue([u8; 32]);
|
||||
|
||||
impl StorageKey {
|
||||
// todo: deal with memory views
|
||||
fn from_mem(vec: Vec<u8>) -> Result<Self, Error> {
|
||||
if vec.len() != 32 { return Err(Error); }
|
||||
let mut result = StorageKey([0u8; 32]);
|
||||
result.0.copy_from_slice(&vec[0..32]);
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
impl StorageValue {
|
||||
// todo: deal with memory views
|
||||
// todo: deal with variable-length values when it comes
|
||||
fn from_mem(vec: Vec<u8>) -> Result<Self, Error> {
|
||||
if vec.len() != 32 { return Err(Error); }
|
||||
let mut result = StorageValue([0u8; 32]);
|
||||
result.0.copy_from_slice(&vec[0..32]);
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn as_slice(&self) -> &[u8] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Storage {
|
||||
runtime: Runtime,
|
||||
}
|
||||
|
||||
pub struct Error;
|
||||
|
||||
impl Storage {
|
||||
|
||||
pub fn new(runtime: Runtime) -> Self {
|
||||
Storage {
|
||||
runtime: runtime,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn writer(self) -> StorageWrite {
|
||||
StorageWrite(self)
|
||||
}
|
||||
|
||||
pub fn reader(self) -> StorageRead {
|
||||
StorageRead(self)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct StorageWrite(Storage);
|
||||
|
||||
impl interpreter::UserFunctionInterface for StorageWrite {
|
||||
fn call(&mut self,
|
||||
module: &interpreter::ModuleInstance,
|
||||
context: interpreter::CallerContext,
|
||||
) -> Result<Option<interpreter::RuntimeValue>, interpreter::Error> {
|
||||
|
||||
// arguments passed are in backward order (since it is stack)
|
||||
let val_ptr = context.value_stack.pop_as::<i32>()?;
|
||||
let key_ptr = context.value_stack.pop_as::<i32>()?;
|
||||
|
||||
let memory = match module.memory(DEFAULT_MEMORY_INDEX) {
|
||||
Err(_) => { return Ok(Some((-1i32).into())) },
|
||||
Ok(memory) => memory,
|
||||
};
|
||||
|
||||
let key = StorageKey::from_mem(memory.get(key_ptr as u32, 32)?)
|
||||
.map_err(|_| interpreter::Error::Trap("Memory access violation".to_owned()))?;
|
||||
let val = StorageValue::from_mem(memory.get(val_ptr as u32, 32)?)
|
||||
.map_err(|_| interpreter::Error::Trap("Memory access violation".to_owned()))?;
|
||||
|
||||
println!("set storage {:?} = {:?}", key, val);
|
||||
|
||||
Ok(Some(0.into()))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct StorageRead(Storage);
|
||||
|
||||
impl interpreter::UserFunctionInterface for StorageRead {
|
||||
fn call(&mut self,
|
||||
module: &interpreter::ModuleInstance,
|
||||
context: interpreter::CallerContext,
|
||||
) -> Result<Option<interpreter::RuntimeValue>, interpreter::Error> {
|
||||
|
||||
// arguments passed are in backward order (since it is stack)
|
||||
let val_ptr = context.value_stack.pop_as::<i32>()?;
|
||||
let key_ptr = context.value_stack.pop_as::<i32>()?;
|
||||
|
||||
let memory = match module.memory(DEFAULT_MEMORY_INDEX) {
|
||||
Err(_) => { return Ok(Some((-1i32).into())) },
|
||||
Ok(memory) => memory,
|
||||
};
|
||||
|
||||
let key = StorageKey::from_mem(memory.get(key_ptr as u32, 32)?)
|
||||
.map_err(|_| interpreter::Error::Trap("Memory access violation".to_owned()))?;
|
||||
let empty = StorageValue([0u8; 32]);
|
||||
let storage = self.0.runtime.env().storage.borrow();
|
||||
let val = storage.get(&key).unwrap_or(&empty);
|
||||
|
||||
memory.set(val_ptr as u32, val.as_slice());
|
||||
|
||||
println!("read storage {:?} (evaluated as {:?})", key, val);
|
||||
|
||||
Ok(Some(0.into()))
|
||||
}
|
||||
}
|
||||
+11
-11
@@ -13,15 +13,15 @@ pub fn call() {
|
||||
|
||||
/* This produces the following code (after injecting gas counter & optimizing)
|
||||
(module
|
||||
(type (;0;) (func))
|
||||
(type (;1;) (func (param i32)))
|
||||
(import "env" "memory" (memory (;0;) 256 256))
|
||||
(import "env" "table" (table (;0;) 0 0 anyfunc))
|
||||
(import "env" "gas" (func (;0;) (type 1)))
|
||||
(func (;1;) (type 0)
|
||||
i32.const 2
|
||||
call 0
|
||||
nop)
|
||||
(export "_call" (func 1))
|
||||
(data (i32.const 1212) "\1c\05"))
|
||||
(type (;0;) (func))
|
||||
(type (;1;) (func (param i32)))
|
||||
(import "env" "memory" (memory (;0;) 256 256))
|
||||
(import "env" "table" (table (;0;) 0 0 anyfunc))
|
||||
(import "env" "gas" (func (;0;) (type 1)))
|
||||
(func (;1;) (type 0)
|
||||
i32.const 2
|
||||
call 0
|
||||
nop)
|
||||
(export "_call" (func 1))
|
||||
(data (i32.const 1212) "\1c\05"))
|
||||
*/
|
||||
+81
-81
@@ -3,99 +3,99 @@ use parity_wasm::{elements, builder};
|
||||
type Insertion = (usize, u32, u32, String);
|
||||
|
||||
pub fn update_call_index(opcodes: &mut elements::Opcodes, original_imports: usize, inserts: &[Insertion]) {
|
||||
use parity_wasm::elements::Opcode::*;
|
||||
for opcode in opcodes.elements_mut().iter_mut() {
|
||||
match opcode {
|
||||
&mut Block(_, ref mut block) | &mut If(_, ref mut block) | &mut Loop(_, ref mut block) => {
|
||||
update_call_index(block, original_imports, inserts)
|
||||
},
|
||||
&mut Call(ref mut call_index) => {
|
||||
if let Some(pos) = inserts.iter().position(|x| x.1 == *call_index) {
|
||||
*call_index = (original_imports + pos) as u32;
|
||||
} else if *call_index as usize > original_imports {
|
||||
*call_index += inserts.len() as u32;
|
||||
}
|
||||
},
|
||||
_ => { }
|
||||
}
|
||||
}
|
||||
use parity_wasm::elements::Opcode::*;
|
||||
for opcode in opcodes.elements_mut().iter_mut() {
|
||||
match opcode {
|
||||
&mut Block(_, ref mut block) | &mut If(_, ref mut block) | &mut Loop(_, ref mut block) => {
|
||||
update_call_index(block, original_imports, inserts)
|
||||
},
|
||||
&mut Call(ref mut call_index) => {
|
||||
if let Some(pos) = inserts.iter().position(|x| x.1 == *call_index) {
|
||||
*call_index = (original_imports + pos) as u32;
|
||||
} else if *call_index as usize > original_imports {
|
||||
*call_index += inserts.len() as u32;
|
||||
}
|
||||
},
|
||||
_ => { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn externalize(
|
||||
module: elements::Module,
|
||||
replaced_funcs: Vec<&str>,
|
||||
module: elements::Module,
|
||||
replaced_funcs: Vec<&str>,
|
||||
) -> elements::Module {
|
||||
// Save import functions number for later
|
||||
let import_funcs_total = module
|
||||
.import_section().expect("Import section to exist")
|
||||
.entries()
|
||||
.iter()
|
||||
.filter(|e| if let &elements::External::Function(_) = e.external() { true } else { false })
|
||||
.count();
|
||||
let import_funcs_total = module
|
||||
.import_section().expect("Import section to exist")
|
||||
.entries()
|
||||
.iter()
|
||||
.filter(|e| if let &elements::External::Function(_) = e.external() { true } else { false })
|
||||
.count();
|
||||
|
||||
// First, we find functions indices that are to be rewired to externals
|
||||
// Triple is (function_index (callable), type_index, function_name)
|
||||
let mut replaces: Vec<Insertion> = replaced_funcs
|
||||
.into_iter()
|
||||
.filter_map(|f| {
|
||||
let export = module
|
||||
.export_section().expect("Export section to exist")
|
||||
.entries().iter().enumerate()
|
||||
.find(|&(_, entry)| entry.field() == f)
|
||||
.expect("All functions of interest to exist");
|
||||
// First, we find functions indices that are to be rewired to externals
|
||||
// Triple is (function_index (callable), type_index, function_name)
|
||||
let mut replaces: Vec<Insertion> = replaced_funcs
|
||||
.into_iter()
|
||||
.filter_map(|f| {
|
||||
let export = module
|
||||
.export_section().expect("Export section to exist")
|
||||
.entries().iter().enumerate()
|
||||
.find(|&(_, entry)| entry.field() == f)
|
||||
.expect("All functions of interest to exist");
|
||||
|
||||
if let &elements::Internal::Function(func_idx) = export.1.internal() {
|
||||
let type_ref = module
|
||||
.functions_section().expect("Functions section to exist")
|
||||
.entries()[func_idx as usize - import_funcs_total]
|
||||
.type_ref();
|
||||
if let &elements::Internal::Function(func_idx) = export.1.internal() {
|
||||
let type_ref = module
|
||||
.functions_section().expect("Functions section to exist")
|
||||
.entries()[func_idx as usize - import_funcs_total]
|
||||
.type_ref();
|
||||
|
||||
Some((export.0, func_idx, type_ref, export.1.field().to_owned()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
Some((export.0, func_idx, type_ref, export.1.field().to_owned()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
replaces.sort_by_key(|e| e.0);
|
||||
replaces.sort_by_key(|e| e.0);
|
||||
|
||||
// Second, we duplicate them as import definitions
|
||||
let mut mbuilder = builder::from_module(module);
|
||||
for &(_, _, type_ref, ref field) in replaces.iter() {
|
||||
mbuilder.push_import(
|
||||
builder::import()
|
||||
.module("env")
|
||||
.field(field)
|
||||
.external().func(type_ref)
|
||||
.build()
|
||||
);
|
||||
}
|
||||
// Second, we duplicate them as import definitions
|
||||
let mut mbuilder = builder::from_module(module);
|
||||
for &(_, _, type_ref, ref field) in replaces.iter() {
|
||||
mbuilder.push_import(
|
||||
builder::import()
|
||||
.module("env")
|
||||
.field(field)
|
||||
.external().func(type_ref)
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
// Back to mutable access
|
||||
let mut module = mbuilder.build();
|
||||
// Back to mutable access
|
||||
let mut module = mbuilder.build();
|
||||
|
||||
// Third, rewire all calls to imported functions and update all other calls indices
|
||||
for section in module.sections_mut() {
|
||||
match section {
|
||||
&mut elements::Section::Code(ref mut code_section) => {
|
||||
for ref mut func_body in code_section.bodies_mut() {
|
||||
update_call_index(func_body.code_mut(), import_funcs_total, &replaces);
|
||||
}
|
||||
},
|
||||
&mut elements::Section::Export(ref mut export_section) => {
|
||||
for ref mut export in export_section.entries_mut() {
|
||||
match export.internal_mut() {
|
||||
&mut elements::Internal::Function(ref mut func_index) => {
|
||||
if *func_index >= import_funcs_total as u32 { *func_index += replaces.len() as u32; }
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => { }
|
||||
}
|
||||
}
|
||||
// Third, rewire all calls to imported functions and update all other calls indices
|
||||
for section in module.sections_mut() {
|
||||
match section {
|
||||
&mut elements::Section::Code(ref mut code_section) => {
|
||||
for ref mut func_body in code_section.bodies_mut() {
|
||||
update_call_index(func_body.code_mut(), import_funcs_total, &replaces);
|
||||
}
|
||||
},
|
||||
&mut elements::Section::Export(ref mut export_section) => {
|
||||
for ref mut export in export_section.entries_mut() {
|
||||
match export.internal_mut() {
|
||||
&mut elements::Internal::Function(ref mut func_index) => {
|
||||
if *func_index >= import_funcs_total as u32 { *func_index += replaces.len() as u32; }
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => { }
|
||||
}
|
||||
}
|
||||
|
||||
module
|
||||
module
|
||||
|
||||
}
|
||||
+80
-80
@@ -2,98 +2,98 @@ use parity_wasm::{elements, builder};
|
||||
|
||||
|
||||
pub fn update_call_index(opcodes: &mut elements::Opcodes, inserted_index: u32) {
|
||||
use parity_wasm::elements::Opcode::*;
|
||||
for opcode in opcodes.elements_mut().iter_mut() {
|
||||
match opcode {
|
||||
&mut Block(_, ref mut block) | &mut If(_, ref mut block) | &mut Loop(_, ref mut block) => {
|
||||
update_call_index(block, inserted_index)
|
||||
},
|
||||
&mut Call(ref mut call_index) => {
|
||||
if *call_index >= inserted_index { *call_index += 1}
|
||||
},
|
||||
_ => { }
|
||||
}
|
||||
}
|
||||
use parity_wasm::elements::Opcode::*;
|
||||
for opcode in opcodes.elements_mut().iter_mut() {
|
||||
match opcode {
|
||||
&mut Block(_, ref mut block) | &mut If(_, ref mut block) | &mut Loop(_, ref mut block) => {
|
||||
update_call_index(block, inserted_index)
|
||||
},
|
||||
&mut Call(ref mut call_index) => {
|
||||
if *call_index >= inserted_index { *call_index += 1}
|
||||
},
|
||||
_ => { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn inject_counter(opcodes: &mut elements::Opcodes, gas_func: u32) {
|
||||
use parity_wasm::elements::Opcode::*;
|
||||
for opcode in opcodes.elements_mut().iter_mut() {
|
||||
match opcode {
|
||||
&mut Block(_, ref mut block) | &mut If(_, ref mut block) | &mut Loop(_, ref mut block) => {
|
||||
inject_counter(block, gas_func)
|
||||
},
|
||||
_ => { }
|
||||
}
|
||||
}
|
||||
use parity_wasm::elements::Opcode::*;
|
||||
for opcode in opcodes.elements_mut().iter_mut() {
|
||||
match opcode {
|
||||
&mut Block(_, ref mut block) | &mut If(_, ref mut block) | &mut Loop(_, ref mut block) => {
|
||||
inject_counter(block, gas_func)
|
||||
},
|
||||
_ => { }
|
||||
}
|
||||
}
|
||||
|
||||
let ops = opcodes.elements_mut().len() as u32;
|
||||
opcodes.elements_mut().insert(0, I32Const(ops as i32));
|
||||
opcodes.elements_mut().insert(1, Call(gas_func));
|
||||
let ops = opcodes.elements_mut().len() as u32;
|
||||
opcodes.elements_mut().insert(0, I32Const(ops as i32));
|
||||
opcodes.elements_mut().insert(1, Call(gas_func));
|
||||
}
|
||||
|
||||
pub fn inject_gas_counter(module: elements::Module) -> elements::Module {
|
||||
// Injecting gas counting external
|
||||
let mut mbuilder = builder::from_module(module);
|
||||
let import_sig = mbuilder.push_signature(
|
||||
builder::signature()
|
||||
.param().i32()
|
||||
.build_sig()
|
||||
);
|
||||
// Injecting gas counting external
|
||||
let mut mbuilder = builder::from_module(module);
|
||||
let import_sig = mbuilder.push_signature(
|
||||
builder::signature()
|
||||
.param().i32()
|
||||
.build_sig()
|
||||
);
|
||||
|
||||
let mut gas_func = mbuilder.push_import(
|
||||
builder::import()
|
||||
.module("env")
|
||||
.field("gas")
|
||||
.external().func(import_sig)
|
||||
.build()
|
||||
);
|
||||
let mut gas_func = mbuilder.push_import(
|
||||
builder::import()
|
||||
.module("env")
|
||||
.field("gas")
|
||||
.external().func(import_sig)
|
||||
.build()
|
||||
);
|
||||
|
||||
// back to plain module
|
||||
let mut module = mbuilder.build();
|
||||
// back to plain module
|
||||
let mut module = mbuilder.build();
|
||||
|
||||
assert!(module.global_section().is_some());
|
||||
assert!(module.global_section().is_some());
|
||||
|
||||
// calculate actual function index of the imported definition
|
||||
// (substract all imports that are NOT functions)
|
||||
// calculate actual function index of the imported definition
|
||||
// (substract all imports that are NOT functions)
|
||||
|
||||
for import_entry in module.import_section().expect("Builder should have insert the import section").entries() {
|
||||
match *import_entry.external() {
|
||||
elements::External::Function(_) => {},
|
||||
_ => { gas_func -= 1; }
|
||||
}
|
||||
}
|
||||
for import_entry in module.import_section().expect("Builder should have insert the import section").entries() {
|
||||
match *import_entry.external() {
|
||||
elements::External::Function(_) => {},
|
||||
_ => { gas_func -= 1; }
|
||||
}
|
||||
}
|
||||
|
||||
// Updating calling addresses (all calls to function index >= `gas_func` should be incremented)
|
||||
for section in module.sections_mut() {
|
||||
match section {
|
||||
&mut elements::Section::Code(ref mut code_section) => {
|
||||
for ref mut func_body in code_section.bodies_mut() {
|
||||
update_call_index(func_body.code_mut(), gas_func);
|
||||
inject_counter(func_body.code_mut(), gas_func);
|
||||
}
|
||||
},
|
||||
&mut elements::Section::Export(ref mut export_section) => {
|
||||
for ref mut export in export_section.entries_mut() {
|
||||
match export.internal_mut() {
|
||||
&mut elements::Internal::Function(ref mut func_index) => {
|
||||
if *func_index >= gas_func { *func_index += 1}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
},
|
||||
&mut elements::Section::Element(ref mut elements_section) => {
|
||||
for ref mut segment in elements_section.entries_mut() {
|
||||
// update all indirect call addresses initial values
|
||||
for func_index in segment.members_mut() {
|
||||
if *func_index >= gas_func { *func_index += 1}
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => { }
|
||||
}
|
||||
}
|
||||
// Updating calling addresses (all calls to function index >= `gas_func` should be incremented)
|
||||
for section in module.sections_mut() {
|
||||
match section {
|
||||
&mut elements::Section::Code(ref mut code_section) => {
|
||||
for ref mut func_body in code_section.bodies_mut() {
|
||||
update_call_index(func_body.code_mut(), gas_func);
|
||||
inject_counter(func_body.code_mut(), gas_func);
|
||||
}
|
||||
},
|
||||
&mut elements::Section::Export(ref mut export_section) => {
|
||||
for ref mut export in export_section.entries_mut() {
|
||||
match export.internal_mut() {
|
||||
&mut elements::Internal::Function(ref mut func_index) => {
|
||||
if *func_index >= gas_func { *func_index += 1}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
},
|
||||
&mut elements::Section::Element(ref mut elements_section) => {
|
||||
for ref mut segment in elements_section.entries_mut() {
|
||||
// update all indirect call addresses initial values
|
||||
for func_index in segment.members_mut() {
|
||||
if *func_index >= gas_func { *func_index += 1}
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => { }
|
||||
}
|
||||
}
|
||||
|
||||
module
|
||||
module
|
||||
}
|
||||
+462
-462
File diff suppressed because it is too large
Load Diff
+127
-127
@@ -3,150 +3,150 @@ use std::collections::HashSet;
|
||||
|
||||
#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
|
||||
pub enum Symbol {
|
||||
Type(usize),
|
||||
Import(usize),
|
||||
Global(usize),
|
||||
Function(usize),
|
||||
Export(usize),
|
||||
Type(usize),
|
||||
Import(usize),
|
||||
Global(usize),
|
||||
Function(usize),
|
||||
Export(usize),
|
||||
}
|
||||
|
||||
pub fn resolve_function(module: &elements::Module, index: u32) -> Symbol {
|
||||
let mut functions = 0;
|
||||
if let Some(import_section) = module.import_section() {
|
||||
for (item_index, item) in import_section.entries().iter().enumerate() {
|
||||
match item.external() {
|
||||
&elements::External::Function(_) => {
|
||||
if functions == index {
|
||||
return Symbol::Import(item_index as usize);
|
||||
}
|
||||
functions += 1;
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut functions = 0;
|
||||
if let Some(import_section) = module.import_section() {
|
||||
for (item_index, item) in import_section.entries().iter().enumerate() {
|
||||
match item.external() {
|
||||
&elements::External::Function(_) => {
|
||||
if functions == index {
|
||||
return Symbol::Import(item_index as usize);
|
||||
}
|
||||
functions += 1;
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Symbol::Function(index as usize - functions as usize)
|
||||
Symbol::Function(index as usize - functions as usize)
|
||||
}
|
||||
|
||||
pub fn resolve_global(module: &elements::Module, index: u32) -> Symbol {
|
||||
let mut globals = 0;
|
||||
if let Some(import_section) = module.import_section() {
|
||||
for (item_index, item) in import_section.entries().iter().enumerate() {
|
||||
match item.external() {
|
||||
&elements::External::Global(_) => {
|
||||
if globals == index {
|
||||
return Symbol::Import(item_index as usize);
|
||||
}
|
||||
globals += 1;
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut globals = 0;
|
||||
if let Some(import_section) = module.import_section() {
|
||||
for (item_index, item) in import_section.entries().iter().enumerate() {
|
||||
match item.external() {
|
||||
&elements::External::Global(_) => {
|
||||
if globals == index {
|
||||
return Symbol::Import(item_index as usize);
|
||||
}
|
||||
globals += 1;
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Symbol::Global(index as usize - globals as usize)
|
||||
Symbol::Global(index as usize - globals as usize)
|
||||
}
|
||||
|
||||
pub fn push_code_symbols(module: &elements::Module, opcodes: &[elements::Opcode], dest: &mut Vec<Symbol>) {
|
||||
use parity_wasm::elements::Opcode::*;
|
||||
use parity_wasm::elements::Opcode::*;
|
||||
|
||||
for opcode in opcodes {
|
||||
match opcode {
|
||||
&Call(idx) => {
|
||||
dest.push(resolve_function(module, idx));
|
||||
},
|
||||
&GetGlobal(idx) | &SetGlobal(idx) => {
|
||||
dest.push(resolve_global(module, idx))
|
||||
},
|
||||
&If(_, ref block) | &Loop(_, ref block) | &Block(_, ref block) => {
|
||||
push_code_symbols(module, block.elements(), dest);
|
||||
},
|
||||
_ => { },
|
||||
}
|
||||
}
|
||||
for opcode in opcodes {
|
||||
match opcode {
|
||||
&Call(idx) => {
|
||||
dest.push(resolve_function(module, idx));
|
||||
},
|
||||
&GetGlobal(idx) | &SetGlobal(idx) => {
|
||||
dest.push(resolve_global(module, idx))
|
||||
},
|
||||
&If(_, ref block) | &Loop(_, ref block) | &Block(_, ref block) => {
|
||||
push_code_symbols(module, block.elements(), dest);
|
||||
},
|
||||
_ => { },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expand_symbols(module: &elements::Module, set: &mut HashSet<Symbol>) {
|
||||
use self::Symbol::*;
|
||||
use self::Symbol::*;
|
||||
|
||||
// symbols that were already processed
|
||||
let mut stop: HashSet<Symbol> = HashSet::new();
|
||||
let mut fringe = set.iter().cloned().collect::<Vec<Symbol>>();
|
||||
loop {
|
||||
let next = match fringe.pop() {
|
||||
Some(s) if stop.contains(&s) => { continue; }
|
||||
Some(s) => s,
|
||||
_ => { break; }
|
||||
};
|
||||
trace!("Processing symbol {:?}", next);
|
||||
// symbols that were already processed
|
||||
let mut stop: HashSet<Symbol> = HashSet::new();
|
||||
let mut fringe = set.iter().cloned().collect::<Vec<Symbol>>();
|
||||
loop {
|
||||
let next = match fringe.pop() {
|
||||
Some(s) if stop.contains(&s) => { continue; }
|
||||
Some(s) => s,
|
||||
_ => { break; }
|
||||
};
|
||||
trace!("Processing symbol {:?}", next);
|
||||
|
||||
match next {
|
||||
Export(idx) => {
|
||||
let entry = &module.export_section().expect("Export section to exist").entries()[idx];
|
||||
match entry.internal() {
|
||||
&elements::Internal::Function(func_idx) => {
|
||||
let symbol = resolve_function(module, func_idx);
|
||||
if !stop.contains(&symbol) {
|
||||
fringe.push(symbol);
|
||||
}
|
||||
set.insert(symbol);
|
||||
},
|
||||
&elements::Internal::Global(global_idx) => {
|
||||
let symbol = resolve_global(module, global_idx);
|
||||
if !stop.contains(&symbol) {
|
||||
fringe.push(symbol);
|
||||
}
|
||||
set.insert(symbol);
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
},
|
||||
Import(idx) => {
|
||||
let entry = &module.import_section().expect("Import section to exist").entries()[idx];
|
||||
match entry.external() {
|
||||
&elements::External::Function(type_idx) => {
|
||||
let type_symbol = Symbol::Type(type_idx as usize);
|
||||
if !stop.contains(&type_symbol) {
|
||||
fringe.push(type_symbol);
|
||||
}
|
||||
set.insert(type_symbol);
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
},
|
||||
Function(idx) => {
|
||||
let body = &module.code_section().expect("Code section to exist").bodies()[idx];
|
||||
let mut code_symbols = Vec::new();
|
||||
push_code_symbols(module, body.code().elements(), &mut code_symbols);
|
||||
for symbol in code_symbols.drain(..) {
|
||||
if !stop.contains(&symbol) {
|
||||
fringe.push(symbol);
|
||||
}
|
||||
set.insert(symbol);
|
||||
}
|
||||
match next {
|
||||
Export(idx) => {
|
||||
let entry = &module.export_section().expect("Export section to exist").entries()[idx];
|
||||
match entry.internal() {
|
||||
&elements::Internal::Function(func_idx) => {
|
||||
let symbol = resolve_function(module, func_idx);
|
||||
if !stop.contains(&symbol) {
|
||||
fringe.push(symbol);
|
||||
}
|
||||
set.insert(symbol);
|
||||
},
|
||||
&elements::Internal::Global(global_idx) => {
|
||||
let symbol = resolve_global(module, global_idx);
|
||||
if !stop.contains(&symbol) {
|
||||
fringe.push(symbol);
|
||||
}
|
||||
set.insert(symbol);
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
},
|
||||
Import(idx) => {
|
||||
let entry = &module.import_section().expect("Import section to exist").entries()[idx];
|
||||
match entry.external() {
|
||||
&elements::External::Function(type_idx) => {
|
||||
let type_symbol = Symbol::Type(type_idx as usize);
|
||||
if !stop.contains(&type_symbol) {
|
||||
fringe.push(type_symbol);
|
||||
}
|
||||
set.insert(type_symbol);
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
},
|
||||
Function(idx) => {
|
||||
let body = &module.code_section().expect("Code section to exist").bodies()[idx];
|
||||
let mut code_symbols = Vec::new();
|
||||
push_code_symbols(module, body.code().elements(), &mut code_symbols);
|
||||
for symbol in code_symbols.drain(..) {
|
||||
if !stop.contains(&symbol) {
|
||||
fringe.push(symbol);
|
||||
}
|
||||
set.insert(symbol);
|
||||
}
|
||||
|
||||
let signature = &module.functions_section().expect("Functions section to exist").entries()[idx];
|
||||
let type_symbol = Symbol::Type(signature.type_ref() as usize);
|
||||
if !stop.contains(&type_symbol) {
|
||||
fringe.push(type_symbol);
|
||||
}
|
||||
set.insert(type_symbol);
|
||||
},
|
||||
Global(idx) => {
|
||||
let entry = &module.global_section().expect("Global section to exist").entries()[idx];
|
||||
let mut code_symbols = Vec::new();
|
||||
push_code_symbols(module, entry.init_expr().code(), &mut code_symbols);
|
||||
for symbol in code_symbols.drain(..) {
|
||||
if !stop.contains(&symbol) {
|
||||
fringe.push(symbol);
|
||||
}
|
||||
set.insert(symbol);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
let signature = &module.functions_section().expect("Functions section to exist").entries()[idx];
|
||||
let type_symbol = Symbol::Type(signature.type_ref() as usize);
|
||||
if !stop.contains(&type_symbol) {
|
||||
fringe.push(type_symbol);
|
||||
}
|
||||
set.insert(type_symbol);
|
||||
},
|
||||
Global(idx) => {
|
||||
let entry = &module.global_section().expect("Global section to exist").entries()[idx];
|
||||
let mut code_symbols = Vec::new();
|
||||
push_code_symbols(module, entry.init_expr().code(), &mut code_symbols);
|
||||
for symbol in code_symbols.drain(..) {
|
||||
if !stop.contains(&symbol) {
|
||||
fringe.push(symbol);
|
||||
}
|
||||
set.insert(symbol);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
stop.insert(next);
|
||||
}
|
||||
stop.insert(next);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user