• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! An ergonomic and easy-to-integrate implementation of the
2 //! [GDB Remote Serial Protocol](https://sourceware.org/gdb/onlinedocs/gdb/Remote-Protocol.html#Remote-Protocol)
3 //! in Rust, with full `#![no_std]` support.
4 //!
5 //! ## Getting Started
6 //!
7 //! This section provides a brief overview of the key traits and types used in
8 //! `gdbstub`, and walks though the basic steps required to integrate `gdbstub`
9 //! into a project.
10 //!
11 //! At a high level, there are only two things that are required to get up and
12 //! running with `gdbstub`: a [`Connection`](#the-connection-trait), and a
13 //! [`Target`](#the-target-trait)
14 //!
15 //! > _Note:_ I _highly recommended_ referencing some of the
16 //! [examples](https://github.com/daniel5151/gdbstub/blob/master/README.md#examples)
17 //! listed in the project README when integrating `gdbstub` into a project for
18 //! the first time.
19 //!
20 //! > In particular, the in-tree
21 //! [`armv4t`](https://github.com/daniel5151/gdbstub/tree/master/examples/armv4t)
22 //! example contains basic implementations off almost all protocol extensions,
23 //! making it an incredibly valuable reference when implementing protocol
24 //! extensions.
25 //!
26 //! ### The `Connection` Trait
27 //!
28 //! First things first: `gdbstub` needs some way to communicate with a GDB
29 //! client. To facilitate this communication, `gdbstub` uses a custom
30 //! [`Connection`] trait.
31 //!
32 //! `Connection` is automatically implemented for common `std` types such as
33 //! [`TcpStream`](std::net::TcpStream) and
34 //! [`UnixStream`](std::os::unix::net::UnixStream).
35 //!
36 //! If you're using `gdbstub` in a `#![no_std]` environment, `Connection` will
37 //! most likely need to be manually implemented on top of whatever in-order,
38 //! serial, byte-wise I/O your particular platform has available (e.g:
39 //! putchar/getchar over UART, using an embedded TCP stack, etc.).
40 //!
41 //! One common way to start a remote debugging session is to simply wait for a
42 //! GDB client to connect via TCP:
43 //!
44 //! ```rust
45 //! use std::io;
46 //! use std::net::{TcpListener, TcpStream};
47 //!
48 //! fn wait_for_gdb_connection(port: u16) -> io::Result<TcpStream> {
49 //!     let sockaddr = format!("localhost:{}", port);
50 //!     eprintln!("Waiting for a GDB connection on {:?}...", sockaddr);
51 //!     let sock = TcpListener::bind(sockaddr)?;
52 //!     let (stream, addr) = sock.accept()?;
53 //!
54 //!     // Blocks until a GDB client connects via TCP.
55 //!     // i.e: Running `target remote localhost:<port>` from the GDB prompt.
56 //!
57 //!     eprintln!("Debugger connected from {}", addr);
58 //!     Ok(stream) // `TcpStream` implements `gdbstub::Connection`
59 //! }
60 //! ```
61 //!
62 //! ### The `Target` Trait
63 //!
64 //! The [`Target`](target::Target) trait describes how to control and modify
65 //! a system's execution state during a GDB debugging session, and serves as the
66 //! primary bridge between `gdbstub`'s generic GDB protocol implementation and a
67 //! specific target's project/platform-specific code.
68 //!
69 //! At a high level, the `Target` trait is a collection of user-defined handler
70 //! methods that the GDB client can invoke via the GDB remote serial protocol.
71 //! For example, the `Target` trait includes methods to read/write
72 //! registers/memory, start/stop execution, etc...
73 //!
74 //! **`Target` is the most important trait in `gdbstub`, and must be implemented
75 //! by anyone integrating `gdbstub` into their project!**
76 //!
77 //! Please refer to the [`target` module documentation](target) for in-depth
78 //! instructions on how to implement [`Target`](target::Target) for a particular
79 //! platform.
80 //!
81 //! ### Starting the debugging session using `GdbStub`
82 //!
83 //! Once a [`Connection`](#the-connection-trait) has been established and
84 //! [`Target`](#the-target-trait) has been all wired up, all that's left is to
85 //! hand things off to [`gdbstub::GdbStub`](GdbStub) and let it do the rest!
86 //!
87 //! ```rust,ignore
88 //! // Set-up a valid `Target`
89 //! let mut target = MyTarget::new()?; // implements `Target`
90 //!
91 //! // Establish a `Connection`
92 //! let connection: TcpStream = wait_for_gdb_connection(9001);
93 //!
94 //! // Create a new `gdbstub::GdbStub` using the established `Connection`.
95 //! let mut debugger = gdbstub::GdbStub::new(connection);
96 //!
97 //! // Instead of taking ownership of the system, `GdbStub` takes a &mut, yielding
98 //! // ownership back to the caller once the debugging session is closed.
99 //! match debugger.run(&mut target) {
100 //!     Ok(disconnect_reason) => match disconnect_reason {
101 //!         DisconnectReason::Disconnect => println!("GDB client disconnected."),
102 //!         DisconnectReason::TargetHalted => println!("Target halted!"),
103 //!         DisconnectReason::Kill => println!("GDB client sent a kill command!"),
104 //!     }
105 //!     // Handle any target-specific errors
106 //!     Err(GdbStubError::TargetError(e)) => {
107 //!         println!("Target raised a fatal error: {:?}", e);
108 //!         // `gdbstub` will not immediate close the debugging session if a
109 //!         // fatal error occurs, enabling "post mortem" debugging if required.
110 //!         debugger.run(&mut target)?;
111 //!     }
112 //!     Err(e) => return Err(e.into())
113 //! }
114 //! ```
115 //!
116 //! ## Feature flags
117 //!
118 //! By default, both the `std` and `alloc` features are enabled.
119 //!
120 //! When using `gdbstub` in `#![no_std]` contexts, make sure to set
121 //! `default-features = false`.
122 //!
123 //! - `alloc`
124 //!     - Implement `Connection` for `Box<dyn Connection>`.
125 //!     - Log outgoing packets via `log::trace!` (uses a heap-allocated output
126 //!       buffer).
127 //!     - Provide built-in implementations for certain protocol features:
128 //!         - Use a heap-allocated packet buffer in `GdbStub` (if none is
129 //!           provided via `GdbStubBuilder::with_packet_buffer`).
130 //!         - (Monitor Command) Use a heap-allocated output buffer in
131 //!           `ConsoleOutput`.
132 //! - `std` (implies `alloc`)
133 //!     - Implement `Connection` for [`TcpStream`](std::net::TcpStream) and
134 //!       [`UnixStream`](std::os::unix::net::UnixStream).
135 //!     - Implement [`std::error::Error`] for `gdbstub::Error`.
136 //!     - Add a `TargetError::Io` error variant to simplify I/O Error handling
137 //!       from `Target` methods.
138 
139 #![cfg_attr(not(feature = "std"), no_std)]
140 #![deny(missing_docs)]
141 
142 #[cfg(feature = "alloc")]
143 extern crate alloc;
144 
145 #[macro_use]
146 extern crate log;
147 
148 mod connection;
149 mod gdbstub_impl;
150 mod protocol;
151 mod util;
152 
153 #[doc(hidden)]
154 pub mod internal;
155 
156 pub mod arch;
157 pub mod common;
158 pub mod target;
159 
160 pub use connection::Connection;
161 pub use gdbstub_impl::*;
162 
163 /// (Internal) The fake Tid that's used when running in single-threaded mode.
164 // SAFETY: 1 is clearly non-zero.
165 const SINGLE_THREAD_TID: common::Tid = unsafe { common::Tid::new_unchecked(1) };
166 /// (Internal) The fake Pid reported to GDB (since `gdbstub` only supports
167 /// debugging a single process).
168 const FAKE_PID: common::Pid = unsafe { common::Pid::new_unchecked(1) };
169