github/workflows/pycopy-test: Upgrade Pycopy to 3.6.1.
[ScratchABlock.git] / progdb.py
blob7241257c286fba2ce9522f10c3cb77131ff317df
2 # This module deals with various "program databases" as required
3 # for analysis passes. Among them: function database (funcdb.yaml),
4 # databases of structures, etc.
6 import os.path
7 import copy
8 import logging
10 import yaml
11 import yamlutils
13 import utils
14 from utils import repr_stable
15 import core
16 from core import is_addr, is_value, is_expr, EXPR
19 _log = logging.getLogger(__name__)
21 FUNC_DB = {}
22 FUNC_DB_BY_ADDR = {}
23 struct_types = {}
24 struct_instances = {}
27 def set_funcdb(db):
28 global FUNC_DB, FUNC_DB_BY_ADDR
29 # index by name in addition to by addr
30 for addr, props in list(db.items()):
31 FUNC_DB[props["label"]] = props
32 FUNC_DB_BY_ADDR = db
35 REG_PROPS = [
36 "callsites_live_out", "modifieds", "preserveds", "reach_exit", "reach_exit_maybe",
37 "params", "estimated_params", "returns",
41 def reglist2set(regs):
42 return set(core.REG(x) for x in regs)
45 def preprocess_funcdb(FUNC_DB):
46 for addr, props in FUNC_DB.items():
47 for prop in REG_PROPS:
48 if prop in props:
49 props[prop] = reglist2set(props[prop])
51 if "calls_live_out" in props:
52 new = [(x, core.ADDR(y), reglist2set(z)) for x, y, z in props["calls_live_out"]]
53 props["calls_live_out"] = new
56 def postprocess_funcdb(FUNC_DB):
57 for addr, props in FUNC_DB.items():
58 for prop in REG_PROPS:
59 if prop in props:
60 props[prop] = sorted([x.name for x in props[prop]], key=utils.natural_sort_key)
63 def load_funcdb(*fnames):
64 FUNC_DB = {}
65 for fname in fnames:
66 with open(fname) as f:
67 db = yaml.safe_load(f)
68 FUNC_DB.update(db)
70 preprocess_funcdb(FUNC_DB)
71 set_funcdb(FUNC_DB)
74 def save_funcdb(fname, backup=True):
75 db = copy.deepcopy(FUNC_DB_BY_ADDR)
76 postprocess_funcdb(db)
77 if backup and os.path.exists(fname):
78 os.rename(fname, fname + ".bak")
80 with open(fname, "w") as f:
81 yaml.dump(db, f)
84 def check_invariants(cfg):
85 if "reach_exit" in cfg.props:
86 reach = cfg.props["reach_exit"]
87 reach_maybe = cfg.props.get("reach_exit_maybe", set())
88 assert reach_maybe.issubset(reach), "%s: maybe: %s, sure: %s" % (cfg.props["name"],
89 repr_stable(reach_maybe), repr_stable(reach))
92 def update_funcdb(cfg):
93 "Aggregate data from each CFG processed into a function DB."
94 if "addr" not in cfg.props:
95 return
97 check_invariants(cfg)
99 func_props = FUNC_DB_BY_ADDR.setdefault(cfg.props["addr"], {})
100 func_props["label"] = cfg.props["name"]
102 PROPS = (
103 "params", "estimated_params", "params_why", "modifieds", "preserveds",
104 "reach_exit", "reach_exit_maybe", "calls_live_out", "noreturn",
105 "has_infloops"
108 for prop in PROPS:
109 if prop in cfg.props:
110 func_props[prop] = cfg.props[prop]
112 for prop in ("calls", "calls_indir", "func_refs", "mmio_refs"):
113 if prop in cfg.props:
114 def ext_repr(x):
115 if is_addr(x):
116 return x.addr
117 if is_value(x):
118 return hex(x.val)
119 if is_expr(x):
120 if x.op == "+" and len(x.args) == 2:
121 if is_value(x.args[1]):
122 x = EXPR("+", [x.args[1], x.args[0]])
123 return str(x)
124 return str(x)
125 func_props[prop] = sorted([ext_repr(x) for x in cfg.props[prop]])
128 # Updated funcs tracking
131 UPDATED_FUNCS = set()
133 def clear_updated():
134 UPDATED_FUNCS.clear()
136 def mark_updated(func):
137 UPDATED_FUNCS.add(func)
139 def update_cfg_prop(cfg, prop, new_val):
140 if cfg.props.get(prop) != new_val:
141 mark_updated(cfg.props["name"])
142 _log.info("%s: %s updated from %s to %s" % (cfg.props["name"], prop,
143 utils.repr_stable(cfg.props.get(prop)), utils.repr_stable(new_val)))
144 cfg.props[prop] = new_val
147 # Symtab functions
150 SYMTAB = {}
152 def load_symtab(fname):
153 with open(fname) as f:
154 for l in f:
155 l = l.strip()
156 addr, sym = l.split()
157 SYMTAB[sym] = int(addr, 16)
159 # Returns real memory (not symbolic) address, for bindata module.
160 def lookup_sym(sym):
161 return SYMTAB[sym]
164 # Struct database functions
167 def set_struct_types(data):
168 global struct_types
169 struct_types = data
172 def set_struct_instances(data):
173 global struct_instances
174 struct_instances = data
177 def get_struct_types():
178 global struct_types
179 return struct_types
182 def get_struct_instances():
183 global struct_instances
184 return struct_instances