summaryrefslogtreecommitdiff
path: root/rust/src/low_level_il/operation.rs
diff options
context:
space:
mode:
Diffstat (limited to 'rust/src/low_level_il/operation.rs')
-rw-r--r--rust/src/low_level_il/operation.rs1367
1 files changed, 1367 insertions, 0 deletions
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 {}