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
, RelocationSection
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_relocations(self
):
270 """ Display the relocations contained in the file
272 has_relocation_sections
= False
273 for section
in self
.elffile
.iter_sections():
274 if not isinstance(section
, RelocationSection
):
277 has_relocation_sections
= True
278 self
._emitline
("\nRelocation section '%s' at offset %s contains %s entries:" % (
280 self
._format
_hex
(section
['sh_offset']),
281 section
.num_relocations()))
282 for rel
in section
.iter_relocations():
283 self
._emitline
('%s %s' % (
284 self
._format
_hex
(rel
['r_offset'], fullhex
=True, lead0x
=False),
285 self
._format
_hex
(rel
['r_info'], fullhex
=True, lead0x
=False)))
286 #print rel, rel.entry
288 if not has_relocation_sections
:
289 self
._emitline
('\nThere are no relocations in this file.')
291 def display_hex_dump(self
, section_spec
):
292 """ Display a hex dump of a section. section_spec is either a section
295 section
= self
._section
_from
_spec
(section_spec
)
297 self
._emitline
("Section '%s' does not exist in the file!" % (
301 self
._emitline
("\nHex dump of section '%s':" % section
.name
)
303 addr
= section
['sh_addr']
304 data
= section
.data()
307 while dataptr
< len(data
):
308 bytesleft
= len(data
) - dataptr
309 # chunks of 16 bytes per line
310 linebytes
= 16 if bytesleft
> 16 else bytesleft
312 self
._emit
(' %s ' % self
._format
_hex
(addr
, fieldsize
=8))
315 self
._emit
('%2.2x' % ord(data
[dataptr
+ i
]))
321 for i
in range(linebytes
):
322 c
= data
[dataptr
+ i
]
323 if c
>= ' ' and ord(c
) <= 0x7f:
334 def display_string_dump(self
, section_spec
):
335 """ Display a strings dump of a section. section_spec is either a
336 section number or a name.
338 section
= self
._section
_from
_spec
(section_spec
)
340 self
._emitline
("Section '%s' does not exist in the file!" % (
344 printables
= set(string
.printable
)
345 self
._emitline
("\nString dump of section '%s':" % section
.name
)
348 data
= section
.data()
351 while dataptr
< len(data
):
352 while dataptr
< len(data
) and data
[dataptr
] not in printables
:
355 if dataptr
>= len(data
):
359 while endptr
< len(data
) and data
[endptr
] != '\x00':
363 self
._emitline
(' [%6x] %s' % (
364 dataptr
, data
[dataptr
:endptr
]))
369 self
._emitline
(' No strings found in this section.')
373 def _format_hex(self
, addr
, fieldsize
=None, fullhex
=False, lead0x
=True):
374 """ Format an address into a hexadecimal string.
377 Size of the hexadecimal field (with leading zeros to fit the
378 address into. For example with fieldsize=8, the format will
380 If None, the minimal required field size will be used.
383 If True, override fieldsize to set it to the maximal size
384 needed for the elfclass
387 If True, leading 0x is added
389 s
= '0x' if lead0x
else ''
391 fieldsize
= 8 if self
.elffile
.elfclass
== 32 else 16
392 if fieldsize
is None:
395 field
= '%' + '0%sx' % fieldsize
396 return s
+ field
% addr
398 def _section_from_spec(self
, spec
):
399 """ Retrieve a section given a "spec" (either number or name).
400 Return None if no such section exists in the file.
404 if num
< self
.elffile
.num_sections():
405 return self
.elffile
.get_section(num
)
409 # Not a number. Must be a name then
410 return self
.elffile
.get_section_by_name(spec
)
412 def _emit(self
, s
=''):
413 """ Emit an object to output
415 self
.output
.write(str(s
))
417 def _emitline(self
, s
=''):
418 """ Emit an object to output, followed by a newline
420 self
.output
.write(str(s
) + '\n')
423 SCRIPT_DESCRIPTION
= 'Display information about the contents of ELF format files'
424 VERSION_STRING
= '%%prog: based on pyelftools %s' % __version__
428 # parse the command-line arguments and invoke ReadElf
429 optparser
= OptionParser(
430 usage
='usage: %prog [options] <elf-file>',
431 description
=SCRIPT_DESCRIPTION
,
432 add_help_option
=False, # -h is a real option of readelf
434 version
=VERSION_STRING
)
435 optparser
.add_option('-H', '--help',
436 action
='store_true', dest
='help',
437 help='Display this information')
438 optparser
.add_option('-h', '--file-header',
439 action
='store_true', dest
='show_file_header',
440 help='Display the ELF file header')
441 optparser
.add_option('-l', '--program-headers', '--segments',
442 action
='store_true', dest
='show_program_header',
443 help='Display the program headers')
444 optparser
.add_option('-S', '--section-headers', '--sections',
445 action
='store_true', dest
='show_section_header',
446 help="Display the sections' headers")
447 optparser
.add_option('-e', '--headers',
448 action
='store_true', dest
='show_all_headers',
449 help='Equivalent to: -h -l -S')
450 optparser
.add_option('-s', '--symbols', '--syms',
451 action
='store_true', dest
='show_symbols',
452 help='Display the symbol table')
453 optparser
.add_option('-r', '--relocs',
454 action
='store_true', dest
='show_relocs',
455 help='Display the relocations (if present)')
456 optparser
.add_option('-x', '--hex-dump',
457 action
='store', dest
='show_hex_dump', metavar
='<number|name>',
458 help='Dump the contents of section <number|name> as bytes')
459 optparser
.add_option('-p', '--string-dump',
460 action
='store', dest
='show_string_dump', metavar
='<number|name>',
461 help='Dump the contents of section <number|name> as strings')
463 options
, args
= optparser
.parse_args()
465 if options
.help or len(args
) == 0:
466 optparser
.print_help()
469 if options
.show_all_headers
:
470 do_file_header
= do_section_header
= do_program_header
= True
472 do_file_header
= options
.show_file_header
473 do_section_header
= options
.show_section_header
474 do_program_header
= options
.show_program_header
476 with
open(args
[0], 'rb') as file:
478 readelf
= ReadElf(file, sys
.stdout
)
480 readelf
.display_file_header()
481 if do_section_header
:
482 readelf
.display_section_headers(
483 show_heading
=not do_file_header
)
484 if do_program_header
:
485 readelf
.display_program_headers(
486 show_heading
=not do_file_header
)
487 if options
.show_symbols
:
488 readelf
.display_symbol_tables()
489 if options
.show_relocs
:
490 readelf
.display_relocations()
491 if options
.show_hex_dump
:
492 readelf
.display_hex_dump(options
.show_hex_dump
)
493 if options
.show_string_dump
:
494 readelf
.display_string_dump(options
.show_string_dump
)
495 except ELFError
as ex
:
496 sys
.stderr
.write('ELF error: %s\n' % ex
)
500 #-------------------------------------------------------------------------------
501 if __name__
== '__main__':