• 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::common::Tid;
6 use crate::protocol::{IdKind, SpecificIdKind, SpecificThreadId};
7 use crate::target::ext::base::{BaseOps, ResumeOps};
8 use crate::{FAKE_PID, SINGLE_THREAD_TID};
9 
10 use super::DisconnectReason;
11 
12 impl<T: Target, C: Connection> GdbStubImpl<T, C> {
13     #[inline(always)]
get_sane_any_tid(&mut self, target: &mut T) -> Result<Tid, Error<T::Error, C::Error>>14     fn get_sane_any_tid(&mut self, target: &mut T) -> Result<Tid, Error<T::Error, C::Error>> {
15         let tid = match target.base_ops() {
16             BaseOps::SingleThread(_) => SINGLE_THREAD_TID,
17             BaseOps::MultiThread(ops) => {
18                 let mut first_tid = None;
19                 ops.list_active_threads(&mut |tid| {
20                     if first_tid.is_none() {
21                         first_tid = Some(tid);
22                     }
23                 })
24                 .map_err(Error::TargetError)?;
25                 // Note that `Error::NoActiveThreads` shouldn't ever occur, since this method is
26                 // called from the `H` packet handler, which AFAIK is only sent after the GDB
27                 // client has confirmed that a thread / process exists.
28                 //
29                 // If it does, that really sucks, and will require rethinking how to handle "any
30                 // thread" messages.
31                 first_tid.ok_or(Error::NoActiveThreads)?
32             }
33         };
34         Ok(tid)
35     }
36 
handle_base( &mut self, res: &mut ResponseWriter<'_, C>, target: &mut T, command: Base<'_>, ) -> Result<HandlerStatus, Error<T::Error, C::Error>>37     pub(crate) fn handle_base(
38         &mut self,
39         res: &mut ResponseWriter<'_, C>,
40         target: &mut T,
41         command: Base<'_>,
42     ) -> Result<HandlerStatus, Error<T::Error, C::Error>> {
43         let handler_status = match command {
44             // ------------------ Handshaking and Queries ------------------- //
45             Base::qSupported(cmd) => {
46                 use crate::protocol::commands::_qSupported::Feature;
47 
48                 // perform incoming feature negotiation
49                 for feature in cmd.features.into_iter() {
50                     let (feature, supported) = match feature {
51                         Ok(Some(v)) => v,
52                         Ok(None) => continue,
53                         Err(()) => {
54                             return Err(Error::PacketParse(
55                                 crate::protocol::PacketParseError::MalformedCommand,
56                             ))
57                         }
58                     };
59 
60                     match feature {
61                         Feature::Multiprocess => self.features.set_multiprocess(supported),
62                     }
63                 }
64 
65                 res.write_str("PacketSize=")?;
66                 res.write_num(cmd.packet_buffer_len)?;
67 
68                 // these are the few features that gdbstub unconditionally supports
69                 res.write_str(concat!(
70                     ";vContSupported+",
71                     ";multiprocess+",
72                     ";QStartNoAckMode+",
73                 ))?;
74 
75                 if let Some(resume_ops) = target.base_ops().resume_ops() {
76                     let (reverse_cont, reverse_step) = match resume_ops {
77                         ResumeOps::MultiThread(ops) => (
78                             ops.support_reverse_cont().is_some(),
79                             ops.support_reverse_step().is_some(),
80                         ),
81                         ResumeOps::SingleThread(ops) => (
82                             ops.support_reverse_cont().is_some(),
83                             ops.support_reverse_step().is_some(),
84                         ),
85                     };
86 
87                     if reverse_cont {
88                         res.write_str(";ReverseContinue+")?;
89                     }
90 
91                     if reverse_step {
92                         res.write_str(";ReverseStep+")?;
93                     }
94                 }
95 
96                 if let Some(ops) = target.support_extended_mode() {
97                     if ops.support_configure_aslr().is_some() {
98                         res.write_str(";QDisableRandomization+")?;
99                     }
100 
101                     if ops.support_configure_env().is_some() {
102                         res.write_str(";QEnvironmentHexEncoded+")?;
103                         res.write_str(";QEnvironmentUnset+")?;
104                         res.write_str(";QEnvironmentReset+")?;
105                     }
106 
107                     if ops.support_configure_startup_shell().is_some() {
108                         res.write_str(";QStartupWithShell+")?;
109                     }
110 
111                     if ops.support_configure_working_dir().is_some() {
112                         res.write_str(";QSetWorkingDir+")?;
113                     }
114                 }
115 
116                 if let Some(ops) = target.support_breakpoints() {
117                     if ops.support_sw_breakpoint().is_some() {
118                         res.write_str(";swbreak+")?;
119                     }
120 
121                     if ops.support_hw_breakpoint().is_some()
122                         || ops.support_hw_watchpoint().is_some()
123                     {
124                         res.write_str(";hwbreak+")?;
125                     }
126                 }
127 
128                 if target.support_catch_syscalls().is_some() {
129                     res.write_str(";QCatchSyscalls+")?;
130                 }
131 
132                 if target.use_target_description_xml()
133                     && (T::Arch::target_description_xml().is_some()
134                         || target.support_target_description_xml_override().is_some())
135                 {
136                     res.write_str(";qXfer:features:read+")?;
137                 }
138 
139                 if target.support_memory_map().is_some() {
140                     res.write_str(";qXfer:memory-map:read+")?;
141                 }
142 
143                 if target.support_exec_file().is_some() {
144                     res.write_str(";qXfer:exec-file:read+")?;
145                 }
146 
147                 if target.support_auxv().is_some() {
148                     res.write_str(";qXfer:auxv:read+")?;
149                 }
150 
151                 HandlerStatus::Handled
152             }
153             Base::QStartNoAckMode(_) => {
154                 self.features.set_no_ack_mode(true);
155                 HandlerStatus::NeedsOk
156             }
157 
158             // -------------------- "Core" Functionality -------------------- //
159             // TODO: Improve the '?' response based on last-sent stop reason.
160             // this will be particularly relevant when working on non-stop mode.
161             Base::QuestionMark(_) => {
162                 // Reply with a valid thread-id or GDB issues a warning when more
163                 // than one thread is active
164                 res.write_str("T05thread:")?;
165                 res.write_specific_thread_id(SpecificThreadId {
166                     pid: self
167                         .features
168                         .multiprocess()
169                         .then_some(SpecificIdKind::WithId(FAKE_PID)),
170                     tid: SpecificIdKind::WithId(self.get_sane_any_tid(target)?),
171                 })?;
172                 res.write_str(";")?;
173                 HandlerStatus::Handled
174             }
175             Base::qAttached(cmd) => {
176                 let is_attached = match target.support_extended_mode() {
177                     // when _not_ running in extended mode, just report that we're attaching to an
178                     // existing process.
179                     None => true, // assume attached to an existing process
180                     // When running in extended mode, we must defer to the target
181                     Some(ops) => {
182                         match cmd.pid {
183                             Some(pid) => ops.query_if_attached(pid).handle_error()?.was_attached(),
184                             None => true, // assume attached to an existing process
185                         }
186                     }
187                 };
188                 res.write_str(if is_attached { "1" } else { "0" })?;
189                 HandlerStatus::Handled
190             }
191             Base::g(_) => {
192                 let mut regs: <T::Arch as Arch>::Registers = Default::default();
193                 match target.base_ops() {
194                     BaseOps::SingleThread(ops) => ops.read_registers(&mut regs),
195                     BaseOps::MultiThread(ops) => {
196                         ops.read_registers(&mut regs, self.current_mem_tid)
197                     }
198                 }
199                 .handle_error()?;
200 
201                 let mut err = Ok(());
202                 regs.gdb_serialize(|val| {
203                     let res = match val {
204                         Some(b) => res.write_hex_buf(&[b]),
205                         None => res.write_str("xx"),
206                     };
207                     if let Err(e) = res {
208                         err = Err(e);
209                     }
210                 });
211                 err?;
212                 HandlerStatus::Handled
213             }
214             Base::G(cmd) => {
215                 let mut regs: <T::Arch as Arch>::Registers = Default::default();
216                 regs.gdb_deserialize(cmd.vals)
217                     .map_err(|_| Error::TargetMismatch)?;
218 
219                 match target.base_ops() {
220                     BaseOps::SingleThread(ops) => ops.write_registers(&regs),
221                     BaseOps::MultiThread(ops) => ops.write_registers(&regs, self.current_mem_tid),
222                 }
223                 .handle_error()?;
224 
225                 HandlerStatus::NeedsOk
226             }
227             Base::m(cmd) => {
228                 let buf = cmd.buf;
229                 let addr = <T::Arch as Arch>::Usize::from_be_bytes(cmd.addr)
230                     .ok_or(Error::TargetMismatch)?;
231 
232                 let mut i = 0;
233                 let mut n = cmd.len;
234                 while n != 0 {
235                     let chunk_size = n.min(buf.len());
236 
237                     use num_traits::NumCast;
238 
239                     let addr = addr + NumCast::from(i).ok_or(Error::TargetMismatch)?;
240                     let data = &mut buf[..chunk_size];
241                     match target.base_ops() {
242                         BaseOps::SingleThread(ops) => ops.read_addrs(addr, data),
243                         BaseOps::MultiThread(ops) => {
244                             ops.read_addrs(addr, data, self.current_mem_tid)
245                         }
246                     }
247                     .handle_error()?;
248 
249                     n -= chunk_size;
250                     i += chunk_size;
251 
252                     res.write_hex_buf(data)?;
253                 }
254                 HandlerStatus::Handled
255             }
256             Base::M(cmd) => {
257                 let addr = <T::Arch as Arch>::Usize::from_be_bytes(cmd.addr)
258                     .ok_or(Error::TargetMismatch)?;
259 
260                 match target.base_ops() {
261                     BaseOps::SingleThread(ops) => ops.write_addrs(addr, cmd.val),
262                     BaseOps::MultiThread(ops) => {
263                         ops.write_addrs(addr, cmd.val, self.current_mem_tid)
264                     }
265                 }
266                 .handle_error()?;
267 
268                 HandlerStatus::NeedsOk
269             }
270             Base::k(_) | Base::vKill(_) => {
271                 match target.support_extended_mode() {
272                     // When not running in extended mode, stop the `GdbStub` and disconnect.
273                     None => HandlerStatus::Disconnect(DisconnectReason::Kill),
274 
275                     // When running in extended mode, a kill command does not necessarily result in
276                     // a disconnect...
277                     Some(ops) => {
278                         let pid = match command {
279                             Base::vKill(cmd) => Some(cmd.pid),
280                             _ => None,
281                         };
282 
283                         let should_terminate = ops.kill(pid).handle_error()?;
284                         if should_terminate.into_bool() {
285                             // manually write OK, since we need to return a DisconnectReason
286                             res.write_str("OK")?;
287                             HandlerStatus::Disconnect(DisconnectReason::Kill)
288                         } else {
289                             HandlerStatus::NeedsOk
290                         }
291                     }
292                 }
293             }
294             Base::D(_) => {
295                 // TODO: plumb-through Pid when exposing full multiprocess + extended mode
296                 res.write_str("OK")?; // manually write OK, since we need to return a DisconnectReason
297                 HandlerStatus::Disconnect(DisconnectReason::Disconnect)
298             }
299 
300             // ------------------- Multi-threading Support ------------------ //
301             Base::H(cmd) => {
302                 use crate::protocol::commands::_h_upcase::Op;
303                 match cmd.kind {
304                     Op::Other => match cmd.thread.tid {
305                         IdKind::Any => self.current_mem_tid = self.get_sane_any_tid(target)?,
306                         // "All" threads doesn't make sense for memory accesses
307                         IdKind::All => return Err(Error::PacketUnexpected),
308                         IdKind::WithId(tid) => self.current_mem_tid = tid,
309                     },
310                     // technically, this variant is deprecated in favor of vCont...
311                     Op::StepContinue => match cmd.thread.tid {
312                         IdKind::Any => {
313                             self.current_resume_tid =
314                                 SpecificIdKind::WithId(self.get_sane_any_tid(target)?)
315                         }
316                         IdKind::All => self.current_resume_tid = SpecificIdKind::All,
317                         IdKind::WithId(tid) => {
318                             self.current_resume_tid = SpecificIdKind::WithId(tid)
319                         }
320                     },
321                 }
322                 HandlerStatus::NeedsOk
323             }
324             Base::qfThreadInfo(_) => {
325                 res.write_str("m")?;
326 
327                 match target.base_ops() {
328                     BaseOps::SingleThread(_) => res.write_specific_thread_id(SpecificThreadId {
329                         pid: self
330                             .features
331                             .multiprocess()
332                             .then_some(SpecificIdKind::WithId(FAKE_PID)),
333                         tid: SpecificIdKind::WithId(SINGLE_THREAD_TID),
334                     })?,
335                     BaseOps::MultiThread(ops) => {
336                         let mut err: Result<_, Error<T::Error, C::Error>> = Ok(());
337                         let mut first = true;
338                         ops.list_active_threads(&mut |tid| {
339                             // TODO: replace this with a try block (once stabilized)
340                             let e = (|| {
341                                 if !first {
342                                     res.write_str(",")?
343                                 }
344                                 first = false;
345                                 res.write_specific_thread_id(SpecificThreadId {
346                                     pid: self
347                                         .features
348                                         .multiprocess()
349                                         .then_some(SpecificIdKind::WithId(FAKE_PID)),
350                                     tid: SpecificIdKind::WithId(tid),
351                                 })?;
352                                 Ok(())
353                             })();
354 
355                             if let Err(e) = e {
356                                 err = Err(e)
357                             }
358                         })
359                         .map_err(Error::TargetError)?;
360                         err?;
361                     }
362                 }
363 
364                 HandlerStatus::Handled
365             }
366             Base::qsThreadInfo(_) => {
367                 res.write_str("l")?;
368                 HandlerStatus::Handled
369             }
370             Base::T(cmd) => {
371                 let alive = match cmd.thread.tid {
372                     IdKind::WithId(tid) => match target.base_ops() {
373                         BaseOps::SingleThread(_) => tid == SINGLE_THREAD_TID,
374                         BaseOps::MultiThread(ops) => {
375                             ops.is_thread_alive(tid).map_err(Error::TargetError)?
376                         }
377                     },
378                     _ => return Err(Error::PacketUnexpected),
379                 };
380                 if alive {
381                     HandlerStatus::NeedsOk
382                 } else {
383                     // any error code will do
384                     return Err(Error::NonFatalError(1));
385                 }
386             }
387         };
388         Ok(handler_status)
389     }
390 }
391