Skip to content

Commit e883361

Browse files
authored
dylink: Synchronize dlsym calls (as well as dlopen) between threads (#18590)
Prior to this change we were tracking the sequence of `dlopen` events and replaying them on each thread during `dlsync`. Now we also track `dlsym` events. A new data structure is used here to abstract over these two events (`dlevent`). During dlsync we then reply both the `dlopen` and the `dlsym` events. For `dlsym` events we serialized them as "dso+index", i.e. which dso did the symbol come from and what is the index of the symbol within that dso's symbol table. This is important for #18376.
1 parent ad684d4 commit e883361

14 files changed

+236
-103
lines changed

src/library_addfunction.js

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -175,27 +175,33 @@ mergeInto(LibraryManager.library, {
175175
}
176176
},
177177

178+
$getFunctionAddress__deps: ['$updateTableMap', '$functionsInTableMap'],
179+
$getFunctionAddress: function(func) {
180+
// First, create the map if this is the first use.
181+
if (!functionsInTableMap) {
182+
functionsInTableMap = new WeakMap();
183+
updateTableMap(0, wasmTable.length);
184+
}
185+
return functionsInTableMap.get(func) || 0;
186+
},
187+
178188
/**
179189
* Add a function to the table.
180190
* 'sig' parameter is required if the function being added is a JS function.
181191
*/
182192
$addFunction__docs: '/** @param {string=} sig */',
183-
$addFunction__deps: ['$convertJsFunctionToWasm', '$updateTableMap',
193+
$addFunction__deps: ['$convertJsFunctionToWasm', '$getFunctionAddress',
184194
'$functionsInTableMap', '$getEmptyTableSlot',
185195
'$getWasmTableEntry', '$setWasmTableEntry'],
186196
$addFunction: function(func, sig) {
187197
#if ASSERTIONS
188198
assert(typeof func != 'undefined');
189199
#endif // ASSERTIONS
190-
191200
// Check if the function is already in the table, to ensure each function
192-
// gets a unique index. First, create the map if this is the first use.
193-
if (!functionsInTableMap) {
194-
functionsInTableMap = new WeakMap();
195-
updateTableMap(0, wasmTable.length);
196-
}
197-
if (functionsInTableMap.has(func)) {
198-
return functionsInTableMap.get(func);
201+
// gets a unique index.
202+
var rtn = getFunctionAddress(func);
203+
if (rtn) {
204+
return rtn;
199205
}
200206

201207
// It's not in the table, add it now.

src/library_async.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ mergeInto(LibraryManager.library, {
283283
// Exported functions in side modules are not listed in `Module["asm"]`,
284284
// So we should use `resolveGlobalSymbol` helper function, which is defined in `library_dylink.js`.
285285
if (!func) {
286-
func = resolveGlobalSymbol(name, false);
286+
func = resolveGlobalSymbol(name, false).sym;
287287
}
288288
#endif
289289
return func;

src/library_dylink.js

Lines changed: 82 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,26 @@ var LibraryDylink = {
2727
return true;
2828
},
2929

30+
// Resolve a global symbol by name. This is used during module loading to
31+
// resolve imports, and by `dlsym` when used with `RTLD_DEFAULT`.
32+
// Returns both the resolved symbol (i.e. a function or a global) along with
33+
// the canonical name of the symbol (in some cases is modify the symbol as
34+
// part of the loop process, so that actual symbol looked up has a different
35+
// name).
3036
$resolveGlobalSymbol__deps: ['$isSymbolDefined'],
3137
$resolveGlobalSymbol__internal: true,
3238
$resolveGlobalSymbol: function(symName, direct = false) {
3339
var sym;
3440
#if !WASM_BIGINT
35-
if (direct) {
36-
// First look for the orig$ symbol which is the symbols without
37-
// any legalization performed.
38-
sym = wasmImports['orig$' + symName];
39-
if (sym) return sym;
41+
// First look for the orig$ symbol which is the symbol without i64
42+
// legalization performed.
43+
if (direct && ('orig$' + symName in wasmImports)) {
44+
symName = 'orig$' + symName;
45+
}
46+
#endif
47+
#if !DISABLE_EXCEPTION_CATCHING
48+
if (symName.startsWith('__cxa_find_matching_catch_')) {
49+
symName = '__cxa_find_matching_catch';
4050
}
4151
#endif
4252
if (isSymbolDefined(symName)) {
@@ -45,12 +55,7 @@ var LibraryDylink = {
4555
// Create (and cache) new invoke_ functions on demand.
4656
sym = wasmImports[symName] = createInvokeFunction(symName.split('_')[1]);
4757
}
48-
#if !DISABLE_EXCEPTION_CATCHING
49-
else if (symName.startsWith('__cxa_find_matching_catch_')) {
50-
sym = wasmImports['__cxa_find_matching_catch'];
51-
}
52-
#endif
53-
return sym;
58+
return {sym: sym, name: symName};
5459
},
5560

5661
$GOT: {},
@@ -104,7 +109,7 @@ var LibraryDylink = {
104109
},
105110

106111
$updateGOT__internal: true,
107-
$updateGOT__deps: ['$GOT', '$isInternalSym', '$addFunction'],
112+
$updateGOT__deps: ['$GOT', '$isInternalSym', '$addFunction', '$getFunctionAddress'],
108113
$updateGOT: function(exports, replace) {
109114
#if DYLINK_DEBUG
110115
dbg("updateGOT: adding " + Object.keys(exports).length + " symbols");
@@ -192,7 +197,7 @@ var LibraryDylink = {
192197
#endif
193198
for (var symName in GOT) {
194199
if (GOT[symName].value == 0) {
195-
var value = resolveGlobalSymbol(symName, true)
200+
var value = resolveGlobalSymbol(symName, true).sym;
196201
if (!value && !GOT[symName].required) {
197202
// Ignore undefined symbols that are imported as weak.
198203
#if DYLINK_DEBUG
@@ -234,13 +239,15 @@ var LibraryDylink = {
234239
_dlopen_js__deps: [function() { error(dlopenMissingError); }],
235240
_emscripten_dlopen_js__deps: [function() { error(dlopenMissingError); }],
236241
_dlsym_js__deps: [function() { error(dlopenMissingError); }],
242+
_dlsym_catchup_js__deps: [function() { error(dlopenMissingError); }],
237243
#else
238244
$dlopenMissingError: `= ${dlopenMissingError}`,
239245
_dlopen_js__deps: ['$dlopenMissingError'],
240246
_emscripten_dlopen_js__deps: ['$dlopenMissingError'],
241247
_dlsym_js__deps: ['$dlopenMissingError'],
248+
_dlsym_catchup_js__deps: ['$dlopenMissingError'],
242249
#endif
243-
_dlopen_js: function(filename, flag) {
250+
_dlopen_js: function(handle) {
244251
abort(dlopenMissingError);
245252
},
246253
_emscripten_dlopen_js: function(handle, onsuccess, onerror, user_data) {
@@ -249,6 +256,9 @@ var LibraryDylink = {
249256
_dlsym_js: function(handle, symbol) {
250257
abort(dlopenMissingError);
251258
},
259+
_dlsym_catchup_js: function(handle, symbolIndex) {
260+
abort(dlopenMissingError);
261+
},
252262
_dlinit: function(main_dso_handle) {},
253263
#else // MAIN_MODULE != 0
254264
// dynamic linker/loader (a-la ld.so on ELF systems)
@@ -262,6 +272,9 @@ var LibraryDylink = {
262272
$dlSetError__internal: true,
263273
$dlSetError__deps: ['__dl_seterr', '$allocateUTF8OnStack', '$withStackSave'],
264274
$dlSetError: function(msg) {
275+
#if DYLINK_DEBUG
276+
dbg('dlSetError: ' + msg);
277+
#endif
265278
withStackSave(function() {
266279
var cmsg = allocateUTF8OnStack(msg);
267280
___dl_seterr(cmsg, 0);
@@ -591,7 +604,7 @@ var LibraryDylink = {
591604
var moduleExports;
592605

593606
function resolveSymbol(sym) {
594-
var resolved = resolveGlobalSymbol(sym);
607+
var resolved = resolveGlobalSymbol(sym).sym;
595608
if (!resolved) {
596609
resolved = moduleExports[sym];
597610
}
@@ -1061,21 +1074,46 @@ var LibraryDylink = {
10611074
}
10621075
},
10631076

1077+
_dlsym_catchup_js__sig: 'ppp',
1078+
_dlsym_catchup_js: function(handle, symbolIndex) {
1079+
#if DYLINK_DEBUG
1080+
dbg("_dlsym_catchup: handle=" + ptrToString(handle) + " symbolIndex=" + symbolIndex);
1081+
#endif
1082+
var symDict = wasmImports;
1083+
if (handle != {{{ cDefine('RTLD_DEFAULT') }}}) {
1084+
var lib = LDSO.loadedLibsByHandle[handle];
1085+
symDict = lib.module;
1086+
}
1087+
var symName = Object.keys(symDict)[symbolIndex];
1088+
var sym = symDict[symName];
1089+
var result = addFunction(sym, sym.sig);
1090+
#if DYLINK_DEBUG
1091+
dbg('_dlsym_catchup: result=' + result);
1092+
#endif
1093+
return result;
1094+
},
1095+
10641096
// void* dlsym(void* handle, const char* symbol);
1065-
_dlsym_js__deps: ['$dlSetError'],
1097+
_dlsym_js__deps: ['$dlSetError', '$getFunctionAddress', '$addFunction'],
10661098
_dlsym_js__sig: 'ppp',
1067-
_dlsym_js: function(handle, symbol) {
1099+
_dlsym_js: function(handle, symbol, symbolIndex) {
10681100
// void *dlsym(void *restrict handle, const char *restrict name);
10691101
// http://pubs.opengroup.org/onlinepubs/009695399/functions/dlsym.html
10701102
symbol = UTF8ToString(symbol);
1103+
#if DYLINK_DEBUG
1104+
dbg('dlsym_js: ' + symbol);
1105+
#endif
10711106
var result;
1107+
var newSymIndex;
10721108

10731109
if (handle == {{{ cDefine('RTLD_DEFAULT') }}}) {
1074-
result = resolveGlobalSymbol(symbol, true);
1110+
var resolved = resolveGlobalSymbol(symbol, true);
1111+
result = resolved.sym;
10751112
if (!result) {
10761113
dlSetError('Tried to lookup unknown symbol "' + symbol + '" in dynamic lib: RTLD_DEFAULT');
10771114
return 0;
10781115
}
1116+
newSymIndex = Object.keys(wasmImports).indexOf(resolved.name);
10791117
} else {
10801118
var lib = LDSO.loadedLibsByHandle[handle];
10811119
#if ASSERTIONS
@@ -1085,16 +1123,21 @@ var LibraryDylink = {
10851123
dlSetError('Tried to lookup unknown symbol "' + symbol + '" in dynamic lib: ' + lib.name)
10861124
return 0;
10871125
}
1126+
newSymIndex = Object.keys(lib.module).indexOf(symbol);
10881127
#if !WASM_BIGINT
1089-
result = lib.module['orig$' + symbol];
1090-
if (!result)
1128+
var origSym = 'orig$' + symbol;
1129+
result = lib.module[origSym];
1130+
if (result) {
1131+
newSymIndex = Object.keys(lib.module).indexOf(origSym);
1132+
}
1133+
else
10911134
#endif
10921135
result = lib.module[symbol];
10931136
}
10941137

10951138
if (typeof result == 'function') {
10961139
#if DYLINK_DEBUG
1097-
dbg('dlsym: ' + symbol + ' getting table slot for: ' + result);
1140+
dbg('dlsym_js: ' + symbol + ' getting table slot for: ' + result);
10981141
#endif
10991142

11001143
#if ASYNCIFY
@@ -1103,14 +1146,26 @@ var LibraryDylink = {
11031146
result = result.orig;
11041147
}
11051148
#endif
1106-
// Insert the function into the wasm table. If its a direct wasm function
1107-
// the second argument will not be needed. If its a JS function we rely
1108-
// on the `sig` attribute being set based on the `<func>__sig` specified
1109-
// in library JS file.
1110-
result = addFunction(result, result.sig);
1149+
var addr = getFunctionAddress(result);
1150+
if (addr) {
1151+
#if DYLINK_DEBUG
1152+
dbg('symbol already exists in table: ' + symbol);
1153+
#endif
1154+
result = addr;
1155+
} else {
1156+
// Insert the function into the wasm table. If its a direct wasm
1157+
// function the second argument will not be needed. If its a JS
1158+
// function we rely on the `sig` attribute being set based on the
1159+
// `<func>__sig` specified in library JS file.
1160+
result = addFunction(result, result.sig);
1161+
#if DYLINK_DEBUG
1162+
dbg('adding symbol to table: ' + symbol);
1163+
#endif
1164+
{{{ makeSetValue('symbolIndex', 0, 'newSymIndex', '*') }}};
1165+
}
11111166
}
11121167
#if DYLINK_DEBUG
1113-
dbg('dlsym: ' + symbol + ' -> ' + result);
1168+
dbg('dlsym_js: ' + symbol + ' -> ' + result);
11141169
#endif
11151170
return result;
11161171
},

src/parseTools.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1114,7 +1114,7 @@ function getEntryFunction() {
11141114
entryFunction = '_emscripten_proxy_main';
11151115
}
11161116
if (MAIN_MODULE) {
1117-
return `resolveGlobalSymbol('${entryFunction}');`
1117+
return `resolveGlobalSymbol('${entryFunction}').sym;`
11181118
}
11191119
return '_' + entryFunction;
11201120
}

0 commit comments

Comments
 (0)