bug-hurd
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH v2 17/20] hurd: Add an AArch64 signal implementation


From: Sergey Bugaev
Subject: [PATCH v2 17/20] hurd: Add an AArch64 signal implementation
Date: Sat, 23 Mar 2024 20:32:58 +0300

Signed-off-by: Sergey Bugaev <bugaevc@gmail.com>
---
 sysdeps/mach/hurd/aarch64/Makefile          |   4 +
 sysdeps/mach/hurd/aarch64/bits/sigcontext.h |  96 ++++++
 sysdeps/mach/hurd/aarch64/exc2signal.c      | 149 +++++++++
 sysdeps/mach/hurd/aarch64/intr-msg.h        | 123 ++++++++
 sysdeps/mach/hurd/aarch64/sigreturn.c       | 127 ++++++++
 sysdeps/mach/hurd/aarch64/trampoline.c      | 325 ++++++++++++++++++++
 6 files changed, 824 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/sigreturn.c
 create mode 100644 sysdeps/mach/hurd/aarch64/trampoline.c

diff --git a/sysdeps/mach/hurd/aarch64/Makefile 
b/sysdeps/mach/hurd/aarch64/Makefile
index 9210d436..6cc831d6 100644
--- a/sysdeps/mach/hurd/aarch64/Makefile
+++ b/sysdeps/mach/hurd/aarch64/Makefile
@@ -22,3 +22,7 @@ endif
 ifeq ($(subdir),setjmp)
 gen-as-const-headers += signal-defines.sym
 endif
+
+ifeq ($(subdir),signal)
+CFLAGS-sigreturn.c += -mgeneral-regs-only
+endif
diff --git a/sysdeps/mach/hurd/aarch64/bits/sigcontext.h 
b/sysdeps/mach/hurd/aarch64/bits/sigcontext.h
new file mode 100644
index 00000000..163523fa
--- /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;
+    long sc_cpsr;
+
+#define sc_aarch64_float_state sc_v[0]
+    __int128_t sc_v[32];
+    long sc_fpsr;
+    long 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..7027bbf7
--- /dev/null
+++ b/sysdeps/mach/hurd/aarch64/exc2signal.c
@@ -0,0 +1,149 @@
+/* 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;
+
+       case EXC_AARCH64_MTE:
+         *signo = SIGSEGV;
+         /* TODO: Should be SEGV_MTESERR */
+         detail->code = posix ? SEGV_ACCERR : detail->exc_subcode;
+         break;
+
+       case EXC_AARCH64_BTI:
+         *signo = SIGILL;
+         /* XXX: Consider adopting ILL_BTCFI from OpenBSD.  */
+         /* XXX: exc_subcode / signal code contains BTYPE */
+         detail->code = posix ? ILL_ILLOPN : detail->exc_subcode;
+         break;
+
+       case EXC_AARCH64_AL:
+       case EXC_AARCH64_AL_PC:
+       case EXC_AARCH64_AL_SP:
+         *signo = SIGBUS;
+         detail->code = posix ? BUS_ADRALN : 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;
+      switch (detail->exc_code)
+        {
+        case EXC_AARCH64_SVC:
+          detail->code = posix ? ILL_ILLTRP : 0;
+          break;
+
+       default:
+         detail->code = posix ? ILL_ILLOPC : 0;
+         break;
+        }
+      break;
+
+    case EXC_ARITHMETIC:
+      *signo = SIGFPE;
+      switch (detail->exc_code)
+       {
+       case EXC_AARCH64_IDF:
+         detail->code = posix ? FPE_FLTIDO : 0;
+         break;
+       case EXC_AARCH64_IXF:
+         detail->code = posix ? FPE_FLTRES : FPE_FLTINX_FAULT;
+         break;
+       case EXC_AARCH64_UFF:
+         detail->code = posix ? FPE_FLTUND : FPE_FLTDNR_FAULT;
+         break;
+       case EXC_AARCH64_OFF:
+         detail->code = posix ? FPE_FLTOVF : FPE_FLTOVF_FAULT;
+         break;
+       case EXC_AARCH64_DZF:
+         detail->code = posix ? FPE_FLTDIV : FPE_FLTDIV_FAULT;
+         break;
+       case EXC_AARCH64_IOF:
+         /* 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..b511d7d7
--- /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/sigreturn.c 
b/sysdeps/mach/hurd/aarch64/sigreturn.c
new file mode 100644
index 00000000..c58b9c49
--- /dev/null
+++ b/sysdeps/mach/hurd/aarch64/sigreturn.c
@@ -0,0 +1,127 @@
+/* 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 <hurd/msg.h>
+#include <stdlib.h>
+
+/* This is run on the thread stack after restoring it, to be able to
+   unlock SS off sigstack.  */
+void
+__sigreturn2 (struct sigcontext *scp,
+             struct hurd_sigstate *ss)
+{
+  error_t err;
+  mach_port_t reply_port;
+  _hurd_sigstate_unlock (ss);
+
+  /* Destroy the MiG reply port used by the signal handler, and restore the
+     reply port in use by the thread when interrupted.
+
+     We cannot use the original reply port for our RPCs that we do here, since
+     we could unexpectedly receive/consume a reply message meant for the user
+     (in particular, msg_sig_post_reply), and also since we would deallocate
+     the port if *our* RPC fails, which we don't want to do since the user
+     still has the old name.  And so, temporarily set MACH_PORT_DEAD as our
+     reply name, and make sure destroying the port is the very last RPC we
+     do.  */
+  reply_port = THREAD_GETMEM (THREAD_SELF, reply_port);
+  THREAD_SETMEM (THREAD_SELF, reply_port, MACH_PORT_DEAD);
+  if (__glibc_likely (MACH_PORT_VALID (reply_port)))
+    (void) __mach_port_mod_refs (__mach_task_self (), reply_port,
+                                 MACH_PORT_RIGHT_RECEIVE, -1);
+  THREAD_SETMEM (THREAD_SELF, reply_port, scp->sc_reply_port);
+
+  /* Restore thread state.  */
+  err = __thread_set_self_state (AARCH64_FLOAT_STATE,
+                                (thread_state_t) &scp->sc_aarch64_float_state,
+                                AARCH64_FLOAT_STATE_COUNT);
+  assert_perror (err);
+  err = __thread_set_self_state (AARCH64_THREAD_STATE,
+                                (thread_state_t) &scp->sc_aarch64_thread_state,
+                                AARCH64_THREAD_STATE_COUNT);
+  assert_perror (err);
+  __builtin_unreachable ();
+}
+
+int
+__sigreturn (struct sigcontext *scp)
+{
+  struct hurd_sigstate *ss;
+  struct hurd_userlink *link = (void *) &scp[1];
+  uintptr_t usp;
+
+  if (__glibc_unlikely (scp == NULL || (scp->sc_mask & _SIG_CANT_MASK)))
+    return __hurd_fail (EINVAL);
+
+  ss = _hurd_self_sigstate ();
+  _hurd_sigstate_lock (ss);
+
+  /* Remove the link on the `active resources' chain added by
+     _hurd_setup_sighandler.  Its purpose was to make sure
+     that we got called; now we have, it is done.  */
+  _hurd_userlink_unlink (link);
+
+  /* Restore the set of blocked signals, and the intr_port slot.  */
+  ss->blocked = scp->sc_mask;
+  ss->intr_port = scp->sc_intr_port;
+
+  /* Check for pending signals that were blocked by the old set.  */
+  if (_hurd_sigstate_pending (ss) & ~ss->blocked)
+    {
+      /* There are pending signals that just became unblocked.  Wake up the
+        signal thread to deliver them.  But first, squirrel away SCP where
+        the signal thread will notice it if it runs another handler, and
+        arrange to have us called over again in the new reality.  */
+      ss->context = scp;
+      _hurd_sigstate_unlock (ss);
+      __msg_sig_post (_hurd_msgport, 0, 0, __mach_task_self ());
+      /* If a pending signal was handled, sig_post never returned.
+        If it did return, the pending signal didn't run a handler;
+        proceed as usual.  */
+      _hurd_sigstate_lock (ss);
+      ss->context = NULL;
+    }
+
+  if (scp->sc_onstack)
+    ss->sigaltstack.ss_flags &= ~SS_ONSTACK;
+
+  /* Copy the signal context onto the user's stack, to be able to release the
+     altstack (by unlocking sigstate).  Note that unless an altstack is used,
+     the sigcontext will itself be located on the user's stack, so we may well
+     be overwriting it here (or later in __sigreturn2).  */
+
+  usp = ALIGN_DOWN(scp->sc_sp - sizeof (struct sigcontext), 16);
+  memmove ((void *) usp, scp, sizeof (struct sigcontext));
+
+  /* Switch to the user's stack that we have just prepared, and call
+     __sigreturn2.  Clobber "memory" to make sure GCC flushes the stack
+     setup to actual memory.  */
+  {
+    register uintptr_t usp_x0 asm("x0") = usp;
+    register struct hurd_sigstate *ss_x1 asm("x1") = ss;
+
+    asm volatile ("mov sp, %0\n"
+                 "b __sigreturn2"
+                 :: "r" (usp_x0), "r" (ss_x1)
+                 : "memory");
+    __builtin_unreachable ();
+  }
+}
+
+weak_alias (__sigreturn, sigreturn)
diff --git a/sysdeps/mach/hurd/aarch64/trampoline.c 
b/sysdeps/mach/hurd/aarch64/trampoline.c
new file mode 100644
index 00000000..4b301335
--- /dev/null
+++ b/sysdeps/mach/hurd/aarch64/trampoline.c
@@ -0,0 +1,325 @@
+/* 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 <assert.h>
+#include <errno.h>
+#include "hurdfault.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;
+
+  memcpy (&uc->uc_mcontext.gregs, &sc->sc_aarch64_thread_state,
+         sizeof (struct aarch64_thread_state));
+  memcpy (&uc->uc_mcontext.fpregs, &sc->sc_aarch64_float_state,
+         sizeof (struct aarch64_float_state));
+}
+
+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;
+
+      /* NB: sigreturn assumes link is next to ctx.  */
+      struct sigcontext ctx;
+      struct hurd_userlink link;
+      ucontext_t ucontext;
+      siginfo_t siginfo;
+    } *stackframe;
+
+  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 = (void *) state->basic.sp;
+
+  /* Push the arguments to call `trampoline' on the stack.
+     Note that user's SP doesn't strictly have to be 16-aligned, since
+     AArch64 only requires 16-alignment for the stack pointer when
+     making accesses relative to it.  */
+  sigsp = PTR_ALIGN_DOWN (sigsp - sizeof (*stackframe), 16);
+  stackframe = sigsp;
+
+  _Static_assert ((void *) (&stackframe->_pad + 1) == (void *) 
&stackframe->ctx,
+                 "trampoline expects no extra padding between "
+                 "_pad and ctx");
+
+  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;
+       }
+
+      _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;
+
+  /* Clear pstate, notably reset BTYPE to 0.  */
+  state->basic.cpsr = 0;
+
+  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"
+     /* Call handler (signo, sigcode, scp).  Note that this is an indirect
+        call not using x16/x17, so this requires the signal handler to start
+        with a proper "bti c" marker.  */
+     "blr x20\n"
+     /* Call __sigreturn (), passing &CTX as SCP.  CTX is on the top of
+        the stack.  If __sigreturn () fails, we're in trouble.  */
+     "mov x0, sp\n"
+     "bl __sigreturn\n"
+     "udf #0xdead");
-- 
2.44.0




reply via email to

[Prev in Thread] Current Thread [Next in Thread]