• 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     /// Support for enabling / disabling ASLR for spawned processes.
150     #[inline(always)]
support_configure_aslr(&mut self) -> Option<ConfigureAslrOps<'_, Self>>151     fn support_configure_aslr(&mut self) -> Option<ConfigureAslrOps<'_, Self>> {
152         None
153     }
154 
155     /// Support for setting / removing / resetting environment variables for
156     /// spawned processes.
157     #[inline(always)]
support_configure_env(&mut self) -> Option<ConfigureEnvOps<'_, Self>>158     fn support_configure_env(&mut self) -> Option<ConfigureEnvOps<'_, Self>> {
159         None
160     }
161 
162     /// Support for configuring if spawned processes should be spawned using a
163     /// shell.
164     #[inline(always)]
support_configure_startup_shell(&mut self) -> Option<ConfigureStartupShellOps<'_, Self>>165     fn support_configure_startup_shell(&mut self) -> Option<ConfigureStartupShellOps<'_, Self>> {
166         None
167     }
168 
169     /// Support for configuring the working directory for spawned processes.
170     #[inline(always)]
support_configure_working_dir(&mut self) -> Option<ConfigureWorkingDirOps<'_, Self>>171     fn support_configure_working_dir(&mut self) -> Option<ConfigureWorkingDirOps<'_, Self>> {
172         None
173     }
174 }
175 
176 define_ext!(ExtendedModeOps, ExtendedMode);
177 
178 /// Iterator of `args` passed to a spawned process (used in
179 /// `ExtendedMode::run`)
180 pub struct Args<'a, 'args> {
181     inner: &'a mut dyn Iterator<Item = &'args [u8]>,
182 }
183 
184 impl core::fmt::Debug for Args<'_, '_> {
fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result185     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
186         write!(f, "Args {{ .. }}")
187     }
188 }
189 
190 impl<'a, 'b> Args<'a, 'b> {
new(inner: &'a mut dyn Iterator<Item = &'b [u8]>) -> Args<'a, 'b>191     pub(crate) fn new(inner: &'a mut dyn Iterator<Item = &'b [u8]>) -> Args<'a, 'b> {
192         Args { inner }
193     }
194 }
195 
196 impl<'args> Iterator for Args<'_, 'args> {
197     type Item = &'args [u8];
198 
next(&mut self) -> Option<Self::Item>199     fn next(&mut self) -> Option<Self::Item> {
200         self.inner.next()
201     }
202 }
203 
204 /// Nested Target Extension - Enable/Disable ASLR for spawned processes (for a
205 /// more consistent debugging experience).
206 ///
207 /// Corresponds to GDB's [`set disable-randomization`](https://sourceware.org/gdb/onlinedocs/gdb/Starting.html) command.
208 pub trait ConfigureAslr: ExtendedMode {
209     /// Enable/Disable ASLR for spawned processes.
cfg_aslr(&mut self, enabled: bool) -> TargetResult<(), Self>210     fn cfg_aslr(&mut self, enabled: bool) -> TargetResult<(), Self>;
211 }
212 
213 define_ext!(ConfigureAslrOps, ConfigureAslr);
214 
215 /// Nested Target Extension - Set/Remove/Reset the Environment variables for
216 /// spawned processes.
217 ///
218 /// Corresponds to GDB's [`set environment`](https://sourceware.org/gdb/onlinedocs/gdb/Environment.html#set-environment) cmd.
219 ///
220 /// _Note:_ Environment variables are not guaranteed to be UTF-8, and are passed
221 /// as raw byte arrays. If the provided keys/values could not be converted into
222 /// an appropriate representation, a non fatal error should be returned.
223 pub trait ConfigureEnv: ExtendedMode {
224     /// Set an environment variable.
set_env(&mut self, key: &[u8], val: Option<&[u8]>) -> TargetResult<(), Self>225     fn set_env(&mut self, key: &[u8], val: Option<&[u8]>) -> TargetResult<(), Self>;
226 
227     /// Remove an environment variable.
remove_env(&mut self, key: &[u8]) -> TargetResult<(), Self>228     fn remove_env(&mut self, key: &[u8]) -> TargetResult<(), Self>;
229 
230     /// Reset all environment variables to their initial state (i.e: undo all
231     /// previous `set/remove_env` calls).
reset_env(&mut self) -> TargetResult<(), Self>232     fn reset_env(&mut self) -> TargetResult<(), Self>;
233 }
234 
235 define_ext!(ConfigureEnvOps, ConfigureEnv);
236 
237 /// Nested Target Extension - Configure if spawned processes should be spawned
238 /// using a shell.
239 ///
240 /// Corresponds to GDB's [`set startup-with-shell`](https://sourceware.org/gdb/onlinedocs/gdb/Starting.html) command.
241 pub trait ConfigureStartupShell: ExtendedMode {
242     /// Configure if spawned processes should be spawned using a shell.
243     ///
244     /// On UNIX-like targets, it is possible to start the inferior using a shell
245     /// program. This is the default behavior on both `GDB` and `gdbserver`.
cfg_startup_with_shell(&mut self, enabled: bool) -> TargetResult<(), Self>246     fn cfg_startup_with_shell(&mut self, enabled: bool) -> TargetResult<(), Self>;
247 }
248 
249 define_ext!(ConfigureStartupShellOps, ConfigureStartupShell);
250 
251 /// Nested Target Extension - Configure the working directory for spawned
252 /// processes.
253 ///
254 /// Corresponds to GDB's [`set cwd` and `cd`](https://sourceware.org/gdb/onlinedocs/gdb/Working-Directory.html) commands.
255 pub trait ConfigureWorkingDir: ExtendedMode {
256     /// Set the working directory for spawned processes.
257     ///
258     /// If no directory is provided, the stub should reset the value to it's
259     /// original value.
260     ///
261     /// The path is not guaranteed to be valid UTF-8, and is passed as a raw
262     /// byte array. If the path could not be converted into an appropriate
263     /// representation, a non fatal error should be returned.
cfg_working_dir(&mut self, dir: Option<&[u8]>) -> TargetResult<(), Self>264     fn cfg_working_dir(&mut self, dir: Option<&[u8]>) -> TargetResult<(), Self>;
265 }
266 
267 define_ext!(ConfigureWorkingDirOps, ConfigureWorkingDir);
268