1 //! An ergonomic, featureful, and easy-to-integrate implementation of the [GDB 2 //! Remote Serial Protocol](https://sourceware.org/gdb/onlinedocs/gdb/Remote-Protocol.html#Remote-Protocol) 3 //! in Rust, with no-compromises `#![no_std]` support. 4 //! 5 //! ## Feature flags 6 //! 7 //! By default, both the `std` and `alloc` features are enabled. 8 //! 9 //! When using `gdbstub` in `#![no_std]` contexts, make sure to set 10 //! `default-features = false`. 11 //! 12 //! - `alloc` 13 //! - Implement `Connection` for `Box<dyn Connection>`. 14 //! - Log outgoing packets via `log::trace!` (uses a heap-allocated output 15 //! buffer). 16 //! - Provide built-in implementations for certain protocol features: 17 //! - Use a heap-allocated packet buffer in `GdbStub` (if none is 18 //! provided via `GdbStubBuilder::with_packet_buffer`). 19 //! - (Monitor Command) Use a heap-allocated output buffer in 20 //! `ConsoleOutput`. 21 //! - `std` (implies `alloc`) 22 //! - Implement `Connection` for [`TcpStream`](std::net::TcpStream) and 23 //! [`UnixStream`](std::os::unix::net::UnixStream). 24 //! - Implement [`std::error::Error`] for `gdbstub::Error`. 25 //! - Add a `TargetError::Io` variant to simplify `std::io::Error` handling 26 //! from Target methods. 27 //! - `paranoid_unsafe` 28 //! - Please refer to the [`unsafe` in `gdbstub`](https://github.com/daniel5151/gdbstub#unsafe-in-gdbstub) 29 //! section of the README.md for more details. 30 //! - `core_error` 31 //! - Make `GdbStubError` implement [`core::error::Error`](https://doc.rust-lang.org/core/error/trait.Error.html) 32 //! instead of `std::error::Error`. 33 //! 34 //! ## Getting Started 35 //! 36 //! This section provides a brief overview of the key traits and types used in 37 //! `gdbstub`, and walks though the basic steps required to integrate `gdbstub` 38 //! into a project. 39 //! 40 //! At a high level, there are only three things that are required to get up and 41 //! running with `gdbstub`: a [`Connection`](#the-connection-trait), a 42 //! [`Target`](#the-target-trait), and a [event loop](#the-event-loop). 43 //! 44 //! > _Note:_ I _highly recommended_ referencing some of the 45 //! [examples](https://github.com/daniel5151/gdbstub#examples) listed in the 46 //! project README when integrating `gdbstub` into a project for the first time. 47 //! 48 //! > In particular, the in-tree 49 //! [`armv4t`](https://github.com/daniel5151/gdbstub/tree/master/examples/armv4t) 50 //! example contains basic implementations off almost all protocol extensions, 51 //! making it an incredibly valuable reference when implementing protocol 52 //! extensions. 53 //! 54 //! ### The `Connection` Trait 55 //! 56 //! First things first: `gdbstub` needs some way to communicate with a GDB 57 //! client. To facilitate this communication, `gdbstub` uses a custom 58 //! [`Connection`](conn::Connection) trait. 59 //! 60 //! `Connection` is automatically implemented for common `std` types such as 61 //! [`TcpStream`](std::net::TcpStream) and 62 //! [`UnixStream`](std::os::unix::net::UnixStream). 63 //! 64 //! If you're using `gdbstub` in a `#![no_std]` environment, `Connection` will 65 //! most likely need to be manually implemented on top of whatever in-order, 66 //! serial, byte-wise I/O your particular platform has available (e.g: 67 //! putchar/getchar over UART, using an embedded TCP stack, etc.). 68 //! 69 //! One common way to start a remote debugging session is to simply wait for a 70 //! GDB client to connect via TCP: 71 //! 72 //! ```rust 73 //! use std::io; 74 //! use std::net::{TcpListener, TcpStream}; 75 //! 76 //! fn wait_for_gdb_connection(port: u16) -> io::Result<TcpStream> { 77 //! let sockaddr = format!("localhost:{}", port); 78 //! eprintln!("Waiting for a GDB connection on {:?}...", sockaddr); 79 //! let sock = TcpListener::bind(sockaddr)?; 80 //! let (stream, addr) = sock.accept()?; 81 //! 82 //! // Blocks until a GDB client connects via TCP. 83 //! // i.e: Running `target remote localhost:<port>` from the GDB prompt. 84 //! 85 //! eprintln!("Debugger connected from {}", addr); 86 //! Ok(stream) // `TcpStream` implements `gdbstub::Connection` 87 //! } 88 //! ``` 89 //! 90 //! ### The `Target` Trait 91 //! 92 //! The [`Target`](target::Target) trait describes how to control and modify a 93 //! system's execution state during a GDB debugging session, and serves as the 94 //! primary bridge between `gdbstub`'s generic GDB protocol implementation and a 95 //! specific target's project/platform-specific code. 96 //! 97 //! At a high level, the `Target` trait is a collection of user-defined handler 98 //! methods that the GDB client can invoke via the GDB remote serial protocol. 99 //! For example, the `Target` trait includes methods to read/write 100 //! registers/memory, start/stop execution, etc... 101 //! 102 //! **`Target` is the most important trait in `gdbstub`, and must be implemented 103 //! by anyone integrating `gdbstub` into their project!** 104 //! 105 //! Please refer to the [`target` module documentation](target) for in-depth 106 //! instructions on how to implement [`Target`](target::Target) for a particular 107 //! platform. 108 //! 109 //! ## The Event Loop 110 //! 111 //! Once a [`Connection`](#the-connection-trait) has been established and the 112 //! [`Target`](#the-target-trait) has been initialized, all that's left is to 113 //! wire things up and decide what kind of event loop will be used to drive the 114 //! debugging session! 115 //! 116 //! First things first, let's get an instance of `GdbStub` ready to run: 117 //! 118 //! ```rust,ignore 119 //! // Set-up a valid `Target` 120 //! let mut target = MyTarget::new()?; // implements `Target` 121 //! 122 //! // Establish a `Connection` 123 //! let connection: TcpStream = wait_for_gdb_connection(9001); 124 //! 125 //! // Create a new `gdbstub::GdbStub` using the established `Connection`. 126 //! let mut debugger = gdbstub::GdbStub::new(connection); 127 //! ``` 128 //! 129 //! Cool, but how do you actually start the debugging session? 130 // use an explicit doc attribute to avoid automatic rustfmt wrapping 131 #![doc = "### `GdbStub::run_blocking`: The quick and easy way to get up and running with `gdbstub`"] 132 //! 133 //! If you've got an extra thread to spare, the quickest way to get up and 134 //! running with `gdbstub` is by using the 135 //! [`GdbStub::run_blocking`](stub::run_blocking) API alongside the 136 //! [`BlockingEventLoop`] trait. 137 //! 138 //! If you are on a more resource constrained platform, and/or don't wish to 139 //! dedicate an entire thread to `gdbstub`, feel free to skip ahead to the 140 //! [following 141 //! section](#gdbstubstatemachine-driving-gdbstub-in-an-async-event-loop--via-interrupt-handlers). 142 //! 143 //! A basic integration of `gdbstub` into a project using the 144 //! `GdbStub::run_blocking` API might look something like this: 145 //! 146 //! ```rust 147 //! # use gdbstub::target::ext::base::BaseOps; 148 //! # 149 //! # struct MyTarget; 150 //! # 151 //! # impl Target for MyTarget { 152 //! # type Error = &'static str; 153 //! # type Arch = gdbstub_arch::arm::Armv4t; // as an example 154 //! # fn base_ops(&mut self) -> BaseOps<Self::Arch, Self::Error> { todo!() } 155 //! # } 156 //! # 157 //! # impl MyTarget { 158 //! # fn run_and_check_for_incoming_data( 159 //! # &mut self, 160 //! # conn: &mut impl Connection 161 //! # ) -> MyTargetEvent { todo!() } 162 //! # 163 //! # fn stop_in_response_to_ctrl_c_interrupt( 164 //! # &mut self 165 //! # ) -> Result<(), &'static str> { todo!() } 166 //! # } 167 //! # 168 //! # enum MyTargetEvent { 169 //! # IncomingData, 170 //! # StopReason(SingleThreadStopReason<u32>), 171 //! # } 172 //! # 173 //! use gdbstub::common::Signal; 174 //! use gdbstub::conn::{Connection, ConnectionExt}; // note the use of `ConnectionExt` 175 //! use gdbstub::stub::{run_blocking, DisconnectReason, GdbStub}; 176 //! use gdbstub::stub::SingleThreadStopReason; 177 //! use gdbstub::target::Target; 178 //! 179 //! enum MyGdbBlockingEventLoop {} 180 //! 181 //! // The `run_blocking::BlockingEventLoop` groups together various callbacks 182 //! // the `GdbStub::run_blocking` event loop requires you to implement. 183 //! impl run_blocking::BlockingEventLoop for MyGdbBlockingEventLoop { 184 //! type Target = MyTarget; 185 //! type Connection = Box<dyn ConnectionExt<Error = std::io::Error>>; 186 //! 187 //! // or MultiThreadStopReason on multi threaded targets 188 //! type StopReason = SingleThreadStopReason<u32>; 189 //! 190 //! // Invoked immediately after the target's `resume` method has been 191 //! // called. The implementation should block until either the target 192 //! // reports a stop reason, or if new data was sent over the connection. 193 //! fn wait_for_stop_reason( 194 //! target: &mut MyTarget, 195 //! conn: &mut Self::Connection, 196 //! ) -> Result< 197 //! run_blocking::Event<SingleThreadStopReason<u32>>, 198 //! run_blocking::WaitForStopReasonError< 199 //! <Self::Target as Target>::Error, 200 //! <Self::Connection as Connection>::Error, 201 //! >, 202 //! > { 203 //! // the specific mechanism to "select" between incoming data and target 204 //! // events will depend on your project's architecture. 205 //! // 206 //! // some examples of how you might implement this method include: `epoll`, 207 //! // `select!` across multiple event channels, periodic polling, etc... 208 //! // 209 //! // in this example, lets assume the target has a magic method that handles 210 //! // this for us. 211 //! let event = match target.run_and_check_for_incoming_data(conn) { 212 //! MyTargetEvent::IncomingData => { 213 //! let byte = conn 214 //! .read() // method provided by the `ConnectionExt` trait 215 //! .map_err(run_blocking::WaitForStopReasonError::Connection)?; 216 //! 217 //! run_blocking::Event::IncomingData(byte) 218 //! } 219 //! MyTargetEvent::StopReason(reason) => { 220 //! run_blocking::Event::TargetStopped(reason) 221 //! } 222 //! }; 223 //! 224 //! Ok(event) 225 //! } 226 //! 227 //! // Invoked when the GDB client sends a Ctrl-C interrupt. 228 //! fn on_interrupt( 229 //! target: &mut MyTarget, 230 //! ) -> Result<Option<SingleThreadStopReason<u32>>, <MyTarget as Target>::Error> { 231 //! // notify the target that a ctrl-c interrupt has occurred. 232 //! target.stop_in_response_to_ctrl_c_interrupt()?; 233 //! 234 //! // a pretty typical stop reason in response to a Ctrl-C interrupt is to 235 //! // report a "Signal::SIGINT". 236 //! Ok(Some(SingleThreadStopReason::Signal(Signal::SIGINT).into())) 237 //! } 238 //! } 239 //! 240 //! fn gdb_event_loop_thread( 241 //! debugger: GdbStub<MyTarget, Box<dyn ConnectionExt<Error = std::io::Error>>>, 242 //! mut target: MyTarget 243 //! ) { 244 //! match debugger.run_blocking::<MyGdbBlockingEventLoop>(&mut target) { 245 //! Ok(disconnect_reason) => match disconnect_reason { 246 //! DisconnectReason::Disconnect => { 247 //! println!("Client disconnected") 248 //! } 249 //! DisconnectReason::TargetExited(code) => { 250 //! println!("Target exited with code {}", code) 251 //! } 252 //! DisconnectReason::TargetTerminated(sig) => { 253 //! println!("Target terminated with signal {}", sig) 254 //! } 255 //! DisconnectReason::Kill => println!("GDB sent a kill command"), 256 //! }, 257 //! Err(e) => { 258 //! if e.is_target_error() { 259 //! println!( 260 //! "target encountered a fatal error: {}", 261 //! e.into_target_error().unwrap() 262 //! ) 263 //! } else if e.is_connection_error() { 264 //! let (e, kind) = e.into_connection_error().unwrap(); 265 //! println!("connection error: {:?} - {}", kind, e,) 266 //! } else { 267 //! println!("gdbstub encountered a fatal error: {}", e) 268 //! } 269 //! } 270 //! } 271 //! } 272 //! ``` 273 // use an explicit doc attribute to avoid automatic rustfmt wrapping 274 #![doc = "### `GdbStubStateMachine`: Driving `gdbstub` in an async event loop / via interrupt handlers"] 275 //! 276 //! `GdbStub::run_blocking` requires that the target implement the 277 //! [`BlockingEventLoop`] trait, which as the name implies, uses _blocking_ IO 278 //! when handling certain events. Blocking the thread is a totally reasonable 279 //! approach in most implementations, as one can simply spin up a separate 280 //! thread to run the GDB stub (or in certain emulator implementations, run the 281 //! emulator as part of the `wait_for_stop_reason` method). 282 //! 283 //! Unfortunately, this blocking behavior can be a non-starter when integrating 284 //! `gdbstub` in projects that don't support / wish to avoid the traditional 285 //! thread-based execution model, such as projects using `async/await`, or 286 //! bare-metal `no_std` projects running on embedded hardware. 287 //! 288 //! In these cases, `gdbstub` provides access to the underlying 289 //! [`GdbStubStateMachine`] API, which gives implementations full control over 290 //! the GDB stub's "event loop". This API requires implementations to "push" 291 //! data to the `gdbstub` implementation whenever new data becomes available 292 //! (e.g: when a UART interrupt handler receives a byte, when the target hits a 293 //! breakpoint, etc...), as opposed to the `GdbStub::run_blocking` API, which 294 //! "pulls" these events in a blocking manner. 295 //! 296 //! See the [`GdbStubStateMachine`] docs for more details on how to use this 297 //! API. 298 //! 299 //! <br> 300 //! 301 //! * * * 302 //! 303 //! <br> 304 //! 305 //! And with that lengthy introduction, I wish you the best of luck in your 306 //! debugging adventures! 307 //! 308 //! If you have any suggestions, feature requests, or run into any problems, 309 //! please start a discussion / open an issue over on the 310 //! [`gdbstub` GitHub repo](https://github.com/daniel5151/gdbstub/). 311 //! 312 //! [`GdbStubStateMachine`]: stub::state_machine::GdbStubStateMachine 313 //! [`BlockingEventLoop`]: stub::run_blocking::BlockingEventLoop 314 315 #![cfg_attr(not(feature = "std"), no_std)] 316 #![cfg_attr(feature = "paranoid_unsafe", forbid(unsafe_code))] 317 #![warn(missing_docs)] 318 319 #[cfg(feature = "alloc")] 320 extern crate alloc; 321 322 #[macro_use] 323 extern crate log; 324 325 mod protocol; 326 mod util; 327 328 #[doc(hidden)] 329 pub mod internal; 330 331 pub mod arch; 332 pub mod common; 333 pub mod conn; 334 pub mod stub; 335 pub mod target; 336 337 // https://users.rust-lang.org/t/compile-time-const-unwrapping/51619/7 338 // 339 // This works from Rust 1.46.0 onwards, which stabilized branching and looping 340 // in const contexts. 341 macro_rules! unwrap { 342 ($e:expr $(,)*) => { 343 match $e { 344 ::core::option::Option::Some(x) => x, 345 #[allow(clippy::out_of_bounds_indexing)] 346 ::core::option::Option::None => { 347 ["tried to unwrap a None"][99]; 348 loop {} 349 } 350 } 351 }; 352 } 353 354 /// (Internal) The fake Tid that's used when running in single-threaded mode. 355 const SINGLE_THREAD_TID: common::Tid = unwrap!(common::Tid::new(1)); 356 /// (Internal) The fake Pid reported to GDB when the target hasn't opted into 357 /// reporting a custom Pid itself. 358 const FAKE_PID: common::Pid = unwrap!(common::Pid::new(1)); 359 360 pub(crate) mod is_valid_tid { 361 pub trait IsValidTid {} 362 363 impl IsValidTid for () {} 364 impl IsValidTid for crate::common::Tid {} 365 } 366