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 #-------------------------------------------------------------------------------
15 # Note: zip has different behaviour between Python 2.x and 3.x.
16 # - Using izip ensures compatibility.
18 from itertools
import izip
22 # For running from development directory. It should take precedence over the
23 # installed pyelftools.
24 sys
.path
.insert(0, '.')
27 from elftools
import __version__
28 from elftools
.common
.exceptions
import ELFError
29 from elftools
.common
.py3compat
import (
30 ifilter
, byte2int
, bytes2str
, itervalues
, str2bytes
, iterbytes
)
31 from elftools
.elf
.elffile
import ELFFile
32 from elftools
.elf
.dynamic
import DynamicSection
, DynamicSegment
33 from elftools
.elf
.enums
import ENUM_D_TAG
34 from elftools
.elf
.segments
import InterpSegment
35 from elftools
.elf
.sections
import (
36 NoteSection
, SymbolTableSection
, SymbolTableIndexSection
38 from elftools
.elf
.gnuversions
import (
39 GNUVerSymSection
, GNUVerDefSection
,
42 from elftools
.elf
.relocation
import RelocationSection
43 from elftools
.elf
.descriptions
import (
44 describe_ei_class
, describe_ei_data
, describe_ei_version
,
45 describe_ei_osabi
, describe_e_type
, describe_e_machine
,
46 describe_e_version_numeric
, describe_p_type
, describe_p_flags
,
47 describe_rh_flags
, describe_sh_type
, describe_sh_flags
,
48 describe_symbol_type
, describe_symbol_bind
, describe_symbol_visibility
,
49 describe_symbol_shndx
, describe_reloc_type
, describe_dyn_tag
,
50 describe_dt_flags
, describe_dt_flags_1
, describe_ver_flags
, describe_note
,
51 describe_attr_tag_arm
, describe_symbol_other
53 from elftools
.elf
.constants
import E_FLAGS
54 from elftools
.elf
.constants
import E_FLAGS_MASKS
55 from elftools
.elf
.constants
import SH_FLAGS
56 from elftools
.elf
.constants
import SHN_INDICES
57 from elftools
.dwarf
.dwarfinfo
import DWARFInfo
58 from elftools
.dwarf
.descriptions
import (
59 describe_reg_name
, describe_attr_value
, set_global_machine_arch
,
60 describe_CFI_instructions
, describe_CFI_register_rule
,
61 describe_CFI_CFA_rule
, describe_DWARF_expr
63 from elftools
.dwarf
.constants
import (
64 DW_LNS_copy
, DW_LNS_set_file
, DW_LNE_define_file
)
65 from elftools
.dwarf
.locationlists
import LocationParser
, LocationEntry
, LocationViewPair
, BaseAddressEntry
66 from elftools
.dwarf
.ranges
import RangeEntry
# ranges.BaseAddressEntry collides with the one above
67 import elftools
.dwarf
.ranges
68 from elftools
.dwarf
.callframe
import CIE
, FDE
, ZERO
69 from elftools
.ehabi
.ehabiinfo
import CorruptEHABIEntry
, CannotUnwindEHABIEntry
, GenericEHABIEntry
70 from elftools
.dwarf
.enums
import ENUM_DW_UT
73 top_die
= cu
.get_top_DIE()
74 attr
= top_die
.attributes
75 if 'DW_AT_low_pc' in attr
:
76 return attr
['DW_AT_low_pc'].value
77 elif 'DW_AT_entry_pc' in attr
:
78 return attr
['DW_AT_entry_pc'].value
80 raise ValueError("Can't find the base IP (low_pc) for a CU")
82 class ReadElf(object):
83 """ display_* methods are used to emit output into the output stream
85 def __init__(self
, file, output
):
87 stream object with the ELF file to read
90 output stream to write to
92 self
.elffile
= ELFFile(file)
95 # Lazily initialized if a debug dump is requested
96 self
._dwarfinfo
= None
98 self
._versioninfo
= None
100 self
._shndx
_sections
= None
102 def display_file_header(self
):
103 """ Display the ELF file header
105 self
._emitline
('ELF Header:')
106 self
._emit
(' Magic: ')
107 self
._emit
(' '.join('%2.2x' % byte2int(b
)
108 for b
in self
.elffile
.e_ident_raw
))
110 header
= self
.elffile
.header
111 e_ident
= header
['e_ident']
112 self
._emitline
(' Class: %s' %
113 describe_ei_class(e_ident
['EI_CLASS']))
114 self
._emitline
(' Data: %s' %
115 describe_ei_data(e_ident
['EI_DATA']))
116 self
._emitline
(' Version: %s' %
117 describe_ei_version(e_ident
['EI_VERSION']))
118 self
._emitline
(' OS/ABI: %s' %
119 describe_ei_osabi(e_ident
['EI_OSABI']))
120 self
._emitline
(' ABI Version: %d' %
121 e_ident
['EI_ABIVERSION'])
122 self
._emitline
(' Type: %s' %
123 describe_e_type(header
['e_type'], self
.elffile
))
124 self
._emitline
(' Machine: %s' %
125 describe_e_machine(header
['e_machine']))
126 self
._emitline
(' Version: %s' %
127 describe_e_version_numeric(header
['e_version']))
128 self
._emitline
(' Entry point address: %s' %
129 self
._format
_hex
(header
['e_entry']))
130 self
._emit
(' Start of program headers: %s' %
132 self
._emitline
(' (bytes into file)')
133 self
._emit
(' Start of section headers: %s' %
135 self
._emitline
(' (bytes into file)')
136 self
._emitline
(' Flags: %s%s' %
137 (self
._format
_hex
(header
['e_flags']),
138 self
.decode_flags(header
['e_flags'])))
139 self
._emitline
(' Size of this header: %s (bytes)' %
141 self
._emitline
(' Size of program headers: %s (bytes)' %
142 header
['e_phentsize'])
143 self
._emitline
(' Number of program headers: %s' %
145 self
._emitline
(' Size of section headers: %s (bytes)' %
146 header
['e_shentsize'])
147 self
._emit
(' Number of section headers: %s' %
149 if header
['e_shnum'] == 0 and self
.elffile
.num_sections() != 0:
150 self
._emitline
(' (%d)' % self
.elffile
.num_sections())
153 self
._emit
(' Section header string table index: %s' %
154 header
['e_shstrndx'])
155 if header
['e_shstrndx'] == SHN_INDICES
.SHN_XINDEX
:
156 self
._emitline
(' (%d)' % self
.elffile
.get_shstrndx())
160 def decode_flags(self
, flags
):
162 if self
.elffile
['e_machine'] == "EM_ARM":
163 eabi
= flags
& E_FLAGS
.EF_ARM_EABIMASK
164 flags
&= ~E_FLAGS
.EF_ARM_EABIMASK
166 if flags
& E_FLAGS
.EF_ARM_RELEXEC
:
167 description
+= ', relocatable executabl'
168 flags
&= ~E_FLAGS
.EF_ARM_RELEXEC
170 if eabi
== E_FLAGS
.EF_ARM_EABI_VER5
:
171 EF_ARM_KNOWN_FLAGS
= E_FLAGS
.EF_ARM_ABI_FLOAT_SOFT|E_FLAGS
.EF_ARM_ABI_FLOAT_HARD|E_FLAGS
.EF_ARM_LE8|E_FLAGS
.EF_ARM_BE8
172 description
+= ', Version5 EABI'
173 if flags
& E_FLAGS
.EF_ARM_ABI_FLOAT_SOFT
:
174 description
+= ", soft-float ABI"
175 elif flags
& E_FLAGS
.EF_ARM_ABI_FLOAT_HARD
:
176 description
+= ", hard-float ABI"
178 if flags
& E_FLAGS
.EF_ARM_BE8
:
179 description
+= ", BE8"
180 elif flags
& E_FLAGS
.EF_ARM_LE8
:
181 description
+= ", LE8"
183 if flags
& ~EF_ARM_KNOWN_FLAGS
:
184 description
+= ', <unknown>'
186 description
+= ', <unrecognized EABI>'
188 elif self
.elffile
['e_machine'] == 'EM_PPC64':
189 if flags
& E_FLAGS
.EF_PPC64_ABI_V2
:
190 description
+= ', abiv2'
192 elif self
.elffile
['e_machine'] == "EM_MIPS":
193 if flags
& E_FLAGS
.EF_MIPS_NOREORDER
:
194 description
+= ", noreorder"
195 if flags
& E_FLAGS
.EF_MIPS_PIC
:
196 description
+= ", pic"
197 if flags
& E_FLAGS
.EF_MIPS_CPIC
:
198 description
+= ", cpic"
199 if (flags
& E_FLAGS
.EF_MIPS_ABI2
):
200 description
+= ", abi2"
201 if (flags
& E_FLAGS
.EF_MIPS_32BITMODE
):
202 description
+= ", 32bitmode"
203 if (flags
& E_FLAGS_MASKS
.EFM_MIPS_ABI_O32
):
204 description
+= ", o32"
205 elif (flags
& E_FLAGS_MASKS
.EFM_MIPS_ABI_O64
):
206 description
+= ", o64"
207 elif (flags
& E_FLAGS_MASKS
.EFM_MIPS_ABI_EABI32
):
208 description
+= ", eabi32"
209 elif (flags
& E_FLAGS_MASKS
.EFM_MIPS_ABI_EABI64
):
210 description
+= ", eabi64"
211 if (flags
& E_FLAGS
.EF_MIPS_ARCH
) == E_FLAGS
.EF_MIPS_ARCH_1
:
212 description
+= ", mips1"
213 if (flags
& E_FLAGS
.EF_MIPS_ARCH
) == E_FLAGS
.EF_MIPS_ARCH_2
:
214 description
+= ", mips2"
215 if (flags
& E_FLAGS
.EF_MIPS_ARCH
) == E_FLAGS
.EF_MIPS_ARCH_3
:
216 description
+= ", mips3"
217 if (flags
& E_FLAGS
.EF_MIPS_ARCH
) == E_FLAGS
.EF_MIPS_ARCH_4
:
218 description
+= ", mips4"
219 if (flags
& E_FLAGS
.EF_MIPS_ARCH
) == E_FLAGS
.EF_MIPS_ARCH_5
:
220 description
+= ", mips5"
221 if (flags
& E_FLAGS
.EF_MIPS_ARCH
) == E_FLAGS
.EF_MIPS_ARCH_32R2
:
222 description
+= ", mips32r2"
223 if (flags
& E_FLAGS
.EF_MIPS_ARCH
) == E_FLAGS
.EF_MIPS_ARCH_64R2
:
224 description
+= ", mips64r2"
225 if (flags
& E_FLAGS
.EF_MIPS_ARCH
) == E_FLAGS
.EF_MIPS_ARCH_32
:
226 description
+= ", mips32"
227 if (flags
& E_FLAGS
.EF_MIPS_ARCH
) == E_FLAGS
.EF_MIPS_ARCH_64
:
228 description
+= ", mips64"
232 def display_program_headers(self
, show_heading
=True):
233 """ Display the ELF program headers.
234 If show_heading is True, displays the heading for this information
235 (Elf file type is...)
238 if self
.elffile
.num_segments() == 0:
239 self
._emitline
('There are no program headers in this file.')
242 elfheader
= self
.elffile
.header
244 self
._emitline
('Elf file type is %s' %
245 describe_e_type(elfheader
['e_type'], self
.elffile
))
246 self
._emitline
('Entry point is %s' %
247 self
._format
_hex
(elfheader
['e_entry']))
248 # readelf weirness - why isn't e_phoff printed as hex? (for section
250 self
._emitline
('There are %s program headers, starting at offset %s' % (
251 self
.elffile
.num_segments(), elfheader
['e_phoff']))
254 self
._emitline
('Program Headers:')
256 # Now comes the table of program headers with their attributes. Note
257 # that due to different formatting constraints of 32-bit and 64-bit
258 # addresses, there are some conditions on elfclass here.
260 # First comes the table heading
262 if self
.elffile
.elfclass
== 32:
263 self
._emitline
(' Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align')
265 self
._emitline
(' Type Offset VirtAddr PhysAddr')
266 self
._emitline
(' FileSiz MemSiz Flags Align')
270 for segment
in self
.elffile
.iter_segments():
271 self
._emit
(' %-14s ' % describe_p_type(segment
['p_type']))
273 if self
.elffile
.elfclass
== 32:
274 self
._emitline
('%s %s %s %s %s %-3s %s' % (
275 self
._format
_hex
(segment
['p_offset'], fieldsize
=6),
276 self
._format
_hex
(segment
['p_vaddr'], fullhex
=True),
277 self
._format
_hex
(segment
['p_paddr'], fullhex
=True),
278 self
._format
_hex
(segment
['p_filesz'], fieldsize
=5),
279 self
._format
_hex
(segment
['p_memsz'], fieldsize
=5),
280 describe_p_flags(segment
['p_flags']),
281 self
._format
_hex
(segment
['p_align'])))
283 self
._emitline
('%s %s %s' % (
284 self
._format
_hex
(segment
['p_offset'], fullhex
=True),
285 self
._format
_hex
(segment
['p_vaddr'], fullhex
=True),
286 self
._format
_hex
(segment
['p_paddr'], fullhex
=True)))
287 self
._emitline
(' %s %s %-3s %s' % (
288 self
._format
_hex
(segment
['p_filesz'], fullhex
=True),
289 self
._format
_hex
(segment
['p_memsz'], fullhex
=True),
290 describe_p_flags(segment
['p_flags']),
291 # lead0x set to False for p_align, to mimic readelf.
292 # No idea why the difference from 32-bit mode :-|
293 self
._format
_hex
(segment
['p_align'], lead0x
=False)))
295 if isinstance(segment
, InterpSegment
):
296 self
._emitline
(' [Requesting program interpreter: %s]' %
297 segment
.get_interp_name())
299 # Sections to segments mapping
301 if self
.elffile
.num_sections() == 0:
302 # No sections? We're done
305 self
._emitline
('\n Section to Segment mapping:')
306 self
._emitline
(' Segment Sections...')
308 for nseg
, segment
in enumerate(self
.elffile
.iter_segments()):
309 self
._emit
(' %2.2d ' % nseg
)
311 for section
in self
.elffile
.iter_sections():
312 if ( not section
.is_null() and
313 not ((section
['sh_flags'] & SH_FLAGS
.SHF_TLS
) != 0 and
314 section
['sh_type'] == 'SHT_NOBITS' and
315 segment
['p_type'] != 'PT_TLS') and
316 segment
.section_in_segment(section
)):
317 self
._emit
('%s ' % section
.name
)
321 def display_section_headers(self
, show_heading
=True):
322 """ Display the ELF section headers
324 elfheader
= self
.elffile
.header
326 self
._emitline
('There are %s section headers, starting at offset %s' % (
327 elfheader
['e_shnum'], self
._format
_hex
(elfheader
['e_shoff'])))
329 if self
.elffile
.num_sections() == 0:
330 self
._emitline
('There are no sections in this file.')
333 self
._emitline
('\nSection Header%s:' % (
334 's' if self
.elffile
.num_sections() > 1 else ''))
336 # Different formatting constraints of 32-bit and 64-bit addresses
338 if self
.elffile
.elfclass
== 32:
339 self
._emitline
(' [Nr] Name Type Addr Off Size ES Flg Lk Inf Al')
341 self
._emitline
(' [Nr] Name Type Address Offset')
342 self
._emitline
(' Size EntSize Flags Link Info Align')
346 for nsec
, section
in enumerate(self
.elffile
.iter_sections()):
347 self
._emit
(' [%2u] %-17.17s %-15.15s ' % (
348 nsec
, section
.name
, describe_sh_type(section
['sh_type'])))
350 if self
.elffile
.elfclass
== 32:
351 self
._emitline
('%s %s %s %s %3s %2s %3s %2s' % (
352 self
._format
_hex
(section
['sh_addr'], fieldsize
=8, lead0x
=False),
353 self
._format
_hex
(section
['sh_offset'], fieldsize
=6, lead0x
=False),
354 self
._format
_hex
(section
['sh_size'], fieldsize
=6, lead0x
=False),
355 self
._format
_hex
(section
['sh_entsize'], fieldsize
=2, lead0x
=False),
356 describe_sh_flags(section
['sh_flags']),
357 section
['sh_link'], section
['sh_info'],
358 section
['sh_addralign']))
360 self
._emitline
(' %s %s' % (
361 self
._format
_hex
(section
['sh_addr'], fullhex
=True, lead0x
=False),
362 self
._format
_hex
(section
['sh_offset'],
363 fieldsize
=16 if section
['sh_offset'] > 0xffffffff else 8,
365 self
._emitline
(' %s %s %3s %2s %3s %s' % (
366 self
._format
_hex
(section
['sh_size'], fullhex
=True, lead0x
=False),
367 self
._format
_hex
(section
['sh_entsize'], fullhex
=True, lead0x
=False),
368 describe_sh_flags(section
['sh_flags']),
369 section
['sh_link'], section
['sh_info'],
370 section
['sh_addralign']))
372 self
._emitline
('Key to Flags:')
373 self
._emitline
(' W (write), A (alloc), X (execute), M (merge),'
374 ' S (strings), I (info),')
375 self
._emitline
(' L (link order), O (extra OS processing required),'
376 ' G (group), T (TLS),')
377 self
._emitline
(' C (compressed), x (unknown), o (OS specific),'
380 if self
.elffile
['e_machine'] == 'EM_ARM':
381 self
._emit
('y (purecode), ')
382 self
._emitline
('p (processor specific)')
384 def display_symbol_tables(self
):
385 """ Display the symbol tables contained in the file
387 self
._init
_versioninfo
()
389 symbol_tables
= [(idx
, s
) for idx
, s
in enumerate(self
.elffile
.iter_sections())
390 if isinstance(s
, SymbolTableSection
)]
392 if not symbol_tables
and self
.elffile
.num_sections() == 0:
394 self
._emitline
('Dynamic symbol information is not available for'
395 ' displaying symbols.')
397 for section_index
, section
in symbol_tables
:
398 if not isinstance(section
, SymbolTableSection
):
401 if section
['sh_entsize'] == 0:
402 self
._emitline
("\nSymbol table '%s' has a sh_entsize of zero!" % (
406 self
._emitline
("\nSymbol table '%s' contains %d %s:" % (
408 section
.num_symbols(),
409 'entry' if section
.num_symbols() == 1 else 'entries'))
411 if self
.elffile
.elfclass
== 32:
412 self
._emitline
(' Num: Value Size Type Bind Vis Ndx Name')
414 self
._emitline
(' Num: Value Size Type Bind Vis Ndx Name')
416 for nsym
, symbol
in enumerate(section
.iter_symbols()):
418 # readelf doesn't display version info for Solaris versioning
419 if (section
['sh_type'] == 'SHT_DYNSYM' and
420 self
._versioninfo
['type'] == 'GNU'):
421 version
= self
._symbol
_version
(nsym
)
422 if (version
['name'] != symbol
.name
and
423 version
['index'] not in ('VER_NDX_LOCAL',
425 if version
['filename']:
427 version_info
= '@%(name)s (%(index)i)' % version
430 if version
['hidden']:
431 version_info
= '@%(name)s' % version
433 version_info
= '@@%(name)s' % version
435 symbol_name
= symbol
.name
436 # Print section names for STT_SECTION symbols as readelf does
437 if (symbol
['st_info']['type'] == 'STT_SECTION'
438 and symbol
['st_shndx'] < self
.elffile
.num_sections()
439 and symbol
['st_name'] == 0):
440 symbol_name
= self
.elffile
.get_section(symbol
['st_shndx']).name
442 # symbol names are truncated to 25 chars, similarly to readelf
443 self
._emitline
('%6d: %s %s %-7s %-6s %-7s %4s %.25s%s' % (
446 symbol
['st_value'], fullhex
=True, lead0x
=False),
447 "%5d" % symbol
['st_size'] if symbol
['st_size'] < 100000 else hex(symbol
['st_size']),
448 describe_symbol_type(symbol
['st_info']['type']),
449 describe_symbol_bind(symbol
['st_info']['bind']),
450 describe_symbol_other(symbol
['st_other']),
451 describe_symbol_shndx(self
._get
_symbol
_shndx
(symbol
,
457 def display_dynamic_tags(self
):
458 """ Display the dynamic tags contained in the file
460 has_dynamic_sections
= False
461 for section
in self
.elffile
.iter_sections():
462 if not isinstance(section
, DynamicSection
):
465 has_dynamic_sections
= True
466 self
._emitline
("\nDynamic section at offset %s contains %d %s:" % (
467 self
._format
_hex
(section
['sh_offset']),
469 'entry' if section
.num_tags() == 1 else 'entries'))
470 self
._emitline
(" Tag Type Name/Value")
472 padding
= 20 + (8 if self
.elffile
.elfclass
== 32 else 0)
473 for tag
in section
.iter_tags():
474 if tag
.entry
.d_tag
== 'DT_NEEDED':
475 parsed
= 'Shared library: [%s]' % tag
.needed
476 elif tag
.entry
.d_tag
== 'DT_RPATH':
477 parsed
= 'Library rpath: [%s]' % tag
.rpath
478 elif tag
.entry
.d_tag
== 'DT_RUNPATH':
479 parsed
= 'Library runpath: [%s]' % tag
.runpath
480 elif tag
.entry
.d_tag
== 'DT_SONAME':
481 parsed
= 'Library soname: [%s]' % tag
.soname
482 elif tag
.entry
.d_tag
.endswith(('SZ', 'ENT')):
483 parsed
= '%i (bytes)' % tag
['d_val']
484 elif tag
.entry
.d_tag
== 'DT_FLAGS':
485 parsed
= describe_dt_flags(tag
.entry
.d_val
)
486 elif tag
.entry
.d_tag
== 'DT_FLAGS_1':
487 parsed
= 'Flags: %s' % describe_dt_flags_1(tag
.entry
.d_val
)
488 elif tag
.entry
.d_tag
.endswith(('NUM', 'COUNT')):
489 parsed
= '%i' % tag
['d_val']
490 elif tag
.entry
.d_tag
== 'DT_PLTREL':
491 s
= describe_dyn_tag(tag
.entry
.d_val
)
492 if s
.startswith('DT_'):
495 elif tag
.entry
.d_tag
== 'DT_MIPS_FLAGS':
496 parsed
= describe_rh_flags(tag
.entry
.d_val
)
497 elif tag
.entry
.d_tag
in ('DT_MIPS_SYMTABNO',
498 'DT_MIPS_LOCAL_GOTNO'):
499 parsed
= str(tag
.entry
.d_val
)
501 parsed
= '%#x' % tag
['d_val']
503 self
._emitline
(" %s %-*s %s" % (
504 self
._format
_hex
(ENUM_D_TAG
.get(tag
.entry
.d_tag
, tag
.entry
.d_tag
),
505 fullhex
=True, lead0x
=True),
507 '(%s)' % (tag
.entry
.d_tag
[3:],),
509 if not has_dynamic_sections
:
510 self
._emitline
("\nThere is no dynamic section in this file.")
512 def display_notes(self
):
513 """ Display the notes contained in the file
515 for section
in self
.elffile
.iter_sections():
516 if isinstance(section
, NoteSection
):
517 for note
in section
.iter_notes():
518 self
._emitline
("\nDisplaying notes found in: {}".format(
520 self
._emitline
(' Owner Data size Description')
521 self
._emitline
(' %s %s\t%s' % (
522 note
['n_name'].ljust(20),
523 self
._format
_hex
(note
['n_descsz'], fieldsize
=8),
524 describe_note(note
)))
526 def display_relocations(self
):
527 """ Display the relocations contained in the file
529 has_relocation_sections
= False
530 for section
in self
.elffile
.iter_sections():
531 if not isinstance(section
, RelocationSection
):
534 has_relocation_sections
= True
535 self
._emitline
("\nRelocation section '%.128s' at offset %s contains %d %s:" % (
537 self
._format
_hex
(section
['sh_offset']),
538 section
.num_relocations(),
539 'entry' if section
.num_relocations() == 1 else 'entries'))
540 if section
.is_RELA():
541 self
._emitline
(" Offset Info Type Sym. Value Sym. Name + Addend")
543 self
._emitline
(" Offset Info Type Sym.Value Sym. Name")
545 # The symbol table section pointed to in sh_link
546 symtable
= self
.elffile
.get_section(section
['sh_link'])
548 for rel
in section
.iter_relocations():
549 hexwidth
= 8 if self
.elffile
.elfclass
== 32 else 12
550 self
._emit
('%s %s %-17.17s' % (
551 self
._format
_hex
(rel
['r_offset'],
552 fieldsize
=hexwidth
, lead0x
=False),
553 self
._format
_hex
(rel
['r_info'],
554 fieldsize
=hexwidth
, lead0x
=False),
556 rel
['r_info_type'], self
.elffile
)))
558 if rel
['r_info_sym'] == 0:
559 if section
.is_RELA():
560 fieldsize
= 8 if self
.elffile
.elfclass
== 32 else 16
561 addend
= self
._format
_hex
(rel
['r_addend'], lead0x
=False)
562 self
._emit
(' %s %s' % (' ' * fieldsize
, addend
))
566 symbol
= symtable
.get_symbol(rel
['r_info_sym'])
567 # Some symbols have zero 'st_name', so instead what's used
568 # is the name of the section they point at. Truncate symbol
569 # names (excluding version info) to 22 chars, similarly to
571 if symbol
['st_name'] == 0:
572 symsecidx
= self
._get
_symbol
_shndx
(symbol
,
575 symsec
= self
.elffile
.get_section(symsecidx
)
576 symbol_name
= symsec
.name
579 symbol_name
= symbol
.name
580 version
= self
._symbol
_version
(rel
['r_info_sym'])
581 version
= (version
['name']
582 if version
and version
['name'] else '')
583 symbol_name
= '%.22s' % symbol_name
585 symbol_name
+= '@' + version
587 self
._emit
(' %s %s' % (
590 fullhex
=True, lead0x
=False),
592 if section
.is_RELA():
593 self
._emit
(' %s %x' % (
594 '+' if rel
['r_addend'] >= 0 else '-',
595 abs(rel
['r_addend'])))
598 # Emit the two additional relocation types for ELF64 MIPS
600 if (self
.elffile
.elfclass
== 64 and
601 self
.elffile
['e_machine'] == 'EM_MIPS'):
603 rtype
= rel
['r_info_type%s' % i
]
604 self
._emit
(' Type%s: %s' % (
606 describe_reloc_type(rtype
, self
.elffile
)))
609 if not has_relocation_sections
:
610 self
._emitline
('\nThere are no relocations in this file.')
612 def display_arm_unwind(self
):
613 if not self
.elffile
.has_ehabi_info():
614 self
._emitline
('There are no .ARM.idx sections in this file.')
616 for ehabi_info
in self
.elffile
.get_ehabi_infos():
617 # Unwind section '.ARM.exidx' at offset 0x203e8 contains 1009 entries:
618 self
._emitline
("\nUnwind section '%s' at offset 0x%x contains %d %s" % (
619 ehabi_info
.section_name(),
620 ehabi_info
.section_offset(),
621 ehabi_info
.num_entry(),
622 'entry' if ehabi_info
.num_entry() == 1 else 'entries'))
624 for i
in range(ehabi_info
.num_entry()):
625 entry
= ehabi_info
.get_entry(i
)
627 self
._emitline
("Entry %d:" % i
)
628 if isinstance(entry
, CorruptEHABIEntry
):
629 self
._emitline
(" [corrupt] %s" % entry
.reason
)
631 self
._emit
(" Function offset 0x%x: " % entry
.function_offset
)
632 if isinstance(entry
, CannotUnwindEHABIEntry
):
633 self
._emitline
("[cantunwind]")
635 elif entry
.eh_table_offset
:
636 self
._emitline
("@0x%x" % entry
.eh_table_offset
)
638 self
._emitline
("Compact (inline)")
639 if isinstance(entry
, GenericEHABIEntry
):
640 self
._emitline
(" Personality: 0x%x" % entry
.personality
)
642 self
._emitline
(" Compact model index: %d" % entry
.personality
)
643 for mnemonic_item
in entry
.mnmemonic_array():
645 self
._emitline
(mnemonic_item
)
647 def display_version_info(self
):
648 """ Display the version info contained in the file
650 self
._init
_versioninfo
()
652 if not self
._versioninfo
['type']:
653 self
._emitline
("\nNo version information found in this file.")
656 for section
in self
.elffile
.iter_sections():
657 if isinstance(section
, GNUVerSymSection
):
658 self
._print
_version
_section
_header
(section
, 'Version symbols')
659 num_symbols
= section
.num_symbols()
661 # Symbol version info are printed four by four entries
662 for idx_by_4
in range(0, num_symbols
, 4):
664 self
._emit
(' %03x:' % idx_by_4
)
666 for idx
in range(idx_by_4
, min(idx_by_4
+ 4, num_symbols
)):
668 symbol_version
= self
._symbol
_version
(idx
)
669 if symbol_version
['index'] == 'VER_NDX_LOCAL':
671 version_name
= '(*local*)'
672 elif symbol_version
['index'] == 'VER_NDX_GLOBAL':
674 version_name
= '(*global*)'
676 version_index
= symbol_version
['index']
677 version_name
= '(%(name)s)' % symbol_version
679 visibility
= 'h' if symbol_version
['hidden'] else ' '
681 self
._emit
('%4x%s%-13s' % (
682 version_index
, visibility
, version_name
))
686 elif isinstance(section
, GNUVerDefSection
):
687 self
._print
_version
_section
_header
(
688 section
, 'Version definition', indent
=2)
691 for verdef
, verdaux_iter
in section
.iter_versions():
692 verdaux
= next(verdaux_iter
)
695 if verdef
['vd_flags']:
696 flags
= describe_ver_flags(verdef
['vd_flags'])
697 # Mimic exactly the readelf output
702 self
._emitline
(' %s: Rev: %i Flags: %s Index: %i'
703 ' Cnt: %i Name: %s' % (
704 self
._format
_hex
(offset
, fieldsize
=6,
706 verdef
['vd_version'], flags
, verdef
['vd_ndx'],
707 verdef
['vd_cnt'], name
))
710 offset
+ verdef
['vd_aux'] + verdaux
['vda_next'])
711 for idx
, verdaux
in enumerate(verdaux_iter
, start
=1):
712 self
._emitline
(' %s: Parent %i: %s' %
713 (self
._format
_hex
(verdaux_offset
, fieldsize
=4),
715 verdaux_offset
+= verdaux
['vda_next']
717 offset
+= verdef
['vd_next']
719 elif isinstance(section
, GNUVerNeedSection
):
720 self
._print
_version
_section
_header
(section
, 'Version needs')
723 for verneed
, verneed_iter
in section
.iter_versions():
725 self
._emitline
(' %s: Version: %i File: %s Cnt: %i' % (
726 self
._format
_hex
(offset
, fieldsize
=6,
728 verneed
['vn_version'], verneed
.name
,
731 vernaux_offset
= offset
+ verneed
['vn_aux']
732 for idx
, vernaux
in enumerate(verneed_iter
, start
=1):
733 if vernaux
['vna_flags']:
734 flags
= describe_ver_flags(vernaux
['vna_flags'])
735 # Mimic exactly the readelf output
741 ' %s: Name: %s Flags: %s Version: %i' % (
742 self
._format
_hex
(vernaux_offset
, fieldsize
=4),
744 vernaux
['vna_other']))
746 vernaux_offset
+= vernaux
['vna_next']
748 offset
+= verneed
['vn_next']
750 def display_arch_specific(self
):
751 """ Display the architecture-specific info contained in the file.
753 if self
.elffile
['e_machine'] == 'EM_ARM':
754 self
._display
_arch
_specific
_arm
()
756 def display_hex_dump(self
, section_spec
):
757 """ Display a hex dump of a section. section_spec is either a section
760 section
= self
._section
_from
_spec
(section_spec
)
762 # readelf prints the warning to stderr. Even though stderrs are not compared
763 # in tests, we comply with that behavior.
764 sys
.stderr
.write('readelf: Warning: Section \'%s\' was not dumped because it does not exist!\n' % (
767 if section
['sh_type'] == 'SHT_NOBITS':
768 self
._emitline
("\nSection '%s' has no data to dump." % (
772 self
._emitline
("\nHex dump of section '%s':" % section
.name
)
773 self
._note
_relocs
_for
_section
(section
)
774 addr
= section
['sh_addr']
775 data
= section
.data()
778 while dataptr
< len(data
):
779 bytesleft
= len(data
) - dataptr
780 # chunks of 16 bytes per line
781 linebytes
= 16 if bytesleft
> 16 else bytesleft
783 self
._emit
(' %s ' % self
._format
_hex
(addr
, fieldsize
=8))
786 self
._emit
('%2.2x' % byte2int(data
[dataptr
+ i
]))
792 for i
in range(linebytes
):
793 c
= data
[dataptr
+ i
: dataptr
+ i
+ 1]
794 if byte2int(c
[0]) >= 32 and byte2int(c
[0]) < 0x7f:
795 self
._emit
(bytes2str(c
))
797 self
._emit
(bytes2str(b
'.'))
805 def display_string_dump(self
, section_spec
):
806 """ Display a strings dump of a section. section_spec is either a
807 section number or a name.
809 section
= self
._section
_from
_spec
(section_spec
)
811 # readelf prints the warning to stderr. Even though stderrs are not compared
812 # in tests, we comply with that behavior.
813 sys
.stderr
.write('readelf.py: Warning: Section \'%s\' was not dumped because it does not exist!\n' % (
816 if section
['sh_type'] == 'SHT_NOBITS':
817 self
._emitline
("\nSection '%s' has no data to dump." % (
821 self
._emitline
("\nString dump of section '%s':" % section
.name
)
824 data
= section
.data()
827 while dataptr
< len(data
):
828 while ( dataptr
< len(data
) and
829 not (32 <= byte2int(data
[dataptr
]) <= 127)):
832 if dataptr
>= len(data
):
836 while endptr
< len(data
) and byte2int(data
[endptr
]) != 0:
840 self
._emitline
(' [%6x] %s' % (
841 dataptr
, bytes2str(data
[dataptr
:endptr
])))
846 self
._emitline
(' No strings found in this section.')
850 def display_debug_dump(self
, dump_what
):
851 """ Dump a DWARF section
853 self
._init
_dwarfinfo
()
854 if self
._dwarfinfo
is None:
857 set_global_machine_arch(self
.elffile
.get_machine_arch())
859 if dump_what
== 'info':
860 self
._dump
_debug
_info
()
861 elif dump_what
== 'decodedline':
862 self
._dump
_debug
_line
_programs
()
863 elif dump_what
== 'frames':
864 self
._dump
_debug
_frames
()
865 elif dump_what
== 'frames-interp':
866 self
._dump
_debug
_frames
_interp
()
867 elif dump_what
== 'aranges':
868 self
._dump
_debug
_aranges
()
869 elif dump_what
in { 'pubtypes', 'pubnames' }:
870 self
._dump
_debug
_namelut
(dump_what
)
871 elif dump_what
== 'loc':
872 self
._dump
_debug
_locations
()
873 elif dump_what
== 'Ranges':
874 self
._dump
_debug
_ranges
()
876 self
._emitline
('debug dump not yet supported for "%s"' % dump_what
)
878 def _format_hex(self
, addr
, fieldsize
=None, fullhex
=False, lead0x
=True,
880 """ Format an address into a hexadecimal string.
883 Size of the hexadecimal field (with leading zeros to fit the
884 address into. For example with fieldsize=8, the format will
886 If None, the minimal required field size will be used.
889 If True, override fieldsize to set it to the maximal size
890 needed for the elfclass
893 If True, leading 0x is added
896 If True, override lead0x to emulate the alternate
897 hexadecimal form specified in format string with the #
898 character: only non-zero values are prefixed with 0x.
899 This form is used by readelf.
908 s
= '0x' if lead0x
else ''
910 fieldsize
= 8 if self
.elffile
.elfclass
== 32 else 16
911 if fieldsize
is None:
914 field
= '%' + '0%sx' % fieldsize
915 return s
+ field
% addr
917 def _print_version_section_header(self
, version_section
, name
, lead0x
=True,
919 """ Print a section header of one version related section (versym,
920 verneed or verdef) with some options to accomodate readelf
921 little differences between each header (e.g. indentation
924 if hasattr(version_section
, 'num_versions'):
925 num_entries
= version_section
.num_versions()
927 num_entries
= version_section
.num_symbols()
929 self
._emitline
("\n%s section '%s' contains %d %s:" % (
930 name
, version_section
.name
, num_entries
,
931 'entry' if num_entries
== 1 else 'entries'))
932 self
._emitline
('%sAddr: %s Offset: %s Link: %i (%s)' % (
935 version_section
['sh_addr'], fieldsize
=16, lead0x
=lead0x
),
937 version_section
['sh_offset'], fieldsize
=6, lead0x
=True),
938 version_section
['sh_link'],
939 self
.elffile
.get_section(version_section
['sh_link']).name
943 def _init_versioninfo(self
):
944 """ Search and initialize informations about version related sections
945 and the kind of versioning used (GNU or Solaris).
947 if self
._versioninfo
is not None:
950 self
._versioninfo
= {'versym': None, 'verdef': None,
951 'verneed': None, 'type': None}
953 for section
in self
.elffile
.iter_sections():
954 if isinstance(section
, GNUVerSymSection
):
955 self
._versioninfo
['versym'] = section
956 elif isinstance(section
, GNUVerDefSection
):
957 self
._versioninfo
['verdef'] = section
958 elif isinstance(section
, GNUVerNeedSection
):
959 self
._versioninfo
['verneed'] = section
960 elif isinstance(section
, DynamicSection
):
961 for tag
in section
.iter_tags():
962 if tag
['d_tag'] == 'DT_VERSYM':
963 self
._versioninfo
['type'] = 'GNU'
966 if not self
._versioninfo
['type'] and (
967 self
._versioninfo
['verneed'] or self
._versioninfo
['verdef']):
968 self
._versioninfo
['type'] = 'Solaris'
970 def _symbol_version(self
, nsym
):
971 """ Return a dict containing information on the
972 or None if no version information is available
974 self
._init
_versioninfo
()
976 symbol_version
= dict.fromkeys(('index', 'name', 'filename', 'hidden'))
978 if (not self
._versioninfo
['versym'] or
979 nsym
>= self
._versioninfo
['versym'].num_symbols()):
982 symbol
= self
._versioninfo
['versym'].get_symbol(nsym
)
983 index
= symbol
.entry
['ndx']
984 if not index
in ('VER_NDX_LOCAL', 'VER_NDX_GLOBAL'):
987 if self
._versioninfo
['type'] == 'GNU':
988 # In GNU versioning mode, the highest bit is used to
989 # store whether the symbol is hidden or not
992 symbol_version
['hidden'] = True
994 if (self
._versioninfo
['verdef'] and
995 index
<= self
._versioninfo
['verdef'].num_versions()):
997 self
._versioninfo
['verdef'].get_version(index
)
998 symbol_version
['name'] = next(verdaux_iter
).name
1000 verneed
, vernaux
= \
1001 self
._versioninfo
['verneed'].get_version(index
)
1002 symbol_version
['name'] = vernaux
.name
1003 symbol_version
['filename'] = verneed
.name
1005 symbol_version
['index'] = index
1006 return symbol_version
1008 def _section_from_spec(self
, spec
):
1009 """ Retrieve a section given a "spec" (either number or name).
1010 Return None if no such section exists in the file.
1014 if num
< self
.elffile
.num_sections():
1015 return self
.elffile
.get_section(num
)
1019 # Not a number. Must be a name then
1020 return self
.elffile
.get_section_by_name(spec
)
1022 def _get_symbol_shndx(self
, symbol
, symbol_index
, symtab_index
):
1023 """ Get the index into the section header table for the "symbol"
1024 at "symbol_index" located in the symbol table with section index
1027 symbol_shndx
= symbol
['st_shndx']
1028 if symbol_shndx
!= SHN_INDICES
.SHN_XINDEX
:
1031 # Check for or lazily construct index section mapping (symbol table
1032 # index -> corresponding symbol table index section object)
1033 if self
._shndx
_sections
is None:
1034 self
._shndx
_sections
= {sec
.symboltable
: sec
for sec
in self
.elffile
.iter_sections()
1035 if isinstance(sec
, SymbolTableIndexSection
)}
1036 return self
._shndx
_sections
[symtab_index
].get_section_index(symbol_index
)
1038 def _note_relocs_for_section(self
, section
):
1039 """ If there are relocation sections pointing to the givne section,
1040 emit a note about it.
1042 for relsec
in self
.elffile
.iter_sections():
1043 if isinstance(relsec
, RelocationSection
):
1044 info_idx
= relsec
['sh_info']
1045 if self
.elffile
.get_section(info_idx
) == section
:
1046 self
._emitline
(' Note: This section has relocations against it, but these have NOT been applied to this dump.')
1049 def _init_dwarfinfo(self
):
1050 """ Initialize the DWARF info contained in the file and assign it to
1052 Leave self._dwarfinfo at None if no DWARF info was found in the file
1054 if self
._dwarfinfo
is not None:
1057 if self
.elffile
.has_dwarf_info():
1058 self
._dwarfinfo
= self
.elffile
.get_dwarf_info()
1060 self
._dwarfinfo
= None
1062 def _dump_debug_info(self
):
1063 """ Dump the debugging info section.
1065 if not self
._dwarfinfo
.has_debug_info
:
1067 self
._emitline
('Contents of the %s section:\n' % self
._dwarfinfo
.debug_info_sec
.name
)
1069 # Offset of the .debug_info section in the stream
1070 section_offset
= self
._dwarfinfo
.debug_info_sec
.global_offset
1072 for cu
in self
._dwarfinfo
.iter_CUs():
1073 self
._emitline
(' Compilation Unit @ offset %s:' %
1074 self
._format
_hex
(cu
.cu_offset
))
1075 self
._emitline
(' Length: %s (%s)' % (
1076 self
._format
_hex
(cu
['unit_length']),
1077 '%s-bit' % cu
.dwarf_format()))
1078 self
._emitline
(' Version: %s' % cu
['version'])
1079 if cu
.header
.get("unit_type", False):
1080 ut
= next((key
for key
, value
in ENUM_DW_UT
.items() if value
== cu
.header
.unit_type
), '?')
1081 self
._emitline
(' Unit Type: %s (%d)' % (ut
, cu
.header
.unit_type
))
1082 self
._emitline
(' Abbrev Offset: %s' % (
1083 self
._format
_hex
(cu
['debug_abbrev_offset']))),
1084 self
._emitline
(' Pointer Size: %s' % cu
['address_size'])
1086 # The nesting depth of each DIE within the tree of DIEs must be
1087 # displayed. To implement this, a counter is incremented each time
1088 # the current DIE has children, and decremented when a null die is
1089 # encountered. Due to the way the DIE tree is serialized, this will
1090 # correctly reflect the nesting depth
1093 current_function
= None
1094 for die
in cu
.iter_DIEs():
1095 if die
.tag
== 'DW_TAG_subprogram':
1096 current_function
= die
1097 self
._emitline
(' <%s><%x>: Abbrev Number: %s%s' % (
1101 (' (%s)' % die
.tag
) if not die
.is_null() else ''))
1106 for attr
in itervalues(die
.attributes
):
1108 # Unknown attribute values are passed-through as integers
1109 if isinstance(name
, int):
1110 name
= 'Unknown AT value: %x' % name
1112 attr_desc
= describe_attr_value(attr
, die
, section_offset
)
1114 if 'DW_OP_fbreg' in attr_desc
and current_function
and not 'DW_AT_frame_base' in current_function
.attributes
:
1115 postfix
= ' [without dw_at_frame_base]'
1119 self
._emitline
(' <%x> %-18s: %s%s' % (
1125 if die
.has_children
:
1130 def _dump_debug_line_programs(self
):
1131 """ Dump the (decoded) line programs from .debug_line
1132 The programs are dumped in the order of the CUs they belong to.
1134 if not self
._dwarfinfo
.has_debug_info
:
1136 self
._emitline
('Contents of the %s section:' % self
._dwarfinfo
.debug_line_sec
.name
)
1139 for cu
in self
._dwarfinfo
.iter_CUs():
1140 lineprogram
= self
._dwarfinfo
.line_program_for_CU(cu
)
1141 ver5
= lineprogram
.header
.version
>= 5
1143 cu_filename
= bytes2str(lineprogram
['file_entry'][0].name
)
1144 if len(lineprogram
['include_directory']) > 0:
1145 # GNU readelf 2.38 only outputs directory in wide mode
1146 self
._emitline
('%s:' % cu_filename
)
1148 self
._emitline
('CU: %s:' % cu_filename
)
1150 self
._emitline
('File name Line number Starting address Stmt')
1151 # GNU readelf has a View column that we don't try to replicate
1152 # The autotest has logic in place to ignore that
1154 # Print each state's file, line and address information. For some
1155 # instructions other output is needed to be compatible with
1157 for entry
in lineprogram
.get_entries():
1160 # Special handling for commands that don't set a new state
1161 if entry
.command
== DW_LNS_set_file
:
1162 file_entry
= lineprogram
['file_entry'][entry
.args
[0] - 1]
1163 if file_entry
.dir_index
== 0:
1165 self
._emitline
('\n./%s:[++]' % (
1166 bytes2str(file_entry
.name
)))
1168 self
._emitline
('\n%s/%s:' % (
1169 bytes2str(lineprogram
['include_directory'][file_entry
.dir_index
- 1]),
1170 bytes2str(file_entry
.name
)))
1171 elif entry
.command
== DW_LNE_define_file
:
1172 self
._emitline
('%s:' % (
1173 bytes2str(lineprogram
['include_directory'][entry
.args
[0].dir_index
])))
1174 elif lineprogram
['version'] < 4 or self
.elffile
['e_machine'] == 'EM_PPC64':
1175 self
._emitline
('%-35s %11s %18s %s' % (
1176 bytes2str(lineprogram
['file_entry'][state
.file - 1].name
),
1177 state
.line
if not state
.end_sequence
else '-',
1178 '0' if state
.address
== 0 else self
._format
_hex
(state
.address
),
1179 'x' if state
.is_stmt
and not state
.end_sequence
else ''))
1181 # What's the deal with op_index after address on DWARF 5? Is omitting it
1182 # a function of DWARF version, or ISA, or what?
1183 # Used to be unconditional, even on non-VLIW machines.
1184 self
._emitline
('%-35s %s %18s%s %s' % (
1185 bytes2str(lineprogram
['file_entry'][state
.file - 1].name
),
1186 "%11d" % (state
.line
,) if not state
.end_sequence
else '-',
1187 '0' if state
.address
== 0 else self
._format
_hex
(state
.address
),
1188 '' if ver5
else '[%d]' % (state
.op_index
,),
1189 'x' if state
.is_stmt
and not state
.end_sequence
else ''))
1190 if entry
.command
== DW_LNS_copy
:
1191 # Another readelf oddity...
1194 def _dump_frames_info(self
, section
, cfi_entries
):
1195 """ Dump the raw call frame info in a section.
1197 `section` is the Section instance that contains the call frame info
1198 while `cfi_entries` must be an iterable that yields the sequence of
1199 CIE or FDE instances.
1201 self
._emitline
('Contents of the %s section:' % section
.name
)
1203 for entry
in cfi_entries
:
1204 if isinstance(entry
, CIE
):
1205 self
._emitline
('\n%08x %s %s CIE' % (
1207 self
._format
_hex
(entry
['length'], fullhex
=True, lead0x
=False),
1208 self
._format
_hex
(entry
['CIE_id'], fieldsize
=8, lead0x
=False)))
1209 self
._emitline
(' Version: %d' % entry
['version'])
1210 self
._emitline
(' Augmentation: "%s"' % bytes2str(entry
['augmentation']))
1211 self
._emitline
(' Code alignment factor: %u' % entry
['code_alignment_factor'])
1212 self
._emitline
(' Data alignment factor: %d' % entry
['data_alignment_factor'])
1213 self
._emitline
(' Return address column: %d' % entry
['return_address_register'])
1214 if entry
.augmentation_bytes
:
1215 self
._emitline
(' Augmentation data: {}'.format(' '.join(
1216 '{:02x}'.format(ord(b
))
1217 for b
in iterbytes(entry
.augmentation_bytes
)
1221 elif isinstance(entry
, FDE
):
1222 self
._emitline
('\n%08x %s %s FDE cie=%08x pc=%s..%s' % (
1224 self
._format
_hex
(entry
['length'], fullhex
=True, lead0x
=False),
1225 self
._format
_hex
(entry
['CIE_pointer'], fieldsize
=8, lead0x
=False),
1227 self
._format
_hex
(entry
['initial_location'], fullhex
=True, lead0x
=False),
1229 entry
['initial_location'] + entry
['address_range'],
1230 fullhex
=True, lead0x
=False)))
1231 if entry
.augmentation_bytes
:
1232 self
._emitline
(' Augmentation data: {}'.format(' '.join(
1233 '{:02x}'.format(ord(b
))
1234 for b
in iterbytes(entry
.augmentation_bytes
)
1237 else: # ZERO terminator
1238 assert isinstance(entry
, ZERO
)
1239 self
._emitline
('\n%08x ZERO terminator' % entry
.offset
)
1242 self
._emit
(describe_CFI_instructions(entry
))
1245 def _dump_debug_frames(self
):
1246 """ Dump the raw frame info from .debug_frame and .eh_frame sections.
1248 if self
._dwarfinfo
.has_EH_CFI():
1249 self
._dump
_frames
_info
(
1250 self
._dwarfinfo
.eh_frame_sec
,
1251 self
._dwarfinfo
.EH_CFI_entries())
1254 if self
._dwarfinfo
.has_CFI():
1255 self
._dump
_frames
_info
(
1256 self
._dwarfinfo
.debug_frame_sec
,
1257 self
._dwarfinfo
.CFI_entries())
1259 def _dump_debug_namelut(self
, what
):
1261 Dump the debug pubnames section.
1263 if what
== 'pubnames':
1264 namelut
= self
._dwarfinfo
.get_pubnames()
1265 section
= self
._dwarfinfo
.debug_pubnames_sec
1267 namelut
= self
._dwarfinfo
.get_pubtypes()
1268 section
= self
._dwarfinfo
.debug_pubtypes_sec
1270 # readelf prints nothing if the section is not present.
1271 if namelut
is None or len(namelut
) == 0:
1274 self
._emitline
('Contents of the %s section:' % section
.name
)
1277 cu_headers
= namelut
.get_cu_headers()
1279 # go over CU-by-CU first and item-by-item next.
1280 for (cu_hdr
, (cu_ofs
, items
)) in izip(cu_headers
, itertools
.groupby(
1281 namelut
.items(), key
= lambda x
: x
[1].cu_ofs
)):
1283 self
._emitline
(' Length: %d' % cu_hdr
.unit_length
)
1284 self
._emitline
(' Version: %d' % cu_hdr
.version
)
1285 self
._emitline
(' Offset into .debug_info section: 0x%x' % cu_hdr
.debug_info_offset
)
1286 self
._emitline
(' Size of area in .debug_info section: %d' % cu_hdr
.debug_info_length
)
1288 self
._emitline
(' Offset Name')
1290 self
._emitline
(' %x %s' % (item
[1].die_ofs
- cu_ofs
, item
[0]))
1293 def _dump_debug_aranges(self
):
1294 """ Dump the aranges table
1296 aranges_table
= self
._dwarfinfo
.get_aranges()
1297 if aranges_table
== None:
1299 # seems redundent, but we need to get the unsorted set of entries to match system readelf
1300 unordered_entries
= aranges_table
._get
_entries
()
1302 if len(unordered_entries
) == 0:
1304 self
._emitline
("Section '.debug_aranges' has no debugging data.")
1307 self
._emitline
('Contents of the %s section:' % self
._dwarfinfo
.debug_aranges_sec
.name
)
1310 for entry
in unordered_entries
:
1311 if prev_offset
!= entry
.info_offset
:
1312 if entry
!= unordered_entries
[0]:
1313 self
._emitline
(' %s %s' % (
1314 self
._format
_hex
(0, fullhex
=True, lead0x
=False),
1315 self
._format
_hex
(0, fullhex
=True, lead0x
=False)))
1316 self
._emitline
(' Length: %d' % (entry
.unit_length
))
1317 self
._emitline
(' Version: %d' % (entry
.version
))
1318 self
._emitline
(' Offset into .debug_info: 0x%x' % (entry
.info_offset
))
1319 self
._emitline
(' Pointer Size: %d' % (entry
.address_size
))
1320 self
._emitline
(' Segment Size: %d' % (entry
.segment_size
))
1322 self
._emitline
(' Address Length')
1323 self
._emitline
(' %s %s' % (
1324 self
._format
_hex
(entry
.begin_addr
, fullhex
=True, lead0x
=False),
1325 self
._format
_hex
(entry
.length
, fullhex
=True, lead0x
=False)))
1326 prev_offset
= entry
.info_offset
1327 self
._emitline
(' %s %s' % (
1328 self
._format
_hex
(0, fullhex
=True, lead0x
=False),
1329 self
._format
_hex
(0, fullhex
=True, lead0x
=False)))
1331 def _dump_frames_interp_info(self
, section
, cfi_entries
):
1332 """ Dump interpreted (decoded) frame information in a section.
1334 `section` is the Section instance that contains the call frame info
1335 while `cfi_entries` must be an iterable that yields the sequence of
1336 CIE or FDE instances.
1338 self
._emitline
('Contents of the %s section:' % section
.name
)
1340 for entry
in cfi_entries
:
1341 if isinstance(entry
, CIE
):
1342 self
._emitline
('\n%08x %s %s CIE "%s" cf=%d df=%d ra=%d' % (
1344 self
._format
_hex
(entry
['length'], fullhex
=True, lead0x
=False),
1345 self
._format
_hex
(entry
['CIE_id'], fieldsize
=8, lead0x
=False),
1346 bytes2str(entry
['augmentation']),
1347 entry
['code_alignment_factor'],
1348 entry
['data_alignment_factor'],
1349 entry
['return_address_register']))
1350 ra_regnum
= entry
['return_address_register']
1352 elif isinstance(entry
, FDE
):
1353 self
._emitline
('\n%08x %s %s FDE cie=%08x pc=%s..%s' % (
1355 self
._format
_hex
(entry
['length'], fullhex
=True, lead0x
=False),
1356 self
._format
_hex
(entry
['CIE_pointer'], fieldsize
=8, lead0x
=False),
1358 self
._format
_hex
(entry
['initial_location'], fullhex
=True, lead0x
=False),
1359 self
._format
_hex
(entry
['initial_location'] + entry
['address_range'],
1360 fullhex
=True, lead0x
=False)))
1361 ra_regnum
= entry
.cie
['return_address_register']
1363 # If the FDE brings adds no unwinding information compared to
1364 # its CIE, omit its table.
1365 if (len(entry
.get_decoded().table
) ==
1366 len(entry
.cie
.get_decoded().table
)):
1369 else: # ZERO terminator
1370 assert isinstance(entry
, ZERO
)
1371 self
._emitline
('\n%08x ZERO terminator' % entry
.offset
)
1375 decoded_table
= entry
.get_decoded()
1376 if len(decoded_table
.table
) == 0:
1379 # Print the heading row for the decoded table
1381 self
._emit
(' ' if entry
.structs
.address_size
== 4 else ' ')
1384 # Look at the registers the decoded table describes.
1385 # We build reg_order here to match readelf's order. In particular,
1386 # registers are sorted by their number, and the register matching
1387 # ra_regnum is always listed last with a special heading.
1388 decoded_table
= entry
.get_decoded()
1389 reg_order
= sorted(ifilter(
1390 lambda r
: r
!= ra_regnum
,
1391 decoded_table
.reg_order
))
1392 if len(decoded_table
.reg_order
):
1394 # Headings for the registers
1395 for regnum
in reg_order
:
1396 self
._emit
('%-6s' % describe_reg_name(regnum
))
1397 self
._emitline
('ra ')
1399 # Now include ra_regnum in reg_order to print its values
1400 # similarly to the other registers.
1401 reg_order
.append(ra_regnum
)
1405 for line
in decoded_table
.table
:
1406 self
._emit
(self
._format
_hex
(
1407 line
['pc'], fullhex
=True, lead0x
=False))
1409 if line
['cfa'] is not None:
1410 s
= describe_CFI_CFA_rule(line
['cfa'])
1413 self
._emit
(' %-9s' % s
)
1415 for regnum
in reg_order
:
1417 s
= describe_CFI_register_rule(line
[regnum
])
1420 self
._emit
('%-6s' % s
)
1424 def _dump_debug_frames_interp(self
):
1425 """ Dump the interpreted (decoded) frame information from .debug_frame
1426 and .eh_frame sections.
1428 if self
._dwarfinfo
.has_EH_CFI():
1429 self
._dump
_frames
_interp
_info
(
1430 self
._dwarfinfo
.eh_frame_sec
,
1431 self
._dwarfinfo
.EH_CFI_entries())
1434 if self
._dwarfinfo
.has_CFI():
1435 self
._dump
_frames
_interp
_info
(
1436 self
._dwarfinfo
.debug_frame_sec
,
1437 self
._dwarfinfo
.CFI_entries())
1439 def _dump_debug_locations(self
):
1440 """ Dump the location lists from .debug_loc/.debug_loclists section
1442 di
= self
._dwarfinfo
1443 loc_lists
= di
.location_lists()
1444 if not loc_lists
: # No locations section - readelf outputs nothing
1447 loc_lists
= list(loc_lists
.iter_location_lists())
1448 if len(loc_lists
) == 0:
1449 # Present but empty locations section - readelf outputs a message
1450 self
._emitline
("\nSection '%s' has no debugging data." % (di
.debug_loclists_sec
or di
.debug_loc_sec
).name
)
1453 # To dump a location list, one needs to know the CU.
1454 # Scroll through DIEs once, list the known location list offsets.
1455 # Don't need this CU/DIE scan if all entries are absolute or prefixed by base,
1456 # but let's not optimize for that yet.
1457 cu_map
= dict() # Loc list offset => CU
1458 for cu
in di
.iter_CUs():
1459 for die
in cu
.iter_DIEs():
1460 for key
in die
.attributes
:
1461 attr
= die
.attributes
[key
]
1462 if (LocationParser
.attribute_has_location(attr
, cu
['version']) and
1463 LocationParser
._attribute
_has
_loc
_list
(attr
, cu
['version'])):
1464 cu_map
[attr
.value
] = cu
1466 addr_size
= di
.config
.default_address_size
# In bytes, 4 or 8
1467 addr_width
= addr_size
* 2 # In hex digits, 8 or 16
1468 line_template
= " %%08x %%0%dx %%0%dx %%s%%s" % (addr_width
, addr_width
)
1470 self
._emitline
('Contents of the %s section:\n' % (di
.debug_loclists_sec
or di
.debug_loc_sec
).name
)
1471 self
._emitline
(' Offset Begin End Expression')
1472 for loc_list
in loc_lists
:
1478 for entry
in loc_list
:
1479 if isinstance(entry
, LocationViewPair
):
1480 has_views
= in_views
= True
1481 # The "v" before address is conditional in binutils, haven't figured out how
1482 self
._emitline
(" %08x v%015x v%015x location view pair" % (entry
.entry_offset
, entry
.begin
, entry
.end
))
1488 # Need the CU for this loclist, but the map is keyed by the offset
1489 # of the first entry in the loclist. Got to skip the views first.
1491 cu
= cu_map
.get(entry
.entry_offset
, False)
1493 raise ValueError("Location list can't be tracked to a CU")
1495 if isinstance(entry
, LocationEntry
):
1496 if base_ip
is None and not entry
.is_absolute
:
1497 base_ip
= _get_cu_base(cu
)
1499 begin_offset
= (0 if entry
.is_absolute
else base_ip
) + entry
.begin_offset
1500 end_offset
= (0 if entry
.is_absolute
else base_ip
) + entry
.end_offset
1501 expr
= describe_DWARF_expr(entry
.loc_expr
, cu
.structs
, cu
.cu_offset
)
1503 view
= loc_list
[loc_entry_count
]
1504 postfix
= ' (start == end)' if entry
.begin_offset
== entry
.end_offset
and view
.begin
== view
.end
else ''
1505 self
._emitline
(' %08x v%015x v%015x views at %08x for:' %(
1510 self
._emitline
(' %016x %016x %s%s' %(
1515 loc_entry_count
+= 1
1517 postfix
= ' (start == end)' if entry
.begin_offset
== entry
.end_offset
else ''
1518 self
._emitline
(line_template
% (
1524 elif isinstance(entry
, BaseAddressEntry
):
1525 base_ip
= entry
.base_address
1526 self
._emitline
(" %08x %016x (base address)" % (entry
.entry_offset
, entry
.base_address
))
1528 # Pyelftools doesn't store the terminating entry,
1529 # but readelf emits its offset, so this should too.
1531 self
._emitline
(" %08x <End of list>" % (last
.entry_offset
+ last
.entry_length
))
1533 def _dump_debug_ranges(self
):
1534 # TODO: GNU readelf format doesn't need entry_length?
1535 di
= self
._dwarfinfo
1536 range_lists
= di
.range_lists()
1537 if not range_lists
: # No ranges section - readelf outputs nothing
1540 ver5
= range_lists
.version
>= 5
1541 range_lists
= list(range_lists
.iter_range_lists())
1542 if len(range_lists
) == 0:
1543 # Present but empty locations section - readelf outputs a message
1544 self
._emitline
("\nSection '%s' has no debugging data." % (di
.debug_rnglists_sec
or di
.debug_ranges_sec
).name
)
1547 # In order to determine the base address of the range
1548 # We need to know the corresponding CU.
1549 cu_map
= {die
.attributes
['DW_AT_ranges'].value
: cu
# Range list offset => CU
1550 for cu
in di
.iter_CUs()
1551 for die
in cu
.iter_DIEs()
1552 if 'DW_AT_ranges' in die
.attributes
}
1554 addr_size
= di
.config
.default_address_size
# In bytes, 4 or 8
1555 addr_width
= addr_size
* 2 # In hex digits, 8 or 16
1556 line_template
= " %%08x %%0%dx %%0%dx %%s" % (addr_width
, addr_width
)
1557 base_template
= " %%08x %%0%dx (base address)" % (addr_width
)
1559 self
._emitline
('Contents of the %s section:\n' % (di
.debug_rnglists_sec
or di
.debug_ranges_sec
).name
)
1560 self
._emitline
(' Offset Begin End')
1562 for range_list
in range_lists
:
1563 # Weird discrepancy in binutils: for DWARFv5 it outputs entry offset,
1564 # for DWARF<=4 list offset.
1565 first
= range_list
[0]
1566 base_ip
= _get_cu_base(cu_map
[first
.entry_offset
])
1567 for entry
in range_list
:
1568 if isinstance(entry
, RangeEntry
):
1569 postfix
= ' (start == end)' if entry
.begin_offset
== entry
.end_offset
else ''
1570 self
._emitline
(line_template
% (
1571 entry
.entry_offset
if ver5
else first
.entry_offset
,
1572 (0 if entry
.is_absolute
else base_ip
) + entry
.begin_offset
,
1573 (0 if entry
.is_absolute
else base_ip
) + entry
.end_offset
,
1575 elif isinstance(entry
, elftools
.dwarf
.ranges
.BaseAddressEntry
):
1576 base_ip
= entry
.base_address
1577 self
._emitline
(base_template
% (
1578 entry
.entry_offset
if ver5
else first
.entry_offset
,
1579 entry
.base_address
))
1581 raise NotImplementedError("Unknown object in a range list")
1582 last
= range_list
[-1]
1583 self
._emitline
(' %08x <End of list>' % (last
.entry_offset
+ last
.entry_length
if ver5
else first
.entry_offset
))
1585 def _display_arch_specific_arm(self
):
1586 """ Display the ARM architecture-specific info contained in the file.
1588 attr_sec
= self
.elffile
.get_section_by_name('.ARM.attributes')
1590 for s
in attr_sec
.iter_subsections():
1591 self
._emitline
("Attribute Section: %s" % s
.header
['vendor_name'])
1592 for ss
in s
.iter_subsubsections():
1593 h_val
= "" if ss
.header
.extra
is None else " ".join("%d" % x
for x
in ss
.header
.extra
)
1594 self
._emitline
(describe_attr_tag_arm(ss
.header
.tag
, h_val
, None))
1596 for attr
in ss
.iter_attributes():
1598 self
._emitline
(describe_attr_tag_arm(attr
.tag
,
1602 def _emit(self
, s
=''):
1603 """ Emit an object to output
1605 self
.output
.write(str(s
))
1607 def _emitline(self
, s
=''):
1608 """ Emit an object to output, followed by a newline
1610 self
.output
.write(str(s
).rstrip() + '\n')
1613 SCRIPT_DESCRIPTION
= 'Display information about the contents of ELF format files'
1614 VERSION_STRING
= '%%(prog)s: based on pyelftools %s' % __version__
1617 def main(stream
=None):
1618 # parse the command-line arguments and invoke ReadElf
1619 argparser
= argparse
.ArgumentParser(
1620 usage
='usage: %(prog)s [options] <elf-file>',
1621 description
=SCRIPT_DESCRIPTION
,
1622 add_help
=False, # -h is a real option of readelf
1624 argparser
.add_argument('file',
1625 nargs
='?', default
=None,
1626 help='ELF file to parse')
1627 argparser
.add_argument('-v', '--version',
1628 action
='version', version
=VERSION_STRING
)
1629 argparser
.add_argument('-d', '--dynamic',
1630 action
='store_true', dest
='show_dynamic_tags',
1631 help='Display the dynamic section')
1632 argparser
.add_argument('-H', '--help',
1633 action
='store_true', dest
='help',
1634 help='Display this information')
1635 argparser
.add_argument('-h', '--file-header',
1636 action
='store_true', dest
='show_file_header',
1637 help='Display the ELF file header')
1638 argparser
.add_argument('-l', '--program-headers', '--segments',
1639 action
='store_true', dest
='show_program_header',
1640 help='Display the program headers')
1641 argparser
.add_argument('-S', '--section-headers', '--sections',
1642 action
='store_true', dest
='show_section_header',
1643 help="Display the sections' headers")
1644 argparser
.add_argument('-e', '--headers',
1645 action
='store_true', dest
='show_all_headers',
1646 help='Equivalent to: -h -l -S')
1647 argparser
.add_argument('-s', '--symbols', '--syms',
1648 action
='store_true', dest
='show_symbols',
1649 help='Display the symbol table')
1650 argparser
.add_argument('-n', '--notes',
1651 action
='store_true', dest
='show_notes',
1652 help='Display the core notes (if present)')
1653 argparser
.add_argument('-r', '--relocs',
1654 action
='store_true', dest
='show_relocs',
1655 help='Display the relocations (if present)')
1656 argparser
.add_argument('-au', '--arm-unwind',
1657 action
='store_true', dest
='show_arm_unwind',
1658 help='Display the armeabi unwind information (if present)')
1659 argparser
.add_argument('-x', '--hex-dump',
1660 action
='store', dest
='show_hex_dump', metavar
='<number|name>',
1661 help='Dump the contents of section <number|name> as bytes')
1662 argparser
.add_argument('-p', '--string-dump',
1663 action
='store', dest
='show_string_dump', metavar
='<number|name>',
1664 help='Dump the contents of section <number|name> as strings')
1665 argparser
.add_argument('-V', '--version-info',
1666 action
='store_true', dest
='show_version_info',
1667 help='Display the version sections (if present)')
1668 argparser
.add_argument('-A', '--arch-specific',
1669 action
='store_true', dest
='show_arch_specific',
1670 help='Display the architecture-specific information (if present)')
1671 argparser
.add_argument('--debug-dump',
1672 action
='store', dest
='debug_dump_what', metavar
='<what>',
1674 'Display the contents of DWARF debug sections. <what> can ' +
1675 'one of {info,decodedline,frames,frames-interp,aranges,pubtypes,pubnames,loc,Ranges}'))
1676 argparser
.add_argument('--traceback',
1677 action
='store_true', dest
='show_traceback',
1678 help='Dump the Python traceback on ELFError'
1679 ' exceptions from elftools')
1681 args
= argparser
.parse_args()
1683 if args
.help or not args
.file:
1684 argparser
.print_help()
1687 if args
.show_all_headers
:
1688 do_file_header
= do_section_header
= do_program_header
= True
1690 do_file_header
= args
.show_file_header
1691 do_section_header
= args
.show_section_header
1692 do_program_header
= args
.show_program_header
1694 with
open(args
.file, 'rb') as file:
1696 readelf
= ReadElf(file, stream
or sys
.stdout
)
1698 readelf
.display_file_header()
1699 if do_section_header
:
1700 readelf
.display_section_headers(
1701 show_heading
=not do_file_header
)
1702 if do_program_header
:
1703 readelf
.display_program_headers(
1704 show_heading
=not do_file_header
)
1705 if args
.show_dynamic_tags
:
1706 readelf
.display_dynamic_tags()
1707 if args
.show_symbols
:
1708 readelf
.display_symbol_tables()
1710 readelf
.display_notes()
1711 if args
.show_relocs
:
1712 readelf
.display_relocations()
1713 if args
.show_arm_unwind
:
1714 readelf
.display_arm_unwind()
1715 if args
.show_version_info
:
1716 readelf
.display_version_info()
1717 if args
.show_arch_specific
:
1718 readelf
.display_arch_specific()
1719 if args
.show_hex_dump
:
1720 readelf
.display_hex_dump(args
.show_hex_dump
)
1721 if args
.show_string_dump
:
1722 readelf
.display_string_dump(args
.show_string_dump
)
1723 if args
.debug_dump_what
:
1724 readelf
.display_debug_dump(args
.debug_dump_what
)
1725 except ELFError
as ex
:
1727 sys
.stderr
.write('ELF error: %s\n' % ex
)
1728 if args
.show_traceback
:
1729 traceback
.print_exc()
1734 # Run 'main' redirecting its output to readelfout.txt
1735 # Saves profiling information in readelf.profile
1736 PROFFILE
= 'readelf.profile'
1738 cProfile
.run('main(open("readelfout.txt", "w"))', PROFFILE
)
1740 # Dig in some profiling stats
1742 p
= pstats
.Stats(PROFFILE
)
1743 p
.sort_stats('cumulative').print_stats(25)
1746 #-------------------------------------------------------------------------------
1747 if __name__
== '__main__':