diff --git a/Cargo.toml b/Cargo.toml index 542e2fe..93e8f52 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,26 +8,41 @@ edition = "2018" repository = "https://github.com/bytecodealliance/waffle" [dependencies] -wasmparser = "0.212" -wasm-encoder = "0.212" +wasmparser = { version = "0.248", default-features = false, features = ["std", "validate", "simd"] } +wasm-encoder = { version = "0.248", default-features = false } anyhow = "1.0" -structopt = "0.3" log = "0.4" -env_logger = "0.11" fxhash = "0.2" smallvec = "1.13" -rayon = "1.10" -lazy_static = "1.4" -libc = "0.2" -addr2line = "0.21" -# For fuzzing only. Versions must match those in fuzz/Cargo.toml. +# Optional dependencies below. + +rayon = { version = "1.10", optional = true } +addr2line = { version = "0.21", optional = true } +structopt = { version = "0.3", optional = true } +env_logger = { version = "0.11", optional = true } libfuzzer-sys = { version = "0.4.7", optional = true } -wasm-smith = { version = "0.202", optional = true } +wasm-smith = { version = "0.248", optional = true } [dev-dependencies] -wat = "1.212.0" +wat = "1.248.0" +env_logger = "0.11" [features] default = [] + +# Enable parallelism in the backend via rayon. +parallel = ["dep:rayon"] + +# Enable DWARF debug-info parsing/preservation via addr2line/gimli. +dwarf = ["dep:addr2line"] + +# Enable the `waffle-util` command-line tool. +cli = ["dep:structopt", "dep:env_logger"] + +# Enable fuzzing entrypoints. fuzzing = ["libfuzzer-sys", "wasm-smith"] + +[[bin]] +name = "waffle-util" +required-features = ["cli"] diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 2b4105b..11d4504 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -12,11 +12,11 @@ cargo-fuzz = true [dependencies] libfuzzer-sys = { version = "0.4.7" } arbitrary = { version = "1.3.2", features = ["derive"] } -wasm-smith = "0.202.0" -env_logger = "0.9" +wasm-smith = "0.248.0" +env_logger = "0.11" log = "0.4" -wasmparser = "0.202.0" -wasmtime = "19.0" +wasmparser = "0.248.0" +wasmtime = "44.0" [dependencies.waffle] path = ".." diff --git a/fuzz/fuzz_targets/irreducible.rs b/fuzz/fuzz_targets/irreducible.rs index b377a42..11b5f74 100644 --- a/fuzz/fuzz_targets/irreducible.rs +++ b/fuzz/fuzz_targets/irreducible.rs @@ -21,7 +21,7 @@ struct CFG { } impl CFG { - fn to_module(&self) -> Option { + fn to_module(&self) -> Option> { let mut module = Module::empty(); let sig = module.signatures.push(SignatureData { params: vec![Type::I32], diff --git a/src/backend/mod.rs b/src/backend/mod.rs index 1875bda..2f37d3d 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -5,8 +5,10 @@ use crate::entity::EntityRef; use crate::ir::{ExportKind, FuncDecl, FunctionBody, ImportKind, Module, Type, Value, ValueDef}; use crate::Operator; use anyhow::Result; +#[cfg(feature = "parallel")] use rayon::prelude::*; use std::borrow::Cow; +use std::convert::TryFrom; pub mod reducify; use reducify::Reducifier; @@ -385,12 +387,12 @@ impl<'a> WasmFuncBackend<'a> { Operator::I64Const { value } => { Some(wasm_encoder::Instruction::I64Const(*value as i64)) } - Operator::F32Const { value } => { - Some(wasm_encoder::Instruction::F32Const(f32::from_bits(*value))) - } - Operator::F64Const { value } => { - Some(wasm_encoder::Instruction::F64Const(f64::from_bits(*value))) - } + Operator::F32Const { value } => Some(wasm_encoder::Instruction::F32Const( + f32::from_bits(*value).into(), + )), + Operator::F64Const { value } => Some(wasm_encoder::Instruction::F64Const( + f64::from_bits(*value).into(), + )), Operator::I32Eqz => op!(I32Eqz), Operator::I32Eq => op!(I32Eq), @@ -996,7 +998,7 @@ pub fn compile(module: &Module<'_>) -> anyhow::Result> { .returns .iter() .map(|&ty| wasm_encoder::ValType::from(ty)); - types.function(params, returns); + types.ty().function(params, returns); } into_mod.section(&types); @@ -1020,6 +1022,7 @@ pub fn compile(module: &Module<'_>) -> anyhow::Result> { minimum: table.initial, maximum: table.max, table64: false, + shared: false, }) } &ImportKind::Global(global) => { @@ -1069,6 +1072,7 @@ pub fn compile(module: &Module<'_>) -> anyhow::Result> { minimum: table_data.initial, maximum: table_data.max, table64: false, + shared: false, }); } into_mod.section(&tables); @@ -1150,7 +1154,9 @@ pub fn compile(module: &Module<'_>) -> anyhow::Result> { elem.active( Some(table.index() as u32), &wasm_encoder::ConstExpr::i32_const(i as i32), - wasm_encoder::Elements::Functions(&[elt.index() as u32]), + wasm_encoder::Elements::Functions(std::borrow::Cow::Borrowed(&[ + u32::try_from(elt.index()).unwrap(), + ])), ); } Type::TypedFuncRef(..) => { @@ -1159,7 +1165,11 @@ pub fn compile(module: &Module<'_>) -> anyhow::Result> { &wasm_encoder::ConstExpr::i32_const(i as i32), wasm_encoder::Elements::Expressions( table_data.ty.into(), - &[wasm_encoder::ConstExpr::ref_func(elt.index() as u32)], + std::borrow::Cow::Borrowed(&[ + wasm_encoder::ConstExpr::ref_func( + u32::try_from(elt.index()).unwrap(), + ), + ]), ), ); } @@ -1173,12 +1183,16 @@ pub fn compile(module: &Module<'_>) -> anyhow::Result> { let mut code = wasm_encoder::CodeSection::new(); - let bodies = module + let entries = module .funcs .entries() .skip(num_func_imports) - .collect::>() - .par_iter() + .collect::>(); + #[cfg(feature = "parallel")] + let iter = entries.par_iter(); + #[cfg(not(feature = "parallel"))] + let iter = entries.iter(); + let bodies = iter .map(|(func, func_decl)| -> Result<_> { match func_decl { FuncDecl::Lazy(_, _name, reader) => { @@ -1237,8 +1251,8 @@ fn const_init(ty: Type, value: Option) -> wasm_encoder::ConstExpr { match ty { Type::I32 => wasm_encoder::ConstExpr::i32_const(bits as u32 as i32), Type::I64 => wasm_encoder::ConstExpr::i64_const(bits as i64), - Type::F32 => wasm_encoder::ConstExpr::f32_const(f32::from_bits(bits as u32)), - Type::F64 => wasm_encoder::ConstExpr::f64_const(f64::from_bits(bits as u64)), + Type::F32 => wasm_encoder::ConstExpr::f32_const(f32::from_bits(bits as u32).into()), + Type::F64 => wasm_encoder::ConstExpr::f64_const(f64::from_bits(bits as u64).into()), Type::TypedFuncRef(true, sig) if bits == 0 => { let hty = wasm_encoder::HeapType::Concrete(sig); wasm_encoder::ConstExpr::ref_null(hty) diff --git a/src/frontend.rs b/src/frontend.rs index fdaa1ec..5be0944 100644 --- a/src/frontend.rs +++ b/src/frontend.rs @@ -7,6 +7,7 @@ use crate::errors::FrontendError; use crate::ir::*; use crate::op_traits::{op_inputs, op_outputs}; use crate::ops::Operator; +#[cfg(feature = "dwarf")] use addr2line::gimli; use anyhow::{bail, Result}; use fxhash::{FxHashMap, FxHashSet}; @@ -27,6 +28,7 @@ pub(crate) fn wasm_to_ir<'a>(bytes: &'a [u8], options: &FrontendOptions) -> Resu let mut module = Module::with_orig_bytes(bytes); let parser = Parser::new(0); let mut next_func = 0; + #[cfg(feature = "dwarf")] let mut dwarf = gimli::Dwarf::default(); let mut extra_sections = ExtraSections::default(); for payload in parser.parse_all(bytes) { @@ -35,18 +37,27 @@ pub(crate) fn wasm_to_ir<'a>(bytes: &'a [u8], options: &FrontendOptions) -> Resu &mut module, payload, &mut next_func, + #[cfg(feature = "dwarf")] &mut dwarf, &mut extra_sections, )?; } - dwarf.locations = - gimli::LocationLists::new(extra_sections.debug_loc, extra_sections.debug_loclists); - dwarf.ranges = - gimli::RangeLists::new(extra_sections.debug_ranges, extra_sections.debug_rnglists); - - if options.debug { - let debug_map = DebugMap::from_dwarf(dwarf, &mut module.debug, extra_sections.code_offset)?; - module.debug_map = debug_map; + #[cfg(feature = "dwarf")] + { + dwarf.locations = + gimli::LocationLists::new(extra_sections.debug_loc, extra_sections.debug_loclists); + dwarf.ranges = + gimli::RangeLists::new(extra_sections.debug_ranges, extra_sections.debug_rnglists); + + if options.debug { + let debug_map = + DebugMap::from_dwarf(dwarf, &mut module.debug, extra_sections.code_offset)?; + module.debug_map = debug_map; + } + } + #[cfg(not(feature = "dwarf"))] + { + let _ = options.debug; } Ok(module) @@ -80,18 +91,24 @@ fn parse_init_expr<'a>(init_expr: &wasmparser::ConstExpr<'a>) -> Result { + #[cfg(feature = "dwarf")] debug_loc: gimli::DebugLoc>, + #[cfg(feature = "dwarf")] debug_loclists: gimli::DebugLocLists>, + #[cfg(feature = "dwarf")] debug_ranges: gimli::DebugRanges>, + #[cfg(feature = "dwarf")] debug_rnglists: gimli::DebugRngLists>, code_offset: u32, + #[cfg(not(feature = "dwarf"))] + _phantom: std::marker::PhantomData<&'a ()>, } fn handle_payload<'a>( module: &mut Module<'a>, payload: Payload<'a>, next_func: &mut usize, - dwarf: &mut gimli::Dwarf>, + #[cfg(feature = "dwarf")] dwarf: &mut gimli::Dwarf>, extra_sections: &mut ExtraSections<'a>, ) -> Result<()> { trace!("Wasm parser item: {:?}", payload); @@ -99,8 +116,8 @@ fn handle_payload<'a>( Payload::TypeSection(reader) => { for rec_group in reader { for ty in rec_group?.into_types() { - match &ty.composite_type { - wasmparser::CompositeType::Func(fty) => { + match &ty.composite_type.inner { + wasmparser::CompositeInnerType::Func(fty) => { module.signatures.push(fty.into()); } _ => bail!(FrontendError::UnsupportedFeature( @@ -111,57 +128,81 @@ fn handle_payload<'a>( } } Payload::ImportSection(reader) => { - for import in reader { - let import = import?; - let module_name = import.module.to_owned(); - let name = import.name.to_owned(); - let kind = match import.ty { - TypeRef::Func(sig_idx) => { - let func = module - .funcs - .push(FuncDecl::Import(Signature::from(sig_idx), "".to_owned())); - *next_func += 1; - ImportKind::Func(func) - } - TypeRef::Global(ty) => { - let mutable = ty.mutable; - let ty = ty.content_type.into(); - let global = module.globals.push(GlobalData { - ty, - value: None, - mutable, - }); - ImportKind::Global(global) - } - TypeRef::Table(ty) => { - let table = module.tables.push(TableData { - ty: ty.element_type.into(), - initial: ty.initial, - max: ty.maximum, - func_elements: Some(vec![]), - }); - ImportKind::Table(table) + for imports in reader { + let imports = imports?; + let mut handle = |module_name: &str, name: &str, ty: TypeRef| -> Result<()> { + let module_name = module_name.to_owned(); + let name = name.to_owned(); + let kind = match ty { + TypeRef::Func(sig_idx) | TypeRef::FuncExact(sig_idx) => { + let func = module + .funcs + .push(FuncDecl::Import(Signature::from(sig_idx), "".to_owned())); + *next_func += 1; + ImportKind::Func(func) + } + TypeRef::Global(ty) => { + let mutable = ty.mutable; + let ty = ty.content_type.into(); + let global = module.globals.push(GlobalData { + ty, + value: None, + mutable, + }); + ImportKind::Global(global) + } + TypeRef::Table(ty) => { + let table = module.tables.push(TableData { + ty: ty.element_type.into(), + initial: ty.initial, + max: ty.maximum, + func_elements: Some(vec![]), + }); + ImportKind::Table(table) + } + TypeRef::Memory(mem) => { + let mem = module.memories.push(MemoryData { + initial_pages: mem.initial as usize, + maximum_pages: mem.maximum.map(|max| max as usize), + segments: vec![], + }); + ImportKind::Memory(mem) + } + t => { + bail!(FrontendError::UnsupportedFeature(format!( + "Unknown import type: {:?}", + t + ))); + } + }; + module.imports.push(Import { + module: module_name, + name, + kind, + }); + Ok(()) + }; + match imports { + wasmparser::Imports::Single(_, import) => { + handle(import.module, import.name, import.ty)?; } - TypeRef::Memory(mem) => { - let mem = module.memories.push(MemoryData { - initial_pages: mem.initial as usize, - maximum_pages: mem.maximum.map(|max| max as usize), - segments: vec![], - }); - ImportKind::Memory(mem) + wasmparser::Imports::Compact1 { module: m, items } => { + for item in items { + let item = item?; + handle(m, item.name, item.ty)?; + } } - t => { - bail!(FrontendError::UnsupportedFeature(format!( - "Unknown import type: {:?}", - t - ))); + wasmparser::Imports::Compact2 { + module: m, + ty, + names, + } => { + for name in names { + let name = name?; + handle(m, name, ty)?; + } } - }; - module.imports.push(Import { - module: module_name, - name, - kind, - }); + } } } Payload::GlobalSection(reader) => { @@ -273,61 +314,69 @@ fn handle_payload<'a>( true } KnownCustom::Unknown => { - if reader.name() == ".debug_info" { - dwarf.debug_info = - gimli::DebugInfo::new(reader.data(), gimli::LittleEndian); - true - } else if reader.name() == ".debug_abbrev" { - dwarf.debug_abbrev = - gimli::DebugAbbrev::new(reader.data(), gimli::LittleEndian); - true - } else if reader.name() == ".debug_addr" { - dwarf.debug_addr = gimli::DebugAddr::from(gimli::EndianSlice::new( - reader.data(), - gimli::LittleEndian, - )); - true - } else if reader.name() == ".debug_aranges" { - dwarf.debug_aranges = - gimli::DebugAranges::new(reader.data(), gimli::LittleEndian); - true - } else if reader.name() == ".debug_line" { - dwarf.debug_line = - gimli::DebugLine::new(reader.data(), gimli::LittleEndian); - true - } else if reader.name() == ".debug_line_str" { - dwarf.debug_line_str = - gimli::DebugLineStr::new(reader.data(), gimli::LittleEndian); - true - } else if reader.name() == ".debug_str" { - dwarf.debug_str = gimli::DebugStr::new(reader.data(), gimli::LittleEndian); - true - } else if reader.name() == ".debug_str_offsets" { - dwarf.debug_str_offsets = gimli::DebugStrOffsets::from( - gimli::EndianSlice::new(reader.data(), gimli::LittleEndian), - ); - true - } else if reader.name() == ".debug_types" { - dwarf.debug_types = - gimli::DebugTypes::new(reader.data(), gimli::LittleEndian); - true - } else if reader.name() == ".debug_loc" { - extra_sections.debug_loc = - gimli::DebugLoc::new(reader.data(), gimli::LittleEndian); - true - } else if reader.name() == ".debug_loclists" { - extra_sections.debug_loclists = - gimli::DebugLocLists::new(reader.data(), gimli::LittleEndian); - true - } else if reader.name() == ".debug_ranges" { - extra_sections.debug_ranges = - gimli::DebugRanges::new(reader.data(), gimli::LittleEndian); - true - } else if reader.name() == ".debug_rnglists" { - extra_sections.debug_rnglists = - gimli::DebugRngLists::new(reader.data(), gimli::LittleEndian); - true - } else { + #[cfg(feature = "dwarf")] + { + if reader.name() == ".debug_info" { + dwarf.debug_info = + gimli::DebugInfo::new(reader.data(), gimli::LittleEndian); + true + } else if reader.name() == ".debug_abbrev" { + dwarf.debug_abbrev = + gimli::DebugAbbrev::new(reader.data(), gimli::LittleEndian); + true + } else if reader.name() == ".debug_addr" { + dwarf.debug_addr = gimli::DebugAddr::from(gimli::EndianSlice::new( + reader.data(), + gimli::LittleEndian, + )); + true + } else if reader.name() == ".debug_aranges" { + dwarf.debug_aranges = + gimli::DebugAranges::new(reader.data(), gimli::LittleEndian); + true + } else if reader.name() == ".debug_line" { + dwarf.debug_line = + gimli::DebugLine::new(reader.data(), gimli::LittleEndian); + true + } else if reader.name() == ".debug_line_str" { + dwarf.debug_line_str = + gimli::DebugLineStr::new(reader.data(), gimli::LittleEndian); + true + } else if reader.name() == ".debug_str" { + dwarf.debug_str = + gimli::DebugStr::new(reader.data(), gimli::LittleEndian); + true + } else if reader.name() == ".debug_str_offsets" { + dwarf.debug_str_offsets = gimli::DebugStrOffsets::from( + gimli::EndianSlice::new(reader.data(), gimli::LittleEndian), + ); + true + } else if reader.name() == ".debug_types" { + dwarf.debug_types = + gimli::DebugTypes::new(reader.data(), gimli::LittleEndian); + true + } else if reader.name() == ".debug_loc" { + extra_sections.debug_loc = + gimli::DebugLoc::new(reader.data(), gimli::LittleEndian); + true + } else if reader.name() == ".debug_loclists" { + extra_sections.debug_loclists = + gimli::DebugLocLists::new(reader.data(), gimli::LittleEndian); + true + } else if reader.name() == ".debug_ranges" { + extra_sections.debug_ranges = + gimli::DebugRanges::new(reader.data(), gimli::LittleEndian); + true + } else if reader.name() == ".debug_rnglists" { + extra_sections.debug_rnglists = + gimli::DebugRngLists::new(reader.data(), gimli::LittleEndian); + true + } else { + false + } + } + #[cfg(not(feature = "dwarf"))] + { false } } diff --git a/src/fuzzing.rs b/src/fuzzing.rs index 8adada6..9346740 100644 --- a/src/fuzzing.rs +++ b/src/fuzzing.rs @@ -75,7 +75,7 @@ fn fuzzing_config() -> wasm_smith::Config { max_exports: 12, allow_start_export: true, canonicalize_nans: true, - max_memory32_pages: 1, + max_memory32_bytes: 1 << 16, ..Default::default() } } diff --git a/src/ir/debug.rs b/src/ir/debug.rs index cd8a229..bfcc5df 100644 --- a/src/ir/debug.rs +++ b/src/ir/debug.rs @@ -2,6 +2,7 @@ use crate::declare_entity; use crate::entity::EntityVec; +#[cfg(feature = "dwarf")] use addr2line::gimli; use std::collections::hash_map::Entry as HashEntry; use std::collections::HashMap; @@ -67,6 +68,7 @@ pub struct DebugMap { } impl DebugMap { + #[cfg(feature = "dwarf")] pub(crate) fn from_dwarf( dwarf: gimli::Dwarf, debug: &mut Debug,