aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoel Galenson <jgalenson@google.com>2021-08-03 22:02:33 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2021-08-03 22:02:33 +0000
commit2966eb1057c8723f1abe318994978d92d5f30691 (patch)
treec39c3a7ddb6c612daf46e1921f2ca6e98e1f56b1
parent861ca6705fbacabc9b281bd5b901be70b9125677 (diff)
parenta481b6e84e329cae1eddce23c99699223a65dba8 (diff)
downloadgdbstub_arch-2966eb1057c8723f1abe318994978d92d5f30691.tar.gz
Import gdbstub_arch-0.1.0 am: 52a992f950 am: e54eca01fa am: bd0fc1143e am: a481b6e84e
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/gdbstub_arch/+/1783794 Change-Id: I081d45dd2aaba0709cc95c8599fa6d075e9af13d
-rw-r--r--.cargo_vcs_info.json5
-rw-r--r--Cargo.toml32
-rw-r--r--Cargo.toml.orig18
-rw-r--r--README.md22
-rw-r--r--src/arm/mod.rs45
-rw-r--r--src/arm/reg/arm_core.rs82
-rw-r--r--src/arm/reg/id.rs36
-rw-r--r--src/arm/reg/mod.rs8
-rw-r--r--src/lib.rs65
-rw-r--r--src/mips/mod.rs110
-rw-r--r--src/mips/reg/id.rs123
-rw-r--r--src/mips/reg/mips.rs271
-rw-r--r--src/mips/reg/mod.rs11
-rw-r--r--src/msp430/mod.rs26
-rw-r--r--src/msp430/reg/id.rs71
-rw-r--r--src/msp430/reg/mod.rs8
-rw-r--r--src/msp430/reg/msp430.rs71
-rw-r--r--src/ppc/mod.rs28
-rw-r--r--src/ppc/reg/common.rs166
-rw-r--r--src/ppc/reg/id.rs2
-rw-r--r--src/ppc/reg/mod.rs9
-rw-r--r--src/riscv/mod.rs35
-rw-r--r--src/riscv/reg/id.rs43
-rw-r--r--src/riscv/reg/mod.rs8
-rw-r--r--src/riscv/reg/riscv.rs77
-rw-r--r--src/x86/mod.rs52
-rw-r--r--src/x86/reg/core32.rs185
-rw-r--r--src/x86/reg/core64.rs119
-rw-r--r--src/x86/reg/id.rs227
-rw-r--r--src/x86/reg/mod.rs155
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!(&reg.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!(&reg);
+ }
+
+ // 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!(&reg.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!(&reg.to_be_bytes());
+ }
+
+ for reg in &self.f {
+ write_bytes!(&reg.to_be_bytes());
+ }
+
+ write_regs!(pc, msr, cr, lr, ctr, xer, fpscr);
+
+ for &reg in &self.vr {
+ let reg: u128 = reg;
+ write_bytes!(&reg.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!(&reg.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(())
+ }
+}