wasmtime/module/
registry.rs1use crate::code::CodeObject;
4#[cfg(feature = "component-model")]
5use crate::component::Component;
6use crate::{FrameInfo, Module, Trap};
7use once_cell::sync::Lazy;
8use std::collections::btree_map::Entry;
9use std::{
10 collections::BTreeMap,
11 sync::{Arc, RwLock},
12};
13use wasmtime_jit::CodeMemory;
14use wasmtime_runtime::{ModuleInfo, VMCallerCheckedFuncRef, VMTrampoline};
15
16#[derive(Default)]
25pub struct ModuleRegistry {
26 loaded_code: BTreeMap<usize, (usize, LoadedCode)>,
31
32 modules_without_code: Vec<Module>,
34}
35
36struct LoadedCode {
37 code: Arc<CodeObject>,
40
41 modules: BTreeMap<usize, Module>,
44}
45
46impl ModuleRegistry {
47 pub fn lookup_module(&self, pc: usize) -> Option<&dyn ModuleInfo> {
49 self.module(pc).map(|(m, _)| m.module_info())
50 }
51
52 fn code(&self, pc: usize) -> Option<(&LoadedCode, usize)> {
53 let (end, (start, code)) = self.loaded_code.range(pc..).next()?;
54 if pc < *start || *end < pc {
55 return None;
56 }
57 Some((code, pc - *start))
58 }
59
60 fn module(&self, pc: usize) -> Option<(&Module, usize)> {
61 let (code, offset) = self.code(pc)?;
62 Some((code.module(pc)?, offset))
63 }
64
65 pub fn register_module(&mut self, module: &Module) {
67 self.register(module.code_object(), Some(module))
68 }
69
70 #[cfg(feature = "component-model")]
71 pub fn register_component(&mut self, component: &Component) {
72 self.register(component.code_object(), None)
73 }
74
75 fn register(&mut self, code: &Arc<CodeObject>, module: Option<&Module>) {
77 let text = code.code_memory().text();
78
79 if text.is_empty() {
86 self.modules_without_code.extend(module.cloned());
87 return;
88 }
89
90 let start_addr = text.as_ptr() as usize;
93 let end_addr = start_addr + text.len() - 1;
94
95 if let Some((other_start, prev)) = self.loaded_code.get_mut(&end_addr) {
100 assert_eq!(*other_start, start_addr);
101 if let Some(module) = module {
102 prev.push_module(module);
103 }
104 return;
105 }
106
107 if let Some((_, (prev_start, _))) = self.loaded_code.range(start_addr..).next() {
110 assert!(*prev_start > end_addr);
111 }
112 if let Some((prev_end, _)) = self.loaded_code.range(..=start_addr).next_back() {
113 assert!(*prev_end < start_addr);
114 }
115
116 let mut item = LoadedCode {
117 code: code.clone(),
118 modules: Default::default(),
119 };
120 if let Some(module) = module {
121 item.push_module(module);
122 }
123 let prev = self.loaded_code.insert(end_addr, (start_addr, item));
124 assert!(prev.is_none());
125 }
126
127 pub fn lookup_trampoline(&self, anyfunc: &VMCallerCheckedFuncRef) -> Option<VMTrampoline> {
129 let (code, _offset) = self.code(anyfunc.func_ptr.as_ptr() as usize)?;
130 code.code.signatures().trampoline(anyfunc.type_index)
131 }
132
133 pub fn lookup_trap_code(&self, pc: usize) -> Option<Trap> {
135 let (code, offset) = self.code(pc)?;
136 wasmtime_environ::lookup_trap_code(code.code.code_memory().trap_data(), offset)
137 }
138
139 pub(crate) fn lookup_frame_info(&self, pc: usize) -> Option<(FrameInfo, &Module)> {
148 let (module, offset) = self.module(pc)?;
149 let info = FrameInfo::new(module, offset)?;
150 Some((info, module))
151 }
152}
153
154impl LoadedCode {
155 fn push_module(&mut self, module: &Module) {
156 let func = match module.compiled_module().finished_functions().next() {
157 Some((_, func)) => func,
158 None => return,
163 };
164 let start = func.as_ptr() as usize;
165
166 match self.modules.entry(start) {
167 Entry::Occupied(m) => {
170 debug_assert!(Arc::ptr_eq(&module.inner, &m.get().inner));
171 }
172 Entry::Vacant(v) => {
174 v.insert(module.clone());
175 }
176 }
177 }
178
179 fn module(&self, pc: usize) -> Option<&Module> {
180 let (_start, module) = self.modules.range(..=pc).next_back()?;
185 Some(module)
186 }
187}
188
189static GLOBAL_CODE: Lazy<RwLock<GlobalRegistry>> = Lazy::new(Default::default);
203
204type GlobalRegistry = BTreeMap<usize, (usize, Arc<CodeMemory>)>;
205
206pub fn is_wasm_trap_pc(pc: usize) -> bool {
209 let (code, text_offset) = {
210 let all_modules = GLOBAL_CODE.read().unwrap();
211
212 let (end, (start, module)) = match all_modules.range(pc..).next() {
213 Some(info) => info,
214 None => return false,
215 };
216 if pc < *start || *end < pc {
217 return false;
218 }
219 (module.clone(), pc - *start)
220 };
221
222 wasmtime_environ::lookup_trap_code(code.trap_data(), text_offset).is_some()
223}
224
225pub fn register_code(code: &Arc<CodeMemory>) {
234 let text = code.text();
235 if text.is_empty() {
236 return;
237 }
238 let start = text.as_ptr() as usize;
239 let end = start + text.len() - 1;
240 let prev = GLOBAL_CODE
241 .write()
242 .unwrap()
243 .insert(end, (start, code.clone()));
244 assert!(prev.is_none());
245}
246
247pub fn unregister_code(code: &Arc<CodeMemory>) {
251 let text = code.text();
252 if text.is_empty() {
253 return;
254 }
255 let end = (text.as_ptr() as usize) + text.len() - 1;
256 let code = GLOBAL_CODE.write().unwrap().remove(&end);
257 assert!(code.is_some());
258}
259
260#[test]
261fn test_frame_info() -> Result<(), anyhow::Error> {
262 use crate::*;
263 let mut store = Store::<()>::default();
264 let module = Module::new(
265 store.engine(),
266 r#"
267 (module
268 (func (export "add") (param $x i32) (param $y i32) (result i32) (i32.add (local.get $x) (local.get $y)))
269 (func (export "sub") (param $x i32) (param $y i32) (result i32) (i32.sub (local.get $x) (local.get $y)))
270 (func (export "mul") (param $x i32) (param $y i32) (result i32) (i32.mul (local.get $x) (local.get $y)))
271 (func (export "div_s") (param $x i32) (param $y i32) (result i32) (i32.div_s (local.get $x) (local.get $y)))
272 (func (export "div_u") (param $x i32) (param $y i32) (result i32) (i32.div_u (local.get $x) (local.get $y)))
273 (func (export "rem_s") (param $x i32) (param $y i32) (result i32) (i32.rem_s (local.get $x) (local.get $y)))
274 (func (export "rem_u") (param $x i32) (param $y i32) (result i32) (i32.rem_u (local.get $x) (local.get $y)))
275 )
276 "#,
277 )?;
278 Instance::new(&mut store, &module, &[])?;
280
281 for (i, alloc) in module.compiled_module().finished_functions() {
282 let (start, end) = {
283 let ptr = alloc.as_ptr();
284 let len = alloc.len();
285 (ptr as usize, ptr as usize + len)
286 };
287 for pc in start..end {
288 let (frame, _) = store
289 .as_context()
290 .0
291 .modules()
292 .lookup_frame_info(pc)
293 .unwrap();
294 assert!(
295 frame.func_index() == i.as_u32(),
296 "lookup of {:#x} returned {}, expected {}",
297 pc,
298 frame.func_index(),
299 i.as_u32()
300 );
301 }
302 }
303 Ok(())
304}