Commit b3cca34b authored by WuEcho's avatar WuEcho

add code

parents
Pipeline #636 failed with stages
# Changelog
## [0.5.0] - 2019-07-25
- New module: `remapstart`. [[#107](https://github.com/wasmx/wasm-chisel/pull/107)]
- New module: `dropsection`. [[#72](https://github.com/wasmx/wasm-chisel/pull/72)]
- New module: `fromwat`. [[#100](https://github.com/wasmx/wasm-chisel/pull/100)]
- New module: `snip` (for using wasm-snip). [[#123](https://github.com/wasmx/wasm-chisel/pull/123)]
- New module: `dropnames`. [[#126](https://github.com/wasmx/wasm-chisel/pull/126)]
- New module: `checkfloats`. [[#118](https://github.com/wasmx/wasm-chisel/pull/118)]
- Refactored `verifyimpotrs` and `remapimports`. [[#6](https://github.com/wasmx/wasm-chisel/pull/66)]
- Added support for `eth2` / `debug` and `bignum` imports. [[#112](https://github.com/wasmx/wasm-chisel/pull/112)]
- Better handling for the names section.
- Removed Rust and many Clippy warnings.
## [0.4.0] - 2019-06-04
- New module: `repack`.
- Fixed: [[#97](https://github.com/wasmx/wasm-chisel/pull/97)]
Code generated by `deployer` now uses proper import signature.
- Improved internal APIs regarding error handling and testing.
## [0.3.0] - 2019-05-23
- New module: `trimstart`.
- Fixed: [[#84](https://github.com/wasmx/wasm-chisel/pull/84)
[#91](https://github.com/wasmx/wasm-chisel/pull/91)]
Fixed import list for `ewasm` in `remapimports` and `verifyimports`.
- Fixed: [[#85](https://github.com/wasmx/wasm-chisel/pull/85)]
Code generated by `deployer` now properly allocates memory.
## [0.2.0] - 2019-03-26
- New modules: `deployer`, `verifyexports` and `verifyimports`.
- Introduced new CLI frontend supporting a YAML configuration.
- Internal API improvements.
- Updated dependency: `pwasm` to 0.35.
- Moved on to Rust 2018 edition.
## [0.1.0] - 2018-11-15
- Initial release.
- Modules: `checkstartfunc`, `remapimports` and `trimexports`. All of
them with the `ewasm` profile.
[0.5.0]: https://github.com/wasmx/wasm-chisel/compare/v0.4.0...master
[0.4.0]: https://github.com/wasmx/wasm-chisel/releases/tag/v0.4.0
[0.3.0]: https://github.com/wasmx/wasm-chisel/releases/tag/v0.3.0
[0.2.0]: https://github.com/wasmx/wasm-chisel/releases/tag/v0.2.0
[0.1.0]: https://github.com/wasmx/wasm-chisel/releases/tag/v0.1.0
[Cable]: https://github.com/ethereum/cable
This diff is collapsed.
[workspace]
members = [
"chisel",
"libchisel"
]
\ No newline at end of file
This diff is collapsed.
# wasm-chisel
![Build](https://circleci.com/gh/wasmx/wasm-chisel.svg?style=shield&circle-token=:circle-token)
![Version](https://img.shields.io/crates/v/chisel.svg)
Some useful utilities to transform WebAssembly binaries, most importantly for WebAssembly used in a deterministic / blockchain context,
such as with [ewasm].
## Library
### remapimports
Provide a list of imports (with namespace and name) and replace them with a new set of namespace and name pairs.
This can be very useful together with compilers, which do not support the specification of a namespace in imports yet. As of writing mid-2018,
that includes pretty much every compiler (one exception is AssemblyScript).
It supports the same presets as `verifyimports`.
### trimexports
Removes all exports, but the ones specified.
This comes with some presets:
- `ewasm`: keeps `main` and exported memory
- `pwasm`: keeps `_call`
### trimstartfunc
Remove start function.
This comes with the following preset:
- `ewasm`: removes `start` function if present
### verifyimports
Verifies that the module's imports are compliant with the provided import interface.
Can be set to require the existence of the entire import set, or just the validity of existing imports with matching identifiers.
Can be set to allow or prohibit unlisted additional imports.
The following presets are provided:
- `ewasm`: Verifies the ewasm [EEI](https://github.com/ewasm/design/blob/master/eth_interface.md). Disallows unlisted imports, and does not require that the entire interface be imported.
- `debug`: Debug utilities for ewasm.
- `bignum`: Big-number library for ewasm.
- `eth2`: Verifies imports according to [Scout](https://github.com/ewasm/scout).
### verifyexports
Verifies that the module's exports are compliant with the provided export interface.
Can be set to allow or prohibit unlisted additional exports.
The following presets are provided:
- `ewasm`: Verifies that the `main` function and `memory` is exported. Disallows any unlisted exports.
### dropsection
Removes selected sections from the module.
### deployer
Wraps module into an ewasm-compatible constructor. It has two presets:
- `memory`: wrap the module as a pre-defined memory section
- `customsection`: include the module as a custom section
### repack
Re-serializes the module. It will drop any unknown (custom) sections.
### remapstart
If there is a start section, export it as `main` (replacing any pre-existing `main` export) and remove the start section
### snip
Wraps [wasm-snip](https://github.com/rustwasm/wasm-snip/) and turns on removing Rust formatting and debugging from wasm.
### dropnames
Drops the NamesSection if present.
## CLI
`chisel` is available as a command line tool.
It uses features implemented in the library as well in [wasm-gc] and [wasm-utils]. It comes with a configuration file `chisel.yml`.
`chisel run`: searches for `chisel.yml` in the current directory, if not specified otherwise using the flag `-c`. Runs the modules specified in the configuration, outputs a new file if any changes were made by translator or creator modules, and prints a brief report of each module's results.
## Configuration file
The configuration file starts with a ruleset entry, where the name can be anything. Inside the ruleset are its options.
The only required field is `file`, which specifies the path to the Wasm binary to be chiseled.
Optionally, one may also specified an output file through the `output` option.
It is important to note that the configuration parsing will not work if all the rules are prepended with a hyphen. Please avoid this until the configuration parser is generalized.
```yaml
ewasm:
file: "target/wasm32-unknown-unknown/release/sentinel.wasm"
output: "out.wasm"
remapimports:
preset: "ewasm"
```
## sentinel.rs
TBA
## Changelog
A [changelog is available](CHANGELOG.md).
## Maintainers
* Alex Beregszaszi [@axic]
* Jake Lang [@jakelang]
## License
[Apache 2.0](LICENSE).
[@axic]: https://github.com/axic
[@jakelang]: https://github.com/jakelang
[ewasm]: http://github.com/ewasm
[wasm-gc]: https://github.com/alexcrichton/wasm-gc
[wasm-utils]: https://github.com/paritytech/wasm-utils
[package]
name = "chisel"
description = "Some useful utilities to transform WebAssembly binaries, most importantly for WebAssembly used in a deterministic / blockchain context"
version = "0.5.0"
authors = ["Alex Beregszaszi <alex@rtfs.hu>", "Jake Lang <jak3lang@gmail.com>"]
license = "Apache-2.0"
repository = "https://github.com/wasmx/wasm-chisel"
homepage = "https://github.com/wasmx/wasm-chisel"
categories = ["webassembly", "development tools"]
keywords = ["webassembly", "wasm", "blockchain", "ethereum"]
edition = "2018"
[dependencies]
libchisel = { path = "../libchisel", version = "0.5.0" }
parity-wasm = "^0.35.6"
clap = "2.32.0"
serde = "1.0.80"
serde_derive = "1.0.80"
serde_yaml = "0.8.7"
This diff is collapsed.
[package]
name = "libchisel"
description = "Some useful utilities to transform WebAssembly binaries, most importantly for WebAssembly used in a deterministic / blockchain context"
version = "0.5.0"
authors = ["Alex Beregszaszi <alex@rtfs.hu>", "Jake Lang <jak3lang@gmail.com>"]
license = "Apache-2.0"
repository = "https://github.com/wasmx/wasm-chisel"
homepage = "https://github.com/wasmx/wasm-chisel"
categories = ["webassembly", "development tools"]
keywords = ["webassembly", "wasm", "blockchain", "ethereum"]
edition = "2018"
[dependencies]
parity-wasm = "^0.35.6"
rustc-hex = "1.0"
byteorder = "1.2.4"
failure = "0.1.5"
wabt = { version = "0.8.0", optional = true }
wasm-snip = "0.2.0"
[features]
default = []
use parity_wasm::elements::{Instruction, Module};
use super::{ModuleError, ModuleValidator};
/// Struct on which ModuleValidator is implemented.
pub struct CheckFloat {}
impl CheckFloat {
pub fn new() -> Self {
CheckFloat {}
}
}
impl ModuleValidator for CheckFloat {
fn validate(&self, module: &Module) -> Result<bool, ModuleError> {
let code_section = module.code_section();
if code_section.is_none() {
return Err(ModuleError::NotFound);
}
for function in code_section.unwrap().bodies() {
for instruction in function.code().elements() {
match instruction {
Instruction::F32Eq
| Instruction::F32Ne
| Instruction::F32Lt
| Instruction::F32Gt
| Instruction::F32Le
| Instruction::F32Ge
| Instruction::F32Abs
| Instruction::F32Neg
| Instruction::F32Ceil
| Instruction::F32Floor
| Instruction::F32Trunc
| Instruction::F32Nearest
| Instruction::F32Sqrt
| Instruction::F32Add
| Instruction::F32Sub
| Instruction::F32Mul
| Instruction::F32Div
| Instruction::F32Min
| Instruction::F32Max
| Instruction::F32Copysign
| Instruction::I32TruncSF32
| Instruction::I32TruncUF32
| Instruction::I64TruncSF32
| Instruction::I64TruncUF32
| Instruction::F32ConvertSI32
| Instruction::F32ConvertUI32
| Instruction::F32ConvertSI64
| Instruction::F32ConvertUI64
| Instruction::F32DemoteF64
| Instruction::F64PromoteF32
| Instruction::I32ReinterpretF32
| Instruction::F32ReinterpretI32
| Instruction::F32x4Splat
| Instruction::F32x4Eq
| Instruction::F32x4Ne
| Instruction::F32x4Lt
| Instruction::F32x4Le
| Instruction::F32x4Gt
| Instruction::F32x4Ge
| Instruction::F32x4Neg
| Instruction::F32x4Abs
| Instruction::F32x4Min
| Instruction::F32x4Max
| Instruction::F32x4Add
| Instruction::F32x4Sub
| Instruction::F32x4Div
| Instruction::F32x4Mul
| Instruction::F32x4Sqrt
| Instruction::F32x4ConvertSI32x4
| Instruction::F32x4ConvertUI32x4
| Instruction::I32x4TruncSF32x4Sat
| Instruction::I32x4TruncUF32x4Sat
| Instruction::F64Eq
| Instruction::F64Ne
| Instruction::F64Lt
| Instruction::F64Gt
| Instruction::F64Le
| Instruction::F64Ge
| Instruction::F64Abs
| Instruction::F64Neg
| Instruction::F64Ceil
| Instruction::F64Floor
| Instruction::F64Trunc
| Instruction::F64Nearest
| Instruction::F64Sqrt
| Instruction::F64Add
| Instruction::F64Sub
| Instruction::F64Mul
| Instruction::F64Div
| Instruction::F64Min
| Instruction::F64Max
| Instruction::F64Copysign
| Instruction::I32TruncSF64
| Instruction::I32TruncUF64
| Instruction::I64TruncSF64
| Instruction::I64TruncUF64
| Instruction::F64ConvertSI32
| Instruction::F64ConvertUI32
| Instruction::F64ConvertSI64
| Instruction::F64ConvertUI64
| Instruction::I64ReinterpretF64
| Instruction::F64ReinterpretI64
| Instruction::F64x2Splat
| Instruction::F64x2Eq
| Instruction::F64x2Ne
| Instruction::F64x2Lt
| Instruction::F64x2Le
| Instruction::F64x2Gt
| Instruction::F64x2Ge
| Instruction::F64x2Neg
| Instruction::F64x2Abs
| Instruction::F64x2Min
| Instruction::F64x2Max
| Instruction::F64x2Add
| Instruction::F64x2Sub
| Instruction::F64x2Div
| Instruction::F64x2Mul
| Instruction::F64x2Sqrt
| Instruction::F64x2ConvertSI64x2
| Instruction::F64x2ConvertUI64x2
| Instruction::I64x2TruncSF64x2Sat
| Instruction::I64x2TruncUF64x2Sat
| Instruction::F32x4ExtractLane(_)
| Instruction::F32x4ReplaceLane(_)
| Instruction::F64x2ExtractLane(_)
| Instruction::F64x2ReplaceLane(_)
| Instruction::F32Const(_)
| Instruction::F32Load(_, _)
| Instruction::F32Store(_, _)
| Instruction::F64Const(_)
| Instruction::F64Load(_, _)
| Instruction::F64Store(_, _) => {
return Ok(false);
}
_ => {}
}
}
}
Ok(true)
}
}
#[cfg(test)]
mod tests {
use parity_wasm::builder;
use parity_wasm::elements::deserialize_buffer;
use super::*;
#[test]
fn add_i32_no_fp() {
// (module
// (func $add (param $lhs i32) (param $rhs i32) (result i32)
// get_local $lhs
// get_local $rhs
// i32.add)
// (export "add" (func $add))
// )
let wasm: Vec<u8> = vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x07, 0x01, 0x60, 0x02, 0x7f,
0x7f, 0x01, 0x7f, 0x03, 0x02, 0x01, 0x00, 0x07, 0x07, 0x01, 0x03, 0x61, 0x64, 0x64,
0x00, 0x00, 0x0a, 0x09, 0x01, 0x07, 0x00, 0x20, 0x00, 0x20, 0x01, 0x6a, 0x0b,
];
let module = deserialize_buffer::<Module>(&wasm).unwrap();
let checker = CheckFloat::new();
let result = checker.validate(&module).unwrap();
assert_eq!(true, result);
}
#[test]
fn add_f32_fp() {
// (module
// (func $add (param $lhs f32) (param $rhs f32) (result f32)
// get_local $lhs
// get_local $rhs
// f32.add)
// (export "add" (func $add))
// )
let wasm: Vec<u8> = vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x07, 0x01, 0x60, 0x02, 0x7d,
0x7d, 0x01, 0x7d, 0x03, 0x02, 0x01, 0x00, 0x07, 0x07, 0x01, 0x03, 0x61, 0x64, 0x64,
0x00, 0x00, 0x0a, 0x09, 0x01, 0x07, 0x00, 0x20, 0x00, 0x20, 0x01, 0x92, 0x0b,
];
let module = deserialize_buffer::<Module>(&wasm).unwrap();
let checker = CheckFloat::new();
let result = checker.validate(&module).unwrap();
assert_eq!(false, result);
}
#[test]
fn add_f64_fp() {
// (module
// (func $add (param $lhs f64) (param $rhs f64) (result f64)
// get_local $lhs
// get_local $rhs
// f64.add)
// (export "add" (func $add))
// )
let wasm: Vec<u8> = vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x07, 0x01, 0x60, 0x02, 0x7c,
0x7c, 0x01, 0x7c, 0x03, 0x02, 0x01, 0x00, 0x07, 0x07, 0x01, 0x03, 0x61, 0x64, 0x64,
0x00, 0x00, 0x0a, 0x09, 0x01, 0x07, 0x00, 0x20, 0x00, 0x20, 0x01, 0xa0, 0x0b,
];
let module = deserialize_buffer::<Module>(&wasm).unwrap();
let checker = CheckFloat::new();
let result = checker.validate(&module).unwrap();
assert_eq!(false, result);
}
#[test]
fn no_code_section() {
let module = builder::module().build();
let checker = CheckFloat::new();
let result = checker.validate(&module);
assert_eq!(true, result.is_err());
assert_eq!(result.err().unwrap(), ModuleError::NotFound)
}
}
use super::{ModuleError, ModuleValidator};
use parity_wasm::elements::Module;
/// Struct on which ModuleValidator is implemented.
pub struct CheckStartFunc {
start_required: bool,
}
impl CheckStartFunc {
pub fn new(is_start_required: bool) -> Self {
CheckStartFunc {
start_required: is_start_required,
}
}
}
impl ModuleValidator for CheckStartFunc {
fn validate(&self, module: &Module) -> Result<bool, ModuleError> {
Ok(module.start_section().is_some() == self.start_required)
}
}
#[cfg(test)]
mod tests {
use super::*;
use parity_wasm::elements::deserialize_buffer;
#[test]
fn start_required_good() {
let wasm: Vec<u8> = vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01, 0x60, 0x00, 0x00,
0x03, 0x02, 0x01, 0x00, 0x07, 0x08, 0x01, 0x04, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00,
0x08, 0x01, 0x00, 0x0a, 0x04, 0x01, 0x02, 0x00, 0x0b,
];
let module = deserialize_buffer::<Module>(&wasm).unwrap();
let checker = CheckStartFunc::new(true);
let result = checker.validate(&module).unwrap();
assert_eq!(true, result);
}
#[test]
fn start_required_bad() {
let wasm: Vec<u8> = vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01, 0x60, 0x00, 0x00,
0x03, 0x02, 0x01, 0x00, 0x07, 0x08, 0x01, 0x04, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00,
0x08, 0x01, 0x00, 0x0a, 0x04, 0x01, 0x02, 0x00, 0x0b,
];
let module = deserialize_buffer::<Module>(&wasm).unwrap();
let checker = CheckStartFunc::new(false);
let result = checker.validate(&module).unwrap();
assert_eq!(false, result);
}
#[test]
fn start_not_required_good() {
let wasm: Vec<u8> = vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01, 0x60, 0x00, 0x00,
0x03, 0x02, 0x01, 0x00, 0x07, 0x08, 0x01, 0x04, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00,
0x0a, 0x04, 0x01, 0x02, 0x00, 0x0b,
];
let module = deserialize_buffer::<Module>(&wasm).unwrap();
let checker = CheckStartFunc::new(false);
let result = checker.validate(&module).unwrap();
assert_eq!(true, result);
}
#[test]
fn start_not_required_bad() {
let wasm: Vec<u8> = vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01, 0x60, 0x00, 0x00,
0x03, 0x02, 0x01, 0x00, 0x07, 0x08, 0x01, 0x04, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00,
0x0a, 0x04, 0x01, 0x02, 0x00, 0x0b,
];
let module = deserialize_buffer::<Module>(&wasm).unwrap();
let checker = CheckStartFunc::new(true);
let result = checker.validate(&module).unwrap();
assert_eq!(false, result);
}
}
This diff is collapsed.
This diff is collapsed.
use super::{ModuleError, ModuleTranslator};
use parity_wasm::elements::*;
/// Enum on which ModuleTranslator is implemented.
pub enum DropSection<'a> {
NamesSection,
/// Name of the custom section.
CustomSectionByName(&'a String),
/// Index of the custom section.
CustomSectionByIndex(usize),
/// Index of the unknown section.
UnknownSectionByIndex(usize),
}
fn names_section_index_for(module: &Module) -> Option<usize> {
module.sections().iter().position(|e| {
match e {
// The default case, when the section was not parsed by parity-wasm
Section::Custom(_section) => _section.name() == "name",
// This is the case, when the section was parsed by parity-wasm
Section::Name(_) => true,
_ => false,
}
})
}
fn custom_section_index_for(module: &Module, name: &str) -> Option<usize> {
module.sections().iter().position(|e| match e {
Section::Custom(_section) => _section.name() == name,
_ => false,
})
}
impl<'a> DropSection<'a> {
fn find_index(&self, module: &Module) -> Option<usize> {
match &self {
DropSection::NamesSection => names_section_index_for(module),
DropSection::CustomSectionByName(name) => custom_section_index_for(module, &name),
DropSection::CustomSectionByIndex(index) => Some(*index),
DropSection::UnknownSectionByIndex(index) => Some(*index),
}
}
fn drop_section(&self, module: &mut Module) -> Result<bool, ModuleError> {
let index = self.find_index(&module);
if index.is_none() {
return Ok(false);
}
let index = index.unwrap();
let sections = module.sections_mut();
if index >= sections.len() {
return Ok(false);
}
sections.remove(index);
Ok(true)
}
}
impl<'a> ModuleTranslator for DropSection<'a> {
fn translate_inplace(&self, module: &mut Module) -> Result<bool, ModuleError> {
Ok(self.drop_section(module)?)
}
fn translate(&self, module: &Module) -> Result<Option<Module>, ModuleError> {
let mut ret = module.clone();
if self.drop_section(&mut ret)? {
Ok(Some(ret))
} else {
Ok(None)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use parity_wasm::builder;
#[test]
fn keep_intact() {
let mut module = builder::module().build();
let name = "empty".to_string();
let dropper = DropSection::CustomSectionByName(&name);
let did_change = dropper.translate_inplace(&mut module).unwrap();
assert_eq!(did_change, false);
}
#[test]
fn keep_intact_custom_section() {
let mut module = builder::module()
.with_section(Section::Custom(CustomSection::new(
"test".to_string(),
vec![],
)))
.build();
let name = "empty".to_string();
let dropper = DropSection::CustomSectionByName(&name);
let did_change = dropper.translate_inplace(&mut module).unwrap();
assert_eq!(did_change, false);
}
#[test]
fn remove_custom_section() {
let mut module = builder::module()
.with_section(Section::Custom(CustomSection::new(
"test".to_string(),
vec![],
)))
.build();
let name = "test".to_string();
let dropper = DropSection::CustomSectionByName(&name);
let did_change = dropper.translate_inplace(&mut module).unwrap();
assert_eq!(did_change, true);
}
#[test]
fn remove_custom_section_by_index() {
let mut module = builder::module()
.with_section(Section::Custom(CustomSection::new(
"test".to_string(),
vec![],
)))
.build();
let dropper = DropSection::CustomSectionByIndex(0);
let did_change = dropper.translate_inplace(&mut module).unwrap();
assert_eq!(did_change, true);
}
#[test]
fn remove_oob_custom_section_by_index() {
let mut module = builder::module()
.with_section(Section::Custom(CustomSection::new(
"test".to_string(),
vec![],
)))
.build();
let dropper = DropSection::CustomSectionByIndex(1);
let did_change = dropper.translate_inplace(&mut module).unwrap();
assert_eq!(did_change, false);
}
#[test]
fn remove_custom_unknown_by_index() {
let mut module = builder::module()
.with_section(Section::Custom(CustomSection::new(
"test".to_string(),
vec![],
)))
.build();
let dropper = DropSection::UnknownSectionByIndex(0);
let did_change = dropper.translate_inplace(&mut module).unwrap();
assert_eq!(did_change, true);
}
#[test]
fn remove_oob_unknown_section_by_index() {
let mut module = builder::module()
.with_section(Section::Custom(CustomSection::new(
"test".to_string(),
vec![],
)))
.build();
let dropper = DropSection::UnknownSectionByIndex(1);
let did_change = dropper.translate_inplace(&mut module).unwrap();
assert_eq!(did_change, false);
}
}
use super::{ModuleCreator, ModuleError};
use crate::utils::*;
use parity_wasm::elements::{deserialize_buffer, Module};
use wabt::Wat2Wasm;
/// Struct on which ModuleCreator is implemented.
pub struct FromWat<'a> {
filename: &'a str,
}
impl<'a> FromWat<'a> {
pub fn new(filename: &'a str) -> Result<Self, ()> {
Ok(FromWat { filename: filename })
}
}
fn load_file(filename: &str) -> Result<Vec<u8>, ModuleError> {
use std::fs::File;
use std::io::prelude::*;
let mut file = File::open(filename)?;
let mut buf = Vec::new();
file.read_to_end(&mut buf)?;
Ok(buf)
}
impl From<wabt::Error> for ModuleError {
fn from(error: wabt::Error) -> Self {
use std::error::Error;
ModuleError::Custom(error.description().to_string())
}
}
fn convert_wat(input: &[u8]) -> Result<Module, ModuleError> {
// TODO: turn on relocatable(true)?
let module = Wat2Wasm::new()
.canonicalize_lebs(true)
.write_debug_names(true)
.convert(&input)?;
let module = deserialize_buffer::<Module>(module.as_ref())?;
Ok(module)
}
impl<'a> ModuleCreator for FromWat<'a> {
fn create(&self) -> Result<Module, ModuleError> {
let input = load_file(&self.filename)?;
convert_wat(&input)
}
}
#[cfg(test)]
mod tests {
use super::*;
use parity_wasm::elements::*;
#[test]
fn smoke() {
let expectation: Vec<u8> = vec![
0, 97, 115, 109, 1, 0, 0, 0, 0, 8, 4, 110, 97, 109, 101, 2, 1, 0,
];
let module = convert_wat(&r#"(module)"#.as_bytes()).unwrap();
let result = serialize::<Module>(module).unwrap();
assert_eq!(result, expectation);
}
#[test]
fn simple_function() {
let expectation: Vec<u8> = vec![
0, 97, 115, 109, 1, 0, 0, 0, 1, 9, 2, 96, 2, 127, 127, 0, 96, 0, 0, 2, 19, 1, 8, 101,
116, 104, 101, 114, 101, 117, 109, 6, 102, 105, 110, 105, 115, 104, 0, 0, 3, 2, 1, 1,
7, 8, 1, 4, 109, 97, 105, 110, 0, 1, 10, 10, 1, 8, 0, 65, 0, 65, 0, 16, 0, 11, 0, 27,
4, 110, 97, 109, 101, 1, 9, 1, 0, 6, 102, 105, 110, 105, 115, 104, 2, 9, 2, 0, 2, 0, 0,
1, 0, 1, 0,
];
let module = convert_wat(
&r#"
(module
(import "ethereum" "finish" (func $finish (param i32 i32)))
(func (export "main")
(call $finish (i32.const 0) (i32.const 0))
)
)
"#
.as_bytes(),
)
.unwrap();
let result = serialize::<Module>(module).unwrap();
assert_eq!(result, expectation);
}
}
This diff is collapsed.
extern crate byteorder;
extern crate parity_wasm;
extern crate rustc_hex;
use parity_wasm::elements::Module;
pub mod imports;
pub mod checkfloat;
pub mod checkstartfunc;
pub mod deployer;
pub mod dropsection;
#[cfg(feature = "wabt")]
pub mod fromwat;
pub mod remapimports;
pub mod remapstart;
pub mod repack;
pub mod snip;
pub mod trimexports;
pub mod trimstartfunc;
pub mod verifyexports;
pub mod verifyimports;
mod depgraph;
mod utils;
use std::{error, fmt};
#[derive(Eq, PartialEq, Debug)]
pub enum ModuleError {
NotSupported,
NotFound,
Custom(String),
}
pub trait ModuleCreator {
/// Returns new module.
fn create(&self) -> Result<Module, ModuleError>;
}
pub trait ModuleTranslator {
/// Translates module. Returns new module or none if nothing was modified. Can fail with ModuleError::NotSupported.
fn translate(&self, module: &Module) -> Result<Option<Module>, ModuleError>;
/// Translates module in-place. Returns true if the module was modified. Can fail with ModuleError::NotSupported.
fn translate_inplace(&self, module: &mut Module) -> Result<bool, ModuleError>;
}
pub trait ModuleValidator {
/// Validates module. Returns true if it is valid or false if invalid.
fn validate(&self, module: &Module) -> Result<bool, ModuleError>;
}
pub trait ModulePreset {
fn with_preset(preset: &str) -> Result<Self, ()>
where
Self: std::marker::Sized;
}
impl From<String> for ModuleError {
fn from(error: String) -> Self {
ModuleError::Custom(error)
}
}
impl From<std::io::Error> for ModuleError {
fn from(error: std::io::Error) -> Self {
use std::error::Error;
ModuleError::Custom(error.description().to_string())
}
}
impl fmt::Display for ModuleError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}",
match self {
ModuleError::NotSupported => "Method unsupported",
ModuleError::NotFound => "Not found",
ModuleError::Custom(msg) => msg,
}
)
}
}
impl error::Error for ModuleError {
fn description(&self) -> &str {
match self {
ModuleError::NotSupported => "Method unsupported",
ModuleError::NotFound => "Not found",
ModuleError::Custom(msg) => msg,
}
}
fn cause(&self) -> Option<&error::Error> {
None
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::error::Error;
struct SampleModule {}
impl ModuleCreator for SampleModule {
fn create(&self) -> Result<Module, ModuleError> {
Ok(Module::default())
}
}
impl ModuleTranslator for SampleModule {
fn translate(&self, _module: &Module) -> Result<Option<Module>, ModuleError> {
Ok(Some(Module::default()))
}
fn translate_inplace(&self, _module: &mut Module) -> Result<bool, ModuleError> {
Ok(true)
}
}
impl ModuleValidator for SampleModule {
fn validate(&self, _module: &Module) -> Result<bool, ModuleError> {
Ok(true)
}
}
#[test]
fn creator_succeeds() {
let creator = SampleModule {};
let result = creator.create();
assert!(result.is_ok());
}
#[test]
fn translator_succeeds() {
let translator = SampleModule {};
let result = translator.translate(&Module::default());
assert!(result.is_ok());
}
#[test]
fn translator_inplace_succeeds() {
let translator = SampleModule {};
let result = translator.translate_inplace(&mut Module::default());
assert!(result.is_ok());
}
#[test]
fn validator_succeeds() {
let validator = SampleModule {};
let result = validator.validate(&Module::default());
assert!(result.is_ok());
}
#[test]
fn from_error() {
let err: ModuleError = "custom message".to_string().into();
assert_eq!(err, ModuleError::Custom("custom message".to_string()));
}
#[test]
fn fmt_good() {
// Add new tests for each enum variant here as they are implemented.
let fmt_result_unsupported = format!("{}", ModuleError::NotSupported);
assert_eq!("Method unsupported", fmt_result_unsupported);
let fmt_result_custom = format!("{}", ModuleError::Custom("foo".to_string()));
assert_eq!("foo", fmt_result_custom);
}
#[test]
fn error_good() {
// Add new tests for each enum variant here as they are implemented.
let err_unsupported = ModuleError::NotSupported;
let err_description_unsupported = err_unsupported.description();
assert_eq!("Method unsupported", err_description_unsupported);
let err_custom = ModuleError::Custom("bar".to_string());
let err_description_custom = err_custom.description();
assert_eq!("bar", err_description_custom);
}
}
This diff is collapsed.
use parity_wasm::elements::*;
use super::{ModuleError, ModulePreset, ModuleTranslator};
pub struct RemapStart;
impl ModulePreset for RemapStart {
fn with_preset(preset: &str) -> Result<Self, ()> {
match preset {
// TODO refactor this later
"ewasm" => Ok(RemapStart {}),
_ => Err(()),
}
}
}
impl ModuleTranslator for RemapStart {
fn translate_inplace(&self, module: &mut Module) -> Result<bool, ModuleError> {
Ok(remap_start(module))
}
fn translate(&self, module: &Module) -> Result<Option<Module>, ModuleError> {
let mut ret = module.clone();
if remap_start(&mut ret) {
Ok(Some(ret))
} else {
Ok(None)
}
}
}
// NOTE: This seems to be exported properly in later versions of parity-wasm.
// TODO: When updated, use the proper method instead.
fn section_order(s: &Section) -> u8 {
match s {
Section::Custom(_) => 0x00,
Section::Unparsed { .. } => 0x00,
Section::Type(_) => 0x1,
Section::Import(_) => 0x2,
Section::Function(_) => 0x3,
Section::Table(_) => 0x4,
Section::Memory(_) => 0x5,
Section::Global(_) => 0x6,
Section::Export(_) => 0x7,
Section::Start(_) => 0x8,
Section::Element(_) => 0x9,
Section::Code(_) => 0x0a,
Section::Data(_) => 0x0b,
Section::Name(_) => 0x00,
Section::Reloc(_) => 0x00,
}
}
/// Replace an exported function with another function, or export if unexported.
fn remap_or_export_main(module: &mut Module, export_name: &str, func_idx: u32) {
let new_func_export = ExportEntry::new(export_name.to_string(), Internal::Function(func_idx));
if let Some(export_section) = module.export_section_mut() {
let export_section = export_section.entries_mut();
// If we find an export named `export_name`, replace it. Otherwise, append an entry to the
// section with the supplied func index.
if let Some(main_export_loc) = export_section
.iter_mut()
.position(|e| e.field() == export_name)
{
export_section[main_export_loc] = new_func_export;
} else {
export_section.push(new_func_export);
}
} else {
let sections = module.sections_mut();
let new_export_section =
Section::Export(ExportSection::with_entries(vec![new_func_export]));
// If we can find a section that is supposed to be after exports, insert the new section at its position (shifts other elements to the right).
// Otherwise, append it at the end.
// NOTE: Assumes that the ordering of sections is otherwise correct.
if let Some(exports_loc) = sections.iter().position(|sec| section_order(&sec) > 0x7) {
sections.insert(exports_loc, new_export_section);
} else {
sections.push(new_export_section);
}
}
}
fn remap_start(module: &mut Module) -> bool {
if let Some(start_func_idx) = module.start_section() {
// Look for an export "main". If found, replace it with an export of the function to
// which the start section points.
remap_or_export_main(module, "main", start_func_idx);
// Remove the start section, leaving the "main" export as the entry point.
module.clear_start_section();
true
} else {
false
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::utils::SerializationHelpers;
use crate::{ModulePreset, ModuleTranslator};
use rustc_hex::FromHex;
#[test]
fn remapstart_mutation() {
//wat:
//(module
// (import "env" "ethereum_useGas" (func (param i64)))
// (memory 1)
// (export "main" (func $main))
// (export "memory" (memory 0))
// (func $main2)
// (func $main)
// (start $main2)
//)
let wasm: Vec<u8> = FromHex::from_hex(
"0061736d0100000001080260017e0060
000002170103656e760f657468657265756d5f75736547617300000303020101050301000107110
2046d61696e0001066d656d6f727902000801020a070202000b02000b0020046e616d65010e0201
046d61696e02056d61696e320209030001000001000200",
)
.unwrap();
let mut module = Module::from_slice(&wasm);
module = module.parse_names().unwrap();
assert!(module.names_section().is_some());
let start_idx = module
.start_section()
.expect("Module missing start function");
let new = RemapStart::with_preset("ewasm")
.unwrap()
.translate(&module)
.expect("Module internal error")
.expect("new module not returned");
assert!(
new.start_section().is_none(),
"start section wasn't removed"
);
assert!(new
.export_section()
.expect("Module missing export section")
.entries()
.iter()
.find(|e| e.field() == String::from("main")
&& *e.internal() == Internal::Function(start_idx))
.is_some());
}
#[test]
fn remapstart_no_mutation() {
// (module
// (import "env" "ethereum_useGas" (func (param i64)))
// (memory 1)
// (export "main" (func $main))
// (export "memory" (memory 0))
// (func $main)
//)
let wasm: Vec<u8> = FromHex::from_hex(
"0061736d0100000001080260017e0060
000002170103656e760f657468657265756d5f757365476173000003020101050301000
1071102046d61696e0001066d656d6f727902000a040102000b",
)
.unwrap();
let module = Module::from_slice(&wasm);
let new = RemapStart::with_preset("ewasm")
.unwrap()
.translate(&module)
.expect("Module internal error");
assert!(new.is_none());
}
#[test]
fn remapstart_inplace_mutation() {
//wat:
//(module
// (import "env" "ethereum_useGas" (func (param i64)))
// (memory 1)
// (export "main" (func $main))
// (export "memory" (memory 0))
// (func $main2)
// (func $main)
// (start $main2)
//)
let wasm: Vec<u8> = FromHex::from_hex(
"0061736d0100000001080260017e0060
000002170103656e760f657468657265756d5f75736547617300000303020101050301000107110
2046d61696e0001066d656d6f727902000801020a070202000b02000b0020046e616d65010e0201
046d61696e02056d61696e320209030001000001000200",
)
.unwrap();
let mut module = Module::from_slice(&wasm);
module = module.parse_names().unwrap();
assert!(module.names_section().is_some());
let res = RemapStart::with_preset("ewasm")
.unwrap()
.translate_inplace(&mut module)
.unwrap();
assert!(res, "module was not modified");
assert!(
module.start_section().is_none(),
"start section wasn't removed"
);
}
#[test]
fn remapstart_inplace_no_mutation() {
// (module
// (import "env" "ethereum_useGas" (func (param i64)))
// (memory 1)
// (export "main" (func $main))
// (export "memory" (memory 0))
// (func $main)
//)
let wasm: Vec<u8> = FromHex::from_hex(
"0061736d0100000001080260017e0060
000002170103656e760f657468657265756d5f75736547617300000302010105030100010711020
46d61696e0001066d656d6f727902000a040102000b",
)
.unwrap();
let mut module = Module::from_slice(&wasm);
let res = RemapStart::with_preset("ewasm")
.unwrap()
.translate_inplace(&mut module)
.unwrap();
assert!(!res, "module was modified");
}
#[test]
fn remapstart_mutation_no_exports() {
//wat:
//(module
// (import "env" "ethereum_useGas" (func (param i64)))
// (memory 1)
// (func $main2)
// (func $main)
// (start $main2)
//)
let wasm: Vec<u8> = FromHex::from_hex(
"0061736d0100000001080260017e0060000002170103656e760f657468657265756d5f7573654761730000030302010105030100010801010a070202000b02000b",
)
.unwrap();
let mut module = Module::from_slice(&wasm);
let res = RemapStart::with_preset("ewasm")
.unwrap()
.translate_inplace(&mut module)
.unwrap();
assert!(res, "module was not modified");
assert!(
module.export_section().is_some(),
"export section does not exist"
);
}
#[test]
fn export_section_exists_but_no_main() {
// wat:
// (module
// (import "env" "ethereum_useGas" (func (param i64)))
// (memory 1)
// (start $main)
// (export "memory" (memory 0))
// (func $main)
// )
let wasm: Vec<u8> = FromHex::from_hex(
"0061736d0100000001080260017e0060000002170103656e760f657468657265756d5f7573654761730000030201010503010001070a01066d656d6f727902000801010a040102000b"
).unwrap();
let mut module = Module::from_slice(&wasm);
let remapper = RemapStart::with_preset("ewasm").expect("Can't fail");
let res = remapper.translate_inplace(&mut module);
assert!(res.is_ok());
let mutated = res.unwrap();
assert_eq!(mutated, true);
assert!(module.export_section().is_some());
assert!(module.start_section().is_none());
assert!(module
.export_section()
.unwrap()
.entries()
.iter()
.find(|e| e.field() == "main")
.is_some());
}
}
use super::{ModuleError, ModuleTranslator};
use parity_wasm::builder;
use parity_wasm::elements::*;
pub struct Repack;
impl Repack {
pub fn new() -> Self {
Repack {}
}
}
impl ModuleTranslator for Repack {
fn translate_inplace(&self, _module: &mut Module) -> Result<bool, ModuleError> {
Err(ModuleError::NotSupported)
}
fn translate(&self, module: &Module) -> Result<Option<Module>, ModuleError> {
// TODO: check in names section is carried over.
let module = module.clone();
let module = builder::from_module(module).build();
Ok(Some(module))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::utils::*;
use parity_wasm::builder;
use rustc_hex::FromHex;
#[test]
fn smoke_test() {
let module = Module::default();
let repack = Repack::new();
assert_eq!(module, repack.translate(&module).unwrap().unwrap());
}
#[test]
fn basic_sections_only() {
let module = builder::module()
.function()
.signature()
.build()
.body()
.build()
.build()
.export()
.field("main")
.internal()
.func(0)
.build()
.export()
.field("memory")
.internal()
.memory(0)
.build()
.build();
let repack = Repack::new();
assert_eq!(module, repack.translate(&module).unwrap().unwrap());
}
#[test]
fn custom_section() {
let mut module = builder::module()
.function()
.signature()
.build()
.body()
.build()
.build()
.export()
.field("main")
.internal()
.func(0)
.build()
.export()
.field("memory")
.internal()
.memory(0)
.build()
.build();
let custom = CustomSection::new("test".to_string(), vec![42u8; 16]);
module
.sections_mut()
.push(parity_wasm::elements::Section::Custom(custom));
let repack = Repack::new();
assert_ne!(module, repack.translate(&module).unwrap().unwrap());
}
#[test]
fn names_section() {
let input = FromHex::from_hex(
"0061736d010000000104016000000303020000070801046d61696e00010a
0a020300010b040010000b0014046e616d65010d0200047465737401046d
61696e",
)
.unwrap();
let module = Module::from_slice(&input);
// Forcefully parse names section here.
let module = module
.parse_names()
.expect("parsing the names section failed");
assert_eq!(module.names_section().is_some(), true);
let repack = Repack::new();
// Repack drops names section too.
let output = repack.translate(&module).unwrap().unwrap();
assert_eq!(output.has_names_section(), false);
}
}
use super::{ModuleError, ModuleTranslator};
use parity_wasm::elements::Module;
#[derive(Clone)]
pub struct Snip(wasm_snip::Options);
impl Snip {
pub fn new() -> Self {
let mut options = wasm_snip::Options::default();
// TODO: expose these as options
options.snip_rust_fmt_code = true;
options.snip_rust_panicking_code = true;
options.skip_producers_section = true;
Snip { 0: options }
}
}
impl From<failure::Error> for ModuleError {
fn from(error: failure::Error) -> Self {
ModuleError::Custom(error.to_string())
}
}
impl ModuleTranslator for Snip {
fn translate_inplace(&self, _module: &mut Module) -> Result<bool, ModuleError> {
Err(ModuleError::NotSupported)
}
fn translate(&self, module: &Module) -> Result<Option<Module>, ModuleError> {
// TODO: there must be a better way to accomplish this.
let serialized = parity_wasm::elements::serialize::<Module>(module.clone())?;
// TODO: improve wasm-snip API...
let mut options = self.0.clone();
options.input = wasm_snip::Input::Buffer(serialized);
let ret = wasm_snip::snip(options)?;
let output = ret.emit_wasm()?;
let output = parity_wasm::elements::deserialize_buffer::<Module>(&output[..])?;
Ok(Some(output))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::utils::SerializationHelpers;
use rustc_hex::FromHex;
#[test]
fn smoke_test() {
// (module
// (import "env" "ethereum_useGas" (func (param i64)))
// (memory 1)
// (export "main" (func $main))
// (export "memory" (memory 0))
// (func $main
// (call $std::panicking::rust_panic_with_hook::h12b7239ed4348eae)
// (call $core::fmt::write::h9f284ae8e8e9b94a)
// )
// (func $std::panicking::rust_panic_with_hook::h12b7239ed4348eae)
// (func $core::fmt::write::h9f284ae8e8e9b94a)
// )
let wasm: Vec<u8> = FromHex::from_hex(
"0061736d0100000001080260017e0060000002170103656e760f65746865
7265756d5f75736547617300000304030101010503010001071102046d61
696e0001066d656d6f727902000a10030600100210030b0300010b030001
0b007f046e616d650178040011696d706f72742466756e6374696f6e2430
01046d61696e02377374643a3a70616e69636b696e673a3a727573745f70
616e69635f776974685f686f6f6b3a3a6831326237323339656434333438
6561650323636f72653a3a666d743a3a77726974653a3a68396632383461
65386538653962393461",
)
.unwrap();
let module = Module::from_slice(&wasm);
let module = Snip::new().translate(&module);
let module = module
.expect("translation to be succesful")
.expect("new module to be returned");
assert!(module.to_vec().len() < wasm.len());
}
}
use super::{ModuleError, ModulePreset, ModuleTranslator};
use parity_wasm::elements::*;
/// Struct containing a list of valid exports.
struct ExportWhitelist {
pub entries: Vec<ExportEntry>,
}
/// Wrapper struct implementing ModuleTranslator.
/// Removes any exports that are noncompliant with a specified interface.
pub struct TrimExports {
whitelist: ExportWhitelist,
}
/// Helper that compares the enum variant of two WebAssembly exports.
fn cmp_internal_variant(a: &Internal, b: &Internal) -> bool {
std::mem::discriminant(a) == std::mem::discriminant(b)
}
impl ModulePreset for ExportWhitelist {
fn with_preset(preset: &str) -> Result<Self, ()> {
match preset {
"ewasm" => Ok(ExportWhitelist {
entries: vec![
//NOTE: function signatures are not checked yet
ExportEntry::new("main".to_string(), Internal::Function(0)),
ExportEntry::new("memory".to_string(), Internal::Memory(0)),
],
}),
"pwasm" => Ok(ExportWhitelist {
entries: vec![ExportEntry::new("_call".to_string(), Internal::Function(0))],
}),
_ => Err(()),
}
}
}
impl ExportWhitelist {
/// Constructs an empty whitelist. Mostly useless.
fn new() -> Self {
ExportWhitelist {
entries: Vec::new(),
}
}
/// Looks up a given export entry in the whitelist and returns true if it is valid.
fn lookup(&self, export: &ExportEntry) -> bool {
self.entries
.iter()
.find(|matched_export| {
export.field() == matched_export.field()
&& cmp_internal_variant(export.internal(), matched_export.internal())
})
.is_some()
}
}
impl TrimExports {
/// Constructs an empty `trimexports` context.
pub fn new() -> Self {
TrimExports {
whitelist: ExportWhitelist::new(),
}
}
/// Takes a given preset string and constructs a context with the
/// corresponding whitelist.
pub fn with_preset(preset: &str) -> Result<Self, ()> {
match preset {
"ewasm" => Ok(TrimExports {
whitelist: ExportWhitelist::with_preset("ewasm").unwrap(),
}),
"pwasm" => Ok(TrimExports {
whitelist: ExportWhitelist::with_preset("pwasm").unwrap(),
}),
_ => Err(()),
}
}
/// Iterates over the export section, if there is one, and removes
/// unnecessary entries.
fn trim_exports(&self, module: &mut Module) -> bool {
if let Some(section) = module.export_section_mut() {
let new_section = ExportSection::with_entries(
section
.entries()
.iter()
.cloned()
.filter(|entry| self.whitelist.lookup(entry))
.collect(),
);
if new_section.entries().len() < section.entries().len() {
*section = new_section;
return true;
}
false
} else {
false
}
}
}
impl ModuleTranslator for TrimExports {
fn translate_inplace(&self, module: &mut Module) -> Result<bool, ModuleError> {
Ok(self.trim_exports(module))
}
fn translate(&self, module: &Module) -> Result<Option<Module>, ModuleError> {
let mut ret = module.clone();
let modified = self.trim_exports(&mut ret);
if modified {
return Ok(Some(ret));
}
Ok(None)
}
}
#[cfg(test)]
mod tests {
use super::*;
use parity_wasm::builder;
// Smoke tests
#[test]
fn builder_all_exports_good_ewasm() {
let mut module = builder::module()
.function()
.signature()
.build()
.body()
.build()
.build()
.export()
.field("main")
.internal()
.func(0)
.build()
.export()
.field("memory")
.internal()
.memory(0)
.build()
.build();
let trimmer = TrimExports::with_preset("ewasm").unwrap();
let did_change = trimmer.translate_inplace(&mut module).unwrap();
assert_eq!(false, did_change);
}
#[test]
fn builder_one_wrong_mem_export_ewasm() {
let mut module = builder::module()
.function()
.signature()
.build()
.body()
.build()
.build()
.export()
.field("main")
.internal()
.func(0)
.build()
.export()
.field("memory")
.internal()
.memory(0)
.build()
.export()
.field("foo")
.internal()
.memory(0)
.build()
.build();
let trimmer = TrimExports::with_preset("ewasm").unwrap();
let did_change = trimmer.translate_inplace(&mut module).unwrap();
assert_eq!(true, did_change);
}
#[test]
fn builder_no_export_ewasm() {
let mut module = builder::module()
.function()
.signature()
.build()
.body()
.build()
.build()
.build();
let trimmer = TrimExports::with_preset("ewasm").unwrap();
let did_change = trimmer.translate_inplace(&mut module).unwrap();
assert_eq!(false, did_change);
}
#[test]
fn builder_all_exports_good_pwasm() {
let mut module = builder::module()
.function()
.signature()
.build()
.body()
.build()
.build()
.export()
.field("_call")
.internal()
.func(0)
.build()
.build();
let trimmer = TrimExports::with_preset("pwasm").unwrap();
let did_change = trimmer.translate_inplace(&mut module).unwrap();
assert_eq!(false, did_change);
}
}
use super::{ModuleError, ModulePreset, ModuleTranslator};
use parity_wasm::elements::*;
pub struct TrimStartFunc;
impl TrimStartFunc {
fn trim_startfunc(&self, module: &mut Module) -> bool {
if let Some(_start_section) = module.start_section() {
module.clear_start_section();
true
} else {
false
}
}
}
impl ModulePreset for TrimStartFunc {
fn with_preset(preset: &str) -> Result<Self, ()> {
match preset {
"ewasm" => Ok(TrimStartFunc {}),
_ => Err(()),
}
}
}
impl ModuleTranslator for TrimStartFunc {
fn translate_inplace(&self, module: &mut Module) -> Result<bool, ModuleError> {
Ok(self.trim_startfunc(module))
}
fn translate(&self, _module: &Module) -> Result<Option<Module>, ModuleError> {
Err(ModuleError::NotSupported)
}
}
#[cfg(test)]
mod tests {
use super::*;
use parity_wasm::elements::deserialize_buffer;
#[test]
fn start_removed() {
let wasm: Vec<u8> = vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01, 0x60, 0x00, 0x00,
0x03, 0x02, 0x01, 0x00, 0x07, 0x08, 0x01, 0x04, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00,
0x08, 0x01, 0x00, 0x0a, 0x04, 0x01, 0x02, 0x00, 0x0b,
];
let mut module = deserialize_buffer::<Module>(&wasm).unwrap();
let trimmer = TrimStartFunc::with_preset("ewasm").unwrap();
trimmer.translate_inplace(&mut module).unwrap();
let result = serialize::<Module>(module).unwrap();
let expect: Vec<u8> = vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01, 0x60, 0x00, 0x00,
0x03, 0x02, 0x01, 0x00, 0x07, 0x08, 0x01, 0x04, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00,
0x0a, 0x04, 0x01, 0x02, 0x00, 0x0b,
];
assert_eq!(expect, result);
}
#[test]
fn start_not_removed() {
let wasm: Vec<u8> = vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01, 0x60, 0x00, 0x00,
0x03, 0x02, 0x01, 0x00, 0x07, 0x08, 0x01, 0x04, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00,
0x0a, 0x04, 0x01, 0x02, 0x00, 0x0b,
];
let mut module = deserialize_buffer::<Module>(&wasm).unwrap();
let trimmer = TrimStartFunc::with_preset("ewasm").unwrap();
trimmer.translate_inplace(&mut module).unwrap();
let result = serialize::<Module>(module).unwrap();
// result is equal to initial wasm (not changed)
assert_eq!(result, wasm);
}
}
//! These are helpers to be used internally.
use super::ModuleError;
use parity_wasm::elements::{deserialize_buffer, serialize, Module, Section};
pub trait HasNamesSection {
/// Returns true if the module has a NamesSection.
fn has_names_section(&self) -> bool;
}
impl HasNamesSection for Module {
fn has_names_section(&self) -> bool {
// TODO: move names_section_index_for helper from dropsection and consider upstreaming it
self.sections()
.iter()
.position(|e| {
match e {
// The default case, when the section was not parsed by parity-wasm
Section::Custom(_section) => _section.name() == "name",
// This is the case, when the section was parsed by parity-wasm
Section::Name(_) => true,
_ => false,
}
})
.is_some()
}
}
pub trait SerializationHelpers {
/// Deserialize bytecode to a Module.
fn from_slice(input: &[u8]) -> Module;
/// Serialize Module to bytecode. Serialization consumes the input.
fn to_vec(self) -> Vec<u8>;
}
impl SerializationHelpers for Module {
fn from_slice(input: &[u8]) -> Self {
deserialize_buffer::<Module>(&input).expect("invalid Wasm bytecode")
}
fn to_vec(self) -> Vec<u8> {
serialize::<Module>(self).expect("invalid Wasm module")
}
}
impl From<parity_wasm::SerializationError> for ModuleError {
fn from(a: parity_wasm::SerializationError) -> Self {
use std::error::Error;
ModuleError::Custom(a.description().to_string())
}
}
#[cfg(test)]
mod tests {
use super::*;
use rustc_hex::FromHex;
#[test]
fn module_roundtrip() {
let input = FromHex::from_hex(
"0061736d01000000010401600000030201000405017001010105030100100619
037f01418080c0000b7f00418080c0000b7f00418080c0000b072503066d656d
6f727902000b5f5f686561705f6261736503010a5f5f646174615f656e640302
0a040102000b",
)
.unwrap();
let module = Module::from_slice(&input);
let output = module.to_vec();
assert_eq!(input, output);
}
#[test]
fn bytecode_has_names_section() {
let input = FromHex::from_hex(
"0061736d010000000104016000000303020000070801046d61696e00010a
0a020300010b040010000b0014046e616d65010d0200047465737401046d
61696e",
)
.unwrap();
let module = Module::from_slice(&input);
assert_eq!(module.has_names_section(), true);
}
#[test]
fn bytecode_has_no_names_section() {
let input = FromHex::from_hex(
"0061736d010000000104016000000303020000070801046d61696e00010a
0a020300010b040010000b",
)
.unwrap();
let module = Module::from_slice(&input);
assert_eq!(module.has_names_section(), false);
}
fn try_serialize(module: Module) -> Result<Vec<u8>, ModuleError> {
Ok(serialize::<Module>(module)?)
}
fn try_deserialize(input: &[u8]) -> Result<Module, ModuleError> {
Ok(deserialize_buffer::<Module>(&input)?)
}
#[test]
fn test_serialize_error() {
// The failure case.
let module = parity_wasm::builder::module()
.export()
.field("invalid")
.internal()
.func(15)
.build()
.build();
// Shouldn't this one fail due to the invalid function reference?
assert_eq!(try_serialize(module).is_ok(), true);
// The success case.
assert_eq!(try_serialize(Module::default()).is_ok(), true)
}
#[test]
fn test_deserialize_error() {
// The failure case.
assert_eq!(try_deserialize(&[0u8; 0]).is_ok(), false);
// The success case.
let input = FromHex::from_hex(
"0061736d01000000010401600000030201000405017001010105030100100619
037f01418080c0000b7f00418080c0000b7f00418080c0000b072503066d656d
6f727902000b5f5f686561705f6261736503010a5f5f646174615f656e640302
0a040102000b",
)
.unwrap();
assert_eq!(try_deserialize(&input).is_ok(), true)
}
}
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment