diff --git a/Cargo.lock b/Cargo.lock index f829cc5bff..db69b07263 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2061,6 +2061,7 @@ dependencies = [ "pretty_env_logger", "rayon", "region", + "rustc-demangle", "target-lexicon", "wasi-common", "wasmparser 0.47.0", @@ -2166,7 +2167,6 @@ dependencies = [ "log", "wasmtime", "wasmtime-fuzzing", - "wasmtime-jit", ] [[package]] @@ -2181,8 +2181,6 @@ dependencies = [ "wasmparser 0.47.0", "wasmprinter", "wasmtime", - "wasmtime-environ", - "wasmtime-jit", "wat", ] @@ -2206,6 +2204,7 @@ name = "wasmtime-jit" version = "0.9.0" dependencies = [ "anyhow", + "cfg-if", "cranelift-codegen", "cranelift-entity", "cranelift-frontend", diff --git a/Cargo.toml b/Cargo.toml index 09bdaa1cfe..35060e7854 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,11 @@ publish = false [lib] doctest = false +[[bin]] +name = "wasmtime" +path = "src/bin/wasmtime.rs" +doc = false + [dependencies] # Enable all supported architectures by default. wasmtime = { path = "crates/api" } diff --git a/crates/api/Cargo.toml b/crates/api/Cargo.toml index 66d2e42024..a5f559bc4f 100644 --- a/crates/api/Cargo.toml +++ b/crates/api/Cargo.toml @@ -19,6 +19,7 @@ region = "2.0.0" libc = "0.2" cfg-if = "0.1.9" backtrace = "0.3.42" +rustc-demangle = "0.1.16" [target.'cfg(target_os = "windows")'.dependencies] winapi = "0.3.7" diff --git a/crates/api/src/instance.rs b/crates/api/src/instance.rs index 3299b16382..672b62380d 100644 --- a/crates/api/src/instance.rs +++ b/crates/api/src/instance.rs @@ -20,55 +20,96 @@ impl Resolver for SimpleResolver<'_> { } } -fn instantiate_in_context( - store: &Store, - data: &[u8], +fn instantiate( + compiled_module: &CompiledModule, imports: &[Extern], - module_name: Option<&str>, -) -> Result { +) -> Result { let mut resolver = SimpleResolver { imports }; - let mut compiled_module = CompiledModule::new( - &mut store.compiler_mut(), - data, - module_name, - &mut resolver, - store.global_exports().clone(), - store.engine().config().debug_info, - )?; - - // Register all module signatures - for signature in compiled_module.module().signatures.values() { - store.register_wasmtime_signature(signature); - } - - let instance = compiled_module.instantiate().map_err(|e| -> Error { - if let Some(trap) = take_api_trap() { - trap.into() - } else if let InstantiationError::StartTrap(trap) = e { - Trap::from_jit(trap).into() - } else { - e.into() - } - })?; + let instance = compiled_module + .instantiate(&mut resolver) + .map_err(|e| -> Error { + if let Some(trap) = take_api_trap() { + trap.into() + } else if let InstantiationError::StartTrap(trap) = e { + Trap::from_jit(trap).into() + } else { + e.into() + } + })?; Ok(instance) } +/// An instantiated WebAssembly module. +/// +/// This type represents the instantiation of a [`Module`]. Once instantiated +/// you can access the [`exports`](Instance::exports) which are of type +/// [`Extern`] and provide the ability to call functions, set globals, read +/// memory, etc. This is where all the fun stuff happens! +/// +/// An [`Instance`] is created from two inputs, a [`Module`] and a list of +/// imports, provided as a list of [`Extern`] values. The [`Module`] is the wasm +/// code that was compiled and we're instantiating, and the [`Extern`] imports +/// are how we're satisfying the imports of the module provided. On successful +/// instantiation an [`Instance`] will automatically invoke the wasm `start` +/// function. +/// +/// When interacting with any wasm code you'll want to make an [`Instance`] to +/// call any code or execute anything! #[derive(Clone)] pub struct Instance { - instance_handle: InstanceHandle, + pub(crate) instance_handle: InstanceHandle, module: Module, exports: Box<[Extern]>, } impl Instance { - pub fn new(module: &Module, externs: &[Extern]) -> Result { + /// Creates a new [`Instance`] from the previously compiled [`Module`] and + /// list of `imports` specified. + /// + /// This method instantiates the `module` provided with the `imports`, + /// following the procedure in the [core specification][inst] to + /// instantiate. Instantiation can fail for a number of reasons (many + /// specified below), but if successful the `start` function will be + /// automatically run (if provided) and then the [`Instance`] will be + /// returned. + /// + /// ## Providing Imports + /// + /// The `imports` array here is a bit tricky. The entries in the list of + /// `imports` are intended to correspond 1:1 with the list of imports + /// returned by [`Module::imports`]. Before calling [`Instance::new`] you'll + /// want to inspect the return value of [`Module::imports`] and, for each + /// import type, create an [`Extern`] which corresponds to that type. + /// These [`Extern`] values are all then collected into a list and passed to + /// this function. + /// + /// Note that this function is intentionally relatively low level. It is the + /// intention that we'll soon provide a [higher level API][issue] which will + /// be much more ergonomic for instantiating modules. If you need the full + /// power of customization of imports, though, this is the method for you! + /// + /// ## Errors + /// + /// This function can fail for a number of reasons, including, but not + /// limited to: + /// + /// * The number of `imports` provided doesn't match the number of imports + /// returned by the `module`'s [`Module::imports`] method. + /// * The type of any [`Extern`] doesn't match the corresponding + /// [`ExternType`] entry that it maps to. + /// * The `start` function in the instance, if present, traps. + /// * Module/instance resource limits are exceeded. + /// + /// When instantiation fails it's recommended to inspect the return value to + /// see why it failed, or bubble it upwards. If you'd like to specifically + /// check for trap errors, you can use `error.downcast::()`. + /// + /// [inst]: https://webassembly.github.io/spec/core/exec/modules.html#exec-instantiation + /// [issue]: https://github.com/bytecodealliance/wasmtime/issues/727 + pub fn new(module: &Module, imports: &[Extern]) -> Result { let store = module.store(); - let mut instance_handle = instantiate_in_context( - store, - module.binary().expect("binary"), - externs, - module.name(), - )?; + let mut instance_handle = + instantiate(module.compiled_module().expect("compiled_module"), imports)?; let exports = { let mut exports = Vec::with_capacity(module.exports().len()); @@ -107,11 +148,24 @@ impl Instance { &self.module } + /// Returns the list of exported items from this [`Instance`]. + /// + /// Note that the exports here do not have names associated with them, + /// they're simply the values that are exported. To learn the value of each + /// export you'll need to consult [`Module::exports`]. The list returned + /// here maps 1:1 with the list that [`Module::exports`] returns, and + /// [`ExportType`] contains the name of each export. pub fn exports(&self) -> &[Extern] { &self.exports } - pub fn find_export_by_name(&self, name: &str) -> Option<&Extern> { + /// Looks up an exported [`Extern`] value by name. + /// + /// This method will search the module for an export named `name` and return + /// the value, if found. + /// + /// Returns `None` if there was no export named `name`. + pub fn get_export(&self, name: &str) -> Option<&Extern> { let (i, _) = self .module .exports() @@ -121,6 +175,7 @@ impl Instance { Some(&self.exports()[i]) } + #[doc(hidden)] pub fn from_handle(store: &Store, instance_handle: InstanceHandle) -> Instance { let mut exports = Vec::new(); let mut exports_types = Vec::new(); @@ -158,46 +213,14 @@ impl Instance { } } + #[doc(hidden)] pub fn handle(&self) -> &InstanceHandle { &self.instance_handle } + #[doc(hidden)] pub fn get_wasmtime_memory(&self) -> Option { let mut instance_handle = self.instance_handle.clone(); instance_handle.lookup("memory") } } - -// OS-specific signal handling -cfg_if::cfg_if! { - if #[cfg(target_os = "linux")] { - impl Instance { - /// The signal handler must be - /// [async-signal-safe](http://man7.org/linux/man-pages/man7/signal-safety.7.html). - pub fn set_signal_handler(&self, handler: H) - where - H: 'static + Fn(libc::c_int, *const libc::siginfo_t, *const libc::c_void) -> bool, - { - self.instance_handle.clone().set_signal_handler(handler); - } - } - } else if #[cfg(target_os = "windows")] { - impl Instance { - pub fn set_signal_handler(&self, handler: H) - where - H: 'static + Fn(winapi::um::winnt::EXCEPTION_POINTERS) -> bool, - { - self.instance_handle.clone().set_signal_handler(handler); - } - } - } else if #[cfg(target_os = "macos")] { - impl Instance { - pub fn set_signal_handler(&self, handler: H) - where - H: 'static + Fn(libc::c_int, *const libc::siginfo_t, *const libc::c_void) -> bool, - { - self.instance_handle.clone().set_signal_handler(handler); - } - } - } -} diff --git a/crates/api/src/lib.rs b/crates/api/src/lib.rs index 7d1dc17588..63e41fb572 100644 --- a/crates/api/src/lib.rs +++ b/crates/api/src/lib.rs @@ -26,3 +26,13 @@ pub use crate::runtime::{Config, Engine, OptLevel, Store, Strategy}; pub use crate::trap::{FrameInfo, Trap}; pub use crate::types::*; pub use crate::values::*; + +cfg_if::cfg_if! { + if #[cfg(unix)] { + pub mod unix; + } else if #[cfg(windows)] { + pub mod windows; + } else { + // ... unknown os! + } +} diff --git a/crates/api/src/module.rs b/crates/api/src/module.rs index d6fd7f0579..f901a83762 100644 --- a/crates/api/src/module.rs +++ b/crates/api/src/module.rs @@ -9,6 +9,7 @@ use wasmparser::{ validate, CustomSectionKind, ExternalKind, ImportSectionEntryType, ModuleReader, Name, OperatorValidatorConfig, SectionCode, ValidatingParserConfig, }; +use wasmtime_jit::CompiledModule; fn into_memory_type(mt: wasmparser::MemoryType) -> MemoryType { assert!(!mt.shared); @@ -56,12 +57,6 @@ fn into_table_type(tt: wasmparser::TableType) -> TableType { TableType::new(ty, limits) } -#[derive(Clone)] -pub(crate) enum ModuleCodeSource { - Binary(Box<[u8]>), - Unknown, -} - /// A compiled WebAssembly module, ready to be instantiated. /// /// A `Module` is a compiled in-memory representation of an input WebAssembly @@ -84,10 +79,10 @@ pub struct Module { struct ModuleInner { store: Store, - source: ModuleCodeSource, imports: Box<[ImportType]>, exports: Box<[ExportType]>, name: Option, + compiled: Option, } impl Module { @@ -131,7 +126,7 @@ 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 { Module::new_unchecked(store, binary) } + unsafe { Module::new_internal(store, binary, None) } } /// Creates a new WebAssembly `Module` from the given in-memory `binary` @@ -139,9 +134,11 @@ impl Module { /// /// See [`Module::new`] for other details. pub fn new_with_name(store: &Store, binary: &[u8], name: &str) -> Result { - let mut ret = Module::new(store, binary)?; - Rc::get_mut(&mut ret.inner).unwrap().name = Some(name.to_string()); - Ok(ret) + Module::validate(store, binary)?; + // 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 { Module::new_internal(store, binary, Some(name)) } } /// Creates a new WebAssembly `Module` from the given in-memory `binary` @@ -171,8 +168,20 @@ 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 { + Module::new_internal(store, binary, None) + } + + /// Creates a new `Module` and compiles it without doing any validation. + unsafe fn new_internal(store: &Store, binary: &[u8], name: Option<&str>) -> Result { let mut ret = Module::empty(store); ret.read_imports_and_exports(binary)?; + + let inner = Rc::get_mut(&mut ret.inner).unwrap(); + if let Some(name) = name { + // Assign or override the module's name if supplied. + inner.name = Some(name.to_string()); + } + inner.compiled = Some(compile(store, binary, inner.name.as_deref())?); Ok(ret) } @@ -219,19 +228,16 @@ impl Module { Module { inner: Rc::new(ModuleInner { store: store.clone(), - source: ModuleCodeSource::Unknown, imports: Box::new([]), exports: Box::new([]), name: None, + compiled: None, }), } } - pub(crate) fn binary(&self) -> Option<&[u8]> { - match &self.inner.source { - ModuleCodeSource::Binary(b) => Some(b), - _ => None, - } + pub(crate) fn compiled_module(&self) -> Option<&CompiledModule> { + self.inner.compiled.as_ref() } /// Returns identifier/name that this [`Module`] has. This name @@ -259,7 +265,6 @@ impl Module { fn read_imports_and_exports(&mut self, binary: &[u8]) -> Result<()> { let inner = Rc::get_mut(&mut self.inner).unwrap(); - inner.source = ModuleCodeSource::Binary(binary.into()); let mut reader = ModuleReader::new(binary)?; let mut imports = Vec::new(); let mut exports = Vec::new(); @@ -387,3 +392,21 @@ impl Module { Ok(()) } } + +fn compile(store: &Store, binary: &[u8], module_name: Option<&str>) -> Result { + let exports = store.global_exports().clone(); + let compiled_module = CompiledModule::new( + &mut store.compiler_mut(), + binary, + module_name, + exports, + store.engine().config().debug_info, + )?; + + // Register all module signatures + for signature in compiled_module.module().signatures.values() { + store.register_wasmtime_signature(signature); + } + + Ok(compiled_module) +} diff --git a/crates/api/src/trap.rs b/crates/api/src/trap.rs index fb4cdaf760..653fd0abe2 100644 --- a/crates/api/src/trap.rs +++ b/crates/api/src/trap.rs @@ -78,7 +78,25 @@ impl fmt::Debug for Trap { impl fmt::Display for Trap { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.inner.message.fmt(f) + write!(f, "{}", self.inner.message)?; + let trace = self.trace(); + if trace.is_empty() { + return Ok(()); + } + writeln!(f, "\nwasm backtrace:")?; + for (i, frame) in self.trace().iter().enumerate() { + let name = frame.module_name().unwrap_or(""); + write!(f, " {}: {}!", i, name)?; + match frame.func_name() { + Some(name) => match rustc_demangle::try_demangle(name) { + Ok(name) => write!(f, "{}", name)?, + Err(_) => write!(f, "{}", name)?, + }, + None => write!(f, "", frame.func_index)?, + } + writeln!(f, "")?; + } + Ok(()) } } diff --git a/crates/api/src/unix.rs b/crates/api/src/unix.rs new file mode 100644 index 0000000000..f17eecfe13 --- /dev/null +++ b/crates/api/src/unix.rs @@ -0,0 +1,31 @@ +//! Unix-specific extension for the `wasmtime` crate. +//! +//! This module is only available on Unix targets, for example Linux and macOS. +//! It is not available on Windows, for example. Note that the import path for +//! this module is `wasmtime::unix::...`, which is intended to emphasize that it +//! is platform-specific. +//! +//! The traits contained in this module are intended to extend various types +//! throughout the `wasmtime` crate with extra functionality that's only +//! available on Unix. + +use crate::Instance; + +/// Extensions for the [`Instance`] type only available on Unix. +pub trait InstanceExt { + // TODO: needs more docs? + /// The signal handler must be + /// [async-signal-safe](http://man7.org/linux/man-pages/man7/signal-safety.7.html). + unsafe fn set_signal_handler(&self, handler: H) + where + H: 'static + Fn(libc::c_int, *const libc::siginfo_t, *const libc::c_void) -> bool; +} + +impl InstanceExt for Instance { + unsafe fn set_signal_handler(&self, handler: H) + where + H: 'static + Fn(libc::c_int, *const libc::siginfo_t, *const libc::c_void) -> bool, + { + self.instance_handle.clone().set_signal_handler(handler); + } +} diff --git a/crates/api/src/windows.rs b/crates/api/src/windows.rs new file mode 100644 index 0000000000..11ce1bbec0 --- /dev/null +++ b/crates/api/src/windows.rs @@ -0,0 +1,29 @@ +//! windows-specific extension for the `wasmtime` crate. +//! +//! This module is only available on Windows targets. +//! It is not available on Linux or macOS, for example. Note that the import path for +//! this module is `wasmtime::windows::...`, which is intended to emphasize that it +//! is platform-specific. +//! +//! The traits contained in this module are intended to extend various types +//! throughout the `wasmtime` crate with extra functionality that's only +//! available on Windows. + +use crate::Instance; + +/// Extensions for the [`Instance`] type only available on Windows. +pub trait InstanceExt { + // TODO: docs? + unsafe fn set_signal_handler(&self, handler: H) + where + H: 'static + Fn(winapi::um::winnt::EXCEPTION_POINTERS) -> bool; +} + +impl InstanceExt for Instance { + unsafe fn set_signal_handler(&self, handler: H) + where + H: 'static + Fn(winapi::um::winnt::EXCEPTION_POINTERS) -> bool, + { + self.instance_handle.clone().set_signal_handler(handler); + } +} diff --git a/crates/api/tests/import-indexes.rs b/crates/api/tests/import-indexes.rs index 8406a8b63a..2e41ff09eb 100644 --- a/crates/api/tests/import-indexes.rs +++ b/crates/api/tests/import-indexes.rs @@ -57,7 +57,7 @@ fn same_import_names_still_distinct() -> anyhow::Result<()> { ]; let instance = Instance::new(&module, &imports)?; - let func = instance.find_export_by_name("foo").unwrap().func().unwrap(); + let func = instance.get_export("foo").unwrap().func().unwrap(); let results = func.call(&[])?; assert_eq!(results.len(), 1); match results[0] { diff --git a/crates/api/tests/invoke_func_via_table.rs b/crates/api/tests/invoke_func_via_table.rs index 9b64e894d8..3320ebbb9e 100644 --- a/crates/api/tests/invoke_func_via_table.rs +++ b/crates/api/tests/invoke_func_via_table.rs @@ -19,7 +19,7 @@ fn test_invoke_func_via_table() -> Result<()> { let instance = Instance::new(&module, &[]).context("> Error instantiating module!")?; let f = instance - .find_export_by_name("table") + .get_export("table") .unwrap() .table() .unwrap() diff --git a/crates/api/tests/traps.rs b/crates/api/tests/traps.rs index 0c93ca3f82..159aebce5f 100644 --- a/crates/api/tests/traps.rs +++ b/crates/api/tests/traps.rs @@ -1,9 +1,9 @@ +use anyhow::Result; use std::rc::Rc; use wasmtime::*; -use wat::parse_str; #[test] -fn test_trap_return() -> Result<(), String> { +fn test_trap_return() -> Result<()> { struct HelloCallback; impl Callable for HelloCallback { @@ -13,24 +13,20 @@ fn test_trap_return() -> Result<(), String> { } let store = Store::default(); - let binary = parse_str( + let binary = wat::parse_str( r#" (module (func $hello (import "" "hello")) (func (export "run") (call $hello)) ) "#, - ) - .map_err(|e| format!("failed to parse WebAssembly text source: {}", e))?; + )?; - let module = - Module::new(&store, &binary).map_err(|e| format!("failed to compile module: {}", e))?; + let module = Module::new(&store, &binary)?; let hello_type = FuncType::new(Box::new([]), Box::new([])); let hello_func = Func::new(&store, hello_type, Rc::new(HelloCallback)); - let imports = vec![hello_func.into()]; - let instance = Instance::new(&module, &imports) - .map_err(|e| format!("failed to instantiate module: {:?}", e))?; + let instance = Instance::new(&module, &[hello_func.into()])?; let run_func = instance.exports()[0] .func() .expect("expected function export"); @@ -43,22 +39,19 @@ fn test_trap_return() -> Result<(), String> { } #[test] -fn test_trap_trace() -> Result<(), String> { +fn test_trap_trace() -> Result<()> { let store = Store::default(); - let binary = parse_str( + let binary = wat::parse_str( r#" (module $hello_mod (func (export "run") (call $hello)) (func $hello (unreachable)) ) "#, - ) - .map_err(|e| format!("failed to parse WebAssembly text source: {}", e))?; + )?; - let module = - Module::new(&store, &binary).map_err(|e| format!("failed to compile module: {}", e))?; - let instance = Instance::new(&module, &[]) - .map_err(|e| format!("failed to instantiate module: {:?}", e))?; + let module = Module::new(&store, &binary)?; + let instance = Instance::new(&module, &[])?; let run_func = instance.exports()[0] .func() .expect("expected function export"); @@ -79,7 +72,7 @@ fn test_trap_trace() -> Result<(), String> { } #[test] -fn test_trap_trace_cb() -> Result<(), String> { +fn test_trap_trace_cb() -> Result<()> { struct ThrowCallback; impl Callable for ThrowCallback { @@ -89,7 +82,7 @@ fn test_trap_trace_cb() -> Result<(), String> { } let store = Store::default(); - let binary = parse_str( + let binary = wat::parse_str( r#" (module $hello_mod (import "" "throw" (func $throw)) @@ -97,16 +90,13 @@ fn test_trap_trace_cb() -> Result<(), String> { (func $hello (call $throw)) ) "#, - ) - .map_err(|e| format!("failed to parse WebAssembly text source: {}", e))?; + )?; let fn_type = FuncType::new(Box::new([]), Box::new([])); let fn_func = Func::new(&store, fn_type, Rc::new(ThrowCallback)); - let module = - Module::new(&store, &binary).map_err(|e| format!("failed to compile module: {}", e))?; - let instance = Instance::new(&module, &[fn_func.into()]) - .map_err(|e| format!("failed to instantiate module: {:?}", e))?; + let module = Module::new(&store, &binary)?; + let instance = Instance::new(&module, &[fn_func.into()])?; let run_func = instance.exports()[0] .func() .expect("expected function export"); @@ -125,21 +115,18 @@ fn test_trap_trace_cb() -> Result<(), String> { } #[test] -fn test_trap_stack_overflow() -> Result<(), String> { +fn test_trap_stack_overflow() -> Result<()> { let store = Store::default(); - let binary = parse_str( + let binary = wat::parse_str( r#" (module $rec_mod (func $run (export "run") (call $run)) ) "#, - ) - .map_err(|e| format!("failed to parse WebAssembly text source: {}", e))?; + )?; - let module = - Module::new(&store, &binary).map_err(|e| format!("failed to compile module: {}", e))?; - let instance = Instance::new(&module, &[]) - .map_err(|e| format!("failed to instantiate module: {:?}", e))?; + let module = Module::new(&store, &binary)?; + let instance = Instance::new(&module, &[])?; let run_func = instance.exports()[0] .func() .expect("expected function export"); @@ -151,9 +138,94 @@ fn test_trap_stack_overflow() -> Result<(), String> { for i in 0..trace.len() { assert_eq!(trace[i].module_name().unwrap(), "rec_mod"); assert_eq!(trace[i].func_index(), 0); - assert_eq!(trace[1].func_name(), Some("run")); + assert_eq!(trace[i].func_name(), Some("run")); } assert!(e.message().contains("call stack exhausted")); Ok(()) } + +#[test] +fn trap_display_pretty() -> Result<()> { + let store = Store::default(); + let binary = wat::parse_str( + r#" + (module $m + (func $die unreachable) + (func call $die) + (func $foo call 1) + (func (export "bar") call $foo) + ) + "#, + )?; + + let module = Module::new(&store, &binary)?; + let instance = Instance::new(&module, &[])?; + let run_func = instance.exports()[0] + .func() + .expect("expected function export"); + + let e = run_func.call(&[]).err().expect("error calling function"); + assert_eq!( + e.to_string(), + "\ +wasm trap: unreachable, source location: @0023 +wasm backtrace: + 0: m!die + 1: m! + 2: m!foo + 3: m! +" + ); + Ok(()) +} + +#[test] +fn trap_display_multi_module() -> Result<()> { + let store = Store::default(); + let binary = wat::parse_str( + r#" + (module $a + (func $die unreachable) + (func call $die) + (func $foo call 1) + (func (export "bar") call $foo) + ) + "#, + )?; + + let module = Module::new(&store, &binary)?; + let instance = Instance::new(&module, &[])?; + let bar = instance.exports()[0].clone(); + + let binary = wat::parse_str( + r#" + (module $b + (import "" "" (func $bar)) + (func $middle call $bar) + (func (export "bar2") call $middle) + ) + "#, + )?; + let module = Module::new(&store, &binary)?; + let instance = Instance::new(&module, &[bar])?; + let bar2 = instance.exports()[0] + .func() + .expect("expected function export"); + + let e = bar2.call(&[]).err().expect("error calling function"); + assert_eq!( + e.to_string(), + "\ +wasm trap: unreachable, source location: @0023 +wasm backtrace: + 0: a!die + 1: a! + 2: a!foo + 3: a! + 4: b!middle + 5: b! +" + ); + Ok(()) +} diff --git a/crates/fuzzing/Cargo.toml b/crates/fuzzing/Cargo.toml index 81b1d471b3..98fe89affe 100644 --- a/crates/fuzzing/Cargo.toml +++ b/crates/fuzzing/Cargo.toml @@ -17,8 +17,6 @@ log = "0.4.8" wasmparser = "0.47.0" wasmprinter = "0.2.0" wasmtime = { path = "../api", version = "0.9.0" } -wasmtime-environ = { path = "../environ", version = "0.9.0" } -wasmtime-jit = { path = "../jit", version = "0.9.0" } [dev-dependencies] wat = "1.0" diff --git a/crates/fuzzing/src/oracles.rs b/crates/fuzzing/src/oracles.rs index bb5860c9a1..09d7afca8c 100644 --- a/crates/fuzzing/src/oracles.rs +++ b/crates/fuzzing/src/oracles.rs @@ -13,25 +13,15 @@ pub mod dummy; use dummy::{dummy_imports, dummy_value}; -use std::cell::RefCell; use std::collections::HashMap; -use std::rc::Rc; use wasmtime::*; -use wasmtime_environ::{isa, settings}; -use wasmtime_jit::{native, CompilationStrategy, CompiledModule, Compiler, NullResolver}; - -fn host_isa() -> Box { - let flag_builder = settings::builder(); - let isa_builder = native::builder(); - isa_builder.finish(settings::Flags::new(flag_builder)) -} /// Instantiate the Wasm buffer, and implicitly fail if we have an unexpected /// panic or segfault or anything else that can be detected "passively". /// /// Performs initial validation, and returns early if the Wasm is invalid. /// -/// You can control which compiler is used via passing a `CompilationStrategy`. +/// You can control which compiler is used via passing a `Strategy`. pub fn instantiate(wasm: &[u8], strategy: Strategy) { if wasmparser::validate(wasm, None).is_err() { return; @@ -68,24 +58,13 @@ pub fn instantiate(wasm: &[u8], strategy: Strategy) { /// /// Performs initial validation, and returns early if the Wasm is invalid. /// -/// You can control which compiler is used via passing a `CompilationStrategy`. -pub fn compile(wasm: &[u8], compilation_strategy: CompilationStrategy) { - if wasmparser::validate(wasm, None).is_err() { - return; - } - - let isa = host_isa(); - 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, - None, - &mut resolver, - global_exports, - false, - ); +/// You can control which compiler is used via passing a `Strategy`. +pub fn compile(wasm: &[u8], strategy: Strategy) { + let mut config = Config::new(); + config.strategy(strategy).unwrap(); + let engine = Engine::new(&config); + let store = Store::new(&engine); + let _ = Module::new(&store, wasm); } /// Invoke the given API calls. diff --git a/crates/interface-types/src/lib.rs b/crates/interface-types/src/lib.rs index 4934e7370c..85278872a0 100644 --- a/crates/interface-types/src/lib.rs +++ b/crates/interface-types/src/lib.rs @@ -136,7 +136,7 @@ impl ModuleData { let outgoing = binding.result_bindings()?; let f = instance - .find_export_by_name(export) + .get_export(export) .ok_or_else(|| format_err!("failed to find export `{}`", export))? .func() .ok_or_else(|| format_err!("`{}` is not a function", export))? @@ -147,21 +147,19 @@ impl ModuleData { .into_iter() .map(|rv| rv.into()) .collect::>(); - let wasm_results = match f.call(&wasm_args) { - Ok(values) => values - .to_vec() - .into_iter() - .map(|v: wasmtime::Val| match v { - wasmtime::Val::I32(i) => RuntimeValue::I32(i), - wasmtime::Val::I64(i) => RuntimeValue::I64(i), - wasmtime::Val::F32(i) => RuntimeValue::F32(i), - wasmtime::Val::F64(i) => RuntimeValue::F64(i), - wasmtime::Val::V128(i) => RuntimeValue::V128(i.to_le_bytes()), - _ => panic!("unsupported value {:?}", v), - }) - .collect::>(), - Err(trap) => bail!("trapped: {:?}", trap), - }; + let wasm_results = f + .call(&wasm_args)? + .to_vec() + .into_iter() + .map(|v: wasmtime::Val| match v { + wasmtime::Val::I32(i) => RuntimeValue::I32(i), + wasmtime::Val::I64(i) => RuntimeValue::I64(i), + wasmtime::Val::F32(i) => RuntimeValue::F32(i), + wasmtime::Val::F64(i) => RuntimeValue::F64(i), + wasmtime::Val::V128(i) => RuntimeValue::V128(i.to_le_bytes()), + _ => panic!("unsupported value {:?}", v), + }) + .collect::>(); translate_outgoing(&mut cx, &outgoing, &wasm_results) } @@ -327,16 +325,13 @@ impl TranslateContext for InstanceTranslateContext { fn invoke_alloc(&mut self, alloc_func_name: &str, len: i32) -> Result { let alloc = self .0 - .find_export_by_name(alloc_func_name) + .get_export(alloc_func_name) .ok_or_else(|| format_err!("failed to find alloc function `{}`", alloc_func_name))? .func() .ok_or_else(|| format_err!("`{}` is not a (alloc) function", alloc_func_name))? .clone(); let alloc_args = vec![wasmtime::Val::I32(len)]; - let results = match alloc.call(&alloc_args) { - Ok(values) => values, - Err(trap) => bail!("trapped: {:?}", trap), - }; + let results = alloc.call(&alloc_args)?; if results.len() != 1 { bail!("allocator function wrong number of results"); } @@ -348,7 +343,7 @@ impl TranslateContext for InstanceTranslateContext { unsafe fn get_memory(&mut self) -> Result<&mut [u8]> { let memory = self .0 - .find_export_by_name("memory") + .get_export("memory") .ok_or_else(|| format_err!("failed to find `memory` export"))? .memory() .ok_or_else(|| format_err!("`memory` is not a memory"))? diff --git a/crates/jit/Cargo.toml b/crates/jit/Cargo.toml index 27490f81e1..4c4901c1ed 100644 --- a/crates/jit/Cargo.toml +++ b/crates/jit/Cargo.toml @@ -25,6 +25,7 @@ target-lexicon = { version = "0.10.0", default-features = false } wasmparser = { version = "0.47.0", default-features = false } more-asserts = "0.2.1" anyhow = "1.0" +cfg-if = "0.1.9" [target.'cfg(target_os = "windows")'.dependencies] winapi = { version = "0.3.7", features = ["winnt", "impl-default"] } diff --git a/crates/jit/src/context.rs b/crates/jit/src/context.rs index 8632b0db8e..98f5d17682 100644 --- a/crates/jit/src/context.rs +++ b/crates/jit/src/context.rs @@ -156,7 +156,6 @@ impl Context { &mut *self.compiler, data, None, - &mut self.namespace, Rc::clone(&self.global_exports), debug_info, ) diff --git a/crates/jit/src/imports.rs b/crates/jit/src/imports.rs new file mode 100644 index 0000000000..a8f7daa24f --- /dev/null +++ b/crates/jit/src/imports.rs @@ -0,0 +1,289 @@ +//! Module imports resolving logic. + +use crate::resolver::Resolver; +use more_asserts::assert_ge; +use std::collections::HashSet; +use wasmtime_environ::entity::PrimaryMap; +use wasmtime_environ::wasm::{Global, GlobalInit, Memory, Table, TableElementType}; +use wasmtime_environ::{MemoryPlan, MemoryStyle, Module, TablePlan}; +use wasmtime_runtime::{ + Export, Imports, InstanceHandle, LinkError, VMFunctionImport, VMGlobalImport, VMMemoryImport, + VMTableImport, +}; + +/// This function allows to match all imports of a `Module` with concrete definitions provided by +/// a `Resolver`. +/// +/// If all imports are satisfied returns an `Imports` instance required for a module instantiation. +pub fn resolve_imports(module: &Module, resolver: &mut dyn Resolver) -> Result { + let mut dependencies = HashSet::new(); + + let mut function_imports = PrimaryMap::with_capacity(module.imported_funcs.len()); + for (index, (module_name, field, import_idx)) in module.imported_funcs.iter() { + match resolver.resolve(*import_idx, module_name, field) { + Some(export_value) => match export_value { + Export::Function { + address, + signature, + vmctx, + } => { + let import_signature = &module.signatures[module.functions[index]]; + if signature != *import_signature { + // TODO: If the difference is in the calling convention, + // we could emit a wrapper function to fix it up. + return Err(LinkError(format!( + "{}/{}: incompatible import type: exported function with signature {} \ + incompatible with function import with signature {}", + module_name, field, signature, import_signature + ))); + } + dependencies.insert(unsafe { InstanceHandle::from_vmctx(vmctx) }); + function_imports.push(VMFunctionImport { + body: address, + vmctx, + }); + } + Export::Table { .. } | Export::Memory { .. } | Export::Global { .. } => { + return Err(LinkError(format!( + "{}/{}: incompatible import type: export incompatible with function import", + module_name, field + ))); + } + }, + None => { + return Err(LinkError(format!( + "{}/{}: unknown import function: function not provided", + module_name, field + ))); + } + } + } + + let mut table_imports = PrimaryMap::with_capacity(module.imported_tables.len()); + for (index, (module_name, field, import_idx)) in module.imported_tables.iter() { + match resolver.resolve(*import_idx, module_name, field) { + Some(export_value) => match export_value { + Export::Table { + definition, + vmctx, + table, + } => { + let import_table = &module.table_plans[index]; + if !is_table_compatible(&table, import_table) { + return Err(LinkError(format!( + "{}/{}: incompatible import type: exported table incompatible with \ + table import", + module_name, field, + ))); + } + dependencies.insert(unsafe { InstanceHandle::from_vmctx(vmctx) }); + table_imports.push(VMTableImport { + from: definition, + vmctx, + }); + } + Export::Global { .. } | Export::Memory { .. } | Export::Function { .. } => { + return Err(LinkError(format!( + "{}/{}: incompatible import type: export incompatible with table import", + module_name, field + ))); + } + }, + None => { + return Err(LinkError(format!( + "unknown import: no provided import table for {}/{}", + module_name, field + ))); + } + } + } + + let mut memory_imports = PrimaryMap::with_capacity(module.imported_memories.len()); + for (index, (module_name, field, import_idx)) in module.imported_memories.iter() { + match resolver.resolve(*import_idx, module_name, field) { + Some(export_value) => match export_value { + Export::Memory { + definition, + vmctx, + memory, + } => { + let import_memory = &module.memory_plans[index]; + if !is_memory_compatible(&memory, import_memory) { + return Err(LinkError(format!( + "{}/{}: incompatible import type: exported memory incompatible with \ + memory import", + module_name, field + ))); + } + + // Sanity-check: Ensure that the imported memory has at least + // guard-page protections the importing module expects it to have. + if let ( + MemoryStyle::Static { bound }, + MemoryStyle::Static { + bound: import_bound, + }, + ) = (memory.style, &import_memory.style) + { + assert_ge!(bound, *import_bound); + } + assert_ge!(memory.offset_guard_size, import_memory.offset_guard_size); + + dependencies.insert(unsafe { InstanceHandle::from_vmctx(vmctx) }); + memory_imports.push(VMMemoryImport { + from: definition, + vmctx, + }); + } + Export::Table { .. } | Export::Global { .. } | Export::Function { .. } => { + return Err(LinkError(format!( + "{}/{}: incompatible import type: export incompatible with memory import", + module_name, field + ))); + } + }, + None => { + return Err(LinkError(format!( + "unknown import: no provided import memory for {}/{}", + module_name, field + ))); + } + } + } + + let mut global_imports = PrimaryMap::with_capacity(module.imported_globals.len()); + for (index, (module_name, field, import_idx)) in module.imported_globals.iter() { + match resolver.resolve(*import_idx, module_name, field) { + Some(export_value) => match export_value { + Export::Table { .. } | Export::Memory { .. } | Export::Function { .. } => { + return Err(LinkError(format!( + "{}/{}: incompatible import type: exported global incompatible with \ + global import", + module_name, field + ))); + } + Export::Global { + definition, + vmctx, + global, + } => { + let imported_global = module.globals[index]; + if !is_global_compatible(&global, &imported_global) { + return Err(LinkError(format!( + "{}/{}: incompatible import type: exported global incompatible with \ + global import", + module_name, field + ))); + } + dependencies.insert(unsafe { InstanceHandle::from_vmctx(vmctx) }); + global_imports.push(VMGlobalImport { from: definition }); + } + }, + None => { + return Err(LinkError(format!( + "unknown import: no provided import global for {}/{}", + module_name, field + ))); + } + } + } + + Ok(Imports::new( + dependencies, + function_imports, + table_imports, + memory_imports, + global_imports, + )) +} + +fn is_global_compatible(exported: &Global, imported: &Global) -> bool { + match imported.initializer { + GlobalInit::Import => (), + _ => panic!("imported Global should have an Imported initializer"), + } + + let Global { + ty: exported_ty, + mutability: exported_mutability, + initializer: _exported_initializer, + } = exported; + let Global { + ty: imported_ty, + mutability: imported_mutability, + initializer: _imported_initializer, + } = imported; + exported_ty == imported_ty && imported_mutability == exported_mutability +} + +fn is_table_element_type_compatible( + exported_type: TableElementType, + imported_type: TableElementType, +) -> bool { + match exported_type { + TableElementType::Func => match imported_type { + TableElementType::Func => true, + _ => false, + }, + TableElementType::Val(exported_val_ty) => match imported_type { + TableElementType::Val(imported_val_ty) => exported_val_ty == imported_val_ty, + _ => false, + }, + } +} + +fn is_table_compatible(exported: &TablePlan, imported: &TablePlan) -> bool { + let TablePlan { + table: + Table { + ty: exported_ty, + minimum: exported_minimum, + maximum: exported_maximum, + }, + style: _exported_style, + } = exported; + let TablePlan { + table: + Table { + ty: imported_ty, + minimum: imported_minimum, + maximum: imported_maximum, + }, + style: _imported_style, + } = imported; + + is_table_element_type_compatible(*exported_ty, *imported_ty) + && imported_minimum <= exported_minimum + && (imported_maximum.is_none() + || (!exported_maximum.is_none() + && imported_maximum.unwrap() >= exported_maximum.unwrap())) +} + +fn is_memory_compatible(exported: &MemoryPlan, imported: &MemoryPlan) -> bool { + let MemoryPlan { + memory: + Memory { + minimum: exported_minimum, + maximum: exported_maximum, + shared: exported_shared, + }, + style: _exported_style, + offset_guard_size: _exported_offset_guard_size, + } = exported; + let MemoryPlan { + memory: + Memory { + minimum: imported_minimum, + maximum: imported_maximum, + shared: imported_shared, + }, + style: _imported_style, + offset_guard_size: _imported_offset_guard_size, + } = imported; + + imported_minimum <= exported_minimum + && (imported_maximum.is_none() + || (!exported_maximum.is_none() + && imported_maximum.unwrap() >= exported_maximum.unwrap())) + && exported_shared == imported_shared +} diff --git a/crates/jit/src/instantiate.rs b/crates/jit/src/instantiate.rs index dd927b2ed7..c303266c43 100644 --- a/crates/jit/src/instantiate.rs +++ b/crates/jit/src/instantiate.rs @@ -4,6 +4,7 @@ //! steps. use crate::compiler::Compiler; +use crate::imports::resolve_imports; use crate::link::link_module; use crate::resolver::Resolver; use std::cell::RefCell; @@ -19,7 +20,7 @@ use wasmtime_environ::{ ModuleSyncString, }; use wasmtime_runtime::{ - Export, GdbJitImageRegistration, Imports, InstanceHandle, InstantiationError, VMFunctionBody, + Export, GdbJitImageRegistration, InstanceHandle, InstantiationError, VMFunctionBody, VMSharedSignatureIndex, }; @@ -50,7 +51,6 @@ pub enum SetupError { struct RawCompiledModule<'data> { module: Module, finished_functions: BoxedSlice, - imports: Imports, data_initializers: Box<[DataInitializer<'data>]>, signatures: BoxedSlice, dbg_jit_registration: Option, @@ -62,7 +62,6 @@ impl<'data> RawCompiledModule<'data> { compiler: &mut Compiler, data: &'data [u8], module_name: Option<&str>, - resolver: &mut dyn Resolver, debug_info: bool, ) -> Result { let environ = ModuleEnvironment::new(compiler.frontend_config(), compiler.tunables()); @@ -86,14 +85,12 @@ impl<'data> RawCompiledModule<'data> { debug_data, )?; - let imports = link_module( + link_module( &translation.module, &allocated_functions, &jt_offsets, relocations, - resolver, - ) - .map_err(|err| SetupError::Instantiate(InstantiationError::Link(err)))?; + ); // Gather up the pointers to the compiled functions. let finished_functions: BoxedSlice = @@ -132,7 +129,6 @@ impl<'data> RawCompiledModule<'data> { Ok(Self { module: translation.module, finished_functions, - imports, data_initializers: translation.data_initializers.into_boxed_slice(), signatures: signatures.into_boxed_slice(), dbg_jit_registration, @@ -144,7 +140,6 @@ impl<'data> RawCompiledModule<'data> { pub struct CompiledModule { module: Rc, finished_functions: BoxedSlice, - imports: Imports, data_initializers: Box<[OwnedDataInitializer]>, signatures: BoxedSlice, global_exports: Rc>>>, @@ -157,18 +152,15 @@ impl CompiledModule { compiler: &mut Compiler, data: &'data [u8], module_name: Option<&str>, - resolver: &mut dyn Resolver, global_exports: Rc>>>, debug_info: bool, ) -> Result { - let raw = - RawCompiledModule::<'data>::new(compiler, data, module_name, resolver, debug_info)?; + let raw = RawCompiledModule::<'data>::new(compiler, data, module_name, debug_info)?; Ok(Self::from_parts( raw.module, global_exports, raw.finished_functions, - raw.imports, raw.data_initializers .iter() .map(OwnedDataInitializer::new) @@ -184,7 +176,6 @@ impl CompiledModule { module: Module, global_exports: Rc>>>, finished_functions: BoxedSlice, - imports: Imports, data_initializers: Box<[OwnedDataInitializer]>, signatures: BoxedSlice, dbg_jit_registration: Option, @@ -193,7 +184,6 @@ impl CompiledModule { module: Rc::new(module), global_exports: Rc::clone(&global_exports), finished_functions, - imports, data_initializers, signatures, dbg_jit_registration: dbg_jit_registration.map(Rc::new), @@ -205,7 +195,10 @@ impl CompiledModule { /// Note that if only one instance of this module is needed, it may be more /// efficient to call the top-level `instantiate`, since that avoids copying /// the data initializers. - pub fn instantiate(&mut self) -> Result { + pub fn instantiate( + &self, + resolver: &mut dyn Resolver, + ) -> Result { let data_initializers = self .data_initializers .iter() @@ -214,11 +207,12 @@ impl CompiledModule { data: &*init.data, }) .collect::>(); + let imports = resolve_imports(&self.module, resolver)?; InstanceHandle::new( Rc::clone(&self.module), Rc::clone(&self.global_exports), self.finished_functions.clone(), - self.imports.clone(), + imports, &data_initializers, self.signatures.clone(), self.dbg_jit_registration.as_ref().map(|r| Rc::clone(&r)), @@ -269,13 +263,14 @@ pub fn instantiate( global_exports: Rc>>>, debug_info: bool, ) -> Result { - let raw = RawCompiledModule::new(compiler, data, module_name, resolver, debug_info)?; - + let raw = RawCompiledModule::new(compiler, data, module_name, debug_info)?; + let imports = resolve_imports(&raw.module, resolver) + .map_err(|err| SetupError::Instantiate(InstantiationError::Link(err)))?; InstanceHandle::new( Rc::new(raw.module), global_exports, raw.finished_functions, - raw.imports, + imports, &*raw.data_initializers, raw.signatures, raw.dbg_jit_registration.map(Rc::new), diff --git a/crates/jit/src/lib.rs b/crates/jit/src/lib.rs index bc5fc3a2fe..30cf311991 100644 --- a/crates/jit/src/lib.rs +++ b/crates/jit/src/lib.rs @@ -26,6 +26,7 @@ mod code_memory; mod compiler; mod context; mod function_table; +mod imports; mod instantiate; mod link; mod namespace; diff --git a/crates/jit/src/link.rs b/crates/jit/src/link.rs index f8c9c62589..ac8c7c97e9 100644 --- a/crates/jit/src/link.rs +++ b/crates/jit/src/link.rs @@ -1,313 +1,22 @@ //! Linking for JIT-compiled code. -use crate::resolver::Resolver; use cranelift_codegen::binemit::Reloc; use cranelift_codegen::ir::JumpTableOffsets; -use more_asserts::assert_ge; -use std::collections::HashSet; use std::ptr::write_unaligned; use wasmtime_environ::entity::PrimaryMap; -use wasmtime_environ::wasm::{ - DefinedFuncIndex, Global, GlobalInit, Memory, Table, TableElementType, -}; -use wasmtime_environ::{ - MemoryPlan, MemoryStyle, Module, Relocation, RelocationTarget, Relocations, TablePlan, -}; +use wasmtime_environ::wasm::DefinedFuncIndex; +use wasmtime_environ::{Module, RelocationTarget, Relocations}; use wasmtime_runtime::libcalls; -use wasmtime_runtime::{ - Export, Imports, InstanceHandle, LinkError, VMFunctionBody, VMFunctionImport, VMGlobalImport, - VMMemoryImport, VMTableImport, -}; +use wasmtime_runtime::VMFunctionBody; /// Links a module that has been compiled with `compiled_module` in `wasmtime-environ`. +/// +/// Performs all required relocations inside the function code, provided the necessary metadata. pub fn link_module( module: &Module, allocated_functions: &PrimaryMap, jt_offsets: &PrimaryMap, relocations: Relocations, - resolver: &mut dyn Resolver, -) -> Result { - let mut dependencies = HashSet::new(); - - let mut function_imports = PrimaryMap::with_capacity(module.imported_funcs.len()); - for (index, (module_name, field, import_idx)) in module.imported_funcs.iter() { - match resolver.resolve(*import_idx, module_name, field) { - Some(export_value) => match export_value { - Export::Function { - address, - signature, - vmctx, - } => { - let import_signature = &module.signatures[module.functions[index]]; - if signature != *import_signature { - // TODO: If the difference is in the calling convention, - // we could emit a wrapper function to fix it up. - return Err(LinkError(format!( - "{}/{}: incompatible import type: exported function with signature {} \ - incompatible with function import with signature {}", - module_name, field, signature, import_signature - ))); - } - dependencies.insert(unsafe { InstanceHandle::from_vmctx(vmctx) }); - function_imports.push(VMFunctionImport { - body: address, - vmctx, - }); - } - Export::Table { .. } | Export::Memory { .. } | Export::Global { .. } => { - return Err(LinkError(format!( - "{}/{}: incompatible import type: export incompatible with function import", - module_name, field - ))); - } - }, - None => { - return Err(LinkError(format!( - "{}/{}: unknown import function: function not provided", - module_name, field - ))); - } - } - } - - let mut table_imports = PrimaryMap::with_capacity(module.imported_tables.len()); - for (index, (module_name, field, import_idx)) in module.imported_tables.iter() { - match resolver.resolve(*import_idx, module_name, field) { - Some(export_value) => match export_value { - Export::Table { - definition, - vmctx, - table, - } => { - let import_table = &module.table_plans[index]; - if !is_table_compatible(&table, import_table) { - return Err(LinkError(format!( - "{}/{}: incompatible import type: exported table incompatible with \ - table import", - module_name, field, - ))); - } - dependencies.insert(unsafe { InstanceHandle::from_vmctx(vmctx) }); - table_imports.push(VMTableImport { - from: definition, - vmctx, - }); - } - Export::Global { .. } | Export::Memory { .. } | Export::Function { .. } => { - return Err(LinkError(format!( - "{}/{}: incompatible import type: export incompatible with table import", - module_name, field - ))); - } - }, - None => { - return Err(LinkError(format!( - "unknown import: no provided import table for {}/{}", - module_name, field - ))); - } - } - } - - let mut memory_imports = PrimaryMap::with_capacity(module.imported_memories.len()); - for (index, (module_name, field, import_idx)) in module.imported_memories.iter() { - match resolver.resolve(*import_idx, module_name, field) { - Some(export_value) => match export_value { - Export::Memory { - definition, - vmctx, - memory, - } => { - let import_memory = &module.memory_plans[index]; - if !is_memory_compatible(&memory, import_memory) { - return Err(LinkError(format!( - "{}/{}: incompatible import type: exported memory incompatible with \ - memory import", - module_name, field - ))); - } - - // Sanity-check: Ensure that the imported memory has at least - // guard-page protections the importing module expects it to have. - if let ( - MemoryStyle::Static { bound }, - MemoryStyle::Static { - bound: import_bound, - }, - ) = (memory.style, &import_memory.style) - { - assert_ge!(bound, *import_bound); - } - assert_ge!(memory.offset_guard_size, import_memory.offset_guard_size); - - dependencies.insert(unsafe { InstanceHandle::from_vmctx(vmctx) }); - memory_imports.push(VMMemoryImport { - from: definition, - vmctx, - }); - } - Export::Table { .. } | Export::Global { .. } | Export::Function { .. } => { - return Err(LinkError(format!( - "{}/{}: incompatible import type: export incompatible with memory import", - module_name, field - ))); - } - }, - None => { - return Err(LinkError(format!( - "unknown import: no provided import memory for {}/{}", - module_name, field - ))); - } - } - } - - let mut global_imports = PrimaryMap::with_capacity(module.imported_globals.len()); - for (index, (module_name, field, import_idx)) in module.imported_globals.iter() { - match resolver.resolve(*import_idx, module_name, field) { - Some(export_value) => match export_value { - Export::Table { .. } | Export::Memory { .. } | Export::Function { .. } => { - return Err(LinkError(format!( - "{}/{}: incompatible import type: exported global incompatible with \ - global import", - module_name, field - ))); - } - Export::Global { - definition, - vmctx, - global, - } => { - let imported_global = module.globals[index]; - if !is_global_compatible(&global, &imported_global) { - return Err(LinkError(format!( - "{}/{}: incompatible import type: exported global incompatible with \ - global import", - module_name, field - ))); - } - dependencies.insert(unsafe { InstanceHandle::from_vmctx(vmctx) }); - global_imports.push(VMGlobalImport { from: definition }); - } - }, - None => { - return Err(LinkError(format!( - "unknown import: no provided import global for {}/{}", - module_name, field - ))); - } - } - } - - // Apply relocations, now that we have virtual addresses for everything. - relocate(allocated_functions, jt_offsets, relocations, module); - - Ok(Imports::new( - dependencies, - function_imports, - table_imports, - memory_imports, - global_imports, - )) -} - -fn is_global_compatible(exported: &Global, imported: &Global) -> bool { - match imported.initializer { - GlobalInit::Import => (), - _ => panic!("imported Global should have an Imported initializer"), - } - - let Global { - ty: exported_ty, - mutability: exported_mutability, - initializer: _exported_initializer, - } = exported; - let Global { - ty: imported_ty, - mutability: imported_mutability, - initializer: _imported_initializer, - } = imported; - exported_ty == imported_ty && imported_mutability == exported_mutability -} - -fn is_table_element_type_compatible( - exported_type: TableElementType, - imported_type: TableElementType, -) -> bool { - match exported_type { - TableElementType::Func => match imported_type { - TableElementType::Func => true, - _ => false, - }, - TableElementType::Val(exported_val_ty) => match imported_type { - TableElementType::Val(imported_val_ty) => exported_val_ty == imported_val_ty, - _ => false, - }, - } -} - -fn is_table_compatible(exported: &TablePlan, imported: &TablePlan) -> bool { - let TablePlan { - table: - Table { - ty: exported_ty, - minimum: exported_minimum, - maximum: exported_maximum, - }, - style: _exported_style, - } = exported; - let TablePlan { - table: - Table { - ty: imported_ty, - minimum: imported_minimum, - maximum: imported_maximum, - }, - style: _imported_style, - } = imported; - - is_table_element_type_compatible(*exported_ty, *imported_ty) - && imported_minimum <= exported_minimum - && (imported_maximum.is_none() - || (!exported_maximum.is_none() - && imported_maximum.unwrap() >= exported_maximum.unwrap())) -} - -fn is_memory_compatible(exported: &MemoryPlan, imported: &MemoryPlan) -> bool { - let MemoryPlan { - memory: - Memory { - minimum: exported_minimum, - maximum: exported_maximum, - shared: exported_shared, - }, - style: _exported_style, - offset_guard_size: _exported_offset_guard_size, - } = exported; - let MemoryPlan { - memory: - Memory { - minimum: imported_minimum, - maximum: imported_maximum, - shared: imported_shared, - }, - style: _imported_style, - offset_guard_size: _imported_offset_guard_size, - } = imported; - - imported_minimum <= exported_minimum - && (imported_maximum.is_none() - || (!exported_maximum.is_none() - && imported_maximum.unwrap() >= exported_maximum.unwrap())) - && exported_shared == imported_shared -} - -/// Performs the relocations inside the function bytecode, provided the necessary metadata. -fn relocate( - allocated_functions: &PrimaryMap, - jt_offsets: &PrimaryMap, - relocations: PrimaryMap>, - module: &Module, ) { for (i, function_relocs) in relocations.into_iter() { for r in function_relocs { @@ -335,16 +44,7 @@ fn relocate( FloorF64 => wasmtime_f64_floor as usize, TruncF64 => wasmtime_f64_trunc as usize, NearestF64 => wasmtime_f64_nearest as usize, - #[cfg(not(target_os = "windows"))] - Probestack => __rust_probestack as usize, - #[cfg(all(target_os = "windows", target_env = "gnu"))] - Probestack => ___chkstk as usize, - #[cfg(all( - target_os = "windows", - target_env = "msvc", - target_pointer_width = "64" - ))] - Probestack => __chkstk as usize, + Probestack => PROBESTACK as usize, other => panic!("unexpected libcall: {}", other), } } @@ -398,19 +98,33 @@ fn relocate( } } -/// A declaration for the stack probe function in Rust's standard library, for -/// catching callstack overflow. -extern "C" { - #[cfg(not(target_os = "windows"))] - pub fn __rust_probestack(); - #[cfg(all( - target_os = "windows", - target_env = "msvc", - target_pointer_width = "64" - ))] - pub fn __chkstk(); - // ___chkstk (note the triple underscore) is implemented in compiler-builtins/src/x86_64.rs - // by the Rust compiler for the MinGW target - #[cfg(all(target_os = "windows", target_env = "gnu"))] - pub fn ___chkstk(); +// A declaration for the stack probe function in Rust's standard library, for +// catching callstack overflow. +cfg_if::cfg_if! { + if #[cfg(any( + target_arch="aarch64", + all( + target_os = "windows", + target_env = "msvc", + target_pointer_width = "64" + ) + ))] { + extern "C" { + pub fn __chkstk(); + } + const PROBESTACK: unsafe extern "C" fn() = __chkstk; + } else if #[cfg(all(target_os = "windows", target_env = "gnu"))] { + extern "C" { + // ___chkstk (note the triple underscore) is implemented in compiler-builtins/src/x86_64.rs + // by the Rust compiler for the MinGW target + #[cfg(all(target_os = "windows", target_env = "gnu"))] + pub fn ___chkstk(); + } + const PROBESTACK: unsafe extern "C" fn() = ___chkstk; + } else { + extern "C" { + pub fn __rust_probestack(); + } + static PROBESTACK: unsafe extern "C" fn() = __rust_probestack; + } } diff --git a/crates/misc/py/src/function.rs b/crates/misc/py/src/function.rs index 8227394c7a..4b73bf9430 100644 --- a/crates/misc/py/src/function.rs +++ b/crates/misc/py/src/function.rs @@ -20,7 +20,7 @@ impl Function { pub fn func(&self) -> wasmtime::Func { let e = self .instance - .find_export_by_name(&self.export_name) + .get_export(&self.export_name) .expect("named export") .clone(); e.func().expect("function export").clone() diff --git a/crates/misc/py/src/lib.rs b/crates/misc/py/src/lib.rs index 61a6132c53..b84975e178 100644 --- a/crates/misc/py/src/lib.rs +++ b/crates/misc/py/src/lib.rs @@ -107,7 +107,7 @@ pub fn instantiate( .as_ref() .unwrap() .1 - .find_export_by_name(i.name()) + .get_export(i.name()) .ok_or_else(|| { PyErr::new::(format!("wasi export {} is not found", i.name(),)) })?; diff --git a/crates/misc/rust/macro/src/lib.rs b/crates/misc/rust/macro/src/lib.rs index 0cbc1e027c..75198534fa 100644 --- a/crates/misc/rust/macro/src/lib.rs +++ b/crates/misc/rust/macro/src/lib.rs @@ -67,7 +67,7 @@ fn generate_load(item: &syn::ItemTrait) -> syn::Result { if i.module() != module_name { bail!("unknown import module {}", i.module()); } - if let Some(export) = wasi_instance.find_export_by_name(i.name()) { + if let Some(export) = wasi_instance.get_export(i.name()) { imports.push(export.clone()); } else { bail!("unknown import {}:{}", i.module(), i.name()) diff --git a/crates/runtime/src/signalhandlers.rs b/crates/runtime/src/signalhandlers.rs index 5beb737769..97f27b9b42 100644 --- a/crates/runtime/src/signalhandlers.rs +++ b/crates/runtime/src/signalhandlers.rs @@ -61,6 +61,17 @@ pub extern "C" fn wasmtime_init_eager() { state.tried = true; assert!(!state.success); + // This is a really weird and unfortunate function call. For all the gory + // details see #829, but the tl;dr; is that in a trap handler we have 2 + // pages of stack space on Linux, and calling into libunwind which triggers + // the dynamic loader blows the stack. + // + // This is a dumb hack to work around this system-specific issue by + // capturing a backtrace once in the lifetime of a process to ensure that + // when we capture a backtrace in the trap handler all caches are primed, + // aka the dynamic loader has resolved all the relevant symbols. + drop(backtrace::Backtrace::new_unresolved()); + if unsafe { EnsureEagerSignalHandlers() == 0 } { return; } diff --git a/crates/test-programs/tests/wasm_tests/runtime.rs b/crates/test-programs/tests/wasm_tests/runtime.rs index 28eef2878d..7836b1ed22 100644 --- a/crates/test-programs/tests/wasm_tests/runtime.rs +++ b/crates/test-programs/tests/wasm_tests/runtime.rs @@ -49,7 +49,7 @@ pub fn instantiate(data: &[u8], bin_name: &str, workspace: Option<&Path>) -> any .iter() .map(|i| { let field_name = i.name(); - if let Some(export) = snapshot1.find_export_by_name(field_name) { + if let Some(export) = snapshot1.get_export(field_name) { Ok(export.clone()) } else { bail!( @@ -67,17 +67,14 @@ pub fn instantiate(data: &[u8], bin_name: &str, workspace: Option<&Path>) -> any ))?; let export = instance - .find_export_by_name("_start") + .get_export("_start") .context("expected a _start export")? .clone(); - if let Err(trap) = export + export .func() .context("expected export to be a func")? - .call(&[]) - { - bail!("trapped: {:?}", trap); - } + .call(&[])?; Ok(()) } diff --git a/crates/wasi-common/src/error.rs b/crates/wasi-common/src/error.rs index 821ba0a476..c18856d4bc 100644 --- a/crates/wasi-common/src/error.rs +++ b/crates/wasi-common/src/error.rs @@ -8,7 +8,7 @@ use thiserror::Error; #[derive(Clone, Copy, Debug, Error, Eq, PartialEq)] #[repr(u16)] -#[error("{:?}", self)] +#[error("{:?} ({})", self, wasi::strerror(*self as wasi::__wasi_errno_t))] pub enum WasiError { ESUCCESS = wasi::__WASI_ERRNO_SUCCESS, E2BIG = wasi::__WASI_ERRNO_2BIG, @@ -104,14 +104,11 @@ pub enum Error { #[cfg(unix)] #[error("Yanix error: {0}")] Yanix(#[from] yanix::YanixError), - #[cfg(windows)] - #[error("Winx error: {0}")] - Winx(#[from] winx::winerror::WinError), } impl From for Error { fn from(_: TryFromIntError) -> Self { - Self::Wasi(WasiError::EOVERFLOW) + Self::EOVERFLOW } } @@ -123,39 +120,46 @@ impl From for Error { impl From for Error { fn from(_: str::Utf8Error) -> Self { - Self::Wasi(WasiError::EILSEQ) + Self::EILSEQ } } impl From for Error { fn from(_: ffi::NulError) -> Self { - Self::Wasi(WasiError::EILSEQ) + Self::EILSEQ } } impl From<&ffi::NulError> for Error { fn from(_: &ffi::NulError) -> Self { - Self::Wasi(WasiError::EILSEQ) + Self::EILSEQ } } impl Error { - pub(crate) fn as_wasi_errno(&self) -> wasi::__wasi_errno_t { + pub(crate) fn as_wasi_error(&self) -> WasiError { match self { - Self::Wasi(no) => no.as_raw_errno(), - Self::Io(e) => errno_from_ioerror(e.to_owned()), + Self::Wasi(err) => *err, + Self::Io(err) => { + let err = match err.raw_os_error() { + Some(code) => Self::from_raw_os_error(code), + None => { + log::debug!("Inconvertible OS error: {}", err); + Self::EIO + } + }; + err.as_wasi_error() + } #[cfg(unix)] Self::Yanix(err) => { use yanix::YanixError::*; - let err = match err { - Errno(errno) => crate::sys::host_impl::errno_from_nix(*errno), + let err: Self = match err { + Errno(errno) => (*errno).into(), NulError(err) => err.into(), TryFromIntError(err) => (*err).into(), }; - err.as_wasi_errno() + err.as_wasi_error() } - #[cfg(windows)] - Self::Winx(err) => crate::sys::host_impl::errno_from_win(*err), } } @@ -238,14 +242,8 @@ impl Error { pub const ENOTCAPABLE: Self = Error::Wasi(WasiError::ENOTCAPABLE); } -fn errno_from_ioerror(e: &std::io::Error) -> wasi::__wasi_errno_t { - match e.raw_os_error() { - Some(code) => crate::sys::errno_from_host(code), - None => { - log::debug!("Inconvertible OS error: {}", e); - wasi::__WASI_ERRNO_IO - } - } +pub(crate) trait FromRawOsError { + fn from_raw_os_error(code: i32) -> Self; } pub(crate) type Result = std::result::Result; diff --git a/crates/wasi-common/src/hostcalls/fs.rs b/crates/wasi-common/src/hostcalls/fs.rs index 6746ba79d4..acbd85f006 100644 --- a/crates/wasi-common/src/hostcalls/fs.rs +++ b/crates/wasi-common/src/hostcalls/fs.rs @@ -1,6 +1,6 @@ #![allow(non_camel_case_types)] use crate::ctx::WasiCtx; -use crate::{hostcalls_impl, wasi, wasi32}; +use crate::{wasi, wasi32}; hostcalls! { pub unsafe fn fd_close(wasi_ctx: &mut WasiCtx, memory: &mut [u8], fd: wasi::__wasi_fd_t,) -> wasi::__wasi_errno_t; diff --git a/crates/wasi-common/src/hostcalls/misc.rs b/crates/wasi-common/src/hostcalls/misc.rs index 883b5a54ac..7546987570 100644 --- a/crates/wasi-common/src/hostcalls/misc.rs +++ b/crates/wasi-common/src/hostcalls/misc.rs @@ -1,6 +1,6 @@ #![allow(non_camel_case_types)] use crate::ctx::WasiCtx; -use crate::{hostcalls_impl, wasi, wasi32}; +use crate::{wasi, wasi32}; use log::trace; use wasi_common_cbindgen::wasi_common_cbindgen; diff --git a/crates/wasi-common/src/hostcalls_impl/fs_helpers.rs b/crates/wasi-common/src/hostcalls_impl/fs_helpers.rs index 5fd939b167..23a06c23fc 100644 --- a/crates/wasi-common/src/hostcalls_impl/fs_helpers.rs +++ b/crates/wasi-common/src/hostcalls_impl/fs_helpers.rs @@ -1,7 +1,7 @@ #![allow(non_camel_case_types)] use crate::sys::host_impl; use crate::sys::hostcalls_impl::fs_helpers::*; -use crate::{fdentry::FdEntry, wasi, Error, Result}; +use crate::{error::WasiError, fdentry::FdEntry, wasi, Error, Result}; use std::fs::File; use std::path::{Component, Path}; @@ -117,10 +117,10 @@ pub(crate) fn path_get( dir_stack.push(new_dir); } Err(e) => { - match e.as_wasi_errno() { - wasi::__WASI_ERRNO_LOOP - | wasi::__WASI_ERRNO_MLINK - | wasi::__WASI_ERRNO_NOTDIR => + match e.as_wasi_error() { + WasiError::ELOOP + | WasiError::EMLINK + | WasiError::ENOTDIR => // Check to see if it was a symlink. Linux indicates // this with ENOTDIR because of the O_DIRECTORY flag. { @@ -179,12 +179,12 @@ pub(crate) fn path_get( continue; } Err(e) => { - if e.as_wasi_errno() != wasi::__WASI_ERRNO_INVAL - && e.as_wasi_errno() != wasi::__WASI_ERRNO_NOENT + if e.as_wasi_error() != WasiError::EINVAL + && e.as_wasi_error() != WasiError::ENOENT // this handles the cases when trying to link to // a destination that already exists, and the target // path contains a slash - && e.as_wasi_errno() != wasi::__WASI_ERRNO_NOTDIR + && e.as_wasi_error() != WasiError::ENOTDIR { return Err(e); } diff --git a/crates/wasi-common/src/hostcalls_impl/misc.rs b/crates/wasi-common/src/hostcalls_impl/misc.rs index 7e266033f8..893d3dae0b 100644 --- a/crates/wasi-common/src/hostcalls_impl/misc.rs +++ b/crates/wasi-common/src/hostcalls_impl/misc.rs @@ -261,7 +261,7 @@ pub(crate) fn poll_oneoff( let event = wasi::__wasi_event_t { userdata: subscription.userdata, r#type, - error: err.as_wasi_errno(), + error: err.as_wasi_error().as_raw_errno(), u: wasi::__wasi_event_u_t { fd_readwrite: wasi::__wasi_event_fd_readwrite_t { nbytes: 0, diff --git a/crates/wasi-common/src/macros.rs b/crates/wasi-common/src/macros.rs index b3da97c215..14cbaba8a6 100644 --- a/crates/wasi-common/src/macros.rs +++ b/crates/wasi-common/src/macros.rs @@ -2,12 +2,12 @@ macro_rules! hostcalls { ($(pub unsafe fn $name:ident($($arg:ident: $ty:ty,)*) -> $ret:ty;)*) => ($( #[wasi_common_cbindgen::wasi_common_cbindgen] pub unsafe fn $name($($arg: $ty,)*) -> $ret { - let ret = match hostcalls_impl::$name($($arg,)*) { - Ok(()) => wasi::__WASI_ERRNO_SUCCESS, - Err(e) => e.as_wasi_errno(), - }; - - ret + let ret = crate::hostcalls_impl::$name($($arg,)*) + .err() + .unwrap_or(crate::Error::ESUCCESS) + .as_wasi_error(); + log::trace!(" | errno={}", ret); + ret.as_raw_errno() } )*) } @@ -18,12 +18,12 @@ macro_rules! hostcalls_old { ($(pub unsafe fn $name:ident($($arg:ident: $ty:ty,)*) -> $ret:ty;)*) => ($( #[wasi_common_cbindgen::wasi_common_cbindgen_old] pub unsafe fn $name($($arg: $ty,)*) -> $ret { - let ret = match hostcalls_impl::$name($($arg,)*) { - Ok(()) => wasi::__WASI_ERRNO_SUCCESS, - Err(e) => e.as_wasi_errno(), - }; - - ret + let ret = crate::old::snapshot_0::hostcalls_impl::$name($($arg,)*) + .err() + .unwrap_or(crate::old::snapshot_0::Error::ESUCCESS) + .as_wasi_error(); + log::trace!(" | errno={}", ret); + ret.as_raw_errno() } )*) } diff --git a/crates/wasi-common/src/old/snapshot_0/error.rs b/crates/wasi-common/src/old/snapshot_0/error.rs index 534e8a3697..ae634520d3 100644 --- a/crates/wasi-common/src/old/snapshot_0/error.rs +++ b/crates/wasi-common/src/old/snapshot_0/error.rs @@ -8,7 +8,7 @@ use thiserror::Error; #[derive(Clone, Copy, Debug, Error, Eq, PartialEq)] #[repr(u16)] -#[error("{:?}", self)] +#[error("{:?} ({})", self, wasi::strerror(*self as wasi::__wasi_errno_t))] pub enum WasiError { ESUCCESS = wasi::__WASI_ERRNO_SUCCESS, E2BIG = wasi::__WASI_ERRNO_2BIG, @@ -104,14 +104,11 @@ pub enum Error { #[cfg(unix)] #[error("Yanix error: {0}")] Yanix(#[from] yanix::YanixError), - #[cfg(windows)] - #[error("Winx error: {0}")] - Winx(#[from] winx::winerror::WinError), } impl From for Error { fn from(_: TryFromIntError) -> Self { - Self::Wasi(WasiError::EOVERFLOW) + Self::EOVERFLOW } } @@ -123,41 +120,49 @@ impl From for Error { impl From for Error { fn from(_: str::Utf8Error) -> Self { - Self::Wasi(WasiError::EILSEQ) + Self::EILSEQ } } impl From for Error { fn from(_: ffi::NulError) -> Self { - Self::Wasi(WasiError::EILSEQ) + Self::EILSEQ } } impl From<&ffi::NulError> for Error { fn from(_: &ffi::NulError) -> Self { - Self::Wasi(WasiError::EILSEQ) + Self::EILSEQ } } impl Error { - pub(crate) fn as_wasi_errno(&self) -> wasi::__wasi_errno_t { + pub(crate) fn as_wasi_error(&self) -> WasiError { match self { - Self::Wasi(no) => no.as_raw_errno(), - Self::Io(e) => errno_from_ioerror(e.to_owned()), + Self::Wasi(err) => *err, + Self::Io(err) => { + let err = match err.raw_os_error() { + Some(code) => Self::from_raw_os_error(code), + None => { + log::debug!("Inconvertible OS error: {}", err); + Self::EIO + } + }; + err.as_wasi_error() + } #[cfg(unix)] Self::Yanix(err) => { use yanix::YanixError::*; - let err = match err { - Errno(errno) => crate::old::snapshot_0::sys::host_impl::errno_from_nix(*errno), + let err: Self = match err { + Errno(errno) => (*errno).into(), NulError(err) => err.into(), TryFromIntError(err) => (*err).into(), }; - err.as_wasi_errno() + err.as_wasi_error() } - #[cfg(windows)] - Self::Winx(err) => crate::old::snapshot_0::sys::host_impl::errno_from_win(*err), } } + pub const ESUCCESS: Self = Error::Wasi(WasiError::ESUCCESS); pub const E2BIG: Self = Error::Wasi(WasiError::E2BIG); pub const EACCES: Self = Error::Wasi(WasiError::EACCES); @@ -237,12 +242,6 @@ impl Error { pub const ENOTCAPABLE: Self = Error::Wasi(WasiError::ENOTCAPABLE); } -fn errno_from_ioerror(e: &std::io::Error) -> wasi::__wasi_errno_t { - match e.raw_os_error() { - Some(code) => crate::old::snapshot_0::sys::errno_from_host(code), - None => { - log::debug!("Inconvertible OS error: {}", e); - wasi::__WASI_ERRNO_IO - } - } +pub(crate) trait FromRawOsError { + fn from_raw_os_error(code: i32) -> Self; } diff --git a/crates/wasi-common/src/old/snapshot_0/hostcalls/fs.rs b/crates/wasi-common/src/old/snapshot_0/hostcalls/fs.rs index 4836da4e76..7c7158b3a0 100644 --- a/crates/wasi-common/src/old/snapshot_0/hostcalls/fs.rs +++ b/crates/wasi-common/src/old/snapshot_0/hostcalls/fs.rs @@ -1,6 +1,6 @@ #![allow(non_camel_case_types)] use crate::old::snapshot_0::ctx::WasiCtx; -use crate::old::snapshot_0::{hostcalls_impl, wasi, wasi32}; +use crate::old::snapshot_0::{wasi, wasi32}; hostcalls_old! { pub unsafe fn fd_close(wasi_ctx: &mut WasiCtx, fd: wasi::__wasi_fd_t,) -> wasi::__wasi_errno_t; diff --git a/crates/wasi-common/src/old/snapshot_0/hostcalls/misc.rs b/crates/wasi-common/src/old/snapshot_0/hostcalls/misc.rs index 42712cb4d2..98812e2e04 100644 --- a/crates/wasi-common/src/old/snapshot_0/hostcalls/misc.rs +++ b/crates/wasi-common/src/old/snapshot_0/hostcalls/misc.rs @@ -1,6 +1,6 @@ #![allow(non_camel_case_types)] use crate::old::snapshot_0::ctx::WasiCtx; -use crate::old::snapshot_0::{hostcalls_impl, wasi, wasi32}; +use crate::old::snapshot_0::{wasi, wasi32}; use log::trace; use wasi_common_cbindgen::wasi_common_cbindgen_old; diff --git a/crates/wasi-common/src/old/snapshot_0/hostcalls_impl/fs_helpers.rs b/crates/wasi-common/src/old/snapshot_0/hostcalls_impl/fs_helpers.rs index 3a26bfc54b..ff41bbc3a9 100644 --- a/crates/wasi-common/src/old/snapshot_0/hostcalls_impl/fs_helpers.rs +++ b/crates/wasi-common/src/old/snapshot_0/hostcalls_impl/fs_helpers.rs @@ -1,7 +1,7 @@ #![allow(non_camel_case_types)] use crate::old::snapshot_0::sys::host_impl; use crate::old::snapshot_0::sys::hostcalls_impl::fs_helpers::*; -use crate::old::snapshot_0::{fdentry::FdEntry, wasi, Error, Result}; +use crate::old::snapshot_0::{error::WasiError, fdentry::FdEntry, wasi, Error, Result}; use std::fs::File; use std::path::{Component, Path}; @@ -117,10 +117,10 @@ pub(crate) fn path_get( dir_stack.push(new_dir); } Err(e) => { - match e.as_wasi_errno() { - wasi::__WASI_ERRNO_LOOP - | wasi::__WASI_ERRNO_MLINK - | wasi::__WASI_ERRNO_NOTDIR => + match e.as_wasi_error() { + WasiError::ELOOP + | WasiError::EMLINK + | WasiError::ENOTDIR => // Check to see if it was a symlink. Linux indicates // this with ENOTDIR because of the O_DIRECTORY flag. { @@ -179,12 +179,12 @@ pub(crate) fn path_get( continue; } Err(e) => { - if e.as_wasi_errno() != wasi::__WASI_ERRNO_INVAL - && e.as_wasi_errno() != wasi::__WASI_ERRNO_NOENT + if e.as_wasi_error() != WasiError::EINVAL + && e.as_wasi_error() != WasiError::ENOENT // this handles the cases when trying to link to // a destination that already exists, and the target // path contains a slash - && e.as_wasi_errno() != wasi::__WASI_ERRNO_NOTDIR + && e.as_wasi_error() != WasiError::ENOTDIR { return Err(e); } diff --git a/crates/wasi-common/src/old/snapshot_0/hostcalls_impl/misc.rs b/crates/wasi-common/src/old/snapshot_0/hostcalls_impl/misc.rs index f06e052348..32cd6468e9 100644 --- a/crates/wasi-common/src/old/snapshot_0/hostcalls_impl/misc.rs +++ b/crates/wasi-common/src/old/snapshot_0/hostcalls_impl/misc.rs @@ -258,7 +258,7 @@ pub(crate) fn poll_oneoff( let event = wasi::__wasi_event_t { userdata: subscription.userdata, r#type, - error: err.as_wasi_errno(), + error: err.as_wasi_error().as_raw_errno(), u: wasi::__wasi_event_u_t { fd_readwrite: wasi::__wasi_event_fd_readwrite_t { nbytes: 0, diff --git a/crates/wasi-common/src/old/snapshot_0/sys/mod.rs b/crates/wasi-common/src/old/snapshot_0/sys/mod.rs index 2e62d6f1c4..6d8b3fd82f 100644 --- a/crates/wasi-common/src/old/snapshot_0/sys/mod.rs +++ b/crates/wasi-common/src/old/snapshot_0/sys/mod.rs @@ -1,21 +1,12 @@ -use crate::old::snapshot_0::wasi; use cfg_if::cfg_if; cfg_if! { if #[cfg(unix)] { mod unix; pub(crate) use self::unix::*; - - pub(crate) fn errno_from_host(err: i32) -> wasi::__wasi_errno_t { - host_impl::errno_from_nix(yanix::Errno::from_i32(err)).as_wasi_errno() - } } else if #[cfg(windows)] { mod windows; pub(crate) use self::windows::*; - - pub(crate) fn errno_from_host(err: i32) -> wasi::__wasi_errno_t { - host_impl::errno_from_win(winx::winerror::WinError::from_u32(err as u32)) - } } else { compile_error!("wasi-common doesn't compile for this platform yet"); } diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/bsd/hostcalls_impl.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/bsd/hostcalls_impl.rs index 7db8808ea7..5afab3305b 100644 --- a/crates/wasi-common/src/old/snapshot_0/sys/unix/bsd/hostcalls_impl.rs +++ b/crates/wasi-common/src/old/snapshot_0/sys/unix/bsd/hostcalls_impl.rs @@ -1,5 +1,4 @@ use crate::old::snapshot_0::hostcalls_impl::PathGet; -use crate::old::snapshot_0::sys::host_impl; use crate::old::snapshot_0::{Error, Result}; use std::os::unix::prelude::AsRawFd; @@ -80,7 +79,7 @@ pub(crate) fn path_symlink(old_path: &str, resolved: PathGet) -> Result<()> { Err(Error::ENOTDIR) } } - x => Err(host_impl::errno_from_nix(x)), + x => Err(x.into()), } } else { Err(err.into()) @@ -132,7 +131,7 @@ pub(crate) fn path_rename(resolved_old: PathGet, resolved_new: PathGet) -> Resul Err(Error::ENOENT) } } - x => Err(host_impl::errno_from_nix(x)), + x => Err(x.into()), } } else { Err(err.into()) diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/host_impl.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/host_impl.rs index ac74fd3e83..1233478ab5 100644 --- a/crates/wasi-common/src/old/snapshot_0/sys/unix/host_impl.rs +++ b/crates/wasi-common/src/old/snapshot_0/sys/unix/host_impl.rs @@ -3,7 +3,9 @@ #![allow(non_snake_case)] #![allow(dead_code)] use crate::old::snapshot_0::host::FileType; -use crate::old::snapshot_0::{helpers, sys::unix::sys_impl, wasi, Error, Result}; +use crate::old::snapshot_0::{ + error::FromRawOsError, helpers, sys::unix::sys_impl, wasi, Error, Result, +}; use std::ffi::OsStr; use std::os::unix::prelude::OsStrExt; use yanix::{ @@ -13,82 +15,90 @@ use yanix::{ pub(crate) use sys_impl::host_impl::*; -pub(crate) fn errno_from_nix(errno: Errno) -> Error { - match errno { - Errno::EPERM => Error::EPERM, - Errno::ENOENT => Error::ENOENT, - Errno::ESRCH => Error::ESRCH, - Errno::EINTR => Error::EINTR, - Errno::EIO => Error::EIO, - Errno::ENXIO => Error::ENXIO, - Errno::E2BIG => Error::E2BIG, - Errno::ENOEXEC => Error::ENOEXEC, - Errno::EBADF => Error::EBADF, - Errno::ECHILD => Error::ECHILD, - Errno::EAGAIN => Error::EAGAIN, - Errno::ENOMEM => Error::ENOMEM, - Errno::EACCES => Error::EACCES, - Errno::EFAULT => Error::EFAULT, - Errno::EBUSY => Error::EBUSY, - Errno::EEXIST => Error::EEXIST, - Errno::EXDEV => Error::EXDEV, - Errno::ENODEV => Error::ENODEV, - Errno::ENOTDIR => Error::ENOTDIR, - Errno::EISDIR => Error::EISDIR, - Errno::EINVAL => Error::EINVAL, - Errno::ENFILE => Error::ENFILE, - Errno::EMFILE => Error::EMFILE, - Errno::ENOTTY => Error::ENOTTY, - Errno::ETXTBSY => Error::ETXTBSY, - Errno::EFBIG => Error::EFBIG, - Errno::ENOSPC => Error::ENOSPC, - Errno::ESPIPE => Error::ESPIPE, - Errno::EROFS => Error::EROFS, - Errno::EMLINK => Error::EMLINK, - Errno::EPIPE => Error::EPIPE, - Errno::EDOM => Error::EDOM, - Errno::ERANGE => Error::ERANGE, - Errno::EDEADLK => Error::EDEADLK, - Errno::ENAMETOOLONG => Error::ENAMETOOLONG, - Errno::ENOLCK => Error::ENOLCK, - Errno::ENOSYS => Error::ENOSYS, - Errno::ENOTEMPTY => Error::ENOTEMPTY, - Errno::ELOOP => Error::ELOOP, - Errno::ENOMSG => Error::ENOMSG, - Errno::EIDRM => Error::EIDRM, - Errno::ENOLINK => Error::ENOLINK, - Errno::EPROTO => Error::EPROTO, - Errno::EMULTIHOP => Error::EMULTIHOP, - Errno::EBADMSG => Error::EBADMSG, - Errno::EOVERFLOW => Error::EOVERFLOW, - Errno::EILSEQ => Error::EILSEQ, - Errno::ENOTSOCK => Error::ENOTSOCK, - Errno::EDESTADDRREQ => Error::EDESTADDRREQ, - Errno::EMSGSIZE => Error::EMSGSIZE, - Errno::EPROTOTYPE => Error::EPROTOTYPE, - Errno::ENOPROTOOPT => Error::ENOPROTOOPT, - Errno::EPROTONOSUPPORT => Error::EPROTONOSUPPORT, - Errno::EAFNOSUPPORT => Error::EAFNOSUPPORT, - Errno::EADDRINUSE => Error::EADDRINUSE, - Errno::EADDRNOTAVAIL => Error::EADDRNOTAVAIL, - Errno::ENETDOWN => Error::ENETDOWN, - Errno::ENETUNREACH => Error::ENETUNREACH, - Errno::ENETRESET => Error::ENETRESET, - Errno::ECONNABORTED => Error::ECONNABORTED, - Errno::ECONNRESET => Error::ECONNRESET, - Errno::ENOBUFS => Error::ENOBUFS, - Errno::EISCONN => Error::EISCONN, - Errno::ENOTCONN => Error::ENOTCONN, - Errno::ETIMEDOUT => Error::ETIMEDOUT, - Errno::ECONNREFUSED => Error::ECONNREFUSED, - Errno::EHOSTUNREACH => Error::EHOSTUNREACH, - Errno::EALREADY => Error::EALREADY, - Errno::EINPROGRESS => Error::EINPROGRESS, - Errno::ESTALE => Error::ESTALE, - Errno::EDQUOT => Error::EDQUOT, - Errno::ECANCELED => Error::ECANCELED, - Errno::EOWNERDEAD => Error::EOWNERDEAD, - Errno::ENOTRECOVERABLE => Error::ENOTRECOVERABLE, +impl FromRawOsError for Error { + fn from_raw_os_error(code: i32) -> Self { + Self::from(Errno::from_i32(code)) + } +} + +impl From for Error { + fn from(errno: Errno) -> Self { + match errno { + Errno::EPERM => Self::EPERM, + Errno::ENOENT => Self::ENOENT, + Errno::ESRCH => Self::ESRCH, + Errno::EINTR => Self::EINTR, + Errno::EIO => Self::EIO, + Errno::ENXIO => Self::ENXIO, + Errno::E2BIG => Self::E2BIG, + Errno::ENOEXEC => Self::ENOEXEC, + Errno::EBADF => Self::EBADF, + Errno::ECHILD => Self::ECHILD, + Errno::EAGAIN => Self::EAGAIN, + Errno::ENOMEM => Self::ENOMEM, + Errno::EACCES => Self::EACCES, + Errno::EFAULT => Self::EFAULT, + Errno::EBUSY => Self::EBUSY, + Errno::EEXIST => Self::EEXIST, + Errno::EXDEV => Self::EXDEV, + Errno::ENODEV => Self::ENODEV, + Errno::ENOTDIR => Self::ENOTDIR, + Errno::EISDIR => Self::EISDIR, + Errno::EINVAL => Self::EINVAL, + Errno::ENFILE => Self::ENFILE, + Errno::EMFILE => Self::EMFILE, + Errno::ENOTTY => Self::ENOTTY, + Errno::ETXTBSY => Self::ETXTBSY, + Errno::EFBIG => Self::EFBIG, + Errno::ENOSPC => Self::ENOSPC, + Errno::ESPIPE => Self::ESPIPE, + Errno::EROFS => Self::EROFS, + Errno::EMLINK => Self::EMLINK, + Errno::EPIPE => Self::EPIPE, + Errno::EDOM => Self::EDOM, + Errno::ERANGE => Self::ERANGE, + Errno::EDEADLK => Self::EDEADLK, + Errno::ENAMETOOLONG => Self::ENAMETOOLONG, + Errno::ENOLCK => Self::ENOLCK, + Errno::ENOSYS => Self::ENOSYS, + Errno::ENOTEMPTY => Self::ENOTEMPTY, + Errno::ELOOP => Self::ELOOP, + Errno::ENOMSG => Self::ENOMSG, + Errno::EIDRM => Self::EIDRM, + Errno::ENOLINK => Self::ENOLINK, + Errno::EPROTO => Self::EPROTO, + Errno::EMULTIHOP => Self::EMULTIHOP, + Errno::EBADMSG => Self::EBADMSG, + Errno::EOVERFLOW => Self::EOVERFLOW, + Errno::EILSEQ => Self::EILSEQ, + Errno::ENOTSOCK => Self::ENOTSOCK, + Errno::EDESTADDRREQ => Self::EDESTADDRREQ, + Errno::EMSGSIZE => Self::EMSGSIZE, + Errno::EPROTOTYPE => Self::EPROTOTYPE, + Errno::ENOPROTOOPT => Self::ENOPROTOOPT, + Errno::EPROTONOSUPPORT => Self::EPROTONOSUPPORT, + Errno::EAFNOSUPPORT => Self::EAFNOSUPPORT, + Errno::EADDRINUSE => Self::EADDRINUSE, + Errno::EADDRNOTAVAIL => Self::EADDRNOTAVAIL, + Errno::ENETDOWN => Self::ENETDOWN, + Errno::ENETUNREACH => Self::ENETUNREACH, + Errno::ENETRESET => Self::ENETRESET, + Errno::ECONNABORTED => Self::ECONNABORTED, + Errno::ECONNRESET => Self::ECONNRESET, + Errno::ENOBUFS => Self::ENOBUFS, + Errno::EISCONN => Self::EISCONN, + Errno::ENOTCONN => Self::ENOTCONN, + Errno::ETIMEDOUT => Self::ETIMEDOUT, + Errno::ECONNREFUSED => Self::ECONNREFUSED, + Errno::EHOSTUNREACH => Self::EHOSTUNREACH, + Errno::EALREADY => Self::EALREADY, + Errno::EINPROGRESS => Self::EINPROGRESS, + Errno::ESTALE => Self::ESTALE, + Errno::EDQUOT => Self::EDQUOT, + Errno::ECANCELED => Self::ECANCELED, + Errno::EOWNERDEAD => Self::EOWNERDEAD, + Errno::ENOTRECOVERABLE => Self::ENOTRECOVERABLE, + } } } diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/hostcalls_impl/fs.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/hostcalls_impl/fs.rs index a26f24e505..f67320d88c 100644 --- a/crates/wasi-common/src/old/snapshot_0/sys/unix/hostcalls_impl/fs.rs +++ b/crates/wasi-common/src/old/snapshot_0/sys/unix/hostcalls_impl/fs.rs @@ -170,7 +170,7 @@ pub(crate) fn path_open( Errno::EMLINK if !(nix_all_oflags & OFlag::NOFOLLOW).is_empty() => { return Err(Error::ELOOP); } - errno => return Err(host_impl::errno_from_nix(errno)), + errno => return Err(errno.into()), } } else { return Err(e.into()); diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/hostcalls_impl/misc.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/hostcalls_impl/misc.rs index 4be75c61d4..42f67c1178 100644 --- a/crates/wasi-common/src/old/snapshot_0/sys/unix/hostcalls_impl/misc.rs +++ b/crates/wasi-common/src/old/snapshot_0/sys/unix/hostcalls_impl/misc.rs @@ -1,7 +1,6 @@ #![allow(non_camel_case_types)] #![allow(unused_unsafe)] use crate::old::snapshot_0::hostcalls_impl::{ClockEventData, FdEventData}; -use crate::old::snapshot_0::sys::host_impl; use crate::old::snapshot_0::{wasi, Error, Result}; use yanix::clock::{clock_getres, clock_gettime, ClockId}; @@ -92,7 +91,7 @@ pub(crate) fn poll_oneoff( if Errno::last() == Errno::EINTR { continue; } - return Err(host_impl::errno_from_nix(Errno::last())); + return Err(Errno::last().into()); } Ok(ready) => break ready, } diff --git a/crates/wasi-common/src/old/snapshot_0/sys/windows/host_impl.rs b/crates/wasi-common/src/old/snapshot_0/sys/windows/host_impl.rs index e5b80e2193..5fcebb052e 100644 --- a/crates/wasi-common/src/old/snapshot_0/sys/windows/host_impl.rs +++ b/crates/wasi-common/src/old/snapshot_0/sys/windows/host_impl.rs @@ -3,7 +3,7 @@ #![allow(non_snake_case)] #![allow(unused)] use crate::old::snapshot_0::host::FileType; -use crate::old::snapshot_0::{wasi, Error, Result}; +use crate::old::snapshot_0::{error::FromRawOsError, wasi, Error, Result}; use std::convert::TryInto; use std::ffi::OsStr; use std::fs::OpenOptions; @@ -13,35 +13,44 @@ use std::os::windows::ffi::OsStrExt; use std::os::windows::fs::OpenOptionsExt; use std::time::{SystemTime, UNIX_EPOCH}; use winx::file::{AccessMode, Attributes, CreationDisposition, Flags}; +use winx::winerror::WinError; -pub(crate) fn errno_from_win(error: winx::winerror::WinError) -> wasi::__wasi_errno_t { - // TODO: implement error mapping between Windows and WASI - use winx::winerror::WinError::*; - match error { - ERROR_SUCCESS => wasi::__WASI_ERRNO_SUCCESS, - ERROR_BAD_ENVIRONMENT => wasi::__WASI_ERRNO_2BIG, - ERROR_FILE_NOT_FOUND => wasi::__WASI_ERRNO_NOENT, - ERROR_PATH_NOT_FOUND => wasi::__WASI_ERRNO_NOENT, - ERROR_TOO_MANY_OPEN_FILES => wasi::__WASI_ERRNO_NFILE, - ERROR_ACCESS_DENIED => wasi::__WASI_ERRNO_ACCES, - ERROR_SHARING_VIOLATION => wasi::__WASI_ERRNO_ACCES, - ERROR_PRIVILEGE_NOT_HELD => wasi::__WASI_ERRNO_NOTCAPABLE, // TODO is this the correct mapping? - ERROR_INVALID_HANDLE => wasi::__WASI_ERRNO_BADF, - ERROR_INVALID_NAME => wasi::__WASI_ERRNO_NOENT, - ERROR_NOT_ENOUGH_MEMORY => wasi::__WASI_ERRNO_NOMEM, - ERROR_OUTOFMEMORY => wasi::__WASI_ERRNO_NOMEM, - ERROR_DIR_NOT_EMPTY => wasi::__WASI_ERRNO_NOTEMPTY, - ERROR_NOT_READY => wasi::__WASI_ERRNO_BUSY, - ERROR_BUSY => wasi::__WASI_ERRNO_BUSY, - ERROR_NOT_SUPPORTED => wasi::__WASI_ERRNO_NOTSUP, - ERROR_FILE_EXISTS => wasi::__WASI_ERRNO_EXIST, - ERROR_BROKEN_PIPE => wasi::__WASI_ERRNO_PIPE, - ERROR_BUFFER_OVERFLOW => wasi::__WASI_ERRNO_NAMETOOLONG, - ERROR_NOT_A_REPARSE_POINT => wasi::__WASI_ERRNO_INVAL, - ERROR_NEGATIVE_SEEK => wasi::__WASI_ERRNO_INVAL, - ERROR_DIRECTORY => wasi::__WASI_ERRNO_NOTDIR, - ERROR_ALREADY_EXISTS => wasi::__WASI_ERRNO_EXIST, - _ => wasi::__WASI_ERRNO_NOTSUP, +impl FromRawOsError for Error { + fn from_raw_os_error(code: i32) -> Self { + Self::from(WinError::from_u32(code as u32)) + } +} + +impl From for Error { + fn from(err: WinError) -> Self { + // TODO: implement error mapping between Windows and WASI + use winx::winerror::WinError::*; + match err { + ERROR_SUCCESS => Self::ESUCCESS, + ERROR_BAD_ENVIRONMENT => Self::E2BIG, + ERROR_FILE_NOT_FOUND => Self::ENOENT, + ERROR_PATH_NOT_FOUND => Self::ENOENT, + ERROR_TOO_MANY_OPEN_FILES => Self::ENFILE, + ERROR_ACCESS_DENIED => Self::EACCES, + ERROR_SHARING_VIOLATION => Self::EACCES, + ERROR_PRIVILEGE_NOT_HELD => Self::ENOTCAPABLE, // TODO is this the correct mapping? + ERROR_INVALID_HANDLE => Self::EBADF, + ERROR_INVALID_NAME => Self::ENOENT, + ERROR_NOT_ENOUGH_MEMORY => Self::ENOMEM, + ERROR_OUTOFMEMORY => Self::ENOMEM, + ERROR_DIR_NOT_EMPTY => Self::ENOTEMPTY, + ERROR_NOT_READY => Self::EBUSY, + ERROR_BUSY => Self::EBUSY, + ERROR_NOT_SUPPORTED => Self::ENOTSUP, + ERROR_FILE_EXISTS => Self::EEXIST, + ERROR_BROKEN_PIPE => Self::EPIPE, + ERROR_BUFFER_OVERFLOW => Self::ENAMETOOLONG, + ERROR_NOT_A_REPARSE_POINT => Self::EINVAL, + ERROR_NEGATIVE_SEEK => Self::EINVAL, + ERROR_DIRECTORY => Self::ENOTDIR, + ERROR_ALREADY_EXISTS => Self::EEXIST, + _ => Self::ENOTSUP, + } } } diff --git a/crates/wasi-common/src/old/snapshot_0/wasi.rs b/crates/wasi-common/src/old/snapshot_0/wasi.rs index 5f690fc03a..864585cbdf 100644 --- a/crates/wasi-common/src/old/snapshot_0/wasi.rs +++ b/crates/wasi-common/src/old/snapshot_0/wasi.rs @@ -107,89 +107,6 @@ pub(crate) const RIGHTS_TTY_BASE: __wasi_rights_t = __WASI_RIGHTS_FD_READ #[allow(unused)] pub(crate) const RIGHTS_TTY_INHERITING: __wasi_rights_t = 0; -pub fn strerror(errno: __wasi_errno_t) -> &'static str { - match errno { - __WASI_ERRNO_SUCCESS => "__WASI_ERRNO_SUCCESS", - __WASI_ERRNO_2BIG => "__WASI_ERRNO_2BIG", - __WASI_ERRNO_ACCES => "__WASI_ERRNO_ACCES", - __WASI_ERRNO_ADDRINUSE => "__WASI_ERRNO_ADDRINUSE", - __WASI_ERRNO_ADDRNOTAVAIL => "__WASI_ERRNO_ADDRNOTAVAIL", - __WASI_ERRNO_AFNOSUPPORT => "__WASI_ERRNO_AFNOSUPPORT", - __WASI_ERRNO_AGAIN => "__WASI_ERRNO_AGAIN", - __WASI_ERRNO_ALREADY => "__WASI_ERRNO_ALREADY", - __WASI_ERRNO_BADF => "__WASI_ERRNO_BADF", - __WASI_ERRNO_BADMSG => "__WASI_ERRNO_BADMSG", - __WASI_ERRNO_BUSY => "__WASI_ERRNO_BUSY", - __WASI_ERRNO_CANCELED => "__WASI_ERRNO_CANCELED", - __WASI_ERRNO_CHILD => "__WASI_ERRNO_CHILD", - __WASI_ERRNO_CONNABORTED => "__WASI_ERRNO_CONNABORTED", - __WASI_ERRNO_CONNREFUSED => "__WASI_ERRNO_CONNREFUSED", - __WASI_ERRNO_CONNRESET => "__WASI_ERRNO_CONNRESET", - __WASI_ERRNO_DEADLK => "__WASI_ERRNO_DEADLK", - __WASI_ERRNO_DESTADDRREQ => "__WASI_ERRNO_DESTADDRREQ", - __WASI_ERRNO_DOM => "__WASI_ERRNO_DOM", - __WASI_ERRNO_DQUOT => "__WASI_ERRNO_DQUOT", - __WASI_ERRNO_EXIST => "__WASI_ERRNO_EXIST", - __WASI_ERRNO_FAULT => "__WASI_ERRNO_FAULT", - __WASI_ERRNO_FBIG => "__WASI_ERRNO_FBIG", - __WASI_ERRNO_HOSTUNREACH => "__WASI_ERRNO_HOSTUNREACH", - __WASI_ERRNO_IDRM => "__WASI_ERRNO_IDRM", - __WASI_ERRNO_ILSEQ => "__WASI_ERRNO_ILSEQ", - __WASI_ERRNO_INPROGRESS => "__WASI_ERRNO_INPROGRESS", - __WASI_ERRNO_INTR => "__WASI_ERRNO_INTR", - __WASI_ERRNO_INVAL => "__WASI_ERRNO_INVAL", - __WASI_ERRNO_IO => "__WASI_ERRNO_IO", - __WASI_ERRNO_ISCONN => "__WASI_ERRNO_ISCONN", - __WASI_ERRNO_ISDIR => "__WASI_ERRNO_ISDIR", - __WASI_ERRNO_LOOP => "__WASI_ERRNO_LOOP", - __WASI_ERRNO_MFILE => "__WASI_ERRNO_MFILE", - __WASI_ERRNO_MLINK => "__WASI_ERRNO_MLINK", - __WASI_ERRNO_MSGSIZE => "__WASI_ERRNO_MSGSIZE", - __WASI_ERRNO_MULTIHOP => "__WASI_ERRNO_MULTIHOP", - __WASI_ERRNO_NAMETOOLONG => "__WASI_ERRNO_NAMETOOLONG", - __WASI_ERRNO_NETDOWN => "__WASI_ERRNO_NETDOWN", - __WASI_ERRNO_NETRESET => "__WASI_ERRNO_NETRESET", - __WASI_ERRNO_NETUNREACH => "__WASI_ERRNO_NETUNREACH", - __WASI_ERRNO_NFILE => "__WASI_ERRNO_NFILE", - __WASI_ERRNO_NOBUFS => "__WASI_ERRNO_NOBUFS", - __WASI_ERRNO_NODEV => "__WASI_ERRNO_NODEV", - __WASI_ERRNO_NOENT => "__WASI_ERRNO_NOENT", - __WASI_ERRNO_NOEXEC => "__WASI_ERRNO_NOEXEC", - __WASI_ERRNO_NOLCK => "__WASI_ERRNO_NOLCK", - __WASI_ERRNO_NOLINK => "__WASI_ERRNO_NOLINK", - __WASI_ERRNO_NOMEM => "__WASI_ERRNO_NOMEM", - __WASI_ERRNO_NOMSG => "__WASI_ERRNO_NOMSG", - __WASI_ERRNO_NOPROTOOPT => "__WASI_ERRNO_NOPROTOOPT", - __WASI_ERRNO_NOSPC => "__WASI_ERRNO_NOSPC", - __WASI_ERRNO_NOSYS => "__WASI_ERRNO_NOSYS", - __WASI_ERRNO_NOTCONN => "__WASI_ERRNO_NOTCONN", - __WASI_ERRNO_NOTDIR => "__WASI_ERRNO_NOTDIR", - __WASI_ERRNO_NOTEMPTY => "__WASI_ERRNO_NOTEMPTY", - __WASI_ERRNO_NOTRECOVERABLE => "__WASI_ERRNO_NOTRECOVERABLE", - __WASI_ERRNO_NOTSOCK => "__WASI_ERRNO_NOTSOCK", - __WASI_ERRNO_NOTSUP => "__WASI_ERRNO_NOTSUP", - __WASI_ERRNO_NOTTY => "__WASI_ERRNO_NOTTY", - __WASI_ERRNO_NXIO => "__WASI_ERRNO_NXIO", - __WASI_ERRNO_OVERFLOW => "__WASI_ERRNO_OVERFLOW", - __WASI_ERRNO_OWNERDEAD => "__WASI_ERRNO_OWNERDEAD", - __WASI_ERRNO_PERM => "__WASI_ERRNO_PERM", - __WASI_ERRNO_PIPE => "__WASI_ERRNO_PIPE", - __WASI_ERRNO_PROTO => "__WASI_ERRNO_PROTO", - __WASI_ERRNO_PROTONOSUPPORT => "__WASI_ERRNO_PROTONOSUPPORT", - __WASI_ERRNO_PROTOTYPE => "__WASI_ERRNO_PROTOTYPE", - __WASI_ERRNO_RANGE => "__WASI_ERRNO_RANGE", - __WASI_ERRNO_ROFS => "__WASI_ERRNO_ROFS", - __WASI_ERRNO_SPIPE => "__WASI_ERRNO_SPIPE", - __WASI_ERRNO_SRCH => "__WASI_ERRNO_SRCH", - __WASI_ERRNO_STALE => "__WASI_ERRNO_STALE", - __WASI_ERRNO_TIMEDOUT => "__WASI_ERRNO_TIMEDOUT", - __WASI_ERRNO_TXTBSY => "__WASI_ERRNO_TXTBSY", - __WASI_ERRNO_XDEV => "__WASI_ERRNO_XDEV", - __WASI_ERRNO_NOTCAPABLE => "__WASI_ERRNO_NOTCAPABLE", - other => panic!("Undefined errno value {:?}", other), - } -} - pub fn whence_to_str(whence: __wasi_whence_t) -> &'static str { match whence { __WASI_WHENCE_CUR => "__WASI_WHENCE_CUR", diff --git a/crates/wasi-common/src/sys/mod.rs b/crates/wasi-common/src/sys/mod.rs index 1149a426a3..849fb7ee16 100644 --- a/crates/wasi-common/src/sys/mod.rs +++ b/crates/wasi-common/src/sys/mod.rs @@ -1,4 +1,3 @@ -use crate::wasi; use cfg_if::cfg_if; cfg_if! { @@ -6,18 +5,10 @@ cfg_if! { mod unix; pub(crate) use unix::*; pub use unix::preopen_dir; - - pub(crate) fn errno_from_host(err: i32) -> wasi::__wasi_errno_t { - host_impl::errno_from_nix(yanix::Errno::from_i32(err)).as_wasi_errno() - } } else if #[cfg(windows)] { mod windows; pub(crate) use windows::*; pub use windows::preopen_dir; - - pub(crate) fn errno_from_host(err: i32) -> wasi::__wasi_errno_t { - host_impl::errno_from_win(winx::winerror::WinError::from_u32(err as u32)) - } } else { compile_error!("wasi-common doesn't compile for this platform yet"); } diff --git a/crates/wasi-common/src/sys/unix/bsd/hostcalls_impl.rs b/crates/wasi-common/src/sys/unix/bsd/hostcalls_impl.rs index 015c52da4f..e03cbb8a86 100644 --- a/crates/wasi-common/src/sys/unix/bsd/hostcalls_impl.rs +++ b/crates/wasi-common/src/sys/unix/bsd/hostcalls_impl.rs @@ -1,5 +1,4 @@ use crate::hostcalls_impl::PathGet; -use crate::sys::host_impl; use crate::{Error, Result}; use std::os::unix::prelude::AsRawFd; @@ -80,7 +79,7 @@ pub(crate) fn path_symlink(old_path: &str, resolved: PathGet) -> Result<()> { Err(Error::ENOTDIR) } } - x => Err(host_impl::errno_from_nix(x)), + x => Err(x.into()), } } else { Err(err.into()) @@ -132,7 +131,7 @@ pub(crate) fn path_rename(resolved_old: PathGet, resolved_new: PathGet) -> Resul Err(Error::ENOENT) } } - x => Err(host_impl::errno_from_nix(x)), + x => Err(x.into()), } } else { Err(err.into()) diff --git a/crates/wasi-common/src/sys/unix/host_impl.rs b/crates/wasi-common/src/sys/unix/host_impl.rs index e74c2bcc7f..8aecef3083 100644 --- a/crates/wasi-common/src/sys/unix/host_impl.rs +++ b/crates/wasi-common/src/sys/unix/host_impl.rs @@ -3,7 +3,7 @@ #![allow(non_snake_case)] #![allow(dead_code)] use crate::host::FileType; -use crate::{helpers, sys::unix::sys_impl, wasi, Error, Result}; +use crate::{error::FromRawOsError, helpers, sys::unix::sys_impl, wasi, Error, Result}; use std::ffi::OsStr; use std::os::unix::prelude::OsStrExt; use yanix::{ @@ -13,82 +13,90 @@ use yanix::{ pub(crate) use sys_impl::host_impl::*; -pub(crate) fn errno_from_nix(errno: Errno) -> Error { - match errno { - Errno::EPERM => Error::EPERM, - Errno::ENOENT => Error::ENOENT, - Errno::ESRCH => Error::ESRCH, - Errno::EINTR => Error::EINTR, - Errno::EIO => Error::EIO, - Errno::ENXIO => Error::ENXIO, - Errno::E2BIG => Error::E2BIG, - Errno::ENOEXEC => Error::ENOEXEC, - Errno::EBADF => Error::EBADF, - Errno::ECHILD => Error::ECHILD, - Errno::EAGAIN => Error::EAGAIN, - Errno::ENOMEM => Error::ENOMEM, - Errno::EACCES => Error::EACCES, - Errno::EFAULT => Error::EFAULT, - Errno::EBUSY => Error::EBUSY, - Errno::EEXIST => Error::EEXIST, - Errno::EXDEV => Error::EXDEV, - Errno::ENODEV => Error::ENODEV, - Errno::ENOTDIR => Error::ENOTDIR, - Errno::EISDIR => Error::EISDIR, - Errno::EINVAL => Error::EINVAL, - Errno::ENFILE => Error::ENFILE, - Errno::EMFILE => Error::EMFILE, - Errno::ENOTTY => Error::ENOTTY, - Errno::ETXTBSY => Error::ETXTBSY, - Errno::EFBIG => Error::EFBIG, - Errno::ENOSPC => Error::ENOSPC, - Errno::ESPIPE => Error::ESPIPE, - Errno::EROFS => Error::EROFS, - Errno::EMLINK => Error::EMLINK, - Errno::EPIPE => Error::EPIPE, - Errno::EDOM => Error::EDOM, - Errno::ERANGE => Error::ERANGE, - Errno::EDEADLK => Error::EDEADLK, - Errno::ENAMETOOLONG => Error::ENAMETOOLONG, - Errno::ENOLCK => Error::ENOLCK, - Errno::ENOSYS => Error::ENOSYS, - Errno::ENOTEMPTY => Error::ENOTEMPTY, - Errno::ELOOP => Error::ELOOP, - Errno::ENOMSG => Error::ENOMSG, - Errno::EIDRM => Error::EIDRM, - Errno::ENOLINK => Error::ENOLINK, - Errno::EPROTO => Error::EPROTO, - Errno::EMULTIHOP => Error::EMULTIHOP, - Errno::EBADMSG => Error::EBADMSG, - Errno::EOVERFLOW => Error::EOVERFLOW, - Errno::EILSEQ => Error::EILSEQ, - Errno::ENOTSOCK => Error::ENOTSOCK, - Errno::EDESTADDRREQ => Error::EDESTADDRREQ, - Errno::EMSGSIZE => Error::EMSGSIZE, - Errno::EPROTOTYPE => Error::EPROTOTYPE, - Errno::ENOPROTOOPT => Error::ENOPROTOOPT, - Errno::EPROTONOSUPPORT => Error::EPROTONOSUPPORT, - Errno::EAFNOSUPPORT => Error::EAFNOSUPPORT, - Errno::EADDRINUSE => Error::EADDRINUSE, - Errno::EADDRNOTAVAIL => Error::EADDRNOTAVAIL, - Errno::ENETDOWN => Error::ENETDOWN, - Errno::ENETUNREACH => Error::ENETUNREACH, - Errno::ENETRESET => Error::ENETRESET, - Errno::ECONNABORTED => Error::ECONNABORTED, - Errno::ECONNRESET => Error::ECONNRESET, - Errno::ENOBUFS => Error::ENOBUFS, - Errno::EISCONN => Error::EISCONN, - Errno::ENOTCONN => Error::ENOTCONN, - Errno::ETIMEDOUT => Error::ETIMEDOUT, - Errno::ECONNREFUSED => Error::ECONNREFUSED, - Errno::EHOSTUNREACH => Error::EHOSTUNREACH, - Errno::EALREADY => Error::EALREADY, - Errno::EINPROGRESS => Error::EINPROGRESS, - Errno::ESTALE => Error::ESTALE, - Errno::EDQUOT => Error::EDQUOT, - Errno::ECANCELED => Error::ECANCELED, - Errno::EOWNERDEAD => Error::EOWNERDEAD, - Errno::ENOTRECOVERABLE => Error::ENOTRECOVERABLE, +impl FromRawOsError for Error { + fn from_raw_os_error(code: i32) -> Self { + Self::from(Errno::from_i32(code)) + } +} + +impl From for Error { + fn from(errno: Errno) -> Self { + match errno { + Errno::EPERM => Self::EPERM, + Errno::ENOENT => Self::ENOENT, + Errno::ESRCH => Self::ESRCH, + Errno::EINTR => Self::EINTR, + Errno::EIO => Self::EIO, + Errno::ENXIO => Self::ENXIO, + Errno::E2BIG => Self::E2BIG, + Errno::ENOEXEC => Self::ENOEXEC, + Errno::EBADF => Self::EBADF, + Errno::ECHILD => Self::ECHILD, + Errno::EAGAIN => Self::EAGAIN, + Errno::ENOMEM => Self::ENOMEM, + Errno::EACCES => Self::EACCES, + Errno::EFAULT => Self::EFAULT, + Errno::EBUSY => Self::EBUSY, + Errno::EEXIST => Self::EEXIST, + Errno::EXDEV => Self::EXDEV, + Errno::ENODEV => Self::ENODEV, + Errno::ENOTDIR => Self::ENOTDIR, + Errno::EISDIR => Self::EISDIR, + Errno::EINVAL => Self::EINVAL, + Errno::ENFILE => Self::ENFILE, + Errno::EMFILE => Self::EMFILE, + Errno::ENOTTY => Self::ENOTTY, + Errno::ETXTBSY => Self::ETXTBSY, + Errno::EFBIG => Self::EFBIG, + Errno::ENOSPC => Self::ENOSPC, + Errno::ESPIPE => Self::ESPIPE, + Errno::EROFS => Self::EROFS, + Errno::EMLINK => Self::EMLINK, + Errno::EPIPE => Self::EPIPE, + Errno::EDOM => Self::EDOM, + Errno::ERANGE => Self::ERANGE, + Errno::EDEADLK => Self::EDEADLK, + Errno::ENAMETOOLONG => Self::ENAMETOOLONG, + Errno::ENOLCK => Self::ENOLCK, + Errno::ENOSYS => Self::ENOSYS, + Errno::ENOTEMPTY => Self::ENOTEMPTY, + Errno::ELOOP => Self::ELOOP, + Errno::ENOMSG => Self::ENOMSG, + Errno::EIDRM => Self::EIDRM, + Errno::ENOLINK => Self::ENOLINK, + Errno::EPROTO => Self::EPROTO, + Errno::EMULTIHOP => Self::EMULTIHOP, + Errno::EBADMSG => Self::EBADMSG, + Errno::EOVERFLOW => Self::EOVERFLOW, + Errno::EILSEQ => Self::EILSEQ, + Errno::ENOTSOCK => Self::ENOTSOCK, + Errno::EDESTADDRREQ => Self::EDESTADDRREQ, + Errno::EMSGSIZE => Self::EMSGSIZE, + Errno::EPROTOTYPE => Self::EPROTOTYPE, + Errno::ENOPROTOOPT => Self::ENOPROTOOPT, + Errno::EPROTONOSUPPORT => Self::EPROTONOSUPPORT, + Errno::EAFNOSUPPORT => Self::EAFNOSUPPORT, + Errno::EADDRINUSE => Self::EADDRINUSE, + Errno::EADDRNOTAVAIL => Self::EADDRNOTAVAIL, + Errno::ENETDOWN => Self::ENETDOWN, + Errno::ENETUNREACH => Self::ENETUNREACH, + Errno::ENETRESET => Self::ENETRESET, + Errno::ECONNABORTED => Self::ECONNABORTED, + Errno::ECONNRESET => Self::ECONNRESET, + Errno::ENOBUFS => Self::ENOBUFS, + Errno::EISCONN => Self::EISCONN, + Errno::ENOTCONN => Self::ENOTCONN, + Errno::ETIMEDOUT => Self::ETIMEDOUT, + Errno::ECONNREFUSED => Self::ECONNREFUSED, + Errno::EHOSTUNREACH => Self::EHOSTUNREACH, + Errno::EALREADY => Self::EALREADY, + Errno::EINPROGRESS => Self::EINPROGRESS, + Errno::ESTALE => Self::ESTALE, + Errno::EDQUOT => Self::EDQUOT, + Errno::ECANCELED => Self::ECANCELED, + Errno::EOWNERDEAD => Self::EOWNERDEAD, + Errno::ENOTRECOVERABLE => Self::ENOTRECOVERABLE, + } } } diff --git a/crates/wasi-common/src/sys/unix/hostcalls_impl/fs.rs b/crates/wasi-common/src/sys/unix/hostcalls_impl/fs.rs index cf7c143a41..1ac926cb1f 100644 --- a/crates/wasi-common/src/sys/unix/hostcalls_impl/fs.rs +++ b/crates/wasi-common/src/sys/unix/hostcalls_impl/fs.rs @@ -175,7 +175,7 @@ pub(crate) fn path_open( Errno::EMLINK if !(nix_all_oflags & OFlag::NOFOLLOW).is_empty() => { return Err(Error::ELOOP); } - errno => return Err(host_impl::errno_from_nix(errno)), + errno => return Err(errno.into()), } } else { return Err(e.into()); diff --git a/crates/wasi-common/src/sys/unix/hostcalls_impl/misc.rs b/crates/wasi-common/src/sys/unix/hostcalls_impl/misc.rs index 34135f59de..30adda29ed 100644 --- a/crates/wasi-common/src/sys/unix/hostcalls_impl/misc.rs +++ b/crates/wasi-common/src/sys/unix/hostcalls_impl/misc.rs @@ -1,7 +1,6 @@ #![allow(non_camel_case_types)] #![allow(unused_unsafe)] use crate::hostcalls_impl::{ClockEventData, FdEventData}; -use crate::sys::host_impl; use crate::{wasi, Error, Result}; use yanix::clock::{clock_getres, clock_gettime, ClockId}; @@ -92,7 +91,7 @@ pub(crate) fn poll_oneoff( if Errno::last() == Errno::EINTR { continue; } - return Err(host_impl::errno_from_nix(Errno::last())); + return Err(Errno::last().into()); } Ok(ready) => break ready, } diff --git a/crates/wasi-common/src/sys/windows/host_impl.rs b/crates/wasi-common/src/sys/windows/host_impl.rs index 71d7ef0332..ba6717e5e6 100644 --- a/crates/wasi-common/src/sys/windows/host_impl.rs +++ b/crates/wasi-common/src/sys/windows/host_impl.rs @@ -1,41 +1,50 @@ //! WASI host types specific to Windows host. use crate::host::FileType; -use crate::{wasi, Error, Result}; +use crate::{error::FromRawOsError, wasi, Error, Result}; use std::convert::TryInto; use std::ffi::OsStr; use std::fs::{self, File}; use std::io; use std::os::windows::ffi::OsStrExt; use std::time::{SystemTime, UNIX_EPOCH}; +use winx::winerror::WinError; -pub(crate) fn errno_from_win(error: winx::winerror::WinError) -> wasi::__wasi_errno_t { - // TODO: implement error mapping between Windows and WASI - use winx::winerror::WinError::*; - match error { - ERROR_SUCCESS => wasi::__WASI_ERRNO_SUCCESS, - ERROR_BAD_ENVIRONMENT => wasi::__WASI_ERRNO_2BIG, - ERROR_FILE_NOT_FOUND => wasi::__WASI_ERRNO_NOENT, - ERROR_PATH_NOT_FOUND => wasi::__WASI_ERRNO_NOENT, - ERROR_TOO_MANY_OPEN_FILES => wasi::__WASI_ERRNO_NFILE, - ERROR_ACCESS_DENIED => wasi::__WASI_ERRNO_ACCES, - ERROR_SHARING_VIOLATION => wasi::__WASI_ERRNO_ACCES, - ERROR_PRIVILEGE_NOT_HELD => wasi::__WASI_ERRNO_NOTCAPABLE, // TODO is this the correct mapping? - ERROR_INVALID_HANDLE => wasi::__WASI_ERRNO_BADF, - ERROR_INVALID_NAME => wasi::__WASI_ERRNO_NOENT, - ERROR_NOT_ENOUGH_MEMORY => wasi::__WASI_ERRNO_NOMEM, - ERROR_OUTOFMEMORY => wasi::__WASI_ERRNO_NOMEM, - ERROR_DIR_NOT_EMPTY => wasi::__WASI_ERRNO_NOTEMPTY, - ERROR_NOT_READY => wasi::__WASI_ERRNO_BUSY, - ERROR_BUSY => wasi::__WASI_ERRNO_BUSY, - ERROR_NOT_SUPPORTED => wasi::__WASI_ERRNO_NOTSUP, - ERROR_FILE_EXISTS => wasi::__WASI_ERRNO_EXIST, - ERROR_BROKEN_PIPE => wasi::__WASI_ERRNO_PIPE, - ERROR_BUFFER_OVERFLOW => wasi::__WASI_ERRNO_NAMETOOLONG, - ERROR_NOT_A_REPARSE_POINT => wasi::__WASI_ERRNO_INVAL, - ERROR_NEGATIVE_SEEK => wasi::__WASI_ERRNO_INVAL, - ERROR_DIRECTORY => wasi::__WASI_ERRNO_NOTDIR, - ERROR_ALREADY_EXISTS => wasi::__WASI_ERRNO_EXIST, - _ => wasi::__WASI_ERRNO_NOTSUP, +impl FromRawOsError for Error { + fn from_raw_os_error(code: i32) -> Self { + Self::from(WinError::from_u32(code as u32)) + } +} + +impl From for Error { + fn from(err: WinError) -> Self { + // TODO: implement error mapping between Windows and WASI + use winx::winerror::WinError::*; + match err { + ERROR_SUCCESS => Self::ESUCCESS, + ERROR_BAD_ENVIRONMENT => Self::E2BIG, + ERROR_FILE_NOT_FOUND => Self::ENOENT, + ERROR_PATH_NOT_FOUND => Self::ENOENT, + ERROR_TOO_MANY_OPEN_FILES => Self::ENFILE, + ERROR_ACCESS_DENIED => Self::EACCES, + ERROR_SHARING_VIOLATION => Self::EACCES, + ERROR_PRIVILEGE_NOT_HELD => Self::ENOTCAPABLE, // TODO is this the correct mapping? + ERROR_INVALID_HANDLE => Self::EBADF, + ERROR_INVALID_NAME => Self::ENOENT, + ERROR_NOT_ENOUGH_MEMORY => Self::ENOMEM, + ERROR_OUTOFMEMORY => Self::ENOMEM, + ERROR_DIR_NOT_EMPTY => Self::ENOTEMPTY, + ERROR_NOT_READY => Self::EBUSY, + ERROR_BUSY => Self::EBUSY, + ERROR_NOT_SUPPORTED => Self::ENOTSUP, + ERROR_FILE_EXISTS => Self::EEXIST, + ERROR_BROKEN_PIPE => Self::EPIPE, + ERROR_BUFFER_OVERFLOW => Self::ENAMETOOLONG, + ERROR_NOT_A_REPARSE_POINT => Self::EINVAL, + ERROR_NEGATIVE_SEEK => Self::EINVAL, + ERROR_DIRECTORY => Self::ENOTDIR, + ERROR_ALREADY_EXISTS => Self::EEXIST, + _ => Self::ENOTSUP, + } } } diff --git a/crates/wasi-common/src/wasi.rs b/crates/wasi-common/src/wasi.rs index 5bcc4d3b27..2c05d03957 100644 --- a/crates/wasi-common/src/wasi.rs +++ b/crates/wasi-common/src/wasi.rs @@ -107,89 +107,6 @@ pub(crate) const RIGHTS_TTY_BASE: __wasi_rights_t = __WASI_RIGHTS_FD_READ #[allow(unused)] pub(crate) const RIGHTS_TTY_INHERITING: __wasi_rights_t = 0; -pub fn strerror(errno: __wasi_errno_t) -> &'static str { - match errno { - __WASI_ERRNO_SUCCESS => "__WASI_ERRNO_SUCCESS", - __WASI_ERRNO_2BIG => "__WASI_ERRNO_2BIG", - __WASI_ERRNO_ACCES => "__WASI_ERRNO_ACCES", - __WASI_ERRNO_ADDRINUSE => "__WASI_ERRNO_ADDRINUSE", - __WASI_ERRNO_ADDRNOTAVAIL => "__WASI_ERRNO_ADDRNOTAVAIL", - __WASI_ERRNO_AFNOSUPPORT => "__WASI_ERRNO_AFNOSUPPORT", - __WASI_ERRNO_AGAIN => "__WASI_ERRNO_AGAIN", - __WASI_ERRNO_ALREADY => "__WASI_ERRNO_ALREADY", - __WASI_ERRNO_BADF => "__WASI_ERRNO_BADF", - __WASI_ERRNO_BADMSG => "__WASI_ERRNO_BADMSG", - __WASI_ERRNO_BUSY => "__WASI_ERRNO_BUSY", - __WASI_ERRNO_CANCELED => "__WASI_ERRNO_CANCELED", - __WASI_ERRNO_CHILD => "__WASI_ERRNO_CHILD", - __WASI_ERRNO_CONNABORTED => "__WASI_ERRNO_CONNABORTED", - __WASI_ERRNO_CONNREFUSED => "__WASI_ERRNO_CONNREFUSED", - __WASI_ERRNO_CONNRESET => "__WASI_ERRNO_CONNRESET", - __WASI_ERRNO_DEADLK => "__WASI_ERRNO_DEADLK", - __WASI_ERRNO_DESTADDRREQ => "__WASI_ERRNO_DESTADDRREQ", - __WASI_ERRNO_DOM => "__WASI_ERRNO_DOM", - __WASI_ERRNO_DQUOT => "__WASI_ERRNO_DQUOT", - __WASI_ERRNO_EXIST => "__WASI_ERRNO_EXIST", - __WASI_ERRNO_FAULT => "__WASI_ERRNO_FAULT", - __WASI_ERRNO_FBIG => "__WASI_ERRNO_FBIG", - __WASI_ERRNO_HOSTUNREACH => "__WASI_ERRNO_HOSTUNREACH", - __WASI_ERRNO_IDRM => "__WASI_ERRNO_IDRM", - __WASI_ERRNO_ILSEQ => "__WASI_ERRNO_ILSEQ", - __WASI_ERRNO_INPROGRESS => "__WASI_ERRNO_INPROGRESS", - __WASI_ERRNO_INTR => "__WASI_ERRNO_INTR", - __WASI_ERRNO_INVAL => "__WASI_ERRNO_INVAL", - __WASI_ERRNO_IO => "__WASI_ERRNO_IO", - __WASI_ERRNO_ISCONN => "__WASI_ERRNO_ISCONN", - __WASI_ERRNO_ISDIR => "__WASI_ERRNO_ISDIR", - __WASI_ERRNO_LOOP => "__WASI_ERRNO_LOOP", - __WASI_ERRNO_MFILE => "__WASI_ERRNO_MFILE", - __WASI_ERRNO_MLINK => "__WASI_ERRNO_MLINK", - __WASI_ERRNO_MSGSIZE => "__WASI_ERRNO_MSGSIZE", - __WASI_ERRNO_MULTIHOP => "__WASI_ERRNO_MULTIHOP", - __WASI_ERRNO_NAMETOOLONG => "__WASI_ERRNO_NAMETOOLONG", - __WASI_ERRNO_NETDOWN => "__WASI_ERRNO_NETDOWN", - __WASI_ERRNO_NETRESET => "__WASI_ERRNO_NETRESET", - __WASI_ERRNO_NETUNREACH => "__WASI_ERRNO_NETUNREACH", - __WASI_ERRNO_NFILE => "__WASI_ERRNO_NFILE", - __WASI_ERRNO_NOBUFS => "__WASI_ERRNO_NOBUFS", - __WASI_ERRNO_NODEV => "__WASI_ERRNO_NODEV", - __WASI_ERRNO_NOENT => "__WASI_ERRNO_NOENT", - __WASI_ERRNO_NOEXEC => "__WASI_ERRNO_NOEXEC", - __WASI_ERRNO_NOLCK => "__WASI_ERRNO_NOLCK", - __WASI_ERRNO_NOLINK => "__WASI_ERRNO_NOLINK", - __WASI_ERRNO_NOMEM => "__WASI_ERRNO_NOMEM", - __WASI_ERRNO_NOMSG => "__WASI_ERRNO_NOMSG", - __WASI_ERRNO_NOPROTOOPT => "__WASI_ERRNO_NOPROTOOPT", - __WASI_ERRNO_NOSPC => "__WASI_ERRNO_NOSPC", - __WASI_ERRNO_NOSYS => "__WASI_ERRNO_NOSYS", - __WASI_ERRNO_NOTCONN => "__WASI_ERRNO_NOTCONN", - __WASI_ERRNO_NOTDIR => "__WASI_ERRNO_NOTDIR", - __WASI_ERRNO_NOTEMPTY => "__WASI_ERRNO_NOTEMPTY", - __WASI_ERRNO_NOTRECOVERABLE => "__WASI_ERRNO_NOTRECOVERABLE", - __WASI_ERRNO_NOTSOCK => "__WASI_ERRNO_NOTSOCK", - __WASI_ERRNO_NOTSUP => "__WASI_ERRNO_NOTSUP", - __WASI_ERRNO_NOTTY => "__WASI_ERRNO_NOTTY", - __WASI_ERRNO_NXIO => "__WASI_ERRNO_NXIO", - __WASI_ERRNO_OVERFLOW => "__WASI_ERRNO_OVERFLOW", - __WASI_ERRNO_OWNERDEAD => "__WASI_ERRNO_OWNERDEAD", - __WASI_ERRNO_PERM => "__WASI_ERRNO_PERM", - __WASI_ERRNO_PIPE => "__WASI_ERRNO_PIPE", - __WASI_ERRNO_PROTO => "__WASI_ERRNO_PROTO", - __WASI_ERRNO_PROTONOSUPPORT => "__WASI_ERRNO_PROTONOSUPPORT", - __WASI_ERRNO_PROTOTYPE => "__WASI_ERRNO_PROTOTYPE", - __WASI_ERRNO_RANGE => "__WASI_ERRNO_RANGE", - __WASI_ERRNO_ROFS => "__WASI_ERRNO_ROFS", - __WASI_ERRNO_SPIPE => "__WASI_ERRNO_SPIPE", - __WASI_ERRNO_SRCH => "__WASI_ERRNO_SRCH", - __WASI_ERRNO_STALE => "__WASI_ERRNO_STALE", - __WASI_ERRNO_TIMEDOUT => "__WASI_ERRNO_TIMEDOUT", - __WASI_ERRNO_TXTBSY => "__WASI_ERRNO_TXTBSY", - __WASI_ERRNO_XDEV => "__WASI_ERRNO_XDEV", - __WASI_ERRNO_NOTCAPABLE => "__WASI_ERRNO_NOTCAPABLE", - other => panic!("Undefined errno value {:?}", other), - } -} - pub fn whence_to_str(whence: __wasi_whence_t) -> &'static str { match whence { __WASI_WHENCE_CUR => "__WASI_WHENCE_CUR", diff --git a/crates/wasi-common/wig/src/raw_types.rs b/crates/wasi-common/wig/src/raw_types.rs index 15de007bdb..44b82db391 100644 --- a/crates/wasi-common/wig/src/raw_types.rs +++ b/crates/wasi-common/wig/src/raw_types.rs @@ -148,6 +148,38 @@ fn gen_datatype(output: &mut TokenStream, mode: Mode, namedtype: &witx::NamedTyp } }, } + + if namedtype.name.as_str() == "errno" { + // Generate strerror for errno type + gen_errno_strerror(output, namedtype); + } +} + +fn gen_errno_strerror(output: &mut TokenStream, namedtype: &witx::NamedType) { + let inner = match &namedtype.dt { + witx::TypeRef::Value(v) => match &**v { + witx::Type::Enum(e) => e, + x => panic!("expected Enum('errno'), instead received {:?}", x), + }, + x => panic!("expected Enum('errno'), instead received {:?}", x), + }; + let mut inner_group = TokenStream::new(); + for variant in &inner.variants { + let value_name = format_ident!( + "__WASI_ERRNO_{}", + variant.name.as_str().to_shouty_snake_case() + ); + let docs = variant.docs.trim(); + inner_group.extend(quote!(#value_name => #docs,)); + } + output.extend( + quote!(pub fn strerror(errno: __wasi_errno_t) -> &'static str { + match errno { + #inner_group + other => panic!("Undefined errno value {:?}", other), + } + }), + ); } fn int_repr_tokens(int_repr: witx::IntRepr) -> TokenStream { diff --git a/crates/wast/src/wast.rs b/crates/wast/src/wast.rs index fc71896ac9..72a4fd74e0 100644 --- a/crates/wast/src/wast.rs +++ b/crates/wast/src/wast.rs @@ -94,7 +94,7 @@ impl WastContext { .get(import.module()) .ok_or_else(|| anyhow!("no module named `{}`", import.module()))?; let export = instance - .find_export_by_name(import.name()) + .get_export(import.name()) .ok_or_else(|| anyhow!("unknown import `{}::{}`", import.name(), import.module()))? .clone(); imports.push(export); @@ -165,13 +165,10 @@ impl WastContext { args: &[Val], ) -> Result { let instance = self.get_instance(instance_name.as_ref().map(|x| &**x))?; - let export = instance - .find_export_by_name(field) - .ok_or_else(|| anyhow!("no global named `{}`", field))?; - let func = match export { - Extern::Func(f) => f, - _ => bail!("export of `{}` wasn't a global", field), - }; + let func = instance + .get_export(field) + .and_then(|e| e.func()) + .ok_or_else(|| anyhow!("no function named `{}`", field))?; Ok(match func.call(args) { Ok(result) => Outcome::Ok(result.into()), Err(e) => Outcome::Trap(e), @@ -181,13 +178,10 @@ impl WastContext { /// Get the value of an exported global from an instance. fn get(&mut self, instance_name: Option<&str>, field: &str) -> Result { let instance = self.get_instance(instance_name.as_ref().map(|x| &**x))?; - let export = instance - .find_export_by_name(field) + let global = instance + .get_export(field) + .and_then(|e| e.global()) .ok_or_else(|| anyhow!("no global named `{}`", field))?; - let global = match export { - Extern::Global(g) => g, - _ => bail!("export of `{}` wasn't a global", field), - }; Ok(Outcome::Ok(vec![global.get()])) } diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 50dfe42fae..0d28aae9a1 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -13,7 +13,6 @@ arbitrary = "0.2.0" env_logger = "0.7.1" log = "0.4.8" wasmtime-fuzzing = { path = "../crates/fuzzing", features = ["env_logger"] } -wasmtime-jit = { path = "../crates/jit" } wasmtime = { path = "../crates/api" } libfuzzer-sys = "0.2.0" diff --git a/fuzz/fuzz_targets/compile.rs b/fuzz/fuzz_targets/compile.rs index 2b495f63dc..371ba11947 100644 --- a/fuzz/fuzz_targets/compile.rs +++ b/fuzz/fuzz_targets/compile.rs @@ -1,20 +1,14 @@ #![no_main] use libfuzzer_sys::fuzz_target; +use wasmtime::Strategy; use wasmtime_fuzzing::{oracles, with_log_wasm_test_case}; -use wasmtime_jit::CompilationStrategy; fuzz_target!(|data: &[u8]| { - with_log_wasm_test_case!(data, |data| oracles::compile( - data, - CompilationStrategy::Cranelift - )); + with_log_wasm_test_case!(data, |data| oracles::compile(data, Strategy::Cranelift)); }); #[cfg(feature = "lightbeam")] fuzz_target!(|data: &[u8]| { - with_log_wasm_test_case!(data, |data| oracles::compile( - data, - CompilationStrategy::Lightbeam - )); + with_log_wasm_test_case!(data, |data| oracles::compile(data, Strategy::Lightbeam)); }); diff --git a/src/commands/run.rs b/src/commands/run.rs index 313e837017..3d0ce4df7a 100644 --- a/src/commands/run.rs +++ b/src/commands/run.rs @@ -244,7 +244,7 @@ impl RunCommand { let module_name = i.module(); if let Some(instance) = module_registry.get(module_name) { let field_name = i.name(); - if let Some(export) = instance.find_export_by_name(field_name) { + if let Some(export) = instance.get_export(field_name) { Ok(export.clone()) } else { bail!( diff --git a/tests/custom_signal_handler.rs b/tests/custom_signal_handler.rs index 3bbb590424..378b77441e 100644 --- a/tests/custom_signal_handler.rs +++ b/tests/custom_signal_handler.rs @@ -3,6 +3,7 @@ mod tests { use anyhow::Result; use std::rc::Rc; use std::sync::atomic::{AtomicBool, Ordering}; + use wasmtime::unix::InstanceExt; use wasmtime::*; const WAT1: &str = r#" @@ -38,7 +39,7 @@ mod tests { fn invoke_export(instance: &Instance, func_name: &str) -> Result, Trap> { let ret = instance - .find_export_by_name(func_name) + .get_export(func_name) .unwrap() .func() .unwrap() @@ -111,9 +112,11 @@ mod tests { let instance = Instance::new(&module, &[])?; let (base, length) = set_up_memory(&instance); - instance.set_signal_handler(move |signum, siginfo, _| { - handle_sigsegv(base, length, signum, siginfo) - }); + unsafe { + instance.set_signal_handler(move |signum, siginfo, _| { + handle_sigsegv(base, length, signum, siginfo) + }); + } let exports = instance.exports(); assert!(!exports.is_empty()); @@ -171,20 +174,18 @@ mod tests { let instance1 = Instance::new(&module, &[])?; let instance1_handler_triggered = Rc::new(AtomicBool::new(false)); - { + unsafe { let (base1, length1) = set_up_memory(&instance1); instance1.set_signal_handler({ let instance1_handler_triggered = instance1_handler_triggered.clone(); move |_signum, _siginfo, _context| { // Remove protections so the execution may resume - unsafe { - libc::mprotect( - base1 as *mut libc::c_void, - length1, - libc::PROT_READ | libc::PROT_WRITE, - ); - } + libc::mprotect( + base1 as *mut libc::c_void, + length1, + libc::PROT_READ | libc::PROT_WRITE, + ); instance1_handler_triggered.store(true, Ordering::SeqCst); println!( "Hello from instance1 signal handler! {}", @@ -198,20 +199,18 @@ mod tests { let instance2 = Instance::new(&module, &[]).expect("failed to instantiate module"); let instance2_handler_triggered = Rc::new(AtomicBool::new(false)); - { + unsafe { let (base2, length2) = set_up_memory(&instance2); instance2.set_signal_handler({ let instance2_handler_triggered = instance2_handler_triggered.clone(); move |_signum, _siginfo, _context| { // Remove protections so the execution may resume - unsafe { - libc::mprotect( - base2 as *mut libc::c_void, - length2, - libc::PROT_READ | libc::PROT_WRITE, - ); - } + libc::mprotect( + base2 as *mut libc::c_void, + length2, + libc::PROT_READ | libc::PROT_WRITE, + ); instance2_handler_triggered.store(true, Ordering::SeqCst); println!( "Hello from instance2 signal handler! {}", @@ -266,10 +265,12 @@ mod tests { let module1 = Module::new(&store, &data1)?; let instance1 = Instance::new(&module1, &[])?; let (base1, length1) = set_up_memory(&instance1); - instance1.set_signal_handler(move |signum, siginfo, _| { - println!("instance1"); - handle_sigsegv(base1, length1, signum, siginfo) - }); + unsafe { + instance1.set_signal_handler(move |signum, siginfo, _| { + println!("instance1"); + handle_sigsegv(base1, length1, signum, siginfo) + }); + } let instance1_exports = instance1.exports(); assert!(!instance1_exports.is_empty()); @@ -281,9 +282,11 @@ mod tests { let instance2 = Instance::new(&module2, &[instance1_read])?; // since 'instance2.run' calls 'instance1.read' we need to set up the signal handler to handle // SIGSEGV originating from within the memory of instance1 - instance2.set_signal_handler(move |signum, siginfo, _| { - handle_sigsegv(base1, length1, signum, siginfo) - }); + unsafe { + instance2.set_signal_handler(move |signum, siginfo, _| { + handle_sigsegv(base1, length1, signum, siginfo) + }); + } println!("calling instance2.run"); let result = invoke_export(&instance2, "run")?;