use super::operation;
use super::operation::Operation;
use super::VisitorAction;
use super::*;
use crate::architecture::Architecture;
use binaryninjacore_sys::BNGetLowLevelILByIndex;
use binaryninjacore_sys::BNGetLowLevelILIndexForInstruction;
use binaryninjacore_sys::BNLowLevelILInstruction;
use std::fmt::{Debug, Display, Formatter};
#[repr(transparent)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct LowLevelInstructionIndex(pub usize);
impl LowLevelInstructionIndex {
pub fn next(&self) -> Self {
Self(self.0 + 1)
}
}
impl From<usize> for LowLevelInstructionIndex {
fn from(index: usize) -> Self {
Self(index)
}
}
impl From<u64> for LowLevelInstructionIndex {
fn from(index: u64) -> Self {
Self(index as usize)
}
}
impl Display for LowLevelInstructionIndex {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_fmt(format_args!("{}", self.0))
}
}
pub trait InstructionHandler<'func, A, M, F>
where
A: Architecture,
M: FunctionMutability,
F: FunctionForm,
{
fn kind(&self) -> LowLevelILInstructionKind<'func, A, M, F>;
fn visit_tree<T>(&self, f: &mut T) -> VisitorAction
where
T: FnMut(&LowLevelILExpression<'func, A, M, F, ValueExpr>) -> VisitorAction;
}
pub struct LowLevelILInstruction<'func, A, M, F>
where
A: 'func + Architecture,
M: FunctionMutability,
F: FunctionForm,
{
pub(crate) function: &'func LowLevelILFunction<A, M, F>,
pub index: LowLevelInstructionIndex,
}
impl<'func, A, M, F> LowLevelILInstruction<'func, A, M, F>
where
A: 'func + Architecture,
M: FunctionMutability,
F: FunctionForm,
{
pub fn new(
function: &'func LowLevelILFunction<A, M, F>,
index: LowLevelInstructionIndex,
) -> Self {
Self { function, index }
}
pub fn address(&self) -> u64 {
self.into_raw().address
}
pub fn expr_idx(&self) -> LowLevelExpressionIndex {
let idx = unsafe { BNGetLowLevelILIndexForInstruction(self.function.handle, self.index.0) };
LowLevelExpressionIndex(idx)
}
pub fn into_raw(&self) -> BNLowLevelILInstruction {
unsafe { BNGetLowLevelILByIndex(self.function.handle, self.expr_idx().0) }
}
}
impl<'func, A, M, F> Debug for LowLevelILInstruction<'func, A, M, F>
where
A: 'func + Architecture,
M: FunctionMutability,
F: FunctionForm,
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("Instruction")
.field("index", &self.index)
.field("expr_idx", &self.expr_idx())
.field("address", &self.address())
.finish()
}
}
impl<'func, A, M> InstructionHandler<'func, A, M, SSA> for LowLevelILInstruction<'func, A, M, SSA>
where
A: 'func + Architecture,
M: FunctionMutability,
{
fn kind(&self) -> LowLevelILInstructionKind<'func, A, M, SSA> {
#[allow(unused_imports)]
use binaryninjacore_sys::BNLowLevelILOperation::*;
let raw_op = self.into_raw();
#[allow(clippy::match_single_binding)]
match raw_op.operation {
_ => unsafe {
LowLevelILInstructionKind::from_raw(self.function, self.expr_idx(), raw_op)
},
}
}
fn visit_tree<T>(&self, f: &mut T) -> VisitorAction
where
T: FnMut(&LowLevelILExpression<'func, A, M, SSA, ValueExpr>) -> VisitorAction,
{
self.kind().visit_sub_expressions(|e| e.visit_tree(f))
}
}
impl<'func, A, M> InstructionHandler<'func, A, M, NonSSA<LiftedNonSSA>>
for LowLevelILInstruction<'func, A, M, NonSSA<LiftedNonSSA>>
where
A: 'func + Architecture,
M: FunctionMutability,
{
fn kind(&self) -> LowLevelILInstructionKind<'func, A, M, NonSSA<LiftedNonSSA>> {
#[allow(unused_imports)]
use binaryninjacore_sys::BNLowLevelILOperation::*;
let raw_op = self.into_raw();
#[allow(clippy::match_single_binding)]
match raw_op.operation {
_ => unsafe {
LowLevelILInstructionKind::from_raw(self.function, self.expr_idx(), raw_op)
},
}
}
fn visit_tree<T>(&self, f: &mut T) -> VisitorAction
where
T: FnMut(
&LowLevelILExpression<'func, A, M, NonSSA<LiftedNonSSA>, ValueExpr>,
) -> VisitorAction,
{
self.kind().visit_sub_expressions(|e| e.visit_tree(f))
}
}
impl<'func, A, M> InstructionHandler<'func, A, M, NonSSA<RegularNonSSA>>
for LowLevelILInstruction<'func, A, M, NonSSA<RegularNonSSA>>
where
A: 'func + Architecture,
M: FunctionMutability,
{
fn kind(&self) -> LowLevelILInstructionKind<'func, A, M, NonSSA<RegularNonSSA>> {
#[allow(unused_imports)]
use binaryninjacore_sys::BNLowLevelILOperation::*;
let raw_op = self.into_raw();
#[allow(clippy::match_single_binding)]
match raw_op.operation {
_ => unsafe {
LowLevelILInstructionKind::from_raw(self.function, self.expr_idx(), raw_op)
},
}
}
fn visit_tree<T>(&self, f: &mut T) -> VisitorAction
where
T: FnMut(
&LowLevelILExpression<'func, A, M, NonSSA<RegularNonSSA>, ValueExpr>,
) -> VisitorAction,
{
self.kind().visit_sub_expressions(|e| e.visit_tree(f))
}
}
#[derive(Debug)]
pub enum LowLevelILInstructionKind<'func, A, M, F>
where
A: 'func + Architecture,
M: FunctionMutability,
F: FunctionForm,
{
Nop(Operation<'func, A, M, F, operation::NoArgs>),
SetReg(Operation<'func, A, M, F, operation::SetReg>),
SetRegSplit(Operation<'func, A, M, F, operation::SetRegSplit>),
SetFlag(Operation<'func, A, M, F, operation::SetFlag>),
Store(Operation<'func, A, M, F, operation::Store>),
Push(Operation<'func, A, M, F, operation::UnaryOp>),
RegStackPush(Operation<'func, A, M, F, operation::RegStackPush>),
Jump(Operation<'func, A, M, F, operation::Jump>),
JumpTo(Operation<'func, A, M, F, operation::JumpTo>),
Call(Operation<'func, A, M, F, operation::Call>),
TailCall(Operation<'func, A, M, F, operation::Call>),
Ret(Operation<'func, A, M, F, operation::Ret>),
NoRet(Operation<'func, A, M, F, operation::NoArgs>),
If(Operation<'func, A, M, F, operation::If>),
Goto(Operation<'func, A, M, F, operation::Goto>),
Syscall(Operation<'func, A, M, F, operation::Syscall>),
Intrinsic(Operation<'func, A, M, F, operation::Intrinsic>),
Bp(Operation<'func, A, M, F, operation::NoArgs>),
Trap(Operation<'func, A, M, F, operation::Trap>),
Undef(Operation<'func, A, M, F, operation::NoArgs>),
Value(LowLevelILExpression<'func, A, M, F, ValueExpr>),
}
impl<'func, A, M, F> LowLevelILInstructionKind<'func, A, M, F>
where
A: Architecture,
M: FunctionMutability,
F: FunctionForm,
{
pub(crate) unsafe fn from_raw(
function: &'func LowLevelILFunction<A, M, F>,
expr_index: LowLevelExpressionIndex,
op: BNLowLevelILInstruction,
) -> Self {
use binaryninjacore_sys::BNLowLevelILOperation::*;
match op.operation {
LLIL_NOP => LowLevelILInstructionKind::Nop(Operation::new(function, op)),
LLIL_SET_REG | LLIL_SET_REG_SSA => {
LowLevelILInstructionKind::SetReg(Operation::new(function, op))
}
LLIL_SET_REG_SPLIT | LLIL_SET_REG_SPLIT_SSA => {
LowLevelILInstructionKind::SetRegSplit(Operation::new(function, op))
}
LLIL_SET_FLAG | LLIL_SET_FLAG_SSA => {
LowLevelILInstructionKind::SetFlag(Operation::new(function, op))
}
LLIL_STORE | LLIL_STORE_SSA => {
LowLevelILInstructionKind::Store(Operation::new(function, op))
}
LLIL_PUSH => LowLevelILInstructionKind::Push(Operation::new(function, op)),
LLIL_REG_STACK_PUSH => {
LowLevelILInstructionKind::RegStackPush(Operation::new(function, op))
}
LLIL_JUMP => LowLevelILInstructionKind::Jump(Operation::new(function, op)),
LLIL_JUMP_TO => LowLevelILInstructionKind::JumpTo(Operation::new(function, op)),
LLIL_CALL | LLIL_CALL_STACK_ADJUST | LLIL_CALL_SSA => {
LowLevelILInstructionKind::Call(Operation::new(function, op))
}
LLIL_TAILCALL | LLIL_TAILCALL_SSA => {
LowLevelILInstructionKind::TailCall(Operation::new(function, op))
}
LLIL_RET => LowLevelILInstructionKind::Ret(Operation::new(function, op)),
LLIL_NORET => LowLevelILInstructionKind::NoRet(Operation::new(function, op)),
LLIL_IF => LowLevelILInstructionKind::If(Operation::new(function, op)),
LLIL_GOTO => LowLevelILInstructionKind::Goto(Operation::new(function, op)),
LLIL_SYSCALL | LLIL_SYSCALL_SSA => {
LowLevelILInstructionKind::Syscall(Operation::new(function, op))
}
LLIL_INTRINSIC | LLIL_INTRINSIC_SSA => {
LowLevelILInstructionKind::Intrinsic(Operation::new(function, op))
}
LLIL_BP => LowLevelILInstructionKind::Bp(Operation::new(function, op)),
LLIL_TRAP => LowLevelILInstructionKind::Trap(Operation::new(function, op)),
LLIL_UNDEF => LowLevelILInstructionKind::Undef(Operation::new(function, op)),
_ => LowLevelILInstructionKind::Value(LowLevelILExpression::new(function, expr_index)),
}
}
fn visit_sub_expressions<T>(&self, mut visitor: T) -> VisitorAction
where
T: FnMut(&LowLevelILExpression<'func, A, M, F, ValueExpr>) -> VisitorAction,
{
use LowLevelILInstructionKind::*;
macro_rules! visit {
($expr:expr) => {
if let VisitorAction::Halt = visitor($expr) {
return VisitorAction::Halt;
}
};
}
match self {
SetReg(ref op) => visit!(&op.source_expr()),
SetRegSplit(ref op) => visit!(&op.source_expr()),
SetFlag(ref op) => visit!(&op.source_expr()),
Store(ref op) => {
visit!(&op.dest_mem_expr());
visit!(&op.source_expr());
}
Push(ref op) => visit!(&op.operand()),
RegStackPush(ref op) => visit!(&op.source_expr()),
Jump(ref op) => visit!(&op.target()),
JumpTo(ref op) => visit!(&op.target()),
Call(ref op) | TailCall(ref op) => visit!(&op.target()),
Ret(ref op) => visit!(&op.target()),
If(ref op) => visit!(&op.condition()),
Intrinsic(ref _op) => {
}
Value(e) => visit!(e),
Nop(_) | NoRet(_) | Goto(_) | Syscall(_) | Bp(_) | Trap(_) | Undef(_) => {}
}
VisitorAction::Sibling
}
}