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