Next Generation WASM Microkernel Operating System
1// Copyright 2025 Jonas Kruckenberg
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8use crate::wasm::Engine;
9use crate::wasm::code_registry::{register_code, unregister_code};
10use crate::wasm::compile::{CompileInputs, CompiledFunctionInfo};
11use crate::wasm::indices::{DefinedFuncIndex, EntityIndex, VMSharedTypeIndex};
12use crate::wasm::translate::{Import, ModuleTranslator, TranslatedModule};
13use crate::wasm::type_registry::RuntimeTypeCollection;
14use crate::wasm::utils::u8_size_of;
15use crate::wasm::vm::{CodeObject, MmapVec, VMArrayCallFunction, VMShape, VMWasmCallFunction};
16use alloc::string::{String, ToString};
17use alloc::sync::Arc;
18use core::mem;
19use core::ops::DerefMut;
20use core::ptr::NonNull;
21use wasmparser::{Validator, WasmFeatures};
22
23/// A compiled WebAssembly module, ready to be instantiated.
24///
25/// It holds all compiled code as well as the module's type information and other metadata (e.g. for
26/// trap handling and backtrace information).
27///
28/// Currently, no form of dynamic tiering is implemented instead all functions are compiled synchronously
29/// when the module is created. This is expected to change though.
30#[derive(Debug, Clone)]
31pub struct Module(Arc<ModuleInner>);
32
33#[derive(Debug)]
34struct ModuleInner {
35 name: String,
36 engine: Engine,
37 translated_module: TranslatedModule,
38 required_features: WasmFeatures,
39 vmshape: VMShape,
40 code: Arc<CodeObject>,
41 type_collection: RuntimeTypeCollection,
42}
43
44impl Drop for ModuleInner {
45 fn drop(&mut self) {
46 tracing::warn!("Dropping wasm module {}", self.name);
47 unregister_code(&self.code);
48 }
49}
50
51impl Module {
52 pub fn from_bytes(
53 engine: &Engine,
54 validator: &mut Validator,
55 bytes: &[u8],
56 ) -> crate::Result<Self> {
57 let (mut translation, types) = ModuleTranslator::new(validator).translate(bytes)?;
58
59 tracing::debug!("Gathering compile inputs...");
60 let function_body_data = mem::take(&mut translation.function_bodies);
61 let inputs = CompileInputs::from_module(&translation, &types, function_body_data);
62
63 tracing::debug!("Compiling inputs...");
64 let unlinked_outputs = inputs.compile(engine.compiler())?;
65
66 let type_collection = engine.type_registry().register_module_types(engine, types);
67
68 let code = crate::mem::with_kernel_aspace(|aspace| -> crate::Result<_> {
69 tracing::debug!("Applying static relocations...");
70 let mut code =
71 unlinked_outputs.link_and_finish(engine, &translation.module, |code| {
72 tracing::debug!("Allocating new memory map...");
73 MmapVec::from_slice(aspace.clone(), &code)
74 })?;
75
76 code.publish(aspace.lock().deref_mut())?;
77 Ok(Arc::new(code))
78 })?;
79
80 // register this code memory with the trap handler, so we can correctly unwind from traps
81 register_code(&code);
82
83 Ok(Self(Arc::new(ModuleInner {
84 name: translation
85 .module
86 .name
87 .clone()
88 .unwrap_or("<unnamed mystery module>".to_string()),
89 engine: engine.clone(),
90 vmshape: VMShape::for_module(u8_size_of::<*mut u8>(), &translation.module),
91 translated_module: translation.module,
92 required_features: translation.required_features,
93 code,
94 type_collection,
95 })))
96 }
97
98 /// Returns the modules name if present.
99 pub fn name(&self) -> Option<&str> {
100 self.translated().name.as_deref()
101 }
102
103 /// Returns the modules imports.
104 pub fn imports(&self) -> impl ExactSizeIterator<Item = &Import> {
105 self.translated().imports.iter()
106 }
107
108 /// Returns the modules exports.
109 pub fn exports(&self) -> impl ExactSizeIterator<Item = (&str, EntityIndex)> + '_ {
110 self.translated()
111 .exports
112 .iter()
113 .map(|(name, index)| (name.as_str(), *index))
114 }
115
116 pub(super) fn required_features(&self) -> WasmFeatures {
117 self.0.required_features
118 }
119 pub(super) fn engine(&self) -> &Engine {
120 &self.0.engine
121 }
122 pub(super) fn translated(&self) -> &TranslatedModule {
123 &self.0.translated_module
124 }
125 pub(super) fn vmshape(&self) -> &VMShape {
126 &self.0.vmshape
127 }
128 pub(crate) fn code(&self) -> &Arc<CodeObject> {
129 &self.0.code
130 }
131 pub(crate) fn type_collection(&self) -> &RuntimeTypeCollection {
132 &self.0.type_collection
133 }
134 pub(crate) fn type_ids(&self) -> &[VMSharedTypeIndex] {
135 self.0.type_collection.type_map().values().as_slice()
136 }
137 pub(crate) fn functions(
138 &self,
139 ) -> cranelift_entity::Iter<DefinedFuncIndex, CompiledFunctionInfo> {
140 self.0.code.function_info().iter()
141 }
142
143 pub(super) fn array_to_wasm_trampoline(
144 &self,
145 index: DefinedFuncIndex,
146 ) -> Option<VMArrayCallFunction> {
147 let loc = self.0.code.function_info()[index].array_to_wasm_trampoline?;
148 let raw = self.code().resolve_function_loc(loc);
149 // Safety: TODO
150 Some(unsafe { mem::transmute::<usize, VMArrayCallFunction>(raw) })
151 }
152
153 /// Return the address, in memory, of the trampoline that allows Wasm to
154 /// call a array function of the given signature.
155 pub(super) fn wasm_to_array_trampoline(
156 &self,
157 signature: VMSharedTypeIndex,
158 ) -> Option<NonNull<VMWasmCallFunction>> {
159 let trampoline_shared_ty = self.0.engine.type_registry().get_trampoline_type(signature);
160 let trampoline_module_ty = self
161 .0
162 .type_collection
163 .trampoline_type(trampoline_shared_ty)?;
164
165 debug_assert!(
166 self.0
167 .engine
168 .type_registry()
169 .borrow(
170 self.0
171 .type_collection
172 .lookup_shared_type(trampoline_module_ty)
173 .unwrap()
174 )
175 .unwrap()
176 .unwrap_func()
177 .is_trampoline_type()
178 );
179
180 let ptr = self.code().wasm_to_host_trampoline(trampoline_module_ty);
181
182 Some(ptr)
183 }
184
185 pub(super) fn function(&self, index: DefinedFuncIndex) -> NonNull<VMWasmCallFunction> {
186 let loc = self.0.code.function_info()[index].wasm_func_loc;
187 NonNull::new(self.code().resolve_function_loc(loc) as *mut VMWasmCallFunction).unwrap()
188 }
189
190 pub(super) fn same(&self, other: &Self) -> bool {
191 Arc::ptr_eq(&self.0, &other.0)
192 }
193
194 pub(super) fn new_stub(engine: Engine) -> Self {
195 let translated_module = TranslatedModule::default();
196 Self(Arc::new(ModuleInner {
197 name: "<Stub>".to_string(),
198 engine: engine.clone(),
199 vmshape: VMShape::for_module(u8_size_of::<usize>(), &translated_module),
200 translated_module,
201 required_features: WasmFeatures::default(),
202 code: Arc::new(CodeObject::empty()),
203 type_collection: RuntimeTypeCollection::empty(engine),
204 }))
205 }
206}