bsd-user/signal.c: implement do_sigaction
Implement the meat of the sigaction(2) system call with do_sigaction and helper routiner block_signals (which is also used to implemement signal masking so it's global). Signed-off-by: Stacey Son <sson@FreeBSD.org> Signed-off-by: Kyle Evans <kevans@freebsd.org> Signed-off-by: Warner Losh <imp@bsdimp.com> Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
parent
c885ae0e4e
commit
394cf69427
@ -9,7 +9,29 @@
|
||||
#ifndef SIGNAL_COMMON_H
|
||||
#define SIGNAL_COMMON_H
|
||||
|
||||
/**
|
||||
* block_signals: block all signals while handling this guest syscall
|
||||
*
|
||||
* Block all signals, and arrange that the signal mask is returned to
|
||||
* its correct value for the guest before we resume execution of guest code.
|
||||
* If this function returns non-zero, then the caller should immediately
|
||||
* return -TARGET_ERESTARTSYS to the main loop, which will take the pending
|
||||
* signal and restart execution of the syscall.
|
||||
* If block_signals() returns zero, then the caller can continue with
|
||||
* emulation of the system call knowing that no signals can be taken
|
||||
* (and therefore that no race conditions will result).
|
||||
* This should only be called once, because if it is called a second time
|
||||
* it will always return non-zero. (Think of it like a mutex that can't
|
||||
* be recursively locked.)
|
||||
* Signals will be unblocked again by process_pending_signals().
|
||||
*
|
||||
* Return value: non-zero if there was a pending signal, zero if not.
|
||||
*/
|
||||
int block_signals(void); /* Returns non zero if signal pending */
|
||||
|
||||
long do_rt_sigreturn(CPUArchState *env);
|
||||
int do_sigaction(int sig, const struct target_sigaction *act,
|
||||
struct target_sigaction *oact);
|
||||
abi_long do_sigaltstack(abi_ulong uss_addr, abi_ulong uoss_addr, abi_ulong sp);
|
||||
long do_sigreturn(CPUArchState *env, abi_ulong addr);
|
||||
void force_sig_fault(int sig, int code, abi_ulong addr);
|
||||
|
@ -309,6 +309,25 @@ static void tswap_siginfo(target_siginfo_t *tinfo, const target_siginfo_t *info)
|
||||
}
|
||||
}
|
||||
|
||||
int block_signals(void)
|
||||
{
|
||||
TaskState *ts = (TaskState *)thread_cpu->opaque;
|
||||
sigset_t set;
|
||||
|
||||
/*
|
||||
* It's OK to block everything including SIGSEGV, because we won't run any
|
||||
* further guest code before unblocking signals in
|
||||
* process_pending_signals(). We depend on the FreeBSD behaivor here where
|
||||
* this will only affect this thread's signal mask. We don't use
|
||||
* pthread_sigmask which might seem more correct because that routine also
|
||||
* does odd things with SIGCANCEL to implement pthread_cancel().
|
||||
*/
|
||||
sigfillset(&set);
|
||||
sigprocmask(SIG_SETMASK, &set, 0);
|
||||
|
||||
return qatomic_xchg(&ts->signal_pending, 1);
|
||||
}
|
||||
|
||||
/* Returns 1 if given signal should dump core if not handled. */
|
||||
static int core_dump_signal(int sig)
|
||||
{
|
||||
@ -554,6 +573,69 @@ static void host_signal_handler(int host_sig, siginfo_t *info, void *puc)
|
||||
cpu_exit(thread_cpu);
|
||||
}
|
||||
|
||||
/* do_sigaction() return host values and errnos */
|
||||
int do_sigaction(int sig, const struct target_sigaction *act,
|
||||
struct target_sigaction *oact)
|
||||
{
|
||||
struct target_sigaction *k;
|
||||
struct sigaction act1;
|
||||
int host_sig;
|
||||
int ret = 0;
|
||||
|
||||
if (sig < 1 || sig > TARGET_NSIG) {
|
||||
return -TARGET_EINVAL;
|
||||
}
|
||||
|
||||
if ((sig == TARGET_SIGKILL || sig == TARGET_SIGSTOP) &&
|
||||
act != NULL && act->_sa_handler != TARGET_SIG_DFL) {
|
||||
return -TARGET_EINVAL;
|
||||
}
|
||||
|
||||
if (block_signals()) {
|
||||
return -TARGET_ERESTART;
|
||||
}
|
||||
|
||||
k = &sigact_table[sig - 1];
|
||||
if (oact) {
|
||||
oact->_sa_handler = tswapal(k->_sa_handler);
|
||||
oact->sa_flags = tswap32(k->sa_flags);
|
||||
oact->sa_mask = k->sa_mask;
|
||||
}
|
||||
if (act) {
|
||||
k->_sa_handler = tswapal(act->_sa_handler);
|
||||
k->sa_flags = tswap32(act->sa_flags);
|
||||
k->sa_mask = act->sa_mask;
|
||||
|
||||
/* Update the host signal state. */
|
||||
host_sig = target_to_host_signal(sig);
|
||||
if (host_sig != SIGSEGV && host_sig != SIGBUS) {
|
||||
memset(&act1, 0, sizeof(struct sigaction));
|
||||
sigfillset(&act1.sa_mask);
|
||||
act1.sa_flags = SA_SIGINFO;
|
||||
if (k->sa_flags & TARGET_SA_RESTART) {
|
||||
act1.sa_flags |= SA_RESTART;
|
||||
}
|
||||
/*
|
||||
* Note: It is important to update the host kernel signal mask to
|
||||
* avoid getting unexpected interrupted system calls.
|
||||
*/
|
||||
if (k->_sa_handler == TARGET_SIG_IGN) {
|
||||
act1.sa_sigaction = (void *)SIG_IGN;
|
||||
} else if (k->_sa_handler == TARGET_SIG_DFL) {
|
||||
if (fatal_signal(sig)) {
|
||||
act1.sa_sigaction = host_signal_handler;
|
||||
} else {
|
||||
act1.sa_sigaction = (void *)SIG_DFL;
|
||||
}
|
||||
} else {
|
||||
act1.sa_sigaction = host_signal_handler;
|
||||
}
|
||||
ret = sigaction(host_sig, &act1, NULL);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline abi_ulong get_sigframe(struct target_sigaction *ka,
|
||||
CPUArchState *env, size_t frame_size)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user