blob: 97f7b2def3cc871248f2d72456d023cf89697ba1 [file] [log] [blame]
use core::convert::TryInto;
use armv4t_emu::{reg, Memory};
use gdbstub::common::Signal;
use gdbstub::target;
use gdbstub::target::ext::base::singlethread::{SingleThreadBase, SingleThreadResume};
use gdbstub::target::{Target, TargetError, TargetResult};
use gdbstub_arch::arm::reg::id::ArmCoreRegId;
use crate::emu::{Emu, ExecMode};
// Additional GDB extensions
mod auxv;
mod breakpoints;
mod catch_syscalls;
mod exec_file;
mod extended_mode;
mod host_io;
mod lldb_register_info_override;
mod memory_map;
mod monitor_cmd;
mod section_offsets;
mod target_description_xml_override;
/// Turn a `ArmCoreRegId` into an internal register number of `armv4t_emu`.
fn cpu_reg_id(id: ArmCoreRegId) -> Option<u8> {
match id {
ArmCoreRegId::Gpr(i) => Some(i),
ArmCoreRegId::Sp => Some(reg::SP),
ArmCoreRegId::Lr => Some(reg::LR),
ArmCoreRegId::Pc => Some(reg::PC),
ArmCoreRegId::Cpsr => Some(reg::CPSR),
_ => None,
}
}
/// Copy all bytes of `data` to `buf`.
/// Return the size of data copied.
pub fn copy_to_buf(data: &[u8], buf: &mut [u8]) -> usize {
let len = buf.len().min(data.len());
buf[..len].copy_from_slice(&data[..len]);
len
}
/// Copy a range of `data` (start at `offset` with a size of `length`) to `buf`.
/// Return the size of data copied. Returns 0 if `offset >= buf.len()`.
///
/// Mainly used by qXfer:_object_:read commands.
pub fn copy_range_to_buf(data: &[u8], offset: u64, length: usize, buf: &mut [u8]) -> usize {
let offset = offset as usize;
if offset > data.len() {
return 0;
}
let start = offset;
let end = (offset + length).min(data.len());
copy_to_buf(&data[start..end], buf)
}
impl Target for Emu {
// As an example, I've defined a custom architecture based off
// `gdbstub_arch::arm::Armv4t`. The implementation is in the `custom_arch`
// module at the bottom of this file.
//
// unless you're working with a particularly funky architecture that uses custom
// registers, you should probably stick to using the simple `target.xml`
// implementations from the `gdbstub_arch` repo (i.e: `target.xml` files that
// only specify the <architecture> and <feature>s of the arch, instead of
// listing out all the registers out manually).
type Arch = custom_arch::Armv4tCustom;
type Error = &'static str;
// --------------- IMPORTANT NOTE ---------------
// Always remember to annotate IDET enable methods with `inline(always)`!
// Without this annotation, LLVM might fail to dead-code-eliminate nested IDET
// implementations, resulting in unnecessary binary bloat.
#[inline(always)]
fn base_ops(&mut self) -> target::ext::base::BaseOps<'_, Self::Arch, Self::Error> {
target::ext::base::BaseOps::SingleThread(self)
}
#[inline(always)]
fn support_breakpoints(
&mut self,
) -> Option<target::ext::breakpoints::BreakpointsOps<'_, Self>> {
Some(self)
}
#[inline(always)]
fn support_extended_mode(
&mut self,
) -> Option<target::ext::extended_mode::ExtendedModeOps<'_, Self>> {
Some(self)
}
#[inline(always)]
fn support_monitor_cmd(&mut self) -> Option<target::ext::monitor_cmd::MonitorCmdOps<'_, Self>> {
Some(self)
}
#[inline(always)]
fn support_section_offsets(
&mut self,
) -> Option<target::ext::section_offsets::SectionOffsetsOps<'_, Self>> {
Some(self)
}
#[inline(always)]
fn support_target_description_xml_override(
&mut self,
) -> Option<
target::ext::target_description_xml_override::TargetDescriptionXmlOverrideOps<'_, Self>,
> {
Some(self)
}
#[inline(always)]
fn support_lldb_register_info_override(
&mut self,
) -> Option<target::ext::lldb_register_info_override::LldbRegisterInfoOverrideOps<'_, Self>>
{
Some(self)
}
#[inline(always)]
fn support_memory_map(&mut self) -> Option<target::ext::memory_map::MemoryMapOps<'_, Self>> {
Some(self)
}
#[inline(always)]
fn support_catch_syscalls(
&mut self,
) -> Option<target::ext::catch_syscalls::CatchSyscallsOps<'_, Self>> {
Some(self)
}
#[inline(always)]
fn support_host_io(&mut self) -> Option<target::ext::host_io::HostIoOps<'_, Self>> {
Some(self)
}
#[inline(always)]
fn support_exec_file(&mut self) -> Option<target::ext::exec_file::ExecFileOps<'_, Self>> {
Some(self)
}
#[inline(always)]
fn support_auxv(&mut self) -> Option<target::ext::auxv::AuxvOps<'_, Self>> {
Some(self)
}
}
impl SingleThreadBase for Emu {
fn read_registers(
&mut self,
regs: &mut custom_arch::ArmCoreRegsCustom,
) -> TargetResult<(), Self> {
let mode = self.cpu.mode();
for i in 0..13 {
regs.core.r[i] = self.cpu.reg_get(mode, i as u8);
}
regs.core.sp = self.cpu.reg_get(mode, reg::SP);
regs.core.lr = self.cpu.reg_get(mode, reg::LR);
regs.core.pc = self.cpu.reg_get(mode, reg::PC);
regs.core.cpsr = self.cpu.reg_get(mode, reg::CPSR);
regs.custom = self.custom_reg;
Ok(())
}
fn write_registers(&mut self, regs: &custom_arch::ArmCoreRegsCustom) -> TargetResult<(), Self> {
let mode = self.cpu.mode();
for i in 0..13 {
self.cpu.reg_set(mode, i, regs.core.r[i as usize]);
}
self.cpu.reg_set(mode, reg::SP, regs.core.sp);
self.cpu.reg_set(mode, reg::LR, regs.core.lr);
self.cpu.reg_set(mode, reg::PC, regs.core.pc);
self.cpu.reg_set(mode, reg::CPSR, regs.core.cpsr);
self.custom_reg = regs.custom;
Ok(())
}
#[inline(always)]
fn support_single_register_access(
&mut self,
) -> Option<target::ext::base::single_register_access::SingleRegisterAccessOps<'_, (), Self>>
{
Some(self)
}
fn read_addrs(&mut self, start_addr: u32, data: &mut [u8]) -> TargetResult<(), Self> {
for (addr, val) in (start_addr..).zip(data.iter_mut()) {
*val = self.mem.r8(addr)
}
Ok(())
}
fn write_addrs(&mut self, start_addr: u32, data: &[u8]) -> TargetResult<(), Self> {
for (addr, val) in (start_addr..).zip(data.iter().copied()) {
self.mem.w8(addr, val)
}
Ok(())
}
#[inline(always)]
fn support_resume(
&mut self,
) -> Option<target::ext::base::singlethread::SingleThreadResumeOps<'_, Self>> {
Some(self)
}
}
impl SingleThreadResume for Emu {
fn resume(&mut self, signal: Option<Signal>) -> Result<(), Self::Error> {
// Upon returning from the `resume` method, the target being debugged should be
// configured to run according to whatever resume actions the GDB client has
// specified (as specified by `set_resume_action`, `resume_range_step`,
// `reverse_{step, continue}`, etc...)
//
// In this basic `armv4t` example, the `resume` method simply sets the exec mode
// of the emulator's interpreter loop and returns.
//
// In more complex implementations, it's likely that the target being debugged
// will be running in another thread / process, and will require some kind of
// external "orchestration" to set it's execution mode (e.g: modifying the
// target's process state via platform specific debugging syscalls).
if signal.is_some() {
return Err("no support for continuing with signal");
}
self.exec_mode = ExecMode::Continue;
Ok(())
}
#[inline(always)]
fn support_reverse_cont(
&mut self,
) -> Option<target::ext::base::reverse_exec::ReverseContOps<'_, (), Self>> {
Some(self)
}
#[inline(always)]
fn support_reverse_step(
&mut self,
) -> Option<target::ext::base::reverse_exec::ReverseStepOps<'_, (), Self>> {
Some(self)
}
#[inline(always)]
fn support_single_step(
&mut self,
) -> Option<target::ext::base::singlethread::SingleThreadSingleStepOps<'_, Self>> {
Some(self)
}
#[inline(always)]
fn support_range_step(
&mut self,
) -> Option<target::ext::base::singlethread::SingleThreadRangeSteppingOps<'_, Self>> {
Some(self)
}
}
impl target::ext::base::singlethread::SingleThreadSingleStep for Emu {
fn step(&mut self, signal: Option<Signal>) -> Result<(), Self::Error> {
if signal.is_some() {
return Err("no support for stepping with signal");
}
self.exec_mode = ExecMode::Step;
Ok(())
}
}
impl target::ext::base::single_register_access::SingleRegisterAccess<()> for Emu {
fn read_register(
&mut self,
_tid: (),
reg_id: custom_arch::ArmCoreRegIdCustom,
buf: &mut [u8],
) -> TargetResult<usize, Self> {
match reg_id {
custom_arch::ArmCoreRegIdCustom::Core(reg_id) => {
if let Some(i) = cpu_reg_id(reg_id) {
let w = self.cpu.reg_get(self.cpu.mode(), i);
buf.copy_from_slice(&w.to_le_bytes());
Ok(buf.len())
} else {
Err(().into())
}
}
custom_arch::ArmCoreRegIdCustom::Custom => {
buf.copy_from_slice(&self.custom_reg.to_le_bytes());
Ok(buf.len())
}
custom_arch::ArmCoreRegIdCustom::Time => {
buf.copy_from_slice(
&(std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_millis() as u32)
.to_le_bytes(),
);
Ok(buf.len())
}
custom_arch::ArmCoreRegIdCustom::Unavailable => Ok(0),
}
}
fn write_register(
&mut self,
_tid: (),
reg_id: custom_arch::ArmCoreRegIdCustom,
val: &[u8],
) -> TargetResult<(), Self> {
let w = u32::from_le_bytes(
val.try_into()
.map_err(|_| TargetError::Fatal("invalid data"))?,
);
match reg_id {
custom_arch::ArmCoreRegIdCustom::Core(reg_id) => {
if let Some(i) = cpu_reg_id(reg_id) {
self.cpu.reg_set(self.cpu.mode(), i, w);
Ok(())
} else {
Err(().into())
}
}
custom_arch::ArmCoreRegIdCustom::Custom => {
self.custom_reg = w;
Ok(())
}
// ignore writes
custom_arch::ArmCoreRegIdCustom::Unavailable
| custom_arch::ArmCoreRegIdCustom::Time => Ok(()),
}
}
}
impl target::ext::base::reverse_exec::ReverseCont<()> for Emu {
fn reverse_cont(&mut self) -> Result<(), Self::Error> {
// FIXME: actually implement reverse step
eprintln!(
"FIXME: Not actually reverse-continuing. Performing forwards continue instead..."
);
self.exec_mode = ExecMode::Continue;
Ok(())
}
}
impl target::ext::base::reverse_exec::ReverseStep<()> for Emu {
fn reverse_step(&mut self, _tid: ()) -> Result<(), Self::Error> {
// FIXME: actually implement reverse step
eprintln!(
"FIXME: Not actually reverse-stepping. Performing single forwards step instead..."
);
self.exec_mode = ExecMode::Step;
Ok(())
}
}
impl target::ext::base::singlethread::SingleThreadRangeStepping for Emu {
fn resume_range_step(&mut self, start: u32, end: u32) -> Result<(), Self::Error> {
self.exec_mode = ExecMode::RangeStep(start, end);
Ok(())
}
}
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;
use gdbstub_arch::arm::reg::ArmCoreRegs;
use gdbstub_arch::arm::ArmBreakpointKind;
/// Implements `Arch` for ARMv4T
pub enum Armv4tCustom {}
#[derive(Debug, Default, Clone, Eq, PartialEq)]
pub struct ArmCoreRegsCustom {
pub core: ArmCoreRegs,
pub custom: u32,
}
impl Registers for ArmCoreRegsCustom {
type ProgramCounter = u32;
fn pc(&self) -> Self::ProgramCounter {
self.core.pc
}
fn gdb_serialize(&self, mut write_byte: impl FnMut(Option<u8>)) {
self.core.gdb_serialize(&mut write_byte);
macro_rules! write_bytes {
($bytes:expr) => {
for b in $bytes {
write_byte(Some(*b))
}
};
}
write_bytes!(&self.custom.to_le_bytes());
}
fn gdb_deserialize(&mut self, bytes: &[u8]) -> Result<(), ()> {
// ensure bytes.chunks_exact(4) won't panic
if bytes.len() % 4 != 0 {
return Err(());
}
use core::convert::TryInto;
let mut regs = bytes
.chunks_exact(4)
.map(|c| u32::from_le_bytes(c.try_into().unwrap()));
// copied from ArmCoreRegs
{
for reg in self.core.r.iter_mut() {
*reg = regs.next().ok_or(())?
}
self.core.sp = regs.next().ok_or(())?;
self.core.lr = regs.next().ok_or(())?;
self.core.pc = regs.next().ok_or(())?;
// Floating point registers (unused)
for _ in 0..25 {
regs.next().ok_or(())?;
}
self.core.cpsr = regs.next().ok_or(())?;
}
self.custom = regs.next().ok_or(())?;
if regs.next().is_some() {
return Err(());
}
Ok(())
}
}
#[derive(Debug)]
pub enum ArmCoreRegIdCustom {
Core(ArmCoreRegId),
Custom,
// not sent as part of `struct ArmCoreRegsCustom`, and only accessible via the single
// register read/write functions
Time,
/// This pseudo-register is valid but never available
Unavailable,
}
impl RegId for ArmCoreRegIdCustom {
fn from_raw_id(id: usize) -> Option<(Self, Option<NonZeroUsize>)> {
let reg = match id {
26 => Self::Custom,
27 => Self::Time,
28 => Self::Unavailable,
_ => {
let (reg, size) = ArmCoreRegId::from_raw_id(id)?;
return Some((Self::Core(reg), size));
}
};
Some((reg, Some(NonZeroUsize::new(4)?)))
}
}
impl Arch for Armv4tCustom {
type Usize = u32;
type Registers = ArmCoreRegsCustom;
type RegId = ArmCoreRegIdCustom;
type BreakpointKind = ArmBreakpointKind;
// for _purely demonstrative purposes_, i'll return dummy data from this
// function, as it will be overwritten by TargetDescriptionXmlOverride.
//
// See `examples/armv4t/gdb/target_description_xml_override.rs`
//
// in an actual implementation, you'll want to return an actual string here!
fn target_description_xml() -> Option<&'static str> {
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 lldb_register_info(reg_id: usize) -> Option<RegisterInfo<'static>> {
match ArmCoreRegIdCustom::from_raw_id(reg_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",
ArmCoreRegIdCustom::Unavailable => "Unavailable",
_ => "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::Unavailable
| 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::Unavailable
| 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::Unavailable
| 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: reg_id * (usize::from(size)),
encoding,
format,
set,
gcc: None,
dwarf: Some(reg_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
// optional single stepping.
#[inline(always)]
fn single_step_gdb_behavior() -> SingleStepGdbBehavior {
SingleStepGdbBehavior::Optional
}
}
}