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