mirror of
https://github.com/pezkuwichain/wasm-instrument.git
synced 2026-06-13 15:11:07 +00:00
Rename folder tools to cli to keep it consistent with crate naming
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
[package]
|
||||
name = "pwasm-utils-cli"
|
||||
version = "0.1.5"
|
||||
authors = ["Nikolay Volf <nikvolf@gmail.com>", "Sergey Pepyakin <s.pepyakin@gmail.com>"]
|
||||
license = "MIT/Apache-2.0"
|
||||
readme = "README.md"
|
||||
description = "Collection of command-line utilities and corresponding Rust api for producing pwasm-compatible executables"
|
||||
keywords = ["wasm", "webassembly", "pwasm"]
|
||||
|
||||
[lib]
|
||||
|
||||
[[bin]]
|
||||
name = "wasm-prune"
|
||||
path = "prune/main.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "wasm-ext"
|
||||
path = "ext/main.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "wasm-gas"
|
||||
path = "gas/main.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "wasm-build"
|
||||
path = "build/main.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "wasm-stack-height"
|
||||
path = "stack_height/main.rs"
|
||||
|
||||
[dependencies]
|
||||
parity-wasm = { git = "https://github.com/paritytech/parity-wasm" }
|
||||
pwasm-utils = { path = ".." }
|
||||
glob = "0.2"
|
||||
clap = "2.24"
|
||||
log = "0.4"
|
||||
env_logger = "0.5"
|
||||
lazy_static = "1.0"
|
||||
|
||||
[dev-dependencies]
|
||||
tempdir = "0.3"
|
||||
@@ -0,0 +1,269 @@
|
||||
//! Experimental build tool for cargo
|
||||
|
||||
extern crate glob;
|
||||
extern crate pwasm_utils as utils;
|
||||
extern crate clap;
|
||||
extern crate parity_wasm;
|
||||
extern crate pwasm_utils_cli as logger;
|
||||
|
||||
mod source;
|
||||
|
||||
use std::{fs, io};
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use clap::{App, Arg};
|
||||
use parity_wasm::elements;
|
||||
|
||||
use utils::{CREATE_SYMBOL, CALL_SYMBOL, ununderscore_funcs, externalize_mem, shrink_unknown_stack};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
Io(io::Error),
|
||||
FailedToCopy(String),
|
||||
Decoding(elements::Error, String),
|
||||
Encoding(elements::Error),
|
||||
Packing(utils::PackingError),
|
||||
Optimizer,
|
||||
}
|
||||
|
||||
impl From<io::Error> for Error {
|
||||
fn from(err: io::Error) -> Self {
|
||||
Error::Io(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<utils::OptimizerError> for Error {
|
||||
fn from(_err: utils::OptimizerError) -> Self {
|
||||
Error::Optimizer
|
||||
}
|
||||
}
|
||||
|
||||
impl From<utils::PackingError> for Error {
|
||||
fn from(err: utils::PackingError) -> Self {
|
||||
Error::Packing(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
|
||||
use Error::*;
|
||||
match *self {
|
||||
Io(ref io) => write!(f, "Generic i/o error: {}", io),
|
||||
FailedToCopy(ref msg) => write!(f, "{}. Have you tried to run \"cargo build\"?", msg),
|
||||
Decoding(ref err, ref file) => write!(f, "Decoding error ({}). Must be a valid wasm file {}. Pointed wrong file?", err, file),
|
||||
Encoding(ref err) => write!(f, "Encoding error ({}). Almost impossible to happen, no free disk space?", err),
|
||||
Optimizer => write!(f, "Optimization error due to missing export section. Pointed wrong file?"),
|
||||
Packing(ref e) => write!(f, "Packing failed due to module structure error: {}. Sure used correct libraries for building contracts?", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn wasm_path(input: &source::SourceInput) -> String {
|
||||
let mut path = PathBuf::from(input.target_dir());
|
||||
path.push(format!("{}.wasm", input.final_name()));
|
||||
path.to_string_lossy().to_string()
|
||||
}
|
||||
|
||||
pub fn process_output(input: &source::SourceInput) -> Result<(), Error> {
|
||||
let mut cargo_path = PathBuf::from(input.target_dir());
|
||||
let wasm_name = input.bin_name().to_string().replace("-", "_");
|
||||
cargo_path.push(
|
||||
match input.target() {
|
||||
source::SourceTarget::Emscripten => source::EMSCRIPTEN_TRIPLET,
|
||||
source::SourceTarget::Unknown => source::UNKNOWN_TRIPLET,
|
||||
}
|
||||
);
|
||||
cargo_path.push("release");
|
||||
cargo_path.push(format!("{}.wasm", wasm_name));
|
||||
|
||||
let mut target_path = PathBuf::from(input.target_dir());
|
||||
target_path.push(format!("{}.wasm", input.final_name()));
|
||||
fs::copy(cargo_path.as_path(), target_path.as_path())
|
||||
.map_err(|io| Error::FailedToCopy(
|
||||
format!("Failed to copy '{}' to '{}': {}", cargo_path.display(), target_path.display(), io)
|
||||
))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn has_ctor(module: &elements::Module) -> bool {
|
||||
if let Some(ref section) = module.export_section() {
|
||||
section.entries().iter().any(|e| CREATE_SYMBOL == e.field())
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn do_main() -> Result<(), Error> {
|
||||
logger::init_log();
|
||||
|
||||
let matches = App::new("wasm-build")
|
||||
.arg(Arg::with_name("target")
|
||||
.index(1)
|
||||
.required(true)
|
||||
.help("Cargo target directory"))
|
||||
.arg(Arg::with_name("wasm")
|
||||
.index(2)
|
||||
.required(true)
|
||||
.help("Wasm binary name"))
|
||||
.arg(Arg::with_name("skip_optimization")
|
||||
.help("Skip symbol optimization step producing final wasm")
|
||||
.long("skip-optimization"))
|
||||
.arg(Arg::with_name("enforce_stack_adjustment")
|
||||
.help("Enforce stack size adjustment (used for old wasm32-unknown-unknown)")
|
||||
.long("enforce-stack-adjustment"))
|
||||
.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"))
|
||||
.arg(Arg::with_name("source_target")
|
||||
.help("Cargo target type kind ('wasm32-unknown-unknown' or 'wasm32-unknown-emscripten'")
|
||||
.takes_value(true)
|
||||
.long("target"))
|
||||
.arg(Arg::with_name("final_name")
|
||||
.help("Final wasm binary name")
|
||||
.takes_value(true)
|
||||
.long("final"))
|
||||
.arg(Arg::with_name("save_raw")
|
||||
.help("Save intermediate raw bytecode to path")
|
||||
.takes_value(true)
|
||||
.long("save-raw"))
|
||||
.arg(Arg::with_name("shrink_stack")
|
||||
.help("Shrinks the new stack size for wasm32-unknown-unknown")
|
||||
.takes_value(true)
|
||||
.long("shrink-stack"))
|
||||
.get_matches();
|
||||
|
||||
let target_dir = matches.value_of("target").expect("is required; qed");
|
||||
let wasm_binary = matches.value_of("wasm").expect("is required; qed");
|
||||
let mut source_input = source::SourceInput::new(target_dir, wasm_binary);
|
||||
|
||||
let source_target_val = matches.value_of("source_target").unwrap_or_else(|| source::EMSCRIPTEN_TRIPLET);
|
||||
if source_target_val == source::UNKNOWN_TRIPLET {
|
||||
source_input = source_input.unknown()
|
||||
} else if source_target_val == source::EMSCRIPTEN_TRIPLET {
|
||||
source_input = source_input.emscripten()
|
||||
} else {
|
||||
eprintln!("--target can be: '{}' or '{}'", source::EMSCRIPTEN_TRIPLET, source::UNKNOWN_TRIPLET);
|
||||
::std::process::exit(1);
|
||||
}
|
||||
|
||||
if let Some(final_name) = matches.value_of("final_name") {
|
||||
source_input = source_input.with_final(final_name);
|
||||
}
|
||||
|
||||
process_output(&source_input)?;
|
||||
|
||||
let path = wasm_path(&source_input);
|
||||
|
||||
let mut module = parity_wasm::deserialize_file(&path)
|
||||
.map_err(|e| Error::Decoding(e, path.to_string()))?;
|
||||
|
||||
if let source::SourceTarget::Emscripten = source_input.target() {
|
||||
module = ununderscore_funcs(module);
|
||||
}
|
||||
|
||||
if let source::SourceTarget::Unknown = source_input.target() {
|
||||
// 49152 is 48kb!
|
||||
if matches.is_present("enforce_stack_adjustment") {
|
||||
let stack_size: u32 = matches.value_of("shrink_stack").unwrap_or_else(|| "49152").parse().expect("New stack size is not valid u32");
|
||||
assert!(stack_size <= 1024*1024);
|
||||
let (new_module, new_stack_top) = shrink_unknown_stack(module, 1024 * 1024 - stack_size);
|
||||
module = new_module;
|
||||
let mut stack_top_page = new_stack_top / 65536;
|
||||
if new_stack_top % 65536 > 0 { stack_top_page += 1 };
|
||||
module = externalize_mem(module, Some(stack_top_page), 16);
|
||||
} else {
|
||||
module = externalize_mem(module, None, 16);
|
||||
}
|
||||
}
|
||||
|
||||
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 = utils::inject_runtime_type(module, &runtime_type, runtime_version);
|
||||
}
|
||||
|
||||
let mut ctor_module = module.clone();
|
||||
|
||||
if !matches.is_present("skip_optimization") {
|
||||
utils::optimize(
|
||||
&mut module,
|
||||
vec![CALL_SYMBOL]
|
||||
)?;
|
||||
}
|
||||
|
||||
if let Some(save_raw_path) = matches.value_of("save_raw") {
|
||||
parity_wasm::serialize_to_file(save_raw_path, module.clone()).map_err(Error::Encoding)?;
|
||||
}
|
||||
|
||||
let raw_module = parity_wasm::serialize(module).map_err(Error::Encoding)?;
|
||||
|
||||
// If module has an exported function with name=CREATE_SYMBOL
|
||||
// build will pack the module (raw_module) into this funciton and export as CALL_SYMBOL.
|
||||
// Otherwise it will just save an optimised raw_module
|
||||
if has_ctor(&ctor_module) {
|
||||
if !matches.is_present("skip_optimization") {
|
||||
utils::optimize(&mut ctor_module, vec![CREATE_SYMBOL])?;
|
||||
}
|
||||
let ctor_module = utils::pack_instance(raw_module, ctor_module)?;
|
||||
parity_wasm::serialize_to_file(&path, ctor_module).map_err(Error::Encoding)?;
|
||||
} else {
|
||||
let mut file = fs::File::create(&path)?;
|
||||
file.write_all(&raw_module)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() {
|
||||
if let Err(e) = do_main() {
|
||||
eprintln!("{}", e);
|
||||
std::process::exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
extern crate tempdir;
|
||||
|
||||
use self::tempdir::TempDir;
|
||||
use std::fs;
|
||||
|
||||
use super::process_output;
|
||||
use super::source::SourceInput;
|
||||
|
||||
#[test]
|
||||
fn processes_cargo_output() {
|
||||
let tmp_dir = TempDir::new("target").expect("temp dir failed");
|
||||
|
||||
let target_path = tmp_dir.path().join("wasm32-unknown-emscripten").join("release");
|
||||
fs::create_dir_all(target_path.clone()).expect("create dir failed");
|
||||
|
||||
{
|
||||
use std::io::Write;
|
||||
|
||||
let wasm_path = target_path.join("example_wasm.wasm");
|
||||
let mut f = fs::File::create(wasm_path).expect("create fail failed");
|
||||
f.write(b"\0asm").expect("write file failed");
|
||||
}
|
||||
|
||||
let path = tmp_dir.path().to_string_lossy();
|
||||
let input = SourceInput::new(&path, "example-wasm");
|
||||
|
||||
process_output(&input).expect("process output failed");
|
||||
|
||||
assert!(
|
||||
fs::metadata(tmp_dir.path().join("example-wasm.wasm")).expect("metadata failed").is_file()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
//! Configuration of source binaries
|
||||
|
||||
pub const UNKNOWN_TRIPLET: &str = "wasm32-unknown-unknown";
|
||||
pub const EMSCRIPTEN_TRIPLET: &str = "wasm32-unknown-emscripten";
|
||||
|
||||
/// Target configiration of previous build step
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum SourceTarget {
|
||||
Emscripten,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
/// Configuration of previous build step (cargo compilation)
|
||||
#[derive(Debug)]
|
||||
pub struct SourceInput<'a> {
|
||||
target_dir: &'a str,
|
||||
bin_name: &'a str,
|
||||
final_name: &'a str,
|
||||
target: SourceTarget,
|
||||
}
|
||||
|
||||
impl<'a> SourceInput<'a> {
|
||||
pub fn new<'b>(target_dir: &'b str, bin_name: &'b str) -> SourceInput<'b> {
|
||||
SourceInput {
|
||||
target_dir: target_dir,
|
||||
bin_name: bin_name,
|
||||
final_name: bin_name,
|
||||
target: SourceTarget::Emscripten,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unknown(mut self) -> Self {
|
||||
self.target = SourceTarget::Unknown;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn emscripten(mut self) -> Self {
|
||||
self.target = SourceTarget::Emscripten;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_final(mut self, final_name: &'a str) -> Self {
|
||||
self.final_name = final_name;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn target_dir(&self) -> &str {
|
||||
self.target_dir
|
||||
}
|
||||
|
||||
pub fn bin_name(&self) -> &str {
|
||||
self.bin_name
|
||||
}
|
||||
|
||||
pub fn final_name(&self) -> &str {
|
||||
self.final_name
|
||||
}
|
||||
|
||||
pub fn target(&self) -> SourceTarget {
|
||||
self.target
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
extern crate parity_wasm;
|
||||
extern crate pwasm_utils as utils;
|
||||
extern crate pwasm_utils_cli as logger;
|
||||
|
||||
use std::env;
|
||||
|
||||
fn main() {
|
||||
|
||||
logger::init_log();
|
||||
|
||||
let args = env::args().collect::<Vec<_>>();
|
||||
if args.len() != 3 {
|
||||
println!("Usage: {} input_file.wasm output_file.wasm", args[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
let module = utils::externalize(
|
||||
parity_wasm::deserialize_file(&args[1]).expect("Module to deserialize ok"),
|
||||
vec!["_free", "_malloc", "_memcpy", "_memset", "_memmove"],
|
||||
);
|
||||
|
||||
parity_wasm::serialize_to_file(&args[2], module).expect("Module to serialize ok");
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
extern crate parity_wasm;
|
||||
extern crate pwasm_utils as utils;
|
||||
extern crate pwasm_utils_cli as logger;
|
||||
|
||||
use std::env;
|
||||
|
||||
fn main() {
|
||||
logger::init_log();
|
||||
|
||||
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");
|
||||
|
||||
let result = utils::inject_gas_counter(
|
||||
module, &Default::default()
|
||||
).expect("Failed to inject gas. Some forbidden opcodes?");
|
||||
|
||||
parity_wasm::serialize_to_file(&args[2], result).expect("Module serialization to succeed")
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
extern crate parity_wasm;
|
||||
extern crate pwasm_utils as utils;
|
||||
extern crate pwasm_utils_cli as logger;
|
||||
extern crate clap;
|
||||
|
||||
use clap::{App, Arg};
|
||||
|
||||
fn main() {
|
||||
logger::init_log();
|
||||
|
||||
let matches = App::new("wasm-opt")
|
||||
.arg(Arg::with_name("input")
|
||||
.index(1)
|
||||
.required(true)
|
||||
.help("Input WASM file"))
|
||||
.arg(Arg::with_name("output")
|
||||
.index(2)
|
||||
.required(true)
|
||||
.help("Output WASM file"))
|
||||
.arg(Arg::with_name("exports")
|
||||
.long("exports")
|
||||
.short("e")
|
||||
.takes_value(true)
|
||||
.value_name("functions")
|
||||
.help("Comma-separated list of exported functions to keep. Default: _call"))
|
||||
.get_matches();
|
||||
|
||||
let exports = matches
|
||||
.value_of("exports")
|
||||
.unwrap_or("_call")
|
||||
.split(',')
|
||||
.collect();
|
||||
|
||||
let input = matches.value_of("input").expect("is required; qed");
|
||||
let output = matches.value_of("output").expect("is required; qed");
|
||||
|
||||
let mut module = parity_wasm::deserialize_file(&input).unwrap();
|
||||
|
||||
// Invoke optimizer
|
||||
// Contract is supposed to have only these functions as public api
|
||||
// All other symbols not usable by this list is optimized away
|
||||
utils::optimize(&mut module, exports).expect("Optimizer to finish without errors");
|
||||
|
||||
parity_wasm::serialize_to_file(&output, module).unwrap();
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#[macro_use] extern crate log;
|
||||
#[macro_use] extern crate lazy_static;
|
||||
extern crate env_logger;
|
||||
|
||||
use std::env;
|
||||
use log::LevelFilter;
|
||||
use env_logger::Builder;
|
||||
|
||||
lazy_static! {
|
||||
static ref LOG_DUMMY: bool = {
|
||||
let mut builder = Builder::new();
|
||||
builder.filter(None, LevelFilter::Info);
|
||||
|
||||
if let Ok(log) = env::var("RUST_LOG") {
|
||||
builder.parse(&log);
|
||||
}
|
||||
|
||||
builder.init();
|
||||
trace!("logger initialized");
|
||||
true
|
||||
};
|
||||
}
|
||||
|
||||
/// Intialize log with default settings
|
||||
pub fn init_log() {
|
||||
let _ = *LOG_DUMMY;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
extern crate pwasm_utils as utils;
|
||||
extern crate parity_wasm;
|
||||
extern crate pwasm_utils_cli as logger;
|
||||
|
||||
use std::env;
|
||||
use utils::stack_height;
|
||||
|
||||
fn main() {
|
||||
logger::init_log();
|
||||
|
||||
let args = env::args().collect::<Vec<_>>();
|
||||
if args.len() != 3 {
|
||||
println!("Usage: {} input_file.wasm output_file.wasm", args[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
let input_file = &args[1];
|
||||
let output_file = &args[2];
|
||||
|
||||
// Loading module
|
||||
let module = parity_wasm::deserialize_file(&input_file).expect("Module deserialization to succeed");
|
||||
|
||||
let result = stack_height::inject_limiter(
|
||||
module, 1024
|
||||
).expect("Failed to inject stack height counter");
|
||||
|
||||
parity_wasm::serialize_to_file(&output_file, result).expect("Module serialization to succeed")
|
||||
}
|
||||
Reference in New Issue
Block a user