Crate gdbstub[][src]

Expand description

An ergonomic and easy-to-integrate implementation of the GDB Remote Serial 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, and a Target

Note: I highly recommended referencing some of the examples listed in the project README when integrating gdbstub into a project for the first time.

In particular, the in-tree 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 and 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:

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 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 for in-depth instructions on how to implement Target for a particular platform.

Starting the debugging session using GdbStub

Once a Connection has been established and Target has been all wired up, all that’s left is to hand things off to gdbstub::GdbStub and let it do the rest!

// 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 and UnixStream.
    • Implement std::error::Error for gdbstub::Error.
    • Add a TargetError::Io error variant to simplify I/O Error handling from Target methods.

Modules

arch

Traits to encode architecture-specific target information.

common

Common types and definitions.

target

The core Target trait, and all its various protocol extension traits.

Macros

output

Send formatted data to the GDB client console.

outputln

Send formatted data to the GDB client console, with a newline appended.

Structs

GdbStub

Debug a Target using the GDB Remote Serial Protocol over a given Connection.

GdbStubBuilder

Helper to construct and customize GdbStub.

Enums

DisconnectReason

Describes why the GDB session ended.

GdbStubBuilderError

An error which may occur when building a GdbStub.

GdbStubError

An error which may occur during a GDB debugging session.

Traits

Connection

A trait to perform in-order, serial, byte-wise I/O.