From 4cf36b6df367326e4d54ab6004466efe7ed3f265 Mon Sep 17 00:00:00 2001 From: Heejin Ahn Date: Tue, 10 May 2022 18:54:22 -0700 Subject: [PATCH] [EH] Make abort() work with Wasm EH (#16910) We used to implement `abort()` with throwing a JS exception. #9730 changed it to `RuntimeError`, because it is the class a trap becomes once it hits a JS frame. But this turned out to be not working, because Wasm EH currently does not assume all `RuntimeError`s are from traps; it decides whether a `RuntimeError` is from a trap or not based on a hidden field within the object. Relevant past discussion: https://github.com/WebAssembly/exception-handling/issues/89#issuecomment-559533953 So at the moment we don't have a way of throwing a trap from JS, which is inconvenient. I think we eventually want to make JS API for this, but for the moment, this PR resorts to a wasm function that traps. This at least makes calling `std::terminate` work. So far calling it exhausted the call stack and aborted. --- emcc.py | 3 +++ src/preamble.js | 11 ++++++++++- system/lib/compiler-rt/__trap.c | 3 +++ tests/test_core.py | 18 ++++++++++++++++++ tools/system_libs.py | 3 ++- 5 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 system/lib/compiler-rt/__trap.c diff --git a/emcc.py b/emcc.py index de969fa545fd5..e28fa0b917baa 100755 --- a/emcc.py +++ b/emcc.py @@ -2570,6 +2570,9 @@ def get_full_import_name(name): if settings.SUPPORT_LONGJMP == 'wasm': settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE.append('__c_longjmp') + if settings.EXCEPTION_HANDLING: + settings.REQUIRED_EXPORTS += ['__trap'] + return target, wasm_target diff --git a/src/preamble.js b/src/preamble.js index 2090af133dbb7..c4472becda997 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -605,12 +605,20 @@ function abort(what) { // Use a wasm runtime error, because a JS error might be seen as a foreign // exception, which means we'd run destructors on it. We need the error to // simply make the program stop. + // FIXME This approach does not work in Wasm EH because it currently does not assume + // all RuntimeErrors are from traps; it decides whether a RuntimeError is from + // a trap or not based on a hidden field within the object. So at the moment + // we don't have a way of throwing a wasm trap from JS. TODO Make a JS API that + // allows this in the wasm spec. // Suppress closure compiler warning here. Closure compiler's builtin extern // defintion for WebAssembly.RuntimeError claims it takes no arguments even // though it can. // TODO(https://github.com/google/closure-compiler/pull/3913): Remove if/when upstream closure gets fixed. - +#if EXCEPTION_HANDLING == 1 + // See above, in the meantime, we resort to wasm code for trapping. + ___trap(); +#else /** @suppress {checkTypes} */ var e = new WebAssembly.RuntimeError(what); @@ -621,6 +629,7 @@ function abort(what) { // in code paths apart from instantiation where an exception is expected // to be thrown when abort is called. throw e; +#endif } // {{MEM_INITIALIZER}} diff --git a/system/lib/compiler-rt/__trap.c b/system/lib/compiler-rt/__trap.c new file mode 100644 index 0000000000000..8c714078ae91a --- /dev/null +++ b/system/lib/compiler-rt/__trap.c @@ -0,0 +1,3 @@ +void __trap() { + __builtin_trap(); +} diff --git a/tests/test_core.py b/tests/test_core.py index 949b3da8b59a9..7413c21327cc7 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -1655,6 +1655,24 @@ class Polymorphic {virtual void member(){}}; } ''', 'exception caught: std::bad_typeid') + @with_both_eh_sjlj + def test_abort_no_dtors(self): + # abort() should not run destructors + out = self.do_run(r''' +#include +#include + +struct Foo { + ~Foo() { std::cout << "Destructing Foo" << std::endl; } +}; + +int main() { + Foo f; + abort(); +} +''', assert_returncode=NON_ZERO) + self.assertNotContained('Destructing Foo', out) + def test_iostream_ctors(self): # iostream stuff must be globally constructed before user global # constructors, so iostream works in global constructors diff --git a/tools/system_libs.py b/tools/system_libs.py index e670e2f1929c3..2420e662932d0 100644 --- a/tools/system_libs.py +++ b/tools/system_libs.py @@ -708,7 +708,8 @@ class libcompiler_rt(MTLibrary, SjLjLibrary): 'stack_ops.S', 'stack_limits.S', 'emscripten_setjmp.c', - 'emscripten_exception_builtins.c' + 'emscripten_exception_builtins.c', + '__trap.c', ])