angr 9.2.150__py3-none-manylinux2014_aarch64.whl → 9.2.153__py3-none-manylinux2014_aarch64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of angr might be problematic. Click here for more details.
- angr/__init__.py +1 -1
- angr/analyses/calling_convention/calling_convention.py +17 -9
- angr/analyses/cfg/cfg_base.py +1 -1
- angr/analyses/cfg/cfg_fast.py +39 -0
- angr/analyses/decompiler/ail_simplifier.py +0 -1
- angr/analyses/decompiler/ccall_rewriters/amd64_ccalls.py +39 -0
- angr/analyses/decompiler/clinic.py +118 -2
- angr/analyses/decompiler/dephication/rewriting_engine.py +38 -1
- angr/analyses/decompiler/optimization_passes/condition_constprop.py +6 -0
- angr/analyses/decompiler/optimization_passes/engine_base.py +5 -0
- angr/analyses/decompiler/peephole_optimizations/__init__.py +2 -0
- angr/analyses/decompiler/peephole_optimizations/cas_intrinsics.py +115 -0
- angr/analyses/decompiler/region_identifier.py +171 -119
- angr/analyses/decompiler/ssailification/rewriting_engine.py +37 -1
- angr/analyses/decompiler/ssailification/traversal_engine.py +10 -1
- angr/analyses/reaching_definitions/engine_ail.py +20 -0
- angr/analyses/s_propagator.py +28 -0
- angr/analyses/smc.py +3 -1
- angr/analyses/stack_pointer_tracker.py +2 -1
- angr/analyses/typehoon/simple_solver.py +143 -81
- angr/analyses/typehoon/typehoon.py +2 -1
- angr/analyses/variable_recovery/engine_ail.py +9 -0
- angr/engines/light/engine.py +7 -0
- angr/knowledge_plugins/functions/function.py +10 -4
- angr/storage/memory_mixins/clouseau_mixin.py +7 -1
- angr/utils/graph.py +10 -12
- angr/utils/ssa/__init__.py +6 -1
- {angr-9.2.150.dist-info → angr-9.2.153.dist-info}/METADATA +6 -6
- {angr-9.2.150.dist-info → angr-9.2.153.dist-info}/RECORD +33 -32
- {angr-9.2.150.dist-info → angr-9.2.153.dist-info}/WHEEL +1 -1
- {angr-9.2.150.dist-info → angr-9.2.153.dist-info}/entry_points.txt +0 -0
- {angr-9.2.150.dist-info → angr-9.2.153.dist-info}/licenses/LICENSE +0 -0
- {angr-9.2.150.dist-info → angr-9.2.153.dist-info}/top_level.txt +0 -0
angr/__init__.py
CHANGED
|
@@ -21,6 +21,7 @@ from angr.calling_conventions import (
|
|
|
21
21
|
default_cc,
|
|
22
22
|
SimCCMicrosoftThiscall,
|
|
23
23
|
)
|
|
24
|
+
from angr.errors import SimTranslationError
|
|
24
25
|
from angr.sim_type import (
|
|
25
26
|
SimTypeCppFunction,
|
|
26
27
|
SimTypeInt,
|
|
@@ -585,16 +586,23 @@ class CallingConventionAnalysis(Analysis):
|
|
|
585
586
|
# include its successor.
|
|
586
587
|
|
|
587
588
|
# Re-lift the target block
|
|
588
|
-
|
|
589
|
+
dst_block_size = func.get_block_size(dst.addr)
|
|
590
|
+
if dst_block_size is not None and dst_block_size > 0:
|
|
591
|
+
dst_bb = self.project.factory.block(dst.addr, dst_block_size, opt_level=1)
|
|
592
|
+
try:
|
|
593
|
+
vex_block = dst_bb.vex
|
|
594
|
+
except SimTranslationError:
|
|
595
|
+
# failed to lift the block
|
|
596
|
+
continue
|
|
589
597
|
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
+
# If there is only one 'IMark' statement in vex --> the target block contains only direct jump
|
|
599
|
+
if (
|
|
600
|
+
len(vex_block.statements) == 1
|
|
601
|
+
and vex_block.statements[0].tag == "Ist_IMark"
|
|
602
|
+
and func.graph.out_degree(dst) == 1
|
|
603
|
+
):
|
|
604
|
+
for _, jmp_dst, jmp_data in func_graph.out_edges(dst, data=True):
|
|
605
|
+
subgraph.add_edge(dst, jmp_dst, **jmp_data)
|
|
598
606
|
|
|
599
607
|
return subgraph
|
|
600
608
|
|
angr/analyses/cfg/cfg_base.py
CHANGED
|
@@ -1515,7 +1515,7 @@ class CFGBase(Analysis):
|
|
|
1515
1515
|
Revisit the entire control flow graph, create Function instances accordingly, and correctly put blocks into
|
|
1516
1516
|
each function.
|
|
1517
1517
|
|
|
1518
|
-
Although Function objects are
|
|
1518
|
+
Although Function objects are created during the CFG recovery, they are neither sound nor accurate. With a
|
|
1519
1519
|
pre-constructed CFG, this method rebuilds all functions bearing the following rules:
|
|
1520
1520
|
|
|
1521
1521
|
- A block may only belong to one function.
|
angr/analyses/cfg/cfg_fast.py
CHANGED
|
@@ -1554,6 +1554,45 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
|
|
|
1554
1554
|
}:
|
|
1555
1555
|
func.info["is_alloca_probe"] = True
|
|
1556
1556
|
|
|
1557
|
+
# determine if the function is _guard_xfg_dispatch_icall_nop or _guard_xfg_dispatch_icall_fptr
|
|
1558
|
+
if func is not None and not func.is_simprocedure and len(func.block_addrs_set) in {1, 2}:
|
|
1559
|
+
# _guard_xfg_dispatch_icall_nop jumps to _guard_xfg_dispatch_icall_fptr, but we may or may not identify
|
|
1560
|
+
# _guard_xfg_dispatch_icall_fptr as a separate function.
|
|
1561
|
+
# so, two possibilities:
|
|
1562
|
+
# - _guard_xfg_dispatch_icall_nop is a function with one block and jumps to
|
|
1563
|
+
# _guard_xfg_dispatch_icall_fptr.
|
|
1564
|
+
# - _guard_xfg_dispatch_icall_nop is a function with 2 blocks, and the second block is the body of
|
|
1565
|
+
# _guard_xfg_dispatch_icall_fptr.
|
|
1566
|
+
try:
|
|
1567
|
+
block = func.get_block(func.addr)
|
|
1568
|
+
except SimTranslationError:
|
|
1569
|
+
block = None
|
|
1570
|
+
if block is not None and block.instructions == 1:
|
|
1571
|
+
insn = block.capstone.insns[0]
|
|
1572
|
+
if block.bytes == b"\xff\xe0":
|
|
1573
|
+
func.info["jmp_rax"] = True
|
|
1574
|
+
elif (
|
|
1575
|
+
insn.mnemonic == "jmp"
|
|
1576
|
+
and insn.operands[0].type == capstone.x86.X86_OP_MEM
|
|
1577
|
+
and insn.operands[0].mem.base == capstone.x86.X86_REG_RIP
|
|
1578
|
+
and insn.operands[0].mem.disp > 0
|
|
1579
|
+
and insn.operands[0].mem.index == 0
|
|
1580
|
+
):
|
|
1581
|
+
# where is it jumping to?
|
|
1582
|
+
jumpout_targets = list(self.graph.successors(self.model.get_any_node(func.addr)))
|
|
1583
|
+
if len(jumpout_targets) == 1:
|
|
1584
|
+
jumpout_target = jumpout_targets[0].addr
|
|
1585
|
+
if len(func.block_addrs_set) == 1 and len(func.jumpout_sites) == 1:
|
|
1586
|
+
if (
|
|
1587
|
+
self.kb.functions.contains_addr(jumpout_target)
|
|
1588
|
+
and self.kb.functions.get_by_addr(jumpout_target).get_block(jumpout_target).bytes
|
|
1589
|
+
== b"\xff\xe0"
|
|
1590
|
+
):
|
|
1591
|
+
func.info["jmp_rax"] = True
|
|
1592
|
+
elif len(func.block_addrs_set) == 2 and func.get_block(jumpout_target).bytes == b"\xff\xe0":
|
|
1593
|
+
# check the second block and ensure it's jmp rax
|
|
1594
|
+
func.info["jmp_rax"] = True
|
|
1595
|
+
|
|
1557
1596
|
elif self.project.arch.name == "X86":
|
|
1558
1597
|
# determine if the function is __alloca_probe
|
|
1559
1598
|
func = self.kb.functions.get_by_addr(func_addr) if self.kb.functions.contains_addr(func_addr) else None
|
|
@@ -203,7 +203,6 @@ class AILSimplifier(Analysis):
|
|
|
203
203
|
AILGraphWalker(self.func_graph, _handler, replace_nodes=True).walk()
|
|
204
204
|
self.blocks = {}
|
|
205
205
|
|
|
206
|
-
@timethis
|
|
207
206
|
def _compute_reaching_definitions(self) -> SRDAModel:
|
|
208
207
|
# Computing reaching definitions or return the cached one
|
|
209
208
|
if self._reaching_definitions is not None:
|
|
@@ -412,6 +412,45 @@ class AMD64CCallRewriter(CCallRewriterBase):
|
|
|
412
412
|
)
|
|
413
413
|
return Expr.Convert(None, r.bits, ccall.bits, False, r, **ccall.tags)
|
|
414
414
|
|
|
415
|
+
elif (
|
|
416
|
+
cond_v == AMD64_CondTypes["CondNS"]
|
|
417
|
+
and op_v
|
|
418
|
+
in {
|
|
419
|
+
AMD64_OpTypes["G_CC_OP_LOGICB"],
|
|
420
|
+
AMD64_OpTypes["G_CC_OP_LOGICW"],
|
|
421
|
+
AMD64_OpTypes["G_CC_OP_LOGICL"],
|
|
422
|
+
AMD64_OpTypes["G_CC_OP_LOGICQ"],
|
|
423
|
+
}
|
|
424
|
+
and isinstance(dep_2, Expr.Const)
|
|
425
|
+
and dep_2.value == 0
|
|
426
|
+
):
|
|
427
|
+
# dep_1 >= 0
|
|
428
|
+
dep_1 = self._fix_size(
|
|
429
|
+
dep_1,
|
|
430
|
+
op_v,
|
|
431
|
+
AMD64_OpTypes["G_CC_OP_LOGICB"],
|
|
432
|
+
AMD64_OpTypes["G_CC_OP_LOGICW"],
|
|
433
|
+
AMD64_OpTypes["G_CC_OP_LOGICL"],
|
|
434
|
+
ccall.tags,
|
|
435
|
+
)
|
|
436
|
+
dep_2 = self._fix_size(
|
|
437
|
+
dep_2,
|
|
438
|
+
op_v,
|
|
439
|
+
AMD64_OpTypes["G_CC_OP_LOGICB"],
|
|
440
|
+
AMD64_OpTypes["G_CC_OP_LOGICW"],
|
|
441
|
+
AMD64_OpTypes["G_CC_OP_LOGICL"],
|
|
442
|
+
ccall.tags,
|
|
443
|
+
)
|
|
444
|
+
|
|
445
|
+
r = Expr.BinaryOp(
|
|
446
|
+
ccall.idx,
|
|
447
|
+
"CmpGE",
|
|
448
|
+
(dep_1, dep_2),
|
|
449
|
+
True,
|
|
450
|
+
**ccall.tags,
|
|
451
|
+
)
|
|
452
|
+
return Expr.Convert(None, r.bits, ccall.bits, False, r, **ccall.tags)
|
|
453
|
+
|
|
415
454
|
elif ccall.callee == "amd64g_calculate_rflags_c":
|
|
416
455
|
# calculate the carry flag
|
|
417
456
|
op = ccall.operands[0]
|
|
@@ -483,6 +483,11 @@ class Clinic(Analysis):
|
|
|
483
483
|
arg_vvars = self._create_function_argument_vvars(arg_list)
|
|
484
484
|
func_args = {arg_vvar for arg_vvar, _ in arg_vvars.values()}
|
|
485
485
|
|
|
486
|
+
# duplicate orphaned conditional jump blocks
|
|
487
|
+
ail_graph = self._duplicate_orphaned_cond_jumps(ail_graph)
|
|
488
|
+
# rewrite jmp_rax function calls
|
|
489
|
+
ail_graph = self._rewrite_jump_rax_calls(ail_graph)
|
|
490
|
+
|
|
486
491
|
# Transform the graph into partial SSA form
|
|
487
492
|
self._update_progress(35.0, text="Transforming to partial-SSA form")
|
|
488
493
|
ail_graph = self._transform_to_ssa_level0(ail_graph, func_args)
|
|
@@ -927,7 +932,7 @@ class Clinic(Analysis):
|
|
|
927
932
|
self.kb.callsite_prototypes.set_prototype(callsite.addr, cc.cc, cc.prototype, manual=False)
|
|
928
933
|
if func_graph is not None and cc.prototype.returnty is not None:
|
|
929
934
|
# patch the AIL call statement if we can find one
|
|
930
|
-
callsite_ail_block: ailment.Block = next(
|
|
935
|
+
callsite_ail_block: ailment.Block | None = next(
|
|
931
936
|
iter(bb for bb in func_graph if bb.addr == callsite.addr), None
|
|
932
937
|
)
|
|
933
938
|
if callsite_ail_block is not None and callsite_ail_block.statements:
|
|
@@ -1002,6 +1007,7 @@ class Clinic(Analysis):
|
|
|
1002
1007
|
:return: None
|
|
1003
1008
|
"""
|
|
1004
1009
|
assert self._func_graph is not None
|
|
1010
|
+
assert self._blocks_by_addr_and_size is not None
|
|
1005
1011
|
|
|
1006
1012
|
for block_node in self._func_graph.nodes():
|
|
1007
1013
|
ail_block = self._convert(block_node)
|
|
@@ -1892,6 +1898,19 @@ class Clinic(Analysis):
|
|
|
1892
1898
|
self._link_variables_on_expr(variable_manager, global_variables, block, stmt_idx, stmt, stmt.dst)
|
|
1893
1899
|
self._link_variables_on_expr(variable_manager, global_variables, block, stmt_idx, stmt, stmt.src)
|
|
1894
1900
|
|
|
1901
|
+
elif stmt_type is ailment.Stmt.CAS:
|
|
1902
|
+
for expr in [
|
|
1903
|
+
stmt.addr,
|
|
1904
|
+
stmt.data_lo,
|
|
1905
|
+
stmt.data_hi,
|
|
1906
|
+
stmt.expd_lo,
|
|
1907
|
+
stmt.expd_hi,
|
|
1908
|
+
stmt.old_lo,
|
|
1909
|
+
stmt.old_hi,
|
|
1910
|
+
]:
|
|
1911
|
+
if expr is not None:
|
|
1912
|
+
self._link_variables_on_expr(variable_manager, global_variables, block, stmt_idx, stmt, expr)
|
|
1913
|
+
|
|
1895
1914
|
elif stmt_type is ailment.Stmt.ConditionalJump:
|
|
1896
1915
|
self._link_variables_on_expr(variable_manager, global_variables, block, stmt_idx, stmt, stmt.condition)
|
|
1897
1916
|
|
|
@@ -2123,6 +2142,86 @@ class Clinic(Analysis):
|
|
|
2123
2142
|
|
|
2124
2143
|
return graph
|
|
2125
2144
|
|
|
2145
|
+
@staticmethod
|
|
2146
|
+
def _duplicate_orphaned_cond_jumps(ail_graph) -> networkx.DiGraph:
|
|
2147
|
+
"""
|
|
2148
|
+
Find conditional jumps that are orphaned (e.g., being the only instruction of the block). If these blocks have
|
|
2149
|
+
multiple predecessors, duplicate them to all predecessors. This is a workaround for cases where these
|
|
2150
|
+
conditional jumps rely on comparisons in more than one predecessor and we cannot resolve ccalls into
|
|
2151
|
+
comparisons.
|
|
2152
|
+
|
|
2153
|
+
This pass runs before any SSA transformations.
|
|
2154
|
+
|
|
2155
|
+
# 140017162 jz short 1400171e1
|
|
2156
|
+
"""
|
|
2157
|
+
|
|
2158
|
+
for block in list(ail_graph):
|
|
2159
|
+
if len(block.statements) > 1 and block.statements[0].ins_addr == block.statements[-1].ins_addr:
|
|
2160
|
+
preds = list(ail_graph.predecessors(block))
|
|
2161
|
+
if len(preds) > 1 and block not in preds:
|
|
2162
|
+
has_ccall = any(
|
|
2163
|
+
isinstance(stmt, ailment.Stmt.Assignment)
|
|
2164
|
+
and isinstance(stmt.src, ailment.Expr.VEXCCallExpression)
|
|
2165
|
+
for stmt in block.statements
|
|
2166
|
+
)
|
|
2167
|
+
if has_ccall:
|
|
2168
|
+
# duplicate this block to its predecessors!
|
|
2169
|
+
preds = sorted(preds, key=lambda x: x.addr)
|
|
2170
|
+
succs = sorted(ail_graph.successors(block), key=lambda x: x.addr)
|
|
2171
|
+
# FIXME: We should track block IDs globally and ensure block IDs do not collide
|
|
2172
|
+
block_idx_start = block.idx + 1 if block.idx is not None else 1
|
|
2173
|
+
for pred in preds[1:]:
|
|
2174
|
+
ail_graph.remove_edge(pred, block)
|
|
2175
|
+
new_block = block.copy()
|
|
2176
|
+
new_block.idx = block_idx_start
|
|
2177
|
+
block_idx_start += 1
|
|
2178
|
+
ail_graph.add_edge(pred, new_block)
|
|
2179
|
+
for succ in succs:
|
|
2180
|
+
ail_graph.add_edge(new_block, succ if succ is not block else new_block)
|
|
2181
|
+
|
|
2182
|
+
return ail_graph
|
|
2183
|
+
|
|
2184
|
+
def _rewrite_jump_rax_calls(self, ail_graph: networkx.DiGraph) -> networkx.DiGraph:
|
|
2185
|
+
"""
|
|
2186
|
+
Rewrite calls to special functions (e.g., guard_dispatch_icall_nop) into `call rax`.
|
|
2187
|
+
"""
|
|
2188
|
+
|
|
2189
|
+
if self.project.arch.name != "AMD64":
|
|
2190
|
+
return ail_graph
|
|
2191
|
+
if self._cfg is None:
|
|
2192
|
+
return ail_graph
|
|
2193
|
+
|
|
2194
|
+
for block in ail_graph:
|
|
2195
|
+
if not block.statements:
|
|
2196
|
+
continue
|
|
2197
|
+
assert block.addr is not None
|
|
2198
|
+
last_stmt = block.statements[-1]
|
|
2199
|
+
if isinstance(last_stmt, ailment.Stmt.Call):
|
|
2200
|
+
# we can't examine the call target at this point because constant propagation hasn't run yet; we consult
|
|
2201
|
+
# the CFG instead
|
|
2202
|
+
callsite_node = self._cfg.get_any_node(block.addr, anyaddr=True)
|
|
2203
|
+
if callsite_node is None:
|
|
2204
|
+
break
|
|
2205
|
+
callees = self._cfg.get_successors(callsite_node, jumpkind="Ijk_Call")
|
|
2206
|
+
if len(callees) != 1:
|
|
2207
|
+
break
|
|
2208
|
+
callee = callees[0].addr
|
|
2209
|
+
if self.kb.functions.contains_addr(callee):
|
|
2210
|
+
callee_func = self.kb.functions.get_by_addr(callee)
|
|
2211
|
+
if callee_func.info.get("jmp_rax", False) is True:
|
|
2212
|
+
# rewrite this statement into Call(rax)
|
|
2213
|
+
call_stmt = last_stmt.copy()
|
|
2214
|
+
call_stmt.target = ailment.Expr.Register(
|
|
2215
|
+
self._ail_manager.next_atom(),
|
|
2216
|
+
None,
|
|
2217
|
+
self.project.arch.registers["rax"][0],
|
|
2218
|
+
64,
|
|
2219
|
+
ins_addr=call_stmt.ins_addr,
|
|
2220
|
+
)
|
|
2221
|
+
block.statements[-1] = call_stmt
|
|
2222
|
+
|
|
2223
|
+
return ail_graph
|
|
2224
|
+
|
|
2126
2225
|
def _rewrite_ite_expressions(self, ail_graph):
|
|
2127
2226
|
cfg = self._cfg
|
|
2128
2227
|
for block in list(ail_graph):
|
|
@@ -2130,11 +2229,16 @@ class Clinic(Analysis):
|
|
|
2130
2229
|
continue
|
|
2131
2230
|
|
|
2132
2231
|
ite_ins_addrs = []
|
|
2232
|
+
cas_ins_addrs = set()
|
|
2133
2233
|
for stmt in block.statements:
|
|
2134
|
-
if (
|
|
2234
|
+
if isinstance(stmt, ailment.Stmt.CAS):
|
|
2235
|
+
# we do not rewrite ITE statements that are caused by CAS statements
|
|
2236
|
+
cas_ins_addrs.add(stmt.ins_addr)
|
|
2237
|
+
elif (
|
|
2135
2238
|
isinstance(stmt, ailment.Stmt.Assignment)
|
|
2136
2239
|
and isinstance(stmt.src, ailment.Expr.ITE)
|
|
2137
2240
|
and stmt.ins_addr not in ite_ins_addrs
|
|
2241
|
+
and stmt.ins_addr not in cas_ins_addrs
|
|
2138
2242
|
):
|
|
2139
2243
|
ite_ins_addrs.append(stmt.ins_addr)
|
|
2140
2244
|
|
|
@@ -2998,6 +3102,12 @@ class Clinic(Analysis):
|
|
|
2998
3102
|
and last_stmt.addr.offset < 0
|
|
2999
3103
|
and isinstance(last_stmt.data, ailment.Expr.Const)
|
|
3000
3104
|
and last_stmt.data.value == succ.addr
|
|
3105
|
+
) or (
|
|
3106
|
+
isinstance(last_stmt, ailment.Stmt.Assignment)
|
|
3107
|
+
and last_stmt.dst.was_stack
|
|
3108
|
+
and last_stmt.dst.stack_offset < 0
|
|
3109
|
+
and isinstance(last_stmt.src, ailment.Expr.Const)
|
|
3110
|
+
and last_stmt.src.value == succ.addr
|
|
3001
3111
|
):
|
|
3002
3112
|
# remove the statement that pushes the return address
|
|
3003
3113
|
node.statements = node.statements[:-1]
|
|
@@ -3031,6 +3141,12 @@ class Clinic(Analysis):
|
|
|
3031
3141
|
and last_stmt.addr.offset < 0
|
|
3032
3142
|
and isinstance(last_stmt.data, ailment.Expr.Const)
|
|
3033
3143
|
and last_stmt.data.value == succ.addr
|
|
3144
|
+
) or (
|
|
3145
|
+
isinstance(last_stmt, ailment.Stmt.Assignment)
|
|
3146
|
+
and last_stmt.dst.was_stack
|
|
3147
|
+
and last_stmt.dst.stack_offset < 0
|
|
3148
|
+
and isinstance(last_stmt.src, ailment.Expr.Const)
|
|
3149
|
+
and last_stmt.src.value == succ.addr
|
|
3034
3150
|
):
|
|
3035
3151
|
# remove the statement that pushes the return address
|
|
3036
3152
|
node.statements = node.statements[:-1]
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# pylint:disable=unused-argument,no-self-use
|
|
1
|
+
# pylint:disable=unused-argument,no-self-use,too-many-boolean-expressions
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
import logging
|
|
4
4
|
|
|
@@ -8,12 +8,14 @@ from ailment.statement import (
|
|
|
8
8
|
Assignment,
|
|
9
9
|
Store,
|
|
10
10
|
Call,
|
|
11
|
+
CAS,
|
|
11
12
|
Return,
|
|
12
13
|
ConditionalJump,
|
|
13
14
|
DirtyStatement,
|
|
14
15
|
WeakAssignment,
|
|
15
16
|
)
|
|
16
17
|
from ailment.expression import (
|
|
18
|
+
Atom,
|
|
17
19
|
Expression,
|
|
18
20
|
VirtualVariable,
|
|
19
21
|
Load,
|
|
@@ -121,6 +123,40 @@ class SimEngineDephiRewriting(SimEngineNostmtAIL[None, Expression | None, Statem
|
|
|
121
123
|
)
|
|
122
124
|
return None
|
|
123
125
|
|
|
126
|
+
def _handle_stmt_CAS(self, stmt: CAS) -> CAS | None:
|
|
127
|
+
new_addr = self._expr(stmt.addr)
|
|
128
|
+
new_data_lo = self._expr(stmt.data_lo)
|
|
129
|
+
new_data_hi = self._expr(stmt.data_hi) if stmt.data_hi is not None else None
|
|
130
|
+
new_expd_lo = self._expr(stmt.expd_lo)
|
|
131
|
+
new_expd_hi = self._expr(stmt.expd_hi) if stmt.expd_hi is not None else None
|
|
132
|
+
new_old_lo = self._expr(stmt.old_lo)
|
|
133
|
+
new_old_hi = self._expr(stmt.old_hi) if stmt.old_hi is not None else None
|
|
134
|
+
assert new_old_lo is None or isinstance(new_old_lo, Atom)
|
|
135
|
+
assert new_old_hi is None or isinstance(new_old_hi, Atom)
|
|
136
|
+
|
|
137
|
+
if (
|
|
138
|
+
new_addr is not None
|
|
139
|
+
or new_old_lo is not None
|
|
140
|
+
or new_old_hi is not None
|
|
141
|
+
or new_data_lo is not None
|
|
142
|
+
or new_data_hi is not None
|
|
143
|
+
or new_expd_lo is not None
|
|
144
|
+
or new_expd_hi is not None
|
|
145
|
+
):
|
|
146
|
+
return CAS(
|
|
147
|
+
stmt.idx,
|
|
148
|
+
stmt.addr if new_addr is None else new_addr,
|
|
149
|
+
stmt.data_lo if new_data_lo is None else new_data_lo,
|
|
150
|
+
stmt.data_hi if new_data_hi is None else new_data_hi,
|
|
151
|
+
stmt.expd_lo if new_expd_lo is None else new_expd_lo,
|
|
152
|
+
stmt.expd_hi if new_expd_hi is None else new_expd_hi,
|
|
153
|
+
stmt.old_lo if new_old_lo is None else new_old_lo,
|
|
154
|
+
stmt.old_hi if new_old_hi is None else new_old_hi,
|
|
155
|
+
stmt.endness,
|
|
156
|
+
**stmt.tags,
|
|
157
|
+
)
|
|
158
|
+
return None
|
|
159
|
+
|
|
124
160
|
def _handle_stmt_Store(self, stmt):
|
|
125
161
|
new_addr = self._expr(stmt.addr)
|
|
126
162
|
new_data = self._expr(stmt.data)
|
|
@@ -179,6 +215,7 @@ class SimEngineDephiRewriting(SimEngineNostmtAIL[None, Expression | None, Statem
|
|
|
179
215
|
dirty = self._expr(stmt.dirty)
|
|
180
216
|
if dirty is None or dirty is stmt.dirty:
|
|
181
217
|
return None
|
|
218
|
+
assert isinstance(dirty, DirtyExpression)
|
|
182
219
|
return DirtyStatement(stmt.idx, dirty, **stmt.tags)
|
|
183
220
|
|
|
184
221
|
def _handle_expr_Load(self, expr):
|
|
@@ -107,6 +107,12 @@ class ConditionConstantPropagation(OptimizationPass):
|
|
|
107
107
|
cconds_by_src[src] = []
|
|
108
108
|
cconds_by_src[src].append(ccond)
|
|
109
109
|
|
|
110
|
+
# eliminate sources with more than one in-edges; this is because the condition may not hold on all in-edges!
|
|
111
|
+
for src in list(cconds_by_src):
|
|
112
|
+
block = self._get_block(src[0], idx=src[1])
|
|
113
|
+
if block is not None and block in self._graph and self._graph.in_degree[block] > 1:
|
|
114
|
+
del cconds_by_src[src]
|
|
115
|
+
|
|
110
116
|
# eliminate conflicting conditions
|
|
111
117
|
for src in list(cconds_by_src):
|
|
112
118
|
cconds = cconds_by_src[src]
|
|
@@ -86,6 +86,11 @@ class SimplifierAILEngine(
|
|
|
86
86
|
|
|
87
87
|
return stmt
|
|
88
88
|
|
|
89
|
+
def _handle_stmt_CAS(self, stmt: ailment.statement.CAS) -> ailment.statement.CAS:
|
|
90
|
+
# we assume that we never have to deal with CAS statements at this point; they should have been rewritten to
|
|
91
|
+
# intrinsics
|
|
92
|
+
return stmt
|
|
93
|
+
|
|
89
94
|
def _handle_stmt_Store(self, stmt):
|
|
90
95
|
addr = self._expr(stmt.addr)
|
|
91
96
|
data = self._expr(stmt.data)
|
|
@@ -8,6 +8,7 @@ from .a_sub_a_div_const_mul_const import ASubADivConstMulConst
|
|
|
8
8
|
from .a_sub_a_shr_const_shr_const import ASubAShrConstShrConst
|
|
9
9
|
from .arm_cmpf import ARMCmpF
|
|
10
10
|
from .bswap import Bswap
|
|
11
|
+
from .cas_intrinsics import CASIntrinsics
|
|
11
12
|
from .coalesce_same_cascading_ifs import CoalesceSameCascadingIfs
|
|
12
13
|
from .constant_derefs import ConstantDereferences
|
|
13
14
|
from .const_mull_a_shift import ConstMullAShift
|
|
@@ -64,6 +65,7 @@ ALL_PEEPHOLE_OPTS: list[type[PeepholeOptimizationExprBase]] = [
|
|
|
64
65
|
ASubAShrConstShrConst,
|
|
65
66
|
ARMCmpF,
|
|
66
67
|
Bswap,
|
|
68
|
+
CASIntrinsics,
|
|
67
69
|
CoalesceSameCascadingIfs,
|
|
68
70
|
ConstantDereferences,
|
|
69
71
|
ConstMullAShift,
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# pylint:disable=arguments-differ,too-many-boolean-expressions
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from ailment.expression import BinaryOp, Load
|
|
5
|
+
from ailment.statement import CAS, ConditionalJump, Statement, Assignment, Call
|
|
6
|
+
|
|
7
|
+
from .base import PeepholeOptimizationMultiStmtBase
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
_INTRINSICS_NAMES = {
|
|
11
|
+
"xchg": {"Win32": "InterlockedExchange", "Linux": "atomic_exchange"},
|
|
12
|
+
"cmpxchg": {"Win32": "InterlockedCompareExchange", "Linux": "atomic_compare_exchange"},
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class CASIntrinsics(PeepholeOptimizationMultiStmtBase):
|
|
17
|
+
"""
|
|
18
|
+
Rewrite lock-prefixed instructions (or rather, their VEX/AIL forms) into intrinsic calls.
|
|
19
|
+
|
|
20
|
+
Case 1.
|
|
21
|
+
|
|
22
|
+
mov eax, r12d
|
|
23
|
+
0x140014b57: xchg eax, [0x14000365f8]
|
|
24
|
+
|
|
25
|
+
LABEL_0x140014b57:
|
|
26
|
+
CAS(0x1400365f8<64>, Conv(64->32, vvar_365{reg 112}), Load(addr=0x1400365f8<64>, size=4, endness=Iend_LE),
|
|
27
|
+
vvar_27756)
|
|
28
|
+
if (CasCmpNE(vvar_27756, g_1400365f8))
|
|
29
|
+
goto LABEL_0x140014b57;
|
|
30
|
+
|
|
31
|
+
=> vvar_27756 = _InterlockedExchange(0x1400365f8, vvar_365{reg 112})
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
Case 2.
|
|
35
|
+
|
|
36
|
+
lock cmpxchg cs:g_WarbirdSecureFunctionsLock, r14d
|
|
37
|
+
|
|
38
|
+
CAS(0x1400365f8<64>, 0x1<32>, 0x0<32>, vvar_27751)
|
|
39
|
+
|
|
40
|
+
=> var_27751 = _InterlockedCompareExchange(0x1400365f8, 0x1<32>, 0x0<32>)
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
__slots__ = ()
|
|
44
|
+
|
|
45
|
+
NAME = "Rewrite compare-and-swap instructions into intrinsics."
|
|
46
|
+
stmt_classes = ((CAS, ConditionalJump), (CAS, Statement))
|
|
47
|
+
|
|
48
|
+
def optimize(self, stmts: list[Statement], stmt_idx: int | None = None, block=None, **kwargs):
|
|
49
|
+
assert len(stmts) == 2
|
|
50
|
+
cas_stmt = stmts[0]
|
|
51
|
+
next_stmt = stmts[1]
|
|
52
|
+
assert isinstance(cas_stmt, CAS)
|
|
53
|
+
|
|
54
|
+
# TODO: We ignored endianness. Are there cases where the endianness is different from the host's?
|
|
55
|
+
|
|
56
|
+
if (
|
|
57
|
+
isinstance(next_stmt, ConditionalJump)
|
|
58
|
+
and isinstance(next_stmt.condition, BinaryOp)
|
|
59
|
+
and next_stmt.condition.op == "CasCmpNE"
|
|
60
|
+
and next_stmt.ins_addr == cas_stmt.ins_addr
|
|
61
|
+
):
|
|
62
|
+
addr = cas_stmt.addr
|
|
63
|
+
if (
|
|
64
|
+
isinstance(cas_stmt.expd_lo, Load)
|
|
65
|
+
and cas_stmt.expd_lo.addr.likes(addr)
|
|
66
|
+
and isinstance(next_stmt.condition.operands[1], Load)
|
|
67
|
+
and next_stmt.condition.operands[1].addr.likes(addr)
|
|
68
|
+
and cas_stmt.old_lo.likes(next_stmt.condition.operands[0])
|
|
69
|
+
and cas_stmt.old_hi is None
|
|
70
|
+
):
|
|
71
|
+
# TODO: Support cases where cas_stmt.old_hi is not None
|
|
72
|
+
# Case 1
|
|
73
|
+
call_expr = Call(
|
|
74
|
+
cas_stmt.idx,
|
|
75
|
+
self._get_instrincs_name("xchg"),
|
|
76
|
+
args=[addr, cas_stmt.data_lo],
|
|
77
|
+
bits=cas_stmt.bits,
|
|
78
|
+
ins_addr=cas_stmt.ins_addr,
|
|
79
|
+
)
|
|
80
|
+
stmt = Assignment(cas_stmt.idx, cas_stmt.old_lo, call_expr, **cas_stmt.tags)
|
|
81
|
+
return [stmt]
|
|
82
|
+
|
|
83
|
+
if next_stmt.ins_addr <= cas_stmt.ins_addr:
|
|
84
|
+
# avoid matching against statements prematurely
|
|
85
|
+
return None
|
|
86
|
+
|
|
87
|
+
if cas_stmt.old_hi is None:
|
|
88
|
+
# TODO: Support cases where cas_stmt.old_hi is not None
|
|
89
|
+
call_expr = Call(
|
|
90
|
+
cas_stmt.idx,
|
|
91
|
+
self._get_instrincs_name("cmpxchg"),
|
|
92
|
+
args=[
|
|
93
|
+
cas_stmt.addr,
|
|
94
|
+
cas_stmt.data_lo,
|
|
95
|
+
cas_stmt.expd_lo,
|
|
96
|
+
],
|
|
97
|
+
bits=cas_stmt.bits,
|
|
98
|
+
ins_addr=cas_stmt.ins_addr,
|
|
99
|
+
)
|
|
100
|
+
stmt = Assignment(cas_stmt.idx, cas_stmt.old_lo, call_expr, **cas_stmt.tags)
|
|
101
|
+
return [stmt, next_stmt]
|
|
102
|
+
|
|
103
|
+
return None
|
|
104
|
+
|
|
105
|
+
def _get_instrincs_name(self, mnemonic: str) -> str:
|
|
106
|
+
if mnemonic in _INTRINSICS_NAMES:
|
|
107
|
+
os = (
|
|
108
|
+
self.project.simos.name
|
|
109
|
+
if self.project is not None and self.project.simos is not None and self.project.simos.name is not None
|
|
110
|
+
else "Linux"
|
|
111
|
+
)
|
|
112
|
+
if os not in _INTRINSICS_NAMES[mnemonic]:
|
|
113
|
+
os = "Linux"
|
|
114
|
+
return _INTRINSICS_NAMES[mnemonic][os]
|
|
115
|
+
return mnemonic
|