#!/usr/bin/env python
#-------------------------------------------------------------------------------
-# readelf.py
+# scripts/readelf.py
#
# A clone of 'readelf' in Python, based on the pyelftools library
#
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' %
(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' %
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
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
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
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:
# 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):
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)
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'])
+