rename Ver(def|need|sym)TableSection to GNUVer(Def|Need|Sym)TableSection
[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 (
28 SymbolTableSection, GNUVerSymTableSection,
29 GNUVerDefTableSection, GNUVerNeedTableSection,
30 )
31 from elftools.elf.relocation import RelocationSection
32 from elftools.elf.descriptions import (
33 describe_ei_class, describe_ei_data, describe_ei_version,
34 describe_ei_osabi, describe_e_type, describe_e_machine,
35 describe_e_version_numeric, describe_p_type, describe_p_flags,
36 describe_sh_type, describe_sh_flags,
37 describe_symbol_type, describe_symbol_bind, describe_symbol_visibility,
38 describe_symbol_shndx, describe_reloc_type, describe_dyn_tag,
39 describe_ver_flags,
40 )
41 from elftools.dwarf.dwarfinfo import DWARFInfo
42 from elftools.dwarf.descriptions import (
43 describe_reg_name, describe_attr_value, set_global_machine_arch,
44 describe_CFI_instructions, describe_CFI_register_rule,
45 describe_CFI_CFA_rule,
46 )
47 from elftools.dwarf.constants import (
48 DW_LNS_copy, DW_LNS_set_file, DW_LNE_define_file)
49 from elftools.dwarf.callframe import CIE, FDE
50
51
52 class ReadElf(object):
53 """ display_* methods are used to emit output into the output stream
54 """
55 def __init__(self, file, output):
56 """ file:
57 stream object with the ELF file to read
58
59 output:
60 output stream to write to
61 """
62 self.elffile = ELFFile(file)
63 self.output = output
64
65 # Lazily initialized if a debug dump is requested
66 self._dwarfinfo = None
67
68 self._versioninfo = None
69
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
276 self._emitline("\nSymbol table '%s' contains %s entries:" % (
277 bytes2str(section.name), section.num_symbols()))
278
279 if self.elffile.elfclass == 32:
280 self._emitline(' Num: Value Size Type Bind Vis Ndx Name')
281 else: # 64
282 self._emitline(' Num: Value Size Type Bind Vis Ndx Name')
283
284 for nsym, symbol in enumerate(section.iter_symbols()):
285
286 version_info = ''
287 # readelf doesn't display version info for Solaris versioning
288 if (section['sh_type'] == 'SHT_DYNSYM' and
289 self._versioninfo['type'] == 'GNU'):
290 version = self._symbol_version(nsym)
291 if (version['name'] != bytes2str(symbol.name) and
292 version['index'] not in ('VER_NDX_LOCAL',
293 'VER_NDX_GLOBAL')):
294 if version['filename']:
295 # external symbol
296 version_info = '@%(name)s (%(index)i)' % version
297 else:
298 # internal symbol
299 if version['hidden']:
300 version_info = '@%(name)s' % version
301 else:
302 version_info = '@@%(name)s' % version
303
304 # symbol names are truncated to 25 chars, similarly to readelf
305 self._emitline('%6d: %s %5d %-7s %-6s %-7s %4s %.25s%s' % (
306 nsym,
307 self._format_hex(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, GNUVerSymTableSection):
428
429 self._print_version_section_header(
430 section, 'Version symbols', lead0x=False)
431
432 num_symbols = section.num_symbols()
433
434 # Symbol version info are printed four by four entries
435 for idx_by_4 in range(0, num_symbols, 4):
436
437 self._emit(' %03x:' % idx_by_4)
438
439 for idx in range(idx_by_4, min(idx_by_4 + 4, num_symbols)):
440
441 symbol_version = self._symbol_version(idx)
442 if symbol_version['index'] == 'VER_NDX_LOCAL':
443 version_index = 0
444 version_name = '(*local*)'
445 elif symbol_version['index'] == 'VER_NDX_GLOBAL':
446 version_index = 1
447 version_name = '(*global*)'
448 else:
449 version_index = symbol_version['index']
450 version_name = '(%(name)s)' % symbol_version
451
452 visibility = 'h' if symbol_version['hidden'] else ' '
453
454 self._emit('%4x%s%-13s' % (
455 version_index, visibility, version_name))
456
457 self._emitline()
458
459 elif isinstance(section, GNUVerDefTableSection):
460
461 self._print_version_section_header(
462 section, 'Version definition', indent=2)
463
464 offset = 0
465 for verdef, verdaux_iter in section.iter_versions():
466 verdaux = next(verdaux_iter)
467
468 name = verdaux.name
469 if verdef['vd_flags']:
470 flags = describe_ver_flags(verdef['vd_flags'])
471 # Mimic exactly the readelf output
472 flags += ' '
473 else:
474 flags = 'none'
475
476 self._emitline(' %s: Rev: %i Flags: %s Index: %i'
477 ' Cnt: %i Name: %s' % (
478 self._format_hex(offset, fieldsize=6,
479 alternate=True),
480 verdef['vd_version'], flags, verdef['vd_ndx'],
481 verdef['vd_cnt'], bytes2str(name)))
482
483 verdaux_offset = (
484 offset + verdef['vd_aux'] + verdaux['vda_next'])
485 for idx, verdaux in enumerate(verdaux_iter, start=1):
486 self._emitline(' %s: Parent %i: %s' %
487 (self._format_hex(verdaux_offset, fieldsize=4),
488 idx, bytes2str(verdaux.name)))
489 verdaux_offset += verdaux['vda_next']
490
491 offset += verdef['vd_next']
492
493 elif isinstance(section, GNUVerNeedTableSection):
494
495 self._print_version_section_header(section, 'Version needs')
496
497 offset = 0
498 for verneed, verneed_iter in section.iter_versions():
499
500 self._emitline(' %s: Version: %i File: %s Cnt: %i' % (
501 self._format_hex(offset, fieldsize=6,
502 alternate=True),
503 verneed['vn_version'], bytes2str(verneed.name),
504 verneed['vn_cnt']))
505
506 vernaux_offset = offset + verneed['vn_aux']
507 for idx, vernaux in enumerate(verneed_iter, start=1):
508 if vernaux['vna_flags']:
509 flags = describe_ver_flags(vernaux['vna_flags'])
510 # Mimic exactly the readelf output
511 flags += ' '
512 else:
513 flags = 'none'
514
515 self._emitline(
516 ' %s: Name: %s Flags: %s Version: %i' % (
517 self._format_hex(vernaux_offset, fieldsize=4),
518 bytes2str(vernaux.name), flags,
519 vernaux['vna_other']))
520
521 vernaux_offset += vernaux['vna_next']
522
523 offset += verneed['vn_next']
524
525 def display_hex_dump(self, section_spec):
526 """ Display a hex dump of a section. section_spec is either a section
527 number or a name.
528 """
529 section = self._section_from_spec(section_spec)
530 if section is None:
531 self._emitline("Section '%s' does not exist in the file!" % (
532 section_spec))
533 return
534
535 self._emitline("\nHex dump of section '%s':" % bytes2str(section.name))
536 self._note_relocs_for_section(section)
537 addr = section['sh_addr']
538 data = section.data()
539 dataptr = 0
540
541 while dataptr < len(data):
542 bytesleft = len(data) - dataptr
543 # chunks of 16 bytes per line
544 linebytes = 16 if bytesleft > 16 else bytesleft
545
546 self._emit(' %s ' % self._format_hex(addr, fieldsize=8))
547 for i in range(16):
548 if i < linebytes:
549 self._emit('%2.2x' % byte2int(data[dataptr + i]))
550 else:
551 self._emit(' ')
552 if i % 4 == 3:
553 self._emit(' ')
554
555 for i in range(linebytes):
556 c = data[dataptr + i : dataptr + i + 1]
557 if byte2int(c[0]) >= 32 and byte2int(c[0]) < 0x7f:
558 self._emit(bytes2str(c))
559 else:
560 self._emit(bytes2str(b'.'))
561
562 self._emitline()
563 addr += linebytes
564 dataptr += linebytes
565
566 self._emitline()
567
568 def display_string_dump(self, section_spec):
569 """ Display a strings dump of a section. section_spec is either a
570 section number or a name.
571 """
572 section = self._section_from_spec(section_spec)
573 if section is None:
574 self._emitline("Section '%s' does not exist in the file!" % (
575 section_spec))
576 return
577
578 self._emitline("\nString dump of section '%s':" % bytes2str(section.name))
579
580 found = False
581 data = section.data()
582 dataptr = 0
583
584 while dataptr < len(data):
585 while ( dataptr < len(data) and
586 not (32 <= byte2int(data[dataptr]) <= 127)):
587 dataptr += 1
588
589 if dataptr >= len(data):
590 break
591
592 endptr = dataptr
593 while endptr < len(data) and byte2int(data[endptr]) != 0:
594 endptr += 1
595
596 found = True
597 self._emitline(' [%6x] %s' % (
598 dataptr, bytes2str(data[dataptr:endptr])))
599
600 dataptr = endptr
601
602 if not found:
603 self._emitline(' No strings found in this section.')
604 else:
605 self._emitline()
606
607 def display_debug_dump(self, dump_what):
608 """ Dump a DWARF section
609 """
610 self._init_dwarfinfo()
611 if self._dwarfinfo is None:
612 return
613
614 set_global_machine_arch(self.elffile.get_machine_arch())
615
616 if dump_what == 'info':
617 self._dump_debug_info()
618 elif dump_what == 'decodedline':
619 self._dump_debug_line_programs()
620 elif dump_what == 'frames':
621 self._dump_debug_frames()
622 elif dump_what == 'frames-interp':
623 self._dump_debug_frames_interp()
624 else:
625 self._emitline('debug dump not yet supported for "%s"' % dump_what)
626
627 def _format_hex(self, addr, fieldsize=None, fullhex=False, lead0x=True,
628 alternate=False):
629 """ Format an address into a hexadecimal string.
630
631 fieldsize:
632 Size of the hexadecimal field (with leading zeros to fit the
633 address into. For example with fieldsize=8, the format will
634 be %08x
635 If None, the minimal required field size will be used.
636
637 fullhex:
638 If True, override fieldsize to set it to the maximal size
639 needed for the elfclass
640
641 lead0x:
642 If True, leading 0x is added
643
644 alternate:
645 If True, override lead0x to emulate the alternate
646 hexadecimal form specified in format string with the #
647 character: only non-zero values are prefixed with 0x.
648 This form is used by readelf.
649 """
650 if alternate:
651 if addr == 0:
652 lead0x = False
653 else:
654 lead0x = True
655 fieldsize -= 2
656
657 s = '0x' if lead0x else ''
658 if fullhex:
659 fieldsize = 8 if self.elffile.elfclass == 32 else 16
660 if fieldsize is None:
661 field = '%x'
662 else:
663 field = '%' + '0%sx' % fieldsize
664 return s + field % addr
665
666
667 def _print_version_section_header(self, version_section, name, lead0x=True, indent=1):
668 """ Print a section header of one version related section (versym, verneed or verdef)
669 with some options to accomodate readelf little differences between each header
670 (e.g. indentation and 0x prefixing).
671 """
672 if hasattr(version_section, 'num_versions'):
673 num_entries = version_section.num_versions()
674 else:
675 num_entries = version_section.num_symbols()
676
677 self._emitline("\n%s section '%s' contains %s entries:" %
678 (name, bytes2str(version_section.name), num_entries))
679 self._emitline('%sAddr: %s Offset: %s Link: %i (%s)' %
680 (' ' * indent,
681 self._format_hex(version_section['sh_addr'], fieldsize=16, lead0x=lead0x),
682 self._format_hex(version_section['sh_offset'], fieldsize=6, lead0x=True),
683 version_section['sh_link'],
684 bytes2str(self.elffile.get_section(version_section['sh_link']).name)))
685
686
687
688 def _init_versioninfo(self):
689 """ Search and initialize informations about version related sections
690 and the kind of versioning used (GNU or Solaris).
691 """
692 if self._versioninfo is not None:
693 return
694
695 self._versioninfo = { 'versym': None, 'verdef': None,
696 'verneed': None, 'type': None }
697
698 for section in self.elffile.iter_sections():
699 if isinstance(section, GNUVerSymTableSection):
700 self._versioninfo['versym'] = section
701 elif isinstance(section, GNUVerDefTableSection):
702 self._versioninfo['verdef'] = section
703 elif isinstance(section, GNUVerNeedTableSection):
704 self._versioninfo['verneed'] = section
705 elif isinstance(section, DynamicSection):
706 for tag in section.iter_tags():
707 if tag['d_tag'] == 'DT_VERSYM':
708 self._versioninfo['type'] = 'GNU'
709 break
710
711 if not self._versioninfo['type'] and (
712 self._versioninfo['verneed'] or self._versioninfo['verdef']):
713 self._versioninfo['type'] = 'Solaris'
714
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
755 def _section_from_spec(self, spec):
756 """ Retrieve a section given a "spec" (either number or name).
757 Return None if no such section exists in the file.
758 """
759 try:
760 num = int(spec)
761 if num < self.elffile.num_sections():
762 return self.elffile.get_section(num)
763 else:
764 return None
765 except ValueError:
766 # Not a number. Must be a name then
767 return self.elffile.get_section_by_name(str2bytes(spec))
768
769 def _note_relocs_for_section(self, section):
770 """ If there are relocation sections pointing to the givne section,
771 emit a note about it.
772 """
773 for relsec in self.elffile.iter_sections():
774 if isinstance(relsec, RelocationSection):
775 info_idx = relsec['sh_info']
776 if self.elffile.get_section(info_idx) == section:
777 self._emitline(' Note: This section has relocations against it, but these have NOT been applied to this dump.')
778 return
779
780 def _init_dwarfinfo(self):
781 """ Initialize the DWARF info contained in the file and assign it to
782 self._dwarfinfo.
783 Leave self._dwarfinfo at None if no DWARF info was found in the file
784 """
785 if self._dwarfinfo is not None:
786 return
787
788 if self.elffile.has_dwarf_info():
789 self._dwarfinfo = self.elffile.get_dwarf_info()
790 else:
791 self._dwarfinfo = None
792
793 def _dump_debug_info(self):
794 """ Dump the debugging info section.
795 """
796 self._emitline('Contents of the .debug_info section:\n')
797
798 # Offset of the .debug_info section in the stream
799 section_offset = self._dwarfinfo.debug_info_sec.global_offset
800
801 for cu in self._dwarfinfo.iter_CUs():
802 self._emitline(' Compilation Unit @ offset %s:' %
803 self._format_hex(cu.cu_offset))
804 self._emitline(' Length: %s (%s)' % (
805 self._format_hex(cu['unit_length']),
806 '%s-bit' % cu.dwarf_format()))
807 self._emitline(' Version: %s' % cu['version']),
808 self._emitline(' Abbrev Offset: %s' % (
809 self._format_hex(cu['debug_abbrev_offset']))),
810 self._emitline(' Pointer Size: %s' % cu['address_size'])
811
812 # The nesting depth of each DIE within the tree of DIEs must be
813 # displayed. To implement this, a counter is incremented each time
814 # the current DIE has children, and decremented when a null die is
815 # encountered. Due to the way the DIE tree is serialized, this will
816 # correctly reflect the nesting depth
817 #
818 die_depth = 0
819 for die in cu.iter_DIEs():
820 self._emitline(' <%s><%x>: Abbrev Number: %s%s' % (
821 die_depth,
822 die.offset,
823 die.abbrev_code,
824 (' (%s)' % die.tag) if not die.is_null() else ''))
825 if die.is_null():
826 die_depth -= 1
827 continue
828
829 for attr in itervalues(die.attributes):
830 name = attr.name
831 # Unknown attribute values are passed-through as integers
832 if isinstance(name, int):
833 name = 'Unknown AT value: %x' % name
834 self._emitline(' <%2x> %-18s: %s' % (
835 attr.offset,
836 name,
837 describe_attr_value(
838 attr, die, section_offset)))
839
840 if die.has_children:
841 die_depth += 1
842
843 self._emitline()
844
845 def _dump_debug_line_programs(self):
846 """ Dump the (decoded) line programs from .debug_line
847 The programs are dumped in the order of the CUs they belong to.
848 """
849 self._emitline('Decoded dump of debug contents of section .debug_line:\n')
850
851 for cu in self._dwarfinfo.iter_CUs():
852 lineprogram = self._dwarfinfo.line_program_for_CU(cu)
853
854 cu_filename = bytes2str(lineprogram['file_entry'][0].name)
855 if len(lineprogram['include_directory']) > 0:
856 dir_index = lineprogram['file_entry'][0].dir_index
857 if dir_index > 0:
858 dir = lineprogram['include_directory'][dir_index - 1]
859 else:
860 dir = b'.'
861 cu_filename = '%s/%s' % (bytes2str(dir), cu_filename)
862
863 self._emitline('CU: %s:' % cu_filename)
864 self._emitline('File name Line number Starting address')
865
866 # Print each state's file, line and address information. For some
867 # instructions other output is needed to be compatible with
868 # readelf.
869 for entry in lineprogram.get_entries():
870 state = entry.state
871 if state is None:
872 # Special handling for commands that don't set a new state
873 if entry.command == DW_LNS_set_file:
874 file_entry = lineprogram['file_entry'][entry.args[0] - 1]
875 if file_entry.dir_index == 0:
876 # current directory
877 self._emitline('\n./%s:[++]' % (
878 bytes2str(file_entry.name)))
879 else:
880 self._emitline('\n%s/%s:' % (
881 bytes2str(lineprogram['include_directory'][file_entry.dir_index - 1]),
882 bytes2str(file_entry.name)))
883 elif entry.command == DW_LNE_define_file:
884 self._emitline('%s:' % (
885 bytes2str(lineprogram['include_directory'][entry.args[0].dir_index])))
886 elif not state.end_sequence:
887 # readelf doesn't print the state after end_sequence
888 # instructions. I think it's a bug but to be compatible
889 # I don't print them too.
890 self._emitline('%-35s %11d %18s' % (
891 bytes2str(lineprogram['file_entry'][state.file - 1].name),
892 state.line,
893 '0' if state.address == 0 else
894 self._format_hex(state.address)))
895 if entry.command == DW_LNS_copy:
896 # Another readelf oddity...
897 self._emitline()
898
899 def _dump_debug_frames(self):
900 """ Dump the raw frame information from .debug_frame
901 """
902 if not self._dwarfinfo.has_CFI():
903 return
904 self._emitline('Contents of the .debug_frame section:')
905
906 for entry in self._dwarfinfo.CFI_entries():
907 if isinstance(entry, CIE):
908 self._emitline('\n%08x %08x %08x CIE' % (
909 entry.offset, entry['length'], entry['CIE_id']))
910 self._emitline(' Version: %d' % entry['version'])
911 self._emitline(' Augmentation: "%s"' % bytes2str(entry['augmentation']))
912 self._emitline(' Code alignment factor: %u' % entry['code_alignment_factor'])
913 self._emitline(' Data alignment factor: %d' % entry['data_alignment_factor'])
914 self._emitline(' Return address column: %d' % entry['return_address_register'])
915 self._emitline()
916 else: # FDE
917 self._emitline('\n%08x %08x %08x FDE cie=%08x pc=%08x..%08x' % (
918 entry.offset,
919 entry['length'],
920 entry['CIE_pointer'],
921 entry.cie.offset,
922 entry['initial_location'],
923 entry['initial_location'] + entry['address_range']))
924
925 self._emit(describe_CFI_instructions(entry))
926 self._emitline()
927
928 def _dump_debug_frames_interp(self):
929 """ Dump the interpreted (decoded) frame information from .debug_frame
930 """
931 if not self._dwarfinfo.has_CFI():
932 return
933
934 self._emitline('Contents of the .debug_frame section:')
935
936 for entry in self._dwarfinfo.CFI_entries():
937 if isinstance(entry, CIE):
938 self._emitline('\n%08x %08x %08x CIE "%s" cf=%d df=%d ra=%d' % (
939 entry.offset,
940 entry['length'],
941 entry['CIE_id'],
942 bytes2str(entry['augmentation']),
943 entry['code_alignment_factor'],
944 entry['data_alignment_factor'],
945 entry['return_address_register']))
946 ra_regnum = entry['return_address_register']
947 else: # FDE
948 self._emitline('\n%08x %08x %08x FDE cie=%08x pc=%08x..%08x' % (
949 entry.offset,
950 entry['length'],
951 entry['CIE_pointer'],
952 entry.cie.offset,
953 entry['initial_location'],
954 entry['initial_location'] + entry['address_range']))
955 ra_regnum = entry.cie['return_address_register']
956
957 # Print the heading row for the decoded table
958 self._emit(' LOC')
959 self._emit(' ' if entry.structs.address_size == 4 else ' ')
960 self._emit(' CFA ')
961
962 # Decode the table nad look at the registers it describes.
963 # We build reg_order here to match readelf's order. In particular,
964 # registers are sorted by their number, and the register matching
965 # ra_regnum is always listed last with a special heading.
966 decoded_table = entry.get_decoded()
967 reg_order = sorted(ifilter(
968 lambda r: r != ra_regnum,
969 decoded_table.reg_order))
970
971 # Headings for the registers
972 for regnum in reg_order:
973 self._emit('%-6s' % describe_reg_name(regnum))
974 self._emitline('ra ')
975
976 # Now include ra_regnum in reg_order to print its values similarly
977 # to the other registers.
978 reg_order.append(ra_regnum)
979 for line in decoded_table.table:
980 self._emit(self._format_hex(
981 line['pc'], fullhex=True, lead0x=False))
982 self._emit(' %-9s' % describe_CFI_CFA_rule(line['cfa']))
983
984 for regnum in reg_order:
985 if regnum in line:
986 s = describe_CFI_register_rule(line[regnum])
987 else:
988 s = 'u'
989 self._emit('%-6s' % s)
990 self._emitline()
991 self._emitline()
992
993 def _emit(self, s=''):
994 """ Emit an object to output
995 """
996 self.output.write(str(s))
997
998 def _emitline(self, s=''):
999 """ Emit an object to output, followed by a newline
1000 """
1001 self.output.write(str(s) + '\n')
1002
1003
1004 SCRIPT_DESCRIPTION = 'Display information about the contents of ELF format files'
1005 VERSION_STRING = '%%prog: based on pyelftools %s' % __version__
1006
1007
1008 def main(stream=None):
1009 # parse the command-line arguments and invoke ReadElf
1010 optparser = OptionParser(
1011 usage='usage: %prog [options] <elf-file>',
1012 description=SCRIPT_DESCRIPTION,
1013 add_help_option=False, # -h is a real option of readelf
1014 prog='readelf.py',
1015 version=VERSION_STRING)
1016 optparser.add_option('-d', '--dynamic',
1017 action='store_true', dest='show_dynamic_tags',
1018 help='Display the dynamic section')
1019 optparser.add_option('-H', '--help',
1020 action='store_true', dest='help',
1021 help='Display this information')
1022 optparser.add_option('-h', '--file-header',
1023 action='store_true', dest='show_file_header',
1024 help='Display the ELF file header')
1025 optparser.add_option('-l', '--program-headers', '--segments',
1026 action='store_true', dest='show_program_header',
1027 help='Display the program headers')
1028 optparser.add_option('-S', '--section-headers', '--sections',
1029 action='store_true', dest='show_section_header',
1030 help="Display the sections' headers")
1031 optparser.add_option('-e', '--headers',
1032 action='store_true', dest='show_all_headers',
1033 help='Equivalent to: -h -l -S')
1034 optparser.add_option('-s', '--symbols', '--syms',
1035 action='store_true', dest='show_symbols',
1036 help='Display the symbol table')
1037 optparser.add_option('-r', '--relocs',
1038 action='store_true', dest='show_relocs',
1039 help='Display the relocations (if present)')
1040 optparser.add_option('-x', '--hex-dump',
1041 action='store', dest='show_hex_dump', metavar='<number|name>',
1042 help='Dump the contents of section <number|name> as bytes')
1043 optparser.add_option('-p', '--string-dump',
1044 action='store', dest='show_string_dump', metavar='<number|name>',
1045 help='Dump the contents of section <number|name> as strings')
1046 optparser.add_option('-V', '--version-info',
1047 action='store_true', dest='show_version_info',
1048 help='Display the version sections (if present)')
1049 optparser.add_option('--debug-dump',
1050 action='store', dest='debug_dump_what', metavar='<what>',
1051 help=(
1052 'Display the contents of DWARF debug sections. <what> can ' +
1053 'one of {info,decodedline,frames,frames-interp}'))
1054
1055 options, args = optparser.parse_args()
1056
1057 if options.help or len(args) == 0:
1058 optparser.print_help()
1059 sys.exit(0)
1060
1061 if options.show_all_headers:
1062 do_file_header = do_section_header = do_program_header = True
1063 else:
1064 do_file_header = options.show_file_header
1065 do_section_header = options.show_section_header
1066 do_program_header = options.show_program_header
1067
1068 with open(args[0], 'rb') as file:
1069 try:
1070 readelf = ReadElf(file, stream or sys.stdout)
1071 if do_file_header:
1072 readelf.display_file_header()
1073 if do_section_header:
1074 readelf.display_section_headers(
1075 show_heading=not do_file_header)
1076 if do_program_header:
1077 readelf.display_program_headers(
1078 show_heading=not do_file_header)
1079 if options.show_dynamic_tags:
1080 readelf.display_dynamic_tags()
1081 if options.show_symbols:
1082 readelf.display_symbol_tables()
1083 if options.show_relocs:
1084 readelf.display_relocations()
1085 if options.show_version_info:
1086 readelf.display_version_info()
1087 if options.show_hex_dump:
1088 readelf.display_hex_dump(options.show_hex_dump)
1089 if options.show_string_dump:
1090 readelf.display_string_dump(options.show_string_dump)
1091 if options.debug_dump_what:
1092 readelf.display_debug_dump(options.debug_dump_what)
1093 except ELFError as ex:
1094 sys.stderr.write('ELF error: %s\n' % ex)
1095 sys.exit(1)
1096
1097
1098 def profile_main():
1099 # Run 'main' redirecting its output to readelfout.txt
1100 # Saves profiling information in readelf.profile
1101 PROFFILE = 'readelf.profile'
1102 import cProfile
1103 cProfile.run('main(open("readelfout.txt", "w"))', PROFFILE)
1104
1105 # Dig in some profiling stats
1106 import pstats
1107 p = pstats.Stats(PROFFILE)
1108 p.sort_stats('cumulative').print_stats(25)
1109
1110
1111 #-------------------------------------------------------------------------------
1112 if __name__ == '__main__':
1113 main()
1114 #profile_main()
1115
1116