summaryrefslogtreecommitdiff
path: root/rust/src/llil/instruction.rs
diff options
context:
space:
mode:
Diffstat (limited to 'rust/src/llil/instruction.rs')
-rw-r--r--rust/src/llil/instruction.rs175
1 files changed, 175 insertions, 0 deletions
diff --git a/rust/src/llil/instruction.rs b/rust/src/llil/instruction.rs
new file mode 100644
index 00000000..4d1554df
--- /dev/null
+++ b/rust/src/llil/instruction.rs
@@ -0,0 +1,175 @@
+use binaryninjacore_sys::BNGetLowLevelILByIndex;
+use binaryninjacore_sys::BNGetLowLevelILIndexForInstruction;
+use binaryninjacore_sys::BNLowLevelILInstruction;
+
+use std::marker::PhantomData;
+
+use super::*;
+use super::operation;
+use super::operation::Operation;
+
+use crate::architecture::Architecture;
+
+pub struct Instruction<'func, A, M, F>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub(crate) function: &'func Function<A, M, F>,
+ pub(crate) instr_idx: usize,
+}
+
+fn common_info<'func, A, M, F>(function: &'func Function<A, M, F>, op: BNLowLevelILInstruction)
+ -> Option<InstrInfo<'func, A, M, F>>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ use binaryninjacore_sys::BNLowLevelILOperation::*;
+
+ match op.operation {
+ LLIL_NOP => InstrInfo::Nop(Operation::new(function, op)).into(),
+ LLIL_JUMP => InstrInfo::Jump(Operation::new(function, op)).into(),
+ LLIL_JUMP_TO => InstrInfo::JumpTo(Operation::new(function, op)).into(),
+ LLIL_RET => InstrInfo::Ret(Operation::new(function, op)).into(),
+ LLIL_NORET => InstrInfo::NoRet(Operation::new(function, op)).into(),
+ LLIL_IF => InstrInfo::If(Operation::new(function, op)).into(),
+ LLIL_GOTO => InstrInfo::Goto(Operation::new(function, op)).into(),
+ LLIL_BP => InstrInfo::Bp(Operation::new(function, op)).into(),
+ LLIL_TRAP => InstrInfo::Trap(Operation::new(function, op)).into(),
+ LLIL_UNDEF => InstrInfo::Undef(Operation::new(function, op)).into(),
+ _ => None,
+ }
+}
+
+use super::VisitorAction;
+
+macro_rules! visit {
+ ($f:expr, $($e:expr),*) => {
+ if let VisitorAction::Halt = $f($($e,)*) {
+ return VisitorAction::Halt;
+ }
+ }
+}
+
+fn common_visit<'func, A, M, F, CB>(info: &InstrInfo<'func, A, M, F>, f: &mut CB)
+ -> VisitorAction
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+ CB: FnMut(&Expression<'func, A, M, F, ValueExpr>) -> VisitorAction,
+{
+ use self::InstrInfo::*;
+
+ match *info {
+ Jump(ref op) => visit!(f, &op.target()),
+ JumpTo(ref op) => visit!(f, &op.target()),
+ Ret(ref op) => visit!(f, &op.target()),
+ If(ref op) => visit!(f, &op.condition()),
+ Value(ref e, _) => visit!(f, e),
+ _ => {},
+ };
+
+ VisitorAction::Sibling
+}
+
+impl<'func, A, M, V> Instruction<'func, A, M, NonSSA<V>>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ V: NonSSAVariant,
+{
+ pub fn info(&self) -> InstrInfo<'func, A, M, NonSSA<V>> {
+ use binaryninjacore_sys::BNLowLevelILOperation::*;
+
+ let expr_idx = unsafe { BNGetLowLevelILIndexForInstruction(self.function.handle, self.instr_idx) };
+ let op = unsafe { BNGetLowLevelILByIndex(self.function.handle, expr_idx) };
+
+ match op.operation {
+ LLIL_SET_REG => InstrInfo::SetReg(Operation::new(self.function, op)),
+ LLIL_SET_REG_SPLIT => InstrInfo::SetRegSplit(Operation::new(self.function, op)),
+ LLIL_SET_FLAG => InstrInfo::SetFlag(Operation::new(self.function, op)),
+ LLIL_STORE => InstrInfo::Store(Operation::new(self.function, op)),
+ LLIL_PUSH => InstrInfo::Push(Operation::new(self.function, op)),
+ LLIL_CALL |
+ LLIL_CALL_STACK_ADJUST => InstrInfo::Call(Operation::new(self.function, op)),
+ LLIL_SYSCALL => InstrInfo::Syscall(Operation::new(self.function, op)),
+ _ => {
+ common_info(self.function, op).unwrap_or_else(|| {
+ // Hopefully this is a bare value. If it isn't (expression
+ // from wrong function form or similar) it won't really cause
+ // any problems as it'll come back as undefined when queried.
+ let expr = Expression {
+ function: self.function,
+ expr_idx: expr_idx,
+ _ty: PhantomData,
+ };
+
+ let info = unsafe { expr.info_from_op(op) };
+
+ InstrInfo::Value(expr, info)
+ })
+ }
+ }
+ }
+
+ pub fn visit_tree<F>(&self, f: &mut F) -> VisitorAction
+ where
+ F: FnMut(&Expression<'func, A, M, NonSSA<V>, ValueExpr>, &ExprInfo<'func, A, M, NonSSA<V>>) -> VisitorAction,
+ {
+ use self::InstrInfo::*;
+ let info = self.info();
+
+ let fb = &mut |e: &Expression<'func, A, M, NonSSA<V>, ValueExpr>| e.visit_tree(f);
+
+ match info {
+ SetReg(ref op) => visit!(fb, &op.source_expr()),
+ SetRegSplit(ref op) => visit!(fb, &op.source_expr()),
+ SetFlag(ref op) => visit!(fb, &op.source_expr()),
+ Store(ref op) => {
+ visit!(fb, &op.dest_mem_expr());
+ visit!(fb, &op.source_expr());
+ }
+ Push(ref op) => visit!(fb, &op.operand()),
+ Call(ref op) => visit!(fb, &op.target()),
+ _ => visit!(common_visit, &info, fb),
+ }
+
+ VisitorAction::Sibling
+ }
+}
+
+pub enum InstrInfo<'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>), // TODO needs a real op
+
+ Jump(Operation<'func, A, M, F, operation::Jump>),
+ JumpTo(Operation<'func, A, M, F, operation::JumpTo>),
+
+ Call(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>),
+ 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(Expression<'func, A, M, F, ValueExpr>, ExprInfo<'func, A, M, F>),
+}