• 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 From<ShouldTerminate> for bool {
from(st: ShouldTerminate) -> bool34     fn from(st: ShouldTerminate) -> bool {
35         match st {
36             ShouldTerminate::Yes => true,
37             ShouldTerminate::No => false,
38         }
39     }
40 }
41 
42 /// Describes how the target attached to a process.
43 #[cfg(not(feature = "alloc"))]
44 pub enum AttachKind {
45     /// It attached to an existing process.
46     Attach,
47     /// It spawned a new process.
48     Run,
49 }
50 
51 #[cfg(not(feature = "alloc"))]
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     /// This method is only required when the `alloc`/`std` features are
104     /// disabled. If `alloc` is available, `gdbstub` will automatically track
105     /// this property using a heap-allocated data structure.
106     #[cfg(not(feature = "alloc"))]
query_if_attached(&mut self, pid: Pid) -> TargetResult<AttachKind, Self>107     fn query_if_attached(&mut self, pid: Pid) -> TargetResult<AttachKind, Self>;
108 
109     /// Called when the GDB client sends a Kill request.
110     ///
111     /// GDB may or may not specify a specific PID to kill. When no PID is
112     /// specified, the target is free to decide what to do (e.g: kill the
113     /// last-used pid, terminate the connection, etc...).
114     ///
115     /// If `ShouldTerminate::Yes` is returned, `GdbStub` will immediately stop
116     /// and return a `DisconnectReason::Kill`. Otherwise, the connection will
117     /// remain open, and `GdbStub` will continue listening for run/attach
118     /// requests.
kill(&mut self, pid: Option<Pid>) -> TargetResult<ShouldTerminate, Self>119     fn kill(&mut self, pid: Option<Pid>) -> TargetResult<ShouldTerminate, Self>;
120 
121     /// Restart the program being debugged.
122     ///
123     /// The GDB docs don't do a good job describing what a "restart" operation
124     /// entails. For reference, the official `gdbserver` seems to kill all
125     /// inferior processes, and then re-run whatever program was provided on the
126     /// command line (if one was provided).
127     ///
128     /// _Author's Note:_ Based on my current (as of Sept 2020) understanding of
129     /// the GDB client;s source code, it seems that the "R" packet is _never_
130     /// sent so-long as the target implements the "vRun" packet (which
131     /// corresponds to this trait's `run` method). As such, while `gdbstub`
132     /// exposes this functionality, and "requires" an implementation, unless
133     /// you're running a fairly old version of GDB, it should be fine to
134     /// simply stub it out -- e.g: using the `unimplemented!()` macro /
135     /// returning a fatal error.
restart(&mut self) -> Result<(), Self::Error>136     fn restart(&mut self) -> Result<(), Self::Error>;
137 
138     /// (optional) Invoked when GDB client switches to extended mode.
139     ///
140     /// The default implementation is a no-op.
141     ///
142     /// Target implementations can override this implementation if they need to
143     /// perform any operations once extended mode is activated.
on_start(&mut self) -> Result<(), Self::Error>144     fn on_start(&mut self) -> Result<(), Self::Error> {
145         Ok(())
146     }
147 
148     /// Enable/Disable ASLR for spawned processes.
configure_aslr(&mut self) -> Option<ConfigureASLROps<Self>>149     fn configure_aslr(&mut self) -> Option<ConfigureASLROps<Self>> {
150         None
151     }
152 
153     /// Set/Remove/Reset Environment variables for spawned processes.
configure_env(&mut self) -> Option<ConfigureEnvOps<Self>>154     fn configure_env(&mut self) -> Option<ConfigureEnvOps<Self>> {
155         None
156     }
157 
158     /// Configure if spawned processes should be spawned using a shell.
configure_startup_shell(&mut self) -> Option<ConfigureStartupShellOps<Self>>159     fn configure_startup_shell(&mut self) -> Option<ConfigureStartupShellOps<Self>> {
160         None
161     }
162 
163     /// Configure the working directory for spawned processes.
configure_working_dir(&mut self) -> Option<ConfigureWorkingDirOps<Self>>164     fn configure_working_dir(&mut self) -> Option<ConfigureWorkingDirOps<Self>> {
165         None
166     }
167 }
168 
169 define_ext!(ExtendedModeOps, ExtendedMode);
170 
171 /// Iterator of `args` passed to a spawned process (used in
172 /// `ExtendedMode::run`)
173 pub struct Args<'a, 'args> {
174     inner: &'a mut dyn Iterator<Item = &'args [u8]>,
175 }
176 
177 impl core::fmt::Debug for Args<'_, '_> {
fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result178     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
179         write!(f, "Args {{ .. }}")
180     }
181 }
182 
183 impl<'a, 'b> Args<'a, 'b> {
new(inner: &'a mut dyn Iterator<Item = &'b [u8]>) -> Args<'a, 'b>184     pub(crate) fn new(inner: &'a mut dyn Iterator<Item = &'b [u8]>) -> Args<'a, 'b> {
185         Args { inner }
186     }
187 }
188 
189 impl<'args> Iterator for Args<'_, 'args> {
190     type Item = &'args [u8];
191 
next(&mut self) -> Option<Self::Item>192     fn next(&mut self) -> Option<Self::Item> {
193         self.inner.next()
194     }
195 }
196 
197 /// Enable/Disable ASLR for spawned processes (for a more consistent debugging
198 /// experience).
199 ///
200 /// Corresponds to GDB's [`set disable-randomization`](https://sourceware.org/gdb/onlinedocs/gdb/Starting.html) command.
201 pub trait ConfigureASLR: ExtendedMode {
202     /// Enable/Disable ASLR for spawned processes.
cfg_aslr(&mut self, enabled: bool) -> TargetResult<(), Self>203     fn cfg_aslr(&mut self, enabled: bool) -> TargetResult<(), Self>;
204 }
205 
206 define_ext!(ConfigureASLROps, ConfigureASLR);
207 
208 /// Set/Remove/Reset the Environment variables for spawned processes.
209 ///
210 /// Corresponds to GDB's [`set environment`](https://sourceware.org/gdb/onlinedocs/gdb/Environment.html#set-environment) cmd.
211 ///
212 /// _Note:_ Environment variables are not guaranteed to be UTF-8, and are passed
213 /// as raw byte arrays. If the provided keys/values could not be converted into
214 /// an appropriate representation, a non fatal error should be returned.
215 pub trait ConfigureEnv: ExtendedMode {
216     /// Set an environment variable.
set_env(&mut self, key: &[u8], val: Option<&[u8]>) -> TargetResult<(), Self>217     fn set_env(&mut self, key: &[u8], val: Option<&[u8]>) -> TargetResult<(), Self>;
218 
219     /// Remove an environment variable.
remove_env(&mut self, key: &[u8]) -> TargetResult<(), Self>220     fn remove_env(&mut self, key: &[u8]) -> TargetResult<(), Self>;
221 
222     /// Reset all environment variables to their initial state (i.e: undo all
223     /// previous `set/remove_env` calls).
reset_env(&mut self) -> TargetResult<(), Self>224     fn reset_env(&mut self) -> TargetResult<(), Self>;
225 }
226 
227 define_ext!(ConfigureEnvOps, ConfigureEnv);
228 
229 /// Configure if spawned processes should be spawned using a shell.
230 ///
231 /// Corresponds to GDB's [`set startup-with-shell`](https://sourceware.org/gdb/onlinedocs/gdb/Starting.html) command.
232 pub trait ConfigureStartupShell: ExtendedMode {
233     /// Configure if spawned processes should be spawned using a shell.
234     ///
235     /// On UNIX-like targets, it is possible to start the inferior using a shell
236     /// program. This is the default behavior on both `GDB` and `gdbserver`.
cfg_startup_with_shell(&mut self, enabled: bool) -> TargetResult<(), Self>237     fn cfg_startup_with_shell(&mut self, enabled: bool) -> TargetResult<(), Self>;
238 }
239 
240 define_ext!(ConfigureStartupShellOps, ConfigureStartupShell);
241 
242 /// Configure the working directory for spawned processes.
243 ///
244 /// Corresponds to GDB's [`set cwd` and `cd`](https://sourceware.org/gdb/onlinedocs/gdb/Working-Directory.html) commands.
245 pub trait ConfigureWorkingDir: ExtendedMode {
246     /// Set the working directory for spawned processes.
247     ///
248     /// If no directory is provided, the stub should reset the value to it's
249     /// original value.
250     ///
251     /// The path is not guaranteed to be valid UTF-8, and is passed as a raw
252     /// byte array. If the path could not be converted into an appropriate
253     /// representation, a non fatal error should be returned.
cfg_working_dir(&mut self, dir: Option<&[u8]>) -> TargetResult<(), Self>254     fn cfg_working_dir(&mut self, dir: Option<&[u8]>) -> TargetResult<(), Self>;
255 }
256 
257 define_ext!(ConfigureWorkingDirOps, ConfigureWorkingDir);
258