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