use crate::basic_block::BasicBlock;
use crate::rc::Ref;
use super::block::LowLevelILBlock;
use super::operation;
use super::operation::Operation;
use super::VisitorAction;
use super::*;
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, M, F>
where
M: FunctionMutability,
F: FunctionForm,
{
fn kind(&self) -> LowLevelILInstructionKind<'func, M, F>;
fn visit_tree<T>(&self, f: &mut T) -> VisitorAction
where
T: FnMut(&LowLevelILExpression<'func, M, F, ValueExpr>) -> VisitorAction;
}
#[derive(Copy, Clone)]
pub struct LowLevelILInstruction<'func, M, F>
where
M: FunctionMutability,
F: FunctionForm,
{
pub(crate) function: &'func LowLevelILFunction<M, F>,
pub index: LowLevelInstructionIndex,
}
impl<'func, M, F> LowLevelILInstruction<'func, M, F>
where
M: FunctionMutability,
F: FunctionForm,
{
pub fn new(function: &'func LowLevelILFunction<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) }
}
pub fn basic_block(&self) -> Option<Ref<BasicBlock<LowLevelILBlock<'func, M, F>>>> {
self.function.basic_block_containing_index(self.index)
}
}
impl<'func, M> LowLevelILInstruction<'func, M, NonSSA>
where
M: FunctionMutability,
{
pub fn ssa_form(
&self,
ssa: &'func LowLevelILFunction<M, SSA>,
) -> LowLevelILInstruction<'func, M, SSA> {
use binaryninjacore_sys::BNGetLowLevelILSSAInstructionIndex;
let idx = unsafe { BNGetLowLevelILSSAInstructionIndex(self.function.handle, self.index.0) };
LowLevelILInstruction::new(ssa, LowLevelInstructionIndex(idx))
}
}
impl<'func, M> LowLevelILInstruction<'func, M, SSA>
where
M: FunctionMutability,
{
pub fn non_ssa_form(
&self,
non_ssa: &'func LowLevelILFunction<M, NonSSA>,
) -> LowLevelILInstruction<'func, M, NonSSA> {
use binaryninjacore_sys::BNGetLowLevelILNonSSAInstructionIndex;
let idx =
unsafe { BNGetLowLevelILNonSSAInstructionIndex(self.function.handle, self.index.0) };
LowLevelILInstruction::new(non_ssa, LowLevelInstructionIndex(idx))
}
}
impl<M, F> Debug for LowLevelILInstruction<'_, M, F>
where
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, M> InstructionHandler<'func, M, SSA> for LowLevelILInstruction<'func, M, SSA>
where
M: FunctionMutability,
{
fn kind(&self) -> LowLevelILInstructionKind<'func, 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, M, SSA, ValueExpr>) -> VisitorAction,
{
self.kind().visit_sub_expressions(|e| e.visit_tree(f))
}
}
impl<'func, M> InstructionHandler<'func, M, NonSSA> for LowLevelILInstruction<'func, M, NonSSA>
where
M: FunctionMutability,
{
fn kind(&self) -> LowLevelILInstructionKind<'func, M, NonSSA> {
#[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, M, NonSSA, ValueExpr>) -> VisitorAction,
{
self.kind().visit_sub_expressions(|e| e.visit_tree(f))
}
}
#[derive(Debug)]
pub enum LowLevelILInstructionKind<'func, M, F>
where
M: FunctionMutability,
F: FunctionForm,
{
Nop(Operation<'func, M, F, operation::NoArgs>),
SetReg(Operation<'func, M, F, operation::SetReg>),
SetRegSsa(Operation<'func, M, F, operation::SetRegSsa>),
SetRegPartialSsa(Operation<'func, M, F, operation::SetRegPartialSsa>),
SetRegSplit(Operation<'func, M, F, operation::SetRegSplit>),
SetRegSplitSsa(Operation<'func, M, F, operation::SetRegSplitSsa>),
SetFlag(Operation<'func, M, F, operation::SetFlag>),
SetFlagSsa(Operation<'func, M, F, operation::SetFlagSsa>),
Store(Operation<'func, M, F, operation::Store>),
StoreSsa(Operation<'func, M, F, operation::StoreSsa>),
Push(Operation<'func, M, F, operation::UnaryOp>),
RegStackPush(Operation<'func, M, F, operation::RegStackPush>),
Jump(Operation<'func, M, F, operation::Jump>),
JumpTo(Operation<'func, M, F, operation::JumpTo>),
Call(Operation<'func, M, F, operation::Call>),
CallSsa(Operation<'func, M, F, operation::CallSsa>),
TailCall(Operation<'func, M, F, operation::Call>),
TailCallSsa(Operation<'func, M, F, operation::CallSsa>),
Ret(Operation<'func, M, F, operation::Ret>),
NoRet(Operation<'func, M, F, operation::NoArgs>),
If(Operation<'func, M, F, operation::If>),
Goto(Operation<'func, M, F, operation::Goto>),
Syscall(Operation<'func, M, F, operation::Syscall>),
SyscallSsa(Operation<'func, M, F, operation::SyscallSsa>),
Intrinsic(Operation<'func, M, F, operation::Intrinsic>),
Bp(Operation<'func, M, F, operation::NoArgs>),
Trap(Operation<'func, M, F, operation::Trap>),
Undef(Operation<'func, M, F, operation::NoArgs>),
Assert(Operation<'func, M, F, operation::Assert>),
AssertSsa(Operation<'func, M, F, operation::AssertSsa>),
ForceVersion(Operation<'func, M, F, operation::ForceVersion>),
ForceVersionSsa(Operation<'func, M, F, operation::ForceVersionSsa>),
RegPhi(Operation<'func, M, F, operation::RegPhi>),
FlagPhi(Operation<'func, M, F, operation::FlagPhi>),
MemPhi(Operation<'func, M, F, operation::MemPhi>),
Value(LowLevelILExpression<'func, M, F, ValueExpr>),
}
impl<'func, M, F> LowLevelILInstructionKind<'func, M, F>
where
M: FunctionMutability,
F: FunctionForm,
{
pub(crate) unsafe fn from_raw(
function: &'func LowLevelILFunction<M, F>,
expr_index: LowLevelExpressionIndex,
op: BNLowLevelILInstruction,
) -> Self {
use binaryninjacore_sys::BNLowLevelILOperation::*;
match op.operation {
LLIL_NOP => LowLevelILInstructionKind::Nop(Operation::new(function, op, expr_index)),
LLIL_SET_REG => {
LowLevelILInstructionKind::SetReg(Operation::new(function, op, expr_index))
}
LLIL_SET_REG_SSA => {
LowLevelILInstructionKind::SetRegSsa(Operation::new(function, op, expr_index))
}
LLIL_SET_REG_SSA_PARTIAL => LowLevelILInstructionKind::SetRegPartialSsa(
Operation::new(function, op, expr_index),
),
LLIL_SET_REG_SPLIT => {
LowLevelILInstructionKind::SetRegSplit(Operation::new(function, op, expr_index))
}
LLIL_SET_REG_SPLIT_SSA => {
LowLevelILInstructionKind::SetRegSplitSsa(Operation::new(function, op, expr_index))
}
LLIL_SET_FLAG => {
LowLevelILInstructionKind::SetFlag(Operation::new(function, op, expr_index))
}
LLIL_SET_FLAG_SSA => {
LowLevelILInstructionKind::SetFlagSsa(Operation::new(function, op, expr_index))
}
LLIL_STORE => {
LowLevelILInstructionKind::Store(Operation::new(function, op, expr_index))
}
LLIL_STORE_SSA => {
LowLevelILInstructionKind::StoreSsa(Operation::new(function, op, expr_index))
}
LLIL_PUSH => LowLevelILInstructionKind::Push(Operation::new(function, op, expr_index)),
LLIL_REG_STACK_PUSH => {
LowLevelILInstructionKind::RegStackPush(Operation::new(function, op, expr_index))
}
LLIL_JUMP => LowLevelILInstructionKind::Jump(Operation::new(function, op, expr_index)),
LLIL_JUMP_TO => {
LowLevelILInstructionKind::JumpTo(Operation::new(function, op, expr_index))
}
LLIL_CALL | LLIL_CALL_STACK_ADJUST => {
LowLevelILInstructionKind::Call(Operation::new(function, op, expr_index))
}
LLIL_CALL_SSA => {
LowLevelILInstructionKind::CallSsa(Operation::new(function, op, expr_index))
}
LLIL_TAILCALL => {
LowLevelILInstructionKind::TailCall(Operation::new(function, op, expr_index))
}
LLIL_TAILCALL_SSA => {
LowLevelILInstructionKind::TailCallSsa(Operation::new(function, op, expr_index))
}
LLIL_RET => LowLevelILInstructionKind::Ret(Operation::new(function, op, expr_index)),
LLIL_NORET => {
LowLevelILInstructionKind::NoRet(Operation::new(function, op, expr_index))
}
LLIL_IF => LowLevelILInstructionKind::If(Operation::new(function, op, expr_index)),
LLIL_GOTO => LowLevelILInstructionKind::Goto(Operation::new(function, op, expr_index)),
LLIL_SYSCALL => {
LowLevelILInstructionKind::Syscall(Operation::new(function, op, expr_index))
}
LLIL_SYSCALL_SSA => {
LowLevelILInstructionKind::SyscallSsa(Operation::new(function, op, expr_index))
}
LLIL_INTRINSIC | LLIL_INTRINSIC_SSA => {
LowLevelILInstructionKind::Intrinsic(Operation::new(function, op, expr_index))
}
LLIL_BP => LowLevelILInstructionKind::Bp(Operation::new(function, op, expr_index)),
LLIL_TRAP => LowLevelILInstructionKind::Trap(Operation::new(function, op, expr_index)),
LLIL_UNDEF => {
LowLevelILInstructionKind::Undef(Operation::new(function, op, expr_index))
}
LLIL_ASSERT => {
LowLevelILInstructionKind::Assert(Operation::new(function, op, expr_index))
}
LLIL_ASSERT_SSA => {
LowLevelILInstructionKind::AssertSsa(Operation::new(function, op, expr_index))
}
LLIL_FORCE_VER => {
LowLevelILInstructionKind::ForceVersion(Operation::new(function, op, expr_index))
}
LLIL_FORCE_VER_SSA => {
LowLevelILInstructionKind::ForceVersionSsa(Operation::new(function, op, expr_index))
}
LLIL_REG_PHI => {
LowLevelILInstructionKind::RegPhi(Operation::new(function, op, expr_index))
}
LLIL_MEM_PHI => {
LowLevelILInstructionKind::MemPhi(Operation::new(function, op, expr_index))
}
LLIL_FLAG_PHI => {
LowLevelILInstructionKind::FlagPhi(Operation::new(function, op, expr_index))
}
_ => LowLevelILInstructionKind::Value(LowLevelILExpression::new(function, expr_index)),
}
}
fn visit_sub_expressions<T>(&self, mut visitor: T) -> VisitorAction
where
T: FnMut(&LowLevelILExpression<'func, 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()),
SetRegSsa(ref op) => visit!(&op.source_expr()),
SetRegPartialSsa(ref op) => visit!(&op.source_expr()),
SetRegSplit(ref op) => visit!(&op.source_expr()),
SetRegSplitSsa(ref op) => visit!(&op.source_expr()),
SetFlag(ref op) => visit!(&op.source_expr()),
SetFlagSsa(ref op) => visit!(&op.source_expr()),
Store(ref op) => {
visit!(&op.dest_expr());
visit!(&op.source_expr());
}
StoreSsa(ref op) => {
visit!(&op.dest_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()),
SyscallSsa(ref op) => {
visit!(&op.output_expr());
visit!(&op.param_expr());
visit!(&op.stack_expr());
}
Call(ref op) | TailCall(ref op) => visit!(&op.target()),
CallSsa(ref op) | TailCallSsa(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(_) | Assert(_)
| AssertSsa(_) | ForceVersion(_) | ForceVersionSsa(_) | RegPhi(_) | FlagPhi(_)
| MemPhi(_) => {}
}
VisitorAction::Sibling
}
}