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 section
= self
._section
_from
_spec
(section_spec
)
275 self
._emitline
("Section '%s' does not exist in the file!" % (
279 self
._emitline
("\nHex dump of section '%s':" % section
.name
)
281 addr
= section
['sh_addr']
282 data
= section
.data()
285 while dataptr
< len(data
):
286 bytesleft
= len(data
) - dataptr
287 # chunks of 16 bytes per line
288 linebytes
= 16 if bytesleft
> 16 else bytesleft
290 self
._emit
(' %s ' % self
._format
_hex
(addr
, fieldsize
=8))
293 self
._emit
('%2.2x' % ord(data
[dataptr
+ i
]))
299 for i
in range(linebytes
):
300 c
= data
[dataptr
+ i
]
301 if c
>= ' ' and ord(c
) <= 0x7f:
312 def display_string_dump(self
, section_spec
):
313 """ Display a strings dump of a section. section_spec is either a
314 section number or a name.
316 section
= self
._section
_from
_spec
(section_spec
)
318 self
._emitline
("Section '%s' does not exist in the file!" % (
322 printables
= set(string
.printable
)
323 self
._emitline
("\nString dump of section '%s':" % section
.name
)
326 data
= section
.data()
329 while dataptr
< len(data
):
330 while dataptr
< len(data
) and data
[dataptr
] not in printables
:
333 if dataptr
>= len(data
):
337 while endptr
< len(data
) and data
[endptr
] != '\x00':
341 self
._emitline
(' [%6x] %s' % (
342 dataptr
, data
[dataptr
:endptr
]))
347 self
._emitline
(' No strings found in this section.')
351 def _format_hex(self
, addr
, fieldsize
=None, fullhex
=False, lead0x
=True):
352 """ Format an address into a hexadecimal string.
355 Size of the hexadecimal field (with leading zeros to fit the
356 address into. For example with fieldsize=8, the format will
358 If None, the minimal required field size will be used.
361 If True, override fieldsize to set it to the maximal size
362 needed for the elfclass
365 If True, leading 0x is added
367 s
= '0x' if lead0x
else ''
369 fieldsize
= 8 if self
.elffile
.elfclass
== 32 else 16
370 if fieldsize
is None:
373 field
= '%' + '0%sx' % fieldsize
374 return s
+ field
% addr
376 def _section_from_spec(self
, spec
):
377 """ Retrieve a section given a "spec" (either number or name).
378 Return None if no such section exists in the file.
382 if num
< self
.elffile
.num_sections():
383 return self
.elffile
.get_section(num
)
387 # Not a number. Must be a name then
388 return self
.elffile
.get_section_by_name(spec
)
390 def _emit(self
, s
=''):
391 """ Emit an object to output
393 self
.output
.write(str(s
))
395 def _emitline(self
, s
=''):
396 """ Emit an object to output, followed by a newline
398 self
.output
.write(str(s
) + '\n')
401 SCRIPT_DESCRIPTION
= 'Display information about the contents of ELF format files'
402 VERSION_STRING
= '%%prog: based on pyelftools %s' % __version__
406 # parse the command-line arguments and invoke ReadElf
407 optparser
= OptionParser(
408 usage
='usage: %prog [options] <elf-file>',
409 description
=SCRIPT_DESCRIPTION
,
410 add_help_option
=False, # -h is a real option of readelf
412 version
=VERSION_STRING
)
413 optparser
.add_option('-H', '--help',
414 action
='store_true', dest
='help',
415 help='Display this information')
416 optparser
.add_option('-h', '--file-header',
417 action
='store_true', dest
='show_file_header',
418 help='Display the ELF file header')
419 optparser
.add_option('-l', '--program-headers', '--segments',
420 action
='store_true', dest
='show_program_header',
421 help='Display the program headers')
422 optparser
.add_option('-S', '--section-headers', '--sections',
423 action
='store_true', dest
='show_section_header',
424 help="Display the sections' headers")
425 optparser
.add_option('-e', '--headers',
426 action
='store_true', dest
='show_all_headers',
427 help='Equivalent to: -h -l -S')
428 optparser
.add_option('-s', '--symbols', '--syms',
429 action
='store_true', dest
='show_symbols',
430 help='Display the symbol table')
431 optparser
.add_option('-x', '--hex-dump',
432 action
='store', dest
='show_hex_dump', metavar
='<number|name>',
433 help='Dump the contents of section <number|name> as bytes')
434 optparser
.add_option('-p', '--string-dump',
435 action
='store', dest
='show_string_dump', metavar
='<number|name>',
436 help='Dump the contents of section <number|name> as strings')
438 options
, args
= optparser
.parse_args()
440 if options
.help or len(args
) == 0:
441 optparser
.print_help()
444 if options
.show_all_headers
:
445 do_file_header
= do_section_header
= do_program_header
= True
447 do_file_header
= options
.show_file_header
448 do_section_header
= options
.show_section_header
449 do_program_header
= options
.show_program_header
451 with
open(args
[0], 'rb') as file:
453 readelf
= ReadElf(file, sys
.stdout
)
455 readelf
.display_file_header()
456 if do_section_header
:
457 readelf
.display_section_headers(
458 show_heading
=not do_file_header
)
459 if do_program_header
:
460 readelf
.display_program_headers(
461 show_heading
=not do_file_header
)
462 if options
.show_symbols
:
463 readelf
.display_symbol_tables()
464 if options
.show_hex_dump
:
465 readelf
.display_hex_dump(options
.show_hex_dump
)
466 if options
.show_string_dump
:
467 readelf
.display_string_dump(options
.show_string_dump
)
468 except ELFError
as ex
:
469 sys
.stderr
.write('ELF error: %s\n' % ex
)
473 #-------------------------------------------------------------------------------
474 if __name__
== '__main__':