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