unsafe extern "C" { // unsafe fn parse_gpr(src: *mut Source, prefix: u8) -> (u64, u64); // unsafe fn parse_reg(src: *mut Source) -> (u64, u64); unsafe fn try_parse_reg(_: *const u8) -> (usize, Register); unsafe fn try_parse_mem(_: *const u8, _: *mut Operand) -> (usize, *const Operand); #[link_name = "buf"] static mut BUF: [u8; 0x100]; } #[repr(C)] #[derive(Debug, PartialEq, Eq)] struct Register { size: u32, num: u32, } #[repr(C)] #[derive(Debug, PartialEq, Eq)] struct Operand { kind: u32, size: u32, reg: Register, index: Register, scale: u32, disp: i32, } impl Register { fn new(num: u32, size: u32) -> Self { Self { num, size: size.ilog2(), } } fn invalid() -> Self { Self { num: !0, size: !0 } } } #[cfg(test)] mod tests { use super::*; use core::mem::MaybeUninit; fn write_to_buf(b: &[u8]) { assert!(b.len() <= 0x100, "Buffer overflow"); unsafe { core::ptr::copy_nonoverlapping(b.as_ptr(), (&raw mut BUF).cast(), b.len()); } } #[test] fn parse_mem() { let mut op = MaybeUninit::::uninit(); // let (n, _) = // unsafe { try_parse_mem(b"qword [rax + rbx * 2 + 0x10]\0".as_ptr(), op.as_mut_ptr()) }; // assert_eq!( // (n, unsafe { op.assume_init_ref() }), // ( // 28, // &Operand { // kind: 2, // size: 3, // reg: Register::new(0, 8), // index: Register::new(3, 8), // scale: 2, // disp: 0x10, // } // ) // ); let (n, _) = unsafe { try_parse_mem(b"byte [r12 + r13 * 4 - 0x20]\0".as_ptr(), op.as_mut_ptr()) }; assert_eq!( (n, unsafe { op.assume_init_ref() }), ( 27, &Operand { kind: 2, size: 0, reg: Register::new(12, 8), index: Register::new(13, 8), scale: 4, disp: -0x20, } ) ); let (n, _) = unsafe { try_parse_mem(b"word [r8 * 8 + 5]\0".as_ptr(), op.as_mut_ptr()) }; assert_eq!( (n, unsafe { op.assume_init_ref() }), ( 17, &Operand { kind: 2, size: 1, reg: Register::invalid(), index: Register::new(8, 8), scale: 8, disp: 5, } ) ); } #[test] #[ignore] fn parse_reg() { let cases = [ (&b"rax"[..], (0, 8)), (&b"rcx"[..], (1, 8)), (&b"rdx"[..], (2, 8)), (&b"rbx"[..], (3, 8)), (&b"rsp"[..], (4, 8)), (&b"rbp"[..], (5, 8)), (&b"rsi"[..], (6, 8)), (&b"rdi"[..], (7, 8)), (&b"r8"[..], (8, 8)), (&b"r9"[..], (9, 8)), (&b"r10"[..], (10, 8)), (&b"r11"[..], (11, 8)), (&b"r12"[..], (12, 8)), (&b"r13"[..], (13, 8)), (&b"r14"[..], (14, 8)), (&b"r15"[..], (15, 8)), (&b"rip"[..], (!0, 8)), (&b"eax"[..], (0, 4)), (&b"ecx"[..], (1, 4)), (&b"edx"[..], (2, 4)), (&b"ebx"[..], (3, 4)), (&b"esp"[..], (4, 4)), (&b"ebp"[..], (5, 4)), (&b"esi"[..], (6, 4)), (&b"edi"[..], (7, 4)), (&b"r8d"[..], (8, 4)), (&b"r9d"[..], (9, 4)), (&b"r10d"[..], (10, 4)), (&b"r11d"[..], (11, 4)), (&b"r12d"[..], (12, 4)), (&b"r13d"[..], (13, 4)), (&b"r14d"[..], (14, 4)), (&b"r15d"[..], (15, 4)), (&b"r8w"[..], (8, 2)), (&b"r9w"[..], (9, 2)), (&b"r10w"[..], (10, 2)), (&b"r11w"[..], (11, 2)), (&b"r12w"[..], (12, 2)), (&b"r13w"[..], (13, 2)), (&b"r14w"[..], (14, 2)), (&b"r15w"[..], (15, 2)), (&b"r8b"[..], (8, 1)), (&b"r9b"[..], (9, 1)), (&b"r10b"[..], (10, 1)), (&b"r11b"[..], (11, 1)), (&b"r12b"[..], (12, 1)), (&b"r13b"[..], (13, 1)), (&b"r14b"[..], (14, 1)), (&b"r15b"[..], (15, 1)), (&b"ax"[..], (0, 2)), (&b"cx"[..], (1, 2)), (&b"dx"[..], (2, 2)), (&b"bx"[..], (3, 2)), (&b"sp"[..], (4, 2)), (&b"bp"[..], (5, 2)), (&b"si"[..], (6, 2)), (&b"di"[..], (7, 2)), (&b"al"[..], (0, 1)), (&b"cl"[..], (1, 1)), (&b"dl"[..], (2, 1)), (&b"bl"[..], (3, 1)), (&b"spl"[..], (4, 1)), (&b"bpl"[..], (5, 1)), (&b"sil"[..], (6, 1)), (&b"dil"[..], (7, 1)), ]; for (text, (reg, size)) in cases { let (n, result) = unsafe { try_parse_reg(text.as_ptr()) }; assert_eq!( result, Register::new(reg, size), "Failed to parse '{}' n={n}", std::str::from_utf8(text).unwrap() ); } } }