Buckets:
| /** | |
| * @license | |
| * Copyright 2020 The Emscripten Authors | |
| * SPDX-License-Identifier: MIT | |
| * | |
| * Dynamic library loading | |
| */ | |
| #if !MAIN_MODULE | |
| #error "library_dylink.js requires MAIN_MODULE" | |
| #endif | |
| {{{ | |
| const UNDEFINED_ADDR = to64(-1); | |
| }}} | |
| var LibraryDylink = { | |
| #if FILESYSTEM | |
| $registerWasmPlugin__deps: ['$preloadPlugins'], | |
| $registerWasmPlugin: () => { | |
| // Use string keys here for public methods to avoid minification since the | |
| // plugin consumer also uses string keys. | |
| var wasmPlugin = { | |
| promiseChainEnd: Promise.resolve(), | |
| 'canHandle': (name) => { | |
| return !Module['noWasmDecoding'] && name.endsWith('.so') | |
| }, | |
| 'handle': async (byteArray, name) => | |
| // loadWebAssemblyModule can not load modules out-of-order, so rather | |
| // than just running the promises in parallel, this makes a chain of | |
| // promises to run in series. | |
| wasmPlugin.promiseChainEnd = wasmPlugin.promiseChainEnd.then(async () => { | |
| try { | |
| var exports = await loadWebAssemblyModule(byteArray, {loadAsync: true, nodelete: true}, name, {}); | |
| } catch (error) { | |
| throw new Error(`failed to instantiate wasm: ${name}: ${error}`); | |
| } | |
| #if DYLINK_DEBUG | |
| dbg('registering preloadedWasm:', name); | |
| #endif | |
| preloadedWasm[name] = exports; | |
| return byteArray; | |
| }) | |
| }; | |
| preloadPlugins.push(wasmPlugin); | |
| }, | |
| $preloadedWasm__deps: ['$registerWasmPlugin'], | |
| $preloadedWasm__postset: ` | |
| registerWasmPlugin(); | |
| `, | |
| $preloadedWasm: {}, | |
| $replaceORIGIN__deps: ['$PATH'], | |
| $replaceORIGIN: (parentLibName, rpath) => { | |
| if (rpath.startsWith('$ORIGIN')) { | |
| // TODO: what to do if we only know the relative path of the file? It will return "." here. | |
| var origin = PATH.dirname(parentLibName); | |
| return rpath.replace('$ORIGIN', origin); | |
| } | |
| return rpath; | |
| }, | |
| #endif // FILESYSTEM | |
| $isSymbolDefined: (symName) => { | |
| // Ignore 'stub' symbols that are auto-generated as part of the original | |
| // `wasmImports` used to instantiate the main module. | |
| var existing = wasmImports[symName]; | |
| if (!existing || existing.stub) { | |
| return false; | |
| } | |
| #if ASYNCIFY | |
| // Even if a symbol exists in wasmImports, and is not itself a stub, it | |
| // could be an ASYNCIFY wrapper function that wraps a stub function. | |
| if (symName in asyncifyStubs && !asyncifyStubs[symName]) { | |
| return false; | |
| } | |
| #endif | |
| return true; | |
| }, | |
| // 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. | |
| #if !DISABLE_EXCEPTION_CATCHING || SUPPORT_LONGJMP == 'emscripten' | |
| $createInvokeFunction__internal: true, | |
| $createInvokeFunction__deps: ['$dynCall', 'setThrew', '$stackSave', '$stackRestore'], | |
| $createInvokeFunction: (sig) => (ptr, ...args) => { | |
| var sp = stackSave(); | |
| try { | |
| return dynCall(sig, ptr, args); | |
| } catch(e) { | |
| stackRestore(sp); | |
| // Create a try-catch guard that rethrows the Emscripten EH exception. | |
| // Exceptions thrown from C++ and longjmps will be an instance of | |
| // EmscriptenEH. | |
| if (!(e instanceof EmscriptenEH)) throw e; | |
| _setThrew(1, 0); | |
| #if WASM_BIGINT | |
| // In theory this if statement could be done on | |
| // creating the function, but I just added this to | |
| // save wasting code space as it only happens on exception. | |
| if (sig[0] == "j") return 0n; | |
| #endif | |
| } | |
| }, | |
| #endif | |
| // Resolve a global symbol by name. This is used during module loading to | |
| // resolve imports, and by `dlsym` when used with `RTLD_DEFAULT`. | |
| // Returns both the resolved symbol (i.e. a function or a global) along with | |
| // the canonical name of the symbol (in some cases modifying the symbol as | |
| // part of the loop process, so that actual symbol looked up has a different | |
| // name). | |
| $resolveGlobalSymbol__deps: ['$isSymbolDefined', '$createNamedFunction', | |
| #if !DISABLE_EXCEPTION_CATCHING || SUPPORT_LONGJMP == 'emscripten' | |
| '$createInvokeFunction', | |
| #endif | |
| ], | |
| $resolveGlobalSymbol__internal: true, | |
| $resolveGlobalSymbol: (symName, direct = false) => { | |
| var sym; | |
| #if !WASM_BIGINT | |
| // First look for the orig$ symbol which is the symbol without i64 | |
| // legalization performed. | |
| if (direct && ('orig$' + symName in wasmImports)) { | |
| symName = 'orig$' + symName; | |
| } | |
| #endif | |
| if (isSymbolDefined(symName)) { | |
| sym = wasmImports[symName]; | |
| } | |
| #if !DISABLE_EXCEPTION_CATCHING || SUPPORT_LONGJMP == 'emscripten' | |
| // Asm.js-style exception handling: invoke wrapper generation | |
| else if (symName.startsWith('invoke_')) { | |
| // Create (and cache) new invoke_ functions on demand. | |
| sym = wasmImports[symName] = createNamedFunction(symName, createInvokeFunction(symName.split('_')[1])); | |
| } | |
| #endif | |
| #if !DISABLE_EXCEPTION_CATCHING | |
| else if (symName.startsWith('__cxa_find_matching_catch_')) { | |
| // When the main module is linked we create whichever variants of | |
| // `__cxa_find_matching_catch_` (see jsifier.js) that we know are needed, | |
| // but a side module loaded at runtime might need different/additional | |
| // variants so we create those dynamically. | |
| sym = wasmImports[symName] = createNamedFunction(symName, (...args) => { | |
| #if MEMORY64 | |
| args = args.map(Number); | |
| #endif | |
| var rtn = findMatchingCatch(args); | |
| return {{{ to64('rtn') }}}; | |
| }); | |
| } | |
| #endif | |
| return {sym, name: symName}; | |
| }, | |
| $GOT: {}, | |
| // Proxy handler used for GOT.mem and GOT.func imports. Each of these | |
| // imports is fulfilled dynamically via the `get` method of this proxy | |
| // handler. We abuse the `target` of the Proxy in order to pass the set of | |
| // weak imports to the handler. | |
| $GOTHandler__internal: true, | |
| $GOTHandler__deps: ['$GOT'], | |
| $GOTHandler: { | |
| get(weakImports, symName) { | |
| var rtn = GOT[symName]; | |
| if (!rtn) { | |
| #if DYLINK_DEBUG == 2 | |
| dbg(`new GOT entry: ${symName}`); | |
| #endif | |
| rtn = GOT[symName] = new WebAssembly.Global({'value': '{{{ POINTER_WASM_TYPE }}}', 'mutable': true}, {{{ UNDEFINED_ADDR }}}); | |
| } | |
| if (!weakImports.has(symName)) { | |
| // Any non-weak reference to a symbol marks it as `required`, which | |
| // enabled `reportUndefinedSymbols` to report undefined symbol errors | |
| // correctly. | |
| rtn.required = true; | |
| } | |
| return rtn; | |
| } | |
| }, | |
| $isInternalSym__internal: true, | |
| $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', | |
| '__dso_handle', | |
| '__tls_size', | |
| '__tls_align', | |
| '__set_stack_limits', | |
| '_emscripten_tls_init', | |
| '__wasm_init_tls', | |
| '__wasm_call_ctors', | |
| '__start_em_asm', | |
| '__stop_em_asm', | |
| '__start_em_js', | |
| '__stop_em_js', | |
| ].includes(symName) || symName.startsWith('__em_js__') | |
| #if SPLIT_MODULE | |
| // Exports synthesized by wasm-split should be prefixed with '%' | |
| || symName[0] == '%' | |
| #endif | |
| ; | |
| }, | |
| $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`); | |
| #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 | |
| var existingEntry = GOT[symName] && GOT[symName].value != {{{ UNDEFINED_ADDR }}}; | |
| if (replace || !existingEntry) { | |
| #if DYLINK_DEBUG == 2 | |
| dbg(`updateGOT: before: ${symName} : ${GOT[symName]?.value}`); | |
| #endif | |
| var newValue; | |
| if (typeof value == 'function') { | |
| newValue = {{{ to64('addFunction(value)') }}}; | |
| } else if (typeof value.value == {{{ POINTER_JS_TYPE }}}) { | |
| newValue = value; | |
| } else { | |
| // The GOT can only contain addresses (i.e data addresses or function | |
| // addresses so we currently ignore other types export here. | |
| #if DYLINK_DEBUG | |
| dbg(`updateGOT: ignoring ${symName} due to its type: ${typeof value}`); | |
| #endif | |
| continue; | |
| } | |
| #if DYLINK_DEBUG == 2 | |
| dbg(`updateGOT: after: ${symName} : ${newValue} (${value})`); | |
| #endif | |
| GOT[symName] ??= new WebAssembly.Global({'value': '{{{ POINTER_WASM_TYPE }}}', 'mutable': true}); | |
| GOT[symName].value = newValue; | |
| } | |
| #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 | |
| }, | |
| $isImmutableGlobal__internal: true, | |
| $isImmutableGlobal: (val) => { | |
| if (val instanceof WebAssembly.Global) { | |
| try { | |
| val.value = val.value; | |
| } catch { | |
| return true; | |
| } | |
| } | |
| return false; | |
| }, | |
| // Applies relocations to exported things. | |
| $relocateExports__internal: true, | |
| $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 | |
| if (name.startsWith('%')) { | |
| return value; | |
| } | |
| #endif | |
| // Detect immutable wasm global exports. These represent data addresses | |
| // which are relative to `memoryBase` | |
| if (isImmutableGlobal(value)) { | |
| return new WebAssembly.Global({'value': '{{{ POINTER_WASM_TYPE }}}'}, value.value + {{{ to64('memoryBase') }}}); | |
| } | |
| // Return unmodified value (no relocation required). | |
| return value; | |
| } | |
| var relocated = {}; | |
| for (var e in exports) { | |
| relocated[e] = relocateExport(e, exports[e]) | |
| } | |
| return relocated; | |
| }, | |
| $reportUndefinedSymbols__internal: true, | |
| $reportUndefinedSymbols__deps: ['$GOT', '$resolveGlobalSymbol'], | |
| $reportUndefinedSymbols: () => { | |
| #if DYLINK_DEBUG | |
| dbg('reportUndefinedSymbols'); | |
| #endif | |
| for (var [symName, entry] of Object.entries(GOT)) { | |
| if (entry.value == {{{ UNDEFINED_ADDR }}}) { | |
| #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 = {{{ to64(0) }}}; | |
| 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 == 2 | |
| dbg(`assigning dynamic symbol from main module: ${symName} -> ${prettyPrint(value)}`); | |
| #endif | |
| if (typeof value == 'function') { | |
| /** @suppress {checkTypes} */ | |
| entry.value = {{{ to64('addFunction(value, value.sig)') }}}; | |
| #if DYLINK_DEBUG == 2 | |
| dbg(`assigning table entry for : ${symName} -> ${entry.value}`); | |
| #endif | |
| } else if (typeof value == 'number') { | |
| entry.value = {{{ to64('value') }}}; | |
| } else if (typeof value.value == {{{ POINTER_JS_TYPE }}}) { | |
| entry.value = value; | |
| } else { | |
| throw new Error(`bad export type for '${symName}': ${typeof value} (${value})`); | |
| } | |
| } | |
| } | |
| #if DYLINK_DEBUG | |
| dbg('done reportUndefinedSymbols'); | |
| #endif | |
| }, | |
| // dynamic linker/loader (a-la ld.so on ELF systems) | |
| $LDSO__deps: ['$newDSO'], | |
| $LDSO: { | |
| // name -> dso [refcount, name, module, global]; Used by dlopen | |
| loadedLibsByName: {}, | |
| // handle -> dso; Used by dlsym | |
| loadedLibsByHandle: {}, | |
| init() { | |
| #if ASSERTIONS | |
| // This function needs to run after the initial wasmImports object | |
| // as been created. | |
| assert(wasmImports); | |
| #endif | |
| newDSO('__main__', {{{ cDefs.RTLD_DEFAULT }}}, wasmImports); | |
| }, | |
| }, | |
| $dlSetError__internal: true, | |
| $dlSetError__deps: ['__dl_seterr', '$stringToUTF8OnStack', '$stackSave', '$stackRestore'], | |
| $dlSetError: (msg) => { | |
| #if DYLINK_DEBUG | |
| dbg('dlSetError:', msg); | |
| #endif | |
| var sp = stackSave(); | |
| var cmsg = stringToUTF8OnStack(msg); | |
| ___dl_seterr(cmsg, 0); | |
| stackRestore(sp); | |
| }, | |
| // 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', 'emscripten_get_sbrk_ptr', '__heap_base', '$alignMemory', 'calloc'], | |
| $getMemory: (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 _calloc(size, 1); | |
| } | |
| var ret = ___heap_base; | |
| // Keep __heap_base stack aligned. | |
| var end = ret + alignMemory(size, {{{ STACK_ALIGN }}}); | |
| #if ASSERTIONS | |
| //dbg(ret); | |
| //dbg(HEAP8.length); | |
| 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; | |
| // After allocating the memory from the start of the heap we need to ensure | |
| // that once the program starts it doesn't use this region. In relocatable | |
| // mode we can just update the __heap_base symbol that we are exporting to | |
| // the main module. | |
| #if PTHREADS | |
| if (!ENVIRONMENT_IS_PTHREAD) { | |
| #endif | |
| var sbrk_ptr = _emscripten_get_sbrk_ptr(); | |
| {{{ makeSetValue('sbrk_ptr', 0, 'end', '*') }}} | |
| #if PTHREADS | |
| } | |
| #endif | |
| return ret; | |
| }, | |
| // returns the side module metadata as an object | |
| // { memorySize, memoryAlign, tableSize, tableAlign, neededDynlibs} | |
| $getDylinkMetadata__deps: ['$UTF8ArrayToString'], | |
| $getDylinkMetadata__internal: true, | |
| $getDylinkMetadata: (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); | |
| } | |
| function getStringList() { | |
| var count = getLEB(); | |
| var rtn = [] | |
| while (count--) rtn.push(getString()); | |
| return rtn; | |
| } | |
| /** @param {string=} message */ | |
| function failIf(condition, message) { | |
| if (condition) throw new Error(message); | |
| } | |
| if (binary instanceof WebAssembly.Module) { | |
| var dylinkSection = WebAssembly.Module.customSections(binary, 'dylink.0'); | |
| 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; | |
| var name = getString(); | |
| failIf(name !== 'dylink.0'); | |
| } | |
| var customSection = { neededDynlibs: [], tlsExports: new Set(), weakImports: new Set(), runtimePaths: [] }; | |
| 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_DYLINK_RUNTIME_PATH = 0x5; | |
| 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) { | |
| customSection.neededDynlibs = getStringList(); | |
| } 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 (subsectionType === WASM_DYLINK_RUNTIME_PATH) { | |
| customSection.runtimePaths = getStringList(); | |
| } 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; | |
| }, | |
| #if DYNCALLS || !WASM_BIGINT | |
| $registerDynCallSymbols: (exports) => { | |
| for (var [sym, exp] of Object.entries(exports)) { | |
| if (sym.startsWith('dynCall_')) { | |
| var sig = sym.substring(8); | |
| if (!dynCalls.hasOwnProperty(sig)) { | |
| dynCalls[sig] = exp; | |
| } | |
| } | |
| } | |
| }, | |
| #endif | |
| // Module.symbols <- libModule.symbols (flags.global handler) | |
| $mergeLibSymbols__deps: ['$isSymbolDefined'], | |
| $mergeLibSymbols: (exports, libName) => { | |
| #if DYNCALLS || !WASM_BIGINT | |
| registerDynCallSymbols(exports); | |
| #endif | |
| // add symbols into global namespace TODO: weak linking etc. | |
| for (var [sym, exp] of Object.entries(exports)) { | |
| #if ASSERTIONS == 2 | |
| if (isSymbolDefined(sym)) { | |
| var curr = wasmImports[sym], next = exp; | |
| // 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 | |
| // When RTLD_GLOBAL is enabled, 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. | |
| const setImport = (target) => { | |
| #if ASYNCIFY | |
| if (target in asyncifyStubs) { | |
| asyncifyStubs[target] = exp; | |
| } | |
| #endif | |
| if (!isSymbolDefined(target)) { | |
| wasmImports[target] = exp; | |
| } | |
| } | |
| setImport(sym); | |
| #if !hasExportedSymbol('main') | |
| // Special case for handling of main symbol: If a side module exports | |
| // `main` that also acts a definition for `__main_argc_argv` and vice | |
| // versa. | |
| const main_alias = '__main_argc_argv'; | |
| if (sym == 'main') { | |
| setImport(main_alias) | |
| } | |
| if (sym == main_alias) { | |
| setImport('main') | |
| } | |
| #endif | |
| } | |
| }, | |
| #if DYLINK_DEBUG | |
| $dumpTable__deps: ['$wasmTable'], | |
| $dumpTable: () => { | |
| var len = wasmTable.length; | |
| for (var i = {{{ toIndexType(0) }}} ; i < len; 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 {string=} libName | |
| * @param {Object=} localScope | |
| * @param {number=} handle | |
| */`, | |
| $loadWebAssemblyModule__deps: [ | |
| '$loadDynamicLibrary', '$getMemory', '$updateGOT', | |
| '$relocateExports', '$resolveGlobalSymbol', '$GOTHandler', | |
| '$getDylinkMetadata', '$alignMemory', | |
| '$updateTableMap', | |
| '$wasmTable', | |
| '$addOnPostCtor', | |
| ], | |
| $loadWebAssemblyModule: (binary, flags, libName, localScope, handle) => { | |
| #if DYLINK_DEBUG | |
| dbg('loadWebAssemblyModule:', libName, handle); | |
| #endif | |
| var metadata = getDylinkMetadata(binary); | |
| // loadModule loads the wasm module after all its dependencies have been loaded. | |
| // can be called both sync/async. | |
| function loadModule() { | |
| #if ASSERTIONS | |
| var originalTable = wasmTable; | |
| #endif | |
| #if PTHREADS | |
| // 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 then 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 DYLINK_DEBUG | |
| dbg('firstLoad:', firstLoad); | |
| #endif | |
| if (firstLoad) { | |
| #endif | |
| // alignments are powers of 2 | |
| var memAlign = Math.pow(2, metadata.memoryAlign); | |
| // prepare memory | |
| var memoryBase = metadata.memorySize ? alignMemory(getMemory(metadata.memorySize + memAlign), memAlign) : 0; // TODO: add to cleanups | |
| var tableBase = metadata.tableSize ? {{{ from64Expr('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') }}}; | |
| } | |
| #if PTHREADS | |
| } else { | |
| // Read the values for tableBase and memoryBase from shared memory. The | |
| // thread that first loaded the DLL already set these values. | |
| memoryBase = {{{ makeGetValue('handle', C_STRUCTS.dso.mem_addr, '*') }}}; | |
| tableBase = {{{ makeGetValue('handle', C_STRUCTS.dso.table_addr, '*') }}}; | |
| } | |
| #endif | |
| if (metadata.tableSize) { | |
| #if ASSERTIONS | |
| assert({{{ from64Expr('wasmTable.length') }}} == tableBase, `unexpected table size while loading ${libName}: ${wasmTable.length}`); | |
| #endif | |
| #if DYLINK_DEBUG | |
| dbg("loadModule: growing table by: " + metadata.tableSize); | |
| #endif | |
| wasmTable.grow({{{ toIndexType('metadata.tableSize') }}}); | |
| } | |
| #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 where they are not present on the | |
| // global Module object. We need this fallback because Modules sometime | |
| // need to import their own symbols | |
| var moduleExports; | |
| function resolveSymbol(sym) { | |
| var resolved = resolveGlobalSymbol(sym).sym; | |
| if (!resolved && localScope) { | |
| resolved = localScope[sym]; | |
| } | |
| 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(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 wasmImports && !wasmImports[prop].stub) { | |
| // No stub needed, symbol already exists in symbol table | |
| var res = wasmImports[prop]; | |
| #if ASYNCIFY | |
| // Asyncify wraps exports, and we need to look through those wrappers. | |
| if (res.orig) { | |
| res = res.orig; | |
| } | |
| #endif | |
| return res; | |
| } | |
| // Return a stub function that will resolve the symbol | |
| // when first called. | |
| if (!(prop in stubs)) { | |
| var resolved; | |
| stubs[prop] = (...args) => { | |
| resolved ||= resolveSymbol(prop); | |
| return resolved(...args); | |
| }; | |
| } | |
| return stubs[prop]; | |
| } | |
| }; | |
| var proxy = new Proxy({}, proxyHandler); | |
| var GOTProxy = new Proxy(metadata.weakImports, GOTHandler); | |
| var info = { | |
| 'GOT.mem': GOTProxy, | |
| 'GOT.func': GOTProxy, | |
| 'env': proxy, | |
| '{{{ WASI_MODULE_NAME }}}': proxy, | |
| }; | |
| function postInstantiation(module, instance) { | |
| #if ASSERTIONS | |
| // the table should be unchanged | |
| assert(wasmTable === originalTable); | |
| #endif | |
| #if PTHREADS | |
| if (!ENVIRONMENT_IS_PTHREAD && libName) { | |
| #if DYLINK_DEBUG | |
| dbg('registering sharedModules:', libName) | |
| #endif | |
| // cache all loaded modules in `sharedModules`, which gets passed | |
| // to new workers when they are created. | |
| sharedModules[libName] = module; | |
| } | |
| #endif | |
| // add new entries to functionsInTableMap | |
| updateTableMap(tableBase, metadata.tableSize); | |
| moduleExports = relocateExports(instance.exports, memoryBase); | |
| updateGOT(moduleExports); | |
| #if ASYNCIFY | |
| moduleExports = Asyncify.instrumentWasmExports(moduleExports); | |
| #endif | |
| if (!flags.allowUndefined) { | |
| reportUndefinedSymbols(); | |
| } | |
| #if STACK_OVERFLOW_CHECK >= 2 | |
| // If the runtime has already been initialized we set the stack limits | |
| // now. Otherwise this is delayed until `setDylinkStackLimits` is | |
| // called after initialization. | |
| if (moduleExports['__set_stack_limits'] && runtimeInitialized) { | |
| moduleExports['__set_stack_limits']({{{ to64('_emscripten_stack_get_base()') }}}, {{{ to64('_emscripten_stack_get_end()') }}}); | |
| } | |
| #endif | |
| #if MAIN_MODULE | |
| function addEmAsm(addr, body) { | |
| var args = []; | |
| for (var arity = 0; ; arity++) { | |
| var argName = '$' + arity; | |
| if (!body.includes(argName)) break; | |
| args.push(argName); | |
| } | |
| 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 functions that exist in the side module | |
| if ('__start_em_asm' in moduleExports) { | |
| var start = moduleExports['__start_em_asm'].value; | |
| var stop = moduleExports['__stop_em_asm'].value; | |
| #if CAN_ADDRESS_2GB | |
| start >>>= 0; | |
| stop >>>= 0; | |
| #else | |
| {{{ from64('start') }}} | |
| {{{ from64('stop') }}} | |
| #endif | |
| while (start < stop) { | |
| var jsString = UTF8ToString(start); | |
| addEmAsm(start, jsString); | |
| start = HEAPU8.indexOf(0, start) + 1; | |
| } | |
| } | |
| function addEmJs(name, cSig, body) { | |
| // The signature here is a C signature (e.g. "(int foo, char* bar)"). | |
| // See `create_em_js` in emcc.py` for the build-time version of this | |
| // code. | |
| var jsArgs = []; | |
| cSig = cSig.slice(1, -1) | |
| if (cSig != 'void') { | |
| cSig = cSig.split(','); | |
| for (var arg of cSig) { | |
| var jsArg = arg.split(' ').pop(); | |
| jsArgs.push(jsArg.replaceAll('*', '')); | |
| } | |
| } | |
| var func = `(${jsArgs}) => ${body};`; | |
| #if DYLINK_DEBUG | |
| dbg(`adding new EM_JS function: ${jsArgs} = ${func}`); | |
| #endif | |
| {{{ makeEval('moduleExports[name] = eval(func)') }}}; | |
| } | |
| for (var name in moduleExports) { | |
| if (name.startsWith('__em_js__')) { | |
| var start = moduleExports[name].value | |
| var jsString = UTF8ToString({{{ from64Expr('start') }}}); | |
| // EM_JS strings are stored in the data section in the form | |
| // SIG<::>BODY. | |
| var [sig, body] = jsString.split('<::>'); | |
| addEmJs(name.replace('__em_js__', ''), sig, body); | |
| delete moduleExports[name]; | |
| } | |
| } | |
| #endif | |
| // initialize the module | |
| #if 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('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); | |
| } | |
| } | |
| #if PTHREADS | |
| } | |
| #endif | |
| return moduleExports; | |
| } | |
| if (flags.loadAsync) { | |
| return (async () => { | |
| var instance; | |
| if (binary instanceof WebAssembly.Module) { | |
| instance = new WebAssembly.Instance(binary, info); | |
| } else { | |
| // Destructuring assignment without declaration has to be wrapped | |
| // with parens or parser will treat the l-value as an object | |
| // literal instead. | |
| ({ module: binary, instance } = await WebAssembly.instantiate(binary, info)); | |
| } | |
| return postInstantiation(binary, instance); | |
| })(); | |
| } | |
| var module = binary instanceof WebAssembly.Module ? binary : new WebAssembly.Module(binary); | |
| var instance = new WebAssembly.Instance(module, info); | |
| return postInstantiation(module, instance); | |
| } | |
| // We need to set rpath in flags based on the current library's rpath. | |
| // We can't mutate flags or else if a depends on b and c and b depends on d, | |
| // then c will be loaded with b's rpath instead of a's. | |
| flags = {...flags, rpath: { parentLibPath: libName, paths: metadata.runtimePaths }} | |
| // now load needed libraries and the module itself. | |
| if (flags.loadAsync) { | |
| return metadata.neededDynlibs | |
| .reduce((chain, needed) => chain.then(() => { | |
| #if FILESYSTEM | |
| needed = findLibraryFS(needed, flags.rpath) ?? needed; | |
| #endif | |
| return loadDynamicLibrary(needed, flags, localScope); | |
| }), Promise.resolve()) | |
| .then(loadModule); | |
| } | |
| for (var needed of metadata.neededDynlibs) { | |
| #if FILESYSTEM | |
| needed = findLibraryFS(needed, flags.rpath) ?? needed; | |
| #endif | |
| loadDynamicLibrary(needed, flags, localScope) | |
| } | |
| return loadModule(); | |
| }, | |
| #if STACK_OVERFLOW_CHECK >= 2 | |
| // Sometimes we load libraries before runtime initialization. In this case | |
| // we delay calling __set_stack_limits (which must be called for each | |
| // module). | |
| $setDylinkStackLimits: (stackTop, stackMax) => { | |
| for (var name in LDSO.loadedLibsByName) { | |
| #if DYLINK_DEBUG | |
| dbg(`setDylinkStackLimits for '${name}'`); | |
| #endif | |
| var lib = LDSO.loadedLibsByName[name]; | |
| lib.exports['__set_stack_limits']?.({{{ to64("stackTop") }}}, {{{ to64("stackMax") }}}); | |
| } | |
| }, | |
| #endif | |
| $newDSO: (name, handle, syms) => { | |
| var dso = { | |
| refcount: Infinity, | |
| name, | |
| exports: syms, | |
| global: true, | |
| }; | |
| LDSO.loadedLibsByName[name] = dso; | |
| if (handle != undefined) { | |
| LDSO.loadedLibsByHandle[handle] = dso; | |
| } | |
| return dso; | |
| }, | |
| #if FILESYSTEM | |
| $findLibraryFS__deps: [ | |
| '$replaceORIGIN', | |
| '_emscripten_find_dylib', | |
| '$withStackSave', | |
| '$stackAlloc', | |
| '$lengthBytesUTF8', | |
| '$stringToUTF8OnStack', | |
| '$stringToUTF8', | |
| '$FS', | |
| '$PATH', | |
| #if WASMFS | |
| '_wasmfs_identify', | |
| '_wasmfs_read_file', | |
| #endif | |
| ], | |
| $findLibraryFS: (libName, rpath) => { | |
| // If we're preloading a dynamic library, the runtime is not ready to call | |
| // __wasmfs_identify or __emscripten_find_dylib. So just quit out. | |
| // | |
| // This means that DT_NEEDED for the main module and transitive dependencies | |
| // of it won't work with this code path. Similarly, it means that calling | |
| // loadDynamicLibrary in a preRun hook can't use this code path. | |
| if (!runtimeInitialized) { | |
| return undefined; | |
| } | |
| if (PATH.isAbs(libName)) { | |
| #if WASMFS | |
| var result = withStackSave(() => __wasmfs_identify(stringToUTF8OnStack(libName))); | |
| return result === {{{ cDefs.EEXIST }}} ? libName : undefined; | |
| #else | |
| try { | |
| FS.lookupPath(libName); | |
| return libName; | |
| } catch (e) { | |
| return undefined; | |
| } | |
| #endif | |
| } | |
| var rpathResolved = (rpath?.paths || []).map((p) => replaceORIGIN(rpath?.parentLibPath, p)); | |
| return withStackSave(() => { | |
| // In dylink.c we use: `char buf[2*NAME_MAX+2];` and NAME_MAX is 255. | |
| // So we use the same size here. | |
| var bufSize = 2*255 + 2; | |
| var buf = stackAlloc(bufSize); | |
| var rpathC = stringToUTF8OnStack(rpathResolved.join(':')); | |
| var libNameC = stringToUTF8OnStack(libName); | |
| var resLibNameC = __emscripten_find_dylib(buf, rpathC, libNameC, bufSize); | |
| return resLibNameC ? UTF8ToString(resLibNameC) : undefined; | |
| }); | |
| }, | |
| #endif // FILESYSTEM | |
| // 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 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', | |
| '$mergeLibSymbols', '$newDSO', | |
| '$asyncLoad', | |
| #if FILESYSTEM | |
| '$preloadedWasm', | |
| '$findLibraryFS', | |
| #endif | |
| #if DYNCALLS || !WASM_BIGINT | |
| '$registerDynCallSymbols', | |
| #endif | |
| ], | |
| $loadDynamicLibrary__docs: ` | |
| /** | |
| * @param {number=} handle | |
| * @param {Object=} localScope | |
| */`, | |
| $loadDynamicLibrary: function(libName, flags = {global: true, nodelete: true}, localScope, handle) { | |
| #if DYLINK_DEBUG | |
| dbg(`loadDynamicLibrary: ${libName} handle: ${handle}`); | |
| dbg('existing:', Object.keys(LDSO.loadedLibsByName)); | |
| #endif | |
| // when loadDynamicLibrary did not have flags, libraries were loaded | |
| // globally & permanently | |
| var dso = LDSO.loadedLibsByName[libName]; | |
| if (dso) { | |
| // the library is being loaded or has been loaded already. | |
| #if ASSERTIONS | |
| assert(dso.exports !== 'loading', `attempt to load '${libName}' a second time, before the first load completed`); | |
| #endif | |
| if (!flags.global) { | |
| if (localScope) { | |
| Object.assign(localScope, dso.exports); | |
| } | |
| #if DYNCALLS || !WASM_BIGINT | |
| registerDynCallSymbols(dso.exports); | |
| #endif | |
| } else if (!dso.global) { | |
| // The library was previously loaded only locally but now | |
| // we have a request with global=true. | |
| dso.global = true; | |
| mergeLibSymbols(dso.exports, libName) | |
| } | |
| // 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 = newDSO(libName, handle, 'loading'); | |
| dso.refcount = flags.nodelete ? Infinity : 1; | |
| dso.global = flags.global; | |
| // libName -> libData | |
| function loadLibData() { | |
| #if PTHREADS | |
| var sharedMod = sharedModules[libName]; | |
| #if DYLINK_DEBUG | |
| dbg(`checking sharedModules: ${libName}: ${sharedMod ? 'found' : 'not found'}`); | |
| #endif | |
| if (sharedMod) { | |
| return flags.loadAsync ? Promise.resolve(sharedMod) : sharedMod; | |
| } | |
| #endif | |
| // for wasm, we can use fetch for async, but for fs mode we can only imitate it | |
| if (handle) { | |
| var data = {{{ makeGetValue('handle', C_STRUCTS.dso.file_data, '*') }}}; | |
| var dataSize = {{{ makeGetValue('handle', C_STRUCTS.dso.file_data_size, '*') }}}; | |
| if (data && dataSize) { | |
| var libData = HEAP8.slice(data, data + dataSize); | |
| return flags.loadAsync ? Promise.resolve(libData) : libData; | |
| } | |
| } | |
| #if FILESYSTEM | |
| var f = findLibraryFS(libName, flags.rpath); | |
| #if DYLINK_DEBUG | |
| dbg(`checking filesystem: ${libName}: ${f ? 'found' : 'not found'}`); | |
| #endif | |
| if (f) { | |
| var libData = FS.readFile(f, {encoding: 'binary'}); | |
| return flags.loadAsync ? Promise.resolve(libData) : libData; | |
| } | |
| #endif | |
| var libFile = locateFile(libName); | |
| if (flags.loadAsync) { | |
| return asyncLoad(libFile); | |
| } | |
| // 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); | |
| } | |
| // libName -> exports | |
| function getExports() { | |
| #if FILESYSTEM | |
| // lookup preloaded cache first | |
| var preloaded = preloadedWasm[libName]; | |
| #if DYLINK_DEBUG | |
| dbg(`checking preloadedWasm: ${libName}: ${preloaded ? 'found' : 'not found'}`); | |
| #endif | |
| if (preloaded) { | |
| return flags.loadAsync ? Promise.resolve(preloaded) : preloaded; | |
| } | |
| #endif | |
| // module not preloaded - load lib data and create new module from it | |
| if (flags.loadAsync) { | |
| return loadLibData().then((libData) => loadWebAssemblyModule(libData, flags, libName, localScope, handle)); | |
| } | |
| return loadWebAssemblyModule(loadLibData(), flags, libName, localScope, handle); | |
| } | |
| // module for lib is loaded - update the dso & global namespace | |
| function moduleLoaded(exports) { | |
| if (dso.global) { | |
| mergeLibSymbols(exports, libName); | |
| } else if (localScope) { | |
| Object.assign(localScope, exports); | |
| #if DYNCALLS || !WASM_BIGINT | |
| registerDynCallSymbols(exports); | |
| #endif | |
| } | |
| dso.exports = exports; | |
| } | |
| if (flags.loadAsync) { | |
| #if DYLINK_DEBUG | |
| dbg("loadDynamicLibrary: done (async)"); | |
| #endif | |
| return getExports().then((exports) => { | |
| moduleLoaded(exports); | |
| return true; | |
| }); | |
| } | |
| moduleLoaded(getExports()); | |
| #if DYLINK_DEBUG | |
| dbg("loadDynamicLibrary: done"); | |
| #endif | |
| return true; | |
| }, | |
| $loadDylibs__internal: true, | |
| $loadDylibs__deps: ['$loadDynamicLibrary', '$reportUndefinedSymbols', '$addRunDependency', '$removeRunDependency'], | |
| $loadDylibs: async () => { | |
| if (!dynamicLibraries.length) { | |
| #if DYLINK_DEBUG | |
| dbg('loadDylibs: no libraries to preload'); | |
| #endif | |
| reportUndefinedSymbols(); | |
| return; | |
| } | |
| #if DYLINK_DEBUG | |
| dbg('loadDylibs:', dynamicLibraries); | |
| #endif | |
| addRunDependency('loadDylibs'); | |
| // Load binaries asynchronously | |
| for (var lib of dynamicLibraries) { | |
| await loadDynamicLibrary(lib, {loadAsync: true, global: true, nodelete: true, allowUndefined: true}) | |
| } | |
| // we got them all, wonderful | |
| reportUndefinedSymbols(); | |
| #if DYLINK_DEBUG | |
| dbg('loadDylibs done!'); | |
| #endif | |
| removeRunDependency('loadDylibs'); | |
| }, | |
| // void* dlopen(const char* filename, int flags); | |
| $dlopenInternal__deps: ['$dlSetError', '$PATH'], | |
| $dlopenInternal: (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 global = Boolean(flags & {{{ cDefs.RTLD_GLOBAL }}}); | |
| var localScope = global ? null : {}; | |
| // We don't care about RTLD_NOW and RTLD_LAZY. | |
| var combinedFlags = { | |
| global, | |
| nodelete: Boolean(flags & {{{ cDefs.RTLD_NODELETE }}}), | |
| loadAsync: jsflags.loadAsync, | |
| } | |
| if (jsflags.loadAsync) { | |
| return loadDynamicLibrary(filename, combinedFlags, localScope, handle); | |
| } | |
| try { | |
| return loadDynamicLibrary(filename, combinedFlags, localScope, handle) | |
| } catch (e) { | |
| #if ASSERTIONS | |
| err(`error loading dynamic library ${filename}: ${e}`); | |
| #endif | |
| dlSetError(`could not load dynamic lib: ${filename}\n${e}`); | |
| return 0; | |
| } | |
| }, | |
| _dlopen_js__deps: ['$dlopenInternal'], | |
| _dlopen_js__async: 'auto', | |
| _dlopen_js: (handle) => | |
| #if ASYNCIFY | |
| dlopenInternal(handle, { loadAsync: true }), | |
| #else | |
| dlopenInternal(handle, { loadAsync: false }), | |
| #endif | |
| // Async version of dlopen. | |
| _emscripten_dlopen_js__deps: ['$dlopenInternal', '$callUserCallback', '$dlSetError'], | |
| _emscripten_dlopen_js: (handle, onsuccess, onerror, user_data) => { | |
| /** @param {Object=} e */ | |
| function errorCallback(e) { | |
| var filename = UTF8ToString(handle + {{{ C_STRUCTS.dso.name }}}); | |
| dlSetError(`'Could not load dynamic lib: ${filename}\n${e}`); | |
| {{{ runtimeKeepalivePop() }}} | |
| callUserCallback(() => {{{ makeDynCall('vpp', 'onerror') }}}(handle, user_data)); | |
| } | |
| function successCallback() { | |
| {{{ runtimeKeepalivePop() }}} | |
| callUserCallback(() => {{{ makeDynCall('vpp', 'onsuccess') }}}(handle, user_data)); | |
| } | |
| {{{ runtimeKeepalivePush() }}} | |
| var promise = dlopenInternal(handle, { loadAsync: true }); | |
| if (promise) { | |
| promise.then(successCallback, errorCallback); | |
| } else { | |
| errorCallback(); | |
| } | |
| }, | |
| _dlsym_catchup_js: (handle, symbolIndex) => { | |
| #if DYLINK_DEBUG | |
| dbg("_dlsym_catchup: handle=" + ptrToString(handle) + " symbolIndex=" + symbolIndex); | |
| #endif | |
| var lib = LDSO.loadedLibsByHandle[handle]; | |
| var symDict = lib.exports; | |
| var symName = Object.keys(symDict)[symbolIndex]; | |
| var sym = symDict[symName]; | |
| var result = addFunction(sym, sym.sig); | |
| #if DYLINK_DEBUG | |
| dbg(`_dlsym_catchup: result=${result}`); | |
| #endif | |
| return result; | |
| }, | |
| // void* dlsym(void* handle, const char* symbol); | |
| _dlsym_js__deps: ['$dlSetError', '$getFunctionAddress', '$addFunction'], | |
| _dlsym_js: (handle, symbol, symbolIndex) => { | |
| // void *dlsym(void *restrict handle, const char *restrict name); | |
| // http://pubs.opengroup.org/onlinepubs/009695399/functions/dlsym.html | |
| symbol = UTF8ToString(symbol); | |
| #if DYLINK_DEBUG | |
| dbg('dlsym_js:', symbol); | |
| #endif | |
| var result; | |
| var newSymIndex; | |
| var lib = LDSO.loadedLibsByHandle[handle]; | |
| #if ASSERTIONS | |
| assert(lib, `Tried to dlsym() from an unopened handle: ${handle}`); | |
| #endif | |
| newSymIndex = Object.keys(lib.exports).indexOf(symbol); | |
| if (newSymIndex == -1 || lib.exports[symbol].stub) { | |
| dlSetError(`Tried to lookup unknown symbol "${symbol}" in dynamic lib: ${lib.name}`) | |
| return 0; | |
| } | |
| #if !WASM_BIGINT | |
| var origSym = 'orig$' + symbol; | |
| result = lib.exports[origSym]; | |
| if (result) { | |
| newSymIndex = Object.keys(lib.exports).indexOf(origSym); | |
| } | |
| else | |
| #endif | |
| result = lib.exports[symbol]; | |
| if (typeof result == 'function') { | |
| #if DYLINK_DEBUG | |
| dbg(`dlsym_js: ${symbol} getting table slot for: ${result}`); | |
| #endif | |
| #if ASYNCIFY | |
| // Asyncify wraps exports, and we need to look through those wrappers. | |
| if (result.orig) { | |
| result = result.orig; | |
| } | |
| #endif | |
| var addr = getFunctionAddress(result); | |
| if (addr) { | |
| #if DYLINK_DEBUG | |
| dbg('symbol already exists in table:', symbol); | |
| #endif | |
| result = addr; | |
| } else { | |
| // Insert the function into the wasm table. If it's a direct wasm | |
| // function the second argument will not be needed. If it's 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('adding symbol to table:', symbol); | |
| #endif | |
| {{{ makeSetValue('symbolIndex', 0, 'newSymIndex', '*') }}}; | |
| } | |
| } | |
| #if DYLINK_DEBUG | |
| dbg(`dlsym_js: ${symbol} -> ${result}`); | |
| #endif | |
| return result; | |
| }, | |
| }; | |
| addToLibrary(LibraryDylink); | |
Xet Storage Details
- Size:
- 48.2 kB
- Xet hash:
- fbfd175f89a83ed224478ea0d9789d4e291ab8521282bfbafa4cd335e0ae89fa
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.