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