rust/tests/run-make/longjmp-across-rust/foo.c

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

19 lines
231 B
C
Raw Normal View History

rustc: Tweak funclet cleanups of ffi functions This commit is targeted at addressing #48251 by specifically fixing a case where a longjmp over Rust frames on MSVC runs cleanups, accidentally running the "abort the program" cleanup as well. Added in #46833 `extern` ABI functions in Rust will abort the process if Rust panics, and currently this is modeled as a normal cleanup like all other destructors. Unfortunately it turns out that `longjmp` on MSVC is implemented with SEH, the same mechanism used to implement panics in Rust. This means that `longjmp` over Rust frames will run Rust cleanups (even though we don't necessarily want it to). Notably this means that if you `longjmp` over a Rust stack frame then that probably means you'll abort the program because one of the cleanups will abort the process. After some discussion on IRC it turns out that `longjmp` doesn't run cleanups for *caught* exceptions, it only runs cleanups for cleanup pads. Using this information this commit tweaks the codegen for an `extern` function to a catch-all clause for exceptions instead of a cleanup block. This catch-all is equivalent to the C++ code: try { foo(); } catch (...) { bar(); } and in fact our codegen here is designed to match exactly what clang emits for that C++ code! With this tweak a longjmp over Rust code will no longer abort the process. A longjmp will continue to "accidentally" run Rust cleanups (destructors) on MSVC. Other non-MSVC platforms will not rust destructors with a longjmp, so we'll probably still recommend "don't have destructors on the stack", but in any case this is a more surgical fix than #48567 and should help us stick to standard personality functions a bit longer.
2018-02-26 20:59:47 -06:00
#include <assert.h>
#include <setjmp.h>
static jmp_buf ENV;
extern void test_middle();
void test_start(void(*f)()) {
if (setjmp(ENV) != 0)
return;
f();
assert(0);
}
void test_end() {
longjmp(ENV, 1);
assert(0);
}