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 byte2int
, bytes2str
, str2bytes
)
31 from elftools
.common
.utils
import iterbytes
32 from elftools
.elf
.elffile
import ELFFile
33 from elftools
.elf
.dynamic
import DynamicSection
, DynamicSegment
34 from elftools
.elf
.enums
import ENUM_D_TAG
35 from elftools
.elf
.segments
import InterpSegment
36 from elftools
.elf
.sections
import (
37 NoteSection
, SymbolTableSection
, SymbolTableIndexSection
39 from elftools
.elf
.gnuversions
import (
40 GNUVerSymSection
, GNUVerDefSection
,
43 from elftools
.elf
.relocation
import RelocationSection
44 from elftools
.elf
.descriptions
import (
45 describe_ei_class
, describe_ei_data
, describe_ei_version
,
46 describe_ei_osabi
, describe_e_type
, describe_e_machine
,
47 describe_e_version_numeric
, describe_p_type
, describe_p_flags
,
48 describe_rh_flags
, describe_sh_type
, describe_sh_flags
,
49 describe_symbol_type
, describe_symbol_bind
, describe_symbol_visibility
,
50 describe_symbol_shndx
, describe_reloc_type
, describe_dyn_tag
,
51 describe_dt_flags
, describe_dt_flags_1
, describe_ver_flags
, describe_note
,
52 describe_attr_tag_arm
, describe_symbol_other
54 from elftools
.elf
.constants
import E_FLAGS
55 from elftools
.elf
.constants
import E_FLAGS_MASKS
56 from elftools
.elf
.constants
import SH_FLAGS
57 from elftools
.elf
.constants
import SHN_INDICES
58 from elftools
.dwarf
.dwarfinfo
import DWARFInfo
59 from elftools
.dwarf
.descriptions
import (
60 describe_reg_name
, describe_attr_value
, set_global_machine_arch
,
61 describe_CFI_instructions
, describe_CFI_register_rule
,
62 describe_CFI_CFA_rule
, describe_DWARF_expr
64 from elftools
.dwarf
.constants
import (
65 DW_LNS_copy
, DW_LNS_set_file
, DW_LNE_define_file
)
66 from elftools
.dwarf
.locationlists
import LocationParser
, LocationEntry
, LocationViewPair
, BaseAddressEntry
as LocBaseAddressEntry
, LocationListsPair
67 from elftools
.dwarf
.ranges
import RangeEntry
, BaseAddressEntry
as RangeBaseAddressEntry
, RangeListsPair
68 from elftools
.dwarf
.callframe
import CIE
, FDE
, ZERO
69 from elftools
.ehabi
.ehabiinfo
import CorruptEHABIEntry
, CannotUnwindEHABIEntry
, GenericEHABIEntry
70 from elftools
.dwarf
.enums
import ENUM_DW_UT
73 top_die
= cu
.get_top_DIE()
74 attr
= top_die
.attributes
75 if 'DW_AT_low_pc' in attr
:
76 return attr
['DW_AT_low_pc'].value
77 elif 'DW_AT_entry_pc' in attr
:
78 return attr
['DW_AT_entry_pc'].value
79 elif 'DW_AT_ranges' in attr
:
80 # Rare case but happens: rangelist in the top DIE.
81 # If there is a base or at least one absolute entry,
82 # this will give us the base IP for the CU.
83 rl
= cu
.dwarfinfo
.range_lists().get_range_list_at_offset(attr
['DW_AT_ranges'].value
, cu
)
86 if isinstance(r
, RangeBaseAddressEntry
):
88 elif isinstance(r
, RangeEntry
) and r
.is_absolute
:
92 if ip
is not None and (base_ip
is None or ip
< base_ip
):
95 raise ValueError("Can't find the base IP (low_pc) for a CU")
98 raise ValueError("Can't find the base IP (low_pc) for a CU")
100 class ReadElf(object):
101 """ display_* methods are used to emit output into the output stream
103 def __init__(self
, file, output
):
105 stream object with the ELF file to read
108 output stream to write to
110 self
.elffile
= ELFFile(file)
113 # Lazily initialized if a debug dump is requested
114 self
._dwarfinfo
= None
116 self
._versioninfo
= None
118 self
._shndx
_sections
= None
120 def display_file_header(self
):
121 """ Display the ELF file header
123 self
._emitline
('ELF Header:')
124 self
._emit
(' Magic: ')
125 self
._emit
(' '.join('%2.2x' % byte2int(b
)
126 for b
in self
.elffile
.e_ident_raw
))
128 header
= self
.elffile
.header
129 e_ident
= header
['e_ident']
130 self
._emitline
(' Class: %s' %
131 describe_ei_class(e_ident
['EI_CLASS']))
132 self
._emitline
(' Data: %s' %
133 describe_ei_data(e_ident
['EI_DATA']))
134 self
._emitline
(' Version: %s' %
135 describe_ei_version(e_ident
['EI_VERSION']))
136 self
._emitline
(' OS/ABI: %s' %
137 describe_ei_osabi(e_ident
['EI_OSABI']))
138 self
._emitline
(' ABI Version: %d' %
139 e_ident
['EI_ABIVERSION'])
140 self
._emitline
(' Type: %s' %
141 describe_e_type(header
['e_type'], self
.elffile
))
142 self
._emitline
(' Machine: %s' %
143 describe_e_machine(header
['e_machine']))
144 self
._emitline
(' Version: %s' %
145 describe_e_version_numeric(header
['e_version']))
146 self
._emitline
(' Entry point address: %s' %
147 self
._format
_hex
(header
['e_entry']))
148 self
._emit
(' Start of program headers: %s' %
150 self
._emitline
(' (bytes into file)')
151 self
._emit
(' Start of section headers: %s' %
153 self
._emitline
(' (bytes into file)')
154 self
._emitline
(' Flags: %s%s' %
155 (self
._format
_hex
(header
['e_flags']),
156 self
.decode_flags(header
['e_flags'])))
157 self
._emitline
(' Size of this header: %s (bytes)' %
159 self
._emitline
(' Size of program headers: %s (bytes)' %
160 header
['e_phentsize'])
161 self
._emitline
(' Number of program headers: %s' %
163 self
._emitline
(' Size of section headers: %s (bytes)' %
164 header
['e_shentsize'])
165 self
._emit
(' Number of section headers: %s' %
167 if header
['e_shnum'] == 0 and self
.elffile
.num_sections() != 0:
168 self
._emitline
(' (%d)' % self
.elffile
.num_sections())
171 self
._emit
(' Section header string table index: %s' %
172 header
['e_shstrndx'])
173 if header
['e_shstrndx'] == SHN_INDICES
.SHN_XINDEX
:
174 self
._emitline
(' (%d)' % self
.elffile
.get_shstrndx())
178 def decode_flags(self
, flags
):
180 if self
.elffile
['e_machine'] == "EM_ARM":
181 eabi
= flags
& E_FLAGS
.EF_ARM_EABIMASK
182 flags
&= ~E_FLAGS
.EF_ARM_EABIMASK
184 if flags
& E_FLAGS
.EF_ARM_RELEXEC
:
185 description
+= ', relocatable executabl'
186 flags
&= ~E_FLAGS
.EF_ARM_RELEXEC
188 if eabi
== E_FLAGS
.EF_ARM_EABI_VER5
:
189 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
190 description
+= ', Version5 EABI'
191 if flags
& E_FLAGS
.EF_ARM_ABI_FLOAT_SOFT
:
192 description
+= ", soft-float ABI"
193 elif flags
& E_FLAGS
.EF_ARM_ABI_FLOAT_HARD
:
194 description
+= ", hard-float ABI"
196 if flags
& E_FLAGS
.EF_ARM_BE8
:
197 description
+= ", BE8"
198 elif flags
& E_FLAGS
.EF_ARM_LE8
:
199 description
+= ", LE8"
201 if flags
& ~EF_ARM_KNOWN_FLAGS
:
202 description
+= ', <unknown>'
204 description
+= ', <unrecognized EABI>'
206 elif self
.elffile
['e_machine'] == 'EM_PPC64':
207 if flags
& E_FLAGS
.EF_PPC64_ABI_V2
:
208 description
+= ', abiv2'
210 elif self
.elffile
['e_machine'] == "EM_MIPS":
211 if flags
& E_FLAGS
.EF_MIPS_NOREORDER
:
212 description
+= ", noreorder"
213 if flags
& E_FLAGS
.EF_MIPS_PIC
:
214 description
+= ", pic"
215 if flags
& E_FLAGS
.EF_MIPS_CPIC
:
216 description
+= ", cpic"
217 if (flags
& E_FLAGS
.EF_MIPS_ABI2
):
218 description
+= ", abi2"
219 if (flags
& E_FLAGS
.EF_MIPS_32BITMODE
):
220 description
+= ", 32bitmode"
221 if (flags
& E_FLAGS_MASKS
.EFM_MIPS_ABI_O32
):
222 description
+= ", o32"
223 elif (flags
& E_FLAGS_MASKS
.EFM_MIPS_ABI_O64
):
224 description
+= ", o64"
225 elif (flags
& E_FLAGS_MASKS
.EFM_MIPS_ABI_EABI32
):
226 description
+= ", eabi32"
227 elif (flags
& E_FLAGS_MASKS
.EFM_MIPS_ABI_EABI64
):
228 description
+= ", eabi64"
229 if (flags
& E_FLAGS
.EF_MIPS_ARCH
) == E_FLAGS
.EF_MIPS_ARCH_1
:
230 description
+= ", mips1"
231 if (flags
& E_FLAGS
.EF_MIPS_ARCH
) == E_FLAGS
.EF_MIPS_ARCH_2
:
232 description
+= ", mips2"
233 if (flags
& E_FLAGS
.EF_MIPS_ARCH
) == E_FLAGS
.EF_MIPS_ARCH_3
:
234 description
+= ", mips3"
235 if (flags
& E_FLAGS
.EF_MIPS_ARCH
) == E_FLAGS
.EF_MIPS_ARCH_4
:
236 description
+= ", mips4"
237 if (flags
& E_FLAGS
.EF_MIPS_ARCH
) == E_FLAGS
.EF_MIPS_ARCH_5
:
238 description
+= ", mips5"
239 if (flags
& E_FLAGS
.EF_MIPS_ARCH
) == E_FLAGS
.EF_MIPS_ARCH_32R2
:
240 description
+= ", mips32r2"
241 if (flags
& E_FLAGS
.EF_MIPS_ARCH
) == E_FLAGS
.EF_MIPS_ARCH_64R2
:
242 description
+= ", mips64r2"
243 if (flags
& E_FLAGS
.EF_MIPS_ARCH
) == E_FLAGS
.EF_MIPS_ARCH_32
:
244 description
+= ", mips32"
245 if (flags
& E_FLAGS
.EF_MIPS_ARCH
) == E_FLAGS
.EF_MIPS_ARCH_64
:
246 description
+= ", mips64"
250 def display_program_headers(self
, show_heading
=True):
251 """ Display the ELF program headers.
252 If show_heading is True, displays the heading for this information
253 (Elf file type is...)
256 if self
.elffile
.num_segments() == 0:
257 self
._emitline
('There are no program headers in this file.')
260 elfheader
= self
.elffile
.header
262 self
._emitline
('Elf file type is %s' %
263 describe_e_type(elfheader
['e_type'], self
.elffile
))
264 self
._emitline
('Entry point is %s' %
265 self
._format
_hex
(elfheader
['e_entry']))
266 # readelf weirness - why isn't e_phoff printed as hex? (for section
268 self
._emitline
('There are %s program headers, starting at offset %s' % (
269 self
.elffile
.num_segments(), elfheader
['e_phoff']))
272 self
._emitline
('Program Headers:')
274 # Now comes the table of program headers with their attributes. Note
275 # that due to different formatting constraints of 32-bit and 64-bit
276 # addresses, there are some conditions on elfclass here.
278 # First comes the table heading
280 if self
.elffile
.elfclass
== 32:
281 self
._emitline
(' Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align')
283 self
._emitline
(' Type Offset VirtAddr PhysAddr')
284 self
._emitline
(' FileSiz MemSiz Flags Align')
288 for segment
in self
.elffile
.iter_segments():
289 self
._emit
(' %-14s ' % describe_p_type(segment
['p_type']))
291 if self
.elffile
.elfclass
== 32:
292 self
._emitline
('%s %s %s %s %s %-3s %s' % (
293 self
._format
_hex
(segment
['p_offset'], fieldsize
=6),
294 self
._format
_hex
(segment
['p_vaddr'], fullhex
=True),
295 self
._format
_hex
(segment
['p_paddr'], fullhex
=True),
296 self
._format
_hex
(segment
['p_filesz'], fieldsize
=5),
297 self
._format
_hex
(segment
['p_memsz'], fieldsize
=5),
298 describe_p_flags(segment
['p_flags']),
299 self
._format
_hex
(segment
['p_align'])))
301 self
._emitline
('%s %s %s' % (
302 self
._format
_hex
(segment
['p_offset'], fullhex
=True),
303 self
._format
_hex
(segment
['p_vaddr'], fullhex
=True),
304 self
._format
_hex
(segment
['p_paddr'], fullhex
=True)))
305 self
._emitline
(' %s %s %-3s %s' % (
306 self
._format
_hex
(segment
['p_filesz'], fullhex
=True),
307 self
._format
_hex
(segment
['p_memsz'], fullhex
=True),
308 describe_p_flags(segment
['p_flags']),
309 # lead0x set to False for p_align, to mimic readelf.
310 # No idea why the difference from 32-bit mode :-|
311 self
._format
_hex
(segment
['p_align'], lead0x
=False)))
313 if isinstance(segment
, InterpSegment
):
314 self
._emitline
(' [Requesting program interpreter: %s]' %
315 segment
.get_interp_name())
317 # Sections to segments mapping
319 if self
.elffile
.num_sections() == 0:
320 # No sections? We're done
323 self
._emitline
('\n Section to Segment mapping:')
324 self
._emitline
(' Segment Sections...')
326 for nseg
, segment
in enumerate(self
.elffile
.iter_segments()):
327 self
._emit
(' %2.2d ' % nseg
)
329 for section
in self
.elffile
.iter_sections():
330 if ( not section
.is_null() and
331 not ((section
['sh_flags'] & SH_FLAGS
.SHF_TLS
) != 0 and
332 section
['sh_type'] == 'SHT_NOBITS' and
333 segment
['p_type'] != 'PT_TLS') and
334 segment
.section_in_segment(section
)):
335 self
._emit
('%s ' % section
.name
)
339 def display_section_headers(self
, show_heading
=True):
340 """ Display the ELF section headers
342 elfheader
= self
.elffile
.header
344 self
._emitline
('There are %s section headers, starting at offset %s' % (
345 elfheader
['e_shnum'], self
._format
_hex
(elfheader
['e_shoff'])))
347 if self
.elffile
.num_sections() == 0:
348 self
._emitline
('There are no sections in this file.')
351 self
._emitline
('\nSection Header%s:' % (
352 's' if self
.elffile
.num_sections() > 1 else ''))
354 # Different formatting constraints of 32-bit and 64-bit addresses
356 if self
.elffile
.elfclass
== 32:
357 self
._emitline
(' [Nr] Name Type Addr Off Size ES Flg Lk Inf Al')
359 self
._emitline
(' [Nr] Name Type Address Offset')
360 self
._emitline
(' Size EntSize Flags Link Info Align')
364 for nsec
, section
in enumerate(self
.elffile
.iter_sections()):
365 self
._emit
(' [%2u] %-17.17s %-15.15s ' % (
366 nsec
, section
.name
, describe_sh_type(section
['sh_type'])))
368 if self
.elffile
.elfclass
== 32:
369 self
._emitline
('%s %s %s %s %3s %2s %3s %2s' % (
370 self
._format
_hex
(section
['sh_addr'], fieldsize
=8, lead0x
=False),
371 self
._format
_hex
(section
['sh_offset'], fieldsize
=6, lead0x
=False),
372 self
._format
_hex
(section
['sh_size'], fieldsize
=6, lead0x
=False),
373 self
._format
_hex
(section
['sh_entsize'], fieldsize
=2, lead0x
=False),
374 describe_sh_flags(section
['sh_flags']),
375 section
['sh_link'], section
['sh_info'],
376 section
['sh_addralign']))
378 self
._emitline
(' %s %s' % (
379 self
._format
_hex
(section
['sh_addr'], fullhex
=True, lead0x
=False),
380 self
._format
_hex
(section
['sh_offset'],
381 fieldsize
=16 if section
['sh_offset'] > 0xffffffff else 8,
383 self
._emitline
(' %s %s %3s %2s %3s %s' % (
384 self
._format
_hex
(section
['sh_size'], fullhex
=True, lead0x
=False),
385 self
._format
_hex
(section
['sh_entsize'], fullhex
=True, lead0x
=False),
386 describe_sh_flags(section
['sh_flags']),
387 section
['sh_link'], section
['sh_info'],
388 section
['sh_addralign']))
390 self
._emitline
('Key to Flags:')
391 self
._emitline
(' W (write), A (alloc), X (execute), M (merge),'
392 ' S (strings), I (info),')
393 self
._emitline
(' L (link order), O (extra OS processing required),'
394 ' G (group), T (TLS),')
395 self
._emitline
(' C (compressed), x (unknown), o (OS specific),'
398 if self
.elffile
['e_machine'] == 'EM_ARM':
399 self
._emit
('y (purecode), ')
400 self
._emitline
('p (processor specific)')
402 def display_symbol_tables(self
):
403 """ Display the symbol tables contained in the file
405 self
._init
_versioninfo
()
407 symbol_tables
= [(idx
, s
) for idx
, s
in enumerate(self
.elffile
.iter_sections())
408 if isinstance(s
, SymbolTableSection
)]
410 if not symbol_tables
and self
.elffile
.num_sections() == 0:
412 self
._emitline
('Dynamic symbol information is not available for'
413 ' displaying symbols.')
415 for section_index
, section
in symbol_tables
:
416 if not isinstance(section
, SymbolTableSection
):
419 if section
['sh_entsize'] == 0:
420 self
._emitline
("\nSymbol table '%s' has a sh_entsize of zero!" % (
424 self
._emitline
("\nSymbol table '%s' contains %d %s:" % (
426 section
.num_symbols(),
427 'entry' if section
.num_symbols() == 1 else 'entries'))
429 if self
.elffile
.elfclass
== 32:
430 self
._emitline
(' Num: Value Size Type Bind Vis Ndx Name')
432 self
._emitline
(' Num: Value Size Type Bind Vis Ndx Name')
434 for nsym
, symbol
in enumerate(section
.iter_symbols()):
436 # readelf doesn't display version info for Solaris versioning
437 if (section
['sh_type'] == 'SHT_DYNSYM' and
438 self
._versioninfo
['type'] == 'GNU'):
439 version
= self
._symbol
_version
(nsym
)
440 if (version
['name'] != symbol
.name
and
441 version
['index'] not in ('VER_NDX_LOCAL',
443 if version
['filename']:
445 version_info
= '@%(name)s (%(index)i)' % version
448 if version
['hidden']:
449 version_info
= '@%(name)s' % version
451 version_info
= '@@%(name)s' % version
453 symbol_name
= symbol
.name
454 # Print section names for STT_SECTION symbols as readelf does
455 if (symbol
['st_info']['type'] == 'STT_SECTION'
456 and symbol
['st_shndx'] < self
.elffile
.num_sections()
457 and symbol
['st_name'] == 0):
458 symbol_name
= self
.elffile
.get_section(symbol
['st_shndx']).name
460 # symbol names are truncated to 25 chars, similarly to readelf
461 self
._emitline
('%6d: %s %s %-7s %-6s %-7s %4s %.25s%s' % (
464 symbol
['st_value'], fullhex
=True, lead0x
=False),
465 "%5d" % symbol
['st_size'] if symbol
['st_size'] < 100000 else hex(symbol
['st_size']),
466 describe_symbol_type(symbol
['st_info']['type']),
467 describe_symbol_bind(symbol
['st_info']['bind']),
468 describe_symbol_other(symbol
['st_other']),
469 describe_symbol_shndx(self
._get
_symbol
_shndx
(symbol
,
475 def display_dynamic_tags(self
):
476 """ Display the dynamic tags contained in the file
478 has_dynamic_sections
= False
479 for section
in self
.elffile
.iter_sections():
480 if not isinstance(section
, DynamicSection
):
483 has_dynamic_sections
= True
484 self
._emitline
("\nDynamic section at offset %s contains %d %s:" % (
485 self
._format
_hex
(section
['sh_offset']),
487 'entry' if section
.num_tags() == 1 else 'entries'))
488 self
._emitline
(" Tag Type Name/Value")
490 padding
= 20 + (8 if self
.elffile
.elfclass
== 32 else 0)
491 for tag
in section
.iter_tags():
492 if tag
.entry
.d_tag
== 'DT_NEEDED':
493 parsed
= 'Shared library: [%s]' % tag
.needed
494 elif tag
.entry
.d_tag
== 'DT_RPATH':
495 parsed
= 'Library rpath: [%s]' % tag
.rpath
496 elif tag
.entry
.d_tag
== 'DT_RUNPATH':
497 parsed
= 'Library runpath: [%s]' % tag
.runpath
498 elif tag
.entry
.d_tag
== 'DT_SONAME':
499 parsed
= 'Library soname: [%s]' % tag
.soname
500 elif tag
.entry
.d_tag
.endswith(('SZ', 'ENT')):
501 parsed
= '%i (bytes)' % tag
['d_val']
502 elif tag
.entry
.d_tag
== 'DT_FLAGS':
503 parsed
= describe_dt_flags(tag
.entry
.d_val
)
504 elif tag
.entry
.d_tag
== 'DT_FLAGS_1':
505 parsed
= 'Flags: %s' % describe_dt_flags_1(tag
.entry
.d_val
)
506 elif tag
.entry
.d_tag
.endswith(('NUM', 'COUNT')):
507 parsed
= '%i' % tag
['d_val']
508 elif tag
.entry
.d_tag
== 'DT_PLTREL':
509 s
= describe_dyn_tag(tag
.entry
.d_val
)
510 if s
.startswith('DT_'):
513 elif tag
.entry
.d_tag
== 'DT_MIPS_FLAGS':
514 parsed
= describe_rh_flags(tag
.entry
.d_val
)
515 elif tag
.entry
.d_tag
in ('DT_MIPS_SYMTABNO',
516 'DT_MIPS_LOCAL_GOTNO'):
517 parsed
= str(tag
.entry
.d_val
)
519 parsed
= '%#x' % tag
['d_val']
521 self
._emitline
(" %s %-*s %s" % (
522 self
._format
_hex
(ENUM_D_TAG
.get(tag
.entry
.d_tag
, tag
.entry
.d_tag
),
523 fullhex
=True, lead0x
=True),
525 '(%s)' % (tag
.entry
.d_tag
[3:],),
527 if not has_dynamic_sections
:
528 self
._emitline
("\nThere is no dynamic section in this file.")
530 def display_notes(self
):
531 """ Display the notes contained in the file
533 for section
in self
.elffile
.iter_sections():
534 if isinstance(section
, NoteSection
):
535 for note
in section
.iter_notes():
536 self
._emitline
("\nDisplaying notes found in: {}".format(
538 self
._emitline
(' Owner Data size Description')
539 self
._emitline
(' %s %s\t%s' % (
540 note
['n_name'].ljust(20),
541 self
._format
_hex
(note
['n_descsz'], fieldsize
=8),
542 describe_note(note
)))
544 def display_relocations(self
):
545 """ Display the relocations contained in the file
547 has_relocation_sections
= False
548 for section
in self
.elffile
.iter_sections():
549 if not isinstance(section
, RelocationSection
):
552 has_relocation_sections
= True
553 self
._emitline
("\nRelocation section '%.128s' at offset %s contains %d %s:" % (
555 self
._format
_hex
(section
['sh_offset']),
556 section
.num_relocations(),
557 'entry' if section
.num_relocations() == 1 else 'entries'))
558 if section
.is_RELA():
559 self
._emitline
(" Offset Info Type Sym. Value Sym. Name + Addend")
561 self
._emitline
(" Offset Info Type Sym.Value Sym. Name")
563 # The symbol table section pointed to in sh_link
564 symtable
= self
.elffile
.get_section(section
['sh_link'])
566 for rel
in section
.iter_relocations():
567 hexwidth
= 8 if self
.elffile
.elfclass
== 32 else 12
568 self
._emit
('%s %s %-17.17s' % (
569 self
._format
_hex
(rel
['r_offset'],
570 fieldsize
=hexwidth
, lead0x
=False),
571 self
._format
_hex
(rel
['r_info'],
572 fieldsize
=hexwidth
, lead0x
=False),
574 rel
['r_info_type'], self
.elffile
)))
576 if rel
['r_info_sym'] == 0:
577 if section
.is_RELA():
578 fieldsize
= 8 if self
.elffile
.elfclass
== 32 else 16
579 addend
= self
._format
_hex
(rel
['r_addend'], lead0x
=False)
580 self
._emit
(' %s %s' % (' ' * fieldsize
, addend
))
584 symbol
= symtable
.get_symbol(rel
['r_info_sym'])
585 # Some symbols have zero 'st_name', so instead what's used
586 # is the name of the section they point at. Truncate symbol
587 # names (excluding version info) to 22 chars, similarly to
589 if symbol
['st_name'] == 0:
590 symsecidx
= self
._get
_symbol
_shndx
(symbol
,
593 symsec
= self
.elffile
.get_section(symsecidx
)
594 symbol_name
= symsec
.name
597 symbol_name
= symbol
.name
598 version
= self
._symbol
_version
(rel
['r_info_sym'])
599 version
= (version
['name']
600 if version
and version
['name'] else '')
601 symbol_name
= '%.22s' % symbol_name
603 symbol_name
+= '@' + version
605 self
._emit
(' %s %s' % (
608 fullhex
=True, lead0x
=False),
610 if section
.is_RELA():
611 self
._emit
(' %s %x' % (
612 '+' if rel
['r_addend'] >= 0 else '-',
613 abs(rel
['r_addend'])))
616 # Emit the two additional relocation types for ELF64 MIPS
618 if (self
.elffile
.elfclass
== 64 and
619 self
.elffile
['e_machine'] == 'EM_MIPS'):
621 rtype
= rel
['r_info_type%s' % i
]
622 self
._emit
(' Type%s: %s' % (
624 describe_reloc_type(rtype
, self
.elffile
)))
627 if not has_relocation_sections
:
628 self
._emitline
('\nThere are no relocations in this file.')
630 def display_arm_unwind(self
):
631 if not self
.elffile
.has_ehabi_info():
632 self
._emitline
('There are no .ARM.idx sections in this file.')
634 for ehabi_info
in self
.elffile
.get_ehabi_infos():
635 # Unwind section '.ARM.exidx' at offset 0x203e8 contains 1009 entries:
636 self
._emitline
("\nUnwind section '%s' at offset 0x%x contains %d %s" % (
637 ehabi_info
.section_name(),
638 ehabi_info
.section_offset(),
639 ehabi_info
.num_entry(),
640 'entry' if ehabi_info
.num_entry() == 1 else 'entries'))
642 for i
in range(ehabi_info
.num_entry()):
643 entry
= ehabi_info
.get_entry(i
)
645 self
._emitline
("Entry %d:" % i
)
646 if isinstance(entry
, CorruptEHABIEntry
):
647 self
._emitline
(" [corrupt] %s" % entry
.reason
)
649 self
._emit
(" Function offset 0x%x: " % entry
.function_offset
)
650 if isinstance(entry
, CannotUnwindEHABIEntry
):
651 self
._emitline
("[cantunwind]")
653 elif entry
.eh_table_offset
:
654 self
._emitline
("@0x%x" % entry
.eh_table_offset
)
656 self
._emitline
("Compact (inline)")
657 if isinstance(entry
, GenericEHABIEntry
):
658 self
._emitline
(" Personality: 0x%x" % entry
.personality
)
660 self
._emitline
(" Compact model index: %d" % entry
.personality
)
661 for mnemonic_item
in entry
.mnmemonic_array():
663 self
._emitline
(mnemonic_item
)
665 def display_version_info(self
):
666 """ Display the version info contained in the file
668 self
._init
_versioninfo
()
670 if not self
._versioninfo
['type']:
671 self
._emitline
("\nNo version information found in this file.")
674 for section
in self
.elffile
.iter_sections():
675 if isinstance(section
, GNUVerSymSection
):
676 self
._print
_version
_section
_header
(section
, 'Version symbols')
677 num_symbols
= section
.num_symbols()
679 # Symbol version info are printed four by four entries
680 for idx_by_4
in range(0, num_symbols
, 4):
682 self
._emit
(' %03x:' % idx_by_4
)
684 for idx
in range(idx_by_4
, min(idx_by_4
+ 4, num_symbols
)):
686 symbol_version
= self
._symbol
_version
(idx
)
687 if symbol_version
['index'] == 'VER_NDX_LOCAL':
689 version_name
= '(*local*)'
690 elif symbol_version
['index'] == 'VER_NDX_GLOBAL':
692 version_name
= '(*global*)'
694 version_index
= symbol_version
['index']
695 version_name
= '(%(name)s)' % symbol_version
697 visibility
= 'h' if symbol_version
['hidden'] else ' '
699 self
._emit
('%4x%s%-13s' % (
700 version_index
, visibility
, version_name
))
704 elif isinstance(section
, GNUVerDefSection
):
705 self
._print
_version
_section
_header
(
706 section
, 'Version definition', indent
=2)
709 for verdef
, verdaux_iter
in section
.iter_versions():
710 verdaux
= next(verdaux_iter
)
713 if verdef
['vd_flags']:
714 flags
= describe_ver_flags(verdef
['vd_flags'])
715 # Mimic exactly the readelf output
720 self
._emitline
(' %s: Rev: %i Flags: %s Index: %i'
721 ' Cnt: %i Name: %s' % (
722 self
._format
_hex
(offset
, fieldsize
=6,
724 verdef
['vd_version'], flags
, verdef
['vd_ndx'],
725 verdef
['vd_cnt'], name
))
728 offset
+ verdef
['vd_aux'] + verdaux
['vda_next'])
729 for idx
, verdaux
in enumerate(verdaux_iter
, start
=1):
730 self
._emitline
(' %s: Parent %i: %s' %
731 (self
._format
_hex
(verdaux_offset
, fieldsize
=4),
733 verdaux_offset
+= verdaux
['vda_next']
735 offset
+= verdef
['vd_next']
737 elif isinstance(section
, GNUVerNeedSection
):
738 self
._print
_version
_section
_header
(section
, 'Version needs')
741 for verneed
, verneed_iter
in section
.iter_versions():
743 self
._emitline
(' %s: Version: %i File: %s Cnt: %i' % (
744 self
._format
_hex
(offset
, fieldsize
=6,
746 verneed
['vn_version'], verneed
.name
,
749 vernaux_offset
= offset
+ verneed
['vn_aux']
750 for idx
, vernaux
in enumerate(verneed_iter
, start
=1):
751 if vernaux
['vna_flags']:
752 flags
= describe_ver_flags(vernaux
['vna_flags'])
753 # Mimic exactly the readelf output
759 ' %s: Name: %s Flags: %s Version: %i' % (
760 self
._format
_hex
(vernaux_offset
, fieldsize
=4),
762 vernaux
['vna_other']))
764 vernaux_offset
+= vernaux
['vna_next']
766 offset
+= verneed
['vn_next']
768 def display_arch_specific(self
):
769 """ Display the architecture-specific info contained in the file.
771 if self
.elffile
['e_machine'] == 'EM_ARM':
772 self
._display
_arch
_specific
_arm
()
774 def display_hex_dump(self
, section_spec
):
775 """ Display a hex dump of a section. section_spec is either a section
778 section
= self
._section
_from
_spec
(section_spec
)
780 # readelf prints the warning to stderr. Even though stderrs are not compared
781 # in tests, we comply with that behavior.
782 sys
.stderr
.write('readelf: Warning: Section \'%s\' was not dumped because it does not exist!\n' % (
785 if section
['sh_type'] == 'SHT_NOBITS':
786 self
._emitline
("\nSection '%s' has no data to dump." % (
790 self
._emitline
("\nHex dump of section '%s':" % section
.name
)
791 self
._note
_relocs
_for
_section
(section
)
792 addr
= section
['sh_addr']
793 data
= section
.data()
796 while dataptr
< len(data
):
797 bytesleft
= len(data
) - dataptr
798 # chunks of 16 bytes per line
799 linebytes
= 16 if bytesleft
> 16 else bytesleft
801 self
._emit
(' %s ' % self
._format
_hex
(addr
, fieldsize
=8))
804 self
._emit
('%2.2x' % byte2int(data
[dataptr
+ i
]))
810 for i
in range(linebytes
):
811 c
= data
[dataptr
+ i
: dataptr
+ i
+ 1]
812 if byte2int(c
[0]) >= 32 and byte2int(c
[0]) < 0x7f:
813 self
._emit
(bytes2str(c
))
815 self
._emit
(bytes2str(b
'.'))
823 def display_string_dump(self
, section_spec
):
824 """ Display a strings dump of a section. section_spec is either a
825 section number or a name.
827 section
= self
._section
_from
_spec
(section_spec
)
829 # readelf prints the warning to stderr. Even though stderrs are not compared
830 # in tests, we comply with that behavior.
831 sys
.stderr
.write('readelf.py: Warning: Section \'%s\' was not dumped because it does not exist!\n' % (
834 if section
['sh_type'] == 'SHT_NOBITS':
835 self
._emitline
("\nSection '%s' has no data to dump." % (
839 self
._emitline
("\nString dump of section '%s':" % section
.name
)
842 data
= section
.data()
845 while dataptr
< len(data
):
846 while ( dataptr
< len(data
) and
847 not (32 <= byte2int(data
[dataptr
]) <= 127)):
850 if dataptr
>= len(data
):
854 while endptr
< len(data
) and byte2int(data
[endptr
]) != 0:
858 self
._emitline
(' [%6x] %s' % (
859 dataptr
, bytes2str(data
[dataptr
:endptr
])))
864 self
._emitline
(' No strings found in this section.')
868 def display_debug_dump(self
, dump_what
):
869 """ Dump a DWARF section
871 self
._init
_dwarfinfo
()
872 if self
._dwarfinfo
is None:
875 set_global_machine_arch(self
.elffile
.get_machine_arch())
877 if dump_what
== 'info':
878 self
._dump
_debug
_info
()
879 elif dump_what
== 'decodedline':
880 self
._dump
_debug
_line
_programs
()
881 elif dump_what
== 'frames':
882 self
._dump
_debug
_frames
()
883 elif dump_what
== 'frames-interp':
884 self
._dump
_debug
_frames
_interp
()
885 elif dump_what
== 'aranges':
886 self
._dump
_debug
_aranges
()
887 elif dump_what
in { 'pubtypes', 'pubnames' }:
888 self
._dump
_debug
_namelut
(dump_what
)
889 elif dump_what
== 'loc':
890 self
._dump
_debug
_locations
()
891 elif dump_what
== 'Ranges':
892 self
._dump
_debug
_ranges
()
894 self
._emitline
('debug dump not yet supported for "%s"' % dump_what
)
896 def _format_hex(self
, addr
, fieldsize
=None, fullhex
=False, lead0x
=True,
898 """ Format an address into a hexadecimal string.
901 Size of the hexadecimal field (with leading zeros to fit the
902 address into. For example with fieldsize=8, the format will
904 If None, the minimal required field size will be used.
907 If True, override fieldsize to set it to the maximal size
908 needed for the elfclass
911 If True, leading 0x is added
914 If True, override lead0x to emulate the alternate
915 hexadecimal form specified in format string with the #
916 character: only non-zero values are prefixed with 0x.
917 This form is used by readelf.
926 s
= '0x' if lead0x
else ''
928 fieldsize
= 8 if self
.elffile
.elfclass
== 32 else 16
929 if fieldsize
is None:
932 field
= '%' + '0%sx' % fieldsize
933 return s
+ field
% addr
935 def _print_version_section_header(self
, version_section
, name
, lead0x
=True,
937 """ Print a section header of one version related section (versym,
938 verneed or verdef) with some options to accomodate readelf
939 little differences between each header (e.g. indentation
942 if hasattr(version_section
, 'num_versions'):
943 num_entries
= version_section
.num_versions()
945 num_entries
= version_section
.num_symbols()
947 self
._emitline
("\n%s section '%s' contains %d %s:" % (
948 name
, version_section
.name
, num_entries
,
949 'entry' if num_entries
== 1 else 'entries'))
950 self
._emitline
('%sAddr: %s Offset: %s Link: %i (%s)' % (
953 version_section
['sh_addr'], fieldsize
=16, lead0x
=lead0x
),
955 version_section
['sh_offset'], fieldsize
=6, lead0x
=True),
956 version_section
['sh_link'],
957 self
.elffile
.get_section(version_section
['sh_link']).name
961 def _init_versioninfo(self
):
962 """ Search and initialize informations about version related sections
963 and the kind of versioning used (GNU or Solaris).
965 if self
._versioninfo
is not None:
968 self
._versioninfo
= {'versym': None, 'verdef': None,
969 'verneed': None, 'type': None}
971 for section
in self
.elffile
.iter_sections():
972 if isinstance(section
, GNUVerSymSection
):
973 self
._versioninfo
['versym'] = section
974 elif isinstance(section
, GNUVerDefSection
):
975 self
._versioninfo
['verdef'] = section
976 elif isinstance(section
, GNUVerNeedSection
):
977 self
._versioninfo
['verneed'] = section
978 elif isinstance(section
, DynamicSection
):
979 for tag
in section
.iter_tags():
980 if tag
['d_tag'] == 'DT_VERSYM':
981 self
._versioninfo
['type'] = 'GNU'
984 if not self
._versioninfo
['type'] and (
985 self
._versioninfo
['verneed'] or self
._versioninfo
['verdef']):
986 self
._versioninfo
['type'] = 'Solaris'
988 def _symbol_version(self
, nsym
):
989 """ Return a dict containing information on the
990 or None if no version information is available
992 self
._init
_versioninfo
()
994 symbol_version
= dict.fromkeys(('index', 'name', 'filename', 'hidden'))
996 if (not self
._versioninfo
['versym'] or
997 nsym
>= self
._versioninfo
['versym'].num_symbols()):
1000 symbol
= self
._versioninfo
['versym'].get_symbol(nsym
)
1001 index
= symbol
.entry
['ndx']
1002 if not index
in ('VER_NDX_LOCAL', 'VER_NDX_GLOBAL'):
1005 if self
._versioninfo
['type'] == 'GNU':
1006 # In GNU versioning mode, the highest bit is used to
1007 # store whether the symbol is hidden or not
1010 symbol_version
['hidden'] = True
1012 if (self
._versioninfo
['verdef'] and
1013 index
<= self
._versioninfo
['verdef'].num_versions()):
1015 self
._versioninfo
['verdef'].get_version(index
)
1016 symbol_version
['name'] = next(verdaux_iter
).name
1018 verneed
, vernaux
= \
1019 self
._versioninfo
['verneed'].get_version(index
)
1020 symbol_version
['name'] = vernaux
.name
1021 symbol_version
['filename'] = verneed
.name
1023 symbol_version
['index'] = index
1024 return symbol_version
1026 def _section_from_spec(self
, spec
):
1027 """ Retrieve a section given a "spec" (either number or name).
1028 Return None if no such section exists in the file.
1032 if num
< self
.elffile
.num_sections():
1033 return self
.elffile
.get_section(num
)
1037 # Not a number. Must be a name then
1038 return self
.elffile
.get_section_by_name(spec
)
1040 def _get_symbol_shndx(self
, symbol
, symbol_index
, symtab_index
):
1041 """ Get the index into the section header table for the "symbol"
1042 at "symbol_index" located in the symbol table with section index
1045 symbol_shndx
= symbol
['st_shndx']
1046 if symbol_shndx
!= SHN_INDICES
.SHN_XINDEX
:
1049 # Check for or lazily construct index section mapping (symbol table
1050 # index -> corresponding symbol table index section object)
1051 if self
._shndx
_sections
is None:
1052 self
._shndx
_sections
= {sec
.symboltable
: sec
for sec
in self
.elffile
.iter_sections()
1053 if isinstance(sec
, SymbolTableIndexSection
)}
1054 return self
._shndx
_sections
[symtab_index
].get_section_index(symbol_index
)
1056 def _note_relocs_for_section(self
, section
):
1057 """ If there are relocation sections pointing to the givne section,
1058 emit a note about it.
1060 for relsec
in self
.elffile
.iter_sections():
1061 if isinstance(relsec
, RelocationSection
):
1062 info_idx
= relsec
['sh_info']
1063 if self
.elffile
.get_section(info_idx
) == section
:
1064 self
._emitline
(' Note: This section has relocations against it, but these have NOT been applied to this dump.')
1067 def _init_dwarfinfo(self
):
1068 """ Initialize the DWARF info contained in the file and assign it to
1070 Leave self._dwarfinfo at None if no DWARF info was found in the file
1072 if self
._dwarfinfo
is not None:
1075 if self
.elffile
.has_dwarf_info():
1076 self
._dwarfinfo
= self
.elffile
.get_dwarf_info()
1078 self
._dwarfinfo
= None
1080 def _dump_debug_info(self
):
1081 """ Dump the debugging info section.
1083 if not self
._dwarfinfo
.has_debug_info
:
1085 self
._emitline
('Contents of the %s section:\n' % self
._dwarfinfo
.debug_info_sec
.name
)
1087 # Offset of the .debug_info section in the stream
1088 section_offset
= self
._dwarfinfo
.debug_info_sec
.global_offset
1090 for cu
in self
._dwarfinfo
.iter_CUs():
1091 self
._emitline
(' Compilation Unit @ offset %s:' %
1092 self
._format
_hex
(cu
.cu_offset
))
1093 self
._emitline
(' Length: %s (%s)' % (
1094 self
._format
_hex
(cu
['unit_length']),
1095 '%s-bit' % cu
.dwarf_format()))
1096 self
._emitline
(' Version: %s' % cu
['version'])
1097 if cu
.header
.get("unit_type", False):
1098 ut
= next((key
for key
, value
in ENUM_DW_UT
.items() if value
== cu
.header
.unit_type
), '?')
1099 self
._emitline
(' Unit Type: %s (%d)' % (ut
, cu
.header
.unit_type
))
1100 self
._emitline
(' Abbrev Offset: %s' % (
1101 self
._format
_hex
(cu
['debug_abbrev_offset']))),
1102 self
._emitline
(' Pointer Size: %s' % cu
['address_size'])
1104 # The nesting depth of each DIE within the tree of DIEs must be
1105 # displayed. To implement this, a counter is incremented each time
1106 # the current DIE has children, and decremented when a null die is
1107 # encountered. Due to the way the DIE tree is serialized, this will
1108 # correctly reflect the nesting depth
1111 current_function
= None
1112 for die
in cu
.iter_DIEs():
1113 if die
.tag
== 'DW_TAG_subprogram':
1114 current_function
= die
1115 self
._emitline
(' <%s><%x>: Abbrev Number: %s%s' % (
1119 (' (%s)' % die
.tag
) if not die
.is_null() else ''))
1124 for attr
in die
.attributes
.values():
1126 # Unknown attribute values are passed-through as integers
1127 if isinstance(name
, int):
1128 name
= 'Unknown AT value: %x' % name
1130 attr_desc
= describe_attr_value(attr
, die
, section_offset
)
1132 if 'DW_OP_fbreg' in attr_desc
and current_function
and not 'DW_AT_frame_base' in current_function
.attributes
:
1133 postfix
= ' [without dw_at_frame_base]'
1137 self
._emitline
(' <%x> %-18s: %s%s' % (
1143 if die
.has_children
:
1148 def _dump_debug_line_programs(self
):
1149 """ Dump the (decoded) line programs from .debug_line
1150 The programs are dumped in the order of the CUs they belong to.
1152 if not self
._dwarfinfo
.has_debug_info
:
1154 self
._emitline
('Contents of the %s section:' % self
._dwarfinfo
.debug_line_sec
.name
)
1157 for cu
in self
._dwarfinfo
.iter_CUs():
1158 lineprogram
= self
._dwarfinfo
.line_program_for_CU(cu
)
1159 ver5
= lineprogram
.header
.version
>= 5
1161 cu_filename
= bytes2str(lineprogram
['file_entry'][0].name
)
1162 if len(lineprogram
['include_directory']) > 0:
1163 # GNU readelf 2.38 only outputs directory in wide mode
1164 self
._emitline
('%s:' % cu_filename
)
1166 self
._emitline
('CU: %s:' % cu_filename
)
1168 self
._emitline
('File name Line number Starting address Stmt')
1169 # GNU readelf has a View column that we don't try to replicate
1170 # The autotest has logic in place to ignore that
1172 # Print each state's file, line and address information. For some
1173 # instructions other output is needed to be compatible with
1175 for entry
in lineprogram
.get_entries():
1178 # Special handling for commands that don't set a new state
1179 if entry
.command
== DW_LNS_set_file
:
1180 file_entry
= lineprogram
['file_entry'][entry
.args
[0] - 1]
1181 if file_entry
.dir_index
== 0:
1183 self
._emitline
('\n./%s:[++]' % (
1184 bytes2str(file_entry
.name
)))
1186 self
._emitline
('\n%s/%s:' % (
1187 bytes2str(lineprogram
['include_directory'][file_entry
.dir_index
- 1]),
1188 bytes2str(file_entry
.name
)))
1189 elif entry
.command
== DW_LNE_define_file
:
1190 self
._emitline
('%s:' % (
1191 bytes2str(lineprogram
['include_directory'][entry
.args
[0].dir_index
])))
1192 elif lineprogram
['version'] < 4 or self
.elffile
['e_machine'] == 'EM_PPC64':
1193 self
._emitline
('%-35s %11s %18s %s' % (
1194 bytes2str(lineprogram
['file_entry'][state
.file - 1].name
),
1195 state
.line
if not state
.end_sequence
else '-',
1196 '0' if state
.address
== 0 else self
._format
_hex
(state
.address
),
1197 'x' if state
.is_stmt
and not state
.end_sequence
else ''))
1199 # In readelf, on non-VLIW machines there is no op_index postfix after address.
1200 # It used to be unconditional.
1201 self
._emitline
('%-35s %s %18s%s %s' % (
1202 bytes2str(lineprogram
['file_entry'][state
.file - 1].name
),
1203 "%11d" % (state
.line
,) if not state
.end_sequence
else '-',
1204 '0' if state
.address
== 0 else self
._format
_hex
(state
.address
),
1205 '' if lineprogram
.header
.maximum_operations_per_instruction
== 1 else '[%d]' % (state
.op_index
,),
1206 'x' if state
.is_stmt
and not state
.end_sequence
else ''))
1207 if entry
.command
== DW_LNS_copy
:
1208 # Another readelf oddity...
1211 def _dump_frames_info(self
, section
, cfi_entries
):
1212 """ Dump the raw call frame info in a section.
1214 `section` is the Section instance that contains the call frame info
1215 while `cfi_entries` must be an iterable that yields the sequence of
1216 CIE or FDE instances.
1218 self
._emitline
('Contents of the %s section:' % section
.name
)
1220 for entry
in cfi_entries
:
1221 if isinstance(entry
, CIE
):
1222 self
._emitline
('\n%08x %s %s CIE' % (
1224 self
._format
_hex
(entry
['length'], fullhex
=True, lead0x
=False),
1225 self
._format
_hex
(entry
['CIE_id'], fieldsize
=8, lead0x
=False)))
1226 self
._emitline
(' Version: %d' % entry
['version'])
1227 self
._emitline
(' Augmentation: "%s"' % bytes2str(entry
['augmentation']))
1228 self
._emitline
(' Code alignment factor: %u' % entry
['code_alignment_factor'])
1229 self
._emitline
(' Data alignment factor: %d' % entry
['data_alignment_factor'])
1230 self
._emitline
(' Return address column: %d' % entry
['return_address_register'])
1231 if entry
.augmentation_bytes
:
1232 self
._emitline
(' Augmentation data: {}'.format(' '.join(
1233 '{:02x}'.format(ord(b
))
1234 for b
in iterbytes(entry
.augmentation_bytes
)
1238 elif isinstance(entry
, FDE
):
1239 self
._emitline
('\n%08x %s %s FDE cie=%08x pc=%s..%s' % (
1241 self
._format
_hex
(entry
['length'], fullhex
=True, lead0x
=False),
1242 self
._format
_hex
(entry
['CIE_pointer'], fieldsize
=8, lead0x
=False),
1244 self
._format
_hex
(entry
['initial_location'], fullhex
=True, lead0x
=False),
1246 entry
['initial_location'] + entry
['address_range'],
1247 fullhex
=True, lead0x
=False)))
1248 if entry
.augmentation_bytes
:
1249 self
._emitline
(' Augmentation data: {}'.format(' '.join(
1250 '{:02x}'.format(ord(b
))
1251 for b
in iterbytes(entry
.augmentation_bytes
)
1254 else: # ZERO terminator
1255 assert isinstance(entry
, ZERO
)
1256 self
._emitline
('\n%08x ZERO terminator' % entry
.offset
)
1259 self
._emit
(describe_CFI_instructions(entry
))
1262 def _dump_debug_frames(self
):
1263 """ Dump the raw frame info from .debug_frame and .eh_frame sections.
1265 if self
._dwarfinfo
.has_EH_CFI():
1266 self
._dump
_frames
_info
(
1267 self
._dwarfinfo
.eh_frame_sec
,
1268 self
._dwarfinfo
.EH_CFI_entries())
1271 if self
._dwarfinfo
.has_CFI():
1272 self
._dump
_frames
_info
(
1273 self
._dwarfinfo
.debug_frame_sec
,
1274 self
._dwarfinfo
.CFI_entries())
1276 def _dump_debug_namelut(self
, what
):
1278 Dump the debug pubnames section.
1280 if what
== 'pubnames':
1281 namelut
= self
._dwarfinfo
.get_pubnames()
1282 section
= self
._dwarfinfo
.debug_pubnames_sec
1284 namelut
= self
._dwarfinfo
.get_pubtypes()
1285 section
= self
._dwarfinfo
.debug_pubtypes_sec
1287 # readelf prints nothing if the section is not present.
1288 if namelut
is None or len(namelut
) == 0:
1291 self
._emitline
('Contents of the %s section:' % section
.name
)
1294 cu_headers
= namelut
.get_cu_headers()
1296 # go over CU-by-CU first and item-by-item next.
1297 for (cu_hdr
, (cu_ofs
, items
)) in izip(cu_headers
, itertools
.groupby(
1298 namelut
.items(), key
= lambda x
: x
[1].cu_ofs
)):
1300 self
._emitline
(' Length: %d' % cu_hdr
.unit_length
)
1301 self
._emitline
(' Version: %d' % cu_hdr
.version
)
1302 self
._emitline
(' Offset into .debug_info section: 0x%x' % cu_hdr
.debug_info_offset
)
1303 self
._emitline
(' Size of area in .debug_info section: %d' % cu_hdr
.debug_info_length
)
1305 self
._emitline
(' Offset Name')
1307 self
._emitline
(' %x %s' % (item
[1].die_ofs
- cu_ofs
, item
[0]))
1310 def _dump_debug_aranges(self
):
1311 """ Dump the aranges table
1313 aranges_table
= self
._dwarfinfo
.get_aranges()
1314 if aranges_table
== None:
1316 # Seems redundant, but we need to get the unsorted set of entries
1317 # to match system readelf.
1318 # Also, sometimes there are blank sections in aranges, but readelf
1319 # dumps them, so we should too.
1320 unordered_entries
= aranges_table
._get
_entries
(need_empty
=True)
1322 if len(unordered_entries
) == 0:
1324 self
._emitline
("Section '.debug_aranges' has no debugging data.")
1327 self
._emitline
('Contents of the %s section:' % self
._dwarfinfo
.debug_aranges_sec
.name
)
1330 for entry
in unordered_entries
:
1331 if prev_offset
!= entry
.info_offset
:
1332 if entry
!= unordered_entries
[0]:
1333 self
._emitline
(' %s %s' % (
1334 self
._format
_hex
(0, fullhex
=True, lead0x
=False),
1335 self
._format
_hex
(0, fullhex
=True, lead0x
=False)))
1336 self
._emitline
(' Length: %d' % (entry
.unit_length
))
1337 self
._emitline
(' Version: %d' % (entry
.version
))
1338 self
._emitline
(' Offset into .debug_info: 0x%x' % (entry
.info_offset
))
1339 self
._emitline
(' Pointer Size: %d' % (entry
.address_size
))
1340 self
._emitline
(' Segment Size: %d' % (entry
.segment_size
))
1342 self
._emitline
(' Address Length')
1343 if entry
.begin_addr
!= 0 or entry
.length
!= 0:
1344 self
._emitline
(' %s %s' % (
1345 self
._format
_hex
(entry
.begin_addr
, fullhex
=True, lead0x
=False),
1346 self
._format
_hex
(entry
.length
, fullhex
=True, lead0x
=False)))
1347 prev_offset
= entry
.info_offset
1348 self
._emitline
(' %s %s' % (
1349 self
._format
_hex
(0, fullhex
=True, lead0x
=False),
1350 self
._format
_hex
(0, fullhex
=True, lead0x
=False)))
1352 def _dump_frames_interp_info(self
, section
, cfi_entries
):
1353 """ Dump interpreted (decoded) frame information in a section.
1355 `section` is the Section instance that contains the call frame info
1356 while `cfi_entries` must be an iterable that yields the sequence of
1357 CIE or FDE instances.
1359 self
._emitline
('Contents of the %s section:' % section
.name
)
1361 for entry
in cfi_entries
:
1362 if isinstance(entry
, CIE
):
1363 self
._emitline
('\n%08x %s %s CIE "%s" cf=%d df=%d ra=%d' % (
1365 self
._format
_hex
(entry
['length'], fullhex
=True, lead0x
=False),
1366 self
._format
_hex
(entry
['CIE_id'], fieldsize
=8, lead0x
=False),
1367 bytes2str(entry
['augmentation']),
1368 entry
['code_alignment_factor'],
1369 entry
['data_alignment_factor'],
1370 entry
['return_address_register']))
1371 ra_regnum
= entry
['return_address_register']
1373 elif isinstance(entry
, FDE
):
1374 self
._emitline
('\n%08x %s %s FDE cie=%08x pc=%s..%s' % (
1376 self
._format
_hex
(entry
['length'], fullhex
=True, lead0x
=False),
1377 self
._format
_hex
(entry
['CIE_pointer'], fieldsize
=8, lead0x
=False),
1379 self
._format
_hex
(entry
['initial_location'], fullhex
=True, lead0x
=False),
1380 self
._format
_hex
(entry
['initial_location'] + entry
['address_range'],
1381 fullhex
=True, lead0x
=False)))
1382 ra_regnum
= entry
.cie
['return_address_register']
1384 # If the FDE brings adds no unwinding information compared to
1385 # its CIE, omit its table.
1386 if (len(entry
.get_decoded().table
) ==
1387 len(entry
.cie
.get_decoded().table
)):
1390 else: # ZERO terminator
1391 assert isinstance(entry
, ZERO
)
1392 self
._emitline
('\n%08x ZERO terminator' % entry
.offset
)
1396 decoded_table
= entry
.get_decoded()
1397 if len(decoded_table
.table
) == 0:
1400 # Print the heading row for the decoded table
1402 self
._emit
(' ' if entry
.structs
.address_size
== 4 else ' ')
1405 # Look at the registers the decoded table describes.
1406 # We build reg_order here to match readelf's order. In particular,
1407 # registers are sorted by their number, and the register matching
1408 # ra_regnum is always listed last with a special heading.
1409 decoded_table
= entry
.get_decoded()
1410 reg_order
= sorted(filter(
1411 lambda r
: r
!= ra_regnum
,
1412 decoded_table
.reg_order
))
1413 if len(decoded_table
.reg_order
):
1415 # Headings for the registers
1416 for regnum
in reg_order
:
1417 self
._emit
('%-6s' % describe_reg_name(regnum
))
1418 self
._emitline
('ra ')
1420 # Now include ra_regnum in reg_order to print its values
1421 # similarly to the other registers.
1422 reg_order
.append(ra_regnum
)
1426 for line
in decoded_table
.table
:
1427 self
._emit
(self
._format
_hex
(
1428 line
['pc'], fullhex
=True, lead0x
=False))
1430 if line
['cfa'] is not None:
1431 s
= describe_CFI_CFA_rule(line
['cfa'])
1434 self
._emit
(' %-9s' % s
)
1436 for regnum
in reg_order
:
1438 s
= describe_CFI_register_rule(line
[regnum
])
1441 self
._emit
('%-6s' % s
)
1445 def _dump_debug_frames_interp(self
):
1446 """ Dump the interpreted (decoded) frame information from .debug_frame
1447 and .eh_frame sections.
1449 if self
._dwarfinfo
.has_EH_CFI():
1450 self
._dump
_frames
_interp
_info
(
1451 self
._dwarfinfo
.eh_frame_sec
,
1452 self
._dwarfinfo
.EH_CFI_entries())
1455 if self
._dwarfinfo
.has_CFI():
1456 self
._dump
_frames
_interp
_info
(
1457 self
._dwarfinfo
.debug_frame_sec
,
1458 self
._dwarfinfo
.CFI_entries())
1460 def _dump_debug_locations(self
):
1461 """ Dump the location lists from .debug_loc/.debug_loclists section
1463 di
= self
._dwarfinfo
1464 loc_lists_sec
= di
.location_lists()
1465 if not loc_lists_sec
: # No locations section - readelf outputs nothing
1468 if isinstance(loc_lists_sec
, LocationListsPair
):
1469 self
._dump
_debug
_locsection
(di
, loc_lists_sec
._loc
)
1470 self
._dump
_debug
_locsection
(di
, loc_lists_sec
._loclists
)
1472 self
._dump
_debug
_locsection
(di
, loc_lists_sec
)
1474 def _dump_debug_locsection(self
, di
, loc_lists_sec
):
1475 """ Dump the location lists from .debug_loc/.debug_loclists section
1477 ver5
= loc_lists_sec
.version
>= 5
1478 section_name
= (di
.debug_loclists_sec
if ver5
else di
.debug_loc_sec
).name
1480 # To dump a location list, one needs to know the CU.
1481 # Scroll through DIEs once, list the known location list offsets.
1482 # Don't need this CU/DIE scan if all entries are absolute or prefixed by base,
1483 # but let's not optimize for that yet.
1484 cu_map
= dict() # Loc list offset => CU
1485 for cu
in di
.iter_CUs():
1486 for die
in cu
.iter_DIEs():
1487 for key
in die
.attributes
:
1488 attr
= die
.attributes
[key
]
1489 if (LocationParser
.attribute_has_location(attr
, cu
['version']) and
1490 LocationParser
._attribute
_has
_loc
_list
(attr
, cu
['version'])):
1491 cu_map
[attr
.value
] = cu
1493 addr_size
= di
.config
.default_address_size
# In bytes, 4 or 8
1494 addr_width
= addr_size
* 2 # In hex digits, 8 or 16
1495 line_template
= " %%08x %%0%dx %%0%dx %%s%%s" % (addr_width
, addr_width
)
1497 loc_lists
= list(loc_lists_sec
.iter_location_lists())
1498 if len(loc_lists
) == 0:
1499 # Present but empty locations section - readelf outputs a message
1500 self
._emitline
("\nSection '%s' has no debugging data." % (section_name
,))
1503 self
._emitline
('Contents of the %s section:\n' % (section_name
,))
1504 self
._emitline
(' Offset Begin End Expression')
1505 for loc_list
in loc_lists
:
1506 self
._dump
_loclist
(loc_list
, line_template
, cu_map
)
1508 def _dump_loclist(self
, loc_list
, line_template
, cu_map
):
1514 for entry
in loc_list
:
1515 if isinstance(entry
, LocationViewPair
):
1516 has_views
= in_views
= True
1517 # The "v" before address is conditional in binutils, haven't figured out how
1518 self
._emitline
(" %08x v%015x v%015x location view pair" % (entry
.entry_offset
, entry
.begin
, entry
.end
))
1524 # Readelf quirk: indexed loclists don't show the real base IP
1528 cu
= cu_map
.get(entry
.entry_offset
, False)
1530 raise ValueError("Location list can't be tracked to a CU")
1532 if isinstance(entry
, LocationEntry
):
1533 if base_ip
is None and not entry
.is_absolute
:
1534 base_ip
= _get_cu_base(cu
)
1536 begin_offset
= (0 if entry
.is_absolute
else base_ip
) + entry
.begin_offset
1537 end_offset
= (0 if entry
.is_absolute
else base_ip
) + entry
.end_offset
1538 expr
= describe_DWARF_expr(entry
.loc_expr
, cu
.structs
, cu
.cu_offset
)
1540 view
= loc_list
[loc_entry_count
]
1541 postfix
= ' (start == end)' if entry
.begin_offset
== entry
.end_offset
and view
.begin
== view
.end
else ''
1542 self
._emitline
(' %08x v%015x v%015x views at %08x for:' %(
1547 self
._emitline
(' %016x %016x %s%s' %(
1552 loc_entry_count
+= 1
1554 postfix
= ' (start == end)' if entry
.begin_offset
== entry
.end_offset
else ''
1555 self
._emitline
(line_template
% (
1561 elif isinstance(entry
, LocBaseAddressEntry
):
1562 base_ip
= entry
.base_address
1563 self
._emitline
(" %08x %016x (base address)" % (entry
.entry_offset
, entry
.base_address
))
1565 # Pyelftools doesn't store the terminating entry,
1566 # but readelf emits its offset, so this should too.
1568 self
._emitline
(" %08x <End of list>" % (last
.entry_offset
+ last
.entry_length
))
1570 def _dump_debug_ranges(self
):
1571 # TODO: GNU readelf format doesn't need entry_length?
1572 di
= self
._dwarfinfo
1573 range_lists_sec
= di
.range_lists()
1574 if not range_lists_sec
: # No ranges section - readelf outputs nothing
1577 if isinstance(range_lists_sec
, RangeListsPair
):
1578 self
._dump
_debug
_rangesection
(di
, range_lists_sec
._ranges
)
1579 self
._dump
_debug
_rangesection
(di
, range_lists_sec
._rnglists
)
1581 self
._dump
_debug
_rangesection
(di
, range_lists_sec
)
1583 def _dump_debug_rangesection(self
, di
, range_lists_sec
):
1584 # In the master branch of binutils, the v5 dump format is way different by now.
1586 ver5
= range_lists_sec
.version
>= 5
1587 section_name
= (di
.debug_rnglists_sec
if ver5
else di
.debug_ranges_sec
).name
1588 addr_size
= di
.config
.default_address_size
# In bytes, 4 or 8
1589 addr_width
= addr_size
* 2 # In hex digits, 8 or 16
1590 line_template
= " %%08x %%0%dx %%0%dx %%s" % (addr_width
, addr_width
)
1591 base_template
= " %%08x %%0%dx (base address)" % (addr_width
)
1593 range_lists
= list(range_lists_sec
.iter_range_lists())
1594 if len(range_lists
) == 0:
1595 # Present but empty locations section - readelf outputs a message
1596 self
._emitline
("\nSection '%s' has no debugging data." % section_name
)
1599 # In order to determine the base address of the range
1600 # We need to know the corresponding CU.
1601 cu_map
= {die
.attributes
['DW_AT_ranges'].value
: cu
# Range list offset => CU
1602 for cu
in di
.iter_CUs()
1603 for die
in cu
.iter_DIEs()
1604 if 'DW_AT_ranges' in die
.attributes
}
1606 self
._emitline
('Contents of the %s section:\n' % section_name
)
1607 self
._emitline
(' Offset Begin End')
1609 for range_list
in range_lists
:
1610 self
._dump
_rangelist
(range_list
, cu_map
, ver5
, line_template
, base_template
)
1612 def _dump_rangelist(self
, range_list
, cu_map
, ver5
, line_template
, base_template
):
1613 # Weird discrepancy in binutils: for DWARFv5 it outputs entry offset,
1614 # for DWARF<=4 list offset.
1615 first
= range_list
[0]
1616 base_ip
= _get_cu_base(cu_map
[first
.entry_offset
])
1617 for entry
in range_list
:
1618 if isinstance(entry
, RangeEntry
):
1619 postfix
= ' (start == end)' if entry
.begin_offset
== entry
.end_offset
else ''
1620 self
._emitline
(line_template
% (
1621 entry
.entry_offset
if ver5
else first
.entry_offset
,
1622 (0 if entry
.is_absolute
else base_ip
) + entry
.begin_offset
,
1623 (0 if entry
.is_absolute
else base_ip
) + entry
.end_offset
,
1625 elif isinstance(entry
,RangeBaseAddressEntry
):
1626 base_ip
= entry
.base_address
1627 self
._emitline
(base_template
% (
1628 entry
.entry_offset
if ver5
else first
.entry_offset
,
1629 entry
.base_address
))
1631 raise NotImplementedError("Unknown object in a range list")
1632 last
= range_list
[-1]
1633 self
._emitline
(' %08x <End of list>' % (last
.entry_offset
+ last
.entry_length
if ver5
else first
.entry_offset
))
1635 def _display_arch_specific_arm(self
):
1636 """ Display the ARM architecture-specific info contained in the file.
1638 attr_sec
= self
.elffile
.get_section_by_name('.ARM.attributes')
1640 for s
in attr_sec
.iter_subsections():
1641 self
._emitline
("Attribute Section: %s" % s
.header
['vendor_name'])
1642 for ss
in s
.iter_subsubsections():
1643 h_val
= "" if ss
.header
.extra
is None else " ".join("%d" % x
for x
in ss
.header
.extra
)
1644 self
._emitline
(describe_attr_tag_arm(ss
.header
.tag
, h_val
, None))
1646 for attr
in ss
.iter_attributes():
1648 self
._emitline
(describe_attr_tag_arm(attr
.tag
,
1652 def _emit(self
, s
=''):
1653 """ Emit an object to output
1655 self
.output
.write(str(s
))
1657 def _emitline(self
, s
=''):
1658 """ Emit an object to output, followed by a newline
1660 self
.output
.write(str(s
).rstrip() + '\n')
1663 SCRIPT_DESCRIPTION
= 'Display information about the contents of ELF format files'
1664 VERSION_STRING
= '%%(prog)s: based on pyelftools %s' % __version__
1667 def main(stream
=None):
1668 # parse the command-line arguments and invoke ReadElf
1669 argparser
= argparse
.ArgumentParser(
1670 usage
='usage: %(prog)s [options] <elf-file>',
1671 description
=SCRIPT_DESCRIPTION
,
1672 add_help
=False, # -h is a real option of readelf
1674 argparser
.add_argument('file',
1675 nargs
='?', default
=None,
1676 help='ELF file to parse')
1677 argparser
.add_argument('-v', '--version',
1678 action
='version', version
=VERSION_STRING
)
1679 argparser
.add_argument('-d', '--dynamic',
1680 action
='store_true', dest
='show_dynamic_tags',
1681 help='Display the dynamic section')
1682 argparser
.add_argument('-H', '--help',
1683 action
='store_true', dest
='help',
1684 help='Display this information')
1685 argparser
.add_argument('-h', '--file-header',
1686 action
='store_true', dest
='show_file_header',
1687 help='Display the ELF file header')
1688 argparser
.add_argument('-l', '--program-headers', '--segments',
1689 action
='store_true', dest
='show_program_header',
1690 help='Display the program headers')
1691 argparser
.add_argument('-S', '--section-headers', '--sections',
1692 action
='store_true', dest
='show_section_header',
1693 help="Display the sections' headers")
1694 argparser
.add_argument('-e', '--headers',
1695 action
='store_true', dest
='show_all_headers',
1696 help='Equivalent to: -h -l -S')
1697 argparser
.add_argument('-s', '--symbols', '--syms',
1698 action
='store_true', dest
='show_symbols',
1699 help='Display the symbol table')
1700 argparser
.add_argument('-n', '--notes',
1701 action
='store_true', dest
='show_notes',
1702 help='Display the core notes (if present)')
1703 argparser
.add_argument('-r', '--relocs',
1704 action
='store_true', dest
='show_relocs',
1705 help='Display the relocations (if present)')
1706 argparser
.add_argument('-au', '--arm-unwind',
1707 action
='store_true', dest
='show_arm_unwind',
1708 help='Display the armeabi unwind information (if present)')
1709 argparser
.add_argument('-x', '--hex-dump',
1710 action
='store', dest
='show_hex_dump', metavar
='<number|name>',
1711 help='Dump the contents of section <number|name> as bytes')
1712 argparser
.add_argument('-p', '--string-dump',
1713 action
='store', dest
='show_string_dump', metavar
='<number|name>',
1714 help='Dump the contents of section <number|name> as strings')
1715 argparser
.add_argument('-V', '--version-info',
1716 action
='store_true', dest
='show_version_info',
1717 help='Display the version sections (if present)')
1718 argparser
.add_argument('-A', '--arch-specific',
1719 action
='store_true', dest
='show_arch_specific',
1720 help='Display the architecture-specific information (if present)')
1721 argparser
.add_argument('--debug-dump',
1722 action
='store', dest
='debug_dump_what', metavar
='<what>',
1724 'Display the contents of DWARF debug sections. <what> can ' +
1725 'one of {info,decodedline,frames,frames-interp,aranges,pubtypes,pubnames,loc,Ranges}'))
1726 argparser
.add_argument('--traceback',
1727 action
='store_true', dest
='show_traceback',
1728 help='Dump the Python traceback on ELFError'
1729 ' exceptions from elftools')
1731 args
= argparser
.parse_args()
1733 if args
.help or not args
.file:
1734 argparser
.print_help()
1737 if args
.show_all_headers
:
1738 do_file_header
= do_section_header
= do_program_header
= True
1740 do_file_header
= args
.show_file_header
1741 do_section_header
= args
.show_section_header
1742 do_program_header
= args
.show_program_header
1744 with
open(args
.file, 'rb') as file:
1746 readelf
= ReadElf(file, stream
or sys
.stdout
)
1748 readelf
.display_file_header()
1749 if do_section_header
:
1750 readelf
.display_section_headers(
1751 show_heading
=not do_file_header
)
1752 if do_program_header
:
1753 readelf
.display_program_headers(
1754 show_heading
=not do_file_header
)
1755 if args
.show_dynamic_tags
:
1756 readelf
.display_dynamic_tags()
1757 if args
.show_symbols
:
1758 readelf
.display_symbol_tables()
1760 readelf
.display_notes()
1761 if args
.show_relocs
:
1762 readelf
.display_relocations()
1763 if args
.show_arm_unwind
:
1764 readelf
.display_arm_unwind()
1765 if args
.show_version_info
:
1766 readelf
.display_version_info()
1767 if args
.show_arch_specific
:
1768 readelf
.display_arch_specific()
1769 if args
.show_hex_dump
:
1770 readelf
.display_hex_dump(args
.show_hex_dump
)
1771 if args
.show_string_dump
:
1772 readelf
.display_string_dump(args
.show_string_dump
)
1773 if args
.debug_dump_what
:
1774 readelf
.display_debug_dump(args
.debug_dump_what
)
1775 except ELFError
as ex
:
1777 sys
.stderr
.write('ELF error: %s\n' % ex
)
1778 if args
.show_traceback
:
1779 traceback
.print_exc()
1784 # Run 'main' redirecting its output to readelfout.txt
1785 # Saves profiling information in readelf.profile
1786 PROFFILE
= 'readelf.profile'
1788 cProfile
.run('main(open("readelfout.txt", "w"))', PROFFILE
)
1790 # Dig in some profiling stats
1792 p
= pstats
.Stats(PROFFILE
)
1793 p
.sort_stats('cumulative').print_stats(25)
1796 #-------------------------------------------------------------------------------
1797 if __name__
== '__main__':