فهرست منبع

LibC: Fix sigsetjmp and siglongjmp

Jean-Baptiste Boric 3 سال پیش
والد
کامیت
c972afbea6

+ 55 - 22
Userland/Libraries/LibC/arch/i386/setjmp.S

@@ -4,34 +4,67 @@
  * SPDX-License-Identifier: BSD-2-Clause
  */
 
+#include <bits/sighow.h>
+
+//
+// /!\ Read setjmp.h before modifying this file!
+//
+
 .global setjmp
 setjmp:
-    mov 4(%esp), %eax
-    mov %ebx, 0(%eax)
-    mov %esi, 4(%eax)
-    mov %edi, 8(%eax)
-    mov %ebp, 12(%eax)
-    lea 4(%esp), %ecx
-    mov %ecx, 16(%eax)
-    mov (%esp), %ecx
-    mov %ecx, 20(%eax)
+    mov 4(%esp), %ecx       // Grab jmp_buf argument
+    xor %eax, %eax          // Grab val argument (hardcoded to zero)
+    jmp .Lsigset_common
+
+.global sigsetjmp
+sigsetjmp:
+    mov 4(%esp), %ecx       // Grab jmp_buf argument
+    mov 8(%esp), %eax       // Grab val argument
+
+.Lsigset_common:
+    mov %eax, 24(%ecx)      // Store val into did_save_signal_mask
+    movl $0, 28(%ecx)       // Clear saved_signal_mask
+    test %eax, %eax
+    jz .Lsaveregs
+
+    lea 28(%ecx), %eax      // Set argument oldset
+    push %eax
+    push $0                 // Set argument set
+    push $0                 // Set argument how
+    call sigprocmask
+    add $12, %esp
+
+.Lsaveregs:
+    mov (%esp), %edx        // Grab return address
+    mov %ebx, (0 * 4)(%ecx) // Save registers
+    mov %esi, (1 * 4)(%ecx)
+    mov %edi, (2 * 4)(%ecx)
+    mov %ebp, (3 * 4)(%ecx)
+    mov %esp, (4 * 4)(%ecx)
+    mov %edx, (5 * 4)(%ecx)
     xor %eax, %eax
     ret
 
 .global longjmp
 longjmp:
-    mov 4(%esp), %edx
-    mov 8(%esp), %eax
-    mov 0(%edx), %ebx
-    mov 4(%edx), %esi
-    mov 8(%edx), %edi
-    mov 12(%edx), %ebp
-    mov 16(%edx), %ecx
-    mov %ecx, %esp
-    mov 20(%edx), %ecx
+    mov 4(%esp), %ecx       // Grab jmp_buf argument
+    mov 8(%esp), %eax       // Grab val argument
     test %eax, %eax
-    jnz  .nonzero
-    mov 1, %eax
-.nonzero:
-    jmp *%ecx
+    jnz  .Lnonzero
+    mov $1, %eax
 
+.Lnonzero:
+    mov (0 * 4)(%ecx), %ebx // Restore registers
+    mov (1 * 4)(%ecx), %esi
+    mov (2 * 4)(%ecx), %edi
+    mov (3 * 4)(%ecx), %ebp
+    //
+    // Until this point, the stack is still from the caller.
+    //
+    mov (4 * 4)(%ecx), %esp
+    mov (5 * 4)(%ecx), %edx
+    mov %edx, (%esp)        // Patch return address
+    //
+    // From this point on, the former stack has been restored.
+    //
+    ret

+ 53 - 23
Userland/Libraries/LibC/arch/x86_64/setjmp.S

@@ -4,34 +4,64 @@
  * SPDX-License-Identifier: BSD-2-Clause
  */
 
+#include <bits/sighow.h>
+
+//
+// /!\ Read setjmp.h before modifying this file!
+//
+
 .global setjmp
 setjmp:
-    mov %rbx, (%rdi)
-    mov %rbp, 8(%rdi)
-    mov %r12, 16(%rdi)
-    mov %r13, 24(%rdi)
-    mov %r14, 32(%rdi)
-    mov %r15, 40(%rdi)
-    lea 8(%rsp), %rcx
-    mov %rcx, 48(%rdi)
-    mov (%rsp), %rcx
-    mov %rcx, 56(%rdi)
+    mov $0, %esi            // Set val argument to 0
+
+.global sigsetjmp
+sigsetjmp:
+    mov %esi, 60(%rdi)      // Store val into did_save_signal_mask
+    movl $0, 64(%rdi)       // Clear saved_signal_mask
+    test %esi, %esi
+    jz .Lsaveregs
+
+    mov %rdi, %r12          // Preserve sigjmp_buf argument
+    mov $0, %rdi            // Set argument how
+    mov $0, %rsi            // Set argument set
+    lea 64(%rdi), %rdx      // Set argument oldset
+    call sigprocmask
+    mov %r12, %rdi          // Restore sigjmp_buf argument
+
+.Lsaveregs:
+    mov %rbx, (0 * 8)(%rdi) // Save registers
+    mov %r12, (1 * 8)(%rdi)
+    mov %r13, (2 * 8)(%rdi)
+    mov %r14, (3 * 8)(%rdi)
+    mov %r15, (4 * 8)(%rdi)
+    mov %rbp, (5 * 8)(%rdi)
+    mov %rsp, (6 * 8)(%rdi)
+    mov (%rsp), %rax        // Grab return address
+    mov %rax, (7 * 8)(%rdi)    
     xor %eax, %eax
     ret
 
 .global longjmp
 longjmp:
-    mov (%rdi), %rbx
-    mov 8(%rdi), %rbp
-    mov 16(%rdi), %r12
-    mov 24(%rdi), %r13
-    mov 32(%rdi), %r14
-    mov 40(%rdi), %r15
-    mov 48(%rdi), %rsp
-
-    test %rsi, %rsi
-    jnz .nonzero
-    mov 1, %rax
-.nonzero:
-    jmp *56(%rdi)
+    mov %esi, %eax
+    test %eax, %eax
+    jnz  .Lnonzero
+    mov $1, %eax
 
+.Lnonzero:
+    mov (0 * 8)(%rdi), %rbx // Restore registers
+    mov (1 * 8)(%rdi), %r12
+    mov (2 * 8)(%rdi), %r13
+    mov (3 * 8)(%rdi), %r14
+    mov (4 * 8)(%rdi), %r15
+    mov (5 * 8)(%rdi), %rbp
+    //
+    // Until this point, the stack is still from the caller.
+    //
+    mov (6 * 8)(%rdi), %rsp
+    mov (7 * 8)(%rdi), %rcx
+    mov %rcx, (%rsp)        // Patch return address
+    //
+    // From this point on, the former stack has been restored.
+    //
+    ret

+ 31 - 5
Userland/Libraries/LibC/setjmp.h

@@ -14,25 +14,51 @@
 
 __BEGIN_DECLS
 
+//
+// /!\ This structure is accessed inside setjmp.S, keep both files in sync!
+//
+
 struct __jmp_buf {
 #ifdef __i386__
-    uint32_t regs[6];
+    uint32_t ebx;
+    uint32_t esi;
+    uint32_t edi;
+    uint32_t ebp;
+    uint32_t esp;
+    uint32_t eip;
 #elif __x86_64__
-    uint64_t regs[8];
+    uint64_t rbx;
+    uint64_t r12;
+    uint64_t r13;
+    uint64_t r14;
+    uint64_t r15;
+    uint64_t rbp;
+    uint64_t rsp;
+    uint64_t rip;
 #else
 #    error
 #endif
-    bool did_save_signal_mask;
+    int did_save_signal_mask;
     sigset_t saved_signal_mask;
 };
 
 typedef struct __jmp_buf jmp_buf[1];
 typedef struct __jmp_buf sigjmp_buf[1];
 
+/**
+ * Calling conventions mandates that sigsetjmp() cannot call setjmp(),
+ * otherwise the restored calling environment will not be the original caller's
+ * but sigsetjmp()'s and we'll return to the wrong call site on siglongjmp().
+ *
+ * The setjmp(), sigsetjmp() and longjmp() functions have to be implemented in
+ * assembly because they touch the call stack and registers in non-portable
+ * ways. However, we *can* implement siglongjmp() as a standard C function.
+ */
+
 int setjmp(jmp_buf);
-void longjmp(jmp_buf, int val);
+__attribute__((noreturn)) void longjmp(jmp_buf, int val);
 
 int sigsetjmp(sigjmp_buf, int savesigs);
-void siglongjmp(sigjmp_buf, int val);
+__attribute__((noreturn)) void siglongjmp(sigjmp_buf, int val);
 
 __END_DECLS

+ 0 - 11
Userland/Libraries/LibC/signal.cpp

@@ -143,17 +143,6 @@ const char* sys_siglist[NSIG] = {
     "Bad system call",
 };
 
-int sigsetjmp(jmp_buf env, int savesigs)
-{
-    if (savesigs) {
-        int rc = sigprocmask(0, nullptr, &env->saved_signal_mask);
-        assert(rc == 0);
-        env->did_save_signal_mask = true;
-    } else {
-        env->did_save_signal_mask = false;
-    }
-    return setjmp(env);
-}
 void siglongjmp(jmp_buf env, int val)
 {
     if (env->did_save_signal_mask) {