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