1 use core::num::NonZeroUsize; 2 3 use gdbstub::arch::RegId; 4 5 /// FPU register identifier. 6 #[derive(Debug, Clone, Copy)] 7 pub enum X87FpuInternalRegId { 8 /// Floating-point control register 9 Fctrl, 10 /// Floating-point status register 11 Fstat, 12 /// Tag word 13 Ftag, 14 /// FPU instruction pointer segment 15 Fiseg, 16 /// FPU instruction pointer offset 17 Fioff, 18 /// FPU operand segment 19 Foseg, 20 /// FPU operand offset 21 Fooff, 22 /// Floating-point opcode 23 Fop, 24 } 25 26 impl X87FpuInternalRegId { from_u8(val: u8) -> Option<Self>27 fn from_u8(val: u8) -> Option<Self> { 28 use self::X87FpuInternalRegId::*; 29 30 let r = match val { 31 0 => Fctrl, 32 1 => Fstat, 33 2 => Ftag, 34 3 => Fiseg, 35 4 => Fioff, 36 5 => Foseg, 37 6 => Fooff, 38 7 => Fop, 39 _ => return None, 40 }; 41 Some(r) 42 } 43 } 44 45 /// Segment register identifier. 46 #[derive(Debug, Clone, Copy)] 47 #[allow(clippy::upper_case_acronyms)] 48 pub enum X86SegmentRegId { 49 /// Code Segment 50 CS, 51 /// Stack Segment 52 SS, 53 /// Data Segment 54 DS, 55 /// Extra Segment 56 ES, 57 /// General Purpose Segment 58 FS, 59 /// General Purpose Segment 60 GS, 61 } 62 63 impl X86SegmentRegId { from_u8(val: u8) -> Option<Self>64 fn from_u8(val: u8) -> Option<Self> { 65 use self::X86SegmentRegId::*; 66 67 let r = match val { 68 0 => CS, 69 1 => SS, 70 2 => DS, 71 3 => ES, 72 4 => FS, 73 5 => GS, 74 _ => return None, 75 }; 76 Some(r) 77 } 78 } 79 80 /// 32-bit x86 core + SSE register identifier. 81 /// 82 /// Source: <https://github.com/bminor/binutils-gdb/blob/master/gdb/features/i386/32bit-core.xml> 83 /// Additionally: <https://github.com/bminor/binutils-gdb/blob/master/gdb/features/i386/32bit-sse.xml> 84 #[derive(Debug, Clone, Copy)] 85 #[non_exhaustive] 86 pub enum X86CoreRegId { 87 /// Accumulator 88 Eax, 89 /// Count register 90 Ecx, 91 /// Data register 92 Edx, 93 /// Base register 94 Ebx, 95 /// Stack pointer 96 Esp, 97 /// Base pointer 98 Ebp, 99 /// Source index 100 Esi, 101 /// Destination index 102 Edi, 103 /// Instruction pointer 104 Eip, 105 /// Status register 106 Eflags, 107 /// Segment registers 108 Segment(X86SegmentRegId), 109 /// FPU registers: ST0 through ST7 110 St(u8), 111 /// FPU internal registers 112 Fpu(X87FpuInternalRegId), 113 /// SIMD Registers: XMM0 through XMM7 114 Xmm(u8), 115 /// SSE Status/Control Register 116 Mxcsr, 117 } 118 119 impl RegId for X86CoreRegId { from_raw_id(id: usize) -> Option<(Self, Option<NonZeroUsize>)>120 fn from_raw_id(id: usize) -> Option<(Self, Option<NonZeroUsize>)> { 121 use self::X86CoreRegId::*; 122 123 let (r, sz): (X86CoreRegId, usize) = match id { 124 0 => (Eax, 4), 125 1 => (Ecx, 4), 126 2 => (Edx, 4), 127 3 => (Ebx, 4), 128 4 => (Esp, 4), 129 5 => (Ebp, 4), 130 6 => (Esi, 4), 131 7 => (Edi, 4), 132 8 => (Eip, 4), 133 9 => (Eflags, 4), 134 10..=15 => (Segment(X86SegmentRegId::from_u8(id as u8 - 10)?), 4), 135 16..=23 => (St(id as u8 - 16), 10), 136 24..=31 => (Fpu(X87FpuInternalRegId::from_u8(id as u8 - 24)?), 4), 137 32..=39 => (Xmm(id as u8 - 32), 16), 138 40 => (Mxcsr, 4), 139 _ => return None, 140 }; 141 142 Some((r, Some(NonZeroUsize::new(sz)?))) 143 } 144 } 145 146 /// 64-bit x86 core + SSE register identifier. 147 /// 148 /// Source: <https://github.com/bminor/binutils-gdb/blob/master/gdb/features/i386/64bit-core.xml> 149 /// Additionally: <https://github.com/bminor/binutils-gdb/blob/master/gdb/features/i386/64bit-sse.xml> 150 #[derive(Debug, Clone, Copy)] 151 #[non_exhaustive] 152 pub enum X86_64CoreRegId { 153 /// General purpose registers: 154 /// RAX, RBX, RCX, RDX, RSI, RDI, RBP, RSP, r8-r15 155 Gpr(u8), 156 /// Instruction pointer 157 Rip, 158 /// Status register 159 Eflags, 160 /// Segment registers 161 Segment(X86SegmentRegId), 162 /// FPU registers: ST0 through ST7 163 St(u8), 164 /// FPU internal registers 165 Fpu(X87FpuInternalRegId), 166 /// SIMD Registers: XMM0 through XMM15 167 Xmm(u8), 168 /// SSE Status/Control Register 169 Mxcsr, 170 } 171 172 impl RegId for X86_64CoreRegId { from_raw_id(id: usize) -> Option<(Self, Option<NonZeroUsize>)>173 fn from_raw_id(id: usize) -> Option<(Self, Option<NonZeroUsize>)> { 174 use self::X86_64CoreRegId::*; 175 176 let (r, sz): (X86_64CoreRegId, usize) = match id { 177 0..=15 => (Gpr(id as u8), 8), 178 16 => (Rip, 8), 179 17 => (Eflags, 4), 180 18..=23 => (Segment(X86SegmentRegId::from_u8(id as u8 - 18)?), 4), 181 24..=31 => (St(id as u8 - 24), 10), 182 32..=39 => (Fpu(X87FpuInternalRegId::from_u8(id as u8 - 32)?), 4), 183 40..=55 => (Xmm(id as u8 - 40), 16), 184 56 => (Mxcsr, 4), 185 _ => return None, 186 }; 187 188 Some((r, Some(NonZeroUsize::new(sz)?))) 189 } 190 } 191 192 #[cfg(test)] 193 mod tests { 194 use gdbstub::arch::RegId; 195 use gdbstub::arch::Registers; 196 197 /// Compare the following two values which are expected to be the same: 198 /// * length of data written by `Registers::gdb_serialize()` in byte 199 /// * sum of sizes of all registers obtained by `RegId::from_raw_id()` test<Rs: Registers, RId: RegId>()200 fn test<Rs: Registers, RId: RegId>() { 201 // Obtain the data length written by `gdb_serialize` by passing a custom 202 // closure. 203 let mut serialized_data_len = 0; 204 let counter = |b: Option<u8>| { 205 if b.is_some() { 206 serialized_data_len += 1; 207 } 208 }; 209 Rs::default().gdb_serialize(counter); 210 211 // Accumulate register sizes returned by `from_raw_id`. 212 let mut i = 0; 213 let mut sum_reg_sizes = 0; 214 while let Some((_, size)) = RId::from_raw_id(i) { 215 sum_reg_sizes += size.unwrap().get(); 216 i += 1; 217 } 218 219 assert_eq!(serialized_data_len, sum_reg_sizes); 220 } 221 222 #[test] test_x86()223 fn test_x86() { 224 test::<crate::x86::reg::X86CoreRegs, crate::x86::reg::id::X86CoreRegId>() 225 } 226 227 #[test] test_x86_64()228 fn test_x86_64() { 229 test::<crate::x86::reg::X86_64CoreRegs, crate::x86::reg::id::X86_64CoreRegId>() 230 } 231 } 232