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