diff --git a/Makefile b/Makefile index ea67590c3c..9f1d31c364 100644 --- a/Makefile +++ b/Makefile @@ -36,7 +36,7 @@ LDFLAGS+=-p main.o: CFLAGS+=-p endif -OBJS= elfload.o main.o thunk.o syscall.o libgemu.a +OBJS= elfload.o main.o thunk.o syscall.o signal.o libgemu.a LIBOBJS+=translate-i386.o op-i386.o exec-i386.o # NOTE: the disassembler code is only needed for debugging diff --git a/TODO b/TODO index 5c7c963df3..64384834a1 100644 --- a/TODO +++ b/TODO @@ -1,5 +1,7 @@ +- asynchronous signal interrupt / clear synchronous signal handling +- add eflags restore in emulator +- finish signal handing (fp87 state) - verify thread support (clone() and various locks) -- signals - optimize translated cache chaining (DLL PLT-like system) - vm86 syscall support - overrides/16bit for string ops diff --git a/linux-user/main.c b/linux-user/main.c index cd08c474c4..bcaa4be161 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -81,10 +81,6 @@ int cpu_x86_inl(int addr) return 0; } -/* default linux values for the selectors */ -#define __USER_CS (0x23) -#define __USER_DS (0x2B) - void write_dt(void *ptr, unsigned long addr, unsigned long limit, int seg32_bit) { @@ -135,6 +131,7 @@ void cpu_loop(struct CPUX86State *env) (long)pc, err); abort(); } + process_pending_signals(env); } } @@ -199,6 +196,7 @@ int main(int argc, char **argv) target_set_brk((char *)info->brk); syscall_init(); + signal_init(); env = cpu_x86_init(); diff --git a/linux-user/qemu.h b/linux-user/qemu.h index ae86176c35..77e9ecadd9 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -3,6 +3,12 @@ #include "thunk.h" +#ifdef TARGET_I386 + +/* default linux values for the selectors */ +#define __USER_CS (0x23) +#define __USER_DS (0x2B) + struct target_pt_regs { long ebx; long ecx; @@ -21,6 +27,8 @@ struct target_pt_regs { int xss; }; +#endif + /* This struct is used to hold certain information about the image. * Basically, it replicates in user space what would be certain * task_struct fields in the kernel @@ -53,5 +61,7 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, void gemu_log(const char *fmt, ...) __attribute__((format(printf,1,2))); struct CPUX86State; void cpu_loop(struct CPUX86State *env); +void process_pending_signals(void *cpu_env); +void signal_init(void); #endif diff --git a/linux-user/signal.c b/linux-user/signal.c index 2e0d599553..61baf995ae 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -1,5 +1,5 @@ /* - * Emulation of Linux signal handling + * Emulation of Linux signals * * Copyright (c) 2003 Fabrice Bellard * @@ -19,22 +19,49 @@ */ #include #include +#include #include #include +#include #include -/* Algorithm strongly inspired from em86 : we queue the signals so - that we can handle them at precise points in the emulated code. */ +#include "gemu.h" + +#include "syscall_defs.h" + +#ifdef TARGET_I386 +#include "cpu-i386.h" +#include "syscall-i386.h" +#endif + +/* signal handling inspired from em86. */ + +//#define DEBUG_SIGNAL + +#define MAX_SIGQUEUE_SIZE 1024 + +struct sigqueue { + struct sigqueue *next; + siginfo_t info; +}; struct emulated_sigaction { struct target_sigaction sa; - int nb_pending; - struct target_siginfo info; + 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 */ }; -struct emulated_sigaction sigact_table[NSIG]; -int signal_pending; +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); + +/* XXX: do it properly */ static inline int host_to_target_signal(int sig) { return sig; @@ -45,6 +72,51 @@ static inline int target_to_host_signal(int sig) return sig; } +void host_to_target_sigset(target_sigset_t *d, sigset_t *s) +{ + int i; + for(i = 0;i < TARGET_NSIG_WORDS; i++) { + d->sig[i] = tswapl(((unsigned long *)s)[i]); + } +} + +void target_to_host_sigset(sigset_t *d, target_sigset_t *s) +{ + int i; + for(i = 0;i < TARGET_NSIG_WORDS; i++) { + ((unsigned long *)d)[i] = tswapl(s->sig[i]); + } +} + +void host_to_target_old_sigset(target_ulong *old_sigset, + const sigset_t *sigset) +{ + *old_sigset = tswap32(*(unsigned long *)sigset & 0xffffffff); +} + +void target_to_host_old_sigset(sigset_t *sigset, + const target_ulong *old_sigset) +{ + sigemptyset(sigset); + *(unsigned long *)sigset = tswapl(*old_sigset); +} + +/* XXX: finish it */ +void host_to_target_siginfo(target_siginfo_t *tinfo, siginfo_t *info) +{ + tinfo->si_signo = tswap32(info->si_signo); + tinfo->si_errno = tswap32(info->si_errno); + tinfo->si_code = tswap32(info->si_code); +} + +/* XXX: finish it */ +void target_to_host_siginfo(siginfo_t *info, 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); +} + void signal_init(void) { struct sigaction act; @@ -55,51 +127,664 @@ void signal_init(void) act.sa_flags = SA_SIGINFO; act.sa_sigaction = host_signal_handler; for(i = 1; i < NSIG; i++) { - sigaction(i, &sa, NULL); + 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; +} + +static int queue_signal(struct emulated_sigaction *k, int sig, siginfo_t *info) +{ + struct sigqueue *q, **pq; + + pq = &k->first; + if (!k->pending || sig < TARGET_SIGRTMIN) { + /* first signal or non real time 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 0; +} + +void force_sig(int sig) +{ + int host_sig; + /* abort execution with signal */ + host_sig = target_to_host_signal(sig); + fprintf(stderr, "gemu: uncaught target signal %d (%s) - exiting\n", + sig, strsignal(host_sig)); + _exit(-host_sig); +} + + static void host_signal_handler(int host_signum, siginfo_t *info, void *puc) { - struct ucontext *uc = puc; - int signum; + struct emulated_sigaction *k; + int sig; + target_ulong handler; + /* get target signal number */ - signum = host_to_target(host_signum); - if (signum >= TARGET_NSIG) + sig = host_to_target_signal(host_signum); + if (sig < 1 || sig > TARGET_NSIG) return; - /* we save the old mask */ - - + k = &sigact_table[sig - 1]; +#ifdef DEBUG_SIGNAL + fprintf(stderr, "gemu: got signal %d\n", sig); +#endif + 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 if (handler == TARGET_SIG_IGN) { + /* ignore signal */ + } else if (handler == TARGET_SIG_ERR) { + force_sig(sig); + } else { + queue_signal(k, sig, info); + } } - -void process_pending_signals(void) +int do_sigaction(int sig, const struct target_sigaction *act, + struct target_sigaction *oact) { - int signum; - target_ulong _sa_handler; + struct emulated_sigaction *k; - struct emulated_sigaction *esig; + if (sig < 1 || sig > TARGET_NSIG) + return -EINVAL; + k = &sigact_table[sig - 1]; +#if defined(DEBUG_SIGNAL) && 0 + 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); + oact->sa_restorer = tswapl(k->sa.sa_restorer); + oact->sa_mask = k->sa.sa_mask; + } + if (act) { + k->sa._sa_handler = tswapl(act->_sa_handler); + k->sa.sa_flags = tswapl(act->sa_flags); + k->sa.sa_restorer = tswapl(act->sa_restorer); + k->sa.sa_mask = act->sa_mask; + } + return 0; +} +#ifdef TARGET_I386 + +/* from the Linux kernel */ + +struct target_fpreg { + uint16_t significand[4]; + uint16_t exponent; +}; + +struct target_fpxreg { + uint16_t significand[4]; + uint16_t exponent; + uint16_t padding[3]; +}; + +struct target_xmmreg { + target_ulong element[4]; +}; + +struct target_fpstate { + /* Regular FPU environment */ + target_ulong cw; + target_ulong sw; + target_ulong tag; + target_ulong ipoff; + target_ulong cssel; + target_ulong dataoff; + target_ulong datasel; + struct target_fpreg _st[8]; + uint16_t status; + uint16_t magic; /* 0xffff = regular FPU data only */ + + /* FXSR FPU environment */ + target_ulong _fxsr_env[6]; /* FXSR FPU env is ignored */ + target_ulong mxcsr; + target_ulong reserved; + struct target_fpxreg _fxsr_st[8]; /* FXSR FPU reg data is ignored */ + struct target_xmmreg _xmm[8]; + target_ulong padding[56]; +}; + +#define X86_FXSR_MAGIC 0x0000 + +struct target_sigcontext { + uint16_t gs, __gsh; + uint16_t fs, __fsh; + uint16_t es, __esh; + uint16_t ds, __dsh; + target_ulong edi; + target_ulong esi; + target_ulong ebp; + target_ulong esp; + target_ulong ebx; + target_ulong edx; + target_ulong ecx; + target_ulong eax; + target_ulong trapno; + target_ulong err; + target_ulong eip; + uint16_t cs, __csh; + target_ulong eflags; + target_ulong esp_at_signal; + uint16_t ss, __ssh; + target_ulong fpstate; /* pointer */ + target_ulong oldmask; + target_ulong cr2; +}; + +typedef struct target_sigaltstack { + target_ulong ss_sp; + int ss_flags; + target_ulong ss_size; +} target_stack_t; + +struct target_ucontext { + target_ulong uc_flags; + target_ulong uc_link; + target_stack_t uc_stack; + struct target_sigcontext uc_mcontext; + target_sigset_t uc_sigmask; /* mask last for extensibility */ +}; + +struct sigframe +{ + target_ulong pretcode; + int sig; + struct target_sigcontext sc; + struct target_fpstate fpstate; + target_ulong extramask[TARGET_NSIG_WORDS-1]; + char retcode[8]; +}; + +struct rt_sigframe +{ + target_ulong pretcode; + int sig; + target_ulong pinfo; + target_ulong puc; + struct target_siginfo info; + struct target_ucontext uc; + struct target_fpstate fpstate; + char retcode[8]; +}; + +/* + * Set up a signal frame. + */ + +#define __put_user(x,ptr)\ +({\ + int size = sizeof(*ptr);\ + switch(size) {\ + case 1:\ + stb(ptr, (typeof(*ptr))(x));\ + break;\ + case 2:\ + stw(ptr, (typeof(*ptr))(x));\ + break;\ + case 4:\ + stl(ptr, (typeof(*ptr))(x));\ + break;\ + case 8:\ + stq(ptr, (typeof(*ptr))(x));\ + break;\ + default:\ + abort();\ + }\ + 0;\ +}) + +#define get_user(val, ptr) (typeof(*ptr))(*(ptr)) + + +#define __copy_to_user(dst, src, size)\ +({\ + memcpy(dst, src, size);\ + 0;\ +}) + +static inline int copy_siginfo_to_user(target_siginfo_t *tinfo, siginfo_t *info) +{ + host_to_target_siginfo(tinfo, info); + return 0; +} + +/* XXX: save x87 state */ +static int +setup_sigcontext(struct target_sigcontext *sc, struct target_fpstate *fpstate, + CPUX86State *env, unsigned long mask) +{ + int err = 0; + + err |= __put_user(env->segs[R_GS], (unsigned int *)&sc->gs); + err |= __put_user(env->segs[R_FS], (unsigned int *)&sc->fs); + err |= __put_user(env->segs[R_ES], (unsigned int *)&sc->es); + err |= __put_user(env->segs[R_DS], (unsigned int *)&sc->ds); + err |= __put_user(env->regs[R_EDI], &sc->edi); + err |= __put_user(env->regs[R_ESI], &sc->esi); + err |= __put_user(env->regs[R_EBP], &sc->ebp); + err |= __put_user(env->regs[R_ESP], &sc->esp); + err |= __put_user(env->regs[R_EBX], &sc->ebx); + err |= __put_user(env->regs[R_EDX], &sc->edx); + err |= __put_user(env->regs[R_ECX], &sc->ecx); + err |= __put_user(env->regs[R_EAX], &sc->eax); + err |= __put_user(/*current->thread.trap_no*/ 0, &sc->trapno); + err |= __put_user(/*current->thread.error_code*/ 0, &sc->err); + err |= __put_user(env->eip, &sc->eip); + err |= __put_user(env->segs[R_CS], (unsigned int *)&sc->cs); + err |= __put_user(env->eflags, &sc->eflags); + err |= __put_user(env->regs[R_ESP], &sc->esp_at_signal); + err |= __put_user(env->segs[R_SS], (unsigned int *)&sc->ss); +#if 0 + tmp = save_i387(fpstate); + if (tmp < 0) + err = 1; + else + err |= __put_user(tmp ? fpstate : NULL, &sc->fpstate); +#else + err |= __put_user(0, &sc->fpstate); +#endif + /* non-iBCS2 extensions.. */ + err |= __put_user(mask, &sc->oldmask); + err |= __put_user(/*current->thread.cr2*/ 0, &sc->cr2); + + return err; +} + +/* + * Determine which stack to use.. + */ + +static inline void * +get_sigframe(struct emulated_sigaction *ka, CPUX86State *env, size_t frame_size) +{ + unsigned long esp; + + /* Default to using normal stack */ + esp = env->regs[R_ESP]; +#if 0 + /* This is the X/Open sanctioned signal stack switching. */ + if (ka->sa.sa_flags & SA_ONSTACK) { + if (sas_ss_flags(esp) == 0) + esp = current->sas_ss_sp + current->sas_ss_size; + } + + /* This is the legacy signal stack switching. */ + else if ((regs->xss & 0xffff) != __USER_DS && + !(ka->sa.sa_flags & SA_RESTORER) && + ka->sa.sa_restorer) { + esp = (unsigned long) ka->sa.sa_restorer; + } +#endif + return (void *)((esp - frame_size) & -8ul); +} + +#define TF_MASK TRAP_FLAG + +static void setup_frame(int sig, struct emulated_sigaction *ka, + target_sigset_t *set, CPUX86State *env) +{ + struct sigframe *frame; + int err = 0; + + frame = get_sigframe(ka, env, sizeof(*frame)); + +#if 0 + if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) + goto give_sigsegv; +#endif + err |= __put_user((/*current->exec_domain + && current->exec_domain->signal_invmap + && sig < 32 + ? current->exec_domain->signal_invmap[sig] + : */ sig), + &frame->sig); + if (err) + goto give_sigsegv; + + setup_sigcontext(&frame->sc, &frame->fpstate, env, set->sig[0]); + if (err) + goto give_sigsegv; + + if (TARGET_NSIG_WORDS > 1) { + err |= __copy_to_user(frame->extramask, &set->sig[1], + sizeof(frame->extramask)); + } + if (err) + goto give_sigsegv; + + /* Set up to return from userspace. If provided, use a stub + already in userspace. */ + if (ka->sa.sa_flags & TARGET_SA_RESTORER) { + err |= __put_user(ka->sa.sa_restorer, &frame->pretcode); + } else { + err |= __put_user(frame->retcode, &frame->pretcode); + /* This is popl %eax ; movl $,%eax ; int $0x80 */ + err |= __put_user(0xb858, (short *)(frame->retcode+0)); + err |= __put_user(TARGET_NR_sigreturn, (int *)(frame->retcode+2)); + err |= __put_user(0x80cd, (short *)(frame->retcode+6)); + } + + if (err) + goto give_sigsegv; + + /* Set up registers for signal handler */ + env->regs[R_ESP] = (unsigned long) frame; + env->eip = (unsigned long) ka->sa._sa_handler; + + cpu_x86_load_seg(env, R_DS, __USER_DS); + cpu_x86_load_seg(env, R_ES, __USER_DS); + cpu_x86_load_seg(env, R_SS, __USER_DS); + cpu_x86_load_seg(env, R_CS, __USER_CS); + env->eflags &= ~TF_MASK; + + return; + +give_sigsegv: + if (sig == TARGET_SIGSEGV) + ka->sa._sa_handler = TARGET_SIG_DFL; + force_sig(TARGET_SIGSEGV /* , current */); +} + +static void setup_rt_frame(int sig, struct emulated_sigaction *ka, siginfo_t *info, + target_sigset_t *set, CPUX86State *env) +{ + struct rt_sigframe *frame; + int err = 0; + + frame = get_sigframe(ka, env, sizeof(*frame)); + +#if 0 + if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) + goto give_sigsegv; +#endif + + err |= __put_user((/*current->exec_domain + && current->exec_domain->signal_invmap + && sig < 32 + ? current->exec_domain->signal_invmap[sig] + : */sig), + &frame->sig); + err |= __put_user((target_ulong)&frame->info, &frame->pinfo); + err |= __put_user((target_ulong)&frame->uc, &frame->puc); + err |= copy_siginfo_to_user(&frame->info, info); + if (err) + goto give_sigsegv; + + /* Create the ucontext. */ + err |= __put_user(0, &frame->uc.uc_flags); + err |= __put_user(0, &frame->uc.uc_link); + err |= __put_user(/*current->sas_ss_sp*/ 0, &frame->uc.uc_stack.ss_sp); + err |= __put_user(/* sas_ss_flags(regs->esp) */ 0, + &frame->uc.uc_stack.ss_flags); + err |= __put_user(/* current->sas_ss_size */ 0, &frame->uc.uc_stack.ss_size); + err |= setup_sigcontext(&frame->uc.uc_mcontext, &frame->fpstate, + env, set->sig[0]); + err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); + if (err) + goto give_sigsegv; + + /* Set up to return from userspace. If provided, use a stub + already in userspace. */ + if (ka->sa.sa_flags & TARGET_SA_RESTORER) { + err |= __put_user(ka->sa.sa_restorer, &frame->pretcode); + } else { + err |= __put_user(frame->retcode, &frame->pretcode); + /* This is movl $,%eax ; int $0x80 */ + err |= __put_user(0xb8, (char *)(frame->retcode+0)); + err |= __put_user(TARGET_NR_rt_sigreturn, (int *)(frame->retcode+1)); + err |= __put_user(0x80cd, (short *)(frame->retcode+5)); + } + + if (err) + goto give_sigsegv; + + /* Set up registers for signal handler */ + env->regs[R_ESP] = (unsigned long) frame; + env->eip = (unsigned long) ka->sa._sa_handler; + + cpu_x86_load_seg(env, R_DS, __USER_DS); + cpu_x86_load_seg(env, R_ES, __USER_DS); + cpu_x86_load_seg(env, R_SS, __USER_DS); + cpu_x86_load_seg(env, R_CS, __USER_CS); + env->eflags &= ~TF_MASK; + + return; + +give_sigsegv: + if (sig == TARGET_SIGSEGV) + ka->sa._sa_handler = TARGET_SIG_DFL; + force_sig(TARGET_SIGSEGV /* , current */); +} + +static int +restore_sigcontext(CPUX86State *env, struct target_sigcontext *sc, int *peax) +{ + unsigned int err = 0; + + + +#define COPY(x) err |= __get_user(regs->x, &sc->x) + +#define COPY_SEG(seg) \ + { unsigned short tmp; \ + err |= __get_user(tmp, &sc->seg); \ + regs->x##seg = tmp; } + +#define COPY_SEG_STRICT(seg) \ + { unsigned short tmp; \ + err |= __get_user(tmp, &sc->seg); \ + regs->x##seg = tmp|3; } + +#define GET_SEG(seg) \ + { unsigned short tmp; \ + err |= __get_user(tmp, &sc->seg); \ + loadsegment(seg,tmp); } + + cpu_x86_load_seg(env, R_GS, lduw(&sc->gs)); + cpu_x86_load_seg(env, R_FS, lduw(&sc->fs)); + cpu_x86_load_seg(env, R_ES, lduw(&sc->es)); + cpu_x86_load_seg(env, R_DS, lduw(&sc->ds)); + + env->regs[R_EDI] = ldl(&sc->edi); + env->regs[R_ESI] = ldl(&sc->esi); + env->regs[R_EBP] = ldl(&sc->ebp); + env->regs[R_ESP] = ldl(&sc->esp); + env->regs[R_EBX] = ldl(&sc->ebx); + env->regs[R_EDX] = ldl(&sc->edx); + env->regs[R_ECX] = ldl(&sc->ecx); + env->eip = ldl(&sc->eip); + + cpu_x86_load_seg(env, R_CS, lduw(&sc->cs) | 3); + cpu_x86_load_seg(env, R_SS, lduw(&sc->ss) | 3); + + { + unsigned int tmpflags; + tmpflags = ldl(&sc->eflags); + env->eflags = (env->eflags & ~0x40DD5) | (tmpflags & 0x40DD5); + // regs->orig_eax = -1; /* disable syscall checks */ + } + +#if 0 + { + struct _fpstate * buf; + err |= __get_user(buf, &sc->fpstate); + if (buf) { + if (verify_area(VERIFY_READ, buf, sizeof(*buf))) + goto badframe; + err |= restore_i387(buf); + } + } +#endif + *peax = ldl(&sc->eax); + return err; +#if 0 +badframe: + return 1; +#endif +} + +long do_sigreturn(CPUX86State *env) +{ + struct sigframe *frame = (struct sigframe *)(env->regs[R_ESP] - 8); + target_sigset_t target_set; + sigset_t set; + int eax, i; + + /* set blocked signals */ + target_set.sig[0] = frame->sc.oldmask; + for(i = 1; i < TARGET_NSIG_WORDS; i++) + target_set.sig[i] = frame->extramask[i - 1]; + + target_to_host_sigset(&set, &target_set); + sigprocmask(SIG_SETMASK, &set, NULL); + + /* restore registers */ + if (restore_sigcontext(env, &frame->sc, &eax)) + goto badframe; + return eax; + +badframe: + force_sig(TARGET_SIGSEGV); + return 0; +} + +long do_rt_sigreturn(CPUX86State *env) +{ + struct rt_sigframe *frame = (struct rt_sigframe *)(env->regs[R_ESP] - 4); + target_sigset_t target_set; + sigset_t set; + // stack_t st; + int eax; + +#if 0 + if (verify_area(VERIFY_READ, frame, sizeof(*frame))) + goto badframe; +#endif + memcpy(&target_set, &frame->uc.uc_sigmask, sizeof(target_sigset_t)); + + target_to_host_sigset(&set, &target_set); + sigprocmask(SIG_SETMASK, &set, NULL); + + if (restore_sigcontext(env, &frame->uc.uc_mcontext, &eax)) + goto badframe; + +#if 0 + if (__copy_from_user(&st, &frame->uc.uc_stack, sizeof(st))) + goto badframe; + /* It is more difficult to avoid calling this function than to + call it and ignore errors. */ + do_sigaltstack(&st, NULL, regs->esp); +#endif + return eax; + +badframe: + force_sig(TARGET_SIGSEGV); + return 0; +} + +#endif + +void process_pending_signals(void *cpu_env) +{ + int sig; + target_ulong handler; + target_sigset_t set; + struct emulated_sigaction *k; + struct sigqueue *q; + if (!signal_pending) return; - esig = sigact_table; - for(signum = 1; signum < TARGET_NSIG; signum++) { - if (esig->nb_pending != 0) + k = sigact_table; + for(sig = 1; sig <= TARGET_NSIG; sig++) { + if (k->pending) goto handle_signal; - esig++; + k++; } /* if no signal is pending, just return */ signal_pending = 0; return; + handle_signal: - _sa_handler = esig->sa._sa_handler; - if (_sa_handler == TARGET_SIG_DFL) { - /* default handling +#ifdef DEBUG_SIGNAL + fprintf(stderr, "gemu: process signal %d\n", sig); +#endif + /* dequeue signal */ + q = k->first; + k->first = q->next; + if (!k->first) + k->pending = 0; + + 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 if (handler == TARGET_SIG_IGN) { + /* ignore sig */ + } else if (handler == TARGET_SIG_ERR) { + force_sig(sig); + } else { + set = k->sa.sa_mask; + /* send the signal to the CPU */ + if (k->sa.sa_flags & TARGET_SA_SIGINFO) + setup_rt_frame(sig, k, &q->info, &set, cpu_env); + else + setup_frame(sig, k, &set, cpu_env); + if (k->sa.sa_flags & TARGET_SA_RESETHAND) + k->sa._sa_handler = TARGET_SIG_DFL; } - - + if (q != &k->info) + free_sigqueue(q); } + + diff --git a/linux-user/syscall.c b/linux-user/syscall.c index da54a0a186..dbe3de3fdf 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -75,12 +75,18 @@ #include "syscall-i386.h" #endif +void host_to_target_siginfo(target_siginfo_t *tinfo, siginfo_t *info); +void target_to_host_siginfo(siginfo_t *info, target_siginfo_t *tinfo); +long do_sigreturn(CPUX86State *env); +long do_rt_sigreturn(CPUX86State *env); + #define __NR_sys_uname __NR_uname #define __NR_sys_getcwd1 __NR_getcwd #define __NR_sys_statfs __NR_statfs #define __NR_sys_fstatfs __NR_fstatfs #define __NR_sys_getdents __NR_getdents #define __NR_sys_getdents64 __NR_getdents64 +#define __NR_sys_rt_sigqueueinfo __NR_rt_sigqueueinfo #ifdef __NR_gettid _syscall0(int, gettid) @@ -97,6 +103,9 @@ _syscall5(int, _llseek, uint, fd, ulong, hi, ulong, lo, loff_t *, res, uint, wh); _syscall2(int,sys_statfs,const char *,path,struct kernel_statfs *,buf) _syscall2(int,sys_fstatfs,int,fd,struct kernel_statfs *,buf) +_syscall3(int,sys_rt_sigqueueinfo,int,pid,int,sig,siginfo_t *,uinfo) + +extern int personality(int); static inline long get_errno(long ret) { @@ -199,18 +208,18 @@ static inline void host_to_target_fds(target_long *target_fds, #endif } -/* XXX: incorrect for some archs */ -static void host_to_target_old_sigset(target_ulong *old_sigset, - const sigset_t *sigset) +static inline void target_to_host_timeval(struct timeval *tv, + struct target_timeval *target_tv) { - *old_sigset = tswap32(*(unsigned long *)sigset & 0xffffffff); + tv->tv_sec = tswapl(target_tv->tv_sec); + tv->tv_usec = tswapl(target_tv->tv_usec); } -static void target_to_host_old_sigset(sigset_t *sigset, - const target_ulong *old_sigset) +static inline void host_to_target_timeval(struct target_timeval *target_tv, + struct timeval *tv) { - sigemptyset(sigset); - *(unsigned long *)sigset = tswapl(*old_sigset); + target_tv->tv_sec = tswapl(tv->tv_sec); + target_tv->tv_usec = tswapl(tv->tv_usec); } @@ -1042,28 +1051,195 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, ret = get_errno(setsid()); break; case TARGET_NR_sigaction: -#if 1 { - ret = 0; + struct target_old_sigaction *old_act = (void *)arg2; + struct target_old_sigaction *old_oact = (void *)arg3; + struct target_sigaction act, oact, *pact; + if (old_act) { + act._sa_handler = old_act->_sa_handler; + target_siginitset(&act.sa_mask, old_act->sa_mask); + act.sa_flags = old_act->sa_flags; + act.sa_restorer = old_act->sa_restorer; + pact = &act; + } else { + pact = NULL; + } + ret = get_errno(do_sigaction(arg1, pact, &oact)); + if (!is_error(ret) && old_oact) { + old_oact->_sa_handler = oact._sa_handler; + old_oact->sa_mask = oact.sa_mask.sig[0]; + old_oact->sa_flags = oact.sa_flags; + old_oact->sa_restorer = oact.sa_restorer; + } } break; -#else - goto unimplemented; -#endif + case TARGET_NR_rt_sigaction: + ret = get_errno(do_sigaction(arg1, (void *)arg2, (void *)arg3)); + break; case TARGET_NR_sgetmask: - goto unimplemented; + { + sigset_t cur_set; + target_ulong target_set; + sigprocmask(0, NULL, &cur_set); + host_to_target_old_sigset(&target_set, &cur_set); + ret = target_set; + } + break; case TARGET_NR_ssetmask: - goto unimplemented; + { + sigset_t set, oset, cur_set; + target_ulong target_set = arg1; + sigprocmask(0, NULL, &cur_set); + target_to_host_old_sigset(&set, &target_set); + sigorset(&set, &set, &cur_set); + sigprocmask(SIG_SETMASK, &set, &oset); + host_to_target_old_sigset(&target_set, &oset); + ret = target_set; + } + break; + case TARGET_NR_sigprocmask: + { + int how = arg1; + sigset_t set, oldset, *set_ptr; + target_ulong *pset = (void *)arg2, *poldset = (void *)arg3; + + if (pset) { + switch(how) { + case TARGET_SIG_BLOCK: + how = SIG_BLOCK; + break; + case TARGET_SIG_UNBLOCK: + how = SIG_UNBLOCK; + break; + case TARGET_SIG_SETMASK: + how = SIG_SETMASK; + break; + default: + ret = -EINVAL; + goto fail; + } + target_to_host_old_sigset(&set, pset); + set_ptr = &set; + } else { + how = 0; + set_ptr = NULL; + } + ret = get_errno(sigprocmask(arg1, set_ptr, &oldset)); + if (!is_error(ret) && poldset) { + host_to_target_old_sigset(poldset, &oldset); + } + } + break; + case TARGET_NR_rt_sigprocmask: + { + int how = arg1; + sigset_t set, oldset, *set_ptr; + target_sigset_t *pset = (void *)arg2; + target_sigset_t *poldset = (void *)arg3; + + if (pset) { + switch(how) { + case TARGET_SIG_BLOCK: + how = SIG_BLOCK; + break; + case TARGET_SIG_UNBLOCK: + how = SIG_UNBLOCK; + break; + case TARGET_SIG_SETMASK: + how = SIG_SETMASK; + break; + default: + ret = -EINVAL; + goto fail; + } + target_to_host_sigset(&set, pset); + set_ptr = &set; + } else { + how = 0; + set_ptr = NULL; + } + ret = get_errno(sigprocmask(how, set_ptr, &oldset)); + if (!is_error(ret) && poldset) { + host_to_target_sigset(poldset, &oldset); + } + } + break; + case TARGET_NR_sigpending: + { + sigset_t set; + ret = get_errno(sigpending(&set)); + if (!is_error(ret)) { + host_to_target_old_sigset((target_ulong *)arg1, &set); + } + } + break; + case TARGET_NR_rt_sigpending: + { + sigset_t set; + ret = get_errno(sigpending(&set)); + if (!is_error(ret)) { + host_to_target_sigset((target_sigset_t *)arg1, &set); + } + } + break; + case TARGET_NR_sigsuspend: + { + sigset_t set; + target_to_host_old_sigset(&set, (target_ulong *)arg1); + ret = get_errno(sigsuspend(&set)); + } + break; + case TARGET_NR_rt_sigsuspend: + { + sigset_t set; + target_to_host_sigset(&set, (target_sigset_t *)arg1); + ret = get_errno(sigsuspend(&set)); + } + break; + case TARGET_NR_rt_sigtimedwait: + { + target_sigset_t *target_set = (void *)arg1; + target_siginfo_t *target_uinfo = (void *)arg2; + struct target_timespec *target_uts = (void *)arg3; + sigset_t set; + struct timespec uts, *puts; + siginfo_t uinfo; + + target_to_host_sigset(&set, target_set); + if (target_uts) { + puts = &uts; + puts->tv_sec = tswapl(target_uts->tv_sec); + puts->tv_nsec = tswapl(target_uts->tv_nsec); + } else { + puts = NULL; + } + ret = get_errno(sigtimedwait(&set, &uinfo, puts)); + if (!is_error(ret) && target_uinfo) { + host_to_target_siginfo(target_uinfo, &uinfo); + } + } + break; + case TARGET_NR_rt_sigqueueinfo: + { + siginfo_t uinfo; + target_to_host_siginfo(&uinfo, (target_siginfo_t *)arg3); + ret = get_errno(sys_rt_sigqueueinfo(arg1, arg2, &uinfo)); + } + break; + case TARGET_NR_sigreturn: + /* NOTE: ret is eax, so not transcoding must be done */ + ret = do_sigreturn(cpu_env); + break; + case TARGET_NR_rt_sigreturn: + /* NOTE: ret is eax, so not transcoding must be done */ + ret = do_rt_sigreturn(cpu_env); + break; case TARGET_NR_setreuid: ret = get_errno(setreuid(arg1, arg2)); break; case TARGET_NR_setregid: ret = get_errno(setregid(arg1, arg2)); break; - case TARGET_NR_sigsuspend: - goto unimplemented; - case TARGET_NR_sigpending: - goto unimplemented; case TARGET_NR_sethostname: ret = get_errno(sethostname((const char *)arg1, arg2)); break; @@ -1190,9 +1366,43 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, case TARGET_NR_syslog: goto unimplemented; case TARGET_NR_setitimer: - goto unimplemented; + { + struct target_itimerval *target_value = (void *)arg2; + struct target_itimerval *target_ovalue = (void *)arg3; + struct itimerval value, ovalue, *pvalue; + + if (target_value) { + pvalue = &value; + target_to_host_timeval(&pvalue->it_interval, + &target_value->it_interval); + target_to_host_timeval(&pvalue->it_value, + &target_value->it_value); + } else { + pvalue = NULL; + } + ret = get_errno(setitimer(arg1, pvalue, &ovalue)); + if (!is_error(ret) && target_ovalue) { + host_to_target_timeval(&target_ovalue->it_interval, + &ovalue.it_interval); + host_to_target_timeval(&target_ovalue->it_value, + &ovalue.it_value); + } + } + break; case TARGET_NR_getitimer: - goto unimplemented; + { + struct target_itimerval *target_value = (void *)arg2; + struct itimerval value; + + ret = get_errno(getitimer(arg1, &value)); + if (!is_error(ret) && target_value) { + host_to_target_timeval(&target_value->it_interval, + &value.it_interval); + host_to_target_timeval(&target_value->it_value, + &value.it_value); + } + } + break; case TARGET_NR_stat: ret = get_errno(stat((const char *)arg1, &st)); goto do_stat; @@ -1279,8 +1489,6 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, case TARGET_NR_fsync: ret = get_errno(fsync(arg1)); break; - case TARGET_NR_sigreturn: - goto unimplemented; case TARGET_NR_clone: ret = get_errno(do_fork(cpu_env, arg1, arg2)); break; @@ -1301,39 +1509,6 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, case TARGET_NR_mprotect: ret = get_errno(mprotect((void *)arg1, arg2, arg3)); break; - case TARGET_NR_sigprocmask: - { - int how = arg1; - sigset_t set, oldset, *set_ptr; - target_ulong *pset = (void *)arg2, *poldset = (void *)arg3; - - switch(how) { - case TARGET_SIG_BLOCK: - how = SIG_BLOCK; - break; - case TARGET_SIG_UNBLOCK: - how = SIG_UNBLOCK; - break; - case TARGET_SIG_SETMASK: - how = SIG_SETMASK; - break; - default: - ret = -EINVAL; - goto fail; - } - - if (pset) { - target_to_host_old_sigset(&set, pset); - set_ptr = &set; - } else { - set_ptr = NULL; - } - ret = get_errno(sigprocmask(arg1, set_ptr, &oldset)); - if (!is_error(ret) && poldset) { - host_to_target_old_sigset(poldset, &oldset); - } - } - break; case TARGET_NR_create_module: case TARGET_NR_init_module: case TARGET_NR_delete_module: @@ -1516,13 +1691,6 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, case TARGET_NR_setresgid: case TARGET_NR_getresgid: case TARGET_NR_prctl: - case TARGET_NR_rt_sigreturn: - case TARGET_NR_rt_sigaction: - case TARGET_NR_rt_sigprocmask: - case TARGET_NR_rt_sigpending: - case TARGET_NR_rt_sigtimedwait: - case TARGET_NR_rt_sigqueueinfo: - case TARGET_NR_rt_sigsuspend: case TARGET_NR_pread: case TARGET_NR_pwrite: goto unimplemented; diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 6b0a714cb7..b83aeacebd 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -29,6 +29,11 @@ struct target_timespec { target_long tv_nsec; }; +struct target_itimerval { + struct target_timeval it_interval; + struct target_timeval it_value; +}; + struct target_iovec { target_long iov_base; /* Starting address */ target_long iov_len; /* Number of bytes */ @@ -113,6 +118,38 @@ typedef struct { target_ulong sig[TARGET_NSIG_WORDS]; } target_sigset_t; +#ifdef BSWAP_NEEDED +static inline void tswap_sigset(target_sigset_t *d, const target_sigset_t *s) +{ + int i; + for(i = 0;i < TARGET_NSIG_WORDS; i++) + d->sig[i] = tswapl(s->sig[i]); +} +#else +static inline void tswap_sigset(target_sigset_t *d, const target_sigset_t *s) +{ + *d = *s; +} +#endif + +static inline void target_siginitset(target_sigset_t *d, target_ulong set) +{ + int i; + d->sig[0] = set; + for(i = 1;i < TARGET_NSIG_WORDS; i++) + d->sig[i] = 0; +} + +void host_to_target_sigset(target_sigset_t *d, sigset_t *s); +void target_to_host_sigset(sigset_t *d, target_sigset_t *s); +void host_to_target_old_sigset(target_ulong *old_sigset, + const sigset_t *sigset); +void target_to_host_old_sigset(sigset_t *sigset, + const target_ulong *old_sigset); +struct target_sigaction; +int do_sigaction(int sig, const struct target_sigaction *act, + struct target_sigaction *oact); + /* Networking ioctls */ #define TARGET_SIOCADDRT 0x890B /* add routing table entry */ #define TARGET_SIOCDELRT 0x890C /* delete routing table entry */ diff --git a/ops_template.h b/ops_template.h index 70ee9f355c..1a23800412 100644 --- a/ops_template.h +++ b/ops_template.h @@ -1,3 +1,23 @@ +/* + * i386 micro operations (included several times to generate + * different operand sizes) + * + * 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. + */ #define DATA_BITS (1 << (3 + SHIFT)) #define SHIFT_MASK (DATA_BITS - 1) diff --git a/syscall-i386.h b/syscall-i386.h index 3d270d7754..39bba1a493 100644 --- a/syscall-i386.h +++ b/syscall-i386.h @@ -302,20 +302,59 @@ struct target_stat64 { unsigned long long st_ino; }; -typedef unsigned long old_sigset_t; /* at least 32 bits */ +#define TARGET_SA_NOCLDSTOP 0x00000001 +#define TARGET_SA_NOCLDWAIT 0x00000002 /* not supported yet */ +#define TARGET_SA_SIGINFO 0x00000004 +#define TARGET_SA_ONSTACK 0x08000000 +#define TARGET_SA_RESTART 0x10000000 +#define TARGET_SA_NODEFER 0x40000000 +#define TARGET_SA_RESETHAND 0x80000000 +#define TARGET_SA_RESTORER 0x04000000 + +#define TARGET_SIGHUP 1 +#define TARGET_SIGINT 2 +#define TARGET_SIGQUIT 3 +#define TARGET_SIGILL 4 +#define TARGET_SIGTRAP 5 +#define TARGET_SIGABRT 6 +#define TARGET_SIGIOT 6 +#define TARGET_SIGBUS 7 +#define TARGET_SIGFPE 8 +#define TARGET_SIGKILL 9 +#define TARGET_SIGUSR1 10 +#define TARGET_SIGSEGV 11 +#define TARGET_SIGUSR2 12 +#define TARGET_SIGPIPE 13 +#define TARGET_SIGALRM 14 +#define TARGET_SIGTERM 15 +#define TARGET_SIGSTKFLT 16 +#define TARGET_SIGCHLD 17 +#define TARGET_SIGCONT 18 +#define TARGET_SIGSTOP 19 +#define TARGET_SIGTSTP 20 +#define TARGET_SIGTTIN 21 +#define TARGET_SIGTTOU 22 +#define TARGET_SIGURG 23 +#define TARGET_SIGXCPU 24 +#define TARGET_SIGXFSZ 25 +#define TARGET_SIGVTALRM 26 +#define TARGET_SIGPROF 27 +#define TARGET_SIGWINCH 28 +#define TARGET_SIGIO 29 +#define TARGET_SIGRTMIN 32 struct target_old_sigaction { target_ulong _sa_handler; target_ulong sa_mask; target_ulong sa_flags; - void (*sa_restorer)(void); + target_ulong sa_restorer; }; struct target_sigaction { target_ulong _sa_handler; - target_sigset_t sa_mask; target_ulong sa_flags; target_ulong sa_restorer; + target_sigset_t sa_mask; }; typedef union target_sigval {