📄 signal.c
字号:
/* * Emulation of Linux signals * * Copyright (c) 2003 Fabrice Bellard * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */#include <stdlib.h>#include <stdio.h>#include <string.h>#include <stdarg.h>#include <unistd.h>#include <signal.h>#include <errno.h>#include <sys/ucontext.h>#include "qemu.h"//#define DEBUG_SIGNAL#define MAX_SIGQUEUE_SIZE 1024struct sigqueue { struct sigqueue *next; target_siginfo_t info;};struct emulated_sigaction { struct target_sigaction sa; int pending; /* true if signal is pending */ struct sigqueue *first; struct sigqueue info; /* in order to always have memory for the first signal, we put it here */};static struct emulated_sigaction sigact_table[TARGET_NSIG];static struct sigqueue sigqueue_table[MAX_SIGQUEUE_SIZE]; /* siginfo queue */static struct sigqueue *first_free; /* first free siginfo queue entry */static int signal_pending; /* non zero if a signal may be pending */static void host_signal_handler(int host_signum, siginfo_t *info, void *puc);static uint8_t host_to_target_signal_table[65] = { [SIGHUP] = TARGET_SIGHUP, [SIGINT] = TARGET_SIGINT, [SIGQUIT] = TARGET_SIGQUIT, [SIGILL] = TARGET_SIGILL, [SIGTRAP] = TARGET_SIGTRAP, [SIGABRT] = TARGET_SIGABRT,/* [SIGIOT] = TARGET_SIGIOT,*/ [SIGBUS] = TARGET_SIGBUS, [SIGFPE] = TARGET_SIGFPE, [SIGKILL] = TARGET_SIGKILL, [SIGUSR1] = TARGET_SIGUSR1, [SIGSEGV] = TARGET_SIGSEGV, [SIGUSR2] = TARGET_SIGUSR2, [SIGPIPE] = TARGET_SIGPIPE, [SIGALRM] = TARGET_SIGALRM, [SIGTERM] = TARGET_SIGTERM,#ifdef SIGSTKFLT [SIGSTKFLT] = TARGET_SIGSTKFLT,#endif [SIGCHLD] = TARGET_SIGCHLD, [SIGCONT] = TARGET_SIGCONT, [SIGSTOP] = TARGET_SIGSTOP, [SIGTSTP] = TARGET_SIGTSTP, [SIGTTIN] = TARGET_SIGTTIN, [SIGTTOU] = TARGET_SIGTTOU, [SIGURG] = TARGET_SIGURG, [SIGXCPU] = TARGET_SIGXCPU, [SIGXFSZ] = TARGET_SIGXFSZ, [SIGVTALRM] = TARGET_SIGVTALRM, [SIGPROF] = TARGET_SIGPROF, [SIGWINCH] = TARGET_SIGWINCH, [SIGIO] = TARGET_SIGIO, [SIGPWR] = TARGET_SIGPWR, [SIGSYS] = TARGET_SIGSYS, /* next signals stay the same */};static uint8_t target_to_host_signal_table[65];static inline int host_to_target_signal(int sig){ return host_to_target_signal_table[sig];}static inline int target_to_host_signal(int sig){ return target_to_host_signal_table[sig];}static void host_to_target_sigset_internal(target_sigset_t *d, const sigset_t *s){ int i; unsigned long sigmask; uint32_t target_sigmask; sigmask = ((unsigned long *)s)[0]; target_sigmask = 0; for(i = 0; i < 32; i++) { if (sigmask & (1 << i)) target_sigmask |= 1 << (host_to_target_signal(i + 1) - 1); }#if TARGET_LONG_BITS == 32 && HOST_LONG_BITS == 32 d->sig[0] = target_sigmask; for(i = 1;i < TARGET_NSIG_WORDS; i++) { d->sig[i] = ((unsigned long *)s)[i]; }#elif TARGET_LONG_BITS == 32 && HOST_LONG_BITS == 64 && TARGET_NSIG_WORDS == 2 d->sig[0] = target_sigmask; d->sig[1] = sigmask >> 32;#else#warning host_to_target_sigset#endif}void host_to_target_sigset(target_sigset_t *d, const sigset_t *s){ target_sigset_t d1; int i; host_to_target_sigset_internal(&d1, s); for(i = 0;i < TARGET_NSIG_WORDS; i++) d->sig[i] = tswapl(d1.sig[i]);}void target_to_host_sigset_internal(sigset_t *d, const target_sigset_t *s){ int i; unsigned long sigmask; target_ulong target_sigmask; target_sigmask = s->sig[0]; sigmask = 0; for(i = 0; i < 32; i++) { if (target_sigmask & (1 << i)) sigmask |= 1 << (target_to_host_signal(i + 1) - 1); }#if TARGET_LONG_BITS == 32 && HOST_LONG_BITS == 32 ((unsigned long *)d)[0] = sigmask; for(i = 1;i < TARGET_NSIG_WORDS; i++) { ((unsigned long *)d)[i] = s->sig[i]; }#elif TARGET_LONG_BITS == 32 && HOST_LONG_BITS == 64 && TARGET_NSIG_WORDS == 2 ((unsigned long *)d)[0] = sigmask | ((unsigned long)(s->sig[1]) << 32);#else#warning target_to_host_sigset#endif /* TARGET_LONG_BITS */}void target_to_host_sigset(sigset_t *d, const target_sigset_t *s){ target_sigset_t s1; int i; for(i = 0;i < TARGET_NSIG_WORDS; i++) s1.sig[i] = tswapl(s->sig[i]); target_to_host_sigset_internal(d, &s1);} void host_to_target_old_sigset(target_ulong *old_sigset, const sigset_t *sigset){ target_sigset_t d; host_to_target_sigset(&d, sigset); *old_sigset = d.sig[0];}void target_to_host_old_sigset(sigset_t *sigset, const target_ulong *old_sigset){ target_sigset_t d; int i; d.sig[0] = *old_sigset; for(i = 1;i < TARGET_NSIG_WORDS; i++) d.sig[i] = 0; target_to_host_sigset(sigset, &d);}/* siginfo conversion */static inline void host_to_target_siginfo_noswap(target_siginfo_t *tinfo, const siginfo_t *info){ int sig; sig = host_to_target_signal(info->si_signo); tinfo->si_signo = sig; tinfo->si_errno = 0; tinfo->si_code = 0; if (sig == SIGILL || sig == SIGFPE || sig == SIGSEGV || sig == SIGBUS || sig == SIGTRAP) { /* should never come here, but who knows. The information for the target is irrelevant */ tinfo->_sifields._sigfault._addr = 0; } else if (sig >= TARGET_SIGRTMIN) { tinfo->_sifields._rt._pid = info->si_pid; tinfo->_sifields._rt._uid = info->si_uid; /* XXX: potential problem if 64 bit */ tinfo->_sifields._rt._sigval.sival_ptr = (target_ulong)info->si_value.sival_ptr; }}static void tswap_siginfo(target_siginfo_t *tinfo, const target_siginfo_t *info){ int sig; sig = info->si_signo; tinfo->si_signo = tswap32(sig); tinfo->si_errno = tswap32(info->si_errno); tinfo->si_code = tswap32(info->si_code); if (sig == SIGILL || sig == SIGFPE || sig == SIGSEGV || sig == SIGBUS || sig == SIGTRAP) { tinfo->_sifields._sigfault._addr = tswapl(info->_sifields._sigfault._addr); } else if (sig >= TARGET_SIGRTMIN) { tinfo->_sifields._rt._pid = tswap32(info->_sifields._rt._pid); tinfo->_sifields._rt._uid = tswap32(info->_sifields._rt._uid); tinfo->_sifields._rt._sigval.sival_ptr = tswapl(info->_sifields._rt._sigval.sival_ptr); }}void host_to_target_siginfo(target_siginfo_t *tinfo, const siginfo_t *info){ host_to_target_siginfo_noswap(tinfo, info); tswap_siginfo(tinfo, tinfo);}/* XXX: we support only POSIX RT signals are used. *//* XXX: find a solution for 64 bit (additionnal malloced data is needed) */void target_to_host_siginfo(siginfo_t *info, const target_siginfo_t *tinfo){ info->si_signo = tswap32(tinfo->si_signo); info->si_errno = tswap32(tinfo->si_errno); info->si_code = tswap32(tinfo->si_code); info->si_pid = tswap32(tinfo->_sifields._rt._pid); info->si_uid = tswap32(tinfo->_sifields._rt._uid); info->si_value.sival_ptr = (void *)tswapl(tinfo->_sifields._rt._sigval.sival_ptr);}void signal_init(void){ struct sigaction act; int i, j; /* generate signal conversion tables */ for(i = 1; i <= 64; i++) { if (host_to_target_signal_table[i] == 0) host_to_target_signal_table[i] = i; } for(i = 1; i <= 64; i++) { j = host_to_target_signal_table[i]; target_to_host_signal_table[j] = i; } /* set all host signal handlers. ALL signals are blocked during the handlers to serialize them. */ sigfillset(&act.sa_mask); act.sa_flags = SA_SIGINFO; act.sa_sigaction = host_signal_handler; for(i = 1; i < NSIG; i++) { sigaction(i, &act, NULL); } memset(sigact_table, 0, sizeof(sigact_table)); first_free = &sigqueue_table[0]; for(i = 0; i < MAX_SIGQUEUE_SIZE - 1; i++) sigqueue_table[i].next = &sigqueue_table[i + 1]; sigqueue_table[MAX_SIGQUEUE_SIZE - 1].next = NULL;}/* signal queue handling */static inline struct sigqueue *alloc_sigqueue(void){ struct sigqueue *q = first_free; if (!q) return NULL; first_free = q->next; return q;}static inline void free_sigqueue(struct sigqueue *q){ q->next = first_free; first_free = q;}/* abort execution with signal */void __attribute((noreturn)) force_sig(int sig){ int host_sig; host_sig = target_to_host_signal(sig); fprintf(stderr, "qemu: uncaught target signal %d (%s) - exiting\n", sig, strsignal(host_sig));#if 1 _exit(-host_sig);#else { struct sigaction act; sigemptyset(&act.sa_mask); act.sa_flags = SA_SIGINFO; act.sa_sigaction = SIG_DFL; sigaction(SIGABRT, &act, NULL); abort(); }#endif}/* queue a signal so that it will be send to the virtual CPU as soon as possible */int queue_signal(int sig, target_siginfo_t *info){ struct emulated_sigaction *k; struct sigqueue *q, **pq; target_ulong handler;#if defined(DEBUG_SIGNAL) fprintf(stderr, "queue_signal: sig=%d\n", sig);#endif k = &sigact_table[sig - 1]; handler = k->sa._sa_handler; if (handler == TARGET_SIG_DFL) { /* default handler : ignore some signal. The other are fatal */ if (sig != TARGET_SIGCHLD && sig != TARGET_SIGURG && sig != TARGET_SIGWINCH) { force_sig(sig); } else { return 0; /* indicate ignored */ } } else if (handler == TARGET_SIG_IGN) { /* ignore signal */ return 0; } else if (handler == TARGET_SIG_ERR) { force_sig(sig); } else { pq = &k->first; if (sig < TARGET_SIGRTMIN) { /* if non real time signal, we queue exactly one signal */ if (!k->pending) q = &k->info; else return 0; } else { if (!k->pending) { /* first signal */ q = &k->info; } else { q = alloc_sigqueue(); if (!q) return -EAGAIN; while (*pq != NULL) pq = &(*pq)->next; } } *pq = q; q->info = *info; q->next = NULL; k->pending = 1; /* signal that a new signal is pending */ signal_pending = 1; return 1; /* indicates that the signal was queued */ }}static void host_signal_handler(int host_signum, siginfo_t *info, void *puc){ int sig; target_siginfo_t tinfo; /* the CPU emulator uses some host signals to detect exceptions, we we forward to it some signals */ if (host_signum == SIGSEGV || host_signum == SIGBUS #if defined(TARGET_I386) && defined(USE_CODE_COPY) || host_signum == SIGFPE#endif ) { if (cpu_signal_handler(host_signum, info, puc)) return; } /* get target signal number */ sig = host_to_target_signal(host_signum); if (sig < 1 || sig > TARGET_NSIG) return;#if defined(DEBUG_SIGNAL) fprintf(stderr, "qemu: got signal %d\n", sig);#endif host_to_target_siginfo_noswap(&tinfo, info); if (queue_signal(sig, &tinfo) == 1) { /* interrupt the virtual CPU as soon as possible */ cpu_interrupt(global_env, CPU_INTERRUPT_EXIT); }}int do_sigaction(int sig, const struct target_sigaction *act, struct target_sigaction *oact){ struct emulated_sigaction *k; struct sigaction act1; int host_sig; if (sig < 1 || sig > TARGET_NSIG) return -EINVAL; k = &sigact_table[sig - 1];#if defined(DEBUG_SIGNAL) fprintf(stderr, "sigaction sig=%d act=0x%08x, oact=0x%08x\n", sig, (int)act, (int)oact);#endif if (oact) { oact->_sa_handler = tswapl(k->sa._sa_handler); oact->sa_flags = tswapl(k->sa.sa_flags);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -