1 //! Synthetic Wasm address space expected by the gdbstub Wasm 2 //! extensions. 3 4 use crate::api::{Debuggee, Frame, Memory, Module}; 5 use anyhow::Result; 6 use gdbstub_arch::wasm::addr::{WasmAddr, WasmAddrType}; 7 use std::collections::{HashMap, hash_map::Entry}; 8 9 /// Representation of the synthesized Wasm address space. 10 pub struct AddrSpace { 11 module_ids: HashMap<u64, u32>, 12 memory_ids: HashMap<u64, u32>, 13 modules: Vec<Module>, 14 module_bytecode: Vec<Vec<u8>>, 15 memories: Vec<Memory>, 16 } 17 18 /// The result of a lookup in the address space. 19 pub enum AddrSpaceLookup<'a> { 20 Module { 21 module: &'a Module, 22 bytecode: &'a [u8], 23 offset: u32, 24 }, 25 Memory { 26 memory: &'a Memory, 27 offset: u32, 28 }, 29 Empty, 30 } 31 32 impl AddrSpace { new() -> Self33 pub fn new() -> Self { 34 AddrSpace { 35 module_ids: HashMap::new(), 36 modules: vec![], 37 module_bytecode: vec![], 38 memory_ids: HashMap::new(), 39 memories: vec![], 40 } 41 } 42 module_id(&mut self, m: &Module) -> u3243 fn module_id(&mut self, m: &Module) -> u32 { 44 match self.module_ids.entry(m.unique_id()) { 45 Entry::Occupied(o) => *o.get(), 46 Entry::Vacant(v) => { 47 let id = u32::try_from(self.modules.len()).unwrap(); 48 let bytecode = m.bytecode().unwrap_or(vec![]); 49 self.module_bytecode.push(bytecode); 50 self.modules.push(m.clone()); 51 *v.insert(id) 52 } 53 } 54 } 55 memory_id(&mut self, m: &Memory) -> u3256 fn memory_id(&mut self, m: &Memory) -> u32 { 57 match self.memory_ids.entry(m.unique_id()) { 58 Entry::Occupied(o) => *o.get(), 59 Entry::Vacant(v) => { 60 let id = u32::try_from(self.memories.len()).unwrap(); 61 self.memories.push(m.clone()); 62 *v.insert(id) 63 } 64 } 65 } 66 67 /// Update/create new mappings so that all modules and instances' 68 /// memories in the debuggee have mappings. update(&mut self, d: &Debuggee) -> Result<()>69 pub fn update(&mut self, d: &Debuggee) -> Result<()> { 70 for module in d.all_modules() { 71 let _ = self.module_id(&module); 72 } 73 for instance in d.all_instances() { 74 let mut idx = 0; 75 loop { 76 if let Ok(m) = instance.get_memory(d, idx) { 77 let _ = self.memory_id(&m); 78 idx += 1; 79 } else { 80 break; 81 } 82 } 83 } 84 Ok(()) 85 } 86 87 /// Iterate over the base `WasmAddr` of every registered module. module_base_addrs(&self) -> impl Iterator<Item = WasmAddr> + '_88 pub fn module_base_addrs(&self) -> impl Iterator<Item = WasmAddr> + '_ { 89 (0..self.modules.len()) 90 .map(|idx| WasmAddr::new(WasmAddrType::Object, u32::try_from(idx).unwrap(), 0).unwrap()) 91 } 92 93 /// Build the GDB memory-map XML describing all known regions. 94 /// 95 /// Module bytecode regions are reported as `rom` (read-only), and 96 /// linear memories as `ram` (read-write). memory_map_xml(&self, debuggee: &Debuggee) -> String97 pub fn memory_map_xml(&self, debuggee: &Debuggee) -> String { 98 use std::fmt::Write; 99 let mut xml = String::from( 100 "<?xml version=\"1.0\"?><!DOCTYPE memory-map SYSTEM \"memory-map.dtd\"><memory-map>", 101 ); 102 for (idx, bc) in self.module_bytecode.iter().enumerate() { 103 let start = 104 WasmAddr::new(WasmAddrType::Object, u32::try_from(idx).unwrap(), 0).unwrap(); 105 let len = bc.len(); 106 if len > 0 { 107 write!( 108 xml, 109 "<memory type=\"rom\" start=\"0x{:x}\" length=\"0x{:x}\"/>", 110 start.as_raw(), 111 len 112 ) 113 .unwrap(); 114 } 115 } 116 for (idx, mem) in self.memories.iter().enumerate() { 117 let start = 118 WasmAddr::new(WasmAddrType::Memory, u32::try_from(idx).unwrap(), 0).unwrap(); 119 let len = mem.size_bytes(debuggee); 120 if len > 0 { 121 write!( 122 xml, 123 "<memory type=\"ram\" start=\"0x{:x}\" length=\"0x{:x}\"/>", 124 start.as_raw(), 125 len 126 ) 127 .unwrap(); 128 } 129 } 130 xml.push_str("</memory-map>"); 131 xml 132 } 133 frame_to_pc(&self, frame: &Frame, debuggee: &Debuggee) -> WasmAddr134 pub fn frame_to_pc(&self, frame: &Frame, debuggee: &Debuggee) -> WasmAddr { 135 let module = frame.get_instance(debuggee).unwrap().get_module(debuggee); 136 let &module_id = self 137 .module_ids 138 .get(&module.unique_id()) 139 .expect("module not found in addr space"); 140 let pc = frame.get_pc(debuggee).unwrap(); 141 WasmAddr::new(WasmAddrType::Object, module_id, pc).unwrap() 142 } 143 frame_to_return_addr(&self, frame: &Frame, debuggee: &Debuggee) -> Option<WasmAddr>144 pub fn frame_to_return_addr(&self, frame: &Frame, debuggee: &Debuggee) -> Option<WasmAddr> { 145 let module = frame.get_instance(debuggee).unwrap().get_module(debuggee); 146 let &module_id = self 147 .module_ids 148 .get(&module.unique_id()) 149 .expect("module not found in addr space"); 150 let ret_pc = frame.get_pc(debuggee).ok()?; 151 Some(WasmAddr::new(WasmAddrType::Object, module_id, ret_pc).unwrap()) 152 } 153 lookup(&self, addr: WasmAddr, d: &Debuggee) -> AddrSpaceLookup<'_>154 pub fn lookup(&self, addr: WasmAddr, d: &Debuggee) -> AddrSpaceLookup<'_> { 155 let index = usize::try_from(addr.module_index()).unwrap(); 156 match addr.addr_type() { 157 WasmAddrType::Object => { 158 if index >= self.modules.len() { 159 return AddrSpaceLookup::Empty; 160 } 161 let bytecode = &self.module_bytecode[index]; 162 if addr.offset() >= u32::try_from(bytecode.len()).unwrap() { 163 return AddrSpaceLookup::Empty; 164 } 165 AddrSpaceLookup::Module { 166 module: &self.modules[index], 167 bytecode, 168 offset: addr.offset(), 169 } 170 } 171 WasmAddrType::Memory => { 172 if index >= self.memories.len() { 173 return AddrSpaceLookup::Empty; 174 } 175 let size = self.memories[index].size_bytes(d); 176 if u64::from(addr.offset()) >= size { 177 return AddrSpaceLookup::Empty; 178 } 179 AddrSpaceLookup::Memory { 180 memory: &self.memories[index], 181 offset: addr.offset(), 182 } 183 } 184 } 185 } 186 } 187