use super::x86_full_instr_info::{
EncodingForm, InstrEncodingInfo, X86FullInstrInfo, X86FullOpcode, X86FullOpcode::*,
};
use super::x86_instr_info::{OperandType, X86MemOperand, X86Operand};
pub use super::x86_mc_encoder::X86Mode;
use super::x86_mc_encoder::{mod_field, prefixes, X86MCEncoder};
use super::x86_register_info::{
RegClass, AH, AL, AX, BH, BL, BPL, BX, CH, CL, CS, CX, DH, DIL, DL, DS, DX, EAX, EBP, EBX, ECX,
EDI, EDX, ES, ESI, ESP, FS, GS, K0, K1, K2, K3, K4, K5, K6, K7, R10, R10B, R10D, R10W, R11,
R11B, R11D, R11W, R12, R12B, R12D, R12W, R13, R13B, R13D, R13W, R14, R14B, R14D, R14W, R15,
R15B, R15D, R15W, R8, R8B, R8D, R8W, R9, R9B, R9D, R9W, RAX, RBP, RBX, RCX, RDI, RDX, RIP, RSI,
RSP, SIL, SPL, SS, XMM0, XMM1, XMM10, XMM11, XMM12, XMM13, XMM14, XMM15, XMM16, XMM17, XMM18,
XMM19, XMM2, XMM20, XMM21, XMM22, XMM23, XMM24, XMM25, XMM26, XMM27, XMM28, XMM29, XMM3, XMM30,
XMM31, XMM4, XMM5, XMM6, XMM7, XMM8, XMM9, YMM0, YMM1, YMM10, YMM11, YMM12, YMM13, YMM14,
YMM15, YMM16, YMM17, YMM18, YMM19, YMM2, YMM20, YMM21, YMM22, YMM23, YMM24, YMM25, YMM26,
YMM27, YMM28, YMM29, YMM3, YMM30, YMM31, YMM4, YMM5, YMM6, YMM7, YMM8, YMM9, ZMM0, ZMM1, ZMM10,
ZMM11, ZMM12, ZMM13, ZMM14, ZMM15, ZMM16, ZMM17, ZMM18, ZMM19, ZMM2, ZMM20, ZMM21, ZMM22,
ZMM23, ZMM24, ZMM25, ZMM26, ZMM27, ZMM28, ZMM29, ZMM3, ZMM30, ZMM31, ZMM4, ZMM5, ZMM6, ZMM7,
ZMM8, ZMM9,
};
use super::x86_subtarget::X86Subtarget;
pub mod rex_prefix {
pub const REX_BASE: u8 = 0x40;
pub const REX_W: u8 = 0x48;
pub const REX_R: u8 = 0x44;
pub const REX_X: u8 = 0x42;
pub const REX_B: u8 = 0x41;
}
pub mod vex_prefix {
pub const VEX_2BYTE: u8 = 0xC5;
pub const VEX_3BYTE: u8 = 0xC4;
pub fn invert_reg(reg: u8) -> u8 {
(!reg) & 0xF
}
pub fn build_2byte(r: bool, vvvv: u8, l: bool, pp: u8) -> [u8; 2] {
let byte1 = (!r as u8) << 7 | (invert_reg(vvvv) << 3) | ((l as u8) << 2) | (pp & 0x3);
[VEX_2BYTE, byte1]
}
pub fn build_3byte(
r: bool,
x: bool,
b: bool,
mmmmm: u8,
w: bool,
vvvv: u8,
l: bool,
pp: u8,
) -> [u8; 3] {
let byte1 = (!r as u8) << 7 | (!x as u8) << 6 | (!b as u8) << 5 | (mmmmm & 0x1F);
let byte2 = (w as u8) << 7 | (invert_reg(vvvv) << 3) | ((l as u8) << 2) | (pp & 0x3);
[VEX_3BYTE, byte1, byte2]
}
}
pub mod evex_prefix {
pub const EVEX_MAGIC: u8 = 0x62;
pub fn build_evex(
r: bool,
x: bool,
b: bool,
r_prime: bool,
mmmm: u8,
w: bool,
vvvv: u8,
pp: u8,
z: bool,
ll: u8,
b_prim: bool,
aaa: u8,
) -> [u8; 4] {
let p0 = (!r as u8) << 7
| (!x as u8) << 6
| (!b as u8) << 5
| (!r_prime as u8) << 4
| (mmmm & 0x07);
let p1 = (w as u8) << 7
| (super::vex_prefix::invert_reg(vvvv) << 3)
| 0x04 | (pp & 0x03);
let p2 = (z as u8) << 7
| ((ll & 0x03) << 5)
| (b_prim as u8) << 4
| 0x08 | (aaa & 0x07);
[EVEX_MAGIC, p0, p1, p2]
}
}
pub mod xop_prefix {
pub const XOP_MAGIC: u8 = 0x8F;
pub fn build_xop(
r: bool,
x: bool,
b: bool,
mmmmm: u8,
w: bool,
vvvv: u8,
l: bool,
pp: u8,
) -> [u8; 3] {
let byte1 = (!r as u8) << 7 | (!x as u8) << 6 | (!b as u8) << 5 | (mmmmm & 0x1F);
let byte2 = (w as u8) << 7
| (super::vex_prefix::invert_reg(vvvv) << 3)
| ((l as u8) << 2)
| (pp & 0x3);
[XOP_MAGIC, byte1, byte2]
}
}
pub mod sib_byte {
pub fn build(scale: u8, index: u8, base: u8) -> u8 {
(scale & 0x3) << 6 | (index & 0x7) << 3 | (base & 0x7)
}
pub const SCALE_1: u8 = 0;
pub const SCALE_2: u8 = 1;
pub const SCALE_4: u8 = 2;
pub const SCALE_8: u8 = 3;
pub const NO_INDEX: u8 = 4;
pub const RBP_BASE: u8 = 5;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EvexTuple {
Full,
FullMem,
Half,
HalfMem,
Quarter,
QuarterMem,
Eighth,
EighthMem,
Scalar,
Mem128,
}
impl EvexTuple {
pub fn multiplier(&self) -> u8 {
match self {
EvexTuple::Full | EvexTuple::FullMem => 64,
EvexTuple::Half | EvexTuple::HalfMem => 32,
EvexTuple::Quarter | EvexTuple::QuarterMem | EvexTuple::Mem128 => 16,
EvexTuple::Eighth | EvexTuple::EighthMem => 8,
EvexTuple::Scalar => 4,
}
}
pub fn is_memory(&self) -> bool {
matches!(
self,
EvexTuple::FullMem | EvexTuple::HalfMem | EvexTuple::QuarterMem | EvexTuple::EighthMem
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EvexBroadcast {
None,
Broadcast1To2,
Broadcast1To4,
Broadcast1To8,
Broadcast1To16,
Broadcast1To32,
}
impl EvexBroadcast {
pub fn is_broadcast(&self) -> bool {
!matches!(self, EvexBroadcast::None)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EvexRounding {
None,
RnSae,
RdSae,
RuSae,
RzSae,
Sae,
}
impl EvexRounding {
pub fn is_active(&self) -> bool {
!matches!(self, EvexRounding::None)
}
pub fn rc_bits(&self) -> u8 {
match self {
EvexRounding::RnSae => 0,
EvexRounding::RdSae => 1,
EvexRounding::RuSae => 2,
EvexRounding::RzSae => 3,
EvexRounding::Sae => 0,
EvexRounding::None => 0,
}
}
}
pub struct X86FullMCEncoder {
pub output: Vec<u8>,
pub mode: X86Mode,
pub subtarget: X86Subtarget,
pub instr_info: X86FullInstrInfo,
}
impl X86FullMCEncoder {
pub fn new(mode: X86Mode, subtarget: X86Subtarget) -> Self {
Self {
output: Vec::new(),
mode,
subtarget,
instr_info: X86FullInstrInfo::new(),
}
}
pub fn with_info(mode: X86Mode, subtarget: X86Subtarget, info: X86FullInstrInfo) -> Self {
Self {
output: Vec::new(),
mode,
subtarget,
instr_info: info,
}
}
pub fn encode_instruction(
&mut self,
opcode: X86FullOpcode,
operands: &[X86Operand],
) -> Vec<u8> {
self.output.clear();
let Some(encoding) = self.instr_info.get(opcode).cloned() else {
self.output.push(0x0F);
self.output.push(0x0B);
return self.output.clone();
};
match encoding.encoding_form {
EncodingForm::VEX | EncodingForm::VEX3Byte => {
self.encode_vex_prefix(&encoding, operands);
}
EncodingForm::EVEX => {
self.encode_evex_prefix(&encoding, operands);
}
EncodingForm::XOP => {
self.encode_xop_prefix(&encoding, operands);
}
EncodingForm::Legacy | EncodingForm::LegacyREX => {
self.encode_legacy_prefixes(&encoding, operands);
}
EncodingForm::None => {
}
}
self.encode_opcode_bytes(&encoding);
if encoding.requires_modrm {
let modrm = self.compute_modrm(&encoding, operands);
self.output.push(modrm);
}
if self.needs_sib(operands) {
let sib = self.compute_sib(operands);
self.output.push(sib);
}
self.encode_displacement(operands);
self.encode_immediates(operands);
self.output.clone()
}
fn encode_vex_prefix(&mut self, encoding: &InstrEncodingInfo, operands: &[X86Operand]) {
use EncodingForm::*;
let use_3byte = matches!(encoding.encoding_form, VEX3Byte);
let mut rex_r = false;
let mut rex_x = false;
let mut rex_b = false;
let mut rex_w = false;
for (i, op) in operands.iter().enumerate() {
if let X86Operand::Reg(reg) = op {
if *reg >= 8 {
match i {
0 => rex_b = true, 1 => rex_r = true, 2 => rex_r = true, _ => {}
}
}
if self.mode.is_64bit() && (*reg == RAX || *reg >= R8) && *reg < 32 {
if self.is_gpr64_register(*reg) {
rex_w = true;
}
}
} else if let X86Operand::Mem(mem) = op {
if mem.base >= 8 {
rex_b = true;
}
if mem.index >= 8 {
rex_x = true;
}
}
}
let vvvv = if operands.len() >= 2 {
self.get_reg_field_for_operand(operands, 1)
} else {
0
};
let l = self.vector_length_from_operands(operands);
let pp = encoding.mandatory_prefix;
let mmmmm = encoding.opcode_map;
if use_3byte || rex_x || rex_w {
let bytes = vex_prefix::build_3byte(rex_r, rex_x, rex_b, mmmmm, rex_w, vvvv, l, pp);
self.output.extend_from_slice(&bytes);
} else {
let bytes = vex_prefix::build_2byte(rex_r, vvvv, l, pp);
self.output.extend_from_slice(&bytes);
}
}
fn encode_evex_prefix(&mut self, encoding: &InstrEncodingInfo, operands: &[X86Operand]) {
let mut rex_r = false;
let mut rex_x = false;
let mut rex_b = false;
let mut rex_w = false;
let mut r_prime = false;
for (i, op) in operands.iter().enumerate() {
if let X86Operand::Reg(reg) = op {
if *reg >= 16 {
r_prime = true;
}
if *reg >= 8 {
match i {
0 => rex_b = true,
1 => rex_r = true,
2 => rex_r = true,
_ => {}
}
}
if self.mode.is_64bit() && self.is_gpr64_register(*reg) {
rex_w = true;
}
} else if let X86Operand::Mem(mem) = op {
if mem.base >= 8 {
rex_b = true;
}
if mem.index >= 16 {
rex_x = true;
r_prime = true;
} else if mem.index >= 8 {
rex_x = true;
}
}
}
let vvvv = if operands.len() >= 2 {
self.get_reg_field_for_operand(operands, 1)
} else {
0
};
let ll = self.vector_length_512(operands);
let pp = encoding.mandatory_prefix;
let mmmm = encoding.opcode_map & 0x07;
let bytes = evex_prefix::build_evex(
rex_r, rex_x, rex_b, r_prime, mmmm, rex_w, vvvv, pp,
false, ll, false, 0, );
self.output.extend_from_slice(&bytes);
}
fn encode_xop_prefix(&mut self, encoding: &InstrEncodingInfo, operands: &[X86Operand]) {
let rex_r = false;
let rex_x = false;
let rex_b = false;
let rex_w = false;
let vvvv = if operands.len() >= 2 {
self.get_reg_field_for_operand(operands, 1)
} else {
0
};
let l = false;
let pp = encoding.mandatory_prefix;
let mmmmm = encoding.opcode_map & 0x1F;
let bytes = xop_prefix::build_xop(rex_r, rex_x, rex_b, mmmmm, rex_w, vvvv, l, pp);
self.output.extend_from_slice(&bytes);
}
fn encode_evex_full(
&mut self,
encoding: &InstrEncodingInfo,
operands: &[X86Operand],
opmask: Option<u8>, zeroing: bool, broadcast: EvexBroadcast,
rounding: EvexRounding,
tuple: EvexTuple,
) {
let mut rex_r = false;
let mut rex_x = false;
let mut rex_b = false;
let mut rex_w = false;
let mut r_prime = false;
let kreg = opmask.unwrap_or(0) & 0x7; let z = zeroing && opmask.is_some();
for (i, op) in operands.iter().enumerate() {
if let X86Operand::Reg(reg) = op {
if *reg >= 16 {
r_prime = true;
}
if *reg >= 8 {
match i {
0 => rex_b = true,
1 => rex_r = true,
2 => rex_r = true,
_ => {}
}
}
if self.mode.is_64bit() && self.is_gpr64_register(*reg) {
rex_w = true;
}
} else if let X86Operand::Mem(mem) = op {
if mem.base >= 8 {
rex_b = true;
}
if mem.index >= 16 {
rex_x = true;
r_prime = true;
} else if mem.index >= 8 {
rex_x = true;
}
}
}
let vvvv = if operands.len() >= 2 {
self.get_reg_field_for_operand(operands, 1)
} else {
0
};
let mut ll = self.vector_length_512(operands);
let b_prim = broadcast.is_broadcast();
if rounding.is_active() {
ll = rounding.rc_bits() & 0x3;
}
let pp = encoding.mandatory_prefix;
let mmmm = encoding.opcode_map & 0x07;
let aaa = kreg;
let bytes = evex_prefix::build_evex(
rex_r, rex_x, rex_b, r_prime, mmmm, rex_w, vvvv, pp, z, ll, b_prim, aaa,
);
self.output.extend_from_slice(&bytes);
}
pub fn encode_evex_masked(
&mut self,
opcode: X86FullOpcode,
operands: &[X86Operand],
opmask: Option<u8>,
zeroing: bool,
) -> Vec<u8> {
self.output.clear();
let Some(encoding) = self.instr_info.get(opcode).cloned() else {
self.output.push(0x0F);
self.output.push(0x0B);
return self.output.clone();
};
self.encode_evex_full(
&encoding,
operands,
opmask,
zeroing,
EvexBroadcast::None,
EvexRounding::None,
EvexTuple::Full,
);
self.encode_opcode_bytes(&encoding);
if encoding.requires_modrm {
let modrm = self.compute_modrm(&encoding, operands);
self.output.push(modrm);
}
if self.needs_sib(operands) {
let sib = self.compute_sib(operands);
self.output.push(sib);
}
self.encode_displacement(operands);
self.encode_immediates(operands);
self.output.clone()
}
pub fn encode_evex_broadcast(
&mut self,
opcode: X86FullOpcode,
operands: &[X86Operand],
broadcast: EvexBroadcast,
tuple: EvexTuple,
) -> Vec<u8> {
self.output.clear();
let Some(encoding) = self.instr_info.get(opcode).cloned() else {
self.output.push(0x0F);
self.output.push(0x0B);
return self.output.clone();
};
self.encode_evex_full(
&encoding,
operands,
None,
false,
broadcast,
EvexRounding::None,
tuple,
);
self.encode_opcode_bytes(&encoding);
if encoding.requires_modrm {
let modrm = self.compute_modrm(&encoding, operands);
self.output.push(modrm);
}
if self.needs_sib(operands) {
let sib = self.compute_sib(operands);
self.output.push(sib);
}
self.encode_evex_displacement(operands, tuple);
self.encode_immediates(operands);
self.output.clone()
}
pub fn encode_evex_rounding(
&mut self,
opcode: X86FullOpcode,
operands: &[X86Operand],
rounding: EvexRounding,
) -> Vec<u8> {
self.output.clear();
let Some(encoding) = self.instr_info.get(opcode).cloned() else {
self.output.push(0x0F);
self.output.push(0x0B);
return self.output.clone();
};
self.encode_evex_full(
&encoding,
operands,
None,
false,
EvexBroadcast::None,
rounding,
EvexTuple::Full,
);
self.encode_opcode_bytes(&encoding);
if encoding.requires_modrm {
let modrm = self.compute_modrm(&encoding, operands);
self.output.push(modrm);
}
if self.needs_sib(operands) {
let sib = self.compute_sib(operands);
self.output.push(sib);
}
self.encode_displacement(operands);
self.encode_immediates(operands);
self.output.clone()
}
fn encode_evex_displacement(&mut self, operands: &[X86Operand], tuple: EvexTuple) {
let multiplier = tuple.multiplier();
for op in operands {
if let X86Operand::Mem(mem) = op {
if mem.displacement != 0 {
let compressed = mem.displacement / multiplier as i32;
let disp = (compressed as i8) as u8;
self.output.push(disp);
return;
}
}
}
}
fn encode_xop_raw(&mut self, mmmmm: u8, opcode: u8, dst: u16, src: u16) -> Vec<u8> {
self.output.clear();
let rex_r = src >= 8;
let rex_x = false;
let rex_b = dst >= 8;
let rex_w = false;
let vvvv = 0; let l = false; let pp = 0;
let xop_prefix = xop_prefix::build_xop(rex_r, rex_x, rex_b, mmmmm, rex_w, vvvv, l, pp);
self.output.extend_from_slice(&xop_prefix);
self.output.push(opcode);
let modrm = 0xC0 | ((src & 0x7) << 3) | (dst & 0x7);
self.output.push(modrm as u8);
self.output.clone()
}
fn encode_xop_raw_imm(
&mut self,
mmmmm: u8,
opcode: u8,
dst: u16,
src: u16,
imm: u8,
) -> Vec<u8> {
let mut bytes = self.encode_xop_raw(mmmmm, opcode, dst, src);
bytes.push(imm);
bytes
}
pub fn encode_vphaddbd(&mut self, dst: u16, src: u16) -> Vec<u8> {
self.encode_xop_raw(8, 0xC2, dst, src)
}
pub fn encode_vphaddbq(&mut self, dst: u16, src: u16) -> Vec<u8> {
self.encode_xop_raw(8, 0xC3, dst, src)
}
pub fn encode_vphaddwd(&mut self, dst: u16, src: u16) -> Vec<u8> {
self.encode_xop_raw(8, 0xC6, dst, src)
}
pub fn encode_vphaddwq(&mut self, dst: u16, src: u16) -> Vec<u8> {
self.encode_xop_raw(8, 0xC7, dst, src)
}
pub fn encode_vphadddq(&mut self, dst: u16, src: u16) -> Vec<u8> {
self.encode_xop_raw(8, 0xCB, dst, src)
}
pub fn encode_vprotb(&mut self, dst: u16, src: u16) -> Vec<u8> {
self.encode_xop_raw(8, 0x90, dst, src)
}
pub fn encode_vprotw(&mut self, dst: u16, src: u16) -> Vec<u8> {
self.encode_xop_raw(8, 0x91, dst, src)
}
pub fn encode_vprotd(&mut self, dst: u16, src: u16) -> Vec<u8> {
self.encode_xop_raw(8, 0x92, dst, src)
}
pub fn encode_vprotq(&mut self, dst: u16, src: u16) -> Vec<u8> {
self.encode_xop_raw(8, 0x93, dst, src)
}
pub fn encode_vpshab(&mut self, dst: u16, src: u16) -> Vec<u8> {
self.encode_xop_raw(9, 0x98, dst, src)
}
pub fn encode_vpshaw(&mut self, dst: u16, src: u16) -> Vec<u8> {
self.encode_xop_raw(9, 0x99, dst, src)
}
pub fn encode_vpshad(&mut self, dst: u16, src: u16) -> Vec<u8> {
self.encode_xop_raw(9, 0x9A, dst, src)
}
pub fn encode_vpshaq(&mut self, dst: u16, src: u16) -> Vec<u8> {
self.encode_xop_raw(9, 0x9B, dst, src)
}
pub fn encode_vpshlb(&mut self, dst: u16, src: u16) -> Vec<u8> {
self.encode_xop_raw(9, 0x94, dst, src)
}
pub fn encode_vpshlw(&mut self, dst: u16, src: u16) -> Vec<u8> {
self.encode_xop_raw(9, 0x95, dst, src)
}
pub fn encode_vpshld(&mut self, dst: u16, src: u16) -> Vec<u8> {
self.encode_xop_raw(9, 0x96, dst, src)
}
pub fn encode_vpshlq(&mut self, dst: u16, src: u16) -> Vec<u8> {
self.encode_xop_raw(9, 0x97, dst, src)
}
fn encode_legacy_prefixes(&mut self, encoding: &InstrEncodingInfo, operands: &[X86Operand]) {
let mut rex = false;
let mut rex_w = false;
let mut rex_r = false;
let mut rex_x = false;
let mut rex_b = false;
if self.mode.is_64bit() {
for (i, op) in operands.iter().enumerate() {
match op {
X86Operand::Reg(reg) => {
if *reg >= 8 {
match i {
0 => rex_b = true,
_ => rex_r = true,
}
rex = true;
}
if self.is_gpr64_register(*reg) {
rex_w = true;
rex = true;
}
}
X86Operand::Mem(mem) => {
if mem.base >= 8 {
rex_b = true;
rex = true;
}
if mem.index >= 8 {
rex_x = true;
rex = true;
}
}
_ => {}
}
}
}
if matches!(encoding.encoding_form, EncodingForm::LegacyREX) && self.mode.is_64bit() {
rex = true;
}
if rex {
let rex_byte = rex_prefix::REX_BASE
| (if rex_w { 0x08 } else { 0 })
| (if rex_r { 0x04 } else { 0 })
| (if rex_x { 0x02 } else { 0 })
| (if rex_b { 0x01 } else { 0 });
self.output.push(rex_byte);
}
if encoding.mandatory_prefix != 0 && encoding.encoding_form == EncodingForm::Legacy {
let prefix_byte = match encoding.mandatory_prefix {
1 => prefixes::OPERAND_SIZE_OVERRIDE, 2 => prefixes::REP, 3 => prefixes::REPNE, _ => 0,
};
if prefix_byte != 0 {
self.output.push(prefix_byte);
}
}
}
fn encode_opcode_bytes(&mut self, encoding: &InstrEncodingInfo) {
match encoding.opcode_map {
0 => {
self.output.push(encoding.base_opcode);
}
1 => {
self.output.push(0x0F);
self.output.push(encoding.base_opcode);
}
2 => {
self.output.push(0x0F);
self.output.push(0x38);
self.output.push(encoding.base_opcode);
}
3 => {
self.output.push(0x0F);
self.output.push(0x3A);
self.output.push(encoding.base_opcode);
}
_ => {
self.output.push(encoding.base_opcode);
}
}
}
fn compute_modrm(&self, encoding: &InstrEncodingInfo, operands: &[X86Operand]) -> u8 {
let mut mod_field_val: u8 = mod_field::REG_DIRECT;
let mut reg_opcode: u8 = encoding.modrm_extension;
let mut rm_val: u8 = 0;
let dst_idx = 0usize;
let src_idx = if operands.len() >= 2 { 1 } else { 0 };
let has_mem = operands.iter().any(|op| matches!(op, X86Operand::Mem(_)));
if has_mem {
for (i, op) in operands.iter().enumerate() {
if let X86Operand::Mem(mem) = op {
rm_val = self.get_rm_reg_field(mem.base);
mod_field_val = self.compute_mem_mod_field(mem);
break;
}
}
for (i, op) in operands.iter().enumerate() {
if let X86Operand::Reg(reg) = op {
if i != 0 || !has_mem {
if reg_opcode == 255 {
reg_opcode = self.get_reg_field(*reg) & 0x7;
}
}
break;
}
}
} else if operands.len() >= 2 {
if let X86Operand::Reg(dst) = &operands[dst_idx] {
rm_val = self.get_reg_field(*dst) & 0x7;
}
if let X86Operand::Reg(src) = &operands[src_idx] {
if reg_opcode == 255 {
reg_opcode = self.get_reg_field(*src) & 0x7;
}
}
} else if operands.len() == 1 {
if let X86Operand::Reg(reg) = &operands[0] {
rm_val = self.get_reg_field(*reg) & 0x7;
}
}
(mod_field_val << 6) | ((reg_opcode & 0x7) << 3) | (rm_val & 0x7)
}
fn compute_mem_mod_field(&self, mem: &X86MemOperand) -> u8 {
if mem.base == 0 && mem.index == 0 {
mod_field::MEM_NO_DISP
} else if mem.displacement == 0
&& mem.base != RBP
&& mem.base != R13
&& !mem.is_rip_relative()
{
mod_field::MEM_NO_DISP
} else if mem.displacement >= -128 && mem.displacement <= 127 {
mod_field::MEM_DISP8
} else {
mod_field::MEM_DISP32
}
}
fn get_rm_reg_field(&self, reg: u16) -> u8 {
self.get_reg_field(reg) & 0x7
}
fn needs_sib(&self, operands: &[X86Operand]) -> bool {
for op in operands {
if let X86Operand::Mem(mem) = op {
if mem.index != 0 {
return true;
}
if mem.base == RSP || mem.base == R12 || mem.base == ESP {
return true;
}
if mem.base == RBP || mem.base == R13 {
return true;
}
}
}
false
}
fn compute_sib(&self, operands: &[X86Operand]) -> u8 {
for op in operands {
if let X86Operand::Mem(mem) = op {
let scale_bits = match mem.scale {
2 => sib_byte::SCALE_2,
4 => sib_byte::SCALE_4,
8 => sib_byte::SCALE_8,
_ => sib_byte::SCALE_1,
};
let index_field = if mem.index == 0 {
sib_byte::NO_INDEX
} else {
self.get_reg_field(mem.index) & 0x7
};
let base_field = if mem.base == 0 {
5 } else {
self.get_reg_field(mem.base) & 0x7
};
return sib_byte::build(scale_bits, index_field, base_field);
}
}
0
}
fn encode_displacement(&mut self, operands: &[X86Operand]) {
for op in operands {
if let X86Operand::Mem(mem) = op {
if mem.is_rip_relative() {
self.output
.extend_from_slice(&mem.displacement.to_le_bytes());
return;
}
if mem.displacement == 0 && mem.base != RBP && mem.base != R13 {
return;
}
if mem.displacement >= -128 && mem.displacement <= 127 {
self.output.push(mem.displacement as u8);
} else {
self.output
.extend_from_slice(&mem.displacement.to_le_bytes());
}
return;
}
}
}
fn encode_immediates(&mut self, operands: &[X86Operand]) {
for op in operands {
if let X86Operand::Imm(val) = op {
let abs_val = val.unsigned_abs();
if abs_val <= 0xFF && (*val as i8 as i64) == *val {
self.output.push(*val as u8);
} else if abs_val <= 0xFFFF_FFFF {
self.output.extend_from_slice(&(*val as i32).to_le_bytes());
} else {
self.output.extend_from_slice(&val.to_le_bytes());
}
return; }
}
}
pub fn get_reg_field(&self, reg_id: u16) -> u8 {
match reg_id {
RAX..=R15 => (reg_id % 16) as u8,
EAX..=R15D => (reg_id % 16) as u8,
AX..=R15W => (reg_id % 16) as u8,
AL..=R15B => {
let base = reg_id % 16;
if base >= 4 && reg_id != SPL && reg_id != BPL && reg_id != SIL && reg_id != DIL {
match reg_id {
4 => 4, 5 => 5, 6 => 6, 7 => 7, _ => (reg_id % 8) as u8,
}
} else {
base as u8
}
}
XMM0..=XMM15 => (reg_id % 16) as u8,
YMM0..=YMM15 => (reg_id % 16) as u8,
ZMM0..=ZMM15 => (reg_id % 16) as u8,
K0..=K7 => (reg_id % 8) as u8,
_ => 0,
}
}
fn get_reg_field_for_operand(&self, operands: &[X86Operand], idx: usize) -> u8 {
if idx < operands.len() {
if let X86Operand::Reg(reg) = &operands[idx] {
return self.get_reg_field(*reg);
}
}
0
}
fn is_gpr64_register(&self, reg: u16) -> bool {
(RAX..=R15).contains(®)
}
fn vector_length_from_operands(&self, operands: &[X86Operand]) -> bool {
for op in operands {
if let X86Operand::Reg(reg) = op {
if (YMM0..=YMM15).contains(reg) {
return true; }
}
}
false }
fn vector_length_512(&self, operands: &[X86Operand]) -> u8 {
for op in operands {
if let X86Operand::Reg(reg) = op {
if (ZMM0..=ZMM31).contains(reg) {
return 2; }
if (YMM0..=YMM31).contains(reg) {
return 1; }
}
}
0 }
pub fn encode_nop(&mut self) -> Vec<u8> {
self.output.clear();
self.output.push(0x90);
self.output.clone()
}
pub fn encode_nop3(&mut self) -> Vec<u8> {
self.output.clear();
self.output.push(0x0F);
self.output.push(0x1F);
self.output.push(0x00);
self.output.clone()
}
pub fn encode_call(&mut self, target_offset: i32) -> Vec<u8> {
self.output.clear();
self.output.push(0xE8);
self.output.extend_from_slice(&target_offset.to_le_bytes());
self.output.clone()
}
pub fn encode_mov_rr(&mut self, dst: u16, src: u16) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::MOVrr,
&[X86Operand::Reg(dst), X86Operand::Reg(src)],
)
}
pub fn encode_add_rr(&mut self, dst: u16, src: u16) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::ADD,
&[X86Operand::Reg(dst), X86Operand::Reg(src)],
)
}
pub fn encode_add_ri(&mut self, dst: u16, imm: i64) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::ADD,
&[X86Operand::Reg(dst), X86Operand::Imm(imm)],
)
}
pub fn encode_sub_rr(&mut self, dst: u16, src: u16) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::SUB,
&[X86Operand::Reg(dst), X86Operand::Reg(src)],
)
}
pub fn encode_and_rr(&mut self, dst: u16, src: u16) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::AND,
&[X86Operand::Reg(dst), X86Operand::Reg(src)],
)
}
pub fn encode_or_rr(&mut self, dst: u16, src: u16) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::OR,
&[X86Operand::Reg(dst), X86Operand::Reg(src)],
)
}
pub fn encode_xor_rr(&mut self, dst: u16, src: u16) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::XOR,
&[X86Operand::Reg(dst), X86Operand::Reg(src)],
)
}
pub fn encode_cmp_rr(&mut self, dst: u16, src: u16) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::CMP,
&[X86Operand::Reg(dst), X86Operand::Reg(src)],
)
}
pub fn encode_ret(&mut self) -> Vec<u8> {
self.encode_instruction(X86FullOpcode::RET, &[])
}
pub fn encode_mov_mr(&mut self, mem: X86MemOperand, src: u16) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::MOVmr,
&[X86Operand::Mem(mem), X86Operand::Reg(src)],
)
}
pub fn encode_mov_rm(&mut self, dst: u16, mem: X86MemOperand) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::MOVrm,
&[X86Operand::Reg(dst), X86Operand::Mem(mem)],
)
}
pub fn encode_vaddps(&mut self, dst: u16, src1: u16, src2: u16) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::VADDPS,
&[
X86Operand::Reg(dst),
X86Operand::Reg(src1),
X86Operand::Reg(src2),
],
)
}
pub fn encode_aesenc(&mut self, dst: u16, src: u16) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::AESENC,
&[X86Operand::Reg(dst), X86Operand::Reg(src)],
)
}
pub fn encode_sha256rnds2(&mut self, dst: u16, src1: u16, src2: u16) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::SHA256RNDS2,
&[
X86Operand::Reg(dst),
X86Operand::Reg(src1),
X86Operand::Reg(src2),
],
)
}
pub fn encode_vgatherdd_vsib(&mut self, dst: u16, vsib_mem: X86MemOperand) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::VPGATHERDD,
&[X86Operand::Reg(dst), X86Operand::Mem(vsib_mem)],
)
}
pub fn encode_vgatherdq_vsib(&mut self, dst: u16, vsib_mem: X86MemOperand) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::VPGATHERDQ,
&[X86Operand::Reg(dst), X86Operand::Mem(vsib_mem)],
)
}
pub fn encode_vp2intersectd(&mut self, k: u16, s1: u16, s2: u16) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::VP2INTERSECTD,
&[X86Operand::Reg(k), X86Operand::Reg(s1), X86Operand::Reg(s2)],
)
}
pub fn encode_vp2intersectq(&mut self, k: u16, s1: u16, s2: u16) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::VP2INTERSECTQ,
&[X86Operand::Reg(k), X86Operand::Reg(s1), X86Operand::Reg(s2)],
)
}
pub fn encode_vaddph(&mut self, d: u16, s1: u16, s2: u16) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::VADDPH,
&[X86Operand::Reg(d), X86Operand::Reg(s1), X86Operand::Reg(s2)],
)
}
pub fn encode_vmulph(&mut self, d: u16, s1: u16, s2: u16) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::VMULPH,
&[X86Operand::Reg(d), X86Operand::Reg(s1), X86Operand::Reg(s2)],
)
}
pub fn encode_vdivph(&mut self, d: u16, s1: u16, s2: u16) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::VDIVPH,
&[X86Operand::Reg(d), X86Operand::Reg(s1), X86Operand::Reg(s2)],
)
}
pub fn encode_vsubph(&mut self, d: u16, s1: u16, s2: u16) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::VSUBPH,
&[X86Operand::Reg(d), X86Operand::Reg(s1), X86Operand::Reg(s2)],
)
}
pub fn encode_vcvtne2ps2bf16(&mut self, d: u16, s1: u16, s2: u16) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::VCVTNE2PS2BF16,
&[X86Operand::Reg(d), X86Operand::Reg(s1), X86Operand::Reg(s2)],
)
}
pub fn encode_vdpbf16ps(&mut self, d: u16, s1: u16, s2: u16) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::VDPBF16PS,
&[X86Operand::Reg(d), X86Operand::Reg(s1), X86Operand::Reg(s2)],
)
}
pub fn encode_ldtilecfg(&mut self, mem: X86MemOperand) -> Vec<u8> {
self.encode_instruction(X86FullOpcode::LDTILECFG, &[X86Operand::Mem(mem)])
}
pub fn encode_sttilecfg(&mut self, mem: X86MemOperand) -> Vec<u8> {
self.encode_instruction(X86FullOpcode::STTILECFG, &[X86Operand::Mem(mem)])
}
pub fn encode_tilerelease(&mut self) -> Vec<u8> {
self.encode_instruction(X86FullOpcode::TILERELEASE, &[])
}
pub fn encode_tilezero(&mut self, t: u16) -> Vec<u8> {
self.encode_instruction(X86FullOpcode::TILEZERO, &[X86Operand::Reg(t)])
}
pub fn encode_tileloadd(&mut self, t: u16, mem: X86MemOperand) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::TILELOADD,
&[X86Operand::Reg(t), X86Operand::Mem(mem)],
)
}
pub fn encode_tilestored(&mut self, mem: X86MemOperand, t: u16) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::TILESTORED,
&[X86Operand::Mem(mem), X86Operand::Reg(t)],
)
}
pub fn encode_tdpbssd(&mut self, td: u16, ts1: u16, ts2: u16) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::TDPBSSD,
&[
X86Operand::Reg(td),
X86Operand::Reg(ts1),
X86Operand::Reg(ts2),
],
)
}
pub fn encode_tdpbsud(&mut self, td: u16, ts1: u16, ts2: u16) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::TDPBSUD,
&[
X86Operand::Reg(td),
X86Operand::Reg(ts1),
X86Operand::Reg(ts2),
],
)
}
pub fn encode_tdpbusd(&mut self, td: u16, ts1: u16, ts2: u16) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::TDPBUSD,
&[
X86Operand::Reg(td),
X86Operand::Reg(ts1),
X86Operand::Reg(ts2),
],
)
}
pub fn encode_tdpbuud(&mut self, td: u16, ts1: u16, ts2: u16) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::TDPBUUD,
&[
X86Operand::Reg(td),
X86Operand::Reg(ts1),
X86Operand::Reg(ts2),
],
)
}
pub fn encode_tdpbf16ps(&mut self, td: u16, ts1: u16, ts2: u16) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::TDPBF16PS,
&[
X86Operand::Reg(td),
X86Operand::Reg(ts1),
X86Operand::Reg(ts2),
],
)
}
pub fn encode_tdpfp16ps(&mut self, td: u16, ts1: u16, ts2: u16) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::TDPFP16PS,
&[
X86Operand::Reg(td),
X86Operand::Reg(ts1),
X86Operand::Reg(ts2),
],
)
}
pub fn encode_loadiwkey(&mut self, s1: u16, s2: u16) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::LOADIWKEY,
&[X86Operand::Reg(s1), X86Operand::Reg(s2)],
)
}
pub fn encode_aesenc128kl(&mut self, d: u16, s: u16) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::AESENC128KL,
&[X86Operand::Reg(d), X86Operand::Reg(s)],
)
}
pub fn encode_aesdec128kl(&mut self, d: u16, s: u16) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::AESDEC128KL,
&[X86Operand::Reg(d), X86Operand::Reg(s)],
)
}
pub fn encode_encodekey128(&mut self, d: u16, s: u16) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::ENCODEKEY128,
&[X86Operand::Reg(d), X86Operand::Reg(s)],
)
}
pub fn encode_hreset(&mut self, imm: u8) -> Vec<u8> {
self.encode_instruction(X86FullOpcode::HRESET, &[X86Operand::Imm(imm as i64)])
}
pub fn encode_senduip(&mut self, r: u16) -> Vec<u8> {
self.encode_instruction(X86FullOpcode::SENDUIP, &[X86Operand::Reg(r)])
}
pub fn encode_uiret(&mut self) -> Vec<u8> {
self.encode_instruction(X86FullOpcode::UIRET, &[])
}
pub fn encode_erets(&mut self) -> Vec<u8> {
self.encode_instruction(X86FullOpcode::ERETS, &[])
}
pub fn encode_eretu(&mut self) -> Vec<u8> {
self.encode_instruction(X86FullOpcode::ERETU, &[])
}
pub fn encode_lkgs(&mut self, r: u16) -> Vec<u8> {
self.encode_instruction(X86FullOpcode::LKGS, &[X86Operand::Reg(r)])
}
pub fn encode_cmpccxadd(&mut self, mem: X86MemOperand, r: u16, cc: u8) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::CMPCCXADD,
&[
X86Operand::Mem(mem),
X86Operand::Reg(r),
X86Operand::Imm(cc as i64),
],
)
}
pub fn encode_aadd(&mut self, mem: X86MemOperand, r: u16) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::AADD,
&[X86Operand::Mem(mem), X86Operand::Reg(r)],
)
}
pub fn encode_aand(&mut self, mem: X86MemOperand, r: u16) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::AAND,
&[X86Operand::Mem(mem), X86Operand::Reg(r)],
)
}
pub fn encode_aor(&mut self, mem: X86MemOperand, r: u16) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::AOR,
&[X86Operand::Mem(mem), X86Operand::Reg(r)],
)
}
pub fn encode_axor(&mut self, mem: X86MemOperand, r: u16) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::AXOR,
&[X86Operand::Mem(mem), X86Operand::Reg(r)],
)
}
pub fn encode_wrmsrns(&mut self) -> Vec<u8> {
self.encode_instruction(X86FullOpcode::WRMSRNS, &[])
}
pub fn encode_vcvtph2ps(&mut self, d: u16, s: u16) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::VCVTPH2PS,
&[X86Operand::Reg(d), X86Operand::Reg(s)],
)
}
pub fn encode_vcvtps2ph(&mut self, d: u16, s: u16, imm: u8) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::VCVTPS2PH,
&[
X86Operand::Reg(d),
X86Operand::Reg(s),
X86Operand::Imm(imm as i64),
],
)
}
pub fn encode_rdrand16(&mut self, d: u16) -> Vec<u8> {
self.encode_instruction(X86FullOpcode::RDRAND, &[X86Operand::Reg(d)])
}
pub fn encode_rdrand32(&mut self, d: u16) -> Vec<u8> {
self.encode_instruction(X86FullOpcode::RDRAND, &[X86Operand::Reg(d)])
}
pub fn encode_rdrand64(&mut self, d: u16) -> Vec<u8> {
self.encode_instruction(X86FullOpcode::RDRAND, &[X86Operand::Reg(d)])
}
pub fn encode_rdseed16(&mut self, d: u16) -> Vec<u8> {
self.encode_instruction(X86FullOpcode::RDSEED, &[X86Operand::Reg(d)])
}
pub fn encode_rdseed32(&mut self, d: u16) -> Vec<u8> {
self.encode_instruction(X86FullOpcode::RDSEED, &[X86Operand::Reg(d)])
}
pub fn encode_rdseed64(&mut self, d: u16) -> Vec<u8> {
self.encode_instruction(X86FullOpcode::RDSEED, &[X86Operand::Reg(d)])
}
pub fn encode_aesenclast(&mut self, d: u16, s: u16) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::AESENCLAST,
&[X86Operand::Reg(d), X86Operand::Reg(s)],
)
}
pub fn encode_aesdec(&mut self, d: u16, s: u16) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::AESDEC,
&[X86Operand::Reg(d), X86Operand::Reg(s)],
)
}
pub fn encode_aesdeclast(&mut self, d: u16, s: u16) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::AESDECLAST,
&[X86Operand::Reg(d), X86Operand::Reg(s)],
)
}
pub fn encode_aesimc(&mut self, d: u16, s: u16) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::AESIMC,
&[X86Operand::Reg(d), X86Operand::Reg(s)],
)
}
pub fn encode_aeskeygenassist(&mut self, d: u16, s: u16, imm: u8) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::AESKEYGENASSIST,
&[
X86Operand::Reg(d),
X86Operand::Reg(s),
X86Operand::Imm(imm as i64),
],
)
}
pub fn encode_sha1rnds4(&mut self, d: u16, s: u16, imm: u8) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::SHA1RNDS4,
&[
X86Operand::Reg(d),
X86Operand::Reg(s),
X86Operand::Imm(imm as i64),
],
)
}
pub fn encode_sha1nexte(&mut self, d: u16, s: u16) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::SHA1NEXTE,
&[X86Operand::Reg(d), X86Operand::Reg(s)],
)
}
pub fn encode_sha1msg1(&mut self, d: u16, s: u16) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::SHA1MSG1,
&[X86Operand::Reg(d), X86Operand::Reg(s)],
)
}
pub fn encode_sha1msg2(&mut self, d: u16, s: u16) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::SHA1MSG2,
&[X86Operand::Reg(d), X86Operand::Reg(s)],
)
}
pub fn encode_sha256msg1(&mut self, d: u16, s: u16) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::SHA256MSG1,
&[X86Operand::Reg(d), X86Operand::Reg(s)],
)
}
pub fn encode_sha256msg2(&mut self, d: u16, s: u16) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::SHA256MSG2,
&[X86Operand::Reg(d), X86Operand::Reg(s)],
)
}
pub fn encode_vgf2p8affineqb(&mut self, d: u16, s1: u16, s2: u16, imm: u8) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::VGF2P8AFFINEQB_Z,
&[
X86Operand::Reg(d),
X86Operand::Reg(s1),
X86Operand::Reg(s2),
X86Operand::Imm(imm as i64),
],
)
}
pub fn encode_vgf2p8affineinvqb(&mut self, d: u16, s1: u16, s2: u16, imm: u8) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::VGF2P8AFFINEINVQB_Z,
&[
X86Operand::Reg(d),
X86Operand::Reg(s1),
X86Operand::Reg(s2),
X86Operand::Imm(imm as i64),
],
)
}
pub fn encode_vgf2p8mulb(&mut self, d: u16, s1: u16, s2: u16) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::VGF2P8MULB_Z,
&[X86Operand::Reg(d), X86Operand::Reg(s1), X86Operand::Reg(s2)],
)
}
pub fn encode_vaesenc(&mut self, d: u16, s1: u16, s2: u16) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::VAESENC_Z,
&[X86Operand::Reg(d), X86Operand::Reg(s1), X86Operand::Reg(s2)],
)
}
pub fn encode_vaesenclast(&mut self, d: u16, s1: u16, s2: u16) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::VAESENCLAST_Z,
&[X86Operand::Reg(d), X86Operand::Reg(s1), X86Operand::Reg(s2)],
)
}
pub fn encode_vaesdec(&mut self, d: u16, s1: u16, s2: u16) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::VAESDEC_Z,
&[X86Operand::Reg(d), X86Operand::Reg(s1), X86Operand::Reg(s2)],
)
}
pub fn encode_vaesdeclast(&mut self, d: u16, s1: u16, s2: u16) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::VAESDECLAST_Z,
&[X86Operand::Reg(d), X86Operand::Reg(s1), X86Operand::Reg(s2)],
)
}
pub fn encode_vpclmulqdq(&mut self, d: u16, s1: u16, s2: u16, imm: u8) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::VPCLMULQDQ_Z,
&[
X86Operand::Reg(d),
X86Operand::Reg(s1),
X86Operand::Reg(s2),
X86Operand::Imm(imm as i64),
],
)
}
pub fn encode_vpdpbusd(&mut self, d: u16, s1: u16, s2: u16) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.push(0xC4);
bytes.push(0xE2);
bytes.push(0x79);
bytes.push(0x50);
let modrm: u8 = (0xC0 | ((d & 7) << 3) | (s2 & 7)) as u8;
bytes.push(modrm);
bytes
}
pub fn encode_vpdpbusds(&mut self, d: u16, s1: u16, s2: u16) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.push(0xC4);
bytes.push(0xE2);
bytes.push(0x79);
bytes.push(0x51);
let modrm: u8 = (0xC0 | ((d & 7) << 3) | (s2 & 7)) as u8;
bytes.push(modrm);
bytes
}
pub fn encode_vpdpwssd(&mut self, d: u16, s1: u16, s2: u16) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.push(0xC4);
bytes.push(0xE2);
bytes.push(0x79);
bytes.push(0x52);
let modrm: u8 = (0xC0 | ((d & 7) << 3) | (s2 & 7)) as u8;
bytes.push(modrm);
bytes
}
pub fn encode_vpdpwssds(&mut self, d: u16, s1: u16, s2: u16) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.push(0xC4);
bytes.push(0xE2);
bytes.push(0x79);
bytes.push(0x53);
let modrm: u8 = (0xC0 | ((d & 7) << 3) | (s2 & 7)) as u8;
bytes.push(modrm);
bytes
}
pub fn encode_vpmadd52luq(&mut self, d: u16, s1: u16, s2: u16) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.push(0xC4);
bytes.push(0xE2);
bytes.push(0x79);
bytes.push(0xB4);
let modrm: u8 = (0xC0 | ((d & 7) << 3) | (s2 & 7)) as u8;
bytes.push(modrm);
bytes
}
pub fn encode_vpmadd52huq(&mut self, d: u16, s1: u16, s2: u16) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.push(0xC4);
bytes.push(0xE2);
bytes.push(0x79);
bytes.push(0xB5);
let modrm: u8 = (0xC0 | ((d & 7) << 3) | (s2 & 7)) as u8;
bytes.push(modrm);
bytes
}
pub fn encode_vbcstnebf162ps(&mut self, d: u16, s: u16) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.push(0xC4);
bytes.push(0xE2);
bytes.push(0x7D);
bytes.push(0xB0);
let modrm: u8 = (0xC0 | ((d & 7) << 3) | (s & 7)) as u8;
bytes.push(modrm);
bytes
}
pub fn encode_vbcstnesh2ps(&mut self, d: u16, s: u16) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.push(0xC4);
bytes.push(0xE2);
bytes.push(0x7D);
bytes.push(0xB1);
let modrm: u8 = (0xC0 | ((d & 7) << 3) | (s & 7)) as u8;
bytes.push(modrm);
bytes
}
pub fn encode_vcvtneeph2ps(&mut self, d: u16, s1: u16, s2: u16) -> Vec<u8> {
self.encode_instruction(
X86FullOpcode::VCVTNEEPH2PS,
&[X86Operand::Reg(d), X86Operand::Reg(s1), X86Operand::Reg(s2)],
)
}
pub fn encode_vbcstnebf162ps_ymm(&mut self, d: u16, s: u16) -> Vec<u8> {
self.encode_vbcstnebf162ps(d, s)
}
pub fn encode_vbcstnesh2ps_ymm(&mut self, d: u16, s: u16) -> Vec<u8> {
self.encode_vbcstnesh2ps(d, s)
}
pub fn encode_vpdpbusd_ymm(&mut self, d: u16, s1: u16, s2: u16) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.push(0xC4);
bytes.push(0xE2);
bytes.push(0x7D);
bytes.push(0x50);
let modrm: u8 = (0xC0 | ((s1 & 7) << 3) | (s2 & 7)) as u8;
bytes.push(modrm);
bytes
}
pub fn encode_vpdpwssd_ymm(&mut self, d: u16, s1: u16, s2: u16) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.push(0xC4);
bytes.push(0xE2);
bytes.push(0x7D);
bytes.push(0x52);
let modrm: u8 = (0xC0 | ((s1 & 7) << 3) | (s2 & 7)) as u8;
bytes.push(modrm);
bytes
}
pub fn encode_vpmadd52luq_ymm(&mut self, d: u16, s1: u16, s2: u16) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.push(0xC4);
bytes.push(0xE2);
bytes.push(0x7D);
bytes.push(0xB4);
let modrm: u8 = (0xC0 | ((s1 & 7) << 3) | (s2 & 7)) as u8;
bytes.push(modrm);
bytes
}
pub fn encode_vpmadd52huq_ymm(&mut self, d: u16, s1: u16, s2: u16) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.push(0xC4);
bytes.push(0xE2);
bytes.push(0x7D);
bytes.push(0xB5);
let modrm: u8 = (0xC0 | ((s1 & 7) << 3) | (s2 & 7)) as u8;
bytes.push(modrm);
bytes
}
pub fn encode_vgf2p8affineqb_ymm(&mut self, d: u16, s1: u16, s2: u16, imm: u8) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.push(0xC4);
bytes.push(0xE2);
bytes.push(0x7D);
bytes.push(0xCE);
let modrm: u8 = (0xC0 | ((s1 & 7) << 3) | (s2 & 7)) as u8;
bytes.push(modrm);
bytes.push(imm);
bytes
}
pub fn encode_vgf2p8mulb_ymm(&mut self, d: u16, s1: u16, s2: u16) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.push(0xC4);
bytes.push(0xE2);
bytes.push(0x7D);
bytes.push(0xCF);
let modrm: u8 = (0xC0 | ((s1 & 7) << 3) | (s2 & 7)) as u8;
bytes.push(modrm);
bytes
}
pub fn encode_vaesenc_ymm(&mut self, d: u16, s1: u16, s2: u16) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.push(0xC4);
bytes.push(0xE2);
bytes.push(0x7D);
bytes.push(0xDC);
let modrm: u8 = (0xC0 | ((s1 & 7) << 3) | (s2 & 7)) as u8;
bytes.push(modrm);
bytes
}
pub fn encode_vaesdec_ymm(&mut self, d: u16, s1: u16, s2: u16) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.push(0xC4);
bytes.push(0xE2);
bytes.push(0x7D);
bytes.push(0xDE);
let modrm: u8 = (0xC0 | ((s1 & 7) << 3) | (s2 & 7)) as u8;
bytes.push(modrm);
bytes
}
pub fn rip_relative(displacement: i32) -> X86MemOperand {
X86MemOperand {
base: RIP,
index: 0,
scale: 0,
displacement,
segment: 0,
}
}
}
pub mod apx_rex2_encoder {
use super::*;
pub const REX2_PREFIX: u8 = 0xD5;
pub fn encode_rex2(
w: bool,
r: u8, x: u8, b: u8, ) -> u8 {
let r4 = (r >> 4) & 1;
let r3 = (r >> 3) & 1;
let x4 = (x >> 4) & 1;
let x3 = (x >> 3) & 1;
let b4 = (b >> 4) & 1;
let b3 = (b >> 3) & 1;
(r4 << 6) | (x4 << 5) | (b4 << 4) | ((w as u8) << 3) | (r3 << 2) | (x3 << 1) | b3
}
pub fn needs_rex2(reg: u8, rm: u8, index: Option<u8>) -> bool {
reg >= 16 || rm >= 16 || index.map_or(false, |i| i >= 16)
}
pub fn needs_rex2_3op(dst: u8, src1: u8, src2: u8) -> bool {
dst >= 16 || src1 >= 16 || src2 >= 16
}
pub fn encode_prefix(w: bool, reg: u8, rm: u8) -> Vec<u8> {
let needs_ext = reg >= 16 || rm >= 16;
if !needs_ext && !w {
return Vec::new();
}
if needs_ext {
vec![REX2_PREFIX, encode_rex2(w, reg, 0, rm)]
} else {
vec![0x40 | ((w as u8) << 3) | (((reg >> 3) & 1) << 2) | ((rm >> 3) & 1)]
}
}
pub fn encode_prefix_mem(w: bool, reg: u8, base: u8, index: u8) -> Vec<u8> {
let needs_ext = reg >= 16 || base >= 16 || index >= 16;
if !needs_ext && !w {
return Vec::new();
}
if needs_ext {
vec![REX2_PREFIX, encode_rex2(w, reg, index, base)]
} else {
let rex = 0x40
| ((w as u8) << 3)
| (((reg >> 3) & 1) << 2)
| (((index >> 3) & 1) << 1)
| ((base >> 3) & 1);
vec![rex]
}
}
}
pub mod apx_evex_encoder {
use super::*;
pub const EVEX_PREFIX: u8 = 0x62;
pub fn build_evex(
r: bool,
x: bool,
b: bool,
r_prime: bool, mmm: u8, w: bool,
vvvv: u8, v_prime: bool, pp: u8, z: bool, ll: u8, b_bit: bool, aaa: u8, ) -> [u8; 4] {
let byte1 = ((!r as u8) << 7)
| ((!x as u8) << 6)
| ((!b as u8) << 5)
| ((r_prime as u8) << 4)
| (mmm & 0x7);
let byte2 = ((w as u8) << 7)
| ((!vvvv & 0xF) << 3)
| (1 << 2) | (pp & 0x3);
let byte3 = ((z as u8) << 7)
| (((ll >> 1) & 1) << 6) | (((ll >> 0) & 1) << 5) | ((b_bit as u8) << 4)
| ((v_prime as u8) << 3)
| (aaa & 0x7);
[EVEX_PREFIX, byte1, byte2, byte3]
}
pub fn build_evex_rr(
opcode_map: u8,
w: bool,
vvvv: u8,
pp: u8,
reg: u8,
rm: u8,
ll: u8,
) -> [u8; 4] {
let r = (reg >> 3) & 1 != 0;
let b = (rm >> 3) & 1 != 0;
let r_prime = (reg >> 4) & 1 != 0;
let v_prime = (vvvv >> 4) & 1 != 0;
build_evex(
r,
false,
b,
r_prime,
opcode_map,
w,
vvvv & 0xF,
v_prime,
pp,
false,
ll,
false,
0,
)
}
pub fn build_evex_mem(
opcode_map: u8,
w: bool,
vvvv: u8,
pp: u8,
reg: u8,
base: u8,
index: u8,
ll: u8,
disp_size: u8, ) -> [u8; 4] {
let r = (reg >> 3) & 1 != 0;
let x = (index >> 3) & 1 != 0;
let b = (base >> 3) & 1 != 0;
let r_prime = (reg >> 4) & 1 != 0;
let v_prime = (vvvv >> 4) & 1 != 0;
let ll_encoded = match disp_size {
1 | 2 | 4 => 0,
8 | 16 => 1,
32 | 64 => 2,
_ => 0,
};
build_evex(
r,
x,
b,
r_prime,
opcode_map,
w,
vvvv & 0xF,
v_prime,
pp,
false,
ll_encoded,
false,
0,
)
}
}
pub mod apx_vex_encoder {
use super::*;
pub const VEX3_PREFIX: u8 = 0xC4;
pub const VEX2_PREFIX: u8 = 0xC5;
pub fn build_vex3(
r: bool,
x: bool,
b: bool,
mmmmm: u8,
w: bool,
vvvv: u8,
l: bool,
pp: u8,
) -> [u8; 3] {
let byte1 = ((!r as u8) << 7) | ((!x as u8) << 6) | ((!b as u8) << 5) | (mmmmm & 0x1F);
let byte2 = ((w as u8) << 7) | ((!vvvv & 0xF) << 3) | ((l as u8) << 2) | (pp & 0x3);
[VEX3_PREFIX, byte1, byte2]
}
pub fn build_vex2(r: bool, vvvv: u8, l: bool, pp: u8) -> [u8; 2] {
let byte1 = ((!r as u8) << 7) | ((!vvvv & 0xF) << 3) | ((l as u8) << 2) | (pp & 0x3);
[VEX2_PREFIX, byte1]
}
pub fn should_use_vex(num_operands: u8, has_imm: bool) -> bool {
num_operands >= 3 || has_imm
}
}
pub mod xop_encoder {
use super::*;
pub const XOP_PREFIX: u8 = 0x8F;
pub fn build_xop(
r: bool,
x: bool,
b: bool,
mmmmm: u8, w: bool,
vvvv: u8,
l: bool,
pp: u8,
) -> [u8; 3] {
let byte1 = ((!r as u8) << 7) | ((!x as u8) << 6) | ((!b as u8) << 5) | (mmmmm & 0x1F);
let byte2 = ((w as u8) << 7) | ((!vvvv & 0xF) << 3) | ((l as u8) << 2) | (pp & 0x3);
[XOP_PREFIX, byte1, byte2]
}
pub mod xop_maps {
pub const XOP_MAP8: u8 = 8;
pub const XOP_MAP9: u8 = 9;
pub const XOP_MAP10: u8 = 10;
}
}
#[derive(Debug, Clone)]
pub struct ModRMEncoder {
pub mod_: u8,
pub reg: u8,
pub rm: u8,
pub rex_r: bool, pub rex_b: bool, pub rex2_r4: bool, pub rex2_b4: bool, }
impl ModRMEncoder {
pub fn reg_direct(reg: u8, rm: u8) -> Self {
Self {
mod_: 3,
reg: reg & 0x7,
rm: rm & 0x7,
rex_r: (reg >> 3) & 1 != 0,
rex_b: (rm >> 3) & 1 != 0,
rex2_r4: (reg >> 4) & 1 != 0,
rex2_b4: (rm >> 4) & 1 != 0,
}
}
pub fn mem_no_disp(reg: u8, rm: u8) -> Self {
Self {
mod_: 0,
reg: reg & 0x7,
rm: rm & 0x7,
rex_r: (reg >> 3) & 1 != 0,
rex_b: (rm >> 3) & 1 != 0,
rex2_r4: (reg >> 4) & 1 != 0,
rex2_b4: (rm >> 4) & 1 != 0,
}
}
pub fn mem_disp8(reg: u8, rm: u8) -> Self {
Self {
mod_: 1,
reg: reg & 0x7,
rm: rm & 0x7,
rex_r: (reg >> 3) & 1 != 0,
rex_b: (rm >> 3) & 1 != 0,
rex2_r4: (reg >> 4) & 1 != 0,
rex2_b4: (rm >> 4) & 1 != 0,
}
}
pub fn mem_disp32(reg: u8, rm: u8) -> Self {
Self {
mod_: 2,
reg: reg & 0x7,
rm: rm & 0x7,
rex_r: (reg >> 3) & 1 != 0,
rex_b: (rm >> 3) & 1 != 0,
rex2_r4: (reg >> 4) & 1 != 0,
rex2_b4: (rm >> 4) & 1 != 0,
}
}
pub fn encode(&self) -> u8 {
(self.mod_ << 6) | ((self.reg & 0x7) << 3) | (self.rm & 0x7)
}
pub fn full_reg(&self) -> u8 {
self.reg | ((self.rex_r as u8) << 3) | ((self.rex2_r4 as u8) << 4)
}
pub fn full_rm(&self) -> u8 {
self.rm | ((self.rex_b as u8) << 3) | ((self.rex2_b4 as u8) << 4)
}
pub fn needs_rex2(&self) -> bool {
self.rex2_r4 || self.rex2_b4
}
}
#[derive(Debug, Clone)]
pub struct SIBEncoder {
pub scale: u8,
pub index: u8,
pub base: u8,
pub rex_x: bool,
pub rex2_x4: bool,
}
impl SIBEncoder {
pub fn no_index(base: u8) -> Self {
Self {
scale: 0,
index: 4, base: base & 0x7,
rex_x: false,
rex2_x4: false,
}
}
pub fn with_index(base: u8, index: u8, scale: u8) -> Self {
assert!(scale == 1 || scale == 2 || scale == 4 || scale == 8);
let scale_bits = match scale {
1 => 0,
2 => 1,
4 => 2,
8 => 3,
_ => 0,
};
Self {
scale: scale_bits,
index: index & 0x7,
base: base & 0x7,
rex_x: (index >> 3) & 1 != 0,
rex2_x4: (index >> 4) & 1 != 0,
}
}
pub fn rip_relative() -> Self {
Self {
scale: 0,
index: 4,
base: 5,
rex_x: false,
rex2_x4: false,
}
}
pub fn encode(&self) -> u8 {
(self.scale << 6) | ((self.index & 0x7) << 3) | (self.base & 0x7)
}
pub fn needs_rex2(&self) -> bool {
self.rex2_x4
}
}
#[derive(Debug, Clone)]
pub struct DisplacementEncoder {
pub value: i64,
pub size: u8,
pub evex_compression_n: u8,
}
impl DisplacementEncoder {
pub fn disp8(value: i8) -> Self {
Self {
value: value as i64,
size: 1,
evex_compression_n: 1,
}
}
pub fn disp16(value: i16) -> Self {
Self {
value: value as i64,
size: 2,
evex_compression_n: 1,
}
}
pub fn disp32(value: i32) -> Self {
Self {
value: value as i64,
size: 4,
evex_compression_n: 1,
}
}
pub fn evex_compressed(value: i64, n: u8) -> Self {
assert!(n == 1 || n == 2 || n == 4 || n == 8 || n == 16 || n == 32 || n == 64);
let compressed = value / n as i64;
assert!(
compressed >= -128 && compressed <= 127,
"compressed disp out of range"
);
Self {
value: compressed,
size: 1,
evex_compression_n: n,
}
}
pub fn encode(&self) -> Vec<u8> {
match self.size {
1 => vec![self.value as u8],
2 => {
let v = self.value as u16;
vec![v as u8, (v >> 8) as u8]
}
4 => {
let v = self.value as u32;
vec![v as u8, (v >> 8) as u8, (v >> 16) as u8, (v >> 24) as u8]
}
_ => vec![self.value as u8],
}
}
pub fn effective_disp(&self) -> i64 {
if self.evex_compression_n > 1 {
self.value * self.evex_compression_n as i64
} else {
self.value
}
}
pub fn fits_in_disp8(&self) -> bool {
let eff = self.effective_disp();
eff >= -128 && eff <= 127
}
}
#[derive(Debug, Clone)]
pub struct ImmediateEncoder {
pub value: i64,
pub size: u8,
pub sign_extend: bool,
}
impl ImmediateEncoder {
pub fn imm8(value: i8) -> Self {
Self {
value: value as i64,
size: 1,
sign_extend: false,
}
}
pub fn imm16(value: i16) -> Self {
Self {
value: value as i64,
size: 2,
sign_extend: false,
}
}
pub fn imm32(value: i32) -> Self {
Self {
value: value as i64,
size: 4,
sign_extend: false,
}
}
pub fn imm64(value: i64) -> Self {
Self {
value,
size: 8,
sign_extend: false,
}
}
pub fn imm32s(value: i64) -> Self {
Self {
value,
size: 4,
sign_extend: true,
}
}
pub fn encode(&self) -> Vec<u8> {
match self.size {
1 => vec![self.value as u8],
2 => {
let v = self.value as u16;
vec![v as u8, (v >> 8) as u8]
}
4 => {
let v = self.value as u32;
vec![v as u8, (v >> 8) as u8, (v >> 16) as u8, (v >> 24) as u8]
}
8 => {
let v = self.value as u64;
vec![
v as u8,
(v >> 8) as u8,
(v >> 16) as u8,
(v >> 24) as u8,
(v >> 32) as u8,
(v >> 40) as u8,
(v >> 48) as u8,
(v >> 56) as u8,
]
}
_ => vec![self.value as u8],
}
}
pub fn fits_in_imm8(&self) -> bool {
self.value >= -128 && self.value <= 127
}
pub fn fits_in_imm32(&self) -> bool {
self.value >= -(1i64 << 31) && self.value <= (1i64 << 31) - 1
}
pub fn promote_to_fit(&self) -> Self {
if self.fits_in_imm8() {
Self {
value: self.value,
size: 1,
sign_extend: true,
}
} else if self.fits_in_imm32() && self.size > 4 {
Self {
value: self.value,
size: 4,
sign_extend: true,
}
} else {
self.clone()
}
}
}
#[derive(Debug, Clone)]
pub struct OpcodeEncoder {
pub opcode_map: u8,
pub base_opcode: u8,
pub mandatory_prefix: u8,
}
impl OpcodeEncoder {
pub fn new(base_opcode: u8) -> Self {
Self {
opcode_map: 0,
base_opcode,
mandatory_prefix: 0,
}
}
pub fn with_map(mut self, map: u8) -> Self {
self.opcode_map = map;
self
}
pub fn with_prefix(mut self, prefix: u8) -> Self {
self.mandatory_prefix = prefix;
self
}
pub fn encode(&self) -> Vec<u8> {
let mut bytes = Vec::new();
match self.mandatory_prefix {
1 => bytes.push(0x66),
2 => bytes.push(0xF3),
3 => bytes.push(0xF2),
_ => {}
}
match self.opcode_map {
1 => bytes.push(0x0F),
2 => {
bytes.push(0x0F);
bytes.push(0x38);
}
3 => {
bytes.push(0x0F);
bytes.push(0x3A);
}
_ => {}
}
bytes.push(self.base_opcode);
bytes
}
pub fn prefix_len(&self) -> u8 {
let mut len: u8 = 0;
if self.mandatory_prefix != 0 {
len += 1;
}
match self.opcode_map {
0 => {}
1 => len += 1,
2 | 3 => len += 2,
_ => {}
}
len + 1 }
}
#[derive(Debug, Clone)]
pub struct FullInstructionEncoder {
pub rex_bytes: Vec<u8>,
pub vex_bytes: Vec<u8>,
pub opcode_bytes: Vec<u8>,
pub modrm: Option<u8>,
pub sib: Option<u8>,
pub displacement: Vec<u8>,
pub immediate: Vec<u8>,
}
impl FullInstructionEncoder {
pub fn new() -> Self {
Self {
rex_bytes: Vec::new(),
vex_bytes: Vec::new(),
opcode_bytes: Vec::new(),
modrm: None,
sib: None,
displacement: Vec::new(),
immediate: Vec::new(),
}
}
pub fn assemble(&self) -> Vec<u8> {
let mut result = Vec::new();
if !self.vex_bytes.is_empty() {
result.extend(&self.vex_bytes);
} else {
result.extend(&self.rex_bytes);
}
result.extend(&self.opcode_bytes);
if let Some(m) = self.modrm {
result.push(m);
}
if let Some(s) = self.sib {
result.push(s);
}
result.extend(&self.displacement);
result.extend(&self.immediate);
result
}
pub fn len(&self) -> usize {
self.rex_bytes.len()
+ self.vex_bytes.len()
+ self.opcode_bytes.len()
+ self.modrm.map_or(0, |_| 1)
+ self.sib.map_or(0, |_| 1)
+ self.displacement.len()
+ self.immediate.len()
}
pub fn is_valid_length(&self) -> bool {
self.len() <= 15
}
pub fn is_vex_encoded(&self) -> bool {
!self.vex_bytes.is_empty()
}
pub fn is_rex_encoded(&self) -> bool {
!self.rex_bytes.is_empty()
}
pub fn is_legacy_encoded(&self) -> bool {
self.rex_bytes.is_empty() && self.vex_bytes.is_empty()
}
}
impl Default for FullInstructionEncoder {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct InstructionBuilder {
pub encoder: FullInstructionEncoder,
}
impl InstructionBuilder {
pub fn new() -> Self {
Self {
encoder: FullInstructionEncoder::new(),
}
}
pub fn opcode(mut self, op: OpcodeEncoder) -> Self {
self.encoder.opcode_bytes = op.encode();
self
}
pub fn rex(mut self, bytes: &[u8]) -> Self {
self.encoder.rex_bytes = bytes.to_vec();
self
}
pub fn modrm(mut self, enc: &ModRMEncoder) -> Self {
self.encoder.modrm = Some(enc.encode());
self
}
pub fn sib(mut self, enc: &SIBEncoder) -> Self {
self.encoder.sib = Some(enc.encode());
self
}
pub fn displacement(mut self, enc: &DisplacementEncoder) -> Self {
self.encoder.displacement = enc.encode();
self
}
pub fn immediate(mut self, enc: &ImmediateEncoder) -> Self {
self.encoder.immediate = enc.encode();
self
}
pub fn build(&self) -> Vec<u8> {
self.encoder.assemble()
}
pub fn len(&self) -> usize {
self.encoder.len()
}
}
impl Default for InstructionBuilder {
fn default() -> Self {
Self::new()
}
}
pub fn encode_alu_rr(opcode: &OpcodeEncoder, dst: u8, src: u8, is_64bit: bool) -> Vec<u8> {
let modrm = ModRMEncoder::reg_direct(dst, src);
let rex = apx_rex2_encoder::encode_prefix(is_64bit, dst, src);
let mut bytes = Vec::new();
bytes.extend(&rex);
bytes.extend(opcode.encode());
bytes.push(modrm.encode());
bytes
}
pub fn encode_alu_rm(
opcode: &OpcodeEncoder,
reg: u8,
base: u8,
index: u8,
scale: u8,
disp: &DisplacementEncoder,
is_64bit: bool,
) -> Vec<u8> {
let sib = SIBEncoder::with_index(base, index, scale);
let modrm = if disp.effective_disp() == 0 {
ModRMEncoder::mem_no_disp(reg, base)
} else if disp.fits_in_disp8() {
ModRMEncoder::mem_disp8(reg, base)
} else {
ModRMEncoder::mem_disp32(reg, base)
};
let rex = apx_rex2_encoder::encode_prefix_mem(is_64bit, reg, base, index);
let mut bytes = Vec::new();
bytes.extend(&rex);
bytes.extend(opcode.encode());
bytes.push(modrm.encode());
bytes.push(sib.encode());
bytes.extend(disp.encode());
bytes
}
pub fn encode_alu_ri(
group_opcode: u8, reg_field: u8, dst: u8,
imm: &ImmediateEncoder,
is_64bit: bool,
) -> Vec<u8> {
let modrm = ModRMEncoder::reg_direct(reg_field, dst);
let rex = apx_rex2_encoder::encode_prefix(is_64bit, reg_field, dst);
let mut bytes = Vec::new();
bytes.extend(&rex);
bytes.push(group_opcode);
bytes.push(modrm.encode());
bytes.extend(imm.encode());
bytes
}
pub mod evex_compressed_displacement {
use super::EvexTuple;
pub fn multiplier_for(tuple: EvexTuple, element_size_bits: u8) -> u8 {
let full_n = match element_size_bits {
8 => 64, 16 => 64, 32 => 64, 64 => 64, _ => 64,
};
match tuple {
EvexTuple::Full => full_n,
EvexTuple::FullMem => 64,
EvexTuple::Half | EvexTuple::HalfMem => 32,
EvexTuple::Quarter | EvexTuple::QuarterMem | EvexTuple::Mem128 => 16,
EvexTuple::Eighth | EvexTuple::EighthMem => 8,
EvexTuple::Scalar => match element_size_bits {
64 => 8,
_ => 4,
},
}
}
pub const DISP8_TABLE_FULL: [(u8, i32); 256] = {
let mut table = [(0u8, 0i32); 256];
let mut i = 0u16;
while i < 256 {
let disp8 = i as i8;
table[i as usize] = (i as u8, disp8 as i32 * 64);
i += 1;
}
table
};
pub const DISP8_TABLE_HALF: [(u8, i32); 256] = {
let mut table = [(0u8, 0i32); 256];
let mut i = 0u16;
while i < 256 {
let disp8 = i as i8;
table[i as usize] = (i as u8, disp8 as i32 * 32);
i += 1;
}
table
};
pub const DISP8_TABLE_QUARTER: [(u8, i32); 256] = {
let mut table = [(0u8, 0i32); 256];
let mut i = 0u16;
while i < 256 {
let disp8 = i as i8;
table[i as usize] = (i as u8, disp8 as i32 * 16);
i += 1;
}
table
};
pub const DISP8_TABLE_EIGHTH: [(u8, i32); 256] = {
let mut table = [(0u8, 0i32); 256];
let mut i = 0u16;
while i < 256 {
let disp8 = i as i8;
table[i as usize] = (i as u8, disp8 as i32 * 8);
i += 1;
}
table
};
pub const DISP8_TABLE_SCALAR_4: [(u8, i32); 256] = {
let mut table = [(0u8, 0i32); 256];
let mut i = 0u16;
while i < 256 {
let disp8 = i as i8;
table[i as usize] = (i as u8, disp8 as i32 * 4);
i += 1;
}
table
};
pub const DISP8_TABLE_SCALAR_8: [(u8, i32); 256] = {
let mut table = [(0u8, 0i32); 256];
let mut i = 0u16;
while i < 256 {
let disp8 = i as i8;
table[i as usize] = (i as u8, disp8 as i32 * 8);
i += 1;
}
table
};
pub fn compress_disp(real_disp: i32, n: u8) -> Option<u8> {
if real_disp % n as i32 != 0 {
return None;
}
let compressed = real_disp / n as i32;
if compressed < -128 || compressed > 127 {
return None;
}
Some(compressed as u8)
}
pub fn decompress_disp(disp8: u8, n: u8) -> i32 {
(disp8 as i8 as i32) * n as i32
}
pub fn get_disp_table(tuple: EvexTuple, element_size_bits: u8) -> &'static [(u8, i32); 256] {
match tuple {
EvexTuple::Full | EvexTuple::FullMem => &DISP8_TABLE_FULL,
EvexTuple::Half | EvexTuple::HalfMem => &DISP8_TABLE_HALF,
EvexTuple::Quarter | EvexTuple::QuarterMem | EvexTuple::Mem128 => &DISP8_TABLE_QUARTER,
EvexTuple::Eighth | EvexTuple::EighthMem => &DISP8_TABLE_EIGHTH,
EvexTuple::Scalar => {
if element_size_bits == 64 {
&DISP8_TABLE_SCALAR_8
} else {
&DISP8_TABLE_SCALAR_4
}
}
}
}
}
pub mod evex_broadcast_encoding {
use super::EvexBroadcast;
pub fn element_count(broadcast: EvexBroadcast, vector_len_bits: u16) -> u8 {
let vl_bytes = vector_len_bits / 8;
match broadcast {
EvexBroadcast::None => vl_bytes as u8,
EvexBroadcast::Broadcast1To2 => 2,
EvexBroadcast::Broadcast1To4 => 4,
EvexBroadcast::Broadcast1To8 => 8,
EvexBroadcast::Broadcast1To16 => 16,
EvexBroadcast::Broadcast1To32 => 32,
}
}
pub fn element_size_bytes(broadcast: EvexBroadcast, vector_len_bits: u16) -> u8 {
let count = element_count(broadcast, vector_len_bits);
if count == 0 {
return 0;
}
(vector_len_bits / 8) as u8 / count
}
pub fn is_valid_broadcast(broadcast: EvexBroadcast, vector_len_bits: u16) -> bool {
match broadcast {
EvexBroadcast::None => true,
EvexBroadcast::Broadcast1To2 => vector_len_bits >= 128,
EvexBroadcast::Broadcast1To4 => vector_len_bits >= 128,
EvexBroadcast::Broadcast1To8 => vector_len_bits >= 256,
EvexBroadcast::Broadcast1To16 => vector_len_bits >= 256,
EvexBroadcast::Broadcast1To32 => vector_len_bits == 512,
}
}
pub fn broadcast_disp_multiplier(broadcast: EvexBroadcast, vector_len_bits: u16) -> u8 {
if broadcast == EvexBroadcast::None {
return 64; }
element_size_bytes(broadcast, vector_len_bits)
}
pub fn to_b_bit(broadcast: EvexBroadcast) -> bool {
broadcast.is_broadcast()
}
pub fn to_string(broadcast: EvexBroadcast) -> &'static str {
match broadcast {
EvexBroadcast::None => "",
EvexBroadcast::Broadcast1To2 => "{1to2}",
EvexBroadcast::Broadcast1To4 => "{1to4}",
EvexBroadcast::Broadcast1To8 => "{1to8}",
EvexBroadcast::Broadcast1To16 => "{1to16}",
EvexBroadcast::Broadcast1To32 => "{1to32}",
}
}
}
pub mod evex_rounding_encoding {
use super::EvexRounding;
pub fn to_rc_bits(rounding: EvexRounding) -> u8 {
match rounding {
EvexRounding::None => 0,
EvexRounding::RnSae => 0,
EvexRounding::RdSae => 1,
EvexRounding::RuSae => 2,
EvexRounding::RzSae => 3,
EvexRounding::Sae => 0,
}
}
pub fn has_sae(rounding: EvexRounding) -> bool {
matches!(
rounding,
EvexRounding::RnSae
| EvexRounding::RdSae
| EvexRounding::RuSae
| EvexRounding::RzSae
| EvexRounding::Sae
)
}
pub fn has_explicit_rc(rounding: EvexRounding) -> bool {
matches!(
rounding,
EvexRounding::RnSae | EvexRounding::RdSae | EvexRounding::RuSae | EvexRounding::RzSae
)
}
pub fn encode_ll_for_rounding(rounding: EvexRounding) -> u8 {
to_rc_bits(rounding) & 0x3
}
pub fn to_string(rounding: EvexRounding) -> &'static str {
match rounding {
EvexRounding::None => "",
EvexRounding::RnSae => "{rn-sae}",
EvexRounding::RdSae => "{rd-sae}",
EvexRounding::RuSae => "{ru-sae}",
EvexRounding::RzSae => "{rz-sae}",
EvexRounding::Sae => "{sae}",
}
}
pub fn from_suffix(s: &str) -> Option<EvexRounding> {
match s {
"rn-sae" => Some(EvexRounding::RnSae),
"rd-sae" => Some(EvexRounding::RdSae),
"ru-sae" => Some(EvexRounding::RuSae),
"rz-sae" => Some(EvexRounding::RzSae),
"sae" => Some(EvexRounding::Sae),
_ => None,
}
}
}
pub mod evex_opmask_encoding {
pub fn to_aaa(mask_reg: u8) -> u8 {
mask_reg & 0x7
}
pub fn from_aaa(aaa: u8) -> u8 {
aaa & 0x7
}
pub fn reg_name(reg: u8) -> &'static str {
match reg & 0x7 {
0 => "k0",
1 => "k1",
2 => "k2",
3 => "k3",
4 => "k4",
5 => "k5",
6 => "k6",
7 => "k7",
_ => "k0",
}
}
pub fn has_active_mask(mask_reg: u8) -> bool {
(mask_reg & 0x7) != 0
}
pub fn is_valid_opmask(reg: u8) -> bool {
(reg & 0xF8) == 0
}
}
pub mod evex_zeroing_encoding {
pub fn has_zeroing(z: bool) -> bool {
z
}
pub fn effective_zeroing(z: bool, has_mask: bool) -> bool {
z && has_mask
}
pub fn to_string(z: bool) -> &'static str {
if z {
"{z}"
} else {
""
}
}
}
pub mod evex_vector_length {
pub fn encode(vl_bits: u16) -> u8 {
match vl_bits {
128 => 0b00,
256 => 0b01,
512 => 0b10,
_ => 0b00, }
}
pub fn decode(ll: u8) -> u16 {
match ll & 0x3 {
0b00 => 128,
0b01 => 256,
0b10 => 512,
_ => 128,
}
}
pub fn to_l_prime(ll: u8) -> bool {
(ll >> 1) & 1 != 0
}
pub fn to_l(ll: u8) -> bool {
ll & 1 != 0
}
pub fn vex_l_bit(vl_bits: u16) -> bool {
vl_bits >= 256
}
pub fn is_valid_evex_vl(vl_bits: u16) -> bool {
matches!(vl_bits, 128 | 256 | 512)
}
pub fn to_string(vl_bits: u16) -> &'static str {
match vl_bits {
128 => "xmm",
256 => "ymm",
512 => "zmm",
_ => "unknown",
}
}
}
pub mod vex_full_encoder {
pub fn build_full(
r: bool,
x: bool,
b: bool,
mmmmm: u8,
w: bool,
vvvv: u8,
l: bool,
pp: u8,
) -> [u8; 3] {
let byte1 = ((!r as u8) << 7) | ((!x as u8) << 6) | ((!b as u8) << 5) | (mmmmm & 0x1F);
let byte2 = ((w as u8) << 7) | ((!vvvv & 0xF) << 3) | ((l as u8) << 2) | (pp & 0x3);
[0xC4, byte1, byte2]
}
pub mod vex_map {
pub const MAP_NONE: u8 = 0;
pub const MAP_0F: u8 = 1;
pub const MAP_0F38: u8 = 2;
pub const MAP_0F3A: u8 = 3;
}
pub mod vex_pp {
pub const PP_NONE: u8 = 0;
pub const PP_66: u8 = 1;
pub const PP_F3: u8 = 2;
pub const PP_F2: u8 = 3;
}
pub fn decode_vex3(bytes: &[u8]) -> Option<(bool, bool, bool, u8, bool, u8, bool, u8)> {
if bytes.len() < 3 || bytes[0] != 0xC4 {
return None;
}
let byte1 = bytes[1];
let byte2 = bytes[2];
let r = (byte1 >> 7) & 1 == 0;
let x = (byte1 >> 6) & 1 == 0;
let b = (byte1 >> 5) & 1 == 0;
let mmmmm = byte1 & 0x1F;
let w = (byte2 >> 7) & 1 != 0;
let vvvv = !((byte2 >> 3) & 0xF) & 0xF;
let l = (byte2 >> 2) & 1 != 0;
let pp = byte2 & 0x3;
Some((r, x, b, mmmmm, w, vvvv, l, pp))
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct VexFields {
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 VexFields {
pub fn encode_3byte(&self) -> [u8; 3] {
build_full(
self.r, self.x, self.b, self.mmmmm, self.w, self.vvvv, self.l, self.pp,
)
}
pub fn effective_reg_r(&self, reg: u8) -> u8 {
if self.r {
reg | 0x8
} else {
reg & 0x7
}
}
pub fn effective_reg_x(&self, idx: u8) -> u8 {
if self.x {
idx | 0x8
} else {
idx & 0x7
}
}
pub fn effective_reg_b(&self, base: u8) -> u8 {
if self.b {
base | 0x8
} else {
base & 0x7
}
}
}
}
pub mod xop_full_encoder {
pub mod xop_opcodes {
pub const VPHADDBD: u8 = 0xC2;
pub const VPHADDBQ: u8 = 0xC3;
pub const VPHADDWD: u8 = 0xC6;
pub const VPHADDWQ: u8 = 0xC7;
pub const VPHADDDQ: u8 = 0xCB;
pub const VPHADDUBD: u8 = 0xD2;
pub const VPHADDUBQ: u8 = 0xD3;
pub const VPHADDUWD: u8 = 0xD6;
pub const VPHADDUWQ: u8 = 0xD7;
pub const VPHADDUDQ: u8 = 0xDB;
pub const VPHSUBBW: u8 = 0xE2;
pub const VPHSUBDQ: u8 = 0xE3;
pub const VPHSUBWD: u8 = 0xE6;
pub const VPROTB: u8 = 0x90;
pub const VPROTW: u8 = 0x91;
pub const VPROTD: u8 = 0x92;
pub const VPROTQ: u8 = 0x93;
pub const VPMACSSWW: u8 = 0x95;
pub const VPMACSSWD: u8 = 0x96;
pub const VPMACSSDQL: u8 = 0x9E;
pub const VPMACSSDQH: u8 = 0x9F;
pub const VPMACSSDD: u8 = 0x9C;
pub const VPMACSWW: u8 = 0xA5;
pub const VPMACSWD: u8 = 0xA6;
pub const VPMACSDQL: u8 = 0xAE;
pub const VPMACSDQH: u8 = 0xAF;
pub const VPMACSDD: u8 = 0xAC;
pub const VPMADCSSWD: u8 = 0xB6;
pub const VPMADCSWD: u8 = 0xC6;
pub const VPSHAB: u8 = 0x98;
pub const VPSHAW: u8 = 0x99;
pub const VPSHAD: u8 = 0x9A;
pub const VPSHAQ: u8 = 0x9B;
pub const VPSHLB: u8 = 0x94;
pub const VPSHLW: u8 = 0x95;
pub const VPSHLD: u8 = 0x96;
pub const VPSHLQ: u8 = 0x97;
pub const VFRCZPS: u8 = 0x80;
pub const VFRCZPD: u8 = 0x81;
pub const VFRCZSS: u8 = 0x82;
pub const VFRCZSD: u8 = 0x83;
pub const VPCMOV: u8 = 0xA2;
pub const VPPERM: u8 = 0xA3;
pub const VPCOMB: u8 = 0xCC;
pub const VPCOMW: u8 = 0xCD;
pub const VPCOMD: u8 = 0xCE;
pub const VPCOMQ: u8 = 0xCF;
pub const VPCOMUB: u8 = 0xEC;
pub const VPCOMUW: u8 = 0xED;
pub const VPCOMUD: u8 = 0xEE;
pub const VPCOMUQ: u8 = 0xEF;
}
pub fn xop_mnemonic(opcode: u8, map: u8) -> Option<&'static str> {
match (map, opcode) {
(8, 0x90) => Some("vprotb"),
(8, 0x91) => Some("vprotw"),
(8, 0x92) => Some("vprotd"),
(8, 0x93) => Some("vprotq"),
(8, 0x95) => Some("vpmacssww"),
(8, 0x96) => Some("vpmacsswd"),
(8, 0x9C) => Some("vpmacssdd"),
(8, 0x9E) => Some("vpmacssdql"),
(8, 0x9F) => Some("vpmacssdqh"),
(8, 0xA5) => Some("vpmacsww"),
(8, 0xA6) => Some("vpmacswd"),
(8, 0xAC) => Some("vpmacsdd"),
(8, 0xAE) => Some("vpmacsdql"),
(8, 0xAF) => Some("vpmacsdqh"),
(8, 0xB6) => Some("vpmadcsswd"),
(8, 0xC2) => Some("vphaddbd"),
(8, 0xC3) => Some("vphaddbq"),
(8, 0xC6) => Some("vphaddwd"),
(8, 0xC7) => Some("vphaddwq"),
(8, 0xCB) => Some("vphadddq"),
(8, 0xD2) => Some("vphaddubd"),
(8, 0xD3) => Some("vphaddubq"),
(8, 0xD6) => Some("vphadduwd"),
(8, 0xD7) => Some("vphadduwq"),
(8, 0xDB) => Some("vphaddudq"),
(8, 0xE2) => Some("vphsubbw"),
(8, 0xE3) => Some("vphsubdq"),
(8, 0xE6) => Some("vphsubwd"),
(9, 0x94) => Some("vpshlb"),
(9, 0x95) => Some("vpshlw"),
(9, 0x96) => Some("vpshld"),
(9, 0x97) => Some("vpshlq"),
(9, 0x98) => Some("vpshab"),
(9, 0x99) => Some("vpshaw"),
(9, 0x9A) => Some("vpshad"),
(9, 0x9B) => Some("vpshaq"),
(10, 0x80) => Some("vfrcsps"),
(10, 0x81) => Some("vfrcspd"),
(10, 0x82) => Some("vfrcsss"),
(10, 0x83) => Some("vfrcssd"),
(10, 0xA2) => Some("vpcmov"),
(10, 0xA3) => Some("vpperm"),
(10, 0xCC) => Some("vpcomb"),
(10, 0xCD) => Some("vpcomw"),
(10, 0xCE) => Some("vpcomd"),
(10, 0xCF) => Some("vpcomq"),
(10, 0xEC) => Some("vpcomub"),
(10, 0xED) => Some("vpcomuw"),
(10, 0xEE) => Some("vpcomud"),
(10, 0xEF) => Some("vpcomuq"),
_ => None,
}
}
}
pub mod tdnow_encoder {
pub mod tdnow_opcodes {
pub const PI2FD: u8 = 0x0D;
pub const PI2FW: u8 = 0x0C;
pub const PF2ID: u8 = 0x1D;
pub const PF2IW: u8 = 0x1C;
pub const PFACC: u8 = 0xAE;
pub const PFADD: u8 = 0x9E;
pub const PFCMPEQ: u8 = 0xB0;
pub const PFCMPGE: u8 = 0x90;
pub const PFCMPGT: u8 = 0xA0;
pub const PFMAX: u8 = 0xA4;
pub const PFMIN: u8 = 0x94;
pub const PFMUL: u8 = 0xB4;
pub const PFNACC: u8 = 0x8A;
pub const PFPNACC: u8 = 0x8E;
pub const PFRCP: u8 = 0x96;
pub const PFRCPIT1: u8 = 0xA6;
pub const PFRCPIT2: u8 = 0xB6;
pub const PFRSQIT1: u8 = 0xA7;
pub const PFRSQRT: u8 = 0x97;
pub const PFSUB: u8 = 0x9A;
pub const PFSUBR: u8 = 0xAA;
pub const PMULHRW: u8 = 0xB7;
pub const PAVGUSB: u8 = 0xBF;
pub const FEMMS: u8 = 0x0E;
pub const PREFETCH: u8 = 0x0D;
pub const PREFETCHW: u8 = 0x0D; }
pub fn encode(mm_reg1: u8, mm_reg2: u8, tdnow_opcode: u8) -> [u8; 4] {
let modrm = 0xC0 | ((mm_reg2 & 0x7) << 3) | (mm_reg1 & 0x7);
[0x0F, 0x0F, modrm, tdnow_opcode]
}
pub fn encode_femms() -> [u8; 2] {
[0x0F, 0x0E]
}
pub fn is_tdnow_prefix(b0: u8, b1: u8) -> bool {
b0 == 0x0F && b1 == 0x0F
}
pub fn tdnow_mnemonic(opcode: u8) -> Option<&'static str> {
match opcode {
0x0C => Some("pi2fw"),
0x0D => Some("pi2fd"),
0x1C => Some("pf2iw"),
0x1D => Some("pf2id"),
0x8A => Some("pfnacc"),
0x8E => Some("pfpnacc"),
0x90 => Some("pfcmpge"),
0x94 => Some("pfmin"),
0x96 => Some("pfrcp"),
0x97 => Some("pfrsqrt"),
0x9A => Some("pfsub"),
0x9E => Some("pfadd"),
0xA0 => Some("pfcmpgt"),
0xA4 => Some("pfmax"),
0xA6 => Some("pfrcpit1"),
0xA7 => Some("pfrsqit1"),
0xAA => Some("pfsubr"),
0xAE => Some("pfacc"),
0xB0 => Some("pfcmpeq"),
0xB4 => Some("pfmul"),
0xB6 => Some("pfrcpit2"),
0xB7 => Some("pmulhrw"),
0xBF => Some("pavgusb"),
0x0E => Some("femms"),
_ => None,
}
}
}
pub mod immediate_size_table {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ImmSize {
None,
Imm8,
Imm16,
Imm32,
Imm64,
Imm8SignExt,
}
pub fn size_for_opcode(opcode: u8, has_rex_w: bool) -> ImmSize {
match opcode {
0x6A => ImmSize::Imm8, 0x6B => ImmSize::Imm8, 0x80 => ImmSize::Imm8, 0x82 => ImmSize::Imm8, 0x83 => ImmSize::Imm8, 0xA8 => ImmSize::Imm8, 0xC0 => ImmSize::Imm8, 0xC1 => ImmSize::Imm8, 0xC6 => ImmSize::Imm8,
0x68 => {
if has_rex_w {
ImmSize::Imm64
} else {
ImmSize::Imm32
}
}
0x69 => ImmSize::Imm32, 0x81 => {
if has_rex_w {
ImmSize::Imm32
} else {
ImmSize::Imm32
}
}
0xB8..=0xBF => {
if has_rex_w {
ImmSize::Imm64
} else {
ImmSize::Imm32
}
}
0xC7 => {
if has_rex_w {
ImmSize::Imm32
} else {
ImmSize::Imm32
}
}
0x04 => ImmSize::Imm8, 0x05 => {
if has_rex_w {
ImmSize::Imm32
} else {
ImmSize::Imm32
}
}
0x0C => ImmSize::Imm8, 0x0D => {
if has_rex_w {
ImmSize::Imm32
} else {
ImmSize::Imm32
}
}
0x14 => ImmSize::Imm8, 0x15 => {
if has_rex_w {
ImmSize::Imm32
} else {
ImmSize::Imm32
}
}
0x1C => ImmSize::Imm8, 0x1D => {
if has_rex_w {
ImmSize::Imm32
} else {
ImmSize::Imm32
}
}
0x24 => ImmSize::Imm8, 0x25 => {
if has_rex_w {
ImmSize::Imm32
} else {
ImmSize::Imm32
}
}
0x2C => ImmSize::Imm8, 0x2D => {
if has_rex_w {
ImmSize::Imm32
} else {
ImmSize::Imm32
}
}
0x34 => ImmSize::Imm8, 0x35 => {
if has_rex_w {
ImmSize::Imm32
} else {
ImmSize::Imm32
}
}
0x3C => ImmSize::Imm8, 0x3D => {
if has_rex_w {
ImmSize::Imm32
} else {
ImmSize::Imm32
}
}
0xE8 | 0xE9 => ImmSize::Imm32, 0xEB => ImmSize::Imm8, 0x70..=0x7F => ImmSize::Imm8, 0xE0..=0xE3 => ImmSize::Imm8,
0xC8 => ImmSize::Imm16,
0xC2 | 0xCA => ImmSize::Imm16,
0xCC => ImmSize::None, 0xCD => ImmSize::Imm8,
0xE4 | 0xE6 => ImmSize::Imm8, 0xE5 | 0xE7 => ImmSize::Imm8,
0xD4 => ImmSize::Imm8, 0xD5 => ImmSize::Imm8,
_ => ImmSize::None,
}
}
pub fn size_in_bytes(size: ImmSize) -> u8 {
match size {
ImmSize::None => 0,
ImmSize::Imm8 | ImmSize::Imm8SignExt => 1,
ImmSize::Imm16 => 2,
ImmSize::Imm32 => 4,
ImmSize::Imm64 => 8,
}
}
pub fn is_sign_extended(size: ImmSize) -> bool {
matches!(size, ImmSize::Imm8SignExt)
}
}
pub mod sign_extension {
pub fn ext8_to_16(value: i8) -> i16 {
value as i16
}
pub fn ext8_to_32(value: i8) -> i32 {
value as i32
}
pub fn ext8_to_64(value: i8) -> i64 {
value as i64
}
pub fn ext16_to_32(value: i16) -> i32 {
value as i32
}
pub fn ext16_to_64(value: i16) -> i64 {
value as i64
}
pub fn ext32_to_64(value: i32) -> i64 {
value as i64
}
pub fn sign_extend_imm(value: i64, dest_size_bits: u8) -> i64 {
match dest_size_bits {
8 => (value as i8) as i64,
16 => (value as i16) as i64,
32 => (value as i32) as i64,
64 => value,
_ => value,
}
}
pub fn fits_in_sign_ext_imm8(value: i64, operand_size_bits: u8) -> bool {
let truncated = sign_extend_imm(value, operand_size_bits);
let back_to_8 = (truncated as i8) as i64;
sign_extend_imm(back_to_8, operand_size_bits) == truncated
}
pub fn fits_in_sign_ext_imm32(value: i64) -> bool {
(value as i32) as i64 == value
}
}
#[cfg(test)]
mod tests {
use super::super::x86_subtarget::X86Subtarget;
use super::*;
fn make_subtarget() -> X86Subtarget {
X86Subtarget::new("x86_64-unknown-none", "generic", "")
}
fn make_encoder() -> X86FullMCEncoder {
X86FullMCEncoder::new(X86Mode::Mode64, make_subtarget())
}
#[test]
fn test_encode_nop() {
let mut enc = make_encoder();
let bytes = enc.encode_nop();
assert!(!bytes.is_empty(), "NOP should produce at least 1 byte");
}
#[test]
fn test_encode_ret() {
let mut enc = make_encoder();
let bytes = enc.encode_ret();
assert!(!bytes.is_empty(), "RET should produce at least 1 byte");
}
#[test]
fn test_encode_call() {
let mut enc = make_encoder();
let bytes = enc.encode_call(0x12345678);
assert_eq!(bytes[0], 0xE8);
assert_eq!(&bytes[1..5], &0x12345678u32.to_le_bytes());
}
#[test]
fn test_encode_mov_rax_rcx() {
let mut enc = make_encoder();
let bytes = enc.encode_mov_rr(RAX, RCX);
assert_eq!(bytes[1], 0x89); assert_eq!(bytes[2], 0xC8); }
#[test]
fn test_encode_add_rax_imm5() {
let mut enc = make_encoder();
let bytes = enc.encode_add_ri(RAX, 5);
assert!(bytes.len() >= 3, "ADD RAX, 5 should be 3+ bytes");
}
#[test]
fn test_encode_sub_rax_rdx() {
let mut enc = make_encoder();
let bytes = enc.encode_sub_rr(RAX, RDX);
assert!(bytes.len() >= 2);
}
#[test]
fn test_encode_and_rax_rbx() {
let mut enc = make_encoder();
let bytes = enc.encode_and_rr(RAX, RBX);
assert!(bytes.len() >= 2);
}
#[test]
fn test_encode_xor_rax_rax() {
let mut enc = make_encoder();
let bytes = enc.encode_xor_rr(RAX, RAX);
assert!(bytes.len() >= 2);
}
#[test]
fn test_encode_cmp_rax_rcx() {
let mut enc = make_encoder();
let bytes = enc.encode_cmp_rr(RAX, RCX);
assert!(bytes.len() >= 2);
}
#[test]
fn test_encode_mov_r8_r9() {
let mut enc = make_encoder();
let bytes = enc.encode_mov_rr(R8, R9);
assert_eq!(bytes[0], 0x4D); assert_eq!(bytes[1], 0x89); assert_eq!(bytes[2], 0xC8);
}
#[test]
fn test_encode_mov_rax_r8() {
let mut enc = make_encoder();
let bytes = enc.encode_mov_rr(RAX, R8);
assert_eq!(bytes[0], 0x4C); assert_eq!(bytes[1], 0x89);
assert_eq!(bytes[2], 0xC0); }
#[test]
fn test_encode_mov_from_mem() {
let mut enc = make_encoder();
let mem = X86MemOperand::base_disp(RBP, -8);
let bytes = enc.encode_mov_rm(RAX, mem);
assert_eq!(bytes[2], 0x45);
assert_eq!(bytes[3], 0xF8); }
#[test]
fn test_encode_mov_to_mem() {
let mut enc = make_encoder();
let mem = X86MemOperand::base_disp(RSP, 16);
let bytes = enc.encode_mov_mr(mem, RCX);
assert_eq!(bytes[1], 0x89); assert_eq!(bytes[2], 0x4C);
assert_eq!(bytes[3], 0x24);
assert_eq!(bytes[4], 0x10); }
#[test]
fn test_encode_mov_with_index() {
let mut enc = make_encoder();
let mem = X86MemOperand::full(RBX, RSI, 4, 0);
let bytes = enc.encode_mov_rm(RAX, mem);
assert_eq!(bytes[2], 0x04);
assert_eq!(bytes[3], 0x73);
assert_eq!(bytes.len(), 4);
}
#[test]
fn test_encode_rip_relative() {
let mut enc = make_encoder();
let mem = X86FullMCEncoder::rip_relative(0x1234);
let bytes = enc.encode_mov_rm(RAX, mem);
assert_eq!(bytes[2], 0x05);
assert_eq!(&bytes[3..7], &0x1234i32.to_le_bytes());
}
#[test]
fn test_encode_vaddps() {
let mut enc = make_encoder();
let bytes = enc.encode_vaddps(XMM0, XMM1, XMM2);
assert_eq!(bytes[0], 0xC4);
assert!(!bytes.is_empty());
let opcode_pos = 3;
}
#[test]
fn test_encode_aesenc() {
let mut enc = make_encoder();
let bytes = enc.encode_aesenc(XMM0, XMM1);
assert_eq!(bytes[0], 0x66); assert_eq!(bytes[1], 0x0F);
assert_eq!(bytes[2], 0x38);
assert_eq!(bytes[3], 0xDC);
assert_eq!(bytes[4], 0xC8);
}
#[test]
fn test_encode_vpaddd_z() {
let mut enc = make_encoder();
let bytes = enc.encode_instruction(
X86FullOpcode::VPADDD_Z,
&[
X86Operand::Reg(ZMM0),
X86Operand::Reg(ZMM1),
X86Operand::Reg(ZMM2),
],
);
assert!(bytes.len() >= 6); }
#[test]
fn test_roundtrip_nop() {
let mut enc = make_encoder();
let bytes = enc.encode_nop();
assert!(!bytes.is_empty(), "NOP should produce at least 1 byte");
}
#[test]
fn test_roundtrip_ret() {
let mut enc = make_encoder();
let bytes = enc.encode_ret();
assert!(!bytes.is_empty(), "RET should produce at least 1 byte");
}
#[test]
fn test_roundtrip_mov_rax_rcx() {
let mut enc = make_encoder();
let bytes = enc.encode_mov_rr(RAX, RCX);
assert!(bytes.len() >= 2);
}
#[test]
fn test_roundtrip_add_rax_rcx() {
let mut enc = make_encoder();
let bytes = enc.encode_add_rr(RAX, RCX);
assert!(bytes.len() >= 2);
}
#[test]
fn test_roundtrip_xor_rax_rax() {
let mut enc = make_encoder();
let bytes = enc.encode_xor_rr(RAX, RAX);
assert!(bytes.len() >= 2);
}
}