diff --git a/src/frontend.rs b/src/frontend.rs index d5c2890..c250642 100644 --- a/src/frontend.rs +++ b/src/frontend.rs @@ -11,7 +11,8 @@ use fxhash::{FxHashMap, FxHashSet}; use log::trace; use std::convert::TryFrom; use wasmparser::{ - Ieee32, Ieee64, ImportSectionEntryType, Parser, Payload, TypeDef, TypeOrFuncType, + DataKind, ExternalKind, Ieee32, Ieee64, ImportSectionEntryType, Parser, Payload, TypeDef, + TypeOrFuncType, }; pub fn wasm_to_ir(bytes: &[u8]) -> Result> { @@ -33,46 +34,60 @@ fn handle_payload<'a>( ) -> Result<()> { trace!("Wasm parser item: {:?}", payload); match payload { - Payload::TypeSection(mut reader) => { - for _ in 0..reader.get_count() { - let ty = reader.read()?; + Payload::TypeSection(reader) => { + for ty in reader { + let ty = ty?; if let TypeDef::Func(fty) = ty { module.frontend_add_signature(fty.into()); } } } - Payload::ImportSection(mut reader) => { - for _ in 0..reader.get_count() { - match reader.read()?.ty { + Payload::ImportSection(reader) => { + for import in reader { + let import = import?; + let module_name = import.module.to_owned(); + let name = import.field.unwrap_or("").to_owned(); + let kind = match import.ty { ImportSectionEntryType::Function(sig_idx) => { - module.frontend_add_func(FuncDecl::Import(Signature::from(sig_idx))); + let func = + module.frontend_add_func(FuncDecl::Import(Signature::from(sig_idx))); *next_func += 1; + Some(ImportKind::Func(func)) } ImportSectionEntryType::Global(ty) => { - module.frontend_add_global(ty.content_type.into()); + let global = module.frontend_add_global(ty.content_type.into()); + Some(ImportKind::Global(global)) } ImportSectionEntryType::Table(ty) => { - module.frontend_add_table(ty.element_type.into()); + let table = module.frontend_add_table(ty.element_type.into()); + Some(ImportKind::Table(table)) } - _ => {} + _ => None, + }; + if let Some(kind) = kind { + module.frontend_add_import(Import { + module: module_name, + name, + kind, + }); } } } - Payload::GlobalSection(mut reader) => { - for _ in 0..reader.get_count() { - let global = reader.read()?; + Payload::GlobalSection(reader) => { + for global in reader { + let global = global?; module.frontend_add_global(global.ty.content_type.into()); } } - Payload::TableSection(mut reader) => { - for _ in 0..reader.get_count() { - let table = reader.read()?; + Payload::TableSection(reader) => { + for table in reader { + let table = table?; module.frontend_add_table(table.element_type.into()); } } - Payload::FunctionSection(mut reader) => { - for _ in 0..reader.get_count() { - let sig_idx = Signature::from(reader.read()?); + Payload::FunctionSection(reader) => { + for sig_idx in reader { + let sig_idx = Signature::from(sig_idx?); module.frontend_add_func(FuncDecl::Body(sig_idx, FunctionBody::default())); } } @@ -86,6 +101,71 @@ fn handle_payload<'a>( let existing_body = module.func_mut(func_idx).body_mut().unwrap(); *existing_body = body; } + Payload::ExportSection(reader) => { + for export in reader { + let export = export?; + let name = export.field.to_owned(); + let kind = match export.kind { + ExternalKind::Function => Some(ExportKind::Func(Func::from(export.index))), + ExternalKind::Table => Some(ExportKind::Table(Table::from(export.index))), + ExternalKind::Global => Some(ExportKind::Global(Global::from(export.index))), + ExternalKind::Memory => Some(ExportKind::Memory(Memory::from(export.index))), + _ => None, + }; + if let Some(kind) = kind { + module.frontend_add_export(Export { name, kind }); + } + } + } + Payload::MemorySection(reader) => { + for memory in reader { + let memory = memory?; + module.frontend_add_memory(MemoryData { + initial_pages: memory.initial as usize, + maximum_pages: memory.maximum.map(|max| max as usize), + segments: vec![], + }); + } + } + Payload::DataSection(reader) => { + for segment in reader { + let segment = segment?; + match &segment.kind { + DataKind::Passive => {} + DataKind::Active { + memory_index, + init_expr, + } => { + let data = segment.data.to_vec(); + let memory = Memory::from(*memory_index); + let operators = init_expr + .get_operators_reader() + .into_iter() + .collect::, _>>()?; + if operators.len() != 2 + || !matches!(&operators[1], &wasmparser::Operator::End) + { + anyhow::bail!( + "Unsupported operator seq in base-address expr: {:?}", + operators + ); + } + let offset = match &operators[0] { + &wasmparser::Operator::I32Const { value } => value as usize, + &wasmparser::Operator::I64Const { value } => value as usize, + op => anyhow::bail!( + "Unsupported data segment base-address operator: {:?}", + op + ), + }; + module + .frontend_memory_mut(memory) + .segments + .push(MemorySegment { offset, data }); + } + } + } + } _ => {} } diff --git a/src/ir/display.rs b/src/ir/display.rs index 049aeeb..4ede1f9 100644 --- a/src/ir/display.rs +++ b/src/ir/display.rs @@ -158,6 +158,36 @@ impl<'a> Display for ModuleDisplay<'a> { for (table, table_ty) in self.0.tables() { writeln!(f, " {}: {}", table, table_ty)?; } + for (memory, memory_data) in self.0.memories() { + writeln!( + f, + " {}: initial {} max {:?}", + memory, memory_data.initial_pages, memory_data.maximum_pages + )?; + for seg in &memory_data.segments { + writeln!( + f, + " {} offset {}: [{}]", + memory, + seg.offset, + seg.data + .iter() + .map(|&byte| format!("0x{:02x}", byte)) + .collect::>() + .join(", ") + )?; + } + } + for import in self.0.imports() { + writeln!( + f, + " import \"{}\".\"{}\": {}", + import.module, import.name, import.kind + )?; + } + for export in self.0.exports() { + writeln!(f, " export \"{}\": {}", export.name, export.kind)?; + } for (func, func_decl) in self.0.funcs() { match func_decl { FuncDecl::Body(sig, body) => { diff --git a/src/ir/module.rs b/src/ir/module.rs index a05dd92..3b80b37 100644 --- a/src/ir/module.rs +++ b/src/ir/module.rs @@ -1,16 +1,19 @@ -use super::{Func, FuncDecl, Global, ModuleDisplay, Signature, Table, Type}; +use super::{Func, FuncDecl, Global, Memory, ModuleDisplay, Signature, Table, Type}; use crate::entity::EntityVec; use crate::frontend; use anyhow::Result; use fxhash::FxHashSet; -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug)] pub struct Module<'a> { orig_bytes: &'a [u8], funcs: EntityVec, signatures: EntityVec, globals: EntityVec, tables: EntityVec, + imports: Vec, + exports: Vec, + memories: EntityVec, dirty_funcs: FxHashSet, } @@ -21,6 +24,19 @@ pub struct SignatureData { pub returns: Vec, } +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct MemoryData { + pub initial_pages: usize, + pub maximum_pages: Option, + pub segments: Vec, +} + +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct MemorySegment { + pub offset: usize, + pub data: Vec, +} + impl From<&wasmparser::FuncType> for SignatureData { fn from(fty: &wasmparser::FuncType) -> Self { Self { @@ -43,11 +59,70 @@ impl From for SignatureData { } } +#[derive(Clone, Debug)] +pub struct Import { + pub module: String, + pub name: String, + pub kind: ImportKind, +} + +#[derive(Clone, Debug)] +pub enum ImportKind { + Table(Table), + Func(Func), + Global(Global), +} + +impl std::fmt::Display for ImportKind { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + ImportKind::Table(table) => write!(f, "{}", table)?, + ImportKind::Func(func) => write!(f, "{}", func)?, + ImportKind::Global(global) => write!(f, "{}", global)?, + } + Ok(()) + } +} + +#[derive(Clone, Debug)] +pub struct Export { + pub name: String, + pub kind: ExportKind, +} + +#[derive(Clone, Debug)] +pub enum ExportKind { + Table(Table), + Func(Func), + Global(Global), + Memory(Memory), +} + +impl std::fmt::Display for ExportKind { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + ExportKind::Table(table) => write!(f, "{}", table)?, + ExportKind::Func(func) => write!(f, "{}", func)?, + ExportKind::Global(global) => write!(f, "{}", global)?, + ExportKind::Memory(memory) => write!(f, "{}", memory)?, + } + Ok(()) + } +} + impl<'a> Module<'a> { pub(crate) fn with_orig_bytes(orig_bytes: &'a [u8]) -> Module<'a> { - let mut m = Module::default(); - m.orig_bytes = orig_bytes; - m + Module { + orig_bytes, + funcs: EntityVec::default(), + signatures: EntityVec::default(), + globals: EntityVec::default(), + tables: EntityVec::default(), + imports: vec![], + exports: vec![], + memories: EntityVec::default(), + dirty_funcs: FxHashSet::default(), + } } } @@ -80,18 +155,39 @@ impl<'a> Module<'a> { pub fn tables<'b>(&'b self) -> impl Iterator + 'b { self.tables.entries().map(|(id, ty)| (id, *ty)) } + pub fn memories<'b>(&'b self) -> impl Iterator + 'b { + self.memories.entries() + } + pub fn imports<'b>(&'b self) -> impl Iterator + 'b { + self.imports.iter() + } + pub fn exports<'b>(&'b self) -> impl Iterator + 'b { + self.exports.iter() + } pub(crate) fn frontend_add_signature(&mut self, ty: SignatureData) { self.signatures.push(ty); } - pub(crate) fn frontend_add_func(&mut self, body: FuncDecl) { - self.funcs.push(body); + pub(crate) fn frontend_add_func(&mut self, body: FuncDecl) -> Func { + self.funcs.push(body) } - pub(crate) fn frontend_add_table(&mut self, ty: Type) { - self.tables.push(ty); + pub(crate) fn frontend_add_table(&mut self, ty: Type) -> Table { + self.tables.push(ty) } - pub(crate) fn frontend_add_global(&mut self, ty: Type) { - self.globals.push(ty); + pub(crate) fn frontend_add_global(&mut self, ty: Type) -> Global { + self.globals.push(ty) + } + pub(crate) fn frontend_add_import(&mut self, import: Import) { + self.imports.push(import); + } + pub(crate) fn frontend_add_export(&mut self, export: Export) { + self.exports.push(export); + } + pub(crate) fn frontend_add_memory(&mut self, memory: MemoryData) -> Memory { + self.memories.push(memory) + } + pub(crate) fn frontend_memory_mut<'b>(&'b mut self, memory: Memory) -> &'b mut MemoryData { + &mut self.memories[memory] } pub fn from_wasm_bytes(bytes: &'a [u8]) -> Result {