summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--rust/src/architecture.rs14
-rw-r--r--rust/src/low_level_il/expression.rs5
-rw-r--r--rust/src/low_level_il/operation.rs96
-rw-r--r--rust/tests/low_level_il.rs43
4 files changed, 149 insertions, 9 deletions
diff --git a/rust/src/architecture.rs b/rust/src/architecture.rs
index 0a6e4f82..c7fbe5f1 100644
--- a/rust/src/architecture.rs
+++ b/rust/src/architecture.rs
@@ -1310,7 +1310,7 @@ impl FlagGroup for CoreFlagGroup {
}
}
-#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+#[derive(Copy, Clone, Eq, PartialEq)]
pub struct CoreIntrinsic {
pub arch: CoreArchitecture,
pub id: IntrinsicId,
@@ -1396,6 +1396,18 @@ impl Intrinsic for CoreIntrinsic {
}
}
+impl Debug for CoreIntrinsic {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("CoreIntrinsic")
+ .field("id", &self.id)
+ .field("name", &self.name())
+ .field("class", &self.class())
+ .field("inputs", &self.inputs())
+ .field("outputs", &self.outputs())
+ .finish()
+ }
+}
+
// TODO: WTF?!?!?!?
pub struct CoreArchitectureList(*mut *mut BNArchitecture, usize);
diff --git a/rust/src/low_level_il/expression.rs b/rust/src/low_level_il/expression.rs
index ecd850f9..de69e112 100644
--- a/rust/src/low_level_il/expression.rs
+++ b/rust/src/low_level_il/expression.rs
@@ -98,10 +98,7 @@ where
F: FunctionForm,
R: ExpressionResultType,
{
- pub(crate) fn new(
- function: &'func LowLevelILFunction<M, F>,
- index: LowLevelExpressionIndex,
- ) -> Self {
+ pub fn new(function: &'func LowLevelILFunction<M, F>, index: LowLevelExpressionIndex) -> Self {
// TODO: Validate expression here?
Self {
function,
diff --git a/rust/src/low_level_il/operation.rs b/rust/src/low_level_il/operation.rs
index e74a8240..2ddb84f2 100644
--- a/rust/src/low_level_il/operation.rs
+++ b/rust/src/low_level_il/operation.rs
@@ -88,6 +88,22 @@ where
};
PossibleValueSet::from_owned_core_raw(raw_pvs)
}
+
+ /// Get the raw operand from the operand list.
+ ///
+ /// This has no type information associated with it. It's up to the caller to know what the correct type of the
+ /// underlying u64 should be.
+ ///
+ /// # Panic
+ /// `idx` must be less than 4. This is to protect against an out of bounds access.
+ ///
+ /// # Safety
+ /// Even if `idx` is valid, it may index to an unitialized or unused value. Make sure you index into an operand that
+ /// you know should be initialized properly.
+ pub unsafe fn get_operand(&self, idx: usize) -> u64 {
+ assert!(idx < 4);
+ self.op.operands[idx]
+ }
}
impl<M, O> Operation<'_, M, NonSSA, O>
@@ -214,16 +230,69 @@ where
// LLIL_INTRINSIC, LLIL_INTRINSIC_SSA
pub struct Intrinsic;
+#[derive(Debug, Clone, Copy, PartialEq)]
+pub enum IntrinsicOutput {
+ Reg(CoreRegister),
+ Flag(CoreFlag),
+}
+
+impl From<CoreRegister> for IntrinsicOutput {
+ fn from(value: CoreRegister) -> Self {
+ Self::Reg(value)
+ }
+}
+
+impl From<CoreFlag> for IntrinsicOutput {
+ fn from(value: CoreFlag) -> Self {
+ Self::Flag(value)
+ }
+}
+
impl<M, F> Operation<'_, M, F, Intrinsic>
where
M: FunctionMutability,
F: FunctionForm,
{
- // TODO: Support register and expression lists
pub fn intrinsic(&self) -> Option<CoreIntrinsic> {
let raw_id = self.op.operands[2] as u32;
self.function.arch().intrinsic_from_id(IntrinsicId(raw_id))
}
+
+ /// Get the output list.
+ pub fn outputs(&self) -> Vec<IntrinsicOutput> {
+ // Convert the operand to either a register or flag id.
+ let operand_to_output = |o: u64| {
+ if o & (1 << 32) != 0 {
+ self.function
+ .arch()
+ .flag_from_id(FlagId((o & 0xffffffff) as u32))
+ .expect("Invalid core flag ID")
+ .into()
+ } else {
+ self.function
+ .arch()
+ .register_from_id(RegisterId((o & 0xffffffff) as u32))
+ .expect("Invalid register ID")
+ .into()
+ }
+ };
+
+ self.get_operand_list(0)
+ .into_iter()
+ .map(operand_to_output)
+ .collect::<Vec<_>>()
+ }
+
+ /// Get the input list for the intrinsic.
+ ///
+ /// This will just be a CallParamSsa expression.
+ #[inline]
+ pub fn inputs(&self) -> LowLevelILExpression<'_, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[3] as usize),
+ )
+ }
}
impl<M, F> Debug for Operation<'_, M, F, Intrinsic>
@@ -232,9 +301,15 @@ where
F: FunctionForm,
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ use crate::architecture::Intrinsic;
f.debug_struct("Intrinsic")
.field("address", &self.address())
- .field("size", &self.intrinsic())
+ .field(
+ "intrinsic",
+ &self.intrinsic().expect("Valid intrinsic").name(),
+ )
+ .field("outputs", &self.outputs())
+ .field("inputs", &self.inputs())
.finish()
}
}
@@ -1013,13 +1088,28 @@ where
// LLIL_FLAG, LLIL_FLAG_SSA
pub struct Flag;
+impl<M, F> Operation<'_, M, F, Flag>
+where
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn source_flag(&self) -> CoreFlag {
+ self.function
+ .arch()
+ .flag_from_id(FlagId(self.op.operands[0] as u32))
+ .expect("Bad flag ID")
+ }
+}
+
impl<M, F> Debug for Operation<'_, M, F, Flag>
where
M: FunctionMutability,
F: FunctionForm,
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
- f.debug_struct("Flag").finish()
+ f.debug_struct("Flag")
+ .field("source_flag", &self.source_flag())
+ .finish()
}
}
diff --git a/rust/tests/low_level_il.rs b/rust/tests/low_level_il.rs
index db8cc549..84ae3247 100644
--- a/rust/tests/low_level_il.rs
+++ b/rust/tests/low_level_il.rs
@@ -1,4 +1,4 @@
-use binaryninja::architecture::Register;
+use binaryninja::architecture::{ArchitectureExt, Intrinsic, Register};
use binaryninja::binary_view::BinaryViewExt;
use binaryninja::headless::Session;
use binaryninja::low_level_il::expression::{
@@ -7,6 +7,7 @@ use binaryninja::low_level_il::expression::{
use binaryninja::low_level_il::instruction::{
InstructionHandler, LowLevelILInstructionKind, LowLevelInstructionIndex,
};
+use binaryninja::low_level_il::operation::IntrinsicOutput;
use binaryninja::low_level_il::{LowLevelILRegisterKind, LowLevelILSSARegisterKind, VisitorAction};
use std::path::PathBuf;
@@ -308,3 +309,43 @@ fn test_llil_ssa() {
_ => panic!("Expected CallSsa"),
}
}
+
+#[test]
+fn test_llil_intrinsic() {
+ 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("atof.obj")).expect("Failed to create view");
+ let image_base = view.original_image_base();
+ let platform = view.default_platform().unwrap();
+ let arch = platform.arch();
+
+ // Sample function: __crt_strtox::bit_scan_reverse
+ let sample_function = view
+ .function_at(&platform, image_base + 0x00037310)
+ .unwrap();
+ let llil_function = sample_function.low_level_il().unwrap();
+
+ // 5 @ 0004731d (LLIL_INTRINSIC eax, eflags = __bsr_gprv_memv((LLIL_LOAD.d [(LLIL_ADD.d (LLIL_REG.d ebp) + (LLIL_CONST.d 8)) {arg1}].d)))
+ let instr_5 = llil_function
+ .instruction_from_index(LowLevelInstructionIndex(5))
+ .expect("Valid instruction");
+ assert_eq!(instr_5.address(), image_base + 0x0003731d);
+ println!("{:?}", instr_5);
+ println!("{:#?}", instr_5.kind());
+ match instr_5.kind() {
+ LowLevelILInstructionKind::Intrinsic(op) => {
+ assert_eq!(op.intrinsic().unwrap().name(), "__bsr_gprv_memv");
+ assert_eq!(op.outputs().len(), 2);
+ let reg_out_0 = arch.register_by_name("eax").unwrap();
+ let reg_out_1 = arch.register_by_name("eflags").unwrap();
+ assert_eq!(
+ op.outputs(),
+ vec![
+ IntrinsicOutput::Reg(reg_out_0),
+ IntrinsicOutput::Reg(reg_out_1),
+ ]
+ );
+ }
+ _ => panic!("Expected Intrinsic"),
+ }
+}