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
.elf
.constants
import E_FLAGS
43 from elftools
.dwarf
.dwarfinfo
import DWARFInfo
44 from elftools
.dwarf
.descriptions
import (
45 describe_reg_name
, describe_attr_value
, set_global_machine_arch
,
46 describe_CFI_instructions
, describe_CFI_register_rule
,
47 describe_CFI_CFA_rule
,
49 from elftools
.dwarf
.constants
import (
50 DW_LNS_copy
, DW_LNS_set_file
, DW_LNE_define_file
)
51 from elftools
.dwarf
.callframe
import CIE
, FDE
54 class ReadElf(object):
55 """ display_* methods are used to emit output into the output stream
57 def __init__(self
, file, output
):
59 stream object with the ELF file to read
62 output stream to write to
64 self
.elffile
= ELFFile(file)
67 # Lazily initialized if a debug dump is requested
68 self
._dwarfinfo
= None
70 self
._versioninfo
= None
72 def display_file_header(self
):
73 """ Display the ELF file header
75 self
._emitline
('ELF Header:')
76 self
._emit
(' Magic: ')
77 self
._emitline
(' '.join('%2.2x' % byte2int(b
)
78 for b
in self
.elffile
.e_ident_raw
))
79 header
= self
.elffile
.header
80 e_ident
= header
['e_ident']
81 self
._emitline
(' Class: %s' %
82 describe_ei_class(e_ident
['EI_CLASS']))
83 self
._emitline
(' Data: %s' %
84 describe_ei_data(e_ident
['EI_DATA']))
85 self
._emitline
(' Version: %s' %
86 describe_ei_version(e_ident
['EI_VERSION']))
87 self
._emitline
(' OS/ABI: %s' %
88 describe_ei_osabi(e_ident
['EI_OSABI']))
89 self
._emitline
(' ABI Version: %d' %
90 e_ident
['EI_ABIVERSION'])
91 self
._emitline
(' Type: %s' %
92 describe_e_type(header
['e_type']))
93 self
._emitline
(' Machine: %s' %
94 describe_e_machine(header
['e_machine']))
95 self
._emitline
(' Version: %s' %
96 describe_e_version_numeric(header
['e_version']))
97 self
._emitline
(' Entry point address: %s' %
98 self
._format
_hex
(header
['e_entry']))
99 self
._emit
(' Start of program headers: %s' %
101 self
._emitline
(' (bytes into file)')
102 self
._emit
(' Start of section headers: %s' %
104 self
._emitline
(' (bytes into file)')
105 self
._emitline
(' Flags: %s%s' %
106 (self
._format
_hex
(header
['e_flags']),
107 self
.decode_flags(header
['e_flags'])))
108 self
._emitline
(' Size of this header: %s (bytes)' %
110 self
._emitline
(' Size of program headers: %s (bytes)' %
111 header
['e_phentsize'])
112 self
._emitline
(' Number of program headers: %s' %
114 self
._emitline
(' Size of section headers: %s (bytes)' %
115 header
['e_shentsize'])
116 self
._emitline
(' Number of section headers: %s' %
118 self
._emitline
(' Section header string table index: %s' %
119 header
['e_shstrndx'])
121 def decode_flags(self
, flags
):
123 if self
.elffile
['e_machine'] == "EM_ARM":
124 if flags
& E_FLAGS
.EF_ARM_HASENTRY
:
125 description
+= ", has entry point"
127 version
= flags
& E_FLAGS
.EF_ARM_EABIMASK
128 if version
== E_FLAGS
.EF_ARM_EABI_VER5
:
129 description
+= ", Version5 EABI"
132 def display_program_headers(self
, show_heading
=True):
133 """ Display the ELF program headers.
134 If show_heading is True, displays the heading for this information
135 (Elf file type is...)
138 if self
.elffile
.num_segments() == 0:
139 self
._emitline
('There are no program headers in this file.')
142 elfheader
= self
.elffile
.header
144 self
._emitline
('Elf file type is %s' %
145 describe_e_type(elfheader
['e_type']))
146 self
._emitline
('Entry point is %s' %
147 self
._format
_hex
(elfheader
['e_entry']))
148 # readelf weirness - why isn't e_phoff printed as hex? (for section
150 self
._emitline
('There are %s program headers, starting at offset %s' % (
151 elfheader
['e_phnum'], elfheader
['e_phoff']))
154 self
._emitline
('Program Headers:')
156 # Now comes the table of program headers with their attributes. Note
157 # that due to different formatting constraints of 32-bit and 64-bit
158 # addresses, there are some conditions on elfclass here.
160 # First comes the table heading
162 if self
.elffile
.elfclass
== 32:
163 self
._emitline
(' Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align')
165 self
._emitline
(' Type Offset VirtAddr PhysAddr')
166 self
._emitline
(' FileSiz MemSiz Flags Align')
170 for segment
in self
.elffile
.iter_segments():
171 self
._emit
(' %-14s ' % describe_p_type(segment
['p_type']))
173 if self
.elffile
.elfclass
== 32:
174 self
._emitline
('%s %s %s %s %s %-3s %s' % (
175 self
._format
_hex
(segment
['p_offset'], fieldsize
=6),
176 self
._format
_hex
(segment
['p_vaddr'], fullhex
=True),
177 self
._format
_hex
(segment
['p_paddr'], fullhex
=True),
178 self
._format
_hex
(segment
['p_filesz'], fieldsize
=5),
179 self
._format
_hex
(segment
['p_memsz'], fieldsize
=5),
180 describe_p_flags(segment
['p_flags']),
181 self
._format
_hex
(segment
['p_align'])))
183 self
._emitline
('%s %s %s' % (
184 self
._format
_hex
(segment
['p_offset'], fullhex
=True),
185 self
._format
_hex
(segment
['p_vaddr'], fullhex
=True),
186 self
._format
_hex
(segment
['p_paddr'], fullhex
=True)))
187 self
._emitline
(' %s %s %-3s %s' % (
188 self
._format
_hex
(segment
['p_filesz'], fullhex
=True),
189 self
._format
_hex
(segment
['p_memsz'], fullhex
=True),
190 describe_p_flags(segment
['p_flags']),
191 # lead0x set to False for p_align, to mimic readelf.
192 # No idea why the difference from 32-bit mode :-|
193 self
._format
_hex
(segment
['p_align'], lead0x
=False)))
195 if isinstance(segment
, InterpSegment
):
196 self
._emitline
(' [Requesting program interpreter: %s]' %
197 bytes2str(segment
.get_interp_name()))
199 # Sections to segments mapping
201 if self
.elffile
.num_sections() == 0:
202 # No sections? We're done
205 self
._emitline
('\n Section to Segment mapping:')
206 self
._emitline
(' Segment Sections...')
208 for nseg
, segment
in enumerate(self
.elffile
.iter_segments()):
209 self
._emit
(' %2.2d ' % nseg
)
211 for section
in self
.elffile
.iter_sections():
212 if ( not section
.is_null() and
213 segment
.section_in_segment(section
)):
214 self
._emit
('%s ' % bytes2str(section
.name
))
218 def display_section_headers(self
, show_heading
=True):
219 """ Display the ELF section headers
221 elfheader
= self
.elffile
.header
223 self
._emitline
('There are %s section headers, starting at offset %s' % (
224 elfheader
['e_shnum'], self
._format
_hex
(elfheader
['e_shoff'])))
226 self
._emitline
('\nSection Header%s:' % (
227 's' if elfheader
['e_shnum'] > 1 else ''))
229 # Different formatting constraints of 32-bit and 64-bit addresses
231 if self
.elffile
.elfclass
== 32:
232 self
._emitline
(' [Nr] Name Type Addr Off Size ES Flg Lk Inf Al')
234 self
._emitline
(' [Nr] Name Type Address Offset')
235 self
._emitline
(' Size EntSize Flags Link Info Align')
239 for nsec
, section
in enumerate(self
.elffile
.iter_sections()):
240 self
._emit
(' [%2u] %-17.17s %-15.15s ' % (
241 nsec
, bytes2str(section
.name
), describe_sh_type(section
['sh_type'])))
243 if self
.elffile
.elfclass
== 32:
244 self
._emitline
('%s %s %s %s %3s %2s %3s %2s' % (
245 self
._format
_hex
(section
['sh_addr'], fieldsize
=8, lead0x
=False),
246 self
._format
_hex
(section
['sh_offset'], fieldsize
=6, lead0x
=False),
247 self
._format
_hex
(section
['sh_size'], fieldsize
=6, lead0x
=False),
248 self
._format
_hex
(section
['sh_entsize'], fieldsize
=2, lead0x
=False),
249 describe_sh_flags(section
['sh_flags']),
250 section
['sh_link'], section
['sh_info'],
251 section
['sh_addralign']))
253 self
._emitline
(' %s %s' % (
254 self
._format
_hex
(section
['sh_addr'], fullhex
=True, lead0x
=False),
255 self
._format
_hex
(section
['sh_offset'],
256 fieldsize
=16 if section
['sh_offset'] > 0xffffffff else 8,
258 self
._emitline
(' %s %s %3s %2s %3s %s' % (
259 self
._format
_hex
(section
['sh_size'], fullhex
=True, lead0x
=False),
260 self
._format
_hex
(section
['sh_entsize'], fullhex
=True, lead0x
=False),
261 describe_sh_flags(section
['sh_flags']),
262 section
['sh_link'], section
['sh_info'],
263 section
['sh_addralign']))
265 self
._emitline
('Key to Flags:')
266 self
._emit
(' W (write), A (alloc), X (execute), M (merge), S (strings)')
267 if self
.elffile
['e_machine'] in ('EM_X86_64', 'EM_L10M'):
268 self
._emitline
(', l (large)')
271 self
._emitline
(' I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)')
272 self
._emitline
(' O (extra OS processing required) o (OS specific), p (processor specific)')
274 def display_symbol_tables(self
):
275 """ Display the symbol tables contained in the file
277 self
._init
_versioninfo
()
279 for section
in self
.elffile
.iter_sections():
280 if not isinstance(section
, SymbolTableSection
):
283 if section
['sh_entsize'] == 0:
284 self
._emitline
("\nSymbol table '%s' has a sh_entsize of zero!" % (
285 bytes2str(section
.name
)))
288 self
._emitline
("\nSymbol table '%s' contains %s entries:" % (
289 bytes2str(section
.name
), section
.num_symbols()))
291 if self
.elffile
.elfclass
== 32:
292 self
._emitline
(' Num: Value Size Type Bind Vis Ndx Name')
294 self
._emitline
(' Num: Value Size Type Bind Vis Ndx Name')
296 for nsym
, symbol
in enumerate(section
.iter_symbols()):
299 # readelf doesn't display version info for Solaris versioning
300 if (section
['sh_type'] == 'SHT_DYNSYM' and
301 self
._versioninfo
['type'] == 'GNU'):
302 version
= self
._symbol
_version
(nsym
)
303 if (version
['name'] != bytes2str(symbol
.name
) and
304 version
['index'] not in ('VER_NDX_LOCAL',
306 if version
['filename']:
308 version_info
= '@%(name)s (%(index)i)' % version
311 if version
['hidden']:
312 version_info
= '@%(name)s' % version
314 version_info
= '@@%(name)s' % version
316 # symbol names are truncated to 25 chars, similarly to readelf
317 self
._emitline
('%6d: %s %5d %-7s %-6s %-7s %4s %.25s%s' % (
320 symbol
['st_value'], fullhex
=True, lead0x
=False),
322 describe_symbol_type(symbol
['st_info']['type']),
323 describe_symbol_bind(symbol
['st_info']['bind']),
324 describe_symbol_visibility(symbol
['st_other']['visibility']),
325 describe_symbol_shndx(symbol
['st_shndx']),
326 bytes2str(symbol
.name
),
329 def display_dynamic_tags(self
):
330 """ Display the dynamic tags contained in the file
332 has_dynamic_sections
= False
333 for section
in self
.elffile
.iter_sections():
334 if not isinstance(section
, DynamicSection
):
337 has_dynamic_sections
= True
338 self
._emitline
("\nDynamic section at offset %s contains %s entries:" % (
339 self
._format
_hex
(section
['sh_offset']),
341 self
._emitline
(" Tag Type Name/Value")
343 padding
= 20 + (8 if self
.elffile
.elfclass
== 32 else 0)
344 for tag
in section
.iter_tags():
345 if tag
.entry
.d_tag
== 'DT_NEEDED':
346 parsed
= 'Shared library: [%s]' % bytes2str(tag
.needed
)
347 elif tag
.entry
.d_tag
== 'DT_RPATH':
348 parsed
= 'Library rpath: [%s]' % bytes2str(tag
.rpath
)
349 elif tag
.entry
.d_tag
== 'DT_RUNPATH':
350 parsed
= 'Library runpath: [%s]' % bytes2str(tag
.runpath
)
351 elif tag
.entry
.d_tag
== 'DT_SONAME':
352 parsed
= 'Library soname: [%s]' % bytes2str(tag
.soname
)
353 elif (tag
.entry
.d_tag
.endswith('SZ') or
354 tag
.entry
.d_tag
.endswith('ENT')):
355 parsed
= '%i (bytes)' % tag
['d_val']
356 elif (tag
.entry
.d_tag
.endswith('NUM') or
357 tag
.entry
.d_tag
.endswith('COUNT')):
358 parsed
= '%i' % tag
['d_val']
359 elif tag
.entry
.d_tag
== 'DT_PLTREL':
360 s
= describe_dyn_tag(tag
.entry
.d_val
)
361 if s
.startswith('DT_'):
365 parsed
= '%#x' % tag
['d_val']
367 self
._emitline
(" %s %-*s %s" % (
368 self
._format
_hex
(ENUM_D_TAG
.get(tag
.entry
.d_tag
, tag
.entry
.d_tag
),
369 fullhex
=True, lead0x
=True),
371 '(%s)' % (tag
.entry
.d_tag
[3:],),
373 if not has_dynamic_sections
:
374 # readelf only prints this if there is at least one segment
375 if self
.elffile
.num_segments():
376 self
._emitline
("\nThere is no dynamic section in this file.")
378 def display_relocations(self
):
379 """ Display the relocations contained in the file
381 has_relocation_sections
= False
382 for section
in self
.elffile
.iter_sections():
383 if not isinstance(section
, RelocationSection
):
386 has_relocation_sections
= True
387 self
._emitline
("\nRelocation section '%s' at offset %s contains %s entries:" % (
388 bytes2str(section
.name
),
389 self
._format
_hex
(section
['sh_offset']),
390 section
.num_relocations()))
391 if section
.is_RELA():
392 self
._emitline
(" Offset Info Type Sym. Value Sym. Name + Addend")
394 self
._emitline
(" Offset Info Type Sym.Value Sym. Name")
396 # The symbol table section pointed to in sh_link
397 symtable
= self
.elffile
.get_section(section
['sh_link'])
399 for rel
in section
.iter_relocations():
400 hexwidth
= 8 if self
.elffile
.elfclass
== 32 else 12
401 self
._emit
('%s %s %-17.17s' % (
402 self
._format
_hex
(rel
['r_offset'],
403 fieldsize
=hexwidth
, lead0x
=False),
404 self
._format
_hex
(rel
['r_info'],
405 fieldsize
=hexwidth
, lead0x
=False),
407 rel
['r_info_type'], self
.elffile
)))
409 if rel
['r_info_sym'] == 0:
413 symbol
= symtable
.get_symbol(rel
['r_info_sym'])
414 # Some symbols have zero 'st_name', so instead what's used is
415 # the name of the section they point at
416 if symbol
['st_name'] == 0:
417 symsec
= self
.elffile
.get_section(symbol
['st_shndx'])
418 symbol_name
= symsec
.name
420 symbol_name
= symbol
.name
421 self
._emit
(' %s %s%22.22s' % (
424 fullhex
=True, lead0x
=False),
425 ' ' if self
.elffile
.elfclass
== 32 else '',
426 bytes2str(symbol_name
)))
427 if section
.is_RELA():
428 self
._emit
(' %s %x' % (
429 '+' if rel
['r_addend'] >= 0 else '-',
430 abs(rel
['r_addend'])))
433 if not has_relocation_sections
:
434 self
._emitline
('\nThere are no relocations in this file.')
436 def display_version_info(self
):
437 """ Display the version info contained in the file
439 self
._init
_versioninfo
()
441 if not self
._versioninfo
['type']:
442 self
._emitline
("\nNo version information found in this file.")
445 for section
in self
.elffile
.iter_sections():
446 if isinstance(section
, GNUVerSymSection
):
447 self
._print
_version
_section
_header
(
448 section
, 'Version symbols', lead0x
=False)
450 num_symbols
= section
.num_symbols()
452 # Symbol version info are printed four by four entries
453 for idx_by_4
in range(0, num_symbols
, 4):
455 self
._emit
(' %03x:' % idx_by_4
)
457 for idx
in range(idx_by_4
, min(idx_by_4
+ 4, num_symbols
)):
459 symbol_version
= self
._symbol
_version
(idx
)
460 if symbol_version
['index'] == 'VER_NDX_LOCAL':
462 version_name
= '(*local*)'
463 elif symbol_version
['index'] == 'VER_NDX_GLOBAL':
465 version_name
= '(*global*)'
467 version_index
= symbol_version
['index']
468 version_name
= '(%(name)s)' % symbol_version
470 visibility
= 'h' if symbol_version
['hidden'] else ' '
472 self
._emit
('%4x%s%-13s' % (
473 version_index
, visibility
, version_name
))
477 elif isinstance(section
, GNUVerDefSection
):
478 self
._print
_version
_section
_header
(
479 section
, 'Version definition', indent
=2)
482 for verdef
, verdaux_iter
in section
.iter_versions():
483 verdaux
= next(verdaux_iter
)
486 if verdef
['vd_flags']:
487 flags
= describe_ver_flags(verdef
['vd_flags'])
488 # Mimic exactly the readelf output
493 self
._emitline
(' %s: Rev: %i Flags: %s Index: %i'
494 ' Cnt: %i Name: %s' % (
495 self
._format
_hex
(offset
, fieldsize
=6,
497 verdef
['vd_version'], flags
, verdef
['vd_ndx'],
498 verdef
['vd_cnt'], bytes2str(name
)))
501 offset
+ verdef
['vd_aux'] + verdaux
['vda_next'])
502 for idx
, verdaux
in enumerate(verdaux_iter
, start
=1):
503 self
._emitline
(' %s: Parent %i: %s' %
504 (self
._format
_hex
(verdaux_offset
, fieldsize
=4),
505 idx
, bytes2str(verdaux
.name
)))
506 verdaux_offset
+= verdaux
['vda_next']
508 offset
+= verdef
['vd_next']
510 elif isinstance(section
, GNUVerNeedSection
):
511 self
._print
_version
_section
_header
(section
, 'Version needs')
514 for verneed
, verneed_iter
in section
.iter_versions():
516 self
._emitline
(' %s: Version: %i File: %s Cnt: %i' % (
517 self
._format
_hex
(offset
, fieldsize
=6,
519 verneed
['vn_version'], bytes2str(verneed
.name
),
522 vernaux_offset
= offset
+ verneed
['vn_aux']
523 for idx
, vernaux
in enumerate(verneed_iter
, start
=1):
524 if vernaux
['vna_flags']:
525 flags
= describe_ver_flags(vernaux
['vna_flags'])
526 # Mimic exactly the readelf output
532 ' %s: Name: %s Flags: %s Version: %i' % (
533 self
._format
_hex
(vernaux_offset
, fieldsize
=4),
534 bytes2str(vernaux
.name
), flags
,
535 vernaux
['vna_other']))
537 vernaux_offset
+= vernaux
['vna_next']
539 offset
+= verneed
['vn_next']
541 def display_hex_dump(self
, section_spec
):
542 """ Display a hex dump of a section. section_spec is either a section
545 section
= self
._section
_from
_spec
(section_spec
)
547 self
._emitline
("Section '%s' does not exist in the file!" % (
551 self
._emitline
("\nHex dump of section '%s':" % bytes2str(section
.name
))
552 self
._note
_relocs
_for
_section
(section
)
553 addr
= section
['sh_addr']
554 data
= section
.data()
557 while dataptr
< len(data
):
558 bytesleft
= len(data
) - dataptr
559 # chunks of 16 bytes per line
560 linebytes
= 16 if bytesleft
> 16 else bytesleft
562 self
._emit
(' %s ' % self
._format
_hex
(addr
, fieldsize
=8))
565 self
._emit
('%2.2x' % byte2int(data
[dataptr
+ i
]))
571 for i
in range(linebytes
):
572 c
= data
[dataptr
+ i
: dataptr
+ i
+ 1]
573 if byte2int(c
[0]) >= 32 and byte2int(c
[0]) < 0x7f:
574 self
._emit
(bytes2str(c
))
576 self
._emit
(bytes2str(b
'.'))
584 def display_string_dump(self
, section_spec
):
585 """ Display a strings dump of a section. section_spec is either a
586 section number or a name.
588 section
= self
._section
_from
_spec
(section_spec
)
590 self
._emitline
("Section '%s' does not exist in the file!" % (
594 self
._emitline
("\nString dump of section '%s':" % bytes2str(section
.name
))
597 data
= section
.data()
600 while dataptr
< len(data
):
601 while ( dataptr
< len(data
) and
602 not (32 <= byte2int(data
[dataptr
]) <= 127)):
605 if dataptr
>= len(data
):
609 while endptr
< len(data
) and byte2int(data
[endptr
]) != 0:
613 self
._emitline
(' [%6x] %s' % (
614 dataptr
, bytes2str(data
[dataptr
:endptr
])))
619 self
._emitline
(' No strings found in this section.')
623 def display_debug_dump(self
, dump_what
):
624 """ Dump a DWARF section
626 self
._init
_dwarfinfo
()
627 if self
._dwarfinfo
is None:
630 set_global_machine_arch(self
.elffile
.get_machine_arch())
632 if dump_what
== 'info':
633 self
._dump
_debug
_info
()
634 elif dump_what
== 'decodedline':
635 self
._dump
_debug
_line
_programs
()
636 elif dump_what
== 'frames':
637 self
._dump
_debug
_frames
()
638 elif dump_what
== 'frames-interp':
639 self
._dump
_debug
_frames
_interp
()
641 self
._emitline
('debug dump not yet supported for "%s"' % dump_what
)
643 def _format_hex(self
, addr
, fieldsize
=None, fullhex
=False, lead0x
=True,
645 """ Format an address into a hexadecimal string.
648 Size of the hexadecimal field (with leading zeros to fit the
649 address into. For example with fieldsize=8, the format will
651 If None, the minimal required field size will be used.
654 If True, override fieldsize to set it to the maximal size
655 needed for the elfclass
658 If True, leading 0x is added
661 If True, override lead0x to emulate the alternate
662 hexadecimal form specified in format string with the #
663 character: only non-zero values are prefixed with 0x.
664 This form is used by readelf.
673 s
= '0x' if lead0x
else ''
675 fieldsize
= 8 if self
.elffile
.elfclass
== 32 else 16
676 if fieldsize
is None:
679 field
= '%' + '0%sx' % fieldsize
680 return s
+ field
% addr
682 def _print_version_section_header(self
, version_section
, name
, lead0x
=True,
684 """ Print a section header of one version related section (versym,
685 verneed or verdef) with some options to accomodate readelf
686 little differences between each header (e.g. indentation
689 if hasattr(version_section
, 'num_versions'):
690 num_entries
= version_section
.num_versions()
692 num_entries
= version_section
.num_symbols()
694 self
._emitline
("\n%s section '%s' contains %s entries:" %
695 (name
, bytes2str(version_section
.name
), num_entries
))
696 self
._emitline
('%sAddr: %s Offset: %s Link: %i (%s)' % (
699 version_section
['sh_addr'], fieldsize
=16, lead0x
=lead0x
),
701 version_section
['sh_offset'], fieldsize
=6, lead0x
=True),
702 version_section
['sh_link'],
704 self
.elffile
.get_section(version_section
['sh_link']).name
)
708 def _init_versioninfo(self
):
709 """ Search and initialize informations about version related sections
710 and the kind of versioning used (GNU or Solaris).
712 if self
._versioninfo
is not None:
715 self
._versioninfo
= {'versym': None, 'verdef': None,
716 'verneed': None, 'type': None}
718 for section
in self
.elffile
.iter_sections():
719 if isinstance(section
, GNUVerSymSection
):
720 self
._versioninfo
['versym'] = section
721 elif isinstance(section
, GNUVerDefSection
):
722 self
._versioninfo
['verdef'] = section
723 elif isinstance(section
, GNUVerNeedSection
):
724 self
._versioninfo
['verneed'] = section
725 elif isinstance(section
, DynamicSection
):
726 for tag
in section
.iter_tags():
727 if tag
['d_tag'] == 'DT_VERSYM':
728 self
._versioninfo
['type'] = 'GNU'
731 if not self
._versioninfo
['type'] and (
732 self
._versioninfo
['verneed'] or self
._versioninfo
['verdef']):
733 self
._versioninfo
['type'] = 'Solaris'
735 def _symbol_version(self
, nsym
):
736 """ Return a dict containing information on the
737 or None if no version information is available
739 self
._init
_versioninfo
()
741 symbol_version
= dict.fromkeys(('index', 'name', 'filename', 'hidden'))
743 if (not self
._versioninfo
['versym'] or
744 nsym
>= self
._versioninfo
['versym'].num_symbols()):
747 symbol
= self
._versioninfo
['versym'].get_symbol(nsym
)
748 index
= symbol
.entry
['ndx']
749 if not index
in ('VER_NDX_LOCAL', 'VER_NDX_GLOBAL'):
752 if self
._versioninfo
['type'] == 'GNU':
753 # In GNU versioning mode, the highest bit is used to
754 # store wether the symbol is hidden or not
757 symbol_version
['hidden'] = True
759 if (self
._versioninfo
['verdef'] and
760 index
<= self
._versioninfo
['verdef'].num_versions()):
762 self
._versioninfo
['verdef'].get_version(index
)
763 symbol_version
['name'] = bytes2str(next(verdaux_iter
).name
)
766 self
._versioninfo
['verneed'].get_version(index
)
767 symbol_version
['name'] = bytes2str(vernaux
.name
)
768 symbol_version
['filename'] = bytes2str(verneed
.name
)
770 symbol_version
['index'] = index
771 return symbol_version
773 def _section_from_spec(self
, spec
):
774 """ Retrieve a section given a "spec" (either number or name).
775 Return None if no such section exists in the file.
779 if num
< self
.elffile
.num_sections():
780 return self
.elffile
.get_section(num
)
784 # Not a number. Must be a name then
785 return self
.elffile
.get_section_by_name(str2bytes(spec
))
787 def _note_relocs_for_section(self
, section
):
788 """ If there are relocation sections pointing to the givne section,
789 emit a note about it.
791 for relsec
in self
.elffile
.iter_sections():
792 if isinstance(relsec
, RelocationSection
):
793 info_idx
= relsec
['sh_info']
794 if self
.elffile
.get_section(info_idx
) == section
:
795 self
._emitline
(' Note: This section has relocations against it, but these have NOT been applied to this dump.')
798 def _init_dwarfinfo(self
):
799 """ Initialize the DWARF info contained in the file and assign it to
801 Leave self._dwarfinfo at None if no DWARF info was found in the file
803 if self
._dwarfinfo
is not None:
806 if self
.elffile
.has_dwarf_info():
807 self
._dwarfinfo
= self
.elffile
.get_dwarf_info()
809 self
._dwarfinfo
= None
811 def _dump_debug_info(self
):
812 """ Dump the debugging info section.
814 self
._emitline
('Contents of the .debug_info section:\n')
816 # Offset of the .debug_info section in the stream
817 section_offset
= self
._dwarfinfo
.debug_info_sec
.global_offset
819 for cu
in self
._dwarfinfo
.iter_CUs():
820 self
._emitline
(' Compilation Unit @ offset %s:' %
821 self
._format
_hex
(cu
.cu_offset
))
822 self
._emitline
(' Length: %s (%s)' % (
823 self
._format
_hex
(cu
['unit_length']),
824 '%s-bit' % cu
.dwarf_format()))
825 self
._emitline
(' Version: %s' % cu
['version']),
826 self
._emitline
(' Abbrev Offset: %s' % (
827 self
._format
_hex
(cu
['debug_abbrev_offset']))),
828 self
._emitline
(' Pointer Size: %s' % cu
['address_size'])
830 # The nesting depth of each DIE within the tree of DIEs must be
831 # displayed. To implement this, a counter is incremented each time
832 # the current DIE has children, and decremented when a null die is
833 # encountered. Due to the way the DIE tree is serialized, this will
834 # correctly reflect the nesting depth
837 for die
in cu
.iter_DIEs():
838 self
._emitline
(' <%s><%x>: Abbrev Number: %s%s' % (
842 (' (%s)' % die
.tag
) if not die
.is_null() else ''))
847 for attr
in itervalues(die
.attributes
):
849 # Unknown attribute values are passed-through as integers
850 if isinstance(name
, int):
851 name
= 'Unknown AT value: %x' % name
852 self
._emitline
(' <%2x> %-18s: %s' % (
856 attr
, die
, section_offset
)))
863 def _dump_debug_line_programs(self
):
864 """ Dump the (decoded) line programs from .debug_line
865 The programs are dumped in the order of the CUs they belong to.
867 self
._emitline
('Decoded dump of debug contents of section .debug_line:\n')
869 for cu
in self
._dwarfinfo
.iter_CUs():
870 lineprogram
= self
._dwarfinfo
.line_program_for_CU(cu
)
872 cu_filename
= bytes2str(lineprogram
['file_entry'][0].name
)
873 if len(lineprogram
['include_directory']) > 0:
874 dir_index
= lineprogram
['file_entry'][0].dir_index
876 dir = lineprogram
['include_directory'][dir_index
- 1]
879 cu_filename
= '%s/%s' % (bytes2str(dir), cu_filename
)
881 self
._emitline
('CU: %s:' % cu_filename
)
882 self
._emitline
('File name Line number Starting address')
884 # Print each state's file, line and address information. For some
885 # instructions other output is needed to be compatible with
887 for entry
in lineprogram
.get_entries():
890 # Special handling for commands that don't set a new state
891 if entry
.command
== DW_LNS_set_file
:
892 file_entry
= lineprogram
['file_entry'][entry
.args
[0] - 1]
893 if file_entry
.dir_index
== 0:
895 self
._emitline
('\n./%s:[++]' % (
896 bytes2str(file_entry
.name
)))
898 self
._emitline
('\n%s/%s:' % (
899 bytes2str(lineprogram
['include_directory'][file_entry
.dir_index
- 1]),
900 bytes2str(file_entry
.name
)))
901 elif entry
.command
== DW_LNE_define_file
:
902 self
._emitline
('%s:' % (
903 bytes2str(lineprogram
['include_directory'][entry
.args
[0].dir_index
])))
904 elif not state
.end_sequence
:
905 # readelf doesn't print the state after end_sequence
906 # instructions. I think it's a bug but to be compatible
907 # I don't print them too.
908 self
._emitline
('%-35s %11d %18s' % (
909 bytes2str(lineprogram
['file_entry'][state
.file - 1].name
),
911 '0' if state
.address
== 0 else
912 self
._format
_hex
(state
.address
)))
913 if entry
.command
== DW_LNS_copy
:
914 # Another readelf oddity...
917 def _dump_debug_frames(self
):
918 """ Dump the raw frame information from .debug_frame
920 if not self
._dwarfinfo
.has_CFI():
922 self
._emitline
('Contents of the .debug_frame section:')
924 for entry
in self
._dwarfinfo
.CFI_entries():
925 if isinstance(entry
, CIE
):
926 self
._emitline
('\n%08x %s %s CIE' % (
928 self
._format
_hex
(entry
['length'], fullhex
=True, lead0x
=False),
929 self
._format
_hex
(entry
['CIE_id'], fullhex
=True, lead0x
=False)))
930 self
._emitline
(' Version: %d' % entry
['version'])
931 self
._emitline
(' Augmentation: "%s"' % bytes2str(entry
['augmentation']))
932 self
._emitline
(' Code alignment factor: %u' % entry
['code_alignment_factor'])
933 self
._emitline
(' Data alignment factor: %d' % entry
['data_alignment_factor'])
934 self
._emitline
(' Return address column: %d' % entry
['return_address_register'])
937 self
._emitline
('\n%08x %s %s FDE cie=%08x pc=%s..%s' % (
939 self
._format
_hex
(entry
['length'], fullhex
=True, lead0x
=False),
940 self
._format
_hex
(entry
['CIE_pointer'], fullhex
=True, lead0x
=False),
942 self
._format
_hex
(entry
['initial_location'], fullhex
=True, lead0x
=False),
944 entry
['initial_location'] + entry
['address_range'],
945 fullhex
=True, lead0x
=False)))
947 self
._emit
(describe_CFI_instructions(entry
))
950 def _dump_debug_frames_interp(self
):
951 """ Dump the interpreted (decoded) frame information from .debug_frame
953 if not self
._dwarfinfo
.has_CFI():
956 self
._emitline
('Contents of the .debug_frame section:')
958 for entry
in self
._dwarfinfo
.CFI_entries():
959 if isinstance(entry
, CIE
):
960 self
._emitline
('\n%08x %s %s CIE "%s" cf=%d df=%d ra=%d' % (
962 self
._format
_hex
(entry
['length'], fullhex
=True, lead0x
=False),
963 self
._format
_hex
(entry
['CIE_id'], fullhex
=True, lead0x
=False),
964 bytes2str(entry
['augmentation']),
965 entry
['code_alignment_factor'],
966 entry
['data_alignment_factor'],
967 entry
['return_address_register']))
968 ra_regnum
= entry
['return_address_register']
970 self
._emitline
('\n%08x %s %s FDE cie=%08x pc=%s..%s' % (
972 self
._format
_hex
(entry
['length'], fullhex
=True, lead0x
=False),
973 self
._format
_hex
(entry
['CIE_pointer'], fullhex
=True, lead0x
=False),
975 self
._format
_hex
(entry
['initial_location'], fullhex
=True, lead0x
=False),
976 self
._format
_hex
(entry
['initial_location'] + entry
['address_range'],
977 fullhex
=True, lead0x
=False)))
978 ra_regnum
= entry
.cie
['return_address_register']
980 # Print the heading row for the decoded table
982 self
._emit
(' ' if entry
.structs
.address_size
== 4 else ' ')
985 # Decode the table nad look at the registers it describes.
986 # We build reg_order here to match readelf's order. In particular,
987 # registers are sorted by their number, and the register matching
988 # ra_regnum is always listed last with a special heading.
989 decoded_table
= entry
.get_decoded()
990 reg_order
= sorted(ifilter(
991 lambda r
: r
!= ra_regnum
,
992 decoded_table
.reg_order
))
994 # Headings for the registers
995 for regnum
in reg_order
:
996 self
._emit
('%-6s' % describe_reg_name(regnum
))
997 self
._emitline
('ra ')
999 # Now include ra_regnum in reg_order to print its values similarly
1000 # to the other registers.
1001 reg_order
.append(ra_regnum
)
1002 for line
in decoded_table
.table
:
1003 self
._emit
(self
._format
_hex
(
1004 line
['pc'], fullhex
=True, lead0x
=False))
1005 self
._emit
(' %-9s' % describe_CFI_CFA_rule(line
['cfa']))
1007 for regnum
in reg_order
:
1009 s
= describe_CFI_register_rule(line
[regnum
])
1012 self
._emit
('%-6s' % s
)
1016 def _emit(self
, s
=''):
1017 """ Emit an object to output
1019 self
.output
.write(str(s
))
1021 def _emitline(self
, s
=''):
1022 """ Emit an object to output, followed by a newline
1024 self
.output
.write(str(s
) + '\n')
1027 SCRIPT_DESCRIPTION
= 'Display information about the contents of ELF format files'
1028 VERSION_STRING
= '%%prog: based on pyelftools %s' % __version__
1031 def main(stream
=None):
1032 # parse the command-line arguments and invoke ReadElf
1033 optparser
= OptionParser(
1034 usage
='usage: %prog [options] <elf-file>',
1035 description
=SCRIPT_DESCRIPTION
,
1036 add_help_option
=False, # -h is a real option of readelf
1038 version
=VERSION_STRING
)
1039 optparser
.add_option('-d', '--dynamic',
1040 action
='store_true', dest
='show_dynamic_tags',
1041 help='Display the dynamic section')
1042 optparser
.add_option('-H', '--help',
1043 action
='store_true', dest
='help',
1044 help='Display this information')
1045 optparser
.add_option('-h', '--file-header',
1046 action
='store_true', dest
='show_file_header',
1047 help='Display the ELF file header')
1048 optparser
.add_option('-l', '--program-headers', '--segments',
1049 action
='store_true', dest
='show_program_header',
1050 help='Display the program headers')
1051 optparser
.add_option('-S', '--section-headers', '--sections',
1052 action
='store_true', dest
='show_section_header',
1053 help="Display the sections' headers")
1054 optparser
.add_option('-e', '--headers',
1055 action
='store_true', dest
='show_all_headers',
1056 help='Equivalent to: -h -l -S')
1057 optparser
.add_option('-s', '--symbols', '--syms',
1058 action
='store_true', dest
='show_symbols',
1059 help='Display the symbol table')
1060 optparser
.add_option('-r', '--relocs',
1061 action
='store_true', dest
='show_relocs',
1062 help='Display the relocations (if present)')
1063 optparser
.add_option('-x', '--hex-dump',
1064 action
='store', dest
='show_hex_dump', metavar
='<number|name>',
1065 help='Dump the contents of section <number|name> as bytes')
1066 optparser
.add_option('-p', '--string-dump',
1067 action
='store', dest
='show_string_dump', metavar
='<number|name>',
1068 help='Dump the contents of section <number|name> as strings')
1069 optparser
.add_option('-V', '--version-info',
1070 action
='store_true', dest
='show_version_info',
1071 help='Display the version sections (if present)')
1072 optparser
.add_option('--debug-dump',
1073 action
='store', dest
='debug_dump_what', metavar
='<what>',
1075 'Display the contents of DWARF debug sections. <what> can ' +
1076 'one of {info,decodedline,frames,frames-interp}'))
1078 options
, args
= optparser
.parse_args()
1080 if options
.help or len(args
) == 0:
1081 optparser
.print_help()
1084 if options
.show_all_headers
:
1085 do_file_header
= do_section_header
= do_program_header
= True
1087 do_file_header
= options
.show_file_header
1088 do_section_header
= options
.show_section_header
1089 do_program_header
= options
.show_program_header
1091 with
open(args
[0], 'rb') as file:
1093 readelf
= ReadElf(file, stream
or sys
.stdout
)
1095 readelf
.display_file_header()
1096 if do_section_header
:
1097 readelf
.display_section_headers(
1098 show_heading
=not do_file_header
)
1099 if do_program_header
:
1100 readelf
.display_program_headers(
1101 show_heading
=not do_file_header
)
1102 if options
.show_dynamic_tags
:
1103 readelf
.display_dynamic_tags()
1104 if options
.show_symbols
:
1105 readelf
.display_symbol_tables()
1106 if options
.show_relocs
:
1107 readelf
.display_relocations()
1108 if options
.show_version_info
:
1109 readelf
.display_version_info()
1110 if options
.show_hex_dump
:
1111 readelf
.display_hex_dump(options
.show_hex_dump
)
1112 if options
.show_string_dump
:
1113 readelf
.display_string_dump(options
.show_string_dump
)
1114 if options
.debug_dump_what
:
1115 readelf
.display_debug_dump(options
.debug_dump_what
)
1116 except ELFError
as ex
:
1117 sys
.stderr
.write('ELF error: %s\n' % ex
)
1122 # Run 'main' redirecting its output to readelfout.txt
1123 # Saves profiling information in readelf.profile
1124 PROFFILE
= 'readelf.profile'
1126 cProfile
.run('main(open("readelfout.txt", "w"))', PROFFILE
)
1128 # Dig in some profiling stats
1130 p
= pstats
.Stats(PROFFILE
)
1131 p
.sort_stats('cumulative').print_stats(25)
1134 #-------------------------------------------------------------------------------
1135 if __name__
== '__main__':