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 SymbolTableSection
28 from elftools
.elf
.gnuversions
import (
29 GNUVerSymSection
, GNUVerDefSection
,
32 from elftools
.elf
.relocation
import RelocationSection
33 from elftools
.elf
.descriptions
import (
34 describe_ei_class
, describe_ei_data
, describe_ei_version
,
35 describe_ei_osabi
, describe_e_type
, describe_e_machine
,
36 describe_e_version_numeric
, describe_p_type
, describe_p_flags
,
37 describe_sh_type
, describe_sh_flags
,
38 describe_symbol_type
, describe_symbol_bind
, describe_symbol_visibility
,
39 describe_symbol_shndx
, describe_reloc_type
, describe_dyn_tag
,
42 from elftools
.dwarf
.dwarfinfo
import DWARFInfo
43 from elftools
.dwarf
.descriptions
import (
44 describe_reg_name
, describe_attr_value
, set_global_machine_arch
,
45 describe_CFI_instructions
, describe_CFI_register_rule
,
46 describe_CFI_CFA_rule
,
48 from elftools
.dwarf
.constants
import (
49 DW_LNS_copy
, DW_LNS_set_file
, DW_LNE_define_file
)
50 from elftools
.dwarf
.callframe
import CIE
, FDE
53 class ReadElf(object):
54 """ display_* methods are used to emit output into the output stream
56 def __init__(self
, file, output
):
58 stream object with the ELF file to read
61 output stream to write to
63 self
.elffile
= ELFFile(file)
66 # Lazily initialized if a debug dump is requested
67 self
._dwarfinfo
= None
69 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
)))
275 self
._emitline
("\nSymbol table '%s' contains %s entries:" % (
276 bytes2str(section
.name
), section
.num_symbols()))
278 if self
.elffile
.elfclass
== 32:
279 self
._emitline
(' Num: Value Size Type Bind Vis Ndx Name')
281 self
._emitline
(' Num: Value Size Type Bind Vis Ndx Name')
283 for nsym
, symbol
in enumerate(section
.iter_symbols()):
286 # readelf doesn't display version info for Solaris versioning
287 if (section
['sh_type'] == 'SHT_DYNSYM' and
288 self
._versioninfo
['type'] == 'GNU'):
289 version
= self
._symbol
_version
(nsym
)
290 if (version
['name'] != bytes2str(symbol
.name
) and
291 version
['index'] not in ('VER_NDX_LOCAL',
293 if version
['filename']:
295 version_info
= '@%(name)s (%(index)i)' % version
298 if version
['hidden']:
299 version_info
= '@%(name)s' % version
301 version_info
= '@@%(name)s' % version
303 # symbol names are truncated to 25 chars, similarly to readelf
304 self
._emitline
('%6d: %s %5d %-7s %-6s %-7s %4s %.25s%s' % (
307 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
, GNUVerSymSection
):
428 self
._print
_version
_section
_header
(
429 section
, 'Version symbols', lead0x
=False)
431 num_symbols
= section
.num_symbols()
433 # Symbol version info are printed four by four entries
434 for idx_by_4
in range(0, num_symbols
, 4):
436 self
._emit
(' %03x:' % idx_by_4
)
438 for idx
in range(idx_by_4
, min(idx_by_4
+ 4, num_symbols
)):
440 symbol_version
= self
._symbol
_version
(idx
)
441 if symbol_version
['index'] == 'VER_NDX_LOCAL':
443 version_name
= '(*local*)'
444 elif symbol_version
['index'] == 'VER_NDX_GLOBAL':
446 version_name
= '(*global*)'
448 version_index
= symbol_version
['index']
449 version_name
= '(%(name)s)' % symbol_version
451 visibility
= 'h' if symbol_version
['hidden'] else ' '
453 self
._emit
('%4x%s%-13s' % (
454 version_index
, visibility
, version_name
))
458 elif isinstance(section
, GNUVerDefSection
):
459 self
._print
_version
_section
_header
(
460 section
, 'Version definition', indent
=2)
463 for verdef
, verdaux_iter
in section
.iter_versions():
464 verdaux
= next(verdaux_iter
)
467 if verdef
['vd_flags']:
468 flags
= describe_ver_flags(verdef
['vd_flags'])
469 # Mimic exactly the readelf output
474 self
._emitline
(' %s: Rev: %i Flags: %s Index: %i'
475 ' Cnt: %i Name: %s' % (
476 self
._format
_hex
(offset
, fieldsize
=6,
478 verdef
['vd_version'], flags
, verdef
['vd_ndx'],
479 verdef
['vd_cnt'], bytes2str(name
)))
482 offset
+ verdef
['vd_aux'] + verdaux
['vda_next'])
483 for idx
, verdaux
in enumerate(verdaux_iter
, start
=1):
484 self
._emitline
(' %s: Parent %i: %s' %
485 (self
._format
_hex
(verdaux_offset
, fieldsize
=4),
486 idx
, bytes2str(verdaux
.name
)))
487 verdaux_offset
+= verdaux
['vda_next']
489 offset
+= verdef
['vd_next']
491 elif isinstance(section
, GNUVerNeedSection
):
492 self
._print
_version
_section
_header
(section
, 'Version needs')
495 for verneed
, verneed_iter
in section
.iter_versions():
497 self
._emitline
(' %s: Version: %i File: %s Cnt: %i' % (
498 self
._format
_hex
(offset
, fieldsize
=6,
500 verneed
['vn_version'], bytes2str(verneed
.name
),
503 vernaux_offset
= offset
+ verneed
['vn_aux']
504 for idx
, vernaux
in enumerate(verneed_iter
, start
=1):
505 if vernaux
['vna_flags']:
506 flags
= describe_ver_flags(vernaux
['vna_flags'])
507 # Mimic exactly the readelf output
513 ' %s: Name: %s Flags: %s Version: %i' % (
514 self
._format
_hex
(vernaux_offset
, fieldsize
=4),
515 bytes2str(vernaux
.name
), flags
,
516 vernaux
['vna_other']))
518 vernaux_offset
+= vernaux
['vna_next']
520 offset
+= verneed
['vn_next']
522 def display_hex_dump(self
, section_spec
):
523 """ Display a hex dump of a section. section_spec is either a section
526 section
= self
._section
_from
_spec
(section_spec
)
528 self
._emitline
("Section '%s' does not exist in the file!" % (
532 self
._emitline
("\nHex dump of section '%s':" % bytes2str(section
.name
))
533 self
._note
_relocs
_for
_section
(section
)
534 addr
= section
['sh_addr']
535 data
= section
.data()
538 while dataptr
< len(data
):
539 bytesleft
= len(data
) - dataptr
540 # chunks of 16 bytes per line
541 linebytes
= 16 if bytesleft
> 16 else bytesleft
543 self
._emit
(' %s ' % self
._format
_hex
(addr
, fieldsize
=8))
546 self
._emit
('%2.2x' % byte2int(data
[dataptr
+ i
]))
552 for i
in range(linebytes
):
553 c
= data
[dataptr
+ i
: dataptr
+ i
+ 1]
554 if byte2int(c
[0]) >= 32 and byte2int(c
[0]) < 0x7f:
555 self
._emit
(bytes2str(c
))
557 self
._emit
(bytes2str(b
'.'))
565 def display_string_dump(self
, section_spec
):
566 """ Display a strings dump of a section. section_spec is either a
567 section number or a name.
569 section
= self
._section
_from
_spec
(section_spec
)
571 self
._emitline
("Section '%s' does not exist in the file!" % (
575 self
._emitline
("\nString dump of section '%s':" % bytes2str(section
.name
))
578 data
= section
.data()
581 while dataptr
< len(data
):
582 while ( dataptr
< len(data
) and
583 not (32 <= byte2int(data
[dataptr
]) <= 127)):
586 if dataptr
>= len(data
):
590 while endptr
< len(data
) and byte2int(data
[endptr
]) != 0:
594 self
._emitline
(' [%6x] %s' % (
595 dataptr
, bytes2str(data
[dataptr
:endptr
])))
600 self
._emitline
(' No strings found in this section.')
604 def display_debug_dump(self
, dump_what
):
605 """ Dump a DWARF section
607 self
._init
_dwarfinfo
()
608 if self
._dwarfinfo
is None:
611 set_global_machine_arch(self
.elffile
.get_machine_arch())
613 if dump_what
== 'info':
614 self
._dump
_debug
_info
()
615 elif dump_what
== 'decodedline':
616 self
._dump
_debug
_line
_programs
()
617 elif dump_what
== 'frames':
618 self
._dump
_debug
_frames
()
619 elif dump_what
== 'frames-interp':
620 self
._dump
_debug
_frames
_interp
()
622 self
._emitline
('debug dump not yet supported for "%s"' % dump_what
)
624 def _format_hex(self
, addr
, fieldsize
=None, fullhex
=False, lead0x
=True,
626 """ Format an address into a hexadecimal string.
629 Size of the hexadecimal field (with leading zeros to fit the
630 address into. For example with fieldsize=8, the format will
632 If None, the minimal required field size will be used.
635 If True, override fieldsize to set it to the maximal size
636 needed for the elfclass
639 If True, leading 0x is added
642 If True, override lead0x to emulate the alternate
643 hexadecimal form specified in format string with the #
644 character: only non-zero values are prefixed with 0x.
645 This form is used by readelf.
654 s
= '0x' if lead0x
else ''
656 fieldsize
= 8 if self
.elffile
.elfclass
== 32 else 16
657 if fieldsize
is None:
660 field
= '%' + '0%sx' % fieldsize
661 return s
+ field
% addr
663 def _print_version_section_header(self
, version_section
, name
, lead0x
=True,
665 """ Print a section header of one version related section (versym,
666 verneed or verdef) with some options to accomodate readelf
667 little differences between each header (e.g. indentation
670 if hasattr(version_section
, 'num_versions'):
671 num_entries
= version_section
.num_versions()
673 num_entries
= version_section
.num_symbols()
675 self
._emitline
("\n%s section '%s' contains %s entries:" %
676 (name
, bytes2str(version_section
.name
), num_entries
))
677 self
._emitline
('%sAddr: %s Offset: %s Link: %i (%s)' % (
680 version_section
['sh_addr'], fieldsize
=16, lead0x
=lead0x
),
682 version_section
['sh_offset'], fieldsize
=6, lead0x
=True),
683 version_section
['sh_link'],
685 self
.elffile
.get_section(version_section
['sh_link']).name
)
689 def _init_versioninfo(self
):
690 """ Search and initialize informations about version related sections
691 and the kind of versioning used (GNU or Solaris).
693 if self
._versioninfo
is not None:
696 self
._versioninfo
= {'versym': None, 'verdef': None,
697 'verneed': None, 'type': None}
699 for section
in self
.elffile
.iter_sections():
700 if isinstance(section
, GNUVerSymSection
):
701 self
._versioninfo
['versym'] = section
702 elif isinstance(section
, GNUVerDefSection
):
703 self
._versioninfo
['verdef'] = section
704 elif isinstance(section
, GNUVerNeedSection
):
705 self
._versioninfo
['verneed'] = section
706 elif isinstance(section
, DynamicSection
):
707 for tag
in section
.iter_tags():
708 if tag
['d_tag'] == 'DT_VERSYM':
709 self
._versioninfo
['type'] = 'GNU'
712 if not self
._versioninfo
['type'] and (
713 self
._versioninfo
['verneed'] or self
._versioninfo
['verdef']):
714 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
754 def _section_from_spec(self
, spec
):
755 """ Retrieve a section given a "spec" (either number or name).
756 Return None if no such section exists in the file.
760 if num
< self
.elffile
.num_sections():
761 return self
.elffile
.get_section(num
)
765 # Not a number. Must be a name then
766 return self
.elffile
.get_section_by_name(str2bytes(spec
))
768 def _note_relocs_for_section(self
, section
):
769 """ If there are relocation sections pointing to the givne section,
770 emit a note about it.
772 for relsec
in self
.elffile
.iter_sections():
773 if isinstance(relsec
, RelocationSection
):
774 info_idx
= relsec
['sh_info']
775 if self
.elffile
.get_section(info_idx
) == section
:
776 self
._emitline
(' Note: This section has relocations against it, but these have NOT been applied to this dump.')
779 def _init_dwarfinfo(self
):
780 """ Initialize the DWARF info contained in the file and assign it to
782 Leave self._dwarfinfo at None if no DWARF info was found in the file
784 if self
._dwarfinfo
is not None:
787 if self
.elffile
.has_dwarf_info():
788 self
._dwarfinfo
= self
.elffile
.get_dwarf_info()
790 self
._dwarfinfo
= None
792 def _dump_debug_info(self
):
793 """ Dump the debugging info section.
795 self
._emitline
('Contents of the .debug_info section:\n')
797 # Offset of the .debug_info section in the stream
798 section_offset
= self
._dwarfinfo
.debug_info_sec
.global_offset
800 for cu
in self
._dwarfinfo
.iter_CUs():
801 self
._emitline
(' Compilation Unit @ offset %s:' %
802 self
._format
_hex
(cu
.cu_offset
))
803 self
._emitline
(' Length: %s (%s)' % (
804 self
._format
_hex
(cu
['unit_length']),
805 '%s-bit' % cu
.dwarf_format()))
806 self
._emitline
(' Version: %s' % cu
['version']),
807 self
._emitline
(' Abbrev Offset: %s' % (
808 self
._format
_hex
(cu
['debug_abbrev_offset']))),
809 self
._emitline
(' Pointer Size: %s' % cu
['address_size'])
811 # The nesting depth of each DIE within the tree of DIEs must be
812 # displayed. To implement this, a counter is incremented each time
813 # the current DIE has children, and decremented when a null die is
814 # encountered. Due to the way the DIE tree is serialized, this will
815 # correctly reflect the nesting depth
818 for die
in cu
.iter_DIEs():
819 self
._emitline
(' <%s><%x>: Abbrev Number: %s%s' % (
823 (' (%s)' % die
.tag
) if not die
.is_null() else ''))
828 for attr
in itervalues(die
.attributes
):
830 # Unknown attribute values are passed-through as integers
831 if isinstance(name
, int):
832 name
= 'Unknown AT value: %x' % name
833 self
._emitline
(' <%2x> %-18s: %s' % (
837 attr
, die
, section_offset
)))
844 def _dump_debug_line_programs(self
):
845 """ Dump the (decoded) line programs from .debug_line
846 The programs are dumped in the order of the CUs they belong to.
848 self
._emitline
('Decoded dump of debug contents of section .debug_line:\n')
850 for cu
in self
._dwarfinfo
.iter_CUs():
851 lineprogram
= self
._dwarfinfo
.line_program_for_CU(cu
)
853 cu_filename
= bytes2str(lineprogram
['file_entry'][0].name
)
854 if len(lineprogram
['include_directory']) > 0:
855 dir_index
= lineprogram
['file_entry'][0].dir_index
857 dir = lineprogram
['include_directory'][dir_index
- 1]
860 cu_filename
= '%s/%s' % (bytes2str(dir), cu_filename
)
862 self
._emitline
('CU: %s:' % cu_filename
)
863 self
._emitline
('File name Line number Starting address')
865 # Print each state's file, line and address information. For some
866 # instructions other output is needed to be compatible with
868 for entry
in lineprogram
.get_entries():
871 # Special handling for commands that don't set a new state
872 if entry
.command
== DW_LNS_set_file
:
873 file_entry
= lineprogram
['file_entry'][entry
.args
[0] - 1]
874 if file_entry
.dir_index
== 0:
876 self
._emitline
('\n./%s:[++]' % (
877 bytes2str(file_entry
.name
)))
879 self
._emitline
('\n%s/%s:' % (
880 bytes2str(lineprogram
['include_directory'][file_entry
.dir_index
- 1]),
881 bytes2str(file_entry
.name
)))
882 elif entry
.command
== DW_LNE_define_file
:
883 self
._emitline
('%s:' % (
884 bytes2str(lineprogram
['include_directory'][entry
.args
[0].dir_index
])))
885 elif not state
.end_sequence
:
886 # readelf doesn't print the state after end_sequence
887 # instructions. I think it's a bug but to be compatible
888 # I don't print them too.
889 self
._emitline
('%-35s %11d %18s' % (
890 bytes2str(lineprogram
['file_entry'][state
.file - 1].name
),
892 '0' if state
.address
== 0 else
893 self
._format
_hex
(state
.address
)))
894 if entry
.command
== DW_LNS_copy
:
895 # Another readelf oddity...
898 def _dump_debug_frames(self
):
899 """ Dump the raw frame information from .debug_frame
901 if not self
._dwarfinfo
.has_CFI():
903 self
._emitline
('Contents of the .debug_frame section:')
905 for entry
in self
._dwarfinfo
.CFI_entries():
906 if isinstance(entry
, CIE
):
907 self
._emitline
('\n%08x %08x %08x CIE' % (
908 entry
.offset
, entry
['length'], entry
['CIE_id']))
909 self
._emitline
(' Version: %d' % entry
['version'])
910 self
._emitline
(' Augmentation: "%s"' % bytes2str(entry
['augmentation']))
911 self
._emitline
(' Code alignment factor: %u' % entry
['code_alignment_factor'])
912 self
._emitline
(' Data alignment factor: %d' % entry
['data_alignment_factor'])
913 self
._emitline
(' Return address column: %d' % entry
['return_address_register'])
916 self
._emitline
('\n%08x %08x %08x FDE cie=%08x pc=%08x..%08x' % (
919 entry
['CIE_pointer'],
921 entry
['initial_location'],
922 entry
['initial_location'] + entry
['address_range']))
924 self
._emit
(describe_CFI_instructions(entry
))
927 def _dump_debug_frames_interp(self
):
928 """ Dump the interpreted (decoded) frame information from .debug_frame
930 if not self
._dwarfinfo
.has_CFI():
933 self
._emitline
('Contents of the .debug_frame section:')
935 for entry
in self
._dwarfinfo
.CFI_entries():
936 if isinstance(entry
, CIE
):
937 self
._emitline
('\n%08x %08x %08x CIE "%s" cf=%d df=%d ra=%d' % (
941 bytes2str(entry
['augmentation']),
942 entry
['code_alignment_factor'],
943 entry
['data_alignment_factor'],
944 entry
['return_address_register']))
945 ra_regnum
= entry
['return_address_register']
947 self
._emitline
('\n%08x %08x %08x FDE cie=%08x pc=%08x..%08x' % (
950 entry
['CIE_pointer'],
952 entry
['initial_location'],
953 entry
['initial_location'] + entry
['address_range']))
954 ra_regnum
= entry
.cie
['return_address_register']
956 # Print the heading row for the decoded table
958 self
._emit
(' ' if entry
.structs
.address_size
== 4 else ' ')
961 # Decode the table nad look at the registers it describes.
962 # We build reg_order here to match readelf's order. In particular,
963 # registers are sorted by their number, and the register matching
964 # ra_regnum is always listed last with a special heading.
965 decoded_table
= entry
.get_decoded()
966 reg_order
= sorted(ifilter(
967 lambda r
: r
!= ra_regnum
,
968 decoded_table
.reg_order
))
970 # Headings for the registers
971 for regnum
in reg_order
:
972 self
._emit
('%-6s' % describe_reg_name(regnum
))
973 self
._emitline
('ra ')
975 # Now include ra_regnum in reg_order to print its values similarly
976 # to the other registers.
977 reg_order
.append(ra_regnum
)
978 for line
in decoded_table
.table
:
979 self
._emit
(self
._format
_hex
(
980 line
['pc'], fullhex
=True, lead0x
=False))
981 self
._emit
(' %-9s' % describe_CFI_CFA_rule(line
['cfa']))
983 for regnum
in reg_order
:
985 s
= describe_CFI_register_rule(line
[regnum
])
988 self
._emit
('%-6s' % s
)
992 def _emit(self
, s
=''):
993 """ Emit an object to output
995 self
.output
.write(str(s
))
997 def _emitline(self
, s
=''):
998 """ Emit an object to output, followed by a newline
1000 self
.output
.write(str(s
) + '\n')
1003 SCRIPT_DESCRIPTION
= 'Display information about the contents of ELF format files'
1004 VERSION_STRING
= '%%prog: based on pyelftools %s' % __version__
1007 def main(stream
=None):
1008 # parse the command-line arguments and invoke ReadElf
1009 optparser
= OptionParser(
1010 usage
='usage: %prog [options] <elf-file>',
1011 description
=SCRIPT_DESCRIPTION
,
1012 add_help_option
=False, # -h is a real option of readelf
1014 version
=VERSION_STRING
)
1015 optparser
.add_option('-d', '--dynamic',
1016 action
='store_true', dest
='show_dynamic_tags',
1017 help='Display the dynamic section')
1018 optparser
.add_option('-H', '--help',
1019 action
='store_true', dest
='help',
1020 help='Display this information')
1021 optparser
.add_option('-h', '--file-header',
1022 action
='store_true', dest
='show_file_header',
1023 help='Display the ELF file header')
1024 optparser
.add_option('-l', '--program-headers', '--segments',
1025 action
='store_true', dest
='show_program_header',
1026 help='Display the program headers')
1027 optparser
.add_option('-S', '--section-headers', '--sections',
1028 action
='store_true', dest
='show_section_header',
1029 help="Display the sections' headers")
1030 optparser
.add_option('-e', '--headers',
1031 action
='store_true', dest
='show_all_headers',
1032 help='Equivalent to: -h -l -S')
1033 optparser
.add_option('-s', '--symbols', '--syms',
1034 action
='store_true', dest
='show_symbols',
1035 help='Display the symbol table')
1036 optparser
.add_option('-r', '--relocs',
1037 action
='store_true', dest
='show_relocs',
1038 help='Display the relocations (if present)')
1039 optparser
.add_option('-x', '--hex-dump',
1040 action
='store', dest
='show_hex_dump', metavar
='<number|name>',
1041 help='Dump the contents of section <number|name> as bytes')
1042 optparser
.add_option('-p', '--string-dump',
1043 action
='store', dest
='show_string_dump', metavar
='<number|name>',
1044 help='Dump the contents of section <number|name> as strings')
1045 optparser
.add_option('-V', '--version-info',
1046 action
='store_true', dest
='show_version_info',
1047 help='Display the version sections (if present)')
1048 optparser
.add_option('--debug-dump',
1049 action
='store', dest
='debug_dump_what', metavar
='<what>',
1051 'Display the contents of DWARF debug sections. <what> can ' +
1052 'one of {info,decodedline,frames,frames-interp}'))
1054 options
, args
= optparser
.parse_args()
1056 if options
.help or len(args
) == 0:
1057 optparser
.print_help()
1060 if options
.show_all_headers
:
1061 do_file_header
= do_section_header
= do_program_header
= True
1063 do_file_header
= options
.show_file_header
1064 do_section_header
= options
.show_section_header
1065 do_program_header
= options
.show_program_header
1067 with
open(args
[0], 'rb') as file:
1069 readelf
= ReadElf(file, stream
or sys
.stdout
)
1071 readelf
.display_file_header()
1072 if do_section_header
:
1073 readelf
.display_section_headers(
1074 show_heading
=not do_file_header
)
1075 if do_program_header
:
1076 readelf
.display_program_headers(
1077 show_heading
=not do_file_header
)
1078 if options
.show_dynamic_tags
:
1079 readelf
.display_dynamic_tags()
1080 if options
.show_symbols
:
1081 readelf
.display_symbol_tables()
1082 if options
.show_relocs
:
1083 readelf
.display_relocations()
1084 if options
.show_version_info
:
1085 readelf
.display_version_info()
1086 if options
.show_hex_dump
:
1087 readelf
.display_hex_dump(options
.show_hex_dump
)
1088 if options
.show_string_dump
:
1089 readelf
.display_string_dump(options
.show_string_dump
)
1090 if options
.debug_dump_what
:
1091 readelf
.display_debug_dump(options
.debug_dump_what
)
1092 except ELFError
as ex
:
1093 sys
.stderr
.write('ELF error: %s\n' % ex
)
1098 # Run 'main' redirecting its output to readelfout.txt
1099 # Saves profiling information in readelf.profile
1100 PROFFILE
= 'readelf.profile'
1102 cProfile
.run('main(open("readelfout.txt", "w"))', PROFFILE
)
1104 # Dig in some profiling stats
1106 p
= pstats
.Stats(PROFFILE
)
1107 p
.sort_stats('cumulative').print_stats(25)
1110 #-------------------------------------------------------------------------------
1111 if __name__
== '__main__':