summaryrefslogtreecommitdiff
path: root/python
diff options
context:
space:
mode:
authorBrandon Miller <brandon@vector35.com>2026-06-02 09:00:15 -0400
committerBrandon Miller <brandon@vector35.com>2026-06-02 09:00:15 -0400
commit5953d087a9a104e7abb63c5739091214edf2faca (patch)
treeabd95a2698f32b8e1f6ae7feaa9647cff8d218ae /python
parent68215ea14cfbaa4966f830d85d98f7f49b416455 (diff)
Add support for multiple global pointer registers
Diffstat (limited to 'python')
-rw-r--r--python/binaryview.py83
-rw-r--r--python/callingconvention.py44
-rw-r--r--python/function.py23
3 files changed, 127 insertions, 23 deletions
diff --git a/python/binaryview.py b/python/binaryview.py
index d97e9f4d..b1fbde79 100644
--- a/python/binaryview.py
+++ b/python/binaryview.py
@@ -4204,21 +4204,65 @@ class BinaryView:
@property
def global_pointer_value(self) -> 'variable.RegisterValue':
- """Discovered value of the global pointer register, if the binary uses one (read-only)"""
- result = core.BNGetGlobalPointerValue(self.handle)
- return variable.RegisterValue.from_BNRegisterValue(result, self.arch)
+ """Deprecated. Use :py:attr:`global_pointer_values` instead."""
+ values = self.global_pointer_values
+ if not values:
+ return variable.Undetermined()
+ return values[0][1]
@property
+ def global_pointer_values(self) -> List[Tuple['architecture.RegisterName', 'variable.RegisterValue']]:
+ """Discovered values of the global pointer registers, if the binary uses any (read-only)"""
+ return self._get_global_pointer_values(core.BNGetGlobalPointerValues)
+
+ @property
+ def default_global_pointer_values(self) -> List[Tuple['architecture.RegisterName', 'variable.RegisterValue']]:
+ """Auto-discovered values of the global pointer registers before user overrides are applied (read-only)"""
+ return self._get_global_pointer_values(core.BNGetDefaultGlobalPointerValues)
+
+ @property
+ def user_global_pointer_values(self) -> List[Tuple['architecture.RegisterName', 'variable.RegisterValue']]:
+ """User overrides for global pointer register values (read-only)"""
+ return self._get_global_pointer_values(core.BNGetUserGlobalPointerValues)
+
+ def _get_global_pointer_values(self, getter) -> List[Tuple['architecture.RegisterName', 'variable.RegisterValue']]:
+ count = ctypes.c_ulonglong()
+ values = getter(self.handle, count)
+ if values is None:
+ return []
+ try:
+ return [
+ (self.arch.get_reg_name(values[i].reg), variable.RegisterValue.from_BNRegisterValue(values[i].value, self.arch))
+ for i in range(count.value)
+ ]
+ finally:
+ core.BNFreeRegisterValueWithConfidenceAndRegisterList(values)
+
+ @property
+ @deprecation.deprecated(deprecated_in="5.4", details="Use `BinaryView.user_global_pointer_values_set` instead.")
def user_global_pointer_value_set(self) -> bool:
- """Check whether a user global pointer value has been set"""
- return core.BNUserGlobalPointerValueSet(self.handle)
+ """Deprecated. Use :py:attr:`user_global_pointer_values_set` instead."""
+ return self.user_global_pointer_values_set
+
+ @property
+ def user_global_pointer_values_set(self) -> bool:
+ """Check whether user global pointer values have been set"""
+ return core.BNUserGlobalPointerValuesSet(self.handle)
+ @deprecation.deprecated(deprecated_in="5.4", details="Use `BinaryView.clear_user_global_pointer_values` instead.")
def clear_user_global_pointer_value(self):
- """Clear a previously set user global pointer value, so the auto-analysis can calculate a new value"""
- core.BNClearUserGlobalPointerValue(self.handle)
+ """Deprecated. Use :py:meth:`clear_user_global_pointer_values` instead."""
+ self.clear_user_global_pointer_values()
+ def clear_user_global_pointer_values(self):
+ """Clear previously set user global pointer values, so the auto-analysis can calculate new values"""
+ core.BNClearUserGlobalPointerValues(self.handle)
+
+ @deprecation.deprecated(deprecated_in="5.4", details="Use `BinaryView.set_user_global_pointer_values` instead.")
def set_user_global_pointer_value(self, value: variable.RegisterValue, confidence = 255):
"""
+ Deprecated. Use :py:meth:`set_user_global_pointer_values` instead.
+
Set a user global pointer value. This is useful when the auto analysis fails to find out the value of the global
pointer, or the value is wrong. In this case, we can call ``set_user_global_pointer_value`` with a
``ConstantRegisterValue`` or ``ConstantPointerRegisterValue`` to provide a user global pointer value to assist the
@@ -4258,10 +4302,27 @@ class BinaryView:
>>> bv.global_pointer_value
<undetermined>
"""
- val = core.BNRegisterValueWithConfidence()
- val.value = value._to_core_struct()
- val.confidence = confidence
- core.BNSetUserGlobalPointerValue(self.handle, val)
+ values = [(reg, value, confidence) for reg, _ in self.global_pointer_values]
+ if not values:
+ values = [(0xffffffff, value, confidence)]
+ self.set_user_global_pointer_values(values)
+
+ def set_user_global_pointer_values(self, values):
+ """
+ Set user global pointer values for multiple registers.
+
+ :param list[tuple[str, variable.RegisterValue]] values: register name/value pairs
+ :return: None
+ :rtype: None
+ """
+ api_values = (core.BNRegisterValueWithConfidenceAndRegister * len(values))()
+ for i, value in enumerate(values):
+ reg, reg_value = value[:2]
+ confidence = value[2] if len(value) > 2 else 255
+ api_values[i].reg = reg if isinstance(reg, int) else self.arch.get_reg_index(reg)
+ api_values[i].value.value = reg_value._to_core_struct()
+ api_values[i].value.confidence = confidence
+ core.BNSetUserGlobalPointerValues(self.handle, api_values, len(values))
@property
def parameters_for_analysis(self):
diff --git a/python/callingconvention.py b/python/callingconvention.py
index a76f59f0..6c6e1409 100644
--- a/python/callingconvention.py
+++ b/python/callingconvention.py
@@ -122,8 +122,11 @@ class CallingConvention:
that is too large to fit in a single register, or ``None`` if there is none.
:cvar float_return_reg: The register that holds the floating point return value, or ``None``
if there is none.
- :cvar global_pointer_reg: The register that holds the global pointer, if the calling
- convention defines one, or ``None`` if there is none.
+ :cvar global_pointer_reg: Deprecated. Use ``global_pointer_regs`` instead. The register
+ that holds the global pointer, if the calling convention defines one, or ``None`` if
+ there is none.
+ :cvar global_pointer_regs: The registers that hold global pointers, if the calling
+ convention defines any.
:cvar implicitly_defined_regs: The registers that are implicitly given a known value on
function entry by this calling convention.
:cvar stack_args_naturally_aligned: Whether arguments passed on the stack are aligned to their
@@ -147,6 +150,7 @@ class CallingConvention:
high_int_return_reg = None
float_return_reg = None
global_pointer_reg = None
+ global_pointer_regs = []
implicitly_defined_regs = []
stack_args_naturally_aligned = False
stack_args_pushed_left_to_right = False
@@ -204,8 +208,8 @@ class CallingConvention:
self._cb.getFloatReturnValueRegister = self._cb.getFloatReturnValueRegister.__class__(
self._get_float_return_reg
)
- self._cb.getGlobalPointerRegister = self._cb.getGlobalPointerRegister.__class__(
- self._get_global_pointer_reg
+ self._cb.getGlobalPointerRegisters = self._cb.getGlobalPointerRegisters.__class__(
+ self._get_global_pointer_regs
)
self._cb.getImplicitlyDefinedRegisters = self._cb.getImplicitlyDefinedRegisters.__class__(
self._get_implicitly_defined_regs
@@ -463,6 +467,23 @@ class CallingConvention:
log_error_for_exception("Unhandled Python exception in CallingConvention._get_global_pointer_reg")
return False
+ def _get_global_pointer_regs(self, ctxt, count):
+ try:
+ regs = self.__class__.global_pointer_regs
+ if not regs and self.__class__.global_pointer_reg is not None:
+ regs = [self.__class__.global_pointer_reg]
+ count[0] = len(regs)
+ reg_buf = (ctypes.c_uint * len(regs))()
+ for i in range(0, len(regs)):
+ reg_buf[i] = self.arch.regs[regs[i]].index
+ result = ctypes.cast(reg_buf, ctypes.c_void_p)
+ self._pending_reg_lists[result.value] = (result, reg_buf)
+ return result.value
+ except Exception:
+ log_error_for_exception("Unhandled Python exception in CallingConvention._get_global_pointer_regs")
+ count[0] = 0
+ return None
+
def _get_implicitly_defined_regs(self, ctxt, count):
try:
regs = self.__class__.implicitly_defined_regs
@@ -1509,11 +1530,16 @@ class CoreCallingConvention(CallingConvention):
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.BNGetGlobalPointerRegisters(handle, count)
+ result = []
+ arch = self.arch
+ if regs is not None:
+ for i in range(0, count.value):
+ result.append(arch.get_reg_name(regs[i]))
+ core.BNFreeRegisterList(regs)
+ self.__dict__["global_pointer_regs"] = result
+ self.__dict__["global_pointer_reg"] = result[0] if result else None
count = ctypes.c_ulonglong()
regs = core.BNGetImplicitlyDefinedRegisters(handle, count)
diff --git a/python/function.py b/python/function.py
index 900aa0de..5e7541f6 100644
--- a/python/function.py
+++ b/python/function.py
@@ -1605,9 +1605,26 @@ class Function:
@property
def global_pointer_value(self) -> variable.RegisterValue:
- """Discovered value of the global pointer register, if the function uses one (read-only)"""
- result = core.BNGetFunctionGlobalPointerValue(self.handle)
- return variable.RegisterValue.from_BNRegisterValue(result, self.arch)
+ """Deprecated. Use :py:attr:`global_pointer_values` instead."""
+ values = self.global_pointer_values
+ if not values:
+ return variable.Undetermined()
+ return values[0][1]
+
+ @property
+ def global_pointer_values(self) -> List[Tuple['architecture.RegisterName', 'variable.RegisterValue']]:
+ """Discovered values of the global pointer registers, if the function uses any (read-only)"""
+ count = ctypes.c_ulonglong()
+ values = core.BNGetFunctionGlobalPointerValues(self.handle, count)
+ if values is None:
+ return []
+ try:
+ return [
+ (self.arch.get_reg_name(values[i].reg), variable.RegisterValue.from_BNRegisterValue(values[i].value, self.arch))
+ for i in range(count.value)
+ ]
+ finally:
+ core.BNFreeRegisterValueWithConfidenceAndRegisterList(values)
@property
def uses_incoming_global_pointer(self) -> bool: