diff --git a/examples/armv4t/gdb/mod.rs b/examples/armv4t/gdb/mod.rs index 685beb7f..34f2b85b 100644 --- a/examples/armv4t/gdb/mod.rs +++ b/examples/armv4t/gdb/mod.rs @@ -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; @@ -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()) diff --git a/gdbstub_arch/src/arm/reg/id.rs b/gdbstub_arch/src/arm/reg/id.rs index c7e11a2e..5b79c898 100644 --- a/gdbstub_arch/src/arm/reg/id.rs +++ b/gdbstub_arch/src/arm/reg/id.rs @@ -1,3 +1,5 @@ +use core::num::NonZeroUsize; + use gdbstub::arch::RegId; /// 32-bit ARM core register identifier. @@ -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)> { let reg = match id { 0..=12 => Self::Gpr(id as u8), 13 => Self::Sp, @@ -31,6 +33,6 @@ impl RegId for ArmCoreRegId { 25 => Self::Cpsr, _ => return None, }; - Some((reg, 4)) + Some((reg, Some(NonZeroUsize::new(4)?))) } } diff --git a/gdbstub_arch/src/mips/reg/id.rs b/gdbstub_arch/src/mips/reg/id.rs index 57665c6a..b7c7ba50 100644 --- a/gdbstub_arch/src/mips/reg/id.rs +++ b/gdbstub_arch/src/mips/reg/id.rs @@ -1,3 +1,5 @@ +use core::num::NonZeroUsize; + use gdbstub::arch::RegId; /// MIPS register identifier. @@ -44,7 +46,7 @@ pub enum MipsRegId { _Size(U), } -fn from_raw_id(id: usize) -> Option<(MipsRegId, usize)> { +fn from_raw_id(id: usize) -> Option<(MipsRegId, Option)> { let reg = match id { 0..=31 => MipsRegId::Gpr(id as u8), 32 => MipsRegId::Status, @@ -63,23 +65,23 @@ fn from_raw_id(id: usize) -> Option<(MipsRegId, 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::(); - Some((reg, ptrsize)) + Some((reg, Some(NonZeroUsize::new(ptrsize)?))) } impl RegId for MipsRegId { - fn from_raw_id(id: usize) -> Option<(Self, usize)> { + fn from_raw_id(id: usize) -> Option<(Self, Option)> { from_raw_id::(id) } } impl RegId for MipsRegId { - fn from_raw_id(id: usize) -> Option<(Self, usize)> { + fn from_raw_id(id: usize) -> Option<(Self, Option)> { from_raw_id::(id) } } @@ -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; } diff --git a/gdbstub_arch/src/msp430/reg/id.rs b/gdbstub_arch/src/msp430/reg/id.rs index 1c387635..33807c88 100644 --- a/gdbstub_arch/src/msp430/reg/id.rs +++ b/gdbstub_arch/src/msp430/reg/id.rs @@ -1,3 +1,5 @@ +use core::num::NonZeroUsize; + use gdbstub::arch::RegId; /// TI-MSP430 register identifier. @@ -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)> { let reg = match id { 0 => Self::Pc, 1 => Self::Sp, @@ -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)?))) } } @@ -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; } diff --git a/gdbstub_arch/src/riscv/reg/id.rs b/gdbstub_arch/src/riscv/reg/id.rs index b6e589dd..621231e3 100644 --- a/gdbstub_arch/src/riscv/reg/id.rs +++ b/gdbstub_arch/src/riscv/reg/id.rs @@ -1,3 +1,5 @@ +use core::num::NonZeroUsize; + use gdbstub::arch::RegId; /// RISC-V Register identifier. @@ -22,10 +24,10 @@ pub enum RiscvRegId { 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)> { const USIZE: usize = core::mem::size_of::<$usize>(); - 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), @@ -33,7 +35,8 @@ macro_rules! impl_riscv_reg_id { 4161 => (Self::Priv, 1), _ => return None, }; - Some(reg_size) + + Some((id, Some(NonZeroUsize::new(size)?))) } } }; diff --git a/gdbstub_arch/src/x86/reg/id.rs b/gdbstub_arch/src/x86/reg/id.rs index 03de64b6..b7dce820 100644 --- a/gdbstub_arch/src/x86/reg/id.rs +++ b/gdbstub_arch/src/x86/reg/id.rs @@ -1,3 +1,5 @@ +use core::num::NonZeroUsize; + use gdbstub::arch::RegId; /// FPU register identifier. @@ -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)> { use self::X86CoreRegId::*; - let r = match id { + let (r, sz): (X86CoreRegId, usize) = match id { 0 => (Eax, 4), 1 => (Ecx, 4), 2 => (Edx, 4), @@ -136,7 +138,8 @@ impl RegId for X86CoreRegId { 40 => (Mxcsr, 4), _ => return None, }; - Some(r) + + Some((r, Some(NonZeroUsize::new(sz)?))) } } @@ -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)> { 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), @@ -181,7 +184,8 @@ impl RegId for X86_64CoreRegId { 56 => (Mxcsr, 4), _ => return None, }; - Some(r) + + Some((r, Some(NonZeroUsize::new(sz)?))) } } @@ -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; } diff --git a/src/arch.rs b/src/arch.rs index dbb21fae..89810c20 100644 --- a/src/arch.rs +++ b/src/arch.rs @@ -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}; @@ -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)>; } /// 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)> { None } } diff --git a/src/gdbstub_impl/ext/single_register_access.rs b/src/gdbstub_impl/ext/single_register_access.rs index 04ef9f2a..4f538d35 100644 --- a/src/gdbstub_impl/ext/single_register_access.rs +++ b/src/gdbstub_impl/ext/single_register_access.rs @@ -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 GdbStubImpl { fn inner( @@ -13,17 +13,41 @@ impl GdbStubImpl { ) -> Result> { let handler_status = match command { SingleRegisterAccess::p(p) => { - let mut dst = [0u8; 32]; // enough for 256-bit registers let reg = ::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) => { diff --git a/src/target/ext/base/mod.rs b/src/target/ext/base/mod.rs index 04bcbd95..2335b33f 100644 --- a/src/target/ext/base/mod.rs +++ b/src/target/ext/base/mod.rs @@ -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> { diff --git a/src/target/ext/base/single_register_access.rs b/src/target/ext/base/single_register_access.rs index bf94c80d..d00451a8 100644 --- a/src/target/ext/base/single_register_access.rs +++ b/src/target/ext/base/single_register_access.rs @@ -22,7 +22,7 @@ pub trait SingleRegisterAccess: 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. @@ -35,7 +35,7 @@ pub trait SingleRegisterAccess: Target { &mut self, tid: Id, reg_id: ::RegId, - dst: &mut [u8], + output: SendRegisterOutput<'_>, ) -> TargetResult<(), Self>; /// Write from a single register on the target. @@ -65,3 +65,19 @@ pub trait SingleRegisterAccess: Target { /// See [`SingleRegisterAccess`] pub type SingleRegisterAccessOps<'a, Id, T> = &'a mut dyn SingleRegisterAccess::Arch, Error = ::Error>; + +/// An interface to send register data to the GDB remote debugger. +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. + pub fn write(&mut self, data: &[u8]) { + (self.inner)(data) + } +}