diff --git a/lib/llvm-backend/build.rs b/lib/llvm-backend/build.rs index 6c8117dd9..904a83fb1 100644 --- a/lib/llvm-backend/build.rs +++ b/lib/llvm-backend/build.rs @@ -209,12 +209,14 @@ fn main() { cc::Build::new() .cpp(true) .file("cpp/object_loader.cpp") + .file("cpp/unwinding.s") .compile("llvm-backend"); println!("cargo:rustc-link-lib=static=llvm-backend"); println!("cargo:rerun-if-changed=build.rs"); println!("cargo:rerun-if-changed=cpp/object_loader.cpp"); println!("cargo:rerun-if-changed=cpp/object_loader.hh"); + println!("cargo:rerun-if-changed=cpp/unwinding.s"); // Enable "nightly" cfg if the current compiler is nightly. if rustc_version::version_meta().unwrap().channel == rustc_version::Channel::Nightly { diff --git a/lib/llvm-backend/cpp/object_loader.cpp b/lib/llvm-backend/cpp/object_loader.cpp index f79b0316d..ff2c4132b 100644 --- a/lib/llvm-backend/cpp/object_loader.cpp +++ b/lib/llvm-backend/cpp/object_loader.cpp @@ -4,37 +4,40 @@ extern "C" void __register_frame(uint8_t *); extern "C" void __deregister_frame(uint8_t *); +extern "C" void unwinding_setjmp(uint8_t **stack_out, void (*func)(void *), void *userdata); +[[noreturn]] extern "C" void unwinding_longjmp(uint8_t *stack_in); struct UnwindPoint { UnwindPoint *prev; - jmp_buf unwind_info; + uint8_t *stack; + std::function *f; std::unique_ptr exception; }; static thread_local UnwindPoint *unwind_state = nullptr; +static void unwind_payload(void *_point) { + UnwindPoint *point = (UnwindPoint *) _point; + (*point->f)(); +} + void catch_unwind(std::function&& f) { UnwindPoint current; current.prev = unwind_state; + current.f = &f; unwind_state = ¤t; - bool rethrow = false; - - if(setjmp(current.unwind_info)) { - rethrow = true; - } else { - f(); + unwinding_setjmp(¤t.stack, unwind_payload, (void *) ¤t); + if(current.exception) { + throw *current.exception; } - - unwind_state = current.prev; - if(rethrow) throw *current.exception; } void unsafe_unwind(std::exception *exception) { UnwindPoint *state = unwind_state; if(state) { state->exception.reset(exception); - longjmp(state->unwind_info, 42); + unwinding_longjmp(state->stack); } else { abort(); } diff --git a/lib/llvm-backend/cpp/unwinding.s b/lib/llvm-backend/cpp/unwinding.s new file mode 100644 index 000000000..dfe91234e --- /dev/null +++ b/lib/llvm-backend/cpp/unwinding.s @@ -0,0 +1,27 @@ +# (save_place, func(userdata), userdata) +.globl _unwinding_setjmp +_unwinding_setjmp: +push %r15 +push %r14 +push %r13 +push %r12 +push %rbx +push %rbp +sub $8, %rsp # 16-byte alignment +mov %rsp, (%rdi) +mov %rdx, %rdi +callq *%rsi +setjmp_ret: +add $8, %rsp +pop %rbp +pop %rbx +pop %r12 +pop %r13 +pop %r14 +pop %r15 +ret + +.globl _unwinding_longjmp +_unwinding_longjmp: +mov %rdi, %rsp +jmp setjmp_ret