summaryrefslogtreecommitdiff
path: root/python
diff options
context:
space:
mode:
authorRusty Wagner <rusty.wagner@gmail.com>2022-07-07 21:03:29 -0400
committerRusty Wagner <rusty.wagner@gmail.com>2022-08-10 15:17:53 -0400
commit390d5d27d4de6b7c9cb06b957a483bb2ab69f241 (patch)
tree556133db637166968a77d68daf7ff2160df79b40 /python
parent4037000b6cb8a184ad098d0eff397518d7fe55b9 (diff)
Merge variables API/UI, variable liveness API for determining soundness of variable merges
Diffstat (limited to 'python')
-rw-r--r--python/function.py58
-rw-r--r--python/mediumlevelil.py22
-rw-r--r--python/variable.py9
3 files changed, 87 insertions, 2 deletions
diff --git a/python/function.py b/python/function.py
index 9a0f099a..d2855234 100644
--- a/python/function.py
+++ b/python/function.py
@@ -1377,6 +1377,26 @@ class Function:
return None
return flowgraph.CoreFlowGraph(graph)
+ @property
+ def merged_vars(self) -> Dict['variable.Variable', List['variable.Variable']]:
+ """
+ Map of merged variables, organized by target variable (read-only). Use ``merge_vars`` and
+ ``unmerge_vars`` to update merged variables.
+ """
+ count = ctypes.c_ulonglong()
+ data = core.BNGetMergedVariables(self.handle, count)
+
+ result = {}
+ for i in range(count.value):
+ target = Variable.from_BNVariable(self, data[i].target)
+ sources = []
+ for j in range(data[i].sourceCount):
+ sources.append(Variable.from_BNVariable(self, data[i].sources[j]))
+ result[target] = sources
+
+ core.BNFreeMergedVariableList(data, count.value)
+ return result
+
def mark_recent_use(self) -> None:
core.BNMarkFunctionAsRecentlyUsed(self.handle)
@@ -3316,6 +3336,44 @@ class Function:
return start.value
return None
+ def merge_vars(
+ self, target: 'variable.Variable', sources: Union[List['variable.Variable'], 'variable.Variable']
+ ) -> None:
+ """
+ ``merge_vars`` merges one or more varibles in ``sources`` into the ``target`` variable. All
+ variable accesses to the variables in ``sources`` will be rewritten to use ``target``.
+
+ :param Variable target: target variable
+ :param list(Variable) sources: list of source variables
+ """
+ if isinstance(sources, variable.Variable):
+ sources = [sources]
+ source_list = (core.BNVariable * len(sources))()
+ for i in range(0, len(sources)):
+ source_list[i].type = sources[i].source_type
+ source_list[i].index = sources[i].index
+ source_list[i].storage = sources[i].storage
+ core.BNMergeVariables(self.handle, target.to_BNVariable(), source_list, len(sources))
+
+ def unmerge_vars(
+ self, target: 'variable.Variable', sources: Union[List['variable.Variable'], 'variable.Variable']
+ ) -> None:
+ """
+ ``unmerge_vars`` undoes variable merging performed with ``merge_vars``. The variables in
+ ``sources`` will no longer be merged into the ``target`` variable.
+
+ :param Variable target: target variable
+ :param list(Variable) sources: list of source variables
+ """
+ if isinstance(sources, variable.Variable):
+ sources = [sources]
+ source_list = (core.BNVariable * len(sources))()
+ for i in range(0, len(sources)):
+ source_list[i].type = sources[i].source_type
+ source_list[i].index = sources[i].index
+ source_list[i].storage = sources[i].storage
+ core.BNUnmergeVariables(self.handle, target.to_BNVariable(), source_list, len(sources))
+
class AdvancedFunctionAnalysisDataRequestor:
def __init__(self, func: 'Function' = None):
diff --git a/python/mediumlevelil.py b/python/mediumlevelil.py
index 07ca7a22..ceb33f50 100644
--- a/python/mediumlevelil.py
+++ b/python/mediumlevelil.py
@@ -3024,6 +3024,28 @@ class MediumLevelILFunction:
finally:
core.BNFreeILInstructionList(instrs)
+ def get_live_instructions_for_var(self, var: 'variable.Variable', include_last_use: bool = True) -> List[MediumLevelILInstruction]:
+ """
+ ``get_live_instructions_for_var`` computes the list of instructions for which ``var`` is live.
+ If ``include_last_use`` is False, the last use of the variable will not be included in the
+ list (this allows for easier computation of overlaps in liveness between two variables).
+ If the variable is never used, this function will return an empty list.
+
+ :param SSAVariable var: the variable to query
+ :param bool include_last_use: whether to include the last use of the variable in the list of instructions
+ :return: list of instructions for which ``var`` is live
+ :rtype: list(MediumLevelILInstruction)
+ """
+ count = ctypes.c_ulonglong()
+ var_data = var.to_BNVariable()
+ instrs = core.BNGetMediumLevelILLiveInstructionsForVariable(self.handle, var_data, include_last_use, count)
+ assert instrs is not None, "core.BNGetMediumLevelILLiveInstructionsForVariable returned None"
+ result = []
+ for i in range(0, count.value):
+ result.append(self[instrs[i]])
+ core.BNFreeILInstructionList(instrs)
+ return result
+
def get_ssa_var_value(self, ssa_var: SSAVariable) -> 'variable.RegisterValue':
var_data = ssa_var.var.to_BNVariable()
value = core.BNGetMediumLevelILSSAVarValue(self.handle, var_data, ssa_var.version)
diff --git a/python/variable.py b/python/variable.py
index 07b8863c..839e87ce 100644
--- a/python/variable.py
+++ b/python/variable.py
@@ -663,7 +663,7 @@ class Variable(CoreVariable):
if self.type is not None:
return f"<var {self.type.get_string_before_name()} {self.name}{self.type.get_string_after_name()}>"
else:
- return repr(super())
+ return f"<var {self.name}>"
def __str__(self):
return self.name
@@ -712,7 +712,7 @@ class Variable(CoreVariable):
@property
def name(self) -> str:
"""Name of the variable, Settings thisslow because it ensures that analysis has been updated. """
- return core.BNGetRealVariableName(self._function.handle, self._function.arch.handle, self.to_BNVariable())
+ return core.BNGetVariableNameOrDefault(self._function.handle, self.to_BNVariable())
@name.setter
def name(self, name: Optional[str]) -> None:
@@ -720,6 +720,11 @@ class Variable(CoreVariable):
self._function.view.update_analysis_and_wait()
@property
+ def last_seen_name(self) -> str:
+ """Name of the variable, or the name most recently assigned if the variable has since been removed (read-only). """
+ return core.BNGetLastSeenVariableNameOrDefault(self._function.handle, self.to_BNVariable())
+
+ @property
def type(self) -> Optional['binaryninja.types.Type']:
var_type_conf = core.BNGetVariableType(self._function.handle, self.to_BNVariable())
if var_type_conf.type: