From 2689c37e12d93ba13db480561a50ad368d01d308 Mon Sep 17 00:00:00 2001 From: wj Date: Wed, 8 Jun 2022 16:47:12 +0200 Subject: [PATCH] add qRegisterInfo packet support --- examples/armv4t/gdb/mod.rs | 101 ++++++++++++ examples/armv4t/gdb/register_info_override.rs | 106 +++++++++++++ src/arch.rs | 147 ++++++++++++++++++ src/protocol/commands.rs | 17 ++ src/protocol/commands/_qRegisterInfo.rs | 17 ++ src/protocol/response_writer.rs | 41 ++++- src/stub/core_impl.rs | 2 + src/stub/core_impl/register_info.rs | 138 ++++++++++++++++ src/target/ext/mod.rs | 1 + src/target/ext/register_info_override.rs | 54 +++++++ src/target/mod.rs | 34 ++++ 11 files changed, 656 insertions(+), 2 deletions(-) create mode 100644 examples/armv4t/gdb/register_info_override.rs create mode 100644 src/protocol/commands/_qRegisterInfo.rs create mode 100644 src/stub/core_impl/register_info.rs create mode 100644 src/target/ext/register_info_override.rs diff --git a/examples/armv4t/gdb/mod.rs b/examples/armv4t/gdb/mod.rs index 155fff3e..4c5d32f8 100644 --- a/examples/armv4t/gdb/mod.rs +++ b/examples/armv4t/gdb/mod.rs @@ -19,6 +19,7 @@ mod extended_mode; mod host_io; mod memory_map; mod monitor_cmd; +mod register_info_override; mod section_offsets; mod target_description_xml_override; @@ -115,6 +116,13 @@ impl Target for Emu { Some(self) } + #[inline(always)] + fn support_register_info_override( + &mut self, + ) -> Option> { + Some(self) + } + #[inline(always)] fn support_memory_map(&mut self) -> Option> { Some(self) @@ -369,6 +377,7 @@ impl target::ext::base::singlethread::SingleThreadRangeStepping for Emu { mod custom_arch { use core::num::NonZeroUsize; + use gdbstub::arch::lldb::{Encoding, Format, Generic, Register, RegisterInfo}; use gdbstub::arch::{Arch, RegId, Registers, SingleStepGdbBehavior}; use gdbstub_arch::arm::reg::id::ArmCoreRegId; @@ -482,6 +491,98 @@ mod custom_arch { Some("never gets returned") } + // (LLDB extension) + // + // for _purely demonstrative purposes_, even though this provides a working + // example, it will get overwritten by RegisterInfoOverride. + // + // See `examples/armv4t/gdb/register_info_override.rs` + fn register_info(reg_id: usize) -> Option> { + // Fix for missing 24 => Self::Fps in ArmCoreRegId::from_raw_id + let id = if reg_id == 24 { 23 } else { reg_id }; + + match ArmCoreRegIdCustom::from_raw_id(id) { + Some((_, None)) | None => Some(RegisterInfo::Done), + Some((r, Some(size))) => { + let name = match r { + // For the purpose of demonstration, we end the qRegisterInfo packet + // exchange when reaching the Time register id, so that this register can + // only be explicitly queried via the single-register read packet. + ArmCoreRegIdCustom::Time => return Some(RegisterInfo::Done), + ArmCoreRegIdCustom::Core(ArmCoreRegId::Gpr(i)) => match i { + 0 => "r0", + 1 => "r1", + 2 => "r2", + 3 => "r3", + 4 => "r4", + 5 => "r5", + 6 => "r6", + 7 => "r7", + 8 => "r8", + 9 => "r9", + 10 => "r10", + 11 => "r11", + 12 => "r12", + _ => "unknown", + }, + ArmCoreRegIdCustom::Core(ArmCoreRegId::Sp) => "sp", + ArmCoreRegIdCustom::Core(ArmCoreRegId::Lr) => "lr", + ArmCoreRegIdCustom::Core(ArmCoreRegId::Pc) => "pc", + ArmCoreRegIdCustom::Core(ArmCoreRegId::Fpr(_i)) => "padding", + ArmCoreRegIdCustom::Core(ArmCoreRegId::Fps) => "padding", + ArmCoreRegIdCustom::Core(ArmCoreRegId::Cpsr) => "cpsr", + ArmCoreRegIdCustom::Custom => "custom", + _ => "unknown", + }; + let encoding = match r { + ArmCoreRegIdCustom::Core(ArmCoreRegId::Gpr(_i)) => Encoding::Uint, + ArmCoreRegIdCustom::Core(ArmCoreRegId::Sp) + | ArmCoreRegIdCustom::Core(ArmCoreRegId::Pc) + | ArmCoreRegIdCustom::Core(ArmCoreRegId::Cpsr) + | ArmCoreRegIdCustom::Custom => Encoding::Uint, + _ => Encoding::Vector, + }; + let format = match r { + ArmCoreRegIdCustom::Core(ArmCoreRegId::Gpr(_i)) => Format::Hex, + ArmCoreRegIdCustom::Core(ArmCoreRegId::Sp) + | ArmCoreRegIdCustom::Core(ArmCoreRegId::Pc) + | ArmCoreRegIdCustom::Core(ArmCoreRegId::Cpsr) + | ArmCoreRegIdCustom::Custom => Format::Hex, + _ => Format::VectorUInt8, + }; + let set = match r { + ArmCoreRegIdCustom::Core(ArmCoreRegId::Gpr(_i)) => { + "General Purpose Registers" + } + ArmCoreRegIdCustom::Core(ArmCoreRegId::Sp) + | ArmCoreRegIdCustom::Core(ArmCoreRegId::Pc) + | ArmCoreRegIdCustom::Core(ArmCoreRegId::Cpsr) + | ArmCoreRegIdCustom::Custom => "General Purpose Registers", + _ => "Floating Point Registers", + }; + let generic = match r { + ArmCoreRegIdCustom::Core(ArmCoreRegId::Sp) => Some(Generic::Sp), + ArmCoreRegIdCustom::Core(ArmCoreRegId::Pc) => Some(Generic::Pc), + _ => None, + }; + let reg = Register { + name, + alt_name: None, + bitsize: (usize::from(size)) * 8, + offset: id * (usize::from(size)), + encoding, + format, + set, + gcc: None, + dwarf: Some(id), + generic, + container_regs: None, + invalidate_regs: None, + }; + Some(RegisterInfo::Register(reg)) + } + } + } // armv4t supports optional single stepping. // // notably, x86 is an example of an arch that does _not_ support diff --git a/examples/armv4t/gdb/register_info_override.rs b/examples/armv4t/gdb/register_info_override.rs new file mode 100644 index 00000000..98676839 --- /dev/null +++ b/examples/armv4t/gdb/register_info_override.rs @@ -0,0 +1,106 @@ +use gdbstub::arch::lldb::{Encoding, Format, Generic, Register}; +use gdbstub::arch::RegId; +use gdbstub::target; +use gdbstub::target::ext::register_info_override::{Callback, CallbackToken}; +use gdbstub_arch::arm::reg::id::ArmCoreRegId; + +use crate::gdb::custom_arch::ArmCoreRegIdCustom; +use crate::gdb::Emu; + +// (LLDB extension) This implementation is for illustrative purposes only. +// +// Note: In this implementation, we have r0-pc from 0-16 but cpsr is at offset +// 25*4 in the 'g'/'G' packets, so we add 8 padding registers here. Please see +// gdbstub/examples/armv4t/gdb/target_description_xml_override.rs for more info. +impl target::ext::register_info_override::RegisterInfoOverride for Emu { + fn register_info<'a>( + &mut self, + reg_id: usize, + reg_info: Callback<'a>, + ) -> Result, Self::Error> { + // Fix for missing 24 => Self::Fps in ArmCoreRegId::from_raw_id + let id = if reg_id == 24 { 23 } else { reg_id }; + + match ArmCoreRegIdCustom::from_raw_id(id) { + Some((_, None)) | None => Ok(reg_info.done()), + Some((r, Some(size))) => { + let name: String = match r { + // For the purpose of demonstration, we end the qRegisterInfo packet exchange + // when reaching the Time register id, so that this register can only be + // explicitly queried via the single-register read packet. + ArmCoreRegIdCustom::Time => return Ok(reg_info.done()), + ArmCoreRegIdCustom::Core(ArmCoreRegId::Gpr(i)) => match i { + 0 => "r0", + 1 => "r1", + 2 => "r2", + 3 => "r3", + 4 => "r4", + 5 => "r5", + 6 => "r6", + 7 => "r7", + 8 => "r8", + 9 => "r9", + 10 => "r10", + 11 => "r11", + 12 => "r12", + _ => "unknown", + }, + ArmCoreRegIdCustom::Core(ArmCoreRegId::Sp) => "sp", + ArmCoreRegIdCustom::Core(ArmCoreRegId::Lr) => "lr", + ArmCoreRegIdCustom::Core(ArmCoreRegId::Pc) => "pc", + ArmCoreRegIdCustom::Core(ArmCoreRegId::Fpr(_i)) => "padding", + ArmCoreRegIdCustom::Core(ArmCoreRegId::Fps) => "padding", + ArmCoreRegIdCustom::Core(ArmCoreRegId::Cpsr) => "cpsr", + ArmCoreRegIdCustom::Custom => "custom", + _ => "unknown", + } + .into(); + let encoding = match r { + ArmCoreRegIdCustom::Core(ArmCoreRegId::Gpr(_i)) => Encoding::Uint, + ArmCoreRegIdCustom::Core(ArmCoreRegId::Sp) + | ArmCoreRegIdCustom::Core(ArmCoreRegId::Pc) + | ArmCoreRegIdCustom::Core(ArmCoreRegId::Cpsr) + | ArmCoreRegIdCustom::Custom => Encoding::Uint, + _ => Encoding::Vector, + }; + let format = match r { + ArmCoreRegIdCustom::Core(ArmCoreRegId::Gpr(_i)) => Format::Hex, + ArmCoreRegIdCustom::Core(ArmCoreRegId::Sp) + | ArmCoreRegIdCustom::Core(ArmCoreRegId::Pc) + | ArmCoreRegIdCustom::Core(ArmCoreRegId::Cpsr) + | ArmCoreRegIdCustom::Custom => Format::Hex, + _ => Format::VectorUInt8, + }; + let set: String = match r { + ArmCoreRegIdCustom::Core(ArmCoreRegId::Gpr(_i)) => "General Purpose Registers", + ArmCoreRegIdCustom::Core(ArmCoreRegId::Sp) + | ArmCoreRegIdCustom::Core(ArmCoreRegId::Pc) + | ArmCoreRegIdCustom::Core(ArmCoreRegId::Cpsr) + | ArmCoreRegIdCustom::Custom => "General Purpose Registers", + _ => "Floating Point Registers", + } + .into(); + let generic = match r { + ArmCoreRegIdCustom::Core(ArmCoreRegId::Sp) => Some(Generic::Sp), + ArmCoreRegIdCustom::Core(ArmCoreRegId::Pc) => Some(Generic::Pc), + _ => None, + }; + let reg = Register { + name: &name, + alt_name: None, + bitsize: (usize::from(size)) * 8, + offset: id * (usize::from(size)), + encoding, + format, + set: &set, + gcc: None, + dwarf: Some(id), + generic, + container_regs: None, + invalidate_regs: None, + }; + Ok(reg_info.write(reg)) + } + } + } +} diff --git a/src/arch.rs b/src/arch.rs index 06cb49e0..00caaf6b 100644 --- a/src/arch.rs +++ b/src/arch.rs @@ -159,6 +159,33 @@ pub trait Arch { None } + /// (optional) (LLDB extension) Write register info for one of the arch's + /// 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 register_info(reg_id: usize) -> Option> { + let _ = reg_id; + None + } + /// Encode how the mainline GDB client handles target support for /// single-step on this particular architecture. /// @@ -255,3 +282,123 @@ pub enum SingleStepGdbBehavior { #[doc(hidden)] Unknown, } + +/// LLDB-specific types supporting [`Arch::register_info`] and +/// [`RegisterInfoOverride`](crate::target::ext::register_info_override:: +/// RegisterInfoOverride) APIs +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, + /// The DWARF register number for this register that is used for this + /// register in the debug information. + pub dwarf: Option, + /// Specify as a generic register. + pub generic: Option, + /// 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, + } +} diff --git a/src/protocol/commands.rs b/src/protocol/commands.rs index 1e1f973f..22399f8b 100644 --- a/src/protocol/commands.rs +++ b/src/protocol/commands.rs @@ -87,6 +87,7 @@ macro_rules! commands { trait Hack { fn support_base(&mut self) -> Option<()>; fn support_target_xml(&mut self) -> Option<()>; + fn support_register_info(&mut self) -> Option<()>; fn support_resume(&mut self) -> Option<()>; fn support_single_register_access(&mut self) -> Option<()>; fn support_reverse_step(&mut self) -> Option<()>; @@ -112,6 +113,18 @@ macro_rules! commands { } } + fn support_register_info(&mut self) -> Option<()> { + use crate::arch::Arch; + if self.use_register_info() + && (T::Arch::register_info(usize::max_value()).is_some() + || self.support_register_info_override().is_some()) + { + Some(()) + } else { + None + } + } + fn support_resume(&mut self) -> Option<()> { self.base_ops().resume_ops().map(drop) } @@ -301,4 +314,8 @@ commands! { thread_extra_info use 'a { "qThreadExtraInfo" => _qThreadExtraInfo::qThreadExtraInfo<'a>, } + + register_info { + "qRegisterInfo" => _qRegisterInfo::qRegisterInfo, + } } diff --git a/src/protocol/commands/_qRegisterInfo.rs b/src/protocol/commands/_qRegisterInfo.rs new file mode 100644 index 00000000..545b841d --- /dev/null +++ b/src/protocol/commands/_qRegisterInfo.rs @@ -0,0 +1,17 @@ +use super::prelude::*; + +#[derive(Debug)] +pub struct qRegisterInfo { + pub reg_id: usize, +} + +impl<'a> ParseCommand<'a> for qRegisterInfo { + #[inline(always)] + fn from_packet(buf: PacketBuf<'a>) -> Option { + let body = buf.into_body(); + + let reg_id = decode_hex(body).ok()?; + + Some(qRegisterInfo { reg_id }) + } +} diff --git a/src/protocol/response_writer.rs b/src/protocol/response_writer.rs index 2117181d..3dea22a5 100644 --- a/src/protocol/response_writer.rs +++ b/src/protocol/response_writer.rs @@ -3,7 +3,8 @@ use alloc::string::String; #[cfg(feature = "trace-pkt")] use alloc::vec::Vec; -use num_traits::PrimInt; +use num_traits::identities::one; +use num_traits::{CheckedRem, PrimInt}; use crate::conn::Connection; use crate::internal::BeBytes; @@ -156,7 +157,7 @@ impl<'a, C: Connection + 'a> ResponseWriter<'a, C> { } /// Write an entire string over the connection. - pub fn write_str(&mut self, s: &'static str) -> Result<(), Error> { + pub fn write_str(&mut self, s: &str) -> Result<(), Error> { for b in s.as_bytes().iter() { self.write(*b)?; } @@ -228,6 +229,42 @@ impl<'a, C: Connection + 'a> ResponseWriter<'a, C> { Ok(()) } + /// Write a number as a decimal string, converting every digit to an ascii + /// char. + pub fn write_dec( + &mut self, + mut digit: D, + ) -> Result<(), Error> { + if digit.is_zero() { + return self.write(b'0'); + } + + let one: D = one(); + let ten = (one << 3) + (one << 1); + let mut d = digit; + let mut pow_10 = one; + // Get the number of digits in digit + while d >= ten { + d = d / ten; + pow_10 = pow_10 * ten; + } + + // Write every digit from left to right as an ascii char + while !pow_10.is_zero() { + let mut byte = 0; + // We have a single digit here which uses up to 4 bit + for i in 0..4 { + if !((digit / pow_10) & (one << i)).is_zero() { + byte += 1 << i; + } + } + self.write(b'0' + byte)?; + digit = digit % pow_10; + pow_10 = pow_10 / ten; + } + Ok(()) + } + #[inline] fn write_specific_id_kind(&mut self, tid: SpecificIdKind) -> Result<(), Error> { match tid { diff --git a/src/stub/core_impl.rs b/src/stub/core_impl.rs index d7c142e8..73503dfb 100644 --- a/src/stub/core_impl.rs +++ b/src/stub/core_impl.rs @@ -30,6 +30,7 @@ mod extended_mode; mod host_io; mod memory_map; mod monitor_cmd; +mod register_info; mod resume; mod reverse_exec; mod section_offsets; @@ -209,6 +210,7 @@ impl GdbStubImpl { Command::ExecFile(cmd) => self.handle_exec_file(res, target, cmd), Command::Auxv(cmd) => self.handle_auxv(res, target, cmd), Command::ThreadExtraInfo(cmd) => self.handle_thread_extra_info(res, target, cmd), + Command::RegisterInfo(cmd) => self.handle_register_info(res, target, cmd), // in the worst case, the command could not be parsed... Command::Unknown(cmd) => { // HACK: if the user accidentally sends a resume command to a diff --git a/src/stub/core_impl/register_info.rs b/src/stub/core_impl/register_info.rs new file mode 100644 index 00000000..ea3b0899 --- /dev/null +++ b/src/stub/core_impl/register_info.rs @@ -0,0 +1,138 @@ +use super::prelude::*; +use crate::protocol::commands::ext::RegisterInfo; + +use crate::arch::lldb::{Encoding, Format, Generic, Register, RegisterInfo as LLDBRegisterInfo}; +use crate::arch::Arch; + +impl GdbStubImpl { + pub(crate) fn handle_register_info( + &mut self, + res: &mut ResponseWriter<'_, C>, + target: &mut T, + command: RegisterInfo, + ) -> Result> { + if !target.use_register_info() { + return Ok(HandlerStatus::Handled); + } + + let handler_status = match command { + RegisterInfo::qRegisterInfo(cmd) => { + let mut err = Ok(()); + let cb = &mut |reg: Option>| { + let res = match reg { + // TODO: replace this with a try block (once stabilized) + Some(reg) => (|| { + res.write_str("name:")?; + res.write_str(reg.name)?; + if let Some(alt_name) = reg.alt_name { + res.write_str(";alt-name:")?; + res.write_str(alt_name)?; + } + res.write_str(";bitsize:")?; + res.write_dec(reg.bitsize)?; + res.write_str(";offset:")?; + res.write_dec(reg.offset)?; + res.write_str(";encoding:")?; + res.write_str(match reg.encoding { + Encoding::Uint => "uint", + Encoding::Sint => "sint", + Encoding::IEEE754 => "ieee754", + Encoding::Vector => "vector", + })?; + res.write_str(";format:")?; + res.write_str(match reg.format { + Format::Binary => "binary", + Format::Decimal => "decimal", + Format::Hex => "hex", + Format::Float => "float", + Format::VectorSInt8 => "vector-sint8", + Format::VectorUInt8 => "vector-uint8", + Format::VectorSInt16 => "vector-sint16", + Format::VectorUInt16 => "vector-uint16", + Format::VectorSInt32 => "vector-sint32", + Format::VectorUInt32 => "vector-uint32", + Format::VectorFloat32 => "vector-float32", + Format::VectorUInt128 => "vector-uint128", + })?; + res.write_str(";set:")?; + res.write_str(reg.set)?; + if let Some(gcc) = reg.gcc { + res.write_str(";gcc:")?; + res.write_dec(gcc)?; + } + if let Some(dwarf) = reg.dwarf { + res.write_str(";dwarf:")?; + res.write_dec(dwarf)?; + } + if let Some(generic) = reg.generic { + res.write_str(";generic:")?; + res.write_str(match generic { + Generic::Pc => "pc", + Generic::Sp => "sp", + Generic::Fp => "fp", + Generic::Ra => "ra", + Generic::Flags => "flags", + Generic::Arg1 => "arg1", + Generic::Arg2 => "arg2", + Generic::Arg3 => "arg3", + Generic::Arg4 => "arg4", + Generic::Arg5 => "arg5", + Generic::Arg6 => "arg6", + Generic::Arg7 => "arg7", + Generic::Arg8 => "arg8", + })?; + } + if let Some(c_regs) = reg.container_regs { + res.write_str(";container-regs:")?; + res.write_num(c_regs[0])?; + for reg in c_regs.iter().skip(1) { + res.write_str(",")?; + res.write_num(*reg)?; + } + } + if let Some(i_regs) = reg.invalidate_regs { + res.write_str(";invalidate-regs:")?; + res.write_num(i_regs[0])?; + for reg in i_regs.iter().skip(1) { + res.write_str(",")?; + res.write_num(*reg)?; + } + } + res.write_str(";") + })(), + // In fact, this doesn't has to be E45! It could equally well be any + // other error code or even an eOk, eAck or eNack! It turns out that + // 0x45 == 69, so presumably the LLDB people were just having some fun + // here. For a little discussion on this and LLDB source code pointers, + // see https://github.com/daniel5151/gdbstub/pull/103#discussion_r888590197 + _ => res.write_str("E45"), + }; + if let Err(e) = res { + err = Err(e); + } + }; + if let Some(ops) = target.support_register_info_override() { + use crate::target::ext::register_info_override::{Callback, CallbackToken}; + + ops.register_info( + cmd.reg_id, + Callback { + cb, + token: CallbackToken(core::marker::PhantomData), + }, + ) + .map_err(Error::TargetError)?; + err?; + } else if let Some(reg) = T::Arch::register_info(cmd.reg_id) { + match reg { + LLDBRegisterInfo::Register(reg) => cb(Some(reg)), + LLDBRegisterInfo::Done => cb(None), + }; + } + HandlerStatus::Handled + } + }; + + Ok(handler_status) + } +} diff --git a/src/target/ext/mod.rs b/src/target/ext/mod.rs index ef2c155f..d9c49ad5 100644 --- a/src/target/ext/mod.rs +++ b/src/target/ext/mod.rs @@ -267,6 +267,7 @@ pub mod extended_mode; pub mod host_io; pub mod memory_map; pub mod monitor_cmd; +pub mod register_info_override; pub mod section_offsets; pub mod target_description_xml_override; pub mod thread_extra_info; diff --git a/src/target/ext/register_info_override.rs b/src/target/ext/register_info_override.rs new file mode 100644 index 00000000..07006eb7 --- /dev/null +++ b/src/target/ext/register_info_override.rs @@ -0,0 +1,54 @@ +//! (LLDB extension) Override the register info specified by `Target::Arch`. +use crate::arch::lldb::Register; +use crate::target::Target; + +/// If your target is using the `qRegisterInfo` packet, this +/// token hast to be returned from the `register_info` function to guarantee +/// that the callback function to write the register info has been invoked. +pub struct CallbackToken<'a>(pub(crate) core::marker::PhantomData<&'a *mut ()>); + +/// This struct is used internally by `gdbstub` to wrap a +/// callback function which has to be used to direct the `qRegisterInfo` query. +pub struct Callback<'a> { + /// The callback function that is directing the `qRegisterInfo` query. + pub(crate) cb: &'a mut dyn FnMut(Option>), + /// A token to guarantee the callback has been used. + pub(crate) token: CallbackToken<'a>, +} + +impl<'a> Callback<'a> { + /// The `qRegisterInfo` query shall be concluded. + #[inline(always)] + pub fn done(self) -> CallbackToken<'a> { + (self.cb)(None); + self.token + } + + /// Write the register info of a single register. + #[inline(always)] + pub fn write(self, reg: Register<'_>) -> CallbackToken<'a> { + (self.cb)(Some(reg)); + self.token + } +} + +/// Target Extension - Override the target register info +/// specified by `Target::Arch`. +/// +/// _Note:_ Unless you're working with a particularly dynamic, +/// runtime-configurable target, it's unlikely that you'll need to implement +/// this extension. +pub trait RegisterInfoOverride: Target { + /// Invoke `reg_info.write(reg)` where `reg` is a [`Register` + /// ](crate::arch::lldb::Register) struct to write information of + /// a single register or `reg_info.done()` if you want to end the + /// `qRegisterInfo` packet exchange. These two methods will return a + /// `CallbackToken`, which has to be returned from this method. + fn register_info<'a>( + &mut self, + reg_id: usize, + reg_info: Callback<'a>, + ) -> Result, Self::Error>; +} + +define_ext!(RegisterInfoOverrideOps, RegisterInfoOverride); diff --git a/src/target/mod.rs b/src/target/mod.rs index eaed4270..11febc98 100644 --- a/src/target/mod.rs +++ b/src/target/mod.rs @@ -597,6 +597,21 @@ pub trait Target { true } + /// (LLDB extension) Whether to send register information to the client. + /// + /// Setting this to `false` will override both + /// [`Target::support_register_info_override`] and the associated + /// [`Arch::register_info`]. + /// + /// _Author's note:_ Having the LLDB client autodetect your target's + /// register set is really useful, so unless you're _really_ trying to + /// squeeze `gdbstub` onto a particularly resource-constrained platform, you + /// may as well leave this enabled. + #[inline(always)] + fn use_register_info(&self) -> bool { + true + } + /// Support for setting / removing breakpoints. #[inline(always)] fn support_breakpoints(&mut self) -> Option> { @@ -634,6 +649,15 @@ pub trait Target { None } + /// (LLDB extension) Support for overriding the register info specified by + /// `Target::Arch`. + #[inline(always)] + fn support_register_info_override( + &mut self, + ) -> Option> { + None + } + /// Support for reading the target's memory map. #[inline(always)] fn support_memory_map(&mut self) -> Option> { @@ -704,6 +728,10 @@ macro_rules! impl_dyn_target { (**self).use_target_description_xml() } + fn use_register_info(&self) -> bool { + (**self).use_register_info() + } + fn support_breakpoints( &mut self, ) -> Option> { @@ -734,6 +762,12 @@ macro_rules! impl_dyn_target { (**self).support_target_description_xml_override() } + fn support_register_info_override( + &mut self, + ) -> Option> { + (**self).support_register_info_override() + } + fn support_memory_map(&mut self) -> Option> { (**self).support_memory_map() }