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
14 # For running from development directory. It should take precedence over the
15 # installed pyelftools.
16 sys
.path
.insert(0, '.')
19 from elftools
import __version__
20 from elftools
.common
.exceptions
import ELFError
21 from elftools
.common
.py3compat
import (
22 ifilter
, byte2int
, bytes2str
, itervalues
, str2bytes
)
23 from elftools
.elf
.elffile
import ELFFile
24 from elftools
.elf
.dynamic
import DynamicSection
, DynamicSegment
25 from elftools
.elf
.enums
import ENUM_D_TAG
26 from elftools
.elf
.segments
import InterpSegment
27 from elftools
.elf
.sections
import (
28 SymbolTableSection
, GNUVerSymTableSection
,
29 GNUVerDefTableSection
, GNUVerNeedTableSection
,
31 from elftools
.elf
.relocation
import RelocationSection
32 from elftools
.elf
.descriptions
import (
33 describe_ei_class
, describe_ei_data
, describe_ei_version
,
34 describe_ei_osabi
, describe_e_type
, describe_e_machine
,
35 describe_e_version_numeric
, describe_p_type
, describe_p_flags
,
36 describe_sh_type
, describe_sh_flags
,
37 describe_symbol_type
, describe_symbol_bind
, describe_symbol_visibility
,
38 describe_symbol_shndx
, describe_reloc_type
, describe_dyn_tag
,
41 from elftools
.dwarf
.dwarfinfo
import DWARFInfo
42 from elftools
.dwarf
.descriptions
import (
43 describe_reg_name
, describe_attr_value
, set_global_machine_arch
,
44 describe_CFI_instructions
, describe_CFI_register_rule
,
45 describe_CFI_CFA_rule
,
47 from elftools
.dwarf
.constants
import (
48 DW_LNS_copy
, DW_LNS_set_file
, DW_LNE_define_file
)
49 from elftools
.dwarf
.callframe
import CIE
, FDE
52 class ReadElf(object):
53 """ display_* methods are used to emit output into the output stream
55 def __init__(self
, file, output
):
57 stream object with the ELF file to read
60 output stream to write to
62 self
.elffile
= ELFFile(file)
65 # Lazily initialized if a debug dump is requested
66 self
._dwarfinfo
= None
68 self
._versioninfo
= None
71 def display_file_header(self
):
72 """ Display the ELF file header
74 self
._emitline
('ELF Header:')
75 self
._emit
(' Magic: ')
76 self
._emitline
(' '.join('%2.2x' % byte2int(b
)
77 for b
in self
.elffile
.e_ident_raw
))
78 header
= self
.elffile
.header
79 e_ident
= header
['e_ident']
80 self
._emitline
(' Class: %s' %
81 describe_ei_class(e_ident
['EI_CLASS']))
82 self
._emitline
(' Data: %s' %
83 describe_ei_data(e_ident
['EI_DATA']))
84 self
._emitline
(' Version: %s' %
85 describe_ei_version(e_ident
['EI_VERSION']))
86 self
._emitline
(' OS/ABI: %s' %
87 describe_ei_osabi(e_ident
['EI_OSABI']))
88 self
._emitline
(' ABI Version: %d' %
89 e_ident
['EI_ABIVERSION'])
90 self
._emitline
(' Type: %s' %
91 describe_e_type(header
['e_type']))
92 self
._emitline
(' Machine: %s' %
93 describe_e_machine(header
['e_machine']))
94 self
._emitline
(' Version: %s' %
95 describe_e_version_numeric(header
['e_version']))
96 self
._emitline
(' Entry point address: %s' %
97 self
._format
_hex
(header
['e_entry']))
98 self
._emit
(' Start of program headers: %s' %
100 self
._emitline
(' (bytes into file)')
101 self
._emit
(' Start of section headers: %s' %
103 self
._emitline
(' (bytes into file)')
104 self
._emitline
(' Flags: %s' %
105 self
._format
_hex
(header
['e_flags']))
106 self
._emitline
(' Size of this header: %s (bytes)' %
108 self
._emitline
(' Size of program headers: %s (bytes)' %
109 header
['e_phentsize'])
110 self
._emitline
(' Number of program headers: %s' %
112 self
._emitline
(' Size of section headers: %s (bytes)' %
113 header
['e_shentsize'])
114 self
._emitline
(' Number of section headers: %s' %
116 self
._emitline
(' Section header string table index: %s' %
117 header
['e_shstrndx'])
119 def display_program_headers(self
, show_heading
=True):
120 """ Display the ELF program headers.
121 If show_heading is True, displays the heading for this information
122 (Elf file type is...)
125 if self
.elffile
.num_segments() == 0:
126 self
._emitline
('There are no program headers in this file.')
129 elfheader
= self
.elffile
.header
131 self
._emitline
('Elf file type is %s' %
132 describe_e_type(elfheader
['e_type']))
133 self
._emitline
('Entry point is %s' %
134 self
._format
_hex
(elfheader
['e_entry']))
135 # readelf weirness - why isn't e_phoff printed as hex? (for section
137 self
._emitline
('There are %s program headers, starting at offset %s' % (
138 elfheader
['e_phnum'], elfheader
['e_phoff']))
141 self
._emitline
('Program Headers:')
143 # Now comes the table of program headers with their attributes. Note
144 # that due to different formatting constraints of 32-bit and 64-bit
145 # addresses, there are some conditions on elfclass here.
147 # First comes the table heading
149 if self
.elffile
.elfclass
== 32:
150 self
._emitline
(' Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align')
152 self
._emitline
(' Type Offset VirtAddr PhysAddr')
153 self
._emitline
(' FileSiz MemSiz Flags Align')
157 for segment
in self
.elffile
.iter_segments():
158 self
._emit
(' %-14s ' % describe_p_type(segment
['p_type']))
160 if self
.elffile
.elfclass
== 32:
161 self
._emitline
('%s %s %s %s %s %-3s %s' % (
162 self
._format
_hex
(segment
['p_offset'], fieldsize
=6),
163 self
._format
_hex
(segment
['p_vaddr'], fullhex
=True),
164 self
._format
_hex
(segment
['p_paddr'], fullhex
=True),
165 self
._format
_hex
(segment
['p_filesz'], fieldsize
=5),
166 self
._format
_hex
(segment
['p_memsz'], fieldsize
=5),
167 describe_p_flags(segment
['p_flags']),
168 self
._format
_hex
(segment
['p_align'])))
170 self
._emitline
('%s %s %s' % (
171 self
._format
_hex
(segment
['p_offset'], fullhex
=True),
172 self
._format
_hex
(segment
['p_vaddr'], fullhex
=True),
173 self
._format
_hex
(segment
['p_paddr'], fullhex
=True)))
174 self
._emitline
(' %s %s %-3s %s' % (
175 self
._format
_hex
(segment
['p_filesz'], fullhex
=True),
176 self
._format
_hex
(segment
['p_memsz'], fullhex
=True),
177 describe_p_flags(segment
['p_flags']),
178 # lead0x set to False for p_align, to mimic readelf.
179 # No idea why the difference from 32-bit mode :-|
180 self
._format
_hex
(segment
['p_align'], lead0x
=False)))
182 if isinstance(segment
, InterpSegment
):
183 self
._emitline
(' [Requesting program interpreter: %s]' %
184 bytes2str(segment
.get_interp_name()))
186 # Sections to segments mapping
188 if self
.elffile
.num_sections() == 0:
189 # No sections? We're done
192 self
._emitline
('\n Section to Segment mapping:')
193 self
._emitline
(' Segment Sections...')
195 for nseg
, segment
in enumerate(self
.elffile
.iter_segments()):
196 self
._emit
(' %2.2d ' % nseg
)
198 for section
in self
.elffile
.iter_sections():
199 if ( not section
.is_null() and
200 segment
.section_in_segment(section
)):
201 self
._emit
('%s ' % bytes2str(section
.name
))
205 def display_section_headers(self
, show_heading
=True):
206 """ Display the ELF section headers
208 elfheader
= self
.elffile
.header
210 self
._emitline
('There are %s section headers, starting at offset %s' % (
211 elfheader
['e_shnum'], self
._format
_hex
(elfheader
['e_shoff'])))
213 self
._emitline
('\nSection Header%s:' % (
214 's' if elfheader
['e_shnum'] > 1 else ''))
216 # Different formatting constraints of 32-bit and 64-bit addresses
218 if self
.elffile
.elfclass
== 32:
219 self
._emitline
(' [Nr] Name Type Addr Off Size ES Flg Lk Inf Al')
221 self
._emitline
(' [Nr] Name Type Address Offset')
222 self
._emitline
(' Size EntSize Flags Link Info Align')
226 for nsec
, section
in enumerate(self
.elffile
.iter_sections()):
227 self
._emit
(' [%2u] %-17.17s %-15.15s ' % (
228 nsec
, bytes2str(section
.name
), describe_sh_type(section
['sh_type'])))
230 if self
.elffile
.elfclass
== 32:
231 self
._emitline
('%s %s %s %s %3s %2s %3s %2s' % (
232 self
._format
_hex
(section
['sh_addr'], fieldsize
=8, lead0x
=False),
233 self
._format
_hex
(section
['sh_offset'], fieldsize
=6, lead0x
=False),
234 self
._format
_hex
(section
['sh_size'], fieldsize
=6, lead0x
=False),
235 self
._format
_hex
(section
['sh_entsize'], fieldsize
=2, lead0x
=False),
236 describe_sh_flags(section
['sh_flags']),
237 section
['sh_link'], section
['sh_info'],
238 section
['sh_addralign']))
240 self
._emitline
(' %s %s' % (
241 self
._format
_hex
(section
['sh_addr'], fullhex
=True, lead0x
=False),
242 self
._format
_hex
(section
['sh_offset'],
243 fieldsize
=16 if section
['sh_offset'] > 0xffffffff else 8,
245 self
._emitline
(' %s %s %3s %2s %3s %s' % (
246 self
._format
_hex
(section
['sh_size'], fullhex
=True, lead0x
=False),
247 self
._format
_hex
(section
['sh_entsize'], fullhex
=True, lead0x
=False),
248 describe_sh_flags(section
['sh_flags']),
249 section
['sh_link'], section
['sh_info'],
250 section
['sh_addralign']))
252 self
._emitline
('Key to Flags:')
253 self
._emit
(' W (write), A (alloc), X (execute), M (merge), S (strings)')
254 if self
.elffile
['e_machine'] in ('EM_X86_64', 'EM_L10M'):
255 self
._emitline
(', l (large)')
258 self
._emitline
(' I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)')
259 self
._emitline
(' O (extra OS processing required) o (OS specific), p (processor specific)')
261 def display_symbol_tables(self
):
262 """ Display the symbol tables contained in the file
264 self
._init
_versioninfo
()
266 for section
in self
.elffile
.iter_sections():
267 if not isinstance(section
, SymbolTableSection
):
270 if section
['sh_entsize'] == 0:
271 self
._emitline
("\nSymbol table '%s' has a sh_entsize of zero!" % (
272 bytes2str(section
.name
)))
276 self
._emitline
("\nSymbol table '%s' contains %s entries:" % (
277 bytes2str(section
.name
), section
.num_symbols()))
279 if self
.elffile
.elfclass
== 32:
280 self
._emitline
(' Num: Value Size Type Bind Vis Ndx Name')
282 self
._emitline
(' Num: Value Size Type Bind Vis Ndx Name')
284 for nsym
, symbol
in enumerate(section
.iter_symbols()):
287 # readelf doesn't display version info for Solaris versioning
288 if (section
['sh_type'] == 'SHT_DYNSYM' and
289 self
._versioninfo
['type'] == 'GNU'):
290 version
= self
._symbol
_version
(nsym
)
291 if (version
['name'] != bytes2str(symbol
.name
) and
292 version
['index'] not in ('VER_NDX_LOCAL',
294 if version
['filename']:
296 version_info
= '@%(name)s (%(index)i)' % version
299 if version
['hidden']:
300 version_info
= '@%(name)s' % version
302 version_info
= '@@%(name)s' % version
304 # symbol names are truncated to 25 chars, similarly to readelf
305 self
._emitline
('%6d: %s %5d %-7s %-6s %-7s %4s %.25s%s' % (
307 self
._format
_hex
(symbol
['st_value'], fullhex
=True, lead0x
=False),
309 describe_symbol_type(symbol
['st_info']['type']),
310 describe_symbol_bind(symbol
['st_info']['bind']),
311 describe_symbol_visibility(symbol
['st_other']['visibility']),
312 describe_symbol_shndx(symbol
['st_shndx']),
313 bytes2str(symbol
.name
),
316 def display_dynamic_tags(self
):
317 """ Display the dynamic tags contained in the file
319 for section
in self
.elffile
.iter_sections():
320 if not isinstance(section
, DynamicSection
):
323 self
._emitline
("\nDynamic section at offset %s contains %s entries:" % (
324 self
._format
_hex
(section
['sh_offset']),
326 self
._emitline
(" Tag Type Name/Value")
328 padding
= 20 + (8 if self
.elffile
.elfclass
== 32 else 0)
329 for tag
in section
.iter_tags():
330 if tag
.entry
.d_tag
== 'DT_NEEDED':
331 parsed
= 'Shared library: [%s]' % bytes2str(tag
.needed
)
332 elif tag
.entry
.d_tag
== 'DT_RPATH':
333 parsed
= 'Library rpath: [%s]' % bytes2str(tag
.rpath
)
334 elif tag
.entry
.d_tag
== 'DT_RUNPATH':
335 parsed
= 'Library runpath: [%s]' % bytes2str(tag
.runpath
)
336 elif tag
.entry
.d_tag
== 'DT_SONAME':
337 parsed
= 'Library soname: [%s]' % bytes2str(tag
.soname
)
338 elif (tag
.entry
.d_tag
.endswith('SZ') or
339 tag
.entry
.d_tag
.endswith('ENT')):
340 parsed
= '%i (bytes)' % tag
['d_val']
341 elif (tag
.entry
.d_tag
.endswith('NUM') or
342 tag
.entry
.d_tag
.endswith('COUNT')):
343 parsed
= '%i' % tag
['d_val']
344 elif tag
.entry
.d_tag
== 'DT_PLTREL':
345 s
= describe_dyn_tag(tag
.entry
.d_val
)
346 if s
.startswith('DT_'):
350 parsed
= '%#x' % tag
['d_val']
352 self
._emitline
(" %s %-*s %s" % (
353 self
._format
_hex
(ENUM_D_TAG
.get(tag
.entry
.d_tag
, tag
.entry
.d_tag
),
354 fullhex
=True, lead0x
=True),
356 '(%s)' % (tag
.entry
.d_tag
[3:],),
359 def display_relocations(self
):
360 """ Display the relocations contained in the file
362 has_relocation_sections
= False
363 for section
in self
.elffile
.iter_sections():
364 if not isinstance(section
, RelocationSection
):
367 has_relocation_sections
= True
368 self
._emitline
("\nRelocation section '%s' at offset %s contains %s entries:" % (
369 bytes2str(section
.name
),
370 self
._format
_hex
(section
['sh_offset']),
371 section
.num_relocations()))
372 if section
.is_RELA():
373 self
._emitline
(" Offset Info Type Sym. Value Sym. Name + Addend")
375 self
._emitline
(" Offset Info Type Sym.Value Sym. Name")
377 # The symbol table section pointed to in sh_link
378 symtable
= self
.elffile
.get_section(section
['sh_link'])
380 for rel
in section
.iter_relocations():
381 hexwidth
= 8 if self
.elffile
.elfclass
== 32 else 12
382 self
._emit
('%s %s %-17.17s' % (
383 self
._format
_hex
(rel
['r_offset'],
384 fieldsize
=hexwidth
, lead0x
=False),
385 self
._format
_hex
(rel
['r_info'],
386 fieldsize
=hexwidth
, lead0x
=False),
388 rel
['r_info_type'], self
.elffile
)))
390 if rel
['r_info_sym'] == 0:
394 symbol
= symtable
.get_symbol(rel
['r_info_sym'])
395 # Some symbols have zero 'st_name', so instead what's used is
396 # the name of the section they point at
397 if symbol
['st_name'] == 0:
398 symsec
= self
.elffile
.get_section(symbol
['st_shndx'])
399 symbol_name
= symsec
.name
401 symbol_name
= symbol
.name
402 self
._emit
(' %s %s%22.22s' % (
405 fullhex
=True, lead0x
=False),
406 ' ' if self
.elffile
.elfclass
== 32 else '',
407 bytes2str(symbol_name
)))
408 if section
.is_RELA():
409 self
._emit
(' %s %x' % (
410 '+' if rel
['r_addend'] >= 0 else '-',
411 abs(rel
['r_addend'])))
414 if not has_relocation_sections
:
415 self
._emitline
('\nThere are no relocations in this file.')
417 def display_version_info(self
):
418 """ Display the version info contained in the file
420 self
._init
_versioninfo
()
422 if not self
._versioninfo
['type']:
423 self
._emitline
("\nNo version information found in this file.")
426 for section
in self
.elffile
.iter_sections():
427 if isinstance(section
, GNUVerSymTableSection
):
429 self
._print
_version
_section
_header
(
430 section
, 'Version symbols', lead0x
=False)
432 num_symbols
= section
.num_symbols()
434 # Symbol version info are printed four by four entries
435 for idx_by_4
in range(0, num_symbols
, 4):
437 self
._emit
(' %03x:' % idx_by_4
)
439 for idx
in range(idx_by_4
, min(idx_by_4
+ 4, num_symbols
)):
441 symbol_version
= self
._symbol
_version
(idx
)
442 if symbol_version
['index'] == 'VER_NDX_LOCAL':
444 version_name
= '(*local*)'
445 elif symbol_version
['index'] == 'VER_NDX_GLOBAL':
447 version_name
= '(*global*)'
449 version_index
= symbol_version
['index']
450 version_name
= '(%(name)s)' % symbol_version
452 visibility
= 'h' if symbol_version
['hidden'] else ' '
454 self
._emit
('%4x%s%-13s' % (
455 version_index
, visibility
, version_name
))
459 elif isinstance(section
, GNUVerDefTableSection
):
461 self
._print
_version
_section
_header
(
462 section
, 'Version definition', indent
=2)
465 for verdef
, verdaux_iter
in section
.iter_versions():
466 verdaux
= next(verdaux_iter
)
469 if verdef
['vd_flags']:
470 flags
= describe_ver_flags(verdef
['vd_flags'])
471 # Mimic exactly the readelf output
476 self
._emitline
(' %s: Rev: %i Flags: %s Index: %i'
477 ' Cnt: %i Name: %s' % (
478 self
._format
_hex
(offset
, fieldsize
=6,
480 verdef
['vd_version'], flags
, verdef
['vd_ndx'],
481 verdef
['vd_cnt'], bytes2str(name
)))
484 offset
+ verdef
['vd_aux'] + verdaux
['vda_next'])
485 for idx
, verdaux
in enumerate(verdaux_iter
, start
=1):
486 self
._emitline
(' %s: Parent %i: %s' %
487 (self
._format
_hex
(verdaux_offset
, fieldsize
=4),
488 idx
, bytes2str(verdaux
.name
)))
489 verdaux_offset
+= verdaux
['vda_next']
491 offset
+= verdef
['vd_next']
493 elif isinstance(section
, GNUVerNeedTableSection
):
495 self
._print
_version
_section
_header
(section
, 'Version needs')
498 for verneed
, verneed_iter
in section
.iter_versions():
500 self
._emitline
(' %s: Version: %i File: %s Cnt: %i' % (
501 self
._format
_hex
(offset
, fieldsize
=6,
503 verneed
['vn_version'], bytes2str(verneed
.name
),
506 vernaux_offset
= offset
+ verneed
['vn_aux']
507 for idx
, vernaux
in enumerate(verneed_iter
, start
=1):
508 if vernaux
['vna_flags']:
509 flags
= describe_ver_flags(vernaux
['vna_flags'])
510 # Mimic exactly the readelf output
516 ' %s: Name: %s Flags: %s Version: %i' % (
517 self
._format
_hex
(vernaux_offset
, fieldsize
=4),
518 bytes2str(vernaux
.name
), flags
,
519 vernaux
['vna_other']))
521 vernaux_offset
+= vernaux
['vna_next']
523 offset
+= verneed
['vn_next']
525 def display_hex_dump(self
, section_spec
):
526 """ Display a hex dump of a section. section_spec is either a section
529 section
= self
._section
_from
_spec
(section_spec
)
531 self
._emitline
("Section '%s' does not exist in the file!" % (
535 self
._emitline
("\nHex dump of section '%s':" % bytes2str(section
.name
))
536 self
._note
_relocs
_for
_section
(section
)
537 addr
= section
['sh_addr']
538 data
= section
.data()
541 while dataptr
< len(data
):
542 bytesleft
= len(data
) - dataptr
543 # chunks of 16 bytes per line
544 linebytes
= 16 if bytesleft
> 16 else bytesleft
546 self
._emit
(' %s ' % self
._format
_hex
(addr
, fieldsize
=8))
549 self
._emit
('%2.2x' % byte2int(data
[dataptr
+ i
]))
555 for i
in range(linebytes
):
556 c
= data
[dataptr
+ i
: dataptr
+ i
+ 1]
557 if byte2int(c
[0]) >= 32 and byte2int(c
[0]) < 0x7f:
558 self
._emit
(bytes2str(c
))
560 self
._emit
(bytes2str(b
'.'))
568 def display_string_dump(self
, section_spec
):
569 """ Display a strings dump of a section. section_spec is either a
570 section number or a name.
572 section
= self
._section
_from
_spec
(section_spec
)
574 self
._emitline
("Section '%s' does not exist in the file!" % (
578 self
._emitline
("\nString dump of section '%s':" % bytes2str(section
.name
))
581 data
= section
.data()
584 while dataptr
< len(data
):
585 while ( dataptr
< len(data
) and
586 not (32 <= byte2int(data
[dataptr
]) <= 127)):
589 if dataptr
>= len(data
):
593 while endptr
< len(data
) and byte2int(data
[endptr
]) != 0:
597 self
._emitline
(' [%6x] %s' % (
598 dataptr
, bytes2str(data
[dataptr
:endptr
])))
603 self
._emitline
(' No strings found in this section.')
607 def display_debug_dump(self
, dump_what
):
608 """ Dump a DWARF section
610 self
._init
_dwarfinfo
()
611 if self
._dwarfinfo
is None:
614 set_global_machine_arch(self
.elffile
.get_machine_arch())
616 if dump_what
== 'info':
617 self
._dump
_debug
_info
()
618 elif dump_what
== 'decodedline':
619 self
._dump
_debug
_line
_programs
()
620 elif dump_what
== 'frames':
621 self
._dump
_debug
_frames
()
622 elif dump_what
== 'frames-interp':
623 self
._dump
_debug
_frames
_interp
()
625 self
._emitline
('debug dump not yet supported for "%s"' % dump_what
)
627 def _format_hex(self
, addr
, fieldsize
=None, fullhex
=False, lead0x
=True,
629 """ Format an address into a hexadecimal string.
632 Size of the hexadecimal field (with leading zeros to fit the
633 address into. For example with fieldsize=8, the format will
635 If None, the minimal required field size will be used.
638 If True, override fieldsize to set it to the maximal size
639 needed for the elfclass
642 If True, leading 0x is added
645 If True, override lead0x to emulate the alternate
646 hexadecimal form specified in format string with the #
647 character: only non-zero values are prefixed with 0x.
648 This form is used by readelf.
657 s
= '0x' if lead0x
else ''
659 fieldsize
= 8 if self
.elffile
.elfclass
== 32 else 16
660 if fieldsize
is None:
663 field
= '%' + '0%sx' % fieldsize
664 return s
+ field
% addr
667 def _print_version_section_header(self
, version_section
, name
, lead0x
=True, indent
=1):
668 """ Print a section header of one version related section (versym, verneed or verdef)
669 with some options to accomodate readelf little differences between each header
670 (e.g. indentation and 0x prefixing).
672 if hasattr(version_section
, 'num_versions'):
673 num_entries
= version_section
.num_versions()
675 num_entries
= version_section
.num_symbols()
677 self
._emitline
("\n%s section '%s' contains %s entries:" %
678 (name
, bytes2str(version_section
.name
), num_entries
))
679 self
._emitline
('%sAddr: %s Offset: %s Link: %i (%s)' %
681 self
._format
_hex
(version_section
['sh_addr'], fieldsize
=16, lead0x
=lead0x
),
682 self
._format
_hex
(version_section
['sh_offset'], fieldsize
=6, lead0x
=True),
683 version_section
['sh_link'],
684 bytes2str(self
.elffile
.get_section(version_section
['sh_link']).name
)))
688 def _init_versioninfo(self
):
689 """ Search and initialize informations about version related sections
690 and the kind of versioning used (GNU or Solaris).
692 if self
._versioninfo
is not None:
695 self
._versioninfo
= { 'versym': None, 'verdef': None,
696 'verneed': None, 'type': None }
698 for section
in self
.elffile
.iter_sections():
699 if isinstance(section
, GNUVerSymTableSection
):
700 self
._versioninfo
['versym'] = section
701 elif isinstance(section
, GNUVerDefTableSection
):
702 self
._versioninfo
['verdef'] = section
703 elif isinstance(section
, GNUVerNeedTableSection
):
704 self
._versioninfo
['verneed'] = section
705 elif isinstance(section
, DynamicSection
):
706 for tag
in section
.iter_tags():
707 if tag
['d_tag'] == 'DT_VERSYM':
708 self
._versioninfo
['type'] = 'GNU'
711 if not self
._versioninfo
['type'] and (
712 self
._versioninfo
['verneed'] or self
._versioninfo
['verdef']):
713 self
._versioninfo
['type'] = 'Solaris'
716 def _symbol_version(self
, nsym
):
717 """ Return a dict containing information on the
718 or None if no version information is available
720 self
._init
_versioninfo
()
722 symbol_version
= dict.fromkeys(('index', 'name', 'filename', 'hidden'))
724 if (not self
._versioninfo
['versym'] or
725 nsym
>= self
._versioninfo
['versym'].num_symbols()):
728 symbol
= self
._versioninfo
['versym'].get_symbol(nsym
)
729 index
= symbol
.entry
['ndx']
730 if not index
in ('VER_NDX_LOCAL', 'VER_NDX_GLOBAL'):
733 if self
._versioninfo
['type'] == 'GNU':
734 # In GNU versioning mode, the highest bit is used to
735 # store wether the symbol is hidden or not
738 symbol_version
['hidden'] = True
740 if (self
._versioninfo
['verdef'] and
741 index
<= self
._versioninfo
['verdef'].num_versions()):
743 self
._versioninfo
['verdef'].get_version(index
)
744 symbol_version
['name'] = bytes2str(next(verdaux_iter
).name
)
747 self
._versioninfo
['verneed'].get_version(index
)
748 symbol_version
['name'] = bytes2str(vernaux
.name
)
749 symbol_version
['filename'] = bytes2str(verneed
.name
)
751 symbol_version
['index'] = index
752 return symbol_version
755 def _section_from_spec(self
, spec
):
756 """ Retrieve a section given a "spec" (either number or name).
757 Return None if no such section exists in the file.
761 if num
< self
.elffile
.num_sections():
762 return self
.elffile
.get_section(num
)
766 # Not a number. Must be a name then
767 return self
.elffile
.get_section_by_name(str2bytes(spec
))
769 def _note_relocs_for_section(self
, section
):
770 """ If there are relocation sections pointing to the givne section,
771 emit a note about it.
773 for relsec
in self
.elffile
.iter_sections():
774 if isinstance(relsec
, RelocationSection
):
775 info_idx
= relsec
['sh_info']
776 if self
.elffile
.get_section(info_idx
) == section
:
777 self
._emitline
(' Note: This section has relocations against it, but these have NOT been applied to this dump.')
780 def _init_dwarfinfo(self
):
781 """ Initialize the DWARF info contained in the file and assign it to
783 Leave self._dwarfinfo at None if no DWARF info was found in the file
785 if self
._dwarfinfo
is not None:
788 if self
.elffile
.has_dwarf_info():
789 self
._dwarfinfo
= self
.elffile
.get_dwarf_info()
791 self
._dwarfinfo
= None
793 def _dump_debug_info(self
):
794 """ Dump the debugging info section.
796 self
._emitline
('Contents of the .debug_info section:\n')
798 # Offset of the .debug_info section in the stream
799 section_offset
= self
._dwarfinfo
.debug_info_sec
.global_offset
801 for cu
in self
._dwarfinfo
.iter_CUs():
802 self
._emitline
(' Compilation Unit @ offset %s:' %
803 self
._format
_hex
(cu
.cu_offset
))
804 self
._emitline
(' Length: %s (%s)' % (
805 self
._format
_hex
(cu
['unit_length']),
806 '%s-bit' % cu
.dwarf_format()))
807 self
._emitline
(' Version: %s' % cu
['version']),
808 self
._emitline
(' Abbrev Offset: %s' % (
809 self
._format
_hex
(cu
['debug_abbrev_offset']))),
810 self
._emitline
(' Pointer Size: %s' % cu
['address_size'])
812 # The nesting depth of each DIE within the tree of DIEs must be
813 # displayed. To implement this, a counter is incremented each time
814 # the current DIE has children, and decremented when a null die is
815 # encountered. Due to the way the DIE tree is serialized, this will
816 # correctly reflect the nesting depth
819 for die
in cu
.iter_DIEs():
820 self
._emitline
(' <%s><%x>: Abbrev Number: %s%s' % (
824 (' (%s)' % die
.tag
) if not die
.is_null() else ''))
829 for attr
in itervalues(die
.attributes
):
831 # Unknown attribute values are passed-through as integers
832 if isinstance(name
, int):
833 name
= 'Unknown AT value: %x' % name
834 self
._emitline
(' <%2x> %-18s: %s' % (
838 attr
, die
, section_offset
)))
845 def _dump_debug_line_programs(self
):
846 """ Dump the (decoded) line programs from .debug_line
847 The programs are dumped in the order of the CUs they belong to.
849 self
._emitline
('Decoded dump of debug contents of section .debug_line:\n')
851 for cu
in self
._dwarfinfo
.iter_CUs():
852 lineprogram
= self
._dwarfinfo
.line_program_for_CU(cu
)
854 cu_filename
= bytes2str(lineprogram
['file_entry'][0].name
)
855 if len(lineprogram
['include_directory']) > 0:
856 dir_index
= lineprogram
['file_entry'][0].dir_index
858 dir = lineprogram
['include_directory'][dir_index
- 1]
861 cu_filename
= '%s/%s' % (bytes2str(dir), cu_filename
)
863 self
._emitline
('CU: %s:' % cu_filename
)
864 self
._emitline
('File name Line number Starting address')
866 # Print each state's file, line and address information. For some
867 # instructions other output is needed to be compatible with
869 for entry
in lineprogram
.get_entries():
872 # Special handling for commands that don't set a new state
873 if entry
.command
== DW_LNS_set_file
:
874 file_entry
= lineprogram
['file_entry'][entry
.args
[0] - 1]
875 if file_entry
.dir_index
== 0:
877 self
._emitline
('\n./%s:[++]' % (
878 bytes2str(file_entry
.name
)))
880 self
._emitline
('\n%s/%s:' % (
881 bytes2str(lineprogram
['include_directory'][file_entry
.dir_index
- 1]),
882 bytes2str(file_entry
.name
)))
883 elif entry
.command
== DW_LNE_define_file
:
884 self
._emitline
('%s:' % (
885 bytes2str(lineprogram
['include_directory'][entry
.args
[0].dir_index
])))
886 elif not state
.end_sequence
:
887 # readelf doesn't print the state after end_sequence
888 # instructions. I think it's a bug but to be compatible
889 # I don't print them too.
890 self
._emitline
('%-35s %11d %18s' % (
891 bytes2str(lineprogram
['file_entry'][state
.file - 1].name
),
893 '0' if state
.address
== 0 else
894 self
._format
_hex
(state
.address
)))
895 if entry
.command
== DW_LNS_copy
:
896 # Another readelf oddity...
899 def _dump_debug_frames(self
):
900 """ Dump the raw frame information from .debug_frame
902 if not self
._dwarfinfo
.has_CFI():
904 self
._emitline
('Contents of the .debug_frame section:')
906 for entry
in self
._dwarfinfo
.CFI_entries():
907 if isinstance(entry
, CIE
):
908 self
._emitline
('\n%08x %08x %08x CIE' % (
909 entry
.offset
, entry
['length'], entry
['CIE_id']))
910 self
._emitline
(' Version: %d' % entry
['version'])
911 self
._emitline
(' Augmentation: "%s"' % bytes2str(entry
['augmentation']))
912 self
._emitline
(' Code alignment factor: %u' % entry
['code_alignment_factor'])
913 self
._emitline
(' Data alignment factor: %d' % entry
['data_alignment_factor'])
914 self
._emitline
(' Return address column: %d' % entry
['return_address_register'])
917 self
._emitline
('\n%08x %08x %08x FDE cie=%08x pc=%08x..%08x' % (
920 entry
['CIE_pointer'],
922 entry
['initial_location'],
923 entry
['initial_location'] + entry
['address_range']))
925 self
._emit
(describe_CFI_instructions(entry
))
928 def _dump_debug_frames_interp(self
):
929 """ Dump the interpreted (decoded) frame information from .debug_frame
931 if not self
._dwarfinfo
.has_CFI():
934 self
._emitline
('Contents of the .debug_frame section:')
936 for entry
in self
._dwarfinfo
.CFI_entries():
937 if isinstance(entry
, CIE
):
938 self
._emitline
('\n%08x %08x %08x CIE "%s" cf=%d df=%d ra=%d' % (
942 bytes2str(entry
['augmentation']),
943 entry
['code_alignment_factor'],
944 entry
['data_alignment_factor'],
945 entry
['return_address_register']))
946 ra_regnum
= entry
['return_address_register']
948 self
._emitline
('\n%08x %08x %08x FDE cie=%08x pc=%08x..%08x' % (
951 entry
['CIE_pointer'],
953 entry
['initial_location'],
954 entry
['initial_location'] + entry
['address_range']))
955 ra_regnum
= entry
.cie
['return_address_register']
957 # Print the heading row for the decoded table
959 self
._emit
(' ' if entry
.structs
.address_size
== 4 else ' ')
962 # Decode the table nad look at the registers it describes.
963 # We build reg_order here to match readelf's order. In particular,
964 # registers are sorted by their number, and the register matching
965 # ra_regnum is always listed last with a special heading.
966 decoded_table
= entry
.get_decoded()
967 reg_order
= sorted(ifilter(
968 lambda r
: r
!= ra_regnum
,
969 decoded_table
.reg_order
))
971 # Headings for the registers
972 for regnum
in reg_order
:
973 self
._emit
('%-6s' % describe_reg_name(regnum
))
974 self
._emitline
('ra ')
976 # Now include ra_regnum in reg_order to print its values similarly
977 # to the other registers.
978 reg_order
.append(ra_regnum
)
979 for line
in decoded_table
.table
:
980 self
._emit
(self
._format
_hex
(
981 line
['pc'], fullhex
=True, lead0x
=False))
982 self
._emit
(' %-9s' % describe_CFI_CFA_rule(line
['cfa']))
984 for regnum
in reg_order
:
986 s
= describe_CFI_register_rule(line
[regnum
])
989 self
._emit
('%-6s' % s
)
993 def _emit(self
, s
=''):
994 """ Emit an object to output
996 self
.output
.write(str(s
))
998 def _emitline(self
, s
=''):
999 """ Emit an object to output, followed by a newline
1001 self
.output
.write(str(s
) + '\n')
1004 SCRIPT_DESCRIPTION
= 'Display information about the contents of ELF format files'
1005 VERSION_STRING
= '%%prog: based on pyelftools %s' % __version__
1008 def main(stream
=None):
1009 # parse the command-line arguments and invoke ReadElf
1010 optparser
= OptionParser(
1011 usage
='usage: %prog [options] <elf-file>',
1012 description
=SCRIPT_DESCRIPTION
,
1013 add_help_option
=False, # -h is a real option of readelf
1015 version
=VERSION_STRING
)
1016 optparser
.add_option('-d', '--dynamic',
1017 action
='store_true', dest
='show_dynamic_tags',
1018 help='Display the dynamic section')
1019 optparser
.add_option('-H', '--help',
1020 action
='store_true', dest
='help',
1021 help='Display this information')
1022 optparser
.add_option('-h', '--file-header',
1023 action
='store_true', dest
='show_file_header',
1024 help='Display the ELF file header')
1025 optparser
.add_option('-l', '--program-headers', '--segments',
1026 action
='store_true', dest
='show_program_header',
1027 help='Display the program headers')
1028 optparser
.add_option('-S', '--section-headers', '--sections',
1029 action
='store_true', dest
='show_section_header',
1030 help="Display the sections' headers")
1031 optparser
.add_option('-e', '--headers',
1032 action
='store_true', dest
='show_all_headers',
1033 help='Equivalent to: -h -l -S')
1034 optparser
.add_option('-s', '--symbols', '--syms',
1035 action
='store_true', dest
='show_symbols',
1036 help='Display the symbol table')
1037 optparser
.add_option('-r', '--relocs',
1038 action
='store_true', dest
='show_relocs',
1039 help='Display the relocations (if present)')
1040 optparser
.add_option('-x', '--hex-dump',
1041 action
='store', dest
='show_hex_dump', metavar
='<number|name>',
1042 help='Dump the contents of section <number|name> as bytes')
1043 optparser
.add_option('-p', '--string-dump',
1044 action
='store', dest
='show_string_dump', metavar
='<number|name>',
1045 help='Dump the contents of section <number|name> as strings')
1046 optparser
.add_option('-V', '--version-info',
1047 action
='store_true', dest
='show_version_info',
1048 help='Display the version sections (if present)')
1049 optparser
.add_option('--debug-dump',
1050 action
='store', dest
='debug_dump_what', metavar
='<what>',
1052 'Display the contents of DWARF debug sections. <what> can ' +
1053 'one of {info,decodedline,frames,frames-interp}'))
1055 options
, args
= optparser
.parse_args()
1057 if options
.help or len(args
) == 0:
1058 optparser
.print_help()
1061 if options
.show_all_headers
:
1062 do_file_header
= do_section_header
= do_program_header
= True
1064 do_file_header
= options
.show_file_header
1065 do_section_header
= options
.show_section_header
1066 do_program_header
= options
.show_program_header
1068 with
open(args
[0], 'rb') as file:
1070 readelf
= ReadElf(file, stream
or sys
.stdout
)
1072 readelf
.display_file_header()
1073 if do_section_header
:
1074 readelf
.display_section_headers(
1075 show_heading
=not do_file_header
)
1076 if do_program_header
:
1077 readelf
.display_program_headers(
1078 show_heading
=not do_file_header
)
1079 if options
.show_dynamic_tags
:
1080 readelf
.display_dynamic_tags()
1081 if options
.show_symbols
:
1082 readelf
.display_symbol_tables()
1083 if options
.show_relocs
:
1084 readelf
.display_relocations()
1085 if options
.show_version_info
:
1086 readelf
.display_version_info()
1087 if options
.show_hex_dump
:
1088 readelf
.display_hex_dump(options
.show_hex_dump
)
1089 if options
.show_string_dump
:
1090 readelf
.display_string_dump(options
.show_string_dump
)
1091 if options
.debug_dump_what
:
1092 readelf
.display_debug_dump(options
.debug_dump_what
)
1093 except ELFError
as ex
:
1094 sys
.stderr
.write('ELF error: %s\n' % ex
)
1099 # Run 'main' redirecting its output to readelfout.txt
1100 # Saves profiling information in readelf.profile
1101 PROFFILE
= 'readelf.profile'
1103 cProfile
.run('main(open("readelfout.txt", "w"))', PROFFILE
)
1105 # Dig in some profiling stats
1107 p
= pstats
.Stats(PROFFILE
)
1108 p
.sort_stats('cumulative').print_stats(25)
1111 #-------------------------------------------------------------------------------
1112 if __name__
== '__main__':