summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRusty Wagner <rusty.wagner@gmail.com>2026-05-19 20:01:42 -0400
committerRusty Wagner <rusty.wagner@gmail.com>2026-05-22 17:52:21 -0400
commitea82d201e7efb2065d0cea459bea0fcda1985762 (patch)
treefc7494275ddf85e7e68f3c333a79bd3404590dd6
parentede9c92ba6155be37571743df7172aec76dd3c4b (diff)
Refactor calling convention Rust API to allow default implementations
Also implements the new calling convention APIs in Rust
-rw-r--r--arch/riscv/src/lib.rs34
-rw-r--r--plugins/pdb-ng/src/type_parser.rs2
-rw-r--r--rust/src/architecture.rs20
-rw-r--r--rust/src/calling_convention.rs1758
-rw-r--r--rust/src/ffi.rs2
-rw-r--r--rust/src/function.rs4
-rw-r--r--rust/src/types.rs12
7 files changed, 1547 insertions, 285 deletions
diff --git a/arch/riscv/src/lib.rs b/arch/riscv/src/lib.rs
index 3537a082..8504abe6 100644
--- a/arch/riscv/src/lib.rs
+++ b/arch/riscv/src/lib.rs
@@ -14,7 +14,9 @@ use binaryninja::{
UnusedRegisterStack,
},
binary_view::{BinaryView, BinaryViewType},
- calling_convention::{register_calling_convention, CallingConvention, ConventionBuilder},
+ calling_convention::{
+ register_calling_convention, CallingConvention, ConventionBuilder, CoreCallingConvention,
+ },
disassembly::{InstructionTextToken, InstructionTextTokenKind},
function::Function,
function_recognizer::FunctionRecognizer,
@@ -2755,12 +2757,22 @@ impl<D: RiscVDisassembler> AsRef<CoreRelocationHandler> for RiscVELFRelocationHa
}
struct RiscVCC<D: RiscVDisassembler> {
+ core: CoreCallingConvention,
_dis: PhantomData<D>,
}
impl<D: RiscVDisassembler> RiscVCC<D> {
- fn new() -> Self {
- RiscVCC { _dis: PhantomData }
+ fn new(core: CoreCallingConvention) -> Self {
+ RiscVCC {
+ core,
+ _dis: PhantomData,
+ }
+ }
+}
+
+impl<D: RiscVDisassembler> AsRef<CoreCallingConvention> for RiscVCC<D> {
+ fn as_ref(&self) -> &CoreCallingConvention {
+ &self.core
}
}
@@ -3087,17 +3099,13 @@ pub extern "C" fn CorePluginInit() -> bool {
arch32.register_function_recognizer(RiscVELFPLTRecognizer);
arch64.register_function_recognizer(RiscVELFPLTRecognizer);
- let cc32 = register_calling_convention(
- arch32,
- "default",
- RiscVCC::<RiscVIMACDisassembler<Rv32GRegs>>::new(),
- );
+ let cc32 = register_calling_convention(arch32, "default", |core| {
+ RiscVCC::<RiscVIMACDisassembler<Rv32GRegs>>::new(core)
+ });
arch32.set_default_calling_convention(&cc32);
- let cc64 = register_calling_convention(
- arch64,
- "default",
- RiscVCC::<RiscVIMACDisassembler<Rv64GRegs>>::new(),
- );
+ let cc64 = register_calling_convention(arch64, "default", |core| {
+ RiscVCC::<RiscVIMACDisassembler<Rv64GRegs>>::new(core)
+ });
arch64.set_default_calling_convention(&cc64);
if let Some(bvt) = BinaryViewType::by_name("ELF") {
diff --git a/plugins/pdb-ng/src/type_parser.rs b/plugins/pdb-ng/src/type_parser.rs
index dde34bde..7805cbc4 100644
--- a/plugins/pdb-ng/src/type_parser.rs
+++ b/plugins/pdb-ng/src/type_parser.rs
@@ -2421,7 +2421,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> {
// that the calling convention can determine this by default
let default_return_location = convention
.contents
- .return_value_location(self.bv, return_value.clone());
+ .return_value_location(Some(self.bv), &return_value);
if default_return_location.indirect {
None
} else {
diff --git a/rust/src/architecture.rs b/rust/src/architecture.rs
index e854d53b..76e9ab91 100644
--- a/rust/src/architecture.rs
+++ b/rust/src/architecture.rs
@@ -26,6 +26,7 @@ use crate::{
calling_convention::CoreCallingConvention,
data_buffer::DataBuffer,
disassembly::InstructionTextToken,
+ ffi::INVALID_REGISTER,
function::Function,
platform::Platform,
rc::*,
@@ -665,6 +666,13 @@ impl CoreArchitecture {
pub fn name(&self) -> String {
unsafe { BnString::into_string(BNGetArchitectureName(self.handle)) }
}
+
+ pub fn register_stack_for_register(&self, reg: CoreRegister) -> Option<CoreRegisterStack> {
+ match unsafe { BNGetArchitectureRegisterStackForRegister(self.handle, reg.id().0) } {
+ INVALID_REGISTER => None,
+ reg_stack => CoreRegisterStack::new(*self, RegisterStackId::from(reg_stack)),
+ }
+ }
}
unsafe impl Send for CoreArchitecture {}
@@ -967,14 +975,14 @@ impl Architecture for CoreArchitecture {
fn stack_pointer_reg(&self) -> Option<CoreRegister> {
match unsafe { BNGetArchitectureStackPointerRegister(self.handle) } {
- 0xffff_ffff => None,
+ INVALID_REGISTER => None,
reg => Some(CoreRegister::new(*self, reg.into())?),
}
}
fn link_reg(&self) -> Option<CoreRegister> {
match unsafe { BNGetArchitectureLinkRegister(self.handle) } {
- 0xffff_ffff => None,
+ INVALID_REGISTER => None,
reg => Some(CoreRegister::new(*self, reg.into())?),
}
}
@@ -1257,7 +1265,7 @@ pub trait ArchitectureExt: Architecture {
let name = name.to_cstr();
match unsafe { BNGetArchitectureRegisterByName(self.as_ref().handle, name.as_ptr()) } {
- 0xffff_ffff => None,
+ INVALID_REGISTER => None,
reg => self.register_from_id(reg.into()),
}
}
@@ -2130,7 +2138,7 @@ where
if let Some(reg) = custom_arch.stack_pointer_reg() {
reg.id().0
} else {
- 0xffff_ffff
+ INVALID_REGISTER
}
}
@@ -2143,7 +2151,7 @@ where
if let Some(reg) = custom_arch.link_reg() {
reg.id().0
} else {
- 0xffff_ffff
+ INVALID_REGISTER
}
}
@@ -2198,7 +2206,7 @@ where
result.firstTopRelativeReg = reg.id().0;
result.topRelativeCount = count as u32;
} else {
- result.firstTopRelativeReg = 0xffff_ffff;
+ result.firstTopRelativeReg = INVALID_REGISTER;
result.topRelativeCount = 0;
}
diff --git a/rust/src/calling_convention.rs b/rust/src/calling_convention.rs
index 7da7db02..c0d349a8 100644
--- a/rust/src/calling_convention.rs
+++ b/rust/src/calling_convention.rs
@@ -14,75 +14,360 @@
//! Contains and provides information about different systems' calling conventions to analysis.
+use binaryninjacore_sys::*;
use std::borrow::Borrow;
use std::collections::BTreeMap;
use std::ffi::c_void;
use std::fmt::{Debug, Formatter};
use std::hash::{Hash, Hasher};
use std::marker::PhantomData;
-
-use binaryninjacore_sys::*;
+use std::mem::MaybeUninit;
+use std::ptr;
use crate::architecture::{
- Architecture, ArchitectureExt, CoreArchitecture, CoreRegister, Register, RegisterId,
+ Architecture, ArchitectureExt, CoreArchitecture, FlagId, Register, RegisterId, RegisterStack,
+ RegisterStackInfo,
};
use crate::binary_view::BinaryView;
-use crate::ffi::slice_from_raw_parts;
+use crate::ffi::{slice_from_raw_parts, INVALID_REGISTER};
+use crate::function::Function;
use crate::rc::{CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable};
use crate::string::*;
-use crate::types::{FunctionParameter, ReturnValue, ValueLocation};
+use crate::types::{FunctionParameter, ReturnValue, Type, ValueLocation};
+use crate::variable::{RegisterValue, RegisterValueType, Variable};
+
// TODO
// force valid registers once Arch has _from_id methods
-// CallingConvention impl
-// dataflow callbacks
-pub trait CallingConvention: Sync {
+/// Describes how parameters, return values, and the stack are handled when a function is called.
+///
+/// Implementors only need to provide the methods that describe their convention; every method that
+/// computes a layout (parameter locations, return value location, and stack adjustments) has a
+/// default implementation that delegates to the core's default behavior via the implementor's
+/// associated [`CoreCallingConvention`]. A [`CallingConvention`] implementation must expose its
+/// core handle through [`AsRef<CoreCallingConvention>`]; see [`register_calling_convention`] for
+/// how the handle is provided to implementors.
+pub trait CallingConvention: 'static + Sync + Sized + AsRef<CoreCallingConvention> {
+ /// Gets the list of registers that are not preserved across a call
+ /// (caller-saved / volatile registers).
fn caller_saved_registers(&self) -> Vec<RegisterId>;
+
+ /// Gets the list of registers that a callee must preserve across a call
+ /// (callee-saved / non-volatile registers).
fn callee_saved_registers(&self) -> Vec<RegisterId>;
+
+ /// Gets the registers used to pass integer and pointer arguments, in the order they are used.
fn int_arg_registers(&self) -> Vec<RegisterId>;
+
+ /// Gets the registers used to pass floating point arguments, in the order they are used.
fn float_arg_registers(&self) -> Vec<RegisterId>;
+
+ /// Gets the set of registers that must be arguments for heuristic calling convention
+ /// detection to consider this calling convention as a valid option.
fn required_argument_registers(&self) -> Vec<RegisterId> {
Vec::new()
}
+
+ /// Gets the set of registers that must be clobbered for heuristic calling convention
+ /// detection to consider this calling convention as a valid option.
fn required_clobbered_registers(&self) -> Vec<RegisterId> {
Vec::new()
}
+ /// Whether the integer and floating point argument registers share a single argument index.
+ ///
+ /// When true, the Nth argument consumes the Nth slot of both the integer and float register
+ /// lists regardless of its type. When false, integer and float arguments are assigned from
+ /// their respective register lists independently.
fn arg_registers_shared_index(&self) -> bool;
+
+ /// Whether stack space is reserved by the caller for the register arguments (for example,
+ /// the shadow/home space used by the Windows x64 calling convention).
fn reserved_stack_space_for_arg_registers(&self) -> bool;
+
+ /// Whether the callee adjusts the stack to remove the arguments before returning (as in
+ /// stdcall), rather than leaving the caller to clean up the stack (as in cdecl).
fn stack_adjusted_on_return(&self) -> bool;
+
+ /// Whether this calling convention may be selected by heuristic calling convention detection.
fn is_eligible_for_heuristics(&self) -> bool;
+ /// Gets the register that holds the integer return value.
fn return_int_reg(&self) -> Option<RegisterId>;
+
+ /// Gets the register that holds the high part of an integer return value that is too large
+ /// to fit in a single register.
fn return_hi_int_reg(&self) -> Option<RegisterId>;
+
+ /// Gets the register that holds the floating point return value.
fn return_float_reg(&self) -> Option<RegisterId>;
+ /// Gets the register that holds the global pointer, if the calling convention defines one.
fn global_pointer_reg(&self) -> Option<RegisterId>;
+ /// Gets the registers that are implicitly given a known value on function entry by this
+ /// calling convention.
fn implicitly_defined_registers(&self) -> Vec<RegisterId>;
+
+ /// Whether argument registers are used to pass variadic arguments.
fn are_argument_registers_used_for_var_args(&self) -> bool;
+
+ /// The known value of a register on entry to a function. The default implementation models the
+ /// top of a register stack (such as the x87 floating point stack) as the constant zero and
+ /// leaves all other registers undetermined.
+ fn incoming_register_value(&self, reg: RegisterId, _func: Option<&Function>) -> RegisterValue {
+ let arch = self.as_ref().arch_handle;
+ if let Some(reg) = arch.register_from_id(reg) {
+ if let Some(reg_stack) = arch.register_stack_for_register(reg) {
+ if reg == reg_stack.info().stack_top_reg() {
+ return RegisterValue::new(RegisterValueType::ConstantValue, 0, 0, 0);
+ }
+ }
+ }
+ RegisterValue::new(RegisterValueType::UndeterminedValue, 0, 0, 0)
+ }
+
+ /// The known value of a flag on entry to a function. The default implementation leaves all
+ /// flags undetermined.
+ fn incoming_flag_value(&self, _flag: FlagId, _func: Option<&Function>) -> RegisterValue {
+ RegisterValue::new(RegisterValueType::UndeterminedValue, 0, 0, 0)
+ }
+
+ /// The incoming variable used to pass the given parameter variable.
+ fn incoming_variable_for_parameter_variable(
+ &self,
+ var: &Variable,
+ _func: Option<&Function>,
+ ) -> Variable {
+ self.as_ref()
+ .default_incoming_variable_for_parameter_variable(var)
+ }
+
+ /// The parameter variable corresponding to the given incoming variable.
+ fn parameter_variable_for_incoming_variable(
+ &self,
+ var: &Variable,
+ _func: Option<&Function>,
+ ) -> Variable {
+ self.as_ref()
+ .default_parameter_variable_for_incoming_variable(var)
+ }
+
+ /// Whether a value of the given type can be returned in registers, as opposed to being
+ /// returned indirectly through memory. The default implementation allows register returns
+ /// for types that fit in a single register, have a size equal to two registers when
+ /// [`CallingConvention::return_hi_int_reg`] is a valid register, or are a floating point
+ /// type when [`CallingConvention::return_float_reg`] is a valid register.
+ fn is_return_type_register_compatible(&self, _view: Option<&BinaryView>, ty: &Type) -> bool {
+ self.as_ref().default_is_return_type_register_compatible(ty)
+ }
+
+ /// The location used to pass the hidden pointer argument for return values that are returned
+ /// indirectly through memory. The default location is the first integer argument register,
+ /// or the first stack slot if there are no integer argument registers.
+ fn indirect_return_value_location(&self) -> Variable {
+ self.as_ref().default_indirect_return_value_location()
+ }
+
+ /// The location in which the hidden indirect return value pointer is returned to the caller,
+ /// for calling conventions that return it.
+ fn returned_indirect_return_value_pointer(&self) -> Option<Variable> {
+ None
+ }
+
+ /// Whether a value of the given type can be passed as an argument in registers. The default
+ /// implementation allows register arguments for types that fit in a single register, or are
+ /// a floating point type when [`CallingConvention::float_arg_registers`] has valid registers.
+ fn is_argument_type_register_compatible(&self, _view: Option<&BinaryView>, ty: &Type) -> bool {
+ self.as_ref()
+ .default_is_argument_type_register_compatible(ty)
+ }
+
+ /// Whether an argument that cannot be passed in registers is passed indirectly by pointer, as
+ /// opposed to being passed directly on the stack.
+ fn is_non_register_argument_indirect(&self, _view: Option<&BinaryView>, _ty: &Type) -> bool {
+ false
+ }
+
+ /// Whether arguments passed on the stack are aligned to their natural alignment. If false,
+ /// arguments are aligned to the address size.
+ fn are_stack_arguments_naturally_aligned(&self) -> bool {
+ false
+ }
+
+ /// Whether arguments passed on the stack are pushed left-to-right, as opposed to the more
+ /// common right-to-left order.
+ fn are_stack_arguments_pushed_left_to_right(&self) -> bool {
+ false
+ }
+
+ /// Computes the complete call layout (parameter locations, return value location, and stack
+ /// adjustments) for a call with the given return value and parameters. The default
+ /// implementation uses [`CallingConvention::return_value_location`],
+ /// [`CallingConvention::parameter_locations`], [`CallingConvention::stack_adjustment_for_locations`],
+ /// and [`CallingConvention::stack_adjustment_for_locations`] to compute the layout.
+ ///
+ /// It is recommended to only override this method if the calling convention behavior cannot be
+ /// modeled with [`CallingConvention::return_value_location`] and/or
+ /// [`CallingConvention::parameter_locations`].
+ ///
+ /// When calling this function to query the layout of a function, the return value and
+ /// parameters should have their named type references dereferenced before passing them to
+ /// this function. Calling the functions [`BinaryView::deref_return_value_named_type_references`]
+ /// and [`BinaryView::deref_parameter_named_type_references`] will perform this dereferencing.
+ fn call_layout(
+ &self,
+ view: Option<&BinaryView>,
+ return_value: &ReturnValue,
+ params: &[FunctionParameter],
+ permitted_registers: Option<&[RegisterId]>,
+ ) -> CallLayout {
+ self.as_ref()
+ .default_call_layout(view, return_value, params, permitted_registers)
+ }
+
+ /// Computes the location of the return value for the given return value type. The default
+ /// implementation checks [`CallingConvention::is_return_type_register_compatible`] and places
+ /// the return value in registers if it can, or uses an indirect return by pointer if not. If
+ /// an indirect return is required, then [`CallingConvention::indirect_return_value_location`]
+ /// and [`CallingConvention::returned_indirect_return_value_pointer`] are used to provide the
+ /// location of the indirect return value.
+ fn return_value_location(
+ &self,
+ view: Option<&BinaryView>,
+ return_value: &ReturnValue,
+ ) -> ValueLocation {
+ self.as_ref()
+ .default_return_value_location(view, return_value)
+ }
+
+ /// Computes the locations of the parameters for a call with the given return value and
+ /// parameters. The default implementation uses [`CallingConvention::int_arg_registers`],
+ /// [`CallingConvention::float_arg_registers`], [`CallingConvention::arg_registers_shared_index`],
+ /// [`CallingConvention::reserved_stack_space_for_arg_registers`],
+ /// [`CallingConvention::is_argument_type_register_compatible`],
+ /// [`CallingConvention::is_non_register_argument_indirect`],
+ /// [`CallingConvention::are_stack_arguments_naturally_aligned`], and
+ /// [`CallingConvention::are_stack_arguments_pushed_left_to_right`] to compute the parameter
+ /// layout.
+ ///
+ /// This function is usually sufficient unless the calling convention has unusual parameter
+ /// passing behavior. Most calling conventions can be defined per-argument using the methods
+ /// listed above.
+ fn parameter_locations(
+ &self,
+ view: Option<&BinaryView>,
+ return_value: Option<&ValueLocation>,
+ params: &[FunctionParameter],
+ permitted_registers: Option<&[RegisterId]>,
+ ) -> Vec<ValueLocation> {
+ self.as_ref()
+ .default_parameter_locations(view, return_value, params, permitted_registers)
+ }
+
+ /// Computes the order in which the given parameter variables are passed. The default
+ /// implementation first checks [`CallingConvention::arg_registers_shared_index`] to see if the
+ /// parameter ordering is well defined. If the arguments do not share an index, it places all
+ /// integer arguments before the floating point arguments. Arguments that are not passed in a
+ /// normal location are placed last.
+ fn parameter_ordering_for_variables(
+ &self,
+ _view: Option<&BinaryView>,
+ params: &[(Variable, Ref<Type>)],
+ ) -> Vec<Variable> {
+ self.as_ref()
+ .default_parameter_ordering_for_variables(params)
+ }
+
+ /// Computes the stack adjustment applied on return for a call with the given return value and
+ /// parameter locations. The default implementation first checks
+ /// [`CallingConvention::stack_adjusted_on_return`], and returns zero if that returns false.
+ /// Otherwise, it checks the stack parameter locations and
+ /// [`CallingConvention::are_stack_arguments_naturally_aligned`] to compute the stack
+ /// adjustment necessary to cover all parameters.
+ fn stack_adjustment_for_locations(
+ &self,
+ _view: Option<&BinaryView>,
+ return_value: Option<&ValueLocation>,
+ params: &[(ValueLocation, Ref<Type>)],
+ ) -> i64 {
+ self.as_ref()
+ .default_stack_adjustment_for_locations(return_value, params)
+ }
+
+ /// Computes the per-register-stack adjustments (for architectures with register stacks, such
+ /// as the x87 floating point stack) for a call with the given return value and parameter
+ /// locations. The default implementation compares the register stack slots used by the
+ /// parameters and the return value to compute the adjustments.
+ fn register_stack_adjustments(
+ &self,
+ _view: Option<&BinaryView>,
+ return_value: Option<&ValueLocation>,
+ params: &[ValueLocation],
+ ) -> BTreeMap<RegisterId, i32> {
+ self.as_ref()
+ .default_register_stack_adjustments(return_value, params)
+ }
}
-pub fn register_calling_convention<A, C>(arch: &A, name: &str, cc: C) -> Ref<CoreCallingConvention>
+/// Registers a new calling convention with the given architecture.
+///
+/// The convention object is constructed by `func`, which is given the [`CoreCallingConvention`]
+/// handle of the newly created convention. Implementors must store this handle and return it from
+/// their [`AsRef<CoreCallingConvention>`] implementation so that the trait's layout methods can
+/// delegate to the core's default behavior.
+///
+/// NOTE: This function should only be called within `CorePluginInit`.
+pub fn register_calling_convention<A, C, F>(
+ arch: &A,
+ name: &str,
+ func: F,
+) -> Ref<CoreCallingConvention>
where
A: Architecture,
C: 'static + CallingConvention,
+ F: FnOnce(CoreCallingConvention) -> C,
{
+ #[repr(C)]
struct CustomCallingConventionContext<C>
where
C: CallingConvention,
{
- raw_handle: *mut BNCallingConvention,
- cc: C,
+ cc: MaybeUninit<C>,
+ }
+
+ unsafe fn from_ctxt<'a, C: CallingConvention>(ctxt: *mut c_void) -> &'a C {
+ (*(ctxt as *mut CustomCallingConventionContext<C>))
+ .cc
+ .assume_init_ref()
+ }
+
+ unsafe fn register_list(regs: Vec<RegisterId>, count: *mut usize) -> *mut u32 {
+ let regs: Box<[u32]> = regs.iter().map(|r| r.0).collect();
+ *count = regs.len();
+ Box::leak(regs).as_mut_ptr()
+ }
+
+ unsafe fn permitted_registers(
+ has_permitted: bool,
+ regs: *mut u32,
+ count: usize,
+ ) -> Option<Vec<RegisterId>> {
+ if has_permitted {
+ let regs = unsafe { slice_from_raw_parts(regs, count) };
+ Some(regs.iter().copied().map(RegisterId::from).collect())
+ } else {
+ None
+ }
}
- // TODO: It would be nice if these callbacks were moved out to the bottom of this file (maybe in another mod)
extern "C" fn cb_free<C>(ctxt: *mut c_void)
where
C: CallingConvention,
{
ffi_wrap!("CallingConvention::free", unsafe {
- let _ctxt = Box::from_raw(ctxt as *mut CustomCallingConventionContext<C>);
+ let mut ctxt = Box::from_raw(ctxt as *mut CustomCallingConventionContext<C>);
+ ctxt.cc.assume_init_drop();
})
}
@@ -92,7 +377,7 @@ where
return;
}
- let regs_ptr = std::ptr::slice_from_raw_parts_mut(regs, count);
+ let regs_ptr = ptr::slice_from_raw_parts_mut(regs, count);
let _regs = Box::from_raw(regs_ptr);
})
}
@@ -102,19 +387,7 @@ where
C: CallingConvention,
{
ffi_wrap!("CallingConvention::caller_saved_registers", unsafe {
- let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
- let mut regs: Vec<_> = ctxt
- .cc
- .caller_saved_registers()
- .iter()
- .map(|r| r.0)
- .collect();
-
- // SAFETY: `count` is an out parameter
- *count = regs.len();
- let regs_ptr = regs.as_mut_ptr();
- std::mem::forget(regs);
- regs_ptr
+ register_list(from_ctxt::<C>(ctxt).caller_saved_registers(), count)
})
}
@@ -123,19 +396,7 @@ where
C: CallingConvention,
{
ffi_wrap!("CallingConvention::callee_saved_registers", unsafe {
- let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
- let mut regs: Vec<_> = ctxt
- .cc
- .callee_saved_registers()
- .iter()
- .map(|r| r.0)
- .collect();
-
- // SAFETY: `count` is an out parameter
- *count = regs.len();
- let regs_ptr = regs.as_mut_ptr();
- std::mem::forget(regs);
- regs_ptr
+ register_list(from_ctxt::<C>(ctxt).callee_saved_registers(), count)
})
}
@@ -144,14 +405,7 @@ where
C: CallingConvention,
{
ffi_wrap!("CallingConvention::int_arg_registers", unsafe {
- let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
- let mut regs: Vec<_> = ctxt.cc.int_arg_registers().iter().map(|r| r.0).collect();
-
- // SAFETY: `count` is an out parameter
- *count = regs.len();
- let regs_ptr = regs.as_mut_ptr();
- std::mem::forget(regs);
- regs_ptr
+ register_list(from_ctxt::<C>(ctxt).int_arg_registers(), count)
})
}
@@ -160,14 +414,7 @@ where
C: CallingConvention,
{
ffi_wrap!("CallingConvention::float_arg_registers", unsafe {
- let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
- let mut regs: Vec<_> = ctxt.cc.float_arg_registers().iter().map(|r| r.0).collect();
-
- // SAFETY: `count` is an out parameter
- *count = regs.len();
- let regs_ptr = regs.as_mut_ptr();
- std::mem::forget(regs);
- regs_ptr
+ register_list(from_ctxt::<C>(ctxt).float_arg_registers(), count)
})
}
@@ -179,19 +426,7 @@ where
C: CallingConvention,
{
ffi_wrap!("CallingConvention::required_argument_registers", unsafe {
- let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
- let mut regs: Vec<_> = ctxt
- .cc
- .required_argument_registers()
- .iter()
- .map(|r| r.0)
- .collect();
-
- // SAFETY: `count` is an out parameter
- *count = regs.len();
- let regs_ptr = regs.as_mut_ptr();
- std::mem::forget(regs);
- regs_ptr
+ register_list(from_ctxt::<C>(ctxt).required_argument_registers(), count)
})
}
@@ -203,19 +438,7 @@ where
C: CallingConvention,
{
ffi_wrap!("CallingConvention::required_clobbered_registers", unsafe {
- let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
- let mut regs: Vec<_> = ctxt
- .cc
- .required_clobbered_registers()
- .iter()
- .map(|r| r.0)
- .collect();
-
- // SAFETY: `count` is an out parameter
- *count = regs.len();
- let regs_ptr = regs.as_mut_ptr();
- std::mem::forget(regs);
- regs_ptr
+ register_list(from_ctxt::<C>(ctxt).required_clobbered_registers(), count)
})
}
@@ -224,9 +447,7 @@ where
C: CallingConvention,
{
ffi_wrap!("CallingConvention::arg_registers_shared_index", unsafe {
- let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
-
- ctxt.cc.arg_registers_shared_index()
+ from_ctxt::<C>(ctxt).arg_registers_shared_index()
})
}
@@ -236,11 +457,7 @@ where
{
ffi_wrap!(
"CallingConvention::reserved_stack_space_for_arg_registers",
- unsafe {
- let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
-
- ctxt.cc.reserved_stack_space_for_arg_registers()
- }
+ unsafe { from_ctxt::<C>(ctxt).reserved_stack_space_for_arg_registers() }
)
}
@@ -249,9 +466,7 @@ where
C: CallingConvention,
{
ffi_wrap!("CallingConvention::stack_adjusted_on_return", unsafe {
- let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
-
- ctxt.cc.stack_adjusted_on_return()
+ from_ctxt::<C>(ctxt).stack_adjusted_on_return()
})
}
@@ -260,9 +475,7 @@ where
C: CallingConvention,
{
ffi_wrap!("CallingConvention::is_eligible_for_heuristics", unsafe {
- let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
-
- ctxt.cc.is_eligible_for_heuristics()
+ from_ctxt::<C>(ctxt).is_eligible_for_heuristics()
})
}
@@ -271,12 +484,10 @@ where
C: CallingConvention,
{
ffi_wrap!("CallingConvention::return_int_reg", unsafe {
- let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
-
- match ctxt.cc.return_int_reg() {
- Some(r) => r.0,
- _ => 0xffff_ffff,
- }
+ from_ctxt::<C>(ctxt)
+ .return_int_reg()
+ .map(|r| r.0)
+ .unwrap_or(INVALID_REGISTER)
})
}
@@ -285,12 +496,10 @@ where
C: CallingConvention,
{
ffi_wrap!("CallingConvention::return_hi_int_reg", unsafe {
- let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
-
- match ctxt.cc.return_hi_int_reg() {
- Some(r) => r.0,
- _ => 0xffff_ffff,
- }
+ from_ctxt::<C>(ctxt)
+ .return_hi_int_reg()
+ .map(|r| r.0)
+ .unwrap_or(INVALID_REGISTER)
})
}
@@ -299,12 +508,10 @@ where
C: CallingConvention,
{
ffi_wrap!("CallingConvention::return_float_reg", unsafe {
- let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
-
- match ctxt.cc.return_float_reg() {
- Some(r) => r.0,
- _ => 0xffff_ffff,
- }
+ from_ctxt::<C>(ctxt)
+ .return_float_reg()
+ .map(|r| r.0)
+ .unwrap_or(INVALID_REGISTER)
})
}
@@ -313,12 +520,10 @@ where
C: CallingConvention,
{
ffi_wrap!("CallingConvention::global_pointer_reg", unsafe {
- let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
-
- match ctxt.cc.global_pointer_reg() {
- Some(r) => r.0,
- _ => 0xffff_ffff,
- }
+ from_ctxt::<C>(ctxt)
+ .global_pointer_reg()
+ .map(|r| r.0)
+ .unwrap_or(INVALID_REGISTER)
})
}
@@ -330,91 +535,72 @@ where
C: CallingConvention,
{
ffi_wrap!("CallingConvention::implicitly_defined_registers", unsafe {
- let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
- let mut regs: Vec<_> = ctxt
- .cc
- .implicitly_defined_registers()
- .iter()
- .map(|r| r.0)
- .collect();
-
- // SAFETY: `count` is an out parameter
- *count = regs.len();
- let regs_ptr = regs.as_mut_ptr();
- std::mem::forget(regs);
- regs_ptr
+ register_list(from_ctxt::<C>(ctxt).implicitly_defined_registers(), count)
})
}
- #[allow(clippy::extra_unused_type_parameters)]
extern "C" fn cb_incoming_reg_value<C>(
- _ctxt: *mut c_void,
- _reg: u32,
- _func: *mut BNFunction,
+ ctxt: *mut c_void,
+ reg: u32,
+ func: *mut BNFunction,
val: *mut BNRegisterValue,
) where
C: CallingConvention,
{
- // TODO: This is bad; need to finish this stub
ffi_wrap!("CallingConvention::incoming_reg_value", unsafe {
- //let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
- let val = &mut *val;
-
- val.state = BNRegisterValueType::EntryValue;
- val.value = _reg as i64;
+ let func = (!func.is_null()).then(|| Function::from_raw(func));
+ let value =
+ from_ctxt::<C>(ctxt).incoming_register_value(RegisterId(reg), func.as_ref());
+ ptr::write(val, value.into());
})
}
- #[allow(clippy::extra_unused_type_parameters)]
extern "C" fn cb_incoming_flag_value<C>(
- _ctxt: *mut c_void,
- _flag: u32,
- _func: *mut BNFunction,
+ ctxt: *mut c_void,
+ flag: u32,
+ func: *mut BNFunction,
val: *mut BNRegisterValue,
) where
C: CallingConvention,
{
- // TODO: This is bad; need to finish this stub
ffi_wrap!("CallingConvention::incoming_flag_value", unsafe {
- //let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
- let val = &mut *val;
-
- val.state = BNRegisterValueType::EntryValue;
- val.value = _flag as i64;
+ let func = (!func.is_null()).then(|| Function::from_raw(func));
+ let value = from_ctxt::<C>(ctxt).incoming_flag_value(FlagId(flag), func.as_ref());
+ ptr::write(val, value.into());
})
}
extern "C" fn cb_incoming_var_for_param<C>(
ctxt: *mut c_void,
var: *const BNVariable,
- _func: *mut BNFunction,
+ func: *mut BNFunction,
param: *mut BNVariable,
) where
C: CallingConvention,
{
ffi_wrap!("CallingConvention::incoming_var_for_param", unsafe {
- let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
- std::ptr::write(
- param,
- BNGetDefaultIncomingVariableForParameterVariable(ctxt.raw_handle, var),
- );
+ let func = (!func.is_null()).then(|| Function::from_raw(func));
+ let var = Variable::from(&*var);
+ let result =
+ from_ctxt::<C>(ctxt).incoming_variable_for_parameter_variable(&var, func.as_ref());
+ ptr::write(param, result.into());
})
}
extern "C" fn cb_incoming_param_for_var<C>(
ctxt: *mut c_void,
var: *const BNVariable,
- _func: *mut BNFunction,
+ func: *mut BNFunction,
param: *mut BNVariable,
) where
C: CallingConvention,
{
ffi_wrap!("CallingConvention::incoming_param_for_var", unsafe {
- let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
- std::ptr::write(
- param,
- BNGetDefaultParameterVariableForIncomingVariable(ctxt.raw_handle, var),
- );
+ let func = (!func.is_null()).then(|| Function::from_raw(func));
+ let var = Variable::from(&*var);
+ let result =
+ from_ctxt::<C>(ctxt).parameter_variable_for_incoming_variable(&var, func.as_ref());
+ ptr::write(param, result.into());
})
}
@@ -424,18 +610,404 @@ where
{
ffi_wrap!(
"CallingConvention::are_argument_registers_used_for_var_args",
+ unsafe { from_ctxt::<C>(ctxt).are_argument_registers_used_for_var_args() }
+ )
+ }
+
+ extern "C" fn cb_is_return_type_register_compatible<C>(
+ ctxt: *mut c_void,
+ view: *mut BNBinaryView,
+ ty: *mut BNType,
+ ) -> bool
+ where
+ C: CallingConvention,
+ {
+ ffi_wrap!(
+ "CallingConvention::is_return_type_register_compatible",
+ unsafe {
+ let view = (!view.is_null()).then(|| BinaryView::from_raw(view));
+ let ty = (!ty.is_null()).then(|| Type::from_raw(ty));
+ match ty.as_ref() {
+ Some(ty) => {
+ from_ctxt::<C>(ctxt).is_return_type_register_compatible(view.as_ref(), ty)
+ }
+ _ => false,
+ }
+ }
+ )
+ }
+
+ extern "C" fn cb_indirect_return_value_location<C>(ctxt: *mut c_void, out_var: *mut BNVariable)
+ where
+ C: CallingConvention,
+ {
+ ffi_wrap!(
+ "CallingConvention::indirect_return_value_location",
+ unsafe {
+ ptr::write(
+ out_var,
+ from_ctxt::<C>(ctxt).indirect_return_value_location().into(),
+ );
+ }
+ )
+ }
+
+ extern "C" fn cb_returned_indirect_return_value_pointer<C>(
+ ctxt: *mut c_void,
+ out_var: *mut BNVariable,
+ ) -> bool
+ where
+ C: CallingConvention,
+ {
+ ffi_wrap!(
+ "CallingConvention::returned_indirect_return_value_pointer",
+ unsafe {
+ match from_ctxt::<C>(ctxt).returned_indirect_return_value_pointer() {
+ Some(var) => {
+ ptr::write(out_var, var.into());
+ true
+ }
+ None => false,
+ }
+ }
+ )
+ }
+
+ extern "C" fn cb_is_argument_type_register_compatible<C>(
+ ctxt: *mut c_void,
+ view: *mut BNBinaryView,
+ ty: *mut BNType,
+ ) -> bool
+ where
+ C: CallingConvention,
+ {
+ ffi_wrap!(
+ "CallingConvention::is_argument_type_register_compatible",
+ unsafe {
+ let view = (!view.is_null()).then(|| BinaryView::from_raw(view));
+ let ty = (!ty.is_null()).then(|| Type::from_raw(ty));
+ match ty.as_ref() {
+ Some(ty) => {
+ from_ctxt::<C>(ctxt).is_argument_type_register_compatible(view.as_ref(), ty)
+ }
+ _ => false,
+ }
+ }
+ )
+ }
+
+ extern "C" fn cb_is_non_register_argument_indirect<C>(
+ ctxt: *mut c_void,
+ view: *mut BNBinaryView,
+ ty: *mut BNType,
+ ) -> bool
+ where
+ C: CallingConvention,
+ {
+ ffi_wrap!(
+ "CallingConvention::is_non_register_argument_indirect",
+ unsafe {
+ let view = (!view.is_null()).then(|| BinaryView::from_raw(view));
+ let ty = (!ty.is_null()).then(|| Type::from_raw(ty));
+ match ty.as_ref() {
+ Some(ty) => {
+ from_ctxt::<C>(ctxt).is_non_register_argument_indirect(view.as_ref(), ty)
+ }
+ _ => false,
+ }
+ }
+ )
+ }
+
+ extern "C" fn cb_are_stack_arguments_naturally_aligned<C>(ctxt: *mut c_void) -> bool
+ where
+ C: CallingConvention,
+ {
+ ffi_wrap!(
+ "CallingConvention::are_stack_arguments_naturally_aligned",
+ unsafe { from_ctxt::<C>(ctxt).are_stack_arguments_naturally_aligned() }
+ )
+ }
+
+ extern "C" fn cb_are_stack_arguments_pushed_left_to_right<C>(ctxt: *mut c_void) -> bool
+ where
+ C: CallingConvention,
+ {
+ ffi_wrap!(
+ "CallingConvention::are_stack_arguments_pushed_left_to_right",
+ unsafe { from_ctxt::<C>(ctxt).are_stack_arguments_pushed_left_to_right() }
+ )
+ }
+
+ #[allow(clippy::too_many_arguments)]
+ extern "C" fn cb_get_call_layout<C>(
+ ctxt: *mut c_void,
+ view: *mut BNBinaryView,
+ return_value: *mut BNReturnValue,
+ params: *mut BNFunctionParameter,
+ param_count: usize,
+ has_permitted_regs: bool,
+ permitted_regs: *mut u32,
+ permitted_reg_count: usize,
+ result: *mut BNCallLayout,
+ ) where
+ C: CallingConvention,
+ {
+ ffi_wrap!("CallingConvention::get_call_layout", unsafe {
+ let view = (!view.is_null()).then(|| BinaryView::from_raw(view));
+ let return_value = ReturnValue::from_raw(&*return_value);
+ let params: Vec<FunctionParameter> = slice_from_raw_parts(params, param_count)
+ .iter()
+ .map(FunctionParameter::from_raw)
+ .collect();
+ let permitted =
+ permitted_registers(has_permitted_regs, permitted_regs, permitted_reg_count);
+ let layout = from_ctxt::<C>(ctxt).call_layout(
+ view.as_ref(),
+ &return_value,
+ &params,
+ permitted.as_deref(),
+ );
+ ptr::write(result, CallLayout::into_rust_raw(&layout));
+ })
+ }
+
+ extern "C" fn cb_free_call_layout(_ctxt: *mut c_void, layout: *mut BNCallLayout) {
+ ffi_wrap!("CallingConvention::free_call_layout", unsafe {
+ CallLayout::free_rust_raw(&mut *layout);
+ })
+ }
+
+ extern "C" fn cb_get_return_value_location<C>(
+ ctxt: *mut c_void,
+ view: *mut BNBinaryView,
+ return_value: *mut BNReturnValue,
+ out_location: *mut BNValueLocation,
+ ) where
+ C: CallingConvention,
+ {
+ ffi_wrap!("CallingConvention::get_return_value_location", unsafe {
+ let view = (!view.is_null()).then(|| BinaryView::from_raw(view));
+ let return_value = ReturnValue::from_raw(&*return_value);
+ let location = from_ctxt::<C>(ctxt).return_value_location(view.as_ref(), &return_value);
+ ptr::write(out_location, ValueLocation::into_rust_raw(&location));
+ })
+ }
+
+ extern "C" fn cb_free_value_location(_ctxt: *mut c_void, location: *mut BNValueLocation) {
+ ffi_wrap!("CallingConvention::free_value_location", unsafe {
+ ValueLocation::free_rust_raw(ptr::read(location));
+ })
+ }
+
+ #[allow(clippy::too_many_arguments)]
+ extern "C" fn cb_get_parameter_locations<C>(
+ ctxt: *mut c_void,
+ view: *mut BNBinaryView,
+ return_value: *mut BNValueLocation,
+ params: *mut BNFunctionParameter,
+ param_count: usize,
+ has_permitted_regs: bool,
+ permitted_regs: *mut u32,
+ permitted_reg_count: usize,
+ out_count: *mut usize,
+ ) -> *mut BNValueLocation
+ where
+ C: CallingConvention,
+ {
+ ffi_wrap!("CallingConvention::get_parameter_locations", unsafe {
+ let view = (!view.is_null()).then(|| BinaryView::from_raw(view));
+ let return_value =
+ (!return_value.is_null()).then(|| ValueLocation::from_raw(&*return_value));
+ let params: Vec<FunctionParameter> = slice_from_raw_parts(params, param_count)
+ .iter()
+ .map(FunctionParameter::from_raw)
+ .collect();
+ let permitted =
+ permitted_registers(has_permitted_regs, permitted_regs, permitted_reg_count);
+ let locations = from_ctxt::<C>(ctxt).parameter_locations(
+ view.as_ref(),
+ return_value.as_ref(),
+ &params,
+ permitted.as_deref(),
+ );
+ let raw: Box<[BNValueLocation]> =
+ locations.iter().map(ValueLocation::into_rust_raw).collect();
+ *out_count = raw.len();
+ Box::leak(raw).as_mut_ptr()
+ })
+ }
+
+ extern "C" fn cb_free_parameter_locations(
+ _ctxt: *mut c_void,
+ locations: *mut BNValueLocation,
+ count: usize,
+ ) {
+ ffi_wrap!("CallingConvention::free_parameter_locations", unsafe {
+ if locations.is_null() {
+ return;
+ }
+ let raw = Box::from_raw(ptr::slice_from_raw_parts_mut(locations, count));
+ for loc in raw.into_vec() {
+ ValueLocation::free_rust_raw(loc);
+ }
+ })
+ }
+
+ extern "C" fn cb_get_parameter_ordering_for_variables<C>(
+ ctxt: *mut c_void,
+ view: *mut BNBinaryView,
+ vars: *mut BNVariable,
+ types: *mut *mut BNType,
+ param_count: usize,
+ out_count: *mut usize,
+ ) -> *mut BNVariable
+ where
+ C: CallingConvention,
+ {
+ ffi_wrap!(
+ "CallingConvention::get_parameter_ordering_for_variables",
unsafe {
- let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
+ let view = (!view.is_null()).then(|| BinaryView::from_raw(view));
+ let vars = slice_from_raw_parts(vars, param_count);
+ let types = slice_from_raw_parts(types, param_count);
+ let params: Option<Vec<(Variable, Ref<Type>)>> = vars
+ .iter()
+ .zip(types.iter())
+ .map(|(v, &ty)| {
+ (!ty.is_null()).then(|| {
+ (
+ Variable::from(v),
+ Type::ref_from_raw(BNNewTypeReference(ty)),
+ )
+ })
+ })
+ .collect();
+ let ordering = if let Some(params) = params.as_deref() {
+ from_ctxt::<C>(ctxt).parameter_ordering_for_variables(view.as_ref(), params)
+ } else {
+ Vec::new()
+ };
+ let raw: Box<[BNVariable]> =
+ ordering.iter().map(|v| BNVariable::from(*v)).collect();
+ *out_count = raw.len();
+ Box::leak(raw).as_mut_ptr()
+ }
+ )
+ }
- ctxt.cc.are_argument_registers_used_for_var_args()
+ extern "C" fn cb_free_variable_list(_ctxt: *mut c_void, vars: *mut BNVariable, count: usize) {
+ ffi_wrap!("CallingConvention::free_variable_list", unsafe {
+ if vars.is_null() {
+ return;
+ }
+ let _vars = Box::from_raw(ptr::slice_from_raw_parts_mut(vars, count));
+ })
+ }
+
+ extern "C" fn cb_get_stack_adjustment_for_locations<C>(
+ ctxt: *mut c_void,
+ view: *mut BNBinaryView,
+ return_value: *mut BNValueLocation,
+ locations: *mut BNValueLocation,
+ types: *mut *mut BNType,
+ param_count: usize,
+ ) -> i64
+ where
+ C: CallingConvention,
+ {
+ ffi_wrap!(
+ "CallingConvention::get_stack_adjustment_for_locations",
+ unsafe {
+ let view = (!view.is_null()).then(|| BinaryView::from_raw(view));
+ let return_value =
+ (!return_value.is_null()).then(|| ValueLocation::from_raw(&*return_value));
+ let locations = slice_from_raw_parts(locations, param_count);
+ let types = slice_from_raw_parts(types, param_count);
+ let params: Option<Vec<(ValueLocation, Ref<Type>)>> = locations
+ .iter()
+ .zip(types.iter())
+ .map(|(loc, &ty)| {
+ (!ty.is_null()).then(|| {
+ (
+ ValueLocation::from_raw(loc),
+ Type::ref_from_raw(BNNewTypeReference(ty)),
+ )
+ })
+ })
+ .collect();
+ if let Some(params) = params.as_deref() {
+ from_ctxt::<C>(ctxt).stack_adjustment_for_locations(
+ view.as_ref(),
+ return_value.as_ref(),
+ params,
+ )
+ } else {
+ 0
+ }
+ }
+ )
+ }
+
+ extern "C" fn cb_get_register_stack_adjustments<C>(
+ ctxt: *mut c_void,
+ view: *mut BNBinaryView,
+ return_value: *mut BNValueLocation,
+ params: *mut BNValueLocation,
+ param_count: usize,
+ out_regs: *mut *mut u32,
+ out_adjust: *mut *mut i32,
+ ) -> usize
+ where
+ C: CallingConvention,
+ {
+ ffi_wrap!(
+ "CallingConvention::get_register_stack_adjustments",
+ unsafe {
+ let view = (!view.is_null()).then(|| BinaryView::from_raw(view));
+ let return_value =
+ (!return_value.is_null()).then(|| ValueLocation::from_raw(&*return_value));
+ let param_objs: Vec<ValueLocation> = slice_from_raw_parts(params, param_count)
+ .iter()
+ .map(ValueLocation::from_raw)
+ .collect();
+ let adjustments = from_ctxt::<C>(ctxt).register_stack_adjustments(
+ view.as_ref(),
+ return_value.as_ref(),
+ &param_objs,
+ );
+ let regs: Box<[u32]> = adjustments.keys().map(|r| r.0).collect();
+ let adjust: Box<[i32]> = adjustments.values().copied().collect();
+ let count = regs.len();
+ ptr::write(out_regs, Box::leak(regs).as_mut_ptr());
+ ptr::write(out_adjust, Box::leak(adjust).as_mut_ptr());
+ count
+ }
+ )
+ }
+
+ extern "C" fn cb_free_register_stack_adjustments(
+ _ctxt: *mut c_void,
+ regs: *mut u32,
+ adjust: *mut i32,
+ count: usize,
+ ) {
+ ffi_wrap!(
+ "CallingConvention::free_register_stack_adjustments",
+ unsafe {
+ if !regs.is_null() {
+ let _ = Box::from_raw(ptr::slice_from_raw_parts_mut(regs, count));
+ }
+ if !adjust.is_null() {
+ let _ = Box::from_raw(ptr::slice_from_raw_parts_mut(adjust, count));
+ }
}
)
}
let name = name.to_cstr();
- let raw = Box::into_raw(Box::new(CustomCallingConventionContext {
- raw_handle: std::ptr::null_mut(),
- cc,
+ let raw = Box::into_raw(Box::new(CustomCallingConventionContext::<C> {
+ cc: MaybeUninit::uninit(),
}));
let mut cc = BNCustomCallingConvention {
context: raw as *mut _,
@@ -468,25 +1040,25 @@ where
areArgumentRegistersUsedForVarArgs: Some(cb_are_argument_registers_used_for_var_args::<C>),
- isReturnTypeRegisterCompatible: None,
- getIndirectReturnValueLocation: None,
- getReturnedIndirectReturnValuePointer: None,
- isArgumentTypeRegisterCompatible: None,
- isNonRegisterArgumentIndirect: None,
- areStackArgumentsNaturallyAligned: None,
- areStackArgumentsPushedLeftToRight: None,
+ isReturnTypeRegisterCompatible: Some(cb_is_return_type_register_compatible::<C>),
+ getIndirectReturnValueLocation: Some(cb_indirect_return_value_location::<C>),
+ getReturnedIndirectReturnValuePointer: Some(cb_returned_indirect_return_value_pointer::<C>),
+ isArgumentTypeRegisterCompatible: Some(cb_is_argument_type_register_compatible::<C>),
+ isNonRegisterArgumentIndirect: Some(cb_is_non_register_argument_indirect::<C>),
+ areStackArgumentsNaturallyAligned: Some(cb_are_stack_arguments_naturally_aligned::<C>),
+ areStackArgumentsPushedLeftToRight: Some(cb_are_stack_arguments_pushed_left_to_right::<C>),
- getCallLayout: None,
- freeCallLayout: None,
- getReturnValueLocation: None,
- freeValueLocation: None,
- getParameterLocations: None,
- freeParameterLocations: None,
- getParameterOrderingForVariables: None,
- freeVariableList: None,
- getStackAdjustmentForLocations: None,
- getRegisterStackAdjustments: None,
- freeRegisterStackAdjustments: None,
+ getCallLayout: Some(cb_get_call_layout::<C>),
+ freeCallLayout: Some(cb_free_call_layout),
+ getReturnValueLocation: Some(cb_get_return_value_location::<C>),
+ freeValueLocation: Some(cb_free_value_location),
+ getParameterLocations: Some(cb_get_parameter_locations::<C>),
+ freeParameterLocations: Some(cb_free_parameter_locations),
+ getParameterOrderingForVariables: Some(cb_get_parameter_ordering_for_variables::<C>),
+ freeVariableList: Some(cb_free_variable_list),
+ getStackAdjustmentForLocations: Some(cb_get_stack_adjustment_for_locations::<C>),
+ getRegisterStackAdjustments: Some(cb_get_register_stack_adjustments::<C>),
+ freeRegisterStackAdjustments: Some(cb_free_register_stack_adjustments),
};
unsafe {
@@ -495,7 +1067,8 @@ where
assert!(!result.is_null());
- (*raw).raw_handle = result;
+ let core = CoreCallingConvention::from_raw(result, arch.as_ref().handle());
+ (*raw).cc.write(func(core));
BNRegisterCallingConvention(arch.as_ref().handle, result);
@@ -536,57 +1109,96 @@ impl CoreCallingConvention {
unsafe { BnString::into_string(BNGetCallingConventionName(self.handle)) }
}
- pub fn call_layout(
+ pub fn arch(&self) -> CoreArchitecture {
+ self.arch_handle
+ }
+
+ fn raw_permitted_args(permitted_registers: Option<&[RegisterId]>) -> Option<Vec<u32>> {
+ permitted_registers.map(|regs| regs.iter().map(|r| r.0).collect())
+ }
+
+ pub fn default_incoming_variable_for_parameter_variable(&self, var: &Variable) -> Variable {
+ let raw = BNVariable::from(var);
+ Variable::from(unsafe {
+ BNGetDefaultIncomingVariableForParameterVariable(self.handle, &raw)
+ })
+ }
+
+ pub fn default_parameter_variable_for_incoming_variable(&self, var: &Variable) -> Variable {
+ let raw = BNVariable::from(var);
+ Variable::from(unsafe {
+ BNGetDefaultParameterVariableForIncomingVariable(self.handle, &raw)
+ })
+ }
+
+ pub fn default_is_return_type_register_compatible(&self, ty: &Type) -> bool {
+ unsafe { BNDefaultIsReturnTypeRegisterCompatible(self.handle, ty.handle) }
+ }
+
+ pub fn default_indirect_return_value_location(&self) -> Variable {
+ Variable::from(unsafe { BNGetDefaultIndirectReturnValueLocation(self.handle) })
+ }
+
+ pub fn default_is_argument_type_register_compatible(&self, ty: &Type) -> bool {
+ unsafe { BNDefaultIsArgumentTypeRegisterCompatible(self.handle, ty.handle) }
+ }
+
+ pub fn default_call_layout(
&self,
- view: &BinaryView,
- return_value: impl Into<ReturnValue>,
+ view: Option<&BinaryView>,
+ return_value: &ReturnValue,
params: &[FunctionParameter],
- permitted_registers: Option<&[CoreRegister]>,
+ permitted_registers: Option<&[RegisterId]>,
) -> CallLayout {
- let raw_return_value = ReturnValue::into_rust_raw(return_value.into());
+ let raw_return_value = ReturnValue::into_rust_raw(return_value);
let raw_params: Vec<BNFunctionParameter> = params
.iter()
.cloned()
.map(FunctionParameter::into_raw)
.collect();
- 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 {
- BNGetCallLayout(
+ let raw_layout: BNCallLayout = match Self::raw_permitted_args(permitted_registers) {
+ Some(permitted) => unsafe {
+ BNGetDefaultCallLayout(
self.handle,
- view.handle,
+ view.map(|view| view.handle).unwrap_or(ptr::null_mut()),
&raw_return_value,
raw_params.as_ptr(),
raw_params.len(),
- permitted_regs.as_ptr(),
- permitted_regs.len(),
+ permitted.as_ptr(),
+ permitted.len(),
)
- }
- } else {
- unsafe {
- BNGetCallLayoutDefaultPermittedArgs(
+ },
+ None => unsafe {
+ BNGetDefaultCallLayoutDefaultPermittedArgs(
self.handle,
- view.handle,
+ view.map(|view| view.handle).unwrap_or(ptr::null_mut()),
&raw_return_value,
raw_params.as_ptr(),
raw_params.len(),
)
- }
+ },
};
ReturnValue::free_rust_raw(raw_return_value);
+ for param in raw_params {
+ FunctionParameter::free_raw(param);
+ }
CallLayout::from_owned_core_raw(raw_layout)
}
- pub fn return_value_location(
+ pub fn default_return_value_location(
&self,
- view: &BinaryView,
- return_value: impl Into<ReturnValue>,
+ view: Option<&BinaryView>,
+ return_value: &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) };
+ let mut raw_return_value = ReturnValue::into_rust_raw(return_value);
+ let mut raw_location = unsafe {
+ BNGetDefaultReturnValueLocation(
+ self.handle,
+ view.map(|view| view.handle).unwrap_or(ptr::null_mut()),
+ &mut raw_return_value,
+ )
+ };
ReturnValue::free_rust_raw(raw_return_value);
let result = ValueLocation::from_raw(&raw_location);
unsafe {
@@ -594,6 +1206,176 @@ impl CoreCallingConvention {
}
result
}
+
+ pub fn default_parameter_locations(
+ &self,
+ view: Option<&BinaryView>,
+ return_value: Option<&ValueLocation>,
+ params: &[FunctionParameter],
+ permitted_registers: Option<&[RegisterId]>,
+ ) -> Vec<ValueLocation> {
+ let mut raw_return_value = return_value.map(ValueLocation::into_rust_raw);
+ let return_value_ptr = raw_return_value
+ .as_mut()
+ .map(|r| r as *mut _)
+ .unwrap_or(ptr::null_mut());
+ let mut raw_params: Vec<BNFunctionParameter> = params
+ .iter()
+ .cloned()
+ .map(FunctionParameter::into_raw)
+ .collect();
+ let mut count = 0;
+ let locations_ptr = unsafe {
+ match Self::raw_permitted_args(permitted_registers) {
+ Some(permitted) => BNGetDefaultParameterLocations(
+ self.handle,
+ view.map(|view| view.handle).unwrap_or(ptr::null_mut()),
+ return_value_ptr,
+ raw_params.as_mut_ptr(),
+ raw_params.len(),
+ permitted.as_ptr(),
+ permitted.len(),
+ &mut count,
+ ),
+ None => BNGetDefaultParameterLocationsDefaultPermittedArgs(
+ self.handle,
+ view.map(|view| view.handle).unwrap_or(ptr::null_mut()),
+ return_value_ptr,
+ raw_params.as_mut_ptr(),
+ raw_params.len(),
+ &mut count,
+ ),
+ }
+ };
+
+ if let Some(raw_return_value) = raw_return_value {
+ ValueLocation::free_rust_raw(raw_return_value);
+ }
+ for param in raw_params.drain(..) {
+ FunctionParameter::free_raw(param);
+ }
+
+ let result = unsafe {
+ slice_from_raw_parts(locations_ptr, count)
+ .iter()
+ .map(ValueLocation::from_raw)
+ .collect()
+ };
+ unsafe {
+ BNFreeValueLocationList(locations_ptr, count);
+ }
+ result
+ }
+
+ pub fn default_parameter_ordering_for_variables(
+ &self,
+ params: &[(Variable, Ref<Type>)],
+ ) -> Vec<Variable> {
+ let raw_vars: Vec<BNVariable> = params.iter().map(|(v, _)| BNVariable::from(*v)).collect();
+ let mut raw_types: Vec<*const BNType> = params
+ .iter()
+ .map(|(_, t)| t.handle as *const BNType)
+ .collect();
+ let mut count = 0;
+ let vars_ptr = unsafe {
+ BNGetDefaultParameterOrderingForVariables(
+ self.handle,
+ raw_vars.as_ptr(),
+ raw_types.as_mut_ptr(),
+ params.len(),
+ &mut count,
+ )
+ };
+ let result = unsafe {
+ slice_from_raw_parts(vars_ptr, count)
+ .iter()
+ .map(Variable::from)
+ .collect()
+ };
+ unsafe {
+ BNFreeVariableList(vars_ptr);
+ }
+ result
+ }
+
+ pub fn default_stack_adjustment_for_locations(
+ &self,
+ return_value: Option<&ValueLocation>,
+ params: &[(ValueLocation, Ref<Type>)],
+ ) -> i64 {
+ let mut raw_return_value = return_value.map(ValueLocation::into_rust_raw);
+ let return_value_ptr = raw_return_value
+ .as_mut()
+ .map(|r| r as *mut _)
+ .unwrap_or(ptr::null_mut());
+ let raw_locations: Vec<BNValueLocation> = params
+ .iter()
+ .map(|(loc, _)| ValueLocation::into_rust_raw(loc))
+ .collect();
+ let mut raw_types: Vec<*const BNType> = params
+ .iter()
+ .map(|(_, t)| t.handle as *const BNType)
+ .collect();
+ let result = unsafe {
+ BNGetDefaultStackAdjustmentForLocations(
+ self.handle,
+ return_value_ptr,
+ raw_locations.as_ptr(),
+ raw_types.as_mut_ptr(),
+ params.len(),
+ )
+ };
+ if let Some(raw_return_value) = raw_return_value {
+ ValueLocation::free_rust_raw(raw_return_value);
+ }
+ for loc in raw_locations {
+ ValueLocation::free_rust_raw(loc);
+ }
+ result
+ }
+
+ pub fn default_register_stack_adjustments(
+ &self,
+ return_value: Option<&ValueLocation>,
+ params: &[ValueLocation],
+ ) -> BTreeMap<RegisterId, i32> {
+ let mut raw_return_value = return_value.map(ValueLocation::into_rust_raw);
+ let return_value_ptr = raw_return_value
+ .as_mut()
+ .map(|r| r as *mut _)
+ .unwrap_or(ptr::null_mut());
+ let mut raw_params: Vec<BNValueLocation> =
+ params.iter().map(ValueLocation::into_rust_raw).collect();
+ let mut regs: *mut u32 = ptr::null_mut();
+ let mut adjust: *mut i32 = ptr::null_mut();
+ let count = unsafe {
+ BNGetCallingConventionDefaultRegisterStackAdjustments(
+ self.handle,
+ return_value_ptr,
+ raw_params.as_mut_ptr(),
+ raw_params.len(),
+ &mut regs,
+ &mut adjust,
+ )
+ };
+ let regs_slice = unsafe { slice_from_raw_parts(regs, count) };
+ let adjust_slice = unsafe { slice_from_raw_parts(adjust, count) };
+ let result = regs_slice
+ .iter()
+ .zip(adjust_slice.iter())
+ .map(|(r, a)| (RegisterId(*r), *a))
+ .collect();
+ if let Some(raw_return_value) = raw_return_value {
+ ValueLocation::free_rust_raw(raw_return_value);
+ }
+ for param in raw_params {
+ ValueLocation::free_rust_raw(param);
+ }
+ unsafe {
+ BNFreeCallingConventionRegisterStackAdjustments(regs, adjust);
+ }
+ result
+ }
}
unsafe impl Send for CoreCallingConvention {}
@@ -606,6 +1388,12 @@ impl PartialEq for CoreCallingConvention {
}
}
+impl AsRef<CoreCallingConvention> for CoreCallingConvention {
+ fn as_ref(&self) -> &CoreCallingConvention {
+ self
+ }
+}
+
impl Debug for CoreCallingConvention {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CoreCallingConvention")
@@ -819,6 +1607,333 @@ impl CallingConvention for CoreCallingConvention {
fn are_argument_registers_used_for_var_args(&self) -> bool {
unsafe { BNAreArgumentRegistersUsedForVarArgs(self.handle) }
}
+
+ fn incoming_register_value(&self, reg: RegisterId, func: Option<&Function>) -> RegisterValue {
+ let func = func.map(|f| f.handle).unwrap_or(ptr::null_mut());
+ RegisterValue::from(unsafe { BNGetIncomingRegisterValue(self.handle, reg.0, func) })
+ }
+
+ fn incoming_flag_value(&self, flag: FlagId, func: Option<&Function>) -> RegisterValue {
+ let func = func.map(|f| f.handle).unwrap_or(ptr::null_mut());
+ RegisterValue::from(unsafe { BNGetIncomingFlagValue(self.handle, flag.0, func) })
+ }
+
+ fn incoming_variable_for_parameter_variable(
+ &self,
+ var: &Variable,
+ func: Option<&Function>,
+ ) -> Variable {
+ let raw = BNVariable::from(var);
+ let func = func.map(|f| f.handle).unwrap_or(ptr::null_mut());
+ Variable::from(unsafe {
+ BNGetIncomingVariableForParameterVariable(self.handle, &raw, func)
+ })
+ }
+
+ fn parameter_variable_for_incoming_variable(
+ &self,
+ var: &Variable,
+ func: Option<&Function>,
+ ) -> Variable {
+ let raw = BNVariable::from(var);
+ let func = func.map(|f| f.handle).unwrap_or(ptr::null_mut());
+ Variable::from(unsafe {
+ BNGetParameterVariableForIncomingVariable(self.handle, &raw, func)
+ })
+ }
+
+ fn is_return_type_register_compatible(&self, view: Option<&BinaryView>, ty: &Type) -> bool {
+ unsafe {
+ BNIsReturnTypeRegisterCompatible(
+ self.handle,
+ view.map(|view| view.handle).unwrap_or(ptr::null_mut()),
+ ty.handle,
+ )
+ }
+ }
+
+ fn indirect_return_value_location(&self) -> Variable {
+ Variable::from(unsafe { BNGetIndirectReturnValueLocation(self.handle) })
+ }
+
+ fn returned_indirect_return_value_pointer(&self) -> Option<Variable> {
+ let mut var = BNVariable::default();
+ unsafe {
+ BNGetReturnedIndirectReturnValuePointer(self.handle, &mut var)
+ .then(|| Variable::from(var))
+ }
+ }
+
+ fn is_argument_type_register_compatible(&self, view: Option<&BinaryView>, ty: &Type) -> bool {
+ unsafe {
+ BNIsArgumentTypeRegisterCompatible(
+ self.handle,
+ view.map(|view| view.handle).unwrap_or(ptr::null_mut()),
+ ty.handle,
+ )
+ }
+ }
+
+ fn is_non_register_argument_indirect(&self, view: Option<&BinaryView>, ty: &Type) -> bool {
+ unsafe {
+ BNIsNonRegisterArgumentIndirect(
+ self.handle,
+ view.map(|view| view.handle).unwrap_or(ptr::null_mut()),
+ ty.handle,
+ )
+ }
+ }
+
+ fn are_stack_arguments_naturally_aligned(&self) -> bool {
+ unsafe { BNAreStackArgumentsNaturallyAligned(self.handle) }
+ }
+
+ fn are_stack_arguments_pushed_left_to_right(&self) -> bool {
+ unsafe { BNAreStackArgumentsPushedLeftToRight(self.handle) }
+ }
+
+ fn call_layout(
+ &self,
+ view: Option<&BinaryView>,
+ return_value: &ReturnValue,
+ params: &[FunctionParameter],
+ permitted_registers: Option<&[RegisterId]>,
+ ) -> CallLayout {
+ let raw_return_value = ReturnValue::into_rust_raw(return_value);
+ let raw_params: Vec<BNFunctionParameter> = params
+ .iter()
+ .cloned()
+ .map(FunctionParameter::into_raw)
+ .collect();
+ let raw_layout: BNCallLayout = if let Some(permitted_args) = permitted_registers {
+ let permitted_regs = permitted_args.iter().map(|r| r.0).collect::<Vec<_>>();
+
+ unsafe {
+ BNGetCallLayout(
+ self.handle,
+ view.map(|view| view.handle).unwrap_or(ptr::null_mut()),
+ &raw_return_value,
+ raw_params.as_ptr(),
+ raw_params.len(),
+ permitted_regs.as_ptr(),
+ permitted_regs.len(),
+ )
+ }
+ } else {
+ unsafe {
+ BNGetCallLayoutDefaultPermittedArgs(
+ self.handle,
+ view.map(|view| view.handle).unwrap_or(ptr::null_mut()),
+ &raw_return_value,
+ raw_params.as_ptr(),
+ raw_params.len(),
+ )
+ }
+ };
+
+ ReturnValue::free_rust_raw(raw_return_value);
+ for param in raw_params {
+ FunctionParameter::free_raw(param);
+ }
+ CallLayout::from_owned_core_raw(raw_layout)
+ }
+
+ fn return_value_location(
+ &self,
+ view: Option<&BinaryView>,
+ return_value: &ReturnValue,
+ ) -> ValueLocation {
+ let mut raw_return_value = ReturnValue::into_rust_raw(return_value);
+ let mut raw_location = unsafe {
+ BNGetReturnValueLocation(
+ self.handle,
+ view.map(|view| view.handle).unwrap_or(ptr::null_mut()),
+ &mut raw_return_value,
+ )
+ };
+ ReturnValue::free_rust_raw(raw_return_value);
+ let result = ValueLocation::from_raw(&raw_location);
+ unsafe {
+ BNFreeValueLocation(&mut raw_location);
+ }
+ result
+ }
+
+ fn parameter_locations(
+ &self,
+ view: Option<&BinaryView>,
+ return_value: Option<&ValueLocation>,
+ params: &[FunctionParameter],
+ permitted_registers: Option<&[RegisterId]>,
+ ) -> Vec<ValueLocation> {
+ let mut raw_return_value = return_value.map(ValueLocation::into_rust_raw);
+ let return_value_ptr = raw_return_value
+ .as_mut()
+ .map(|r| r as *mut _)
+ .unwrap_or(ptr::null_mut());
+ let mut raw_params: Vec<BNFunctionParameter> = params
+ .iter()
+ .cloned()
+ .map(FunctionParameter::into_raw)
+ .collect();
+ let mut count = 0;
+ let locations_ptr = unsafe {
+ match Self::raw_permitted_args(permitted_registers) {
+ Some(permitted) => BNGetParameterLocations(
+ self.handle,
+ view.map(|view| view.handle).unwrap_or(ptr::null_mut()),
+ return_value_ptr,
+ raw_params.as_mut_ptr(),
+ raw_params.len(),
+ permitted.as_ptr(),
+ permitted.len(),
+ &mut count,
+ ),
+ None => BNGetParameterLocationsDefaultPermittedArgs(
+ self.handle,
+ view.map(|view| view.handle).unwrap_or(ptr::null_mut()),
+ return_value_ptr,
+ raw_params.as_mut_ptr(),
+ raw_params.len(),
+ &mut count,
+ ),
+ }
+ };
+
+ if let Some(raw_return_value) = raw_return_value {
+ ValueLocation::free_rust_raw(raw_return_value);
+ }
+ for param in raw_params {
+ FunctionParameter::free_raw(param);
+ }
+
+ let result = unsafe {
+ slice_from_raw_parts(locations_ptr, count)
+ .iter()
+ .map(ValueLocation::from_raw)
+ .collect()
+ };
+ unsafe {
+ BNFreeValueLocationList(locations_ptr, count);
+ }
+ result
+ }
+
+ fn parameter_ordering_for_variables(
+ &self,
+ view: Option<&BinaryView>,
+ params: &[(Variable, Ref<Type>)],
+ ) -> Vec<Variable> {
+ let raw_vars: Vec<BNVariable> = params.iter().map(|(v, _)| BNVariable::from(*v)).collect();
+ let mut raw_types: Vec<*const BNType> = params
+ .iter()
+ .map(|(_, t)| t.handle as *const BNType)
+ .collect();
+ let mut count = 0;
+ let vars_ptr = unsafe {
+ BNGetParameterOrderingForVariables(
+ self.handle,
+ view.map(|view| view.handle).unwrap_or(ptr::null_mut()),
+ raw_vars.as_ptr(),
+ raw_types.as_mut_ptr(),
+ params.len(),
+ &mut count,
+ )
+ };
+ let result = unsafe {
+ slice_from_raw_parts(vars_ptr, count)
+ .iter()
+ .map(Variable::from)
+ .collect()
+ };
+ unsafe {
+ BNFreeVariableList(vars_ptr);
+ }
+ result
+ }
+
+ fn stack_adjustment_for_locations(
+ &self,
+ view: Option<&BinaryView>,
+ return_value: Option<&ValueLocation>,
+ params: &[(ValueLocation, Ref<Type>)],
+ ) -> i64 {
+ let mut raw_return_value = return_value.map(ValueLocation::into_rust_raw);
+ let return_value_ptr = raw_return_value
+ .as_mut()
+ .map(|r| r as *mut _)
+ .unwrap_or(ptr::null_mut());
+ let raw_locations: Vec<BNValueLocation> = params
+ .iter()
+ .map(|(loc, _)| ValueLocation::into_rust_raw(loc))
+ .collect();
+ let mut raw_types: Vec<*const BNType> = params
+ .iter()
+ .map(|(_, t)| t.handle as *const BNType)
+ .collect();
+ let result = unsafe {
+ BNGetStackAdjustmentForLocations(
+ self.handle,
+ view.map(|view| view.handle).unwrap_or(ptr::null_mut()),
+ return_value_ptr,
+ raw_locations.as_ptr(),
+ raw_types.as_mut_ptr(),
+ params.len(),
+ )
+ };
+ if let Some(raw_return_value) = raw_return_value {
+ ValueLocation::free_rust_raw(raw_return_value);
+ }
+ for loc in raw_locations {
+ ValueLocation::free_rust_raw(loc);
+ }
+ result
+ }
+
+ fn register_stack_adjustments(
+ &self,
+ view: Option<&BinaryView>,
+ return_value: Option<&ValueLocation>,
+ params: &[ValueLocation],
+ ) -> BTreeMap<RegisterId, i32> {
+ let mut raw_return_value = return_value.map(ValueLocation::into_rust_raw);
+ let return_value_ptr = raw_return_value
+ .as_mut()
+ .map(|r| r as *mut _)
+ .unwrap_or(ptr::null_mut());
+ let mut raw_params: Vec<BNValueLocation> =
+ params.iter().map(ValueLocation::into_rust_raw).collect();
+ let mut regs: *mut u32 = ptr::null_mut();
+ let mut adjust: *mut i32 = ptr::null_mut();
+ let count = unsafe {
+ BNGetCallingConventionRegisterStackAdjustments(
+ self.handle,
+ view.map(|view| view.handle).unwrap_or(ptr::null_mut()),
+ return_value_ptr,
+ raw_params.as_mut_ptr(),
+ raw_params.len(),
+ &mut regs,
+ &mut adjust,
+ )
+ };
+ let regs_slice = unsafe { slice_from_raw_parts(regs, count) };
+ let adjust_slice = unsafe { slice_from_raw_parts(adjust, count) };
+ let result = regs_slice
+ .iter()
+ .zip(adjust_slice.iter())
+ .map(|(r, a)| (RegisterId(*r), *a))
+ .collect();
+ if let Some(raw_return_value) = raw_return_value {
+ ValueLocation::free_rust_raw(raw_return_value);
+ }
+ for param in raw_params {
+ ValueLocation::free_rust_raw(param);
+ }
+ unsafe {
+ BNFreeCallingConventionRegisterStackAdjustments(regs, adjust);
+ }
+ result
+ }
}
impl ToOwned for CoreCallingConvention {
@@ -864,7 +1979,19 @@ unsafe impl CoreArrayProviderInner for CoreCallingConvention {
}
}
+/// A builder for a calling convention defined entirely by its register sets and flags.
+///
+/// Conventions built this way use the core's default behavior for parameter and return value
+/// placement. Override the [`CallingConvention`] trait directly if you need custom layout logic.
pub struct ConventionBuilder<A: Architecture> {
+ config: ConventionConfig,
+ arch_handle: A::Handle,
+ _arch: PhantomData<*const A>,
+}
+
+/// The plain-data portion of a [`ConventionBuilder`]. Moved to a [`RegisteredConvention`] when
+/// registered.
+struct ConventionConfig {
caller_saved_registers: Vec<RegisterId>,
callee_saved_registers: Vec<RegisterId>,
int_arg_registers: Vec<RegisterId>,
@@ -886,15 +2013,19 @@ pub struct ConventionBuilder<A: Architecture> {
implicitly_defined_registers: Vec<RegisterId>,
are_argument_registers_used_for_var_args: bool,
+ are_stack_arguments_naturally_aligned: bool,
+ are_stack_arguments_pushed_left_to_right: bool,
- arch_handle: A::Handle,
- _arch: PhantomData<*const A>,
+ is_non_register_argument_indirect: bool,
+
+ indirect_return_value_location: Option<Variable>,
+ returned_indirect_return_value_pointer: Option<Variable>,
}
macro_rules! bool_arg {
($name:ident) => {
pub fn $name(mut self, val: bool) -> Self {
- self.$name = val;
+ self.config.$name = val;
self
}
};
@@ -911,7 +2042,7 @@ macro_rules! reg_list {
.filter_map(|&r| arch.register_by_name(r))
.map(|r| r.id());
- self.$name = arch_regs.collect();
+ self.config.$name = arch_regs.collect();
}
self
@@ -925,7 +2056,7 @@ macro_rules! reg {
{
// FIXME NLL
let arch = self.arch_handle.borrow();
- self.$name = arch.register_by_name(reg).map(|r| r.id());
+ self.config.$name = arch.register_by_name(reg).map(|r| r.id());
}
self
@@ -936,28 +2067,36 @@ macro_rules! reg {
impl<A: Architecture> ConventionBuilder<A> {
pub fn new(arch: &A) -> Self {
Self {
- caller_saved_registers: Vec::new(),
- callee_saved_registers: Vec::new(),
- int_arg_registers: Vec::new(),
- float_arg_registers: Vec::new(),
- required_argument_registers: Vec::new(),
- required_clobbered_registers: Vec::new(),
+ config: ConventionConfig {
+ caller_saved_registers: Vec::new(),
+ callee_saved_registers: Vec::new(),
+ int_arg_registers: Vec::new(),
+ float_arg_registers: Vec::new(),
+ required_argument_registers: Vec::new(),
+ required_clobbered_registers: Vec::new(),
+
+ arg_registers_shared_index: false,
+ reserved_stack_space_for_arg_registers: false,
+ stack_adjusted_on_return: false,
+ is_eligible_for_heuristics: false,
- arg_registers_shared_index: false,
- reserved_stack_space_for_arg_registers: false,
- stack_adjusted_on_return: false,
- is_eligible_for_heuristics: false,
+ return_int_reg: None,
+ return_hi_int_reg: None,
+ return_float_reg: None,
- return_int_reg: None,
- return_hi_int_reg: None,
- return_float_reg: None,
+ global_pointer_reg: None,
- global_pointer_reg: None,
+ implicitly_defined_registers: Vec::new(),
- implicitly_defined_registers: Vec::new(),
+ are_argument_registers_used_for_var_args: false,
+ are_stack_arguments_naturally_aligned: false,
+ are_stack_arguments_pushed_left_to_right: false,
- are_argument_registers_used_for_var_args: false,
+ is_non_register_argument_indirect: false,
+ indirect_return_value_location: None,
+ returned_indirect_return_value_pointer: None,
+ },
arch_handle: arch.handle(),
_arch: PhantomData,
}
@@ -984,83 +2123,135 @@ impl<A: Architecture> ConventionBuilder<A> {
reg_list!(implicitly_defined_registers);
bool_arg!(are_argument_registers_used_for_var_args);
+ bool_arg!(are_stack_arguments_naturally_aligned);
+ bool_arg!(are_stack_arguments_pushed_left_to_right);
+
+ bool_arg!(is_non_register_argument_indirect);
+
+ pub fn indirect_return_value_location(mut self, var: Variable) -> Self {
+ self.config.indirect_return_value_location = Some(var);
+ self
+ }
+
+ pub fn returned_indirect_return_value_pointer(mut self, var: Variable) -> Self {
+ self.config.returned_indirect_return_value_pointer = Some(var);
+ self
+ }
pub fn register(self, name: &str) -> Ref<CoreCallingConvention> {
let arch = self.arch_handle.clone();
- register_calling_convention(arch.borrow(), name, self)
+ register_calling_convention(arch.borrow(), name, move |core| RegisteredConvention {
+ config: self.config,
+ core,
+ })
}
}
-impl<A: Architecture> CallingConvention for ConventionBuilder<A> {
+/// A registered [`ConventionBuilder`], holding its configuration and core handle.
+struct RegisteredConvention {
+ config: ConventionConfig,
+ core: CoreCallingConvention,
+}
+
+unsafe impl Send for RegisteredConvention {}
+unsafe impl Sync for RegisteredConvention {}
+
+impl AsRef<CoreCallingConvention> for RegisteredConvention {
+ fn as_ref(&self) -> &CoreCallingConvention {
+ &self.core
+ }
+}
+
+impl CallingConvention for RegisteredConvention {
fn caller_saved_registers(&self) -> Vec<RegisterId> {
- self.caller_saved_registers.clone()
+ self.config.caller_saved_registers.clone()
}
fn callee_saved_registers(&self) -> Vec<RegisterId> {
- self.callee_saved_registers.clone()
+ self.config.callee_saved_registers.clone()
}
fn int_arg_registers(&self) -> Vec<RegisterId> {
- self.int_arg_registers.clone()
+ self.config.int_arg_registers.clone()
}
fn float_arg_registers(&self) -> Vec<RegisterId> {
- self.float_arg_registers.clone()
+ self.config.float_arg_registers.clone()
}
fn required_argument_registers(&self) -> Vec<RegisterId> {
- self.required_argument_registers.clone()
+ self.config.required_argument_registers.clone()
}
fn required_clobbered_registers(&self) -> Vec<RegisterId> {
- self.required_clobbered_registers.clone()
+ self.config.required_clobbered_registers.clone()
}
fn arg_registers_shared_index(&self) -> bool {
- self.arg_registers_shared_index
+ self.config.arg_registers_shared_index
}
fn reserved_stack_space_for_arg_registers(&self) -> bool {
- self.reserved_stack_space_for_arg_registers
+ self.config.reserved_stack_space_for_arg_registers
}
fn stack_adjusted_on_return(&self) -> bool {
- self.stack_adjusted_on_return
+ self.config.stack_adjusted_on_return
}
fn is_eligible_for_heuristics(&self) -> bool {
- self.is_eligible_for_heuristics
+ self.config.is_eligible_for_heuristics
}
fn return_int_reg(&self) -> Option<RegisterId> {
- self.return_int_reg
+ self.config.return_int_reg
}
fn return_hi_int_reg(&self) -> Option<RegisterId> {
- self.return_hi_int_reg
+ self.config.return_hi_int_reg
}
fn return_float_reg(&self) -> Option<RegisterId> {
- self.return_float_reg
+ self.config.return_float_reg
}
fn global_pointer_reg(&self) -> Option<RegisterId> {
- self.global_pointer_reg
+ self.config.global_pointer_reg
}
fn implicitly_defined_registers(&self) -> Vec<RegisterId> {
- self.implicitly_defined_registers.clone()
+ self.config.implicitly_defined_registers.clone()
}
fn are_argument_registers_used_for_var_args(&self) -> bool {
- self.are_argument_registers_used_for_var_args
+ self.config.are_argument_registers_used_for_var_args
+ }
+
+ fn are_stack_arguments_naturally_aligned(&self) -> bool {
+ self.config.are_stack_arguments_naturally_aligned
+ }
+
+ fn are_stack_arguments_pushed_left_to_right(&self) -> bool {
+ self.config.are_stack_arguments_pushed_left_to_right
+ }
+
+ fn is_non_register_argument_indirect(&self, _view: Option<&BinaryView>, _ty: &Type) -> bool {
+ self.config.is_non_register_argument_indirect
+ }
+
+ fn indirect_return_value_location(&self) -> Variable {
+ match self.config.indirect_return_value_location {
+ Some(var) => var,
+ None => self.as_ref().default_indirect_return_value_location(),
+ }
}
-}
-unsafe impl<A: Architecture> Send for ConventionBuilder<A> {}
-unsafe impl<A: Architecture> Sync for ConventionBuilder<A> {}
+ fn returned_indirect_return_value_pointer(&self) -> Option<Variable> {
+ self.config.returned_indirect_return_value_pointer
+ }
+}
-#[derive(Debug, Clone, PartialEq, Eq)]
+#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct CallLayout {
pub parameters: Vec<ValueLocation>,
pub return_value: Option<ValueLocation>,
@@ -1112,4 +2303,57 @@ impl CallLayout {
pub(crate) fn free_core_raw(value: &mut BNCallLayout) {
unsafe { BNFreeCallLayout(value) }
}
+
+ /// Build a RUST ALLOCATED value. Free it only with [Self::free_rust_raw].
+ pub(crate) fn into_rust_raw(value: &Self) -> BNCallLayout {
+ let parameters: Box<[BNValueLocation]> = value
+ .parameters
+ .iter()
+ .map(ValueLocation::into_rust_raw)
+ .collect();
+ let regs: Box<[u32]> = value
+ .register_stack_adjustments
+ .keys()
+ .map(|r| r.0)
+ .collect();
+ let amounts: Box<[i32]> = value.register_stack_adjustments.values().copied().collect();
+ let return_value =
+ ValueLocation::into_rust_raw(value.return_value.as_ref().unwrap_or(&ValueLocation {
+ components: Vec::new(),
+ indirect: false,
+ returned_pointer: None,
+ }));
+ BNCallLayout {
+ parameterCount: parameters.len(),
+ parameters: Box::leak(parameters).as_mut_ptr(),
+ returnValueValid: value.return_value.is_some(),
+ returnValue: return_value,
+ stackAdjustment: value.stack_adjustment,
+ registerStackAdjustmentCount: regs.len(),
+ registerStackAdjustmentRegisters: Box::leak(regs).as_mut_ptr(),
+ registerStackAdjustmentAmounts: Box::leak(amounts).as_mut_ptr(),
+ }
+ }
+
+ /// Free a RUST ALLOCATED value. Do not use this with CORE ALLOCATED values.
+ pub(crate) fn free_rust_raw(value: &mut BNCallLayout) {
+ unsafe {
+ let params = Box::from_raw(ptr::slice_from_raw_parts_mut(
+ value.parameters,
+ value.parameterCount,
+ ));
+ for loc in params.into_vec() {
+ ValueLocation::free_rust_raw(loc);
+ }
+ ValueLocation::free_rust_raw(ptr::read(&value.returnValue));
+ let _ = Box::from_raw(ptr::slice_from_raw_parts_mut(
+ value.registerStackAdjustmentRegisters,
+ value.registerStackAdjustmentCount,
+ ));
+ let _ = Box::from_raw(ptr::slice_from_raw_parts_mut(
+ value.registerStackAdjustmentAmounts,
+ value.registerStackAdjustmentCount,
+ ));
+ }
+ }
}
diff --git a/rust/src/ffi.rs b/rust/src/ffi.rs
index aaa4851e..66e5f70f 100644
--- a/rust/src/ffi.rs
+++ b/rust/src/ffi.rs
@@ -14,6 +14,8 @@
use std::time::{Duration, SystemTime, UNIX_EPOCH};
+pub(crate) const INVALID_REGISTER: u32 = 0xffff_ffff;
+
macro_rules! ffi_wrap {
($n:expr, $b:expr) => {{
use std::panic;
diff --git a/rust/src/function.rs b/rust/src/function.rs
index 6a62946f..669ec267 100644
--- a/rust/src/function.rs
+++ b/rust/src/function.rs
@@ -682,7 +682,7 @@ impl Function {
}
pub fn set_auto_return_value(&self, return_value: impl Into<ReturnValue>) {
- let mut raw_return_value = ReturnValue::into_rust_raw(return_value.into());
+ 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);
}
@@ -706,7 +706,7 @@ impl Function {
}
pub fn set_user_return_value(&self, return_value: impl Into<ReturnValue>) {
- let mut raw_return_value = ReturnValue::into_rust_raw(return_value.into());
+ 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);
}
diff --git a/rust/src/types.rs b/rust/src/types.rs
index a7ec517e..167c8bbd 100644
--- a/rust/src/types.rs
+++ b/rust/src/types.rs
@@ -454,7 +454,7 @@ impl TypeBuilder {
parameters: Vec<FunctionParameter>,
variable_arguments: bool,
) -> Self {
- let mut owned_raw_return_value = ReturnValue::into_rust_raw(return_value.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();
@@ -511,7 +511,7 @@ impl TypeBuilder {
calling_convention: C,
stack_adjust: Conf<i64>,
) -> Self {
- let mut owned_raw_return_value = ReturnValue::into_rust_raw(return_value.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();
@@ -934,7 +934,7 @@ impl Type {
parameters: Vec<FunctionParameter>,
variable_arguments: bool,
) -> Ref<Self> {
- let mut owned_raw_return_value = ReturnValue::into_rust_raw(return_value.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();
@@ -990,7 +990,7 @@ impl Type {
calling_convention: C,
stack_adjust: Conf<i64>,
) -> Ref<Self> {
- let mut owned_raw_return_value = ReturnValue::into_rust_raw(return_value.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();
@@ -1365,9 +1365,9 @@ impl ReturnValue {
owned
}
- pub(crate) fn into_rust_raw(value: Self) -> BNReturnValue {
+ pub(crate) fn into_rust_raw(value: &Self) -> BNReturnValue {
BNReturnValue {
- type_: unsafe { Ref::into_raw(value.ty.contents) }.handle,
+ type_: unsafe { Ref::into_raw(value.ty.contents.clone()) }.handle,
typeConfidence: value.ty.confidence,
defaultLocation: value.location.is_none(),
location: ValueLocation::into_rust_raw(