2 #-------------------------------------------------------------------------------
5 # A clone of 'readelf' in Python, based on the pyelftools library
7 # Eli Bendersky (eliben@gmail.com)
8 # This code is in the public domain
9 #-------------------------------------------------------------------------------
11 from optparse
import OptionParser
15 # If elftools is not installed, maybe we're running from the root or scripts
16 # dir of the source distribution
21 sys
.path
.extend(['.', '..'])
23 from elftools
import __version__
24 from elftools
.common
.exceptions
import ELFError
25 from elftools
.elf
.elffile
import ELFFile
26 from elftools
.elf
.segments
import InterpSegment
27 from elftools
.elf
.sections
import SymbolTableSection
28 from elftools
.elf
.relocation
import RelocationSection
29 from elftools
.elf
.descriptions
import (
30 describe_ei_class
, describe_ei_data
, describe_ei_version
,
31 describe_ei_osabi
, describe_e_type
, describe_e_machine
,
32 describe_e_version_numeric
, describe_p_type
, describe_p_flags
,
33 describe_sh_type
, describe_sh_flags
,
34 describe_symbol_type
, describe_symbol_bind
, describe_symbol_visibility
,
35 describe_symbol_shndx
, describe_reloc_type
,
37 from elftools
.dwarf
.dwarfinfo
import DWARFInfo
38 from elftools
.dwarf
.descriptions
import (
39 describe_attr_value
, set_global_machine_arch
)
40 from elftools
.dwarf
.constants
import (
41 DW_LNS_copy
, DW_LNS_set_file
, DW_LNE_define_file
)
44 class ReadElf(object):
45 """ display_* methods are used to emit output into the output stream
47 def __init__(self
, file, output
):
49 stream object with the ELF file to read
52 output stream to write to
54 self
.elffile
= ELFFile(file)
57 # Lazily initialized if a debug dump is requested
58 self
._dwarfinfo
= None
60 def display_file_header(self
):
61 """ Display the ELF file header
63 self
._emitline
('ELF Header:')
64 self
._emit
(' Magic: ')
65 self
._emitline
(' '.join('%2.2x' % ord(b
)
66 for b
in self
.elffile
.e_ident_raw
))
67 header
= self
.elffile
.header
68 e_ident
= header
['e_ident']
69 self
._emitline
(' Class: %s' %
70 describe_ei_class(e_ident
['EI_CLASS']))
71 self
._emitline
(' Data: %s' %
72 describe_ei_data(e_ident
['EI_DATA']))
73 self
._emitline
(' Version: %s' %
74 describe_ei_version(e_ident
['EI_VERSION']))
75 self
._emitline
(' OS/ABI: %s' %
76 describe_ei_osabi(e_ident
['EI_OSABI']))
77 self
._emitline
(' ABI Version: %d' %
78 e_ident
['EI_ABIVERSION'])
79 self
._emitline
(' Type: %s' %
80 describe_e_type(header
['e_type']))
81 self
._emitline
(' Machine: %s' %
82 describe_e_machine(header
['e_machine']))
83 self
._emitline
(' Version: %s' %
84 describe_e_version_numeric(header
['e_version']))
85 self
._emitline
(' Entry point address: %s' %
86 self
._format
_hex
(header
['e_entry']))
87 self
._emit
(' Start of program headers: %s' %
89 self
._emitline
(' (bytes into file)')
90 self
._emit
(' Start of section headers: %s' %
92 self
._emitline
(' (bytes into file)')
93 self
._emitline
(' Flags: %s' %
94 self
._format
_hex
(header
['e_flags']))
95 self
._emitline
(' Size of this header: %s (bytes)' %
97 self
._emitline
(' Size of program headers: %s (bytes)' %
98 header
['e_phentsize'])
99 self
._emitline
(' Number of program headers: %s' %
101 self
._emitline
(' Size of section headers: %s (bytes)' %
102 header
['e_shentsize'])
103 self
._emitline
(' Number of section headers: %s' %
105 self
._emitline
(' Section header string table index: %s' %
106 header
['e_shstrndx'])
108 def display_program_headers(self
, show_heading
=True):
109 """ Display the ELF program headers.
110 If show_heading is True, displays the heading for this information
111 (Elf file type is...)
114 if self
.elffile
.num_segments() == 0:
115 self
._emitline
('There are no program headers in this file.')
118 elfheader
= self
.elffile
.header
120 self
._emitline
('Elf file type is %s' %
121 describe_e_type(elfheader
['e_type']))
122 self
._emitline
('Entry point is %s' %
123 self
._format
_hex
(elfheader
['e_entry']))
124 # readelf weirness - why isn't e_phoff printed as hex? (for section
126 self
._emitline
('There are %s program headers, starting at offset %s' % (
127 elfheader
['e_phnum'], elfheader
['e_phoff']))
130 self
._emitline
('Program Headers:')
132 # Now comes the table of program headers with their attributes. Note
133 # that due to different formatting constraints of 32-bit and 64-bit
134 # addresses, there are some conditions on elfclass here.
136 # First comes the table heading
138 if self
.elffile
.elfclass
== 32:
139 self
._emitline
(' Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align')
141 self
._emitline
(' Type Offset VirtAddr PhysAddr')
142 self
._emitline
(' FileSiz MemSiz Flags Align')
146 for segment
in self
.elffile
.iter_segments():
147 self
._emit
(' %-14s ' % describe_p_type(segment
['p_type']))
149 if self
.elffile
.elfclass
== 32:
150 self
._emitline
('%s %s %s %s %s %-3s %s' % (
151 self
._format
_hex
(segment
['p_offset'], fieldsize
=6),
152 self
._format
_hex
(segment
['p_vaddr'], fullhex
=True),
153 self
._format
_hex
(segment
['p_paddr'], fullhex
=True),
154 self
._format
_hex
(segment
['p_filesz'], fieldsize
=5),
155 self
._format
_hex
(segment
['p_memsz'], fieldsize
=5),
156 describe_p_flags(segment
['p_flags']),
157 self
._format
_hex
(segment
['p_align'])))
159 self
._emitline
('%s %s %s' % (
160 self
._format
_hex
(segment
['p_offset'], fullhex
=True),
161 self
._format
_hex
(segment
['p_vaddr'], fullhex
=True),
162 self
._format
_hex
(segment
['p_paddr'], fullhex
=True)))
163 self
._emitline
(' %s %s %-3s %s' % (
164 self
._format
_hex
(segment
['p_filesz'], fullhex
=True),
165 self
._format
_hex
(segment
['p_memsz'], fullhex
=True),
166 describe_p_flags(segment
['p_flags']),
167 # lead0x set to False for p_align, to mimic readelf.
168 # No idea why the difference from 32-bit mode :-|
169 self
._format
_hex
(segment
['p_align'], lead0x
=False)))
171 if isinstance(segment
, InterpSegment
):
172 self
._emitline
(' [Requesting program interpreter: %s]' %
173 segment
.get_interp_name())
175 # Sections to segments mapping
177 if self
.elffile
.num_sections() == 0:
178 # No sections? We're done
181 self
._emitline
('\n Section to Segment mapping:')
182 self
._emitline
(' Segment Sections...')
184 for nseg
, segment
in enumerate(self
.elffile
.iter_segments()):
185 self
._emit
(' %2.2d ' % nseg
)
187 for section
in self
.elffile
.iter_sections():
188 if ( not section
.is_null() and
189 segment
.section_in_segment(section
)):
190 self
._emit
('%s ' % section
.name
)
194 def display_section_headers(self
, show_heading
=True):
195 """ Display the ELF section headers
197 elfheader
= self
.elffile
.header
199 self
._emitline
('There are %s section headers, starting at offset %s' % (
200 elfheader
['e_shnum'], self
._format
_hex
(elfheader
['e_shoff'])))
202 self
._emitline
('\nSection Header%s:' % (
203 's' if elfheader
['e_shnum'] > 1 else ''))
205 # Different formatting constraints of 32-bit and 64-bit addresses
207 if self
.elffile
.elfclass
== 32:
208 self
._emitline
(' [Nr] Name Type Addr Off Size ES Flg Lk Inf Al')
210 self
._emitline
(' [Nr] Name Type Address Offset')
211 self
._emitline
(' Size EntSize Flags Link Info Align')
215 for nsec
, section
in enumerate(self
.elffile
.iter_sections()):
216 self
._emit
(' [%2u] %-17.17s %-15.15s ' % (
217 nsec
, section
.name
, describe_sh_type(section
['sh_type'])))
219 if self
.elffile
.elfclass
== 32:
220 self
._emitline
('%s %s %s %s %3s %2s %3s %2s' % (
221 self
._format
_hex
(section
['sh_addr'], fieldsize
=8, lead0x
=False),
222 self
._format
_hex
(section
['sh_offset'], fieldsize
=6, lead0x
=False),
223 self
._format
_hex
(section
['sh_size'], fieldsize
=6, lead0x
=False),
224 self
._format
_hex
(section
['sh_entsize'], fieldsize
=2, lead0x
=False),
225 describe_sh_flags(section
['sh_flags']),
226 section
['sh_link'], section
['sh_info'],
227 section
['sh_addralign']))
229 self
._emitline
(' %s %s' % (
230 self
._format
_hex
(section
['sh_addr'], fullhex
=True, lead0x
=False),
231 self
._format
_hex
(section
['sh_offset'],
232 fieldsize
=16 if section
['sh_offset'] > 0xffffffff else 8,
234 self
._emitline
(' %s %s %3s %2s %3s %s' % (
235 self
._format
_hex
(section
['sh_size'], fullhex
=True, lead0x
=False),
236 self
._format
_hex
(section
['sh_entsize'], fullhex
=True, lead0x
=False),
237 describe_sh_flags(section
['sh_flags']),
238 section
['sh_link'], section
['sh_info'],
239 section
['sh_addralign']))
241 self
._emitline
('Key to Flags:')
242 self
._emit
(' W (write), A (alloc), X (execute), M (merge), S (strings)')
243 if self
.elffile
['e_machine'] in ('EM_X86_64', 'EM_L10M'):
244 self
._emitline
(', l (large)')
247 self
._emitline
(' I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)')
248 self
._emitline
(' O (extra OS processing required) o (OS specific), p (processor specific)')
250 def display_symbol_tables(self
):
251 """ Display the symbol tables contained in the file
253 for section
in self
.elffile
.iter_sections():
254 if not isinstance(section
, SymbolTableSection
):
257 if section
['sh_entsize'] == 0:
258 self
._emitline
("\nSymbol table '%s' has a sh_entsize of zero!" % (
262 self
._emitline
("\nSymbol table '%s' contains %s entries:" % (
263 section
.name
, section
.num_symbols()))
265 if self
.elffile
.elfclass
== 32:
266 self
._emitline
(' Num: Value Size Type Bind Vis Ndx Name')
268 self
._emitline
(' Num: Value Size Type Bind Vis Ndx Name')
270 for nsym
, symbol
in enumerate(section
.iter_symbols()):
271 # symbol names are truncated to 25 chars, similarly to readelf
272 self
._emitline
('%6d: %s %5d %-7s %-6s %-7s %4s %.25s' % (
274 self
._format
_hex
(symbol
['st_value'], fullhex
=True, lead0x
=False),
276 describe_symbol_type(symbol
['st_info']['type']),
277 describe_symbol_bind(symbol
['st_info']['bind']),
278 describe_symbol_visibility(symbol
['st_other']['visibility']),
279 describe_symbol_shndx(symbol
['st_shndx']),
282 def display_relocations(self
):
283 """ Display the relocations contained in the file
285 has_relocation_sections
= False
286 for section
in self
.elffile
.iter_sections():
287 if not isinstance(section
, RelocationSection
):
290 has_relocation_sections
= True
291 self
._emitline
("\nRelocation section '%s' at offset %s contains %s entries:" % (
293 self
._format
_hex
(section
['sh_offset']),
294 section
.num_relocations()))
295 if section
.is_RELA():
296 self
._emitline
(" Offset Info Type Sym. Value Sym. Name + Addend")
298 self
._emitline
(" Offset Info Type Sym.Value Sym. Name")
300 # The symbol table section pointed to in sh_link
301 symtable
= self
.elffile
.get_section(section
['sh_link'])
303 for rel
in section
.iter_relocations():
304 hexwidth
= 8 if self
.elffile
.elfclass
== 32 else 12
305 self
._emit
('%s %s %-17.17s' % (
306 self
._format
_hex
(rel
['r_offset'],
307 fieldsize
=hexwidth
, lead0x
=False),
308 self
._format
_hex
(rel
['r_info'],
309 fieldsize
=hexwidth
, lead0x
=False),
311 rel
['r_info_type'], self
.elffile
)))
313 if rel
['r_info_sym'] == 0:
317 symbol
= symtable
.get_symbol(rel
['r_info_sym'])
318 # Some symbols have zero 'st_name', so instead what's used is
319 # the name of the section they point at
320 if symbol
['st_name'] == 0:
321 symsec
= self
.elffile
.get_section(symbol
['st_shndx'])
322 symbol_name
= symsec
.name
324 symbol_name
= symbol
.name
325 self
._emit
(' %s %s%22.22s' % (
328 fullhex
=True, lead0x
=False),
329 ' ' if self
.elffile
.elfclass
== 32 else '',
331 if section
.is_RELA():
332 self
._emit
(' %s %x' % (
333 '+' if rel
['r_addend'] >= 0 else '-',
334 abs(rel
['r_addend'])))
337 if not has_relocation_sections
:
338 self
._emitline
('\nThere are no relocations in this file.')
340 def display_hex_dump(self
, section_spec
):
341 """ Display a hex dump of a section. section_spec is either a section
344 section
= self
._section
_from
_spec
(section_spec
)
346 self
._emitline
("Section '%s' does not exist in the file!" % (
350 self
._emitline
("\nHex dump of section '%s':" % section
.name
)
351 self
._note
_relocs
_for
_section
(section
)
352 addr
= section
['sh_addr']
353 data
= section
.data()
356 while dataptr
< len(data
):
357 bytesleft
= len(data
) - dataptr
358 # chunks of 16 bytes per line
359 linebytes
= 16 if bytesleft
> 16 else bytesleft
361 self
._emit
(' %s ' % self
._format
_hex
(addr
, fieldsize
=8))
364 self
._emit
('%2.2x' % ord(data
[dataptr
+ i
]))
370 for i
in range(linebytes
):
371 c
= data
[dataptr
+ i
]
372 if c
>= ' ' and ord(c
) < 0x7f:
383 def display_string_dump(self
, section_spec
):
384 """ Display a strings dump of a section. section_spec is either a
385 section number or a name.
387 section
= self
._section
_from
_spec
(section_spec
)
389 self
._emitline
("Section '%s' does not exist in the file!" % (
393 printables
= set(string
.printable
)
394 self
._emitline
("\nString dump of section '%s':" % section
.name
)
397 data
= section
.data()
400 while dataptr
< len(data
):
401 while dataptr
< len(data
) and data
[dataptr
] not in printables
:
404 if dataptr
>= len(data
):
408 while endptr
< len(data
) and data
[endptr
] != '\x00':
412 self
._emitline
(' [%6x] %s' % (
413 dataptr
, data
[dataptr
:endptr
]))
418 self
._emitline
(' No strings found in this section.')
422 def display_debug_dump(self
, dump_what
):
423 """ Dump a DWARF section
425 self
._init
_dwarfinfo
()
426 if self
._dwarfinfo
is None:
429 set_global_machine_arch(self
.elffile
.get_machine_arch())
431 if dump_what
== 'info':
432 self
._dump
_debug
_info
()
433 elif dump_what
== 'decodedline':
434 self
._dump
_debug
_line
_programs
()
436 self
._emitline
('debug dump not yet supported for "%s"' % dump_what
)
438 def _format_hex(self
, addr
, fieldsize
=None, fullhex
=False, lead0x
=True):
439 """ Format an address into a hexadecimal string.
442 Size of the hexadecimal field (with leading zeros to fit the
443 address into. For example with fieldsize=8, the format will
445 If None, the minimal required field size will be used.
448 If True, override fieldsize to set it to the maximal size
449 needed for the elfclass
452 If True, leading 0x is added
454 s
= '0x' if lead0x
else ''
456 fieldsize
= 8 if self
.elffile
.elfclass
== 32 else 16
457 if fieldsize
is None:
460 field
= '%' + '0%sx' % fieldsize
461 return s
+ field
% addr
463 def _section_from_spec(self
, spec
):
464 """ Retrieve a section given a "spec" (either number or name).
465 Return None if no such section exists in the file.
469 if num
< self
.elffile
.num_sections():
470 return self
.elffile
.get_section(num
)
474 # Not a number. Must be a name then
475 return self
.elffile
.get_section_by_name(spec
)
477 def _note_relocs_for_section(self
, section
):
478 """ If there are relocation sections pointing to the givne section,
479 emit a note about it.
481 for relsec
in self
.elffile
.iter_sections():
482 if isinstance(relsec
, RelocationSection
):
483 info_idx
= relsec
['sh_info']
484 if self
.elffile
.get_section(info_idx
) == section
:
485 self
._emitline
(' Note: This section has relocations against it, but these have NOT been applied to this dump.')
488 def _init_dwarfinfo(self
):
489 """ Initialize the DWARF info contained in the file and assign it to
491 Leave self._dwarfinfo at None if no DWARF info was found in the file
493 if self
._dwarfinfo
is not None:
496 if self
.elffile
.has_dwarf_info():
497 self
._dwarfinfo
= self
.elffile
.get_dwarf_info()
499 self
._dwarfinfo
= None
501 def _dump_debug_info(self
):
502 """ Dump the debugging info section.
504 self
._emitline
('Contents of the .debug_info section:\n')
506 # Offset of the .debug_info section in the stream
507 section_offset
= self
._dwarfinfo
.debug_info_sec
.global_offset
509 for cu
in self
._dwarfinfo
.iter_CUs():
510 self
._emitline
(' Compilation Unit @ offset %s:' %
511 self
._format
_hex
(cu
.cu_offset
))
512 self
._emitline
(' Length: %s (%s)' % (
513 self
._format
_hex
(cu
['unit_length']),
514 '%s-bit' % cu
.dwarf_format()))
515 self
._emitline
(' Version: %s' % cu
['version']),
516 self
._emitline
(' Abbrev Offset: %s' % cu
['debug_abbrev_offset']),
517 self
._emitline
(' Pointer Size: %s' % cu
['address_size'])
519 # The nesting depth of each DIE within the tree of DIEs must be
520 # displayed. To implement this, a counter is incremented each time
521 # the current DIE has children, and decremented when a null die is
522 # encountered. Due to the way the DIE tree is serialized, this will
523 # correctly reflect the nesting depth
526 for die
in cu
.iter_DIEs():
530 self
._emitline
(' <%s><%x>: Abbrev Number: %s (%s)' % (
536 for attr
in die
.attributes
.itervalues():
537 self
._emitline
(' <%2x> %-18s: %s' % (
541 attr
, die
, section_offset
)))
548 def _dump_debug_line_programs(self
):
549 """ Dump the (decoded) line programs from .debug_line
550 The programs are dumped in the order of the CUs they belong to.
552 self
._emitline
('Decoded dump of debug contents of section .debug_line:\n')
554 for cu
in self
._dwarfinfo
.iter_CUs():
555 lineprogram
= self
._dwarfinfo
.line_program_for_CU(cu
)
558 if len(lineprogram
['include_directory']) > 0:
559 cu_filename
= '%s/%s' % (
560 lineprogram
['include_directory'][0],
561 lineprogram
['file_entry'][0].name
)
563 cu_filename
= lineprogram
['file_entry'][0].name
565 self
._emitline
('CU: %s:' % cu_filename
)
566 self
._emitline
('File name Line number Starting address')
568 # Print each state's file, line and address information. For some
569 # instructions other output is needed to be compatible with
571 for entry
in lineprogram
.get_entries():
574 # Special handling for commands that don't set a new state
575 if entry
.command
== DW_LNS_set_file
:
576 file_entry
= lineprogram
['file_entry'][entry
.args
[0] - 1]
577 if file_entry
.dir_index
== 0:
579 self
._emitline
('\n./%s:[++]' % (
582 self
._emitline
('\n%s/%s:' % (
583 lineprogram
['include_directory'][file_entry
.dir_index
- 1],
585 elif entry
.command
== DW_LNE_define_file
:
586 self
._emitline
('%s:' % (
587 lineprogram
['include_directory'][entry
.args
[0].dir_index
]))
588 elif not state
.end_sequence
:
589 # readelf doesn't print the state after end_sequence
590 # instructions. I think it's a bug but to be compatible
591 # I don't print them too.
592 self
._emitline
('%-35s %11d %18s' % (
593 lineprogram
['file_entry'][state
.file - 1].name
,
595 '0' if state
.address
== 0 else
596 self
._format
_hex
(state
.address
)))
597 if entry
.command
== DW_LNS_copy
:
598 # Another readelf oddity...
601 def _emit(self
, s
=''):
602 """ Emit an object to output
604 self
.output
.write(str(s
))
606 def _emitline(self
, s
=''):
607 """ Emit an object to output, followed by a newline
609 self
.output
.write(str(s
) + '\n')
612 SCRIPT_DESCRIPTION
= 'Display information about the contents of ELF format files'
613 VERSION_STRING
= '%%prog: based on pyelftools %s' % __version__
616 def main(stream
=None):
617 # parse the command-line arguments and invoke ReadElf
618 optparser
= OptionParser(
619 usage
='usage: %prog [options] <elf-file>',
620 description
=SCRIPT_DESCRIPTION
,
621 add_help_option
=False, # -h is a real option of readelf
623 version
=VERSION_STRING
)
624 optparser
.add_option('-H', '--help',
625 action
='store_true', dest
='help',
626 help='Display this information')
627 optparser
.add_option('-h', '--file-header',
628 action
='store_true', dest
='show_file_header',
629 help='Display the ELF file header')
630 optparser
.add_option('-l', '--program-headers', '--segments',
631 action
='store_true', dest
='show_program_header',
632 help='Display the program headers')
633 optparser
.add_option('-S', '--section-headers', '--sections',
634 action
='store_true', dest
='show_section_header',
635 help="Display the sections' headers")
636 optparser
.add_option('-e', '--headers',
637 action
='store_true', dest
='show_all_headers',
638 help='Equivalent to: -h -l -S')
639 optparser
.add_option('-s', '--symbols', '--syms',
640 action
='store_true', dest
='show_symbols',
641 help='Display the symbol table')
642 optparser
.add_option('-r', '--relocs',
643 action
='store_true', dest
='show_relocs',
644 help='Display the relocations (if present)')
645 optparser
.add_option('-x', '--hex-dump',
646 action
='store', dest
='show_hex_dump', metavar
='<number|name>',
647 help='Dump the contents of section <number|name> as bytes')
648 optparser
.add_option('-p', '--string-dump',
649 action
='store', dest
='show_string_dump', metavar
='<number|name>',
650 help='Dump the contents of section <number|name> as strings')
651 optparser
.add_option('--debug-dump',
652 action
='store', dest
='debug_dump_what', metavar
='<section>',
653 help='Display the contents of DWARF debug sections')
655 options
, args
= optparser
.parse_args()
657 if options
.help or len(args
) == 0:
658 optparser
.print_help()
661 if options
.show_all_headers
:
662 do_file_header
= do_section_header
= do_program_header
= True
664 do_file_header
= options
.show_file_header
665 do_section_header
= options
.show_section_header
666 do_program_header
= options
.show_program_header
668 with
open(args
[0], 'rb') as file:
670 readelf
= ReadElf(file, stream
or sys
.stdout
)
672 readelf
.display_file_header()
673 if do_section_header
:
674 readelf
.display_section_headers(
675 show_heading
=not do_file_header
)
676 if do_program_header
:
677 readelf
.display_program_headers(
678 show_heading
=not do_file_header
)
679 if options
.show_symbols
:
680 readelf
.display_symbol_tables()
681 if options
.show_relocs
:
682 readelf
.display_relocations()
683 if options
.show_hex_dump
:
684 readelf
.display_hex_dump(options
.show_hex_dump
)
685 if options
.show_string_dump
:
686 readelf
.display_string_dump(options
.show_string_dump
)
687 if options
.debug_dump_what
:
688 readelf
.display_debug_dump(options
.debug_dump_what
)
689 except ELFError
as ex
:
690 sys
.stderr
.write('ELF error: %s\n' % ex
)
695 # Run 'main' redirecting its output to readelfout.txt
696 # Saves profiling information in readelf.profile
697 PROFFILE
= 'readelf.profile'
699 cProfile
.run('main(open("readelfout.txt", "w"))', PROFFILE
)
701 # Dig in some profiling stats
703 p
= pstats
.Stats(PROFFILE
)
704 p
.sort_stats('cumulative').print_stats(25)
707 #-------------------------------------------------------------------------------
708 if __name__
== '__main__':