From abc2ec7bb5382fcf0aa865272680f45062b7fdef Mon Sep 17 00:00:00 2001
From: 4lDO2 <4lDO2@protonmail.com>
Date: Tue, 30 Jul 2024 23:24:42 +0200
Subject: [PATCH] Add prototype ucontext_t support.

---
 redox-rt/src/arch/i686.rs    |  6 ++--
 redox-rt/src/arch/x86_64.rs  |  5 +++-
 redox-rt/src/signal.rs       | 55 +++++++++++++++++++++++++-----------
 src/platform/linux/signal.rs | 42 +++++++++++++++++++++++++++
 src/platform/redox/signal.rs | 53 ++++++++++++++++++++++++++++++++--
 5 files changed, 138 insertions(+), 23 deletions(-)

diff --git a/redox-rt/src/arch/i686.rs b/redox-rt/src/arch/i686.rs
index 9889ff3a2..f425a6c1f 100644
--- a/redox-rt/src/arch/i686.rs
+++ b/redox-rt/src/arch/i686.rs
@@ -222,13 +222,13 @@ asmfunction!(__relibc_internal_sigentry: ["
 
     push eax
     push dword ptr gs:[{tcb_sa_off} + {sa_tmp_ptr}]
-    sub esp, 2 * 4
+    sub esp, 24
 
     mov ecx, esp
     call {inner}
 
-    fxrstor [esp + 16]
-    add esp, 16 + 29 * 16 + 2 * 4
+    fxrstor [esp + 32]
+    add esp, 32 + 29 * 16 + 2 * 4
 
     pop ebp
     pop esi
diff --git a/redox-rt/src/arch/x86_64.rs b/redox-rt/src/arch/x86_64.rs
index a0756c128..1faf2ab0c 100644
--- a/redox-rt/src/arch/x86_64.rs
+++ b/redox-rt/src/arch/x86_64.rs
@@ -1,5 +1,6 @@
 use core::{
     mem::offset_of,
+    ptr::NonNull,
     sync::atomic::{AtomicU8, Ordering},
 };
 
@@ -33,6 +34,7 @@ pub struct SigArea {
     pub altstack_bottom: usize,
     pub disable_signals_depth: u64,
     pub last_sig_was_restart: bool,
+    pub last_sigstack: Option<NonNull<SigStack>>,
 }
 
 #[repr(C, align(16))]
@@ -301,11 +303,12 @@ asmfunction!(__relibc_internal_sigentry: ["
 5:
     push rax // selected signal
     push fs:[{tcb_sa_off} + {sa_tmp_ptr}]
+    sub rsp, 48 // alloc space for ucontext fields
 
     mov rdi, rsp
     call {inner}
 
-    add rsp, 16
+    add rsp, 64
 
     fxrstor64 [rsp + 16 * 16]
 
diff --git a/redox-rt/src/signal.rs b/redox-rt/src/signal.rs
index 529d1b356..4e9ca55cd 100644
--- a/redox-rt/src/signal.rs
+++ b/redox-rt/src/signal.rs
@@ -1,6 +1,7 @@
 use core::{
     cell::{Cell, UnsafeCell},
     ffi::{c_int, c_void},
+    ptr::NonNull,
     sync::atomic::{AtomicU8, AtomicUsize, Ordering},
 };
 
@@ -22,14 +23,19 @@ pub fn sighandler_function() -> usize {
     __relibc_internal_sigentry as usize
 }
 
+/// ucontext_t representation
 #[repr(C)]
 pub struct SigStack {
     #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
-    _pad: [usize; 0], // pad to 16 bytes alignment
+    _pad: [usize; 1], // pad from 7*8 to 64
 
     #[cfg(target_arch = "x86")]
-    _pad: [usize; 2], // pad to 16 bytes alignment
+    _pad: [usize; 0], // don't pad from 8*4
 
+    pub link: *mut SigStack,
+
+    pub old_stack: PosixStackt,
+    pub old_mask: u64,
     sival: usize,
     sig_num: usize,
 
@@ -38,10 +44,35 @@ pub struct SigStack {
     // aarch64: 272 bytes (SIMD TODO)
     pub regs: ArchIntRegs,
 }
+#[repr(C)]
+pub struct PosixStackt {
+    pub sp: *mut (),
+    pub flags: i32,
+    pub size: usize,
+}
+
+#[repr(C)]
+// TODO: This struct is for practical reasons locked to Linux's ABI, but avoid redefining
+// it here. Alternatively, check at compile time that the structs are equivalent.
+pub struct SiginfoAbi {
+    pub si_signo: i32,
+    pub si_errno: i32,
+    pub si_code: i32,
+    pub si_pid: i32,      // pid_t
+    pub si_uid: i32,      // uid_t
+    pub si_addr: *mut (), // *mut c_void
+    pub si_status: i32,
+    pub si_value: usize, // sigval
+}
 
 #[inline(always)]
 unsafe fn inner(stack: &mut SigStack) {
     let os = &Tcb::current().unwrap().os_specific;
+
+    let stack_ptr = NonNull::from(&mut *stack);
+    stack.link = core::mem::replace(&mut (*os.arch.get()).last_sigstack, Some(stack_ptr))
+        .map_or_else(core::ptr::null_mut, |x| x.as_ptr());
+
     let signals_were_disabled = (*os.arch.get()).disable_signals_depth > 0;
 
     let _targeted_thread_not_process = stack.sig_num >= 64;
@@ -119,21 +150,11 @@ unsafe fn inner(stack: &mut SigStack) {
     if sigaction.flags.contains(SigactionFlags::SIGINFO)
         && let Some(sigaction) = handler.sigaction
     {
+        stack.old_mask = ((prev_w1 >> 32) << 32) | (prev_w0 & 0xffff_ffff);
+        // TODO: stack.old_stack
+
         //let _ = syscall::write(1, alloc::format!("SIGACTION {:p}\n", sigaction).as_bytes());
-        // TODO: This struct is for practical reasons locked to Linux's ABI, but avoid redefining
-        // it here. Alternatively, check at compile time that the structs are equivalent.
-        #[repr(C)]
-        struct siginfo {
-            si_signo: c_int,
-            si_errno: c_int,
-            si_code: c_int,
-            si_pid: c_int, // TODO pid_t
-            si_uid: c_int, // TODO uid_t
-            si_addr: *mut c_void,
-            si_status: c_int,
-            si_value: usize, // TODO union
-        }
-        let info = siginfo {
+        let info = SiginfoAbi {
             si_signo: stack.sig_num as c_int,
             si_addr: core::ptr::null_mut(),
             si_code: 0, // TODO: SIG_QUEUE/SIG_USER/etc
@@ -146,7 +167,7 @@ unsafe fn inner(stack: &mut SigStack) {
         sigaction(
             stack.sig_num as c_int,
             core::ptr::addr_of!(info).cast(),
-            core::ptr::null_mut(), // TODO
+            stack as *mut SigStack as *mut (),
         );
     } else if let Some(handler) = handler.handler {
         //let _ = syscall::write(1, alloc::format!("HANDLER {:p}\n", handler).as_bytes());
diff --git a/src/platform/linux/signal.rs b/src/platform/linux/signal.rs
index b1d4d2e06..a6ec22fb3 100644
--- a/src/platform/linux/signal.rs
+++ b/src/platform/linux/signal.rs
@@ -14,6 +14,48 @@ use crate::{
     },
 };
 
+// Mirrors the ucontext_t struct from the libc crate on Linux.
+#[repr(C)]
+pub struct ucontext_t {
+    pub uc_flags: c_ulong,
+    pub uc_link: *mut ucontext_t,
+    pub uc_stack: stack_t,
+    pub uc_mcontext: mcontext_t,
+    pub uc_sigmask: sigset_t,
+    __private: [u8; 512],
+}
+#[repr(C)]
+pub struct _libc_fpstate {
+    pub cwd: u16,
+    pub swd: u16,
+    pub ftw: u16,
+    pub fop: u16,
+    pub rip: u64,
+    pub rdp: u64,
+    pub mxcsr: u32,
+    pub mxcr_mask: u32,
+    pub _st: [_libc_fpxreg; 8],
+    pub _xmm: [_libc_xmmreg; 16],
+    __private: [u64; 12],
+}
+#[repr(C)]
+pub struct _libc_fpxreg {
+    pub significand: [u16; 4],
+    pub exponent: u16,
+    __private: [u16; 3],
+}
+
+#[repr(C)]
+pub struct _libc_xmmreg {
+    pub element: [u32; 4],
+}
+#[repr(C)]
+pub struct mcontext_t {
+    pub gregs: [i64; 23], // TODO: greg_t?
+    pub fpregs: *mut _libc_fpstate,
+    __private: [u64; 8],
+}
+
 impl PalSignal for Sys {
     unsafe fn getitimer(which: c_int, out: *mut itimerval) -> c_int {
         e(syscall!(GETITIMER, which, out)) as c_int
diff --git a/src/platform/redox/signal.rs b/src/platform/redox/signal.rs
index 6f343754c..3be1da844 100644
--- a/src/platform/redox/signal.rs
+++ b/src/platform/redox/signal.rs
@@ -1,5 +1,7 @@
-use core::mem;
-use redox_rt::signal::{Sigaction, SigactionFlags, SigactionKind, Sigaltstack, SignalHandler};
+use core::mem::{self, offset_of};
+use redox_rt::signal::{
+    SigStack, Sigaction, SigactionFlags, SigactionKind, Sigaltstack, SignalHandler,
+};
 use syscall::{self, Result};
 
 use super::{
@@ -20,6 +22,53 @@ use crate::{
     platform::ERRNO,
 };
 
+#[repr(C)]
+pub struct ucontext_t {
+    #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
+    _pad: [usize; 1], // pad from 7*8 to 64
+
+    #[cfg(target_arch = "x86")]
+    _pad: [usize; 0], // don't pad from 8*4
+
+    pub uc_link: *mut ucontext_t,
+    pub uc_stack: stack_t,
+    pub uc_sigmask: sigset_t,
+    _sival: usize,
+    _signum: usize,
+    pub uc_mcontext: mcontext_t,
+}
+
+const _: () = {
+    const fn assert_eq(a: usize, b: usize) {
+        if a != b {
+            panic!("compile-time struct verification failed");
+        }
+    }
+    assert_eq(offset_of!(ucontext_t, uc_link), offset_of!(SigStack, link));
+    assert_eq(
+        offset_of!(ucontext_t, uc_stack),
+        offset_of!(SigStack, old_stack),
+    );
+    assert_eq(
+        offset_of!(ucontext_t, uc_sigmask),
+        offset_of!(SigStack, old_mask),
+    );
+    assert_eq(
+        offset_of!(ucontext_t, uc_mcontext),
+        offset_of!(SigStack, regs),
+    );
+};
+
+#[repr(C)]
+pub struct mcontext_t {
+    #[cfg(target_arch = "x86")]
+    _opaque: [u8; 512],
+    #[cfg(target_arch = "x86-64")]
+    _opaque: [u8; 864],
+    #[cfg(target_arch = "aarch64")]
+    _opaque: [u8; 272],
+}
+
 impl PalSignal for Sys {
     unsafe fn getitimer(which: c_int, out: *mut itimerval) -> c_int {
         let path = match which {
-- 
GitLab