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