blob: f3a0b54c9b17babed4791a976451e42256c5b308 [file] [log] [blame]
//! Extensions to [`Target`](super::Target) which add support for various
//! subsets of the GDB Remote Serial Protocol.
//!
//! On it's own, the [`Target`](super::Target) trait doesn't actually include
//! any methods to debug the target. Instead, `Target` uses a collection of
//! "Inlineable Dyn Extension Traits" (IDETs) to optionally implement various
//! subsets of the GDB protocol. For more details on IDETs, scroll down to the
//! [How Protocol Extensions Work - Inlineable Dyn Extension Traits
//! (IDETs)](#how-protocol-extensions-work---inlineable-dyn-extension-traits-idets)
//! section below.
//!
//! As a starting point, consider implementing some of the extensions under
//! [`breakpoints`]. For example, adding support for Software Breakpoints would
//! require implementing the
//! [`breakpoints::SwBreakpoint`](breakpoints::SwBreakpoint) extension, and
//! overriding the `Target::sw_breakpoint` method to return `Some(self)`.
//!
//! ### Note: Missing Protocol Extensions
//!
//! `gdbstub`'s development is guided by the needs of it's contributors, with
//! new features being added on an "as-needed" basis.
//!
//! If there's a GDB feature you need that hasn't been implemented yet, (e.g:
//! remote filesystem access, tracepoint support, etc...), consider opening an
//! issue / filing a PR on Github!
//!
//! Check out the [GDB Remote Configuration Docs](https://sourceware.org/gdb/onlinedocs/gdb/Remote-Configuration.html)
//! for a table of GDB commands + their corresponding Remote Serial Protocol
//! packets.
//!
//! ### Note: What's with all the `<Self::Arch as Arch>::` syntax?
//!
//! Many of the method signatures across the `Target` extension traits include
//! some pretty gnarly type syntax.
//!
//! If [rust-lang/rust#38078](https://github.com/rust-lang/rust/issues/38078)
//! gets fixed, then types like `<Self::Arch as Arch>::Foo` could be simplified
//! to just `Self::Arch::Foo`. Until then, the much more explicit
//! [fully qualified syntax](https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#fully-qualified-syntax-for-disambiguation-calling-methods-with-the-same-name)
//! must be used instead.
//!
//! When you come across this syntax, it's highly recommended to use the
//! concrete type instead. e.g: on a 32-bit target, instead of cluttering up
//! the implementation with `<Self::Arch as Arch>::Usize`, just use `u32`
//! directly.
//!
//! ## How Protocol Extensions Work - Inlineable Dyn Extension Traits (IDETs)
//!
//! The GDB protocol is massive, and contains all sorts of optional
//! functionality. In previous versions of `gdbstub`, the `Target` trait would
//! directly have a method for _every single protocol extension_, resulting in
//! literally _hundreds_ of associated methods!
//!
//! This approach had numerous drawbacks:
//!
//! - Implementations that did not implement all available protocol extensions
//! still had to "pay" for the unused packet parsing/handler code, resulting
//! in substantial code bloat, even on `no_std` platforms.
//! - Required the `GdbStub` implementation to include runtime checks to deal
//! with incorrectly implemented `Target`s.
//! - No way to enforce "mutually-dependent" trait methods at compile-time.
//! - e.g: When implementing hardware breakpoint extensions, targets
//! _must_ implement both the `add_breakpoint` and
//! `remove_breakpoints` methods.
//! - No way to enforce "mutually-exclusive" trait methods at compile-time.
//! - e.g: The `resume` method for single-threaded targets has a much
//! simpler API than for multi-threaded targets, but it would be
//! incorrect for a target to implement both.
//!
//! Starting from version `0.4.0`, `gdbstub` is taking a new approach to
//! implementing and enumerating available Target features, using a technique
//! called **Inlineable Dyn Extension Traits**.
//!
//! _Author's note:_ As far as I can tell, this isn't a very well-known trick,
//! or at the very least, I've personally never encountered any library that
//! uses this sort of API. As such, I've decided to be a bit cheeky and give it
//! a name! At some point, I'm hoping to write a standalone blog post which
//! further explores this technique, comparing it to other/existing approaches,
//! and diving into details of the how the compiler optimizes this sort of code.
//!
//! So, what are "Inlineable Dyn Extension Traits"? Well, let's break it down:
//!
//! - **Extension Traits** - A common [Rust convention](https://rust-lang.github.io/rfcs/0445-extension-trait-conventions.html#what-is-an-extension-trait)
//! to extend the functionality of a Trait, _without_ modifying the original
//! trait.
//! - **Dyn** - Alludes to the use of Dynamic Dispatch via [Trait Objects](https://doc.rust-lang.org/book/ch17-02-trait-objects.html).
//! - **Inlineable** - Alludes to the fact that this approach can be easily
//! inlined, making it a truly zero-cost abstraction.
//!
//! In a nutshell, Inlineable Dyn Extension Traits (or IDETs) are an abuse of
//! the Rust trait system + modern compiler optimizations to emulate zero-cost,
//! runtime-query-able optional trait methods!
//!
//! #### Technical overview
//!
//! The basic principles behind Inlineable Dyn Extension Traits are best
//! explained though example:
//!
//! Lets say we want to add an optional protocol extension described by an
//! `OptExt` trait to the `Target` trait. How would we do that using IDETs?
//!
//! - (library) Define a `trait OptExt: Target { ... }` with all the optional
//! methods:
//! - Making `OptExt` a supertrait of `Target` enables using `Target`'s
//! associated types.
//!
//! ```rust,ignore
//! /// `foo` and `bar` are mutually-dependent methods.
//! trait OptExt: Target {
//! fn foo(&self);
//! // can use associated types in method signature!
//! fn bar(&mut self) -> Result<(), Self::Error>;
//! }
//! ```
//!
//! - (library) "Tie" the `OptExt` extension trait to the original `Target`
//! trait by adding a new `Target` method that simply returns `self` cast to a
//! `&mut dyn OptExt`:
//!
//! ```rust,ignore
//! trait Target {
//! // Optional extension
//! fn ext_optfeat(&mut self) -> Option<OptExtOps<Self>> {
//! // disabled by default
//! None
//! }
//! // Mutually-exclusive extensions
//! fn ext_a_or_b(&mut self) -> EitherOrExt<Self::Arch, Self::Error>;
//! }
//!
//! // Using a typedef for readability
//! type OptExtOps<T> =
//! &'a mut dyn OptExt<Arch = <T as Target>::Arch, Error = <T as Target>::Error>;
//!
//! enum EitherOrExt<A, E> {
//! OptExtA(&'a mut dyn OptExtA<Arch = A, Error = E>),
//! OptExtB(&'a mut dyn OptExtB<Arch = A, Error = E>),
//! }
//! ```
//!
//! - (user) Implements the `OptExt` extension for their target (just like a
//! normal trait).
//!
//! ```rust,ignore
//! impl OptExt for Target {
//! fn foo(&self) { ... }
//! fn bar(&mut self) -> Result<(), Self::Error> { ... }
//! }
//! ```
//!
//! - (user) Implements the base `Target` trait, returning `Some(self)` to
//! "enable" an extension, or `None` to leave it disabled.
//!
//! ```rust,ignore
//! impl Target for MyTarget {
//! // Optional extension - Always enabled
//! fn ext_optfeat(&mut self) -> Option<OptExtOps<Self>> {
//! Some(self) // will not compile unless `MyTarget` also implements `OptExt`
//! }
//! // Mutually-exclusive extensions
//! fn ext_a_or_b(&mut self) -> EitherOrExt<Self::Arch, Self::Error> {
//! EitherOrExt::OptExtA(self)
//! }
//! }
//! ```
//!
//! If the user didn't implement `OptExt`, but tried to return `Some(self)`,
//! they'll get an error similar to:
//!
//! ```text
//! error[E0277]: the trait bound `MyTarget: OptExt` is not satisfied
//! --> path/to/implementation.rs:44:14
//! |
//! 44 | Some(self)
//! | ^^^^ the trait `OptExt` is not implemented for `MyTarget`
//! |
//! = note: required for the cast to the object type `dyn OptExt<Arch = ..., Error = ...>`
//! ```
//!
//! - (library) Can now _query_ whether or not the extension is available,
//! _without_ having to actually invoke any method on the target!
//! ```rust,ignore
//! // in a method that accepts `target: impl Target`
//! match target.ext_optfeat() {
//! Some(ops) => ops.cool_feature(),
//! None => { /* do nothing */ }
//! }
//! ```
//!
//! Moreover, if you take a look at the generated assembly (e.g: using
//! godbolt.org), you'll find that the compiler is able to efficiently inline
//! and devirtualize all the single-line `ext_` methods, which in-turn allows
//! the dead-code-eliminator to work it's magic, and remove the unused branches
//! from the generated code! i.e: If a target didn't implement the `OptExt`
//! extension, then that `match` statement would be converted into a noop!
//!
//! Check out [daniel5151/optional-trait-methods](https://github.com/daniel5151/optional-trait-methods)
//! for some sample code that shows off the power of IDETs. It includes code
//! snippets which can be pasted into godbolt.org directly to confirm the
//! optimizations described above.
//!
//! Optimizing compilers really are magic!
//!
//! #### Summary: The Benefits of IDETs
//!
//! IDETs solve the numerous issues and shortcomings that arise from the
//! traditional single trait + "optional" methods approach:
//!
//! - **Compile-time enforcement of mutually-dependent methods**
//! - By grouping mutually-dependent methods behind a single extension trait
//! and marking them all as required methods, the Rust compiler is able to
//! catch missing mutually-dependent methods at compile time, with no need
//! for any runtime checks!
//! - **Compile-time enforcement of mutually-exclusive methods**
//! - By grouping mutually-exclusive methods behind two extension traits, and
//! wrapping those in an `enum`, the API is able to document
//! mutually-exclusive functions _at the type-level_, in-turn enabling the
//! library to omit any runtime checks!
//! - _Note:_ Strictly speaking, this isn't really compile time
//! "enforcement", as there's nothing stopping an "adversarial"
//! implementation from implementing both sets of methods, and then
//! "flipping" between the two at runtime. Nonetheless, it serves as a good
//! guardrail.
//! - **Enforce dead-code-elimination _without_ `cargo` feature flags**
//! - This is a really awesome trick: by wrapping code in a `if
//! target.ext_optfeat().is_some()` block, it's possible to specify
//! _arbitrary_ blocks of code to be feature-dependent!
//! - This is used to great effect in `gdbstub` to optimize-out any packet
//! parsing / handler code for unimplemented protocol extensions.
macro_rules! doc_comment {
($x:expr, $($tt:tt)*) => {
#[doc = $x]
$($tt)*
};
}
macro_rules! define_ext {
($extname:ident, $exttrait:ident) => {
doc_comment! {
concat!("See [`", stringify!($exttrait), "`](trait.", stringify!($exttrait), ".html)."),
pub type $extname<'a, T> =
&'a mut dyn $exttrait<Arch = <T as Target>::Arch, Error = <T as Target>::Error>;
}
};
}
pub mod base;
pub mod breakpoints;
pub mod extended_mode;
pub mod monitor_cmd;
pub mod section_offsets;