Skip to content

Commit 72b8df3

Browse files
committed
Add UI action and API to control expression folding
1 parent 6248965 commit 72b8df3

File tree

11 files changed

+70
-2
lines changed

11 files changed

+70
-2
lines changed

binaryninjaapi.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11423,6 +11423,9 @@ namespace BinaryNinja {
1142311423
BNDeadStoreElimination GetVariableDeadStoreElimination(const Variable& var);
1142411424
void SetVariableDeadStoreElimination(const Variable& var, BNDeadStoreElimination mode);
1142511425

11426+
BNExprFolding GetExprFolding(uint64_t addr);
11427+
void SetExprFolding(uint64_t addr, BNExprFolding mode);
11428+
1142611429
std::map<Variable, std::set<Variable>> GetMergedVariables();
1142711430
void MergeVariables(const Variable& target, const std::set<Variable>& sources);
1142811431
void UnmergeVariables(const Variable& target, const std::set<Variable>& sources);

binaryninjacore.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1021,6 +1021,9 @@ extern "C"
10211021

10221022
// MLIL instruction appears to be an otherwise unused intermediate
10231023
MLILPossiblyUnusedIntermediate = 0x80,
1024+
1025+
// HLIL expression can be folded into other expressions or has been folded
1026+
HLILFoldableExpr = 0x100,
10241027
} BNILInstructionAttribute;
10251028

10261029
typedef enum BNIntrinsicClass
@@ -3226,6 +3229,13 @@ extern "C"
32263229
AllowDeadStoreElimination
32273230
} BNDeadStoreElimination;
32283231

3232+
typedef enum BNExprFolding
3233+
{
3234+
DefaultExprFolding,
3235+
PreventExprFolding,
3236+
AllowExprFolding
3237+
} BNExprFolding;
3238+
32293239
typedef struct BNDebugFunctionInfo
32303240
{
32313241
char* shortName;
@@ -4979,6 +4989,8 @@ extern "C"
49794989
BNFunction* func, const BNVariable* var);
49804990
BINARYNINJACOREAPI void BNSetFunctionVariableDeadStoreElimination(
49814991
BNFunction* func, const BNVariable* var, BNDeadStoreElimination mode);
4992+
BINARYNINJACOREAPI BNExprFolding BNGetExprFolding(BNFunction* func, uint64_t addr);
4993+
BINARYNINJACOREAPI void BNSetExprFolding(BNFunction* func, uint64_t addr, BNExprFolding mode);
49824994
BINARYNINJACOREAPI BNMergedVariable* BNGetMergedVariables(BNFunction* func, size_t* count);
49834995
BINARYNINJACOREAPI void BNFreeMergedVariableList(BNMergedVariable* vars, size_t count);
49844996
BINARYNINJACOREAPI void BNMergeVariables(BNFunction* func, const BNVariable* target, const BNVariable* sources,

docs/guide/index.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -807,6 +807,28 @@ Performing this action on both variables in the example results in the following
807807

808808
![Dead Store Elimination Results](../img/dead-store-after.png "Dead Store Elimination Results"){ width="500" }
809809

810+
## Expression Folding
811+
812+
Binary Ninja automatically performs optimization passes on high level code. One of these optimizations is to fold
813+
assignments with a single use into the statement that uses it. An example of a function call being folded into another
814+
is shown below:
815+
816+
![Expression Folding](../img/folding-before.png "Expression Folding"){ width="500" }
817+
818+
Binary Ninja uses heuristics to determine if this will improve readability, but sometimes it doesn't make the preferred
819+
choice. You can override the heuristic by right-clicking an expression and choosing "Allow" or "Prevent" from the "
820+
Expression Folding" submenu.
821+
822+
![Expression Folding Menu](../img/folding-menu.png "Expression Folding Menu"){ width="500" }
823+
824+
This option will only appear if the expression can be folded. If Binary Ninja's analysis determines that it is not sound
825+
to fold an expression, the submenu will not be present.
826+
827+
Choosing "Prevent" from the "Expression Folding" menu on the call to `_strlen` in the example above results in the
828+
following output:
829+
830+
![Expression Folding Results](../img/folding-after.png "Expression Folding Results"){ width="500" }
831+
810832
## Merging and Splitting Variables
811833

812834
Binary Ninja automatically splits all variables that the analysis determines to be safely splittable. This allows the user to assign different types to different uses of the same register or memory location. Sometimes, however, the code is more clear if two or more of these variables are merged together into a single variable.

docs/img/folding-after.png

35.8 KB
Loading

docs/img/folding-before.png

36.1 KB
Loading

docs/img/folding-menu.png

211 KB
Loading

function.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2813,6 +2813,18 @@ void Function::SetVariableDeadStoreElimination(const Variable& var, BNDeadStoreE
28132813
}
28142814

28152815

2816+
BNExprFolding Function::GetExprFolding(uint64_t addr)
2817+
{
2818+
return BNGetExprFolding(m_object, addr);
2819+
}
2820+
2821+
2822+
void Function::SetExprFolding(uint64_t addr, BNExprFolding mode)
2823+
{
2824+
BNSetExprFolding(m_object, addr, mode);
2825+
}
2826+
2827+
28162828
std::map<Variable, std::set<Variable>> Function::GetMergedVariables()
28172829
{
28182830
size_t count;

python/function.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
from .enums import (
3030
AnalysisSkipReason, FunctionGraphType, SymbolType, InstructionTextTokenType, HighlightStandardColor,
3131
HighlightColorStyle, DisassemblyOption, IntegerDisplayType, FunctionAnalysisSkipOverride, FunctionUpdateType,
32-
BuiltinType
32+
BuiltinType, ExprFolding
3333
)
3434

3535
from . import associateddatastore # Required in the main scope due to being an argument for _FunctionAssociatedDataStore
@@ -3456,6 +3456,16 @@ def auto_metadata(self) -> Dict[str, 'metadata.MetadataValueType']:
34563456
assert isinstance(value, dict), "core.BNFunctionGetAutoMetadata did not return a dict"
34573457
return value
34583458

3459+
def get_expr_folding(self, addr: Union[int, highlevelil.HighLevelILInstruction]) -> ExprFolding:
3460+
if isinstance(addr, highlevelil.HighLevelILInstruction):
3461+
addr = addr.address
3462+
return ExprFolding(core.BNGetExprFolding(self.handle, addr))
3463+
3464+
def set_expr_folding(self, addr: Union[int, highlevelil.HighLevelILInstruction], value: ExprFolding):
3465+
if isinstance(addr, highlevelil.HighLevelILInstruction):
3466+
addr = addr.address
3467+
core.BNSetExprFolding(self.handle, addr, value)
3468+
34593469

34603470
class AdvancedFunctionAnalysisDataRequestor:
34613471
def __init__(self, func: Optional['Function'] = None):

ui/commands.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,9 @@ std::optional<std::string> GetVariableNameFromExpr(BinaryNinja::Function* func,
9595
// typedef-ed type
9696
TypeRef GetFunctionType(BinaryViewRef data, TypeRef type);
9797

98+
std::optional<uint64_t> getFoldableExprAddress(
99+
BinaryNinja::HighLevelILFunction* hlil, const HighlightTokenState& highlight);
100+
98101
/*!
99-
@}
102+
@}
100103
*/

ui/flowgraphwidget.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,8 @@ class BINARYNINJAUIAPI FlowGraphWidget :
166166
void recenterUpdatedGraph(FlowGraphRef oldGraph, int oldXOfs, int oldYOfs);
167167

168168
BNDeadStoreElimination getCurrentVariableDeadStoreElimination();
169+
std::optional<uint64_t> getCurrentFoldableExprAddress();
170+
BNExprFolding getCurrentExprFolding();
169171
std::optional<std::pair<BinaryNinja::Variable, BinaryNinja::Variable>> getMergeVariablesAtCurrentLocation();
170172

171173
protected:
@@ -395,6 +397,7 @@ class BINARYNINJAUIAPI FlowGraphWidget :
395397
void instrEditDoneEvent();
396398

397399
void setCurrentVariableDeadStoreElimination(BNDeadStoreElimination elimination);
400+
void setCurrentExprFolding(BNExprFolding folding);
398401
void splitToNewTabAndNavigateFromCursorPosition();
399402
void splitToNewWindowAndNavigateFromCursorPosition();
400403
void splitToNewPaneAndNavigateFromCursorPosition();

0 commit comments

Comments
 (0)