/* * 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, see <http://www.gnu.org/licenses/>. */ #include <stdlib.h> #include <stdio.h> #include <string.h> #include <stdarg.h> #include <unistd.h> #include <errno.h> #include <sys/ucontext.h> #ifdef __ia64__ #undef uc_mcontext #undef uc_sigmask #undef uc_stack #undef uc_link #endif #include "qemu.h" #include "qemu-common.h" #define DEBUG_SIGNAL #define MAX_SIGQUEUE_SIZE 1024 struct 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 sigaltstack target_sigaltstack_used = { 0, 0, SA_DISABLE }; static struct emulated_sigaction sigact_table[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 inline int host_to_target_signal(int sig) { return sig; } static inline int target_to_host_signal(int sig) { return sig; } /* siginfo conversion */ void host_to_target_siginfo(target_siginfo_t *tinfo, const siginfo_t *info) { } void target_to_host_siginfo(siginfo_t *info, const target_siginfo_t *tinfo) { } void signal_init(void) { struct sigaction act; int 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 QEMU_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)); _exit(-host_sig); } /* 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 = (target_ulong)k->sa.sa_handler; if (handler == SIG_DFL) { /* default handler : ignore some signal. The other are fatal */ if (sig != SIGCHLD && sig != SIGURG && sig != SIGWINCH) { force_sig(sig); } else { return 0; /* indicate ignored */ } } else if (handler == host_to_target_signal(SIG_IGN)) { /* ignore signal */ return 0; } else if (handler == host_to_target_signal(SIG_ERR)) { force_sig(sig); } else { pq = &k->first; 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 (cpu_signal_handler(host_signum, (void*)info, puc)) return; } /* get target signal number */ sig = host_to_target_signal(host_signum); if (sig < 1 || sig > NSIG) return; #if defined(DEBUG_SIGNAL) fprintf(stderr, "qemu: got signal %d\n", sig); #endif if (queue_signal(sig, &tinfo) == 1) { /* interrupt the virtual CPU as soon as possible */ cpu_exit(global_env); } } int do_sigaltstack(const struct sigaltstack *ss, struct sigaltstack *oss) { /* XXX: test errors */ if(oss) { oss->ss_sp = tswap32(target_sigaltstack_used.ss_sp); oss->ss_size = tswap32(target_sigaltstack_used.ss_size); oss->ss_flags = tswap32(target_sigaltstack_used.ss_flags); } if(ss) { target_sigaltstack_used.ss_sp = tswap32(ss->ss_sp); target_sigaltstack_used.ss_size = tswap32(ss->ss_size); target_sigaltstack_used.ss_flags = tswap32(ss->ss_flags); } return 0; } int do_sigaction(int sig, const struct sigaction *act, struct sigaction *oact) { struct emulated_sigaction *k; struct sigaction act1; int host_sig; if (sig < 1 || sig > NSIG) return -EINVAL; k = &sigact_table[sig - 1]; #if defined(DEBUG_SIGNAL) fprintf(stderr, "sigaction 1 sig=%d act=0x%08x, oact=0x%08x\n", sig, (int)act, (int)oact); #endif if (oact) { #if defined(DEBUG_SIGNAL) fprintf(stderr, "sigaction 1 sig=%d act=0x%08x, oact=0x%08x\n", sig, (int)act, (int)oact); #endif oact->sa_handler = tswapl(k->sa.sa_handler); oact->sa_flags = tswapl(k->sa.sa_flags); oact->sa_mask = tswapl(k->sa.sa_mask); } if (act) { #if defined(DEBUG_SIGNAL) fprintf(stderr, "sigaction handler 0x%x flag 0x%x mask 0x%x\n", act->sa_handler, act->sa_flags, act->sa_mask); #endif k->sa.sa_handler = tswapl(act->sa_handler); k->sa.sa_flags = tswapl(act->sa_flags); k->sa.sa_mask = tswapl(act->sa_mask); /* we update the host signal state */ host_sig = target_to_host_signal(sig); if (host_sig != SIGSEGV && host_sig != SIGBUS) { #if defined(DEBUG_SIGNAL) fprintf(stderr, "sigaction handler going to call sigaction\n"); #endif sigfillset(&act1.sa_mask); act1.sa_flags = SA_SIGINFO; if (k->sa.sa_flags & SA_RESTART) act1.sa_flags |= SA_RESTART; /* NOTE: it is important to update the host kernel signal ignore state to avoid getting unexpected interrupted syscalls */ if (k->sa.sa_handler == SIG_IGN) { act1.sa_sigaction = (void *)SIG_IGN; } else if (k->sa.sa_handler == SIG_DFL) { act1.sa_sigaction = (void *)SIG_DFL; } else { act1.sa_sigaction = host_signal_handler; } sigaction(host_sig, &act1, NULL); } } return 0; } #ifdef TARGET_I386 static inline void * get_sigframe(struct emulated_sigaction *ka, CPUX86State *env, size_t frame_size) { /* XXX Fix that */ if(target_sigaltstack_used.ss_flags & SA_DISABLE) { int esp; /* Default to using normal stack */ esp = env->regs[R_ESP]; return (void *)((esp - frame_size) & -8ul); } else { return target_sigaltstack_used.ss_sp; } } static void setup_frame(int sig, struct emulated_sigaction *ka, void *set, CPUState *env) { void *frame; fprintf(stderr, "setup_frame %d\n", sig); frame = get_sigframe(ka, env, sizeof(*frame)); /* Set up registers for signal handler */ env->regs[R_ESP] = (unsigned long) frame; env->eip = (unsigned long) ka->sa.sa_handler; env->eflags &= ~TF_MASK; return; give_sigsegv: if (sig == SIGSEGV) ka->sa.sa_handler = SIG_DFL; force_sig(SIGSEGV /* , current */); } long do_sigreturn(CPUState *env, int num) { int i = 0; struct target_sigcontext *scp = get_int_arg(&i, env); /* XXX Get current signal number */ /* XXX Adjust accordin to sc_onstack, sc_mask */ if(tswapl(scp->sc_onstack) & 0x1) target_sigaltstack_used.ss_flags |= ~SA_DISABLE; else target_sigaltstack_used.ss_flags &= SA_DISABLE; int set = tswapl(scp->sc_eax); sigprocmask(SIG_SETMASK, &set, NULL); fprintf(stderr, "do_sigreturn: partially implemented %x EAX:%x EBX:%x\n", scp->sc_mask, tswapl(scp->sc_eax), tswapl(scp->sc_ebx)); fprintf(stderr, "ECX:%x EDX:%x EDI:%x\n", scp->sc_ecx, tswapl(scp->sc_edx), tswapl(scp->sc_edi)); fprintf(stderr, "EIP:%x\n", tswapl(scp->sc_eip)); env->regs[R_EAX] = tswapl(scp->sc_eax); env->regs[R_EBX] = tswapl(scp->sc_ebx); env->regs[R_ECX] = tswapl(scp->sc_ecx); env->regs[R_EDX] = tswapl(scp->sc_edx); env->regs[R_EDI] = tswapl(scp->sc_edi); env->regs[R_ESI] = tswapl(scp->sc_esi); env->regs[R_EBP] = tswapl(scp->sc_ebp); env->regs[R_ESP] = tswapl(scp->sc_esp); env->segs[R_SS].selector = (void*)tswapl(scp->sc_ss); env->eflags = tswapl(scp->sc_eflags); env->eip = tswapl(scp->sc_eip); env->segs[R_CS].selector = (void*)tswapl(scp->sc_cs); env->segs[R_DS].selector = (void*)tswapl(scp->sc_ds); env->segs[R_ES].selector = (void*)tswapl(scp->sc_es); env->segs[R_FS].selector = (void*)tswapl(scp->sc_fs); env->segs[R_GS].selector = (void*)tswapl(scp->sc_gs); /* Again, because our caller's caller will reset EAX */ return env->regs[R_EAX]; } #else static void setup_frame(int sig, struct emulated_sigaction *ka, void *set, CPUState *env) { fprintf(stderr, "setup_frame: not implemented\n"); } long do_sigreturn(CPUState *env, int num) { int i = 0; struct target_sigcontext *scp = get_int_arg(&i, env); fprintf(stderr, "do_sigreturn: not implemented\n"); return -ENOSYS; } #endif void process_pending_signals(void *cpu_env) { struct emulated_sigaction *k; struct sigqueue *q; target_ulong handler; int sig; if (!signal_pending) return; k = sigact_table; for(sig = 1; sig <= NSIG; sig++) { if (k->pending) goto handle_signal; k++; } /* if no signal is pending, just return */ signal_pending = 0; return; handle_signal: #ifdef DEBUG_SIGNAL fprintf(stderr, "qemu: process signal %d\n", sig); #endif /* dequeue signal */ q = k->first; k->first = q->next; if (!k->first) k->pending = 0; sig = gdb_handlesig (cpu_env, sig); if (!sig) { fprintf (stderr, "Lost signal\n"); abort(); } handler = k->sa.sa_handler; if (handler == SIG_DFL) { /* default handler : ignore some signal. The other are fatal */ if (sig != SIGCHLD && sig != SIGURG && sig != SIGWINCH) { force_sig(sig); } } else if (handler == SIG_IGN) { /* ignore sig */ } else if (handler == SIG_ERR) { force_sig(sig); } else { setup_frame(sig, k, 0, cpu_env); if (k->sa.sa_flags & SA_RESETHAND) k->sa.sa_handler = SIG_DFL; } if (q != &k->info) free_sigqueue(q); }