[RFC,20/23] hurd: Add an AArch64 signal implementation

Message ID 20240103171502.1358371-21-bugaevc@gmail.com
State Superseded
Headers
Series aarch64-gnu port |

Checks

Context Check Description
redhat-pt-bot/TryBot-apply_patch success Patch applied to master at the time it was sent
linaro-tcwg-bot/tcwg_glibc_build--master-arm fail Patch failed to apply

Commit Message

Sergey Bugaev Jan. 3, 2024, 5:14 p.m. UTC
  Signed-off-by: Sergey Bugaev <bugaevc@gmail.com>
---

This is somewhat complete, but untested. Notably absent is a
sigreturn () implementation, which would depend on whether we manage to
make thread_set_state () able to operate on the calling thread.

I didn't bother too much with exception2signal () because the details of
which exceptions we're going to be able to detect (and how to map them
properly) are not quite clear yet.

 sysdeps/mach/hurd/aarch64/bits/sigcontext.h |  96 ++++++
 sysdeps/mach/hurd/aarch64/exc2signal.c      | 119 +++++++
 sysdeps/mach/hurd/aarch64/intr-msg.h        | 123 ++++++++
 sysdeps/mach/hurd/aarch64/trampoline.c      | 327 ++++++++++++++++++++
 4 files changed, 665 insertions(+)
 create mode 100644 sysdeps/mach/hurd/aarch64/bits/sigcontext.h
 create mode 100644 sysdeps/mach/hurd/aarch64/exc2signal.c
 create mode 100644 sysdeps/mach/hurd/aarch64/intr-msg.h
 create mode 100644 sysdeps/mach/hurd/aarch64/trampoline.c
  

Patch

diff --git a/sysdeps/mach/hurd/aarch64/bits/sigcontext.h b/sysdeps/mach/hurd/aarch64/bits/sigcontext.h
new file mode 100644
index 00000000..1e3b8b71
--- /dev/null
+++ b/sysdeps/mach/hurd/aarch64/bits/sigcontext.h
@@ -0,0 +1,96 @@ 
+/* Machine-dependent signal context structure for GNU Hurd.  aarch64 version.
+   Copyright (C) 1991-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/>.  */
+
+#ifndef _BITS_SIGCONTEXT_H
+#define _BITS_SIGCONTEXT_H 1
+
+#if !defined _SIGNAL_H && !defined _SYS_UCONTEXT_H
+# error "Never use <bits/sigcontext.h> directly; include <signal.h> instead."
+#endif
+
+/* Signal handlers are actually called:
+   void handler (int sig, int code, struct sigcontext *scp);  */
+
+#include <bits/types/__sigset_t.h>
+#include <mach/machine/fp_reg.h>
+
+/* State of this thread when the signal was taken.  */
+struct sigcontext
+  {
+    /* These first members are machine-independent.  */
+
+    int sc_onstack;		/* Nonzero if running on sigstack.  */
+    __sigset_t sc_mask;		/* Blocked signals to restore.  */
+
+    /* MiG reply port this thread is using.  */
+    unsigned int sc_reply_port;
+
+    /* Port this thread is doing an interruptible RPC on.  */
+    unsigned int sc_intr_port;
+
+    /* Error code associated with this signal (interpreted as `error_t').  */
+    int sc_error;
+
+    /* Make sure the below members are properly aligned, and not packed
+       together with sc_error -- otherwise the layout won't match that of
+       aarch64_thread_state.  */
+    int sc_pad1;
+
+    /* All following members are machine-dependent.  The rest of this
+       structure is written to be laid out identically to:
+       {
+	 struct aarch64_thread_state basic;
+	 struct aarch64_float_state fpu;
+       }
+       trampoline.c knows this, so it must be changed if this changes.  */
+
+#define sc_aarch64_thread_state sc_x[0] /* Beginning of correspondence.  */
+    long sc_x[31];
+    long sc_sp;
+    long sc_pc;
+    long sc_tpidr_el0;
+    int sc_cpsr;
+
+#define sc_aarch64_float_state sc_v[0]
+    __int128_t sc_v[32];
+    int sc_fpsr;
+    int sc_fpcr;
+  };
+
+/* Traditional BSD names for some members.  */
+#define sc_fp	sc_x[29]	/* Frame pointer.  */
+#define sc_ps	sc_cpsr
+
+
+/* The deprecated sigcode values below are passed as an extra, non-portable
+   argument to regular signal handlers.  You should use SA_SIGINFO handlers
+   instead, which use the standard POSIX signal codes.  */
+
+/* Codes for SIGFPE.  */
+#define FPE_INTOVF_TRAP		0x1 /* integer overflow */
+#define FPE_INTDIV_FAULT	0x2 /* integer divide by zero */
+#define FPE_FLTOVF_FAULT	0x3 /* floating overflow */
+#define FPE_FLTDIV_FAULT	0x4 /* floating divide by zero */
+#define FPE_FLTUND_FAULT	0x5 /* floating underflow */
+#define FPE_SUBRNG_FAULT	0x7 /* BOUNDS instruction failed */
+#define FPE_FLTDNR_FAULT	0x8 /* denormalized operand */
+#define FPE_FLTINX_FAULT	0x9 /* floating loss of precision */
+#define FPE_EMERR_FAULT		0xa /* mysterious emulation error 33 */
+#define FPE_EMBND_FAULT		0xb /* emulation BOUNDS instruction failed */
+
+#endif /* bits/sigcontext.h */
diff --git a/sysdeps/mach/hurd/aarch64/exc2signal.c b/sysdeps/mach/hurd/aarch64/exc2signal.c
new file mode 100644
index 00000000..c260b496
--- /dev/null
+++ b/sysdeps/mach/hurd/aarch64/exc2signal.c
@@ -0,0 +1,119 @@ 
+/* Translate Mach exception codes into signal numbers.  aarch64 version.
+   Copyright (C) 1991-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 <hurd.h>
+#include <hurd/signal.h>
+#include <mach/exception.h>
+
+/* Translate the Mach exception codes, as received in an `exception_raise' RPC,
+   into a signal number and signal subcode.  */
+
+static void
+exception2signal (struct hurd_signal_detail *detail, int *signo, int posix)
+{
+  detail->error = 0;
+
+  switch (detail->exc)
+    {
+    default:
+      *signo = SIGIOT;
+      detail->code = detail->exc;
+      break;
+
+    case EXC_BAD_ACCESS:
+      switch (detail->exc_code)
+        {
+	case KERN_INVALID_ADDRESS:
+	case KERN_MEMORY_FAILURE:
+	  *signo = SIGSEGV;
+	  detail->code = posix ? SEGV_MAPERR : detail->exc_subcode;
+	  break;
+
+	case KERN_PROTECTION_FAILURE:
+	case KERN_WRITE_PROTECTION_FAILURE:
+	  *signo = SIGSEGV;
+	  detail->code = posix ? SEGV_ACCERR : detail->exc_subcode;
+	  break;
+
+	default:
+	  *signo = SIGBUS;
+	  detail->code = posix ? BUS_ADRERR : detail->exc_subcode;
+	  break;
+	}
+      detail->error = detail->exc_code;
+      break;
+
+    case EXC_BAD_INSTRUCTION:
+      *signo = SIGILL;
+      detail->code = posix ? ILL_ILLOPC : 0;
+      break;
+
+    case EXC_ARITHMETIC:
+      *signo = SIGFPE;
+      switch (detail->exc_code)
+	{
+	case EXC_AARCH64_FP_ID:
+	  detail->code = posix ? FPE_FLTIDO : 0;
+	case EXC_AARCH64_FP_IX:
+	  detail->code = posix ? FPE_FLTRES : FPE_FLTINX_FAULT;
+	  break;
+	case EXC_AARCH64_FP_UF:
+	  detail->code = posix ? FPE_FLTUND : FPE_FLTDNR_FAULT;
+	  break;
+	case EXC_AARCH64_FP_OF:
+	  detail->code = posix ? FPE_FLTOVF : FPE_FLTOVF_FAULT;
+	  break;
+	case EXC_AARCH64_FP_DZ:
+	  detail->code = posix ? FPE_FLTDIV : FPE_FLTDIV_FAULT;
+	  break;
+	case EXC_AARCH64_FP_IO:
+	  /* NB: We used to send SIGILL here but we can't distinguish
+	     POSIX vs. legacy with respect to what signal we send.  */
+	  detail->code = posix ? FPE_FLTINV : 0 /*ILL_FPEOPR_FAULT*/;
+	  break;
+	default:
+	  detail->code = 0;
+	}
+      break;
+
+    case EXC_EMULATION:
+    case EXC_SOFTWARE:
+      *signo = SIGEMT;
+      detail->code = 0;
+      break;
+
+
+    case EXC_BREAKPOINT:
+      *signo = SIGTRAP;
+      detail->code = posix ? TRAP_BRKPT : 0;
+      break;
+    }
+}
+libc_hidden_def (_hurd_exception2signal)
+
+void
+_hurd_exception2signal (struct hurd_signal_detail *detail, int *signo)
+{
+  exception2signal (detail, signo, 1);
+}
+
+void
+_hurd_exception2signal_legacy (struct hurd_signal_detail *detail, int *signo)
+{
+  exception2signal (detail, signo, 0);
+}
diff --git a/sysdeps/mach/hurd/aarch64/intr-msg.h b/sysdeps/mach/hurd/aarch64/intr-msg.h
new file mode 100644
index 00000000..fd2e9092
--- /dev/null
+++ b/sysdeps/mach/hurd/aarch64/intr-msg.h
@@ -0,0 +1,123 @@ 
+/* Machine-dependent details of interruptible RPC messaging.  aarch64 version.
+   Copyright (C) 1995-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/>.  */
+
+
+/* Note that we must mark OPTION and TIMEOUT as outputs of this operation,
+   to indicate that the signal thread might mutate them as part
+   of sending us to a signal handler.  */
+
+#define INTR_MSG_TRAP(msg, option, send_size, rcv_size, rcv_name, timeout, notify, cancel_p, intr_port_p) \
+({									      \
+  register uintptr_t /* error_t */ err asm ("x0");			      \
+  register uintptr_t option_x1 asm ("x1") = option;			      \
+  register uintptr_t send_size_x2 asm ("x2") = send_size;		      \
+  register uintptr_t rcv_size_x3 asm ("x3") = rcv_size;			      \
+  register uintptr_t rcv_name_x4 asm ("x4") = rcv_name;			      \
+  register uintptr_t timeout_x5 asm ("x5") = timeout;			      \
+  register uintptr_t notify_x6 asm ("x6") = notify;			      \
+  register void *msg_x9 asm ("x9") = msg;  /* used by trampoline.c */	      \
+  asm volatile ("\n"							      \
+       ".globl _hurd_intr_rpc_msg_about_to\n"				      \
+       ".globl _hurd_intr_rpc_msg_setup_done\n"				      \
+       ".globl _hurd_intr_rpc_msg_in_trap\n"				      \
+       /* Clear x0 before we do the check for cancel below.  This is to
+          detect x0 being set to non-zero (actually MACH_SEND_INTERRUPTED)
+          from the outside (namely, _hurdsig_abort_rpcs), which signals us
+          to skip the trap we were about to enter.  */			      \
+       "	mov %[err], #0\n"					      \
+       "_hurd_intr_rpc_msg_about_to:\n"					      \
+       /* We need to make a last check of cancel, in case we got interrupted
+          right before _hurd_intr_rpc_msg_about_to.  */			      \
+       "	ldr w8, %[cancel]\n"					      \
+       "	cbz w8, _hurd_intr_rpc_msg_do\n"			      \
+       /* We got interrupted, note so and return EINTR.  */		      \
+       "	str wzr, %[intr_port]\n"				      \
+       "	mov %[err], %[eintr_lo]\n"				      \
+       "	movk %[err], %[eintr_hi], lsl 16\n"			      \
+       "	b _hurd_intr_rpc_msg_sp_restored\n"			      \
+       "_hurd_intr_rpc_msg_do:\n"					      \
+       /* Ok, prepare the mach_msg_trap arguments.  We pass all the 7 args
+          in registers.  */						      \
+       "_hurd_intr_rpc_msg_setup_done:\n"				      \
+       /* From here on, it is safe to make us jump over the syscall.  Now
+          check if we have been told to skip the syscall while running
+          the above.  */						      \
+       "	cbnz %[err], _hurd_intr_rpc_msg_in_trap\n"		      \
+       /* Do the actual syscall.  */					      \
+       "	mov %[err], %[msg]\n"					      \
+       "	mov x8, #-25\n"						      \
+       "_hurd_intr_rpc_msg_do_trap:\n"					      \
+       "	svc #0\n" /* status in %[err] */			      \
+       "_hurd_intr_rpc_msg_in_trap:\n"					      \
+       "_hurd_intr_rpc_msg_sp_restored:\n"				      \
+       : [err] "=r" (err), "+r" (option_x1),				      \
+         [intr_port] "=m" (*intr_port_p),				      \
+         "+r" (timeout_x5)						      \
+       : [msg] "r" (msg_x9), "r" (send_size_x2), "r" (rcv_size_x3),	      \
+         "r" (rcv_name_x4), "r" (notify_x6),				      \
+         [cancel] "m" (*cancel_p),					      \
+         [eintr_lo] "i" (EINTR & 0xffff), [eintr_hi] "i" (EINTR >> 16));      \
+  option = option_x1;							      \
+  timeout = timeout_x5;							      \
+  err;									      \
+})
+
+#include "hurdfault.h"
+
+/* This cannot be an inline function because it calls setjmp.  */
+#define SYSCALL_EXAMINE(state, callno)					      \
+({									      \
+  int result;								      \
+  unsigned int *p = (unsigned int *) (state)->pc - 4;			      \
+  if (_hurdsig_catch_memory_fault (p))					      \
+    return 0;								      \
+  if (result = (*p == 0xd4000001))					      \
+    /* The PC is just after a "svc #0" instruction.
+       This is a system call in progress; x8 holds the call number.  */	      \
+    *(callno) = (state)->x[8];						      \
+  _hurdsig_end_catch_fault ();						      \
+  result;								      \
+})
+
+
+/* This cannot be an inline function because it calls setjmp.  */
+#define MSG_EXAMINE(state, msgid, rcvname, send_name, opt, tmout)	      \
+({									      \
+  int ret = 0;								      \
+  const struct machine_thread_state *s = (state);			      \
+  const mach_msg_header_t *msg = (const void *) s->x[0];		      \
+  *(opt) = s->x[1];							      \
+  *(rcvname) = s->x[4];							      \
+  *(tmout) = s->x[5];							      \
+  if (msg == 0)								      \
+    {									      \
+      *(send_name) = MACH_PORT_NULL;					      \
+      *(msgid) = 0;							      \
+    }									      \
+  else									      \
+    {									      \
+      ret = _hurdsig_catch_memory_fault (msg) ? -1 : 0;			      \
+      if (ret == 0)							      \
+        {								      \
+          *(send_name) = msg->msgh_remote_port;				      \
+          *(msgid) = msg->msgh_id;					      \
+          _hurdsig_end_catch_fault ();					      \
+        }								      \
+    }									      \
+  ret;									      \
+})
diff --git a/sysdeps/mach/hurd/aarch64/trampoline.c b/sysdeps/mach/hurd/aarch64/trampoline.c
new file mode 100644
index 00000000..864175c6
--- /dev/null
+++ b/sysdeps/mach/hurd/aarch64/trampoline.c
@@ -0,0 +1,327 @@ 
+/* Set thread_state for sighandler, and sigcontext to recover.  aarch64 version.
+   Copyright (C) 1994-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 <hurd/signal.h>
+#include <hurd/userlink.h>
+#include <thread_state.h>
+#include <mach/exception.h>
+//#include <mach/machine/eflags.h>
+#include <assert.h>
+#include <errno.h>
+#include "hurdfault.h"
+//#include <intr-msg.h>
+#include <sys/ucontext.h>
+
+
+/* Fill in a siginfo_t structure for SA_SIGINFO-enabled handlers.  */
+static void fill_siginfo (siginfo_t *si, int signo,
+			  const struct hurd_signal_detail *detail,
+			  const struct machine_thread_all_state *state)
+{
+  si->si_signo = signo;
+  si->si_errno = detail->error;
+  si->si_code = detail->code;
+
+  /* XXX We would need a protocol change for sig_post to include
+   * this information.  */
+  si->si_pid = -1;
+  si->si_uid = -1;
+
+  /* Address of the faulting instruction or memory access.  */
+  if (detail->exc == EXC_BAD_ACCESS)
+    si->si_addr = (void *) detail->exc_subcode;
+  else
+    si->si_addr = (void *) state->basic.pc;
+
+  /* XXX On SIGCHLD, this should be the exit status of the child
+   * process.  We would need a protocol change for the proc server
+   * to send this information along with the signal.  */
+  si->si_status = 0;
+
+  si->si_band = 0;              /* SIGPOLL is not supported yet.  */
+  si->si_value.sival_int = 0;   /* sigqueue() is not supported yet.  */
+}
+
+/* Fill in a ucontext_t structure SA_SIGINFO-enabled handlers.  */
+static void fill_ucontext (ucontext_t *uc, const struct sigcontext *sc)
+{
+  uc->uc_flags = 0;
+  uc->uc_link = NULL;
+  uc->uc_sigmask = sc->sc_mask;
+  uc->uc_stack.ss_sp = (__ptr_t) sc->sc_sp;
+  uc->uc_stack.ss_size = 0;
+  uc->uc_stack.ss_flags = 0;
+
+  /* Registers.  */
+  memcpy (&uc->uc_mcontext.gregs.x[0], &sc->sc_x[0],
+	  (const unsigned char *) &sc->sc_aarch64_float_state
+          - (const unsigned char *) &sc->sc_aarch64_thread_state);
+  /* XXX FPU state.  */
+  memset (&uc->uc_mcontext.fpregs, 0, sizeof (fpregset_t));
+}
+
+struct sigcontext *
+_hurd_setup_sighandler (struct hurd_sigstate *ss, const struct sigaction *action,
+			__sighandler_t handler,
+			int signo, struct hurd_signal_detail *detail,
+			int rpc_wait,
+			struct machine_thread_all_state *state)
+{
+  void trampoline (void) attribute_hidden;
+  void rpc_wait_trampoline (void) attribute_hidden;
+  void *sigsp;
+  struct sigcontext *scp;
+  struct
+    {
+      union
+        {
+          int signo;
+          /* Make sure signo takes up a pointer-sized slot on the stack.
+             (This should already be the case considering the siginfop
+             pointer below, but better be explicit.)  */
+          void *_pointer_sized;
+        };
+      union
+	{
+	  /* Extra arguments for traditional signal handlers */
+	  struct
+	    {
+	      long int sigcode;
+	      struct sigcontext *scp;       /* Points to ctx, below.  */
+	    } legacy;
+
+	  /* Extra arguments for SA_SIGINFO handlers */
+	  struct
+	    {
+	      siginfo_t *siginfop;          /* Points to siginfo, below.  */
+	      ucontext_t *uctxp;            /* Points to uctx, below.  */
+	    } posix;
+	};
+
+      void *_pad;
+      void *sigreturn_addr;
+      struct sigcontext *return_scp; /* Argument to sigreturn.  */
+
+      /* NB: sigreturn assumes link is next to ctx.  */
+      struct sigcontext ctx;
+      struct hurd_userlink link;
+      ucontext_t ucontext;
+      siginfo_t siginfo;
+    } *stackframe;
+
+  _Static_assert (offsetof (typeof (*stackframe), sigreturn_addr) % 16 == 0,
+                  "sigreturn_addr must be 16-byte aligned");
+  _Static_assert (sizeof (*stackframe) % 16 == 0,
+		  "stackframe size must be a multiple of 16");
+
+  if (ss->context)
+    {
+      /* We have a previous sigcontext that sigreturn was about
+	 to restore when another signal arrived.  We will just base
+	 our setup on that.  */
+      if (! _hurdsig_catch_memory_fault (ss->context))
+	{
+	  memcpy (&state->basic, &ss->context->sc_aarch64_thread_state,
+		  sizeof (state->basic));
+	  memcpy (&state->fpu, &ss->context->sc_aarch64_float_state,
+		  sizeof (state->fpu));
+	  state->set |= (1 << AARCH64_THREAD_STATE) | (1 << AARCH64_FLOAT_STATE);
+	}
+    }
+
+  if (! machine_get_basic_state (ss->thread, state))
+    return NULL;
+
+  if ((action->sa_flags & SA_ONSTACK)
+      && !(ss->sigaltstack.ss_flags & (SS_DISABLE|SS_ONSTACK)))
+    {
+      sigsp = ss->sigaltstack.ss_sp + ss->sigaltstack.ss_size;
+      ss->sigaltstack.ss_flags |= SS_ONSTACK;
+    }
+  else
+    /* No red zone on aarch64-gnu.  */
+    sigsp = (char *) state->basic.sp;
+
+  /* Push the arguments to call `trampoline' on the stack.  */
+  sigsp -= sizeof (*stackframe);
+  stackframe = sigsp;
+
+  if (_hurdsig_catch_memory_fault (stackframe))
+    {
+      /* We got a fault trying to write the stack frame.
+	 We cannot set up the signal handler.
+	 Returning NULL tells our caller, who will nuke us with a SIGILL.  */
+      return NULL;
+    }
+  else
+    {
+      int ok;
+
+      extern void _hurdsig_longjmp_from_handler (void *, jmp_buf, int);
+
+      /* Add a link to the thread's active-resources list.  We mark this as
+	 the only user of the "resource", so the cleanup function will be
+	 called by any longjmp which is unwinding past the signal frame.
+	 The cleanup function (in sigunwind.c) will make sure that all the
+	 appropriate cleanups done by sigreturn are taken care of.  */
+      stackframe->link.cleanup = &_hurdsig_longjmp_from_handler;
+      stackframe->link.cleanup_data = &stackframe->ctx;
+      stackframe->link.resource.next = NULL;
+      stackframe->link.resource.prevp = NULL;
+      stackframe->link.thread.next = ss->active_resources;
+      stackframe->link.thread.prevp = &ss->active_resources;
+      if (stackframe->link.thread.next)
+	stackframe->link.thread.next->thread.prevp
+	  = &stackframe->link.thread.next;
+      ss->active_resources = &stackframe->link;
+
+      /* Set up the sigcontext from the current state of the thread.  */
+
+      scp = &stackframe->ctx;
+      scp->sc_onstack = ss->sigaltstack.ss_flags & SS_ONSTACK ? 1 : 0;
+
+      /* struct sigcontext is laid out so that starting at sc_x[0] mimics a
+	 struct aarch64_thread_state.  */
+      _Static_assert (offsetof (struct sigcontext, sc_aarch64_thread_state)
+		      % __alignof__ (struct aarch64_thread_state) == 0,
+		      "sc_aarch64_thread_state layout mismatch");
+      memcpy (&scp->sc_aarch64_thread_state,
+	      &state->basic, sizeof (state->basic));
+
+      /* struct sigcontext is laid out so that starting at sc_v[0] mimics
+	 a struct aarch64_float_state.  */
+      _Static_assert (offsetof (struct sigcontext, sc_aarch64_float_state)
+		      % __alignof__ (struct aarch64_float_state) == 0,
+		      "sc_aarch64_float_state layout mismatch");
+      ok = machine_get_state (ss->thread, state, AARCH64_FLOAT_STATE,
+			      &state->fpu, &scp->sc_aarch64_float_state,
+			      sizeof (state->fpu));
+
+      /* Set up the arguments for the signal handler.  */
+      stackframe->signo = signo;
+      if (action->sa_flags & SA_SIGINFO)
+	{
+	  stackframe->posix.siginfop = &stackframe->siginfo;
+	  stackframe->posix.uctxp = &stackframe->ucontext;
+	  fill_siginfo (&stackframe->siginfo, signo, detail, state);
+	  fill_ucontext (&stackframe->ucontext, scp);
+	}
+      else
+	{
+	  if (detail->exc)
+	    {
+	      int nsigno;
+	      _hurd_exception2signal_legacy (detail, &nsigno);
+	      assert (nsigno == signo);
+	    }
+	  else
+	    detail->code = 0;
+
+	  stackframe->legacy.sigcode = detail->code;
+	  stackframe->legacy.scp = &stackframe->ctx;
+	}
+
+      /* Set up the bottom of the stack.  */
+      stackframe->sigreturn_addr = &__sigreturn;
+      stackframe->return_scp = &stackframe->ctx;
+
+      _hurdsig_end_catch_fault ();
+
+      if (! ok)
+	return NULL;
+    }
+
+  /* Modify the thread state to call the trampoline code on the new stack.  */
+  state->basic.sp = (uintptr_t) sigsp;
+
+  if (rpc_wait)
+    {
+      /* The signalee thread was blocked in a mach_msg_trap system call,
+         still waiting for a reply.  We will have it run the special
+         trampoline code which retries the message receive before running
+         the signal handler.
+
+         To do this we change the OPTION argument (in x1) to enable only
+         message reception, since the request message has already been
+         sent.  */
+
+      assert (state->basic.x[1] & MACH_RCV_MSG);
+      /* Disable the message-send, since it has already completed.  The
+         calls we retry need only wait to receive the reply message.  */
+      state->basic.x[1] &= ~MACH_SEND_MSG;
+
+      /* Limit the time to receive the reply message, in case the server
+         claimed that `interrupt_operation' succeeded but in fact the RPC
+         is hung.  */
+      state->basic.x[1] |= MACH_RCV_TIMEOUT;
+      state->basic.x[5] = _hurd_interrupted_rpc_timeout;
+
+      state->basic.pc = (uintptr_t) rpc_wait_trampoline;
+      /* After doing the message receive, the trampoline code will need to
+         update the x0 value to be restored by sigreturn.  To simplify
+         the assembly code, we pass the address of its slot in SCP to the
+         trampoline code in x21.  */
+      state->basic.x[21] = (uintptr_t) &scp->sc_x[0];
+    }
+  else
+    state->basic.pc = (uintptr_t) trampoline;
+
+  /* We pass the handler function to the trampoline code in x20.  */
+  state->basic.x[20] = (uintptr_t) handler;
+
+  return scp;
+}
+
+asm ("rpc_wait_trampoline:\n"
+  /* This is the entry point when we have an RPC reply message to receive
+     before running the handler.  The MACH_MSG_SEND bit has already been
+     cleared in the OPTION argument in our x1.  For our convenience, x21
+     points to the sc_x[0] member of the sigcontext.
+
+     When the sigcontext was saved, x0 was MACH_RCV_INTERRUPTED.  To repeat
+     the message call, we need to restore the original message buffer
+     pointer; intr-msg.h keeps a backup copy of it in x9 specifically for
+     this purpose.  */
+     "mov x0, x9\n"
+     "svc #0\n"
+     /* Now the message receive has completed and the original caller of
+        the RPC (i.e. the code running when the signal arrived) needs to
+        see the final return value of the message receive in x0.  So store
+        the new x0 value into the sc_x[0] member of the sigcontext (whose
+        address is in x21 to make this code simpler).  */
+     "str x0, [x21]\n"
+
+     "trampoline:\n"
+     /* Entry point for running the handler normally.  The arguments to the
+        handler function are on the top of the stack:
+
+        [sp]		SIGNO
+        [sp, #8]	SIGCODE
+        [sp, #16]	SCP
+        [sp, #24]	_pad
+
+        Pop them off to the registers, to pass as arguments to the handler.
+     */
+     "ldp x0, x1, [sp], 16\n"
+     "ldr x2, [sp], 16\n"
+     "blr x20\n"		/* Call handler (signo, sigcode, scp).  */
+     /* The word at the top of stack is &__sigreturn, following it is a copy
+        of SCP for __sigreturn's argument.  Load the SCP as for a call, and
+        "return" to calling __sigreturn (SCP); this call never returns.  */
+     "ldp x30, x0, [sp], 16\n"
+     "ret");