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