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 # If elftools is not installed, maybe we're running from the root or scripts
15 # dir of the source distribution
19 sys
.path
.extend(['.', '..'])
21 from elftools
import __version__
22 from elftools
.common
.exceptions
import ELFError
23 from elftools
.common
.py3compat
import (
24 ifilter
, byte2int
, bytes2str
, itervalues
, str2bytes
)
25 from elftools
.elf
.elffile
import ELFFile
26 from elftools
.elf
.dynamic
import DynamicSection
, DynamicSegment
27 from elftools
.elf
.enums
import ENUM_D_TAG
28 from elftools
.elf
.segments
import InterpSegment
29 from elftools
.elf
.sections
import SymbolTableSection
30 from elftools
.elf
.relocation
import RelocationSection
31 from elftools
.elf
.descriptions
import (
32 describe_ei_class
, describe_ei_data
, describe_ei_version
,
33 describe_ei_osabi
, describe_e_type
, describe_e_machine
,
34 describe_e_version_numeric
, describe_p_type
, describe_p_flags
,
35 describe_sh_type
, describe_sh_flags
,
36 describe_symbol_type
, describe_symbol_bind
, describe_symbol_visibility
,
37 describe_symbol_shndx
, describe_reloc_type
, describe_dyn_tag
,
39 from elftools
.dwarf
.dwarfinfo
import DWARFInfo
40 from elftools
.dwarf
.descriptions
import (
41 describe_reg_name
, describe_attr_value
, set_global_machine_arch
,
42 describe_CFI_instructions
, describe_CFI_register_rule
,
43 describe_CFI_CFA_rule
,
45 from elftools
.dwarf
.constants
import (
46 DW_LNS_copy
, DW_LNS_set_file
, DW_LNE_define_file
)
47 from elftools
.dwarf
.callframe
import CIE
, FDE
50 class ReadElf(object):
51 """ display_* methods are used to emit output into the output stream
53 def __init__(self
, file, output
):
55 stream object with the ELF file to read
58 output stream to write to
60 self
.elffile
= ELFFile(file)
63 # Lazily initialized if a debug dump is requested
64 self
._dwarfinfo
= None
66 def display_file_header(self
):
67 """ Display the ELF file header
69 self
._emitline
('ELF Header:')
70 self
._emit
(' Magic: ')
71 self
._emitline
(' '.join('%2.2x' % byte2int(b
)
72 for b
in self
.elffile
.e_ident_raw
))
73 header
= self
.elffile
.header
74 e_ident
= header
['e_ident']
75 self
._emitline
(' Class: %s' %
76 describe_ei_class(e_ident
['EI_CLASS']))
77 self
._emitline
(' Data: %s' %
78 describe_ei_data(e_ident
['EI_DATA']))
79 self
._emitline
(' Version: %s' %
80 describe_ei_version(e_ident
['EI_VERSION']))
81 self
._emitline
(' OS/ABI: %s' %
82 describe_ei_osabi(e_ident
['EI_OSABI']))
83 self
._emitline
(' ABI Version: %d' %
84 e_ident
['EI_ABIVERSION'])
85 self
._emitline
(' Type: %s' %
86 describe_e_type(header
['e_type']))
87 self
._emitline
(' Machine: %s' %
88 describe_e_machine(header
['e_machine']))
89 self
._emitline
(' Version: %s' %
90 describe_e_version_numeric(header
['e_version']))
91 self
._emitline
(' Entry point address: %s' %
92 self
._format
_hex
(header
['e_entry']))
93 self
._emit
(' Start of program headers: %s' %
95 self
._emitline
(' (bytes into file)')
96 self
._emit
(' Start of section headers: %s' %
98 self
._emitline
(' (bytes into file)')
99 self
._emitline
(' Flags: %s' %
100 self
._format
_hex
(header
['e_flags']))
101 self
._emitline
(' Size of this header: %s (bytes)' %
103 self
._emitline
(' Size of program headers: %s (bytes)' %
104 header
['e_phentsize'])
105 self
._emitline
(' Number of program headers: %s' %
107 self
._emitline
(' Size of section headers: %s (bytes)' %
108 header
['e_shentsize'])
109 self
._emitline
(' Number of section headers: %s' %
111 self
._emitline
(' Section header string table index: %s' %
112 header
['e_shstrndx'])
114 def display_program_headers(self
, show_heading
=True):
115 """ Display the ELF program headers.
116 If show_heading is True, displays the heading for this information
117 (Elf file type is...)
120 if self
.elffile
.num_segments() == 0:
121 self
._emitline
('There are no program headers in this file.')
124 elfheader
= self
.elffile
.header
126 self
._emitline
('Elf file type is %s' %
127 describe_e_type(elfheader
['e_type']))
128 self
._emitline
('Entry point is %s' %
129 self
._format
_hex
(elfheader
['e_entry']))
130 # readelf weirness - why isn't e_phoff printed as hex? (for section
132 self
._emitline
('There are %s program headers, starting at offset %s' % (
133 elfheader
['e_phnum'], elfheader
['e_phoff']))
136 self
._emitline
('Program Headers:')
138 # Now comes the table of program headers with their attributes. Note
139 # that due to different formatting constraints of 32-bit and 64-bit
140 # addresses, there are some conditions on elfclass here.
142 # First comes the table heading
144 if self
.elffile
.elfclass
== 32:
145 self
._emitline
(' Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align')
147 self
._emitline
(' Type Offset VirtAddr PhysAddr')
148 self
._emitline
(' FileSiz MemSiz Flags Align')
152 for segment
in self
.elffile
.iter_segments():
153 self
._emit
(' %-14s ' % describe_p_type(segment
['p_type']))
155 if self
.elffile
.elfclass
== 32:
156 self
._emitline
('%s %s %s %s %s %-3s %s' % (
157 self
._format
_hex
(segment
['p_offset'], fieldsize
=6),
158 self
._format
_hex
(segment
['p_vaddr'], fullhex
=True),
159 self
._format
_hex
(segment
['p_paddr'], fullhex
=True),
160 self
._format
_hex
(segment
['p_filesz'], fieldsize
=5),
161 self
._format
_hex
(segment
['p_memsz'], fieldsize
=5),
162 describe_p_flags(segment
['p_flags']),
163 self
._format
_hex
(segment
['p_align'])))
165 self
._emitline
('%s %s %s' % (
166 self
._format
_hex
(segment
['p_offset'], fullhex
=True),
167 self
._format
_hex
(segment
['p_vaddr'], fullhex
=True),
168 self
._format
_hex
(segment
['p_paddr'], fullhex
=True)))
169 self
._emitline
(' %s %s %-3s %s' % (
170 self
._format
_hex
(segment
['p_filesz'], fullhex
=True),
171 self
._format
_hex
(segment
['p_memsz'], fullhex
=True),
172 describe_p_flags(segment
['p_flags']),
173 # lead0x set to False for p_align, to mimic readelf.
174 # No idea why the difference from 32-bit mode :-|
175 self
._format
_hex
(segment
['p_align'], lead0x
=False)))
177 if isinstance(segment
, InterpSegment
):
178 self
._emitline
(' [Requesting program interpreter: %s]' %
179 bytes2str(segment
.get_interp_name()))
181 # Sections to segments mapping
183 if self
.elffile
.num_sections() == 0:
184 # No sections? We're done
187 self
._emitline
('\n Section to Segment mapping:')
188 self
._emitline
(' Segment Sections...')
190 for nseg
, segment
in enumerate(self
.elffile
.iter_segments()):
191 self
._emit
(' %2.2d ' % nseg
)
193 for section
in self
.elffile
.iter_sections():
194 if ( not section
.is_null() and
195 segment
.section_in_segment(section
)):
196 self
._emit
('%s ' % bytes2str(section
.name
))
200 def display_section_headers(self
, show_heading
=True):
201 """ Display the ELF section headers
203 elfheader
= self
.elffile
.header
205 self
._emitline
('There are %s section headers, starting at offset %s' % (
206 elfheader
['e_shnum'], self
._format
_hex
(elfheader
['e_shoff'])))
208 self
._emitline
('\nSection Header%s:' % (
209 's' if elfheader
['e_shnum'] > 1 else ''))
211 # Different formatting constraints of 32-bit and 64-bit addresses
213 if self
.elffile
.elfclass
== 32:
214 self
._emitline
(' [Nr] Name Type Addr Off Size ES Flg Lk Inf Al')
216 self
._emitline
(' [Nr] Name Type Address Offset')
217 self
._emitline
(' Size EntSize Flags Link Info Align')
221 for nsec
, section
in enumerate(self
.elffile
.iter_sections()):
222 self
._emit
(' [%2u] %-17.17s %-15.15s ' % (
223 nsec
, bytes2str(section
.name
), describe_sh_type(section
['sh_type'])))
225 if self
.elffile
.elfclass
== 32:
226 self
._emitline
('%s %s %s %s %3s %2s %3s %2s' % (
227 self
._format
_hex
(section
['sh_addr'], fieldsize
=8, lead0x
=False),
228 self
._format
_hex
(section
['sh_offset'], fieldsize
=6, lead0x
=False),
229 self
._format
_hex
(section
['sh_size'], fieldsize
=6, lead0x
=False),
230 self
._format
_hex
(section
['sh_entsize'], fieldsize
=2, lead0x
=False),
231 describe_sh_flags(section
['sh_flags']),
232 section
['sh_link'], section
['sh_info'],
233 section
['sh_addralign']))
235 self
._emitline
(' %s %s' % (
236 self
._format
_hex
(section
['sh_addr'], fullhex
=True, lead0x
=False),
237 self
._format
_hex
(section
['sh_offset'],
238 fieldsize
=16 if section
['sh_offset'] > 0xffffffff else 8,
240 self
._emitline
(' %s %s %3s %2s %3s %s' % (
241 self
._format
_hex
(section
['sh_size'], fullhex
=True, lead0x
=False),
242 self
._format
_hex
(section
['sh_entsize'], fullhex
=True, lead0x
=False),
243 describe_sh_flags(section
['sh_flags']),
244 section
['sh_link'], section
['sh_info'],
245 section
['sh_addralign']))
247 self
._emitline
('Key to Flags:')
248 self
._emit
(' W (write), A (alloc), X (execute), M (merge), S (strings)')
249 if self
.elffile
['e_machine'] in ('EM_X86_64', 'EM_L10M'):
250 self
._emitline
(', l (large)')
253 self
._emitline
(' I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)')
254 self
._emitline
(' O (extra OS processing required) o (OS specific), p (processor specific)')
256 def display_symbol_tables(self
):
257 """ Display the symbol tables contained in the file
259 for section
in self
.elffile
.iter_sections():
260 if not isinstance(section
, SymbolTableSection
):
263 if section
['sh_entsize'] == 0:
264 self
._emitline
("\nSymbol table '%s' has a sh_entsize of zero!" % (
265 bytes2str(section
.name
)))
268 self
._emitline
("\nSymbol table '%s' contains %s entries:" % (
269 bytes2str(section
.name
), section
.num_symbols()))
271 if self
.elffile
.elfclass
== 32:
272 self
._emitline
(' Num: Value Size Type Bind Vis Ndx Name')
274 self
._emitline
(' Num: Value Size Type Bind Vis Ndx Name')
276 for nsym
, symbol
in enumerate(section
.iter_symbols()):
277 # symbol names are truncated to 25 chars, similarly to readelf
278 self
._emitline
('%6d: %s %5d %-7s %-6s %-7s %4s %.25s' % (
280 self
._format
_hex
(symbol
['st_value'], fullhex
=True, lead0x
=False),
282 describe_symbol_type(symbol
['st_info']['type']),
283 describe_symbol_bind(symbol
['st_info']['bind']),
284 describe_symbol_visibility(symbol
['st_other']['visibility']),
285 describe_symbol_shndx(symbol
['st_shndx']),
286 bytes2str(symbol
.name
)))
288 def display_dynamic_tags(self
):
289 """ Display the dynamic tags contained in the file
291 for section
in self
.elffile
.iter_sections():
292 if not isinstance(section
, DynamicSection
):
295 self
._emitline
("\nDynamic section at offset %s contains %s entries:" % (
296 self
._format
_hex
(section
['sh_offset']),
298 self
._emitline
(" Tag Type Name/Value")
300 padding
= 20 + (8 if self
.elffile
.elfclass
== 32 else 0)
301 for tag
in section
.iter_tags():
302 if tag
.entry
.d_tag
== 'DT_NEEDED':
303 parsed
= 'Shared library: [%s]' % tag
.needed
304 elif tag
.entry
.d_tag
== 'DT_RPATH':
305 parsed
= 'Library rpath: [%s]' % tag
.rpath
306 elif tag
.entry
.d_tag
== 'DT_RUNPATH':
307 parsed
= 'Library runpath: [%s]' % tag
.runpath
308 elif tag
.entry
.d_tag
== 'DT_SONAME':
309 parsed
= 'Library soname: [%s]' % tag
.soname
310 elif (tag
.entry
.d_tag
.endswith('SZ') or
311 tag
.entry
.d_tag
.endswith('ENT')):
312 parsed
= '%i (bytes)' % tag
['d_val']
313 elif tag
.entry
.d_tag
.endswith('NUM'):
314 parsed
= '%i' % tag
['d_val']
315 elif tag
.entry
.d_tag
== 'DT_PLTREL':
316 s
= describe_dyn_tag(tag
.entry
.d_val
)
317 if s
.startswith('DT_'):
321 parsed
= '%#x' % tag
['d_val']
323 self
._emitline
(" %s %-*s %s" % (
324 self
._format
_hex
(ENUM_D_TAG
.get(tag
.entry
.d_tag
, tag
.entry
.d_tag
),
325 fullhex
=True, lead0x
=True),
327 '(%s)' % (tag
.entry
.d_tag
[3:],),
330 def display_relocations(self
):
331 """ Display the relocations contained in the file
333 has_relocation_sections
= False
334 for section
in self
.elffile
.iter_sections():
335 if not isinstance(section
, RelocationSection
):
338 has_relocation_sections
= True
339 self
._emitline
("\nRelocation section '%s' at offset %s contains %s entries:" % (
340 bytes2str(section
.name
),
341 self
._format
_hex
(section
['sh_offset']),
342 section
.num_relocations()))
343 if section
.is_RELA():
344 self
._emitline
(" Offset Info Type Sym. Value Sym. Name + Addend")
346 self
._emitline
(" Offset Info Type Sym.Value Sym. Name")
348 # The symbol table section pointed to in sh_link
349 symtable
= self
.elffile
.get_section(section
['sh_link'])
351 for rel
in section
.iter_relocations():
352 hexwidth
= 8 if self
.elffile
.elfclass
== 32 else 12
353 self
._emit
('%s %s %-17.17s' % (
354 self
._format
_hex
(rel
['r_offset'],
355 fieldsize
=hexwidth
, lead0x
=False),
356 self
._format
_hex
(rel
['r_info'],
357 fieldsize
=hexwidth
, lead0x
=False),
359 rel
['r_info_type'], self
.elffile
)))
361 if rel
['r_info_sym'] == 0:
365 symbol
= symtable
.get_symbol(rel
['r_info_sym'])
366 # Some symbols have zero 'st_name', so instead what's used is
367 # the name of the section they point at
368 if symbol
['st_name'] == 0:
369 symsec
= self
.elffile
.get_section(symbol
['st_shndx'])
370 symbol_name
= symsec
.name
372 symbol_name
= symbol
.name
373 self
._emit
(' %s %s%22.22s' % (
376 fullhex
=True, lead0x
=False),
377 ' ' if self
.elffile
.elfclass
== 32 else '',
378 bytes2str(symbol_name
)))
379 if section
.is_RELA():
380 self
._emit
(' %s %x' % (
381 '+' if rel
['r_addend'] >= 0 else '-',
382 abs(rel
['r_addend'])))
385 if not has_relocation_sections
:
386 self
._emitline
('\nThere are no relocations in this file.')
388 def display_hex_dump(self
, section_spec
):
389 """ Display a hex dump of a section. section_spec is either a section
392 section
= self
._section
_from
_spec
(section_spec
)
394 self
._emitline
("Section '%s' does not exist in the file!" % (
398 self
._emitline
("\nHex dump of section '%s':" % bytes2str(section
.name
))
399 self
._note
_relocs
_for
_section
(section
)
400 addr
= section
['sh_addr']
401 data
= section
.data()
404 while dataptr
< len(data
):
405 bytesleft
= len(data
) - dataptr
406 # chunks of 16 bytes per line
407 linebytes
= 16 if bytesleft
> 16 else bytesleft
409 self
._emit
(' %s ' % self
._format
_hex
(addr
, fieldsize
=8))
412 self
._emit
('%2.2x' % byte2int(data
[dataptr
+ i
]))
418 for i
in range(linebytes
):
419 c
= data
[dataptr
+ i
: dataptr
+ i
+ 1]
420 if byte2int(c
[0]) >= 32 and byte2int(c
[0]) < 0x7f:
421 self
._emit
(bytes2str(c
))
423 self
._emit
(bytes2str(b
'.'))
431 def display_string_dump(self
, section_spec
):
432 """ Display a strings dump of a section. section_spec is either a
433 section number or a name.
435 section
= self
._section
_from
_spec
(section_spec
)
437 self
._emitline
("Section '%s' does not exist in the file!" % (
441 self
._emitline
("\nString dump of section '%s':" % bytes2str(section
.name
))
444 data
= section
.data()
447 while dataptr
< len(data
):
448 while ( dataptr
< len(data
) and
449 not (32 <= byte2int(data
[dataptr
]) <= 127)):
452 if dataptr
>= len(data
):
456 while endptr
< len(data
) and byte2int(data
[endptr
]) != 0:
460 self
._emitline
(' [%6x] %s' % (
461 dataptr
, bytes2str(data
[dataptr
:endptr
])))
466 self
._emitline
(' No strings found in this section.')
470 def display_debug_dump(self
, dump_what
):
471 """ Dump a DWARF section
473 self
._init
_dwarfinfo
()
474 if self
._dwarfinfo
is None:
477 set_global_machine_arch(self
.elffile
.get_machine_arch())
479 if dump_what
== 'info':
480 self
._dump
_debug
_info
()
481 elif dump_what
== 'decodedline':
482 self
._dump
_debug
_line
_programs
()
483 elif dump_what
== 'frames':
484 self
._dump
_debug
_frames
()
485 elif dump_what
== 'frames-interp':
486 self
._dump
_debug
_frames
_interp
()
488 self
._emitline
('debug dump not yet supported for "%s"' % dump_what
)
490 def _format_hex(self
, addr
, fieldsize
=None, fullhex
=False, lead0x
=True):
491 """ Format an address into a hexadecimal string.
494 Size of the hexadecimal field (with leading zeros to fit the
495 address into. For example with fieldsize=8, the format will
497 If None, the minimal required field size will be used.
500 If True, override fieldsize to set it to the maximal size
501 needed for the elfclass
504 If True, leading 0x is added
506 s
= '0x' if lead0x
else ''
508 fieldsize
= 8 if self
.elffile
.elfclass
== 32 else 16
509 if fieldsize
is None:
512 field
= '%' + '0%sx' % fieldsize
513 return s
+ field
% addr
515 def _section_from_spec(self
, spec
):
516 """ Retrieve a section given a "spec" (either number or name).
517 Return None if no such section exists in the file.
521 if num
< self
.elffile
.num_sections():
522 return self
.elffile
.get_section(num
)
526 # Not a number. Must be a name then
527 return self
.elffile
.get_section_by_name(str2bytes(spec
))
529 def _note_relocs_for_section(self
, section
):
530 """ If there are relocation sections pointing to the givne section,
531 emit a note about it.
533 for relsec
in self
.elffile
.iter_sections():
534 if isinstance(relsec
, RelocationSection
):
535 info_idx
= relsec
['sh_info']
536 if self
.elffile
.get_section(info_idx
) == section
:
537 self
._emitline
(' Note: This section has relocations against it, but these have NOT been applied to this dump.')
540 def _init_dwarfinfo(self
):
541 """ Initialize the DWARF info contained in the file and assign it to
543 Leave self._dwarfinfo at None if no DWARF info was found in the file
545 if self
._dwarfinfo
is not None:
548 if self
.elffile
.has_dwarf_info():
549 self
._dwarfinfo
= self
.elffile
.get_dwarf_info()
551 self
._dwarfinfo
= None
553 def _dump_debug_info(self
):
554 """ Dump the debugging info section.
556 self
._emitline
('Contents of the .debug_info section:\n')
558 # Offset of the .debug_info section in the stream
559 section_offset
= self
._dwarfinfo
.debug_info_sec
.global_offset
561 for cu
in self
._dwarfinfo
.iter_CUs():
562 self
._emitline
(' Compilation Unit @ offset %s:' %
563 self
._format
_hex
(cu
.cu_offset
))
564 self
._emitline
(' Length: %s (%s)' % (
565 self
._format
_hex
(cu
['unit_length']),
566 '%s-bit' % cu
.dwarf_format()))
567 self
._emitline
(' Version: %s' % cu
['version']),
568 self
._emitline
(' Abbrev Offset: %s' % (
569 self
._format
_hex
(cu
['debug_abbrev_offset']))),
570 self
._emitline
(' Pointer Size: %s' % cu
['address_size'])
572 # The nesting depth of each DIE within the tree of DIEs must be
573 # displayed. To implement this, a counter is incremented each time
574 # the current DIE has children, and decremented when a null die is
575 # encountered. Due to the way the DIE tree is serialized, this will
576 # correctly reflect the nesting depth
579 for die
in cu
.iter_DIEs():
580 self
._emitline
(' <%s><%x>: Abbrev Number: %s%s' % (
584 (' (%s)' % die
.tag
) if not die
.is_null() else ''))
589 for attr
in itervalues(die
.attributes
):
591 # Unknown attribute values are passed-through as integers
592 if isinstance(name
, int):
593 name
= 'Unknown AT value: %x' % name
594 self
._emitline
(' <%2x> %-18s: %s' % (
598 attr
, die
, section_offset
)))
605 def _dump_debug_line_programs(self
):
606 """ Dump the (decoded) line programs from .debug_line
607 The programs are dumped in the order of the CUs they belong to.
609 self
._emitline
('Decoded dump of debug contents of section .debug_line:\n')
611 for cu
in self
._dwarfinfo
.iter_CUs():
612 lineprogram
= self
._dwarfinfo
.line_program_for_CU(cu
)
614 cu_filename
= bytes2str(lineprogram
['file_entry'][0].name
)
615 if len(lineprogram
['include_directory']) > 0:
616 dir_index
= lineprogram
['file_entry'][0].dir_index
618 dir = lineprogram
['include_directory'][dir_index
- 1]
621 cu_filename
= '%s/%s' % (bytes2str(dir), cu_filename
)
623 self
._emitline
('CU: %s:' % cu_filename
)
624 self
._emitline
('File name Line number Starting address')
626 # Print each state's file, line and address information. For some
627 # instructions other output is needed to be compatible with
629 for entry
in lineprogram
.get_entries():
632 # Special handling for commands that don't set a new state
633 if entry
.command
== DW_LNS_set_file
:
634 file_entry
= lineprogram
['file_entry'][entry
.args
[0] - 1]
635 if file_entry
.dir_index
== 0:
637 self
._emitline
('\n./%s:[++]' % (
638 bytes2str(file_entry
.name
)))
640 self
._emitline
('\n%s/%s:' % (
641 bytes2str(lineprogram
['include_directory'][file_entry
.dir_index
- 1]),
642 bytes2str(file_entry
.name
)))
643 elif entry
.command
== DW_LNE_define_file
:
644 self
._emitline
('%s:' % (
645 bytes2str(lineprogram
['include_directory'][entry
.args
[0].dir_index
])))
646 elif not state
.end_sequence
:
647 # readelf doesn't print the state after end_sequence
648 # instructions. I think it's a bug but to be compatible
649 # I don't print them too.
650 self
._emitline
('%-35s %11d %18s' % (
651 bytes2str(lineprogram
['file_entry'][state
.file - 1].name
),
653 '0' if state
.address
== 0 else
654 self
._format
_hex
(state
.address
)))
655 if entry
.command
== DW_LNS_copy
:
656 # Another readelf oddity...
659 def _dump_debug_frames(self
):
660 """ Dump the raw frame information from .debug_frame
662 if not self
._dwarfinfo
.has_CFI():
664 self
._emitline
('Contents of the .debug_frame section:')
666 for entry
in self
._dwarfinfo
.CFI_entries():
667 if isinstance(entry
, CIE
):
668 self
._emitline
('\n%08x %08x %08x CIE' % (
669 entry
.offset
, entry
['length'], entry
['CIE_id']))
670 self
._emitline
(' Version: %d' % entry
['version'])
671 self
._emitline
(' Augmentation: "%s"' % bytes2str(entry
['augmentation']))
672 self
._emitline
(' Code alignment factor: %u' % entry
['code_alignment_factor'])
673 self
._emitline
(' Data alignment factor: %d' % entry
['data_alignment_factor'])
674 self
._emitline
(' Return address column: %d' % entry
['return_address_register'])
677 self
._emitline
('\n%08x %08x %08x FDE cie=%08x pc=%08x..%08x' % (
680 entry
['CIE_pointer'],
682 entry
['initial_location'],
683 entry
['initial_location'] + entry
['address_range']))
685 self
._emit
(describe_CFI_instructions(entry
))
688 def _dump_debug_frames_interp(self
):
689 """ Dump the interpreted (decoded) frame information from .debug_frame
691 if not self
._dwarfinfo
.has_CFI():
694 self
._emitline
('Contents of the .debug_frame section:')
696 for entry
in self
._dwarfinfo
.CFI_entries():
697 if isinstance(entry
, CIE
):
698 self
._emitline
('\n%08x %08x %08x CIE "%s" cf=%d df=%d ra=%d' % (
702 bytes2str(entry
['augmentation']),
703 entry
['code_alignment_factor'],
704 entry
['data_alignment_factor'],
705 entry
['return_address_register']))
706 ra_regnum
= entry
['return_address_register']
708 self
._emitline
('\n%08x %08x %08x FDE cie=%08x pc=%08x..%08x' % (
711 entry
['CIE_pointer'],
713 entry
['initial_location'],
714 entry
['initial_location'] + entry
['address_range']))
715 ra_regnum
= entry
.cie
['return_address_register']
717 # Print the heading row for the decoded table
719 self
._emit
(' ' if entry
.structs
.address_size
== 4 else ' ')
722 # Decode the table nad look at the registers it describes.
723 # We build reg_order here to match readelf's order. In particular,
724 # registers are sorted by their number, and the register matching
725 # ra_regnum is always listed last with a special heading.
726 decoded_table
= entry
.get_decoded()
727 reg_order
= sorted(ifilter(
728 lambda r
: r
!= ra_regnum
,
729 decoded_table
.reg_order
))
731 # Headings for the registers
732 for regnum
in reg_order
:
733 self
._emit
('%-6s' % describe_reg_name(regnum
))
734 self
._emitline
('ra ')
736 # Now include ra_regnum in reg_order to print its values similarly
737 # to the other registers.
738 reg_order
.append(ra_regnum
)
739 for line
in decoded_table
.table
:
740 self
._emit
(self
._format
_hex
(
741 line
['pc'], fullhex
=True, lead0x
=False))
742 self
._emit
(' %-9s' % describe_CFI_CFA_rule(line
['cfa']))
744 for regnum
in reg_order
:
746 s
= describe_CFI_register_rule(line
[regnum
])
749 self
._emit
('%-6s' % s
)
753 def _emit(self
, s
=''):
754 """ Emit an object to output
756 self
.output
.write(str(s
))
758 def _emitline(self
, s
=''):
759 """ Emit an object to output, followed by a newline
761 self
.output
.write(str(s
) + '\n')
764 SCRIPT_DESCRIPTION
= 'Display information about the contents of ELF format files'
765 VERSION_STRING
= '%%prog: based on pyelftools %s' % __version__
768 def main(stream
=None):
769 # parse the command-line arguments and invoke ReadElf
770 optparser
= OptionParser(
771 usage
='usage: %prog [options] <elf-file>',
772 description
=SCRIPT_DESCRIPTION
,
773 add_help_option
=False, # -h is a real option of readelf
775 version
=VERSION_STRING
)
776 optparser
.add_option('-d', '--dynamic',
777 action
='store_true', dest
='show_dynamic_tags',
778 help='Display the dynamic section')
779 optparser
.add_option('-H', '--help',
780 action
='store_true', dest
='help',
781 help='Display this information')
782 optparser
.add_option('-h', '--file-header',
783 action
='store_true', dest
='show_file_header',
784 help='Display the ELF file header')
785 optparser
.add_option('-l', '--program-headers', '--segments',
786 action
='store_true', dest
='show_program_header',
787 help='Display the program headers')
788 optparser
.add_option('-S', '--section-headers', '--sections',
789 action
='store_true', dest
='show_section_header',
790 help="Display the sections' headers")
791 optparser
.add_option('-e', '--headers',
792 action
='store_true', dest
='show_all_headers',
793 help='Equivalent to: -h -l -S')
794 optparser
.add_option('-s', '--symbols', '--syms',
795 action
='store_true', dest
='show_symbols',
796 help='Display the symbol table')
797 optparser
.add_option('-r', '--relocs',
798 action
='store_true', dest
='show_relocs',
799 help='Display the relocations (if present)')
800 optparser
.add_option('-x', '--hex-dump',
801 action
='store', dest
='show_hex_dump', metavar
='<number|name>',
802 help='Dump the contents of section <number|name> as bytes')
803 optparser
.add_option('-p', '--string-dump',
804 action
='store', dest
='show_string_dump', metavar
='<number|name>',
805 help='Dump the contents of section <number|name> as strings')
806 optparser
.add_option('--debug-dump',
807 action
='store', dest
='debug_dump_what', metavar
='<what>',
809 'Display the contents of DWARF debug sections. <what> can ' +
810 'one of {info,decodedline,frames,frames-interp}'))
812 options
, args
= optparser
.parse_args()
814 if options
.help or len(args
) == 0:
815 optparser
.print_help()
818 if options
.show_all_headers
:
819 do_file_header
= do_section_header
= do_program_header
= True
821 do_file_header
= options
.show_file_header
822 do_section_header
= options
.show_section_header
823 do_program_header
= options
.show_program_header
825 with
open(args
[0], 'rb') as file:
827 readelf
= ReadElf(file, stream
or sys
.stdout
)
829 readelf
.display_file_header()
830 if do_section_header
:
831 readelf
.display_section_headers(
832 show_heading
=not do_file_header
)
833 if do_program_header
:
834 readelf
.display_program_headers(
835 show_heading
=not do_file_header
)
836 if options
.show_dynamic_tags
:
837 readelf
.display_dynamic_tags()
838 if options
.show_symbols
:
839 readelf
.display_symbol_tables()
840 if options
.show_relocs
:
841 readelf
.display_relocations()
842 if options
.show_hex_dump
:
843 readelf
.display_hex_dump(options
.show_hex_dump
)
844 if options
.show_string_dump
:
845 readelf
.display_string_dump(options
.show_string_dump
)
846 if options
.debug_dump_what
:
847 readelf
.display_debug_dump(options
.debug_dump_what
)
848 except ELFError
as ex
:
849 sys
.stderr
.write('ELF error: %s\n' % ex
)
854 # Run 'main' redirecting its output to readelfout.txt
855 # Saves profiling information in readelf.profile
856 PROFFILE
= 'readelf.profile'
858 cProfile
.run('main(open("readelfout.txt", "w"))', PROFFILE
)
860 # Dig in some profiling stats
862 p
= pstats
.Stats(PROFFILE
)
863 p
.sort_stats('cumulative').print_stats(25)
866 #-------------------------------------------------------------------------------
867 if __name__
== '__main__':