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