2 # Copyright 2008, Google Inc.
5 # Redistribution and use in source and binary forms, with or without
6 # modification, are permitted provided that the following conditions are
9 # * Redistributions of source code must retain the above copyright
10 # notice, this list of conditions and the following disclaimer.
11 # * Redistributions in binary form must reproduce the above
12 # copyright notice, this list of conditions and the following disclaimer
13 # in the documentation and/or other materials provided with the
15 # * Neither the name of Google Inc. nor the names of its
16 # contributors may be used to endorse or promote products derived from
17 # this software without specific prior written permission.
19 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 """discmp.py - compare disassembler output
34 This script compares output from the Native Client decoder to output
35 from GNU objdump to make sure they agree on instruction lengths. It
36 returns a zero status on success and non-zero on failure.
40 discmp.py <ncdis-exe> <test-binary>*
50 print("USAGE: discmp.py <ncdis-exe> <test-binary>*\n")
53 class DisFile(object):
54 """Processes variants of disassembler output from various sources,
55 providing a list of instruction lengths, based on addresses."""
57 def __init__(self, stream):
63 # note we ignore lines that have only continuation bytes;
65 self.dislinefmt = re.compile("\s*([0-9a-f]+):\t(.*\t+.*)")
66 self.data16fmt = re.compile("\s*([0-9a-f]+):\s+66\s+data16")
67 self.waitfmt = re.compile("\s*([0-9a-f]+):\s+9b\s+[f]?wait")
69 def NextDisInst(self):
71 line = self.fd.readline()
72 if line == "": return 0, None
73 match = self.data16fmt.match(line)
76 line = self.fd.readline()
77 match = self.dislinefmt.match(line)
78 return (int(addr, 16),
79 " " + addr + ":\t60 " + match.group(2) + "\n")
80 match = self.waitfmt.match(line)
83 line = self.fd.readline()
84 match = self.dislinefmt.match(line)
85 return (int(addr, 16),
86 " " + addr + ":\t9b " + match.group(2) + "\n")
87 match = self.dislinefmt.match(line)
89 return int(match.group(1), 16), line
92 if self.lastaddr is None:
93 self.lastaddr, self.lastline = self.NextDisInst()
95 self.lastaddr = self.thisaddr
96 self.lastline = self.thisline
97 self.thisaddr, self.thisline = self.NextDisInst()
98 if self.thisline is None:
99 # don't know how long the last line was, so just return 1
100 return (1, self.lastline)
101 if self.lastline is None:
104 return (self.thisaddr - self.lastaddr, self.lastline)
107 def IsElfBinary(fname):
109 iselfbinary = fd.readline().startswith("\x7fELF")
114 def CheckOneFile(fname, ncdis, objdump):
115 logging.info("processing %s", fname)
116 if not IsElfBinary(fname):
117 print("Error:", fname, "is not an ELF binary\nContinuing...")
119 df1 = DisFile(os.popen(objdump + " -dz " + fname))
120 df2 = DisFile(os.popen(ncdis + " " + fname))
124 len1, line1 = df1.NextInst()
125 len2, line2 = df2.NextInst()
126 if line1 is None: break
127 if line2 is None: break
129 logging.error("inst length mistmatch %d != %d\n", len1, len2)
134 logging.error("disasm output is different lengths\n")
136 logging.info("%s: %d instructions; 0 errors!\n", fname, instcount)
140 logging.basicConfig(level=logging.DEBUG,
143 logging.info("using ncdis at: %s", ncdis)
145 logging.info("using objdump at: %s", objdump)
146 for filename in argv[2:]:
147 if os.path.isdir(filename):
148 logging.info("traversing dir: %s", filename)
149 for dirpath, dirlist, filelist in os.walk(filename):
150 for fname in filelist:
151 CheckOneFile(os.path.join(dirpath, fname), ncdis, objdump)
153 CheckOneFile(filename, ncdis, objdump)
156 if '__main__' == __name__:
157 sys.exit(main(sys.argv))