Remove Table from GNU version sections class name
[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 os, sys
11 from optparse import OptionParser
12 import string
13
14 # For running from development directory. It should take precedence over the
15 # installed pyelftools.
16 sys.path.insert(0, '.')
17
18
19 from elftools import __version__
20 from elftools.common.exceptions import ELFError
21 from elftools.common.py3compat import (
22 ifilter, byte2int, bytes2str, itervalues, str2bytes)
23 from elftools.elf.elffile import ELFFile
24 from elftools.elf.dynamic import DynamicSection, DynamicSegment
25 from elftools.elf.enums import ENUM_D_TAG
26 from elftools.elf.segments import InterpSegment
27 from elftools.elf.sections import SymbolTableSection
28 from elftools.elf.gnuversions import (
29 GNUVerSymSection, GNUVerDefSection,
30 GNUVerNeedSection,
31 )
32 from elftools.elf.relocation import RelocationSection
33 from elftools.elf.descriptions import (
34 describe_ei_class, describe_ei_data, describe_ei_version,
35 describe_ei_osabi, describe_e_type, describe_e_machine,
36 describe_e_version_numeric, describe_p_type, describe_p_flags,
37 describe_sh_type, describe_sh_flags,
38 describe_symbol_type, describe_symbol_bind, describe_symbol_visibility,
39 describe_symbol_shndx, describe_reloc_type, describe_dyn_tag,
40 describe_ver_flags,
41 )
42 from elftools.dwarf.dwarfinfo import DWARFInfo
43 from elftools.dwarf.descriptions import (
44 describe_reg_name, describe_attr_value, set_global_machine_arch,
45 describe_CFI_instructions, describe_CFI_register_rule,
46 describe_CFI_CFA_rule,
47 )
48 from elftools.dwarf.constants import (
49 DW_LNS_copy, DW_LNS_set_file, DW_LNE_define_file)
50 from elftools.dwarf.callframe import CIE, FDE
51
52
53 class ReadElf(object):
54 """ display_* methods are used to emit output into the output stream
55 """
56 def __init__(self, file, output):
57 """ file:
58 stream object with the ELF file to read
59
60 output:
61 output stream to write to
62 """
63 self.elffile = ELFFile(file)
64 self.output = output
65
66 # Lazily initialized if a debug dump is requested
67 self._dwarfinfo = None
68
69 self._versioninfo = None
70
71 def display_file_header(self):
72 """ Display the ELF file header
73 """
74 self._emitline('ELF Header:')
75 self._emit(' Magic: ')
76 self._emitline(' '.join('%2.2x' % byte2int(b)
77 for b in self.elffile.e_ident_raw))
78 header = self.elffile.header
79 e_ident = header['e_ident']
80 self._emitline(' Class: %s' %
81 describe_ei_class(e_ident['EI_CLASS']))
82 self._emitline(' Data: %s' %
83 describe_ei_data(e_ident['EI_DATA']))
84 self._emitline(' Version: %s' %
85 describe_ei_version(e_ident['EI_VERSION']))
86 self._emitline(' OS/ABI: %s' %
87 describe_ei_osabi(e_ident['EI_OSABI']))
88 self._emitline(' ABI Version: %d' %
89 e_ident['EI_ABIVERSION'])
90 self._emitline(' Type: %s' %
91 describe_e_type(header['e_type']))
92 self._emitline(' Machine: %s' %
93 describe_e_machine(header['e_machine']))
94 self._emitline(' Version: %s' %
95 describe_e_version_numeric(header['e_version']))
96 self._emitline(' Entry point address: %s' %
97 self._format_hex(header['e_entry']))
98 self._emit(' Start of program headers: %s' %
99 header['e_phoff'])
100 self._emitline(' (bytes into file)')
101 self._emit(' Start of section headers: %s' %
102 header['e_shoff'])
103 self._emitline(' (bytes into file)')
104 self._emitline(' Flags: %s' %
105 self._format_hex(header['e_flags']))
106 self._emitline(' Size of this header: %s (bytes)' %
107 header['e_ehsize'])
108 self._emitline(' Size of program headers: %s (bytes)' %
109 header['e_phentsize'])
110 self._emitline(' Number of program headers: %s' %
111 header['e_phnum'])
112 self._emitline(' Size of section headers: %s (bytes)' %
113 header['e_shentsize'])
114 self._emitline(' Number of section headers: %s' %
115 header['e_shnum'])
116 self._emitline(' Section header string table index: %s' %
117 header['e_shstrndx'])
118
119 def display_program_headers(self, show_heading=True):
120 """ Display the ELF program headers.
121 If show_heading is True, displays the heading for this information
122 (Elf file type is...)
123 """
124 self._emitline()
125 if self.elffile.num_segments() == 0:
126 self._emitline('There are no program headers in this file.')
127 return
128
129 elfheader = self.elffile.header
130 if show_heading:
131 self._emitline('Elf file type is %s' %
132 describe_e_type(elfheader['e_type']))
133 self._emitline('Entry point is %s' %
134 self._format_hex(elfheader['e_entry']))
135 # readelf weirness - why isn't e_phoff printed as hex? (for section
136 # headers, it is...)
137 self._emitline('There are %s program headers, starting at offset %s' % (
138 elfheader['e_phnum'], elfheader['e_phoff']))
139 self._emitline()
140
141 self._emitline('Program Headers:')
142
143 # Now comes the table of program headers with their attributes. Note
144 # that due to different formatting constraints of 32-bit and 64-bit
145 # addresses, there are some conditions on elfclass here.
146 #
147 # First comes the table heading
148 #
149 if self.elffile.elfclass == 32:
150 self._emitline(' Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align')
151 else:
152 self._emitline(' Type Offset VirtAddr PhysAddr')
153 self._emitline(' FileSiz MemSiz Flags Align')
154
155 # Now the entries
156 #
157 for segment in self.elffile.iter_segments():
158 self._emit(' %-14s ' % describe_p_type(segment['p_type']))
159
160 if self.elffile.elfclass == 32:
161 self._emitline('%s %s %s %s %s %-3s %s' % (
162 self._format_hex(segment['p_offset'], fieldsize=6),
163 self._format_hex(segment['p_vaddr'], fullhex=True),
164 self._format_hex(segment['p_paddr'], fullhex=True),
165 self._format_hex(segment['p_filesz'], fieldsize=5),
166 self._format_hex(segment['p_memsz'], fieldsize=5),
167 describe_p_flags(segment['p_flags']),
168 self._format_hex(segment['p_align'])))
169 else: # 64
170 self._emitline('%s %s %s' % (
171 self._format_hex(segment['p_offset'], fullhex=True),
172 self._format_hex(segment['p_vaddr'], fullhex=True),
173 self._format_hex(segment['p_paddr'], fullhex=True)))
174 self._emitline(' %s %s %-3s %s' % (
175 self._format_hex(segment['p_filesz'], fullhex=True),
176 self._format_hex(segment['p_memsz'], fullhex=True),
177 describe_p_flags(segment['p_flags']),
178 # lead0x set to False for p_align, to mimic readelf.
179 # No idea why the difference from 32-bit mode :-|
180 self._format_hex(segment['p_align'], lead0x=False)))
181
182 if isinstance(segment, InterpSegment):
183 self._emitline(' [Requesting program interpreter: %s]' %
184 bytes2str(segment.get_interp_name()))
185
186 # Sections to segments mapping
187 #
188 if self.elffile.num_sections() == 0:
189 # No sections? We're done
190 return
191
192 self._emitline('\n Section to Segment mapping:')
193 self._emitline(' Segment Sections...')
194
195 for nseg, segment in enumerate(self.elffile.iter_segments()):
196 self._emit(' %2.2d ' % nseg)
197
198 for section in self.elffile.iter_sections():
199 if ( not section.is_null() and
200 segment.section_in_segment(section)):
201 self._emit('%s ' % bytes2str(section.name))
202
203 self._emitline('')
204
205 def display_section_headers(self, show_heading=True):
206 """ Display the ELF section headers
207 """
208 elfheader = self.elffile.header
209 if show_heading:
210 self._emitline('There are %s section headers, starting at offset %s' % (
211 elfheader['e_shnum'], self._format_hex(elfheader['e_shoff'])))
212
213 self._emitline('\nSection Header%s:' % (
214 's' if elfheader['e_shnum'] > 1 else ''))
215
216 # Different formatting constraints of 32-bit and 64-bit addresses
217 #
218 if self.elffile.elfclass == 32:
219 self._emitline(' [Nr] Name Type Addr Off Size ES Flg Lk Inf Al')
220 else:
221 self._emitline(' [Nr] Name Type Address Offset')
222 self._emitline(' Size EntSize Flags Link Info Align')
223
224 # Now the entries
225 #
226 for nsec, section in enumerate(self.elffile.iter_sections()):
227 self._emit(' [%2u] %-17.17s %-15.15s ' % (
228 nsec, bytes2str(section.name), describe_sh_type(section['sh_type'])))
229
230 if self.elffile.elfclass == 32:
231 self._emitline('%s %s %s %s %3s %2s %3s %2s' % (
232 self._format_hex(section['sh_addr'], fieldsize=8, lead0x=False),
233 self._format_hex(section['sh_offset'], fieldsize=6, lead0x=False),
234 self._format_hex(section['sh_size'], fieldsize=6, lead0x=False),
235 self._format_hex(section['sh_entsize'], fieldsize=2, lead0x=False),
236 describe_sh_flags(section['sh_flags']),
237 section['sh_link'], section['sh_info'],
238 section['sh_addralign']))
239 else: # 64
240 self._emitline(' %s %s' % (
241 self._format_hex(section['sh_addr'], fullhex=True, lead0x=False),
242 self._format_hex(section['sh_offset'],
243 fieldsize=16 if section['sh_offset'] > 0xffffffff else 8,
244 lead0x=False)))
245 self._emitline(' %s %s %3s %2s %3s %s' % (
246 self._format_hex(section['sh_size'], fullhex=True, lead0x=False),
247 self._format_hex(section['sh_entsize'], fullhex=True, lead0x=False),
248 describe_sh_flags(section['sh_flags']),
249 section['sh_link'], section['sh_info'],
250 section['sh_addralign']))
251
252 self._emitline('Key to Flags:')
253 self._emit(' W (write), A (alloc), X (execute), M (merge), S (strings)')
254 if self.elffile['e_machine'] in ('EM_X86_64', 'EM_L10M'):
255 self._emitline(', l (large)')
256 else:
257 self._emitline()
258 self._emitline(' I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)')
259 self._emitline(' O (extra OS processing required) o (OS specific), p (processor specific)')
260
261 def display_symbol_tables(self):
262 """ Display the symbol tables contained in the file
263 """
264 self._init_versioninfo()
265
266 for section in self.elffile.iter_sections():
267 if not isinstance(section, SymbolTableSection):
268 continue
269
270 if section['sh_entsize'] == 0:
271 self._emitline("\nSymbol table '%s' has a sh_entsize of zero!" % (
272 bytes2str(section.name)))
273 continue
274
275 self._emitline("\nSymbol table '%s' contains %s entries:" % (
276 bytes2str(section.name), section.num_symbols()))
277
278 if self.elffile.elfclass == 32:
279 self._emitline(' Num: Value Size Type Bind Vis Ndx Name')
280 else: # 64
281 self._emitline(' Num: Value Size Type Bind Vis Ndx Name')
282
283 for nsym, symbol in enumerate(section.iter_symbols()):
284
285 version_info = ''
286 # readelf doesn't display version info for Solaris versioning
287 if (section['sh_type'] == 'SHT_DYNSYM' and
288 self._versioninfo['type'] == 'GNU'):
289 version = self._symbol_version(nsym)
290 if (version['name'] != bytes2str(symbol.name) and
291 version['index'] not in ('VER_NDX_LOCAL',
292 'VER_NDX_GLOBAL')):
293 if version['filename']:
294 # external symbol
295 version_info = '@%(name)s (%(index)i)' % version
296 else:
297 # internal symbol
298 if version['hidden']:
299 version_info = '@%(name)s' % version
300 else:
301 version_info = '@@%(name)s' % version
302
303 # symbol names are truncated to 25 chars, similarly to readelf
304 self._emitline('%6d: %s %5d %-7s %-6s %-7s %4s %.25s%s' % (
305 nsym,
306 self._format_hex(
307 symbol['st_value'], fullhex=True, lead0x=False),
308 symbol['st_size'],
309 describe_symbol_type(symbol['st_info']['type']),
310 describe_symbol_bind(symbol['st_info']['bind']),
311 describe_symbol_visibility(symbol['st_other']['visibility']),
312 describe_symbol_shndx(symbol['st_shndx']),
313 bytes2str(symbol.name),
314 version_info))
315
316 def display_dynamic_tags(self):
317 """ Display the dynamic tags contained in the file
318 """
319 for section in self.elffile.iter_sections():
320 if not isinstance(section, DynamicSection):
321 continue
322
323 self._emitline("\nDynamic section at offset %s contains %s entries:" % (
324 self._format_hex(section['sh_offset']),
325 section.num_tags()))
326 self._emitline(" Tag Type Name/Value")
327
328 padding = 20 + (8 if self.elffile.elfclass == 32 else 0)
329 for tag in section.iter_tags():
330 if tag.entry.d_tag == 'DT_NEEDED':
331 parsed = 'Shared library: [%s]' % bytes2str(tag.needed)
332 elif tag.entry.d_tag == 'DT_RPATH':
333 parsed = 'Library rpath: [%s]' % bytes2str(tag.rpath)
334 elif tag.entry.d_tag == 'DT_RUNPATH':
335 parsed = 'Library runpath: [%s]' % bytes2str(tag.runpath)
336 elif tag.entry.d_tag == 'DT_SONAME':
337 parsed = 'Library soname: [%s]' % bytes2str(tag.soname)
338 elif (tag.entry.d_tag.endswith('SZ') or
339 tag.entry.d_tag.endswith('ENT')):
340 parsed = '%i (bytes)' % tag['d_val']
341 elif (tag.entry.d_tag.endswith('NUM') or
342 tag.entry.d_tag.endswith('COUNT')):
343 parsed = '%i' % tag['d_val']
344 elif tag.entry.d_tag == 'DT_PLTREL':
345 s = describe_dyn_tag(tag.entry.d_val)
346 if s.startswith('DT_'):
347 s = s[3:]
348 parsed = '%s' % s
349 else:
350 parsed = '%#x' % tag['d_val']
351
352 self._emitline(" %s %-*s %s" % (
353 self._format_hex(ENUM_D_TAG.get(tag.entry.d_tag, tag.entry.d_tag),
354 fullhex=True, lead0x=True),
355 padding,
356 '(%s)' % (tag.entry.d_tag[3:],),
357 parsed))
358
359 def display_relocations(self):
360 """ Display the relocations contained in the file
361 """
362 has_relocation_sections = False
363 for section in self.elffile.iter_sections():
364 if not isinstance(section, RelocationSection):
365 continue
366
367 has_relocation_sections = True
368 self._emitline("\nRelocation section '%s' at offset %s contains %s entries:" % (
369 bytes2str(section.name),
370 self._format_hex(section['sh_offset']),
371 section.num_relocations()))
372 if section.is_RELA():
373 self._emitline(" Offset Info Type Sym. Value Sym. Name + Addend")
374 else:
375 self._emitline(" Offset Info Type Sym.Value Sym. Name")
376
377 # The symbol table section pointed to in sh_link
378 symtable = self.elffile.get_section(section['sh_link'])
379
380 for rel in section.iter_relocations():
381 hexwidth = 8 if self.elffile.elfclass == 32 else 12
382 self._emit('%s %s %-17.17s' % (
383 self._format_hex(rel['r_offset'],
384 fieldsize=hexwidth, lead0x=False),
385 self._format_hex(rel['r_info'],
386 fieldsize=hexwidth, lead0x=False),
387 describe_reloc_type(
388 rel['r_info_type'], self.elffile)))
389
390 if rel['r_info_sym'] == 0:
391 self._emitline()
392 continue
393
394 symbol = symtable.get_symbol(rel['r_info_sym'])
395 # Some symbols have zero 'st_name', so instead what's used is
396 # the name of the section they point at
397 if symbol['st_name'] == 0:
398 symsec = self.elffile.get_section(symbol['st_shndx'])
399 symbol_name = symsec.name
400 else:
401 symbol_name = symbol.name
402 self._emit(' %s %s%22.22s' % (
403 self._format_hex(
404 symbol['st_value'],
405 fullhex=True, lead0x=False),
406 ' ' if self.elffile.elfclass == 32 else '',
407 bytes2str(symbol_name)))
408 if section.is_RELA():
409 self._emit(' %s %x' % (
410 '+' if rel['r_addend'] >= 0 else '-',
411 abs(rel['r_addend'])))
412 self._emitline()
413
414 if not has_relocation_sections:
415 self._emitline('\nThere are no relocations in this file.')
416
417 def display_version_info(self):
418 """ Display the version info contained in the file
419 """
420 self._init_versioninfo()
421
422 if not self._versioninfo['type']:
423 self._emitline("\nNo version information found in this file.")
424 return
425
426 for section in self.elffile.iter_sections():
427 if isinstance(section, GNUVerSymSection):
428 self._print_version_section_header(
429 section, 'Version symbols', lead0x=False)
430
431 num_symbols = section.num_symbols()
432
433 # Symbol version info are printed four by four entries
434 for idx_by_4 in range(0, num_symbols, 4):
435
436 self._emit(' %03x:' % idx_by_4)
437
438 for idx in range(idx_by_4, min(idx_by_4 + 4, num_symbols)):
439
440 symbol_version = self._symbol_version(idx)
441 if symbol_version['index'] == 'VER_NDX_LOCAL':
442 version_index = 0
443 version_name = '(*local*)'
444 elif symbol_version['index'] == 'VER_NDX_GLOBAL':
445 version_index = 1
446 version_name = '(*global*)'
447 else:
448 version_index = symbol_version['index']
449 version_name = '(%(name)s)' % symbol_version
450
451 visibility = 'h' if symbol_version['hidden'] else ' '
452
453 self._emit('%4x%s%-13s' % (
454 version_index, visibility, version_name))
455
456 self._emitline()
457
458 elif isinstance(section, GNUVerDefSection):
459 self._print_version_section_header(
460 section, 'Version definition', indent=2)
461
462 offset = 0
463 for verdef, verdaux_iter in section.iter_versions():
464 verdaux = next(verdaux_iter)
465
466 name = verdaux.name
467 if verdef['vd_flags']:
468 flags = describe_ver_flags(verdef['vd_flags'])
469 # Mimic exactly the readelf output
470 flags += ' '
471 else:
472 flags = 'none'
473
474 self._emitline(' %s: Rev: %i Flags: %s Index: %i'
475 ' Cnt: %i Name: %s' % (
476 self._format_hex(offset, fieldsize=6,
477 alternate=True),
478 verdef['vd_version'], flags, verdef['vd_ndx'],
479 verdef['vd_cnt'], bytes2str(name)))
480
481 verdaux_offset = (
482 offset + verdef['vd_aux'] + verdaux['vda_next'])
483 for idx, verdaux in enumerate(verdaux_iter, start=1):
484 self._emitline(' %s: Parent %i: %s' %
485 (self._format_hex(verdaux_offset, fieldsize=4),
486 idx, bytes2str(verdaux.name)))
487 verdaux_offset += verdaux['vda_next']
488
489 offset += verdef['vd_next']
490
491 elif isinstance(section, GNUVerNeedSection):
492 self._print_version_section_header(section, 'Version needs')
493
494 offset = 0
495 for verneed, verneed_iter in section.iter_versions():
496
497 self._emitline(' %s: Version: %i File: %s Cnt: %i' % (
498 self._format_hex(offset, fieldsize=6,
499 alternate=True),
500 verneed['vn_version'], bytes2str(verneed.name),
501 verneed['vn_cnt']))
502
503 vernaux_offset = offset + verneed['vn_aux']
504 for idx, vernaux in enumerate(verneed_iter, start=1):
505 if vernaux['vna_flags']:
506 flags = describe_ver_flags(vernaux['vna_flags'])
507 # Mimic exactly the readelf output
508 flags += ' '
509 else:
510 flags = 'none'
511
512 self._emitline(
513 ' %s: Name: %s Flags: %s Version: %i' % (
514 self._format_hex(vernaux_offset, fieldsize=4),
515 bytes2str(vernaux.name), flags,
516 vernaux['vna_other']))
517
518 vernaux_offset += vernaux['vna_next']
519
520 offset += verneed['vn_next']
521
522 def display_hex_dump(self, section_spec):
523 """ Display a hex dump of a section. section_spec is either a section
524 number or a name.
525 """
526 section = self._section_from_spec(section_spec)
527 if section is None:
528 self._emitline("Section '%s' does not exist in the file!" % (
529 section_spec))
530 return
531
532 self._emitline("\nHex dump of section '%s':" % bytes2str(section.name))
533 self._note_relocs_for_section(section)
534 addr = section['sh_addr']
535 data = section.data()
536 dataptr = 0
537
538 while dataptr < len(data):
539 bytesleft = len(data) - dataptr
540 # chunks of 16 bytes per line
541 linebytes = 16 if bytesleft > 16 else bytesleft
542
543 self._emit(' %s ' % self._format_hex(addr, fieldsize=8))
544 for i in range(16):
545 if i < linebytes:
546 self._emit('%2.2x' % byte2int(data[dataptr + i]))
547 else:
548 self._emit(' ')
549 if i % 4 == 3:
550 self._emit(' ')
551
552 for i in range(linebytes):
553 c = data[dataptr + i : dataptr + i + 1]
554 if byte2int(c[0]) >= 32 and byte2int(c[0]) < 0x7f:
555 self._emit(bytes2str(c))
556 else:
557 self._emit(bytes2str(b'.'))
558
559 self._emitline()
560 addr += linebytes
561 dataptr += linebytes
562
563 self._emitline()
564
565 def display_string_dump(self, section_spec):
566 """ Display a strings dump of a section. section_spec is either a
567 section number or a name.
568 """
569 section = self._section_from_spec(section_spec)
570 if section is None:
571 self._emitline("Section '%s' does not exist in the file!" % (
572 section_spec))
573 return
574
575 self._emitline("\nString dump of section '%s':" % bytes2str(section.name))
576
577 found = False
578 data = section.data()
579 dataptr = 0
580
581 while dataptr < len(data):
582 while ( dataptr < len(data) and
583 not (32 <= byte2int(data[dataptr]) <= 127)):
584 dataptr += 1
585
586 if dataptr >= len(data):
587 break
588
589 endptr = dataptr
590 while endptr < len(data) and byte2int(data[endptr]) != 0:
591 endptr += 1
592
593 found = True
594 self._emitline(' [%6x] %s' % (
595 dataptr, bytes2str(data[dataptr:endptr])))
596
597 dataptr = endptr
598
599 if not found:
600 self._emitline(' No strings found in this section.')
601 else:
602 self._emitline()
603
604 def display_debug_dump(self, dump_what):
605 """ Dump a DWARF section
606 """
607 self._init_dwarfinfo()
608 if self._dwarfinfo is None:
609 return
610
611 set_global_machine_arch(self.elffile.get_machine_arch())
612
613 if dump_what == 'info':
614 self._dump_debug_info()
615 elif dump_what == 'decodedline':
616 self._dump_debug_line_programs()
617 elif dump_what == 'frames':
618 self._dump_debug_frames()
619 elif dump_what == 'frames-interp':
620 self._dump_debug_frames_interp()
621 else:
622 self._emitline('debug dump not yet supported for "%s"' % dump_what)
623
624 def _format_hex(self, addr, fieldsize=None, fullhex=False, lead0x=True,
625 alternate=False):
626 """ Format an address into a hexadecimal string.
627
628 fieldsize:
629 Size of the hexadecimal field (with leading zeros to fit the
630 address into. For example with fieldsize=8, the format will
631 be %08x
632 If None, the minimal required field size will be used.
633
634 fullhex:
635 If True, override fieldsize to set it to the maximal size
636 needed for the elfclass
637
638 lead0x:
639 If True, leading 0x is added
640
641 alternate:
642 If True, override lead0x to emulate the alternate
643 hexadecimal form specified in format string with the #
644 character: only non-zero values are prefixed with 0x.
645 This form is used by readelf.
646 """
647 if alternate:
648 if addr == 0:
649 lead0x = False
650 else:
651 lead0x = True
652 fieldsize -= 2
653
654 s = '0x' if lead0x else ''
655 if fullhex:
656 fieldsize = 8 if self.elffile.elfclass == 32 else 16
657 if fieldsize is None:
658 field = '%x'
659 else:
660 field = '%' + '0%sx' % fieldsize
661 return s + field % addr
662
663 def _print_version_section_header(self, version_section, name, lead0x=True,
664 indent=1):
665 """ Print a section header of one version related section (versym,
666 verneed or verdef) with some options to accomodate readelf
667 little differences between each header (e.g. indentation
668 and 0x prefixing).
669 """
670 if hasattr(version_section, 'num_versions'):
671 num_entries = version_section.num_versions()
672 else:
673 num_entries = version_section.num_symbols()
674
675 self._emitline("\n%s section '%s' contains %s entries:" %
676 (name, bytes2str(version_section.name), num_entries))
677 self._emitline('%sAddr: %s Offset: %s Link: %i (%s)' % (
678 ' ' * indent,
679 self._format_hex(
680 version_section['sh_addr'], fieldsize=16, lead0x=lead0x),
681 self._format_hex(
682 version_section['sh_offset'], fieldsize=6, lead0x=True),
683 version_section['sh_link'],
684 bytes2str(
685 self.elffile.get_section(version_section['sh_link']).name)
686 )
687 )
688
689 def _init_versioninfo(self):
690 """ Search and initialize informations about version related sections
691 and the kind of versioning used (GNU or Solaris).
692 """
693 if self._versioninfo is not None:
694 return
695
696 self._versioninfo = {'versym': None, 'verdef': None,
697 'verneed': None, 'type': None}
698
699 for section in self.elffile.iter_sections():
700 if isinstance(section, GNUVerSymSection):
701 self._versioninfo['versym'] = section
702 elif isinstance(section, GNUVerDefSection):
703 self._versioninfo['verdef'] = section
704 elif isinstance(section, GNUVerNeedSection):
705 self._versioninfo['verneed'] = section
706 elif isinstance(section, DynamicSection):
707 for tag in section.iter_tags():
708 if tag['d_tag'] == 'DT_VERSYM':
709 self._versioninfo['type'] = 'GNU'
710 break
711
712 if not self._versioninfo['type'] and (
713 self._versioninfo['verneed'] or self._versioninfo['verdef']):
714 self._versioninfo['type'] = 'Solaris'
715
716 def _symbol_version(self, nsym):
717 """ Return a dict containing information on the
718 or None if no version information is available
719 """
720 self._init_versioninfo()
721
722 symbol_version = dict.fromkeys(('index', 'name', 'filename', 'hidden'))
723
724 if (not self._versioninfo['versym'] or
725 nsym >= self._versioninfo['versym'].num_symbols()):
726 return None
727
728 symbol = self._versioninfo['versym'].get_symbol(nsym)
729 index = symbol.entry['ndx']
730 if not index in ('VER_NDX_LOCAL', 'VER_NDX_GLOBAL'):
731 index = int(index)
732
733 if self._versioninfo['type'] == 'GNU':
734 # In GNU versioning mode, the highest bit is used to
735 # store wether the symbol is hidden or not
736 if index & 0x8000:
737 index &= ~0x8000
738 symbol_version['hidden'] = True
739
740 if (self._versioninfo['verdef'] and
741 index <= self._versioninfo['verdef'].num_versions()):
742 _, verdaux_iter = \
743 self._versioninfo['verdef'].get_version(index)
744 symbol_version['name'] = bytes2str(next(verdaux_iter).name)
745 else:
746 verneed, vernaux = \
747 self._versioninfo['verneed'].get_version(index)
748 symbol_version['name'] = bytes2str(vernaux.name)
749 symbol_version['filename'] = bytes2str(verneed.name)
750
751 symbol_version['index'] = index
752 return symbol_version
753
754 def _section_from_spec(self, spec):
755 """ Retrieve a section given a "spec" (either number or name).
756 Return None if no such section exists in the file.
757 """
758 try:
759 num = int(spec)
760 if num < self.elffile.num_sections():
761 return self.elffile.get_section(num)
762 else:
763 return None
764 except ValueError:
765 # Not a number. Must be a name then
766 return self.elffile.get_section_by_name(str2bytes(spec))
767
768 def _note_relocs_for_section(self, section):
769 """ If there are relocation sections pointing to the givne section,
770 emit a note about it.
771 """
772 for relsec in self.elffile.iter_sections():
773 if isinstance(relsec, RelocationSection):
774 info_idx = relsec['sh_info']
775 if self.elffile.get_section(info_idx) == section:
776 self._emitline(' Note: This section has relocations against it, but these have NOT been applied to this dump.')
777 return
778
779 def _init_dwarfinfo(self):
780 """ Initialize the DWARF info contained in the file and assign it to
781 self._dwarfinfo.
782 Leave self._dwarfinfo at None if no DWARF info was found in the file
783 """
784 if self._dwarfinfo is not None:
785 return
786
787 if self.elffile.has_dwarf_info():
788 self._dwarfinfo = self.elffile.get_dwarf_info()
789 else:
790 self._dwarfinfo = None
791
792 def _dump_debug_info(self):
793 """ Dump the debugging info section.
794 """
795 self._emitline('Contents of the .debug_info section:\n')
796
797 # Offset of the .debug_info section in the stream
798 section_offset = self._dwarfinfo.debug_info_sec.global_offset
799
800 for cu in self._dwarfinfo.iter_CUs():
801 self._emitline(' Compilation Unit @ offset %s:' %
802 self._format_hex(cu.cu_offset))
803 self._emitline(' Length: %s (%s)' % (
804 self._format_hex(cu['unit_length']),
805 '%s-bit' % cu.dwarf_format()))
806 self._emitline(' Version: %s' % cu['version']),
807 self._emitline(' Abbrev Offset: %s' % (
808 self._format_hex(cu['debug_abbrev_offset']))),
809 self._emitline(' Pointer Size: %s' % cu['address_size'])
810
811 # The nesting depth of each DIE within the tree of DIEs must be
812 # displayed. To implement this, a counter is incremented each time
813 # the current DIE has children, and decremented when a null die is
814 # encountered. Due to the way the DIE tree is serialized, this will
815 # correctly reflect the nesting depth
816 #
817 die_depth = 0
818 for die in cu.iter_DIEs():
819 self._emitline(' <%s><%x>: Abbrev Number: %s%s' % (
820 die_depth,
821 die.offset,
822 die.abbrev_code,
823 (' (%s)' % die.tag) if not die.is_null() else ''))
824 if die.is_null():
825 die_depth -= 1
826 continue
827
828 for attr in itervalues(die.attributes):
829 name = attr.name
830 # Unknown attribute values are passed-through as integers
831 if isinstance(name, int):
832 name = 'Unknown AT value: %x' % name
833 self._emitline(' <%2x> %-18s: %s' % (
834 attr.offset,
835 name,
836 describe_attr_value(
837 attr, die, section_offset)))
838
839 if die.has_children:
840 die_depth += 1
841
842 self._emitline()
843
844 def _dump_debug_line_programs(self):
845 """ Dump the (decoded) line programs from .debug_line
846 The programs are dumped in the order of the CUs they belong to.
847 """
848 self._emitline('Decoded dump of debug contents of section .debug_line:\n')
849
850 for cu in self._dwarfinfo.iter_CUs():
851 lineprogram = self._dwarfinfo.line_program_for_CU(cu)
852
853 cu_filename = bytes2str(lineprogram['file_entry'][0].name)
854 if len(lineprogram['include_directory']) > 0:
855 dir_index = lineprogram['file_entry'][0].dir_index
856 if dir_index > 0:
857 dir = lineprogram['include_directory'][dir_index - 1]
858 else:
859 dir = b'.'
860 cu_filename = '%s/%s' % (bytes2str(dir), cu_filename)
861
862 self._emitline('CU: %s:' % cu_filename)
863 self._emitline('File name Line number Starting address')
864
865 # Print each state's file, line and address information. For some
866 # instructions other output is needed to be compatible with
867 # readelf.
868 for entry in lineprogram.get_entries():
869 state = entry.state
870 if state is None:
871 # Special handling for commands that don't set a new state
872 if entry.command == DW_LNS_set_file:
873 file_entry = lineprogram['file_entry'][entry.args[0] - 1]
874 if file_entry.dir_index == 0:
875 # current directory
876 self._emitline('\n./%s:[++]' % (
877 bytes2str(file_entry.name)))
878 else:
879 self._emitline('\n%s/%s:' % (
880 bytes2str(lineprogram['include_directory'][file_entry.dir_index - 1]),
881 bytes2str(file_entry.name)))
882 elif entry.command == DW_LNE_define_file:
883 self._emitline('%s:' % (
884 bytes2str(lineprogram['include_directory'][entry.args[0].dir_index])))
885 elif not state.end_sequence:
886 # readelf doesn't print the state after end_sequence
887 # instructions. I think it's a bug but to be compatible
888 # I don't print them too.
889 self._emitline('%-35s %11d %18s' % (
890 bytes2str(lineprogram['file_entry'][state.file - 1].name),
891 state.line,
892 '0' if state.address == 0 else
893 self._format_hex(state.address)))
894 if entry.command == DW_LNS_copy:
895 # Another readelf oddity...
896 self._emitline()
897
898 def _dump_debug_frames(self):
899 """ Dump the raw frame information from .debug_frame
900 """
901 if not self._dwarfinfo.has_CFI():
902 return
903 self._emitline('Contents of the .debug_frame section:')
904
905 for entry in self._dwarfinfo.CFI_entries():
906 if isinstance(entry, CIE):
907 self._emitline('\n%08x %08x %08x CIE' % (
908 entry.offset, entry['length'], entry['CIE_id']))
909 self._emitline(' Version: %d' % entry['version'])
910 self._emitline(' Augmentation: "%s"' % bytes2str(entry['augmentation']))
911 self._emitline(' Code alignment factor: %u' % entry['code_alignment_factor'])
912 self._emitline(' Data alignment factor: %d' % entry['data_alignment_factor'])
913 self._emitline(' Return address column: %d' % entry['return_address_register'])
914 self._emitline()
915 else: # FDE
916 self._emitline('\n%08x %08x %08x FDE cie=%08x pc=%08x..%08x' % (
917 entry.offset,
918 entry['length'],
919 entry['CIE_pointer'],
920 entry.cie.offset,
921 entry['initial_location'],
922 entry['initial_location'] + entry['address_range']))
923
924 self._emit(describe_CFI_instructions(entry))
925 self._emitline()
926
927 def _dump_debug_frames_interp(self):
928 """ Dump the interpreted (decoded) frame information from .debug_frame
929 """
930 if not self._dwarfinfo.has_CFI():
931 return
932
933 self._emitline('Contents of the .debug_frame section:')
934
935 for entry in self._dwarfinfo.CFI_entries():
936 if isinstance(entry, CIE):
937 self._emitline('\n%08x %08x %08x CIE "%s" cf=%d df=%d ra=%d' % (
938 entry.offset,
939 entry['length'],
940 entry['CIE_id'],
941 bytes2str(entry['augmentation']),
942 entry['code_alignment_factor'],
943 entry['data_alignment_factor'],
944 entry['return_address_register']))
945 ra_regnum = entry['return_address_register']
946 else: # FDE
947 self._emitline('\n%08x %08x %08x FDE cie=%08x pc=%08x..%08x' % (
948 entry.offset,
949 entry['length'],
950 entry['CIE_pointer'],
951 entry.cie.offset,
952 entry['initial_location'],
953 entry['initial_location'] + entry['address_range']))
954 ra_regnum = entry.cie['return_address_register']
955
956 # Print the heading row for the decoded table
957 self._emit(' LOC')
958 self._emit(' ' if entry.structs.address_size == 4 else ' ')
959 self._emit(' CFA ')
960
961 # Decode the table nad look at the registers it describes.
962 # We build reg_order here to match readelf's order. In particular,
963 # registers are sorted by their number, and the register matching
964 # ra_regnum is always listed last with a special heading.
965 decoded_table = entry.get_decoded()
966 reg_order = sorted(ifilter(
967 lambda r: r != ra_regnum,
968 decoded_table.reg_order))
969
970 # Headings for the registers
971 for regnum in reg_order:
972 self._emit('%-6s' % describe_reg_name(regnum))
973 self._emitline('ra ')
974
975 # Now include ra_regnum in reg_order to print its values similarly
976 # to the other registers.
977 reg_order.append(ra_regnum)
978 for line in decoded_table.table:
979 self._emit(self._format_hex(
980 line['pc'], fullhex=True, lead0x=False))
981 self._emit(' %-9s' % describe_CFI_CFA_rule(line['cfa']))
982
983 for regnum in reg_order:
984 if regnum in line:
985 s = describe_CFI_register_rule(line[regnum])
986 else:
987 s = 'u'
988 self._emit('%-6s' % s)
989 self._emitline()
990 self._emitline()
991
992 def _emit(self, s=''):
993 """ Emit an object to output
994 """
995 self.output.write(str(s))
996
997 def _emitline(self, s=''):
998 """ Emit an object to output, followed by a newline
999 """
1000 self.output.write(str(s) + '\n')
1001
1002
1003 SCRIPT_DESCRIPTION = 'Display information about the contents of ELF format files'
1004 VERSION_STRING = '%%prog: based on pyelftools %s' % __version__
1005
1006
1007 def main(stream=None):
1008 # parse the command-line arguments and invoke ReadElf
1009 optparser = OptionParser(
1010 usage='usage: %prog [options] <elf-file>',
1011 description=SCRIPT_DESCRIPTION,
1012 add_help_option=False, # -h is a real option of readelf
1013 prog='readelf.py',
1014 version=VERSION_STRING)
1015 optparser.add_option('-d', '--dynamic',
1016 action='store_true', dest='show_dynamic_tags',
1017 help='Display the dynamic section')
1018 optparser.add_option('-H', '--help',
1019 action='store_true', dest='help',
1020 help='Display this information')
1021 optparser.add_option('-h', '--file-header',
1022 action='store_true', dest='show_file_header',
1023 help='Display the ELF file header')
1024 optparser.add_option('-l', '--program-headers', '--segments',
1025 action='store_true', dest='show_program_header',
1026 help='Display the program headers')
1027 optparser.add_option('-S', '--section-headers', '--sections',
1028 action='store_true', dest='show_section_header',
1029 help="Display the sections' headers")
1030 optparser.add_option('-e', '--headers',
1031 action='store_true', dest='show_all_headers',
1032 help='Equivalent to: -h -l -S')
1033 optparser.add_option('-s', '--symbols', '--syms',
1034 action='store_true', dest='show_symbols',
1035 help='Display the symbol table')
1036 optparser.add_option('-r', '--relocs',
1037 action='store_true', dest='show_relocs',
1038 help='Display the relocations (if present)')
1039 optparser.add_option('-x', '--hex-dump',
1040 action='store', dest='show_hex_dump', metavar='<number|name>',
1041 help='Dump the contents of section <number|name> as bytes')
1042 optparser.add_option('-p', '--string-dump',
1043 action='store', dest='show_string_dump', metavar='<number|name>',
1044 help='Dump the contents of section <number|name> as strings')
1045 optparser.add_option('-V', '--version-info',
1046 action='store_true', dest='show_version_info',
1047 help='Display the version sections (if present)')
1048 optparser.add_option('--debug-dump',
1049 action='store', dest='debug_dump_what', metavar='<what>',
1050 help=(
1051 'Display the contents of DWARF debug sections. <what> can ' +
1052 'one of {info,decodedline,frames,frames-interp}'))
1053
1054 options, args = optparser.parse_args()
1055
1056 if options.help or len(args) == 0:
1057 optparser.print_help()
1058 sys.exit(0)
1059
1060 if options.show_all_headers:
1061 do_file_header = do_section_header = do_program_header = True
1062 else:
1063 do_file_header = options.show_file_header
1064 do_section_header = options.show_section_header
1065 do_program_header = options.show_program_header
1066
1067 with open(args[0], 'rb') as file:
1068 try:
1069 readelf = ReadElf(file, stream or sys.stdout)
1070 if do_file_header:
1071 readelf.display_file_header()
1072 if do_section_header:
1073 readelf.display_section_headers(
1074 show_heading=not do_file_header)
1075 if do_program_header:
1076 readelf.display_program_headers(
1077 show_heading=not do_file_header)
1078 if options.show_dynamic_tags:
1079 readelf.display_dynamic_tags()
1080 if options.show_symbols:
1081 readelf.display_symbol_tables()
1082 if options.show_relocs:
1083 readelf.display_relocations()
1084 if options.show_version_info:
1085 readelf.display_version_info()
1086 if options.show_hex_dump:
1087 readelf.display_hex_dump(options.show_hex_dump)
1088 if options.show_string_dump:
1089 readelf.display_string_dump(options.show_string_dump)
1090 if options.debug_dump_what:
1091 readelf.display_debug_dump(options.debug_dump_what)
1092 except ELFError as ex:
1093 sys.stderr.write('ELF error: %s\n' % ex)
1094 sys.exit(1)
1095
1096
1097 def profile_main():
1098 # Run 'main' redirecting its output to readelfout.txt
1099 # Saves profiling information in readelf.profile
1100 PROFFILE = 'readelf.profile'
1101 import cProfile
1102 cProfile.run('main(open("readelfout.txt", "w"))', PROFFILE)
1103
1104 # Dig in some profiling stats
1105 import pstats
1106 p = pstats.Stats(PROFFILE)
1107 p.sort_stats('cumulative').print_stats(25)
1108
1109
1110 #-------------------------------------------------------------------------------
1111 if __name__ == '__main__':
1112 main()
1113 #profile_main()
1114
1115