Skip to content

Commit

Permalink
dylink: Synchronize dlsym calls (as well as dlopen) between threads (#…
Browse files Browse the repository at this point in the history
…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.
  • Loading branch information
sbc100 committed Jan 26, 2023
1 parent ad684d4 commit e883361
Show file tree
Hide file tree
Showing 14 changed files with 236 additions and 103 deletions.
24 changes: 15 additions & 9 deletions src/library_addfunction.js
Original file line number Diff line number Diff line change
Expand Up @@ -175,27 +175,33 @@ mergeInto(LibraryManager.library, {
}
},

$getFunctionAddress__deps: ['$updateTableMap', '$functionsInTableMap'],
$getFunctionAddress: function(func) {
// First, create the map if this is the first use.
if (!functionsInTableMap) {
functionsInTableMap = new WeakMap();
updateTableMap(0, wasmTable.length);
}
return functionsInTableMap.get(func) || 0;
},

/**
* Add a function to the table.
* 'sig' parameter is required if the function being added is a JS function.
*/
$addFunction__docs: '/** @param {string=} sig */',
$addFunction__deps: ['$convertJsFunctionToWasm', '$updateTableMap',
$addFunction__deps: ['$convertJsFunctionToWasm', '$getFunctionAddress',
'$functionsInTableMap', '$getEmptyTableSlot',
'$getWasmTableEntry', '$setWasmTableEntry'],
$addFunction: function(func, sig) {
#if ASSERTIONS
assert(typeof func != 'undefined');
#endif // ASSERTIONS

// Check if the function is already in the table, to ensure each function
// gets a unique index. First, create the map if this is the first use.
if (!functionsInTableMap) {
functionsInTableMap = new WeakMap();
updateTableMap(0, wasmTable.length);
}
if (functionsInTableMap.has(func)) {
return functionsInTableMap.get(func);
// gets a unique index.
var rtn = getFunctionAddress(func);
if (rtn) {
return rtn;
}

// It's not in the table, add it now.
Expand Down
2 changes: 1 addition & 1 deletion src/library_async.js
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ mergeInto(LibraryManager.library, {
// Exported functions in side modules are not listed in `Module["asm"]`,
// So we should use `resolveGlobalSymbol` helper function, which is defined in `library_dylink.js`.
if (!func) {
func = resolveGlobalSymbol(name, false);
func = resolveGlobalSymbol(name, false).sym;
}
#endif
return func;
Expand Down
109 changes: 82 additions & 27 deletions src/library_dylink.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,26 @@ var LibraryDylink = {
return true;
},

// 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 is modify the symbol as
// part of the loop process, so that actual symbol looked up has a different
// name).
$resolveGlobalSymbol__deps: ['$isSymbolDefined'],
$resolveGlobalSymbol__internal: true,
$resolveGlobalSymbol: function(symName, direct = false) {
var sym;
#if !WASM_BIGINT
if (direct) {
// First look for the orig$ symbol which is the symbols without
// any legalization performed.
sym = wasmImports['orig$' + symName];
if (sym) return sym;
// 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 !DISABLE_EXCEPTION_CATCHING
if (symName.startsWith('__cxa_find_matching_catch_')) {
symName = '__cxa_find_matching_catch';
}
#endif
if (isSymbolDefined(symName)) {
Expand All @@ -45,12 +55,7 @@ var LibraryDylink = {
// Create (and cache) new invoke_ functions on demand.
sym = wasmImports[symName] = createInvokeFunction(symName.split('_')[1]);
}
#if !DISABLE_EXCEPTION_CATCHING
else if (symName.startsWith('__cxa_find_matching_catch_')) {
sym = wasmImports['__cxa_find_matching_catch'];
}
#endif
return sym;
return {sym: sym, name: symName};
},

$GOT: {},
Expand Down Expand Up @@ -104,7 +109,7 @@ var LibraryDylink = {
},

$updateGOT__internal: true,
$updateGOT__deps: ['$GOT', '$isInternalSym', '$addFunction'],
$updateGOT__deps: ['$GOT', '$isInternalSym', '$addFunction', '$getFunctionAddress'],
$updateGOT: function(exports, replace) {
#if DYLINK_DEBUG
dbg("updateGOT: adding " + Object.keys(exports).length + " symbols");
Expand Down Expand Up @@ -192,7 +197,7 @@ var LibraryDylink = {
#endif
for (var symName in GOT) {
if (GOT[symName].value == 0) {
var value = resolveGlobalSymbol(symName, true)
var value = resolveGlobalSymbol(symName, true).sym;
if (!value && !GOT[symName].required) {
// Ignore undefined symbols that are imported as weak.
#if DYLINK_DEBUG
Expand Down Expand Up @@ -234,13 +239,15 @@ var LibraryDylink = {
_dlopen_js__deps: [function() { error(dlopenMissingError); }],
_emscripten_dlopen_js__deps: [function() { error(dlopenMissingError); }],
_dlsym_js__deps: [function() { error(dlopenMissingError); }],
_dlsym_catchup_js__deps: [function() { error(dlopenMissingError); }],
#else
$dlopenMissingError: `= ${dlopenMissingError}`,
_dlopen_js__deps: ['$dlopenMissingError'],
_emscripten_dlopen_js__deps: ['$dlopenMissingError'],
_dlsym_js__deps: ['$dlopenMissingError'],
_dlsym_catchup_js__deps: ['$dlopenMissingError'],
#endif
_dlopen_js: function(filename, flag) {
_dlopen_js: function(handle) {
abort(dlopenMissingError);
},
_emscripten_dlopen_js: function(handle, onsuccess, onerror, user_data) {
Expand All @@ -249,6 +256,9 @@ var LibraryDylink = {
_dlsym_js: function(handle, symbol) {
abort(dlopenMissingError);
},
_dlsym_catchup_js: function(handle, symbolIndex) {
abort(dlopenMissingError);
},
_dlinit: function(main_dso_handle) {},
#else // MAIN_MODULE != 0
// dynamic linker/loader (a-la ld.so on ELF systems)
Expand All @@ -262,6 +272,9 @@ var LibraryDylink = {
$dlSetError__internal: true,
$dlSetError__deps: ['__dl_seterr', '$allocateUTF8OnStack', '$withStackSave'],
$dlSetError: function(msg) {
#if DYLINK_DEBUG
dbg('dlSetError: ' + msg);
#endif
withStackSave(function() {
var cmsg = allocateUTF8OnStack(msg);
___dl_seterr(cmsg, 0);
Expand Down Expand Up @@ -591,7 +604,7 @@ var LibraryDylink = {
var moduleExports;

function resolveSymbol(sym) {
var resolved = resolveGlobalSymbol(sym);
var resolved = resolveGlobalSymbol(sym).sym;
if (!resolved) {
resolved = moduleExports[sym];
}
Expand Down Expand Up @@ -1061,21 +1074,46 @@ var LibraryDylink = {
}
},

_dlsym_catchup_js__sig: 'ppp',
_dlsym_catchup_js: function(handle, symbolIndex) {
#if DYLINK_DEBUG
dbg("_dlsym_catchup: handle=" + ptrToString(handle) + " symbolIndex=" + symbolIndex);
#endif
var symDict = wasmImports;
if (handle != {{{ cDefine('RTLD_DEFAULT') }}}) {
var lib = LDSO.loadedLibsByHandle[handle];
symDict = lib.module;
}
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'],
_dlsym_js__deps: ['$dlSetError', '$getFunctionAddress', '$addFunction'],
_dlsym_js__sig: 'ppp',
_dlsym_js: function(handle, symbol) {
_dlsym_js: function(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;

if (handle == {{{ cDefine('RTLD_DEFAULT') }}}) {
result = resolveGlobalSymbol(symbol, true);
var resolved = resolveGlobalSymbol(symbol, true);
result = resolved.sym;
if (!result) {
dlSetError('Tried to lookup unknown symbol "' + symbol + '" in dynamic lib: RTLD_DEFAULT');
return 0;
}
newSymIndex = Object.keys(wasmImports).indexOf(resolved.name);
} else {
var lib = LDSO.loadedLibsByHandle[handle];
#if ASSERTIONS
Expand All @@ -1085,16 +1123,21 @@ var LibraryDylink = {
dlSetError('Tried to lookup unknown symbol "' + symbol + '" in dynamic lib: ' + lib.name)
return 0;
}
newSymIndex = Object.keys(lib.module).indexOf(symbol);
#if !WASM_BIGINT
result = lib.module['orig$' + symbol];
if (!result)
var origSym = 'orig$' + symbol;
result = lib.module[origSym];
if (result) {
newSymIndex = Object.keys(lib.module).indexOf(origSym);
}
else
#endif
result = lib.module[symbol];
}

if (typeof result == 'function') {
#if DYLINK_DEBUG
dbg('dlsym: ' + symbol + ' getting table slot for: ' + result);
dbg('dlsym_js: ' + symbol + ' getting table slot for: ' + result);
#endif

#if ASYNCIFY
Expand All @@ -1103,14 +1146,26 @@ var LibraryDylink = {
result = result.orig;
}
#endif
// Insert the function into the wasm table. If its a direct wasm function
// the second argument will not be needed. If its a JS function we rely
// on the `sig` attribute being set based on the `<func>__sig` specified
// in library JS file.
result = addFunction(result, result.sig);
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 its a direct wasm
// function the second argument will not be needed. If its a JS
// function we rely on the `sig` attribute being set based on the
// `<func>__sig` specified in library JS file.
result = addFunction(result, result.sig);
#if DYLINK_DEBUG
dbg('adding symbol to table: ' + symbol);
#endif
{{{ makeSetValue('symbolIndex', 0, 'newSymIndex', '*') }}};
}
}
#if DYLINK_DEBUG
dbg('dlsym: ' + symbol + ' -> ' + result);
dbg('dlsym_js: ' + symbol + ' -> ' + result);
#endif
return result;
},
Expand Down
2 changes: 1 addition & 1 deletion src/parseTools.js
Original file line number Diff line number Diff line change
Expand Up @@ -1114,7 +1114,7 @@ function getEntryFunction() {
entryFunction = '_emscripten_proxy_main';
}
if (MAIN_MODULE) {
return `resolveGlobalSymbol('${entryFunction}');`
return `resolveGlobalSymbol('${entryFunction}').sym;`
}
return '_' + entryFunction;
}
Expand Down
Loading

0 comments on commit e883361

Please sign in to comment.