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 target.support_catch_syscalls().is_some() {
176                     res.write_str(";QCatchSyscalls+")?;
177                 }
178 
179                 if target.use_target_description_xml()
180                     && (T::Arch::target_description_xml().is_some()
181                         || target.support_target_description_xml_override().is_some())
182                 {
183                     res.write_str(";qXfer:features:read+")?;
184                 }
185 
186                 if target.support_memory_map().is_some() {
187                     res.write_str(";qXfer:memory-map:read+")?;
188                 }
189 
190                 if target.support_exec_file().is_some() {
191                     res.write_str(";qXfer:exec-file:read+")?;
192                 }
193 
194                 if target.support_auxv().is_some() {
195                     res.write_str(";qXfer:auxv:read+")?;
196                 }
197 
198                 if target.support_libraries_svr4().is_some() {
199                     res.write_str(";qXfer:libraries-svr4:read+")?;
200                 }
201 
202                 HandlerStatus::Handled
203             }
204 
205             // -------------------- "Core" Functionality -------------------- //
206             Base::QuestionMark(_) => {
207                 // TODO: Improve the '?' response.
208                 // this will be particularly relevant when working on non-stop mode.
209                 self.report_reasonable_stop_reason(res, target)?
210             }
211             Base::qAttached(cmd) => {
212                 let is_attached = match target.support_extended_mode() {
213                     // when _not_ running in extended mode, just report that we're attaching to an
214                     // existing process.
215                     None => true, // assume attached to an existing process
216                     // When running in extended mode, we must defer to the target
217                     Some(ops) => {
218                         match cmd.pid {
219                             Some(pid) => ops.query_if_attached(pid).handle_error()?.was_attached(),
220                             None => true, // assume attached to an existing process
221                         }
222                     }
223                 };
224                 res.write_str(if is_attached { "1" } else { "0" })?;
225                 HandlerStatus::Handled
226             }
227             Base::g(_) => {
228                 let mut regs: <T::Arch as Arch>::Registers = Default::default();
229                 match target.base_ops() {
230                     BaseOps::SingleThread(ops) => ops.read_registers(&mut regs),
231                     BaseOps::MultiThread(ops) => {
232                         ops.read_registers(&mut regs, self.current_mem_tid)
233                     }
234                 }
235                 .handle_error()?;
236 
237                 let mut err = Ok(());
238                 regs.gdb_serialize(|val| {
239                     let res = match val {
240                         Some(b) => res.write_hex_buf(&[b]),
241                         None => res.write_str("xx"),
242                     };
243                     if let Err(e) = res {
244                         err = Err(e);
245                     }
246                 });
247                 err?;
248                 HandlerStatus::Handled
249             }
250             Base::G(cmd) => {
251                 let mut regs: <T::Arch as Arch>::Registers = Default::default();
252                 regs.gdb_deserialize(cmd.vals)
253                     .map_err(|_| Error::TargetMismatch)?;
254 
255                 match target.base_ops() {
256                     BaseOps::SingleThread(ops) => ops.write_registers(&regs),
257                     BaseOps::MultiThread(ops) => ops.write_registers(&regs, self.current_mem_tid),
258                 }
259                 .handle_error()?;
260 
261                 HandlerStatus::NeedsOk
262             }
263             Base::m(cmd) => {
264                 let buf = cmd.buf;
265                 let addr = <T::Arch as Arch>::Usize::from_be_bytes(cmd.addr)
266                     .ok_or(Error::TargetMismatch)?;
267 
268                 let mut i = 0;
269                 let mut n = cmd.len;
270                 while n != 0 {
271                     let chunk_size = n.min(buf.len());
272 
273                     use num_traits::NumCast;
274 
275                     let addr = addr + NumCast::from(i).ok_or(Error::TargetMismatch)?;
276                     let data = &mut buf[..chunk_size];
277                     let data_len = match target.base_ops() {
278                         BaseOps::SingleThread(ops) => ops.read_addrs(addr, data),
279                         BaseOps::MultiThread(ops) => {
280                             ops.read_addrs(addr, data, self.current_mem_tid)
281                         }
282                     }
283                     .handle_error()?;
284 
285                     n -= chunk_size;
286                     i += chunk_size;
287 
288                     // TODO: add more specific error variant?
289                     let data = data.get(..data_len).ok_or(Error::PacketBufferOverflow)?;
290                     res.write_hex_buf(data)?;
291                 }
292                 HandlerStatus::Handled
293             }
294             Base::M(cmd) => {
295                 let addr = <T::Arch as Arch>::Usize::from_be_bytes(cmd.addr)
296                     .ok_or(Error::TargetMismatch)?;
297 
298                 match target.base_ops() {
299                     BaseOps::SingleThread(ops) => ops.write_addrs(addr, cmd.val),
300                     BaseOps::MultiThread(ops) => {
301                         ops.write_addrs(addr, cmd.val, self.current_mem_tid)
302                     }
303                 }
304                 .handle_error()?;
305 
306                 HandlerStatus::NeedsOk
307             }
308             Base::k(_) | Base::vKill(_) => {
309                 match target.support_extended_mode() {
310                     // When not running in extended mode, stop the `GdbStub` and disconnect.
311                     None => HandlerStatus::Disconnect(DisconnectReason::Kill),
312 
313                     // When running in extended mode, a kill command does not necessarily result in
314                     // a disconnect...
315                     Some(ops) => {
316                         let pid = match command {
317                             Base::vKill(cmd) => Some(cmd.pid),
318                             _ => None,
319                         };
320 
321                         let should_terminate = ops.kill(pid).handle_error()?;
322                         if should_terminate.into_bool() {
323                             // manually write OK, since we need to return a DisconnectReason
324                             res.write_str("OK")?;
325                             HandlerStatus::Disconnect(DisconnectReason::Kill)
326                         } else {
327                             HandlerStatus::NeedsOk
328                         }
329                     }
330                 }
331             }
332             Base::D(cmd) => {
333                 // TODO: plumb-through Pid when exposing full multiprocess + extended mode
334                 let _pid = cmd.pid;
335                 res.write_str("OK")?; // manually write OK, since we need to return a DisconnectReason
336                 HandlerStatus::Disconnect(DisconnectReason::Disconnect)
337             }
338 
339             // ------------------- Multi-threading Support ------------------ //
340             Base::H(cmd) => {
341                 use crate::protocol::commands::_h_upcase::Op;
342                 match cmd.kind {
343                     Op::Other => match cmd.thread.tid {
344                         IdKind::Any => match self.get_sane_any_tid(target)? {
345                             Some(tid) => self.current_mem_tid = tid,
346                             None => {
347                                 return Err(Error::NonFatalError(1));
348                             }
349                         },
350                         // "All" threads doesn't make sense for memory accesses
351                         IdKind::All => return Err(Error::PacketUnexpected),
352                         IdKind::WithId(tid) => self.current_mem_tid = tid,
353                     },
354                     // technically, this variant is deprecated in favor of vCont...
355                     Op::StepContinue => match cmd.thread.tid {
356                         IdKind::Any => match self.get_sane_any_tid(target)? {
357                             Some(tid) => self.current_resume_tid = SpecificIdKind::WithId(tid),
358                             None => {
359                                 return Err(Error::NonFatalError(1));
360                             }
361                         },
362                         IdKind::All => self.current_resume_tid = SpecificIdKind::All,
363                         IdKind::WithId(tid) => {
364                             self.current_resume_tid = SpecificIdKind::WithId(tid)
365                         }
366                     },
367                 }
368                 HandlerStatus::NeedsOk
369             }
370             Base::qfThreadInfo(_) => {
371                 res.write_str("m")?;
372                 let pid = self.get_current_pid(target)?;
373 
374                 match target.base_ops() {
375                     BaseOps::SingleThread(_) => res.write_specific_thread_id(SpecificThreadId {
376                         pid: self
377                             .features
378                             .multiprocess()
379                             .then_some(SpecificIdKind::WithId(pid)),
380                         tid: SpecificIdKind::WithId(SINGLE_THREAD_TID),
381                     })?,
382                     BaseOps::MultiThread(ops) => {
383                         let mut err: Result<_, Error<T::Error, C::Error>> = Ok(());
384                         let mut first = true;
385                         ops.list_active_threads(&mut |tid| {
386                             // TODO: replace this with a try block (once stabilized)
387                             let e = (|| {
388                                 if !first {
389                                     res.write_str(",")?
390                                 }
391                                 first = false;
392                                 res.write_specific_thread_id(SpecificThreadId {
393                                     pid: self
394                                         .features
395                                         .multiprocess()
396                                         .then_some(SpecificIdKind::WithId(pid)),
397                                     tid: SpecificIdKind::WithId(tid),
398                                 })?;
399                                 Ok(())
400                             })();
401 
402                             if let Err(e) = e {
403                                 err = Err(e)
404                             }
405                         })
406                         .map_err(Error::TargetError)?;
407                         err?;
408                     }
409                 }
410 
411                 HandlerStatus::Handled
412             }
413             Base::qsThreadInfo(_) => {
414                 res.write_str("l")?;
415                 HandlerStatus::Handled
416             }
417             Base::T(cmd) => {
418                 let alive = match cmd.thread.tid {
419                     IdKind::WithId(tid) => match target.base_ops() {
420                         BaseOps::SingleThread(_) => tid == SINGLE_THREAD_TID,
421                         BaseOps::MultiThread(ops) => {
422                             ops.is_thread_alive(tid).map_err(Error::TargetError)?
423                         }
424                     },
425                     _ => return Err(Error::PacketUnexpected),
426                 };
427                 if alive {
428                     HandlerStatus::NeedsOk
429                 } else {
430                     // any error code will do
431                     return Err(Error::NonFatalError(1));
432                 }
433             }
434         };
435         Ok(handler_status)
436     }
437 }
438