github/workflows/pycopy-test: Upgrade Pycopy to 3.6.1.
[ScratchABlock.git] / xform_inter.py
blobfbc2f2c896364607a94cbcac5e2716a1aa706404
1 # ScratchABlock - Program analysis and decompilation framework
3 # Copyright (c) 2015-2018 Paul Sokolovsky
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation, either version 3 of the License, or
8 # (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program. If not, see <http://www.gnu.org/licenses/>.
18 """Interprocedural transformation passes"""
20 from graph import Graph
21 from core import is_addr
22 import progdb
23 from utils import maybesorted
24 import utils
27 def build_callgraph():
28 "Build program callgraph from progdb."
30 callgraph = Graph()
32 for addr, props in progdb.FUNC_DB_BY_ADDR.items():
33 callgraph.add_node(props["label"])
35 for addr, props in progdb.FUNC_DB_BY_ADDR.items():
36 for callee in props.get("calls", []):
37 if callee in callgraph:
38 callgraph.add_edge(props["label"], callee)
40 callgraph.number_postorder_forest()
42 return callgraph
45 def calc_callsites_live_out(cg, callee):
46 """Calculate function's callsites_live_out property.
48 Go thru function's callers (using callgraph), and union their
49 calls_live_out information pertinent to this function.
50 """
52 callers = maybesorted(cg.pred(callee))
53 # If there're no callers, will return empty set, which
54 # is formally correct - if there're no callers, the
55 # function is dead. However, realistically that means
56 # that callers aren't known, and we should treat that
57 # specially.
58 call_lo_union = set()
59 for c in callers:
60 clo = progdb.FUNC_DB[c].get("calls_live_out", [])
61 #print(" %s: calls_live_out: %s" % (c, utils.repr_stable(clo)))
62 for bbaddr, callee_expr, live_out in clo:
63 if is_addr(callee_expr) and callee_expr.addr == callee:
64 print(" %s: calls_live_out[%s]: %s" % (c, callee, utils.repr_stable((bbaddr, callee_expr, live_out))))
65 call_lo_union.update(live_out)
67 progdb.FUNC_DB[callee]["callsites_live_out"] = call_lo_union
69 return call_lo_union
72 def collect_returns():
73 import progdb
74 import arch
75 for addr, props in progdb.FUNC_DB.items():
76 if "modifieds" in props and "callsites_live_out" in props:
77 props["returns"] = arch.ret_filter(set(props["modifieds"]) & set(props["callsites_live_out"]))