• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use super::prelude::*;
2 use super::DisconnectReason;
3 use crate::arch::Arch;
4 use crate::arch::Registers;
5 use crate::common::Pid;
6 use crate::common::Tid;
7 use crate::protocol::commands::ext::Base;
8 use crate::protocol::IdKind;
9 use crate::protocol::SpecificIdKind;
10 use crate::protocol::SpecificThreadId;
11 use crate::target::ext::base::BaseOps;
12 use crate::target::ext::base::ResumeOps;
13 use crate::FAKE_PID;
14 use crate::SINGLE_THREAD_TID;
15 
16 impl<T: Target, C: Connection> GdbStubImpl<T, C> {
17     #[inline(always)]
get_sane_any_tid( &mut self, target: &mut T, ) -> Result<Option<Tid>, Error<T::Error, C::Error>>18     fn get_sane_any_tid(
19         &mut self,
20         target: &mut T,
21     ) -> Result<Option<Tid>, Error<T::Error, C::Error>> {
22         let tid = match target.base_ops() {
23             BaseOps::SingleThread(_) => Some(SINGLE_THREAD_TID),
24             BaseOps::MultiThread(ops) => {
25                 let mut first_tid = None;
26                 ops.list_active_threads(&mut |tid| {
27                     if first_tid.is_none() {
28                         first_tid = Some(tid);
29                     }
30                 })
31                 .map_err(Error::TargetError)?;
32                 // It is possible for this to be `None` in the case where the target has
33                 // not yet called `register_thread()`. This can happen, for example, if
34                 // there are no active threads in the current target process.
35                 first_tid
36             }
37         };
38         Ok(tid)
39     }
40 
get_current_pid( &mut self, target: &mut T, ) -> Result<Pid, Error<T::Error, C::Error>>41     pub(crate) fn get_current_pid(
42         &mut self,
43         target: &mut T,
44     ) -> Result<Pid, Error<T::Error, C::Error>> {
45         if let Some(ops) = target
46             .support_extended_mode()
47             .and_then(|ops| ops.support_current_active_pid())
48         {
49             ops.current_active_pid().map_err(Error::TargetError)
50         } else {
51             Ok(FAKE_PID)
52         }
53     }
54 
55     // Used by `?` and `vAttach` to return a "reasonable" stop reason.
56     //
57     // This is a bit of an implementation wart, since this is really something
58     // the user ought to be able to customize.
59     //
60     // Works fine for now though...
report_reasonable_stop_reason( &mut self, res: &mut ResponseWriter<'_, C>, target: &mut T, ) -> Result<HandlerStatus, Error<T::Error, C::Error>>61     pub(crate) fn report_reasonable_stop_reason(
62         &mut self,
63         res: &mut ResponseWriter<'_, C>,
64         target: &mut T,
65     ) -> Result<HandlerStatus, Error<T::Error, C::Error>> {
66         // Reply with a valid thread-id or GDB issues a warning when more
67         // than one thread is active
68         if let Some(tid) = self.get_sane_any_tid(target)? {
69             res.write_str("T05thread:")?;
70             res.write_specific_thread_id(SpecificThreadId {
71                 pid: self
72                     .features
73                     .multiprocess()
74                     .then_some(SpecificIdKind::WithId(self.get_current_pid(target)?)),
75                 tid: SpecificIdKind::WithId(tid),
76             })?;
77         } else {
78             res.write_str("W00")?;
79         }
80         res.write_str(";")?;
81         Ok(HandlerStatus::Handled)
82     }
83 
handle_base( &mut self, res: &mut ResponseWriter<'_, C>, target: &mut T, command: Base<'_>, ) -> Result<HandlerStatus, Error<T::Error, C::Error>>84     pub(crate) fn handle_base(
85         &mut self,
86         res: &mut ResponseWriter<'_, C>,
87         target: &mut T,
88         command: Base<'_>,
89     ) -> Result<HandlerStatus, Error<T::Error, C::Error>> {
90         let handler_status = match command {
91             // ------------------ Handshaking and Queries ------------------- //
92             Base::qSupported(cmd) => {
93                 use crate::protocol::commands::_qSupported::Feature;
94 
95                 // perform incoming feature negotiation
96                 for feature in cmd.features.into_iter() {
97                     let (feature, supported) = match feature {
98                         Ok(Some(v)) => v,
99                         Ok(None) => continue,
100                         Err(()) => {
101                             return Err(Error::PacketParse(
102                                 crate::protocol::PacketParseError::MalformedCommand,
103                             ))
104                         }
105                     };
106 
107                     match feature {
108                         Feature::Multiprocess => self.features.set_multiprocess(supported),
109                     }
110                 }
111 
112                 res.write_str("PacketSize=")?;
113                 res.write_num(cmd.packet_buffer_len)?;
114 
115                 // these are the few features that gdbstub unconditionally supports
116                 res.write_str(concat!(";vContSupported+", ";multiprocess+",))?;
117 
118                 if target.use_no_ack_mode() {
119                     res.write_str(";QStartNoAckMode+")?;
120                 }
121 
122                 if let Some(resume_ops) = target.base_ops().resume_ops() {
123                     let (reverse_cont, reverse_step) = match resume_ops {
124                         ResumeOps::MultiThread(ops) => (
125                             ops.support_reverse_cont().is_some(),
126                             ops.support_reverse_step().is_some(),
127                         ),
128                         ResumeOps::SingleThread(ops) => (
129                             ops.support_reverse_cont().is_some(),
130                             ops.support_reverse_step().is_some(),
131                         ),
132                     };
133 
134                     if reverse_cont {
135                         res.write_str(";ReverseContinue+")?;
136                     }
137 
138                     if reverse_step {
139                         res.write_str(";ReverseStep+")?;
140                     }
141                 }
142 
143                 if let Some(ops) = target.support_extended_mode() {
144                     if ops.support_configure_aslr().is_some() {
145                         res.write_str(";QDisableRandomization+")?;
146                     }
147 
148                     if ops.support_configure_env().is_some() {
149                         res.write_str(";QEnvironmentHexEncoded+")?;
150                         res.write_str(";QEnvironmentUnset+")?;
151                         res.write_str(";QEnvironmentReset+")?;
152                     }
153 
154                     if ops.support_configure_startup_shell().is_some() {
155                         res.write_str(";QStartupWithShell+")?;
156                     }
157 
158                     if ops.support_configure_working_dir().is_some() {
159                         res.write_str(";QSetWorkingDir+")?;
160                     }
161                 }
162 
163                 if let Some(ops) = target.support_breakpoints() {
164                     if ops.support_sw_breakpoint().is_some() {
165                         res.write_str(";swbreak+")?;
166                     }
167 
168                     if ops.support_hw_breakpoint().is_some()
169                         || ops.support_hw_watchpoint().is_some()
170                     {
171                         res.write_str(";hwbreak+")?;
172                     }
173                 }
174 
175                 if let Some(ops) = target.support_tracepoints() {
176                     // There are a number of optional tracepoint extensions that
177                     // gdbstub should eventually implement.
178                     // * `StaticTracepoint` for static tracepoint support.
179                     // * `EnableDisableTracepoints` for enabling/disabling tracepoints during a
180                     //   trace experiment.
181                     // * `tracenz` for the tracenz agent bytecode operation.
182                     // * The `Qbtrace:*` family for branch tracing.
183                     // * `InstallInTrace` allows for gdbstub to deliver tracepoint configuration
184                     //   commands while the trace experiment is running instead of them only taking
185                     //   affect on the next `tstart` command.
186                     //
187                     // For now, gdbstub doesn't provide trait extensions for these
188                     // options and so we don't report support. We do report support
189                     // for one extension however:
190                     // * `QTBuffer:size` for configuring the trace buffer size, since the target is
191                     //   allowed to implement it as a no-op.
192                     res.write_str(";QTBuffer:size+")?;
193                     if ops.support_tracepoint_source().is_some() {
194                         res.write_str(";TracepointSource+")?;
195                     }
196                 }
197 
198                 if target.support_catch_syscalls().is_some() {
199                     res.write_str(";QCatchSyscalls+")?;
200                 }
201 
202                 if target.use_target_description_xml()
203                     && (T::Arch::target_description_xml().is_some()
204                         || target.support_target_description_xml_override().is_some())
205                 {
206                     res.write_str(";qXfer:features:read+")?;
207                 }
208 
209                 if target.support_memory_map().is_some() {
210                     res.write_str(";qXfer:memory-map:read+")?;
211                 }
212 
213                 if target.support_exec_file().is_some() {
214                     res.write_str(";qXfer:exec-file:read+")?;
215                 }
216 
217                 if target.support_auxv().is_some() {
218                     res.write_str(";qXfer:auxv:read+")?;
219                 }
220 
221                 if target.support_libraries_svr4().is_some() {
222                     res.write_str(";qXfer:libraries-svr4:read+")?;
223                 }
224 
225                 HandlerStatus::Handled
226             }
227 
228             // -------------------- "Core" Functionality -------------------- //
229             Base::QuestionMark(_) => {
230                 // TODO: Improve the '?' response.
231                 // this will be particularly relevant when working on non-stop mode.
232                 self.report_reasonable_stop_reason(res, target)?
233             }
234             Base::qAttached(cmd) => {
235                 let is_attached = match target.support_extended_mode() {
236                     // when _not_ running in extended mode, just report that we're attaching to an
237                     // existing process.
238                     None => true, // assume attached to an existing process
239                     // When running in extended mode, we must defer to the target
240                     Some(ops) => {
241                         match cmd.pid {
242                             Some(pid) => ops.query_if_attached(pid).handle_error()?.was_attached(),
243                             None => true, // assume attached to an existing process
244                         }
245                     }
246                 };
247                 res.write_str(if is_attached { "1" } else { "0" })?;
248                 HandlerStatus::Handled
249             }
250             Base::g(_) => {
251                 let mut regs: <T::Arch as Arch>::Registers = Default::default();
252                 match target.base_ops() {
253                     BaseOps::SingleThread(ops) => ops.read_registers(&mut regs),
254                     BaseOps::MultiThread(ops) => {
255                         ops.read_registers(&mut regs, self.current_mem_tid)
256                     }
257                 }
258                 .handle_error()?;
259 
260                 let mut err = Ok(());
261                 regs.gdb_serialize(|val| {
262                     let res = match val {
263                         Some(b) => res.write_hex_buf(&[b]),
264                         None => res.write_str("xx"),
265                     };
266                     if let Err(e) = res {
267                         err = Err(e);
268                     }
269                 });
270                 err?;
271                 HandlerStatus::Handled
272             }
273             Base::G(cmd) => {
274                 let mut regs: <T::Arch as Arch>::Registers = Default::default();
275                 regs.gdb_deserialize(cmd.vals)
276                     .map_err(|_| Error::TargetMismatch)?;
277 
278                 match target.base_ops() {
279                     BaseOps::SingleThread(ops) => ops.write_registers(&regs),
280                     BaseOps::MultiThread(ops) => ops.write_registers(&regs, self.current_mem_tid),
281                 }
282                 .handle_error()?;
283 
284                 HandlerStatus::NeedsOk
285             }
286             Base::m(cmd) => {
287                 let buf = cmd.buf;
288                 let addr = <T::Arch as Arch>::Usize::from_be_bytes(cmd.addr)
289                     .ok_or(Error::TargetMismatch)?;
290 
291                 let mut i = 0;
292                 let mut n = cmd.len;
293                 while n != 0 {
294                     let chunk_size = n.min(buf.len());
295 
296                     use num_traits::NumCast;
297 
298                     let addr = addr + NumCast::from(i).ok_or(Error::TargetMismatch)?;
299                     let data = &mut buf[..chunk_size];
300                     let data_len = match target.base_ops() {
301                         BaseOps::SingleThread(ops) => ops.read_addrs(addr, data),
302                         BaseOps::MultiThread(ops) => {
303                             ops.read_addrs(addr, data, self.current_mem_tid)
304                         }
305                     }
306                     .handle_error()?;
307 
308                     n -= chunk_size;
309                     i += chunk_size;
310 
311                     // TODO: add more specific error variant?
312                     let data = data.get(..data_len).ok_or(Error::PacketBufferOverflow)?;
313                     res.write_hex_buf(data)?;
314                 }
315                 HandlerStatus::Handled
316             }
317             Base::M(cmd) => {
318                 let addr = <T::Arch as Arch>::Usize::from_be_bytes(cmd.addr)
319                     .ok_or(Error::TargetMismatch)?;
320 
321                 match target.base_ops() {
322                     BaseOps::SingleThread(ops) => ops.write_addrs(addr, cmd.val),
323                     BaseOps::MultiThread(ops) => {
324                         ops.write_addrs(addr, cmd.val, self.current_mem_tid)
325                     }
326                 }
327                 .handle_error()?;
328 
329                 HandlerStatus::NeedsOk
330             }
331             Base::k(_) | Base::vKill(_) => {
332                 match target.support_extended_mode() {
333                     // When not running in extended mode, stop the `GdbStub` and disconnect.
334                     None => HandlerStatus::Disconnect(DisconnectReason::Kill),
335 
336                     // When running in extended mode, a kill command does not necessarily result in
337                     // a disconnect...
338                     Some(ops) => {
339                         let pid = match command {
340                             Base::vKill(cmd) => Some(cmd.pid),
341                             _ => None,
342                         };
343 
344                         let should_terminate = ops.kill(pid).handle_error()?;
345                         if should_terminate.into_bool() {
346                             // manually write OK, since we need to return a DisconnectReason
347                             res.write_str("OK")?;
348                             HandlerStatus::Disconnect(DisconnectReason::Kill)
349                         } else {
350                             HandlerStatus::NeedsOk
351                         }
352                     }
353                 }
354             }
355             Base::D(cmd) => {
356                 // TODO: plumb-through Pid when exposing full multiprocess + extended mode
357                 let _pid = cmd.pid;
358                 res.write_str("OK")?; // manually write OK, since we need to return a DisconnectReason
359                 HandlerStatus::Disconnect(DisconnectReason::Disconnect)
360             }
361 
362             // ------------------- Multi-threading Support ------------------ //
363             Base::H(cmd) => {
364                 use crate::protocol::commands::_h_upcase::Op;
365                 match cmd.kind {
366                     Op::Other => match cmd.thread.tid {
367                         IdKind::Any => match self.get_sane_any_tid(target)? {
368                             Some(tid) => self.current_mem_tid = tid,
369                             None => {
370                                 return Err(Error::NonFatalError(1));
371                             }
372                         },
373                         // "All" threads doesn't make sense for memory accesses
374                         IdKind::All => return Err(Error::PacketUnexpected),
375                         IdKind::WithId(tid) => self.current_mem_tid = tid,
376                     },
377                     // technically, this variant is deprecated in favor of vCont...
378                     Op::StepContinue => match cmd.thread.tid {
379                         IdKind::Any => match self.get_sane_any_tid(target)? {
380                             Some(tid) => self.current_resume_tid = SpecificIdKind::WithId(tid),
381                             None => {
382                                 return Err(Error::NonFatalError(1));
383                             }
384                         },
385                         IdKind::All => self.current_resume_tid = SpecificIdKind::All,
386                         IdKind::WithId(tid) => {
387                             self.current_resume_tid = SpecificIdKind::WithId(tid)
388                         }
389                     },
390                 }
391                 HandlerStatus::NeedsOk
392             }
393             Base::qfThreadInfo(_) => {
394                 res.write_str("m")?;
395                 let pid = self.get_current_pid(target)?;
396 
397                 match target.base_ops() {
398                     BaseOps::SingleThread(_) => res.write_specific_thread_id(SpecificThreadId {
399                         pid: self
400                             .features
401                             .multiprocess()
402                             .then_some(SpecificIdKind::WithId(pid)),
403                         tid: SpecificIdKind::WithId(SINGLE_THREAD_TID),
404                     })?,
405                     BaseOps::MultiThread(ops) => {
406                         let mut err: Result<_, Error<T::Error, C::Error>> = Ok(());
407                         let mut first = true;
408                         ops.list_active_threads(&mut |tid| {
409                             // TODO: replace this with a try block (once stabilized)
410                             let e = (|| {
411                                 if !first {
412                                     res.write_str(",")?
413                                 }
414                                 first = false;
415                                 res.write_specific_thread_id(SpecificThreadId {
416                                     pid: self
417                                         .features
418                                         .multiprocess()
419                                         .then_some(SpecificIdKind::WithId(pid)),
420                                     tid: SpecificIdKind::WithId(tid),
421                                 })?;
422                                 Ok(())
423                             })();
424 
425                             if let Err(e) = e {
426                                 err = Err(e)
427                             }
428                         })
429                         .map_err(Error::TargetError)?;
430                         err?;
431                     }
432                 }
433 
434                 HandlerStatus::Handled
435             }
436             Base::qsThreadInfo(_) => {
437                 res.write_str("l")?;
438                 HandlerStatus::Handled
439             }
440             Base::T(cmd) => {
441                 let alive = match cmd.thread.tid {
442                     IdKind::WithId(tid) => match target.base_ops() {
443                         BaseOps::SingleThread(_) => tid == SINGLE_THREAD_TID,
444                         BaseOps::MultiThread(ops) => {
445                             ops.is_thread_alive(tid).map_err(Error::TargetError)?
446                         }
447                     },
448                     _ => return Err(Error::PacketUnexpected),
449                 };
450                 if alive {
451                     HandlerStatus::NeedsOk
452                 } else {
453                     // any error code will do
454                     return Err(Error::NonFatalError(1));
455                 }
456             }
457         };
458         Ok(handler_status)
459     }
460 }
461