summaryrefslogtreecommitdiff
path: root/rust/src/llil
diff options
context:
space:
mode:
authorRyan Snyder <ryan@vector35.com>2021-01-21 18:27:48 +0000
committerKyleMiles <krm504@nyu.edu>2021-01-21 19:06:55 +0000
commitd3140edec185f47235b9e4642bdd56d6c585a341 (patch)
treea61859c29e4e3539daea2b761bb1439d942beaf4 /rust/src/llil
parentc0ddbf0c76d3f1bb7a2b2024f749afc8b9482575 (diff)
This is a combination of 23 commits, the work of Ryan Snyder:
Initial fresh repo Add support for recent calling convention API updates and folds the binaryninjacore-sys crate directly into this one. Add support for auto function analysis suppression Finish moving binaryninjacore-sys back into this crate Update for Symbol/Segment core API changes Update for Symbol API cleanup api: advance submodule reference, support Token changes arch/lifting: support for flags in custom architectures arch/lifting: support default flag write behaviors, handle more ops build: enable headless binary support on MacOS via evil hack bv: add BinaryView wrapper support, remove wrong comment api: update to latest binja dev branch support deps: bump dep versions rust: bump to 2018 edition api: bump to avoid cargo submodule brokenness build: improve binaryninja path detection; enable linux linkhack bv: stub for bv load settings arch: fix flag related crash, minor llil update api: update for recent changes macos: disable linkhack briefly
Diffstat (limited to 'rust/src/llil')
-rw-r--r--rust/src/llil/block.rs91
-rw-r--r--rust/src/llil/expression.rs816
-rw-r--r--rust/src/llil/function.rs198
-rw-r--r--rust/src/llil/instruction.rs175
-rw-r--r--rust/src/llil/lifting.rs1292
-rw-r--r--rust/src/llil/mod.rs80
-rw-r--r--rust/src/llil/operation.rs765
7 files changed, 3417 insertions, 0 deletions
diff --git a/rust/src/llil/block.rs b/rust/src/llil/block.rs
new file mode 100644
index 00000000..543ab670
--- /dev/null
+++ b/rust/src/llil/block.rs
@@ -0,0 +1,91 @@
+use std::ops::Range;
+
+use crate::architecture::Architecture;
+use crate::basicblock::{BasicBlock, BlockContext};
+
+use super::*;
+
+pub struct BlockIter<'func, A, M, F>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ function: &'func Function<A, M, F>,
+ range: Range<u64>,
+}
+
+impl<'func, A, M, F> Iterator for BlockIter<'func, A, M, F>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ type Item = Instruction<'func, A, M, F>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ self.range.next().map(|i| Instruction {
+ function: self.function,
+ instr_idx: i as usize,
+ })
+ }
+}
+
+
+
+pub struct Block<'func, A, M, F>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub(crate) function: &'func Function<A, M, F>,
+}
+
+impl<'func, A, M, F> fmt::Debug for Block<'func, A, M, F>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "llil_bb {:?}", self.function)
+ }
+}
+
+impl<'func, A, M, F> BlockContext for Block<'func, A, M, F>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ type Iter = BlockIter<'func, A, M, F>;
+ type Instruction = Instruction<'func, A, M, F>;
+
+ fn start(&self, block: &BasicBlock<Self>) -> Instruction<'func, A, M, F> {
+ Instruction {
+ function: self.function,
+ instr_idx: block.raw_start() as usize,
+ }
+ }
+
+ fn iter(&self, block: &BasicBlock<Self>) -> BlockIter<'func, A, M, F> {
+ BlockIter {
+ function: self.function,
+ range: block.raw_start() .. block.raw_end(),
+ }
+ }
+}
+
+impl<'func, A, M, F> Clone for Block<'func, A, M, F>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn clone(&self) -> Self {
+ Block { function: self.function }
+ }
+}
+
+
diff --git a/rust/src/llil/expression.rs b/rust/src/llil/expression.rs
new file mode 100644
index 00000000..1e1edda4
--- /dev/null
+++ b/rust/src/llil/expression.rs
@@ -0,0 +1,816 @@
+use binaryninjacore_sys::BNGetLowLevelILByIndex;
+use binaryninjacore_sys::BNLowLevelILInstruction;
+
+use std::marker::PhantomData;
+use std::fmt;
+
+use super::*;
+use super::operation;
+use super::operation::Operation;
+
+use crate::architecture::Architecture;
+use crate::architecture::RegisterInfo;
+
+// used as a marker for Expressions that can produce a value
+#[derive(Copy, Clone, Debug)]
+pub struct ValueExpr;
+
+// used as a marker for Expressions that can not produce a value
+#[derive(Copy, Clone, Debug)]
+pub struct VoidExpr;
+
+pub trait ExpressionResultType: 'static {}
+impl ExpressionResultType for ValueExpr {}
+impl ExpressionResultType for VoidExpr {}
+
+pub struct Expression<'func, A, M, F, R>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+ R: ExpressionResultType,
+{
+ pub(crate) function: &'func Function<A, M, F>,
+ pub(crate) expr_idx: usize,
+
+ // tag the 'return' type of this expression
+ pub(crate) _ty: PhantomData<R>,
+}
+
+impl<'func, A, M, F, R> Expression<'func, A, M, F, R>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+ R: ExpressionResultType,
+{
+ pub fn index(&self) -> usize {
+ self.expr_idx
+ }
+}
+
+impl<'func, A, M, V> fmt::Debug for Expression<'func, A, M, NonSSA<V>, ValueExpr>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ V: NonSSAVariant,
+{
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let op_info = self.info();
+ write!(f, "<expr {}: {:?}>", self.expr_idx, op_info)
+ }
+}
+
+fn common_info<'func, A, M, F>(function: &'func Function<A, M, F>, op: BNLowLevelILInstruction)
+ -> ExprInfo<'func, A, M, F>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ use binaryninjacore_sys::BNLowLevelILOperation::*;
+
+ match op.operation {
+ LLIL_CONST => ExprInfo::Const(Operation::new(function, op)),
+ LLIL_CONST_PTR => ExprInfo::ConstPtr(Operation::new(function, op)),
+
+ LLIL_ADD => ExprInfo::Add(Operation::new(function, op)),
+ LLIL_ADC => ExprInfo::Adc(Operation::new(function, op)),
+ LLIL_SUB => ExprInfo::Sub(Operation::new(function, op)),
+ LLIL_SBB => ExprInfo::Sbb(Operation::new(function, op)),
+ LLIL_AND => ExprInfo::And(Operation::new(function, op)),
+ LLIL_OR => ExprInfo::Or (Operation::new(function, op)),
+ LLIL_XOR => ExprInfo::Xor(Operation::new(function, op)),
+ LLIL_LSL => ExprInfo::Lsl(Operation::new(function, op)),
+ LLIL_LSR => ExprInfo::Lsr(Operation::new(function, op)),
+ LLIL_ASR => ExprInfo::Asr(Operation::new(function, op)),
+ LLIL_ROL => ExprInfo::Rol(Operation::new(function, op)),
+ LLIL_RLC => ExprInfo::Rlc(Operation::new(function, op)),
+ LLIL_ROR => ExprInfo::Ror(Operation::new(function, op)),
+ LLIL_RRC => ExprInfo::Rrc(Operation::new(function, op)),
+ LLIL_MUL => ExprInfo::Mul(Operation::new(function, op)),
+
+ LLIL_MULU_DP => ExprInfo::MuluDp(Operation::new(function, op)),
+ LLIL_MULS_DP => ExprInfo::MulsDp(Operation::new(function, op)),
+
+ LLIL_DIVU => ExprInfo::Divu(Operation::new(function, op)),
+ LLIL_DIVS => ExprInfo::Divs(Operation::new(function, op)),
+
+ LLIL_DIVU_DP => ExprInfo::DivuDp(Operation::new(function, op)),
+ LLIL_DIVS_DP => ExprInfo::DivsDp(Operation::new(function, op)),
+
+ LLIL_MODU => ExprInfo::Modu(Operation::new(function, op)),
+ LLIL_MODS => ExprInfo::Mods(Operation::new(function, op)),
+
+ LLIL_MODU_DP => ExprInfo::ModuDp(Operation::new(function, op)),
+ LLIL_MODS_DP => ExprInfo::ModsDp(Operation::new(function, op)),
+
+ LLIL_NEG => ExprInfo::Neg(Operation::new(function, op)),
+ LLIL_NOT => ExprInfo::Not(Operation::new(function, op)),
+
+ LLIL_SX => ExprInfo::Sx(Operation::new(function, op)),
+ LLIL_ZX => ExprInfo::Zx(Operation::new(function, op)),
+ LLIL_LOW_PART => ExprInfo::LowPart(Operation::new(function, op)),
+
+ LLIL_CMP_E => ExprInfo::CmpE(Operation::new(function, op)),
+ LLIL_CMP_NE => ExprInfo::CmpNe(Operation::new(function, op)),
+ LLIL_CMP_SLT => ExprInfo::CmpSlt(Operation::new(function, op)),
+ LLIL_CMP_ULT => ExprInfo::CmpUlt(Operation::new(function, op)),
+ LLIL_CMP_SLE => ExprInfo::CmpSle(Operation::new(function, op)),
+ LLIL_CMP_ULE => ExprInfo::CmpUle(Operation::new(function, op)),
+ LLIL_CMP_SGE => ExprInfo::CmpSge(Operation::new(function, op)),
+ LLIL_CMP_UGE => ExprInfo::CmpUge(Operation::new(function, op)),
+ LLIL_CMP_SGT => ExprInfo::CmpSgt(Operation::new(function, op)),
+ LLIL_CMP_UGT => ExprInfo::CmpUgt(Operation::new(function, op)),
+
+ LLIL_BOOL_TO_INT => ExprInfo::BoolToInt(Operation::new(function, op)),
+
+ LLIL_UNIMPL => ExprInfo::Unimpl(Operation::new(function, op)),
+ LLIL_UNIMPL_MEM => ExprInfo::UnimplMem(Operation::new(function, op)),
+
+ // TODO TEST_BIT ADD_OVERFLOW
+ _ => {
+ #[cfg(debug_assertions)]
+ {
+ error!("Got unexpected operation {:?} in value expr at 0x{:x}",
+ op.operation, op.address);
+ }
+
+ ExprInfo::Undef(Operation::new(function, op))
+ }
+ }
+}
+
+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: &ExprInfo<'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::ExprInfo::*;
+
+ match *info {
+ CmpE(ref op) | CmpNe(ref op) |
+ CmpSlt(ref op) | CmpUlt(ref op) |
+ CmpSle(ref op) | CmpUle(ref op) |
+ CmpSge(ref op) | CmpUge(ref op) |
+ CmpSgt(ref op) | CmpUgt(ref op) => {
+ visit!(f, &op.left());
+ visit!(f, &op.right());
+ }
+
+ Adc(ref op) |
+ Sbb(ref op) |
+ Rlc(ref op) |
+ Rrc(ref op) => {
+ visit!(f, &op.left());
+ visit!(f, &op.right());
+ visit!(f, &op.carry());
+ }
+
+ Add(ref op) |
+ Sub(ref op) |
+ And(ref op) |
+ Or (ref op) |
+ Xor(ref op) |
+ Lsl(ref op) |
+ Lsr(ref op) |
+ Asr(ref op) |
+ Rol(ref op) |
+ Ror(ref op) |
+ Mul(ref op) |
+ MulsDp(ref op) |
+ MuluDp(ref op) |
+ Divu(ref op) |
+ Divs(ref op) |
+ Modu(ref op) |
+ Mods(ref op) => {
+ visit!(f, &op.left());
+ visit!(f, &op.right());
+ }
+
+ DivuDp(ref op) |
+ DivsDp(ref op) |
+ ModuDp(ref op) |
+ ModsDp(ref op) => {
+ visit!(f, &op.high());
+ visit!(f, &op.low());
+ visit!(f, &op.right());
+ }
+
+ Neg(ref op) |
+ Not(ref op) |
+ Sx(ref op) |
+ Zx(ref op) |
+ LowPart(ref op) |
+ BoolToInt(ref op) => {
+ visit!(f, &op.operand());
+ }
+
+ UnimplMem(ref op) => {
+ visit!(f, &op.mem_expr());
+ }
+
+ _ => {}
+ };
+
+ VisitorAction::Sibling
+}
+
+impl<'func, A, M, V> Expression<'func, A, M, NonSSA<V>, ValueExpr>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ V: NonSSAVariant,
+{
+ pub(crate) unsafe fn info_from_op(&self, op: BNLowLevelILInstruction) -> ExprInfo<'func, A, M, NonSSA<V>> {
+ use binaryninjacore_sys::BNLowLevelILOperation::*;
+
+ match op.operation {
+ LLIL_LOAD => ExprInfo::Load(Operation::new(self.function, op)),
+ LLIL_POP => ExprInfo::Pop(Operation::new(self.function, op)),
+ LLIL_REG => ExprInfo::Reg(Operation::new(self.function, op)),
+ LLIL_FLAG => ExprInfo::Flag(Operation::new(self.function, op)),
+ LLIL_FLAG_BIT => ExprInfo::FlagBit(Operation::new(self.function, op)),
+ LLIL_FLAG_COND => ExprInfo::FlagCond(Operation::new(self.function, op)), // TODO lifted only
+ LLIL_FLAG_GROUP => ExprInfo::FlagGroup(Operation::new(self.function, op)), // TODO lifted only
+ _ => common_info(self.function, op),
+ }
+ }
+
+ pub fn info(&self) -> ExprInfo<'func, A, M, NonSSA<V>> {
+ unsafe {
+ let op = BNGetLowLevelILByIndex(self.function.handle, self.expr_idx);
+ self.info_from_op(op)
+ }
+ }
+
+ pub fn visit_tree<F>(&self, f: &mut F) -> VisitorAction
+ where
+ F: FnMut(&Self, &ExprInfo<'func, A, M, NonSSA<V>>) -> VisitorAction,
+ {
+ use self::ExprInfo::*;
+
+ let info = self.info();
+
+ match f(self, &info) {
+ VisitorAction::Descend => {},
+ action => return action,
+ };
+
+ match info {
+ Load(ref op) => visit!(Self::visit_tree, &op.source_mem_expr(), f),
+ _ => {
+ let mut fb = |e: &Self| e.visit_tree(f);
+ visit!(common_visit, &info, &mut fb);
+ }
+ };
+
+ VisitorAction::Sibling
+ }
+}
+
+impl<'func, A, M> Expression<'func, A, M, SSA, ValueExpr>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+{
+ pub(crate) unsafe fn info_from_op(&self, op: BNLowLevelILInstruction) -> ExprInfo<'func, A, M, SSA> {
+ use binaryninjacore_sys::BNLowLevelILOperation::*;
+
+ match op.operation {
+ LLIL_LOAD_SSA => ExprInfo::Load(Operation::new(self.function, op)),
+ LLIL_REG_SSA |
+ LLIL_REG_SSA_PARTIAL => ExprInfo::Reg(Operation::new(self.function, op)),
+ LLIL_FLAG_SSA => ExprInfo::Flag(Operation::new(self.function, op)),
+ LLIL_FLAG_BIT_SSA => ExprInfo::FlagBit(Operation::new(self.function, op)),
+ _ => common_info(self.function, op),
+ }
+ }
+
+ pub fn info(&self) -> ExprInfo<'func, A, M, SSA> {
+ unsafe {
+ let op = BNGetLowLevelILByIndex(self.function.handle, self.expr_idx);
+ self.info_from_op(op)
+ }
+ }
+
+ pub fn visit_tree<F>(&self, f: &mut F) -> VisitorAction
+ where
+ F: FnMut(&Self, &ExprInfo<'func, A, M, SSA>) -> VisitorAction,
+ {
+ use self::ExprInfo::*;
+
+ let info = self.info();
+
+ match f(self, &info) {
+ VisitorAction::Descend => {},
+ action => return action,
+ };
+
+ match info {
+ // TODO ssa
+ Load(ref _op) => {} //visit!(Self::visit_tree, &op.source_mem_expr(), f),
+ _ => {
+ let mut fb = |e: &Self| e.visit_tree(f);
+ visit!(common_visit, &info, &mut fb);
+ }
+ };
+
+ VisitorAction::Sibling
+ }
+}
+
+impl<'func, A, F> Expression<'func, A, Finalized, F, ValueExpr>
+where
+ A: 'func + Architecture,
+ F: FunctionForm,
+{
+ // TODO possible values
+}
+
+
+
+pub enum ExprInfo<'func, A, M, F>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ Load(Operation<'func, A, M, F, operation::Load>),
+ Pop(Operation<'func, A, M, F, operation::Pop>),
+ Reg(Operation<'func, A, M, F, operation::Reg>),
+ Const(Operation<'func, A, M, F, operation::Const>),
+ ConstPtr(Operation<'func, A, M, F, operation::Const>),
+ Flag(Operation<'func, A, M, F, operation::Flag>),
+ FlagBit(Operation<'func, A, M, F, operation::FlagBit>),
+
+ Add(Operation<'func, A, M, F, operation::BinaryOp>),
+ Adc(Operation<'func, A, M, F, operation::BinaryOpCarry>),
+ Sub(Operation<'func, A, M, F, operation::BinaryOp>),
+ Sbb(Operation<'func, A, M, F, operation::BinaryOpCarry>),
+ And(Operation<'func, A, M, F, operation::BinaryOp>),
+ Or (Operation<'func, A, M, F, operation::BinaryOp>),
+ Xor(Operation<'func, A, M, F, operation::BinaryOp>),
+ Lsl(Operation<'func, A, M, F, operation::BinaryOp>),
+ Lsr(Operation<'func, A, M, F, operation::BinaryOp>),
+ Asr(Operation<'func, A, M, F, operation::BinaryOp>),
+ Rol(Operation<'func, A, M, F, operation::BinaryOp>),
+ Rlc(Operation<'func, A, M, F, operation::BinaryOpCarry>),
+ Ror(Operation<'func, A, M, F, operation::BinaryOp>),
+ Rrc(Operation<'func, A, M, F, operation::BinaryOpCarry>),
+ Mul(Operation<'func, A, M, F, operation::BinaryOp>),
+
+ MulsDp(Operation<'func, A, M, F, operation::BinaryOp>),
+ MuluDp(Operation<'func, A, M, F, operation::BinaryOp>),
+
+ Divu(Operation<'func, A, M, F, operation::BinaryOp>),
+ Divs(Operation<'func, A, M, F, operation::BinaryOp>),
+
+ DivuDp(Operation<'func, A, M, F, operation::DoublePrecDivOp>),
+ DivsDp(Operation<'func, A, M, F, operation::DoublePrecDivOp>),
+
+ Modu(Operation<'func, A, M, F, operation::BinaryOp>),
+ Mods(Operation<'func, A, M, F, operation::BinaryOp>),
+
+ ModuDp(Operation<'func, A, M, F, operation::DoublePrecDivOp>),
+ ModsDp(Operation<'func, A, M, F, operation::DoublePrecDivOp>),
+
+ Neg(Operation<'func, A, M, F, operation::UnaryOp>),
+ Not(Operation<'func, A, M, F, operation::UnaryOp>),
+ Sx(Operation<'func, A, M, F, operation::UnaryOp>),
+ Zx(Operation<'func, A, M, F, operation::UnaryOp>),
+ LowPart(Operation<'func, A, M, F, operation::UnaryOp>),
+
+ FlagCond(Operation<'func, A, M, F, operation::FlagCond>),
+ FlagGroup(Operation<'func, A, M, F, operation::FlagGroup>),
+
+ CmpE(Operation<'func, A, M, F, operation::Condition>),
+ CmpNe(Operation<'func, A, M, F, operation::Condition>),
+ CmpSlt(Operation<'func, A, M, F, operation::Condition>),
+ CmpUlt(Operation<'func, A, M, F, operation::Condition>),
+ CmpSle(Operation<'func, A, M, F, operation::Condition>),
+ CmpUle(Operation<'func, A, M, F, operation::Condition>),
+ CmpSge(Operation<'func, A, M, F, operation::Condition>),
+ CmpUge(Operation<'func, A, M, F, operation::Condition>),
+ CmpSgt(Operation<'func, A, M, F, operation::Condition>),
+ CmpUgt(Operation<'func, A, M, F, operation::Condition>),
+
+ //TestBit(Operation<'func, A, M, F, operation::TestBit>), // TODO
+
+ BoolToInt(Operation<'func, A, M, F, operation::UnaryOp>),
+
+ // TODO ADD_OVERFLOW
+
+ Unimpl(Operation<'func, A, M, F, operation::NoArgs>),
+ UnimplMem(Operation<'func, A, M, F, operation::UnimplMem>),
+
+ Undef(Operation<'func, A, M, F, operation::NoArgs>),
+}
+
+impl<'func, A, M, F> ExprInfo<'func, A, M, F>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ /// Returns the size of the result of this expression
+ ///
+ /// If the expression is malformed or is `Unimpl` there
+ /// is no meaningful size associated with the result.
+ pub fn size(&self) -> Option<usize> {
+ use self::ExprInfo::*;
+
+ match *self {
+ Undef(..) |
+ Unimpl(..) => None,
+
+ FlagCond(..) | FlagGroup(..) |
+ CmpE(..) | CmpNe(..) |
+ CmpSlt(..) | CmpUlt(..) |
+ CmpSle(..) | CmpUle(..) |
+ CmpSge(..) | CmpUge(..) |
+ CmpSgt(..) | CmpUgt(..) => Some(0),
+
+ _ => Some(self.raw_struct().size),
+
+ //TestBit(Operation<'func, A, M, F, operation::TestBit>), // TODO
+ }
+ }
+
+ pub fn address(&self) -> u64 {
+ self.raw_struct().address
+ }
+
+ /// Determines if the expressions represent the same operation
+ ///
+ /// It does not examine the operands for equality.
+ pub fn is_same_op_as(&self, other: &Self) -> bool {
+ use self::ExprInfo::*;
+
+ match (self, other) {
+ (&Reg(..), &Reg(..)) => true,
+ _ => self.raw_struct().operation == other.raw_struct().operation,
+ }
+ }
+
+ pub fn as_cmp_op(&self) -> Option<&Operation<'func, A, M, F, operation::Condition>> {
+ use self::ExprInfo::*;
+
+ match *self {
+ CmpE (ref op) | CmpNe (ref op) |
+ CmpSlt(ref op) | CmpUlt(ref op) |
+ CmpSle(ref op) | CmpUle(ref op) |
+ CmpSge(ref op) | CmpUge(ref op) |
+ CmpSgt(ref op) | CmpUgt(ref op) => Some(op),
+ _ => None,
+ }
+ }
+
+ pub fn as_binary_op(&self) -> Option<&Operation<'func, A, M, F, operation::BinaryOp>> {
+ use self::ExprInfo::*;
+
+ match *self {
+ Add(ref op) |
+ Sub(ref op) |
+ And(ref op) |
+ Or (ref op) |
+ Xor(ref op) |
+ Lsl(ref op) |
+ Lsr(ref op) |
+ Asr(ref op) |
+ Rol(ref op) |
+ Ror(ref op) |
+ Mul(ref op) |
+ MulsDp(ref op) |
+ MuluDp(ref op) |
+ Divu(ref op) |
+ Divs(ref op) |
+ Modu(ref op) |
+ Mods(ref op) => Some(op),
+ _ => None,
+ }
+ }
+
+ pub fn as_binary_op_carry(&self) -> Option<&Operation<'func, A, M, F, operation::BinaryOpCarry>> {
+ use self::ExprInfo::*;
+
+ match *self {
+ Adc(ref op) |
+ Sbb(ref op) |
+ Rlc(ref op) |
+ Rrc(ref op) => Some(op),
+ _ => None,
+ }
+ }
+
+ pub fn as_double_prec_div_op(&self) -> Option<&Operation<'func, A, M, F, operation::DoublePrecDivOp>> {
+ use self::ExprInfo::*;
+
+ match *self {
+ DivuDp(ref op) |
+ DivsDp(ref op) |
+ ModuDp(ref op) |
+ ModsDp(ref op) => Some(op),
+ _ => None,
+ }
+ }
+
+ pub fn as_unary_op(&self) -> Option<&Operation<'func, A, M, F, operation::UnaryOp>> {
+ use self::ExprInfo::*;
+
+ match *self {
+ Neg(ref op) |
+ Not(ref op) |
+ Sx(ref op) |
+ Zx(ref op) |
+ LowPart(ref op) |
+ BoolToInt(ref op) => Some(op),
+ _ => None,
+ }
+ }
+
+ pub(crate) fn raw_struct(&self) -> &BNLowLevelILInstruction {
+ use self::ExprInfo::*;
+
+ match *self {
+ Undef(ref op) => &op.op,
+
+ Unimpl(ref op) => &op.op,
+
+ FlagCond(ref op) => &op.op,
+ FlagGroup(ref op) => &op.op,
+
+ CmpE (ref op) | CmpNe (ref op) |
+ CmpSlt(ref op) | CmpUlt(ref op) |
+ CmpSle(ref op) | CmpUle(ref op) |
+ CmpSge(ref op) | CmpUge(ref op) |
+ CmpSgt(ref op) | CmpUgt(ref op) => &op.op,
+
+ Load(ref op) => &op.op,
+
+ Pop(ref op) => &op.op,
+
+ Reg(ref op) => &op.op,
+
+ Flag(ref op) => &op.op,
+
+ FlagBit(ref op) => &op.op,
+
+ Const(ref op) |
+ ConstPtr(ref op) => &op.op,
+
+ Adc(ref op) |
+ Sbb(ref op) |
+ Rlc(ref op) |
+ Rrc(ref op) => &op.op,
+
+ Add(ref op) |
+ Sub(ref op) |
+ And(ref op) |
+ Or (ref op) |
+ Xor(ref op) |
+ Lsl(ref op) |
+ Lsr(ref op) |
+ Asr(ref op) |
+ Rol(ref op) |
+ Ror(ref op) |
+ Mul(ref op) |
+ MulsDp(ref op) |
+ MuluDp(ref op) |
+ Divu(ref op) |
+ Divs(ref op) |
+ Modu(ref op) |
+ Mods(ref op) => &op.op,
+
+ DivuDp(ref op) |
+ DivsDp(ref op) |
+ ModuDp(ref op) |
+ ModsDp(ref op) => &op.op,
+
+ Neg(ref op) |
+ Not(ref op) |
+ Sx(ref op) |
+ Zx(ref op) |
+ LowPart(ref op) |
+ BoolToInt(ref op) => &op.op,
+
+ UnimplMem(ref op) => &op.op,
+
+ //TestBit(Operation<'func, A, M, F, operation::TestBit>), // TODO
+ }
+ }
+}
+
+impl<'func, A> ExprInfo<'func, A, Mutable, NonSSA<LiftedNonSSA>>
+where
+ A: 'func + Architecture,
+{
+
+ pub fn flag_write(&self) -> Option<A::FlagWrite> {
+ use self::ExprInfo::*;
+
+ match *self {
+ Undef(ref op) => None,
+
+ Unimpl(ref op) => None,
+
+ FlagCond(ref op) => None,
+ FlagGroup(ref op) => None,
+
+ CmpE (ref op) | CmpNe (ref op) |
+ CmpSlt(ref op) | CmpUlt(ref op) |
+ CmpSle(ref op) | CmpUle(ref op) |
+ CmpSge(ref op) | CmpUge(ref op) |
+ CmpSgt(ref op) | CmpUgt(ref op) => None,
+
+ Load(ref op) => op.flag_write(),
+
+ Pop(ref op) => op.flag_write(),
+
+ Reg(ref op) => op.flag_write(),
+
+ Flag(ref op) => op.flag_write(),
+
+ FlagBit(ref op) => op.flag_write(),
+
+ Const(ref op) |
+ ConstPtr(ref op) => op.flag_write(),
+
+ Adc(ref op) |
+ Sbb(ref op) |
+ Rlc(ref op) |
+ Rrc(ref op) => op.flag_write(),
+
+ Add(ref op) |
+ Sub(ref op) |
+ And(ref op) |
+ Or (ref op) |
+ Xor(ref op) |
+ Lsl(ref op) |
+ Lsr(ref op) |
+ Asr(ref op) |
+ Rol(ref op) |
+ Ror(ref op) |
+ Mul(ref op) |
+ MulsDp(ref op) |
+ MuluDp(ref op) |
+ Divu(ref op) |
+ Divs(ref op) |
+ Modu(ref op) |
+ Mods(ref op) => op.flag_write(),
+
+ DivuDp(ref op) |
+ DivsDp(ref op) |
+ ModuDp(ref op) |
+ ModsDp(ref op) => op.flag_write(),
+
+ Neg(ref op) |
+ Not(ref op) |
+ Sx(ref op) |
+ Zx(ref op) |
+ LowPart(ref op) |
+ BoolToInt(ref op) => op.flag_write(),
+
+ UnimplMem(ref op) => op.flag_write(),
+
+ //TestBit(Operation<'func, A, M, F, operation::TestBit>), // TODO
+ }
+ }
+}
+
+impl<'func, A, M, V> fmt::Debug for ExprInfo<'func, A, M, NonSSA<V>>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ V: NonSSAVariant,
+{
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::ExprInfo::*;
+
+ match *self {
+ Undef(..) => f.write_str("undefined"),
+
+ Unimpl(..) => f.write_str("unimplemented"),
+
+ FlagCond(..) => f.write_str("some_flag_cond"),
+ FlagGroup(..) => f.write_str("some_flag_group"),
+
+ CmpE(ref op) | CmpNe(ref op) |
+ CmpSlt(ref op) | CmpUlt(ref op) |
+ CmpSle(ref op) | CmpUle(ref op) |
+ CmpSge(ref op) | CmpUge(ref op) |
+ CmpSgt(ref op) | CmpUgt(ref op) => {
+ let left = op.left();
+ let right = op.right();
+
+ write!(f, "{:?}({}, {:?}, {:?})", op.op.operation, op.size(), left, right)
+ }
+
+ Load(ref op) => {
+ let source = op.source_mem_expr();
+ let size = op.size();
+
+ write!(f, "[{:?}].{}", source, size)
+ }
+
+ Pop(ref op) => write!(f, "pop.{}", op.size()),
+
+ Reg(ref op) => {
+ let reg = op.source_reg();
+ let size = op.size();
+
+ let size = match reg {
+ Register::Temp(_) => Some(size),
+ Register::ArchReg(ref r) if r.info().size() != size => Some(size),
+ _ => None
+ };
+
+ match size {
+ Some(s) => write!(f, "{:?}.{}", reg, s),
+ _ => write!(f, "{:?}", reg),
+ }
+ }
+
+ Flag(ref _op) => write!(f, "flag"), // TODO
+
+ FlagBit(ref _op) => write!(f, "flag_bit"), // TODO
+
+ Const(ref op) |
+ ConstPtr(ref op) => write!(f, "0x{:x}", op.value()),
+
+ Adc(ref op) |
+ Sbb(ref op) |
+ Rlc(ref op) |
+ Rrc(ref op) => {
+ let left = op.left();
+ let right = op.right();
+ let carry = op.carry();
+
+ write!(f, "{:?}({}, {:?}, {:?}, carry: {:?})",
+ op.op.operation, op.size(), left, right, carry)
+ }
+
+ Add(ref op) |
+ Sub(ref op) |
+ And(ref op) |
+ Or (ref op) |
+ Xor(ref op) |
+ Lsl(ref op) |
+ Lsr(ref op) |
+ Asr(ref op) |
+ Rol(ref op) |
+ Ror(ref op) |
+ Mul(ref op) |
+ MulsDp(ref op) |
+ MuluDp(ref op) |
+ Divu(ref op) |
+ Divs(ref op) |
+ Modu(ref op) |
+ Mods(ref op) => {
+ let left = op.left();
+ let right = op.right();
+
+ write!(f, "{:?}({}, {:?}, {:?})",
+ op.op.operation, op.size(), left, right)
+ }
+
+ DivuDp(ref op) |
+ DivsDp(ref op) |
+ ModuDp(ref op) |
+ ModsDp(ref op) => {
+ let high = op.high();
+ let low = op.low();
+ let right = op.right();
+
+ write!(f, "{:?}({}, {:?}:{:?},{:?})",
+ op.op.operation, op.size(), high, low, right)
+ }
+
+ Neg(ref op) |
+ Not(ref op) |
+ Sx(ref op) |
+ Zx(ref op) |
+ LowPart(ref op) |
+ BoolToInt(ref op) => {
+ write!(f, "{:?}({}, {:?})", op.op.operation, op.size(), op.operand())
+ }
+
+ UnimplMem(ref op) => write!(f, "unimplemented_mem({:?})", op.mem_expr()),
+
+ //TestBit(Operation<'func, A, M, F, operation::TestBit>), // TODO
+ }
+ }
+}
diff --git a/rust/src/llil/function.rs b/rust/src/llil/function.rs
new file mode 100644
index 00000000..ac50e72c
--- /dev/null
+++ b/rust/src/llil/function.rs
@@ -0,0 +1,198 @@
+use binaryninjacore_sys::BNLowLevelILFunction;
+use binaryninjacore_sys::BNNewLowLevelILFunctionReference;
+use binaryninjacore_sys::BNFreeLowLevelILFunction;
+
+use std::borrow::Borrow;
+use std::marker::PhantomData;
+
+use crate::basicblock::BasicBlock;
+use crate::rc::*;
+
+use super::*;
+
+#[derive(Copy, Clone, Debug)]
+pub struct Mutable;
+#[derive(Copy, Clone, Debug)]
+pub struct Finalized;
+
+pub trait FunctionMutability: 'static {}
+impl FunctionMutability for Mutable {}
+impl FunctionMutability for Finalized {}
+
+
+#[derive(Copy, Clone, Debug)]
+pub struct LiftedNonSSA;
+#[derive(Copy, Clone, Debug)]
+pub struct RegularNonSSA;
+
+pub trait NonSSAVariant: 'static {}
+impl NonSSAVariant for LiftedNonSSA {}
+impl NonSSAVariant for RegularNonSSA {}
+
+#[derive(Copy, Clone, Debug)]
+pub struct SSA;
+#[derive(Copy, Clone, Debug)]
+pub struct NonSSA<V: NonSSAVariant>(V);
+
+pub trait FunctionForm: 'static {}
+impl FunctionForm for SSA {}
+impl<V: NonSSAVariant> FunctionForm for NonSSA<V> {}
+
+
+pub struct Function<A: Architecture, M: FunctionMutability, F: FunctionForm> {
+ pub(crate) borrower: A::Handle,
+ pub(crate) handle: *mut BNLowLevelILFunction,
+ _arch: PhantomData<*mut A>,
+ _mutability: PhantomData<M>,
+ _form: PhantomData<F>,
+}
+
+unsafe impl<A: Architecture, M: FunctionMutability, F: FunctionForm> Send for Function<A, M, F> {}
+unsafe impl<A: Architecture, M: FunctionMutability, F: FunctionForm> Sync for Function<A, M, F> {}
+
+impl<A: Architecture, M: FunctionMutability, F: FunctionForm> Eq for Function<A, M, F> {}
+impl<A: Architecture, M: FunctionMutability, F: FunctionForm> PartialEq for Function<A, M, F> {
+ fn eq(&self, rhs: &Self) -> bool {
+ self.handle == rhs.handle
+ }
+}
+
+use std::hash::{Hash, Hasher};
+impl<A: Architecture, M: FunctionMutability, F: FunctionForm> Hash for Function<A, M, F> {
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ self.handle.hash(state);
+ }
+}
+
+impl<'func, A, M, F> Function<A, M, F>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm
+{
+ pub(crate) unsafe fn from_raw(borrower: A::Handle, handle: *mut BNLowLevelILFunction) -> Self {
+ debug_assert!(!handle.is_null());
+
+ Self {
+ borrower,
+ handle,
+ _arch: PhantomData,
+ _mutability: PhantomData,
+ _form: PhantomData,
+ }
+ }
+
+ pub(crate) fn arch(&self) -> &A {
+ self.borrower.borrow()
+ }
+
+ pub fn instruction_at<L: Into<Location>>(&self, loc: L) -> Option<Instruction<A, M, F>> {
+ use binaryninjacore_sys::BNLowLevelILGetInstructionStart;
+ use binaryninjacore_sys::BNGetLowLevelILInstructionCount;
+
+ let loc: Location = loc.into();
+ let arch_handle = loc.arch.unwrap_or_else(|| *self.arch().as_ref());
+
+ unsafe {
+ let instr_idx = BNLowLevelILGetInstructionStart(self.handle, arch_handle.0, loc.addr);
+
+ if instr_idx >= BNGetLowLevelILInstructionCount(self.handle) {
+ None
+ } else {
+ Some(Instruction {
+ function: self,
+ instr_idx: instr_idx,
+ })
+ }
+ }
+ }
+
+ pub fn instruction_from_idx(&self, instr_idx: usize) -> Instruction<A, M, F> {
+ unsafe {
+ use binaryninjacore_sys::BNGetLowLevelILInstructionCount;
+ if instr_idx >= BNGetLowLevelILInstructionCount(self.handle) {
+ panic!("instruction index {} out of bounds", instr_idx);
+ }
+
+ Instruction {
+ function: self,
+ instr_idx: instr_idx,
+ }
+ }
+ }
+
+ pub fn instruction_count(&self) -> usize {
+ unsafe {
+ use binaryninjacore_sys::BNGetLowLevelILInstructionCount;
+ BNGetLowLevelILInstructionCount(self.handle)
+ }
+ }
+
+}
+
+// LLIL basic blocks are not available until the function object
+// is finalized, so ensure we can't try requesting basic blocks
+// during lifting
+impl<'func, A, F> Function<A, Finalized, F>
+where
+ A: 'func + Architecture,
+ F: FunctionForm
+{
+ pub fn basic_blocks(&self) -> Array<BasicBlock<LowLevelBlock<A, Finalized, F>>> {
+ use binaryninjacore_sys::BNGetLowLevelILBasicBlockList;
+
+ unsafe {
+ let mut count = 0;
+ let blocks = BNGetLowLevelILBasicBlockList(self.handle, &mut count);
+ let context = LowLevelBlock { function: self };
+
+ Array::new(blocks, count, context)
+ }
+ }
+}
+
+impl<'func, A, M, F> ToOwned for Function<A, M, F>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm
+{
+ type Owned = Ref<Self>;
+
+ fn to_owned(&self) -> Self::Owned {
+ unsafe { RefCountable::inc_ref(self) }
+ }
+}
+
+
+unsafe impl<'func, A, M, F> RefCountable for Function<A, M, F>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm
+{
+ unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
+ Ref::new(Self {
+ borrower: handle.borrower.clone(),
+ handle: BNNewLowLevelILFunctionReference(handle.handle),
+ _arch: PhantomData,
+ _mutability: PhantomData,
+ _form: PhantomData,
+ })
+ }
+
+ unsafe fn dec_ref(handle: &Self) {
+ BNFreeLowLevelILFunction(handle.handle);
+ }
+}
+
+impl<'func, A, M, F> fmt::Debug for Function<A, M, F>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "<llil func handle {:p}>", self.handle)
+ }
+}
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>),
+}
diff --git a/rust/src/llil/lifting.rs b/rust/src/llil/lifting.rs
new file mode 100644
index 00000000..e50eaaf1
--- /dev/null
+++ b/rust/src/llil/lifting.rs
@@ -0,0 +1,1292 @@
+use std::marker::PhantomData;
+use std::mem;
+
+use crate::architecture::Register as ArchReg;
+use crate::architecture::{FlagWrite, Flag, FlagClass, FlagGroup, FlagRole, FlagCondition};
+use crate::architecture::Architecture;
+
+
+use super::*;
+
+pub trait Liftable<'func, A: 'func + Architecture> {
+ type Result: ExpressionResultType;
+
+ fn lift(il: &'func Function<A, Mutable, NonSSA<LiftedNonSSA>>, expr: Self)
+ -> Expression<'func, A, Mutable, NonSSA<LiftedNonSSA>, Self::Result>;
+}
+
+pub trait LiftableWithSize<'func, A: 'func + Architecture>: Liftable<'func, A, Result=ValueExpr> {
+ fn lift_with_size(il: &'func Function<A, Mutable, NonSSA<LiftedNonSSA>>, expr: Self, size: usize)
+ -> Expression<'func, A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr>;
+}
+
+use binaryninjacore_sys::BNRegisterOrConstant;
+
+#[derive(Copy, Clone)]
+pub enum RegisterOrConstant<R: ArchReg> {
+ Register(usize, Register<R>),
+ Constant(usize, u64),
+}
+
+impl<R: ArchReg> RegisterOrConstant<R> {
+ pub(crate) fn into_api(self) -> BNRegisterOrConstant {
+ match self {
+ RegisterOrConstant::Register(_, r) => BNRegisterOrConstant {
+ constant: false,
+ reg: r.id(),
+ value: 0,
+ },
+ RegisterOrConstant::Constant(_, value) => BNRegisterOrConstant {
+ constant: true,
+ reg: 0,
+ value: value,
+ }
+ }
+ }
+}
+
+// TODO flesh way out
+#[derive(Copy, Clone)]
+pub enum FlagWriteOp<R: ArchReg> {
+ SetReg(usize, RegisterOrConstant<R>),
+ SetRegSplit(usize, RegisterOrConstant<R>, RegisterOrConstant<R>),
+
+ Sub(usize, RegisterOrConstant<R>, RegisterOrConstant<R>),
+ Add(usize, RegisterOrConstant<R>, RegisterOrConstant<R>),
+
+ Load(usize, RegisterOrConstant<R>),
+
+ Push(usize, RegisterOrConstant<R>),
+ Neg(usize, RegisterOrConstant<R>),
+ Not(usize, RegisterOrConstant<R>),
+ Sx(usize, RegisterOrConstant<R>),
+ Zx(usize, RegisterOrConstant<R>),
+ LowPart(usize, RegisterOrConstant<R>),
+ BoolToInt(usize, RegisterOrConstant<R>),
+ FloatToInt(usize, RegisterOrConstant<R>),
+
+ Store(usize, RegisterOrConstant<R>, RegisterOrConstant<R>),
+
+ And(usize, RegisterOrConstant<R>, RegisterOrConstant<R>),
+ Or(usize, RegisterOrConstant<R>, RegisterOrConstant<R>),
+ Xor(usize, RegisterOrConstant<R>, RegisterOrConstant<R>),
+ Lsl(usize, RegisterOrConstant<R>, RegisterOrConstant<R>),
+ Lsr(usize, RegisterOrConstant<R>, RegisterOrConstant<R>),
+ Asr(usize, RegisterOrConstant<R>, RegisterOrConstant<R>),
+ Rol(usize, RegisterOrConstant<R>, RegisterOrConstant<R>),
+ Ror(usize, RegisterOrConstant<R>, RegisterOrConstant<R>),
+ Mul(usize, RegisterOrConstant<R>, RegisterOrConstant<R>),
+ MuluDp(usize, RegisterOrConstant<R>, RegisterOrConstant<R>),
+ MulsDp(usize, RegisterOrConstant<R>, RegisterOrConstant<R>),
+ Divu(usize, RegisterOrConstant<R>, RegisterOrConstant<R>),
+ Divs(usize, RegisterOrConstant<R>, RegisterOrConstant<R>),
+ Modu(usize, RegisterOrConstant<R>, RegisterOrConstant<R>),
+ Mods(usize, RegisterOrConstant<R>, RegisterOrConstant<R>),
+ DivuDp(usize, RegisterOrConstant<R>, RegisterOrConstant<R>),
+ DivsDp(usize, RegisterOrConstant<R>, RegisterOrConstant<R>),
+ ModuDp(usize, RegisterOrConstant<R>, RegisterOrConstant<R>),
+ ModsDp(usize, RegisterOrConstant<R>, RegisterOrConstant<R>),
+
+ TestBit(usize, RegisterOrConstant<R>, RegisterOrConstant<R>),
+ AddOverflow(usize, RegisterOrConstant<R>, RegisterOrConstant<R>),
+
+ Adc(usize, RegisterOrConstant<R>, RegisterOrConstant<R>, RegisterOrConstant<R>),
+ Sbb(usize, RegisterOrConstant<R>, RegisterOrConstant<R>, RegisterOrConstant<R>),
+ Rlc(usize, RegisterOrConstant<R>, RegisterOrConstant<R>, RegisterOrConstant<R>),
+ Rrc(usize, RegisterOrConstant<R>, RegisterOrConstant<R>, RegisterOrConstant<R>),
+
+ Pop(usize),
+
+ // TODO: floating point stuff, llil comparison ops that set flags, intrinsics
+}
+
+impl<R: ArchReg> FlagWriteOp<R> {
+ pub(crate) fn from_op<A>(arch: &A, size: usize, op: BNLowLevelILOperation, operands: &[BNRegisterOrConstant])
+ -> Option<Self>
+ where
+ A: Architecture<Register=R>,
+ R: ArchReg<InfoType=A::RegisterInfo>,
+ {
+ use binaryninjacore_sys::BNLowLevelILOperation::*;
+ use self::FlagWriteOp::*;
+
+ fn build_op<A, R>(arch: &A, size: usize, operand: &BNRegisterOrConstant) -> RegisterOrConstant<R>
+ where
+ A: Architecture<Register=R>,
+ R: ArchReg<InfoType=A::RegisterInfo>,
+ {
+ if operand.constant {
+ RegisterOrConstant::Constant(size, operand.value)
+ } else {
+ let il_reg = if 0x8000_0000 & operand.reg == 0 {
+ Register::ArchReg(arch.register_from_id(operand.reg).unwrap())
+ } else {
+ Register::Temp(operand.reg)
+ };
+
+ RegisterOrConstant::Register(size, il_reg)
+ }
+ }
+
+ macro_rules! op {
+ ($x:ident, $($ops:expr),*) => {
+ ( $x(size, $( build_op(arch, size, &operands[$ops]), )* ) )
+ };
+ }
+
+ Some(match (operands.len(), op) {
+ (1, LLIL_SET_REG) => op!(SetReg, 0),
+ (2, LLIL_SET_REG_SPLIT) => op!(SetRegSplit, 0, 1),
+
+ (2, LLIL_SUB) => op!(Sub, 0, 1),
+ (2, LLIL_ADD) => op!(Add, 0, 1),
+
+ (1, LLIL_LOAD) => op!(Load, 0),
+
+ (1, LLIL_PUSH) => op!(Push, 0),
+ (1, LLIL_NEG) => op!(Neg, 0),
+ (1, LLIL_NOT) => op!(Not, 0),
+ (1, LLIL_SX) => op!(Sx, 0),
+ (1, LLIL_ZX) => op!(Zx, 0),
+ (1, LLIL_LOW_PART) => op!(LowPart, 0),
+ (1, LLIL_BOOL_TO_INT) => op!(BoolToInt, 0),
+ (1, LLIL_FLOAT_TO_INT) => op!(FloatToInt, 0),
+
+ (2, LLIL_STORE) => op!(Store, 0, 1),
+
+ (2, LLIL_AND) => op!(And, 0, 1),
+ (2, LLIL_OR) => op!(Or, 0, 1),
+ (2, LLIL_XOR) => op!(Xor, 0, 1),
+ (2, LLIL_LSL) => op!(Lsl, 0, 1),
+ (2, LLIL_LSR) => op!(Lsr, 0, 1),
+ (2, LLIL_ASR) => op!(Asr, 0, 1),
+ (2, LLIL_ROL) => op!(Rol, 0, 1),
+ (2, LLIL_ROR) => op!(Ror, 0, 1),
+ (2, LLIL_MUL) => op!(Mul, 0, 1),
+ (2, LLIL_MULU_DP) => op!(MuluDp, 0, 1),
+ (2, LLIL_MULS_DP) => op!(MulsDp, 0, 1),
+ (2, LLIL_DIVU) => op!(Divu, 0, 1),
+ (2, LLIL_DIVS) => op!(Divs, 0, 1),
+ (2, LLIL_MODU) => op!(Modu, 0, 1),
+ (2, LLIL_MODS) => op!(Mods, 0, 1),
+ (2, LLIL_DIVU_DP) => op!(DivuDp, 0, 1),
+ (2, LLIL_DIVS_DP) => op!(DivsDp, 0, 1),
+ (2, LLIL_MODU_DP) => op!(ModuDp, 0, 1),
+ (2, LLIL_MODS_DP) => op!(ModsDp, 0, 1),
+
+ (2, LLIL_TEST_BIT) => op!(TestBit, 0, 1),
+ (2, LLIL_ADD_OVERFLOW) => op!(AddOverflow, 0, 1),
+
+ (3, LLIL_ADC) => op!(Adc, 0, 1, 2),
+ (3, LLIL_SBB) => op!(Sbb, 0, 1, 2),
+ (3, LLIL_RLC) => op!(Rlc, 0, 1, 2),
+ (3, LLIL_RRC) => op!(Rrc, 0, 1, 2),
+
+ (0, LLIL_POP) => op!(Pop, ),
+
+ _ => return None,
+ })
+ }
+
+ pub(crate) fn size_and_op(&self) -> (usize, BNLowLevelILOperation) {
+ use binaryninjacore_sys::BNLowLevelILOperation::*;
+ use self::FlagWriteOp::*;
+
+ match *self {
+ SetReg(size, ..) => (size, LLIL_SET_REG),
+ SetRegSplit(size, ..) => (size, LLIL_SET_REG_SPLIT),
+
+ Sub(size, ..) => (size, LLIL_SUB),
+ Add(size, ..) => (size, LLIL_ADD),
+
+ Load(size, ..) => (size, LLIL_LOAD),
+
+ Push(size, ..) => (size, LLIL_PUSH),
+ Neg(size, ..) => (size, LLIL_NEG),
+ Not(size, ..) => (size, LLIL_NOT),
+ Sx(size, ..) => (size, LLIL_SX),
+ Zx(size, ..) => (size, LLIL_ZX),
+ LowPart(size, ..) => (size, LLIL_LOW_PART),
+ BoolToInt(size, ..) => (size, LLIL_BOOL_TO_INT),
+ FloatToInt(size, ..) => (size, LLIL_FLOAT_TO_INT),
+
+ Store(size, ..) => (size, LLIL_STORE),
+
+ And(size, ..) => (size, LLIL_AND),
+ Or(size, ..) => (size, LLIL_OR),
+ Xor(size, ..) => (size, LLIL_XOR),
+ Lsl(size, ..) => (size, LLIL_LSL),
+ Lsr(size, ..) => (size, LLIL_LSR),
+ Asr(size, ..) => (size, LLIL_ASR),
+ Rol(size, ..) => (size, LLIL_ROL),
+ Ror(size, ..) => (size, LLIL_ROR),
+ Mul(size, ..) => (size, LLIL_MUL),
+ MuluDp(size, ..) => (size, LLIL_MULU_DP),
+ MulsDp(size, ..) => (size, LLIL_MULS_DP),
+ Divu(size, ..) => (size, LLIL_DIVU),
+ Divs(size, ..) => (size, LLIL_DIVS),
+ Modu(size, ..) => (size, LLIL_MODU),
+ Mods(size, ..) => (size, LLIL_MODS),
+ DivuDp(size, ..) => (size, LLIL_DIVU_DP),
+ DivsDp(size, ..) => (size, LLIL_DIVS_DP),
+ ModuDp(size, ..) => (size, LLIL_MODU_DP),
+ ModsDp(size, ..) => (size, LLIL_MODS_DP),
+
+ TestBit(size, ..) => (size, LLIL_TEST_BIT),
+ AddOverflow(size, ..) => (size, LLIL_ADD_OVERFLOW),
+
+ Adc(size, ..) => (size, LLIL_ADC),
+ Sbb(size, ..) => (size, LLIL_SBB),
+ Rlc(size, ..) => (size, LLIL_RLC),
+ Rrc(size, ..) => (size, LLIL_RRC),
+
+ Pop(size) => (size, LLIL_POP),
+ }
+ }
+
+ pub(crate) fn api_operands(&self) -> (usize, [BNRegisterOrConstant; 5]) {
+ use self::FlagWriteOp::*;
+
+ let mut operands: [BNRegisterOrConstant; 5] = unsafe { mem::zeroed() };
+
+ let count = match *self {
+ Pop(_) => 0,
+
+ SetReg(_, op0) |
+ Load(_, op0) |
+ Push(_, op0) |
+ Neg(_, op0) |
+ Not(_, op0) |
+ Sx(_, op0) |
+ Zx(_, op0) |
+ LowPart(_, op0) |
+ BoolToInt(_, op0) |
+ FloatToInt(_, op0) => {
+ operands[0] = op0.into_api();
+ 1
+ }
+
+ SetRegSplit(_, op0, op1) |
+ Sub(_, op0, op1) |
+ Add(_, op0, op1) |
+ Store(_, op0, op1) |
+ And(_, op0, op1) |
+ Or(_, op0, op1) |
+ Xor(_, op0, op1) |
+ Lsl(_, op0, op1) |
+ Lsr(_, op0, op1) |
+ Asr(_, op0, op1) |
+ Rol(_, op0, op1) |
+ Ror(_, op0, op1) |
+ Mul(_, op0, op1) |
+ MuluDp(_, op0, op1) |
+ MulsDp(_, op0, op1) |
+ Divu(_, op0, op1) |
+ Divs(_, op0, op1) |
+ Modu(_, op0, op1) |
+ Mods(_, op0, op1) |
+ DivuDp(_, op0, op1) |
+ DivsDp(_, op0, op1) |
+ ModuDp(_, op0, op1) |
+ ModsDp(_, op0, op1) |
+ TestBit(_, op0, op1) |
+ AddOverflow(_, op0, op1) => {
+ operands[0] = op0.into_api();
+ operands[1] = op1.into_api();
+ 2
+ }
+
+ Adc(_, op0, op1, op2) |
+ Sbb(_, op0, op1, op2) |
+ Rlc(_, op0, op1, op2) |
+ Rrc(_, op0, op1, op2) => {
+ operands[0] = op0.into_api();
+ operands[1] = op1.into_api();
+ operands[2] = op2.into_api();
+ 3
+ }
+ };
+
+ (count, operands)
+ }
+}
+
+
+pub fn get_default_flag_write_llil<'func, A>(arch: &A, role: FlagRole, op: FlagWriteOp<A::Register>, il: &'func Lifter<A>)
+ -> LiftedExpr<'func, A>
+where
+ A: 'func + Architecture
+{
+ let (size, operation) = op.size_and_op();
+ let (count, operands) = op.api_operands();
+
+ let expr_idx = unsafe {
+ use binaryninjacore_sys::BNGetDefaultArchitectureFlagWriteLowLevelIL;
+ BNGetDefaultArchitectureFlagWriteLowLevelIL(arch.as_ref().0, operation, size, role,
+ operands.as_ptr() as *mut _, count, il.handle)
+ };
+
+ Expression {
+ function: il,
+ expr_idx: expr_idx,
+ _ty: PhantomData,
+ }
+}
+
+pub fn get_default_flag_cond_llil<'func, A>(arch: &A, cond: FlagCondition, class: Option<A::FlagClass>, il: &'func Lifter<A>)
+ -> LiftedExpr<'func, A>
+where
+ A: 'func + Architecture
+{
+ use binaryninjacore_sys::BNGetDefaultArchitectureFlagConditionLowLevelIL;
+
+ let handle = arch.as_ref();
+ let class_id = class.map(|c| c.id()).unwrap_or(0);
+
+ unsafe {
+ let expr_idx = BNGetDefaultArchitectureFlagConditionLowLevelIL(handle.0, cond, class_id, il.handle);
+
+ Expression {
+ function: il,
+ expr_idx: expr_idx,
+ _ty: PhantomData,
+ }
+ }
+}
+
+macro_rules! prim_int_lifter {
+ ($x:ty) => {
+ impl<'a, A: 'a + Architecture> Liftable<'a, A> for $x {
+ type Result = ValueExpr;
+
+ fn lift(il: &'a Function<A, Mutable, NonSSA<LiftedNonSSA>>, val: Self)
+ -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, Self::Result>
+ {
+ il.const_int(mem::size_of::<Self>(), val as i64 as u64)
+ }
+ }
+
+ impl<'a, A: 'a + Architecture> LiftableWithSize<'a, A> for $x {
+ fn lift_with_size(il: &'a Function<A, Mutable, NonSSA<LiftedNonSSA>>, val: Self, size: usize)
+ -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr>
+ {
+ let raw = val as i64;
+
+ #[cfg(debug_assertions)]
+ {
+ let is_safe = match raw.overflowing_shr(size as u32 * 8) {
+ (_, true) => true,
+ (res, false) => [-1, 0].contains(&res),
+ };
+
+ if !is_safe {
+ error!("il @ {:x} attempted to lift constant 0x{:x} as {} byte expr (won't fit!)",
+ il.current_address(), val, size);
+ }
+ }
+
+ il.const_int(size, raw as u64)
+ }
+ }
+ }
+}
+
+prim_int_lifter!(i8);
+prim_int_lifter!(i16);
+prim_int_lifter!(i32);
+prim_int_lifter!(i64);
+
+prim_int_lifter!(u8);
+prim_int_lifter!(u16);
+prim_int_lifter!(u32);
+prim_int_lifter!(u64);
+
+impl<'a, R: ArchReg, A: 'a + Architecture> Liftable<'a, A> for Register<R>
+ where R: Liftable<'a, A, Result=ValueExpr> + Into<Register<R>>
+{
+ type Result = ValueExpr;
+
+ fn lift(il: &'a Function<A, Mutable, NonSSA<LiftedNonSSA>>, reg: Self)
+ -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, Self::Result>
+ {
+ match reg {
+ Register::ArchReg(r) => R::lift(il, r),
+ Register::Temp(t) => il.reg(il.arch().default_integer_size(), Register::Temp(t)),
+ }
+ }
+}
+
+impl<'a, R: ArchReg, A: 'a + Architecture> LiftableWithSize<'a, A> for Register<R>
+ where R: LiftableWithSize<'a, A> + Into<Register<R>>
+{
+ fn lift_with_size(il: &'a Function<A, Mutable, NonSSA<LiftedNonSSA>>, reg: Self, size: usize)
+ -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr>
+ {
+ match reg {
+ Register::ArchReg(r) => R::lift_with_size(il, r, size),
+ Register::Temp(t) => il.reg(size, Register::Temp(t)),
+ }
+ }
+}
+
+
+impl<'a, R: ArchReg, A: 'a + Architecture> Liftable<'a, A> for RegisterOrConstant<R>
+ where R: LiftableWithSize<'a, A, Result=ValueExpr> + Into<Register<R>>
+{
+ type Result = ValueExpr;
+
+ fn lift(il: &'a Function<A, Mutable, NonSSA<LiftedNonSSA>>, reg: Self)
+ -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, Self::Result>
+ {
+ match reg {
+ RegisterOrConstant::Register(size, r) => Register::<R>::lift_with_size(il, r, size),
+ RegisterOrConstant::Constant(size, value) => u64::lift_with_size(il, value, size),
+ }
+ }
+}
+
+impl<'a, R: ArchReg, A: 'a + Architecture> LiftableWithSize<'a, A> for RegisterOrConstant<R>
+ where R: LiftableWithSize<'a, A> + Into<Register<R>>
+{
+ fn lift_with_size(il: &'a Function<A, Mutable, NonSSA<LiftedNonSSA>>, reg: Self, size: usize)
+ -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr>
+ {
+ // TODO ensure requested size is compatible with size of this constant
+ match reg {
+ RegisterOrConstant::Register(_, r) => Register::<R>::lift_with_size(il, r, size),
+ RegisterOrConstant::Constant(_, value) => u64::lift_with_size(il, value, size),
+ }
+ }
+}
+
+
+impl<'a, A, R> Liftable<'a, A> for Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, R>
+where
+ A: 'a + Architecture,
+ R: ExpressionResultType,
+{
+ type Result = R;
+
+ fn lift(il: &'a Function<A, Mutable, NonSSA<LiftedNonSSA>>, expr: Self)
+ -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, Self::Result>
+ {
+ debug_assert!(expr.function.handle == il.handle);
+ expr
+ }
+}
+
+impl<'a, A: 'a + Architecture> LiftableWithSize<'a, A> for Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr> {
+ fn lift_with_size(il: &'a Function<A, Mutable, NonSSA<LiftedNonSSA>>, expr: Self, _size: usize)
+ -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, Self::Result>
+ {
+ #[cfg(debug_assertions)]
+ {
+ if let Some(expr_size) = expr.info().size() {
+ if expr_size != _size {
+ warn!("il @ {:x} attempted to lift {} byte expression as {} bytes",
+ il.current_address(), expr_size, _size);
+ }
+ }
+ }
+
+ Liftable::lift(il, expr)
+ }
+}
+
+
+impl<'func, A, R> Expression<'func, A, Mutable, NonSSA<LiftedNonSSA>, R>
+where
+ A: 'func + Architecture,
+ R: ExpressionResultType,
+{
+ pub fn with_source_operand(self, op: u32) -> Self {
+ use binaryninjacore_sys::BNLowLevelILSetExprSourceOperand;
+
+ unsafe {
+ BNLowLevelILSetExprSourceOperand(self.function.handle, self.expr_idx, op)
+ }
+
+ self
+ }
+
+ pub fn append(self) {
+ let il = self.function;
+ il.instruction(self);
+ }
+}
+
+
+use binaryninjacore_sys::BNLowLevelILOperation;
+pub struct ExpressionBuilder<'func, A, R>
+where
+ A: 'func + Architecture,
+ R: ExpressionResultType,
+{
+ function: &'func Function<A, Mutable, NonSSA<LiftedNonSSA>>,
+ op: BNLowLevelILOperation,
+ size: usize,
+ flags: u32,
+ op1: u64,
+ op2: u64,
+ op3: u64,
+ op4: u64,
+ _ty: PhantomData<R>,
+}
+
+impl<'a, A, R> ExpressionBuilder<'a, A, R>
+where
+ A: 'a + Architecture,
+ R: ExpressionResultType,
+{
+ pub fn with_flag_write(mut self, flag_write: A::FlagWrite) -> Self {
+ // TODO verify valid id
+ self.flags = flag_write.id();
+ self
+ }
+
+ pub fn into_expr(self) -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, R> {
+ self.into()
+ }
+
+ pub fn with_source_operand(self, op: u32) -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, R> {
+ let expr = self.into_expr();
+ expr.with_source_operand(op)
+ }
+
+ pub fn append(self) {
+ let expr = self.into_expr();
+ let il = expr.function;
+
+ il.instruction(expr);
+ }
+}
+
+impl<'a, A, R> Into<Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, R>> for ExpressionBuilder<'a, A, R>
+where
+ A: 'a + Architecture,
+ R: ExpressionResultType,
+{
+ fn into(self) -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, R> {
+ use binaryninjacore_sys::BNLowLevelILAddExpr;
+
+ let expr_idx = unsafe {
+ BNLowLevelILAddExpr(self.function.handle,
+ self.op, self.size, self.flags,
+ self.op1, self.op2, self.op3, self.op4)
+ };
+
+ Expression {
+ function: self.function,
+ expr_idx: expr_idx,
+ _ty: PhantomData,
+ }
+ }
+}
+
+impl<'a, A, R> Liftable<'a, A> for ExpressionBuilder<'a, A, R>
+where
+ A: 'a + Architecture,
+ R: ExpressionResultType,
+{
+ type Result = R;
+
+ fn lift(il: &'a Function<A, Mutable, NonSSA<LiftedNonSSA>>, expr: Self)
+ -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, Self::Result>
+ {
+ debug_assert!(expr.function.handle == il.handle);
+
+ expr.into()
+ }
+}
+
+impl<'a, A> LiftableWithSize<'a, A> for ExpressionBuilder<'a, A, ValueExpr>
+where
+ A: 'a + Architecture,
+{
+ fn lift_with_size(il: &'a Function<A, Mutable, NonSSA<LiftedNonSSA>>, expr: Self, _size: usize)
+ -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr>
+ {
+ #[cfg(debug_assertions)]
+ {
+ use binaryninjacore_sys::BNLowLevelILOperation::{LLIL_UNIMPL, LLIL_UNIMPL_MEM};
+
+ if expr.size != _size && ![LLIL_UNIMPL, LLIL_UNIMPL_MEM].contains(&expr.op) {
+ warn!("il @ {:x} attempted to lift {} byte expression builder as {} bytes",
+ il.current_address(), expr.size, _size);
+ }
+ }
+
+ Liftable::lift(il, expr)
+ }
+}
+
+
+macro_rules! no_arg_lifter {
+ ($name:ident, $op:ident, $result:ty) => {
+ pub fn $name(&self) -> Expression<A, Mutable, NonSSA<LiftedNonSSA>, $result> {
+ use binaryninjacore_sys::BNLowLevelILOperation::$op;
+ use binaryninjacore_sys::BNLowLevelILAddExpr;
+
+ let expr_idx = unsafe {
+ BNLowLevelILAddExpr(self.handle, $op,
+ 0, 0, 0, 0, 0, 0)
+ };
+
+ Expression {
+ function: self,
+ expr_idx: expr_idx,
+ _ty: PhantomData,
+ }
+ }
+ }
+}
+
+macro_rules! sized_no_arg_lifter {
+ ($name:ident, $op:ident, $result:ty) => {
+ pub fn $name(&self, size: usize) -> ExpressionBuilder<A, $result>
+ {
+ use binaryninjacore_sys::BNLowLevelILOperation::$op;
+
+ ExpressionBuilder {
+ function: self,
+ op: $op,
+ size: size,
+ flags: 0,
+ op1: 0,
+ op2: 0,
+ op3: 0,
+ op4: 0,
+ _ty: PhantomData,
+ }
+ }
+ }
+}
+
+macro_rules! unsized_unary_op_lifter {
+ ($name:ident, $op:ident, $result:ty) => {
+ pub fn $name<'a, E>(&'a self, expr: E)
+ -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, $result>
+ where
+ E: Liftable<'a, A, Result=ValueExpr>
+ {
+ use binaryninjacore_sys::BNLowLevelILOperation::$op;
+ use binaryninjacore_sys::BNLowLevelILAddExpr;
+
+ let expr = E::lift(self, expr);
+
+ let expr_idx = unsafe {
+ BNLowLevelILAddExpr(self.handle, $op, 0, 0,
+ expr.expr_idx as u64, 0, 0, 0)
+ };
+
+ Expression {
+ function: self,
+ expr_idx: expr_idx,
+ _ty: PhantomData,
+ }
+ }
+ }
+}
+
+macro_rules! sized_unary_op_lifter {
+ ($name:ident, $op:ident, $result:ty) => {
+ pub fn $name<'a, E>(&'a self, size: usize, expr: E)
+ -> ExpressionBuilder<'a, A, $result>
+ where
+ E: LiftableWithSize<'a, A>
+ {
+ use binaryninjacore_sys::BNLowLevelILOperation::$op;
+
+ let expr = E::lift_with_size(self, expr, size);
+
+ ExpressionBuilder {
+ function: self,
+ op: $op,
+ size: size,
+ flags: 0,
+ op1: expr.expr_idx as u64,
+ op2: 0,
+ op3: 0,
+ op4: 0,
+ _ty: PhantomData,
+ }
+ }
+ }
+}
+
+macro_rules! size_changing_unary_op_lifter {
+ ($name:ident, $op:ident, $result:ty) => {
+ pub fn $name<'a, E>(&'a self, size: usize, expr: E)
+ -> ExpressionBuilder<'a, A, $result>
+ where
+ E: LiftableWithSize<'a, A>
+ {
+ use binaryninjacore_sys::BNLowLevelILOperation::$op;
+
+ let expr = E::lift(self, expr);
+
+ ExpressionBuilder {
+ function: self,
+ op: $op,
+ size: size,
+ flags: 0,
+ op1: expr.expr_idx as u64,
+ op2: 0,
+ op3: 0,
+ op4: 0,
+ _ty: PhantomData,
+ }
+ }
+ }
+}
+
+macro_rules! binary_op_lifter {
+ ($name:ident, $op:ident) => {
+ pub fn $name<'a, L, R>(&'a self, size: usize, left: L, right: R)
+ -> ExpressionBuilder<'a, A, ValueExpr>
+ where
+ L: LiftableWithSize<'a, A>,
+ R: LiftableWithSize<'a, A>
+ {
+ use binaryninjacore_sys::BNLowLevelILOperation::$op;
+
+ let left = L::lift_with_size(self, left, size);
+ let right = R::lift_with_size(self, right, size);
+
+ ExpressionBuilder {
+ function: self,
+ op: $op,
+ size: size,
+ flags: 0,
+ op1: left.expr_idx as u64,
+ op2: right.expr_idx as u64,
+ op3: 0,
+ op4: 0,
+ _ty: PhantomData,
+ }
+ }
+ }
+}
+
+macro_rules! binary_op_carry_lifter {
+ ($name:ident, $op:ident) => {
+ pub fn $name<'a, L, R, C>(&'a self, size: usize, left: L, right: R, carry: C)
+ -> ExpressionBuilder<'a, A, ValueExpr>
+ where
+ L: LiftableWithSize<'a, A>,
+ R: LiftableWithSize<'a, A>,
+ C: LiftableWithSize<'a, A>,
+ {
+ use binaryninjacore_sys::BNLowLevelILOperation::$op;
+
+ let left = L::lift_with_size(self, left, size);
+ let right = R::lift_with_size(self, right, size);
+ let carry = C::lift_with_size(self, carry, 1); // TODO 0?
+
+ ExpressionBuilder {
+ function: self,
+ op: $op,
+ size: size,
+ flags: 0,
+ op1: left.expr_idx as u64,
+ op2: right.expr_idx as u64,
+ op3: carry.expr_idx as u64,
+ op4: 0,
+ _ty: PhantomData,
+ }
+ }
+ }
+}
+
+impl<A> Function<A, Mutable, NonSSA<LiftedNonSSA>>
+where
+ A: Architecture,
+{
+ pub fn expression<'a, E: Liftable<'a, A>>(&'a self, expr: E)
+ -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, E::Result>
+ {
+ E::lift(self, expr)
+ }
+
+ pub fn instruction<'a, E: Liftable<'a, A>>(&'a self, expr: E)
+ {
+ let expr = self.expression(expr);
+
+ unsafe {
+ use binaryninjacore_sys::BNLowLevelILAddInstruction;
+ BNLowLevelILAddInstruction(self.handle, expr.expr_idx);
+ }
+ }
+
+
+ pub unsafe fn replace_expression<'a, E: Liftable<'a, A>>(
+ &'a self,
+ replaced_expr_index: usize,
+ replacement: E)
+ {
+ unsafe {
+ use binaryninjacore_sys::BNReplaceLowLevelILExpr;
+ use binaryninjacore_sys::BNGetLowLevelILExprCount;
+
+ if replaced_expr_index >= BNGetLowLevelILExprCount(self.handle) {
+ panic!("bad expr idx used: {} exceeds function bounds", replaced_expr_index);
+ }
+
+ let expr = self.expression(replacement);
+ BNReplaceLowLevelILExpr(self.handle, replaced_expr_index, expr.expr_idx);
+ }
+ }
+
+ pub fn const_int(&self, size: usize, val: u64)
+ -> Expression<A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr>
+ {
+ use binaryninjacore_sys::BNLowLevelILOperation::LLIL_CONST;
+ use binaryninjacore_sys::BNLowLevelILAddExpr;
+
+ let expr_idx = unsafe {
+ BNLowLevelILAddExpr(self.handle, LLIL_CONST, size, 0,
+ val, 0, 0, 0)
+ };
+
+ Expression {
+ function: self,
+ expr_idx: expr_idx,
+ _ty: PhantomData,
+ }
+ }
+
+ pub fn const_ptr_sized(&self, size: usize, val: u64)
+ -> Expression<A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr>
+ {
+ use binaryninjacore_sys::BNLowLevelILOperation::LLIL_CONST_PTR;
+ use binaryninjacore_sys::BNLowLevelILAddExpr;
+
+ let expr_idx = unsafe {
+ BNLowLevelILAddExpr(self.handle, LLIL_CONST_PTR, size, 0,
+ val, 0, 0, 0)
+ };
+
+ Expression {
+ function: self,
+ expr_idx: expr_idx,
+ _ty: PhantomData,
+ }
+ }
+
+ pub fn const_ptr(&self, val: u64)
+ -> Expression<A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr>
+ {
+ self.const_ptr_sized(self.arch().address_size(), val)
+ }
+
+ pub fn trap(&self, val: u64)
+ -> Expression<A, Mutable, NonSSA<LiftedNonSSA>, VoidExpr>
+ {
+ use binaryninjacore_sys::BNLowLevelILOperation::LLIL_TRAP;
+ use binaryninjacore_sys::BNLowLevelILAddExpr;
+
+ let expr_idx = unsafe {
+ BNLowLevelILAddExpr(self.handle, LLIL_TRAP, 0, 0,
+ val, 0, 0, 0)
+ };
+
+ Expression {
+ function: self,
+ expr_idx: expr_idx,
+ _ty: PhantomData,
+ }
+ }
+
+ no_arg_lifter!(unimplemented, LLIL_UNIMPL, ValueExpr);
+ no_arg_lifter!(undefined, LLIL_UNDEF, VoidExpr);
+ no_arg_lifter!(nop, LLIL_NOP, VoidExpr);
+
+ no_arg_lifter!(no_ret, LLIL_NORET, VoidExpr);
+ no_arg_lifter!(syscall, LLIL_SYSCALL, VoidExpr);
+ no_arg_lifter!(bp, LLIL_BP, VoidExpr);
+
+ unsized_unary_op_lifter!(call, LLIL_CALL, VoidExpr);
+ unsized_unary_op_lifter!(ret, LLIL_RET, VoidExpr);
+ unsized_unary_op_lifter!(jump, LLIL_JUMP, VoidExpr);
+ // JumpTo TODO
+
+ pub fn if_expr<'a: 'b, 'b, C>(&'a self, cond: C, t: &'b Label, f: &'b Label)
+ -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, VoidExpr>
+ where
+ C: Liftable<'b, A, Result=ValueExpr>,
+ {
+ use binaryninjacore_sys::BNLowLevelILIf;
+
+ let cond = C::lift(self, cond);
+
+ let expr_idx = unsafe {
+ BNLowLevelILIf(self.handle, cond.expr_idx as u64,
+ &t.0 as *const _ as *mut _,
+ &f.0 as *const _ as *mut _)
+ };
+
+ Expression {
+ function: self,
+ expr_idx: expr_idx,
+ _ty: PhantomData,
+ }
+ }
+
+ pub fn goto<'a: 'b, 'b>(&'a self, l: &'b Label)
+ -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, VoidExpr>
+ {
+ use binaryninjacore_sys::BNLowLevelILGoto;
+
+ let expr_idx = unsafe {
+ BNLowLevelILGoto(self.handle, &l.0 as *const _ as *mut _)
+ };
+
+ Expression {
+ function: self,
+ expr_idx: expr_idx,
+ _ty: PhantomData,
+ }
+ }
+
+ pub fn reg<R: Into<Register<A::Register>>>(&self, size: usize, reg: R)
+ -> Expression<A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr>
+ {
+ use binaryninjacore_sys::BNLowLevelILOperation::LLIL_REG;
+ use binaryninjacore_sys::BNLowLevelILAddExpr;
+
+ // TODO verify valid id
+ let reg = match reg.into() {
+ Register::ArchReg(r) => r.id(),
+ Register::Temp(r) => 0x8000_0000 | r,
+ };
+
+ let expr_idx = unsafe {
+ BNLowLevelILAddExpr(self.handle, LLIL_REG, size, 0,
+ reg as u64, 0, 0, 0)
+ };
+
+ Expression {
+ function: self,
+ expr_idx: expr_idx,
+ _ty: PhantomData,
+ }
+ }
+
+ pub fn set_reg<'a, R, E>(&'a self, size: usize, dest_reg: R, expr: E)
+ -> ExpressionBuilder<'a, A, VoidExpr>
+ where
+ R: Into<Register<A::Register>>,
+ E: LiftableWithSize<'a, A>
+ {
+ use binaryninjacore_sys::BNLowLevelILOperation::LLIL_SET_REG;
+
+ // TODO verify valid id
+ let dest_reg = match dest_reg.into() {
+ Register::ArchReg(r) => r.id(),
+ Register::Temp(r) => 0x8000_0000 | r,
+ };
+
+ let expr = E::lift_with_size(self, expr, size);
+
+ ExpressionBuilder {
+ function: self,
+ op: LLIL_SET_REG,
+ size: size,
+ flags: 0,
+ op1: dest_reg as u64,
+ op2: expr.expr_idx as u64,
+ op3: 0,
+ op4: 0,
+ _ty: PhantomData,
+ }
+ }
+
+ pub fn set_reg_split<'a, H, L, E>(&'a self, size: usize, hi_reg: H, lo_reg: L, expr: E)
+ -> ExpressionBuilder<'a, A, VoidExpr>
+ where
+ H: Into<Register<A::Register>>,
+ L: Into<Register<A::Register>>,
+ E: LiftableWithSize<'a, A>
+ {
+ use binaryninjacore_sys::BNLowLevelILOperation::LLIL_SET_REG_SPLIT;
+
+ // TODO verify valid id
+ let hi_reg = match hi_reg.into() {
+ Register::ArchReg(r) => r.id(),
+ Register::Temp(r) => 0x8000_0000 | r,
+ };
+
+ // TODO verify valid id
+ let lo_reg = match lo_reg.into() {
+ Register::ArchReg(r) => r.id(),
+ Register::Temp(r) => 0x8000_0000 | r,
+ };
+
+ let expr = E::lift_with_size(self, expr, size);
+
+ ExpressionBuilder {
+ function: self,
+ op: LLIL_SET_REG_SPLIT,
+ size: size,
+ flags: 0,
+ op1: hi_reg as u64,
+ op2: lo_reg as u64,
+ op3: expr.expr_idx as u64,
+ op4: 0,
+ _ty: PhantomData,
+ }
+ }
+
+ pub fn flag(&self, flag: A::Flag)
+ -> Expression<A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr>
+ {
+ use binaryninjacore_sys::BNLowLevelILOperation::LLIL_FLAG;
+ use binaryninjacore_sys::BNLowLevelILAddExpr;
+
+ // TODO verify valid id
+ let expr_idx = unsafe {
+ BNLowLevelILAddExpr(self.handle, LLIL_FLAG, 0, 0,
+ flag.id() as u64, 0, 0, 0)
+ };
+
+ Expression {
+ function: self,
+ expr_idx: expr_idx,
+ _ty: PhantomData,
+ }
+ }
+
+ pub fn flag_cond(&self, cond: FlagCondition)
+ -> Expression<A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr>
+ {
+ use binaryninjacore_sys::BNLowLevelILOperation::LLIL_FLAG_COND;
+ use binaryninjacore_sys::BNLowLevelILAddExpr;
+
+ // TODO verify valid id
+ let expr_idx = unsafe {
+ BNLowLevelILAddExpr(self.handle, LLIL_FLAG_COND, 0, 0,
+ cond as u64, 0, 0, 0)
+ };
+
+ Expression {
+ function: self,
+ expr_idx: expr_idx,
+ _ty: PhantomData,
+ }
+ }
+
+ pub fn flag_group(&self, group: A::FlagGroup)
+ -> Expression<A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr>
+ {
+ use binaryninjacore_sys::BNLowLevelILOperation::LLIL_FLAG_GROUP;
+ use binaryninjacore_sys::BNLowLevelILAddExpr;
+
+ // TODO verify valid id
+ let expr_idx = unsafe {
+ BNLowLevelILAddExpr(self.handle, LLIL_FLAG_GROUP, 0, 0,
+ group.id() as u64, 0, 0, 0)
+ };
+
+ Expression {
+ function: self,
+ expr_idx: expr_idx,
+ _ty: PhantomData,
+ }
+ }
+
+ pub fn set_flag<'a, E>(&'a self, dest_flag: A::Flag, expr: E)
+ -> ExpressionBuilder<'a, A, VoidExpr>
+ where
+ E: LiftableWithSize<'a, A>
+ {
+ use binaryninjacore_sys::BNLowLevelILOperation::LLIL_SET_FLAG;
+
+ // TODO verify valid id
+
+ let expr = E::lift_with_size(self, expr, 0);
+
+ ExpressionBuilder {
+ function: self,
+ op: LLIL_SET_FLAG,
+ size: 0,
+ flags: 0,
+ op1: dest_flag.id() as u64,
+ op2: expr.expr_idx as u64,
+ op3: 0,
+ op4: 0,
+ _ty: PhantomData,
+ }
+ }
+
+ /*
+ * TODO
+ FlagBit(usize, Flag<A>, u64),
+ */
+
+ pub fn load<'a, E>(&'a self, size: usize, source_mem: E)
+ -> ExpressionBuilder<'a, A, ValueExpr>
+ where
+ E: Liftable<'a, A, Result=ValueExpr>
+ {
+ use binaryninjacore_sys::BNLowLevelILOperation::LLIL_LOAD;
+
+ let expr = E::lift(self, source_mem);
+
+ ExpressionBuilder {
+ function: self,
+ op: LLIL_LOAD,
+ size: size,
+ flags: 0,
+ op1: expr.expr_idx as u64,
+ op2: 0,
+ op3: 0,
+ op4: 0,
+ _ty: PhantomData,
+ }
+ }
+
+ pub fn store<'a, D, V>(&'a self, size: usize, dest_mem: D, value: V)
+ -> ExpressionBuilder<'a, A, VoidExpr>
+ where
+ D: Liftable<'a, A, Result=ValueExpr>,
+ V: LiftableWithSize<'a, A>
+ {
+ use binaryninjacore_sys::BNLowLevelILOperation::LLIL_STORE;
+
+ let dest_mem = D::lift(self, dest_mem);
+ let value = V::lift_with_size(self, value, size);
+
+ ExpressionBuilder {
+ function: self,
+ op: LLIL_STORE,
+ size: size,
+ flags: 0,
+ op1: dest_mem.expr_idx as u64,
+ op2: value.expr_idx as u64,
+ op3: 0,
+ op4: 0,
+ _ty: PhantomData,
+ }
+ }
+
+ sized_unary_op_lifter!(push, LLIL_PUSH, VoidExpr);
+ sized_no_arg_lifter!(pop, LLIL_POP, ValueExpr);
+
+ size_changing_unary_op_lifter!(unimplemented_mem, LLIL_UNIMPL_MEM, ValueExpr);
+
+ sized_unary_op_lifter!(neg, LLIL_NEG, ValueExpr);
+ sized_unary_op_lifter!(not, LLIL_NOT, ValueExpr);
+
+ size_changing_unary_op_lifter!(sx, LLIL_SX, ValueExpr);
+ size_changing_unary_op_lifter!(zx, LLIL_ZX, ValueExpr);
+ size_changing_unary_op_lifter!(low_part, LLIL_LOW_PART, ValueExpr);
+
+ binary_op_lifter!(add, LLIL_ADD);
+ binary_op_lifter!(add_overflow, LLIL_ADD_OVERFLOW);
+ binary_op_lifter!(sub, LLIL_SUB);
+ binary_op_lifter!(and, LLIL_AND);
+ binary_op_lifter!(or, LLIL_OR);
+ binary_op_lifter!(xor, LLIL_XOR);
+ binary_op_lifter!(lsl, LLIL_LSL);
+ binary_op_lifter!(lsr, LLIL_LSR);
+ binary_op_lifter!(asr, LLIL_ASR);
+
+ binary_op_lifter!(rol, LLIL_ROL);
+ binary_op_lifter!(rlc, LLIL_RLC);
+ binary_op_lifter!(ror, LLIL_ROR);
+ binary_op_lifter!(rrc, LLIL_RRC);
+ binary_op_lifter!(mul, LLIL_MUL);
+ binary_op_lifter!(muls_dp, LLIL_MULS_DP);
+ binary_op_lifter!(mulu_dp, LLIL_MULU_DP);
+ binary_op_lifter!(divs, LLIL_DIVS);
+ binary_op_lifter!(divu, LLIL_DIVU);
+ binary_op_lifter!(mods, LLIL_MODS);
+ binary_op_lifter!(modu, LLIL_MODU);
+
+ binary_op_carry_lifter!(adc, LLIL_ADC);
+ binary_op_carry_lifter!(sbb, LLIL_SBB);
+
+
+ /*
+ DivsDp(usize, Expr, Expr, Expr, Option<A::FlagWrite>),
+ DivuDp(usize, Expr, Expr, Expr, Option<A::FlagWrite>),
+ ModsDp(usize, Expr, Expr, Expr, Option<A::FlagWrite>),
+ ModuDp(usize, Expr, Expr, Expr, Option<A::FlagWrite>),
+ */
+
+ // FlagCond(u32), // TODO
+
+ binary_op_lifter!(cmp_e, LLIL_CMP_E);
+ binary_op_lifter!(cmp_ne, LLIL_CMP_NE);
+ binary_op_lifter!(cmp_slt, LLIL_CMP_SLT);
+ binary_op_lifter!(cmp_ult, LLIL_CMP_ULT);
+ binary_op_lifter!(cmp_sle, LLIL_CMP_SLE);
+ binary_op_lifter!(cmp_ule, LLIL_CMP_ULE);
+ binary_op_lifter!(cmp_sge, LLIL_CMP_SGE);
+ binary_op_lifter!(cmp_uge, LLIL_CMP_UGE);
+ binary_op_lifter!(cmp_sgt, LLIL_CMP_SGT);
+ binary_op_lifter!(cmp_ugt, LLIL_CMP_UGT);
+ binary_op_lifter!(test_bit, LLIL_TEST_BIT);
+
+ // TODO no flags
+ size_changing_unary_op_lifter!(bool_to_int, LLIL_BOOL_TO_INT, ValueExpr);
+
+ pub fn current_address(&self) -> u64 {
+ use binaryninjacore_sys::BNLowLevelILGetCurrentAddress;
+ unsafe {
+ BNLowLevelILGetCurrentAddress(self.handle)
+ }
+ }
+
+ pub fn set_current_address<L: Into<Location>>(&self, loc: L) {
+ use binaryninjacore_sys::BNLowLevelILSetCurrentAddress;
+
+ let loc: Location = loc.into();
+ let arch = loc.arch.unwrap_or_else(|| *self.arch().as_ref());
+
+ unsafe { BNLowLevelILSetCurrentAddress(self.handle, arch.0, loc.addr); }
+ }
+
+ pub fn label_for_address<L: Into<Location>>(&self, loc: L) -> Option<&Label> {
+ use binaryninjacore_sys::BNGetLowLevelILLabelForAddress;
+
+ let loc: Location = loc.into();
+ let arch = loc.arch.unwrap_or_else(|| *self.arch().as_ref());
+
+ let res = unsafe {
+ BNGetLowLevelILLabelForAddress(self.handle, arch.0, loc.addr)
+ };
+
+ if res.is_null() {
+ None
+ } else {
+ Some(unsafe { &*(res as *mut Label) })
+ }
+ }
+
+ pub fn mark_label(&self, label: &mut Label) {
+ use binaryninjacore_sys::BNLowLevelILMarkLabel;
+
+ unsafe {
+ BNLowLevelILMarkLabel(self.handle, &mut label.0 as *mut _);
+ }
+ }
+}
+
+use binaryninjacore_sys::BNLowLevelILLabel;
+
+#[repr(C)]
+pub struct Label(BNLowLevelILLabel);
+impl Label {
+ pub fn new() -> Self {
+ use binaryninjacore_sys::BNLowLevelILInitLabel;
+
+ unsafe {
+ let mut res = Label(mem::uninitialized());
+ BNLowLevelILInitLabel(&mut res.0 as *mut _);
+ res
+ }
+ }
+}
+
+
diff --git a/rust/src/llil/mod.rs b/rust/src/llil/mod.rs
new file mode 100644
index 00000000..c3b1b350
--- /dev/null
+++ b/rust/src/llil/mod.rs
@@ -0,0 +1,80 @@
+use std::fmt;
+
+// TODO provide some way to forbid emitting register reads for certain registers
+// also writing for certain registers (e.g. zero register must prohibit il.set_reg and il.reg
+// (replace with nop or const(0) respectively)
+// requirements on load/store memory address sizes?
+// can reg/set_reg be used with sizes that differ from what is in BNRegisterInfo?
+
+use crate::architecture::Register as ArchReg;
+use crate::architecture::Architecture;
+use crate::function::Location;
+
+mod function;
+mod instruction;
+mod expression;
+mod lifting;
+mod block;
+pub mod operation;
+
+pub use self::function::*;
+pub use self::instruction::*;
+pub use self::expression::*;
+pub use self::lifting::{Liftable, LiftableWithSize, Label, ExpressionBuilder, FlagWriteOp, RegisterOrConstant};
+pub use self::lifting::get_default_flag_write_llil;
+pub use self::lifting::get_default_flag_cond_llil;
+
+pub use self::block::Block as LowLevelBlock;
+pub use self::block::BlockIter as LowLevelBlockIter;
+
+pub type Lifter<Arch> = Function<Arch, Mutable, NonSSA<LiftedNonSSA>>;
+pub type LiftedFunction<Arch> = Function<Arch, Finalized, NonSSA<LiftedNonSSA>>;
+pub type LiftedExpr<'a, Arch> = Expression<'a, Arch, Mutable, NonSSA<LiftedNonSSA>, ValueExpr>;
+pub type RegularFunction<Arch> = Function<Arch, Finalized, NonSSA<RegularNonSSA>>;
+pub type SSAFunction<Arch> = Function<Arch, Finalized, SSA>;
+
+#[derive(Copy, Clone)]
+pub enum Register<R: ArchReg> {
+ ArchReg(R),
+ Temp(u32),
+}
+
+impl<R: ArchReg> Register<R> {
+ fn id(&self) -> u32 {
+ match *self {
+ Register::ArchReg(ref r) => r.id(),
+ Register::Temp(id) => 0x8000_0000 | id,
+ }
+ }
+}
+
+impl<R: ArchReg> fmt::Debug for Register<R> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match *self {
+ Register::ArchReg(ref r) => write!(f, "{}", r.name().as_ref()),
+ Register::Temp(id) => write!(f, "temp{}", id),
+ }
+ }
+}
+
+#[derive(Copy, Clone, Debug)]
+pub enum SSARegister<R: ArchReg> {
+ Full(Register<R>, u32), // no such thing as partial access to a temp register, I think
+ Partial(R, u32, R), // partial accesses only possible for arch registers, I think
+}
+
+impl<R: ArchReg> SSARegister<R> {
+ pub fn version(&self) -> u32 {
+ match *self {
+ SSARegister::Full(_, ver) |
+ SSARegister::Partial(_, ver, _) => ver
+ }
+ }
+}
+
+pub enum VisitorAction {
+ Descend,
+ Sibling,
+ Halt,
+}
+
diff --git a/rust/src/llil/operation.rs b/rust/src/llil/operation.rs
new file mode 100644
index 00000000..81d72d57
--- /dev/null
+++ b/rust/src/llil/operation.rs
@@ -0,0 +1,765 @@
+use binaryninjacore_sys::BNLowLevelILInstruction;
+
+use std::marker::PhantomData;
+use std::mem;
+
+use super::*;
+
+pub struct Operation<'func, A, M, F, O>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+ O: OperationArguments,
+{
+ pub(crate) function: &'func Function<A, M, F>,
+ pub(crate) op: BNLowLevelILInstruction,
+ _args: PhantomData<O>,
+}
+
+impl<'func, A, M, F, O> Operation<'func, A, M, F, O>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+ O: OperationArguments,
+{
+ pub(crate) fn new(function: &'func Function<A, M, F>, op: BNLowLevelILInstruction) -> Self {
+ Self {
+ function: function,
+ op: op,
+ _args: PhantomData,
+ }
+ }
+
+ pub fn address(&self) -> u64 {
+ self.op.address
+ }
+}
+
+impl<'func, A, M, O> Operation<'func, A, M, NonSSA<LiftedNonSSA>, O>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ O: OperationArguments,
+{
+ pub fn flag_write(&self) -> Option<A::FlagWrite> {
+ match self.op.flags {
+ 0 => None,
+ id => self.function.arch().flag_write_from_id(id)
+ }
+ }
+}
+
+// LLIL_NOP, LLIL_NORET, LLIL_BP, LLIL_UNDEF, LLIL_UNIMPL
+pub struct NoArgs;
+
+
+
+// LLIL_POP
+pub struct Pop;
+
+impl<'func, A, M, F> Operation<'func, A, M, F, Pop>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn size(&self) -> usize {
+ self.op.size
+ }
+}
+
+
+
+
+// LLIL_SYSCALL, LLIL_SYSCALL_SSA
+pub struct Syscall;
+
+
+
+
+// LLIL_SET_REG, LLIL_SET_REG_SSA, LLIL_SET_REG_PARTIAL_SSA
+pub struct SetReg;
+
+impl<'func, A, M, V> Operation<'func, A, M, NonSSA<V>, SetReg>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ V: NonSSAVariant,
+{
+ pub fn size(&self) -> usize {
+ self.op.size
+ }
+
+ pub fn dest_reg(&self) -> Register<A::Register> {
+ let raw_id = self.op.operands[0] as u32;
+
+ if raw_id >= 0x8000_0000 {
+ Register::Temp(raw_id & 0x7fff_ffff)
+ } else {
+ self.function.arch().register_from_id(raw_id)
+ .map(Register::ArchReg)
+ .unwrap_or_else(|| {
+ error!("got garbage register from LLIL_SET_REG @ 0x{:x}",
+ self.op.address);
+
+ Register::Temp(0)
+ })
+ }
+ }
+
+ pub fn source_expr(&self) -> Expression<'func, A, M, NonSSA<V>, ValueExpr> {
+ Expression {
+ function: self.function,
+ expr_idx: self.op.operands[1] as usize,
+ _ty: PhantomData,
+ }
+ }
+}
+
+
+// LLIL_SET_REG_SPLIT, LLIL_SET_REG_SPLIT_SSA
+pub struct SetRegSplit;
+
+impl<'func, A, M, V> Operation<'func, A, M, NonSSA<V>, SetRegSplit>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ V: NonSSAVariant,
+{
+ pub fn size(&self) -> usize {
+ self.op.size
+ }
+
+ pub fn dest_reg_high(&self) -> Register<A::Register> {
+ let raw_id = self.op.operands[0] as u32;
+
+ if raw_id >= 0x8000_0000 {
+ Register::Temp(raw_id & 0x7fff_ffff)
+ } else {
+ self.function.arch().register_from_id(raw_id)
+ .map(Register::ArchReg)
+ .unwrap_or_else(|| {
+ error!("got garbage register from LLIL_SET_REG_SPLIT @ 0x{:x}",
+ self.op.address);
+
+ Register::Temp(0)
+ })
+ }
+ }
+
+ pub fn dest_reg_low(&self) -> Register<A::Register> {
+ let raw_id = self.op.operands[1] as u32;
+
+ if raw_id >= 0x8000_0000 {
+ Register::Temp(raw_id & 0x7fff_ffff)
+ } else {
+ self.function.arch().register_from_id(raw_id)
+ .map(Register::ArchReg)
+ .unwrap_or_else(|| {
+ error!("got garbage register from LLIL_SET_REG_SPLIT @ 0x{:x}",
+ self.op.address);
+
+ Register::Temp(0)
+ })
+ }
+ }
+
+ pub fn source_expr(&self) -> Expression<'func, A, M, NonSSA<V>, ValueExpr> {
+ Expression {
+ function: self.function,
+ expr_idx: self.op.operands[2] as usize,
+ _ty: PhantomData,
+ }
+ }
+}
+
+
+
+
+// LLIL_SET_FLAG, LLIL_SET_FLAG_SSA
+pub struct SetFlag;
+
+impl<'func, A, M, V> Operation<'func, A, M, NonSSA<V>, SetFlag>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ V: NonSSAVariant,
+{
+ pub fn source_expr(&self) -> Expression<'func, A, M, NonSSA<V>, ValueExpr> {
+ Expression {
+ function: self.function,
+ expr_idx: self.op.operands[1] as usize,
+ _ty: PhantomData,
+ }
+ }
+}
+
+
+// LLIL_LOAD, LLIL_LOAD_SSA
+pub struct Load;
+
+impl<'func, A, M, V> Operation<'func, A, M, NonSSA<V>, Load>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ V: NonSSAVariant,
+{
+ pub fn size(&self) -> usize {
+ self.op.size
+ }
+
+ pub fn source_mem_expr(&self) -> Expression<'func, A, M, NonSSA<V>, ValueExpr> {
+ Expression {
+ function: self.function,
+ expr_idx: self.op.operands[0] as usize,
+ _ty: PhantomData,
+ }
+ }
+}
+
+
+
+// LLIL_STORE, LLIL_STORE_SSA
+pub struct Store;
+
+impl<'func, A, M, V> Operation<'func, A, M, NonSSA<V>, Store>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ V: NonSSAVariant,
+{
+ pub fn size(&self) -> usize {
+ self.op.size
+ }
+
+ pub fn dest_mem_expr(&self) -> Expression<'func, A, M, NonSSA<V>, ValueExpr> {
+ Expression {
+ function: self.function,
+ expr_idx: self.op.operands[0] as usize,
+ _ty: PhantomData,
+ }
+ }
+
+ pub fn source_expr(&self) -> Expression<'func, A, M, NonSSA<V>, ValueExpr> {
+ Expression {
+ function: self.function,
+ expr_idx: self.op.operands[1] as usize,
+ _ty: PhantomData,
+ }
+ }
+}
+
+
+
+// LLIL_REG, LLIL_REG_SSA, LLIL_REG_SSA_PARTIAL
+pub struct Reg;
+
+impl<'func, A, M, V> Operation<'func, A, M, NonSSA<V>, Reg>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ V: NonSSAVariant,
+{
+ pub fn size(&self) -> usize {
+ self.op.size
+ }
+
+ pub fn source_reg(&self) -> Register<A::Register> {
+ let raw_id = self.op.operands[0] as u32;
+
+ if raw_id >= 0x8000_0000 {
+ Register::Temp(raw_id & 0x7fff_ffff)
+ } else {
+ self.function.arch().register_from_id(raw_id)
+ .map(Register::ArchReg)
+ .unwrap_or_else(|| {
+ error!("got garbage register from LLIL_REG @ 0x{:x}",
+ self.op.address);
+
+ Register::Temp(0)
+ })
+ }
+ }
+}
+
+
+
+// LLIL_FLAG, LLIL_FLAG_SSA
+pub struct Flag;
+
+
+
+
+// LLIL_FLAG_BIT, LLIL_FLAG_BIT_SSA
+pub struct FlagBit;
+
+
+
+// LLIL_JUMP
+pub struct Jump;
+
+impl<'func, A, M, F> Operation<'func, A, M, F, Jump>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn target(&self) -> Expression<'func, A, M, F, ValueExpr> {
+ Expression {
+ function: self.function,
+ expr_idx: self.op.operands[0] as usize,
+ _ty: PhantomData,
+ }
+ }
+}
+
+
+
+// LLIL_JUMP_TO
+pub struct JumpTo;
+
+impl<'func, A, M, F> Operation<'func, A, M, F, JumpTo>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn target(&self) -> Expression<'func, A, M, F, ValueExpr> {
+ Expression {
+ function: self.function,
+ expr_idx: self.op.operands[0] as usize,
+ _ty: PhantomData,
+ }
+ }
+ // TODO target list
+}
+
+
+
+// LLIL_CALL, LLIL_CALL_SSA
+pub struct Call;
+
+impl<'func, A, M, V> Operation<'func, A, M, NonSSA<V>, Call>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ V: NonSSAVariant,
+{
+ pub fn target(&self) -> Expression<'func, A, M, NonSSA<V>, ValueExpr> {
+ Expression {
+ function: self.function,
+ expr_idx: self.op.operands[0] as usize,
+ _ty: PhantomData,
+ }
+ }
+
+ pub fn stack_adjust(&self) -> Option<u64> {
+ use binaryninjacore_sys::BNLowLevelILOperation::LLIL_CALL_STACK_ADJUST;
+
+ if self.op.operation == LLIL_CALL_STACK_ADJUST {
+ Some(self.op.operands[1])
+ } else {
+ None
+ }
+ }
+}
+
+
+
+// LLIL_RET
+pub struct Ret;
+
+impl<'func, A, M, F> Operation<'func, A, M, F, Ret>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn target(&self) -> Expression<'func, A, M, F, ValueExpr> {
+ Expression {
+ function: self.function,
+ expr_idx: self.op.operands[0] as usize,
+ _ty: PhantomData,
+ }
+ }
+}
+
+
+
+// LLIL_IF
+pub struct If;
+
+impl<'func, A, M, F> Operation<'func, A, M, F, If>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn condition(&self) -> Expression<'func, A, M, F, ValueExpr> {
+ Expression {
+ function: self.function,
+ expr_idx: self.op.operands[0] as usize,
+ _ty: PhantomData,
+ }
+ }
+
+ pub fn true_target(&self) -> Instruction<'func, A, M, F> {
+ Instruction {
+ function: self.function,
+ instr_idx: self.op.operands[1] as usize,
+ }
+ }
+
+ pub fn false_target(&self) -> Instruction<'func, A, M, F> {
+ Instruction {
+ function: self.function,
+ instr_idx: self.op.operands[2] as usize,
+ }
+ }
+}
+
+
+
+// LLIL_GOTO
+pub struct Goto;
+
+impl<'func, A, M, F> Operation<'func, A, M, F, Goto>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn target(&self) -> Instruction<'func, A, M, F> {
+ Instruction {
+ function: self.function,
+ instr_idx: self.op.operands[0] as usize,
+ }
+ }
+}
+
+
+
+// LLIL_FLAG_COND
+pub struct FlagCond;
+
+
+
+// LLIL_FLAG_GROUP
+pub struct FlagGroup;
+
+impl<'func, A, M> Operation<'func, A, M, NonSSA<LiftedNonSSA>, FlagGroup>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+{
+ pub fn flag_group(&self) -> A::FlagGroup {
+ let id = self.op.operands[0] as u32;
+ self.function.arch().flag_group_from_id(id).unwrap()
+ }
+}
+
+
+
+// LLIL_TRAP
+pub struct Trap;
+
+impl<'func, A, M, F> Operation<'func, A, M, F, Trap>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn vector(&self) -> u64 {
+ self.op.operands[0]
+ }
+}
+
+
+
+// LLIL_REG_PHI
+pub struct RegPhi;
+
+
+
+// LLIL_FLAG_PHI
+pub struct FlagPhi;
+
+
+
+// LLIL_MEM_PHI
+pub struct MemPhi;
+
+
+
+// LLIL_CONST, LLIL_CONST_PTR
+pub struct Const;
+
+impl<'func, A, M, F> Operation<'func, A, M, F, Const>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn size(&self) -> usize {
+ self.op.size
+ }
+
+ pub fn value(&self) -> u64 {
+ #[cfg(debug_assertions)]
+ {
+ let raw = self.op.operands[0] as i64;
+
+ let is_safe = match raw.overflowing_shr(self.op.size as u32 * 8) {
+ (_, true) => true,
+ (res, false) => [-1, 0].contains(&res),
+ };
+
+ if !is_safe {
+ error!("il expr @ {:x} contains constant 0x{:x} as {} byte value (doesn't fit!)",
+ self.op.address, self.op.operands[0], self.op.size);
+ }
+ }
+
+ let mut mask = -1i64 as u64;
+
+ if self.op.size < mem::size_of::<u64>() {
+ mask <<= self.op.size * 8;
+ mask = !mask;
+ }
+
+ self.op.operands[0] & mask
+ }
+}
+
+
+
+// LLIL_ADD, LLIL_SUB, LLIL_AND, LLIL_OR
+// LLIL_XOR, LLIL_LSL, LLIL_LSR, LLIL_ASR
+// LLIL_ROL, LLIL_ROR, LLIL_MUL, LLIL_MULU_DP,
+// LLIL_MULS_DP, LLIL_DIVU, LLIL_DIVS, LLIL_MODU,
+// LLIL_MODS
+pub struct BinaryOp;
+
+impl<'func, A, M, F> Operation<'func, A, M, F, BinaryOp>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn size(&self) -> usize {
+ self.op.size
+ }
+
+ pub fn left(&self) -> Expression<'func, A, M, F, ValueExpr> {
+ Expression {
+ function: self.function,
+ expr_idx: self.op.operands[0] as usize,
+ _ty: PhantomData,
+ }
+ }
+
+ pub fn right(&self) -> Expression<'func, A, M, F, ValueExpr> {
+ Expression {
+ function: self.function,
+ expr_idx: self.op.operands[1] as usize,
+ _ty: PhantomData,
+ }
+ }
+}
+
+
+
+// LLIL_ADC, LLIL_SBB, LLIL_RLC, LLIL_RRC
+pub struct BinaryOpCarry;
+
+impl<'func, A, M, F> Operation<'func, A, M, F, BinaryOpCarry>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn size(&self) -> usize {
+ self.op.size
+ }
+
+ pub fn left(&self) -> Expression<'func, A, M, F, ValueExpr> {
+ Expression {
+ function: self.function,
+ expr_idx: self.op.operands[0] as usize,
+ _ty: PhantomData,
+ }
+ }
+
+ pub fn right(&self) -> Expression<'func, A, M, F, ValueExpr> {
+ Expression {
+ function: self.function,
+ expr_idx: self.op.operands[1] as usize,
+ _ty: PhantomData,
+ }
+ }
+
+ pub fn carry(&self) -> Expression<'func, A, M, F, ValueExpr> {
+ Expression {
+ function: self.function,
+ expr_idx: self.op.operands[2] as usize,
+ _ty: PhantomData,
+ }
+ }
+}
+
+
+
+// LLIL_DIVS_DP, LLIL_DIVU_DP, LLIL_MODU_DP, LLIL_MODS_DP
+pub struct DoublePrecDivOp;
+
+impl<'func, A, M, F> Operation<'func, A, M, F, DoublePrecDivOp>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn size(&self) -> usize {
+ self.op.size
+ }
+
+ pub fn high(&self) -> Expression<'func, A, M, F, ValueExpr> {
+ Expression {
+ function: self.function,
+ expr_idx: self.op.operands[0] as usize,
+ _ty: PhantomData,
+ }
+ }
+
+ pub fn low(&self) -> Expression<'func, A, M, F, ValueExpr> {
+ Expression {
+ function: self.function,
+ expr_idx: self.op.operands[1] as usize,
+ _ty: PhantomData,
+ }
+ }
+
+ pub fn right(&self) -> Expression<'func, A, M, F, ValueExpr> {
+ Expression {
+ function: self.function,
+ expr_idx: self.op.operands[2] as usize,
+ _ty: PhantomData,
+ }
+ }
+}
+
+
+
+// LLIL_PUSH, LLIL_NEG, LLIL_NOT, LLIL_SX,
+// LLIL_ZX, LLIL_LOW_PART, LLIL_BOOL_TO_INT, LLIL_UNIMPL_MEM
+pub struct UnaryOp;
+
+impl<'func, A, M, F> Operation<'func, A, M, F, UnaryOp>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn size(&self) -> usize {
+ self.op.size
+ }
+
+ pub fn operand(&self) -> Expression<'func, A, M, F, ValueExpr> {
+ Expression {
+ function: self.function,
+ expr_idx: self.op.operands[0] as usize,
+ _ty: PhantomData,
+ }
+ }
+}
+
+
+
+// LLIL_CMP_X
+pub struct Condition;
+
+impl<'func, A, M, F> Operation<'func, A, M, F, Condition>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn size(&self) -> usize {
+ self.op.size
+ }
+
+ pub fn left(&self) -> Expression<'func, A, M, F, ValueExpr> {
+ Expression {
+ function: self.function,
+ expr_idx: self.op.operands[0] as usize,
+ _ty: PhantomData,
+ }
+ }
+
+ pub fn right(&self) -> Expression<'func, A, M, F, ValueExpr> {
+ Expression {
+ function: self.function,
+ expr_idx: self.op.operands[1] as usize,
+ _ty: PhantomData,
+ }
+ }
+}
+
+
+// LLIL_UNIMPL_MEM
+pub struct UnimplMem;
+
+impl<'func, A, M, F> Operation<'func, A, M, F, UnimplMem>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn size(&self) -> usize {
+ self.op.size
+ }
+
+ pub fn mem_expr(&self) -> Expression<'func, A, M, F, ValueExpr> {
+ Expression {
+ function: self.function,
+ expr_idx: self.op.operands[0] as usize,
+ _ty: PhantomData,
+ }
+ }
+}
+
+// TODO TEST_BIT
+
+pub trait OperationArguments: 'static {}
+
+impl OperationArguments for NoArgs {}
+impl OperationArguments for Pop {}
+impl OperationArguments for Syscall {}
+impl OperationArguments for SetReg {}
+impl OperationArguments for SetRegSplit {}
+impl OperationArguments for SetFlag {}
+impl OperationArguments for Load {}
+impl OperationArguments for Store {}
+impl OperationArguments for Reg {}
+impl OperationArguments for Flag {}
+impl OperationArguments for FlagBit {}
+impl OperationArguments for Jump {}
+impl OperationArguments for JumpTo {}
+impl OperationArguments for Call {}
+impl OperationArguments for Ret {}
+impl OperationArguments for If {}
+impl OperationArguments for Goto {}
+impl OperationArguments for FlagCond {}
+impl OperationArguments for FlagGroup {}
+impl OperationArguments for Trap {}
+impl OperationArguments for RegPhi {}
+impl OperationArguments for FlagPhi {}
+impl OperationArguments for MemPhi {}
+impl OperationArguments for Const {}
+impl OperationArguments for BinaryOp {}
+impl OperationArguments for BinaryOpCarry {}
+impl OperationArguments for DoublePrecDivOp {}
+impl OperationArguments for UnaryOp {}
+impl OperationArguments for Condition {}
+impl OperationArguments for UnimplMem {}