1 use crate::common::Signal; 2 use crate::common::Tid; 3 use crate::conn::Connection; 4 use crate::protocol::commands::Command; 5 use crate::protocol::Packet; 6 use crate::protocol::ResponseWriter; 7 use crate::protocol::SpecificIdKind; 8 use crate::stub::error::InternalError; 9 use crate::target::Target; 10 use crate::SINGLE_THREAD_TID; 11 use core::marker::PhantomData; 12 13 /// Common imports used by >50% of all extensions. 14 /// 15 /// Do not clutter this prelude with types only used by a few extensions. 16 mod prelude { 17 pub(super) use crate::conn::Connection; 18 pub(super) use crate::internal::BeBytes; 19 pub(super) use crate::protocol::ResponseWriter; 20 pub(super) use crate::stub::core_impl::target_result_ext::TargetResultExt; 21 pub(super) use crate::stub::core_impl::GdbStubImpl; 22 pub(super) use crate::stub::core_impl::HandlerStatus; 23 pub(super) use crate::stub::error::InternalError as Error; 24 pub(super) use crate::target::Target; 25 } 26 27 mod auxv; 28 mod base; 29 mod breakpoints; 30 mod catch_syscalls; 31 mod exec_file; 32 mod extended_mode; 33 mod host_io; 34 mod libraries; 35 mod lldb_register_info; 36 mod memory_map; 37 mod monitor_cmd; 38 mod no_ack_mode; 39 mod resume; 40 mod reverse_exec; 41 mod section_offsets; 42 mod single_register_access; 43 mod target_xml; 44 mod thread_extra_info; 45 mod tracepoints; 46 mod x_upcase_packet; 47 48 pub(crate) use resume::FinishExecStatus; 49 50 pub(crate) mod target_result_ext { 51 use crate::stub::error::InternalError; 52 use crate::target::TargetError; 53 54 /// Extension trait to ease working with `TargetResult` in the GdbStub 55 /// implementation. 56 pub(super) trait TargetResultExt<V, T, C> { 57 /// Encapsulates the boilerplate associated with handling 58 /// `TargetError`s, such as bailing-out on Fatal errors, or 59 /// returning response codes. handle_error(self) -> Result<V, InternalError<T, C>>60 fn handle_error(self) -> Result<V, InternalError<T, C>>; 61 } 62 63 impl<V, T, C> TargetResultExt<V, T, C> for Result<V, TargetError<T>> { handle_error(self) -> Result<V, InternalError<T, C>>64 fn handle_error(self) -> Result<V, InternalError<T, C>> { 65 let code = match self { 66 Ok(v) => return Ok(v), 67 Err(TargetError::Fatal(e)) => return Err(InternalError::TargetError(e)), 68 // Recoverable errors: 69 // Error code 121 corresponds to `EREMOTEIO` lol 70 Err(TargetError::NonFatal) => 121, 71 Err(TargetError::Errno(code)) => code, 72 #[cfg(feature = "std")] 73 Err(TargetError::Io(e)) => e.raw_os_error().unwrap_or(121) as u8, 74 }; 75 76 Err(InternalError::NonFatalError(code)) 77 } 78 } 79 } 80 81 /// Describes why the GDB session ended. 82 #[derive(Clone, Copy, Debug, PartialEq, Eq)] 83 pub enum DisconnectReason { 84 /// Target exited with given status code 85 TargetExited(u8), 86 /// Target terminated with given signal 87 TargetTerminated(Signal), 88 /// GDB issued a disconnect command 89 Disconnect, 90 /// GDB issued a kill command 91 Kill, 92 } 93 94 pub enum State { 95 Pump, 96 DeferredStopReason, 97 CtrlCInterrupt, 98 Disconnect(DisconnectReason), 99 } 100 101 pub(crate) struct GdbStubImpl<T: Target, C: Connection> { 102 _target: PhantomData<T>, 103 _connection: PhantomData<C>, 104 105 current_mem_tid: Tid, 106 current_resume_tid: SpecificIdKind, 107 features: ProtocolFeatures, 108 } 109 110 pub enum HandlerStatus { 111 Handled, 112 NeedsOk, 113 DeferredStopReason, 114 Disconnect(DisconnectReason), 115 } 116 117 impl<T: Target, C: Connection> GdbStubImpl<T, C> { new() -> GdbStubImpl<T, C>118 pub fn new() -> GdbStubImpl<T, C> { 119 GdbStubImpl { 120 _target: PhantomData, 121 _connection: PhantomData, 122 123 // NOTE: `current_mem_tid` and `current_resume_tid` are never queried prior to being set 124 // by the GDB client (via the 'H' packet), so it's fine to use dummy values here. 125 // 126 // The alternative would be to use `Option`, and while this would be more "correct", it 127 // would introduce a _lot_ of noisy and heavy error handling logic all over the place. 128 // 129 // Plus, even if the GDB client is acting strangely and doesn't overwrite these values, 130 // the target will simply return a non-fatal error, which is totally fine. 131 current_mem_tid: SINGLE_THREAD_TID, 132 current_resume_tid: SpecificIdKind::WithId(SINGLE_THREAD_TID), 133 features: ProtocolFeatures::empty(), 134 } 135 } 136 handle_packet( &mut self, target: &mut T, conn: &mut C, packet: Packet<'_>, ) -> Result<State, InternalError<T::Error, C::Error>>137 pub fn handle_packet( 138 &mut self, 139 target: &mut T, 140 conn: &mut C, 141 packet: Packet<'_>, 142 ) -> Result<State, InternalError<T::Error, C::Error>> { 143 match packet { 144 Packet::Ack => Ok(State::Pump), 145 Packet::Nack => Err(InternalError::ClientSentNack), 146 Packet::Interrupt => { 147 debug!("<-- interrupt packet"); 148 Ok(State::CtrlCInterrupt) 149 } 150 Packet::Command(command) => { 151 // Acknowledge the command 152 if !self.features.no_ack_mode() { 153 conn.write(b'+').map_err(InternalError::conn_write)?; 154 } 155 156 let mut res = ResponseWriter::new(conn, target.use_rle()); 157 let disconnect_reason = match self.handle_command(&mut res, target, command) { 158 Ok(HandlerStatus::Handled) => None, 159 Ok(HandlerStatus::NeedsOk) => { 160 res.write_str("OK")?; 161 None 162 } 163 Ok(HandlerStatus::DeferredStopReason) => return Ok(State::DeferredStopReason), 164 Ok(HandlerStatus::Disconnect(reason)) => Some(reason), 165 // HACK: handling this "dummy" error is required as part of the 166 // `TargetResultExt::handle_error()` machinery. 167 Err(InternalError::NonFatalError(code)) => { 168 res.write_str("E")?; 169 res.write_num(code)?; 170 None 171 } 172 Err(e) => return Err(e), 173 }; 174 175 // every response needs to be flushed, _except_ for the response to a kill 176 // packet, but ONLY when extended mode is NOT implemented. 177 let is_kill = matches!(disconnect_reason, Some(DisconnectReason::Kill)); 178 if !(target.support_extended_mode().is_none() && is_kill) { 179 res.flush()?; 180 } 181 182 let state = match disconnect_reason { 183 Some(reason) => State::Disconnect(reason), 184 None => State::Pump, 185 }; 186 187 Ok(state) 188 } 189 } 190 } 191 handle_command( &mut self, res: &mut ResponseWriter<'_, C>, target: &mut T, cmd: Command<'_>, ) -> Result<HandlerStatus, InternalError<T::Error, C::Error>>192 fn handle_command( 193 &mut self, 194 res: &mut ResponseWriter<'_, C>, 195 target: &mut T, 196 cmd: Command<'_>, 197 ) -> Result<HandlerStatus, InternalError<T::Error, C::Error>> { 198 match cmd { 199 // `handle_X` methods are defined in the `ext` module 200 Command::Base(cmd) => self.handle_base(res, target, cmd), 201 Command::TargetXml(cmd) => self.handle_target_xml(res, target, cmd), 202 Command::Resume(cmd) => self.handle_stop_resume(res, target, cmd), 203 Command::NoAckMode(cmd) => self.handle_no_ack_mode(res, target, cmd), 204 Command::XUpcasePacket(cmd) => self.handle_x_upcase_packet(res, target, cmd), 205 Command::SingleRegisterAccess(cmd) => { 206 self.handle_single_register_access(res, target, cmd) 207 } 208 Command::Breakpoints(cmd) => self.handle_breakpoints(res, target, cmd), 209 Command::CatchSyscalls(cmd) => self.handle_catch_syscalls(res, target, cmd), 210 Command::ExtendedMode(cmd) => self.handle_extended_mode(res, target, cmd), 211 Command::MonitorCmd(cmd) => self.handle_monitor_cmd(res, target, cmd), 212 Command::SectionOffsets(cmd) => self.handle_section_offsets(res, target, cmd), 213 Command::ReverseCont(cmd) => self.handle_reverse_cont(res, target, cmd), 214 Command::ReverseStep(cmd) => self.handle_reverse_step(res, target, cmd), 215 Command::MemoryMap(cmd) => self.handle_memory_map(res, target, cmd), 216 Command::HostIo(cmd) => self.handle_host_io(res, target, cmd), 217 Command::ExecFile(cmd) => self.handle_exec_file(res, target, cmd), 218 Command::Auxv(cmd) => self.handle_auxv(res, target, cmd), 219 Command::ThreadExtraInfo(cmd) => self.handle_thread_extra_info(res, target, cmd), 220 Command::LldbRegisterInfo(cmd) => self.handle_lldb_register_info(res, target, cmd), 221 Command::LibrariesSvr4(cmd) => self.handle_libraries_svr4(res, target, cmd), 222 Command::Tracepoints(cmd) => self.handle_tracepoints(res, target, cmd), 223 // in the worst case, the command could not be parsed... 224 Command::Unknown(cmd) => { 225 // HACK: if the user accidentally sends a resume command to a 226 // target without resume support, inform them of their mistake + 227 // return a dummy stop reason. 228 if target.base_ops().resume_ops().is_none() && target.use_resume_stub() { 229 let is_resume_pkt = cmd 230 .first() 231 .map(|c| matches!(c, b'c' | b'C' | b's' | b'S')) 232 .unwrap_or(false); 233 234 if is_resume_pkt { 235 warn!("attempted to resume target without resume support!"); 236 237 // TODO: omit this message if non-stop mode is active 238 { 239 let mut res = ResponseWriter::new(res.as_conn(), target.use_rle()); 240 res.write_str("O")?; 241 res.write_hex_buf(b"target has not implemented `support_resume()`\n")?; 242 res.flush()?; 243 } 244 245 res.write_str("S05")?; 246 } 247 } 248 249 info!("Unknown command: {:?}", core::str::from_utf8(cmd)); 250 Ok(HandlerStatus::Handled) 251 } 252 } 253 } 254 } 255 256 #[derive(Copy, Clone)] 257 #[repr(transparent)] 258 struct ProtocolFeatures(u8); 259 260 // This bitflag is not part of the protocol - it is an internal implementation 261 // detail. The alternative would be to use multiple `bool` fields, which wastes 262 // space in minimal `gdbstub` configurations. 263 bitflags::bitflags! { 264 impl ProtocolFeatures: u8 { 265 const NO_ACK_MODE = 1 << 0; 266 const MULTIPROCESS = 1 << 1; 267 } 268 } 269 270 impl ProtocolFeatures { 271 #[inline(always)] no_ack_mode(&self) -> bool272 fn no_ack_mode(&self) -> bool { 273 self.contains(ProtocolFeatures::NO_ACK_MODE) 274 } 275 276 #[inline(always)] set_no_ack_mode(&mut self, val: bool)277 fn set_no_ack_mode(&mut self, val: bool) { 278 self.set(ProtocolFeatures::NO_ACK_MODE, val) 279 } 280 281 #[inline(always)] multiprocess(&self) -> bool282 fn multiprocess(&self) -> bool { 283 self.contains(ProtocolFeatures::MULTIPROCESS) 284 } 285 286 #[inline(always)] set_multiprocess(&mut self, val: bool)287 fn set_multiprocess(&mut self, val: bool) { 288 self.set(ProtocolFeatures::MULTIPROCESS, val) 289 } 290 } 291