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