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