Skip to content
Merged
32 changes: 19 additions & 13 deletions src/library.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
16 changes: 0 additions & 16 deletions src/library_signals.js
Original file line number Diff line number Diff line change
Expand Up @@ -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') }}};
Expand Down
7 changes: 7 additions & 0 deletions system/lib/compiler-rt/emscripten_setjmp.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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();
}
3 changes: 2 additions & 1 deletion system/lib/libc/musl/include/setjmp.h
Original file line number Diff line number Diff line change
Expand Up @@ -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) \
Expand Down
11 changes: 9 additions & 2 deletions tests/test_other.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
17 changes: 13 additions & 4 deletions tools/deps_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'],
Expand Down Expand Up @@ -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'],
Comment thread
aheejin marked this conversation as resolved.
'mktime': ['_get_tzname', '_get_daylight', '_get_timezone', 'malloc'],
'mmap': ['memalign', 'memset', 'malloc'],
'munmap': ['malloc', 'free'],
Expand All @@ -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'],
Expand Down
13 changes: 1 addition & 12 deletions tools/wasm2c/base.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});

Expand Down