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
.utils
import bytes2str
, iterbytes
30 from elftools
.elf
.elffile
import ELFFile
31 from elftools
.elf
.dynamic
import DynamicSection
, DynamicSegment
32 from elftools
.elf
.enums
import ENUM_D_TAG
33 from elftools
.elf
.segments
import InterpSegment
34 from elftools
.elf
.sections
import (
35 NoteSection
, SymbolTableSection
, SymbolTableIndexSection
37 from elftools
.elf
.gnuversions
import (
38 GNUVerSymSection
, GNUVerDefSection
,
41 from elftools
.elf
.relocation
import RelocationSection
42 from elftools
.elf
.descriptions
import (
43 describe_ei_class
, describe_ei_data
, describe_ei_version
,
44 describe_ei_osabi
, describe_e_type
, describe_e_machine
,
45 describe_e_version_numeric
, describe_p_type
, describe_p_flags
,
46 describe_rh_flags
, describe_sh_type
, describe_sh_flags
,
47 describe_symbol_type
, describe_symbol_bind
, describe_symbol_visibility
,
48 describe_symbol_shndx
, describe_reloc_type
, describe_dyn_tag
,
49 describe_dt_flags
, describe_dt_flags_1
, describe_ver_flags
, describe_note
,
50 describe_attr_tag_arm
, describe_symbol_other
52 from elftools
.elf
.constants
import E_FLAGS
53 from elftools
.elf
.constants
import E_FLAGS_MASKS
54 from elftools
.elf
.constants
import SH_FLAGS
55 from elftools
.elf
.constants
import SHN_INDICES
56 from elftools
.dwarf
.dwarfinfo
import DWARFInfo
57 from elftools
.dwarf
.descriptions
import (
58 describe_reg_name
, describe_attr_value
, set_global_machine_arch
,
59 describe_CFI_instructions
, describe_CFI_register_rule
,
60 describe_CFI_CFA_rule
, describe_DWARF_expr
62 from elftools
.dwarf
.constants
import (
63 DW_LNS_copy
, DW_LNS_set_file
, DW_LNE_define_file
)
64 from elftools
.dwarf
.locationlists
import LocationParser
, LocationEntry
, LocationViewPair
, BaseAddressEntry
as LocBaseAddressEntry
, LocationListsPair
65 from elftools
.dwarf
.ranges
import RangeEntry
, BaseAddressEntry
as RangeBaseAddressEntry
, RangeListsPair
66 from elftools
.dwarf
.callframe
import CIE
, FDE
, ZERO
67 from elftools
.ehabi
.ehabiinfo
import CorruptEHABIEntry
, CannotUnwindEHABIEntry
, GenericEHABIEntry
68 from elftools
.dwarf
.enums
import ENUM_DW_UT
71 top_die
= cu
.get_top_DIE()
72 attr
= top_die
.attributes
73 if 'DW_AT_low_pc' in attr
:
74 return attr
['DW_AT_low_pc'].value
75 elif 'DW_AT_entry_pc' in attr
:
76 return attr
['DW_AT_entry_pc'].value
77 elif 'DW_AT_ranges' in attr
:
78 # Rare case but happens: rangelist in the top DIE.
79 # If there is a base or at least one absolute entry,
80 # this will give us the base IP for the CU.
81 rl
= cu
.dwarfinfo
.range_lists().get_range_list_at_offset(attr
['DW_AT_ranges'].value
, cu
)
84 if isinstance(r
, RangeBaseAddressEntry
):
86 elif isinstance(r
, RangeEntry
) and r
.is_absolute
:
90 if ip
is not None and (base_ip
is None or ip
< base_ip
):
93 raise ValueError("Can't find the base IP (low_pc) for a CU")
96 raise ValueError("Can't find the base IP (low_pc) for a CU")
98 class ReadElf(object):
99 """ display_* methods are used to emit output into the output stream
101 def __init__(self
, file, output
):
103 stream object with the ELF file to read
106 output stream to write to
108 self
.elffile
= ELFFile(file)
111 # Lazily initialized if a debug dump is requested
112 self
._dwarfinfo
= None
114 self
._versioninfo
= None
116 self
._shndx
_sections
= None
118 def display_file_header(self
):
119 """ Display the ELF file header
121 self
._emitline
('ELF Header:')
122 self
._emit
(' Magic: ')
123 self
._emit
(' '.join('%2.2x' % b
124 for b
in self
.elffile
.e_ident_raw
))
126 header
= self
.elffile
.header
127 e_ident
= header
['e_ident']
128 self
._emitline
(' Class: %s' %
129 describe_ei_class(e_ident
['EI_CLASS']))
130 self
._emitline
(' Data: %s' %
131 describe_ei_data(e_ident
['EI_DATA']))
132 self
._emitline
(' Version: %s' %
133 describe_ei_version(e_ident
['EI_VERSION']))
134 self
._emitline
(' OS/ABI: %s' %
135 describe_ei_osabi(e_ident
['EI_OSABI']))
136 self
._emitline
(' ABI Version: %d' %
137 e_ident
['EI_ABIVERSION'])
138 self
._emitline
(' Type: %s' %
139 describe_e_type(header
['e_type'], self
.elffile
))
140 self
._emitline
(' Machine: %s' %
141 describe_e_machine(header
['e_machine']))
142 self
._emitline
(' Version: %s' %
143 describe_e_version_numeric(header
['e_version']))
144 self
._emitline
(' Entry point address: %s' %
145 self
._format
_hex
(header
['e_entry']))
146 self
._emit
(' Start of program headers: %s' %
148 self
._emitline
(' (bytes into file)')
149 self
._emit
(' Start of section headers: %s' %
151 self
._emitline
(' (bytes into file)')
152 self
._emitline
(' Flags: %s%s' %
153 (self
._format
_hex
(header
['e_flags']),
154 self
.decode_flags(header
['e_flags'])))
155 self
._emitline
(' Size of this header: %s (bytes)' %
157 self
._emitline
(' Size of program headers: %s (bytes)' %
158 header
['e_phentsize'])
159 self
._emitline
(' Number of program headers: %s' %
161 self
._emitline
(' Size of section headers: %s (bytes)' %
162 header
['e_shentsize'])
163 self
._emit
(' Number of section headers: %s' %
165 if header
['e_shnum'] == 0 and self
.elffile
.num_sections() != 0:
166 self
._emitline
(' (%d)' % self
.elffile
.num_sections())
169 self
._emit
(' Section header string table index: %s' %
170 header
['e_shstrndx'])
171 if header
['e_shstrndx'] == SHN_INDICES
.SHN_XINDEX
:
172 self
._emitline
(' (%d)' % self
.elffile
.get_shstrndx())
176 def decode_flags(self
, flags
):
178 if self
.elffile
['e_machine'] == "EM_ARM":
179 eabi
= flags
& E_FLAGS
.EF_ARM_EABIMASK
180 flags
&= ~E_FLAGS
.EF_ARM_EABIMASK
182 if flags
& E_FLAGS
.EF_ARM_RELEXEC
:
183 description
+= ', relocatable executabl'
184 flags
&= ~E_FLAGS
.EF_ARM_RELEXEC
186 if eabi
== E_FLAGS
.EF_ARM_EABI_VER5
:
187 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
188 description
+= ', Version5 EABI'
189 if flags
& E_FLAGS
.EF_ARM_ABI_FLOAT_SOFT
:
190 description
+= ", soft-float ABI"
191 elif flags
& E_FLAGS
.EF_ARM_ABI_FLOAT_HARD
:
192 description
+= ", hard-float ABI"
194 if flags
& E_FLAGS
.EF_ARM_BE8
:
195 description
+= ", BE8"
196 elif flags
& E_FLAGS
.EF_ARM_LE8
:
197 description
+= ", LE8"
199 if flags
& ~EF_ARM_KNOWN_FLAGS
:
200 description
+= ', <unknown>'
202 description
+= ', <unrecognized EABI>'
204 elif self
.elffile
['e_machine'] == 'EM_PPC64':
205 if flags
& E_FLAGS
.EF_PPC64_ABI_V2
:
206 description
+= ', abiv2'
208 elif self
.elffile
['e_machine'] == "EM_MIPS":
209 if flags
& E_FLAGS
.EF_MIPS_NOREORDER
:
210 description
+= ", noreorder"
211 if flags
& E_FLAGS
.EF_MIPS_PIC
:
212 description
+= ", pic"
213 if flags
& E_FLAGS
.EF_MIPS_CPIC
:
214 description
+= ", cpic"
215 if (flags
& E_FLAGS
.EF_MIPS_ABI2
):
216 description
+= ", abi2"
217 if (flags
& E_FLAGS
.EF_MIPS_32BITMODE
):
218 description
+= ", 32bitmode"
219 if (flags
& E_FLAGS_MASKS
.EFM_MIPS_ABI_O32
):
220 description
+= ", o32"
221 elif (flags
& E_FLAGS_MASKS
.EFM_MIPS_ABI_O64
):
222 description
+= ", o64"
223 elif (flags
& E_FLAGS_MASKS
.EFM_MIPS_ABI_EABI32
):
224 description
+= ", eabi32"
225 elif (flags
& E_FLAGS_MASKS
.EFM_MIPS_ABI_EABI64
):
226 description
+= ", eabi64"
227 if (flags
& E_FLAGS
.EF_MIPS_ARCH
) == E_FLAGS
.EF_MIPS_ARCH_1
:
228 description
+= ", mips1"
229 if (flags
& E_FLAGS
.EF_MIPS_ARCH
) == E_FLAGS
.EF_MIPS_ARCH_2
:
230 description
+= ", mips2"
231 if (flags
& E_FLAGS
.EF_MIPS_ARCH
) == E_FLAGS
.EF_MIPS_ARCH_3
:
232 description
+= ", mips3"
233 if (flags
& E_FLAGS
.EF_MIPS_ARCH
) == E_FLAGS
.EF_MIPS_ARCH_4
:
234 description
+= ", mips4"
235 if (flags
& E_FLAGS
.EF_MIPS_ARCH
) == E_FLAGS
.EF_MIPS_ARCH_5
:
236 description
+= ", mips5"
237 if (flags
& E_FLAGS
.EF_MIPS_ARCH
) == E_FLAGS
.EF_MIPS_ARCH_32R2
:
238 description
+= ", mips32r2"
239 if (flags
& E_FLAGS
.EF_MIPS_ARCH
) == E_FLAGS
.EF_MIPS_ARCH_64R2
:
240 description
+= ", mips64r2"
241 if (flags
& E_FLAGS
.EF_MIPS_ARCH
) == E_FLAGS
.EF_MIPS_ARCH_32
:
242 description
+= ", mips32"
243 if (flags
& E_FLAGS
.EF_MIPS_ARCH
) == E_FLAGS
.EF_MIPS_ARCH_64
:
244 description
+= ", mips64"
248 def display_program_headers(self
, show_heading
=True):
249 """ Display the ELF program headers.
250 If show_heading is True, displays the heading for this information
251 (Elf file type is...)
254 if self
.elffile
.num_segments() == 0:
255 self
._emitline
('There are no program headers in this file.')
258 elfheader
= self
.elffile
.header
260 self
._emitline
('Elf file type is %s' %
261 describe_e_type(elfheader
['e_type'], self
.elffile
))
262 self
._emitline
('Entry point is %s' %
263 self
._format
_hex
(elfheader
['e_entry']))
264 # readelf weirness - why isn't e_phoff printed as hex? (for section
266 self
._emitline
('There are %s program headers, starting at offset %s' % (
267 self
.elffile
.num_segments(), elfheader
['e_phoff']))
270 self
._emitline
('Program Headers:')
272 # Now comes the table of program headers with their attributes. Note
273 # that due to different formatting constraints of 32-bit and 64-bit
274 # addresses, there are some conditions on elfclass here.
276 # First comes the table heading
278 if self
.elffile
.elfclass
== 32:
279 self
._emitline
(' Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align')
281 self
._emitline
(' Type Offset VirtAddr PhysAddr')
282 self
._emitline
(' FileSiz MemSiz Flags Align')
286 for segment
in self
.elffile
.iter_segments():
287 self
._emit
(' %-14s ' % describe_p_type(segment
['p_type']))
289 if self
.elffile
.elfclass
== 32:
290 self
._emitline
('%s %s %s %s %s %-3s %s' % (
291 self
._format
_hex
(segment
['p_offset'], fieldsize
=6),
292 self
._format
_hex
(segment
['p_vaddr'], fullhex
=True),
293 self
._format
_hex
(segment
['p_paddr'], fullhex
=True),
294 self
._format
_hex
(segment
['p_filesz'], fieldsize
=5),
295 self
._format
_hex
(segment
['p_memsz'], fieldsize
=5),
296 describe_p_flags(segment
['p_flags']),
297 self
._format
_hex
(segment
['p_align'])))
299 self
._emitline
('%s %s %s' % (
300 self
._format
_hex
(segment
['p_offset'], fullhex
=True),
301 self
._format
_hex
(segment
['p_vaddr'], fullhex
=True),
302 self
._format
_hex
(segment
['p_paddr'], fullhex
=True)))
303 self
._emitline
(' %s %s %-3s %s' % (
304 self
._format
_hex
(segment
['p_filesz'], fullhex
=True),
305 self
._format
_hex
(segment
['p_memsz'], fullhex
=True),
306 describe_p_flags(segment
['p_flags']),
307 # lead0x set to False for p_align, to mimic readelf.
308 # No idea why the difference from 32-bit mode :-|
309 self
._format
_hex
(segment
['p_align'], lead0x
=False)))
311 if isinstance(segment
, InterpSegment
):
312 self
._emitline
(' [Requesting program interpreter: %s]' %
313 segment
.get_interp_name())
315 # Sections to segments mapping
317 if self
.elffile
.num_sections() == 0:
318 # No sections? We're done
321 self
._emitline
('\n Section to Segment mapping:')
322 self
._emitline
(' Segment Sections...')
324 for nseg
, segment
in enumerate(self
.elffile
.iter_segments()):
325 self
._emit
(' %2.2d ' % nseg
)
327 for section
in self
.elffile
.iter_sections():
328 if ( not section
.is_null() and
329 not ((section
['sh_flags'] & SH_FLAGS
.SHF_TLS
) != 0 and
330 section
['sh_type'] == 'SHT_NOBITS' and
331 segment
['p_type'] != 'PT_TLS') and
332 segment
.section_in_segment(section
)):
333 self
._emit
('%s ' % section
.name
)
337 def display_section_headers(self
, show_heading
=True):
338 """ Display the ELF section headers
340 elfheader
= self
.elffile
.header
342 self
._emitline
('There are %s section headers, starting at offset %s' % (
343 elfheader
['e_shnum'], self
._format
_hex
(elfheader
['e_shoff'])))
345 if self
.elffile
.num_sections() == 0:
346 self
._emitline
('There are no sections in this file.')
349 self
._emitline
('\nSection Header%s:' % (
350 's' if self
.elffile
.num_sections() > 1 else ''))
352 # Different formatting constraints of 32-bit and 64-bit addresses
354 if self
.elffile
.elfclass
== 32:
355 self
._emitline
(' [Nr] Name Type Addr Off Size ES Flg Lk Inf Al')
357 self
._emitline
(' [Nr] Name Type Address Offset')
358 self
._emitline
(' Size EntSize Flags Link Info Align')
362 for nsec
, section
in enumerate(self
.elffile
.iter_sections()):
363 self
._emit
(' [%2u] %-17.17s %-15.15s ' % (
364 nsec
, section
.name
, describe_sh_type(section
['sh_type'])))
366 if self
.elffile
.elfclass
== 32:
367 self
._emitline
('%s %s %s %s %3s %2s %3s %2s' % (
368 self
._format
_hex
(section
['sh_addr'], fieldsize
=8, lead0x
=False),
369 self
._format
_hex
(section
['sh_offset'], fieldsize
=6, lead0x
=False),
370 self
._format
_hex
(section
['sh_size'], fieldsize
=6, lead0x
=False),
371 self
._format
_hex
(section
['sh_entsize'], fieldsize
=2, lead0x
=False),
372 describe_sh_flags(section
['sh_flags']),
373 section
['sh_link'], section
['sh_info'],
374 section
['sh_addralign']))
376 self
._emitline
(' %s %s' % (
377 self
._format
_hex
(section
['sh_addr'], fullhex
=True, lead0x
=False),
378 self
._format
_hex
(section
['sh_offset'],
379 fieldsize
=16 if section
['sh_offset'] > 0xffffffff else 8,
381 self
._emitline
(' %s %s %3s %2s %3s %s' % (
382 self
._format
_hex
(section
['sh_size'], fullhex
=True, lead0x
=False),
383 self
._format
_hex
(section
['sh_entsize'], fullhex
=True, lead0x
=False),
384 describe_sh_flags(section
['sh_flags']),
385 section
['sh_link'], section
['sh_info'],
386 section
['sh_addralign']))
388 self
._emitline
('Key to Flags:')
389 self
._emitline
(' W (write), A (alloc), X (execute), M (merge),'
390 ' S (strings), I (info),')
391 self
._emitline
(' L (link order), O (extra OS processing required),'
392 ' G (group), T (TLS),')
393 self
._emitline
(' C (compressed), x (unknown), o (OS specific),'
396 if self
.elffile
['e_machine'] == 'EM_ARM':
397 self
._emit
('y (purecode), ')
398 self
._emitline
('p (processor specific)')
400 def display_symbol_tables(self
):
401 """ Display the symbol tables contained in the file
403 self
._init
_versioninfo
()
405 symbol_tables
= [(idx
, s
) for idx
, s
in enumerate(self
.elffile
.iter_sections())
406 if isinstance(s
, SymbolTableSection
)]
408 if not symbol_tables
and self
.elffile
.num_sections() == 0:
410 self
._emitline
('Dynamic symbol information is not available for'
411 ' displaying symbols.')
413 for section_index
, section
in symbol_tables
:
414 if not isinstance(section
, SymbolTableSection
):
417 if section
['sh_entsize'] == 0:
418 self
._emitline
("\nSymbol table '%s' has a sh_entsize of zero!" % (
422 self
._emitline
("\nSymbol table '%s' contains %d %s:" % (
424 section
.num_symbols(),
425 'entry' if section
.num_symbols() == 1 else 'entries'))
427 if self
.elffile
.elfclass
== 32:
428 self
._emitline
(' Num: Value Size Type Bind Vis Ndx Name')
430 self
._emitline
(' Num: Value Size Type Bind Vis Ndx Name')
432 for nsym
, symbol
in enumerate(section
.iter_symbols()):
434 # readelf doesn't display version info for Solaris versioning
435 if (section
['sh_type'] == 'SHT_DYNSYM' and
436 self
._versioninfo
['type'] == 'GNU'):
437 version
= self
._symbol
_version
(nsym
)
438 if (version
['name'] != symbol
.name
and
439 version
['index'] not in ('VER_NDX_LOCAL',
441 if version
['filename']:
443 version_info
= '@%(name)s (%(index)i)' % version
446 if version
['hidden']:
447 version_info
= '@%(name)s' % version
449 version_info
= '@@%(name)s' % version
451 symbol_name
= symbol
.name
452 # Print section names for STT_SECTION symbols as readelf does
453 if (symbol
['st_info']['type'] == 'STT_SECTION'
454 and symbol
['st_shndx'] < self
.elffile
.num_sections()
455 and symbol
['st_name'] == 0):
456 symbol_name
= self
.elffile
.get_section(symbol
['st_shndx']).name
458 # symbol names are truncated to 25 chars, similarly to readelf
459 self
._emitline
('%6d: %s %s %-7s %-6s %-7s %4s %.25s%s' % (
462 symbol
['st_value'], fullhex
=True, lead0x
=False),
463 "%5d" % symbol
['st_size'] if symbol
['st_size'] < 100000 else hex(symbol
['st_size']),
464 describe_symbol_type(symbol
['st_info']['type']),
465 describe_symbol_bind(symbol
['st_info']['bind']),
466 describe_symbol_other(symbol
['st_other']),
467 describe_symbol_shndx(self
._get
_symbol
_shndx
(symbol
,
473 def display_dynamic_tags(self
):
474 """ Display the dynamic tags contained in the file
476 has_dynamic_sections
= False
477 for section
in self
.elffile
.iter_sections():
478 if not isinstance(section
, DynamicSection
):
481 has_dynamic_sections
= True
482 self
._emitline
("\nDynamic section at offset %s contains %d %s:" % (
483 self
._format
_hex
(section
['sh_offset']),
485 'entry' if section
.num_tags() == 1 else 'entries'))
486 self
._emitline
(" Tag Type Name/Value")
488 padding
= 20 + (8 if self
.elffile
.elfclass
== 32 else 0)
489 for tag
in section
.iter_tags():
490 if tag
.entry
.d_tag
== 'DT_NEEDED':
491 parsed
= 'Shared library: [%s]' % tag
.needed
492 elif tag
.entry
.d_tag
== 'DT_RPATH':
493 parsed
= 'Library rpath: [%s]' % tag
.rpath
494 elif tag
.entry
.d_tag
== 'DT_RUNPATH':
495 parsed
= 'Library runpath: [%s]' % tag
.runpath
496 elif tag
.entry
.d_tag
== 'DT_SONAME':
497 parsed
= 'Library soname: [%s]' % tag
.soname
498 elif tag
.entry
.d_tag
.endswith(('SZ', 'ENT')):
499 parsed
= '%i (bytes)' % tag
['d_val']
500 elif tag
.entry
.d_tag
== 'DT_FLAGS':
501 parsed
= describe_dt_flags(tag
.entry
.d_val
)
502 elif tag
.entry
.d_tag
== 'DT_FLAGS_1':
503 parsed
= 'Flags: %s' % describe_dt_flags_1(tag
.entry
.d_val
)
504 elif tag
.entry
.d_tag
.endswith(('NUM', 'COUNT')):
505 parsed
= '%i' % tag
['d_val']
506 elif tag
.entry
.d_tag
== 'DT_PLTREL':
507 s
= describe_dyn_tag(tag
.entry
.d_val
)
508 if s
.startswith('DT_'):
511 elif tag
.entry
.d_tag
== 'DT_MIPS_FLAGS':
512 parsed
= describe_rh_flags(tag
.entry
.d_val
)
513 elif tag
.entry
.d_tag
in ('DT_MIPS_SYMTABNO',
514 'DT_MIPS_LOCAL_GOTNO'):
515 parsed
= str(tag
.entry
.d_val
)
517 parsed
= '%#x' % tag
['d_val']
519 self
._emitline
(" %s %-*s %s" % (
520 self
._format
_hex
(ENUM_D_TAG
.get(tag
.entry
.d_tag
, tag
.entry
.d_tag
),
521 fullhex
=True, lead0x
=True),
523 '(%s)' % (tag
.entry
.d_tag
[3:],),
525 if not has_dynamic_sections
:
526 self
._emitline
("\nThere is no dynamic section in this file.")
528 def display_notes(self
):
529 """ Display the notes contained in the file
531 for section
in self
.elffile
.iter_sections():
532 if isinstance(section
, NoteSection
):
533 for note
in section
.iter_notes():
534 self
._emitline
("\nDisplaying notes found in: {}".format(
536 self
._emitline
(' Owner Data size Description')
537 self
._emitline
(' %s %s\t%s' % (
538 note
['n_name'].ljust(20),
539 self
._format
_hex
(note
['n_descsz'], fieldsize
=8),
540 describe_note(note
)))
542 def display_relocations(self
):
543 """ Display the relocations contained in the file
545 has_relocation_sections
= False
546 for section
in self
.elffile
.iter_sections():
547 if not isinstance(section
, RelocationSection
):
550 has_relocation_sections
= True
551 self
._emitline
("\nRelocation section '%.128s' at offset %s contains %d %s:" % (
553 self
._format
_hex
(section
['sh_offset']),
554 section
.num_relocations(),
555 'entry' if section
.num_relocations() == 1 else 'entries'))
556 if section
.is_RELA():
557 self
._emitline
(" Offset Info Type Sym. Value Sym. Name + Addend")
559 self
._emitline
(" Offset Info Type Sym.Value Sym. Name")
561 # The symbol table section pointed to in sh_link
562 symtable
= self
.elffile
.get_section(section
['sh_link'])
564 for rel
in section
.iter_relocations():
565 hexwidth
= 8 if self
.elffile
.elfclass
== 32 else 12
566 self
._emit
('%s %s %-17.17s' % (
567 self
._format
_hex
(rel
['r_offset'],
568 fieldsize
=hexwidth
, lead0x
=False),
569 self
._format
_hex
(rel
['r_info'],
570 fieldsize
=hexwidth
, lead0x
=False),
572 rel
['r_info_type'], self
.elffile
)))
574 if rel
['r_info_sym'] == 0:
575 if section
.is_RELA():
576 fieldsize
= 8 if self
.elffile
.elfclass
== 32 else 16
577 addend
= self
._format
_hex
(rel
['r_addend'], lead0x
=False)
578 self
._emit
(' %s %s' % (' ' * fieldsize
, addend
))
582 symbol
= symtable
.get_symbol(rel
['r_info_sym'])
583 # Some symbols have zero 'st_name', so instead what's used
584 # is the name of the section they point at. Truncate symbol
585 # names (excluding version info) to 22 chars, similarly to
587 if symbol
['st_name'] == 0:
588 symsecidx
= self
._get
_symbol
_shndx
(symbol
,
591 symsec
= self
.elffile
.get_section(symsecidx
)
592 symbol_name
= symsec
.name
595 symbol_name
= symbol
.name
596 version
= self
._symbol
_version
(rel
['r_info_sym'])
597 version
= (version
['name']
598 if version
and version
['name'] else '')
599 symbol_name
= '%.22s' % symbol_name
601 symbol_name
+= '@' + version
603 self
._emit
(' %s %s' % (
606 fullhex
=True, lead0x
=False),
608 if section
.is_RELA():
609 self
._emit
(' %s %x' % (
610 '+' if rel
['r_addend'] >= 0 else '-',
611 abs(rel
['r_addend'])))
614 # Emit the two additional relocation types for ELF64 MIPS
616 if (self
.elffile
.elfclass
== 64 and
617 self
.elffile
['e_machine'] == 'EM_MIPS'):
619 rtype
= rel
['r_info_type%s' % i
]
620 self
._emit
(' Type%s: %s' % (
622 describe_reloc_type(rtype
, self
.elffile
)))
625 if not has_relocation_sections
:
626 self
._emitline
('\nThere are no relocations in this file.')
628 def display_arm_unwind(self
):
629 if not self
.elffile
.has_ehabi_info():
630 self
._emitline
('There are no .ARM.idx sections in this file.')
632 for ehabi_info
in self
.elffile
.get_ehabi_infos():
633 # Unwind section '.ARM.exidx' at offset 0x203e8 contains 1009 entries:
634 self
._emitline
("\nUnwind section '%s' at offset 0x%x contains %d %s" % (
635 ehabi_info
.section_name(),
636 ehabi_info
.section_offset(),
637 ehabi_info
.num_entry(),
638 'entry' if ehabi_info
.num_entry() == 1 else 'entries'))
640 for i
in range(ehabi_info
.num_entry()):
641 entry
= ehabi_info
.get_entry(i
)
643 self
._emitline
("Entry %d:" % i
)
644 if isinstance(entry
, CorruptEHABIEntry
):
645 self
._emitline
(" [corrupt] %s" % entry
.reason
)
647 self
._emit
(" Function offset 0x%x: " % entry
.function_offset
)
648 if isinstance(entry
, CannotUnwindEHABIEntry
):
649 self
._emitline
("[cantunwind]")
651 elif entry
.eh_table_offset
:
652 self
._emitline
("@0x%x" % entry
.eh_table_offset
)
654 self
._emitline
("Compact (inline)")
655 if isinstance(entry
, GenericEHABIEntry
):
656 self
._emitline
(" Personality: 0x%x" % entry
.personality
)
658 self
._emitline
(" Compact model index: %d" % entry
.personality
)
659 for mnemonic_item
in entry
.mnmemonic_array():
661 self
._emitline
(mnemonic_item
)
663 def display_version_info(self
):
664 """ Display the version info contained in the file
666 self
._init
_versioninfo
()
668 if not self
._versioninfo
['type']:
669 self
._emitline
("\nNo version information found in this file.")
672 for section
in self
.elffile
.iter_sections():
673 if isinstance(section
, GNUVerSymSection
):
674 self
._print
_version
_section
_header
(section
, 'Version symbols')
675 num_symbols
= section
.num_symbols()
677 # Symbol version info are printed four by four entries
678 for idx_by_4
in range(0, num_symbols
, 4):
680 self
._emit
(' %03x:' % idx_by_4
)
682 for idx
in range(idx_by_4
, min(idx_by_4
+ 4, num_symbols
)):
684 symbol_version
= self
._symbol
_version
(idx
)
685 if symbol_version
['index'] == 'VER_NDX_LOCAL':
687 version_name
= '(*local*)'
688 elif symbol_version
['index'] == 'VER_NDX_GLOBAL':
690 version_name
= '(*global*)'
692 version_index
= symbol_version
['index']
693 version_name
= '(%(name)s)' % symbol_version
695 visibility
= 'h' if symbol_version
['hidden'] else ' '
697 self
._emit
('%4x%s%-13s' % (
698 version_index
, visibility
, version_name
))
702 elif isinstance(section
, GNUVerDefSection
):
703 self
._print
_version
_section
_header
(
704 section
, 'Version definition', indent
=2)
707 for verdef
, verdaux_iter
in section
.iter_versions():
708 verdaux
= next(verdaux_iter
)
711 if verdef
['vd_flags']:
712 flags
= describe_ver_flags(verdef
['vd_flags'])
713 # Mimic exactly the readelf output
718 self
._emitline
(' %s: Rev: %i Flags: %s Index: %i'
719 ' Cnt: %i Name: %s' % (
720 self
._format
_hex
(offset
, fieldsize
=6,
722 verdef
['vd_version'], flags
, verdef
['vd_ndx'],
723 verdef
['vd_cnt'], name
))
726 offset
+ verdef
['vd_aux'] + verdaux
['vda_next'])
727 for idx
, verdaux
in enumerate(verdaux_iter
, start
=1):
728 self
._emitline
(' %s: Parent %i: %s' %
729 (self
._format
_hex
(verdaux_offset
, fieldsize
=4),
731 verdaux_offset
+= verdaux
['vda_next']
733 offset
+= verdef
['vd_next']
735 elif isinstance(section
, GNUVerNeedSection
):
736 self
._print
_version
_section
_header
(section
, 'Version needs')
739 for verneed
, verneed_iter
in section
.iter_versions():
741 self
._emitline
(' %s: Version: %i File: %s Cnt: %i' % (
742 self
._format
_hex
(offset
, fieldsize
=6,
744 verneed
['vn_version'], verneed
.name
,
747 vernaux_offset
= offset
+ verneed
['vn_aux']
748 for idx
, vernaux
in enumerate(verneed_iter
, start
=1):
749 if vernaux
['vna_flags']:
750 flags
= describe_ver_flags(vernaux
['vna_flags'])
751 # Mimic exactly the readelf output
757 ' %s: Name: %s Flags: %s Version: %i' % (
758 self
._format
_hex
(vernaux_offset
, fieldsize
=4),
760 vernaux
['vna_other']))
762 vernaux_offset
+= vernaux
['vna_next']
764 offset
+= verneed
['vn_next']
766 def display_arch_specific(self
):
767 """ Display the architecture-specific info contained in the file.
769 if self
.elffile
['e_machine'] == 'EM_ARM':
770 self
._display
_arch
_specific
_arm
()
772 def display_hex_dump(self
, section_spec
):
773 """ Display a hex dump of a section. section_spec is either a section
776 section
= self
._section
_from
_spec
(section_spec
)
778 # readelf prints the warning to stderr. Even though stderrs are not compared
779 # in tests, we comply with that behavior.
780 sys
.stderr
.write('readelf: Warning: Section \'%s\' was not dumped because it does not exist!\n' % (
783 if section
['sh_type'] == 'SHT_NOBITS':
784 self
._emitline
("\nSection '%s' has no data to dump." % (
788 self
._emitline
("\nHex dump of section '%s':" % section
.name
)
789 self
._note
_relocs
_for
_section
(section
)
790 addr
= section
['sh_addr']
791 data
= section
.data()
794 while dataptr
< len(data
):
795 bytesleft
= len(data
) - dataptr
796 # chunks of 16 bytes per line
797 linebytes
= 16 if bytesleft
> 16 else bytesleft
799 self
._emit
(' %s ' % self
._format
_hex
(addr
, fieldsize
=8))
802 self
._emit
('%2.2x' % data
[dataptr
+ i
])
808 for i
in range(linebytes
):
809 c
= data
[dataptr
+ i
: dataptr
+ i
+ 1]
810 if c
[0] >= 32 and c
[0] < 0x7f:
811 self
._emit
(bytes2str(c
))
813 self
._emit
(bytes2str(b
'.'))
821 def display_string_dump(self
, section_spec
):
822 """ Display a strings dump of a section. section_spec is either a
823 section number or a name.
825 section
= self
._section
_from
_spec
(section_spec
)
827 # readelf prints the warning to stderr. Even though stderrs are not compared
828 # in tests, we comply with that behavior.
829 sys
.stderr
.write('readelf.py: Warning: Section \'%s\' was not dumped because it does not exist!\n' % (
832 if section
['sh_type'] == 'SHT_NOBITS':
833 self
._emitline
("\nSection '%s' has no data to dump." % (
837 self
._emitline
("\nString dump of section '%s':" % section
.name
)
840 data
= section
.data()
843 while dataptr
< len(data
):
844 while ( dataptr
< len(data
) and
845 not (32 <= data
[dataptr
] <= 127)):
848 if dataptr
>= len(data
):
852 while endptr
< len(data
) and data
[endptr
] != 0:
856 self
._emitline
(' [%6x] %s' % (
857 dataptr
, bytes2str(data
[dataptr
:endptr
])))
862 self
._emitline
(' No strings found in this section.')
866 def display_debug_dump(self
, dump_what
):
867 """ Dump a DWARF section
869 self
._init
_dwarfinfo
()
870 if self
._dwarfinfo
is None:
873 set_global_machine_arch(self
.elffile
.get_machine_arch())
875 if dump_what
== 'info':
876 self
._dump
_debug
_info
()
877 elif dump_what
== 'decodedline':
878 self
._dump
_debug
_line
_programs
()
879 elif dump_what
== 'frames':
880 self
._dump
_debug
_frames
()
881 elif dump_what
== 'frames-interp':
882 self
._dump
_debug
_frames
_interp
()
883 elif dump_what
== 'aranges':
884 self
._dump
_debug
_aranges
()
885 elif dump_what
in { 'pubtypes', 'pubnames' }:
886 self
._dump
_debug
_namelut
(dump_what
)
887 elif dump_what
== 'loc':
888 self
._dump
_debug
_locations
()
889 elif dump_what
== 'Ranges':
890 self
._dump
_debug
_ranges
()
892 self
._emitline
('debug dump not yet supported for "%s"' % dump_what
)
894 def _format_hex(self
, addr
, fieldsize
=None, fullhex
=False, lead0x
=True,
896 """ Format an address into a hexadecimal string.
899 Size of the hexadecimal field (with leading zeros to fit the
900 address into. For example with fieldsize=8, the format will
902 If None, the minimal required field size will be used.
905 If True, override fieldsize to set it to the maximal size
906 needed for the elfclass
909 If True, leading 0x is added
912 If True, override lead0x to emulate the alternate
913 hexadecimal form specified in format string with the #
914 character: only non-zero values are prefixed with 0x.
915 This form is used by readelf.
924 s
= '0x' if lead0x
else ''
926 fieldsize
= 8 if self
.elffile
.elfclass
== 32 else 16
927 if fieldsize
is None:
930 field
= '%' + '0%sx' % fieldsize
931 return s
+ field
% addr
933 def _print_version_section_header(self
, version_section
, name
, lead0x
=True,
935 """ Print a section header of one version related section (versym,
936 verneed or verdef) with some options to accomodate readelf
937 little differences between each header (e.g. indentation
940 if hasattr(version_section
, 'num_versions'):
941 num_entries
= version_section
.num_versions()
943 num_entries
= version_section
.num_symbols()
945 self
._emitline
("\n%s section '%s' contains %d %s:" % (
946 name
, version_section
.name
, num_entries
,
947 'entry' if num_entries
== 1 else 'entries'))
948 self
._emitline
('%sAddr: %s Offset: %s Link: %i (%s)' % (
951 version_section
['sh_addr'], fieldsize
=16, lead0x
=lead0x
),
953 version_section
['sh_offset'], fieldsize
=6, lead0x
=True),
954 version_section
['sh_link'],
955 self
.elffile
.get_section(version_section
['sh_link']).name
959 def _init_versioninfo(self
):
960 """ Search and initialize informations about version related sections
961 and the kind of versioning used (GNU or Solaris).
963 if self
._versioninfo
is not None:
966 self
._versioninfo
= {'versym': None, 'verdef': None,
967 'verneed': None, 'type': None}
969 for section
in self
.elffile
.iter_sections():
970 if isinstance(section
, GNUVerSymSection
):
971 self
._versioninfo
['versym'] = section
972 elif isinstance(section
, GNUVerDefSection
):
973 self
._versioninfo
['verdef'] = section
974 elif isinstance(section
, GNUVerNeedSection
):
975 self
._versioninfo
['verneed'] = section
976 elif isinstance(section
, DynamicSection
):
977 for tag
in section
.iter_tags():
978 if tag
['d_tag'] == 'DT_VERSYM':
979 self
._versioninfo
['type'] = 'GNU'
982 if not self
._versioninfo
['type'] and (
983 self
._versioninfo
['verneed'] or self
._versioninfo
['verdef']):
984 self
._versioninfo
['type'] = 'Solaris'
986 def _symbol_version(self
, nsym
):
987 """ Return a dict containing information on the
988 or None if no version information is available
990 self
._init
_versioninfo
()
992 symbol_version
= dict.fromkeys(('index', 'name', 'filename', 'hidden'))
994 if (not self
._versioninfo
['versym'] or
995 nsym
>= self
._versioninfo
['versym'].num_symbols()):
998 symbol
= self
._versioninfo
['versym'].get_symbol(nsym
)
999 index
= symbol
.entry
['ndx']
1000 if not index
in ('VER_NDX_LOCAL', 'VER_NDX_GLOBAL'):
1003 if self
._versioninfo
['type'] == 'GNU':
1004 # In GNU versioning mode, the highest bit is used to
1005 # store whether the symbol is hidden or not
1008 symbol_version
['hidden'] = True
1010 if (self
._versioninfo
['verdef'] and
1011 index
<= self
._versioninfo
['verdef'].num_versions()):
1013 self
._versioninfo
['verdef'].get_version(index
)
1014 symbol_version
['name'] = next(verdaux_iter
).name
1016 verneed
, vernaux
= \
1017 self
._versioninfo
['verneed'].get_version(index
)
1018 symbol_version
['name'] = vernaux
.name
1019 symbol_version
['filename'] = verneed
.name
1021 symbol_version
['index'] = index
1022 return symbol_version
1024 def _section_from_spec(self
, spec
):
1025 """ Retrieve a section given a "spec" (either number or name).
1026 Return None if no such section exists in the file.
1030 if num
< self
.elffile
.num_sections():
1031 return self
.elffile
.get_section(num
)
1035 # Not a number. Must be a name then
1036 return self
.elffile
.get_section_by_name(spec
)
1038 def _get_symbol_shndx(self
, symbol
, symbol_index
, symtab_index
):
1039 """ Get the index into the section header table for the "symbol"
1040 at "symbol_index" located in the symbol table with section index
1043 symbol_shndx
= symbol
['st_shndx']
1044 if symbol_shndx
!= SHN_INDICES
.SHN_XINDEX
:
1047 # Check for or lazily construct index section mapping (symbol table
1048 # index -> corresponding symbol table index section object)
1049 if self
._shndx
_sections
is None:
1050 self
._shndx
_sections
= {sec
.symboltable
: sec
for sec
in self
.elffile
.iter_sections()
1051 if isinstance(sec
, SymbolTableIndexSection
)}
1052 return self
._shndx
_sections
[symtab_index
].get_section_index(symbol_index
)
1054 def _note_relocs_for_section(self
, section
):
1055 """ If there are relocation sections pointing to the givne section,
1056 emit a note about it.
1058 for relsec
in self
.elffile
.iter_sections():
1059 if isinstance(relsec
, RelocationSection
):
1060 info_idx
= relsec
['sh_info']
1061 if self
.elffile
.get_section(info_idx
) == section
:
1062 self
._emitline
(' Note: This section has relocations against it, but these have NOT been applied to this dump.')
1065 def _init_dwarfinfo(self
):
1066 """ Initialize the DWARF info contained in the file and assign it to
1068 Leave self._dwarfinfo at None if no DWARF info was found in the file
1070 if self
._dwarfinfo
is not None:
1073 if self
.elffile
.has_dwarf_info():
1074 self
._dwarfinfo
= self
.elffile
.get_dwarf_info()
1076 self
._dwarfinfo
= None
1078 def _dump_debug_info(self
):
1079 """ Dump the debugging info section.
1081 if not self
._dwarfinfo
.has_debug_info
:
1083 self
._emitline
('Contents of the %s section:\n' % self
._dwarfinfo
.debug_info_sec
.name
)
1085 # Offset of the .debug_info section in the stream
1086 section_offset
= self
._dwarfinfo
.debug_info_sec
.global_offset
1088 for cu
in self
._dwarfinfo
.iter_CUs():
1089 self
._emitline
(' Compilation Unit @ offset %s:' %
1090 self
._format
_hex
(cu
.cu_offset
))
1091 self
._emitline
(' Length: %s (%s)' % (
1092 self
._format
_hex
(cu
['unit_length']),
1093 '%s-bit' % cu
.dwarf_format()))
1094 self
._emitline
(' Version: %s' % cu
['version'])
1095 if cu
.header
.get("unit_type", False):
1096 ut
= next((key
for key
, value
in ENUM_DW_UT
.items() if value
== cu
.header
.unit_type
), '?')
1097 self
._emitline
(' Unit Type: %s (%d)' % (ut
, cu
.header
.unit_type
))
1098 self
._emitline
(' Abbrev Offset: %s' % (
1099 self
._format
_hex
(cu
['debug_abbrev_offset']))),
1100 self
._emitline
(' Pointer Size: %s' % cu
['address_size'])
1102 # The nesting depth of each DIE within the tree of DIEs must be
1103 # displayed. To implement this, a counter is incremented each time
1104 # the current DIE has children, and decremented when a null die is
1105 # encountered. Due to the way the DIE tree is serialized, this will
1106 # correctly reflect the nesting depth
1109 current_function
= None
1110 for die
in cu
.iter_DIEs():
1111 if die
.tag
== 'DW_TAG_subprogram':
1112 current_function
= die
1113 self
._emitline
(' <%s><%x>: Abbrev Number: %s%s' % (
1117 (' (%s)' % die
.tag
) if not die
.is_null() else ''))
1122 for attr
in die
.attributes
.values():
1124 # Unknown attribute values are passed-through as integers
1125 if isinstance(name
, int):
1126 name
= 'Unknown AT value: %x' % name
1128 attr_desc
= describe_attr_value(attr
, die
, section_offset
)
1130 if 'DW_OP_fbreg' in attr_desc
and current_function
and not 'DW_AT_frame_base' in current_function
.attributes
:
1131 postfix
= ' [without dw_at_frame_base]'
1135 self
._emitline
(' <%x> %-18s: %s%s' % (
1141 if die
.has_children
:
1146 def _dump_debug_line_programs(self
):
1147 """ Dump the (decoded) line programs from .debug_line
1148 The programs are dumped in the order of the CUs they belong to.
1150 if not self
._dwarfinfo
.has_debug_info
:
1152 self
._emitline
('Contents of the %s section:' % self
._dwarfinfo
.debug_line_sec
.name
)
1155 for cu
in self
._dwarfinfo
.iter_CUs():
1156 lineprogram
= self
._dwarfinfo
.line_program_for_CU(cu
)
1157 ver5
= lineprogram
.header
.version
>= 5
1159 cu_filename
= bytes2str(lineprogram
['file_entry'][0].name
)
1160 if len(lineprogram
['include_directory']) > 0:
1161 # GNU readelf 2.38 only outputs directory in wide mode
1162 self
._emitline
('%s:' % cu_filename
)
1164 self
._emitline
('CU: %s:' % cu_filename
)
1166 self
._emitline
('File name Line number Starting address Stmt')
1167 # GNU readelf has a View column that we don't try to replicate
1168 # The autotest has logic in place to ignore that
1170 # Print each state's file, line and address information. For some
1171 # instructions other output is needed to be compatible with
1173 for entry
in lineprogram
.get_entries():
1176 # Special handling for commands that don't set a new state
1177 if entry
.command
== DW_LNS_set_file
:
1178 file_entry
= lineprogram
['file_entry'][entry
.args
[0] - 1]
1179 if file_entry
.dir_index
== 0:
1181 self
._emitline
('\n./%s:[++]' % (
1182 bytes2str(file_entry
.name
)))
1184 self
._emitline
('\n%s/%s:' % (
1185 bytes2str(lineprogram
['include_directory'][file_entry
.dir_index
- 1]),
1186 bytes2str(file_entry
.name
)))
1187 elif entry
.command
== DW_LNE_define_file
:
1188 self
._emitline
('%s:' % (
1189 bytes2str(lineprogram
['include_directory'][entry
.args
[0].dir_index
])))
1190 elif lineprogram
['version'] < 4 or self
.elffile
['e_machine'] == 'EM_PPC64':
1191 self
._emitline
('%-35s %11s %18s %s' % (
1192 bytes2str(lineprogram
['file_entry'][state
.file - 1].name
),
1193 state
.line
if not state
.end_sequence
else '-',
1194 '0' if state
.address
== 0 else self
._format
_hex
(state
.address
),
1195 'x' if state
.is_stmt
and not state
.end_sequence
else ''))
1197 # In readelf, on non-VLIW machines there is no op_index postfix after address.
1198 # It used to be unconditional.
1199 self
._emitline
('%-35s %s %18s%s %s' % (
1200 bytes2str(lineprogram
['file_entry'][state
.file - 1].name
),
1201 "%11d" % (state
.line
,) if not state
.end_sequence
else '-',
1202 '0' if state
.address
== 0 else self
._format
_hex
(state
.address
),
1203 '' if lineprogram
.header
.maximum_operations_per_instruction
== 1 else '[%d]' % (state
.op_index
,),
1204 'x' if state
.is_stmt
and not state
.end_sequence
else ''))
1205 if entry
.command
== DW_LNS_copy
:
1206 # Another readelf oddity...
1209 def _dump_frames_info(self
, section
, cfi_entries
):
1210 """ Dump the raw call frame info in a section.
1212 `section` is the Section instance that contains the call frame info
1213 while `cfi_entries` must be an iterable that yields the sequence of
1214 CIE or FDE instances.
1216 self
._emitline
('Contents of the %s section:' % section
.name
)
1218 for entry
in cfi_entries
:
1219 if isinstance(entry
, CIE
):
1220 self
._emitline
('\n%08x %s %s CIE' % (
1222 self
._format
_hex
(entry
['length'], fullhex
=True, lead0x
=False),
1223 self
._format
_hex
(entry
['CIE_id'], fieldsize
=8, lead0x
=False)))
1224 self
._emitline
(' Version: %d' % entry
['version'])
1225 self
._emitline
(' Augmentation: "%s"' % bytes2str(entry
['augmentation']))
1226 self
._emitline
(' Code alignment factor: %u' % entry
['code_alignment_factor'])
1227 self
._emitline
(' Data alignment factor: %d' % entry
['data_alignment_factor'])
1228 self
._emitline
(' Return address column: %d' % entry
['return_address_register'])
1229 if entry
.augmentation_bytes
:
1230 self
._emitline
(' Augmentation data: {}'.format(' '.join(
1231 '{:02x}'.format(ord(b
))
1232 for b
in iterbytes(entry
.augmentation_bytes
)
1236 elif isinstance(entry
, FDE
):
1237 self
._emitline
('\n%08x %s %s FDE cie=%08x pc=%s..%s' % (
1239 self
._format
_hex
(entry
['length'], fullhex
=True, lead0x
=False),
1240 self
._format
_hex
(entry
['CIE_pointer'], fieldsize
=8, lead0x
=False),
1242 self
._format
_hex
(entry
['initial_location'], fullhex
=True, lead0x
=False),
1244 entry
['initial_location'] + entry
['address_range'],
1245 fullhex
=True, lead0x
=False)))
1246 if entry
.augmentation_bytes
:
1247 self
._emitline
(' Augmentation data: {}'.format(' '.join(
1248 '{:02x}'.format(ord(b
))
1249 for b
in iterbytes(entry
.augmentation_bytes
)
1252 else: # ZERO terminator
1253 assert isinstance(entry
, ZERO
)
1254 self
._emitline
('\n%08x ZERO terminator' % entry
.offset
)
1257 self
._emit
(describe_CFI_instructions(entry
))
1260 def _dump_debug_frames(self
):
1261 """ Dump the raw frame info from .debug_frame and .eh_frame sections.
1263 if self
._dwarfinfo
.has_EH_CFI():
1264 self
._dump
_frames
_info
(
1265 self
._dwarfinfo
.eh_frame_sec
,
1266 self
._dwarfinfo
.EH_CFI_entries())
1269 if self
._dwarfinfo
.has_CFI():
1270 self
._dump
_frames
_info
(
1271 self
._dwarfinfo
.debug_frame_sec
,
1272 self
._dwarfinfo
.CFI_entries())
1274 def _dump_debug_namelut(self
, what
):
1276 Dump the debug pubnames section.
1278 if what
== 'pubnames':
1279 namelut
= self
._dwarfinfo
.get_pubnames()
1280 section
= self
._dwarfinfo
.debug_pubnames_sec
1282 namelut
= self
._dwarfinfo
.get_pubtypes()
1283 section
= self
._dwarfinfo
.debug_pubtypes_sec
1285 # readelf prints nothing if the section is not present.
1286 if namelut
is None or len(namelut
) == 0:
1289 self
._emitline
('Contents of the %s section:' % section
.name
)
1292 cu_headers
= namelut
.get_cu_headers()
1294 # go over CU-by-CU first and item-by-item next.
1295 for (cu_hdr
, (cu_ofs
, items
)) in izip(cu_headers
, itertools
.groupby(
1296 namelut
.items(), key
= lambda x
: x
[1].cu_ofs
)):
1298 self
._emitline
(' Length: %d' % cu_hdr
.unit_length
)
1299 self
._emitline
(' Version: %d' % cu_hdr
.version
)
1300 self
._emitline
(' Offset into .debug_info section: 0x%x' % cu_hdr
.debug_info_offset
)
1301 self
._emitline
(' Size of area in .debug_info section: %d' % cu_hdr
.debug_info_length
)
1303 self
._emitline
(' Offset Name')
1305 self
._emitline
(' %x %s' % (item
[1].die_ofs
- cu_ofs
, item
[0]))
1308 def _dump_debug_aranges(self
):
1309 """ Dump the aranges table
1311 aranges_table
= self
._dwarfinfo
.get_aranges()
1312 if aranges_table
== None:
1314 # Seems redundant, but we need to get the unsorted set of entries
1315 # to match system readelf.
1316 # Also, sometimes there are blank sections in aranges, but readelf
1317 # dumps them, so we should too.
1318 unordered_entries
= aranges_table
._get
_entries
(need_empty
=True)
1320 if len(unordered_entries
) == 0:
1322 self
._emitline
("Section '.debug_aranges' has no debugging data.")
1325 self
._emitline
('Contents of the %s section:' % self
._dwarfinfo
.debug_aranges_sec
.name
)
1328 for entry
in unordered_entries
:
1329 if prev_offset
!= entry
.info_offset
:
1330 if entry
!= unordered_entries
[0]:
1331 self
._emitline
(' %s %s' % (
1332 self
._format
_hex
(0, fullhex
=True, lead0x
=False),
1333 self
._format
_hex
(0, fullhex
=True, lead0x
=False)))
1334 self
._emitline
(' Length: %d' % (entry
.unit_length
))
1335 self
._emitline
(' Version: %d' % (entry
.version
))
1336 self
._emitline
(' Offset into .debug_info: 0x%x' % (entry
.info_offset
))
1337 self
._emitline
(' Pointer Size: %d' % (entry
.address_size
))
1338 self
._emitline
(' Segment Size: %d' % (entry
.segment_size
))
1340 self
._emitline
(' Address Length')
1341 if entry
.begin_addr
!= 0 or entry
.length
!= 0:
1342 self
._emitline
(' %s %s' % (
1343 self
._format
_hex
(entry
.begin_addr
, fullhex
=True, lead0x
=False),
1344 self
._format
_hex
(entry
.length
, fullhex
=True, lead0x
=False)))
1345 prev_offset
= entry
.info_offset
1346 self
._emitline
(' %s %s' % (
1347 self
._format
_hex
(0, fullhex
=True, lead0x
=False),
1348 self
._format
_hex
(0, fullhex
=True, lead0x
=False)))
1350 def _dump_frames_interp_info(self
, section
, cfi_entries
):
1351 """ Dump interpreted (decoded) frame information in a section.
1353 `section` is the Section instance that contains the call frame info
1354 while `cfi_entries` must be an iterable that yields the sequence of
1355 CIE or FDE instances.
1357 self
._emitline
('Contents of the %s section:' % section
.name
)
1359 for entry
in cfi_entries
:
1360 if isinstance(entry
, CIE
):
1361 self
._emitline
('\n%08x %s %s CIE "%s" cf=%d df=%d ra=%d' % (
1363 self
._format
_hex
(entry
['length'], fullhex
=True, lead0x
=False),
1364 self
._format
_hex
(entry
['CIE_id'], fieldsize
=8, lead0x
=False),
1365 bytes2str(entry
['augmentation']),
1366 entry
['code_alignment_factor'],
1367 entry
['data_alignment_factor'],
1368 entry
['return_address_register']))
1369 ra_regnum
= entry
['return_address_register']
1371 elif isinstance(entry
, FDE
):
1372 self
._emitline
('\n%08x %s %s FDE cie=%08x pc=%s..%s' % (
1374 self
._format
_hex
(entry
['length'], fullhex
=True, lead0x
=False),
1375 self
._format
_hex
(entry
['CIE_pointer'], fieldsize
=8, lead0x
=False),
1377 self
._format
_hex
(entry
['initial_location'], fullhex
=True, lead0x
=False),
1378 self
._format
_hex
(entry
['initial_location'] + entry
['address_range'],
1379 fullhex
=True, lead0x
=False)))
1380 ra_regnum
= entry
.cie
['return_address_register']
1382 # If the FDE brings adds no unwinding information compared to
1383 # its CIE, omit its table.
1384 if (len(entry
.get_decoded().table
) ==
1385 len(entry
.cie
.get_decoded().table
)):
1388 else: # ZERO terminator
1389 assert isinstance(entry
, ZERO
)
1390 self
._emitline
('\n%08x ZERO terminator' % entry
.offset
)
1394 decoded_table
= entry
.get_decoded()
1395 if len(decoded_table
.table
) == 0:
1398 # Print the heading row for the decoded table
1400 self
._emit
(' ' if entry
.structs
.address_size
== 4 else ' ')
1403 # Look at the registers the decoded table describes.
1404 # We build reg_order here to match readelf's order. In particular,
1405 # registers are sorted by their number, and the register matching
1406 # ra_regnum is always listed last with a special heading.
1407 decoded_table
= entry
.get_decoded()
1408 reg_order
= sorted(filter(
1409 lambda r
: r
!= ra_regnum
,
1410 decoded_table
.reg_order
))
1411 if len(decoded_table
.reg_order
):
1413 # Headings for the registers
1414 for regnum
in reg_order
:
1415 self
._emit
('%-6s' % describe_reg_name(regnum
))
1416 self
._emitline
('ra ')
1418 # Now include ra_regnum in reg_order to print its values
1419 # similarly to the other registers.
1420 reg_order
.append(ra_regnum
)
1424 for line
in decoded_table
.table
:
1425 self
._emit
(self
._format
_hex
(
1426 line
['pc'], fullhex
=True, lead0x
=False))
1428 if line
['cfa'] is not None:
1429 s
= describe_CFI_CFA_rule(line
['cfa'])
1432 self
._emit
(' %-9s' % s
)
1434 for regnum
in reg_order
:
1436 s
= describe_CFI_register_rule(line
[regnum
])
1439 self
._emit
('%-6s' % s
)
1443 def _dump_debug_frames_interp(self
):
1444 """ Dump the interpreted (decoded) frame information from .debug_frame
1445 and .eh_frame sections.
1447 if self
._dwarfinfo
.has_EH_CFI():
1448 self
._dump
_frames
_interp
_info
(
1449 self
._dwarfinfo
.eh_frame_sec
,
1450 self
._dwarfinfo
.EH_CFI_entries())
1453 if self
._dwarfinfo
.has_CFI():
1454 self
._dump
_frames
_interp
_info
(
1455 self
._dwarfinfo
.debug_frame_sec
,
1456 self
._dwarfinfo
.CFI_entries())
1458 def _dump_debug_locations(self
):
1459 """ Dump the location lists from .debug_loc/.debug_loclists section
1461 di
= self
._dwarfinfo
1462 loc_lists_sec
= di
.location_lists()
1463 if not loc_lists_sec
: # No locations section - readelf outputs nothing
1466 if isinstance(loc_lists_sec
, LocationListsPair
):
1467 self
._dump
_debug
_locsection
(di
, loc_lists_sec
._loc
)
1468 self
._dump
_debug
_locsection
(di
, loc_lists_sec
._loclists
)
1470 self
._dump
_debug
_locsection
(di
, loc_lists_sec
)
1472 def _dump_debug_locsection(self
, di
, loc_lists_sec
):
1473 """ Dump the location lists from .debug_loc/.debug_loclists section
1475 ver5
= loc_lists_sec
.version
>= 5
1476 section_name
= (di
.debug_loclists_sec
if ver5
else di
.debug_loc_sec
).name
1478 # To dump a location list, one needs to know the CU.
1479 # Scroll through DIEs once, list the known location list offsets.
1480 # Don't need this CU/DIE scan if all entries are absolute or prefixed by base,
1481 # but let's not optimize for that yet.
1482 cu_map
= dict() # Loc list offset => CU
1483 for cu
in di
.iter_CUs():
1484 for die
in cu
.iter_DIEs():
1485 for key
in die
.attributes
:
1486 attr
= die
.attributes
[key
]
1487 if (LocationParser
.attribute_has_location(attr
, cu
['version']) and
1488 LocationParser
._attribute
_has
_loc
_list
(attr
, cu
['version'])):
1489 cu_map
[attr
.value
] = cu
1491 addr_size
= di
.config
.default_address_size
# In bytes, 4 or 8
1492 addr_width
= addr_size
* 2 # In hex digits, 8 or 16
1493 line_template
= " %%08x %%0%dx %%0%dx %%s%%s" % (addr_width
, addr_width
)
1495 loc_lists
= list(loc_lists_sec
.iter_location_lists())
1496 if len(loc_lists
) == 0:
1497 # Present but empty locations section - readelf outputs a message
1498 self
._emitline
("\nSection '%s' has no debugging data." % (section_name
,))
1501 self
._emitline
('Contents of the %s section:\n' % (section_name
,))
1502 self
._emitline
(' Offset Begin End Expression')
1503 for loc_list
in loc_lists
:
1504 self
._dump
_loclist
(loc_list
, line_template
, cu_map
)
1506 def _dump_loclist(self
, loc_list
, line_template
, cu_map
):
1512 for entry
in loc_list
:
1513 if isinstance(entry
, LocationViewPair
):
1514 has_views
= in_views
= True
1515 # The "v" before address is conditional in binutils, haven't figured out how
1516 self
._emitline
(" %08x v%015x v%015x location view pair" % (entry
.entry_offset
, entry
.begin
, entry
.end
))
1522 # Readelf quirk: indexed loclists don't show the real base IP
1526 cu
= cu_map
.get(entry
.entry_offset
, False)
1528 raise ValueError("Location list can't be tracked to a CU")
1530 if isinstance(entry
, LocationEntry
):
1531 if base_ip
is None and not entry
.is_absolute
:
1532 base_ip
= _get_cu_base(cu
)
1534 begin_offset
= (0 if entry
.is_absolute
else base_ip
) + entry
.begin_offset
1535 end_offset
= (0 if entry
.is_absolute
else base_ip
) + entry
.end_offset
1536 expr
= describe_DWARF_expr(entry
.loc_expr
, cu
.structs
, cu
.cu_offset
)
1538 view
= loc_list
[loc_entry_count
]
1539 postfix
= ' (start == end)' if entry
.begin_offset
== entry
.end_offset
and view
.begin
== view
.end
else ''
1540 self
._emitline
(' %08x v%015x v%015x views at %08x for:' %(
1545 self
._emitline
(' %016x %016x %s%s' %(
1550 loc_entry_count
+= 1
1552 postfix
= ' (start == end)' if entry
.begin_offset
== entry
.end_offset
else ''
1553 self
._emitline
(line_template
% (
1559 elif isinstance(entry
, LocBaseAddressEntry
):
1560 base_ip
= entry
.base_address
1561 self
._emitline
(" %08x %016x (base address)" % (entry
.entry_offset
, entry
.base_address
))
1563 # Pyelftools doesn't store the terminating entry,
1564 # but readelf emits its offset, so this should too.
1566 self
._emitline
(" %08x <End of list>" % (last
.entry_offset
+ last
.entry_length
))
1568 def _dump_debug_ranges(self
):
1569 # TODO: GNU readelf format doesn't need entry_length?
1570 di
= self
._dwarfinfo
1571 range_lists_sec
= di
.range_lists()
1572 if not range_lists_sec
: # No ranges section - readelf outputs nothing
1575 if isinstance(range_lists_sec
, RangeListsPair
):
1576 self
._dump
_debug
_rangesection
(di
, range_lists_sec
._ranges
)
1577 self
._dump
_debug
_rangesection
(di
, range_lists_sec
._rnglists
)
1579 self
._dump
_debug
_rangesection
(di
, range_lists_sec
)
1581 def _dump_debug_rangesection(self
, di
, range_lists_sec
):
1582 # In the master branch of binutils, the v5 dump format is way different by now.
1584 ver5
= range_lists_sec
.version
>= 5
1585 section_name
= (di
.debug_rnglists_sec
if ver5
else di
.debug_ranges_sec
).name
1586 addr_size
= di
.config
.default_address_size
# In bytes, 4 or 8
1587 addr_width
= addr_size
* 2 # In hex digits, 8 or 16
1588 line_template
= " %%08x %%0%dx %%0%dx %%s" % (addr_width
, addr_width
)
1589 base_template
= " %%08x %%0%dx (base address)" % (addr_width
)
1591 range_lists
= list(range_lists_sec
.iter_range_lists())
1592 if len(range_lists
) == 0:
1593 # Present but empty locations section - readelf outputs a message
1594 self
._emitline
("\nSection '%s' has no debugging data." % section_name
)
1597 # In order to determine the base address of the range
1598 # We need to know the corresponding CU.
1599 cu_map
= {die
.attributes
['DW_AT_ranges'].value
: cu
# Range list offset => CU
1600 for cu
in di
.iter_CUs()
1601 for die
in cu
.iter_DIEs()
1602 if 'DW_AT_ranges' in die
.attributes
}
1604 self
._emitline
('Contents of the %s section:\n' % section_name
)
1605 self
._emitline
(' Offset Begin End')
1607 for range_list
in range_lists
:
1608 self
._dump
_rangelist
(range_list
, cu_map
, ver5
, line_template
, base_template
)
1610 def _dump_rangelist(self
, range_list
, cu_map
, ver5
, line_template
, base_template
):
1611 # Weird discrepancy in binutils: for DWARFv5 it outputs entry offset,
1612 # for DWARF<=4 list offset.
1613 first
= range_list
[0]
1614 base_ip
= _get_cu_base(cu_map
[first
.entry_offset
])
1615 for entry
in range_list
:
1616 if isinstance(entry
, RangeEntry
):
1617 postfix
= ' (start == end)' if entry
.begin_offset
== entry
.end_offset
else ''
1618 self
._emitline
(line_template
% (
1619 entry
.entry_offset
if ver5
else first
.entry_offset
,
1620 (0 if entry
.is_absolute
else base_ip
) + entry
.begin_offset
,
1621 (0 if entry
.is_absolute
else base_ip
) + entry
.end_offset
,
1623 elif isinstance(entry
,RangeBaseAddressEntry
):
1624 base_ip
= entry
.base_address
1625 self
._emitline
(base_template
% (
1626 entry
.entry_offset
if ver5
else first
.entry_offset
,
1627 entry
.base_address
))
1629 raise NotImplementedError("Unknown object in a range list")
1630 last
= range_list
[-1]
1631 self
._emitline
(' %08x <End of list>' % (last
.entry_offset
+ last
.entry_length
if ver5
else first
.entry_offset
))
1633 def _display_arch_specific_arm(self
):
1634 """ Display the ARM architecture-specific info contained in the file.
1636 attr_sec
= self
.elffile
.get_section_by_name('.ARM.attributes')
1638 for s
in attr_sec
.iter_subsections():
1639 self
._emitline
("Attribute Section: %s" % s
.header
['vendor_name'])
1640 for ss
in s
.iter_subsubsections():
1641 h_val
= "" if ss
.header
.extra
is None else " ".join("%d" % x
for x
in ss
.header
.extra
)
1642 self
._emitline
(describe_attr_tag_arm(ss
.header
.tag
, h_val
, None))
1644 for attr
in ss
.iter_attributes():
1646 self
._emitline
(describe_attr_tag_arm(attr
.tag
,
1650 def _emit(self
, s
=''):
1651 """ Emit an object to output
1653 self
.output
.write(str(s
))
1655 def _emitline(self
, s
=''):
1656 """ Emit an object to output, followed by a newline
1658 self
.output
.write(str(s
).rstrip() + '\n')
1661 SCRIPT_DESCRIPTION
= 'Display information about the contents of ELF format files'
1662 VERSION_STRING
= '%%(prog)s: based on pyelftools %s' % __version__
1665 def main(stream
=None):
1666 # parse the command-line arguments and invoke ReadElf
1667 argparser
= argparse
.ArgumentParser(
1668 usage
='usage: %(prog)s [options] <elf-file>',
1669 description
=SCRIPT_DESCRIPTION
,
1670 add_help
=False, # -h is a real option of readelf
1672 argparser
.add_argument('file',
1673 nargs
='?', default
=None,
1674 help='ELF file to parse')
1675 argparser
.add_argument('-v', '--version',
1676 action
='version', version
=VERSION_STRING
)
1677 argparser
.add_argument('-d', '--dynamic',
1678 action
='store_true', dest
='show_dynamic_tags',
1679 help='Display the dynamic section')
1680 argparser
.add_argument('-H', '--help',
1681 action
='store_true', dest
='help',
1682 help='Display this information')
1683 argparser
.add_argument('-h', '--file-header',
1684 action
='store_true', dest
='show_file_header',
1685 help='Display the ELF file header')
1686 argparser
.add_argument('-l', '--program-headers', '--segments',
1687 action
='store_true', dest
='show_program_header',
1688 help='Display the program headers')
1689 argparser
.add_argument('-S', '--section-headers', '--sections',
1690 action
='store_true', dest
='show_section_header',
1691 help="Display the sections' headers")
1692 argparser
.add_argument('-e', '--headers',
1693 action
='store_true', dest
='show_all_headers',
1694 help='Equivalent to: -h -l -S')
1695 argparser
.add_argument('-s', '--symbols', '--syms',
1696 action
='store_true', dest
='show_symbols',
1697 help='Display the symbol table')
1698 argparser
.add_argument('-n', '--notes',
1699 action
='store_true', dest
='show_notes',
1700 help='Display the core notes (if present)')
1701 argparser
.add_argument('-r', '--relocs',
1702 action
='store_true', dest
='show_relocs',
1703 help='Display the relocations (if present)')
1704 argparser
.add_argument('-au', '--arm-unwind',
1705 action
='store_true', dest
='show_arm_unwind',
1706 help='Display the armeabi unwind information (if present)')
1707 argparser
.add_argument('-x', '--hex-dump',
1708 action
='store', dest
='show_hex_dump', metavar
='<number|name>',
1709 help='Dump the contents of section <number|name> as bytes')
1710 argparser
.add_argument('-p', '--string-dump',
1711 action
='store', dest
='show_string_dump', metavar
='<number|name>',
1712 help='Dump the contents of section <number|name> as strings')
1713 argparser
.add_argument('-V', '--version-info',
1714 action
='store_true', dest
='show_version_info',
1715 help='Display the version sections (if present)')
1716 argparser
.add_argument('-A', '--arch-specific',
1717 action
='store_true', dest
='show_arch_specific',
1718 help='Display the architecture-specific information (if present)')
1719 argparser
.add_argument('--debug-dump',
1720 action
='store', dest
='debug_dump_what', metavar
='<what>',
1722 'Display the contents of DWARF debug sections. <what> can ' +
1723 'one of {info,decodedline,frames,frames-interp,aranges,pubtypes,pubnames,loc,Ranges}'))
1724 argparser
.add_argument('--traceback',
1725 action
='store_true', dest
='show_traceback',
1726 help='Dump the Python traceback on ELFError'
1727 ' exceptions from elftools')
1729 args
= argparser
.parse_args()
1731 if args
.help or not args
.file:
1732 argparser
.print_help()
1735 if args
.show_all_headers
:
1736 do_file_header
= do_section_header
= do_program_header
= True
1738 do_file_header
= args
.show_file_header
1739 do_section_header
= args
.show_section_header
1740 do_program_header
= args
.show_program_header
1742 with
open(args
.file, 'rb') as file:
1744 readelf
= ReadElf(file, stream
or sys
.stdout
)
1746 readelf
.display_file_header()
1747 if do_section_header
:
1748 readelf
.display_section_headers(
1749 show_heading
=not do_file_header
)
1750 if do_program_header
:
1751 readelf
.display_program_headers(
1752 show_heading
=not do_file_header
)
1753 if args
.show_dynamic_tags
:
1754 readelf
.display_dynamic_tags()
1755 if args
.show_symbols
:
1756 readelf
.display_symbol_tables()
1758 readelf
.display_notes()
1759 if args
.show_relocs
:
1760 readelf
.display_relocations()
1761 if args
.show_arm_unwind
:
1762 readelf
.display_arm_unwind()
1763 if args
.show_version_info
:
1764 readelf
.display_version_info()
1765 if args
.show_arch_specific
:
1766 readelf
.display_arch_specific()
1767 if args
.show_hex_dump
:
1768 readelf
.display_hex_dump(args
.show_hex_dump
)
1769 if args
.show_string_dump
:
1770 readelf
.display_string_dump(args
.show_string_dump
)
1771 if args
.debug_dump_what
:
1772 readelf
.display_debug_dump(args
.debug_dump_what
)
1773 except ELFError
as ex
:
1775 sys
.stderr
.write('ELF error: %s\n' % ex
)
1776 if args
.show_traceback
:
1777 traceback
.print_exc()
1782 # Run 'main' redirecting its output to readelfout.txt
1783 # Saves profiling information in readelf.profile
1784 PROFFILE
= 'readelf.profile'
1786 cProfile
.run('main(open("readelfout.txt", "w"))', PROFFILE
)
1788 # Dig in some profiling stats
1790 p
= pstats
.Stats(PROFFILE
)
1791 p
.sort_stats('cumulative').print_stats(25)
1794 #-------------------------------------------------------------------------------
1795 if __name__
== '__main__':