summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--binaryninjaapi.h8
-rw-r--r--binaryninjacore.h22
-rw-r--r--function.cpp93
-rw-r--r--mediumlevelil.cpp14
-rw-r--r--python/function.py58
-rw-r--r--python/mediumlevelil.py22
-rw-r--r--python/variable.py9
-rw-r--r--suite/testcommon.py45
-rw-r--r--ui/flowgraphwidget.h1
-rw-r--r--ui/linearview.h39
-rw-r--r--ui/mergevariablesdialog.h76
-rw-r--r--ui/uitypes.h2
12 files changed, 364 insertions, 25 deletions
diff --git a/binaryninjaapi.h b/binaryninjaapi.h
index 8af70be6..7c73d066 100644
--- a/binaryninjaapi.h
+++ b/binaryninjaapi.h
@@ -5736,6 +5736,8 @@ namespace BinaryNinja {
bool IsVariableUserDefinded(const Variable& var);
Confidence<Ref<Type>> GetVariableType(const Variable& var);
std::string GetVariableName(const Variable& var);
+ std::string GetVariableNameOrDefault(const Variable& var);
+ std::string GetLastSeenVariableNameOrDefault(const Variable& var);
void SetAutoIndirectBranches(
Architecture* sourceArch, uint64_t source, const std::vector<ArchAndAddr>& branches);
@@ -5880,6 +5882,10 @@ namespace BinaryNinja {
BNDeadStoreElimination GetVariableDeadStoreElimination(const Variable& var);
void SetVariableDeadStoreElimination(const Variable& var, BNDeadStoreElimination mode);
+ std::map<Variable, std::set<Variable>> GetMergedVariables();
+ void MergeVariables(const Variable& target, const std::set<Variable>& sources);
+ void UnmergeVariables(const Variable& target, const std::set<Variable>& sources);
+
uint64_t GetHighestAddress();
uint64_t GetLowestAddress();
std::vector<BNAddressRange> GetAddressRanges();
@@ -6755,6 +6761,8 @@ namespace BinaryNinja {
}
Ref<FlowGraph> CreateFunctionGraph(DisassemblySettings* settings = nullptr);
+
+ std::set<size_t> GetLiveInstructionsForVariable(const Variable& var, bool includeLastUse = true);
};
struct HighLevelILInstruction;
diff --git a/binaryninjacore.h b/binaryninjacore.h
index 6c830729..082dd4aa 100644
--- a/binaryninjacore.h
+++ b/binaryninjacore.h
@@ -36,14 +36,14 @@
// Current ABI version for linking to the core. This is incremented any time
// there are changes to the API that affect linking, including new functions,
// new types, or modifications to existing functions or types.
-#define BN_CURRENT_CORE_ABI_VERSION 23
+#define BN_CURRENT_CORE_ABI_VERSION 24
// Minimum ABI version that is supported for loading of plugins. Plugins that
// are linked to an ABI version less than this will not be able to load and
// will require rebuilding. The minimum version is increased when there are
// incompatible changes that break binary compatibility, such as changes to
// existing types or functions.
-#define BN_MINIMUM_CORE_ABI_VERSION 22
+#define BN_MINIMUM_CORE_ABI_VERSION 24
#ifdef __GNUC__
#ifdef BINARYNINJACORE_LIBRARY
@@ -2849,6 +2849,13 @@ extern "C"
const char* channel;
};
+ struct BNMergedVariable
+ {
+ BNVariable target;
+ BNVariable* sources;
+ size_t sourceCount;
+ };
+
BINARYNINJACOREAPI char* BNAllocString(const char* contents);
BINARYNINJACOREAPI void BNFreeString(char* str);
BINARYNINJACOREAPI char** BNAllocStringList(const char** contents, size_t size);
@@ -3989,13 +3996,20 @@ extern "C"
BINARYNINJACOREAPI bool BNIsVariableUserDefined(BNFunction* func, const BNVariable* var);
BINARYNINJACOREAPI BNTypeWithConfidence BNGetVariableType(BNFunction* func, const BNVariable* var);
BINARYNINJACOREAPI char* BNGetVariableName(BNFunction* func, const BNVariable* var);
- BINARYNINJACOREAPI char* BNGetRealVariableName(BNFunction* func, BNArchitecture* arch, const BNVariable* var);
+ BINARYNINJACOREAPI char* BNGetVariableNameOrDefault(BNFunction* func, const BNVariable* var);
+ BINARYNINJACOREAPI char* BNGetLastSeenVariableNameOrDefault(BNFunction* func, const BNVariable* var);
BINARYNINJACOREAPI uint64_t BNToVariableIdentifier(const BNVariable* var);
BINARYNINJACOREAPI BNVariable BNFromVariableIdentifier(uint64_t id);
BINARYNINJACOREAPI BNDeadStoreElimination BNGetFunctionVariableDeadStoreElimination(
BNFunction* func, const BNVariable* var);
BINARYNINJACOREAPI void BNSetFunctionVariableDeadStoreElimination(
BNFunction* func, const BNVariable* var, BNDeadStoreElimination mode);
+ BINARYNINJACOREAPI BNMergedVariable* BNGetMergedVariables(BNFunction* func, size_t* count);
+ BINARYNINJACOREAPI void BNFreeMergedVariableList(BNMergedVariable* vars, size_t count);
+ BINARYNINJACOREAPI void BNMergeVariables(BNFunction* func, const BNVariable* target, const BNVariable* sources,
+ size_t sourceCount);
+ BINARYNINJACOREAPI void BNUnmergeVariables(BNFunction* func, const BNVariable* target, const BNVariable* sources,
+ size_t sourceCount);
BINARYNINJACOREAPI BNReferenceSource* BNGetFunctionCallSites(BNFunction* func, size_t* count);
BINARYNINJACOREAPI uint64_t* BNGetCallees(BNBinaryView* view, BNReferenceSource* callSite, size_t* count);
@@ -4892,6 +4906,8 @@ extern "C"
BNMediumLevelILFunction* func, const BNVariable* var, size_t* count);
BINARYNINJACOREAPI size_t* BNGetMediumLevelILVariableUses(
BNMediumLevelILFunction* func, const BNVariable* var, size_t* count);
+ BINARYNINJACOREAPI size_t* BNGetMediumLevelILLiveInstructionsForVariable(
+ BNMediumLevelILFunction* func, const BNVariable* var, bool includeLastUse, size_t* count);
BINARYNINJACOREAPI BNRegisterValue BNGetMediumLevelILSSAVarValue(
BNMediumLevelILFunction* func, const BNVariable* var, size_t version);
diff --git a/function.cpp b/function.cpp
index c93a8329..06815c39 100644
--- a/function.cpp
+++ b/function.cpp
@@ -1496,6 +1496,24 @@ string Function::GetVariableName(const Variable& var)
}
+string Function::GetVariableNameOrDefault(const Variable& var)
+{
+ char* name = BNGetVariableNameOrDefault(m_object, &var);
+ string result = name;
+ BNFreeString(name);
+ return result;
+}
+
+
+string Function::GetLastSeenVariableNameOrDefault(const Variable& var)
+{
+ char* name = BNGetLastSeenVariableNameOrDefault(m_object, &var);
+ string result = name;
+ BNFreeString(name);
+ return result;
+}
+
+
void Function::SetAutoIndirectBranches(
Architecture* sourceArch, uint64_t source, const std::vector<ArchAndAddr>& branches)
{
@@ -2518,6 +2536,81 @@ void Function::SetVariableDeadStoreElimination(const Variable& var, BNDeadStoreE
}
+std::map<Variable, std::set<Variable>> Function::GetMergedVariables()
+{
+ size_t count;
+ BNMergedVariable* mergedVars = BNGetMergedVariables(m_object, &count);
+
+ std::map<Variable, std::set<Variable>> result;
+ for (size_t i = 0; i < count; i++)
+ {
+ Variable target;
+ target.type = mergedVars[i].target.type;
+ target.index = mergedVars[i].target.index;
+ target.storage = mergedVars[i].target.storage;
+
+ set<Variable> sources;
+ for (size_t j = 0; j < mergedVars[i].sourceCount; j++)
+ {
+ Variable source;
+ source.type = mergedVars[i].sources[j].type;
+ source.index = mergedVars[i].sources[j].index;
+ source.storage = mergedVars[i].sources[j].storage;
+ sources.insert(source);
+ }
+
+ result[target] = sources;
+ }
+
+ BNFreeMergedVariableList(mergedVars, count);
+ return result;
+}
+
+
+void Function::MergeVariables(const Variable& target, const std::set<Variable>& sources)
+{
+ BNVariable targetData;
+ targetData.type = target.type;
+ targetData.index = target.index;
+ targetData.storage = target.storage;
+
+ BNVariable* sourceData = new BNVariable[sources.size()];
+ size_t i = 0;
+ for (auto& var : sources)
+ {
+ sourceData[i].type = var.type;
+ sourceData[i].index = var.index;
+ sourceData[i].storage = var.storage;
+ i++;
+ }
+
+ BNMergeVariables(m_object, &targetData, sourceData, sources.size());
+ delete[] sourceData;
+}
+
+
+void Function::UnmergeVariables(const Variable& target, const std::set<Variable>& sources)
+{
+ BNVariable targetData;
+ targetData.type = target.type;
+ targetData.index = target.index;
+ targetData.storage = target.storage;
+
+ BNVariable* sourceData = new BNVariable[sources.size()];
+ size_t i = 0;
+ for (auto& var : sources)
+ {
+ sourceData[i].type = var.type;
+ sourceData[i].index = var.index;
+ sourceData[i].storage = var.storage;
+ i++;
+ }
+
+ BNUnmergeVariables(m_object, &targetData, sourceData, sources.size());
+ delete[] sourceData;
+}
+
+
vector<ILReferenceSource> Function::GetMediumLevelILVariableReferences(const Variable& var)
{
size_t count;
diff --git a/mediumlevelil.cpp b/mediumlevelil.cpp
index 71604caf..6827c356 100644
--- a/mediumlevelil.cpp
+++ b/mediumlevelil.cpp
@@ -847,3 +847,17 @@ Ref<FlowGraph> MediumLevelILFunction::CreateFunctionGraph(DisassemblySettings* s
BNFlowGraph* graph = BNCreateMediumLevelILFunctionGraph(m_object, settings ? settings->GetObject() : nullptr);
return new CoreFlowGraph(graph);
}
+
+
+set<size_t> MediumLevelILFunction::GetLiveInstructionsForVariable(const Variable& var, bool includeLastUse)
+{
+ size_t count;
+ size_t* instrs = BNGetMediumLevelILLiveInstructionsForVariable(m_object, &var, includeLastUse, &count);
+
+ set<size_t> result;
+ for (size_t i = 0; i < count; i++)
+ result.insert(instrs[i]);
+
+ BNFreeILInstructionList(instrs);
+ return result;
+}
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:
diff --git a/suite/testcommon.py b/suite/testcommon.py
index a2c77037..f77d3cf6 100644
--- a/suite/testcommon.py
+++ b/suite/testcommon.py
@@ -1447,6 +1447,51 @@ class TestBuilder(Builder):
assert code1 != code2
assert text1 != text2, f"{asm1} and {asm2} are different but both disassemble to {text1}"
+ def test_merge_vars(self):
+ """Variable merging produced different output"""
+ file_name = self.unpackage_file("array_test.bndb")
+ try:
+ with binja.BinaryViewType.get_view_of_file(file_name) as bv:
+ func = bv.get_function_at(0x100003920)
+ target = None
+ sources = []
+ for var in func.vars:
+ if var.storage == -0x758:
+ target = var
+ func.delete_user_var(var)
+ if var.storage in [-0x760, -0x768, -0x778]:
+ sources.append(var)
+ func.delete_user_var(var)
+
+ func.merge_vars(target, sources)
+ bv.update_analysis_and_wait()
+
+ retinfo = ["HLIL after merge: " + x for x in str(func.hlil).split("\n")]
+
+ sources = sources[1:]
+ func.unmerge_vars(target, sources)
+ bv.update_analysis_and_wait()
+
+ retinfo += ["HLIL after unmerge: " + x for x in str(func.hlil).split("\n")]
+ return retinfo
+ finally:
+ self.delete_package("array_test.bndb")
+
+ def test_live_instrs_for_var(self):
+ """Live instructions for variable produced different output"""
+ file_name = self.unpackage_file("array_test.bndb")
+ try:
+ with binja.BinaryViewType.get_view_of_file(file_name) as bv:
+ func = bv.get_function_at(0x100003920)
+ retinfo = []
+ for var in func.vars:
+ instrs = func.mlil.get_live_instructions_for_var(var)
+ for instr in instrs:
+ retinfo += [f"MLIL live instr for {var}: {repr(instr)}"]
+ return retinfo
+ finally:
+ self.delete_package("array_test.bndb")
+
class VerifyBuilder(Builder):
""" The VerifyBuilder is for tests that verify
diff --git a/ui/flowgraphwidget.h b/ui/flowgraphwidget.h
index f6a38a85..f4751dea 100644
--- a/ui/flowgraphwidget.h
+++ b/ui/flowgraphwidget.h
@@ -338,6 +338,7 @@ class BINARYNINJAUIAPI FlowGraphWidget :
void tagAddress();
void tagAddressAccepted(TagTypeRef tt);
void manageAddressTags();
+ void mergeVariables();
void convertToNop();
void alwaysBranch();
diff --git a/ui/linearview.h b/ui/linearview.h
index 6bec459c..38d8dfaf 100644
--- a/ui/linearview.h
+++ b/ui/linearview.h
@@ -66,7 +66,7 @@ class BINARYNINJAUIAPI LinearViewHistoryEntry : public HistoryEntry
bool m_inFunc = false;
HighlightTokenState m_highlight;
- public:
+public:
const std::vector<BinaryNinja::LinearViewObjectIdentifier>& getTopPath() const { return m_topPath; }
size_t getTopLineIndex() const { return m_topLineIndex; }
uint64_t getTopAddress() const { return m_topAddr; }
@@ -99,22 +99,22 @@ class BINARYNINJAUIAPI LinearView : public QAbstractScrollArea, public View, pub
class LinearViewOptionsWidget : public MenuHelper
{
- public:
+ public:
LinearViewOptionsWidget(LinearView* parent);
- protected:
+ protected:
virtual void showMenu();
- private:
+ private:
LinearView* m_view;
};
class LinearViewOptionsIconWidget : public QWidget
{
- public:
+ public:
LinearViewOptionsIconWidget(LinearView* parent);
- private:
+ private:
LinearView* m_view;
ContextMenuManager* m_contextMenuManager;
Menu m_menu;
@@ -124,11 +124,11 @@ class BINARYNINJAUIAPI LinearView : public QAbstractScrollArea, public View, pub
class LinearViewStatusBarWidget : public StatusBarWidget
{
- public:
+ public:
LinearViewStatusBarWidget(LinearView* parent);
virtual void updateStatus() override;
- private:
+ private:
LinearView* m_view;
LinearViewOptionsWidget* m_options;
ILChooserWidget* m_chooser;
@@ -199,7 +199,7 @@ class BINARYNINJAUIAPI LinearView : public QAbstractScrollArea, public View, pub
void refreshAtCurrentLocation(bool cursorFixup = false);
bool navigateToAddress(uint64_t addr, bool center, bool updateHighlight, bool navByRef = false);
bool navigateToLine(
- FunctionRef func, uint64_t offset, size_t instrIndex, bool center, bool updateHighlight, bool navByRef = false);
+ FunctionRef func, uint64_t offset, size_t instrIndex, bool center, bool updateHighlight, bool navByRef = false);
bool navigateToGotoLabel(uint64_t label);
void viewData();
@@ -209,7 +209,7 @@ class BINARYNINJAUIAPI LinearView : public QAbstractScrollArea, public View, pub
static void addOptionsMenuActions(Menu& menu);
void getHexDumpLineBytes(
- const BinaryNinja::LinearDisassemblyLine& line, size_t& skippedBytes, size_t& totalBytes, size_t& totalCols);
+ const BinaryNinja::LinearDisassemblyLine& line, size_t& skippedBytes, size_t& totalBytes, size_t& totalCols);
void paintHexDumpLine(QPainter& p, const LinearViewLine& line, int xoffset, int y, uint32_t addrLen, int tagOffset);
void paintAnalysisWarningLine(QPainter& p, const LinearViewLine& line, int xoffset, int y);
@@ -223,9 +223,9 @@ class BINARYNINJAUIAPI LinearView : public QAbstractScrollArea, public View, pub
TypeRef createStructure(BinaryNinja::QualifiedName& name, uint64_t size);
TypeRef getInnerType(TypeRef type, uint64_t offset, uint64_t size, std::set<TypeRef>& seen);
StructureRef defineInnerType(
- TypeRef type, TypeRef baseType, uint64_t offset, uint64_t size, std::set<TypeRef>& seen);
+ TypeRef type, TypeRef baseType, uint64_t offset, uint64_t size, std::set<TypeRef>& seen);
StructureRef defineInnerPointer(TypeRef type, ArchitectureRef arch, uint64_t baseAddress, uint64_t offset,
- uint64_t size, std::set<TypeRef>& seen);
+ uint64_t size, std::set<TypeRef>& seen);
StructureRef defineInnerStruct(TypeRef type, uint64_t offset, uint64_t size, std::set<TypeRef>& seen);
StructureRef defineInnerArray(TypeRef type, uint64_t offset, uint64_t size, std::set<TypeRef>& seen);
StructureRef defineInnerName(TypeRef type, uint64_t offset, uint64_t size, std::set<TypeRef>& seen);
@@ -239,7 +239,7 @@ class BINARYNINJAUIAPI LinearView : public QAbstractScrollArea, public View, pub
bool updateCursor(LinearViewCursorPosition& cursorToUpdate, BinaryNinja::LinearViewCursor* matched, bool fullMatch);
bool updateCursor(LinearViewCursorPosition& cursorToUpdate, BinaryNinja::LinearViewCursor* newCursor);
bool updateCursor(LinearViewCursorPosition& cursorToUpdate,
- const std::vector<BinaryNinja::LinearViewObjectIdentifier>& path, BinaryNinja::LinearViewCursor* newCursor);
+ const std::vector<BinaryNinja::LinearViewObjectIdentifier>& path, BinaryNinja::LinearViewCursor* newCursor);
uint64_t getOrderingIndexForLine(const LinearViewLine& line);
void updateAnalysisRequestorsForCache();
@@ -250,13 +250,13 @@ class BINARYNINJAUIAPI LinearView : public QAbstractScrollArea, public View, pub
BNAnalysisWarningActionType getAnalysisWarningActionAtPos(const LinearViewLine& line, int x);
void getCurrentOffsetByTypeInternal(
- TypeRef resType, uint64_t baseAddr, uint64_t& begin, uint64_t& end, bool singleLine, std::set<TypeRef>& seen);
+ TypeRef resType, uint64_t baseAddr, uint64_t& begin, uint64_t& end, bool singleLine, std::set<TypeRef>& seen);
BNDeadStoreElimination getCurrentVariableDeadStoreElimination();
void setDataButtonVisible(bool visible);
- private Q_SLOTS:
+private Q_SLOTS:
void adjustSize(int width, int height);
void viewInHexEditor();
void viewInGraph();
@@ -310,6 +310,7 @@ class BINARYNINJAUIAPI LinearView : public QAbstractScrollArea, public View, pub
void createArray();
void createStruct();
void createNewTypes();
+ void mergeVariables();
//! Get the length of of the string (if there is one) starting at the
//! given address. String type is assumed to be UTF-8 by default, but the
@@ -335,10 +336,10 @@ class BINARYNINJAUIAPI LinearView : public QAbstractScrollArea, public View, pub
void setCurrentVariableDeadStoreElimination(BNDeadStoreElimination elimination);
- Q_SIGNALS:
+Q_SIGNALS:
void notifyResizeEvent(int width, int height);
- public:
+public:
explicit LinearView(BinaryViewRef data, ViewFrame* view);
virtual ~LinearView();
@@ -423,7 +424,7 @@ class BINARYNINJAUIAPI LinearView : public QAbstractScrollArea, public View, pub
static void registerActions();
- protected:
+protected:
virtual void resizeEvent(QResizeEvent* event) override;
virtual void paintEvent(QPaintEvent* event) override;
virtual void wheelEvent(QWheelEvent* event) override;
@@ -452,7 +453,7 @@ class LinearViewType : public ViewType
{
static LinearViewType* m_instance;
- public:
+public:
LinearViewType();
virtual int getPriority(BinaryViewRef data, const QString& filename) override;
virtual QWidget* create(BinaryViewRef data, ViewFrame* viewFrame) override;
diff --git a/ui/mergevariablesdialog.h b/ui/mergevariablesdialog.h
new file mode 100644
index 00000000..b4c81c06
--- /dev/null
+++ b/ui/mergevariablesdialog.h
@@ -0,0 +1,76 @@
+#pragma once
+
+#include <QtWidgets/QDialog>
+#include <QtWidgets/QComboBox>
+#include <QtWidgets/QListWidget>
+#include <QtWidgets/QListWidgetItem>
+#include <QtWidgets/QStyledItemDelegate>
+
+#include <string>
+#include "binaryninjaapi.h"
+#include "uicontext.h"
+#include "render.h"
+
+class BINARYNINJAUIAPI MergeVariableHeader : public QWidget
+{
+ std::vector<BinaryNinja::InstructionTextToken> m_tokens;
+ RenderContext m_renderContext;
+ int m_length;
+
+protected:
+ void paintEvent(QPaintEvent* event) override;
+ QSize sizeHint() const override;
+
+public:
+ MergeVariableHeader(const std::vector<BinaryNinja::InstructionTextToken>& tokens, QWidget* parent = nullptr);
+};
+
+class BINARYNINJAUIAPI MergeVariableListItem : public QListWidgetItem
+{
+ QWidget* m_owner;
+ FunctionRef m_func;
+ BinaryNinja::Variable m_var;
+ std::string m_name;
+ BinaryNinja::Confidence<BinaryNinja::Ref<BinaryNinja::Type>> m_type;
+ bool m_grayed;
+
+public:
+ MergeVariableListItem(QWidget* parent, BinaryNinja::Function* func, const BinaryNinja::Variable& var,
+ const std::string& name, BinaryNinja::Confidence<BinaryNinja::Ref<BinaryNinja::Type>> type,
+ const QString& warnings, bool grayed);
+ const BinaryNinja::Variable& variable() const { return m_var; }
+ virtual QVariant data(int role) const override;
+
+ static std::vector<BinaryNinja::InstructionTextToken> tokensForVariable(BinaryNinja::Function* func,
+ const BinaryNinja::Variable& var, BinaryNinja::Confidence<BinaryNinja::Ref<BinaryNinja::Type>> type,
+ const std::string& name);
+};
+
+class BINARYNINJAUIAPI MergeVariableItemDelegate : public QStyledItemDelegate
+{
+ Q_OBJECT
+
+ QWidget* m_parent;
+ QFont m_font;
+ int m_baseline, m_charWidth, m_charHeight, m_charOffset;
+
+public:
+ MergeVariableItemDelegate(QWidget* parent);
+
+ void updateFonts();
+ virtual QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& idx) const override;
+ virtual void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& idx) const override;
+};
+
+class BINARYNINJAUIAPI MergeVariablesDialog : public QDialog
+{
+ Q_OBJECT
+ QListWidget* m_list;
+ std::set<BinaryNinja::Variable> m_existingVariables;
+
+public:
+ MergeVariablesDialog(QWidget* parent, FunctionRef func, BinaryNinja::Variable target);
+
+ std::set<BinaryNinja::Variable> mergedVariables();
+ std::set<BinaryNinja::Variable> unmergedVariables();
+};
diff --git a/ui/uitypes.h b/ui/uitypes.h
index f16a84eb..b47444e4 100644
--- a/ui/uitypes.h
+++ b/ui/uitypes.h
@@ -6,7 +6,7 @@
// there are changes to the API that affect linking, including new functions,
// new types, modifications to existing functions or types, or new versions
// of the Qt libraries.
-#define BN_CURRENT_UI_ABI_VERSION 5
+#define BN_CURRENT_UI_ABI_VERSION 6
// Minimum ABI version that is supported for loading of plugins. Plugins that
// are linked to an ABI version less than this will not be able to load and