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