use core::convert::From;
use core::convert::TryInto;
use core::{cmp, fmt, slice};
use capstone_sys::cs_x86_encoding;
pub use capstone_sys::x86_avx_bcast as X86AvxBcast;
pub use capstone_sys::x86_avx_cc as X86AvxCC;
pub use capstone_sys::x86_avx_rm as X86AvxRm;
pub use capstone_sys::x86_insn as X86Insn;
pub use capstone_sys::x86_insn_group as X86InsnGroup;
pub use capstone_sys::x86_prefix as X86Prefix;
pub use capstone_sys::x86_reg as X86Reg;
pub use capstone_sys::x86_sse_cc as X86SseCC;
pub use capstone_sys::x86_xop_cc as X86XopCC;
use capstone_sys::{
cs_ac_type, cs_x86, cs_x86_op, cs_x86_op__bindgen_ty_1, x86_op_mem, x86_op_type,
};
use super::InsnOffsetSpan;
pub use crate::arch::arch_builder::x86::*;
use crate::arch::DetailsArchInsn;
use crate::instruction::{AccessType, RegId, RegIdInt};
pub struct X86InsnDetail<'a>(pub(crate) &'a cs_x86);
impl X86OperandType {
fn new(op_type: x86_op_type, value: cs_x86_op__bindgen_ty_1) -> X86OperandType {
use self::x86_op_type::*;
use self::X86OperandType::*;
match op_type {
X86_OP_REG => Reg(RegId(unsafe { value.reg } as RegIdInt)),
X86_OP_IMM => Imm(unsafe { value.imm }),
X86_OP_MEM => Mem(X86OpMem(unsafe { value.mem })),
X86_OP_INVALID => Invalid,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct X86Operand {
pub size: u8,
pub access: Option<AccessType>,
pub avx_bcast: X86AvxBcast,
pub avx_zero_opmask: bool,
pub op_type: X86OperandType,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum X86OperandType {
Reg(RegId),
Imm(i64),
Mem(X86OpMem),
Invalid,
}
#[derive(Debug, Copy, Clone)]
pub struct X86OpMem(pub(crate) x86_op_mem);
impl X86InsnDetail<'_> {
pub fn prefix(&self) -> &[u8; 4] {
&self.0.prefix
}
pub fn opcode(&self) -> &[u8; 4] {
&self.0.opcode
}
pub fn encoding(&self) -> X86Encoding {
self.0.encoding.into()
}
pub fn rex(&self) -> u8 {
self.0.rex
}
pub fn addr_size(&self) -> u8 {
self.0.addr_size
}
pub fn modrm(&self) -> u8 {
self.0.modrm
}
pub fn sib(&self) -> u8 {
self.0.sib
}
pub fn disp(&self) -> i64 {
self.0.disp
}
pub fn sib_index(&self) -> RegId {
RegId(self.0.sib_index as RegIdInt)
}
pub fn sib_scale(&self) -> i8 {
self.0.sib_scale
}
pub fn sib_base(&self) -> RegId {
RegId(self.0.sib_base as RegIdInt)
}
pub fn xop_cc(&self) -> X86XopCC {
self.0.xop_cc
}
pub fn sse_cc(&self) -> X86SseCC {
self.0.sse_cc
}
pub fn avx_cc(&self) -> X86AvxCC {
self.0.avx_cc
}
pub fn avx_sae(&self) -> bool {
self.0.avx_sae
}
pub fn avx_rm(&self) -> X86AvxRm {
self.0.avx_rm
}
}
impl_PartialEq_repr_fields!(X86InsnDetail<'a> [ 'a ];
prefix, opcode, rex, addr_size, modrm, sib, disp, sib_index, sib_scale, sib_base, sse_cc,
avx_cc, avx_sae, avx_rm, operands
);
impl X86OpMem {
pub fn segment(&self) -> RegId {
RegId(self.0.segment as RegIdInt)
}
pub fn base(&self) -> RegId {
RegId(self.0.base as RegIdInt)
}
pub fn index(&self) -> RegId {
RegId(self.0.index as RegIdInt)
}
pub fn scale(&self) -> i32 {
self.0.scale as i32
}
pub fn disp(&self) -> i64 {
self.0.disp
}
}
impl_PartialEq_repr_fields!(X86OpMem;
segment, base, index, scale, disp
);
impl cmp::Eq for X86OpMem {}
impl Default for X86Operand {
fn default() -> Self {
X86Operand {
size: 0,
access: None,
avx_bcast: X86AvxBcast::X86_AVX_BCAST_INVALID,
avx_zero_opmask: false,
op_type: X86OperandType::Invalid,
}
}
}
impl From<&cs_x86_op> for X86Operand {
fn from(op: &cs_x86_op) -> X86Operand {
let op_type = X86OperandType::new(op.type_, op.__bindgen_anon_1);
X86Operand {
size: op.size,
access: cs_ac_type(op.access as _).try_into().ok(),
avx_bcast: op.avx_bcast,
avx_zero_opmask: op.avx_zero_opmask,
op_type,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct X86Encoding {
pub modrm_offset: u8,
pub disp: Option<InsnOffsetSpan>,
pub imm: Option<InsnOffsetSpan>,
}
impl From<cs_x86_encoding> for X86Encoding {
fn from(encoding: cs_x86_encoding) -> Self {
Self {
modrm_offset: encoding.modrm_offset,
disp: if encoding.disp_offset == 0 {
None
} else {
Some(InsnOffsetSpan {
offset: encoding.disp_offset,
size: encoding.disp_size,
})
},
imm: if encoding.imm_offset == 0 {
None
} else {
Some(InsnOffsetSpan {
offset: encoding.imm_offset,
size: encoding.imm_size,
})
},
}
}
}
def_arch_details_struct!(
InsnDetail = X86InsnDetail;
Operand = X86Operand;
OperandIterator = X86OperandIterator;
OperandIteratorLife = X86OperandIterator<'a>;
[ pub struct X86OperandIterator<'a>(slice::Iter<'a, cs_x86_op>); ]
cs_arch_op = cs_x86_op;
cs_arch = cs_x86;
);
#[cfg(test)]
mod test {
use super::*;
use capstone_sys::*;
#[test]
fn test_x86_op_type() {
use super::x86_op_type::*;
use super::X86OperandType::*;
fn t(
op_type_value: (x86_op_type, cs_x86_op__bindgen_ty_1),
expected_op_type: X86OperandType,
) {
let (op_type, op_value) = op_type_value;
let op_type = X86OperandType::new(op_type, op_value);
assert_eq!(expected_op_type, op_type);
}
t(
(X86_OP_INVALID, cs_x86_op__bindgen_ty_1 { reg: 0 }),
Invalid,
);
t(
(X86_OP_REG, cs_x86_op__bindgen_ty_1 { reg: 0 }),
Reg(RegId(0)),
);
}
#[test]
fn test_x86_op_eq() {
let a1 = X86Operand {
op_type: X86OperandType::Imm(0),
..Default::default()
};
let a2 = X86Operand {
op_type: X86OperandType::Imm(-100),
..Default::default()
};
assert_eq!(a1, a1.clone());
assert_ne!(a1, a2);
}
#[test]
fn test_x86_insn_eq() {
fn t_eq(a: &cs_x86, b: &cs_x86) {
assert_eq!(X86InsnDetail(a), X86InsnDetail(b))
}
fn t_ne(a: &cs_x86, b: &cs_x86) {
assert_ne!(X86InsnDetail(a), X86InsnDetail(b))
}
let a1 = cs_x86 {
prefix: [0, 0, 0, 0],
opcode: [0, 0, 0, 0],
rex: 0,
addr_size: 0,
modrm: 0,
sib: 0,
disp: 0,
sib_index: x86_reg::X86_REG_INVALID,
sib_scale: 0,
sib_base: x86_reg::X86_REG_INVALID,
sse_cc: x86_sse_cc::X86_SSE_CC_INVALID,
avx_cc: x86_avx_cc::X86_AVX_CC_INVALID,
avx_sae: false,
avx_rm: x86_avx_rm::X86_AVX_RM_INVALID,
op_count: 0,
__bindgen_anon_1: cs_x86__bindgen_ty_1 { eflags: 0 },
encoding: cs_x86_encoding {
modrm_offset: 0,
disp_offset: 0,
disp_size: 0,
imm_offset: 0,
imm_size: 0,
},
xop_cc: x86_xop_cc::X86_XOP_CC_INVALID,
operands: [cs_x86_op {
type_: x86_op_type::X86_OP_INVALID,
__bindgen_anon_1: cs_x86_op__bindgen_ty_1 {
reg: x86_reg::X86_REG_INVALID,
},
size: 0,
avx_bcast: x86_avx_bcast::X86_AVX_BCAST_INVALID,
avx_zero_opmask: false,
access: 0,
}; 8],
};
let mut a2 = a1;
a2.operands[1].type_ = x86_op_type::X86_OP_REG;
let a1_clone = cs_x86 { ..a1 };
let a3 = cs_x86 { rex: 1, ..a1 };
let op_count_differ = cs_x86 { op_count: 1, ..a1 };
let mut op1_differ = op_count_differ;
op1_differ.operands[0].avx_bcast = x86_avx_bcast::X86_AVX_BCAST_2;
t_eq(&a1, &a1);
t_eq(&a1, &a2);
t_eq(&a1, &a1_clone);
t_ne(&a1, &a3);
t_ne(&a1, &op_count_differ);
t_ne(&op_count_differ, &op1_differ);
}
#[test]
fn test_x86_insn_encoding() {
assert_eq!(
X86Encoding::from(cs_x86_encoding {
modrm_offset: 0,
disp_offset: 0,
disp_size: 0,
imm_offset: 0,
imm_size: 0,
}),
X86Encoding {
modrm_offset: 0,
disp: None,
imm: None
}
);
assert_eq!(
X86Encoding::from(cs_x86_encoding {
modrm_offset: 1,
disp_offset: 2,
disp_size: 3,
imm_offset: 4,
imm_size: 5,
}),
X86Encoding {
modrm_offset: 1,
disp: Some(InsnOffsetSpan { offset: 2, size: 3 }),
imm: Some(InsnOffsetSpan { offset: 4, size: 5 }),
}
);
}
}