blob: f13adb0c27972110c2b213db53caca81cd24410c [file] [log] [blame] [edit]
//! 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
}
/// (optional) (LLDB extension) Return register info for the specified
/// register.
///
/// Implementing this method enables LLDB to dynamically query the target's
/// register information one by one.
///
/// Some targets don't have register context in the compiled version of the
/// debugger. Help the debugger by dynamically supplying the register info
/// from the target. The debugger will request the register info in a
/// sequential manner till an error packet is received. In LLDB, the
/// register info search has the following
/// [order](https://github.com/llvm/llvm-project/blob/369ce54bb302f209239b8ebc77ad824add9df089/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp#L397-L402):
///
/// 1. Use the target definition python file if one is specified.
/// 2. If the target definition doesn't have any of the info from the
/// target.xml (registers) then proceed to read the `target.xml`.
/// 3. Fall back on the `qRegisterInfo` packets.
/// 4. Use hardcoded defaults if available.
///
/// See the LLDB [gdb-remote docs](https://github.com/llvm-mirror/lldb/blob/d01083a850f577b85501a0902b52fd0930de72c7/docs/lldb-gdb-remote.txt#L396)
/// for more details on the available information that a single register can
/// be described by and [#99](https://github.com/daniel5151/gdbstub/issues/99)
/// for more information on LLDB compatibility.
#[inline(always)]
fn lldb_register_info(reg_id: usize) -> Option<lldb::RegisterInfo<'static>> {
let _ = reg_id;
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,
}
/// LLDB-specific types supporting [`Arch::lldb_register_info`] and
/// [`LldbRegisterInfoOverride`] APIs.
///
/// [`LldbRegisterInfoOverride`]: crate::target::ext::lldb_register_info_override::LldbRegisterInfoOverride
pub mod lldb {
/// The architecture's register information of a single register.
pub enum RegisterInfo<'a> {
/// The register info of a single register that should be written.
Register(Register<'a>),
/// The `qRegisterInfo` query shall be concluded.
Done,
}
/// Describes the register info for a single register of
/// the target.
pub struct Register<'a> {
/// The primary register name.
pub name: &'a str,
/// An alternate name for the register.
pub alt_name: Option<&'a str>,
/// Size in bits of a register.
pub bitsize: usize,
/// The offset within the 'g' and 'G' packet of the register data for
/// this register.
pub offset: usize,
/// The encoding type of the register.
pub encoding: Encoding,
/// The preferred format for display of this register.
pub format: Format,
/// The register set name this register belongs to.
pub set: &'a str,
/// The GCC compiler registers number for this register.
///
/// _Note:_ This denotes the same `KEY:VALUE;` pair as `ehframe:VALUE;`.
/// See the LLDB [source](https://github.com/llvm/llvm-project/blob/b92436efcb7813fc481b30f2593a4907568d917a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp#L493).
pub gcc: Option<usize>,
/// The DWARF register number for this register that is used for this
/// register in the debug information.
pub dwarf: Option<usize>,
/// Specify as a generic register.
pub generic: Option<Generic>,
/// Other concrete register values this register is contained in.
pub container_regs: Option<&'a [usize]>,
/// Specifies which register values should be invalidated when this
/// register is modified.
pub invalidate_regs: Option<&'a [usize]>,
}
/// Describes the encoding type of the register.
#[non_exhaustive]
pub enum Encoding {
/// Unsigned integer
Uint,
/// Signed integer
Sint,
/// IEEE 754 float
IEEE754,
/// Vector register
Vector,
}
/// Describes the preferred format for display of this register.
#[non_exhaustive]
pub enum Format {
/// Binary format
Binary,
/// Decimal format
Decimal,
/// Hexadecimal format
Hex,
/// Floating point format
Float,
/// 8 bit signed int vector
VectorSInt8,
/// 8 bit unsigned int vector
VectorUInt8,
/// 16 bit signed int vector
VectorSInt16,
/// 16 bit unsigned int vector
VectorUInt16,
/// 32 bit signed int vector
VectorSInt32,
/// 32 bit unsigned int vector
VectorUInt32,
/// 32 bit floating point vector
VectorFloat32,
/// 128 bit unsigned int vector
VectorUInt128,
}
/// Describes the generic types that most CPUs have.
#[non_exhaustive]
pub enum Generic {
/// Program counter register
Pc,
/// Stack pointer register
Sp,
/// Frame pointer register
Fp,
/// Return address register
Ra,
/// CPU flags register
Flags,
/// Function argument 1
Arg1,
/// Function argument 2
Arg2,
/// Function argument 3
Arg3,
/// Function argument 4
Arg4,
/// Function argument 5
Arg5,
/// Function argument 6
Arg6,
/// Function argument 7
Arg7,
/// Function argument 8
Arg8,
}
}