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 dir_index
= lineprogram
['file_entry'][0].dir_index
1147 dir = lineprogram
['include_directory'][dir_index
- 1]
1150 cu_filename
= '%s/%s' % (bytes2str(dir), cu_filename
)
1152 self
._emitline
('CU: %s:' % cu_filename
)
1153 self
._emitline
('File name Line number Starting address Stmt')
1154 # GNU readelf has a View column that we don't try to replicate
1155 # The autotest has logic in place to ignore that
1157 # Print each state's file, line and address information. For some
1158 # instructions other output is needed to be compatible with
1160 for entry
in lineprogram
.get_entries():
1163 # Special handling for commands that don't set a new state
1164 if entry
.command
== DW_LNS_set_file
:
1165 file_entry
= lineprogram
['file_entry'][entry
.args
[0] - 1]
1166 if file_entry
.dir_index
== 0:
1168 self
._emitline
('\n./%s:[++]' % (
1169 bytes2str(file_entry
.name
)))
1171 self
._emitline
('\n%s/%s:' % (
1172 bytes2str(lineprogram
['include_directory'][file_entry
.dir_index
- 1]),
1173 bytes2str(file_entry
.name
)))
1174 elif entry
.command
== DW_LNE_define_file
:
1175 self
._emitline
('%s:' % (
1176 bytes2str(lineprogram
['include_directory'][entry
.args
[0].dir_index
])))
1177 elif lineprogram
['version'] < 4 or self
.elffile
['e_machine'] == 'EM_PPC64':
1178 self
._emitline
('%-35s %11s %18s %s' % (
1179 bytes2str(lineprogram
['file_entry'][state
.file - 1].name
),
1180 state
.line
if not state
.end_sequence
else '-',
1181 '0' if state
.address
== 0 else self
._format
_hex
(state
.address
),
1182 'x' if state
.is_stmt
and not state
.end_sequence
else ''))
1184 # What's the deal with op_index after address on DWARF 5? Is omitting it
1185 # a function of DWARF version, or ISA, or what?
1186 # Used to be unconditional, even on non-VLIW machines.
1187 self
._emitline
('%-35s %s %18s%s %s' % (
1188 bytes2str(lineprogram
['file_entry'][state
.file - 1].name
),
1189 "%11d" % (state
.line
,) if not state
.end_sequence
else '-',
1190 '0' if state
.address
== 0 else self
._format
_hex
(state
.address
),
1191 '' if ver5
else '[%d]' % (state
.op_index
,),
1192 'x' if state
.is_stmt
and not state
.end_sequence
else ''))
1193 if entry
.command
== DW_LNS_copy
:
1194 # Another readelf oddity...
1197 def _dump_frames_info(self
, section
, cfi_entries
):
1198 """ Dump the raw call frame info in a section.
1200 `section` is the Section instance that contains the call frame info
1201 while `cfi_entries` must be an iterable that yields the sequence of
1202 CIE or FDE instances.
1204 self
._emitline
('Contents of the %s section:' % section
.name
)
1206 for entry
in cfi_entries
:
1207 if isinstance(entry
, CIE
):
1208 self
._emitline
('\n%08x %s %s CIE' % (
1210 self
._format
_hex
(entry
['length'], fullhex
=True, lead0x
=False),
1211 self
._format
_hex
(entry
['CIE_id'], fieldsize
=8, lead0x
=False)))
1212 self
._emitline
(' Version: %d' % entry
['version'])
1213 self
._emitline
(' Augmentation: "%s"' % bytes2str(entry
['augmentation']))
1214 self
._emitline
(' Code alignment factor: %u' % entry
['code_alignment_factor'])
1215 self
._emitline
(' Data alignment factor: %d' % entry
['data_alignment_factor'])
1216 self
._emitline
(' Return address column: %d' % entry
['return_address_register'])
1217 if entry
.augmentation_bytes
:
1218 self
._emitline
(' Augmentation data: {}'.format(' '.join(
1219 '{:02x}'.format(ord(b
))
1220 for b
in iterbytes(entry
.augmentation_bytes
)
1224 elif isinstance(entry
, FDE
):
1225 self
._emitline
('\n%08x %s %s FDE cie=%08x pc=%s..%s' % (
1227 self
._format
_hex
(entry
['length'], fullhex
=True, lead0x
=False),
1228 self
._format
_hex
(entry
['CIE_pointer'], fieldsize
=8, lead0x
=False),
1230 self
._format
_hex
(entry
['initial_location'], fullhex
=True, lead0x
=False),
1232 entry
['initial_location'] + entry
['address_range'],
1233 fullhex
=True, lead0x
=False)))
1234 if entry
.augmentation_bytes
:
1235 self
._emitline
(' Augmentation data: {}'.format(' '.join(
1236 '{:02x}'.format(ord(b
))
1237 for b
in iterbytes(entry
.augmentation_bytes
)
1240 else: # ZERO terminator
1241 assert isinstance(entry
, ZERO
)
1242 self
._emitline
('\n%08x ZERO terminator' % entry
.offset
)
1245 self
._emit
(describe_CFI_instructions(entry
))
1248 def _dump_debug_frames(self
):
1249 """ Dump the raw frame info from .debug_frame and .eh_frame sections.
1251 if self
._dwarfinfo
.has_EH_CFI():
1252 self
._dump
_frames
_info
(
1253 self
._dwarfinfo
.eh_frame_sec
,
1254 self
._dwarfinfo
.EH_CFI_entries())
1257 if self
._dwarfinfo
.has_CFI():
1258 self
._dump
_frames
_info
(
1259 self
._dwarfinfo
.debug_frame_sec
,
1260 self
._dwarfinfo
.CFI_entries())
1262 def _dump_debug_namelut(self
, what
):
1264 Dump the debug pubnames section.
1266 if what
== 'pubnames':
1267 namelut
= self
._dwarfinfo
.get_pubnames()
1268 section
= self
._dwarfinfo
.debug_pubnames_sec
1270 namelut
= self
._dwarfinfo
.get_pubtypes()
1271 section
= self
._dwarfinfo
.debug_pubtypes_sec
1273 # readelf prints nothing if the section is not present.
1274 if namelut
is None or len(namelut
) == 0:
1277 self
._emitline
('Contents of the %s section:' % section
.name
)
1280 cu_headers
= namelut
.get_cu_headers()
1282 # go over CU-by-CU first and item-by-item next.
1283 for (cu_hdr
, (cu_ofs
, items
)) in izip(cu_headers
, itertools
.groupby(
1284 namelut
.items(), key
= lambda x
: x
[1].cu_ofs
)):
1286 self
._emitline
(' Length: %d' % cu_hdr
.unit_length
)
1287 self
._emitline
(' Version: %d' % cu_hdr
.version
)
1288 self
._emitline
(' Offset into .debug_info section: 0x%x' % cu_hdr
.debug_info_offset
)
1289 self
._emitline
(' Size of area in .debug_info section: %d' % cu_hdr
.debug_info_length
)
1291 self
._emitline
(' Offset Name')
1293 self
._emitline
(' %x %s' % (item
[1].die_ofs
- cu_ofs
, item
[0]))
1296 def _dump_debug_aranges(self
):
1297 """ Dump the aranges table
1299 aranges_table
= self
._dwarfinfo
.get_aranges()
1300 if aranges_table
== None:
1302 # seems redundent, but we need to get the unsorted set of entries to match system readelf
1303 unordered_entries
= aranges_table
._get
_entries
()
1305 if len(unordered_entries
) == 0:
1307 self
._emitline
("Section '.debug_aranges' has no debugging data.")
1310 self
._emitline
('Contents of the %s section:' % self
._dwarfinfo
.debug_aranges_sec
.name
)
1313 for entry
in unordered_entries
:
1314 if prev_offset
!= entry
.info_offset
:
1315 if entry
!= unordered_entries
[0]:
1316 self
._emitline
(' %s %s' % (
1317 self
._format
_hex
(0, fullhex
=True, lead0x
=False),
1318 self
._format
_hex
(0, fullhex
=True, lead0x
=False)))
1319 self
._emitline
(' Length: %d' % (entry
.unit_length
))
1320 self
._emitline
(' Version: %d' % (entry
.version
))
1321 self
._emitline
(' Offset into .debug_info: 0x%x' % (entry
.info_offset
))
1322 self
._emitline
(' Pointer Size: %d' % (entry
.address_size
))
1323 self
._emitline
(' Segment Size: %d' % (entry
.segment_size
))
1325 self
._emitline
(' Address Length')
1326 self
._emitline
(' %s %s' % (
1327 self
._format
_hex
(entry
.begin_addr
, fullhex
=True, lead0x
=False),
1328 self
._format
_hex
(entry
.length
, fullhex
=True, lead0x
=False)))
1329 prev_offset
= entry
.info_offset
1330 self
._emitline
(' %s %s' % (
1331 self
._format
_hex
(0, fullhex
=True, lead0x
=False),
1332 self
._format
_hex
(0, fullhex
=True, lead0x
=False)))
1334 def _dump_frames_interp_info(self
, section
, cfi_entries
):
1335 """ Dump interpreted (decoded) frame information in a section.
1337 `section` is the Section instance that contains the call frame info
1338 while `cfi_entries` must be an iterable that yields the sequence of
1339 CIE or FDE instances.
1341 self
._emitline
('Contents of the %s section:' % section
.name
)
1343 for entry
in cfi_entries
:
1344 if isinstance(entry
, CIE
):
1345 self
._emitline
('\n%08x %s %s CIE "%s" cf=%d df=%d ra=%d' % (
1347 self
._format
_hex
(entry
['length'], fullhex
=True, lead0x
=False),
1348 self
._format
_hex
(entry
['CIE_id'], fieldsize
=8, lead0x
=False),
1349 bytes2str(entry
['augmentation']),
1350 entry
['code_alignment_factor'],
1351 entry
['data_alignment_factor'],
1352 entry
['return_address_register']))
1353 ra_regnum
= entry
['return_address_register']
1355 elif isinstance(entry
, FDE
):
1356 self
._emitline
('\n%08x %s %s FDE cie=%08x pc=%s..%s' % (
1358 self
._format
_hex
(entry
['length'], fullhex
=True, lead0x
=False),
1359 self
._format
_hex
(entry
['CIE_pointer'], fieldsize
=8, lead0x
=False),
1361 self
._format
_hex
(entry
['initial_location'], fullhex
=True, lead0x
=False),
1362 self
._format
_hex
(entry
['initial_location'] + entry
['address_range'],
1363 fullhex
=True, lead0x
=False)))
1364 ra_regnum
= entry
.cie
['return_address_register']
1366 # If the FDE brings adds no unwinding information compared to
1367 # its CIE, omit its table.
1368 if (len(entry
.get_decoded().table
) ==
1369 len(entry
.cie
.get_decoded().table
)):
1372 else: # ZERO terminator
1373 assert isinstance(entry
, ZERO
)
1374 self
._emitline
('\n%08x ZERO terminator' % entry
.offset
)
1378 decoded_table
= entry
.get_decoded()
1379 if len(decoded_table
.table
) == 0:
1382 # Print the heading row for the decoded table
1384 self
._emit
(' ' if entry
.structs
.address_size
== 4 else ' ')
1387 # Look at the registers the decoded table describes.
1388 # We build reg_order here to match readelf's order. In particular,
1389 # registers are sorted by their number, and the register matching
1390 # ra_regnum is always listed last with a special heading.
1391 decoded_table
= entry
.get_decoded()
1392 reg_order
= sorted(ifilter(
1393 lambda r
: r
!= ra_regnum
,
1394 decoded_table
.reg_order
))
1395 if len(decoded_table
.reg_order
):
1397 # Headings for the registers
1398 for regnum
in reg_order
:
1399 self
._emit
('%-6s' % describe_reg_name(regnum
))
1400 self
._emitline
('ra ')
1402 # Now include ra_regnum in reg_order to print its values
1403 # similarly to the other registers.
1404 reg_order
.append(ra_regnum
)
1408 for line
in decoded_table
.table
:
1409 self
._emit
(self
._format
_hex
(
1410 line
['pc'], fullhex
=True, lead0x
=False))
1412 if line
['cfa'] is not None:
1413 s
= describe_CFI_CFA_rule(line
['cfa'])
1416 self
._emit
(' %-9s' % s
)
1418 for regnum
in reg_order
:
1420 s
= describe_CFI_register_rule(line
[regnum
])
1423 self
._emit
('%-6s' % s
)
1427 def _dump_debug_frames_interp(self
):
1428 """ Dump the interpreted (decoded) frame information from .debug_frame
1429 and .eh_frame sections.
1431 if self
._dwarfinfo
.has_EH_CFI():
1432 self
._dump
_frames
_interp
_info
(
1433 self
._dwarfinfo
.eh_frame_sec
,
1434 self
._dwarfinfo
.EH_CFI_entries())
1437 if self
._dwarfinfo
.has_CFI():
1438 self
._dump
_frames
_interp
_info
(
1439 self
._dwarfinfo
.debug_frame_sec
,
1440 self
._dwarfinfo
.CFI_entries())
1442 def _dump_debug_locations(self
):
1443 """ Dump the location lists from .debug_loc/.debug_loclists section
1445 di
= self
._dwarfinfo
1446 loc_lists
= di
.location_lists()
1447 if not loc_lists
: # No locations section - readelf outputs nothing
1450 loc_lists
= list(loc_lists
.iter_location_lists())
1451 if len(loc_lists
) == 0:
1452 # Present but empty locations section - readelf outputs a message
1453 self
._emitline
("\nSection '%s' has no debugging data." % (di
.debug_loclists_sec
or di
.debug_loc_sec
).name
)
1456 # To dump a location list, one needs to know the CU.
1457 # Scroll through DIEs once, list the known location list offsets.
1458 # Don't need this CU/DIE scan if all entries are absolute or prefixed by base,
1459 # but let's not optimize for that yet.
1460 cu_map
= dict() # Loc list offset => CU
1461 for cu
in di
.iter_CUs():
1462 for die
in cu
.iter_DIEs():
1463 for key
in die
.attributes
:
1464 attr
= die
.attributes
[key
]
1465 if (LocationParser
.attribute_has_location(attr
, cu
['version']) and
1466 LocationParser
._attribute
_has
_loc
_list
(attr
, cu
['version'])):
1467 cu_map
[attr
.value
] = cu
1469 addr_size
= di
.config
.default_address_size
# In bytes, 4 or 8
1470 addr_width
= addr_size
* 2 # In hex digits, 8 or 16
1471 line_template
= " %%08x %%0%dx %%0%dx %%s%%s" % (addr_width
, addr_width
)
1473 self
._emitline
('Contents of the %s section:\n' % (di
.debug_loclists_sec
or di
.debug_loc_sec
).name
)
1474 self
._emitline
(' Offset Begin End Expression')
1475 for loc_list
in loc_lists
:
1481 for entry
in loc_list
:
1482 if isinstance(entry
, LocationViewPair
):
1483 has_views
= in_views
= True
1484 # The "v" before address is conditional in binutils, haven't figured out how
1485 self
._emitline
(" %08x v%015x v%015x location view pair" % (entry
.entry_offset
, entry
.begin
, entry
.end
))
1491 # Need the CU for this loclist, but the map is keyed by the offset
1492 # of the first entry in the loclist. Got to skip the views first.
1494 cu
= cu_map
.get(entry
.entry_offset
, False)
1496 raise ValueError("Location list can't be tracked to a CU")
1498 if isinstance(entry
, LocationEntry
):
1499 if base_ip
is None and not entry
.is_absolute
:
1500 base_ip
= _get_cu_base(cu
)
1502 begin_offset
= (0 if entry
.is_absolute
else base_ip
) + entry
.begin_offset
1503 end_offset
= (0 if entry
.is_absolute
else base_ip
) + entry
.end_offset
1504 expr
= describe_DWARF_expr(entry
.loc_expr
, cu
.structs
, cu
.cu_offset
)
1506 view
= loc_list
[loc_entry_count
]
1507 postfix
= ' (start == end)' if entry
.begin_offset
== entry
.end_offset
and view
.begin
== view
.end
else ''
1508 self
._emitline
(' %08x v%015x v%015x views at %08x for:' %(
1513 self
._emitline
(' %016x %016x %s%s' %(
1518 loc_entry_count
+= 1
1520 postfix
= ' (start == end)' if entry
.begin_offset
== entry
.end_offset
else ''
1521 self
._emitline
(line_template
% (
1527 elif isinstance(entry
, BaseAddressEntry
):
1528 base_ip
= entry
.base_address
1529 self
._emitline
(" %08x %016x (base address)" % (entry
.entry_offset
, entry
.base_address
))
1531 # Pyelftools doesn't store the terminating entry,
1532 # but readelf emits its offset, so this should too.
1534 self
._emitline
(" %08x <End of list>" % (last
.entry_offset
+ last
.entry_length
))
1536 def _dump_debug_ranges(self
):
1537 # TODO: GNU readelf format doesn't need entry_length?
1538 di
= self
._dwarfinfo
1539 range_lists
= di
.range_lists()
1540 if not range_lists
: # No ranges section - readelf outputs nothing
1543 ver5
= range_lists
.version
>= 5
1544 range_lists
= list(range_lists
.iter_range_lists())
1545 if len(range_lists
) == 0:
1546 # Present but empty locations section - readelf outputs a message
1547 self
._emitline
("\nSection '%s' has no debugging data." % (di
.debug_rnglists_sec
or di
.debug_ranges_sec
).name
)
1550 # In order to determine the base address of the range
1551 # We need to know the corresponding CU.
1552 cu_map
= {die
.attributes
['DW_AT_ranges'].value
: cu
# Range list offset => CU
1553 for cu
in di
.iter_CUs()
1554 for die
in cu
.iter_DIEs()
1555 if 'DW_AT_ranges' in die
.attributes
}
1557 addr_size
= di
.config
.default_address_size
# In bytes, 4 or 8
1558 addr_width
= addr_size
* 2 # In hex digits, 8 or 16
1559 line_template
= " %%08x %%0%dx %%0%dx %%s" % (addr_width
, addr_width
)
1560 base_template
= " %%08x %%0%dx (base address)" % (addr_width
)
1562 self
._emitline
('Contents of the %s section:\n' % (di
.debug_rnglists_sec
or di
.debug_ranges_sec
).name
)
1563 self
._emitline
(' Offset Begin End')
1565 for range_list
in range_lists
:
1566 # Weird discrepancy in binutils: for DWARFv5 it outputs entry offset,
1567 # for DWARF<=4 list offset.
1568 first
= range_list
[0]
1569 base_ip
= _get_cu_base(cu_map
[first
.entry_offset
])
1570 for entry
in range_list
:
1571 if isinstance(entry
, RangeEntry
):
1572 postfix
= ' (start == end)' if entry
.begin_offset
== entry
.end_offset
else ''
1573 self
._emitline
(line_template
% (
1574 entry
.entry_offset
if ver5
else first
.entry_offset
,
1575 (0 if entry
.is_absolute
else base_ip
) + entry
.begin_offset
,
1576 (0 if entry
.is_absolute
else base_ip
) + entry
.end_offset
,
1578 elif isinstance(entry
, elftools
.dwarf
.ranges
.BaseAddressEntry
):
1579 base_ip
= entry
.base_address
1580 self
._emitline
(base_template
% (
1581 entry
.entry_offset
if ver5
else first
.entry_offset
,
1582 entry
.base_address
))
1584 raise NotImplementedError("Unknown object in a range list")
1585 last
= range_list
[-1]
1586 self
._emitline
(' %08x <End of list>' % (last
.entry_offset
+ last
.entry_length
if ver5
else first
.entry_offset
))
1588 def _display_arch_specific_arm(self
):
1589 """ Display the ARM architecture-specific info contained in the file.
1591 attr_sec
= self
.elffile
.get_section_by_name('.ARM.attributes')
1593 for s
in attr_sec
.iter_subsections():
1594 self
._emitline
("Attribute Section: %s" % s
.header
['vendor_name'])
1595 for ss
in s
.iter_subsubsections():
1596 h_val
= "" if ss
.header
.extra
is None else " ".join("%d" % x
for x
in ss
.header
.extra
)
1597 self
._emitline
(describe_attr_tag_arm(ss
.header
.tag
, h_val
, None))
1599 for attr
in ss
.iter_attributes():
1601 self
._emitline
(describe_attr_tag_arm(attr
.tag
,
1605 def _emit(self
, s
=''):
1606 """ Emit an object to output
1608 self
.output
.write(str(s
))
1610 def _emitline(self
, s
=''):
1611 """ Emit an object to output, followed by a newline
1613 self
.output
.write(str(s
).rstrip() + '\n')
1616 SCRIPT_DESCRIPTION
= 'Display information about the contents of ELF format files'
1617 VERSION_STRING
= '%%(prog)s: based on pyelftools %s' % __version__
1620 def main(stream
=None):
1621 # parse the command-line arguments and invoke ReadElf
1622 argparser
= argparse
.ArgumentParser(
1623 usage
='usage: %(prog)s [options] <elf-file>',
1624 description
=SCRIPT_DESCRIPTION
,
1625 add_help
=False, # -h is a real option of readelf
1627 argparser
.add_argument('file',
1628 nargs
='?', default
=None,
1629 help='ELF file to parse')
1630 argparser
.add_argument('-v', '--version',
1631 action
='version', version
=VERSION_STRING
)
1632 argparser
.add_argument('-d', '--dynamic',
1633 action
='store_true', dest
='show_dynamic_tags',
1634 help='Display the dynamic section')
1635 argparser
.add_argument('-H', '--help',
1636 action
='store_true', dest
='help',
1637 help='Display this information')
1638 argparser
.add_argument('-h', '--file-header',
1639 action
='store_true', dest
='show_file_header',
1640 help='Display the ELF file header')
1641 argparser
.add_argument('-l', '--program-headers', '--segments',
1642 action
='store_true', dest
='show_program_header',
1643 help='Display the program headers')
1644 argparser
.add_argument('-S', '--section-headers', '--sections',
1645 action
='store_true', dest
='show_section_header',
1646 help="Display the sections' headers")
1647 argparser
.add_argument('-e', '--headers',
1648 action
='store_true', dest
='show_all_headers',
1649 help='Equivalent to: -h -l -S')
1650 argparser
.add_argument('-s', '--symbols', '--syms',
1651 action
='store_true', dest
='show_symbols',
1652 help='Display the symbol table')
1653 argparser
.add_argument('-n', '--notes',
1654 action
='store_true', dest
='show_notes',
1655 help='Display the core notes (if present)')
1656 argparser
.add_argument('-r', '--relocs',
1657 action
='store_true', dest
='show_relocs',
1658 help='Display the relocations (if present)')
1659 argparser
.add_argument('-au', '--arm-unwind',
1660 action
='store_true', dest
='show_arm_unwind',
1661 help='Display the armeabi unwind information (if present)')
1662 argparser
.add_argument('-x', '--hex-dump',
1663 action
='store', dest
='show_hex_dump', metavar
='<number|name>',
1664 help='Dump the contents of section <number|name> as bytes')
1665 argparser
.add_argument('-p', '--string-dump',
1666 action
='store', dest
='show_string_dump', metavar
='<number|name>',
1667 help='Dump the contents of section <number|name> as strings')
1668 argparser
.add_argument('-V', '--version-info',
1669 action
='store_true', dest
='show_version_info',
1670 help='Display the version sections (if present)')
1671 argparser
.add_argument('-A', '--arch-specific',
1672 action
='store_true', dest
='show_arch_specific',
1673 help='Display the architecture-specific information (if present)')
1674 argparser
.add_argument('--debug-dump',
1675 action
='store', dest
='debug_dump_what', metavar
='<what>',
1677 'Display the contents of DWARF debug sections. <what> can ' +
1678 'one of {info,decodedline,frames,frames-interp,aranges,pubtypes,pubnames,loc,Ranges}'))
1679 argparser
.add_argument('--traceback',
1680 action
='store_true', dest
='show_traceback',
1681 help='Dump the Python traceback on ELFError'
1682 ' exceptions from elftools')
1684 args
= argparser
.parse_args()
1686 if args
.help or not args
.file:
1687 argparser
.print_help()
1690 if args
.show_all_headers
:
1691 do_file_header
= do_section_header
= do_program_header
= True
1693 do_file_header
= args
.show_file_header
1694 do_section_header
= args
.show_section_header
1695 do_program_header
= args
.show_program_header
1697 with
open(args
.file, 'rb') as file:
1699 readelf
= ReadElf(file, stream
or sys
.stdout
)
1701 readelf
.display_file_header()
1702 if do_section_header
:
1703 readelf
.display_section_headers(
1704 show_heading
=not do_file_header
)
1705 if do_program_header
:
1706 readelf
.display_program_headers(
1707 show_heading
=not do_file_header
)
1708 if args
.show_dynamic_tags
:
1709 readelf
.display_dynamic_tags()
1710 if args
.show_symbols
:
1711 readelf
.display_symbol_tables()
1713 readelf
.display_notes()
1714 if args
.show_relocs
:
1715 readelf
.display_relocations()
1716 if args
.show_arm_unwind
:
1717 readelf
.display_arm_unwind()
1718 if args
.show_version_info
:
1719 readelf
.display_version_info()
1720 if args
.show_arch_specific
:
1721 readelf
.display_arch_specific()
1722 if args
.show_hex_dump
:
1723 readelf
.display_hex_dump(args
.show_hex_dump
)
1724 if args
.show_string_dump
:
1725 readelf
.display_string_dump(args
.show_string_dump
)
1726 if args
.debug_dump_what
:
1727 readelf
.display_debug_dump(args
.debug_dump_what
)
1728 except ELFError
as ex
:
1730 sys
.stderr
.write('ELF error: %s\n' % ex
)
1731 if args
.show_traceback
:
1732 traceback
.print_exc()
1737 # Run 'main' redirecting its output to readelfout.txt
1738 # Saves profiling information in readelf.profile
1739 PROFFILE
= 'readelf.profile'
1741 cProfile
.run('main(open("readelfout.txt", "w"))', PROFFILE
)
1743 # Dig in some profiling stats
1745 p
= pstats
.Stats(PROFFILE
)
1746 p
.sort_stats('cumulative').print_stats(25)
1749 #-------------------------------------------------------------------------------
1750 if __name__
== '__main__':