• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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