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