And there's still more to come on the signal handling. Currently, my
"hack" to get ARM working is as follows, and is not the best thing to
view on a full stomach.
Generalising the signal handling might have made sense, but this amount
of duplication _just_ to be able to handle non-hardware breakpoints is
getting rather rediculous.
I will be looking into the possibility of carving up the generic
signal handling into a saner structure so we don't have this mess.
Obviously, this patch is not for applying. 8)
--- orig/arch/arm/kernel/signal.c Tue Feb 11 16:09:38 2003
+++ linux/arch/arm/kernel/signal.c Tue Feb 11 16:13:55 2003
@x@ -492,6 +492,78 @x@
force_sig(SIGSEGV, tsk);
}
+int
+specific_send_sig_info(int sig, struct siginfo *info, struct task_struct *t);
+void finish_stop(int stop_count);
+void do_signal_stop(int signr);
+
+/* Some systems do not have a SIGSTKFLT and the kernel never
+ * generates such signals anyways.
+ */
+#ifdef SIGSTKFLT
+#define M_SIGSTKFLT M(SIGSTKFLT)
+#else
+#define M_SIGSTKFLT 0
+#endif
+
+#ifdef SIGEMT
+#define M_SIGEMT M(SIGEMT)
+#else
+#define M_SIGEMT 0
+#endif
+
+#if SIGRTMIN > BITS_PER_LONG
+#define M(sig) (1ULL << ((sig)-1))
+#else
+#define M(sig) (1UL << ((sig)-1))
+#endif
+#define T(sig, mask) (M(sig) & (mask))
+
+#define SIG_KERNEL_BROADCAST_MASK (\
+ M(SIGHUP) | M(SIGINT) | M(SIGQUIT) | M(SIGILL) | \
+ M(SIGTRAP) | M(SIGABRT) | M(SIGBUS) | M(SIGFPE) | \
+ M(SIGKILL) | M(SIGUSR1) | M(SIGSEGV) | M(SIGUSR2) | \
+ M(SIGPIPE) | M(SIGALRM) | M(SIGTERM) | M(SIGXCPU) | \
+ M(SIGXFSZ) | M(SIGVTALRM) | M(SIGPROF) | M(SIGPOLL) | \
+ M(SIGSYS) | M_SIGSTKFLT | M(SIGPWR) | M(SIGCONT) | \
+ M(SIGSTOP) | M(SIGTSTP) | M(SIGTTIN) | M(SIGTTOU) | \
+ M_SIGEMT )
+
+#define SIG_KERNEL_ONLY_MASK (\
+ M(SIGKILL) | M(SIGSTOP) )
+
+#define SIG_KERNEL_STOP_MASK (\
+ M(SIGSTOP) | M(SIGTSTP) | M(SIGTTIN) | M(SIGTTOU) )
+
+#define SIG_KERNEL_COREDUMP_MASK (\
+ M(SIGQUIT) | M(SIGILL) | M(SIGTRAP) | M(SIGABRT) | \
+ M(SIGFPE) | M(SIGSEGV) | M(SIGBUS) | M(SIGSYS) | \
+ M(SIGXCPU) | M(SIGXFSZ) | M_SIGEMT )
+
+#define SIG_KERNEL_IGNORE_MASK (\
+ M(SIGCONT) | M(SIGCHLD) | M(SIGWINCH) | M(SIGURG) )
+
+#define sig_kernel_only(sig) \
+ (((sig) < SIGRTMIN) && T(sig, SIG_KERNEL_ONLY_MASK))
+#define sig_kernel_coredump(sig) \
+ (((sig) < SIGRTMIN) && T(sig, SIG_KERNEL_COREDUMP_MASK))
+#define sig_kernel_ignore(sig) \
+ (((sig) < SIGRTMIN) && T(sig, SIG_KERNEL_IGNORE_MASK))
+#define sig_kernel_stop(sig) \
+ (((sig) < SIGRTMIN) && T(sig, SIG_KERNEL_STOP_MASK))
+
+#define sig_user_defined(t, signr) \
+ (((t)->sighand->action[(signr)-1].sa.sa_handler != SIG_DFL) && \
+ ((t)->sighand->action[(signr)-1].sa.sa_handler != SIG_IGN))
+
+#define sig_ignored(t, signr) \
+ (!((t)->ptrace & PT_PTRACED) && \
+ (t)->sighand->action[(signr)-1].sa.sa_handler == SIG_IGN)
+
+#define sig_fatal(t, signr) \
+ (!T(signr, SIG_KERNEL_IGNORE_MASK|SIG_KERNEL_STOP_MASK) && \
+ (t)->sighand->action[(signr)-1].sa.sa_handler == SIG_DFL)
+
/*
* Note that 'init' is a special process: it doesn't get signals it doesn't
* want to handle. Thus you cannot kill init even with a SIGKILL even by
@@ -522,6 +594,29 @@
struct k_sigaction *ka;
spin_lock_irq(¤t->sighand->siglock);
+ if (unlikely(current->signal->group_stop_count > 0)) {
+ int stop_count;
+ if (current->signal->group_exit_task == current) {
+ /*
+ * Group stop is so we can do a core dump.
+ */
+ current->signal->group_exit_task = NULL;
+ goto dequeue;
+ }
+ /*
+ * There is a group stop in progress. We stop
+ * without any associated signal being in our queue.
+ */
+ stop_count = --current->signal->group_stop_count;
+ signr = current->signal->group_exit_code;
+ current->exit_code = signr;
+ set_current_state(TASK_STOPPED);
+ spin_unlock_irq(¤t->sighand->siglock);
+ finish_stop(stop_count);
+ single_stepping |= ptrace_cancel_bpt(current);
+ continue;
+ }
+ dequeue:
signr = dequeue_signal(¤t->blocked, &info);
spin_unlock_irq(¤t->sighand->siglock);
@@ -529,8 +624,19 @@
break;
if ((current->ptrace & PT_PTRACED) && signr != SIGKILL) {
+ /*
+ * If there is a group stop in progress,
+ * we must participate in the bookkeeping.
+ */
+ if (current->signal->group_stop_count > 0) {
+ spin_lock_irq(¤t->sighand->siglock);
+ --current->signal->group_stop_count;
+ spin_unlock_irq(¤t->sighand->siglock);
+ }
+
/* Let the debugger run. */
current->exit_code = signr;
+ current->last_siginfo = &info;
set_current_state(TASK_STOPPED);
notify_parent(current, SIGCHLD);
schedule();
@@ -542,11 +648,10 @@
continue;
current->exit_code = 0;
- /* The debugger continued. Ignore SIGSTOP. */
- if (signr == SIGSTOP)
- continue;
-
- /* Update the siginfo structure. Is this good? */
+ /* Update the siginfo structure if the signal has
+ changed. If the debugger wanted something
+ specific in the siginfo structure then it should
+ have updated *info via PTRACE_SETSIGINFO. */
if (signr != info.si_signo) {
info.si_signo = signr;
info.si_errno = 0;
@@ -557,60 +662,68 @@
/* If the (new) signal is now blocked, requeue it. */
if (sigismember(¤t->blocked, signr)) {
- send_sig_info(signr, &info, current);
+ spin_lock_irq(¤t->sighand->siglock);
+ specific_send_sig_info(signr, &info, current);
+ spin_unlock_irq(¤t->sighand->siglock);
continue;
}
}
- ka = ¤t->sig->action[signr-1];
- if (ka->sa.sa_handler == SIG_IGN) {
- if (signr != SIGCHLD)
- continue;
- /* Check for SIGCHLD: it's special. */
- while (sys_wait4(-1, NULL, WNOHANG, NULL) > 0)
- /* nothing */;
+ ka = ¤t->sighand->action[signr-1];
+ if (ka->sa.sa_handler == SIG_IGN) /* Do nothing. */
continue;
- }
if (ka->sa.sa_handler == SIG_DFL) {
- int exit_code = signr;
+ /*
+ * Now we are doing the default action for this signal.
+ */
+ if (sig_kernel_ignore(signr)) /* Default is nothing. */
+ continue;
/* Init gets no signals it doesn't want. */
if (current->pid == 1)
continue;
- switch (signr) {
- case SIGCONT: case SIGCHLD: case SIGWINCH: case SIGURG:
- continue;
-
- case SIGTSTP: case SIGTTIN: case SIGTTOU:
- if (is_orphaned_pgrp(current->pgrp))
- continue;
- /* FALLTHRU */
-
- case SIGSTOP: {
- struct signal_struct *sig;
- set_current_state(TASK_STOPPED);
- current->exit_code = signr;
- sig = current->parent->sig;
- if (sig && !(sig->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDSTOP))
- notify_parent(current, SIGCHLD);
- schedule();
- single_stepping |= ptrace_cancel_bpt(current);
+ if (sig_kernel_stop(signr)) {
+ /*
+ * The default action is to stop all threads in
+ * the thread group. The job control signals
+ * do nothing in an orphaned pgrp, but SIGSTOP
+ * always works.
+ */
+ if (signr == SIGSTOP ||
+ !is_orphaned_pgrp(current->pgrp))
+ do_signal_stop(signr);
continue;
}
- case SIGQUIT: case SIGILL: case SIGTRAP:
- case SIGABRT: case SIGFPE: case SIGSEGV:
- case SIGBUS: case SIGSYS: case SIGXCPU: case SIGXFSZ:
- if (do_coredump(signr, exit_code, regs))
- exit_code |= 0x80;
- /* FALLTHRU */
-
- default:
- sig_exit(signr, exit_code, &info);
- /* NOTREACHED */
+ /*
+ * Anything else is fatal, maybe with a core dump.
+ */
+ current->flags |= PF_SIGNALED;
+ if (sig_kernel_coredump(signr) &&
+ do_coredump(signr, signr, regs)) {
+ /*
+ * That killed all other threads in the group and
+ * synchronized with their demise, so there can't
+ * be any more left to kill now. The group_exit
+ * flags are set by do_coredump. Note that
+ * thread_group_empty won't always be true yet,
+ * because those threads were blocked in __exit_mm
+ * and we just let them go to finish dying.
+ */
+ const int code = signr | 0x80;
+ BUG_ON(!current->signal->group_exit);
+ BUG_ON(current->signal->group_exit_code != code);
+ do_exit(code);
+ /* NOTREACHED */
}
+
+ /*
+ * Death signals, no core dump.
+ */
+ do_group_exit(signr);
+ /* NOTREACHED */
}
/* Are we from a system call? */
-- Russell King (rmk@arm.linux.org.uk) The developer of ARM Linux http://www.arm.linux.org.uk/personal/aboutme.html- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/