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