1 //! The core [`GdbStub`] type, used to drive a GDB debugging session for a 2 //! particular [`Target`] over a given [`Connection`]. 3 4 use managed::ManagedSlice; 5 6 use crate::conn::{Connection, ConnectionExt}; 7 use crate::target::Target; 8 9 mod builder; 10 mod core_impl; 11 mod error; 12 mod stop_reason; 13 14 pub mod state_machine; 15 16 pub use builder::{GdbStubBuilder, GdbStubBuilderError}; 17 pub use core_impl::DisconnectReason; 18 pub use error::GdbStubError; 19 pub use stop_reason::{ 20 BaseStopReason, IntoStopReason, MultiThreadStopReason, SingleThreadStopReason, 21 }; 22 23 use GdbStubError as Error; 24 25 /// Types and traits related to the [`GdbStub::run_blocking`] interface. 26 pub mod run_blocking { 27 use super::*; 28 29 use crate::conn::ConnectionExt; 30 31 /// A set of user-provided methods required to run a GDB debugging session 32 /// using the [`GdbStub::run_blocking`] method. 33 /// 34 /// Reminder: to use `gdbstub` in a non-blocking manner (e.g: via 35 /// async/await, unix polling, from an interrupt handler, etc...) you will 36 /// need to interface with the 37 /// [`GdbStubStateMachine`](state_machine::GdbStubStateMachine) API 38 /// directly. 39 pub trait BlockingEventLoop { 40 /// The Target being driven. 41 type Target: Target; 42 /// Connection being used to drive the target. 43 type Connection: ConnectionExt; 44 45 /// Which variant of the `StopReason` type should be used. Single 46 /// threaded targets should use [`SingleThreadStopReason`], whereas 47 /// multi threaded targets should use [`MultiThreadStopReason`]. 48 /// 49 /// [`SingleThreadStopReason`]: crate::stub::SingleThreadStopReason 50 /// [`MultiThreadStopReason`]: crate::stub::MultiThreadStopReason 51 type StopReason: IntoStopReason<Self::Target>; 52 53 /// Invoked immediately after the target's `resume` method has been 54 /// called. The implementation should block until either the target 55 /// reports a stop reason, or if new data was sent over the connection. 56 /// 57 /// The specific mechanism to "select" between these two events is 58 /// implementation specific. Some examples might include: `epoll`, 59 /// `select!` across multiple event channels, periodic polling, etc... wait_for_stop_reason( target: &mut Self::Target, conn: &mut Self::Connection, ) -> Result< Event<Self::StopReason>, WaitForStopReasonError< <Self::Target as Target>::Error, <Self::Connection as Connection>::Error, >, >60 fn wait_for_stop_reason( 61 target: &mut Self::Target, 62 conn: &mut Self::Connection, 63 ) -> Result< 64 Event<Self::StopReason>, 65 WaitForStopReasonError< 66 <Self::Target as Target>::Error, 67 <Self::Connection as Connection>::Error, 68 >, 69 >; 70 71 /// Invoked when the GDB client sends a Ctrl-C interrupt. 72 /// 73 /// Depending on how the target is implemented, it may or may not make 74 /// sense to immediately return a stop reason as part of handling the 75 /// Ctrl-C interrupt. e.g: in some cases, it may be better to send the 76 /// target a signal upon receiving a Ctrl-C interrupt _without_ 77 /// immediately sending a stop reason, and instead deferring the stop 78 /// reason to some later point in the target's execution. 79 /// 80 /// _Suggestion_: If you're unsure which stop reason to report, 81 /// [`BaseStopReason::Signal(Signal::SIGINT)`] is a sensible default. 82 /// 83 /// [`BaseStopReason::Signal(Signal::SIGINT)`]: 84 /// crate::stub::BaseStopReason::Signal on_interrupt( target: &mut Self::Target, ) -> Result<Option<Self::StopReason>, <Self::Target as Target>::Error>85 fn on_interrupt( 86 target: &mut Self::Target, 87 ) -> Result<Option<Self::StopReason>, <Self::Target as Target>::Error>; 88 } 89 90 /// Returned by the `wait_for_stop_reason` closure in 91 /// [`GdbStub::run_blocking`] 92 pub enum Event<StopReason> { 93 /// GDB Client sent data while the target was running. 94 IncomingData(u8), 95 /// The target has stopped. 96 TargetStopped(StopReason), 97 } 98 99 /// Error value returned by the `wait_for_stop_reason` closure in 100 /// [`GdbStub::run_blocking`] 101 pub enum WaitForStopReasonError<T, C> { 102 /// A fatal target error has occurred. 103 Target(T), 104 /// A fatal connection error has occurred. 105 Connection(C), 106 } 107 } 108 109 /// Debug a [`Target`] using the GDB Remote Serial Protocol over a given 110 /// [`Connection`]. 111 pub struct GdbStub<'a, T: Target, C: Connection> { 112 conn: C, 113 packet_buffer: ManagedSlice<'a, u8>, 114 inner: core_impl::GdbStubImpl<T, C>, 115 } 116 117 impl<'a, T: Target, C: Connection> GdbStub<'a, T, C> { 118 /// Create a [`GdbStubBuilder`] using the provided Connection. builder(conn: C) -> GdbStubBuilder<'a, T, C>119 pub fn builder(conn: C) -> GdbStubBuilder<'a, T, C> { 120 GdbStubBuilder::new(conn) 121 } 122 123 /// Create a new `GdbStub` using the provided connection. 124 /// 125 /// _Note:_ `new` is only available when the `alloc` feature is enabled, as 126 /// it will use a dynamically allocated `Vec` as a packet buffer. 127 /// 128 /// For fine-grained control over various `GdbStub` options, including the 129 /// ability to specify a fixed-size buffer, use the [`GdbStub::builder`] 130 /// method instead. 131 #[cfg(feature = "alloc")] new(conn: C) -> GdbStub<'a, T, C>132 pub fn new(conn: C) -> GdbStub<'a, T, C> { 133 GdbStubBuilder::new(conn).build().unwrap() 134 } 135 136 /// (Quickstart) Start a GDB remote debugging session using a blocking event 137 /// loop. 138 /// 139 /// This method provides a quick and easy way to get up and running with 140 /// `gdbstub` without directly having to immediately interface with the 141 /// lower-level [state-machine](state_machine::GdbStubStateMachine) 142 /// based interface. 143 /// 144 /// Instead, an implementation simply needs to provide a implementation of 145 /// [`run_blocking::BlockingEventLoop`], which is a simplified set 146 /// of methods describing how to drive the target. 147 /// 148 /// `GdbStub::run_blocking` returns once the GDB client closes the debugging 149 /// session, or if the target triggers a disconnect. 150 /// 151 /// Note that this implementation is **blocking**, which many not be 152 /// preferred (or suitable) in all cases. To use `gdbstub` in a non-blocking 153 /// manner (e.g: via async/await, unix polling, from an interrupt handler, 154 /// etc...) you will need to interface with the underlying 155 /// [`GdbStubStateMachine`](state_machine::GdbStubStateMachine) API 156 /// directly. run_blocking<E>( self, target: &mut T, ) -> Result<DisconnectReason, Error<T::Error, C::Error>> where C: ConnectionExt, E: run_blocking::BlockingEventLoop<Target = T, Connection = C>,157 pub fn run_blocking<E>( 158 self, 159 target: &mut T, 160 ) -> Result<DisconnectReason, Error<T::Error, C::Error>> 161 where 162 C: ConnectionExt, 163 E: run_blocking::BlockingEventLoop<Target = T, Connection = C>, 164 { 165 let mut gdb = self.run_state_machine(target)?; 166 loop { 167 gdb = match gdb { 168 state_machine::GdbStubStateMachine::Idle(mut gdb) => { 169 // needs more data, so perform a blocking read on the connection 170 let byte = gdb.borrow_conn().read().map_err(Error::ConnectionRead)?; 171 gdb.incoming_data(target, byte)? 172 } 173 174 state_machine::GdbStubStateMachine::Disconnected(gdb) => { 175 // run_blocking keeps things simple, and doesn't expose a way to re-use the 176 // state machine 177 break Ok(gdb.get_reason()); 178 } 179 180 state_machine::GdbStubStateMachine::CtrlCInterrupt(gdb) => { 181 // defer to the implementation on how it wants to handle the interrupt 182 let stop_reason = E::on_interrupt(target).map_err(Error::TargetError)?; 183 gdb.interrupt_handled(target, stop_reason)? 184 } 185 186 state_machine::GdbStubStateMachine::Running(mut gdb) => { 187 use run_blocking::{Event as BlockingEventLoopEvent, WaitForStopReasonError}; 188 189 // block waiting for the target to return a stop reason 190 let event = E::wait_for_stop_reason(target, gdb.borrow_conn()); 191 match event { 192 Ok(BlockingEventLoopEvent::TargetStopped(stop_reason)) => { 193 gdb.report_stop(target, stop_reason)? 194 } 195 196 Ok(BlockingEventLoopEvent::IncomingData(byte)) => { 197 gdb.incoming_data(target, byte)? 198 } 199 200 Err(WaitForStopReasonError::Target(e)) => { 201 break Err(Error::TargetError(e)); 202 } 203 Err(WaitForStopReasonError::Connection(e)) => { 204 break Err(Error::ConnectionRead(e)); 205 } 206 } 207 } 208 } 209 } 210 } 211 212 /// Starts a GDB remote debugging session, converting this instance of 213 /// `GdbStub` into a 214 /// [`GdbStubStateMachine`](state_machine::GdbStubStateMachine) that is 215 /// ready to receive data. run_state_machine( mut self, target: &mut T, ) -> Result<state_machine::GdbStubStateMachine<'a, T, C>, Error<T::Error, C::Error>>216 pub fn run_state_machine( 217 mut self, 218 target: &mut T, 219 ) -> Result<state_machine::GdbStubStateMachine<'a, T, C>, Error<T::Error, C::Error>> { 220 // Check if the target hasn't explicitly opted into implicit sw breakpoints 221 { 222 let support_software_breakpoints = target 223 .support_breakpoints() 224 .map(|ops| ops.support_sw_breakpoint().is_some()) 225 .unwrap_or(false); 226 227 if !support_software_breakpoints && !target.guard_rail_implicit_sw_breakpoints() { 228 return Err(Error::ImplicitSwBreakpoints); 229 } 230 } 231 232 // Check how the target's arch handles single stepping 233 { 234 use crate::arch::SingleStepGdbBehavior; 235 use crate::target::ext::base::ResumeOps; 236 237 if let Some(ops) = target.base_ops().resume_ops() { 238 let support_single_step = match ops { 239 ResumeOps::SingleThread(ops) => ops.support_single_step().is_some(), 240 ResumeOps::MultiThread(ops) => ops.support_single_step().is_some(), 241 }; 242 243 let behavior = target.guard_rail_single_step_gdb_behavior(); 244 245 let return_error = match behavior { 246 SingleStepGdbBehavior::Optional => false, 247 SingleStepGdbBehavior::Required => !support_single_step, 248 SingleStepGdbBehavior::Ignored => support_single_step, 249 SingleStepGdbBehavior::Unknown => true, 250 }; 251 252 if return_error { 253 return Err(Error::SingleStepGdbBehavior(behavior)); 254 } 255 } 256 } 257 258 // Perform any connection initialization 259 { 260 self.conn 261 .on_session_start() 262 .map_err(Error::ConnectionInit)?; 263 } 264 265 Ok(state_machine::GdbStubStateMachineInner::from_plain_gdbstub(self).into()) 266 } 267 } 268