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