For i386, add code to restore %gs from the signal context in
[kopensolaris-gnu/glibc.git] / linuxthreads / signals.c
1 /* Linuxthreads - a simple clone()-based implementation of Posix        */
2 /* threads for Linux.                                                   */
3 /* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr)              */
4 /*                                                                      */
5 /* This program is free software; you can redistribute it and/or        */
6 /* modify it under the terms of the GNU Library General Public License  */
7 /* as published by the Free Software Foundation; either version 2       */
8 /* of the License, or (at your option) any later version.               */
9 /*                                                                      */
10 /* This program is distributed in the hope that it will be useful,      */
11 /* but WITHOUT ANY WARRANTY; without even the implied warranty of       */
12 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        */
13 /* GNU Library General Public License for more details.                 */
14
15 /* Handling of signals */
16
17 #include <errno.h>
18 #include <signal.h>
19 #include "pthread.h"
20 #include "internals.h"
21 #include "spinlock.h"
22 #include <ucontext.h>
23 #include <sigcontextinfo.h>
24
25 int pthread_sigmask(int how, const sigset_t * newmask, sigset_t * oldmask)
26 {
27   sigset_t mask;
28
29   if (newmask != NULL) {
30     mask = *newmask;
31     /* Don't allow __pthread_sig_restart to be unmasked.
32        Don't allow __pthread_sig_cancel to be masked. */
33     switch(how) {
34     case SIG_SETMASK:
35       sigaddset(&mask, __pthread_sig_restart);
36       sigdelset(&mask, __pthread_sig_cancel);
37       break;
38     case SIG_BLOCK:
39       sigdelset(&mask, __pthread_sig_cancel);
40       break;
41     case SIG_UNBLOCK:
42       sigdelset(&mask, __pthread_sig_restart);
43       break;
44     }
45     newmask = &mask;
46   }
47   if (sigprocmask(how, newmask, oldmask) == -1)
48     return errno;
49   else
50     return 0;
51 }
52
53 int pthread_kill(pthread_t thread, int signo)
54 {
55   pthread_handle handle = thread_handle(thread);
56   int pid;
57
58   __pthread_lock(&handle->h_lock, NULL);
59   if (invalid_handle(handle, thread)) {
60     __pthread_unlock(&handle->h_lock);
61     return ESRCH;
62   }
63   pid = handle->h_descr->p_pid;
64   __pthread_unlock(&handle->h_lock);
65   if (kill(pid, signo) == -1)
66     return errno;
67   else
68     return 0;
69 }
70
71 /* User-provided signal handlers */
72 typedef void (*arch_sighandler_t) __PMT ((int, SIGCONTEXT));
73 static union
74 {
75   arch_sighandler_t old;
76   void (*rt) (int, struct siginfo *, struct ucontext *);
77 } sighandler[NSIG];
78
79 /* The wrapper around user-provided signal handlers */
80 static void pthread_sighandler(int signo, SIGCONTEXT ctx)
81 {
82   pthread_descr self;
83   char * in_sighandler;
84 #ifdef __i386__
85   asm volatile ("movw %w0,%%gs" : : "r" (ctx.gs));
86 #endif
87   self = thread_self();
88   /* If we're in a sigwait operation, just record the signal received
89      and return without calling the user's handler */
90   if (THREAD_GETMEM(self, p_sigwaiting)) {
91     THREAD_SETMEM(self, p_sigwaiting, 0);
92     THREAD_SETMEM(self, p_signal, signo);
93     return;
94   }
95   /* Record that we're in a signal handler and call the user's
96      handler function */
97   in_sighandler = THREAD_GETMEM(self, p_in_sighandler);
98   if (in_sighandler == NULL)
99     THREAD_SETMEM(self, p_in_sighandler, CURRENT_STACK_FRAME);
100   sighandler[signo].old(signo, SIGCONTEXT_EXTRA_ARGS ctx);
101   if (in_sighandler == NULL)
102     THREAD_SETMEM(self, p_in_sighandler, NULL);
103 }
104
105 /* The same, this time for real-time signals.  */
106 static void pthread_sighandler_rt(int signo, struct siginfo *si,
107                                   struct ucontext *uc)
108 {
109   pthread_descr self;
110   char * in_sighandler;
111 #ifdef __i386__
112   asm volatile ("movw %w0,%%gs" : : "r" (uc->uc_mcontext.gregs[GS]));
113 #endif
114   self =  thread_self();
115   /* If we're in a sigwait operation, just record the signal received
116      and return without calling the user's handler */
117   if (THREAD_GETMEM(self, p_sigwaiting)) {
118     THREAD_SETMEM(self, p_sigwaiting, 0);
119     THREAD_SETMEM(self, p_signal, signo);
120     return;
121   }
122   /* Record that we're in a signal handler and call the user's
123      handler function */
124   in_sighandler = THREAD_GETMEM(self, p_in_sighandler);
125   if (in_sighandler == NULL)
126     THREAD_SETMEM(self, p_in_sighandler, CURRENT_STACK_FRAME);
127   sighandler[signo].rt(signo, si, uc);
128   if (in_sighandler == NULL)
129     THREAD_SETMEM(self, p_in_sighandler, NULL);
130 }
131
132 /* The wrapper around sigaction.  Install our own signal handler
133    around the signal. */
134 int sigaction(int sig, const struct sigaction * act,
135               struct sigaction * oact)
136 {
137   struct sigaction newact;
138   struct sigaction *newactp;
139
140   if (sig == __pthread_sig_restart ||
141       sig == __pthread_sig_cancel ||
142       (sig == __pthread_sig_debug && __pthread_sig_debug > 0))
143     return EINVAL;
144   if (act)
145     {
146       newact = *act;
147       if (act->sa_handler != SIG_IGN && act->sa_handler != SIG_DFL
148           && sig > 0 && sig < NSIG)
149         {
150           if (act->sa_flags & SA_SIGINFO)
151             newact.sa_handler = (__sighandler_t) pthread_sighandler_rt;
152           else
153             newact.sa_handler = (__sighandler_t) pthread_sighandler;
154         }
155       newactp = &newact;
156     }
157   else
158     newactp = NULL;
159   if (__sigaction(sig, newactp, oact) == -1)
160     return -1;
161   if (sig > 0 && sig < NSIG)
162     {
163       if (oact != NULL)
164         oact->sa_handler = (__sighandler_t) sighandler[sig].old;
165       if (act)
166         /* For the assignment is does not matter whether it's a normal
167            or real-time signal.  */
168         sighandler[sig].old = (arch_sighandler_t) act->sa_handler;
169     }
170   return 0;
171 }
172
173 /* A signal handler that does nothing */
174 static void pthread_null_sighandler(int sig) { }
175
176 /* sigwait -- synchronously wait for a signal */
177 int sigwait(const sigset_t * set, int * sig)
178 {
179   volatile pthread_descr self = thread_self();
180   sigset_t mask;
181   int s;
182   sigjmp_buf jmpbuf;
183   struct sigaction sa;
184
185   /* Get ready to block all signals except those in set
186      and the cancellation signal.
187      Also check that handlers are installed on all signals in set,
188      and if not, install our dummy handler.  This is conformant to
189      POSIX: "The effect of sigwait() on the signal actions for the
190      signals in set is unspecified." */
191   sigfillset(&mask);
192   sigdelset(&mask, __pthread_sig_cancel);
193   for (s = 1; s <= NSIG; s++) {
194     if (sigismember(set, s) &&
195         s != __pthread_sig_restart &&
196         s != __pthread_sig_cancel &&
197         s != __pthread_sig_debug) {
198       sigdelset(&mask, s);
199       if (sighandler[s].old == NULL ||
200           sighandler[s].old == (arch_sighandler_t) SIG_DFL ||
201           sighandler[s].old == (arch_sighandler_t) SIG_IGN) {
202         sa.sa_handler = pthread_null_sighandler;
203         sigemptyset(&sa.sa_mask);
204         sa.sa_flags = 0;
205         sigaction(s, &sa, NULL);
206       }
207     }
208   }
209   /* Test for cancellation */
210   if (sigsetjmp(jmpbuf, 1) == 0) {
211     THREAD_SETMEM(self, p_cancel_jmp, &jmpbuf);
212     if (! (THREAD_GETMEM(self, p_canceled)
213            && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE)) {
214       /* Reset the signal count */
215       THREAD_SETMEM(self, p_signal, 0);
216       /* Say we're in sigwait */
217       THREAD_SETMEM(self, p_sigwaiting, 1);
218       /* Unblock the signals and wait for them */
219       sigsuspend(&mask);
220     }
221   }
222   THREAD_SETMEM(self, p_cancel_jmp, NULL);
223   /* The signals are now reblocked.  Check for cancellation */
224   pthread_testcancel();
225   /* We should have self->p_signal != 0 and equal to the signal received */
226   *sig = THREAD_GETMEM(self, p_signal);
227   return 0;
228 }
229
230 /* Redefine raise() to send signal to calling thread only,
231    as per POSIX 1003.1c */
232 int raise (int sig)
233 {
234   int retcode = pthread_kill(pthread_self(), sig);
235   if (retcode == 0)
236     return 0;
237   else {
238     errno = retcode;
239     return -1;
240   }
241 }