• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use core::marker::PhantomData;
2 
3 #[cfg(feature = "alloc")]
4 use alloc::collections::BTreeMap;
5 
6 use managed::ManagedSlice;
7 
8 use crate::common::*;
9 use crate::{
10     arch::{Arch, RegId, Registers},
11     connection::Connection,
12     internal::*,
13     protocol::{
14         commands::{ext, Command},
15         ConsoleOutput, IdKind, Packet, ResponseWriter, ThreadId,
16     },
17     target::ext::base::multithread::{Actions, ResumeAction, ThreadStopReason, TidSelector},
18     target::ext::base::BaseOps,
19     target::Target,
20     util::managed_vec::ManagedVec,
21     FAKE_PID, SINGLE_THREAD_TID,
22 };
23 
24 mod builder;
25 mod error;
26 mod target_result_ext;
27 
28 pub use builder::{GdbStubBuilder, GdbStubBuilderError};
29 pub use error::GdbStubError;
30 
31 use target_result_ext::TargetResultExt;
32 
33 use GdbStubError as Error;
34 
35 /// Describes why the GDB session ended.
36 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
37 pub enum DisconnectReason {
38     /// Target Halted
39     TargetHalted,
40     /// GDB issued a disconnect command
41     Disconnect,
42     /// GDB issued a kill command
43     Kill,
44 }
45 
46 /// Debug a [`Target`] using the GDB Remote Serial Protocol over a given
47 /// [`Connection`].
48 pub struct GdbStub<'a, T: Target, C: Connection> {
49     conn: C,
50     packet_buffer: ManagedSlice<'a, u8>,
51     state: GdbStubImpl<T, C>,
52 }
53 
54 impl<'a, T: Target, C: Connection> GdbStub<'a, T, C> {
55     /// Create a [`GdbStubBuilder`] using the provided Connection.
builder(conn: C) -> GdbStubBuilder<'a, T, C>56     pub fn builder(conn: C) -> GdbStubBuilder<'a, T, C> {
57         GdbStubBuilder::new(conn)
58     }
59 
60     /// Create a new `GdbStub` using the provided connection.
61     ///
62     /// For fine-grained control over various `GdbStub` options, use the
63     /// [`builder()`](GdbStub::builder) method instead.
64     ///
65     /// _Note:_ `new` is only available when the `alloc` feature is enabled.
66     #[cfg(feature = "alloc")]
new(conn: C) -> GdbStub<'a, T, C>67     pub fn new(conn: C) -> GdbStub<'a, T, C> {
68         GdbStubBuilder::new(conn).build().unwrap()
69     }
70 
71     /// Starts a GDB remote debugging session.
72     ///
73     /// Returns once the GDB client closes the debugging session, or if the
74     /// target halts.
run(&mut self, target: &mut T) -> Result<DisconnectReason, Error<T::Error, C::Error>>75     pub fn run(&mut self, target: &mut T) -> Result<DisconnectReason, Error<T::Error, C::Error>> {
76         self.state
77             .run(target, &mut self.conn, &mut self.packet_buffer)
78     }
79 }
80 
81 struct GdbStubImpl<T: Target, C: Connection> {
82     _target: PhantomData<T>,
83     _connection: PhantomData<C>,
84 
85     packet_buffer_len: usize,
86     current_mem_tid: Tid,
87     current_resume_tid: TidSelector,
88     no_ack_mode: bool,
89 
90     // Used to track which Pids were attached to / spawned when running in extended mode.
91     //
92     // An empty `BTreeMap<Pid, bool>` is only 24 bytes (on 64-bit systems), and doesn't allocate
93     // until the first element is inserted, so it should be fine to include it as part of the main
94     // state structure whether or not extended mode is actually being used.
95     #[cfg(feature = "alloc")]
96     attached_pids: BTreeMap<Pid, bool>,
97 }
98 
99 enum HandlerStatus {
100     Handled,
101     NeedsOK,
102     Disconnect(DisconnectReason),
103 }
104 
105 impl<T: Target, C: Connection> GdbStubImpl<T, C> {
new(packet_buffer_len: usize) -> GdbStubImpl<T, C>106     fn new(packet_buffer_len: usize) -> GdbStubImpl<T, C> {
107         GdbStubImpl {
108             _target: PhantomData,
109             _connection: PhantomData,
110 
111             packet_buffer_len,
112             // HACK: current_mem_tid is immediately updated with valid value once `run` is called.
113             // While the more idiomatic way to handle this would be to use an Option, given that
114             // it's only ever unset prior to the start of `run`, it's probably okay leaving it as-is
115             // for code-clarity purposes.
116             current_mem_tid: SINGLE_THREAD_TID,
117             current_resume_tid: TidSelector::All,
118             no_ack_mode: false,
119 
120             #[cfg(feature = "alloc")]
121             attached_pids: BTreeMap::new(),
122         }
123     }
124 
run( &mut self, target: &mut T, conn: &mut C, packet_buffer: &mut ManagedSlice<u8>, ) -> Result<DisconnectReason, Error<T::Error, C::Error>>125     fn run(
126         &mut self,
127         target: &mut T,
128         conn: &mut C,
129         packet_buffer: &mut ManagedSlice<u8>,
130     ) -> Result<DisconnectReason, Error<T::Error, C::Error>> {
131         conn.on_session_start().map_err(Error::ConnectionRead)?;
132 
133         // before even accepting packets, we query the target to get a sane value for
134         // `self.current_mem_tid`.
135         // NOTE: this will break if extended mode is ever implemented...
136 
137         self.current_mem_tid = match target.base_ops() {
138             BaseOps::SingleThread(_) => SINGLE_THREAD_TID,
139             BaseOps::MultiThread(ops) => {
140                 let mut first_tid = None;
141                 ops.list_active_threads(&mut |tid| {
142                     if first_tid.is_none() {
143                         first_tid = Some(tid);
144                     }
145                 })
146                 .map_err(Error::TargetError)?;
147                 first_tid.ok_or(Error::NoActiveThreads)?
148             }
149         };
150 
151         loop {
152             match Self::recv_packet(conn, target, packet_buffer)? {
153                 Packet::Ack => {}
154                 Packet::Nack => return Err(Error::ClientSentNack),
155                 Packet::Interrupt => {
156                     debug!("<-- interrupt packet");
157                     let mut res = ResponseWriter::new(conn);
158                     res.write_str("S05")?;
159                     res.flush()?;
160                 }
161                 Packet::Command(command) => {
162                     // Acknowledge the command
163                     if !self.no_ack_mode {
164                         conn.write(b'+').map_err(Error::ConnectionRead)?;
165                     }
166 
167                     let mut res = ResponseWriter::new(conn);
168                     let disconnect = match self.handle_command(&mut res, target, command) {
169                         Ok(HandlerStatus::Handled) => None,
170                         Ok(HandlerStatus::NeedsOK) => {
171                             res.write_str("OK")?;
172                             None
173                         }
174                         Ok(HandlerStatus::Disconnect(reason)) => Some(reason),
175                         // HACK: handling this "dummy" error is required as part of the
176                         // `TargetResultExt::handle_error()` machinery.
177                         Err(Error::NonFatalError(code)) => {
178                             res.write_str("E")?;
179                             res.write_num(code)?;
180                             None
181                         }
182                         Err(Error::TargetError(e)) => {
183                             // unlike all other errors which are "unrecoverable" in the sense that
184                             // the GDB session cannot continue, there's still a chance that a target
185                             // might want to keep the debugging session alive to do a "post-mortem"
186                             // analysis. As such, we simply report a standard TRAP stop reason.
187                             let mut res = ResponseWriter::new(conn);
188                             res.write_str("S05")?;
189                             res.flush()?;
190                             return Err(Error::TargetError(e));
191                         }
192                         Err(e) => return Err(e),
193                     };
194 
195                     // HACK: this could be more elegant...
196                     if disconnect != Some(DisconnectReason::Kill) {
197                         res.flush()?;
198                     }
199 
200                     if let Some(disconnect_reason) = disconnect {
201                         return Ok(disconnect_reason);
202                     }
203                 }
204             };
205         }
206     }
207 
recv_packet<'a>( conn: &mut C, target: &mut T, pkt_buf: &'a mut ManagedSlice<u8>, ) -> Result<Packet<'a>, Error<T::Error, C::Error>>208     fn recv_packet<'a>(
209         conn: &mut C,
210         target: &mut T,
211         pkt_buf: &'a mut ManagedSlice<u8>,
212     ) -> Result<Packet<'a>, Error<T::Error, C::Error>> {
213         let header_byte = conn.read().map_err(Error::ConnectionRead)?;
214 
215         // Wrap the buf in a `ManagedVec` to keep the code readable.
216         let mut buf = ManagedVec::new(pkt_buf);
217 
218         buf.clear();
219         buf.push(header_byte)?;
220         if header_byte == b'$' {
221             // read the packet body
222             loop {
223                 let c = conn.read().map_err(Error::ConnectionRead)?;
224                 buf.push(c)?;
225                 if c == b'#' {
226                     break;
227                 }
228             }
229             // read the checksum as well
230             buf.push(conn.read().map_err(Error::ConnectionRead)?)?;
231             buf.push(conn.read().map_err(Error::ConnectionRead)?)?;
232         }
233 
234         match Packet::from_buf(target, pkt_buf.as_mut()) {
235             Ok(packet) => Ok(packet),
236             Err(e) => Err(Error::PacketParse(e)),
237         }
238     }
239 
handle_command( &mut self, res: &mut ResponseWriter<C>, target: &mut T, cmd: Command<'_>, ) -> Result<HandlerStatus, Error<T::Error, C::Error>>240     fn handle_command(
241         &mut self,
242         res: &mut ResponseWriter<C>,
243         target: &mut T,
244         cmd: Command<'_>,
245     ) -> Result<HandlerStatus, Error<T::Error, C::Error>> {
246         match cmd {
247             Command::Unknown(cmd) => {
248                 info!("Unknown command: {}", cmd);
249                 Ok(HandlerStatus::Handled)
250             }
251             Command::Base(cmd) => self.handle_base(res, target, cmd),
252             Command::ExtendedMode(cmd) => self.handle_extended_mode(res, target, cmd),
253             Command::MonitorCmd(cmd) => self.handle_monitor_cmd(res, target, cmd),
254             Command::SectionOffsets(cmd) => self.handle_section_offsets(res, target, cmd),
255         }
256     }
257 
handle_base<'a>( &mut self, res: &mut ResponseWriter<C>, target: &mut T, command: ext::Base<'a>, ) -> Result<HandlerStatus, Error<T::Error, C::Error>>258     fn handle_base<'a>(
259         &mut self,
260         res: &mut ResponseWriter<C>,
261         target: &mut T,
262         command: ext::Base<'a>,
263     ) -> Result<HandlerStatus, Error<T::Error, C::Error>> {
264         let handler_status = match command {
265             // ------------------ Handshaking and Queries ------------------- //
266             ext::Base::qSupported(cmd) => {
267                 // XXX: actually read what the client supports, and enable/disable features
268                 // appropriately
269                 let _features = cmd.features.into_iter();
270 
271                 res.write_str("PacketSize=")?;
272                 res.write_num(self.packet_buffer_len)?;
273 
274                 res.write_str(";vContSupported+")?;
275                 res.write_str(";multiprocess+")?;
276                 res.write_str(";QStartNoAckMode+")?;
277 
278                 if let Some(ops) = target.extended_mode() {
279                     if ops.configure_aslr().is_some() {
280                         res.write_str(";QDisableRandomization+")?;
281                     }
282 
283                     if ops.configure_env().is_some() {
284                         res.write_str(";QEnvironmentHexEncoded+")?;
285                         res.write_str(";QEnvironmentUnset+")?;
286                         res.write_str(";QEnvironmentReset+")?;
287                     }
288 
289                     if ops.configure_startup_shell().is_some() {
290                         res.write_str(";QStartupWithShell+")?;
291                     }
292 
293                     if ops.configure_working_dir().is_some() {
294                         res.write_str(";QSetWorkingDir+")?;
295                     }
296                 }
297 
298                 res.write_str(";swbreak+")?;
299                 if target.hw_breakpoint().is_some() || target.hw_watchpoint().is_some() {
300                     res.write_str(";hwbreak+")?;
301                 }
302 
303                 // TODO: implement conditional breakpoint support (since that's kool).
304                 // res.write_str("ConditionalBreakpoints+;")?;
305 
306                 if T::Arch::target_description_xml().is_some()
307                     || target.target_description_xml_override().is_some()
308                 {
309                     res.write_str(";qXfer:features:read+")?;
310                 }
311 
312                 HandlerStatus::Handled
313             }
314             ext::Base::QStartNoAckMode(_) => {
315                 self.no_ack_mode = true;
316                 HandlerStatus::NeedsOK
317             }
318             ext::Base::qXferFeaturesRead(cmd) => {
319                 #[allow(clippy::redundant_closure)]
320                 let xml = target
321                     .target_description_xml_override()
322                     .map(|ops| ops.target_description_xml())
323                     .or_else(|| T::Arch::target_description_xml());
324 
325                 match xml {
326                     Some(xml) => {
327                         let xml = xml.trim();
328                         if cmd.offset >= xml.len() {
329                             // no more data
330                             res.write_str("l")?;
331                         } else if cmd.offset + cmd.len >= xml.len() {
332                             // last little bit of data
333                             res.write_str("l")?;
334                             res.write_binary(&xml.as_bytes()[cmd.offset..])?
335                         } else {
336                             // still more data
337                             res.write_str("m")?;
338                             res.write_binary(&xml.as_bytes()[cmd.offset..(cmd.offset + cmd.len)])?
339                         }
340                     }
341                     // If the target hasn't provided their own XML, then the initial response to
342                     // "qSupported" wouldn't have included  "qXfer:features:read", and gdb wouldn't
343                     // send this packet unless it was explicitly marked as supported.
344                     None => return Err(Error::PacketUnexpected),
345                 }
346                 HandlerStatus::Handled
347             }
348 
349             // -------------------- "Core" Functionality -------------------- //
350             // TODO: Improve the '?' response based on last-sent stop reason.
351             ext::Base::QuestionMark(_) => {
352                 res.write_str("S05")?;
353                 HandlerStatus::Handled
354             }
355             ext::Base::qAttached(cmd) => {
356                 let is_attached = match target.extended_mode() {
357                     // when _not_ running in extended mode, just report that we're attaching to an
358                     // existing process.
359                     None => true, // assume attached to an existing process
360                     // When running in extended mode, we must defer to the target
361                     Some(ops) => {
362                         let pid: Pid = cmd.pid.ok_or(Error::PacketUnexpected)?;
363 
364                         #[cfg(feature = "alloc")]
365                         {
366                             let _ = ops; // doesn't actually query the target
367                             *self.attached_pids.get(&pid).unwrap_or(&true)
368                         }
369 
370                         #[cfg(not(feature = "alloc"))]
371                         {
372                             ops.query_if_attached(pid).handle_error()?.was_attached()
373                         }
374                     }
375                 };
376                 res.write_str(if is_attached { "1" } else { "0" })?;
377                 HandlerStatus::Handled
378             }
379             ext::Base::g(_) => {
380                 let mut regs: <T::Arch as Arch>::Registers = Default::default();
381                 match target.base_ops() {
382                     BaseOps::SingleThread(ops) => ops.read_registers(&mut regs),
383                     BaseOps::MultiThread(ops) => {
384                         ops.read_registers(&mut regs, self.current_mem_tid)
385                     }
386                 }
387                 .handle_error()?;
388 
389                 let mut err = Ok(());
390                 regs.gdb_serialize(|val| {
391                     let res = match val {
392                         Some(b) => res.write_hex_buf(&[b]),
393                         None => res.write_str("xx"),
394                     };
395                     if let Err(e) = res {
396                         err = Err(e);
397                     }
398                 });
399                 err?;
400                 HandlerStatus::Handled
401             }
402             ext::Base::G(cmd) => {
403                 let mut regs: <T::Arch as Arch>::Registers = Default::default();
404                 regs.gdb_deserialize(cmd.vals)
405                     .map_err(|_| Error::TargetMismatch)?;
406 
407                 match target.base_ops() {
408                     BaseOps::SingleThread(ops) => ops.write_registers(&regs),
409                     BaseOps::MultiThread(ops) => ops.write_registers(&regs, self.current_mem_tid),
410                 }
411                 .handle_error()?;
412 
413                 HandlerStatus::NeedsOK
414             }
415             ext::Base::m(cmd) => {
416                 let buf = cmd.buf;
417                 let addr = <T::Arch as Arch>::Usize::from_be_bytes(cmd.addr)
418                     .ok_or(Error::TargetMismatch)?;
419 
420                 let mut i = 0;
421                 let mut n = cmd.len;
422                 while n != 0 {
423                     let chunk_size = n.min(buf.len());
424 
425                     use num_traits::NumCast;
426 
427                     let addr = addr + NumCast::from(i).ok_or(Error::TargetMismatch)?;
428                     let data = &mut buf[..chunk_size];
429                     match target.base_ops() {
430                         BaseOps::SingleThread(ops) => ops.read_addrs(addr, data),
431                         BaseOps::MultiThread(ops) => {
432                             ops.read_addrs(addr, data, self.current_mem_tid)
433                         }
434                     }
435                     .handle_error()?;
436 
437                     n -= chunk_size;
438                     i += chunk_size;
439 
440                     res.write_hex_buf(data)?;
441                 }
442                 HandlerStatus::Handled
443             }
444             ext::Base::M(cmd) => {
445                 let addr = <T::Arch as Arch>::Usize::from_be_bytes(cmd.addr)
446                     .ok_or(Error::TargetMismatch)?;
447 
448                 match target.base_ops() {
449                     BaseOps::SingleThread(ops) => ops.write_addrs(addr, cmd.val),
450                     BaseOps::MultiThread(ops) => {
451                         ops.write_addrs(addr, cmd.val, self.current_mem_tid)
452                     }
453                 }
454                 .handle_error()?;
455 
456                 HandlerStatus::NeedsOK
457             }
458             ext::Base::k(_) | ext::Base::vKill(_) => {
459                 match target.extended_mode() {
460                     // When not running in extended mode, stop the `GdbStub` and disconnect.
461                     None => HandlerStatus::Disconnect(DisconnectReason::Kill),
462 
463                     // When running in extended mode, a kill command does not necessarily result in
464                     // a disconnect...
465                     Some(ops) => {
466                         let pid = match command {
467                             ext::Base::vKill(cmd) => Some(cmd.pid),
468                             _ => None,
469                         };
470 
471                         let should_terminate = ops.kill(pid).handle_error()?;
472                         if should_terminate.into() {
473                             // manually write OK, since we need to return a DisconnectReason
474                             res.write_str("OK")?;
475                             HandlerStatus::Disconnect(DisconnectReason::Kill)
476                         } else {
477                             HandlerStatus::NeedsOK
478                         }
479                     }
480                 }
481             }
482             ext::Base::D(_) => {
483                 // TODO: plumb-through Pid when exposing full multiprocess + extended mode
484                 res.write_str("OK")?; // manually write OK, since we need to return a DisconnectReason
485                 HandlerStatus::Disconnect(DisconnectReason::Disconnect)
486             }
487             ext::Base::Z(cmd) => {
488                 let addr = <T::Arch as Arch>::Usize::from_be_bytes(cmd.addr)
489                     .ok_or(Error::TargetMismatch)?;
490 
491                 use crate::target::ext::breakpoints::WatchKind::*;
492                 let supported = match cmd.type_ {
493                     0 => (target.sw_breakpoint()).map(|op| op.add_sw_breakpoint(addr)),
494                     1 => (target.hw_breakpoint()).map(|op| op.add_hw_breakpoint(addr)),
495                     2 => (target.hw_watchpoint()).map(|op| op.add_hw_watchpoint(addr, Write)),
496                     3 => (target.hw_watchpoint()).map(|op| op.add_hw_watchpoint(addr, Read)),
497                     4 => (target.hw_watchpoint()).map(|op| op.add_hw_watchpoint(addr, ReadWrite)),
498                     // only 5 types in the protocol
499                     _ => None,
500                 };
501 
502                 match supported {
503                     None => HandlerStatus::Handled,
504                     Some(Err(e)) => {
505                         Err(e).handle_error()?;
506                         HandlerStatus::Handled
507                     }
508                     Some(Ok(true)) => HandlerStatus::NeedsOK,
509                     Some(Ok(false)) => return Err(Error::NonFatalError(22)),
510                 }
511             }
512             ext::Base::z(cmd) => {
513                 let addr = <T::Arch as Arch>::Usize::from_be_bytes(cmd.addr)
514                     .ok_or(Error::TargetMismatch)?;
515 
516                 use crate::target::ext::breakpoints::WatchKind::*;
517                 let supported = match cmd.type_ {
518                     0 => (target.sw_breakpoint()).map(|op| op.remove_sw_breakpoint(addr)),
519                     1 => (target.hw_breakpoint()).map(|op| op.remove_hw_breakpoint(addr)),
520                     2 => (target.hw_watchpoint()).map(|op| op.remove_hw_watchpoint(addr, Write)),
521                     3 => (target.hw_watchpoint()).map(|op| op.remove_hw_watchpoint(addr, Read)),
522                     4 => {
523                         (target.hw_watchpoint()).map(|op| op.remove_hw_watchpoint(addr, ReadWrite))
524                     }
525                     // only 5 types in the protocol
526                     _ => None,
527                 };
528 
529                 match supported {
530                     None => HandlerStatus::Handled,
531                     Some(Err(e)) => {
532                         Err(e).handle_error()?;
533                         HandlerStatus::Handled
534                     }
535                     Some(Ok(true)) => HandlerStatus::NeedsOK,
536                     Some(Ok(false)) => return Err(Error::NonFatalError(22)),
537                 }
538             }
539             ext::Base::p(p) => {
540                 let mut dst = [0u8; 32]; // enough for 256-bit registers
541                 let reg = <T::Arch as Arch>::RegId::from_raw_id(p.reg_id);
542                 let (reg_id, reg_size) = match reg {
543                     Some(v) => v,
544                     // empty packet indicates unrecognized query
545                     None => return Ok(HandlerStatus::Handled),
546                 };
547                 let dst = &mut dst[0..reg_size];
548                 match target.base_ops() {
549                     BaseOps::SingleThread(ops) => ops.read_register(reg_id, dst),
550                     BaseOps::MultiThread(ops) => {
551                         ops.read_register(reg_id, dst, self.current_mem_tid)
552                     }
553                 }
554                 .handle_error()?;
555 
556                 res.write_hex_buf(dst)?;
557                 HandlerStatus::Handled
558             }
559             ext::Base::P(p) => {
560                 let reg = <T::Arch as Arch>::RegId::from_raw_id(p.reg_id);
561                 match reg {
562                     None => return Err(Error::NonFatalError(22)),
563                     Some((reg_id, _)) => match target.base_ops() {
564                         BaseOps::SingleThread(ops) => ops.write_register(reg_id, p.val),
565                         BaseOps::MultiThread(ops) => {
566                             ops.write_register(reg_id, p.val, self.current_mem_tid)
567                         }
568                     }
569                     .handle_error()?,
570                 }
571                 HandlerStatus::NeedsOK
572             }
573             ext::Base::vCont(cmd) => {
574                 use crate::protocol::commands::_vCont::{vCont, VContKind};
575 
576                 let actions = match cmd {
577                     vCont::Query => {
578                         res.write_str("vCont;c;C;s;S")?;
579                         return Ok(HandlerStatus::Handled);
580                     }
581                     vCont::Actions(actions) => actions,
582                 };
583 
584                 // map raw vCont action iterator to a format the `Target` expects
585                 let mut err = Ok(());
586                 let mut actions = actions.into_iter().filter_map(|action| {
587                     let action = match action {
588                         Some(action) => action,
589                         None => {
590                             err = Err(Error::PacketParse(
591                                 crate::protocol::PacketParseError::MalformedCommand,
592                             ));
593                             return None;
594                         }
595                     };
596 
597                     let resume_action = match action.kind {
598                         VContKind::Step => ResumeAction::Step,
599                         VContKind::Continue => ResumeAction::Continue,
600                         _ => {
601                             // there seems to be a GDB bug where it doesn't use `vCont` unless
602                             // `vCont?` returns support for resuming with a signal.
603                             //
604                             // This error case can be removed once "Resume with Signal" is
605                             // implemented
606                             err = Err(Error::ResumeWithSignalUnimplemented);
607                             return None;
608                         }
609                     };
610 
611                     let tid = match action.thread {
612                         Some(thread) => match thread.tid {
613                             IdKind::Any => {
614                                 err = Err(Error::PacketUnexpected);
615                                 return None;
616                             }
617                             IdKind::All => TidSelector::All,
618                             IdKind::WithID(tid) => TidSelector::WithID(tid),
619                         },
620                         // An action with no thread-id matches all threads
621                         None => TidSelector::All,
622                     };
623 
624                     Some((tid, resume_action))
625                 });
626 
627                 let ret = match self.do_vcont(res, target, &mut actions) {
628                     Ok(None) => HandlerStatus::Handled,
629                     Ok(Some(dc)) => HandlerStatus::Disconnect(dc),
630                     Err(e) => return Err(e),
631                 };
632                 err?;
633                 ret
634             }
635             // TODO?: support custom resume addr in 'c' and 's'
636             ext::Base::c(_) => {
637                 match self.do_vcont(
638                     res,
639                     target,
640                     &mut core::iter::once((self.current_resume_tid, ResumeAction::Continue)),
641                 ) {
642                     Ok(None) => HandlerStatus::Handled,
643                     Ok(Some(dc)) => HandlerStatus::Disconnect(dc),
644                     Err(e) => return Err(e),
645                 }
646             }
647             ext::Base::s(_) => {
648                 match self.do_vcont(
649                     res,
650                     target,
651                     &mut core::iter::once((self.current_resume_tid, ResumeAction::Step)),
652                 ) {
653                     Ok(None) => HandlerStatus::Handled,
654                     Ok(Some(dc)) => HandlerStatus::Disconnect(dc),
655                     Err(e) => return Err(e),
656                 }
657             }
658 
659             // ------------------- Multi-threading Support ------------------ //
660             ext::Base::H(cmd) => {
661                 use crate::protocol::commands::_h_upcase::Op;
662                 match cmd.kind {
663                     Op::Other => match cmd.thread.tid {
664                         IdKind::Any => {} // reuse old tid
665                         // "All" threads doesn't make sense for memory accesses
666                         IdKind::All => return Err(Error::PacketUnexpected),
667                         IdKind::WithID(tid) => self.current_mem_tid = tid,
668                     },
669                     // technically, this variant is deprecated in favor of vCont...
670                     Op::StepContinue => match cmd.thread.tid {
671                         IdKind::Any => {} // reuse old tid
672                         IdKind::All => self.current_resume_tid = TidSelector::All,
673                         IdKind::WithID(tid) => self.current_resume_tid = TidSelector::WithID(tid),
674                     },
675                 }
676                 HandlerStatus::NeedsOK
677             }
678             ext::Base::qfThreadInfo(_) => {
679                 res.write_str("m")?;
680 
681                 match target.base_ops() {
682                     BaseOps::SingleThread(_) => res.write_thread_id(ThreadId {
683                         pid: Some(IdKind::WithID(FAKE_PID)),
684                         tid: IdKind::WithID(SINGLE_THREAD_TID),
685                     })?,
686                     BaseOps::MultiThread(ops) => {
687                         let mut err: Result<_, Error<T::Error, C::Error>> = Ok(());
688                         let mut first = true;
689                         ops.list_active_threads(&mut |tid| {
690                             // TODO: replace this with a try block (once stabilized)
691                             let e = (|| {
692                                 if !first {
693                                     res.write_str(",")?
694                                 }
695                                 first = false;
696                                 res.write_thread_id(ThreadId {
697                                     pid: Some(IdKind::WithID(FAKE_PID)),
698                                     tid: IdKind::WithID(tid),
699                                 })?;
700                                 Ok(())
701                             })();
702 
703                             if let Err(e) = e {
704                                 err = Err(e)
705                             }
706                         })
707                         .map_err(Error::TargetError)?;
708                         err?;
709                     }
710                 }
711 
712                 HandlerStatus::Handled
713             }
714             ext::Base::qsThreadInfo(_) => {
715                 res.write_str("l")?;
716                 HandlerStatus::Handled
717             }
718             ext::Base::T(cmd) => {
719                 let alive = match cmd.thread.tid {
720                     IdKind::WithID(tid) => match target.base_ops() {
721                         BaseOps::SingleThread(_) => tid == SINGLE_THREAD_TID,
722                         BaseOps::MultiThread(ops) => {
723                             ops.is_thread_alive(tid).map_err(Error::TargetError)?
724                         }
725                     },
726                     // TODO: double-check if GDB ever sends other variants
727                     // Even after ample testing, this arm has never been hit...
728                     _ => return Err(Error::PacketUnexpected),
729                 };
730                 if alive {
731                     HandlerStatus::NeedsOK
732                 } else {
733                     // any error code will do
734                     return Err(Error::NonFatalError(1));
735                 }
736             }
737         };
738         Ok(handler_status)
739     }
740 
handle_monitor_cmd<'a>( &mut self, res: &mut ResponseWriter<C>, target: &mut T, command: ext::MonitorCmd<'a>, ) -> Result<HandlerStatus, Error<T::Error, C::Error>>741     fn handle_monitor_cmd<'a>(
742         &mut self,
743         res: &mut ResponseWriter<C>,
744         target: &mut T,
745         command: ext::MonitorCmd<'a>,
746     ) -> Result<HandlerStatus, Error<T::Error, C::Error>> {
747         let ops = match target.monitor_cmd() {
748             Some(ops) => ops,
749             None => return Ok(HandlerStatus::Handled),
750         };
751 
752         let handler_status = match command {
753             ext::MonitorCmd::qRcmd(cmd) => {
754                 crate::__dead_code_marker!("qRcmd", "impl");
755 
756                 let mut err: Result<_, Error<T::Error, C::Error>> = Ok(());
757                 let mut callback = |msg: &[u8]| {
758                     // TODO: replace this with a try block (once stabilized)
759                     let e = (|| {
760                         let mut res = ResponseWriter::new(res.as_conn());
761                         res.write_str("O")?;
762                         res.write_hex_buf(msg)?;
763                         res.flush()?;
764                         Ok(())
765                     })();
766 
767                     if let Err(e) = e {
768                         err = Err(e)
769                     }
770                 };
771 
772                 ops.handle_monitor_cmd(cmd.hex_cmd, ConsoleOutput::new(&mut callback))
773                     .map_err(Error::TargetError)?;
774                 err?;
775 
776                 HandlerStatus::NeedsOK
777             }
778         };
779 
780         Ok(handler_status)
781     }
782 
handle_section_offsets( &mut self, res: &mut ResponseWriter<C>, target: &mut T, command: ext::SectionOffsets, ) -> Result<HandlerStatus, Error<T::Error, C::Error>>783     fn handle_section_offsets(
784         &mut self,
785         res: &mut ResponseWriter<C>,
786         target: &mut T,
787         command: ext::SectionOffsets,
788     ) -> Result<HandlerStatus, Error<T::Error, C::Error>> {
789         let ops = match target.section_offsets() {
790             Some(ops) => ops,
791             None => return Ok(HandlerStatus::Handled),
792         };
793 
794         let handler_status = match command {
795             ext::SectionOffsets::qOffsets(_cmd) => {
796                 use crate::target::ext::section_offsets::Offsets;
797 
798                 crate::__dead_code_marker!("qOffsets", "impl");
799 
800                 match ops.get_section_offsets().map_err(Error::TargetError)? {
801                     Offsets::Sections { text, data, bss } => {
802                         res.write_str("Text=")?;
803                         res.write_num(text)?;
804 
805                         res.write_str(";Data=")?;
806                         res.write_num(data)?;
807 
808                         // "Note: while a Bss offset may be included in the response,
809                         // GDB ignores this and instead applies the Data offset to the Bss section."
810                         //
811                         // While this would suggest that it's OK to omit `Bss=` entirely, recent
812                         // versions of GDB seem to require that `Bss=` is present.
813                         //
814                         // See https://github.com/bminor/binutils-gdb/blob/master/gdb/remote.c#L4149-L4159
815                         let bss = bss.unwrap_or(data);
816                         res.write_str(";Bss=")?;
817                         res.write_num(bss)?;
818                     }
819                     Offsets::Segments { text_seg, data_seg } => {
820                         res.write_str("TextSeg=")?;
821                         res.write_num(text_seg)?;
822 
823                         if let Some(data) = data_seg {
824                             res.write_str(";DataSeg=")?;
825                             res.write_num(data)?;
826                         }
827                     }
828                 }
829                 HandlerStatus::Handled
830             }
831         };
832 
833         Ok(handler_status)
834     }
835 
handle_extended_mode<'a>( &mut self, res: &mut ResponseWriter<C>, target: &mut T, command: ext::ExtendedMode<'a>, ) -> Result<HandlerStatus, Error<T::Error, C::Error>>836     fn handle_extended_mode<'a>(
837         &mut self,
838         res: &mut ResponseWriter<C>,
839         target: &mut T,
840         command: ext::ExtendedMode<'a>,
841     ) -> Result<HandlerStatus, Error<T::Error, C::Error>> {
842         let ops = match target.extended_mode() {
843             Some(ops) => ops,
844             None => return Ok(HandlerStatus::Handled),
845         };
846 
847         let handler_status = match command {
848             ext::ExtendedMode::ExclamationMark(_cmd) => {
849                 ops.on_start().map_err(Error::TargetError)?;
850                 HandlerStatus::NeedsOK
851             }
852             ext::ExtendedMode::R(_cmd) => {
853                 ops.restart().map_err(Error::TargetError)?;
854                 HandlerStatus::Handled
855             }
856             ext::ExtendedMode::vAttach(cmd) => {
857                 ops.attach(cmd.pid).handle_error()?;
858 
859                 #[cfg(feature = "alloc")]
860                 self.attached_pids.insert(cmd.pid, true);
861 
862                 // TODO: sends OK when running in Non-Stop mode
863                 HandlerStatus::Handled
864             }
865             ext::ExtendedMode::vRun(cmd) => {
866                 use crate::target::ext::extended_mode::Args;
867 
868                 let mut pid = ops
869                     .run(cmd.filename, Args::new(&mut cmd.args.into_iter()))
870                     .handle_error()?;
871 
872                 // on single-threaded systems, we'll ignore the provided PID and keep
873                 // using the FAKE_PID.
874                 if let BaseOps::SingleThread(_) = target.base_ops() {
875                     pid = FAKE_PID;
876                 }
877 
878                 let _ = pid; // squelch warning on no_std targets
879                 #[cfg(feature = "alloc")]
880                 self.attached_pids.insert(pid, false);
881 
882                 // TODO: send a more descriptive stop packet?
883                 res.write_str("S05")?;
884                 HandlerStatus::Handled
885             }
886             // --------- ASLR --------- //
887             ext::ExtendedMode::QDisableRandomization(cmd) if ops.configure_aslr().is_some() => {
888                 let ops = ops.configure_aslr().unwrap();
889                 ops.cfg_aslr(cmd.value).handle_error()?;
890                 HandlerStatus::NeedsOK
891             }
892             // --------- Environment --------- //
893             ext::ExtendedMode::QEnvironmentHexEncoded(cmd) if ops.configure_env().is_some() => {
894                 let ops = ops.configure_env().unwrap();
895                 ops.set_env(cmd.key, cmd.value).handle_error()?;
896                 HandlerStatus::NeedsOK
897             }
898             ext::ExtendedMode::QEnvironmentUnset(cmd) if ops.configure_env().is_some() => {
899                 let ops = ops.configure_env().unwrap();
900                 ops.remove_env(cmd.key).handle_error()?;
901                 HandlerStatus::NeedsOK
902             }
903             ext::ExtendedMode::QEnvironmentReset(_cmd) if ops.configure_env().is_some() => {
904                 let ops = ops.configure_env().unwrap();
905                 ops.reset_env().handle_error()?;
906                 HandlerStatus::NeedsOK
907             }
908             // --------- Working Dir --------- //
909             ext::ExtendedMode::QSetWorkingDir(cmd) if ops.configure_working_dir().is_some() => {
910                 let ops = ops.configure_working_dir().unwrap();
911                 ops.cfg_working_dir(cmd.dir).handle_error()?;
912                 HandlerStatus::NeedsOK
913             }
914             // --------- Startup Shell --------- //
915             ext::ExtendedMode::QStartupWithShell(cmd)
916                 if ops.configure_startup_shell().is_some() =>
917             {
918                 let ops = ops.configure_startup_shell().unwrap();
919                 ops.cfg_startup_with_shell(cmd.value).handle_error()?;
920                 HandlerStatus::NeedsOK
921             }
922             _ => HandlerStatus::Handled,
923         };
924 
925         Ok(handler_status)
926     }
927 
do_vcont( &mut self, res: &mut ResponseWriter<C>, target: &mut T, actions: &mut dyn Iterator<Item = (TidSelector, ResumeAction)>, ) -> Result<Option<DisconnectReason>, Error<T::Error, C::Error>>928     fn do_vcont(
929         &mut self,
930         res: &mut ResponseWriter<C>,
931         target: &mut T,
932         actions: &mut dyn Iterator<Item = (TidSelector, ResumeAction)>,
933     ) -> Result<Option<DisconnectReason>, Error<T::Error, C::Error>> {
934         let mut err = Ok(());
935 
936         let mut check_gdb_interrupt = || match res.as_conn().peek() {
937             Ok(Some(0x03)) => true, // 0x03 is the interrupt byte
938             Ok(Some(_)) => false,   // it's nothing that can't wait...
939             Ok(None) => false,
940             Err(e) => {
941                 err = Err(Error::ConnectionRead(e));
942                 true // break ASAP if a connection error occurred
943             }
944         };
945 
946         let stop_reason = match target.base_ops() {
947             BaseOps::SingleThread(ops) => ops
948                 .resume(
949                     // TODO?: add a more descriptive error if vcont has multiple threads in
950                     // single-threaded mode?
951                     actions.next().ok_or(Error::PacketUnexpected)?.1,
952                     &mut check_gdb_interrupt,
953                 )
954                 .map_err(Error::TargetError)?
955                 .into(),
956             BaseOps::MultiThread(ops) => ops
957                 .resume(Actions::new(actions), &mut check_gdb_interrupt)
958                 .map_err(Error::TargetError)?,
959         };
960 
961         err?;
962 
963         self.finish_vcont(stop_reason, res)
964     }
965 
966     // DEVNOTE: `do_vcont` and `finish_vcont` could be merged into a single
967     // function, at the expense of slightly larger code. In the future, if the
968     // `vCont` machinery is re-written, there's no reason why the two functions
969     // couldn't be re-merged.
970 
finish_vcont( &mut self, stop_reason: ThreadStopReason<<T::Arch as Arch>::Usize>, res: &mut ResponseWriter<C>, ) -> Result<Option<DisconnectReason>, Error<T::Error, C::Error>>971     fn finish_vcont(
972         &mut self,
973         stop_reason: ThreadStopReason<<T::Arch as Arch>::Usize>,
974         res: &mut ResponseWriter<C>,
975     ) -> Result<Option<DisconnectReason>, Error<T::Error, C::Error>> {
976         match stop_reason {
977             ThreadStopReason::DoneStep | ThreadStopReason::GdbInterrupt => {
978                 res.write_str("S05")?;
979                 Ok(None)
980             }
981             ThreadStopReason::Signal(code) => {
982                 res.write_str("S")?;
983                 res.write_num(code)?;
984                 Ok(None)
985             }
986             ThreadStopReason::Halted => {
987                 res.write_str("W19")?; // SIGSTOP
988                 Ok(Some(DisconnectReason::TargetHalted))
989             }
990             ThreadStopReason::SwBreak(tid)
991             | ThreadStopReason::HwBreak(tid)
992             | ThreadStopReason::Watch { tid, .. } => {
993                 self.current_mem_tid = tid;
994                 self.current_resume_tid = TidSelector::WithID(tid);
995 
996                 res.write_str("T05")?;
997 
998                 res.write_str("thread:")?;
999                 res.write_thread_id(ThreadId {
1000                     pid: Some(IdKind::WithID(FAKE_PID)),
1001                     tid: IdKind::WithID(tid),
1002                 })?;
1003                 res.write_str(";")?;
1004 
1005                 match stop_reason {
1006                     // don't include addr on sw/hw break
1007                     ThreadStopReason::SwBreak(_) => res.write_str("swbreak:")?,
1008                     ThreadStopReason::HwBreak(_) => res.write_str("hwbreak:")?,
1009                     ThreadStopReason::Watch { kind, addr, .. } => {
1010                         use crate::target::ext::breakpoints::WatchKind;
1011                         match kind {
1012                             WatchKind::Write => res.write_str("watch:")?,
1013                             WatchKind::Read => res.write_str("rwatch:")?,
1014                             WatchKind::ReadWrite => res.write_str("awatch:")?,
1015                         }
1016                         res.write_num(addr)?;
1017                     }
1018                     _ => unreachable!(),
1019                 };
1020 
1021                 res.write_str(";")?;
1022                 Ok(None)
1023             }
1024         }
1025     }
1026 }
1027 
1028 use crate::target::ext::base::singlethread::StopReason;
1029 impl<U> From<StopReason<U>> for ThreadStopReason<U> {
from(st_stop_reason: StopReason<U>) -> ThreadStopReason<U>1030     fn from(st_stop_reason: StopReason<U>) -> ThreadStopReason<U> {
1031         match st_stop_reason {
1032             StopReason::DoneStep => ThreadStopReason::DoneStep,
1033             StopReason::GdbInterrupt => ThreadStopReason::GdbInterrupt,
1034             StopReason::Halted => ThreadStopReason::Halted,
1035             StopReason::SwBreak => ThreadStopReason::SwBreak(SINGLE_THREAD_TID),
1036             StopReason::HwBreak => ThreadStopReason::HwBreak(SINGLE_THREAD_TID),
1037             StopReason::Watch { kind, addr } => ThreadStopReason::Watch {
1038                 tid: SINGLE_THREAD_TID,
1039                 kind,
1040                 addr,
1041             },
1042             StopReason::Signal(sig) => ThreadStopReason::Signal(sig),
1043         }
1044     }
1045 }
1046