test suite implemented. fixing some problems in elftools and readelf
authorEli Bendersky <eliben@gmail.com>
Sat, 17 Sep 2011 07:39:29 +0000 (10:39 +0300)
committerEli Bendersky <eliben@gmail.com>
Sat, 17 Sep 2011 07:39:29 +0000 (10:39 +0300)
elftools/elf/descriptions.py
scripts/readelf.py
tests/run_tests.py

index 23e789df577a610cb53e1b17551ea977f33f1040..35232b98c33e1c6030680eff1d68de6ae27df46c 100644 (file)
@@ -142,7 +142,7 @@ _DESCR_P_TYPE = dict(
     PT_PHDR='PHDR',
     PT_GNU_EH_FRAME='GNU_EH_FRAME',
     PT_GNU_STACK='GNU_STACK',
-    PT_GNU_RELRO='GNU_RELR0',
+    PT_GNU_RELRO='GNU_RELRO',
 )
 
 _DESCR_P_FLAGS = {
index 7b3cc963ad64ea4f575c0ec01e5430d1ad6fa328..c284edc92dba0d0a4c16d9d531eb2500889ba9b4 100755 (executable)
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 #-------------------------------------------------------------------------------
-# readelf.py
+# scripts/readelf.py
 #
 # A clone of 'readelf' in Python, based on the pyelftools library
 #
@@ -73,10 +73,10 @@ class ReadElf(object):
                 describe_e_version_numeric(header['e_version']))
         self._emitline('  Entry point address:               %s' % 
                 self._format_hex(header['e_entry']))
-        self._emit('  Start of program headers           %s' % 
+        self._emit('  Start of program headers:          %s' % 
                 header['e_phoff'])
         self._emitline(' (bytes into file)')
-        self._emit('  Start of section headers           %s' % 
+        self._emit('  Start of section headers:          %s' % 
                 header['e_shoff'])
         self._emitline(' (bytes into file)')
         self._emitline('  Flags:                             %s' % 
@@ -100,6 +100,10 @@ class ReadElf(object):
             (Elf file type is...)
         """
         self._emitline()
+        if self.elffile.num_segments() == 0:
+            self._emitline('There are no program headers in this file.')
+            return
+
         elfheader = self.elffile.header
         if show_heading:
             self._emitline('Elf file type is %s' %
@@ -112,7 +116,7 @@ class ReadElf(object):
                 elfheader['e_phnum'], elfheader['e_phoff']))
             self._emitline()
 
-        self._emitline('Program headers:')
+        self._emitline('Program Headers:')
 
         # Now comes the table of program headers with their attributes. Note
         # that due to different formatting constraints of 32-bit and 64-bit
@@ -164,10 +168,10 @@ class ReadElf(object):
             return 
 
         self._emitline('\n Section to Segment mapping:')
-        self._emitline('  Segment Sections...\n')
+        self._emitline('  Segment Sections...')
 
         for nseg, segment in enumerate(self.elffile.iter_segments()):
-            self._emit('   %2.2d    ' % nseg)
+            self._emit('   %2.2d     ' % nseg)
 
             for section in self.elffile.iter_sections():
                 if (    not section.is_null() and 
@@ -184,7 +188,7 @@ class ReadElf(object):
             self._emitline('There are %s section headers, starting at offset %s' % (
                 elfheader['e_shnum'], self._format_hex(elfheader['e_shoff'])))
 
-        self._emitline('\nSection header%s:' % (
+        self._emitline('\nSection Header%s:' % (
             's' if elfheader['e_shnum'] > 1 else ''))
 
         # Different formatting constraints of 32-bit and 64-bit addresses
@@ -344,12 +348,12 @@ def main():
             readelf = ReadElf(file, sys.stdout)
             if do_file_header:
                 readelf.display_file_header()
-            if do_program_header:
-                readelf.display_program_headers(
-                        show_heading=not do_file_header)
             if do_section_header:
                 readelf.display_section_headers(
                         show_heading=not do_file_header)
+            if do_program_header:
+                readelf.display_program_headers(
+                        show_heading=not do_file_header)
             if options.show_symbols:
                 readelf.display_symbol_tables()
         except ELFError as ex:
index b86b3bf5987be4b817d83cd0d482cd431e3c74fa..f9169786f9c256fe5dc808879f1bf85dcdce4848 100755 (executable)
@@ -8,7 +8,16 @@
 # This code is in the public domain
 #-------------------------------------------------------------------------------
 import os, sys
+import logging
 import subprocess
+import tempfile
+
+
+# Create a global logger object
+#
+testlog = logging.getLogger('run_tests')
+testlog.setLevel(logging.DEBUG)
+testlog.addHandler(logging.StreamHandler(sys.stdout))
 
 
 def discover_testfiles(rootdir):
@@ -20,8 +29,82 @@ def discover_testfiles(rootdir):
             yield os.path.join(rootdir, filename)
 
 
+def run_exe(exe_path, args):
+    """ Runs the given executable as a subprocess, given the
+        list of arguments. Captures its return code (rc) and stdout and
+        returns a pair: rc, stdout_str
+    """
+    popen_cmd = [exe_path] + args
+    if os.path.splitext(exe_path)[1] == '.py':
+        popen_cmd.insert(0, 'python')
+    proc = subprocess.Popen(popen_cmd, stdout=subprocess.PIPE)
+    proc_stdout = proc.communicate()[0]
+    return proc.returncode, proc_stdout
+    
+
+def run_test_on_file(filename):
+    """ Runs a test on the given input filename
+    """
+    testlog.info("Running test on file '%s'" % filename)
+    for option in ['-e', '-s']:
+        testlog.info("..option='%s'" % option)
+        # stdouts will be a 2-element list: output of readelf and output 
+        # of scripts/readelf.py
+        stdouts = []
+        for exe_path in ['readelf', 'scripts/readelf.py']:
+            args = [option, filename]
+            testlog.info("....executing: '%s %s'" % (
+                exe_path, ' '.join(args)))
+            rc, stdout = run_exe(exe_path, args)
+            if rc != 0:
+                testlog.error("@@ aborting - '%s' returned '%s'" % (exe_path, rc))
+                break
+            stdouts.append(stdout)
+        testlog.info('....comparing output...')
+        success, errmsg = compare_output(*stdouts)
+        if success:
+            testlog.info('.......................SUCCESS')
+        else:
+            testlog.info('.......................FAIL')
+            testlog.info('@@ ' + errmsg)
+            dump_output_to_temp_files(*stdouts)
+
+
+def compare_output(s1, s2):
+    """ Compare stdout strings s1 and s2.
+        Return pair success, errmsg. If comparison succeeds, success is True
+        and errmsg is empty. Otherwise success is False and errmsg holds a
+        description of the mismatch.
+    """
+    lines1 = s1.splitlines()
+    lines2 = s2.splitlines()
+    if len(lines1) != len(lines2):
+        return False, 'Number of lines different: %s vs %s' % (
+                len(lines1), len(lines2))
+    for i in range(len(lines1)):
+        if lines1[i].split() != lines2[i].split():
+            errmsg = 'Mismatch on line #%s:\n>>%s<<\n>>%s<<\n' % (
+                    i, lines1[i], lines2[i])
+            return False, errmsg
+    return True, ''
+    
+
+def dump_output_to_temp_files(*args):
+    """ Dumps the output strings given in 'args' to temp files: one for each
+        arg.
+    """
+    for i, s in enumerate(args):
+        fd, path = tempfile.mkstemp(
+                prefix='out' + str(i + 1) + '_',
+                suffix='.stdout')
+        file = os.fdopen(fd, 'w')
+        file.write(s)
+        file.close()
+        testlog.info('@@ Output #%s dumped to file: %s' % (i + 1, path))
+    
+
 def die(msg):
-    print 'Error:', msg
+    testlog.error('Error: %s' % msg)
     sys.exit(1)
 
 
@@ -36,10 +119,15 @@ def main():
     if not is_in_rootdir():
         die('Please run me from the root dir of pyelftools!')
 
+    for filename in discover_testfiles('tests/testfiles'):
+        run_test_on_file(filename)
+
 
 if __name__ == '__main__':
     main()
-    print list(discover_testfiles('tests/testfiles'))
+    #testlog.info(list(discover_testfiles('tests/testfiles'))) 
+    #print run_exe('scripts/readelf.py', ['-h', 'tests/testfiles/z32.o.elf'])
+