diff --git a/emsdk/upstream/emscripten/src/library.js b/emsdk/upstream/emscripten/src/library.js index 2990646..517fc33 100644 @@ -3749,6 +3749,15 @@ LibraryManager.library = { #endif #if ASSERTIONS assert(wasmTable.get(ptr), 'missing table entry in dynCall: ' + ptr); +#endif +#if EMULATE_FUNCTION_POINTER_CASTS + if( ptr in dyncallInvokeMap) + { + // in fpcast mode, we use a separate table or else we'll attempt + // to call the fpcast emulated version which is not what the linker + // expects for a linked dynamic call + ptr=dyncallInvokeMap[ptr]; + } #endif return wasmTable.get(ptr).apply(null, args) #endif --- a/emsdk/upstream/emscripten/src/runtime_init_table.js +++ b/emsdk/upstream/emscripten/src/runtime_init_table.js @@ -13,3 +13,17 @@ var wasmTable = new WebAssembly.Table({ // the exports are available. var wasmTable; #endif +#if EMULATE_FUNCTION_POINTER_CASTS==1 + // when we call dynamic link functions through the relocation + // invoke xxx methods, we aren't doing a function pointer + // call, so we don't mess with the function prototype. + // however, we do need to make the function table + // point to the emulated bny$fpcast-emu$ function pointer calls or else you + // can't take function pointers in wasm code. + // this means we need a different table for javascript dyncall invokes + // as given these are pre-linked and don't use function pointer casts, they + // will use the non-emulated version with the normal arguments. + // nb. this doesn't have to be a proper wasm table because it is + // only ever called from javascript + var dyncallInvokeMap=new Map(); +#endif diff --git a/emsdk/upstream/emscripten/tools/shared.py b/emsdk/upstream/emscripten/tools/shared.py index 74666ee..3d5657b 100644 --- a/emsdk/upstream/emscripten/tools/shared.py +++ b/emsdk/upstream/emscripten/tools/shared.py @@ -844,7 +844,7 @@ class JS(object): else: return 'Module["dynCall_%s"](%s)' % (sig, args) else: - return 'wasmTable.get(%s)(%s)' % (args[0], ','.join(args[1:])) + return 'dynCall("%s",%s,[%s])' %(sig,args[0],','.join(args[1:])) @staticmethod def make_invoke(sig, named=True): diff --git a/emdsk/upstream/emscripten/src/library_dylink.js b/emsdk/upstream/emscripten/src/library_dylink.js index 876492b..750a18d 100644 --- a/emdsk/upstream/emscripten/src/library_dylink.js +++ b/emsdk/upstream/emscripten/src/library_dylink.js @@ -90,33 +93,84 @@ var LibraryDylink = { var value = exports[symName]; #if !WASM_BIGINT if (symName.indexOf('orig$') == 0) { - symName = symName.split('$')[1]; + symName = symName.substring(5); replace = true; } #endif - - if (!GOT[symName]) { - GOT[symName] = new WebAssembly.Global({value: 'i32', mutable: true}); + baseName=symName; +#if EMULATE_FUNCTION_POINTER_CASTS==1 + const FPCAST_PREFIX="byn$fpcast-emu$"; + const FPCAST_DYNCALL_PREFIX="$no-fpcast-emu$"; + var dyncallName; + var is_fpcast=false; + var is_nonfpcast=false; + var newFn; + if(typeof(value) === 'function') + { + if(symName.startsWith(FPCAST_PREFIX)) + { + baseName=symName.substr(FPCAST_PREFIX.length); + dyncallName=FPCAST_DYNCALL_PREFIX+baseName; + is_fpcast=true; + }else + { + if(FPCAST_PREFIX+symName in exports) + { + baseName=FPCAST_DYNCALL_PREFIX+symName; + is_nonfpcast=true; + } + } } - if (replace || GOT[symName].value == 0) { +#endif + if (!GOT[baseName]) { + GOT[baseName] = new WebAssembly.Global({value: 'i32', mutable: true}); + } + if (replace || GOT[baseName].value == 0) { if (typeof value === 'function') { - GOT[symName].value = addFunctionWasm(value); + newFn=addFunctionWasm(value); + GOT[baseName].value = newFn #if DYLINK_DEBUG - err("updateGOT FUNC: " + symName + ' : ' + GOT[symName].value); + err("updateGOT FUNC: " + symName + ' : ' + GOT[baseName].value); #endif } else if (typeof value === 'number') { - GOT[symName].value = value; + GOT[baseName].value = value; } else { err("unhandled export type for `" + symName + "`: " + (typeof value)); } #if DYLINK_DEBUG - err("updateGOT: " + symName + ' : ' + GOT[symName].value); + err("updateGOT: " + baseName + "("+ symName + ")" + ' : ' + GOT[symName].value); #endif } #if DYLINK_DEBUG - else if (GOT[symName].value != value) { - err("updateGOT: EXISTING SYMBOL: " + symName + ' : ' + GOT[symName].value + " " + value); + else if (GOT[baseName].value != value) { + err("updateGOT: EXISTING SYMBOL: " + symName + ' : ' + GOT[baseName].value + " " + value); } +#endif +#if EMULATE_FUNCTION_POINTER_CASTS==1 + // update map for dynamic calls so they can bypass fp casts + if(!newFn)newFn= GOT[baseName].value; + if(is_fpcast && newFn) + { + dyncallGOT=GOT[dyncallName] + if(dyncallGOT){ + dcVal=dyncallGOT.value; + if(dcVal) + { + dyncallInvokeMap[newFn]=dcVal; + } + } + }else if(is_nonfpcast && newFn) + { + fpcGOT=GOT[symName]; + if(fpcGOT) + { + fpVal=fpcGOT.value; + if(fpVal) + { + dyncallInvokeMap[fpVal]=newFn; + } + } + } #endif } #if DYLINK_DEBUG @@ -713,6 +769,8 @@ var LibraryDylink = { nodelete: Boolean(flags & {{{ cDefine('RTLD_NODELETE') }}}), fs: FS, // load libraries from provided filesystem + allowUndefined: true, // allow undefined symbols - otherwise scipy won't + // import because modules have cross dependencies } try { @@ -754,6 +812,10 @@ var LibraryDylink = { // http://pubs.opengroup.org/onlinepubs/009695399/functions/dlsym.html symbol = UTF8ToString(symbol); var result; +#if EMULATE_FUNCTION_POINTER_CASTS==1 + // look for fpcast-emu version as this is deffo going to be a function pointer that we return + symbol="byn$fpcast-emu$"+symbol +#endif if (handle == {{{ cDefine('RTLD_DEFAULT') }}}) { result = resolveGlobalSymbol(symbol, true);