150
|
1 #!/usr/bin/env python
|
|
2
|
|
3 from __future__ import absolute_import, division, print_function
|
|
4
|
|
5 import argparse
|
|
6 import difflib
|
|
7 import filecmp
|
|
8 import os
|
|
9 import subprocess
|
|
10 import sys
|
|
11
|
|
12 disassembler = 'objdump'
|
|
13
|
|
14 def 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
|
|
19 def 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 [line for line in out.split(os.linesep) if keep_line(line)]
|
|
29
|
|
30 def 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 [line for line in out.split(os.linesep) if keep_line(line)]
|
|
38
|
|
39 def 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
|
|
67 def 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
|
|
76 def 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
|
|
86 def 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
|
|
92 if __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")
|