2 #-------------------------------------------------------------------------------
5 # A clone of 'readelf' in Python, based on the pyelftools library
7 # Eli Bendersky (eliben@gmail.com)
8 # This code is in the public domain
9 #-------------------------------------------------------------------------------
11 from optparse
import OptionParser
15 # If elftools is not installed, maybe we're running from the root or scripts
16 # dir of the source distribution
21 sys
.path
.extend(['.', '..'])
23 from elftools
import __version__
24 from elftools
.common
.exceptions
import ELFError
25 from elftools
.elf
.elffile
import ELFFile
26 from elftools
.elf
.segments
import InterpSegment
27 from elftools
.elf
.sections
import SymbolTableSection
28 from elftools
.elf
.descriptions
import (
29 describe_ei_class
, describe_ei_data
, describe_ei_version
,
30 describe_ei_osabi
, describe_e_type
, describe_e_machine
,
31 describe_e_version_numeric
, describe_p_type
, describe_p_flags
,
32 describe_sh_type
, describe_sh_flags
,
33 describe_symbol_type
, describe_symbol_bind
, describe_symbol_visibility
,
34 describe_symbol_shndx
,
38 class ReadElf(object):
39 """ display_* methods are used to emit output into the output stream
41 def __init__(self
, file, output
):
43 stream object with the ELF file to read
46 output stream to write to
48 self
.elffile
= ELFFile(file)
51 def display_file_header(self
):
52 """ Display the ELF file header
54 self
._emitline
('ELF Header:')
55 self
._emit
(' Magic: ')
56 self
._emitline
(' '.join('%2.2x' % ord(b
)
57 for b
in self
.elffile
.e_ident_raw
))
58 header
= self
.elffile
.header
59 e_ident
= header
['e_ident']
60 self
._emitline
(' Class: %s' %
61 describe_ei_class(e_ident
['EI_CLASS']))
62 self
._emitline
(' Data: %s' %
63 describe_ei_data(e_ident
['EI_DATA']))
64 self
._emitline
(' Version: %s' %
65 describe_ei_version(e_ident
['EI_VERSION']))
66 self
._emitline
(' OS/ABI: %s' %
67 describe_ei_osabi(e_ident
['EI_OSABI']))
68 self
._emitline
(' ABI Version: %d' %
69 e_ident
['EI_ABIVERSION'])
70 self
._emitline
(' Type: %s' %
71 describe_e_type(header
['e_type']))
72 self
._emitline
(' Machine: %s' %
73 describe_e_machine(header
['e_machine']))
74 self
._emitline
(' Version: %s' %
75 describe_e_version_numeric(header
['e_version']))
76 self
._emitline
(' Entry point address: %s' %
77 self
._format
_hex
(header
['e_entry']))
78 self
._emit
(' Start of program headers: %s' %
80 self
._emitline
(' (bytes into file)')
81 self
._emit
(' Start of section headers: %s' %
83 self
._emitline
(' (bytes into file)')
84 self
._emitline
(' Flags: %s' %
85 self
._format
_hex
(header
['e_flags']))
86 self
._emitline
(' Size of this header: %s (bytes)' %
88 self
._emitline
(' Size of program headers: %s (bytes)' %
89 header
['e_phentsize'])
90 self
._emitline
(' Number of program headers: %s' %
92 self
._emitline
(' Size of section headers: %s (bytes)' %
93 header
['e_shentsize'])
94 self
._emitline
(' Number of section headers: %s' %
96 self
._emitline
(' Section header string table index: %s' %
99 def display_program_headers(self
, show_heading
=True):
100 """ Display the ELF program headers.
101 If show_heading is True, displays the heading for this information
102 (Elf file type is...)
105 if self
.elffile
.num_segments() == 0:
106 self
._emitline
('There are no program headers in this file.')
109 elfheader
= self
.elffile
.header
111 self
._emitline
('Elf file type is %s' %
112 describe_e_type(elfheader
['e_type']))
113 self
._emitline
('Entry point is %s' %
114 self
._format
_hex
(elfheader
['e_entry']))
115 # readelf weirness - why isn't e_phoff printed as hex? (for section
117 self
._emitline
('There are %s program headers, starting at offset %s' % (
118 elfheader
['e_phnum'], elfheader
['e_phoff']))
121 self
._emitline
('Program Headers:')
123 # Now comes the table of program headers with their attributes. Note
124 # that due to different formatting constraints of 32-bit and 64-bit
125 # addresses, there are some conditions on elfclass here.
127 # First comes the table heading
129 if self
.elffile
.elfclass
== 32:
130 self
._emitline
(' Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align')
132 self
._emitline
(' Type Offset VirtAddr PhysAddr')
133 self
._emitline
(' FileSiz MemSiz Flags Align')
137 for segment
in self
.elffile
.iter_segments():
138 self
._emit
(' %-14s ' % describe_p_type(segment
['p_type']))
140 if self
.elffile
.elfclass
== 32:
141 self
._emitline
('%s %s %s %s %s %-3s %s' % (
142 self
._format
_hex
(segment
['p_offset'], fieldsize
=6),
143 self
._format
_hex
(segment
['p_vaddr'], fullhex
=True),
144 self
._format
_hex
(segment
['p_paddr'], fullhex
=True),
145 self
._format
_hex
(segment
['p_filesz'], fieldsize
=5),
146 self
._format
_hex
(segment
['p_memsz'], fieldsize
=5),
147 describe_p_flags(segment
['p_flags']),
148 self
._format
_hex
(segment
['p_align'])))
150 self
._emitline
('%s %s %s' % (
151 self
._format
_hex
(segment
['p_offset'], fullhex
=True),
152 self
._format
_hex
(segment
['p_vaddr'], fullhex
=True),
153 self
._format
_hex
(segment
['p_paddr'], fullhex
=True)))
154 self
._emitline
(' %s %s %-3s %s' % (
155 self
._format
_hex
(segment
['p_filesz'], fullhex
=True),
156 self
._format
_hex
(segment
['p_memsz'], fullhex
=True),
157 describe_p_flags(segment
['p_flags']),
158 # lead0x set to False for p_align, to mimic readelf.
159 # No idea why the difference from 32-bit mode :-|
160 self
._format
_hex
(segment
['p_align'], lead0x
=False)))
162 if isinstance(segment
, InterpSegment
):
163 self
._emitline
(' [Requesting program interpreter: %s]' %
164 segment
.get_interp_name())
166 # Sections to segments mapping
168 if self
.elffile
.num_sections() == 0:
169 # No sections? We're done
172 self
._emitline
('\n Section to Segment mapping:')
173 self
._emitline
(' Segment Sections...')
175 for nseg
, segment
in enumerate(self
.elffile
.iter_segments()):
176 self
._emit
(' %2.2d ' % nseg
)
178 for section
in self
.elffile
.iter_sections():
179 if ( not section
.is_null() and
180 segment
.section_in_segment(section
)):
181 self
._emit
('%s ' % section
.name
)
185 def display_section_headers(self
, show_heading
=True):
186 """ Display the ELF section headers
188 elfheader
= self
.elffile
.header
190 self
._emitline
('There are %s section headers, starting at offset %s' % (
191 elfheader
['e_shnum'], self
._format
_hex
(elfheader
['e_shoff'])))
193 self
._emitline
('\nSection Header%s:' % (
194 's' if elfheader
['e_shnum'] > 1 else ''))
196 # Different formatting constraints of 32-bit and 64-bit addresses
198 if self
.elffile
.elfclass
== 32:
199 self
._emitline
(' [Nr] Name Type Addr Off Size ES Flg Lk Inf Al')
201 self
._emitline
(' [Nr] Name Type Address Offset')
202 self
._emitline
(' Size EntSize Flags Link Info Align')
206 for nsec
, section
in enumerate(self
.elffile
.iter_sections()):
207 self
._emit
(' [%2u] %-17.17s %-15.15s ' % (
208 nsec
, section
.name
, describe_sh_type(section
['sh_type'])))
210 if self
.elffile
.elfclass
== 32:
211 self
._emitline
('%s %s %s %s %3s %2s %3s %2s' % (
212 self
._format
_hex
(section
['sh_addr'], fieldsize
=8, lead0x
=False),
213 self
._format
_hex
(section
['sh_offset'], fieldsize
=6, lead0x
=False),
214 self
._format
_hex
(section
['sh_size'], fieldsize
=6, lead0x
=False),
215 self
._format
_hex
(section
['sh_entsize'], fieldsize
=2, lead0x
=False),
216 describe_sh_flags(section
['sh_flags']),
217 section
['sh_link'], section
['sh_info'],
218 section
['sh_addralign']))
220 self
._emitline
(' %s %s' % (
221 self
._format
_hex
(section
['sh_addr'], fullhex
=True, lead0x
=False),
222 self
._format
_hex
(section
['sh_offset'],
223 fieldsize
=16 if section
['sh_offset'] > 0xffffffff else 8,
225 self
._emitline
(' %s %s %3s %2s %3s %s' % (
226 self
._format
_hex
(section
['sh_size'], fullhex
=True, lead0x
=False),
227 self
._format
_hex
(section
['sh_entsize'], fullhex
=True, lead0x
=False),
228 describe_sh_flags(section
['sh_flags']),
229 section
['sh_link'], section
['sh_info'],
230 section
['sh_addralign']))
232 self
._emitline
('Key to Flags:')
233 self
._emitline
(' W (write), A (alloc), X (execute), M (merge), S (strings)')
234 self
._emitline
(' I (info), L (link order), G (group), x (unknown)')
235 self
._emitline
(' O (extra OS processing required) o (OS specific), p (processor specific)')
237 def display_symbol_tables(self
):
238 """ Display the symbol tables contained in the file
240 for section
in self
.elffile
.iter_sections():
241 if not isinstance(section
, SymbolTableSection
):
244 if section
['sh_entsize'] == 0:
245 self
._emitline
("\nSymbol table '%s' has a sh_entsize of zero!" % (
249 self
._emitline
("\nSymbol table '%s' contains %s entries:" % (
250 section
.name
, section
.num_symbols()))
252 if self
.elffile
.elfclass
== 32:
253 self
._emitline
(' Num: Value Size Type Bind Vis Ndx Name')
255 self
._emitline
(' Num: Value Size Type Bind Vis Ndx Name')
257 for nsym
, symbol
in enumerate(section
.iter_symbols()):
258 # symbol names are truncated to 25 chars, similarly to readelf
259 self
._emitline
('%6d: %s %5d %-7s %-6s %-7s %4s %.25s' % (
261 self
._format
_hex
(symbol
['st_value'], fullhex
=True, lead0x
=False),
263 describe_symbol_type(symbol
['st_info']['type']),
264 describe_symbol_bind(symbol
['st_info']['bind']),
265 describe_symbol_visibility(symbol
['st_other']['visibility']),
266 describe_symbol_shndx(symbol
['st_shndx']),
269 def display_hex_dump(self
, section_spec
):
270 """ Display a hex dump of a section. section_spec is either a section
273 secnum
= self
._section
_num
_from
_spec
(section_spec
)
275 self
._emitline
("Section '%s' does not exist in the file!" % (
279 section
= self
.elffile
.get_section(secnum
)
280 self
._emitline
("\nHex dump of section '%s':" % section
.name
)
282 addr
= section
['sh_addr']
283 data
= section
.data()
286 while dataptr
< len(data
):
287 bytesleft
= len(data
) - dataptr
288 # chunks of 16 bytes per line
289 linebytes
= 16 if bytesleft
> 16 else bytesleft
291 self
._emit
(' %s ' % self
._format
_hex
(addr
, fieldsize
=8))
294 self
._emit
('%2.2x' % ord(data
[dataptr
+ i
]))
300 for i
in range(linebytes
):
301 c
= data
[dataptr
+ i
]
302 if c
>= ' ' and ord(c
) <= 0x7f:
313 def display_string_dump(self
, section_spec
):
314 """ Display a strings dump of a section. section_spec is either a
315 section number or a name.
317 secnum
= self
._section
_num
_from
_spec
(section_spec
)
319 self
._emitline
("Section '%s' does not exist in the file!" % (
323 printables
= set(string
.printable
)
325 section
= self
.elffile
.get_section(secnum
)
326 self
._emitline
("\nString dump of section '%s':" % section
.name
)
329 data
= section
.data()
332 while dataptr
< len(data
):
333 while dataptr
< len(data
) and data
[dataptr
] not in printables
:
336 if dataptr
>= len(data
):
340 while endptr
< len(data
) and data
[endptr
] != '\x00':
344 self
._emitline
(' [%6x] %s' % (
345 dataptr
, data
[dataptr
:endptr
]))
350 self
._emitline
(' No strings found in this section.')
354 def _format_hex(self
, addr
, fieldsize
=None, fullhex
=False, lead0x
=True):
355 """ Format an address into a hexadecimal string.
358 Size of the hexadecimal field (with leading zeros to fit the
359 address into. For example with fieldsize=8, the format will
361 If None, the minimal required field size will be used.
364 If True, override fieldsize to set it to the maximal size
365 needed for the elfclass
368 If True, leading 0x is added
370 s
= '0x' if lead0x
else ''
372 fieldsize
= 8 if self
.elffile
.elfclass
== 32 else 16
373 if fieldsize
is None:
376 field
= '%' + '0%sx' % fieldsize
377 return s
+ field
% addr
379 def _section_num_from_spec(self
, spec
):
380 """ Translate a section "spec" (either number or name) to its number.
381 Return None if no such section exists in the file.
385 return num
if num
< self
.elffile
.num_sections() else None
387 # Not a number. Must be a name then
388 for nsec
, section
in enumerate(self
.elffile
.iter_sections()):
389 if section
.name
== spec
:
392 def _emit(self
, s
=''):
393 """ Emit an object to output
395 self
.output
.write(str(s
))
397 def _emitline(self
, s
=''):
398 """ Emit an object to output, followed by a newline
400 self
.output
.write(str(s
) + '\n')
403 SCRIPT_DESCRIPTION
= 'Display information about the contents of ELF format files'
404 VERSION_STRING
= '%%prog: based on pyelftools %s' % __version__
408 # parse the command-line arguments and invoke ReadElf
409 optparser
= OptionParser(
410 usage
='usage: %prog [options] <elf-file>',
411 description
=SCRIPT_DESCRIPTION
,
412 add_help_option
=False, # -h is a real option of readelf
414 version
=VERSION_STRING
)
415 optparser
.add_option('-H', '--help',
416 action
='store_true', dest
='help',
417 help='Display this information')
418 optparser
.add_option('-h', '--file-header',
419 action
='store_true', dest
='show_file_header',
420 help='Display the ELF file header')
421 optparser
.add_option('-l', '--program-headers', '--segments',
422 action
='store_true', dest
='show_program_header',
423 help='Display the program headers')
424 optparser
.add_option('-S', '--section-headers', '--sections',
425 action
='store_true', dest
='show_section_header',
426 help="Display the sections' headers")
427 optparser
.add_option('-e', '--headers',
428 action
='store_true', dest
='show_all_headers',
429 help='Equivalent to: -h -l -S')
430 optparser
.add_option('-s', '--symbols', '--syms',
431 action
='store_true', dest
='show_symbols',
432 help='Display the symbol table')
433 optparser
.add_option('-x', '--hex-dump',
434 action
='store', dest
='show_hex_dump', metavar
='<number|name>',
435 help='Dump the contents of section <number|name> as bytes')
436 optparser
.add_option('-p', '--string-dump',
437 action
='store', dest
='show_string_dump', metavar
='<number|name>',
438 help='Dump the contents of section <number|name> as strings')
440 options
, args
= optparser
.parse_args()
442 if options
.help or len(args
) == 0:
443 optparser
.print_help()
446 if options
.show_all_headers
:
447 do_file_header
= do_section_header
= do_program_header
= True
449 do_file_header
= options
.show_file_header
450 do_section_header
= options
.show_section_header
451 do_program_header
= options
.show_program_header
453 with
open(args
[0], 'rb') as file:
455 readelf
= ReadElf(file, sys
.stdout
)
457 readelf
.display_file_header()
458 if do_section_header
:
459 readelf
.display_section_headers(
460 show_heading
=not do_file_header
)
461 if do_program_header
:
462 readelf
.display_program_headers(
463 show_heading
=not do_file_header
)
464 if options
.show_symbols
:
465 readelf
.display_symbol_tables()
466 if options
.show_hex_dump
:
467 readelf
.display_hex_dump(options
.show_hex_dump
)
468 if options
.show_string_dump
:
469 readelf
.display_string_dump(options
.show_string_dump
)
470 except ELFError
as ex
:
471 sys
.stderr
.write('ELF error: %s\n' % ex
)
475 #-------------------------------------------------------------------------------
476 if __name__
== '__main__':