1 use super::prelude::*;
2 use super::DisconnectReason;
3 use crate::arch::Arch;
4 use crate::common::Signal;
5 use crate::common::Tid;
6 use crate::protocol::commands::_vCont::Actions;
7 use crate::protocol::commands::ext::Resume;
8 use crate::protocol::SpecificIdKind;
9 use crate::protocol::SpecificThreadId;
10 use crate::stub::MultiThreadStopReason;
11 use crate::target::ext::base::reverse_exec::ReplayLogPosition;
12 use crate::target::ext::base::ResumeOps;
13 use crate::target::ext::catch_syscalls::CatchSyscallPosition;
14 
15 impl<T: Target, C: Connection> GdbStubImpl<T, C> {
handle_stop_resume( &mut self, res: &mut ResponseWriter<'_, C>, target: &mut T, command: Resume<'_>, ) -> Result<HandlerStatus, Error<T::Error, C::Error>>16     pub(crate) fn handle_stop_resume(
17         &mut self,
18         res: &mut ResponseWriter<'_, C>,
19         target: &mut T,
20         command: Resume<'_>,
21     ) -> Result<HandlerStatus, Error<T::Error, C::Error>> {
22         let mut ops = match target.base_ops().resume_ops() {
23             Some(ops) => ops,
24             None => return Ok(HandlerStatus::Handled),
25         };
26 
27         let actions = match command {
28             Resume::vCont(cmd) => {
29                 use crate::protocol::commands::_vCont::vCont;
30                 match cmd {
31                     vCont::Query => {
32                         // Continue is part of the base protocol
33                         res.write_str("vCont;c;C")?;
34 
35                         // Single stepping is optional
36                         if match &mut ops {
37                             ResumeOps::SingleThread(ops) => ops.support_single_step().is_some(),
38                             ResumeOps::MultiThread(ops) => ops.support_single_step().is_some(),
39                         } {
40                             res.write_str(";s;S")?;
41                         }
42 
43                         // Range stepping is optional
44                         if match &mut ops {
45                             ResumeOps::SingleThread(ops) => ops.support_range_step().is_some(),
46                             ResumeOps::MultiThread(ops) => ops.support_range_step().is_some(),
47                         } {
48                             res.write_str(";r")?;
49                         }
50 
51                         // doesn't actually invoke vCont
52                         return Ok(HandlerStatus::Handled);
53                     }
54                     vCont::Actions(actions) => actions,
55                 }
56             }
57             // TODO?: support custom resume addr in 'c' and 's'
58             //
59             // vCont doesn't have a notion of "resume addr", and since the implementation of these
60             // packets reuse vCont infrastructure, supporting this obscure feature will be a bit
61             // annoying...
62             //
63             // TODO: add `support_legacy_s_c_packets` flag (similar to `use_X_packet`)
64             Resume::c(cmd) => {
65                 let _addr = cmd.addr;
66                 Actions::new_continue(SpecificThreadId {
67                     pid: None,
68                     tid: self.current_resume_tid,
69                 })
70             }
71             Resume::s(cmd) => {
72                 let _addr = cmd.addr;
73                 Actions::new_step(SpecificThreadId {
74                     pid: None,
75                     tid: self.current_resume_tid,
76                 })
77             }
78         };
79 
80         self.do_vcont(ops, actions)
81     }
82 
do_vcont_single_thread( ops: &mut dyn crate::target::ext::base::singlethread::SingleThreadResume< Arch = T::Arch, Error = T::Error, >, actions: &Actions<'_>, ) -> Result<(), Error<T::Error, C::Error>>83     fn do_vcont_single_thread(
84         ops: &mut dyn crate::target::ext::base::singlethread::SingleThreadResume<
85             Arch = T::Arch,
86             Error = T::Error,
87         >,
88         actions: &Actions<'_>,
89     ) -> Result<(), Error<T::Error, C::Error>> {
90         use crate::protocol::commands::_vCont::VContKind;
91 
92         let mut actions = actions.iter();
93         let first_action = actions
94             .next()
95             .ok_or(Error::PacketParse(
96                 crate::protocol::PacketParseError::MalformedCommand,
97             ))?
98             .ok_or(Error::PacketParse(
99                 crate::protocol::PacketParseError::MalformedCommand,
100             ))?;
101 
102         let invalid_second_action = match actions.next() {
103             None => false,
104             Some(act) => match act {
105                 None => {
106                     return Err(Error::PacketParse(
107                         crate::protocol::PacketParseError::MalformedCommand,
108                     ))
109                 }
110                 Some(act) => !matches!(act.kind, VContKind::Continue),
111             },
112         };
113 
114         if invalid_second_action || actions.next().is_some() {
115             return Err(Error::PacketUnexpected);
116         }
117 
118         match first_action.kind {
119             VContKind::Continue | VContKind::ContinueWithSig(_) => {
120                 let signal = match first_action.kind {
121                     VContKind::ContinueWithSig(sig) => Some(sig),
122                     _ => None,
123                 };
124 
125                 ops.resume(signal).map_err(Error::TargetError)?;
126                 Ok(())
127             }
128             VContKind::Step | VContKind::StepWithSig(_) if ops.support_single_step().is_some() => {
129                 let ops = ops.support_single_step().unwrap();
130 
131                 let signal = match first_action.kind {
132                     VContKind::StepWithSig(sig) => Some(sig),
133                     _ => None,
134                 };
135 
136                 ops.step(signal).map_err(Error::TargetError)?;
137                 Ok(())
138             }
139             VContKind::RangeStep(start, end) if ops.support_range_step().is_some() => {
140                 let ops = ops.support_range_step().unwrap();
141 
142                 let start = start.decode().map_err(|_| Error::TargetMismatch)?;
143                 let end = end.decode().map_err(|_| Error::TargetMismatch)?;
144 
145                 ops.resume_range_step(start, end)
146                     .map_err(Error::TargetError)?;
147                 Ok(())
148             }
149             // TODO: update this case when non-stop mode is implemented
150             VContKind::Stop => Err(Error::PacketUnexpected),
151 
152             // Instead of using `_ =>`, explicitly list out any remaining unguarded cases.
153             VContKind::RangeStep(..) | VContKind::Step | VContKind::StepWithSig(..) => {
154                 error!("GDB client sent resume action not reported by `vCont?`");
155                 Err(Error::PacketUnexpected)
156             }
157         }
158     }
159 
do_vcont_multi_thread( ops: &mut dyn crate::target::ext::base::multithread::MultiThreadResume< Arch = T::Arch, Error = T::Error, >, actions: &Actions<'_>, ) -> Result<(), Error<T::Error, C::Error>>160     fn do_vcont_multi_thread(
161         ops: &mut dyn crate::target::ext::base::multithread::MultiThreadResume<
162             Arch = T::Arch,
163             Error = T::Error,
164         >,
165         actions: &Actions<'_>,
166     ) -> Result<(), Error<T::Error, C::Error>> {
167         ops.clear_resume_actions().map_err(Error::TargetError)?;
168 
169         for action in actions.iter() {
170             use crate::protocol::commands::_vCont::VContKind;
171 
172             let action = action.ok_or(Error::PacketParse(
173                 crate::protocol::PacketParseError::MalformedCommand,
174             ))?;
175 
176             match action.kind {
177                 VContKind::Continue | VContKind::ContinueWithSig(_) => {
178                     let signal = match action.kind {
179                         VContKind::ContinueWithSig(sig) => Some(sig),
180                         _ => None,
181                     };
182 
183                     match action.thread.map(|thread| thread.tid) {
184                         // An action with no thread-id matches all threads
185                         None | Some(SpecificIdKind::All) => {
186                             // Target API contract specifies that the default
187                             // resume action for all threads is continue.
188                         }
189                         Some(SpecificIdKind::WithId(tid)) => ops
190                             .set_resume_action_continue(tid, signal)
191                             .map_err(Error::TargetError)?,
192                     }
193                 }
194                 VContKind::Step | VContKind::StepWithSig(_)
195                     if ops.support_single_step().is_some() =>
196                 {
197                     let ops = ops.support_single_step().unwrap();
198 
199                     let signal = match action.kind {
200                         VContKind::StepWithSig(sig) => Some(sig),
201                         _ => None,
202                     };
203 
204                     match action.thread.map(|thread| thread.tid) {
205                         // An action with no thread-id matches all threads
206                         None | Some(SpecificIdKind::All) => {
207                             error!("GDB client sent 'step' as default resume action");
208                             return Err(Error::PacketUnexpected);
209                         }
210                         Some(SpecificIdKind::WithId(tid)) => {
211                             ops.set_resume_action_step(tid, signal)
212                                 .map_err(Error::TargetError)?;
213                         }
214                     };
215                 }
216 
217                 VContKind::RangeStep(start, end) if ops.support_range_step().is_some() => {
218                     let ops = ops.support_range_step().unwrap();
219 
220                     match action.thread.map(|thread| thread.tid) {
221                         // An action with no thread-id matches all threads
222                         None | Some(SpecificIdKind::All) => {
223                             error!("GDB client sent 'range step' as default resume action");
224                             return Err(Error::PacketUnexpected);
225                         }
226                         Some(SpecificIdKind::WithId(tid)) => {
227                             let start = start.decode().map_err(|_| Error::TargetMismatch)?;
228                             let end = end.decode().map_err(|_| Error::TargetMismatch)?;
229 
230                             ops.set_resume_action_range_step(tid, start, end)
231                                 .map_err(Error::TargetError)?;
232                         }
233                     };
234                 }
235                 // TODO: update this case when non-stop mode is implemented
236                 VContKind::Stop => return Err(Error::PacketUnexpected),
237 
238                 // GDB doesn't always respect `vCont?` responses that omit `;s;S`, and will try to
239                 // send step packets regardless. Inform the user of this bug by issuing a
240                 // `UnexpectedStepPacket` error, which is more useful than a generic
241                 // `PacketUnexpected` error.
242                 VContKind::Step | VContKind::StepWithSig(..) => {
243                     return Err(Error::UnexpectedStepPacket)
244                 }
245 
246                 // Instead of using `_ =>`, explicitly list out any remaining unguarded cases.
247                 VContKind::RangeStep(..) => {
248                     error!("GDB client sent resume action not reported by `vCont?`");
249                     return Err(Error::PacketUnexpected);
250                 }
251             }
252         }
253 
254         ops.resume().map_err(Error::TargetError)
255     }
256 
do_vcont( &mut self, ops: ResumeOps<'_, T::Arch, T::Error>, actions: Actions<'_>, ) -> Result<HandlerStatus, Error<T::Error, C::Error>>257     fn do_vcont(
258         &mut self,
259         ops: ResumeOps<'_, T::Arch, T::Error>,
260         actions: Actions<'_>,
261     ) -> Result<HandlerStatus, Error<T::Error, C::Error>> {
262         match ops {
263             ResumeOps::SingleThread(ops) => Self::do_vcont_single_thread(ops, &actions)?,
264             ResumeOps::MultiThread(ops) => Self::do_vcont_multi_thread(ops, &actions)?,
265         };
266 
267         Ok(HandlerStatus::DeferredStopReason)
268     }
269 
write_stop_common( &mut self, res: &mut ResponseWriter<'_, C>, target: &mut T, tid: Option<Tid>, signal: Signal, ) -> Result<(), Error<T::Error, C::Error>>270     fn write_stop_common(
271         &mut self,
272         res: &mut ResponseWriter<'_, C>,
273         target: &mut T,
274         tid: Option<Tid>,
275         signal: Signal,
276     ) -> Result<(), Error<T::Error, C::Error>> {
277         res.write_str("T")?;
278         res.write_num(signal.0)?;
279 
280         if let Some(tid) = tid {
281             self.current_mem_tid = tid;
282             self.current_resume_tid = SpecificIdKind::WithId(tid);
283 
284             res.write_str("thread:")?;
285             res.write_specific_thread_id(SpecificThreadId {
286                 pid: self
287                     .features
288                     .multiprocess()
289                     .then_some(SpecificIdKind::WithId(self.get_current_pid(target)?)),
290                 tid: SpecificIdKind::WithId(tid),
291             })?;
292             res.write_str(";")?;
293         }
294 
295         Ok(())
296     }
297 
finish_exec( &mut self, res: &mut ResponseWriter<'_, C>, target: &mut T, stop_reason: MultiThreadStopReason<<T::Arch as Arch>::Usize>, ) -> Result<FinishExecStatus, Error<T::Error, C::Error>>298     pub(crate) fn finish_exec(
299         &mut self,
300         res: &mut ResponseWriter<'_, C>,
301         target: &mut T,
302         stop_reason: MultiThreadStopReason<<T::Arch as Arch>::Usize>,
303     ) -> Result<FinishExecStatus, Error<T::Error, C::Error>> {
304         macro_rules! guard_reverse_exec {
305             () => {{
306                 if let Some(resume_ops) = target.base_ops().resume_ops() {
307                     let (reverse_cont, reverse_step) = match resume_ops {
308                         ResumeOps::MultiThread(ops) => (
309                             ops.support_reverse_cont().is_some(),
310                             ops.support_reverse_step().is_some(),
311                         ),
312                         ResumeOps::SingleThread(ops) => (
313                             ops.support_reverse_cont().is_some(),
314                             ops.support_reverse_step().is_some(),
315                         ),
316                     };
317 
318                     reverse_cont || reverse_step
319                 } else {
320                     false
321                 }
322             }};
323         }
324 
325         macro_rules! guard_break {
326             ($op:ident) => {
327                 target
328                     .support_breakpoints()
329                     .and_then(|ops| ops.$op())
330                     .is_some()
331             };
332         }
333 
334         macro_rules! guard_catch_syscall {
335             () => {
336                 target.support_catch_syscalls().is_some()
337             };
338         }
339 
340         let status = match stop_reason {
341             MultiThreadStopReason::DoneStep => {
342                 res.write_str("S")?;
343                 res.write_num(Signal::SIGTRAP.0)?;
344                 FinishExecStatus::Handled
345             }
346             MultiThreadStopReason::Signal(sig) => {
347                 res.write_str("S")?;
348                 res.write_num(sig.0)?;
349                 FinishExecStatus::Handled
350             }
351             MultiThreadStopReason::Exited(code) => {
352                 res.write_str("W")?;
353                 res.write_num(code)?;
354                 FinishExecStatus::Disconnect(DisconnectReason::TargetExited(code))
355             }
356             MultiThreadStopReason::Terminated(sig) => {
357                 res.write_str("X")?;
358                 res.write_num(sig.0)?;
359                 FinishExecStatus::Disconnect(DisconnectReason::TargetTerminated(sig))
360             }
361             MultiThreadStopReason::SignalWithThread { tid, signal } => {
362                 self.write_stop_common(res, target, Some(tid), signal)?;
363                 FinishExecStatus::Handled
364             }
365             MultiThreadStopReason::SwBreak(tid) if guard_break!(support_sw_breakpoint) => {
366                 crate::__dead_code_marker!("sw_breakpoint", "stop_reason");
367 
368                 self.write_stop_common(res, target, Some(tid), Signal::SIGTRAP)?;
369                 res.write_str("swbreak:;")?;
370                 FinishExecStatus::Handled
371             }
372             MultiThreadStopReason::HwBreak(tid) if guard_break!(support_hw_breakpoint) => {
373                 crate::__dead_code_marker!("hw_breakpoint", "stop_reason");
374 
375                 self.write_stop_common(res, target, Some(tid), Signal::SIGTRAP)?;
376                 res.write_str("hwbreak:;")?;
377                 FinishExecStatus::Handled
378             }
379             MultiThreadStopReason::Watch { tid, kind, addr }
380                 if guard_break!(support_hw_watchpoint) =>
381             {
382                 crate::__dead_code_marker!("hw_watchpoint", "stop_reason");
383 
384                 self.write_stop_common(res, target, Some(tid), Signal::SIGTRAP)?;
385 
386                 use crate::target::ext::breakpoints::WatchKind;
387                 match kind {
388                     WatchKind::Write => res.write_str("watch:")?,
389                     WatchKind::Read => res.write_str("rwatch:")?,
390                     WatchKind::ReadWrite => res.write_str("awatch:")?,
391                 }
392                 res.write_num(addr)?;
393                 res.write_str(";")?;
394                 FinishExecStatus::Handled
395             }
396             MultiThreadStopReason::ReplayLog { tid, pos } if guard_reverse_exec!() => {
397                 crate::__dead_code_marker!("reverse_exec", "stop_reason");
398 
399                 self.write_stop_common(res, target, tid, Signal::SIGTRAP)?;
400 
401                 res.write_str("replaylog:")?;
402                 res.write_str(match pos {
403                     ReplayLogPosition::Begin => "begin",
404                     ReplayLogPosition::End => "end",
405                 })?;
406                 res.write_str(";")?;
407 
408                 FinishExecStatus::Handled
409             }
410             MultiThreadStopReason::CatchSyscall {
411                 tid,
412                 number,
413                 position,
414             } if guard_catch_syscall!() => {
415                 crate::__dead_code_marker!("catch_syscall", "stop_reason");
416 
417                 self.write_stop_common(res, target, tid, Signal::SIGTRAP)?;
418 
419                 res.write_str(match position {
420                     CatchSyscallPosition::Entry => "syscall_entry:",
421                     CatchSyscallPosition::Return => "syscall_return:",
422                 })?;
423                 res.write_num(number)?;
424                 res.write_str(";")?;
425 
426                 FinishExecStatus::Handled
427             }
428             // Explicitly avoid using `_ =>` to handle the "unguarded" variants, as doing so would
429             // squelch the useful compiler error that crops up whenever stop reasons are added.
430             MultiThreadStopReason::SwBreak(_)
431             | MultiThreadStopReason::HwBreak(_)
432             | MultiThreadStopReason::Watch { .. }
433             | MultiThreadStopReason::ReplayLog { .. }
434             | MultiThreadStopReason::CatchSyscall { .. } => {
435                 return Err(Error::UnsupportedStopReason);
436             }
437         };
438 
439         Ok(status)
440     }
441 }
442 
443 pub(crate) enum FinishExecStatus {
444     Handled,
445     Disconnect(DisconnectReason),
446 }
447