c298aa28466f2f0e9e61be15eb45f152f274f9de
[pyelftools.git] / scripts / readelf.py
1 #!/usr/bin/env python
2 #-------------------------------------------------------------------------------
3 # scripts/readelf.py
4 #
5 # A clone of 'readelf' in Python, based on the pyelftools library
6 #
7 # Eli Bendersky (eliben@gmail.com)
8 # This code is in the public domain
9 #-------------------------------------------------------------------------------
10 import argparse
11 import os, sys
12 import string
13 import traceback
14 import itertools
15 # Note: zip has different behaviour between Python 2.x and 3.x.
16 # - Using izip ensures compatibility.
17 try:
18 from itertools import izip
19 except:
20 izip = zip
21
22 # For running from development directory. It should take precedence over the
23 # installed pyelftools.
24 sys.path.insert(0, '.')
25
26
27 from elftools import __version__
28 from elftools.common.exceptions import ELFError
29 from elftools.common.py3compat import (
30 ifilter, byte2int, bytes2str, itervalues, str2bytes, iterbytes)
31 from elftools.elf.elffile import ELFFile
32 from elftools.elf.dynamic import DynamicSection, DynamicSegment
33 from elftools.elf.enums import ENUM_D_TAG
34 from elftools.elf.segments import InterpSegment
35 from elftools.elf.sections import (
36 NoteSection, SymbolTableSection, SymbolTableIndexSection
37 )
38 from elftools.elf.gnuversions import (
39 GNUVerSymSection, GNUVerDefSection,
40 GNUVerNeedSection,
41 )
42 from elftools.elf.relocation import RelocationSection
43 from elftools.elf.descriptions import (
44 describe_ei_class, describe_ei_data, describe_ei_version,
45 describe_ei_osabi, describe_e_type, describe_e_machine,
46 describe_e_version_numeric, describe_p_type, describe_p_flags,
47 describe_rh_flags, describe_sh_type, describe_sh_flags,
48 describe_symbol_type, describe_symbol_bind, describe_symbol_visibility,
49 describe_symbol_shndx, describe_reloc_type, describe_dyn_tag,
50 describe_dt_flags, describe_dt_flags_1, describe_ver_flags, describe_note,
51 describe_attr_tag_arm
52 )
53 from elftools.elf.constants import E_FLAGS
54 from elftools.elf.constants import E_FLAGS_MASKS
55 from elftools.elf.constants import SH_FLAGS
56 from elftools.elf.constants import SHN_INDICES
57 from elftools.dwarf.dwarfinfo import DWARFInfo
58 from elftools.dwarf.descriptions import (
59 describe_reg_name, describe_attr_value, set_global_machine_arch,
60 describe_CFI_instructions, describe_CFI_register_rule,
61 describe_CFI_CFA_rule, describe_DWARF_expr
62 )
63 from elftools.dwarf.constants import (
64 DW_LNS_copy, DW_LNS_set_file, DW_LNE_define_file)
65 from elftools.dwarf.locationlists import LocationParser, LocationEntry
66 from elftools.dwarf.callframe import CIE, FDE, ZERO
67 from elftools.ehabi.ehabiinfo import CorruptEHABIEntry, CannotUnwindEHABIEntry, GenericEHABIEntry
68
69
70 class 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
1500 SCRIPT_DESCRIPTION = 'Display information about the contents of ELF format files'
1501 VERSION_STRING = '%%(prog)s: based on pyelftools %s' % __version__
1502
1503
1504 def 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
1620 def 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 #-------------------------------------------------------------------------------
1634 if __name__ == '__main__':
1635 main()
1636 #profile_main()