• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! Base debugging operations for multi threaded targets.
2 
3 use crate::arch::Arch;
4 use crate::common::*;
5 use crate::target::ext::breakpoints::WatchKind;
6 use crate::target::{Target, TargetResult};
7 
8 use super::{ReplayLogPosition, SingleRegisterAccessOps};
9 
10 // Convenient re-exports
11 pub use super::{GdbInterrupt, ResumeAction};
12 
13 /// Base debugging operations for multi threaded targets.
14 #[allow(clippy::type_complexity)]
15 pub trait MultiThreadOps: Target {
16     /// Resume execution on the target.
17     ///
18     /// Prior to calling `resume`, `gdbstub` will call `clear_resume_actions`,
19     /// followed by zero or more calls to `set_resume_action`, specifying any
20     /// thread-specific resume actions.
21     ///
22     /// The `default_action` parameter specifies the "fallback" resume action
23     /// for any threads that did not have a specific resume action set via
24     /// `set_resume_action`. The GDB client typically sets this to
25     /// `ResumeAction::Continue`, though this is not guaranteed.
26     ///
27     /// The `check_gdb_interrupt` callback can be invoked to check if GDB sent
28     /// an Interrupt packet (i.e: the user pressed Ctrl-C). It's recommended to
29     /// invoke this callback every-so-often while the system is running (e.g:
30     /// every X cycles/milliseconds). Periodically checking for incoming
31     /// interrupt packets is _not_ required, but it is _recommended_.
32     ///
33     /// # Implementation requirements
34     ///
35     /// These requirements cannot be satisfied by `gdbstub` internally, and must
36     /// be handled on a per-target basis.
37     ///
38     /// ### Adjusting PC after a breakpoint is hit
39     ///
40     /// The [GDB remote serial protocol documentation](https://sourceware.org/gdb/current/onlinedocs/gdb/Stop-Reply-Packets.html#swbreak-stop-reason)
41     /// notes the following:
42     ///
43     /// > On some architectures, such as x86, at the architecture level, when a
44     /// > breakpoint instruction executes the program counter points at the
45     /// > breakpoint address plus an offset. On such targets, the stub is
46     /// > responsible for adjusting the PC to point back at the breakpoint
47     /// > address.
48     ///
49     /// Omitting PC adjustment may result in unexpected execution flow and/or
50     /// breakpoints not working correctly.
51     ///
52     /// # Additional Considerations
53     ///
54     /// ### Bare-Metal Targets
55     ///
56     /// On bare-metal targets (such as microcontrollers or emulators), it's
57     /// common to treat individual _CPU cores_ as a separate "threads". e.g:
58     /// in a dual-core system, [CPU0, CPU1] might be mapped to [TID1, TID2]
59     /// (note that TIDs cannot be zero).
60     ///
61     /// In this case, the `Tid` argument of `read/write_addrs` becomes quite
62     /// relevant, as different cores may have different memory maps.
63     ///
64     /// ### Running in "Non-stop" mode
65     ///
66     /// At the moment, `gdbstub` only supports GDB's
67     /// ["All-Stop" mode](https://sourceware.org/gdb/current/onlinedocs/gdb/All_002dStop-Mode.html),
68     /// whereby _all_ threads must be stopped when returning from `resume`
69     /// (not just the thread associated with the `ThreadStopReason`).
resume( &mut self, default_resume_action: ResumeAction, gdb_interrupt: GdbInterrupt<'_>, ) -> Result<ThreadStopReason<<Self::Arch as Arch>::Usize>, Self::Error>70     fn resume(
71         &mut self,
72         default_resume_action: ResumeAction,
73         gdb_interrupt: GdbInterrupt<'_>,
74     ) -> Result<ThreadStopReason<<Self::Arch as Arch>::Usize>, Self::Error>;
75 
76     /// Clear all previously set resume actions.
clear_resume_actions(&mut self) -> Result<(), Self::Error>77     fn clear_resume_actions(&mut self) -> Result<(), Self::Error>;
78 
79     /// Specify what action each thread should take when
80     /// [`resume`](Self::resume) is called.
81     ///
82     /// A simple implementation of this method would simply update an internal
83     /// `HashMap<Tid, ResumeAction>`.
84     ///
85     /// Aside from the four "base" resume actions handled by this method (i.e:
86     /// `Step`, `Continue`, `StepWithSignal`, and `ContinueWithSignal`),
87     /// there are also two additional resume actions which are only set if the
88     /// target implements their corresponding protocol extension:
89     ///
90     /// Action                     | Protocol Extension
91     /// ---------------------------|---------------------------
92     /// Optimized [Range Stepping] | See [`support_range_step()`]
93     /// "Stop"                     | Used in "Non-Stop" mode \*
94     ///
95     /// \* "Non-Stop" mode is currently unimplemented
96     ///
97     /// [Range Stepping]: https://sourceware.org/gdb/current/onlinedocs/gdb/Continuing-and-Stepping.html#range-stepping
98     /// [`support_range_step()`]: Self::support_range_step
set_resume_action(&mut self, tid: Tid, action: ResumeAction) -> Result<(), Self::Error>99     fn set_resume_action(&mut self, tid: Tid, action: ResumeAction) -> Result<(), Self::Error>;
100 
101     /// Support for the optimized [range stepping] resume action.
102     ///
103     /// [range stepping]: https://sourceware.org/gdb/current/onlinedocs/gdb/Continuing-and-Stepping.html#range-stepping
104     #[inline(always)]
support_range_step(&mut self) -> Option<MultiThreadRangeSteppingOps<Self>>105     fn support_range_step(&mut self) -> Option<MultiThreadRangeSteppingOps<Self>> {
106         None
107     }
108 
109     /// Support for [reverse stepping] a target.
110     ///
111     /// [reverse stepping]: https://sourceware.org/gdb/current/onlinedocs/gdb/Reverse-Execution.html
112     #[inline(always)]
support_reverse_step(&mut self) -> Option<MultiThreadReverseStepOps<Self>>113     fn support_reverse_step(&mut self) -> Option<MultiThreadReverseStepOps<Self>> {
114         None
115     }
116 
117     /// Support for [reverse continuing] a target.
118     ///
119     /// [reverse continuing]: https://sourceware.org/gdb/current/onlinedocs/gdb/Reverse-Execution.html
120     #[inline(always)]
support_reverse_cont(&mut self) -> Option<MultiThreadReverseContOps<Self>>121     fn support_reverse_cont(&mut self) -> Option<MultiThreadReverseContOps<Self>> {
122         None
123     }
124 
125     /// Read the target's registers.
126     ///
127     /// If the registers could not be accessed, an appropriate non-fatal error
128     /// should be returned.
read_registers( &mut self, regs: &mut <Self::Arch as Arch>::Registers, tid: Tid, ) -> TargetResult<(), Self>129     fn read_registers(
130         &mut self,
131         regs: &mut <Self::Arch as Arch>::Registers,
132         tid: Tid,
133     ) -> TargetResult<(), Self>;
134 
135     /// Write the target's registers.
136     ///
137     /// If the registers could not be accessed, an appropriate non-fatal error
138     /// should be returned.
write_registers( &mut self, regs: &<Self::Arch as Arch>::Registers, tid: Tid, ) -> TargetResult<(), Self>139     fn write_registers(
140         &mut self,
141         regs: &<Self::Arch as Arch>::Registers,
142         tid: Tid,
143     ) -> TargetResult<(), Self>;
144 
145     /// Support for single-register access.
146     /// See [`SingleRegisterAccess`](super::SingleRegisterAccess) for more
147     /// details.
148     ///
149     /// While this is an optional feature, it is **highly recommended** to
150     /// implement it when possible, as it can significantly improve performance
151     /// on certain architectures.
152     #[inline(always)]
single_register_access(&mut self) -> Option<SingleRegisterAccessOps<Tid, Self>>153     fn single_register_access(&mut self) -> Option<SingleRegisterAccessOps<Tid, Self>> {
154         None
155     }
156 
157     /// Read bytes from the specified address range.
158     ///
159     /// If the requested address range could not be accessed (e.g: due to
160     /// MMU protection, unhanded page fault, etc...), an appropriate non-fatal
161     /// error should be returned.
read_addrs( &mut self, start_addr: <Self::Arch as Arch>::Usize, data: &mut [u8], tid: Tid, ) -> TargetResult<(), Self>162     fn read_addrs(
163         &mut self,
164         start_addr: <Self::Arch as Arch>::Usize,
165         data: &mut [u8],
166         tid: Tid,
167     ) -> TargetResult<(), Self>;
168 
169     /// Write bytes to the specified address range.
170     ///
171     /// If the requested address range could not be accessed (e.g: due to
172     /// MMU protection, unhanded page fault, etc...), an appropriate non-fatal
173     /// error should be returned.
write_addrs( &mut self, start_addr: <Self::Arch as Arch>::Usize, data: &[u8], tid: Tid, ) -> TargetResult<(), Self>174     fn write_addrs(
175         &mut self,
176         start_addr: <Self::Arch as Arch>::Usize,
177         data: &[u8],
178         tid: Tid,
179     ) -> TargetResult<(), Self>;
180 
181     /// List all currently active threads.
182     ///
183     /// See [the section above](#bare-metal-targets) on implementing
184     /// thread-related methods on bare-metal (threadless) targets.
list_active_threads( &mut self, thread_is_active: &mut dyn FnMut(Tid), ) -> Result<(), Self::Error>185     fn list_active_threads(
186         &mut self,
187         thread_is_active: &mut dyn FnMut(Tid),
188     ) -> Result<(), Self::Error>;
189 
190     /// Check if the specified thread is alive.
191     ///
192     /// As a convenience, this method provides a default implementation which
193     /// uses `list_active_threads` to do a linear-search through all active
194     /// threads. On thread-heavy systems, it may be more efficient
195     /// to override this method with a more direct query.
is_thread_alive(&mut self, tid: Tid) -> Result<bool, Self::Error>196     fn is_thread_alive(&mut self, tid: Tid) -> Result<bool, Self::Error> {
197         let mut found = false;
198         self.list_active_threads(&mut |active_tid| {
199             if tid == active_tid {
200                 found = true;
201             }
202         })?;
203         Ok(found)
204     }
205 }
206 
207 /// Target Extension - [Reverse continue] for multi threaded targets.
208 ///
209 /// Reverse continue allows the target to run backwards until it reaches the end
210 /// of the replay log.
211 ///
212 /// [Reverse continue]: https://sourceware.org/gdb/current/onlinedocs/gdb/Reverse-Execution.html
213 pub trait MultiThreadReverseCont: Target + MultiThreadOps {
214     /// Reverse-continue the target.
reverse_cont( &mut self, gdb_interrupt: GdbInterrupt<'_>, ) -> Result<ThreadStopReason<<Self::Arch as Arch>::Usize>, Self::Error>215     fn reverse_cont(
216         &mut self,
217         gdb_interrupt: GdbInterrupt<'_>,
218     ) -> Result<ThreadStopReason<<Self::Arch as Arch>::Usize>, Self::Error>;
219 }
220 
221 define_ext!(MultiThreadReverseContOps, MultiThreadReverseCont);
222 
223 /// Target Extension - [Reverse stepping] for multi threaded targets.
224 ///
225 /// Reverse stepping allows the target to run backwards by one step.
226 ///
227 /// [Reverse stepping]: https://sourceware.org/gdb/current/onlinedocs/gdb/Reverse-Execution.html
228 pub trait MultiThreadReverseStep: Target + MultiThreadOps {
229     /// Reverse-step the specified [`Tid`].
reverse_step( &mut self, tid: Tid, gdb_interrupt: GdbInterrupt<'_>, ) -> Result<ThreadStopReason<<Self::Arch as Arch>::Usize>, Self::Error>230     fn reverse_step(
231         &mut self,
232         tid: Tid,
233         gdb_interrupt: GdbInterrupt<'_>,
234     ) -> Result<ThreadStopReason<<Self::Arch as Arch>::Usize>, Self::Error>;
235 }
236 
237 define_ext!(MultiThreadReverseStepOps, MultiThreadReverseStep);
238 
239 /// Target Extension - Optimized [range stepping] for multi threaded targets.
240 /// See [`MultiThreadOps::support_range_step`].
241 ///
242 /// Range Stepping will step the target once, and keep stepping the target as
243 /// long as execution remains between the specified start (inclusive) and end
244 /// (exclusive) addresses, or another stop condition is met (e.g: a breakpoint
245 /// it hit).
246 ///
247 /// If the range is empty (`start` == `end`), then the action becomes
248 /// equivalent to the ‘s’ action. In other words, single-step once, and
249 /// report the stop (even if the stepped instruction jumps to start).
250 ///
251 /// _Note:_ A stop reply may be sent at any point even if the PC is still
252 /// within the stepping range; for example, it is valid to implement range
253 /// stepping in a degenerate way as a single instruction step operation.
254 ///
255 /// [range stepping]: https://sourceware.org/gdb/current/onlinedocs/gdb/Continuing-and-Stepping.html#range-stepping
256 pub trait MultiThreadRangeStepping: Target + MultiThreadOps {
257     /// See [`MultiThreadOps::set_resume_action`].
set_resume_action_range_step( &mut self, tid: Tid, start: <Self::Arch as Arch>::Usize, end: <Self::Arch as Arch>::Usize, ) -> Result<(), Self::Error>258     fn set_resume_action_range_step(
259         &mut self,
260         tid: Tid,
261         start: <Self::Arch as Arch>::Usize,
262         end: <Self::Arch as Arch>::Usize,
263     ) -> Result<(), Self::Error>;
264 }
265 
266 define_ext!(MultiThreadRangeSteppingOps, MultiThreadRangeStepping);
267 
268 /// Describes why a thread stopped.
269 ///
270 /// Targets MUST only respond with stop reasons that correspond to IDETs that
271 /// target has implemented.
272 ///
273 /// e.g: A target which has not implemented the [`HwBreakpoint`] IDET must not
274 /// return a `HwBreak` stop reason. While this is not enforced at compile time,
275 /// doing so will result in a runtime `UnsupportedStopReason` error.
276 ///
277 /// [`HwBreakpoint`]: crate::target::ext::breakpoints::HwBreakpoint
278 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
279 #[non_exhaustive]
280 pub enum ThreadStopReason<U> {
281     /// Completed the single-step request.
282     DoneStep,
283     /// `check_gdb_interrupt` returned `true`.
284     GdbInterrupt,
285     /// The process exited with the specified exit status.
286     Exited(u8),
287     /// The process terminated with the specified signal number.
288     Terminated(u8),
289     /// The program received a signal.
290     Signal(u8),
291     /// A thread hit a software breakpoint (e.g. due to a trap instruction).
292     ///
293     /// Requires: [`SwBreakpoint`].
294     ///
295     /// NOTE: This does not necessarily have to be a breakpoint configured by
296     /// the client/user of the current GDB session.
297     ///
298     /// [`SwBreakpoint`]: crate::target::ext::breakpoints::SwBreakpoint
299     SwBreak(Tid),
300     /// A thread hit a hardware breakpoint.
301     ///
302     /// Requires: [`HwBreakpoint`].
303     ///
304     /// [`HwBreakpoint`]: crate::target::ext::breakpoints::HwBreakpoint
305     HwBreak(Tid),
306     /// A thread hit a watchpoint.
307     ///
308     /// Requires: [`HwWatchpoint`].
309     ///
310     /// [`HwWatchpoint`]: crate::target::ext::breakpoints::HwWatchpoint
311     Watch {
312         /// Which thread hit the watchpoint
313         tid: Tid,
314         /// Kind of watchpoint that was hit
315         kind: WatchKind,
316         /// Address of watched memory
317         addr: U,
318     },
319     /// The program has reached the end of the logged replay events.
320     ///
321     /// Requires: [`MultiThreadReverseCont`] or [`MultiThreadReverseStep`].
322     ///
323     /// This is used for GDB's reverse execution. When playing back a recording,
324     /// you may hit the end of the buffer of recorded events, and as such no
325     /// further execution can be done. This stop reason tells GDB that this has
326     /// occurred.
327     ReplayLog(ReplayLogPosition),
328 }
329