• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use super::prelude::*;
2 use crate::protocol::commands::ext::Base;
3 
4 use crate::arch::{Arch, Registers};
5 use crate::protocol::{IdKind, SpecificIdKind, SpecificThreadId};
6 use crate::target::ext::base::multithread::ThreadStopReason;
7 use crate::target::ext::base::{BaseOps, GdbInterrupt, ReplayLogPosition, ResumeAction};
8 use crate::{FAKE_PID, SINGLE_THREAD_TID};
9 
10 impl<T: Target, C: Connection> GdbStubImpl<T, C> {
11     #[inline(always)]
get_sane_any_tid(&mut self, target: &mut T) -> Result<Tid, Error<T::Error, C::Error>>12     fn get_sane_any_tid(&mut self, target: &mut T) -> Result<Tid, Error<T::Error, C::Error>> {
13         let tid = match target.base_ops() {
14             BaseOps::SingleThread(_) => SINGLE_THREAD_TID,
15             BaseOps::MultiThread(ops) => {
16                 let mut first_tid = None;
17                 ops.list_active_threads(&mut |tid| {
18                     if first_tid.is_none() {
19                         first_tid = Some(tid);
20                     }
21                 })
22                 .map_err(Error::TargetError)?;
23                 // Note that `Error::NoActiveThreads` shouldn't ever occur, since this method is
24                 // called from the `H` packet handler, which AFAIK is only sent after the GDB
25                 // client has confirmed that a thread / process exists.
26                 //
27                 // If it does, that really sucks, and will require rethinking how to handle "any
28                 // thread" messages.
29                 first_tid.ok_or(Error::NoActiveThreads)?
30             }
31         };
32         Ok(tid)
33     }
34 
handle_base<'a>( &mut self, res: &mut ResponseWriter<C>, target: &mut T, command: Base<'a>, ) -> Result<HandlerStatus, Error<T::Error, C::Error>>35     pub(crate) fn handle_base<'a>(
36         &mut self,
37         res: &mut ResponseWriter<C>,
38         target: &mut T,
39         command: Base<'a>,
40     ) -> Result<HandlerStatus, Error<T::Error, C::Error>> {
41         let handler_status = match command {
42             // ------------------ Handshaking and Queries ------------------- //
43             Base::qSupported(cmd) => {
44                 // XXX: actually read what the client supports, and enable/disable features
45                 // appropriately
46                 let _features = cmd.features.into_iter();
47 
48                 res.write_str("PacketSize=")?;
49                 res.write_num(cmd.packet_buffer_len)?;
50 
51                 res.write_str(";vContSupported+")?;
52                 res.write_str(";multiprocess+")?;
53                 res.write_str(";QStartNoAckMode+")?;
54 
55                 let (reverse_cont, reverse_step) = match target.base_ops() {
56                     BaseOps::MultiThread(ops) => (
57                         ops.support_reverse_cont().is_some(),
58                         ops.support_reverse_step().is_some(),
59                     ),
60                     BaseOps::SingleThread(ops) => (
61                         ops.support_reverse_cont().is_some(),
62                         ops.support_reverse_step().is_some(),
63                     ),
64                 };
65 
66                 if reverse_cont {
67                     res.write_str(";ReverseContinue+")?;
68                 }
69 
70                 if reverse_step {
71                     res.write_str(";ReverseStep+")?;
72                 }
73 
74                 if let Some(ops) = target.extended_mode() {
75                     if ops.configure_aslr().is_some() {
76                         res.write_str(";QDisableRandomization+")?;
77                     }
78 
79                     if ops.configure_env().is_some() {
80                         res.write_str(";QEnvironmentHexEncoded+")?;
81                         res.write_str(";QEnvironmentUnset+")?;
82                         res.write_str(";QEnvironmentReset+")?;
83                     }
84 
85                     if ops.configure_startup_shell().is_some() {
86                         res.write_str(";QStartupWithShell+")?;
87                     }
88 
89                     if ops.configure_working_dir().is_some() {
90                         res.write_str(";QSetWorkingDir+")?;
91                     }
92                 }
93 
94                 if let Some(ops) = target.breakpoints() {
95                     if ops.sw_breakpoint().is_some() {
96                         res.write_str(";swbreak+")?;
97                     }
98 
99                     if ops.hw_breakpoint().is_some() || ops.hw_watchpoint().is_some() {
100                         res.write_str(";hwbreak+")?;
101                     }
102                 }
103 
104                 if T::Arch::target_description_xml().is_some()
105                     || target.target_description_xml_override().is_some()
106                 {
107                     res.write_str(";qXfer:features:read+")?;
108                 }
109 
110                 HandlerStatus::Handled
111             }
112             Base::QStartNoAckMode(_) => {
113                 self.no_ack_mode = true;
114                 HandlerStatus::NeedsOk
115             }
116             Base::qXferFeaturesRead(cmd) => {
117                 #[allow(clippy::redundant_closure)]
118                 let xml = target
119                     .target_description_xml_override()
120                     .map(|ops| ops.target_description_xml())
121                     .or_else(|| T::Arch::target_description_xml());
122 
123                 match xml {
124                     Some(xml) => {
125                         let xml = xml.trim();
126                         if cmd.offset >= xml.len() {
127                             // no more data
128                             res.write_str("l")?;
129                         } else if cmd.offset + cmd.len >= xml.len() {
130                             // last little bit of data
131                             res.write_str("l")?;
132                             res.write_binary(&xml.as_bytes()[cmd.offset..])?
133                         } else {
134                             // still more data
135                             res.write_str("m")?;
136                             res.write_binary(&xml.as_bytes()[cmd.offset..(cmd.offset + cmd.len)])?
137                         }
138                     }
139                     // If the target hasn't provided their own XML, then the initial response to
140                     // "qSupported" wouldn't have included  "qXfer:features:read", and gdb wouldn't
141                     // send this packet unless it was explicitly marked as supported.
142                     None => return Err(Error::PacketUnexpected),
143                 }
144                 HandlerStatus::Handled
145             }
146 
147             // -------------------- "Core" Functionality -------------------- //
148             // TODO: Improve the '?' response based on last-sent stop reason.
149             // this will be particularly relevant when working on non-stop mode.
150             Base::QuestionMark(_) => {
151                 res.write_str("S05")?;
152                 HandlerStatus::Handled
153             }
154             Base::qAttached(cmd) => {
155                 let is_attached = match target.extended_mode() {
156                     // when _not_ running in extended mode, just report that we're attaching to an
157                     // existing process.
158                     None => true, // assume attached to an existing process
159                     // When running in extended mode, we must defer to the target
160                     Some(ops) => {
161                         let pid: Pid = cmd.pid.ok_or(Error::PacketUnexpected)?;
162                         ops.query_if_attached(pid).handle_error()?.was_attached()
163                     }
164                 };
165                 res.write_str(if is_attached { "1" } else { "0" })?;
166                 HandlerStatus::Handled
167             }
168             Base::g(_) => {
169                 let mut regs: <T::Arch as Arch>::Registers = Default::default();
170                 match target.base_ops() {
171                     BaseOps::SingleThread(ops) => ops.read_registers(&mut regs),
172                     BaseOps::MultiThread(ops) => {
173                         ops.read_registers(&mut regs, self.current_mem_tid)
174                     }
175                 }
176                 .handle_error()?;
177 
178                 let mut err = Ok(());
179                 regs.gdb_serialize(|val| {
180                     let res = match val {
181                         Some(b) => res.write_hex_buf(&[b]),
182                         None => res.write_str("xx"),
183                     };
184                     if let Err(e) = res {
185                         err = Err(e);
186                     }
187                 });
188                 err?;
189                 HandlerStatus::Handled
190             }
191             Base::G(cmd) => {
192                 let mut regs: <T::Arch as Arch>::Registers = Default::default();
193                 regs.gdb_deserialize(cmd.vals)
194                     .map_err(|_| Error::TargetMismatch)?;
195 
196                 match target.base_ops() {
197                     BaseOps::SingleThread(ops) => ops.write_registers(&regs),
198                     BaseOps::MultiThread(ops) => ops.write_registers(&regs, self.current_mem_tid),
199                 }
200                 .handle_error()?;
201 
202                 HandlerStatus::NeedsOk
203             }
204             Base::m(cmd) => {
205                 let buf = cmd.buf;
206                 let addr = <T::Arch as Arch>::Usize::from_be_bytes(cmd.addr)
207                     .ok_or(Error::TargetMismatch)?;
208 
209                 let mut i = 0;
210                 let mut n = cmd.len;
211                 while n != 0 {
212                     let chunk_size = n.min(buf.len());
213 
214                     use num_traits::NumCast;
215 
216                     let addr = addr + NumCast::from(i).ok_or(Error::TargetMismatch)?;
217                     let data = &mut buf[..chunk_size];
218                     match target.base_ops() {
219                         BaseOps::SingleThread(ops) => ops.read_addrs(addr, data),
220                         BaseOps::MultiThread(ops) => {
221                             ops.read_addrs(addr, data, self.current_mem_tid)
222                         }
223                     }
224                     .handle_error()?;
225 
226                     n -= chunk_size;
227                     i += chunk_size;
228 
229                     res.write_hex_buf(data)?;
230                 }
231                 HandlerStatus::Handled
232             }
233             Base::M(cmd) => {
234                 let addr = <T::Arch as Arch>::Usize::from_be_bytes(cmd.addr)
235                     .ok_or(Error::TargetMismatch)?;
236 
237                 match target.base_ops() {
238                     BaseOps::SingleThread(ops) => ops.write_addrs(addr, cmd.val),
239                     BaseOps::MultiThread(ops) => {
240                         ops.write_addrs(addr, cmd.val, self.current_mem_tid)
241                     }
242                 }
243                 .handle_error()?;
244 
245                 HandlerStatus::NeedsOk
246             }
247             Base::k(_) | Base::vKill(_) => {
248                 match target.extended_mode() {
249                     // When not running in extended mode, stop the `GdbStub` and disconnect.
250                     None => HandlerStatus::Disconnect(DisconnectReason::Kill),
251 
252                     // When running in extended mode, a kill command does not necessarily result in
253                     // a disconnect...
254                     Some(ops) => {
255                         let pid = match command {
256                             Base::vKill(cmd) => Some(cmd.pid),
257                             _ => None,
258                         };
259 
260                         let should_terminate = ops.kill(pid).handle_error()?;
261                         if should_terminate.into_bool() {
262                             // manually write OK, since we need to return a DisconnectReason
263                             res.write_str("OK")?;
264                             HandlerStatus::Disconnect(DisconnectReason::Kill)
265                         } else {
266                             HandlerStatus::NeedsOk
267                         }
268                     }
269                 }
270             }
271             Base::D(_) => {
272                 // TODO: plumb-through Pid when exposing full multiprocess + extended mode
273                 res.write_str("OK")?; // manually write OK, since we need to return a DisconnectReason
274                 HandlerStatus::Disconnect(DisconnectReason::Disconnect)
275             }
276             Base::vCont(cmd) => {
277                 use crate::protocol::commands::_vCont::vCont;
278                 match cmd {
279                     vCont::Query => {
280                         res.write_str("vCont;c;C;s;S")?;
281                         if match target.base_ops() {
282                             BaseOps::SingleThread(ops) => ops.support_resume_range_step().is_some(),
283                             BaseOps::MultiThread(ops) => ops.support_range_step().is_some(),
284                         } {
285                             res.write_str(";r")?;
286                         }
287                         HandlerStatus::Handled
288                     }
289                     vCont::Actions(actions) => self.do_vcont(res, target, actions)?,
290                 }
291             }
292             // TODO?: support custom resume addr in 'c' and 's'
293             //
294             // unfortunately, this wouldn't be a particularly easy thing to implement, since the
295             // vCont packet doesn't natively support custom resume addresses. This leaves a few
296             // options for the implementation:
297             //
298             // 1. Adding new ResumeActions (i.e: ContinueWithAddr(U) and StepWithAddr(U))
299             // 2. Automatically calling `read_registers`, updating the `pc`, and calling
300             //    `write_registers` prior to resuming.
301             //    - will require adding some sort of `get_pc_mut` method to the `Registers` trait.
302             //
303             // Option 1 is easier to implement, but puts more burden on the implementor. Option 2
304             // will require more effort to implement (and will be less performant), but it will hide
305             // this protocol wart from the end user.
306             //
307             // Oh, one more thought - there's a subtle pitfall to watch out for if implementing
308             // Option 1: if the target is using conditional breakpoints, `do_vcont` has to be
309             // modified to only pass the resume with address variants on the _first_ iteration
310             // through the loop.
311             Base::c(_) => {
312                 use crate::protocol::commands::_vCont::Actions;
313 
314                 self.do_vcont(
315                     res,
316                     target,
317                     Actions::new_continue(SpecificThreadId {
318                         pid: None,
319                         tid: self.current_resume_tid,
320                     }),
321                 )?
322             }
323             Base::s(_) => {
324                 use crate::protocol::commands::_vCont::Actions;
325 
326                 self.do_vcont(
327                     res,
328                     target,
329                     Actions::new_step(SpecificThreadId {
330                         pid: None,
331                         tid: self.current_resume_tid,
332                     }),
333                 )?
334             }
335 
336             // ------------------- Multi-threading Support ------------------ //
337             Base::H(cmd) => {
338                 use crate::protocol::commands::_h_upcase::Op;
339                 match cmd.kind {
340                     Op::Other => match cmd.thread.tid {
341                         IdKind::Any => self.current_mem_tid = self.get_sane_any_tid(target)?,
342                         // "All" threads doesn't make sense for memory accesses
343                         IdKind::All => return Err(Error::PacketUnexpected),
344                         IdKind::WithId(tid) => self.current_mem_tid = tid,
345                     },
346                     // technically, this variant is deprecated in favor of vCont...
347                     Op::StepContinue => match cmd.thread.tid {
348                         IdKind::Any => {
349                             self.current_resume_tid =
350                                 SpecificIdKind::WithId(self.get_sane_any_tid(target)?)
351                         }
352                         IdKind::All => self.current_resume_tid = SpecificIdKind::All,
353                         IdKind::WithId(tid) => {
354                             self.current_resume_tid = SpecificIdKind::WithId(tid)
355                         }
356                     },
357                 }
358                 HandlerStatus::NeedsOk
359             }
360             Base::qfThreadInfo(_) => {
361                 res.write_str("m")?;
362 
363                 match target.base_ops() {
364                     BaseOps::SingleThread(_) => res.write_specific_thread_id(SpecificThreadId {
365                         pid: Some(SpecificIdKind::WithId(FAKE_PID)),
366                         tid: SpecificIdKind::WithId(SINGLE_THREAD_TID),
367                     })?,
368                     BaseOps::MultiThread(ops) => {
369                         let mut err: Result<_, Error<T::Error, C::Error>> = Ok(());
370                         let mut first = true;
371                         ops.list_active_threads(&mut |tid| {
372                             // TODO: replace this with a try block (once stabilized)
373                             let e = (|| {
374                                 if !first {
375                                     res.write_str(",")?
376                                 }
377                                 first = false;
378                                 res.write_specific_thread_id(SpecificThreadId {
379                                     pid: Some(SpecificIdKind::WithId(FAKE_PID)),
380                                     tid: SpecificIdKind::WithId(tid),
381                                 })?;
382                                 Ok(())
383                             })();
384 
385                             if let Err(e) = e {
386                                 err = Err(e)
387                             }
388                         })
389                         .map_err(Error::TargetError)?;
390                         err?;
391                     }
392                 }
393 
394                 HandlerStatus::Handled
395             }
396             Base::qsThreadInfo(_) => {
397                 res.write_str("l")?;
398                 HandlerStatus::Handled
399             }
400             Base::T(cmd) => {
401                 let alive = match cmd.thread.tid {
402                     IdKind::WithId(tid) => match target.base_ops() {
403                         BaseOps::SingleThread(_) => tid == SINGLE_THREAD_TID,
404                         BaseOps::MultiThread(ops) => {
405                             ops.is_thread_alive(tid).map_err(Error::TargetError)?
406                         }
407                     },
408                     // TODO: double-check if GDB ever sends other variants
409                     // Even after ample testing, this arm has never been hit...
410                     _ => return Err(Error::PacketUnexpected),
411                 };
412                 if alive {
413                     HandlerStatus::NeedsOk
414                 } else {
415                     // any error code will do
416                     return Err(Error::NonFatalError(1));
417                 }
418             }
419         };
420         Ok(handler_status)
421     }
422 
423     #[allow(clippy::type_complexity)]
do_vcont_single_thread( ops: &mut dyn crate::target::ext::base::singlethread::SingleThreadOps< Arch = T::Arch, Error = T::Error, >, res: &mut ResponseWriter<C>, actions: &crate::protocol::commands::_vCont::Actions, ) -> Result<ThreadStopReason<<T::Arch as Arch>::Usize>, Error<T::Error, C::Error>>424     fn do_vcont_single_thread(
425         ops: &mut dyn crate::target::ext::base::singlethread::SingleThreadOps<
426             Arch = T::Arch,
427             Error = T::Error,
428         >,
429         res: &mut ResponseWriter<C>,
430         actions: &crate::protocol::commands::_vCont::Actions,
431     ) -> Result<ThreadStopReason<<T::Arch as Arch>::Usize>, Error<T::Error, C::Error>> {
432         use crate::protocol::commands::_vCont::VContKind;
433 
434         let mut err = Ok(());
435         let mut check_gdb_interrupt = || match res.as_conn().peek() {
436             Ok(Some(0x03)) => true, // 0x03 is the interrupt byte
437             Ok(Some(_)) => false,   // it's nothing that can't wait...
438             Ok(None) => false,
439             Err(e) => {
440                 err = Err(Error::ConnectionRead(e));
441                 true // break ASAP if a connection error occurred
442             }
443         };
444 
445         let mut actions = actions.iter();
446         let first_action = actions
447             .next()
448             .ok_or(Error::PacketParse(
449                 crate::protocol::PacketParseError::MalformedCommand,
450             ))?
451             .ok_or(Error::PacketParse(
452                 crate::protocol::PacketParseError::MalformedCommand,
453             ))?;
454 
455         let invalid_second_action = match actions.next() {
456             None => false,
457             Some(act) => match act {
458                 None => {
459                     return Err(Error::PacketParse(
460                         crate::protocol::PacketParseError::MalformedCommand,
461                     ))
462                 }
463                 Some(act) => !matches!(act.kind, VContKind::Continue),
464             },
465         };
466 
467         if invalid_second_action || actions.next().is_some() {
468             return Err(Error::PacketUnexpected);
469         }
470 
471         let action = match first_action.kind {
472             VContKind::Step => ResumeAction::Step,
473             VContKind::Continue => ResumeAction::Continue,
474             VContKind::StepWithSig(sig) => ResumeAction::StepWithSignal(sig),
475             VContKind::ContinueWithSig(sig) => ResumeAction::ContinueWithSignal(sig),
476             VContKind::RangeStep(start, end) => {
477                 if let Some(ops) = ops.support_resume_range_step() {
478                     let start = start.decode().map_err(|_| Error::TargetMismatch)?;
479                     let end = end.decode().map_err(|_| Error::TargetMismatch)?;
480 
481                     let ret = ops
482                         .resume_range_step(start, end, GdbInterrupt::new(&mut check_gdb_interrupt))
483                         .map_err(Error::TargetError)?
484                         .into();
485                     err?;
486                     return Ok(ret);
487                 } else {
488                     return Err(Error::PacketUnexpected);
489                 }
490             }
491             // TODO: update this case when non-stop mode is implemented
492             VContKind::Stop => return Err(Error::PacketUnexpected),
493         };
494 
495         let ret = ops
496             .resume(action, GdbInterrupt::new(&mut check_gdb_interrupt))
497             .map_err(Error::TargetError)?
498             .into();
499         err?;
500         Ok(ret)
501     }
502 
503     #[allow(clippy::type_complexity)]
do_vcont_multi_thread( ops: &mut dyn crate::target::ext::base::multithread::MultiThreadOps< Arch = T::Arch, Error = T::Error, >, res: &mut ResponseWriter<C>, actions: &crate::protocol::commands::_vCont::Actions, ) -> Result<ThreadStopReason<<T::Arch as Arch>::Usize>, Error<T::Error, C::Error>>504     fn do_vcont_multi_thread(
505         ops: &mut dyn crate::target::ext::base::multithread::MultiThreadOps<
506             Arch = T::Arch,
507             Error = T::Error,
508         >,
509         res: &mut ResponseWriter<C>,
510         actions: &crate::protocol::commands::_vCont::Actions,
511     ) -> Result<ThreadStopReason<<T::Arch as Arch>::Usize>, Error<T::Error, C::Error>> {
512         // this is a pretty arbitrary choice, but it seems reasonable for most cases.
513         let mut default_resume_action = ResumeAction::Continue;
514 
515         ops.clear_resume_actions().map_err(Error::TargetError)?;
516 
517         for action in actions.iter() {
518             use crate::protocol::commands::_vCont::VContKind;
519 
520             let action = action.ok_or(Error::PacketParse(
521                 crate::protocol::PacketParseError::MalformedCommand,
522             ))?;
523 
524             let resume_action = match action.kind {
525                 VContKind::Step => ResumeAction::Step,
526                 VContKind::Continue => ResumeAction::Continue,
527                 // there seems to be a GDB bug where it doesn't use `vCont` unless
528                 // `vCont?` returns support for resuming with a signal.
529                 VContKind::StepWithSig(sig) => ResumeAction::StepWithSignal(sig),
530                 VContKind::ContinueWithSig(sig) => ResumeAction::ContinueWithSignal(sig),
531                 VContKind::RangeStep(start, end) => {
532                     if let Some(ops) = ops.support_range_step() {
533                         match action.thread.map(|thread| thread.tid) {
534                             // An action with no thread-id matches all threads
535                             None | Some(SpecificIdKind::All) => {
536                                 return Err(Error::PacketUnexpected)
537                             }
538                             Some(SpecificIdKind::WithId(tid)) => {
539                                 let start = start.decode().map_err(|_| Error::TargetMismatch)?;
540                                 let end = end.decode().map_err(|_| Error::TargetMismatch)?;
541 
542                                 ops.set_resume_action_range_step(tid, start, end)
543                                     .map_err(Error::TargetError)?;
544                                 continue;
545                             }
546                         };
547                     } else {
548                         return Err(Error::PacketUnexpected);
549                     }
550                 }
551                 // TODO: update this case when non-stop mode is implemented
552                 VContKind::Stop => return Err(Error::PacketUnexpected),
553             };
554 
555             match action.thread.map(|thread| thread.tid) {
556                 // An action with no thread-id matches all threads
557                 None | Some(SpecificIdKind::All) => default_resume_action = resume_action,
558                 Some(SpecificIdKind::WithId(tid)) => ops
559                     .set_resume_action(tid, resume_action)
560                     .map_err(Error::TargetError)?,
561             };
562         }
563 
564         let mut err = Ok(());
565         let mut check_gdb_interrupt = || match res.as_conn().peek() {
566             Ok(Some(0x03)) => true, // 0x03 is the interrupt byte
567             Ok(Some(_)) => false,   // it's nothing that can't wait...
568             Ok(None) => false,
569             Err(e) => {
570                 err = Err(Error::ConnectionRead(e));
571                 true // break ASAP if a connection error occurred
572             }
573         };
574 
575         let ret = ops
576             .resume(
577                 default_resume_action,
578                 GdbInterrupt::new(&mut check_gdb_interrupt),
579             )
580             .map_err(Error::TargetError)?;
581 
582         err?;
583 
584         Ok(ret)
585     }
586 
do_vcont( &mut self, res: &mut ResponseWriter<C>, target: &mut T, actions: crate::protocol::commands::_vCont::Actions, ) -> Result<HandlerStatus, Error<T::Error, C::Error>>587     fn do_vcont(
588         &mut self,
589         res: &mut ResponseWriter<C>,
590         target: &mut T,
591         actions: crate::protocol::commands::_vCont::Actions,
592     ) -> Result<HandlerStatus, Error<T::Error, C::Error>> {
593         loop {
594             let stop_reason = match target.base_ops() {
595                 BaseOps::SingleThread(ops) => Self::do_vcont_single_thread(ops, res, &actions)?,
596                 BaseOps::MultiThread(ops) => Self::do_vcont_multi_thread(ops, res, &actions)?,
597             };
598 
599             match self.finish_exec(res, target, stop_reason)? {
600                 Some(status) => break Ok(status),
601                 None => continue,
602             }
603         }
604     }
605 
write_break_common( &mut self, res: &mut ResponseWriter<C>, tid: Tid, ) -> Result<(), Error<T::Error, C::Error>>606     fn write_break_common(
607         &mut self,
608         res: &mut ResponseWriter<C>,
609         tid: Tid,
610     ) -> Result<(), Error<T::Error, C::Error>> {
611         self.current_mem_tid = tid;
612         self.current_resume_tid = SpecificIdKind::WithId(tid);
613 
614         res.write_str("T05")?;
615 
616         res.write_str("thread:")?;
617         res.write_specific_thread_id(SpecificThreadId {
618             pid: Some(SpecificIdKind::WithId(FAKE_PID)),
619             tid: SpecificIdKind::WithId(tid),
620         })?;
621         res.write_str(";")?;
622 
623         Ok(())
624     }
625 
finish_exec( &mut self, res: &mut ResponseWriter<C>, target: &mut T, stop_reason: ThreadStopReason<<T::Arch as Arch>::Usize>, ) -> Result<Option<HandlerStatus>, Error<T::Error, C::Error>>626     pub(super) fn finish_exec(
627         &mut self,
628         res: &mut ResponseWriter<C>,
629         target: &mut T,
630         stop_reason: ThreadStopReason<<T::Arch as Arch>::Usize>,
631     ) -> Result<Option<HandlerStatus>, Error<T::Error, C::Error>> {
632         macro_rules! guard_reverse_exec {
633             () => {{
634                 let (reverse_cont, reverse_step) = match target.base_ops() {
635                     BaseOps::MultiThread(ops) => (
636                         ops.support_reverse_cont().is_some(),
637                         ops.support_reverse_step().is_some(),
638                     ),
639                     BaseOps::SingleThread(ops) => (
640                         ops.support_reverse_cont().is_some(),
641                         ops.support_reverse_step().is_some(),
642                     ),
643                 };
644                 reverse_cont || reverse_step
645             }};
646         }
647 
648         macro_rules! guard_break {
649             ($op:ident) => {
650                 target.breakpoints().and_then(|ops| ops.$op()).is_some()
651             };
652         }
653 
654         let status = match stop_reason {
655             ThreadStopReason::DoneStep | ThreadStopReason::GdbInterrupt => {
656                 res.write_str("S05")?;
657                 HandlerStatus::Handled
658             }
659             ThreadStopReason::Signal(sig) => {
660                 res.write_str("S")?;
661                 res.write_num(sig)?;
662                 HandlerStatus::Handled
663             }
664             ThreadStopReason::Exited(code) => {
665                 res.write_str("W")?;
666                 res.write_num(code)?;
667                 HandlerStatus::Disconnect(DisconnectReason::TargetExited(code))
668             }
669             ThreadStopReason::Terminated(sig) => {
670                 res.write_str("X")?;
671                 res.write_num(sig)?;
672                 HandlerStatus::Disconnect(DisconnectReason::TargetTerminated(sig))
673             }
674             ThreadStopReason::SwBreak(tid) if guard_break!(sw_breakpoint) => {
675                 crate::__dead_code_marker!("sw_breakpoint", "stop_reason");
676 
677                 self.write_break_common(res, tid)?;
678                 res.write_str("swbreak:;")?;
679                 HandlerStatus::Handled
680             }
681             ThreadStopReason::HwBreak(tid) if guard_break!(hw_breakpoint) => {
682                 crate::__dead_code_marker!("hw_breakpoint", "stop_reason");
683 
684                 self.write_break_common(res, tid)?;
685                 res.write_str("hwbreak:;")?;
686                 HandlerStatus::Handled
687             }
688             ThreadStopReason::Watch { tid, kind, addr } if guard_break!(hw_watchpoint) => {
689                 crate::__dead_code_marker!("hw_watchpoint", "stop_reason");
690 
691                 self.write_break_common(res, tid)?;
692 
693                 use crate::target::ext::breakpoints::WatchKind;
694                 match kind {
695                     WatchKind::Write => res.write_str("watch:")?,
696                     WatchKind::Read => res.write_str("rwatch:")?,
697                     WatchKind::ReadWrite => res.write_str("awatch:")?,
698                 }
699                 res.write_num(addr)?;
700                 res.write_str(";")?;
701                 HandlerStatus::Handled
702             }
703             ThreadStopReason::ReplayLog(pos) if guard_reverse_exec!() => {
704                 crate::__dead_code_marker!("reverse_exec", "stop_reason");
705 
706                 res.write_str("T05")?;
707 
708                 res.write_str("replaylog:")?;
709                 res.write_str(match pos {
710                     ReplayLogPosition::Begin => "begin",
711                     ReplayLogPosition::End => "end",
712                 })?;
713                 res.write_str(";")?;
714 
715                 HandlerStatus::Handled
716             }
717             _ => return Err(Error::UnsupportedStopReason),
718         };
719 
720         Ok(Some(status))
721     }
722 }
723 
724 use crate::target::ext::base::singlethread::StopReason;
725 impl<U> From<StopReason<U>> for ThreadStopReason<U> {
from(st_stop_reason: StopReason<U>) -> ThreadStopReason<U>726     fn from(st_stop_reason: StopReason<U>) -> ThreadStopReason<U> {
727         match st_stop_reason {
728             StopReason::DoneStep => ThreadStopReason::DoneStep,
729             StopReason::GdbInterrupt => ThreadStopReason::GdbInterrupt,
730             StopReason::Exited(code) => ThreadStopReason::Exited(code),
731             StopReason::Terminated(sig) => ThreadStopReason::Terminated(sig),
732             StopReason::SwBreak => ThreadStopReason::SwBreak(SINGLE_THREAD_TID),
733             StopReason::HwBreak => ThreadStopReason::HwBreak(SINGLE_THREAD_TID),
734             StopReason::Watch { kind, addr } => ThreadStopReason::Watch {
735                 tid: SINGLE_THREAD_TID,
736                 kind,
737                 addr,
738             },
739             StopReason::Signal(sig) => ThreadStopReason::Signal(sig),
740             StopReason::ReplayLog(pos) => ThreadStopReason::ReplayLog(pos),
741         }
742     }
743 }
744