| // ========================================================================== |
| // Dynamic library loading |
| // |
| // ========================================================================== |
| |
| 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]; |
| } |
| |
| // 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]); |
| } |
| |
| return sym; |
| }, |
| |
| $GOT: {}, |
| |
| // Create globals to each imported symbol. These are all initialized to zero |
| // and get assigned later in `updateGOT` |
| $GOTHandler__internal: true, |
| $GOTHandler__deps: ['$GOT'], |
| $GOTHandler: { |
| 'get': function(obj, symName) { |
| if (!GOT[symName]) { |
| GOT[symName] = new WebAssembly.Global({'value': '{{{ POINTER_TYPE }}}', 'mutable': true}); |
| #if DYLINK_DEBUG |
| err("new GOT entry: " + symName); |
| #endif |
| } |
| return GOT[symName] |
| } |
| }, |
| |
| $isInternalSym__internal: true, |
| $isInternalSym: function(symName) { |
| // TODO: find a way to mark these in the binary or avoid exporting them. |
| return [ |
| '__cpp_exception', |
| '__wasm_apply_data_relocs', |
| '__dso_handle', |
| '__tls_size', |
| '__tls_align', |
| '__set_stack_limits', |
| 'emscripten_tls_init', |
| '__wasm_init_tls', |
| '__wasm_call_ctors', |
| ].includes(symName) |
| #if SPLIT_MODULE |
| // Exports synthesized by wasm-split should be prefixed with '%' |
| || symName[0] == '%' |
| #endif |
| ; |
| }, |
| |
| $updateGOT__internal: true, |
| $updateGOT__deps: ['$GOT', '$isInternalSym'], |
| $updateGOT: function(exports, replace) { |
| #if DYLINK_DEBUG |
| err("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': 'i32', 'mutable': true}); |
| } |
| if (replace || GOT[symName].value == 0) { |
| if (typeof value === 'function') { |
| GOT[symName].value = addFunction(value); |
| #if DYLINK_DEBUG |
| err("updateGOT: FUNC: " + symName + ' : ' + GOT[symName].value); |
| #endif |
| } else if (typeof value === 'number') { |
| GOT[symName].value = value; |
| } else if (typeof value === 'bigint') { |
| GOT[symName].value = Number(value); |
| } else { |
| err("unhandled export type for `" + symName + "`: " + (typeof value)); |
| } |
| #if DYLINK_DEBUG |
| err("updateGOT: " + symName + ' : ' + GOT[symName].value); |
| #endif |
| } |
| #if DYLINK_DEBUG |
| else if (GOT[symName].value != value) { |
| err("updateGOT: EXISTING SYMBOL: " + symName + ' : ' + GOT[symName].value + " " + value); |
| } |
| #endif |
| } |
| #if DYLINK_DEBUG |
| err("done updateGOT"); |
| #endif |
| }, |
| |
| // Applies relocations to exported things. |
| $relocateExports__internal: true, |
| $relocateExports__deps: ['$updateGOT'], |
| $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 === 'number') { |
| value += memoryBase; |
| } |
| relocated[e] = value; |
| } |
| updateGOT(relocated, replace); |
| return relocated; |
| }, |
| |
| $reportUndefinedSymbols__internal: true, |
| $reportUndefinedSymbols__deps: ['$GOT', '$resolveGlobalSymbol'], |
| $reportUndefinedSymbols: function() { |
| #if DYLINK_DEBUG |
| err('reportUndefinedSymbols'); |
| #endif |
| for (var symName in GOT) { |
| if (GOT[symName].value == 0) { |
| var value = resolveGlobalSymbol(symName, true) |
| #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 |
| err('assigning dynamic symbol from main module: ' + symName + ' -> ' + prettyPrint(value)); |
| #endif |
| if (typeof value === 'function') { |
| GOT[symName].value = addFunction(value, value.sig); |
| #if DYLINK_DEBUG |
| err('assigning table entry for : ' + symName + ' -> ' + GOT[symName].value); |
| #endif |
| } else if (typeof value === 'number') { |
| GOT[symName].value = value; |
| } else { |
| assert(false, 'bad export type for `' + symName + '`: ' + (typeof value)); |
| } |
| } |
| } |
| #if DYLINK_DEBUG |
| err('done reportUndefinedSymbols'); |
| #endif |
| }, |
| #endif |
| |
| #if MAIN_MODULE == 0 |
| _dlopen_js: function(filename, flag) { |
| abort("To use dlopen, you need to use Emscripten's linking support, see https://github.com/emscripten-core/emscripten/wiki/Linking"); |
| }, |
| _emscripten_dlopen_js: function(filename, flags, user_data, onsuccess, onerror) { |
| abort("To use dlopen, you need to use Emscripten's linking support, see https://github.com/emscripten-core/emscripten/wiki/Linking"); |
| }, |
| _dlsym_js: function(handle, symbol) { |
| abort("To use dlopen, you need to use Emscripten's linking support, see https://github.com/emscripten-core/emscripten/wiki/Linking"); |
| }, |
| #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: ['___dl_seterr', |
| #if MINIMAL_RUNTIME |
| '$intArrayFromString' |
| #endif |
| ], |
| $dlSetError: function(msg) { |
| withStackSave(function() { |
| var cmsg = allocate(intArrayFromString(msg), ALLOC_STACK); |
| ___dl_seterr(cmsg); |
| }); |
| }, |
| |
| // 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); |
| if (e !== e+0 && e !== 'longjmp') 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. |
| $getMemory__deps: ['$GOT', '__heap_base'], |
| $getMemory: function(size) { |
| // After the runtime is initialized, we must only use sbrk() normally. |
| #if DYLINK_DEBUG |
| err("getMemory: " + size + " runtimeInitialized=" + runtimeInitialized); |
| #endif |
| if (runtimeInitialized) |
| return _malloc(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 = 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); |
| } |
| |
| 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); |
| } |
| assert(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); |
| assert(int32View[0] == 0x6d736100, 'need to see wasm magic number'); // \0asm |
| // we should see the dylink custom section right after the magic number and wasm version |
| assert(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: {} }; |
| 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 name = getString(); |
| customSection.neededDynlibs.push(name); |
| } |
| } else { |
| assert(name === 'dylink.0'); |
| var WASM_DYLINK_MEM_INFO = 0x1; |
| var WASM_DYLINK_NEEDED = 0x2; |
| var WASM_DYLINK_EXPORT_INFO = 0x3; |
| var WASM_SYMBOL_TLS = 0x100; |
| 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) { |
| var name = getString(); |
| customSection.neededDynlibs.push(name); |
| } |
| } else if (subsectionType === WASM_DYLINK_EXPORT_INFO) { |
| var count = getLEB(); |
| while (count--) { |
| var name = getString(); |
| var flags = getLEB(); |
| if (flags & WASM_SYMBOL_TLS) { |
| customSection.tlsExports[name] = 1; |
| } |
| } |
| } 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); |
| #endif |
| |
| #if DYLINK_DEBUG |
| err('dylink needed:' + customSection.neededDynlibs); |
| #endif |
| |
| assert(offset == end); |
| 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]; |
| } |
| } |
| }, |
| |
| // 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__deps: ['$loadDynamicLibrary', '$createInvokeFunction', '$getMemory', '$relocateExports', '$resolveGlobalSymbol', '$GOTHandler', '$getDylinkMetadata', '$alignMemory'], |
| $loadWebAssemblyModule: function(binary, flags) { |
| var metadata = getDylinkMetadata(binary); |
| #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() { |
| // 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 = alignMemory(getMemory(metadata.memorySize + memAlign), memAlign); // TODO: add to cleanups |
| #if DYLINK_DEBUG |
| err("loadModule: memoryBase=" + memoryBase); |
| #endif |
| // TODO: use only __memory_base and __table_base, need to update asm.js backend |
| var tableBase = wasmTable.length; |
| wasmTable.grow(metadata.tableSize); |
| // zero-initialize memory and table |
| // The static area consists of explicitly initialized data, followed by zero-initialized data. |
| // The latter may need zeroing out if the MAIN_MODULE has already used this memory area before |
| // dlopen'ing the SIDE_MODULE. Since we don't know the size of the explicitly initialized data |
| // here, we just zero the whole thing, which is suboptimal, but should at least resolve bugs |
| // from uninitialized memory. |
| #if USE_PTHREADS |
| // in a pthread the module heap was already allocated and initialized in the main thread. |
| if (!ENVIRONMENT_IS_PTHREAD) { |
| #endif |
| for (var i = memoryBase; i < memoryBase + metadata.memorySize; i++) { |
| HEAP8[i] = 0; |
| } |
| #if USE_PTHREADS |
| } |
| #endif |
| for (var i = tableBase; i < tableBase + metadata.tableSize; i++) { |
| wasmTable.set(i, null); |
| } |
| |
| // 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 memoryBase; |
| case '__table_base': |
| return tableBase; |
| } |
| 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, true); |
| 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 |
| for (var i = 0; i < metadata.tableSize; i++) { |
| var item = wasmTable.get(tableBase + i); |
| #if ASSERTIONS |
| // verify that the new table region was filled in |
| assert(item !== undefined, 'table entry was not filled in'); |
| #endif |
| // Ignore null values. |
| if (item) { |
| functionsInTableMap.set(item, tableBase + i); |
| } |
| } |
| moduleExports = relocateExports(instance.exports, memoryBase); |
| if (!flags.allowUndefined) { |
| reportUndefinedSymbols(); |
| } |
| #if STACK_OVERFLOW_CHECK >= 2 |
| moduleExports['__set_stack_limits']({{{ STACK_BASE }}} , {{{ STACK_MAX }}}); |
| #endif |
| |
| // initialize the module |
| #if USE_PTHREADS |
| // Only one thread (currently The main thread) should call |
| // __wasm_call_ctors, but all threads need to call emscripten_tls_init |
| registerTlsInit(moduleExports['emscripten_tls_init'], instance.exports, metadata) |
| if (!ENVIRONMENT_IS_PTHREAD) { |
| #endif |
| 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(); |
| }, |
| |
| // 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: function(lib, flags, handle) { |
| #if DYLINK_DEBUG |
| err('loadDynamicLibrary: ' + lib + ' handle:' + handle); |
| #endif |
| if (lib == '__main__' && !LDSO.loadedLibsByName[lib]) { |
| LDSO.loadedLibsByName[lib] = { |
| refcount: Infinity, // = nodelete |
| name: '__main__', |
| module: Module['asm'], |
| global: true |
| }; |
| } |
| |
| // 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; |
| } |
| |
| if (flags.loadAsync) { |
| return new Promise(function(resolve, reject) { |
| readAsync(libFile, function(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 (Module['preloadedWasm'] !== undefined && |
| Module['preloadedWasm'][lib] !== undefined) { |
| var libModule = Module['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); |
| }); |
| } |
| |
| return loadWebAssemblyModule(loadLibData(lib), flags); |
| } |
| |
| // 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 |
| err("loadDynamicLibrary: done (async)"); |
| #endif |
| return getLibModule().then(function(libModule) { |
| moduleLoaded(libModule); |
| return true; |
| }); |
| } |
| |
| moduleLoaded(getLibModule()); |
| #if DYLINK_DEBUG |
| err("loadDynamicLibrary: done"); |
| #endif |
| return true; |
| }, |
| |
| $preloadDylibs__internal: true, |
| $preloadDylibs__deps: ['$loadDynamicLibrary', '$reportUndefinedSymbols'], |
| $preloadDylibs: function() { |
| #if DYLINK_DEBUG |
| err('preloadDylibs'); |
| #endif |
| if (!dynamicLibraries.length) { |
| #if DYLINK_DEBUG |
| err('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 |
| err('preloadDylibs done!'); |
| #endif |
| }); |
| }, |
| |
| // void* dlopen(const char* filename, int flags); |
| $dlopenInternal__deps: ['$FS', '$ENV', '$dlSetError'], |
| $dlopenInternal: function(handle, flags, jsflags) { |
| // void *dlopen(const char *file, int mode); |
| // http://pubs.opengroup.org/onlinepubs/009695399/functions/dlopen.html |
| var searchpaths = []; |
| var filename = UTF8ToString(handle + {{{ C_STRUCTS.dso.name }}}); |
| |
| var isValidFile = function(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; |
| } |
| } |
| } |
| |
| #if DYLINK_DEBUG |
| err('dlopenInternal: ' + filename); |
| #endif |
| |
| // 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: 'iiii', |
| _dlopen_js: function(handle, flags) { |
| #if ASYNCIFY |
| return Asyncify.handleSleep(function(wakeUp) { |
| var jsflags = { |
| loadAsync: true, |
| fs: FS, // load libraries from provided filesystem |
| } |
| var promise = dlopenInternal(handle, flags, jsflags); |
| promise.then(wakeUp).catch(function() { wakeUp(0) }); |
| }); |
| #else |
| var jsflags = { |
| loadAsync: false, |
| fs: FS, // load libraries from provided filesystem |
| } |
| return dlopenInternal(handle, flags, jsflags); |
| #endif |
| }, |
| |
| // Async version of dlopen. |
| _emscripten_dlopen_js__deps: ['$dlopenInternal', '$callUserCallback', '$dlSetError', |
| #if !MINIMAL_RUNTIME |
| '$runtimeKeepalivePush', |
| '$runtimeKeepalivePop', |
| #endif |
| ], |
| _emscripten_dlopen_js__sig: 'viiiii', |
| _emscripten_dlopen_js: function(handle, flags, onsuccess, onerror) { |
| 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('vi', 'onerror') }}}(handle); }); |
| } |
| function successCallback() { |
| {{{ runtimeKeepalivePop() }}} |
| callUserCallback(function () { {{{ makeDynCall('vii', 'onsuccess') }}}(handle); }); |
| } |
| |
| {{{ runtimeKeepalivePush() }}} |
| var promise = dlopenInternal(handle, flags, { loadAsync: true }); |
| if (promise) { |
| promise.then(successCallback, errorCallback); |
| } else { |
| errorCallback(); |
| } |
| }, |
| |
| // void* dlsym(void* handle, const char* symbol); |
| _dlsym_js__deps: ['$dlSetError'], |
| _dlsym_js__sig: 'iii', |
| _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') { |
| // 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. |
| return addFunction(result, result.sig); |
| } else { |
| return result; |
| } |
| }, |
| #endif // MAIN_MODULE != 0 |
| }; |
| |
| mergeInto(LibraryManager.library, LibraryDylink); |