diff --git a/src/library.js b/src/library.js index e913a6df0cf5f..da3580ab6ea84 100644 --- a/src/library.js +++ b/src/library.js @@ -1524,27 +1524,33 @@ LibraryManager.library = { // setjmp.h // ========================================================================== - longjmp__sig: 'vii', -#if SUPPORT_LONGJMP - longjmp: function(env, value) { - _setThrew(env, value || 1); - throw 'longjmp'; - }, -#else + _emscripten_throw_longjmp__sig: 'v', + _emscripten_throw_longjmp: function() { throw 'longjmp'; }, +#if !SUPPORT_LONGJMP + // These are in order to print helpful error messages when either longjmp of + // setjmp is used. longjmp__deps: [function() { error('longjmp support was disabled (SUPPORT_LONGJMP=0), but it is required by the code (either set SUPPORT_LONGJMP=1, or remove uses of it in the project)'); }], + get setjmp__deps() { + return this.longjmp__deps; + }, + // This is to print the correct error message when a program is built with + // SUPPORT_LONGJMP=1 but linked with SUPPORT_LONGJMP=0. When a program is + // built with SUPPORT_LONGJMP=1, the object file contains references of not + // longjmp but _emscripten_throw_longjmp, which is called from + // emscripten_longjmp. + get _emscripten_throw_longjmp__deps() { + return this.longjmp__deps; + }, // will never be emitted, as the dep errors at compile time longjmp: function(env, value) { abort('longjmp not supported'); }, + setjmp: function(env, value) { + abort('setjmp not supported'); + }, #endif - // TODO: remove these aliases if/when the LLVM backend can stop emitting them - // (it emits them atm as they are generated by an IR pass, at at that time - // they each have a different signature - it is only at the wasm level that - // they become identical). - emscripten_longjmp__sig: 'vii', - emscripten_longjmp: 'longjmp', // ========================================================================== // sys/wait.h diff --git a/src/library_signals.js b/src/library_signals.js index 5fc5f926d8257..d6ebb09214dcc 100644 --- a/src/library_signals.js +++ b/src/library_signals.js @@ -137,22 +137,6 @@ var funs = { setErrNo(ERRNO_CODES.EINTR); return -1; }, -#if SUPPORT_LONGJMP -#if ASSERTIONS - siglongjmp__deps: ['longjmp'], - siglongjmp: function(env, value) { - // We cannot wrap the sigsetjmp, but I hope that - // in most cases siglongjmp will be called later. - - // siglongjmp can be called very many times, so don't flood the stderr. - warnOnce("Calling longjmp() instead of siglongjmp()"); - _longjmp(env, value); - }, -#else - siglongjmp__sig: 'vii', - siglongjmp: 'longjmp', -#endif -#endif sigpending: function(set) { {{{ makeSetValue('set', 0, 0, 'i32') }}}; diff --git a/system/lib/compiler-rt/emscripten_setjmp.c b/system/lib/compiler-rt/emscripten_setjmp.c index e9bf41f136668..5be8554ea19e3 100644 --- a/system/lib/compiler-rt/emscripten_setjmp.c +++ b/system/lib/compiler-rt/emscripten_setjmp.c @@ -16,6 +16,8 @@ typedef struct TableEntry { } TableEntry; extern void setTempRet0(uint32_t value); +extern void setThrew(uintptr_t threw, int value); +extern void _emscripten_throw_longjmp(); // defined in src/library.js TableEntry* saveSetjmp(uint32_t* env, uint32_t label, TableEntry* table, uint32_t size) { // Not particularly fast: slow table lookup of setjmpId to label. But setjmp @@ -55,3 +57,8 @@ uint32_t testSetjmp(uint32_t id, TableEntry* table, uint32_t size) { } return 0; } + +void emscripten_longjmp(uintptr_t env, int val) { + setThrew(env, val); + _emscripten_throw_longjmp(); +} diff --git a/system/lib/libc/musl/include/setjmp.h b/system/lib/libc/musl/include/setjmp.h index fac697fc4a3aa..a9d3d85c42b97 100644 --- a/system/lib/libc/musl/include/setjmp.h +++ b/system/lib/libc/musl/include/setjmp.h @@ -22,10 +22,11 @@ typedef jmp_buf sigjmp_buf; /* XXX EMSCRIPTEN: No signals support, alias sigsetjmp and siglongjmp to their non-signals counterparts. */ #if __EMSCRIPTEN__ #define sigsetjmp(buf, x) setjmp((buf)) +#define siglongjmp(buf, val) longjmp(buf, val) #else int sigsetjmp (sigjmp_buf, int); +_Noreturn int siglongjmp (sigjmp_buf, int); #endif -_Noreturn void siglongjmp (sigjmp_buf, int); #endif #if defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) \ diff --git a/tests/test_other.py b/tests/test_other.py index b96238a20ae13..b1dbf3bb6f6ea 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -10433,8 +10433,15 @@ def test_deps_info(self): cmd.append('-sUSE_WEBGPU') if function.startswith('__cxa_'): cmd.append('-fexceptions') - # Causes WebAssemblyLowerEmscriptenEHSjLj pass in llvm to crash - if function == 'setjmp': + # In WebAssemblyLowerEmscriptenEHSjLj pass in the LLVM backend, function + # calls that exist in the same function with setjmp are converted to some + # code sequence that includes emscripten_longjmp. emscripten_longjmp is + # included in deps_info.py because in non-LTO builds setjmp does not exist + # anymore in the object files. So the mere indirect reference of setjmp or + # emscripten_longjmp does not generate calls to its dependencies specified + # in deps_info.py. Also Emscripten EH has a known restriction that setjmp + # cannot be called or referenced indirectly anyway. + if function in ['emscripten_longjmp', 'setjmp']: continue print(shared.shlex_join(cmd)) self.run_process(cmd) diff --git a/tools/deps_info.py b/tools/deps_info.py index 7f45a8b7bd2b7..ddb504ea12654 100644 --- a/tools/deps_info.py +++ b/tools/deps_info.py @@ -93,7 +93,12 @@ 'emscripten_idb_load': ['malloc', 'free'], 'emscripten_init_websocket_to_posix_socket_bridge': ['malloc', 'free'], 'emscripten_log': ['strlen'], - 'emscripten_longjmp': ['setThrew'], + # This list is the same as setjmp's dependencies. In non-LTO builds, setjmp + # does not exist in the object files; it is converted into a code sequence + # that includes several functions, one of which is emscripten_longjmp. This is + # a trick to include these dependencies for setjmp even when setjmp does not + # exist. Refer to setjmp's entry for more details. + 'emscripten_longjmp': ['malloc', 'free', 'saveSetjmp', 'setThrew'], 'emscripten_pc_get_file': ['emscripten_builtin_malloc', 'emscripten_builtin_free', 'emscripten_builtin_memalign', 'malloc', 'free'], 'emscripten_pc_get_function': ['emscripten_builtin_malloc', 'emscripten_builtin_free', 'emscripten_builtin_memalign', 'malloc', 'free'], 'emscripten_run_preload_plugins_data': ['malloc'], @@ -167,7 +172,6 @@ 'gmtime_r': ['malloc'], 'localtime': ['_get_tzname', '_get_daylight', '_get_timezone', 'malloc'], 'localtime_r': ['_get_tzname', '_get_daylight', '_get_timezone', 'malloc'], - 'longjmp': ['setThrew'], 'mktime': ['_get_tzname', '_get_daylight', '_get_timezone', 'malloc'], 'mmap': ['memalign', 'memset', 'malloc'], 'munmap': ['malloc', 'free'], @@ -178,10 +182,15 @@ 'accept': ['htons'], 'recvfrom': ['htons'], 'send': ['ntohs'], - 'setjmp': ['saveSetjmp'], + # In WebAssemblyLowerEmscriptenEHSjLj pass in the LLVM backend, function calls + # that exist in the same function with setjmp are converted to some code + # sequence that includes invokes, malloc, free, saveSetjmp, and + # emscripten_longjmp. setThrew is called from invokes, but there's no way to + # directly include invokes in deps_info.py, so we list it as a setjmp's + # dependency. + 'setjmp': ['malloc', 'free', 'saveSetjmp', 'setThrew'], 'setprotoent': ['malloc'], 'setgroups': ['sysconf'], - 'siglongjmp': ['setThrew'], 'syslog': ['malloc', 'ntohs'], 'timegm': ['_get_tzname', '_get_daylight', '_get_timezone', 'malloc'], 'times': ['memset'], diff --git a/tools/wasm2c/base.c b/tools/wasm2c/base.c index bb5db047895be..0b7aea1d7d08f 100644 --- a/tools/wasm2c/base.c +++ b/tools/wasm2c/base.c @@ -113,21 +113,10 @@ static jmp_buf setjmp_stack[MAX_SETJMP_STACK]; static u32 next_setjmp = 0; -// Declare an export that may be needed and may not be. For example if longjmp -// is included then we need setThrew, but we must declare setThrew so that -// the C compiler can build us without error if longjmp is not actually used. - -#define DECLARE_WEAK_EXPORT(ret, name, args) \ -__attribute__((weak)) \ -ret (*WASM_RT_ADD_PREFIX(name)) args = NULL; - -DECLARE_WEAK_EXPORT(void, Z_setThrewZ_vii, (u32, u32)); - -IMPORT_IMPL(void, Z_envZ_emscripten_longjmpZ_vii, (u32 buf, u32 value), { +IMPORT_IMPL(void, Z_envZ__emscripten_throw_longjmpZ_vv, (), { if (next_setjmp == 0) { abort_with_message("longjmp without setjmp"); } - WASM_RT_ADD_PREFIX(Z_setThrewZ_vii)(buf, value ? value : 1); longjmp(setjmp_stack[next_setjmp - 1], 1); });