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
,
36 from elftools
.dwarf
.dwarfinfo
import DWARFInfo
, DebugSectionLocator
37 from elftools
.dwarf
.descriptions
import describe_attr_value
40 class ReadElf(object):
41 """ display_* methods are used to emit output into the output stream
43 def __init__(self
, file, output
):
45 stream object with the ELF file to read
48 output stream to write to
50 self
.elffile
= ELFFile(file)
53 # Lazily initialized if a debug dump is requested
54 self
._dwarfinfo
= None
56 def display_file_header(self
):
57 """ Display the ELF file header
59 self
._emitline
('ELF Header:')
60 self
._emit
(' Magic: ')
61 self
._emitline
(' '.join('%2.2x' % ord(b
)
62 for b
in self
.elffile
.e_ident_raw
))
63 header
= self
.elffile
.header
64 e_ident
= header
['e_ident']
65 self
._emitline
(' Class: %s' %
66 describe_ei_class(e_ident
['EI_CLASS']))
67 self
._emitline
(' Data: %s' %
68 describe_ei_data(e_ident
['EI_DATA']))
69 self
._emitline
(' Version: %s' %
70 describe_ei_version(e_ident
['EI_VERSION']))
71 self
._emitline
(' OS/ABI: %s' %
72 describe_ei_osabi(e_ident
['EI_OSABI']))
73 self
._emitline
(' ABI Version: %d' %
74 e_ident
['EI_ABIVERSION'])
75 self
._emitline
(' Type: %s' %
76 describe_e_type(header
['e_type']))
77 self
._emitline
(' Machine: %s' %
78 describe_e_machine(header
['e_machine']))
79 self
._emitline
(' Version: %s' %
80 describe_e_version_numeric(header
['e_version']))
81 self
._emitline
(' Entry point address: %s' %
82 self
._format
_hex
(header
['e_entry']))
83 self
._emit
(' Start of program headers: %s' %
85 self
._emitline
(' (bytes into file)')
86 self
._emit
(' Start of section headers: %s' %
88 self
._emitline
(' (bytes into file)')
89 self
._emitline
(' Flags: %s' %
90 self
._format
_hex
(header
['e_flags']))
91 self
._emitline
(' Size of this header: %s (bytes)' %
93 self
._emitline
(' Size of program headers: %s (bytes)' %
94 header
['e_phentsize'])
95 self
._emitline
(' Number of program headers: %s' %
97 self
._emitline
(' Size of section headers: %s (bytes)' %
98 header
['e_shentsize'])
99 self
._emitline
(' Number of section headers: %s' %
101 self
._emitline
(' Section header string table index: %s' %
102 header
['e_shstrndx'])
104 def display_program_headers(self
, show_heading
=True):
105 """ Display the ELF program headers.
106 If show_heading is True, displays the heading for this information
107 (Elf file type is...)
110 if self
.elffile
.num_segments() == 0:
111 self
._emitline
('There are no program headers in this file.')
114 elfheader
= self
.elffile
.header
116 self
._emitline
('Elf file type is %s' %
117 describe_e_type(elfheader
['e_type']))
118 self
._emitline
('Entry point is %s' %
119 self
._format
_hex
(elfheader
['e_entry']))
120 # readelf weirness - why isn't e_phoff printed as hex? (for section
122 self
._emitline
('There are %s program headers, starting at offset %s' % (
123 elfheader
['e_phnum'], elfheader
['e_phoff']))
126 self
._emitline
('Program Headers:')
128 # Now comes the table of program headers with their attributes. Note
129 # that due to different formatting constraints of 32-bit and 64-bit
130 # addresses, there are some conditions on elfclass here.
132 # First comes the table heading
134 if self
.elffile
.elfclass
== 32:
135 self
._emitline
(' Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align')
137 self
._emitline
(' Type Offset VirtAddr PhysAddr')
138 self
._emitline
(' FileSiz MemSiz Flags Align')
142 for segment
in self
.elffile
.iter_segments():
143 self
._emit
(' %-14s ' % describe_p_type(segment
['p_type']))
145 if self
.elffile
.elfclass
== 32:
146 self
._emitline
('%s %s %s %s %s %-3s %s' % (
147 self
._format
_hex
(segment
['p_offset'], fieldsize
=6),
148 self
._format
_hex
(segment
['p_vaddr'], fullhex
=True),
149 self
._format
_hex
(segment
['p_paddr'], fullhex
=True),
150 self
._format
_hex
(segment
['p_filesz'], fieldsize
=5),
151 self
._format
_hex
(segment
['p_memsz'], fieldsize
=5),
152 describe_p_flags(segment
['p_flags']),
153 self
._format
_hex
(segment
['p_align'])))
155 self
._emitline
('%s %s %s' % (
156 self
._format
_hex
(segment
['p_offset'], fullhex
=True),
157 self
._format
_hex
(segment
['p_vaddr'], fullhex
=True),
158 self
._format
_hex
(segment
['p_paddr'], fullhex
=True)))
159 self
._emitline
(' %s %s %-3s %s' % (
160 self
._format
_hex
(segment
['p_filesz'], fullhex
=True),
161 self
._format
_hex
(segment
['p_memsz'], fullhex
=True),
162 describe_p_flags(segment
['p_flags']),
163 # lead0x set to False for p_align, to mimic readelf.
164 # No idea why the difference from 32-bit mode :-|
165 self
._format
_hex
(segment
['p_align'], lead0x
=False)))
167 if isinstance(segment
, InterpSegment
):
168 self
._emitline
(' [Requesting program interpreter: %s]' %
169 segment
.get_interp_name())
171 # Sections to segments mapping
173 if self
.elffile
.num_sections() == 0:
174 # No sections? We're done
177 self
._emitline
('\n Section to Segment mapping:')
178 self
._emitline
(' Segment Sections...')
180 for nseg
, segment
in enumerate(self
.elffile
.iter_segments()):
181 self
._emit
(' %2.2d ' % nseg
)
183 for section
in self
.elffile
.iter_sections():
184 if ( not section
.is_null() and
185 segment
.section_in_segment(section
)):
186 self
._emit
('%s ' % section
.name
)
190 def display_section_headers(self
, show_heading
=True):
191 """ Display the ELF section headers
193 elfheader
= self
.elffile
.header
195 self
._emitline
('There are %s section headers, starting at offset %s' % (
196 elfheader
['e_shnum'], self
._format
_hex
(elfheader
['e_shoff'])))
198 self
._emitline
('\nSection Header%s:' % (
199 's' if elfheader
['e_shnum'] > 1 else ''))
201 # Different formatting constraints of 32-bit and 64-bit addresses
203 if self
.elffile
.elfclass
== 32:
204 self
._emitline
(' [Nr] Name Type Addr Off Size ES Flg Lk Inf Al')
206 self
._emitline
(' [Nr] Name Type Address Offset')
207 self
._emitline
(' Size EntSize Flags Link Info Align')
211 for nsec
, section
in enumerate(self
.elffile
.iter_sections()):
212 self
._emit
(' [%2u] %-17.17s %-15.15s ' % (
213 nsec
, section
.name
, describe_sh_type(section
['sh_type'])))
215 if self
.elffile
.elfclass
== 32:
216 self
._emitline
('%s %s %s %s %3s %2s %3s %2s' % (
217 self
._format
_hex
(section
['sh_addr'], fieldsize
=8, lead0x
=False),
218 self
._format
_hex
(section
['sh_offset'], fieldsize
=6, lead0x
=False),
219 self
._format
_hex
(section
['sh_size'], fieldsize
=6, lead0x
=False),
220 self
._format
_hex
(section
['sh_entsize'], fieldsize
=2, lead0x
=False),
221 describe_sh_flags(section
['sh_flags']),
222 section
['sh_link'], section
['sh_info'],
223 section
['sh_addralign']))
225 self
._emitline
(' %s %s' % (
226 self
._format
_hex
(section
['sh_addr'], fullhex
=True, lead0x
=False),
227 self
._format
_hex
(section
['sh_offset'],
228 fieldsize
=16 if section
['sh_offset'] > 0xffffffff else 8,
230 self
._emitline
(' %s %s %3s %2s %3s %s' % (
231 self
._format
_hex
(section
['sh_size'], fullhex
=True, lead0x
=False),
232 self
._format
_hex
(section
['sh_entsize'], fullhex
=True, lead0x
=False),
233 describe_sh_flags(section
['sh_flags']),
234 section
['sh_link'], section
['sh_info'],
235 section
['sh_addralign']))
237 self
._emitline
('Key to Flags:')
238 self
._emitline
(' W (write), A (alloc), X (execute), M (merge), S (strings)')
239 self
._emitline
(' I (info), L (link order), G (group), x (unknown)')
240 self
._emitline
(' O (extra OS processing required) o (OS specific), p (processor specific)')
242 def display_symbol_tables(self
):
243 """ Display the symbol tables contained in the file
245 for section
in self
.elffile
.iter_sections():
246 if not isinstance(section
, SymbolTableSection
):
249 if section
['sh_entsize'] == 0:
250 self
._emitline
("\nSymbol table '%s' has a sh_entsize of zero!" % (
254 self
._emitline
("\nSymbol table '%s' contains %s entries:" % (
255 section
.name
, section
.num_symbols()))
257 if self
.elffile
.elfclass
== 32:
258 self
._emitline
(' Num: Value Size Type Bind Vis Ndx Name')
260 self
._emitline
(' Num: Value Size Type Bind Vis Ndx Name')
262 for nsym
, symbol
in enumerate(section
.iter_symbols()):
263 # symbol names are truncated to 25 chars, similarly to readelf
264 self
._emitline
('%6d: %s %5d %-7s %-6s %-7s %4s %.25s' % (
266 self
._format
_hex
(symbol
['st_value'], fullhex
=True, lead0x
=False),
268 describe_symbol_type(symbol
['st_info']['type']),
269 describe_symbol_bind(symbol
['st_info']['bind']),
270 describe_symbol_visibility(symbol
['st_other']['visibility']),
271 describe_symbol_shndx(symbol
['st_shndx']),
274 def display_relocations(self
):
275 """ Display the relocations contained in the file
277 has_relocation_sections
= False
278 for section
in self
.elffile
.iter_sections():
279 if not isinstance(section
, RelocationSection
):
282 has_relocation_sections
= True
283 self
._emitline
("\nRelocation section '%s' at offset %s contains %s entries:" % (
285 self
._format
_hex
(section
['sh_offset']),
286 section
.num_relocations()))
287 if section
.is_RELA():
288 self
._emitline
(" Offset Info Type Sym. Value Sym. Name + Addend")
290 self
._emitline
(" Offset Info Type Sym.Value Sym. Name")
292 # The symbol table section pointed to in sh_link
293 symtable
= self
.elffile
.get_section(section
['sh_link'])
295 for rel
in section
.iter_relocations():
296 hexwidth
= 8 if self
.elffile
.elfclass
== 32 else 12
297 self
._emit
('%s %s %-17.17s' % (
298 self
._format
_hex
(rel
['r_offset'],
299 fieldsize
=hexwidth
, lead0x
=False),
300 self
._format
_hex
(rel
['r_info'],
301 fieldsize
=hexwidth
, lead0x
=False),
303 rel
['r_info_type'], self
.elffile
['e_machine'])))
305 if rel
['r_info_sym'] == 0:
309 symbol
= symtable
.get_symbol(rel
['r_info_sym'])
310 # Some symbols have zero 'st_name', so instead what's used is
311 # the name of the section they point at
312 if symbol
['st_name'] == 0:
313 symsec
= self
.elffile
.get_section(symbol
['st_shndx'])
314 symbol_name
= symsec
.name
316 symbol_name
= symbol
.name
317 self
._emit
(' %s %s%22.22s' % (
320 fullhex
=True, lead0x
=False),
321 ' ' if self
.elffile
.elfclass
== 32 else '',
323 if section
.is_RELA():
324 self
._emit
(' %s %x' % (
325 '+' if rel
['r_addend'] >= 0 else '-',
326 abs(rel
['r_addend'])))
329 if not has_relocation_sections
:
330 self
._emitline
('\nThere are no relocations in this file.')
332 def display_hex_dump(self
, section_spec
):
333 """ Display a hex dump of a section. section_spec is either a section
336 section
= self
._section
_from
_spec
(section_spec
)
338 self
._emitline
("Section '%s' does not exist in the file!" % (
342 self
._emitline
("\nHex dump of section '%s':" % section
.name
)
343 self
._note
_relocs
_for
_section
(section
)
344 addr
= section
['sh_addr']
345 data
= section
.data()
348 while dataptr
< len(data
):
349 bytesleft
= len(data
) - dataptr
350 # chunks of 16 bytes per line
351 linebytes
= 16 if bytesleft
> 16 else bytesleft
353 self
._emit
(' %s ' % self
._format
_hex
(addr
, fieldsize
=8))
356 self
._emit
('%2.2x' % ord(data
[dataptr
+ i
]))
362 for i
in range(linebytes
):
363 c
= data
[dataptr
+ i
]
364 if c
>= ' ' and ord(c
) < 0x7f:
375 def display_string_dump(self
, section_spec
):
376 """ Display a strings dump of a section. section_spec is either a
377 section number or a name.
379 section
= self
._section
_from
_spec
(section_spec
)
381 self
._emitline
("Section '%s' does not exist in the file!" % (
385 printables
= set(string
.printable
)
386 self
._emitline
("\nString dump of section '%s':" % section
.name
)
389 data
= section
.data()
392 while dataptr
< len(data
):
393 while dataptr
< len(data
) and data
[dataptr
] not in printables
:
396 if dataptr
>= len(data
):
400 while endptr
< len(data
) and data
[endptr
] != '\x00':
404 self
._emitline
(' [%6x] %s' % (
405 dataptr
, data
[dataptr
:endptr
]))
410 self
._emitline
(' No strings found in this section.')
414 def display_debug_dump(self
, section_name
):
415 """ Dump a DWARF section
417 self
._init
_dwarfinfo
()
418 if self
._dwarfinfo
is None:
421 if section_name
== 'info':
422 self
._dump
_debug
_info
()
424 self
._emitline
('debug dump not yet supported for "%s"' % section_name
)
426 def _format_hex(self
, addr
, fieldsize
=None, fullhex
=False, lead0x
=True):
427 """ Format an address into a hexadecimal string.
430 Size of the hexadecimal field (with leading zeros to fit the
431 address into. For example with fieldsize=8, the format will
433 If None, the minimal required field size will be used.
436 If True, override fieldsize to set it to the maximal size
437 needed for the elfclass
440 If True, leading 0x is added
442 s
= '0x' if lead0x
else ''
444 fieldsize
= 8 if self
.elffile
.elfclass
== 32 else 16
445 if fieldsize
is None:
448 field
= '%' + '0%sx' % fieldsize
449 return s
+ field
% addr
451 def _section_from_spec(self
, spec
):
452 """ Retrieve a section given a "spec" (either number or name).
453 Return None if no such section exists in the file.
457 if num
< self
.elffile
.num_sections():
458 return self
.elffile
.get_section(num
)
462 # Not a number. Must be a name then
463 return self
.elffile
.get_section_by_name(spec
)
465 def _note_relocs_for_section(self
, section
):
466 """ If there are relocation sections pointing to the givne section,
467 emit a note about it.
469 for relsec
in self
.elffile
.iter_sections():
470 if isinstance(relsec
, RelocationSection
):
471 info_idx
= relsec
['sh_info']
472 if self
.elffile
.get_section(info_idx
) == section
:
473 self
._emitline
(' Note: This section has relocations against it, but these have NOT been applied to this dump.')
476 def _init_dwarfinfo(self
):
477 """ Initialize the DWARF info contained in the file and assign it to
479 Leave self._dwarfinfo at None if no DWARF info was found in the file
481 if self
._dwarfinfo
is not None:
484 if self
.elffile
.has_dwarf_info():
485 self
._dwarfinfo
= self
.elffile
.get_dwarf_info()
487 self
._dwarfinfo
= None
489 def _dump_debug_info(self
):
490 """ Dump the debugging info section.
492 self
._emitline
('Contents of the .debug_info section:\n')
494 # Offset of the .debug_info section in the stream
495 section_offset
= self
._dwarfinfo
.debug_info_loc
.offset
497 for cu
in self
._dwarfinfo
.iter_CUs():
498 self
._emitline
(' Compilation Unit @ offset %s:' %
499 self
._format
_hex
(cu
.cu_offset
- section_offset
))
500 self
._emitline
(' Length: %s (%s)' % (
501 self
._format
_hex
(cu
['unit_length']),
502 '%s-bit' % cu
.dwarf_format()))
503 self
._emitline
(' Version: %s' % cu
['version']),
504 self
._emitline
(' Abbrev Offset: %s' % cu
['debug_abbrev_offset']),
505 self
._emitline
(' Pointer Size: %s' % cu
['address_size'])
507 # The nesting depth of each DIE within the tree of DIEs must be
508 # displayed. To implement this, a counter is incremented each time
509 # the current DIE has children, and decremented when a null die is
510 # encountered. Due to the way the DIE tree is serialized, this will
511 # correctly reflect the nesting depth
514 for die
in cu
.iter_DIEs():
518 self
._emitline
(' <%s><%x>: Abbrev Number: %s (%s)' % (
520 die
.offset
- section_offset
,
524 for attr
in die
.attributes
.itervalues():
525 self
._emitline
(' <%2x> %-18s: %s' % (
526 attr
.offset
- section_offset
,
529 attr
, die
, section_offset
)))
535 def _emit(self
, s
=''):
536 """ Emit an object to output
538 self
.output
.write(str(s
))
540 def _emitline(self
, s
=''):
541 """ Emit an object to output, followed by a newline
543 self
.output
.write(str(s
) + '\n')
546 SCRIPT_DESCRIPTION
= 'Display information about the contents of ELF format files'
547 VERSION_STRING
= '%%prog: based on pyelftools %s' % __version__
551 # parse the command-line arguments and invoke ReadElf
552 optparser
= OptionParser(
553 usage
='usage: %prog [options] <elf-file>',
554 description
=SCRIPT_DESCRIPTION
,
555 add_help_option
=False, # -h is a real option of readelf
557 version
=VERSION_STRING
)
558 optparser
.add_option('-H', '--help',
559 action
='store_true', dest
='help',
560 help='Display this information')
561 optparser
.add_option('-h', '--file-header',
562 action
='store_true', dest
='show_file_header',
563 help='Display the ELF file header')
564 optparser
.add_option('-l', '--program-headers', '--segments',
565 action
='store_true', dest
='show_program_header',
566 help='Display the program headers')
567 optparser
.add_option('-S', '--section-headers', '--sections',
568 action
='store_true', dest
='show_section_header',
569 help="Display the sections' headers")
570 optparser
.add_option('-e', '--headers',
571 action
='store_true', dest
='show_all_headers',
572 help='Equivalent to: -h -l -S')
573 optparser
.add_option('-s', '--symbols', '--syms',
574 action
='store_true', dest
='show_symbols',
575 help='Display the symbol table')
576 optparser
.add_option('-r', '--relocs',
577 action
='store_true', dest
='show_relocs',
578 help='Display the relocations (if present)')
579 optparser
.add_option('-x', '--hex-dump',
580 action
='store', dest
='show_hex_dump', metavar
='<number|name>',
581 help='Dump the contents of section <number|name> as bytes')
582 optparser
.add_option('-p', '--string-dump',
583 action
='store', dest
='show_string_dump', metavar
='<number|name>',
584 help='Dump the contents of section <number|name> as strings')
585 optparser
.add_option('--debug-dump',
586 action
='store', dest
='debug_dump_section', metavar
='<section>',
587 help='Display the contents of DWARF debug sections')
589 options
, args
= optparser
.parse_args()
591 if options
.help or len(args
) == 0:
592 optparser
.print_help()
595 if options
.show_all_headers
:
596 do_file_header
= do_section_header
= do_program_header
= True
598 do_file_header
= options
.show_file_header
599 do_section_header
= options
.show_section_header
600 do_program_header
= options
.show_program_header
602 with
open(args
[0], 'rb') as file:
604 readelf
= ReadElf(file, sys
.stdout
)
606 readelf
.display_file_header()
607 if do_section_header
:
608 readelf
.display_section_headers(
609 show_heading
=not do_file_header
)
610 if do_program_header
:
611 readelf
.display_program_headers(
612 show_heading
=not do_file_header
)
613 if options
.show_symbols
:
614 readelf
.display_symbol_tables()
615 if options
.show_relocs
:
616 readelf
.display_relocations()
617 if options
.show_hex_dump
:
618 readelf
.display_hex_dump(options
.show_hex_dump
)
619 if options
.show_string_dump
:
620 readelf
.display_string_dump(options
.show_string_dump
)
621 if options
.debug_dump_section
:
622 readelf
.display_debug_dump(options
.debug_dump_section
)
623 except ELFError
as ex
:
624 sys
.stderr
.write('ELF error: %s\n' % ex
)
628 #-------------------------------------------------------------------------------
629 if __name__
== '__main__':