[libc-commits] [libc] [libc] Implement ucontext functions for x86_64 (PR #187712)
Jeff Bailey via libc-commits
libc-commits at lists.llvm.org
Mon Mar 23 07:22:35 PDT 2026
https://github.com/kaladron updated https://github.com/llvm/llvm-project/pull/187712
>From 37304c525e63a355ef7e029f7fd0050a168d29c7 Mon Sep 17 00:00:00 2001
From: Jeff Bailey <jbailey at raspberryginger.com>
Date: Fri, 20 Mar 2026 14:11:51 +0000
Subject: [PATCH 1/3] [libc] Implement ucontext functions for x86_64
Implemented getcontext, setcontext, makecontext, and swapcontext for x86_64:
- getcontext: Captures general-purpose registers and captures FP state (fxsave).
- setcontext: Restores general-purpose registers and FP state (fxrstor).
- swapcontext: Saves current context into oucp and switches to ucp.
- makecontext: Sets up a new stack and trampoline for context transition.
Updated ucontext_t with 16-byte alignment needed for fxsave/fxrstor.
Added unit tests covering context switching, argument passing, and uc_link.
Added TODOs for future signal mask support using rt_sigprocmask.
---
libc/config/linux/x86_64/entrypoints.txt | 6 ++
libc/include/CMakeLists.txt | 12 +++
libc/include/llvm-libc-types/CMakeLists.txt | 2 +
libc/include/llvm-libc-types/mcontext_t.h | 78 ++++++++++++++
libc/include/llvm-libc-types/ucontext_t.h | 32 ++++++
libc/include/ucontext.h.def | 21 ++++
libc/include/ucontext.yaml | 37 +++++++
libc/src/CMakeLists.txt | 1 +
libc/src/ucontext/CMakeLists.txt | 29 ++++++
libc/src/ucontext/getcontext.h | 21 ++++
libc/src/ucontext/makecontext.h | 21 ++++
libc/src/ucontext/setcontext.h | 21 ++++
libc/src/ucontext/swapcontext.h | 21 ++++
libc/src/ucontext/x86_64/CMakeLists.txt | 60 +++++++++++
libc/src/ucontext/x86_64/getcontext.cpp | 94 +++++++++++++++++
libc/src/ucontext/x86_64/makecontext.cpp | 84 +++++++++++++++
libc/src/ucontext/x86_64/setcontext.cpp | 90 ++++++++++++++++
libc/src/ucontext/x86_64/swapcontext.cpp | 107 +++++++++++++++++++
libc/test/src/CMakeLists.txt | 1 +
libc/test/src/ucontext/CMakeLists.txt | 14 +++
libc/test/src/ucontext/ucontext_test.cpp | 109 ++++++++++++++++++++
21 files changed, 861 insertions(+)
create mode 100644 libc/include/llvm-libc-types/mcontext_t.h
create mode 100644 libc/include/llvm-libc-types/ucontext_t.h
create mode 100644 libc/include/ucontext.h.def
create mode 100644 libc/include/ucontext.yaml
create mode 100644 libc/src/ucontext/CMakeLists.txt
create mode 100644 libc/src/ucontext/getcontext.h
create mode 100644 libc/src/ucontext/makecontext.h
create mode 100644 libc/src/ucontext/setcontext.h
create mode 100644 libc/src/ucontext/swapcontext.h
create mode 100644 libc/src/ucontext/x86_64/CMakeLists.txt
create mode 100644 libc/src/ucontext/x86_64/getcontext.cpp
create mode 100644 libc/src/ucontext/x86_64/makecontext.cpp
create mode 100644 libc/src/ucontext/x86_64/setcontext.cpp
create mode 100644 libc/src/ucontext/x86_64/swapcontext.cpp
create mode 100644 libc/test/src/ucontext/CMakeLists.txt
create mode 100644 libc/test/src/ucontext/ucontext_test.cpp
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 0602955dc1cb1..e6342e792dd64 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -435,6 +435,12 @@ set(TARGET_LIBC_ENTRYPOINTS
# sys/time.h entrypoints
libc.src.sys.time.setitimer
libc.src.sys.time.getitimer
+
+ # ucontext.h entrypoints
+ libc.src.ucontext.getcontext
+ libc.src.ucontext.setcontext
+ libc.src.ucontext.makecontext
+ libc.src.ucontext.swapcontext
)
if(LLVM_LIBC_INCLUDE_SCUDO)
diff --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt
index b2ebd035872aa..ba669a9db84e5 100644
--- a/libc/include/CMakeLists.txt
+++ b/libc/include/CMakeLists.txt
@@ -432,6 +432,18 @@ add_header_macro(
.llvm_libc_common_h
)
+add_header_macro(
+ ucontext
+ ../libc/include/ucontext.yaml
+ ucontext.h
+ DEPENDS
+ .llvm_libc_common_h
+ .llvm-libc-types.mcontext_t
+ .llvm-libc-types.ucontext_t
+ .llvm-libc-types.sigset_t
+ .llvm-libc-types.stack_t
+)
+
add_header_macro(
sched
../libc/include/sched.yaml
diff --git a/libc/include/llvm-libc-types/CMakeLists.txt b/libc/include/llvm-libc-types/CMakeLists.txt
index 5d82660fd6762..0e2e43aa296c6 100644
--- a/libc/include/llvm-libc-types/CMakeLists.txt
+++ b/libc/include/llvm-libc-types/CMakeLists.txt
@@ -99,6 +99,8 @@ add_header(union_sigval HDR union_sigval.h)
add_header(siginfo_t HDR siginfo_t.h DEPENDS .union_sigval .pid_t .uid_t .clock_t)
add_header(sig_atomic_t HDR sig_atomic_t.h)
add_header(sigset_t HDR sigset_t.h DEPENDS libc.include.llvm-libc-macros.signal_macros)
+add_header(mcontext_t HDR mcontext_t.h)
+add_header(ucontext_t HDR ucontext_t.h DEPENDS .mcontext_t .sigset_t .stack_t)
add_header(__jmp_buf HDR __jmp_buf.h DEPENDS .sigset_t)
add_header(jmp_buf HDR jmp_buf.h DEPENDS .__jmp_buf)
add_header(sigjmp_buf HDR sigjmp_buf.h DEPENDS .__jmp_buf)
diff --git a/libc/include/llvm-libc-types/mcontext_t.h b/libc/include/llvm-libc-types/mcontext_t.h
new file mode 100644
index 0000000000000..c45040fc66243
--- /dev/null
+++ b/libc/include/llvm-libc-types/mcontext_t.h
@@ -0,0 +1,78 @@
+//===-- Definition of type mcontext_t -------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_TYPES_MCONTEXT_T_H
+#define LLVM_LIBC_TYPES_MCONTEXT_T_H
+
+#if defined(__x86_64__)
+typedef long long int greg_t;
+typedef greg_t gregset_t[23];
+
+enum {
+ REG_R8 = 0,
+ REG_R9,
+ REG_R10,
+ REG_R11,
+ REG_R12,
+ REG_R13,
+ REG_R14,
+ REG_R15,
+ REG_RDI,
+ REG_RSI,
+ REG_RBP,
+ REG_RBX,
+ REG_RDX,
+ REG_RAX,
+ REG_RCX,
+ REG_RSP,
+ REG_RIP,
+ REG_EFL,
+ REG_CSGSFS,
+ REG_ERR,
+ REG_TRAPNO,
+ REG_OLDMASK,
+ REG_CR2
+};
+
+struct _libc_fpxreg {
+ unsigned short significand[4];
+ unsigned short exponent;
+ unsigned short padding[3];
+};
+
+struct _libc_xmmreg {
+ unsigned int element[4];
+};
+
+struct _libc_fpstate {
+ unsigned short cwd;
+ unsigned short swd;
+ unsigned short ftw;
+ unsigned short fop;
+ unsigned long long rip;
+ unsigned long long rdp;
+ unsigned int mxcsr;
+ unsigned int mxcr_mask;
+ struct _libc_fpxreg _st[8];
+ struct _libc_xmmreg _xmm[16];
+ unsigned int padding[24];
+};
+
+typedef struct _libc_fpstate *fpregset_t;
+
+typedef struct {
+ gregset_t gregs;
+ fpregset_t fpregs;
+ unsigned long long __reserved1[8];
+} mcontext_t;
+
+#else
+#error "mcontext_t not available for your target architecture."
+#endif
+
+#endif // LLVM_LIBC_TYPES_MCONTEXT_T_H
diff --git a/libc/include/llvm-libc-types/ucontext_t.h b/libc/include/llvm-libc-types/ucontext_t.h
new file mode 100644
index 0000000000000..068c257bc470e
--- /dev/null
+++ b/libc/include/llvm-libc-types/ucontext_t.h
@@ -0,0 +1,32 @@
+//===-- Definition of type ucontext_t -------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_TYPES_UCONTEXT_T_H
+#define LLVM_LIBC_TYPES_UCONTEXT_T_H
+
+#include "mcontext_t.h"
+#include "sigset_t.h"
+#include "stack_t.h"
+
+#if defined(__x86_64__)
+
+typedef struct alignas(16) ucontext_t {
+ unsigned long uc_flags;
+ struct ucontext_t *uc_link;
+ stack_t uc_stack;
+ mcontext_t uc_mcontext;
+ sigset_t uc_sigmask;
+ struct _libc_fpstate __fpregs_mem;
+ unsigned long long __ssp[4];
+} ucontext_t;
+
+#else
+#error "ucontext_t not available for your target architecture."
+#endif
+
+#endif // LLVM_LIBC_TYPES_UCONTEXT_T_H
diff --git a/libc/include/ucontext.h.def b/libc/include/ucontext.h.def
new file mode 100644
index 0000000000000..37113bcac6776
--- /dev/null
+++ b/libc/include/ucontext.h.def
@@ -0,0 +1,21 @@
+//===-- C standard library header ucontext.h --------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_UCONTEXT_H
+#define LLVM_LIBC_UCONTEXT_H
+
+#include "__llvm-libc-common.h"
+
+#include "llvm-libc-types/mcontext_t.h"
+#include "llvm-libc-types/ucontext_t.h"
+#include "llvm-libc-types/sigset_t.h"
+#include "llvm-libc-types/stack_t.h"
+
+%%public_api()
+
+#endif // LLVM_LIBC_UCONTEXT_H
diff --git a/libc/include/ucontext.yaml b/libc/include/ucontext.yaml
new file mode 100644
index 0000000000000..05ae5e9d38602
--- /dev/null
+++ b/libc/include/ucontext.yaml
@@ -0,0 +1,37 @@
+header: ucontext.h
+standards:
+ - posix
+types:
+ - type_name: mcontext_t
+ - type_name: ucontext_t
+ - type_name: sigset_t
+ - type_name: stack_t
+functions:
+ - name: getcontext
+ standards:
+ - posix
+ return_type: int
+ arguments:
+ - type: ucontext_t *
+ - name: setcontext
+ standards:
+ - posix
+ return_type: int
+ arguments:
+ - type: const ucontext_t *
+ - name: makecontext
+ standards:
+ - posix
+ return_type: void
+ arguments:
+ - type: ucontext_t *
+ - type: void(*)(void)
+ - type: int
+ - type: ...
+ - name: swapcontext
+ standards:
+ - posix
+ return_type: int
+ arguments:
+ - type: ucontext_t *__restrict
+ - type: const ucontext_t *__restrict
diff --git a/libc/src/CMakeLists.txt b/libc/src/CMakeLists.txt
index 8a0acccaed708..3021e65c3edc4 100644
--- a/libc/src/CMakeLists.txt
+++ b/libc/src/CMakeLists.txt
@@ -44,6 +44,7 @@ add_subdirectory(locale)
add_subdirectory(nl_types)
add_subdirectory(search)
add_subdirectory(setjmp)
+add_subdirectory(ucontext)
add_subdirectory(signal)
add_subdirectory(spawn)
add_subdirectory(threads)
diff --git a/libc/src/ucontext/CMakeLists.txt b/libc/src/ucontext/CMakeLists.txt
new file mode 100644
index 0000000000000..b163eac0e339d
--- /dev/null
+++ b/libc/src/ucontext/CMakeLists.txt
@@ -0,0 +1,29 @@
+add_subdirectory(${LIBC_TARGET_ARCHITECTURE})
+
+add_entrypoint_object(
+ getcontext
+ ALIAS
+ DEPENDS
+ .${LIBC_TARGET_ARCHITECTURE}.getcontext
+)
+
+add_entrypoint_object(
+ setcontext
+ ALIAS
+ DEPENDS
+ .${LIBC_TARGET_ARCHITECTURE}.setcontext
+)
+
+add_entrypoint_object(
+ makecontext
+ ALIAS
+ DEPENDS
+ .${LIBC_TARGET_ARCHITECTURE}.makecontext
+)
+
+add_entrypoint_object(
+ swapcontext
+ ALIAS
+ DEPENDS
+ .${LIBC_TARGET_ARCHITECTURE}.swapcontext
+)
diff --git a/libc/src/ucontext/getcontext.h b/libc/src/ucontext/getcontext.h
new file mode 100644
index 0000000000000..b8f1ef46e3829
--- /dev/null
+++ b/libc/src/ucontext/getcontext.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for getcontext ----------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_UCONTEXT_GETCONTEXT_H
+#define LLVM_LIBC_SRC_UCONTEXT_GETCONTEXT_H
+
+#include "src/__support/macros/config.h"
+#include "include/llvm-libc-types/ucontext_t.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int getcontext(ucontext_t *ucp);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_UCONTEXT_GETCONTEXT_H
diff --git a/libc/src/ucontext/makecontext.h b/libc/src/ucontext/makecontext.h
new file mode 100644
index 0000000000000..78e7999831738
--- /dev/null
+++ b/libc/src/ucontext/makecontext.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for makecontext ---------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_UCONTEXT_MAKECONTEXT_H
+#define LLVM_LIBC_SRC_UCONTEXT_MAKECONTEXT_H
+
+#include "src/__support/macros/config.h"
+#include "include/llvm-libc-types/ucontext_t.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+void makecontext(ucontext_t *ucp, void (*func)(void), int argc, ...);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_UCONTEXT_MAKECONTEXT_H
diff --git a/libc/src/ucontext/setcontext.h b/libc/src/ucontext/setcontext.h
new file mode 100644
index 0000000000000..e5fbac6da482f
--- /dev/null
+++ b/libc/src/ucontext/setcontext.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for setcontext ----------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_UCONTEXT_SETCONTEXT_H
+#define LLVM_LIBC_SRC_UCONTEXT_SETCONTEXT_H
+
+#include "src/__support/macros/config.h"
+#include "include/llvm-libc-types/ucontext_t.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int setcontext(const ucontext_t *ucp);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_UCONTEXT_SETCONTEXT_H
diff --git a/libc/src/ucontext/swapcontext.h b/libc/src/ucontext/swapcontext.h
new file mode 100644
index 0000000000000..4412c862a427a
--- /dev/null
+++ b/libc/src/ucontext/swapcontext.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for swapcontext ---------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_UCONTEXT_SWAPCONTEXT_H
+#define LLVM_LIBC_SRC_UCONTEXT_SWAPCONTEXT_H
+
+#include "src/__support/macros/config.h"
+#include "include/llvm-libc-types/ucontext_t.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int swapcontext(ucontext_t *__restrict oucp, const ucontext_t *__restrict ucp);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_UCONTEXT_SWAPCONTEXT_H
diff --git a/libc/src/ucontext/x86_64/CMakeLists.txt b/libc/src/ucontext/x86_64/CMakeLists.txt
new file mode 100644
index 0000000000000..014ef3d095572
--- /dev/null
+++ b/libc/src/ucontext/x86_64/CMakeLists.txt
@@ -0,0 +1,60 @@
+add_entrypoint_object(
+ getcontext
+ SRCS
+ getcontext.cpp
+ HDRS
+ ../getcontext.h
+ COMPILE_OPTIONS
+ -O3
+ -fno-omit-frame-pointer
+ DEPENDS
+ libc.include.llvm-libc-types.ucontext_t
+ libc.src.__support.common
+ libc.src.__support.macros.config
+)
+
+add_entrypoint_object(
+ setcontext
+ SRCS
+ setcontext.cpp
+ HDRS
+ ../setcontext.h
+ COMPILE_OPTIONS
+ -O3
+ -fno-omit-frame-pointer
+ DEPENDS
+ libc.include.llvm-libc-types.ucontext_t
+ libc.src.__support.common
+ libc.src.__support.macros.config
+)
+
+add_entrypoint_object(
+ makecontext
+ SRCS
+ makecontext.cpp
+ HDRS
+ ../makecontext.h
+ COMPILE_OPTIONS
+ -O3
+ -fno-omit-frame-pointer
+ DEPENDS
+ libc.include.llvm-libc-types.ucontext_t
+ libc.src.__support.common
+ libc.src.__support.macros.config
+ libc.src.__support.OSUtil.osutil
+)
+
+add_entrypoint_object(
+ swapcontext
+ SRCS
+ swapcontext.cpp
+ HDRS
+ ../swapcontext.h
+ COMPILE_OPTIONS
+ -O3
+ -fno-omit-frame-pointer
+ DEPENDS
+ libc.include.llvm-libc-types.ucontext_t
+ libc.src.__support.common
+ libc.src.__support.macros.config
+)
diff --git a/libc/src/ucontext/x86_64/getcontext.cpp b/libc/src/ucontext/x86_64/getcontext.cpp
new file mode 100644
index 0000000000000..f499873202a33
--- /dev/null
+++ b/libc/src/ucontext/x86_64/getcontext.cpp
@@ -0,0 +1,94 @@
+//===-- 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 "src/__support/common.h"
+#include "src/__support/macros/config.h"
+
+#include "include/llvm-libc-types/ucontext_t.h"
+#include <stddef.h>
+
+// We define these locally so we don't depend on system headers.
+// These are the standard sysV x86_64 ABI indices for gregset_t.
+
+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.
+[[gnu::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)
+
+ // Reset return value to 0
+ xor %%eax, %%eax
+
+ // TODO: Capture the signal mask using rt_sigprocmask syscall.
+ // Signal support is currently minimal in LLVM-libc.
+
+ retq
+ )"
+ ::
+ [ret_size] "i"(sizeof(void*)),
+ [r8] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_R8])),
+ [r9] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_R9])),
+ [r10] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_R10])),
+ [r11] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_R11])),
+ [r12] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_R12])),
+ [r13] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_R13])),
+ [r14] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_R14])),
+ [r15] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_R15])),
+ [rdi] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RDI])),
+ [rsi] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RSI])),
+ [rbp] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RBP])),
+ [rbx] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RBX])),
+ [rdx] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RDX])),
+ [rax] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RAX])),
+ [rcx] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RCX])),
+ [rsp] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RSP])),
+ [rip] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RIP])),
+ [fpregs_mem] "i"(offsetof(ucontext_t, __fpregs_mem)),
+ [fpregs_ptr] "i"(offsetof(ucontext_t, uc_mcontext.fpregs))
+ : "memory"
+ );
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/ucontext/x86_64/makecontext.cpp b/libc/src/ucontext/x86_64/makecontext.cpp
new file mode 100644
index 0000000000000..8907dab4f1d18
--- /dev/null
+++ b/libc/src/ucontext/x86_64/makecontext.cpp
@@ -0,0 +1,84 @@
+//===-- Implementation of makecontext 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/makecontext.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+
+#include "include/llvm-libc-types/ucontext_t.h"
+#include <stddef.h>
+#include <stdint.h>
+#include <stdarg.h>
+
+#include "src/__support/OSUtil/exit.h"
+#include "src/ucontext/setcontext.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+extern "C" void __makecontext_trampoline_c(ucontext_t *uc_link) {
+ if (uc_link)
+ setcontext(uc_link);
+
+ internal::exit(0);
+}
+
+[[gnu::naked]] void __makecontext_trampoline() {
+ asm(R"(
+ mov %rbx, %rdi
+ call __makecontext_trampoline_c
+ hlt
+ )");
+}
+
+LLVM_LIBC_FUNCTION(void, makecontext, (ucontext_t *ucp, void (*func)(void), int argc, ...)) {
+ if (!ucp || !func)
+ return;
+
+ // System V AMD64 ABI requirements.
+ constexpr uintptr_t STACK_ALIGN_BYTES = 16;
+ constexpr uintptr_t STACK_ALIGN_MASK = ~(STACK_ALIGN_BYTES - 1);
+ constexpr int REGISTER_ARGS_COUNT = 6;
+ constexpr uintptr_t ARG_SIZE = sizeof(greg_t);
+
+ uintptr_t stack_top = reinterpret_cast<uintptr_t>(ucp->uc_stack.ss_sp) + ucp->uc_stack.ss_size;
+ stack_top &= STACK_ALIGN_MASK;
+
+ int stack_args = argc > REGISTER_ARGS_COUNT ? argc - REGISTER_ARGS_COUNT : 0;
+
+ uintptr_t new_rsp = stack_top - stack_args * ARG_SIZE;
+ new_rsp &= STACK_ALIGN_MASK;
+
+ // The System V ABI requires the stack to be 16-byte aligned before the 'call' instruction.
+ // When a function is entered, the return address has been pushed, making the stack misaligned by 8.
+ // We simulate this state by subtracting 8, storing the trampoline address at the top of the stack.
+ new_rsp -= ARG_SIZE;
+
+ greg_t *stack_area = reinterpret_cast<greg_t *>(new_rsp);
+ stack_area[0] = reinterpret_cast<greg_t>(&__makecontext_trampoline);
+
+ va_list ap;
+ va_start(ap, argc);
+ if (argc > 0) ucp->uc_mcontext.gregs[REG_RDI] = va_arg(ap, greg_t);
+ if (argc > 1) ucp->uc_mcontext.gregs[REG_RSI] = va_arg(ap, greg_t);
+ if (argc > 2) ucp->uc_mcontext.gregs[REG_RDX] = va_arg(ap, greg_t);
+ if (argc > 3) ucp->uc_mcontext.gregs[REG_RCX] = va_arg(ap, greg_t);
+ if (argc > 4) ucp->uc_mcontext.gregs[REG_R8] = va_arg(ap, greg_t);
+ if (argc > 5) ucp->uc_mcontext.gregs[REG_R9] = va_arg(ap, greg_t);
+
+ for (int i = 0; i < stack_args; ++i) {
+ stack_area[i + 1] = va_arg(ap, greg_t);
+ }
+
+ va_end(ap);
+
+ ucp->uc_mcontext.gregs[REG_RIP] = reinterpret_cast<greg_t>(func);
+ ucp->uc_mcontext.gregs[REG_RSP] = new_rsp;
+ ucp->uc_mcontext.gregs[REG_RBX] = reinterpret_cast<greg_t>(ucp->uc_link);
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/ucontext/x86_64/setcontext.cpp b/libc/src/ucontext/x86_64/setcontext.cpp
new file mode 100644
index 0000000000000..43aa45ba2e0f7
--- /dev/null
+++ b/libc/src/ucontext/x86_64/setcontext.cpp
@@ -0,0 +1,90 @@
+//===-- 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 "src/__support/common.h"
+#include "src/__support/macros/config.h"
+
+#include "include/llvm-libc-types/ucontext_t.h"
+#include <stddef.h>
+
+namespace LIBC_NAMESPACE_DECL {
+
+[[gnu::naked]]
+LLVM_LIBC_FUNCTION(int, setcontext, (const ucontext_t *ucp)) {
+ asm(R"(
+ // ucp is in rdi
+
+ // TODO: Restore the signal mask using rt_sigprocmask syscall.
+
+ // Restore floating point state
+ fxrstorq %c[fpregs_mem](%%rdi)
+
+ // Store the desired target rip in a temporary safe register
+ // so we can jump there at the end easily.
+ mov %c[rip](%%rdi), %%rcx
+
+ // Restore stack pointer first
+ mov %c[rsp](%%rdi), %%rsp
+
+ // We simulate returning to the instruction pointer by pushing it onto
+ // our new stack so that a simple `ret` at the end can jump to it.
+ pushq %%rcx
+
+ // 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
+
+ // We restore RSI and others
+ mov %c[rsi](%%rdi), %%rsi
+ mov %c[rbp](%%rdi), %%rbp
+ mov %c[rbx](%%rdi), %%rbx
+ mov %c[rdx](%%rdi), %%rdx
+ mov %c[rax](%%rdi), %%rax
+
+ // Notice we are relying on RDI currently to access ucontext_t,
+ // so we restore it very last. RDI holds the address of the ucontext.
+ // Wait, we need to restore RCX, but we used it for RIP earlier -- wait,
+ // we pushed it, so we can restore RCX properly too.
+ mov %c[rcx](%%rdi), %%rcx
+ mov %c[rdi](%%rdi), %%rdi
+
+ // Now we have the correct RSP, the RIP is on the top of the stack.
+ // General registers are restored.
+ // Jump and continue!
+ retq
+ )"
+ ::
+ [r8] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_R8])),
+ [r9] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_R9])),
+ [r10] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_R10])),
+ [r11] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_R11])),
+ [r12] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_R12])),
+ [r13] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_R13])),
+ [r14] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_R14])),
+ [r15] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_R15])),
+ [rdi] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RDI])),
+ [rsi] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RSI])),
+ [rbp] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RBP])),
+ [rbx] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RBX])),
+ [rdx] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RDX])),
+ [rax] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RAX])),
+ [rcx] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RCX])),
+ [rsp] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RSP])),
+ [rip] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RIP])),
+ [fpregs_mem] "i"(offsetof(ucontext_t, __fpregs_mem))
+ );
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/ucontext/x86_64/swapcontext.cpp b/libc/src/ucontext/x86_64/swapcontext.cpp
new file mode 100644
index 0000000000000..ba50df6694942
--- /dev/null
+++ b/libc/src/ucontext/x86_64/swapcontext.cpp
@@ -0,0 +1,107 @@
+//===-- Implementation of swapcontext 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/swapcontext.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+
+#include "include/llvm-libc-types/ucontext_t.h"
+#include <stddef.h>
+namespace LIBC_NAMESPACE_DECL {
+
+[[gnu::naked]]
+LLVM_LIBC_FUNCTION(int, swapcontext, (ucontext_t *__restrict oucp, const ucontext_t *__restrict ucp)) {
+ asm(R"(
+ // oucp is in rdi, ucp is in rsi
+
+ // SAVE TO oucp
+ 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)
+ movq $0, %c[rax](%%rdi)
+ mov %%rcx, %c[rcx](%%rdi)
+
+ // 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 FP 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)
+
+ // TODO: Capture oucp signal mask and restore ucp signal mask
+
+ // RESTORE FROM ucp (rsi)
+ // Restore floating point state
+ fxrstorq %c[fpregs_mem](%%rsi)
+
+ // The pushq instruction restores the return address onto the stack.
+ mov %c[rip](%%rsi), %%rcx
+ mov %c[rsp](%%rsi), %%rsp
+ pushq %%rcx
+
+ mov %c[r8](%%rsi), %%r8
+ mov %c[r9](%%rsi), %%r9
+ mov %c[r10](%%rsi), %%r10
+ mov %c[r11](%%rsi), %%r11
+ mov %c[r12](%%rsi), %%r12
+ mov %c[r13](%%rsi), %%r13
+ mov %c[r14](%%rsi), %%r14
+ mov %c[r15](%%rsi), %%r15
+
+ mov %c[rbp](%%rsi), %%rbp
+ mov %c[rbx](%%rsi), %%rbx
+ mov %c[rdx](%%rsi), %%rdx
+ mov %c[rax](%%rsi), %%rax
+ mov %c[rcx](%%rsi), %%rcx
+
+ mov %c[rdi](%%rsi), %%rdi
+ mov %c[rsi](%%rsi), %%rsi
+
+ retq
+ )"
+ ::
+ [ret_size] "i"(sizeof(void*)),
+ [r8] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_R8])),
+ [r9] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_R9])),
+ [r10] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_R10])),
+ [r11] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_R11])),
+ [r12] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_R12])),
+ [r13] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_R13])),
+ [r14] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_R14])),
+ [r15] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_R15])),
+ [rdi] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RDI])),
+ [rsi] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RSI])),
+ [rbp] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RBP])),
+ [rbx] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RBX])),
+ [rdx] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RDX])),
+ [rax] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RAX])),
+ [rcx] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RCX])),
+ [rsp] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RSP])),
+ [rip] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RIP])),
+ [fpregs_mem] "i"(offsetof(ucontext_t, __fpregs_mem)),
+ [fpregs_ptr] "i"(offsetof(ucontext_t, uc_mcontext.fpregs))
+ : "memory"
+ );
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/test/src/CMakeLists.txt b/libc/test/src/CMakeLists.txt
index 5e61c739c066a..df0e2e9c915b3 100644
--- a/libc/test/src/CMakeLists.txt
+++ b/libc/test/src/CMakeLists.txt
@@ -65,6 +65,7 @@ add_subdirectory(fenv)
add_subdirectory(math)
add_subdirectory(search)
add_subdirectory(setjmp)
+add_subdirectory(ucontext)
add_subdirectory(stdbit)
add_subdirectory(stdfix)
add_subdirectory(stdio)
diff --git a/libc/test/src/ucontext/CMakeLists.txt b/libc/test/src/ucontext/CMakeLists.txt
new file mode 100644
index 0000000000000..8846267855e86
--- /dev/null
+++ b/libc/test/src/ucontext/CMakeLists.txt
@@ -0,0 +1,14 @@
+add_custom_target(libc_ucontext_unittests)
+
+add_libc_unittest(
+ ucontext_test
+ SUITE
+ libc_ucontext_unittests
+ SRCS
+ ucontext_test.cpp
+ DEPENDS
+ libc.src.ucontext.getcontext
+ libc.src.ucontext.setcontext
+ libc.src.ucontext.makecontext
+ libc.src.ucontext.swapcontext
+)
diff --git a/libc/test/src/ucontext/ucontext_test.cpp b/libc/test/src/ucontext/ucontext_test.cpp
new file mode 100644
index 0000000000000..c74f0a8c3a1d0
--- /dev/null
+++ b/libc/test/src/ucontext/ucontext_test.cpp
@@ -0,0 +1,109 @@
+//===-- Unittests for ucontext routines -----------------------------------===//
+//
+// 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 "src/ucontext/setcontext.h"
+#include "src/ucontext/makecontext.h"
+#include "src/ucontext/swapcontext.h"
+
+#include "test/UnitTest/Test.h"
+
+#include <ucontext.h>
+
+volatile int jumped = 0;
+TEST(LlvmLibcUcontextTest, BasicStubTest) {
+ ucontext_t ctx;
+ ASSERT_EQ(LIBC_NAMESPACE::getcontext(&ctx), 0);
+ if (!jumped) {
+ jumped = 1;
+ LIBC_NAMESPACE::setcontext(&ctx);
+ ASSERT_TRUE(false && "setcontext should not return on success");
+ }
+}
+
+ucontext_t old_ctx, new_ctx;
+volatile int swap_called = 0;
+
+void swap_func() {
+ swap_called = 1;
+ LIBC_NAMESPACE::setcontext(&old_ctx);
+}
+
+TEST(LlvmLibcUcontextTest, SwapcontextTest) {
+ LIBC_NAMESPACE::getcontext(&new_ctx);
+ constexpr size_t STACK_SIZE = 8192;
+ char stack[STACK_SIZE];
+ new_ctx.uc_stack.ss_sp = stack;
+ new_ctx.uc_stack.ss_size = sizeof(stack);
+ LIBC_NAMESPACE::makecontext(&new_ctx, swap_func, 0);
+
+ LIBC_NAMESPACE::swapcontext(&old_ctx, &new_ctx);
+
+ ASSERT_EQ(swap_called, 1);
+}
+
+ucontext_t old_ctx_args, new_ctx_args;
+volatile int makecontext_args_called = 0;
+volatile int arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8;
+
+void args_func(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8) {
+ makecontext_args_called = 1;
+ arg1 = a1; arg2 = a2; arg3 = a3; arg4 = a4;
+ arg5 = a5; arg6 = a6; arg7 = a7; arg8 = a8;
+ LIBC_NAMESPACE::setcontext(&old_ctx_args);
+}
+
+TEST(LlvmLibcUcontextTest, MakecontextArgsTest) {
+ LIBC_NAMESPACE::getcontext(&new_ctx_args);
+ constexpr size_t STACK_SIZE = 8192;
+ char stack[STACK_SIZE];
+ new_ctx_args.uc_stack.ss_sp = stack;
+ new_ctx_args.uc_stack.ss_size = sizeof(stack);
+
+ // Cast function pointer to void(*)(void) as required by makecontext
+ using func_t = void (*)(void);
+ auto func = reinterpret_cast<func_t>(args_func);
+
+ LIBC_NAMESPACE::makecontext(&new_ctx_args, func, 8, 11, 22, 33, 44, 55, 66, 77, 88);
+
+ LIBC_NAMESPACE::swapcontext(&old_ctx_args, &new_ctx_args);
+
+ ASSERT_EQ(makecontext_args_called, 1);
+ ASSERT_EQ(arg1, 11);
+ ASSERT_EQ(arg2, 22);
+ ASSERT_EQ(arg3, 33);
+ ASSERT_EQ(arg4, 44);
+ ASSERT_EQ(arg5, 55);
+ ASSERT_EQ(arg6, 66);
+ ASSERT_EQ(arg8, 88);
+}
+
+ucontext_t old_ctx_return, new_ctx_return;
+volatile int makecontext_return_called = 0;
+
+void return_func() {
+ makecontext_return_called = 1;
+}
+
+TEST(LlvmLibcUcontextTest, MakecontextReturnTest) {
+ LIBC_NAMESPACE::getcontext(&new_ctx_return);
+ constexpr size_t STACK_SIZE = 8192;
+ char stack[STACK_SIZE];
+ new_ctx_return.uc_stack.ss_sp = stack;
+ new_ctx_return.uc_stack.ss_size = sizeof(stack);
+ new_ctx_return.uc_link = &old_ctx_return;
+
+ using func_t = void (*)(void);
+ auto func = reinterpret_cast<func_t>(return_func);
+
+ LIBC_NAMESPACE::makecontext(&new_ctx_return, func, 0);
+
+ LIBC_NAMESPACE::swapcontext(&old_ctx_return, &new_ctx_return);
+
+ ASSERT_EQ(makecontext_return_called, 1);
+}
>From d1b728247a7a595eaac068f925e8db144a6f0231 Mon Sep 17 00:00:00 2001
From: Jeff Bailey <jbailey at raspberryginger.com>
Date: Mon, 23 Mar 2026 11:53:17 +0000
Subject: [PATCH 2/3] [libc] Implement signal mask support for x86_64 ucontext
(#187712)
Implemented the following changes for the x86-64 ucontext implementation:
* Adjusted mcontext_t.h register indices to match standard ABI (RSP=11, etc.).
* Implemented signal mask capture in getcontext via rt_sigprocmask syscall.
* Implemented signal mask restoration in setcontext and swapcontext.
* Fixed register restoration sequence in setcontext and swapcontext to prevent
clobbering the base pointer before all registers are loaded.
* Added architecture-conditional build logic for ucontext entrypoints,
unittests, and the ucontext.h header to prevent build failures on
non-x86 platforms.
* Added SignalMaskTest to ucontext_test.cpp to verify mask preservation.
---
libc/include/CMakeLists.txt | 24 ++++-----
libc/include/llvm-libc-types/mcontext_t.h | 6 +--
libc/src/ucontext/CMakeLists.txt | 60 +++++++++++++----------
libc/src/ucontext/x86_64/getcontext.cpp | 24 ++++++---
libc/src/ucontext/x86_64/setcontext.cpp | 51 ++++++++++---------
libc/src/ucontext/x86_64/swapcontext.cpp | 58 +++++++++++++++-------
libc/test/src/ucontext/CMakeLists.txt | 26 +++++-----
libc/test/src/ucontext/ucontext_test.cpp | 36 +++++++++++++-
8 files changed, 182 insertions(+), 103 deletions(-)
diff --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt
index ba669a9db84e5..4e6be222a600d 100644
--- a/libc/include/CMakeLists.txt
+++ b/libc/include/CMakeLists.txt
@@ -432,17 +432,19 @@ add_header_macro(
.llvm_libc_common_h
)
-add_header_macro(
- ucontext
- ../libc/include/ucontext.yaml
- ucontext.h
- DEPENDS
- .llvm_libc_common_h
- .llvm-libc-types.mcontext_t
- .llvm-libc-types.ucontext_t
- .llvm-libc-types.sigset_t
- .llvm-libc-types.stack_t
-)
+if(LIBC_TARGET_ARCHITECTURE STREQUAL "x86_64")
+ add_header_macro(
+ ucontext
+ ../libc/include/ucontext.yaml
+ ucontext.h
+ DEPENDS
+ .llvm_libc_common_h
+ .llvm-libc-types.mcontext_t
+ .llvm-libc-types.ucontext_t
+ .llvm-libc-types.sigset_t
+ .llvm-libc-types.stack_t
+ )
+endif()
add_header_macro(
sched
diff --git a/libc/include/llvm-libc-types/mcontext_t.h b/libc/include/llvm-libc-types/mcontext_t.h
index c45040fc66243..52b5cfc97ef3a 100644
--- a/libc/include/llvm-libc-types/mcontext_t.h
+++ b/libc/include/llvm-libc-types/mcontext_t.h
@@ -25,14 +25,14 @@ enum {
REG_RDI,
REG_RSI,
REG_RBP,
+ REG_RSP,
REG_RBX,
REG_RDX,
- REG_RAX,
REG_RCX,
- REG_RSP,
+ REG_RAX,
REG_RIP,
- REG_EFL,
REG_CSGSFS,
+ REG_EFL,
REG_ERR,
REG_TRAPNO,
REG_OLDMASK,
diff --git a/libc/src/ucontext/CMakeLists.txt b/libc/src/ucontext/CMakeLists.txt
index b163eac0e339d..67424c2e5d1f6 100644
--- a/libc/src/ucontext/CMakeLists.txt
+++ b/libc/src/ucontext/CMakeLists.txt
@@ -1,29 +1,39 @@
-add_subdirectory(${LIBC_TARGET_ARCHITECTURE})
+if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_ARCHITECTURE})
+ add_subdirectory(${LIBC_TARGET_ARCHITECTURE})
+endif()
-add_entrypoint_object(
- getcontext
- ALIAS
- DEPENDS
- .${LIBC_TARGET_ARCHITECTURE}.getcontext
-)
+if(TARGET libc.src.ucontext.${LIBC_TARGET_ARCHITECTURE}.getcontext)
+ add_entrypoint_object(
+ getcontext
+ ALIAS
+ DEPENDS
+ .${LIBC_TARGET_ARCHITECTURE}.getcontext
+ )
+endif()
-add_entrypoint_object(
- setcontext
- ALIAS
- DEPENDS
- .${LIBC_TARGET_ARCHITECTURE}.setcontext
-)
+if(TARGET libc.src.ucontext.${LIBC_TARGET_ARCHITECTURE}.setcontext)
+ add_entrypoint_object(
+ setcontext
+ ALIAS
+ DEPENDS
+ .${LIBC_TARGET_ARCHITECTURE}.setcontext
+ )
+endif()
-add_entrypoint_object(
- makecontext
- ALIAS
- DEPENDS
- .${LIBC_TARGET_ARCHITECTURE}.makecontext
-)
+if(TARGET libc.src.ucontext.${LIBC_TARGET_ARCHITECTURE}.makecontext)
+ add_entrypoint_object(
+ makecontext
+ ALIAS
+ DEPENDS
+ .${LIBC_TARGET_ARCHITECTURE}.makecontext
+ )
+endif()
-add_entrypoint_object(
- swapcontext
- ALIAS
- DEPENDS
- .${LIBC_TARGET_ARCHITECTURE}.swapcontext
-)
+if(TARGET libc.src.ucontext.${LIBC_TARGET_ARCHITECTURE}.swapcontext)
+ add_entrypoint_object(
+ swapcontext
+ ALIAS
+ DEPENDS
+ .${LIBC_TARGET_ARCHITECTURE}.swapcontext
+ )
+endif()
diff --git a/libc/src/ucontext/x86_64/getcontext.cpp b/libc/src/ucontext/x86_64/getcontext.cpp
index f499873202a33..22890fbb9c0b4 100644
--- a/libc/src/ucontext/x86_64/getcontext.cpp
+++ b/libc/src/ucontext/x86_64/getcontext.cpp
@@ -8,10 +8,12 @@
#include "src/ucontext/getcontext.h"
#include "src/__support/common.h"
+#include "include/llvm-libc-types/ucontext_t.h"
#include "src/__support/macros/config.h"
-#include "include/llvm-libc-types/ucontext_t.h"
+#include <signal.h>
#include <stddef.h>
+#include <sys/syscall.h>
// We define these locally so we don't depend on system headers.
// These are the standard sysV x86_64 ABI indices for gregset_t.
@@ -58,16 +60,23 @@ LLVM_LIBC_FUNCTION(int, getcontext, (ucontext_t *ucp)) {
lea %c[fpregs_mem](%%rdi), %%rax
mov %%rax, %c[fpregs_ptr](%%rdi)
- // Reset return value to 0
- xor %%eax, %%eax
+ // Capture the signal mask using rt_sigprocmask syscall.
+ // rt_sigprocmask(SIG_BLOCK, NULL, &ucp->uc_sigmask, sizeof(sigset_t))
+ leaq %c[sigmask](%%rdi), %%rdx
+ xorq %%rsi, %%rsi
+ movq $0, %%rdi // SIG_BLOCK
+ movq $%c[sigset_size], %%r10
+ movq $SYS_rt_sigprocmask, %%rax
+ syscall
- // TODO: Capture the signal mask using rt_sigprocmask syscall.
- // Signal support is currently minimal in LLVM-libc.
+ // getcontext should return 0 on success
+ xor %%eax, %%eax
retq
)"
::
[ret_size] "i"(sizeof(void*)),
+ [sigset_size] "i"(sizeof(sigset_t)),
[r8] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_R8])),
[r9] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_R9])),
[r10] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_R10])),
@@ -86,8 +95,9 @@ LLVM_LIBC_FUNCTION(int, getcontext, (ucontext_t *ucp)) {
[rsp] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RSP])),
[rip] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RIP])),
[fpregs_mem] "i"(offsetof(ucontext_t, __fpregs_mem)),
- [fpregs_ptr] "i"(offsetof(ucontext_t, uc_mcontext.fpregs))
- : "memory"
+ [fpregs_ptr] "i"(offsetof(ucontext_t, uc_mcontext.fpregs)),
+ [sigmask] "i"(offsetof(ucontext_t, uc_sigmask))
+ : "memory", "rcx", "r11"
);
}
diff --git a/libc/src/ucontext/x86_64/setcontext.cpp b/libc/src/ucontext/x86_64/setcontext.cpp
index 43aa45ba2e0f7..d7d3b400ccfd4 100644
--- a/libc/src/ucontext/x86_64/setcontext.cpp
+++ b/libc/src/ucontext/x86_64/setcontext.cpp
@@ -8,10 +8,12 @@
#include "src/ucontext/setcontext.h"
#include "src/__support/common.h"
+#include "include/llvm-libc-types/ucontext_t.h"
#include "src/__support/macros/config.h"
-#include "include/llvm-libc-types/ucontext_t.h"
+#include <signal.h>
#include <stddef.h>
+#include <sys/syscall.h>
namespace LIBC_NAMESPACE_DECL {
@@ -20,22 +22,20 @@ LLVM_LIBC_FUNCTION(int, setcontext, (const ucontext_t *ucp)) {
asm(R"(
// ucp is in rdi
- // TODO: Restore the signal mask using rt_sigprocmask syscall.
+ // Restore the signal mask using rt_sigprocmask syscall.
+ // rt_sigprocmask(SIG_SETMASK, &ucp->uc_sigmask, NULL, sizeof(sigset_t))
+ 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 $2, %%rdi // how = SIG_SETMASK
+ movq $SYS_rt_sigprocmask, %%rax
+ syscall
+ popq %%rdi // Restore ucp
// Restore floating point state
fxrstorq %c[fpregs_mem](%%rdi)
- // Store the desired target rip in a temporary safe register
- // so we can jump there at the end easily.
- mov %c[rip](%%rdi), %%rcx
-
- // Restore stack pointer first
- mov %c[rsp](%%rdi), %%rsp
-
- // We simulate returning to the instruction pointer by pushing it onto
- // our new stack so that a simple `ret` at the end can jump to it.
- pushq %%rcx
-
// Restore other general purpose registers
mov %c[r8](%%rdi), %%r8
mov %c[r9](%%rdi), %%r9
@@ -45,27 +45,24 @@ LLVM_LIBC_FUNCTION(int, setcontext, (const ucontext_t *ucp)) {
mov %c[r13](%%rdi), %%r13
mov %c[r14](%%rdi), %%r14
mov %c[r15](%%rdi), %%r15
-
- // We restore RSI and others
- mov %c[rsi](%%rdi), %%rsi
mov %c[rbp](%%rdi), %%rbp
mov %c[rbx](%%rdi), %%rbx
mov %c[rdx](%%rdi), %%rdx
mov %c[rax](%%rdi), %%rax
-
- // Notice we are relying on RDI currently to access ucontext_t,
- // so we restore it very last. RDI holds the address of the ucontext.
- // Wait, we need to restore RCX, but we used it for RIP earlier -- wait,
- // we pushed it, so we can restore RCX properly too.
mov %c[rcx](%%rdi), %%rcx
+
+ // Restore stack pointer and instruction pointer
+ mov %c[rsp](%%rdi), %%rsp
+ mov %c[rip](%%rdi), %%r11 // Use r11 as temp for rip
+
+ // Restore RSI and RDI last
+ mov %c[rsi](%%rdi), %%rsi
mov %c[rdi](%%rdi), %%rdi
- // Now we have the correct RSP, the RIP is on the top of the stack.
- // General registers are restored.
- // Jump and continue!
- retq
+ jmp %%r11 // Jump to the saved instruction pointer
)"
::
+ [sigset_size] "i"(sizeof(sigset_t)),
[r8] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_R8])),
[r9] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_R9])),
[r10] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_R10])),
@@ -83,7 +80,9 @@ LLVM_LIBC_FUNCTION(int, setcontext, (const ucontext_t *ucp)) {
[rcx] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RCX])),
[rsp] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RSP])),
[rip] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RIP])),
- [fpregs_mem] "i"(offsetof(ucontext_t, __fpregs_mem))
+ [fpregs_mem] "i"(offsetof(ucontext_t, __fpregs_mem)),
+ [sigmask] "i"(offsetof(ucontext_t, uc_sigmask))
+ : "memory", "rcx", "r11"
);
}
diff --git a/libc/src/ucontext/x86_64/swapcontext.cpp b/libc/src/ucontext/x86_64/swapcontext.cpp
index ba50df6694942..989f93679afd2 100644
--- a/libc/src/ucontext/x86_64/swapcontext.cpp
+++ b/libc/src/ucontext/x86_64/swapcontext.cpp
@@ -8,18 +8,22 @@
#include "src/ucontext/swapcontext.h"
#include "src/__support/common.h"
+#include "include/llvm-libc-types/ucontext_t.h"
#include "src/__support/macros/config.h"
-#include "include/llvm-libc-types/ucontext_t.h"
+#include <signal.h>
#include <stddef.h>
+#include <sys/syscall.h>
+
namespace LIBC_NAMESPACE_DECL {
[[gnu::naked]]
-LLVM_LIBC_FUNCTION(int, swapcontext, (ucontext_t *__restrict oucp, const ucontext_t *__restrict ucp)) {
+LLVM_LIBC_FUNCTION(int, swapcontext, (ucontext_t *oucp, const ucontext_t *ucp)) {
asm(R"(
// oucp is in rdi, ucp is in rsi
-
- // SAVE TO oucp
+
+ // Save current context into oucp
+ // Save general purpose registers
mov %%r8, %c[r8](%%rdi)
mov %%r9, %c[r9](%%rdi)
mov %%r10, %c[r10](%%rdi)
@@ -28,37 +32,49 @@ LLVM_LIBC_FUNCTION(int, swapcontext, (ucontext_t *__restrict oucp, const ucontex
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 %%rdi, %c[rdi](%%rdi) // oucp itself
+ mov %%rsi, %c[rsi](%%rdi) // ucp
mov %%rbp, %c[rbp](%%rdi)
mov %%rbx, %c[rbx](%%rdi)
mov %%rdx, %c[rdx](%%rdi)
+ // setcontext 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 FP state
+ // 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)
- // TODO: Capture oucp signal mask and restore ucp signal mask
+ // Capture oucp signal mask and restore ucp signal mask atomically.
+ // rt_sigprocmask(SIG_SETMASK, &ucp->uc_sigmask, &oucp->uc_sigmask, sizeof(sigset_t))
+ // oucp is in rdi, ucp is in rsi
+ pushq %%rdi // Save oucp
+ pushq %%rsi // Save ucp
+ leaq %c[sigmask](%%rdi), %%rdx // oldset = &oucp->uc_sigmask
+ leaq %c[sigmask](%%rsi), %%rsi // set = &ucp->uc_sigmask
+ movq $%c[sigset_size], %%r10 // sigsetsize = sizeof(sigset_t)
+ movq $2, %%rdi // how = SIG_SETMASK
+ movq $SYS_rt_sigprocmask, %%rax
+ syscall
+ popq %%rsi // Restore ucp (new context)
+ popq %%rdi // Restore oucp (old context - not needed but for clean stack)
- // RESTORE FROM ucp (rsi)
+ // Restore context from ucp (now in rsi)
// Restore floating point state
fxrstorq %c[fpregs_mem](%%rsi)
- // The pushq instruction restores the return address onto the stack.
- mov %c[rip](%%rsi), %%rcx
- mov %c[rsp](%%rsi), %%rsp
- pushq %%rcx
-
+ // Restore general purpose registers EXECPT rdi, rsi, rsp, rip
mov %c[r8](%%rsi), %%r8
mov %c[r9](%%rsi), %%r9
mov %c[r10](%%rsi), %%r10
@@ -67,20 +83,25 @@ LLVM_LIBC_FUNCTION(int, swapcontext, (ucontext_t *__restrict oucp, const ucontex
mov %c[r13](%%rsi), %%r13
mov %c[r14](%%rsi), %%r14
mov %c[r15](%%rsi), %%r15
-
mov %c[rbp](%%rsi), %%rbp
mov %c[rbx](%%rsi), %%rbx
mov %c[rdx](%%rsi), %%rdx
mov %c[rax](%%rsi), %%rax
mov %c[rcx](%%rsi), %%rcx
+ // Restore stack pointer and instruction pointer
+ mov %c[rsp](%%rsi), %%rsp
+ mov %c[rip](%%rsi), %%r11 // Use r11 as temp for rip
+
+ // Restore RSI and RDI last
mov %c[rdi](%%rsi), %%rdi
mov %c[rsi](%%rsi), %%rsi
- retq
+ jmp %%r11 // Jump to the saved instruction pointer
)"
::
[ret_size] "i"(sizeof(void*)),
+ [sigset_size] "i"(sizeof(sigset_t)),
[r8] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_R8])),
[r9] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_R9])),
[r10] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_R10])),
@@ -99,8 +120,9 @@ LLVM_LIBC_FUNCTION(int, swapcontext, (ucontext_t *__restrict oucp, const ucontex
[rsp] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RSP])),
[rip] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RIP])),
[fpregs_mem] "i"(offsetof(ucontext_t, __fpregs_mem)),
- [fpregs_ptr] "i"(offsetof(ucontext_t, uc_mcontext.fpregs))
- : "memory"
+ [fpregs_ptr] "i"(offsetof(ucontext_t, uc_mcontext.fpregs)),
+ [sigmask] "i"(offsetof(ucontext_t, uc_sigmask))
+ : "memory", "rcx", "r11"
);
}
diff --git a/libc/test/src/ucontext/CMakeLists.txt b/libc/test/src/ucontext/CMakeLists.txt
index 8846267855e86..a39638f299120 100644
--- a/libc/test/src/ucontext/CMakeLists.txt
+++ b/libc/test/src/ucontext/CMakeLists.txt
@@ -1,14 +1,16 @@
add_custom_target(libc_ucontext_unittests)
-add_libc_unittest(
- ucontext_test
- SUITE
- libc_ucontext_unittests
- SRCS
- ucontext_test.cpp
- DEPENDS
- libc.src.ucontext.getcontext
- libc.src.ucontext.setcontext
- libc.src.ucontext.makecontext
- libc.src.ucontext.swapcontext
-)
+if(TARGET libc.src.ucontext.getcontext)
+ add_libc_unittest(
+ ucontext_test
+ SUITE
+ libc_ucontext_unittests
+ SRCS
+ ucontext_test.cpp
+ DEPENDS
+ libc.src.ucontext.getcontext
+ libc.src.ucontext.setcontext
+ libc.src.ucontext.makecontext
+ libc.src.ucontext.swapcontext
+ )
+endif()
diff --git a/libc/test/src/ucontext/ucontext_test.cpp b/libc/test/src/ucontext/ucontext_test.cpp
index c74f0a8c3a1d0..488be9a35f931 100644
--- a/libc/test/src/ucontext/ucontext_test.cpp
+++ b/libc/test/src/ucontext/ucontext_test.cpp
@@ -12,7 +12,7 @@
#include "src/ucontext/swapcontext.h"
#include "test/UnitTest/Test.h"
-
+#include <signal.h>
#include <ucontext.h>
volatile int jumped = 0;
@@ -107,3 +107,37 @@ TEST(LlvmLibcUcontextTest, MakecontextReturnTest) {
ASSERT_EQ(makecontext_return_called, 1);
}
+
+TEST(LlvmLibcUcontextTest, SignalMaskTest) {
+ sigset_t set, old_set;
+ sigemptyset(&set);
+ sigaddset(&set, SIGUSR1);
+
+ // Set mask to [SIGUSR1] using system sigprocmask
+ sigprocmask(SIG_SETMASK, &set, &old_set);
+
+ ucontext_t ctx;
+ ASSERT_EQ(LIBC_NAMESPACE::getcontext(&ctx), 0);
+
+ // Change current mask to [SIGUSR2]
+ sigemptyset(&set);
+ sigaddset(&set, SIGUSR2);
+ sigprocmask(SIG_SETMASK, &set, NULL);
+
+ // Restore context, which should restore mask to [SIGUSR1]
+ static volatile int done = 0;
+ if (!done) {
+ done = 1;
+ LIBC_NAMESPACE::setcontext(&ctx);
+ }
+
+ // Check current mask
+ sigset_t current;
+ sigprocmask(SIG_BLOCK, NULL, ¤t);
+
+ // Restore original mask for clean state
+ sigprocmask(SIG_SETMASK, &old_set, NULL);
+
+ ASSERT_TRUE(sigismember(¤t, SIGUSR1));
+ ASSERT_FALSE(sigismember(¤t, SIGUSR2));
+}
>From cf98b35efb7d712e24c6a08bc2e994eab29cfb38 Mon Sep 17 00:00:00 2001
From: Jeff Bailey <jbailey at raspberryginger.com>
Date: Mon, 23 Mar 2026 13:46:06 +0000
Subject: [PATCH 3/3] Address code review findings for ucontext implementation
TAG=agy
---
libc/include/llvm-libc-types/mcontext_t.h | 46 ++++++-------
libc/include/llvm-libc-types/ucontext_t.h | 2 +-
libc/src/ucontext/x86_64/CMakeLists.txt | 9 +++
libc/src/ucontext/x86_64/getcontext.cpp | 52 +++++++--------
libc/src/ucontext/x86_64/makecontext.cpp | 22 +++---
libc/src/ucontext/x86_64/setcontext.cpp | 46 ++++++-------
libc/src/ucontext/x86_64/swapcontext.cpp | 48 +++++++-------
libc/test/src/ucontext/CMakeLists.txt | 3 +
libc/test/src/ucontext/ucontext_test.cpp | 81 ++++++++++++++---------
9 files changed, 170 insertions(+), 139 deletions(-)
diff --git a/libc/include/llvm-libc-types/mcontext_t.h b/libc/include/llvm-libc-types/mcontext_t.h
index 52b5cfc97ef3a..41549805d4189 100644
--- a/libc/include/llvm-libc-types/mcontext_t.h
+++ b/libc/include/llvm-libc-types/mcontext_t.h
@@ -14,29 +14,29 @@ typedef long long int greg_t;
typedef greg_t gregset_t[23];
enum {
- REG_R8 = 0,
- REG_R9,
- REG_R10,
- REG_R11,
- REG_R12,
- REG_R13,
- REG_R14,
- REG_R15,
- REG_RDI,
- REG_RSI,
- REG_RBP,
- REG_RSP,
- REG_RBX,
- REG_RDX,
- REG_RCX,
- REG_RAX,
- REG_RIP,
- REG_CSGSFS,
- REG_EFL,
- REG_ERR,
- REG_TRAPNO,
- REG_OLDMASK,
- REG_CR2
+ __LIBC_REG_R8 = 0,
+ __LIBC_REG_R9,
+ __LIBC_REG_R10,
+ __LIBC_REG_R11,
+ __LIBC_REG_R12,
+ __LIBC_REG_R13,
+ __LIBC_REG_R14,
+ __LIBC_REG_R15,
+ __LIBC_REG_RDI,
+ __LIBC_REG_RSI,
+ __LIBC_REG_RBP,
+ __LIBC_REG_RSP,
+ __LIBC_REG_RBX,
+ __LIBC_REG_RDX,
+ __LIBC_REG_RCX,
+ __LIBC_REG_RAX,
+ __LIBC_REG_RIP,
+ __LIBC_REG_CSGSFS,
+ __LIBC_REG_EFL,
+ __LIBC_REG_ERR,
+ __LIBC_REG_TRAPNO,
+ __LIBC_REG_OLDMASK,
+ __LIBC_REG_CR2
};
struct _libc_fpxreg {
diff --git a/libc/include/llvm-libc-types/ucontext_t.h b/libc/include/llvm-libc-types/ucontext_t.h
index 068c257bc470e..3b9ff5e239427 100644
--- a/libc/include/llvm-libc-types/ucontext_t.h
+++ b/libc/include/llvm-libc-types/ucontext_t.h
@@ -21,7 +21,7 @@ typedef struct alignas(16) ucontext_t {
stack_t uc_stack;
mcontext_t uc_mcontext;
sigset_t uc_sigmask;
- struct _libc_fpstate __fpregs_mem;
+ alignas(16) long int __fpregs_mem[64];
unsigned long long __ssp[4];
} ucontext_t;
diff --git a/libc/src/ucontext/x86_64/CMakeLists.txt b/libc/src/ucontext/x86_64/CMakeLists.txt
index 014ef3d095572..744c6c24239f7 100644
--- a/libc/src/ucontext/x86_64/CMakeLists.txt
+++ b/libc/src/ucontext/x86_64/CMakeLists.txt
@@ -9,8 +9,10 @@ add_entrypoint_object(
-fno-omit-frame-pointer
DEPENDS
libc.include.llvm-libc-types.ucontext_t
+ libc.include.sys_syscall
libc.src.__support.common
libc.src.__support.macros.config
+ libc.hdr.types.size_t
)
add_entrypoint_object(
@@ -24,8 +26,10 @@ add_entrypoint_object(
-fno-omit-frame-pointer
DEPENDS
libc.include.llvm-libc-types.ucontext_t
+ libc.include.sys_syscall
libc.src.__support.common
libc.src.__support.macros.config
+ libc.hdr.types.size_t
)
add_entrypoint_object(
@@ -42,6 +46,9 @@ add_entrypoint_object(
libc.src.__support.common
libc.src.__support.macros.config
libc.src.__support.OSUtil.osutil
+ libc.src.ucontext.setcontext
+ libc.hdr.types.size_t
+ libc.hdr.stdint_proxy
)
add_entrypoint_object(
@@ -55,6 +62,8 @@ add_entrypoint_object(
-fno-omit-frame-pointer
DEPENDS
libc.include.llvm-libc-types.ucontext_t
+ libc.include.sys_syscall
libc.src.__support.common
libc.src.__support.macros.config
+ libc.hdr.types.size_t
)
diff --git a/libc/src/ucontext/x86_64/getcontext.cpp b/libc/src/ucontext/x86_64/getcontext.cpp
index 22890fbb9c0b4..1cec2055fa7fd 100644
--- a/libc/src/ucontext/x86_64/getcontext.cpp
+++ b/libc/src/ucontext/x86_64/getcontext.cpp
@@ -11,8 +11,7 @@
#include "include/llvm-libc-types/ucontext_t.h"
#include "src/__support/macros/config.h"
-#include <signal.h>
-#include <stddef.h>
+#include "hdr/types/size_t.h"
#include <sys/syscall.h>
// We define these locally so we don't depend on system headers.
@@ -62,11 +61,11 @@ LLVM_LIBC_FUNCTION(int, getcontext, (ucontext_t *ucp)) {
// Capture the signal mask using rt_sigprocmask syscall.
// rt_sigprocmask(SIG_BLOCK, NULL, &ucp->uc_sigmask, sizeof(sigset_t))
- leaq %c[sigmask](%%rdi), %%rdx
- xorq %%rsi, %%rsi
- movq $0, %%rdi // SIG_BLOCK
+ 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 $SYS_rt_sigprocmask, %%rax
+ movq $%c[syscall_num], %%rax
syscall
// getcontext should return 0 on success
@@ -77,26 +76,27 @@ LLVM_LIBC_FUNCTION(int, getcontext, (ucontext_t *ucp)) {
::
[ret_size] "i"(sizeof(void*)),
[sigset_size] "i"(sizeof(sigset_t)),
- [r8] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_R8])),
- [r9] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_R9])),
- [r10] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_R10])),
- [r11] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_R11])),
- [r12] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_R12])),
- [r13] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_R13])),
- [r14] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_R14])),
- [r15] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_R15])),
- [rdi] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RDI])),
- [rsi] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RSI])),
- [rbp] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RBP])),
- [rbx] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RBX])),
- [rdx] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RDX])),
- [rax] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RAX])),
- [rcx] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RCX])),
- [rsp] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RSP])),
- [rip] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RIP])),
- [fpregs_mem] "i"(offsetof(ucontext_t, __fpregs_mem)),
- [fpregs_ptr] "i"(offsetof(ucontext_t, uc_mcontext.fpregs)),
- [sigmask] "i"(offsetof(ucontext_t, uc_sigmask))
+ [syscall_num] "i"(SYS_rt_sigprocmask),
+ [r8] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[__LIBC_REG_R8])),
+ [r9] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[__LIBC_REG_R9])),
+ [r10] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[__LIBC_REG_R10])),
+ [r11] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[__LIBC_REG_R11])),
+ [r12] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[__LIBC_REG_R12])),
+ [r13] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[__LIBC_REG_R13])),
+ [r14] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[__LIBC_REG_R14])),
+ [r15] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[__LIBC_REG_R15])),
+ [rdi] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[__LIBC_REG_RDI])),
+ [rsi] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[__LIBC_REG_RSI])),
+ [rbp] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[__LIBC_REG_RBP])),
+ [rbx] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[__LIBC_REG_RBX])),
+ [rdx] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[__LIBC_REG_RDX])),
+ [rax] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[__LIBC_REG_RAX])),
+ [rcx] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[__LIBC_REG_RCX])),
+ [rsp] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[__LIBC_REG_RSP])),
+ [rip] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[__LIBC_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"
);
}
diff --git a/libc/src/ucontext/x86_64/makecontext.cpp b/libc/src/ucontext/x86_64/makecontext.cpp
index 8907dab4f1d18..0705a56322b02 100644
--- a/libc/src/ucontext/x86_64/makecontext.cpp
+++ b/libc/src/ucontext/x86_64/makecontext.cpp
@@ -11,8 +11,8 @@
#include "src/__support/macros/config.h"
#include "include/llvm-libc-types/ucontext_t.h"
-#include <stddef.h>
-#include <stdint.h>
+#include "hdr/types/size_t.h"
+#include "hdr/stdint_proxy.h"
#include <stdarg.h>
#include "src/__support/OSUtil/exit.h"
@@ -63,12 +63,12 @@ LLVM_LIBC_FUNCTION(void, makecontext, (ucontext_t *ucp, void (*func)(void), int
va_list ap;
va_start(ap, argc);
- if (argc > 0) ucp->uc_mcontext.gregs[REG_RDI] = va_arg(ap, greg_t);
- if (argc > 1) ucp->uc_mcontext.gregs[REG_RSI] = va_arg(ap, greg_t);
- if (argc > 2) ucp->uc_mcontext.gregs[REG_RDX] = va_arg(ap, greg_t);
- if (argc > 3) ucp->uc_mcontext.gregs[REG_RCX] = va_arg(ap, greg_t);
- if (argc > 4) ucp->uc_mcontext.gregs[REG_R8] = va_arg(ap, greg_t);
- if (argc > 5) ucp->uc_mcontext.gregs[REG_R9] = va_arg(ap, greg_t);
+ if (argc > 0) ucp->uc_mcontext.gregs[__LIBC_REG_RDI] = va_arg(ap, greg_t);
+ if (argc > 1) ucp->uc_mcontext.gregs[__LIBC_REG_RSI] = va_arg(ap, greg_t);
+ if (argc > 2) ucp->uc_mcontext.gregs[__LIBC_REG_RDX] = va_arg(ap, greg_t);
+ if (argc > 3) ucp->uc_mcontext.gregs[__LIBC_REG_RCX] = va_arg(ap, greg_t);
+ if (argc > 4) ucp->uc_mcontext.gregs[__LIBC_REG_R8] = va_arg(ap, greg_t);
+ if (argc > 5) ucp->uc_mcontext.gregs[__LIBC_REG_R9] = va_arg(ap, greg_t);
for (int i = 0; i < stack_args; ++i) {
stack_area[i + 1] = va_arg(ap, greg_t);
@@ -76,9 +76,9 @@ LLVM_LIBC_FUNCTION(void, makecontext, (ucontext_t *ucp, void (*func)(void), int
va_end(ap);
- ucp->uc_mcontext.gregs[REG_RIP] = reinterpret_cast<greg_t>(func);
- ucp->uc_mcontext.gregs[REG_RSP] = new_rsp;
- ucp->uc_mcontext.gregs[REG_RBX] = reinterpret_cast<greg_t>(ucp->uc_link);
+ ucp->uc_mcontext.gregs[__LIBC_REG_RIP] = reinterpret_cast<greg_t>(func);
+ ucp->uc_mcontext.gregs[__LIBC_REG_RSP] = new_rsp;
+ ucp->uc_mcontext.gregs[__LIBC_REG_RBX] = reinterpret_cast<greg_t>(ucp->uc_link);
}
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/ucontext/x86_64/setcontext.cpp b/libc/src/ucontext/x86_64/setcontext.cpp
index d7d3b400ccfd4..4098688a84643 100644
--- a/libc/src/ucontext/x86_64/setcontext.cpp
+++ b/libc/src/ucontext/x86_64/setcontext.cpp
@@ -11,8 +11,7 @@
#include "include/llvm-libc-types/ucontext_t.h"
#include "src/__support/macros/config.h"
-#include <signal.h>
-#include <stddef.h>
+#include "hdr/types/size_t.h"
#include <sys/syscall.h>
namespace LIBC_NAMESPACE_DECL {
@@ -29,7 +28,7 @@ LLVM_LIBC_FUNCTION(int, setcontext, (const ucontext_t *ucp)) {
xorq %%rdx, %%rdx // oldset = NULL
movq $%c[sigset_size], %%r10 // sigsetsize = sizeof(sigset_t)
movq $2, %%rdi // how = SIG_SETMASK
- movq $SYS_rt_sigprocmask, %%rax
+ movq $%c[syscall_num], %%rax
syscall
popq %%rdi // Restore ucp
@@ -59,29 +58,30 @@ LLVM_LIBC_FUNCTION(int, setcontext, (const ucontext_t *ucp)) {
mov %c[rsi](%%rdi), %%rsi
mov %c[rdi](%%rdi), %%rdi
- jmp %%r11 // Jump to the saved instruction pointer
+ jmpq *%%r11 // Jump to the saved instruction pointer
)"
::
[sigset_size] "i"(sizeof(sigset_t)),
- [r8] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_R8])),
- [r9] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_R9])),
- [r10] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_R10])),
- [r11] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_R11])),
- [r12] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_R12])),
- [r13] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_R13])),
- [r14] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_R14])),
- [r15] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_R15])),
- [rdi] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RDI])),
- [rsi] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RSI])),
- [rbp] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RBP])),
- [rbx] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RBX])),
- [rdx] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RDX])),
- [rax] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RAX])),
- [rcx] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RCX])),
- [rsp] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RSP])),
- [rip] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RIP])),
- [fpregs_mem] "i"(offsetof(ucontext_t, __fpregs_mem)),
- [sigmask] "i"(offsetof(ucontext_t, uc_sigmask))
+ [syscall_num] "i"(SYS_rt_sigprocmask),
+ [r8] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[__LIBC_REG_R8])),
+ [r9] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[__LIBC_REG_R9])),
+ [r10] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[__LIBC_REG_R10])),
+ [r11] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[__LIBC_REG_R11])),
+ [r12] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[__LIBC_REG_R12])),
+ [r13] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[__LIBC_REG_R13])),
+ [r14] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[__LIBC_REG_R14])),
+ [r15] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[__LIBC_REG_R15])),
+ [rdi] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[__LIBC_REG_RDI])),
+ [rsi] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[__LIBC_REG_RSI])),
+ [rbp] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[__LIBC_REG_RBP])),
+ [rbx] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[__LIBC_REG_RBX])),
+ [rdx] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[__LIBC_REG_RDX])),
+ [rax] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[__LIBC_REG_RAX])),
+ [rcx] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[__LIBC_REG_RCX])),
+ [rsp] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[__LIBC_REG_RSP])),
+ [rip] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[__LIBC_REG_RIP])),
+ [fpregs_mem] "i"(__builtin_offsetof(ucontext_t, __fpregs_mem)),
+ [sigmask] "i"(__builtin_offsetof(ucontext_t, uc_sigmask))
: "memory", "rcx", "r11"
);
}
diff --git a/libc/src/ucontext/x86_64/swapcontext.cpp b/libc/src/ucontext/x86_64/swapcontext.cpp
index 989f93679afd2..78987011219b5 100644
--- a/libc/src/ucontext/x86_64/swapcontext.cpp
+++ b/libc/src/ucontext/x86_64/swapcontext.cpp
@@ -11,8 +11,7 @@
#include "include/llvm-libc-types/ucontext_t.h"
#include "src/__support/macros/config.h"
-#include <signal.h>
-#include <stddef.h>
+#include "hdr/types/size_t.h"
#include <sys/syscall.h>
namespace LIBC_NAMESPACE_DECL {
@@ -65,7 +64,7 @@ LLVM_LIBC_FUNCTION(int, swapcontext, (ucontext_t *oucp, const ucontext_t *ucp))
leaq %c[sigmask](%%rsi), %%rsi // set = &ucp->uc_sigmask
movq $%c[sigset_size], %%r10 // sigsetsize = sizeof(sigset_t)
movq $2, %%rdi // how = SIG_SETMASK
- movq $SYS_rt_sigprocmask, %%rax
+ movq $%c[syscall_num], %%rax
syscall
popq %%rsi // Restore ucp (new context)
popq %%rdi // Restore oucp (old context - not needed but for clean stack)
@@ -97,31 +96,32 @@ LLVM_LIBC_FUNCTION(int, swapcontext, (ucontext_t *oucp, const ucontext_t *ucp))
mov %c[rdi](%%rsi), %%rdi
mov %c[rsi](%%rsi), %%rsi
- jmp %%r11 // Jump to the saved instruction pointer
+ jmpq *%%r11 // Jump to the saved instruction pointer
)"
::
[ret_size] "i"(sizeof(void*)),
[sigset_size] "i"(sizeof(sigset_t)),
- [r8] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_R8])),
- [r9] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_R9])),
- [r10] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_R10])),
- [r11] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_R11])),
- [r12] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_R12])),
- [r13] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_R13])),
- [r14] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_R14])),
- [r15] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_R15])),
- [rdi] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RDI])),
- [rsi] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RSI])),
- [rbp] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RBP])),
- [rbx] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RBX])),
- [rdx] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RDX])),
- [rax] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RAX])),
- [rcx] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RCX])),
- [rsp] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RSP])),
- [rip] "i"(offsetof(ucontext_t, uc_mcontext.gregs[REG_RIP])),
- [fpregs_mem] "i"(offsetof(ucontext_t, __fpregs_mem)),
- [fpregs_ptr] "i"(offsetof(ucontext_t, uc_mcontext.fpregs)),
- [sigmask] "i"(offsetof(ucontext_t, uc_sigmask))
+ [syscall_num] "i"(SYS_rt_sigprocmask),
+ [r8] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[__LIBC_REG_R8])),
+ [r9] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[__LIBC_REG_R9])),
+ [r10] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[__LIBC_REG_R10])),
+ [r11] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[__LIBC_REG_R11])),
+ [r12] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[__LIBC_REG_R12])),
+ [r13] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[__LIBC_REG_R13])),
+ [r14] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[__LIBC_REG_R14])),
+ [r15] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[__LIBC_REG_R15])),
+ [rdi] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[__LIBC_REG_RDI])),
+ [rsi] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[__LIBC_REG_RSI])),
+ [rbp] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[__LIBC_REG_RBP])),
+ [rbx] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[__LIBC_REG_RBX])),
+ [rdx] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[__LIBC_REG_RDX])),
+ [rax] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[__LIBC_REG_RAX])),
+ [rcx] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[__LIBC_REG_RCX])),
+ [rsp] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[__LIBC_REG_RSP])),
+ [rip] "i"(__builtin_offsetof(ucontext_t, uc_mcontext.gregs[__LIBC_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"
);
}
diff --git a/libc/test/src/ucontext/CMakeLists.txt b/libc/test/src/ucontext/CMakeLists.txt
index a39638f299120..8dae7050e1f86 100644
--- a/libc/test/src/ucontext/CMakeLists.txt
+++ b/libc/test/src/ucontext/CMakeLists.txt
@@ -12,5 +12,8 @@ if(TARGET libc.src.ucontext.getcontext)
libc.src.ucontext.setcontext
libc.src.ucontext.makecontext
libc.src.ucontext.swapcontext
+ libc.src.signal.sigemptyset
+ libc.src.signal.sigaddset
+ libc.src.signal.sigprocmask
)
endif()
diff --git a/libc/test/src/ucontext/ucontext_test.cpp b/libc/test/src/ucontext/ucontext_test.cpp
index 488be9a35f931..c7d567d0e4db9 100644
--- a/libc/test/src/ucontext/ucontext_test.cpp
+++ b/libc/test/src/ucontext/ucontext_test.cpp
@@ -11,17 +11,31 @@
#include "src/ucontext/makecontext.h"
#include "src/ucontext/swapcontext.h"
+#include "src/signal/sigemptyset.h"
+#include "src/signal/sigaddset.h"
+#include "src/signal/sigprocmask.h"
+
#include "test/UnitTest/Test.h"
-#include <signal.h>
-#include <ucontext.h>
+
+#include "include/llvm-libc-macros/signal-macros.h"
+
+namespace LIBC_NAMESPACE {
+
+static bool is_signal_set(const sigset_t *set, int signum) {
+ // NSIG is 64, sigset_t is an array of unsigned long.
+ // Signum is 1-indexed.
+ int word = (signum - 1) / (sizeof(unsigned long) * 8);
+ int bit = (signum - 1) % (sizeof(unsigned long) * 8);
+ return (set->__signals[word] & (1UL << bit)) != 0;
+}
volatile int jumped = 0;
TEST(LlvmLibcUcontextTest, BasicStubTest) {
ucontext_t ctx;
- ASSERT_EQ(LIBC_NAMESPACE::getcontext(&ctx), 0);
+ ASSERT_EQ(getcontext(&ctx), 0);
if (!jumped) {
jumped = 1;
- LIBC_NAMESPACE::setcontext(&ctx);
+ setcontext(&ctx);
ASSERT_TRUE(false && "setcontext should not return on success");
}
}
@@ -31,18 +45,18 @@ volatile int swap_called = 0;
void swap_func() {
swap_called = 1;
- LIBC_NAMESPACE::setcontext(&old_ctx);
+ setcontext(&old_ctx);
}
TEST(LlvmLibcUcontextTest, SwapcontextTest) {
- LIBC_NAMESPACE::getcontext(&new_ctx);
+ getcontext(&new_ctx);
constexpr size_t STACK_SIZE = 8192;
char stack[STACK_SIZE];
new_ctx.uc_stack.ss_sp = stack;
new_ctx.uc_stack.ss_size = sizeof(stack);
- LIBC_NAMESPACE::makecontext(&new_ctx, swap_func, 0);
+ makecontext(&new_ctx, swap_func, 0);
- LIBC_NAMESPACE::swapcontext(&old_ctx, &new_ctx);
+ swapcontext(&old_ctx, &new_ctx);
ASSERT_EQ(swap_called, 1);
}
@@ -55,11 +69,11 @@ void args_func(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8) {
makecontext_args_called = 1;
arg1 = a1; arg2 = a2; arg3 = a3; arg4 = a4;
arg5 = a5; arg6 = a6; arg7 = a7; arg8 = a8;
- LIBC_NAMESPACE::setcontext(&old_ctx_args);
+ setcontext(&old_ctx_args);
}
TEST(LlvmLibcUcontextTest, MakecontextArgsTest) {
- LIBC_NAMESPACE::getcontext(&new_ctx_args);
+ getcontext(&new_ctx_args);
constexpr size_t STACK_SIZE = 8192;
char stack[STACK_SIZE];
new_ctx_args.uc_stack.ss_sp = stack;
@@ -69,9 +83,9 @@ TEST(LlvmLibcUcontextTest, MakecontextArgsTest) {
using func_t = void (*)(void);
auto func = reinterpret_cast<func_t>(args_func);
- LIBC_NAMESPACE::makecontext(&new_ctx_args, func, 8, 11, 22, 33, 44, 55, 66, 77, 88);
+ makecontext(&new_ctx_args, func, 8, 11, 22, 33, 44, 55, 66, 77, 88);
- LIBC_NAMESPACE::swapcontext(&old_ctx_args, &new_ctx_args);
+ swapcontext(&old_ctx_args, &new_ctx_args);
ASSERT_EQ(makecontext_args_called, 1);
ASSERT_EQ(arg1, 11);
@@ -91,7 +105,7 @@ void return_func() {
}
TEST(LlvmLibcUcontextTest, MakecontextReturnTest) {
- LIBC_NAMESPACE::getcontext(&new_ctx_return);
+ getcontext(&new_ctx_return);
constexpr size_t STACK_SIZE = 8192;
char stack[STACK_SIZE];
new_ctx_return.uc_stack.ss_sp = stack;
@@ -101,9 +115,9 @@ TEST(LlvmLibcUcontextTest, MakecontextReturnTest) {
using func_t = void (*)(void);
auto func = reinterpret_cast<func_t>(return_func);
- LIBC_NAMESPACE::makecontext(&new_ctx_return, func, 0);
+ makecontext(&new_ctx_return, func, 0);
- LIBC_NAMESPACE::swapcontext(&old_ctx_return, &new_ctx_return);
+ swapcontext(&old_ctx_return, &new_ctx_return);
ASSERT_EQ(makecontext_return_called, 1);
}
@@ -113,31 +127,36 @@ TEST(LlvmLibcUcontextTest, SignalMaskTest) {
sigemptyset(&set);
sigaddset(&set, SIGUSR1);
- // Set mask to [SIGUSR1] using system sigprocmask
+ // Set mask to [SIGUSR1] using sigprocmask
sigprocmask(SIG_SETMASK, &set, &old_set);
ucontext_t ctx;
- ASSERT_EQ(LIBC_NAMESPACE::getcontext(&ctx), 0);
-
- // Change current mask to [SIGUSR2]
- sigemptyset(&set);
- sigaddset(&set, SIGUSR2);
- sigprocmask(SIG_SETMASK, &set, NULL);
+ getcontext(&ctx);
- // Restore context, which should restore mask to [SIGUSR1]
- static volatile int done = 0;
- if (!done) {
- done = 1;
- LIBC_NAMESPACE::setcontext(&ctx);
+ // Verify that getcontext captured the mask
+ ASSERT_TRUE(is_signal_set(&ctx.uc_sigmask, SIGUSR1));
+ ASSERT_FALSE(is_signal_set(&ctx.uc_sigmask, SIGUSR2));
+
+ sigset_t new_set;
+ static volatile int mask_jumped = 0;
+ if (mask_jumped == 0) {
+ mask_jumped = 1;
+ sigemptyset(&new_set);
+ sigaddset(&new_set, SIGUSR2);
+ sigprocmask(SIG_SETMASK, &new_set, nullptr);
+
+ setcontext(&ctx);
}
// Check current mask
sigset_t current;
- sigprocmask(SIG_BLOCK, NULL, ¤t);
+ sigprocmask(SIG_BLOCK, nullptr, ¤t);
// Restore original mask for clean state
- sigprocmask(SIG_SETMASK, &old_set, NULL);
+ sigprocmask(SIG_SETMASK, &old_set, nullptr);
- ASSERT_TRUE(sigismember(¤t, SIGUSR1));
- ASSERT_FALSE(sigismember(¤t, SIGUSR2));
+ ASSERT_TRUE(is_signal_set(¤t, SIGUSR1));
+ ASSERT_FALSE(is_signal_set(¤t, SIGUSR2));
}
+
+} // namespace LIBC_NAMESPACE
More information about the libc-commits
mailing list