Skip to content

Commit

Permalink
add qRegisterInfo packet support
Browse files Browse the repository at this point in the history
  • Loading branch information
jawilk committed Jun 8, 2022
1 parent 6fce31a commit 2689c37
Show file tree
Hide file tree
Showing 11 changed files with 656 additions and 2 deletions.
101 changes: 101 additions & 0 deletions examples/armv4t/gdb/mod.rs
Expand Up @@ -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;

Expand Down Expand Up @@ -115,6 +116,13 @@ impl Target for Emu {
Some(self)
}

#[inline(always)]
fn support_register_info_override(
&mut self,
) -> Option<target::ext::register_info_override::RegisterInfoOverrideOps<'_, Self>> {
Some(self)
}

#[inline(always)]
fn support_memory_map(&mut self) -> Option<target::ext::memory_map::MemoryMapOps<'_, Self>> {
Some(self)
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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<RegisterInfo<'static>> {
// 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
Expand Down
106 changes: 106 additions & 0 deletions 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<CallbackToken<'a>, 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))
}
}
}
}
147 changes: 147 additions & 0 deletions src/arch.rs
Expand Up @@ -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<lldb::RegisterInfo<'static>> {
let _ = reg_id;
None
}

/// Encode how the mainline GDB client handles target support for
/// single-step on this particular architecture.
///
Expand Down Expand Up @@ -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<usize>,
/// The DWARF register number for this register that is used for this
/// register in the debug information.
pub dwarf: Option<usize>,
/// Specify as a generic register.
pub generic: Option<Generic>,
/// 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,
}
}

0 comments on commit 2689c37

Please sign in to comment.