From ea82d201e7efb2065d0cea459bea0fcda1985762 Mon Sep 17 00:00:00 2001 From: Rusty Wagner Date: Tue, 19 May 2026 20:01:42 -0400 Subject: Refactor calling convention Rust API to allow default implementations Also implements the new calling convention APIs in Rust --- arch/riscv/src/lib.rs | 34 +- plugins/pdb-ng/src/type_parser.rs | 2 +- rust/src/architecture.rs | 20 +- rust/src/calling_convention.rs | 1782 +++++++++++++++++++++++++++++++------ rust/src/ffi.rs | 2 + rust/src/function.rs | 4 +- rust/src/types.rs | 12 +- 7 files changed, 1559 insertions(+), 297 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 AsRef for RiscVELFRelocationHa } struct RiscVCC { + core: CoreCallingConvention, _dis: PhantomData, } impl RiscVCC { - fn new() -> Self { - RiscVCC { _dis: PhantomData } + fn new(core: CoreCallingConvention) -> Self { + RiscVCC { + core, + _dis: PhantomData, + } + } +} + +impl AsRef for RiscVCC { + 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::>::new(), - ); + let cc32 = register_calling_convention(arch32, "default", |core| { + RiscVCC::>::new(core) + }); arch32.set_default_calling_convention(&cc32); - let cc64 = register_calling_convention( - arch64, - "default", - RiscVCC::>::new(), - ); + let cc64 = register_calling_convention(arch64, "default", |core| { + RiscVCC::>::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 { + 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 { match unsafe { BNGetArchitectureStackPointerRegister(self.handle) } { - 0xffff_ffff => None, + INVALID_REGISTER => None, reg => Some(CoreRegister::new(*self, reg.into())?), } } fn link_reg(&self) -> Option { 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`]; see [`register_calling_convention`] for +/// how the handle is provided to implementors. +pub trait CallingConvention: 'static + Sync + Sized + AsRef { + /// Gets the list of registers that are not preserved across a call + /// (caller-saved / volatile registers). fn caller_saved_registers(&self) -> Vec; + + /// Gets the list of registers that a callee must preserve across a call + /// (callee-saved / non-volatile registers). fn callee_saved_registers(&self) -> Vec; + + /// Gets the registers used to pass integer and pointer arguments, in the order they are used. fn int_arg_registers(&self) -> Vec; + + /// Gets the registers used to pass floating point arguments, in the order they are used. fn float_arg_registers(&self) -> Vec; + + /// 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 { 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 { 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; + + /// 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; + + /// Gets the register that holds the floating point return value. fn return_float_reg(&self) -> Option; + /// Gets the register that holds the global pointer, if the calling convention defines one. fn global_pointer_reg(&self) -> Option; + /// Gets the registers that are implicitly given a known value on function entry by this + /// calling convention. fn implicitly_defined_registers(&self) -> Vec; + + /// 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 { + 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 { + 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)], + ) -> Vec { + 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)], + ) -> 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 { + self.as_ref() + .default_register_stack_adjustments(return_value, params) + } } -pub fn register_calling_convention(arch: &A, name: &str, cc: C) -> Ref +/// 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`] 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( + arch: &A, + name: &str, + func: F, +) -> Ref where A: Architecture, C: 'static + CallingConvention, + F: FnOnce(CoreCallingConvention) -> C, { + #[repr(C)] struct CustomCallingConventionContext where C: CallingConvention, { - raw_handle: *mut BNCallingConvention, - cc: C, + cc: MaybeUninit, + } + + unsafe fn from_ctxt<'a, C: CallingConvention>(ctxt: *mut c_void) -> &'a C { + (*(ctxt as *mut CustomCallingConventionContext)) + .cc + .assume_init_ref() + } + + unsafe fn register_list(regs: Vec, 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> { + 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(ctxt: *mut c_void) where C: CallingConvention, { ffi_wrap!("CallingConvention::free", unsafe { - let _ctxt = Box::from_raw(ctxt as *mut CustomCallingConventionContext); + let mut ctxt = Box::from_raw(ctxt as *mut CustomCallingConventionContext); + 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); - 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::(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); - 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::(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); - 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::(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); - 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::(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); - 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::(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); - 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::(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); - - ctxt.cc.arg_registers_shared_index() + from_ctxt::(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); - - ctxt.cc.reserved_stack_space_for_arg_registers() - } + unsafe { from_ctxt::(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); - - ctxt.cc.stack_adjusted_on_return() + from_ctxt::(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); - - ctxt.cc.is_eligible_for_heuristics() + from_ctxt::(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); - - match ctxt.cc.return_int_reg() { - Some(r) => r.0, - _ => 0xffff_ffff, - } + from_ctxt::(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); - - match ctxt.cc.return_hi_int_reg() { - Some(r) => r.0, - _ => 0xffff_ffff, - } + from_ctxt::(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); - - match ctxt.cc.return_float_reg() { - Some(r) => r.0, - _ => 0xffff_ffff, - } + from_ctxt::(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); - - match ctxt.cc.global_pointer_reg() { - Some(r) => r.0, - _ => 0xffff_ffff, - } + from_ctxt::(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); - 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::(ctxt).implicitly_defined_registers(), count) }) } - #[allow(clippy::extra_unused_type_parameters)] extern "C" fn cb_incoming_reg_value( - _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); - 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::(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( - _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); - 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::(ctxt).incoming_flag_value(FlagId(flag), func.as_ref()); + ptr::write(val, value.into()); }) } extern "C" fn cb_incoming_var_for_param( 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); - 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::(ctxt).incoming_variable_for_parameter_variable(&var, func.as_ref()); + ptr::write(param, result.into()); }) } extern "C" fn cb_incoming_param_for_var( 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); - 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::(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::(ctxt).are_argument_registers_used_for_var_args() } + ) + } + + extern "C" fn cb_is_return_type_register_compatible( + ctxt: *mut c_void, + view: *mut BNBinaryView, + ty: *mut BNType, + ) -> bool + where + C: CallingConvention, + { + ffi_wrap!( + "CallingConvention::is_return_type_register_compatible", unsafe { - let ctxt = &*(ctxt as *mut CustomCallingConventionContext); + 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::(ctxt).is_return_type_register_compatible(view.as_ref(), ty) + } + _ => false, + } + } + ) + } - ctxt.cc.are_argument_registers_used_for_var_args() + extern "C" fn cb_indirect_return_value_location(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::(ctxt).indirect_return_value_location().into(), + ); + } + ) + } + + extern "C" fn cb_returned_indirect_return_value_pointer( + ctxt: *mut c_void, + out_var: *mut BNVariable, + ) -> bool + where + C: CallingConvention, + { + ffi_wrap!( + "CallingConvention::returned_indirect_return_value_pointer", + unsafe { + match from_ctxt::(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( + 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::(ctxt).is_argument_type_register_compatible(view.as_ref(), ty) + } + _ => false, + } + } + ) + } + + extern "C" fn cb_is_non_register_argument_indirect( + 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::(ctxt).is_non_register_argument_indirect(view.as_ref(), ty) + } + _ => false, + } + } + ) + } + + extern "C" fn cb_are_stack_arguments_naturally_aligned(ctxt: *mut c_void) -> bool + where + C: CallingConvention, + { + ffi_wrap!( + "CallingConvention::are_stack_arguments_naturally_aligned", + unsafe { from_ctxt::(ctxt).are_stack_arguments_naturally_aligned() } + ) + } + + extern "C" fn cb_are_stack_arguments_pushed_left_to_right(ctxt: *mut c_void) -> bool + where + C: CallingConvention, + { + ffi_wrap!( + "CallingConvention::are_stack_arguments_pushed_left_to_right", + unsafe { from_ctxt::(ctxt).are_stack_arguments_pushed_left_to_right() } + ) + } + + #[allow(clippy::too_many_arguments)] + extern "C" fn cb_get_call_layout( + 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 = 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::(ctxt).call_layout( + view.as_ref(), + &return_value, + ¶ms, + 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( + 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::(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( + 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 = 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::(ctxt).parameter_locations( + view.as_ref(), + return_value.as_ref(), + ¶ms, + 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( + 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 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)>> = 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::(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() + } + ) + } + + 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( + 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)>> = 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::(ctxt).stack_adjustment_for_locations( + view.as_ref(), + return_value.as_ref(), + params, + ) + } else { + 0 + } + } + ) + } + + extern "C" fn cb_get_register_stack_adjustments( + 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 = slice_from_raw_parts(params, param_count) + .iter() + .map(ValueLocation::from_raw) + .collect(); + let adjustments = from_ctxt::(ctxt).register_stack_adjustments( + view.as_ref(), + return_value.as_ref(), + ¶m_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:: { + 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::), - isReturnTypeRegisterCompatible: None, - getIndirectReturnValueLocation: None, - getReturnedIndirectReturnValuePointer: None, - isArgumentTypeRegisterCompatible: None, - isNonRegisterArgumentIndirect: None, - areStackArgumentsNaturallyAligned: None, - areStackArgumentsPushedLeftToRight: None, - - getCallLayout: None, - freeCallLayout: None, - getReturnValueLocation: None, - freeValueLocation: None, - getParameterLocations: None, - freeParameterLocations: None, - getParameterOrderingForVariables: None, - freeVariableList: None, - getStackAdjustmentForLocations: None, - getRegisterStackAdjustments: None, - freeRegisterStackAdjustments: None, + isReturnTypeRegisterCompatible: Some(cb_is_return_type_register_compatible::), + getIndirectReturnValueLocation: Some(cb_indirect_return_value_location::), + getReturnedIndirectReturnValuePointer: Some(cb_returned_indirect_return_value_pointer::), + isArgumentTypeRegisterCompatible: Some(cb_is_argument_type_register_compatible::), + isNonRegisterArgumentIndirect: Some(cb_is_non_register_argument_indirect::), + areStackArgumentsNaturallyAligned: Some(cb_are_stack_arguments_naturally_aligned::), + areStackArgumentsPushedLeftToRight: Some(cb_are_stack_arguments_pushed_left_to_right::), + + getCallLayout: Some(cb_get_call_layout::), + freeCallLayout: Some(cb_free_call_layout), + getReturnValueLocation: Some(cb_get_return_value_location::), + freeValueLocation: Some(cb_free_value_location), + getParameterLocations: Some(cb_get_parameter_locations::), + freeParameterLocations: Some(cb_free_parameter_locations), + getParameterOrderingForVariables: Some(cb_get_parameter_ordering_for_variables::), + freeVariableList: Some(cb_free_variable_list), + getStackAdjustmentForLocations: Some(cb_get_stack_adjustment_for_locations::), + getRegisterStackAdjustments: Some(cb_get_register_stack_adjustments::), + 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,61 +1109,270 @@ 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> { + 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, + 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 = 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::>(); - - unsafe { - BNGetCallLayout( + let raw_layout: BNCallLayout = match Self::raw_permitted_args(permitted_registers) { + Some(permitted) => unsafe { + BNGetDefaultCallLayout( + self.handle, + view.map(|view| view.handle).unwrap_or(ptr::null_mut()), + &raw_return_value, + raw_params.as_ptr(), + raw_params.len(), + permitted.as_ptr(), + permitted.len(), + ) + }, + None => unsafe { + BNGetDefaultCallLayoutDefaultPermittedArgs( + 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) + } + + pub fn default_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 { + 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 { + BNFreeValueLocation(&mut raw_location); + } + result + } + + pub fn default_parameter_locations( + &self, + view: Option<&BinaryView>, + return_value: Option<&ValueLocation>, + params: &[FunctionParameter], + permitted_registers: Option<&[RegisterId]>, + ) -> Vec { + 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 = 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.handle, - &raw_return_value, - raw_params.as_ptr(), + view.map(|view| view.handle).unwrap_or(ptr::null_mut()), + return_value_ptr, + raw_params.as_mut_ptr(), raw_params.len(), - permitted_regs.as_ptr(), - permitted_regs.len(), - ) - } - } else { - unsafe { - BNGetCallLayoutDefaultPermittedArgs( + permitted.as_ptr(), + permitted.len(), + &mut count, + ), + None => BNGetDefaultParameterLocationsDefaultPermittedArgs( self.handle, - view.handle, - &raw_return_value, - raw_params.as_ptr(), + view.map(|view| view.handle).unwrap_or(ptr::null_mut()), + return_value_ptr, + raw_params.as_mut_ptr(), raw_params.len(), - ) + &mut count, + ), } }; - ReturnValue::free_rust_raw(raw_return_value); - CallLayout::from_owned_core_raw(raw_layout) + 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 return_value_location( + pub fn default_parameter_ordering_for_variables( &self, - view: &BinaryView, - return_value: impl Into, - ) -> 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); + params: &[(Variable, Ref)], + ) -> Vec { + let raw_vars: Vec = 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 { - BNFreeValueLocation(&mut raw_location); + BNFreeVariableList(vars_ptr); + } + result + } + + pub fn default_stack_adjustment_for_locations( + &self, + return_value: Option<&ValueLocation>, + params: &[(ValueLocation, Ref)], + ) -> 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 = 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 { + 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 = + 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 } @@ -606,6 +1388,12 @@ impl PartialEq for CoreCallingConvention { } } +impl AsRef 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 { + 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 = 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::>(); + + 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 { + 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 = 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)], + ) -> Vec { + let raw_vars: Vec = 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)], + ) -> 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 = 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 { + 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 = + 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 { + 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, callee_saved_registers: Vec, int_arg_registers: Vec, @@ -886,15 +2013,19 @@ pub struct ConventionBuilder { implicitly_defined_registers: Vec, 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, + returned_indirect_return_value_pointer: Option, } 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 ConventionBuilder { 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 ConventionBuilder { 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 { 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, + }) + } +} + +/// 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 for RegisteredConvention { + fn as_ref(&self) -> &CoreCallingConvention { + &self.core } } -impl CallingConvention for ConventionBuilder { +impl CallingConvention for RegisteredConvention { fn caller_saved_registers(&self) -> Vec { - self.caller_saved_registers.clone() + self.config.caller_saved_registers.clone() } fn callee_saved_registers(&self) -> Vec { - self.callee_saved_registers.clone() + self.config.callee_saved_registers.clone() } fn int_arg_registers(&self) -> Vec { - self.int_arg_registers.clone() + self.config.int_arg_registers.clone() } fn float_arg_registers(&self) -> Vec { - self.float_arg_registers.clone() + self.config.float_arg_registers.clone() } fn required_argument_registers(&self) -> Vec { - self.required_argument_registers.clone() + self.config.required_argument_registers.clone() } fn required_clobbered_registers(&self) -> Vec { - 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 { - self.return_int_reg + self.config.return_int_reg } fn return_hi_int_reg(&self) -> Option { - self.return_hi_int_reg + self.config.return_hi_int_reg } fn return_float_reg(&self) -> Option { - self.return_float_reg + self.config.return_float_reg } fn global_pointer_reg(&self) -> Option { - self.global_pointer_reg + self.config.global_pointer_reg } fn implicitly_defined_registers(&self) -> Vec { - 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 } -} -unsafe impl Send for ConventionBuilder {} -unsafe impl Sync for ConventionBuilder {} + 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(), + } + } + + fn returned_indirect_return_value_pointer(&self) -> Option { + self.config.returned_indirect_return_value_pointer + } +} -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Default)] pub struct CallLayout { pub parameters: Vec, pub return_value: Option, @@ -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) { - 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) { - 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, 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, ) -> 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, variable_arguments: bool, ) -> Ref { - 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, ) -> Ref { - 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( -- cgit v1.3.1