diff options
-rw-r--r-- | Userland/Libraries/LibC/arch/i386/setjmp.S | 77 | ||||
-rw-r--r-- | Userland/Libraries/LibC/arch/x86_64/setjmp.S | 76 | ||||
-rw-r--r-- | Userland/Libraries/LibC/setjmp.h | 36 | ||||
-rw-r--r-- | Userland/Libraries/LibC/signal.cpp | 11 |
4 files changed, 139 insertions, 61 deletions
diff --git a/Userland/Libraries/LibC/arch/i386/setjmp.S b/Userland/Libraries/LibC/arch/i386/setjmp.S index 629d894b97..9178690772 100644 --- a/Userland/Libraries/LibC/arch/i386/setjmp.S +++ b/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 diff --git a/Userland/Libraries/LibC/arch/x86_64/setjmp.S b/Userland/Libraries/LibC/arch/x86_64/setjmp.S index 1f325b6532..e00c1ebd46 100644 --- a/Userland/Libraries/LibC/arch/x86_64/setjmp.S +++ b/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 diff --git a/Userland/Libraries/LibC/setjmp.h b/Userland/Libraries/LibC/setjmp.h index f5258cdb1e..bb08783088 100644 --- a/Userland/Libraries/LibC/setjmp.h +++ b/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 diff --git a/Userland/Libraries/LibC/signal.cpp b/Userland/Libraries/LibC/signal.cpp index 610f6d8a58..1ecfddc2bf 100644 --- a/Userland/Libraries/LibC/signal.cpp +++ b/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) { |