Revision 9de5e440 linux-user/signal.c
b/linux-user/signal.c | ||
---|---|---|
27 | 27 |
|
28 | 28 |
#include "gemu.h" |
29 | 29 |
|
30 |
#include "syscall_defs.h" |
|
31 |
|
|
32 |
#ifdef TARGET_I386 |
|
33 |
#include "cpu-i386.h" |
|
34 |
#include "syscall-i386.h" |
|
35 |
#endif |
|
36 |
|
|
37 | 30 |
/* signal handling inspired from em86. */ |
38 | 31 |
|
39 | 32 |
//#define DEBUG_SIGNAL |
... | ... | |
42 | 35 |
|
43 | 36 |
struct sigqueue { |
44 | 37 |
struct sigqueue *next; |
45 |
siginfo_t info; |
|
38 |
target_siginfo_t info;
|
|
46 | 39 |
}; |
47 | 40 |
|
48 | 41 |
struct emulated_sigaction { |
... | ... | |
101 | 94 |
*(unsigned long *)sigset = tswapl(*old_sigset); |
102 | 95 |
} |
103 | 96 |
|
104 |
/* XXX: finish it */ |
|
105 |
void host_to_target_siginfo(target_siginfo_t *tinfo, siginfo_t *info) |
|
97 |
/* siginfo conversion */ |
|
98 |
|
|
99 |
static inline void host_to_target_siginfo_noswap(target_siginfo_t *tinfo, |
|
100 |
const siginfo_t *info) |
|
106 | 101 |
{ |
107 |
tinfo->si_signo = tswap32(info->si_signo); |
|
102 |
int sig; |
|
103 |
sig = host_to_target_signal(info->si_signo); |
|
104 |
tinfo->si_signo = sig; |
|
105 |
tinfo->si_errno = 0; |
|
106 |
tinfo->si_code = 0; |
|
107 |
if (sig == SIGILL || sig == SIGFPE || sig == SIGSEGV || sig == SIGBUS) { |
|
108 |
/* should never come here, but who knows. The information for |
|
109 |
the target is irrelevant */ |
|
110 |
tinfo->_sifields._sigfault._addr = 0; |
|
111 |
} else if (sig >= TARGET_SIGRTMIN) { |
|
112 |
tinfo->_sifields._rt._pid = info->si_pid; |
|
113 |
tinfo->_sifields._rt._uid = info->si_uid; |
|
114 |
/* XXX: potential problem if 64 bit */ |
|
115 |
tinfo->_sifields._rt._sigval.sival_ptr = |
|
116 |
(target_ulong)info->si_value.sival_ptr; |
|
117 |
} |
|
118 |
} |
|
119 |
|
|
120 |
static void tswap_siginfo(target_siginfo_t *tinfo, |
|
121 |
const target_siginfo_t *info) |
|
122 |
{ |
|
123 |
int sig; |
|
124 |
sig = info->si_signo; |
|
125 |
tinfo->si_signo = tswap32(sig); |
|
108 | 126 |
tinfo->si_errno = tswap32(info->si_errno); |
109 | 127 |
tinfo->si_code = tswap32(info->si_code); |
128 |
if (sig == SIGILL || sig == SIGFPE || sig == SIGSEGV || sig == SIGBUS) { |
|
129 |
tinfo->_sifields._sigfault._addr = |
|
130 |
tswapl(info->_sifields._sigfault._addr); |
|
131 |
} else if (sig >= TARGET_SIGRTMIN) { |
|
132 |
tinfo->_sifields._rt._pid = tswap32(info->_sifields._rt._pid); |
|
133 |
tinfo->_sifields._rt._uid = tswap32(info->_sifields._rt._uid); |
|
134 |
tinfo->_sifields._rt._sigval.sival_ptr = |
|
135 |
tswapl(info->_sifields._rt._sigval.sival_ptr); |
|
136 |
} |
|
137 |
} |
|
138 |
|
|
139 |
|
|
140 |
void host_to_target_siginfo(target_siginfo_t *tinfo, const siginfo_t *info) |
|
141 |
{ |
|
142 |
host_to_target_siginfo_noswap(tinfo, info); |
|
143 |
tswap_siginfo(tinfo, tinfo); |
|
110 | 144 |
} |
111 | 145 |
|
112 |
/* XXX: finish it */ |
|
113 |
void target_to_host_siginfo(siginfo_t *info, target_siginfo_t *tinfo) |
|
146 |
/* XXX: we support only POSIX RT signals are used. */ |
|
147 |
/* XXX: find a solution for 64 bit (additionnal malloced data is needed) */ |
|
148 |
void target_to_host_siginfo(siginfo_t *info, const target_siginfo_t *tinfo) |
|
114 | 149 |
{ |
115 | 150 |
info->si_signo = tswap32(tinfo->si_signo); |
116 | 151 |
info->si_errno = tswap32(tinfo->si_errno); |
117 | 152 |
info->si_code = tswap32(tinfo->si_code); |
153 |
info->si_pid = tswap32(tinfo->_sifields._rt._pid); |
|
154 |
info->si_uid = tswap32(tinfo->_sifields._rt._uid); |
|
155 |
info->si_value.sival_ptr = |
|
156 |
(void *)tswapl(tinfo->_sifields._rt._sigval.sival_ptr); |
|
118 | 157 |
} |
119 | 158 |
|
120 | 159 |
void signal_init(void) |
... | ... | |
122 | 161 |
struct sigaction act; |
123 | 162 |
int i; |
124 | 163 |
|
125 |
/* set all host signal handlers */ |
|
126 |
sigemptyset(&act.sa_mask); |
|
164 |
/* set all host signal handlers. ALL signals are blocked during |
|
165 |
the handlers to serialize them. */ |
|
166 |
sigfillset(&act.sa_mask); |
|
127 | 167 |
act.sa_flags = SA_SIGINFO; |
128 | 168 |
act.sa_sigaction = host_signal_handler; |
129 | 169 |
for(i = 1; i < NSIG; i++) { |
... | ... | |
155 | 195 |
first_free = q; |
156 | 196 |
} |
157 | 197 |
|
158 |
static int queue_signal(struct emulated_sigaction *k, int sig, siginfo_t *info) |
|
159 |
{ |
|
160 |
struct sigqueue *q, **pq; |
|
161 |
|
|
162 |
pq = &k->first; |
|
163 |
if (!k->pending || sig < TARGET_SIGRTMIN) { |
|
164 |
/* first signal or non real time signal */ |
|
165 |
q = &k->info; |
|
166 |
} else { |
|
167 |
q = alloc_sigqueue(); |
|
168 |
if (!q) |
|
169 |
return -EAGAIN; |
|
170 |
while (*pq != NULL) |
|
171 |
pq = &(*pq)->next; |
|
172 |
} |
|
173 |
*pq = q; |
|
174 |
q->info = *info; |
|
175 |
q->next = NULL; |
|
176 |
k->pending = 1; |
|
177 |
/* signal that a new signal is pending */ |
|
178 |
signal_pending = 1; |
|
179 |
return 0; |
|
180 |
} |
|
181 |
|
|
182 |
void force_sig(int sig) |
|
198 |
/* abort execution with signal */ |
|
199 |
void __attribute((noreturn)) force_sig(int sig) |
|
183 | 200 |
{ |
184 | 201 |
int host_sig; |
185 |
/* abort execution with signal */ |
|
186 | 202 |
host_sig = target_to_host_signal(sig); |
187 | 203 |
fprintf(stderr, "gemu: uncaught target signal %d (%s) - exiting\n", |
188 | 204 |
sig, strsignal(host_sig)); |
205 |
#if 1 |
|
189 | 206 |
_exit(-host_sig); |
207 |
#else |
|
208 |
{ |
|
209 |
struct sigaction act; |
|
210 |
sigemptyset(&act.sa_mask); |
|
211 |
act.sa_flags = SA_SIGINFO; |
|
212 |
act.sa_sigaction = SIG_DFL; |
|
213 |
sigaction(SIGABRT, &act, NULL); |
|
214 |
abort(); |
|
215 |
} |
|
216 |
#endif |
|
190 | 217 |
} |
191 | 218 |
|
192 |
|
|
193 |
static void host_signal_handler(int host_signum, siginfo_t *info,
|
|
194 |
void *puc)
|
|
219 |
/* queue a signal so that it will be send to the virtual CPU as soon |
|
220 |
as possible */
|
|
221 |
int queue_signal(int sig, target_siginfo_t *info)
|
|
195 | 222 |
{ |
196 | 223 |
struct emulated_sigaction *k; |
197 |
int sig;
|
|
224 |
struct sigqueue *q, **pq;
|
|
198 | 225 |
target_ulong handler; |
199 | 226 |
|
200 |
/* get target signal number */ |
|
201 |
sig = host_to_target_signal(host_signum); |
|
202 |
if (sig < 1 || sig > TARGET_NSIG) |
|
203 |
return; |
|
204 |
k = &sigact_table[sig - 1]; |
|
205 |
#ifdef DEBUG_SIGNAL |
|
206 |
fprintf(stderr, "gemu: got signal %d\n", sig); |
|
227 |
#if defined(DEBUG_SIGNAL) |
|
228 |
fprintf(stderr, "queue_sigal: sig=%d\n", |
|
229 |
sig); |
|
207 | 230 |
#endif |
231 |
k = &sigact_table[sig - 1]; |
|
208 | 232 |
handler = k->sa._sa_handler; |
209 | 233 |
if (handler == TARGET_SIG_DFL) { |
210 | 234 |
/* default handler : ignore some signal. The other are fatal */ |
... | ... | |
212 | 236 |
sig != TARGET_SIGURG && |
213 | 237 |
sig != TARGET_SIGWINCH) { |
214 | 238 |
force_sig(sig); |
239 |
} else { |
|
240 |
return 0; /* indicate ignored */ |
|
215 | 241 |
} |
216 | 242 |
} else if (handler == TARGET_SIG_IGN) { |
217 | 243 |
/* ignore signal */ |
244 |
return 0; |
|
218 | 245 |
} else if (handler == TARGET_SIG_ERR) { |
219 | 246 |
force_sig(sig); |
220 | 247 |
} else { |
221 |
queue_signal(k, sig, info); |
|
248 |
pq = &k->first; |
|
249 |
if (sig < TARGET_SIGRTMIN) { |
|
250 |
/* if non real time signal, we queue exactly one signal */ |
|
251 |
if (!k->pending) |
|
252 |
q = &k->info; |
|
253 |
else |
|
254 |
return 0; |
|
255 |
} else { |
|
256 |
if (!k->pending) { |
|
257 |
/* first signal */ |
|
258 |
q = &k->info; |
|
259 |
} else { |
|
260 |
q = alloc_sigqueue(); |
|
261 |
if (!q) |
|
262 |
return -EAGAIN; |
|
263 |
while (*pq != NULL) |
|
264 |
pq = &(*pq)->next; |
|
265 |
} |
|
266 |
} |
|
267 |
*pq = q; |
|
268 |
q->info = *info; |
|
269 |
q->next = NULL; |
|
270 |
k->pending = 1; |
|
271 |
/* signal that a new signal is pending */ |
|
272 |
signal_pending = 1; |
|
273 |
return 1; /* indicates that the signal was queued */ |
|
274 |
} |
|
275 |
} |
|
276 |
|
|
277 |
#if defined(DEBUG_SIGNAL) |
|
278 |
#ifdef __i386__ |
|
279 |
static void dump_regs(struct ucontext *uc) |
|
280 |
{ |
|
281 |
fprintf(stderr, |
|
282 |
"EAX=%08x EBX=%08x ECX=%08x EDX=%08x\n" |
|
283 |
"ESI=%08x EDI=%08x EBP=%08x ESP=%08x\n" |
|
284 |
"EFL=%08x EIP=%08x\n", |
|
285 |
uc->uc_mcontext.gregs[EAX], |
|
286 |
uc->uc_mcontext.gregs[EBX], |
|
287 |
uc->uc_mcontext.gregs[ECX], |
|
288 |
uc->uc_mcontext.gregs[EDX], |
|
289 |
uc->uc_mcontext.gregs[ESI], |
|
290 |
uc->uc_mcontext.gregs[EDI], |
|
291 |
uc->uc_mcontext.gregs[EBP], |
|
292 |
uc->uc_mcontext.gregs[ESP], |
|
293 |
uc->uc_mcontext.gregs[EFL], |
|
294 |
uc->uc_mcontext.gregs[EIP]); |
|
295 |
} |
|
296 |
#else |
|
297 |
static void dump_regs(struct ucontext *uc) |
|
298 |
{ |
|
299 |
} |
|
300 |
#endif |
|
301 |
|
|
302 |
#endif |
|
303 |
|
|
304 |
static void host_signal_handler(int host_signum, siginfo_t *info, |
|
305 |
void *puc) |
|
306 |
{ |
|
307 |
int sig; |
|
308 |
target_siginfo_t tinfo; |
|
309 |
|
|
310 |
/* the CPU emulator uses some host signals to detect exceptions, |
|
311 |
we we forward to it some signals */ |
|
312 |
if (host_signum == SIGSEGV || host_signum == SIGBUS) { |
|
313 |
if (cpu_x86_signal_handler(host_signum, info, puc)) |
|
314 |
return; |
|
315 |
} |
|
316 |
|
|
317 |
/* get target signal number */ |
|
318 |
sig = host_to_target_signal(host_signum); |
|
319 |
if (sig < 1 || sig > TARGET_NSIG) |
|
320 |
return; |
|
321 |
#if defined(DEBUG_SIGNAL) |
|
322 |
fprintf(stderr, "gemu: got signal %d\n", sig); |
|
323 |
dump_regs(puc); |
|
324 |
#endif |
|
325 |
host_to_target_siginfo_noswap(&tinfo, info); |
|
326 |
if (queue_signal(sig, &tinfo) == 1) { |
|
327 |
/* interrupt the virtual CPU as soon as possible */ |
|
328 |
cpu_x86_interrupt(global_env); |
|
222 | 329 |
} |
223 | 330 |
} |
224 | 331 |
|
... | ... | |
388 | 495 |
0;\ |
389 | 496 |
}) |
390 | 497 |
|
391 |
static inline int copy_siginfo_to_user(target_siginfo_t *tinfo, siginfo_t *info) |
|
498 |
static inline int copy_siginfo_to_user(target_siginfo_t *tinfo, |
|
499 |
const target_siginfo_t *info) |
|
392 | 500 |
{ |
393 |
host_to_target_siginfo(tinfo, info);
|
|
501 |
tswap_siginfo(tinfo, info);
|
|
394 | 502 |
return 0; |
395 | 503 |
} |
396 | 504 |
|
... | ... | |
531 | 639 |
force_sig(TARGET_SIGSEGV /* , current */); |
532 | 640 |
} |
533 | 641 |
|
534 |
static void setup_rt_frame(int sig, struct emulated_sigaction *ka, siginfo_t *info, |
|
642 |
static void setup_rt_frame(int sig, struct emulated_sigaction *ka, |
|
643 |
target_siginfo_t *info, |
|
535 | 644 |
target_sigset_t *set, CPUX86State *env) |
536 | 645 |
{ |
537 | 646 |
struct rt_sigframe *frame; |
... | ... | |
734 | 843 |
{ |
735 | 844 |
int sig; |
736 | 845 |
target_ulong handler; |
737 |
target_sigset_t set; |
|
846 |
sigset_t set, old_set; |
|
847 |
target_sigset_t target_old_set; |
|
738 | 848 |
struct emulated_sigaction *k; |
739 | 849 |
struct sigqueue *q; |
740 | 850 |
|
... | ... | |
774 | 884 |
} else if (handler == TARGET_SIG_ERR) { |
775 | 885 |
force_sig(sig); |
776 | 886 |
} else { |
777 |
set = k->sa.sa_mask; |
|
778 |
/* send the signal to the CPU */ |
|
887 |
/* compute the blocked signals during the handler execution */ |
|
888 |
target_to_host_sigset(&set, &k->sa.sa_mask); |
|
889 |
/* SA_NODEFER indicates that the current signal should not be |
|
890 |
blocked during the handler */ |
|
891 |
if (!(k->sa.sa_flags & TARGET_SA_NODEFER)) |
|
892 |
sigaddset(&set, target_to_host_signal(sig)); |
|
893 |
|
|
894 |
/* block signals in the handler using Linux */ |
|
895 |
sigprocmask(SIG_BLOCK, &set, &old_set); |
|
896 |
/* save the previous blocked signal state to restore it at the |
|
897 |
end of the signal execution (see do_sigreturn) */ |
|
898 |
host_to_target_sigset(&target_old_set, &old_set); |
|
899 |
|
|
900 |
/* prepare the stack frame of the virtual CPU */ |
|
779 | 901 |
if (k->sa.sa_flags & TARGET_SA_SIGINFO) |
780 |
setup_rt_frame(sig, k, &q->info, &set, cpu_env); |
|
902 |
setup_rt_frame(sig, k, &q->info, &target_old_set, cpu_env);
|
|
781 | 903 |
else |
782 |
setup_frame(sig, k, &set, cpu_env); |
|
904 |
setup_frame(sig, k, &target_old_set, cpu_env);
|
|
783 | 905 |
if (k->sa.sa_flags & TARGET_SA_RESETHAND) |
784 | 906 |
k->sa._sa_handler = TARGET_SIG_DFL; |
785 | 907 |
} |
Also available in: Unified diff