1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
|
#!/usr/bin/env python3
# Copyright 2016 The Emscripten Authors. All rights reserved.
# Emscripten is available under two separate licenses, the MIT license and the
# University of Illinois/NCSA Open Source License. Both these licenses can be
# found in the LICENSE file.
"""Tries to evaluate global constructors, applying their effects ahead of time.
This is an LTO-like operation, and to avoid parsing the entire tree (we might
fail to parse a massive project, we operate on the text in python.
"""
import logging
import os
import subprocess
import sys
sys.path.insert(1, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from tools import shared
js_file = sys.argv[1]
binary_file = sys.argv[2] # mem init for js, wasm binary for wasm
total_memory = int(sys.argv[3])
total_stack = int(sys.argv[4])
global_base = int(sys.argv[5])
binaryen_bin = sys.argv[6]
debug_info = int(sys.argv[7])
extra_args = sys.argv[8:]
wasm = bool(binaryen_bin)
assert global_base > 0
logger = logging.getLogger('ctor_evaller')
# helpers
def find_ctors(js):
ctors_start = js.find('__ATINIT__.push(')
if ctors_start < 0:
return (-1, -1)
ctors_end = js.find(');', ctors_start)
assert ctors_end > 0
ctors_end += 3
return (ctors_start, ctors_end)
def find_ctors_data(js, num):
ctors_start, ctors_end = find_ctors(js)
assert ctors_start > 0
ctors_text = js[ctors_start:ctors_end]
all_ctors = [ctor for ctor in ctors_text.split(' ') if ctor.endswith('()') and not ctor == 'function()' and '.' not in ctor]
all_ctors = [ctor.replace('()', '') for ctor in all_ctors]
assert all(ctor.startswith('_') for ctor in all_ctors)
all_ctors = [ctor[1:] for ctor in all_ctors]
assert len(all_ctors)
ctors = all_ctors[:num]
return ctors_start, ctors_end, all_ctors, ctors
def eval_ctors(js, wasm_file, num):
ctors_start, ctors_end, all_ctors, ctors = find_ctors_data(js, num)
cmd = [os.path.join(binaryen_bin, 'wasm-ctor-eval'), wasm_file, '-o', wasm_file, '--ctors=' + ','.join(ctors)]
cmd += extra_args
if debug_info:
cmd += ['-g']
logger.debug('wasm ctor cmd: ' + str(cmd))
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
try:
err = shared.timeout_run(proc, timeout=10, full_output=True, check=False)
except Exception as e:
if 'Timed out' not in str(e):
raise
logger.debug('ctors timed out\n')
return 0, js
if proc.returncode != 0:
shared.exit_with_error('unexpected error while trying to eval ctors:\n' + err)
num_successful = err.count('success on')
logger.debug(err)
if len(ctors) == num_successful:
new_ctors = ''
else:
elements = []
for ctor in all_ctors[num_successful:]:
elements.append('{ func: function() { %s() } }' % ctor)
new_ctors = '__ATINIT__.push(' + ', '.join(elements) + ');'
js = js[:ctors_start] + new_ctors + js[ctors_end:]
return num_successful, js
# main
def main():
js = open(js_file).read()
ctors_start, ctors_end = find_ctors(js)
if ctors_start < 0:
logger.debug('ctor_evaller: no ctors')
sys.exit(0)
ctors_text = js[ctors_start:ctors_end]
if ctors_text.count('(') == 1:
logger.debug('ctor_evaller: push, but no ctors')
sys.exit(0)
num_ctors = ctors_text.count('function()')
logger.debug('ctor_evaller: %d ctors, from |%s|' % (num_ctors, ctors_text))
wasm_file = binary_file
logger.debug('ctor_evaller (wasm): trying to eval %d global constructors' % num_ctors)
num_successful, new_js = eval_ctors(js, wasm_file, num_ctors)
if num_successful == 0:
logger.debug('ctor_evaller: not successful')
sys.exit(0)
logger.debug('ctor_evaller: we managed to remove %d ctors' % num_successful)
open(js_file, 'w').write(new_js)
if __name__ == '__main__':
sys.exit(main())
|