mirror of
https://github.com/pezkuwichain/wasm-instrument.git
synced 2026-04-30 14:08:00 +00:00
Merge remote-tracking branch 'origin/master' into inject-rules
This commit is contained in:
+8
-3
@@ -10,12 +10,13 @@ env_logger = "0.4"
|
||||
lazy_static = "0.2"
|
||||
clap = "2.24"
|
||||
glob = "0.2"
|
||||
byteorder = "1"
|
||||
|
||||
[lib]
|
||||
|
||||
[[bin]]
|
||||
name = "wasm-opt"
|
||||
path = "opt/src/main.rs"
|
||||
name = "wasm-prune"
|
||||
path = "prune/src/main.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "wasm-ext"
|
||||
@@ -31,4 +32,8 @@ path = "pack/src/main.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "wasm-build"
|
||||
path = "build/src/main.rs"
|
||||
path = "build/src/main.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "nondeterminism-check"
|
||||
path = "nondeterminism_check/src/main.rs"
|
||||
|
||||
@@ -10,10 +10,10 @@ Easiest way to use is to install via `cargo install`:
|
||||
cargo install --git https://github.com/paritytech/wasm-utils wasm-build
|
||||
```
|
||||
|
||||
## Symbols optimizer (wasm-opt)
|
||||
## Symbols pruning (wasm-prune)
|
||||
|
||||
```
|
||||
cargo run --release --bin wasm-opt -- <input_binary.wasm> <output_binary.wasm>
|
||||
cargo run --release --bin wasm-prune -- <input_binary.wasm> <output_binary.wasm>
|
||||
```
|
||||
|
||||
This will optimize WASM symbols tree to leave only those elements that are used by contract `_call` function entry.
|
||||
|
||||
+2
-2
@@ -7,7 +7,7 @@ authors = ["NikVolf <nikvolf@gmail.com>"]
|
||||
glob = "0.2"
|
||||
wasm-utils = { path = "../" }
|
||||
clap = "2.24"
|
||||
parity-wasm = "0.12"
|
||||
parity-wasm = "0.14"
|
||||
|
||||
[[bin]]
|
||||
name = "wasm-build"
|
||||
name = "wasm-build"
|
||||
|
||||
+19
-1
@@ -76,6 +76,14 @@ fn main() {
|
||||
.arg(Arg::with_name("skip_alloc")
|
||||
.help("Skip allocator externalizer step producing final wasm")
|
||||
.long("skip-externalize"))
|
||||
.arg(Arg::with_name("runtime_type")
|
||||
.help("Injects RUNTIME_TYPE global export")
|
||||
.takes_value(true)
|
||||
.long("runtime-type"))
|
||||
.arg(Arg::with_name("runtime_version")
|
||||
.help("Injects RUNTIME_VERSION global export")
|
||||
.takes_value(true)
|
||||
.long("runtime-version"))
|
||||
.get_matches();
|
||||
|
||||
let target_dir = matches.value_of("target").expect("is required; qed");
|
||||
@@ -98,5 +106,15 @@ fn main() {
|
||||
wasm_utils::optimize(&mut module, vec!["_call", "setTempRet0"]).expect("Optimizer to finish without errors");
|
||||
}
|
||||
|
||||
if let Some(runtime_type) = matches.value_of("runtime_type") {
|
||||
let runtime_type: &[u8] = runtime_type.as_bytes();
|
||||
if runtime_type.len() != 4 {
|
||||
panic!("--runtime-type should be equal to 4 bytes");
|
||||
}
|
||||
let runtime_version: u32 = matches.value_of("runtime_version").unwrap_or("1").parse()
|
||||
.expect("--runtime-version should be a positive integer");
|
||||
module = wasm_utils::inject_runtime_type(module, &runtime_type, runtime_version);
|
||||
}
|
||||
|
||||
parity_wasm::serialize_to_file(&path, module).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
target
|
||||
Cargo.lock
|
||||
@@ -0,0 +1,9 @@
|
||||
[package]
|
||||
name = "nondeterminism_check"
|
||||
version = "0.1.0"
|
||||
authors = ["NikVolf <nikvolf@gmail.com>"]
|
||||
|
||||
[dependencies]
|
||||
parity-wasm = "0.14"
|
||||
wasm-utils = { path = "../" }
|
||||
clap = "2.24"
|
||||
@@ -0,0 +1,26 @@
|
||||
extern crate parity_wasm;
|
||||
extern crate wasm_utils;
|
||||
|
||||
use std::env;
|
||||
|
||||
|
||||
fn main() {
|
||||
|
||||
wasm_utils::init_log();
|
||||
|
||||
let args = env::args().collect::<Vec<_>>();
|
||||
if args.len() != 2 {
|
||||
println!("Usage: {} input_file.wasm", args[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
// Loading module
|
||||
let module = parity_wasm::deserialize_file(&args[1]).expect("Module deserialization to succeed");
|
||||
|
||||
if wasm_utils::is_deterministic(&module) {
|
||||
println!("Module is deterministic");
|
||||
} else {
|
||||
println!("Module is not deterministic");
|
||||
}
|
||||
|
||||
}
|
||||
+6
-1
@@ -1,5 +1,6 @@
|
||||
extern crate parity_wasm;
|
||||
extern crate env_logger;
|
||||
extern crate byteorder;
|
||||
#[macro_use] extern crate log;
|
||||
#[macro_use] extern crate lazy_static;
|
||||
|
||||
@@ -11,9 +12,13 @@ mod symbols;
|
||||
mod logger;
|
||||
mod ext;
|
||||
mod pack;
|
||||
mod nondeterminism_check;
|
||||
mod runtime_type;
|
||||
|
||||
pub use optimizer::{optimize, Error as OptimizerError};
|
||||
pub use gas::inject_gas_counter;
|
||||
pub use logger::init_log;
|
||||
pub use ext::externalize;
|
||||
pub use pack::pack_instance;
|
||||
pub use pack::pack_instance;
|
||||
pub use nondeterminism_check::is_deterministic;
|
||||
pub use runtime_type::inject_runtime_type;
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
use parity_wasm::{elements};
|
||||
use parity_wasm::elements::{ Section, Opcode };
|
||||
use parity_wasm::elements::Opcode::*;
|
||||
|
||||
fn have_nondeterministic_opcodes (opcodes: &[Opcode]) -> bool {
|
||||
for opcode in opcodes {
|
||||
match *opcode {
|
||||
F32Abs |
|
||||
F32Neg |
|
||||
F32Ceil |
|
||||
F32Floor |
|
||||
F32Trunc |
|
||||
F32Nearest |
|
||||
F32Sqrt |
|
||||
F32Add |
|
||||
F32Sub |
|
||||
F32Mul |
|
||||
F32Div |
|
||||
F32Min |
|
||||
F32Max |
|
||||
F32Copysign |
|
||||
F64Abs |
|
||||
F64Neg |
|
||||
F64Ceil |
|
||||
F64Floor |
|
||||
F64Trunc |
|
||||
F64Nearest |
|
||||
F64Sqrt |
|
||||
F64Add |
|
||||
F64Sub |
|
||||
F64Mul |
|
||||
F64Div |
|
||||
F64Min |
|
||||
F64Max |
|
||||
F64Copysign |
|
||||
I32TruncSF32 |
|
||||
I32TruncUF32 |
|
||||
I32TruncSF64 |
|
||||
I32TruncUF64 |
|
||||
I64TruncSF32 |
|
||||
I64TruncUF32 |
|
||||
I64TruncSF64 |
|
||||
I64TruncUF64 |
|
||||
F32ConvertSI32 |
|
||||
F32ConvertUI32 |
|
||||
F32ConvertSI64 |
|
||||
F32ConvertUI64 |
|
||||
F32DemoteF64 |
|
||||
F64ConvertSI32 |
|
||||
F64ConvertUI32 |
|
||||
F64ConvertSI64 |
|
||||
F64ConvertUI64 |
|
||||
F64PromoteF32 |
|
||||
I32ReinterpretF32 |
|
||||
I64ReinterpretF64 |
|
||||
F32ReinterpretI32 |
|
||||
F64ReinterpretI64 |
|
||||
F32Eq |
|
||||
F32Ne |
|
||||
F32Lt |
|
||||
F32Gt |
|
||||
F32Le |
|
||||
F32Ge |
|
||||
F64Eq |
|
||||
F64Ne |
|
||||
F64Lt |
|
||||
F64Gt |
|
||||
F64Le |
|
||||
F64Ge
|
||||
=> return true,
|
||||
_ => continue
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
pub fn is_deterministic(module: &elements::Module) -> bool {
|
||||
for section in module.sections() {
|
||||
match *section {
|
||||
Section::Code(ref cs) => {
|
||||
for body in cs.bodies() {
|
||||
if have_nondeterministic_opcodes(body.code().elements()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => continue
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use parity_wasm::{builder, elements};
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn nondeterminism_found() {
|
||||
let module = builder::module()
|
||||
.function().signature().return_type().f32().build()
|
||||
.body()
|
||||
.with_opcodes(elements::Opcodes::new(
|
||||
vec![
|
||||
elements::Opcode::F32Const(1), // unrelated to this test matter
|
||||
elements::Opcode::F32Const(1), // unrelated to this test matter
|
||||
elements::Opcode::F32Add,
|
||||
elements::Opcode::End
|
||||
]
|
||||
))
|
||||
.build()
|
||||
.build()
|
||||
.build();
|
||||
assert_eq!(false, is_deterministic(&module));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nondeterminism_not() {
|
||||
let module = builder::module()
|
||||
.function().signature().return_type().i32().build()
|
||||
.body()
|
||||
.with_opcodes(elements::Opcodes::new(
|
||||
vec![
|
||||
elements::Opcode::I32Const(1),
|
||||
elements::Opcode::I32Const(1),
|
||||
elements::Opcode::I32Add,
|
||||
elements::Opcode::End
|
||||
]
|
||||
))
|
||||
.build()
|
||||
.build()
|
||||
.build();
|
||||
assert_eq!(true, is_deterministic(&module));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
use parity_wasm::{elements, builder};
|
||||
use self::elements::{ Module, GlobalEntry, External, ExportEntry, GlobalType, ValueType, InitExpr, Opcode, Internal };
|
||||
use byteorder::{ LittleEndian, ByteOrder };
|
||||
|
||||
pub fn inject_runtime_type(module: Module, runtime_type: &[u8], runtime_version: u32) -> Module {
|
||||
let runtime_type: u32 = LittleEndian::read_u32(&runtime_type);
|
||||
let globals_count: u32 = match module.global_section() {
|
||||
Some(ref section) => section.entries().len() as u32,
|
||||
None => 0
|
||||
};
|
||||
let imported_globals_count: u32 = match module.import_section() {
|
||||
Some(ref section) => section.entries().iter().filter(|e| match *e.external() {
|
||||
External::Global(ref _a) => true,
|
||||
_ => false
|
||||
}).count() as u32,
|
||||
None => 0
|
||||
};
|
||||
let total_globals_count: u32 = globals_count + imported_globals_count;
|
||||
|
||||
builder::from_module(module)
|
||||
.with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Opcode::I32Const(runtime_type as i32), Opcode::End])))
|
||||
.with_export(ExportEntry::new("RUNTIME_TYPE".into(), Internal::Global(total_globals_count)))
|
||||
.with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Opcode::I32Const(runtime_version as i32), Opcode::End])))
|
||||
.with_export(ExportEntry::new("RUNTIME_VERSION".into(), Internal::Global(total_globals_count + 1)))
|
||||
.build()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn it_injects() {
|
||||
let mut module = builder::module()
|
||||
.with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Opcode::I32Const(42 as i32)])))
|
||||
.build();
|
||||
module = inject_runtime_type(module, b"emcc", 1);
|
||||
let global_section = module.global_section().expect("Global section expected");
|
||||
assert_eq!(3, global_section.entries().len());
|
||||
let export_section = module.export_section().expect("Export section expected");
|
||||
assert!(export_section.entries().iter().find(|e| e.field() == "RUNTIME_TYPE" ).is_some());
|
||||
assert!(export_section.entries().iter().find(|e| e.field() == "RUNTIME_VERSION" ).is_some());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user