pub trait Arch {
    type Usize: Debug + FromPrimitive + PrimInt + Unsigned + BeBytes + LeBytes;
    type Registers: Registers<ProgramCounter = Self::Usize>;
    type BreakpointKind: BreakpointKind;
    type RegId: RegId;
    fn single_step_gdb_behavior() -> SingleStepGdbBehavior;

    fn target_description_xml() -> Option<&'static str> { ... }
}
Expand description

Encodes architecture-specific information, such as pointer size, register layout, etc…

Types implementing Arch should be Zero-variant Enums, as Arch impls are only ever used at the type level, and should never be explicitly instantiated.

Associated Types

The architecture’s pointer size (e.g: u32 on a 32-bit system).

The architecture’s register file. See Registers for more details.

The architecture’s breakpoint “kind”, used to determine the “size” of breakpoint to set. See BreakpointKind for more details.

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)

Required methods

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.

Unknown implementations

Because this method was only introduced in gdbstub version 0.6, there are many existing Arch implementations in the gdbstub_arch companion crate that have not yet been tested and updated what kind of behavior they exhibit.

These implementations currently return SingleStepGdbBehavior::Unknown, which will result in a pre-init error that notifies users of this issue, along with imploring them to be a Good Citizen and discover + upstream a proper implementation of this method for their Arch.

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.

Provided methods

(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 for details on the target description XML format.

Implementors