diff --git a/crates/api/src/instance.rs b/crates/api/src/instance.rs index dc7a89390b..81b2a4ccee 100644 --- a/crates/api/src/instance.rs +++ b/crates/api/src/instance.rs @@ -29,6 +29,7 @@ impl Resolver for SimpleResolver { pub fn instantiate_in_context( data: &[u8], imports: Vec<(String, String, Extern)>, + module_name: Option, context: Context, exports: Rc>>>, ) -> Result<(InstanceHandle, HashSet), Error> { @@ -38,6 +39,7 @@ pub fn instantiate_in_context( let instance = instantiate( &mut context.compiler(), data, + module_name, &mut resolver, exports, debug_info, @@ -77,8 +79,13 @@ impl Instance { .zip(externs.iter()) .map(|(i, e)| (i.module().to_string(), i.name().to_string(), e.clone())) .collect::>(); - let (mut instance_handle, contexts) = - instantiate_in_context(module.binary().expect("binary"), imports, context, exports)?; + let (mut instance_handle, contexts) = instantiate_in_context( + module.binary().expect("binary"), + imports, + module.name().cloned(), + context, + exports, + )?; let exports = { let mut exports = Vec::with_capacity(module.exports().len()); diff --git a/crates/api/src/module.rs b/crates/api/src/module.rs index 7815dd169a..f7bed9928e 100644 --- a/crates/api/src/module.rs +++ b/crates/api/src/module.rs @@ -6,8 +6,8 @@ use crate::types::{ use anyhow::{Error, Result}; use std::rc::Rc; use wasmparser::{ - validate, ExternalKind, ImportSectionEntryType, ModuleReader, OperatorValidatorConfig, - SectionCode, ValidatingParserConfig, + validate, CustomSectionKind, ExternalKind, ImportSectionEntryType, ModuleReader, Name, + OperatorValidatorConfig, SectionCode, ValidatingParserConfig, }; fn into_memory_type(mt: wasmparser::MemoryType) -> MemoryType { @@ -56,7 +56,9 @@ fn into_table_type(tt: wasmparser::TableType) -> TableType { TableType::new(ty, limits) } -fn read_imports_and_exports(binary: &[u8]) -> Result<(Box<[ImportType]>, Box<[ExportType]>)> { +fn read_imports_and_exports( + binary: &[u8], +) -> Result<(Box<[ImportType]>, Box<[ExportType]>, Option)> { let mut reader = ModuleReader::new(binary)?; let mut imports = Vec::new(); let mut exports = Vec::new(); @@ -65,6 +67,7 @@ fn read_imports_and_exports(binary: &[u8]) -> Result<(Box<[ImportType]>, Box<[Ex let mut func_sig = Vec::new(); let mut sigs = Vec::new(); let mut globals = Vec::new(); + let mut module_name = None; while !reader.eof() { let section = reader.read()?; match section.code { @@ -157,12 +160,32 @@ fn read_imports_and_exports(binary: &[u8]) -> Result<(Box<[ImportType]>, Box<[Ex exports.push(ExportType::new(entry.field, r#type)); } } + SectionCode::Custom { + kind: CustomSectionKind::Name, + .. + } => { + // Read name section. Per spec, ignore invalid custom section. + if let Ok(mut reader) = section.get_name_section_reader() { + while let Ok(entry) = reader.read() { + if let Name::Module(name) = entry { + if let Ok(name) = name.get_name() { + module_name = Some(name.to_string()); + } + break; + } + } + } + } _ => { // skip other sections } } } - Ok((imports.into_boxed_slice(), exports.into_boxed_slice())) + Ok(( + imports.into_boxed_slice(), + exports.into_boxed_slice(), + module_name, + )) } #[derive(Clone)] @@ -196,6 +219,7 @@ struct ModuleInner { source: ModuleCodeSource, imports: Box<[ImportType]>, exports: Box<[ExportType]>, + name: Option, } impl Module { @@ -239,7 +263,16 @@ impl Module { // Note that the call to `unsafe` here should be ok because we // previously validated the binary, meaning we're guaranteed to pass a // valid binary for `store`. - unsafe { Self::new_unchecked(store, binary) } + unsafe { Self::create(store, binary, None) } + } + + /// Creates a new WebAssembly `Module` from the given in-memory `binary` + /// data. The provided `name` will be used in traps/backtrace details. + /// + /// See [`Module::new`] for other details. + pub fn new_with_name(store: &Store, binary: &[u8], name: String) -> Result { + Self::validate(store, binary)?; + unsafe { Self::create(store, binary, Some(name)) } } /// Creates a new WebAssembly `Module` from the given in-memory `binary` @@ -269,13 +302,22 @@ impl Module { /// be somewhat valid for decoding purposes, and the basics of decoding can /// still fail. pub unsafe fn new_unchecked(store: &Store, binary: &[u8]) -> Result { - let (imports, exports) = read_imports_and_exports(binary)?; + Self::create(store, binary, None) + } + + unsafe fn create( + store: &Store, + binary: &[u8], + name_override: Option, + ) -> Result { + let (imports, exports, name) = read_imports_and_exports(binary)?; Ok(Module { inner: Rc::new(ModuleInner { store: store.clone(), source: ModuleCodeSource::Binary(binary.into()), imports, exports, + name: name_override.or(name), }), }) } @@ -320,6 +362,7 @@ impl Module { source: ModuleCodeSource::Unknown, imports: Box::new([]), exports, + name: None, }), } } @@ -331,6 +374,12 @@ impl Module { } } + /// Returns identifier/name that this [`Module`] has. This name + /// is used in traps/backtrace details. + pub fn name(&self) -> Option<&String> { + self.inner.name.as_ref() + } + /// Returns the list of imports that this [`Module`] has and must be /// satisfied. pub fn imports(&self) -> &[ImportType] { diff --git a/crates/api/tests/name.rs b/crates/api/tests/name.rs new file mode 100644 index 0000000000..0d83708639 --- /dev/null +++ b/crates/api/tests/name.rs @@ -0,0 +1,54 @@ +use wasmtime::*; +use wat::parse_str; + +#[test] +fn test_module_no_name() -> Result<(), String> { + let store = Store::default(); + let binary = parse_str( + r#" + (module + (func (export "run") (nop)) + ) + "#, + ) + .map_err(|e| format!("failed to parse WebAssembly text source: {}", e))?; + + let module = HostRef::new( + Module::new(&store, &binary).map_err(|e| format!("failed to compile module: {}", e))?, + ); + assert_eq!(module.borrow().name().cloned(), None); + + Ok(()) +} + +#[test] +fn test_module_name() -> Result<(), String> { + let store = Store::default(); + let binary = parse_str( + r#" + (module $from_name_section + (func (export "run") (nop)) + ) + "#, + ) + .map_err(|e| format!("failed to parse WebAssembly text source: {}", e))?; + + let module = HostRef::new( + Module::new(&store, &binary).map_err(|e| format!("failed to compile module: {}", e))?, + ); + assert_eq!( + module.borrow().name().cloned(), + Some("from_name_section".to_string()) + ); + + let module = HostRef::new( + Module::new_with_name(&store, &binary, "override".to_string()) + .map_err(|e| format!("failed to compile module: {}", e))?, + ); + assert_eq!( + module.borrow().name().cloned(), + Some("override".to_string()) + ); + + Ok(()) +} diff --git a/crates/environ/src/module.rs b/crates/environ/src/module.rs index fefaf72dca..0ff8c79d04 100644 --- a/crates/environ/src/module.rs +++ b/crates/environ/src/module.rs @@ -168,6 +168,9 @@ pub struct Module { /// WebAssembly table initializers. pub table_elements: Vec, + + /// Module name. + pub name: Option, } impl Module { @@ -186,6 +189,7 @@ impl Module { exports: IndexMap::new(), start_func: None, table_elements: Vec::new(), + name: None, } } diff --git a/crates/fuzzing/src/oracles.rs b/crates/fuzzing/src/oracles.rs index 5ebd621f40..1afd7467aa 100644 --- a/crates/fuzzing/src/oracles.rs +++ b/crates/fuzzing/src/oracles.rs @@ -78,7 +78,14 @@ pub fn compile(wasm: &[u8], compilation_strategy: CompilationStrategy) { let mut compiler = Compiler::new(isa, compilation_strategy); let mut resolver = NullResolver {}; let global_exports = Rc::new(RefCell::new(HashMap::new())); - let _ = CompiledModule::new(&mut compiler, wasm, &mut resolver, global_exports, false); + let _ = CompiledModule::new( + &mut compiler, + wasm, + None, + &mut resolver, + global_exports, + false, + ); } /// Invoke the given API calls. diff --git a/crates/jit/src/context.rs b/crates/jit/src/context.rs index fc7a219cff..8632b0db8e 100644 --- a/crates/jit/src/context.rs +++ b/crates/jit/src/context.rs @@ -117,6 +117,7 @@ impl Context { instantiate( &mut *self.compiler, &data, + None, &mut self.namespace, Rc::clone(&self.global_exports), debug_info, @@ -154,6 +155,7 @@ impl Context { CompiledModule::new( &mut *self.compiler, data, + None, &mut self.namespace, Rc::clone(&self.global_exports), debug_info, diff --git a/crates/jit/src/instantiate.rs b/crates/jit/src/instantiate.rs index 68e54bbbf2..1a22399125 100644 --- a/crates/jit/src/instantiate.rs +++ b/crates/jit/src/instantiate.rs @@ -60,12 +60,13 @@ impl<'data> RawCompiledModule<'data> { fn new( compiler: &mut Compiler, data: &'data [u8], + module_name: Option, resolver: &mut dyn Resolver, debug_info: bool, ) -> Result { let environ = ModuleEnvironment::new(compiler.frontend_config(), compiler.tunables()); - let translation = environ + let mut translation = environ .translate(data) .map_err(|error| SetupError::Compile(CompileError::Wasm(error)))?; @@ -75,6 +76,8 @@ impl<'data> RawCompiledModule<'data> { None }; + translation.module.name = module_name; + let (allocated_functions, jt_offsets, relocations, dbg_image) = compiler.compile( &translation.module, translation.module_translation.as_ref().unwrap(), @@ -152,11 +155,13 @@ impl CompiledModule { pub fn new<'data>( compiler: &mut Compiler, data: &'data [u8], + module_name: Option, resolver: &mut dyn Resolver, global_exports: Rc>>>, debug_info: bool, ) -> Result { - let raw = RawCompiledModule::<'data>::new(compiler, data, resolver, debug_info)?; + let raw = + RawCompiledModule::<'data>::new(compiler, data, module_name, resolver, debug_info)?; Ok(Self::from_parts( raw.module, @@ -258,11 +263,12 @@ impl OwnedDataInitializer { pub fn instantiate( compiler: &mut Compiler, data: &[u8], + module_name: Option, resolver: &mut dyn Resolver, global_exports: Rc>>>, debug_info: bool, ) -> Result { - let raw = RawCompiledModule::new(compiler, data, resolver, debug_info)?; + let raw = RawCompiledModule::new(compiler, data, module_name, resolver, debug_info)?; InstanceHandle::new( Rc::new(raw.module), diff --git a/tests/instantiate.rs b/tests/instantiate.rs index 1bf28864d8..1d5a2d38b8 100644 --- a/tests/instantiate.rs +++ b/tests/instantiate.rs @@ -25,6 +25,13 @@ fn test_environ_translate() { let mut resolver = NullResolver {}; let mut compiler = Compiler::new(isa, CompilationStrategy::Auto); let global_exports = Rc::new(RefCell::new(HashMap::new())); - let instance = instantiate(&mut compiler, &data, &mut resolver, global_exports, false); + let instance = instantiate( + &mut compiler, + &data, + None, + &mut resolver, + global_exports, + false, + ); assert!(instance.is_ok()); }