diff options
author | Joel Galenson <jgalenson@google.com> | 2021-08-03 21:23:32 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2021-08-03 21:23:32 +0000 |
commit | bd0fc1143e7ad56e6c189d21b1d5ab24006a8122 (patch) | |
tree | c39c3a7ddb6c612daf46e1921f2ca6e98e1f56b1 | |
parent | 861ca6705fbacabc9b281bd5b901be70b9125677 (diff) | |
parent | e54eca01fafc67f5e293bb7b4a3959e9313ba06b (diff) | |
download | gdbstub_arch-bd0fc1143e7ad56e6c189d21b1d5ab24006a8122.tar.gz |
Import gdbstub_arch-0.1.0 am: 52a992f950 am: e54eca01fa
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/gdbstub_arch/+/1783794
Change-Id: I4f8d69e99766caeeb819038fddc0db343d0a2b2a
-rw-r--r-- | .cargo_vcs_info.json | 5 | ||||
-rw-r--r-- | Cargo.toml | 32 | ||||
-rw-r--r-- | Cargo.toml.orig | 18 | ||||
-rw-r--r-- | README.md | 22 | ||||
-rw-r--r-- | src/arm/mod.rs | 45 | ||||
-rw-r--r-- | src/arm/reg/arm_core.rs | 82 | ||||
-rw-r--r-- | src/arm/reg/id.rs | 36 | ||||
-rw-r--r-- | src/arm/reg/mod.rs | 8 | ||||
-rw-r--r-- | src/lib.rs | 65 | ||||
-rw-r--r-- | src/mips/mod.rs | 110 | ||||
-rw-r--r-- | src/mips/reg/id.rs | 123 | ||||
-rw-r--r-- | src/mips/reg/mips.rs | 271 | ||||
-rw-r--r-- | src/mips/reg/mod.rs | 11 | ||||
-rw-r--r-- | src/msp430/mod.rs | 26 | ||||
-rw-r--r-- | src/msp430/reg/id.rs | 71 | ||||
-rw-r--r-- | src/msp430/reg/mod.rs | 8 | ||||
-rw-r--r-- | src/msp430/reg/msp430.rs | 71 | ||||
-rw-r--r-- | src/ppc/mod.rs | 28 | ||||
-rw-r--r-- | src/ppc/reg/common.rs | 166 | ||||
-rw-r--r-- | src/ppc/reg/id.rs | 2 | ||||
-rw-r--r-- | src/ppc/reg/mod.rs | 9 | ||||
-rw-r--r-- | src/riscv/mod.rs | 35 | ||||
-rw-r--r-- | src/riscv/reg/id.rs | 43 | ||||
-rw-r--r-- | src/riscv/reg/mod.rs | 8 | ||||
-rw-r--r-- | src/riscv/reg/riscv.rs | 77 | ||||
-rw-r--r-- | src/x86/mod.rs | 52 | ||||
-rw-r--r-- | src/x86/reg/core32.rs | 185 | ||||
-rw-r--r-- | src/x86/reg/core64.rs | 119 | ||||
-rw-r--r-- | src/x86/reg/id.rs | 227 | ||||
-rw-r--r-- | src/x86/reg/mod.rs | 155 |
30 files changed, 2110 insertions, 0 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json new file mode 100644 index 0000000..5653d83 --- /dev/null +++ b/.cargo_vcs_info.json @@ -0,0 +1,5 @@ +{ + "git": { + "sha1": "90aad2e136d15d486324f1985a398c02da982cdb" + } +} diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..f2e6592 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,32 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies +# +# If you believe there's an error in this file please file an +# issue against the rust-lang/cargo repository. If you're +# editing this file be aware that the upstream Cargo.toml +# will likely look very different (and much more reasonable) + +[package] +edition = "2018" +name = "gdbstub_arch" +version = "0.1.0" +authors = ["Daniel Prilik <danielprilik@gmail.com>"] +description = "Implementations of `gdbstub::arch::Arch` for various architectures." +homepage = "https://github.com/daniel5151/gdbstub" +documentation = "https://docs.rs/gdbstub_arch" +readme = "README.md" +keywords = ["gdb", "emulation", "no_std", "debugging"] +categories = ["development-tools::debugging", "embedded", "emulators", "no-std"] +license = "MIT" +repository = "https://github.com/daniel5151/gdbstub" +[dependencies.gdbstub] +version = "0.5" +default-features = false + +[dependencies.num-traits] +version = "0.2" +default-features = false diff --git a/Cargo.toml.orig b/Cargo.toml.orig new file mode 100644 index 0000000..d4b7ddb --- /dev/null +++ b/Cargo.toml.orig @@ -0,0 +1,18 @@ +[package] +name = "gdbstub_arch" +description = "Implementations of `gdbstub::arch::Arch` for various architectures." +authors = ["Daniel Prilik <danielprilik@gmail.com>"] +version = "0.1.0" +license = "MIT" +edition = "2018" +readme = "README.md" +documentation = "https://docs.rs/gdbstub_arch" +homepage = "https://github.com/daniel5151/gdbstub" +repository = "https://github.com/daniel5151/gdbstub" +keywords = ["gdb", "emulation", "no_std", "debugging"] +categories = ["development-tools::debugging", "embedded", "emulators", "no-std"] + +[dependencies] +gdbstub = { path = "../", version = "0.5", default-features = false } + +num-traits = { version = "0.2", default-features = false } diff --git a/README.md b/README.md new file mode 100644 index 0000000..92a2e6e --- /dev/null +++ b/README.md @@ -0,0 +1,22 @@ +# gdbstub_arch + +[![](http://meritbadge.herokuapp.com/gdbstub_arch)](https://crates.io/crates/gdbstub_arch) +[![](https://docs.rs/gdbstub_arch/badge.svg)](https://docs.rs/gdbstub_arch) + +Community-contributed implementations of `gdbstub::arch::Arch` for various +architectures. + +_Note:_ If an architecture is missing from this crate, that does _not_ mean +that it can't be used with `gdbstub`! So-long as there's support for the +target architecture in GDB, it should be fairly straightforward to implement +`Arch` manually. + +Please consider upstreaming any missing `Arch` implementations you happen to +implement yourself! Aside from the altruistic motive of improving `gdbstub`, +upstreaming your `Arch` implementation will ensure that it's kept up-to-date +with any future breaking API changes. + +**Disclaimer:** These implementations are all community contributions, and +while they are tested (by the PR's author) and code-reviewed, it's not +particularly feasible to write detailed tests for each architecture! If you +spot a bug in any of the implementations, please file an issue / open a PR! diff --git a/src/arm/mod.rs b/src/arm/mod.rs new file mode 100644 index 0000000..96fc404 --- /dev/null +++ b/src/arm/mod.rs @@ -0,0 +1,45 @@ +//! Implementations for various ARM architectures. + +use gdbstub::arch::Arch; + +pub mod reg; + +/// ARM-specific breakpoint kinds. +/// +/// Extracted from the GDB documentation at +/// [E.5.1.1 ARM Breakpoint Kinds](https://sourceware.org/gdb/current/onlinedocs/gdb/ARM-Breakpoint-Kinds.html#ARM-Breakpoint-Kinds) +#[derive(Debug)] +pub enum ArmBreakpointKind { + /// 16-bit Thumb mode breakpoint. + Thumb16, + /// 32-bit Thumb mode (Thumb-2) breakpoint. + Thumb32, + /// 32-bit ARM mode breakpoint. + Arm32, +} + +impl gdbstub::arch::BreakpointKind for ArmBreakpointKind { + fn from_usize(kind: usize) -> Option<Self> { + let kind = match kind { + 2 => ArmBreakpointKind::Thumb16, + 3 => ArmBreakpointKind::Thumb32, + 4 => ArmBreakpointKind::Arm32, + _ => return None, + }; + Some(kind) + } +} + +/// Implements `Arch` for ARMv4T +pub enum Armv4t {} + +impl Arch for Armv4t { + type Usize = u32; + type Registers = reg::ArmCoreRegs; + type RegId = reg::id::ArmCoreRegId; + type BreakpointKind = ArmBreakpointKind; + + fn target_description_xml() -> Option<&'static str> { + Some(r#"<target version="1.0"><architecture>armv4t</architecture></target>"#) + } +} diff --git a/src/arm/reg/arm_core.rs b/src/arm/reg/arm_core.rs new file mode 100644 index 0000000..179d590 --- /dev/null +++ b/src/arm/reg/arm_core.rs @@ -0,0 +1,82 @@ +use gdbstub::arch::Registers; + +/// 32-bit ARM core registers. +/// +/// Source: https://github.com/bminor/binutils-gdb/blob/master/gdb/features/arm/arm-core.xml +#[derive(Debug, Default, Clone, Eq, PartialEq)] +pub struct ArmCoreRegs { + /// General purpose registers (R0-R12) + pub r: [u32; 13], + /// Stack Pointer (R13) + pub sp: u32, + /// Link Register (R14) + pub lr: u32, + /// Program Counter (R15) + pub pc: u32, + /// Current Program Status Register (cpsr) + pub cpsr: u32, +} + +impl Registers for ArmCoreRegs { + type ProgramCounter = u32; + + fn pc(&self) -> Self::ProgramCounter { + self.pc + } + + fn gdb_serialize(&self, mut write_byte: impl FnMut(Option<u8>)) { + macro_rules! write_bytes { + ($bytes:expr) => { + for b in $bytes { + write_byte(Some(*b)) + } + }; + } + + for reg in self.r.iter() { + write_bytes!(®.to_le_bytes()); + } + write_bytes!(&self.sp.to_le_bytes()); + write_bytes!(&self.lr.to_le_bytes()); + write_bytes!(&self.pc.to_le_bytes()); + + // Floating point registers (unused) + for _ in 0..25 { + (0..4).for_each(|_| write_byte(None)) + } + + write_bytes!(&self.cpsr.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())); + + for reg in self.r.iter_mut() { + *reg = regs.next().ok_or(())? + } + self.sp = regs.next().ok_or(())?; + self.lr = regs.next().ok_or(())?; + self.pc = regs.next().ok_or(())?; + + // Floating point registers (unused) + for _ in 0..25 { + regs.next().ok_or(())?; + } + + self.cpsr = regs.next().ok_or(())?; + + if regs.next().is_some() { + return Err(()); + } + + Ok(()) + } +} diff --git a/src/arm/reg/id.rs b/src/arm/reg/id.rs new file mode 100644 index 0000000..c7e11a2 --- /dev/null +++ b/src/arm/reg/id.rs @@ -0,0 +1,36 @@ +use gdbstub::arch::RegId; + +/// 32-bit ARM core register identifier. +#[derive(Debug, Clone, Copy)] +#[non_exhaustive] +pub enum ArmCoreRegId { + /// General purpose registers (R0-R12) + Gpr(u8), + /// Stack Pointer (R13) + Sp, + /// Link Register (R14) + Lr, + /// Program Counter (R15) + Pc, + /// Floating point registers (F0-F7) + Fpr(u8), + /// Floating point status + Fps, + /// Current Program Status Register (cpsr) + Cpsr, +} + +impl RegId for ArmCoreRegId { + fn from_raw_id(id: usize) -> Option<(Self, usize)> { + let reg = match id { + 0..=12 => Self::Gpr(id as u8), + 13 => Self::Sp, + 14 => Self::Lr, + 15 => Self::Pc, + 16..=23 => Self::Fpr((id as u8) - 16), + 25 => Self::Cpsr, + _ => return None, + }; + Some((reg, 4)) + } +} diff --git a/src/arm/reg/mod.rs b/src/arm/reg/mod.rs new file mode 100644 index 0000000..8f5a8be --- /dev/null +++ b/src/arm/reg/mod.rs @@ -0,0 +1,8 @@ +//! `Register` structs for various ARM architectures. + +/// `RegId` definitions for ARM architectures. +pub mod id; + +mod arm_core; + +pub use arm_core::ArmCoreRegs; diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..ccf9b2d --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,65 @@ +//! Community-created implementations of [`gdbstub::arch::Arch`] for various +//! architectures. +//! +//! _Note:_ If an architecture is missing from this crate, that does _not_ mean +//! that it can't be used with `gdbstub`! So-long as there's support for the +//! target architecture in GDB, it should be fairly straightforward to implement +//! `Arch` manually. +//! +//! Please consider upstreaming any missing `Arch` implementations you happen to +//! implement yourself! Aside from the altruistic motive of improving `gdbstub`, +//! upstreaming your `Arch` implementation will ensure that it's kept up-to-date +//! with any future breaking API changes. +//! +//! **Disclaimer:** These implementations are all community contributions, and +//! while they are tested (by the PR's author) and code-reviewed, it's not +//! particularly feasible to write detailed tests for each architecture! If you +//! spot a bug in any of the implementations, please file an issue / open a PR! +//! +//! # What's with `RegIdImpl`? +//! +//! Supporting the `Target::read/write_register` API required introducing a new +//! [`RegId`] trait + [`Arch::RegId`] associated type. `RegId` is used by +//! `gdbstub` to translate raw GDB register ids (a protocol level arch-dependent +//! `usize`) into human-readable enum variants. +//! +//! Unfortunately, this API was added after several contributors had already +//! upstreamed their `Arch` implementations, and as a result, there are several +//! built-in arch implementations which are missing proper `RegId` enums +//! (tracked under [issue #29](https://github.com/daniel5151/gdbstub/issues/29)). +//! +//! As a stop-gap measure, affected `Arch` implementations have been modified to +//! accept a `RegIdImpl` type parameter, which requires users to manually +//! specify a `RegId` implementation. +//! +//! If you're not interested in implementing the `Target::read/write_register` +//! methods and just want to get up-and-running with `gdbstub`, it's fine to +//! set `RegIdImpl` to `()` and use the built-in stubbed `impl RegId for ()`. +//! +//! A better approach would be to implement (and hopefully upstream!) a proper +//! `RegId` enum. While this will require doing a bit of digging through the GDB +//! docs + [architecture XML definitions](https://github.com/bminor/binutils-gdb/tree/master/gdb/features/), +//! it's not too tricky to get a working implementation up and running, and +//! makes it possible to safely and efficiently implement the +//! `Target::read/write_register` API. As an example, check out +//! [`ArmCoreRegId`](arm::reg::id::ArmCoreRegId#impl-RegId). +//! +//! Whenever a `RegId` enum is upstreamed, the associated `Arch`'s `RegIdImpl` +//! parameter will be defaulted to the newly added enum. This will simplify the +//! API without requiring an explicit breaking API change. Once all `RegIdImpl` +//! have a default implementation, only a single breaking API change will be +//! required to remove `RegIdImpl` entirely (along with this documentation). + +#![cfg_attr(not(test), no_std)] +#![deny(missing_docs)] + +pub mod arm; +pub mod mips; +pub mod msp430; +pub mod ppc; +pub mod riscv; +pub mod x86; + +// used as part of intra-doc link +#[allow(unused_imports)] +use gdbstub::arch::*; diff --git a/src/mips/mod.rs b/src/mips/mod.rs new file mode 100644 index 0000000..7784fe8 --- /dev/null +++ b/src/mips/mod.rs @@ -0,0 +1,110 @@ +//! Implementations for the MIPS architecture. + +use gdbstub::arch::Arch; +use gdbstub::arch::RegId; + +pub mod reg; + +/// MIPS-specific breakpoint kinds. +/// +/// Extracted from the GDB documentation at +/// [E.5.1.1 MIPS Breakpoint Kinds](https://sourceware.org/gdb/current/onlinedocs/gdb/MIPS-Breakpoint-Kinds.html#MIPS-Breakpoint-Kinds) +#[derive(Debug)] +pub enum MipsBreakpointKind { + /// 16-bit MIPS16 mode breakpoint. + Mips16, + + /// 16-bit microMIPS mode breakpoint. + MicroMips16, + + /// 32-bit standard MIPS mode breakpoint. + Mips32, + + /// 32-bit microMIPS mode breakpoint. + MicroMips32, +} + +impl gdbstub::arch::BreakpointKind for MipsBreakpointKind { + fn from_usize(kind: usize) -> Option<Self> { + let kind = match kind { + 2 => MipsBreakpointKind::Mips16, + 3 => MipsBreakpointKind::MicroMips16, + 4 => MipsBreakpointKind::Mips32, + 5 => MipsBreakpointKind::MicroMips32, + _ => return None, + }; + Some(kind) + } +} + +/// Implements `Arch` for 32-bit MIPS. +/// +/// Check out the [module level docs](gdbstub::arch#whats-with-regidimpl) for +/// more info about the `RegIdImpl` type parameter. +pub enum Mips<RegIdImpl: RegId = reg::id::MipsRegId<u32>> { + #[doc(hidden)] + _Marker(core::marker::PhantomData<RegIdImpl>), +} + +/// Implements `Arch` for 64-bit MIPS. +/// +/// Check out the [module level docs](gdbstub::arch#whats-with-regidimpl) for +/// more info about the `RegIdImpl` type parameter. +pub enum Mips64<RegIdImpl: RegId = reg::id::MipsRegId<u64>> { + #[doc(hidden)] + _Marker(core::marker::PhantomData<RegIdImpl>), +} + +/// Implements `Arch` for 32-bit MIPS with the DSP feature enabled. +pub enum MipsWithDsp {} + +/// Implements `Arch` for 64-bit MIPS with the DSP feature enabled. +pub enum Mips64WithDsp {} + +impl<RegIdImpl: RegId> Arch for Mips<RegIdImpl> { + type Usize = u32; + type Registers = reg::MipsCoreRegs<u32>; + type RegId = RegIdImpl; + type BreakpointKind = MipsBreakpointKind; + + fn target_description_xml() -> Option<&'static str> { + Some(r#"<target version="1.0"><architecture>mips</architecture></target>"#) + } +} + +impl<RegIdImpl: RegId> Arch for Mips64<RegIdImpl> { + type Usize = u64; + type Registers = reg::MipsCoreRegs<u64>; + type RegId = RegIdImpl; + type BreakpointKind = MipsBreakpointKind; + + fn target_description_xml() -> Option<&'static str> { + Some(r#"<target version="1.0"><architecture>mips64</architecture></target>"#) + } +} + +impl Arch for MipsWithDsp { + type Usize = u32; + type Registers = reg::MipsCoreRegsWithDsp<u32>; + type RegId = reg::id::MipsRegId<u32>; + type BreakpointKind = MipsBreakpointKind; + + fn target_description_xml() -> Option<&'static str> { + Some( + r#"<target version="1.0"><architecture>mips</architecture><feature name="org.gnu.gdb.mips.dsp"></feature></target>"#, + ) + } +} + +impl Arch for Mips64WithDsp { + type Usize = u64; + type Registers = reg::MipsCoreRegsWithDsp<u64>; + type RegId = reg::id::MipsRegId<u64>; + type BreakpointKind = MipsBreakpointKind; + + fn target_description_xml() -> Option<&'static str> { + Some( + r#"<target version="1.0"><architecture>mips64</architecture><feature name="org.gnu.gdb.mips.dsp"></feature></target>"#, + ) + } +} diff --git a/src/mips/reg/id.rs b/src/mips/reg/id.rs new file mode 100644 index 0000000..57665c6 --- /dev/null +++ b/src/mips/reg/id.rs @@ -0,0 +1,123 @@ +use gdbstub::arch::RegId; + +/// MIPS register identifier. +#[derive(Debug, Clone, Copy)] +#[non_exhaustive] +pub enum MipsRegId<U> { + /// General purpose registers (R0-R31) + Gpr(u8), + /// Status register + Status, + /// Low register + Lo, + /// High register + Hi, + /// Bad Virtual Address register + Badvaddr, + /// Exception Cause register + Cause, + /// Program Counter + Pc, + /// Floating point registers (F0-F31) + Fpr(u8), + /// Floating-point Control Status register + Fcsr, + /// Floating-point Implementation Register + Fir, + /// High 1 register + Hi1, + /// Low 1 register + Lo1, + /// High 2 register + Hi2, + /// Low 2 register + Lo2, + /// High 3 register + Hi3, + /// Low 3 register + Lo3, + /// DSP Control register + Dspctl, + /// Restart register + Restart, + #[doc(hidden)] + _Size(U), +} + +fn from_raw_id<U>(id: usize) -> Option<(MipsRegId<U>, usize)> { + let reg = match id { + 0..=31 => MipsRegId::Gpr(id as u8), + 32 => MipsRegId::Status, + 33 => MipsRegId::Lo, + 34 => MipsRegId::Hi, + 35 => MipsRegId::Badvaddr, + 36 => MipsRegId::Cause, + 37 => MipsRegId::Pc, + 38..=69 => MipsRegId::Fpr((id as u8) - 38), + 70 => MipsRegId::Fcsr, + 71 => MipsRegId::Fir, + 72 => MipsRegId::Hi1, + 73 => MipsRegId::Lo1, + 74 => MipsRegId::Hi2, + 75 => MipsRegId::Lo2, + 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)), + 79 => MipsRegId::Restart, + _ => return None, + }; + + let ptrsize = core::mem::size_of::<U>(); + Some((reg, ptrsize)) +} + +impl RegId for MipsRegId<u32> { + fn from_raw_id(id: usize) -> Option<(Self, usize)> { + from_raw_id::<u32>(id) + } +} + +impl RegId for MipsRegId<u64> { + fn from_raw_id(id: usize) -> Option<(Self, usize)> { + from_raw_id::<u64>(id) + } +} + +#[cfg(test)] +mod tests { + use gdbstub::arch::RegId; + use gdbstub::arch::Registers; + + fn test<Rs: Registers, RId: RegId>() { + // Obtain the data length written by `gdb_serialize` by passing a custom + // closure. + let mut serialized_data_len = 0; + let counter = |b: Option<u8>| { + if b.is_some() { + serialized_data_len += 1; + } + }; + Rs::default().gdb_serialize(counter); + + // Accumulate register sizes returned by `from_raw_id`. + let mut i = 0; + let mut sum_reg_sizes = 0; + while let Some((_, size)) = RId::from_raw_id(i) { + sum_reg_sizes += size; + i += 1; + } + + assert_eq!(serialized_data_len, sum_reg_sizes); + } + + #[test] + fn test_mips32() { + test::<crate::mips::reg::MipsCoreRegsWithDsp<u32>, crate::mips::reg::id::MipsRegId<u32>>() + } + + #[test] + fn test_mips64() { + test::<crate::mips::reg::MipsCoreRegsWithDsp<u64>, crate::mips::reg::id::MipsRegId<u64>>() + } +} diff --git a/src/mips/reg/mips.rs b/src/mips/reg/mips.rs new file mode 100644 index 0000000..be14e33 --- /dev/null +++ b/src/mips/reg/mips.rs @@ -0,0 +1,271 @@ +use core::convert::TryInto; + +use num_traits::PrimInt; + +use gdbstub::arch::Registers; +use gdbstub::internal::LeBytes; + +/// MIPS registers. +/// +/// The register width is set to `u32` or `u64` based on the `<U>` type. +/// +/// Source: https://github.com/bminor/binutils-gdb/blob/master/gdb/features/mips-cpu.xml +#[derive(Debug, Default, Clone, Eq, PartialEq)] +pub struct MipsCoreRegs<U> { + /// General purpose registers (R0-R31) + pub r: [U; 32], + /// Low register (regnum 33) + pub lo: U, + /// High register (regnum 34) + pub hi: U, + /// Program Counter (regnum 37) + pub pc: U, + /// CP0 registers + pub cp0: MipsCp0Regs<U>, + /// FPU registers + pub fpu: MipsFpuRegs<U>, +} + +/// MIPS CP0 (coprocessor 0) registers. +/// +/// Source: https://github.com/bminor/binutils-gdb/blob/master/gdb/features/mips-cp0.xml +#[derive(Debug, Default, Clone, Eq, PartialEq)] +pub struct MipsCp0Regs<U> { + /// Status register (regnum 32) + pub status: U, + /// Bad Virtual Address register (regnum 35) + pub badvaddr: U, + /// Exception Cause register (regnum 36) + pub cause: U, +} + +/// MIPS FPU registers. +/// +/// Source: https://github.com/bminor/binutils-gdb/blob/master/gdb/features/mips-fpu.xml +#[derive(Debug, Default, Clone, Eq, PartialEq)] +pub struct MipsFpuRegs<U> { + /// FP registers (F0-F31) starting at regnum 38 + pub r: [U; 32], + /// Floating-point Control Status register + pub fcsr: U, + /// Floating-point Implementation Register + pub fir: U, +} + +/// MIPS DSP registers. +/// +/// Source: https://github.com/bminor/binutils-gdb/blob/master/gdb/features/mips-dsp.xml +#[derive(Debug, Default, Clone, Eq, PartialEq)] +pub struct MipsDspRegs<U> { + /// High 1 register (regnum 72) + pub hi1: U, + /// Low 1 register (regnum 73) + pub lo1: U, + /// High 2 register (regnum 74) + pub hi2: U, + /// Low 2 register (regnum 75) + pub lo2: U, + /// High 3 register (regnum 76) + pub hi3: U, + /// Low 3 register (regnum 77) + pub lo3: U, + /// DSP Control register (regnum 78) + /// Note: This register will always be 32-bit regardless of the target + /// https://sourceware.org/gdb/current/onlinedocs/gdb/MIPS-Features.html#MIPS-Features + pub dspctl: u32, + /// Restart register (regnum 79) + pub restart: U, +} + +/// MIPS core and DSP registers. +/// +/// Source: https://github.com/bminor/binutils-gdb/blob/master/gdb/features/mips-dsp-linux.xml +#[derive(Debug, Default, Clone, Eq, PartialEq)] +pub struct MipsCoreRegsWithDsp<U> { + /// Core registers + pub core: MipsCoreRegs<U>, + /// DSP registers + pub dsp: MipsDspRegs<U>, +} + +impl<U> Registers for MipsCoreRegs<U> +where + U: PrimInt + LeBytes + Default + core::fmt::Debug, +{ + type ProgramCounter = U; + + fn pc(&self) -> Self::ProgramCounter { + self.pc + } + + fn gdb_serialize(&self, mut write_byte: impl FnMut(Option<u8>)) { + macro_rules! write_le_bytes { + ($value:expr) => { + let mut buf = [0; 16]; + // infallible (unless digit is a >128 bit number) + let len = $value.to_le_bytes(&mut buf).unwrap(); + let buf = &buf[..len]; + for b in buf { + write_byte(Some(*b)); + } + }; + } + + // Write GPRs + for reg in self.r.iter() { + write_le_bytes!(reg); + } + + // Status register is regnum 32 + write_le_bytes!(&self.cp0.status); + + // Low and high registers are regnums 33 and 34 + write_le_bytes!(&self.lo); + write_le_bytes!(&self.hi); + + // Badvaddr and Cause registers are regnums 35 and 36 + write_le_bytes!(&self.cp0.badvaddr); + write_le_bytes!(&self.cp0.cause); + + // Program Counter is regnum 37 + write_le_bytes!(&self.pc); + + // Write FPRs + for reg in self.fpu.r.iter() { + write_le_bytes!(®); + } + + // Write FCSR and FIR registers + write_le_bytes!(&self.fpu.fcsr); + write_le_bytes!(&self.fpu.fir); + } + + fn gdb_deserialize(&mut self, bytes: &[u8]) -> Result<(), ()> { + let ptrsize = core::mem::size_of::<U>(); + + // Ensure bytes contains enough data for all 72 registers + if bytes.len() < ptrsize * 72 { + return Err(()); + } + + // All core registers are the same size + let mut regs = bytes + .chunks_exact(ptrsize) + .map(|c| U::from_le_bytes(c).unwrap()); + + // Read GPRs + for reg in self.r.iter_mut() { + *reg = regs.next().ok_or(())? + } + + // Read Status register + self.cp0.status = regs.next().ok_or(())?; + + // Read Low and High registers + self.lo = regs.next().ok_or(())?; + self.hi = regs.next().ok_or(())?; + + // Read Badvaddr and Cause registers + self.cp0.badvaddr = regs.next().ok_or(())?; + self.cp0.cause = regs.next().ok_or(())?; + + // Read the Program Counter + self.pc = regs.next().ok_or(())?; + + // Read FPRs + for reg in self.fpu.r.iter_mut() { + *reg = regs.next().ok_or(())? + } + + // Read FCSR and FIR registers + self.fpu.fcsr = regs.next().ok_or(())?; + self.fpu.fir = regs.next().ok_or(())?; + + Ok(()) + } +} + +impl<U> Registers for MipsCoreRegsWithDsp<U> +where + U: PrimInt + LeBytes + Default + core::fmt::Debug, +{ + type ProgramCounter = U; + + fn pc(&self) -> Self::ProgramCounter { + self.core.pc + } + + fn gdb_serialize(&self, mut write_byte: impl FnMut(Option<u8>)) { + macro_rules! write_le_bytes { + ($value:expr) => { + let mut buf = [0; 16]; + // infallible (unless digit is a >128 bit number) + let len = $value.to_le_bytes(&mut buf).unwrap(); + let buf = &buf[..len]; + for b in buf { + write_byte(Some(*b)); + } + }; + } + + // Serialize the core registers first + self.core.gdb_serialize(&mut write_byte); + + // Write the DSP registers + write_le_bytes!(&self.dsp.hi1); + write_le_bytes!(&self.dsp.lo1); + write_le_bytes!(&self.dsp.hi2); + write_le_bytes!(&self.dsp.lo2); + write_le_bytes!(&self.dsp.hi3); + write_le_bytes!(&self.dsp.lo3); + + for b in &self.dsp.dspctl.to_le_bytes() { + write_byte(Some(*b)); + } + + write_le_bytes!(&self.dsp.restart); + } + + fn gdb_deserialize(&mut self, bytes: &[u8]) -> Result<(), ()> { + // Deserialize the core registers first + self.core.gdb_deserialize(bytes)?; + + // Ensure bytes contains enough data for all 79 registers of target-width + // and the dspctl register which is always 4 bytes + let ptrsize = core::mem::size_of::<U>(); + if bytes.len() < (ptrsize * 79) + 4 { + return Err(()); + } + + // Calculate the offsets to the DSP registers based on the ptrsize + let dspregs_start = ptrsize * 72; + let dspctl_start = ptrsize * 78; + + // Read up until the dspctl register + let mut regs = bytes[dspregs_start..dspctl_start] + .chunks_exact(ptrsize) + .map(|c| U::from_le_bytes(c).unwrap()); + + self.dsp.hi1 = regs.next().ok_or(())?; + self.dsp.lo1 = regs.next().ok_or(())?; + self.dsp.hi2 = regs.next().ok_or(())?; + self.dsp.lo2 = regs.next().ok_or(())?; + self.dsp.hi3 = regs.next().ok_or(())?; + self.dsp.lo3 = regs.next().ok_or(())?; + + // Dspctl will always be a u32 + self.dsp.dspctl = + u32::from_le_bytes(bytes[dspctl_start..dspctl_start + 4].try_into().unwrap()); + + // Only 4 or 8 bytes should remain to be read + self.dsp.restart = U::from_le_bytes( + bytes[dspctl_start + 4..] + .chunks_exact(ptrsize) + .next() + .ok_or(())?, + ) + .unwrap(); + + Ok(()) + } +} diff --git a/src/mips/reg/mod.rs b/src/mips/reg/mod.rs new file mode 100644 index 0000000..3cafbd1 --- /dev/null +++ b/src/mips/reg/mod.rs @@ -0,0 +1,11 @@ +//! `Register` structs for MIPS architectures. + +/// `RegId` definitions for MIPS architectures. +pub mod id; + +mod mips; + +pub use mips::MipsCoreRegs; +pub use mips::MipsCoreRegsWithDsp; +pub use mips::MipsCp0Regs; +pub use mips::MipsFpuRegs; diff --git a/src/msp430/mod.rs b/src/msp430/mod.rs new file mode 100644 index 0000000..456ce18 --- /dev/null +++ b/src/msp430/mod.rs @@ -0,0 +1,26 @@ +//! Implementations for the TI-MSP430 family of MCUs. + +use gdbstub::arch::Arch; +use gdbstub::arch::RegId; + +pub mod reg; + +/// Implements `Arch` for standard 16-bit TI-MSP430 MCUs. +/// +/// Check out the [module level docs](gdbstub::arch#whats-with-regidimpl) for +/// more info about the `RegIdImpl` type parameter. +pub enum Msp430<RegIdImpl: RegId = reg::id::Msp430RegId> { + #[doc(hidden)] + _Marker(core::marker::PhantomData<RegIdImpl>), +} + +impl<RegIdImpl: RegId> Arch for Msp430<RegIdImpl> { + type Usize = u16; + type Registers = reg::Msp430Regs; + type RegId = RegIdImpl; + type BreakpointKind = usize; + + fn target_description_xml() -> Option<&'static str> { + Some(r#"<target version="1.0"><architecture>msp430</architecture></target>"#) + } +} diff --git a/src/msp430/reg/id.rs b/src/msp430/reg/id.rs new file mode 100644 index 0000000..1c38763 --- /dev/null +++ b/src/msp430/reg/id.rs @@ -0,0 +1,71 @@ +use gdbstub::arch::RegId; + +/// TI-MSP430 register identifier. +/// +/// GDB does not provide a XML file for the MSP430. +/// The best file to reference is [msp430-tdep.c](https://github.com/bminor/binutils-gdb/blob/master/gdb/msp430-tdep.c). +#[derive(Debug, Clone, Copy)] +#[non_exhaustive] +pub enum Msp430RegId { + /// Program Counter (R0) + Pc, + /// Stack Pointer (R1) + Sp, + /// Status Register (R2) + Sr, + /// Constant Generator (R3) + Cg, + /// General Purpose Registers (R4-R15) + Gpr(u8), +} + +impl RegId for Msp430RegId { + fn from_raw_id(id: usize) -> Option<(Self, usize)> { + let reg = match id { + 0 => Self::Pc, + 1 => Self::Sp, + 2 => Self::Sr, + 3 => Self::Cg, + 4..=15 => Self::Gpr((id as u8) - 4), + _ => return None, + }; + Some((reg, 2)) + } +} + +#[cfg(test)] +mod tests { + use gdbstub::arch::RegId; + use gdbstub::arch::Registers; + + fn test<Rs: Registers, RId: RegId>() { + // Obtain the data length written by `gdb_serialize` by passing a custom + // closure. + let mut serialized_data_len = 0; + let counter = |b: Option<u8>| { + if b.is_some() { + serialized_data_len += 1; + } + }; + Rs::default().gdb_serialize(counter); + + // The `Msp430Regs` implementation does not increment the size for + // the CG register since it will always be the constant zero. + serialized_data_len += 4; + + // Accumulate register sizes returned by `from_raw_id`. + let mut i = 0; + let mut sum_reg_sizes = 0; + while let Some((_, size)) = RId::from_raw_id(i) { + sum_reg_sizes += size; + i += 1; + } + + assert_eq!(serialized_data_len, sum_reg_sizes); + } + + #[test] + fn test_msp430() { + test::<crate::msp430::reg::Msp430Regs, crate::msp430::reg::id::Msp430RegId>() + } +} diff --git a/src/msp430/reg/mod.rs b/src/msp430/reg/mod.rs new file mode 100644 index 0000000..2b1285b --- /dev/null +++ b/src/msp430/reg/mod.rs @@ -0,0 +1,8 @@ +//! `Register` structs for various TI-MSP430 CPUs. + +/// `RegId` definitions for various TI-MSP430 CPUs. +pub mod id; + +mod msp430; + +pub use msp430::Msp430Regs; diff --git a/src/msp430/reg/msp430.rs b/src/msp430/reg/msp430.rs new file mode 100644 index 0000000..652fc1b --- /dev/null +++ b/src/msp430/reg/msp430.rs @@ -0,0 +1,71 @@ +use gdbstub::arch::Registers; + +/// 16-bit TI-MSP430 registers. +#[derive(Debug, Default, Clone, Eq, PartialEq)] +pub struct Msp430Regs { + /// Program Counter (R0) + pub pc: u16, + /// Stack Pointer (R1) + pub sp: u16, + /// Status Register (R2) + pub sr: u16, + /// General Purpose Registers (R4-R15) + pub r: [u16; 11], +} + +impl Registers for Msp430Regs { + type ProgramCounter = u16; + + fn pc(&self) -> Self::ProgramCounter { + self.pc + } + + fn gdb_serialize(&self, mut write_byte: impl FnMut(Option<u8>)) { + macro_rules! write_bytes { + ($bytes:expr) => { + for b in $bytes { + write_byte(Some(*b)) + } + }; + } + + write_bytes!(&self.pc.to_le_bytes()); + write_bytes!(&self.sp.to_le_bytes()); + write_bytes!(&self.sr.to_le_bytes()); + (0..4).for_each(|_| write_byte(None)); // Constant Generator (CG/R3) + for reg in self.r.iter() { + write_bytes!(®.to_le_bytes()); + } + } + + fn gdb_deserialize(&mut self, bytes: &[u8]) -> Result<(), ()> { + // ensure bytes.chunks_exact(2) won't panic + if bytes.len() % 2 != 0 { + return Err(()); + } + + use core::convert::TryInto; + let mut regs = bytes + .chunks_exact(2) + .map(|c| u16::from_le_bytes(c.try_into().unwrap())); + + self.pc = regs.next().ok_or(())?; + self.sp = regs.next().ok_or(())?; + self.sr = regs.next().ok_or(())?; + + // Constant Generator (CG/R3) should always be 0 + if regs.next().ok_or(())? != 0 { + return Err(()); + } + + for reg in self.r.iter_mut() { + *reg = regs.next().ok_or(())? + } + + if regs.next().is_some() { + return Err(()); + } + + Ok(()) + } +} diff --git a/src/ppc/mod.rs b/src/ppc/mod.rs new file mode 100644 index 0000000..60beed3 --- /dev/null +++ b/src/ppc/mod.rs @@ -0,0 +1,28 @@ +//! Implementations for various PowerPC architectures. + +use gdbstub::arch::Arch; +use gdbstub::arch::RegId; + +pub mod reg; + +/// Implements `Arch` for 32-bit PowerPC + AltiVec SIMD. +/// +/// Check out the [module level docs](gdbstub::arch#whats-with-regidimpl) for +/// more info about the `RegIdImpl` type parameter. +pub enum PowerPcAltivec32<RegIdImpl: RegId> { + #[doc(hidden)] + _Marker(core::marker::PhantomData<RegIdImpl>), +} + +impl<RegIdImpl: RegId> Arch for PowerPcAltivec32<RegIdImpl> { + type Usize = u32; + type Registers = reg::PowerPcCommonRegs; + type RegId = RegIdImpl; + type BreakpointKind = usize; + + fn target_description_xml() -> Option<&'static str> { + Some( + r#"<target version="1.0"><architecture>powerpc:common</architecture><feature name="org.gnu.gdb.power.core"></feature><feature name="org.gnu.gdb.power.fpu"></feature><feature name="org.gnu.gdb.power.altivec"></feature></target>"#, + ) + } +} diff --git a/src/ppc/reg/common.rs b/src/ppc/reg/common.rs new file mode 100644 index 0000000..45bdb1c --- /dev/null +++ b/src/ppc/reg/common.rs @@ -0,0 +1,166 @@ +use gdbstub::arch::Registers; + +use super::PpcVector; + +use core::convert::TryInto; + +/// 32-bit PowerPC core registers, FPU registers, and AltiVec SIMD registers. +/// +/// Sources: +/// * https://github.com/bminor/binutils-gdb/blob/master/gdb/features/rs6000/powerpc-altivec32.xml +/// * https://github.com/bminor/binutils-gdb/blob/master/gdb/features/rs6000/power-core.xml +/// * https://github.com/bminor/binutils-gdb/blob/master/gdb/features/rs6000/power-fpu.xml +/// * https://github.com/bminor/binutils-gdb/blob/master/gdb/features/rs6000/power-altivec.xml +#[derive(Debug, Default, Clone, PartialEq)] +pub struct PowerPcCommonRegs { + /// General purpose registers + pub r: [u32; 32], + /// Floating Point registers + pub f: [f64; 32], + /// Program counter + pub pc: u32, + /// Machine state + pub msr: u32, + /// Condition register + pub cr: u32, + /// Link register + pub lr: u32, + /// Count register + pub ctr: u32, + /// Integer exception register + pub xer: u32, + /// Floating-point status and control register + pub fpscr: u32, + /// Vector registers + pub vr: [PpcVector; 32], + /// Vector status and control register + pub vscr: u32, + /// Vector context save register + pub vrsave: u32, +} + +impl Registers for PowerPcCommonRegs { + type ProgramCounter = u32; + + fn pc(&self) -> Self::ProgramCounter { + self.pc + } + + fn gdb_serialize(&self, mut write_byte: impl FnMut(Option<u8>)) { + macro_rules! write_bytes { + ($bytes:expr) => { + for b in $bytes { + write_byte(Some(*b)) + } + }; + } + + macro_rules! write_regs { + ($($reg:ident),*) => { + $( + write_bytes!(&self.$reg.to_be_bytes()); + )* + } + } + + for reg in &self.r { + write_bytes!(®.to_be_bytes()); + } + + for reg in &self.f { + write_bytes!(®.to_be_bytes()); + } + + write_regs!(pc, msr, cr, lr, ctr, xer, fpscr); + + for ® in &self.vr { + let reg: u128 = reg; + write_bytes!(®.to_be_bytes()); + } + + write_regs!(vscr, vrsave); + } + + fn gdb_deserialize(&mut self, bytes: &[u8]) -> Result<(), ()> { + if bytes.len() < 0x3a4 { + return Err(()); + } + + let mut regs = bytes[0..0x80] + .chunks_exact(4) + .map(|x| u32::from_be_bytes(x.try_into().unwrap())); + + for reg in &mut self.r { + *reg = regs.next().ok_or(())?; + } + + let mut regs = bytes[0x80..0x180] + .chunks_exact(8) + .map(|x| f64::from_be_bytes(x.try_into().unwrap())); + + for reg in &mut self.f { + *reg = regs.next().ok_or(())?; + } + + macro_rules! parse_regs { + ($start:literal..$end:literal, $($reg:ident),*) => { + let mut regs = bytes[$start..$end] + .chunks_exact(4) + .map(|x| u32::from_be_bytes(x.try_into().unwrap())); + $( + self.$reg = regs.next().ok_or(())?; + )* + } + } + + parse_regs!(0x180..0x19c, pc, msr, cr, lr, ctr, xer, fpscr); + + let mut regs = bytes[0x19c..0x39c] + .chunks_exact(0x10) + .map(|x| u128::from_be_bytes(x.try_into().unwrap())); + + for reg in &mut self.vr { + *reg = regs.next().ok_or(())?; + } + + parse_regs!(0x39c..0x3a4, vscr, vrsave); + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn ppc_core_round_trip() { + let regs_before = PowerPcCommonRegs { + r: [1; 32], + pc: 2, + msr: 3, + cr: 4, + lr: 5, + ctr: 6, + xer: 7, + fpscr: 8, + f: [9.0; 32], + vr: [52; 32], + vrsave: 10, + vscr: 11, + }; + + let mut data = vec![]; + + regs_before.gdb_serialize(|x| { + data.push(x.unwrap_or(b'x')); + }); + + assert_eq!(data.len(), 0x3a4); + + let mut regs_after = PowerPcCommonRegs::default(); + regs_after.gdb_deserialize(&data).unwrap(); + + assert_eq!(regs_before, regs_after); + } +} diff --git a/src/ppc/reg/id.rs b/src/ppc/reg/id.rs new file mode 100644 index 0000000..97ccfea --- /dev/null +++ b/src/ppc/reg/id.rs @@ -0,0 +1,2 @@ +// TODO: Add proper `RegId` implementation. See [issue #29](https://github.com/daniel5151/gdbstub/issues/29) +// pub enum PowerPc32RegId {} diff --git a/src/ppc/reg/mod.rs b/src/ppc/reg/mod.rs new file mode 100644 index 0000000..fde8e55 --- /dev/null +++ b/src/ppc/reg/mod.rs @@ -0,0 +1,9 @@ +//! `Register` structs for PowerPC architectures + +/// `RegId` definitions for PowerPC architectures. +pub mod id; + +mod common; + +pub use common::PowerPcCommonRegs; +type PpcVector = u128; diff --git a/src/riscv/mod.rs b/src/riscv/mod.rs new file mode 100644 index 0000000..62ce98e --- /dev/null +++ b/src/riscv/mod.rs @@ -0,0 +1,35 @@ +//! Implementations for the [RISC-V](https://riscv.org/) architecture. +//! +//! *Note*: currently only supports integer versions of the ISA. + +use gdbstub::arch::Arch; + +pub mod reg; + +/// Implements `Arch` for 32-bit RISC-V. +pub enum Riscv32 {} + +/// Implements `Arch` for 64-bit RISC-V. +pub enum Riscv64 {} + +impl Arch for Riscv32 { + type Usize = u32; + type Registers = reg::RiscvCoreRegs<u32>; + type RegId = reg::id::RiscvRegId<u32>; + type BreakpointKind = usize; + + fn target_description_xml() -> Option<&'static str> { + Some(r#"<target version="1.0"><architecture>riscv</architecture></target>"#) + } +} + +impl Arch for Riscv64 { + type Usize = u64; + type Registers = reg::RiscvCoreRegs<u64>; + type RegId = reg::id::RiscvRegId<u64>; + type BreakpointKind = usize; + + fn target_description_xml() -> Option<&'static str> { + Some(r#"<target version="1.0"><architecture>riscv64</architecture></target>"#) + } +} diff --git a/src/riscv/reg/id.rs b/src/riscv/reg/id.rs new file mode 100644 index 0000000..b6e589d --- /dev/null +++ b/src/riscv/reg/id.rs @@ -0,0 +1,43 @@ +use gdbstub::arch::RegId; + +/// RISC-V Register identifier. +#[derive(Debug, Clone, Copy)] +#[non_exhaustive] +pub enum RiscvRegId<U> { + /// General Purpose Register (x0-x31). + Gpr(u8), + /// Floating Point Register (f0-f31). + Fpr(u8), + /// Program Counter. + Pc, + /// Control and Status Register. + Csr(u16), + /// Privilege level. + Priv, + + #[doc(hidden)] + _Marker(core::marker::PhantomData<U>), +} + +macro_rules! impl_riscv_reg_id { + ($usize:ty) => { + impl RegId for RiscvRegId<$usize> { + fn from_raw_id(id: usize) -> Option<(Self, usize)> { + 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), + _ => return None, + }; + Some(reg_size) + } + } + }; +} + +impl_riscv_reg_id!(u32); +impl_riscv_reg_id!(u64); diff --git a/src/riscv/reg/mod.rs b/src/riscv/reg/mod.rs new file mode 100644 index 0000000..e501c47 --- /dev/null +++ b/src/riscv/reg/mod.rs @@ -0,0 +1,8 @@ +//! `Register` structs for RISC-V architectures. + +/// `RegId` definitions for RISC-V architectures. +pub mod id; + +mod riscv; + +pub use riscv::RiscvCoreRegs; diff --git a/src/riscv/reg/riscv.rs b/src/riscv/reg/riscv.rs new file mode 100644 index 0000000..9875ad2 --- /dev/null +++ b/src/riscv/reg/riscv.rs @@ -0,0 +1,77 @@ +use num_traits::PrimInt; + +use gdbstub::arch::Registers; +use gdbstub::internal::LeBytes; + +/// RISC-V Integer registers. +/// +/// The register width is set to `u32` or `u64` based on the `<U>` type. +/// +/// Useful links: +/// * [GNU binutils-gdb XML descriptions](https://github.com/bminor/binutils-gdb/blob/master/gdb/features/riscv) +/// * [riscv-tdep.h](https://github.com/bminor/binutils-gdb/blob/master/gdb/riscv-tdep.h) +#[derive(Debug, Default, Clone, PartialEq)] +pub struct RiscvCoreRegs<U> { + /// General purpose registers (x0-x31) + pub x: [U; 32], + /// Program counter + pub pc: U, +} + +impl<U> Registers for RiscvCoreRegs<U> +where + U: PrimInt + LeBytes + Default + core::fmt::Debug, +{ + type ProgramCounter = U; + + fn pc(&self) -> Self::ProgramCounter { + self.pc + } + + fn gdb_serialize(&self, mut write_byte: impl FnMut(Option<u8>)) { + macro_rules! write_le_bytes { + ($value:expr) => { + let mut buf = [0; 16]; + // infallible (unless digit is a >128 bit number) + let len = $value.to_le_bytes(&mut buf).unwrap(); + let buf = &buf[..len]; + for b in buf { + write_byte(Some(*b)); + } + }; + } + + // Write GPRs + for reg in self.x.iter() { + write_le_bytes!(reg); + } + + // Program Counter is regnum 33 + write_le_bytes!(&self.pc); + } + + fn gdb_deserialize(&mut self, bytes: &[u8]) -> Result<(), ()> { + let ptrsize = core::mem::size_of::<U>(); + + // ensure bytes.chunks_exact(ptrsize) won't panic + if bytes.len() % ptrsize != 0 { + return Err(()); + } + + let mut regs = bytes + .chunks_exact(ptrsize) + .map(|c| U::from_le_bytes(c).unwrap()); + + // Read GPRs + for reg in self.x.iter_mut() { + *reg = regs.next().ok_or(())? + } + self.pc = regs.next().ok_or(())?; + + if regs.next().is_some() { + return Err(()); + } + + Ok(()) + } +} diff --git a/src/x86/mod.rs b/src/x86/mod.rs new file mode 100644 index 0000000..e7fec5c --- /dev/null +++ b/src/x86/mod.rs @@ -0,0 +1,52 @@ +//! Implementations for various x86 architectures. + +use gdbstub::arch::Arch; +use gdbstub::arch::RegId; + +pub mod reg; + +/// Implements `Arch` for 64-bit x86 + SSE Extensions. +/// +/// Check out the [module level docs](gdbstub::arch#whats-with-regidimpl) for +/// more info about the `RegIdImpl` type parameter. +#[allow(non_camel_case_types, clippy::upper_case_acronyms)] +pub enum X86_64_SSE<RegIdImpl: RegId = reg::id::X86_64CoreRegId> { + #[doc(hidden)] + _Marker(core::marker::PhantomData<RegIdImpl>), +} + +impl<RegIdImpl: RegId> Arch for X86_64_SSE<RegIdImpl> { + type Usize = u64; + type Registers = reg::X86_64CoreRegs; + type RegId = RegIdImpl; + type BreakpointKind = usize; + + fn target_description_xml() -> Option<&'static str> { + Some( + r#"<target version="1.0"><architecture>i386:x86-64</architecture><feature name="org.gnu.gdb.i386.sse"></feature></target>"#, + ) + } +} + +/// Implements `Arch` for 32-bit x86 + SSE Extensions. +/// +/// Check out the [module level docs](gdbstub::arch#whats-with-regidimpl) for +/// more info about the `RegIdImpl` type parameter. +#[allow(non_camel_case_types, clippy::upper_case_acronyms)] +pub enum X86_SSE<RegIdImpl: RegId = reg::id::X86CoreRegId> { + #[doc(hidden)] + _Marker(core::marker::PhantomData<RegIdImpl>), +} + +impl<RegIdImpl: RegId> Arch for X86_SSE<RegIdImpl> { + type Usize = u32; + type Registers = reg::X86CoreRegs; + type RegId = RegIdImpl; + type BreakpointKind = usize; + + fn target_description_xml() -> Option<&'static str> { + Some( + r#"<target version="1.0"><architecture>i386:intel</architecture><feature name="org.gnu.gdb.i386.sse"></feature></target>"#, + ) + } +} diff --git a/src/x86/reg/core32.rs b/src/x86/reg/core32.rs new file mode 100644 index 0000000..d91e707 --- /dev/null +++ b/src/x86/reg/core32.rs @@ -0,0 +1,185 @@ +use core::convert::TryInto; + +use gdbstub::arch::Registers; + +use super::{X86SegmentRegs, X87FpuInternalRegs, F80}; + +/// 32-bit x86 core registers (+ SSE extensions). +/// +/// Source: https://github.com/bminor/binutils-gdb/blob/master/gdb/features/i386/32bit-core.xml +/// Additionally: https://github.com/bminor/binutils-gdb/blob/master/gdb/features/i386/32bit-sse.xml +#[derive(Debug, Default, Clone, PartialEq)] +pub struct X86CoreRegs { + /// Accumulator + pub eax: u32, + /// Count register + pub ecx: u32, + /// Data register + pub edx: u32, + /// Base register + pub ebx: u32, + /// Stack pointer + pub esp: u32, + /// Base pointer + pub ebp: u32, + /// Source index + pub esi: u32, + /// Destination index + pub edi: u32, + /// Instruction pointer + pub eip: u32, + /// Status register + pub eflags: u32, + /// Segment registers: CS, SS, DS, ES, FS, GS + pub segments: X86SegmentRegs, + /// FPU registers: ST0 through ST7 + pub st: [F80; 8], + /// FPU internal registers + pub fpu: X87FpuInternalRegs, + /// SIMD Registers: XMM0 through XMM7 + pub xmm: [u128; 8], + /// SSE Status/Control Register + pub mxcsr: u32, +} + +impl Registers for X86CoreRegs { + type ProgramCounter = u32; + + fn pc(&self) -> Self::ProgramCounter { + self.eip + } + + fn gdb_serialize(&self, mut write_byte: impl FnMut(Option<u8>)) { + macro_rules! write_bytes { + ($bytes:expr) => { + for b in $bytes { + write_byte(Some(*b)) + } + }; + } + + macro_rules! write_regs { + ($($reg:ident),*) => { + $( + write_bytes!(&self.$reg.to_le_bytes()); + )* + } + } + + write_regs!(eax, ecx, edx, ebx, esp, ebp, esi, edi, eip, eflags); + + self.segments.gdb_serialize(&mut write_byte); + + // st0 to st7 + for st_reg in &self.st { + write_bytes!(st_reg); + } + + self.fpu.gdb_serialize(&mut write_byte); + + // xmm0 to xmm15 + for xmm_reg in &self.xmm { + write_bytes!(&xmm_reg.to_le_bytes()); + } + + // mxcsr + write_bytes!(&self.mxcsr.to_le_bytes()); + + // padding + (0..4).for_each(|_| write_byte(None)) + } + + fn gdb_deserialize(&mut self, bytes: &[u8]) -> Result<(), ()> { + if bytes.len() < 0x138 { + return Err(()); + } + + macro_rules! parse_regs { + ($($reg:ident),*) => { + let mut regs = bytes[0..0x28] + .chunks_exact(4) + .map(|x| u32::from_le_bytes(x.try_into().unwrap())); + $( + self.$reg = regs.next().ok_or(())?; + )* + } + } + + parse_regs!(eax, ecx, edx, ebx, esp, ebp, esi, edi, eip, eflags); + + self.segments.gdb_deserialize(&bytes[0x28..0x40])?; + + let mut regs = bytes[0x40..0x90].chunks_exact(10).map(TryInto::try_into); + + for reg in self.st.iter_mut() { + *reg = regs.next().ok_or(())?.map_err(|_| ())?; + } + + self.fpu.gdb_deserialize(&bytes[0x90..0xb0])?; + + let mut regs = bytes[0xb0..0x130] + .chunks_exact(0x10) + .map(|x| u128::from_le_bytes(x.try_into().unwrap())); + + for reg in self.xmm.iter_mut() { + *reg = regs.next().ok_or(())?; + } + + self.mxcsr = u32::from_le_bytes(bytes[0x130..0x134].try_into().unwrap()); + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn x86_core_round_trip() { + let regs_before = X86CoreRegs { + eax: 1, + ecx: 2, + edx: 3, + ebx: 4, + esp: 5, + ebp: 6, + esi: 7, + edi: 8, + eip: 9, + eflags: 10, + segments: X86SegmentRegs { + cs: 11, + ss: 12, + ds: 13, + es: 14, + fs: 15, + gs: 16, + }, + st: Default::default(), + fpu: X87FpuInternalRegs { + fctrl: 17, + fstat: 18, + ftag: 19, + fiseg: 20, + fioff: 21, + foseg: 22, + fooff: 23, + fop: 24, + }, + xmm: Default::default(), + mxcsr: 99, + }; + + let mut data = vec![]; + + regs_before.gdb_serialize(|x| { + data.push(x.unwrap_or(b'x')); + }); + + let mut regs_after = X86CoreRegs::default(); + regs_after.gdb_deserialize(&data).unwrap(); + + assert_eq!(regs_before, regs_after); + } +} diff --git a/src/x86/reg/core64.rs b/src/x86/reg/core64.rs new file mode 100644 index 0000000..e0fc5af --- /dev/null +++ b/src/x86/reg/core64.rs @@ -0,0 +1,119 @@ +use core::convert::TryInto; + +use gdbstub::arch::Registers; + +use super::{X86SegmentRegs, X87FpuInternalRegs, F80}; + +/// 64-bit x86 core registers (+ SSE extensions). +/// +/// Source: https://github.com/bminor/binutils-gdb/blob/master/gdb/features/i386/64bit-core.xml +/// Additionally: https://github.com/bminor/binutils-gdb/blob/master/gdb/features/i386/64bit-sse.xml +#[derive(Debug, Default, Clone, PartialEq)] +pub struct X86_64CoreRegs { + /// RAX, RBX, RCX, RDX, RSI, RDI, RBP, RSP, r8-r15 + pub regs: [u64; 16], + /// Status register + pub eflags: u32, + /// Instruction pointer + pub rip: u64, + /// Segment registers: CS, SS, DS, ES, FS, GS + pub segments: X86SegmentRegs, + /// FPU registers: ST0 through ST7 + pub st: [F80; 8], + /// FPU internal registers + pub fpu: X87FpuInternalRegs, + /// SIMD Registers: XMM0 through XMM15 + pub xmm: [u128; 0x10], + /// SSE Status/Control Register + pub mxcsr: u32, +} + +impl Registers for X86_64CoreRegs { + type ProgramCounter = u64; + + fn pc(&self) -> Self::ProgramCounter { + self.rip + } + + fn gdb_serialize(&self, mut write_byte: impl FnMut(Option<u8>)) { + macro_rules! write_bytes { + ($bytes:expr) => { + for b in $bytes { + write_byte(Some(*b)) + } + }; + } + + // rax, rbx, rcx, rdx, rsi, rdi, rbp, rsp, r8-r15 + for reg in &self.regs { + write_bytes!(®.to_le_bytes()); + } + + // rip + write_bytes!(&self.rip.to_le_bytes()); + + // eflags + write_bytes!(&self.eflags.to_le_bytes()); + + self.segments.gdb_serialize(&mut write_byte); + + // st0 to st7 + for st_reg in &self.st { + write_bytes!(st_reg); + } + + self.fpu.gdb_serialize(&mut write_byte); + + // xmm0 to xmm15 + for xmm_reg in &self.xmm { + write_bytes!(&xmm_reg.to_le_bytes()); + } + + // mxcsr + write_bytes!(&self.mxcsr.to_le_bytes()); + + // padding? + // XXX: Couldn't figure out what these do and GDB doesn't actually display any + // registers that use these values. + (0..0x18).for_each(|_| write_byte(None)) + } + + fn gdb_deserialize(&mut self, bytes: &[u8]) -> Result<(), ()> { + if bytes.len() < 0x218 { + return Err(()); + } + + let mut regs = bytes[0..0x80] + .chunks_exact(8) + .map(|x| u64::from_le_bytes(x.try_into().unwrap())); + + for reg in self.regs.iter_mut() { + *reg = regs.next().ok_or(())?; + } + + self.rip = u64::from_le_bytes(bytes[0x80..0x88].try_into().unwrap()); + self.eflags = u32::from_le_bytes(bytes[0x88..0x8C].try_into().unwrap()); + + self.segments.gdb_deserialize(&bytes[0x8C..0xA4])?; + + let mut regs = bytes[0xA4..0xF4].chunks_exact(10).map(TryInto::try_into); + + for reg in self.st.iter_mut() { + *reg = regs.next().ok_or(())?.map_err(|_| ())?; + } + + self.fpu.gdb_deserialize(&bytes[0xF4..0x114])?; + + let mut regs = bytes[0x114..0x214] + .chunks_exact(0x10) + .map(|x| u128::from_le_bytes(x.try_into().unwrap())); + + for reg in self.xmm.iter_mut() { + *reg = regs.next().ok_or(())?; + } + + self.mxcsr = u32::from_le_bytes(bytes[0x214..0x218].try_into().unwrap()); + + Ok(()) + } +} diff --git a/src/x86/reg/id.rs b/src/x86/reg/id.rs new file mode 100644 index 0000000..03de64b --- /dev/null +++ b/src/x86/reg/id.rs @@ -0,0 +1,227 @@ +use gdbstub::arch::RegId; + +/// FPU register identifier. +#[derive(Debug, Clone, Copy)] +pub enum X87FpuInternalRegId { + /// Floating-point control register + Fctrl, + /// Floating-point status register + Fstat, + /// Tag word + Ftag, + /// FPU instruction pointer segment + Fiseg, + /// FPU instruction pointer offset + Fioff, + /// FPU operand segment + Foseg, + /// FPU operand offset + Fooff, + /// Floating-point opcode + Fop, +} + +impl X87FpuInternalRegId { + fn from_u8(val: u8) -> Option<Self> { + use self::X87FpuInternalRegId::*; + + let r = match val { + 0 => Fctrl, + 1 => Fstat, + 2 => Ftag, + 3 => Fiseg, + 4 => Fioff, + 5 => Foseg, + 6 => Fooff, + 7 => Fop, + _ => return None, + }; + Some(r) + } +} + +/// Segment register identifier. +#[derive(Debug, Clone, Copy)] +#[allow(clippy::upper_case_acronyms)] +pub enum X86SegmentRegId { + /// Code Segment + CS, + /// Stack Segment + SS, + /// Data Segment + DS, + /// Extra Segment + ES, + /// General Purpose Segment + FS, + /// General Purpose Segment + GS, +} + +impl X86SegmentRegId { + fn from_u8(val: u8) -> Option<Self> { + use self::X86SegmentRegId::*; + + let r = match val { + 0 => CS, + 1 => SS, + 2 => DS, + 3 => ES, + 4 => FS, + 5 => GS, + _ => return None, + }; + Some(r) + } +} + +/// 32-bit x86 core + SSE register identifier. +/// +/// Source: https://github.com/bminor/binutils-gdb/blob/master/gdb/features/i386/32bit-core.xml +/// Additionally: https://github.com/bminor/binutils-gdb/blob/master/gdb/features/i386/32bit-sse.xml +#[derive(Debug, Clone, Copy)] +#[non_exhaustive] +pub enum X86CoreRegId { + /// Accumulator + Eax, + /// Count register + Ecx, + /// Data register + Edx, + /// Base register + Ebx, + /// Stack pointer + Esp, + /// Base pointer + Ebp, + /// Source index + Esi, + /// Destination index + Edi, + /// Instruction pointer + Eip, + /// Status register + Eflags, + /// Segment registers + Segment(X86SegmentRegId), + /// FPU registers: ST0 through ST7 + St(u8), + /// FPU internal registers + Fpu(X87FpuInternalRegId), + /// SIMD Registers: XMM0 through XMM7 + Xmm(u8), + /// SSE Status/Control Register + Mxcsr, +} + +impl RegId for X86CoreRegId { + fn from_raw_id(id: usize) -> Option<(Self, usize)> { + 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), + _ => return None, + }; + Some(r) + } +} + +/// 64-bit x86 core + SSE register identifier. +/// +/// Source: https://github.com/bminor/binutils-gdb/blob/master/gdb/features/i386/64bit-core.xml +/// Additionally: https://github.com/bminor/binutils-gdb/blob/master/gdb/features/i386/64bit-sse.xml +#[derive(Debug, Clone, Copy)] +#[non_exhaustive] +pub enum X86_64CoreRegId { + /// General purpose registers: + /// RAX, RBX, RCX, RDX, RSI, RDI, RBP, RSP, r8-r15 + Gpr(u8), + /// Instruction pointer + Rip, + /// Status register + Eflags, + /// Segment registers + Segment(X86SegmentRegId), + /// FPU registers: ST0 through ST7 + St(u8), + /// FPU internal registers + Fpu(X87FpuInternalRegId), + /// SIMD Registers: XMM0 through XMM15 + Xmm(u8), + /// SSE Status/Control Register + Mxcsr, +} + +impl RegId for X86_64CoreRegId { + fn from_raw_id(id: usize) -> Option<(Self, usize)> { + 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), + _ => return None, + }; + Some(r) + } +} + +#[cfg(test)] +mod tests { + use gdbstub::arch::RegId; + use gdbstub::arch::Registers; + + /// Compare the following two values which are expected to be the same: + /// * length of data written by `Registers::gdb_serialize()` in byte + /// * sum of sizes of all registers obtained by `RegId::from_raw_id()` + fn test<Rs: Registers, RId: RegId>() { + // Obtain the data length written by `gdb_serialize` by passing a custom + // closure. + let mut serialized_data_len = 0; + let counter = |b: Option<u8>| { + if b.is_some() { + serialized_data_len += 1; + } + }; + Rs::default().gdb_serialize(counter); + + // Accumulate register sizes returned by `from_raw_id`. + let mut i = 0; + let mut sum_reg_sizes = 0; + while let Some((_, size)) = RId::from_raw_id(i) { + sum_reg_sizes += size; + i += 1; + } + + assert_eq!(serialized_data_len, sum_reg_sizes); + } + + #[test] + fn test_x86() { + test::<crate::x86::reg::X86CoreRegs, crate::x86::reg::id::X86CoreRegId>() + } + + #[test] + fn test_x86_64() { + test::<crate::x86::reg::X86_64CoreRegs, crate::x86::reg::id::X86_64CoreRegId>() + } +} diff --git a/src/x86/reg/mod.rs b/src/x86/reg/mod.rs new file mode 100644 index 0000000..bac64cd --- /dev/null +++ b/src/x86/reg/mod.rs @@ -0,0 +1,155 @@ +//! `Register` structs for x86 architectures. + +use core::convert::TryInto; + +use gdbstub::arch::Registers; + +/// `RegId` definitions for x86 architectures. +pub mod id; + +mod core32; +mod core64; + +pub use core32::X86CoreRegs; +pub use core64::X86_64CoreRegs; + +/// 80-bit floating point value +pub type F80 = [u8; 10]; + +/// FPU registers +#[derive(Debug, Default, Clone, PartialEq)] +pub struct X87FpuInternalRegs { + /// Floating-point control register + pub fctrl: u32, + /// Floating-point status register + pub fstat: u32, + /// Tag word + pub ftag: u32, + /// FPU instruction pointer segment + pub fiseg: u32, + /// FPU instruction pointer offset + pub fioff: u32, + /// FPU operand segment + pub foseg: u32, + /// FPU operand offset + pub fooff: u32, + /// Floating-point opcode + pub fop: u32, +} + +impl Registers for X87FpuInternalRegs { + type ProgramCounter = u32; + + // HACK: this struct is never used as an architecture's main register file, so + // using a dummy value here is fine. + fn pc(&self) -> Self::ProgramCounter { + 0 + } + + fn gdb_serialize(&self, mut write_byte: impl FnMut(Option<u8>)) { + macro_rules! write_bytes { + ($bytes:expr) => { + for b in $bytes { + write_byte(Some(*b)) + } + }; + } + + // Note: GDB section names don't make sense unless you read x87 FPU section 8.1: + // https://web.archive.org/web/20150123212110/http://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-vol-1-manual.pdf + write_bytes!(&self.fctrl.to_le_bytes()); + write_bytes!(&self.fstat.to_le_bytes()); + write_bytes!(&self.ftag.to_le_bytes()); + write_bytes!(&self.fiseg.to_le_bytes()); + write_bytes!(&self.fioff.to_le_bytes()); + write_bytes!(&self.foseg.to_le_bytes()); + write_bytes!(&self.fooff.to_le_bytes()); + write_bytes!(&self.fop.to_le_bytes()); + } + + fn gdb_deserialize(&mut self, bytes: &[u8]) -> Result<(), ()> { + if bytes.len() != 0x20 { + return Err(()); + } + + let mut regs = bytes + .chunks_exact(4) + .map(|x| u32::from_le_bytes(x.try_into().unwrap())); + + self.fctrl = regs.next().ok_or(())?; + self.fstat = regs.next().ok_or(())?; + self.ftag = regs.next().ok_or(())?; + self.fiseg = regs.next().ok_or(())?; + self.fioff = regs.next().ok_or(())?; + self.foseg = regs.next().ok_or(())?; + self.fooff = regs.next().ok_or(())?; + self.fop = regs.next().ok_or(())?; + + Ok(()) + } +} + +/// x86 segment registers. +/// +/// Source: https://github.com/bminor/binutils-gdb/blob/master/gdb/features/i386/64bit-core.xml +#[derive(Debug, Default, Clone, PartialEq)] +pub struct X86SegmentRegs { + /// Code Segment + pub cs: u32, + /// Stack Segment + pub ss: u32, + /// Data Segment + pub ds: u32, + /// Extra Segment + pub es: u32, + /// General Purpose Segment + pub fs: u32, + /// General Purpose Segment + pub gs: u32, +} + +impl Registers for X86SegmentRegs { + type ProgramCounter = u32; + + // HACK: this struct is never used as an architecture's main register file, so + // using a dummy value here is fine. + fn pc(&self) -> Self::ProgramCounter { + 0 + } + + fn gdb_serialize(&self, mut write_byte: impl FnMut(Option<u8>)) { + macro_rules! write_bytes { + ($bytes:expr) => { + for b in $bytes { + write_byte(Some(*b)) + } + }; + } + + write_bytes!(&self.cs.to_le_bytes()); + write_bytes!(&self.ss.to_le_bytes()); + write_bytes!(&self.ds.to_le_bytes()); + write_bytes!(&self.es.to_le_bytes()); + write_bytes!(&self.fs.to_le_bytes()); + write_bytes!(&self.gs.to_le_bytes()); + } + + fn gdb_deserialize(&mut self, bytes: &[u8]) -> Result<(), ()> { + if bytes.len() != core::mem::size_of::<u32>() * 6 { + return Err(()); + } + + let mut regs = bytes + .chunks_exact(4) + .map(|x| u32::from_le_bytes(x.try_into().unwrap())); + + self.cs = regs.next().ok_or(())?; + self.ss = regs.next().ok_or(())?; + self.ds = regs.next().ok_or(())?; + self.es = regs.next().ok_or(())?; + self.fs = regs.next().ok_or(())?; + self.gs = regs.next().ok_or(())?; + + Ok(()) + } +} |