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