Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
emscripten-core
GitHub Repository: emscripten-core/emscripten
Path: blob/main/src/lib/libdylink.js
6161 views
1
/**
2
* @license
3
* Copyright 2020 The Emscripten Authors
4
* SPDX-License-Identifier: MIT
5
*
6
* Dynamic library loading
7
*/
8
9
#if !MAIN_MODULE && !RELOCATABLE
10
#error "library_dylink.js requires MAIN_MODULE or RELOCATABLE"
11
#endif
12
13
{{{
14
const UNDEFINED_ADDR = to64(-1);
15
}}}
16
17
var LibraryDylink = {
18
#if FILESYSTEM
19
$registerWasmPlugin__deps: ['$preloadPlugins'],
20
$registerWasmPlugin: () => {
21
// Use string keys here for public methods to avoid minification since the
22
// plugin consumer also uses string keys.
23
var wasmPlugin = {
24
promiseChainEnd: Promise.resolve(),
25
'canHandle': (name) => {
26
return !Module['noWasmDecoding'] && name.endsWith('.so')
27
},
28
'handle': async (byteArray, name) =>
29
// loadWebAssemblyModule can not load modules out-of-order, so rather
30
// than just running the promises in parallel, this makes a chain of
31
// promises to run in series.
32
wasmPlugin.promiseChainEnd = wasmPlugin.promiseChainEnd.then(async () => {
33
try {
34
var exports = await loadWebAssemblyModule(byteArray, {loadAsync: true, nodelete: true}, name, {});
35
} catch (error) {
36
throw new Error(`failed to instantiate wasm: ${name}: ${error}`);
37
}
38
#if DYLINK_DEBUG
39
dbg('registering preloadedWasm:', name);
40
#endif
41
preloadedWasm[name] = exports;
42
return byteArray;
43
})
44
};
45
preloadPlugins.push(wasmPlugin);
46
},
47
48
$preloadedWasm__deps: ['$registerWasmPlugin'],
49
$preloadedWasm__postset: `
50
registerWasmPlugin();
51
`,
52
$preloadedWasm: {},
53
54
$replaceORIGIN__deps: ['$PATH'],
55
$replaceORIGIN: (parentLibName, rpath) => {
56
if (rpath.startsWith('$ORIGIN')) {
57
// TODO: what to do if we only know the relative path of the file? It will return "." here.
58
var origin = PATH.dirname(parentLibName);
59
return rpath.replace('$ORIGIN', origin);
60
}
61
62
return rpath;
63
},
64
#endif // FILESYSTEM
65
66
$isSymbolDefined: (symName) => {
67
// Ignore 'stub' symbols that are auto-generated as part of the original
68
// `wasmImports` used to instantiate the main module.
69
var existing = wasmImports[symName];
70
if (!existing || existing.stub) {
71
return false;
72
}
73
#if ASYNCIFY
74
// Even if a symbol exists in wasmImports, and is not itself a stub, it
75
// could be an ASYNCIFY wrapper function that wraps a stub function.
76
if (symName in asyncifyStubs && !asyncifyStubs[symName]) {
77
return false;
78
}
79
#endif
80
return true;
81
},
82
83
// Dynamic version of shared.py:make_invoke. This is needed for invokes
84
// that originate from side modules since these are not known at JS
85
// generation time.
86
#if !DISABLE_EXCEPTION_CATCHING || SUPPORT_LONGJMP == 'emscripten'
87
$createInvokeFunction__internal: true,
88
$createInvokeFunction__deps: ['$dynCall', 'setThrew', '$stackSave', '$stackRestore'],
89
$createInvokeFunction: (sig) => (ptr, ...args) => {
90
var sp = stackSave();
91
try {
92
return dynCall(sig, ptr, args);
93
} catch(e) {
94
stackRestore(sp);
95
// Create a try-catch guard that rethrows the Emscripten EH exception.
96
#if EXCEPTION_STACK_TRACES
97
// Exceptions thrown from C++ and longjmps will be an instance of
98
// EmscriptenEH.
99
if (!(e instanceof EmscriptenEH)) throw e;
100
#else
101
// Exceptions thrown from C++ will be a pointer (number) and longjmp
102
// will throw the number Infinity. Use the compact and fast "e !== e+0"
103
// test to check if e was not a Number.
104
if (e !== e+0) throw e;
105
#endif
106
_setThrew(1, 0);
107
#if WASM_BIGINT
108
// In theory this if statement could be done on
109
// creating the function, but I just added this to
110
// save wasting code space as it only happens on exception.
111
if (sig[0] == "j") return 0n;
112
#endif
113
}
114
},
115
#endif
116
117
// Resolve a global symbol by name. This is used during module loading to
118
// resolve imports, and by `dlsym` when used with `RTLD_DEFAULT`.
119
// Returns both the resolved symbol (i.e. a function or a global) along with
120
// the canonical name of the symbol (in some cases modifying the symbol as
121
// part of the loop process, so that actual symbol looked up has a different
122
// name).
123
$resolveGlobalSymbol__deps: ['$isSymbolDefined', '$createNamedFunction',
124
#if !DISABLE_EXCEPTION_CATCHING || SUPPORT_LONGJMP == 'emscripten'
125
'$createInvokeFunction',
126
#endif
127
],
128
$resolveGlobalSymbol__internal: true,
129
$resolveGlobalSymbol: (symName, direct = false) => {
130
var sym;
131
#if !WASM_BIGINT
132
// First look for the orig$ symbol which is the symbol without i64
133
// legalization performed.
134
if (direct && ('orig$' + symName in wasmImports)) {
135
symName = 'orig$' + symName;
136
}
137
#endif
138
if (isSymbolDefined(symName)) {
139
sym = wasmImports[symName];
140
}
141
#if !DISABLE_EXCEPTION_CATCHING || SUPPORT_LONGJMP == 'emscripten'
142
// Asm.js-style exception handling: invoke wrapper generation
143
else if (symName.startsWith('invoke_')) {
144
// Create (and cache) new invoke_ functions on demand.
145
sym = wasmImports[symName] = createNamedFunction(symName, createInvokeFunction(symName.split('_')[1]));
146
}
147
#endif
148
#if !DISABLE_EXCEPTION_CATCHING
149
else if (symName.startsWith('__cxa_find_matching_catch_')) {
150
// When the main module is linked we create whichever variants of
151
// `__cxa_find_matching_catch_` (see jsifier.js) that we know are needed,
152
// but a side module loaded at runtime might need different/additional
153
// variants so we create those dynamically.
154
sym = wasmImports[symName] = createNamedFunction(symName, (...args) => {
155
#if MEMORY64
156
args = args.map(Number);
157
#endif
158
var rtn = findMatchingCatch(args);
159
return {{{ to64('rtn') }}};
160
});
161
}
162
#endif
163
return {sym, name: symName};
164
},
165
166
$GOT: {},
167
168
// Proxy handler used for GOT.mem and GOT.func imports. Each of these
169
// imports is fulfilled dynamically via the `get` method of this proxy
170
// handler. We abuse the `target` of the Proxy in order to pass the set of
171
// weak imports to the handler.
172
$GOTHandler__internal: true,
173
$GOTHandler__deps: ['$GOT'],
174
$GOTHandler: {
175
get(weakImports, symName) {
176
var rtn = GOT[symName];
177
if (!rtn) {
178
#if DYLINK_DEBUG == 2
179
dbg(`new GOT entry: ${symName}`);
180
#endif
181
rtn = GOT[symName] = new WebAssembly.Global({'value': '{{{ POINTER_WASM_TYPE }}}', 'mutable': true}, {{{ UNDEFINED_ADDR }}});
182
}
183
if (!weakImports.has(symName)) {
184
// Any non-weak reference to a symbol marks it as `required`, which
185
// enabled `reportUndefinedSymbols` to report undefined symbol errors
186
// correctly.
187
rtn.required = true;
188
}
189
return rtn;
190
}
191
},
192
193
$isInternalSym__internal: true,
194
$isInternalSym: (symName) => {
195
// TODO: find a way to mark these in the binary or avoid exporting them.
196
return [
197
'memory',
198
'__memory_base',
199
'__table_base',
200
'__stack_pointer',
201
'__indirect_function_table',
202
'__cpp_exception',
203
'__c_longjmp',
204
'__wasm_apply_data_relocs',
205
'__dso_handle',
206
'__tls_size',
207
'__tls_align',
208
'__set_stack_limits',
209
'_emscripten_tls_init',
210
'__wasm_init_tls',
211
'__wasm_call_ctors',
212
'__start_em_asm',
213
'__stop_em_asm',
214
'__start_em_js',
215
'__stop_em_js',
216
].includes(symName) || symName.startsWith('__em_js__')
217
#if SPLIT_MODULE
218
// Exports synthesized by wasm-split should be prefixed with '%'
219
|| symName[0] == '%'
220
#endif
221
;
222
},
223
224
$updateGOT__internal: true,
225
$updateGOT__deps: ['$GOT', '$isInternalSym', '$addFunction'],
226
$updateGOT__docs: '/** @param {boolean=} replace */',
227
$updateGOT: (exports, replace) => {
228
#if DYLINK_DEBUG
229
dbg(`updateGOT: adding ${Object.keys(exports).length} symbols`);
230
#endif
231
for (var symName in exports) {
232
if (isInternalSym(symName)) {
233
continue;
234
}
235
236
var value = exports[symName];
237
#if !WASM_BIGINT
238
if (symName.startsWith('orig$')) {
239
symName = symName.split('$')[1];
240
replace = true;
241
}
242
#endif
243
244
var existingEntry = GOT[symName] && GOT[symName].value != {{{ UNDEFINED_ADDR }}};
245
if (replace || !existingEntry) {
246
#if DYLINK_DEBUG == 2
247
dbg(`updateGOT: before: ${symName} : ${GOT[symName]?.value}`);
248
#endif
249
var newValue;
250
if (typeof value == 'function') {
251
newValue = {{{ to64('addFunction(value)') }}};
252
} else if (typeof value.value == {{{ POINTER_JS_TYPE }}}) {
253
newValue = value;
254
} else {
255
// The GOT can only contain addresses (i.e data addresses or function
256
// addresses so we currently ignore other types export here.
257
#if DYLINK_DEBUG
258
dbg(`updateGOT: ignoring ${symName} due to its type: ${typeof value}`);
259
#endif
260
continue;
261
}
262
#if DYLINK_DEBUG == 2
263
dbg(`updateGOT: after: ${symName} : ${newValue} (${value})`);
264
#endif
265
GOT[symName] ??= new WebAssembly.Global({'value': '{{{ POINTER_WASM_TYPE }}}', 'mutable': true});
266
GOT[symName].value = newValue;
267
}
268
#if DYLINK_DEBUG
269
else if (GOT[symName].value != value) {
270
dbg(`updateGOT: EXISTING SYMBOL: ${symName} : ${GOT[symName].value} (${value})`);
271
}
272
#endif
273
}
274
#if DYLINK_DEBUG
275
dbg("done updateGOT");
276
#endif
277
},
278
279
$isImmutableGlobal__internal: true,
280
$isImmutableGlobal: (val) => {
281
if (val instanceof WebAssembly.Global) {
282
try {
283
val.value = val.value;
284
} catch {
285
return true;
286
}
287
}
288
return false;
289
},
290
291
// Applies relocations to exported things.
292
$relocateExports__internal: true,
293
$relocateExports__deps: ['$isImmutableGlobal'],
294
$relocateExports: (exports, memoryBase = 0) => {
295
#if DYLINK_DEBUG
296
dbg(`relocateExports memoryBase=${memoryBase} count=${Object.keys(exports).length}`);
297
#endif
298
299
function relocateExport(name, value) {
300
#if SPLIT_MODULE
301
// Do not modify exports synthesized by wasm-split
302
if (name.startsWith('%')) {
303
return value;
304
}
305
#endif
306
// Detect immutable wasm global exports. These represent data addresses
307
// which are relative to `memoryBase`
308
if (isImmutableGlobal(value)) {
309
return new WebAssembly.Global({'value': '{{{ POINTER_WASM_TYPE }}}'}, value.value + {{{ to64('memoryBase') }}});
310
}
311
312
// Return unmodified value (no relocation required).
313
return value;
314
}
315
316
var relocated = {};
317
for (var e in exports) {
318
relocated[e] = relocateExport(e, exports[e])
319
}
320
return relocated;
321
},
322
323
$reportUndefinedSymbols__internal: true,
324
$reportUndefinedSymbols__deps: ['$GOT', '$resolveGlobalSymbol'],
325
$reportUndefinedSymbols: () => {
326
#if DYLINK_DEBUG
327
dbg('reportUndefinedSymbols');
328
#endif
329
for (var [symName, entry] of Object.entries(GOT)) {
330
if (entry.value == {{{ UNDEFINED_ADDR }}}) {
331
#if DYLINK_DEBUG
332
dbg(`undef GOT entry: ${symName}`);
333
#endif
334
var value = resolveGlobalSymbol(symName, true).sym;
335
if (!value && !entry.required) {
336
// Ignore undefined symbols that are imported as weak.
337
#if DYLINK_DEBUG
338
dbg('ignoring undefined weak symbol:', symName);
339
#endif
340
entry.value = {{{ to64(0) }}};
341
continue;
342
}
343
#if ASSERTIONS
344
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`);
345
#endif
346
#if DYLINK_DEBUG == 2
347
dbg(`assigning dynamic symbol from main module: ${symName} -> ${prettyPrint(value)}`);
348
#endif
349
if (typeof value == 'function') {
350
/** @suppress {checkTypes} */
351
entry.value = {{{ to64('addFunction(value, value.sig)') }}};
352
#if DYLINK_DEBUG == 2
353
dbg(`assigning table entry for : ${symName} -> ${entry.value}`);
354
#endif
355
} else if (typeof value == 'number') {
356
entry.value = {{{ to64('value') }}};
357
} else if (typeof value.value == {{{ POINTER_JS_TYPE }}}) {
358
entry.value = value;
359
} else {
360
throw new Error(`bad export type for '${symName}': ${typeof value} (${value})`);
361
}
362
}
363
}
364
#if DYLINK_DEBUG
365
dbg('done reportUndefinedSymbols');
366
#endif
367
},
368
369
// dynamic linker/loader (a-la ld.so on ELF systems)
370
$LDSO__deps: ['$newDSO'],
371
$LDSO: {
372
// name -> dso [refcount, name, module, global]; Used by dlopen
373
loadedLibsByName: {},
374
// handle -> dso; Used by dlsym
375
loadedLibsByHandle: {},
376
init() {
377
#if ASSERTIONS
378
// This function needs to run after the initial wasmImports object
379
// as been created.
380
assert(wasmImports);
381
#endif
382
newDSO('__main__', {{{ cDefs.RTLD_DEFAULT }}}, wasmImports);
383
},
384
},
385
386
$dlSetError__internal: true,
387
$dlSetError__deps: ['__dl_seterr', '$stringToUTF8OnStack', '$stackSave', '$stackRestore'],
388
$dlSetError: (msg) => {
389
#if DYLINK_DEBUG
390
dbg('dlSetError:', msg);
391
#endif
392
var sp = stackSave();
393
var cmsg = stringToUTF8OnStack(msg);
394
___dl_seterr(cmsg, 0);
395
stackRestore(sp);
396
},
397
398
// We support some amount of allocation during startup in the case of
399
// dynamic linking, which needs to allocate memory for dynamic libraries that
400
// are loaded. That has to happen before the main program can start to run,
401
// because the main program needs those linked in before it runs (so we can't
402
// use normally malloc from the main program to do these allocations).
403
//
404
// Allocate memory even if malloc isn't ready yet. The allocated memory here
405
// must be zero initialized since its used for all static data, including bss.
406
$getMemory__noleakcheck: true,
407
$getMemory__deps: ['$GOT', 'emscripten_get_sbrk_ptr', '__heap_base', '$alignMemory', 'calloc'],
408
$getMemory: (size) => {
409
// After the runtime is initialized, we must only use sbrk() normally.
410
#if DYLINK_DEBUG
411
dbg("getMemory: " + size + " runtimeInitialized=" + runtimeInitialized);
412
#endif
413
if (runtimeInitialized) {
414
// Currently we don't support freeing of static data when modules are
415
// unloaded via dlclose. This function is tagged as `noleakcheck` to
416
// avoid having this reported as leak.
417
return _calloc(size, 1);
418
}
419
var ret = ___heap_base;
420
// Keep __heap_base stack aligned.
421
var end = ret + alignMemory(size, {{{ STACK_ALIGN }}});
422
#if ASSERTIONS
423
//dbg(ret);
424
//dbg(HEAP8.length);
425
assert(end <= HEAP8.length, 'failure to getMemory - memory growth etc. is not supported there, call malloc/sbrk directly or increase INITIAL_MEMORY');
426
#endif
427
___heap_base = end;
428
429
// After allocating the memory from the start of the heap we need to ensure
430
// that once the program starts it doesn't use this region. In relocatable
431
// mode we can just update the __heap_base symbol that we are exporting to
432
// the main module.
433
// When not relocatable `__heap_base` is fixed and exported by the main
434
// module, but we can update the `sbrk_ptr` value instead. We call
435
// `_emscripten_get_sbrk_ptr` knowing that it is safe to call prior to
436
// runtime initialization (unlike, the higher level sbrk function)
437
#if RELOCATABLE
438
GOT['__heap_base'].value = {{{ to64('end') }}};
439
#else
440
#if PTHREADS
441
if (!ENVIRONMENT_IS_PTHREAD) {
442
#endif
443
var sbrk_ptr = _emscripten_get_sbrk_ptr();
444
{{{ makeSetValue('sbrk_ptr', 0, 'end', '*') }}}
445
#if PTHREADS
446
}
447
#endif
448
#endif
449
return ret;
450
},
451
452
// returns the side module metadata as an object
453
// { memorySize, memoryAlign, tableSize, tableAlign, neededDynlibs}
454
$getDylinkMetadata__deps: ['$UTF8ArrayToString'],
455
$getDylinkMetadata__internal: true,
456
$getDylinkMetadata: (binary) => {
457
var offset = 0;
458
var end = 0;
459
460
function getU8() {
461
return binary[offset++];
462
}
463
464
function getLEB() {
465
var ret = 0;
466
var mul = 1;
467
while (1) {
468
var byte = binary[offset++];
469
ret += ((byte & 0x7f) * mul);
470
mul *= 0x80;
471
if (!(byte & 0x80)) break;
472
}
473
return ret;
474
}
475
476
function getString() {
477
var len = getLEB();
478
offset += len;
479
return UTF8ArrayToString(binary, offset - len, len);
480
}
481
482
function getStringList() {
483
var count = getLEB();
484
var rtn = []
485
while (count--) rtn.push(getString());
486
return rtn;
487
}
488
489
/** @param {string=} message */
490
function failIf(condition, message) {
491
if (condition) throw new Error(message);
492
}
493
494
if (binary instanceof WebAssembly.Module) {
495
var dylinkSection = WebAssembly.Module.customSections(binary, 'dylink.0');
496
failIf(dylinkSection.length === 0, 'need dylink section');
497
binary = new Uint8Array(dylinkSection[0]);
498
end = binary.length
499
} else {
500
var int32View = new Uint32Array(new Uint8Array(binary.subarray(0, 24)).buffer);
501
#if SUPPORT_BIG_ENDIAN
502
var magicNumberFound = int32View[0] == 0x6d736100 || int32View[0] == 0x0061736d;
503
#else
504
var magicNumberFound = int32View[0] == 0x6d736100;
505
#endif
506
failIf(!magicNumberFound, 'need to see wasm magic number'); // \0asm
507
// we should see the dylink custom section right after the magic number and wasm version
508
failIf(binary[8] !== 0, 'need the dylink section to be first')
509
offset = 9;
510
var section_size = getLEB(); // section size
511
end = offset + section_size;
512
var name = getString();
513
failIf(name !== 'dylink.0');
514
}
515
516
var customSection = { neededDynlibs: [], tlsExports: new Set(), weakImports: new Set(), runtimePaths: [] };
517
var WASM_DYLINK_MEM_INFO = 0x1;
518
var WASM_DYLINK_NEEDED = 0x2;
519
var WASM_DYLINK_EXPORT_INFO = 0x3;
520
var WASM_DYLINK_IMPORT_INFO = 0x4;
521
var WASM_DYLINK_RUNTIME_PATH = 0x5;
522
var WASM_SYMBOL_TLS = 0x100;
523
var WASM_SYMBOL_BINDING_MASK = 0x3;
524
var WASM_SYMBOL_BINDING_WEAK = 0x1;
525
while (offset < end) {
526
var subsectionType = getU8();
527
var subsectionSize = getLEB();
528
if (subsectionType === WASM_DYLINK_MEM_INFO) {
529
customSection.memorySize = getLEB();
530
customSection.memoryAlign = getLEB();
531
customSection.tableSize = getLEB();
532
customSection.tableAlign = getLEB();
533
} else if (subsectionType === WASM_DYLINK_NEEDED) {
534
customSection.neededDynlibs = getStringList();
535
} else if (subsectionType === WASM_DYLINK_EXPORT_INFO) {
536
var count = getLEB();
537
while (count--) {
538
var symname = getString();
539
var flags = getLEB();
540
if (flags & WASM_SYMBOL_TLS) {
541
customSection.tlsExports.add(symname);
542
}
543
}
544
} else if (subsectionType === WASM_DYLINK_IMPORT_INFO) {
545
var count = getLEB();
546
while (count--) {
547
var modname = getString();
548
var symname = getString();
549
var flags = getLEB();
550
if ((flags & WASM_SYMBOL_BINDING_MASK) == WASM_SYMBOL_BINDING_WEAK) {
551
customSection.weakImports.add(symname);
552
}
553
}
554
} else if (subsectionType === WASM_DYLINK_RUNTIME_PATH) {
555
customSection.runtimePaths = getStringList();
556
} else {
557
#if ASSERTIONS
558
err('unknown dylink.0 subsection:', subsectionType)
559
#endif
560
// unknown subsection
561
offset += subsectionSize;
562
}
563
}
564
565
#if ASSERTIONS
566
var tableAlign = Math.pow(2, customSection.tableAlign);
567
assert(tableAlign === 1, `invalid tableAlign ${tableAlign}`);
568
assert(offset == end);
569
#endif
570
571
#if DYLINK_DEBUG
572
dbg('dylink needed:', customSection.neededDynlibs);
573
#endif
574
575
return customSection;
576
},
577
578
#if DYNCALLS || !WASM_BIGINT
579
$registerDynCallSymbols: (exports) => {
580
for (var [sym, exp] of Object.entries(exports)) {
581
if (sym.startsWith('dynCall_')) {
582
var sig = sym.substring(8);
583
if (!dynCalls.hasOwnProperty(sig)) {
584
dynCalls[sig] = exp;
585
}
586
}
587
}
588
},
589
#endif
590
591
// Module.symbols <- libModule.symbols (flags.global handler)
592
$mergeLibSymbols__deps: ['$isSymbolDefined'],
593
$mergeLibSymbols: (exports, libName) => {
594
#if DYNCALLS || !WASM_BIGINT
595
registerDynCallSymbols(exports);
596
#endif
597
// add symbols into global namespace TODO: weak linking etc.
598
for (var [sym, exp] of Object.entries(exports)) {
599
#if ASSERTIONS == 2
600
if (isSymbolDefined(sym)) {
601
var curr = wasmImports[sym], next = exp;
602
// don't warn on functions - might be odr, linkonce_odr, etc.
603
if (!(typeof curr == 'function' && typeof next == 'function')) {
604
err(`warning: symbol '${sym}' from '${libName}' already exists (duplicate symbol? or weak linking, which isn't supported yet?)`); // + [curr, ' vs ', next]);
605
}
606
}
607
#endif
608
609
// When RTLD_GLOBAL is enabled, the symbols defined by this shared object
610
// will be made available for symbol resolution of subsequently loaded
611
// shared objects.
612
//
613
// We should copy the symbols (which include methods and variables) from
614
// SIDE_MODULE to MAIN_MODULE.
615
const setImport = (target) => {
616
#if ASYNCIFY
617
if (target in asyncifyStubs) {
618
asyncifyStubs[target] = exp;
619
}
620
#endif
621
if (!isSymbolDefined(target)) {
622
wasmImports[target] = exp;
623
}
624
}
625
setImport(sym);
626
627
#if !hasExportedSymbol('main')
628
// Special case for handling of main symbol: If a side module exports
629
// `main` that also acts a definition for `__main_argc_argv` and vice
630
// versa.
631
const main_alias = '__main_argc_argv';
632
if (sym == 'main') {
633
setImport(main_alias)
634
}
635
if (sym == main_alias) {
636
setImport('main')
637
}
638
#endif
639
}
640
},
641
642
#if DYLINK_DEBUG
643
$dumpTable__deps: ['$wasmTable'],
644
$dumpTable: () => {
645
var len = wasmTable.length;
646
for (var i = {{{ toIndexType(0) }}} ; i < len; i++) {
647
dbg(`table: ${i} : ${wasmTable.get(i)}`);
648
}
649
},
650
#endif
651
652
// Loads a side module from binary data or compiled Module. Returns the module's exports or a
653
// promise that resolves to its exports if the loadAsync flag is set.
654
$loadWebAssemblyModule__docs: `
655
/**
656
* @param {string=} libName
657
* @param {Object=} localScope
658
* @param {number=} handle
659
*/`,
660
$loadWebAssemblyModule__deps: [
661
'$loadDynamicLibrary', '$getMemory', '$updateGOT',
662
'$relocateExports', '$resolveGlobalSymbol', '$GOTHandler',
663
'$getDylinkMetadata', '$alignMemory',
664
'$updateTableMap',
665
'$wasmTable',
666
'$addOnPostCtor',
667
],
668
$loadWebAssemblyModule: (binary, flags, libName, localScope, handle) => {
669
#if DYLINK_DEBUG
670
dbg('loadWebAssemblyModule:', libName, handle);
671
#endif
672
var metadata = getDylinkMetadata(binary);
673
674
// loadModule loads the wasm module after all its dependencies have been loaded.
675
// can be called both sync/async.
676
function loadModule() {
677
#if ASSERTIONS
678
var originalTable = wasmTable;
679
#endif
680
#if PTHREADS
681
// The first thread to load a given module needs to allocate the static
682
// table and memory regions. Later threads re-use the same table region
683
// and can ignore the memory region (since memory is shared between
684
// threads already).
685
// If `handle` is specified then it is assumed that the calling thread has
686
// exclusive access to it for the duration of this function. See the
687
// locking in `dynlink.c`.
688
var firstLoad = !handle || !{{{ makeGetValue('handle', C_STRUCTS.dso.mem_allocated, 'i8') }}};
689
#if DYLINK_DEBUG
690
dbg('firstLoad:', firstLoad);
691
#endif
692
if (firstLoad) {
693
#endif
694
// alignments are powers of 2
695
var memAlign = Math.pow(2, metadata.memoryAlign);
696
// prepare memory
697
var memoryBase = metadata.memorySize ? alignMemory(getMemory(metadata.memorySize + memAlign), memAlign) : 0; // TODO: add to cleanups
698
var tableBase = metadata.tableSize ? {{{ from64Expr('wasmTable.length') }}} : 0;
699
if (handle) {
700
{{{ makeSetValue('handle', C_STRUCTS.dso.mem_allocated, '1', 'i8') }}};
701
{{{ makeSetValue('handle', C_STRUCTS.dso.mem_addr, 'memoryBase', '*') }}};
702
{{{ makeSetValue('handle', C_STRUCTS.dso.mem_size, 'metadata.memorySize', 'i32') }}};
703
{{{ makeSetValue('handle', C_STRUCTS.dso.table_addr, 'tableBase', '*') }}};
704
{{{ makeSetValue('handle', C_STRUCTS.dso.table_size, 'metadata.tableSize', 'i32') }}};
705
}
706
#if PTHREADS
707
} else {
708
// Read the values for tableBase and memoryBase from shared memory. The
709
// thread that first loaded the DLL already set these values.
710
memoryBase = {{{ makeGetValue('handle', C_STRUCTS.dso.mem_addr, '*') }}};
711
tableBase = {{{ makeGetValue('handle', C_STRUCTS.dso.table_addr, '*') }}};
712
}
713
#endif
714
715
if (metadata.tableSize) {
716
#if ASSERTIONS
717
assert({{{ from64Expr('wasmTable.length') }}} == tableBase, `unexpected table size while loading ${libName}: ${wasmTable.length}`);
718
#endif
719
#if DYLINK_DEBUG
720
dbg("loadModule: growing table by: " + metadata.tableSize);
721
#endif
722
wasmTable.grow({{{ toIndexType('metadata.tableSize') }}});
723
}
724
#if DYLINK_DEBUG
725
dbg(`loadModule: memory[${memoryBase}:${memoryBase + metadata.memorySize}]` +
726
` table[${tableBase}:${tableBase + metadata.tableSize}]`);
727
#endif
728
729
// This is the export map that we ultimately return. We declare it here
730
// so it can be used within resolveSymbol. We resolve symbols against
731
// this local symbol map in the case where they are not present on the
732
// global Module object. We need this fallback because Modules sometime
733
// need to import their own symbols
734
var moduleExports;
735
736
function resolveSymbol(sym) {
737
var resolved = resolveGlobalSymbol(sym).sym;
738
if (!resolved && localScope) {
739
resolved = localScope[sym];
740
}
741
if (!resolved) {
742
resolved = moduleExports[sym];
743
}
744
#if ASSERTIONS
745
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`);
746
#endif
747
return resolved;
748
}
749
750
// TODO kill ↓↓↓ (except "symbols local to this module", it will likely be
751
// not needed if we require that if A wants symbols from B it has to link
752
// to B explicitly: similarly to -Wl,--no-undefined)
753
//
754
// wasm dynamic libraries are pure wasm, so they cannot assist in
755
// their own loading. When side module A wants to import something
756
// provided by a side module B that is loaded later, we need to
757
// add a layer of indirection, but worse, we can't even tell what
758
// to add the indirection for, without inspecting what A's imports
759
// are. To do that here, we use a JS proxy (another option would
760
// be to inspect the binary directly).
761
var proxyHandler = {
762
get(stubs, prop) {
763
// symbols that should be local to this module
764
switch (prop) {
765
case '__memory_base':
766
return {{{ to64('memoryBase') }}};
767
case '__table_base':
768
return {{{ to64('tableBase') }}};
769
#if MEMORY64
770
#if MEMORY64 == 2
771
case '__memory_base32':
772
return memoryBase;
773
#endif
774
case '__table_base32':
775
return tableBase;
776
#endif
777
}
778
if (prop in wasmImports && !wasmImports[prop].stub) {
779
// No stub needed, symbol already exists in symbol table
780
var res = wasmImports[prop];
781
#if ASYNCIFY
782
// Asyncify wraps exports, and we need to look through those wrappers.
783
if (res.orig) {
784
res = res.orig;
785
}
786
#endif
787
return res;
788
}
789
// Return a stub function that will resolve the symbol
790
// when first called.
791
if (!(prop in stubs)) {
792
var resolved;
793
stubs[prop] = (...args) => {
794
resolved ||= resolveSymbol(prop);
795
return resolved(...args);
796
};
797
}
798
return stubs[prop];
799
}
800
};
801
var proxy = new Proxy({}, proxyHandler);
802
var GOTProxy = new Proxy(metadata.weakImports, GOTHandler);
803
var info = {
804
'GOT.mem': GOTProxy,
805
'GOT.func': GOTProxy,
806
'env': proxy,
807
'{{{ WASI_MODULE_NAME }}}': proxy,
808
};
809
810
function postInstantiation(module, instance) {
811
#if ASSERTIONS
812
// the table should be unchanged
813
assert(wasmTable === originalTable);
814
#endif
815
#if PTHREADS
816
if (!ENVIRONMENT_IS_PTHREAD && libName) {
817
#if DYLINK_DEBUG
818
dbg('registering sharedModules:', libName)
819
#endif
820
// cache all loaded modules in `sharedModules`, which gets passed
821
// to new workers when they are created.
822
sharedModules[libName] = module;
823
}
824
#endif
825
// add new entries to functionsInTableMap
826
updateTableMap(tableBase, metadata.tableSize);
827
moduleExports = relocateExports(instance.exports, memoryBase);
828
updateGOT(moduleExports);
829
#if ASYNCIFY
830
moduleExports = Asyncify.instrumentWasmExports(moduleExports);
831
#endif
832
if (!flags.allowUndefined) {
833
reportUndefinedSymbols();
834
}
835
#if STACK_OVERFLOW_CHECK >= 2
836
// If the runtime has already been initialized we set the stack limits
837
// now. Otherwise this is delayed until `setDylinkStackLimits` is
838
// called after initialization.
839
if (moduleExports['__set_stack_limits'] && runtimeInitialized) {
840
moduleExports['__set_stack_limits']({{{ to64('_emscripten_stack_get_base()') }}}, {{{ to64('_emscripten_stack_get_end()') }}});
841
}
842
#endif
843
844
#if MAIN_MODULE
845
function addEmAsm(addr, body) {
846
var args = [];
847
for (var arity = 0; ; arity++) {
848
var argName = '$' + arity;
849
if (!body.includes(argName)) break;
850
args.push(argName);
851
}
852
args = args.join(',');
853
var func = `(${args}) => { ${body} };`;
854
#if DYLINK_DEBUG
855
dbg('adding new EM_ASM constant at:', ptrToString(start));
856
#endif
857
{{{ makeEval('ASM_CONSTS[start] = eval(func)') }}};
858
}
859
860
// Add any EM_ASM functions that exist in the side module
861
if ('__start_em_asm' in moduleExports) {
862
var start = moduleExports['__start_em_asm'].value;
863
var stop = moduleExports['__stop_em_asm'].value;
864
#if CAN_ADDRESS_2GB
865
start >>>= 0;
866
stop >>>= 0;
867
#else
868
{{{ from64('start') }}}
869
{{{ from64('stop') }}}
870
#endif
871
while (start < stop) {
872
var jsString = UTF8ToString(start);
873
addEmAsm(start, jsString);
874
start = HEAPU8.indexOf(0, start) + 1;
875
}
876
}
877
878
function addEmJs(name, cSig, body) {
879
// The signature here is a C signature (e.g. "(int foo, char* bar)").
880
// See `create_em_js` in emcc.py` for the build-time version of this
881
// code.
882
var jsArgs = [];
883
cSig = cSig.slice(1, -1)
884
if (cSig != 'void') {
885
cSig = cSig.split(',');
886
for (var arg of cSig) {
887
var jsArg = arg.split(' ').pop();
888
jsArgs.push(jsArg.replaceAll('*', ''));
889
}
890
}
891
var func = `(${jsArgs}) => ${body};`;
892
#if DYLINK_DEBUG
893
dbg(`adding new EM_JS function: ${jsArgs} = ${func}`);
894
#endif
895
{{{ makeEval('moduleExports[name] = eval(func)') }}};
896
}
897
898
for (var name in moduleExports) {
899
if (name.startsWith('__em_js__')) {
900
var start = moduleExports[name].value
901
var jsString = UTF8ToString({{{ from64Expr('start') }}});
902
// EM_JS strings are stored in the data section in the form
903
// SIG<::>BODY.
904
var [sig, body] = jsString.split('<::>');
905
addEmJs(name.replace('__em_js__', ''), sig, body);
906
delete moduleExports[name];
907
}
908
}
909
#endif
910
911
// initialize the module
912
#if PTHREADS
913
// Only one thread should call __wasm_call_ctors, but all threads need
914
// to call _emscripten_tls_init
915
registerTLSInit(moduleExports['_emscripten_tls_init'], instance.exports, metadata)
916
if (firstLoad) {
917
#endif
918
var applyRelocs = moduleExports['__wasm_apply_data_relocs'];
919
if (applyRelocs) {
920
if (runtimeInitialized) {
921
#if DYLINK_DEBUG
922
dbg('running __wasm_apply_data_relocs');
923
#endif
924
applyRelocs();
925
} else {
926
#if DYLINK_DEBUG
927
dbg('delaying __wasm_apply_data_relocs');
928
#endif
929
__RELOC_FUNCS__.push(applyRelocs);
930
}
931
}
932
var init = moduleExports['__wasm_call_ctors'];
933
if (init) {
934
if (runtimeInitialized) {
935
#if DYLINK_DEBUG
936
dbg('running __wasm_call_ctors');
937
#endif
938
init();
939
} else {
940
#if DYLINK_DEBUG
941
dbg('delaying __wasm_call_ctors');
942
#endif
943
// we aren't ready to run compiled code yet
944
addOnPostCtor(init);
945
}
946
}
947
#if PTHREADS
948
}
949
#endif
950
return moduleExports;
951
}
952
953
if (flags.loadAsync) {
954
return (async () => {
955
var instance;
956
if (binary instanceof WebAssembly.Module) {
957
instance = new WebAssembly.Instance(binary, info);
958
} else {
959
// Destructuring assignment without declaration has to be wrapped
960
// with parens or parser will treat the l-value as an object
961
// literal instead.
962
({ module: binary, instance } = await WebAssembly.instantiate(binary, info));
963
}
964
return postInstantiation(binary, instance);
965
})();
966
}
967
968
var module = binary instanceof WebAssembly.Module ? binary : new WebAssembly.Module(binary);
969
var instance = new WebAssembly.Instance(module, info);
970
return postInstantiation(module, instance);
971
}
972
973
// We need to set rpath in flags based on the current library's rpath.
974
// We can't mutate flags or else if a depends on b and c and b depends on d,
975
// then c will be loaded with b's rpath instead of a's.
976
flags = {...flags, rpath: { parentLibPath: libName, paths: metadata.runtimePaths }}
977
// now load needed libraries and the module itself.
978
if (flags.loadAsync) {
979
return metadata.neededDynlibs
980
.reduce((chain, needed) => chain.then(() => {
981
#if FILESYSTEM
982
needed = findLibraryFS(needed, flags.rpath) ?? needed;
983
#endif
984
return loadDynamicLibrary(needed, flags, localScope);
985
}), Promise.resolve())
986
.then(loadModule);
987
}
988
989
for (var needed of metadata.neededDynlibs) {
990
#if FILESYSTEM
991
needed = findLibraryFS(needed, flags.rpath) ?? needed;
992
#endif
993
loadDynamicLibrary(needed, flags, localScope)
994
}
995
return loadModule();
996
},
997
998
#if STACK_OVERFLOW_CHECK >= 2
999
// Sometimes we load libraries before runtime initialization. In this case
1000
// we delay calling __set_stack_limits (which must be called for each
1001
// module).
1002
$setDylinkStackLimits: (stackTop, stackMax) => {
1003
for (var name in LDSO.loadedLibsByName) {
1004
#if DYLINK_DEBUG
1005
dbg(`setDylinkStackLimits for '${name}'`);
1006
#endif
1007
var lib = LDSO.loadedLibsByName[name];
1008
lib.exports['__set_stack_limits']?.({{{ to64("stackTop") }}}, {{{ to64("stackMax") }}});
1009
}
1010
},
1011
#endif
1012
1013
$newDSO: (name, handle, syms) => {
1014
var dso = {
1015
refcount: Infinity,
1016
name,
1017
exports: syms,
1018
global: true,
1019
};
1020
LDSO.loadedLibsByName[name] = dso;
1021
if (handle != undefined) {
1022
LDSO.loadedLibsByHandle[handle] = dso;
1023
}
1024
return dso;
1025
},
1026
1027
#if FILESYSTEM
1028
$findLibraryFS__deps: [
1029
'$replaceORIGIN',
1030
'_emscripten_find_dylib',
1031
'$withStackSave',
1032
'$stackAlloc',
1033
'$lengthBytesUTF8',
1034
'$stringToUTF8OnStack',
1035
'$stringToUTF8',
1036
'$FS',
1037
'$PATH',
1038
#if WASMFS
1039
'_wasmfs_identify',
1040
'_wasmfs_read_file',
1041
#endif
1042
],
1043
$findLibraryFS: (libName, rpath) => {
1044
// If we're preloading a dynamic library, the runtime is not ready to call
1045
// __wasmfs_identify or __emscripten_find_dylib. So just quit out.
1046
//
1047
// This means that DT_NEEDED for the main module and transitive dependencies
1048
// of it won't work with this code path. Similarly, it means that calling
1049
// loadDynamicLibrary in a preRun hook can't use this code path.
1050
if (!runtimeInitialized) {
1051
return undefined;
1052
}
1053
if (PATH.isAbs(libName)) {
1054
#if WASMFS
1055
var result = withStackSave(() => __wasmfs_identify(stringToUTF8OnStack(libName)));
1056
return result === {{{ cDefs.EEXIST }}} ? libName : undefined;
1057
#else
1058
try {
1059
FS.lookupPath(libName);
1060
return libName;
1061
} catch (e) {
1062
return undefined;
1063
}
1064
#endif
1065
}
1066
var rpathResolved = (rpath?.paths || []).map((p) => replaceORIGIN(rpath?.parentLibPath, p));
1067
return withStackSave(() => {
1068
// In dylink.c we use: `char buf[2*NAME_MAX+2];` and NAME_MAX is 255.
1069
// So we use the same size here.
1070
var bufSize = 2*255 + 2;
1071
var buf = stackAlloc(bufSize);
1072
var rpathC = stringToUTF8OnStack(rpathResolved.join(':'));
1073
var libNameC = stringToUTF8OnStack(libName);
1074
var resLibNameC = __emscripten_find_dylib(buf, rpathC, libNameC, bufSize);
1075
return resLibNameC ? UTF8ToString(resLibNameC) : undefined;
1076
});
1077
},
1078
#endif // FILESYSTEM
1079
1080
// loadDynamicLibrary loads dynamic library @ lib URL / path and returns
1081
// handle for loaded DSO.
1082
//
1083
// Several flags affect the loading:
1084
//
1085
// - if flags.global=true, symbols from the loaded library are merged into global
1086
// process namespace. Flags.global is thus similar to RTLD_GLOBAL in ELF.
1087
//
1088
// - if flags.nodelete=true, the library will be never unloaded. Flags.nodelete
1089
// is thus similar to RTLD_NODELETE in ELF.
1090
//
1091
// - if flags.loadAsync=true, the loading is performed asynchronously and
1092
// loadDynamicLibrary returns corresponding promise.
1093
//
1094
// If a library was already loaded, it is not loaded a second time. However
1095
// flags.global and flags.nodelete are handled every time a load request is made.
1096
// Once a library becomes "global" or "nodelete", it cannot be removed or unloaded.
1097
$loadDynamicLibrary__deps: ['$LDSO', '$loadWebAssemblyModule',
1098
'$mergeLibSymbols', '$newDSO',
1099
'$asyncLoad',
1100
#if FILESYSTEM
1101
'$preloadedWasm',
1102
'$findLibraryFS',
1103
#endif
1104
#if DYNCALLS || !WASM_BIGINT
1105
'$registerDynCallSymbols',
1106
#endif
1107
],
1108
$loadDynamicLibrary__docs: `
1109
/**
1110
* @param {number=} handle
1111
* @param {Object=} localScope
1112
*/`,
1113
$loadDynamicLibrary: function(libName, flags = {global: true, nodelete: true}, localScope, handle) {
1114
#if DYLINK_DEBUG
1115
dbg(`loadDynamicLibrary: ${libName} handle: ${handle}`);
1116
dbg('existing:', Object.keys(LDSO.loadedLibsByName));
1117
#endif
1118
// when loadDynamicLibrary did not have flags, libraries were loaded
1119
// globally & permanently
1120
1121
var dso = LDSO.loadedLibsByName[libName];
1122
if (dso) {
1123
// the library is being loaded or has been loaded already.
1124
#if ASSERTIONS
1125
assert(dso.exports !== 'loading', `Attempt to load '${libName}' twice before the first load completed`);
1126
#endif
1127
if (!flags.global) {
1128
if (localScope) {
1129
Object.assign(localScope, dso.exports);
1130
}
1131
#if DYNCALLS || !WASM_BIGINT
1132
registerDynCallSymbols(dso.exports);
1133
#endif
1134
} else if (!dso.global) {
1135
// The library was previously loaded only locally but now
1136
// we have a request with global=true.
1137
dso.global = true;
1138
mergeLibSymbols(dso.exports, libName)
1139
}
1140
// same for "nodelete"
1141
if (flags.nodelete && dso.refcount !== Infinity) {
1142
dso.refcount = Infinity;
1143
}
1144
dso.refcount++
1145
if (handle) {
1146
LDSO.loadedLibsByHandle[handle] = dso;
1147
}
1148
return flags.loadAsync ? Promise.resolve(true) : true;
1149
}
1150
1151
// allocate new DSO
1152
dso = newDSO(libName, handle, 'loading');
1153
dso.refcount = flags.nodelete ? Infinity : 1;
1154
dso.global = flags.global;
1155
1156
// libName -> libData
1157
function loadLibData() {
1158
#if PTHREADS
1159
var sharedMod = sharedModules[libName];
1160
#if DYLINK_DEBUG
1161
dbg(`checking sharedModules: ${libName}: ${sharedMod ? 'found' : 'not found'}`);
1162
#endif
1163
if (sharedMod) {
1164
return flags.loadAsync ? Promise.resolve(sharedMod) : sharedMod;
1165
}
1166
#endif
1167
1168
// for wasm, we can use fetch for async, but for fs mode we can only imitate it
1169
if (handle) {
1170
var data = {{{ makeGetValue('handle', C_STRUCTS.dso.file_data, '*') }}};
1171
var dataSize = {{{ makeGetValue('handle', C_STRUCTS.dso.file_data_size, '*') }}};
1172
if (data && dataSize) {
1173
var libData = HEAP8.slice(data, data + dataSize);
1174
return flags.loadAsync ? Promise.resolve(libData) : libData;
1175
}
1176
}
1177
1178
#if FILESYSTEM
1179
var f = findLibraryFS(libName, flags.rpath);
1180
#if DYLINK_DEBUG
1181
dbg(`checking filesystem: ${libName}: ${f ? 'found' : 'not found'}`);
1182
#endif
1183
if (f) {
1184
var libData = FS.readFile(f, {encoding: 'binary'});
1185
return flags.loadAsync ? Promise.resolve(libData) : libData;
1186
}
1187
#endif
1188
1189
var libFile = locateFile(libName);
1190
if (flags.loadAsync) {
1191
return asyncLoad(libFile);
1192
}
1193
1194
// load the binary synchronously
1195
if (!readBinary) {
1196
throw new Error(`${libFile}: file not found, and synchronous loading of external files is not available`);
1197
}
1198
return readBinary(libFile);
1199
}
1200
1201
// libName -> exports
1202
function getExports() {
1203
#if FILESYSTEM
1204
// lookup preloaded cache first
1205
var preloaded = preloadedWasm[libName];
1206
#if DYLINK_DEBUG
1207
dbg(`checking preloadedWasm: ${libName}: ${preloaded ? 'found' : 'not found'}`);
1208
#endif
1209
if (preloaded) {
1210
return flags.loadAsync ? Promise.resolve(preloaded) : preloaded;
1211
}
1212
#endif
1213
1214
// module not preloaded - load lib data and create new module from it
1215
if (flags.loadAsync) {
1216
return loadLibData().then((libData) => loadWebAssemblyModule(libData, flags, libName, localScope, handle));
1217
}
1218
1219
return loadWebAssemblyModule(loadLibData(), flags, libName, localScope, handle);
1220
}
1221
1222
// module for lib is loaded - update the dso & global namespace
1223
function moduleLoaded(exports) {
1224
if (dso.global) {
1225
mergeLibSymbols(exports, libName);
1226
} else if (localScope) {
1227
Object.assign(localScope, exports);
1228
#if DYNCALLS || !WASM_BIGINT
1229
registerDynCallSymbols(exports);
1230
#endif
1231
}
1232
dso.exports = exports;
1233
}
1234
1235
if (flags.loadAsync) {
1236
#if DYLINK_DEBUG
1237
dbg("loadDynamicLibrary: done (async)");
1238
#endif
1239
return getExports().then((exports) => {
1240
moduleLoaded(exports);
1241
return true;
1242
});
1243
}
1244
1245
moduleLoaded(getExports());
1246
#if DYLINK_DEBUG
1247
dbg("loadDynamicLibrary: done");
1248
#endif
1249
return true;
1250
},
1251
1252
$loadDylibs__internal: true,
1253
$loadDylibs__deps: ['$loadDynamicLibrary', '$reportUndefinedSymbols', '$addRunDependency', '$removeRunDependency'],
1254
$loadDylibs: async () => {
1255
if (!dynamicLibraries.length) {
1256
#if DYLINK_DEBUG
1257
dbg('loadDylibs: no libraries to preload');
1258
#endif
1259
reportUndefinedSymbols();
1260
return;
1261
}
1262
1263
#if DYLINK_DEBUG
1264
dbg('loadDylibs:', dynamicLibraries);
1265
#endif
1266
addRunDependency('loadDylibs');
1267
1268
// Load binaries asynchronously
1269
for (var lib of dynamicLibraries) {
1270
await loadDynamicLibrary(lib, {loadAsync: true, global: true, nodelete: true, allowUndefined: true})
1271
}
1272
// we got them all, wonderful
1273
reportUndefinedSymbols();
1274
1275
#if DYLINK_DEBUG
1276
dbg('loadDylibs done!');
1277
#endif
1278
removeRunDependency('loadDylibs');
1279
},
1280
1281
// void* dlopen(const char* filename, int flags);
1282
$dlopenInternal__deps: ['$dlSetError', '$PATH'],
1283
$dlopenInternal: (handle, jsflags) => {
1284
// void *dlopen(const char *file, int mode);
1285
// http://pubs.opengroup.org/onlinepubs/009695399/functions/dlopen.html
1286
var filename = UTF8ToString(handle + {{{ C_STRUCTS.dso.name }}});
1287
var flags = {{{ makeGetValue('handle', C_STRUCTS.dso.flags, 'i32') }}};
1288
#if DYLINK_DEBUG
1289
dbg('dlopenInternal:', filename);
1290
#endif
1291
filename = PATH.normalize(filename);
1292
var searchpaths = [];
1293
1294
var global = Boolean(flags & {{{ cDefs.RTLD_GLOBAL }}});
1295
var localScope = global ? null : {};
1296
1297
// We don't care about RTLD_NOW and RTLD_LAZY.
1298
var combinedFlags = {
1299
global,
1300
nodelete: Boolean(flags & {{{ cDefs.RTLD_NODELETE }}}),
1301
loadAsync: jsflags.loadAsync,
1302
}
1303
1304
if (jsflags.loadAsync) {
1305
return loadDynamicLibrary(filename, combinedFlags, localScope, handle);
1306
}
1307
1308
try {
1309
return loadDynamicLibrary(filename, combinedFlags, localScope, handle)
1310
} catch (e) {
1311
#if ASSERTIONS
1312
err(`error loading dynamic library ${filename}: ${e}`);
1313
#endif
1314
dlSetError(`could not load dynamic lib: ${filename}\n${e}`);
1315
return 0;
1316
}
1317
},
1318
1319
_dlopen_js__deps: ['$dlopenInternal'],
1320
_dlopen_js__async: 'auto',
1321
_dlopen_js: (handle) =>
1322
#if ASYNCIFY
1323
dlopenInternal(handle, { loadAsync: true }),
1324
#else
1325
dlopenInternal(handle, { loadAsync: false }),
1326
#endif
1327
1328
// Async version of dlopen.
1329
_emscripten_dlopen_js__deps: ['$dlopenInternal', '$callUserCallback', '$dlSetError'],
1330
_emscripten_dlopen_js: (handle, onsuccess, onerror, user_data) => {
1331
/** @param {Object=} e */
1332
function errorCallback(e) {
1333
var filename = UTF8ToString(handle + {{{ C_STRUCTS.dso.name }}});
1334
dlSetError(`'Could not load dynamic lib: ${filename}\n${e}`);
1335
{{{ runtimeKeepalivePop() }}}
1336
callUserCallback(() => {{{ makeDynCall('vpp', 'onerror') }}}(handle, user_data));
1337
}
1338
function successCallback() {
1339
{{{ runtimeKeepalivePop() }}}
1340
callUserCallback(() => {{{ makeDynCall('vpp', 'onsuccess') }}}(handle, user_data));
1341
}
1342
1343
{{{ runtimeKeepalivePush() }}}
1344
var promise = dlopenInternal(handle, { loadAsync: true });
1345
if (promise) {
1346
promise.then(successCallback, errorCallback);
1347
} else {
1348
errorCallback();
1349
}
1350
},
1351
1352
_dlsym_catchup_js: (handle, symbolIndex) => {
1353
#if DYLINK_DEBUG
1354
dbg("_dlsym_catchup: handle=" + ptrToString(handle) + " symbolIndex=" + symbolIndex);
1355
#endif
1356
var lib = LDSO.loadedLibsByHandle[handle];
1357
var symDict = lib.exports;
1358
var symName = Object.keys(symDict)[symbolIndex];
1359
var sym = symDict[symName];
1360
var result = addFunction(sym, sym.sig);
1361
#if DYLINK_DEBUG
1362
dbg(`_dlsym_catchup: result=${result}`);
1363
#endif
1364
return result;
1365
},
1366
1367
// void* dlsym(void* handle, const char* symbol);
1368
_dlsym_js__deps: ['$dlSetError', '$getFunctionAddress', '$addFunction'],
1369
_dlsym_js: (handle, symbol, symbolIndex) => {
1370
// void *dlsym(void *restrict handle, const char *restrict name);
1371
// http://pubs.opengroup.org/onlinepubs/009695399/functions/dlsym.html
1372
symbol = UTF8ToString(symbol);
1373
#if DYLINK_DEBUG
1374
dbg('dlsym_js:', symbol);
1375
#endif
1376
var result;
1377
var newSymIndex;
1378
1379
var lib = LDSO.loadedLibsByHandle[handle];
1380
#if ASSERTIONS
1381
assert(lib, `Tried to dlsym() from an unopened handle: ${handle}`);
1382
#endif
1383
newSymIndex = Object.keys(lib.exports).indexOf(symbol);
1384
if (newSymIndex == -1 || lib.exports[symbol].stub) {
1385
dlSetError(`Tried to lookup unknown symbol "${symbol}" in dynamic lib: ${lib.name}`)
1386
return 0;
1387
}
1388
#if !WASM_BIGINT
1389
var origSym = 'orig$' + symbol;
1390
result = lib.exports[origSym];
1391
if (result) {
1392
newSymIndex = Object.keys(lib.exports).indexOf(origSym);
1393
}
1394
else
1395
#endif
1396
result = lib.exports[symbol];
1397
1398
if (typeof result == 'function') {
1399
#if DYLINK_DEBUG
1400
dbg(`dlsym_js: ${symbol} getting table slot for: ${result}`);
1401
#endif
1402
1403
#if ASYNCIFY
1404
// Asyncify wraps exports, and we need to look through those wrappers.
1405
if (result.orig) {
1406
result = result.orig;
1407
}
1408
#endif
1409
var addr = getFunctionAddress(result);
1410
if (addr) {
1411
#if DYLINK_DEBUG
1412
dbg('symbol already exists in table:', symbol);
1413
#endif
1414
result = addr;
1415
} else {
1416
// Insert the function into the wasm table. If it's a direct wasm
1417
// function the second argument will not be needed. If it's a JS
1418
// function we rely on the `sig` attribute being set based on the
1419
// `<func>__sig` specified in library JS file.
1420
result = addFunction(result, result.sig);
1421
#if DYLINK_DEBUG
1422
dbg('adding symbol to table:', symbol);
1423
#endif
1424
{{{ makeSetValue('symbolIndex', 0, 'newSymIndex', '*') }}};
1425
}
1426
}
1427
#if DYLINK_DEBUG
1428
dbg(`dlsym_js: ${symbol} -> ${result}`);
1429
#endif
1430
return result;
1431
},
1432
};
1433
1434
addToLibrary(LibraryDylink);
1435
1436