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 # Offset of the .debug_info section in the stream
493 section_offset
= self
._dwarfinfo
.debug_info_loc
.offset
495 for cu
in self
._dwarfinfo
.iter_CUs():
496 self
._emitline
(' Compilation Unit @ offset %s' %
497 self
._format
_hex
(cu
.cu_offset
- section_offset
))
498 self
._emitline
(' Length: %s (%s)' % (
499 self
._format
_hex
(cu
['unit_length']),
500 '%s-bit' % cu
.dwarf_format()))
501 self
._emitline
(' Version: %s' % cu
['version']),
502 self
._emitline
(' Abbrev Offset: %s' % cu
['debug_abbrev_offset']),
503 self
._emitline
(' Pointer Size: %s' % cu
['address_size'])
505 # The nesting depth of each DIE within the tree of DIEs must be
506 # displayed. To implement this, a counter is incremented each time
507 # the current DIE has children, and decremented when a null die is
508 # encountered. Due to the way the DIE tree is serialized, this will
509 # correctly reflect the nesting depth
512 for die
in cu
.iter_DIEs():
516 self
._emitline
(' <%s><%x>: Abbrev Number: %s (%s)' % (
518 die
.offset
- section_offset
,
522 for attrname
, attr
in die
.attributes
.iteritems():
523 self
._emitline
(' <%2x> %-18s: %s' % (
524 attr
.offset
- section_offset
,
527 attrname
, attr
, die
, section_offset
)))
533 def _emit(self
, s
=''):
534 """ Emit an object to output
536 self
.output
.write(str(s
))
538 def _emitline(self
, s
=''):
539 """ Emit an object to output, followed by a newline
541 self
.output
.write(str(s
) + '\n')
544 SCRIPT_DESCRIPTION
= 'Display information about the contents of ELF format files'
545 VERSION_STRING
= '%%prog: based on pyelftools %s' % __version__
549 # parse the command-line arguments and invoke ReadElf
550 optparser
= OptionParser(
551 usage
='usage: %prog [options] <elf-file>',
552 description
=SCRIPT_DESCRIPTION
,
553 add_help_option
=False, # -h is a real option of readelf
555 version
=VERSION_STRING
)
556 optparser
.add_option('-H', '--help',
557 action
='store_true', dest
='help',
558 help='Display this information')
559 optparser
.add_option('-h', '--file-header',
560 action
='store_true', dest
='show_file_header',
561 help='Display the ELF file header')
562 optparser
.add_option('-l', '--program-headers', '--segments',
563 action
='store_true', dest
='show_program_header',
564 help='Display the program headers')
565 optparser
.add_option('-S', '--section-headers', '--sections',
566 action
='store_true', dest
='show_section_header',
567 help="Display the sections' headers")
568 optparser
.add_option('-e', '--headers',
569 action
='store_true', dest
='show_all_headers',
570 help='Equivalent to: -h -l -S')
571 optparser
.add_option('-s', '--symbols', '--syms',
572 action
='store_true', dest
='show_symbols',
573 help='Display the symbol table')
574 optparser
.add_option('-r', '--relocs',
575 action
='store_true', dest
='show_relocs',
576 help='Display the relocations (if present)')
577 optparser
.add_option('-x', '--hex-dump',
578 action
='store', dest
='show_hex_dump', metavar
='<number|name>',
579 help='Dump the contents of section <number|name> as bytes')
580 optparser
.add_option('-p', '--string-dump',
581 action
='store', dest
='show_string_dump', metavar
='<number|name>',
582 help='Dump the contents of section <number|name> as strings')
583 optparser
.add_option('--debug-dump',
584 action
='store', dest
='debug_dump_section', metavar
='<section>',
585 help='Display the contents of DWARF debug sections')
587 options
, args
= optparser
.parse_args()
589 if options
.help or len(args
) == 0:
590 optparser
.print_help()
593 if options
.show_all_headers
:
594 do_file_header
= do_section_header
= do_program_header
= True
596 do_file_header
= options
.show_file_header
597 do_section_header
= options
.show_section_header
598 do_program_header
= options
.show_program_header
600 with
open(args
[0], 'rb') as file:
602 readelf
= ReadElf(file, sys
.stdout
)
604 readelf
.display_file_header()
605 if do_section_header
:
606 readelf
.display_section_headers(
607 show_heading
=not do_file_header
)
608 if do_program_header
:
609 readelf
.display_program_headers(
610 show_heading
=not do_file_header
)
611 if options
.show_symbols
:
612 readelf
.display_symbol_tables()
613 if options
.show_relocs
:
614 readelf
.display_relocations()
615 if options
.show_hex_dump
:
616 readelf
.display_hex_dump(options
.show_hex_dump
)
617 if options
.show_string_dump
:
618 readelf
.display_string_dump(options
.show_string_dump
)
619 if options
.debug_dump_section
:
620 readelf
.display_debug_dump(options
.debug_dump_section
)
621 except ELFError
as ex
:
622 sys
.stderr
.write('ELF error: %s\n' % ex
)
626 #-------------------------------------------------------------------------------
627 if __name__
== '__main__':