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