Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dynamic RegId Support #58

Closed
wants to merge 9 commits into from
5 changes: 3 additions & 2 deletions examples/armv4t/gdb/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use gdbstub::target::ext::base::singlethread::{
GdbInterrupt, ResumeAction, SingleThreadOps, SingleThreadReverseContOps,
SingleThreadReverseStepOps, StopReason,
};
use gdbstub::target::ext::base::SendRegisterOutput;
use gdbstub::target::ext::breakpoints::WatchKind;
use gdbstub::target::{Target, TargetError, TargetResult};
use gdbstub_arch::arm::reg::id::ArmCoreRegId;
Expand Down Expand Up @@ -212,11 +213,11 @@ impl target::ext::base::SingleRegisterAccess<()> for Emu {
&mut self,
_tid: (),
reg_id: gdbstub_arch::arm::reg::id::ArmCoreRegId,
dst: &mut [u8],
mut output: SendRegisterOutput,
) -> TargetResult<(), Self> {
if let Some(i) = cpu_reg_id(reg_id) {
let w = self.cpu.reg_get(self.cpu.mode(), i);
dst.copy_from_slice(&w.to_le_bytes());
output.write(&w.to_le_bytes());
Ok(())
} else {
Err(().into())
Expand Down
6 changes: 4 additions & 2 deletions gdbstub_arch/src/arm/reg/id.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use core::num::NonZeroUsize;

use gdbstub::arch::RegId;

/// 32-bit ARM core register identifier.
Expand All @@ -21,7 +23,7 @@ pub enum ArmCoreRegId {
}

impl RegId for ArmCoreRegId {
fn from_raw_id(id: usize) -> Option<(Self, usize)> {
fn from_raw_id(id: usize) -> Option<(Self, Option<NonZeroUsize>)> {
let reg = match id {
0..=12 => Self::Gpr(id as u8),
13 => Self::Sp,
Expand All @@ -31,6 +33,6 @@ impl RegId for ArmCoreRegId {
25 => Self::Cpsr,
_ => return None,
};
Some((reg, 4))
Some((reg, Some(NonZeroUsize::new(4)?)))
}
}
14 changes: 8 additions & 6 deletions gdbstub_arch/src/mips/reg/id.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use core::num::NonZeroUsize;

use gdbstub::arch::RegId;

/// MIPS register identifier.
Expand Down Expand Up @@ -44,7 +46,7 @@ pub enum MipsRegId<U> {
_Size(U),
}

fn from_raw_id<U>(id: usize) -> Option<(MipsRegId<U>, usize)> {
fn from_raw_id<U>(id: usize) -> Option<(MipsRegId<U>, Option<NonZeroUsize>)> {
let reg = match id {
0..=31 => MipsRegId::Gpr(id as u8),
32 => MipsRegId::Status,
Expand All @@ -63,23 +65,23 @@ fn from_raw_id<U>(id: usize) -> Option<(MipsRegId<U>, usize)> {
76 => MipsRegId::Hi3,
77 => MipsRegId::Lo3,
// `MipsRegId::Dspctl` is the only register that will always be 4 bytes wide
78 => return Some((MipsRegId::Dspctl, 4)),
78 => return Some((MipsRegId::Dspctl, Some(NonZeroUsize::new(4)?))),
79 => MipsRegId::Restart,
_ => return None,
};

let ptrsize = core::mem::size_of::<U>();
Some((reg, ptrsize))
Some((reg, Some(NonZeroUsize::new(ptrsize)?)))
}

impl RegId for MipsRegId<u32> {
fn from_raw_id(id: usize) -> Option<(Self, usize)> {
fn from_raw_id(id: usize) -> Option<(Self, Option<NonZeroUsize>)> {
from_raw_id::<u32>(id)
}
}

impl RegId for MipsRegId<u64> {
fn from_raw_id(id: usize) -> Option<(Self, usize)> {
fn from_raw_id(id: usize) -> Option<(Self, Option<NonZeroUsize>)> {
from_raw_id::<u64>(id)
}
}
Expand All @@ -104,7 +106,7 @@ mod tests {
let mut i = 0;
let mut sum_reg_sizes = 0;
while let Some((_, size)) = RId::from_raw_id(i) {
sum_reg_sizes += size;
sum_reg_sizes += size.unwrap().get();
i += 1;
}

Expand Down
8 changes: 5 additions & 3 deletions gdbstub_arch/src/msp430/reg/id.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use core::num::NonZeroUsize;

use gdbstub::arch::RegId;

/// TI-MSP430 register identifier.
Expand All @@ -20,7 +22,7 @@ pub enum Msp430RegId {
}

impl RegId for Msp430RegId {
fn from_raw_id(id: usize) -> Option<(Self, usize)> {
fn from_raw_id(id: usize) -> Option<(Self, Option<NonZeroUsize>)> {
let reg = match id {
0 => Self::Pc,
1 => Self::Sp,
Expand All @@ -29,7 +31,7 @@ impl RegId for Msp430RegId {
4..=15 => Self::Gpr((id as u8) - 4),
_ => return None,
};
Some((reg, 2))
Some((reg, Some(NonZeroUsize::new(2)?)))
}
}

Expand Down Expand Up @@ -57,7 +59,7 @@ mod tests {
let mut i = 0;
let mut sum_reg_sizes = 0;
while let Some((_, size)) = RId::from_raw_id(i) {
sum_reg_sizes += size;
sum_reg_sizes += size.unwrap().get();
i += 1;
}

Expand Down
9 changes: 6 additions & 3 deletions gdbstub_arch/src/riscv/reg/id.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use core::num::NonZeroUsize;

use gdbstub::arch::RegId;

/// RISC-V Register identifier.
Expand All @@ -22,18 +24,19 @@ pub enum RiscvRegId<U> {
macro_rules! impl_riscv_reg_id {
($usize:ty) => {
impl RegId for RiscvRegId<$usize> {
fn from_raw_id(id: usize) -> Option<(Self, usize)> {
fn from_raw_id(id: usize) -> Option<(Self, Option<NonZeroUsize>)> {
const USIZE: usize = core::mem::size_of::<$usize>();
DrChat marked this conversation as resolved.
Show resolved Hide resolved

let reg_size = match id {
let (id, size) = match id {
0..=31 => (Self::Gpr(id as u8), USIZE),
32 => (Self::Pc, USIZE),
33..=64 => (Self::Fpr((id - 33) as u8), USIZE),
65..=4160 => (Self::Csr((id - 65) as u16), USIZE),
4161 => (Self::Priv, 1),
_ => return None,
};
Some(reg_size)

Some((id, Some(NonZeroUsize::new(size)?)))
}
}
};
Expand Down
18 changes: 11 additions & 7 deletions gdbstub_arch/src/x86/reg/id.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use core::num::NonZeroUsize;

use gdbstub::arch::RegId;

/// FPU register identifier.
Expand Down Expand Up @@ -115,10 +117,10 @@ pub enum X86CoreRegId {
}

impl RegId for X86CoreRegId {
fn from_raw_id(id: usize) -> Option<(Self, usize)> {
fn from_raw_id(id: usize) -> Option<(Self, Option<NonZeroUsize>)> {
use self::X86CoreRegId::*;

let r = match id {
let (r, sz): (X86CoreRegId, usize) = match id {
0 => (Eax, 4),
1 => (Ecx, 4),
2 => (Edx, 4),
Expand All @@ -136,7 +138,8 @@ impl RegId for X86CoreRegId {
40 => (Mxcsr, 4),
_ => return None,
};
Some(r)

Some((r, Some(NonZeroUsize::new(sz)?)))
}
}

Expand Down Expand Up @@ -167,10 +170,10 @@ pub enum X86_64CoreRegId {
}

impl RegId for X86_64CoreRegId {
fn from_raw_id(id: usize) -> Option<(Self, usize)> {
fn from_raw_id(id: usize) -> Option<(Self, Option<NonZeroUsize>)> {
use self::X86_64CoreRegId::*;

let r = match id {
let (r, sz): (X86_64CoreRegId, usize) = match id {
0..=15 => (Gpr(id as u8), 8),
16 => (Rip, 4),
17 => (Eflags, 8),
Expand All @@ -181,7 +184,8 @@ impl RegId for X86_64CoreRegId {
56 => (Mxcsr, 4),
_ => return None,
};
Some(r)

Some((r, Some(NonZeroUsize::new(sz)?)))
}
}

Expand All @@ -208,7 +212,7 @@ mod tests {
let mut i = 0;
let mut sum_reg_sizes = 0;
while let Some((_, size)) = RId::from_raw_id(i) {
sum_reg_sizes += size;
sum_reg_sizes += size.unwrap().get();
i += 1;
}

Expand Down
13 changes: 9 additions & 4 deletions src/arch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
//! > 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::{fmt::Debug, num::NonZeroUsize};

use num_traits::{FromPrimitive, PrimInt, Unsigned};

Expand All @@ -28,15 +28,20 @@ use crate::internal::{BeBytes, LeBytes};
///
/// [single register accesses]: crate::target::ext::base::SingleRegisterAccess
pub trait RegId: Sized + Debug {
/// Map raw GDB register number corresponding `RegId` and register size.
/// 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, usize)>;
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, usize)> {
fn from_raw_id(_id: usize) -> Option<(Self, Option<NonZeroUsize>)> {
None
}
}
Expand Down
34 changes: 29 additions & 5 deletions src/gdbstub_impl/ext/single_register_access.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use super::prelude::*;
use crate::protocol::commands::ext::SingleRegisterAccess;

use crate::arch::{Arch, RegId};
use crate::target::ext::base::BaseOps;
use crate::target::ext::base::{BaseOps, SendRegisterOutput};

impl<T: Target, C: Connection> GdbStubImpl<T, C> {
fn inner<Id>(
Expand All @@ -13,17 +13,41 @@ impl<T: Target, C: Connection> GdbStubImpl<T, C> {
) -> Result<HandlerStatus, Error<T::Error, C::Error>> {
let handler_status = match command {
SingleRegisterAccess::p(p) => {
let mut dst = [0u8; 32]; // enough for 256-bit registers
let reg = <T::Arch as Arch>::RegId::from_raw_id(p.reg_id);
let (reg_id, reg_size) = match reg {
// empty packet indicates unrecognized query
None => return Ok(HandlerStatus::Handled),
Some(v) => v,
};
let dst = &mut dst[0..reg_size];
ops.read_register(id, reg_id, dst).handle_error()?;

res.write_hex_buf(dst)?;
let mut n = 0usize;
let mut err = Ok(());

ops.read_register(
id,
reg_id,
SendRegisterOutput::new(&mut |buf| {
if err.is_ok() {
// If the register has a known size and read_register attempts
// to send more bytes than are present in the register,
// error out and stop sending data.
if let Some(size) = reg_size {
n += buf.len();

if n > size.get() {
err = Err(Error::TargetMismatch);
return;
}
}

err = res.write_hex_buf(buf).map_err(|e| e.into());
}
}),
)
.handle_error()?;

err?;

HandlerStatus::Handled
}
SingleRegisterAccess::P(p) => {
Expand Down
4 changes: 3 additions & 1 deletion src/target/ext/base/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ pub mod singlethread;

mod single_register_access;

pub use single_register_access::{SingleRegisterAccess, SingleRegisterAccessOps};
pub use single_register_access::{
SendRegisterOutput, SingleRegisterAccess, SingleRegisterAccessOps,
};

/// Base operations for single/multi threaded targets.
pub enum BaseOps<'a, A, E> {
Expand Down
20 changes: 18 additions & 2 deletions src/target/ext/base/single_register_access.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ pub trait SingleRegisterAccess<Id>: Target {
/// On single threaded targets, `tid` is set to `()` and can be ignored.
///
/// Implementations should write the value of the register using target's
/// native byte order in the buffer `dst`.
/// native byte order when writing via `output`.
///
/// If the requested register could not be accessed, an appropriate
/// non-fatal error should be returned.
Expand All @@ -35,7 +35,7 @@ pub trait SingleRegisterAccess<Id>: Target {
&mut self,
DrChat marked this conversation as resolved.
Show resolved Hide resolved
tid: Id,
reg_id: <Self::Arch as Arch>::RegId,
dst: &mut [u8],
output: SendRegisterOutput<'_>,
) -> TargetResult<(), Self>;

/// Write from a single register on the target.
Expand Down Expand Up @@ -65,3 +65,19 @@ pub trait SingleRegisterAccess<Id>: Target {
/// See [`SingleRegisterAccess`]
pub type SingleRegisterAccessOps<'a, Id, T> =
&'a mut dyn SingleRegisterAccess<Id, Arch = <T as Target>::Arch, Error = <T as Target>::Error>;

/// An interface to send register data to the GDB remote debugger.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// An interface to send register data to the GDB remote debugger.
/// An interface to send register data back to the GDB client.

pub struct SendRegisterOutput<'a> {
inner: &'a mut dyn FnMut(&[u8]),
}

impl<'a> SendRegisterOutput<'a> {
pub(crate) fn new(inner: &'a mut dyn FnMut(&[u8])) -> Self {
Self { inner }
}

/// Write out raw register bytes to the GDB debugger.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Write out raw register bytes to the GDB debugger.
/// Write raw register bytes to GDB. Bytes must be sent in the target's native endian order.

GDB stands for GNU DeBugger, so "GDB debugger" is redundant (like ATM Machine)

pub fn write(&mut self, data: &[u8]) {
(self.inner)(data)
}
}