[libc-commits] [libc] [libc] Implement getcontext and setcontext for x86_64 (PR #192343)
Pavel Labath via libc-commits
libc-commits at lists.llvm.org
Thu Apr 16 01:16:00 PDT 2026
================
@@ -0,0 +1,97 @@
+//===-- Implementation of getcontext 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/getcontext.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 <sys/syscall.h>
+
+namespace LIBC_NAMESPACE_DECL {
+
+// We use naked because we need to capture the exact register state
+// at the moment of the function call, avoiding any compiler prologue/epilogue.
+__attribute__((naked)) LLVM_LIBC_FUNCTION(int, getcontext, (ucontext_t * ucp)) {
+ asm(R"(
+ # ucp is in rdi
+
+ # Save general purpose registers
+ mov %%r8, %c[r8](%%rdi)
+ mov %%r9, %c[r9](%%rdi)
+ mov %%r10, %c[r10](%%rdi)
+ mov %%r11, %c[r11](%%rdi)
+ mov %%r12, %c[r12](%%rdi)
+ mov %%r13, %c[r13](%%rdi)
+ mov %%r14, %c[r14](%%rdi)
+ mov %%r15, %c[r15](%%rdi)
+ mov %%rdi, %c[rdi](%%rdi)
+ mov %%rsi, %c[rsi](%%rdi)
+ mov %%rbp, %c[rbp](%%rdi)
+ mov %%rbx, %c[rbx](%%rdi)
+ mov %%rdx, %c[rdx](%%rdi)
+ # getcontext should return 0 when resumed by setcontext.
+ # So we save 0 into the RAX register of the context.
+ movq $0, %c[rax](%%rdi)
+ mov %%rcx, %c[rcx](%%rdi)
+
+ # The stack pointer before the call is rsp + sizeof(void*).
+ # The return address was pushed when this function was called.
+ # Save instruction pointer and stack pointer
+ mov (%%rsp), %%rax
+ mov %%rax, %c[rip](%%rdi)
+ lea %c[ret_size](%%rsp), %%rax
+ mov %%rax, %c[rsp](%%rdi)
+
+ # Save floating point state
+ fxsaveq %c[fpregs_mem](%%rdi)
+ # Point mcontext.fpregs to our internal FP storage
+ lea %c[fpregs_mem](%%rdi), %%rax
+ mov %%rax, %c[fpregs_ptr](%%rdi)
+
+ # Capture the signal mask using rt_sigprocmask syscall.
+ # rt_sigprocmask(SIG_BLOCK, NULL, &ucp->uc_sigmask, sizeof(sigset_t))
+ leaq %c[sigmask](%%rdi), %%rdx # oldset = &ucp->uc_sigmask
+ xorq %%rsi, %%rsi # set = NULL
+ movq $0, %%rdi # SIG_BLOCK (captured mask in oldset)
+ movq $%c[sigset_size], %%r10
+ movq $%c[syscall_num], %%rax
+ syscall
+
+ # getcontext should return 0 on success
+ xor %%eax, %%eax
+
+ retq
+ )" ::[ret_size] "i"(sizeof(void *)),
+ [sigset_size] "i"(sizeof(sigset_t)),
+ [syscall_num] "i"(SYS_rt_sigprocmask),
+ [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)),
+ [fpregs_ptr] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.fpregs)),
+ [sigmask] "i"(__builtin_offsetof(ucontext_t, uc_sigmask))
+ : "memory", "rcx", "r11");
----------------
labath wrote:
I'm not sure what to think about these. Like, the only way the clobber list can matter is if the function gets inlined somehow. I don't think the compiler can do that, but even if it can, we probably don't want it to happen. So, I'm tempted to leave this empty, but if you want to put something here, then it should probably include all modified registers (I see rdi, rsi, rax, r10)
https://github.com/llvm/llvm-project/pull/192343
More information about the libc-commits
mailing list