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
.callframe
import CIE
, FDE
, ZERO
67 from elftools
.ehabi
.ehabiinfo
import CorruptEHABIEntry
, CannotUnwindEHABIEntry
, GenericEHABIEntry
68 from elftools
.dwarf
.enums
import ENUM_DW_UT
71 class ReadElf(object):
72 """ display_* methods are used to emit output into the output stream
74 def __init__(self
, file, output
):
76 stream object with the ELF file to read
79 output stream to write to
81 self
.elffile
= ELFFile(file)
84 # Lazily initialized if a debug dump is requested
85 self
._dwarfinfo
= None
87 self
._versioninfo
= None
89 self
._shndx
_sections
= None
91 def display_file_header(self
):
92 """ Display the ELF file header
94 self
._emitline
('ELF Header:')
95 self
._emit
(' Magic: ')
96 self
._emit
(' '.join('%2.2x' % byte2int(b
)
97 for b
in self
.elffile
.e_ident_raw
))
99 header
= self
.elffile
.header
100 e_ident
= header
['e_ident']
101 self
._emitline
(' Class: %s' %
102 describe_ei_class(e_ident
['EI_CLASS']))
103 self
._emitline
(' Data: %s' %
104 describe_ei_data(e_ident
['EI_DATA']))
105 self
._emitline
(' Version: %s' %
106 describe_ei_version(e_ident
['EI_VERSION']))
107 self
._emitline
(' OS/ABI: %s' %
108 describe_ei_osabi(e_ident
['EI_OSABI']))
109 self
._emitline
(' ABI Version: %d' %
110 e_ident
['EI_ABIVERSION'])
111 self
._emitline
(' Type: %s' %
112 describe_e_type(header
['e_type'], self
.elffile
))
113 self
._emitline
(' Machine: %s' %
114 describe_e_machine(header
['e_machine']))
115 self
._emitline
(' Version: %s' %
116 describe_e_version_numeric(header
['e_version']))
117 self
._emitline
(' Entry point address: %s' %
118 self
._format
_hex
(header
['e_entry']))
119 self
._emit
(' Start of program headers: %s' %
121 self
._emitline
(' (bytes into file)')
122 self
._emit
(' Start of section headers: %s' %
124 self
._emitline
(' (bytes into file)')
125 self
._emitline
(' Flags: %s%s' %
126 (self
._format
_hex
(header
['e_flags']),
127 self
.decode_flags(header
['e_flags'])))
128 self
._emitline
(' Size of this header: %s (bytes)' %
130 self
._emitline
(' Size of program headers: %s (bytes)' %
131 header
['e_phentsize'])
132 self
._emitline
(' Number of program headers: %s' %
134 self
._emitline
(' Size of section headers: %s (bytes)' %
135 header
['e_shentsize'])
136 self
._emit
(' Number of section headers: %s' %
138 if header
['e_shnum'] == 0 and self
.elffile
.num_sections() != 0:
139 self
._emitline
(' (%d)' % self
.elffile
.num_sections())
142 self
._emit
(' Section header string table index: %s' %
143 header
['e_shstrndx'])
144 if header
['e_shstrndx'] == SHN_INDICES
.SHN_XINDEX
:
145 self
._emitline
(' (%d)' % self
.elffile
.get_shstrndx())
149 def decode_flags(self
, flags
):
151 if self
.elffile
['e_machine'] == "EM_ARM":
152 eabi
= flags
& E_FLAGS
.EF_ARM_EABIMASK
153 flags
&= ~E_FLAGS
.EF_ARM_EABIMASK
155 if flags
& E_FLAGS
.EF_ARM_RELEXEC
:
156 description
+= ', relocatable executabl'
157 flags
&= ~E_FLAGS
.EF_ARM_RELEXEC
159 if eabi
== E_FLAGS
.EF_ARM_EABI_VER5
:
160 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
161 description
+= ', Version5 EABI'
162 if flags
& E_FLAGS
.EF_ARM_ABI_FLOAT_SOFT
:
163 description
+= ", soft-float ABI"
164 elif flags
& E_FLAGS
.EF_ARM_ABI_FLOAT_HARD
:
165 description
+= ", hard-float ABI"
167 if flags
& E_FLAGS
.EF_ARM_BE8
:
168 description
+= ", BE8"
169 elif flags
& E_FLAGS
.EF_ARM_LE8
:
170 description
+= ", LE8"
172 if flags
& ~EF_ARM_KNOWN_FLAGS
:
173 description
+= ', <unknown>'
175 description
+= ', <unrecognized EABI>'
177 elif self
.elffile
['e_machine'] == 'EM_PPC64':
178 if flags
& E_FLAGS
.EF_PPC64_ABI_V2
:
179 description
+= ', abiv2'
181 elif self
.elffile
['e_machine'] == "EM_MIPS":
182 if flags
& E_FLAGS
.EF_MIPS_NOREORDER
:
183 description
+= ", noreorder"
184 if flags
& E_FLAGS
.EF_MIPS_PIC
:
185 description
+= ", pic"
186 if flags
& E_FLAGS
.EF_MIPS_CPIC
:
187 description
+= ", cpic"
188 if (flags
& E_FLAGS
.EF_MIPS_ABI2
):
189 description
+= ", abi2"
190 if (flags
& E_FLAGS
.EF_MIPS_32BITMODE
):
191 description
+= ", 32bitmode"
192 if (flags
& E_FLAGS_MASKS
.EFM_MIPS_ABI_O32
):
193 description
+= ", o32"
194 elif (flags
& E_FLAGS_MASKS
.EFM_MIPS_ABI_O64
):
195 description
+= ", o64"
196 elif (flags
& E_FLAGS_MASKS
.EFM_MIPS_ABI_EABI32
):
197 description
+= ", eabi32"
198 elif (flags
& E_FLAGS_MASKS
.EFM_MIPS_ABI_EABI64
):
199 description
+= ", eabi64"
200 if (flags
& E_FLAGS
.EF_MIPS_ARCH
) == E_FLAGS
.EF_MIPS_ARCH_1
:
201 description
+= ", mips1"
202 if (flags
& E_FLAGS
.EF_MIPS_ARCH
) == E_FLAGS
.EF_MIPS_ARCH_2
:
203 description
+= ", mips2"
204 if (flags
& E_FLAGS
.EF_MIPS_ARCH
) == E_FLAGS
.EF_MIPS_ARCH_3
:
205 description
+= ", mips3"
206 if (flags
& E_FLAGS
.EF_MIPS_ARCH
) == E_FLAGS
.EF_MIPS_ARCH_4
:
207 description
+= ", mips4"
208 if (flags
& E_FLAGS
.EF_MIPS_ARCH
) == E_FLAGS
.EF_MIPS_ARCH_5
:
209 description
+= ", mips5"
210 if (flags
& E_FLAGS
.EF_MIPS_ARCH
) == E_FLAGS
.EF_MIPS_ARCH_32R2
:
211 description
+= ", mips32r2"
212 if (flags
& E_FLAGS
.EF_MIPS_ARCH
) == E_FLAGS
.EF_MIPS_ARCH_64R2
:
213 description
+= ", mips64r2"
214 if (flags
& E_FLAGS
.EF_MIPS_ARCH
) == E_FLAGS
.EF_MIPS_ARCH_32
:
215 description
+= ", mips32"
216 if (flags
& E_FLAGS
.EF_MIPS_ARCH
) == E_FLAGS
.EF_MIPS_ARCH_64
:
217 description
+= ", mips64"
221 def display_program_headers(self
, show_heading
=True):
222 """ Display the ELF program headers.
223 If show_heading is True, displays the heading for this information
224 (Elf file type is...)
227 if self
.elffile
.num_segments() == 0:
228 self
._emitline
('There are no program headers in this file.')
231 elfheader
= self
.elffile
.header
233 self
._emitline
('Elf file type is %s' %
234 describe_e_type(elfheader
['e_type'], self
.elffile
))
235 self
._emitline
('Entry point is %s' %
236 self
._format
_hex
(elfheader
['e_entry']))
237 # readelf weirness - why isn't e_phoff printed as hex? (for section
239 self
._emitline
('There are %s program headers, starting at offset %s' % (
240 self
.elffile
.num_segments(), elfheader
['e_phoff']))
243 self
._emitline
('Program Headers:')
245 # Now comes the table of program headers with their attributes. Note
246 # that due to different formatting constraints of 32-bit and 64-bit
247 # addresses, there are some conditions on elfclass here.
249 # First comes the table heading
251 if self
.elffile
.elfclass
== 32:
252 self
._emitline
(' Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align')
254 self
._emitline
(' Type Offset VirtAddr PhysAddr')
255 self
._emitline
(' FileSiz MemSiz Flags Align')
259 for segment
in self
.elffile
.iter_segments():
260 self
._emit
(' %-14s ' % describe_p_type(segment
['p_type']))
262 if self
.elffile
.elfclass
== 32:
263 self
._emitline
('%s %s %s %s %s %-3s %s' % (
264 self
._format
_hex
(segment
['p_offset'], fieldsize
=6),
265 self
._format
_hex
(segment
['p_vaddr'], fullhex
=True),
266 self
._format
_hex
(segment
['p_paddr'], fullhex
=True),
267 self
._format
_hex
(segment
['p_filesz'], fieldsize
=5),
268 self
._format
_hex
(segment
['p_memsz'], fieldsize
=5),
269 describe_p_flags(segment
['p_flags']),
270 self
._format
_hex
(segment
['p_align'])))
272 self
._emitline
('%s %s %s' % (
273 self
._format
_hex
(segment
['p_offset'], fullhex
=True),
274 self
._format
_hex
(segment
['p_vaddr'], fullhex
=True),
275 self
._format
_hex
(segment
['p_paddr'], fullhex
=True)))
276 self
._emitline
(' %s %s %-3s %s' % (
277 self
._format
_hex
(segment
['p_filesz'], fullhex
=True),
278 self
._format
_hex
(segment
['p_memsz'], fullhex
=True),
279 describe_p_flags(segment
['p_flags']),
280 # lead0x set to False for p_align, to mimic readelf.
281 # No idea why the difference from 32-bit mode :-|
282 self
._format
_hex
(segment
['p_align'], lead0x
=False)))
284 if isinstance(segment
, InterpSegment
):
285 self
._emitline
(' [Requesting program interpreter: %s]' %
286 segment
.get_interp_name())
288 # Sections to segments mapping
290 if self
.elffile
.num_sections() == 0:
291 # No sections? We're done
294 self
._emitline
('\n Section to Segment mapping:')
295 self
._emitline
(' Segment Sections...')
297 for nseg
, segment
in enumerate(self
.elffile
.iter_segments()):
298 self
._emit
(' %2.2d ' % nseg
)
300 for section
in self
.elffile
.iter_sections():
301 if ( not section
.is_null() and
302 not ((section
['sh_flags'] & SH_FLAGS
.SHF_TLS
) != 0 and
303 section
['sh_type'] == 'SHT_NOBITS' and
304 segment
['p_type'] != 'PT_TLS') and
305 segment
.section_in_segment(section
)):
306 self
._emit
('%s ' % section
.name
)
310 def display_section_headers(self
, show_heading
=True):
311 """ Display the ELF section headers
313 elfheader
= self
.elffile
.header
315 self
._emitline
('There are %s section headers, starting at offset %s' % (
316 elfheader
['e_shnum'], self
._format
_hex
(elfheader
['e_shoff'])))
318 if self
.elffile
.num_sections() == 0:
319 self
._emitline
('There are no sections in this file.')
322 self
._emitline
('\nSection Header%s:' % (
323 's' if self
.elffile
.num_sections() > 1 else ''))
325 # Different formatting constraints of 32-bit and 64-bit addresses
327 if self
.elffile
.elfclass
== 32:
328 self
._emitline
(' [Nr] Name Type Addr Off Size ES Flg Lk Inf Al')
330 self
._emitline
(' [Nr] Name Type Address Offset')
331 self
._emitline
(' Size EntSize Flags Link Info Align')
335 for nsec
, section
in enumerate(self
.elffile
.iter_sections()):
336 self
._emit
(' [%2u] %-17.17s %-15.15s ' % (
337 nsec
, section
.name
, describe_sh_type(section
['sh_type'])))
339 if self
.elffile
.elfclass
== 32:
340 self
._emitline
('%s %s %s %s %3s %2s %3s %2s' % (
341 self
._format
_hex
(section
['sh_addr'], fieldsize
=8, lead0x
=False),
342 self
._format
_hex
(section
['sh_offset'], fieldsize
=6, lead0x
=False),
343 self
._format
_hex
(section
['sh_size'], fieldsize
=6, lead0x
=False),
344 self
._format
_hex
(section
['sh_entsize'], fieldsize
=2, lead0x
=False),
345 describe_sh_flags(section
['sh_flags']),
346 section
['sh_link'], section
['sh_info'],
347 section
['sh_addralign']))
349 self
._emitline
(' %s %s' % (
350 self
._format
_hex
(section
['sh_addr'], fullhex
=True, lead0x
=False),
351 self
._format
_hex
(section
['sh_offset'],
352 fieldsize
=16 if section
['sh_offset'] > 0xffffffff else 8,
354 self
._emitline
(' %s %s %3s %2s %3s %s' % (
355 self
._format
_hex
(section
['sh_size'], fullhex
=True, lead0x
=False),
356 self
._format
_hex
(section
['sh_entsize'], fullhex
=True, lead0x
=False),
357 describe_sh_flags(section
['sh_flags']),
358 section
['sh_link'], section
['sh_info'],
359 section
['sh_addralign']))
361 self
._emitline
('Key to Flags:')
362 self
._emitline
(' W (write), A (alloc), X (execute), M (merge),'
363 ' S (strings), I (info),')
364 self
._emitline
(' L (link order), O (extra OS processing required),'
365 ' G (group), T (TLS),')
366 self
._emitline
(' C (compressed), x (unknown), o (OS specific),'
369 if self
.elffile
['e_machine'] == 'EM_ARM':
370 self
._emit
('y (purecode), ')
371 self
._emitline
('p (processor specific)')
373 def display_symbol_tables(self
):
374 """ Display the symbol tables contained in the file
376 self
._init
_versioninfo
()
378 symbol_tables
= [(idx
, s
) for idx
, s
in enumerate(self
.elffile
.iter_sections())
379 if isinstance(s
, SymbolTableSection
)]
381 if not symbol_tables
and self
.elffile
.num_sections() == 0:
383 self
._emitline
('Dynamic symbol information is not available for'
384 ' displaying symbols.')
386 for section_index
, section
in symbol_tables
:
387 if not isinstance(section
, SymbolTableSection
):
390 if section
['sh_entsize'] == 0:
391 self
._emitline
("\nSymbol table '%s' has a sh_entsize of zero!" % (
395 self
._emitline
("\nSymbol table '%s' contains %d %s:" % (
397 section
.num_symbols(),
398 'entry' if section
.num_symbols() == 1 else 'entries'))
400 if self
.elffile
.elfclass
== 32:
401 self
._emitline
(' Num: Value Size Type Bind Vis Ndx Name')
403 self
._emitline
(' Num: Value Size Type Bind Vis Ndx Name')
405 for nsym
, symbol
in enumerate(section
.iter_symbols()):
407 # readelf doesn't display version info for Solaris versioning
408 if (section
['sh_type'] == 'SHT_DYNSYM' and
409 self
._versioninfo
['type'] == 'GNU'):
410 version
= self
._symbol
_version
(nsym
)
411 if (version
['name'] != symbol
.name
and
412 version
['index'] not in ('VER_NDX_LOCAL',
414 if version
['filename']:
416 version_info
= '@%(name)s (%(index)i)' % version
419 if version
['hidden']:
420 version_info
= '@%(name)s' % version
422 version_info
= '@@%(name)s' % version
424 symbol_name
= symbol
.name
425 # Print section names for STT_SECTION symbols as readelf does
426 if (symbol
['st_info']['type'] == 'STT_SECTION'
427 and symbol
['st_shndx'] < self
.elffile
.num_sections()
428 and symbol
['st_name'] == 0):
429 symbol_name
= self
.elffile
.get_section(symbol
['st_shndx']).name
431 # symbol names are truncated to 25 chars, similarly to readelf
432 self
._emitline
('%6d: %s %s %-7s %-6s %-7s %4s %.25s%s' % (
435 symbol
['st_value'], fullhex
=True, lead0x
=False),
436 "%5d" % symbol
['st_size'] if symbol
['st_size'] < 100000 else hex(symbol
['st_size']),
437 describe_symbol_type(symbol
['st_info']['type']),
438 describe_symbol_bind(symbol
['st_info']['bind']),
439 describe_symbol_other(symbol
['st_other']),
440 describe_symbol_shndx(self
._get
_symbol
_shndx
(symbol
,
446 def display_dynamic_tags(self
):
447 """ Display the dynamic tags contained in the file
449 has_dynamic_sections
= False
450 for section
in self
.elffile
.iter_sections():
451 if not isinstance(section
, DynamicSection
):
454 has_dynamic_sections
= True
455 self
._emitline
("\nDynamic section at offset %s contains %d %s:" % (
456 self
._format
_hex
(section
['sh_offset']),
458 'entry' if section
.num_tags() == 1 else 'entries'))
459 self
._emitline
(" Tag Type Name/Value")
461 padding
= 20 + (8 if self
.elffile
.elfclass
== 32 else 0)
462 for tag
in section
.iter_tags():
463 if tag
.entry
.d_tag
== 'DT_NEEDED':
464 parsed
= 'Shared library: [%s]' % tag
.needed
465 elif tag
.entry
.d_tag
== 'DT_RPATH':
466 parsed
= 'Library rpath: [%s]' % tag
.rpath
467 elif tag
.entry
.d_tag
== 'DT_RUNPATH':
468 parsed
= 'Library runpath: [%s]' % tag
.runpath
469 elif tag
.entry
.d_tag
== 'DT_SONAME':
470 parsed
= 'Library soname: [%s]' % tag
.soname
471 elif tag
.entry
.d_tag
.endswith(('SZ', 'ENT')):
472 parsed
= '%i (bytes)' % tag
['d_val']
473 elif tag
.entry
.d_tag
== 'DT_FLAGS':
474 parsed
= describe_dt_flags(tag
.entry
.d_val
)
475 elif tag
.entry
.d_tag
== 'DT_FLAGS_1':
476 parsed
= 'Flags: %s' % describe_dt_flags_1(tag
.entry
.d_val
)
477 elif tag
.entry
.d_tag
.endswith(('NUM', 'COUNT')):
478 parsed
= '%i' % tag
['d_val']
479 elif tag
.entry
.d_tag
== 'DT_PLTREL':
480 s
= describe_dyn_tag(tag
.entry
.d_val
)
481 if s
.startswith('DT_'):
484 elif tag
.entry
.d_tag
== 'DT_MIPS_FLAGS':
485 parsed
= describe_rh_flags(tag
.entry
.d_val
)
486 elif tag
.entry
.d_tag
in ('DT_MIPS_SYMTABNO',
487 'DT_MIPS_LOCAL_GOTNO'):
488 parsed
= str(tag
.entry
.d_val
)
490 parsed
= '%#x' % tag
['d_val']
492 self
._emitline
(" %s %-*s %s" % (
493 self
._format
_hex
(ENUM_D_TAG
.get(tag
.entry
.d_tag
, tag
.entry
.d_tag
),
494 fullhex
=True, lead0x
=True),
496 '(%s)' % (tag
.entry
.d_tag
[3:],),
498 if not has_dynamic_sections
:
499 self
._emitline
("\nThere is no dynamic section in this file.")
501 def display_notes(self
):
502 """ Display the notes contained in the file
504 for section
in self
.elffile
.iter_sections():
505 if isinstance(section
, NoteSection
):
506 for note
in section
.iter_notes():
507 self
._emitline
("\nDisplaying notes found in: {}".format(
509 self
._emitline
(' Owner Data size Description')
510 self
._emitline
(' %s %s\t%s' % (
511 note
['n_name'].ljust(20),
512 self
._format
_hex
(note
['n_descsz'], fieldsize
=8),
513 describe_note(note
)))
515 def display_relocations(self
):
516 """ Display the relocations contained in the file
518 has_relocation_sections
= False
519 for section
in self
.elffile
.iter_sections():
520 if not isinstance(section
, RelocationSection
):
523 has_relocation_sections
= True
524 self
._emitline
("\nRelocation section '%.128s' at offset %s contains %d %s:" % (
526 self
._format
_hex
(section
['sh_offset']),
527 section
.num_relocations(),
528 'entry' if section
.num_relocations() == 1 else 'entries'))
529 if section
.is_RELA():
530 self
._emitline
(" Offset Info Type Sym. Value Sym. Name + Addend")
532 self
._emitline
(" Offset Info Type Sym.Value Sym. Name")
534 # The symbol table section pointed to in sh_link
535 symtable
= self
.elffile
.get_section(section
['sh_link'])
537 for rel
in section
.iter_relocations():
538 hexwidth
= 8 if self
.elffile
.elfclass
== 32 else 12
539 self
._emit
('%s %s %-17.17s' % (
540 self
._format
_hex
(rel
['r_offset'],
541 fieldsize
=hexwidth
, lead0x
=False),
542 self
._format
_hex
(rel
['r_info'],
543 fieldsize
=hexwidth
, lead0x
=False),
545 rel
['r_info_type'], self
.elffile
)))
547 if rel
['r_info_sym'] == 0:
548 if section
.is_RELA():
549 fieldsize
= 8 if self
.elffile
.elfclass
== 32 else 16
550 addend
= self
._format
_hex
(rel
['r_addend'], lead0x
=False)
551 self
._emit
(' %s %s' % (' ' * fieldsize
, addend
))
555 symbol
= symtable
.get_symbol(rel
['r_info_sym'])
556 # Some symbols have zero 'st_name', so instead what's used
557 # is the name of the section they point at. Truncate symbol
558 # names (excluding version info) to 22 chars, similarly to
560 if symbol
['st_name'] == 0:
561 symsecidx
= self
._get
_symbol
_shndx
(symbol
,
564 symsec
= self
.elffile
.get_section(symsecidx
)
565 symbol_name
= symsec
.name
568 symbol_name
= symbol
.name
569 version
= self
._symbol
_version
(rel
['r_info_sym'])
570 version
= (version
['name']
571 if version
and version
['name'] else '')
572 symbol_name
= '%.22s' % symbol_name
574 symbol_name
+= '@' + version
576 self
._emit
(' %s %s' % (
579 fullhex
=True, lead0x
=False),
581 if section
.is_RELA():
582 self
._emit
(' %s %x' % (
583 '+' if rel
['r_addend'] >= 0 else '-',
584 abs(rel
['r_addend'])))
587 # Emit the two additional relocation types for ELF64 MIPS
589 if (self
.elffile
.elfclass
== 64 and
590 self
.elffile
['e_machine'] == 'EM_MIPS'):
592 rtype
= rel
['r_info_type%s' % i
]
593 self
._emit
(' Type%s: %s' % (
595 describe_reloc_type(rtype
, self
.elffile
)))
598 if not has_relocation_sections
:
599 self
._emitline
('\nThere are no relocations in this file.')
601 def display_arm_unwind(self
):
602 if not self
.elffile
.has_ehabi_info():
603 self
._emitline
('There are no .ARM.idx sections in this file.')
605 for ehabi_info
in self
.elffile
.get_ehabi_infos():
606 # Unwind section '.ARM.exidx' at offset 0x203e8 contains 1009 entries:
607 self
._emitline
("\nUnwind section '%s' at offset 0x%x contains %d %s" % (
608 ehabi_info
.section_name(),
609 ehabi_info
.section_offset(),
610 ehabi_info
.num_entry(),
611 'entry' if ehabi_info
.num_entry() == 1 else 'entries'))
613 for i
in range(ehabi_info
.num_entry()):
614 entry
= ehabi_info
.get_entry(i
)
616 self
._emitline
("Entry %d:" % i
)
617 if isinstance(entry
, CorruptEHABIEntry
):
618 self
._emitline
(" [corrupt] %s" % entry
.reason
)
620 self
._emit
(" Function offset 0x%x: " % entry
.function_offset
)
621 if isinstance(entry
, CannotUnwindEHABIEntry
):
622 self
._emitline
("[cantunwind]")
624 elif entry
.eh_table_offset
:
625 self
._emitline
("@0x%x" % entry
.eh_table_offset
)
627 self
._emitline
("Compact (inline)")
628 if isinstance(entry
, GenericEHABIEntry
):
629 self
._emitline
(" Personality: 0x%x" % entry
.personality
)
631 self
._emitline
(" Compact model index: %d" % entry
.personality
)
632 for mnemonic_item
in entry
.mnmemonic_array():
634 self
._emitline
(mnemonic_item
)
636 def display_version_info(self
):
637 """ Display the version info contained in the file
639 self
._init
_versioninfo
()
641 if not self
._versioninfo
['type']:
642 self
._emitline
("\nNo version information found in this file.")
645 for section
in self
.elffile
.iter_sections():
646 if isinstance(section
, GNUVerSymSection
):
647 self
._print
_version
_section
_header
(section
, 'Version symbols')
648 num_symbols
= section
.num_symbols()
650 # Symbol version info are printed four by four entries
651 for idx_by_4
in range(0, num_symbols
, 4):
653 self
._emit
(' %03x:' % idx_by_4
)
655 for idx
in range(idx_by_4
, min(idx_by_4
+ 4, num_symbols
)):
657 symbol_version
= self
._symbol
_version
(idx
)
658 if symbol_version
['index'] == 'VER_NDX_LOCAL':
660 version_name
= '(*local*)'
661 elif symbol_version
['index'] == 'VER_NDX_GLOBAL':
663 version_name
= '(*global*)'
665 version_index
= symbol_version
['index']
666 version_name
= '(%(name)s)' % symbol_version
668 visibility
= 'h' if symbol_version
['hidden'] else ' '
670 self
._emit
('%4x%s%-13s' % (
671 version_index
, visibility
, version_name
))
675 elif isinstance(section
, GNUVerDefSection
):
676 self
._print
_version
_section
_header
(
677 section
, 'Version definition', indent
=2)
680 for verdef
, verdaux_iter
in section
.iter_versions():
681 verdaux
= next(verdaux_iter
)
684 if verdef
['vd_flags']:
685 flags
= describe_ver_flags(verdef
['vd_flags'])
686 # Mimic exactly the readelf output
691 self
._emitline
(' %s: Rev: %i Flags: %s Index: %i'
692 ' Cnt: %i Name: %s' % (
693 self
._format
_hex
(offset
, fieldsize
=6,
695 verdef
['vd_version'], flags
, verdef
['vd_ndx'],
696 verdef
['vd_cnt'], name
))
699 offset
+ verdef
['vd_aux'] + verdaux
['vda_next'])
700 for idx
, verdaux
in enumerate(verdaux_iter
, start
=1):
701 self
._emitline
(' %s: Parent %i: %s' %
702 (self
._format
_hex
(verdaux_offset
, fieldsize
=4),
704 verdaux_offset
+= verdaux
['vda_next']
706 offset
+= verdef
['vd_next']
708 elif isinstance(section
, GNUVerNeedSection
):
709 self
._print
_version
_section
_header
(section
, 'Version needs')
712 for verneed
, verneed_iter
in section
.iter_versions():
714 self
._emitline
(' %s: Version: %i File: %s Cnt: %i' % (
715 self
._format
_hex
(offset
, fieldsize
=6,
717 verneed
['vn_version'], verneed
.name
,
720 vernaux_offset
= offset
+ verneed
['vn_aux']
721 for idx
, vernaux
in enumerate(verneed_iter
, start
=1):
722 if vernaux
['vna_flags']:
723 flags
= describe_ver_flags(vernaux
['vna_flags'])
724 # Mimic exactly the readelf output
730 ' %s: Name: %s Flags: %s Version: %i' % (
731 self
._format
_hex
(vernaux_offset
, fieldsize
=4),
733 vernaux
['vna_other']))
735 vernaux_offset
+= vernaux
['vna_next']
737 offset
+= verneed
['vn_next']
739 def display_arch_specific(self
):
740 """ Display the architecture-specific info contained in the file.
742 if self
.elffile
['e_machine'] == 'EM_ARM':
743 self
._display
_arch
_specific
_arm
()
745 def display_hex_dump(self
, section_spec
):
746 """ Display a hex dump of a section. section_spec is either a section
749 section
= self
._section
_from
_spec
(section_spec
)
751 # readelf prints the warning to stderr. Even though stderrs are not compared
752 # in tests, we comply with that behavior.
753 sys
.stderr
.write('readelf: Warning: Section \'%s\' was not dumped because it does not exist!\n' % (
756 if section
['sh_type'] == 'SHT_NOBITS':
757 self
._emitline
("\nSection '%s' has no data to dump." % (
761 self
._emitline
("\nHex dump of section '%s':" % section
.name
)
762 self
._note
_relocs
_for
_section
(section
)
763 addr
= section
['sh_addr']
764 data
= section
.data()
767 while dataptr
< len(data
):
768 bytesleft
= len(data
) - dataptr
769 # chunks of 16 bytes per line
770 linebytes
= 16 if bytesleft
> 16 else bytesleft
772 self
._emit
(' %s ' % self
._format
_hex
(addr
, fieldsize
=8))
775 self
._emit
('%2.2x' % byte2int(data
[dataptr
+ i
]))
781 for i
in range(linebytes
):
782 c
= data
[dataptr
+ i
: dataptr
+ i
+ 1]
783 if byte2int(c
[0]) >= 32 and byte2int(c
[0]) < 0x7f:
784 self
._emit
(bytes2str(c
))
786 self
._emit
(bytes2str(b
'.'))
794 def display_string_dump(self
, section_spec
):
795 """ Display a strings dump of a section. section_spec is either a
796 section number or a name.
798 section
= self
._section
_from
_spec
(section_spec
)
800 # readelf prints the warning to stderr. Even though stderrs are not compared
801 # in tests, we comply with that behavior.
802 sys
.stderr
.write('readelf.py: Warning: Section \'%s\' was not dumped because it does not exist!\n' % (
805 if section
['sh_type'] == 'SHT_NOBITS':
806 self
._emitline
("\nSection '%s' has no data to dump." % (
810 self
._emitline
("\nString dump of section '%s':" % section
.name
)
813 data
= section
.data()
816 while dataptr
< len(data
):
817 while ( dataptr
< len(data
) and
818 not (32 <= byte2int(data
[dataptr
]) <= 127)):
821 if dataptr
>= len(data
):
825 while endptr
< len(data
) and byte2int(data
[endptr
]) != 0:
829 self
._emitline
(' [%6x] %s' % (
830 dataptr
, bytes2str(data
[dataptr
:endptr
])))
835 self
._emitline
(' No strings found in this section.')
839 def display_debug_dump(self
, dump_what
):
840 """ Dump a DWARF section
842 self
._init
_dwarfinfo
()
843 if self
._dwarfinfo
is None:
846 set_global_machine_arch(self
.elffile
.get_machine_arch())
848 if dump_what
== 'info':
849 self
._dump
_debug
_info
()
850 elif dump_what
== 'decodedline':
851 self
._dump
_debug
_line
_programs
()
852 elif dump_what
== 'frames':
853 self
._dump
_debug
_frames
()
854 elif dump_what
== 'frames-interp':
855 self
._dump
_debug
_frames
_interp
()
856 elif dump_what
== 'aranges':
857 self
._dump
_debug
_aranges
()
858 elif dump_what
in { 'pubtypes', 'pubnames' }:
859 self
._dump
_debug
_namelut
(dump_what
)
860 elif dump_what
== 'loc':
861 self
._dump
_debug
_locations
()
863 self
._emitline
('debug dump not yet supported for "%s"' % dump_what
)
865 def _format_hex(self
, addr
, fieldsize
=None, fullhex
=False, lead0x
=True,
867 """ Format an address into a hexadecimal string.
870 Size of the hexadecimal field (with leading zeros to fit the
871 address into. For example with fieldsize=8, the format will
873 If None, the minimal required field size will be used.
876 If True, override fieldsize to set it to the maximal size
877 needed for the elfclass
880 If True, leading 0x is added
883 If True, override lead0x to emulate the alternate
884 hexadecimal form specified in format string with the #
885 character: only non-zero values are prefixed with 0x.
886 This form is used by readelf.
895 s
= '0x' if lead0x
else ''
897 fieldsize
= 8 if self
.elffile
.elfclass
== 32 else 16
898 if fieldsize
is None:
901 field
= '%' + '0%sx' % fieldsize
902 return s
+ field
% addr
904 def _print_version_section_header(self
, version_section
, name
, lead0x
=True,
906 """ Print a section header of one version related section (versym,
907 verneed or verdef) with some options to accomodate readelf
908 little differences between each header (e.g. indentation
911 if hasattr(version_section
, 'num_versions'):
912 num_entries
= version_section
.num_versions()
914 num_entries
= version_section
.num_symbols()
916 self
._emitline
("\n%s section '%s' contains %d %s:" % (
917 name
, version_section
.name
, num_entries
,
918 'entry' if num_entries
== 1 else 'entries'))
919 self
._emitline
('%sAddr: %s Offset: %s Link: %i (%s)' % (
922 version_section
['sh_addr'], fieldsize
=16, lead0x
=lead0x
),
924 version_section
['sh_offset'], fieldsize
=6, lead0x
=True),
925 version_section
['sh_link'],
926 self
.elffile
.get_section(version_section
['sh_link']).name
930 def _init_versioninfo(self
):
931 """ Search and initialize informations about version related sections
932 and the kind of versioning used (GNU or Solaris).
934 if self
._versioninfo
is not None:
937 self
._versioninfo
= {'versym': None, 'verdef': None,
938 'verneed': None, 'type': None}
940 for section
in self
.elffile
.iter_sections():
941 if isinstance(section
, GNUVerSymSection
):
942 self
._versioninfo
['versym'] = section
943 elif isinstance(section
, GNUVerDefSection
):
944 self
._versioninfo
['verdef'] = section
945 elif isinstance(section
, GNUVerNeedSection
):
946 self
._versioninfo
['verneed'] = section
947 elif isinstance(section
, DynamicSection
):
948 for tag
in section
.iter_tags():
949 if tag
['d_tag'] == 'DT_VERSYM':
950 self
._versioninfo
['type'] = 'GNU'
953 if not self
._versioninfo
['type'] and (
954 self
._versioninfo
['verneed'] or self
._versioninfo
['verdef']):
955 self
._versioninfo
['type'] = 'Solaris'
957 def _symbol_version(self
, nsym
):
958 """ Return a dict containing information on the
959 or None if no version information is available
961 self
._init
_versioninfo
()
963 symbol_version
= dict.fromkeys(('index', 'name', 'filename', 'hidden'))
965 if (not self
._versioninfo
['versym'] or
966 nsym
>= self
._versioninfo
['versym'].num_symbols()):
969 symbol
= self
._versioninfo
['versym'].get_symbol(nsym
)
970 index
= symbol
.entry
['ndx']
971 if not index
in ('VER_NDX_LOCAL', 'VER_NDX_GLOBAL'):
974 if self
._versioninfo
['type'] == 'GNU':
975 # In GNU versioning mode, the highest bit is used to
976 # store whether the symbol is hidden or not
979 symbol_version
['hidden'] = True
981 if (self
._versioninfo
['verdef'] and
982 index
<= self
._versioninfo
['verdef'].num_versions()):
984 self
._versioninfo
['verdef'].get_version(index
)
985 symbol_version
['name'] = next(verdaux_iter
).name
988 self
._versioninfo
['verneed'].get_version(index
)
989 symbol_version
['name'] = vernaux
.name
990 symbol_version
['filename'] = verneed
.name
992 symbol_version
['index'] = index
993 return symbol_version
995 def _section_from_spec(self
, spec
):
996 """ Retrieve a section given a "spec" (either number or name).
997 Return None if no such section exists in the file.
1001 if num
< self
.elffile
.num_sections():
1002 return self
.elffile
.get_section(num
)
1006 # Not a number. Must be a name then
1007 return self
.elffile
.get_section_by_name(spec
)
1009 def _get_symbol_shndx(self
, symbol
, symbol_index
, symtab_index
):
1010 """ Get the index into the section header table for the "symbol"
1011 at "symbol_index" located in the symbol table with section index
1014 symbol_shndx
= symbol
['st_shndx']
1015 if symbol_shndx
!= SHN_INDICES
.SHN_XINDEX
:
1018 # Check for or lazily construct index section mapping (symbol table
1019 # index -> corresponding symbol table index section object)
1020 if self
._shndx
_sections
is None:
1021 self
._shndx
_sections
= {sec
.symboltable
: sec
for sec
in self
.elffile
.iter_sections()
1022 if isinstance(sec
, SymbolTableIndexSection
)}
1023 return self
._shndx
_sections
[symtab_index
].get_section_index(symbol_index
)
1025 def _note_relocs_for_section(self
, section
):
1026 """ If there are relocation sections pointing to the givne section,
1027 emit a note about it.
1029 for relsec
in self
.elffile
.iter_sections():
1030 if isinstance(relsec
, RelocationSection
):
1031 info_idx
= relsec
['sh_info']
1032 if self
.elffile
.get_section(info_idx
) == section
:
1033 self
._emitline
(' Note: This section has relocations against it, but these have NOT been applied to this dump.')
1036 def _init_dwarfinfo(self
):
1037 """ Initialize the DWARF info contained in the file and assign it to
1039 Leave self._dwarfinfo at None if no DWARF info was found in the file
1041 if self
._dwarfinfo
is not None:
1044 if self
.elffile
.has_dwarf_info():
1045 self
._dwarfinfo
= self
.elffile
.get_dwarf_info()
1047 self
._dwarfinfo
= None
1049 def _dump_debug_info(self
):
1050 """ Dump the debugging info section.
1052 if not self
._dwarfinfo
.has_debug_info
:
1054 self
._emitline
('Contents of the %s section:\n' % self
._dwarfinfo
.debug_info_sec
.name
)
1056 # Offset of the .debug_info section in the stream
1057 section_offset
= self
._dwarfinfo
.debug_info_sec
.global_offset
1059 for cu
in self
._dwarfinfo
.iter_CUs():
1060 self
._emitline
(' Compilation Unit @ offset %s:' %
1061 self
._format
_hex
(cu
.cu_offset
))
1062 self
._emitline
(' Length: %s (%s)' % (
1063 self
._format
_hex
(cu
['unit_length']),
1064 '%s-bit' % cu
.dwarf_format()))
1065 self
._emitline
(' Version: %s' % cu
['version'])
1066 if cu
.header
.get("unit_type", False):
1067 ut
= next((key
for key
, value
in ENUM_DW_UT
.items() if value
== cu
.header
.unit_type
), '?')
1068 self
._emitline
(' Unit Type: %s (%d)' % (ut
, cu
.header
.unit_type
))
1069 self
._emitline
(' Abbrev Offset: %s' % (
1070 self
._format
_hex
(cu
['debug_abbrev_offset']))),
1071 self
._emitline
(' Pointer Size: %s' % cu
['address_size'])
1073 # The nesting depth of each DIE within the tree of DIEs must be
1074 # displayed. To implement this, a counter is incremented each time
1075 # the current DIE has children, and decremented when a null die is
1076 # encountered. Due to the way the DIE tree is serialized, this will
1077 # correctly reflect the nesting depth
1080 current_function
= None
1081 for die
in cu
.iter_DIEs():
1082 if die
.tag
== 'DW_TAG_subprogram':
1083 current_function
= die
1084 self
._emitline
(' <%s><%x>: Abbrev Number: %s%s' % (
1088 (' (%s)' % die
.tag
) if not die
.is_null() else ''))
1093 for attr
in itervalues(die
.attributes
):
1095 # Unknown attribute values are passed-through as integers
1096 if isinstance(name
, int):
1097 name
= 'Unknown AT value: %x' % name
1099 attr_desc
= describe_attr_value(attr
, die
, section_offset
)
1101 if 'DW_OP_fbreg' in attr_desc
and current_function
and not 'DW_AT_frame_base' in current_function
.attributes
:
1102 postfix
= ' [without dw_at_frame_base]'
1106 self
._emitline
(' <%x> %-18s: %s%s' % (
1112 if die
.has_children
:
1117 def _dump_debug_line_programs(self
):
1118 """ Dump the (decoded) line programs from .debug_line
1119 The programs are dumped in the order of the CUs they belong to.
1121 if not self
._dwarfinfo
.has_debug_info
:
1123 self
._emitline
('Contents of the %s section:' % self
._dwarfinfo
.debug_line_sec
.name
)
1126 for cu
in self
._dwarfinfo
.iter_CUs():
1127 lineprogram
= self
._dwarfinfo
.line_program_for_CU(cu
)
1128 ver5
= lineprogram
.header
.version
>= 5
1130 cu_filename
= bytes2str(lineprogram
['file_entry'][0].name
)
1131 if len(lineprogram
['include_directory']) > 0:
1132 dir_index
= lineprogram
['file_entry'][0].dir_index
1134 dir = lineprogram
['include_directory'][dir_index
- 1]
1137 cu_filename
= '%s/%s' % (bytes2str(dir), cu_filename
)
1139 self
._emitline
('CU: %s:' % cu_filename
)
1140 self
._emitline
('File name Line number Starting address Stmt')
1141 # GNU readelf has a View column that we don't try to replicate
1142 # The autotest has logic in place to ignore that
1144 # Print each state's file, line and address information. For some
1145 # instructions other output is needed to be compatible with
1147 for entry
in lineprogram
.get_entries():
1150 # Special handling for commands that don't set a new state
1151 if entry
.command
== DW_LNS_set_file
:
1152 file_entry
= lineprogram
['file_entry'][entry
.args
[0] - 1]
1153 if file_entry
.dir_index
== 0:
1155 self
._emitline
('\n./%s:[++]' % (
1156 bytes2str(file_entry
.name
)))
1158 self
._emitline
('\n%s/%s:' % (
1159 bytes2str(lineprogram
['include_directory'][file_entry
.dir_index
- 1]),
1160 bytes2str(file_entry
.name
)))
1161 elif entry
.command
== DW_LNE_define_file
:
1162 self
._emitline
('%s:' % (
1163 bytes2str(lineprogram
['include_directory'][entry
.args
[0].dir_index
])))
1164 elif lineprogram
['version'] < 4 or self
.elffile
['e_machine'] == 'EM_PPC64':
1165 self
._emitline
('%-35s %11s %18s %s' % (
1166 bytes2str(lineprogram
['file_entry'][state
.file - 1].name
),
1167 state
.line
if not state
.end_sequence
else '-',
1168 '0' if state
.address
== 0 else self
._format
_hex
(state
.address
),
1169 'x' if state
.is_stmt
and not state
.end_sequence
else ''))
1171 # What's the deal with op_index after address on DWARF 5? Is omitting it
1172 # a function of DWARF version, or ISA, or what?
1173 # Used to be unconditional, even on non-VLIW machines.
1174 self
._emitline
('%-35s %s %18s%s %s' % (
1175 bytes2str(lineprogram
['file_entry'][state
.file - 1].name
),
1176 "%11d" % (state
.line
,) if not state
.end_sequence
else '-',
1177 '0' if state
.address
== 0 else self
._format
_hex
(state
.address
),
1178 '' if ver5
else '[%d]' % (state
.op_index
,),
1179 'x' if state
.is_stmt
and not state
.end_sequence
else ''))
1180 if entry
.command
== DW_LNS_copy
:
1181 # Another readelf oddity...
1184 def _dump_frames_info(self
, section
, cfi_entries
):
1185 """ Dump the raw call frame info in a section.
1187 `section` is the Section instance that contains the call frame info
1188 while `cfi_entries` must be an iterable that yields the sequence of
1189 CIE or FDE instances.
1191 self
._emitline
('Contents of the %s section:' % section
.name
)
1193 for entry
in cfi_entries
:
1194 if isinstance(entry
, CIE
):
1195 self
._emitline
('\n%08x %s %s CIE' % (
1197 self
._format
_hex
(entry
['length'], fullhex
=True, lead0x
=False),
1198 self
._format
_hex
(entry
['CIE_id'], fieldsize
=8, lead0x
=False)))
1199 self
._emitline
(' Version: %d' % entry
['version'])
1200 self
._emitline
(' Augmentation: "%s"' % bytes2str(entry
['augmentation']))
1201 self
._emitline
(' Code alignment factor: %u' % entry
['code_alignment_factor'])
1202 self
._emitline
(' Data alignment factor: %d' % entry
['data_alignment_factor'])
1203 self
._emitline
(' Return address column: %d' % entry
['return_address_register'])
1204 if entry
.augmentation_bytes
:
1205 self
._emitline
(' Augmentation data: {}'.format(' '.join(
1206 '{:02x}'.format(ord(b
))
1207 for b
in iterbytes(entry
.augmentation_bytes
)
1211 elif isinstance(entry
, FDE
):
1212 self
._emitline
('\n%08x %s %s FDE cie=%08x pc=%s..%s' % (
1214 self
._format
_hex
(entry
['length'], fullhex
=True, lead0x
=False),
1215 self
._format
_hex
(entry
['CIE_pointer'], fieldsize
=8, lead0x
=False),
1217 self
._format
_hex
(entry
['initial_location'], fullhex
=True, lead0x
=False),
1219 entry
['initial_location'] + entry
['address_range'],
1220 fullhex
=True, lead0x
=False)))
1221 if entry
.augmentation_bytes
:
1222 self
._emitline
(' Augmentation data: {}'.format(' '.join(
1223 '{:02x}'.format(ord(b
))
1224 for b
in iterbytes(entry
.augmentation_bytes
)
1227 else: # ZERO terminator
1228 assert isinstance(entry
, ZERO
)
1229 self
._emitline
('\n%08x ZERO terminator' % entry
.offset
)
1232 self
._emit
(describe_CFI_instructions(entry
))
1235 def _dump_debug_frames(self
):
1236 """ Dump the raw frame info from .debug_frame and .eh_frame sections.
1238 if self
._dwarfinfo
.has_EH_CFI():
1239 self
._dump
_frames
_info
(
1240 self
._dwarfinfo
.eh_frame_sec
,
1241 self
._dwarfinfo
.EH_CFI_entries())
1244 if self
._dwarfinfo
.has_CFI():
1245 self
._dump
_frames
_info
(
1246 self
._dwarfinfo
.debug_frame_sec
,
1247 self
._dwarfinfo
.CFI_entries())
1249 def _dump_debug_namelut(self
, what
):
1251 Dump the debug pubnames section.
1253 if what
== 'pubnames':
1254 namelut
= self
._dwarfinfo
.get_pubnames()
1255 section
= self
._dwarfinfo
.debug_pubnames_sec
1257 namelut
= self
._dwarfinfo
.get_pubtypes()
1258 section
= self
._dwarfinfo
.debug_pubtypes_sec
1260 # readelf prints nothing if the section is not present.
1261 if namelut
is None or len(namelut
) == 0:
1264 self
._emitline
('Contents of the %s section:' % section
.name
)
1267 cu_headers
= namelut
.get_cu_headers()
1269 # go over CU-by-CU first and item-by-item next.
1270 for (cu_hdr
, (cu_ofs
, items
)) in izip(cu_headers
, itertools
.groupby(
1271 namelut
.items(), key
= lambda x
: x
[1].cu_ofs
)):
1273 self
._emitline
(' Length: %d' % cu_hdr
.unit_length
)
1274 self
._emitline
(' Version: %d' % cu_hdr
.version
)
1275 self
._emitline
(' Offset into .debug_info section: 0x%x' % cu_hdr
.debug_info_offset
)
1276 self
._emitline
(' Size of area in .debug_info section: %d' % cu_hdr
.debug_info_length
)
1278 self
._emitline
(' Offset Name')
1280 self
._emitline
(' %x %s' % (item
[1].die_ofs
- cu_ofs
, item
[0]))
1283 def _dump_debug_aranges(self
):
1284 """ Dump the aranges table
1286 aranges_table
= self
._dwarfinfo
.get_aranges()
1287 if aranges_table
== None:
1289 # seems redundent, but we need to get the unsorted set of entries to match system readelf
1290 unordered_entries
= aranges_table
._get
_entries
()
1292 if len(unordered_entries
) == 0:
1294 self
._emitline
("Section '.debug_aranges' has no debugging data.")
1297 self
._emitline
('Contents of the %s section:' % self
._dwarfinfo
.debug_aranges_sec
.name
)
1300 for entry
in unordered_entries
:
1301 if prev_offset
!= entry
.info_offset
:
1302 if entry
!= unordered_entries
[0]:
1303 self
._emitline
(' %s %s' % (
1304 self
._format
_hex
(0, fullhex
=True, lead0x
=False),
1305 self
._format
_hex
(0, fullhex
=True, lead0x
=False)))
1306 self
._emitline
(' Length: %d' % (entry
.unit_length
))
1307 self
._emitline
(' Version: %d' % (entry
.version
))
1308 self
._emitline
(' Offset into .debug_info: 0x%x' % (entry
.info_offset
))
1309 self
._emitline
(' Pointer Size: %d' % (entry
.address_size
))
1310 self
._emitline
(' Segment Size: %d' % (entry
.segment_size
))
1312 self
._emitline
(' Address Length')
1313 self
._emitline
(' %s %s' % (
1314 self
._format
_hex
(entry
.begin_addr
, fullhex
=True, lead0x
=False),
1315 self
._format
_hex
(entry
.length
, fullhex
=True, lead0x
=False)))
1316 prev_offset
= entry
.info_offset
1317 self
._emitline
(' %s %s' % (
1318 self
._format
_hex
(0, fullhex
=True, lead0x
=False),
1319 self
._format
_hex
(0, fullhex
=True, lead0x
=False)))
1321 def _dump_frames_interp_info(self
, section
, cfi_entries
):
1322 """ Dump interpreted (decoded) frame information in a section.
1324 `section` is the Section instance that contains the call frame info
1325 while `cfi_entries` must be an iterable that yields the sequence of
1326 CIE or FDE instances.
1328 self
._emitline
('Contents of the %s section:' % section
.name
)
1330 for entry
in cfi_entries
:
1331 if isinstance(entry
, CIE
):
1332 self
._emitline
('\n%08x %s %s CIE "%s" cf=%d df=%d ra=%d' % (
1334 self
._format
_hex
(entry
['length'], fullhex
=True, lead0x
=False),
1335 self
._format
_hex
(entry
['CIE_id'], fieldsize
=8, lead0x
=False),
1336 bytes2str(entry
['augmentation']),
1337 entry
['code_alignment_factor'],
1338 entry
['data_alignment_factor'],
1339 entry
['return_address_register']))
1340 ra_regnum
= entry
['return_address_register']
1342 elif isinstance(entry
, FDE
):
1343 self
._emitline
('\n%08x %s %s FDE cie=%08x pc=%s..%s' % (
1345 self
._format
_hex
(entry
['length'], fullhex
=True, lead0x
=False),
1346 self
._format
_hex
(entry
['CIE_pointer'], fieldsize
=8, lead0x
=False),
1348 self
._format
_hex
(entry
['initial_location'], fullhex
=True, lead0x
=False),
1349 self
._format
_hex
(entry
['initial_location'] + entry
['address_range'],
1350 fullhex
=True, lead0x
=False)))
1351 ra_regnum
= entry
.cie
['return_address_register']
1353 # If the FDE brings adds no unwinding information compared to
1354 # its CIE, omit its table.
1355 if (len(entry
.get_decoded().table
) ==
1356 len(entry
.cie
.get_decoded().table
)):
1359 else: # ZERO terminator
1360 assert isinstance(entry
, ZERO
)
1361 self
._emitline
('\n%08x ZERO terminator' % entry
.offset
)
1365 decoded_table
= entry
.get_decoded()
1366 if len(decoded_table
.table
) == 0:
1369 # Print the heading row for the decoded table
1371 self
._emit
(' ' if entry
.structs
.address_size
== 4 else ' ')
1374 # Look at the registers the decoded table describes.
1375 # We build reg_order here to match readelf's order. In particular,
1376 # registers are sorted by their number, and the register matching
1377 # ra_regnum is always listed last with a special heading.
1378 decoded_table
= entry
.get_decoded()
1379 reg_order
= sorted(ifilter(
1380 lambda r
: r
!= ra_regnum
,
1381 decoded_table
.reg_order
))
1382 if len(decoded_table
.reg_order
):
1384 # Headings for the registers
1385 for regnum
in reg_order
:
1386 self
._emit
('%-6s' % describe_reg_name(regnum
))
1387 self
._emitline
('ra ')
1389 # Now include ra_regnum in reg_order to print its values
1390 # similarly to the other registers.
1391 reg_order
.append(ra_regnum
)
1395 for line
in decoded_table
.table
:
1396 self
._emit
(self
._format
_hex
(
1397 line
['pc'], fullhex
=True, lead0x
=False))
1399 if line
['cfa'] is not None:
1400 s
= describe_CFI_CFA_rule(line
['cfa'])
1403 self
._emit
(' %-9s' % s
)
1405 for regnum
in reg_order
:
1407 s
= describe_CFI_register_rule(line
[regnum
])
1410 self
._emit
('%-6s' % s
)
1414 def _dump_debug_frames_interp(self
):
1415 """ Dump the interpreted (decoded) frame information from .debug_frame
1416 and .eh_frame sections.
1418 if self
._dwarfinfo
.has_EH_CFI():
1419 self
._dump
_frames
_interp
_info
(
1420 self
._dwarfinfo
.eh_frame_sec
,
1421 self
._dwarfinfo
.EH_CFI_entries())
1424 if self
._dwarfinfo
.has_CFI():
1425 self
._dump
_frames
_interp
_info
(
1426 self
._dwarfinfo
.debug_frame_sec
,
1427 self
._dwarfinfo
.CFI_entries())
1429 def _dump_debug_locations(self
):
1430 """ Dump the location lists from .debug_loc/.debug_loclists section
1432 def _get_cu_base(cu
):
1433 top_die
= cu
.get_top_DIE()
1434 attr
= top_die
.attributes
1435 if 'DW_AT_low_pc' in attr
:
1436 return attr
['DW_AT_low_pc'].value
1437 elif 'DW_AT_entry_pc' in attr
:
1438 return attr
['DW_AT_entry_pc'].value
1440 raise ValueError("Can't find the base IP (low_pc) for a CU")
1442 di
= self
._dwarfinfo
1443 loc_lists
= di
.location_lists()
1444 if not loc_lists
: # No locations section - readelf outputs nothing
1447 loc_lists
= list(loc_lists
.iter_location_lists())
1448 if len(loc_lists
) == 0:
1449 # Present but empty locations section - readelf outputs a message
1450 self
._emitline
("\nSection '%s' has no debugging data." % (di
.debug_loclists_sec
or di
.debug_loc_sec
).name
)
1453 # To dump a location list, one needs to know the CU.
1454 # Scroll through DIEs once, list the known location list offsets.
1455 # Don't need this CU/DIE scan if all entries are absolute or prefixed by base,
1456 # but let's not optimize for that yet.
1457 cu_map
= dict() # Loc list offset => CU
1458 for cu
in di
.iter_CUs():
1459 for die
in cu
.iter_DIEs():
1460 for key
in die
.attributes
:
1461 attr
= die
.attributes
[key
]
1462 if (LocationParser
.attribute_has_location(attr
, cu
['version']) and
1463 LocationParser
._attribute
_has
_loc
_list
(attr
, cu
['version'])):
1464 cu_map
[attr
.value
] = cu
1466 addr_size
= di
.config
.default_address_size
# In bytes, 4 or 8
1467 addr_width
= addr_size
* 2 # In hex digits, 8 or 16
1468 line_template
= " %%08x %%0%dx %%0%dx %%s%%s" % (addr_width
, addr_width
)
1470 self
._emitline
('Contents of the %s section:\n' % (di
.debug_loclists_sec
or di
.debug_loc_sec
).name
)
1471 self
._emitline
(' Offset Begin End Expression')
1472 for loc_list
in loc_lists
:
1478 for entry
in loc_list
:
1479 if isinstance(entry
, LocationViewPair
):
1480 has_views
= in_views
= True
1481 # The "v" before address is conditional in binutils, haven't figured out how
1482 self
._emitline
(" %08x v%015x v%015x location view pair" % (entry
.entry_offset
, entry
.begin
, entry
.end
))
1488 # Need the CU for this loclist, but the map is keyed by the offset
1489 # of the first entry in the loclist. Got to skip the views first.
1491 cu
= cu_map
.get(entry
.entry_offset
, False)
1493 raise ValueError("Location list can't be tracked to a CU")
1495 if isinstance(entry
, LocationEntry
):
1496 if base_ip
is None and not entry
.is_absolute
:
1497 base_ip
= _get_cu_base(cu
)
1499 begin_offset
= (0 if entry
.is_absolute
else base_ip
) + entry
.begin_offset
1500 end_offset
= (0 if entry
.is_absolute
else base_ip
) + entry
.end_offset
1501 expr
= describe_DWARF_expr(entry
.loc_expr
, cu
.structs
, cu
.cu_offset
)
1503 view
= loc_list
[loc_entry_count
]
1504 postfix
= ' (start == end)' if entry
.begin_offset
== entry
.end_offset
and view
.begin
== view
.end
else ''
1505 self
._emitline
(' %08x v%015x v%015x views at %08x for:' %(
1510 self
._emitline
(' %016x %016x %s%s' %(
1515 loc_entry_count
+= 1
1517 postfix
= ' (start == end)' if entry
.begin_offset
== entry
.end_offset
else ''
1518 self
._emitline
(line_template
% (
1524 elif isinstance(entry
, BaseAddressEntry
):
1525 base_ip
= entry
.base_address
1526 self
._emitline
(" %08x %016x (base address)" % (entry
.entry_offset
, entry
.base_address
))
1528 # Pyelftools doesn't store the terminating entry,
1529 # but readelf emits its offset, so this should too.
1531 self
._emitline
(" %08x <End of list>" % (last
.entry_offset
+ last
.entry_length
))
1533 def _display_arch_specific_arm(self
):
1534 """ Display the ARM architecture-specific info contained in the file.
1536 attr_sec
= self
.elffile
.get_section_by_name('.ARM.attributes')
1538 for s
in attr_sec
.iter_subsections():
1539 self
._emitline
("Attribute Section: %s" % s
.header
['vendor_name'])
1540 for ss
in s
.iter_subsubsections():
1541 h_val
= "" if ss
.header
.extra
is None else " ".join("%d" % x
for x
in ss
.header
.extra
)
1542 self
._emitline
(describe_attr_tag_arm(ss
.header
.tag
, h_val
, None))
1544 for attr
in ss
.iter_attributes():
1546 self
._emitline
(describe_attr_tag_arm(attr
.tag
,
1550 def _emit(self
, s
=''):
1551 """ Emit an object to output
1553 self
.output
.write(str(s
))
1555 def _emitline(self
, s
=''):
1556 """ Emit an object to output, followed by a newline
1558 self
.output
.write(str(s
).rstrip() + '\n')
1561 SCRIPT_DESCRIPTION
= 'Display information about the contents of ELF format files'
1562 VERSION_STRING
= '%%(prog)s: based on pyelftools %s' % __version__
1565 def main(stream
=None):
1566 # parse the command-line arguments and invoke ReadElf
1567 argparser
= argparse
.ArgumentParser(
1568 usage
='usage: %(prog)s [options] <elf-file>',
1569 description
=SCRIPT_DESCRIPTION
,
1570 add_help
=False, # -h is a real option of readelf
1572 argparser
.add_argument('file',
1573 nargs
='?', default
=None,
1574 help='ELF file to parse')
1575 argparser
.add_argument('-v', '--version',
1576 action
='version', version
=VERSION_STRING
)
1577 argparser
.add_argument('-d', '--dynamic',
1578 action
='store_true', dest
='show_dynamic_tags',
1579 help='Display the dynamic section')
1580 argparser
.add_argument('-H', '--help',
1581 action
='store_true', dest
='help',
1582 help='Display this information')
1583 argparser
.add_argument('-h', '--file-header',
1584 action
='store_true', dest
='show_file_header',
1585 help='Display the ELF file header')
1586 argparser
.add_argument('-l', '--program-headers', '--segments',
1587 action
='store_true', dest
='show_program_header',
1588 help='Display the program headers')
1589 argparser
.add_argument('-S', '--section-headers', '--sections',
1590 action
='store_true', dest
='show_section_header',
1591 help="Display the sections' headers")
1592 argparser
.add_argument('-e', '--headers',
1593 action
='store_true', dest
='show_all_headers',
1594 help='Equivalent to: -h -l -S')
1595 argparser
.add_argument('-s', '--symbols', '--syms',
1596 action
='store_true', dest
='show_symbols',
1597 help='Display the symbol table')
1598 argparser
.add_argument('-n', '--notes',
1599 action
='store_true', dest
='show_notes',
1600 help='Display the core notes (if present)')
1601 argparser
.add_argument('-r', '--relocs',
1602 action
='store_true', dest
='show_relocs',
1603 help='Display the relocations (if present)')
1604 argparser
.add_argument('-au', '--arm-unwind',
1605 action
='store_true', dest
='show_arm_unwind',
1606 help='Display the armeabi unwind information (if present)')
1607 argparser
.add_argument('-x', '--hex-dump',
1608 action
='store', dest
='show_hex_dump', metavar
='<number|name>',
1609 help='Dump the contents of section <number|name> as bytes')
1610 argparser
.add_argument('-p', '--string-dump',
1611 action
='store', dest
='show_string_dump', metavar
='<number|name>',
1612 help='Dump the contents of section <number|name> as strings')
1613 argparser
.add_argument('-V', '--version-info',
1614 action
='store_true', dest
='show_version_info',
1615 help='Display the version sections (if present)')
1616 argparser
.add_argument('-A', '--arch-specific',
1617 action
='store_true', dest
='show_arch_specific',
1618 help='Display the architecture-specific information (if present)')
1619 argparser
.add_argument('--debug-dump',
1620 action
='store', dest
='debug_dump_what', metavar
='<what>',
1622 'Display the contents of DWARF debug sections. <what> can ' +
1623 'one of {info,decodedline,frames,frames-interp,aranges,pubtypes,pubnames,loc}'))
1624 argparser
.add_argument('--traceback',
1625 action
='store_true', dest
='show_traceback',
1626 help='Dump the Python traceback on ELFError'
1627 ' exceptions from elftools')
1629 args
= argparser
.parse_args()
1631 if args
.help or not args
.file:
1632 argparser
.print_help()
1635 if args
.show_all_headers
:
1636 do_file_header
= do_section_header
= do_program_header
= True
1638 do_file_header
= args
.show_file_header
1639 do_section_header
= args
.show_section_header
1640 do_program_header
= args
.show_program_header
1642 with
open(args
.file, 'rb') as file:
1644 readelf
= ReadElf(file, stream
or sys
.stdout
)
1646 readelf
.display_file_header()
1647 if do_section_header
:
1648 readelf
.display_section_headers(
1649 show_heading
=not do_file_header
)
1650 if do_program_header
:
1651 readelf
.display_program_headers(
1652 show_heading
=not do_file_header
)
1653 if args
.show_dynamic_tags
:
1654 readelf
.display_dynamic_tags()
1655 if args
.show_symbols
:
1656 readelf
.display_symbol_tables()
1658 readelf
.display_notes()
1659 if args
.show_relocs
:
1660 readelf
.display_relocations()
1661 if args
.show_arm_unwind
:
1662 readelf
.display_arm_unwind()
1663 if args
.show_version_info
:
1664 readelf
.display_version_info()
1665 if args
.show_arch_specific
:
1666 readelf
.display_arch_specific()
1667 if args
.show_hex_dump
:
1668 readelf
.display_hex_dump(args
.show_hex_dump
)
1669 if args
.show_string_dump
:
1670 readelf
.display_string_dump(args
.show_string_dump
)
1671 if args
.debug_dump_what
:
1672 readelf
.display_debug_dump(args
.debug_dump_what
)
1673 except ELFError
as ex
:
1675 sys
.stderr
.write('ELF error: %s\n' % ex
)
1676 if args
.show_traceback
:
1677 traceback
.print_exc()
1682 # Run 'main' redirecting its output to readelfout.txt
1683 # Saves profiling information in readelf.profile
1684 PROFFILE
= 'readelf.profile'
1686 cProfile
.run('main(open("readelfout.txt", "w"))', PROFFILE
)
1688 # Dig in some profiling stats
1690 p
= pstats
.Stats(PROFFILE
)
1691 p
.sort_stats('cumulative').print_stats(25)
1694 #-------------------------------------------------------------------------------
1695 if __name__
== '__main__':