aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurent Vivier <laurent@vivier.eu>2020-02-12 13:56:58 +0100
committerLaurent Vivier <laurent@vivier.eu>2020-02-12 18:56:41 +0100
commit6bc024e713fd35eb5fddbe16acd8dc92d27872a9 (patch)
treeec7229f3d63b2b4071d8d4ab12da477a62e9b38f
parent9fcff3a67f2be53de2d9b27c270ba2a4ecba8810 (diff)
linux-user: fix use of SIGRTMIN
Some RT signals can be in use by glibc, it's why SIGRTMIN (34) is generally greater than __SIGRTMIN (32). So SIGRTMIN cannot be mapped to TARGET_SIGRTMIN. Instead of swapping only SIGRTMIN and SIGRTMAX, map all the range [TARGET_SIGRTMIN ... TARGET_SIGRTMAX - X] to [__SIGRTMIN + X ... SIGRTMAX ] (SIGRTMIN is __SIGRTMIN + X). Signed-off-by: Laurent Vivier <laurent@vivier.eu> Reviewed-by: Taylor Simson <tsimpson@quicinc.com> Tested-by: Taylor Simpson <tsimpson@quicinc.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-Id: <20200212125658.644558-5-laurent@vivier.eu>
-rw-r--r--linux-user/signal.c50
-rw-r--r--linux-user/trace-events3
2 files changed, 48 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;
diff --git a/linux-user/trace-events b/linux-user/trace-events
index f6de1b8bef..0296133dae 100644
--- a/linux-user/trace-events
+++ b/linux-user/trace-events
@@ -1,6 +1,9 @@
# See docs/devel/tracing.txt for syntax documentation.
# signal.c
+signal_table_init(int i) "number of unavailable signals: %d"
+signal_do_sigaction_guest(int sig, int max) "target signal %d (MAX %d)"
+signal_do_sigaction_host(int sig, int max) "host signal %d (MAX %d)"
# */signal.c
user_setup_frame(void *env, uint64_t frame_addr) "env=%p frame_addr=0x%"PRIx64
user_setup_rt_frame(void *env, uint64_t frame_addr) "env=%p frame_addr=0x%"PRIx64