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]' % bytes2str(tag
.needed
)
304 elif tag
.entry
.d_tag
== 'DT_RPATH':
305 parsed
= 'Library rpath: [%s]' % bytes2str(tag
.rpath
)
306 elif tag
.entry
.d_tag
== 'DT_RUNPATH':
307 parsed
= 'Library runpath: [%s]' % bytes2str(tag
.runpath
)
308 elif tag
.entry
.d_tag
== 'DT_SONAME':
309 parsed
= 'Library soname: [%s]' % bytes2str(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') or
314 tag
.entry
.d_tag
.endswith('COUNT')):
315 parsed
= '%i' % tag
['d_val']
316 elif tag
.entry
.d_tag
== 'DT_PLTREL':
317 s
= describe_dyn_tag(tag
.entry
.d_val
)
318 if s
.startswith('DT_'):
322 parsed
= '%#x' % tag
['d_val']
324 self
._emitline
(" %s %-*s %s" % (
325 self
._format
_hex
(ENUM_D_TAG
.get(tag
.entry
.d_tag
, tag
.entry
.d_tag
),
326 fullhex
=True, lead0x
=True),
328 '(%s)' % (tag
.entry
.d_tag
[3:],),
331 def display_relocations(self
):
332 """ Display the relocations contained in the file
334 has_relocation_sections
= False
335 for section
in self
.elffile
.iter_sections():
336 if not isinstance(section
, RelocationSection
):
339 has_relocation_sections
= True
340 self
._emitline
("\nRelocation section '%s' at offset %s contains %s entries:" % (
341 bytes2str(section
.name
),
342 self
._format
_hex
(section
['sh_offset']),
343 section
.num_relocations()))
344 if section
.is_RELA():
345 self
._emitline
(" Offset Info Type Sym. Value Sym. Name + Addend")
347 self
._emitline
(" Offset Info Type Sym.Value Sym. Name")
349 # The symbol table section pointed to in sh_link
350 symtable
= self
.elffile
.get_section(section
['sh_link'])
352 for rel
in section
.iter_relocations():
353 hexwidth
= 8 if self
.elffile
.elfclass
== 32 else 12
354 self
._emit
('%s %s %-17.17s' % (
355 self
._format
_hex
(rel
['r_offset'],
356 fieldsize
=hexwidth
, lead0x
=False),
357 self
._format
_hex
(rel
['r_info'],
358 fieldsize
=hexwidth
, lead0x
=False),
360 rel
['r_info_type'], self
.elffile
)))
362 if rel
['r_info_sym'] == 0:
366 symbol
= symtable
.get_symbol(rel
['r_info_sym'])
367 # Some symbols have zero 'st_name', so instead what's used is
368 # the name of the section they point at
369 if symbol
['st_name'] == 0:
370 symsec
= self
.elffile
.get_section(symbol
['st_shndx'])
371 symbol_name
= symsec
.name
373 symbol_name
= symbol
.name
374 self
._emit
(' %s %s%22.22s' % (
377 fullhex
=True, lead0x
=False),
378 ' ' if self
.elffile
.elfclass
== 32 else '',
379 bytes2str(symbol_name
)))
380 if section
.is_RELA():
381 self
._emit
(' %s %x' % (
382 '+' if rel
['r_addend'] >= 0 else '-',
383 abs(rel
['r_addend'])))
386 if not has_relocation_sections
:
387 self
._emitline
('\nThere are no relocations in this file.')
389 def display_hex_dump(self
, section_spec
):
390 """ Display a hex dump of a section. section_spec is either a section
393 section
= self
._section
_from
_spec
(section_spec
)
395 self
._emitline
("Section '%s' does not exist in the file!" % (
399 self
._emitline
("\nHex dump of section '%s':" % bytes2str(section
.name
))
400 self
._note
_relocs
_for
_section
(section
)
401 addr
= section
['sh_addr']
402 data
= section
.data()
405 while dataptr
< len(data
):
406 bytesleft
= len(data
) - dataptr
407 # chunks of 16 bytes per line
408 linebytes
= 16 if bytesleft
> 16 else bytesleft
410 self
._emit
(' %s ' % self
._format
_hex
(addr
, fieldsize
=8))
413 self
._emit
('%2.2x' % byte2int(data
[dataptr
+ i
]))
419 for i
in range(linebytes
):
420 c
= data
[dataptr
+ i
: dataptr
+ i
+ 1]
421 if byte2int(c
[0]) >= 32 and byte2int(c
[0]) < 0x7f:
422 self
._emit
(bytes2str(c
))
424 self
._emit
(bytes2str(b
'.'))
432 def display_string_dump(self
, section_spec
):
433 """ Display a strings dump of a section. section_spec is either a
434 section number or a name.
436 section
= self
._section
_from
_spec
(section_spec
)
438 self
._emitline
("Section '%s' does not exist in the file!" % (
442 self
._emitline
("\nString dump of section '%s':" % bytes2str(section
.name
))
445 data
= section
.data()
448 while dataptr
< len(data
):
449 while ( dataptr
< len(data
) and
450 not (32 <= byte2int(data
[dataptr
]) <= 127)):
453 if dataptr
>= len(data
):
457 while endptr
< len(data
) and byte2int(data
[endptr
]) != 0:
461 self
._emitline
(' [%6x] %s' % (
462 dataptr
, bytes2str(data
[dataptr
:endptr
])))
467 self
._emitline
(' No strings found in this section.')
471 def display_debug_dump(self
, dump_what
):
472 """ Dump a DWARF section
474 self
._init
_dwarfinfo
()
475 if self
._dwarfinfo
is None:
478 set_global_machine_arch(self
.elffile
.get_machine_arch())
480 if dump_what
== 'info':
481 self
._dump
_debug
_info
()
482 elif dump_what
== 'decodedline':
483 self
._dump
_debug
_line
_programs
()
484 elif dump_what
== 'frames':
485 self
._dump
_debug
_frames
()
486 elif dump_what
== 'frames-interp':
487 self
._dump
_debug
_frames
_interp
()
489 self
._emitline
('debug dump not yet supported for "%s"' % dump_what
)
491 def _format_hex(self
, addr
, fieldsize
=None, fullhex
=False, lead0x
=True):
492 """ Format an address into a hexadecimal string.
495 Size of the hexadecimal field (with leading zeros to fit the
496 address into. For example with fieldsize=8, the format will
498 If None, the minimal required field size will be used.
501 If True, override fieldsize to set it to the maximal size
502 needed for the elfclass
505 If True, leading 0x is added
507 s
= '0x' if lead0x
else ''
509 fieldsize
= 8 if self
.elffile
.elfclass
== 32 else 16
510 if fieldsize
is None:
513 field
= '%' + '0%sx' % fieldsize
514 return s
+ field
% addr
516 def _section_from_spec(self
, spec
):
517 """ Retrieve a section given a "spec" (either number or name).
518 Return None if no such section exists in the file.
522 if num
< self
.elffile
.num_sections():
523 return self
.elffile
.get_section(num
)
527 # Not a number. Must be a name then
528 return self
.elffile
.get_section_by_name(str2bytes(spec
))
530 def _note_relocs_for_section(self
, section
):
531 """ If there are relocation sections pointing to the givne section,
532 emit a note about it.
534 for relsec
in self
.elffile
.iter_sections():
535 if isinstance(relsec
, RelocationSection
):
536 info_idx
= relsec
['sh_info']
537 if self
.elffile
.get_section(info_idx
) == section
:
538 self
._emitline
(' Note: This section has relocations against it, but these have NOT been applied to this dump.')
541 def _init_dwarfinfo(self
):
542 """ Initialize the DWARF info contained in the file and assign it to
544 Leave self._dwarfinfo at None if no DWARF info was found in the file
546 if self
._dwarfinfo
is not None:
549 if self
.elffile
.has_dwarf_info():
550 self
._dwarfinfo
= self
.elffile
.get_dwarf_info()
552 self
._dwarfinfo
= None
554 def _dump_debug_info(self
):
555 """ Dump the debugging info section.
557 self
._emitline
('Contents of the .debug_info section:\n')
559 # Offset of the .debug_info section in the stream
560 section_offset
= self
._dwarfinfo
.debug_info_sec
.global_offset
562 for cu
in self
._dwarfinfo
.iter_CUs():
563 self
._emitline
(' Compilation Unit @ offset %s:' %
564 self
._format
_hex
(cu
.cu_offset
))
565 self
._emitline
(' Length: %s (%s)' % (
566 self
._format
_hex
(cu
['unit_length']),
567 '%s-bit' % cu
.dwarf_format()))
568 self
._emitline
(' Version: %s' % cu
['version']),
569 self
._emitline
(' Abbrev Offset: %s' % (
570 self
._format
_hex
(cu
['debug_abbrev_offset']))),
571 self
._emitline
(' Pointer Size: %s' % cu
['address_size'])
573 # The nesting depth of each DIE within the tree of DIEs must be
574 # displayed. To implement this, a counter is incremented each time
575 # the current DIE has children, and decremented when a null die is
576 # encountered. Due to the way the DIE tree is serialized, this will
577 # correctly reflect the nesting depth
580 for die
in cu
.iter_DIEs():
581 self
._emitline
(' <%s><%x>: Abbrev Number: %s%s' % (
585 (' (%s)' % die
.tag
) if not die
.is_null() else ''))
590 for attr
in itervalues(die
.attributes
):
592 # Unknown attribute values are passed-through as integers
593 if isinstance(name
, int):
594 name
= 'Unknown AT value: %x' % name
595 self
._emitline
(' <%2x> %-18s: %s' % (
599 attr
, die
, section_offset
)))
606 def _dump_debug_line_programs(self
):
607 """ Dump the (decoded) line programs from .debug_line
608 The programs are dumped in the order of the CUs they belong to.
610 self
._emitline
('Decoded dump of debug contents of section .debug_line:\n')
612 for cu
in self
._dwarfinfo
.iter_CUs():
613 lineprogram
= self
._dwarfinfo
.line_program_for_CU(cu
)
615 cu_filename
= bytes2str(lineprogram
['file_entry'][0].name
)
616 if len(lineprogram
['include_directory']) > 0:
617 dir_index
= lineprogram
['file_entry'][0].dir_index
619 dir = lineprogram
['include_directory'][dir_index
- 1]
622 cu_filename
= '%s/%s' % (bytes2str(dir), cu_filename
)
624 self
._emitline
('CU: %s:' % cu_filename
)
625 self
._emitline
('File name Line number Starting address')
627 # Print each state's file, line and address information. For some
628 # instructions other output is needed to be compatible with
630 for entry
in lineprogram
.get_entries():
633 # Special handling for commands that don't set a new state
634 if entry
.command
== DW_LNS_set_file
:
635 file_entry
= lineprogram
['file_entry'][entry
.args
[0] - 1]
636 if file_entry
.dir_index
== 0:
638 self
._emitline
('\n./%s:[++]' % (
639 bytes2str(file_entry
.name
)))
641 self
._emitline
('\n%s/%s:' % (
642 bytes2str(lineprogram
['include_directory'][file_entry
.dir_index
- 1]),
643 bytes2str(file_entry
.name
)))
644 elif entry
.command
== DW_LNE_define_file
:
645 self
._emitline
('%s:' % (
646 bytes2str(lineprogram
['include_directory'][entry
.args
[0].dir_index
])))
647 elif not state
.end_sequence
:
648 # readelf doesn't print the state after end_sequence
649 # instructions. I think it's a bug but to be compatible
650 # I don't print them too.
651 self
._emitline
('%-35s %11d %18s' % (
652 bytes2str(lineprogram
['file_entry'][state
.file - 1].name
),
654 '0' if state
.address
== 0 else
655 self
._format
_hex
(state
.address
)))
656 if entry
.command
== DW_LNS_copy
:
657 # Another readelf oddity...
660 def _dump_debug_frames(self
):
661 """ Dump the raw frame information from .debug_frame
663 if not self
._dwarfinfo
.has_CFI():
665 self
._emitline
('Contents of the .debug_frame section:')
667 for entry
in self
._dwarfinfo
.CFI_entries():
668 if isinstance(entry
, CIE
):
669 self
._emitline
('\n%08x %08x %08x CIE' % (
670 entry
.offset
, entry
['length'], entry
['CIE_id']))
671 self
._emitline
(' Version: %d' % entry
['version'])
672 self
._emitline
(' Augmentation: "%s"' % bytes2str(entry
['augmentation']))
673 self
._emitline
(' Code alignment factor: %u' % entry
['code_alignment_factor'])
674 self
._emitline
(' Data alignment factor: %d' % entry
['data_alignment_factor'])
675 self
._emitline
(' Return address column: %d' % entry
['return_address_register'])
678 self
._emitline
('\n%08x %08x %08x FDE cie=%08x pc=%08x..%08x' % (
681 entry
['CIE_pointer'],
683 entry
['initial_location'],
684 entry
['initial_location'] + entry
['address_range']))
686 self
._emit
(describe_CFI_instructions(entry
))
689 def _dump_debug_frames_interp(self
):
690 """ Dump the interpreted (decoded) frame information from .debug_frame
692 if not self
._dwarfinfo
.has_CFI():
695 self
._emitline
('Contents of the .debug_frame section:')
697 for entry
in self
._dwarfinfo
.CFI_entries():
698 if isinstance(entry
, CIE
):
699 self
._emitline
('\n%08x %08x %08x CIE "%s" cf=%d df=%d ra=%d' % (
703 bytes2str(entry
['augmentation']),
704 entry
['code_alignment_factor'],
705 entry
['data_alignment_factor'],
706 entry
['return_address_register']))
707 ra_regnum
= entry
['return_address_register']
709 self
._emitline
('\n%08x %08x %08x FDE cie=%08x pc=%08x..%08x' % (
712 entry
['CIE_pointer'],
714 entry
['initial_location'],
715 entry
['initial_location'] + entry
['address_range']))
716 ra_regnum
= entry
.cie
['return_address_register']
718 # Print the heading row for the decoded table
720 self
._emit
(' ' if entry
.structs
.address_size
== 4 else ' ')
723 # Decode the table nad look at the registers it describes.
724 # We build reg_order here to match readelf's order. In particular,
725 # registers are sorted by their number, and the register matching
726 # ra_regnum is always listed last with a special heading.
727 decoded_table
= entry
.get_decoded()
728 reg_order
= sorted(ifilter(
729 lambda r
: r
!= ra_regnum
,
730 decoded_table
.reg_order
))
732 # Headings for the registers
733 for regnum
in reg_order
:
734 self
._emit
('%-6s' % describe_reg_name(regnum
))
735 self
._emitline
('ra ')
737 # Now include ra_regnum in reg_order to print its values similarly
738 # to the other registers.
739 reg_order
.append(ra_regnum
)
740 for line
in decoded_table
.table
:
741 self
._emit
(self
._format
_hex
(
742 line
['pc'], fullhex
=True, lead0x
=False))
743 self
._emit
(' %-9s' % describe_CFI_CFA_rule(line
['cfa']))
745 for regnum
in reg_order
:
747 s
= describe_CFI_register_rule(line
[regnum
])
750 self
._emit
('%-6s' % s
)
754 def _emit(self
, s
=''):
755 """ Emit an object to output
757 self
.output
.write(str(s
))
759 def _emitline(self
, s
=''):
760 """ Emit an object to output, followed by a newline
762 self
.output
.write(str(s
) + '\n')
765 SCRIPT_DESCRIPTION
= 'Display information about the contents of ELF format files'
766 VERSION_STRING
= '%%prog: based on pyelftools %s' % __version__
769 def main(stream
=None):
770 # parse the command-line arguments and invoke ReadElf
771 optparser
= OptionParser(
772 usage
='usage: %prog [options] <elf-file>',
773 description
=SCRIPT_DESCRIPTION
,
774 add_help_option
=False, # -h is a real option of readelf
776 version
=VERSION_STRING
)
777 optparser
.add_option('-d', '--dynamic',
778 action
='store_true', dest
='show_dynamic_tags',
779 help='Display the dynamic section')
780 optparser
.add_option('-H', '--help',
781 action
='store_true', dest
='help',
782 help='Display this information')
783 optparser
.add_option('-h', '--file-header',
784 action
='store_true', dest
='show_file_header',
785 help='Display the ELF file header')
786 optparser
.add_option('-l', '--program-headers', '--segments',
787 action
='store_true', dest
='show_program_header',
788 help='Display the program headers')
789 optparser
.add_option('-S', '--section-headers', '--sections',
790 action
='store_true', dest
='show_section_header',
791 help="Display the sections' headers")
792 optparser
.add_option('-e', '--headers',
793 action
='store_true', dest
='show_all_headers',
794 help='Equivalent to: -h -l -S')
795 optparser
.add_option('-s', '--symbols', '--syms',
796 action
='store_true', dest
='show_symbols',
797 help='Display the symbol table')
798 optparser
.add_option('-r', '--relocs',
799 action
='store_true', dest
='show_relocs',
800 help='Display the relocations (if present)')
801 optparser
.add_option('-x', '--hex-dump',
802 action
='store', dest
='show_hex_dump', metavar
='<number|name>',
803 help='Dump the contents of section <number|name> as bytes')
804 optparser
.add_option('-p', '--string-dump',
805 action
='store', dest
='show_string_dump', metavar
='<number|name>',
806 help='Dump the contents of section <number|name> as strings')
807 optparser
.add_option('--debug-dump',
808 action
='store', dest
='debug_dump_what', metavar
='<what>',
810 'Display the contents of DWARF debug sections. <what> can ' +
811 'one of {info,decodedline,frames,frames-interp}'))
813 options
, args
= optparser
.parse_args()
815 if options
.help or len(args
) == 0:
816 optparser
.print_help()
819 if options
.show_all_headers
:
820 do_file_header
= do_section_header
= do_program_header
= True
822 do_file_header
= options
.show_file_header
823 do_section_header
= options
.show_section_header
824 do_program_header
= options
.show_program_header
826 with
open(args
[0], 'rb') as file:
828 readelf
= ReadElf(file, stream
or sys
.stdout
)
830 readelf
.display_file_header()
831 if do_section_header
:
832 readelf
.display_section_headers(
833 show_heading
=not do_file_header
)
834 if do_program_header
:
835 readelf
.display_program_headers(
836 show_heading
=not do_file_header
)
837 if options
.show_dynamic_tags
:
838 readelf
.display_dynamic_tags()
839 if options
.show_symbols
:
840 readelf
.display_symbol_tables()
841 if options
.show_relocs
:
842 readelf
.display_relocations()
843 if options
.show_hex_dump
:
844 readelf
.display_hex_dump(options
.show_hex_dump
)
845 if options
.show_string_dump
:
846 readelf
.display_string_dump(options
.show_string_dump
)
847 if options
.debug_dump_what
:
848 readelf
.display_debug_dump(options
.debug_dump_what
)
849 except ELFError
as ex
:
850 sys
.stderr
.write('ELF error: %s\n' % ex
)
855 # Run 'main' redirecting its output to readelfout.txt
856 # Saves profiling information in readelf.profile
857 PROFFILE
= 'readelf.profile'
859 cProfile
.run('main(open("readelfout.txt", "w"))', PROFFILE
)
861 # Dig in some profiling stats
863 p
= pstats
.Stats(PROFFILE
)
864 p
.sort_stats('cumulative').print_stats(25)
867 #-------------------------------------------------------------------------------
868 if __name__
== '__main__':