def process_one(cg, func, xform_pass): global subiter_cnt, update_cnt upward_queue = [func] downward_queue = [] cnt = 0 cur_queue = upward_queue while upward_queue or downward_queue: subiter_cnt += 1 cnt += 1 if not cur_queue: if cur_queue is upward_queue: cur_queue = downward_queue else: cur_queue = upward_queue func = cur_queue.pop(0) print("--- Next to process: %s ---" % func) progdb.clear_updated() cfg = CFG_MAP["pre"][func].copy() call_lo_union = xform_inter.calc_callsites_live_out(cg, func) progdb.update_cfg_prop(cfg, "callsites_live_out", call_lo_union) print("%s: callsites_live_out set to %s" % (func, utils.repr_stable(call_lo_union))) if "modifieds" in progdb.FUNC_DB[func]: progdb.FUNC_DB[func]["returns"] = arch.ret_filter( progdb.FUNC_DB[func]["modifieds"] & call_lo_union) else: print("%s: doesn't yet have modifieds!" % func) xform_pass.apply(cfg) if progdb.UPDATED_FUNCS: assert len(progdb.UPDATED_FUNCS) == 1, repr(progdb.UPDATED_FUNCS) func2 = progdb.UPDATED_FUNCS.pop() assert func2 == func update_cnt += 1 progdb.update_funcdb(cfg) save_cfg(cfg, ".1") CFG_MAP["pre"][func].props = cfg.props upward_queue.extend(maybesorted(cg.pred(func))) for callee in maybesorted(cg.succ(func)): print("! updating callee", callee) if callee not in downward_queue: downward_queue.insert(0, callee) print("--- Finished processing: %s ---" % func) print("# New up (caller) queue:", upward_queue) print("# New down (callee) queue:", downward_queue) else: print("%s not updated" % func) print("Subiters:", cnt)
def calc_callsites_live_out(cg, callee): """Calculate function's callsites_live_out property. Go thru function's callers (using callgraph), and union their calls_live_out information pertinent to this function. """ callers = maybesorted(cg.pred(callee)) # If there're no callers, will return empty set, which # is formally correct - if there're no callers, the # function is dead. However, realistically that means # that callers aren't known, and we should treat that # specially. call_lo_union = set() for c in callers: clo = progdb.FUNC_DB[c].get("calls_live_out", []) #print(" %s: calls_live_out: %s" % (c, utils.repr_stable(clo))) for bbaddr, callee_expr, live_out in clo: if is_addr(callee_expr) and callee_expr.addr == callee: print(" %s: calls_live_out[%s]: %s" % (c, callee, utils.repr_stable((bbaddr, callee_expr, live_out)))) call_lo_union.update(live_out) progdb.FUNC_DB[callee]["callsites_live_out"] = call_lo_union return call_lo_union
def calc_callsites_live_out(cg, callee): callers = maybesorted(cg.pred(callee)) # If there're no callers, will return empty set, which # is formally correct - if there're no callers, the # function is dead. However, realistically that means # that callers aren't known, and we should treat that # specially. call_lo_union = set() for c in callers: clo = progdb.FUNC_DB[c].get("calls_live_out", []) print("%s: calls_live_out: %s" % (c, clo)) for bbaddr, callee_expr, live_out in clo: if is_addr(callee_expr) and callee_expr.addr == callee: call_lo_union.update(live_out) return call_lo_union
def dump_level(func, props): if get_group(props["label"]): return for propname in ("calls", "func_refs"): if propname == "func_refs" and args.no_refs: continue for callee in maybesorted(props.get(propname, [])): if callee in IGNORE: continue callee = map_group(callee) if not args.each_call: if (props["label"], callee) in dup_set: continue dup_set.add((props["label"], callee)) attrs = {"func_refs": " [style=dashed]"}.get(propname, "") out.write("%s -> %s%s\n" % (props["label"], callee, attrs))
else: print("%s not updated" % func) print("Subiters:", cnt) import script_i_func_params_returns iter_cnt = 1 while True: print("=== Iteration %d ===" % iter_cnt) old_funcdb = copy.deepcopy(progdb.FUNC_DB) progdb.clear_updated() for e in maybesorted(callgraph.exits()): print("Processing leaf", e) process_one(callgraph, e, script_i_func_params_returns) progdb.save_funcdb(sys.argv[1] + "/funcdb.yaml.out%d" % iter_cnt) if progdb.FUNC_DB == old_funcdb: break iter_cnt += 1 # if iter_cnt > 3: # break print("Done in %d iterations, %d sub-iterations, %d updates" % (iter_cnt, subiter_cnt, update_cnt))
if not args.each_call: if (props["label"], callee) in dup_set: continue dup_set.add((props["label"], callee)) attrs = {"func_refs": " [style=dashed]"}.get(propname, "") out.write("%s -> %s%s\n" % (props["label"], callee, attrs)) out.write("digraph G {\n") if args.func: index_by_name(FUNC_DB) todo = [args.func] done = set() while todo: f = todo.pop() props = FUNC_DB.get(f) if not props: continue dump_level(f, props) done.add(f) for callee in props["calls"]: if callee not in done: todo.append(callee) else: for func, props in maybesorted(FUNC_DB.items()): dump_level(func, props) out.write("}\n")
def process_one(cg, func, xform_pass): global subiter_cnt, update_cnt upward_queue = [func] downward_queue = [] cnt = 0 cur_queue = upward_queue while upward_queue or downward_queue: subiter_cnt += 1 cnt += 1 if not cur_queue: if cur_queue is upward_queue: cur_queue = downward_queue else: cur_queue = upward_queue func = cur_queue.pop(0) print("--- Next to process: %s ---" % func) progdb.clear_updated() cfg = CFG_MAP["pre"][func].copy() call_lo_union = xform_inter.calc_callsites_live_out(cg, func) progdb.update_cfg_prop(cfg, "callsites_live_out", call_lo_union) print("%s: callsites_live_out set to %s" % (func, utils.repr_stable(call_lo_union))) if "modifieds" in progdb.FUNC_DB[func]: progdb.FUNC_DB[func]["returns"] = arch.ret_filter(progdb.FUNC_DB[func]["modifieds"] & call_lo_union) else: print("%s: doesn't yet have modifieds!" % func) xform_pass.apply(cfg) if progdb.UPDATED_FUNCS: func_stats[func][0] += 1 assert len(progdb.UPDATED_FUNCS) == 1, repr(progdb.UPDATED_FUNCS) func2 = progdb.UPDATED_FUNCS.pop() assert func2 == func update_cnt += 1 progdb.update_funcdb(cfg) save_cfg(cfg, ".1") dot.save_dot(cfg, ".1") CFG_MAP["pre"][func].props = cfg.props for x in maybesorted(cg.pred(func)): if x not in upward_queue: upward_queue.append(x) for callee in maybesorted(cg.succ(func)): print("! updating callee", callee) if callee not in downward_queue: downward_queue.append(callee) print("--- Finished processing: %s ---" % func) print("# New up (caller) queue:", upward_queue) print("# New down (callee) queue:", downward_queue) else: func_stats[func][1] += 1 print("%s not updated" % func) # Maybe funcdb properties not updated, but bblocks props can very well be save_cfg(cfg, ".1") dot.save_dot(cfg, ".1") print("Subiters:", cnt)
iter_cnt = 1 while True: print("=== Iteration %d ===" % iter_cnt) old_funcdb = copy.deepcopy(progdb.FUNC_DB) progdb.clear_updated() # We start with some leaf node (then eventually with all the rest of # leafs). With leaf (call-less) function, we can know everything about # its parameters. So, we learn that, and then propagate this information # to all its callers, then to callers of its callers. We go in this # upward fashion (propagating parameter information) until we can, and # then we start downward motion, hoping to collect as much information # as possible about function live-outs, i.e. returns. We go in this # zig-zag fashion, until there's something to update. for e in maybesorted(callgraph.exits()): print("Processing leaf", e) process_one(callgraph, e, script_i_func_params_returns) progdb.save_funcdb(sys.argv[1] + "/funcdb.yaml.out%d" % iter_cnt) if progdb.FUNC_DB == old_funcdb: break print("So far: %d iterations, %d sub-iterations, %d updates" % (iter_cnt, subiter_cnt, update_cnt)) iter_cnt += 1 # if iter_cnt > 3: # break