| /** |
| * @license |
| * Copyright 2020 The Emscripten Authors |
| * SPDX-License-Identifier: MIT |
| * |
| * Dynamic library loading |
| */ |
| |
| var dlopenMissingError = "'To use dlopen, you need enable dynamic linking, see https://github.com/emscripten-core/emscripten/wiki/Linking'" |
| |
| var LibraryDylink = { |
| #if RELOCATABLE |
| $resolveGlobalSymbol__internal: true, |
| $resolveGlobalSymbol__deps: ['$asmjsMangle'], |
| $resolveGlobalSymbol: function(symName, direct) { |
| var sym; |
| #if !WASM_BIGINT |
| if (direct) { |
| // First look for the orig$ symbol which is the symbols without |
| // any legalization performed. |
| sym = asmLibraryArg['orig$' + symName]; |
| } |
| #endif |
| if (!sym) { |
| sym = asmLibraryArg[symName]; |
| // Ignore 'stub' symbols that are auto-generated as part of the original |
| // `asmLibraryArg` used to instantate the main module. |
| if (sym && sym.stub) sym = undefined; |
| } |
| |
| // Check for the symbol on the Module object. This is the only |
| // way to dynamically access JS library symbols that were not |
| // referenced by the main module (and therefore not part of the |
| // initial set of symbols included in asmLibraryArg when it |
| // was declared. |
| if (!sym) { |
| sym = Module[asmjsMangle(symName)]; |
| } |
| |
| if (!sym && symName.startsWith('invoke_')) { |
| sym = createInvokeFunction(symName.split('_')[1]); |
| } |
| |
| #if !DISABLE_EXCEPTION_CATCHING |
| if (!sym && symName.startsWith("__cxa_find_matching_catch")) { |
| sym = Module["___cxa_find_matching_catch"]; |
| } |
| #endif |
| return sym; |
| }, |
| |
| $GOT: {}, |
| $CurrentModuleWeakSymbols: '=new Set({{{ JSON.stringify(Array.from(WEAK_IMPORTS)) }}})', |
| |
| // Create globals to each imported symbol. These are all initialized to zero |
| // and get assigned later in `updateGOT` |
| $GOTHandler__internal: true, |
| $GOTHandler__deps: ['$GOT', '$CurrentModuleWeakSymbols'], |
| $GOTHandler: { |
| 'get': function(obj, symName) { |
| var rtn = GOT[symName]; |
| if (!rtn) { |
| rtn = GOT[symName] = new WebAssembly.Global({'value': '{{{ POINTER_WASM_TYPE }}}', 'mutable': true}); |
| #if DYLINK_DEBUG |
| dbg("new GOT entry: " + symName); |
| #endif |
| } |
| if (!CurrentModuleWeakSymbols.has(symName)) { |
| // Any non-weak reference to a symbol marks it as `required`, which |
| // enabled `reportUndefinedSymbols` to report undefeind symbol errors |
| // correctly. |
| rtn.required = true; |
| } |
| return rtn; |
| } |
| }, |
| |
| $isInternalSym__internal: true, |
| $isInternalSym: function(symName) { |
| // TODO: find a way to mark these in the binary or avoid exporting them. |
| return [ |
| '__cpp_exception', |
| '__c_longjmp', |
| '__wasm_apply_data_relocs', |
| '__dso_handle', |
| '__tls_size', |
| '__tls_align', |
| '__set_stack_limits', |
| '_emscripten_tls_init', |
| '__wasm_init_tls', |
| '__wasm_call_ctors', |
| '__start_em_asm', |
| '__stop_em_asm', |
| ].includes(symName) |
| #if SPLIT_MODULE |
| // Exports synthesized by wasm-split should be prefixed with '%' |
| || symName[0] == '%' |
| #endif |
| ; |
| }, |
| |
| $updateGOT__internal: true, |
| $updateGOT__deps: ['$GOT', '$isInternalSym', '$addFunction'], |
| $updateGOT: function(exports, replace) { |
| #if DYLINK_DEBUG |
| dbg("updateGOT: adding " + Object.keys(exports).length + " symbols"); |
| #endif |
| for (var symName in exports) { |
| if (isInternalSym(symName)) { |
| continue; |
| } |
| |
| var value = exports[symName]; |
| #if !WASM_BIGINT |
| if (symName.startsWith('orig$')) { |
| symName = symName.split('$')[1]; |
| replace = true; |
| } |
| #endif |
| |
| if (!GOT[symName]) { |
| GOT[symName] = new WebAssembly.Global({'value': '{{{ POINTER_WASM_TYPE }}}', 'mutable': true}); |
| } |
| if (replace || GOT[symName].value == 0) { |
| #if DYLINK_DEBUG |
| dbg("updateGOT: before: " + symName + ' : ' + GOT[symName].value); |
| #endif |
| if (typeof value == 'function') { |
| GOT[symName].value = {{{ to64('addFunction(value)') }}}; |
| #if DYLINK_DEBUG |
| dbg("updateGOT: FUNC: " + symName + ' : ' + GOT[symName].value); |
| #endif |
| } else if (typeof value == {{{ POINTER_JS_TYPE }}}) { |
| GOT[symName].value = value; |
| } else { |
| err("unhandled export type for `" + symName + "`: " + (typeof value)); |
| } |
| #if DYLINK_DEBUG |
| dbg("updateGOT: after: " + symName + ' : ' + GOT[symName].value + ' (' + value + ')'); |
| #endif |
| } |
| #if DYLINK_DEBUG |
| else if (GOT[symName].value != value) { |
| dbg("updateGOT: EXISTING SYMBOL: " + symName + ' : ' + GOT[symName].value + ' (' + value + ')'); |
| } |
| #endif |
| } |
| #if DYLINK_DEBUG |
| dbg("done updateGOT"); |
| #endif |
| }, |
| |
| // Applies relocations to exported things. |
| $relocateExports__internal: true, |
| $relocateExports__deps: ['$updateGOT'], |
| $relocateExports__docs: '/** @param {boolean=} replace */', |
| $relocateExports: function(exports, memoryBase, replace) { |
| var relocated = {}; |
| |
| for (var e in exports) { |
| var value = exports[e]; |
| #if SPLIT_MODULE |
| // Do not modify exports synthesized by wasm-split |
| if (e.startsWith('%')) { |
| relocated[e] = value |
| continue; |
| } |
| #endif |
| if (typeof value == 'object') { |
| // a breaking change in the wasm spec, globals are now objects |
| // https://github.com/WebAssembly/mutable-global/issues/1 |
| value = value.value; |
| } |
| if (typeof value == {{{ POINTER_JS_TYPE }}}) { |
| value += {{{ to64('memoryBase') }}}; |
| } |
| relocated[e] = value; |
| } |
| updateGOT(relocated, replace); |
| return relocated; |
| }, |
| |
| $reportUndefinedSymbols__internal: true, |
| $reportUndefinedSymbols__deps: ['$GOT', '$resolveGlobalSymbol'], |
| $reportUndefinedSymbols: function() { |
| #if DYLINK_DEBUG |
| dbg('reportUndefinedSymbols'); |
| #endif |
| for (var symName in GOT) { |
| if (GOT[symName].value == 0) { |
| var value = resolveGlobalSymbol(symName, true) |
| if (!value && !GOT[symName].required) { |
| // Ignore undefined symbols that are imported as weak. |
| #if DYLINK_DEBUG |
| dbg('ignoring undefined weak symbol: ' + symName); |
| #endif |
| continue; |
| } |
| #if ASSERTIONS |
| assert(value, 'undefined symbol `' + symName + '`. perhaps a side module was not linked in? if this global was expected to arrive from a system library, try to build the MAIN_MODULE with EMCC_FORCE_STDLIBS=1 in the environment'); |
| #endif |
| #if DYLINK_DEBUG |
| dbg('assigning dynamic symbol from main module: ' + symName + ' -> ' + prettyPrint(value)); |
| #endif |
| if (typeof value == 'function') { |
| /** @suppress {checkTypes} */ |
| GOT[symName].value = {{{ to64('addFunction(value, value.sig)') }}}; |
| #if DYLINK_DEBUG |
| dbg('assigning table entry for : ' + symName + ' -> ' + GOT[symName].value); |
| #endif |
| } else if (typeof value == 'number') { |
| GOT[symName].value = {{{ to64('value') }}}; |
| #if MEMORY64 |
| } else if (typeof value == 'bigint') { |
| GOT[symName].value = value; |
| #endif |
| } else { |
| throw new Error('bad export type for `' + symName + '`: ' + (typeof value)); |
| } |
| } |
| } |
| #if DYLINK_DEBUG |
| dbg('done reportUndefinedSymbols'); |
| #endif |
| }, |
| #endif |
| |
| #if !MAIN_MODULE |
| #if !ALLOW_UNIMPLEMENTED_SYSCALLS |
| _dlopen_js__deps: [function() { error(dlopenMissingError); }], |
| _emscripten_dlopen_js__deps: [function() { error(dlopenMissingError); }], |
| _dlsym_js__deps: [function() { error(dlopenMissingError); }], |
| #else |
| $dlopenMissingError: `= ${dlopenMissingError}`, |
| _dlopen_js__deps: ['$dlopenMissingError'], |
| _emscripten_dlopen_js__deps: ['$dlopenMissingError'], |
| _dlsym_js__deps: ['$dlopenMissingError'], |
| #endif |
| _dlopen_js: function(filename, flag) { |
| abort(dlopenMissingError); |
| }, |
| _emscripten_dlopen_js: function(handle, onsuccess, onerror, user_data) { |
| abort(dlopenMissingError); |
| }, |
| _dlsym_js: function(handle, symbol) { |
| abort(dlopenMissingError); |
| }, |
| _dlinit: function(main_dso_handle) {}, |
| #else // MAIN_MODULE != 0 |
| // dynamic linker/loader (a-la ld.so on ELF systems) |
| $LDSO: { |
| // name -> dso [refcount, name, module, global]; Used by dlopen |
| loadedLibsByName: {}, |
| // handle -> dso; Used by dlsym |
| loadedLibsByHandle: {}, |
| }, |
| |
| $dlSetError__internal: true, |
| $dlSetError__deps: ['__dl_seterr', '$allocateUTF8OnStack'], |
| $dlSetError: function(msg) { |
| withStackSave(function() { |
| var cmsg = allocateUTF8OnStack(msg); |
| ___dl_seterr(cmsg, 0); |
| }); |
| }, |
| |
| // Dynamic version of shared.py:make_invoke. This is needed for invokes |
| // that originate from side modules since these are not known at JS |
| // generation time. |
| $createInvokeFunction__internal: true, |
| $createInvokeFunction__deps: ['$dynCall', 'setThrew'], |
| $createInvokeFunction: function(sig) { |
| return function() { |
| var sp = stackSave(); |
| try { |
| return dynCall(sig, arguments[0], Array.prototype.slice.call(arguments, 1)); |
| } catch(e) { |
| stackRestore(sp); |
| // Exceptions thrown from C++ exception will be integer numbers. |
| // longjmp will throw the number Infinity. Re-throw other types of |
| // exceptions using a compact and fast check. |
| if (e !== e+0) throw e; |
| _setThrew(1, 0); |
| } |
| } |
| }, |
| |
| // We support some amount of allocation during startup in the case of |
| // dynamic linking, which needs to allocate memory for dynamic libraries that |
| // are loaded. That has to happen before the main program can start to run, |
| // because the main program needs those linked in before it runs (so we can't |
| // use normally malloc from the main program to do these allocations). |
| // |
| // 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', '$zeroMemory'], |
| $getMemory: function(size) { |
| // After the runtime is initialized, we must only use sbrk() normally. |
| #if DYLINK_DEBUG |
| dbg("getMemory: " + size + " runtimeInitialized=" + runtimeInitialized); |
| #endif |
| if (runtimeInitialized) { |
| // Currently we don't support freeing of static data when modules are |
| // unloaded via dlclose. This function is tagged as `noleakcheck` to |
| // avoid having this reported as leak. |
| return zeroMemory(_malloc(size), size); |
| } |
| var ret = ___heap_base; |
| var end = (ret + size + 15) & -16; |
| #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') }}}; |
| return ret; |
| }, |
| |
| // returns the side module metadata as an object |
| // { memorySize, memoryAlign, tableSize, tableAlign, neededDynlibs} |
| $getDylinkMetadata__internal: true, |
| $getDylinkMetadata: function(binary) { |
| var offset = 0; |
| var end = 0; |
| |
| function getU8() { |
| return binary[offset++]; |
| } |
| |
| function getLEB() { |
| var ret = 0; |
| var mul = 1; |
| while (1) { |
| var byte = binary[offset++]; |
| ret += ((byte & 0x7f) * mul); |
| mul *= 0x80; |
| if (!(byte & 0x80)) break; |
| } |
| return ret; |
| } |
| |
| function getString() { |
| var len = getLEB(); |
| offset += len; |
| return UTF8ArrayToString(binary, offset - len, len); |
| } |
| |
| /** @param {string=} message */ |
| function failIf(condition, message) { |
| if (condition) throw new Error(message); |
| } |
| |
| var name = 'dylink.0'; |
| if (binary instanceof WebAssembly.Module) { |
| var dylinkSection = WebAssembly.Module.customSections(binary, name); |
| if (dylinkSection.length === 0) { |
| name = 'dylink' |
| dylinkSection = WebAssembly.Module.customSections(binary, name); |
| } |
| failIf(dylinkSection.length === 0, 'need dylink section'); |
| binary = new Uint8Array(dylinkSection[0]); |
| end = binary.length |
| } else { |
| var int32View = new Uint32Array(new Uint8Array(binary.subarray(0, 24)).buffer); |
| #if SUPPORT_BIG_ENDIAN |
| var magicNumberFound = int32View[0] == 0x6d736100 || int32View[0] == 0x0061736d; |
| #else |
| var magicNumberFound = int32View[0] == 0x6d736100; |
| #endif |
| failIf(!magicNumberFound, 'need to see wasm magic number'); // \0asm |
| // we should see the dylink custom section right after the magic number and wasm version |
| failIf(binary[8] !== 0, 'need the dylink section to be first') |
| offset = 9; |
| var section_size = getLEB(); //section size |
| end = offset + section_size; |
| name = getString(); |
| } |
| |
| var customSection = { neededDynlibs: [], tlsExports: new Set(), weakImports: new Set() }; |
| if (name == 'dylink') { |
| customSection.memorySize = getLEB(); |
| customSection.memoryAlign = getLEB(); |
| customSection.tableSize = getLEB(); |
| customSection.tableAlign = getLEB(); |
| // shared libraries this module needs. We need to load them first, so that |
| // current module could resolve its imports. (see tools/shared.py |
| // WebAssembly.make_shared_library() for "dylink" section extension format) |
| var neededDynlibsCount = getLEB(); |
| for (var i = 0; i < neededDynlibsCount; ++i) { |
| var libname = getString(); |
| customSection.neededDynlibs.push(libname); |
| } |
| } else { |
| failIf(name !== 'dylink.0'); |
| var WASM_DYLINK_MEM_INFO = 0x1; |
| var WASM_DYLINK_NEEDED = 0x2; |
| var WASM_DYLINK_EXPORT_INFO = 0x3; |
| var WASM_DYLINK_IMPORT_INFO = 0x4; |
| var WASM_SYMBOL_TLS = 0x100; |
| var WASM_SYMBOL_BINDING_MASK = 0x3; |
| var WASM_SYMBOL_BINDING_WEAK = 0x1; |
| while (offset < end) { |
| var subsectionType = getU8(); |
| var subsectionSize = getLEB(); |
| if (subsectionType === WASM_DYLINK_MEM_INFO) { |
| customSection.memorySize = getLEB(); |
| customSection.memoryAlign = getLEB(); |
| customSection.tableSize = getLEB(); |
| customSection.tableAlign = getLEB(); |
| } else if (subsectionType === WASM_DYLINK_NEEDED) { |
| var neededDynlibsCount = getLEB(); |
| for (var i = 0; i < neededDynlibsCount; ++i) { |
| libname = getString(); |
| customSection.neededDynlibs.push(libname); |
| } |
| } else if (subsectionType === WASM_DYLINK_EXPORT_INFO) { |
| var count = getLEB(); |
| while (count--) { |
| var symname = getString(); |
| var flags = getLEB(); |
| if (flags & WASM_SYMBOL_TLS) { |
| customSection.tlsExports.add(symname); |
| } |
| } |
| } else if (subsectionType === WASM_DYLINK_IMPORT_INFO) { |
| var count = getLEB(); |
| while (count--) { |
| var modname = getString(); |
| var symname = getString(); |
| var flags = getLEB(); |
| if ((flags & WASM_SYMBOL_BINDING_MASK) == WASM_SYMBOL_BINDING_WEAK) { |
| customSection.weakImports.add(symname); |
| } |
| } |
| } else { |
| #if ASSERTIONS |
| err('unknown dylink.0 subsection: ' + subsectionType) |
| #endif |
| // unknown subsection |
| offset += subsectionSize; |
| } |
| } |
| } |
| |
| #if ASSERTIONS |
| var tableAlign = Math.pow(2, customSection.tableAlign); |
| assert(tableAlign === 1, 'invalid tableAlign ' + tableAlign); |
| assert(offset == end); |
| #endif |
| |
| #if DYLINK_DEBUG |
| dbg('dylink needed:' + customSection.neededDynlibs); |
| #endif |
| |
| return customSection; |
| }, |
| |
| // Module.symbols <- libModule.symbols (flags.global handler) |
| $mergeLibSymbols__deps: ['$asmjsMangle'], |
| $mergeLibSymbols: function(exports, libName) { |
| // add symbols into global namespace TODO: weak linking etc. |
| for (var sym in exports) { |
| if (!exports.hasOwnProperty(sym)) { |
| continue; |
| } |
| |
| // When RTLD_GLOBAL is enable, the symbols defined by this shared object will be made |
| // available for symbol resolution of subsequently loaded shared objects. |
| // |
| // We should copy the symbols (which include methods and variables) from SIDE_MODULE to MAIN_MODULE. |
| |
| if (!asmLibraryArg.hasOwnProperty(sym)) { |
| asmLibraryArg[sym] = exports[sym]; |
| } |
| #if ASSERTIONS == 2 |
| else { |
| var curr = asmLibraryArg[sym], next = exports[sym]; |
| // don't warn on functions - might be odr, linkonce_odr, etc. |
| if (!(typeof curr == 'function' && typeof next == 'function')) { |
| err("warning: symbol '" + sym + "' from '" + libName + "' already exists (duplicate symbol? or weak linking, which isn't supported yet?)"); // + [curr, ' vs ', next]); |
| } |
| } |
| #endif |
| |
| // Export native export on the Module object. |
| // TODO(sbc): Do all users want this? Should we skip this by default? |
| var module_sym = asmjsMangle(sym); |
| if (!Module.hasOwnProperty(module_sym)) { |
| Module[module_sym] = exports[sym]; |
| } |
| #if !hasExportedSymbol('main') |
| // If the main module doesn't define main it could be defined in one of |
| // the side modules, and we need to handle the mangled named. |
| if (sym == '__main_argc_argv') { |
| Module['_main'] = exports[sym]; |
| } |
| #endif |
| } |
| }, |
| |
| #if DYLINK_DEBUG |
| $dumpTable: function() { |
| for (var i = 0; i < wasmTable.length; i++) |
| dbg('table: ' + i + ' : ' + wasmTable.get(i)); |
| }, |
| #endif |
| |
| // Loads a side module from binary data or compiled Module. Returns the module's exports or a |
| // promise that resolves to its exports if the loadAsync flag is set. |
| $loadWebAssemblyModule__docs: '/** @param {number=} handle */', |
| $loadWebAssemblyModule__deps: [ |
| '$loadDynamicLibrary', '$createInvokeFunction', '$getMemory', |
| '$relocateExports', '$resolveGlobalSymbol', '$GOTHandler', |
| '$getDylinkMetadata', '$alignMemory', '$zeroMemory', |
| '$alignMemory', '$zeroMemory', |
| '$CurrentModuleWeakSymbols', '$alignMemory', '$zeroMemory', |
| '$updateTableMap', |
| ], |
| $loadWebAssemblyModule: function(binary, flags, handle) { |
| var metadata = getDylinkMetadata(binary); |
| CurrentModuleWeakSymbols = metadata.weakImports; |
| #if ASSERTIONS |
| var originalTable = wasmTable; |
| #endif |
| |
| // loadModule loads the wasm module after all its dependencies have been loaded. |
| // can be called both sync/async. |
| function loadModule() { |
| // The first thread to load a given module needs to allocate the static |
| // table and memory regions. Later threads re-use the same table region |
| // and can ignore the memory region (since memory is shared between |
| // threads already). |
| // If `handle` is specified than it is assumed that the calling thread has |
| // 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 (firstLoad) { |
| // alignments are powers of 2 |
| var memAlign = Math.pow(2, metadata.memoryAlign); |
| // finalize alignments and verify them |
| memAlign = Math.max(memAlign, {{{ STACK_ALIGN }}}); // we at least need stack alignment |
| // prepare memory |
| var memoryBase = metadata.memorySize ? alignMemory(getMemory(metadata.memorySize + memAlign), memAlign) : 0; // TODO: add to cleanups |
| var tableBase = metadata.tableSize ? wasmTable.length : 0; |
| if (handle) { |
| {{{ makeSetValue('handle', C_STRUCTS.dso.mem_allocated, '1', 'i8') }}}; |
| {{{ makeSetValue('handle', C_STRUCTS.dso.mem_addr, 'memoryBase', '*') }}}; |
| {{{ makeSetValue('handle', C_STRUCTS.dso.mem_size, 'metadata.memorySize', 'i32') }}}; |
| {{{ makeSetValue('handle', C_STRUCTS.dso.table_addr, 'tableBase', '*') }}}; |
| {{{ makeSetValue('handle', C_STRUCTS.dso.table_size, 'metadata.tableSize', 'i32') }}}; |
| } |
| } else { |
| memoryBase = {{{ makeGetValue('handle', C_STRUCTS.dso.mem_addr, '*') }}}; |
| tableBase = {{{ makeGetValue('handle', C_STRUCTS.dso.table_addr, '*') }}}; |
| } |
| |
| var tableGrowthNeeded = tableBase + metadata.tableSize - wasmTable.length; |
| if (tableGrowthNeeded > 0) { |
| #if DYLINK_DEBUG |
| dbg("loadModule: growing table: " + tableGrowthNeeded); |
| #endif |
| wasmTable.grow(tableGrowthNeeded); |
| } |
| #if DYLINK_DEBUG |
| dbg("loadModule: memory[" + memoryBase + ":" + (memoryBase + metadata.memorySize) + "]" + |
| " table[" + tableBase + ":" + (tableBase + metadata.tableSize) + "]"); |
| #endif |
| |
| // This is the export map that we ultimately return. We declare it here |
| // so it can be used within resolveSymbol. We resolve symbols against |
| // this local symbol map in the case there they are not present on the |
| // global Module object. We need this fallback because: |
| // a) Modules sometime need to import their own symbols |
| // b) Symbols from side modules are not always added to the global namespace. |
| var moduleExports; |
| |
| function resolveSymbol(sym) { |
| var resolved = resolveGlobalSymbol(sym, false); |
| if (!resolved) { |
| resolved = moduleExports[sym]; |
| } |
| #if ASSERTIONS |
| assert(resolved, 'undefined symbol `' + sym + '`. perhaps a side module was not linked in? if this global was expected to arrive from a system library, try to build the MAIN_MODULE with EMCC_FORCE_STDLIBS=1 in the environment'); |
| #endif |
| return resolved; |
| } |
| |
| // TODO kill ↓↓↓ (except "symbols local to this module", it will likely be |
| // not needed if we require that if A wants symbols from B it has to link |
| // to B explicitly: similarly to -Wl,--no-undefined) |
| // |
| // wasm dynamic libraries are pure wasm, so they cannot assist in |
| // their own loading. When side module A wants to import something |
| // provided by a side module B that is loaded later, we need to |
| // add a layer of indirection, but worse, we can't even tell what |
| // to add the indirection for, without inspecting what A's imports |
| // are. To do that here, we use a JS proxy (another option would |
| // be to inspect the binary directly). |
| var proxyHandler = { |
| 'get': function(stubs, prop) { |
| // symbols that should be local to this module |
| switch (prop) { |
| case '__memory_base': |
| return {{{ to64('memoryBase') }}}; |
| case '__table_base': |
| return {{{ to64('tableBase') }}}; |
| #if MEMORY64 |
| #if MEMORY64 == 2 |
| case '__memory_base32': |
| return memoryBase; |
| #endif |
| case '__table_base32': |
| return tableBase; |
| #endif |
| } |
| if (prop in asmLibraryArg) { |
| // No stub needed, symbol already exists in symbol table |
| return asmLibraryArg[prop]; |
| } |
| // Return a stub function that will resolve the symbol |
| // when first called. |
| if (!(prop in stubs)) { |
| var resolved; |
| stubs[prop] = function() { |
| if (!resolved) resolved = resolveSymbol(prop); |
| return resolved.apply(null, arguments); |
| }; |
| } |
| return stubs[prop]; |
| } |
| }; |
| var proxy = new Proxy({}, proxyHandler); |
| var info = { |
| 'GOT.mem': new Proxy({}, GOTHandler), |
| 'GOT.func': new Proxy({}, GOTHandler), |
| 'env': proxy, |
| {{{ WASI_MODULE_NAME }}}: proxy, |
| }; |
| |
| function postInstantiation(instance) { |
| #if ASSERTIONS |
| // the table should be unchanged |
| assert(wasmTable === originalTable); |
| #endif |
| // add new entries to functionsInTableMap |
| updateTableMap(tableBase, metadata.tableSize); |
| moduleExports = relocateExports(instance.exports, memoryBase); |
| #if ASYNCIFY |
| moduleExports = Asyncify.instrumentWasmExports(moduleExports); |
| #endif |
| if (!flags.allowUndefined) { |
| reportUndefinedSymbols(); |
| } |
| #if STACK_OVERFLOW_CHECK >= 2 |
| if (moduleExports['__set_stack_limits']) { |
| #if USE_PTHREADS |
| // When we are on an uninitialized pthread we delay calling |
| // __set_stack_limits until $setDylinkStackLimits. |
| if (!ENVIRONMENT_IS_PTHREAD || runtimeInitialized) |
| #endif |
| moduleExports['__set_stack_limits']({{{ to64('_emscripten_stack_get_base()') }}}, {{{ to64('_emscripten_stack_get_end()') }}}); |
| } |
| #endif |
| |
| #if MAIN_MODULE |
| function addEmAsm(addr, body) { |
| var args = []; |
| var arity = 0; |
| for (; arity < 16; arity++) { |
| if (body.indexOf('$' + arity) != -1) { |
| args.push('$' + arity); |
| } else { |
| break; |
| } |
| } |
| args = args.join(','); |
| var func = '(' + args +' ) => { ' + body + '};' |
| #if DYLINK_DEBUG |
| dbg('adding new EM_ASM constant at: ' + ptrToString(start)); |
| #endif |
| {{{ makeEval('ASM_CONSTS[start] = eval(func)') }}}; |
| } |
| |
| // Add any EM_ASM function that exist in the side module |
| if ('__start_em_asm' in moduleExports) { |
| var start = moduleExports['__start_em_asm']; |
| var stop = moduleExports['__stop_em_asm']; |
| {{{ from64('start') }}} |
| {{{ from64('stop') }}} |
| while (start < stop) { |
| var jsString = UTF8ToString(start); |
| addEmAsm(start, jsString); |
| start = HEAPU8.indexOf(0, start) + 1; |
| } |
| } |
| #endif |
| |
| // initialize the module |
| #if USE_PTHREADS |
| // Only one thread should call __wasm_call_ctors, but all threads need |
| // to call _emscripten_tls_init |
| registerTLSInit(moduleExports['_emscripten_tls_init'], instance.exports, metadata) |
| if (firstLoad) { |
| #endif |
| var applyRelocs = moduleExports['__wasm_apply_data_relocs']; |
| if (applyRelocs) { |
| if (runtimeInitialized) { |
| #if DYLINK_DEBUG |
| dbg('applyRelocs'); |
| #endif |
| applyRelocs(); |
| } else { |
| __RELOC_FUNCS__.push(applyRelocs); |
| } |
| } |
| var init = moduleExports['__wasm_call_ctors']; |
| if (init) { |
| if (runtimeInitialized) { |
| init(); |
| } else { |
| // we aren't ready to run compiled code yet |
| __ATINIT__.push(init); |
| } |
| } |
| #if USE_PTHREADS |
| } |
| #endif |
| return moduleExports; |
| } |
| |
| if (flags.loadAsync) { |
| if (binary instanceof WebAssembly.Module) { |
| var instance = new WebAssembly.Instance(binary, info); |
| return Promise.resolve(postInstantiation(instance)); |
| } |
| return WebAssembly.instantiate(binary, info).then(function(result) { |
| return postInstantiation(result.instance); |
| }); |
| } |
| |
| var module = binary instanceof WebAssembly.Module ? binary : new WebAssembly.Module(binary); |
| var instance = new WebAssembly.Instance(module, info); |
| return postInstantiation(instance); |
| } |
| |
| // now load needed libraries and the module itself. |
| if (flags.loadAsync) { |
| return metadata.neededDynlibs.reduce(function(chain, dynNeeded) { |
| return chain.then(function() { |
| return loadDynamicLibrary(dynNeeded, flags); |
| }); |
| }, Promise.resolve()).then(function() { |
| return loadModule(); |
| }); |
| } |
| |
| metadata.neededDynlibs.forEach(function(dynNeeded) { |
| loadDynamicLibrary(dynNeeded, flags); |
| }); |
| return loadModule(); |
| }, |
| |
| #if STACK_OVERFLOW_CHECK >= 2 && USE_PTHREADS |
| // With USE_PTHREADS we load libraries before we are running a pthread and |
| // therefore before we have a stack. Instead we delay calling |
| // `__set_stack_limits` until we start running a thread. We also need to call |
| // this again for each new thread that the runs on a worker (since each thread |
| // has its own separate stack region). |
| $setDylinkStackLimits: function(stackTop, stackMax) { |
| for (var name in LDSO.loadedLibsByName) { |
| #if DYLINK_DEBUG |
| dbg('setDylinkStackLimits[' + name + ']'); |
| #endif |
| var lib = LDSO.loadedLibsByName[name]; |
| if (lib.module['__set_stack_limits']) { |
| lib.module['__set_stack_limits'](stackTop, stackMax); |
| } |
| } |
| }, |
| #endif |
| |
| // loadDynamicLibrary loads dynamic library @ lib URL / path and returns |
| // handle for loaded DSO. |
| // |
| // Several flags affect the loading: |
| // |
| // - if flags.global=true, symbols from the loaded library are merged into global |
| // process namespace. Flags.global is thus similar to RTLD_GLOBAL in ELF. |
| // |
| // - if flags.nodelete=true, the library will be never unloaded. Flags.nodelete |
| // is thus similar to RTLD_NODELETE in ELF. |
| // |
| // - if flags.loadAsync=true, the loading is performed asynchronously and |
| // loadDynamicLibrary returns corresponding promise. |
| // |
| // - if flags.fs is provided, it is used as FS-like interface to load library data. |
| // By default, when flags.fs=undefined, native loading capabilities of the |
| // environment are used. |
| // |
| // If a library was already loaded, it is not loaded a second time. However |
| // flags.global and flags.nodelete are handled every time a load request is made. |
| // Once a library becomes "global" or "nodelete", it cannot be removed or unloaded. |
| $loadDynamicLibrary__deps: ['$LDSO', '$loadWebAssemblyModule', '$asmjsMangle', '$isInternalSym', '$mergeLibSymbols'], |
| $loadDynamicLibrary__docs: '/** @param {number=} handle */', |
| $loadDynamicLibrary: function(lib, flags, handle) { |
| #if DYLINK_DEBUG |
| dbg('loadDynamicLibrary: ' + lib + ' handle:' + handle); |
| dbg('existing: ' + Object.keys(LDSO.loadedLibsByName)); |
| #endif |
| // when loadDynamicLibrary did not have flags, libraries were loaded |
| // globally & permanently |
| flags = flags || {global: true, nodelete: true} |
| |
| var dso = LDSO.loadedLibsByName[lib]; |
| if (dso) { |
| // the library is being loaded or has been loaded already. |
| // |
| // however it could be previously loaded only locally and if we get |
| // load request with global=true we have to make it globally visible now. |
| if (flags.global && !dso.global) { |
| dso.global = true; |
| if (dso.module !== 'loading') { |
| // ^^^ if module is 'loading' - symbols merging will be eventually done by the loader. |
| mergeLibSymbols(dso.module, lib) |
| } |
| } |
| // same for "nodelete" |
| if (flags.nodelete && dso.refcount !== Infinity) { |
| dso.refcount = Infinity; |
| } |
| dso.refcount++ |
| if (handle) { |
| LDSO.loadedLibsByHandle[handle] = dso; |
| } |
| return flags.loadAsync ? Promise.resolve(true) : true; |
| } |
| |
| // allocate new DSO |
| dso = { |
| refcount: flags.nodelete ? Infinity : 1, |
| name: lib, |
| module: 'loading', |
| global: flags.global, |
| }; |
| LDSO.loadedLibsByName[lib] = dso; |
| if (handle) { |
| LDSO.loadedLibsByHandle[handle] = dso; |
| } |
| |
| // libData <- libFile |
| function loadLibData(libFile) { |
| // for wasm, we can use fetch for async, but for fs mode we can only imitate it |
| if (flags.fs && flags.fs.findObject(libFile)) { |
| var libData = flags.fs.readFile(libFile, {encoding: 'binary'}); |
| if (!(libData instanceof Uint8Array)) { |
| libData = new Uint8Array(libData); |
| } |
| return flags.loadAsync ? Promise.resolve(libData) : libData; |
| } |
| |
| libFile = locateFile(libFile); |
| if (flags.loadAsync) { |
| return new Promise(function(resolve, reject) { |
| readAsync(libFile, (data) => resolve(new Uint8Array(data)), reject); |
| }); |
| } |
| |
| // load the binary synchronously |
| if (!readBinary) { |
| throw new Error(libFile + ': file not found, and synchronous loading of external files is not available'); |
| } |
| return readBinary(libFile); |
| } |
| |
| // libModule <- lib |
| function getLibModule() { |
| // lookup preloaded cache first |
| if (typeof preloadedWasm != 'undefined' && preloadedWasm[lib]) { |
| var libModule = preloadedWasm[lib]; |
| return flags.loadAsync ? Promise.resolve(libModule) : libModule; |
| } |
| |
| // module not preloaded - load lib data and create new module from it |
| if (flags.loadAsync) { |
| return loadLibData(lib).then(function(libData) { |
| return loadWebAssemblyModule(libData, flags, handle); |
| }); |
| } |
| |
| return loadWebAssemblyModule(loadLibData(lib), flags, handle); |
| } |
| |
| // module for lib is loaded - update the dso & global namespace |
| function moduleLoaded(libModule) { |
| if (dso.global) { |
| mergeLibSymbols(libModule, lib); |
| } |
| dso.module = libModule; |
| } |
| |
| if (flags.loadAsync) { |
| #if DYLINK_DEBUG |
| dbg("loadDynamicLibrary: done (async)"); |
| #endif |
| return getLibModule().then(function(libModule) { |
| moduleLoaded(libModule); |
| return true; |
| }); |
| } |
| |
| moduleLoaded(getLibModule()); |
| #if DYLINK_DEBUG |
| dbg("loadDynamicLibrary: done"); |
| #endif |
| return true; |
| }, |
| |
| $preloadDylibs__internal: true, |
| $preloadDylibs__deps: ['$loadDynamicLibrary', '$reportUndefinedSymbols'], |
| $preloadDylibs: function() { |
| #if DYLINK_DEBUG |
| dbg('preloadDylibs'); |
| #endif |
| if (!dynamicLibraries.length) { |
| #if DYLINK_DEBUG |
| dbg('preloadDylibs: no libraries to preload'); |
| #endif |
| reportUndefinedSymbols(); |
| return; |
| } |
| |
| // Load binaries asynchronously |
| addRunDependency('preloadDylibs'); |
| dynamicLibraries.reduce(function(chain, lib) { |
| return chain.then(function() { |
| return loadDynamicLibrary(lib, {loadAsync: true, global: true, nodelete: true, allowUndefined: true}); |
| }); |
| }, Promise.resolve()).then(function() { |
| // we got them all, wonderful |
| reportUndefinedSymbols(); |
| removeRunDependency('preloadDylibs'); |
| #if DYLINK_DEBUG |
| dbg('preloadDylibs done!'); |
| #endif |
| }); |
| }, |
| |
| // void* dlopen(const char* filename, int flags); |
| $dlopenInternal__deps: ['$FS', '$ENV', '$dlSetError', '$PATH'], |
| $dlopenInternal: function(handle, jsflags) { |
| // void *dlopen(const char *file, int mode); |
| // http://pubs.opengroup.org/onlinepubs/009695399/functions/dlopen.html |
| var filename = UTF8ToString(handle + {{{ C_STRUCTS.dso.name }}}); |
| var flags = {{{ makeGetValue('handle', C_STRUCTS.dso.flags, 'i32') }}}; |
| #if DYLINK_DEBUG |
| dbg('dlopenInternal: ' + filename); |
| #endif |
| filename = PATH.normalize(filename); |
| var searchpaths = []; |
| |
| var isValidFile = (filename) => { |
| var target = FS.findObject(filename); |
| return target && !target.isFolder && !target.isDevice; |
| }; |
| |
| if (!isValidFile(filename)) { |
| if (ENV['LD_LIBRARY_PATH']) { |
| searchpaths = ENV['LD_LIBRARY_PATH'].split(':'); |
| } |
| |
| for (var ident in searchpaths) { |
| var searchfile = PATH.join2(searchpaths[ident], filename); |
| if (isValidFile(searchfile)) { |
| filename = searchfile; |
| break; |
| } |
| } |
| } |
| |
| // We don't care about RTLD_NOW and RTLD_LAZY. |
| var combinedFlags = { |
| global: Boolean(flags & {{{ cDefine('RTLD_GLOBAL') }}}), |
| nodelete: Boolean(flags & {{{ cDefine('RTLD_NODELETE') }}}), |
| loadAsync: jsflags.loadAsync, |
| fs: jsflags.fs, |
| } |
| |
| if (jsflags.loadAsync) { |
| return loadDynamicLibrary(filename, combinedFlags, handle); |
| } |
| |
| try { |
| return loadDynamicLibrary(filename, combinedFlags, handle) |
| } catch (e) { |
| #if ASSERTIONS |
| err('Error in loading dynamic library ' + filename + ": " + e); |
| #endif |
| dlSetError('Could not load dynamic lib: ' + filename + '\n' + e); |
| return 0; |
| } |
| }, |
| |
| _dlopen_js__deps: ['$dlopenInternal'], |
| _dlopen_js__sig: 'pp', |
| _dlopen_js: function(handle) { |
| #if ASYNCIFY |
| return Asyncify.handleSleep(function(wakeUp) { |
| var jsflags = { |
| loadAsync: true, |
| fs: FS, // load libraries from provided filesystem |
| } |
| var promise = dlopenInternal(handle, jsflags); |
| promise.then(wakeUp).catch(function() { wakeUp(0) }); |
| }); |
| #else |
| var jsflags = { |
| loadAsync: false, |
| fs: FS, // load libraries from provided filesystem |
| } |
| return dlopenInternal(handle, jsflags); |
| #endif |
| }, |
| |
| // Async version of dlopen. |
| _emscripten_dlopen_js__deps: ['$dlopenInternal', '$callUserCallback', '$dlSetError'], |
| _emscripten_dlopen_js__sig: 'vppp', |
| _emscripten_dlopen_js: function(handle, onsuccess, onerror, user_data) { |
| /** @param {Object=} e */ |
| function errorCallback(e) { |
| var filename = UTF8ToString({{{ makeGetValue('handle', C_STRUCTS.dso.name, '*') }}}); |
| dlSetError('Could not load dynamic lib: ' + filename + '\n' + e); |
| {{{ runtimeKeepalivePop() }}} |
| callUserCallback(function () { {{{ makeDynCall('vii', 'onerror') }}}(handle, user_data); }); |
| } |
| function successCallback() { |
| {{{ runtimeKeepalivePop() }}} |
| callUserCallback(function () { {{{ makeDynCall('vii', 'onsuccess') }}}(handle, user_data); }); |
| } |
| |
| {{{ runtimeKeepalivePush() }}} |
| var promise = dlopenInternal(handle, { loadAsync: true }); |
| if (promise) { |
| promise.then(successCallback, errorCallback); |
| } else { |
| errorCallback(); |
| } |
| }, |
| |
| // void* dlsym(void* handle, const char* symbol); |
| _dlsym_js__deps: ['$dlSetError'], |
| _dlsym_js__sig: 'ppp', |
| _dlsym_js: function(handle, symbol) { |
| // void *dlsym(void *restrict handle, const char *restrict name); |
| // http://pubs.opengroup.org/onlinepubs/009695399/functions/dlsym.html |
| symbol = UTF8ToString(symbol); |
| var result; |
| |
| if (handle == {{{ cDefine('RTLD_DEFAULT') }}}) { |
| result = resolveGlobalSymbol(symbol, true); |
| if (!result) { |
| dlSetError('Tried to lookup unknown symbol "' + symbol + '" in dynamic lib: RTLD_DEFAULT'); |
| return 0; |
| } |
| } else { |
| var lib = LDSO.loadedLibsByHandle[handle]; |
| #if ASSERTIONS |
| assert(lib, 'Tried to dlsym() from an unopened handle: ' + handle); |
| #endif |
| if (!lib.module.hasOwnProperty(symbol)) { |
| dlSetError('Tried to lookup unknown symbol "' + symbol + '" in dynamic lib: ' + lib.name) |
| return 0; |
| } |
| #if !WASM_BIGINT |
| result = lib.module['orig$' + symbol]; |
| if (!result) |
| #endif |
| result = lib.module[symbol]; |
| } |
| |
| if (typeof result == 'function') { |
| #if DYLINK_DEBUG |
| dbg('dlsym: ' + symbol + ' getting table slot for: ' + result); |
| #endif |
| |
| #if ASYNCIFY |
| // Asyncify wraps exports, and we need to look through those wrappers. |
| if ('orig' in result) { |
| result = result.orig; |
| } |
| #endif |
| // Insert the function into the wasm table. If its a direct wasm function |
| // the second argument will not be needed. If its a JS function we rely |
| // on the `sig` attribute being set based on the `<func>__sig` specified |
| // in library JS file. |
| result = addFunction(result, result.sig); |
| } |
| #if DYLINK_DEBUG |
| dbg('dlsym: ' + symbol + ' -> ' + result); |
| #endif |
| return result; |
| }, |
| |
| _dlinit: function(main_dso_handle) { |
| var dso = { |
| refcount: Infinity, // = nodelete |
| name: '__main__', |
| module: Module['asm'], |
| global: true |
| }; |
| LDSO.loadedLibsByName[dso.name] = dso; |
| LDSO.loadedLibsByHandle[main_dso_handle] = dso; |
| } |
| #endif // MAIN_MODULE != 0 |
| }; |
| |
| mergeInto(LibraryManager.library, LibraryDylink); |