use crate::mc_inst::{MCInst, MCOperand};
use crate::mc_streamer::x86_opcodes;
#[derive(Debug, Clone)]
pub struct DisassembledInst {
pub inst: MCInst,
pub size: usize,
pub address: u64,
}
pub struct X86Disassembler {
pub base_address: u64,
}
impl X86Disassembler {
pub fn new() -> Self {
Self { base_address: 0 }
}
pub fn decode_one(&self, bytes: &[u8], offset: usize) -> Option<DisassembledInst> {
if offset >= bytes.len() {
return None;
}
let addr = self.base_address + offset as u64;
let b = bytes[offset];
match b {
0x90 => Some(DisassembledInst {
inst: MCInst::new(x86_opcodes::NOP),
size: 1,
address: addr,
}),
0xC3 => Some(DisassembledInst {
inst: MCInst::new(x86_opcodes::RET),
size: 1,
address: addr,
}),
0x50..=0x57 => {
let reg = (b - 0x50) as u32;
let mut inst = MCInst::new(x86_opcodes::PUSH);
inst.add_operand(MCOperand::reg(reg));
Some(DisassembledInst {
inst,
size: 1,
address: addr,
})
}
0x58..=0x5F => {
let reg = (b - 0x58) as u32;
let mut inst = MCInst::new(x86_opcodes::POP);
inst.add_operand(MCOperand::reg(reg));
Some(DisassembledInst {
inst,
size: 1,
address: addr,
})
}
0x48 => {
if offset + 2 >= bytes.len() {
return None;
}
let b2 = bytes[offset + 1];
match b2 {
0xB8..=0xBF => {
let reg = (b2 - 0xB8) as u32;
if offset + 10 > bytes.len() {
return None;
}
let imm =
i64::from_le_bytes(bytes[offset + 2..offset + 10].try_into().ok()?);
let mut inst = MCInst::new(x86_opcodes::MOV);
inst.add_operand(MCOperand::reg(reg));
inst.add_operand(MCOperand::imm(imm));
Some(DisassembledInst {
inst,
size: 10,
address: addr,
})
}
0x01 | 0x29 => {
if offset + 3 > bytes.len() {
return None;
}
let modrm = bytes[offset + 2];
let r1 = (modrm & 0x07) as u32;
let r2 = ((modrm >> 3) & 0x07) as u32;
let opcode = if b2 == 0x01 {
x86_opcodes::ADD
} else {
x86_opcodes::SUB
};
let mut inst = MCInst::new(opcode);
inst.add_operand(MCOperand::reg(r2));
inst.add_operand(MCOperand::reg(r1));
Some(DisassembledInst {
inst,
size: 3,
address: addr,
})
}
0x89 => {
if offset + 3 > bytes.len() {
return None;
}
let modrm = bytes[offset + 2];
let src = (modrm & 0x07) as u32;
let dst = ((modrm >> 3) & 0x07) as u32;
let mut inst = MCInst::new(x86_opcodes::MOV);
inst.add_operand(MCOperand::reg(dst));
inst.add_operand(MCOperand::reg(src));
Some(DisassembledInst {
inst,
size: 3,
address: addr,
})
}
_ => None,
}
}
0x83 => {
if offset + 3 > bytes.len() {
return None;
}
let modrm = bytes[offset + 1];
let reg_field = (modrm >> 3) & 0x07;
let rm = (modrm & 0x07) as u32;
let imm = bytes[offset + 2] as i8 as i64;
let opcode = match reg_field {
0 => x86_opcodes::ADD,
5 => x86_opcodes::SUB,
7 => x86_opcodes::CMP,
_ => return None,
};
let mut inst = MCInst::new(opcode);
inst.add_operand(MCOperand::reg(rm + 16)); inst.add_operand(MCOperand::imm(imm));
Some(DisassembledInst {
inst,
size: 3,
address: addr,
})
}
0xC2 => {
if offset + 3 > bytes.len() {
return None;
}
let imm = u16::from_le_bytes([bytes[offset + 1], bytes[offset + 2]]) as i64;
let mut inst = MCInst::new(x86_opcodes::RET);
inst.add_operand(MCOperand::imm(imm));
Some(DisassembledInst {
inst,
size: 3,
address: addr,
})
}
_ => None,
}
}
pub fn decode_all(&self, bytes: &[u8]) -> Vec<DisassembledInst> {
let mut results = Vec::new();
let mut offset = 0;
while offset < bytes.len() {
if let Some(di) = self.decode_one(bytes, offset) {
offset += di.size;
results.push(di);
} else {
let mut inst = MCInst::new(0);
inst.add_operand(MCOperand::imm(bytes[offset] as i64));
results.push(DisassembledInst {
inst,
size: 1,
address: self.base_address + offset as u64,
});
offset += 1;
}
}
results
}
}
impl Default for X86Disassembler {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86Mode {
Mode16,
Mode32,
Mode64,
}
#[derive(Debug, Clone)]
pub struct DecodedInst {
pub opcode: u32,
pub mnemonic: String,
pub operands: Vec<DecodedOperand>,
pub size: usize,
pub address: u64,
}
#[derive(Debug, Clone, PartialEq)]
pub enum DecodedOperand {
Reg {
name: String,
size: u8,
},
Mem {
base: Option<String>,
index: Option<String>,
scale: u8,
disp: i64,
size: u8,
},
Imm(i64),
RelOffset(i64),
}
impl DecodedOperand {
pub fn reg(name: &str, size: u8) -> Self {
DecodedOperand::Reg {
name: name.to_string(),
size,
}
}
pub fn mem(base: Option<&str>, index: Option<&str>, scale: u8, disp: i64, size: u8) -> Self {
DecodedOperand::Mem {
base: base.map(|s| s.to_string()),
index: index.map(|s| s.to_string()),
scale,
disp,
size,
}
}
pub fn imm(val: i64) -> Self {
DecodedOperand::Imm(val)
}
pub fn rel(offset: i64) -> Self {
DecodedOperand::RelOffset(offset)
}
}
#[derive(Debug, Clone, Default)]
pub struct PrefixInfo {
pub has_lock: bool,
pub has_rep: bool,
pub has_repne: bool,
pub has_operand_size_override: bool,
pub has_address_size_override: bool,
pub segment_override: Option<u8>,
pub rex: Option<u8>,
pub vex: Option<VexInfo>,
pub evex: Option<EvexInfo>,
}
#[derive(Debug, Clone)]
pub struct VexInfo {
pub r: bool,
pub x: bool,
pub b: bool,
pub m_mmmm: u8,
pub w: bool,
pub vvvv: u8,
pub l: bool,
pub pp: u8,
}
#[derive(Debug, Clone)]
pub struct EvexInfo {
pub r: bool,
pub x: bool,
pub b: bool,
pub r_prime: bool,
pub m_mmmm: u8,
pub w: bool,
pub vvvv: u8,
pub ppp: u8,
pub z: bool,
pub l_prime: bool,
pub l: bool,
pub b_prime: bool,
pub v_prime: bool,
pub aaa: u8,
}
#[derive(Debug, Clone, Copy)]
pub struct ModRM {
pub mod_field: u8,
pub reg_field: u8,
pub rm_field: u8,
}
#[derive(Debug, Clone, Copy)]
pub struct SIB {
pub scale: u8,
pub index: u8,
pub base: u8,
}
#[derive(Debug, Clone)]
pub struct InstructionInfo {
pub opcode: u32,
pub mnemonic: &'static str,
pub has_modrm: bool,
pub operand_encoding: OperandEncoding,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OperandEncoding {
Implicit,
RegOnly,
RegRM,
RmReg,
RmOnly,
RegImm,
RmImm,
RegRmImm,
RelBranch,
AccumImm,
AccumMem,
MemAccum,
}
pub struct X86FullDecoder {
pub base_address: u64,
pub mode: X86Mode,
}
impl X86FullDecoder {
pub fn new(mode: X86Mode) -> Self {
Self {
base_address: 0,
mode,
}
}
pub fn with_address(mode: X86Mode, base_address: u64) -> Self {
Self { base_address, mode }
}
pub fn decode_one(&self, bytes: &[u8], offset: usize) -> Option<DecodedInst> {
if offset >= bytes.len() {
return None;
}
let (prefix, pos) = self.decode_prefixes(bytes, offset);
if prefix.vex.is_some() || prefix.evex.is_some() {
return None;
}
if pos >= bytes.len() {
return None;
}
let mut opcode_pos = pos;
let has_0f_escape = bytes[opcode_pos] == 0x0F;
let mut has_0f38 = false;
let mut has_0f3a = false;
if has_0f_escape {
opcode_pos += 1;
if opcode_pos >= bytes.len() {
return None;
}
if bytes[opcode_pos] == 0x38 {
has_0f38 = true;
opcode_pos += 1;
} else if bytes[opcode_pos] == 0x3A {
has_0f3a = true;
opcode_pos += 1;
}
}
if opcode_pos >= bytes.len() {
return None;
}
let opcode_byte = bytes[opcode_pos];
opcode_pos += 1;
let info = self.lookup_opcode(opcode_byte, has_0f_escape, has_0f38, has_0f3a, &prefix);
let mut modrm = None;
let mut sib = None;
let mut disp: i64 = 0;
let mut imm: i64 = 0;
let mut imm2: i64 = 0;
if info.has_modrm {
if opcode_pos >= bytes.len() {
return None;
}
let (m, _) = self.decode_modrm(bytes, opcode_pos);
modrm = Some(m);
opcode_pos += 1;
let m = modrm.unwrap();
if m.rm_field == 4 && m.mod_field != 3 {
if opcode_pos >= bytes.len() {
return None;
}
let (s, _) = self.decode_sib(bytes, opcode_pos);
sib = Some(s);
opcode_pos += 1;
}
let (d, disp_bytes) = self.decode_displacement(bytes, opcode_pos, modrm, sib);
disp = d;
opcode_pos += disp_bytes;
}
let imm_size = self.immediate_size(&info, &prefix, modrm);
if imm_size > 0 {
if opcode_pos + imm_size as usize > bytes.len() {
return None;
}
let (im, isz) = self.decode_immediate(bytes, opcode_pos, imm_size);
imm = im;
opcode_pos += isz;
}
let imm2_size = self.immediate2_size(&info, &prefix);
if imm2_size > 0 {
if opcode_pos + imm2_size as usize > bytes.len() {
return None;
}
let (im2, isz) = self.decode_immediate(bytes, opcode_pos, imm2_size);
imm2 = im2;
opcode_pos += isz;
}
let resolved_mnemonic = self.resolve_group_mnemonic(&info, modrm);
let operands =
self.decode_operands(&info, &prefix, modrm, sib, disp, imm, imm2, opcode_byte);
let total_size = opcode_pos - offset;
Some(DecodedInst {
opcode: info.opcode,
mnemonic: resolved_mnemonic.to_string(),
operands,
size: total_size,
address: self.base_address + offset as u64,
})
}
pub fn decode_prefixes(&self, bytes: &[u8], pos: usize) -> (PrefixInfo, usize) {
let mut p = PrefixInfo::default();
let mut i = pos;
let mut saw_rex = false;
while i < bytes.len() {
let b = bytes[i];
match b {
0x26 | 0x2E | 0x36 | 0x3E | 0x64 | 0x65 => {
if self.mode == X86Mode::Mode64 {
if b == 0x64 || b == 0x65 {
p.segment_override = Some(b);
}
} else {
p.segment_override = Some(b);
}
i += 1;
}
0x66 => {
p.has_operand_size_override = true;
i += 1;
}
0x67 => {
p.has_address_size_override = true;
i += 1;
}
0xF0 => {
p.has_lock = true;
i += 1;
}
0xF2 => {
p.has_repne = true;
i += 1;
}
0xF3 => {
p.has_rep = true;
i += 1;
}
0x40..=0x4F => {
if self.mode == X86Mode::Mode64 && !saw_rex {
p.rex = Some(b);
saw_rex = true;
i += 1;
} else {
return (p, i);
}
}
0xC5 | 0xC4 => {
if let Some((vex, new_i)) = self.decode_vex(bytes, i) {
p.vex = Some(vex);
i = new_i;
}
return (p, i);
}
0x62 => {
if let Some((evex, new_i)) = self.decode_evex(bytes, i) {
p.evex = Some(evex);
i = new_i;
}
return (p, i);
}
_ => {
return (p, i);
}
}
}
(p, i)
}
pub fn decode_rex(&self, bytes: &[u8], pos: usize) -> (Option<u8>, usize) {
if pos >= bytes.len() {
return (None, pos);
}
let b = bytes[pos];
if (0x40..=0x4F).contains(&b) && self.mode == X86Mode::Mode64 {
(Some(b), pos + 1)
} else {
(None, pos)
}
}
pub fn decode_vex(&self, bytes: &[u8], pos: usize) -> Option<(VexInfo, usize)> {
if pos >= bytes.len() {
return None;
}
let b0 = bytes[pos];
if b0 == 0xC5 {
if pos + 1 >= bytes.len() {
return None;
}
let b1 = bytes[pos + 1];
let vex = VexInfo {
r: ((b1 >> 7) & 1) == 0, x: true, b: true, m_mmmm: 1, w: false, vvvv: (!(b1 >> 3)) & 0x0F, l: ((b1 >> 2) & 1) != 0,
pp: b1 & 0x03,
};
Some((vex, pos + 2))
} else if b0 == 0xC4 {
if pos + 2 >= bytes.len() {
return None;
}
let b1 = bytes[pos + 1];
let b2 = bytes[pos + 2];
let vex = VexInfo {
r: ((b1 >> 7) & 1) == 0,
x: ((b1 >> 6) & 1) == 0,
b: ((b1 >> 5) & 1) == 0,
m_mmmm: b1 & 0x1F,
w: ((b2 >> 7) & 1) != 0,
vvvv: (!(b2 >> 3)) & 0x0F,
l: ((b2 >> 2) & 1) != 0,
pp: b2 & 0x03,
};
Some((vex, pos + 3))
} else {
None
}
}
pub fn decode_evex(&self, bytes: &[u8], pos: usize) -> Option<(EvexInfo, usize)> {
if pos + 3 >= bytes.len() {
return None;
}
if bytes[pos] != 0x62 {
return None;
}
let b1 = bytes[pos + 1];
let b2 = bytes[pos + 2];
let b3 = bytes[pos + 3];
let evex = EvexInfo {
r: ((b1 >> 7) & 1) == 0,
x: ((b1 >> 6) & 1) == 0,
b: ((b1 >> 5) & 1) == 0,
r_prime: ((b1 >> 4) & 1) == 0,
m_mmmm: b1 & 0x0F,
w: ((b2 >> 7) & 1) != 0,
vvvv: (!(b2 >> 3)) & 0x0F,
ppp: b2 & 0x03,
z: ((b3 >> 7) & 1) != 0,
l_prime: ((b3 >> 6) & 1) != 0,
l: ((b3 >> 5) & 1) != 0,
b_prime: ((b3 >> 4) & 1) != 0,
v_prime: ((b3 >> 3) & 1) != 0,
aaa: b3 & 0x07,
};
Some((evex, pos + 4))
}
pub fn decode_modrm(&self, bytes: &[u8], pos: usize) -> (ModRM, usize) {
let b = if pos < bytes.len() { bytes[pos] } else { 0 };
let modrm = ModRM {
mod_field: (b >> 6) & 0x03,
reg_field: (b >> 3) & 0x07,
rm_field: b & 0x07,
};
(modrm, pos + 1)
}
pub fn decode_sib(&self, bytes: &[u8], pos: usize) -> (SIB, usize) {
let b = if pos < bytes.len() { bytes[pos] } else { 0 };
let sib = SIB {
scale: (b >> 6) & 0x03,
index: (b >> 3) & 0x07,
base: b & 0x07,
};
(sib, pos + 1)
}
pub fn decode_displacement(
&self,
bytes: &[u8],
pos: usize,
modrm: Option<ModRM>,
sib: Option<SIB>,
) -> (i64, usize) {
let m = match modrm {
Some(m) => m,
None => return (0, 0),
};
match m.mod_field {
0 => {
if m.rm_field == 5 {
if self.mode == X86Mode::Mode64 {
if pos + 4 <= bytes.len() {
let d = i32::from_le_bytes(bytes[pos..pos + 4].try_into().unwrap());
return (d as i64, 4);
}
} else {
if pos + 4 <= bytes.len() {
let d = i32::from_le_bytes(bytes[pos..pos + 4].try_into().unwrap());
return (d as i64, 4);
}
}
}
if let Some(s) = sib {
if s.base == 5 {
if pos + 4 <= bytes.len() {
let d = i32::from_le_bytes(bytes[pos..pos + 4].try_into().unwrap());
return (d as i64, 4);
}
}
}
(0, 0)
}
1 => {
if pos < bytes.len() {
(bytes[pos] as i8 as i64, 1)
} else {
(0, 0)
}
}
2 => {
if pos + 4 <= bytes.len() {
let d = i32::from_le_bytes(bytes[pos..pos + 4].try_into().unwrap());
(d as i64, 4)
} else {
(0, 0)
}
}
_ => (0, 0), }
}
pub fn decode_immediate(&self, bytes: &[u8], pos: usize, size: u8) -> (i64, usize) {
let sz = size as usize;
if pos + sz > bytes.len() {
return (0, 0);
}
let val = match size {
1 => bytes[pos] as i8 as i64,
2 => i16::from_le_bytes(bytes[pos..pos + 2].try_into().unwrap()) as i64,
4 => i32::from_le_bytes(bytes[pos..pos + 4].try_into().unwrap()) as i64,
8 => i64::from_le_bytes(bytes[pos..pos + 8].try_into().unwrap()),
_ => 0,
};
(val, sz)
}
fn immediate_size(
&self,
info: &InstructionInfo,
prefix: &PrefixInfo,
modrm: Option<ModRM>,
) -> u8 {
let rex_w = prefix.rex.map_or(false, |r| (r & 0x08) != 0);
let opsize_override = prefix.has_operand_size_override;
match info.operand_encoding {
OperandEncoding::RegImm => {
if self.mode == X86Mode::Mode64 && rex_w {
8
} else if self.mode == X86Mode::Mode64 && !opsize_override {
4 } else if opsize_override {
2
} else {
if self.mode == X86Mode::Mode16 {
2
} else {
4
}
}
}
OperandEncoding::RmImm => {
if info.opcode & 0xFF == 0x83 || info.opcode == 0x83 {
1
} else if self.mode == X86Mode::Mode64 && rex_w {
4 } else if self.mode == X86Mode::Mode64 {
4
} else {
if self.mode == X86Mode::Mode16 {
2
} else {
4
}
}
}
OperandEncoding::AccumImm => {
if self.mode == X86Mode::Mode64 && rex_w {
8
} else if self.mode == X86Mode::Mode64 && !opsize_override {
4
} else if opsize_override {
2
} else {
if self.mode == X86Mode::Mode16 {
2
} else {
4
}
}
}
OperandEncoding::RelBranch => {
let op_byte = (info.opcode & 0xFF) as u8;
let has_0f = (info.opcode & 0xFF00) != 0;
if op_byte == 0xEB || (0x70..=0x7F).contains(&op_byte) {
1
} else if op_byte == 0xE9
|| op_byte == 0xE8
|| (has_0f && (0x80..=0x8F).contains(&op_byte))
{
4
} else if self.mode == X86Mode::Mode16 {
2
} else {
4
}
}
OperandEncoding::RegRmImm => 4, OperandEncoding::RmOnly => {
if let Some(m) = modrm {
let op_low = (info.opcode & 0xFF) as u8;
if (op_low == 0xF6 || op_low == 0xF7) && (m.reg_field == 0 || m.reg_field == 1)
{
if op_low == 0xF6 {
1
} else if self.mode == X86Mode::Mode64 && rex_w {
4
} else if self.mode == X86Mode::Mode64 {
4
} else if self.mode == X86Mode::Mode16 {
2
} else {
4
}
} else {
0
}
} else {
0
}
}
_ => 0,
}
}
fn immediate2_size(&self, _info: &InstructionInfo, _prefix: &PrefixInfo) -> u8 {
0
}
pub fn lookup_opcode(
&self,
opcode_byte: u8,
has_0f_escape: bool,
has_0f38: bool,
has_0f3a: bool,
prefix: &PrefixInfo,
) -> InstructionInfo {
let full_opcode = if has_0f3a {
0x0F3A00 | (opcode_byte as u32)
} else if has_0f38 {
0x0F3800 | (opcode_byte as u32)
} else if has_0f_escape {
0x0F00 | (opcode_byte as u32)
} else {
opcode_byte as u32
};
if has_0f_escape && !has_0f38 && !has_0f3a {
self.lookup_0f_opcode(full_opcode, opcode_byte, prefix)
} else if has_0f38 {
self.lookup_0f38_opcode(full_opcode, opcode_byte)
} else if has_0f3a {
self.lookup_0f3a_opcode(full_opcode, opcode_byte)
} else {
self.lookup_1byte_opcode(full_opcode, opcode_byte, prefix)
}
}
fn lookup_1byte_opcode(
&self,
full_opcode: u32,
opcode_byte: u8,
_prefix: &PrefixInfo,
) -> InstructionInfo {
let _has_rex = _prefix.rex.is_some();
let rex_w = _prefix.rex.map_or(false, |r| (r & 0x08) != 0);
match opcode_byte {
0x90 => InstructionInfo {
opcode: full_opcode,
mnemonic: "nop",
has_modrm: false,
operand_encoding: OperandEncoding::Implicit,
},
0x00 | 0x01 | 0x08 | 0x09 | 0x10 | 0x11 | 0x18 | 0x19 | 0x20 | 0x21 | 0x28 | 0x29
| 0x30 | 0x31 | 0x38 | 0x39 => {
let mnem = match (opcode_byte / 8) & 0x07 {
0 => "add",
1 => "or",
2 => "adc",
3 => "sbb",
4 => "and",
5 => "sub",
6 => "xor",
7 => "cmp",
_ => "unknown",
};
InstructionInfo {
opcode: full_opcode,
mnemonic: mnem,
has_modrm: true,
operand_encoding: OperandEncoding::RmReg,
}
}
0x02 | 0x03 | 0x0A | 0x0B | 0x12 | 0x13 | 0x1A | 0x1B | 0x22 | 0x23 | 0x2A | 0x2B
| 0x32 | 0x33 | 0x3A | 0x3B => {
let mnem = match (opcode_byte / 8) & 0x07 {
0 => "add",
1 => "or",
2 => "adc",
3 => "sbb",
4 => "and",
5 => "sub",
6 => "xor",
7 => "cmp",
_ => "unknown",
};
InstructionInfo {
opcode: full_opcode,
mnemonic: mnem,
has_modrm: true,
operand_encoding: OperandEncoding::RegRM,
}
}
0x91..=0x97 => InstructionInfo {
opcode: full_opcode,
mnemonic: "xchg",
has_modrm: false,
operand_encoding: OperandEncoding::RegOnly,
},
0x50..=0x57 => InstructionInfo {
opcode: full_opcode,
mnemonic: "push",
has_modrm: false,
operand_encoding: OperandEncoding::RegOnly,
},
0x58..=0x5F => InstructionInfo {
opcode: full_opcode,
mnemonic: "pop",
has_modrm: false,
operand_encoding: OperandEncoding::RegOnly,
},
0x70..=0x7F => {
let condition = opcode_byte & 0x0F;
let mnem = jcc_mnemonic(condition);
InstructionInfo {
opcode: full_opcode,
mnemonic: mnem,
has_modrm: false,
operand_encoding: OperandEncoding::RelBranch,
}
}
0x80..=0x83 => {
let mnem = match opcode_byte & 0x03 {
0 => "add",
1 => "or",
2 => "adc",
3 => "sbb",
_ => "unknown",
};
InstructionInfo {
opcode: full_opcode,
mnemonic: mnem, has_modrm: true,
operand_encoding: OperandEncoding::RmImm,
}
}
0x84 => InstructionInfo {
opcode: full_opcode,
mnemonic: "test",
has_modrm: true,
operand_encoding: OperandEncoding::RmReg,
},
0x85 => InstructionInfo {
opcode: full_opcode,
mnemonic: "test",
has_modrm: true,
operand_encoding: OperandEncoding::RmReg,
},
0x86 => InstructionInfo {
opcode: full_opcode,
mnemonic: "xchg",
has_modrm: true,
operand_encoding: OperandEncoding::RmReg,
},
0x87 => InstructionInfo {
opcode: full_opcode,
mnemonic: "xchg",
has_modrm: true,
operand_encoding: OperandEncoding::RmReg,
},
0x88 => InstructionInfo {
opcode: full_opcode,
mnemonic: "mov",
has_modrm: true,
operand_encoding: OperandEncoding::RmReg,
},
0x89 => InstructionInfo {
opcode: full_opcode,
mnemonic: "mov",
has_modrm: true,
operand_encoding: OperandEncoding::RmReg,
},
0x8A => InstructionInfo {
opcode: full_opcode,
mnemonic: "mov",
has_modrm: true,
operand_encoding: OperandEncoding::RegRM,
},
0x8B => InstructionInfo {
opcode: full_opcode,
mnemonic: "mov",
has_modrm: true,
operand_encoding: OperandEncoding::RegRM,
},
0xB0..=0xB7 => InstructionInfo {
opcode: full_opcode,
mnemonic: "mov",
has_modrm: false,
operand_encoding: OperandEncoding::RegImm,
},
0xB8..=0xBF => InstructionInfo {
opcode: full_opcode,
mnemonic: "mov",
has_modrm: false,
operand_encoding: OperandEncoding::RegImm,
},
0xC3 => InstructionInfo {
opcode: full_opcode,
mnemonic: "ret",
has_modrm: false,
operand_encoding: OperandEncoding::Implicit,
},
0xC2 => InstructionInfo {
opcode: full_opcode,
mnemonic: "ret",
has_modrm: false,
operand_encoding: OperandEncoding::RegImm, },
0xC6 => InstructionInfo {
opcode: full_opcode,
mnemonic: "mov",
has_modrm: true,
operand_encoding: OperandEncoding::RmImm,
},
0xC7 => InstructionInfo {
opcode: full_opcode,
mnemonic: "mov",
has_modrm: true,
operand_encoding: OperandEncoding::RmImm,
},
0xD0..=0xD1 => InstructionInfo {
opcode: full_opcode,
mnemonic: "rol", has_modrm: true,
operand_encoding: OperandEncoding::RmOnly,
},
0xD2..=0xD3 => InstructionInfo {
opcode: full_opcode,
mnemonic: "rol", has_modrm: true,
operand_encoding: OperandEncoding::RmOnly,
},
0x98 => {
let mnem = if rex_w {
"cdqe"
} else if self.mode == X86Mode::Mode64 {
"cwde"
} else if self.mode == X86Mode::Mode16 {
"cbw"
} else {
"cwde"
};
InstructionInfo {
opcode: full_opcode,
mnemonic: mnem,
has_modrm: false,
operand_encoding: OperandEncoding::Implicit,
}
}
0x99 => {
let mnem = if rex_w {
"cqo"
} else if self.mode == X86Mode::Mode64 {
"cdq"
} else if self.mode == X86Mode::Mode16 {
"cwd"
} else {
"cdq"
};
InstructionInfo {
opcode: full_opcode,
mnemonic: mnem,
has_modrm: false,
operand_encoding: OperandEncoding::Implicit,
}
}
0xA8 | 0xA9 => InstructionInfo {
opcode: full_opcode,
mnemonic: "test",
has_modrm: false,
operand_encoding: OperandEncoding::AccumImm,
},
0xA4 | 0xA5 => InstructionInfo {
opcode: full_opcode,
mnemonic: "movs",
has_modrm: false,
operand_encoding: OperandEncoding::Implicit,
},
0xAA | 0xAB => InstructionInfo {
opcode: full_opcode,
mnemonic: "stos",
has_modrm: false,
operand_encoding: OperandEncoding::Implicit,
},
0xAC | 0xAD => InstructionInfo {
opcode: full_opcode,
mnemonic: "lods",
has_modrm: false,
operand_encoding: OperandEncoding::Implicit,
},
0xAE | 0xAF => InstructionInfo {
opcode: full_opcode,
mnemonic: "scas",
has_modrm: false,
operand_encoding: OperandEncoding::Implicit,
},
0xEB => InstructionInfo {
opcode: full_opcode,
mnemonic: "jmp",
has_modrm: false,
operand_encoding: OperandEncoding::RelBranch,
},
0xE9 => InstructionInfo {
opcode: full_opcode,
mnemonic: "jmp",
has_modrm: false,
operand_encoding: OperandEncoding::RelBranch,
},
0xE8 => InstructionInfo {
opcode: full_opcode,
mnemonic: "call",
has_modrm: false,
operand_encoding: OperandEncoding::RelBranch,
},
0xF6..=0xF7 => InstructionInfo {
opcode: full_opcode,
mnemonic: "test", has_modrm: true,
operand_encoding: OperandEncoding::RmOnly,
},
0xF8 => InstructionInfo {
opcode: full_opcode,
mnemonic: "clc",
has_modrm: false,
operand_encoding: OperandEncoding::Implicit,
},
0xF9 => InstructionInfo {
opcode: full_opcode,
mnemonic: "stc",
has_modrm: false,
operand_encoding: OperandEncoding::Implicit,
},
0xFA => InstructionInfo {
opcode: full_opcode,
mnemonic: "cli",
has_modrm: false,
operand_encoding: OperandEncoding::Implicit,
},
0xFB => InstructionInfo {
opcode: full_opcode,
mnemonic: "sti",
has_modrm: false,
operand_encoding: OperandEncoding::Implicit,
},
0xFC => InstructionInfo {
opcode: full_opcode,
mnemonic: "cld",
has_modrm: false,
operand_encoding: OperandEncoding::Implicit,
},
0xFD => InstructionInfo {
opcode: full_opcode,
mnemonic: "std",
has_modrm: false,
operand_encoding: OperandEncoding::Implicit,
},
0xFE => InstructionInfo {
opcode: full_opcode,
mnemonic: "inc", has_modrm: true,
operand_encoding: OperandEncoding::RmOnly,
},
0xFF => InstructionInfo {
opcode: full_opcode,
mnemonic: "inc", has_modrm: true,
operand_encoding: OperandEncoding::RmOnly,
},
0x8D => InstructionInfo {
opcode: full_opcode,
mnemonic: "lea",
has_modrm: true,
operand_encoding: OperandEncoding::RegRM,
},
_ => InstructionInfo {
opcode: full_opcode,
mnemonic: "<unknown>",
has_modrm: false,
operand_encoding: OperandEncoding::Implicit,
},
}
}
fn lookup_0f_opcode(
&self,
full_opcode: u32,
opcode_byte: u8,
_prefix: &PrefixInfo,
) -> InstructionInfo {
match opcode_byte {
0x1F => InstructionInfo {
opcode: full_opcode,
mnemonic: "nop",
has_modrm: true,
operand_encoding: OperandEncoding::RmOnly,
},
0xB6 => InstructionInfo {
opcode: full_opcode,
mnemonic: "movzx",
has_modrm: true,
operand_encoding: OperandEncoding::RegRM,
},
0xB7 => InstructionInfo {
opcode: full_opcode,
mnemonic: "movzx",
has_modrm: true,
operand_encoding: OperandEncoding::RegRM,
},
0xBE => InstructionInfo {
opcode: full_opcode,
mnemonic: "movsx",
has_modrm: true,
operand_encoding: OperandEncoding::RegRM,
},
0xBF => InstructionInfo {
opcode: full_opcode,
mnemonic: "movsx",
has_modrm: true,
operand_encoding: OperandEncoding::RegRM,
},
0xAF => InstructionInfo {
opcode: full_opcode,
mnemonic: "imul",
has_modrm: true,
operand_encoding: OperandEncoding::RegRM,
},
0xC8..=0xCF => InstructionInfo {
opcode: full_opcode,
mnemonic: "bswap",
has_modrm: false,
operand_encoding: OperandEncoding::RegOnly,
},
0xA3 => InstructionInfo {
opcode: full_opcode,
mnemonic: "bt",
has_modrm: true,
operand_encoding: OperandEncoding::RmReg,
},
0xAB => InstructionInfo {
opcode: full_opcode,
mnemonic: "bts",
has_modrm: true,
operand_encoding: OperandEncoding::RmReg,
},
0xB3 => InstructionInfo {
opcode: full_opcode,
mnemonic: "btr",
has_modrm: true,
operand_encoding: OperandEncoding::RmReg,
},
0xBB => InstructionInfo {
opcode: full_opcode,
mnemonic: "btc",
has_modrm: true,
operand_encoding: OperandEncoding::RmReg,
},
0xBC => InstructionInfo {
opcode: full_opcode,
mnemonic: "bsf",
has_modrm: true,
operand_encoding: OperandEncoding::RegRM,
},
0xBD => InstructionInfo {
opcode: full_opcode,
mnemonic: "bsr",
has_modrm: true,
operand_encoding: OperandEncoding::RegRM,
},
0x40..=0x4F => {
let condition = opcode_byte & 0x0F;
let mnem = cmov_mnemonic(condition);
InstructionInfo {
opcode: full_opcode,
mnemonic: mnem,
has_modrm: true,
operand_encoding: OperandEncoding::RegRM,
}
}
0x90..=0x9F => {
let condition = opcode_byte & 0x0F;
let mnem = setcc_mnemonic(condition);
InstructionInfo {
opcode: full_opcode,
mnemonic: mnem,
has_modrm: true,
operand_encoding: OperandEncoding::RmOnly,
}
}
0x80..=0x8F => {
let condition = opcode_byte & 0x0F;
let mnem = jcc_mnemonic(condition);
InstructionInfo {
opcode: full_opcode,
mnemonic: mnem,
has_modrm: false,
operand_encoding: OperandEncoding::RelBranch,
}
}
0x05 => InstructionInfo {
opcode: full_opcode,
mnemonic: "syscall",
has_modrm: false,
operand_encoding: OperandEncoding::Implicit,
},
0x07 => InstructionInfo {
opcode: full_opcode,
mnemonic: "sysret",
has_modrm: false,
operand_encoding: OperandEncoding::Implicit,
},
0xA2 => InstructionInfo {
opcode: full_opcode,
mnemonic: "cpuid",
has_modrm: false,
operand_encoding: OperandEncoding::Implicit,
},
0x31 => InstructionInfo {
opcode: full_opcode,
mnemonic: "rdtsc",
has_modrm: false,
operand_encoding: OperandEncoding::Implicit,
},
_ => InstructionInfo {
opcode: full_opcode,
mnemonic: "<unknown>",
has_modrm: false,
operand_encoding: OperandEncoding::Implicit,
},
}
}
fn lookup_0f38_opcode(&self, full_opcode: u32, _opcode_byte: u8) -> InstructionInfo {
InstructionInfo {
opcode: full_opcode,
mnemonic: "<unknown>",
has_modrm: true,
operand_encoding: OperandEncoding::RegRM,
}
}
fn lookup_0f3a_opcode(&self, full_opcode: u32, _opcode_byte: u8) -> InstructionInfo {
InstructionInfo {
opcode: full_opcode,
mnemonic: "<unknown>",
has_modrm: true,
operand_encoding: OperandEncoding::RegRmImm,
}
}
fn resolve_group_mnemonic(&self, info: &InstructionInfo, modrm: Option<ModRM>) -> &'static str {
if let Some(m) = modrm {
let opcode_low = (info.opcode & 0xFF) as u8;
match opcode_low {
0x80..=0x83 => group1_mnemonic(m.reg_field),
0xD0..=0xD3 => group2_mnemonic(m.reg_field),
0xF6..=0xF7 => group3_mnemonic(m.reg_field),
0xFE => group4_mnemonic(m.reg_field),
0xFF => group5_mnemonic(m.reg_field),
0x1F if (info.opcode & 0xFF00) == 0x0F00 => "nop",
_ => info.mnemonic,
}
} else {
info.mnemonic
}
}
pub fn decode_operands(
&self,
info: &InstructionInfo,
prefix: &PrefixInfo,
modrm: Option<ModRM>,
sib: Option<SIB>,
disp: i64,
imm: i64,
_imm2: i64,
opcode_byte: u8,
) -> Vec<DecodedOperand> {
let mut ops = Vec::new();
let rex_w = prefix.rex.map_or(false, |r| (r & 0x08) != 0);
let opsize_override = prefix.has_operand_size_override;
let rex = prefix.rex;
let eff_opsize: u8 = if self.mode == X86Mode::Mode64 && rex_w {
8
} else if self.mode == X86Mode::Mode64 && !opsize_override {
4
} else if opsize_override {
2
} else if self.mode == X86Mode::Mode16 {
2
} else {
4
};
let addr_size: u8 = if prefix.has_address_size_override {
4 } else if self.mode == X86Mode::Mode64 {
8
} else if self.mode == X86Mode::Mode16 {
2
} else {
4
};
match info.operand_encoding {
OperandEncoding::Implicit => {
}
OperandEncoding::RegOnly => {
let reg = opcode_byte & 0x07;
let reg_name = if self.mode == X86Mode::Mode64 {
x86_64_reg_name_ext(reg as u32, rex, 8)
} else if rex_w {
x86_64_reg_name_ext(reg as u32, rex, 8)
} else if opsize_override {
x86_16_reg_name(reg as u32)
} else if self.mode == X86Mode::Mode16 {
x86_16_reg_name(reg as u32)
} else {
x86_32_reg_name(reg as u32)
};
ops.push(DecodedOperand::reg(®_name, eff_opsize));
}
OperandEncoding::RegRM => {
if let Some(m) = modrm {
let (src_name, _src_is_mem) =
self.resolve_rm(m, sib, disp, rex, eff_opsize, addr_size);
let dst_reg = m.reg_field as u32;
let dst_name = if self.mode == X86Mode::Mode64 && rex_w {
x86_64_reg_name_ext(dst_reg, rex, eff_opsize)
} else if self.mode == X86Mode::Mode64 {
x86_32_reg_name(dst_reg)
} else {
x86_32_reg_name(dst_reg)
};
ops.push(DecodedOperand::reg(&dst_name, eff_opsize));
ops.push(src_name);
}
}
OperandEncoding::RmReg => {
if let Some(m) = modrm {
let (dst_name, _dst_is_mem) =
self.resolve_rm(m, sib, disp, rex, eff_opsize, addr_size);
let src_reg = m.reg_field as u32;
let src_name = if self.mode == X86Mode::Mode64 && rex_w {
x86_64_reg_name_ext(src_reg, rex, eff_opsize)
} else if self.mode == X86Mode::Mode64 {
x86_32_reg_name(src_reg)
} else {
x86_32_reg_name(src_reg)
};
ops.push(dst_name);
ops.push(DecodedOperand::reg(&src_name, eff_opsize));
}
}
OperandEncoding::RmOnly => {
if let Some(m) = modrm {
let (name, _is_mem) = self.resolve_rm(m, sib, disp, rex, eff_opsize, addr_size);
ops.push(name);
let op_low = (info.opcode & 0xFF) as u8;
if (op_low == 0xF6 || op_low == 0xF7) && (m.reg_field == 0 || m.reg_field == 1)
{
ops.push(DecodedOperand::imm(imm));
}
}
}
OperandEncoding::RegImm => {
let reg = opcode_byte & 0x07;
let reg_name = if self.mode == X86Mode::Mode64 && rex_w {
x86_64_reg_name_ext(reg as u32, rex, eff_opsize)
} else if self.mode == X86Mode::Mode64 && !opsize_override {
x86_32_reg_name(reg as u32)
} else if rex_w {
x86_64_reg_name_ext(reg as u32, rex, eff_opsize)
} else if opsize_override {
x86_16_reg_name(reg as u32)
} else {
x86_32_reg_name(reg as u32)
};
ops.push(DecodedOperand::reg(®_name, eff_opsize));
ops.push(DecodedOperand::imm(imm));
}
OperandEncoding::RmImm => {
if let Some(m) = modrm {
let (name, _is_mem) = self.resolve_rm(m, sib, disp, rex, eff_opsize, addr_size);
ops.push(name);
}
ops.push(DecodedOperand::imm(imm));
}
OperandEncoding::RelBranch => {
ops.push(DecodedOperand::rel(imm));
}
OperandEncoding::AccumImm => {
let acc_name = if rex_w {
"rax"
} else if self.mode == X86Mode::Mode64 && !opsize_override {
"eax"
} else if opsize_override || self.mode == X86Mode::Mode16 {
"ax"
} else {
"eax"
};
ops.push(DecodedOperand::reg(acc_name, eff_opsize));
ops.push(DecodedOperand::imm(imm));
}
OperandEncoding::AccumMem => {
let acc_name = if rex_w {
"rax"
} else if self.mode == X86Mode::Mode64 && !opsize_override {
"eax"
} else if opsize_override || self.mode == X86Mode::Mode16 {
"ax"
} else {
"eax"
};
ops.push(DecodedOperand::reg(acc_name, eff_opsize));
ops.push(DecodedOperand::mem(None, None, 1, disp, eff_opsize));
}
OperandEncoding::MemAccum => {
let acc_name = if rex_w {
"rax"
} else if self.mode == X86Mode::Mode64 && !opsize_override {
"eax"
} else if opsize_override || self.mode == X86Mode::Mode16 {
"ax"
} else {
"eax"
};
ops.push(DecodedOperand::mem(None, None, 1, disp, eff_opsize));
ops.push(DecodedOperand::reg(acc_name, eff_opsize));
}
OperandEncoding::RegRmImm => {
if let Some(m) = modrm {
let (src_name, _src_is_mem) =
self.resolve_rm(m, sib, disp, rex, eff_opsize, addr_size);
let dst_reg = m.reg_field as u32;
let dst_name = if rex_w || self.mode == X86Mode::Mode64 {
x86_64_reg_name_ext(dst_reg, rex, eff_opsize)
} else {
x86_32_reg_name(dst_reg)
};
ops.push(DecodedOperand::reg(&dst_name, eff_opsize));
ops.push(src_name);
ops.push(DecodedOperand::imm(imm));
}
}
}
ops
}
fn resolve_rm(
&self,
modrm: ModRM,
sib: Option<SIB>,
disp: i64,
rex: Option<u8>,
opsize: u8,
addr_size: u8,
) -> (DecodedOperand, bool) {
if modrm.mod_field == 3 {
let reg = modrm.rm_field as u32;
let name = if opsize == 8 {
x86_64_reg_name_ext(reg, rex, opsize)
} else if opsize == 4 {
x86_32_reg_name(reg)
} else {
x86_16_reg_name(reg)
};
(DecodedOperand::reg(&name, opsize), false)
} else {
let mem_op = self.resolve_memory(modrm, sib, disp, rex, opsize, addr_size);
(mem_op, true)
}
}
fn resolve_memory(
&self,
modrm: ModRM,
sib: Option<SIB>,
disp: i64,
rex: Option<u8>,
opsize: u8,
addr_size: u8,
) -> DecodedOperand {
let rex_b = rex.map_or(false, |r| (r & 0x01) != 0);
let rex_x = rex.map_or(false, |r| (r & 0x02) != 0);
if modrm.rm_field == 4 {
if let Some(s) = sib {
let scale_val = 1u8 << s.scale;
let base_name = if s.base == 5 && modrm.mod_field == 0 {
None
} else {
let b = if rex_b { s.base | 0x08 } else { s.base };
Some(x86_addr_reg_name(b as u32, addr_size))
};
let index_name = if s.index == 4 {
None
} else {
let idx = if rex_x { s.index | 0x08 } else { s.index };
Some(x86_addr_reg_name(idx as u32, addr_size))
};
DecodedOperand::mem(
base_name.as_deref(),
index_name.as_deref(),
scale_val,
disp,
opsize,
)
} else {
DecodedOperand::mem(None, None, 1, disp, opsize)
}
} else if modrm.rm_field == 5 && modrm.mod_field == 0 && self.mode == X86Mode::Mode64 {
let rip_disp = disp;
DecodedOperand::mem(Some("rip"), None, 1, rip_disp, opsize)
} else if modrm.rm_field == 5 && modrm.mod_field == 0 {
DecodedOperand::mem(None, None, 1, disp, opsize)
} else {
let base = if rex_b {
modrm.rm_field | 0x08
} else {
modrm.rm_field
};
let base_name = x86_addr_reg_name(base as u32, addr_size);
DecodedOperand::mem(Some(&base_name), None, 1, disp, opsize)
}
}
pub fn format_instruction(&self, decoded: &DecodedInst) -> String {
let mut s = String::new();
s.push_str(&decoded.mnemonic);
if !decoded.operands.is_empty() {
s.push(' ');
let parts: Vec<String> = decoded
.operands
.iter()
.map(|op| self.format_operand(op))
.collect();
s.push_str(&parts.join(", "));
}
s
}
pub fn format_operand(&self, op: &DecodedOperand) -> String {
match op {
DecodedOperand::Reg { name, .. } => format!("%{}", name),
DecodedOperand::Mem {
base,
index,
scale,
disp,
..
} => {
let mut s = String::new();
let mut parts: Vec<String> = Vec::new();
if let Some(b) = base {
parts.push(format!("%{}", b));
}
if let Some(idx) = index {
if *scale > 1 {
parts.push(format!("%{}, {}", idx, scale));
} else {
parts.push(format!("%{}", idx));
}
}
let base_disp = if *disp != 0 || (base.is_none() && index.is_none()) {
if *disp < 0 {
format!("-{}", -disp)
} else if !parts.is_empty() {
format!("+{}", disp)
} else {
format!("{}", disp)
}
} else {
String::new()
};
if !parts.is_empty() || !base_disp.is_empty() {
s.push_str(&format!("({})", parts.join(", ")));
if !base_disp.is_empty() && !parts.is_empty() {
s.pop(); s.push_str(&base_disp);
s.push(')');
}
}
if s.is_empty() {
s = format!("{}", disp);
}
s
}
DecodedOperand::Imm(v) => format!("${}", v),
DecodedOperand::RelOffset(v) => format!("{}", v),
}
}
pub fn decode_all(&self, bytes: &[u8]) -> Vec<DecodedInst> {
let mut results = Vec::new();
let mut offset = 0;
while offset < bytes.len() {
if let Some(di) = self.decode_one(bytes, offset) {
offset += di.size;
results.push(di);
} else {
offset += 1; }
}
results
}
}
fn x86_64_reg_name_ext(reg: u32, rex: Option<u8>, _opsize: u8) -> String {
let rex_b = rex.map_or(false, |r| (r & 0x01) != 0);
let rex_r = rex.map_or(false, |r| (r & 0x04) != 0);
let reg_num = if rex_b || rex_r { reg | 0x08 } else { reg };
match reg_num {
0 => "rax".to_string(),
1 => "rcx".to_string(),
2 => "rdx".to_string(),
3 => "rbx".to_string(),
4 => "rsp".to_string(),
5 => "rbp".to_string(),
6 => "rsi".to_string(),
7 => "rdi".to_string(),
8 => "r8".to_string(),
9 => "r9".to_string(),
10 => "r10".to_string(),
11 => "r11".to_string(),
12 => "r12".to_string(),
13 => "r13".to_string(),
14 => "r14".to_string(),
15 => "r15".to_string(),
_ => format!("r{}", reg_num),
}
}
fn x86_32_reg_name(reg: u32) -> String {
match reg & 0x07 {
0 => "eax".to_string(),
1 => "ecx".to_string(),
2 => "edx".to_string(),
3 => "ebx".to_string(),
4 => "esp".to_string(),
5 => "ebp".to_string(),
6 => "esi".to_string(),
7 => "edi".to_string(),
_ => format!("r{}d", reg),
}
}
fn x86_16_reg_name(reg: u32) -> String {
match reg & 0x07 {
0 => "ax".to_string(),
1 => "cx".to_string(),
2 => "dx".to_string(),
3 => "bx".to_string(),
4 => "sp".to_string(),
5 => "bp".to_string(),
6 => "si".to_string(),
7 => "di".to_string(),
_ => format!("r{}w", reg),
}
}
fn x86_addr_reg_name(reg: u32, addr_size: u8) -> String {
if addr_size == 8 {
x86_64_reg_name_ext(reg, None, 8)
} else if addr_size == 4 {
x86_32_reg_name(reg)
} else {
x86_16_reg_name(reg)
}
}
fn jcc_mnemonic(condition: u8) -> &'static str {
match condition {
0 => "jo",
1 => "jno",
2 => "jb",
3 => "jnb",
4 => "je",
5 => "jne",
6 => "jbe",
7 => "ja",
8 => "js",
9 => "jns",
10 => "jp",
11 => "jnp",
12 => "jl",
13 => "jge",
14 => "jle",
15 => "jg",
_ => "j?",
}
}
fn cmov_mnemonic(condition: u8) -> &'static str {
match condition {
0 => "cmovo",
1 => "cmovno",
2 => "cmovb",
3 => "cmovae",
4 => "cmove",
5 => "cmovne",
6 => "cmovbe",
7 => "cmova",
8 => "cmovs",
9 => "cmovns",
10 => "cmovp",
11 => "cmovnp",
12 => "cmovl",
13 => "cmovge",
14 => "cmovle",
15 => "cmovg",
_ => "cmov?",
}
}
fn setcc_mnemonic(condition: u8) -> &'static str {
match condition {
0 => "seto",
1 => "setno",
2 => "setb",
3 => "setnb",
4 => "sete",
5 => "setne",
6 => "setbe",
7 => "seta",
8 => "sets",
9 => "setns",
10 => "setp",
11 => "setnp",
12 => "setl",
13 => "setge",
14 => "setle",
15 => "setg",
_ => "set?",
}
}
fn group1_mnemonic(reg_field: u8) -> &'static str {
match reg_field {
0 => "add",
1 => "or",
2 => "adc",
3 => "sbb",
4 => "and",
5 => "sub",
6 => "xor",
7 => "cmp",
_ => "unknown",
}
}
fn group2_mnemonic(reg_field: u8) -> &'static str {
match reg_field {
0 => "rol",
1 => "ror",
2 => "rcl",
3 => "rcr",
4 => "shl",
5 => "shr",
6 => "sal",
7 => "sar",
_ => "unknown",
}
}
fn group3_mnemonic(reg_field: u8) -> &'static str {
match reg_field {
0 => "test",
1 => "test",
2 => "not",
3 => "neg",
4 => "mul",
5 => "imul",
6 => "div",
7 => "idiv",
_ => "unknown",
}
}
fn group4_mnemonic(reg_field: u8) -> &'static str {
match reg_field {
0 => "inc",
1 => "dec",
_ => "unknown",
}
}
fn group5_mnemonic(reg_field: u8) -> &'static str {
match reg_field {
0 => "inc",
1 => "dec",
2 => "call",
3 => "call",
4 => "jmp",
5 => "jmp",
6 => "push",
_ => "unknown",
}
}
fn aarch64_reg_name(reg: u32, is_64bit: bool) -> String {
if is_64bit {
match reg {
0..=30 => format!("x{}", reg),
31 => "sp".to_string(),
_ => format!("x?{}", reg),
}
} else {
match reg {
0..=30 => format!("w{}", reg),
31 => "wzr".to_string(),
_ => format!("w?{}", reg),
}
}
}
fn aarch64_fp_reg_name(reg: u32, size: u32) -> String {
match size {
0 => format!("b{}", reg),
1 => format!("h{}", reg),
2 => format!("s{}", reg),
3 => format!("d{}", reg),
4 => format!("q{}", reg),
_ => format!("v{}", reg),
}
}
fn aarch64_cond_name(cond: u32) -> &'static str {
match cond {
0b0000 => "eq",
0b0001 => "ne",
0b0010 => "cs",
0b0011 => "cc",
0b0100 => "mi",
0b0101 => "pl",
0b0110 => "vs",
0b0111 => "vc",
0b1000 => "hi",
0b1001 => "ls",
0b1010 => "ge",
0b1011 => "lt",
0b1100 => "gt",
0b1101 => "le",
0b1110 => "al",
0b1111 => "nv",
_ => "??",
}
}
fn aarch64_shift_name(shift: u32) -> &'static str {
match shift {
0b00 => "lsl",
0b01 => "lsr",
0b10 => "asr",
0b11 => "ror",
_ => "???",
}
}
fn aarch64_extend_name(ext: u32) -> &'static str {
match ext {
0b000 => "uxtb",
0b001 => "uxth",
0b010 => "uxtw",
0b011 => "uxtx",
0b100 => "sxtb",
0b101 => "sxth",
0b110 => "sxtw",
0b111 => "sxtx",
_ => "???",
}
}
#[derive(Debug, Clone)]
pub struct AArch64DecodedInst {
pub mnemonic: String,
pub operands: Vec<String>,
pub size: u32,
pub address: u64,
}
impl AArch64DecodedInst {
pub fn format(&self) -> String {
if self.operands.is_empty() {
self.mnemonic.clone()
} else {
format!("{} {}", self.mnemonic, self.operands.join(", "))
}
}
}
pub fn decode_aarch64(insn: u32, address: u64) -> Option<AArch64DecodedInst> {
let _top = (insn >> 25) & 0x7F; let op0 = (insn >> 28) & 0xF; let _op1 = (insn >> 25) & 0xF;
match op0 {
0b0000 | 0b0001 => decode_aarch64_reserved(insn, address),
0b0010 | 0b0011 => None,
0b0100..=0b0101 => decode_aarch64_load_store(insn, address),
0b0110..=0b0111 => None,
0b1000..=0b1001 => decode_aarch64_dp_imm(insn, address),
0b1010..=0b1011 => decode_aarch64_branch_sys(insn, address),
0b1100..=0b1101 => decode_aarch64_load_store_simd(insn, address),
0b1110..=0b1111 => decode_aarch64_dp_reg(insn, address),
_ => None,
}
}
fn decode_aarch64_reserved(_insn: u32, _address: u64) -> Option<AArch64DecodedInst> {
None
}
fn decode_aarch64_dp_imm(insn: u32, address: u64) -> Option<AArch64DecodedInst> {
let op = (insn >> 23) & 0x7;
match op {
0b000 | 0b001 => {
let rd = insn & 0x1F;
let immlo = (insn >> 29) & 0x3;
let immhi = (insn >> 5) & 0x7FFFF;
let imm = ((immlo as i64) << 29) | ((immhi as i64) << 5);
let mnem = if op == 0b001 { "adrp" } else { "adr" };
Some(AArch64DecodedInst {
mnemonic: mnem.to_string(),
operands: vec![aarch64_reg_name(rd, true), format!("#0x{:x}", imm)],
size: 4,
address,
})
}
0b010 | 0b011 => {
let sf = (insn >> 31) & 0x1;
let op_bit = (insn >> 30) & 0x1; let sh = (insn >> 22) & 0x1;
let imm12 = (insn >> 10) & 0xFFF;
let rn = (insn >> 5) & 0x1F;
let rd = insn & 0x1F;
let imm = if sh == 1 { imm12 << 12 } else { imm12 };
let mnem = if op_bit == 0 { "add" } else { "sub" };
let is_64 = sf == 1;
Some(AArch64DecodedInst {
mnemonic: mnem.to_string(),
operands: vec![
aarch64_reg_name(rd, is_64),
aarch64_reg_name(rn, is_64),
format!("#0x{}", imm),
],
size: 4,
address,
})
}
0b100 => {
let sf = (insn >> 31) & 0x1;
let opc = (insn >> 29) & 0x3;
let n = (insn >> 22) & 0x1;
let immr = (insn >> 16) & 0x3F;
let imms = (insn >> 10) & 0x3F;
let rn = (insn >> 5) & 0x1F;
let rd = insn & 0x1F;
let mnem = match opc {
0b00 => "and",
0b01 => "orr",
0b10 => "eor",
0b11 => "ands",
_ => "???",
};
let is_64 = sf == 1;
Some(AArch64DecodedInst {
mnemonic: mnem.to_string(),
operands: vec![
aarch64_reg_name(rd, is_64),
aarch64_reg_name(rn, is_64),
format!("#0x{:x}", decode_a64_bitmask(n, immr, imms, sf == 1)),
],
size: 4,
address,
})
}
0b101 => {
let sf = (insn >> 31) & 0x1;
let opc = (insn >> 29) & 0x3;
let hw = (insn >> 21) & 0x3;
let imm16 = (insn >> 5) & 0xFFFF;
let rd = insn & 0x1F;
let mnem = match opc {
0b00 => "movn",
0b10 => "movz",
0b11 => "movk",
_ => "???",
};
let is_64 = sf == 1;
let shift = hw * 16;
Some(AArch64DecodedInst {
mnemonic: mnem.to_string(),
operands: vec![
aarch64_reg_name(rd, is_64),
if shift > 0 {
format!("#0x{:x}, lsl #{}", imm16, shift)
} else {
format!("#0x{:x}", imm16)
},
],
size: 4,
address,
})
}
0b110 => {
let sf = (insn >> 31) & 0x1;
let opc = (insn >> 29) & 0x3;
let _n = (insn >> 22) & 0x1;
let immr = (insn >> 16) & 0x3F;
let imms = (insn >> 10) & 0x3F;
let rn = (insn >> 5) & 0x1F;
let rd = insn & 0x1F;
let mnem = match opc {
0b00 => "sbfm",
0b01 => "bfm",
0b10 => "ubfm",
_ => "???",
};
let is_64 = sf == 1;
Some(AArch64DecodedInst {
mnemonic: mnem.to_string(),
operands: vec![
aarch64_reg_name(rd, is_64),
aarch64_reg_name(rn, is_64),
format!("#0x{:x}", immr),
format!("#0x{:x}", imms),
],
size: 4,
address,
})
}
0b111 => {
let sf = (insn >> 31) & 0x1;
let _op21 = (insn >> 29) & 0x3;
let _n = (insn >> 22) & 0x1;
let rm = (insn >> 16) & 0x1F;
let imms = (insn >> 10) & 0x3F;
let rn = (insn >> 5) & 0x1F;
let rd = insn & 0x1F;
let is_64 = sf == 1;
Some(AArch64DecodedInst {
mnemonic: "extr".to_string(),
operands: vec![
aarch64_reg_name(rd, is_64),
aarch64_reg_name(rn, is_64),
aarch64_reg_name(rm, is_64),
format!("#0x{:x}", imms),
],
size: 4,
address,
})
}
_ => None,
}
}
fn decode_aarch64_dp_reg(insn: u32, address: u64) -> Option<AArch64DecodedInst> {
let op0 = (insn >> 30) & 0x1; let op1 = (insn >> 28) & 0x3; let _op2 = (insn >> 21) & 0xF; let _op3 = (insn >> 10) & 0x3F;
if op0 == 0 && op1 == 0b10 {
let sf = (insn >> 31) & 0x1;
let opc = (insn >> 29) & 0x3;
let shift = (insn >> 22) & 0x3;
let n = (insn >> 21) & 0x1;
let rm = (insn >> 16) & 0x1F;
let imm6 = (insn >> 10) & 0x3F;
let rn = (insn >> 5) & 0x1F;
let rd = insn & 0x1F;
let mnem = match (opc, n) {
(0b00, 0) => "and",
(0b00, 1) => "bic",
(0b01, 0) => "orr",
(0b01, 1) => "orn",
(0b10, 0) => "eor",
(0b10, 1) => "eon",
(0b11, 0) => "ands",
(0b11, 1) => "bics",
_ => "???",
};
let is_64 = sf == 1;
let mut operands = vec![
aarch64_reg_name(rd, is_64),
aarch64_reg_name(rn, is_64),
aarch64_reg_name(rm, is_64),
];
if imm6 > 0 {
operands.push(format!("{} #{}", aarch64_shift_name(shift), imm6));
}
Some(AArch64DecodedInst {
mnemonic: mnem.to_string(),
operands,
size: 4,
address,
})
} else if op0 == 0 && op1 == 0b01 {
let sf = (insn >> 31) & 0x1;
let op = (insn >> 30) & 0x1;
let s = (insn >> 29) & 0x1;
let shift = (insn >> 22) & 0x3;
let rm = (insn >> 16) & 0x1F;
let imm6 = (insn >> 10) & 0x3F;
let rn = (insn >> 5) & 0x1F;
let rd = insn & 0x1F;
let mnem = match (op, s) {
(0, 0) => "add",
(0, 1) => "adds",
(1, 0) => "sub",
(1, 1) => "subs",
_ => "???",
};
let is_64 = sf == 1;
let mut operands = vec![
aarch64_reg_name(rd, is_64),
aarch64_reg_name(rn, is_64),
aarch64_reg_name(rm, is_64),
];
if imm6 > 0 {
operands.push(format!("{} #{}", aarch64_shift_name(shift), imm6));
}
Some(AArch64DecodedInst {
mnemonic: mnem.to_string(),
operands,
size: 4,
address,
})
} else if op0 == 1 && op1 == 0b10 {
let sf = (insn >> 31) & 0x1;
let op54 = (insn >> 29) & 0x3;
let o0 = (insn >> 15) & 0x1;
let rm = (insn >> 16) & 0x1F;
let ra = (insn >> 10) & 0x1F;
let rn = (insn >> 5) & 0x1F;
let rd = insn & 0x1F;
let mnem = match (op54, o0) {
(0b00, 0) => "madd",
(0b00, 1) => "msub",
(0b01, 0) => "smaddl",
(0b01, 1) => "smsubl",
(0b10, 0) => "smulh",
(0b11, 0) => "umaddl",
(0b11, 1) => "umsubl",
_ => "???",
};
let is_64 = sf == 1;
Some(AArch64DecodedInst {
mnemonic: mnem.to_string(),
operands: vec![
aarch64_reg_name(rd, is_64),
aarch64_reg_name(rn, is_64),
aarch64_reg_name(rm, is_64),
aarch64_reg_name(ra, is_64),
],
size: 4,
address,
})
} else if op0 == 0 && op1 == 0b11 {
let sf = (insn >> 31) & 0x1;
let op = (insn >> 30) & 0x1;
let s = (insn >> 29) & 0x1;
let rm = (insn >> 16) & 0x1F;
let option = (insn >> 13) & 0x7;
let imm3 = (insn >> 10) & 0x7;
let rn = (insn >> 5) & 0x1F;
let rd = insn & 0x1F;
let mnem = match (op, s) {
(0, 0) => "add",
(0, 1) => "adds",
(1, 0) => "sub",
(1, 1) => "subs",
_ => "???",
};
let is_64 = sf == 1;
Some(AArch64DecodedInst {
mnemonic: mnem.to_string(),
operands: vec![
aarch64_reg_name(rd, is_64),
aarch64_reg_name(rn, is_64),
format!(
"{}, {} #{}",
aarch64_reg_name(rm, false), aarch64_extend_name(option),
imm3
),
],
size: 4,
address,
})
} else if op0 == 1 && op1 == 0b01 {
let sf = (insn >> 31) & 0x1;
let s = (insn >> 29) & 0x1;
let opcode = (insn >> 10) & 0x3F;
let rm = (insn >> 16) & 0x1F;
let rn = (insn >> 5) & 0x1F;
let rd = insn & 0x1F;
let mnem = match opcode {
0b000010 => {
if s == 0 {
"udiv"
} else {
"sdiv"
}
}
0b001000 => {
if s == 0 {
"lslv"
} else {
"???"
}
}
0b001001 => {
if s == 0 {
"lsrv"
} else {
"???"
}
}
0b001010 => {
if s == 0 {
"asrv"
} else {
"???"
}
}
0b001011 => {
if s == 0 {
"rorv"
} else {
"???"
}
}
0b000011 => {
if s == 0 {
"mneg"
} else {
"???"
}
}
0b000000 => {
if s == 0 {
"???"
} else {
"subp"
}
}
_ => "???",
};
let is_64 = sf == 1;
Some(AArch64DecodedInst {
mnemonic: mnem.to_string(),
operands: vec![
aarch64_reg_name(rd, is_64),
aarch64_reg_name(rn, is_64),
aarch64_reg_name(rm, is_64),
],
size: 4,
address,
})
} else {
None
}
}
fn decode_aarch64_branch_sys(insn: u32, address: u64) -> Option<AArch64DecodedInst> {
let op0 = (insn >> 29) & 0x7;
match op0 {
0b010 => {
let cond = insn & 0xF;
let imm19 = (insn >> 5) & 0x7FFFF;
let offset = sign_extend_64(imm19, 19) * 4;
Some(AArch64DecodedInst {
mnemonic: format!("b.{}", aarch64_cond_name(cond)),
operands: vec![format!("#0x{:x}", (address as i64) + offset)],
size: 4,
address,
})
}
0b101 => {
let op = (insn >> 31) & 0x1;
let imm26 = insn & 0x03FFFFFF;
let offset = sign_extend_64(imm26, 26) * 4;
let mnem = if op == 0 { "b" } else { "bl" };
Some(AArch64DecodedInst {
mnemonic: mnem.to_string(),
operands: vec![format!("#0x{:x}", (address as i64) + offset)],
size: 4,
address,
})
}
0b110 => {
let sf = (insn >> 31) & 0x1;
let op = (insn >> 24) & 0x1;
let imm19 = (insn >> 5) & 0x7FFFF;
let rt = insn & 0x1F;
let offset = sign_extend_64(imm19, 19) * 4;
let mnem = if op == 0 { "cbz" } else { "cbnz" };
let is_64 = sf == 1;
Some(AArch64DecodedInst {
mnemonic: mnem.to_string(),
operands: vec![
aarch64_reg_name(rt, is_64),
format!("#0x{:x}", (address as i64) + offset),
],
size: 4,
address,
})
}
0b111 => {
let b5 = (insn >> 31) & 0x1;
let op = (insn >> 24) & 0x1;
let b40 = (insn >> 19) & 0x1F;
let imm14 = (insn >> 5) & 0x3FFF;
let rt = insn & 0x1F;
let bit = (b5 << 5) | b40;
let offset = sign_extend_64(imm14, 14) * 4;
let mnem = if op == 0 { "tbz" } else { "tbnz" };
Some(AArch64DecodedInst {
mnemonic: mnem.to_string(),
operands: vec![
aarch64_reg_name(rt, true),
format!("#0x{:x}", bit),
format!("#0x{:x}", (address as i64) + offset),
],
size: 4,
address,
})
}
0b000 | 0b001 | 0b011 => {
let opc = (insn >> 21) & 0x7;
let imm16 = (insn >> 5) & 0xFFFF;
match opc {
0b000 => Some(AArch64DecodedInst {
mnemonic: "svc".to_string(),
operands: vec![format!("#0x{:x}", imm16)],
size: 4,
address,
}),
0b001 => Some(AArch64DecodedInst {
mnemonic: "brk".to_string(),
operands: vec![format!("#0x{:x}", imm16)],
size: 4,
address,
}),
0b010 => Some(AArch64DecodedInst {
mnemonic: "hvc".to_string(),
operands: vec![format!("#0x{:x}", imm16)],
size: 4,
address,
}),
0b011 => Some(AArch64DecodedInst {
mnemonic: "smc".to_string(),
operands: vec![format!("#0x{:x}", imm16)],
size: 4,
address,
}),
_ => None,
}
}
0b100 => {
let l = (insn >> 21) & 0x1;
let op0 = (insn >> 19) & 0x3;
let op1 = (insn >> 16) & 0x7;
let crn = (insn >> 12) & 0xF;
let crm = (insn >> 8) & 0xF;
let op2 = (insn >> 5) & 0x7;
let rt = insn & 0x1F;
if l == 0 && op0 == 0b00 && op1 == 0b011 && crn == 0b0100 {
let mnem = match (crm, op2) {
(0b0000, 0b011) => "clrex",
(0b0000, 0b100) => "dsb",
(0b0001, 0b100) => "dsb",
(0b0010, 0b100) => "dsb",
(0b0011, 0b100) => "dsb",
(0b0100, 0b100) => "dsb",
(0b0101, 0b100) => "dmb",
(0b0110, 0b100) => "isb",
(0b0111, 0b100) => "sb",
_ => {
if crn == 0b0010 && crm == 0b0000 {
"nop"
} else if crn == 0b0011 && crm == 0b0000 {
"yield"
} else if crn == 0b0010 && crm == 0b0001 {
"wfe"
} else if crn == 0b0011 && crm == 0b0001 {
"wfi"
} else if crn == 0b0010 && crm == 0b0010 {
"sev"
} else if crn == 0b0011 && crm == 0b0010 {
"sevl"
} else {
"hint"
}
}
};
Some(AArch64DecodedInst {
mnemonic: mnem.to_string(),
operands: vec![],
size: 4,
address,
})
} else if l == 1 {
Some(AArch64DecodedInst {
mnemonic: "mrs".to_string(),
operands: vec![
aarch64_reg_name(rt, true),
format!("s{}_{}_c{}_c{}_{}", op0, op1, crn, crm, op2),
],
size: 4,
address,
})
} else {
Some(AArch64DecodedInst {
mnemonic: "msr".to_string(),
operands: vec![
format!("s{}_{}_c{}_c{}_{}", op0, op1, crn, crm, op2),
aarch64_reg_name(rt, true),
],
size: 4,
address,
})
}
}
_ => None,
}
}
fn decode_aarch64_load_store(insn: u32, address: u64) -> Option<AArch64DecodedInst> {
let op0 = (insn >> 28) & 0xF; let _op1 = (insn >> 26) & 0x1; let _op2 = (insn >> 23) & 0x3; let _op3 = (insn >> 16) & 0x3F; let _op4 = (insn >> 10) & 0x3;
let size = (insn >> 30) & 0x3; let rt = insn & 0x1F;
let rn = (insn >> 5) & 0x1F;
let rt2 = (insn >> 10) & 0x1F;
if op0 == 0b0100 || op0 == 0b0101 {
let v = (insn >> 26) & 0x1;
let opc = (insn >> 22) & 0x3;
let imm12 = (insn >> 10) & 0xFFF;
if v == 0 {
let mnem = match (op0 & 0x1, opc, size) {
(0, 0b00, _) => "strb",
(0, 0b01, _) => "ldrb",
(0, 0b10, 0b00) => "str",
(0, 0b10, 0b01) => "str",
(0, 0b11, 0b00) => "ldr",
(0, 0b11, 0b01) => "ldr",
(1, 0b00, _) => "strh",
(1, 0b01, _) => "ldrh",
(1, 0b10, _) => "ldrsw",
(1, 0b11, _) => "???",
_ => "???",
};
let scale = match mnem {
"strb" | "ldrb" => 1,
"strh" | "ldrh" => 2,
"str" | "ldr" if size == 0b00 => 4,
"str" | "ldr" => 8,
"ldrsw" => 4,
_ => 1,
};
let is_64 = size >= 0b01;
let offset = imm12 * scale;
Some(AArch64DecodedInst {
mnemonic: mnem.to_string(),
operands: vec![
aarch64_reg_name(rt, is_64),
format!("[{}, #0x{:x}]", aarch64_reg_name(rn, true), offset),
],
size: 4,
address,
})
} else {
None
}
} else if op0 >= 0b1000 && op0 <= 0b1011 {
let opc = (insn >> 30) & 0x3;
let l = (insn >> 22) & 0x1;
let imm7 = (insn >> 15) & 0x7F;
let mnem = match (l, opc) {
(0, 0b00) => "stp", (1, 0b00) => "ldp", (0, 0b10) => "stp", (1, 0b10) => "ldp", _ => "???",
};
let scale = if opc == 0b00 { 4 } else { 8 };
let offset = (sign_extend_64(imm7, 7) * scale as i64) as u32;
let is_64 = opc == 0b10;
Some(AArch64DecodedInst {
mnemonic: mnem.to_string(),
operands: vec![
aarch64_reg_name(rt, is_64),
aarch64_reg_name(rt2, is_64),
format!("[{}, #0x{:x}]", aarch64_reg_name(rn, true), offset),
],
size: 4,
address,
})
} else {
None
}
}
fn decode_aarch64_load_store_simd(insn: u32, address: u64) -> Option<AArch64DecodedInst> {
let op0 = (insn >> 24) & 0xF;
let size = (insn >> 10) & 0x3;
let rt = insn & 0x1F;
let rn = (insn >> 5) & 0x1F;
let imm12 = (insn >> 10) & 0xFFF;
if op0 == 0b0011 || op0 == 0b0111 {
let _opc = (insn >> 22) & 0x3;
let l = (insn >> 22) & 0x1;
let mnem = if l == 1 { "ldr" } else { "str" };
let scale = match size {
0b00 => 1, 0b01 => 2, 0b10 => 4, 0b11 => 8, _ => 16, };
Some(AArch64DecodedInst {
mnemonic: mnem.to_string(),
operands: vec![
aarch64_fp_reg_name(rt, size),
format!("[{}, #0x{:x}]", aarch64_reg_name(rn, true), imm12 * scale),
],
size: 4,
address,
})
} else {
None
}
}
fn sign_extend_64(value: u32, bits: u32) -> i64 {
let shift = 64 - bits;
((value as i64) << shift) >> shift
}
fn decode_a64_bitmask(n: u32, immr: u32, imms: u32, is_64: bool) -> u64 {
let len = if is_64 { 64 } else { 32 };
if n == 0 && imms == 0 && immr == 0 {
return 0;
}
let levels = if is_64 {
highest_set_bit((n << 6) | (!imms & 0x3F))
} else {
highest_set_bit(!imms & 0x3F)
};
let s = imms & ((1 << (levels + 1)) - 1);
let r = immr & ((1 << (levels + 1)) - 1);
let diff = s.wrapping_sub(r);
let welem = (1u64 << (s + 1)) - 1;
let telem = (1u64 << (diff + 1)) - 1;
let wmask = rotate_right(welem, r as u32, len);
let tmask = rotate_right(telem, r as u32, len);
replicate(wmask & tmask, len)
}
fn highest_set_bit(val: u32) -> u32 {
if val == 0 {
return 0;
}
31 - val.leading_zeros()
}
fn rotate_right(val: u64, rot: u32, len: u32) -> u64 {
let r = rot % len;
(val >> r) | (val << (len - r))
}
fn replicate(val: u64, len: u32) -> u64 {
if len >= 64 {
val
} else {
let mut result = val;
let mut shift = len;
while shift < 64 {
result |= val << shift;
shift += len;
}
result
}
}
fn riscv_reg_name(reg: u32, abi: bool) -> String {
if abi {
match reg {
0 => "zero".to_string(),
1 => "ra".to_string(),
2 => "sp".to_string(),
3 => "gp".to_string(),
4 => "tp".to_string(),
5 => "t0".to_string(),
6 => "t1".to_string(),
7 => "t2".to_string(),
8 => "s0".to_string(),
9 => "s1".to_string(),
10 => "a0".to_string(),
11 => "a1".to_string(),
12 => "a2".to_string(),
13 => "a3".to_string(),
14 => "a4".to_string(),
15 => "a5".to_string(),
16 => "a6".to_string(),
17 => "a7".to_string(),
18 => "s2".to_string(),
19 => "s3".to_string(),
20 => "s4".to_string(),
21 => "s5".to_string(),
22 => "s6".to_string(),
23 => "s7".to_string(),
24 => "s8".to_string(),
25 => "s9".to_string(),
26 => "s10".to_string(),
27 => "s11".to_string(),
28 => "t3".to_string(),
29 => "t4".to_string(),
30 => "t5".to_string(),
31 => "t6".to_string(),
_ => format!("x{}", reg),
}
} else {
format!("x{}", reg)
}
}
#[derive(Debug, Clone)]
pub struct RISCVDecodedInst {
pub mnemonic: String,
pub operands: Vec<String>,
pub size: u32,
pub address: u64,
}
impl RISCVDecodedInst {
pub fn format(&self) -> String {
if self.operands.is_empty() {
self.mnemonic.clone()
} else {
format!("{} {}", self.mnemonic, self.operands.join(", "))
}
}
}
pub fn decode_riscv(insn: u32, address: u64) -> Option<RISCVDecodedInst> {
let opcode = insn & 0x7F;
match opcode {
0b0110111 => decode_riscv_u(insn, address, "lui"),
0b0010111 => decode_riscv_u(insn, address, "auipc"),
0b1101111 => decode_riscv_j(insn, address),
0b1100111 => decode_riscv_i_jalr(insn, address),
0b1100011 => decode_riscv_b(insn, address),
0b0000011 => decode_riscv_i_load(insn, address),
0b0100011 => decode_riscv_s(insn, address),
0b0010011 => decode_riscv_i_alu(insn, address),
0b0110011 => decode_riscv_r(insn, address),
0b0011011 => decode_riscv_i_alu32(insn, address),
0b0111011 => decode_riscv_r_32(insn, address),
0b0001111 => decode_riscv_fence(insn, address),
0b1110011 => decode_riscv_system(insn, address),
_ => None,
}
}
fn decode_riscv_r(insn: u32, address: u64) -> Option<RISCVDecodedInst> {
let rd = (insn >> 7) & 0x1F;
let funct3 = (insn >> 12) & 0x7;
let rs1 = (insn >> 15) & 0x1F;
let rs2 = (insn >> 20) & 0x1F;
let funct7 = (insn >> 25) & 0x7F;
let mnem = match (funct3, funct7) {
(0b000, 0b0000000) => "add",
(0b000, 0b0100000) => "sub",
(0b001, 0b0000000) => "sll",
(0b010, 0b0000000) => "slt",
(0b011, 0b0000000) => "sltu",
(0b100, 0b0000000) => "xor",
(0b101, 0b0000000) => "srl",
(0b101, 0b0100000) => "sra",
(0b110, 0b0000000) => "or",
(0b111, 0b0000000) => "and",
(0b000, 0b0000001) => "mul",
(0b001, 0b0000001) => "mulh",
(0b010, 0b0000001) => "mulhsu",
(0b011, 0b0000001) => "mulhu",
(0b100, 0b0000001) => "div",
(0b101, 0b0000001) => "divu",
(0b110, 0b0000001) => "rem",
(0b111, 0b0000001) => "remu",
_ => "???",
};
Some(RISCVDecodedInst {
mnemonic: mnem.to_string(),
operands: vec![
riscv_reg_name(rd, true),
riscv_reg_name(rs1, true),
riscv_reg_name(rs2, true),
],
size: 4,
address,
})
}
fn decode_riscv_r_32(insn: u32, address: u64) -> Option<RISCVDecodedInst> {
let rd = (insn >> 7) & 0x1F;
let funct3 = (insn >> 12) & 0x7;
let rs1 = (insn >> 15) & 0x1F;
let rs2 = (insn >> 20) & 0x1F;
let funct7 = (insn >> 25) & 0x7F;
let mnem = match (funct3, funct7) {
(0b000, 0b0000000) => "addw",
(0b000, 0b0100000) => "subw",
(0b001, 0b0000000) => "sllw",
(0b101, 0b0000000) => "srlw",
(0b101, 0b0100000) => "sraw",
(0b000, 0b0000001) => "mulw",
(0b100, 0b0000001) => "divw",
(0b101, 0b0000001) => "divuw",
(0b110, 0b0000001) => "remw",
(0b111, 0b0000001) => "remuw",
_ => "???",
};
Some(RISCVDecodedInst {
mnemonic: mnem.to_string(),
operands: vec![
riscv_reg_name(rd, true),
riscv_reg_name(rs1, true),
riscv_reg_name(rs2, true),
],
size: 4,
address,
})
}
fn decode_riscv_i_alu(insn: u32, address: u64) -> Option<RISCVDecodedInst> {
let rd = (insn >> 7) & 0x1F;
let funct3 = (insn >> 12) & 0x7;
let rs1 = (insn >> 15) & 0x1F;
let imm = ((insn >> 20) & 0xFFF) as i32;
let imm_signed = if (imm & 0x800) != 0 {
imm | !0xFFF
} else {
imm
};
let (mnem, use_imm) = match funct3 {
0b000 => ("addi", true),
0b010 => ("slti", true),
0b011 => ("sltiu", true),
0b100 => ("xori", true),
0b110 => ("ori", true),
0b111 => ("andi", true),
0b001 => {
let _shamt = (imm & 0x3F) as u32;
if (imm >> 6) == 0 {
("slli", false)
} else {
return None;
}
}
0b101 => {
let shamt = (imm & 0x3F) as u32;
return Some(RISCVDecodedInst {
mnemonic: if (imm >> 6) == 0x10 { "srai" } else { "srli" }.to_string(),
operands: vec![
riscv_reg_name(rd, true),
riscv_reg_name(rs1, true),
format!("0x{:x}", shamt),
],
size: 4,
address,
});
}
_ => ("???", false),
};
Some(RISCVDecodedInst {
mnemonic: mnem.to_string(),
operands: vec![
riscv_reg_name(rd, true),
riscv_reg_name(rs1, true),
if use_imm {
format!("0x{:x}", imm_signed)
} else {
"".to_string()
},
],
size: 4,
address,
})
}
fn decode_riscv_i_alu32(insn: u32, address: u64) -> Option<RISCVDecodedInst> {
let rd = (insn >> 7) & 0x1F;
let funct3 = (insn >> 12) & 0x7;
let rs1 = (insn >> 15) & 0x1F;
let imm = ((insn >> 20) & 0xFFF) as i32;
match funct3 {
0b000 => Some(RISCVDecodedInst {
mnemonic: "addiw".to_string(),
operands: vec![
riscv_reg_name(rd, true),
riscv_reg_name(rs1, true),
format!(
"0x{:x}",
if (imm & 0x800) != 0 {
imm | !0xFFF
} else {
imm
}
),
],
size: 4,
address,
}),
0b001 => {
let shamt = (imm & 0x1F) as u32;
Some(RISCVDecodedInst {
mnemonic: "slliw".to_string(),
operands: vec![
riscv_reg_name(rd, true),
riscv_reg_name(rs1, true),
format!("0x{:x}", shamt),
],
size: 4,
address,
})
}
0b101 => {
let shamt = (imm & 0x1F) as u32;
Some(RISCVDecodedInst {
mnemonic: if (imm >> 5) & 0x7F == 0x10 {
"sraiw"
} else {
"srliw"
}
.to_string(),
operands: vec![
riscv_reg_name(rd, true),
riscv_reg_name(rs1, true),
format!("0x{:x}", shamt),
],
size: 4,
address,
})
}
_ => None,
}
}
fn decode_riscv_i_load(insn: u32, address: u64) -> Option<RISCVDecodedInst> {
let rd = (insn >> 7) & 0x1F;
let funct3 = (insn >> 12) & 0x7;
let rs1 = (insn >> 15) & 0x1F;
let imm = ((insn >> 20) & 0xFFF) as i32;
let offset = if (imm & 0x800) != 0 {
imm | !0xFFF
} else {
imm
};
let mnem = match funct3 {
0b000 => "lb",
0b001 => "lh",
0b010 => "lw",
0b011 => "ld",
0b100 => "lbu",
0b101 => "lhu",
0b110 => "lwu",
_ => "???",
};
Some(RISCVDecodedInst {
mnemonic: mnem.to_string(),
operands: vec![
riscv_reg_name(rd, true),
format!("0x{:x}({})", offset, riscv_reg_name(rs1, true)),
],
size: 4,
address,
})
}
fn decode_riscv_s(insn: u32, address: u64) -> Option<RISCVDecodedInst> {
let funct3 = (insn >> 12) & 0x7;
let rs1 = (insn >> 15) & 0x1F;
let rs2 = (insn >> 20) & 0x1F;
let imm_high = (insn >> 25) & 0x7F;
let imm_low = (insn >> 7) & 0x1F;
let imm = ((imm_high << 5) | imm_low) as i32;
let offset = if (imm & 0x800) != 0 {
imm | !0xFFF
} else {
imm
};
let mnem = match funct3 {
0b000 => "sb",
0b001 => "sh",
0b010 => "sw",
0b011 => "sd",
_ => "???",
};
Some(RISCVDecodedInst {
mnemonic: mnem.to_string(),
operands: vec![
riscv_reg_name(rs2, true),
format!("0x{:x}({})", offset, riscv_reg_name(rs1, true)),
],
size: 4,
address,
})
}
fn decode_riscv_b(insn: u32, address: u64) -> Option<RISCVDecodedInst> {
let funct3 = (insn >> 12) & 0x7;
let rs1 = (insn >> 15) & 0x1F;
let rs2 = (insn >> 20) & 0x1F;
let imm = ((insn >> 31) & 0x1) << 12
| ((insn >> 7) & 0x1) << 11
| ((insn >> 25) & 0x3F) << 5
| ((insn >> 8) & 0xF) << 1;
let offset = if (imm & 0x1000) != 0 {
(imm | !0x1FFF) as i32
} else {
imm as i32
};
let mnem = match funct3 {
0b000 => "beq",
0b001 => "bne",
0b100 => "blt",
0b101 => "bge",
0b110 => "bltu",
0b111 => "bgeu",
_ => "???",
};
Some(RISCVDecodedInst {
mnemonic: mnem.to_string(),
operands: vec![
riscv_reg_name(rs1, true),
riscv_reg_name(rs2, true),
format!("0x{:x}", (address as i64) + offset as i64),
],
size: 4,
address,
})
}
fn decode_riscv_j(insn: u32, address: u64) -> Option<RISCVDecodedInst> {
let rd = (insn >> 7) & 0x1F;
let imm = ((insn >> 31) & 0x1) << 20
| ((insn >> 12) & 0xFF) << 12
| ((insn >> 20) & 0x1) << 11
| ((insn >> 21) & 0x3FF) << 1;
let offset = if (imm & 0x100000) != 0 {
(imm | !0x1FFFFF) as i32
} else {
imm as i32
};
Some(RISCVDecodedInst {
mnemonic: "jal".to_string(),
operands: vec![
riscv_reg_name(rd, true),
format!("0x{:x}", (address as i64) + offset as i64),
],
size: 4,
address,
})
}
fn decode_riscv_i_jalr(insn: u32, address: u64) -> Option<RISCVDecodedInst> {
let rd = (insn >> 7) & 0x1F;
let rs1 = (insn >> 15) & 0x1F;
let imm = ((insn >> 20) & 0xFFF) as i32;
let offset = if (imm & 0x800) != 0 {
imm | !0xFFF
} else {
imm
};
Some(RISCVDecodedInst {
mnemonic: if rd == 0 {
"jr".to_string()
} else {
"jalr".to_string()
},
operands: vec![
riscv_reg_name(rd, true),
format!("0x{:x}({})", offset, riscv_reg_name(rs1, true)),
],
size: 4,
address,
})
}
fn decode_riscv_u(insn: u32, address: u64, mnem: &str) -> Option<RISCVDecodedInst> {
let rd = (insn >> 7) & 0x1F;
let imm = (insn & 0xFFFFF000) as i32;
Some(RISCVDecodedInst {
mnemonic: mnem.to_string(),
operands: vec![riscv_reg_name(rd, true), format!("0x{:x}", imm)],
size: 4,
address,
})
}
fn decode_riscv_fence(insn: u32, address: u64) -> Option<RISCVDecodedInst> {
if insn == 0x00000073 {
Some(RISCVDecodedInst {
mnemonic: "ecall".to_string(),
operands: vec![],
size: 4,
address,
})
} else if insn == 0x00100073 {
Some(RISCVDecodedInst {
mnemonic: "ebreak".to_string(),
operands: vec![],
size: 4,
address,
})
} else {
Some(RISCVDecodedInst {
mnemonic: "fence".to_string(),
operands: vec![],
size: 4,
address,
})
}
}
fn decode_riscv_system(insn: u32, address: u64) -> Option<RISCVDecodedInst> {
if insn == 0x00000073 {
Some(RISCVDecodedInst {
mnemonic: "ecall".to_string(),
operands: vec![],
size: 4,
address,
})
} else if insn == 0x00100073 {
Some(RISCVDecodedInst {
mnemonic: "ebreak".to_string(),
operands: vec![],
size: 4,
address,
})
} else {
let funct3 = (insn >> 12) & 0x7;
if funct3 == 0b000 {
let funct12 = (insn >> 20) & 0xFFF;
match funct12 {
0x000 => Some(RISCVDecodedInst {
mnemonic: "ecall".to_string(),
operands: vec![],
size: 4,
address,
}),
0x001 => Some(RISCVDecodedInst {
mnemonic: "ebreak".to_string(),
operands: vec![],
size: 4,
address,
}),
_ => None,
}
} else if funct3 == 0b001 {
let rd = (insn >> 7) & 0x1F;
let rs1 = (insn >> 15) & 0x1F;
let csr = (insn >> 20) & 0xFFF;
Some(RISCVDecodedInst {
mnemonic: "csrrw".to_string(),
operands: vec![
riscv_reg_name(rd, true),
format!("0x{:x}", csr),
riscv_reg_name(rs1, true),
],
size: 4,
address,
})
} else if funct3 == 0b010 {
let rd = (insn >> 7) & 0x1F;
let rs1 = (insn >> 15) & 0x1F;
let csr = (insn >> 20) & 0xFFF;
Some(RISCVDecodedInst {
mnemonic: "csrrs".to_string(),
operands: vec![
riscv_reg_name(rd, true),
format!("0x{:x}", csr),
riscv_reg_name(rs1, true),
],
size: 4,
address,
})
} else if funct3 == 0b011 {
let rd = (insn >> 7) & 0x1F;
let rs1 = (insn >> 15) & 0x1F;
let csr = (insn >> 20) & 0xFFF;
Some(RISCVDecodedInst {
mnemonic: "csrrc".to_string(),
operands: vec![
riscv_reg_name(rd, true),
format!("0x{:x}", csr),
riscv_reg_name(rs1, true),
],
size: 4,
address,
})
} else {
None
}
}
}
pub fn decode_register_name(reg: u32, target: &str) -> String {
match target {
"aarch64" => aarch64_reg_name(reg, true),
"arm32" => format!("r{}", reg),
"riscv32" | "riscv64" => riscv_reg_name(reg, true),
"x86_64" => {
let names_64: [&str; 16] = [
"rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi", "r8", "r9", "r10", "r11",
"r12", "r13", "r14", "r15",
];
if reg < 16 {
names_64[reg as usize].to_string()
} else {
format!("r{}", reg)
}
}
_ => format!("r{}", reg),
}
}
pub fn format_immediate(value: i64, signed: bool) -> String {
if signed {
if value >= -127 && value <= 127 {
return format!("#0x{:x}", value);
}
if value < 0 {
format!("#-0x{:x}", -value)
} else {
format!("#0x{:x}", value)
}
} else {
if value <= 9 {
format!("#{}", value)
} else {
format!("#0x{:x}", value)
}
}
}
pub fn format_memory_operand(
base: Option<u32>,
index: Option<u32>,
scale: u32,
disp: i64,
target: &str,
) -> String {
let mut parts = Vec::new();
if disp != 0 || (base.is_none() && index.is_none()) {
parts.push(format!("0x{:x}", disp));
}
if let Some(b) = base {
parts.push(decode_register_name(b, target));
}
if let Some(i) = index {
if scale > 1 {
parts.push(format!("{}*{}", decode_register_name(i, target), scale));
} else {
parts.push(decode_register_name(i, target));
}
}
format!("[{}]", parts.join(" + "))
}
pub fn format_preindex_operand(base: u32, offset: i64, target: &str) -> String {
if offset >= 0 {
format!("[{}, #0x{:x}]!", decode_register_name(base, target), offset)
} else {
format!(
"[{}, #-0x{:x}]!",
decode_register_name(base, target),
-offset
)
}
}
pub fn format_postindex_operand(base: u32, offset: i64, target: &str) -> String {
format!("[{}], #0x{:x}", decode_register_name(base, target), offset)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DecodeStatus {
Success,
Fail,
SoftFail,
}
pub trait MCDisassembler {
fn get_instruction(&self, bytes: &[u8], address: u64) -> Option<(MCInst, u32, DecodeStatus)>;
}
#[derive(Debug, Clone)]
pub struct DisassembledResult {
pub mnemonic: String,
pub operands: String,
pub size: u32,
pub address: u64,
pub status: DecodeStatus,
pub comment: Option<String>,
}
impl DisassembledResult {
pub fn format(&self) -> String {
let base = format!("{} {}", self.mnemonic, self.operands);
match &self.comment {
Some(c) => format!("{} ; {}", base, c),
None => base,
}
}
}
#[derive(Debug, Clone)]
pub struct SymbolEntry {
pub name: String,
pub address: u64,
pub size: u64,
pub kind: SymbolKind,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SymbolKind {
Function,
Data,
Unknown,
}
pub fn resolve_symbol(address: u64, symbols: &[SymbolEntry]) -> Option<String> {
for sym in symbols {
if sym.address == address {
return Some(sym.name.clone());
}
}
for sym in symbols {
if sym.kind == SymbolKind::Function
&& address >= sym.address
&& address < sym.address + sym.size
{
let offset = address - sym.address;
return Some(format!("{}+0x{:x}", sym.name, offset));
}
}
None
}
pub fn add_symbol_comment(target_addr: u64, symbols: &[SymbolEntry], pc: u64) -> Option<String> {
let resolved = resolve_symbol(target_addr, symbols)?;
if target_addr == pc {
Some(format!("<{}>", resolved))
} else if target_addr > pc {
let offset = target_addr - pc;
Some(format!("<{}+0x{:x}>", resolved, offset))
} else {
let offset = pc - target_addr;
Some(format!("<{}-0x{:x}>", resolved, offset))
}
}
pub fn print_annotations(insn: &MCInst, address: u64, symbols: &[SymbolEntry]) -> Vec<String> {
let mut annotations = Vec::new();
for op in &insn.operands {
if let Some(expr) = op.get_expr() {
annotations.push(format!("; expr: {}", expr));
}
}
if let Some(sym) = resolve_symbol(address, symbols) {
annotations.push(format!("{}:", sym));
}
annotations
}
#[cfg(test)]
mod tests {
use super::*;
fn decoder64() -> X86FullDecoder {
X86FullDecoder::new(X86Mode::Mode64)
}
#[test]
fn test_decode_nop() {
let d = decoder64();
let inst = d.decode_one(&[0x90], 0).unwrap();
assert_eq!(inst.mnemonic, "nop");
assert_eq!(inst.size, 1);
assert!(inst.operands.is_empty());
}
#[test]
fn test_decode_ret() {
let d = decoder64();
let inst = d.decode_one(&[0xC3], 0).unwrap();
assert_eq!(inst.mnemonic, "ret");
assert_eq!(inst.size, 1);
}
#[test]
fn test_decode_push_rax() {
let d = decoder64();
let inst = d.decode_one(&[0x50], 0).unwrap();
assert_eq!(inst.mnemonic, "push");
assert_eq!(inst.size, 1);
assert_eq!(inst.operands.len(), 1);
match &inst.operands[0] {
DecodedOperand::Reg { name, .. } => assert_eq!(name, "rax"),
_ => panic!("expected Reg"),
}
}
#[test]
fn test_decode_push_r12() {
let d = decoder64();
let inst = d.decode_one(&[0x41, 0x54], 0).unwrap();
assert_eq!(inst.mnemonic, "push");
match &inst.operands[0] {
DecodedOperand::Reg { name, .. } => assert_eq!(name, "r12"),
_ => panic!("expected Reg"),
}
}
#[test]
fn test_decode_pop_rdi() {
let d = decoder64();
let inst = d.decode_one(&[0x5F], 0).unwrap();
assert_eq!(inst.mnemonic, "pop");
match &inst.operands[0] {
DecodedOperand::Reg { name, .. } => assert_eq!(name, "rdi"),
_ => panic!("expected Reg"),
}
}
#[test]
fn test_decode_group1_add_imm8() {
let d = decoder64();
let inst = d.decode_one(&[0x83, 0xC0, 0x01], 0).unwrap();
assert_eq!(inst.mnemonic, "add");
assert_eq!(inst.size, 3);
}
#[test]
fn test_decode_group1_sub_imm8() {
let d = decoder64();
let inst = d.decode_one(&[0x83, 0xE8, 0x05], 0).unwrap();
assert_eq!(inst.mnemonic, "sub");
assert_eq!(inst.size, 3);
}
#[test]
fn test_decode_group1_cmp_imm8() {
let d = decoder64();
let inst = d.decode_one(&[0x83, 0xF8, 0x01], 0).unwrap();
assert_eq!(inst.mnemonic, "cmp");
}
#[test]
fn test_decode_group1_and_imm8() {
let d = decoder64();
let inst = d.decode_one(&[0x83, 0xE0, 0x0F], 0).unwrap();
assert_eq!(inst.mnemonic, "and");
}
#[test]
fn test_decode_group1_or_imm8() {
let d = decoder64();
let inst = d.decode_one(&[0x83, 0xC8, 0xFF], 0).unwrap();
assert_eq!(inst.mnemonic, "or");
}
#[test]
fn test_decode_group1_xor_imm8() {
let d = decoder64();
let inst = d.decode_one(&[0x83, 0xF0, 0x01], 0).unwrap();
assert_eq!(inst.mnemonic, "xor");
}
#[test]
fn test_decode_group1_adc_imm8() {
let d = decoder64();
let inst = d.decode_one(&[0x83, 0xD0, 0x00], 0).unwrap();
assert_eq!(inst.mnemonic, "adc");
}
#[test]
fn test_decode_group1_sbb_imm8() {
let d = decoder64();
let inst = d.decode_one(&[0x83, 0xD8, 0x00], 0).unwrap();
assert_eq!(inst.mnemonic, "sbb");
}
#[test]
fn test_decode_group3_not() {
let d = decoder64();
let inst = d.decode_one(&[0xF7, 0xD0], 0).unwrap();
assert_eq!(inst.mnemonic, "not");
assert_eq!(inst.operands.len(), 1);
}
#[test]
fn test_decode_group3_neg() {
let d = decoder64();
let inst = d.decode_one(&[0xF7, 0xD8], 0).unwrap();
assert_eq!(inst.mnemonic, "neg");
}
#[test]
fn test_decode_group3_mul() {
let d = decoder64();
let inst = d.decode_one(&[0xF7, 0xE0], 0).unwrap();
assert_eq!(inst.mnemonic, "mul");
}
#[test]
fn test_decode_group3_div() {
let d = decoder64();
let inst = d.decode_one(&[0xF7, 0xF1], 0).unwrap();
assert_eq!(inst.mnemonic, "div");
}
#[test]
fn test_decode_group3_test_rm() {
let d = decoder64();
let inst = d
.decode_one(&[0xF7, 0xC0, 0x34, 0x12, 0x00, 0x00], 0)
.unwrap();
assert_eq!(inst.mnemonic, "test");
assert_eq!(inst.operands.len(), 2);
}
#[test]
fn test_decode_group5_inc() {
let d = decoder64();
let inst = d.decode_one(&[0xFF, 0xC0], 0).unwrap();
assert_eq!(inst.mnemonic, "inc");
}
#[test]
fn test_decode_group5_dec() {
let d = decoder64();
let inst = d.decode_one(&[0xFF, 0xC8], 0).unwrap();
assert_eq!(inst.mnemonic, "dec");
}
#[test]
fn test_decode_group5_push() {
let d = decoder64();
let inst = d.decode_one(&[0xFF, 0xF0], 0).unwrap();
assert_eq!(inst.mnemonic, "push");
}
#[test]
fn test_decode_group2_shl_by_cl() {
let d = decoder64();
let inst = d.decode_one(&[0xD3, 0xE0], 0).unwrap();
assert_eq!(inst.mnemonic, "shl");
}
#[test]
fn test_decode_group2_shr_by_cl() {
let d = decoder64();
let inst = d.decode_one(&[0xD3, 0xE8], 0).unwrap();
assert_eq!(inst.mnemonic, "shr");
}
#[test]
fn test_decode_group2_sar_by_cl() {
let d = decoder64();
let inst = d.decode_one(&[0xD3, 0xF8], 0).unwrap();
assert_eq!(inst.mnemonic, "sar");
}
#[test]
fn test_decode_rex_w_mov_imm64() {
let d = decoder64();
let bytes = [0x48, 0xB8, 0xF0, 0xDE, 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12];
let inst = d.decode_one(&bytes, 0).unwrap();
assert_eq!(inst.mnemonic, "mov");
assert_eq!(inst.size, 10);
match &inst.operands[1] {
DecodedOperand::Imm(v) => assert_eq!(*v, 0x123456789ABCDEF0),
_ => panic!("expected Imm"),
}
}
#[test]
fn test_decode_rex_w_add_r64_r64() {
let d = decoder64();
let inst = d.decode_one(&[0x4D, 0x01, 0xC8], 0).unwrap();
assert_eq!(inst.mnemonic, "add");
}
#[test]
fn test_decode_operand_size_override() {
let d = decoder64();
let inst = d.decode_one(&[0x66, 0x83, 0xC0, 0x01], 0).unwrap();
assert_eq!(inst.mnemonic, "add");
assert_eq!(inst.size, 4);
}
#[test]
fn test_decode_modrm_register_mode() {
let d = decoder64();
let inst = d.decode_one(&[0x89, 0xC8], 0).unwrap();
assert_eq!(inst.mnemonic, "mov");
assert_eq!(inst.operands.len(), 2);
}
#[test]
fn test_decode_modrm_memory_disp8() {
let d = decoder64();
let inst = d.decode_one(&[0x8B, 0x41, 0x08], 0).unwrap();
assert_eq!(inst.mnemonic, "mov");
match &inst.operands[0] {
DecodedOperand::Reg { name, .. } => assert_eq!(name, "eax"),
_ => panic!("expected Reg"),
}
match &inst.operands[1] {
DecodedOperand::Mem { base, disp, .. } => {
assert_eq!(base.as_deref(), Some("rcx"));
assert_eq!(*disp, 8);
}
_ => panic!("expected Mem"),
}
}
#[test]
fn test_decode_sib_base_index_scale() {
let d = decoder64();
let inst = d.decode_one(&[0x8B, 0x04, 0x71], 0).unwrap();
assert_eq!(inst.mnemonic, "mov");
match &inst.operands[1] {
DecodedOperand::Mem {
base, index, scale, ..
} => {
assert_eq!(base.as_deref(), Some("rcx"));
assert_eq!(index.as_deref(), Some("rsi"));
assert_eq!(*scale, 2);
}
_ => panic!("expected Mem"),
}
}
#[test]
fn test_decode_sib_disp32_no_base() {
let d = decoder64();
let inst = d
.decode_one(&[0x8B, 0x04, 0xB5, 0x78, 0x56, 0x34, 0x12], 0)
.unwrap();
match &inst.operands[1] {
DecodedOperand::Mem {
base,
index,
scale,
disp,
..
} => {
assert_eq!(*base, None);
assert_eq!(index.as_deref(), Some("rsi"));
assert_eq!(*scale, 4);
assert_eq!(*disp, 0x12345678);
}
_ => panic!("expected Mem"),
}
}
#[test]
fn test_decode_je_rel8() {
let d = decoder64();
let inst = d.decode_one(&[0x74, 0x05], 0).unwrap();
assert_eq!(inst.mnemonic, "je");
assert_eq!(inst.size, 2);
match &inst.operands[0] {
DecodedOperand::RelOffset(v) => assert_eq!(*v, 5),
_ => panic!("expected RelOffset"),
}
}
#[test]
fn test_decode_jne_rel8() {
let d = decoder64();
let inst = d.decode_one(&[0x75, 0xFD], 0).unwrap();
assert_eq!(inst.mnemonic, "jne");
match &inst.operands[0] {
DecodedOperand::RelOffset(v) => assert_eq!(*v, -3),
_ => panic!("expected RelOffset"),
}
}
#[test]
fn test_decode_jmp_rel8() {
let d = decoder64();
let inst = d.decode_one(&[0xEB, 0x02], 0).unwrap();
assert_eq!(inst.mnemonic, "jmp");
}
#[test]
fn test_decode_jmp_rel32() {
let d = decoder64();
let inst = d.decode_one(&[0xE9, 0x00, 0x10, 0x00, 0x00], 0).unwrap();
assert_eq!(inst.mnemonic, "jmp");
assert_eq!(inst.size, 5);
match &inst.operands[0] {
DecodedOperand::RelOffset(v) => assert_eq!(*v, 0x1000),
_ => panic!("expected RelOffset"),
}
}
#[test]
fn test_decode_call_rel32() {
let d = decoder64();
let inst = d.decode_one(&[0xE8, 0x00, 0x01, 0x00, 0x00], 0).unwrap();
assert_eq!(inst.mnemonic, "call");
assert_eq!(inst.size, 5);
}
#[test]
fn test_decode_movzx_rm8() {
let d = decoder64();
let inst = d.decode_one(&[0x0F, 0xB6, 0xC1], 0).unwrap();
assert_eq!(inst.mnemonic, "movzx");
assert_eq!(inst.size, 3);
}
#[test]
fn test_decode_movsx_rm8() {
let d = decoder64();
let inst = d.decode_one(&[0x0F, 0xBE, 0xC1], 0).unwrap();
assert_eq!(inst.mnemonic, "movsx");
assert_eq!(inst.size, 3);
}
#[test]
fn test_decode_bswap() {
let d = decoder64();
let inst = d.decode_one(&[0x0F, 0xC8], 0).unwrap();
assert_eq!(inst.mnemonic, "bswap");
assert_eq!(inst.size, 2);
}
#[test]
fn test_decode_cmove() {
let d = decoder64();
let inst = d.decode_one(&[0x0F, 0x44, 0xC1], 0).unwrap();
assert_eq!(inst.mnemonic, "cmove");
assert_eq!(inst.size, 3);
}
#[test]
fn test_decode_sete() {
let d = decoder64();
let inst = d.decode_one(&[0x0F, 0x94, 0xC0], 0).unwrap();
assert_eq!(inst.mnemonic, "sete");
}
#[test]
fn test_decode_syscall() {
let d = decoder64();
let inst = d.decode_one(&[0x0F, 0x05], 0).unwrap();
assert_eq!(inst.mnemonic, "syscall");
assert_eq!(inst.size, 2);
}
#[test]
fn test_decode_cpuid() {
let d = decoder64();
let inst = d.decode_one(&[0x0F, 0xA2], 0).unwrap();
assert_eq!(inst.mnemonic, "cpuid");
}
#[test]
fn test_decode_cdq() {
let d = decoder64();
let inst = d.decode_one(&[0x99], 0).unwrap();
assert_eq!(inst.mnemonic, "cdq");
assert_eq!(inst.size, 1);
}
#[test]
fn test_decode_lock_prefix() {
let d = decoder64();
let (prefix, pos) = d.decode_prefixes(&[0xF0, 0x83, 0x00, 0x01], 0);
assert!(prefix.has_lock);
assert_eq!(pos, 1);
}
#[test]
fn test_decode_rep_prefix() {
let d = decoder64();
let (prefix, pos) = d.decode_prefixes(&[0xF3, 0x90], 0);
assert!(prefix.has_rep);
assert_eq!(pos, 1);
}
#[test]
fn test_decode_repne_prefix() {
let d = decoder64();
let (prefix, pos) = d.decode_prefixes(&[0xF2, 0x90], 0);
assert!(prefix.has_repne);
assert_eq!(pos, 1);
}
#[test]
fn test_format_instruction_nop() {
let d = decoder64();
let inst = DecodedInst {
opcode: 0x90,
mnemonic: "nop".into(),
operands: vec![],
size: 1,
address: 0,
};
assert_eq!(d.format_instruction(&inst), "nop");
}
#[test]
fn test_format_instruction_mov_reg_reg() {
let d = decoder64();
let inst = DecodedInst {
opcode: 0x89,
mnemonic: "mov".into(),
operands: vec![DecodedOperand::reg("eax", 4), DecodedOperand::reg("ecx", 4)],
size: 2,
address: 0,
};
assert_eq!(d.format_instruction(&inst), "mov %eax, %ecx");
}
#[test]
fn test_format_memory_operand() {
let d = decoder64();
let inst = DecodedInst {
opcode: 0x8B,
mnemonic: "mov".into(),
operands: vec![
DecodedOperand::reg("eax", 4),
DecodedOperand::mem(Some("rcx"), Some("rsi"), 2, 8, 4),
],
size: 3,
address: 0,
};
let formatted = d.format_instruction(&inst);
assert!(formatted.contains("%eax"));
assert!(formatted.contains("%rcx"));
assert!(formatted.contains("%rsi"));
assert!(formatted.contains("2"));
}
#[test]
fn test_decode_all_multiple_instructions() {
let d = decoder64();
let bytes = [0x90, 0xC3, 0x50, 0x58, 0x90];
let insts = d.decode_all(&bytes);
assert_eq!(insts.len(), 5);
assert_eq!(insts[0].mnemonic, "nop");
assert_eq!(insts[1].mnemonic, "ret");
assert_eq!(insts[2].mnemonic, "push");
assert_eq!(insts[3].mnemonic, "pop");
assert_eq!(insts[4].mnemonic, "nop");
}
#[test]
fn test_roundtrip_nop() {
let d = decoder64();
let bytes = [0x90];
let inst = d.decode_one(&bytes, 0).unwrap();
assert_eq!(inst.mnemonic, "nop");
assert_eq!(inst.size, 1);
}
#[test]
fn test_roundtrip_push_rbp() {
let d = decoder64();
let bytes = [0x55];
let inst = d.decode_one(&bytes, 0).unwrap();
assert_eq!(inst.mnemonic, "push");
match &inst.operands[0] {
DecodedOperand::Reg { name, .. } => assert_eq!(name, "rbp"),
_ => panic!("expected Reg"),
}
}
#[test]
fn test_roundtrip_mov_eax_imm32() {
let d = decoder64();
let bytes = [0xB8, 0x42, 0x00, 0x00, 0x00];
let inst = d.decode_one(&bytes, 0).unwrap();
assert_eq!(inst.mnemonic, "mov");
assert_eq!(inst.size, 5);
}
}