From 731b88210d9a48a5efa404eb966f77eaa197e3bb Mon Sep 17 00:00:00 2001 From: wj Date: Wed, 1 Jun 2022 00:46:12 +0200 Subject: [PATCH] add qRegisterInfo packet support --- CHANGELOG.md | 4 ++ examples/armv4t/gdb/mod.rs | 9 +++ examples/armv4t/gdb/register_info.rs | 67 ++++++++++++++++++ src/protocol/commands.rs | 4 ++ src/protocol/commands/_qRegisterInfo.rs | 17 +++++ src/protocol/response_writer.rs | 17 ++++- src/stub/core_impl.rs | 2 + src/stub/core_impl/register_info.rs | 93 +++++++++++++++++++++++++ src/target/ext/mod.rs | 1 + src/target/ext/register_info.rs | 45 ++++++++++++ src/target/mod.rs | 13 ++++ 11 files changed, 271 insertions(+), 1 deletion(-) create mode 100644 examples/armv4t/gdb/register_info.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.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index ab1e028f..816faecc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +# 0.6.2 +- add LLDB-specific `qRegisterInfo` packet +- add `write_dec` method in the `ResponseWriter` as a convenient method to write base 10 numbers as ascii chars + # 0.6.1 - add LLDB-specific HostIoOpenFlags [\#100](https://github.com/daniel5151/gdbstub/pull/100) ([mrk](https://github.com/mrk-its)) diff --git a/examples/armv4t/gdb/mod.rs b/examples/armv4t/gdb/mod.rs index 155fff3e..02814162 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; mod section_offsets; mod target_description_xml_override; @@ -141,6 +142,14 @@ impl Target for Emu { fn support_auxv(&mut self) -> Option> { Some(self) } + + // LLDB only + #[inline(always)] + fn support_register_info( + &mut self, + ) -> Option> { + Some(self) + } } impl SingleThreadBase for Emu { diff --git a/examples/armv4t/gdb/register_info.rs b/examples/armv4t/gdb/register_info.rs new file mode 100644 index 00000000..f0fd4e73 --- /dev/null +++ b/examples/armv4t/gdb/register_info.rs @@ -0,0 +1,67 @@ +use gdbstub::target; +use gdbstub::target::ext::register_info::Register; + +use crate::gdb::Emu; + +// This implementation is for illustrative purposes only. If the .xml target description is used, the qRegisterInfo Packet is not necessary. (Note: This is an LLDB specific packet) + +// We have r0-pc from 0-16 but cpsr is at offset 25*4 in the 'g'/'G' packet, so we add 8 padding +// registers here. Please see gdbstub/examples/armv4t/gdb/target_description_xml_override.rs for +// more info. +const MAX_REG_NUM: u8 = 17 + 8; +const SP: u8 = 13; +const PC: u8 = 15; +const CPSR: u8 = 25; +const REG_NAMES: &[&str] = &[ + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "sp", "lr", + "pc", "padding", "padding", "padding", "padding", "padding", "padding", "padding", "padding", + "padding", "cpsr", +]; + +impl target::ext::register_info::RegisterInfo for Emu { + fn get_register_info(&mut self, reg_id: u8) -> Result, Self::Error> { + match reg_id { + 0..=MAX_REG_NUM => { + let name = REG_NAMES[reg_id as usize]; + let encoding = if reg_id < 16 || reg_id == CPSR { + "uint" + } else { + "vector" + }; + let format = if reg_id < 16 || reg_id == CPSR { + "hex" + } else { + "vector-uint8" + }; + let set = if reg_id < 16 || reg_id == CPSR { + "General Purpose Registers" + } else { + "Floating Point Registers" + }; + let generic = if reg_id == SP { + Some("sp") + } else if reg_id == PC { + Some("pc") + } else { + None + }; + Ok(Some(Register { + name, + alt_name: name, + bitsize: 32, + offset: (reg_id as u32) * 4, + encoding, + format, + set, + gcc: None, + dwarf: Some(reg_id as u32), + generic, + container_regs: None, + invalidate_regs: None, + })) + } + // Will end the 'qRegisterInfo' query + _ => Ok(None), + } + } +} diff --git a/src/protocol/commands.rs b/src/protocol/commands.rs index e96a1a59..88504a78 100644 --- a/src/protocol/commands.rs +++ b/src/protocol/commands.rs @@ -288,4 +288,8 @@ commands! { catch_syscalls use 'a { "QCatchSyscalls" => _QCatchSyscalls::QCatchSyscalls<'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..b671620d --- /dev/null +++ b/src/protocol/commands/_qRegisterInfo.rs @@ -0,0 +1,17 @@ +use super::prelude::*; + +#[derive(Debug)] +pub struct qRegisterInfo { + pub reg_id: u8, +} + +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..8233f769 100644 --- a/src/protocol/response_writer.rs +++ b/src/protocol/response_writer.rs @@ -114,7 +114,6 @@ impl<'a, C: Connection + 'a> ResponseWriter<'a, C> { if !self.rle_enabled { return self.inner_write(byte); } - const ASCII_FIRST_PRINT: u8 = b' '; const ASCII_LAST_PRINT: u8 = b'~'; @@ -163,6 +162,22 @@ 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: u32) -> Result<(), Error> { + let mut stack = Vec::new(); + if digit == 0 { + return self.write(b'0'); + } + while digit != 0 { + stack.push(char::from_digit(digit % 10, 10).unwrap() as u8); + digit /= 10; + } + for i in stack.into_iter().rev() { + self.write(i)?; + } + Ok(()) + } + /// Write a single byte as a hex string (two ascii chars) fn write_hex(&mut self, byte: u8) -> Result<(), Error> { for &digit in [(byte & 0xf0) >> 4, byte & 0x0f].iter() { diff --git a/src/stub/core_impl.rs b/src/stub/core_impl.rs index 82f3657b..9f14d9d4 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; @@ -207,6 +208,7 @@ impl GdbStubImpl { Command::HostIo(cmd) => self.handle_host_io(res, target, cmd), Command::ExecFile(cmd) => self.handle_exec_file(res, target, cmd), Command::Auxv(cmd) => self.handle_auxv(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..b29edc07 --- /dev/null +++ b/src/stub/core_impl/register_info.rs @@ -0,0 +1,93 @@ +use super::prelude::*; +use crate::protocol::commands::ext::RegisterInfo; + +impl GdbStubImpl { + pub(crate) fn handle_register_info( + &mut self, + res: &mut ResponseWriter<'_, C>, + target: &mut T, + command: RegisterInfo, + ) -> Result> { + let ops = match target.support_register_info() { + Some(ops) => ops, + None => return Ok(HandlerStatus::Handled), + }; + + crate::__dead_code_marker!("register_info", "impl"); + + let handler_status = match command { + RegisterInfo::qRegisterInfo(cmd) => { + use crate::target::ext::register_info::Register; + + match ops + .get_register_info(cmd.reg_id) + .map_err(Error::TargetError)? + { + Some(Register { + name, + alt_name, + bitsize, + offset, + encoding, + format, + set, + gcc, + dwarf, + generic, + container_regs, + invalidate_regs, + }) => { + res.write_str("name:")?; + res.write_str(name)?; + res.write_str(";alt-name:")?; + res.write_str(alt_name)?; + res.write_str(";bitsize:")?; + res.write_dec(bitsize)?; + res.write_str(";offset:")?; + res.write_dec(offset)?; + res.write_str(";encoding:")?; + res.write_str(encoding)?; + res.write_str(";format:")?; + res.write_str(format)?; + res.write_str(";set:")?; + res.write_str(set)?; + if let Some(gcc) = gcc { + res.write_str(";gcc:")?; + res.write_dec(gcc)?; + } + if let Some(dwarf) = dwarf { + res.write_str(";dwarf:")?; + res.write_dec(dwarf)?; + } + if let Some(generic) = generic { + res.write_str(";generic:")?; + res.write_str(generic)?; + } + if let Some(mut c_regs) = container_regs { + res.write_str(";container-regs:")?; + res.write_num(c_regs.pop().unwrap())?; + for c_reg in c_regs { + res.write_str(",")?; + res.write_num(c_reg)?; + } + } + if let Some(mut i_regs) = invalidate_regs { + res.write_str(";invalidate-regs:")?; + res.write_num(i_regs.pop().unwrap())?; + for i_reg in i_regs { + res.write_str(",")?; + res.write_num(i_reg)?; + } + } + res.write_str(";")?; + } + // This will end the 'qRegisterInfo' query + None => res.write_str("E45")?, + } + HandlerStatus::Handled + } + }; + + Ok(handler_status) + } +} diff --git a/src/target/ext/mod.rs b/src/target/ext/mod.rs index 81989360..1b9db77a 100644 --- a/src/target/ext/mod.rs +++ b/src/target/ext/mod.rs @@ -267,5 +267,6 @@ pub mod extended_mode; pub mod host_io; pub mod memory_map; pub mod monitor_cmd; +pub mod register_info; pub mod section_offsets; pub mod target_description_xml_override; diff --git a/src/target/ext/register_info.rs b/src/target/ext/register_info.rs new file mode 100644 index 00000000..33e6e80c --- /dev/null +++ b/src/target/ext/register_info.rs @@ -0,0 +1,45 @@ +//! Provide register information for the target. See https://github.com/llvm/llvm-project/blob/main/lldb/docs/lldb-gdb-remote.txt for more information. +//! +//! 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 if the packet 'qXfer:features:read' isn't used to provide a target description. The debugger will request the register info in a sequential manner till an 'E45' packet is received. +//! +//! _Note:_ LLDB specific! This packet is meant as an alternative to the 'qXfer:features:read:target.xml' packet. LLDB will fall back to the .xml file if qRegisterInfo isn't supported on the target. See issue [#99](https://github.com/daniel5151/gdbstub/issues/99) for more info on LLDB compatibility. +//! +use crate::target::Target; + +/// Describes the register info for a single register of the target +pub struct Register { + /// The primary register name + pub name: &'static str, + /// An alternate name for the register + pub alt_name: &'static str, + /// Size in bits of a register (base 10) + pub bitsize: u32, + /// The offset within the 'g' and 'G' packet of the register data for this register (base 10) + pub offset: u32, + /// The encoding type of the register + pub encoding: &'static str, + /// The preferred format for display of this register + pub format: &'static str, + /// The register set name this register belongs to + pub set: &'static str, + /// The GCC compiler registers number for this register + 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<&'static str>, + /// Other concrete register values this register is contained in + pub container_regs: Option>, + /// Specifies which register values should be invalidated when this register is modified + pub invalidate_regs: Option>, +} + +/// Target Extension - Get register information from the target. +/// +/// Corresponds to the `qRegisterInfo` command. +pub trait RegisterInfo: Target { + /// Return the target's register info for a single register or Ok(None) if is greater than the number of registers to end the request. + fn get_register_info(&mut self, reg_id: u8) -> Result, Self::Error>; +} + +define_ext!(RegisterInfoOps, RegisterInfo); diff --git a/src/target/mod.rs b/src/target/mod.rs index eaed4270..42f31606 100644 --- a/src/target/mod.rs +++ b/src/target/mod.rs @@ -665,6 +665,13 @@ pub trait Target { fn support_auxv(&mut self) -> Option> { None } + + /// Support for querying register info in LLDB. + /// Let the debugger request register info one by one. Should be used if 'use_target_description_xml()' is disabled. + #[inline(always)] + fn support_register_info(&mut self) -> Option> { + None + } } macro_rules! impl_dyn_target { @@ -755,6 +762,12 @@ macro_rules! impl_dyn_target { fn support_auxv(&mut self) -> Option> { (**self).support_auxv() } + + fn support_register_info( + &mut self, + ) -> Option> { + (**self).support_register_info() + } } }; }