• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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