1 use super::X86SegmentRegs; 2 use super::X87FpuInternalRegs; 3 use super::F80; 4 use core::convert::TryInto; 5 use gdbstub::arch::Registers; 6 7 /// 32-bit x86 core registers (+ SSE extensions). 8 /// 9 /// Source: <https://github.com/bminor/binutils-gdb/blob/master/gdb/features/i386/32bit-core.xml> 10 /// Additionally: <https://github.com/bminor/binutils-gdb/blob/master/gdb/features/i386/32bit-sse.xml> 11 #[derive(Debug, Default, Clone, PartialEq, Eq)] 12 pub struct X86CoreRegs { 13 /// Accumulator 14 pub eax: u32, 15 /// Count register 16 pub ecx: u32, 17 /// Data register 18 pub edx: u32, 19 /// Base register 20 pub ebx: u32, 21 /// Stack pointer 22 pub esp: u32, 23 /// Base pointer 24 pub ebp: u32, 25 /// Source index 26 pub esi: u32, 27 /// Destination index 28 pub edi: u32, 29 /// Instruction pointer 30 pub eip: u32, 31 /// Status register 32 pub eflags: u32, 33 /// Segment registers: CS, SS, DS, ES, FS, GS 34 pub segments: X86SegmentRegs, 35 /// FPU registers: ST0 through ST7 36 pub st: [F80; 8], 37 /// FPU internal registers 38 pub fpu: X87FpuInternalRegs, 39 /// SIMD Registers: XMM0 through XMM7 40 pub xmm: [u128; 8], 41 /// SSE Status/Control Register 42 pub mxcsr: u32, 43 } 44 45 impl Registers for X86CoreRegs { 46 type ProgramCounter = u32; 47 pc(&self) -> Self::ProgramCounter48 fn pc(&self) -> Self::ProgramCounter { 49 self.eip 50 } 51 gdb_serialize(&self, mut write_byte: impl FnMut(Option<u8>))52 fn gdb_serialize(&self, mut write_byte: impl FnMut(Option<u8>)) { 53 macro_rules! write_bytes { 54 ($bytes:expr) => { 55 for b in $bytes { 56 write_byte(Some(*b)) 57 } 58 }; 59 } 60 61 macro_rules! write_regs { 62 ($($reg:ident),*) => { 63 $( 64 write_bytes!(&self.$reg.to_le_bytes()); 65 )* 66 } 67 } 68 69 write_regs!(eax, ecx, edx, ebx, esp, ebp, esi, edi, eip, eflags); 70 71 self.segments.gdb_serialize(&mut write_byte); 72 73 // st0 to st7 74 for st_reg in &self.st { 75 write_bytes!(st_reg); 76 } 77 78 self.fpu.gdb_serialize(&mut write_byte); 79 80 // xmm0 to xmm15 81 for xmm_reg in &self.xmm { 82 write_bytes!(&xmm_reg.to_le_bytes()); 83 } 84 85 // mxcsr 86 write_bytes!(&self.mxcsr.to_le_bytes()); 87 88 // padding 89 (0..4).for_each(|_| write_byte(None)) 90 } 91 gdb_deserialize(&mut self, bytes: &[u8]) -> Result<(), ()>92 fn gdb_deserialize(&mut self, bytes: &[u8]) -> Result<(), ()> { 93 if bytes.len() < 0x138 { 94 return Err(()); 95 } 96 97 macro_rules! parse_regs { 98 ($($reg:ident),*) => { 99 let mut regs = bytes[0..0x28] 100 .chunks_exact(4) 101 .map(|x| u32::from_le_bytes(x.try_into().unwrap())); 102 $( 103 self.$reg = regs.next().ok_or(())?; 104 )* 105 } 106 } 107 108 parse_regs!(eax, ecx, edx, ebx, esp, ebp, esi, edi, eip, eflags); 109 110 self.segments.gdb_deserialize(&bytes[0x28..0x40])?; 111 112 let mut regs = bytes[0x40..0x90].chunks_exact(10).map(TryInto::try_into); 113 114 for reg in self.st.iter_mut() { 115 *reg = regs.next().ok_or(())?.map_err(|_| ())?; 116 } 117 118 self.fpu.gdb_deserialize(&bytes[0x90..0xb0])?; 119 120 let mut regs = bytes[0xb0..0x130] 121 .chunks_exact(0x10) 122 .map(|x| u128::from_le_bytes(x.try_into().unwrap())); 123 124 for reg in self.xmm.iter_mut() { 125 *reg = regs.next().ok_or(())?; 126 } 127 128 self.mxcsr = u32::from_le_bytes(bytes[0x130..0x134].try_into().unwrap()); 129 130 Ok(()) 131 } 132 } 133 134 #[cfg(test)] 135 mod tests { 136 use super::*; 137 138 #[test] x86_core_round_trip()139 fn x86_core_round_trip() { 140 let regs_before = X86CoreRegs { 141 eax: 1, 142 ecx: 2, 143 edx: 3, 144 ebx: 4, 145 esp: 5, 146 ebp: 6, 147 esi: 7, 148 edi: 8, 149 eip: 9, 150 eflags: 10, 151 segments: X86SegmentRegs { 152 cs: 11, 153 ss: 12, 154 ds: 13, 155 es: 14, 156 fs: 15, 157 gs: 16, 158 }, 159 st: Default::default(), 160 fpu: X87FpuInternalRegs { 161 fctrl: 17, 162 fstat: 18, 163 ftag: 19, 164 fiseg: 20, 165 fioff: 21, 166 foseg: 22, 167 fooff: 23, 168 fop: 24, 169 }, 170 xmm: Default::default(), 171 mxcsr: 99, 172 }; 173 174 let mut data = vec![]; 175 176 regs_before.gdb_serialize(|x| { 177 data.push(x.unwrap_or(b'x')); 178 }); 179 180 let mut regs_after = X86CoreRegs::default(); 181 regs_after.gdb_deserialize(&data).unwrap(); 182 183 assert_eq!(regs_before, regs_after); 184 } 185 } 186