• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! Enables [Extended Mode](https://sourceware.org/gdb/current/onlinedocs/gdb/Connecting.html)
2 //! functionality when connecting using `target extended-remote`, such as
3 //! spawning new processes and/or attaching to existing processes.
4 //!
5 //! # Disclaimer
6 //!
7 //! While this API has been end-to-end tested and confirmed working with a "toy"
8 //! target implementation (see the included `armv4t` example), it has _not_ been
9 //! "battle-tested" with a fully-featured extended-mode capable target.
10 //!
11 //! If you end up using this API to implement an extended-mode capable target,
12 //! _please_ file an issue on the repo detailing any bugs / usability issues you
13 //! may encountered while implementing this API! If everything happens to Just
14 //! Work as expected, nonetheless file an issue so that this disclaimer can be
15 //! removed in future releases!
16 
17 use crate::common::*;
18 use crate::target::{Target, TargetResult};
19 
20 /// Returned from `ExtendedMode::kill`
21 ///
22 /// Retuning `ShouldTerminate::Yes` will cause the `GdbStub` to immediately
23 /// shut down and return a `DisconnectReason::Kill`. Returning
24 /// `ShouldTerminate::No` will keep the `GdbStub` running and listening for
25 /// further run/attach requests.
26 pub enum ShouldTerminate {
27     /// Terminate GdbStub
28     Yes,
29     /// Don't Terminate GdbStub
30     No,
31 }
32 
33 impl ShouldTerminate {
34     /// Convert `ShouldTerminate::Yes` into `true`, and `ShouldTerminate::No`
35     /// into `false`
into_bool(self) -> bool36     pub fn into_bool(self) -> bool {
37         match self {
38             ShouldTerminate::Yes => true,
39             ShouldTerminate::No => false,
40         }
41     }
42 }
43 
44 /// Describes how the target attached to a process.
45 pub enum AttachKind {
46     /// It attached to an existing process.
47     Attach,
48     /// It spawned a new process.
49     Run,
50 }
51 
52 impl AttachKind {
was_attached(self) -> bool53     pub(crate) fn was_attached(self) -> bool {
54         match self {
55             AttachKind::Attach => true,
56             AttachKind::Run => false,
57         }
58     }
59 }
60 
61 /// Target Extension - Support
62 /// [Extended Mode](https://sourceware.org/gdb/current/onlinedocs/gdb/Connecting.html) functionality.
63 ///
64 /// # Extended Mode for Single/Multi Threaded Targets
65 ///
66 /// While extended-mode is primarily intended to be implemented by targets which
67 /// support debugging multiple processes, there's no reason why a basic
68 /// single/multi-threaded target can't implement these extensions as well.
69 ///
70 /// For example, instead of "spawning" a process, the `run` command could be
71 /// used to reset the execution state instead (e.g: resetting an emulator).
72 pub trait ExtendedMode: Target {
73     /// Spawn and attach to the program `filename`, passing it the provided
74     /// `args` on its command line.
75     ///
76     /// The program is created in the stopped state.
77     ///
78     /// If no filename is provided, the stub may use a default program (e.g. the
79     /// last program run), or a non fatal error should be returned.
80     ///
81     /// `filename` and `args` are not guaranteed to be valid UTF-8, and are
82     /// passed as raw byte arrays. If the filenames/arguments could not be
83     /// converted into an appropriate representation, a non fatal error should
84     /// be returned.
85     ///
86     /// _Note:_ This method's implementation should handle any additional
87     /// configuration options set via the various `ConfigureXXX` extensions to
88     /// `ExtendedMode`. e.g: if the [`ConfigureEnv`](trait.ConfigureEnv.html)
89     /// extension is implemented and enabled, this method should set the spawned
90     /// processes' environment variables accordingly.
run(&mut self, filename: Option<&[u8]>, args: Args) -> TargetResult<Pid, Self>91     fn run(&mut self, filename: Option<&[u8]>, args: Args) -> TargetResult<Pid, Self>;
92 
93     /// Attach to a new process with the specified PID.
94     ///
95     /// In all-stop mode, all threads in the attached process are stopped; in
96     /// non-stop mode, it may be attached without being stopped (if that is
97     /// supported by the target).
attach(&mut self, pid: Pid) -> TargetResult<(), Self>98     fn attach(&mut self, pid: Pid) -> TargetResult<(), Self>;
99 
100     /// Query if specified PID was spawned by the target (via `run`), or if the
101     /// target attached to an existing process (via `attach`).
102     ///
103     /// If the PID doesn't correspond to a process the target has run or
104     /// attached to, a non fatal error should be returned.
query_if_attached(&mut self, pid: Pid) -> TargetResult<AttachKind, Self>105     fn query_if_attached(&mut self, pid: Pid) -> TargetResult<AttachKind, Self>;
106 
107     /// Called when the GDB client sends a Kill request.
108     ///
109     /// If the PID doesn't correspond to a process the target has run or
110     /// attached to, a non fatal error should be returned.
111     ///
112     /// GDB may or may not specify a specific PID to kill. When no PID is
113     /// specified, the target is free to decide what to do (e.g: kill the
114     /// last-used pid, terminate the connection, etc...).
115     ///
116     /// If `ShouldTerminate::Yes` is returned, `GdbStub` will immediately stop
117     /// and return a `DisconnectReason::Kill`. Otherwise, the connection will
118     /// remain open, and `GdbStub` will continue listening for run/attach
119     /// requests.
kill(&mut self, pid: Option<Pid>) -> TargetResult<ShouldTerminate, Self>120     fn kill(&mut self, pid: Option<Pid>) -> TargetResult<ShouldTerminate, Self>;
121 
122     /// Restart the program being debugged.
123     ///
124     /// The GDB docs don't do a good job describing what a "restart" operation
125     /// entails. For reference, the official `gdbserver` seems to kill all
126     /// inferior processes, and then re-run whatever program was provided on the
127     /// command line (if one was provided).
128     ///
129     /// _Author's Note:_ Based on my current (as of Sept 2020) understanding of
130     /// the GDB client;s source code, it seems that the "R" packet is _never_
131     /// sent so-long as the target implements the "vRun" packet (which
132     /// corresponds to this trait's `run` method). As such, while `gdbstub`
133     /// exposes this functionality, and "requires" an implementation, unless
134     /// you're running a fairly old version of GDB, it should be fine to
135     /// simply stub it out -- e.g: using the `unimplemented!()` macro /
136     /// returning a fatal error.
restart(&mut self) -> Result<(), Self::Error>137     fn restart(&mut self) -> Result<(), Self::Error>;
138 
139     /// (optional) Invoked when GDB client switches to extended mode.
140     ///
141     /// The default implementation is a no-op.
142     ///
143     /// Target implementations can override this implementation if they need to
144     /// perform any operations once extended mode is activated.
on_start(&mut self) -> Result<(), Self::Error>145     fn on_start(&mut self) -> Result<(), Self::Error> {
146         Ok(())
147     }
148 
149     /// Enable/Disable ASLR for spawned processes.
150     #[inline(always)]
configure_aslr(&mut self) -> Option<ConfigureAslrOps<Self>>151     fn configure_aslr(&mut self) -> Option<ConfigureAslrOps<Self>> {
152         None
153     }
154 
155     /// Set/Remove/Reset Environment variables for spawned processes.
156     #[inline(always)]
configure_env(&mut self) -> Option<ConfigureEnvOps<Self>>157     fn configure_env(&mut self) -> Option<ConfigureEnvOps<Self>> {
158         None
159     }
160 
161     /// Configure if spawned processes should be spawned using a shell.
162     #[inline(always)]
configure_startup_shell(&mut self) -> Option<ConfigureStartupShellOps<Self>>163     fn configure_startup_shell(&mut self) -> Option<ConfigureStartupShellOps<Self>> {
164         None
165     }
166 
167     /// Configure the working directory for spawned processes.
168     #[inline(always)]
configure_working_dir(&mut self) -> Option<ConfigureWorkingDirOps<Self>>169     fn configure_working_dir(&mut self) -> Option<ConfigureWorkingDirOps<Self>> {
170         None
171     }
172 }
173 
174 define_ext!(ExtendedModeOps, ExtendedMode);
175 
176 /// Iterator of `args` passed to a spawned process (used in
177 /// `ExtendedMode::run`)
178 pub struct Args<'a, 'args> {
179     inner: &'a mut dyn Iterator<Item = &'args [u8]>,
180 }
181 
182 impl core::fmt::Debug for Args<'_, '_> {
fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result183     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
184         write!(f, "Args {{ .. }}")
185     }
186 }
187 
188 impl<'a, 'b> Args<'a, 'b> {
new(inner: &'a mut dyn Iterator<Item = &'b [u8]>) -> Args<'a, 'b>189     pub(crate) fn new(inner: &'a mut dyn Iterator<Item = &'b [u8]>) -> Args<'a, 'b> {
190         Args { inner }
191     }
192 }
193 
194 impl<'args> Iterator for Args<'_, 'args> {
195     type Item = &'args [u8];
196 
next(&mut self) -> Option<Self::Item>197     fn next(&mut self) -> Option<Self::Item> {
198         self.inner.next()
199     }
200 }
201 
202 /// Nested Target Extension - Enable/Disable ASLR for spawned processes (for a
203 /// more consistent debugging experience).
204 ///
205 /// Corresponds to GDB's [`set disable-randomization`](https://sourceware.org/gdb/onlinedocs/gdb/Starting.html) command.
206 pub trait ConfigureAslr: ExtendedMode {
207     /// Enable/Disable ASLR for spawned processes.
cfg_aslr(&mut self, enabled: bool) -> TargetResult<(), Self>208     fn cfg_aslr(&mut self, enabled: bool) -> TargetResult<(), Self>;
209 }
210 
211 define_ext!(ConfigureAslrOps, ConfigureAslr);
212 
213 /// Nested Target Extension - Set/Remove/Reset the Environment variables for
214 /// spawned processes.
215 ///
216 /// Corresponds to GDB's [`set environment`](https://sourceware.org/gdb/onlinedocs/gdb/Environment.html#set-environment) cmd.
217 ///
218 /// _Note:_ Environment variables are not guaranteed to be UTF-8, and are passed
219 /// as raw byte arrays. If the provided keys/values could not be converted into
220 /// an appropriate representation, a non fatal error should be returned.
221 pub trait ConfigureEnv: ExtendedMode {
222     /// Set an environment variable.
set_env(&mut self, key: &[u8], val: Option<&[u8]>) -> TargetResult<(), Self>223     fn set_env(&mut self, key: &[u8], val: Option<&[u8]>) -> TargetResult<(), Self>;
224 
225     /// Remove an environment variable.
remove_env(&mut self, key: &[u8]) -> TargetResult<(), Self>226     fn remove_env(&mut self, key: &[u8]) -> TargetResult<(), Self>;
227 
228     /// Reset all environment variables to their initial state (i.e: undo all
229     /// previous `set/remove_env` calls).
reset_env(&mut self) -> TargetResult<(), Self>230     fn reset_env(&mut self) -> TargetResult<(), Self>;
231 }
232 
233 define_ext!(ConfigureEnvOps, ConfigureEnv);
234 
235 /// Nested Target Extension - Configure if spawned processes should be spawned
236 /// using a shell.
237 ///
238 /// Corresponds to GDB's [`set startup-with-shell`](https://sourceware.org/gdb/onlinedocs/gdb/Starting.html) command.
239 pub trait ConfigureStartupShell: ExtendedMode {
240     /// Configure if spawned processes should be spawned using a shell.
241     ///
242     /// On UNIX-like targets, it is possible to start the inferior using a shell
243     /// program. This is the default behavior on both `GDB` and `gdbserver`.
cfg_startup_with_shell(&mut self, enabled: bool) -> TargetResult<(), Self>244     fn cfg_startup_with_shell(&mut self, enabled: bool) -> TargetResult<(), Self>;
245 }
246 
247 define_ext!(ConfigureStartupShellOps, ConfigureStartupShell);
248 
249 /// Nested Target Extension - Configure the working directory for spawned
250 /// processes.
251 ///
252 /// Corresponds to GDB's [`set cwd` and `cd`](https://sourceware.org/gdb/onlinedocs/gdb/Working-Directory.html) commands.
253 pub trait ConfigureWorkingDir: ExtendedMode {
254     /// Set the working directory for spawned processes.
255     ///
256     /// If no directory is provided, the stub should reset the value to it's
257     /// original value.
258     ///
259     /// The path is not guaranteed to be valid UTF-8, and is passed as a raw
260     /// byte array. If the path could not be converted into an appropriate
261     /// representation, a non fatal error should be returned.
cfg_working_dir(&mut self, dir: Option<&[u8]>) -> TargetResult<(), Self>262     fn cfg_working_dir(&mut self, dir: Option<&[u8]>) -> TargetResult<(), Self>;
263 }
264 
265 define_ext!(ConfigureWorkingDirOps, ConfigureWorkingDir);
266