gdbstub/
lib.rs

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")]
320extern crate alloc;
321
322#[macro_use]
323extern crate log;
324
325mod protocol;
326mod util;
327
328#[doc(hidden)]
329pub mod internal;
330
331pub mod arch;
332pub mod common;
333pub mod conn;
334pub mod stub;
335pub 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.
341macro_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.
355const 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.
358const FAKE_PID: common::Pid = unwrap!(common::Pid::new(1));
359
360pub(crate) mod is_valid_tid {
361    pub trait IsValidTid {}
362
363    impl IsValidTid for () {}
364    impl IsValidTid for crate::common::Tid {}
365}