blob: 06cb49e0d6586be513a7bfe3123e98077df43c90 [file] [log] [blame]
//! Traits to encode architecture-specific target information.
//!
//! # Community created `Arch` Implementations
//!
//! Before getting your hands dirty and implementing a new `Arch` from scratch,
//! make sure to check out [`gdbstub_arch`](https://docs.rs/gdbstub_arch), a
//! companion crate to `gdbstub` which aggregates community-created `Arch`
//! implementations for most common architectures!
//!
//! > _Note:_ Prior to `gdbstub 0.5`, `Arch` implementations were distributed as
//! a part of the main `gdbstub` crate (under the `gdbstub::arch` module). This
//! wasn't ideal, any `gdbstub::arch`-level breaking-changes forced the _entire_
//! `gdbstub` crate to release a new (potentially breaking!) version.
//!
//! > Having community-created `Arch` implementations distributed in a separate
//! crate helps minimize any unnecessary "version churn" in `gdbstub` core.
use core::fmt::Debug;
use core::num::NonZeroUsize;
use num_traits::{FromPrimitive, PrimInt, Unsigned};
use crate::internal::{BeBytes, LeBytes};
/// Register identifier for target registers.
///
/// These identifiers are used by GDB to signal which register to read/wite when
/// performing [single register accesses].
///
/// [single register accesses]:
/// crate::target::ext::base::single_register_access::SingleRegisterAccess
pub trait RegId: Sized + Debug {
/// Map raw GDB register number to a corresponding `RegId` and optional
/// register size.
///
/// If the register size is specified here, gdbstub will include a runtime
/// check that ensures target implementations do not send back more
/// bytes than the register allows.
///
/// Returns `None` if the register is not available.
fn from_raw_id(id: usize) -> Option<(Self, Option<NonZeroUsize>)>;
}
/// Stub implementation -- Returns `None` for all raw IDs.
impl RegId for () {
fn from_raw_id(_id: usize) -> Option<(Self, Option<NonZeroUsize>)> {
None
}
}
/// Methods to read/write architecture-specific registers.
///
/// Registers must be de/serialized in the order specified by the architecture's
/// `<target>.xml` in the GDB source tree.
///
/// e.g: for ARM:
/// github.com/bminor/binutils-gdb/blob/master/gdb/features/arm/arm-core.xml
// TODO: add way to de/serialize arbitrary "missing"/"uncollected" registers.
pub trait Registers: Default + Debug + Clone + PartialEq {
/// The type of the architecture's program counter / instruction pointer.
/// Must match with the corresponding `Arch::Usize`.
type ProgramCounter: Copy;
/// Return the value of the program counter / instruction pointer.
fn pc(&self) -> Self::ProgramCounter;
/// Serialize `self` into a GDB register bytestream.
///
/// Missing registers are serialized by passing `None` to write_byte.
fn gdb_serialize(&self, write_byte: impl FnMut(Option<u8>));
/// Deserialize a GDB register bytestream into `self`.
#[allow(clippy::result_unit_err)]
fn gdb_deserialize(&mut self, bytes: &[u8]) -> Result<(), ()>;
}
/// Breakpoint kind for specific architectures.
///
/// This trait corresponds to the _kind_ field of the "z" and "Z" breakpoint
/// packets, as documented [here](https://sourceware.org/gdb/onlinedocs/gdb/Packets.html#insert-breakpoint-or-watchpoint-packet).
///
/// A breakpoint "kind" is architecture-specific and typically indicates the
/// size of the breakpoint in bytes that should be inserted. As such, most
/// architectures will set `BreakpointKind = usize`.
///
/// Some architectures, such as ARM and MIPS, have additional meanings for
/// _kind_. See the [Architecture-Specific Protocol Details](https://sourceware.org/gdb/current/onlinedocs/gdb/Architecture_002dSpecific-Protocol-Details.html#Architecture_002dSpecific-Protocol-Details)
/// section of the GBD documentation for more details.
///
/// If no architecture-specific value is being used, _kind_ should be set to
/// '0', and the `BreakpointKind` associated type should be `()`.
pub trait BreakpointKind: Sized + Debug {
/// Parse `Self` from a raw usize.
fn from_usize(kind: usize) -> Option<Self>;
}
impl BreakpointKind for () {
fn from_usize(kind: usize) -> Option<Self> {
if kind != 0 {
None
} else {
Some(())
}
}
}
impl BreakpointKind for usize {
#[allow(clippy::wrong_self_convention)]
fn from_usize(kind: usize) -> Option<Self> {
Some(kind)
}
}
/// Encodes architecture-specific information, such as pointer size, register
/// layout, etc...
///
/// Types implementing `Arch` should be
/// [Zero-variant Enums](https://doc.rust-lang.org/reference/items/enumerations.html#zero-variant-enums),
/// as `Arch` impls are only ever used at the type level, and should never be
/// explicitly instantiated.
pub trait Arch {
/// The architecture's pointer size (e.g: `u32` on a 32-bit system).
type Usize: Debug + FromPrimitive + PrimInt + Unsigned + BeBytes + LeBytes;
/// The architecture's register file. See [`Registers`] for more details.
type Registers: Registers<ProgramCounter = Self::Usize>;
/// The architecture's breakpoint "kind", used to determine the "size"
/// of breakpoint to set. See [`BreakpointKind`] for more details.
type BreakpointKind: BreakpointKind;
/// Register identifier enum/struct.
///
/// Used to access individual registers via `Target::read/write_register`.
///
/// > NOTE: An arch's `RegId` type is not strictly required to have a 1:1
/// correspondence with the `Registers` type, and may include register
/// identifiers which are separate from the main `Registers` structure.
/// (e.g: the RISC-V Control and Status registers)
type RegId: RegId;
/// (optional) Return the arch's description XML file (`target.xml`).
///
/// Implementing this method enables GDB to automatically detect the
/// target's architecture, saving the hassle of having to run `set
/// architecture <arch>` when starting a debugging session.
///
/// These descriptions can be quite succinct. For example, the target
/// description for an `armv4t` target can be as simple as:
///
/// ```
/// r#"<target version="1.0"><architecture>armv4t</architecture></target>"#;
/// ```
///
/// See the [GDB docs](https://sourceware.org/gdb/current/onlinedocs/gdb/Target-Description-Format.html)
/// for details on the target description XML format.
#[inline(always)]
fn target_description_xml() -> Option<&'static str> {
None
}
/// Encode how the mainline GDB client handles target support for
/// single-step on this particular architecture.
///
/// # Context
///
/// According to the spec, supporting single step _should_ be quite
/// straightforward:
///
/// - The GDB client sends a `vCont?` packet to enumerate supported
/// resumption modes
/// - If the target supports single-step, it responds with the `s;S`
/// capability as part of the response, omitting it if it is not
/// supported.
/// - Later, when the user attempts to `stepi`, the GDB client sends a `s`
/// resumption reason if it is supported, falling back to setting a
/// temporary breakpoint + continue to "emulate" the single step.
///
/// Unfortunately, the reality is that the mainline GDB client does _not_ do
/// this on all architectures...
///
/// - On certain architectures (e.g: x86), GDB will _unconditionally_ assume
/// single-step support, regardless whether or not the target reports
/// supports it.
/// - On certain architectures (e.g: MIPS), GDB will _never_ use single-step
/// support, even in the target has explicitly reported support for it.
///
/// This is a bug, and has been reported at
/// <https://sourceware.org/bugzilla/show_bug.cgi?id=28440>.
///
/// For a easy repro of this behavior, also see
/// <https://github.com/daniel5151/gdb-optional-step-bug>.
///
/// # Implications
///
/// Unfortunately, even if these idiosyncratic behaviors get fixed in the
/// mainline GDB client, it will be quite a while until the typical
/// user's distro-provided GDB client includes this bugfix.
///
/// As such, `gdbstub` has opted to include this method as a "guard rail" to
/// preemptively detect cases of this idiosyncratic behavior, and throw a
/// pre-init error that informs the user of the potential issues they may
/// run into.
///
/// # Writing a proper implementation
///
/// To check whether or not a particular architecture exhibits this
/// behavior, an implementation should temporarily override this method to
/// return [`SingleStepGdbBehavior::Optional`], toggle target support for
/// single-step on/off, and observe the behavior of the GDB client after
/// invoking `stepi`.
///
/// If single-stepping was **disabled**, yet the client nonetheless sent a
/// `vCont` packet with a `s` resume action, then this architecture
/// _does not_ support optional single stepping, and this method should
/// return [`SingleStepGdbBehavior::Required`].
///
/// If single-stepping was **disabled**, and the client attempted to set a
/// temporary breakpoint (using the `z` packet), and then sent a `vCont`
/// packet with a `c` resume action, then this architecture _does_
/// support optional single stepping, and this method should return
/// [`SingleStepGdbBehavior::Optional`].
///
/// If single-stepping was **enabled**, yet the client did _not_ send a
/// `vCont` packet with a `s` resume action, then this architecture
/// _ignores_ single stepping entirely, and this method should return
/// [`SingleStepGdbBehavior::Ignored`].
fn single_step_gdb_behavior() -> SingleStepGdbBehavior;
}
/// Encodes how the mainline GDB client handles target support for single-step
/// on a particular architecture.
///
/// See [Arch::single_step_gdb_behavior] for details.
#[non_exhaustive]
#[derive(Debug, Clone, Copy)]
pub enum SingleStepGdbBehavior {
/// GDB will use single-stepping if available, falling back to using
/// a temporary breakpoint + continue if unsupported.
///
/// e.g: ARM
Optional,
/// GDB will unconditionally send single-step packets, _requiring_ the
/// target to handle these requests.
///
/// e.g: x86/x64
Required,
/// GDB will never use single-stepping, regardless if it's supported by the
/// stub. It will always use a temporary breakpoint + continue.
///
/// e.g: MIPS
Ignored,
/// Unknown behavior - no one has tested this platform yet. If possible,
/// please conduct a test + upstream your findings to `gdbstub_arch`.
#[doc(hidden)]
Unknown,
}