Merge remote-tracking branch 'upstream/master' into poll
This commit is contained in:
5
Cargo.lock
generated
5
Cargo.lock
generated
@@ -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",
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -20,28 +20,14 @@ impl Resolver for SimpleResolver<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
fn instantiate_in_context(
|
||||
store: &Store,
|
||||
data: &[u8],
|
||||
fn instantiate(
|
||||
compiled_module: &CompiledModule,
|
||||
imports: &[Extern],
|
||||
module_name: Option<&str>,
|
||||
) -> Result<InstanceHandle> {
|
||||
) -> Result<InstanceHandle, Error> {
|
||||
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 {
|
||||
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 {
|
||||
@@ -53,22 +39,77 @@ fn instantiate_in_context(
|
||||
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<Instance, Error> {
|
||||
/// 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::<Trap>()`.
|
||||
///
|
||||
/// [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<Instance, Error> {
|
||||
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<wasmtime_runtime::Export> {
|
||||
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<H>(&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<H>(&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<H>(&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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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!
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<String>,
|
||||
compiled: Option<CompiledModule>,
|
||||
}
|
||||
|
||||
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<Module> {
|
||||
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> {
|
||||
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<Module> {
|
||||
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<CompiledModule> {
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -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("<unknown>");
|
||||
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, "<wasm function {}>", frame.func_index)?,
|
||||
}
|
||||
writeln!(f, "")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
31
crates/api/src/unix.rs
Normal file
31
crates/api/src/unix.rs
Normal file
@@ -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<H>(&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<H>(&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);
|
||||
}
|
||||
}
|
||||
29
crates/api/src/windows.rs
Normal file
29
crates/api/src/windows.rs
Normal file
@@ -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<H>(&self, handler: H)
|
||||
where
|
||||
H: 'static + Fn(winapi::um::winnt::EXCEPTION_POINTERS) -> bool;
|
||||
}
|
||||
|
||||
impl InstanceExt for Instance {
|
||||
unsafe fn set_signal_handler<H>(&self, handler: H)
|
||||
where
|
||||
H: 'static + Fn(winapi::um::winnt::EXCEPTION_POINTERS) -> bool,
|
||||
{
|
||||
self.instance_handle.clone().set_signal_handler(handler);
|
||||
}
|
||||
}
|
||||
@@ -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] {
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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!<wasm function 1>
|
||||
2: m!foo
|
||||
3: m!<wasm function 3>
|
||||
"
|
||||
);
|
||||
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!<wasm function 1>
|
||||
2: a!foo
|
||||
3: a!<wasm function 3>
|
||||
4: b!middle
|
||||
5: b!<wasm function 2>
|
||||
"
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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<dyn isa::TargetIsa> {
|
||||
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.
|
||||
|
||||
@@ -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,8 +147,8 @@ impl ModuleData {
|
||||
.into_iter()
|
||||
.map(|rv| rv.into())
|
||||
.collect::<Vec<_>>();
|
||||
let wasm_results = match f.call(&wasm_args) {
|
||||
Ok(values) => values
|
||||
let wasm_results = f
|
||||
.call(&wasm_args)?
|
||||
.to_vec()
|
||||
.into_iter()
|
||||
.map(|v: wasmtime::Val| match v {
|
||||
@@ -159,9 +159,7 @@ impl ModuleData {
|
||||
wasmtime::Val::V128(i) => RuntimeValue::V128(i.to_le_bytes()),
|
||||
_ => panic!("unsupported value {:?}", v),
|
||||
})
|
||||
.collect::<Vec<RuntimeValue>>(),
|
||||
Err(trap) => bail!("trapped: {:?}", trap),
|
||||
};
|
||||
.collect::<Vec<RuntimeValue>>();
|
||||
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<i32> {
|
||||
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"))?
|
||||
|
||||
@@ -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"] }
|
||||
|
||||
@@ -156,7 +156,6 @@ impl Context {
|
||||
&mut *self.compiler,
|
||||
data,
|
||||
None,
|
||||
&mut self.namespace,
|
||||
Rc::clone(&self.global_exports),
|
||||
debug_info,
|
||||
)
|
||||
|
||||
289
crates/jit/src/imports.rs
Normal file
289
crates/jit/src/imports.rs
Normal file
@@ -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<Imports, LinkError> {
|
||||
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
|
||||
}
|
||||
@@ -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<DefinedFuncIndex, *const VMFunctionBody>,
|
||||
imports: Imports,
|
||||
data_initializers: Box<[DataInitializer<'data>]>,
|
||||
signatures: BoxedSlice<SignatureIndex, VMSharedSignatureIndex>,
|
||||
dbg_jit_registration: Option<GdbJitImageRegistration>,
|
||||
@@ -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<Self, SetupError> {
|
||||
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<DefinedFuncIndex, *const VMFunctionBody> =
|
||||
@@ -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<Module>,
|
||||
finished_functions: BoxedSlice<DefinedFuncIndex, *const VMFunctionBody>,
|
||||
imports: Imports,
|
||||
data_initializers: Box<[OwnedDataInitializer]>,
|
||||
signatures: BoxedSlice<SignatureIndex, VMSharedSignatureIndex>,
|
||||
global_exports: Rc<RefCell<HashMap<String, Option<Export>>>>,
|
||||
@@ -157,18 +152,15 @@ impl CompiledModule {
|
||||
compiler: &mut Compiler,
|
||||
data: &'data [u8],
|
||||
module_name: Option<&str>,
|
||||
resolver: &mut dyn Resolver,
|
||||
global_exports: Rc<RefCell<HashMap<String, Option<Export>>>>,
|
||||
debug_info: bool,
|
||||
) -> Result<Self, SetupError> {
|
||||
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<RefCell<HashMap<String, Option<Export>>>>,
|
||||
finished_functions: BoxedSlice<DefinedFuncIndex, *const VMFunctionBody>,
|
||||
imports: Imports,
|
||||
data_initializers: Box<[OwnedDataInitializer]>,
|
||||
signatures: BoxedSlice<SignatureIndex, VMSharedSignatureIndex>,
|
||||
dbg_jit_registration: Option<GdbJitImageRegistration>,
|
||||
@@ -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<InstanceHandle, InstantiationError> {
|
||||
pub fn instantiate(
|
||||
&self,
|
||||
resolver: &mut dyn Resolver,
|
||||
) -> Result<InstanceHandle, InstantiationError> {
|
||||
let data_initializers = self
|
||||
.data_initializers
|
||||
.iter()
|
||||
@@ -214,11 +207,12 @@ impl CompiledModule {
|
||||
data: &*init.data,
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
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<RefCell<HashMap<String, Option<Export>>>>,
|
||||
debug_info: bool,
|
||||
) -> Result<InstanceHandle, SetupError> {
|
||||
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),
|
||||
|
||||
@@ -26,6 +26,7 @@ mod code_memory;
|
||||
mod compiler;
|
||||
mod context;
|
||||
mod function_table;
|
||||
mod imports;
|
||||
mod instantiate;
|
||||
mod link;
|
||||
mod namespace;
|
||||
|
||||
@@ -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<DefinedFuncIndex, *mut [VMFunctionBody]>,
|
||||
jt_offsets: &PrimaryMap<DefinedFuncIndex, JumpTableOffsets>,
|
||||
relocations: Relocations,
|
||||
resolver: &mut dyn Resolver,
|
||||
) -> Result<Imports, LinkError> {
|
||||
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<DefinedFuncIndex, *mut [VMFunctionBody]>,
|
||||
jt_offsets: &PrimaryMap<DefinedFuncIndex, JumpTableOffsets>,
|
||||
relocations: PrimaryMap<DefinedFuncIndex, Vec<Relocation>>,
|
||||
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(
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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::<Exception, _>(format!("wasi export {} is not found", i.name(),))
|
||||
})?;
|
||||
|
||||
@@ -67,7 +67,7 @@ fn generate_load(item: &syn::ItemTrait) -> syn::Result<TokenStream> {
|
||||
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())
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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<TryFromIntError> for Error {
|
||||
fn from(_: TryFromIntError) -> Self {
|
||||
Self::Wasi(WasiError::EOVERFLOW)
|
||||
Self::EOVERFLOW
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,39 +120,46 @@ impl From<Infallible> for Error {
|
||||
|
||||
impl From<str::Utf8Error> for Error {
|
||||
fn from(_: str::Utf8Error) -> 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 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<T> = std::result::Result<T, Error>;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
)*)
|
||||
}
|
||||
|
||||
@@ -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<TryFromIntError> for Error {
|
||||
fn from(_: TryFromIntError) -> Self {
|
||||
Self::Wasi(WasiError::EOVERFLOW)
|
||||
Self::EOVERFLOW
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,41 +120,49 @@ impl From<Infallible> for Error {
|
||||
|
||||
impl From<str::Utf8Error> for Error {
|
||||
fn from(_: str::Utf8Error) -> 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 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()
|
||||
}
|
||||
#[cfg(windows)]
|
||||
Self::Winx(err) => crate::old::snapshot_0::sys::host_impl::errno_from_win(*err),
|
||||
err.as_wasi_error()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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 {
|
||||
impl FromRawOsError for Error {
|
||||
fn from_raw_os_error(code: i32) -> Self {
|
||||
Self::from(Errno::from_i32(code))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Errno> for Error {
|
||||
fn from(errno: Errno) -> Self {
|
||||
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,
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
impl FromRawOsError for Error {
|
||||
fn from_raw_os_error(code: i32) -> Self {
|
||||
Self::from(WinError::from_u32(code as u32))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<WinError> for Error {
|
||||
fn from(err: WinError) -> Self {
|
||||
// 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,
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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 {
|
||||
impl FromRawOsError for Error {
|
||||
fn from_raw_os_error(code: i32) -> Self {
|
||||
Self::from(Errno::from_i32(code))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Errno> for Error {
|
||||
fn from(errno: Errno) -> Self {
|
||||
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,
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
impl FromRawOsError for Error {
|
||||
fn from_raw_os_error(code: i32) -> Self {
|
||||
Self::from(WinError::from_u32(code as u32))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<WinError> for Error {
|
||||
fn from(err: WinError) -> Self {
|
||||
// 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,
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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<Outcome> {
|
||||
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<Outcome> {
|
||||
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()]))
|
||||
}
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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));
|
||||
});
|
||||
|
||||
@@ -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!(
|
||||
|
||||
@@ -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<Box<[Val]>, 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);
|
||||
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,
|
||||
);
|
||||
}
|
||||
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,
|
||||
);
|
||||
}
|
||||
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);
|
||||
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
|
||||
unsafe {
|
||||
instance2.set_signal_handler(move |signum, siginfo, _| {
|
||||
handle_sigsegv(base1, length1, signum, siginfo)
|
||||
});
|
||||
}
|
||||
|
||||
println!("calling instance2.run");
|
||||
let result = invoke_export(&instance2, "run")?;
|
||||
|
||||
Reference in New Issue
Block a user