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