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