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