blob: 408298bed3d358799f1490fb006ae3d0f8dd962f [file] [edit]
//===-- 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