1 | /* Copyright (C) 1991-2022 Free Software Foundation, Inc. |
2 | This file is part of the GNU C Library. |
3 | |
4 | The GNU C Library is free software; you can redistribute it and/or |
5 | modify it under the terms of the GNU Lesser General Public |
6 | License as published by the Free Software Foundation; either |
7 | version 2.1 of the License, or (at your option) any later version. |
8 | |
9 | The GNU C Library is distributed in the hope that it will be useful, |
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | Lesser General Public License for more details. |
13 | |
14 | You should have received a copy of the GNU Lesser General Public |
15 | License along with the GNU C Library; if not, see |
16 | <https://www.gnu.org/licenses/>. */ |
17 | |
18 | register int *sp asm ("%esp" ); |
19 | |
20 | #include <hurd.h> |
21 | #include <hurd/signal.h> |
22 | #include <hurd/threadvar.h> |
23 | #include <hurd/msg.h> |
24 | #include <stdlib.h> |
25 | #include <string.h> |
26 | |
27 | /* This is run on the thread stack after restoring it, to be able to |
28 | unlock SS off sigstack. */ |
29 | static void |
30 | __sigreturn2 (int *usp) |
31 | { |
32 | struct hurd_sigstate *ss = _hurd_self_sigstate (); |
33 | _hurd_sigstate_unlock (ss); |
34 | |
35 | sp = usp; |
36 | #define A(line) asm volatile (#line) |
37 | /* The members in the sigcontext are arranged in this order |
38 | so we can pop them easily. */ |
39 | |
40 | /* Pop the segment registers (except %cs and %ss, done last). */ |
41 | A (popl %gs); |
42 | A (popl %fs); |
43 | A (popl %es); |
44 | A (popl %ds); |
45 | /* Pop the general registers. */ |
46 | A (popa); |
47 | /* Pop the processor flags. */ |
48 | A (popf); |
49 | /* Return to the saved PC. */ |
50 | A (ret); |
51 | |
52 | /* Firewall. */ |
53 | A (hlt); |
54 | #undef A |
55 | } |
56 | |
57 | int |
58 | __sigreturn (struct sigcontext *scp) |
59 | { |
60 | struct hurd_sigstate *ss; |
61 | struct hurd_userlink *link = (void *) &scp[1]; |
62 | mach_port_t *reply_port; |
63 | |
64 | if (scp == NULL || (scp->sc_mask & _SIG_CANT_MASK)) |
65 | { |
66 | errno = EINVAL; |
67 | return -1; |
68 | } |
69 | |
70 | ss = _hurd_self_sigstate (); |
71 | _hurd_sigstate_lock (ss); |
72 | |
73 | /* Remove the link on the `active resources' chain added by |
74 | _hurd_setup_sighandler. Its purpose was to make sure |
75 | that we got called; now we have, it is done. */ |
76 | _hurd_userlink_unlink (link); |
77 | |
78 | /* Restore the set of blocked signals, and the intr_port slot. */ |
79 | ss->blocked = scp->sc_mask; |
80 | ss->intr_port = scp->sc_intr_port; |
81 | |
82 | /* Check for pending signals that were blocked by the old set. */ |
83 | if (_hurd_sigstate_pending (ss) & ~ss->blocked) |
84 | { |
85 | /* There are pending signals that just became unblocked. Wake up the |
86 | signal thread to deliver them. But first, squirrel away SCP where |
87 | the signal thread will notice it if it runs another handler, and |
88 | arrange to have us called over again in the new reality. */ |
89 | ss->context = scp; |
90 | _hurd_sigstate_unlock (ss); |
91 | __msg_sig_post (_hurd_msgport, 0, 0, __mach_task_self ()); |
92 | /* If a pending signal was handled, sig_post never returned. |
93 | If it did return, the pending signal didn't run a handler; |
94 | proceed as usual. */ |
95 | _hurd_sigstate_lock (ss); |
96 | ss->context = NULL; |
97 | } |
98 | |
99 | if (scp->sc_onstack) |
100 | ss->sigaltstack.ss_flags &= ~SS_ONSTACK; |
101 | |
102 | /* Destroy the MiG reply port used by the signal handler, and restore the |
103 | reply port in use by the thread when interrupted. */ |
104 | reply_port = &__hurd_local_reply_port; |
105 | if (*reply_port) |
106 | { |
107 | mach_port_t port = *reply_port; |
108 | |
109 | /* Assigning MACH_PORT_DEAD here tells libc's mig_get_reply_port not to |
110 | get another reply port, but avoids mig_dealloc_reply_port trying to |
111 | deallocate it after the receive fails (which it will, because the |
112 | reply port will be bogus, whether we do this or not). */ |
113 | *reply_port = MACH_PORT_DEAD; |
114 | |
115 | __mach_port_destroy (__mach_task_self (), port); |
116 | } |
117 | *reply_port = scp->sc_reply_port; |
118 | |
119 | if (scp->sc_fpused) |
120 | /* Restore the FPU state. Mach conveniently stores the state |
121 | in the format the i387 `frstor' instruction uses to restore it. */ |
122 | asm volatile ("frstor %0" : : "m" (scp->sc_fpsave)); |
123 | |
124 | { |
125 | /* There are convenient instructions to pop state off the stack, so we |
126 | copy the registers onto the user's stack, switch there, pop and |
127 | return. */ |
128 | |
129 | int *usp = (int *) scp->sc_uesp; |
130 | |
131 | *--usp = scp->sc_eip; |
132 | *--usp = scp->sc_efl; |
133 | memcpy (usp -= 12, &scp->sc_i386_thread_state, 12 * sizeof (int)); |
134 | |
135 | /* Pass usp to __sigreturn2 so it can unwind itself easily. */ |
136 | *(usp-1) = (int) usp; |
137 | --usp; |
138 | /* Bogus return address for __sigreturn2 */ |
139 | *--usp = 0; |
140 | *--usp = (int) __sigreturn2; |
141 | |
142 | /* Restore thread stack */ |
143 | sp = usp; |
144 | /* Return into __sigreturn2. */ |
145 | asm volatile ("ret" ); |
146 | /* Firewall. */ |
147 | asm volatile ("hlt" ); |
148 | } |
149 | |
150 | /* NOTREACHED */ |
151 | return -1; |
152 | } |
153 | |
154 | weak_alias (__sigreturn, sigreturn) |
155 | |