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