def modules_diff(mod_first, mod_second, glob_var, fun, config): """ Analyse semantic difference of two LLVM IR modules w.r.t. some parameter :param mod_first: First LLVM module :param mod_second: Second LLVM module :param glob_var: Parameter (global variable) to compare (if specified, all functions using this variable are compared). :param fun: Function to be compared. :param config: Configuration. """ result = Result(Result.Kind.NONE, mod_first, mod_second) if fun: funs_first = {fun} funs_second = {fun} elif glob_var: funs_first = mod_first.get_functions_using_param(glob_var) funs_second = mod_second.get_functions_using_param(glob_var) else: funs_first = [] funs_second = [] for fun in funs_first: if fun not in funs_second: continue if (not mod_first.has_function(fun) or not mod_second.has_function(fun)): print(" Given function not found in module") result.kind = Result.Kind.ERROR return result fun_result = functions_diff(mod_first=mod_first, mod_second=mod_second, fun_first=fun, fun_second=fun, glob_var=glob_var, config=config) result.add_inner(fun_result) return result
def diff_all_modules_using_global(glob_first, glob_second, config): """ Compare semantics of all modules using given global variable. Finds all source files that use the given globals and compare all of them. :param glob_first: First global to compare :param glob_second: Second global to compare :param config: Configuration """ result = Result(Result.Kind.NONE, glob_first.name, glob_second.name) if glob_first.name != glob_second.name: # Variables with different names are treated as unequal result.kind = Result.Kind.NOT_EQUAL return result srcs_first = config.source_first.find_srcs_using_symbol(glob_first.name) srcs_second = config.source_second.find_srcs_using_symbol(glob_second.name) # Compare all sources containing functions using the variable for src in srcs_first: if src not in srcs_second: result.add_inner(Result(Result.Kind.NOT_EQUAL, src, src)) else: try: mod_first = config.source_first.get_module_from_source(src) mod_second = config.source_second.get_module_from_source(src) mod_first.parse_module() mod_second.parse_module() if (mod_first.has_global(glob_first.name) and mod_second.has_global(glob_second.name)): src_result = modules_diff(mod_first=mod_first, mod_second=mod_second, glob_var=glob_first, fun=None, config=config) for res in src_result.inner.values(): result.add_inner(res) except BuildException as e: if config.verbosity: print(e) result.add_inner(Result(Result.Kind.ERROR, src, src)) return result
def functions_diff(mod_first, mod_second, fun_first, fun_second, glob_var, config, prev_result_graph=None, function_cache=None): """ Compare two functions for equality. First, functions are simplified and compared for syntactic equality using the SimpLL tool. If they are not syntactically equal, SimpLL prints a list of functions that the syntactic equality depends on. These are then compared for semantic equality. :param mod_first: First LLVM module :param mod_second: Second LLVM module :param fun_first: Function from the first module to be compared :param fun_second: Function from the second module to be compared :param glob_var: Global variable whose effect on the functions to compare :param config: Configuration :param prev_result_graph: Graph generated by the previous comparison (used to pass already known results to be used in this comparison). :param function_cache: Cache for SimpLL containing all functions present in the current graph (passed to this function to be updated with the results of the comparison). """ result = Result(Result.Kind.NONE, fun_first, fun_second) curr_result_graph = None try: if config.verbosity: if fun_first == fun_second: fun_str = fun_first else: fun_str = "{} and {}".format(fun_first, fun_second) print("Syntactic diff of {} (in {})".format( fun_str, mod_first.llvm)) simplify = True while simplify: simplify = False if (prev_result_graph and fun_first in prev_result_graph.vertices and (prev_result_graph.vertices[fun_first].result != Result.Kind.ASSUMED_EQUAL)): first_simpl = "" second_simpl = "" curr_result_graph = prev_result_graph else: # Simplify modules and get the output graph. first_simpl, second_simpl, curr_result_graph, missing_defs = \ run_simpll(first=mod_first.llvm, second=mod_second.llvm, fun_first=fun_first, fun_second=fun_second, var=glob_var.name if glob_var else None, suffix=glob_var.name if glob_var else "simpl", cache_dir=function_cache.directory if function_cache else None, control_flow_only=config.control_flow_only, output_llvm_ir=config.output_llvm_ir, print_asm_diffs=config.print_asm_diffs, verbose=config.verbosity, use_ffi=config.use_ffi) if missing_defs: # If there are missing function definitions, try to find # their implementation, link them to the current modules, # and rerun the simplification. for fun_pair in missing_defs: if "first" in fun_pair: if _link_symbol_def(config.snapshot_first, mod_first, fun_pair["first"]): simplify = True if "second" in fun_pair: if _link_symbol_def(config.snapshot_second, mod_second, fun_pair["second"]): simplify = True if prev_result_graph and not simplify: # Note: "curr_result_graph" is here the partial result # graph, i.e. can contain unknown results that are known in # the graph from the previous comparison. prev_result_graph.absorb_graph(curr_result_graph) curr_result_graph = prev_result_graph # Add the newly received results to the ignored functions # file. # Note: there won't be any duplicates, since all functions # that were in the cache before will be marked as unknown. if function_cache: function_cache.update([ v for v in curr_result_graph.vertices.values() if v.result not in [Result.Kind.UNKNOWN, Result.Kind.ASSUMED_EQUAL] ]) objects_to_compare, syndiff_bodies_left, syndiff_bodies_right = \ curr_result_graph.graph_to_fun_pair_list(fun_first, fun_second) mod_first.restore_unlinked_llvm() mod_second.restore_unlinked_llvm() if not objects_to_compare: result.kind = Result.Kind.EQUAL_SYNTAX else: # If the functions are not syntactically equal, objects_to_compare # contains a list of functions and macros that are different. for fun_pair in objects_to_compare: if (not fun_pair[0].diff_kind == "function" and config.semdiff_tool is not None): # If a semantic diff tool is set, use it for further # comparison of non-equal functions fun_result = functions_semdiff(first_simpl, second_simpl, fun_pair[0].name, fun_pair[1].name, config) else: fun_result = Result(fun_pair[2], fun_first, fun_second) fun_result.first = fun_pair[0] fun_result.second = fun_pair[1] if fun_result.kind == Result.Kind.NOT_EQUAL: if fun_result.first.diff_kind in ["function", "type"]: # Get the syntactic diff of functions or types fun_result.diff = syntax_diff( fun_result.first.filename, fun_result.second.filename, fun_result.first.name, fun_result.first.diff_kind, fun_pair[0].line, fun_pair[1].line) elif fun_result.first.diff_kind == "syntactic": # Find the syntax differences and append the left and # right value to create the resulting diff fun_result.diff = " {}\n\n {}\n".format( syndiff_bodies_left[fun_result.first.name], syndiff_bodies_right[fun_result.second.name]) else: sys.stderr.write( "warning: unknown diff kind: {}\n".format( fun_result.first.diff_kind)) fun_result.diff = "unknown\n" result.add_inner(fun_result) if config.verbosity: print(" {}".format(result)) except ValueError: result.kind = Result.Kind.ERROR except SimpLLException as e: if config.verbosity: print(e) result.kind = Result.Kind.ERROR result.graph = (curr_result_graph if curr_result_graph else prev_result_graph) return result