wasmtime/runtime/module/
registry.rs1use crate::code::CodeObject;
4#[cfg(feature = "component-model")]
5use crate::component::Component;
6use crate::prelude::*;
7use crate::runtime::vm::VMWasmCallFunction;
8use crate::sync::{OnceLock, RwLock};
9use crate::{code_memory::CodeMemory, FrameInfo, Module};
10use alloc::collections::btree_map::{BTreeMap, Entry};
11use alloc::sync::Arc;
12use core::ptr::NonNull;
13use wasmtime_environ::VMSharedTypeIndex;
14
15#[derive(Default)]
24pub struct ModuleRegistry {
25 loaded_code: BTreeMap<usize, (usize, LoadedCode)>,
30
31 modules_without_code: Vec<Module>,
33}
34
35struct LoadedCode {
36 _code: Arc<CodeObject>,
39
40 modules: BTreeMap<usize, Module>,
43}
44
45#[derive(Clone, Copy)]
48pub enum RegisteredModuleId {
49 WithoutCode(usize),
51 LoadedCode(usize),
54}
55
56impl ModuleRegistry {
57 pub fn lookup_module_by_id(&self, id: RegisteredModuleId) -> Option<&Module> {
59 match id {
60 RegisteredModuleId::WithoutCode(idx) => self.modules_without_code.get(idx),
61 RegisteredModuleId::LoadedCode(pc) => {
62 let (module, _) = self.module_and_offset(pc)?;
63 Some(module)
64 }
65 }
66 }
67
68 #[cfg(feature = "gc")]
70 pub fn lookup_module_by_pc(&self, pc: usize) -> Option<&Module> {
71 let (module, _) = self.module_and_offset(pc)?;
72 Some(module)
73 }
74
75 fn code(&self, pc: usize) -> Option<(&LoadedCode, usize)> {
76 let (end, (start, code)) = self.loaded_code.range(pc..).next()?;
77 if pc < *start || *end < pc {
78 return None;
79 }
80 Some((code, pc - *start))
81 }
82
83 fn module_and_offset(&self, pc: usize) -> Option<(&Module, usize)> {
84 let (code, offset) = self.code(pc)?;
85 Some((code.module(pc)?, offset))
86 }
87
88 #[cfg(feature = "coredump")]
90 pub fn all_modules(&self) -> impl Iterator<Item = &'_ Module> + '_ {
91 self.loaded_code
92 .values()
93 .flat_map(|(_, code)| code.modules.values())
94 .chain(self.modules_without_code.iter())
95 }
96
97 pub fn register_module(&mut self, module: &Module) -> RegisteredModuleId {
99 self.register(module.code_object(), Some(module)).unwrap()
100 }
101
102 #[cfg(feature = "component-model")]
103 pub fn register_component(&mut self, component: &Component) {
104 self.register(component.code_object(), None);
105 }
106
107 fn register(
109 &mut self,
110 code: &Arc<CodeObject>,
111 module: Option<&Module>,
112 ) -> Option<RegisteredModuleId> {
113 let text = code.code_memory().text();
114
115 if text.is_empty() {
122 return module.map(|module| {
123 let id = RegisteredModuleId::WithoutCode(self.modules_without_code.len());
124 self.modules_without_code.push(module.clone());
125 id
126 });
127 }
128
129 let start_addr = text.as_ptr() as usize;
132 let end_addr = start_addr + text.len() - 1;
133 let id = module.map(|_| RegisteredModuleId::LoadedCode(start_addr));
134
135 if let Some((other_start, prev)) = self.loaded_code.get_mut(&end_addr) {
140 assert_eq!(*other_start, start_addr);
141 if let Some(module) = module {
142 prev.push_module(module);
143 }
144 return id;
145 }
146
147 if let Some((_, (prev_start, _))) = self.loaded_code.range(start_addr..).next() {
150 assert!(*prev_start > end_addr);
151 }
152 if let Some((prev_end, _)) = self.loaded_code.range(..=start_addr).next_back() {
153 assert!(*prev_end < start_addr);
154 }
155
156 let mut item = LoadedCode {
157 _code: code.clone(),
158 modules: Default::default(),
159 };
160 if let Some(module) = module {
161 item.push_module(module);
162 }
163 let prev = self.loaded_code.insert(end_addr, (start_addr, item));
164 assert!(prev.is_none());
165 id
166 }
167
168 pub(crate) fn lookup_frame_info(&self, pc: usize) -> Option<(FrameInfo, &Module)> {
177 let (module, offset) = self.module_and_offset(pc)?;
178 let info = FrameInfo::new(module.clone(), offset)?;
179 Some((info, module))
180 }
181
182 pub fn wasm_to_array_trampoline(
183 &self,
184 sig: VMSharedTypeIndex,
185 ) -> Option<NonNull<VMWasmCallFunction>> {
186 for (_, code) in self.loaded_code.values() {
194 for module in code.modules.values() {
195 if let Some(trampoline) = module.wasm_to_array_trampoline(sig) {
196 return Some(trampoline);
197 }
198 }
199 }
200 None
201 }
202}
203
204impl LoadedCode {
205 fn push_module(&mut self, module: &Module) {
206 let func = match module.compiled_module().finished_functions().next() {
207 Some((_, func)) => func,
208 None => return,
213 };
214 let start = func.as_ptr() as usize;
215
216 match self.modules.entry(start) {
217 Entry::Occupied(m) => {
220 debug_assert!(Arc::ptr_eq(&module.inner, &m.get().inner));
221 }
222 Entry::Vacant(v) => {
224 v.insert(module.clone());
225 }
226 }
227 }
228
229 fn module(&self, pc: usize) -> Option<&Module> {
230 let (_start, module) = self.modules.range(..=pc).next_back()?;
235 Some(module)
236 }
237}
238
239fn global_code() -> &'static RwLock<GlobalRegistry> {
253 static GLOBAL_CODE: OnceLock<RwLock<GlobalRegistry>> = OnceLock::new();
254 GLOBAL_CODE.get_or_init(Default::default)
255}
256
257type GlobalRegistry = BTreeMap<usize, (usize, Arc<CodeMemory>)>;
258
259pub fn lookup_code(pc: usize) -> Option<(Arc<CodeMemory>, usize)> {
262 let all_modules = global_code().read();
263 let (_end, (start, module)) = all_modules.range(pc..).next()?;
264 let text_offset = pc.checked_sub(*start)?;
265 Some((module.clone(), text_offset))
266}
267
268pub fn register_code(code: &Arc<CodeMemory>) {
277 let text = code.text();
278 if text.is_empty() {
279 return;
280 }
281 let start = text.as_ptr() as usize;
282 let end = start + text.len() - 1;
283 let prev = global_code().write().insert(end, (start, code.clone()));
284 assert!(prev.is_none());
285}
286
287pub fn unregister_code(code: &Arc<CodeMemory>) {
291 let text = code.text();
292 if text.is_empty() {
293 return;
294 }
295 let end = (text.as_ptr() as usize) + text.len() - 1;
296 let code = global_code().write().remove(&end);
297 assert!(code.is_some());
298}
299
300#[test]
301#[cfg_attr(miri, ignore)]
302fn test_frame_info() -> Result<(), anyhow::Error> {
303 use crate::*;
304
305 let mut store = Store::<()>::default();
306 let module = Module::new(
307 store.engine(),
308 r#"
309 (module
310 (func (export "add") (param $x i32) (param $y i32) (result i32) (i32.add (local.get $x) (local.get $y)))
311 (func (export "sub") (param $x i32) (param $y i32) (result i32) (i32.sub (local.get $x) (local.get $y)))
312 (func (export "mul") (param $x i32) (param $y i32) (result i32) (i32.mul (local.get $x) (local.get $y)))
313 (func (export "div_s") (param $x i32) (param $y i32) (result i32) (i32.div_s (local.get $x) (local.get $y)))
314 (func (export "div_u") (param $x i32) (param $y i32) (result i32) (i32.div_u (local.get $x) (local.get $y)))
315 (func (export "rem_s") (param $x i32) (param $y i32) (result i32) (i32.rem_s (local.get $x) (local.get $y)))
316 (func (export "rem_u") (param $x i32) (param $y i32) (result i32) (i32.rem_u (local.get $x) (local.get $y)))
317 )
318 "#,
319 )?;
320 Instance::new(&mut store, &module, &[])?;
322
323 for (i, alloc) in module.compiled_module().finished_functions() {
324 let (start, end) = {
325 let ptr = alloc.as_ptr();
326 let len = alloc.len();
327 (ptr as usize, ptr as usize + len)
328 };
329 for pc in start..end {
330 let (frame, _) = store
331 .as_context()
332 .0
333 .modules()
334 .lookup_frame_info(pc)
335 .unwrap();
336 assert!(
337 frame.func_index() == i.as_u32(),
338 "lookup of {:#x} returned {}, expected {}",
339 pc,
340 frame.func_index(),
341 i.as_u32()
342 );
343 }
344 }
345 Ok(())
346}