diff options
Diffstat (limited to 'rust/src/low_level_il')
| -rw-r--r-- | rust/src/low_level_il/block.rs | 108 | ||||
| -rw-r--r-- | rust/src/low_level_il/expression.rs | 715 | ||||
| -rw-r--r-- | rust/src/low_level_il/function.rs | 275 | ||||
| -rw-r--r-- | rust/src/low_level_il/instruction.rs | 348 | ||||
| -rw-r--r-- | rust/src/low_level_il/lifting.rs | 1671 | ||||
| -rw-r--r-- | rust/src/low_level_il/operation.rs | 1367 |
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 {} |
