summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Userland/Libraries/LibC/arch/i386/setjmp.S77
-rw-r--r--Userland/Libraries/LibC/arch/x86_64/setjmp.S76
-rw-r--r--Userland/Libraries/LibC/setjmp.h36
-rw-r--r--Userland/Libraries/LibC/signal.cpp11
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) {