]> git.proxmox.com Git - qemu.git/blobdiff - linux-user/signal.c
User-mode GDB stub improvements - handle signals
[qemu.git] / linux-user / signal.c
index 8fb3469ccab2463667ac887fb50bdd11dd5666c0..5e30522091d9be3624b501591a71c0f93937125d 100644 (file)
@@ -31,7 +31,7 @@
 
 //#define DEBUG_SIGNAL
 
-struct target_sigaltstack target_sigaltstack_used = {
+static struct target_sigaltstack target_sigaltstack_used = {
     .ss_sp = 0,
     .ss_size = 0,
     .ss_flags = TARGET_SS_DISABLE,
@@ -264,6 +264,26 @@ void target_to_host_siginfo(siginfo_t *info, const target_siginfo_t *tinfo)
             (void *)(long)tswapl(tinfo->_sifields._rt._sigval.sival_ptr);
 }
 
+static int fatal_signal (int sig)
+{
+    switch (sig) {
+    case TARGET_SIGCHLD:
+    case TARGET_SIGURG:
+    case TARGET_SIGWINCH:
+        /* Ignored by default.  */
+        return 0;
+    case TARGET_SIGCONT:
+    case TARGET_SIGSTOP:
+    case TARGET_SIGTSTP:
+    case TARGET_SIGTTIN:
+    case TARGET_SIGTTOU:
+        /* Job control signals.  */
+        return 0;
+    default:
+        return 1;
+    }
+}
+
 void signal_init(void)
 {
     struct sigaction act;
@@ -298,10 +318,12 @@ void signal_init(void)
         }
         /* If there's already a handler installed then something has
            gone horribly wrong, so don't even try to handle that case.  */
-        /* Install some handlers for our own use.  */
-        if (host_sig == SIGSEGV || host_sig == SIGBUS) {
+        /* Install some handlers for our own use.  We need at least
+           SIGSEGV and SIGBUS, to detect exceptions.  We can not just
+           trap all signals because it affects syscall interrupt
+           behavior.  But do trap all default-fatal signals.  */
+        if (fatal_signal (i))
             sigaction(host_sig, &act, NULL);
-        }
     }
 }
 
@@ -332,6 +354,7 @@ static void __attribute((noreturn)) force_sig(int sig)
     fprintf(stderr, "qemu: uncaught target signal %d (%s) - exiting\n",
             sig, strsignal(host_sig));
 #if 1
+    gdb_signalled(thread_env, sig);
     _exit(-host_sig);
 #else
     {
@@ -353,26 +376,33 @@ int queue_signal(CPUState *env, int sig, target_siginfo_t *info)
     struct emulated_sigtable *k;
     struct sigqueue *q, **pq;
     abi_ulong handler;
+    int queue;
 
 #if defined(DEBUG_SIGNAL)
     fprintf(stderr, "queue_signal: sig=%d\n",
             sig);
 #endif
     k = &ts->sigtab[sig - 1];
+    queue = gdb_queuesig ();
     handler = sigact_table[sig - 1]._sa_handler;
-    if (handler == TARGET_SIG_DFL) {
+    if (!queue && handler == TARGET_SIG_DFL) {
+        if (sig == TARGET_SIGTSTP || sig == TARGET_SIGTTIN || sig == TARGET_SIGTTOU) {
+            kill(getpid(),SIGSTOP);
+            return 0;
+        } else
         /* default handler : ignore some signal. The other are fatal */
         if (sig != TARGET_SIGCHLD &&
             sig != TARGET_SIGURG &&
-            sig != TARGET_SIGWINCH) {
+            sig != TARGET_SIGWINCH &&
+            sig != TARGET_SIGCONT) {
             force_sig(sig);
         } else {
             return 0; /* indicate ignored */
         }
-    } else if (handler == TARGET_SIG_IGN) {
+    } else if (!queue && handler == TARGET_SIG_IGN) {
         /* ignore signal */
         return 0;
-    } else if (handler == TARGET_SIG_ERR) {
+    } else if (!queue && handler == TARGET_SIG_ERR) {
         force_sig(sig);
     } else {
         pq = &k->first;
@@ -412,7 +442,8 @@ static void host_signal_handler(int host_signum, siginfo_t *info,
 
     /* 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 ((host_signum == SIGSEGV || host_signum == SIGBUS)
+        && info->si_code == SI_KERNEL) {
         if (cpu_signal_handler(host_signum, info, puc))
             return;
     }
@@ -502,7 +533,7 @@ int do_sigaction(int sig, const struct target_sigaction *act,
     int host_sig;
     int ret = 0;
 
-    if (sig < 1 || sig > TARGET_NSIG || sig == SIGKILL || sig == SIGSTOP)
+    if (sig < 1 || sig > TARGET_NSIG || sig == TARGET_SIGKILL || sig == TARGET_SIGSTOP)
         return -EINVAL;
     k = &sigact_table[sig - 1];
 #if defined(DEBUG_SIGNAL)
@@ -539,7 +570,10 @@ int do_sigaction(int sig, const struct target_sigaction *act,
             if (k->_sa_handler == TARGET_SIG_IGN) {
                 act1.sa_sigaction = (void *)SIG_IGN;
             } else if (k->_sa_handler == TARGET_SIG_DFL) {
-                act1.sa_sigaction = (void *)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;
             }
@@ -549,10 +583,6 @@ int do_sigaction(int sig, const struct target_sigaction *act,
     return ret;
 }
 
-#ifndef offsetof
-#define offsetof(type, field) ((size_t) &((type *)0)->field)
-#endif
-
 static inline int copy_siginfo_to_user(target_siginfo_t *tinfo,
                                        const target_siginfo_t *info)
 {
@@ -3027,9 +3057,6 @@ long do_sigreturn(CPUState *env)
        sigprocmask(SIG_SETMASK, &set, NULL);
 
        restore_sigcontext(&frame->sc, env);
-       /* Compensate for the syscall return path advancing brk.  */
-       env->pc -= 2;
-
        unlock_user_struct(frame, frame_addr, 0);
        return env->regs[10];
   badframe:
@@ -3109,17 +3136,21 @@ void process_pending_signals(CPUState *cpu_env)
 
     sig = gdb_handlesig (cpu_env, sig);
     if (!sig) {
-        fprintf (stderr, "Lost signal\n");
-        abort();
+        sa = NULL;
+        handler = TARGET_SIG_IGN;
+    } else {
+        sa = &sigact_table[sig - 1];
+        handler = sa->_sa_handler;
     }
 
-    sa = &sigact_table[sig - 1];
-    handler = 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) {
+        /* default handler : ignore some signal. The other are job control or fatal */
+        if (sig == TARGET_SIGTSTP || sig == TARGET_SIGTTIN || sig == TARGET_SIGTTOU) {
+            kill(getpid(),SIGSTOP);
+        } else if (sig != TARGET_SIGCHLD &&
+                   sig != TARGET_SIGURG &&
+                   sig != TARGET_SIGWINCH &&
+                   sig != TARGET_SIGCONT) {
             force_sig(sig);
         }
     } else if (handler == TARGET_SIG_IGN) {