1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
use iced_x86::Code;
/// Architecture-neutral decoded instruction.
///
/// Wraps the native instruction type for each supported ISA so that hooks,
/// display, tracing, and the run loop can operate on a single type without
/// caring which architecture is active.
///
/// The enum is `Copy` (both variants are small value types) and imposes no
/// heap allocation. At runtime exactly one variant is ever constructed, so
/// every `match` is a perfectly-predicted branch — effectively zero cost.
#[derive(Clone, Copy)]
pub enum DecodedInstruction {
X86(iced_x86::Instruction),
AArch64(yaxpeax_arm::armv8::a64::Instruction),
}
impl DecodedInstruction {
/// Address of the instruction (IP / PC).
///
/// For x86 this comes from `ins.ip()` which the decoder sets.
/// For aarch64 the decoder doesn't embed the PC in the instruction,
/// so callers must use the PC value they already know. We store 0
/// here as a sentinel; the run loop should prefer `self.pc()` for
/// the canonical address.
#[inline]
pub fn address(&self) -> u64 {
match self {
DecodedInstruction::X86(ins) => ins.ip(),
DecodedInstruction::AArch64(_) => 0, // caller should use emu.pc()
}
}
/// Size of the encoded instruction in bytes.
#[inline]
pub fn size(&self) -> usize {
match self {
DecodedInstruction::X86(ins) => ins.len(),
DecodedInstruction::AArch64(_) => 4, // fixed-width ISA
}
}
/// Returns `true` if this is a return instruction.
#[inline]
pub fn is_return(&self) -> bool {
match self {
DecodedInstruction::X86(ins) => matches!(
ins.code(),
Code::Retnw | Code::Retnd | Code::Retnq
),
DecodedInstruction::AArch64(ins) => {
ins.opcode == yaxpeax_arm::armv8::a64::Opcode::RET
}
}
}
/// Unwrap as an x86 instruction. Panics on aarch64.
#[inline]
pub fn as_x86(&self) -> &iced_x86::Instruction {
match self {
DecodedInstruction::X86(ins) => ins,
DecodedInstruction::AArch64(_) => panic!("as_x86() called on aarch64 instruction"),
}
}
/// Unwrap as an aarch64 instruction. Panics on x86.
#[inline]
pub fn as_aarch64(&self) -> &yaxpeax_arm::armv8::a64::Instruction {
match self {
DecodedInstruction::AArch64(ins) => ins,
DecodedInstruction::X86(_) => panic!("as_aarch64() called on x86 instruction"),
}
}
/// Returns `true` if this is an x86 instruction.
#[inline]
pub fn is_x86(&self) -> bool {
matches!(self, DecodedInstruction::X86(_))
}
/// Returns `true` if this is an aarch64 instruction.
#[inline]
pub fn is_aarch64(&self) -> bool {
matches!(self, DecodedInstruction::AArch64(_))
}
}
impl std::fmt::Debug for DecodedInstruction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
DecodedInstruction::X86(ins) => write!(f, "X86({:?})", ins),
DecodedInstruction::AArch64(ins) => write!(f, "AArch64({})", ins),
}
}
}