1 //! gdbstub `Target` implementation.
2 
3 use crate::Debugger;
4 use crate::addr::AddrSpaceLookup;
5 use crate::api;
6 use gdbstub::arch::lldb::{Encoding, Format, Generic, Register};
7 use gdbstub::common::{Endianness, Pid, Signal, Tid};
8 use gdbstub::target::Target;
9 use gdbstub::target::TargetError;
10 use gdbstub::target::TargetResult;
11 use gdbstub::target::ext::base::BaseOps;
12 use gdbstub::target::ext::base::multithread::{
13     MultiThreadBase, MultiThreadResume, MultiThreadResumeOps, MultiThreadSchedulerLocking,
14     MultiThreadSchedulerLockingOps, MultiThreadSingleStep, MultiThreadSingleStepOps,
15 };
16 use gdbstub::target::ext::base::single_register_access::{
17     SingleRegisterAccess, SingleRegisterAccessOps,
18 };
19 use gdbstub::target::ext::breakpoints::{
20     Breakpoints, BreakpointsOps, SwBreakpoint, SwBreakpointOps,
21 };
22 use gdbstub::target::ext::host_info::{HostInfo, HostInfoOps, HostInfoResponse};
23 use gdbstub::target::ext::libraries::{Libraries, LibrariesOps};
24 use gdbstub::target::ext::lldb_register_info_override::{
25     Callback, CallbackToken, LldbRegisterInfoOverride, LldbRegisterInfoOverrideOps,
26 };
27 use gdbstub::target::ext::memory_map::{MemoryMap, MemoryMapOps};
28 use gdbstub::target::ext::process_info::{ProcessInfo, ProcessInfoOps, ProcessInfoResponse};
29 use gdbstub::target::ext::wasm::{Wasm, WasmOps};
30 use gdbstub_arch::wasm::Wasm as WasmArch;
31 use gdbstub_arch::wasm::addr::WasmAddr;
32 use gdbstub_arch::wasm::reg::WasmRegisters;
33 use gdbstub_arch::wasm::reg::id::WasmRegId;
34 
35 impl<'a> Target for Debugger<'a> {
36     type Arch = WasmArch;
37     type Error = anyhow::Error;
38 
base_ops(&mut self) -> BaseOps<'_, Self::Arch, Self::Error>39     fn base_ops(&mut self) -> BaseOps<'_, Self::Arch, Self::Error> {
40         BaseOps::MultiThread(self)
41     }
42 
support_wasm(&mut self) -> Option<WasmOps<'_, Self>>43     fn support_wasm(&mut self) -> Option<WasmOps<'_, Self>> {
44         Some(self)
45     }
46 
use_lldb_register_info(&self) -> bool47     fn use_lldb_register_info(&self) -> bool {
48         true
49     }
50 
support_lldb_register_info_override( &mut self, ) -> Option<LldbRegisterInfoOverrideOps<'_, Self>>51     fn support_lldb_register_info_override(
52         &mut self,
53     ) -> Option<LldbRegisterInfoOverrideOps<'_, Self>> {
54         Some(self)
55     }
56 
support_breakpoints(&mut self) -> Option<BreakpointsOps<'_, Self>>57     fn support_breakpoints(&mut self) -> Option<BreakpointsOps<'_, Self>> {
58         Some(self)
59     }
60 
support_libraries(&mut self) -> Option<LibrariesOps<'_, Self>>61     fn support_libraries(&mut self) -> Option<LibrariesOps<'_, Self>> {
62         Some(self)
63     }
64 
support_memory_map(&mut self) -> Option<MemoryMapOps<'_, Self>>65     fn support_memory_map(&mut self) -> Option<MemoryMapOps<'_, Self>> {
66         Some(self)
67     }
68 
support_process_info(&mut self) -> Option<ProcessInfoOps<'_, Self>>69     fn support_process_info(&mut self) -> Option<ProcessInfoOps<'_, Self>> {
70         Some(self)
71     }
72 
support_host_info(&mut self) -> Option<HostInfoOps<'_, Self>>73     fn support_host_info(&mut self) -> Option<HostInfoOps<'_, Self>> {
74         Some(self)
75     }
76 }
77 
78 impl<'a> MultiThreadBase for Debugger<'a> {
read_registers(&mut self, regs: &mut WasmRegisters, _tid: Tid) -> TargetResult<(), Self>79     fn read_registers(&mut self, regs: &mut WasmRegisters, _tid: Tid) -> TargetResult<(), Self> {
80         regs.pc = self.current_pc.as_raw();
81         Ok(())
82     }
83 
write_registers(&mut self, regs: &WasmRegisters, _tid: Tid) -> TargetResult<(), Self>84     fn write_registers(&mut self, regs: &WasmRegisters, _tid: Tid) -> TargetResult<(), Self> {
85         self.current_pc = WasmAddr::from_raw(regs.pc).ok_or(TargetError::NonFatal)?;
86         Ok(())
87     }
88 
read_addrs( &mut self, start_addr: u64, data: &mut [u8], _tid: Tid, ) -> TargetResult<usize, Self>89     fn read_addrs(
90         &mut self,
91         start_addr: u64,
92         data: &mut [u8],
93         _tid: Tid,
94     ) -> TargetResult<usize, Self> {
95         let addr = WasmAddr::from_raw(start_addr).ok_or(TargetError::NonFatal)?;
96         let debuggee = self.debuggee;
97         match self.addr_space.lookup(addr, debuggee) {
98             AddrSpaceLookup::Module {
99                 bytecode, offset, ..
100             } => {
101                 let offset = usize::try_from(offset).unwrap();
102                 let avail = bytecode.len() - offset;
103                 let n = avail.min(data.len());
104                 data[..n].copy_from_slice(&bytecode[offset..offset + n]);
105                 Ok(n)
106             }
107             AddrSpaceLookup::Memory { memory, offset } => {
108                 match memory.get_bytes(debuggee, offset.into(), u64::try_from(data.len()).unwrap())
109                 {
110                     Ok(bytes) => {
111                         assert_eq!(bytes.len(), data.len());
112                         data.copy_from_slice(&bytes);
113                         Ok(data.len())
114                     }
115                     Err(_) => Err(TargetError::NonFatal),
116                 }
117             }
118             AddrSpaceLookup::Empty => Err(TargetError::NonFatal),
119         }
120     }
121 
write_addrs(&mut self, _start_addr: u64, _data: &[u8], _tid: Tid) -> TargetResult<(), Self>122     fn write_addrs(&mut self, _start_addr: u64, _data: &[u8], _tid: Tid) -> TargetResult<(), Self> {
123         Err(TargetError::NonFatal)
124     }
125 
list_active_threads( &mut self, thread_is_active: &mut dyn FnMut(Tid), ) -> Result<(), Self::Error>126     fn list_active_threads(
127         &mut self,
128         thread_is_active: &mut dyn FnMut(Tid),
129     ) -> Result<(), Self::Error> {
130         thread_is_active(self.tid);
131         Ok(())
132     }
133 
support_single_register_access(&mut self) -> Option<SingleRegisterAccessOps<'_, Tid, Self>>134     fn support_single_register_access(&mut self) -> Option<SingleRegisterAccessOps<'_, Tid, Self>> {
135         Some(self)
136     }
137 
support_resume(&mut self) -> Option<MultiThreadResumeOps<'_, Self>>138     fn support_resume(&mut self) -> Option<MultiThreadResumeOps<'_, Self>> {
139         Some(self)
140     }
141 }
142 
143 impl<'a> SingleRegisterAccess<Tid> for Debugger<'a> {
read_register( &mut self, _tid: Tid, reg_id: WasmRegId, buf: &mut [u8], ) -> TargetResult<usize, Self>144     fn read_register(
145         &mut self,
146         _tid: Tid,
147         reg_id: WasmRegId,
148         buf: &mut [u8],
149     ) -> TargetResult<usize, Self> {
150         match reg_id {
151             WasmRegId::Pc => {
152                 let bytes = self.current_pc.as_raw().to_le_bytes();
153                 let n = bytes.len().min(buf.len());
154                 buf[..n].copy_from_slice(&bytes[..n]);
155                 Ok(n)
156             }
157             _ => Err(TargetError::NonFatal),
158         }
159     }
160 
write_register( &mut self, _tid: Tid, reg_id: WasmRegId, val: &[u8], ) -> TargetResult<(), Self>161     fn write_register(
162         &mut self,
163         _tid: Tid,
164         reg_id: WasmRegId,
165         val: &[u8],
166     ) -> TargetResult<(), Self> {
167         match reg_id {
168             WasmRegId::Pc => {
169                 if val.len() < 8 {
170                     return Err(TargetError::NonFatal);
171                 }
172                 let raw = u64::from_le_bytes(val[..8].try_into().unwrap());
173                 self.current_pc = WasmAddr::from_raw(raw).ok_or(TargetError::NonFatal)?;
174                 Ok(())
175             }
176             _ => Err(TargetError::NonFatal),
177         }
178     }
179 }
180 
181 impl<'a> MultiThreadResume for Debugger<'a> {
resume(&mut self) -> Result<(), Self::Error>182     fn resume(&mut self) -> Result<(), Self::Error> {
183         self.frame_cache.clear();
184         log::trace!("resume() -> single_stepping = {}", self.single_stepping);
185         if self.single_stepping {
186             self.start_single_step(api::ResumptionValue::Normal);
187         } else {
188             self.start_continue(api::ResumptionValue::Normal);
189         }
190         Ok(())
191     }
192 
clear_resume_actions(&mut self) -> Result<(), Self::Error>193     fn clear_resume_actions(&mut self) -> Result<(), Self::Error> {
194         self.single_stepping = false;
195         Ok(())
196     }
197 
set_resume_action_continue( &mut self, _tid: Tid, _signal: Option<Signal>, ) -> Result<(), Self::Error>198     fn set_resume_action_continue(
199         &mut self,
200         _tid: Tid,
201         _signal: Option<Signal>,
202     ) -> Result<(), Self::Error> {
203         self.single_stepping = false;
204         Ok(())
205     }
206 
support_single_step(&mut self) -> Option<MultiThreadSingleStepOps<'_, Self>>207     fn support_single_step(&mut self) -> Option<MultiThreadSingleStepOps<'_, Self>> {
208         Some(self)
209     }
210 
support_scheduler_locking(&mut self) -> Option<MultiThreadSchedulerLockingOps<'_, Self>>211     fn support_scheduler_locking(&mut self) -> Option<MultiThreadSchedulerLockingOps<'_, Self>> {
212         Some(self)
213     }
214 }
215 
216 impl<'a> MultiThreadSingleStep for Debugger<'a> {
set_resume_action_step( &mut self, _tid: Tid, _signal: Option<Signal>, ) -> Result<(), Self::Error>217     fn set_resume_action_step(
218         &mut self,
219         _tid: Tid,
220         _signal: Option<Signal>,
221     ) -> Result<(), Self::Error> {
222         self.single_stepping = true;
223         Ok(())
224     }
225 }
226 
227 impl<'a> MultiThreadSchedulerLocking for Debugger<'a> {
set_resume_action_scheduler_lock(&mut self) -> Result<(), Self::Error>228     fn set_resume_action_scheduler_lock(&mut self) -> Result<(), Self::Error> {
229         // We have a single thread, so scheduler locking is a no-op.
230         Ok(())
231     }
232 }
233 
234 impl<'a> Breakpoints for Debugger<'a> {
235     #[inline(always)]
support_sw_breakpoint(&mut self) -> Option<SwBreakpointOps<'_, Self>>236     fn support_sw_breakpoint(&mut self) -> Option<SwBreakpointOps<'_, Self>> {
237         Some(self)
238     }
239 }
240 
241 impl<'a> SwBreakpoint for Debugger<'a> {
add_sw_breakpoint(&mut self, addr: u64, _kind: usize) -> TargetResult<bool, Self>242     fn add_sw_breakpoint(&mut self, addr: u64, _kind: usize) -> TargetResult<bool, Self> {
243         let Some(wasm_addr) = WasmAddr::from_raw(addr) else {
244             return Ok(false);
245         };
246         let debuggee = self.debuggee;
247         if let AddrSpaceLookup::Module { module, .. } = self.addr_space.lookup(wasm_addr, debuggee)
248         {
249             module
250                 .add_breakpoint(debuggee, wasm_addr.offset())
251                 .map_err(|_| TargetError::NonFatal)?;
252             Ok(true)
253         } else {
254             Ok(false)
255         }
256     }
257 
remove_sw_breakpoint(&mut self, addr: u64, _kind: usize) -> TargetResult<bool, Self>258     fn remove_sw_breakpoint(&mut self, addr: u64, _kind: usize) -> TargetResult<bool, Self> {
259         let Some(wasm_addr) = WasmAddr::from_raw(addr) else {
260             return Ok(false);
261         };
262         let debuggee = self.debuggee;
263         if let AddrSpaceLookup::Module { module, .. } = self.addr_space.lookup(wasm_addr, debuggee)
264         {
265             module
266                 .remove_breakpoint(debuggee, wasm_addr.offset())
267                 .map_err(|_| TargetError::NonFatal)?;
268             Ok(true)
269         } else {
270             Ok(false)
271         }
272     }
273 }
274 
275 impl<'a> LldbRegisterInfoOverride for Debugger<'a> {
lldb_register_info<'b>( &mut self, reg_id: usize, reg_info: Callback<'b>, ) -> Result<CallbackToken<'b>, Self::Error>276     fn lldb_register_info<'b>(
277         &mut self,
278         reg_id: usize,
279         reg_info: Callback<'b>,
280     ) -> Result<CallbackToken<'b>, Self::Error> {
281         Ok(match reg_id {
282             0 => reg_info.write(Register {
283                 name: "pc",
284                 alt_name: Some("pc"),
285                 bitsize: 64,
286                 offset: 0,
287                 encoding: Encoding::Uint,
288                 format: Format::Hex,
289                 set: "PC",
290                 gcc: Some(16),
291                 dwarf: Some(16),
292                 generic: Some(Generic::Pc),
293                 container_regs: None,
294                 invalidate_regs: None,
295             }),
296             _ => reg_info.done(),
297         })
298     }
299 }
300 
301 impl<'a> Libraries for Debugger<'a> {
get_libraries( &self, offset: u64, length: usize, buf: &mut [u8], ) -> TargetResult<usize, Self>302     fn get_libraries(
303         &self,
304         offset: u64,
305         length: usize,
306         buf: &mut [u8],
307     ) -> TargetResult<usize, Self> {
308         let mut xml = String::from("<library-list>");
309         for (i, addr) in self.addr_space.module_base_addrs().enumerate() {
310             // Each library entry needs a unique name; LLDB's
311             // DynamicLoader deduplicates by name, so using the same
312             // name for all modules causes only the first to be loaded.
313             xml.push_str(&format!(
314                 "<library name=\"wasm-{i}\"><section address=\"{}\"/></library>",
315                 addr.as_raw()
316             ));
317         }
318         xml.push_str("</library-list>");
319 
320         let xml_bytes = xml.as_bytes();
321         let offset = usize::try_from(offset).unwrap();
322         if offset >= xml_bytes.len() {
323             return Ok(0);
324         }
325         let avail = xml_bytes.len() - offset;
326         let n = avail.min(length).min(buf.len());
327         buf[..n].copy_from_slice(&xml_bytes[offset..offset + n]);
328         Ok(n)
329     }
330 }
331 
332 impl<'a> MemoryMap for Debugger<'a> {
memory_map_xml( &self, offset: u64, length: usize, buf: &mut [u8], ) -> TargetResult<usize, Self>333     fn memory_map_xml(
334         &self,
335         offset: u64,
336         length: usize,
337         buf: &mut [u8],
338     ) -> TargetResult<usize, Self> {
339         let xml = self.addr_space.memory_map_xml(self.debuggee);
340         let xml_bytes = xml.as_bytes();
341         let offset = usize::try_from(offset).unwrap();
342         if offset >= xml_bytes.len() {
343             return Ok(0);
344         }
345         let avail = xml_bytes.len() - offset;
346         let n = avail.min(length).min(buf.len());
347         buf[..n].copy_from_slice(&xml_bytes[offset..offset + n]);
348         Ok(n)
349     }
350 }
351 
352 impl<'a> Wasm for Debugger<'a> {
wasm_call_stack(&self, _tid: Tid, callback: &mut dyn FnMut(u64)) -> Result<(), Self::Error>353     fn wasm_call_stack(&self, _tid: Tid, callback: &mut dyn FnMut(u64)) -> Result<(), Self::Error> {
354         let debuggee = self.debuggee;
355         for (i, f) in self.frame_cache.iter().enumerate() {
356             // For non-innermost frames, report the return address
357             // (the instruction after the call) rather than the call
358             // instruction's PC. This matches the standard debugger
359             // convention and is needed for LLDB's `finish` command
360             // to set a breakpoint at the right address.
361             let pc = if i > 0 {
362                 self.addr_space
363                     .frame_to_return_addr(f, debuggee)
364                     .unwrap_or_else(|| self.addr_space.frame_to_pc(f, debuggee))
365             } else {
366                 self.addr_space.frame_to_pc(f, debuggee)
367             };
368             callback(pc.as_raw());
369         }
370         Ok(())
371     }
372 
read_wasm_local( &self, _tid: Tid, frame_depth: usize, index: usize, buf: &mut [u8], ) -> Result<usize, Self::Error>373     fn read_wasm_local(
374         &self,
375         _tid: Tid,
376         frame_depth: usize,
377         index: usize,
378         buf: &mut [u8],
379     ) -> Result<usize, Self::Error> {
380         let Some(f) = self.frame_cache.get(frame_depth) else {
381             return Ok(0);
382         };
383         let Ok(locals) = f.get_locals(self.debuggee) else {
384             return Ok(0);
385         };
386         let Some(val) = locals.get(index) else {
387             return Ok(0);
388         };
389         let bytes = self.value_to_bytes(val.clone());
390         buf[..bytes.len()].copy_from_slice(&bytes);
391         Ok(bytes.len())
392     }
393 
read_wasm_global( &self, _tid: Tid, frame_depth: usize, index: usize, buf: &mut [u8], ) -> Result<usize, Self::Error>394     fn read_wasm_global(
395         &self,
396         _tid: Tid,
397         frame_depth: usize,
398         index: usize,
399         buf: &mut [u8],
400     ) -> Result<usize, Self::Error> {
401         let Some(f) = self.frame_cache.get(frame_depth) else {
402             return Ok(0);
403         };
404         let debuggee = self.debuggee;
405         let Ok(instance) = f.get_instance(debuggee) else {
406             return Ok(0);
407         };
408         let Ok(global) = instance.get_global(debuggee, u32::try_from(index).unwrap()) else {
409             return Ok(0);
410         };
411         let Ok(val) = global.get(debuggee) else {
412             return Ok(0);
413         };
414         let bytes = self.value_to_bytes(val);
415         buf[..bytes.len()].copy_from_slice(&bytes);
416         Ok(bytes.len())
417     }
418 
read_wasm_stack( &self, _tid: Tid, frame_depth: usize, index: usize, buf: &mut [u8], ) -> Result<usize, Self::Error>419     fn read_wasm_stack(
420         &self,
421         _tid: Tid,
422         frame_depth: usize,
423         index: usize,
424         buf: &mut [u8],
425     ) -> Result<usize, Self::Error> {
426         let Some(f) = self.frame_cache.get(frame_depth) else {
427             return Ok(0);
428         };
429         let Ok(stack) = f.get_stack(self.debuggee) else {
430             return Ok(0);
431         };
432         let Some(val) = stack.get(index) else {
433             return Ok(0);
434         };
435         let bytes = self.value_to_bytes(val.clone());
436         buf[..bytes.len()].copy_from_slice(&bytes);
437         Ok(bytes.len())
438     }
439 }
440 
441 impl<'a> HostInfo for Debugger<'a> {
host_info( &self, write_item: &mut dyn FnMut(&HostInfoResponse<'_>), ) -> Result<(), Self::Error>442     fn host_info(
443         &self,
444         write_item: &mut dyn FnMut(&HostInfoResponse<'_>),
445     ) -> Result<(), Self::Error> {
446         write_item(&HostInfoResponse::Triple("wasm32-unknown-unknown-wasm"));
447         write_item(&HostInfoResponse::Endianness(Endianness::Little));
448         write_item(&HostInfoResponse::PointerSize(4));
449         Ok(())
450     }
451 }
452 
453 impl<'a> ProcessInfo for Debugger<'a> {
process_info( &self, write_item: &mut dyn FnMut(&ProcessInfoResponse<'_>), ) -> Result<(), Self::Error>454     fn process_info(
455         &self,
456         write_item: &mut dyn FnMut(&ProcessInfoResponse<'_>),
457     ) -> Result<(), Self::Error> {
458         write_item(&ProcessInfoResponse::Pid(Pid::new(1).unwrap()));
459         write_item(&ProcessInfoResponse::Triple("wasm32-unknown-unknown-wasm"));
460         write_item(&ProcessInfoResponse::Endianness(Endianness::Little));
461         write_item(&ProcessInfoResponse::PointerSize(4));
462         Ok(())
463     }
464 }
465