diff options
| author | Rusty Wagner <rusty.wagner@gmail.com> | 2025-12-23 13:12:02 -0700 |
|---|---|---|
| committer | Rusty Wagner <rusty.wagner@gmail.com> | 2026-05-22 16:30:56 -0400 |
| commit | 8d621c51b2797fda7b1dc22243dde611cfc04f68 (patch) | |
| tree | ba5e6a90e644d21d13e75dabcbd0cc747444443a /rust | |
| parent | 08e34ac325743085911f96b62c81d9a1f2127806 (diff) | |
Refactor calling conventions to support correct representation of structures
Diffstat (limited to 'rust')
| -rw-r--r-- | rust/src/binary_view.rs | 35 | ||||
| -rw-r--r-- | rust/src/calling_convention.rs | 120 | ||||
| -rw-r--r-- | rust/src/confidence.rs | 17 | ||||
| -rw-r--r-- | rust/src/disassembly.rs | 5 | ||||
| -rw-r--r-- | rust/src/ffi.rs | 13 | ||||
| -rw-r--r-- | rust/src/function.rs | 121 | ||||
| -rw-r--r-- | rust/src/high_level_il/instruction.rs | 10 | ||||
| -rw-r--r-- | rust/src/high_level_il/lift.rs | 14 | ||||
| -rw-r--r-- | rust/src/medium_level_il/instruction.rs | 55 | ||||
| -rw-r--r-- | rust/src/medium_level_il/lift.rs | 38 | ||||
| -rw-r--r-- | rust/src/medium_level_il/operation.rs | 51 | ||||
| -rw-r--r-- | rust/src/types.rs | 396 | ||||
| -rw-r--r-- | rust/src/variable.rs | 51 |
13 files changed, 801 insertions, 125 deletions
diff --git a/rust/src/binary_view.rs b/rust/src/binary_view.rs index 8d49e55e..c455635a 100644 --- a/rust/src/binary_view.rs +++ b/rust/src/binary_view.rs @@ -51,8 +51,9 @@ use crate::string::*; use crate::symbol::{Symbol, SymbolType}; use crate::tags::{Tag, TagReference, TagType}; use crate::types::{ - NamedTypeReference, QualifiedName, QualifiedNameAndType, QualifiedNameTypeAndId, Type, - TypeArchive, TypeArchiveId, TypeContainer, TypeLibrary, + FunctionParameter, NamedTypeReference, QualifiedName, QualifiedNameAndType, + QualifiedNameTypeAndId, ReturnValue, Type, TypeArchive, TypeArchiveId, TypeContainer, + TypeLibrary, }; use crate::variable::DataVariable; use crate::workflow::Workflow; @@ -2964,6 +2965,36 @@ impl BinaryView { let path_str = unsafe { BnString::into_string(result) }; Some(PathBuf::from(path_str)) } + + pub fn deref_return_value_named_type_references( + &self, + return_value: &ReturnValue, + ) -> ReturnValue { + ReturnValue { + ty: Conf::new( + return_value.ty.contents.deref_named_type_reference(self), + return_value.ty.confidence, + ), + location: return_value.location.clone(), + } + } + + pub fn deref_parameter_named_type_references( + &self, + params: &[FunctionParameter], + ) -> Vec<FunctionParameter> { + params + .iter() + .map(|param| FunctionParameter { + ty: Conf::new( + param.ty.contents.deref_named_type_reference(self), + param.ty.confidence, + ), + name: param.name.clone(), + location: param.location.clone(), + }) + .collect() + } } impl BinaryViewBase for BinaryView { diff --git a/rust/src/calling_convention.rs b/rust/src/calling_convention.rs index a2c4fd94..f91e9803 100644 --- a/rust/src/calling_convention.rs +++ b/rust/src/calling_convention.rs @@ -15,6 +15,7 @@ //! Contains and provides information about different systems' calling conventions to analysis. use std::borrow::Borrow; +use std::collections::BTreeMap; use std::ffi::c_void; use std::fmt::{Debug, Formatter}; use std::hash::{Hash, Hasher}; @@ -25,10 +26,11 @@ use binaryninjacore_sys::*; use crate::architecture::{ Architecture, ArchitectureExt, CoreArchitecture, CoreRegister, Register, RegisterId, }; -use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable}; +use crate::binary_view::BinaryView; +use crate::ffi::slice_from_raw_parts; +use crate::rc::{CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable}; use crate::string::*; -use crate::types::FunctionParameter; -use crate::variable::Variable; +use crate::types::{FunctionParameter, ReturnValue, ValueLocation}; // TODO // force valid registers once Arch has _from_id methods // CallingConvention impl @@ -465,6 +467,23 @@ where getParameterVariableForIncomingVariable: Some(cb_incoming_param_for_var::<C>), areArgumentRegistersUsedForVarArgs: Some(cb_are_argument_registers_used_for_var_args::<C>), + + isReturnTypeRegisterCompatible: None, + getIndirectReturnValueLocation: None, + getReturnedIndirectReturnValuePointer: None, + isArgumentTypeRegisterCompatible: None, + isNonRegisterArgumentIndirect: None, + areStackArgumentsNaturallyAligned: None, + + getCallLayout: None, + freeCallLayout: None, + getReturnValueLocation: None, + freeValueLocation: None, + getParameterLocations: None, + freeParameterLocations: None, + getStackAdjustmentForLocations: None, + getRegisterStackAdjustments: None, + freeRegisterStackAdjustments: None, }; unsafe { @@ -514,46 +533,63 @@ impl CoreCallingConvention { unsafe { BnString::into_string(BNGetCallingConventionName(self.handle)) } } - pub fn variables_for_parameters( + pub fn call_layout( &self, + view: &BinaryView, + return_value: impl Into<ReturnValue>, params: &[FunctionParameter], permitted_registers: Option<&[CoreRegister]>, - ) -> Vec<Variable> { - let mut count: usize = 0; + ) -> CallLayout { + let raw_return_value = ReturnValue::into_rust_raw(return_value.into()); let raw_params: Vec<BNFunctionParameter> = params .iter() .cloned() .map(FunctionParameter::into_raw) .collect(); - let raw_vars_ptr: *mut BNVariable = if let Some(permitted_args) = permitted_registers { + let raw_layout: BNCallLayout = if let Some(permitted_args) = permitted_registers { let permitted_regs = permitted_args.iter().map(|r| r.id().0).collect::<Vec<_>>(); unsafe { - BNGetVariablesForParameters( + BNGetCallLayout( self.handle, + view.handle, + &raw_return_value, raw_params.as_ptr(), raw_params.len(), permitted_regs.as_ptr(), permitted_regs.len(), - &mut count, ) } } else { unsafe { - BNGetVariablesForParametersDefaultPermittedArgs( + BNGetCallLayoutDefaultPermittedArgs( self.handle, + view.handle, + &raw_return_value, raw_params.as_ptr(), raw_params.len(), - &mut count, ) } }; - for raw_param in raw_params { - FunctionParameter::free_raw(raw_param); - } + ReturnValue::free_rust_raw(raw_return_value); + CallLayout::from_owned_core_raw(raw_layout) + } - unsafe { Array::<Variable>::new(raw_vars_ptr, count, ()) }.to_vec() + pub fn return_value_location( + &self, + view: &BinaryView, + return_value: impl Into<ReturnValue>, + ) -> ValueLocation { + let mut raw_return_value = ReturnValue::into_rust_raw(return_value.into()); + let mut raw_location = + unsafe { BNGetReturnValueLocation(self.handle, view.handle, &mut raw_return_value) }; + ReturnValue::free_rust_raw(raw_return_value); + let result = ValueLocation::from_raw(&raw_location); + unsafe { + BNFreeValueLocation(&mut raw_location); + } + result } } @@ -1020,3 +1056,57 @@ impl<A: Architecture> CallingConvention for ConventionBuilder<A> { unsafe impl<A: Architecture> Send for ConventionBuilder<A> {} unsafe impl<A: Architecture> Sync for ConventionBuilder<A> {} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CallLayout { + pub parameters: Vec<ValueLocation>, + pub return_value: Option<ValueLocation>, + pub stack_adjustment: i64, + pub register_stack_adjustments: BTreeMap<RegisterId, i32>, +} + +impl CallLayout { + pub(crate) fn from_raw(value: &BNCallLayout) -> Self { + let raw_params = unsafe { slice_from_raw_parts(value.parameters, value.parameterCount) }; + let parameters = raw_params.iter().map(ValueLocation::from_raw).collect(); + let return_value = if value.returnValueValid { + Some(ValueLocation::from_raw(&value.returnValue)) + } else { + None + }; + let raw_regs = unsafe { + slice_from_raw_parts( + value.registerStackAdjustmentRegisters, + value.registerStackAdjustmentCount, + ) + }; + let raw_amounts = unsafe { + slice_from_raw_parts( + value.registerStackAdjustmentAmounts, + value.registerStackAdjustmentCount, + ) + }; + let mut register_stack_adjustments = BTreeMap::new(); + for i in 0..value.registerStackAdjustmentCount { + register_stack_adjustments.insert(RegisterId(raw_regs[i]), raw_amounts[i]); + } + Self { + parameters, + return_value, + stack_adjustment: value.stackAdjustment, + register_stack_adjustments, + } + } + + /// Take ownership over an "owned" **core allocated** value. Do not call this for a rust allocated value. + pub(crate) fn from_owned_core_raw(mut value: BNCallLayout) -> Self { + let owned = Self::from_raw(&value); + Self::free_core_raw(&mut value); + owned + } + + /// Free a CORE ALLOCATED value. Do not use this with [Self::into_rust_raw] values. + pub(crate) fn free_core_raw(value: &mut BNCallLayout) { + unsafe { BNFreeCallLayout(value) } + } +} diff --git a/rust/src/confidence.rs b/rust/src/confidence.rs index f8745180..31ce7582 100644 --- a/rust/src/confidence.rs +++ b/rust/src/confidence.rs @@ -3,11 +3,11 @@ use crate::architecture::{Architecture, CoreArchitecture}; use crate::calling_convention::CoreCallingConvention; use crate::rc::{Ref, RefCountable}; -use crate::types::Type; +use crate::types::{Type, ValueLocation}; use binaryninjacore_sys::{ BNBoolWithConfidence, BNCallingConventionWithConfidence, BNGetCallingConventionArchitecture, BNInlineDuringAnalysis, BNInlineDuringAnalysisWithConfidence, BNOffsetWithConfidence, - BNTypeWithConfidence, + BNTypeWithConfidence, BNValueLocation, BNValueLocationWithConfidence, }; use std::fmt; use std::fmt::{Debug, Display, Formatter}; @@ -225,6 +225,19 @@ impl Conf<Ref<Type>> { } } +impl Conf<ValueLocation> { + pub(crate) fn into_rust_raw(value: Self) -> BNValueLocationWithConfidence { + BNValueLocationWithConfidence { + location: ValueLocation::into_rust_raw(&value.contents), + confidence: value.confidence, + } + } + + pub(crate) fn free_rust_raw(value: BNValueLocationWithConfidence) { + ValueLocation::free_rust_raw(value.location); + } +} + impl Conf<Ref<CoreCallingConvention>> { pub(crate) fn from_raw(value: &BNCallingConventionWithConfidence) -> Self { let arch = unsafe { diff --git a/rust/src/disassembly.rs b/rust/src/disassembly.rs index cb9bf6a1..718c7737 100644 --- a/rust/src/disassembly.rs +++ b/rust/src/disassembly.rs @@ -506,6 +506,7 @@ pub enum InstructionTextTokenKind { // TODO: Explain what this is hash: Option<u64>, }, + ValueLocation, CodeSymbol { // Target address of the symbol value: u64, @@ -700,6 +701,7 @@ impl InstructionTextTokenKind { hash => Some(hash), }, }, + BNInstructionTextTokenType::ValueLocationToken => Self::ValueLocation, BNInstructionTextTokenType::CodeSymbolToken => Self::CodeSymbol { value: value.value, size: value.size, @@ -914,6 +916,9 @@ impl From<InstructionTextTokenKind> for BNInstructionTextTokenType { BNInstructionTextTokenType::BaseStructureSeparatorToken } InstructionTextTokenKind::Brace { .. } => BNInstructionTextTokenType::BraceToken, + InstructionTextTokenKind::ValueLocation => { + BNInstructionTextTokenType::ValueLocationToken + } InstructionTextTokenKind::CodeSymbol { .. } => { BNInstructionTextTokenType::CodeSymbolToken } diff --git a/rust/src/ffi.rs b/rust/src/ffi.rs index 45d142bf..aaa4851e 100644 --- a/rust/src/ffi.rs +++ b/rust/src/ffi.rs @@ -31,6 +31,19 @@ pub(crate) fn time_from_bn(timestamp: u64) -> SystemTime { UNIX_EPOCH + m } +pub(crate) unsafe fn slice_from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T] { + if len == 0 { + // C can and will pass null pointers for data in the case of zero length arrays. + // According to the documentation of std::slice::from_raw_parts, data must be + // non-null and properly aligned. To avoid creating unsound slices, return an + // empty slice directly on any zero-length array, avoiding the unsound call + // to std::slice::from_raw_parts. + &[] + } else { + unsafe { std::slice::from_raw_parts(data, len) } + } +} + #[macro_export] macro_rules! ffi_span { ($name:expr, $bv:expr) => {{ diff --git a/rust/src/function.rs b/rust/src/function.rs index 320382de..6a62946f 100644 --- a/rust/src/function.rs +++ b/rust/src/function.rs @@ -21,6 +21,7 @@ use crate::{ calling_convention::CoreCallingConvention, component::Component, disassembly::{DisassemblySettings, DisassemblyTextLine}, + ffi::slice_from_raw_parts, flowgraph::FlowGraph, medium_level_il::FunctionGraphType, platform::Platform, @@ -28,7 +29,7 @@ use crate::{ string::*, symbol::{Binding, Symbol}, tags::{Tag, TagReference, TagType}, - types::{IntegerDisplayType, QualifiedName, Type}, + types::{IntegerDisplayType, QualifiedName, ReturnValue, Type, ValueLocation}, }; use crate::{data_buffer::DataBuffer, disassembly::InstructionTextToken, rc::*}; pub use binaryninjacore_sys::BNAnalysisSkipReason as AnalysisSkipReason; @@ -657,6 +658,11 @@ impl Function { Conf::<Ref<Type>>::from_owned_raw(raw_return_type) } + pub fn return_value(&self) -> ReturnValue { + let raw_return_value = unsafe { BNGetFunctionReturnValue(self.handle) }; + ReturnValue::from_owned_core_raw(raw_return_value) + } + pub fn set_auto_return_type<'a, C>(&self, return_type: C) where C: Into<Conf<&'a Type>>, @@ -665,6 +671,22 @@ impl Function { unsafe { BNSetAutoFunctionReturnType(self.handle, &mut raw_return_type) } } + pub fn set_auto_is_return_value_default_location(&self, is_default: bool) { + unsafe { BNSetAutoIsFunctionReturnValueDefaultLocation(self.handle, is_default) } + } + + pub fn set_auto_return_value_location(&self, location: impl Into<Conf<ValueLocation>>) { + let mut raw_location = Conf::<ValueLocation>::into_rust_raw(location.into()); + unsafe { BNSetAutoFunctionReturnValueLocation(self.handle, &mut raw_location) }; + Conf::<ValueLocation>::free_rust_raw(raw_location); + } + + pub fn set_auto_return_value(&self, return_value: impl Into<ReturnValue>) { + let mut raw_return_value = ReturnValue::into_rust_raw(return_value.into()); + unsafe { BNSetAutoFunctionReturnValue(self.handle, &mut raw_return_value) } + ReturnValue::free_rust_raw(raw_return_value); + } + pub fn set_user_return_type<'a, C>(&self, return_type: C) where C: Into<Conf<&'a Type>>, @@ -673,6 +695,22 @@ impl Function { unsafe { BNSetUserFunctionReturnType(self.handle, &mut raw_return_type) } } + pub fn set_user_is_return_value_default_location(&self, is_default: bool) { + unsafe { BNSetUserIsFunctionReturnValueDefaultLocation(self.handle, is_default) } + } + + pub fn set_user_return_value_location(&self, location: impl Into<Conf<ValueLocation>>) { + let mut raw_location = Conf::<ValueLocation>::into_rust_raw(location.into()); + unsafe { BNSetUserFunctionReturnValueLocation(self.handle, &mut raw_location) }; + Conf::<ValueLocation>::free_rust_raw(raw_location); + } + + pub fn set_user_return_value(&self, return_value: impl Into<ReturnValue>) { + let mut raw_return_value = ReturnValue::into_rust_raw(return_value.into()); + unsafe { BNSetUserFunctionReturnValue(self.handle, &mut raw_return_value) } + ReturnValue::free_rust_raw(raw_return_value); + } + pub fn function_type(&self) -> Ref<Type> { unsafe { Type::ref_from_raw(BNGetFunctionType(self.handle)) } } @@ -1017,38 +1055,65 @@ impl Function { } } - pub fn set_user_parameter_variables<I>(&self, values: I, confidence: u8) + pub fn parameter_locations(&self) -> Conf<Vec<ValueLocation>> { + unsafe { + let mut raw_locations = BNGetFunctionParameterLocations(self.handle); + let raw_location_list = + slice_from_raw_parts(raw_locations.locations, raw_locations.count); + let locations: Vec<ValueLocation> = raw_location_list + .iter() + .map(ValueLocation::from_raw) + .collect(); + let confidence = raw_locations.confidence; + BNFreeParameterLocations(&mut raw_locations); + Conf::new(locations, confidence) + } + } + + pub fn set_user_parameter_locations<I>(&self, values: I, confidence: u8) where - I: IntoIterator<Item = Variable>, + I: IntoIterator<Item = ValueLocation>, { - let vars: Vec<BNVariable> = values.into_iter().map(Into::into).collect(); + let locations: Vec<BNValueLocation> = values + .into_iter() + .map(|location| ValueLocation::into_rust_raw(&location.into())) + .collect(); unsafe { - BNSetUserFunctionParameterVariables( + BNSetUserFunctionParameterLocations( self.handle, - &mut BNParameterVariablesWithConfidence { - vars: vars.as_ptr() as *mut _, - count: vars.len(), + &mut BNValueLocationListWithConfidence { + locations: locations.as_ptr() as *mut _, + count: locations.len(), confidence, }, ) } + locations + .into_iter() + .for_each(|location| ValueLocation::free_rust_raw(location.into())); } - pub fn set_auto_parameter_variables<I>(&self, values: I, confidence: u8) + pub fn set_auto_parameter_locations<I>(&self, values: I, confidence: u8) where - I: IntoIterator<Item = Variable>, + I: IntoIterator<Item = ValueLocation>, { - let vars: Vec<BNVariable> = values.into_iter().map(Into::into).collect(); + let locations: Vec<BNValueLocation> = values + .into_iter() + .map(|location| ValueLocation::into_rust_raw(&location.into())) + .collect(); unsafe { - BNSetAutoFunctionParameterVariables( + BNSetAutoFunctionParameterLocations( self.handle, - &mut BNParameterVariablesWithConfidence { - vars: vars.as_ptr() as *mut _, - count: vars.len(), + &mut BNValueLocationListWithConfidence { + locations: locations.as_ptr() as *mut _, + count: locations.len(), confidence, }, ) } + locations + .into_iter() + .for_each(|location| ValueLocation::free_rust_raw(location.into())); } pub fn parameter_at( @@ -2536,32 +2601,6 @@ impl Function { Conf::new(regs, result.confidence) } - pub fn set_user_return_registers<I>(&self, values: I, confidence: u8) - where - I: IntoIterator<Item = CoreRegister>, - { - let mut regs: Box<[u32]> = values.into_iter().map(|reg| reg.id().0).collect(); - let mut regs = BNRegisterSetWithConfidence { - regs: regs.as_mut_ptr(), - count: regs.len(), - confidence, - }; - unsafe { BNSetUserFunctionReturnRegisters(self.handle, &mut regs) } - } - - pub fn set_auto_return_registers<I>(&self, values: I, confidence: u8) - where - I: IntoIterator<Item = CoreRegister>, - { - let mut regs: Box<[u32]> = values.into_iter().map(|reg| reg.id().0).collect(); - let mut regs = BNRegisterSetWithConfidence { - regs: regs.as_mut_ptr(), - count: regs.len(), - confidence, - }; - unsafe { BNSetAutoFunctionReturnRegisters(self.handle, &mut regs) } - } - /// Flow graph of unresolved stack adjustments pub fn unresolved_stack_adjustment_graph(&self) -> Option<Ref<FlowGraph>> { let graph = unsafe { BNGetUnresolvedStackAdjustmentGraph(self.handle) }; diff --git a/rust/src/high_level_il/instruction.rs b/rust/src/high_level_il/instruction.rs index 80be2a16..98839cae 100644 --- a/rust/src/high_level_il/instruction.rs +++ b/rust/src/high_level_il/instruction.rs @@ -400,6 +400,12 @@ impl HighLevelILInstruction { HLIL_ADDRESS_OF => Op::AddressOf(UnaryOp { src: HighLevelExpressionIndex::from(op.operands[0]), }), + HLIL_PASS_BY_REF => Op::PassByRef(UnaryOp { + src: HighLevelExpressionIndex::from(op.operands[0]), + }), + HLIL_RETURN_BY_REF => Op::ReturnByRef(UnaryOp { + src: HighLevelExpressionIndex::from(op.operands[0]), + }), HLIL_NEG => Op::Neg(UnaryOp { src: HighLevelExpressionIndex::from(op.operands[0]), }), @@ -784,6 +790,8 @@ impl HighLevelILInstruction { Deref(op) => Lifted::Deref(self.lift_unary_op(op)), AddressOf(op) => Lifted::AddressOf(self.lift_unary_op(op)), + PassByRef(op) => Lifted::PassByRef(self.lift_unary_op(op)), + ReturnByRef(op) => Lifted::ReturnByRef(self.lift_unary_op(op)), Neg(op) => Lifted::Neg(self.lift_unary_op(op)), Not(op) => Lifted::Not(self.lift_unary_op(op)), Sx(op) => Lifted::Sx(self.lift_unary_op(op)), @@ -1161,6 +1169,8 @@ pub enum HighLevelILInstructionKind { ConstData(ConstData), Deref(UnaryOp), AddressOf(UnaryOp), + PassByRef(UnaryOp), + ReturnByRef(UnaryOp), Neg(UnaryOp), Not(UnaryOp), Sx(UnaryOp), diff --git a/rust/src/high_level_il/lift.rs b/rust/src/high_level_il/lift.rs index ab04f44b..e2ce3686 100644 --- a/rust/src/high_level_il/lift.rs +++ b/rust/src/high_level_il/lift.rs @@ -112,6 +112,8 @@ pub enum HighLevelILLiftedInstructionKind { ConstData(LiftedConstData), Deref(LiftedUnaryOp), AddressOf(LiftedUnaryOp), + PassByRef(LiftedUnaryOp), + ReturnByRef(LiftedUnaryOp), Neg(LiftedUnaryOp), Not(LiftedUnaryOp), Sx(LiftedUnaryOp), @@ -240,6 +242,8 @@ impl HighLevelILLiftedInstruction { ConstData(_) => "ConstData", Deref(_) => "Deref", AddressOf(_) => "AddressOf", + PassByRef(_) => "PassByRef", + ReturnByRef(_) => "ReturnByRef", Neg(_) => "Neg", Not(_) => "Not", Sx(_) => "Sx", @@ -360,10 +364,12 @@ impl HighLevelILLiftedInstruction { "constant_data", Operand::ConstantData(op.constant_data.clone()), )], - Deref(op) | AddressOf(op) | Neg(op) | Not(op) | Sx(op) | Zx(op) | LowPart(op) - | BoolToInt(op) | UnimplMem(op) | Fsqrt(op) | Fneg(op) | Fabs(op) | FloatToInt(op) - | IntToFloat(op) | FloatConv(op) | RoundToInt(op) | Floor(op) | Ceil(op) - | Ftrunc(op) => vec![("src", Operand::Expr(*op.src.clone()))], + Deref(op) | AddressOf(op) | PassByRef(op) | ReturnByRef(op) | Neg(op) | Not(op) + | Sx(op) | Zx(op) | LowPart(op) | BoolToInt(op) | UnimplMem(op) | Fsqrt(op) + | Fneg(op) | Fabs(op) | FloatToInt(op) | IntToFloat(op) | FloatConv(op) + | RoundToInt(op) | Floor(op) | Ceil(op) | Ftrunc(op) => { + vec![("src", Operand::Expr(*op.src.clone()))] + } DerefFieldSsa(op) => vec![ ("src", Operand::Expr(*op.src.clone())), ("src_memory", Operand::Int(op.src_memory)), diff --git a/rust/src/medium_level_il/instruction.rs b/rust/src/medium_level_il/instruction.rs index a223f21e..41f2bbe4 100644 --- a/rust/src/medium_level_il/instruction.rs +++ b/rust/src/medium_level_il/instruction.rs @@ -654,9 +654,22 @@ impl MediumLevelILInstruction { MLIL_VAR_OUTPUT => Op::VarOutput(VarOutput { dest: get_var(op.operands[0]), }), + MLIL_VAR_OUTPUT_FIELD => Op::VarOutputField(VarOutputField { + dest: get_var(op.operands[0]), + offset: op.operands[1], + }), + MLIL_STORE_OUTPUT => Op::StoreOutput(StoreOutput { + dest: MediumLevelExpressionIndex::from(op.operands[0]), + }), MLIL_ADDRESS_OF => Op::AddressOf(Var { src: get_var(op.operands[0]), }), + MLIL_PASS_BY_REF => Op::PassByRef(UnaryOp { + src: MediumLevelExpressionIndex::from(op.operands[0] as usize), + }), + MLIL_RETURN_BY_REF => Op::ReturnByRef(UnaryOp { + src: MediumLevelExpressionIndex::from(op.operands[0] as usize), + }), MLIL_VAR_FIELD => Op::VarField(Field { src: get_var(op.operands[0]), offset: op.operands[1], @@ -682,9 +695,27 @@ impl MediumLevelILInstruction { MLIL_VAR_OUTPUT_SSA => Op::VarOutputSsa(VarOutputSsa { dest: get_var_ssa(op.operands[0], op.operands[1] as usize), }), + MLIL_VAR_OUTPUT_SSA_FIELD => Op::VarOutputSsaField(VarOutputSsaField { + dest: get_var_ssa(op.operands[0], op.operands[1] as usize), + prev: get_var_ssa(op.operands[0], op.operands[2] as usize), + offset: op.operands[3], + }), + MLIL_VAR_OUTPUT_ALIASED => Op::VarOutputAliased(VarOutputAliased { + dest: get_var_ssa(op.operands[0], op.operands[1] as usize), + prev: get_var_ssa(op.operands[0], op.operands[2] as usize), + }), + MLIL_VAR_OUTPUT_ALIASED_FIELD => Op::VarOutputAliasedField(VarOutputAliasedField { + dest: get_var_ssa(op.operands[0], op.operands[1] as usize), + prev: get_var_ssa(op.operands[0], op.operands[2] as usize), + offset: op.operands[3], + }), MLIL_TRAP => Op::Trap(Trap { vector: op.operands[0], }), + MLIL_BLOCK_TO_EXPAND => Op::BlockToExpand(BlockToExpand { + num_operands: op.operands[0] as usize, + first_operand: op.operands[1] as usize, + }), }; Self { @@ -1121,7 +1152,13 @@ impl MediumLevelILInstruction { }), Var(op) => Lifted::Var(op), VarOutput(op) => Lifted::VarOutput(op), + VarOutputField(op) => Lifted::VarOutputField(op), + StoreOutput(op) => Lifted::StoreOutput(LiftedStoreOutput { + dest: self.lift_operand(op.dest), + }), AddressOf(op) => Lifted::AddressOf(op), + PassByRef(op) => Lifted::PassByRef(self.lift_unary_op(op)), + ReturnByRef(op) => Lifted::ReturnByRef(self.lift_unary_op(op)), VarField(op) => Lifted::VarField(op), AddressOfField(op) => Lifted::AddressOfField(op), VarSsa(op) => Lifted::VarSsa(op), @@ -1129,7 +1166,17 @@ impl MediumLevelILInstruction { VarSsaField(op) => Lifted::VarSsaField(op), VarAliasedField(op) => Lifted::VarAliasedField(op), VarOutputSsa(op) => Lifted::VarOutputSsa(op), + VarOutputSsaField(op) => Lifted::VarOutputSsaField(op), + VarOutputAliased(op) => Lifted::VarOutputAliased(op), + VarOutputAliasedField(op) => Lifted::VarOutputAliasedField(op), Trap(op) => Lifted::Trap(op), + BlockToExpand(_op) => Lifted::BlockToExpand(LiftedBlockToExpand { + exprs: self + .get_expr_list(0) + .iter() + .map(|expr| expr.lift()) + .collect(), + }), }; MediumLevelILLiftedInstruction { @@ -1823,6 +1870,8 @@ pub enum MediumLevelILInstructionKind { SeparateParamList(SeparateParamList), SharedParamSlot(SharedParamSlot), VarOutput(VarOutput), + VarOutputField(VarOutputField), + StoreOutput(StoreOutput), Neg(UnaryOp), Not(UnaryOp), Sx(UnaryOp), @@ -1847,6 +1896,8 @@ pub enum MediumLevelILInstructionKind { Ret(Ret), Var(Var), AddressOf(Var), + PassByRef(UnaryOp), + ReturnByRef(UnaryOp), VarField(Field), AddressOfField(Field), VarSsa(VarSsa), @@ -1854,7 +1905,11 @@ pub enum MediumLevelILInstructionKind { VarSsaField(VarSsaField), VarAliasedField(VarSsaField), VarOutputSsa(VarOutputSsa), + VarOutputSsaField(VarOutputSsaField), + VarOutputAliased(VarOutputAliased), + VarOutputAliasedField(VarOutputAliasedField), Trap(Trap), + BlockToExpand(BlockToExpand), // A placeholder for instructions that the Rust bindings do not yet support. // Distinct from `Unimpl` as that is a valid instruction. NotYetImplemented, diff --git a/rust/src/medium_level_il/lift.rs b/rust/src/medium_level_il/lift.rs index 58ea8a13..aee8139f 100644 --- a/rust/src/medium_level_il/lift.rs +++ b/rust/src/medium_level_il/lift.rs @@ -176,7 +176,11 @@ pub enum MediumLevelILLiftedInstructionKind { Ret(LiftedRet), Var(Var), VarOutput(VarOutput), + VarOutputField(VarOutputField), + StoreOutput(LiftedStoreOutput), AddressOf(Var), + PassByRef(LiftedUnaryOp), + ReturnByRef(LiftedUnaryOp), VarField(Field), AddressOfField(Field), VarSsa(VarSsa), @@ -184,7 +188,11 @@ pub enum MediumLevelILLiftedInstructionKind { VarSsaField(VarSsaField), VarAliasedField(VarSsaField), VarOutputSsa(VarOutputSsa), + VarOutputSsaField(VarOutputSsaField), + VarOutputAliased(VarOutputAliased), + VarOutputAliasedField(VarOutputAliasedField), Trap(Trap), + BlockToExpand(LiftedBlockToExpand), // A placeholder for instructions that the Rust bindings do not yet support. // Distinct from `Unimpl` as that is a valid instruction. NotYetImplemented, @@ -301,6 +309,8 @@ impl MediumLevelILLiftedInstruction { SeparateParamList(_) => "SeparateParamList", SharedParamSlot(_) => "SharedParamSlot", VarOutput(_) => "VarOutput", + VarOutputField(_) => "VarOutputField", + StoreOutput(_) => "StoreOutput", Neg(_) => "Neg", Not(_) => "Not", Sx(_) => "Sx", @@ -325,6 +335,8 @@ impl MediumLevelILLiftedInstruction { Ret(_) => "Ret", Var(_) => "Var", AddressOf(_) => "AddressOf", + PassByRef(_) => "PassByRef", + ReturnByRef(_) => "ReturnByRef", VarField(_) => "VarField", AddressOfField(_) => "AddressOfField", VarSsa(_) => "VarSsa", @@ -332,7 +344,11 @@ impl MediumLevelILLiftedInstruction { VarSsaField(_) => "VarSsaField", VarAliasedField(_) => "VarAliasedField", VarOutputSsa(_) => "VarOutputSsa", + VarOutputSsaField(_) => "VarOutputSsaField", + VarOutputAliased(_) => "VarOutputAliased", + VarOutputAliasedField(_) => "VarOutputAliasedField", Trap(_) => "Trap", + BlockToExpand(_) => "BlockToExpand", } } @@ -549,6 +565,13 @@ impl MediumLevelILLiftedInstruction { SharedParamSlot(op) => vec![("params", Operand::ExprList(op.params.clone()))], Var(op) | AddressOf(op) => vec![("src", Operand::Var(op.src))], VarOutput(op) => vec![("dest", Operand::Var(op.dest))], + VarOutputField(op) => vec![ + ("dest", Operand::Var(op.dest)), + ("offset", Operand::Int(op.offset)), + ], + StoreOutput(op) => vec![("dest", Operand::Expr(*op.dest.clone()))], + PassByRef(op) => vec![("src", Operand::Expr(*op.src.clone()))], + ReturnByRef(op) => vec![("src", Operand::Expr(*op.src.clone()))], VarField(op) | AddressOfField(op) => vec![ ("src", Operand::Var(op.src)), ("offset", Operand::Int(op.offset)), @@ -559,7 +582,22 @@ impl MediumLevelILLiftedInstruction { ("offset", Operand::Int(op.offset)), ], VarOutputSsa(op) => vec![("dest", Operand::VarSsa(op.dest))], + VarOutputSsaField(op) => vec![ + ("dest", Operand::VarSsa(op.dest)), + ("prev", Operand::VarSsa(op.prev)), + ("offset", Operand::Int(op.offset)), + ], + VarOutputAliased(op) => vec![ + ("dest", Operand::VarSsa(op.dest)), + ("prev", Operand::VarSsa(op.prev)), + ], + VarOutputAliasedField(op) => vec![ + ("dest", Operand::VarSsa(op.dest)), + ("prev", Operand::VarSsa(op.prev)), + ("offset", Operand::Int(op.offset)), + ], Trap(op) => vec![("vector", Operand::Int(op.vector))], + BlockToExpand(op) => vec![("exprs", Operand::ExprList(op.exprs.clone()))], } } } diff --git a/rust/src/medium_level_il/operation.rs b/rust/src/medium_level_il/operation.rs index a9a791b0..1886b3d0 100644 --- a/rust/src/medium_level_il/operation.rs +++ b/rust/src/medium_level_il/operation.rs @@ -641,6 +641,23 @@ pub struct VarOutput { pub dest: Variable, } +// VAR_OUTPUT_FIELD +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub struct VarOutputField { + pub dest: Variable, + pub offset: u64, +} + +// STORE_OUTPUT +#[derive(Debug, Copy, Clone)] +pub struct StoreOutput { + pub dest: MediumLevelExpressionIndex, +} +#[derive(Clone, Debug, PartialEq)] +pub struct LiftedStoreOutput { + pub dest: Box<MediumLevelILLiftedInstruction>, +} + // VAR_FIELD, ADDRESS_OF_FIELD #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] pub struct Field { @@ -667,8 +684,42 @@ pub struct VarOutputSsa { pub dest: SSAVariable, } +// VAR_OUTPUT_SSA_FIELD +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub struct VarOutputSsaField { + pub dest: SSAVariable, + pub prev: SSAVariable, + pub offset: u64, +} + +// VAR_OUTPUT_ALIASED +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub struct VarOutputAliased { + pub dest: SSAVariable, + pub prev: SSAVariable, +} + +// VAR_OUTPUT_ALIASED_FIELD +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub struct VarOutputAliasedField { + pub dest: SSAVariable, + pub prev: SSAVariable, + pub offset: u64, +} + // TRAP #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] pub struct Trap { pub vector: u64, } + +// BLOCK_TO_EXPAND +#[derive(Debug, Copy, Clone)] +pub struct BlockToExpand { + pub first_operand: usize, + pub num_operands: usize, +} +#[derive(Clone, Debug, PartialEq)] +pub struct LiftedBlockToExpand { + pub exprs: Vec<MediumLevelILLiftedInstruction>, +} diff --git a/rust/src/types.rs b/rust/src/types.rs index e6c16f6b..a7ec517e 100644 --- a/rust/src/types.rs +++ b/rust/src/types.rs @@ -35,7 +35,7 @@ pub mod structure; use binaryninjacore_sys::*; use crate::{ - architecture::Architecture, + architecture::{Architecture, Register, RegisterId}, binary_view::BinaryView, calling_convention::CoreCallingConvention, rc::*, @@ -449,12 +449,12 @@ impl TypeBuilder { // TODO: Deprecate this for a FunctionBuilder (along with the Type variant?) /// NOTE: This is likely to be deprecated and removed in favor of a function type builder, please /// use [`Type::function`] where possible. - pub fn function<'a, T: Into<Conf<&'a Type>>>( - return_type: T, + pub fn function<'a, T: Into<ReturnValue>>( + return_value: T, parameters: Vec<FunctionParameter>, variable_arguments: bool, ) -> Self { - let mut owned_raw_return_type = Conf::<&Type>::into_raw(return_type.into()); + let mut owned_raw_return_value = ReturnValue::into_rust_raw(return_value.into()); let mut variable_arguments = Conf::new(variable_arguments, MAX_CONFIDENCE).into(); let mut can_return = Conf::new(true, MIN_CONFIDENCE).into(); let mut pure = Conf::new(false, MIN_CONFIDENCE).into(); @@ -473,15 +473,9 @@ impl TypeBuilder { let reg_stack_adjust_regs = std::ptr::null_mut(); let reg_stack_adjust_values = std::ptr::null_mut(); - let mut return_regs: BNRegisterSetWithConfidence = BNRegisterSetWithConfidence { - regs: std::ptr::null_mut(), - count: 0, - confidence: 0, - }; - let result = unsafe { Self::from_raw(BNCreateFunctionTypeBuilder( - &mut owned_raw_return_type, + &mut owned_raw_return_value, &mut raw_calling_convention, raw_parameters.as_mut_ptr(), raw_parameters.len(), @@ -491,7 +485,6 @@ impl TypeBuilder { reg_stack_adjust_regs, reg_stack_adjust_values, 0, - &mut return_regs, BNNameType::NoNameType, &mut pure, )) @@ -509,16 +502,16 @@ impl TypeBuilder { /// use [`Type::function_with_opts`] where possible. pub fn function_with_opts< 'a, - T: Into<Conf<&'a Type>>, + T: Into<ReturnValue>, C: Into<Conf<Ref<CoreCallingConvention>>>, >( - return_type: T, + return_value: T, parameters: &[FunctionParameter], variable_arguments: bool, calling_convention: C, stack_adjust: Conf<i64>, ) -> Self { - let mut owned_raw_return_type = Conf::<&Type>::into_raw(return_type.into()); + let mut owned_raw_return_value = ReturnValue::into_rust_raw(return_value.into()); let mut variable_arguments = Conf::new(variable_arguments, MAX_CONFIDENCE).into(); let mut can_return = Conf::new(true, MIN_CONFIDENCE).into(); let mut pure = Conf::new(false, MIN_CONFIDENCE).into(); @@ -537,15 +530,9 @@ impl TypeBuilder { let reg_stack_adjust_regs = std::ptr::null_mut(); let reg_stack_adjust_values = std::ptr::null_mut(); - let mut return_regs: BNRegisterSetWithConfidence = BNRegisterSetWithConfidence { - regs: std::ptr::null_mut(), - count: 0, - confidence: 0, - }; - let result = unsafe { Self::from_raw(BNCreateFunctionTypeBuilder( - &mut owned_raw_return_type, + &mut owned_raw_return_value, &mut owned_raw_calling_convention, raw_parameters.as_mut_ptr(), raw_parameters.len(), @@ -555,7 +542,6 @@ impl TypeBuilder { reg_stack_adjust_regs, reg_stack_adjust_values, 0, - &mut return_regs, BNNameType::NoNameType, &mut pure, )) @@ -943,12 +929,12 @@ impl Type { } // TODO: FunctionBuilder - pub fn function<'a, T: Into<Conf<&'a Type>>>( - return_type: T, + pub fn function<'a, T: Into<ReturnValue>>( + return_value: T, parameters: Vec<FunctionParameter>, variable_arguments: bool, ) -> Ref<Self> { - let mut owned_raw_return_type = Conf::<&Type>::into_raw(return_type.into()); + let mut owned_raw_return_value = ReturnValue::into_rust_raw(return_value.into()); let mut variable_arguments = Conf::new(variable_arguments, MAX_CONFIDENCE).into(); let mut can_return = Conf::new(true, MIN_CONFIDENCE).into(); let mut pure = Conf::new(false, MIN_CONFIDENCE).into(); @@ -967,15 +953,9 @@ impl Type { let reg_stack_adjust_regs = std::ptr::null_mut(); let reg_stack_adjust_values = std::ptr::null_mut(); - let mut return_regs: BNRegisterSetWithConfidence = BNRegisterSetWithConfidence { - regs: std::ptr::null_mut(), - count: 0, - confidence: 0, - }; - let result = unsafe { Self::ref_from_raw(BNCreateFunctionType( - &mut owned_raw_return_type, + &mut owned_raw_return_value, &mut raw_calling_convention, raw_parameters.as_mut_ptr(), raw_parameters.len(), @@ -985,12 +965,12 @@ impl Type { reg_stack_adjust_regs, reg_stack_adjust_values, 0, - &mut return_regs, BNNameType::NoNameType, &mut pure, )) }; + ReturnValue::free_rust_raw(owned_raw_return_value); for raw_param in raw_parameters { FunctionParameter::free_raw(raw_param); } @@ -1001,16 +981,16 @@ impl Type { // TODO: FunctionBuilder pub fn function_with_opts< 'a, - T: Into<Conf<&'a Type>>, + T: Into<ReturnValue>, C: Into<Conf<Ref<CoreCallingConvention>>>, >( - return_type: T, + return_value: T, parameters: &[FunctionParameter], variable_arguments: bool, calling_convention: C, stack_adjust: Conf<i64>, ) -> Ref<Self> { - let mut owned_raw_return_type = Conf::<&Type>::into_raw(return_type.into()); + let mut owned_raw_return_value = ReturnValue::into_rust_raw(return_value.into()); let mut variable_arguments = Conf::new(variable_arguments, MAX_CONFIDENCE).into(); let mut can_return = Conf::new(true, MIN_CONFIDENCE).into(); let mut pure = Conf::new(false, MIN_CONFIDENCE).into(); @@ -1029,15 +1009,9 @@ impl Type { let reg_stack_adjust_regs = std::ptr::null_mut(); let reg_stack_adjust_values = std::ptr::null_mut(); - let mut return_regs: BNRegisterSetWithConfidence = BNRegisterSetWithConfidence { - regs: std::ptr::null_mut(), - count: 0, - confidence: 0, - }; - let result = unsafe { Self::ref_from_raw(BNCreateFunctionType( - &mut owned_raw_return_type, + &mut owned_raw_return_value, &mut owned_raw_calling_convention, raw_parameters.as_mut_ptr(), raw_parameters.len(), @@ -1047,12 +1021,12 @@ impl Type { reg_stack_adjust_regs, reg_stack_adjust_values, 0, - &mut return_regs, BNNameType::NoNameType, &mut pure, )) }; + ReturnValue::free_rust_raw(owned_raw_return_value); for raw_param in raw_parameters { FunctionParameter::free_raw(raw_param); } @@ -1110,6 +1084,10 @@ impl Type { QualifiedName::free_raw(raw_name); type_id } + + pub fn deref_named_type_reference(&self, view: &BinaryView) -> Ref<Type> { + unsafe { Self::ref_from_raw(BNDerefNamedTypeReference(view.handle, self.handle)) } + } } impl Display for Type { @@ -1207,10 +1185,285 @@ unsafe impl CoreArrayProviderInner for Type { } #[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub struct ValueLocationComponent { + pub variable: Variable, + pub offset: i64, + pub size: Option<u64>, +} + +impl ValueLocationComponent { + pub(crate) fn from_raw(value: &BNValueLocationComponent) -> Self { + let variable = Variable::from(&value.variable); + let size = if value.sizeValid { + Some(value.size) + } else { + None + }; + Self { + variable, + offset: value.offset, + size, + } + } + + pub(crate) fn into_raw(value: &Self) -> BNValueLocationComponent { + BNValueLocationComponent { + variable: value.variable.into(), + offset: value.offset, + sizeValid: value.size.is_some(), + size: value.size.unwrap_or(0), + } + } +} + +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub struct ValueLocation { + pub components: Vec<ValueLocationComponent>, + pub indirect: bool, + pub returned_pointer: Option<Variable>, +} + +impl ValueLocation { + pub fn from_variable(var: Variable) -> Self { + Self { + components: vec![ValueLocationComponent { + variable: var, + offset: 0, + size: None, + }], + indirect: false, + returned_pointer: None, + } + } + + pub fn from_register(reg: impl Register) -> Self { + Self::from_variable(Variable::from_register(reg)) + } + + pub fn from_register_id(reg: RegisterId) -> Self { + Self::from_variable(Variable::from_register_id(reg)) + } + + pub fn from_stack_offset(offset: i64) -> Self { + Self::from_variable(Variable::from_stack_offset(offset)) + } + + pub fn is_valid(&self) -> bool { + !self.components.is_empty() + } + + pub fn variable_for_return_value(&self) -> Option<Variable> { + let value_raw = Self::into_rust_raw(&self); + let mut var_raw = BNVariable::default(); + let valid = unsafe { BNGetValueLocationVariableForReturnValue(&value_raw, &mut var_raw) }; + Self::free_rust_raw(value_raw); + if valid { + Some(var_raw.into()) + } else { + None + } + } + + pub fn variable_for_parameter(&self, idx: usize) -> Option<Variable> { + let value_raw = Self::into_rust_raw(&self); + let mut var_raw = BNVariable::default(); + let valid = + unsafe { BNGetValueLocationVariableForParameter(&value_raw, &mut var_raw, idx) }; + Self::free_rust_raw(value_raw); + if valid { + Some(var_raw.into()) + } else { + None + } + } + + pub(crate) fn from_raw(loc: &BNValueLocation) -> Self { + let components_raw: &[BNValueLocationComponent] = + unsafe { crate::ffi::slice_from_raw_parts(loc.components, loc.count) }; + Self { + components: components_raw + .iter() + .map(|component| ValueLocationComponent::from_raw(component)) + .collect(), + indirect: loc.indirect, + returned_pointer: if loc.returnedPointerValid { + Some(Variable::from(&loc.returnedPointer)) + } else { + None + }, + } + } + + pub fn into_rust_raw(value: &Self) -> BNValueLocation { + let components: Box<[BNValueLocationComponent]> = value + .components + .iter() + .map(|component| ValueLocationComponent::into_raw(component)) + .collect(); + BNValueLocation { + count: components.len(), + components: Box::leak(components).as_mut_ptr(), + indirect: value.indirect, + returnedPointerValid: value.returned_pointer.is_some(), + returnedPointer: if let Some(ptr) = value.returned_pointer { + ptr.into() + } else { + Variable::new(VariableSourceType::RegisterVariableSourceType, 0, 0).into() + }, + } + } + + /// Free a RUST ALLOCATED possible value set. Do not use this with CORE ALLOCATED values. + pub fn free_rust_raw(value: BNValueLocation) { + let raw_components = + unsafe { std::slice::from_raw_parts_mut(value.components, value.count) }; + let _ = unsafe { Box::from_raw(raw_components) }; + } +} + +impl Into<ValueLocation> for Variable { + fn into(self) -> ValueLocation { + ValueLocation { + components: vec![ValueLocationComponent { + variable: self, + offset: 0, + size: None, + }], + indirect: false, + returned_pointer: None, + } + } +} + +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub struct ReturnValue { + pub ty: Conf<Ref<Type>>, + pub location: Option<Conf<ValueLocation>>, +} + +impl ReturnValue { + pub(crate) fn from_raw(value: &BNReturnValue) -> Self { + Self { + ty: Conf::new( + unsafe { Type::from_raw(value.type_).to_owned() }, + value.typeConfidence, + ), + location: match value.defaultLocation { + false => Some(Conf::new( + ValueLocation::from_raw(&value.location), + value.locationConfidence, + )), + true => None, + }, + } + } + + /// Take ownership over an "owned" **core allocated** value. Do not call this for a rust allocated value. + pub(crate) fn from_owned_core_raw(mut value: BNReturnValue) -> Self { + let owned = Self::from_raw(&value); + Self::free_core_raw(&mut value); + owned + } + + pub(crate) fn into_rust_raw(value: Self) -> BNReturnValue { + BNReturnValue { + type_: unsafe { Ref::into_raw(value.ty.contents) }.handle, + typeConfidence: value.ty.confidence, + defaultLocation: value.location.is_none(), + location: ValueLocation::into_rust_raw( + value + .location + .as_ref() + .map(|v| &v.contents) + .unwrap_or(&ValueLocation { + components: Vec::new(), + indirect: false, + returned_pointer: None, + }), + ), + locationConfidence: value.location.as_ref().map(|v| v.confidence).unwrap_or(0), + } + } + + /// Free a CORE ALLOCATED possible value set. Do not use this with [Self::into_rust_raw] values. + pub(crate) fn free_core_raw(value: &mut BNReturnValue) { + unsafe { BNFreeReturnValue(value) } + } + + /// Free a RUST ALLOCATED possible value set. Do not use this with CORE ALLOCATED values. + pub(crate) fn free_rust_raw(value: BNReturnValue) { + let _ = unsafe { Type::ref_from_raw(value.type_) }; + ValueLocation::free_rust_raw(value.location); + } +} + +impl Into<ReturnValue> for Ref<Type> { + fn into(self) -> ReturnValue { + ReturnValue { + ty: self.into(), + location: None, + } + } +} + +impl Into<ReturnValue> for &Ref<Type> { + fn into(self) -> ReturnValue { + ReturnValue { + ty: self.clone().into(), + location: None, + } + } +} + +impl Into<ReturnValue> for &Type { + fn into(self) -> ReturnValue { + ReturnValue { + ty: self.to_owned().into(), + location: None, + } + } +} + +impl Into<ReturnValue> for Conf<Ref<Type>> { + fn into(self) -> ReturnValue { + ReturnValue { + ty: self, + location: None, + } + } +} + +impl Into<ReturnValue> for &Conf<Ref<Type>> { + fn into(self) -> ReturnValue { + ReturnValue { + ty: self.clone(), + location: None, + } + } +} + +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub enum ValueLocationSource { + Default, + PassByValue, + PassByReference, + Custom(ValueLocation), +} + +impl From<Option<ValueLocation>> for ValueLocationSource { + fn from(loc: Option<ValueLocation>) -> Self { + match loc { + Some(loc) => ValueLocationSource::Custom(loc), + None => ValueLocationSource::Default, + } + } +} + +#[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct FunctionParameter { pub ty: Conf<Ref<Type>>, pub name: String, - pub location: Option<Variable>, + pub location: ValueLocationSource, } impl FunctionParameter { @@ -1218,13 +1471,7 @@ impl FunctionParameter { // TODO: I copied this from the original `from_raw` function. // TODO: So this actually needs to be audited later. let name = if value.name.is_null() { - if value.location.type_ == VariableSourceType::RegisterVariableSourceType { - format!("reg_{}", value.location.storage) - } else if value.location.type_ == VariableSourceType::StackVariableSourceType { - format!("arg_{}", value.location.storage) - } else { - String::new() - } + String::new() } else { raw_to_string(value.name as *const _).unwrap() }; @@ -1235,9 +1482,17 @@ impl FunctionParameter { value.typeConfidence, ), name, - location: match value.defaultLocation { - false => Some(Variable::from(value.location)), - true => None, + location: match value.locationSource { + BNValueLocationSource::DefaultLocationSource => ValueLocationSource::Default, + BNValueLocationSource::PassByValueLocationSource => { + ValueLocationSource::PassByValue + } + BNValueLocationSource::PassByReferenceLocationSource => { + ValueLocationSource::PassByReference + } + BNValueLocationSource::CustomLocationSource => { + ValueLocationSource::Custom(ValueLocation::from_raw(&value.location)) + } }, } } @@ -1255,21 +1510,42 @@ impl FunctionParameter { name: BnString::into_raw(bn_name), type_: unsafe { Ref::into_raw(value.ty.contents) }.handle, typeConfidence: value.ty.confidence, - defaultLocation: value.location.is_none(), - location: value.location.map(Into::into).unwrap_or_default(), + locationSource: match value.location { + ValueLocationSource::Default => BNValueLocationSource::DefaultLocationSource, + ValueLocationSource::PassByValue => { + BNValueLocationSource::PassByValueLocationSource + } + ValueLocationSource::PassByReference => { + BNValueLocationSource::PassByReferenceLocationSource + } + ValueLocationSource::Custom(_) => BNValueLocationSource::CustomLocationSource, + }, + location: match &value.location { + ValueLocationSource::Custom(loc) => ValueLocation::into_rust_raw(loc), + _ => ValueLocation::into_rust_raw(&ValueLocation { + components: Vec::new(), + indirect: false, + returned_pointer: None, + }), + }, } } pub(crate) fn free_raw(value: BNFunctionParameter) { unsafe { BnString::free_raw(value.name) }; let _ = unsafe { Type::ref_from_raw(value.type_) }; + ValueLocation::free_rust_raw(value.location); } - pub fn new<T: Into<Conf<Ref<Type>>>>(ty: T, name: String, location: Option<Variable>) -> Self { + pub fn new<T: Into<Conf<Ref<Type>>>>( + ty: T, + name: String, + location: impl Into<ValueLocationSource>, + ) -> Self { Self { ty: ty.into(), name, - location, + location: location.into(), } } } diff --git a/rust/src/variable.rs b/rust/src/variable.rs index f9384b5d..1eefc9a8 100644 --- a/rust/src/variable.rs +++ b/rust/src/variable.rs @@ -1,6 +1,6 @@ #![allow(unused)] -use crate::architecture::{Architecture, CoreArchitecture, CoreRegister, RegisterId}; +use crate::architecture::{Architecture, CoreArchitecture, CoreRegister, Register, RegisterId}; use crate::confidence::Conf; use crate::function::{Function, Location}; use crate::rc::{CoreArrayProvider, CoreArrayProviderInner, Ref}; @@ -405,6 +405,30 @@ impl Variable { unsafe { BNFromVariableIdentifier(ident) }.into() } + pub fn from_register(reg: impl Register) -> Self { + Self { + ty: VariableSourceType::RegisterVariableSourceType, + index: 0, + storage: reg.id().0 as i64, + } + } + + pub fn from_register_id(reg: RegisterId) -> Self { + Self { + ty: VariableSourceType::RegisterVariableSourceType, + index: 0, + storage: reg.0 as i64, + } + } + + pub fn from_stack_offset(offset: i64) -> Self { + Self { + ty: VariableSourceType::StackVariableSourceType, + index: 0, + storage: offset, + } + } + pub fn to_identifier(&self) -> u64 { let raw = BNVariable::from(*self); unsafe { BNToVariableIdentifier(&raw) } @@ -417,6 +441,8 @@ impl Variable { } VariableSourceType::StackVariableSourceType => None, VariableSourceType::FlagVariableSourceType => None, + VariableSourceType::CompositeReturnValueSourceType => None, + VariableSourceType::CompositeParameterSourceType => None, } } } @@ -674,6 +700,13 @@ pub enum PossibleValueSet { StackFrameOffset { value: i64, }, + ResultPointer { + offset: i64, + }, + ParameterPointer { + index: u64, + offset: i64, + }, ReturnAddressValue, ImportedAddressValue { value: i64, @@ -730,6 +763,13 @@ impl PossibleValueSet { offset: value.offset, }, RegisterValueType::StackFrameOffset => Self::StackFrameOffset { value: value.value }, + RegisterValueType::ResultPointerValue => Self::ResultPointer { + offset: value.value, + }, + RegisterValueType::ParameterPointerValue => Self::ParameterPointer { + index: value.value as u64, + offset: value.offset, + }, RegisterValueType::ReturnAddressValue => Self::ReturnAddressValue, RegisterValueType::ImportedAddressValue => { Self::ImportedAddressValue { value: value.value } @@ -815,6 +855,13 @@ impl PossibleValueSet { PossibleValueSet::StackFrameOffset { value } => { raw.value = value; } + PossibleValueSet::ResultPointer { offset } => { + raw.value = offset; + } + PossibleValueSet::ParameterPointer { index, offset } => { + raw.value = index as i64; + raw.offset = offset; + } PossibleValueSet::ReturnAddressValue => {} PossibleValueSet::ImportedAddressValue { value } => { raw.value = value; @@ -910,6 +957,8 @@ impl PossibleValueSet { RegisterValueType::ExternalPointerValue } PossibleValueSet::StackFrameOffset { .. } => RegisterValueType::StackFrameOffset, + PossibleValueSet::ResultPointer { .. } => RegisterValueType::ResultPointerValue, + PossibleValueSet::ParameterPointer { .. } => RegisterValueType::ParameterPointerValue, PossibleValueSet::ReturnAddressValue => RegisterValueType::ReturnAddressValue, PossibleValueSet::ImportedAddressValue { .. } => { RegisterValueType::ImportedAddressValue |
