aboutsummaryrefslogtreecommitdiff
path: root/linux-user/signal.c
diff options
context:
space:
mode:
Diffstat (limited to 'linux-user/signal.c')
-rw-r--r--linux-user/signal.c50
1 files changed, 45 insertions, 5 deletions
diff --git a/linux-user/signal.c b/linux-user/signal.c
index c1e664f97a..046159dd0c 100644
--- a/linux-user/signal.c
+++ b/linux-user/signal.c
@@ -498,18 +498,30 @@ static int core_dump_signal(int sig)
static void signal_table_init(void)
{
- int host_sig, target_sig;
+ int host_sig, target_sig, count;
/*
- * Nasty hack: Reverse SIGRTMIN and SIGRTMAX to avoid overlap with
- * host libpthread signals. This assumes no one actually uses SIGRTMAX :-/
+ * Signals are supported starting from TARGET_SIGRTMIN and going up
+ * until we run out of host realtime signals.
+ * glibc at least uses only the lower 2 rt signals and probably
+ * nobody's using the upper ones.
+ * it's why SIGRTMIN (34) is generally greater than __SIGRTMIN (32)
* To fix this properly we need to do manual signal delivery multiplexed
* over a single host signal.
+ * Attempts for configure "missing" signals via sigaction will be
+ * silently ignored.
*/
- host_to_target_signal_table[__SIGRTMIN] = __SIGRTMAX;
- host_to_target_signal_table[__SIGRTMAX] = __SIGRTMIN;
+ for (host_sig = SIGRTMIN; host_sig <= SIGRTMAX; host_sig++) {
+ target_sig = host_sig - SIGRTMIN + TARGET_SIGRTMIN;
+ if (target_sig <= TARGET_NSIG) {
+ host_to_target_signal_table[host_sig] = target_sig;
+ }
+ }
/* generate signal conversion tables */
+ for (target_sig = 1; target_sig <= TARGET_NSIG; target_sig++) {
+ target_to_host_signal_table[target_sig] = _NSIG; /* poison */
+ }
for (host_sig = 1; host_sig < _NSIG; host_sig++) {
if (host_to_target_signal_table[host_sig] == 0) {
host_to_target_signal_table[host_sig] = host_sig;
@@ -519,6 +531,15 @@ static void signal_table_init(void)
target_to_host_signal_table[target_sig] = host_sig;
}
}
+
+ if (trace_event_get_state_backends(TRACE_SIGNAL_TABLE_INIT)) {
+ for (target_sig = 1, count = 0; target_sig <= TARGET_NSIG; target_sig++) {
+ if (target_to_host_signal_table[target_sig] == _NSIG) {
+ count++;
+ }
+ }
+ trace_signal_table_init(count);
+ }
}
void signal_init(void)
@@ -817,6 +838,8 @@ int do_sigaction(int sig, const struct target_sigaction *act,
int host_sig;
int ret = 0;
+ trace_signal_do_sigaction_guest(sig, TARGET_NSIG);
+
if (sig < 1 || sig > TARGET_NSIG || sig == TARGET_SIGKILL || sig == TARGET_SIGSTOP) {
return -TARGET_EINVAL;
}
@@ -847,6 +870,23 @@ int do_sigaction(int sig, const struct target_sigaction *act,
/* we update the host linux signal state */
host_sig = target_to_host_signal(sig);
+ trace_signal_do_sigaction_host(host_sig, TARGET_NSIG);
+ if (host_sig > SIGRTMAX) {
+ /* we don't have enough host signals to map all target signals */
+ qemu_log_mask(LOG_UNIMP, "Unsupported target signal #%d, ignored\n",
+ sig);
+ /*
+ * we don't return an error here because some programs try to
+ * register an handler for all possible rt signals even if they
+ * don't need it.
+ * An error here can abort them whereas there can be no problem
+ * to not have the signal available later.
+ * This is the case for golang,
+ * See https://github.com/golang/go/issues/33746
+ * So we silently ignore the error.
+ */
+ return 0;
+ }
if (host_sig != SIGSEGV && host_sig != SIGBUS) {
sigfillset(&act1.sa_mask);
act1.sa_flags = SA_SIGINFO;