1 // 2 // Copyright © 2020 Microsoft 3 // 4 // SPDX-License-Identifier: Apache-2.0 5 // 6 7 #![allow(non_camel_case_types, clippy::upper_case_acronyms)] 8 9 // 10 // CMP-Compare Two Operands 11 // 12 13 use crate::arch::emulator::{EmulationError, PlatformEmulator}; 14 use crate::arch::x86::emulator::instructions::*; 15 use crate::arch::x86::regs::*; 16 use crate::arch::x86::Exception; 17 18 // CMP affects OF, SF, ZF, AF, PF and CF 19 const FLAGS_MASK: u64 = CF | PF | AF | ZF | SF | OF; 20 21 // TODO: Switch to inline asm when that's stable. Executing CMP (or any arthimetic instructions) 22 // natively and extracting RFLAGS will be much faster and make the code simpler. 23 fn calc_rflags_cpazso(op0: u64, op1: u64, op_size: usize) -> u64 { 24 let op_bits = op_size * 8; 25 let msb_shift = op_bits - 1; 26 // CMP is the same as SUB. 27 let result = op0.wrapping_sub(op1); 28 29 // Carry-out vector for SUB. 30 let cout = (!op0 & op1) | ((!op0 ^ op1) & result); 31 32 let cf = ((cout >> msb_shift) & 0x1) << CF_SHIFT; 33 34 // PF only needs the least significant byte. XOR its higher 4 bits with its lower 4 bits then 35 // use the value directly. 36 let pf = ((0x9669 >> ((result ^ (result >> 4)) & 0xf)) & 0x1) << PF_SHIFT; 37 38 // AF cares about the lowest 4 bits (nibble). msb_shift is 3 in this case. 39 let af = ((cout >> 3) & 0x1) << AF_SHIFT; 40 41 let zf = if result & (!0u64 >> (63 - msb_shift)) == 0 { 42 1 43 } else { 44 0 45 } << ZF_SHIFT; 46 47 let sf = ((result >> msb_shift) & 0x1) << SF_SHIFT; 48 49 // Overflow happens when two operands have the same sign but the result has a different sign. 50 let of = ((((op0 ^ op1) & (op0 ^ result)) >> msb_shift) & 0x1) << OF_SHIFT; 51 52 cf | pf | af | zf | sf | of 53 } 54 55 macro_rules! cmp_rm_r { 56 ($bound:ty) => { 57 fn emulate( 58 &self, 59 insn: &Instruction, 60 state: &mut T, 61 platform: &mut dyn PlatformEmulator<CpuState = T>, 62 ) -> Result<(), EmulationError<Exception>> { 63 let op0_value = get_op(&insn, 0, std::mem::size_of::<$bound>(), state, platform) 64 .map_err(EmulationError::PlatformEmulationError)?; 65 let op1_value = get_op(&insn, 1, std::mem::size_of::<$bound>(), state, platform) 66 .map_err(EmulationError::PlatformEmulationError)?; 67 68 let cpazso = calc_rflags_cpazso(op0_value, op1_value, std::mem::size_of::<$bound>()); 69 70 state.set_flags((state.flags() & !FLAGS_MASK) | cpazso); 71 72 Ok(()) 73 } 74 }; 75 } 76 77 macro_rules! cmp_r_rm { 78 ($bound:ty) => { 79 fn emulate( 80 &self, 81 insn: &Instruction, 82 state: &mut T, 83 platform: &mut dyn PlatformEmulator<CpuState = T>, 84 ) -> Result<(), EmulationError<Exception>> { 85 let op0_value = get_op(&insn, 0, std::mem::size_of::<$bound>(), state, platform) 86 .map_err(EmulationError::PlatformEmulationError)?; 87 let op1_value = get_op(&insn, 1, std::mem::size_of::<$bound>(), state, platform) 88 .map_err(EmulationError::PlatformEmulationError)?; 89 90 let cpazso = calc_rflags_cpazso(op0_value, op1_value, std::mem::size_of::<$bound>()); 91 92 state.set_flags((state.flags() & !FLAGS_MASK) | cpazso); 93 94 Ok(()) 95 } 96 }; 97 } 98 99 macro_rules! cmp_rm_imm { 100 ($imm:ty, $bound:ty) => { 101 fn emulate( 102 &self, 103 insn: &Instruction, 104 state: &mut T, 105 platform: &mut dyn PlatformEmulator<CpuState = T>, 106 ) -> Result<(), EmulationError<Exception>> { 107 let op0_value = get_op(&insn, 0, std::mem::size_of::<$bound>(), state, platform) 108 .map_err(EmulationError::PlatformEmulationError)?; 109 let op1_value = get_op(&insn, 1, std::mem::size_of::<$imm>(), state, platform) 110 .map_err(EmulationError::PlatformEmulationError)?; 111 112 let cpazso = calc_rflags_cpazso(op0_value, op1_value, std::mem::size_of::<$bound>()); 113 114 state.set_flags((state.flags() & !FLAGS_MASK) | cpazso); 115 116 Ok(()) 117 } 118 }; 119 } 120 121 pub struct Cmp_rm64_r64; 122 impl<T: CpuStateManager> InstructionHandler<T> for Cmp_rm64_r64 { 123 cmp_rm_r!(u64); 124 } 125 126 pub struct Cmp_rm32_r32; 127 impl<T: CpuStateManager> InstructionHandler<T> for Cmp_rm32_r32 { 128 cmp_rm_r!(u32); 129 } 130 131 pub struct Cmp_rm16_r16; 132 impl<T: CpuStateManager> InstructionHandler<T> for Cmp_rm16_r16 { 133 cmp_rm_r!(u16); 134 } 135 136 pub struct Cmp_rm8_r8; 137 impl<T: CpuStateManager> InstructionHandler<T> for Cmp_rm8_r8 { 138 cmp_rm_r!(u8); 139 } 140 141 pub struct Cmp_r64_rm64; 142 impl<T: CpuStateManager> InstructionHandler<T> for Cmp_r64_rm64 { 143 cmp_r_rm!(u64); 144 } 145 146 pub struct Cmp_r32_rm32; 147 impl<T: CpuStateManager> InstructionHandler<T> for Cmp_r32_rm32 { 148 cmp_r_rm!(u32); 149 } 150 151 pub struct Cmp_r16_rm16; 152 impl<T: CpuStateManager> InstructionHandler<T> for Cmp_r16_rm16 { 153 cmp_r_rm!(u16); 154 } 155 156 pub struct Cmp_r8_rm8; 157 impl<T: CpuStateManager> InstructionHandler<T> for Cmp_r8_rm8 { 158 cmp_r_rm!(u8); 159 } 160 161 pub struct Cmp_AL_imm8; 162 impl<T: CpuStateManager> InstructionHandler<T> for Cmp_AL_imm8 { 163 cmp_rm_imm!(u8, u8); 164 } 165 166 pub struct Cmp_AX_imm16; 167 impl<T: CpuStateManager> InstructionHandler<T> for Cmp_AX_imm16 { 168 cmp_rm_imm!(u16, u16); 169 } 170 171 pub struct Cmp_EAX_imm32; 172 impl<T: CpuStateManager> InstructionHandler<T> for Cmp_EAX_imm32 { 173 cmp_rm_imm!(u32, u32); 174 } 175 176 pub struct Cmp_RAX_imm32; 177 impl<T: CpuStateManager> InstructionHandler<T> for Cmp_RAX_imm32 { 178 cmp_rm_imm!(u32, u64); 179 } 180 181 pub struct Cmp_rm8_imm8; 182 impl<T: CpuStateManager> InstructionHandler<T> for Cmp_rm8_imm8 { 183 cmp_rm_imm!(u8, u8); 184 } 185 186 pub struct Cmp_rm16_imm16; 187 impl<T: CpuStateManager> InstructionHandler<T> for Cmp_rm16_imm16 { 188 cmp_rm_imm!(u16, u16); 189 } 190 191 pub struct Cmp_rm32_imm32; 192 impl<T: CpuStateManager> InstructionHandler<T> for Cmp_rm32_imm32 { 193 cmp_rm_imm!(u32, u32); 194 } 195 196 pub struct Cmp_rm64_imm32; 197 impl<T: CpuStateManager> InstructionHandler<T> for Cmp_rm64_imm32 { 198 cmp_rm_imm!(u32, u64); 199 } 200 201 pub struct Cmp_rm16_imm8; 202 impl<T: CpuStateManager> InstructionHandler<T> for Cmp_rm16_imm8 { 203 cmp_rm_imm!(u8, u16); 204 } 205 206 pub struct Cmp_rm32_imm8; 207 impl<T: CpuStateManager> InstructionHandler<T> for Cmp_rm32_imm8 { 208 cmp_rm_imm!(u8, u32); 209 } 210 211 pub struct Cmp_rm64_imm8; 212 impl<T: CpuStateManager> InstructionHandler<T> for Cmp_rm64_imm8 { 213 cmp_rm_imm!(u8, u64); 214 } 215 216 #[cfg(test)] 217 mod tests { 218 #![allow(unused_mut)] 219 220 use super::*; 221 use crate::arch::x86::emulator::mock_vmm::*; 222 223 #[test] 224 // cmp ah,al 225 fn test_cmp_rm8_r8_1() { 226 let rax: u64 = 0x0; 227 let ip: u64 = 0x1000; 228 let cpu_id = 0; 229 let insn = [0x38, 0xc4]; // cmp ah,al 230 let mut vmm = MockVmm::new(ip, vec![(Register::RAX, rax)], None); 231 assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok()); 232 233 let rflags: u64 = vmm.cpu_state(cpu_id).unwrap().flags() & FLAGS_MASK; 234 assert_eq!(0b1000100, rflags); 235 } 236 237 #[test] 238 // cmp eax,100 239 fn test_cmp_rm32_imm8_1() { 240 let rax: u64 = 0xabcdef; 241 let ip: u64 = 0x1000; 242 let cpu_id = 0; 243 let insn = [0x83, 0xf8, 0x64]; // cmp eax,100 244 let mut vmm = MockVmm::new(ip, vec![(Register::RAX, rax)], None); 245 assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok()); 246 247 let rflags: u64 = vmm.cpu_state(cpu_id).unwrap().flags() & FLAGS_MASK; 248 assert_eq!(0b100, rflags); 249 } 250 251 #[test] 252 // cmp eax,-1 253 fn test_cmp_rm32_imm8_2() { 254 let rax: u64 = 0xabcdef; 255 let ip: u64 = 0x1000; 256 let cpu_id = 0; 257 let insn = [0x83, 0xf8, 0xff]; // cmp eax,-1 258 let mut vmm = MockVmm::new(ip, vec![(Register::RAX, rax)], None); 259 assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok()); 260 261 let rflags: u64 = vmm.cpu_state(cpu_id).unwrap().flags() & FLAGS_MASK; 262 assert_eq!(0b101, rflags); 263 } 264 265 #[test] 266 // cmp rax,rbx 267 fn test_cmp_rm64_r64() { 268 let rax: u64 = 0xabcdef; 269 let rbx: u64 = 0x1234; 270 let ip: u64 = 0x1000; 271 let cpu_id = 0; 272 let insn = [0x48, 0x39, 0xd8, 0x00, 0xc3]; // cmp rax,rbx + two bytes garbage 273 let mut vmm = MockVmm::new(ip, vec![(Register::RAX, rax), (Register::RBX, rbx)], None); 274 assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok()); 275 276 let rflags: u64 = vmm.cpu_state(cpu_id).unwrap().flags() & FLAGS_MASK; 277 assert_eq!(0b100, rflags); 278 } 279 280 #[test] 281 fn test_cmp_64() { 282 let data = [ 283 (0xabcdef, 0x1234, 0b100), 284 (0x0, 0x101, 0b1001_0101), 285 (0x0, 0x8000_0000_0000_0000, 0b1000_1000_0101), 286 (0x1234abcd, 0x1234abcd, 0b100_0100), 287 (0x1234abcd, 0xdeadbeef, 0b1001_0101), 288 (0xffff_ffff_ffff_ffff, 0xdeadbeef, 0b1000_0000), 289 (0xffff_ffff_ffff_ffff, 0x0, 0b1000_0100), 290 ]; 291 292 for d in data.iter() { 293 let rax = d.0; 294 let rbx = d.1; 295 let insn = [0x48, 0x39, 0xd8]; // cmp rax,rbx 296 let mut vmm = MockVmm::new( 297 0x1000, 298 vec![(Register::RAX, rax), (Register::RBX, rbx)], 299 None, 300 ); 301 assert!(vmm.emulate_first_insn(0, &insn).is_ok()); 302 303 let rflags: u64 = vmm.cpu_state(0).unwrap().flags() & FLAGS_MASK; 304 assert_eq!(d.2, rflags); 305 } 306 } 307 308 #[test] 309 fn test_cmp_32() { 310 let data = [ 311 (0xabcdef, 0x1234, 0b100), 312 (0x0, 0x101, 0b1001_0101), 313 (0x0, 0x8000_0000_0000_0000, 0b100_0100), // Same as cmp 0,0 due to truncation 314 (0x1234abcd, 0x1234abcd, 0b100_0100), 315 (0x1234abcd, 0xdeadbeef, 0b1_0101), 316 (0xffff_ffff_ffff_ffff, 0xdeadbeef, 0b0), // Same as cmp 0xffffffff,0xdeadbeef 317 (0xffff_ffff, 0x0, 0b1000_0100), 318 ]; 319 320 for d in data.iter() { 321 let rax = d.0; 322 let rbx = d.1; 323 let insn = [0x39, 0xd8]; // cmp eax,ebx 324 let mut vmm = MockVmm::new( 325 0x1000, 326 vec![(Register::RAX, rax), (Register::RBX, rbx)], 327 None, 328 ); 329 assert!(vmm.emulate_first_insn(0, &insn).is_ok()); 330 331 let rflags: u64 = vmm.cpu_state(0).unwrap().flags() & FLAGS_MASK; 332 assert_eq!(d.2, rflags); 333 } 334 } 335 } 336