An ergonomic and easy-to-integrate implementation of the GDB Remote Serial Protocol in Rust, with full
gdbstub makes it easy to integrate powerful guest debugging support to your emulator / hypervisor / debugger / embedded project. By implementing just a few basic methods of the
gdbstub::Target trait, you can have a rich GDB debugging session up and running in no time!
gdbstub's API makes extensive use of a technique called Inlineable Dyn Extension Traits (IDETs) to expose fine-grained, zero-cost control over enabled GDB protocol features without relying on compile-time features flags. Aside from making it effortless to toggle enabled protocol features, IDETs also ensure that any unimplemented features are guaranteed to be dead-code-eliminated in release builds!
If you're looking for a quick snippet of example code to see what a typical
gdbstub integration might look like, check out examples/armv4t/gdb/mod.rs
gdbstubtries to abstract as much of the raw GDB protocol details from the user.
gdbstubcomes with a community-curated collection of built-in architecture definitions for most popular platforms!
gdbstubmakes extensive use of Rust's powerful type system + generics to enforce protocol invariants at compile time, minimizing the number of tricky protocol details end users have to worry about.
gdbstubenables fine-grained control over active protocol extensions without relying on clunky
cargofeatures or the use of
gdbstub‘s API is designed to be a “drop in” solution when you want to add debugging support into a project, and shouldn’t require any large refactoring effort to integrate into an existing project.
#![no_std]Ready & Size Optimized
no_stdfirst library, whereby all protocol features are required to be
gdbstubdoes not require any dynamic memory allocation, and can be configured to use fixed-size, pre-allocated buffers. This enables
gdbstubto be used on even the most resource constrained, no-
gdbstubis entirely panic free in most minimal configurations*, resulting in substantially smaller and more robust code.
gdbstubis transport-layer agnostic, and uses a basic
Connectioninterface to communicate with the GDB server. As long as target has some method of performing in-order, serial, byte-wise I/O (e.g: putchar/getchar over UART), it's possible to run
gdbstub's minimal configuration has an incredibly low binary size + RAM overhead, enabling it to be used on even the most resource-constrained microcontrollers.
min-sized-rust, a baseline
gdbstubimplementation can weigh in at less than 10kb of
gdbstubrevision. In mixed-language projects, cross-language LTO may be required (#101). Data was collected using the included
example_no_stdproject compiled on x86_64.
Yes, as long as you don't mind some API churn until
1.0.0 is released.
gdbstub‘s heavy use of Rust’s type system in enforcing GDB protocol invariants at compile time, it's often been the case that implementing new GDB protocol features has required making some breaking API changes. While these changes are typically quite minor, they are nonetheless semver-breaking, and may require a code-change when moving between versions. Any particularly involved changes will typically be documented in a dedicated transition guide document.
That being said,
gdbstub has already been integrated into many real-world projects since its initial
0.1 release, and empirical evidence suggests that it seems to be doing its job quite well! Thusfar, most reported issues have been caused by improperly implemented
Arch implementations, while the core
gdbstub library itself has proven to be reasonably bug-free.
See the Future Plans + Roadmap to
1.0.0 for more information on what features
gdbstub still needs to implement before committing to API stability with version
The GDB Remote Serial Protocol is surprisingly complex, supporting advanced features such as remote file I/O, spawning new processes, “rewinding” program execution, and much, much more. Thankfully, most of these features are completely optional, and getting a basic debugging session up-and-running only requires implementing a few basic methods:
Yep, that‘s right! That’s all it takes to get
Of course, most use-cases will want to support additional debugging features as well. At the moment,
gdbstub implements the following GDB protocol extensions:
Note: GDB features are implemented on an as-needed basis by
gdbstub‘s contributors. If there’s a missing GDB feature that you'd like
gdbstub to implement, please file an issue and/or open a PR!
For a full list of GDB remote features, check out the GDB Remote Configuration Docs for a table of GDB commands + their corresponding Remote Serial Protocol packets.
Using a technique called Inlineable Dyn Extension Traits (IDETs),
gdbstub is able to leverage the Rust compiler's powerful optimization passes to ensure any unused features are dead-code-eliminated in release builds without having to rely on compile-time features flags!
For example, if your target doesn‘t implement a custom GDB
monitor command handler, the resulting binary won’t include any code related to parsing / handling the underlying
If you‘re interested in the low-level technical details of how IDETs work, I’ve included a brief writeup in the documentation here.
By default, the
alloc features are enabled.
#![no_std] contexts, make sure to set
default-features = false.
log::trace!(uses a heap-allocated output buffer).
GdbStub(if none is provided via
TargetError::Iovariant to simplify
std::io::Errorhandling from Target methods.
gdbstubsection below for more details.
While some of these projects may use older versions of
gdbstub, they can nonetheless serve as useful examples of what a typical
gdbstub integration might look like.
If you end up using
gdbstub in your project, consider opening a PR and adding it to this list!
vmware-labs/node-replicated-kernel- An (experimental) research OS kernel for x86-64 (amd64) machines
betrusted-io/xous-core- The Xous microkernel operating system
These examples are built as part of the CI, and are guaranteed to be kept up to date with the latest version of
target::extfeatures. This makes it a great resource when first implementing a new protocol extension!
gdbstub's multithread extensions API, but not much else.
gdbstubcan be used in a
armv4t/armv4t_multicoreexamples, this project does not include a working emulator, and simply stubs all
gdbstub's approximate binary footprint (via the
check_size.shscript), as well as validating certain dead-code-elimination optimizations.
gdbstub limits its use of
unsafe to a bare minimum, with all uses of
unsafe required to have a corresponding
// SAFETY comment as justification.
For those paranoid about trusting third-party unsafe code,
gdbstub comes with an opt-in
paranoid_unsafe feature, which enables
#![forbid(unsafe_code)] on the entire
gdbstub crate, swapping out all instances of
unsafe code with equivalent (albeit less-performant) alternatives.
The following list exhaustively documents all uses of
src/protocol/packet.rs: Method in
PacketBufthat use index using stored sub-
Range<usize>into the buffer
src/common.rs: Checked transmute of
std feature is enabled:
src/connection/impls/unixstream.rs: An implementation of
libc::recv. Will be removed once rust-lang/rust#76923 stabilizes this feature in the stdlib.
Ideally, the Rust compiler would have some way to opt-in to a strict “no-panic” mode. Unfortunately, at the time of writing (2022/04/24), no such mode exists. As such, the only way to avoid the Rust compiler + stdlib's implicit panics is by being very careful when writing code, and manually checking that those panicking paths get optimized out!
And when I say “manually checking”, I mean checking generated asm output.
Why even go through this effort?
no_stduse-cases, panic infrastructure brings in hundreds of additional bytes into the final binary.
gdbstubcan be used to implement low-level debuggers, and if the debugger itself panics, well... it's not like you can debug it all that easily!
gdbstub promises to introduce zero additional panics into an existing project, subject to the following conditions:
rustcversion being used (codegen and optimization vary between versions)
x86is actively tested to be panic-free
paranoid_unsafecargo feature is disabled
panicchecks without requiring a bit of
gdbstubsection for more details
Archimplementation being used doesn't include panicking code
gdbstub_archare not guaranteed to be panic free!
gdbstub_arch, consider opening a PR to fix it
If you're using
gdbstub in a no-panic project and have determined that
gdbstub is at fault for introducing a panicking code path, please file an issue!
While the vast majority of GDB protocol features (e.g: remote filesystem support, tracepoint packets, most query packets, etc...) should not require breaking API changes, the following features will most likely require at least some breaking API changes, and should therefore be implemented prior to
Not that this is not an exhaustive list, and is subject to change.
gdbstubalready implements multiprocess extensions “under-the-hood”, and just hard-codes a fake PID, so this is mostly a matter of “putting in the work”.
gdbstubrunning in a “bare-metal”
Additionally, while not strict blockers to
1.0.0, it would be good to explore these features as well:
gdbstubcommit to a MSRV?
async/awaitinterfaces (particularly wrt. handling GDB client interrupts) (#36)
gdbstub is free and open source! All code in this repository is dual-licensed under either:
at your option. This means you can select the license you prefer! This dual-licensing approach is the de-facto standard in the Rust ecosystem and there are very good reasons to include both.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.