|  | #!/usr/bin/env python | 
|  |  | 
|  | from __future__ import absolute_import, division, print_function | 
|  |  | 
|  | import argparse | 
|  | import difflib | 
|  | import filecmp | 
|  | import os | 
|  | import subprocess | 
|  | import sys | 
|  |  | 
|  | disassembler = 'objdump' | 
|  |  | 
|  | def keep_line(line): | 
|  | """Returns true for lines that should be compared in the disassembly | 
|  | output.""" | 
|  | return "file format" not in line | 
|  |  | 
|  | def disassemble(objfile): | 
|  | """Disassemble object to a file.""" | 
|  | p = subprocess.Popen([disassembler, '-d', objfile], | 
|  | stdout=subprocess.PIPE, | 
|  | stderr=subprocess.PIPE) | 
|  | (out, err) = p.communicate() | 
|  | if p.returncode or err: | 
|  | print("Disassemble failed: {}".format(objfile)) | 
|  | sys.exit(1) | 
|  | return [line for line in out.split(os.linesep) if keep_line(line)] | 
|  |  | 
|  | def dump_debug(objfile): | 
|  | """Dump all of the debug info from a file.""" | 
|  | p = subprocess.Popen([disassembler, '-WliaprmfsoRt', objfile], stdout=subprocess.PIPE, stderr=subprocess.PIPE) | 
|  | (out, err) = p.communicate() | 
|  | if p.returncode or err: | 
|  | print("Dump debug failed: {}".format(objfile)) | 
|  | sys.exit(1) | 
|  | return [line for line in out.split(os.linesep) if keep_line(line)] | 
|  |  | 
|  | def first_diff(a, b, fromfile, tofile): | 
|  | """Returns the first few lines of a difference, if there is one.  Python | 
|  | diff can be very slow with large objects and the most interesting changes | 
|  | are the first ones. Truncate data before sending to difflib.  Returns None | 
|  | is there is no difference.""" | 
|  |  | 
|  | # Find first diff | 
|  | first_diff_idx = None | 
|  | for idx, val in enumerate(a): | 
|  | if val != b[idx]: | 
|  | first_diff_idx = idx | 
|  | break | 
|  |  | 
|  | if first_diff_idx == None: | 
|  | # No difference | 
|  | return None | 
|  |  | 
|  | # Diff to first line of diff plus some lines | 
|  | context = 3 | 
|  | diff = difflib.unified_diff(a[:first_diff_idx+context], | 
|  | b[:first_diff_idx+context], | 
|  | fromfile, | 
|  | tofile) | 
|  | difference = "\n".join(diff) | 
|  | if first_diff_idx + context < len(a): | 
|  | difference += "\n*** Diff truncated ***" | 
|  | return difference | 
|  |  | 
|  | def compare_object_files(objfilea, objfileb): | 
|  | """Compare disassembly of two different files. | 
|  | Allowing unavoidable differences, such as filenames. | 
|  | Return the first difference if the disassembly differs, or None. | 
|  | """ | 
|  | disa = disassemble(objfilea) | 
|  | disb = disassemble(objfileb) | 
|  | return first_diff(disa, disb, objfilea, objfileb) | 
|  |  | 
|  | def compare_debug_info(objfilea, objfileb): | 
|  | """Compare debug info of two different files. | 
|  | Allowing unavoidable differences, such as filenames. | 
|  | Return the first difference if the debug info differs, or None. | 
|  | If there are differences in the code, there will almost certainly be differences in the debug info too. | 
|  | """ | 
|  | dbga = dump_debug(objfilea) | 
|  | dbgb = dump_debug(objfileb) | 
|  | return first_diff(dbga, dbgb, objfilea, objfileb) | 
|  |  | 
|  | def compare_exact(objfilea, objfileb): | 
|  | """Byte for byte comparison between object files. | 
|  | Returns True if equal, False otherwise. | 
|  | """ | 
|  | return filecmp.cmp(objfilea, objfileb) | 
|  |  | 
|  | if __name__ == '__main__': | 
|  | parser = argparse.ArgumentParser() | 
|  | parser.add_argument('objfilea', nargs=1) | 
|  | parser.add_argument('objfileb', nargs=1) | 
|  | parser.add_argument('-v', '--verbose', action='store_true') | 
|  | args = parser.parse_args() | 
|  | diff = compare_object_files(args.objfilea[0], args.objfileb[0]) | 
|  | if diff: | 
|  | print("Difference detected") | 
|  | if args.verbose: | 
|  | print(diff) | 
|  | sys.exit(1) | 
|  | else: | 
|  | print("The same") |