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
70 def display_file_header(self
):
71 """ Display the ELF file header
73 self
._emitline
('ELF Header:')
74 self
._emit
(' Magic: ')
75 self
._emitline
(' '.join('%2.2x' % byte2int(b
)
76 for b
in self
.elffile
.e_ident_raw
))
77 header
= self
.elffile
.header
78 e_ident
= header
['e_ident']
79 self
._emitline
(' Class: %s' %
80 describe_ei_class(e_ident
['EI_CLASS']))
81 self
._emitline
(' Data: %s' %
82 describe_ei_data(e_ident
['EI_DATA']))
83 self
._emitline
(' Version: %s' %
84 describe_ei_version(e_ident
['EI_VERSION']))
85 self
._emitline
(' OS/ABI: %s' %
86 describe_ei_osabi(e_ident
['EI_OSABI']))
87 self
._emitline
(' ABI Version: %d' %
88 e_ident
['EI_ABIVERSION'])
89 self
._emitline
(' Type: %s' %
90 describe_e_type(header
['e_type']))
91 self
._emitline
(' Machine: %s' %
92 describe_e_machine(header
['e_machine']))
93 self
._emitline
(' Version: %s' %
94 describe_e_version_numeric(header
['e_version']))
95 self
._emitline
(' Entry point address: %s' %
96 self
._format
_hex
(header
['e_entry']))
97 self
._emit
(' Start of program headers: %s' %
99 self
._emitline
(' (bytes into file)')
100 self
._emit
(' Start of section headers: %s' %
102 self
._emitline
(' (bytes into file)')
103 self
._emitline
(' Flags: %s' %
104 self
._format
_hex
(header
['e_flags']))
105 self
._emitline
(' Size of this header: %s (bytes)' %
107 self
._emitline
(' Size of program headers: %s (bytes)' %
108 header
['e_phentsize'])
109 self
._emitline
(' Number of program headers: %s' %
111 self
._emitline
(' Size of section headers: %s (bytes)' %
112 header
['e_shentsize'])
113 self
._emitline
(' Number of section headers: %s' %
115 self
._emitline
(' Section header string table index: %s' %
116 header
['e_shstrndx'])
118 def display_program_headers(self
, show_heading
=True):
119 """ Display the ELF program headers.
120 If show_heading is True, displays the heading for this information
121 (Elf file type is...)
124 if self
.elffile
.num_segments() == 0:
125 self
._emitline
('There are no program headers in this file.')
128 elfheader
= self
.elffile
.header
130 self
._emitline
('Elf file type is %s' %
131 describe_e_type(elfheader
['e_type']))
132 self
._emitline
('Entry point is %s' %
133 self
._format
_hex
(elfheader
['e_entry']))
134 # readelf weirness - why isn't e_phoff printed as hex? (for section
136 self
._emitline
('There are %s program headers, starting at offset %s' % (
137 elfheader
['e_phnum'], elfheader
['e_phoff']))
140 self
._emitline
('Program Headers:')
142 # Now comes the table of program headers with their attributes. Note
143 # that due to different formatting constraints of 32-bit and 64-bit
144 # addresses, there are some conditions on elfclass here.
146 # First comes the table heading
148 if self
.elffile
.elfclass
== 32:
149 self
._emitline
(' Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align')
151 self
._emitline
(' Type Offset VirtAddr PhysAddr')
152 self
._emitline
(' FileSiz MemSiz Flags Align')
156 for segment
in self
.elffile
.iter_segments():
157 self
._emit
(' %-14s ' % describe_p_type(segment
['p_type']))
159 if self
.elffile
.elfclass
== 32:
160 self
._emitline
('%s %s %s %s %s %-3s %s' % (
161 self
._format
_hex
(segment
['p_offset'], fieldsize
=6),
162 self
._format
_hex
(segment
['p_vaddr'], fullhex
=True),
163 self
._format
_hex
(segment
['p_paddr'], fullhex
=True),
164 self
._format
_hex
(segment
['p_filesz'], fieldsize
=5),
165 self
._format
_hex
(segment
['p_memsz'], fieldsize
=5),
166 describe_p_flags(segment
['p_flags']),
167 self
._format
_hex
(segment
['p_align'])))
169 self
._emitline
('%s %s %s' % (
170 self
._format
_hex
(segment
['p_offset'], fullhex
=True),
171 self
._format
_hex
(segment
['p_vaddr'], fullhex
=True),
172 self
._format
_hex
(segment
['p_paddr'], fullhex
=True)))
173 self
._emitline
(' %s %s %-3s %s' % (
174 self
._format
_hex
(segment
['p_filesz'], fullhex
=True),
175 self
._format
_hex
(segment
['p_memsz'], fullhex
=True),
176 describe_p_flags(segment
['p_flags']),
177 # lead0x set to False for p_align, to mimic readelf.
178 # No idea why the difference from 32-bit mode :-|
179 self
._format
_hex
(segment
['p_align'], lead0x
=False)))
181 if isinstance(segment
, InterpSegment
):
182 self
._emitline
(' [Requesting program interpreter: %s]' %
183 bytes2str(segment
.get_interp_name()))
185 # Sections to segments mapping
187 if self
.elffile
.num_sections() == 0:
188 # No sections? We're done
191 self
._emitline
('\n Section to Segment mapping:')
192 self
._emitline
(' Segment Sections...')
194 for nseg
, segment
in enumerate(self
.elffile
.iter_segments()):
195 self
._emit
(' %2.2d ' % nseg
)
197 for section
in self
.elffile
.iter_sections():
198 if ( not section
.is_null() and
199 segment
.section_in_segment(section
)):
200 self
._emit
('%s ' % bytes2str(section
.name
))
204 def display_section_headers(self
, show_heading
=True):
205 """ Display the ELF section headers
207 elfheader
= self
.elffile
.header
209 self
._emitline
('There are %s section headers, starting at offset %s' % (
210 elfheader
['e_shnum'], self
._format
_hex
(elfheader
['e_shoff'])))
212 self
._emitline
('\nSection Header%s:' % (
213 's' if elfheader
['e_shnum'] > 1 else ''))
215 # Different formatting constraints of 32-bit and 64-bit addresses
217 if self
.elffile
.elfclass
== 32:
218 self
._emitline
(' [Nr] Name Type Addr Off Size ES Flg Lk Inf Al')
220 self
._emitline
(' [Nr] Name Type Address Offset')
221 self
._emitline
(' Size EntSize Flags Link Info Align')
225 for nsec
, section
in enumerate(self
.elffile
.iter_sections()):
226 self
._emit
(' [%2u] %-17.17s %-15.15s ' % (
227 nsec
, bytes2str(section
.name
), describe_sh_type(section
['sh_type'])))
229 if self
.elffile
.elfclass
== 32:
230 self
._emitline
('%s %s %s %s %3s %2s %3s %2s' % (
231 self
._format
_hex
(section
['sh_addr'], fieldsize
=8, lead0x
=False),
232 self
._format
_hex
(section
['sh_offset'], fieldsize
=6, lead0x
=False),
233 self
._format
_hex
(section
['sh_size'], fieldsize
=6, lead0x
=False),
234 self
._format
_hex
(section
['sh_entsize'], fieldsize
=2, lead0x
=False),
235 describe_sh_flags(section
['sh_flags']),
236 section
['sh_link'], section
['sh_info'],
237 section
['sh_addralign']))
239 self
._emitline
(' %s %s' % (
240 self
._format
_hex
(section
['sh_addr'], fullhex
=True, lead0x
=False),
241 self
._format
_hex
(section
['sh_offset'],
242 fieldsize
=16 if section
['sh_offset'] > 0xffffffff else 8,
244 self
._emitline
(' %s %s %3s %2s %3s %s' % (
245 self
._format
_hex
(section
['sh_size'], fullhex
=True, lead0x
=False),
246 self
._format
_hex
(section
['sh_entsize'], fullhex
=True, lead0x
=False),
247 describe_sh_flags(section
['sh_flags']),
248 section
['sh_link'], section
['sh_info'],
249 section
['sh_addralign']))
251 self
._emitline
('Key to Flags:')
252 self
._emit
(' W (write), A (alloc), X (execute), M (merge), S (strings)')
253 if self
.elffile
['e_machine'] in ('EM_X86_64', 'EM_L10M'):
254 self
._emitline
(', l (large)')
257 self
._emitline
(' I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)')
258 self
._emitline
(' O (extra OS processing required) o (OS specific), p (processor specific)')
260 def display_symbol_tables(self
):
261 """ Display the symbol tables contained in the file
263 self
._init
_versioninfo
()
265 for section
in self
.elffile
.iter_sections():
266 if not isinstance(section
, SymbolTableSection
):
269 if section
['sh_entsize'] == 0:
270 self
._emitline
("\nSymbol table '%s' has a sh_entsize of zero!" % (
271 bytes2str(section
.name
)))
274 self
._emitline
("\nSymbol table '%s' contains %s entries:" % (
275 bytes2str(section
.name
), section
.num_symbols()))
277 if self
.elffile
.elfclass
== 32:
278 self
._emitline
(' Num: Value Size Type Bind Vis Ndx Name')
280 self
._emitline
(' Num: Value Size Type Bind Vis Ndx Name')
282 for nsym
, symbol
in enumerate(section
.iter_symbols()):
285 # readelf doesn't display version info for Solaris versioning
286 if (section
['sh_type'] == 'SHT_DYNSYM' and
287 self
._versioninfo
['type'] == 'GNU'):
288 version
= self
._symbol
_version
(nsym
)
289 if (version
['name'] != bytes2str(symbol
.name
) and
290 version
['index'] not in ('VER_NDX_LOCAL',
292 if version
['filename']:
294 version_info
= '@%(name)s (%(index)i)' % version
297 if version
['hidden']:
298 version_info
= '@%(name)s' % version
300 version_info
= '@@%(name)s' % version
302 # symbol names are truncated to 25 chars, similarly to readelf
303 self
._emitline
('%6d: %s %5d %-7s %-6s %-7s %4s %.25s%s' % (
306 symbol
['st_value'], fullhex
=True, lead0x
=False),
308 describe_symbol_type(symbol
['st_info']['type']),
309 describe_symbol_bind(symbol
['st_info']['bind']),
310 describe_symbol_visibility(symbol
['st_other']['visibility']),
311 describe_symbol_shndx(symbol
['st_shndx']),
312 bytes2str(symbol
.name
),
315 def display_dynamic_tags(self
):
316 """ Display the dynamic tags contained in the file
318 for section
in self
.elffile
.iter_sections():
319 if not isinstance(section
, DynamicSection
):
322 self
._emitline
("\nDynamic section at offset %s contains %s entries:" % (
323 self
._format
_hex
(section
['sh_offset']),
325 self
._emitline
(" Tag Type Name/Value")
327 padding
= 20 + (8 if self
.elffile
.elfclass
== 32 else 0)
328 for tag
in section
.iter_tags():
329 if tag
.entry
.d_tag
== 'DT_NEEDED':
330 parsed
= 'Shared library: [%s]' % bytes2str(tag
.needed
)
331 elif tag
.entry
.d_tag
== 'DT_RPATH':
332 parsed
= 'Library rpath: [%s]' % bytes2str(tag
.rpath
)
333 elif tag
.entry
.d_tag
== 'DT_RUNPATH':
334 parsed
= 'Library runpath: [%s]' % bytes2str(tag
.runpath
)
335 elif tag
.entry
.d_tag
== 'DT_SONAME':
336 parsed
= 'Library soname: [%s]' % bytes2str(tag
.soname
)
337 elif (tag
.entry
.d_tag
.endswith('SZ') or
338 tag
.entry
.d_tag
.endswith('ENT')):
339 parsed
= '%i (bytes)' % tag
['d_val']
340 elif (tag
.entry
.d_tag
.endswith('NUM') or
341 tag
.entry
.d_tag
.endswith('COUNT')):
342 parsed
= '%i' % tag
['d_val']
343 elif tag
.entry
.d_tag
== 'DT_PLTREL':
344 s
= describe_dyn_tag(tag
.entry
.d_val
)
345 if s
.startswith('DT_'):
349 parsed
= '%#x' % tag
['d_val']
351 self
._emitline
(" %s %-*s %s" % (
352 self
._format
_hex
(ENUM_D_TAG
.get(tag
.entry
.d_tag
, tag
.entry
.d_tag
),
353 fullhex
=True, lead0x
=True),
355 '(%s)' % (tag
.entry
.d_tag
[3:],),
358 def display_relocations(self
):
359 """ Display the relocations contained in the file
361 has_relocation_sections
= False
362 for section
in self
.elffile
.iter_sections():
363 if not isinstance(section
, RelocationSection
):
366 has_relocation_sections
= True
367 self
._emitline
("\nRelocation section '%s' at offset %s contains %s entries:" % (
368 bytes2str(section
.name
),
369 self
._format
_hex
(section
['sh_offset']),
370 section
.num_relocations()))
371 if section
.is_RELA():
372 self
._emitline
(" Offset Info Type Sym. Value Sym. Name + Addend")
374 self
._emitline
(" Offset Info Type Sym.Value Sym. Name")
376 # The symbol table section pointed to in sh_link
377 symtable
= self
.elffile
.get_section(section
['sh_link'])
379 for rel
in section
.iter_relocations():
380 hexwidth
= 8 if self
.elffile
.elfclass
== 32 else 12
381 self
._emit
('%s %s %-17.17s' % (
382 self
._format
_hex
(rel
['r_offset'],
383 fieldsize
=hexwidth
, lead0x
=False),
384 self
._format
_hex
(rel
['r_info'],
385 fieldsize
=hexwidth
, lead0x
=False),
387 rel
['r_info_type'], self
.elffile
)))
389 if rel
['r_info_sym'] == 0:
393 symbol
= symtable
.get_symbol(rel
['r_info_sym'])
394 # Some symbols have zero 'st_name', so instead what's used is
395 # the name of the section they point at
396 if symbol
['st_name'] == 0:
397 symsec
= self
.elffile
.get_section(symbol
['st_shndx'])
398 symbol_name
= symsec
.name
400 symbol_name
= symbol
.name
401 self
._emit
(' %s %s%22.22s' % (
404 fullhex
=True, lead0x
=False),
405 ' ' if self
.elffile
.elfclass
== 32 else '',
406 bytes2str(symbol_name
)))
407 if section
.is_RELA():
408 self
._emit
(' %s %x' % (
409 '+' if rel
['r_addend'] >= 0 else '-',
410 abs(rel
['r_addend'])))
413 if not has_relocation_sections
:
414 self
._emitline
('\nThere are no relocations in this file.')
416 def display_version_info(self
):
417 """ Display the version info contained in the file
419 self
._init
_versioninfo
()
421 if not self
._versioninfo
['type']:
422 self
._emitline
("\nNo version information found in this file.")
425 for section
in self
.elffile
.iter_sections():
426 if isinstance(section
, GNUVerSymTableSection
):
427 self
._print
_version
_section
_header
(
428 section
, 'Version symbols', lead0x
=False)
430 num_symbols
= section
.num_symbols()
432 # Symbol version info are printed four by four entries
433 for idx_by_4
in range(0, num_symbols
, 4):
435 self
._emit
(' %03x:' % idx_by_4
)
437 for idx
in range(idx_by_4
, min(idx_by_4
+ 4, num_symbols
)):
439 symbol_version
= self
._symbol
_version
(idx
)
440 if symbol_version
['index'] == 'VER_NDX_LOCAL':
442 version_name
= '(*local*)'
443 elif symbol_version
['index'] == 'VER_NDX_GLOBAL':
445 version_name
= '(*global*)'
447 version_index
= symbol_version
['index']
448 version_name
= '(%(name)s)' % symbol_version
450 visibility
= 'h' if symbol_version
['hidden'] else ' '
452 self
._emit
('%4x%s%-13s' % (
453 version_index
, visibility
, version_name
))
457 elif isinstance(section
, GNUVerDefTableSection
):
458 self
._print
_version
_section
_header
(
459 section
, 'Version definition', indent
=2)
462 for verdef
, verdaux_iter
in section
.iter_versions():
463 verdaux
= next(verdaux_iter
)
466 if verdef
['vd_flags']:
467 flags
= describe_ver_flags(verdef
['vd_flags'])
468 # Mimic exactly the readelf output
473 self
._emitline
(' %s: Rev: %i Flags: %s Index: %i'
474 ' Cnt: %i Name: %s' % (
475 self
._format
_hex
(offset
, fieldsize
=6,
477 verdef
['vd_version'], flags
, verdef
['vd_ndx'],
478 verdef
['vd_cnt'], bytes2str(name
)))
481 offset
+ verdef
['vd_aux'] + verdaux
['vda_next'])
482 for idx
, verdaux
in enumerate(verdaux_iter
, start
=1):
483 self
._emitline
(' %s: Parent %i: %s' %
484 (self
._format
_hex
(verdaux_offset
, fieldsize
=4),
485 idx
, bytes2str(verdaux
.name
)))
486 verdaux_offset
+= verdaux
['vda_next']
488 offset
+= verdef
['vd_next']
490 elif isinstance(section
, GNUVerNeedTableSection
):
491 self
._print
_version
_section
_header
(section
, 'Version needs')
494 for verneed
, verneed_iter
in section
.iter_versions():
496 self
._emitline
(' %s: Version: %i File: %s Cnt: %i' % (
497 self
._format
_hex
(offset
, fieldsize
=6,
499 verneed
['vn_version'], bytes2str(verneed
.name
),
502 vernaux_offset
= offset
+ verneed
['vn_aux']
503 for idx
, vernaux
in enumerate(verneed_iter
, start
=1):
504 if vernaux
['vna_flags']:
505 flags
= describe_ver_flags(vernaux
['vna_flags'])
506 # Mimic exactly the readelf output
512 ' %s: Name: %s Flags: %s Version: %i' % (
513 self
._format
_hex
(vernaux_offset
, fieldsize
=4),
514 bytes2str(vernaux
.name
), flags
,
515 vernaux
['vna_other']))
517 vernaux_offset
+= vernaux
['vna_next']
519 offset
+= verneed
['vn_next']
521 def display_hex_dump(self
, section_spec
):
522 """ Display a hex dump of a section. section_spec is either a section
525 section
= self
._section
_from
_spec
(section_spec
)
527 self
._emitline
("Section '%s' does not exist in the file!" % (
531 self
._emitline
("\nHex dump of section '%s':" % bytes2str(section
.name
))
532 self
._note
_relocs
_for
_section
(section
)
533 addr
= section
['sh_addr']
534 data
= section
.data()
537 while dataptr
< len(data
):
538 bytesleft
= len(data
) - dataptr
539 # chunks of 16 bytes per line
540 linebytes
= 16 if bytesleft
> 16 else bytesleft
542 self
._emit
(' %s ' % self
._format
_hex
(addr
, fieldsize
=8))
545 self
._emit
('%2.2x' % byte2int(data
[dataptr
+ i
]))
551 for i
in range(linebytes
):
552 c
= data
[dataptr
+ i
: dataptr
+ i
+ 1]
553 if byte2int(c
[0]) >= 32 and byte2int(c
[0]) < 0x7f:
554 self
._emit
(bytes2str(c
))
556 self
._emit
(bytes2str(b
'.'))
564 def display_string_dump(self
, section_spec
):
565 """ Display a strings dump of a section. section_spec is either a
566 section number or a name.
568 section
= self
._section
_from
_spec
(section_spec
)
570 self
._emitline
("Section '%s' does not exist in the file!" % (
574 self
._emitline
("\nString dump of section '%s':" % bytes2str(section
.name
))
577 data
= section
.data()
580 while dataptr
< len(data
):
581 while ( dataptr
< len(data
) and
582 not (32 <= byte2int(data
[dataptr
]) <= 127)):
585 if dataptr
>= len(data
):
589 while endptr
< len(data
) and byte2int(data
[endptr
]) != 0:
593 self
._emitline
(' [%6x] %s' % (
594 dataptr
, bytes2str(data
[dataptr
:endptr
])))
599 self
._emitline
(' No strings found in this section.')
603 def display_debug_dump(self
, dump_what
):
604 """ Dump a DWARF section
606 self
._init
_dwarfinfo
()
607 if self
._dwarfinfo
is None:
610 set_global_machine_arch(self
.elffile
.get_machine_arch())
612 if dump_what
== 'info':
613 self
._dump
_debug
_info
()
614 elif dump_what
== 'decodedline':
615 self
._dump
_debug
_line
_programs
()
616 elif dump_what
== 'frames':
617 self
._dump
_debug
_frames
()
618 elif dump_what
== 'frames-interp':
619 self
._dump
_debug
_frames
_interp
()
621 self
._emitline
('debug dump not yet supported for "%s"' % dump_what
)
623 def _format_hex(self
, addr
, fieldsize
=None, fullhex
=False, lead0x
=True,
625 """ Format an address into a hexadecimal string.
628 Size of the hexadecimal field (with leading zeros to fit the
629 address into. For example with fieldsize=8, the format will
631 If None, the minimal required field size will be used.
634 If True, override fieldsize to set it to the maximal size
635 needed for the elfclass
638 If True, leading 0x is added
641 If True, override lead0x to emulate the alternate
642 hexadecimal form specified in format string with the #
643 character: only non-zero values are prefixed with 0x.
644 This form is used by readelf.
653 s
= '0x' if lead0x
else ''
655 fieldsize
= 8 if self
.elffile
.elfclass
== 32 else 16
656 if fieldsize
is None:
659 field
= '%' + '0%sx' % fieldsize
660 return s
+ field
% addr
662 def _print_version_section_header(self
, version_section
, name
, lead0x
=True,
664 """ Print a section header of one version related section (versym,
665 verneed or verdef) with some options to accomodate readelf
666 little differences between each header (e.g. indentation
669 if hasattr(version_section
, 'num_versions'):
670 num_entries
= version_section
.num_versions()
672 num_entries
= version_section
.num_symbols()
674 self
._emitline
("\n%s section '%s' contains %s entries:" %
675 (name
, bytes2str(version_section
.name
), num_entries
))
676 self
._emitline
('%sAddr: %s Offset: %s Link: %i (%s)' % (
679 version_section
['sh_addr'], fieldsize
=16, lead0x
=lead0x
),
681 version_section
['sh_offset'], fieldsize
=6, lead0x
=True),
682 version_section
['sh_link'],
684 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'
715 def _symbol_version(self
, nsym
):
716 """ Return a dict containing information on the
717 or None if no version information is available
719 self
._init
_versioninfo
()
721 symbol_version
= dict.fromkeys(('index', 'name', 'filename', 'hidden'))
723 if (not self
._versioninfo
['versym'] or
724 nsym
>= self
._versioninfo
['versym'].num_symbols()):
727 symbol
= self
._versioninfo
['versym'].get_symbol(nsym
)
728 index
= symbol
.entry
['ndx']
729 if not index
in ('VER_NDX_LOCAL', 'VER_NDX_GLOBAL'):
732 if self
._versioninfo
['type'] == 'GNU':
733 # In GNU versioning mode, the highest bit is used to
734 # store wether the symbol is hidden or not
737 symbol_version
['hidden'] = True
739 if (self
._versioninfo
['verdef'] and
740 index
<= self
._versioninfo
['verdef'].num_versions()):
742 self
._versioninfo
['verdef'].get_version(index
)
743 symbol_version
['name'] = bytes2str(next(verdaux_iter
).name
)
746 self
._versioninfo
['verneed'].get_version(index
)
747 symbol_version
['name'] = bytes2str(vernaux
.name
)
748 symbol_version
['filename'] = bytes2str(verneed
.name
)
750 symbol_version
['index'] = index
751 return symbol_version
753 def _section_from_spec(self
, spec
):
754 """ Retrieve a section given a "spec" (either number or name).
755 Return None if no such section exists in the file.
759 if num
< self
.elffile
.num_sections():
760 return self
.elffile
.get_section(num
)
764 # Not a number. Must be a name then
765 return self
.elffile
.get_section_by_name(str2bytes(spec
))
767 def _note_relocs_for_section(self
, section
):
768 """ If there are relocation sections pointing to the givne section,
769 emit a note about it.
771 for relsec
in self
.elffile
.iter_sections():
772 if isinstance(relsec
, RelocationSection
):
773 info_idx
= relsec
['sh_info']
774 if self
.elffile
.get_section(info_idx
) == section
:
775 self
._emitline
(' Note: This section has relocations against it, but these have NOT been applied to this dump.')
778 def _init_dwarfinfo(self
):
779 """ Initialize the DWARF info contained in the file and assign it to
781 Leave self._dwarfinfo at None if no DWARF info was found in the file
783 if self
._dwarfinfo
is not None:
786 if self
.elffile
.has_dwarf_info():
787 self
._dwarfinfo
= self
.elffile
.get_dwarf_info()
789 self
._dwarfinfo
= None
791 def _dump_debug_info(self
):
792 """ Dump the debugging info section.
794 self
._emitline
('Contents of the .debug_info section:\n')
796 # Offset of the .debug_info section in the stream
797 section_offset
= self
._dwarfinfo
.debug_info_sec
.global_offset
799 for cu
in self
._dwarfinfo
.iter_CUs():
800 self
._emitline
(' Compilation Unit @ offset %s:' %
801 self
._format
_hex
(cu
.cu_offset
))
802 self
._emitline
(' Length: %s (%s)' % (
803 self
._format
_hex
(cu
['unit_length']),
804 '%s-bit' % cu
.dwarf_format()))
805 self
._emitline
(' Version: %s' % cu
['version']),
806 self
._emitline
(' Abbrev Offset: %s' % (
807 self
._format
_hex
(cu
['debug_abbrev_offset']))),
808 self
._emitline
(' Pointer Size: %s' % cu
['address_size'])
810 # The nesting depth of each DIE within the tree of DIEs must be
811 # displayed. To implement this, a counter is incremented each time
812 # the current DIE has children, and decremented when a null die is
813 # encountered. Due to the way the DIE tree is serialized, this will
814 # correctly reflect the nesting depth
817 for die
in cu
.iter_DIEs():
818 self
._emitline
(' <%s><%x>: Abbrev Number: %s%s' % (
822 (' (%s)' % die
.tag
) if not die
.is_null() else ''))
827 for attr
in itervalues(die
.attributes
):
829 # Unknown attribute values are passed-through as integers
830 if isinstance(name
, int):
831 name
= 'Unknown AT value: %x' % name
832 self
._emitline
(' <%2x> %-18s: %s' % (
836 attr
, die
, section_offset
)))
843 def _dump_debug_line_programs(self
):
844 """ Dump the (decoded) line programs from .debug_line
845 The programs are dumped in the order of the CUs they belong to.
847 self
._emitline
('Decoded dump of debug contents of section .debug_line:\n')
849 for cu
in self
._dwarfinfo
.iter_CUs():
850 lineprogram
= self
._dwarfinfo
.line_program_for_CU(cu
)
852 cu_filename
= bytes2str(lineprogram
['file_entry'][0].name
)
853 if len(lineprogram
['include_directory']) > 0:
854 dir_index
= lineprogram
['file_entry'][0].dir_index
856 dir = lineprogram
['include_directory'][dir_index
- 1]
859 cu_filename
= '%s/%s' % (bytes2str(dir), cu_filename
)
861 self
._emitline
('CU: %s:' % cu_filename
)
862 self
._emitline
('File name Line number Starting address')
864 # Print each state's file, line and address information. For some
865 # instructions other output is needed to be compatible with
867 for entry
in lineprogram
.get_entries():
870 # Special handling for commands that don't set a new state
871 if entry
.command
== DW_LNS_set_file
:
872 file_entry
= lineprogram
['file_entry'][entry
.args
[0] - 1]
873 if file_entry
.dir_index
== 0:
875 self
._emitline
('\n./%s:[++]' % (
876 bytes2str(file_entry
.name
)))
878 self
._emitline
('\n%s/%s:' % (
879 bytes2str(lineprogram
['include_directory'][file_entry
.dir_index
- 1]),
880 bytes2str(file_entry
.name
)))
881 elif entry
.command
== DW_LNE_define_file
:
882 self
._emitline
('%s:' % (
883 bytes2str(lineprogram
['include_directory'][entry
.args
[0].dir_index
])))
884 elif not state
.end_sequence
:
885 # readelf doesn't print the state after end_sequence
886 # instructions. I think it's a bug but to be compatible
887 # I don't print them too.
888 self
._emitline
('%-35s %11d %18s' % (
889 bytes2str(lineprogram
['file_entry'][state
.file - 1].name
),
891 '0' if state
.address
== 0 else
892 self
._format
_hex
(state
.address
)))
893 if entry
.command
== DW_LNS_copy
:
894 # Another readelf oddity...
897 def _dump_debug_frames(self
):
898 """ Dump the raw frame information from .debug_frame
900 if not self
._dwarfinfo
.has_CFI():
902 self
._emitline
('Contents of the .debug_frame section:')
904 for entry
in self
._dwarfinfo
.CFI_entries():
905 if isinstance(entry
, CIE
):
906 self
._emitline
('\n%08x %08x %08x CIE' % (
907 entry
.offset
, entry
['length'], entry
['CIE_id']))
908 self
._emitline
(' Version: %d' % entry
['version'])
909 self
._emitline
(' Augmentation: "%s"' % bytes2str(entry
['augmentation']))
910 self
._emitline
(' Code alignment factor: %u' % entry
['code_alignment_factor'])
911 self
._emitline
(' Data alignment factor: %d' % entry
['data_alignment_factor'])
912 self
._emitline
(' Return address column: %d' % entry
['return_address_register'])
915 self
._emitline
('\n%08x %08x %08x FDE cie=%08x pc=%08x..%08x' % (
918 entry
['CIE_pointer'],
920 entry
['initial_location'],
921 entry
['initial_location'] + entry
['address_range']))
923 self
._emit
(describe_CFI_instructions(entry
))
926 def _dump_debug_frames_interp(self
):
927 """ Dump the interpreted (decoded) frame information from .debug_frame
929 if not self
._dwarfinfo
.has_CFI():
932 self
._emitline
('Contents of the .debug_frame section:')
934 for entry
in self
._dwarfinfo
.CFI_entries():
935 if isinstance(entry
, CIE
):
936 self
._emitline
('\n%08x %08x %08x CIE "%s" cf=%d df=%d ra=%d' % (
940 bytes2str(entry
['augmentation']),
941 entry
['code_alignment_factor'],
942 entry
['data_alignment_factor'],
943 entry
['return_address_register']))
944 ra_regnum
= entry
['return_address_register']
946 self
._emitline
('\n%08x %08x %08x FDE cie=%08x pc=%08x..%08x' % (
949 entry
['CIE_pointer'],
951 entry
['initial_location'],
952 entry
['initial_location'] + entry
['address_range']))
953 ra_regnum
= entry
.cie
['return_address_register']
955 # Print the heading row for the decoded table
957 self
._emit
(' ' if entry
.structs
.address_size
== 4 else ' ')
960 # Decode the table nad look at the registers it describes.
961 # We build reg_order here to match readelf's order. In particular,
962 # registers are sorted by their number, and the register matching
963 # ra_regnum is always listed last with a special heading.
964 decoded_table
= entry
.get_decoded()
965 reg_order
= sorted(ifilter(
966 lambda r
: r
!= ra_regnum
,
967 decoded_table
.reg_order
))
969 # Headings for the registers
970 for regnum
in reg_order
:
971 self
._emit
('%-6s' % describe_reg_name(regnum
))
972 self
._emitline
('ra ')
974 # Now include ra_regnum in reg_order to print its values similarly
975 # to the other registers.
976 reg_order
.append(ra_regnum
)
977 for line
in decoded_table
.table
:
978 self
._emit
(self
._format
_hex
(
979 line
['pc'], fullhex
=True, lead0x
=False))
980 self
._emit
(' %-9s' % describe_CFI_CFA_rule(line
['cfa']))
982 for regnum
in reg_order
:
984 s
= describe_CFI_register_rule(line
[regnum
])
987 self
._emit
('%-6s' % s
)
991 def _emit(self
, s
=''):
992 """ Emit an object to output
994 self
.output
.write(str(s
))
996 def _emitline(self
, s
=''):
997 """ Emit an object to output, followed by a newline
999 self
.output
.write(str(s
) + '\n')
1002 SCRIPT_DESCRIPTION
= 'Display information about the contents of ELF format files'
1003 VERSION_STRING
= '%%prog: based on pyelftools %s' % __version__
1006 def main(stream
=None):
1007 # parse the command-line arguments and invoke ReadElf
1008 optparser
= OptionParser(
1009 usage
='usage: %prog [options] <elf-file>',
1010 description
=SCRIPT_DESCRIPTION
,
1011 add_help_option
=False, # -h is a real option of readelf
1013 version
=VERSION_STRING
)
1014 optparser
.add_option('-d', '--dynamic',
1015 action
='store_true', dest
='show_dynamic_tags',
1016 help='Display the dynamic section')
1017 optparser
.add_option('-H', '--help',
1018 action
='store_true', dest
='help',
1019 help='Display this information')
1020 optparser
.add_option('-h', '--file-header',
1021 action
='store_true', dest
='show_file_header',
1022 help='Display the ELF file header')
1023 optparser
.add_option('-l', '--program-headers', '--segments',
1024 action
='store_true', dest
='show_program_header',
1025 help='Display the program headers')
1026 optparser
.add_option('-S', '--section-headers', '--sections',
1027 action
='store_true', dest
='show_section_header',
1028 help="Display the sections' headers")
1029 optparser
.add_option('-e', '--headers',
1030 action
='store_true', dest
='show_all_headers',
1031 help='Equivalent to: -h -l -S')
1032 optparser
.add_option('-s', '--symbols', '--syms',
1033 action
='store_true', dest
='show_symbols',
1034 help='Display the symbol table')
1035 optparser
.add_option('-r', '--relocs',
1036 action
='store_true', dest
='show_relocs',
1037 help='Display the relocations (if present)')
1038 optparser
.add_option('-x', '--hex-dump',
1039 action
='store', dest
='show_hex_dump', metavar
='<number|name>',
1040 help='Dump the contents of section <number|name> as bytes')
1041 optparser
.add_option('-p', '--string-dump',
1042 action
='store', dest
='show_string_dump', metavar
='<number|name>',
1043 help='Dump the contents of section <number|name> as strings')
1044 optparser
.add_option('-V', '--version-info',
1045 action
='store_true', dest
='show_version_info',
1046 help='Display the version sections (if present)')
1047 optparser
.add_option('--debug-dump',
1048 action
='store', dest
='debug_dump_what', metavar
='<what>',
1050 'Display the contents of DWARF debug sections. <what> can ' +
1051 'one of {info,decodedline,frames,frames-interp}'))
1053 options
, args
= optparser
.parse_args()
1055 if options
.help or len(args
) == 0:
1056 optparser
.print_help()
1059 if options
.show_all_headers
:
1060 do_file_header
= do_section_header
= do_program_header
= True
1062 do_file_header
= options
.show_file_header
1063 do_section_header
= options
.show_section_header
1064 do_program_header
= options
.show_program_header
1066 with
open(args
[0], 'rb') as file:
1068 readelf
= ReadElf(file, stream
or sys
.stdout
)
1070 readelf
.display_file_header()
1071 if do_section_header
:
1072 readelf
.display_section_headers(
1073 show_heading
=not do_file_header
)
1074 if do_program_header
:
1075 readelf
.display_program_headers(
1076 show_heading
=not do_file_header
)
1077 if options
.show_dynamic_tags
:
1078 readelf
.display_dynamic_tags()
1079 if options
.show_symbols
:
1080 readelf
.display_symbol_tables()
1081 if options
.show_relocs
:
1082 readelf
.display_relocations()
1083 if options
.show_version_info
:
1084 readelf
.display_version_info()
1085 if options
.show_hex_dump
:
1086 readelf
.display_hex_dump(options
.show_hex_dump
)
1087 if options
.show_string_dump
:
1088 readelf
.display_string_dump(options
.show_string_dump
)
1089 if options
.debug_dump_what
:
1090 readelf
.display_debug_dump(options
.debug_dump_what
)
1091 except ELFError
as ex
:
1092 sys
.stderr
.write('ELF error: %s\n' % ex
)
1097 # Run 'main' redirecting its output to readelfout.txt
1098 # Saves profiling information in readelf.profile
1099 PROFFILE
= 'readelf.profile'
1101 cProfile
.run('main(open("readelfout.txt", "w"))', PROFFILE
)
1103 # Dig in some profiling stats
1105 p
= pstats
.Stats(PROFFILE
)
1106 p
.sort_stats('cumulative').print_stats(25)
1109 #-------------------------------------------------------------------------------
1110 if __name__
== '__main__':