summaryrefslogtreecommitdiff
path: root/python/callingconvention.py
diff options
context:
space:
mode:
authorRusty Wagner <rusty.wagner@gmail.com>2025-12-23 13:12:02 -0700
committerRusty Wagner <rusty.wagner@gmail.com>2026-05-22 16:30:56 -0400
commit8d621c51b2797fda7b1dc22243dde611cfc04f68 (patch)
treeba5e6a90e644d21d13e75dabcbd0cc747444443a /python/callingconvention.py
parent08e34ac325743085911f96b62c81d9a1f2127806 (diff)
Refactor calling conventions to support correct representation of structures
Diffstat (limited to 'python/callingconvention.py')
-rw-r--r--python/callingconvention.py968
1 files changed, 858 insertions, 110 deletions
diff --git a/python/callingconvention.py b/python/callingconvention.py
index 94aeb699..4f17d1cf 100644
--- a/python/callingconvention.py
+++ b/python/callingconvention.py
@@ -20,7 +20,8 @@
import traceback
import ctypes
-from typing import Optional, Union
+from typing import Optional, Union, List, Dict, Tuple
+from dataclasses import dataclass
# Binary Ninja components
from . import _binaryninjacore as core
@@ -28,12 +29,62 @@ from .log import log_error_for_exception
from . import variable
from . import function
from . import architecture
+from . import types
+from . import binaryview
FunctionOrILFunction = Union["binaryninja.function.Function", "binaryninja.lowlevelil.LowLevelILFunction",
"binaryninja.mediumlevelil.MediumLevelILFunction",
"binaryninja.highlevelil.HighLevelILFunction"]
+@dataclass
+class CallLayout:
+ parameters: List['types.ValueLocation']
+ return_value: Optional['types.ValueLocation']
+ stack_adjustment: int
+ reg_stack_adjustments: Dict['architecture.RegisterIndex', int]
+
+ @staticmethod
+ def _from_core_struct(struct: core.BNCallLayout, func: Optional['function.Function'] = None) -> 'CallLayout':
+ params = []
+ if func is None:
+ arch = None
+ else:
+ arch = func.arch
+ for i in range(struct.parameterCount):
+ params.append(types.ValueLocation._from_core_struct(struct.parameters[i], arch))
+ if struct.returnValueValid:
+ return_value = types.ValueLocation._from_core_struct(struct.returnValue, arch)
+ else:
+ return_value = None
+ stack_adjust = struct.stackAdjustment
+ reg_stack_adjust = dict()
+ for i in range(struct.registerStackAdjustmentCount):
+ reg = architecture.RegisterIndex(struct.registerStackAdjustmentRegisters[i])
+ reg_stack_adjust[reg] = struct.registerStackAdjustmentAmounts[i]
+ return CallLayout(params, return_value, stack_adjust, reg_stack_adjust)
+
+ def _to_core_struct(self):
+ struct = core.BNCallLayout()
+ struct.parameters = (core.BNValueLocation * len(self.parameters))()
+ struct.parameterCount = len(self.parameters)
+ for i in range(len(self.parameters)):
+ struct.parameters[i] = self.parameters[i]._to_core_struct()
+ if self.return_value is None:
+ struct.returnValueValid = False
+ else:
+ struct.returnValue = self.return_value._to_core_struct()
+ struct.returnValueValid = True
+ struct.stackAdjustment = self.stack_adjustment
+ struct.registerStackAdjustmentRegisters = (ctypes.c_uint * len(self.reg_stack_adjustments))()
+ struct.registerStackAdjustmentAmounts = (ctypes.c_int * len(self.reg_stack_adjustments))()
+ struct.registerStackAdjustmentCount = len(self.reg_stack_adjustments)
+ for i, (reg, amount) in enumerate(self.reg_stack_adjustments.items()):
+ struct.registerStackAdjustmentRegisters[i] = reg
+ struct.registerStackAdjustmentAmounts[i] = amount
+ return struct
+
+
class CallingConvention:
name = None
caller_saved_regs = []
@@ -52,8 +103,13 @@ class CallingConvention:
float_return_reg = None
global_pointer_reg = None
implicitly_defined_regs = []
+ stack_args_naturally_aligned = False
_registered_calling_conventions = []
+ _pending_value_locations = {}
+ _pending_value_location_lists = {}
+ _pending_reg_stack_adjustment_reg_lists = {}
+ _pending_reg_stack_adjustment_amount_lists = {}
def __init__(
self, arch: Optional['architecture.Architecture'] = None, name: Optional[str] = None, handle=None,
@@ -117,111 +173,43 @@ class CallingConvention:
self._cb.getParameterVariableForIncomingVariable = self._cb.getParameterVariableForIncomingVariable.__class__(
self._get_parameter_var_for_incoming_var
)
+ self._cb.isReturnTypeRegisterCompatible = self._cb.isReturnTypeRegisterCompatible.__class__(
+ self._is_return_type_reg_compatible
+ )
+ self._cb.getIndirectReturnValueLocation = self._cb.getIndirectReturnValueLocation.__class__(
+ self._get_indirect_return_value_location
+ )
+ self._cb.getReturnedIndirectReturnValuePointer = self._cb.getReturnedIndirectReturnValuePointer.__class__(
+ self._get_returned_indirect_return_value_pointer
+ )
+ self._cb.isArgumentTypeRegisterCompatible = self._cb.isArgumentTypeRegisterCompatible.__class__(
+ self._is_arg_type_reg_compatible
+ )
+ self._cb.isNonRegisterArgumentIndirect = self._cb.isNonRegisterArgumentIndirect.__class__(
+ self._is_non_reg_arg_indirect
+ )
+ self._cb.areStackArgumentsNaturallyAligned = self._cb.areStackArgumentsNaturallyAligned.__class__(
+ self._are_stack_args_naturally_aligned
+ )
+ self._cb.getCallLayout = self._cb.getCallLayout.__class__(self._get_call_layout)
+ self._cb.freeCallLayout = self._cb.freeCallLayout.__class__(self._free_call_layout)
+ self._cb.getReturnValueLocation = self._cb.getReturnValueLocation.__class__(self._get_return_value_location)
+ self._cb.freeValueLocation = self._cb.freeValueLocation.__class__(self._free_value_location)
+ self._cb.getParameterLocations = self._cb.getParameterLocations.__class__(self._get_parameter_locations)
+ self._cb.freeParameterLocations = self._cb.freeParameterLocations.__class__(self._free_parameter_locations)
+ self._cb.getStackAdjustmentForLocations = self._cb.getStackAdjustmentForLocations.__class__(
+ self._get_stack_adjustment_for_locations
+ )
+ self._cb.getRegisterStackAdjustments = self._cb.getRegisterStackAdjustments.__class__(
+ self._get_register_stack_adjustments
+ )
+ self._cb.freeRegisterStackAdjustments = self._cb.freeRegisterStackAdjustments.__class__(
+ self._free_register_stack_adjustments
+ )
_handle = core.BNCreateCallingConvention(arch.handle, name, self._cb)
self.__class__._registered_calling_conventions.append(self)
else:
_handle = handle
- self.arch = architecture.CoreArchitecture._from_cache(core.BNGetCallingConventionArchitecture(_handle))
- self.__dict__["name"] = core.BNGetCallingConventionName(_handle)
- self.__dict__["arg_regs_share_index"] = core.BNAreArgumentRegistersSharedIndex(_handle)
- self.__dict__["arg_regs_for_varargs"] = core.BNAreArgumentRegistersUsedForVarArgs(_handle)
- self.__dict__["stack_reserved_for_arg_regs"] = core.BNIsStackReservedForArgumentRegisters(_handle)
- self.__dict__["stack_adjusted_on_return"] = core.BNIsStackAdjustedOnReturn(_handle)
- self.__dict__["eligible_for_heuristics"] = core.BNIsEligibleForHeuristics(_handle)
-
- count = ctypes.c_ulonglong()
- regs = core.BNGetCallerSavedRegisters(_handle, count)
- assert regs is not None, "core.BNGetCallerSavedRegisters returned None"
- result = []
- arch = self.arch
- for i in range(0, count.value):
- result.append(arch.get_reg_name(regs[i]))
- core.BNFreeRegisterList(regs)
- self.__dict__["caller_saved_regs"] = result
-
- count = ctypes.c_ulonglong()
- regs = core.BNGetCalleeSavedRegisters(_handle, count)
- assert regs is not None, "core.BNGetCalleeSavedRegisters returned None"
- result = []
- arch = self.arch
- for i in range(0, count.value):
- result.append(arch.get_reg_name(regs[i]))
- core.BNFreeRegisterList(regs)
- self.__dict__["callee_saved_regs"] = result
-
- count = ctypes.c_ulonglong()
- regs = core.BNGetIntegerArgumentRegisters(_handle, count)
- assert regs is not None, "core.BNGetIntegerArgumentRegisters returned None"
- result = []
- arch = self.arch
- for i in range(0, count.value):
- result.append(arch.get_reg_name(regs[i]))
- core.BNFreeRegisterList(regs)
- self.__dict__["int_arg_regs"] = result
-
- count = ctypes.c_ulonglong()
- regs = core.BNGetFloatArgumentRegisters(_handle, count)
- assert regs is not None, "core.BNGetFloatArgumentRegisters returned None"
- result = []
- arch = self.arch
- for i in range(0, count.value):
- result.append(arch.get_reg_name(regs[i]))
- core.BNFreeRegisterList(regs)
- self.__dict__["float_arg_regs"] = result
-
- count = ctypes.c_ulonglong()
- regs = core.BNGetRequiredArgumentRegisters(handle, count)
- assert regs is not None, "core.BNGetRequiredArgumentRegisters returned None"
- result = []
- arch = self.arch
- for i in range(0, count.value):
- result.append(arch.get_reg_name(regs[i]))
- core.BNFreeRegisterList(regs)
- self.__dict__["required_arg_regs"] = result
-
- count = ctypes.c_ulonglong()
- regs = core.BNGetRequiredClobberedRegisters(handle, count)
- assert regs is not None, "core.BNGetRequiredClobberedRegisters returned None"
- result = []
- arch = self.arch
- for i in range(0, count.value):
- result.append(arch.get_reg_name(regs[i]))
- core.BNFreeRegisterList(regs)
- self.__dict__["required_clobbered_regs"] = result
-
- reg = core.BNGetIntegerReturnValueRegister(_handle)
- if reg == 0xffffffff:
- self.__dict__["int_return_reg"] = None
- else:
- self.__dict__["int_return_reg"] = self.arch.get_reg_name(reg)
-
- reg = core.BNGetHighIntegerReturnValueRegister(_handle)
- if reg == 0xffffffff:
- self.__dict__["high_int_return_reg"] = None
- else:
- self.__dict__["high_int_return_reg"] = self.arch.get_reg_name(reg)
-
- reg = core.BNGetFloatReturnValueRegister(_handle)
- if reg == 0xffffffff:
- self.__dict__["float_return_reg"] = None
- else:
- self.__dict__["float_return_reg"] = self.arch.get_reg_name(reg)
-
- reg = core.BNGetGlobalPointerRegister(_handle)
- if reg == 0xffffffff:
- self.__dict__["global_pointer_reg"] = None
- else:
- self.__dict__["global_pointer_reg"] = self.arch.get_reg_name(reg)
-
- count = ctypes.c_ulonglong()
- regs = core.BNGetImplicitlyDefinedRegisters(_handle, count)
- assert regs is not None, "core.BNGetImplicitlyDefinedRegisters returned None"
- result = []
- arch = self.arch
- for i in range(0, count.value):
- result.append(arch.get_reg_name(regs[i]))
- core.BNFreeRegisterList(regs)
- self.__dict__["implicitly_defined_regs"] = result
assert _handle is not None
self.handle = _handle
self.confidence = confidence
@@ -492,9 +480,309 @@ class CallingConvention:
result[0].index = in_var[0].index
result[0].storage = in_var[0].storage
+ def _is_return_type_reg_compatible(self, ctxt, view, type):
+ try:
+ if type:
+ if view:
+ view_obj = binaryview.BinaryView(handle=core.BNNewViewReference(view))
+ else:
+ view_obj = None
+ type_obj = types.Type.create(handle=core.BNNewTypeReference(type))
+ return self.is_return_type_reg_compatible(view_obj, type_obj)
+ else:
+ return False
+ except:
+ log_error_for_exception("Unhandled Python exception in CallingConvention._is_return_type_reg_compatible")
+ return False
+
+ def _get_indirect_return_value_location(self, ctxt, out_var):
+ try:
+ out_var[0] = self.get_indirect_return_value_location().to_BNVariable()
+ except:
+ log_error_for_exception(
+ "Unhandled Python exception in CallingConvention._get_indirect_return_value_location")
+
+ def _get_returned_indirect_return_value_pointer(self, ctxt, out_var):
+ try:
+ result = self.get_returned_indirect_return_value_pointer()
+ if result is None:
+ return False
+ out_var[0] = result.to_BNVariable()
+ return True
+ except:
+ log_error_for_exception(
+ "Unhandled Python exception in CallingConvention._get_returned_indirect_return_value_pointer")
+ return False
+
+ def _is_arg_type_reg_compatible(self, ctxt, view, type):
+ try:
+ if type:
+ if view:
+ view_obj = binaryview.BinaryView(handle=core.BNNewViewReference(view))
+ else:
+ view_obj = None
+ type_obj = types.Type.create(handle=core.BNNewTypeReference(type))
+ return self.is_arg_type_reg_compatible(view_obj, type_obj)
+ else:
+ return False
+ except:
+ log_error_for_exception("Unhandled Python exception in CallingConvention._is_arg_type_reg_compatible")
+ return False
+
+ def _is_non_reg_arg_indirect(self, ctxt, view, type):
+ try:
+ if view:
+ view_obj = binaryview.BinaryView(handle=core.BNNewViewReference(view))
+ else:
+ view_obj = None
+ if type:
+ type_obj = types.Type.create(handle=core.BNNewTypeReference(type))
+ return self.is_non_reg_arg_indirect(view_obj, type_obj)
+ else:
+ return self.is_non_reg_arg_indirect(view_obj, None)
+ except:
+ log_error_for_exception("Unhandled Python exception in CallingConvention._is_non_reg_arg_indirect")
+ return False
+
+ def _are_stack_args_naturally_aligned(self, ctxt):
+ try:
+ return self.__class__.stack_args_naturally_aligned
+ except:
+ log_error_for_exception("Unhandled Python exception in CallingConvention._are_stack_args_naturally_aligned")
+ return False
+
+ def _get_call_layout(
+ self, ctxt, view, ret_value, params, param_count, has_permitted_regs, permitted_regs,
+ permitted_reg_count, out_layout
+ ):
+ try:
+ if view:
+ view_obj = binaryview.BinaryView(handle=core.BNNewViewReference(view))
+ else:
+ view_obj = None
+ if ret_value:
+ ret_value_obj = types.ReturnValue._from_core_struct(ret_value[0])
+ else:
+ ret_value_obj = None
+ param_objs = []
+ for i in range(param_count):
+ param_objs.append(types.FunctionParameter._from_core_struct(params[i]))
+ if has_permitted_regs:
+ reg_objs = []
+ for i in range(permitted_reg_count):
+ reg_objs.append(architecture.RegisterIndex(permitted_regs[i]))
+ else:
+ reg_objs = None
+ layout = self.get_call_layout(view_obj, ret_value_obj, param_objs, permitted_regs = reg_objs)
+
+ result = layout._to_core_struct()
+
+ param_ptr = ctypes.cast(result.parameters, ctypes.c_void_p)
+ self._pending_value_location_lists[param_ptr.value] = (param_ptr.value, result.parameters)
+
+ if result.returnValueValid:
+ ret_ptr = ctypes.cast(result.returnValue.components, ctypes.c_void_p)
+ self._pending_value_locations[ret_ptr.value] = (ret_ptr.value, result.returnValue)
+
+ reg_ptr = ctypes.cast(result.registerStackAdjustmentRegisters, ctypes.c_void_p)
+ self._pending_reg_stack_adjustment_reg_lists[reg_ptr.value] = (reg_ptr.value, result.registerStackAdjustmentRegisters)
+ amount_ptr = ctypes.cast(result.registerStackAdjustmentAmounts, ctypes.c_void_p)
+ self._pending_reg_stack_adjustment_amount_lists[amount_ptr.value] = (amount_ptr.value, result.registerStackAdjustmentAmounts)
+
+ out_layout[0] = result
+ except:
+ log_error_for_exception("Unhandled Python exception in CallingConvention._get_call_layout")
+ result = core.BNCallLayout()
+ result.parameterCount = 0
+ result.returnValueValid = False
+ result.stackAdjustment = 0
+ result.registerStackAdjustmentCount = 0
+ out_layout[0] = result
+
+ def _free_call_layout(self, ctxt, layout_ptr):
+ try:
+ layout = layout_ptr[0]
+ param_ptr = ctypes.cast(layout.parameters, ctypes.c_void_p)
+ if param_ptr.value is not None:
+ if param_ptr.value not in self._pending_value_location_lists:
+ raise ValueError("freeing parameter location list that wasn't allocated")
+ del self._pending_value_location_lists[param_ptr.value]
+
+ if layout.returnValueValid:
+ ret_ptr = ctypes.cast(layout.returnValue.components, ctypes.c_void_p)
+ if ret_ptr.value is not None:
+ if ret_ptr.value not in self._pending_value_locations:
+ raise ValueError("freeing return value location that wasn't allocated")
+ del self._pending_value_locations[ret_ptr.value]
+
+ reg_ptr = ctypes.cast(layout.registerStackAdjustmentRegisters, ctypes.c_void_p)
+ if reg_ptr.value is not None:
+ if reg_ptr.value not in self._pending_reg_stack_adjustment_reg_lists:
+ raise ValueError("freeing register list that wasn't allocated")
+ del self._pending_reg_stack_adjustment_reg_lists[reg_ptr.value]
+
+ amount_ptr = ctypes.cast(layout.registerStackAdjustmentAmounts, ctypes.c_void_p)
+ if amount_ptr.value is not None:
+ if amount_ptr.value not in self._pending_reg_stack_adjustment_amount_lists:
+ raise ValueError("freeing adjustment list that wasn't allocated")
+ del self._pending_reg_stack_adjustment_amount_lists[amount_ptr.value]
+ except:
+ log_error_for_exception("Unhandled Python exception in CallingConvention._free_call_layout")
+
+ def _get_return_value_location(self, ctxt, view, ret_value, out_location):
+ try:
+ if view:
+ view_obj = binaryview.BinaryView(handle=core.BNNewViewReference(view))
+ else:
+ view_obj = None
+ ret = types.ReturnValue._from_core_struct(ret_value[0])
+ location = self.get_return_value_location(view_obj, ret)
+ if location is None:
+ location = types.ValueLocation([])
+ result = location._to_core_struct()
+ result_ptr = ctypes.cast(result.components, ctypes.c_void_p)
+ self._pending_value_locations[result_ptr.value] = (result_ptr.value, result)
+ out_location[0] = result
+ except:
+ log_error_for_exception("Unhandled Python exception in CallingConvention._get_return_value_location")
+ result = core.BNValueLocation()
+ result.count = 0
+ result.components = None
+ out_location[0] = result
+
+ def _free_value_location(self, ctxt, location_ptr):
+ try:
+ location = location_ptr[0]
+ loc_ptr = ctypes.cast(location.components, ctypes.c_void_p)
+ if loc_ptr.value is not None:
+ if loc_ptr.value not in self._pending_value_locations:
+ raise ValueError("freeing value location that wasn't allocated")
+ del self._pending_value_locations[loc_ptr.value]
+ except:
+ log_error_for_exception("Unhandled Python exception in CallingConvention._free_value_location")
+
+ def _get_parameter_locations(
+ self, ctxt, view, ret_value, params, param_count, has_permitted_regs, permitted_regs, permitted_reg_count,
+ out_location_count
+ ):
+ try:
+ if view:
+ view_obj = binaryview.BinaryView(handle=core.BNNewViewReference(view))
+ else:
+ view_obj = None
+ if ret_value:
+ ret_value_obj = types.ValueLocation._from_core_struct(ret_value[0])
+ else:
+ ret_value_obj = None
+ param_objs = []
+ for i in range(param_count):
+ param_objs.append(types.FunctionParameter._from_core_struct(params[i]))
+ if has_permitted_regs:
+ reg_objs = []
+ for i in range(permitted_reg_count):
+ reg_objs.append(architecture.RegisterIndex(permitted_regs[i]))
+ else:
+ reg_objs = None
+ locations = self.get_parameter_locations(view_obj, ret_value_obj, param_objs, permitted_regs = reg_objs)
+
+ out_location_count[0] = len(locations)
+ result = (core.BNValueLocation * len(locations))()
+ for i, location in enumerate(locations):
+ result[i] = location._to_core_struct()
+
+ result_ptr = ctypes.cast(result, ctypes.c_void_p)
+ self._pending_value_location_lists[result_ptr.value] = (result_ptr.value, result)
+
+ return result_ptr.value
+ except:
+ log_error_for_exception("Unhandled Python exception in CallingConvention._get_parameter_locations")
+ out_location_count[0] = 0
+ return None
+
+ def _free_parameter_locations(self, ctxt, locations, count):
+ try:
+ location_ptr = ctypes.cast(locations, ctypes.c_void_p)
+ if location_ptr.value is not None:
+ if location_ptr.value not in self._pending_value_location_lists:
+ raise ValueError("freeing parameter location list that wasn't allocated")
+ del self._pending_value_location_lists[location_ptr.value]
+ except:
+ log_error_for_exception("Unhandled Python exception in CallingConvention._free_parameter_locations")
+
+ def _get_stack_adjustment_for_locations(self, ctxt, view, ret_value, locations, type_list, param_count):
+ try:
+ if view:
+ view_obj = binaryview.BinaryView(handle=core.BNNewViewReference(view))
+ else:
+ view_obj = None
+ if ret_value:
+ ret_value_obj = types.ValueLocation._from_core_struct(ret_value[0])
+ else:
+ ret_value_obj = None
+ params = []
+ for i in range(param_count):
+ loc = types.ValueLocation._from_core_struct(locations[i])
+ ty = types.Type.from_core_struct(type_list[i])
+ params.append((loc, ty))
+ return self.get_stack_adjustment_for_locations(view_obj, ret_value_obj, params)
+ except:
+ log_error_for_exception("Unhandled Python exception in CallingConvention._get_stack_adjustment_for_locations")
+ return 0
+
+ def _get_register_stack_adjustments(self, ctxt, view, ret_value, params, param_count, out_regs, out_adjust):
+ try:
+ if view:
+ view_obj = binaryview.BinaryView(handle=core.BNNewViewReference(view))
+ else:
+ view_obj = None
+ if ret_value:
+ ret_value_obj = types.ValueLocation._from_core_struct(ret_value[0])
+ else:
+ ret_value_obj = None
+ param_objs = []
+ for i in range(param_count):
+ param_objs.append(types.ValueLocation._from_core_struct(params[i]))
+ adjustment = self.get_register_stack_adjustments(view_obj, ret_value_obj, param_objs)
+
+ regs = (ctypes.c_uint * len(adjustment))()
+ adjust = (ctypes.c_int * len(adjustment))()
+ for i, (reg, adj) in enumerate(adjustment.items()):
+ regs[i] = int(reg)
+ adjust[i] = adj
+
+ reg_ptr = ctypes.cast(regs, ctypes.c_void_p)
+ self._pending_reg_stack_adjustment_reg_lists[reg_ptr.value] = (reg_ptr.value, regs)
+ adjust_ptr = ctypes.cast(adjust, ctypes.c_void_p)
+ self._pending_reg_stack_adjustment_amount_lists[adjust_ptr.value] = (adjust_ptr.value, adjust)
+
+ out_regs[0] = regs
+ out_adjust[0] = adjust
+ return len(adjustment)
+ except:
+ log_error_for_exception("Unhandled Python exception in CallingConvention._get_register_stack_adjustments")
+ out_regs[0] = None
+ out_adjust[0] = None
+ return 0
+
+ def _free_register_stack_adjustments(self, ctxt, regs, adjust, count):
+ try:
+ reg_ptr = ctypes.cast(regs, ctypes.c_void_p)
+ if reg_ptr.value is not None:
+ if reg_ptr.value not in self._pending_reg_stack_adjustment_reg_lists:
+ raise ValueError("freeing register list that wasn't allocated")
+ del self._pending_reg_stack_adjustment_reg_lists[reg_ptr.value]
+ adjust_ptr = ctypes.cast(adjust, ctypes.c_void_p)
+ if adjust_ptr.value is not None:
+ if adjust_ptr.value not in self._pending_reg_stack_adjustment_amount_lists:
+ raise ValueError("freeing adjustment list that wasn't allocated")
+ del self._pending_reg_stack_adjustment_amount_lists[adjust_ptr.value]
+ except:
+ log_error_for_exception("Unhandled Python exception in CallingConvention._free_register_stack_adjustments")
+
def perform_get_incoming_reg_value(
self, reg: 'architecture.RegisterName', func: 'function.Function'
) -> 'variable.RegisterValue':
+ """Deprecated, override `get_incoming_reg_value` instead."""
reg_stack = self.arch.get_reg_stack_for_reg(reg)
if reg_stack is not None:
if reg == self.arch.reg_stacks[reg_stack].stack_top_reg:
@@ -504,25 +792,335 @@ class CallingConvention:
def perform_get_incoming_flag_value(
self, flag: 'architecture.FlagName', func: 'function.Function'
) -> 'variable.RegisterValue':
+ """Deprecated, override `get_incoming_flag_value` instead."""
return variable.Undetermined()
def perform_get_incoming_var_for_parameter_var(
self, in_var: 'variable.CoreVariable', func: Optional['function.Function'] = None
) -> 'variable.CoreVariable':
+ """Deprecated, override `get_incoming_var_for_parameter_var` instead."""
out_var = core.BNGetDefaultIncomingVariableForParameterVariable(self.handle, in_var.to_BNVariable())
return variable.CoreVariable.from_BNVariable(out_var)
def perform_get_parameter_var_for_incoming_var(
self, in_var: 'variable.CoreVariable', func: Optional['function.Function'] = None
) -> 'variable.CoreVariable':
+ """Deprecated, override `get_parameter_var_for_incoming_var` instead."""
out_var = core.BNGetDefaultParameterVariableForIncomingVariable(self.handle, in_var.to_BNVariable())
return variable.CoreVariable.from_BNVariable(out_var)
+ def get_incoming_reg_value(
+ self, reg: 'architecture.RegisterName', func: 'function.Function'
+ ) -> 'variable.RegisterValue':
+ return self.perform_get_incoming_reg_value(reg, func)
+
+ def get_incoming_flag_value(
+ self, reg: 'architecture.RegisterName', func: 'function.Function'
+ ) -> 'variable.RegisterValue':
+ return self.perform_get_incoming_flag_value(reg, func)
+
+ def get_incoming_var_for_parameter_var(
+ self, in_var: 'variable.CoreVariable', func: Optional['function.Function'] = None
+ ) -> 'variable.CoreVariable':
+ return self.perform_get_incoming_var_for_parameter_var(in_var, func)
+
+ def get_parameter_var_for_incoming_var(
+ self, in_var: 'variable.CoreVariable', func: Optional['function.Function'] = None
+ ) -> 'variable.CoreVariable':
+ return self.perform_get_incoming_var_for_parameter_var(in_var, func)
+
+ def is_return_type_reg_compatible(self, view: Optional['binaryview.BinaryView'], type: 'types.Type') -> bool:
+ return self.default_is_return_type_reg_compatible(type)
+
+ def is_non_reg_arg_indirect(self, view: Optional['binaryview.BinaryView'], type: Optional['types.Type']) -> bool:
+ return False
+
+ def default_is_return_type_reg_compatible(self, type: 'types.Type') -> bool:
+ return core.BNDefaultIsReturnTypeRegisterCompatible(self.handle, type.handle)
+
+ def get_indirect_return_value_location(self) -> 'variable.CoreVariable':
+ return self.get_default_indirect_return_value_location()
+
+ def get_default_indirect_return_value_location(self) -> 'variable.CoreVariable':
+ result = core.BNGetDefaultIndirectReturnValueLocation(self.handle)
+ return variable.CoreVariable.from_BNVariable(result)
+
+ def get_returned_indirect_return_value_pointer(self) -> Optional['variable.CoreVariable']:
+ return None
+
+ def is_arg_type_reg_compatible(self, view: Optional['binaryview.BinaryView'], type: 'types.Type') -> bool:
+ return self.default_is_arg_type_reg_compatible(type)
+
+ def default_is_arg_type_reg_compatible(self, type: 'types.Type') -> bool:
+ return core.BNDefaultIsArgumentTypeRegisterCompatible(self.handle, type.handle)
+
+ def get_call_layout(
+ self, view: Optional['binaryview.BinaryView'], return_value: Optional['types.ReturnValueOrType'],
+ params: 'types.ParamsType', func: Optional['function.Function'] = None,
+ permitted_regs: Optional[List['architecture.RegisterIndex']] = None
+ ) -> 'CallLayout':
+ return self.get_default_call_layout(view, return_value, params, func, permitted_regs)
+
+ def get_default_call_layout(
+ self, view: Optional['binaryview.BinaryView'], return_value: Optional['types.ReturnValueOrType'],
+ params: 'types.ParamsType', func: Optional['function.Function'] = None,
+ permitted_regs: Optional[List['architecture.RegisterIndex']] = None
+ ) -> 'CallLayout':
+ if view is None:
+ view_obj = None
+ else:
+ view_obj = view.handle
+ if return_value is None:
+ ret = types.ReturnValue(types.Type.void())._to_core_struct()
+ elif isinstance(return_value, types.ReturnValue):
+ ret = return_value._to_core_struct()
+ else:
+ ret = types.ReturnValue(return_value)._to_core_struct()
+ param_structs, type_list = types.FunctionBuilder._to_core_struct(params)
+ if permitted_regs is None:
+ layout = core.BNGetDefaultCallLayoutDefaultPermittedArgs(self.handle, view_obj, ret, param_structs, len(params))
+ else:
+ regs = (ctypes.c_uint * len(permitted_regs))()
+ for i in range(len(permitted_regs)):
+ regs[i] = int(permitted_regs[i])
+ layout = core.BNGetDefaultCallLayout(self.handle, view_obj, ret, param_structs, len(params), regs,
+ len(permitted_regs))
+ result = CallLayout._from_core_struct(layout, func)
+ core.BNFreeCallLayout(layout)
+ return result
+
+ def get_return_value_location(
+ self, view: Optional['binaryview.BinaryView'], return_value: 'types.ReturnValueOrType'
+ ) -> Optional['types.ValueLocation']:
+ return self.get_default_return_value_location(view, return_value)
+
+ def get_default_return_value_location(
+ self, view: Optional['binaryview.BinaryView'], return_value: 'types.ReturnValueOrType'
+ ) -> Optional['types.ValueLocation']:
+ if view is None:
+ view_obj = None
+ else:
+ view_obj = view.handle
+ if return_value is None:
+ ret = types.ReturnValue(types.Type.void())._to_core_struct()
+ elif isinstance(return_value, types.ReturnValue):
+ ret = return_value._to_core_struct()
+ else:
+ ret = types.ReturnValue(return_value)._to_core_struct()
+ location = core.BNGetDefaultReturnValueLocation(self.handle, view_obj, ret)
+ if location.count == 0:
+ result = None
+ else:
+ result = types.ValueLocation._from_core_struct(location)
+ core.BNFreeValueLocation(location)
+ return result
+
+ def get_parameter_locations(
+ self, view: Optional['binaryview.BinaryView'], return_value: Optional['types.ValueLocation'],
+ params: 'types.ParamsType', arch: Optional['architecture.Architecture'] = None,
+ permitted_regs: Optional[List['architecture.RegisterIndex']] = None
+ ) -> List['types.ValueLocation']:
+ return self.get_default_parameter_locations(view, return_value, params, arch, permitted_regs)
+
+ def get_default_parameter_locations(
+ self, view: Optional['binaryview.BinaryView'], return_value: Optional['types.ValueLocation'],
+ params: 'types.ParamsType', arch: Optional['architecture.Architecture'] = None,
+ permitted_regs: Optional[List['architecture.RegisterIndex']] = None
+ ) -> List['types.ValueLocation']:
+ if view is None:
+ view_obj = None
+ else:
+ view_obj = view.handle
+ if return_value is None:
+ ret = None
+ else:
+ ret = return_value._to_core_struct()
+ param_structs, type_list = types.FunctionBuilder._to_core_struct(params)
+ count = ctypes.c_ulonglong()
+ if permitted_regs is None:
+ locations = core.BNGetDefaultParameterLocationsDefaultPermittedArgs(self.handle, view_obj, ret, param_structs,
+ len(params), count)
+ else:
+ regs = (ctypes.c_uint * len(permitted_regs))()
+ for i in range(len(permitted_regs)):
+ regs[i] = int(permitted_regs[i])
+ locations = core.BNGetDefaultParameterLocations(self.handle, view_obj, ret, param_structs, len(params), regs,
+ len(permitted_regs), count)
+ result = []
+ for i in range(count.value):
+ result.append(types.ValueLocation._from_core_struct(locations[i], arch))
+ core.BNFreeValueLocationList(locations, count.value)
+ return result
+
+ def get_stack_adjustment_for_locations(
+ self, view: Optional['binaryview.BinaryView'], return_value: Optional['types.ValueLocation'],
+ params: List[Tuple['types.ValueLocation', 'types.Type']]
+ ):
+ return self.get_default_stack_adjustment_for_locations(return_value, params)
+
+ def get_default_stack_adjustment_for_locations(
+ self, return_value: Optional['types.ValueLocation'],
+ params: List[Tuple['types.ValueLocation', 'types.Type']]
+ ):
+ if return_value is None:
+ ret = None
+ else:
+ ret = return_value._to_core_struct()
+ locations = (core.BNValueLocation * len(params))()
+ type_list = (ctypes.POINTER(core.BNType) * len(params))()
+ for i, (loc, ty) in enumerate(params):
+ locations[i] = loc._to_core_struct()
+ type_list[i] = ty.handle
+ return core.BNGetDefaultStackAdjustmentForLocations(self.handle, ret, locations, type_list, len(params))
+
+ def get_register_stack_adjustments(
+ self, view: Optional['binaryview.BinaryView'], return_value: Optional['types.ValueLocation'],
+ params: List['types.ValueLocation']
+ ) -> Dict['architecture.RegisterIndex', int]:
+ return self.get_default_register_stack_adjustments(return_value, params)
+
+ def get_default_register_stack_adjustments(
+ self, return_value: Optional['types.ValueLocation'], params: List['types.ValueLocation']
+ ) -> Dict['architecture.RegisterIndex', int]:
+ if return_value is None:
+ ret = None
+ else:
+ ret = return_value._to_core_struct()
+ locations = (core.BNValueLocation * len(params))()
+ for i, loc in enumerate(params):
+ locations[i] = loc._to_core_struct()
+ out_regs = ctypes.POINTER(ctypes.c_uint)()
+ out_adjust = ctypes.POINTER(ctypes.c_int)()
+ count = core.BNGetCallingConventionDefaultRegisterStackAdjustments(self.handle, ret, locations, len(params),
+ out_regs, out_adjust)
+
+ result = {}
+ for i in range(count):
+ result[architecture.RegisterIndex(out_regs[i])] = out_adjust[i]
+
+ core.BNFreeCallingConventionRegisterStackAdjustments(out_regs, out_adjust)
+ return result
+
def with_confidence(self, confidence: int) -> 'CallingConvention':
return CallingConvention(
self.arch, handle=core.BNNewCallingConventionReference(self.handle), confidence=confidence
)
+ @property
+ def arch(self) -> 'architecture.Architecture':
+ return self._arch
+
+ @arch.setter
+ def arch(self, value: 'architecture.Architecture') -> None:
+ self._arch = value
+
+
+class CoreCallingConvention(CallingConvention):
+ def __init__(self, handle, confidence: int = core.max_confidence):
+ super().__init__(handle=handle, confidence=confidence)
+
+ self.arch = architecture.CoreArchitecture._from_cache(core.BNGetCallingConventionArchitecture(handle))
+ self.__dict__["name"] = core.BNGetCallingConventionName(handle)
+ self.__dict__["arg_regs_share_index"] = core.BNAreArgumentRegistersSharedIndex(handle)
+ self.__dict__["arg_regs_for_varargs"] = core.BNAreArgumentRegistersUsedForVarArgs(handle)
+ self.__dict__["stack_reserved_for_arg_regs"] = core.BNIsStackReservedForArgumentRegisters(handle)
+ self.__dict__["stack_adjusted_on_return"] = core.BNIsStackAdjustedOnReturn(handle)
+ self.__dict__["eligible_for_heuristics"] = core.BNIsEligibleForHeuristics(handle)
+
+ count = ctypes.c_ulonglong()
+ regs = core.BNGetCallerSavedRegisters(handle, count)
+ assert regs is not None, "core.BNGetCallerSavedRegisters returned None"
+ result = []
+ arch = self.arch
+ for i in range(0, count.value):
+ result.append(arch.get_reg_name(regs[i]))
+ core.BNFreeRegisterList(regs)
+ self.__dict__["caller_saved_regs"] = result
+
+ count = ctypes.c_ulonglong()
+ regs = core.BNGetCalleeSavedRegisters(handle, count)
+ assert regs is not None, "core.BNGetCalleeSavedRegisters returned None"
+ result = []
+ arch = self.arch
+ for i in range(0, count.value):
+ result.append(arch.get_reg_name(regs[i]))
+ core.BNFreeRegisterList(regs)
+ self.__dict__["callee_saved_regs"] = result
+
+ count = ctypes.c_ulonglong()
+ regs = core.BNGetIntegerArgumentRegisters(handle, count)
+ assert regs is not None, "core.BNGetIntegerArgumentRegisters returned None"
+ result = []
+ arch = self.arch
+ for i in range(0, count.value):
+ result.append(arch.get_reg_name(regs[i]))
+ core.BNFreeRegisterList(regs)
+ self.__dict__["int_arg_regs"] = result
+
+ count = ctypes.c_ulonglong()
+ regs = core.BNGetFloatArgumentRegisters(handle, count)
+ assert regs is not None, "core.BNGetFloatArgumentRegisters returned None"
+ result = []
+ arch = self.arch
+ for i in range(0, count.value):
+ result.append(arch.get_reg_name(regs[i]))
+ core.BNFreeRegisterList(regs)
+ self.__dict__["float_arg_regs"] = result
+
+ count = ctypes.c_ulonglong()
+ regs = core.BNGetRequiredArgumentRegisters(handle, count)
+ assert regs is not None, "core.BNGetRequiredArgumentRegisters returned None"
+ result = []
+ arch = self.arch
+ for i in range(0, count.value):
+ result.append(arch.get_reg_name(regs[i]))
+ core.BNFreeRegisterList(regs)
+ self.__dict__["required_arg_regs"] = result
+
+ count = ctypes.c_ulonglong()
+ regs = core.BNGetRequiredClobberedRegisters(handle, count)
+ assert regs is not None, "core.BNGetRequiredClobberedRegisters returned None"
+ result = []
+ arch = self.arch
+ for i in range(0, count.value):
+ result.append(arch.get_reg_name(regs[i]))
+ core.BNFreeRegisterList(regs)
+ self.__dict__["required_clobbered_regs"] = result
+
+ reg = core.BNGetIntegerReturnValueRegister(handle)
+ if reg == 0xffffffff:
+ self.__dict__["int_return_reg"] = None
+ else:
+ self.__dict__["int_return_reg"] = self.arch.get_reg_name(reg)
+
+ reg = core.BNGetHighIntegerReturnValueRegister(handle)
+ if reg == 0xffffffff:
+ self.__dict__["high_int_return_reg"] = None
+ else:
+ self.__dict__["high_int_return_reg"] = self.arch.get_reg_name(reg)
+
+ reg = core.BNGetFloatReturnValueRegister(handle)
+ if reg == 0xffffffff:
+ self.__dict__["float_return_reg"] = None
+ else:
+ self.__dict__["float_return_reg"] = self.arch.get_reg_name(reg)
+
+ reg = core.BNGetGlobalPointerRegister(handle)
+ if reg == 0xffffffff:
+ self.__dict__["global_pointer_reg"] = None
+ else:
+ self.__dict__["global_pointer_reg"] = self.arch.get_reg_name(reg)
+
+ count = ctypes.c_ulonglong()
+ regs = core.BNGetImplicitlyDefinedRegisters(handle, count)
+ assert regs is not None, "core.BNGetImplicitlyDefinedRegisters returned None"
+ result = []
+ arch = self.arch
+ for i in range(0, count.value):
+ result.append(arch.get_reg_name(regs[i]))
+ core.BNFreeRegisterList(regs)
+ self.__dict__["implicitly_defined_regs"] = result
+
def get_incoming_reg_value(
self, reg: 'architecture.RegisterType', func: 'function.Function'
) -> 'variable.RegisterValue':
@@ -546,7 +1144,7 @@ class CallingConvention:
)
def get_incoming_var_for_parameter_var(
- self, in_var: 'variable.CoreVariable', func: FunctionOrILFunction
+ self, in_var: 'variable.CoreVariable', func: FunctionOrILFunction = None
) -> 'variable.Variable':
in_buf = in_var.to_BNVariable()
if func is None:
@@ -557,7 +1155,7 @@ class CallingConvention:
return variable.Variable.from_BNVariable(func, out_var)
def get_parameter_var_for_incoming_var(
- self, in_var: 'variable.CoreVariable', func: FunctionOrILFunction
+ self, in_var: 'variable.CoreVariable', func: FunctionOrILFunction = None
) -> 'variable.Variable':
in_buf = in_var.to_BNVariable()
if func is None:
@@ -567,10 +1165,160 @@ class CallingConvention:
out_var = core.BNGetParameterVariableForIncomingVariable(self.handle, in_buf, func_obj)
return variable.Variable.from_BNVariable(func, out_var)
- @property
- def arch(self) -> 'architecture.Architecture':
- return self._arch
+ def is_return_type_reg_compatible(self, view: Optional['binaryview.BinaryView'], type: 'types.Type') -> bool:
+ if view is None:
+ view_obj = None
+ else:
+ view_obj = view.handle
+ return core.BNIsReturnTypeRegisterCompatible(self.handle, view_obj, type.handle)
- @arch.setter
- def arch(self, value: 'architecture.Architecture') -> None:
- self._arch = value
+ def get_indirect_return_value_location(self) -> 'variable.CoreVariable':
+ result = core.BNGetIndirectReturnValueLocation(self.handle)
+ return variable.CoreVariable.from_BNVariable(result)
+
+ def get_returned_indirect_return_value_pointer(self) -> Optional['variable.CoreVariable']:
+ var = core.BNVariable()
+ if core.BNGetReturnedIndirectReturnValuePointer(self.handle, var):
+ return variable.CoreVariable.from_BNVariable(var)
+ return None
+
+ def is_arg_type_reg_compatible(self, view: Optional['binaryview.BinaryView'], type: 'types.Type') -> bool:
+ if view is None:
+ view_obj = None
+ else:
+ view_obj = view.handle
+ return core.BNIsArgumentTypeRegisterCompatible(self.handle, view_obj, type.handle)
+
+ def is_non_reg_arg_indirect(self, view: Optional['binaryview.BinaryView'], type: Optional['types.Type']) -> bool:
+ if view is None:
+ view_obj = None
+ else:
+ view_obj = view.handle
+ if type is None:
+ return core.BNIsNonRegisterArgumentIndirect(self.handle, view_obj, None)
+ else:
+ return core.BNIsNonRegisterArgumentIndirect(self.handle, view_obj, type.handle)
+
+ def get_call_layout(
+ self, view: Optional['binaryview.BinaryView'], return_value: Optional['types.ReturnValueOrType'],
+ params: 'types.ParamsType', func: Optional['function.Function'] = None,
+ permitted_regs: Optional[List['architecture.RegisterIndex']] = None
+ ) -> 'CallLayout':
+ if view is None:
+ view_obj = None
+ else:
+ view_obj = view.handle
+ if return_value is None:
+ ret = types.ReturnValue(types.Type.void())._to_core_struct()
+ elif isinstance(return_value, types.ReturnValue):
+ ret = return_value._to_core_struct()
+ else:
+ ret = types.ReturnValue(return_value)._to_core_struct()
+ param_structs, type_list = types.FunctionBuilder._to_core_struct(params)
+ if permitted_regs is None:
+ layout = core.BNGetCallLayoutDefaultPermittedArgs(self.handle, view_obj, ret, param_structs, len(params))
+ else:
+ regs = (ctypes.c_uint * len(permitted_regs))()
+ for i in range(len(permitted_regs)):
+ regs[i] = int(permitted_regs[i])
+ layout = core.BNGetCallLayout(self.handle, view_obj, ret, param_structs, len(params), regs, len(permitted_regs))
+ result = CallLayout._from_core_struct(layout, func)
+ core.BNFreeCallLayout(layout)
+ return result
+
+ def get_return_value_location(
+ self, view: Optional['binaryview.BinaryView'], return_value: 'types.ReturnValueOrType'
+ ) -> Optional['types.ValueLocation']:
+ if view is None:
+ view_obj = None
+ else:
+ view_obj = view.handle
+ if return_value is None:
+ ret = types.ReturnValue(types.Type.void())._to_core_struct()
+ elif isinstance(return_value, types.ReturnValue):
+ ret = return_value._to_core_struct()
+ else:
+ ret = types.ReturnValue(return_value)._to_core_struct()
+ location = core.BNGetReturnValueLocation(self.handle, view_obj, ret)
+ if location.count == 0:
+ result = None
+ else:
+ result = types.ValueLocation._from_core_struct(location)
+ core.BNFreeValueLocation(location)
+ return result
+
+ def get_parameter_locations(
+ self, view: Optional['binaryview.BinaryView'], return_value: Optional['types.ValueLocation'],
+ params: 'types.ParamsType', arch: Optional['architecture.Architecture'] = None,
+ permitted_regs: Optional[List['architecture.RegisterIndex']] = None
+ ) -> List['types.ValueLocation']:
+ if view is None:
+ view_obj = None
+ else:
+ view_obj = view.handle
+ if return_value is None:
+ ret = None
+ else:
+ ret = return_value._to_core_struct()
+ param_structs, type_list = types.FunctionBuilder._to_core_struct(params)
+ count = ctypes.c_ulonglong()
+ if permitted_regs is None:
+ locations = core.BNGetParameterLocationsDefaultPermittedArgs(self.handle, view_obj, ret, param_structs,
+ len(params), count)
+ else:
+ regs = (ctypes.c_uint * len(permitted_regs))()
+ for i in range(len(permitted_regs)):
+ regs[i] = int(permitted_regs[i])
+ locations = core.BNGetParameterLocations(self.handle, view_obj, ret, param_structs, len(params), regs,
+ len(permitted_regs), count)
+ result = []
+ for i in range(count.value):
+ result.append(types.ValueLocation._from_core_struct(locations[i], arch))
+ core.BNFreeValueLocationList(locations, count.value)
+ return result
+
+ def get_stack_adjustment_for_locations(
+ self, view: Optional['binaryview.BinaryView'], return_value: Optional['types.ValueLocation'],
+ params: List[Tuple['types.ValueLocation', 'types.Type']]
+ ):
+ if view is None:
+ view_obj = None
+ else:
+ view_obj = view.handle
+ if return_value is None:
+ ret = None
+ else:
+ ret = return_value._to_core_struct()
+ locations = (core.BNValueLocation * len(params))()
+ type_list = (ctypes.POINTER(core.BNType) * len(params))()
+ for i, (loc, ty) in enumerate(params):
+ locations[i] = loc._to_core_struct()
+ type_list[i] = ty.handle
+ return core.BNGetStackAdjustmentForLocations(self.handle, view_obj, ret, locations, type_list, len(params))
+
+ def get_register_stack_adjustments(
+ self, view: Optional['binaryview.BinaryView'], return_value: Optional['types.ValueLocation'],
+ params: List['types.ValueLocation']
+ ) -> Dict['architecture.RegisterIndex', int]:
+ if view is None:
+ view_obj = None
+ else:
+ view_obj = view.handle
+ if return_value is None:
+ ret = None
+ else:
+ ret = return_value._to_core_struct()
+ locations = (core.BNValueLocation * len(params))()
+ for i, loc in enumerate(params):
+ locations[i] = loc._to_core_struct()
+ out_regs = ctypes.POINTER(ctypes.c_uint)()
+ out_adjust = ctypes.POINTER(ctypes.c_int)()
+ count = core.BNGetCallingConventionRegisterStackAdjustments(self.handle, view_obj, ret, locations, len(params),
+ out_regs, out_adjust)
+
+ result = {}
+ for i in range(count):
+ result[architecture.RegisterIndex(out_regs[i])] = out_adjust[i]
+
+ core.BNFreeCallingConventionRegisterStackAdjustments(out_regs, out_adjust)
+ return result