Support for DWARFv5 debug_rnglists section (#419)
[pyelftools.git] / scripts / readelf.py
1 #!/usr/bin/env python
2 #-------------------------------------------------------------------------------
3 # scripts/readelf.py
4 #
5 # A clone of 'readelf' in Python, based on the pyelftools library
6 #
7 # Eli Bendersky (eliben@gmail.com)
8 # This code is in the public domain
9 #-------------------------------------------------------------------------------
10 import argparse
11 import os, sys
12 import string
13 import traceback
14 import itertools
15 # Note: zip has different behaviour between Python 2.x and 3.x.
16 # - Using izip ensures compatibility.
17 try:
18 from itertools import izip
19 except:
20 izip = zip
21
22 # For running from development directory. It should take precedence over the
23 # installed pyelftools.
24 sys.path.insert(0, '.')
25
26
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
37 )
38 from elftools.elf.gnuversions import (
39 GNUVerSymSection, GNUVerDefSection,
40 GNUVerNeedSection,
41 )
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
52 )
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
62 )
63 from elftools.dwarf.constants import (
64 DW_LNS_copy, DW_LNS_set_file, DW_LNE_define_file)
65 from elftools.dwarf.locationlists import LocationParser, LocationEntry, LocationViewPair, BaseAddressEntry
66 from elftools.dwarf.ranges import RangeEntry # ranges.BaseAddressEntry collides with the one above
67 import elftools.dwarf.ranges
68 from elftools.dwarf.callframe import CIE, FDE, ZERO
69 from elftools.ehabi.ehabiinfo import CorruptEHABIEntry, CannotUnwindEHABIEntry, GenericEHABIEntry
70 from elftools.dwarf.enums import ENUM_DW_UT
71
72 def _get_cu_base(cu):
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 else:
80 raise ValueError("Can't find the base IP (low_pc) for a CU")
81
82 class ReadElf(object):
83 """ display_* methods are used to emit output into the output stream
84 """
85 def __init__(self, file, output):
86 """ file:
87 stream object with the ELF file to read
88
89 output:
90 output stream to write to
91 """
92 self.elffile = ELFFile(file)
93 self.output = output
94
95 # Lazily initialized if a debug dump is requested
96 self._dwarfinfo = None
97
98 self._versioninfo = None
99
100 self._shndx_sections = None
101
102 def display_file_header(self):
103 """ Display the ELF file header
104 """
105 self._emitline('ELF Header:')
106 self._emit(' Magic: ')
107 self._emit(' '.join('%2.2x' % byte2int(b)
108 for b in self.elffile.e_ident_raw))
109 self._emitline(' ')
110 header = self.elffile.header
111 e_ident = header['e_ident']
112 self._emitline(' Class: %s' %
113 describe_ei_class(e_ident['EI_CLASS']))
114 self._emitline(' Data: %s' %
115 describe_ei_data(e_ident['EI_DATA']))
116 self._emitline(' Version: %s' %
117 describe_ei_version(e_ident['EI_VERSION']))
118 self._emitline(' OS/ABI: %s' %
119 describe_ei_osabi(e_ident['EI_OSABI']))
120 self._emitline(' ABI Version: %d' %
121 e_ident['EI_ABIVERSION'])
122 self._emitline(' Type: %s' %
123 describe_e_type(header['e_type'], self.elffile))
124 self._emitline(' Machine: %s' %
125 describe_e_machine(header['e_machine']))
126 self._emitline(' Version: %s' %
127 describe_e_version_numeric(header['e_version']))
128 self._emitline(' Entry point address: %s' %
129 self._format_hex(header['e_entry']))
130 self._emit(' Start of program headers: %s' %
131 header['e_phoff'])
132 self._emitline(' (bytes into file)')
133 self._emit(' Start of section headers: %s' %
134 header['e_shoff'])
135 self._emitline(' (bytes into file)')
136 self._emitline(' Flags: %s%s' %
137 (self._format_hex(header['e_flags']),
138 self.decode_flags(header['e_flags'])))
139 self._emitline(' Size of this header: %s (bytes)' %
140 header['e_ehsize'])
141 self._emitline(' Size of program headers: %s (bytes)' %
142 header['e_phentsize'])
143 self._emitline(' Number of program headers: %s' %
144 header['e_phnum'])
145 self._emitline(' Size of section headers: %s (bytes)' %
146 header['e_shentsize'])
147 self._emit(' Number of section headers: %s' %
148 header['e_shnum'])
149 if header['e_shnum'] == 0 and self.elffile.num_sections() != 0:
150 self._emitline(' (%d)' % self.elffile.num_sections())
151 else:
152 self._emitline('')
153 self._emit(' Section header string table index: %s' %
154 header['e_shstrndx'])
155 if header['e_shstrndx'] == SHN_INDICES.SHN_XINDEX:
156 self._emitline(' (%d)' % self.elffile.get_shstrndx())
157 else:
158 self._emitline('')
159
160 def decode_flags(self, flags):
161 description = ""
162 if self.elffile['e_machine'] == "EM_ARM":
163 eabi = flags & E_FLAGS.EF_ARM_EABIMASK
164 flags &= ~E_FLAGS.EF_ARM_EABIMASK
165
166 if flags & E_FLAGS.EF_ARM_RELEXEC:
167 description += ', relocatable executabl'
168 flags &= ~E_FLAGS.EF_ARM_RELEXEC
169
170 if eabi == E_FLAGS.EF_ARM_EABI_VER5:
171 EF_ARM_KNOWN_FLAGS = E_FLAGS.EF_ARM_ABI_FLOAT_SOFT|E_FLAGS.EF_ARM_ABI_FLOAT_HARD|E_FLAGS.EF_ARM_LE8|E_FLAGS.EF_ARM_BE8
172 description += ', Version5 EABI'
173 if flags & E_FLAGS.EF_ARM_ABI_FLOAT_SOFT:
174 description += ", soft-float ABI"
175 elif flags & E_FLAGS.EF_ARM_ABI_FLOAT_HARD:
176 description += ", hard-float ABI"
177
178 if flags & E_FLAGS.EF_ARM_BE8:
179 description += ", BE8"
180 elif flags & E_FLAGS.EF_ARM_LE8:
181 description += ", LE8"
182
183 if flags & ~EF_ARM_KNOWN_FLAGS:
184 description += ', <unknown>'
185 else:
186 description += ', <unrecognized EABI>'
187
188 elif self.elffile['e_machine'] == 'EM_PPC64':
189 if flags & E_FLAGS.EF_PPC64_ABI_V2:
190 description += ', abiv2'
191
192 elif self.elffile['e_machine'] == "EM_MIPS":
193 if flags & E_FLAGS.EF_MIPS_NOREORDER:
194 description += ", noreorder"
195 if flags & E_FLAGS.EF_MIPS_PIC:
196 description += ", pic"
197 if flags & E_FLAGS.EF_MIPS_CPIC:
198 description += ", cpic"
199 if (flags & E_FLAGS.EF_MIPS_ABI2):
200 description += ", abi2"
201 if (flags & E_FLAGS.EF_MIPS_32BITMODE):
202 description += ", 32bitmode"
203 if (flags & E_FLAGS_MASKS.EFM_MIPS_ABI_O32):
204 description += ", o32"
205 elif (flags & E_FLAGS_MASKS.EFM_MIPS_ABI_O64):
206 description += ", o64"
207 elif (flags & E_FLAGS_MASKS.EFM_MIPS_ABI_EABI32):
208 description += ", eabi32"
209 elif (flags & E_FLAGS_MASKS.EFM_MIPS_ABI_EABI64):
210 description += ", eabi64"
211 if (flags & E_FLAGS.EF_MIPS_ARCH) == E_FLAGS.EF_MIPS_ARCH_1:
212 description += ", mips1"
213 if (flags & E_FLAGS.EF_MIPS_ARCH) == E_FLAGS.EF_MIPS_ARCH_2:
214 description += ", mips2"
215 if (flags & E_FLAGS.EF_MIPS_ARCH) == E_FLAGS.EF_MIPS_ARCH_3:
216 description += ", mips3"
217 if (flags & E_FLAGS.EF_MIPS_ARCH) == E_FLAGS.EF_MIPS_ARCH_4:
218 description += ", mips4"
219 if (flags & E_FLAGS.EF_MIPS_ARCH) == E_FLAGS.EF_MIPS_ARCH_5:
220 description += ", mips5"
221 if (flags & E_FLAGS.EF_MIPS_ARCH) == E_FLAGS.EF_MIPS_ARCH_32R2:
222 description += ", mips32r2"
223 if (flags & E_FLAGS.EF_MIPS_ARCH) == E_FLAGS.EF_MIPS_ARCH_64R2:
224 description += ", mips64r2"
225 if (flags & E_FLAGS.EF_MIPS_ARCH) == E_FLAGS.EF_MIPS_ARCH_32:
226 description += ", mips32"
227 if (flags & E_FLAGS.EF_MIPS_ARCH) == E_FLAGS.EF_MIPS_ARCH_64:
228 description += ", mips64"
229
230 return description
231
232 def display_program_headers(self, show_heading=True):
233 """ Display the ELF program headers.
234 If show_heading is True, displays the heading for this information
235 (Elf file type is...)
236 """
237 self._emitline()
238 if self.elffile.num_segments() == 0:
239 self._emitline('There are no program headers in this file.')
240 return
241
242 elfheader = self.elffile.header
243 if show_heading:
244 self._emitline('Elf file type is %s' %
245 describe_e_type(elfheader['e_type'], self.elffile))
246 self._emitline('Entry point is %s' %
247 self._format_hex(elfheader['e_entry']))
248 # readelf weirness - why isn't e_phoff printed as hex? (for section
249 # headers, it is...)
250 self._emitline('There are %s program headers, starting at offset %s' % (
251 self.elffile.num_segments(), elfheader['e_phoff']))
252 self._emitline()
253
254 self._emitline('Program Headers:')
255
256 # Now comes the table of program headers with their attributes. Note
257 # that due to different formatting constraints of 32-bit and 64-bit
258 # addresses, there are some conditions on elfclass here.
259 #
260 # First comes the table heading
261 #
262 if self.elffile.elfclass == 32:
263 self._emitline(' Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align')
264 else:
265 self._emitline(' Type Offset VirtAddr PhysAddr')
266 self._emitline(' FileSiz MemSiz Flags Align')
267
268 # Now the entries
269 #
270 for segment in self.elffile.iter_segments():
271 self._emit(' %-14s ' % describe_p_type(segment['p_type']))
272
273 if self.elffile.elfclass == 32:
274 self._emitline('%s %s %s %s %s %-3s %s' % (
275 self._format_hex(segment['p_offset'], fieldsize=6),
276 self._format_hex(segment['p_vaddr'], fullhex=True),
277 self._format_hex(segment['p_paddr'], fullhex=True),
278 self._format_hex(segment['p_filesz'], fieldsize=5),
279 self._format_hex(segment['p_memsz'], fieldsize=5),
280 describe_p_flags(segment['p_flags']),
281 self._format_hex(segment['p_align'])))
282 else: # 64
283 self._emitline('%s %s %s' % (
284 self._format_hex(segment['p_offset'], fullhex=True),
285 self._format_hex(segment['p_vaddr'], fullhex=True),
286 self._format_hex(segment['p_paddr'], fullhex=True)))
287 self._emitline(' %s %s %-3s %s' % (
288 self._format_hex(segment['p_filesz'], fullhex=True),
289 self._format_hex(segment['p_memsz'], fullhex=True),
290 describe_p_flags(segment['p_flags']),
291 # lead0x set to False for p_align, to mimic readelf.
292 # No idea why the difference from 32-bit mode :-|
293 self._format_hex(segment['p_align'], lead0x=False)))
294
295 if isinstance(segment, InterpSegment):
296 self._emitline(' [Requesting program interpreter: %s]' %
297 segment.get_interp_name())
298
299 # Sections to segments mapping
300 #
301 if self.elffile.num_sections() == 0:
302 # No sections? We're done
303 return
304
305 self._emitline('\n Section to Segment mapping:')
306 self._emitline(' Segment Sections...')
307
308 for nseg, segment in enumerate(self.elffile.iter_segments()):
309 self._emit(' %2.2d ' % nseg)
310
311 for section in self.elffile.iter_sections():
312 if ( not section.is_null() and
313 not ((section['sh_flags'] & SH_FLAGS.SHF_TLS) != 0 and
314 section['sh_type'] == 'SHT_NOBITS' and
315 segment['p_type'] != 'PT_TLS') and
316 segment.section_in_segment(section)):
317 self._emit('%s ' % section.name)
318
319 self._emitline('')
320
321 def display_section_headers(self, show_heading=True):
322 """ Display the ELF section headers
323 """
324 elfheader = self.elffile.header
325 if show_heading:
326 self._emitline('There are %s section headers, starting at offset %s' % (
327 elfheader['e_shnum'], self._format_hex(elfheader['e_shoff'])))
328
329 if self.elffile.num_sections() == 0:
330 self._emitline('There are no sections in this file.')
331 return
332
333 self._emitline('\nSection Header%s:' % (
334 's' if self.elffile.num_sections() > 1 else ''))
335
336 # Different formatting constraints of 32-bit and 64-bit addresses
337 #
338 if self.elffile.elfclass == 32:
339 self._emitline(' [Nr] Name Type Addr Off Size ES Flg Lk Inf Al')
340 else:
341 self._emitline(' [Nr] Name Type Address Offset')
342 self._emitline(' Size EntSize Flags Link Info Align')
343
344 # Now the entries
345 #
346 for nsec, section in enumerate(self.elffile.iter_sections()):
347 self._emit(' [%2u] %-17.17s %-15.15s ' % (
348 nsec, section.name, describe_sh_type(section['sh_type'])))
349
350 if self.elffile.elfclass == 32:
351 self._emitline('%s %s %s %s %3s %2s %3s %2s' % (
352 self._format_hex(section['sh_addr'], fieldsize=8, lead0x=False),
353 self._format_hex(section['sh_offset'], fieldsize=6, lead0x=False),
354 self._format_hex(section['sh_size'], fieldsize=6, lead0x=False),
355 self._format_hex(section['sh_entsize'], fieldsize=2, lead0x=False),
356 describe_sh_flags(section['sh_flags']),
357 section['sh_link'], section['sh_info'],
358 section['sh_addralign']))
359 else: # 64
360 self._emitline(' %s %s' % (
361 self._format_hex(section['sh_addr'], fullhex=True, lead0x=False),
362 self._format_hex(section['sh_offset'],
363 fieldsize=16 if section['sh_offset'] > 0xffffffff else 8,
364 lead0x=False)))
365 self._emitline(' %s %s %3s %2s %3s %s' % (
366 self._format_hex(section['sh_size'], fullhex=True, lead0x=False),
367 self._format_hex(section['sh_entsize'], fullhex=True, lead0x=False),
368 describe_sh_flags(section['sh_flags']),
369 section['sh_link'], section['sh_info'],
370 section['sh_addralign']))
371
372 self._emitline('Key to Flags:')
373 self._emitline(' W (write), A (alloc), X (execute), M (merge),'
374 ' S (strings), I (info),')
375 self._emitline(' L (link order), O (extra OS processing required),'
376 ' G (group), T (TLS),')
377 self._emitline(' C (compressed), x (unknown), o (OS specific),'
378 ' E (exclude),')
379 self._emit(' ')
380 if self.elffile['e_machine'] == 'EM_ARM':
381 self._emit('y (purecode), ')
382 self._emitline('p (processor specific)')
383
384 def display_symbol_tables(self):
385 """ Display the symbol tables contained in the file
386 """
387 self._init_versioninfo()
388
389 symbol_tables = [(idx, s) for idx, s in enumerate(self.elffile.iter_sections())
390 if isinstance(s, SymbolTableSection)]
391
392 if not symbol_tables and self.elffile.num_sections() == 0:
393 self._emitline('')
394 self._emitline('Dynamic symbol information is not available for'
395 ' displaying symbols.')
396
397 for section_index, section in symbol_tables:
398 if not isinstance(section, SymbolTableSection):
399 continue
400
401 if section['sh_entsize'] == 0:
402 self._emitline("\nSymbol table '%s' has a sh_entsize of zero!" % (
403 section.name))
404 continue
405
406 self._emitline("\nSymbol table '%s' contains %d %s:" % (
407 section.name,
408 section.num_symbols(),
409 'entry' if section.num_symbols() == 1 else 'entries'))
410
411 if self.elffile.elfclass == 32:
412 self._emitline(' Num: Value Size Type Bind Vis Ndx Name')
413 else: # 64
414 self._emitline(' Num: Value Size Type Bind Vis Ndx Name')
415
416 for nsym, symbol in enumerate(section.iter_symbols()):
417 version_info = ''
418 # readelf doesn't display version info for Solaris versioning
419 if (section['sh_type'] == 'SHT_DYNSYM' and
420 self._versioninfo['type'] == 'GNU'):
421 version = self._symbol_version(nsym)
422 if (version['name'] != symbol.name and
423 version['index'] not in ('VER_NDX_LOCAL',
424 'VER_NDX_GLOBAL')):
425 if version['filename']:
426 # external symbol
427 version_info = '@%(name)s (%(index)i)' % version
428 else:
429 # internal symbol
430 if version['hidden']:
431 version_info = '@%(name)s' % version
432 else:
433 version_info = '@@%(name)s' % version
434
435 symbol_name = symbol.name
436 # Print section names for STT_SECTION symbols as readelf does
437 if (symbol['st_info']['type'] == 'STT_SECTION'
438 and symbol['st_shndx'] < self.elffile.num_sections()
439 and symbol['st_name'] == 0):
440 symbol_name = self.elffile.get_section(symbol['st_shndx']).name
441
442 # symbol names are truncated to 25 chars, similarly to readelf
443 self._emitline('%6d: %s %s %-7s %-6s %-7s %4s %.25s%s' % (
444 nsym,
445 self._format_hex(
446 symbol['st_value'], fullhex=True, lead0x=False),
447 "%5d" % symbol['st_size'] if symbol['st_size'] < 100000 else hex(symbol['st_size']),
448 describe_symbol_type(symbol['st_info']['type']),
449 describe_symbol_bind(symbol['st_info']['bind']),
450 describe_symbol_other(symbol['st_other']),
451 describe_symbol_shndx(self._get_symbol_shndx(symbol,
452 nsym,
453 section_index)),
454 symbol_name,
455 version_info))
456
457 def display_dynamic_tags(self):
458 """ Display the dynamic tags contained in the file
459 """
460 has_dynamic_sections = False
461 for section in self.elffile.iter_sections():
462 if not isinstance(section, DynamicSection):
463 continue
464
465 has_dynamic_sections = True
466 self._emitline("\nDynamic section at offset %s contains %d %s:" % (
467 self._format_hex(section['sh_offset']),
468 section.num_tags(),
469 'entry' if section.num_tags() == 1 else 'entries'))
470 self._emitline(" Tag Type Name/Value")
471
472 padding = 20 + (8 if self.elffile.elfclass == 32 else 0)
473 for tag in section.iter_tags():
474 if tag.entry.d_tag == 'DT_NEEDED':
475 parsed = 'Shared library: [%s]' % tag.needed
476 elif tag.entry.d_tag == 'DT_RPATH':
477 parsed = 'Library rpath: [%s]' % tag.rpath
478 elif tag.entry.d_tag == 'DT_RUNPATH':
479 parsed = 'Library runpath: [%s]' % tag.runpath
480 elif tag.entry.d_tag == 'DT_SONAME':
481 parsed = 'Library soname: [%s]' % tag.soname
482 elif tag.entry.d_tag.endswith(('SZ', 'ENT')):
483 parsed = '%i (bytes)' % tag['d_val']
484 elif tag.entry.d_tag == 'DT_FLAGS':
485 parsed = describe_dt_flags(tag.entry.d_val)
486 elif tag.entry.d_tag == 'DT_FLAGS_1':
487 parsed = 'Flags: %s' % describe_dt_flags_1(tag.entry.d_val)
488 elif tag.entry.d_tag.endswith(('NUM', 'COUNT')):
489 parsed = '%i' % tag['d_val']
490 elif tag.entry.d_tag == 'DT_PLTREL':
491 s = describe_dyn_tag(tag.entry.d_val)
492 if s.startswith('DT_'):
493 s = s[3:]
494 parsed = '%s' % s
495 elif tag.entry.d_tag == 'DT_MIPS_FLAGS':
496 parsed = describe_rh_flags(tag.entry.d_val)
497 elif tag.entry.d_tag in ('DT_MIPS_SYMTABNO',
498 'DT_MIPS_LOCAL_GOTNO'):
499 parsed = str(tag.entry.d_val)
500 else:
501 parsed = '%#x' % tag['d_val']
502
503 self._emitline(" %s %-*s %s" % (
504 self._format_hex(ENUM_D_TAG.get(tag.entry.d_tag, tag.entry.d_tag),
505 fullhex=True, lead0x=True),
506 padding,
507 '(%s)' % (tag.entry.d_tag[3:],),
508 parsed))
509 if not has_dynamic_sections:
510 self._emitline("\nThere is no dynamic section in this file.")
511
512 def display_notes(self):
513 """ Display the notes contained in the file
514 """
515 for section in self.elffile.iter_sections():
516 if isinstance(section, NoteSection):
517 for note in section.iter_notes():
518 self._emitline("\nDisplaying notes found in: {}".format(
519 section.name))
520 self._emitline(' Owner Data size Description')
521 self._emitline(' %s %s\t%s' % (
522 note['n_name'].ljust(20),
523 self._format_hex(note['n_descsz'], fieldsize=8),
524 describe_note(note)))
525
526 def display_relocations(self):
527 """ Display the relocations contained in the file
528 """
529 has_relocation_sections = False
530 for section in self.elffile.iter_sections():
531 if not isinstance(section, RelocationSection):
532 continue
533
534 has_relocation_sections = True
535 self._emitline("\nRelocation section '%.128s' at offset %s contains %d %s:" % (
536 section.name,
537 self._format_hex(section['sh_offset']),
538 section.num_relocations(),
539 'entry' if section.num_relocations() == 1 else 'entries'))
540 if section.is_RELA():
541 self._emitline(" Offset Info Type Sym. Value Sym. Name + Addend")
542 else:
543 self._emitline(" Offset Info Type Sym.Value Sym. Name")
544
545 # The symbol table section pointed to in sh_link
546 symtable = self.elffile.get_section(section['sh_link'])
547
548 for rel in section.iter_relocations():
549 hexwidth = 8 if self.elffile.elfclass == 32 else 12
550 self._emit('%s %s %-17.17s' % (
551 self._format_hex(rel['r_offset'],
552 fieldsize=hexwidth, lead0x=False),
553 self._format_hex(rel['r_info'],
554 fieldsize=hexwidth, lead0x=False),
555 describe_reloc_type(
556 rel['r_info_type'], self.elffile)))
557
558 if rel['r_info_sym'] == 0:
559 if section.is_RELA():
560 fieldsize = 8 if self.elffile.elfclass == 32 else 16
561 addend = self._format_hex(rel['r_addend'], lead0x=False)
562 self._emit(' %s %s' % (' ' * fieldsize, addend))
563 self._emitline()
564
565 else:
566 symbol = symtable.get_symbol(rel['r_info_sym'])
567 # Some symbols have zero 'st_name', so instead what's used
568 # is the name of the section they point at. Truncate symbol
569 # names (excluding version info) to 22 chars, similarly to
570 # readelf.
571 if symbol['st_name'] == 0:
572 symsecidx = self._get_symbol_shndx(symbol,
573 rel['r_info_sym'],
574 section['sh_link'])
575 symsec = self.elffile.get_section(symsecidx)
576 symbol_name = symsec.name
577 version = ''
578 else:
579 symbol_name = symbol.name
580 version = self._symbol_version(rel['r_info_sym'])
581 version = (version['name']
582 if version and version['name'] else '')
583 symbol_name = '%.22s' % symbol_name
584 if version:
585 symbol_name += '@' + version
586
587 self._emit(' %s %s' % (
588 self._format_hex(
589 symbol['st_value'],
590 fullhex=True, lead0x=False),
591 symbol_name))
592 if section.is_RELA():
593 self._emit(' %s %x' % (
594 '+' if rel['r_addend'] >= 0 else '-',
595 abs(rel['r_addend'])))
596 self._emitline()
597
598 # Emit the two additional relocation types for ELF64 MIPS
599 # binaries.
600 if (self.elffile.elfclass == 64 and
601 self.elffile['e_machine'] == 'EM_MIPS'):
602 for i in (2, 3):
603 rtype = rel['r_info_type%s' % i]
604 self._emit(' Type%s: %s' % (
605 i,
606 describe_reloc_type(rtype, self.elffile)))
607 self._emitline()
608
609 if not has_relocation_sections:
610 self._emitline('\nThere are no relocations in this file.')
611
612 def display_arm_unwind(self):
613 if not self.elffile.has_ehabi_info():
614 self._emitline('There are no .ARM.idx sections in this file.')
615 return
616 for ehabi_info in self.elffile.get_ehabi_infos():
617 # Unwind section '.ARM.exidx' at offset 0x203e8 contains 1009 entries:
618 self._emitline("\nUnwind section '%s' at offset 0x%x contains %d %s" % (
619 ehabi_info.section_name(),
620 ehabi_info.section_offset(),
621 ehabi_info.num_entry(),
622 'entry' if ehabi_info.num_entry() == 1 else 'entries'))
623
624 for i in range(ehabi_info.num_entry()):
625 entry = ehabi_info.get_entry(i)
626 self._emitline()
627 self._emitline("Entry %d:" % i)
628 if isinstance(entry, CorruptEHABIEntry):
629 self._emitline(" [corrupt] %s" % entry.reason)
630 continue
631 self._emit(" Function offset 0x%x: " % entry.function_offset)
632 if isinstance(entry, CannotUnwindEHABIEntry):
633 self._emitline("[cantunwind]")
634 continue
635 elif entry.eh_table_offset:
636 self._emitline("@0x%x" % entry.eh_table_offset)
637 else:
638 self._emitline("Compact (inline)")
639 if isinstance(entry, GenericEHABIEntry):
640 self._emitline(" Personality: 0x%x" % entry.personality)
641 else:
642 self._emitline(" Compact model index: %d" % entry.personality)
643 for mnemonic_item in entry.mnmemonic_array():
644 self._emit(' ')
645 self._emitline(mnemonic_item)
646
647 def display_version_info(self):
648 """ Display the version info contained in the file
649 """
650 self._init_versioninfo()
651
652 if not self._versioninfo['type']:
653 self._emitline("\nNo version information found in this file.")
654 return
655
656 for section in self.elffile.iter_sections():
657 if isinstance(section, GNUVerSymSection):
658 self._print_version_section_header(section, 'Version symbols')
659 num_symbols = section.num_symbols()
660
661 # Symbol version info are printed four by four entries
662 for idx_by_4 in range(0, num_symbols, 4):
663
664 self._emit(' %03x:' % idx_by_4)
665
666 for idx in range(idx_by_4, min(idx_by_4 + 4, num_symbols)):
667
668 symbol_version = self._symbol_version(idx)
669 if symbol_version['index'] == 'VER_NDX_LOCAL':
670 version_index = 0
671 version_name = '(*local*)'
672 elif symbol_version['index'] == 'VER_NDX_GLOBAL':
673 version_index = 1
674 version_name = '(*global*)'
675 else:
676 version_index = symbol_version['index']
677 version_name = '(%(name)s)' % symbol_version
678
679 visibility = 'h' if symbol_version['hidden'] else ' '
680
681 self._emit('%4x%s%-13s' % (
682 version_index, visibility, version_name))
683
684 self._emitline()
685
686 elif isinstance(section, GNUVerDefSection):
687 self._print_version_section_header(
688 section, 'Version definition', indent=2)
689
690 offset = 0
691 for verdef, verdaux_iter in section.iter_versions():
692 verdaux = next(verdaux_iter)
693
694 name = verdaux.name
695 if verdef['vd_flags']:
696 flags = describe_ver_flags(verdef['vd_flags'])
697 # Mimic exactly the readelf output
698 flags += ' '
699 else:
700 flags = 'none'
701
702 self._emitline(' %s: Rev: %i Flags: %s Index: %i'
703 ' Cnt: %i Name: %s' % (
704 self._format_hex(offset, fieldsize=6,
705 alternate=True),
706 verdef['vd_version'], flags, verdef['vd_ndx'],
707 verdef['vd_cnt'], name))
708
709 verdaux_offset = (
710 offset + verdef['vd_aux'] + verdaux['vda_next'])
711 for idx, verdaux in enumerate(verdaux_iter, start=1):
712 self._emitline(' %s: Parent %i: %s' %
713 (self._format_hex(verdaux_offset, fieldsize=4),
714 idx, verdaux.name))
715 verdaux_offset += verdaux['vda_next']
716
717 offset += verdef['vd_next']
718
719 elif isinstance(section, GNUVerNeedSection):
720 self._print_version_section_header(section, 'Version needs')
721
722 offset = 0
723 for verneed, verneed_iter in section.iter_versions():
724
725 self._emitline(' %s: Version: %i File: %s Cnt: %i' % (
726 self._format_hex(offset, fieldsize=6,
727 alternate=True),
728 verneed['vn_version'], verneed.name,
729 verneed['vn_cnt']))
730
731 vernaux_offset = offset + verneed['vn_aux']
732 for idx, vernaux in enumerate(verneed_iter, start=1):
733 if vernaux['vna_flags']:
734 flags = describe_ver_flags(vernaux['vna_flags'])
735 # Mimic exactly the readelf output
736 flags += ' '
737 else:
738 flags = 'none'
739
740 self._emitline(
741 ' %s: Name: %s Flags: %s Version: %i' % (
742 self._format_hex(vernaux_offset, fieldsize=4),
743 vernaux.name, flags,
744 vernaux['vna_other']))
745
746 vernaux_offset += vernaux['vna_next']
747
748 offset += verneed['vn_next']
749
750 def display_arch_specific(self):
751 """ Display the architecture-specific info contained in the file.
752 """
753 if self.elffile['e_machine'] == 'EM_ARM':
754 self._display_arch_specific_arm()
755
756 def display_hex_dump(self, section_spec):
757 """ Display a hex dump of a section. section_spec is either a section
758 number or a name.
759 """
760 section = self._section_from_spec(section_spec)
761 if section is None:
762 # readelf prints the warning to stderr. Even though stderrs are not compared
763 # in tests, we comply with that behavior.
764 sys.stderr.write('readelf: Warning: Section \'%s\' was not dumped because it does not exist!\n' % (
765 section_spec))
766 return
767 if section['sh_type'] == 'SHT_NOBITS':
768 self._emitline("\nSection '%s' has no data to dump." % (
769 section_spec))
770 return
771
772 self._emitline("\nHex dump of section '%s':" % section.name)
773 self._note_relocs_for_section(section)
774 addr = section['sh_addr']
775 data = section.data()
776 dataptr = 0
777
778 while dataptr < len(data):
779 bytesleft = len(data) - dataptr
780 # chunks of 16 bytes per line
781 linebytes = 16 if bytesleft > 16 else bytesleft
782
783 self._emit(' %s ' % self._format_hex(addr, fieldsize=8))
784 for i in range(16):
785 if i < linebytes:
786 self._emit('%2.2x' % byte2int(data[dataptr + i]))
787 else:
788 self._emit(' ')
789 if i % 4 == 3:
790 self._emit(' ')
791
792 for i in range(linebytes):
793 c = data[dataptr + i : dataptr + i + 1]
794 if byte2int(c[0]) >= 32 and byte2int(c[0]) < 0x7f:
795 self._emit(bytes2str(c))
796 else:
797 self._emit(bytes2str(b'.'))
798
799 self._emitline()
800 addr += linebytes
801 dataptr += linebytes
802
803 self._emitline()
804
805 def display_string_dump(self, section_spec):
806 """ Display a strings dump of a section. section_spec is either a
807 section number or a name.
808 """
809 section = self._section_from_spec(section_spec)
810 if section is None:
811 # readelf prints the warning to stderr. Even though stderrs are not compared
812 # in tests, we comply with that behavior.
813 sys.stderr.write('readelf.py: Warning: Section \'%s\' was not dumped because it does not exist!\n' % (
814 section_spec))
815 return
816 if section['sh_type'] == 'SHT_NOBITS':
817 self._emitline("\nSection '%s' has no data to dump." % (
818 section_spec))
819 return
820
821 self._emitline("\nString dump of section '%s':" % section.name)
822
823 found = False
824 data = section.data()
825 dataptr = 0
826
827 while dataptr < len(data):
828 while ( dataptr < len(data) and
829 not (32 <= byte2int(data[dataptr]) <= 127)):
830 dataptr += 1
831
832 if dataptr >= len(data):
833 break
834
835 endptr = dataptr
836 while endptr < len(data) and byte2int(data[endptr]) != 0:
837 endptr += 1
838
839 found = True
840 self._emitline(' [%6x] %s' % (
841 dataptr, bytes2str(data[dataptr:endptr])))
842
843 dataptr = endptr
844
845 if not found:
846 self._emitline(' No strings found in this section.')
847 else:
848 self._emitline()
849
850 def display_debug_dump(self, dump_what):
851 """ Dump a DWARF section
852 """
853 self._init_dwarfinfo()
854 if self._dwarfinfo is None:
855 return
856
857 set_global_machine_arch(self.elffile.get_machine_arch())
858
859 if dump_what == 'info':
860 self._dump_debug_info()
861 elif dump_what == 'decodedline':
862 self._dump_debug_line_programs()
863 elif dump_what == 'frames':
864 self._dump_debug_frames()
865 elif dump_what == 'frames-interp':
866 self._dump_debug_frames_interp()
867 elif dump_what == 'aranges':
868 self._dump_debug_aranges()
869 elif dump_what in { 'pubtypes', 'pubnames' }:
870 self._dump_debug_namelut(dump_what)
871 elif dump_what == 'loc':
872 self._dump_debug_locations()
873 elif dump_what == 'Ranges':
874 self._dump_debug_ranges()
875 else:
876 self._emitline('debug dump not yet supported for "%s"' % dump_what)
877
878 def _format_hex(self, addr, fieldsize=None, fullhex=False, lead0x=True,
879 alternate=False):
880 """ Format an address into a hexadecimal string.
881
882 fieldsize:
883 Size of the hexadecimal field (with leading zeros to fit the
884 address into. For example with fieldsize=8, the format will
885 be %08x
886 If None, the minimal required field size will be used.
887
888 fullhex:
889 If True, override fieldsize to set it to the maximal size
890 needed for the elfclass
891
892 lead0x:
893 If True, leading 0x is added
894
895 alternate:
896 If True, override lead0x to emulate the alternate
897 hexadecimal form specified in format string with the #
898 character: only non-zero values are prefixed with 0x.
899 This form is used by readelf.
900 """
901 if alternate:
902 if addr == 0:
903 lead0x = False
904 else:
905 lead0x = True
906 fieldsize -= 2
907
908 s = '0x' if lead0x else ''
909 if fullhex:
910 fieldsize = 8 if self.elffile.elfclass == 32 else 16
911 if fieldsize is None:
912 field = '%x'
913 else:
914 field = '%' + '0%sx' % fieldsize
915 return s + field % addr
916
917 def _print_version_section_header(self, version_section, name, lead0x=True,
918 indent=1):
919 """ Print a section header of one version related section (versym,
920 verneed or verdef) with some options to accomodate readelf
921 little differences between each header (e.g. indentation
922 and 0x prefixing).
923 """
924 if hasattr(version_section, 'num_versions'):
925 num_entries = version_section.num_versions()
926 else:
927 num_entries = version_section.num_symbols()
928
929 self._emitline("\n%s section '%s' contains %d %s:" % (
930 name, version_section.name, num_entries,
931 'entry' if num_entries == 1 else 'entries'))
932 self._emitline('%sAddr: %s Offset: %s Link: %i (%s)' % (
933 ' ' * indent,
934 self._format_hex(
935 version_section['sh_addr'], fieldsize=16, lead0x=lead0x),
936 self._format_hex(
937 version_section['sh_offset'], fieldsize=6, lead0x=True),
938 version_section['sh_link'],
939 self.elffile.get_section(version_section['sh_link']).name
940 )
941 )
942
943 def _init_versioninfo(self):
944 """ Search and initialize informations about version related sections
945 and the kind of versioning used (GNU or Solaris).
946 """
947 if self._versioninfo is not None:
948 return
949
950 self._versioninfo = {'versym': None, 'verdef': None,
951 'verneed': None, 'type': None}
952
953 for section in self.elffile.iter_sections():
954 if isinstance(section, GNUVerSymSection):
955 self._versioninfo['versym'] = section
956 elif isinstance(section, GNUVerDefSection):
957 self._versioninfo['verdef'] = section
958 elif isinstance(section, GNUVerNeedSection):
959 self._versioninfo['verneed'] = section
960 elif isinstance(section, DynamicSection):
961 for tag in section.iter_tags():
962 if tag['d_tag'] == 'DT_VERSYM':
963 self._versioninfo['type'] = 'GNU'
964 break
965
966 if not self._versioninfo['type'] and (
967 self._versioninfo['verneed'] or self._versioninfo['verdef']):
968 self._versioninfo['type'] = 'Solaris'
969
970 def _symbol_version(self, nsym):
971 """ Return a dict containing information on the
972 or None if no version information is available
973 """
974 self._init_versioninfo()
975
976 symbol_version = dict.fromkeys(('index', 'name', 'filename', 'hidden'))
977
978 if (not self._versioninfo['versym'] or
979 nsym >= self._versioninfo['versym'].num_symbols()):
980 return None
981
982 symbol = self._versioninfo['versym'].get_symbol(nsym)
983 index = symbol.entry['ndx']
984 if not index in ('VER_NDX_LOCAL', 'VER_NDX_GLOBAL'):
985 index = int(index)
986
987 if self._versioninfo['type'] == 'GNU':
988 # In GNU versioning mode, the highest bit is used to
989 # store whether the symbol is hidden or not
990 if index & 0x8000:
991 index &= ~0x8000
992 symbol_version['hidden'] = True
993
994 if (self._versioninfo['verdef'] and
995 index <= self._versioninfo['verdef'].num_versions()):
996 _, verdaux_iter = \
997 self._versioninfo['verdef'].get_version(index)
998 symbol_version['name'] = next(verdaux_iter).name
999 else:
1000 verneed, vernaux = \
1001 self._versioninfo['verneed'].get_version(index)
1002 symbol_version['name'] = vernaux.name
1003 symbol_version['filename'] = verneed.name
1004
1005 symbol_version['index'] = index
1006 return symbol_version
1007
1008 def _section_from_spec(self, spec):
1009 """ Retrieve a section given a "spec" (either number or name).
1010 Return None if no such section exists in the file.
1011 """
1012 try:
1013 num = int(spec)
1014 if num < self.elffile.num_sections():
1015 return self.elffile.get_section(num)
1016 else:
1017 return None
1018 except ValueError:
1019 # Not a number. Must be a name then
1020 return self.elffile.get_section_by_name(spec)
1021
1022 def _get_symbol_shndx(self, symbol, symbol_index, symtab_index):
1023 """ Get the index into the section header table for the "symbol"
1024 at "symbol_index" located in the symbol table with section index
1025 "symtab_index".
1026 """
1027 symbol_shndx = symbol['st_shndx']
1028 if symbol_shndx != SHN_INDICES.SHN_XINDEX:
1029 return symbol_shndx
1030
1031 # Check for or lazily construct index section mapping (symbol table
1032 # index -> corresponding symbol table index section object)
1033 if self._shndx_sections is None:
1034 self._shndx_sections = {sec.symboltable: sec for sec in self.elffile.iter_sections()
1035 if isinstance(sec, SymbolTableIndexSection)}
1036 return self._shndx_sections[symtab_index].get_section_index(symbol_index)
1037
1038 def _note_relocs_for_section(self, section):
1039 """ If there are relocation sections pointing to the givne section,
1040 emit a note about it.
1041 """
1042 for relsec in self.elffile.iter_sections():
1043 if isinstance(relsec, RelocationSection):
1044 info_idx = relsec['sh_info']
1045 if self.elffile.get_section(info_idx) == section:
1046 self._emitline(' Note: This section has relocations against it, but these have NOT been applied to this dump.')
1047 return
1048
1049 def _init_dwarfinfo(self):
1050 """ Initialize the DWARF info contained in the file and assign it to
1051 self._dwarfinfo.
1052 Leave self._dwarfinfo at None if no DWARF info was found in the file
1053 """
1054 if self._dwarfinfo is not None:
1055 return
1056
1057 if self.elffile.has_dwarf_info():
1058 self._dwarfinfo = self.elffile.get_dwarf_info()
1059 else:
1060 self._dwarfinfo = None
1061
1062 def _dump_debug_info(self):
1063 """ Dump the debugging info section.
1064 """
1065 if not self._dwarfinfo.has_debug_info:
1066 return
1067 self._emitline('Contents of the %s section:\n' % self._dwarfinfo.debug_info_sec.name)
1068
1069 # Offset of the .debug_info section in the stream
1070 section_offset = self._dwarfinfo.debug_info_sec.global_offset
1071
1072 for cu in self._dwarfinfo.iter_CUs():
1073 self._emitline(' Compilation Unit @ offset %s:' %
1074 self._format_hex(cu.cu_offset))
1075 self._emitline(' Length: %s (%s)' % (
1076 self._format_hex(cu['unit_length']),
1077 '%s-bit' % cu.dwarf_format()))
1078 self._emitline(' Version: %s' % cu['version'])
1079 if cu.header.get("unit_type", False):
1080 ut = next((key for key, value in ENUM_DW_UT.items() if value == cu.header.unit_type), '?')
1081 self._emitline(' Unit Type: %s (%d)' % (ut, cu.header.unit_type))
1082 self._emitline(' Abbrev Offset: %s' % (
1083 self._format_hex(cu['debug_abbrev_offset']))),
1084 self._emitline(' Pointer Size: %s' % cu['address_size'])
1085
1086 # The nesting depth of each DIE within the tree of DIEs must be
1087 # displayed. To implement this, a counter is incremented each time
1088 # the current DIE has children, and decremented when a null die is
1089 # encountered. Due to the way the DIE tree is serialized, this will
1090 # correctly reflect the nesting depth
1091 #
1092 die_depth = 0
1093 current_function = None
1094 for die in cu.iter_DIEs():
1095 if die.tag == 'DW_TAG_subprogram':
1096 current_function = die
1097 self._emitline(' <%s><%x>: Abbrev Number: %s%s' % (
1098 die_depth,
1099 die.offset,
1100 die.abbrev_code,
1101 (' (%s)' % die.tag) if not die.is_null() else ''))
1102 if die.is_null():
1103 die_depth -= 1
1104 continue
1105
1106 for attr in itervalues(die.attributes):
1107 name = attr.name
1108 # Unknown attribute values are passed-through as integers
1109 if isinstance(name, int):
1110 name = 'Unknown AT value: %x' % name
1111
1112 attr_desc = describe_attr_value(attr, die, section_offset)
1113
1114 if 'DW_OP_fbreg' in attr_desc and current_function and not 'DW_AT_frame_base' in current_function.attributes:
1115 postfix = ' [without dw_at_frame_base]'
1116 else:
1117 postfix = ''
1118
1119 self._emitline(' <%x> %-18s: %s%s' % (
1120 attr.offset,
1121 name,
1122 attr_desc,
1123 postfix))
1124
1125 if die.has_children:
1126 die_depth += 1
1127
1128 self._emitline()
1129
1130 def _dump_debug_line_programs(self):
1131 """ Dump the (decoded) line programs from .debug_line
1132 The programs are dumped in the order of the CUs they belong to.
1133 """
1134 if not self._dwarfinfo.has_debug_info:
1135 return
1136 self._emitline('Contents of the %s section:' % self._dwarfinfo.debug_line_sec.name)
1137 self._emitline()
1138
1139 for cu in self._dwarfinfo.iter_CUs():
1140 lineprogram = self._dwarfinfo.line_program_for_CU(cu)
1141 ver5 = lineprogram.header.version >= 5
1142
1143 cu_filename = bytes2str(lineprogram['file_entry'][0].name)
1144 if len(lineprogram['include_directory']) > 0:
1145 dir_index = lineprogram['file_entry'][0].dir_index
1146 if dir_index > 0:
1147 dir = lineprogram['include_directory'][dir_index - 1]
1148 else:
1149 dir = b'.'
1150 cu_filename = '%s/%s' % (bytes2str(dir), cu_filename)
1151
1152 self._emitline('CU: %s:' % cu_filename)
1153 self._emitline('File name Line number Starting address Stmt')
1154 # GNU readelf has a View column that we don't try to replicate
1155 # The autotest has logic in place to ignore that
1156
1157 # Print each state's file, line and address information. For some
1158 # instructions other output is needed to be compatible with
1159 # readelf.
1160 for entry in lineprogram.get_entries():
1161 state = entry.state
1162 if state is None:
1163 # Special handling for commands that don't set a new state
1164 if entry.command == DW_LNS_set_file:
1165 file_entry = lineprogram['file_entry'][entry.args[0] - 1]
1166 if file_entry.dir_index == 0:
1167 # current directory
1168 self._emitline('\n./%s:[++]' % (
1169 bytes2str(file_entry.name)))
1170 else:
1171 self._emitline('\n%s/%s:' % (
1172 bytes2str(lineprogram['include_directory'][file_entry.dir_index - 1]),
1173 bytes2str(file_entry.name)))
1174 elif entry.command == DW_LNE_define_file:
1175 self._emitline('%s:' % (
1176 bytes2str(lineprogram['include_directory'][entry.args[0].dir_index])))
1177 elif lineprogram['version'] < 4 or self.elffile['e_machine'] == 'EM_PPC64':
1178 self._emitline('%-35s %11s %18s %s' % (
1179 bytes2str(lineprogram['file_entry'][state.file - 1].name),
1180 state.line if not state.end_sequence else '-',
1181 '0' if state.address == 0 else self._format_hex(state.address),
1182 'x' if state.is_stmt and not state.end_sequence else ''))
1183 else:
1184 # What's the deal with op_index after address on DWARF 5? Is omitting it
1185 # a function of DWARF version, or ISA, or what?
1186 # Used to be unconditional, even on non-VLIW machines.
1187 self._emitline('%-35s %s %18s%s %s' % (
1188 bytes2str(lineprogram['file_entry'][state.file - 1].name),
1189 "%11d" % (state.line,) if not state.end_sequence else '-',
1190 '0' if state.address == 0 else self._format_hex(state.address),
1191 '' if ver5 else '[%d]' % (state.op_index,),
1192 'x' if state.is_stmt and not state.end_sequence else ''))
1193 if entry.command == DW_LNS_copy:
1194 # Another readelf oddity...
1195 self._emitline()
1196
1197 def _dump_frames_info(self, section, cfi_entries):
1198 """ Dump the raw call frame info in a section.
1199
1200 `section` is the Section instance that contains the call frame info
1201 while `cfi_entries` must be an iterable that yields the sequence of
1202 CIE or FDE instances.
1203 """
1204 self._emitline('Contents of the %s section:' % section.name)
1205
1206 for entry in cfi_entries:
1207 if isinstance(entry, CIE):
1208 self._emitline('\n%08x %s %s CIE' % (
1209 entry.offset,
1210 self._format_hex(entry['length'], fullhex=True, lead0x=False),
1211 self._format_hex(entry['CIE_id'], fieldsize=8, lead0x=False)))
1212 self._emitline(' Version: %d' % entry['version'])
1213 self._emitline(' Augmentation: "%s"' % bytes2str(entry['augmentation']))
1214 self._emitline(' Code alignment factor: %u' % entry['code_alignment_factor'])
1215 self._emitline(' Data alignment factor: %d' % entry['data_alignment_factor'])
1216 self._emitline(' Return address column: %d' % entry['return_address_register'])
1217 if entry.augmentation_bytes:
1218 self._emitline(' Augmentation data: {}'.format(' '.join(
1219 '{:02x}'.format(ord(b))
1220 for b in iterbytes(entry.augmentation_bytes)
1221 )))
1222 self._emitline()
1223
1224 elif isinstance(entry, FDE):
1225 self._emitline('\n%08x %s %s FDE cie=%08x pc=%s..%s' % (
1226 entry.offset,
1227 self._format_hex(entry['length'], fullhex=True, lead0x=False),
1228 self._format_hex(entry['CIE_pointer'], fieldsize=8, lead0x=False),
1229 entry.cie.offset,
1230 self._format_hex(entry['initial_location'], fullhex=True, lead0x=False),
1231 self._format_hex(
1232 entry['initial_location'] + entry['address_range'],
1233 fullhex=True, lead0x=False)))
1234 if entry.augmentation_bytes:
1235 self._emitline(' Augmentation data: {}'.format(' '.join(
1236 '{:02x}'.format(ord(b))
1237 for b in iterbytes(entry.augmentation_bytes)
1238 )))
1239
1240 else: # ZERO terminator
1241 assert isinstance(entry, ZERO)
1242 self._emitline('\n%08x ZERO terminator' % entry.offset)
1243 continue
1244
1245 self._emit(describe_CFI_instructions(entry))
1246 self._emitline()
1247
1248 def _dump_debug_frames(self):
1249 """ Dump the raw frame info from .debug_frame and .eh_frame sections.
1250 """
1251 if self._dwarfinfo.has_EH_CFI():
1252 self._dump_frames_info(
1253 self._dwarfinfo.eh_frame_sec,
1254 self._dwarfinfo.EH_CFI_entries())
1255 self._emitline()
1256
1257 if self._dwarfinfo.has_CFI():
1258 self._dump_frames_info(
1259 self._dwarfinfo.debug_frame_sec,
1260 self._dwarfinfo.CFI_entries())
1261
1262 def _dump_debug_namelut(self, what):
1263 """
1264 Dump the debug pubnames section.
1265 """
1266 if what == 'pubnames':
1267 namelut = self._dwarfinfo.get_pubnames()
1268 section = self._dwarfinfo.debug_pubnames_sec
1269 else:
1270 namelut = self._dwarfinfo.get_pubtypes()
1271 section = self._dwarfinfo.debug_pubtypes_sec
1272
1273 # readelf prints nothing if the section is not present.
1274 if namelut is None or len(namelut) == 0:
1275 return
1276
1277 self._emitline('Contents of the %s section:' % section.name)
1278 self._emitline()
1279
1280 cu_headers = namelut.get_cu_headers()
1281
1282 # go over CU-by-CU first and item-by-item next.
1283 for (cu_hdr, (cu_ofs, items)) in izip(cu_headers, itertools.groupby(
1284 namelut.items(), key = lambda x: x[1].cu_ofs)):
1285
1286 self._emitline(' Length: %d' % cu_hdr.unit_length)
1287 self._emitline(' Version: %d' % cu_hdr.version)
1288 self._emitline(' Offset into .debug_info section: 0x%x' % cu_hdr.debug_info_offset)
1289 self._emitline(' Size of area in .debug_info section: %d' % cu_hdr.debug_info_length)
1290 self._emitline()
1291 self._emitline(' Offset Name')
1292 for item in items:
1293 self._emitline(' %x %s' % (item[1].die_ofs - cu_ofs, item[0]))
1294 self._emitline()
1295
1296 def _dump_debug_aranges(self):
1297 """ Dump the aranges table
1298 """
1299 aranges_table = self._dwarfinfo.get_aranges()
1300 if aranges_table == None:
1301 return
1302 # seems redundent, but we need to get the unsorted set of entries to match system readelf
1303 unordered_entries = aranges_table._get_entries()
1304
1305 if len(unordered_entries) == 0:
1306 self._emitline()
1307 self._emitline("Section '.debug_aranges' has no debugging data.")
1308 return
1309
1310 self._emitline('Contents of the %s section:' % self._dwarfinfo.debug_aranges_sec.name)
1311 self._emitline()
1312 prev_offset = None
1313 for entry in unordered_entries:
1314 if prev_offset != entry.info_offset:
1315 if entry != unordered_entries[0]:
1316 self._emitline(' %s %s' % (
1317 self._format_hex(0, fullhex=True, lead0x=False),
1318 self._format_hex(0, fullhex=True, lead0x=False)))
1319 self._emitline(' Length: %d' % (entry.unit_length))
1320 self._emitline(' Version: %d' % (entry.version))
1321 self._emitline(' Offset into .debug_info: 0x%x' % (entry.info_offset))
1322 self._emitline(' Pointer Size: %d' % (entry.address_size))
1323 self._emitline(' Segment Size: %d' % (entry.segment_size))
1324 self._emitline()
1325 self._emitline(' Address Length')
1326 self._emitline(' %s %s' % (
1327 self._format_hex(entry.begin_addr, fullhex=True, lead0x=False),
1328 self._format_hex(entry.length, fullhex=True, lead0x=False)))
1329 prev_offset = entry.info_offset
1330 self._emitline(' %s %s' % (
1331 self._format_hex(0, fullhex=True, lead0x=False),
1332 self._format_hex(0, fullhex=True, lead0x=False)))
1333
1334 def _dump_frames_interp_info(self, section, cfi_entries):
1335 """ Dump interpreted (decoded) frame information in a section.
1336
1337 `section` is the Section instance that contains the call frame info
1338 while `cfi_entries` must be an iterable that yields the sequence of
1339 CIE or FDE instances.
1340 """
1341 self._emitline('Contents of the %s section:' % section.name)
1342
1343 for entry in cfi_entries:
1344 if isinstance(entry, CIE):
1345 self._emitline('\n%08x %s %s CIE "%s" cf=%d df=%d ra=%d' % (
1346 entry.offset,
1347 self._format_hex(entry['length'], fullhex=True, lead0x=False),
1348 self._format_hex(entry['CIE_id'], fieldsize=8, lead0x=False),
1349 bytes2str(entry['augmentation']),
1350 entry['code_alignment_factor'],
1351 entry['data_alignment_factor'],
1352 entry['return_address_register']))
1353 ra_regnum = entry['return_address_register']
1354
1355 elif isinstance(entry, FDE):
1356 self._emitline('\n%08x %s %s FDE cie=%08x pc=%s..%s' % (
1357 entry.offset,
1358 self._format_hex(entry['length'], fullhex=True, lead0x=False),
1359 self._format_hex(entry['CIE_pointer'], fieldsize=8, lead0x=False),
1360 entry.cie.offset,
1361 self._format_hex(entry['initial_location'], fullhex=True, lead0x=False),
1362 self._format_hex(entry['initial_location'] + entry['address_range'],
1363 fullhex=True, lead0x=False)))
1364 ra_regnum = entry.cie['return_address_register']
1365
1366 # If the FDE brings adds no unwinding information compared to
1367 # its CIE, omit its table.
1368 if (len(entry.get_decoded().table) ==
1369 len(entry.cie.get_decoded().table)):
1370 continue
1371
1372 else: # ZERO terminator
1373 assert isinstance(entry, ZERO)
1374 self._emitline('\n%08x ZERO terminator' % entry.offset)
1375 continue
1376
1377 # Decode the table.
1378 decoded_table = entry.get_decoded()
1379 if len(decoded_table.table) == 0:
1380 continue
1381
1382 # Print the heading row for the decoded table
1383 self._emit(' LOC')
1384 self._emit(' ' if entry.structs.address_size == 4 else ' ')
1385 self._emit(' CFA ')
1386
1387 # Look at the registers the decoded table describes.
1388 # We build reg_order here to match readelf's order. In particular,
1389 # registers are sorted by their number, and the register matching
1390 # ra_regnum is always listed last with a special heading.
1391 decoded_table = entry.get_decoded()
1392 reg_order = sorted(ifilter(
1393 lambda r: r != ra_regnum,
1394 decoded_table.reg_order))
1395 if len(decoded_table.reg_order):
1396
1397 # Headings for the registers
1398 for regnum in reg_order:
1399 self._emit('%-6s' % describe_reg_name(regnum))
1400 self._emitline('ra ')
1401
1402 # Now include ra_regnum in reg_order to print its values
1403 # similarly to the other registers.
1404 reg_order.append(ra_regnum)
1405 else:
1406 self._emitline()
1407
1408 for line in decoded_table.table:
1409 self._emit(self._format_hex(
1410 line['pc'], fullhex=True, lead0x=False))
1411
1412 if line['cfa'] is not None:
1413 s = describe_CFI_CFA_rule(line['cfa'])
1414 else:
1415 s = 'u'
1416 self._emit(' %-9s' % s)
1417
1418 for regnum in reg_order:
1419 if regnum in line:
1420 s = describe_CFI_register_rule(line[regnum])
1421 else:
1422 s = 'u'
1423 self._emit('%-6s' % s)
1424 self._emitline()
1425 self._emitline()
1426
1427 def _dump_debug_frames_interp(self):
1428 """ Dump the interpreted (decoded) frame information from .debug_frame
1429 and .eh_frame sections.
1430 """
1431 if self._dwarfinfo.has_EH_CFI():
1432 self._dump_frames_interp_info(
1433 self._dwarfinfo.eh_frame_sec,
1434 self._dwarfinfo.EH_CFI_entries())
1435 self._emitline()
1436
1437 if self._dwarfinfo.has_CFI():
1438 self._dump_frames_interp_info(
1439 self._dwarfinfo.debug_frame_sec,
1440 self._dwarfinfo.CFI_entries())
1441
1442 def _dump_debug_locations(self):
1443 """ Dump the location lists from .debug_loc/.debug_loclists section
1444 """
1445 di = self._dwarfinfo
1446 loc_lists = di.location_lists()
1447 if not loc_lists: # No locations section - readelf outputs nothing
1448 return
1449
1450 loc_lists = list(loc_lists.iter_location_lists())
1451 if len(loc_lists) == 0:
1452 # Present but empty locations section - readelf outputs a message
1453 self._emitline("\nSection '%s' has no debugging data." % (di.debug_loclists_sec or di.debug_loc_sec).name)
1454 return
1455
1456 # To dump a location list, one needs to know the CU.
1457 # Scroll through DIEs once, list the known location list offsets.
1458 # Don't need this CU/DIE scan if all entries are absolute or prefixed by base,
1459 # but let's not optimize for that yet.
1460 cu_map = dict() # Loc list offset => CU
1461 for cu in di.iter_CUs():
1462 for die in cu.iter_DIEs():
1463 for key in die.attributes:
1464 attr = die.attributes[key]
1465 if (LocationParser.attribute_has_location(attr, cu['version']) and
1466 LocationParser._attribute_has_loc_list(attr, cu['version'])):
1467 cu_map[attr.value] = cu
1468
1469 addr_size = di.config.default_address_size # In bytes, 4 or 8
1470 addr_width = addr_size * 2 # In hex digits, 8 or 16
1471 line_template = " %%08x %%0%dx %%0%dx %%s%%s" % (addr_width, addr_width)
1472
1473 self._emitline('Contents of the %s section:\n' % (di.debug_loclists_sec or di.debug_loc_sec).name)
1474 self._emitline(' Offset Begin End Expression')
1475 for loc_list in loc_lists:
1476 in_views = False
1477 has_views = False
1478 base_ip = None
1479 loc_entry_count = 0
1480 cu = None
1481 for entry in loc_list:
1482 if isinstance(entry, LocationViewPair):
1483 has_views = in_views = True
1484 # The "v" before address is conditional in binutils, haven't figured out how
1485 self._emitline(" %08x v%015x v%015x location view pair" % (entry.entry_offset, entry.begin, entry.end))
1486 else:
1487 if in_views:
1488 in_views = False
1489 self._emitline("")
1490
1491 # Need the CU for this loclist, but the map is keyed by the offset
1492 # of the first entry in the loclist. Got to skip the views first.
1493 if cu is None:
1494 cu = cu_map.get(entry.entry_offset, False)
1495 if not cu:
1496 raise ValueError("Location list can't be tracked to a CU")
1497
1498 if isinstance(entry, LocationEntry):
1499 if base_ip is None and not entry.is_absolute:
1500 base_ip = _get_cu_base(cu)
1501
1502 begin_offset = (0 if entry.is_absolute else base_ip) + entry.begin_offset
1503 end_offset = (0 if entry.is_absolute else base_ip) + entry.end_offset
1504 expr = describe_DWARF_expr(entry.loc_expr, cu.structs, cu.cu_offset)
1505 if has_views:
1506 view = loc_list[loc_entry_count]
1507 postfix = ' (start == end)' if entry.begin_offset == entry.end_offset and view.begin == view.end else ''
1508 self._emitline(' %08x v%015x v%015x views at %08x for:' %(
1509 entry.entry_offset,
1510 view.begin,
1511 view.end,
1512 view.entry_offset))
1513 self._emitline(' %016x %016x %s%s' %(
1514 begin_offset,
1515 end_offset,
1516 expr,
1517 postfix))
1518 loc_entry_count += 1
1519 else:
1520 postfix = ' (start == end)' if entry.begin_offset == entry.end_offset else ''
1521 self._emitline(line_template % (
1522 entry.entry_offset,
1523 begin_offset,
1524 end_offset,
1525 expr,
1526 postfix))
1527 elif isinstance(entry, BaseAddressEntry):
1528 base_ip = entry.base_address
1529 self._emitline(" %08x %016x (base address)" % (entry.entry_offset, entry.base_address))
1530
1531 # Pyelftools doesn't store the terminating entry,
1532 # but readelf emits its offset, so this should too.
1533 last = loc_list[-1]
1534 self._emitline(" %08x <End of list>" % (last.entry_offset + last.entry_length))
1535
1536 def _dump_debug_ranges(self):
1537 # TODO: GNU readelf format doesn't need entry_length?
1538 di = self._dwarfinfo
1539 range_lists = di.range_lists()
1540 if not range_lists: # No ranges section - readelf outputs nothing
1541 return
1542
1543 ver5 = range_lists.version >= 5
1544 range_lists = list(range_lists.iter_range_lists())
1545 if len(range_lists) == 0:
1546 # Present but empty locations section - readelf outputs a message
1547 self._emitline("\nSection '%s' has no debugging data." % (di.debug_rnglists_sec or di.debug_ranges_sec).name)
1548 return
1549
1550 # In order to determine the base address of the range
1551 # We need to know the corresponding CU.
1552 cu_map = {die.attributes['DW_AT_ranges'].value : cu # Range list offset => CU
1553 for cu in di.iter_CUs()
1554 for die in cu.iter_DIEs()
1555 if 'DW_AT_ranges' in die.attributes}
1556
1557 addr_size = di.config.default_address_size # In bytes, 4 or 8
1558 addr_width = addr_size * 2 # In hex digits, 8 or 16
1559 line_template = " %%08x %%0%dx %%0%dx %%s" % (addr_width, addr_width)
1560 base_template = " %%08x %%0%dx (base address)" % (addr_width)
1561
1562 self._emitline('Contents of the %s section:\n' % (di.debug_rnglists_sec or di.debug_ranges_sec).name)
1563 self._emitline(' Offset Begin End')
1564
1565 for range_list in range_lists:
1566 # Weird discrepancy in binutils: for DWARFv5 it outputs entry offset,
1567 # for DWARF<=4 list offset.
1568 first = range_list[0]
1569 base_ip = _get_cu_base(cu_map[first.entry_offset])
1570 for entry in range_list:
1571 if isinstance(entry, RangeEntry):
1572 postfix = ' (start == end)' if entry.begin_offset == entry.end_offset else ''
1573 self._emitline(line_template % (
1574 entry.entry_offset if ver5 else first.entry_offset,
1575 (0 if entry.is_absolute else base_ip) + entry.begin_offset,
1576 (0 if entry.is_absolute else base_ip) + entry.end_offset,
1577 postfix))
1578 elif isinstance(entry, elftools.dwarf.ranges.BaseAddressEntry):
1579 base_ip = entry.base_address
1580 self._emitline(base_template % (
1581 entry.entry_offset if ver5 else first.entry_offset,
1582 entry.base_address))
1583 else:
1584 raise NotImplementedError("Unknown object in a range list")
1585 last = range_list[-1]
1586 self._emitline(' %08x <End of list>' % (last.entry_offset + last.entry_length if ver5 else first.entry_offset))
1587
1588 def _display_arch_specific_arm(self):
1589 """ Display the ARM architecture-specific info contained in the file.
1590 """
1591 attr_sec = self.elffile.get_section_by_name('.ARM.attributes')
1592
1593 for s in attr_sec.iter_subsections():
1594 self._emitline("Attribute Section: %s" % s.header['vendor_name'])
1595 for ss in s.iter_subsubsections():
1596 h_val = "" if ss.header.extra is None else " ".join("%d" % x for x in ss.header.extra)
1597 self._emitline(describe_attr_tag_arm(ss.header.tag, h_val, None))
1598
1599 for attr in ss.iter_attributes():
1600 self._emit(' ')
1601 self._emitline(describe_attr_tag_arm(attr.tag,
1602 attr.value,
1603 attr.extra))
1604
1605 def _emit(self, s=''):
1606 """ Emit an object to output
1607 """
1608 self.output.write(str(s))
1609
1610 def _emitline(self, s=''):
1611 """ Emit an object to output, followed by a newline
1612 """
1613 self.output.write(str(s).rstrip() + '\n')
1614
1615
1616 SCRIPT_DESCRIPTION = 'Display information about the contents of ELF format files'
1617 VERSION_STRING = '%%(prog)s: based on pyelftools %s' % __version__
1618
1619
1620 def main(stream=None):
1621 # parse the command-line arguments and invoke ReadElf
1622 argparser = argparse.ArgumentParser(
1623 usage='usage: %(prog)s [options] <elf-file>',
1624 description=SCRIPT_DESCRIPTION,
1625 add_help=False, # -h is a real option of readelf
1626 prog='readelf.py')
1627 argparser.add_argument('file',
1628 nargs='?', default=None,
1629 help='ELF file to parse')
1630 argparser.add_argument('-v', '--version',
1631 action='version', version=VERSION_STRING)
1632 argparser.add_argument('-d', '--dynamic',
1633 action='store_true', dest='show_dynamic_tags',
1634 help='Display the dynamic section')
1635 argparser.add_argument('-H', '--help',
1636 action='store_true', dest='help',
1637 help='Display this information')
1638 argparser.add_argument('-h', '--file-header',
1639 action='store_true', dest='show_file_header',
1640 help='Display the ELF file header')
1641 argparser.add_argument('-l', '--program-headers', '--segments',
1642 action='store_true', dest='show_program_header',
1643 help='Display the program headers')
1644 argparser.add_argument('-S', '--section-headers', '--sections',
1645 action='store_true', dest='show_section_header',
1646 help="Display the sections' headers")
1647 argparser.add_argument('-e', '--headers',
1648 action='store_true', dest='show_all_headers',
1649 help='Equivalent to: -h -l -S')
1650 argparser.add_argument('-s', '--symbols', '--syms',
1651 action='store_true', dest='show_symbols',
1652 help='Display the symbol table')
1653 argparser.add_argument('-n', '--notes',
1654 action='store_true', dest='show_notes',
1655 help='Display the core notes (if present)')
1656 argparser.add_argument('-r', '--relocs',
1657 action='store_true', dest='show_relocs',
1658 help='Display the relocations (if present)')
1659 argparser.add_argument('-au', '--arm-unwind',
1660 action='store_true', dest='show_arm_unwind',
1661 help='Display the armeabi unwind information (if present)')
1662 argparser.add_argument('-x', '--hex-dump',
1663 action='store', dest='show_hex_dump', metavar='<number|name>',
1664 help='Dump the contents of section <number|name> as bytes')
1665 argparser.add_argument('-p', '--string-dump',
1666 action='store', dest='show_string_dump', metavar='<number|name>',
1667 help='Dump the contents of section <number|name> as strings')
1668 argparser.add_argument('-V', '--version-info',
1669 action='store_true', dest='show_version_info',
1670 help='Display the version sections (if present)')
1671 argparser.add_argument('-A', '--arch-specific',
1672 action='store_true', dest='show_arch_specific',
1673 help='Display the architecture-specific information (if present)')
1674 argparser.add_argument('--debug-dump',
1675 action='store', dest='debug_dump_what', metavar='<what>',
1676 help=(
1677 'Display the contents of DWARF debug sections. <what> can ' +
1678 'one of {info,decodedline,frames,frames-interp,aranges,pubtypes,pubnames,loc,Ranges}'))
1679 argparser.add_argument('--traceback',
1680 action='store_true', dest='show_traceback',
1681 help='Dump the Python traceback on ELFError'
1682 ' exceptions from elftools')
1683
1684 args = argparser.parse_args()
1685
1686 if args.help or not args.file:
1687 argparser.print_help()
1688 sys.exit(0)
1689
1690 if args.show_all_headers:
1691 do_file_header = do_section_header = do_program_header = True
1692 else:
1693 do_file_header = args.show_file_header
1694 do_section_header = args.show_section_header
1695 do_program_header = args.show_program_header
1696
1697 with open(args.file, 'rb') as file:
1698 try:
1699 readelf = ReadElf(file, stream or sys.stdout)
1700 if do_file_header:
1701 readelf.display_file_header()
1702 if do_section_header:
1703 readelf.display_section_headers(
1704 show_heading=not do_file_header)
1705 if do_program_header:
1706 readelf.display_program_headers(
1707 show_heading=not do_file_header)
1708 if args.show_dynamic_tags:
1709 readelf.display_dynamic_tags()
1710 if args.show_symbols:
1711 readelf.display_symbol_tables()
1712 if args.show_notes:
1713 readelf.display_notes()
1714 if args.show_relocs:
1715 readelf.display_relocations()
1716 if args.show_arm_unwind:
1717 readelf.display_arm_unwind()
1718 if args.show_version_info:
1719 readelf.display_version_info()
1720 if args.show_arch_specific:
1721 readelf.display_arch_specific()
1722 if args.show_hex_dump:
1723 readelf.display_hex_dump(args.show_hex_dump)
1724 if args.show_string_dump:
1725 readelf.display_string_dump(args.show_string_dump)
1726 if args.debug_dump_what:
1727 readelf.display_debug_dump(args.debug_dump_what)
1728 except ELFError as ex:
1729 sys.stdout.flush()
1730 sys.stderr.write('ELF error: %s\n' % ex)
1731 if args.show_traceback:
1732 traceback.print_exc()
1733 sys.exit(1)
1734
1735
1736 def profile_main():
1737 # Run 'main' redirecting its output to readelfout.txt
1738 # Saves profiling information in readelf.profile
1739 PROFFILE = 'readelf.profile'
1740 import cProfile
1741 cProfile.run('main(open("readelfout.txt", "w"))', PROFFILE)
1742
1743 # Dig in some profiling stats
1744 import pstats
1745 p = pstats.Stats(PROFFILE)
1746 p.sort_stats('cumulative').print_stats(25)
1747
1748
1749 #-------------------------------------------------------------------------------
1750 if __name__ == '__main__':
1751 main()
1752 #profile_main()