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
._emit
(' W (write), A (alloc), X (execute), M (merge), S (strings)')
239 if self
.elffile
['e_machine'] in ('EM_X86_64', 'EM_L10M'):
240 self
._emitline
(', l (large)')
243 self
._emitline
(' I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)')
244 self
._emitline
(' O (extra OS processing required) o (OS specific), p (processor specific)')
246 def display_symbol_tables(self
):
247 """ Display the symbol tables contained in the file
249 for section
in self
.elffile
.iter_sections():
250 if not isinstance(section
, SymbolTableSection
):
253 if section
['sh_entsize'] == 0:
254 self
._emitline
("\nSymbol table '%s' has a sh_entsize of zero!" % (
258 self
._emitline
("\nSymbol table '%s' contains %s entries:" % (
259 section
.name
, section
.num_symbols()))
261 if self
.elffile
.elfclass
== 32:
262 self
._emitline
(' Num: Value Size Type Bind Vis Ndx Name')
264 self
._emitline
(' Num: Value Size Type Bind Vis Ndx Name')
266 for nsym
, symbol
in enumerate(section
.iter_symbols()):
267 # symbol names are truncated to 25 chars, similarly to readelf
268 self
._emitline
('%6d: %s %5d %-7s %-6s %-7s %4s %.25s' % (
270 self
._format
_hex
(symbol
['st_value'], fullhex
=True, lead0x
=False),
272 describe_symbol_type(symbol
['st_info']['type']),
273 describe_symbol_bind(symbol
['st_info']['bind']),
274 describe_symbol_visibility(symbol
['st_other']['visibility']),
275 describe_symbol_shndx(symbol
['st_shndx']),
278 def display_relocations(self
):
279 """ Display the relocations contained in the file
281 has_relocation_sections
= False
282 for section
in self
.elffile
.iter_sections():
283 if not isinstance(section
, RelocationSection
):
286 has_relocation_sections
= True
287 self
._emitline
("\nRelocation section '%s' at offset %s contains %s entries:" % (
289 self
._format
_hex
(section
['sh_offset']),
290 section
.num_relocations()))
291 if section
.is_RELA():
292 self
._emitline
(" Offset Info Type Sym. Value Sym. Name + Addend")
294 self
._emitline
(" Offset Info Type Sym.Value Sym. Name")
296 # The symbol table section pointed to in sh_link
297 symtable
= self
.elffile
.get_section(section
['sh_link'])
299 for rel
in section
.iter_relocations():
300 hexwidth
= 8 if self
.elffile
.elfclass
== 32 else 12
301 self
._emit
('%s %s %-17.17s' % (
302 self
._format
_hex
(rel
['r_offset'],
303 fieldsize
=hexwidth
, lead0x
=False),
304 self
._format
_hex
(rel
['r_info'],
305 fieldsize
=hexwidth
, lead0x
=False),
307 rel
['r_info_type'], self
.elffile
['e_machine'])))
309 if rel
['r_info_sym'] == 0:
313 symbol
= symtable
.get_symbol(rel
['r_info_sym'])
314 # Some symbols have zero 'st_name', so instead what's used is
315 # the name of the section they point at
316 if symbol
['st_name'] == 0:
317 symsec
= self
.elffile
.get_section(symbol
['st_shndx'])
318 symbol_name
= symsec
.name
320 symbol_name
= symbol
.name
321 self
._emit
(' %s %s%22.22s' % (
324 fullhex
=True, lead0x
=False),
325 ' ' if self
.elffile
.elfclass
== 32 else '',
327 if section
.is_RELA():
328 self
._emit
(' %s %x' % (
329 '+' if rel
['r_addend'] >= 0 else '-',
330 abs(rel
['r_addend'])))
333 if not has_relocation_sections
:
334 self
._emitline
('\nThere are no relocations in this file.')
336 def display_hex_dump(self
, section_spec
):
337 """ Display a hex dump of a section. section_spec is either a section
340 section
= self
._section
_from
_spec
(section_spec
)
342 self
._emitline
("Section '%s' does not exist in the file!" % (
346 self
._emitline
("\nHex dump of section '%s':" % section
.name
)
347 self
._note
_relocs
_for
_section
(section
)
348 addr
= section
['sh_addr']
349 data
= section
.data()
352 while dataptr
< len(data
):
353 bytesleft
= len(data
) - dataptr
354 # chunks of 16 bytes per line
355 linebytes
= 16 if bytesleft
> 16 else bytesleft
357 self
._emit
(' %s ' % self
._format
_hex
(addr
, fieldsize
=8))
360 self
._emit
('%2.2x' % ord(data
[dataptr
+ i
]))
366 for i
in range(linebytes
):
367 c
= data
[dataptr
+ i
]
368 if c
>= ' ' and ord(c
) < 0x7f:
379 def display_string_dump(self
, section_spec
):
380 """ Display a strings dump of a section. section_spec is either a
381 section number or a name.
383 section
= self
._section
_from
_spec
(section_spec
)
385 self
._emitline
("Section '%s' does not exist in the file!" % (
389 printables
= set(string
.printable
)
390 self
._emitline
("\nString dump of section '%s':" % section
.name
)
393 data
= section
.data()
396 while dataptr
< len(data
):
397 while dataptr
< len(data
) and data
[dataptr
] not in printables
:
400 if dataptr
>= len(data
):
404 while endptr
< len(data
) and data
[endptr
] != '\x00':
408 self
._emitline
(' [%6x] %s' % (
409 dataptr
, data
[dataptr
:endptr
]))
414 self
._emitline
(' No strings found in this section.')
418 def display_debug_dump(self
, section_name
):
419 """ Dump a DWARF section
421 self
._init
_dwarfinfo
()
422 if self
._dwarfinfo
is None:
425 if section_name
== 'info':
426 self
._dump
_debug
_info
()
428 self
._emitline
('debug dump not yet supported for "%s"' % section_name
)
430 def _format_hex(self
, addr
, fieldsize
=None, fullhex
=False, lead0x
=True):
431 """ Format an address into a hexadecimal string.
434 Size of the hexadecimal field (with leading zeros to fit the
435 address into. For example with fieldsize=8, the format will
437 If None, the minimal required field size will be used.
440 If True, override fieldsize to set it to the maximal size
441 needed for the elfclass
444 If True, leading 0x is added
446 s
= '0x' if lead0x
else ''
448 fieldsize
= 8 if self
.elffile
.elfclass
== 32 else 16
449 if fieldsize
is None:
452 field
= '%' + '0%sx' % fieldsize
453 return s
+ field
% addr
455 def _section_from_spec(self
, spec
):
456 """ Retrieve a section given a "spec" (either number or name).
457 Return None if no such section exists in the file.
461 if num
< self
.elffile
.num_sections():
462 return self
.elffile
.get_section(num
)
466 # Not a number. Must be a name then
467 return self
.elffile
.get_section_by_name(spec
)
469 def _note_relocs_for_section(self
, section
):
470 """ If there are relocation sections pointing to the givne section,
471 emit a note about it.
473 for relsec
in self
.elffile
.iter_sections():
474 if isinstance(relsec
, RelocationSection
):
475 info_idx
= relsec
['sh_info']
476 if self
.elffile
.get_section(info_idx
) == section
:
477 self
._emitline
(' Note: This section has relocations against it, but these have NOT been applied to this dump.')
480 def _init_dwarfinfo(self
):
481 """ Initialize the DWARF info contained in the file and assign it to
483 Leave self._dwarfinfo at None if no DWARF info was found in the file
485 if self
._dwarfinfo
is not None:
488 if self
.elffile
.has_dwarf_info():
489 self
._dwarfinfo
= self
.elffile
.get_dwarf_info()
491 self
._dwarfinfo
= None
493 def _dump_debug_info(self
):
494 """ Dump the debugging info section.
496 self
._emitline
('Contents of the .debug_info section:\n')
498 # Offset of the .debug_info section in the stream
499 section_offset
= self
._dwarfinfo
.debug_info_loc
.offset
501 for cu
in self
._dwarfinfo
.iter_CUs():
502 self
._emitline
(' Compilation Unit @ offset %s:' %
503 self
._format
_hex
(cu
.cu_offset
- section_offset
))
504 self
._emitline
(' Length: %s (%s)' % (
505 self
._format
_hex
(cu
['unit_length']),
506 '%s-bit' % cu
.dwarf_format()))
507 self
._emitline
(' Version: %s' % cu
['version']),
508 self
._emitline
(' Abbrev Offset: %s' % cu
['debug_abbrev_offset']),
509 self
._emitline
(' Pointer Size: %s' % cu
['address_size'])
511 # The nesting depth of each DIE within the tree of DIEs must be
512 # displayed. To implement this, a counter is incremented each time
513 # the current DIE has children, and decremented when a null die is
514 # encountered. Due to the way the DIE tree is serialized, this will
515 # correctly reflect the nesting depth
518 for die
in cu
.iter_DIEs():
522 self
._emitline
(' <%s><%x>: Abbrev Number: %s (%s)' % (
524 die
.offset
- section_offset
,
528 for attr
in die
.attributes
.itervalues():
529 self
._emitline
(' <%2x> %-18s: %s' % (
530 attr
.offset
- section_offset
,
533 attr
, die
, section_offset
)))
539 def _emit(self
, s
=''):
540 """ Emit an object to output
542 self
.output
.write(str(s
))
544 def _emitline(self
, s
=''):
545 """ Emit an object to output, followed by a newline
547 self
.output
.write(str(s
) + '\n')
550 SCRIPT_DESCRIPTION
= 'Display information about the contents of ELF format files'
551 VERSION_STRING
= '%%prog: based on pyelftools %s' % __version__
555 # parse the command-line arguments and invoke ReadElf
556 optparser
= OptionParser(
557 usage
='usage: %prog [options] <elf-file>',
558 description
=SCRIPT_DESCRIPTION
,
559 add_help_option
=False, # -h is a real option of readelf
561 version
=VERSION_STRING
)
562 optparser
.add_option('-H', '--help',
563 action
='store_true', dest
='help',
564 help='Display this information')
565 optparser
.add_option('-h', '--file-header',
566 action
='store_true', dest
='show_file_header',
567 help='Display the ELF file header')
568 optparser
.add_option('-l', '--program-headers', '--segments',
569 action
='store_true', dest
='show_program_header',
570 help='Display the program headers')
571 optparser
.add_option('-S', '--section-headers', '--sections',
572 action
='store_true', dest
='show_section_header',
573 help="Display the sections' headers")
574 optparser
.add_option('-e', '--headers',
575 action
='store_true', dest
='show_all_headers',
576 help='Equivalent to: -h -l -S')
577 optparser
.add_option('-s', '--symbols', '--syms',
578 action
='store_true', dest
='show_symbols',
579 help='Display the symbol table')
580 optparser
.add_option('-r', '--relocs',
581 action
='store_true', dest
='show_relocs',
582 help='Display the relocations (if present)')
583 optparser
.add_option('-x', '--hex-dump',
584 action
='store', dest
='show_hex_dump', metavar
='<number|name>',
585 help='Dump the contents of section <number|name> as bytes')
586 optparser
.add_option('-p', '--string-dump',
587 action
='store', dest
='show_string_dump', metavar
='<number|name>',
588 help='Dump the contents of section <number|name> as strings')
589 optparser
.add_option('--debug-dump',
590 action
='store', dest
='debug_dump_section', metavar
='<section>',
591 help='Display the contents of DWARF debug sections')
593 options
, args
= optparser
.parse_args()
595 if options
.help or len(args
) == 0:
596 optparser
.print_help()
599 if options
.show_all_headers
:
600 do_file_header
= do_section_header
= do_program_header
= True
602 do_file_header
= options
.show_file_header
603 do_section_header
= options
.show_section_header
604 do_program_header
= options
.show_program_header
606 with
open(args
[0], 'rb') as file:
608 readelf
= ReadElf(file, sys
.stdout
)
610 readelf
.display_file_header()
611 if do_section_header
:
612 readelf
.display_section_headers(
613 show_heading
=not do_file_header
)
614 if do_program_header
:
615 readelf
.display_program_headers(
616 show_heading
=not do_file_header
)
617 if options
.show_symbols
:
618 readelf
.display_symbol_tables()
619 if options
.show_relocs
:
620 readelf
.display_relocations()
621 if options
.show_hex_dump
:
622 readelf
.display_hex_dump(options
.show_hex_dump
)
623 if options
.show_string_dump
:
624 readelf
.display_string_dump(options
.show_string_dump
)
625 if options
.debug_dump_section
:
626 readelf
.display_debug_dump(options
.debug_dump_section
)
627 except ELFError
as ex
:
628 sys
.stderr
.write('ELF error: %s\n' % ex
)
632 #-------------------------------------------------------------------------------
633 if __name__
== '__main__':