diff --git a/embuilder.py b/embuilder.py index 4ddd19862ad2d..1964d0f9f293d 100755 --- a/embuilder.py +++ b/embuilder.py @@ -120,6 +120,7 @@ 'crtbegin', 'libsanitizer_common_rt', 'libubsan_rt', + 'libwasm_workers-debug', 'libwasm_workers-debug-stub', 'libfetch', 'libfetch-mt', diff --git a/emcc.py b/emcc.py index ea948a708b3db..a65cc3c56960f 100644 --- a/emcc.py +++ b/emcc.py @@ -413,7 +413,7 @@ def phase_setup(options, state): 'unused-command-line-argument', "linker flag ignored during compilation: '%s'" % arg) - if settings.MAIN_MODULE or settings.SIDE_MODULE: + if settings.SIDE_MODULE: settings.RELOCATABLE = 1 if 'USE_PTHREADS' in user_settings: diff --git a/src/jsifier.mjs b/src/jsifier.mjs index 512a9a3b7025b..a3e21ab3f922e 100644 --- a/src/jsifier.mjs +++ b/src/jsifier.mjs @@ -121,7 +121,7 @@ function isDefined(symName) { } // 'invoke_' symbols are created at runtime in library_dylink.py so can // always be considered as defined. - if (RELOCATABLE && symName.startsWith('invoke_')) { + if ((MAIN_MODULE || RELOCATABLE) && symName.startsWith('invoke_')) { return true; } return false; @@ -572,7 +572,7 @@ function(${args}) { if (!LibraryManager.library.hasOwnProperty(symbol)) { const isWeakImport = WEAK_IMPORTS.has(symbol); if (!isDefined(symbol) && !isWeakImport) { - if (PROXY_TO_PTHREAD && !MAIN_MODULE && symbol == '__main_argc_argv') { + if (PROXY_TO_PTHREAD && !(MAIN_MODULE || RELOCATABLE) && symbol == '__main_argc_argv') { error('PROXY_TO_PTHREAD proxies main() for you, but no main exists'); return; } @@ -603,7 +603,7 @@ function(${args}) { // emit a stub that will fail at runtime var stubFunctionBody = `abort('missing function: ${symbol}');` - if (RELOCATABLE) { + if (RELOCATABLE || MAIN_MODULE) { // Create a stub for this symbol which can later be replaced by the // dynamic linker. If this stub is called before the symbol is // resolved assert in debug builds or trap in release builds. @@ -762,8 +762,8 @@ function(${args}) { contentText = 'export ' + contentText; } - // Relocatable code needs signatures to create proper wrappers. - if (sig && RELOCATABLE) { + // Dynamic linking needs signatures to create proper wrappers. + if (sig && (MAIN_MODULE || RELOCATABLE)) { if (!WASM_BIGINT) { sig = sig[0].replace('j', 'i') + sig.slice(1).replace(/j/g, 'ii'); } @@ -774,7 +774,7 @@ function(${args}) { } if (isStub) { contentText += `\n${mangled}.stub = true;`; - if (ASYNCIFY && MAIN_MODULE) { + if (ASYNCIFY && (MAIN_MODULE || RELOCATABLE)) { contentText += `\nasyncifyStubs['${symbol}'] = undefined;`; } } diff --git a/src/lib/libcore.js b/src/lib/libcore.js index e40704103b886..03fd4278d01c3 100644 --- a/src/lib/libcore.js +++ b/src/lib/libcore.js @@ -2213,11 +2213,12 @@ addToLibrary({ __stack_high: '{{{ STACK_HIGH }}}', __stack_low: '{{{ STACK_LOW }}}', __global_base: '{{{ GLOBAL_BASE }}}', -#if ASYNCIFY == 1 +#endif // RELOCATABLE + +#if (MAIN_MODULE || RELOCATABLE) && ASYNCIFY == 1 __asyncify_state: "new WebAssembly.Global({'value': 'i32', 'mutable': true}, 0)", __asyncify_data: "new WebAssembly.Global({'value': '{{{ POINTER_WASM_TYPE }}}', 'mutable': true}, {{{ to64(0) }}})", #endif -#endif // RELOCATABLE _emscripten_fs_load_embedded_files__deps: ['$FS', '$PATH'], _emscripten_fs_load_embedded_files: (ptr) => { diff --git a/src/lib/libdylink.js b/src/lib/libdylink.js index 221e9e1d5fd4c..e879a182d01cb 100644 --- a/src/lib/libdylink.js +++ b/src/lib/libdylink.js @@ -6,8 +6,8 @@ * Dynamic library loading */ -#if !RELOCATABLE -#error "library_dylink.js requires RELOCATABLE" +#if !MAIN_MODULE && !RELOCATABLE +#error "library_dylink.js requires MAIN_MODULE or RELOCATABLE" #endif var LibraryDylink = { @@ -170,10 +170,10 @@ var LibraryDylink = { get(obj, symName) { var rtn = GOT[symName]; if (!rtn) { - rtn = GOT[symName] = new WebAssembly.Global({'value': '{{{ POINTER_WASM_TYPE }}}', 'mutable': true}); #if DYLINK_DEBUG == 2 - dbg("new GOT entry: " + symName); + dbg(`new GOT entry: ${symName}`); #endif + rtn = GOT[symName] = new WebAssembly.Global({'value': '{{{ POINTER_WASM_TYPE }}}', 'mutable': true}, {{{ to64(-1) }}}); } if (!currentModuleWeakSymbols.has(symName)) { // Any non-weak reference to a symbol marks it as `required`, which @@ -189,6 +189,11 @@ var LibraryDylink = { $isInternalSym: (symName) => { // TODO: find a way to mark these in the binary or avoid exporting them. return [ + 'memory', + '__memory_base', + '__table_base', + '__stack_pointer', + '__indirect_function_table', '__cpp_exception', '__c_longjmp', '__wasm_apply_data_relocs', @@ -213,6 +218,7 @@ var LibraryDylink = { $updateGOT__internal: true, $updateGOT__deps: ['$GOT', '$isInternalSym', '$addFunction'], + $updateGOT__docs: '/** @param {boolean=} replace */', $updateGOT: (exports, replace) => { #if DYLINK_DEBUG dbg(`updateGOT: adding ${Object.keys(exports).length} symbols`); @@ -230,18 +236,14 @@ var LibraryDylink = { } #endif - - var existingEntry = GOT[symName] && GOT[symName].value != 0; + var existingEntry = GOT[symName] && GOT[symName].value != {{{ to64(-1) }}}; if (replace || !existingEntry) { #if DYLINK_DEBUG == 2 - dbg(`updateGOT: before: ${symName} : ${GOT[symName].value}`); + dbg(`updateGOT: before: ${symName} : ${GOT[symName]?.value}`); #endif var newValue; if (typeof value == 'function') { newValue = {{{ to64('addFunction(value)') }}}; -#if DYLINK_DEBUG == 2 - dbg(`updateGOT: FUNC: ${symName} : ${GOT[symName].value}`); -#endif } else if (typeof value == {{{ POINTER_JS_TYPE }}}) { newValue = value; } else { @@ -255,7 +257,7 @@ var LibraryDylink = { #if DYLINK_DEBUG == 2 dbg(`updateGOT: after: ${symName} : ${newValue} (${value})`); #endif - GOT[symName] ||= new WebAssembly.Global({'value': '{{{ POINTER_WASM_TYPE }}}', 'mutable': true}); + GOT[symName] ??= new WebAssembly.Global({'value': '{{{ POINTER_WASM_TYPE }}}', 'mutable': true}); GOT[symName].value = newValue; } #if DYLINK_DEBUG @@ -283,9 +285,12 @@ var LibraryDylink = { // Applies relocations to exported things. $relocateExports__internal: true, - $relocateExports__deps: ['$updateGOT', '$isImmutableGlobal'], - $relocateExports__docs: '/** @param {boolean=} replace */', - $relocateExports: (exports, memoryBase, replace) => { + $relocateExports__deps: ['$isImmutableGlobal'], + $relocateExports: (exports, memoryBase = 0) => { +#if DYLINK_DEBUG + dbg(`relocateExports memoryBase=${memoryBase} count=${Object.keys(exports).length}`); +#endif + function relocateExport(name, value) { #if SPLIT_MODULE // Do not modify exports synthesized by wasm-split @@ -307,7 +312,6 @@ var LibraryDylink = { for (var e in exports) { relocated[e] = relocateExport(e, exports[e]) } - updateGOT(relocated, replace); return relocated; }, @@ -318,13 +322,17 @@ var LibraryDylink = { dbg('reportUndefinedSymbols'); #endif for (var [symName, entry] of Object.entries(GOT)) { - if (entry.value == 0) { + if (entry.value == {{{ to64(-1) }}}) { +#if DYLINK_DEBUG + dbg(`undef GOT entry: ${symName}`); +#endif var value = resolveGlobalSymbol(symName, true).sym; if (!value && !entry.required) { // Ignore undefined symbols that are imported as weak. #if DYLINK_DEBUG dbg('ignoring undefined weak symbol:', symName); #endif + entry.value = 0; continue; } #if ASSERTIONS @@ -346,7 +354,7 @@ var LibraryDylink = { entry.value = value; #endif } else { - throw new Error(`bad export type for '${symName}': ${typeof value}`); + throw new Error(`bad export type for '${symName}': ${typeof value} (${value})`); } } } @@ -393,7 +401,7 @@ var LibraryDylink = { // Allocate memory even if malloc isn't ready yet. The allocated memory here // must be zero initialized since its used for all static data, including bss. $getMemory__noleakcheck: true, - $getMemory__deps: ['$GOT', '__heap_base', '$alignMemory', 'calloc'], + $getMemory__deps: ['$GOT', 'emscripten_get_sbrk_ptr', '__heap_base', '$alignMemory', 'calloc'], $getMemory: (size) => { // After the runtime is initialized, we must only use sbrk() normally. #if DYLINK_DEBUG @@ -411,8 +419,15 @@ var LibraryDylink = { #if ASSERTIONS assert(end <= HEAP8.length, 'failure to getMemory - memory growth etc. is not supported there, call malloc/sbrk directly or increase INITIAL_MEMORY'); #endif - ___heap_base = end; - GOT['__heap_base'].value = {{{ to64('end') }}}; + ___heap_base = {{{ to64('end') }}}; +#if PTHREADS + if (!ENVIRONMENT_IS_PTHREAD) { +#endif + var sbrk_ptr = _emscripten_get_sbrk_ptr(); + {{{ makeSetValue('sbrk_ptr', 0, 'end', '*') }}} +#if PTHREADS + } +#endif return ret; }, @@ -625,7 +640,7 @@ var LibraryDylink = { * @param {number=} handle */`, $loadWebAssemblyModule__deps: [ - '$loadDynamicLibrary', '$getMemory', + '$loadDynamicLibrary', '$getMemory', '$updateGOT', '$relocateExports', '$resolveGlobalSymbol', '$GOTHandler', '$getDylinkMetadata', '$alignMemory', '$currentModuleWeakSymbols', @@ -635,7 +650,7 @@ var LibraryDylink = { ], $loadWebAssemblyModule: (binary, flags, libName, localScope, handle) => { #if DYLINK_DEBUG - dbg('loadWebAssemblyModule:', libName); + dbg('loadWebAssemblyModule:', libName, handle); #endif var metadata = getDylinkMetadata(binary); @@ -654,6 +669,9 @@ var LibraryDylink = { // exclusive access to it for the duration of this function. See the // locking in `dynlink.c`. var firstLoad = !handle || !{{{ makeGetValue('handle', C_STRUCTS.dso.mem_allocated, 'i8') }}}; +#if DYLINK_DEBUG + dbg('firstLoad:', firstLoad); +#endif if (firstLoad) { #endif // alignments are powers of 2 @@ -790,6 +808,7 @@ var LibraryDylink = { // add new entries to functionsInTableMap updateTableMap(tableBase, metadata.tableSize); moduleExports = relocateExports(instance.exports, memoryBase); + updateGOT(moduleExports); #if ASYNCIFY moduleExports = Asyncify.instrumentWasmExports(moduleExports); #endif @@ -881,18 +900,27 @@ var LibraryDylink = { if (applyRelocs) { if (runtimeInitialized) { #if DYLINK_DEBUG - dbg('applyRelocs'); + dbg('running __wasm_apply_data_relocs'); #endif applyRelocs(); } else { +#if DYLINK_DEBUG + dbg('delaying __wasm_apply_data_relocs'); +#endif __RELOC_FUNCS__.push(applyRelocs); } } var init = moduleExports['__wasm_call_ctors']; if (init) { if (runtimeInitialized) { +#if DYLINK_DEBUG + dbg('running __wasm_call_ctors'); +#endif init(); } else { +#if DYLINK_DEBUG + dbg('delaying __wasm_call_ctors'); +#endif // we aren't ready to run compiled code yet addOnPostCtor(init); } diff --git a/src/lib/libglemu.js b/src/lib/libglemu.js index b3f6ff60fd867..8265a6ce6e019 100644 --- a/src/lib/libglemu.js +++ b/src/lib/libglemu.js @@ -10,7 +10,7 @@ assert(!FULL_ES3, 'cannot emulate both ES3 and legacy GL'); {{{ const copySigs = (func) => { - if (!RELOCATABLE) return ''; + if (!MAIN_MODULE) return ''; return ` _${func}.sig = _emscripten_${func}.sig = orig_${func}.sig;`; }; const fromPtr = (arg) => { diff --git a/src/lib/libpthread.js b/src/lib/libpthread.js index 695320d4e5304..030fb4f6b98a7 100644 --- a/src/lib/libpthread.js +++ b/src/lib/libpthread.js @@ -594,7 +594,7 @@ var LibraryPThread = { #if MAIN_MODULE $registerTLSInit: (tlsInitFunc, moduleExports, metadata) => { #if DYLINK_DEBUG - dbg("registerTLSInit: " + tlsInitFunc); + dbg('registerTLSInit:', tlsInitFunc, metadata?.tlsExports); #endif // In relocatable builds, we use the result of calling tlsInitFunc // (`_emscripten_tls_init`) to relocate the TLS exports of the module @@ -613,7 +613,7 @@ var LibraryPThread = { } var tlsExports = {}; metadata.tlsExports.forEach((s) => tlsExports[s] = moduleExports[s]); - relocateExports(tlsExports, __tls_base, /*replace=*/true); + updateGOT(relocateExports(tlsExports, __tls_base), /*replace=*/true); } // Register this function so that its gets called for each thread on diff --git a/src/lib/libwasm_worker.js b/src/lib/libwasm_worker.js index 8e9162ecd3aca..649b1cef6ab6a 100644 --- a/src/lib/libwasm_worker.js +++ b/src/lib/libwasm_worker.js @@ -15,7 +15,7 @@ #if LINKABLE #error "-sLINKABLE is not supported with -sWASM_WORKERS" #endif -#if RELOCATABLE +#if RELOCATABLE || MAIN_MODULE #error "dynamic linking is not supported with -sWASM_WORKERS" #endif #if PROXY_TO_WORKER diff --git a/src/modules.mjs b/src/modules.mjs index fa71d8afa75fd..d05f1bb9b8dce 100644 --- a/src/modules.mjs +++ b/src/modules.mjs @@ -93,7 +93,7 @@ function calculateLibraries() { libraries.push('libsyscall.js'); } - if (RELOCATABLE) { + if (MAIN_MODULE || RELOCATABLE) { libraries.push('libdylink.js'); } diff --git a/src/preamble.js b/src/preamble.js index c9002009f05d2..e7fd2222e0fdc 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -14,7 +14,7 @@ // An online HTML version (which may be of a different version of Emscripten) // is up at http://kripken.github.io/emscripten-site/docs/api_reference/preamble.js.html -#if RELOCATABLE +#if MAIN_MODULE {{{ makeModuleReceiveWithVar('dynamicLibraries', undefined, '[]') }}} #endif @@ -108,7 +108,7 @@ assert(globalThis.Int32Array && globalThis.Float64Array && Int32Array.prototype. 'JS engine does not provide full typed array support'); #endif -#if RELOCATABLE +#if RELOCATABLE || MAIN_MODULE var __RELOC_FUNCS__ = []; #endif @@ -155,7 +155,7 @@ function initRuntime() { checkStackCookie(); #endif -#if RELOCATABLE +#if MAIN_MODULE || RELOCATABLE callRuntimeCallbacks(__RELOC_FUNCS__); #endif @@ -167,9 +167,15 @@ function initRuntime() { #else wasmExports['__wasm_call_ctors'](); #endif +#if RUNTIME_DEBUG + dbg('done __wasm_call_ctors'); +#endif #endif <<< ATPOSTCTORS >>> +#if RUNTIME_DEBUG + dbg('done ATPOSTCTORS'); +#endif } #if HAS_MAIN @@ -685,7 +691,7 @@ function getWasmImports() { 'env': wasmImports, '{{{ WASI_MODULE_NAME }}}': wasmImports, #endif // MINIFY_WASM_IMPORTED_MODULES -#if RELOCATABLE +#if MAIN_MODULE || RELOCATABLE 'GOT.mem': new Proxy(wasmImports, GOTHandler), 'GOT.func': new Proxy(wasmImports, GOTHandler), #endif @@ -704,18 +710,15 @@ function getWasmImports() { // performing other necessary setup /** @param {WebAssembly.Module=} module*/ function receiveInstance(instance, module) { - wasmExports = instance.exports; - -#if RELOCATABLE - wasmExports = relocateExports(wasmExports, {{{ GLOBAL_BASE }}}); -#endif - -#if ASYNCIFY - wasmExports = Asyncify.instrumentWasmExports(wasmExports); +#if RUNTIME_DEBUG + dbg('receiveInstance') #endif + wasmExports = instance.exports; -#if ABORT_ON_WASM_EXCEPTIONS - wasmExports = instrumentWasmExportsWithAbort(wasmExports); +#if MAIN_MODULE + // No relocation needed here.. but calling this just so that updateGOT is + // called. + var origExports = wasmExports = relocateExports(wasmExports); #endif #if MAIN_MODULE @@ -725,21 +728,6 @@ function getWasmImports() { dynamicLibraries = metadata.neededDynlibs.concat(dynamicLibraries); } #endif - mergeLibSymbols(wasmExports, 'main') -#if '$LDSO' in addedLibraryItems - LDSO.init(); -#endif - loadDylibs(); -#elif RELOCATABLE - reportUndefinedSymbols(); -#endif - -#if MEMORY64 || CAN_ADDRESS_2GB - wasmExports = applySignatureConversions(wasmExports); -#endif - -#if EXPORTED_RUNTIME_METHODS.includes('wasmExports') - Module['wasmExports'] = wasmExports; #endif #if PTHREADS @@ -754,8 +742,42 @@ function getWasmImports() { __RELOC_FUNCS__.push(wasmExports['__wasm_apply_data_relocs']); #endif +#if RUNTIME_DEBUG + dbg('assigning exports') +#endif + +#if ASYNCIFY + wasmExports = Asyncify.instrumentWasmExports(wasmExports); +#endif + +#if ABORT_ON_WASM_EXCEPTIONS + wasmExports = instrumentWasmExportsWithAbort(wasmExports); +#endif + +#if MEMORY64 || CAN_ADDRESS_2GB + wasmExports = applySignatureConversions(wasmExports); +#endif + assignWasmExports(wasmExports); +#if MAIN_MODULE + updateGOT(origExports); +#endif + +#if EXPORTED_RUNTIME_METHODS.includes('wasmExports') + Module['wasmExports'] = wasmExports; +#endif + +#if MAIN_MODULE + mergeLibSymbols(wasmExports, 'main') +#if '$LDSO' in addedLibraryItems + LDSO.init(); +#endif + loadDylibs(); +#elif RELOCATABLE + reportUndefinedSymbols(); +#endif + #if ABORT_ON_WASM_EXCEPTIONS instrumentWasmTableWithAbort(); #endif @@ -792,7 +814,7 @@ function getWasmImports() { assert(Module === trueModule, 'the Module object should not be replaced during async compilation - perhaps the order of HTML elements is wrong?'); trueModule = null; #endif -#if SHARED_MEMORY || RELOCATABLE +#if SHARED_MEMORY || MAIN_MODULE return receiveInstance(result['instance'], result['module']); #else // TODO: Due to Closure regression https://github.com/google/closure-compiler/issues/3193, the above line no longer optimizes out down to the following line. diff --git a/test/codesize/test_codesize_hello_dylink.json b/test/codesize/test_codesize_hello_dylink.json index 6fcb6be251727..a62adc875c201 100644 --- a/test/codesize/test_codesize_hello_dylink.json +++ b/test/codesize/test_codesize_hello_dylink.json @@ -1,49 +1,36 @@ { - "a.out.js": 26919, - "a.out.js.gz": 11469, - "a.out.nodebug.wasm": 18567, - "a.out.nodebug.wasm.gz": 9199, - "total": 45486, - "total_gz": 20668, + "a.out.js": 26577, + "a.out.js.gz": 11362, + "a.out.nodebug.wasm": 17757, + "a.out.nodebug.wasm.gz": 8974, + "total": 44334, + "total_gz": 20336, "sent": [ - "__heap_base", - "__indirect_function_table", - "__memory_base", - "__stack_high", - "__stack_low", - "__stack_pointer", "__syscall_stat64", - "__table_base", "emscripten_resize_heap", "environ_get", "environ_sizes_get", - "fd_write", - "memory" + "fd_write" ], "imports": [ - "GOT.mem.__heap_base", - "GOT.mem.__stack_high", - "GOT.mem.__stack_low", - "env.__indirect_function_table", - "env.__memory_base", - "env.__stack_pointer", "env.__syscall_stat64", - "env.__table_base", "env.emscripten_resize_heap", - "env.memory", "wasi_snapshot_preview1.environ_get", "wasi_snapshot_preview1.environ_sizes_get", "wasi_snapshot_preview1.fd_write" ], "exports": [ - "__wasm_apply_data_relocs", + "__heap_base", + "__indirect_function_table", "__wasm_call_ctors", "_emscripten_find_dylib", "_emscripten_stack_alloc", "_emscripten_stack_restore", "calloc", + "emscripten_get_sbrk_ptr", "emscripten_stack_get_current", "main", + "memory", "setThrew" ], "funcs": [ @@ -55,14 +42,13 @@ "$__stdio_write", "$__strchrnul", "$__towrite", - "$__wasm_apply_data_relocs", "$__wasm_call_ctors", - "$__wasm_start", "$_emscripten_find_dylib", "$_emscripten_stack_alloc", "$_emscripten_stack_restore", "$dlcalloc", "$dlmalloc", + "$emscripten_get_sbrk_ptr", "$emscripten_stack_get_current", "$fmt_fp", "$fmt_u", diff --git a/test/codesize/test_codesize_hello_dylink_all.json b/test/codesize/test_codesize_hello_dylink_all.json index 6b36b09e98c4a..83cc86f7f6329 100644 --- a/test/codesize/test_codesize_hello_dylink_all.json +++ b/test/codesize/test_codesize_hello_dylink_all.json @@ -1,7 +1,7 @@ { - "a.out.js": 245826, - "a.out.nodebug.wasm": 597777, - "total": 843603, + "a.out.js": 245463, + "a.out.nodebug.wasm": 574091, + "total": 819554, "sent": [ "IMG_Init", "IMG_Load", @@ -215,13 +215,6 @@ "__asctime_r", "__assert_fail", "__call_sighandler", - "__global_base", - "__heap_base", - "__indirect_function_table", - "__memory_base", - "__stack_high", - "__stack_low", - "__stack_pointer", "__syscall__newselect", "__syscall_accept4", "__syscall_bind", @@ -271,7 +264,6 @@ "__syscall_truncate64", "__syscall_unlinkat", "__syscall_utimensat", - "__table_base", "_abort_js", "_dlopen_js", "_dlsym_catchup_js", @@ -1118,7 +1110,6 @@ "glutTimerFunc", "lineColor", "lineRGBA", - "memory", "pixelRGBA", "proc_exit", "random_get", @@ -1411,14 +1402,8 @@ "GOT.func.emscripten_glVertexAttribPointer", "GOT.func.emscripten_glViewport", "GOT.func.emscripten_out", - "GOT.mem.__heap_base", - "GOT.mem.__stack_high", - "GOT.mem.__stack_low", "env.__assert_fail", "env.__call_sighandler", - "env.__indirect_function_table", - "env.__memory_base", - "env.__stack_pointer", "env.__syscall__newselect", "env.__syscall_accept4", "env.__syscall_bind", @@ -1468,7 +1453,6 @@ "env.__syscall_truncate64", "env.__syscall_unlinkat", "env.__syscall_utimensat", - "env.__table_base", "env._abort_js", "env._dlopen_js", "env._dlsym_js", @@ -1500,7 +1484,6 @@ "env.emscripten_wget_data", "env.exit", "env.getnameinfo", - "env.memory", "wasi_snapshot_preview1.clock_res_get", "wasi_snapshot_preview1.clock_time_get", "wasi_snapshot_preview1.environ_get", @@ -1729,6 +1712,8 @@ "__gtsf2", "__gttf2", "__h_errno_location", + "__heap_base", + "__indirect_function_table", "__intscan", "__isalnum_l", "__isalpha_l", @@ -1852,6 +1837,7 @@ "__small_vsprintf", "__stack_chk_fail", "__stack_chk_guard", + "__stack_pointer", "__strcasecmp_l", "__strcoll_l", "__strerror_l", @@ -1953,7 +1939,6 @@ "__wasi_fd_is_valid", "__wasi_syscall_ret", "__wasi_timestamp_to_timespec", - "__wasm_apply_data_relocs", "__wasm_call_ctors", "__wasm_setjmp", "__wasm_setjmp_test", @@ -2714,6 +2699,7 @@ "memcpy", "memmem", "memmove", + "memory", "mempcpy", "memrchr", "memset", @@ -3624,7 +3610,6 @@ "$__libc_current_sigrtmax", "$__libc_current_sigrtmin", "$__llvm_profile_get_num_counters", - "$__loc_is_allocated", "$__localtime_r", "$__lookup_ipliteral", "$__lseek", @@ -3813,11 +3798,9 @@ "$__wasi_fd_is_valid", "$__wasi_syscall_ret", "$__wasi_timestamp_to_timespec", - "$__wasm_apply_data_relocs", "$__wasm_call_ctors", "$__wasm_setjmp", "$__wasm_setjmp_test", - "$__wasm_start", "$__wcscoll_l", "$__wcsftime_l", "$__wcsxfrm_l", @@ -4450,7 +4433,6 @@ "$login_tty", "$logl", "$lookup", - "$lprofBufferIOWrite", "$lprofBufferWriter", "$lprofLockFileHandle", "$lprofMergeValueProfData", @@ -4689,7 +4671,6 @@ "$roundf", "$roundl", "$rsh", - "$savestate", "$sbrk", "$scalb", "$scalbf", diff --git a/test/common.py b/test/common.py index a73287f8589d3..ad3f1590eb47d 100644 --- a/test/common.py +++ b/test/common.py @@ -1416,24 +1416,6 @@ def ccshared(src, linkto=None): ccshared('libc.c', ['liba' + so]) self.set_setting('MAIN_MODULE') - extra_args = ['-L.', 'libb' + so, 'libc' + so] - do_run(r''' - #ifdef __cplusplus - extern "C" { - #endif - void bfunc(); - void cfunc(); - #ifdef __cplusplus - } - #endif - - int test_main() { - bfunc(); - cfunc(); - return 0; - } - ''', - 'a: loaded\na: b (prev: (null))\na: c (prev: b)\n', cflags=extra_args) extra_args = [] for libname in ('liba', 'libb', 'libc'): diff --git a/test/core/test_dlfcn_self.exports b/test/core/test_dlfcn_self.exports index 7c1ea900be697..0bb3488384e51 100644 --- a/test/core/test_dlfcn_self.exports +++ b/test/core/test_dlfcn_self.exports @@ -2,6 +2,7 @@ __THREW__ ___environ __daylight __environ +__heap_base __optpos __optreset __progname @@ -10,6 +11,7 @@ __sig_actions __sig_pending __signgam __stack_chk_guard +__stack_pointer __threwValue __timezone __tzname diff --git a/test/test_core.py b/test/test_core.py index a1d777e014e19..46b2c842ee5f6 100644 --- a/test/test_core.py +++ b/test/test_core.py @@ -4981,8 +4981,10 @@ def test_dylink_exceptions_try_catch_2(self): def test_dylink_exceptions_try_catch_6(self): create_file('main.cpp', r''' #include + #include #include int main() { + printf("in main\n"); void* handle = dlopen("liblib.so", RTLD_LAZY); assert(handle); void (*side)(void) = (void (*)(void))dlsym(handle, "side"); @@ -4999,6 +5001,7 @@ def test_dylink_exceptions_try_catch_6(self): create_file('liblib.cpp', r''' #include extern "C" void side() { + printf("in side\n"); try { throw 3; } catch (int x){ @@ -5024,7 +5027,7 @@ def test_dylink_exceptions_try_catch_6(self): self.set_setting('MAIN_MODULE', 1) self.clear_setting('SIDE_MODULE') - self.do_runf("main.cpp", "side: caught int 3\n") + self.do_runf('main.cpp', 'side: caught int 3\n') @with_dylink_reversed @disabled('https://github.com/emscripten-core/emscripten/issues/12815') diff --git a/test/test_other.py b/test/test_other.py index ddcd572e56a23..9f3b9473c55a7 100644 --- a/test/test_other.py +++ b/test/test_other.py @@ -5185,7 +5185,7 @@ def test_no_dynamic_execution(self): delete_file('a.out.js') # Test that -sDYNAMIC_EXECUTION=0 and -sMAIN_MODULE are allowed together. - self.do_runf('hello_world.c', cflags=['-O1', '-sDYNAMIC_EXECUTION=0', '-sMAIN_MODULE']) + self.do_runf('hello_world.c', cflags=['-O1', '-sDYNAMIC_EXECUTION=0', '-sMAIN_MODULE=2']) create_file('test.c', r''' #include @@ -11747,11 +11747,13 @@ def test_standalone_syscalls(self): @also_with_wasm64 @parameterized({ '': ([],), + 'assertions': (['-sASSERTIONS'],), 'closure': (['--closure=1', '-Werror=closure'],), 'closure_assertions': (['--closure=1', '-Werror=closure', '-sASSERTIONS'],), + 'dylink': (['-sMAIN_MODULE=2'],), }) def test_emdawnwebgpu_link_test(self, args): - if config.FROZEN_CACHE and self.get_setting('MEMORY64'): + if config.FROZEN_CACHE and (self.get_setting('MEMORY64') or '-sMAIN_MODULE=2' in args): # CI configuration doesn't run `embuilder` with wasm64 on ports self.skipTest("test doesn't work with frozen cache") self.emcc(test_file('test_emdawnwebgpu_link_test.cpp'), ['--use-port=emdawnwebgpu', '-sASYNCIFY'] + args) @@ -12427,14 +12429,14 @@ def test_em_js_main_module(self): self.set_setting('EXPORTED_FUNCTIONS', '_main,_malloc') self.do_runf('core/test_em_js.cpp') - def test_em_js_main_module_address(self): + def test_em_js_dylink_address(self): # This works under static linking but is known to fail with dynamic linking # See https://github.com/emscripten-core/emscripten/issues/18494 self.do_runf('other/test_em_js_main_module_address.c') - self.set_setting('MAIN_MODULE', 2) - expected = 'Aborted(Assertion failed: Missing signature argument to addFunction: function foo() { err("hello"); })' - self.do_runf('other/test_em_js_main_module_address.c', expected, assert_returncode=NON_ZERO) + self.emcc('other/test_em_js_main_module_address.c', ['-sSIDE_MODULE'], output_filename='libside.so') + expected = 'Aborted(Assertion failed: Missing signature argument to addFunction: () => { err("hello"); })' + self.do_run('', expected, cflags=['-sMAIN_MODULE=2', 'libside.so'], assert_returncode=NON_ZERO) def test_em_js_external_usage(self): # Verify that EM_JS functions can be called from other source files, even in the case @@ -13491,12 +13493,17 @@ def test_unsafe_optimizations(self): def test_extended_const(self): self.v8_args += ['--experimental-wasm-extended-const'] # Export at least one global so that we exercise the parsing of the global section. - self.do_runf('hello_world.c', cflags=['-sEXPORTED_FUNCTIONS=_main,___stdout_used', '-mextended-const', '-sMAIN_MODULE=2']) - wat = self.get_wasm_text('hello_world.wasm') + create_file('test.c', r''' + #include + int g_data[10] = {1}; + void test() { + printf("some rodata %d\n", g_data[0]); + } + ''') + self.emcc('test.c', ['-mextended-const', '-sSIDE_MODULE']) + wat = self.get_wasm_text('a.out.wasm') # Test that extended-const expressions are used in the data segments. self.assertContained(r'\(data (\$\S+ )?\(offset \(i32.add\s+\(global.get \$\S+\)\s+\(i32.const \d+\)', wat, regex=True) - # Test that extended-const expressions are used in at least one global initializer. - self.assertContained(r'\(global \$\S+ i32 \(i32.add\s+\(global.get \$\S+\)\s+\(i32.const \d+\)', wat, regex=True) # Smoketest for MEMORY64 setting. Most of the testing of MEMORY64 is by way of the wasm64 # variant of the core test suite. diff --git a/tools/building.py b/tools/building.py index c98af739081d0..e2afec294d157 100644 --- a/tools/building.py +++ b/tools/building.py @@ -214,7 +214,7 @@ def lld_flags_for_executable(external_symbols): cmd.extend(f'--export-if-defined={e}' for e in settings.EXPORT_IF_DEFINED) - if settings.RELOCATABLE: + if settings.MAIN_MODULE or settings.RELOCATABLE: cmd.append('--experimental-pic') cmd.append('--unresolved-symbols=import-dynamic') if not settings.WASM_BIGINT: @@ -223,6 +223,8 @@ def lld_flags_for_executable(external_symbols): # shared libraries. Because of this we need to disabled signature # checking of shared library functions in this case. cmd.append('--no-shlib-sigcheck') + + if settings.RELOCATABLE: if settings.SIDE_MODULE: cmd.append('-shared') else: @@ -287,6 +289,9 @@ def link_lld(args, target, external_symbols=None): args.insert(0, '--whole-archive') args.append('--no-whole-archive') + if settings.MAIN_MODULE: + args.insert(0, '-Bdynamic') + if settings.STRICT and '--no-fatal-warnings' not in args: args.append('--fatal-warnings') @@ -1315,7 +1320,7 @@ def read_and_preprocess(filename, expand_macros=False): def js_legalization_pass_flags(): flags = [] - if settings.RELOCATABLE: + if settings.RELOCATABLE or settings.MAIN_MODULE: # When building in relocatable mode, we also want access the original # non-legalized wasm functions (since wasm modules can and do link to # the original, non-legalized, functions). diff --git a/tools/cache.py b/tools/cache.py index 32f732a5e8e1d..90d7f774f9770 100644 --- a/tools/cache.py +++ b/tools/cache.py @@ -124,7 +124,7 @@ def get_lib_dir(absolute): subdir.append('thinlto') else: subdir.append('lto') - if settings.RELOCATABLE: + if settings.RELOCATABLE or settings.MAIN_MODULE: subdir.append('pic') if subdir: path = Path(path, '-'.join(subdir)) diff --git a/tools/compile.py b/tools/compile.py index 4380af51df5ef..31c5a26be11d4 100644 --- a/tools/compile.py +++ b/tools/compile.py @@ -50,10 +50,10 @@ def get_clang_flags(user_args): if '-mbulk-memory' not in user_args: flags.append('-mbulk-memory') - if settings.RELOCATABLE and '-fPIC' not in user_args: + if (settings.MAIN_MODULE or settings.RELOCATABLE) and '-fPIC' not in user_args: flags.append('-fPIC') - if settings.RELOCATABLE or settings.LINKABLE or '-fPIC' in user_args: + if settings.MAIN_MODULE or settings.RELOCATABLE or settings.LINKABLE or '-fPIC' in user_args: if not any(a.startswith('-fvisibility') for a in user_args): # For relocatable code we default to visibility=default in emscripten even # though the upstream backend defaults visibility=hidden. This matches the diff --git a/tools/emscripten.py b/tools/emscripten.py index 871375e3356de..0ba9313e1081b 100644 --- a/tools/emscripten.py +++ b/tools/emscripten.py @@ -374,8 +374,8 @@ def emscript(in_wasm, out_wasm, outfile_js, js_syms, finalize=True, base_metadat if settings.INITIAL_TABLE == -1: settings.INITIAL_TABLE = dylink_sec.table_size + 1 - if settings.ASYNCIFY == 1: - metadata.imports += ['__asyncify_state', '__asyncify_data'] + if settings.MAIN_MODULE and settings.ASYNCIFY == 1: + metadata.imports += ['__asyncify_state', '__asyncify_data'] if metadata.invoke_funcs: settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += ['$getWasmTableEntry'] @@ -902,7 +902,7 @@ def install_debug_wrapper(sym): return False # Likewise `__trap` can occur before the runtime is initialized since it is used in # abort. - return sym != '__trap' + return sym not in ['__trap', 'emscripten_get_sbrk_ptr'] def should_export(sym): diff --git a/tools/link.py b/tools/link.py index 040764ccc5e72..39662c42d29e8 100644 --- a/tools/link.py +++ b/tools/link.py @@ -413,7 +413,7 @@ def get_binaryen_passes(): passes += ['--fpcast-emu'] if settings.ASYNCIFY == 1: passes += ['--asyncify'] - if settings.MAIN_MODULE or settings.SIDE_MODULE: + if settings.RELOCATABLE or settings.MAIN_MODULE: passes += ['--pass-arg=asyncify-relocatable'] if settings.ASSERTIONS: passes += ['--pass-arg=asyncify-asserts'] @@ -520,14 +520,13 @@ def get_worker_js_suffix(): def setup_pthreads(): - if settings.RELOCATABLE: - # pthreads + dynamic linking has certain limitations - if settings.SIDE_MODULE: - diagnostics.warning('experimental', '-sSIDE_MODULE + pthreads is experimental') - elif settings.MAIN_MODULE: - diagnostics.warning('experimental', '-sMAIN_MODULE + pthreads is experimental') - elif settings.LINKABLE: - diagnostics.warning('experimental', '-sLINKABLE + pthreads is experimental') + # pthreads + dynamic linking has certain limitations + if settings.SIDE_MODULE: + diagnostics.warning('experimental', '-sSIDE_MODULE + pthreads is experimental') + elif settings.MAIN_MODULE: + diagnostics.warning('experimental', '-sMAIN_MODULE + pthreads is experimental') + elif settings.LINKABLE: + diagnostics.warning('experimental', '-sLINKABLE + pthreads is experimental') if settings.ALLOW_MEMORY_GROWTH and not settings.GROWABLE_ARRAYBUFFERS: diagnostics.warning('pthreads-mem-growth', '-pthread + ALLOW_MEMORY_GROWTH may run non-wasm code slowly, see https://github.com/WebAssembly/design/issues/1271') @@ -1153,6 +1152,7 @@ def limit_incoming_module_api(): settings.INCLUDE_FULL_LIBRARY = 1 # Called from preamble.js once the main module is instantiated. settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += ['$loadDylibs'] + settings.REQUIRED_EXPORTS += ['__stack_pointer'] if settings.MAIN_MODULE == 1 or settings.SIDE_MODULE == 1: settings.LINKABLE = 1 @@ -1171,13 +1171,11 @@ def limit_incoming_module_api(): '$registerTLSInit', ] - if settings.RELOCATABLE: + if settings.MAIN_MODULE or settings.RELOCATABLE: settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += [ '$reportUndefinedSymbols', '$relocateExports', '$GOTHandler', - '__heap_base', - '__stack_pointer', ] if settings.ASYNCIFY == 1: @@ -1395,6 +1393,7 @@ def limit_incoming_module_api(): if settings.RELOCATABLE: settings.REQUIRED_EXPORTS += ['__wasm_apply_data_relocs'] + settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += ['$wasmTable'] if settings.SIDE_MODULE and 'GLOBAL_BASE' in user_settings: diagnostics.warning('unused-command-line-argument', 'GLOBAL_BASE is not compatible with SIDE_MODULE') @@ -1532,6 +1531,7 @@ def limit_incoming_module_api(): not settings.AUTODEBUG and \ not settings.ASSERTIONS and \ not settings.RELOCATABLE and \ + not settings.MAIN_MODULE and \ settings.MINIFY_WASM_EXPORT_NAMES: settings.MINIFY_WASM_IMPORTS_AND_EXPORTS = 1 settings.MINIFY_WASM_IMPORTED_MODULES = 1 diff --git a/tools/system_libs.py b/tools/system_libs.py index 83bbc5471c23b..d07ec7c7e4c88 100644 --- a/tools/system_libs.py +++ b/tools/system_libs.py @@ -62,8 +62,8 @@ def get_base_cflags(build_dir, force_object_files=False, preprocess=True): flags = ['-g', '-sSTRICT', '-Werror'] if settings.LTO and not force_object_files: flags += ['-flto=' + settings.LTO] - if settings.RELOCATABLE: - flags += ['-sRELOCATABLE'] + if settings.RELOCATABLE or settings.MAIN_MODULE: + flags += ['-fPIC'] if preprocess: flags += ['-DEMSCRIPTEN_DYNAMIC_LINKING'] if settings.MEMORY64: @@ -1361,7 +1361,7 @@ def get_files(self): 'system.c', ]) - if settings.RELOCATABLE: + if settings.RELOCATABLE or settings.MAIN_MODULE: libc_files += files_in_path(path='system/lib/libc', filenames=['dynlink.c']) libc_files += files_in_path(