|
| 1 | +use iced_x86::{IcedError, Register}; |
| 2 | +use rand::Rng; |
| 3 | + |
| 4 | +use crate::assembler::{ |
| 5 | + amd64_assembler::IAmd64Assembler, fast_amd64_assembler::FastAmd64Assembler, |
| 6 | + iced_amd64_assembler::IcedAmd64Assembler, |
| 7 | +}; |
| 8 | + |
| 9 | +/// Differential tester that compares FastAmd64Assembler against IcedAmd64Assembler |
| 10 | +pub struct Amd64AssemblerDifferentialTester { |
| 11 | + rand: rand::rngs::ThreadRng, |
| 12 | + registers: Vec<Register>, |
| 13 | + iced_assembler: IcedAmd64Assembler, |
| 14 | + fast_assembler: FastAmd64Assembler, |
| 15 | +} |
| 16 | + |
| 17 | +impl Amd64AssemblerDifferentialTester { |
| 18 | + /// Creates a new differential tester with the given buffer |
| 19 | + pub unsafe fn new(buffer: *mut u8) -> Result<Self, IcedError> { |
| 20 | + let registers = vec![ |
| 21 | + Register::RAX, |
| 22 | + Register::RCX, |
| 23 | + Register::RDX, |
| 24 | + Register::RBX, |
| 25 | + Register::RSI, |
| 26 | + Register::RDI, |
| 27 | + Register::RSP, |
| 28 | + Register::RBP, |
| 29 | + Register::R8, |
| 30 | + Register::R9, |
| 31 | + Register::R10, |
| 32 | + Register::R11, |
| 33 | + Register::R12, |
| 34 | + Register::R13, |
| 35 | + Register::R14, |
| 36 | + Register::R15, |
| 37 | + ]; |
| 38 | + |
| 39 | + Ok(Self { |
| 40 | + rand: rand::thread_rng(), |
| 41 | + registers, |
| 42 | + iced_assembler: IcedAmd64Assembler::new()?, |
| 43 | + fast_assembler: FastAmd64Assembler { |
| 44 | + p: buffer, |
| 45 | + offset: 0, |
| 46 | + }, |
| 47 | + }) |
| 48 | + } |
| 49 | + |
| 50 | + pub fn test() -> Result<(), Box<dyn std::error::Error>> { |
| 51 | + let mut buffer = vec![0u8; 64 * 4096]; |
| 52 | + let ptr = buffer.as_mut_ptr(); |
| 53 | + |
| 54 | + unsafe { |
| 55 | + let mut tester = Self::new(ptr)?; |
| 56 | + tester.run()?; |
| 57 | + } |
| 58 | + |
| 59 | + Ok(()) |
| 60 | + } |
| 61 | + |
| 62 | + pub fn run(&mut self) -> Result<(), Box<dyn std::error::Error>> { |
| 63 | + for i in 0..self.registers.len() { |
| 64 | + let reg1 = self.registers[i]; |
| 65 | + self.diff_reg_insts(reg1)?; |
| 66 | + |
| 67 | + for j in (i + 1)..self.registers.len() { |
| 68 | + let reg2 = self.registers[j]; |
| 69 | + self.diff_reg_reg_insts(reg1, reg2)?; |
| 70 | + } |
| 71 | + } |
| 72 | + |
| 73 | + println!("All differential tests passed!"); |
| 74 | + Ok(()) |
| 75 | + } |
| 76 | + |
| 77 | + fn diff_reg_insts(&mut self, reg: Register) -> Result<(), Box<dyn std::error::Error>> { |
| 78 | + self.diff("PushReg", |asm| asm.push_reg(reg))?; |
| 79 | + self.diff("PopReg", |asm| asm.pop_reg(reg))?; |
| 80 | + self.diff("NotReg", |asm| asm.not_reg(reg))?; |
| 81 | + self.diff("ShlRegCl", |asm| asm.shl_reg_cl(reg))?; |
| 82 | + self.diff("ShrRegCl", |asm| asm.shr_reg_cl(reg))?; |
| 83 | + self.diff("CallReg", |asm| asm.call_reg(reg))?; |
| 84 | + |
| 85 | + // Test reg, constant instructions |
| 86 | + for _ in 0..100 { |
| 87 | + let c = self.rand.gen::<i64>() as u64; |
| 88 | + |
| 89 | + self.diff("MovabsRegImm64", |asm| asm.movabs_reg_imm64(reg, c))?; |
| 90 | + self.diff("AddRegImm32", |asm| asm.add_reg_imm32(reg, c as u32))?; |
| 91 | + self.diff("SubRegImm32", |asm| asm.sub_reg_imm32(reg, c as u32))?; |
| 92 | + self.diff("AndRegImm32", |asm| asm.and_reg_imm32(reg, c as u32))?; |
| 93 | + self.diff("ShrRegImm8", |asm| asm.shr_reg_imm8(reg, c as u8))?; |
| 94 | + |
| 95 | + if reg != Register::RSP { |
| 96 | + self.diff("PushMem64", |asm| asm.push_mem64(reg, c as i32))?; |
| 97 | + } |
| 98 | + } |
| 99 | + |
| 100 | + Ok(()) |
| 101 | + } |
| 102 | + |
| 103 | + fn diff_reg_reg_insts( |
| 104 | + &mut self, |
| 105 | + reg1: Register, |
| 106 | + reg2: Register, |
| 107 | + ) -> Result<(), Box<dyn std::error::Error>> { |
| 108 | + // Test reg, reg instructions |
| 109 | + self.diff("MovRegReg", |asm| asm.mov_reg_reg(reg1, reg2))?; |
| 110 | + self.diff("MovRegReg", |asm| asm.mov_reg_reg(reg2, reg1))?; |
| 111 | + self.diff("AddRegReg", |asm| asm.add_reg_reg(reg1, reg2))?; |
| 112 | + self.diff("AddRegReg", |asm| asm.add_reg_reg(reg2, reg1))?; |
| 113 | + self.diff("AndRegReg", |asm| asm.and_reg_reg(reg1, reg2))?; |
| 114 | + self.diff("AndRegReg", |asm| asm.and_reg_reg(reg2, reg1))?; |
| 115 | + self.diff("OrRegReg", |asm| asm.or_reg_reg(reg1, reg2))?; |
| 116 | + self.diff("OrRegReg", |asm| asm.or_reg_reg(reg2, reg1))?; |
| 117 | + self.diff("XorRegReg", |asm| asm.xor_reg_reg(reg1, reg2))?; |
| 118 | + self.diff("XorRegReg", |asm| asm.xor_reg_reg(reg2, reg1))?; |
| 119 | + self.diff("ImulRegReg", |asm| asm.imul_reg_reg(reg1, reg2))?; |
| 120 | + self.diff("ImulRegReg", |asm| asm.imul_reg_reg(reg2, reg1))?; |
| 121 | + |
| 122 | + // Test reg, reg, constant instructions |
| 123 | + for _ in 0..100 { |
| 124 | + let c = self.rand.gen::<i32>(); |
| 125 | + |
| 126 | + self.diff("MovMem64Reg", |asm| asm.mov_mem64_reg(reg1, c, reg2))?; |
| 127 | + self.diff("MovMem64Reg", |asm| asm.mov_mem64_reg(reg2, c, reg1))?; |
| 128 | + self.diff("MovRegMem64", |asm| asm.mov_reg_mem64(reg1, reg2, c))?; |
| 129 | + self.diff("MovRegMem64", |asm| asm.mov_reg_mem64(reg2, reg1, c))?; |
| 130 | + self.diff("AndMem64Reg", |asm| asm.and_mem64_reg(reg1, c, reg2))?; |
| 131 | + self.diff("AndMem64Reg", |asm| asm.and_mem64_reg(reg2, c, reg1))?; |
| 132 | + } |
| 133 | + |
| 134 | + Ok(()) |
| 135 | + } |
| 136 | + |
| 137 | + /// Executes a test function on both assemblers and compares results |
| 138 | + fn diff<F>(&mut self, method_name: &str, func: F) -> Result<(), Box<dyn std::error::Error>> |
| 139 | + where |
| 140 | + F: Fn(&mut dyn IAmd64Assembler), |
| 141 | + { |
| 142 | + // Assemble the instruction using both assemblers |
| 143 | + func(&mut self.iced_assembler); |
| 144 | + func(&mut self.fast_assembler); |
| 145 | + |
| 146 | + // Compare the results |
| 147 | + self.compare(method_name)?; |
| 148 | + |
| 149 | + // Reset both assemblers |
| 150 | + self.iced_assembler.reset(); |
| 151 | + self.fast_assembler.reset(); |
| 152 | + |
| 153 | + Ok(()) |
| 154 | + } |
| 155 | + |
| 156 | + /// Compares the output of both assemblers |
| 157 | + fn compare(&mut self, method_name: &str) -> Result<(), Box<dyn std::error::Error>> { |
| 158 | + let iced_insts = self.iced_assembler.get_instructions(); |
| 159 | + let iced_bytes = self.iced_assembler.get_bytes(); |
| 160 | + let our_insts = self.fast_assembler.get_instructions(); |
| 161 | + let our_bytes = self.fast_assembler.get_bytes(); |
| 162 | + |
| 163 | + if iced_insts.is_empty() || iced_bytes.is_empty() || iced_insts.len() != our_insts.len() { |
| 164 | + return Err(format!( |
| 165 | + "Method {} failed: instruction count mismatch (iced: {}, ours: {})", |
| 166 | + method_name, |
| 167 | + iced_insts.len(), |
| 168 | + our_insts.len() |
| 169 | + ) |
| 170 | + .into()); |
| 171 | + } |
| 172 | + |
| 173 | + // Check if instructions are equivalent |
| 174 | + if iced_insts.len() == 1 && our_insts.len() == 1 { |
| 175 | + let iced_str = format!("{}", iced_insts[0]); |
| 176 | + let our_str = format!("{}", our_insts[0]); |
| 177 | + |
| 178 | + if iced_str != our_str { |
| 179 | + return Err(format!( |
| 180 | + "Method {} failed: Instruction '{}' and '{}' not equivalent!\nIced bytes: {:?}\nOur bytes: {:?}", |
| 181 | + method_name, |
| 182 | + iced_str, |
| 183 | + our_str, |
| 184 | + iced_bytes, |
| 185 | + our_bytes |
| 186 | + ).into()); |
| 187 | + } |
| 188 | + } else { |
| 189 | + // Compare all instructions |
| 190 | + for (i, (iced_inst, our_inst)) in iced_insts.iter().zip(our_insts.iter()).enumerate() { |
| 191 | + let iced_str = format!("{}", iced_inst); |
| 192 | + let our_str = format!("{}", our_inst); |
| 193 | + |
| 194 | + if iced_str != our_str { |
| 195 | + return Err(format!( |
| 196 | + "Method {} failed at instruction {}: '{}' != '{}'", |
| 197 | + method_name, i, iced_str, our_str |
| 198 | + ) |
| 199 | + .into()); |
| 200 | + } |
| 201 | + } |
| 202 | + } |
| 203 | + |
| 204 | + Ok(()) |
| 205 | + } |
| 206 | +} |
0 commit comments