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::mem::{AddressSpace, Mmap, VirtualAddress};
9use crate::wasm::TrapKind;
10use crate::wasm::compile::{CompiledFunctionInfo, FunctionLoc};
11use crate::wasm::indices::{DefinedFuncIndex, ModuleInternedTypeIndex};
12use crate::wasm::vm::{MmapVec, VMWasmCallFunction};
13use alloc::vec;
14use alloc::vec::Vec;
15use anyhow::Context;
16use core::ptr::NonNull;
17use core::range::Range;
18use core::slice;
19use cranelift_entity::PrimaryMap;
20
21#[derive(Debug)]
22pub struct CodeObject {
23 mmap: Mmap,
24 len: usize,
25 published: bool,
26
27 trap_offsets: Vec<u32>,
28 traps: Vec<TrapKind>,
29 wasm_to_host_trampolines: Vec<(ModuleInternedTypeIndex, FunctionLoc)>,
30 function_info: PrimaryMap<DefinedFuncIndex, CompiledFunctionInfo>,
31}
32
33impl CodeObject {
34 pub fn empty() -> Self {
35 Self {
36 mmap: Mmap::new_empty(),
37 len: 0,
38 published: false,
39 trap_offsets: vec![],
40 traps: vec![],
41 wasm_to_host_trampolines: vec![],
42 function_info: PrimaryMap::new(),
43 }
44 }
45
46 pub fn new(
47 mmap_vec: MmapVec<u8>,
48 trap_offsets: Vec<u32>,
49 traps: Vec<TrapKind>,
50 wasm_to_host_trampolines: Vec<(ModuleInternedTypeIndex, FunctionLoc)>,
51 function_info: PrimaryMap<DefinedFuncIndex, CompiledFunctionInfo>,
52 ) -> Self {
53 let (mmap, size) = mmap_vec.into_parts();
54 Self {
55 mmap,
56 len: size,
57 published: false,
58 trap_offsets,
59 traps,
60 wasm_to_host_trampolines,
61 function_info,
62 }
63 }
64
65 pub fn publish(&mut self, aspace: &mut AddressSpace) -> crate::Result<()> {
66 debug_assert!(!self.published);
67 self.published = true;
68
69 if self.mmap.is_empty() {
70 tracing::warn!("Compiled module has no code to publish");
71 return Ok(());
72 }
73
74 // Switch the executable portion from readonly to read/execute.
75 self.mmap
76 .make_executable(aspace, true)
77 .context("Failed to mark mmap'ed region as executable")?;
78
79 Ok(())
80 }
81
82 pub fn text(&self) -> &[u8] {
83 let base = self.mmap.as_ptr();
84 if base.is_null() {
85 &[]
86 } else {
87 // Safety: we have checked the slice is valid (both above and through construction)
88 unsafe { slice::from_raw_parts(self.mmap.as_ptr(), self.len) }
89 }
90 }
91
92 #[inline]
93 pub fn text_range(&self) -> Range<VirtualAddress> {
94 let start = self.mmap.range().start;
95
96 Range::from(start..start.checked_add(self.len).unwrap())
97 }
98
99 pub fn resolve_function_loc(&self, func_loc: FunctionLoc) -> usize {
100 let text_range = self.text_range();
101 let addr = text_range.start.get() + func_loc.start as usize;
102
103 tracing::trace!(
104 "resolve_function_loc {func_loc:?}, text {:?} => {:?}",
105 self.mmap.as_ptr(),
106 addr,
107 );
108
109 // Assert the function location actually lies in our text section
110 debug_assert!(
111 text_range.start.get() <= addr
112 && text_range.end.get()
113 >= addr.saturating_add(usize::try_from(func_loc.length).unwrap())
114 );
115
116 addr
117 }
118
119 pub fn lookup_trap_code(&self, text_offset: usize) -> Option<TrapKind> {
120 let text_offset = u32::try_from(text_offset).unwrap();
121
122 let index = self
123 .trap_offsets
124 .binary_search_by_key(&text_offset, |val| *val)
125 .ok()?;
126
127 Some(self.traps[index])
128 }
129
130 pub(crate) fn function_info(&self) -> &PrimaryMap<DefinedFuncIndex, CompiledFunctionInfo> {
131 &self.function_info
132 }
133
134 pub fn wasm_to_host_trampoline(
135 &self,
136 sig: ModuleInternedTypeIndex,
137 ) -> NonNull<VMWasmCallFunction> {
138 let Ok(idx) = self
139 .wasm_to_host_trampolines
140 .binary_search_by_key(&sig, |entry| entry.0)
141 else {
142 panic!("missing trampoline for {sig:?}")
143 };
144
145 let (_, loc) = self.wasm_to_host_trampolines[idx];
146
147 NonNull::new(self.resolve_function_loc(loc) as *mut VMWasmCallFunction).unwrap()
148 }
149}