blob: fc9ce1728470464e7a88dd0b707fa878b6529e36 [file] [log] [blame]
//! An ergonomic and easy-to-integrate implementation of the
//! [GDB Remote Serial Protocol](https://sourceware.org/gdb/onlinedocs/gdb/Remote-Protocol.html#Remote-Protocol)
//! in Rust, with full `#![no_std]` support.
//!
//! ## Getting Started
//!
//! This section provides a brief overview of the key traits and types used in
//! `gdbstub`, and walks though the basic steps required to integrate `gdbstub`
//! into a project.
//!
//! At a high level, there are only two things that are required to get up and
//! running with `gdbstub`: a [`Connection`](#the-connection-trait), and a
//! [`Target`](#the-target-trait)
//!
//! > _Note:_ I _highly recommended_ referencing some of the
//! [examples](https://github.com/daniel5151/gdbstub/blob/master/README.md#examples)
//! listed in the project README when integrating `gdbstub` into a project for
//! the first time.
//!
//! > In particular, the in-tree
//! [`armv4t`](https://github.com/daniel5151/gdbstub/tree/master/examples/armv4t)
//! example contains basic implementations off almost all protocol extensions,
//! making it an incredibly valuable reference when implementing protocol
//! extensions.
//!
//! ### The `Connection` Trait
//!
//! First things first: `gdbstub` needs some way to communicate with a GDB
//! client. To facilitate this communication, `gdbstub` uses a custom
//! [`Connection`] trait.
//!
//! `Connection` is automatically implemented for common `std` types such as
//! [`TcpStream`](std::net::TcpStream) and
//! [`UnixStream`](std::os::unix::net::UnixStream).
//!
//! If you're using `gdbstub` in a `#![no_std]` environment, `Connection` will
//! most likely need to be manually implemented on top of whatever in-order,
//! serial, byte-wise I/O your particular platform has available (e.g:
//! putchar/getchar over UART, using an embedded TCP stack, etc.).
//!
//! One common way to start a remote debugging session is to simply wait for a
//! GDB client to connect via TCP:
//!
//! ```rust
//! use std::io;
//! use std::net::{TcpListener, TcpStream};
//!
//! fn wait_for_gdb_connection(port: u16) -> io::Result<TcpStream> {
//! let sockaddr = format!("localhost:{}", port);
//! eprintln!("Waiting for a GDB connection on {:?}...", sockaddr);
//! let sock = TcpListener::bind(sockaddr)?;
//! let (stream, addr) = sock.accept()?;
//!
//! // Blocks until a GDB client connects via TCP.
//! // i.e: Running `target remote localhost:<port>` from the GDB prompt.
//!
//! eprintln!("Debugger connected from {}", addr);
//! Ok(stream) // `TcpStream` implements `gdbstub::Connection`
//! }
//! ```
//!
//! ### The `Target` Trait
//!
//! The [`Target`](target::Target) trait describes how to control and modify
//! a system's execution state during a GDB debugging session, and serves as the
//! primary bridge between `gdbstub`'s generic GDB protocol implementation and a
//! specific target's project/platform-specific code.
//!
//! At a high level, the `Target` trait is a collection of user-defined handler
//! methods that the GDB client can invoke via the GDB remote serial protocol.
//! For example, the `Target` trait includes methods to read/write
//! registers/memory, start/stop execution, etc...
//!
//! **`Target` is the most important trait in `gdbstub`, and must be implemented
//! by anyone integrating `gdbstub` into their project!**
//!
//! Please refer to the [`target` module documentation](target) for in-depth
//! instructions on how to implement [`Target`](target::Target) for a particular
//! platform.
//!
//! ### Starting the debugging session using `GdbStub`
//!
//! Once a [`Connection`](#the-connection-trait) has been established and
//! [`Target`](#the-target-trait) has been all wired up, all that's left is to
//! hand things off to [`gdbstub::GdbStub`](GdbStub) and let it do the rest!
//!
//! ```rust,ignore
//! // Set-up a valid `Target`
//! let mut target = MyTarget::new()?; // implements `Target`
//!
//! // Establish a `Connection`
//! let connection: TcpStream = wait_for_gdb_connection(9001);
//!
//! // Create a new `gdbstub::GdbStub` using the established `Connection`.
//! let mut debugger = gdbstub::GdbStub::new(connection);
//!
//! // Instead of taking ownership of the system, `GdbStub` takes a &mut, yielding
//! // ownership back to the caller once the debugging session is closed.
//! match debugger.run(&mut target) {
//! Ok(disconnect_reason) => match disconnect_reason {
//! DisconnectReason::Disconnect => println!("GDB client disconnected."),
//! DisconnectReason::TargetHalted => println!("Target halted!"),
//! DisconnectReason::Kill => println!("GDB client sent a kill command!"),
//! }
//! // Handle any target-specific errors
//! Err(GdbStubError::TargetError(e)) => {
//! println!("Target raised a fatal error: {:?}", e);
//! // `gdbstub` will not immediate close the debugging session if a
//! // fatal error occurs, enabling "post mortem" debugging if required.
//! debugger.run(&mut target)?;
//! }
//! Err(e) => return Err(e.into())
//! }
//! ```
//!
//! ## Feature flags
//!
//! By default, both the `std` and `alloc` features are enabled.
//!
//! When using `gdbstub` in `#![no_std]` contexts, make sure to set
//! `default-features = false`.
//!
//! - `alloc`
//! - Implement `Connection` for `Box<dyn Connection>`.
//! - Log outgoing packets via `log::trace!` (uses a heap-allocated output
//! buffer).
//! - Provide built-in implementations for certain protocol features:
//! - Use a heap-allocated packet buffer in `GdbStub` (if none is
//! provided via `GdbStubBuilder::with_packet_buffer`).
//! - (Monitor Command) Use a heap-allocated output buffer in
//! `ConsoleOutput`.
//! - `std` (implies `alloc`)
//! - Implement `Connection` for [`TcpStream`](std::net::TcpStream) and
//! [`UnixStream`](std::os::unix::net::UnixStream).
//! - Implement [`std::error::Error`] for `gdbstub::Error`.
//! - Add a `TargetError::Io` error variant to simplify I/O Error handling
//! from `Target` methods.
#![cfg_attr(not(feature = "std"), no_std)]
#![deny(missing_docs)]
#[cfg(feature = "alloc")]
extern crate alloc;
#[macro_use]
extern crate log;
mod connection;
mod gdbstub_impl;
mod protocol;
mod util;
#[doc(hidden)]
pub mod internal;
pub mod arch;
pub mod common;
pub mod target;
pub use connection::Connection;
pub use gdbstub_impl::*;
/// (Internal) The fake Tid that's used when running in single-threaded mode.
// SAFETY: 1 is clearly non-zero.
const SINGLE_THREAD_TID: common::Tid = unsafe { common::Tid::new_unchecked(1) };
/// (Internal) The fake Pid reported to GDB (since `gdbstub` only supports
/// debugging a single process).
const FAKE_PID: common::Pid = unsafe { common::Pid::new_unchecked(1) };