From a4ff5702214b9b1643cb39de9a1c2cd24f550460 Mon Sep 17 00:00:00 2001 From: "Dr. Chat" Date: Thu, 27 May 2021 13:24:32 -0500 Subject: [PATCH] Allow variable register sizes for `SingleRegisterAccess` --- examples/armv4t/gdb/mod.rs | 4 +- gdbstub_arch/src/arm/reg/id.rs | 6 +- gdbstub_arch/src/mips/reg/id.rs | 14 ++-- gdbstub_arch/src/msp430/reg/id.rs | 8 ++- gdbstub_arch/src/riscv/reg/id.rs | 20 ++++-- gdbstub_arch/src/x86/reg/id.rs | 66 +++++++++++-------- src/arch.rs | 6 +- .../ext/single_register_access.rs | 18 +++-- src/target/ext/base/single_register_access.rs | 2 +- 9 files changed, 90 insertions(+), 54 deletions(-) diff --git a/examples/armv4t/gdb/mod.rs b/examples/armv4t/gdb/mod.rs index 685beb7f..464544f9 100644 --- a/examples/armv4t/gdb/mod.rs +++ b/examples/armv4t/gdb/mod.rs @@ -212,11 +212,11 @@ impl target::ext::base::SingleRegisterAccess<()> for Emu { &mut self, _tid: (), reg_id: gdbstub_arch::arm::reg::id::ArmCoreRegId, - dst: &mut [u8], + output: &mut dyn FnMut(&[u8]), ) -> 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(&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..927f3474 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).unwrap()))) } } diff --git a/gdbstub_arch/src/mips/reg/id.rs b/gdbstub_arch/src/mips/reg/id.rs index 57665c6a..bebb7e0c 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).unwrap()))), 79 => MipsRegId::Restart, _ => return None, }; let ptrsize = core::mem::size_of::(); - Some((reg, ptrsize)) + Some((reg, Some(NonZeroUsize::new(ptrsize).unwrap()))) } 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..6ec4671b 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).unwrap()))) } } @@ -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..ea76d5b4 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,15 +24,21 @@ 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 { - 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), + 0..=31 => (Self::Gpr(id as u8), Some(NonZeroUsize::new(USIZE).unwrap())), + 32 => (Self::Pc, Some(NonZeroUsize::new(USIZE).unwrap())), + 33..=64 => ( + Self::Fpr((id - 33) as u8), + Some(NonZeroUsize::new(USIZE).unwrap()), + ), + 65..=4160 => ( + Self::Csr((id - 65) as u16), + Some(NonZeroUsize::new(USIZE).unwrap()), + ), + 4161 => (Self::Priv, Some(NonZeroUsize::new(1).unwrap())), _ => return None, }; Some(reg_size) diff --git a/gdbstub_arch/src/x86/reg/id.rs b/gdbstub_arch/src/x86/reg/id.rs index 03de64b6..96a740f3 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,25 +117,31 @@ 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 { - 0 => (Eax, 4), - 1 => (Ecx, 4), - 2 => (Edx, 4), - 3 => (Ebx, 4), - 4 => (Esp, 4), - 5 => (Ebp, 4), - 6 => (Esi, 4), - 7 => (Edi, 4), - 8 => (Eip, 4), - 9 => (Eflags, 4), - 10..=15 => (Segment(X86SegmentRegId::from_u8(id as u8 - 10)?), 4), - 16..=23 => (St(id as u8 - 16), 10), - 24..=31 => (Fpu(X87FpuInternalRegId::from_u8(id as u8 - 24)?), 4), - 32..=39 => (Xmm(id as u8 - 32), 16), - 40 => (Mxcsr, 4), + 0 => (Eax, Some(NonZeroUsize::new(4).unwrap())), + 1 => (Ecx, Some(NonZeroUsize::new(4).unwrap())), + 2 => (Edx, Some(NonZeroUsize::new(4).unwrap())), + 3 => (Ebx, Some(NonZeroUsize::new(4).unwrap())), + 4 => (Esp, Some(NonZeroUsize::new(4).unwrap())), + 5 => (Ebp, Some(NonZeroUsize::new(4).unwrap())), + 6 => (Esi, Some(NonZeroUsize::new(4).unwrap())), + 7 => (Edi, Some(NonZeroUsize::new(4).unwrap())), + 8 => (Eip, Some(NonZeroUsize::new(4).unwrap())), + 9 => (Eflags, Some(NonZeroUsize::new(4).unwrap())), + 10..=15 => ( + Segment(X86SegmentRegId::from_u8(id as u8 - 10)?), + Some(NonZeroUsize::new(4).unwrap()), + ), + 16..=23 => (St(id as u8 - 16), Some(NonZeroUsize::new(10).unwrap())), + 24..=31 => ( + Fpu(X87FpuInternalRegId::from_u8(id as u8 - 24)?), + Some(NonZeroUsize::new(4).unwrap()), + ), + 32..=39 => (Xmm(id as u8 - 32), Some(NonZeroUsize::new(16).unwrap())), + 40 => (Mxcsr, Some(NonZeroUsize::new(4).unwrap())), _ => return None, }; Some(r) @@ -167,18 +175,24 @@ 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 { - 0..=15 => (Gpr(id as u8), 8), - 16 => (Rip, 4), - 17 => (Eflags, 8), - 18..=23 => (Segment(X86SegmentRegId::from_u8(id as u8 - 18)?), 4), - 24..=31 => (St(id as u8 - 24), 10), - 32..=39 => (Fpu(X87FpuInternalRegId::from_u8(id as u8 - 32)?), 4), - 40..=55 => (Xmm(id as u8 - 40), 16), - 56 => (Mxcsr, 4), + 0..=15 => (Gpr(id as u8), Some(NonZeroUsize::new(8).unwrap())), + 16 => (Rip, Some(NonZeroUsize::new(4).unwrap())), + 17 => (Eflags, Some(NonZeroUsize::new(8).unwrap())), + 18..=23 => ( + Segment(X86SegmentRegId::from_u8(id as u8 - 18)?), + Some(NonZeroUsize::new(4).unwrap()), + ), + 24..=31 => (St(id as u8 - 24), Some(NonZeroUsize::new(10).unwrap())), + 32..=39 => ( + Fpu(X87FpuInternalRegId::from_u8(id as u8 - 32)?), + Some(NonZeroUsize::new(4).unwrap()), + ), + 40..=55 => (Xmm(id as u8 - 40), Some(NonZeroUsize::new(16).unwrap())), + 56 => (Mxcsr, Some(NonZeroUsize::new(4).unwrap())), _ => return None, }; Some(r) @@ -208,7 +222,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..25968993 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}; @@ -31,12 +31,12 @@ pub trait RegId: Sized + Debug { /// Map raw GDB register number corresponding `RegId` and register size. /// /// 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..cedea5e5 100644 --- a/src/gdbstub_impl/ext/single_register_access.rs +++ b/src/gdbstub_impl/ext/single_register_access.rs @@ -13,17 +13,25 @@ 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 { + 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)?; + // TODO: Limit the number of bytes transferred on the wire to the register size + // (if specified). Maybe pad the register if the callee does not + // send enough data? + let mut err = Ok(()); + ops.read_register(id, reg_id, &mut |buf| match res.write_hex_buf(buf) { + Ok(_) => {} + Err(e) => err = Err(e), + }) + .handle_error()?; + + err?; + HandlerStatus::Handled } SingleRegisterAccess::P(p) => { diff --git a/src/target/ext/base/single_register_access.rs b/src/target/ext/base/single_register_access.rs index bf94c80d..56d4518f 100644 --- a/src/target/ext/base/single_register_access.rs +++ b/src/target/ext/base/single_register_access.rs @@ -35,7 +35,7 @@ pub trait SingleRegisterAccess: Target { &mut self, tid: Id, reg_id: ::RegId, - dst: &mut [u8], + output: &mut dyn FnMut(&[u8]), ) -> TargetResult<(), Self>; /// Write from a single register on the target.