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
.relocation
import RelocationSection
29 from elftools
.elf
.descriptions
import (
30 describe_ei_class
, describe_ei_data
, describe_ei_version
,
31 describe_ei_osabi
, describe_e_type
, describe_e_machine
,
32 describe_e_version_numeric
, describe_p_type
, describe_p_flags
,
33 describe_sh_type
, describe_sh_flags
,
34 describe_symbol_type
, describe_symbol_bind
, describe_symbol_visibility
,
35 describe_symbol_shndx
, describe_reloc_type
, describe_dyn_tag
,
37 from elftools
.elf
.constants
import E_FLAGS
38 from elftools
.dwarf
.dwarfinfo
import DWARFInfo
39 from elftools
.dwarf
.descriptions
import (
40 describe_reg_name
, describe_attr_value
, set_global_machine_arch
,
41 describe_CFI_instructions
, describe_CFI_register_rule
,
42 describe_CFI_CFA_rule
,
44 from elftools
.dwarf
.constants
import (
45 DW_LNS_copy
, DW_LNS_set_file
, DW_LNE_define_file
)
46 from elftools
.dwarf
.callframe
import CIE
, FDE
49 class ReadElf(object):
50 """ display_* methods are used to emit output into the output stream
52 def __init__(self
, file, output
):
54 stream object with the ELF file to read
57 output stream to write to
59 self
.elffile
= ELFFile(file)
62 # Lazily initialized if a debug dump is requested
63 self
._dwarfinfo
= None
65 def display_file_header(self
):
66 """ Display the ELF file header
68 self
._emitline
('ELF Header:')
69 self
._emit
(' Magic: ')
70 self
._emitline
(' '.join('%2.2x' % byte2int(b
)
71 for b
in self
.elffile
.e_ident_raw
))
72 header
= self
.elffile
.header
73 e_ident
= header
['e_ident']
74 self
._emitline
(' Class: %s' %
75 describe_ei_class(e_ident
['EI_CLASS']))
76 self
._emitline
(' Data: %s' %
77 describe_ei_data(e_ident
['EI_DATA']))
78 self
._emitline
(' Version: %s' %
79 describe_ei_version(e_ident
['EI_VERSION']))
80 self
._emitline
(' OS/ABI: %s' %
81 describe_ei_osabi(e_ident
['EI_OSABI']))
82 self
._emitline
(' ABI Version: %d' %
83 e_ident
['EI_ABIVERSION'])
84 self
._emitline
(' Type: %s' %
85 describe_e_type(header
['e_type']))
86 self
._emitline
(' Machine: %s' %
87 describe_e_machine(header
['e_machine']))
88 self
._emitline
(' Version: %s' %
89 describe_e_version_numeric(header
['e_version']))
90 self
._emitline
(' Entry point address: %s' %
91 self
._format
_hex
(header
['e_entry']))
92 self
._emit
(' Start of program headers: %s' %
94 self
._emitline
(' (bytes into file)')
95 self
._emit
(' Start of section headers: %s' %
97 self
._emitline
(' (bytes into file)')
98 self
._emitline
(' Flags: %s%s' %
99 (self
._format
_hex
(header
['e_flags']),
100 self
.decode_flags(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 decode_flags(self
, flags
):
116 if self
.elffile
['e_machine'] == "EM_ARM":
117 if flags
& E_FLAGS
.EF_ARM_HASENTRY
:
118 description
+= ", has entry point"
120 version
= flags
& E_FLAGS
.EF_ARM_EABIMASK
121 if version
== E_FLAGS
.EF_ARM_EABI_VER5
:
122 description
+= ", Version5 EABI"
125 def display_program_headers(self
, show_heading
=True):
126 """ Display the ELF program headers.
127 If show_heading is True, displays the heading for this information
128 (Elf file type is...)
131 if self
.elffile
.num_segments() == 0:
132 self
._emitline
('There are no program headers in this file.')
135 elfheader
= self
.elffile
.header
137 self
._emitline
('Elf file type is %s' %
138 describe_e_type(elfheader
['e_type']))
139 self
._emitline
('Entry point is %s' %
140 self
._format
_hex
(elfheader
['e_entry']))
141 # readelf weirness - why isn't e_phoff printed as hex? (for section
143 self
._emitline
('There are %s program headers, starting at offset %s' % (
144 elfheader
['e_phnum'], elfheader
['e_phoff']))
147 self
._emitline
('Program Headers:')
149 # Now comes the table of program headers with their attributes. Note
150 # that due to different formatting constraints of 32-bit and 64-bit
151 # addresses, there are some conditions on elfclass here.
153 # First comes the table heading
155 if self
.elffile
.elfclass
== 32:
156 self
._emitline
(' Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align')
158 self
._emitline
(' Type Offset VirtAddr PhysAddr')
159 self
._emitline
(' FileSiz MemSiz Flags Align')
163 for segment
in self
.elffile
.iter_segments():
164 self
._emit
(' %-14s ' % describe_p_type(segment
['p_type']))
166 if self
.elffile
.elfclass
== 32:
167 self
._emitline
('%s %s %s %s %s %-3s %s' % (
168 self
._format
_hex
(segment
['p_offset'], fieldsize
=6),
169 self
._format
_hex
(segment
['p_vaddr'], fullhex
=True),
170 self
._format
_hex
(segment
['p_paddr'], fullhex
=True),
171 self
._format
_hex
(segment
['p_filesz'], fieldsize
=5),
172 self
._format
_hex
(segment
['p_memsz'], fieldsize
=5),
173 describe_p_flags(segment
['p_flags']),
174 self
._format
_hex
(segment
['p_align'])))
176 self
._emitline
('%s %s %s' % (
177 self
._format
_hex
(segment
['p_offset'], fullhex
=True),
178 self
._format
_hex
(segment
['p_vaddr'], fullhex
=True),
179 self
._format
_hex
(segment
['p_paddr'], fullhex
=True)))
180 self
._emitline
(' %s %s %-3s %s' % (
181 self
._format
_hex
(segment
['p_filesz'], fullhex
=True),
182 self
._format
_hex
(segment
['p_memsz'], fullhex
=True),
183 describe_p_flags(segment
['p_flags']),
184 # lead0x set to False for p_align, to mimic readelf.
185 # No idea why the difference from 32-bit mode :-|
186 self
._format
_hex
(segment
['p_align'], lead0x
=False)))
188 if isinstance(segment
, InterpSegment
):
189 self
._emitline
(' [Requesting program interpreter: %s]' %
190 bytes2str(segment
.get_interp_name()))
192 # Sections to segments mapping
194 if self
.elffile
.num_sections() == 0:
195 # No sections? We're done
198 self
._emitline
('\n Section to Segment mapping:')
199 self
._emitline
(' Segment Sections...')
201 for nseg
, segment
in enumerate(self
.elffile
.iter_segments()):
202 self
._emit
(' %2.2d ' % nseg
)
204 for section
in self
.elffile
.iter_sections():
205 if ( not section
.is_null() and
206 segment
.section_in_segment(section
)):
207 self
._emit
('%s ' % bytes2str(section
.name
))
211 def display_section_headers(self
, show_heading
=True):
212 """ Display the ELF section headers
214 elfheader
= self
.elffile
.header
216 self
._emitline
('There are %s section headers, starting at offset %s' % (
217 elfheader
['e_shnum'], self
._format
_hex
(elfheader
['e_shoff'])))
219 self
._emitline
('\nSection Header%s:' % (
220 's' if elfheader
['e_shnum'] > 1 else ''))
222 # Different formatting constraints of 32-bit and 64-bit addresses
224 if self
.elffile
.elfclass
== 32:
225 self
._emitline
(' [Nr] Name Type Addr Off Size ES Flg Lk Inf Al')
227 self
._emitline
(' [Nr] Name Type Address Offset')
228 self
._emitline
(' Size EntSize Flags Link Info Align')
232 for nsec
, section
in enumerate(self
.elffile
.iter_sections()):
233 self
._emit
(' [%2u] %-17.17s %-15.15s ' % (
234 nsec
, bytes2str(section
.name
), describe_sh_type(section
['sh_type'])))
236 if self
.elffile
.elfclass
== 32:
237 self
._emitline
('%s %s %s %s %3s %2s %3s %2s' % (
238 self
._format
_hex
(section
['sh_addr'], fieldsize
=8, lead0x
=False),
239 self
._format
_hex
(section
['sh_offset'], fieldsize
=6, lead0x
=False),
240 self
._format
_hex
(section
['sh_size'], fieldsize
=6, lead0x
=False),
241 self
._format
_hex
(section
['sh_entsize'], fieldsize
=2, lead0x
=False),
242 describe_sh_flags(section
['sh_flags']),
243 section
['sh_link'], section
['sh_info'],
244 section
['sh_addralign']))
246 self
._emitline
(' %s %s' % (
247 self
._format
_hex
(section
['sh_addr'], fullhex
=True, lead0x
=False),
248 self
._format
_hex
(section
['sh_offset'],
249 fieldsize
=16 if section
['sh_offset'] > 0xffffffff else 8,
251 self
._emitline
(' %s %s %3s %2s %3s %s' % (
252 self
._format
_hex
(section
['sh_size'], fullhex
=True, lead0x
=False),
253 self
._format
_hex
(section
['sh_entsize'], fullhex
=True, lead0x
=False),
254 describe_sh_flags(section
['sh_flags']),
255 section
['sh_link'], section
['sh_info'],
256 section
['sh_addralign']))
258 self
._emitline
('Key to Flags:')
259 self
._emit
(' W (write), A (alloc), X (execute), M (merge), S (strings)')
260 if self
.elffile
['e_machine'] in ('EM_X86_64', 'EM_L10M'):
261 self
._emitline
(', l (large)')
264 self
._emitline
(' I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)')
265 self
._emitline
(' O (extra OS processing required) o (OS specific), p (processor specific)')
267 def display_symbol_tables(self
):
268 """ Display the symbol tables contained in the file
270 for section
in self
.elffile
.iter_sections():
271 if not isinstance(section
, SymbolTableSection
):
274 if section
['sh_entsize'] == 0:
275 self
._emitline
("\nSymbol table '%s' has a sh_entsize of zero!" % (
276 bytes2str(section
.name
)))
279 self
._emitline
("\nSymbol table '%s' contains %s entries:" % (
280 bytes2str(section
.name
), section
.num_symbols()))
282 if self
.elffile
.elfclass
== 32:
283 self
._emitline
(' Num: Value Size Type Bind Vis Ndx Name')
285 self
._emitline
(' Num: Value Size Type Bind Vis Ndx Name')
287 for nsym
, symbol
in enumerate(section
.iter_symbols()):
288 # symbol names are truncated to 25 chars, similarly to readelf
289 self
._emitline
('%6d: %s %5d %-7s %-6s %-7s %4s %.25s' % (
291 self
._format
_hex
(symbol
['st_value'], fullhex
=True, lead0x
=False),
293 describe_symbol_type(symbol
['st_info']['type']),
294 describe_symbol_bind(symbol
['st_info']['bind']),
295 describe_symbol_visibility(symbol
['st_other']['visibility']),
296 describe_symbol_shndx(symbol
['st_shndx']),
297 bytes2str(symbol
.name
)))
299 def display_dynamic_tags(self
):
300 """ Display the dynamic tags contained in the file
302 has_dynamic_sections
= False
303 for section
in self
.elffile
.iter_sections():
304 if not isinstance(section
, DynamicSection
):
307 has_dynamic_sections
= True
308 self
._emitline
("\nDynamic section at offset %s contains %s entries:" % (
309 self
._format
_hex
(section
['sh_offset']),
311 self
._emitline
(" Tag Type Name/Value")
313 padding
= 20 + (8 if self
.elffile
.elfclass
== 32 else 0)
314 for tag
in section
.iter_tags():
315 if tag
.entry
.d_tag
== 'DT_NEEDED':
316 parsed
= 'Shared library: [%s]' % bytes2str(tag
.needed
)
317 elif tag
.entry
.d_tag
== 'DT_RPATH':
318 parsed
= 'Library rpath: [%s]' % bytes2str(tag
.rpath
)
319 elif tag
.entry
.d_tag
== 'DT_RUNPATH':
320 parsed
= 'Library runpath: [%s]' % bytes2str(tag
.runpath
)
321 elif tag
.entry
.d_tag
== 'DT_SONAME':
322 parsed
= 'Library soname: [%s]' % bytes2str(tag
.soname
)
323 elif (tag
.entry
.d_tag
.endswith('SZ') or
324 tag
.entry
.d_tag
.endswith('ENT')):
325 parsed
= '%i (bytes)' % tag
['d_val']
326 elif (tag
.entry
.d_tag
.endswith('NUM') or
327 tag
.entry
.d_tag
.endswith('COUNT')):
328 parsed
= '%i' % tag
['d_val']
329 elif tag
.entry
.d_tag
== 'DT_PLTREL':
330 s
= describe_dyn_tag(tag
.entry
.d_val
)
331 if s
.startswith('DT_'):
335 parsed
= '%#x' % tag
['d_val']
337 self
._emitline
(" %s %-*s %s" % (
338 self
._format
_hex
(ENUM_D_TAG
.get(tag
.entry
.d_tag
, tag
.entry
.d_tag
),
339 fullhex
=True, lead0x
=True),
341 '(%s)' % (tag
.entry
.d_tag
[3:],),
343 if not has_dynamic_sections
:
344 # readelf only prints this if there is at least one segment
345 if self
.elffile
.num_segments():
346 self
._emitline
("\nThere is no dynamic section in this file.")
348 def display_relocations(self
):
349 """ Display the relocations contained in the file
351 has_relocation_sections
= False
352 for section
in self
.elffile
.iter_sections():
353 if not isinstance(section
, RelocationSection
):
356 has_relocation_sections
= True
357 self
._emitline
("\nRelocation section '%s' at offset %s contains %s entries:" % (
358 bytes2str(section
.name
),
359 self
._format
_hex
(section
['sh_offset']),
360 section
.num_relocations()))
361 if section
.is_RELA():
362 self
._emitline
(" Offset Info Type Sym. Value Sym. Name + Addend")
364 self
._emitline
(" Offset Info Type Sym.Value Sym. Name")
366 # The symbol table section pointed to in sh_link
367 symtable
= self
.elffile
.get_section(section
['sh_link'])
369 for rel
in section
.iter_relocations():
370 hexwidth
= 8 if self
.elffile
.elfclass
== 32 else 12
371 self
._emit
('%s %s %-17.17s' % (
372 self
._format
_hex
(rel
['r_offset'],
373 fieldsize
=hexwidth
, lead0x
=False),
374 self
._format
_hex
(rel
['r_info'],
375 fieldsize
=hexwidth
, lead0x
=False),
377 rel
['r_info_type'], self
.elffile
)))
379 if rel
['r_info_sym'] == 0:
383 symbol
= symtable
.get_symbol(rel
['r_info_sym'])
384 # Some symbols have zero 'st_name', so instead what's used is
385 # the name of the section they point at
386 if symbol
['st_name'] == 0:
387 symsec
= self
.elffile
.get_section(symbol
['st_shndx'])
388 symbol_name
= symsec
.name
390 symbol_name
= symbol
.name
391 self
._emit
(' %s %s%22.22s' % (
394 fullhex
=True, lead0x
=False),
395 ' ' if self
.elffile
.elfclass
== 32 else '',
396 bytes2str(symbol_name
)))
397 if section
.is_RELA():
398 self
._emit
(' %s %x' % (
399 '+' if rel
['r_addend'] >= 0 else '-',
400 abs(rel
['r_addend'])))
403 if not has_relocation_sections
:
404 self
._emitline
('\nThere are no relocations in this file.')
406 def display_hex_dump(self
, section_spec
):
407 """ Display a hex dump of a section. section_spec is either a section
410 section
= self
._section
_from
_spec
(section_spec
)
412 self
._emitline
("Section '%s' does not exist in the file!" % (
416 self
._emitline
("\nHex dump of section '%s':" % bytes2str(section
.name
))
417 self
._note
_relocs
_for
_section
(section
)
418 addr
= section
['sh_addr']
419 data
= section
.data()
422 while dataptr
< len(data
):
423 bytesleft
= len(data
) - dataptr
424 # chunks of 16 bytes per line
425 linebytes
= 16 if bytesleft
> 16 else bytesleft
427 self
._emit
(' %s ' % self
._format
_hex
(addr
, fieldsize
=8))
430 self
._emit
('%2.2x' % byte2int(data
[dataptr
+ i
]))
436 for i
in range(linebytes
):
437 c
= data
[dataptr
+ i
: dataptr
+ i
+ 1]
438 if byte2int(c
[0]) >= 32 and byte2int(c
[0]) < 0x7f:
439 self
._emit
(bytes2str(c
))
441 self
._emit
(bytes2str(b
'.'))
449 def display_string_dump(self
, section_spec
):
450 """ Display a strings dump of a section. section_spec is either a
451 section number or a name.
453 section
= self
._section
_from
_spec
(section_spec
)
455 self
._emitline
("Section '%s' does not exist in the file!" % (
459 self
._emitline
("\nString dump of section '%s':" % bytes2str(section
.name
))
462 data
= section
.data()
465 while dataptr
< len(data
):
466 while ( dataptr
< len(data
) and
467 not (32 <= byte2int(data
[dataptr
]) <= 127)):
470 if dataptr
>= len(data
):
474 while endptr
< len(data
) and byte2int(data
[endptr
]) != 0:
478 self
._emitline
(' [%6x] %s' % (
479 dataptr
, bytes2str(data
[dataptr
:endptr
])))
484 self
._emitline
(' No strings found in this section.')
488 def display_debug_dump(self
, dump_what
):
489 """ Dump a DWARF section
491 self
._init
_dwarfinfo
()
492 if self
._dwarfinfo
is None:
495 set_global_machine_arch(self
.elffile
.get_machine_arch())
497 if dump_what
== 'info':
498 self
._dump
_debug
_info
()
499 elif dump_what
== 'decodedline':
500 self
._dump
_debug
_line
_programs
()
501 elif dump_what
== 'frames':
502 self
._dump
_debug
_frames
()
503 elif dump_what
== 'frames-interp':
504 self
._dump
_debug
_frames
_interp
()
506 self
._emitline
('debug dump not yet supported for "%s"' % dump_what
)
508 def _format_hex(self
, addr
, fieldsize
=None, fullhex
=False, lead0x
=True):
509 """ Format an address into a hexadecimal string.
512 Size of the hexadecimal field (with leading zeros to fit the
513 address into. For example with fieldsize=8, the format will
515 If None, the minimal required field size will be used.
518 If True, override fieldsize to set it to the maximal size
519 needed for the elfclass
522 If True, leading 0x is added
524 s
= '0x' if lead0x
else ''
526 fieldsize
= 8 if self
.elffile
.elfclass
== 32 else 16
527 if fieldsize
is None:
530 field
= '%' + '0%sx' % fieldsize
531 return s
+ field
% addr
533 def _section_from_spec(self
, spec
):
534 """ Retrieve a section given a "spec" (either number or name).
535 Return None if no such section exists in the file.
539 if num
< self
.elffile
.num_sections():
540 return self
.elffile
.get_section(num
)
544 # Not a number. Must be a name then
545 return self
.elffile
.get_section_by_name(str2bytes(spec
))
547 def _note_relocs_for_section(self
, section
):
548 """ If there are relocation sections pointing to the givne section,
549 emit a note about it.
551 for relsec
in self
.elffile
.iter_sections():
552 if isinstance(relsec
, RelocationSection
):
553 info_idx
= relsec
['sh_info']
554 if self
.elffile
.get_section(info_idx
) == section
:
555 self
._emitline
(' Note: This section has relocations against it, but these have NOT been applied to this dump.')
558 def _init_dwarfinfo(self
):
559 """ Initialize the DWARF info contained in the file and assign it to
561 Leave self._dwarfinfo at None if no DWARF info was found in the file
563 if self
._dwarfinfo
is not None:
566 if self
.elffile
.has_dwarf_info():
567 self
._dwarfinfo
= self
.elffile
.get_dwarf_info()
569 self
._dwarfinfo
= None
571 def _dump_debug_info(self
):
572 """ Dump the debugging info section.
574 self
._emitline
('Contents of the .debug_info section:\n')
576 # Offset of the .debug_info section in the stream
577 section_offset
= self
._dwarfinfo
.debug_info_sec
.global_offset
579 for cu
in self
._dwarfinfo
.iter_CUs():
580 self
._emitline
(' Compilation Unit @ offset %s:' %
581 self
._format
_hex
(cu
.cu_offset
))
582 self
._emitline
(' Length: %s (%s)' % (
583 self
._format
_hex
(cu
['unit_length']),
584 '%s-bit' % cu
.dwarf_format()))
585 self
._emitline
(' Version: %s' % cu
['version']),
586 self
._emitline
(' Abbrev Offset: %s' % (
587 self
._format
_hex
(cu
['debug_abbrev_offset']))),
588 self
._emitline
(' Pointer Size: %s' % cu
['address_size'])
590 # The nesting depth of each DIE within the tree of DIEs must be
591 # displayed. To implement this, a counter is incremented each time
592 # the current DIE has children, and decremented when a null die is
593 # encountered. Due to the way the DIE tree is serialized, this will
594 # correctly reflect the nesting depth
597 for die
in cu
.iter_DIEs():
598 self
._emitline
(' <%s><%x>: Abbrev Number: %s%s' % (
602 (' (%s)' % die
.tag
) if not die
.is_null() else ''))
607 for attr
in itervalues(die
.attributes
):
609 # Unknown attribute values are passed-through as integers
610 if isinstance(name
, int):
611 name
= 'Unknown AT value: %x' % name
612 self
._emitline
(' <%2x> %-18s: %s' % (
616 attr
, die
, section_offset
)))
623 def _dump_debug_line_programs(self
):
624 """ Dump the (decoded) line programs from .debug_line
625 The programs are dumped in the order of the CUs they belong to.
627 self
._emitline
('Decoded dump of debug contents of section .debug_line:\n')
629 for cu
in self
._dwarfinfo
.iter_CUs():
630 lineprogram
= self
._dwarfinfo
.line_program_for_CU(cu
)
632 cu_filename
= bytes2str(lineprogram
['file_entry'][0].name
)
633 if len(lineprogram
['include_directory']) > 0:
634 dir_index
= lineprogram
['file_entry'][0].dir_index
636 dir = lineprogram
['include_directory'][dir_index
- 1]
639 cu_filename
= '%s/%s' % (bytes2str(dir), cu_filename
)
641 self
._emitline
('CU: %s:' % cu_filename
)
642 self
._emitline
('File name Line number Starting address')
644 # Print each state's file, line and address information. For some
645 # instructions other output is needed to be compatible with
647 for entry
in lineprogram
.get_entries():
650 # Special handling for commands that don't set a new state
651 if entry
.command
== DW_LNS_set_file
:
652 file_entry
= lineprogram
['file_entry'][entry
.args
[0] - 1]
653 if file_entry
.dir_index
== 0:
655 self
._emitline
('\n./%s:[++]' % (
656 bytes2str(file_entry
.name
)))
658 self
._emitline
('\n%s/%s:' % (
659 bytes2str(lineprogram
['include_directory'][file_entry
.dir_index
- 1]),
660 bytes2str(file_entry
.name
)))
661 elif entry
.command
== DW_LNE_define_file
:
662 self
._emitline
('%s:' % (
663 bytes2str(lineprogram
['include_directory'][entry
.args
[0].dir_index
])))
664 elif not state
.end_sequence
:
665 # readelf doesn't print the state after end_sequence
666 # instructions. I think it's a bug but to be compatible
667 # I don't print them too.
668 self
._emitline
('%-35s %11d %18s' % (
669 bytes2str(lineprogram
['file_entry'][state
.file - 1].name
),
671 '0' if state
.address
== 0 else
672 self
._format
_hex
(state
.address
)))
673 if entry
.command
== DW_LNS_copy
:
674 # Another readelf oddity...
677 def _dump_debug_frames(self
):
678 """ Dump the raw frame information from .debug_frame
680 if not self
._dwarfinfo
.has_CFI():
682 self
._emitline
('Contents of the .debug_frame section:')
684 for entry
in self
._dwarfinfo
.CFI_entries():
685 if isinstance(entry
, CIE
):
686 self
._emitline
('\n%08x %08x %08x CIE' % (
687 entry
.offset
, entry
['length'], entry
['CIE_id']))
688 self
._emitline
(' Version: %d' % entry
['version'])
689 self
._emitline
(' Augmentation: "%s"' % bytes2str(entry
['augmentation']))
690 self
._emitline
(' Code alignment factor: %u' % entry
['code_alignment_factor'])
691 self
._emitline
(' Data alignment factor: %d' % entry
['data_alignment_factor'])
692 self
._emitline
(' Return address column: %d' % entry
['return_address_register'])
695 self
._emitline
('\n%08x %08x %08x FDE cie=%08x pc=%08x..%08x' % (
698 entry
['CIE_pointer'],
700 entry
['initial_location'],
701 entry
['initial_location'] + entry
['address_range']))
703 self
._emit
(describe_CFI_instructions(entry
))
706 def _dump_debug_frames_interp(self
):
707 """ Dump the interpreted (decoded) frame information from .debug_frame
709 if not self
._dwarfinfo
.has_CFI():
712 self
._emitline
('Contents of the .debug_frame section:')
714 for entry
in self
._dwarfinfo
.CFI_entries():
715 if isinstance(entry
, CIE
):
716 self
._emitline
('\n%08x %08x %08x CIE "%s" cf=%d df=%d ra=%d' % (
720 bytes2str(entry
['augmentation']),
721 entry
['code_alignment_factor'],
722 entry
['data_alignment_factor'],
723 entry
['return_address_register']))
724 ra_regnum
= entry
['return_address_register']
726 self
._emitline
('\n%08x %08x %08x FDE cie=%08x pc=%08x..%08x' % (
729 entry
['CIE_pointer'],
731 entry
['initial_location'],
732 entry
['initial_location'] + entry
['address_range']))
733 ra_regnum
= entry
.cie
['return_address_register']
735 # Print the heading row for the decoded table
737 self
._emit
(' ' if entry
.structs
.address_size
== 4 else ' ')
740 # Decode the table nad look at the registers it describes.
741 # We build reg_order here to match readelf's order. In particular,
742 # registers are sorted by their number, and the register matching
743 # ra_regnum is always listed last with a special heading.
744 decoded_table
= entry
.get_decoded()
745 reg_order
= sorted(ifilter(
746 lambda r
: r
!= ra_regnum
,
747 decoded_table
.reg_order
))
749 # Headings for the registers
750 for regnum
in reg_order
:
751 self
._emit
('%-6s' % describe_reg_name(regnum
))
752 self
._emitline
('ra ')
754 # Now include ra_regnum in reg_order to print its values similarly
755 # to the other registers.
756 reg_order
.append(ra_regnum
)
757 for line
in decoded_table
.table
:
758 self
._emit
(self
._format
_hex
(
759 line
['pc'], fullhex
=True, lead0x
=False))
760 self
._emit
(' %-9s' % describe_CFI_CFA_rule(line
['cfa']))
762 for regnum
in reg_order
:
764 s
= describe_CFI_register_rule(line
[regnum
])
767 self
._emit
('%-6s' % s
)
771 def _emit(self
, s
=''):
772 """ Emit an object to output
774 self
.output
.write(str(s
))
776 def _emitline(self
, s
=''):
777 """ Emit an object to output, followed by a newline
779 self
.output
.write(str(s
) + '\n')
782 SCRIPT_DESCRIPTION
= 'Display information about the contents of ELF format files'
783 VERSION_STRING
= '%%prog: based on pyelftools %s' % __version__
786 def main(stream
=None):
787 # parse the command-line arguments and invoke ReadElf
788 optparser
= OptionParser(
789 usage
='usage: %prog [options] <elf-file>',
790 description
=SCRIPT_DESCRIPTION
,
791 add_help_option
=False, # -h is a real option of readelf
793 version
=VERSION_STRING
)
794 optparser
.add_option('-d', '--dynamic',
795 action
='store_true', dest
='show_dynamic_tags',
796 help='Display the dynamic section')
797 optparser
.add_option('-H', '--help',
798 action
='store_true', dest
='help',
799 help='Display this information')
800 optparser
.add_option('-h', '--file-header',
801 action
='store_true', dest
='show_file_header',
802 help='Display the ELF file header')
803 optparser
.add_option('-l', '--program-headers', '--segments',
804 action
='store_true', dest
='show_program_header',
805 help='Display the program headers')
806 optparser
.add_option('-S', '--section-headers', '--sections',
807 action
='store_true', dest
='show_section_header',
808 help="Display the sections' headers")
809 optparser
.add_option('-e', '--headers',
810 action
='store_true', dest
='show_all_headers',
811 help='Equivalent to: -h -l -S')
812 optparser
.add_option('-s', '--symbols', '--syms',
813 action
='store_true', dest
='show_symbols',
814 help='Display the symbol table')
815 optparser
.add_option('-r', '--relocs',
816 action
='store_true', dest
='show_relocs',
817 help='Display the relocations (if present)')
818 optparser
.add_option('-x', '--hex-dump',
819 action
='store', dest
='show_hex_dump', metavar
='<number|name>',
820 help='Dump the contents of section <number|name> as bytes')
821 optparser
.add_option('-p', '--string-dump',
822 action
='store', dest
='show_string_dump', metavar
='<number|name>',
823 help='Dump the contents of section <number|name> as strings')
824 optparser
.add_option('--debug-dump',
825 action
='store', dest
='debug_dump_what', metavar
='<what>',
827 'Display the contents of DWARF debug sections. <what> can ' +
828 'one of {info,decodedline,frames,frames-interp}'))
830 options
, args
= optparser
.parse_args()
832 if options
.help or len(args
) == 0:
833 optparser
.print_help()
836 if options
.show_all_headers
:
837 do_file_header
= do_section_header
= do_program_header
= True
839 do_file_header
= options
.show_file_header
840 do_section_header
= options
.show_section_header
841 do_program_header
= options
.show_program_header
843 with
open(args
[0], 'rb') as file:
845 readelf
= ReadElf(file, stream
or sys
.stdout
)
847 readelf
.display_file_header()
848 if do_section_header
:
849 readelf
.display_section_headers(
850 show_heading
=not do_file_header
)
851 if do_program_header
:
852 readelf
.display_program_headers(
853 show_heading
=not do_file_header
)
854 if options
.show_dynamic_tags
:
855 readelf
.display_dynamic_tags()
856 if options
.show_symbols
:
857 readelf
.display_symbol_tables()
858 if options
.show_relocs
:
859 readelf
.display_relocations()
860 if options
.show_hex_dump
:
861 readelf
.display_hex_dump(options
.show_hex_dump
)
862 if options
.show_string_dump
:
863 readelf
.display_string_dump(options
.show_string_dump
)
864 if options
.debug_dump_what
:
865 readelf
.display_debug_dump(options
.debug_dump_what
)
866 except ELFError
as ex
:
867 sys
.stderr
.write('ELF error: %s\n' % ex
)
872 # Run 'main' redirecting its output to readelfout.txt
873 # Saves profiling information in readelf.profile
874 PROFFILE
= 'readelf.profile'
876 cProfile
.run('main(open("readelfout.txt", "w"))', PROFFILE
)
878 # Dig in some profiling stats
880 p
= pstats
.Stats(PROFFILE
)
881 p
.sort_stats('cumulative').print_stats(25)
884 #-------------------------------------------------------------------------------
885 if __name__
== '__main__':