def report_missing_symbols(js_library_funcs): # Report any symbol that was explicitly exported but is present neither # as a native function nor as a JS library function. defined_symbols = set( asmjs_mangle(e) for e in settings.WASM_EXPORTS).union(js_library_funcs) missing = set(settings.USER_EXPORTED_FUNCTIONS) - defined_symbols for symbol in sorted(missing): diagnostics.warning('undefined', f'undefined exported symbol: "{symbol}"') # Special hanlding for the `_main` symbol if settings.STANDALONE_WASM: # standalone mode doesn't use main, and it always reports missing entry point at link time. # In this mode we never expect _main in the export list. return # PROXY_TO_PTHREAD only makes sense with a main(), so error if one is # missing. note that when main() might arrive from another module we cannot # error here. if settings.PROXY_TO_PTHREAD and '_main' not in defined_symbols and \ not settings.RELOCATABLE: exit_with_error( 'PROXY_TO_PTHREAD proxies main() for you, but no main exists') if settings.IGNORE_MISSING_MAIN: # The default mode for emscripten is to ignore the missing main function allowing # maximum compatibility. return if settings.EXPECT_MAIN and 'main' not in settings.WASM_EXPORTS: # For compatibility with the output of wasm-ld we use the same wording here in our # error message as if wasm-ld had failed (i.e. in LLD_REPORT_UNDEFINED mode). exit_with_error( 'entry symbol not defined (pass --no-entry to suppress): main')
def report_missing_symbols(all_implemented, pre): # the initial list of missing functions are that the user explicitly exported # but were not implemented in compiled code missing = set(shared.Settings.USER_EXPORTED_FUNCTIONS) - all_implemented for requested in sorted(missing): if (f'function {requested}(') not in pre: diagnostics.warning('undefined', f'undefined exported symbol: "{requested}"') # Special hanlding for the `_main` symbol if shared.Settings.STANDALONE_WASM: # standalone mode doesn't use main, and it always reports missing entry point at link time. # In this mode we never expect _main in the export list. return if shared.Settings.IGNORE_MISSING_MAIN: # The default mode for emscripten is to ignore the missing main function allowing # maximum compatibility. return if shared.Settings.EXPECT_MAIN and '_main' not in all_implemented: # For compatibility with the output of wasm-ld we use the same wording here in our # error message as if wasm-ld had failed (i.e. in LLD_REPORT_UNDEFINED mode). exit_with_error( 'entry symbol not defined (pass --no-entry to suppress): main')
def report_missing_symbols(all_implemented, pre): # the initial list of missing functions are that the user explicitly exported # but were not implemented in compiled code missing = list( set(shared.Settings.USER_EXPORTED_FUNCTIONS) - all_implemented) for requested in missing: if ('function ' + asstr(requested)) in pre: continue # special-case malloc, EXPORTED by default for internal use, but we bake in a # trivial allocator and warn at runtime if used in ASSERTIONS if missing == '_malloc': continue diagnostics.warning('undefined', 'undefined exported function: "%s"', requested) # Special hanlding for the `_main` symbol if shared.Settings.STANDALONE_WASM: # standalone mode doesn't use main, and it always reports missing entry point at link time. # In this mode we never expect _main in the export list. return if shared.Settings.IGNORE_MISSING_MAIN: # The default mode for emscripten is to ignore the missing main function allowing # maximum compatibility. return if shared.Settings.EXPECT_MAIN and '_main' not in all_implemented: # For compatibility with the output of wasm-ld we use the same wording here in our # error message as if wasm-ld had failed (i.e. in LLD_REPORT_UNDEFINED mode). exit_with_error( 'entry symbol not defined (pass --no-entry to suppress): main')
def emscript(in_wasm, out_wasm, outfile_js, memfile): # Overview: # * Run wasm-emscripten-finalize to extract metadata and modify the binary # to use emscripten's wasm<->JS ABI # * Use the metadata to generate the JS glue that goes with the wasm if settings.SINGLE_FILE: # placeholder strings for JS glue, to be replaced with subresource locations in do_binaryen settings.WASM_BINARY_FILE = '<<< WASM_BINARY_FILE >>>' else: # set file locations, so that JS glue can find what it needs settings.WASM_BINARY_FILE = js_manipulation.escape_for_js_string( os.path.basename(out_wasm)) metadata = finalize_wasm(in_wasm, out_wasm, memfile) update_settings_glue(out_wasm, metadata) if not settings.WASM_BIGINT and metadata['emJsFuncs']: module = webassembly.Module(in_wasm) types = module.get_types() import_map = {} for imp in module.get_imports(): import_map[imp.field] = imp for em_js_func, raw in metadata.get('emJsFuncs', {}).items(): c_sig = raw.split('<::>')[0].strip('()') if not c_sig or c_sig == 'void': c_sig = [] else: c_sig = c_sig.split(',') if em_js_func in import_map: imp = import_map[em_js_func] assert (imp.kind == webassembly.ExternType.FUNC) signature = types[imp.type] if len(signature.params) != len(c_sig): diagnostics.warning( 'em-js-i64', 'using 64-bit arguments in EM_JS function without WASM_BIGINT is not yet fully supported: `%s` (%s, %s)', em_js_func, c_sig, signature.params) if settings.SIDE_MODULE: if metadata['asmConsts']: exit_with_error('EM_ASM is not supported in side modules') if metadata['emJsFuncs']: exit_with_error('EM_JS is not supported in side modules') logger.debug('emscript: skipping remaining js glue generation') return if DEBUG: logger.debug('emscript: js compiler glue') t = time.time() # memory and global initializers if settings.RELOCATABLE: dylink_sec = webassembly.parse_dylink_section(in_wasm) static_bump = align_memory(dylink_sec.mem_size) set_memory(static_bump) logger.debug('stack_base: %d, stack_max: %d, heap_base: %d', settings.STACK_BASE, settings.STACK_MAX, settings.HEAP_BASE) # When building relocatable output (e.g. MAIN_MODULE) the reported table # size does not include the reserved slot at zero for the null pointer. # So we need to offset the elements by 1. if settings.INITIAL_TABLE == -1: settings.INITIAL_TABLE = dylink_sec.table_size + 1 if settings.ASYNCIFY: metadata['globalImports'] += [ '__asyncify_state', '__asyncify_data' ] invoke_funcs = metadata['invokeFuncs'] if invoke_funcs: settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += ['$getWasmTableEntry'] glue, forwarded_data = compile_settings() if DEBUG: logger.debug(' emscript: glue took %s seconds' % (time.time() - t)) t = time.time() forwarded_json = json.loads(forwarded_data) if forwarded_json['warnings']: diagnostics.warning('js-compiler', 'warnings in JS library compilation') pre, post = glue.split('// EMSCRIPTEN_END_FUNCS') if settings.ASSERTIONS: pre += "function checkIncomingModuleAPI() {\n" for sym in settings.ALL_INCOMING_MODULE_JS_API: if sym not in settings.INCOMING_MODULE_JS_API: pre += f" ignoredModuleProp('{sym}');\n" pre += "}\n" exports = metadata['exports'] if settings.ASYNCIFY: exports += [ 'asyncify_start_unwind', 'asyncify_stop_unwind', 'asyncify_start_rewind', 'asyncify_stop_rewind' ] report_missing_symbols(forwarded_json['librarySymbols']) if not outfile_js: logger.debug('emscript: skipping remaining js glue generation') return if settings.MINIMAL_RUNTIME: # In MINIMAL_RUNTIME, atinit exists in the postamble part post = apply_static_code_hooks(forwarded_json, post) else: # In regular runtime, atinits etc. exist in the preamble part pre = apply_static_code_hooks(forwarded_json, pre) asm_consts = create_asm_consts(metadata) em_js_funcs = create_em_js(metadata) asm_const_pairs = ['%s: %s' % (key, value) for key, value in asm_consts] asm_const_map = 'var ASM_CONSTS = {\n ' + ', \n '.join( asm_const_pairs) + '\n};\n' pre = pre.replace('// === Body ===', ('// === Body ===\n\n' + asm_const_map + '\n'.join(em_js_funcs) + '\n')) with open(outfile_js, 'w', encoding='utf-8') as out: out.write(normalize_line_endings(pre)) pre = None sending = create_sending(invoke_funcs, metadata) receiving = create_receiving(exports) if settings.MINIMAL_RUNTIME: if settings.DECLARE_ASM_MODULE_EXPORTS: post = compute_minimal_runtime_initializer_and_exports( post, exports, receiving) receiving = '' module = create_module(sending, receiving, invoke_funcs, metadata) write_output_file(out, module) out.write(normalize_line_endings(post)) module = None