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 // Convenient re-exports 9 pub use super::ResumeAction; 10 11 /// Selects a thread corresponding to a ResumeAction. 12 // NOTE: this is a subset of the internal `IdKind` type, albeit without an `Any` variant. Selecting 13 // `Any` thread is something that's handled by `gdbstub` internally, and shouldn't be exposed to the 14 // end user. 15 #[derive(PartialEq, Eq, Debug, Clone, Copy)] 16 pub enum TidSelector { 17 /// Thread with a specific ID. 18 WithID(Tid), 19 /// All (other) threads. 20 All, 21 } 22 23 /// Base debugging operations for multi threaded targets. 24 #[allow(clippy::type_complexity)] 25 pub trait MultiThreadOps: Target { 26 /// Resume execution on the target. 27 /// 28 /// `actions` is an iterator over `(TidSelector, ResumeAction)` pairs which 29 /// specify how various threads should be resumed (i.e: single-step vs. 30 /// resume). It is _guaranteed_ to contain at least one action. It is not 31 /// guaranteed to be exhaustive over all live threads, and any threads 32 /// without a corresponding `TidSelector` should be left in the same state 33 /// (if possible). 34 /// 35 /// The `check_gdb_interrupt` callback can be invoked to check if GDB sent 36 /// an Interrupt packet (i.e: the user pressed Ctrl-C). It's recommended to 37 /// invoke this callback every-so-often while the system is running (e.g: 38 /// every X cycles/milliseconds). Periodically checking for incoming 39 /// interrupt packets is _not_ required, but it is _recommended_. 40 /// 41 /// # Implementation requirements 42 /// 43 /// These requirements cannot be satisfied by `gdbstub` internally, and must 44 /// be handled on a per-target basis. 45 /// 46 /// ### Adjusting PC after a breakpoint is hit 47 /// 48 /// The [GDB remote serial protocol documentation](https://sourceware.org/gdb/current/onlinedocs/gdb/Stop-Reply-Packets.html#swbreak-stop-reason) 49 /// notes the following: 50 /// 51 /// > On some architectures, such as x86, at the architecture level, when a 52 /// > breakpoint instruction executes the program counter points at the 53 /// > breakpoint address plus an offset. On such targets, the stub is 54 /// > responsible for adjusting the PC to point back at the breakpoint 55 /// > address. 56 /// 57 /// Omitting PC adjustment may result in unexpected execution flow and/or 58 /// breakpoints not appearing to work correctly. 59 /// 60 /// # Additional Considerations 61 /// 62 /// ### "Non-stop" mode 63 /// 64 /// At the moment, `gdbstub` only supports GDB's 65 /// ["All-Stop" mode](https://sourceware.org/gdb/current/onlinedocs/gdb/All_002dStop-Mode.html), 66 /// whereby _all_ threads should be stopped when returning from `resume` 67 /// (not just the thread associated with the `ThreadStopReason`). 68 /// 69 /// ### Bare-Metal Targets 70 /// 71 /// On bare-metal targets (such as microcontrollers or emulators), it's 72 /// common to treat individual _CPU cores_ as a separate "threads". e.g: 73 /// in a dual-core system, [CPU0, CPU1] might be mapped to [TID1, TID2] 74 /// (note that TIDs cannot be zero). 75 /// 76 /// In this case, the `Tid` argument of `read/write_addrs` becomes quite 77 /// relevant, as different cores may have different memory maps. resume( &mut self, actions: Actions<'_>, check_gdb_interrupt: &mut dyn FnMut() -> bool, ) -> Result<ThreadStopReason<<Self::Arch as Arch>::Usize>, Self::Error>78 fn resume( 79 &mut self, 80 actions: Actions<'_>, 81 check_gdb_interrupt: &mut dyn FnMut() -> bool, 82 ) -> Result<ThreadStopReason<<Self::Arch as Arch>::Usize>, Self::Error>; 83 84 /// Read the target's registers. 85 /// 86 /// If the registers could not be accessed, an appropriate non-fatal error 87 /// should be returned. read_registers( &mut self, regs: &mut <Self::Arch as Arch>::Registers, tid: Tid, ) -> TargetResult<(), Self>88 fn read_registers( 89 &mut self, 90 regs: &mut <Self::Arch as Arch>::Registers, 91 tid: Tid, 92 ) -> TargetResult<(), Self>; 93 94 /// Write the target's registers. 95 /// 96 /// If the registers could not be accessed, an appropriate non-fatal error 97 /// should be returned. write_registers( &mut self, regs: &<Self::Arch as Arch>::Registers, tid: Tid, ) -> TargetResult<(), Self>98 fn write_registers( 99 &mut self, 100 regs: &<Self::Arch as Arch>::Registers, 101 tid: Tid, 102 ) -> TargetResult<(), Self>; 103 104 /// Read to a single register on the target. 105 /// 106 /// Implementations should write the value of the register using target's 107 /// native byte order in the buffer `dst`. 108 /// 109 /// If the requested register could not be accessed, an appropriate 110 /// non-fatal error should be returned. 111 /// 112 /// _Note:_ This method includes a stubbed default implementation which 113 /// simply returns `Ok(())`. This is due to the fact that several built-in 114 /// `arch` implementations haven't been updated with proper `RegId` 115 /// implementations. read_register( &mut self, reg_id: <Self::Arch as Arch>::RegId, dst: &mut [u8], tid: Tid, ) -> TargetResult<(), Self>116 fn read_register( 117 &mut self, 118 reg_id: <Self::Arch as Arch>::RegId, 119 dst: &mut [u8], 120 tid: Tid, 121 ) -> TargetResult<(), Self> { 122 let _ = (reg_id, dst, tid); 123 Ok(()) 124 } 125 126 /// Write from a single register on the target. 127 /// 128 /// The `val` buffer contains the new value of the register in the target's 129 /// native byte order. It is guaranteed to be the exact length as the target 130 /// register. 131 /// 132 /// If the requested register could not be accessed, an appropriate 133 /// non-fatal error should be returned. 134 /// 135 /// _Note:_ This method includes a stubbed default implementation which 136 /// simply returns `Ok(())`. This is due to the fact that several built-in 137 /// `arch` implementations haven't been updated with proper `RegId` 138 /// implementations. write_register( &mut self, reg_id: <Self::Arch as Arch>::RegId, val: &[u8], tid: Tid, ) -> TargetResult<(), Self>139 fn write_register( 140 &mut self, 141 reg_id: <Self::Arch as Arch>::RegId, 142 val: &[u8], 143 tid: Tid, 144 ) -> TargetResult<(), Self> { 145 let _ = (reg_id, val, tid); 146 Ok(()) 147 } 148 149 /// Read bytes from the specified address range. 150 /// 151 /// If the requested address range could not be accessed (e.g: due to 152 /// MMU protection, unhanded page fault, etc...), an appropriate non-fatal 153 /// error should be returned. read_addrs( &mut self, start_addr: <Self::Arch as Arch>::Usize, data: &mut [u8], tid: Tid, ) -> TargetResult<(), Self>154 fn read_addrs( 155 &mut self, 156 start_addr: <Self::Arch as Arch>::Usize, 157 data: &mut [u8], 158 tid: Tid, 159 ) -> TargetResult<(), Self>; 160 161 /// Write bytes to the specified address range. 162 /// 163 /// If the requested address range could not be accessed (e.g: due to 164 /// MMU protection, unhanded page fault, etc...), an appropriate non-fatal 165 /// error should be returned. write_addrs( &mut self, start_addr: <Self::Arch as Arch>::Usize, data: &[u8], tid: Tid, ) -> TargetResult<(), Self>166 fn write_addrs( 167 &mut self, 168 start_addr: <Self::Arch as Arch>::Usize, 169 data: &[u8], 170 tid: Tid, 171 ) -> TargetResult<(), Self>; 172 173 /// List all currently active threads. 174 /// 175 /// See [the section above](#bare-metal-targets) on implementing 176 /// thread-related methods on bare-metal (threadless) targets. list_active_threads( &mut self, thread_is_active: &mut dyn FnMut(Tid), ) -> Result<(), Self::Error>177 fn list_active_threads( 178 &mut self, 179 thread_is_active: &mut dyn FnMut(Tid), 180 ) -> Result<(), Self::Error>; 181 182 /// Check if the specified thread is alive. 183 /// 184 /// As a convenience, this method provides a default implementation which 185 /// uses `list_active_threads` to do a linear-search through all active 186 /// threads. On thread-heavy systems, it may be more efficient 187 /// to override this method with a more direct query. is_thread_alive(&mut self, tid: Tid) -> Result<bool, Self::Error>188 fn is_thread_alive(&mut self, tid: Tid) -> Result<bool, Self::Error> { 189 let mut found = false; 190 self.list_active_threads(&mut |active_tid| { 191 if tid == active_tid { 192 found = true; 193 } 194 })?; 195 Ok(found) 196 } 197 } 198 199 /// Describes why a thread stopped. 200 #[derive(Clone, Copy, Debug, PartialEq, Eq)] 201 #[non_exhaustive] 202 pub enum ThreadStopReason<U> { 203 /// Completed the single-step request. 204 DoneStep, 205 /// `check_gdb_interrupt` returned `true` 206 GdbInterrupt, 207 /// Halted 208 Halted, 209 /// A thread hit a software breakpoint (e.g. due to a trap instruction). 210 /// 211 /// NOTE: This does not necessarily have to be a breakpoint configured by 212 /// the client/user of the current GDB session. 213 SwBreak(Tid), 214 /// A thread hit a hardware breakpoint. 215 HwBreak(Tid), 216 /// A thread hit a watchpoint. 217 Watch { 218 /// Which thread hit the watchpoint 219 tid: Tid, 220 /// Kind of watchpoint that was hit 221 kind: WatchKind, 222 /// Address of watched memory 223 addr: U, 224 }, 225 /// The program received a signal 226 Signal(u8), 227 } 228 229 /// An iterator of `(TidSelector, ResumeAction)` used to specify how threads 230 /// should be resumed when running in multi threaded mode. It is _guaranteed_ to 231 /// contain at least one action. 232 /// 233 /// See the documentation for 234 /// [`Target::resume`](trait.Target.html#tymethod.resume) for more details. 235 pub struct Actions<'a> { 236 inner: &'a mut dyn Iterator<Item = (TidSelector, ResumeAction)>, 237 } 238 239 impl core::fmt::Debug for Actions<'_> { fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result240 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 241 write!(f, "Actions {{ .. }}") 242 } 243 } 244 245 impl Actions<'_> { new(iter: &mut dyn Iterator<Item = (TidSelector, ResumeAction)>) -> Actions<'_>246 pub(crate) fn new(iter: &mut dyn Iterator<Item = (TidSelector, ResumeAction)>) -> Actions<'_> { 247 Actions { inner: iter } 248 } 249 } 250 251 impl Iterator for Actions<'_> { 252 type Item = (TidSelector, ResumeAction); next(&mut self) -> Option<Self::Item>253 fn next(&mut self) -> Option<Self::Item> { 254 self.inner.next() 255 } 256 } 257