106 lines
3.5 KiB
Python
106 lines
3.5 KiB
Python
|
#!/usr/bin/env python
|
||
|
|
||
|
from __future__ import absolute_import, division, print_function
|
||
|
|
||
|
import argparse
|
||
|
import difflib
|
||
|
import filecmp
|
||
|
import os
|
||
|
import subprocess
|
||
|
import sys
|
||
|
|
||
|
disassembler = 'objdump'
|
||
|
|
||
|
def keep_line(line):
|
||
|
"""Returns true for lines that should be compared in the disassembly
|
||
|
output."""
|
||
|
return "file format" not in line
|
||
|
|
||
|
def disassemble(objfile):
|
||
|
"""Disassemble object to a file."""
|
||
|
p = subprocess.Popen([disassembler, '-d', objfile],
|
||
|
stdout=subprocess.PIPE,
|
||
|
stderr=subprocess.PIPE)
|
||
|
(out, err) = p.communicate()
|
||
|
if p.returncode or err:
|
||
|
print("Disassemble failed: {}".format(objfile))
|
||
|
sys.exit(1)
|
||
|
return [line for line in out.split(os.linesep) if keep_line(line)]
|
||
|
|
||
|
def dump_debug(objfile):
|
||
|
"""Dump all of the debug info from a file."""
|
||
|
p = subprocess.Popen([disassembler, '-WliaprmfsoRt', objfile], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||
|
(out, err) = p.communicate()
|
||
|
if p.returncode or err:
|
||
|
print("Dump debug failed: {}".format(objfile))
|
||
|
sys.exit(1)
|
||
|
return [line for line in out.split(os.linesep) if keep_line(line)]
|
||
|
|
||
|
def first_diff(a, b, fromfile, tofile):
|
||
|
"""Returns the first few lines of a difference, if there is one. Python
|
||
|
diff can be very slow with large objects and the most interesting changes
|
||
|
are the first ones. Truncate data before sending to difflib. Returns None
|
||
|
is there is no difference."""
|
||
|
|
||
|
# Find first diff
|
||
|
first_diff_idx = None
|
||
|
for idx, val in enumerate(a):
|
||
|
if val != b[idx]:
|
||
|
first_diff_idx = idx
|
||
|
break
|
||
|
|
||
|
if first_diff_idx == None:
|
||
|
# No difference
|
||
|
return None
|
||
|
|
||
|
# Diff to first line of diff plus some lines
|
||
|
context = 3
|
||
|
diff = difflib.unified_diff(a[:first_diff_idx+context],
|
||
|
b[:first_diff_idx+context],
|
||
|
fromfile,
|
||
|
tofile)
|
||
|
difference = "\n".join(diff)
|
||
|
if first_diff_idx + context < len(a):
|
||
|
difference += "\n*** Diff truncated ***"
|
||
|
return difference
|
||
|
|
||
|
def compare_object_files(objfilea, objfileb):
|
||
|
"""Compare disassembly of two different files.
|
||
|
Allowing unavoidable differences, such as filenames.
|
||
|
Return the first difference if the disassembly differs, or None.
|
||
|
"""
|
||
|
disa = disassemble(objfilea)
|
||
|
disb = disassemble(objfileb)
|
||
|
return first_diff(disa, disb, objfilea, objfileb)
|
||
|
|
||
|
def compare_debug_info(objfilea, objfileb):
|
||
|
"""Compare debug info of two different files.
|
||
|
Allowing unavoidable differences, such as filenames.
|
||
|
Return the first difference if the debug info differs, or None.
|
||
|
If there are differences in the code, there will almost certainly be differences in the debug info too.
|
||
|
"""
|
||
|
dbga = dump_debug(objfilea)
|
||
|
dbgb = dump_debug(objfileb)
|
||
|
return first_diff(dbga, dbgb, objfilea, objfileb)
|
||
|
|
||
|
def compare_exact(objfilea, objfileb):
|
||
|
"""Byte for byte comparison between object files.
|
||
|
Returns True if equal, False otherwise.
|
||
|
"""
|
||
|
return filecmp.cmp(objfilea, objfileb)
|
||
|
|
||
|
if __name__ == '__main__':
|
||
|
parser = argparse.ArgumentParser()
|
||
|
parser.add_argument('objfilea', nargs=1)
|
||
|
parser.add_argument('objfileb', nargs=1)
|
||
|
parser.add_argument('-v', '--verbose', action='store_true')
|
||
|
args = parser.parse_args()
|
||
|
diff = compare_object_files(args.objfilea[0], args.objfileb[0])
|
||
|
if diff:
|
||
|
print("Difference detected")
|
||
|
if args.verbose:
|
||
|
print(diff)
|
||
|
sys.exit(1)
|
||
|
else:
|
||
|
print("The same")
|