| //===-- Implementation of setcontext for x86_64 ---------------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "src/ucontext/setcontext.h" | |
| #include "include/llvm-libc-types/ucontext_t.h" | |
| #include "src/__support/common.h" | |
| #include "src/__support/macros/config.h" | |
| #include "hdr/types/size_t.h" | |
| #include "include/llvm-libc-macros/signal-macros.h" | |
| #include <sys/syscall.h> | |
| namespace LIBC_NAMESPACE_DECL { | |
| __attribute__((naked)) LLVM_LIBC_FUNCTION(int, setcontext, | |
| (const ucontext_t *ucp)) noexcept { | |
| asm(R"( | |
| # ucp is in rdi | |
| # Restore the signal mask using rt_sigprocmask syscall. | |
| # rt_sigprocmask(SIG_SETMASK, &ucp->uc_sigmask, NULL, sizeof(sigset_t)) | |
| # Note: Restoring the signal mask early means that if a signal | |
| # arrives before the context switch is complete, it will run on | |
| # the old stack with the new mask. Doing this later is difficult | |
| # because the syscall clobbers registers. | |
| # | |
| # Note: We could avoid these stack operations by saving rdi in a | |
| # non-volatile register (like r12) across the syscall, since all | |
| # registers will be overwritten anyway. We stick to the stack for | |
| # simplicity and readability. | |
| pushq %%rdi # Save ucp | |
| leaq %c[sigmask](%%rdi), %%rsi # set = &ucp->uc_sigmask | |
| xorq %%rdx, %%rdx # oldset = NULL | |
| movq $%c[sigset_size], %%r10 # sigsetsize = sizeof(sigset_t) | |
| movq $%c[sig_setmask], %%rdi # how = SIG_SETMASK | |
| movq $%c[syscall_num], %%rax | |
| syscall | |
| popq %%rdi # Restore ucp | |
| # Restore floating point state | |
| fxrstorq %c[fpregs_mem](%%rdi) | |
| # Restore other general purpose registers | |
| mov %c[r8](%%rdi), %%r8 | |
| mov %c[r9](%%rdi), %%r9 | |
| mov %c[r10](%%rdi), %%r10 | |
| mov %c[r11](%%rdi), %%r11 | |
| mov %c[r12](%%rdi), %%r12 | |
| mov %c[r13](%%rdi), %%r13 | |
| mov %c[r14](%%rdi), %%r14 | |
| mov %c[r15](%%rdi), %%r15 | |
| mov %c[rbp](%%rdi), %%rbp | |
| mov %c[rbx](%%rdi), %%rbx | |
| mov %c[rdx](%%rdi), %%rdx | |
| mov %c[rax](%%rdi), %%rax | |
| mov %c[rcx](%%rdi), %%rcx | |
| # Restore stack pointer | |
| mov %c[rsp](%%rdi), %%rsp | |
| # Push saved RIP onto the new stack to use ret later | |
| pushq %c[rip](%%rdi) | |
| # Restore RSI and RDI last | |
| mov %c[rsi](%%rdi), %%rsi | |
| mov %c[rdi](%%rdi), %%rdi | |
| retq | |
| )" ::[sigset_size] "i"(sizeof(sigset_t)), | |
| [syscall_num] "i"(SYS_rt_sigprocmask), [sig_setmask] "i"(SIG_SETMASK), | |
| [r8] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[REG_R8])), | |
| [r9] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[REG_R9])), | |
| [r10] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[REG_R10])), | |
| [r11] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[REG_R11])), | |
| [r12] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[REG_R12])), | |
| [r13] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[REG_R13])), | |
| [r14] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[REG_R14])), | |
| [r15] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[REG_R15])), | |
| [rdi] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[REG_RDI])), | |
| [rsi] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[REG_RSI])), | |
| [rbp] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[REG_RBP])), | |
| [rbx] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[REG_RBX])), | |
| [rdx] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[REG_RDX])), | |
| [rax] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[REG_RAX])), | |
| [rcx] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[REG_RCX])), | |
| [rsp] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[REG_RSP])), | |
| [rip] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[REG_RIP])), | |
| [fpregs_mem] "i"(__builtin_offsetof(ucontext_t, __fpregs_mem)), | |
| [sigmask] "i"(__builtin_offsetof(ucontext_t, uc_sigmask)) | |
| : "memory", "rcx", "r11"); | |
| } | |
| } // namespace LIBC_NAMESPACE_DECL |