summaryrefslogtreecommitdiff
path: root/rust
diff options
context:
space:
mode:
authorMason Reed <mason@vector35.com>2025-05-09 14:00:38 -0400
committerMason Reed <35282038+emesare@users.noreply.github.com>2025-05-12 17:45:24 -0400
commit2a9beeec15d3f32312a0c4f8ad5ddbcfcbc97a89 (patch)
tree5562dd4acafb32f6e9826a4247f910507cec4c8d /rust
parent6617b0afa09f549d0a5c9c53063f3ad8ab82b7e4 (diff)
[Rust] Support SSA form properly in low level IL
Diffstat (limited to 'rust')
-rw-r--r--rust/src/low_level_il.rs54
-rw-r--r--rust/src/low_level_il/expression.rs250
-rw-r--r--rust/src/low_level_il/function.rs26
-rw-r--r--rust/src/low_level_il/instruction.rs108
-rw-r--r--rust/src/low_level_il/operation.rs669
-rw-r--r--rust/tests/low_level_il.rs85
6 files changed, 1037 insertions, 155 deletions
diff --git a/rust/src/low_level_il.rs b/rust/src/low_level_il.rs
index 453e0546..4c52e313 100644
--- a/rust/src/low_level_il.rs
+++ b/rust/src/low_level_il.rs
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+use std::borrow::Cow;
use std::fmt;
// TODO : provide some way to forbid emitting register reads for certain registers
@@ -20,7 +21,7 @@ use std::fmt;
// requirements on load/store memory address sizes?
// can reg/set_reg be used with sizes that differ from what is in BNRegisterInfo?
-use crate::architecture::{Architecture, RegisterId};
+use crate::architecture::{Architecture, Flag, RegisterId};
use crate::architecture::{CoreRegister, Register as ArchReg};
use crate::function::Location;
@@ -125,12 +126,19 @@ impl<R: ArchReg> LowLevelILRegisterKind<R> {
LowLevelILRegisterKind::Temp(temp.into())
}
- fn id(&self) -> RegisterId {
+ pub fn id(&self) -> RegisterId {
match *self {
LowLevelILRegisterKind::Arch(ref r) => r.id(),
LowLevelILRegisterKind::Temp(temp) => temp.id(),
}
}
+
+ pub fn name(&self) -> Cow<str> {
+ match *self {
+ LowLevelILRegisterKind::Arch(ref r) => r.name(),
+ LowLevelILRegisterKind::Temp(temp) => Cow::Owned(format!("temp{}", temp.temp_id)),
+ }
+ }
}
impl<R: ArchReg> fmt::Debug for LowLevelILRegisterKind<R> {
@@ -149,19 +157,51 @@ impl From<LowLevelILTempRegister> for LowLevelILRegisterKind<CoreRegister> {
}
#[derive(Copy, Clone, Debug)]
-pub enum LowLevelILSSARegister<R: ArchReg> {
- Full(LowLevelILRegisterKind<R>, u32), // no such thing as partial access to a temp register, I think
- Partial(R, u32, R), // partial accesses only possible for arch registers, I think
+pub enum LowLevelILSSARegisterKind<R: ArchReg> {
+ Full {
+ kind: LowLevelILRegisterKind<R>,
+ version: u32,
+ },
+ Partial {
+ full_reg: CoreRegister,
+ partial_reg: CoreRegister,
+ version: u32,
+ },
}
-impl<R: ArchReg> LowLevelILSSARegister<R> {
+impl<R: ArchReg> LowLevelILSSARegisterKind<R> {
+ pub fn new_full(kind: LowLevelILRegisterKind<R>, version: u32) -> Self {
+ Self::Full { kind, version }
+ }
+
+ pub fn new_partial(full_reg: CoreRegister, partial_reg: CoreRegister, version: u32) -> Self {
+ Self::Partial {
+ full_reg,
+ partial_reg,
+ version,
+ }
+ }
+
pub fn version(&self) -> u32 {
match *self {
- LowLevelILSSARegister::Full(_, ver) | LowLevelILSSARegister::Partial(_, ver, _) => ver,
+ LowLevelILSSARegisterKind::Full { version, .. }
+ | LowLevelILSSARegisterKind::Partial { version, .. } => version,
}
}
}
+#[derive(Copy, Clone, Debug)]
+pub struct LowLevelILSSAFlag<F: Flag> {
+ pub flag: F,
+ pub version: u32,
+}
+
+impl<F: Flag> LowLevelILSSAFlag<F> {
+ pub fn new(flag: F, version: u32) -> Self {
+ Self { flag, version }
+ }
+}
+
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub enum VisitorAction {
Descend,
diff --git a/rust/src/low_level_il/expression.rs b/rust/src/low_level_il/expression.rs
index 6addb7e1..73bec38d 100644
--- a/rust/src/low_level_il/expression.rs
+++ b/rust/src/low_level_il/expression.rs
@@ -99,8 +99,7 @@ where
{
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let op = unsafe { BNGetLowLevelILByIndex(self.function.handle, self.index.0) };
- // SAFETY: This is safe we are not exposing the expression kind to the caller.
- let kind = unsafe { LowLevelILExpressionKind::from_raw(self.function, op) };
+ let kind = LowLevelILExpressionKind::from_raw(self.function, op, self.index);
kind.fmt(f)
}
}
@@ -117,7 +116,7 @@ where
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) },
+ _ => LowLevelILExpressionKind::from_raw(self.function, op, self.index),
}
}
@@ -149,7 +148,7 @@ where
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) },
+ _ => LowLevelILExpressionKind::from_raw(self.function, op, self.index),
}
}
@@ -181,7 +180,7 @@ where
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) },
+ _ => LowLevelILExpressionKind::from_raw(self.function, op, self.index),
}
}
@@ -216,9 +215,13 @@ where
F: FunctionForm,
{
Load(Operation<'func, M, F, operation::Load>),
+ LoadSsa(Operation<'func, M, F, operation::LoadSsa>),
Pop(Operation<'func, M, F, operation::Pop>),
Reg(Operation<'func, M, F, operation::Reg>),
+ RegSsa(Operation<'func, M, F, operation::RegSsa>),
+ RegPartialSsa(Operation<'func, M, F, operation::RegPartialSsa>),
RegSplit(Operation<'func, M, F, operation::RegSplit>),
+ RegSplitSsa(Operation<'func, M, F, operation::RegSplitSsa>),
Const(Operation<'func, M, F, operation::Const>),
ConstPtr(Operation<'func, M, F, operation::Const>),
Flag(Operation<'func, M, F, operation::Flag>),
@@ -227,6 +230,10 @@ where
RegStackPop(Operation<'func, M, F, operation::RegStackPop>),
+ CallOutputSsa(Operation<'func, M, F, operation::CallOutputSsa>),
+ CallParamSsa(Operation<'func, M, F, operation::CallParamSsa>),
+ CallStackSsa(Operation<'func, M, F, operation::CallStackSsa>),
+
Add(Operation<'func, M, F, operation::BinaryOp>),
Adc(Operation<'func, M, F, operation::BinaryOpCarry>),
Sub(Operation<'func, M, F, operation::BinaryOp>),
@@ -319,116 +326,146 @@ where
M: FunctionMutability,
F: FunctionForm,
{
- // TODO: Document what "unchecked" means and how to consume this safely.
- pub(crate) unsafe fn from_raw(
+ pub(crate) fn from_raw(
function: &'func LowLevelILFunction<M, F>,
op: BNLowLevelILInstruction,
+ index: LowLevelExpressionIndex,
) -> Self {
use binaryninjacore_sys::BNLowLevelILOperation::*;
match op.operation {
- LLIL_LOAD | LLIL_LOAD_SSA => {
- LowLevelILExpressionKind::Load(Operation::new(function, op))
+ LLIL_LOAD => LowLevelILExpressionKind::Load(Operation::new(function, op, index)),
+ LLIL_LOAD_SSA => LowLevelILExpressionKind::LoadSsa(Operation::new(function, op, index)),
+ LLIL_POP => LowLevelILExpressionKind::Pop(Operation::new(function, op, index)),
+ LLIL_REG => LowLevelILExpressionKind::Reg(Operation::new(function, op, index)),
+ LLIL_REG_SSA => LowLevelILExpressionKind::RegSsa(Operation::new(function, op, index)),
+ LLIL_REG_SSA_PARTIAL => {
+ LowLevelILExpressionKind::RegPartialSsa(Operation::new(function, op, index))
}
- 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 => {
+ LowLevelILExpressionKind::RegSplit(Operation::new(function, op, index))
}
- LLIL_REG_SPLIT | LLIL_REG_SPLIT_SSA => {
- LowLevelILExpressionKind::RegSplit(Operation::new(function, op))
+ LLIL_REG_SPLIT_SSA => {
+ LowLevelILExpressionKind::RegSplitSsa(Operation::new(function, op, index))
+ }
+ LLIL_CONST => LowLevelILExpressionKind::Const(Operation::new(function, op, index)),
+ LLIL_CONST_PTR => {
+ LowLevelILExpressionKind::ConstPtr(Operation::new(function, op, index))
}
- 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))
+ LowLevelILExpressionKind::Flag(Operation::new(function, op, index))
}
LLIL_FLAG_BIT | LLIL_FLAG_BIT_SSA => {
- LowLevelILExpressionKind::FlagBit(Operation::new(function, op))
+ LowLevelILExpressionKind::FlagBit(Operation::new(function, op, index))
+ }
+ LLIL_EXTERN_PTR => {
+ LowLevelILExpressionKind::ExternPtr(Operation::new(function, op, index))
}
- LLIL_EXTERN_PTR => LowLevelILExpressionKind::ExternPtr(Operation::new(function, op)),
LLIL_REG_STACK_POP => {
- LowLevelILExpressionKind::RegStackPop(Operation::new(function, op))
+ LowLevelILExpressionKind::RegStackPop(Operation::new(function, op, index))
+ }
+
+ LLIL_CALL_OUTPUT_SSA => {
+ LowLevelILExpressionKind::CallOutputSsa(Operation::new(function, op, index))
+ }
+ LLIL_CALL_PARAM => {
+ LowLevelILExpressionKind::CallParamSsa(Operation::new(function, op, index))
+ }
+ LLIL_CALL_STACK_SSA => {
+ LowLevelILExpressionKind::CallStackSsa(Operation::new(function, op, index))
}
- 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_ADD => LowLevelILExpressionKind::Add(Operation::new(function, op, index)),
+ LLIL_ADC => LowLevelILExpressionKind::Adc(Operation::new(function, op, index)),
+ LLIL_SUB => LowLevelILExpressionKind::Sub(Operation::new(function, op, index)),
+ LLIL_SBB => LowLevelILExpressionKind::Sbb(Operation::new(function, op, index)),
+ LLIL_AND => LowLevelILExpressionKind::And(Operation::new(function, op, index)),
+ LLIL_OR => LowLevelILExpressionKind::Or(Operation::new(function, op, index)),
+ LLIL_XOR => LowLevelILExpressionKind::Xor(Operation::new(function, op, index)),
+ LLIL_LSL => LowLevelILExpressionKind::Lsl(Operation::new(function, op, index)),
+ LLIL_LSR => LowLevelILExpressionKind::Lsr(Operation::new(function, op, index)),
+ LLIL_ASR => LowLevelILExpressionKind::Asr(Operation::new(function, op, index)),
+ LLIL_ROL => LowLevelILExpressionKind::Rol(Operation::new(function, op, index)),
+ LLIL_RLC => LowLevelILExpressionKind::Rlc(Operation::new(function, op, index)),
+ LLIL_ROR => LowLevelILExpressionKind::Ror(Operation::new(function, op, index)),
+ LLIL_RRC => LowLevelILExpressionKind::Rrc(Operation::new(function, op, index)),
+ LLIL_MUL => LowLevelILExpressionKind::Mul(Operation::new(function, op, index)),
- LLIL_MULU_DP => LowLevelILExpressionKind::MuluDp(Operation::new(function, op)),
- LLIL_MULS_DP => LowLevelILExpressionKind::MulsDp(Operation::new(function, op)),
+ LLIL_MULU_DP => LowLevelILExpressionKind::MuluDp(Operation::new(function, op, index)),
+ LLIL_MULS_DP => LowLevelILExpressionKind::MulsDp(Operation::new(function, op, index)),
- LLIL_DIVU => LowLevelILExpressionKind::Divu(Operation::new(function, op)),
- LLIL_DIVS => LowLevelILExpressionKind::Divs(Operation::new(function, op)),
+ LLIL_DIVU => LowLevelILExpressionKind::Divu(Operation::new(function, op, index)),
+ LLIL_DIVS => LowLevelILExpressionKind::Divs(Operation::new(function, op, index)),
- LLIL_DIVU_DP => LowLevelILExpressionKind::DivuDp(Operation::new(function, op)),
- LLIL_DIVS_DP => LowLevelILExpressionKind::DivsDp(Operation::new(function, op)),
+ LLIL_DIVU_DP => LowLevelILExpressionKind::DivuDp(Operation::new(function, op, index)),
+ LLIL_DIVS_DP => LowLevelILExpressionKind::DivsDp(Operation::new(function, op, index)),
- LLIL_MODU => LowLevelILExpressionKind::Modu(Operation::new(function, op)),
- LLIL_MODS => LowLevelILExpressionKind::Mods(Operation::new(function, op)),
+ LLIL_MODU => LowLevelILExpressionKind::Modu(Operation::new(function, op, index)),
+ LLIL_MODS => LowLevelILExpressionKind::Mods(Operation::new(function, op, index)),
- LLIL_MODU_DP => LowLevelILExpressionKind::ModuDp(Operation::new(function, op)),
- LLIL_MODS_DP => LowLevelILExpressionKind::ModsDp(Operation::new(function, op)),
+ LLIL_MODU_DP => LowLevelILExpressionKind::ModuDp(Operation::new(function, op, index)),
+ LLIL_MODS_DP => LowLevelILExpressionKind::ModsDp(Operation::new(function, op, index)),
- LLIL_NEG => LowLevelILExpressionKind::Neg(Operation::new(function, op)),
- LLIL_NOT => LowLevelILExpressionKind::Not(Operation::new(function, op)),
+ LLIL_NEG => LowLevelILExpressionKind::Neg(Operation::new(function, op, index)),
+ LLIL_NOT => LowLevelILExpressionKind::Not(Operation::new(function, op, index)),
- 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_SX => LowLevelILExpressionKind::Sx(Operation::new(function, op, index)),
+ LLIL_ZX => LowLevelILExpressionKind::Zx(Operation::new(function, op, index)),
+ LLIL_LOW_PART => LowLevelILExpressionKind::LowPart(Operation::new(function, op, index)),
- 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_CMP_E => LowLevelILExpressionKind::CmpE(Operation::new(function, op, index)),
+ LLIL_CMP_NE => LowLevelILExpressionKind::CmpNe(Operation::new(function, op, index)),
+ LLIL_CMP_SLT => LowLevelILExpressionKind::CmpSlt(Operation::new(function, op, index)),
+ LLIL_CMP_ULT => LowLevelILExpressionKind::CmpUlt(Operation::new(function, op, index)),
+ LLIL_CMP_SLE => LowLevelILExpressionKind::CmpSle(Operation::new(function, op, index)),
+ LLIL_CMP_ULE => LowLevelILExpressionKind::CmpUle(Operation::new(function, op, index)),
+ LLIL_CMP_SGE => LowLevelILExpressionKind::CmpSge(Operation::new(function, op, index)),
+ LLIL_CMP_UGE => LowLevelILExpressionKind::CmpUge(Operation::new(function, op, index)),
+ LLIL_CMP_SGT => LowLevelILExpressionKind::CmpSgt(Operation::new(function, op, index)),
+ LLIL_CMP_UGT => LowLevelILExpressionKind::CmpUgt(Operation::new(function, op, index)),
- LLIL_BOOL_TO_INT => LowLevelILExpressionKind::BoolToInt(Operation::new(function, op)),
+ LLIL_BOOL_TO_INT => {
+ LowLevelILExpressionKind::BoolToInt(Operation::new(function, op, index))
+ }
- 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_FADD => LowLevelILExpressionKind::Fadd(Operation::new(function, op, index)),
+ LLIL_FSUB => LowLevelILExpressionKind::Fsub(Operation::new(function, op, index)),
+ LLIL_FMUL => LowLevelILExpressionKind::Fmul(Operation::new(function, op, index)),
+ LLIL_FDIV => LowLevelILExpressionKind::Fdiv(Operation::new(function, op, index)),
- 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_FSQRT => LowLevelILExpressionKind::Fsqrt(Operation::new(function, op, index)),
+ LLIL_FNEG => LowLevelILExpressionKind::Fneg(Operation::new(function, op, index)),
+ LLIL_FABS => LowLevelILExpressionKind::Fabs(Operation::new(function, op, index)),
+ LLIL_FLOAT_TO_INT => {
+ LowLevelILExpressionKind::FloatToInt(Operation::new(function, op, index))
+ }
+ LLIL_INT_TO_FLOAT => {
+ LowLevelILExpressionKind::IntToFloat(Operation::new(function, op, index))
+ }
+ LLIL_FLOAT_CONV => {
+ LowLevelILExpressionKind::FloatConv(Operation::new(function, op, index))
+ }
+ LLIL_ROUND_TO_INT => {
+ LowLevelILExpressionKind::RoundToInt(Operation::new(function, op, index))
+ }
+ LLIL_FLOOR => LowLevelILExpressionKind::Floor(Operation::new(function, op, index)),
+ LLIL_CEIL => LowLevelILExpressionKind::Ceil(Operation::new(function, op, index)),
+ LLIL_FTRUNC => LowLevelILExpressionKind::Ftrunc(Operation::new(function, op, index)),
- 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_FCMP_E => LowLevelILExpressionKind::FcmpE(Operation::new(function, op, index)),
+ LLIL_FCMP_NE => LowLevelILExpressionKind::FcmpNE(Operation::new(function, op, index)),
+ LLIL_FCMP_LT => LowLevelILExpressionKind::FcmpLT(Operation::new(function, op, index)),
+ LLIL_FCMP_LE => LowLevelILExpressionKind::FcmpLE(Operation::new(function, op, index)),
+ LLIL_FCMP_GT => LowLevelILExpressionKind::FcmpGT(Operation::new(function, op, index)),
+ LLIL_FCMP_GE => LowLevelILExpressionKind::FcmpGE(Operation::new(function, op, index)),
+ LLIL_FCMP_O => LowLevelILExpressionKind::FcmpO(Operation::new(function, op, index)),
+ LLIL_FCMP_UO => LowLevelILExpressionKind::FcmpUO(Operation::new(function, op, index)),
- LLIL_UNIMPL => LowLevelILExpressionKind::Unimpl(Operation::new(function, op)),
- LLIL_UNIMPL_MEM => LowLevelILExpressionKind::UnimplMem(Operation::new(function, op)),
+ LLIL_UNIMPL => LowLevelILExpressionKind::Unimpl(Operation::new(function, op, index)),
+ LLIL_UNIMPL_MEM => {
+ LowLevelILExpressionKind::UnimplMem(Operation::new(function, op, index))
+ }
// TODO TEST_BIT ADD_OVERFLOW LLIL_REG_STACK_PUSH LLIL_REG_STACK_POP
_ => {
@@ -439,7 +476,7 @@ where
op.address
);
- LowLevelILExpressionKind::Undef(Operation::new(function, op))
+ LowLevelILExpressionKind::Undef(Operation::new(function, op, index))
}
}
}
@@ -585,12 +622,21 @@ where
visit!(op.mem_expr());
}
Load(ref op) => {
- visit!(op.source_mem_expr());
+ visit!(op.source_expr());
}
- // Do not have any sub expressions.
- Pop(_) | Reg(_) | RegSplit(_) | Const(_) | ConstPtr(_) | Flag(_) | FlagBit(_)
- | ExternPtr(_) | FlagCond(_) | FlagGroup(_) | Unimpl(_) | Undef(_) | RegStackPop(_) => {
+ LoadSsa(ref op) => {
+ visit!(op.source_expr());
+ }
+ CallParamSsa(ref op) => {
+ for param_expr in op.param_exprs() {
+ visit!(param_expr);
+ }
}
+ // Do not have any sub expressions.
+ Pop(_) | Reg(_) | RegSsa(_) | RegPartialSsa(_) | RegSplit(_) | RegSplitSsa(_)
+ | Const(_) | ConstPtr(_) | Flag(_) | FlagBit(_) | ExternPtr(_) | FlagCond(_)
+ | FlagGroup(_) | Unimpl(_) | Undef(_) | RegStackPop(_) | CallOutputSsa(_)
+ | CallStackSsa(_) => {}
}
VisitorAction::Sibling
@@ -614,12 +660,20 @@ where
Load(ref op) => &op.op,
+ LoadSsa(ref op) => &op.op,
+
Pop(ref op) => &op.op,
Reg(ref op) => &op.op,
+ RegSsa(ref op) => &op.op,
+
+ RegPartialSsa(ref op) => &op.op,
+
RegSplit(ref op) => &op.op,
+ RegSplitSsa(ref op) => &op.op,
+
Flag(ref op) => &op.op,
FlagBit(ref op) => &op.op,
@@ -630,6 +684,10 @@ where
RegStackPop(ref op) => &op.op,
+ CallOutputSsa(ref op) => &op.op,
+ CallParamSsa(ref op) => &op.op,
+ CallStackSsa(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)
@@ -670,12 +728,20 @@ impl LowLevelILExpressionKind<'_, Mutable, NonSSA<LiftedNonSSA>> {
Load(ref op) => op.flag_write(),
+ LoadSsa(ref op) => op.flag_write(),
+
Pop(ref op) => op.flag_write(),
Reg(ref op) => op.flag_write(),
+ RegSsa(ref op) => op.flag_write(),
+
+ RegPartialSsa(ref op) => op.flag_write(),
+
RegSplit(ref op) => op.flag_write(),
+ RegSplitSsa(ref op) => op.flag_write(),
+
Flag(ref op) => op.flag_write(),
FlagBit(ref op) => op.flag_write(),
@@ -686,6 +752,10 @@ impl LowLevelILExpressionKind<'_, Mutable, NonSSA<LiftedNonSSA>> {
RegStackPop(ref op) => op.flag_write(),
+ CallOutputSsa(ref op) => op.flag_write(),
+ CallParamSsa(ref op) => op.flag_write(),
+ CallStackSsa(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)
diff --git a/rust/src/low_level_il/function.rs b/rust/src/low_level_il/function.rs
index 21eebc3c..62811b1d 100644
--- a/rust/src/low_level_il/function.rs
+++ b/rust/src/low_level_il/function.rs
@@ -12,15 +12,12 @@
// 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::fmt::Debug;
use std::hash::{Hash, Hasher};
use std::marker::PhantomData;
+use binaryninjacore_sys::*;
+
use crate::architecture::CoreArchitecture;
use crate::basic_block::BasicBlock;
use crate::function::Function;
@@ -160,13 +157,8 @@ where
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<F: FunctionForm> LowLevelILFunction<Finalized, F> {
- pub fn basic_blocks(&self) -> Array<BasicBlock<LowLevelILBlock<Finalized, F>>> {
+ pub fn basic_blocks(&self) -> Array<BasicBlock<LowLevelILBlock<M, F>>> {
use binaryninjacore_sys::BNGetLowLevelILBasicBlockList;
unsafe {
@@ -178,6 +170,17 @@ impl<F: FunctionForm> LowLevelILFunction<Finalized, F> {
}
}
+impl<M: FunctionMutability, V: NonSSAVariant> LowLevelILFunction<M, NonSSA<V>> {
+ /// Retrieve the SSA form of the function.
+ pub fn ssa_form(&self) -> Option<Ref<LowLevelILFunction<M, SSA>>> {
+ let handle = unsafe { BNGetLowLevelILSSAForm(self.handle) };
+ if handle.is_null() {
+ return None;
+ }
+ Some(unsafe { LowLevelILFunction::ref_from_raw(handle) })
+ }
+}
+
// Allow instantiating Lifted IL functions for querying Lifted IL from Architectures
impl LowLevelILFunction<Mutable, NonSSA<LiftedNonSSA>> {
// TODO: Document what happens when you pass None for `source_func`.
@@ -201,7 +204,6 @@ impl LowLevelILFunction<Mutable, NonSSA<LiftedNonSSA>> {
pub fn generate_ssa_form(&self) {
use binaryninjacore_sys::BNGenerateLowLevelILSSAForm;
-
unsafe { BNGenerateLowLevelILSSAForm(self.handle) };
}
}
diff --git a/rust/src/low_level_il/instruction.rs b/rust/src/low_level_il/instruction.rs
index ded9c6f3..90d2f1d8 100644
--- a/rust/src/low_level_il/instruction.rs
+++ b/rust/src/low_level_il/instruction.rs
@@ -207,9 +207,14 @@ where
{
Nop(Operation<'func, M, F, operation::NoArgs>),
SetReg(Operation<'func, M, F, operation::SetReg>),
+ SetRegSsa(Operation<'func, M, F, operation::SetRegSsa>),
+ SetRegPartialSsa(Operation<'func, M, F, operation::SetRegPartialSsa>),
SetRegSplit(Operation<'func, M, F, operation::SetRegSplit>),
+ SetRegSplitSsa(Operation<'func, M, F, operation::SetRegSplitSsa>),
SetFlag(Operation<'func, M, F, operation::SetFlag>),
+ SetFlagSsa(Operation<'func, M, F, operation::SetFlagSsa>),
Store(Operation<'func, M, F, operation::Store>),
+ StoreSsa(Operation<'func, M, F, operation::StoreSsa>),
// TODO needs a real op
Push(Operation<'func, M, F, operation::UnaryOp>),
@@ -219,7 +224,9 @@ where
JumpTo(Operation<'func, M, F, operation::JumpTo>),
Call(Operation<'func, M, F, operation::Call>),
+ CallSsa(Operation<'func, M, F, operation::CallSsa>),
TailCall(Operation<'func, M, F, operation::Call>),
+ TailCallSsa(Operation<'func, M, F, operation::CallSsa>),
Ret(Operation<'func, M, F, operation::Ret>),
NoRet(Operation<'func, M, F, operation::NoArgs>),
@@ -228,6 +235,7 @@ where
Goto(Operation<'func, M, F, operation::Goto>),
Syscall(Operation<'func, M, F, operation::Syscall>),
+ SyscallSsa(Operation<'func, M, F, operation::SyscallSsa>),
Intrinsic(Operation<'func, M, F, operation::Intrinsic>),
Bp(Operation<'func, M, F, operation::NoArgs>),
Trap(Operation<'func, M, F, operation::Trap>),
@@ -250,50 +258,80 @@ where
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_NOP => LowLevelILInstructionKind::Nop(Operation::new(function, op, expr_index)),
+ LLIL_SET_REG => {
+ LowLevelILInstructionKind::SetReg(Operation::new(function, op, expr_index))
}
- LLIL_SET_REG_SPLIT | LLIL_SET_REG_SPLIT_SSA => {
- LowLevelILInstructionKind::SetRegSplit(Operation::new(function, op))
+ LLIL_SET_REG_SSA => {
+ LowLevelILInstructionKind::SetRegSsa(Operation::new(function, op, expr_index))
}
- LLIL_SET_FLAG | LLIL_SET_FLAG_SSA => {
- LowLevelILInstructionKind::SetFlag(Operation::new(function, op))
+ LLIL_SET_REG_SSA_PARTIAL => LowLevelILInstructionKind::SetRegPartialSsa(
+ Operation::new(function, op, expr_index),
+ ),
+ LLIL_SET_REG_SPLIT => {
+ LowLevelILInstructionKind::SetRegSplit(Operation::new(function, op, expr_index))
}
- LLIL_STORE | LLIL_STORE_SSA => {
- LowLevelILInstructionKind::Store(Operation::new(function, op))
+ LLIL_SET_REG_SPLIT_SSA => {
+ LowLevelILInstructionKind::SetRegSplitSsa(Operation::new(function, op, expr_index))
}
- LLIL_PUSH => LowLevelILInstructionKind::Push(Operation::new(function, op)),
+ LLIL_SET_FLAG => {
+ LowLevelILInstructionKind::SetFlag(Operation::new(function, op, expr_index))
+ }
+ LLIL_SET_FLAG_SSA => {
+ LowLevelILInstructionKind::SetFlagSsa(Operation::new(function, op, expr_index))
+ }
+ LLIL_STORE => {
+ LowLevelILInstructionKind::Store(Operation::new(function, op, expr_index))
+ }
+ LLIL_STORE_SSA => {
+ LowLevelILInstructionKind::StoreSsa(Operation::new(function, op, expr_index))
+ }
+ LLIL_PUSH => LowLevelILInstructionKind::Push(Operation::new(function, op, expr_index)),
LLIL_REG_STACK_PUSH => {
- LowLevelILInstructionKind::RegStackPush(Operation::new(function, op))
+ LowLevelILInstructionKind::RegStackPush(Operation::new(function, op, expr_index))
}
- LLIL_JUMP => LowLevelILInstructionKind::Jump(Operation::new(function, op)),
- LLIL_JUMP_TO => LowLevelILInstructionKind::JumpTo(Operation::new(function, op)),
+ LLIL_JUMP => LowLevelILInstructionKind::Jump(Operation::new(function, op, expr_index)),
+ LLIL_JUMP_TO => {
+ LowLevelILInstructionKind::JumpTo(Operation::new(function, op, expr_index))
+ }
- LLIL_CALL | LLIL_CALL_STACK_ADJUST | LLIL_CALL_SSA => {
- LowLevelILInstructionKind::Call(Operation::new(function, op))
+ LLIL_CALL | LLIL_CALL_STACK_ADJUST => {
+ LowLevelILInstructionKind::Call(Operation::new(function, op, expr_index))
+ }
+ LLIL_CALL_SSA => {
+ LowLevelILInstructionKind::CallSsa(Operation::new(function, op, expr_index))
+ }
+ LLIL_TAILCALL => {
+ LowLevelILInstructionKind::TailCall(Operation::new(function, op, expr_index))
}
- LLIL_TAILCALL | LLIL_TAILCALL_SSA => {
- LowLevelILInstructionKind::TailCall(Operation::new(function, op))
+ LLIL_TAILCALL_SSA => {
+ LowLevelILInstructionKind::TailCallSsa(Operation::new(function, op, expr_index))
}
- LLIL_RET => LowLevelILInstructionKind::Ret(Operation::new(function, op)),
- LLIL_NORET => LowLevelILInstructionKind::NoRet(Operation::new(function, op)),
+ LLIL_RET => LowLevelILInstructionKind::Ret(Operation::new(function, op, expr_index)),
+ LLIL_NORET => {
+ LowLevelILInstructionKind::NoRet(Operation::new(function, op, expr_index))
+ }
- LLIL_IF => LowLevelILInstructionKind::If(Operation::new(function, op)),
- LLIL_GOTO => LowLevelILInstructionKind::Goto(Operation::new(function, op)),
+ LLIL_IF => LowLevelILInstructionKind::If(Operation::new(function, op, expr_index)),
+ LLIL_GOTO => LowLevelILInstructionKind::Goto(Operation::new(function, op, expr_index)),
- LLIL_SYSCALL | LLIL_SYSCALL_SSA => {
- LowLevelILInstructionKind::Syscall(Operation::new(function, op))
+ LLIL_SYSCALL => {
+ LowLevelILInstructionKind::Syscall(Operation::new(function, op, expr_index))
+ }
+ LLIL_SYSCALL_SSA => {
+ LowLevelILInstructionKind::SyscallSsa(Operation::new(function, op, expr_index))
}
LLIL_INTRINSIC | LLIL_INTRINSIC_SSA => {
- LowLevelILInstructionKind::Intrinsic(Operation::new(function, op))
+ LowLevelILInstructionKind::Intrinsic(Operation::new(function, op, expr_index))
+ }
+ LLIL_BP => LowLevelILInstructionKind::Bp(Operation::new(function, op, expr_index)),
+ LLIL_TRAP => LowLevelILInstructionKind::Trap(Operation::new(function, op, expr_index)),
+ LLIL_UNDEF => {
+ LowLevelILInstructionKind::Undef(Operation::new(function, op, expr_index))
}
- 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)),
}
}
@@ -314,17 +352,31 @@ where
match self {
SetReg(ref op) => visit!(&op.source_expr()),
+ SetRegSsa(ref op) => visit!(&op.source_expr()),
+ SetRegPartialSsa(ref op) => visit!(&op.source_expr()),
SetRegSplit(ref op) => visit!(&op.source_expr()),
+ SetRegSplitSsa(ref op) => visit!(&op.source_expr()),
SetFlag(ref op) => visit!(&op.source_expr()),
+ SetFlagSsa(ref op) => visit!(&op.source_expr()),
Store(ref op) => {
- visit!(&op.dest_mem_expr());
+ visit!(&op.dest_expr());
+ visit!(&op.source_expr());
+ }
+ StoreSsa(ref op) => {
+ visit!(&op.dest_expr());
visit!(&op.source_expr());
}
Push(ref op) => visit!(&op.operand()),
RegStackPush(ref op) => visit!(&op.source_expr()),
Jump(ref op) => visit!(&op.target()),
JumpTo(ref op) => visit!(&op.target()),
+ SyscallSsa(ref op) => {
+ visit!(&op.output_expr());
+ visit!(&op.param_expr());
+ visit!(&op.stack_expr());
+ }
Call(ref op) | TailCall(ref op) => visit!(&op.target()),
+ CallSsa(ref op) | TailCallSsa(ref op) => visit!(&op.target()),
Ret(ref op) => visit!(&op.target()),
If(ref op) => visit!(&op.condition()),
Intrinsic(ref _op) => {
diff --git a/rust/src/low_level_il/operation.rs b/rust/src/low_level_il/operation.rs
index bd68bbb1..7f31f62b 100644
--- a/rust/src/low_level_il/operation.rs
+++ b/rust/src/low_level_il/operation.rs
@@ -12,7 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-use binaryninjacore_sys::{BNGetLowLevelILByIndex, BNLowLevelILInstruction};
+use binaryninjacore_sys::{
+ BNGetLowLevelILByIndex, BNLowLevelILFreeOperandList, BNLowLevelILGetOperandList,
+ BNLowLevelILInstruction,
+};
use super::*;
use crate::architecture::{
@@ -32,6 +35,7 @@ where
{
pub(crate) function: &'func LowLevelILFunction<M, F>,
pub(crate) op: BNLowLevelILInstruction,
+ pub(crate) expr_idx: LowLevelExpressionIndex,
_args: PhantomData<O>,
}
@@ -44,10 +48,12 @@ where
pub(crate) fn new(
function: &'func LowLevelILFunction<M, F>,
op: BNLowLevelILInstruction,
+ expr_idx: LowLevelExpressionIndex,
) -> Self {
Self {
function,
op,
+ expr_idx,
_args: PhantomData,
}
}
@@ -55,6 +61,22 @@ where
pub fn address(&self) -> u64 {
self.op.address
}
+
+ pub fn get_operand_list(&self, operand_idx: usize) -> Vec<u64> {
+ let mut count = 0;
+ let raw_list_ptr = unsafe {
+ BNLowLevelILGetOperandList(
+ self.function.handle,
+ self.expr_idx.0,
+ operand_idx,
+ &mut count,
+ )
+ };
+ assert!(!raw_list_ptr.is_null());
+ let list = unsafe { std::slice::from_raw_parts(raw_list_ptr, count).to_vec() };
+ unsafe { BNLowLevelILFreeOperandList(raw_list_ptr) };
+ list
+ }
}
impl<M, O> Operation<'_, M, NonSSA<LiftedNonSSA>, O>
@@ -109,7 +131,7 @@ where
}
}
-// LLIL_SYSCALL, LLIL_SYSCALL_SSA
+// LLIL_SYSCALL
pub struct Syscall;
impl<M, F> Debug for Operation<'_, M, F, Syscall>
@@ -122,6 +144,59 @@ where
}
}
+// LLIL_SYSCALL_SSA
+pub struct SyscallSsa;
+
+impl<'func, M, F> Operation<'func, M, F, SyscallSsa>
+where
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ /// Get the output expression of the call.
+ ///
+ /// NOTE: This is currently always [`CallOutputSsa`].
+ pub fn output_expr(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[0] as usize),
+ )
+ }
+
+ /// Get the parameter expression of the call.
+ ///
+ /// NOTE: This is currently always [`CallParamSsa`].
+ pub fn param_expr(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[2] as usize),
+ )
+ }
+
+ /// Get the stack expression of the call.
+ ///
+ /// NOTE: This is currently always [`CallStackSsa`].
+ pub fn stack_expr(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[1] as usize),
+ )
+ }
+}
+
+impl<M, F> Debug for Operation<'_, M, F, SyscallSsa>
+where
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("SyscallSsa")
+ .field("output_expr", &self.output_expr())
+ .field("param_expr", &self.param_expr())
+ .field("stack_expr", &self.stack_expr())
+ .finish()
+ }
+}
+
// LLIL_INTRINSIC, LLIL_INTRINSIC_SSA
pub struct Intrinsic;
@@ -150,7 +225,7 @@ where
}
}
-// LLIL_SET_REG, LLIL_SET_REG_SSA, LLIL_SET_REG_PARTIAL_SSA
+// LLIL_SET_REG
pub struct SetReg;
impl<'func, M, F> Operation<'func, M, F, SetReg>
@@ -190,7 +265,96 @@ where
}
}
-// LLIL_SET_REG_SPLIT, LLIL_SET_REG_SPLIT_SSA
+// LLIL_SET_REG_SSA
+pub struct SetRegSsa;
+
+impl<'func, M, F> Operation<'func, M, F, SetRegSsa>
+where
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn size(&self) -> usize {
+ self.op.size
+ }
+
+ pub fn dest_reg(&self) -> LowLevelILSSARegisterKind<CoreRegister> {
+ let raw_id = RegisterId(self.op.operands[0] as u32);
+ let reg_kind = LowLevelILRegisterKind::from_raw(&self.function.arch(), raw_id)
+ .expect("Bad register ID");
+ let version = self.op.operands[1] as u32;
+ LowLevelILSSARegisterKind::new_full(reg_kind, version)
+ }
+
+ pub fn source_expr(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[2] as usize),
+ )
+ }
+}
+
+impl<M, F> Debug for Operation<'_, M, F, SetRegSsa>
+where
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("SetRegSsa")
+ .field("address", &self.address())
+ .field("size", &self.size())
+ .field("dest_reg", &self.dest_reg())
+ .field("source_expr", &self.source_expr())
+ .finish()
+ }
+}
+
+// LLIL_SET_REG_PARTIAL_SSA
+pub struct SetRegPartialSsa;
+
+impl<'func, M, F> Operation<'func, M, F, SetRegPartialSsa>
+where
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn size(&self) -> usize {
+ self.op.size
+ }
+
+ pub fn dest_reg(&self) -> LowLevelILSSARegisterKind<CoreRegister> {
+ let full_raw_id = RegisterId(self.op.operands[0] as u32);
+ let version = self.op.operands[1] as u32;
+ let partial_raw_id = RegisterId(self.op.operands[2] as u32);
+ let full_reg =
+ CoreRegister::new(self.function.arch(), full_raw_id).expect("Bad register ID");
+ let partial_reg =
+ CoreRegister::new(self.function.arch(), partial_raw_id).expect("Bad register ID");
+ LowLevelILSSARegisterKind::new_partial(full_reg, partial_reg, version)
+ }
+
+ pub fn source_expr(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[3] as usize),
+ )
+ }
+}
+
+impl<M, F> Debug for Operation<'_, M, F, SetRegPartialSsa>
+where
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("SetRegPartialSsa")
+ .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
pub struct SetRegSplit;
impl<'func, M, F> Operation<'func, M, F, SetRegSplit>
@@ -236,7 +400,63 @@ where
}
}
-// LLIL_SET_FLAG, LLIL_SET_FLAG_SSA
+// LLIL_SET_REG_SPLIT_SSA
+pub struct SetRegSplitSsa;
+
+impl<'func, M, F> Operation<'func, M, F, SetRegSplitSsa>
+where
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn size(&self) -> usize {
+ self.op.size
+ }
+
+ /// Because of the fixed operand list size we use another expression for the dest high register.
+ ///
+ /// NOTE: This should always be an expression of [`RegSsa`].
+ pub fn dest_expr_high(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[0] as usize),
+ )
+ }
+
+ /// Because of the fixed operand list size we use another expression for the dest low register.
+ ///
+ /// NOTE: This should always be an expression of [`RegSsa`].
+ pub fn dest_expr_low(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[1] as usize),
+ )
+ }
+
+ pub fn source_expr(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[2] as usize),
+ )
+ }
+}
+
+impl<M, F> Debug for Operation<'_, M, F, SetRegSplitSsa>
+where
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("SetRegSplitSsa")
+ .field("address", &self.address())
+ .field("size", &self.size())
+ .field("dest_expr_high", &self.dest_expr_high())
+ .field("dest_expr_low", &self.dest_expr_low())
+ .field("source_expr", &self.source_expr())
+ .finish()
+ }
+}
+
+// LLIL_SET_FLAG
pub struct SetFlag;
impl<'func, M, F> Operation<'func, M, F, SetFlag>
@@ -245,12 +465,10 @@ where
F: FunctionForm,
{
pub fn dest_flag(&self) -> CoreFlag {
- // TODO: Error handling?
- // TODO: Test this.
self.function
.arch()
.flag_from_id(FlagId(self.op.operands[0] as u32))
- .unwrap()
+ .expect("Bad flag ID")
}
pub fn source_expr(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> {
@@ -275,7 +493,46 @@ where
}
}
-// LLIL_LOAD, LLIL_LOAD_SSA
+// LLIL_SET_FLAG_SSA
+pub struct SetFlagSsa;
+
+impl<'func, M, F> Operation<'func, M, F, SetFlagSsa>
+where
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn dest_flag(&self) -> LowLevelILSSAFlag<CoreFlag> {
+ let flag = self
+ .function
+ .arch()
+ .flag_from_id(FlagId(self.op.operands[0] as u32))
+ .expect("Bad flag ID");
+ let version = self.op.operands[1] as u32;
+ LowLevelILSSAFlag::new(flag, version)
+ }
+
+ pub fn source_expr(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[2] as usize),
+ )
+ }
+}
+
+impl<M, F> Debug for Operation<'_, M, F, SetFlagSsa>
+where
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("SetFlagSsa")
+ .field("address", &self.address())
+ .field("dest_flag", &self.dest_flag())
+ .field("source_expr", &self.source_expr())
+ .finish()
+ }
+}
+// LLIL_LOAD
pub struct Load;
impl<'func, M, F> Operation<'func, M, F, Load>
@@ -287,7 +544,7 @@ where
self.op.size
}
- pub fn source_mem_expr(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> {
+ pub fn source_expr(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> {
LowLevelILExpression::new(
self.function,
LowLevelExpressionIndex(self.op.operands[0] as usize),
@@ -304,12 +561,50 @@ where
f.debug_struct("Load")
.field("address", &self.address())
.field("size", &self.size())
- .field("source_mem_expr", &self.source_mem_expr())
+ .field("source_expr", &self.source_expr())
+ .finish()
+ }
+}
+
+// LLIL_LOAD_SSA
+pub struct LoadSsa;
+
+impl<'func, M, F> Operation<'func, M, F, LoadSsa>
+where
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn size(&self) -> usize {
+ self.op.size
+ }
+
+ pub fn source_expr(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[0] as usize),
+ )
+ }
+
+ pub fn source_memory_version(&self) -> u64 {
+ self.op.operands[1]
+ }
+}
+
+impl<M, F> Debug for Operation<'_, M, F, LoadSsa>
+where
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("LoadSsa")
+ .field("address", &self.address())
+ .field("size", &self.size())
+ .field("source_expr", &self.source_expr())
.finish()
}
}
-// LLIL_STORE, LLIL_STORE_SSA
+// LLIL_STORE
pub struct Store;
impl<'func, M, F> Operation<'func, M, F, Store>
@@ -321,7 +616,7 @@ where
self.op.size
}
- pub fn dest_mem_expr(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> {
+ pub fn dest_expr(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> {
LowLevelILExpression::new(
self.function,
LowLevelExpressionIndex(self.op.operands[0] as usize),
@@ -345,13 +640,63 @@ where
f.debug_struct("Store")
.field("address", &self.address())
.field("size", &self.size())
- .field("dest_mem_expr", &self.dest_mem_expr())
+ .field("dest_expr", &self.dest_expr())
+ .field("source_expr", &self.source_expr())
+ .finish()
+ }
+}
+
+// LLIL_STORE_SSA
+pub struct StoreSsa;
+
+impl<'func, M, F> Operation<'func, M, F, StoreSsa>
+where
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn size(&self) -> usize {
+ self.op.size
+ }
+
+ pub fn dest_expr(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[0] as usize),
+ )
+ }
+
+ pub fn dest_memory_version(&self) -> u64 {
+ self.op.operands[1]
+ }
+
+ pub fn source_expr(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[3] as usize),
+ )
+ }
+
+ pub fn source_memory_version(&self) -> u64 {
+ self.op.operands[2]
+ }
+}
+
+impl<M, F> Debug for Operation<'_, M, F, StoreSsa>
+where
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("StoreSsa")
+ .field("address", &self.address())
+ .field("size", &self.size())
+ .field("dest_expr", &self.dest_expr())
.field("source_expr", &self.source_expr())
.finish()
}
}
-// LLIL_REG, LLIL_REG_SSLLIL_REG_SSA_PARTIAL
+// LLIL_REG
pub struct Reg;
impl<M, F> Operation<'_, M, F, Reg>
@@ -383,7 +728,80 @@ where
}
}
-// LLIL_REG_SPLIT, LLIL_REG_SPLIT_SSA
+// LLIL_REG_SSA
+pub struct RegSsa;
+
+impl<M, F> Operation<'_, M, F, RegSsa>
+where
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn size(&self) -> usize {
+ self.op.size
+ }
+
+ pub fn source_reg(&self) -> LowLevelILSSARegisterKind<CoreRegister> {
+ let raw_id = RegisterId(self.op.operands[0] as u32);
+ let reg_kind = LowLevelILRegisterKind::from_raw(&self.function.arch(), raw_id)
+ .expect("Bad register ID");
+ let version = self.op.operands[1] as u32;
+ LowLevelILSSARegisterKind::new_full(reg_kind, version)
+ }
+}
+
+impl<M, F> Debug for Operation<'_, M, F, RegSsa>
+where
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("RegSsa")
+ .field("address", &self.address())
+ .field("size", &self.size())
+ .field("source_reg", &self.source_reg())
+ .finish()
+ }
+}
+
+// LLIL_REG_SSA_PARTIAL
+pub struct RegPartialSsa;
+
+impl<M, F> Operation<'_, M, F, RegPartialSsa>
+where
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn size(&self) -> usize {
+ self.op.size
+ }
+
+ pub fn source_reg(&self) -> LowLevelILSSARegisterKind<CoreRegister> {
+ let full_raw_id = RegisterId(self.op.operands[0] as u32);
+ let version = self.op.operands[1] as u32;
+ let partial_raw_id = RegisterId(self.op.operands[2] as u32);
+ let full_reg =
+ CoreRegister::new(self.function.arch(), full_raw_id).expect("Bad register ID");
+ let partial_reg =
+ CoreRegister::new(self.function.arch(), partial_raw_id).expect("Bad register ID");
+ LowLevelILSSARegisterKind::new_partial(full_reg, partial_reg, version)
+ }
+}
+
+impl<M, F> Debug for Operation<'_, M, F, RegPartialSsa>
+where
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("RegPartialSsa")
+ .field("address", &self.address())
+ .field("size", &self.size())
+ .field("source_reg", &self.source_reg())
+ .finish()
+ }
+}
+
+// LLIL_REG_SPLIT
pub struct RegSplit;
impl<M, F> Operation<'_, M, F, RegSplit>
@@ -421,6 +839,50 @@ where
}
}
+// LLIL_REG_SPLIT_SSA
+pub struct RegSplitSsa;
+
+impl<M, F> Operation<'_, M, F, RegSplitSsa>
+where
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn size(&self) -> usize {
+ self.op.size
+ }
+
+ pub fn low_reg(&self) -> LowLevelILSSARegisterKind<CoreRegister> {
+ let raw_id = RegisterId(self.op.operands[0] as u32);
+ let reg_kind = LowLevelILRegisterKind::from_raw(&self.function.arch(), raw_id)
+ .expect("Bad register ID");
+ let version = self.op.operands[1] as u32;
+ LowLevelILSSARegisterKind::new_full(reg_kind, version)
+ }
+
+ pub fn high_reg(&self) -> LowLevelILSSARegisterKind<CoreRegister> {
+ let raw_id = RegisterId(self.op.operands[2] as u32);
+ let reg_kind = LowLevelILRegisterKind::from_raw(&self.function.arch(), raw_id)
+ .expect("Bad register ID");
+ let version = self.op.operands[3] as u32;
+ LowLevelILSSARegisterKind::new_full(reg_kind, version)
+ }
+}
+
+impl<M, F> Debug for Operation<'_, M, F, RegSplitSsa>
+where
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("RegSplitSsa")
+ .field("address", &self.address())
+ .field("size", &self.size())
+ .field("low_reg", &self.low_reg())
+ .field("high_reg", &self.high_reg())
+ .finish()
+ }
+}
+
// LLIL_REG_STACK_PUSH
pub struct RegStackPush;
@@ -630,7 +1092,7 @@ where
}
}
-// LLIL_CALL, LLIL_CALL_SSA
+// LLIL_CALL, LLIL_CALL_STACK_ADJUST
pub struct Call;
impl<'func, M, F> Operation<'func, M, F, Call>
@@ -669,6 +1131,165 @@ where
}
}
+// LLIL_CALL_SSA
+pub struct CallSsa;
+
+impl<'func, M, F> Operation<'func, M, F, CallSsa>
+where
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn target(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[1] as usize),
+ )
+ }
+
+ /// Get the output expression of the call.
+ ///
+ /// NOTE: This is currently always [`CallOutputSsa`].
+ pub fn output_expr(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[0] as usize),
+ )
+ }
+
+ /// Get the parameter expression of the call.
+ ///
+ /// NOTE: This is currently always [`CallParamSsa`].
+ pub fn param_expr(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[3] as usize),
+ )
+ }
+
+ /// Get the stack expression of the call.
+ ///
+ /// NOTE: This is currently always [`CallStackSsa`].
+ pub fn stack_expr(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[2] as usize),
+ )
+ }
+}
+
+impl<M, F> Debug for Operation<'_, M, F, CallSsa>
+where
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("CallSsa")
+ .field("target", &self.target())
+ .field("output_expr", &self.output_expr())
+ .field("param_expr", &self.param_expr())
+ .field("stack_expr", &self.stack_expr())
+ .finish()
+ }
+}
+
+// LLIL_CALL_OUTPUT_SSA
+pub struct CallOutputSsa;
+
+impl<M, F> Operation<'_, M, F, CallOutputSsa>
+where
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn dest_regs(&self) -> Vec<LowLevelILSSARegisterKind<CoreRegister>> {
+ let operand_list = self.get_operand_list(1);
+
+ // The operand list contains a list of ([0: reg, 1: version], ...).
+ let paired_ssa_reg = |paired: &[u64]| {
+ let raw_id = RegisterId(paired[0] as u32);
+ let version = paired[1] as u32;
+ let reg_kind = LowLevelILRegisterKind::from_raw(&self.function.arch(), raw_id)
+ .expect("Bad register ID");
+ LowLevelILSSARegisterKind::new_full(reg_kind, version)
+ };
+
+ operand_list.chunks_exact(2).map(paired_ssa_reg).collect()
+ }
+
+ pub fn dest_memory_version(&self) -> u64 {
+ self.op.operands[0]
+ }
+}
+
+impl<M, F> Debug for Operation<'_, M, F, CallOutputSsa>
+where
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("CallOutputSsa")
+ .field("dest_regs", &self.dest_regs())
+ .finish()
+ }
+}
+
+// LLIL_CALL_PARAM_SSA
+pub struct CallParamSsa;
+
+impl<'func, M, F> Operation<'func, M, F, CallParamSsa>
+where
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn param_exprs(&self) -> Vec<LowLevelILExpression<'func, M, F, ValueExpr>> {
+ self.get_operand_list(0)
+ .into_iter()
+ .map(|val| LowLevelExpressionIndex(val as usize))
+ .map(|expr_idx| LowLevelILExpression::new(self.function, expr_idx))
+ .collect()
+ }
+}
+
+impl<M, F> Debug for Operation<'_, M, F, CallParamSsa>
+where
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("CallParamSsa")
+ .field("param_exprs", &self.param_exprs())
+ .finish()
+ }
+}
+
+// LLIL_CALL_STACK_SSA
+pub struct CallStackSsa;
+
+impl<M, F> Operation<'_, M, F, CallStackSsa>
+where
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn source_reg(&self) -> LowLevelILSSARegisterKind<CoreRegister> {
+ let raw_id = RegisterId(self.op.operands[0] as u32);
+ let reg_kind = LowLevelILRegisterKind::from_raw(&self.function.arch(), raw_id)
+ .expect("Bad register ID");
+ let version = self.op.operands[1] as u32;
+ LowLevelILSSARegisterKind::new_full(reg_kind, version)
+ }
+}
+
+impl<M, F> Debug for Operation<'_, M, F, CallStackSsa>
+where
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("CallStackSsa")
+ .field("source_reg", &self.source_reg())
+ .finish()
+ }
+}
+
// LLIL_RET
pub struct Ret;
@@ -1256,14 +1877,24 @@ pub trait OperationArguments: 'static {}
impl OperationArguments for NoArgs {}
impl OperationArguments for Pop {}
impl OperationArguments for Syscall {}
+impl OperationArguments for SyscallSsa {}
impl OperationArguments for Intrinsic {}
impl OperationArguments for SetReg {}
+impl OperationArguments for SetRegSsa {}
+impl OperationArguments for SetRegPartialSsa {}
impl OperationArguments for SetRegSplit {}
+impl OperationArguments for SetRegSplitSsa {}
impl OperationArguments for SetFlag {}
+impl OperationArguments for SetFlagSsa {}
impl OperationArguments for Load {}
+impl OperationArguments for LoadSsa {}
impl OperationArguments for Store {}
+impl OperationArguments for StoreSsa {}
impl OperationArguments for Reg {}
+impl OperationArguments for RegSsa {}
+impl OperationArguments for RegPartialSsa {}
impl OperationArguments for RegSplit {}
+impl OperationArguments for RegSplitSsa {}
impl OperationArguments for RegStackPush {}
impl OperationArguments for RegStackPop {}
impl OperationArguments for Flag {}
@@ -1271,6 +1902,10 @@ impl OperationArguments for FlagBit {}
impl OperationArguments for Jump {}
impl OperationArguments for JumpTo {}
impl OperationArguments for Call {}
+impl OperationArguments for CallSsa {}
+impl OperationArguments for CallOutputSsa {}
+impl OperationArguments for CallParamSsa {}
+impl OperationArguments for CallStackSsa {}
impl OperationArguments for Ret {}
impl OperationArguments for If {}
impl OperationArguments for Goto {}
diff --git a/rust/tests/low_level_il.rs b/rust/tests/low_level_il.rs
index 88d915dc..db8cc549 100644
--- a/rust/tests/low_level_il.rs
+++ b/rust/tests/low_level_il.rs
@@ -7,7 +7,7 @@ use binaryninja::low_level_il::expression::{
use binaryninja::low_level_il::instruction::{
InstructionHandler, LowLevelILInstructionKind, LowLevelInstructionIndex,
};
-use binaryninja::low_level_il::{LowLevelILRegisterKind, VisitorAction};
+use binaryninja::low_level_il::{LowLevelILRegisterKind, LowLevelILSSARegisterKind, VisitorAction};
use std::path::PathBuf;
#[test]
@@ -225,3 +225,86 @@ fn test_llil_visitor() {
};
}
}
+
+#[test]
+fn test_llil_ssa() {
+ let _session = Session::new().expect("Failed to initialize session");
+ let out_dir = env!("OUT_DIR").parse::<PathBuf>().unwrap();
+ let view = binaryninja::load(out_dir.join("atox.obj")).expect("Failed to create view");
+ let image_base = view.original_image_base();
+ let platform = view.default_platform().unwrap();
+
+ // Sample function: __crt_strtox::c_string_character_source<char>::validate
+ let sample_function = view.function_at(&platform, image_base + 0x2bd80).unwrap();
+ let llil_function = sample_function.low_level_il().unwrap();
+ let llil_ssa_function = llil_function.ssa_form().expect("Valid SSA form");
+
+ let llil_ssa_basic_blocks = llil_ssa_function.basic_blocks();
+ let mut llil_ssa_basic_block_iter = llil_ssa_basic_blocks.iter();
+ let first_basic_block = llil_ssa_basic_block_iter.next().unwrap();
+ let mut llil_instr_iter = first_basic_block.iter();
+
+ // 0 @ 0002bd80 (LLIL_SET_REG_SSA.d edi#1 = (LLIL_REG_SSA.d edi#0))
+ let ssa_instr_0 = llil_instr_iter.next().unwrap();
+ assert_eq!(ssa_instr_0.index, LowLevelInstructionIndex(0));
+ assert_eq!(ssa_instr_0.address(), image_base + 0x0002bd80);
+ println!("{:?}", ssa_instr_0);
+ println!("{:?}", ssa_instr_0.kind());
+ match ssa_instr_0.kind() {
+ LowLevelILInstructionKind::SetRegSsa(op) => {
+ assert_eq!(op.size(), 4);
+ match op.dest_reg() {
+ LowLevelILSSARegisterKind::Full { kind, version } => {
+ assert_eq!(kind.name(), "edi");
+ assert_eq!(version, 1);
+ }
+ _ => panic!("Expected LowLevelILSSARegisterKind::Full"),
+ }
+ assert_eq!(op.source_expr().index, LowLevelExpressionIndex(0));
+ }
+ _ => panic!("Expected SetRegSsa"),
+ }
+
+ // 1 @ 0002bd82 (LLIL_STORE_SSA.d [(LLIL_SUB.d (LLIL_REG_SSA.d esp#0) - (LLIL_CONST.d 4)) {__saved_ebp}].d = (LLIL_REG_SSA.d ebp#0) @ mem#0 -> mem#1)
+ let ssa_instr_1 = llil_instr_iter.next().unwrap();
+ assert_eq!(ssa_instr_1.index, LowLevelInstructionIndex(1));
+ assert_eq!(ssa_instr_1.address(), image_base + 0x0002bd82);
+ println!("{:?}", ssa_instr_1);
+ println!("{:?}", ssa_instr_1.kind());
+ match ssa_instr_1.kind() {
+ LowLevelILInstructionKind::StoreSsa(op) => {
+ assert_eq!(op.size(), 4);
+ let source_expr = op.source_expr();
+ let source_memory_version = op.source_memory_version();
+ assert_eq!(source_memory_version, 0);
+ assert_eq!(source_expr.index, LowLevelExpressionIndex(5));
+ let dest_expr = op.dest_expr();
+ let dest_memory_version = op.dest_memory_version();
+ assert_eq!(dest_memory_version, 1);
+ assert_eq!(dest_expr.index, LowLevelExpressionIndex(4));
+ }
+ _ => panic!("Expected StoreSsa"),
+ }
+
+ // 34 @ 0002bdc7 (LLIL_CALL_SSA eax#8, edx#5, ecx#6, mem#23 = call((LLIL_EXTERN_PTR.d __CrtDbgReportW), stack = esp#18 @ mem#22))
+ let ssa_instr_34 = llil_ssa_function
+ .instruction_from_index(LowLevelInstructionIndex(34))
+ .expect("Valid instruction");
+ assert_eq!(ssa_instr_34.index, LowLevelInstructionIndex(34));
+ assert_eq!(ssa_instr_34.address(), image_base + 0x0002bdc7);
+ println!("{:?}", ssa_instr_34);
+ println!("{:?}", ssa_instr_34.kind());
+ match ssa_instr_34.kind() {
+ LowLevelILInstructionKind::CallSsa(op) => match op.target().kind() {
+ LowLevelILExpressionKind::ExternPtr(extern_ptr) => {
+ assert_eq!(extern_ptr.size(), 4);
+ let extern_sym = view
+ .symbol_by_address(extern_ptr.value())
+ .expect("Valid symbol");
+ assert_eq!(extern_sym.short_name(), "__CrtDbgReportW".into())
+ }
+ _ => panic!("Expected ExternPtr"),
+ },
+ _ => panic!("Expected CallSsa"),
+ }
+}