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