// Copyright 2021-2026 Vector 35 Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //! Contains and provides information about different systems' calling conventions to analysis. use std::borrow::Borrow; use std::collections::BTreeMap; use std::ffi::c_void; use std::fmt::{Debug, Formatter}; use std::hash::{Hash, Hasher}; use std::marker::PhantomData; use binaryninjacore_sys::*; use crate::architecture::{ Architecture, ArchitectureExt, CoreArchitecture, CoreRegister, Register, RegisterId, }; use crate::binary_view::BinaryView; use crate::ffi::slice_from_raw_parts; use crate::rc::{CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable}; use crate::string::*; use crate::types::{FunctionParameter, ReturnValue, ValueLocation}; // TODO // force valid registers once Arch has _from_id methods // CallingConvention impl // dataflow callbacks pub trait CallingConvention: Sync { fn caller_saved_registers(&self) -> Vec; fn callee_saved_registers(&self) -> Vec; fn int_arg_registers(&self) -> Vec; fn float_arg_registers(&self) -> Vec; fn required_argument_registers(&self) -> Vec { Vec::new() } fn required_clobbered_registers(&self) -> Vec { Vec::new() } fn arg_registers_shared_index(&self) -> bool; fn reserved_stack_space_for_arg_registers(&self) -> bool; fn stack_adjusted_on_return(&self) -> bool; fn is_eligible_for_heuristics(&self) -> bool; fn return_int_reg(&self) -> Option; fn return_hi_int_reg(&self) -> Option; fn return_float_reg(&self) -> Option; fn global_pointer_reg(&self) -> Option; fn implicitly_defined_registers(&self) -> Vec; fn are_argument_registers_used_for_var_args(&self) -> bool; } pub fn register_calling_convention(arch: &A, name: &str, cc: C) -> Ref where A: Architecture, C: 'static + CallingConvention, { struct CustomCallingConventionContext where C: CallingConvention, { raw_handle: *mut BNCallingConvention, cc: C, } // 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); }) } extern "C" fn cb_free_register_list(_ctxt: *mut c_void, regs: *mut u32, count: usize) { ffi_wrap!("CallingConvention::free_register_list", unsafe { if regs.is_null() { return; } let regs_ptr = std::ptr::slice_from_raw_parts_mut(regs, count); let _regs = Box::from_raw(regs_ptr); }) } extern "C" fn cb_caller_saved(ctxt: *mut c_void, count: *mut usize) -> *mut u32 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 }) } extern "C" fn cb_callee_saved(ctxt: *mut c_void, count: *mut usize) -> *mut u32 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 }) } extern "C" fn cb_int_args(ctxt: *mut c_void, count: *mut usize) -> *mut u32 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 }) } extern "C" fn cb_float_args(ctxt: *mut c_void, count: *mut usize) -> *mut u32 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 }) } extern "C" fn cb_required_argument_registers( ctxt: *mut c_void, count: *mut usize, ) -> *mut u32 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 }) } extern "C" fn cb_required_clobbered_registers( ctxt: *mut c_void, count: *mut usize, ) -> *mut u32 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 }) } extern "C" fn cb_arg_shared_index(ctxt: *mut c_void) -> bool where C: CallingConvention, { ffi_wrap!("CallingConvention::arg_registers_shared_index", unsafe { let ctxt = &*(ctxt as *mut CustomCallingConventionContext); ctxt.cc.arg_registers_shared_index() }) } extern "C" fn cb_stack_reserved_arg_regs(ctxt: *mut c_void) -> bool where C: CallingConvention, { ffi_wrap!( "CallingConvention::reserved_stack_space_for_arg_registers", unsafe { let ctxt = &*(ctxt as *mut CustomCallingConventionContext); ctxt.cc.reserved_stack_space_for_arg_registers() } ) } extern "C" fn cb_stack_adjusted_on_return(ctxt: *mut c_void) -> bool where C: CallingConvention, { ffi_wrap!("CallingConvention::stack_adjusted_on_return", unsafe { let ctxt = &*(ctxt as *mut CustomCallingConventionContext); ctxt.cc.stack_adjusted_on_return() }) } extern "C" fn cb_is_eligible_for_heuristics(ctxt: *mut c_void) -> bool where C: CallingConvention, { ffi_wrap!("CallingConvention::is_eligible_for_heuristics", unsafe { let ctxt = &*(ctxt as *mut CustomCallingConventionContext); ctxt.cc.is_eligible_for_heuristics() }) } extern "C" fn cb_return_int_reg(ctxt: *mut c_void) -> u32 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, } }) } extern "C" fn cb_return_hi_int_reg(ctxt: *mut c_void) -> u32 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, } }) } extern "C" fn cb_return_float_reg(ctxt: *mut c_void) -> u32 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, } }) } extern "C" fn cb_global_pointer_reg(ctxt: *mut c_void) -> u32 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, } }) } extern "C" fn cb_implicitly_defined_registers( ctxt: *mut c_void, count: *mut usize, ) -> *mut u32 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 }) } #[allow(clippy::extra_unused_type_parameters)] extern "C" fn cb_incoming_reg_value( _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; }) } #[allow(clippy::extra_unused_type_parameters)] extern "C" fn cb_incoming_flag_value( _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; }) } extern "C" fn cb_incoming_var_for_param( ctxt: *mut c_void, var: *const BNVariable, _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), ); }) } extern "C" fn cb_incoming_param_for_var( ctxt: *mut c_void, var: *const BNVariable, _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), ); }) } extern "C" fn cb_are_argument_registers_used_for_var_args(ctxt: *mut c_void) -> bool where C: CallingConvention, { ffi_wrap!( "CallingConvention::are_argument_registers_used_for_var_args", unsafe { let ctxt = &*(ctxt as *mut CustomCallingConventionContext); ctxt.cc.are_argument_registers_used_for_var_args() } ) } let name = name.to_cstr(); let raw = Box::into_raw(Box::new(CustomCallingConventionContext { raw_handle: std::ptr::null_mut(), cc, })); let mut cc = BNCustomCallingConvention { context: raw as *mut _, freeObject: Some(cb_free::), getCallerSavedRegisters: Some(cb_caller_saved::), getCalleeSavedRegisters: Some(cb_callee_saved::), getIntegerArgumentRegisters: Some(cb_int_args::), getFloatArgumentRegisters: Some(cb_float_args::), getRequiredArgumentRegisters: Some(cb_required_argument_registers::), getRequiredClobberedRegisters: Some(cb_required_clobbered_registers::), freeRegisterList: Some(cb_free_register_list), areArgumentRegistersSharedIndex: Some(cb_arg_shared_index::), isStackReservedForArgumentRegisters: Some(cb_stack_reserved_arg_regs::), isStackAdjustedOnReturn: Some(cb_stack_adjusted_on_return::), isEligibleForHeuristics: Some(cb_is_eligible_for_heuristics::), getIntegerReturnValueRegister: Some(cb_return_int_reg::), getHighIntegerReturnValueRegister: Some(cb_return_hi_int_reg::), getFloatReturnValueRegister: Some(cb_return_float_reg::), getGlobalPointerRegister: Some(cb_global_pointer_reg::), getImplicitlyDefinedRegisters: Some(cb_implicitly_defined_registers::), getIncomingRegisterValue: Some(cb_incoming_reg_value::), getIncomingFlagValue: Some(cb_incoming_flag_value::), getIncomingVariableForParameterVariable: Some(cb_incoming_var_for_param::), getParameterVariableForIncomingVariable: Some(cb_incoming_param_for_var::), 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, }; unsafe { let cc_name = name.as_ptr(); let result = BNCreateCallingConvention(arch.as_ref().handle, cc_name, &mut cc); assert!(!result.is_null()); (*raw).raw_handle = result; BNRegisterCallingConvention(arch.as_ref().handle, result); Ref::new(CoreCallingConvention { handle: result, arch_handle: arch.as_ref().handle(), }) } } pub struct CoreCallingConvention { pub(crate) handle: *mut BNCallingConvention, pub(crate) arch_handle: CoreArchitecture, } impl CoreCallingConvention { pub(crate) unsafe fn from_raw( handle: *mut BNCallingConvention, arch: CoreArchitecture, ) -> Self { CoreCallingConvention { handle, arch_handle: arch, } } pub(crate) unsafe fn ref_from_raw( handle: *mut BNCallingConvention, arch: CoreArchitecture, ) -> Ref { Ref::new(CoreCallingConvention { handle, arch_handle: arch, }) } pub fn name(&self) -> String { unsafe { BnString::into_string(BNGetCallingConventionName(self.handle)) } } pub fn call_layout( &self, view: &BinaryView, return_value: impl Into, params: &[FunctionParameter], permitted_registers: Option<&[CoreRegister]>, ) -> CallLayout { let raw_return_value = ReturnValue::into_rust_raw(return_value.into()); 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( self.handle, view.handle, &raw_return_value, raw_params.as_ptr(), raw_params.len(), permitted_regs.as_ptr(), permitted_regs.len(), ) } } else { unsafe { BNGetCallLayoutDefaultPermittedArgs( self.handle, view.handle, &raw_return_value, raw_params.as_ptr(), raw_params.len(), ) } }; ReturnValue::free_rust_raw(raw_return_value); CallLayout::from_owned_core_raw(raw_layout) } pub fn return_value_location( &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); unsafe { BNFreeValueLocation(&mut raw_location); } result } } unsafe impl Send for CoreCallingConvention {} unsafe impl Sync for CoreCallingConvention {} impl Eq for CoreCallingConvention {} impl PartialEq for CoreCallingConvention { fn eq(&self, rhs: &Self) -> bool { self.handle == rhs.handle } } impl Debug for CoreCallingConvention { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.debug_struct("CoreCallingConvention") .field("name", &self.name()) .field("caller_saved_registers", &self.caller_saved_registers()) .field("callee_saved_registers", &self.callee_saved_registers()) .field("int_arg_registers", &self.int_arg_registers()) .field("float_arg_registers", &self.float_arg_registers()) .field( "required_argument_registers", &self.required_argument_registers(), ) .field( "required_clobbered_registers", &self.required_clobbered_registers(), ) .field( "arg_registers_shared_index", &self.arg_registers_shared_index(), ) .field( "reserved_stack_space_for_arg_registers", &self.reserved_stack_space_for_arg_registers(), ) .field("stack_adjusted_on_return", &self.stack_adjusted_on_return()) .field( "is_eligible_for_heuristics", &self.is_eligible_for_heuristics(), ) .field("return_int_reg", &self.return_int_reg()) .field("return_hi_int_reg", &self.return_hi_int_reg()) .field("return_float_reg", &self.return_float_reg()) .field("global_pointer_reg", &self.global_pointer_reg()) .field( "implicitly_defined_registers", &self.implicitly_defined_registers(), ) .field( "are_argument_registers_used_for_var_args", &self.are_argument_registers_used_for_var_args(), ) .finish() } } impl Hash for CoreCallingConvention { fn hash(&self, state: &mut H) { self.handle.hash(state); } } impl CallingConvention for CoreCallingConvention { fn caller_saved_registers(&self) -> Vec { unsafe { let mut count = 0; let regs_ptr = BNGetCallerSavedRegisters(self.handle, &mut count); let regs: Vec = std::slice::from_raw_parts(regs_ptr, count) .iter() .copied() .map(RegisterId::from) .collect(); BNFreeRegisterList(regs_ptr); regs } } fn callee_saved_registers(&self) -> Vec { unsafe { let mut count = 0; let regs_ptr = BNGetCalleeSavedRegisters(self.handle, &mut count); let regs: Vec = std::slice::from_raw_parts(regs_ptr, count) .iter() .copied() .map(RegisterId::from) .collect(); BNFreeRegisterList(regs_ptr); regs } } fn int_arg_registers(&self) -> Vec { unsafe { let mut count = 0; let regs_ptr = BNGetIntegerArgumentRegisters(self.handle, &mut count); let regs: Vec = std::slice::from_raw_parts(regs_ptr, count) .iter() .copied() .map(RegisterId::from) .collect(); BNFreeRegisterList(regs_ptr); regs } } fn float_arg_registers(&self) -> Vec { unsafe { let mut count = 0; let regs_ptr = BNGetFloatArgumentRegisters(self.handle, &mut count); let regs: Vec = std::slice::from_raw_parts(regs_ptr, count) .iter() .copied() .map(RegisterId::from) .collect(); BNFreeRegisterList(regs_ptr); regs } } fn required_argument_registers(&self) -> Vec { unsafe { let mut count = 0; let regs_ptr = BNGetRequiredArgumentRegisters(self.handle, &mut count); let regs: Vec = std::slice::from_raw_parts(regs_ptr, count) .iter() .copied() .map(RegisterId::from) .collect(); BNFreeRegisterList(regs_ptr); regs } } fn required_clobbered_registers(&self) -> Vec { unsafe { let mut count = 0; let regs_ptr = BNGetRequiredClobberedRegisters(self.handle, &mut count); let regs: Vec = std::slice::from_raw_parts(regs_ptr, count) .iter() .copied() .map(RegisterId::from) .collect(); BNFreeRegisterList(regs_ptr); regs } } fn arg_registers_shared_index(&self) -> bool { unsafe { BNAreArgumentRegistersSharedIndex(self.handle) } } fn reserved_stack_space_for_arg_registers(&self) -> bool { unsafe { BNIsStackReservedForArgumentRegisters(self.handle) } } fn stack_adjusted_on_return(&self) -> bool { unsafe { BNIsStackAdjustedOnReturn(self.handle) } } fn is_eligible_for_heuristics(&self) -> bool { unsafe { BNIsEligibleForHeuristics(self.handle) } } fn return_int_reg(&self) -> Option { match unsafe { BNGetIntegerReturnValueRegister(self.handle) } { id if id < 0x8000_0000 => self .arch_handle .borrow() .register_from_id(RegisterId(id)) .map(|r| r.id()), _ => None, } } fn return_hi_int_reg(&self) -> Option { match unsafe { BNGetHighIntegerReturnValueRegister(self.handle) } { id if id < 0x8000_0000 => self .arch_handle .borrow() .register_from_id(RegisterId(id)) .map(|r| r.id()), _ => None, } } fn return_float_reg(&self) -> Option { match unsafe { BNGetFloatReturnValueRegister(self.handle) } { id if id < 0x8000_0000 => self .arch_handle .borrow() .register_from_id(RegisterId(id)) .map(|r| r.id()), _ => None, } } fn global_pointer_reg(&self) -> Option { match unsafe { BNGetGlobalPointerRegister(self.handle) } { id if id < 0x8000_0000 => self .arch_handle .borrow() .register_from_id(RegisterId(id)) .map(|r| r.id()), _ => None, } } fn implicitly_defined_registers(&self) -> Vec { unsafe { let mut count = 0; let regs_ptr = BNGetImplicitlyDefinedRegisters(self.handle, &mut count); let regs: Vec = std::slice::from_raw_parts(regs_ptr, count) .iter() .copied() .map(RegisterId::from) .collect(); BNFreeRegisterList(regs_ptr); regs } } fn are_argument_registers_used_for_var_args(&self) -> bool { unsafe { BNAreArgumentRegistersUsedForVarArgs(self.handle) } } } impl ToOwned for CoreCallingConvention { type Owned = Ref; fn to_owned(&self) -> Self::Owned { unsafe { RefCountable::inc_ref(self) } } } unsafe impl RefCountable for CoreCallingConvention { unsafe fn inc_ref(handle: &Self) -> Ref { Ref::new(Self { handle: BNNewCallingConventionReference(handle.handle), arch_handle: handle.arch_handle, }) } unsafe fn dec_ref(handle: &Self) { BNFreeCallingConvention(handle.handle); } } impl CoreArrayProvider for CoreCallingConvention { type Raw = *mut BNCallingConvention; type Context = CoreArchitecture; type Wrapped<'a> = Guard<'a, CoreCallingConvention>; } unsafe impl CoreArrayProviderInner for CoreCallingConvention { unsafe fn free(raw: *mut *mut BNCallingConvention, count: usize, _content: &Self::Context) { BNFreeCallingConventionList(raw, count); } unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> { Guard::new( CoreCallingConvention { handle: *raw, arch_handle: *context, }, context, ) } } pub struct ConventionBuilder { caller_saved_registers: Vec, callee_saved_registers: Vec, int_arg_registers: Vec, float_arg_registers: Vec, required_argument_registers: Vec, required_clobbered_registers: Vec, arg_registers_shared_index: bool, reserved_stack_space_for_arg_registers: bool, stack_adjusted_on_return: bool, is_eligible_for_heuristics: bool, return_int_reg: Option, return_hi_int_reg: Option, return_float_reg: Option, global_pointer_reg: Option, implicitly_defined_registers: Vec, are_argument_registers_used_for_var_args: bool, arch_handle: A::Handle, _arch: PhantomData<*const A>, } macro_rules! bool_arg { ($name:ident) => { pub fn $name(mut self, val: bool) -> Self { self.$name = val; self } }; } macro_rules! reg_list { ($name:ident) => { pub fn $name(mut self, regs: &[&str]) -> Self { { // FIXME NLL let arch = self.arch_handle.borrow(); let arch_regs = regs .iter() .filter_map(|&r| arch.register_by_name(r)) .map(|r| r.id()); self.$name = arch_regs.collect(); } self } }; } macro_rules! reg { ($name:ident) => { pub fn $name(mut self, reg: &str) -> Self { { // FIXME NLL let arch = self.arch_handle.borrow(); self.$name = arch.register_by_name(reg).map(|r| r.id()); } self } }; } 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(), 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, global_pointer_reg: None, implicitly_defined_registers: Vec::new(), are_argument_registers_used_for_var_args: false, arch_handle: arch.handle(), _arch: PhantomData, } } reg_list!(caller_saved_registers); reg_list!(callee_saved_registers); reg_list!(int_arg_registers); reg_list!(float_arg_registers); reg_list!(required_argument_registers); reg_list!(required_clobbered_registers); bool_arg!(arg_registers_shared_index); bool_arg!(reserved_stack_space_for_arg_registers); bool_arg!(stack_adjusted_on_return); bool_arg!(is_eligible_for_heuristics); reg!(return_int_reg); reg!(return_hi_int_reg); reg!(return_float_reg); reg!(global_pointer_reg); reg_list!(implicitly_defined_registers); bool_arg!(are_argument_registers_used_for_var_args); pub fn register(self, name: &str) -> Ref { let arch = self.arch_handle.clone(); register_calling_convention(arch.borrow(), name, self) } } impl CallingConvention for ConventionBuilder { fn caller_saved_registers(&self) -> Vec { self.caller_saved_registers.clone() } fn callee_saved_registers(&self) -> Vec { self.callee_saved_registers.clone() } fn int_arg_registers(&self) -> Vec { self.int_arg_registers.clone() } fn float_arg_registers(&self) -> Vec { self.float_arg_registers.clone() } fn required_argument_registers(&self) -> Vec { self.required_argument_registers.clone() } fn required_clobbered_registers(&self) -> Vec { self.required_clobbered_registers.clone() } fn arg_registers_shared_index(&self) -> bool { self.arg_registers_shared_index } fn reserved_stack_space_for_arg_registers(&self) -> bool { self.reserved_stack_space_for_arg_registers } fn stack_adjusted_on_return(&self) -> bool { self.stack_adjusted_on_return } fn is_eligible_for_heuristics(&self) -> bool { self.is_eligible_for_heuristics } fn return_int_reg(&self) -> Option { self.return_int_reg } fn return_hi_int_reg(&self) -> Option { self.return_hi_int_reg } fn return_float_reg(&self) -> Option { self.return_float_reg } fn global_pointer_reg(&self) -> Option { self.global_pointer_reg } fn implicitly_defined_registers(&self) -> Vec { self.implicitly_defined_registers.clone() } fn are_argument_registers_used_for_var_args(&self) -> bool { self.are_argument_registers_used_for_var_args } } unsafe impl Send for ConventionBuilder {} unsafe impl Sync for ConventionBuilder {} #[derive(Debug, Clone, PartialEq, Eq)] pub struct CallLayout { pub parameters: Vec, pub return_value: Option, pub stack_adjustment: i64, pub register_stack_adjustments: BTreeMap, } impl CallLayout { pub(crate) fn from_raw(value: &BNCallLayout) -> Self { let raw_params = unsafe { slice_from_raw_parts(value.parameters, value.parameterCount) }; let parameters = raw_params.iter().map(ValueLocation::from_raw).collect(); let return_value = if value.returnValueValid { Some(ValueLocation::from_raw(&value.returnValue)) } else { None }; let raw_regs = unsafe { slice_from_raw_parts( value.registerStackAdjustmentRegisters, value.registerStackAdjustmentCount, ) }; let raw_amounts = unsafe { slice_from_raw_parts( value.registerStackAdjustmentAmounts, value.registerStackAdjustmentCount, ) }; let mut register_stack_adjustments = BTreeMap::new(); for i in 0..value.registerStackAdjustmentCount { register_stack_adjustments.insert(RegisterId(raw_regs[i]), raw_amounts[i]); } Self { parameters, return_value, stack_adjustment: value.stackAdjustment, register_stack_adjustments, } } /// Take ownership over an "owned" **core allocated** value. Do not call this for a rust allocated value. pub(crate) fn from_owned_core_raw(mut value: BNCallLayout) -> Self { let owned = Self::from_raw(&value); Self::free_core_raw(&mut value); owned } /// Free a CORE ALLOCATED value. Do not use this with [Self::into_rust_raw] values. pub(crate) fn free_core_raw(value: &mut BNCallLayout) { unsafe { BNFreeCallLayout(value) } } }