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) ...@@ -446,7 +446,7 @@ void ptrace_set_bpt(struct task_struct *child)
* Ensure no single-step breakpoint is pending. Returns non-zero * Ensure no single-step breakpoint is pending. Returns non-zero
* value if child was being single-stepped. * 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; int i, nsaved = child->thread.debug.nsaved;
...@@ -468,7 +468,8 @@ void __ptrace_cancel_bpt(struct task_struct *child) ...@@ -468,7 +468,8 @@ void __ptrace_cancel_bpt(struct task_struct *child)
*/ */
void ptrace_disable(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) ...@@ -486,7 +487,7 @@ void ptrace_break(struct task_struct *tsk, struct pt_regs *regs)
if (tsk->thread.debug.nsaved == 0) if (tsk->thread.debug.nsaved == 0)
printk(KERN_ERR "ptrace: bogus breakpoint trap\n"); printk(KERN_ERR "ptrace: bogus breakpoint trap\n");
__ptrace_cancel_bpt(tsk); ptrace_cancel_bpt(tsk);
info.si_signo = SIGTRAP; info.si_signo = SIGTRAP;
info.si_errno = 0; info.si_errno = 0;
...@@ -637,7 +638,8 @@ static int do_ptrace(int request, struct task_struct *child, long addr, long dat ...@@ -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); clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
child->exit_code = data; child->exit_code = data;
/* make sure single-step breakpoint is gone. */ /* make sure single-step breakpoint is gone. */
__ptrace_cancel_bpt(child); child->ptrace &= ~PT_SINGLESTEP;
ptrace_cancel_bpt(child);
wake_up_process(child); wake_up_process(child);
ret = 0; ret = 0;
break; break;
...@@ -649,7 +651,8 @@ static int do_ptrace(int request, struct task_struct *child, long addr, long dat ...@@ -649,7 +651,8 @@ static int do_ptrace(int request, struct task_struct *child, long addr, long dat
*/ */
case PTRACE_KILL: case PTRACE_KILL:
/* make sure single-step breakpoint is gone. */ /* make sure single-step breakpoint is gone. */
__ptrace_cancel_bpt(child); child->ptrace &= ~PT_SINGLESTEP;
ptrace_cancel_bpt(child);
if (child->state != TASK_ZOMBIE) { if (child->state != TASK_ZOMBIE) {
child->exit_code = SIGKILL; child->exit_code = SIGKILL;
wake_up_process(child); wake_up_process(child);
...@@ -664,7 +667,7 @@ static int do_ptrace(int request, struct task_struct *child, long addr, long dat ...@@ -664,7 +667,7 @@ static int do_ptrace(int request, struct task_struct *child, long addr, long dat
ret = -EIO; ret = -EIO;
if ((unsigned long) data > _NSIG) if ((unsigned long) data > _NSIG)
break; break;
child->thread.debug.nsaved = -1; child->ptrace |= PT_SINGLESTEP;
clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
child->exit_code = data; child->exit_code = data;
/* give it a chance to run. */ /* give it a chance to run. */
......
/* /*
* linux/arch/arm/kernel/ptrace.h * 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 * 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 * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * 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_set_bpt(struct task_struct *);
extern void ptrace_break(struct task_struct *, struct pt_regs *); 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) ...@@ -216,8 +216,10 @@ asmlinkage int sys_sigreturn(struct pt_regs *regs)
goto badframe; goto badframe;
/* Send SIGTRAP if we're single-stepping */ /* 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); send_sig(SIGTRAP, current, 1);
}
return regs->ARM_r0; return regs->ARM_r0;
...@@ -256,8 +258,10 @@ asmlinkage int sys_rt_sigreturn(struct pt_regs *regs) ...@@ -256,8 +258,10 @@ asmlinkage int sys_rt_sigreturn(struct pt_regs *regs)
goto badframe; goto badframe;
/* Send SIGTRAP if we're single-stepping */ /* 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); send_sig(SIGTRAP, current, 1);
}
return regs->ARM_r0; return regs->ARM_r0;
...@@ -441,18 +445,47 @@ setup_rt_frame(int usig, struct k_sigaction *ka, siginfo_t *info, ...@@ -441,18 +445,47 @@ setup_rt_frame(int usig, struct k_sigaction *ka, siginfo_t *info,
return err; 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 * OK, we're invoking a handler
*/ */
static void static void
handle_signal(unsigned long sig, struct k_sigaction *ka, handle_signal(unsigned long sig, siginfo_t *info, sigset_t *oldset,
siginfo_t *info, sigset_t *oldset, struct pt_regs * regs) struct pt_regs * regs, int syscall)
{ {
struct thread_info *thread = current_thread_info(); struct thread_info *thread = current_thread_info();
struct task_struct *tsk = current; struct task_struct *tsk = current;
struct k_sigaction *ka = &tsk->sighand->action[sig-1];
int usig = sig; int usig = sig;
int ret; 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 * translate the signal
*/ */
...@@ -504,7 +537,7 @@ handle_signal(unsigned long sig, struct k_sigaction *ka, ...@@ -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) static int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall)
{ {
siginfo_t info; siginfo_t info;
int single_stepping; int signr;
/* /*
* We want the common case to go fast, which * 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) ...@@ -515,130 +548,14 @@ static int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall)
if (!user_mode(regs)) if (!user_mode(regs))
return 0; return 0;
single_stepping = ptrace_cancel_bpt(current); if (current->ptrace & PT_SINGLESTEP)
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_PTRACED) && signr != SIGKILL) { signr = get_signal_to_deliver(&info, regs, NULL);
/* Let the debugger run. */ if (signr > 0) {
current->exit_code = signr; handle_signal(signr, &info, oldset, regs, syscall);
set_current_state(TASK_STOPPED); if (current->ptrace & PT_SINGLESTEP)
notify_parent(current, SIGCHLD); ptrace_set_bpt(current);
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);
return 1; return 1;
} }
...@@ -668,11 +585,10 @@ static int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall) ...@@ -668,11 +585,10 @@ static int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall)
if (regs->ARM_r0 == -ERESTARTNOHAND || if (regs->ARM_r0 == -ERESTARTNOHAND ||
regs->ARM_r0 == -ERESTARTSYS || regs->ARM_r0 == -ERESTARTSYS ||
regs->ARM_r0 == -ERESTARTNOINTR) { regs->ARM_r0 == -ERESTARTNOINTR) {
regs->ARM_r0 = regs->ARM_ORIG_r0; restart_syscall(regs);
regs->ARM_pc -= 4;
} }
} }
if (single_stepping) if (current->ptrace & PT_SINGLESTEP)
ptrace_set_bpt(current); ptrace_set_bpt(current);
return 0; return 0;
} }
......
...@@ -184,9 +184,7 @@ typedef struct sigaltstack { ...@@ -184,9 +184,7 @@ typedef struct sigaltstack {
#ifdef __KERNEL__ #ifdef __KERNEL__
#include <asm/sigcontext.h> #include <asm/sigcontext.h>
#define ptrace_signal_deliver(regs, cookie) do { } while (0)
#define HAVE_ARCH_GET_SIGNAL_TO_DELIVER
#endif #endif
#endif #endif
...@@ -460,6 +460,7 @@ do { if (atomic_dec_and_test(&(tsk)->usage)) __put_task_struct(tsk); } while(0) ...@@ -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_EXEC 0x00000080
#define PT_TRACE_VFORK_DONE 0x00000100 #define PT_TRACE_VFORK_DONE 0x00000100
#define PT_TRACE_EXIT 0x00000200 #define PT_TRACE_EXIT 0x00000200
#define PT_SINGLESTEP 0x80000000 /* single step mode */
#if CONFIG_SMP #if CONFIG_SMP
extern void set_cpus_allowed(task_t *p, unsigned long new_mask); 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