use crate::codegen::{MachineFunction, MachineInstr, MachineOperand};
use crate::mc_streamer::x86_opcodes;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86Mode {
Mode16,
Mode32,
Mode64,
}
impl X86Mode {
pub fn is_64bit(self) -> bool {
matches!(self, X86Mode::Mode64)
}
pub fn default_operand_size(self) -> u8 {
match self {
X86Mode::Mode16 => 2,
X86Mode::Mode32 => 4,
X86Mode::Mode64 => 4,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct VexInfo {
pub vvvv: u8,
pub l: u8,
pub pp: u8,
pub r: u8,
pub x: u8,
pub b: u8,
pub w: u8,
pub m_mmmm: u8,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct EvexInfo {
pub vvvv: u8,
pub l: u8,
pub pp: u8,
pub r: u8,
pub x: u8,
pub b: u8,
pub w: u8,
pub m_mmmm: u8,
pub aaa: u8,
pub z: u8,
pub b_prime: u8,
pub mask_reg: u8,
}
mod mod_field {
pub const MEM_NO_DISP: u8 = 0b00;
pub const MEM_DISP8: u8 = 0b01;
pub const MEM_DISP32: u8 = 0b10;
pub const REG_DIRECT: u8 = 0b11;
}
pub struct X86MCDecoder {
pub mode: X86Mode,
pub pos: usize,
}
impl X86MCDecoder {
pub fn new(mode: X86Mode) -> Self {
Self { mode, pos: 0 }
}
pub fn decode_instruction(&self, bytes: &[u8], offset: usize) -> Option<(MachineInstr, usize)> {
if offset >= bytes.len() {
return None;
}
let mut pos = offset;
let (_prefix_bytes, prefix_count) = self.decode_prefixes(&bytes[pos..]);
pos += prefix_count;
if pos >= bytes.len() {
return None;
}
let (rex, rex_count) = self.decode_rex(&bytes[pos..]);
pos += rex_count;
let rex_byte = rex;
if pos >= bytes.len() {
return None;
}
let opcode_byte0 = bytes[pos];
pos += 1;
let opcode_bytes = if opcode_byte0 == 0x0F {
if pos >= bytes.len() {
return None;
}
let opcode_byte1 = bytes[pos];
pos += 1;
vec![opcode_byte0, opcode_byte1]
} else {
vec![opcode_byte0]
};
let mut modrm: Option<(u8, u8, u8)> = None;
let mut sib: Option<(u8, u8, u8)> = None;
if Self::opcode_has_modrm(&opcode_bytes) {
if pos >= bytes.len() {
return None;
}
let (mod_field_val, reg_field, rm_field, consumed) = self.decode_modrm(&bytes[pos..]);
pos += consumed;
modrm = Some((mod_field_val, reg_field, rm_field));
if rm_field == 4 && mod_field_val != mod_field::REG_DIRECT {
if pos >= bytes.len() {
return None;
}
let (scale, index, base, consumed) = self.decode_sib(&bytes[pos..]);
pos += consumed;
sib = Some((scale, index, base));
}
}
let disp = if let Some((mod_field_val, _, rm_field)) = modrm {
if mod_field_val != mod_field::REG_DIRECT
|| (mod_field_val == mod_field::MEM_NO_DISP && rm_field == 5)
{
if pos >= bytes.len() {
return None;
}
let (disp_val, consumed) = self.decode_displacement(&bytes[pos..], mod_field_val);
pos += consumed;
Some(disp_val)
} else {
None
}
} else {
None
};
let imm_size = Self::determine_immediate_size(&opcode_bytes, modrm.map(|(m, r, _)| (m, r)));
let immediate = if imm_size > 0 {
if pos + imm_size as usize > bytes.len() {
return None;
}
let (imm_val, consumed) = self.decode_immediate(&bytes[pos..], imm_size);
pos += consumed;
Some(imm_val)
} else {
None
};
let opcode = Self::lookup_simple_opcode(&opcode_bytes)
.or_else(|| Self::lookup_opcode(&opcode_bytes, modrm.map(|(_, r, _)| r)))
.unwrap_or(0);
let operands =
self.build_operands(opcode, &opcode_bytes, rex_byte, modrm, sib, disp, immediate);
let instr = MachineInstr {
opcode,
operands,
def: None,
size: 0,
};
Some((instr, pos - offset))
}
pub fn decode_function(&self, bytes: &[u8]) -> Option<MachineFunction> {
let mut mf = MachineFunction::new("decoded_func");
let mut mbb = crate::codegen::MachineBasicBlock {
name: String::new(),
instructions: Vec::new(),
successors: Vec::new(),
..Default::default()
};
let mut offset = 0;
while offset < bytes.len() {
if let Some((instr, consumed)) = self.decode_instruction(bytes, offset) {
mbb.instructions.push(instr);
offset += consumed;
} else {
offset += 1;
}
}
mf.push_block(mbb);
Some(mf)
}
pub fn decode_prefixes(&self, bytes: &[u8]) -> (Vec<u8>, usize) {
let mut prefixes = Vec::new();
let mut count = 0;
while count < bytes.len() {
match bytes[count] {
0xF0 | 0xF2 | 0xF3 | 0x2E | 0x36 | 0x3E | 0x26 | 0x64 | 0x65 | 0x66 | 0x67 => {
prefixes.push(bytes[count]);
count += 1;
}
_ => break,
}
}
(prefixes, count)
}
pub fn decode_rex(&self, bytes: &[u8]) -> (Option<u8>, usize) {
if self.mode.is_64bit() && !bytes.is_empty() {
let byte = bytes[0];
if (0x40..=0x4F).contains(&byte) {
return (Some(byte), 1);
}
}
(None, 0)
}
pub fn decode_modrm(&self, bytes: &[u8]) -> (u8, u8, u8, usize) {
if bytes.is_empty() {
return (0, 0, 0, 0);
}
let b = bytes[0];
let mod_field = (b >> 6) & 0x03;
let reg_field = (b >> 3) & 0x07;
let rm_field = b & 0x07;
(mod_field, reg_field, rm_field, 1)
}
pub fn decode_sib(&self, bytes: &[u8]) -> (u8, u8, u8, usize) {
if bytes.is_empty() {
return (0, 4, 0, 0);
}
let b = bytes[0];
let scale = (b >> 6) & 0x03;
let index = (b >> 3) & 0x07;
let base = b & 0x07;
(scale, index, base, 1)
}
pub fn decode_displacement(&self, bytes: &[u8], mod_field_val: u8) -> (i64, usize) {
match mod_field_val {
mod_field::MEM_DISP8 => {
if bytes.is_empty() {
return (0, 0);
}
(bytes[0] as i8 as i64, 1)
}
mod_field::MEM_DISP32 => {
if bytes.len() < 4 {
return (0, 0);
}
let disp = i32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
(disp as i64, 4)
}
mod_field::MEM_NO_DISP => {
(0, 0)
}
_ => (0, 0),
}
}
pub fn decode_immediate(&self, bytes: &[u8], size: u8) -> (i64, usize) {
let sz = size as usize;
if bytes.len() < sz {
return (0, 0);
}
let val = match size {
1 => bytes[0] as i8 as i64,
2 => i16::from_le_bytes([bytes[0], bytes[1]]) as i64,
4 => i32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]) as i64,
8 => i64::from_le_bytes([
bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
]),
_ => 0,
};
(val, sz)
}
fn build_operands(
&self,
opcode: u32,
opcode_bytes: &[u8],
rex: Option<u8>,
modrm: Option<(u8, u8, u8)>,
_sib: Option<(u8, u8, u8)>,
_disp: Option<i64>,
immediate: Option<i64>,
) -> Vec<MachineOperand> {
let mut operands = Vec::new();
match opcode {
x86_opcodes::NOP | x86_opcodes::RET => {
}
x86_opcodes::PUSH | x86_opcodes::POP => {
if opcode_bytes.len() == 1 && opcode_bytes[0] >= 0x50 && opcode_bytes[0] <= 0x57 {
let reg_field = opcode_bytes[0] & 0x07;
let phys_reg = self.decode_register(reg_field, rex, self.mode.is_64bit());
operands.push(MachineOperand::PhysReg(phys_reg as u32));
} else if opcode_bytes.len() == 1
&& opcode_bytes[0] >= 0x58
&& opcode_bytes[0] <= 0x5F
{
let reg_field = opcode_bytes[0] & 0x07;
let phys_reg = self.decode_register(reg_field, rex, self.mode.is_64bit());
operands.push(MachineOperand::PhysReg(phys_reg as u32));
} else if let Some((_mod, _reg, rm)) = modrm {
let rm_reg = self.decode_register(rm, rex, self.mode.is_64bit());
operands.push(MachineOperand::PhysReg(rm_reg as u32));
}
}
x86_opcodes::CALL | x86_opcodes::JMP | x86_opcodes::JE | x86_opcodes::JNE => {
if let Some(imm) = immediate {
operands.push(MachineOperand::Imm(imm));
}
}
x86_opcodes::MOV => {
if opcode_bytes.len() == 1 && opcode_bytes[0] >= 0xB8 && opcode_bytes[0] <= 0xBF {
let reg_field = opcode_bytes[0] & 0x07;
let dst_reg = self.decode_register(reg_field, rex, self.mode.is_64bit());
operands.push(MachineOperand::PhysReg(dst_reg as u32));
if let Some(imm) = immediate {
operands.push(MachineOperand::Imm(imm));
}
} else if let Some((_mod, reg, rm)) = modrm {
let dst_reg = self.decode_register(rm, rex, self.mode.is_64bit());
let src_reg = self.decode_register(reg, rex, self.mode.is_64bit());
operands.push(MachineOperand::PhysReg(dst_reg as u32));
operands.push(MachineOperand::PhysReg(src_reg as u32));
}
}
x86_opcodes::ADD
| x86_opcodes::SUB
| x86_opcodes::AND
| x86_opcodes::OR
| x86_opcodes::XOR
| x86_opcodes::CMP
| x86_opcodes::LEA => {
if let Some((_mod, reg, rm)) = modrm {
let dst_reg = self.decode_register(rm, rex, self.mode.is_64bit());
if let Some(imm) = immediate {
operands.push(MachineOperand::PhysReg(dst_reg as u32));
operands.push(MachineOperand::Imm(imm));
} else {
let src_reg = self.decode_register(reg, rex, self.mode.is_64bit());
operands.push(MachineOperand::PhysReg(dst_reg as u32));
operands.push(MachineOperand::PhysReg(src_reg as u32));
}
}
}
x86_opcodes::SHL | x86_opcodes::SHR => {
if let Some((_mod, _reg, rm)) = modrm {
let reg = self.decode_register(rm, rex, self.mode.is_64bit());
operands.push(MachineOperand::PhysReg(reg as u32));
}
}
x86_opcodes::INC | x86_opcodes::DEC => {
if let Some((_mod, _reg, rm)) = modrm {
let reg = self.decode_register(rm, rex, self.mode.is_64bit());
operands.push(MachineOperand::PhysReg(reg as u32));
}
}
x86_opcodes::NOT | x86_opcodes::NEG => {
if let Some((_mod, _reg, rm)) = modrm {
let reg = self.decode_register(rm, rex, self.mode.is_64bit());
operands.push(MachineOperand::PhysReg(reg as u32));
}
}
x86_opcodes::MUL | x86_opcodes::DIV => {
if let Some((_mod, _reg, rm)) = modrm {
let reg = self.decode_register(rm, rex, self.mode.is_64bit());
operands.push(MachineOperand::PhysReg(reg as u32));
}
}
_ => {
}
}
operands
}
pub fn decode_register(&self, reg_field: u8, rex: Option<u8>, is_64bit: bool) -> u16 {
let rex_b = rex.map_or(false, |r| (r & 0x01) != 0);
if is_64bit {
let ext_bit = if rex_b { 8u16 } else { 0u16 };
let reg_num = ext_bit | (reg_field as u16);
if reg_num < 16 {
reg_num } else {
0 }
} else {
(reg_field as u16) + 16
}
}
fn lookup_simple_opcode(opcode_bytes: &[u8]) -> Option<u32> {
if opcode_bytes.is_empty() {
return None;
}
let op = opcode_bytes[0];
let result = match op {
0x90 => x86_opcodes::NOP,
0xC3 => x86_opcodes::RET,
0xE8 => x86_opcodes::CALL,
0xE9 | 0xEB => x86_opcodes::JMP,
0x8D => x86_opcodes::LEA,
0x50..=0x57 => x86_opcodes::PUSH,
0x58..=0x5F => x86_opcodes::POP,
0xB8..=0xBF => x86_opcodes::MOV,
0x70..=0x7F => {
match op {
0x74 => x86_opcodes::JE,
0x75 => x86_opcodes::JNE,
_ => return None,
}
}
_ => return None,
};
Some(result)
}
pub fn lookup_opcode(opcode_bytes: &[u8], modrm_reg: Option<u8>) -> Option<u32> {
if opcode_bytes.is_empty() {
return None;
}
let op = opcode_bytes[0];
let twobyte = if opcode_bytes.len() >= 2 {
Some(opcode_bytes[1])
} else {
None
};
match op {
0x00 | 0x01 => Some(x86_opcodes::ADD),
0x08 | 0x09 => Some(x86_opcodes::OR),
0x10 | 0x11 => Some(x86_opcodes::ADC),
0x18 | 0x19 => Some(x86_opcodes::SBB),
0x20 | 0x21 => Some(x86_opcodes::AND),
0x28 | 0x29 => Some(x86_opcodes::SUB),
0x30 | 0x31 => Some(x86_opcodes::XOR),
0x38 | 0x39 => Some(x86_opcodes::CMP),
0x02 | 0x03 => Some(x86_opcodes::ADD),
0x0A | 0x0B => Some(x86_opcodes::OR),
0x12 | 0x13 => Some(x86_opcodes::ADC),
0x1A | 0x1B => Some(x86_opcodes::SBB),
0x22 | 0x23 => Some(x86_opcodes::AND),
0x2A | 0x2B => Some(x86_opcodes::SUB),
0x32 | 0x33 => Some(x86_opcodes::XOR),
0x3A | 0x3B => Some(x86_opcodes::CMP),
0x88 | 0x89 | 0x8A | 0x8B => Some(x86_opcodes::MOV),
0x84 | 0x85 => Some(x86_opcodes::TEST),
0x80 | 0x81 | 0x82 | 0x83 => {
if let Some(reg) = modrm_reg {
match reg {
0 => Some(x86_opcodes::ADD),
1 => Some(x86_opcodes::OR),
4 => Some(x86_opcodes::AND),
5 => Some(x86_opcodes::SUB),
6 => Some(x86_opcodes::XOR),
7 => Some(x86_opcodes::CMP),
_ => None,
}
} else {
None
}
}
0xC6 | 0xC7 => Some(x86_opcodes::MOV),
0xD0 | 0xD1 | 0xD2 | 0xD3 => {
if let Some(reg) = modrm_reg {
match reg {
4 => Some(x86_opcodes::SHL),
5 => Some(x86_opcodes::SHR),
_ => None,
}
} else {
None
}
}
0xF6 | 0xF7 => {
if let Some(reg) = modrm_reg {
match reg {
2 => Some(x86_opcodes::NOT),
3 => Some(x86_opcodes::NEG),
4 => Some(x86_opcodes::MUL),
6 => Some(x86_opcodes::DIV),
_ => None,
}
} else {
None
}
}
0xFE => {
if let Some(reg) = modrm_reg {
match reg {
0 => Some(x86_opcodes::INC),
1 => Some(x86_opcodes::DEC),
_ => None,
}
} else {
None
}
}
0xFF => {
if let Some(reg) = modrm_reg {
match reg {
0 => Some(x86_opcodes::INC),
1 => Some(x86_opcodes::DEC),
2 => Some(x86_opcodes::CALL),
4 => Some(x86_opcodes::JMP),
6 => Some(x86_opcodes::PUSH),
_ => None,
}
} else {
None
}
}
0x0F => {
if let Some(op2) = twobyte {
match op2 {
0x84 => Some(x86_opcodes::JE),
0x85 => Some(x86_opcodes::JNE),
0xAF => Some(x86_opcodes::IMUL),
_ => None,
}
} else {
None
}
}
_ => None,
}
}
fn opcode_has_modrm(opcode_bytes: &[u8]) -> bool {
if opcode_bytes.is_empty() {
return false;
}
let op = opcode_bytes[0];
match op {
0x00 | 0x01 | 0x02 | 0x03
| 0x08 | 0x09 | 0x0A | 0x0B
| 0x10 | 0x11 | 0x12 | 0x13
| 0x18 | 0x19 | 0x1A | 0x1B
| 0x20 | 0x21 | 0x22 | 0x23
| 0x28 | 0x29 | 0x2A | 0x2B
| 0x30 | 0x31 | 0x32 | 0x33
| 0x38 | 0x39 | 0x3A | 0x3B
| 0x88 | 0x89 | 0x8A | 0x8B
| 0x80 | 0x81 | 0x82 | 0x83
| 0x84 | 0x85
| 0x8D => true,
0xC6 | 0xC7 => true,
0xD0 | 0xD1 | 0xD2 | 0xD3 => true,
0xF6 | 0xF7 | 0xFE | 0xFF => true,
0x0F => {
if opcode_bytes.len() >= 2 {
matches!(
opcode_bytes[1],
0xAF )
} else {
false
}
}
_ => false,
}
}
fn determine_immediate_size(opcode_bytes: &[u8], _modrm_reg: Option<(u8, u8)>) -> u8 {
if opcode_bytes.is_empty() {
return 0;
}
let op = opcode_bytes[0];
match op {
0x80 | 0x82 => 1, 0x81 => 4, 0x83 => 1,
0xC6 => 1, 0xC7 => 4,
0xE8 => 4, 0xE9 => 4, 0xEB => 1, 0x70..=0x7F => 1,
0xB8..=0xBF => 8,
0x0F => {
if opcode_bytes.len() >= 2 {
match opcode_bytes[1] {
0x80..=0x8F => 4, _ => 0,
}
} else {
0
}
}
_ => 0,
}
}
pub fn decode_vex_prefix(&self, bytes: &[u8]) -> Option<(VexInfo, usize)> {
if bytes.len() < 2 {
return None;
}
match bytes[0] {
0xC5 => {
if bytes.len() < 2 {
return None;
}
let b1 = bytes[1];
let info = VexInfo {
vvvv: (!b1 >> 3) & 0x0F,
l: (b1 >> 2) & 0x01,
pp: b1 & 0x03,
r: (!b1 >> 7) & 0x01,
x: 0,
b: 0,
w: 0,
m_mmmm: 1, };
Some((info, 2))
}
0xC4 => {
if bytes.len() < 3 {
return None;
}
let b1 = bytes[1];
let b2 = bytes[2];
let info = VexInfo {
vvvv: (!b2 >> 3) & 0x0F,
l: (b2 >> 2) & 0x01,
pp: b2 & 0x03,
r: (!b1 >> 7) & 0x01,
x: (!b1 >> 6) & 0x01,
b: (!b1 >> 5) & 0x01,
w: (b2 >> 7) & 0x01,
m_mmmm: b1 & 0x1F,
};
Some((info, 3))
}
_ => None,
}
}
pub fn decode_evex_prefix(&self, bytes: &[u8]) -> Option<(EvexInfo, usize)> {
if bytes.len() < 4 || bytes[0] != 0x62 {
return None;
}
let b1 = bytes[1];
let b2 = bytes[2];
let b3 = bytes[3];
let info = EvexInfo {
vvvv: (!b2 >> 3) & 0x0F,
l: (b3 >> 5) & 0x03,
pp: b2 & 0x03,
r: (!b1 >> 7) & 0x01,
x: (!b1 >> 6) & 0x01,
b: (!b1 >> 5) & 0x01,
w: (b2 >> 7) & 0x01,
m_mmmm: b1 & 0x07,
aaa: b3 & 0x07,
z: (b3 >> 7) & 0x01,
b_prime: (b3 >> 4) & 0x01,
mask_reg: (b2 >> 3) & 0x07, };
Some((info, 4))
}
}
#[derive(Debug, Clone, Copy)]
pub struct OpcodeMapEntry {
pub byte: u8,
pub mnemonic: &'static str,
pub description: &'static str,
pub has_modrm: bool,
pub imm_size: u8,
pub map: OpcodeMap,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OpcodeMap {
Primary, Map0F, Map0F38, Map0F3A, VexMap0F, VexMap0F38, VexMap0F3A, EvexMap0F, EvexMap0F38, EvexMap0F3A, XopMap, }
pub fn primary_opcode_map(byte: u8) -> Option<OpcodeMapEntry> {
match byte {
0x00 => Some(OpcodeMapEntry {
byte,
mnemonic: "add",
description: "ADD r/m8, r8",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Primary,
}),
0x01 => Some(OpcodeMapEntry {
byte,
mnemonic: "add",
description: "ADD r/m, r",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Primary,
}),
0x02 => Some(OpcodeMapEntry {
byte,
mnemonic: "add",
description: "ADD r8, r/m8",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Primary,
}),
0x03 => Some(OpcodeMapEntry {
byte,
mnemonic: "add",
description: "ADD r, r/m",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Primary,
}),
0x04 => Some(OpcodeMapEntry {
byte,
mnemonic: "add",
description: "ADD AL, imm8",
has_modrm: false,
imm_size: 1,
map: OpcodeMap::Primary,
}),
0x05 => Some(OpcodeMapEntry {
byte,
mnemonic: "add",
description: "ADD rAX, imm",
has_modrm: false,
imm_size: 4,
map: OpcodeMap::Primary,
}),
0x08 => Some(OpcodeMapEntry {
byte,
mnemonic: "or",
description: "OR r/m8, r8",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Primary,
}),
0x09 => Some(OpcodeMapEntry {
byte,
mnemonic: "or",
description: "OR r/m, r",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Primary,
}),
0x0A => Some(OpcodeMapEntry {
byte,
mnemonic: "or",
description: "OR r8, r/m8",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Primary,
}),
0x0B => Some(OpcodeMapEntry {
byte,
mnemonic: "or",
description: "OR r, r/m",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Primary,
}),
0x0C => Some(OpcodeMapEntry {
byte,
mnemonic: "or",
description: "OR AL, imm8",
has_modrm: false,
imm_size: 1,
map: OpcodeMap::Primary,
}),
0x0D => Some(OpcodeMapEntry {
byte,
mnemonic: "or",
description: "OR rAX, imm",
has_modrm: false,
imm_size: 4,
map: OpcodeMap::Primary,
}),
0x0F => Some(OpcodeMapEntry {
byte,
mnemonic: "",
description: "Two-byte opcode escape",
has_modrm: false,
imm_size: 0,
map: OpcodeMap::Map0F,
}),
0x10 => Some(OpcodeMapEntry {
byte,
mnemonic: "adc",
description: "ADC r/m8, r8",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Primary,
}),
0x11 => Some(OpcodeMapEntry {
byte,
mnemonic: "adc",
description: "ADC r/m, r",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Primary,
}),
0x18 => Some(OpcodeMapEntry {
byte,
mnemonic: "sbb",
description: "SBB r/m8, r8",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Primary,
}),
0x19 => Some(OpcodeMapEntry {
byte,
mnemonic: "sbb",
description: "SBB r/m, r",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Primary,
}),
0x20 => Some(OpcodeMapEntry {
byte,
mnemonic: "and",
description: "AND r/m8, r8",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Primary,
}),
0x21 => Some(OpcodeMapEntry {
byte,
mnemonic: "and",
description: "AND r/m, r",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Primary,
}),
0x28 => Some(OpcodeMapEntry {
byte,
mnemonic: "sub",
description: "SUB r/m8, r8",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Primary,
}),
0x29 => Some(OpcodeMapEntry {
byte,
mnemonic: "sub",
description: "SUB r/m, r",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Primary,
}),
0x30 => Some(OpcodeMapEntry {
byte,
mnemonic: "xor",
description: "XOR r/m8, r8",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Primary,
}),
0x31 => Some(OpcodeMapEntry {
byte,
mnemonic: "xor",
description: "XOR r/m, r",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Primary,
}),
0x38 => Some(OpcodeMapEntry {
byte,
mnemonic: "cmp",
description: "CMP r/m8, r8",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Primary,
}),
0x39 => Some(OpcodeMapEntry {
byte,
mnemonic: "cmp",
description: "CMP r/m, r",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Primary,
}),
0x40..=0x47 | 0x48..=0x4F => None, 0x50..=0x57 => Some(OpcodeMapEntry {
byte,
mnemonic: "push",
description: "PUSH r",
has_modrm: false,
imm_size: 0,
map: OpcodeMap::Primary,
}),
0x58..=0x5F => Some(OpcodeMapEntry {
byte,
mnemonic: "pop",
description: "POP r",
has_modrm: false,
imm_size: 0,
map: OpcodeMap::Primary,
}),
0x70 => Some(OpcodeMapEntry {
byte,
mnemonic: "jo",
description: "JO rel8",
has_modrm: false,
imm_size: 1,
map: OpcodeMap::Primary,
}),
0x71 => Some(OpcodeMapEntry {
byte,
mnemonic: "jno",
description: "JNO rel8",
has_modrm: false,
imm_size: 1,
map: OpcodeMap::Primary,
}),
0x72 => Some(OpcodeMapEntry {
byte,
mnemonic: "jb",
description: "JB rel8",
has_modrm: false,
imm_size: 1,
map: OpcodeMap::Primary,
}),
0x73 => Some(OpcodeMapEntry {
byte,
mnemonic: "jnb",
description: "JNB rel8",
has_modrm: false,
imm_size: 1,
map: OpcodeMap::Primary,
}),
0x74 => Some(OpcodeMapEntry {
byte,
mnemonic: "je",
description: "JE rel8",
has_modrm: false,
imm_size: 1,
map: OpcodeMap::Primary,
}),
0x75 => Some(OpcodeMapEntry {
byte,
mnemonic: "jne",
description: "JNE rel8",
has_modrm: false,
imm_size: 1,
map: OpcodeMap::Primary,
}),
0x76 => Some(OpcodeMapEntry {
byte,
mnemonic: "jbe",
description: "JBE rel8",
has_modrm: false,
imm_size: 1,
map: OpcodeMap::Primary,
}),
0x77 => Some(OpcodeMapEntry {
byte,
mnemonic: "ja",
description: "JA rel8",
has_modrm: false,
imm_size: 1,
map: OpcodeMap::Primary,
}),
0x78 => Some(OpcodeMapEntry {
byte,
mnemonic: "js",
description: "JS rel8",
has_modrm: false,
imm_size: 1,
map: OpcodeMap::Primary,
}),
0x79 => Some(OpcodeMapEntry {
byte,
mnemonic: "jns",
description: "JNS rel8",
has_modrm: false,
imm_size: 1,
map: OpcodeMap::Primary,
}),
0x7A => Some(OpcodeMapEntry {
byte,
mnemonic: "jp",
description: "JP rel8",
has_modrm: false,
imm_size: 1,
map: OpcodeMap::Primary,
}),
0x7B => Some(OpcodeMapEntry {
byte,
mnemonic: "jnp",
description: "JNP rel8",
has_modrm: false,
imm_size: 1,
map: OpcodeMap::Primary,
}),
0x7C => Some(OpcodeMapEntry {
byte,
mnemonic: "jl",
description: "JL rel8",
has_modrm: false,
imm_size: 1,
map: OpcodeMap::Primary,
}),
0x7D => Some(OpcodeMapEntry {
byte,
mnemonic: "jge",
description: "JGE rel8",
has_modrm: false,
imm_size: 1,
map: OpcodeMap::Primary,
}),
0x7E => Some(OpcodeMapEntry {
byte,
mnemonic: "jle",
description: "JLE rel8",
has_modrm: false,
imm_size: 1,
map: OpcodeMap::Primary,
}),
0x7F => Some(OpcodeMapEntry {
byte,
mnemonic: "jg",
description: "JG rel8",
has_modrm: false,
imm_size: 1,
map: OpcodeMap::Primary,
}),
0x80 => Some(OpcodeMapEntry {
byte,
mnemonic: "grp1",
description: "Group 1 r/m8, imm8",
has_modrm: true,
imm_size: 1,
map: OpcodeMap::Primary,
}),
0x81 => Some(OpcodeMapEntry {
byte,
mnemonic: "grp1",
description: "Group 1 r/m, imm32",
has_modrm: true,
imm_size: 4,
map: OpcodeMap::Primary,
}),
0x83 => Some(OpcodeMapEntry {
byte,
mnemonic: "grp1",
description: "Group 1 r/m, imm8 (sign-ext)",
has_modrm: true,
imm_size: 1,
map: OpcodeMap::Primary,
}),
0x84 => Some(OpcodeMapEntry {
byte,
mnemonic: "test",
description: "TEST r/m8, r8",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Primary,
}),
0x85 => Some(OpcodeMapEntry {
byte,
mnemonic: "test",
description: "TEST r/m, r",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Primary,
}),
0x87 => Some(OpcodeMapEntry {
byte,
mnemonic: "xchg",
description: "XCHG r/m, r",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Primary,
}),
0x88 => Some(OpcodeMapEntry {
byte,
mnemonic: "mov",
description: "MOV r/m8, r8",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Primary,
}),
0x89 => Some(OpcodeMapEntry {
byte,
mnemonic: "mov",
description: "MOV r/m, r",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Primary,
}),
0x8A => Some(OpcodeMapEntry {
byte,
mnemonic: "mov",
description: "MOV r8, r/m8",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Primary,
}),
0x8B => Some(OpcodeMapEntry {
byte,
mnemonic: "mov",
description: "MOV r, r/m",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Primary,
}),
0x8D => Some(OpcodeMapEntry {
byte,
mnemonic: "lea",
description: "LEA r, m",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Primary,
}),
0x8F => Some(OpcodeMapEntry {
byte,
mnemonic: "pop",
description: "POP r/m",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Primary,
}),
0x90 => Some(OpcodeMapEntry {
byte,
mnemonic: "nop",
description: "NOP",
has_modrm: false,
imm_size: 0,
map: OpcodeMap::Primary,
}),
0x98 => Some(OpcodeMapEntry {
byte,
mnemonic: "cbw/cwde/cdqe",
description: "Sign extend AL/AX/EAX",
has_modrm: false,
imm_size: 0,
map: OpcodeMap::Primary,
}),
0x99 => Some(OpcodeMapEntry {
byte,
mnemonic: "cwd/cdq/cqo",
description: "Sign extend AX/EAX/RAX into DX:AX/EDX:EAX/RDX:RAX",
has_modrm: false,
imm_size: 0,
map: OpcodeMap::Primary,
}),
0x9E => Some(OpcodeMapEntry {
byte,
mnemonic: "sahf",
description: "Store AH into Flags",
has_modrm: false,
imm_size: 0,
map: OpcodeMap::Primary,
}),
0x9F => Some(OpcodeMapEntry {
byte,
mnemonic: "lahf",
description: "Load Flags into AH",
has_modrm: false,
imm_size: 0,
map: OpcodeMap::Primary,
}),
0xB0..=0xB7 => Some(OpcodeMapEntry {
byte,
mnemonic: "mov",
description: "MOV r8, imm8",
has_modrm: false,
imm_size: 1,
map: OpcodeMap::Primary,
}),
0xB8..=0xBF => Some(OpcodeMapEntry {
byte,
mnemonic: "mov",
description: "MOV r, imm",
has_modrm: false,
imm_size: 8,
map: OpcodeMap::Primary,
}),
0xC0 => Some(OpcodeMapEntry {
byte,
mnemonic: "grp2",
description: "Group 2 r/m8, imm8",
has_modrm: true,
imm_size: 1,
map: OpcodeMap::Primary,
}),
0xC1 => Some(OpcodeMapEntry {
byte,
mnemonic: "grp2",
description: "Group 2 r/m, imm8",
has_modrm: true,
imm_size: 1,
map: OpcodeMap::Primary,
}),
0xC2 => Some(OpcodeMapEntry {
byte,
mnemonic: "ret",
description: "RET imm16 (near return with stack release)",
has_modrm: false,
imm_size: 2,
map: OpcodeMap::Primary,
}),
0xC3 => Some(OpcodeMapEntry {
byte,
mnemonic: "ret",
description: "RET (near return)",
has_modrm: false,
imm_size: 0,
map: OpcodeMap::Primary,
}),
0xC6 => Some(OpcodeMapEntry {
byte,
mnemonic: "mov",
description: "MOV r/m8, imm8",
has_modrm: true,
imm_size: 1,
map: OpcodeMap::Primary,
}),
0xC7 => Some(OpcodeMapEntry {
byte,
mnemonic: "mov",
description: "MOV r/m, imm",
has_modrm: true,
imm_size: 4,
map: OpcodeMap::Primary,
}),
0xD0 => Some(OpcodeMapEntry {
byte,
mnemonic: "grp2",
description: "Group 2 r/m8, 1",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Primary,
}),
0xD1 => Some(OpcodeMapEntry {
byte,
mnemonic: "grp2",
description: "Group 2 r/m, 1",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Primary,
}),
0xD2 => Some(OpcodeMapEntry {
byte,
mnemonic: "grp2",
description: "Group 2 r/m8, CL",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Primary,
}),
0xD3 => Some(OpcodeMapEntry {
byte,
mnemonic: "grp2",
description: "Group 2 r/m, CL",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Primary,
}),
0xE8 => Some(OpcodeMapEntry {
byte,
mnemonic: "call",
description: "CALL rel32",
has_modrm: false,
imm_size: 4,
map: OpcodeMap::Primary,
}),
0xE9 => Some(OpcodeMapEntry {
byte,
mnemonic: "jmp",
description: "JMP rel32",
has_modrm: false,
imm_size: 4,
map: OpcodeMap::Primary,
}),
0xEB => Some(OpcodeMapEntry {
byte,
mnemonic: "jmp",
description: "JMP rel8",
has_modrm: false,
imm_size: 1,
map: OpcodeMap::Primary,
}),
0xF4 => Some(OpcodeMapEntry {
byte,
mnemonic: "hlt",
description: "HLT",
has_modrm: false,
imm_size: 0,
map: OpcodeMap::Primary,
}),
0xF5 => Some(OpcodeMapEntry {
byte,
mnemonic: "cmc",
description: "CMC (complement carry flag)",
has_modrm: false,
imm_size: 0,
map: OpcodeMap::Primary,
}),
0xF6 => Some(OpcodeMapEntry {
byte,
mnemonic: "grp3",
description: "Group 3 r/m8",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Primary,
}),
0xF7 => Some(OpcodeMapEntry {
byte,
mnemonic: "grp3",
description: "Group 3 r/m",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Primary,
}),
0xF8 => Some(OpcodeMapEntry {
byte,
mnemonic: "clc",
description: "CLC (clear carry flag)",
has_modrm: false,
imm_size: 0,
map: OpcodeMap::Primary,
}),
0xF9 => Some(OpcodeMapEntry {
byte,
mnemonic: "stc",
description: "STC (set carry flag)",
has_modrm: false,
imm_size: 0,
map: OpcodeMap::Primary,
}),
0xFA => Some(OpcodeMapEntry {
byte,
mnemonic: "cli",
description: "CLI (clear interrupt flag)",
has_modrm: false,
imm_size: 0,
map: OpcodeMap::Primary,
}),
0xFB => Some(OpcodeMapEntry {
byte,
mnemonic: "sti",
description: "STI (set interrupt flag)",
has_modrm: false,
imm_size: 0,
map: OpcodeMap::Primary,
}),
0xFC => Some(OpcodeMapEntry {
byte,
mnemonic: "cld",
description: "CLD (clear direction flag)",
has_modrm: false,
imm_size: 0,
map: OpcodeMap::Primary,
}),
0xFD => Some(OpcodeMapEntry {
byte,
mnemonic: "std",
description: "STD (set direction flag)",
has_modrm: false,
imm_size: 0,
map: OpcodeMap::Primary,
}),
0xFE => Some(OpcodeMapEntry {
byte,
mnemonic: "grp4",
description: "Group 4 r/m8 (INC/DEC)",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Primary,
}),
0xFF => Some(OpcodeMapEntry {
byte,
mnemonic: "grp5",
description: "Group 5 r/m (INC/DEC/JMP/CALL/PUSH)",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Primary,
}),
_ => None,
}
}
pub fn opcode_map_0f(op2: u8) -> Option<OpcodeMapEntry> {
match op2 {
0x10 => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "movups/movss/movupd/movsd",
description: "MOVUPS/MOVSS/MOVUPD/MOVSD (dependent on prefix)",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F,
}),
0x11 => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "movups/movss/movupd/movsd",
description: "MOVUPS/MOVSS/MOVUPD/MOVSD (dependent on prefix)",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F,
}),
0x31 => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "rdtsc",
description: "RDTSC",
has_modrm: false,
imm_size: 0,
map: OpcodeMap::Map0F,
}),
0x40 => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "cmovo",
description: "CMOVO r, r/m",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F,
}),
0x41 => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "cmovno",
description: "CMOVNO r, r/m",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F,
}),
0x42 => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "cmovb",
description: "CMOVB r, r/m",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F,
}),
0x43 => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "cmovae",
description: "CMOVAE r, r/m",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F,
}),
0x44 => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "cmove",
description: "CMOVE r, r/m",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F,
}),
0x45 => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "cmovne",
description: "CMOVNE r, r/m",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F,
}),
0x46 => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "cmovbe",
description: "CMOVBE r, r/m",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F,
}),
0x47 => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "cmova",
description: "CMOVA r, r/m",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F,
}),
0x48 => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "cmovs",
description: "CMOVS r, r/m",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F,
}),
0x49 => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "cmovns",
description: "CMOVNS r, r/m",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F,
}),
0x4A => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "cmovp",
description: "CMOVP r, r/m",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F,
}),
0x4B => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "cmovnp",
description: "CMOVNP r, r/m",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F,
}),
0x4C => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "cmovl",
description: "CMOVL r, r/m",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F,
}),
0x4D => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "cmovge",
description: "CMOVGE r, r/m",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F,
}),
0x4E => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "cmovle",
description: "CMOVLE r, r/m",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F,
}),
0x4F => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "cmovg",
description: "CMOVG r, r/m",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F,
}),
0x80 => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "jo",
description: "JO rel32",
has_modrm: false,
imm_size: 4,
map: OpcodeMap::Map0F,
}),
0x81 => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "jno",
description: "JNO rel32",
has_modrm: false,
imm_size: 4,
map: OpcodeMap::Map0F,
}),
0x82 => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "jb",
description: "JB rel32",
has_modrm: false,
imm_size: 4,
map: OpcodeMap::Map0F,
}),
0x83 => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "jnb",
description: "JNB rel32",
has_modrm: false,
imm_size: 4,
map: OpcodeMap::Map0F,
}),
0x84 => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "je",
description: "JE rel32",
has_modrm: false,
imm_size: 4,
map: OpcodeMap::Map0F,
}),
0x85 => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "jne",
description: "JNE rel32",
has_modrm: false,
imm_size: 4,
map: OpcodeMap::Map0F,
}),
0x86 => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "jbe",
description: "JBE rel32",
has_modrm: false,
imm_size: 4,
map: OpcodeMap::Map0F,
}),
0x87 => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "ja",
description: "JA rel32",
has_modrm: false,
imm_size: 4,
map: OpcodeMap::Map0F,
}),
0x88 => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "js",
description: "JS rel32",
has_modrm: false,
imm_size: 4,
map: OpcodeMap::Map0F,
}),
0x89 => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "jns",
description: "JNS rel32",
has_modrm: false,
imm_size: 4,
map: OpcodeMap::Map0F,
}),
0x8A => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "jp",
description: "JP rel32",
has_modrm: false,
imm_size: 4,
map: OpcodeMap::Map0F,
}),
0x8B => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "jnp",
description: "JNP rel32",
has_modrm: false,
imm_size: 4,
map: OpcodeMap::Map0F,
}),
0x8C => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "jl",
description: "JL rel32",
has_modrm: false,
imm_size: 4,
map: OpcodeMap::Map0F,
}),
0x8D => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "jge",
description: "JGE rel32",
has_modrm: false,
imm_size: 4,
map: OpcodeMap::Map0F,
}),
0x8E => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "jle",
description: "JLE rel32",
has_modrm: false,
imm_size: 4,
map: OpcodeMap::Map0F,
}),
0x8F => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "jg",
description: "JG rel32",
has_modrm: false,
imm_size: 4,
map: OpcodeMap::Map0F,
}),
0x90 => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "seto",
description: "SETO r/m8",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F,
}),
0x91 => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "setno",
description: "SETNO r/m8",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F,
}),
0x92 => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "setb",
description: "SETB r/m8",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F,
}),
0x93 => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "setae",
description: "SETAE r/m8",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F,
}),
0x94 => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "sete",
description: "SETE r/m8",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F,
}),
0x95 => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "setne",
description: "SETNE r/m8",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F,
}),
0x96 => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "setbe",
description: "SETBE r/m8",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F,
}),
0x97 => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "seta",
description: "SETA r/m8",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F,
}),
0x98 => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "sets",
description: "SETS r/m8",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F,
}),
0x99 => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "setns",
description: "SETNS r/m8",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F,
}),
0x9A => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "setp",
description: "SETP r/m8",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F,
}),
0x9B => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "setnp",
description: "SETNP r/m8",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F,
}),
0x9C => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "setl",
description: "SETL r/m8",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F,
}),
0x9D => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "setge",
description: "SETGE r/m8",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F,
}),
0x9E => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "setle",
description: "SETLE r/m8",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F,
}),
0x9F => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "setg",
description: "SETG r/m8",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F,
}),
0xA2 => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "cpuid",
description: "CPUID",
has_modrm: false,
imm_size: 0,
map: OpcodeMap::Map0F,
}),
0xA3 => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "bt",
description: "BT r/m, r",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F,
}),
0xAB => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "bts",
description: "BTS r/m, r",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F,
}),
0xAF => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "imul",
description: "IMUL r, r/m",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F,
}),
0xB3 => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "btr",
description: "BTR r/m, r",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F,
}),
0xB6 => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "movzx",
description: "MOVZX r, r/m8",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F,
}),
0xB7 => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "movzx",
description: "MOVZX r, r/m16",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F,
}),
0xBB => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "btc",
description: "BTC r/m, r",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F,
}),
0xBC => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "bsf",
description: "BSF r, r/m",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F,
}),
0xBD => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "bsr",
description: "BSR r, r/m",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F,
}),
0xBE => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "movsx",
description: "MOVSX r, r/m8",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F,
}),
0xBF => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "movsx",
description: "MOVSX r, r/m16",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F,
}),
0xC8..=0xCF => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "bswap",
description: "BSWAP r",
has_modrm: false,
imm_size: 0,
map: OpcodeMap::Map0F,
}),
0x05 => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "syscall",
description: "SYSCALL",
has_modrm: false,
imm_size: 0,
map: OpcodeMap::Map0F,
}),
0x07 => Some(OpcodeMapEntry {
byte: op2,
mnemonic: "sysret",
description: "SYSRET",
has_modrm: false,
imm_size: 0,
map: OpcodeMap::Map0F,
}),
_ => None,
}
}
pub fn opcode_map_0f38(_op3: u8) -> Option<OpcodeMapEntry> {
None }
pub fn opcode_map_0f3a(_op3: u8) -> Option<OpcodeMapEntry> {
None }
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct EvexDecoded {
pub r_prime: bool,
pub x_prime: bool,
pub b_prime: bool,
pub r: bool,
pub x: bool,
pub b: bool,
pub v_prime: bool,
pub w: bool,
pub vvvv: u8,
pub pp: u8,
pub z: bool,
pub ll: u8,
pub b_bit: bool,
pub aaa: u8,
pub mmm: u8,
}
impl EvexDecoded {
pub fn decode(bytes: &[u8]) -> Option<Self> {
if bytes.len() < 4 || bytes[0] != 0x62 {
return None;
}
let p0 = bytes[1];
let p1 = bytes[2];
let p2 = bytes[3];
let r = (p0 >> 7) & 1 == 0;
let x = (p0 >> 6) & 1 == 0;
let b = (p0 >> 5) & 1 == 0;
let r_prime = (p0 >> 4) & 1 != 0;
let mmm = p0 & 0x07;
let w = (p1 >> 7) & 1 != 0;
let vvvv = !((p1 >> 3) & 0xF) & 0xF;
let pp = p1 & 0x03;
let z = (p2 >> 7) & 1 != 0;
let l_prime = (p2 >> 6) & 1;
let l = (p2 >> 5) & 1;
let b_bit = (p2 >> 4) & 1 != 0;
let v_prime = (p2 >> 3) & 1 != 0;
let aaa = p2 & 0x07;
let ll = (l_prime << 1) | l;
Some(Self {
r_prime,
x_prime: false,
b_prime: false,
r,
x,
b,
v_prime,
w,
vvvv,
pp,
z,
ll,
b_bit,
aaa,
mmm,
})
}
pub fn vector_length_bits(&self) -> u16 {
match self.ll {
0 => 128,
1 => 256,
2 => 512,
_ => 128,
}
}
pub fn opmask_reg(&self) -> u8 {
self.aaa
}
pub fn has_mask(&self) -> bool {
self.aaa != 0
}
pub fn has_zeroing(&self) -> bool {
self.z && self.has_mask()
}
pub fn has_broadcast(&self) -> bool {
self.b_bit && self.ll < 3
}
pub fn has_rounding(&self) -> bool {
self.b_bit
}
pub fn full_reg(&self, reg3: u8) -> u8 {
let mut reg = reg3 & 0x7;
if self.r {
reg |= 0x8;
}
if self.r_prime {
reg |= 0x10;
}
reg
}
pub fn full_rm(&self, rm3: u8) -> u8 {
let mut rm = rm3 & 0x7;
if self.b {
rm |= 0x8;
}
if self.b_prime {
rm |= 0x10;
}
rm
}
pub fn describe(&self) -> String {
let mut s = String::from("EVEX{");
s.push_str(&format!("VL={} ", self.vector_length_bits()));
if self.has_mask() {
s.push_str(&format!("k{} ", self.aaa));
}
if self.has_zeroing() {
s.push_str("{z} ");
}
if self.w {
s.push_str("W ");
}
s.push_str(&format!("pp={} mmm={}", self.pp, self.mmm));
s.push('}');
s
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct VexDecoded {
pub r: bool,
pub x: bool,
pub b: bool,
pub mmmmm: u8,
pub w: bool,
pub vvvv: u8,
pub l: bool,
pub pp: u8,
pub is_3byte: bool,
}
impl VexDecoded {
pub fn decode(bytes: &[u8]) -> Option<Self> {
if bytes.len() < 2 {
return None;
}
match bytes[0] {
0xC5 => {
let b1 = bytes[1];
let r = (b1 >> 7) & 1 == 0;
let vvvv = !((b1 >> 3) & 0xF) & 0xF;
let l = (b1 >> 2) & 1 != 0;
let pp = b1 & 0x03;
Some(Self {
r,
x: false,
b: false,
mmmmm: 1,
w: false,
vvvv,
l,
pp,
is_3byte: false,
})
}
0xC4 => {
if bytes.len() < 3 {
return None;
}
let b1 = bytes[1];
let b2 = bytes[2];
let r = (b1 >> 7) & 1 == 0;
let x = (b1 >> 6) & 1 == 0;
let b = (b1 >> 5) & 1 == 0;
let mmmmm = b1 & 0x1F;
let w = (b2 >> 7) & 1 != 0;
let vvvv = !((b2 >> 3) & 0xF) & 0xF;
let l = (b2 >> 2) & 1 != 0;
let pp = b2 & 0x03;
Some(Self {
r,
x,
b,
mmmmm,
w,
vvvv,
l,
pp,
is_3byte: true,
})
}
_ => None,
}
}
pub fn vector_length_bits(&self) -> u16 {
if self.l {
256
} else {
128
}
}
pub fn map_name(&self) -> &'static str {
match self.mmmmm {
0 => "none",
1 => "0F",
2 => "0F38",
3 => "0F3A",
_ => "unknown",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct XopDecoded {
pub r: bool,
pub x: bool,
pub b: bool,
pub mmmmm: u8,
pub w: bool,
pub vvvv: u8,
pub l: bool,
pub pp: u8,
}
impl XopDecoded {
pub fn decode(bytes: &[u8]) -> Option<Self> {
if bytes.len() < 3 || bytes[0] != 0x8F {
return None;
}
let b1 = bytes[1];
let b2 = bytes[2];
let r = (b1 >> 7) & 1 == 0;
let x = (b1 >> 6) & 1 == 0;
let b = (b1 >> 5) & 1 == 0;
let mmmmm = b1 & 0x1F;
let w = (b2 >> 7) & 1 != 0;
let vvvv = !((b2 >> 3) & 0xF) & 0xF;
let l = (b2 >> 2) & 1 != 0;
let pp = b2 & 0x03;
Some(Self {
r,
x,
b,
mmmmm,
w,
vvvv,
l,
pp,
})
}
pub fn is_valid_map(&self) -> bool {
matches!(self.mmmmm, 8 | 9 | 10)
}
pub fn xop_sub_opcode(&self, opcode: u8) -> u8 {
opcode
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Rex2Decoded {
pub m0: bool,
pub r4: bool,
pub x4: bool,
pub b4: bool,
pub w: bool,
pub r3: bool,
pub x3: bool,
pub b3: bool,
}
impl Rex2Decoded {
pub fn decode(bytes: &[u8]) -> Option<Self> {
if bytes.is_empty() {
return None;
}
let mut pos = 0;
if bytes[0] == 0xD5 {
if bytes.len() < 2 {
return None;
}
pos = 1;
}
let b = bytes[pos];
Some(Self {
m0: (b >> 7) & 1 != 0,
r4: (b >> 6) & 1 != 0,
x4: (b >> 5) & 1 != 0,
b4: (b >> 4) & 1 != 0,
w: (b >> 3) & 1 != 0,
r3: (b >> 2) & 1 != 0,
x3: (b >> 1) & 1 != 0,
b3: b & 1 != 0,
})
}
pub fn full_reg(&self, reg3: u8) -> u8 {
reg3 | ((self.r3 as u8) << 3) | ((self.r4 as u8) << 4)
}
pub fn full_index(&self, idx3: u8) -> u8 {
idx3 | ((self.x3 as u8) << 3) | ((self.x4 as u8) << 4)
}
pub fn full_base(&self, base3: u8) -> u8 {
base3 | ((self.b3 as u8) << 3) | ((self.b4 as u8) << 4)
}
pub fn has_extended_reg(&self) -> bool {
self.r4 || self.x4 || self.b4
}
}
pub fn complete_opcode_map_0f38(
opcode: u8,
has_vex: bool,
has_evex: bool,
) -> Option<OpcodeMapEntry> {
match opcode {
0x00 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "pshufb",
description: "PSHUFB",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x01 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "phaddw",
description: "PHADDW",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x02 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "phaddd",
description: "PHADDD",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x03 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "phaddsw",
description: "PHADDSW",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x04 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "pmaddubsw",
description: "PMADDUBSW",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x05 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "phsubw",
description: "PHSUBW",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x06 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "phsubd",
description: "PHSUBD",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x07 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "phsubsw",
description: "PHSUBSW",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x08 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "psignb",
description: "PSIGNB",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x09 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "psignw",
description: "PSIGNW",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x0A => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "psignd",
description: "PSIGND",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x0B => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "pmulhrsw",
description: "PMULHRSW",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x0C => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "vpermilps",
description: "VPERMILPS",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x0D => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "vpermilpd",
description: "VPERMILPD",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x0E => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "vtestps",
description: "VTESTPS",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x0F => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "vtestpd",
description: "VTESTPD",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x10 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "pblendvb",
description: "PBLENDVB",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x14 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "blendvps",
description: "BLENDVPS",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x15 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "blendvpd",
description: "BLENDVPD",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x16 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "vpermps",
description: "VPERMPS",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x17 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "vptest",
description: "VPTEST",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x18 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "vbroadcastss",
description: "VBROADCASTSS",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x19 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "vbroadcastsd",
description: "VBROADCASTSD",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x1A => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "vbroadcastf128",
description: "VBROADCASTF128",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x1C => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "pabsb",
description: "PABSB",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x1D => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "pabsw",
description: "PABSW",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x1E => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "pabsd",
description: "PABSD",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x20 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "pmovsxbw",
description: "PMOVSXBW",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x21 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "pmovsxbd",
description: "PMOVSXBD",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x22 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "pmovsxbq",
description: "PMOVSXBQ",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x23 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "pmovsxwd",
description: "PMOVSXWD",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x24 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "pmovsxwq",
description: "PMOVSXWQ",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x25 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "pmovsxdq",
description: "PMOVSXDQ",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x28 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "pmuldq",
description: "PMULDQ",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x29 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "pcmpeqq",
description: "PCMPEQQ",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x2A => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "movntdqa",
description: "MOVNTDQA",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x2B => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "vpackusdw",
description: "VPACKUSDW",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x30 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "pmovzxbw",
description: "PMOVZXBW",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x31 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "pmovzxbd",
description: "PMOVZXBD",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x32 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "pmovzxbq",
description: "PMOVZXBQ",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x33 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "pmovzxwd",
description: "PMOVZXWD",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x34 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "pmovzxwq",
description: "PMOVZXWQ",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x35 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "pmovzxdq",
description: "PMOVZXDQ",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x37 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "pcmpgtq",
description: "PCMPGTQ",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x38 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "pminsb",
description: "PMINSB",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x39 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "pminsd",
description: "PMINSD",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x3A => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "pminuw",
description: "PMINUW",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x3B => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "pminud",
description: "PMINUD",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x3C => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "pmaxsb",
description: "PMAXSB",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x3D => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "pmaxsd",
description: "PMAXSD",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x3E => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "pmaxuw",
description: "PMAXUW",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x3F => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "pmaxud",
description: "PMAXUD",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x40 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "pmulld",
description: "PMULLD",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x41 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "vphminposuw",
description: "VPHMINPOSUW",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x45 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "vpsrlvd",
description: "VPSRLVD",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x46 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "vpsravd",
description: "VPSRAVD",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x47 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "vpsllvd",
description: "VPSLLVD",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x58 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "vpbroadcastd",
description: "VPBROADCASTD",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x59 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "vpbroadcastq",
description: "VPBROADCASTQ",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x5A => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "vbroadcasti128",
description: "VBROADCASTI128",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x78 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "vpbroadcastb",
description: "VPBROADCASTB",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x79 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "vpbroadcastw",
description: "VPBROADCASTW",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x8C => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "vpmaskmovd",
description: "VPMASKMOVD",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x8E => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "vpmaskmovq",
description: "VPMASKMOVQ",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x90 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "vpgatherdd",
description: "VPGATHERDD",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x91 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "vpgatherdq",
description: "VPGATHERDQ",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x92 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "vpgatherqd",
description: "VPGATHERQD",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0x93 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "vpgatherqq",
description: "VPGATHERQQ",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0xA8 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "vfmadd213ps",
description: "VFMADD213PS",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0xA9 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "vfmadd213pd",
description: "VFMADD213PD",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0xAA => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "vfmadd213ss",
description: "VFMADD213SS",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0xAB => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "vfmadd213sd",
description: "VFMADD213SD",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0xB8 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "vfmadd231ps",
description: "VFMADD231PS",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0xB9 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "vfmadd231pd",
description: "VFMADD231PD",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0xBA => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "vfmadd231ss",
description: "VFMADD231SS",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0xBB => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "vfmadd231sd",
description: "VFMADD231SD",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0xDB => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "aesimc",
description: "AESIMC",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0xDC => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "aesenc",
description: "AESENC",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0xDD => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "aesenclast",
description: "AESENCLAST",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0xDE => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "aesdec",
description: "AESDEC",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0xDF => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "aesdeclast",
description: "AESDECLAST",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0xF0 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "movbe",
description: "MOVBE",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
0xF1 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "crc32",
description: "CRC32",
has_modrm: true,
imm_size: 0,
map: OpcodeMap::Map0F38,
}),
_ => None,
}
}
pub fn complete_opcode_map_0f3a(opcode: u8, has_vex: bool) -> Option<OpcodeMapEntry> {
match opcode {
0x08 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "roundps",
description: "ROUNDPS",
has_modrm: true,
imm_size: 1,
map: OpcodeMap::Map0F3A,
}),
0x09 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "roundpd",
description: "ROUNDPD",
has_modrm: true,
imm_size: 1,
map: OpcodeMap::Map0F3A,
}),
0x0A => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "roundss",
description: "ROUNDSS",
has_modrm: true,
imm_size: 1,
map: OpcodeMap::Map0F3A,
}),
0x0B => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "roundsd",
description: "ROUNDSD",
has_modrm: true,
imm_size: 1,
map: OpcodeMap::Map0F3A,
}),
0x0C => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "blendps",
description: "BLENDPS",
has_modrm: true,
imm_size: 1,
map: OpcodeMap::Map0F3A,
}),
0x0D => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "blendpd",
description: "BLENDPD",
has_modrm: true,
imm_size: 1,
map: OpcodeMap::Map0F3A,
}),
0x0E => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "pblendw",
description: "PBLENDW",
has_modrm: true,
imm_size: 1,
map: OpcodeMap::Map0F3A,
}),
0x0F => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "palignr",
description: "PALIGNR",
has_modrm: true,
imm_size: 1,
map: OpcodeMap::Map0F3A,
}),
0x14 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "pextrb",
description: "PEXTRB",
has_modrm: true,
imm_size: 1,
map: OpcodeMap::Map0F3A,
}),
0x15 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "pextrw",
description: "PEXTRW",
has_modrm: true,
imm_size: 1,
map: OpcodeMap::Map0F3A,
}),
0x16 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "pextrd",
description: "PEXTRD",
has_modrm: true,
imm_size: 1,
map: OpcodeMap::Map0F3A,
}),
0x17 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "extractps",
description: "EXTRACTPS",
has_modrm: true,
imm_size: 1,
map: OpcodeMap::Map0F3A,
}),
0x20 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "pinsrb",
description: "PINSRB",
has_modrm: true,
imm_size: 1,
map: OpcodeMap::Map0F3A,
}),
0x21 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "insertps",
description: "INSERTPS",
has_modrm: true,
imm_size: 1,
map: OpcodeMap::Map0F3A,
}),
0x22 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "pinsrd",
description: "PINSRD",
has_modrm: true,
imm_size: 1,
map: OpcodeMap::Map0F3A,
}),
0x40 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "dpps",
description: "DPPS",
has_modrm: true,
imm_size: 1,
map: OpcodeMap::Map0F3A,
}),
0x41 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "dppd",
description: "DPPD",
has_modrm: true,
imm_size: 1,
map: OpcodeMap::Map0F3A,
}),
0x42 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "mpsadbw",
description: "MPSADBW",
has_modrm: true,
imm_size: 1,
map: OpcodeMap::Map0F3A,
}),
0x44 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "pclmulqdq",
description: "PCLMULQDQ",
has_modrm: true,
imm_size: 1,
map: OpcodeMap::Map0F3A,
}),
0x60 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "pcmpestrm",
description: "PCMPESTRM",
has_modrm: true,
imm_size: 1,
map: OpcodeMap::Map0F3A,
}),
0x61 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "pcmpestri",
description: "PCMPESTRI",
has_modrm: true,
imm_size: 1,
map: OpcodeMap::Map0F3A,
}),
0x62 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "pcmpistrm",
description: "PCMPISTRM",
has_modrm: true,
imm_size: 1,
map: OpcodeMap::Map0F3A,
}),
0x63 => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "pcmpistri",
description: "PCMPISTRI",
has_modrm: true,
imm_size: 1,
map: OpcodeMap::Map0F3A,
}),
0xCC => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "sha1rnds4",
description: "SHA1RNDS4",
has_modrm: true,
imm_size: 1,
map: OpcodeMap::Map0F3A,
}),
0xDF => Some(OpcodeMapEntry {
byte: opcode,
mnemonic: "aeskeygenassist",
description: "AESKEYGENASSIST",
has_modrm: true,
imm_size: 1,
map: OpcodeMap::Map0F3A,
}),
_ => None,
}
}
pub fn vex_map_from_mmmmm(mmmmm: u8) -> Option<OpcodeMap> {
match mmmmm {
0 => None,
1 => Some(OpcodeMap::Map0F),
2 => Some(OpcodeMap::Map0F38),
3 => Some(OpcodeMap::Map0F3A),
_ => None,
}
}
pub fn is_valid_vex_map(mmmmm: u8) -> bool {
matches!(mmmmm, 1 | 2 | 3)
}
pub fn is_valid_xop_map(mmmmm: u8) -> bool {
matches!(mmmmm, 8 | 9 | 10)
}
pub fn decode_group_opcode(group_byte: u8, reg_field: u8, is_64bit: bool) -> Option<&'static str> {
match (group_byte, reg_field) {
(0x80 | 0x81 | 0x82 | 0x83, 0) => Some("add"),
(0x80 | 0x81 | 0x82 | 0x83, 1) => Some("or"),
(0x80 | 0x81 | 0x82 | 0x83, 2) => Some("adc"),
(0x80 | 0x81 | 0x82 | 0x83, 3) => Some("sbb"),
(0x80 | 0x81 | 0x82 | 0x83, 4) => Some("and"),
(0x80 | 0x81 | 0x82 | 0x83, 5) => Some("sub"),
(0x80 | 0x81 | 0x82 | 0x83, 6) => Some("xor"),
(0x80 | 0x81 | 0x82 | 0x83, 7) => Some("cmp"),
(0xC0 | 0xC1 | 0xD0 | 0xD1 | 0xD2 | 0xD3, 0) => Some("rol"),
(0xC0 | 0xC1 | 0xD0 | 0xD1 | 0xD2 | 0xD3, 1) => Some("ror"),
(0xC0 | 0xC1 | 0xD0 | 0xD1 | 0xD2 | 0xD3, 2) => Some("rcl"),
(0xC0 | 0xC1 | 0xD0 | 0xD1 | 0xD2 | 0xD3, 3) => Some("rcr"),
(0xC0 | 0xC1 | 0xD0 | 0xD1 | 0xD2 | 0xD3, 4) => Some("shl"),
(0xC0 | 0xC1 | 0xD0 | 0xD1 | 0xD2 | 0xD3, 5) => Some("shr"),
(0xD0 | 0xD1 | 0xD2 | 0xD3, 6) => Some("sal"),
(0xC0 | 0xC1 | 0xD0 | 0xD1 | 0xD2 | 0xD3, 7) => Some("sar"),
(0xF6 | 0xF7, 0) => Some("test"),
(0xF6 | 0xF7, 1) => Some("test"),
(0xF6 | 0xF7, 2) => Some("not"),
(0xF6 | 0xF7, 3) => Some("neg"),
(0xF6 | 0xF7, 4) => Some("mul"),
(0xF6 | 0xF7, 5) => Some("imul"),
(0xF6 | 0xF7, 6) => Some("div"),
(0xF6 | 0xF7, 7) => Some("idiv"),
(0xFE, 0) => Some("inc"),
(0xFE, 1) => Some("dec"),
(0xFF, 0) => Some("inc"),
(0xFF, 1) => Some("dec"),
(0xFF, 2) => Some("call"),
(0xFF, 3) => Some("call"), (0xFF, 4) => Some("jmp"),
(0xFF, 5) => Some("jmp"), (0xFF, 6) => Some("push"),
(0xFF, 7) => None,
(0x00, 0) if [0x0F].contains(&0x0F) => Some("sldt"),
(0x00, 1) => Some("str"),
(0x00, 2) => Some("lldt"),
(0x00, 3) => Some("ltr"),
(0x00, 4) => Some("verr"),
(0x00, 5) => Some("verw"),
(0x01, 0) => Some("sgdt"),
(0x01, 1) => Some("sidt"),
(0x01, 2) => Some("lgdt"),
(0x01, 3) => Some("lidt"),
(0x01, 4) => Some("smsw"),
(0x01, 6) => Some("lmsw"),
(0x01, 7) => Some("invlpg"),
(0xBA, 4) => Some("bt"),
(0xBA, 5) => Some("bts"),
(0xBA, 6) => Some("btr"),
(0xBA, 7) => Some("btc"),
(0xC7, 1) if is_64bit => Some("cmpxchg16b"),
(0xC7, 1) => Some("cmpxchg8b"),
(0xC7, 6) => Some("vmclear"),
(0xC7, 7) => Some("vmxon"),
(0xC6, 0) => Some("mov"),
(0xC7, 0) => Some("mov"),
(0x71, 2) => Some("psrlw"),
(0x71, 4) => Some("psraw"),
(0x71, 6) => Some("psllw"),
(0x72, 2) => Some("psrld"),
(0x72, 4) => Some("psrad"),
(0x72, 6) => Some("pslld"),
(0x73, 2) => Some("psrlq"),
(0x73, 3) => Some("psrldq"),
(0x73, 6) => Some("psllq"),
(0x73, 7) => Some("pslldq"),
_ => None,
}
}
pub fn is_group_opcode(opcode: u8) -> bool {
matches!(
opcode,
0x80 | 0x81 | 0x82 | 0x83 | 0xC0 | 0xC1 | 0xD0 | 0xD1 | 0xD2 | 0xD3 | 0xF6 | 0xF7 | 0xFE | 0xFF | 0xC6 | 0xC7 )
}
pub mod segment_overrides {
pub const CS: u8 = 0x2E;
pub const DS: u8 = 0x3E;
pub const ES: u8 = 0x26;
pub const FS: u8 = 0x64;
pub const GS: u8 = 0x65;
pub const SS: u8 = 0x36;
pub fn segment_name(prefix: u8) -> Option<&'static str> {
match prefix {
CS => Some("cs"),
DS => Some("ds"),
ES => Some("es"),
FS => Some("fs"),
GS => Some("gs"),
SS => Some("ss"),
_ => None,
}
}
pub fn is_segment_override(byte: u8) -> bool {
matches!(byte, CS | DS | ES | FS | GS | SS)
}
pub fn decode_overrides(bytes: &[u8]) -> (Option<u8>, usize) {
let mut segment: Option<u8> = None;
let mut consumed = 0;
while consumed < bytes.len() && is_segment_override(bytes[consumed]) {
segment = Some(bytes[consumed]);
consumed += 1;
}
(segment, consumed)
}
}
pub mod size_overrides {
pub const ADDR_SIZE_OVERRIDE: u8 = 0x67;
pub const OPERAND_SIZE_OVERRIDE: u8 = 0x66;
pub fn is_addr_size_override(byte: u8) -> bool {
byte == ADDR_SIZE_OVERRIDE
}
pub fn is_operand_size_override(byte: u8) -> bool {
byte == OPERAND_SIZE_OVERRIDE
}
pub fn decode_size_overrides(bytes: &[u8]) -> (bool, bool, usize) {
let mut addr_override = false;
let mut op_override = false;
let mut consumed = 0;
while consumed < bytes.len() {
match bytes[consumed] {
ADDR_SIZE_OVERRIDE => {
addr_override = !addr_override;
consumed += 1;
}
OPERAND_SIZE_OVERRIDE => {
op_override = !op_override;
consumed += 1;
}
_ => break,
}
}
(addr_override, op_override, consumed)
}
pub fn effective_address_size(has_override: bool, default_size: u8) -> u8 {
if has_override {
match default_size {
8 => 4,
4 => 2,
2 => 4,
_ => default_size,
}
} else {
default_size
}
}
pub fn effective_operand_size(has_override: bool, default_size: u8) -> u8 {
if has_override {
match default_size {
4 if default_size == 4 => 2,
2 => 4,
_ => default_size,
}
} else {
default_size
}
}
}
pub fn compute_instruction_length(bytes: &[u8], mode: X86Mode) -> Option<usize> {
if bytes.is_empty() {
return None;
}
let mut pos = 0usize;
let is_64bit = mode.is_64bit();
while pos < bytes.len() {
match bytes[pos] {
0xF0 | 0xF2 | 0xF3 | 0x2E | 0x36 | 0x3E | 0x26 | 0x64 | 0x65 | 0x66 | 0x67 => {
pos += 1;
}
_ => break,
}
}
if pos >= bytes.len() {
return None;
}
if is_64bit && (0x40..=0x4F).contains(&bytes[pos]) {
pos += 1;
}
if pos >= bytes.len() {
return None;
}
if bytes[pos] == 0xC5 && pos + 1 < bytes.len() {
pos += 2;
} else if bytes[pos] == 0xC4 && pos + 2 < bytes.len() {
pos += 3;
} else if bytes[pos] == 0x62 && pos + 3 < bytes.len() {
pos += 4;
} else if bytes[pos] == 0x8F && pos + 2 < bytes.len() {
pos += 3;
}
if pos >= bytes.len() {
return None;
}
let opcode = bytes[pos];
pos += 1;
if opcode == 0x0F {
if pos >= bytes.len() {
return None;
}
let op2 = bytes[pos];
pos += 1;
if (op2 == 0x38 || op2 == 0x3A) && pos < bytes.len() {
pos += 1;
}
}
if let Some(entry) = primary_opcode_map(opcode) {
if entry.has_modrm {
if pos >= bytes.len() {
return None;
}
let modrm = bytes[pos];
pos += 1;
let mod_field = (modrm >> 6) & 0x03;
let rm_field = modrm & 0x07;
if mod_field != 0b11 && rm_field == 4 {
if pos >= bytes.len() {
return None;
}
pos += 1; }
if mod_field == 0b01 {
if pos >= bytes.len() {
return None;
}
pos += 1; } else if mod_field == 0b10 || (mod_field == 0b00 && rm_field == 5) {
if pos + 4 > bytes.len() {
return None;
}
pos += 4; }
let imm_size = entry.imm_size;
if pos + imm_size as usize > bytes.len() {
return None;
}
pos += imm_size as usize;
}
}
Some(pos)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RegisterName {
pub reg_id: u16,
pub name: &'static str,
pub reg_class: X86RegClass,
pub width_bits: u16,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86RegClass {
GPR8,
GPR8High, GPR16,
GPR32,
GPR64,
XMM,
YMM,
ZMM,
Segment,
Control,
Debug,
Opmask,
BND,
ST, MMX, }
pub fn gpr64_name(reg_field: u8, rex_b: bool) -> &'static str {
let idx = (reg_field & 0x07) as usize + if rex_b { 8 } else { 0 };
GPR64_NAMES.get(idx).copied().unwrap_or("???")
}
pub const GPR64_NAMES: [&str; 16] = [
"RAX", "RCX", "RDX", "RBX", "RSP", "RBP", "RSI", "RDI", "R8", "R9", "R10", "R11", "R12", "R13",
"R14", "R15",
];
pub fn gpr32_name(reg_field: u8, rex_b: bool) -> &'static str {
let idx = (reg_field & 0x07) as usize + if rex_b { 8 } else { 0 };
GPR32_NAMES.get(idx).copied().unwrap_or("???")
}
pub const GPR32_NAMES: [&str; 16] = [
"EAX", "ECX", "EDX", "EBX", "ESP", "EBP", "ESI", "EDI", "R8D", "R9D", "R10D", "R11D", "R12D",
"R13D", "R14D", "R15D",
];
pub fn gpr16_name(reg_field: u8, rex_b: bool) -> &'static str {
let idx = (reg_field & 0x07) as usize + if rex_b { 8 } else { 0 };
GPR16_NAMES.get(idx).copied().unwrap_or("???")
}
pub const GPR16_NAMES: [&str; 16] = [
"AX", "CX", "DX", "BX", "SP", "BP", "SI", "DI", "R8W", "R9W", "R10W", "R11W", "R12W", "R13W",
"R14W", "R15W",
];
pub fn gpr8_name(reg_field: u8, rex_b: bool) -> &'static str {
if rex_b || reg_field < 4 {
let idx = (reg_field & 0x07) as usize + if rex_b { 8 } else { 0 };
GPR8L_NAMES.get(idx).copied().unwrap_or("???")
} else {
GPR8H_NAMES
.get((reg_field - 4) as usize)
.copied()
.unwrap_or("???")
}
}
pub const GPR8L_NAMES: [&str; 16] = [
"AL", "CL", "DL", "BL", "SPL", "BPL", "SIL", "DIL", "R8B", "R9B", "R10B", "R11B", "R12B",
"R13B", "R14B", "R15B",
];
pub const GPR8H_NAMES: [&str; 4] = ["AH", "CH", "DH", "BH"];
pub fn xmm_name(reg_field: u8, rex_r: bool) -> &'static str {
let idx = (reg_field & 0x07) as usize + if rex_r { 8 } else { 0 };
XMM_NAMES.get(idx).copied().unwrap_or("???")
}
pub const XMM_NAMES: [&str; 16] = [
"XMM0", "XMM1", "XMM2", "XMM3", "XMM4", "XMM5", "XMM6", "XMM7", "XMM8", "XMM9", "XMM10",
"XMM11", "XMM12", "XMM13", "XMM14", "XMM15",
];
pub fn ymm_name(reg_field: u8, rex_r: bool) -> &'static str {
let idx = (reg_field & 0x07) as usize + if rex_r { 8 } else { 0 };
YMM_NAMES.get(idx).copied().unwrap_or("???")
}
pub const YMM_NAMES: [&str; 16] = [
"YMM0", "YMM1", "YMM2", "YMM3", "YMM4", "YMM5", "YMM6", "YMM7", "YMM8", "YMM9", "YMM10",
"YMM11", "YMM12", "YMM13", "YMM14", "YMM15",
];
pub fn zmm_name(reg_field: u8, rex_r: bool) -> &'static str {
let idx = (reg_field & 0x07) as usize + if rex_r { 8 } else { 0 };
ZMM_NAMES.get(idx).copied().unwrap_or("???")
}
pub const ZMM_NAMES: [&str; 32] = [
"ZMM0", "ZMM1", "ZMM2", "ZMM3", "ZMM4", "ZMM5", "ZMM6", "ZMM7", "ZMM8", "ZMM9", "ZMM10",
"ZMM11", "ZMM12", "ZMM13", "ZMM14", "ZMM15", "ZMM16", "ZMM17", "ZMM18", "ZMM19", "ZMM20",
"ZMM21", "ZMM22", "ZMM23", "ZMM24", "ZMM25", "ZMM26", "ZMM27", "ZMM28", "ZMM29", "ZMM30",
"ZMM31",
];
pub const SEGMENT_REG_NAMES: [&str; 6] = ["ES", "CS", "SS", "DS", "FS", "GS"];
pub const CONTROL_REG_NAMES: [&str; 16] = [
"CR0", "CR1", "CR2", "CR3", "CR4", "CR5", "CR6", "CR7", "CR8", "CR9", "CR10", "CR11", "CR12",
"CR13", "CR14", "CR15",
];
pub const DEBUG_REG_NAMES: [&str; 8] = ["DR0", "DR1", "DR2", "DR3", "DR4", "DR5", "DR6", "DR7"];
pub const OPMASK_NAMES: [&str; 8] = ["K0", "K1", "K2", "K3", "K4", "K5", "K6", "K7"];
pub fn segment_reg_name(reg_field: u8) -> &'static str {
SEGMENT_REG_NAMES
.get(reg_field as usize & 0x07)
.copied()
.unwrap_or("???")
}
pub fn control_reg_name(reg_field: u8) -> &'static str {
CONTROL_REG_NAMES
.get(reg_field as usize & 0x0F)
.copied()
.unwrap_or("???")
}
pub fn debug_reg_name(reg_field: u8) -> &'static str {
DEBUG_REG_NAMES
.get(reg_field as usize & 0x07)
.copied()
.unwrap_or("???")
}
pub fn opmask_name(k_reg: u8) -> &'static str {
OPMASK_NAMES
.get(k_reg as usize & 0x07)
.copied()
.unwrap_or("???")
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DecodedMemoryOperand {
pub segment: u8,
pub base: Option<u16>,
pub index: Option<u16>,
pub scale: u8,
pub displacement: i64,
pub rip_relative: bool,
pub address_size: u8,
}
impl DecodedMemoryOperand {
pub fn to_intel_string(&self) -> String {
let seg_prefix = if self.segment != 0 {
format!("{}:", segment_reg_name(self.segment))
} else {
String::new()
};
let mut parts = Vec::new();
if self.rip_relative {
parts.push("RIP".to_string());
} else if let Some(base) = self.base {
parts.push(gpr64_name((base & 0x07) as u8, base >= 8).to_string());
}
if let Some(idx) = self.index {
let idx_name = gpr64_name((idx & 0x07) as u8, idx >= 8);
if self.scale > 1 {
parts.push(format!("{}*{}", idx_name, self.scale));
} else {
parts.push(idx_name.to_string());
}
}
let disp_str = if self.displacement != 0 || parts.is_empty() {
if self.displacement >= 0 {
format!("+0x{:X}", self.displacement)
} else {
format!("-0x{:X}", -self.displacement)
}
} else {
String::new()
};
format!("{}[{}]", seg_prefix, parts.join("+") + &disp_str)
.replace("[]+", "[") .replace("[]-", "[-")
}
}
pub fn decode_memory_operand(
mod_field: u8,
rm_field: u8,
sib: Option<(u8, u8, u8)>,
displacement: Option<i64>,
mode: X86Mode,
) -> DecodedMemoryOperand {
let is_64bit = mode.is_64bit();
let disp = displacement.unwrap_or(0);
if mod_field == 0b11 {
return DecodedMemoryOperand {
segment: 0,
base: None,
index: None,
scale: 1,
displacement: 0,
rip_relative: false,
address_size: 8,
};
}
let mut base: Option<u16> = None;
let mut index: Option<u16> = None;
let mut scale: u8 = 1;
let mut rip_relative = false;
if rm_field == 4 {
if let Some((sib_scale, sib_index, sib_base)) = sib {
scale = 1u8 << sib_scale;
if sib_index != 4 {
index = Some(sib_index as u16);
}
if sib_base != 5 || mod_field != 0b00 {
base = Some(sib_base as u16);
}
}
} else if is_64bit && mod_field == 0b00 && rm_field == 5 {
rip_relative = true;
} else {
base = Some(rm_field as u16);
}
DecodedMemoryOperand {
segment: 0,
base,
index,
scale,
displacement: disp,
rip_relative,
address_size: if is_64bit { 8 } else { 4 },
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BoundaryResult {
Valid { offset: usize, length: usize },
Invalid { offset: usize },
PrefixOnly { offset: usize },
}
pub fn detect_instruction_boundaries(bytes: &[u8], mode: X86Mode) -> Vec<BoundaryResult> {
let mut results = Vec::new();
let mut offset = 0usize;
while offset < bytes.len() {
match compute_instruction_length(&bytes[offset..], mode) {
Some(len) if len > 0 => {
results.push(BoundaryResult::Valid {
offset,
length: len,
});
offset += len;
}
_ => {
results.push(BoundaryResult::Invalid { offset });
offset += 1; }
}
}
results
}
pub fn validate_instruction_encoding(bytes: &[u8], mode: X86Mode) -> bool {
compute_instruction_length(bytes, mode).is_some()
}
pub fn detect_invalid_encoding(bytes: &[u8], mode: X86Mode) -> Option<&'static str> {
if bytes.is_empty() {
return Some("Empty byte sequence");
}
let is_64bit = mode.is_64bit();
if bytes[0] == 0x62 && !is_64bit {
if bytes.len() >= 4 {
let b1 = bytes[1];
if (b1 & 0x0C) == 0x00 && bytes[1] != 0 {
return Some("Ambiguous EVEX/BOUND encoding in 32-bit mode");
}
}
}
if !is_64bit && (0x40..=0x4F).contains(&bytes[0]) {
}
if bytes.len() == 1 {
match bytes[0] {
0x0F => return Some("Incomplete two-byte opcode escape"),
0xC4 | 0xC5 => return Some("Incomplete VEX prefix"),
0x62 => return Some("Incomplete EVEX prefix"),
0x8F => return Some("Incomplete XOP prefix"),
_ => {}
}
}
None }
pub fn is_valid_modrm(modrm: u8) -> bool {
let _mod_field = (modrm >> 6) & 0x03;
let _rm = modrm & 0x07;
true
}
pub fn is_valid_sib(sib: u8) -> bool {
let _index = (sib >> 3) & 0x07;
let _base = sib & 0x07;
true
}
#[cfg(test)]
mod tests {
use super::*;
#[allow(dead_code)]
fn make_instr(opcode: u32, operands: Vec<MachineOperand>) -> MachineInstr {
MachineInstr {
opcode,
operands,
def: None,
}
}
#[allow(dead_code)]
fn phys_reg(id: u16) -> MachineOperand {
MachineOperand::PhysReg(id as u32)
}
#[allow(dead_code)]
fn imm(val: i64) -> MachineOperand {
MachineOperand::Imm(val)
}
#[test]
fn test_decode_rex_present() {
let dec = X86MCDecoder::new(X86Mode::Mode64);
let (rex, count) = dec.decode_rex(&[0x48]);
assert_eq!(rex, Some(0x48));
assert_eq!(count, 1);
}
#[test]
fn test_decode_rex_not_present() {
let dec = X86MCDecoder::new(X86Mode::Mode64);
let (rex, count) = dec.decode_rex(&[0x90]); assert_eq!(rex, None);
assert_eq!(count, 0);
}
#[test]
fn test_decode_rex_32bit_mode() {
let dec = X86MCDecoder::new(X86Mode::Mode32);
let (rex, count) = dec.decode_rex(&[0x48]);
assert_eq!(rex, None);
assert_eq!(count, 0);
}
#[test]
fn test_decode_rex_all_bits() {
let dec = X86MCDecoder::new(X86Mode::Mode64);
let (rex, count) = dec.decode_rex(&[0x4F]); assert_eq!(rex, Some(0x4F));
assert_eq!(count, 1);
}
#[test]
fn test_decode_prefixes_none() {
let dec = X86MCDecoder::new(X86Mode::Mode64);
let (prefixes, count) = dec.decode_prefixes(&[0x90]);
assert!(prefixes.is_empty());
assert_eq!(count, 0);
}
#[test]
fn test_decode_prefixes_lock() {
let dec = X86MCDecoder::new(X86Mode::Mode64);
let (prefixes, count) = dec.decode_prefixes(&[0xF0, 0x90]);
assert_eq!(prefixes, vec![0xF0]);
assert_eq!(count, 1);
}
#[test]
fn test_decode_prefixes_rep() {
let dec = X86MCDecoder::new(X86Mode::Mode64);
let (prefixes, count) = dec.decode_prefixes(&[0xF3, 0x90]);
assert_eq!(prefixes, vec![0xF3]);
assert_eq!(count, 1);
}
#[test]
fn test_decode_prefixes_operand_size() {
let dec = X86MCDecoder::new(X86Mode::Mode64);
let (prefixes, count) = dec.decode_prefixes(&[0x66, 0x90]);
assert_eq!(prefixes, vec![0x66]);
assert_eq!(count, 1);
}
#[test]
fn test_decode_prefixes_multiple() {
let dec = X86MCDecoder::new(X86Mode::Mode64);
let (prefixes, count) = dec.decode_prefixes(&[0xF0, 0x66, 0x67, 0x90]);
assert_eq!(prefixes, vec![0xF0, 0x66, 0x67]);
assert_eq!(count, 3);
}
#[test]
fn test_decode_modrm_reg_direct() {
let dec = X86MCDecoder::new(X86Mode::Mode64);
let (mod_field, reg, rm, count) = dec.decode_modrm(&[0xC0]);
assert_eq!(mod_field, 3); assert_eq!(reg, 0);
assert_eq!(rm, 0);
assert_eq!(count, 1);
}
#[test]
fn test_decode_modrm_mem_disp8() {
let dec = X86MCDecoder::new(X86Mode::Mode64);
let (mod_field, reg, rm, count) = dec.decode_modrm(&[0x4F]);
assert_eq!(mod_field, 1); assert_eq!(reg, 1);
assert_eq!(rm, 7);
assert_eq!(count, 1);
}
#[test]
fn test_decode_modrm_mem_disp32() {
let dec = X86MCDecoder::new(X86Mode::Mode64);
let (mod_field, reg, rm, count) = dec.decode_modrm(&[0x90]);
assert_eq!(mod_field, 2); assert_eq!(reg, 2);
assert_eq!(rm, 0);
assert_eq!(count, 1);
}
#[test]
fn test_decode_sib() {
let dec = X86MCDecoder::new(X86Mode::Mode64);
let (scale, index, base, count) = dec.decode_sib(&[0x04]);
assert_eq!(scale, 0);
assert_eq!(index, 0);
assert_eq!(base, 4);
assert_eq!(count, 1);
}
#[test]
fn test_decode_sib_no_index() {
let dec = X86MCDecoder::new(X86Mode::Mode64);
let (scale, index, base, count) = dec.decode_sib(&[0x25]);
assert_eq!(scale, 0);
assert_eq!(index, 4);
assert_eq!(base, 5);
assert_eq!(count, 1);
}
#[test]
fn test_decode_displacement_disp8() {
let dec = X86MCDecoder::new(X86Mode::Mode64);
let (disp, count) = dec.decode_displacement(&[0x42], mod_field::MEM_DISP8);
assert_eq!(disp, 0x42);
assert_eq!(count, 1);
}
#[test]
fn test_decode_displacement_disp8_negative() {
let dec = X86MCDecoder::new(X86Mode::Mode64);
let (disp, count) = dec.decode_displacement(&[0xF8], mod_field::MEM_DISP8);
assert_eq!(disp, -8);
assert_eq!(count, 1);
}
#[test]
fn test_decode_displacement_disp32() {
let dec = X86MCDecoder::new(X86Mode::Mode64);
let (disp, count) =
dec.decode_displacement(&[0x78, 0x56, 0x34, 0x12], mod_field::MEM_DISP32);
assert_eq!(disp, 0x12345678);
assert_eq!(count, 4);
}
#[test]
fn test_decode_displacement_disp32_negative() {
let dec = X86MCDecoder::new(X86Mode::Mode64);
let (disp, count) =
dec.decode_displacement(&[0x00, 0x00, 0x00, 0x80], mod_field::MEM_DISP32);
assert_eq!(disp, i32::MIN as i64);
assert_eq!(count, 4);
}
#[test]
fn test_decode_immediate_imm8() {
let dec = X86MCDecoder::new(X86Mode::Mode64);
let (val, count) = dec.decode_immediate(&[0x2A], 1);
assert_eq!(val, 42);
assert_eq!(count, 1);
}
#[test]
fn test_decode_immediate_imm8_negative() {
let dec = X86MCDecoder::new(X86Mode::Mode64);
let (val, count) = dec.decode_immediate(&[0xFF], 1);
assert_eq!(val, -1);
assert_eq!(count, 1);
}
#[test]
fn test_decode_immediate_imm32() {
let dec = X86MCDecoder::new(X86Mode::Mode64);
let (val, count) = dec.decode_immediate(&[0xEF, 0xBE, 0xAD, 0xDE], 4);
assert_eq!(val, 0xDEADBEEF_u32 as i32 as i64);
assert_eq!(count, 4);
}
#[test]
fn test_decode_immediate_imm64() {
let dec = X86MCDecoder::new(X86Mode::Mode64);
let imm_bytes = 0x0102030405060708u64.to_le_bytes();
let (val, count) = dec.decode_immediate(&imm_bytes, 8);
assert_eq!(val as u64, 0x0102030405060708);
assert_eq!(count, 8);
}
#[test]
fn test_decode_nop() {
let dec = X86MCDecoder::new(X86Mode::Mode64);
let (instr, consumed) = dec.decode_instruction(&[0x90], 0).unwrap();
assert_eq!(instr.opcode, x86_opcodes::NOP);
assert!(instr.operands.is_empty());
assert_eq!(consumed, 1);
}
#[test]
fn test_decode_ret() {
let dec = X86MCDecoder::new(X86Mode::Mode64);
let (instr, consumed) = dec.decode_instruction(&[0xC3], 0).unwrap();
assert_eq!(instr.opcode, x86_opcodes::RET);
assert!(instr.operands.is_empty());
assert_eq!(consumed, 1);
}
#[test]
fn test_decode_push_rax() {
let dec = X86MCDecoder::new(X86Mode::Mode64);
let (instr, consumed) = dec.decode_instruction(&[0x50], 0).unwrap();
assert_eq!(instr.opcode, x86_opcodes::PUSH);
assert_eq!(instr.operands.len(), 1);
assert!(matches!(instr.operands[0], MachineOperand::PhysReg(0)));
assert_eq!(consumed, 1);
}
#[test]
fn test_decode_push_rbp() {
let dec = X86MCDecoder::new(X86Mode::Mode64);
let (instr, consumed) = dec.decode_instruction(&[0x55], 0).unwrap();
assert_eq!(instr.opcode, x86_opcodes::PUSH);
assert!(matches!(instr.operands[0], MachineOperand::PhysReg(5)));
assert_eq!(consumed, 1);
}
#[test]
fn test_decode_pop_rax() {
let dec = X86MCDecoder::new(X86Mode::Mode64);
let (instr, consumed) = dec.decode_instruction(&[0x58], 0).unwrap();
assert_eq!(instr.opcode, x86_opcodes::POP);
assert!(matches!(instr.operands[0], MachineOperand::PhysReg(0)));
assert_eq!(consumed, 1);
}
#[test]
fn test_decode_mov_rax_rcx() {
let dec = X86MCDecoder::new(X86Mode::Mode64);
let bytes = [0x48, 0x89, 0xC8];
let (instr, consumed) = dec.decode_instruction(&bytes, 0).unwrap();
assert_eq!(instr.opcode, x86_opcodes::MOV);
assert_eq!(instr.operands.len(), 2);
assert_eq!(consumed, 3);
}
#[test]
fn test_decode_add_rax_rcx() {
let dec = X86MCDecoder::new(X86Mode::Mode64);
let bytes = [0x48, 0x01, 0xC8];
let (instr, consumed) = dec.decode_instruction(&bytes, 0).unwrap();
assert_eq!(instr.opcode, x86_opcodes::ADD);
assert_eq!(consumed, 3);
}
#[test]
fn test_decode_add_rax_imm5() {
let dec = X86MCDecoder::new(X86Mode::Mode64);
let bytes = [0x48, 0x83, 0xC0, 0x05];
let (instr, consumed) = dec.decode_instruction(&bytes, 0).unwrap();
assert_eq!(instr.opcode, x86_opcodes::ADD);
assert_eq!(instr.operands.len(), 2);
assert!(matches!(instr.operands[1], MachineOperand::Imm(5)));
assert_eq!(consumed, 4);
}
#[test]
fn test_decode_sub_rax_rdx() {
let dec = X86MCDecoder::new(X86Mode::Mode64);
let bytes = [0x48, 0x29, 0xD0];
let (instr, consumed) = dec.decode_instruction(&bytes, 0).unwrap();
assert_eq!(instr.opcode, x86_opcodes::SUB);
assert_eq!(consumed, 3);
}
#[test]
fn test_decode_xor_rax_rax() {
let dec = X86MCDecoder::new(X86Mode::Mode64);
let bytes = [0x48, 0x31, 0xC0];
let (instr, consumed) = dec.decode_instruction(&bytes, 0).unwrap();
assert_eq!(instr.opcode, x86_opcodes::XOR);
assert_eq!(consumed, 3);
}
#[test]
fn test_decode_cmp_rax_rcx() {
let dec = X86MCDecoder::new(X86Mode::Mode64);
let bytes = [0x48, 0x39, 0xC8];
let (instr, consumed) = dec.decode_instruction(&bytes, 0).unwrap();
assert_eq!(instr.opcode, x86_opcodes::CMP);
assert_eq!(consumed, 3);
}
#[test]
fn test_decode_mov_rax_imm42() {
let dec = X86MCDecoder::new(X86Mode::Mode64);
let mut bytes = vec![0x48, 0xB8];
bytes.extend_from_slice(&42i64.to_le_bytes());
let (instr, consumed) = dec.decode_instruction(&bytes, 0).unwrap();
assert_eq!(instr.opcode, x86_opcodes::MOV);
assert_eq!(instr.operands.len(), 2);
assert!(matches!(instr.operands[1], MachineOperand::Imm(42)));
assert_eq!(consumed, 10);
}
#[test]
fn test_decode_jmp_rel8() {
let dec = X86MCDecoder::new(X86Mode::Mode64);
let bytes = [0xEB, 0x05];
let (instr, consumed) = dec.decode_instruction(&bytes, 0).unwrap();
assert_eq!(instr.opcode, x86_opcodes::JMP);
assert_eq!(instr.operands.len(), 1);
assert!(matches!(instr.operands[0], MachineOperand::Imm(5)));
assert_eq!(consumed, 2);
}
#[test]
fn test_decode_jmp_rel32() {
let dec = X86MCDecoder::new(X86Mode::Mode64);
let mut bytes = vec![0xE9];
bytes.extend_from_slice(&100i32.to_le_bytes());
let (instr, consumed) = dec.decode_instruction(&bytes, 0).unwrap();
assert_eq!(instr.opcode, x86_opcodes::JMP);
assert!(matches!(instr.operands[0], MachineOperand::Imm(100)));
assert_eq!(consumed, 5);
}
#[test]
fn test_decode_call() {
let dec = X86MCDecoder::new(X86Mode::Mode64);
let mut bytes = vec![0xE8];
bytes.extend_from_slice(&0i32.to_le_bytes());
let (instr, consumed) = dec.decode_instruction(&bytes, 0).unwrap();
assert_eq!(instr.opcode, x86_opcodes::CALL);
assert_eq!(consumed, 5);
}
#[test]
fn test_decode_je_rel32() {
let dec = X86MCDecoder::new(X86Mode::Mode64);
let mut bytes = vec![0x0F, 0x84];
bytes.extend_from_slice(&50i32.to_le_bytes());
let (instr, consumed) = dec.decode_instruction(&bytes, 0).unwrap();
assert_eq!(instr.opcode, x86_opcodes::JE);
assert!(matches!(instr.operands[0], MachineOperand::Imm(50)));
assert_eq!(consumed, 6);
}
#[test]
fn test_decode_je_rel8() {
let dec = X86MCDecoder::new(X86Mode::Mode64);
let (instr, consumed) = dec.decode_instruction(&[0x74, 0x03], 0).unwrap();
assert_eq!(instr.opcode, x86_opcodes::JE);
assert!(matches!(instr.operands[0], MachineOperand::Imm(3)));
assert_eq!(consumed, 2);
}
#[test]
fn test_lookup_add_rm_r() {
let op = X86MCDecoder::lookup_opcode(&[0x01], Some(0));
assert_eq!(op, Some(x86_opcodes::ADD));
}
#[test]
fn test_lookup_sub_rm_r() {
let op = X86MCDecoder::lookup_opcode(&[0x29], Some(0));
assert_eq!(op, Some(x86_opcodes::SUB));
}
#[test]
fn test_lookup_and_rm_r() {
let op = X86MCDecoder::lookup_opcode(&[0x21], Some(0));
assert_eq!(op, Some(x86_opcodes::AND));
}
#[test]
fn test_lookup_or_rm_r() {
let op = X86MCDecoder::lookup_opcode(&[0x09], Some(0));
assert_eq!(op, Some(x86_opcodes::OR));
}
#[test]
fn test_lookup_xor_rm_r() {
let op = X86MCDecoder::lookup_opcode(&[0x31], Some(0));
assert_eq!(op, Some(x86_opcodes::XOR));
}
#[test]
fn test_lookup_cmp_rm_r() {
let op = X86MCDecoder::lookup_opcode(&[0x39], Some(0));
assert_eq!(op, Some(x86_opcodes::CMP));
}
#[test]
fn test_lookup_add_imm_group1() {
let op = X86MCDecoder::lookup_opcode(&[0x83], Some(0));
assert_eq!(op, Some(x86_opcodes::ADD));
}
#[test]
fn test_lookup_cmp_imm_group1() {
let op = X86MCDecoder::lookup_opcode(&[0x83], Some(7));
assert_eq!(op, Some(x86_opcodes::CMP));
}
#[test]
fn test_lookup_mov() {
let op = X86MCDecoder::lookup_opcode(&[0x89], Some(0));
assert_eq!(op, Some(x86_opcodes::MOV));
}
#[test]
fn test_lookup_shl() {
let op = X86MCDecoder::lookup_opcode(&[0xD3], Some(4));
assert_eq!(op, Some(x86_opcodes::SHL));
}
#[test]
fn test_lookup_shr() {
let op = X86MCDecoder::lookup_opcode(&[0xD3], Some(5));
assert_eq!(op, Some(x86_opcodes::SHR));
}
#[test]
fn test_lookup_not() {
let op = X86MCDecoder::lookup_opcode(&[0xF7], Some(2));
assert_eq!(op, Some(x86_opcodes::NOT));
}
#[test]
fn test_lookup_neg() {
let op = X86MCDecoder::lookup_opcode(&[0xF7], Some(3));
assert_eq!(op, Some(x86_opcodes::NEG));
}
#[test]
fn test_lookup_mul() {
let op = X86MCDecoder::lookup_opcode(&[0xF7], Some(4));
assert_eq!(op, Some(x86_opcodes::MUL));
}
#[test]
fn test_lookup_div() {
let op = X86MCDecoder::lookup_opcode(&[0xF7], Some(6));
assert_eq!(op, Some(x86_opcodes::DIV));
}
#[test]
fn test_decode_vex_2byte() {
let dec = X86MCDecoder::new(X86Mode::Mode64);
let result = dec.decode_vex_prefix(&[0xC5, 0xF8]);
assert!(result.is_some());
let (info, consumed) = result.unwrap();
assert_eq!(consumed, 2);
assert_eq!(info.l, 0);
assert_eq!(info.pp, 0);
}
#[test]
fn test_decode_vex_3byte() {
let dec = X86MCDecoder::new(X86Mode::Mode64);
let result = dec.decode_vex_prefix(&[0xC4, 0xE1, 0x78]);
assert!(result.is_some());
let (info, consumed) = result.unwrap();
assert_eq!(consumed, 3);
assert_eq!(info.m_mmmm, 1); assert_eq!(info.l, 0);
assert_eq!(info.pp, 0);
}
#[test]
fn test_decode_vex_not_present() {
let dec = X86MCDecoder::new(X86Mode::Mode64);
let result = dec.decode_vex_prefix(&[0x90, 0x90]);
assert!(result.is_none());
}
#[test]
fn test_decode_evex() {
let dec = X86MCDecoder::new(X86Mode::Mode64);
let result = dec.decode_evex_prefix(&[0x62, 0xF1, 0x7C, 0x48]);
assert!(result.is_some());
let (info, consumed) = result.unwrap();
assert_eq!(consumed, 4);
assert_eq!(info.m_mmmm, 1); }
#[test]
fn test_decode_evex_not_present() {
let dec = X86MCDecoder::new(X86Mode::Mode64);
let result = dec.decode_evex_prefix(&[0x90, 0x90, 0x90, 0x90]);
assert!(result.is_none());
}
#[test]
fn test_decode_function_simple() {
let dec = X86MCDecoder::new(X86Mode::Mode64);
let bytes = vec![
0x55, 0x48, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5D, 0xC3, ];
let mf = dec.decode_function(&bytes).unwrap();
assert_eq!(mf.blocks.len(), 1);
assert_eq!(mf.blocks[0].instructions.len(), 4);
assert_eq!(mf.blocks[0].instructions[0].opcode, x86_opcodes::PUSH);
assert_eq!(mf.blocks[0].instructions[3].opcode, x86_opcodes::RET);
}
#[test]
fn test_decode_function_empty() {
let dec = X86MCDecoder::new(X86Mode::Mode64);
let mf = dec.decode_function(&[]).unwrap();
assert!(mf.blocks[0].instructions.is_empty());
}
#[test]
fn test_opcode_has_modrm_add() {
assert!(X86MCDecoder::opcode_has_modrm(&[0x01]));
assert!(X86MCDecoder::opcode_has_modrm(&[0x29]));
}
#[test]
fn test_opcode_has_modrm_ret() {
assert!(!X86MCDecoder::opcode_has_modrm(&[0xC3]));
}
#[test]
fn test_opcode_has_modrm_nop() {
assert!(!X86MCDecoder::opcode_has_modrm(&[0x90]));
}
#[test]
fn test_opcode_has_modrm_lea() {
assert!(X86MCDecoder::opcode_has_modrm(&[0x8D]));
}
#[test]
fn test_decode_register_64bit_rax() {
let dec = X86MCDecoder::new(X86Mode::Mode64);
let reg = dec.decode_register(0, None, true);
assert_eq!(reg, 0); }
#[test]
fn test_decode_register_64bit_rcx() {
let dec = X86MCDecoder::new(X86Mode::Mode64);
let reg = dec.decode_register(1, None, true);
assert_eq!(reg, 1); }
#[test]
fn test_decode_register_64bit_rdi() {
let dec = X86MCDecoder::new(X86Mode::Mode64);
let reg = dec.decode_register(7, None, true);
assert_eq!(reg, 7); }
#[test]
fn test_decode_register_64bit_with_rex() {
let dec = X86MCDecoder::new(X86Mode::Mode64);
let reg = dec.decode_register(0, Some(0x41), true);
assert_eq!(reg, 8); }
#[test]
fn test_decode_register_32bit() {
let dec = X86MCDecoder::new(X86Mode::Mode32);
let reg = dec.decode_register(0, None, false);
assert_eq!(reg, 16); }
#[test]
fn test_decode_empty_bytes() {
let dec = X86MCDecoder::new(X86Mode::Mode64);
let result = dec.decode_instruction(&[], 0);
assert!(result.is_none());
}
#[test]
fn test_decode_invalid_opcode() {
let dec = X86MCDecoder::new(X86Mode::Mode64);
let result = dec.decode_instruction(&[0x0F], 0);
assert!(result.is_none());
}
#[test]
fn test_decode_rex_as_opcode_32bit() {
let dec = X86MCDecoder::new(X86Mode::Mode32);
let result = dec.decode_instruction(&[0x48], 0);
assert!(result.is_some());
let (_instr, consumed) = result.unwrap();
assert_eq!(consumed, 1);
}
#[test]
fn test_roundtrip_nop() {
let bytes = [0x90];
let dec = X86MCDecoder::new(X86Mode::Mode64);
let (_instr, consumed) = dec.decode_instruction(&bytes, 0).unwrap();
assert_eq!(_instr.opcode, x86_opcodes::NOP);
assert_eq!(consumed, 1);
}
#[test]
fn test_roundtrip_push_rbp() {
let bytes = [0x55];
let dec = X86MCDecoder::new(X86Mode::Mode64);
let (_instr, consumed) = dec.decode_instruction(&bytes, 0).unwrap();
assert_eq!(_instr.opcode, x86_opcodes::PUSH);
assert_eq!(consumed, 1);
}
#[test]
fn test_roundtrip_ret() {
let bytes = [0xC3];
let dec = X86MCDecoder::new(X86Mode::Mode64);
let (_instr, consumed) = dec.decode_instruction(&bytes, 0).unwrap();
assert_eq!(_instr.opcode, x86_opcodes::RET);
assert_eq!(consumed, 1);
}
#[test]
fn test_x86_mode_is_64bit() {
assert!(X86Mode::Mode64.is_64bit());
assert!(!X86Mode::Mode32.is_64bit());
assert!(!X86Mode::Mode16.is_64bit());
}
#[test]
fn test_x86_mode_default_operand_size() {
assert_eq!(X86Mode::Mode16.default_operand_size(), 2);
assert_eq!(X86Mode::Mode32.default_operand_size(), 4);
assert_eq!(X86Mode::Mode64.default_operand_size(), 4);
}
#[test]
fn test_primary_opcode_map_nop() {
let entry = primary_opcode_map(0x90).unwrap();
assert_eq!(entry.mnemonic, "nop");
assert!(!entry.has_modrm);
}
#[test]
fn test_primary_opcode_map_mov() {
let entry = primary_opcode_map(0x89).unwrap();
assert_eq!(entry.mnemonic, "mov");
assert!(entry.has_modrm);
}
#[test]
fn test_primary_opcode_map_add() {
let entry = primary_opcode_map(0x01).unwrap();
assert_eq!(entry.mnemonic, "add");
assert!(entry.has_modrm);
}
#[test]
fn test_primary_opcode_map_call() {
let entry = primary_opcode_map(0xE8).unwrap();
assert_eq!(entry.mnemonic, "call");
assert!(!entry.has_modrm);
assert_eq!(entry.imm_size, 4);
}
#[test]
fn test_primary_opcode_map_ret() {
let entry = primary_opcode_map(0xC3).unwrap();
assert_eq!(entry.mnemonic, "ret");
}
#[test]
fn test_primary_opcode_map_unknown() {
assert!(primary_opcode_map(0x06).is_none());
}
#[test]
fn test_opcode_map_0f_cmove() {
let entry = opcode_map_0f(0x44).unwrap();
assert_eq!(entry.mnemonic, "cmove");
assert!(entry.has_modrm);
}
#[test]
fn test_opcode_map_0f_jmp() {
let entry = opcode_map_0f(0x84).unwrap();
assert_eq!(entry.mnemonic, "je");
assert!(!entry.has_modrm);
assert_eq!(entry.imm_size, 4);
}
#[test]
fn test_opcode_map_0f_bswap() {
let entry = opcode_map_0f(0xC8).unwrap();
assert_eq!(entry.mnemonic, "bswap");
assert!(!entry.has_modrm);
}
#[test]
fn test_opcode_map_0f_cpuid() {
let entry = opcode_map_0f(0xA2).unwrap();
assert_eq!(entry.mnemonic, "cpuid");
}
#[test]
fn test_compute_length_nop() {
assert_eq!(
compute_instruction_length(&[0x90], X86Mode::Mode64),
Some(1)
);
}
#[test]
fn test_compute_length_ret() {
assert_eq!(
compute_instruction_length(&[0xC3], X86Mode::Mode64),
Some(1)
);
}
#[test]
fn test_compute_length_push_rax() {
assert_eq!(
compute_instruction_length(&[0x50], X86Mode::Mode64),
Some(1)
);
}
#[test]
fn test_compute_length_add_reg_reg() {
assert_eq!(
compute_instruction_length(&[0x48, 0x01, 0xC8], X86Mode::Mode64),
Some(3)
);
}
#[test]
fn test_compute_length_add_imm() {
assert_eq!(
compute_instruction_length(&[0x48, 0x83, 0xC0, 0x05], X86Mode::Mode64),
Some(4)
);
}
#[test]
fn test_compute_length_mov_rax_imm() {
let bytes = [0x48, 0xB8, 0, 0, 0, 0, 0, 0, 0, 0];
assert_eq!(
compute_instruction_length(&bytes, X86Mode::Mode64),
Some(10)
);
}
#[test]
fn test_compute_length_jmp_rel8() {
assert_eq!(
compute_instruction_length(&[0xEB, 0x05], X86Mode::Mode64),
Some(2)
);
}
#[test]
fn test_compute_length_jmp_rel32() {
let bytes = [0xE9, 0, 0, 0, 0];
assert_eq!(compute_instruction_length(&bytes, X86Mode::Mode64), Some(5));
}
#[test]
fn test_compute_length_empty() {
assert_eq!(compute_instruction_length(&[], X86Mode::Mode64), None);
}
#[test]
fn test_compute_length_je_rel32() {
let bytes = [0x0F, 0x84, 0, 0, 0, 0];
assert_eq!(compute_instruction_length(&bytes, X86Mode::Mode64), Some(6));
}
#[test]
fn test_gpr64_names() {
assert_eq!(gpr64_name(0, false), "RAX");
assert_eq!(gpr64_name(1, false), "RCX");
assert_eq!(gpr64_name(7, false), "RDI");
assert_eq!(gpr64_name(0, true), "R8");
assert_eq!(gpr64_name(7, true), "R15");
}
#[test]
fn test_gpr32_names() {
assert_eq!(gpr32_name(0, false), "EAX");
assert_eq!(gpr32_name(3, false), "EBX");
assert_eq!(gpr32_name(0, true), "R8D");
}
#[test]
fn test_gpr16_names() {
assert_eq!(gpr16_name(0, false), "AX");
assert_eq!(gpr16_name(5, false), "BP");
assert_eq!(gpr16_name(0, true), "R8W");
}
#[test]
fn test_gpr8_names() {
assert_eq!(gpr8_name(0, false), "AL");
assert_eq!(gpr8_name(3, false), "BL");
assert_eq!(gpr8_name(4, false), "AH");
assert_eq!(gpr8_name(5, false), "CH");
assert_eq!(gpr8_name(6, false), "DH");
assert_eq!(gpr8_name(7, false), "BH");
assert_eq!(gpr8_name(0, true), "R8B");
}
#[test]
fn test_xmm_names() {
assert_eq!(xmm_name(0, false), "XMM0");
assert_eq!(xmm_name(7, false), "XMM7");
assert_eq!(xmm_name(0, true), "XMM8");
}
#[test]
fn test_ymm_names() {
assert_eq!(ymm_name(0, false), "YMM0");
assert_eq!(ymm_name(0, true), "YMM8");
}
#[test]
fn test_segment_reg_names() {
assert_eq!(segment_reg_name(0), "ES");
assert_eq!(segment_reg_name(3), "DS");
assert_eq!(segment_reg_name(4), "FS");
}
#[test]
fn test_control_reg_names() {
assert_eq!(control_reg_name(0), "CR0");
assert_eq!(control_reg_name(3), "CR3");
}
#[test]
fn test_debug_reg_names() {
assert_eq!(debug_reg_name(0), "DR0");
assert_eq!(debug_reg_name(7), "DR7");
}
#[test]
fn test_opmask_names() {
assert_eq!(opmask_name(0), "K0");
assert_eq!(opmask_name(3), "K3");
assert_eq!(opmask_name(7), "K7");
}
#[test]
fn test_decode_memory_operand_simple() {
let mem = decode_memory_operand(0b00, 0, None, None, X86Mode::Mode64);
assert_eq!(mem.base, Some(0));
assert!(mem.index.is_none());
assert!(!mem.rip_relative);
}
#[test]
fn test_decode_memory_operand_rip_relative() {
let mem = decode_memory_operand(0b00, 5, None, Some(0x1234), X86Mode::Mode64);
assert!(mem.rip_relative);
assert_eq!(mem.displacement, 0x1234);
}
#[test]
fn test_decode_memory_operand_disp8() {
let mem = decode_memory_operand(0b01, 7, None, Some(42), X86Mode::Mode64);
assert_eq!(mem.base, Some(7));
assert_eq!(mem.displacement, 42);
}
#[test]
fn test_decode_memory_operand_with_sib() {
let mem = decode_memory_operand(0b00, 4, Some((0, 2, 3)), Some(0), X86Mode::Mode64);
assert_eq!(mem.base, Some(3));
assert_eq!(mem.index, Some(2));
assert_eq!(mem.scale, 1);
}
#[test]
fn test_memory_operand_to_intel() {
let mem = DecodedMemoryOperand {
segment: 0,
base: Some(5),
index: Some(3),
scale: 4,
displacement: 16,
rip_relative: false,
address_size: 8,
};
let s = mem.to_intel_string();
assert!(s.contains("RBP"));
assert!(s.contains("RBX"));
assert!(s.contains("*4"));
assert!(s.contains("0x10"));
}
#[test]
fn test_memory_operand_rip_rel_to_intel() {
let mem = DecodedMemoryOperand {
segment: 0,
base: None,
index: None,
scale: 1,
displacement: 0x1000,
rip_relative: true,
address_size: 8,
};
let s = mem.to_intel_string();
assert!(s.contains("RIP"));
}
#[test]
fn test_validate_valid_nop() {
assert!(validate_instruction_encoding(&[0x90], X86Mode::Mode64));
}
#[test]
fn test_validate_incomplete_0f() {
assert!(!validate_instruction_encoding(&[0x0F], X86Mode::Mode64));
}
#[test]
fn test_detect_invalid_empty() {
assert_eq!(
detect_invalid_encoding(&[], X86Mode::Mode64),
Some("Empty byte sequence")
);
}
#[test]
fn test_detect_invalid_incomplete_0f() {
assert_eq!(
detect_invalid_encoding(&[0x0F], X86Mode::Mode64),
Some("Incomplete two-byte opcode escape")
);
}
#[test]
fn test_is_valid_modrm() {
assert!(is_valid_modrm(0xC0));
assert!(is_valid_modrm(0x05));
}
#[test]
fn test_is_valid_sib() {
assert!(is_valid_sib(0x04));
assert!(is_valid_sib(0x25));
}
#[test]
fn test_detect_boundaries() {
let boundaries = detect_instruction_boundaries(&[0x90, 0xC3], X86Mode::Mode64);
assert_eq!(boundaries.len(), 2);
assert!(matches!(
boundaries[0],
BoundaryResult::Valid {
offset: 0,
length: 1
}
));
assert!(matches!(
boundaries[1],
BoundaryResult::Valid {
offset: 1,
length: 1
}
));
}
#[test]
fn test_detect_boundaries_empty() {
let boundaries = detect_instruction_boundaries(&[], X86Mode::Mode64);
assert!(boundaries.is_empty());
}
}