diff mbox series

Implement setcontext/getcontext/makecontext/swapcontext for Hurd x86_64

Message ID 20240217202535.1860803-1-flaviocruz@gmail.com
State New
Headers show
Series Implement setcontext/getcontext/makecontext/swapcontext for Hurd x86_64 | expand

Commit Message

Flavio Cruz Feb. 17, 2024, 8:25 p.m. UTC
Tested with the tests provided by glibc plus some other toy examples.
---
 sysdeps/mach/hurd/x86_64/Makefile          |   4 +
 sysdeps/mach/hurd/x86_64/__start_context.S |  49 +++++++++
 sysdeps/mach/hurd/x86_64/getcontext.S      |  68 ++++++++++++
 sysdeps/mach/hurd/x86_64/makecontext.c     | 119 ++++++++++++++++++++
 sysdeps/mach/hurd/x86_64/setcontext.S      |  96 +++++++++++++++++
 sysdeps/mach/hurd/x86_64/swapcontext.S     | 120 +++++++++++++++++++++
 6 files changed, 456 insertions(+)
 create mode 100644 sysdeps/mach/hurd/x86_64/__start_context.S
 create mode 100644 sysdeps/mach/hurd/x86_64/getcontext.S
 create mode 100644 sysdeps/mach/hurd/x86_64/makecontext.c
 create mode 100644 sysdeps/mach/hurd/x86_64/setcontext.S
 create mode 100644 sysdeps/mach/hurd/x86_64/swapcontext.S

Comments

Samuel Thibault Feb. 17, 2024, 9:17 p.m. UTC | #1
Applied, thanks!!

Flavio Cruz, le sam. 17 févr. 2024 15:25:35 -0500, a ecrit:
> Tested with the tests provided by glibc plus some other toy examples.
> ---
>  sysdeps/mach/hurd/x86_64/Makefile          |   4 +
>  sysdeps/mach/hurd/x86_64/__start_context.S |  49 +++++++++
>  sysdeps/mach/hurd/x86_64/getcontext.S      |  68 ++++++++++++
>  sysdeps/mach/hurd/x86_64/makecontext.c     | 119 ++++++++++++++++++++
>  sysdeps/mach/hurd/x86_64/setcontext.S      |  96 +++++++++++++++++
>  sysdeps/mach/hurd/x86_64/swapcontext.S     | 120 +++++++++++++++++++++
>  6 files changed, 456 insertions(+)
>  create mode 100644 sysdeps/mach/hurd/x86_64/__start_context.S
>  create mode 100644 sysdeps/mach/hurd/x86_64/getcontext.S
>  create mode 100644 sysdeps/mach/hurd/x86_64/makecontext.c
>  create mode 100644 sysdeps/mach/hurd/x86_64/setcontext.S
>  create mode 100644 sysdeps/mach/hurd/x86_64/swapcontext.S
> 
> diff --git a/sysdeps/mach/hurd/x86_64/Makefile b/sysdeps/mach/hurd/x86_64/Makefile
> index 80cf2eb6..2b43f5d6 100644
> --- a/sysdeps/mach/hurd/x86_64/Makefile
> +++ b/sysdeps/mach/hurd/x86_64/Makefile
> @@ -3,3 +3,7 @@ ifeq ($(subdir),conform)
>  # (missing SA_NOCLDWAIT)
>  conformtest-xfail-conds += x86_64-gnu
>  endif
> +
> +ifeq ($(subdir),stdlib)
> +sysdep_routines += __start_context
> +endif
> diff --git a/sysdeps/mach/hurd/x86_64/__start_context.S b/sysdeps/mach/hurd/x86_64/__start_context.S
> new file mode 100644
> index 00000000..3cb4c6b5
> --- /dev/null
> +++ b/sysdeps/mach/hurd/x86_64/__start_context.S
> @@ -0,0 +1,49 @@
> +/* Copyright (C) 2024 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <sysdep.h>
> +
> +/* This is the helper code which gets called if a function which is
> +   registered with 'makecontext' returns.  In this case we have to
> +   install the context listed in the uc_link element of the context
> +   'makecontext' manipulated at the time of the 'makecontext' call.
> +   If the pointer is NULL the process must terminate.  */
> +
> +
> +ENTRY(__start_context)
> +	/* This removes the parameters passed to the function given to
> +	   'makecontext' from the stack.  RBX contains the address
> +	   on the stack pointer for the next context.  */
> +	movq	%rbx, %rsp
> +
> +	/* Don't use pop here so that stack is aligned to 16 bytes.  */
> +	movq	(%rsp), %rdi		/* This is the next context.  */
> +	testq	%rdi, %rdi
> +	je	2f			/* If it is zero exit.  */
> +
> +	call	__setcontext
> +	/* If this returns (which can happen if __sigprocmask fails) we'll
> +	   exit the program with the return error value (-1).  */
> +	movq	%rax,%rdi
> +
> +2:
> +	call	HIDDEN_JUMPTARGET(exit)
> +	/* The 'exit' call should never return.  In case it does cause
> +	   the process to terminate.  */
> +L(hlt):
> +	hlt
> +END(__start_context)
> diff --git a/sysdeps/mach/hurd/x86_64/getcontext.S b/sysdeps/mach/hurd/x86_64/getcontext.S
> new file mode 100644
> index 00000000..ef431be1
> --- /dev/null
> +++ b/sysdeps/mach/hurd/x86_64/getcontext.S
> @@ -0,0 +1,68 @@
> +/* Save current context.
> +   Copyright (C) 2024 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <sysdep.h>
> +
> +#include "ucontext_i.h"
> +
> +
> +ENTRY(__getcontext)
> +	/* Save the preserved registers, the registers used for passing
> +	   args, and the return address.  */
> +	movq	%rbx, oRBX(%rdi)
> +	movq	%rbp, oRBP(%rdi)
> +	movq	%r12, oR12(%rdi)
> +	movq	%r13, oR13(%rdi)
> +	movq	%r14, oR14(%rdi)
> +	movq	%r15, oR15(%rdi)
> +
> +	movq	%rdi, oRDI(%rdi)
> +	movq	%rsi, oRSI(%rdi)
> +	movq	%rdx, oRDX(%rdi)
> +	movq	%rcx, oRCX(%rdi)
> +	movq	%r8, oR8(%rdi)
> +	movq	%r9, oR9(%rdi)
> +
> +	movq	(%rsp), %rcx
> +	movq	%rcx, oRIP(%rdi)
> +	leaq	8(%rsp), %rcx		/* Exclude the return address.  */
> +	movq	%rcx, oRSP(%rdi)
> +
> +	/* We have separate floating-point register content memory on the
> +	   stack.  We use the __fpregs_mem block in the context.  Set the
> +	   links up correctly.  */
> +
> +	leaq	oFPREGSMEM(%rdi), %rcx
> +	movq	%rcx, oFPREGS(%rdi)
> +	/* Save the floating-point environment.  */
> +	fnstenv	(%rcx)
> +	fldenv	(%rcx)
> +	stmxcsr oMXCSR(%rdi)
> +
> +	/* Save the current signal mask with
> +	 * __sigprocmask(SIG_BLOCK, NULL, oSIGMASK(%rdi)); */
> +	leaq	oSIGMASK(%rdi), %rdx
> +	movq $0, %rsi
> +	movl $SIG_BLOCK, %edi
> +	call	HIDDEN_JUMPTARGET (__sigprocmask)
> +
> +	/* Propagate %rax (and errno, in case).  */
> +	ret
> +PSEUDO_END(__getcontext)
> +
> +weak_alias (__getcontext, getcontext)
> diff --git a/sysdeps/mach/hurd/x86_64/makecontext.c b/sysdeps/mach/hurd/x86_64/makecontext.c
> new file mode 100644
> index 00000000..6990a777
> --- /dev/null
> +++ b/sysdeps/mach/hurd/x86_64/makecontext.c
> @@ -0,0 +1,119 @@
> +/* Create new context.
> +   Copyright (C) 2024 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <sysdep.h>
> +#include <stdarg.h>
> +#include <stdint.h>
> +#include <ucontext.h>
> +
> +#include "ucontext_i.h"
> +
> +/* This implementation can handle any ARGC value but only
> +   normal integer parameters.
> +   makecontext sets up a stack and the registers for the
> +   user context. The stack looks like this:
> +               +-----------------------+
> +               | next context          |
> +               +-----------------------+
> +               | parameter 7-n         |
> +	       +-----------------------+
> +	       | trampoline address    |
> +    %rsp ->    +-----------------------+
> +
> +   The registers are set up like this:
> +     %rdi,%rsi,%rdx,%rcx,%r8,%r9: parameter 1 to 6
> +     %rbx   : address of next context
> +     %rsp   : stack pointer.
> +*/
> +
> +/* XXX: This implementation currently only handles integer arguments.
> +   To handle long int and pointer arguments the va_arg arguments needs
> +   to be changed to long and also the stdlib/tst-setcontext.c file needs
> +   to be changed to pass long arguments to makecontext.  */
> +
> +
> +void
> +__makecontext (ucontext_t *ucp, void (*func) (void), int argc, ...)
> +{
> +  extern void __start_context (void) attribute_hidden;
> +  greg_t *sp;
> +  unsigned int idx_uc_link;
> +  va_list ap;
> +  int i;
> +
> +  /* Generate room on stack for parameter if needed and uc_link.  */
> +  sp = (greg_t *) ((uintptr_t) ucp->uc_stack.ss_sp
> +		   + ucp->uc_stack.ss_size);
> +  sp -= (argc > 6 ? argc - 6 : 0) + 1;
> +  /* Align stack and make space for trampoline address.  */
> +  sp = (greg_t *) ((((uintptr_t) sp) & -16L) - 8);
> +
> +  idx_uc_link = (argc > 6 ? argc - 6 : 0) + 1;
> +
> +  /* Setup context ucp.  */
> +  /* Address to jump to.  */
> +  ucp->uc_mcontext.gregs[REG_RIP] = (uintptr_t) func;
> +  /* Setup rbx.*/
> +  ucp->uc_mcontext.gregs[REG_RBX] = (uintptr_t) &sp[idx_uc_link];
> +  ucp->uc_mcontext.gregs[REG_RSP] = (uintptr_t) sp;
> +
> +  /* Setup stack.  */
> +  sp[0] = (uintptr_t) &__start_context;
> +  sp[idx_uc_link] = (uintptr_t) ucp->uc_link;
> +
> +  va_start (ap, argc);
> +  /* Handle arguments.
> +
> +     The standard says the parameters must all be int values.  This is
> +     an historic accident and would be done differently today.  For
> +     x86-64 all integer values are passed as 64-bit values and
> +     therefore extending the API to copy 64-bit values instead of
> +     32-bit ints makes sense.  It does not break existing
> +     functionality and it does not violate the standard which says
> +     that passing non-int values means undefined behavior.  */
> +  for (i = 0; i < argc; ++i)
> +    switch (i)
> +      {
> +      case 0:
> +	ucp->uc_mcontext.gregs[REG_RDI] = va_arg (ap, greg_t);
> +	break;
> +      case 1:
> +	ucp->uc_mcontext.gregs[REG_RSI] = va_arg (ap, greg_t);
> +	break;
> +      case 2:
> +	ucp->uc_mcontext.gregs[REG_RDX] = va_arg (ap, greg_t);
> +	break;
> +      case 3:
> +	ucp->uc_mcontext.gregs[REG_RCX] = va_arg (ap, greg_t);
> +	break;
> +      case 4:
> +	ucp->uc_mcontext.gregs[REG_R8] = va_arg (ap, greg_t);
> +	break;
> +      case 5:
> +	ucp->uc_mcontext.gregs[REG_R9] = va_arg (ap, greg_t);
> +	break;
> +      default:
> +	/* Put value on stack.  */
> +	sp[i - 5] = va_arg (ap, greg_t);
> +	break;
> +      }
> +  va_end (ap);
> +}
> +
> +
> +weak_alias (__makecontext, makecontext)
> diff --git a/sysdeps/mach/hurd/x86_64/setcontext.S b/sysdeps/mach/hurd/x86_64/setcontext.S
> new file mode 100644
> index 00000000..99919ee2
> --- /dev/null
> +++ b/sysdeps/mach/hurd/x86_64/setcontext.S
> @@ -0,0 +1,96 @@
> +/* Install given context.
> +   Copyright (C) 2024 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <sysdep.h>
> +
> +#include "ucontext_i.h"
> +
> +
> +ENTRY(__setcontext)
> +	/* Save argument since call will destroy it.  */
> +	pushq	%rdi
> +	cfi_adjust_cfa_offset(8)
> +
> +	/* Set the signal mask with
> +	   __sigprocmask (SIG_SETMASK, mask, NULL).  */
> +	xorl	%edx, %edx
> +	leaq	oSIGMASK(%rdi), %rsi
> +	movl	$SIG_SETMASK, %edi
> +	call	HIDDEN_JUMPTARGET (__sigprocmask)
> +	/* Pop the pointer into RDX. The choice is arbitrary, but
> +	   leaving RDI and RSI available for use later can avoid
> +	   shuffling values.  */
> +	popq	%rdx
> +
> +	test	%rax, %rax
> +	jne	L(pseudo_end)
> +
> +	/* Restore the floating-point context.  Not the registers, only the
> +	   rest.  */
> +	movq	oFPREGS(%rdx), %rcx
> +	fldenv	(%rcx)
> +	ldmxcsr oMXCSR(%rdx)
> +
> +	/* Load the new stack pointer, the preserved registers and
> +	   registers used for passing args.  */
> +	cfi_def_cfa(%rdx, 0)
> +	cfi_offset(%rbx,oRBX)
> +	cfi_offset(%rbp,oRBP)
> +	cfi_offset(%r12,oR12)
> +	cfi_offset(%r13,oR13)
> +	cfi_offset(%r14,oR14)
> +	cfi_offset(%r15,oR15)
> +	cfi_offset(%rsp,oRSP)
> +	cfi_offset(%rip,oRIP)
> +
> +	movq	oRSP(%rdx), %rsp
> +	movq	oRBX(%rdx), %rbx
> +	movq	oRBP(%rdx), %rbp
> +	movq	oR12(%rdx), %r12
> +	movq	oR13(%rdx), %r13
> +	movq	oR14(%rdx), %r14
> +	movq	oR15(%rdx), %r15
> +
> +	/* The following ret should return to the address set with
> +	getcontext.  Therefore push the address on the stack.  */
> +	movq	oRIP(%rdx), %rcx
> +	pushq	%rcx
> +
> +	movq	oRSI(%rdx), %rsi
> +	movq	oRDI(%rdx), %rdi
> +	movq	oRCX(%rdx), %rcx
> +	movq	oR8(%rdx), %r8
> +	movq	oR9(%rdx), %r9
> +
> +	/* Setup finally %rdx.  */
> +	movq	oRDX(%rdx), %rdx
> +
> +	/* End FDE here, we fall into another context.  */
> +	cfi_endproc
> +	cfi_startproc
> +
> +	/* Clear rax to indicate success.  */
> +	xorl	%eax, %eax
> +L(pseudo_end):
> +	/* The following 'ret' will pop the address of the code and jump
> +	   to it.  */
> +	ret
> +PSEUDO_END(__setcontext)
> +libc_hidden_def (__setcontext)
> +
> +weak_alias (__setcontext, setcontext)
> diff --git a/sysdeps/mach/hurd/x86_64/swapcontext.S b/sysdeps/mach/hurd/x86_64/swapcontext.S
> new file mode 100644
> index 00000000..79718a1f
> --- /dev/null
> +++ b/sysdeps/mach/hurd/x86_64/swapcontext.S
> @@ -0,0 +1,120 @@
> +/* Save current context and install the given one.
> +   Copyright (C) 2024 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <sysdep.h>
> +
> +#include "ucontext_i.h"
> +
> +
> +/* int __swapcontext (ucontext_t *oucp, const ucontext_t *ucp);
> +
> +  Saves the machine context in oucp such that when it is activated,
> +  it appears as if __swapcontextt() returned again, restores the
> +  machine context in ucp and thereby resumes execution in that
> +  context.
> +
> +  This implementation is intended to be used for *synchronous* context
> +  switches only.  Therefore, it does not have to save anything
> +  other than the PRESERVED state.  */
> +
> +ENTRY(__swapcontext)
> +	/* Save the preserved registers, the registers used for passing args,
> +	   and the return address.  */
> +	movq	%rbx, oRBX(%rdi)
> +	movq	%rbp, oRBP(%rdi)
> +	movq	%r12, oR12(%rdi)
> +	movq	%r13, oR13(%rdi)
> +	movq	%r14, oR14(%rdi)
> +	movq	%r15, oR15(%rdi)
> +
> +	movq	%rdi, oRDI(%rdi)
> +	movq	%rsi, oRSI(%rdi)
> +	movq	%rdx, oRDX(%rdi)
> +	movq	%rcx, oRCX(%rdi)
> +	movq	%r8, oR8(%rdi)
> +	movq	%r9, oR9(%rdi)
> +
> +	movq	(%rsp), %rcx
> +	movq	%rcx, oRIP(%rdi)
> +	leaq	8(%rsp), %rcx		/* Exclude the return address.  */
> +	movq	%rcx, oRSP(%rdi)
> +
> +	/* We have separate floating-point register content memory on the
> +	   stack.  We use the __fpregs_mem block in the context.  Set the
> +	   links up correctly.  */
> +	leaq	oFPREGSMEM(%rdi), %rcx
> +	movq	%rcx, oFPREGS(%rdi)
> +	/* Save the floating-point environment.  */
> +	fnstenv	(%rcx)
> +	stmxcsr oMXCSR(%rdi)
> +
> +
> +	/* The function call destroys some registers, save ucp.  */
> +	movq	%rsi, %r12
> +
> +	/* Save the current signal mask and install the new one with
> +	   __sigprocmask (SIG_BLOCK, newset, oldset).  */
> +	leaq	oSIGMASK(%rdi), %rdx
> +	leaq	oSIGMASK(%rsi), %rsi
> +	movl	$SIG_SETMASK, %edi
> +	call	HIDDEN_JUMPTARGET (__sigprocmask)
> +	test	%rax, %rax
> +	jne	L(pseudo_end)
> +
> +	/* Restore destroyed register into RDX. The choice is arbitrary,
> +	   but leaving RDI and RSI available for use later can avoid
> +	   shuffling values.  */
> +	movq	%r12, %rdx
> +
> +	/* Restore the floating-point context.  Not the registers, only the
> +	   rest.  */
> +	movq	oFPREGS(%rdx), %rcx
> +	fldenv	(%rcx)
> +	ldmxcsr oMXCSR(%rdx)
> +
> +	/* Load the new stack pointer and the preserved registers.  */
> +	movq	oRSP(%rdx), %rsp
> +	movq	oRBX(%rdx), %rbx
> +	movq	oRBP(%rdx), %rbp
> +	movq	oR12(%rdx), %r12
> +	movq	oR13(%rdx), %r13
> +	movq	oR14(%rdx), %r14
> +	movq	oR15(%rdx), %r15
> +
> +	/* The following ret should return to the address set with
> +	getcontext.  Therefore push the address on the stack.  */
> +	movq	oRIP(%rdx), %rcx
> +	pushq	%rcx
> +
> +	/* Setup registers used for passing args.  */
> +	movq	oRDI(%rdx), %rdi
> +	movq	oRSI(%rdx), %rsi
> +	movq	oRCX(%rdx), %rcx
> +	movq	oR8(%rdx), %r8
> +	movq	oR9(%rdx), %r9
> +
> +	/* Setup finally %rdx.  */
> +	movq	oRDX(%rdx), %rdx
> +
> +	/* Clear rax to indicate success.  */
> +	xorl	%eax, %eax
> +L(pseudo_end):
> +	ret
> +PSEUDO_END(__swapcontext)
> +
> +weak_alias (__swapcontext, swapcontext)
> -- 
> 2.39.2
>
diff mbox series

Patch

diff --git a/sysdeps/mach/hurd/x86_64/Makefile b/sysdeps/mach/hurd/x86_64/Makefile
index 80cf2eb6..2b43f5d6 100644
--- a/sysdeps/mach/hurd/x86_64/Makefile
+++ b/sysdeps/mach/hurd/x86_64/Makefile
@@ -3,3 +3,7 @@  ifeq ($(subdir),conform)
 # (missing SA_NOCLDWAIT)
 conformtest-xfail-conds += x86_64-gnu
 endif
+
+ifeq ($(subdir),stdlib)
+sysdep_routines += __start_context
+endif
diff --git a/sysdeps/mach/hurd/x86_64/__start_context.S b/sysdeps/mach/hurd/x86_64/__start_context.S
new file mode 100644
index 00000000..3cb4c6b5
--- /dev/null
+++ b/sysdeps/mach/hurd/x86_64/__start_context.S
@@ -0,0 +1,49 @@ 
+/* Copyright (C) 2024 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <sysdep.h>
+
+/* This is the helper code which gets called if a function which is
+   registered with 'makecontext' returns.  In this case we have to
+   install the context listed in the uc_link element of the context
+   'makecontext' manipulated at the time of the 'makecontext' call.
+   If the pointer is NULL the process must terminate.  */
+
+
+ENTRY(__start_context)
+	/* This removes the parameters passed to the function given to
+	   'makecontext' from the stack.  RBX contains the address
+	   on the stack pointer for the next context.  */
+	movq	%rbx, %rsp
+
+	/* Don't use pop here so that stack is aligned to 16 bytes.  */
+	movq	(%rsp), %rdi		/* This is the next context.  */
+	testq	%rdi, %rdi
+	je	2f			/* If it is zero exit.  */
+
+	call	__setcontext
+	/* If this returns (which can happen if __sigprocmask fails) we'll
+	   exit the program with the return error value (-1).  */
+	movq	%rax,%rdi
+
+2:
+	call	HIDDEN_JUMPTARGET(exit)
+	/* The 'exit' call should never return.  In case it does cause
+	   the process to terminate.  */
+L(hlt):
+	hlt
+END(__start_context)
diff --git a/sysdeps/mach/hurd/x86_64/getcontext.S b/sysdeps/mach/hurd/x86_64/getcontext.S
new file mode 100644
index 00000000..ef431be1
--- /dev/null
+++ b/sysdeps/mach/hurd/x86_64/getcontext.S
@@ -0,0 +1,68 @@ 
+/* Save current context.
+   Copyright (C) 2024 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <sysdep.h>
+
+#include "ucontext_i.h"
+
+
+ENTRY(__getcontext)
+	/* Save the preserved registers, the registers used for passing
+	   args, and the return address.  */
+	movq	%rbx, oRBX(%rdi)
+	movq	%rbp, oRBP(%rdi)
+	movq	%r12, oR12(%rdi)
+	movq	%r13, oR13(%rdi)
+	movq	%r14, oR14(%rdi)
+	movq	%r15, oR15(%rdi)
+
+	movq	%rdi, oRDI(%rdi)
+	movq	%rsi, oRSI(%rdi)
+	movq	%rdx, oRDX(%rdi)
+	movq	%rcx, oRCX(%rdi)
+	movq	%r8, oR8(%rdi)
+	movq	%r9, oR9(%rdi)
+
+	movq	(%rsp), %rcx
+	movq	%rcx, oRIP(%rdi)
+	leaq	8(%rsp), %rcx		/* Exclude the return address.  */
+	movq	%rcx, oRSP(%rdi)
+
+	/* We have separate floating-point register content memory on the
+	   stack.  We use the __fpregs_mem block in the context.  Set the
+	   links up correctly.  */
+
+	leaq	oFPREGSMEM(%rdi), %rcx
+	movq	%rcx, oFPREGS(%rdi)
+	/* Save the floating-point environment.  */
+	fnstenv	(%rcx)
+	fldenv	(%rcx)
+	stmxcsr oMXCSR(%rdi)
+
+	/* Save the current signal mask with
+	 * __sigprocmask(SIG_BLOCK, NULL, oSIGMASK(%rdi)); */
+	leaq	oSIGMASK(%rdi), %rdx
+	movq $0, %rsi
+	movl $SIG_BLOCK, %edi
+	call	HIDDEN_JUMPTARGET (__sigprocmask)
+
+	/* Propagate %rax (and errno, in case).  */
+	ret
+PSEUDO_END(__getcontext)
+
+weak_alias (__getcontext, getcontext)
diff --git a/sysdeps/mach/hurd/x86_64/makecontext.c b/sysdeps/mach/hurd/x86_64/makecontext.c
new file mode 100644
index 00000000..6990a777
--- /dev/null
+++ b/sysdeps/mach/hurd/x86_64/makecontext.c
@@ -0,0 +1,119 @@ 
+/* Create new context.
+   Copyright (C) 2024 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <sysdep.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <ucontext.h>
+
+#include "ucontext_i.h"
+
+/* This implementation can handle any ARGC value but only
+   normal integer parameters.
+   makecontext sets up a stack and the registers for the
+   user context. The stack looks like this:
+               +-----------------------+
+               | next context          |
+               +-----------------------+
+               | parameter 7-n         |
+	       +-----------------------+
+	       | trampoline address    |
+    %rsp ->    +-----------------------+
+
+   The registers are set up like this:
+     %rdi,%rsi,%rdx,%rcx,%r8,%r9: parameter 1 to 6
+     %rbx   : address of next context
+     %rsp   : stack pointer.
+*/
+
+/* XXX: This implementation currently only handles integer arguments.
+   To handle long int and pointer arguments the va_arg arguments needs
+   to be changed to long and also the stdlib/tst-setcontext.c file needs
+   to be changed to pass long arguments to makecontext.  */
+
+
+void
+__makecontext (ucontext_t *ucp, void (*func) (void), int argc, ...)
+{
+  extern void __start_context (void) attribute_hidden;
+  greg_t *sp;
+  unsigned int idx_uc_link;
+  va_list ap;
+  int i;
+
+  /* Generate room on stack for parameter if needed and uc_link.  */
+  sp = (greg_t *) ((uintptr_t) ucp->uc_stack.ss_sp
+		   + ucp->uc_stack.ss_size);
+  sp -= (argc > 6 ? argc - 6 : 0) + 1;
+  /* Align stack and make space for trampoline address.  */
+  sp = (greg_t *) ((((uintptr_t) sp) & -16L) - 8);
+
+  idx_uc_link = (argc > 6 ? argc - 6 : 0) + 1;
+
+  /* Setup context ucp.  */
+  /* Address to jump to.  */
+  ucp->uc_mcontext.gregs[REG_RIP] = (uintptr_t) func;
+  /* Setup rbx.*/
+  ucp->uc_mcontext.gregs[REG_RBX] = (uintptr_t) &sp[idx_uc_link];
+  ucp->uc_mcontext.gregs[REG_RSP] = (uintptr_t) sp;
+
+  /* Setup stack.  */
+  sp[0] = (uintptr_t) &__start_context;
+  sp[idx_uc_link] = (uintptr_t) ucp->uc_link;
+
+  va_start (ap, argc);
+  /* Handle arguments.
+
+     The standard says the parameters must all be int values.  This is
+     an historic accident and would be done differently today.  For
+     x86-64 all integer values are passed as 64-bit values and
+     therefore extending the API to copy 64-bit values instead of
+     32-bit ints makes sense.  It does not break existing
+     functionality and it does not violate the standard which says
+     that passing non-int values means undefined behavior.  */
+  for (i = 0; i < argc; ++i)
+    switch (i)
+      {
+      case 0:
+	ucp->uc_mcontext.gregs[REG_RDI] = va_arg (ap, greg_t);
+	break;
+      case 1:
+	ucp->uc_mcontext.gregs[REG_RSI] = va_arg (ap, greg_t);
+	break;
+      case 2:
+	ucp->uc_mcontext.gregs[REG_RDX] = va_arg (ap, greg_t);
+	break;
+      case 3:
+	ucp->uc_mcontext.gregs[REG_RCX] = va_arg (ap, greg_t);
+	break;
+      case 4:
+	ucp->uc_mcontext.gregs[REG_R8] = va_arg (ap, greg_t);
+	break;
+      case 5:
+	ucp->uc_mcontext.gregs[REG_R9] = va_arg (ap, greg_t);
+	break;
+      default:
+	/* Put value on stack.  */
+	sp[i - 5] = va_arg (ap, greg_t);
+	break;
+      }
+  va_end (ap);
+}
+
+
+weak_alias (__makecontext, makecontext)
diff --git a/sysdeps/mach/hurd/x86_64/setcontext.S b/sysdeps/mach/hurd/x86_64/setcontext.S
new file mode 100644
index 00000000..99919ee2
--- /dev/null
+++ b/sysdeps/mach/hurd/x86_64/setcontext.S
@@ -0,0 +1,96 @@ 
+/* Install given context.
+   Copyright (C) 2024 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <sysdep.h>
+
+#include "ucontext_i.h"
+
+
+ENTRY(__setcontext)
+	/* Save argument since call will destroy it.  */
+	pushq	%rdi
+	cfi_adjust_cfa_offset(8)
+
+	/* Set the signal mask with
+	   __sigprocmask (SIG_SETMASK, mask, NULL).  */
+	xorl	%edx, %edx
+	leaq	oSIGMASK(%rdi), %rsi
+	movl	$SIG_SETMASK, %edi
+	call	HIDDEN_JUMPTARGET (__sigprocmask)
+	/* Pop the pointer into RDX. The choice is arbitrary, but
+	   leaving RDI and RSI available for use later can avoid
+	   shuffling values.  */
+	popq	%rdx
+
+	test	%rax, %rax
+	jne	L(pseudo_end)
+
+	/* Restore the floating-point context.  Not the registers, only the
+	   rest.  */
+	movq	oFPREGS(%rdx), %rcx
+	fldenv	(%rcx)
+	ldmxcsr oMXCSR(%rdx)
+
+	/* Load the new stack pointer, the preserved registers and
+	   registers used for passing args.  */
+	cfi_def_cfa(%rdx, 0)
+	cfi_offset(%rbx,oRBX)
+	cfi_offset(%rbp,oRBP)
+	cfi_offset(%r12,oR12)
+	cfi_offset(%r13,oR13)
+	cfi_offset(%r14,oR14)
+	cfi_offset(%r15,oR15)
+	cfi_offset(%rsp,oRSP)
+	cfi_offset(%rip,oRIP)
+
+	movq	oRSP(%rdx), %rsp
+	movq	oRBX(%rdx), %rbx
+	movq	oRBP(%rdx), %rbp
+	movq	oR12(%rdx), %r12
+	movq	oR13(%rdx), %r13
+	movq	oR14(%rdx), %r14
+	movq	oR15(%rdx), %r15
+
+	/* The following ret should return to the address set with
+	getcontext.  Therefore push the address on the stack.  */
+	movq	oRIP(%rdx), %rcx
+	pushq	%rcx
+
+	movq	oRSI(%rdx), %rsi
+	movq	oRDI(%rdx), %rdi
+	movq	oRCX(%rdx), %rcx
+	movq	oR8(%rdx), %r8
+	movq	oR9(%rdx), %r9
+
+	/* Setup finally %rdx.  */
+	movq	oRDX(%rdx), %rdx
+
+	/* End FDE here, we fall into another context.  */
+	cfi_endproc
+	cfi_startproc
+
+	/* Clear rax to indicate success.  */
+	xorl	%eax, %eax
+L(pseudo_end):
+	/* The following 'ret' will pop the address of the code and jump
+	   to it.  */
+	ret
+PSEUDO_END(__setcontext)
+libc_hidden_def (__setcontext)
+
+weak_alias (__setcontext, setcontext)
diff --git a/sysdeps/mach/hurd/x86_64/swapcontext.S b/sysdeps/mach/hurd/x86_64/swapcontext.S
new file mode 100644
index 00000000..79718a1f
--- /dev/null
+++ b/sysdeps/mach/hurd/x86_64/swapcontext.S
@@ -0,0 +1,120 @@ 
+/* Save current context and install the given one.
+   Copyright (C) 2024 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <sysdep.h>
+
+#include "ucontext_i.h"
+
+
+/* int __swapcontext (ucontext_t *oucp, const ucontext_t *ucp);
+
+  Saves the machine context in oucp such that when it is activated,
+  it appears as if __swapcontextt() returned again, restores the
+  machine context in ucp and thereby resumes execution in that
+  context.
+
+  This implementation is intended to be used for *synchronous* context
+  switches only.  Therefore, it does not have to save anything
+  other than the PRESERVED state.  */
+
+ENTRY(__swapcontext)
+	/* Save the preserved registers, the registers used for passing args,
+	   and the return address.  */
+	movq	%rbx, oRBX(%rdi)
+	movq	%rbp, oRBP(%rdi)
+	movq	%r12, oR12(%rdi)
+	movq	%r13, oR13(%rdi)
+	movq	%r14, oR14(%rdi)
+	movq	%r15, oR15(%rdi)
+
+	movq	%rdi, oRDI(%rdi)
+	movq	%rsi, oRSI(%rdi)
+	movq	%rdx, oRDX(%rdi)
+	movq	%rcx, oRCX(%rdi)
+	movq	%r8, oR8(%rdi)
+	movq	%r9, oR9(%rdi)
+
+	movq	(%rsp), %rcx
+	movq	%rcx, oRIP(%rdi)
+	leaq	8(%rsp), %rcx		/* Exclude the return address.  */
+	movq	%rcx, oRSP(%rdi)
+
+	/* We have separate floating-point register content memory on the
+	   stack.  We use the __fpregs_mem block in the context.  Set the
+	   links up correctly.  */
+	leaq	oFPREGSMEM(%rdi), %rcx
+	movq	%rcx, oFPREGS(%rdi)
+	/* Save the floating-point environment.  */
+	fnstenv	(%rcx)
+	stmxcsr oMXCSR(%rdi)
+
+
+	/* The function call destroys some registers, save ucp.  */
+	movq	%rsi, %r12
+
+	/* Save the current signal mask and install the new one with
+	   __sigprocmask (SIG_BLOCK, newset, oldset).  */
+	leaq	oSIGMASK(%rdi), %rdx
+	leaq	oSIGMASK(%rsi), %rsi
+	movl	$SIG_SETMASK, %edi
+	call	HIDDEN_JUMPTARGET (__sigprocmask)
+	test	%rax, %rax
+	jne	L(pseudo_end)
+
+	/* Restore destroyed register into RDX. The choice is arbitrary,
+	   but leaving RDI and RSI available for use later can avoid
+	   shuffling values.  */
+	movq	%r12, %rdx
+
+	/* Restore the floating-point context.  Not the registers, only the
+	   rest.  */
+	movq	oFPREGS(%rdx), %rcx
+	fldenv	(%rcx)
+	ldmxcsr oMXCSR(%rdx)
+
+	/* Load the new stack pointer and the preserved registers.  */
+	movq	oRSP(%rdx), %rsp
+	movq	oRBX(%rdx), %rbx
+	movq	oRBP(%rdx), %rbp
+	movq	oR12(%rdx), %r12
+	movq	oR13(%rdx), %r13
+	movq	oR14(%rdx), %r14
+	movq	oR15(%rdx), %r15
+
+	/* The following ret should return to the address set with
+	getcontext.  Therefore push the address on the stack.  */
+	movq	oRIP(%rdx), %rcx
+	pushq	%rcx
+
+	/* Setup registers used for passing args.  */
+	movq	oRDI(%rdx), %rdi
+	movq	oRSI(%rdx), %rsi
+	movq	oRCX(%rdx), %rcx
+	movq	oR8(%rdx), %r8
+	movq	oR9(%rdx), %r9
+
+	/* Setup finally %rdx.  */
+	movq	oRDX(%rdx), %rdx
+
+	/* Clear rax to indicate success.  */
+	xorl	%eax, %eax
+L(pseudo_end):
+	ret
+PSEUDO_END(__swapcontext)
+
+weak_alias (__swapcontext, swapcontext)