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
, describe_reloc_type
,
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 if section
.is_RELA():
283 self
._emitline
(" Offset Info Type Sym. Value Sym. Name + Addend")
285 self
._emitline
(" Offset Info Type Sym.Value Sym. Name")
287 # The symbol table section pointed to in sh_link
288 symtable
= self
.elffile
.get_section(section
['sh_link'])
290 for rel
in section
.iter_relocations():
291 hexwidth
= 8 if self
.elffile
.elfclass
== 32 else 12
292 self
._emit
('%s %s %-17.17s' % (
293 self
._format
_hex
(rel
['r_offset'],
294 fieldsize
=hexwidth
, lead0x
=False),
295 self
._format
_hex
(rel
['r_info'],
296 fieldsize
=hexwidth
, lead0x
=False),
298 rel
['r_info_type'], self
.elffile
['e_machine'])))
300 if rel
['r_info_sym'] == 0:
304 symbol
= symtable
.get_symbol(rel
['r_info_sym'])
305 self
._emit
(' %s %s%s' % (
308 fullhex
=True, lead0x
=False),
309 ' ' if self
.elffile
.elfclass
== 32 else '',
311 if section
.is_RELA():
312 self
._emit
(' %s %x' % (
313 '+' if rel
['r_addend'] >= 0 else '-',
314 abs(rel
['r_addend'])))
317 if not has_relocation_sections
:
318 self
._emitline
('\nThere are no relocations in this file.')
320 def display_hex_dump(self
, section_spec
):
321 """ Display a hex dump of a section. section_spec is either a section
324 section
= self
._section
_from
_spec
(section_spec
)
326 self
._emitline
("Section '%s' does not exist in the file!" % (
330 self
._emitline
("\nHex dump of section '%s':" % section
.name
)
332 addr
= section
['sh_addr']
333 data
= section
.data()
336 while dataptr
< len(data
):
337 bytesleft
= len(data
) - dataptr
338 # chunks of 16 bytes per line
339 linebytes
= 16 if bytesleft
> 16 else bytesleft
341 self
._emit
(' %s ' % self
._format
_hex
(addr
, fieldsize
=8))
344 self
._emit
('%2.2x' % ord(data
[dataptr
+ i
]))
350 for i
in range(linebytes
):
351 c
= data
[dataptr
+ i
]
352 if c
>= ' ' and ord(c
) < 0x7f:
363 def display_string_dump(self
, section_spec
):
364 """ Display a strings dump of a section. section_spec is either a
365 section number or a name.
367 section
= self
._section
_from
_spec
(section_spec
)
369 self
._emitline
("Section '%s' does not exist in the file!" % (
373 printables
= set(string
.printable
)
374 self
._emitline
("\nString dump of section '%s':" % section
.name
)
377 data
= section
.data()
380 while dataptr
< len(data
):
381 while dataptr
< len(data
) and data
[dataptr
] not in printables
:
384 if dataptr
>= len(data
):
388 while endptr
< len(data
) and data
[endptr
] != '\x00':
392 self
._emitline
(' [%6x] %s' % (
393 dataptr
, data
[dataptr
:endptr
]))
398 self
._emitline
(' No strings found in this section.')
402 def _format_hex(self
, addr
, fieldsize
=None, fullhex
=False, lead0x
=True):
403 """ Format an address into a hexadecimal string.
406 Size of the hexadecimal field (with leading zeros to fit the
407 address into. For example with fieldsize=8, the format will
409 If None, the minimal required field size will be used.
412 If True, override fieldsize to set it to the maximal size
413 needed for the elfclass
416 If True, leading 0x is added
418 s
= '0x' if lead0x
else ''
420 fieldsize
= 8 if self
.elffile
.elfclass
== 32 else 16
421 if fieldsize
is None:
424 field
= '%' + '0%sx' % fieldsize
425 return s
+ field
% addr
427 def _section_from_spec(self
, spec
):
428 """ Retrieve a section given a "spec" (either number or name).
429 Return None if no such section exists in the file.
433 if num
< self
.elffile
.num_sections():
434 return self
.elffile
.get_section(num
)
438 # Not a number. Must be a name then
439 return self
.elffile
.get_section_by_name(spec
)
441 def _emit(self
, s
=''):
442 """ Emit an object to output
444 self
.output
.write(str(s
))
446 def _emitline(self
, s
=''):
447 """ Emit an object to output, followed by a newline
449 self
.output
.write(str(s
) + '\n')
452 SCRIPT_DESCRIPTION
= 'Display information about the contents of ELF format files'
453 VERSION_STRING
= '%%prog: based on pyelftools %s' % __version__
457 # parse the command-line arguments and invoke ReadElf
458 optparser
= OptionParser(
459 usage
='usage: %prog [options] <elf-file>',
460 description
=SCRIPT_DESCRIPTION
,
461 add_help_option
=False, # -h is a real option of readelf
463 version
=VERSION_STRING
)
464 optparser
.add_option('-H', '--help',
465 action
='store_true', dest
='help',
466 help='Display this information')
467 optparser
.add_option('-h', '--file-header',
468 action
='store_true', dest
='show_file_header',
469 help='Display the ELF file header')
470 optparser
.add_option('-l', '--program-headers', '--segments',
471 action
='store_true', dest
='show_program_header',
472 help='Display the program headers')
473 optparser
.add_option('-S', '--section-headers', '--sections',
474 action
='store_true', dest
='show_section_header',
475 help="Display the sections' headers")
476 optparser
.add_option('-e', '--headers',
477 action
='store_true', dest
='show_all_headers',
478 help='Equivalent to: -h -l -S')
479 optparser
.add_option('-s', '--symbols', '--syms',
480 action
='store_true', dest
='show_symbols',
481 help='Display the symbol table')
482 optparser
.add_option('-r', '--relocs',
483 action
='store_true', dest
='show_relocs',
484 help='Display the relocations (if present)')
485 optparser
.add_option('-x', '--hex-dump',
486 action
='store', dest
='show_hex_dump', metavar
='<number|name>',
487 help='Dump the contents of section <number|name> as bytes')
488 optparser
.add_option('-p', '--string-dump',
489 action
='store', dest
='show_string_dump', metavar
='<number|name>',
490 help='Dump the contents of section <number|name> as strings')
492 options
, args
= optparser
.parse_args()
494 if options
.help or len(args
) == 0:
495 optparser
.print_help()
498 if options
.show_all_headers
:
499 do_file_header
= do_section_header
= do_program_header
= True
501 do_file_header
= options
.show_file_header
502 do_section_header
= options
.show_section_header
503 do_program_header
= options
.show_program_header
505 with
open(args
[0], 'rb') as file:
507 readelf
= ReadElf(file, sys
.stdout
)
509 readelf
.display_file_header()
510 if do_section_header
:
511 readelf
.display_section_headers(
512 show_heading
=not do_file_header
)
513 if do_program_header
:
514 readelf
.display_program_headers(
515 show_heading
=not do_file_header
)
516 if options
.show_symbols
:
517 readelf
.display_symbol_tables()
518 if options
.show_relocs
:
519 readelf
.display_relocations()
520 if options
.show_hex_dump
:
521 readelf
.display_hex_dump(options
.show_hex_dump
)
522 if options
.show_string_dump
:
523 readelf
.display_string_dump(options
.show_string_dump
)
524 except ELFError
as ex
:
525 sys
.stderr
.write('ELF error: %s\n' % ex
)
529 #-------------------------------------------------------------------------------
530 if __name__
== '__main__':