1#!/usr/bin/env python2.7 2 3from __future__ import print_function 4 5import argparse 6import difflib 7import filecmp 8import os 9import subprocess 10import sys 11 12disassembler = 'objdump' 13 14def keep_line(line): 15 """Returns true for lines that should be compared in the disassembly 16 output.""" 17 return "file format" not in line 18 19def disassemble(objfile): 20 """Disassemble object to a file.""" 21 p = subprocess.Popen([disassembler, '-d', objfile], 22 stdout=subprocess.PIPE, 23 stderr=subprocess.PIPE) 24 (out, err) = p.communicate() 25 if p.returncode or err: 26 print("Disassemble failed: {}".format(objfile)) 27 sys.exit(1) 28 return filter(keep_line, out.split(os.linesep)) 29 30def dump_debug(objfile): 31 """Dump all of the debug info from a file.""" 32 p = subprocess.Popen([disassembler, '-WliaprmfsoRt', objfile], stdout=subprocess.PIPE, stderr=subprocess.PIPE) 33 (out, err) = p.communicate() 34 if p.returncode or err: 35 print("Dump debug failed: {}".format(objfile)) 36 sys.exit(1) 37 return filter(keep_line, out.split(os.linesep)) 38 39def first_diff(a, b, fromfile, tofile): 40 """Returns the first few lines of a difference, if there is one. Python 41 diff can be very slow with large objects and the most interesting changes 42 are the first ones. Truncate data before sending to difflib. Returns None 43 is there is no difference.""" 44 45 # Find first diff 46 first_diff_idx = None 47 for idx, val in enumerate(a): 48 if val != b[idx]: 49 first_diff_idx = idx 50 break 51 52 if first_diff_idx == None: 53 # No difference 54 return None 55 56 # Diff to first line of diff plus some lines 57 context = 3 58 diff = difflib.unified_diff(a[:first_diff_idx+context], 59 b[:first_diff_idx+context], 60 fromfile, 61 tofile) 62 difference = "\n".join(diff) 63 if first_diff_idx + context < len(a): 64 difference += "\n*** Diff truncated ***" 65 return difference 66 67def compare_object_files(objfilea, objfileb): 68 """Compare disassembly of two different files. 69 Allowing unavoidable differences, such as filenames. 70 Return the first difference if the disassembly differs, or None. 71 """ 72 disa = disassemble(objfilea) 73 disb = disassemble(objfileb) 74 return first_diff(disa, disb, objfilea, objfileb) 75 76def compare_debug_info(objfilea, objfileb): 77 """Compare debug info of two different files. 78 Allowing unavoidable differences, such as filenames. 79 Return the first difference if the debug info differs, or None. 80 If there are differences in the code, there will almost certainly be differences in the debug info too. 81 """ 82 dbga = dump_debug(objfilea) 83 dbgb = dump_debug(objfileb) 84 return first_diff(dbga, dbgb, objfilea, objfileb) 85 86def compare_exact(objfilea, objfileb): 87 """Byte for byte comparison between object files. 88 Returns True if equal, False otherwise. 89 """ 90 return filecmp.cmp(objfilea, objfileb) 91 92if __name__ == '__main__': 93 parser = argparse.ArgumentParser() 94 parser.add_argument('objfilea', nargs=1) 95 parser.add_argument('objfileb', nargs=1) 96 parser.add_argument('-v', '--verbose', action='store_true') 97 args = parser.parse_args() 98 diff = compare_object_files(args.objfilea[0], args.objfileb[0]) 99 if diff: 100 print("Difference detected") 101 if args.verbose: 102 print(diff) 103 sys.exit(1) 104 else: 105 print("The same") 106