1 use core::marker::PhantomData; 2 3 use managed::ManagedSlice; 4 5 use crate::common::*; 6 use crate::connection::Connection; 7 use crate::protocol::{commands::Command, Packet, ResponseWriter, SpecificIdKind}; 8 use crate::target::Target; 9 use crate::util::managed_vec::ManagedVec; 10 use crate::SINGLE_THREAD_TID; 11 12 mod builder; 13 mod error; 14 mod ext; 15 mod target_result_ext; 16 17 pub use builder::{GdbStubBuilder, GdbStubBuilderError}; 18 pub use error::GdbStubError; 19 20 use GdbStubError as Error; 21 22 /// Describes why the GDB session ended. 23 #[derive(Clone, Copy, Debug, PartialEq, Eq)] 24 pub enum DisconnectReason { 25 /// Target exited with given status code 26 TargetExited(u8), 27 /// Target terminated with given signal 28 TargetTerminated(u8), 29 /// GDB issued a disconnect command 30 Disconnect, 31 /// GDB issued a kill command 32 Kill, 33 } 34 35 /// Debug a [`Target`] using the GDB Remote Serial Protocol over a given 36 /// [`Connection`]. 37 pub struct GdbStub<'a, T: Target, C: Connection> { 38 conn: C, 39 packet_buffer: ManagedSlice<'a, u8>, 40 state: GdbStubImpl<T, C>, 41 } 42 43 impl<'a, T: Target, C: Connection> GdbStub<'a, T, C> { 44 /// Create a [`GdbStubBuilder`] using the provided Connection. builder(conn: C) -> GdbStubBuilder<'a, T, C>45 pub fn builder(conn: C) -> GdbStubBuilder<'a, T, C> { 46 GdbStubBuilder::new(conn) 47 } 48 49 /// Create a new `GdbStub` using the provided connection. 50 /// 51 /// For fine-grained control over various `GdbStub` options, use the 52 /// [`builder()`](GdbStub::builder) method instead. 53 /// 54 /// _Note:_ `new` is only available when the `alloc` feature is enabled. 55 #[cfg(feature = "alloc")] new(conn: C) -> GdbStub<'a, T, C>56 pub fn new(conn: C) -> GdbStub<'a, T, C> { 57 GdbStubBuilder::new(conn).build().unwrap() 58 } 59 60 /// Starts a GDB remote debugging session. 61 /// 62 /// Returns once the GDB client closes the debugging session, or if the 63 /// target halts. run(&mut self, target: &mut T) -> Result<DisconnectReason, Error<T::Error, C::Error>>64 pub fn run(&mut self, target: &mut T) -> Result<DisconnectReason, Error<T::Error, C::Error>> { 65 self.state 66 .run(target, &mut self.conn, &mut self.packet_buffer) 67 } 68 } 69 70 struct GdbStubImpl<T: Target, C: Connection> { 71 _target: PhantomData<T>, 72 _connection: PhantomData<C>, 73 74 current_mem_tid: Tid, 75 current_resume_tid: SpecificIdKind, 76 no_ack_mode: bool, 77 } 78 79 enum HandlerStatus { 80 Handled, 81 NeedsOk, 82 Disconnect(DisconnectReason), 83 } 84 85 impl<T: Target, C: Connection> GdbStubImpl<T, C> { new() -> GdbStubImpl<T, C>86 fn new() -> GdbStubImpl<T, C> { 87 GdbStubImpl { 88 _target: PhantomData, 89 _connection: PhantomData, 90 91 // NOTE: `current_mem_tid` and `current_resume_tid` are never queried prior to being set 92 // by the GDB client (via the 'H' packet), so it's fine to use dummy values here. 93 // 94 // The alternative would be to use `Option`, and while this would be more "correct", it 95 // would introduce a _lot_ of noisy and heavy error handling logic all over the place. 96 // 97 // Plus, even if the GDB client is acting strangely and doesn't overwrite these values, 98 // the target will simply return a non-fatal error, which is totally fine. 99 current_mem_tid: SINGLE_THREAD_TID, 100 current_resume_tid: SpecificIdKind::WithId(SINGLE_THREAD_TID), 101 no_ack_mode: false, 102 } 103 } 104 run( &mut self, target: &mut T, conn: &mut C, packet_buffer: &mut ManagedSlice<u8>, ) -> Result<DisconnectReason, Error<T::Error, C::Error>>105 fn run( 106 &mut self, 107 target: &mut T, 108 conn: &mut C, 109 packet_buffer: &mut ManagedSlice<u8>, 110 ) -> Result<DisconnectReason, Error<T::Error, C::Error>> { 111 conn.on_session_start().map_err(Error::ConnectionRead)?; 112 113 loop { 114 match Self::recv_packet(conn, target, packet_buffer)? { 115 Packet::Ack => {} 116 Packet::Nack => return Err(Error::ClientSentNack), 117 Packet::Interrupt => { 118 debug!("<-- interrupt packet"); 119 let mut res = ResponseWriter::new(conn); 120 res.write_str("S05")?; 121 res.flush()?; 122 } 123 Packet::Command(command) => { 124 // Acknowledge the command 125 if !self.no_ack_mode { 126 conn.write(b'+').map_err(Error::ConnectionRead)?; 127 } 128 129 let mut res = ResponseWriter::new(conn); 130 let disconnect = match self.handle_command(&mut res, target, command) { 131 Ok(HandlerStatus::Handled) => None, 132 Ok(HandlerStatus::NeedsOk) => { 133 res.write_str("OK")?; 134 None 135 } 136 Ok(HandlerStatus::Disconnect(reason)) => Some(reason), 137 // HACK: handling this "dummy" error is required as part of the 138 // `TargetResultExt::handle_error()` machinery. 139 Err(Error::NonFatalError(code)) => { 140 res.write_str("E")?; 141 res.write_num(code)?; 142 None 143 } 144 Err(Error::TargetError(e)) => { 145 // unlike all other errors which are "unrecoverable" in the sense that 146 // the GDB session cannot continue, there's still a chance that a target 147 // might want to keep the debugging session alive to do a "post-mortem" 148 // analysis. As such, we simply report a standard TRAP stop reason. 149 let mut res = ResponseWriter::new(conn); 150 res.write_str("S05")?; 151 res.flush()?; 152 return Err(Error::TargetError(e)); 153 } 154 Err(e) => return Err(e), 155 }; 156 157 // HACK: this could be more elegant... 158 if disconnect != Some(DisconnectReason::Kill) { 159 res.flush()?; 160 } 161 162 if let Some(disconnect_reason) = disconnect { 163 return Ok(disconnect_reason); 164 } 165 } 166 }; 167 } 168 } 169 recv_packet<'a>( conn: &mut C, target: &mut T, pkt_buf: &'a mut ManagedSlice<u8>, ) -> Result<Packet<'a>, Error<T::Error, C::Error>>170 fn recv_packet<'a>( 171 conn: &mut C, 172 target: &mut T, 173 pkt_buf: &'a mut ManagedSlice<u8>, 174 ) -> Result<Packet<'a>, Error<T::Error, C::Error>> { 175 let header_byte = conn.read().map_err(Error::ConnectionRead)?; 176 177 // Wrap the buf in a `ManagedVec` to keep the code readable. 178 let mut buf = ManagedVec::new(pkt_buf); 179 180 buf.clear(); 181 buf.push(header_byte)?; 182 if header_byte == b'$' { 183 // read the packet body 184 loop { 185 let c = conn.read().map_err(Error::ConnectionRead)?; 186 buf.push(c)?; 187 if c == b'#' { 188 break; 189 } 190 } 191 // read the checksum as well 192 buf.push(conn.read().map_err(Error::ConnectionRead)?)?; 193 buf.push(conn.read().map_err(Error::ConnectionRead)?)?; 194 } 195 196 trace!( 197 "<-- {}", 198 core::str::from_utf8(buf.as_slice()).unwrap_or("<invalid packet>") 199 ); 200 201 drop(buf); 202 203 Packet::from_buf(target, pkt_buf.as_mut()).map_err(Error::PacketParse) 204 } 205 handle_command( &mut self, res: &mut ResponseWriter<C>, target: &mut T, cmd: Command<'_>, ) -> Result<HandlerStatus, Error<T::Error, C::Error>>206 fn handle_command( 207 &mut self, 208 res: &mut ResponseWriter<C>, 209 target: &mut T, 210 cmd: Command<'_>, 211 ) -> Result<HandlerStatus, Error<T::Error, C::Error>> { 212 match cmd { 213 Command::Unknown(cmd) => { 214 // cmd must be ASCII, as the slice originated from a PacketBuf, which checks for 215 // ASCII as part of the initial validation. 216 info!("Unknown command: {}", core::str::from_utf8(cmd).unwrap()); 217 Ok(HandlerStatus::Handled) 218 } 219 // `handle_X` methods are defined in the `ext` module 220 Command::Base(cmd) => self.handle_base(res, target, cmd), 221 Command::SingleRegisterAccess(cmd) => { 222 self.handle_single_register_access(res, target, cmd) 223 } 224 Command::Breakpoints(cmd) => self.handle_breakpoints(res, target, cmd), 225 Command::ExtendedMode(cmd) => self.handle_extended_mode(res, target, cmd), 226 Command::MonitorCmd(cmd) => self.handle_monitor_cmd(res, target, cmd), 227 Command::SectionOffsets(cmd) => self.handle_section_offsets(res, target, cmd), 228 Command::ReverseCont(cmd) => self.handle_reverse_cont(res, target, cmd), 229 Command::ReverseStep(cmd) => self.handle_reverse_step(res, target, cmd), 230 } 231 } 232 } 233