1 //! Extensions to [`Target`](super::Target) which add support for various
2 //! subsets of the GDB Remote Serial Protocol.
3 //!
4 //! ### Note: Missing Protocol Extensions
5 //!
6 //! `gdbstub`'s development is guided by the needs of its contributors, with new
7 //! features being added on an "as-needed" basis.
8 //!
9 //! If there's a GDB protocol extensions you're interested in that hasn't been
10 //! implemented in `gdbstub` yet, (e.g: remote filesystem access, tracepoint
11 //! support, etc...), consider opening an issue / filing a PR on the
12 //! [`gdbstub` GitHub repo](https://github.com/daniel5151/gdbstub/).
13 //!
14 //! Check out the [GDB Remote Configuration Docs](https://sourceware.org/gdb/onlinedocs/gdb/Remote-Configuration.html)
15 //! for a table of GDB commands + their corresponding Remote Serial Protocol
16 //! packets.
17 //!
18 //! ## How Protocol Extensions Work - Inlineable Dyn Extension Traits (IDETs)
19 //!
20 //! The GDB protocol is massive, and contains all sorts of optional
21 //! functionality. In the early versions of `gdbstub`, the `Target` trait
22 //! directly implemented a method for _every single protocol extension_. If this
23 //! trend continued, there would've been literally _hundreds_ of associated
24 //! methods - of which only a small subset were ever used at once!
25 //!
26 //! Aside from the cognitive complexity of having so many methods on a single
27 //! trait, this approach had numerous other drawbacks as well:
28 //!
29 //!  - Implementations that did not implement all available protocol extensions
30 //!    still had to "pay" for the unused packet parsing/handler code, resulting
31 //!    in substantial code bloat, even on `no_std` platforms.
32 //!  - `GdbStub`'s internal implementation needed to include a large number of
33 //!    _runtime_ checks to deal with incorrectly implemented `Target`s.
34 //!      - No way to enforce "mutually-dependent" trait methods at compile-time.
35 //!          - e.g: When implementing hardware breakpoint extensions, targets
36 //!            _must_ implement both the `add_breakpoint` and
37 //!            `remove_breakpoints` methods.
38 //!      - No way to enforce "mutually-exclusive" trait methods at compile-time.
39 //!          - e.g: The `resume` method for single-threaded targets has a much
40 //!            simpler API than for multi-threaded targets, but it would be
41 //!            incorrect for a target to implement both.
42 //!
43 //! At first blush, it seems the the solution to all these issues is obvious:
44 //! simply tie each protocol extension to a `cargo` feature! And yes, while this
45 //! would indeed work, there would be several serious ergonomic drawbacks:
46 //!
47 //! - There would be _hundreds_ of individual feature flags that would need to
48 //!   be toggled by end users.
49 //! - It would be functionally impossible to _test_ all permutations of
50 //!   enabled/disabled cargo features.
51 //! - A single binary would need to rely on some [non-trivial `cargo`-fu](https://github.com/rust-lang/cargo/issues/674)
52 //!   in order to have multiple `Target` implementations in a single binary.
53 //!
54 //! After much experimentation and iteration, `gdbstub` ended up taking a
55 //! radically different approach to implementing and enumerating available
56 //! features, using a technique called **Inlineable Dyn Extension Traits**.
57 //!
58 //! > _Author's note:_ As far as I can tell, this isn't a very well-known trick,
59 //! > or at the very least, I've personally never encountered any library that
60 //! > uses this sort of API. As such, I've decided to be a bit cheeky and give
61 //! > it a name! At some point, I'm hoping to write a standalone blog post which
62 //! > further explores this technique, comparing it to other/existing
63 //! > approaches, and diving into details of the how the compiler optimizes this
64 //! > sort of code. In fact, I've already got a [very rough github repo](https://github.com/daniel5151/optional-trait-methods)
65 //! > with some of my findings.
66 //!
67 //! So, what are "Inlineable Dyn Extension Traits"? Well, let's break it down:
68 //!
69 //! - **Extension Traits** - A common [Rust convention](https://rust-lang.github.io/rfcs/0445-extension-trait-conventions.html#what-is-an-extension-trait)
70 //!   to extend the functionality of a Trait, _without_ modifying the original
71 //!   trait.
72 //! - **Dyn** - Alludes to the use of Dynamic Dispatch via [Trait Objects](https://doc.rust-lang.org/book/ch17-02-trait-objects.html).
73 //! - **Inlineable** - Alludes to the fact that this approach can be easily
74 //!   inlined, making it a truly zero-cost abstraction.
75 //!
76 //! In a nutshell, Inlineable Dyn Extension Traits (or IDETs) are an abuse of
77 //! the Rust trait system + modern compiler optimizations to emulate zero-cost,
78 //! runtime-enumerable optional trait methods!
79 //!
80 //! #### Technical overview
81 //!
82 //! The basic principles behind Inlineable Dyn Extension Traits are best
83 //! explained though example:
84 //!
85 //! Lets say we want to add an optional protocol extension described by an
86 //! `ProtocolExt` trait to a base `Protocol` trait. How would we do that using
87 //! IDETs?
88 //!
89 //! - (library) Define a `trait ProtocolExt: Protocol { ... }` which includes
90 //!   all the methods required by the protocol extension:
91 //!    - _Note:_ Making `ProtocolExt` a subtrait of `Protocol` is not strictly
92 //!      required, but it does enable transparently using `Protocol`'s
93 //!      associated types as part of `ProtocolExt`'s method definitions.
94 //!
95 //! ```rust,ignore
96 //! /// `foo` and `bar` are mutually-dependent methods.
97 //! trait ProtocolExt: Protocol {
98 //!     fn foo(&self);
99 //!     // can use associated types in method signature!
100 //!     fn bar(&mut self) -> Result<(), Self::Error>;
101 //! }
102 //! ```
103 //!
104 //! - (library) "Associate" the `ProtocolExt` extension trait to the original
105 //!   `Protocol` trait by adding a new `Protocol` method that "downcasts" `self`
106 //!   into a `&mut dyn ProtocolExt`.
107 //!
108 //! ```rust,ignore
109 //! trait Protocol {
110 //!     // ... other methods ...
111 //!
112 //!     // Optional extension
113 //!     #[inline(always)]
114 //!     fn support_protocol_ext(&mut self) -> Option<ProtocolExtOps<Self>> {
115 //!         // disabled by default
116 //!         None
117 //!     }
118 //!
119 //!     // Mutually-exclusive extensions
120 //!     fn get_ext_a_or_b(&mut self) -> EitherOrExt<Self::Arch, Self::Error>;
121 //! }
122 //!
123 //! // Using a typedef for readability
124 //! type ProtocolExtOps<T> =
125 //!     &'a mut dyn ProtocolExt<Arch = <T as Protocol>::Arch, Error = <T as Protocol>::Error>;
126 //!
127 //! enum EitherOrExt<A, E> {
128 //!     ProtocolExtA(&'a mut dyn ProtocolExtA<Arch = A, Error = E>),
129 //!     ProtocolExtB(&'a mut dyn ProtocolExtB<Arch = A, Error = E>),
130 //! }
131 //! ```
132 //!
133 //! - (user) Implements the `ProtocolExt` extension for their target (just like
134 //!   a normal trait).
135 //!
136 //! ```rust,ignore
137 //! impl ProtocolExt for MyTarget {
138 //!     fn foo(&self) { ... }
139 //!     fn bar(&mut self) -> Result<(), Self::Error> { ... }
140 //! }
141 //! ```
142 //!
143 //! - (user) Implements the base `Protocol` trait, overriding the
144 //!   `support_protocol_ext` method to return `Some(self)`, which will
145 //!   effectively "enable" the extension.
146 //!
147 //! ```rust,ignore
148 //! impl Protocol for MyTarget {
149 //!     // Optional extension
150 //!     #[inline(always)]
151 //!     fn support_protocol_ext(&mut self) -> Option<ProtocolExtOps<Self>> {
152 //!         Some(self) // will not compile unless `MyTarget` also implements `ProtocolExt`
153 //!     }
154 //!
155 //!     // Mutually-exclusive extensions
156 //!     #[inline(always)]
157 //!     fn get_ext_a_or_b(&mut self) -> EitherOrExt<Self::Arch, Self::Error> {
158 //!         EitherOrExt::ProtocolExtA(self)
159 //!     }
160 //! }
161 //! ```
162 //!
163 //! > Please note the use of `#[inline(always)]` when enabling IDET methods.
164 //! > While LLVM is usually smart enough to inline single-level IDETs (such as
165 //! > in the example above), nested IDETs will often require a bit of "help"
166 //! > from the `inline` directive to be correctly optimized.
167 //!
168 //! Now, here's where IDETs really shine: If the user didn't implement
169 //! `ProtocolExt`, but _did_ try to enable the feature by overriding
170 //! `support_protocol_ext` to return `Some(self)`, they'll get a compile-time
171 //! error that looks something like this:
172 //!
173 //! ```text
174 //! error[E0277]: the trait bound `MyTarget: ProtocolExt` is not satisfied
175 //!   --> path/to/implementation.rs:44:14
176 //!    |
177 //! 44 |         Some(self)
178 //!    |              ^^^^ the trait `ProtocolExt` is not implemented for `MyTarget`
179 //!    |
180 //!    = note: required for the cast to the object type `dyn ProtocolExt<Arch = ..., Error = ...>`
181 //! ```
182 //!
183 //! The Rust compiler is preventing you from enabling a feature you haven't
184 //! implemented _at compile time!_
185 //!
186 //! - (library) Is able to _query_ whether or not an extension is available,
187 //!   _without_ having to actually invoke any method on the target!
188 //!
189 //! ```rust,ignore
190 //! fn execute_protocol(mut target: impl Target) {
191 //!     match target.support_protocol_ext() {
192 //!         Some(ops) => ops.foo(),
193 //!         None => { /* fallback when not enabled */ }
194 //!     }
195 //! }
196 //! ```
197 //!
198 //! This is already pretty cool, but what's _even cooler_ is that if you take a
199 //! look at the generated assembly of a monomorphized `execute_protocol` method
200 //! (e.g: using godbolt.org), you'll find that the compiler is able to
201 //! efficiently inline and devirtualize _all_ the calls to
202 //! `support_protocol_ext` method, which in-turn allows the dead-code-eliminator
203 //! to work its magic, and remove the unused branches from the generated code!
204 //! i.e: If a target implemention didn't implement the `ProtocolExt` extension,
205 //! then that `match` statement in `execute_protocol` would simply turn into a
206 //! noop!
207 //!
208 //! If IDETs are something you're interested in, consider checking out
209 //! [daniel5151/optional-trait-methods](https://github.com/daniel5151/optional-trait-methods)
210 //! for some sample code that shows off the power of IDETs. It's not
211 //! particularly polished, but it does includes code snippets which can be
212 //! pasted into godbolt.org directly to confirm the optimizations described
213 //! above, and a brief writeup which compares / contrasts alternatives to IDETs.
214 //!
215 //! Long story short: Optimizing compilers really are magic!
216 //!
217 //! #### Summary: The Benefits of IDETs
218 //!
219 //! IDETs solve the numerous issues and shortcomings that arise from the
220 //! traditional single trait + "optional" methods approach:
221 //!
222 //! - **Compile-time enforcement of mutually-dependent methods**
223 //!    - By grouping mutually-dependent methods behind a single extension trait
224 //!      and marking them all as required methods, the Rust compiler is able to
225 //!      catch missing mutually-dependent methods at compile time, with no need
226 //!      for any runtime checks!
227 //! - **Compile-time enforcement of mutually-exclusive methods**
228 //!    - By grouping mutually-exclusive methods behind two extension traits, and
229 //!      wrapping those in an `enum`, the API is able to document
230 //!      mutually-exclusive functions _at the type-level_, in-turn enabling the
231 //!      library to omit any runtime checks!
232 //!    - _Note:_ Strictly speaking, this isn't really compile time
233 //!      "enforcement", as there's nothing stopping an "adversarial"
234 //!      implementation from implementing both sets of methods, and then
235 //!      "flipping" between the two at runtime. Nonetheless, it serves as a good
236 //!      guardrail.
237 //! - **Enforce dead-code-elimination _without_ `cargo` feature flags**
238 //!     - This is a really awesome trick: by wrapping code in an `if
239 //!       target.support_protocol_ext().is_some()` block, it's possible to
240 //!       specify _arbitrary_ blocks of code to be feature-dependent!
241 //!     - This is used to great effect in `gdbstub` to optimize-out any packet
242 //!       parsing / handler code for unimplemented protocol extensions.
243 
244 macro_rules! doc_comment {
245     ($x:expr, $($tt:tt)*) => {
246         #[doc = $x]
247         $($tt)*
248     };
249 }
250 
251 macro_rules! define_ext {
252     ($extname:ident, $exttrait:ident) => {
253         doc_comment! {
254             concat!("See [`", stringify!($exttrait), "`](trait.", stringify!($exttrait), ".html)."),
255             pub type $extname<'a, T> =
256                 &'a mut dyn $exttrait<Arch = <T as Target>::Arch, Error = <T as Target>::Error>;
257         }
258     };
259 }
260 
261 pub mod auxv;
262 pub mod base;
263 pub mod breakpoints;
264 pub mod catch_syscalls;
265 pub mod exec_file;
266 pub mod extended_mode;
267 pub mod host_io;
268 pub mod libraries;
269 pub mod lldb_register_info_override;
270 pub mod memory_map;
271 pub mod monitor_cmd;
272 pub mod section_offsets;
273 pub mod target_description_xml_override;
274 pub mod thread_extra_info;
275