summaryrefslogtreecommitdiff
path: root/rust/src/low_level_il
diff options
context:
space:
mode:
Diffstat (limited to 'rust/src/low_level_il')
-rw-r--r--rust/src/low_level_il/block.rs108
-rw-r--r--rust/src/low_level_il/expression.rs715
-rw-r--r--rust/src/low_level_il/function.rs275
-rw-r--r--rust/src/low_level_il/instruction.rs348
-rw-r--r--rust/src/low_level_il/lifting.rs1671
-rw-r--r--rust/src/low_level_il/operation.rs1367
6 files changed, 4484 insertions, 0 deletions
diff --git a/rust/src/low_level_il/block.rs b/rust/src/low_level_il/block.rs
new file mode 100644
index 00000000..8096c1aa
--- /dev/null
+++ b/rust/src/low_level_il/block.rs
@@ -0,0 +1,108 @@
+// Copyright 2021-2024 Vector 35 Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use std::fmt::Debug;
+use std::ops::Range;
+
+use crate::architecture::Architecture;
+use crate::basic_block::{BasicBlock, BlockContext};
+
+use super::*;
+
+#[derive(Copy)]
+pub struct LowLevelILBlock<'func, A, M, F>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub(crate) function: &'func LowLevelILFunction<A, M, F>,
+}
+
+impl<'func, A, M, F> BlockContext for LowLevelILBlock<'func, A, M, F>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ type Instruction = LowLevelILInstruction<'func, A, M, F>;
+ type InstructionIndex = LowLevelInstructionIndex;
+ type Iter = LowLevelILBlockIter<'func, A, M, F>;
+
+ fn start(&self, block: &BasicBlock<Self>) -> LowLevelILInstruction<'func, A, M, F> {
+ self.function
+ .instruction_from_index(block.start_index())
+ .unwrap()
+ }
+
+ fn iter(&self, block: &BasicBlock<Self>) -> LowLevelILBlockIter<'func, A, M, F> {
+ LowLevelILBlockIter {
+ function: self.function,
+ range: (block.start_index().0)..(block.end_index().0),
+ }
+ }
+}
+
+impl<'func, A, M, F> Debug for LowLevelILBlock<'func, A, M, F>
+where
+ A: 'func + Architecture + Debug,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.debug_struct("LowLevelILBlock")
+ .field("function", &self.function)
+ .finish()
+ }
+}
+
+impl<'func, A, M, F> Clone for LowLevelILBlock<'func, A, M, F>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn clone(&self) -> Self {
+ LowLevelILBlock {
+ function: self.function,
+ }
+ }
+}
+
+pub struct LowLevelILBlockIter<'func, A, M, F>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ function: &'func LowLevelILFunction<A, M, F>,
+ // TODO: Once step_trait is stable we can do Range<InstructionIndex>
+ range: Range<usize>,
+}
+
+impl<'func, A, M, F> Iterator for LowLevelILBlockIter<'func, A, M, F>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ type Item = LowLevelILInstruction<'func, A, M, F>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ self.range
+ .next()
+ .map(LowLevelInstructionIndex)
+ .and_then(|idx| self.function.instruction_from_index(idx))
+ }
+}
diff --git a/rust/src/low_level_il/expression.rs b/rust/src/low_level_il/expression.rs
new file mode 100644
index 00000000..2fc9f93d
--- /dev/null
+++ b/rust/src/low_level_il/expression.rs
@@ -0,0 +1,715 @@
+// Copyright 2021-2024 Vector 35 Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use binaryninjacore_sys::BNGetLowLevelILByIndex;
+use binaryninjacore_sys::BNLowLevelILInstruction;
+
+use super::operation;
+use super::operation::Operation;
+use super::VisitorAction;
+use super::*;
+use crate::architecture::Architecture;
+use std::fmt;
+use std::fmt::{Display, Formatter};
+use std::marker::PhantomData;
+
+/// Used as a marker for an [`LowLevelILExpression`] that **can** produce a value.
+#[derive(Copy, Clone, Debug)]
+pub struct ValueExpr;
+
+/// Used as a marker for an [`LowLevelILExpression`] 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 {}
+
+#[repr(transparent)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct LowLevelExpressionIndex(pub usize);
+
+impl Display for LowLevelExpressionIndex {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.write_fmt(format_args!("{}", self.0))
+ }
+}
+
+// TODO: Probably want to rename this with a LowLevelIL prefix to avoid collisions when we add handlers for other ILs
+pub trait ExpressionHandler<'func, A, M, F>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn kind(&self) -> LowLevelILExpressionKind<'func, A, M, F>;
+
+ fn visit_tree<T>(&self, f: &mut T) -> VisitorAction
+ where
+ T: FnMut(&LowLevelILExpression<'func, A, M, F, ValueExpr>) -> VisitorAction;
+}
+
+pub struct LowLevelILExpression<'func, A, M, F, R>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+ R: ExpressionResultType,
+{
+ pub(crate) function: &'func LowLevelILFunction<A, M, F>,
+ pub index: LowLevelExpressionIndex,
+
+ // tag the 'return' type of this expression
+ pub(crate) _ty: PhantomData<R>,
+}
+
+impl<'func, A, M, F, R> LowLevelILExpression<'func, A, M, F, R>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+ R: ExpressionResultType,
+{
+ pub(crate) fn new(
+ function: &'func LowLevelILFunction<A, M, F>,
+ index: LowLevelExpressionIndex,
+ ) -> Self {
+ // TODO: Validate expression here?
+ Self {
+ function,
+ index,
+ _ty: PhantomData,
+ }
+ }
+}
+
+impl<'func, A, M, F, R> fmt::Debug for LowLevelILExpression<'func, A, M, F, R>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+ R: ExpressionResultType,
+{
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ f.debug_struct("Expression")
+ .field("index", &self.index)
+ .finish()
+ }
+}
+
+impl<'func, A, M> ExpressionHandler<'func, A, M, SSA>
+ for LowLevelILExpression<'func, A, M, SSA, ValueExpr>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+{
+ fn kind(&self) -> LowLevelILExpressionKind<'func, A, M, SSA> {
+ #[allow(unused_imports)]
+ use binaryninjacore_sys::BNLowLevelILOperation::*;
+ let op = unsafe { BNGetLowLevelILByIndex(self.function.handle, self.index.0) };
+ #[allow(clippy::match_single_binding)]
+ match op.operation {
+ // Any invalid ops for SSA will be checked here.
+ // SAFETY: We have checked for illegal operations.
+ _ => unsafe { LowLevelILExpressionKind::from_raw(self.function, op) },
+ }
+ }
+
+ fn visit_tree<T>(&self, f: &mut T) -> VisitorAction
+ where
+ T: FnMut(&LowLevelILExpression<'func, A, M, SSA, ValueExpr>) -> VisitorAction,
+ {
+ // Visit the current expression.
+ match f(self) {
+ VisitorAction::Descend => {
+ // Recursively visit sub expressions.
+ self.kind().visit_sub_expressions(|e| e.visit_tree(f))
+ }
+ action => action,
+ }
+ }
+}
+
+impl<'func, A, M> ExpressionHandler<'func, A, M, NonSSA<LiftedNonSSA>>
+ for LowLevelILExpression<'func, A, M, NonSSA<LiftedNonSSA>, ValueExpr>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+{
+ fn kind(&self) -> LowLevelILExpressionKind<'func, A, M, NonSSA<LiftedNonSSA>> {
+ #[allow(unused_imports)]
+ use binaryninjacore_sys::BNLowLevelILOperation::*;
+ let op = unsafe { BNGetLowLevelILByIndex(self.function.handle, self.index.0) };
+ #[allow(clippy::match_single_binding)]
+ match op.operation {
+ // Any invalid ops for Lifted IL will be checked here.
+ // SAFETY: We have checked for illegal operations.
+ _ => unsafe { LowLevelILExpressionKind::from_raw(self.function, op) },
+ }
+ }
+
+ fn visit_tree<T>(&self, f: &mut T) -> VisitorAction
+ where
+ T: FnMut(
+ &LowLevelILExpression<'func, A, M, NonSSA<LiftedNonSSA>, ValueExpr>,
+ ) -> VisitorAction,
+ {
+ // Visit the current expression.
+ match f(self) {
+ VisitorAction::Descend => {
+ // Recursively visit sub expressions.
+ self.kind().visit_sub_expressions(|e| e.visit_tree(f))
+ }
+ action => action,
+ }
+ }
+}
+
+impl<'func, A, M> ExpressionHandler<'func, A, M, NonSSA<RegularNonSSA>>
+ for LowLevelILExpression<'func, A, M, NonSSA<RegularNonSSA>, ValueExpr>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+{
+ fn kind(&self) -> LowLevelILExpressionKind<'func, A, M, NonSSA<RegularNonSSA>> {
+ use binaryninjacore_sys::BNLowLevelILOperation::*;
+ let op = unsafe { BNGetLowLevelILByIndex(self.function.handle, self.index.0) };
+ match op.operation {
+ // Any invalid ops for Non-Lifted IL will be checked here.
+ LLIL_FLAG_COND => unreachable!("LLIL_FLAG_COND is only valid in Lifted IL"),
+ LLIL_FLAG_GROUP => unreachable!("LLIL_FLAG_GROUP is only valid in Lifted IL"),
+ // SAFETY: We have checked for illegal operations.
+ _ => unsafe { LowLevelILExpressionKind::from_raw(self.function, op) },
+ }
+ }
+
+ fn visit_tree<T>(&self, f: &mut T) -> VisitorAction
+ where
+ T: FnMut(
+ &LowLevelILExpression<'func, A, M, NonSSA<RegularNonSSA>, ValueExpr>,
+ ) -> VisitorAction,
+ {
+ // Visit the current expression.
+ match f(self) {
+ VisitorAction::Descend => {
+ // Recursively visit sub expressions.
+ self.kind().visit_sub_expressions(|e| e.visit_tree(f))
+ }
+ action => action,
+ }
+ }
+}
+
+impl<'func, A, F> LowLevelILExpression<'func, A, Finalized, F, ValueExpr>
+where
+ A: 'func + Architecture,
+ F: FunctionForm,
+{
+ // TODO possible values
+}
+
+#[derive(Debug)]
+pub enum LowLevelILExpressionKind<'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>),
+ RegSplit(Operation<'func, A, M, F, operation::RegSplit>),
+ 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>),
+ ExternPtr(Operation<'func, A, M, F, operation::Extern>),
+
+ 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>),
+
+ // Valid only in Lifted IL
+ FlagCond(Operation<'func, A, M, NonSSA<LiftedNonSSA>, operation::FlagCond>),
+ // Valid only in Lifted IL
+ FlagGroup(Operation<'func, A, M, NonSSA<LiftedNonSSA>, 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>),
+
+ Fadd(Operation<'func, A, M, F, operation::BinaryOp>),
+ Fsub(Operation<'func, A, M, F, operation::BinaryOp>),
+ Fmul(Operation<'func, A, M, F, operation::BinaryOp>),
+ Fdiv(Operation<'func, A, M, F, operation::BinaryOp>),
+ Fsqrt(Operation<'func, A, M, F, operation::UnaryOp>),
+ Fneg(Operation<'func, A, M, F, operation::UnaryOp>),
+ Fabs(Operation<'func, A, M, F, operation::UnaryOp>),
+ FloatToInt(Operation<'func, A, M, F, operation::UnaryOp>),
+ IntToFloat(Operation<'func, A, M, F, operation::UnaryOp>),
+ FloatConv(Operation<'func, A, M, F, operation::UnaryOp>),
+ RoundToInt(Operation<'func, A, M, F, operation::UnaryOp>),
+ Floor(Operation<'func, A, M, F, operation::UnaryOp>),
+ Ceil(Operation<'func, A, M, F, operation::UnaryOp>),
+ Ftrunc(Operation<'func, A, M, F, operation::UnaryOp>),
+
+ FcmpE(Operation<'func, A, M, F, operation::Condition>),
+ FcmpNE(Operation<'func, A, M, F, operation::Condition>),
+ FcmpLT(Operation<'func, A, M, F, operation::Condition>),
+ FcmpLE(Operation<'func, A, M, F, operation::Condition>),
+ FcmpGE(Operation<'func, A, M, F, operation::Condition>),
+ FcmpGT(Operation<'func, A, M, F, operation::Condition>),
+ FcmpO(Operation<'func, A, M, F, operation::Condition>),
+ FcmpUO(Operation<'func, A, M, F, operation::Condition>),
+
+ // 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> LowLevelILExpressionKind<'func, A, M, F>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ // TODO: Document what "unchecked" means and how to consume this safely.
+ pub(crate) unsafe fn from_raw(
+ function: &'func LowLevelILFunction<A, M, F>,
+ op: BNLowLevelILInstruction,
+ ) -> Self {
+ use binaryninjacore_sys::BNLowLevelILOperation::*;
+
+ match op.operation {
+ LLIL_LOAD | LLIL_LOAD_SSA => {
+ LowLevelILExpressionKind::Load(Operation::new(function, op))
+ }
+ LLIL_POP => LowLevelILExpressionKind::Pop(Operation::new(function, op)),
+ LLIL_REG | LLIL_REG_SSA | LLIL_REG_SSA_PARTIAL => {
+ LowLevelILExpressionKind::Reg(Operation::new(function, op))
+ }
+ LLIL_REG_SPLIT | LLIL_REG_SPLIT_SSA => {
+ LowLevelILExpressionKind::RegSplit(Operation::new(function, op))
+ }
+ LLIL_CONST => LowLevelILExpressionKind::Const(Operation::new(function, op)),
+ LLIL_CONST_PTR => LowLevelILExpressionKind::ConstPtr(Operation::new(function, op)),
+ LLIL_FLAG | LLIL_FLAG_SSA => {
+ LowLevelILExpressionKind::Flag(Operation::new(function, op))
+ }
+ LLIL_FLAG_BIT | LLIL_FLAG_BIT_SSA => {
+ LowLevelILExpressionKind::FlagBit(Operation::new(function, op))
+ }
+ LLIL_EXTERN_PTR => LowLevelILExpressionKind::ExternPtr(Operation::new(function, op)),
+
+ LLIL_ADD => LowLevelILExpressionKind::Add(Operation::new(function, op)),
+ LLIL_ADC => LowLevelILExpressionKind::Adc(Operation::new(function, op)),
+ LLIL_SUB => LowLevelILExpressionKind::Sub(Operation::new(function, op)),
+ LLIL_SBB => LowLevelILExpressionKind::Sbb(Operation::new(function, op)),
+ LLIL_AND => LowLevelILExpressionKind::And(Operation::new(function, op)),
+ LLIL_OR => LowLevelILExpressionKind::Or(Operation::new(function, op)),
+ LLIL_XOR => LowLevelILExpressionKind::Xor(Operation::new(function, op)),
+ LLIL_LSL => LowLevelILExpressionKind::Lsl(Operation::new(function, op)),
+ LLIL_LSR => LowLevelILExpressionKind::Lsr(Operation::new(function, op)),
+ LLIL_ASR => LowLevelILExpressionKind::Asr(Operation::new(function, op)),
+ LLIL_ROL => LowLevelILExpressionKind::Rol(Operation::new(function, op)),
+ LLIL_RLC => LowLevelILExpressionKind::Rlc(Operation::new(function, op)),
+ LLIL_ROR => LowLevelILExpressionKind::Ror(Operation::new(function, op)),
+ LLIL_RRC => LowLevelILExpressionKind::Rrc(Operation::new(function, op)),
+ LLIL_MUL => LowLevelILExpressionKind::Mul(Operation::new(function, op)),
+
+ LLIL_MULU_DP => LowLevelILExpressionKind::MuluDp(Operation::new(function, op)),
+ LLIL_MULS_DP => LowLevelILExpressionKind::MulsDp(Operation::new(function, op)),
+
+ LLIL_DIVU => LowLevelILExpressionKind::Divu(Operation::new(function, op)),
+ LLIL_DIVS => LowLevelILExpressionKind::Divs(Operation::new(function, op)),
+
+ LLIL_DIVU_DP => LowLevelILExpressionKind::DivuDp(Operation::new(function, op)),
+ LLIL_DIVS_DP => LowLevelILExpressionKind::DivsDp(Operation::new(function, op)),
+
+ LLIL_MODU => LowLevelILExpressionKind::Modu(Operation::new(function, op)),
+ LLIL_MODS => LowLevelILExpressionKind::Mods(Operation::new(function, op)),
+
+ LLIL_MODU_DP => LowLevelILExpressionKind::ModuDp(Operation::new(function, op)),
+ LLIL_MODS_DP => LowLevelILExpressionKind::ModsDp(Operation::new(function, op)),
+
+ LLIL_NEG => LowLevelILExpressionKind::Neg(Operation::new(function, op)),
+ LLIL_NOT => LowLevelILExpressionKind::Not(Operation::new(function, op)),
+
+ LLIL_SX => LowLevelILExpressionKind::Sx(Operation::new(function, op)),
+ LLIL_ZX => LowLevelILExpressionKind::Zx(Operation::new(function, op)),
+ LLIL_LOW_PART => LowLevelILExpressionKind::LowPart(Operation::new(function, op)),
+
+ LLIL_CMP_E => LowLevelILExpressionKind::CmpE(Operation::new(function, op)),
+ LLIL_CMP_NE => LowLevelILExpressionKind::CmpNe(Operation::new(function, op)),
+ LLIL_CMP_SLT => LowLevelILExpressionKind::CmpSlt(Operation::new(function, op)),
+ LLIL_CMP_ULT => LowLevelILExpressionKind::CmpUlt(Operation::new(function, op)),
+ LLIL_CMP_SLE => LowLevelILExpressionKind::CmpSle(Operation::new(function, op)),
+ LLIL_CMP_ULE => LowLevelILExpressionKind::CmpUle(Operation::new(function, op)),
+ LLIL_CMP_SGE => LowLevelILExpressionKind::CmpSge(Operation::new(function, op)),
+ LLIL_CMP_UGE => LowLevelILExpressionKind::CmpUge(Operation::new(function, op)),
+ LLIL_CMP_SGT => LowLevelILExpressionKind::CmpSgt(Operation::new(function, op)),
+ LLIL_CMP_UGT => LowLevelILExpressionKind::CmpUgt(Operation::new(function, op)),
+
+ LLIL_BOOL_TO_INT => LowLevelILExpressionKind::BoolToInt(Operation::new(function, op)),
+
+ LLIL_FADD => LowLevelILExpressionKind::Fadd(Operation::new(function, op)),
+ LLIL_FSUB => LowLevelILExpressionKind::Fsub(Operation::new(function, op)),
+ LLIL_FMUL => LowLevelILExpressionKind::Fmul(Operation::new(function, op)),
+ LLIL_FDIV => LowLevelILExpressionKind::Fdiv(Operation::new(function, op)),
+
+ LLIL_FSQRT => LowLevelILExpressionKind::Fsqrt(Operation::new(function, op)),
+ LLIL_FNEG => LowLevelILExpressionKind::Fneg(Operation::new(function, op)),
+ LLIL_FABS => LowLevelILExpressionKind::Fabs(Operation::new(function, op)),
+ LLIL_FLOAT_TO_INT => LowLevelILExpressionKind::FloatToInt(Operation::new(function, op)),
+ LLIL_INT_TO_FLOAT => LowLevelILExpressionKind::IntToFloat(Operation::new(function, op)),
+ LLIL_FLOAT_CONV => LowLevelILExpressionKind::FloatConv(Operation::new(function, op)),
+ LLIL_ROUND_TO_INT => LowLevelILExpressionKind::RoundToInt(Operation::new(function, op)),
+ LLIL_FLOOR => LowLevelILExpressionKind::Floor(Operation::new(function, op)),
+ LLIL_CEIL => LowLevelILExpressionKind::Ceil(Operation::new(function, op)),
+ LLIL_FTRUNC => LowLevelILExpressionKind::Ftrunc(Operation::new(function, op)),
+
+ LLIL_FCMP_E => LowLevelILExpressionKind::FcmpE(Operation::new(function, op)),
+ LLIL_FCMP_NE => LowLevelILExpressionKind::FcmpNE(Operation::new(function, op)),
+ LLIL_FCMP_LT => LowLevelILExpressionKind::FcmpLT(Operation::new(function, op)),
+ LLIL_FCMP_LE => LowLevelILExpressionKind::FcmpLE(Operation::new(function, op)),
+ LLIL_FCMP_GT => LowLevelILExpressionKind::FcmpGT(Operation::new(function, op)),
+ LLIL_FCMP_GE => LowLevelILExpressionKind::FcmpGE(Operation::new(function, op)),
+ LLIL_FCMP_O => LowLevelILExpressionKind::FcmpO(Operation::new(function, op)),
+ LLIL_FCMP_UO => LowLevelILExpressionKind::FcmpUO(Operation::new(function, op)),
+
+ LLIL_UNIMPL => LowLevelILExpressionKind::Unimpl(Operation::new(function, op)),
+ LLIL_UNIMPL_MEM => LowLevelILExpressionKind::UnimplMem(Operation::new(function, op)),
+
+ // TODO TEST_BIT ADD_OVERFLOW LLIL_REG_STACK_PUSH LLIL_REG_STACK_POP
+ _ => {
+ #[cfg(debug_assertions)]
+ log::error!(
+ "Got unexpected operation {:?} in value expr at 0x{:x}",
+ op.operation,
+ op.address
+ );
+
+ LowLevelILExpressionKind::Undef(Operation::new(function, op))
+ }
+ }
+ }
+
+ /// 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::LowLevelILExpressionKind::*;
+
+ 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::LowLevelILExpressionKind::*;
+
+ 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::LowLevelILExpressionKind::*;
+
+ 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) | FcmpE(ref op) | FcmpNE(ref op) | FcmpLT(ref op) | FcmpLE(ref op)
+ | FcmpGE(ref op) | FcmpGT(ref op) | FcmpO(ref op) | FcmpUO(ref op) => Some(op),
+ _ => None,
+ }
+ }
+
+ pub fn as_binary_op(&self) -> Option<&Operation<'func, A, M, F, operation::BinaryOp>> {
+ use self::LowLevelILExpressionKind::*;
+
+ 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) | Fadd(ref op) | Fsub(ref op) | Fmul(ref op) | Fdiv(ref op) => Some(op),
+ _ => None,
+ }
+ }
+
+ pub fn as_binary_op_carry(
+ &self,
+ ) -> Option<&Operation<'func, A, M, F, operation::BinaryOpCarry>> {
+ use self::LowLevelILExpressionKind::*;
+
+ 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::LowLevelILExpressionKind::*;
+
+ 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::LowLevelILExpressionKind::*;
+
+ match *self {
+ Neg(ref op) | Not(ref op) | Sx(ref op) | Zx(ref op) | LowPart(ref op)
+ | BoolToInt(ref op) | Fsqrt(ref op) | Fneg(ref op) | Fabs(ref op)
+ | FloatToInt(ref op) | IntToFloat(ref op) | FloatConv(ref op) | RoundToInt(ref op)
+ | Floor(ref op) | Ceil(ref op) | Ftrunc(ref op) => Some(op),
+ _ => None,
+ }
+ }
+
+ pub fn visit_sub_expressions<T>(&self, mut visitor: T) -> VisitorAction
+ where
+ T: FnMut(LowLevelILExpression<'func, A, M, F, ValueExpr>) -> VisitorAction,
+ {
+ use LowLevelILExpressionKind::*;
+
+ macro_rules! visit {
+ ($expr:expr) => {
+ if let VisitorAction::Halt = visitor($expr) {
+ return VisitorAction::Halt;
+ }
+ };
+ }
+
+ 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) | FcmpE(ref op) | FcmpNE(ref op) | FcmpLT(ref op) | FcmpLE(ref op)
+ | FcmpGE(ref op) | FcmpGT(ref op) | FcmpO(ref op) | FcmpUO(ref op) => {
+ visit!(op.left());
+ visit!(op.right());
+ }
+ Adc(ref op) | Sbb(ref op) | Rlc(ref op) | Rrc(ref op) => {
+ visit!(op.left());
+ visit!(op.right());
+ visit!(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) | Fadd(ref op) | Fsub(ref op) | Fmul(ref op) | Fdiv(ref op) => {
+ visit!(op.left());
+ visit!(op.right());
+ }
+ DivuDp(ref op) | DivsDp(ref op) | ModuDp(ref op) | ModsDp(ref op) => {
+ visit!(op.high());
+ visit!(op.low());
+ visit!(op.right());
+ }
+ Neg(ref op) | Not(ref op) | Sx(ref op) | Zx(ref op) | LowPart(ref op)
+ | BoolToInt(ref op) | Fsqrt(ref op) | Fneg(ref op) | Fabs(ref op)
+ | FloatToInt(ref op) | IntToFloat(ref op) | FloatConv(ref op) | RoundToInt(ref op)
+ | Floor(ref op) | Ceil(ref op) | Ftrunc(ref op) => {
+ visit!(op.operand());
+ }
+ UnimplMem(ref op) => {
+ visit!(op.mem_expr());
+ }
+ Load(ref op) => {
+ visit!(op.source_mem_expr());
+ }
+ // Do not have any sub expressions.
+ Pop(_) | Reg(_) | RegSplit(_) | Const(_) | ConstPtr(_) | Flag(_) | FlagBit(_)
+ | ExternPtr(_) | FlagCond(_) | FlagGroup(_) | Unimpl(_) | Undef(_) => {}
+ }
+
+ VisitorAction::Sibling
+ }
+
+ pub(crate) fn raw_struct(&self) -> &BNLowLevelILInstruction {
+ use self::LowLevelILExpressionKind::*;
+
+ 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) | FcmpE(ref op) | FcmpNE(ref op) | FcmpLT(ref op) | FcmpLE(ref op)
+ | FcmpGE(ref op) | FcmpGT(ref op) | FcmpO(ref op) | FcmpUO(ref op) => &op.op,
+
+ Load(ref op) => &op.op,
+
+ Pop(ref op) => &op.op,
+
+ Reg(ref op) => &op.op,
+
+ RegSplit(ref op) => &op.op,
+
+ Flag(ref op) => &op.op,
+
+ FlagBit(ref op) => &op.op,
+
+ Const(ref op) | ConstPtr(ref op) => &op.op,
+
+ ExternPtr(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) | Fadd(ref op) | Fsub(ref op) | Fmul(ref op) | Fdiv(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) | Fsqrt(ref op) | Fneg(ref op) | Fabs(ref op)
+ | FloatToInt(ref op) | IntToFloat(ref op) | FloatConv(ref op) | RoundToInt(ref op)
+ | Floor(ref op) | Ceil(ref op) | Ftrunc(ref op) => &op.op,
+
+ UnimplMem(ref op) => &op.op,
+ //TestBit(Operation<'func, A, M, F, operation::TestBit>), // TODO
+ }
+ }
+}
+
+impl<'func, A> LowLevelILExpressionKind<'func, A, Mutable, NonSSA<LiftedNonSSA>>
+where
+ A: 'func + Architecture,
+{
+ pub fn flag_write(&self) -> Option<A::FlagWrite> {
+ use self::LowLevelILExpressionKind::*;
+
+ 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) | FcmpE(ref _op) | FcmpNE(ref _op)
+ | FcmpLT(ref _op) | FcmpLE(ref _op) | FcmpGE(ref _op) | FcmpGT(ref _op)
+ | FcmpO(ref _op) | FcmpUO(ref _op) => None,
+
+ Load(ref op) => op.flag_write(),
+
+ Pop(ref op) => op.flag_write(),
+
+ Reg(ref op) => op.flag_write(),
+
+ RegSplit(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(),
+
+ ExternPtr(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) | Fadd(ref op) | Fsub(ref op) | Fmul(ref op) | Fdiv(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) | Fsqrt(ref op) | Fneg(ref op) | Fabs(ref op)
+ | FloatToInt(ref op) | IntToFloat(ref op) | FloatConv(ref op) | RoundToInt(ref op)
+ | Floor(ref op) | Ceil(ref op) | Ftrunc(ref op) => op.flag_write(),
+
+ UnimplMem(ref op) => op.flag_write(),
+ //TestBit(Operation<'func, A, M, F, operation::TestBit>), // TODO
+ }
+ }
+}
diff --git a/rust/src/low_level_il/function.rs b/rust/src/low_level_il/function.rs
new file mode 100644
index 00000000..fea467d0
--- /dev/null
+++ b/rust/src/low_level_il/function.rs
@@ -0,0 +1,275 @@
+// Copyright 2021-2024 Vector 35 Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use binaryninjacore_sys::BNFreeLowLevelILFunction;
+use binaryninjacore_sys::BNGetLowLevelILOwnerFunction;
+use binaryninjacore_sys::BNLowLevelILFunction;
+use binaryninjacore_sys::BNNewLowLevelILFunctionReference;
+
+use std::borrow::Borrow;
+use std::fmt::Debug;
+use std::hash::{Hash, Hasher};
+use std::marker::PhantomData;
+
+use crate::architecture::CoreArchitecture;
+use crate::basic_block::BasicBlock;
+use crate::function::Function;
+use crate::low_level_il::block::LowLevelILBlock;
+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 LowLevelILFunction<A: Architecture, M: FunctionMutability, F: FunctionForm> {
+ pub(crate) arch_handle: A::Handle,
+ pub(crate) handle: *mut BNLowLevelILFunction,
+ _arch: PhantomData<*mut A>,
+ _mutability: PhantomData<M>,
+ _form: PhantomData<F>,
+}
+
+impl<A, M, F> LowLevelILFunction<A, M, F>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub(crate) unsafe fn from_raw(
+ arch_handle: A::Handle,
+ handle: *mut BNLowLevelILFunction,
+ ) -> Self {
+ debug_assert!(!handle.is_null());
+
+ Self {
+ arch_handle,
+ handle,
+ _arch: PhantomData,
+ _mutability: PhantomData,
+ _form: PhantomData,
+ }
+ }
+
+ pub(crate) unsafe fn ref_from_raw(
+ arch_handle: A::Handle,
+ handle: *mut BNLowLevelILFunction,
+ ) -> Ref<Self> {
+ debug_assert!(!handle.is_null());
+ Ref::new(Self::from_raw(arch_handle, handle))
+ }
+
+ pub(crate) fn arch(&self) -> &A {
+ self.arch_handle.borrow()
+ }
+
+ pub fn instruction_at<L: Into<Location>>(
+ &self,
+ loc: L,
+ ) -> Option<LowLevelILInstruction<A, M, F>> {
+ Some(LowLevelILInstruction::new(
+ self,
+ self.instruction_index_at(loc)?,
+ ))
+ }
+
+ pub fn instruction_index_at<L: Into<Location>>(
+ &self,
+ loc: L,
+ ) -> Option<LowLevelInstructionIndex> {
+ use binaryninjacore_sys::BNLowLevelILGetInstructionStart;
+ let loc: Location = loc.into();
+ let arch = loc.arch.unwrap_or_else(|| *self.arch().as_ref());
+ let instr_idx =
+ unsafe { BNLowLevelILGetInstructionStart(self.handle, arch.handle, loc.addr) };
+ // `instr_idx` will equal self.instruction_count() if the instruction is not valid.
+ if instr_idx >= self.instruction_count() {
+ None
+ } else {
+ Some(LowLevelInstructionIndex(instr_idx))
+ }
+ }
+
+ pub fn instruction_from_index(
+ &self,
+ index: LowLevelInstructionIndex,
+ ) -> Option<LowLevelILInstruction<A, M, F>> {
+ if index.0 >= self.instruction_count() {
+ None
+ } else {
+ Some(LowLevelILInstruction::new(self, index))
+ }
+ }
+
+ pub fn instruction_count(&self) -> usize {
+ unsafe {
+ use binaryninjacore_sys::BNGetLowLevelILInstructionCount;
+ BNGetLowLevelILInstructionCount(self.handle)
+ }
+ }
+
+ pub fn expression_count(&self) -> usize {
+ unsafe {
+ use binaryninjacore_sys::BNGetLowLevelILExprCount;
+ BNGetLowLevelILExprCount(self.handle)
+ }
+ }
+
+ pub fn function(&self) -> Ref<Function> {
+ unsafe {
+ let func = BNGetLowLevelILOwnerFunction(self.handle);
+ Function::ref_from_raw(func)
+ }
+ }
+}
+
+// LLIL basic blocks are not available until the function object
+// is finalized, so ensure we can't try requesting basic blocks
+// during lifting
+impl<A, F> LowLevelILFunction<A, Finalized, F>
+where
+ A: Architecture,
+ F: FunctionForm,
+{
+ pub fn basic_blocks(&self) -> Array<BasicBlock<LowLevelILBlock<A, Finalized, F>>> {
+ use binaryninjacore_sys::BNGetLowLevelILBasicBlockList;
+
+ unsafe {
+ let mut count = 0;
+ let blocks = BNGetLowLevelILBasicBlockList(self.handle, &mut count);
+ let context = LowLevelILBlock { function: self };
+ Array::new(blocks, count, context)
+ }
+ }
+}
+
+// Allow instantiating Lifted IL functions for querying Lifted IL from Architectures
+impl LowLevelILFunction<CoreArchitecture, Mutable, NonSSA<LiftedNonSSA>> {
+ // TODO: Document what happens when you pass None for `source_func`.
+ // TODO: Doing so would construct a LowLevelILFunction with no basic blocks
+ // TODO: Document why you would want to do that.
+ pub fn new(arch: CoreArchitecture, source_func: Option<Function>) -> Ref<Self> {
+ use binaryninjacore_sys::BNCreateLowLevelILFunction;
+
+ let handle = unsafe {
+ match source_func {
+ Some(func) => BNCreateLowLevelILFunction(arch.handle, func.handle),
+ None => BNCreateLowLevelILFunction(arch.handle, std::ptr::null_mut()),
+ }
+ };
+
+ // BNCreateLowLevelILFunction should always return a valid object.
+ assert!(!handle.is_null());
+
+ unsafe { Self::ref_from_raw(arch, handle) }
+ }
+}
+
+impl<A, M, F> ToOwned for LowLevelILFunction<A, M, F>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ type Owned = Ref<Self>;
+
+ fn to_owned(&self) -> Self::Owned {
+ unsafe { RefCountable::inc_ref(self) }
+ }
+}
+
+unsafe impl<A, M, F> RefCountable for LowLevelILFunction<A, M, F>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
+ Ref::new(Self {
+ arch_handle: handle.arch_handle.clone(),
+ handle: BNNewLowLevelILFunctionReference(handle.handle),
+ _arch: PhantomData,
+ _mutability: PhantomData,
+ _form: PhantomData,
+ })
+ }
+
+ unsafe fn dec_ref(handle: &Self) {
+ BNFreeLowLevelILFunction(handle.handle);
+ }
+}
+
+impl<A, M, F> Debug for LowLevelILFunction<A, M, F>
+where
+ A: Architecture + Debug,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.debug_struct("LowLevelILFunction")
+ .field("arch", &self.arch())
+ .field("instruction_count", &self.instruction_count())
+ .field("expression_count", &self.expression_count())
+ .finish()
+ }
+}
+
+unsafe impl<A: Architecture, M: FunctionMutability, F: FunctionForm> Send
+ for LowLevelILFunction<A, M, F>
+{
+}
+unsafe impl<A: Architecture, M: FunctionMutability, F: FunctionForm> Sync
+ for LowLevelILFunction<A, M, F>
+{
+}
+
+impl<A: Architecture, M: FunctionMutability, F: FunctionForm> Eq for LowLevelILFunction<A, M, F> {}
+
+impl<A: Architecture, M: FunctionMutability, F: FunctionForm> PartialEq
+ for LowLevelILFunction<A, M, F>
+{
+ fn eq(&self, rhs: &Self) -> bool {
+ self.function().eq(&rhs.function())
+ }
+}
+
+impl<A: Architecture, M: FunctionMutability, F: FunctionForm> Hash for LowLevelILFunction<A, M, F> {
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ self.function().hash(state)
+ }
+}
diff --git a/rust/src/low_level_il/instruction.rs b/rust/src/low_level_il/instruction.rs
new file mode 100644
index 00000000..5c3010ea
--- /dev/null
+++ b/rust/src/low_level_il/instruction.rs
@@ -0,0 +1,348 @@
+// Copyright 2021-2024 Vector 35 Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use super::operation;
+use super::operation::Operation;
+use super::VisitorAction;
+use super::*;
+use crate::architecture::Architecture;
+use binaryninjacore_sys::BNGetLowLevelILByIndex;
+use binaryninjacore_sys::BNGetLowLevelILIndexForInstruction;
+use binaryninjacore_sys::BNLowLevelILInstruction;
+use std::fmt::{Debug, Display, Formatter};
+
+#[repr(transparent)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct LowLevelInstructionIndex(pub usize);
+
+impl LowLevelInstructionIndex {
+ pub fn next(&self) -> Self {
+ Self(self.0 + 1)
+ }
+}
+
+impl From<usize> for LowLevelInstructionIndex {
+ fn from(index: usize) -> Self {
+ Self(index)
+ }
+}
+
+impl From<u64> for LowLevelInstructionIndex {
+ fn from(index: u64) -> Self {
+ Self(index as usize)
+ }
+}
+
+impl Display for LowLevelInstructionIndex {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.write_fmt(format_args!("{}", self.0))
+ }
+}
+
+// TODO: Probably want to rename this with a LowLevelIL prefix to avoid collisions when we add handlers for other ILs
+pub trait InstructionHandler<'func, A, M, F>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn kind(&self) -> LowLevelILInstructionKind<'func, A, M, F>;
+
+ /// Visit the sub expressions of this instruction.
+ ///
+ /// NOTE: This does not visit the root expression, i.e. the instruction.
+ fn visit_tree<T>(&self, f: &mut T) -> VisitorAction
+ where
+ T: FnMut(&LowLevelILExpression<'func, A, M, F, ValueExpr>) -> VisitorAction;
+}
+
+pub struct LowLevelILInstruction<'func, A, M, F>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub(crate) function: &'func LowLevelILFunction<A, M, F>,
+ pub index: LowLevelInstructionIndex,
+}
+
+impl<'func, A, M, F> LowLevelILInstruction<'func, A, M, F>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ // TODO: Should we check the instruction count here with BNGetLowLevelILInstructionCount?
+ // TODO: If we _can_ then this should become an Option<Self> methinks
+ pub fn new(
+ function: &'func LowLevelILFunction<A, M, F>,
+ index: LowLevelInstructionIndex,
+ ) -> Self {
+ Self { function, index }
+ }
+
+ pub fn address(&self) -> u64 {
+ self.into_raw().address
+ }
+
+ // TODO: Document the difference between the self.index and the expr_idx.
+ pub fn expr_idx(&self) -> LowLevelExpressionIndex {
+ let idx = unsafe { BNGetLowLevelILIndexForInstruction(self.function.handle, self.index.0) };
+ LowLevelExpressionIndex(idx)
+ }
+
+ pub fn into_raw(&self) -> BNLowLevelILInstruction {
+ unsafe { BNGetLowLevelILByIndex(self.function.handle, self.expr_idx().0) }
+ }
+}
+
+impl<'func, A, M, F> Debug for LowLevelILInstruction<'func, A, M, F>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Instruction")
+ .field("index", &self.index)
+ .field("expr_idx", &self.expr_idx())
+ .field("address", &self.address())
+ .finish()
+ }
+}
+
+impl<'func, A, M> InstructionHandler<'func, A, M, SSA> for LowLevelILInstruction<'func, A, M, SSA>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+{
+ fn kind(&self) -> LowLevelILInstructionKind<'func, A, M, SSA> {
+ #[allow(unused_imports)]
+ use binaryninjacore_sys::BNLowLevelILOperation::*;
+ let raw_op = self.into_raw();
+ #[allow(clippy::match_single_binding)]
+ match raw_op.operation {
+ // Any invalid ops for Non-Lifted IL will be checked here.
+ // SAFETY: We have checked for illegal operations.
+ _ => unsafe {
+ LowLevelILInstructionKind::from_raw(self.function, self.expr_idx(), raw_op)
+ },
+ }
+ }
+
+ fn visit_tree<T>(&self, f: &mut T) -> VisitorAction
+ where
+ T: FnMut(&LowLevelILExpression<'func, A, M, SSA, ValueExpr>) -> VisitorAction,
+ {
+ // Recursively visit sub expressions.
+ self.kind().visit_sub_expressions(|e| e.visit_tree(f))
+ }
+}
+
+impl<'func, A, M> InstructionHandler<'func, A, M, NonSSA<LiftedNonSSA>>
+ for LowLevelILInstruction<'func, A, M, NonSSA<LiftedNonSSA>>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+{
+ fn kind(&self) -> LowLevelILInstructionKind<'func, A, M, NonSSA<LiftedNonSSA>> {
+ #[allow(unused_imports)]
+ use binaryninjacore_sys::BNLowLevelILOperation::*;
+ let raw_op = self.into_raw();
+ #[allow(clippy::match_single_binding)]
+ match raw_op.operation {
+ // Any invalid ops for Non-Lifted IL will be checked here.
+ // SAFETY: We have checked for illegal operations.
+ _ => unsafe {
+ LowLevelILInstructionKind::from_raw(self.function, self.expr_idx(), raw_op)
+ },
+ }
+ }
+
+ fn visit_tree<T>(&self, f: &mut T) -> VisitorAction
+ where
+ T: FnMut(
+ &LowLevelILExpression<'func, A, M, NonSSA<LiftedNonSSA>, ValueExpr>,
+ ) -> VisitorAction,
+ {
+ // Recursively visit sub expressions.
+ self.kind().visit_sub_expressions(|e| e.visit_tree(f))
+ }
+}
+
+impl<'func, A, M> InstructionHandler<'func, A, M, NonSSA<RegularNonSSA>>
+ for LowLevelILInstruction<'func, A, M, NonSSA<RegularNonSSA>>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+{
+ fn kind(&self) -> LowLevelILInstructionKind<'func, A, M, NonSSA<RegularNonSSA>> {
+ #[allow(unused_imports)]
+ use binaryninjacore_sys::BNLowLevelILOperation::*;
+ let raw_op = self.into_raw();
+ #[allow(clippy::match_single_binding)]
+ match raw_op.operation {
+ // Any invalid ops for Non-Lifted IL will be checked here.
+ // SAFETY: We have checked for illegal operations.
+ _ => unsafe {
+ LowLevelILInstructionKind::from_raw(self.function, self.expr_idx(), raw_op)
+ },
+ }
+ }
+
+ fn visit_tree<T>(&self, f: &mut T) -> VisitorAction
+ where
+ T: FnMut(
+ &LowLevelILExpression<'func, A, M, NonSSA<RegularNonSSA>, ValueExpr>,
+ ) -> VisitorAction,
+ {
+ // Recursively visit sub expressions.
+ self.kind().visit_sub_expressions(|e| e.visit_tree(f))
+ }
+}
+
+#[derive(Debug)]
+pub enum LowLevelILInstructionKind<'func, A, M, F>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ Nop(Operation<'func, A, M, F, operation::NoArgs>),
+ SetReg(Operation<'func, A, M, F, operation::SetReg>),
+ SetRegSplit(Operation<'func, A, M, F, operation::SetRegSplit>),
+ SetFlag(Operation<'func, A, M, F, operation::SetFlag>),
+ Store(Operation<'func, A, M, F, operation::Store>),
+ // TODO needs a real op
+ Push(Operation<'func, A, M, F, operation::UnaryOp>),
+
+ Jump(Operation<'func, A, M, F, operation::Jump>),
+ JumpTo(Operation<'func, A, M, F, operation::JumpTo>),
+
+ Call(Operation<'func, A, M, F, operation::Call>),
+ TailCall(Operation<'func, A, M, F, operation::Call>),
+
+ Ret(Operation<'func, A, M, F, operation::Ret>),
+ NoRet(Operation<'func, A, M, F, operation::NoArgs>),
+
+ If(Operation<'func, A, M, F, operation::If>),
+ Goto(Operation<'func, A, M, F, operation::Goto>),
+
+ Syscall(Operation<'func, A, M, F, operation::Syscall>),
+ Intrinsic(Operation<'func, A, M, F, operation::Intrinsic>),
+ Bp(Operation<'func, A, M, F, operation::NoArgs>),
+ Trap(Operation<'func, A, M, F, operation::Trap>),
+ Undef(Operation<'func, A, M, F, operation::NoArgs>),
+
+ /// The instruction is an expression.
+ Value(LowLevelILExpression<'func, A, M, F, ValueExpr>),
+}
+
+impl<'func, A, M, F> LowLevelILInstructionKind<'func, A, M, F>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub(crate) unsafe fn from_raw(
+ function: &'func LowLevelILFunction<A, M, F>,
+ expr_index: LowLevelExpressionIndex,
+ op: BNLowLevelILInstruction,
+ ) -> Self {
+ use binaryninjacore_sys::BNLowLevelILOperation::*;
+
+ match op.operation {
+ LLIL_NOP => LowLevelILInstructionKind::Nop(Operation::new(function, op)),
+ LLIL_SET_REG | LLIL_SET_REG_SSA => {
+ LowLevelILInstructionKind::SetReg(Operation::new(function, op))
+ }
+ LLIL_SET_REG_SPLIT | LLIL_SET_REG_SPLIT_SSA => {
+ LowLevelILInstructionKind::SetRegSplit(Operation::new(function, op))
+ }
+ LLIL_SET_FLAG | LLIL_SET_FLAG_SSA => {
+ LowLevelILInstructionKind::SetFlag(Operation::new(function, op))
+ }
+ LLIL_STORE | LLIL_STORE_SSA => {
+ LowLevelILInstructionKind::Store(Operation::new(function, op))
+ }
+ LLIL_PUSH => LowLevelILInstructionKind::Push(Operation::new(function, op)),
+
+ LLIL_JUMP => LowLevelILInstructionKind::Jump(Operation::new(function, op)),
+ LLIL_JUMP_TO => LowLevelILInstructionKind::JumpTo(Operation::new(function, op)),
+
+ LLIL_CALL | LLIL_CALL_STACK_ADJUST | LLIL_CALL_SSA => {
+ LowLevelILInstructionKind::Call(Operation::new(function, op))
+ }
+ LLIL_TAILCALL | LLIL_TAILCALL_SSA => {
+ LowLevelILInstructionKind::TailCall(Operation::new(function, op))
+ }
+
+ LLIL_RET => LowLevelILInstructionKind::Ret(Operation::new(function, op)),
+ LLIL_NORET => LowLevelILInstructionKind::NoRet(Operation::new(function, op)),
+
+ LLIL_IF => LowLevelILInstructionKind::If(Operation::new(function, op)),
+ LLIL_GOTO => LowLevelILInstructionKind::Goto(Operation::new(function, op)),
+
+ LLIL_SYSCALL | LLIL_SYSCALL_SSA => {
+ LowLevelILInstructionKind::Syscall(Operation::new(function, op))
+ }
+ LLIL_INTRINSIC | LLIL_INTRINSIC_SSA => {
+ LowLevelILInstructionKind::Intrinsic(Operation::new(function, op))
+ }
+ LLIL_BP => LowLevelILInstructionKind::Bp(Operation::new(function, op)),
+ LLIL_TRAP => LowLevelILInstructionKind::Trap(Operation::new(function, op)),
+ LLIL_UNDEF => LowLevelILInstructionKind::Undef(Operation::new(function, op)),
+ _ => LowLevelILInstructionKind::Value(LowLevelILExpression::new(function, expr_index)),
+ }
+ }
+
+ fn visit_sub_expressions<T>(&self, mut visitor: T) -> VisitorAction
+ where
+ T: FnMut(&LowLevelILExpression<'func, A, M, F, ValueExpr>) -> VisitorAction,
+ {
+ use LowLevelILInstructionKind::*;
+
+ macro_rules! visit {
+ ($expr:expr) => {
+ if let VisitorAction::Halt = visitor($expr) {
+ return VisitorAction::Halt;
+ }
+ };
+ }
+
+ match self {
+ SetReg(ref op) => visit!(&op.source_expr()),
+ SetRegSplit(ref op) => visit!(&op.source_expr()),
+ SetFlag(ref op) => visit!(&op.source_expr()),
+ Store(ref op) => {
+ visit!(&op.dest_mem_expr());
+ visit!(&op.source_expr());
+ }
+ Push(ref op) => visit!(&op.operand()),
+ Jump(ref op) => visit!(&op.target()),
+ JumpTo(ref op) => visit!(&op.target()),
+ Call(ref op) | TailCall(ref op) => visit!(&op.target()),
+ Ret(ref op) => visit!(&op.target()),
+ If(ref op) => visit!(&op.condition()),
+ Intrinsic(ref _op) => {
+ // TODO: Visit when we support expression lists
+ }
+ Value(e) => visit!(e),
+ // Do not have any sub expressions.
+ Nop(_) | NoRet(_) | Goto(_) | Syscall(_) | Bp(_) | Trap(_) | Undef(_) => {}
+ }
+
+ VisitorAction::Sibling
+ }
+}
diff --git a/rust/src/low_level_il/lifting.rs b/rust/src/low_level_il/lifting.rs
new file mode 100644
index 00000000..430cb0f7
--- /dev/null
+++ b/rust/src/low_level_il/lifting.rs
@@ -0,0 +1,1671 @@
+// Copyright 2021-2024 Vector 35 Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use std::marker::PhantomData;
+
+use binaryninjacore_sys::{BNAddLowLevelILLabelForAddress, BNLowLevelILOperation};
+use binaryninjacore_sys::{BNLowLevelILLabel, BNRegisterOrConstant};
+
+use super::*;
+use crate::architecture::Register as ArchReg;
+use crate::architecture::{Architecture, FlagWriteId, RegisterId};
+use crate::architecture::{
+ Flag, FlagClass, FlagCondition, FlagGroup, FlagRole, FlagWrite, Intrinsic,
+};
+use crate::function::Location;
+
+pub trait LiftableLowLevelIL<'func, A: 'func + Architecture> {
+ type Result: ExpressionResultType;
+
+ fn lift(
+ il: &'func MutableLiftedILFunction<A>,
+ expr: Self,
+ ) -> MutableLiftedILExpr<'func, A, Self::Result>;
+}
+
+pub trait LiftableLowLevelILWithSize<'func, A: 'func + Architecture>:
+ LiftableLowLevelIL<'func, A, Result = ValueExpr>
+{
+ fn lift_with_size(
+ il: &'func MutableLiftedILFunction<A>,
+ expr: Self,
+ size: usize,
+ ) -> MutableLiftedILExpr<'func, A, ValueExpr>;
+}
+
+#[derive(Copy, Clone)]
+pub enum LowLevelILRegisterOrConstant<R: ArchReg> {
+ Register(usize, LowLevelILRegister<R>),
+ Constant(usize, u64),
+}
+
+impl<R: ArchReg> From<LowLevelILRegisterOrConstant<R>> for BNRegisterOrConstant {
+ fn from(value: LowLevelILRegisterOrConstant<R>) -> Self {
+ match value {
+ LowLevelILRegisterOrConstant::Register(_, r) => Self {
+ constant: false,
+ reg: r.id().0,
+ value: 0,
+ },
+ LowLevelILRegisterOrConstant::Constant(_, value) => Self {
+ constant: true,
+ reg: 0,
+ value,
+ },
+ }
+ }
+}
+
+// TODO flesh way out
+#[derive(Copy, Clone)]
+pub enum LowLevelILFlagWriteOp<R: ArchReg> {
+ SetReg(usize, LowLevelILRegisterOrConstant<R>),
+ SetRegSplit(
+ usize,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ ),
+
+ Sub(
+ usize,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ ),
+ Add(
+ usize,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ ),
+
+ Load(usize, LowLevelILRegisterOrConstant<R>),
+
+ Push(usize, LowLevelILRegisterOrConstant<R>),
+ Neg(usize, LowLevelILRegisterOrConstant<R>),
+ Not(usize, LowLevelILRegisterOrConstant<R>),
+ Sx(usize, LowLevelILRegisterOrConstant<R>),
+ Zx(usize, LowLevelILRegisterOrConstant<R>),
+ LowPart(usize, LowLevelILRegisterOrConstant<R>),
+ BoolToInt(usize, LowLevelILRegisterOrConstant<R>),
+ FloatToInt(usize, LowLevelILRegisterOrConstant<R>),
+
+ Store(
+ usize,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ ),
+
+ And(
+ usize,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ ),
+ Or(
+ usize,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ ),
+ Xor(
+ usize,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ ),
+ Lsl(
+ usize,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ ),
+ Lsr(
+ usize,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ ),
+ Asr(
+ usize,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ ),
+ Rol(
+ usize,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ ),
+ Ror(
+ usize,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ ),
+ Mul(
+ usize,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ ),
+ MuluDp(
+ usize,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ ),
+ MulsDp(
+ usize,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ ),
+ Divu(
+ usize,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ ),
+ Divs(
+ usize,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ ),
+ Modu(
+ usize,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ ),
+ Mods(
+ usize,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ ),
+ DivuDp(
+ usize,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ ),
+ DivsDp(
+ usize,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ ),
+ ModuDp(
+ usize,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ ),
+ ModsDp(
+ usize,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ ),
+
+ TestBit(
+ usize,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ ),
+ AddOverflow(
+ usize,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ ),
+
+ Adc(
+ usize,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ ),
+ Sbb(
+ usize,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ ),
+ Rlc(
+ usize,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ ),
+ Rrc(
+ usize,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ ),
+
+ Pop(usize),
+ // TODO: floating point stuff, llil comparison ops that set flags, intrinsics
+}
+
+impl<R: ArchReg> LowLevelILFlagWriteOp<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 self::LowLevelILFlagWriteOp::*;
+ use binaryninjacore_sys::BNLowLevelILOperation::*;
+
+ fn build_op<A, R>(
+ arch: &A,
+ size: usize,
+ operand: &BNRegisterOrConstant,
+ ) -> LowLevelILRegisterOrConstant<R>
+ where
+ A: Architecture<Register = R>,
+ R: ArchReg<InfoType = A::RegisterInfo>,
+ {
+ if operand.constant {
+ LowLevelILRegisterOrConstant::Constant(size, operand.value)
+ } else {
+ let il_reg = if 0x8000_0000 & operand.reg == 0 {
+ LowLevelILRegister::ArchReg(
+ arch.register_from_id(RegisterId(operand.reg)).unwrap(),
+ )
+ } else {
+ LowLevelILRegister::Temp(operand.reg)
+ };
+
+ LowLevelILRegisterOrConstant::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 self::LowLevelILFlagWriteOp::*;
+ use binaryninjacore_sys::BNLowLevelILOperation::*;
+
+ 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 raw_operands(&self) -> (usize, [BNRegisterOrConstant; 5]) {
+ use self::LowLevelILFlagWriteOp::*;
+
+ let mut operands: [BNRegisterOrConstant; 5] = [BNRegisterOrConstant::default(); 5];
+
+ 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();
+ 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();
+ operands[1] = op1.into();
+ 2
+ }
+
+ Adc(_, op0, op1, op2)
+ | Sbb(_, op0, op1, op2)
+ | Rlc(_, op0, op1, op2)
+ | Rrc(_, op0, op1, op2) => {
+ operands[0] = op0.into();
+ operands[1] = op1.into();
+ operands[2] = op2.into();
+ 3
+ }
+ };
+
+ (count, operands)
+ }
+}
+
+pub fn get_default_flag_write_llil<'func, A>(
+ arch: &A,
+ role: FlagRole,
+ op: LowLevelILFlagWriteOp<A::Register>,
+ il: &'func MutableLiftedILFunction<A>,
+) -> MutableLiftedILExpr<'func, A, ValueExpr>
+where
+ A: 'func + Architecture,
+{
+ let (size, operation) = op.size_and_op();
+ let (count, operands) = op.raw_operands();
+
+ let expr_idx = unsafe {
+ use binaryninjacore_sys::BNGetDefaultArchitectureFlagWriteLowLevelIL;
+ BNGetDefaultArchitectureFlagWriteLowLevelIL(
+ arch.as_ref().handle,
+ operation,
+ size,
+ role,
+ operands.as_ptr() as *mut _,
+ count,
+ il.handle,
+ )
+ };
+
+ LowLevelILExpression::new(il, LowLevelExpressionIndex(expr_idx))
+}
+
+pub fn get_default_flag_cond_llil<'func, A>(
+ arch: &A,
+ cond: FlagCondition,
+ class: Option<A::FlagClass>,
+ il: &'func MutableLiftedILFunction<A>,
+) -> MutableLiftedILExpr<'func, A, ValueExpr>
+where
+ A: 'func + Architecture,
+{
+ use binaryninjacore_sys::BNGetDefaultArchitectureFlagConditionLowLevelIL;
+ let class_id = class.map(|c| c.id().0).unwrap_or(0);
+
+ unsafe {
+ let expr_idx = BNGetDefaultArchitectureFlagConditionLowLevelIL(
+ arch.as_ref().handle,
+ cond,
+ class_id,
+ il.handle,
+ );
+
+ LowLevelILExpression::new(il, LowLevelExpressionIndex(expr_idx))
+ }
+}
+
+macro_rules! prim_int_lifter {
+ ($x:ty) => {
+ impl<'a, A: 'a + Architecture> LiftableLowLevelIL<'a, A> for $x {
+ type Result = ValueExpr;
+
+ fn lift(il: &'a MutableLiftedILFunction<A>, val: Self)
+ -> MutableLiftedILExpr<'a, A, Self::Result>
+ {
+ il.const_int(std::mem::size_of::<Self>(), val as i64 as u64)
+ }
+ }
+
+ impl<'a, A: 'a + Architecture> LiftableLowLevelILWithSize<'a, A> for $x {
+ fn lift_with_size(il: &'a MutableLiftedILFunction<A>, val: Self, size: usize)
+ -> MutableLiftedILExpr<'a, A, 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 {
+ log::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> LiftableLowLevelIL<'a, A> for LowLevelILRegister<R>
+where
+ R: LiftableLowLevelIL<'a, A, Result = ValueExpr> + Into<LowLevelILRegister<R>>,
+{
+ type Result = ValueExpr;
+
+ fn lift(
+ il: &'a MutableLiftedILFunction<A>,
+ reg: Self,
+ ) -> MutableLiftedILExpr<'a, A, Self::Result> {
+ match reg {
+ LowLevelILRegister::ArchReg(r) => R::lift(il, r),
+ LowLevelILRegister::Temp(t) => il.reg(
+ il.arch().default_integer_size(),
+ LowLevelILRegister::Temp(t),
+ ),
+ }
+ }
+}
+
+impl<'a, R: ArchReg, A: 'a + Architecture> LiftableLowLevelILWithSize<'a, A>
+ for LowLevelILRegister<R>
+where
+ R: LiftableLowLevelILWithSize<'a, A> + Into<LowLevelILRegister<R>>,
+{
+ fn lift_with_size(
+ il: &'a MutableLiftedILFunction<A>,
+ reg: Self,
+ size: usize,
+ ) -> MutableLiftedILExpr<'a, A, ValueExpr> {
+ match reg {
+ LowLevelILRegister::ArchReg(r) => R::lift_with_size(il, r, size),
+ LowLevelILRegister::Temp(t) => il.reg(size, LowLevelILRegister::Temp(t)),
+ }
+ }
+}
+
+impl<'a, R: ArchReg, A: 'a + Architecture> LiftableLowLevelIL<'a, A>
+ for LowLevelILRegisterOrConstant<R>
+where
+ R: LiftableLowLevelILWithSize<'a, A, Result = ValueExpr> + Into<LowLevelILRegister<R>>,
+{
+ type Result = ValueExpr;
+
+ fn lift(
+ il: &'a MutableLiftedILFunction<A>,
+ reg: Self,
+ ) -> MutableLiftedILExpr<'a, A, Self::Result> {
+ match reg {
+ LowLevelILRegisterOrConstant::Register(size, r) => {
+ LowLevelILRegister::<R>::lift_with_size(il, r, size)
+ }
+ LowLevelILRegisterOrConstant::Constant(size, value) => {
+ u64::lift_with_size(il, value, size)
+ }
+ }
+ }
+}
+
+impl<'a, R: ArchReg, A: 'a + Architecture> LiftableLowLevelILWithSize<'a, A>
+ for LowLevelILRegisterOrConstant<R>
+where
+ R: LiftableLowLevelILWithSize<'a, A> + Into<LowLevelILRegister<R>>,
+{
+ fn lift_with_size(
+ il: &'a MutableLiftedILFunction<A>,
+ reg: Self,
+ size: usize,
+ ) -> MutableLiftedILExpr<'a, A, ValueExpr> {
+ // TODO ensure requested size is compatible with size of this constant
+ match reg {
+ LowLevelILRegisterOrConstant::Register(_, r) => {
+ LowLevelILRegister::<R>::lift_with_size(il, r, size)
+ }
+ LowLevelILRegisterOrConstant::Constant(_, value) => {
+ u64::lift_with_size(il, value, size)
+ }
+ }
+ }
+}
+
+impl<'a, A, R> LiftableLowLevelIL<'a, A>
+ for LowLevelILExpression<'a, A, Mutable, NonSSA<LiftedNonSSA>, R>
+where
+ A: 'a + Architecture,
+ R: ExpressionResultType,
+{
+ type Result = R;
+
+ fn lift(
+ il: &'a MutableLiftedILFunction<A>,
+ expr: Self,
+ ) -> MutableLiftedILExpr<'a, A, Self::Result> {
+ debug_assert!(expr.function.handle == il.handle);
+ expr
+ }
+}
+
+impl<'a, A: 'a + Architecture> LiftableLowLevelILWithSize<'a, A>
+ for LowLevelILExpression<'a, A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr>
+{
+ fn lift_with_size(
+ il: &'a MutableLiftedILFunction<A>,
+ expr: Self,
+ _size: usize,
+ ) -> MutableLiftedILExpr<'a, A, Self::Result> {
+ #[cfg(debug_assertions)]
+ {
+ use crate::low_level_il::ExpressionHandler;
+ if let Some(expr_size) = expr.kind().size() {
+ if expr_size != _size {
+ log::warn!(
+ "il @ {:x} attempted to lift {} byte expression as {} bytes",
+ il.current_address(),
+ expr_size,
+ _size
+ );
+ }
+ }
+ }
+
+ LiftableLowLevelIL::lift(il, expr)
+ }
+}
+
+impl<'func, A, R> LowLevelILExpression<'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.index.0, op) }
+ self
+ }
+
+ pub fn append(self) {
+ self.function.add_instruction(self);
+ }
+}
+
+pub struct ExpressionBuilder<'func, A, R>
+where
+ A: 'func + Architecture,
+ R: ExpressionResultType,
+{
+ function: &'func LowLevelILFunction<A, Mutable, NonSSA<LiftedNonSSA>>,
+ op: BNLowLevelILOperation,
+ size: usize,
+ flag_write: FlagWriteId,
+ 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 from_expr(expr: LowLevelILExpression<'a, A, Mutable, NonSSA<LiftedNonSSA>, R>) -> Self {
+ use binaryninjacore_sys::BNGetLowLevelILByIndex;
+
+ let instr = unsafe { BNGetLowLevelILByIndex(expr.function.handle, expr.index.0) };
+
+ ExpressionBuilder {
+ function: expr.function,
+ op: instr.operation,
+ size: instr.size,
+ flag_write: FlagWriteId(instr.flags),
+ op1: instr.operands[0],
+ op2: instr.operands[1],
+ op3: instr.operands[2],
+ op4: instr.operands[3],
+ _ty: PhantomData,
+ }
+ }
+
+ pub fn with_flag_write(mut self, flag_write: A::FlagWrite) -> Self {
+ // TODO verify valid id
+ self.flag_write = flag_write.id();
+ self
+ }
+
+ pub fn build(self) -> LowLevelILExpression<'a, A, Mutable, NonSSA<LiftedNonSSA>, R> {
+ use binaryninjacore_sys::BNLowLevelILAddExpr;
+
+ let expr_idx = unsafe {
+ BNLowLevelILAddExpr(
+ self.function.handle,
+ self.op,
+ self.size,
+ self.flag_write.0,
+ self.op1,
+ self.op2,
+ self.op3,
+ self.op4,
+ )
+ };
+
+ LowLevelILExpression::new(self.function, LowLevelExpressionIndex(expr_idx))
+ }
+
+ pub fn with_source_operand(
+ self,
+ op: u32,
+ ) -> LowLevelILExpression<'a, A, Mutable, NonSSA<LiftedNonSSA>, R> {
+ self.build().with_source_operand(op)
+ }
+
+ pub fn append(self) {
+ let expr = self.build();
+ expr.function.add_instruction(expr);
+ }
+}
+
+impl<'a, A, R> LiftableLowLevelIL<'a, A> for ExpressionBuilder<'a, A, R>
+where
+ A: 'a + Architecture,
+ R: ExpressionResultType,
+{
+ type Result = R;
+
+ fn lift(
+ il: &'a MutableLiftedILFunction<A>,
+ expr: Self,
+ ) -> MutableLiftedILExpr<'a, A, Self::Result> {
+ debug_assert!(expr.function.handle == il.handle);
+
+ expr.build()
+ }
+}
+
+impl<'a, A> LiftableLowLevelILWithSize<'a, A> for ExpressionBuilder<'a, A, ValueExpr>
+where
+ A: 'a + Architecture,
+{
+ fn lift_with_size(
+ il: &'a MutableLiftedILFunction<A>,
+ expr: Self,
+ _size: usize,
+ ) -> MutableLiftedILExpr<'a, A, 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) {
+ log::warn!(
+ "il @ {:x} attempted to lift {} byte expression builder as {} bytes",
+ il.current_address(),
+ expr.size,
+ _size
+ );
+ }
+ }
+
+ LiftableLowLevelIL::lift(il, expr)
+ }
+}
+
+macro_rules! no_arg_lifter {
+ ($name:ident, $op:ident, $result:ty) => {
+ pub fn $name(&self) -> LowLevelILExpression<A, Mutable, NonSSA<LiftedNonSSA>, $result> {
+ use binaryninjacore_sys::BNLowLevelILAddExpr;
+ use binaryninjacore_sys::BNLowLevelILOperation::$op;
+
+ let expr_idx = unsafe { BNLowLevelILAddExpr(self.handle, $op, 0, 0, 0, 0, 0, 0) };
+
+ LowLevelILExpression::new(self, LowLevelExpressionIndex(expr_idx))
+ }
+ };
+}
+
+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,
+ flag_write: FlagWriteId(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,
+ ) -> LowLevelILExpression<'a, A, Mutable, NonSSA<LiftedNonSSA>, $result>
+ where
+ E: LiftableLowLevelIL<'a, A, Result = ValueExpr>,
+ {
+ use binaryninjacore_sys::BNLowLevelILAddExpr;
+ use binaryninjacore_sys::BNLowLevelILOperation::$op;
+
+ let expr = E::lift(self, expr);
+
+ let expr_idx = unsafe {
+ BNLowLevelILAddExpr(self.handle, $op, 0, 0, expr.index.0 as u64, 0, 0, 0)
+ };
+
+ LowLevelILExpression::new(self, LowLevelExpressionIndex(expr_idx))
+ }
+ };
+}
+
+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: LiftableLowLevelILWithSize<'a, A>,
+ {
+ use binaryninjacore_sys::BNLowLevelILOperation::$op;
+
+ let expr = E::lift_with_size(self, expr, size);
+
+ ExpressionBuilder {
+ function: self,
+ op: $op,
+ size,
+ flag_write: FlagWriteId(0),
+ op1: expr.index.0 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: LiftableLowLevelILWithSize<'a, A>,
+ {
+ use binaryninjacore_sys::BNLowLevelILOperation::$op;
+
+ let expr = E::lift(self, expr);
+
+ ExpressionBuilder {
+ function: self,
+ op: $op,
+ size,
+ flag_write: FlagWriteId(0),
+ op1: expr.index.0 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: LiftableLowLevelILWithSize<'a, A>,
+ R: LiftableLowLevelILWithSize<'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,
+ flag_write: FlagWriteId(0),
+ op1: left.index.0 as u64,
+ op2: right.index.0 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: LiftableLowLevelILWithSize<'a, A>,
+ R: LiftableLowLevelILWithSize<'a, A>,
+ C: LiftableLowLevelILWithSize<'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, 0);
+
+ ExpressionBuilder {
+ function: self,
+ op: $op,
+ size,
+ flag_write: FlagWriteId(0),
+ op1: left.index.0 as u64,
+ op2: right.index.0 as u64,
+ op3: carry.index.0 as u64,
+ op4: 0,
+ _ty: PhantomData,
+ }
+ }
+ };
+}
+
+impl<A> LowLevelILFunction<A, Mutable, NonSSA<LiftedNonSSA>>
+where
+ A: Architecture,
+{
+ pub const NO_INPUTS: [ExpressionBuilder<'static, A, ValueExpr>; 0] = [];
+ pub const NO_OUTPUTS: [LowLevelILRegister<A::Register>; 0] = [];
+
+ pub fn expression<'a, E: LiftableLowLevelIL<'a, A>>(
+ &'a self,
+ expr: E,
+ ) -> LowLevelILExpression<'a, A, Mutable, NonSSA<LiftedNonSSA>, E::Result> {
+ E::lift(self, expr)
+ }
+
+ pub fn add_instruction<'a, E: LiftableLowLevelIL<'a, A>>(&'a self, expr: E) {
+ let expr = self.expression(expr);
+
+ unsafe {
+ use binaryninjacore_sys::BNLowLevelILAddInstruction;
+ BNLowLevelILAddInstruction(self.handle, expr.index.0);
+ }
+ }
+
+ pub unsafe fn replace_expression<'a, E: LiftableLowLevelIL<'a, A>>(
+ &'a self,
+ replaced_expr_index: LowLevelExpressionIndex,
+ replacement: E,
+ ) -> bool {
+ use binaryninjacore_sys::BNReplaceLowLevelILExpr;
+ if replaced_expr_index.0 >= self.expression_count() {
+ // Invalid expression index, cant replace expression.
+ return false;
+ }
+ let expr = self.expression(replacement);
+ BNReplaceLowLevelILExpr(self.handle, replaced_expr_index.0, expr.index.0);
+ true
+ }
+
+ pub fn const_int(
+ &self,
+ size: usize,
+ val: u64,
+ ) -> LowLevelILExpression<A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr> {
+ use binaryninjacore_sys::BNLowLevelILAddExpr;
+ use binaryninjacore_sys::BNLowLevelILOperation::LLIL_CONST;
+
+ let expr_idx =
+ unsafe { BNLowLevelILAddExpr(self.handle, LLIL_CONST, size, 0, val, 0, 0, 0) };
+
+ LowLevelILExpression::new(self, LowLevelExpressionIndex(expr_idx))
+ }
+
+ pub fn const_ptr_sized(
+ &self,
+ size: usize,
+ val: u64,
+ ) -> LowLevelILExpression<A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr> {
+ use binaryninjacore_sys::BNLowLevelILAddExpr;
+ use binaryninjacore_sys::BNLowLevelILOperation::LLIL_CONST_PTR;
+
+ let expr_idx =
+ unsafe { BNLowLevelILAddExpr(self.handle, LLIL_CONST_PTR, size, 0, val, 0, 0, 0) };
+
+ LowLevelILExpression::new(self, LowLevelExpressionIndex(expr_idx))
+ }
+
+ pub fn const_ptr(
+ &self,
+ val: u64,
+ ) -> LowLevelILExpression<A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr> {
+ self.const_ptr_sized(self.arch().address_size(), val)
+ }
+
+ pub fn trap(
+ &self,
+ val: u64,
+ ) -> LowLevelILExpression<A, Mutable, NonSSA<LiftedNonSSA>, VoidExpr> {
+ use binaryninjacore_sys::BNLowLevelILAddExpr;
+ use binaryninjacore_sys::BNLowLevelILOperation::LLIL_TRAP;
+
+ let expr_idx = unsafe { BNLowLevelILAddExpr(self.handle, LLIL_TRAP, 0, 0, val, 0, 0, 0) };
+
+ LowLevelILExpression::new(self, LowLevelExpressionIndex(expr_idx))
+ }
+
+ 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);
+ // TODO: LLIL_JUMP_TO
+
+ pub fn if_expr<'a: 'b, 'b, C>(
+ &'a self,
+ cond: C,
+ true_label: &'b mut LowLevelILLabel,
+ false_label: &'b mut LowLevelILLabel,
+ ) -> LowLevelILExpression<'a, A, Mutable, NonSSA<LiftedNonSSA>, VoidExpr>
+ where
+ C: LiftableLowLevelIL<'b, A, Result = ValueExpr>,
+ {
+ use binaryninjacore_sys::BNLowLevelILIf;
+
+ let cond = C::lift(self, cond);
+
+ let mut raw_true_label = BNLowLevelILLabel::from(*true_label);
+ let mut raw_false_label = BNLowLevelILLabel::from(*false_label);
+ let expr_idx = unsafe {
+ BNLowLevelILIf(
+ self.handle,
+ cond.index.0 as u64,
+ &mut raw_true_label,
+ &mut raw_false_label,
+ )
+ };
+
+ // Update the labels after they have been resolved.
+ let mut new_true_label = LowLevelILLabel::from(raw_true_label);
+ let mut new_false_label = LowLevelILLabel::from(raw_false_label);
+ if let Some(location) = true_label.location {
+ new_true_label.location = Some(location);
+ self.update_label_map_for_label(&new_true_label);
+ }
+ if let Some(location) = false_label.location {
+ new_false_label.location = Some(location);
+ self.update_label_map_for_label(&new_false_label);
+ }
+ *true_label = new_true_label;
+ *false_label = new_false_label;
+
+ LowLevelILExpression::new(self, LowLevelExpressionIndex(expr_idx))
+ }
+
+ // TODO: Wtf are these lifetimes??
+ pub fn goto<'a: 'b, 'b>(
+ &'a self,
+ label: &'b mut LowLevelILLabel,
+ ) -> LowLevelILExpression<'a, A, Mutable, NonSSA<LiftedNonSSA>, VoidExpr> {
+ use binaryninjacore_sys::BNLowLevelILGoto;
+
+ let mut raw_label = BNLowLevelILLabel::from(*label);
+ let expr_idx = unsafe { BNLowLevelILGoto(self.handle, &mut raw_label) };
+
+ // Update the labels after they have been resolved.
+ let mut new_label = LowLevelILLabel::from(raw_label);
+ if let Some(location) = label.location {
+ new_label.location = Some(location);
+ self.update_label_map_for_label(&new_label);
+ }
+ *label = new_label;
+
+ LowLevelILExpression::new(self, LowLevelExpressionIndex(expr_idx))
+ }
+
+ pub fn reg<R: Into<LowLevelILRegister<A::Register>>>(
+ &self,
+ size: usize,
+ reg: R,
+ ) -> LowLevelILExpression<A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr> {
+ use binaryninjacore_sys::BNLowLevelILAddExpr;
+ use binaryninjacore_sys::BNLowLevelILOperation::LLIL_REG;
+
+ // TODO verify valid id
+ let reg = reg.into().id();
+
+ let expr_idx =
+ unsafe { BNLowLevelILAddExpr(self.handle, LLIL_REG, size, 0, reg.0 as u64, 0, 0, 0) };
+
+ LowLevelILExpression::new(self, LowLevelExpressionIndex(expr_idx))
+ }
+
+ pub fn reg_split<
+ H: Into<LowLevelILRegister<A::Register>>,
+ L: Into<LowLevelILRegister<A::Register>>,
+ >(
+ &self,
+ size: usize,
+ hi_reg: H,
+ lo_reg: L,
+ ) -> LowLevelILExpression<A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr> {
+ use binaryninjacore_sys::BNLowLevelILAddExpr;
+ use binaryninjacore_sys::BNLowLevelILOperation::LLIL_REG_SPLIT;
+
+ // TODO verify valid id
+ let hi_reg = hi_reg.into().id();
+ let lo_reg = lo_reg.into().id();
+
+ let expr_idx = unsafe {
+ BNLowLevelILAddExpr(
+ self.handle,
+ LLIL_REG_SPLIT,
+ size,
+ 0,
+ hi_reg.0 as u64,
+ lo_reg.0 as u64,
+ 0,
+ 0,
+ )
+ };
+
+ LowLevelILExpression::new(self, LowLevelExpressionIndex(expr_idx))
+ }
+
+ pub fn set_reg<'a, R, E>(
+ &'a self,
+ size: usize,
+ dest_reg: R,
+ expr: E,
+ ) -> ExpressionBuilder<'a, A, VoidExpr>
+ where
+ R: Into<LowLevelILRegister<A::Register>>,
+ E: LiftableLowLevelILWithSize<'a, A>,
+ {
+ use binaryninjacore_sys::BNLowLevelILOperation::LLIL_SET_REG;
+
+ // TODO verify valid id
+ let dest_reg = dest_reg.into().id();
+
+ let expr = E::lift_with_size(self, expr, size);
+
+ ExpressionBuilder {
+ function: self,
+ op: LLIL_SET_REG,
+ size,
+ // TODO: Make these optional?
+ flag_write: FlagWriteId(0),
+ op1: dest_reg.0 as u64,
+ op2: expr.index.0 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<LowLevelILRegister<A::Register>>,
+ L: Into<LowLevelILRegister<A::Register>>,
+ E: LiftableLowLevelILWithSize<'a, A>,
+ {
+ use binaryninjacore_sys::BNLowLevelILOperation::LLIL_SET_REG_SPLIT;
+
+ // TODO verify valid id
+ let hi_reg = hi_reg.into().id();
+ let lo_reg = lo_reg.into().id();
+
+ let expr = E::lift_with_size(self, expr, size);
+
+ ExpressionBuilder {
+ function: self,
+ op: LLIL_SET_REG_SPLIT,
+ size,
+ // TODO: Make these optional?
+ flag_write: FlagWriteId(0),
+ op1: hi_reg.0 as u64,
+ op2: lo_reg.0 as u64,
+ op3: expr.index.0 as u64,
+ op4: 0,
+ _ty: PhantomData,
+ }
+ }
+
+ pub fn flag(
+ &self,
+ flag: A::Flag,
+ ) -> LowLevelILExpression<A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr> {
+ use binaryninjacore_sys::BNLowLevelILAddExpr;
+ use binaryninjacore_sys::BNLowLevelILOperation::LLIL_FLAG;
+
+ // TODO verify valid id
+ let expr_idx = unsafe {
+ BNLowLevelILAddExpr(self.handle, LLIL_FLAG, 0, 0, flag.id().0 as u64, 0, 0, 0)
+ };
+
+ LowLevelILExpression::new(self, LowLevelExpressionIndex(expr_idx))
+ }
+
+ pub fn flag_cond(
+ &self,
+ cond: FlagCondition,
+ ) -> LowLevelILExpression<A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr> {
+ use binaryninjacore_sys::BNLowLevelILAddExpr;
+ use binaryninjacore_sys::BNLowLevelILOperation::LLIL_FLAG_COND;
+
+ // TODO verify valid id
+ let expr_idx =
+ unsafe { BNLowLevelILAddExpr(self.handle, LLIL_FLAG_COND, 0, 0, cond as u64, 0, 0, 0) };
+
+ LowLevelILExpression::new(self, LowLevelExpressionIndex(expr_idx))
+ }
+
+ pub fn flag_group(
+ &self,
+ group: A::FlagGroup,
+ ) -> LowLevelILExpression<A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr> {
+ use binaryninjacore_sys::BNLowLevelILAddExpr;
+ use binaryninjacore_sys::BNLowLevelILOperation::LLIL_FLAG_GROUP;
+
+ // TODO verify valid id
+ let expr_idx = unsafe {
+ BNLowLevelILAddExpr(
+ self.handle,
+ LLIL_FLAG_GROUP,
+ 0,
+ 0,
+ group.id().0 as u64,
+ 0,
+ 0,
+ 0,
+ )
+ };
+
+ LowLevelILExpression::new(self, LowLevelExpressionIndex(expr_idx))
+ }
+
+ pub fn set_flag<'a, E>(
+ &'a self,
+ dest_flag: A::Flag,
+ expr: E,
+ ) -> ExpressionBuilder<'a, A, VoidExpr>
+ where
+ E: LiftableLowLevelILWithSize<'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,
+ flag_write: FlagWriteId(0),
+ op1: dest_flag.id().0 as u64,
+ op2: expr.index.0 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: LiftableLowLevelIL<'a, A, Result = ValueExpr>,
+ {
+ use binaryninjacore_sys::BNLowLevelILOperation::LLIL_LOAD;
+
+ let expr = E::lift(self, source_mem);
+
+ ExpressionBuilder {
+ function: self,
+ op: LLIL_LOAD,
+ size,
+ flag_write: FlagWriteId(0),
+ op1: expr.index.0 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: LiftableLowLevelIL<'a, A, Result = ValueExpr>,
+ V: LiftableLowLevelILWithSize<'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,
+ flag_write: FlagWriteId(0),
+ op1: dest_mem.index.0 as u64,
+ op2: value.index.0 as u64,
+ op3: 0,
+ op4: 0,
+ _ty: PhantomData,
+ }
+ }
+
+ pub fn intrinsic<'a, O, OL, I, P, PL>(
+ &'a self,
+ outputs: OL,
+ intrinsic: I,
+ inputs: PL,
+ ) -> ExpressionBuilder<'a, A, VoidExpr>
+ where
+ O: Into<LowLevelILRegister<A::Register>>,
+ OL: IntoIterator<Item = O>,
+ I: Into<A::Intrinsic>,
+ P: LiftableLowLevelIL<'a, A, Result = ValueExpr>,
+ PL: IntoIterator<Item = P>,
+ {
+ use binaryninjacore_sys::BNLowLevelILOperation::{LLIL_CALL_PARAM, LLIL_INTRINSIC};
+ use binaryninjacore_sys::{BNLowLevelILAddExpr, BNLowLevelILAddOperandList};
+
+ let mut outputs: Vec<u64> = outputs
+ .into_iter()
+ .map(|output| output.into().id().0 as u64)
+ .collect();
+ let output_expr_idx =
+ unsafe { BNLowLevelILAddOperandList(self.handle, outputs.as_mut_ptr(), outputs.len()) };
+
+ let intrinsic: A::Intrinsic = intrinsic.into();
+
+ let mut inputs: Vec<u64> = inputs
+ .into_iter()
+ .map(|input| {
+ let input = P::lift(self, input);
+ input.index.0 as u64
+ })
+ .collect();
+ let input_list_expr_idx =
+ unsafe { BNLowLevelILAddOperandList(self.handle, inputs.as_mut_ptr(), inputs.len()) };
+ let input_expr_idx = unsafe {
+ BNLowLevelILAddExpr(
+ self.handle,
+ LLIL_CALL_PARAM,
+ 0,
+ 0,
+ inputs.len() as u64,
+ input_list_expr_idx as u64,
+ 0,
+ 0,
+ )
+ };
+
+ ExpressionBuilder {
+ function: self,
+ op: LLIL_INTRINSIC,
+ size: 0,
+ flag_write: FlagWriteId(0),
+ op1: outputs.len() as u64,
+ op2: output_expr_idx as u64,
+ op3: intrinsic.id().0 as u64,
+ op4: input_expr_idx as u64,
+ _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);
+
+ binary_op_lifter!(fadd, LLIL_FADD);
+ binary_op_lifter!(fsub, LLIL_FSUB);
+ binary_op_lifter!(fmul, LLIL_FMUL);
+ binary_op_lifter!(fdiv, LLIL_FDIV);
+ sized_unary_op_lifter!(fsqrt, LLIL_FSQRT, ValueExpr);
+ sized_unary_op_lifter!(fneg, LLIL_FNEG, ValueExpr);
+ sized_unary_op_lifter!(fabs, LLIL_FABS, ValueExpr);
+ sized_unary_op_lifter!(float_to_int, LLIL_FLOAT_TO_INT, ValueExpr);
+ sized_unary_op_lifter!(int_to_float, LLIL_INT_TO_FLOAT, ValueExpr);
+ sized_unary_op_lifter!(float_conv, LLIL_FLOAT_CONV, ValueExpr);
+ sized_unary_op_lifter!(round_to_int, LLIL_ROUND_TO_INT, ValueExpr);
+ sized_unary_op_lifter!(floor, LLIL_FLOOR, ValueExpr);
+ sized_unary_op_lifter!(ceil, LLIL_CEIL, ValueExpr);
+ sized_unary_op_lifter!(ftrunc, LLIL_FTRUNC, ValueExpr);
+ binary_op_lifter!(fcmp_e, LLIL_FCMP_E);
+ binary_op_lifter!(fcmp_ne, LLIL_FCMP_NE);
+ binary_op_lifter!(fcmp_lt, LLIL_FCMP_LT);
+ binary_op_lifter!(fcmp_le, LLIL_FCMP_LE);
+ binary_op_lifter!(fcmp_ge, LLIL_FCMP_GE);
+ binary_op_lifter!(fcmp_gt, LLIL_FCMP_GT);
+ binary_op_lifter!(fcmp_o, LLIL_FCMP_O);
+ binary_op_lifter!(fcmp_uo, LLIL_FCMP_UO);
+
+ 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.handle, loc.addr);
+ }
+ }
+
+ pub fn label_for_address<L: Into<Location>>(&self, loc: L) -> Option<LowLevelILLabel> {
+ use binaryninjacore_sys::BNGetLowLevelILLabelForAddress;
+
+ let loc: Location = loc.into();
+ let arch = loc.arch.unwrap_or_else(|| *self.arch().as_ref());
+ let raw_label =
+ unsafe { BNGetLowLevelILLabelForAddress(self.handle, arch.handle, loc.addr) };
+ match raw_label.is_null() {
+ false => {
+ let mut label = unsafe { LowLevelILLabel::from(*raw_label) };
+ // Set the location so that calls to [Self::update_label_map_for_label] will update the label map.
+ label.location = Some(loc);
+ Some(label)
+ }
+ true => None,
+ }
+ }
+
+ /// Call this after updating the label through an il operation or via [`Self::mark_label`].
+ fn update_label_map_for_label(&self, label: &LowLevelILLabel) {
+ use binaryninjacore_sys::BNGetLowLevelILLabelForAddress;
+
+ // Only need to update the label if there is an associated address.
+ if let Some(loc) = label.location {
+ let arch = loc.arch.unwrap_or_else(|| *self.arch().as_ref());
+ // Add the label into the label map
+ unsafe { BNAddLowLevelILLabelForAddress(self.handle, arch.handle, loc.addr) };
+ // Retrieve a pointer to the label in the map
+ let raw_label =
+ unsafe { BNGetLowLevelILLabelForAddress(self.handle, arch.handle, loc.addr) };
+ // We should always have a valid label here
+ assert!(!raw_label.is_null(), "Failed to add label for address!");
+ // Update the label in the map with `label`
+ unsafe { *raw_label = label.into() };
+ }
+ }
+
+ pub fn mark_label(&self, label: &mut LowLevelILLabel) {
+ use binaryninjacore_sys::BNLowLevelILMarkLabel;
+
+ let mut raw_label = BNLowLevelILLabel::from(*label);
+ unsafe { BNLowLevelILMarkLabel(self.handle, &mut raw_label) };
+ let mut new_label = LowLevelILLabel::from(raw_label);
+ if let Some(location) = label.location {
+ new_label.location = Some(location);
+ self.update_label_map_for_label(&new_label);
+ }
+ *label = new_label;
+ }
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+pub struct LowLevelILLabel {
+ /// Used to update the label map if the label is associated with a location.
+ pub location: Option<Location>,
+ pub resolved: bool,
+ // TODO: This expr_ref is not actually a valid one sometimes...
+ // TODO: We should make these non public and only accessible if resolved is true.
+ pub expr_ref: LowLevelExpressionIndex,
+ // TODO: If this is 7 this label is not valid.
+ pub operand: usize,
+}
+
+impl LowLevelILLabel {
+ pub fn new() -> Self {
+ use binaryninjacore_sys::BNLowLevelILInitLabel;
+
+ let mut raw_label = BNLowLevelILLabel::default();
+ unsafe { BNLowLevelILInitLabel(&mut raw_label) };
+ raw_label.into()
+ }
+}
+
+impl From<BNLowLevelILLabel> for LowLevelILLabel {
+ fn from(value: BNLowLevelILLabel) -> Self {
+ Self {
+ location: None,
+ resolved: value.resolved,
+ expr_ref: LowLevelExpressionIndex(value.ref_),
+ operand: value.operand,
+ }
+ }
+}
+
+impl From<LowLevelILLabel> for BNLowLevelILLabel {
+ fn from(value: LowLevelILLabel) -> Self {
+ Self {
+ resolved: value.resolved,
+ ref_: value.expr_ref.0,
+ operand: value.operand,
+ }
+ }
+}
+
+impl From<&LowLevelILLabel> for BNLowLevelILLabel {
+ fn from(value: &LowLevelILLabel) -> Self {
+ Self::from(*value)
+ }
+}
+
+impl Default for LowLevelILLabel {
+ fn default() -> Self {
+ Self::new()
+ }
+}
diff --git a/rust/src/low_level_il/operation.rs b/rust/src/low_level_il/operation.rs
new file mode 100644
index 00000000..dcc761dd
--- /dev/null
+++ b/rust/src/low_level_il/operation.rs
@@ -0,0 +1,1367 @@
+// Copyright 2021-2024 Vector 35 Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use binaryninjacore_sys::{BNGetLowLevelILByIndex, BNLowLevelILInstruction};
+
+use super::*;
+use crate::architecture::{FlagGroupId, FlagId, FlagWriteId, IntrinsicId};
+use std::collections::BTreeMap;
+use std::fmt::{Debug, Formatter};
+use std::marker::PhantomData;
+use std::mem;
+
+pub struct Operation<'func, A, M, F, O>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+ O: OperationArguments,
+{
+ pub(crate) function: &'func LowLevelILFunction<A, M, F>,
+ pub(crate) op: BNLowLevelILInstruction,
+ _args: PhantomData<O>,
+}
+
+impl<'func, A, M, F, O> Operation<'func, A, M, F, O>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+ O: OperationArguments,
+{
+ pub(crate) fn new(
+ function: &'func LowLevelILFunction<A, M, F>,
+ op: BNLowLevelILInstruction,
+ ) -> Self {
+ Self {
+ function,
+ op,
+ _args: PhantomData,
+ }
+ }
+
+ pub fn address(&self) -> u64 {
+ self.op.address
+ }
+}
+
+impl<A, M, O> Operation<'_, A, M, NonSSA<LiftedNonSSA>, O>
+where
+ A: 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(FlagWriteId(id)),
+ }
+ }
+}
+
+// LLIL_NOP, LLIL_NORET, LLIL_BP, LLIL_UNDEF, LLIL_UNIMPL
+pub struct NoArgs;
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, NoArgs>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("NoArgs").finish()
+ }
+}
+
+// LLIL_POP
+pub struct Pop;
+
+impl<A, M, F> Operation<'_, A, M, F, Pop>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn size(&self) -> usize {
+ self.op.size
+ }
+}
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, Pop>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Pop")
+ .field("address", &self.address())
+ .field("size", &self.size())
+ .finish()
+ }
+}
+
+// LLIL_SYSCALL, LLIL_SYSCALL_SSA
+pub struct Syscall;
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, Syscall>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Syscall").finish()
+ }
+}
+
+// LLIL_INTRINSIC, LLIL_INTRINSIC_SSA
+pub struct Intrinsic;
+
+impl<A, M, F> Operation<'_, A, M, F, Intrinsic>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ // TODO: Support register and expression lists
+ pub fn intrinsic(&self) -> Option<A::Intrinsic> {
+ let raw_id = self.op.operands[2] as u32;
+ self.function.arch().intrinsic_from_id(IntrinsicId(raw_id))
+ }
+}
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, Intrinsic>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Intrinsic")
+ .field("address", &self.address())
+ .field("size", &self.intrinsic())
+ .finish()
+ }
+}
+
+// LLIL_SET_REG, LLIL_SET_REG_SSA, LLIL_SET_REG_PARTIAL_SSA
+pub struct SetReg;
+
+impl<'func, A, M, F> Operation<'func, A, M, F, SetReg>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn size(&self) -> usize {
+ self.op.size
+ }
+
+ pub fn dest_reg(&self) -> LowLevelILRegister<A::Register> {
+ let raw_id = self.op.operands[0] as u32;
+
+ if raw_id >= 0x8000_0000 {
+ LowLevelILRegister::Temp(raw_id & 0x7fff_ffff)
+ } else {
+ self.function
+ .arch()
+ .register_from_id(RegisterId(raw_id))
+ .map(LowLevelILRegister::ArchReg)
+ .unwrap_or_else(|| {
+ log::error!(
+ "got garbage register from LLIL_SET_REG @ 0x{:x}",
+ self.op.address
+ );
+
+ LowLevelILRegister::Temp(0)
+ })
+ }
+ }
+
+ pub fn source_expr(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[1] as usize),
+ )
+ }
+}
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, SetReg>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("SetReg")
+ .field("address", &self.address())
+ .field("size", &self.size())
+ .field("dest_reg", &self.dest_reg())
+ .field("source_expr", &self.source_expr())
+ .finish()
+ }
+}
+
+// LLIL_SET_REG_SPLIT, LLIL_SET_REG_SPLIT_SSA
+pub struct SetRegSplit;
+
+impl<'func, A, M, F> Operation<'func, A, M, F, SetRegSplit>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn size(&self) -> usize {
+ self.op.size
+ }
+
+ pub fn dest_reg_high(&self) -> LowLevelILRegister<A::Register> {
+ let raw_id = self.op.operands[0] as u32;
+
+ if raw_id >= 0x8000_0000 {
+ LowLevelILRegister::Temp(raw_id & 0x7fff_ffff)
+ } else {
+ self.function
+ .arch()
+ .register_from_id(RegisterId(raw_id))
+ .map(LowLevelILRegister::ArchReg)
+ .unwrap_or_else(|| {
+ log::error!(
+ "got garbage register from LLIL_SET_REG_SPLIT @ 0x{:x}",
+ self.op.address
+ );
+
+ LowLevelILRegister::Temp(0)
+ })
+ }
+ }
+
+ pub fn dest_reg_low(&self) -> LowLevelILRegister<A::Register> {
+ let raw_id = self.op.operands[1] as u32;
+
+ if raw_id >= 0x8000_0000 {
+ LowLevelILRegister::Temp(raw_id & 0x7fff_ffff)
+ } else {
+ self.function
+ .arch()
+ .register_from_id(RegisterId(raw_id))
+ .map(LowLevelILRegister::ArchReg)
+ .unwrap_or_else(|| {
+ log::error!(
+ "got garbage register from LLIL_SET_REG_SPLIT @ 0x{:x}",
+ self.op.address
+ );
+
+ LowLevelILRegister::Temp(0)
+ })
+ }
+ }
+
+ pub fn source_expr(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[2] as usize),
+ )
+ }
+}
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, SetRegSplit>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("SetRegSplit")
+ .field("address", &self.address())
+ .field("size", &self.size())
+ .field("dest_reg_high", &self.dest_reg_high())
+ .field("dest_reg_low", &self.dest_reg_low())
+ .field("source_expr", &self.source_expr())
+ .finish()
+ }
+}
+
+// LLIL_SET_FLAG, LLIL_SET_FLAG_SSA
+pub struct SetFlag;
+
+impl<'func, A, M, F> Operation<'func, A, M, F, SetFlag>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn dest_flag(&self) -> A::Flag {
+ // TODO: Error handling?
+ // TODO: Test this.
+ self.function
+ .arch()
+ .flag_from_id(FlagId(self.op.operands[0] as u32))
+ .unwrap()
+ }
+
+ pub fn source_expr(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[1] as usize),
+ )
+ }
+}
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, SetFlag>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("SetFlag")
+ .field("address", &self.address())
+ .field("dest_flag", &self.dest_flag())
+ .field("source_expr", &self.source_expr())
+ .finish()
+ }
+}
+
+// LLIL_LOAD, LLIL_LOAD_SSA
+pub struct Load;
+
+impl<'func, A, M, F> Operation<'func, A, M, F, Load>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn size(&self) -> usize {
+ self.op.size
+ }
+
+ pub fn source_mem_expr(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[0] as usize),
+ )
+ }
+}
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, Load>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Load")
+ .field("address", &self.address())
+ .field("size", &self.size())
+ .field("source_mem_expr", &self.source_mem_expr())
+ .finish()
+ }
+}
+
+// LLIL_STORE, LLIL_STORE_SSA
+pub struct Store;
+
+impl<'func, A, M, F> Operation<'func, A, M, F, Store>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn size(&self) -> usize {
+ self.op.size
+ }
+
+ pub fn dest_mem_expr(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[0] as usize),
+ )
+ }
+
+ pub fn source_expr(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[1] as usize),
+ )
+ }
+}
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, Store>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Store")
+ .field("address", &self.address())
+ .field("size", &self.size())
+ .field("dest_mem_expr", &self.dest_mem_expr())
+ .field("source_expr", &self.source_expr())
+ .finish()
+ }
+}
+
+// LLIL_REG, LLIL_REG_SSA, LLIL_REG_SSA_PARTIAL
+pub struct Reg;
+
+impl<A, M, F> Operation<'_, A, M, F, Reg>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn size(&self) -> usize {
+ self.op.size
+ }
+
+ pub fn source_reg(&self) -> LowLevelILRegister<A::Register> {
+ let raw_id = self.op.operands[0] as u32;
+
+ if raw_id >= 0x8000_0000 {
+ LowLevelILRegister::Temp(raw_id & 0x7fff_ffff)
+ } else {
+ self.function
+ .arch()
+ .register_from_id(RegisterId(raw_id))
+ .map(LowLevelILRegister::ArchReg)
+ .unwrap_or_else(|| {
+ log::error!(
+ "got garbage register from LLIL_REG @ 0x{:x}",
+ self.op.address
+ );
+
+ LowLevelILRegister::Temp(0)
+ })
+ }
+ }
+}
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, Reg>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Reg")
+ .field("address", &self.address())
+ .field("size", &self.size())
+ .field("source_reg", &self.source_reg())
+ .finish()
+ }
+}
+
+// LLIL_REG_SPLIT, LLIL_REG_SPLIT_SSA
+pub struct RegSplit;
+
+impl<A, M, F> Operation<'_, A, M, F, RegSplit>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn size(&self) -> usize {
+ self.op.size
+ }
+
+ pub fn low_reg(&self) -> LowLevelILRegister<A::Register> {
+ let raw_id = self.op.operands[0] as u32;
+
+ if raw_id >= 0x8000_0000 {
+ LowLevelILRegister::Temp(raw_id & 0x7fff_ffff)
+ } else {
+ self.function
+ .arch()
+ .register_from_id(RegisterId(raw_id))
+ .map(LowLevelILRegister::ArchReg)
+ .unwrap_or_else(|| {
+ log::error!(
+ "got garbage register from LLIL_REG @ 0x{:x}",
+ self.op.address
+ );
+
+ LowLevelILRegister::Temp(0)
+ })
+ }
+ }
+
+ pub fn high_reg(&self) -> LowLevelILRegister<A::Register> {
+ let raw_id = self.op.operands[1] as u32;
+
+ if raw_id >= 0x8000_0000 {
+ LowLevelILRegister::Temp(raw_id & 0x7fff_ffff)
+ } else {
+ self.function
+ .arch()
+ .register_from_id(RegisterId(raw_id))
+ .map(LowLevelILRegister::ArchReg)
+ .unwrap_or_else(|| {
+ log::error!(
+ "got garbage register from LLIL_REG @ 0x{:x}",
+ self.op.address
+ );
+
+ LowLevelILRegister::Temp(0)
+ })
+ }
+ }
+}
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, RegSplit>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("RegSplit")
+ .field("address", &self.address())
+ .field("size", &self.size())
+ .field("low_reg", &self.low_reg())
+ .field("high_reg", &self.high_reg())
+ .finish()
+ }
+}
+
+// LLIL_FLAG, LLIL_FLAG_SSA
+pub struct Flag;
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, Flag>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Flag").finish()
+ }
+}
+
+// LLIL_FLAG_BIT, LLIL_FLAG_BIT_SSA
+pub struct FlagBit;
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, FlagBit>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("FlagBit").finish()
+ }
+}
+
+// LLIL_JUMP
+pub struct Jump;
+
+impl<'func, A, M, F> Operation<'func, A, M, F, Jump>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn target(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[0] as usize),
+ )
+ }
+}
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, Jump>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Jump")
+ .field("target", &self.target())
+ .finish()
+ }
+}
+
+// LLIL_JUMP_TO
+pub struct JumpTo;
+
+struct TargetListIter<'func, A, M, F>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ function: &'func LowLevelILFunction<A, M, F>,
+ cursor: BNLowLevelILInstruction,
+ cursor_operand: usize,
+}
+
+impl<A, M, F> TargetListIter<'_, A, M, F>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn next(&mut self) -> u64 {
+ if self.cursor_operand >= 3 {
+ self.cursor = unsafe {
+ BNGetLowLevelILByIndex(self.function.handle, self.cursor.operands[3] as usize)
+ };
+ self.cursor_operand = 0;
+ }
+ let result = self.cursor.operands[self.cursor_operand];
+ self.cursor_operand += 1;
+ result
+ }
+}
+
+impl<'func, A, M, F> Operation<'func, A, M, F, JumpTo>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn target(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[0] as usize),
+ )
+ }
+
+ pub fn target_list(&self) -> BTreeMap<u64, LowLevelInstructionIndex> {
+ let mut result = BTreeMap::new();
+ let count = self.op.operands[1] as usize / 2;
+ let mut list = TargetListIter {
+ function: self.function,
+ cursor: unsafe {
+ BNGetLowLevelILByIndex(self.function.handle, self.op.operands[2] as usize)
+ },
+ cursor_operand: 0,
+ };
+
+ for _ in 0..count {
+ let value = list.next();
+ let target = LowLevelInstructionIndex(list.next() as usize);
+ result.insert(value, target);
+ }
+
+ result
+ }
+}
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, JumpTo>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("JumpTo")
+ .field("target", &self.target())
+ .field("target_list", &self.target_list())
+ .finish()
+ }
+}
+
+// LLIL_CALL, LLIL_CALL_SSA
+pub struct Call;
+
+impl<'func, A, M, F> Operation<'func, A, M, F, Call>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn target(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[0] as usize),
+ )
+ }
+
+ 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
+ }
+ }
+}
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, Call>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Call")
+ .field("target", &self.target())
+ .field("stack_adjust", &self.stack_adjust())
+ .finish()
+ }
+}
+
+// LLIL_RET
+pub struct Ret;
+
+impl<'func, A, M, F> Operation<'func, A, M, F, Ret>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn target(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[0] as usize),
+ )
+ }
+}
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, Ret>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Ret")
+ .field("target", &self.target())
+ .finish()
+ }
+}
+
+// LLIL_IF
+pub struct If;
+
+impl<'func, A, M, F> Operation<'func, A, M, F, If>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn condition(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[0] as usize),
+ )
+ }
+
+ pub fn true_target(&self) -> LowLevelILInstruction<'func, A, M, F> {
+ LowLevelILInstruction::new(
+ self.function,
+ LowLevelInstructionIndex(self.op.operands[1] as usize),
+ )
+ }
+
+ pub fn false_target(&self) -> LowLevelILInstruction<'func, A, M, F> {
+ LowLevelILInstruction::new(
+ self.function,
+ LowLevelInstructionIndex(self.op.operands[2] as usize),
+ )
+ }
+}
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, If>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("If")
+ .field("condition", &self.condition())
+ .field("true_target", &self.true_target())
+ .field("false_target", &self.false_target())
+ .finish()
+ }
+}
+
+// LLIL_GOTO
+pub struct Goto;
+
+impl<'func, A, M, F> Operation<'func, A, M, F, Goto>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn target(&self) -> LowLevelILInstruction<'func, A, M, F> {
+ LowLevelILInstruction::new(
+ self.function,
+ LowLevelInstructionIndex(self.op.operands[0] as usize),
+ )
+ }
+}
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, Goto>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Goto")
+ .field("target", &self.target())
+ .finish()
+ }
+}
+
+// LLIL_FLAG_COND
+// Valid only in Lifted IL
+pub struct FlagCond;
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, FlagCond>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("FlagCond").finish()
+ }
+}
+
+// LLIL_FLAG_GROUP
+// Valid only in Lifted IL
+pub struct FlagGroup;
+
+impl<A, M> Operation<'_, A, M, NonSSA<LiftedNonSSA>, FlagGroup>
+where
+ A: 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(FlagGroupId(id))
+ .unwrap()
+ }
+}
+
+impl<A, M> Debug for Operation<'_, A, M, NonSSA<LiftedNonSSA>, FlagGroup>
+where
+ A: Architecture,
+ M: FunctionMutability,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("FlagGroup")
+ .field("flag_group", &self.flag_group())
+ .finish()
+ }
+}
+
+impl<A, M> Debug for Operation<'_, A, M, SSA, FlagGroup>
+where
+ A: Architecture,
+ M: FunctionMutability,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("FlagGroup").finish()
+ }
+}
+
+// LLIL_TRAP
+pub struct Trap;
+
+impl<A, M, F> Operation<'_, A, M, F, Trap>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn vector(&self) -> u64 {
+ self.op.operands[0]
+ }
+}
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, Trap>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Trap")
+ .field("vector", &self.vector())
+ .finish()
+ }
+}
+
+// LLIL_REG_PHI
+pub struct RegPhi;
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, RegPhi>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("RegPhi").finish()
+ }
+}
+
+// LLIL_FLAG_PHI
+pub struct FlagPhi;
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, FlagPhi>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("FlagPhi").finish()
+ }
+}
+
+// LLIL_MEM_PHI
+pub struct MemPhi;
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, MemPhi>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("MemPhi").finish()
+ }
+}
+
+// LLIL_CONST, LLIL_CONST_PTR
+pub struct Const;
+
+impl<A, M, F> Operation<'_, A, M, F, Const>
+where
+ A: 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 {
+ log::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
+ }
+}
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, Const>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Const")
+ .field("size", &self.size())
+ .field("value", &self.value())
+ .finish()
+ }
+}
+
+// LLIL_EXTERN_PTR
+pub struct Extern;
+
+impl<A, M, F> Operation<'_, A, M, F, Extern>
+where
+ A: 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 {
+ log::error!(
+ "il expr @ {:x} contains extern 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
+ }
+}
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, Extern>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Extern")
+ .field("size", &self.size())
+ .field("value", &self.value())
+ .finish()
+ }
+}
+
+// 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: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn size(&self) -> usize {
+ self.op.size
+ }
+
+ pub fn left(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[0] as usize),
+ )
+ }
+
+ pub fn right(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[1] as usize),
+ )
+ }
+}
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, BinaryOp>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("BinaryOp")
+ .field("size", &self.size())
+ .field("left", &self.left())
+ .field("right", &self.right())
+ .finish()
+ }
+}
+
+// LLIL_ADC, LLIL_SBB, LLIL_RLC, LLIL_RRC
+pub struct BinaryOpCarry;
+
+impl<'func, A, M, F> Operation<'func, A, M, F, BinaryOpCarry>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn size(&self) -> usize {
+ self.op.size
+ }
+
+ pub fn left(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[0] as usize),
+ )
+ }
+
+ pub fn right(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[1] as usize),
+ )
+ }
+
+ pub fn carry(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[2] as usize),
+ )
+ }
+}
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, BinaryOpCarry>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("BinaryOpCarry")
+ .field("size", &self.size())
+ .field("left", &self.left())
+ .field("right", &self.right())
+ .field("carry", &self.carry())
+ .finish()
+ }
+}
+
+// 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: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn size(&self) -> usize {
+ self.op.size
+ }
+
+ pub fn high(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[0] as usize),
+ )
+ }
+
+ pub fn low(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[1] as usize),
+ )
+ }
+
+ // TODO: I don't think this actually exists?
+ pub fn right(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[2] as usize),
+ )
+ }
+}
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, DoublePrecDivOp>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("DoublePrecDivOp")
+ .field("size", &self.size())
+ .field("high", &self.high())
+ .field("low", &self.low())
+ // TODO: I don't think this actually is used...
+ .field("right", &self.right())
+ .finish()
+ }
+}
+
+// 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: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn size(&self) -> usize {
+ self.op.size
+ }
+
+ pub fn operand(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[0] as usize),
+ )
+ }
+}
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, UnaryOp>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("UnaryOp")
+ .field("size", &self.size())
+ .field("operand", &self.operand())
+ .finish()
+ }
+}
+
+// LLIL_CMP_X
+pub struct Condition;
+
+impl<'func, A, M, F> Operation<'func, A, M, F, Condition>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn size(&self) -> usize {
+ self.op.size
+ }
+
+ pub fn left(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[0] as usize),
+ )
+ }
+
+ pub fn right(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[1] as usize),
+ )
+ }
+}
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, Condition>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Condition")
+ .field("size", &self.size())
+ .field("left", &self.left())
+ .field("right", &self.right())
+ .finish()
+ }
+}
+
+// LLIL_UNIMPL_MEM
+pub struct UnimplMem;
+
+impl<'func, A, M, F> Operation<'func, A, M, F, UnimplMem>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn size(&self) -> usize {
+ self.op.size
+ }
+
+ pub fn mem_expr(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[0] as usize),
+ )
+ }
+}
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, UnimplMem>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("UnimplMem")
+ .field("size", &self.size())
+ .field("mem_expr", &self.mem_expr())
+ .finish()
+ }
+}
+
+// TODO TEST_BIT
+
+pub trait OperationArguments: 'static {}
+
+impl OperationArguments for NoArgs {}
+impl OperationArguments for Pop {}
+impl OperationArguments for Syscall {}
+impl OperationArguments for Intrinsic {}
+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 RegSplit {}
+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 Extern {}
+impl OperationArguments for BinaryOp {}
+impl OperationArguments for BinaryOpCarry {}
+impl OperationArguments for DoublePrecDivOp {}
+impl OperationArguments for UnaryOp {}
+impl OperationArguments for Condition {}
+impl OperationArguments for UnimplMem {}