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 arithmetic 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 = u64::from(result & (!0u64 >> (63 - msb_shift)) == 0) << ZF_SHIFT; 42 43 let sf = ((result >> msb_shift) & 0x1) << SF_SHIFT; 44 45 // Overflow happens when two operands have the same sign but the result has a different sign. 46 let of = ((((op0 ^ op1) & (op0 ^ result)) >> msb_shift) & 0x1) << OF_SHIFT; 47 48 cf | pf | af | zf | sf | of 49 } 50 51 macro_rules! cmp_rm_r { 52 ($bound:ty) => { 53 fn emulate( 54 &self, 55 insn: &Instruction, 56 state: &mut T, 57 platform: &mut dyn PlatformEmulator<CpuState = T>, 58 ) -> Result<(), EmulationError<Exception>> { 59 let op0_value = get_op(&insn, 0, std::mem::size_of::<$bound>(), state, platform) 60 .map_err(EmulationError::PlatformEmulationError)?; 61 let op1_value = get_op(&insn, 1, std::mem::size_of::<$bound>(), state, platform) 62 .map_err(EmulationError::PlatformEmulationError)?; 63 64 let cpazso = calc_rflags_cpazso(op0_value, op1_value, std::mem::size_of::<$bound>()); 65 66 state.set_flags((state.flags() & !FLAGS_MASK) | cpazso); 67 68 Ok(()) 69 } 70 }; 71 } 72 73 macro_rules! cmp_r_rm { 74 ($bound:ty) => { 75 fn emulate( 76 &self, 77 insn: &Instruction, 78 state: &mut T, 79 platform: &mut dyn PlatformEmulator<CpuState = T>, 80 ) -> Result<(), EmulationError<Exception>> { 81 let op0_value = get_op(&insn, 0, std::mem::size_of::<$bound>(), state, platform) 82 .map_err(EmulationError::PlatformEmulationError)?; 83 let op1_value = get_op(&insn, 1, std::mem::size_of::<$bound>(), state, platform) 84 .map_err(EmulationError::PlatformEmulationError)?; 85 86 let cpazso = calc_rflags_cpazso(op0_value, op1_value, std::mem::size_of::<$bound>()); 87 88 state.set_flags((state.flags() & !FLAGS_MASK) | cpazso); 89 90 Ok(()) 91 } 92 }; 93 } 94 95 macro_rules! cmp_rm_imm { 96 ($imm:ty, $bound:ty) => { 97 fn emulate( 98 &self, 99 insn: &Instruction, 100 state: &mut T, 101 platform: &mut dyn PlatformEmulator<CpuState = T>, 102 ) -> Result<(), EmulationError<Exception>> { 103 let op0_value = get_op(&insn, 0, std::mem::size_of::<$bound>(), state, platform) 104 .map_err(EmulationError::PlatformEmulationError)?; 105 let op1_value = get_op(&insn, 1, std::mem::size_of::<$imm>(), state, platform) 106 .map_err(EmulationError::PlatformEmulationError)?; 107 108 let cpazso = calc_rflags_cpazso(op0_value, op1_value, std::mem::size_of::<$bound>()); 109 110 state.set_flags((state.flags() & !FLAGS_MASK) | cpazso); 111 112 Ok(()) 113 } 114 }; 115 } 116 117 pub struct Cmp_rm64_r64; 118 impl<T: CpuStateManager> InstructionHandler<T> for Cmp_rm64_r64 { 119 cmp_rm_r!(u64); 120 } 121 122 pub struct Cmp_rm32_r32; 123 impl<T: CpuStateManager> InstructionHandler<T> for Cmp_rm32_r32 { 124 cmp_rm_r!(u32); 125 } 126 127 pub struct Cmp_rm16_r16; 128 impl<T: CpuStateManager> InstructionHandler<T> for Cmp_rm16_r16 { 129 cmp_rm_r!(u16); 130 } 131 132 pub struct Cmp_rm8_r8; 133 impl<T: CpuStateManager> InstructionHandler<T> for Cmp_rm8_r8 { 134 cmp_rm_r!(u8); 135 } 136 137 pub struct Cmp_r64_rm64; 138 impl<T: CpuStateManager> InstructionHandler<T> for Cmp_r64_rm64 { 139 cmp_r_rm!(u64); 140 } 141 142 pub struct Cmp_r32_rm32; 143 impl<T: CpuStateManager> InstructionHandler<T> for Cmp_r32_rm32 { 144 cmp_r_rm!(u32); 145 } 146 147 pub struct Cmp_r16_rm16; 148 impl<T: CpuStateManager> InstructionHandler<T> for Cmp_r16_rm16 { 149 cmp_r_rm!(u16); 150 } 151 152 pub struct Cmp_r8_rm8; 153 impl<T: CpuStateManager> InstructionHandler<T> for Cmp_r8_rm8 { 154 cmp_r_rm!(u8); 155 } 156 157 pub struct Cmp_AL_imm8; 158 impl<T: CpuStateManager> InstructionHandler<T> for Cmp_AL_imm8 { 159 cmp_rm_imm!(u8, u8); 160 } 161 162 pub struct Cmp_AX_imm16; 163 impl<T: CpuStateManager> InstructionHandler<T> for Cmp_AX_imm16 { 164 cmp_rm_imm!(u16, u16); 165 } 166 167 pub struct Cmp_EAX_imm32; 168 impl<T: CpuStateManager> InstructionHandler<T> for Cmp_EAX_imm32 { 169 cmp_rm_imm!(u32, u32); 170 } 171 172 pub struct Cmp_RAX_imm32; 173 impl<T: CpuStateManager> InstructionHandler<T> for Cmp_RAX_imm32 { 174 cmp_rm_imm!(u32, u64); 175 } 176 177 pub struct Cmp_rm8_imm8; 178 impl<T: CpuStateManager> InstructionHandler<T> for Cmp_rm8_imm8 { 179 cmp_rm_imm!(u8, u8); 180 } 181 182 pub struct Cmp_rm16_imm16; 183 impl<T: CpuStateManager> InstructionHandler<T> for Cmp_rm16_imm16 { 184 cmp_rm_imm!(u16, u16); 185 } 186 187 pub struct Cmp_rm32_imm32; 188 impl<T: CpuStateManager> InstructionHandler<T> for Cmp_rm32_imm32 { 189 cmp_rm_imm!(u32, u32); 190 } 191 192 pub struct Cmp_rm64_imm32; 193 impl<T: CpuStateManager> InstructionHandler<T> for Cmp_rm64_imm32 { 194 cmp_rm_imm!(u32, u64); 195 } 196 197 pub struct Cmp_rm16_imm8; 198 impl<T: CpuStateManager> InstructionHandler<T> for Cmp_rm16_imm8 { 199 cmp_rm_imm!(u8, u16); 200 } 201 202 pub struct Cmp_rm32_imm8; 203 impl<T: CpuStateManager> InstructionHandler<T> for Cmp_rm32_imm8 { 204 cmp_rm_imm!(u8, u32); 205 } 206 207 pub struct Cmp_rm64_imm8; 208 impl<T: CpuStateManager> InstructionHandler<T> for Cmp_rm64_imm8 { 209 cmp_rm_imm!(u8, u64); 210 } 211 212 #[cfg(test)] 213 mod tests { 214 #![allow(unused_mut)] 215 216 use super::*; 217 use crate::arch::x86::emulator::mock_vmm::*; 218 219 #[test] 220 // cmp ah,al 221 fn test_cmp_rm8_r8_1() { 222 let rax: u64 = 0x0; 223 let ip: u64 = 0x1000; 224 let cpu_id = 0; 225 let insn = [0x38, 0xc4]; // cmp ah,al 226 let mut vmm = MockVmm::new(ip, vec![(Register::RAX, rax)], None); 227 assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok()); 228 229 let rflags: u64 = vmm.cpu_state(cpu_id).unwrap().flags() & FLAGS_MASK; 230 assert_eq!(0b1000100, rflags); 231 } 232 233 #[test] 234 // cmp eax,100 235 fn test_cmp_rm32_imm8_1() { 236 let rax: u64 = 0xabcdef; 237 let ip: u64 = 0x1000; 238 let cpu_id = 0; 239 let insn = [0x83, 0xf8, 0x64]; // cmp eax,100 240 let mut vmm = MockVmm::new(ip, vec![(Register::RAX, rax)], None); 241 assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok()); 242 243 let rflags: u64 = vmm.cpu_state(cpu_id).unwrap().flags() & FLAGS_MASK; 244 assert_eq!(0b100, rflags); 245 } 246 247 #[test] 248 // cmp eax,-1 249 fn test_cmp_rm32_imm8_2() { 250 let rax: u64 = 0xabcdef; 251 let ip: u64 = 0x1000; 252 let cpu_id = 0; 253 let insn = [0x83, 0xf8, 0xff]; // cmp eax,-1 254 let mut vmm = MockVmm::new(ip, vec![(Register::RAX, rax)], None); 255 assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok()); 256 257 let rflags: u64 = vmm.cpu_state(cpu_id).unwrap().flags() & FLAGS_MASK; 258 assert_eq!(0b101, rflags); 259 } 260 261 #[test] 262 // cmp rax,rbx 263 fn test_cmp_rm64_r64() { 264 let rax: u64 = 0xabcdef; 265 let rbx: u64 = 0x1234; 266 let ip: u64 = 0x1000; 267 let cpu_id = 0; 268 let insn = [0x48, 0x39, 0xd8, 0x00, 0xc3]; // cmp rax,rbx + two bytes garbage 269 let mut vmm = MockVmm::new(ip, vec![(Register::RAX, rax), (Register::RBX, rbx)], None); 270 assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok()); 271 272 let rflags: u64 = vmm.cpu_state(cpu_id).unwrap().flags() & FLAGS_MASK; 273 assert_eq!(0b100, rflags); 274 } 275 276 #[test] 277 fn test_cmp_64() { 278 let data = [ 279 (0xabcdef, 0x1234, 0b100), 280 (0x0, 0x101, 0b1001_0101), 281 (0x0, 0x8000_0000_0000_0000, 0b1000_1000_0101), 282 (0x1234abcd, 0x1234abcd, 0b100_0100), 283 (0x1234abcd, 0xdeadbeef, 0b1001_0101), 284 (0xffff_ffff_ffff_ffff, 0xdeadbeef, 0b1000_0000), 285 (0xffff_ffff_ffff_ffff, 0x0, 0b1000_0100), 286 ]; 287 288 for d in data.iter() { 289 let rax = d.0; 290 let rbx = d.1; 291 let insn = [0x48, 0x39, 0xd8]; // cmp rax,rbx 292 let mut vmm = MockVmm::new( 293 0x1000, 294 vec![(Register::RAX, rax), (Register::RBX, rbx)], 295 None, 296 ); 297 assert!(vmm.emulate_first_insn(0, &insn).is_ok()); 298 299 let rflags: u64 = vmm.cpu_state(0).unwrap().flags() & FLAGS_MASK; 300 assert_eq!(d.2, rflags); 301 } 302 } 303 304 #[test] 305 fn test_cmp_32() { 306 let data = [ 307 (0xabcdef, 0x1234, 0b100), 308 (0x0, 0x101, 0b1001_0101), 309 (0x0, 0x8000_0000_0000_0000, 0b100_0100), // Same as cmp 0,0 due to truncation 310 (0x1234abcd, 0x1234abcd, 0b100_0100), 311 (0x1234abcd, 0xdeadbeef, 0b1_0101), 312 (0xffff_ffff_ffff_ffff, 0xdeadbeef, 0b0), // Same as cmp 0xffffffff,0xdeadbeef 313 (0xffff_ffff, 0x0, 0b1000_0100), 314 ]; 315 316 for d in data.iter() { 317 let rax = d.0; 318 let rbx = d.1; 319 let insn = [0x39, 0xd8]; // cmp eax,ebx 320 let mut vmm = MockVmm::new( 321 0x1000, 322 vec![(Register::RAX, rax), (Register::RBX, rbx)], 323 None, 324 ); 325 assert!(vmm.emulate_first_insn(0, &insn).is_ok()); 326 327 let rflags: u64 = vmm.cpu_state(0).unwrap().flags() & FLAGS_MASK; 328 assert_eq!(d.2, rflags); 329 } 330 } 331 } 332