Commit 6a8061e8 authored by Russell King's avatar Russell King

[ARM] Update signal handling for ARM.

Update the ARM signal handling to use the generic
get_signal_to_deliver() implementation.  There is a caveat though -
ARM has no hardware support for single stepping, and thus needs the
kernel to help provide this.

We introduce a new ptrace flag for this, PT_SINGLESTEP, which
indicates to architecture specific code that it should take extra
actions in the signal handler to set and/or clear breakpoints as
appropriate.
parent 64b21832
......@@ -446,7 +446,7 @@ void ptrace_set_bpt(struct task_struct *child)
* Ensure no single-step breakpoint is pending. Returns non-zero
* value if child was being single-stepped.
*/
void __ptrace_cancel_bpt(struct task_struct *child)
void ptrace_cancel_bpt(struct task_struct *child)
{
int i, nsaved = child->thread.debug.nsaved;
......@@ -468,7 +468,8 @@ void __ptrace_cancel_bpt(struct task_struct *child)
*/
void ptrace_disable(struct task_struct *child)
{
__ptrace_cancel_bpt(child);
child->ptrace &= ~PT_SINGLESTEP;
ptrace_cancel_bpt(child);
}
/*
......@@ -486,7 +487,7 @@ void ptrace_break(struct task_struct *tsk, struct pt_regs *regs)
if (tsk->thread.debug.nsaved == 0)
printk(KERN_ERR "ptrace: bogus breakpoint trap\n");
__ptrace_cancel_bpt(tsk);
ptrace_cancel_bpt(tsk);
info.si_signo = SIGTRAP;
info.si_errno = 0;
......@@ -637,7 +638,8 @@ static int do_ptrace(int request, struct task_struct *child, long addr, long dat
clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
child->exit_code = data;
/* make sure single-step breakpoint is gone. */
__ptrace_cancel_bpt(child);
child->ptrace &= ~PT_SINGLESTEP;
ptrace_cancel_bpt(child);
wake_up_process(child);
ret = 0;
break;
......@@ -649,7 +651,8 @@ static int do_ptrace(int request, struct task_struct *child, long addr, long dat
*/
case PTRACE_KILL:
/* make sure single-step breakpoint is gone. */
__ptrace_cancel_bpt(child);
child->ptrace &= ~PT_SINGLESTEP;
ptrace_cancel_bpt(child);
if (child->state != TASK_ZOMBIE) {
child->exit_code = SIGKILL;
wake_up_process(child);
......@@ -664,7 +667,7 @@ static int do_ptrace(int request, struct task_struct *child, long addr, long dat
ret = -EIO;
if ((unsigned long) data > _NSIG)
break;
child->thread.debug.nsaved = -1;
child->ptrace |= PT_SINGLESTEP;
clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
child->exit_code = data;
/* give it a chance to run. */
......
/*
* linux/arch/arm/kernel/ptrace.h
*
* Copyright (C) 2000-2002 Russell King
* Copyright (C) 2000-2003 Russell King
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
extern void __ptrace_cancel_bpt(struct task_struct *);
extern void ptrace_cancel_bpt(struct task_struct *);
extern void ptrace_set_bpt(struct task_struct *);
extern void ptrace_break(struct task_struct *, struct pt_regs *);
/*
* Clear a breakpoint, if one exists.
*/
static inline int ptrace_cancel_bpt(struct task_struct *tsk)
{
int nsaved = tsk->thread.debug.nsaved;
if (nsaved)
__ptrace_cancel_bpt(tsk);
return nsaved;
}
......@@ -216,8 +216,10 @@ asmlinkage int sys_sigreturn(struct pt_regs *regs)
goto badframe;
/* Send SIGTRAP if we're single-stepping */
if (ptrace_cancel_bpt(current))
if (current->ptrace & PT_SINGLESTEP) {
ptrace_cancel_bpt(current);
send_sig(SIGTRAP, current, 1);
}
return regs->ARM_r0;
......@@ -256,8 +258,10 @@ asmlinkage int sys_rt_sigreturn(struct pt_regs *regs)
goto badframe;
/* Send SIGTRAP if we're single-stepping */
if (ptrace_cancel_bpt(current))
if (current->ptrace & PT_SINGLESTEP) {
ptrace_cancel_bpt(current);
send_sig(SIGTRAP, current, 1);
}
return regs->ARM_r0;
......@@ -441,18 +445,47 @@ setup_rt_frame(int usig, struct k_sigaction *ka, siginfo_t *info,
return err;
}
static inline void restart_syscall(struct pt_regs *regs)
{
regs->ARM_r0 = regs->ARM_ORIG_r0;
regs->ARM_pc -= thumb_mode(regs) ? 2 : 4;
}
/*
* OK, we're invoking a handler
*/
static void
handle_signal(unsigned long sig, struct k_sigaction *ka,
siginfo_t *info, sigset_t *oldset, struct pt_regs * regs)
handle_signal(unsigned long sig, siginfo_t *info, sigset_t *oldset,
struct pt_regs * regs, int syscall)
{
struct thread_info *thread = current_thread_info();
struct task_struct *tsk = current;
struct k_sigaction *ka = &tsk->sighand->action[sig-1];
int usig = sig;
int ret;
/*
* If we were from a system call, check for system call restarting...
*/
if (syscall) {
switch (regs->ARM_r0) {
case -ERESTART_RESTARTBLOCK:
current_thread_info()->restart_block.fn =
do_no_restart_syscall;
case -ERESTARTNOHAND:
regs->ARM_r0 = -EINTR;
break;
case -ERESTARTSYS:
if (!(ka->sa.sa_flags & SA_RESTART)) {
regs->ARM_r0 = -EINTR;
break;
}
/* fallthrough */
case -ERESTARTNOINTR:
restart_syscall(regs);
}
}
/*
* translate the signal
*/
......@@ -504,7 +537,7 @@ handle_signal(unsigned long sig, struct k_sigaction *ka,
static int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall)
{
siginfo_t info;
int single_stepping;
int signr;
/*
* We want the common case to go fast, which
......@@ -515,130 +548,14 @@ static int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall)
if (!user_mode(regs))
return 0;
single_stepping = ptrace_cancel_bpt(current);
for (;;) {
unsigned long signr = 0;
struct k_sigaction *ka;
spin_lock_irq(&current->sighand->siglock);
signr = dequeue_signal(&current->blocked, &info);
spin_unlock_irq(&current->sighand->siglock);
if (!signr)
break;
if (current->ptrace & PT_SINGLESTEP)
ptrace_cancel_bpt(current);
if ((current->ptrace & PT_PTRACED) && signr != SIGKILL) {
/* Let the debugger run. */
current->exit_code = signr;
set_current_state(TASK_STOPPED);
notify_parent(current, SIGCHLD);
schedule();
single_stepping |= ptrace_cancel_bpt(current);
/* We're back. Did the debugger cancel the sig? */
signr = current->exit_code;
if (signr == 0)
continue;
current->exit_code = 0;
/* The debugger continued. Ignore SIGSTOP. */
if (signr == SIGSTOP)
continue;
/* Update the siginfo structure. Is this good? */
if (signr != info.si_signo) {
info.si_signo = signr;
info.si_errno = 0;
info.si_code = SI_USER;
info.si_pid = current->parent->pid;
info.si_uid = current->parent->uid;
}
/* If the (new) signal is now blocked, requeue it. */
if (sigismember(&current->blocked, signr)) {
send_sig_info(signr, &info, current);
continue;
}
}
ka = &current->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 */;
continue;
}
if (ka->sa.sa_handler == SIG_DFL) {
int exit_code = signr;
/* 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);
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 */
}
}
/* Are we from a system call? */
if (syscall) {
/* If so, check system call restarting.. */
switch (regs->ARM_r0) {
case -ERESTART_RESTARTBLOCK:
current_thread_info()->restart_block.fn =
do_no_restart_syscall;
case -ERESTARTNOHAND:
regs->ARM_r0 = -EINTR;
break;
case -ERESTARTSYS:
if (!(ka->sa.sa_flags & SA_RESTART)) {
regs->ARM_r0 = -EINTR;
break;
}
/* fallthrough */
case -ERESTARTNOINTR:
regs->ARM_r0 = regs->ARM_ORIG_r0;
regs->ARM_pc -= 4;
}
}
/* Whee! Actually deliver the signal. */
handle_signal(signr, ka, &info, oldset, regs);
if (single_stepping)
ptrace_set_bpt(current);
signr = get_signal_to_deliver(&info, regs, NULL);
if (signr > 0) {
handle_signal(signr, &info, oldset, regs, syscall);
if (current->ptrace & PT_SINGLESTEP)
ptrace_set_bpt(current);
return 1;
}
......@@ -668,11 +585,10 @@ static int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall)
if (regs->ARM_r0 == -ERESTARTNOHAND ||
regs->ARM_r0 == -ERESTARTSYS ||
regs->ARM_r0 == -ERESTARTNOINTR) {
regs->ARM_r0 = regs->ARM_ORIG_r0;
regs->ARM_pc -= 4;
restart_syscall(regs);
}
}
if (single_stepping)
if (current->ptrace & PT_SINGLESTEP)
ptrace_set_bpt(current);
return 0;
}
......
......@@ -184,9 +184,7 @@ typedef struct sigaltstack {
#ifdef __KERNEL__
#include <asm/sigcontext.h>
#define HAVE_ARCH_GET_SIGNAL_TO_DELIVER
#define ptrace_signal_deliver(regs, cookie) do { } while (0)
#endif
#endif
......@@ -460,6 +460,7 @@ do { if (atomic_dec_and_test(&(tsk)->usage)) __put_task_struct(tsk); } while(0)
#define PT_TRACE_EXEC 0x00000080
#define PT_TRACE_VFORK_DONE 0x00000100
#define PT_TRACE_EXIT 0x00000200
#define PT_SINGLESTEP 0x80000000 /* single step mode */
#if CONFIG_SMP
extern void set_cpus_allowed(task_t *p, unsigned long new_mask);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment