From 35a3efd936cc9d53aef8393439cdbecd6ccb90a7 Mon Sep 17 00:00:00 2001
From: Bendeguz Pisch <pisch.beni@gmail.com>
Date: Sat, 4 Jan 2025 00:29:19 +0000
Subject: [PATCH] Implement sigsetjmp and siglongjmp for X64

---
 include/setjmp.h                              |  3 +-
 src/header/setjmp/mod.rs                      |  6 +++
 src/header/signal/mod.rs                      | 41 ++++++++++++++++++-
 src/header/signal/sigsetjmp/README.md         |  5 +++
 .../signal/sigsetjmp/x86_64/sigsetjmp.s       | 26 ++++++++++++
 tests/Makefile                                |  1 +
 tests/expected/bins_dynamic/sigsetjmp.stderr  |  0
 tests/expected/bins_dynamic/sigsetjmp.stdout  |  2 +
 tests/expected/bins_static/sigsetjmp.stderr   |  0
 tests/expected/bins_static/sigsetjmp.stdout   |  2 +
 tests/sigsetjmp.c                             | 15 +++++++
 11 files changed, 98 insertions(+), 3 deletions(-)
 create mode 100644 src/header/signal/sigsetjmp/README.md
 create mode 100644 src/header/signal/sigsetjmp/x86_64/sigsetjmp.s
 create mode 100644 tests/expected/bins_dynamic/sigsetjmp.stderr
 create mode 100644 tests/expected/bins_dynamic/sigsetjmp.stdout
 create mode 100644 tests/expected/bins_static/sigsetjmp.stderr
 create mode 100644 tests/expected/bins_static/sigsetjmp.stdout
 create mode 100644 tests/sigsetjmp.c

diff --git a/include/setjmp.h b/include/setjmp.h
index 4c42dd686..fbbc55df5 100644
--- a/include/setjmp.h
+++ b/include/setjmp.h
@@ -58,7 +58,8 @@ typedef unsigned long long jmp_buf[8];
 #endif
 
 #ifdef __x86_64__
-typedef unsigned long jmp_buf[8];
+typedef unsigned long jmp_buf[16];
+typedef jmp_buf sigjmp_buf;
 #endif
 
 #ifdef __riscv
diff --git a/src/header/setjmp/mod.rs b/src/header/setjmp/mod.rs
index 56b93aa1c..669b786b5 100644
--- a/src/header/setjmp/mod.rs
+++ b/src/header/setjmp/mod.rs
@@ -19,3 +19,9 @@ platform_specific! {
     "x86_64","x86_64","s";
     "riscv64", "riscv64", "S";
 }
+
+//Each platform has different sizes for sigjmp_buf, currently only x86_64 is supported
+extern "C" {
+    pub fn setjmp(jb: *mut u64) -> i32;
+    pub fn longjmp(jb: *mut u64, ret: i32);
+}
diff --git a/src/header/signal/mod.rs b/src/header/signal/mod.rs
index 1457a7337..39e0b3ea0 100644
--- a/src/header/signal/mod.rs
+++ b/src/header/signal/mod.rs
@@ -1,13 +1,13 @@
 //! signal implementation for Redox, following http://pubs.opengroup.org/onlinepubs/7908799/xsh/signal.h.html
 
-use core::{mem, ptr};
+use core::{arch::global_asm, mem, ptr};
 
 use cbitset::BitSet;
 
 use crate::{
     c_str::CStr,
     error::{Errno, ResultExt},
-    header::{errno, time::timespec},
+    header::{errno, setjmp, time::timespec},
     platform::{self, types::*, Pal, PalSignal, Sys},
 };
 
@@ -86,6 +86,43 @@ pub type siginfo_t = siginfo;
 
 pub type stack_t = sigaltstack;
 
+// Macro based on setjmp, only x86_64 is supported at the moment
+macro_rules! sigsetjmp_platforms {
+    ($($rust_arch:expr,$c_arch:expr,$ext:expr;)+) => {
+        $(
+            #[cfg(target_arch = $rust_arch)]
+            global_asm!(include_str!(concat!("sigsetjmp/", $c_arch, "/sigsetjmp.", $ext)));
+        )+
+    }
+}
+
+//Insert more platforms here
+sigsetjmp_platforms! {
+    "x86_64","x86_64","s";
+}
+
+extern "C" {
+    pub fn sigsetjmp(jb: *mut u64, savemask: i32) -> i32;
+}
+
+//NOTE for the following two functions, to see why they're implemented slightly differently from their intended behavior, read
+//     https://git.musl-libc.org/cgit/musl/commit/?id=583e55122e767b1586286a0d9c35e2a4027998ab
+#[no_mangle]
+unsafe extern "C" fn __sigsetjmp_tail(jb: *mut u64, ret: i32) -> i32 {
+    let set = jb.wrapping_add(9);
+    if ret > 0 {
+        sigprocmask(SIG_SETMASK, set, ptr::null_mut());
+    } else {
+        sigprocmask(SIG_SETMASK, ptr::null_mut(), set);
+    }
+    ret
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn siglongjmp(jb: *mut u64, ret: i32) {
+    setjmp::longjmp(jb, ret);
+}
+
 #[no_mangle]
 pub extern "C" fn kill(pid: pid_t, sig: c_int) -> c_int {
     Sys::kill(pid, sig).map(|()| 0).or_minus_one_errno()
diff --git a/src/header/signal/sigsetjmp/README.md b/src/header/signal/sigsetjmp/README.md
new file mode 100644
index 000000000..b41a4e552
--- /dev/null
+++ b/src/header/signal/sigsetjmp/README.md
@@ -0,0 +1,5 @@
+# implementation of sigsetjmp
+
+All code found in the subdirectories of this directory belong to [musl libc](https://musl.libc.org/). They own it. All rights go to them.
+Currently only x86_64 is supported, because I don't have access to non-x86 devices to test it properly.
+Converted to Intel assembly syntax, according to setjmp and longjmp found in setjmp header.
diff --git a/src/header/signal/sigsetjmp/x86_64/sigsetjmp.s b/src/header/signal/sigsetjmp/x86_64/sigsetjmp.s
new file mode 100644
index 000000000..287ec0023
--- /dev/null
+++ b/src/header/signal/sigsetjmp/x86_64/sigsetjmp.s
@@ -0,0 +1,26 @@
+.global sigsetjmp
+.global _sigsetjmp
+.global __sigsetjmp
+.type sigsetjmp,@function
+.type _sigsetjmp,@function
+.type __sigsetjmp,@function
+sigsetjmp:
+_sigsetjmp:
+__sigsetjmp:
+	test esi,esi
+	jz 1f
+
+	pop qword ptr [rdi+64]
+	mov qword ptr [rdi+80],rbx
+	mov rbx,rdi
+
+	call setjmp
+
+	push qword ptr [rbx+64]
+	mov rdi,rbx
+	mov esi,eax
+	mov rbx, qword ptr[rbx+80]
+
+	jmp __sigsetjmp_tail
+
+1:	jmp setjmp
diff --git a/tests/Makefile b/tests/Makefile
index eade24365..102b3ea4a 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -35,6 +35,7 @@ EXPECT_NAMES=\
 	sigaction \
 	sigaltstack \
 	signal \
+	sigsetjmp \
 	stdio/all \
 	stdio/buffer \
 	stdio/dprintf \
diff --git a/tests/expected/bins_dynamic/sigsetjmp.stderr b/tests/expected/bins_dynamic/sigsetjmp.stderr
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/expected/bins_dynamic/sigsetjmp.stdout b/tests/expected/bins_dynamic/sigsetjmp.stdout
new file mode 100644
index 000000000..eb99a60c8
--- /dev/null
+++ b/tests/expected/bins_dynamic/sigsetjmp.stdout
@@ -0,0 +1,2 @@
+Starting jump...
+Jump done.
diff --git a/tests/expected/bins_static/sigsetjmp.stderr b/tests/expected/bins_static/sigsetjmp.stderr
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/expected/bins_static/sigsetjmp.stdout b/tests/expected/bins_static/sigsetjmp.stdout
new file mode 100644
index 000000000..eb99a60c8
--- /dev/null
+++ b/tests/expected/bins_static/sigsetjmp.stdout
@@ -0,0 +1,2 @@
+Starting jump...
+Jump done.
diff --git a/tests/sigsetjmp.c b/tests/sigsetjmp.c
new file mode 100644
index 000000000..0063d2152
--- /dev/null
+++ b/tests/sigsetjmp.c
@@ -0,0 +1,15 @@
+#include <stdio.h>
+#include <setjmp.h>
+#include <signal.h>
+
+int main() {
+sigjmp_buf jb;
+
+if (sigsetjmp(jb, 1)) {
+printf("Jump done.\n");
+} else {
+printf ("Starting jump...\n");
+siglongjmp(jb, 1);
+}
+return 0;
+}
-- 
GitLab