Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
73f19c4
Add functions to get a scalar Value as a slice
zebreus Jun 11, 2025
ef9b2cc
WIP: second call_dynamic iteration
zebreus Jun 11, 2025
3567f12
Implement basic register_function functionality in the linker
zebreus Jun 12, 2025
bac2f04
WIP: Add support for registering closures
zebreus Jun 13, 2025
a41e20b
Reset stack pointer after closure
zebreus Jun 13, 2025
f77d7b0
Add a way to load modules from memory
zebreus Jun 17, 2025
4284114
Make ModuleLoader borrowing
zebreus Jun 17, 2025
53d6883
Rename ModuleLoader to WasmLoader
zebreus Jun 17, 2025
17c1bfb
Rewrite closure_prepare to use dynamic wasm modules
zebreus Jun 17, 2025
525bab9
Allow loading the main module via dlopen
zebreus Jun 18, 2025
32746ed
Improve wasix closures API
zebreus Jun 23, 2025
19b2cb9
Improve error handling
zebreus Jun 23, 2025
af6ebab
Stop hijacking call_ctors for closure table init
zebreus Jun 24, 2025
ecef418
Use wasm_encoder to build closures instead of string interpolation
zebreus Jun 24, 2025
b15ba38
Clean up closure_prepare
zebreus Jun 24, 2025
7e06167
Move WasmValueType into wasix-types
zebreus Jun 24, 2025
f10760a
Improve access to the indirect function table in the linker
zebreus Jun 24, 2025
ae5ca3c
Remove comment about allocate_function_table returning a u64
zebreus Jun 24, 2025
5eca1e0
Cache the indirect function table export
zebreus Jun 24, 2025
785e825
Add fixme to closure prepare
zebreus Jun 30, 2025
10823e4
Allow side modules without tls initialization
zebreus Jun 30, 2025
5e9d3e8
Improve dynamic call tracing
zebreus Jul 1, 2025
e8a55b9
Add trace logs for indirect function table operations
zebreus Jul 7, 2025
16f96ff
Log dlopen flags
zebreus Jul 9, 2025
8299073
Add more information to call_dynamic traces
zebreus Jul 9, 2025
8f828a4
Rename as_bytes
zebreus Jul 11, 2025
400e05a
Fix formatting errors
zebreus Jul 11, 2025
75bb9c5
Move value as_bytes functions to call_dynamic
zebreus Jul 11, 2025
adc2812
Move changed
zebreus Jul 11, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ thiserror = "1"
target-lexicon = { version = "0.12.2", default-features = false }
object = { version = "0.32.0", default-features = true }
derive_more = { version = "2.0.1", features = ["debug", "display"] }
wasm-encoder = {version = "0.235.0", default-features = false, features = ["std"]}

[build-dependencies]
test-generator = { path = "tests/lib/test-generator" }
Expand Down
85 changes: 85 additions & 0 deletions lib/api/src/entities/value.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use thiserror::Error;
use wasmer_types::{RawValue, Type};

use crate::{
Expand Down Expand Up @@ -74,6 +75,20 @@ impl Value {
Self::ExternRef(None)
}

/// Creates a new `Value` with the default value for the given type.
pub fn default_typed(ty: &Type) -> Self {
match ty {
Type::I32 => Self::I32(0),
Type::I64 => Self::I64(0),
Type::F32 => Self::F32(0.0),
Type::F64 => Self::F64(0.0),
Type::V128 => Self::V128(0),
Type::ExternRef => Self::ExternRef(None),
Type::FuncRef => Self::FuncRef(None),
Type::ExceptionRef => Self::ExceptionRef(None),
}
}

/// Returns the corresponding [`Type`] for this [`Value`].
pub fn ty(&self) -> Type {
match self {
Expand Down Expand Up @@ -263,6 +278,76 @@ impl Value {
}
}

#[cfg(target_endian = "little")]
/// Get a slice to the content of this value if it is a scalar type.
///
/// Returns `None` for Value that can not be freely shared between contexts.
/// Returns `None` if the value is not representable as a byte slice.
///
/// Not available on big-endian architectures, because the result of this function
/// should be compatible with wasm memory, which is little-endian.
pub fn as_bytes(&self) -> Option<&[u8]> {
match self {
Self::I32(value) => {
// Safety: This function is only enabled on little-endian architectures,
Some(unsafe { std::slice::from_raw_parts(value as *const i32 as *const u8, 4) })
}
Self::I64(value) => {
// Safety: This function is only enabled on little-endian architectures,
Some(unsafe { std::slice::from_raw_parts(value as *const i64 as *const u8, 8) })
}
Self::F32(value) => {
// Safety: This function is only enabled on little-endian architectures,
Some(unsafe { std::slice::from_raw_parts(value as *const f32 as *const u8, 4) })
}
Self::F64(value) => {
// Safety: This function is only enabled on little-endian architectures,
Some(unsafe { std::slice::from_raw_parts(value as *const f64 as *const u8, 8) })
}
Self::V128(value) => {
// Safety: This function is only enabled on little-endian architectures,
Some(unsafe { std::slice::from_raw_parts(value as *const u128 as *const u8, 16) })
}
// ExternRef, FuncRef, and ExceptionRef cannot be represented as byte slices
_ => None,
}
}

#[cfg(target_endian = "little")]
/// Get a mutable slice to the content of this value if it is a scalar type.
///
/// Returns `None` for Value that can not be freely shared between contexts.
/// Returns `None` if the value is not representable as a byte slice.
///
/// Not available on big-endian architectures, because the result of this function
/// should be compatible with wasm memory, which is little-endian.
pub fn as_bytes_mut(&mut self) -> Option<&mut [u8]> {
match self {
Self::I32(value) => {
// Safety: This function is only enabled on little-endian architectures,
Some(unsafe { std::slice::from_raw_parts_mut(value as *mut i32 as *mut u8, 4) })
}
Self::I64(value) => {
// Safety: This function is only enabled on little-endian architectures,
Some(unsafe { std::slice::from_raw_parts_mut(value as *mut i64 as *mut u8, 8) })
}
Self::F32(value) => {
// Safety: This function is only enabled on little-endian architectures,
Some(unsafe { std::slice::from_raw_parts_mut(value as *mut f32 as *mut u8, 4) })
}
Self::F64(value) => {
// Safety: This function is only enabled on little-endian architectures,
Some(unsafe { std::slice::from_raw_parts_mut(value as *mut f64 as *mut u8, 8) })
}
Self::V128(value) => {
// Safety: This function is only enabled on little-endian architectures,
Some(unsafe { std::slice::from_raw_parts_mut(value as *mut u128 as *mut u8, 16) })
}
// ExternRef, FuncRef, and ExceptionRef cannot be represented as byte slices
_ => None,
}
}

accessors! {
e
(I32(i32) i32 unwrap_i32 *e)
Expand Down
29 changes: 29 additions & 0 deletions lib/wasi-types/src/wasi/bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2437,6 +2437,35 @@ impl DlFlags {
}
}

#[doc = " The type of a WASM value represented at runtime for use with the wasix closures API."]
#[doc = ""]
#[doc = " For now only number types are supported, but this may be extended in the future to include references"]
#[repr(u8)]
#[derive(Clone, Copy, PartialEq, Eq, num_enum :: TryFromPrimitive, Hash)]
pub enum WasmValueType {
#[doc = " A i32 parameter."]
I32,
#[doc = " A i64 parameter."]
I64,
#[doc = " A f32 parameter."]
F32,
#[doc = " A f64 parameter."]
F64,
#[doc = " A v128 parameter."]
V128,
}
impl core::fmt::Debug for WasmValueType {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
WasmValueType::I32 => f.debug_tuple("WasmValueType::I32").finish(),
WasmValueType::I64 => f.debug_tuple("WasmValueType::I64").finish(),
WasmValueType::F32 => f.debug_tuple("WasmValueType::F32").finish(),
WasmValueType::F64 => f.debug_tuple("WasmValueType::F64").finish(),
WasmValueType::V128 => f.debug_tuple("WasmValueType::V128").finish(),
}
}
}

#[repr(C)]
#[derive(Copy, Clone)]
pub struct AddrUnspec {
Expand Down
1 change: 1 addition & 0 deletions lib/wasix/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ rkyv.workspace = true
shared-buffer.workspace = true
hyper = { workspace = true, features = ["server"], optional = true }
derive_more.workspace = true
wasm-encoder.workspace = true

xxhash-rust = { version = "0.8.8", features = ["xxh64"] }
rusty_pool = { version = "0.7.0", optional = true }
Expand Down
8 changes: 8 additions & 0 deletions lib/wasix/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -509,9 +509,13 @@ fn wasix_exports_32(mut store: &mut impl AsStoreMut, env: &FunctionEnv<WasiEnv>)
let namespace = namespace! {
"args_get" => Function::new_typed_with_env(&mut store, env, args_get::<Memory32>),
"args_sizes_get" => Function::new_typed_with_env(&mut store, env, args_sizes_get::<Memory32>),
"call_dynamic" => Function::new_typed_with_env(&mut store, env, call_dynamic::<Memory32>),
"clock_res_get" => Function::new_typed_with_env(&mut store, env, clock_res_get::<Memory32>),
"clock_time_get" => Function::new_typed_with_env(&mut store, env, clock_time_get::<Memory32>),
"clock_time_set" => Function::new_typed_with_env(&mut store, env, clock_time_set::<Memory32>),
"closure_prepare" => Function::new_typed_with_env(&mut store, env, closure_prepare::<Memory32>),
"closure_allocate" => Function::new_typed_with_env(&mut store, env, closure_allocate::<Memory32>),
"closure_free" => Function::new_typed_with_env(&mut store, env, closure_free::<Memory32>),
"environ_get" => Function::new_typed_with_env(&mut store, env, environ_get::<Memory32>),
"environ_sizes_get" => Function::new_typed_with_env(&mut store, env, environ_sizes_get::<Memory32>),
"epoll_create" => Function::new_typed_with_env(&mut store, env, epoll_create::<Memory32>),
Expand Down Expand Up @@ -644,9 +648,13 @@ fn wasix_exports_64(mut store: &mut impl AsStoreMut, env: &FunctionEnv<WasiEnv>)
let namespace = namespace! {
"args_get" => Function::new_typed_with_env(&mut store, env, args_get::<Memory64>),
"args_sizes_get" => Function::new_typed_with_env(&mut store, env, args_sizes_get::<Memory64>),
"call_dynamic" => Function::new_typed_with_env(&mut store, env, call_dynamic::<Memory64>),
"clock_res_get" => Function::new_typed_with_env(&mut store, env, clock_res_get::<Memory64>),
"clock_time_get" => Function::new_typed_with_env(&mut store, env, clock_time_get::<Memory64>),
"clock_time_set" => Function::new_typed_with_env(&mut store, env, clock_time_set::<Memory64>),
"closure_prepare" => Function::new_typed_with_env(&mut store, env, closure_prepare::<Memory64>),
"closure_allocate" => Function::new_typed_with_env(&mut store, env, closure_allocate::<Memory64>),
"closure_free" => Function::new_typed_with_env(&mut store, env, closure_free::<Memory64>),
"environ_get" => Function::new_typed_with_env(&mut store, env, environ_get::<Memory64>),
"environ_sizes_get" => Function::new_typed_with_env(&mut store, env, environ_sizes_get::<Memory64>),
"epoll_create" => Function::new_typed_with_env(&mut store, env, epoll_create::<Memory64>),
Expand Down
47 changes: 46 additions & 1 deletion lib/wasix/src/state/handles/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@ pub(crate) use global::*;
#[cfg(feature = "js")]
pub(crate) use thread_local::*;

use wasmer::{AsStoreRef, Global, Instance, Memory, MemoryView, Module, TypedFunction};
use tracing::trace;
use wasmer::{
AsStoreMut, AsStoreRef, Function, Global, Instance, Memory, MemoryView, Module, Table,
TypedFunction, Value,
};
use wasmer_wasix_types::wasi::Errno;

use super::Linker;

Expand All @@ -22,6 +27,9 @@ pub struct WasiModuleInstanceHandles {
pub(crate) memory: Memory,
pub(crate) instance: wasmer::Instance,

/// Points to the indirect function table
pub(crate) indirect_function_table: Option<Table>,

/// Points to the current location of the memory stack pointer
pub(crate) stack_pointer: Option<Global>,

Expand Down Expand Up @@ -113,6 +121,11 @@ impl WasiModuleInstanceHandles {
.any(|f| f.name() == "stack_checkpoint");
Self {
memory,
indirect_function_table: instance
.exports
.get_table("__indirect_function_table")
.cloned()
.ok(),
stack_pointer: instance.exports.get_global("__stack_pointer").cloned().ok(),
data_end: instance.exports.get_global("__data_end").cloned().ok(),
stack_low: instance.exports.get_global("__stack_low").cloned().ok(),
Expand Down Expand Up @@ -276,4 +289,36 @@ impl WasiModuleTreeHandles {
Self::Dynamic { linker, .. } => Some(linker),
}
}

pub fn main_module_indirect_function_table_lookup(
&self,
store: &mut impl AsStoreMut,
index: u32,
) -> Result<Function, Errno> {
let value = match self {
Self::Static(a) => a
.indirect_function_table
.as_ref()
.ok_or(Errno::Notsup)?
.get(store, index),
Self::Dynamic { linker, .. } => linker
.lookup_indirect_function_table(store, index)
.map_err(|_| Errno::Notsup)?,
};
let Some(value) = value else {
trace!(
function_id = index,
"Function not found in indirect function table"
);
return Err(Errno::Inval);
};
let Value::FuncRef(funcref) = value else {
unreachable!("Function table contains something other than a funcref. At this point this should never happen");
};
let Some(funcref) = funcref else {
trace!(function_id = index, "No function at the supplied index");
return Err(Errno::Inval);
};
Ok(funcref)
}
}
Loading
Loading