Commit 687bc18d authored by Daniel Jacobowitz's avatar Daniel Jacobowitz

Hand merge

parents 8eae2998 88d9b869
......@@ -26,6 +26,8 @@
/* 0x4200-0x4300 are reserved for architecture-independent additions. */
#define PTRACE_SETOPTIONS 0x4200
#define PTRACE_GETEVENTMSG 0x4201
#define PTRACE_GETSIGINFO 0x4202
#define PTRACE_SETSIGINFO 0x4203
/* options set using PTRACE_SETOPTIONS */
#define PTRACE_O_TRACESYSGOOD 0x00000001
......@@ -33,12 +35,16 @@
#define PTRACE_O_TRACEVFORK 0x00000004
#define PTRACE_O_TRACECLONE 0x00000008
#define PTRACE_O_TRACEEXEC 0x00000010
#define PTRACE_O_TRACEVFORKDONE 0x00000020
#define PTRACE_O_TRACEEXIT 0x00000040
/* Wait extended result codes for the above trace options. */
#define PTRACE_EVENT_FORK 1
#define PTRACE_EVENT_VFORK 2
#define PTRACE_EVENT_CLONE 3
#define PTRACE_EVENT_EXEC 4
#define PTRACE_EVENT_VFORK_DONE 5
#define PTRACE_EVENT_EXIT 6
#include <asm/ptrace.h>
#include <linux/sched.h>
......
......@@ -417,6 +417,7 @@ struct task_struct {
struct backing_dev_info *backing_dev_info;
unsigned long ptrace_message;
siginfo_t *last_siginfo; /* For ptrace use. */
};
extern void __put_task_struct(struct task_struct *tsk);
......@@ -457,6 +458,8 @@ do { if (atomic_dec_and_test(&(tsk)->usage)) __put_task_struct(tsk); } while(0)
#define PT_TRACE_VFORK 0x00000020
#define PT_TRACE_CLONE 0x00000040
#define PT_TRACE_EXEC 0x00000080
#define PT_TRACE_VFORK_DONE 0x00000100
#define PT_TRACE_EXIT 0x00000200
#if CONFIG_SMP
extern void set_cpus_allowed(task_t *p, unsigned long new_mask);
......
......@@ -199,6 +199,17 @@ static inline int has_stopped_jobs(int pgrp)
for_each_task_pid(pgrp, PIDTYPE_PGID, p, l, pid) {
if (p->state != TASK_STOPPED)
continue;
/* If p is stopped by a debugger on a signal that won't
stop it, then don't count p as stopped. This isn't
perfect but it's a good approximation. */
if (unlikely (p->ptrace)
&& p->exit_code != SIGSTOP
&& p->exit_code != SIGTSTP
&& p->exit_code != SIGTTOU
&& p->exit_code != SIGTTIN)
continue;
retval = 1;
break;
}
......@@ -594,7 +605,7 @@ static void exit_notify(struct task_struct *tsk)
* is about to become orphaned.
*/
t = tsk->parent;
t = tsk->real_parent;
if ((t->pgrp != tsk->pgrp) &&
(t->session == tsk->session) &&
......@@ -627,8 +638,16 @@ static void exit_notify(struct task_struct *tsk)
tsk->exit_signal = SIGCHLD;
if (tsk->exit_signal != -1)
do_notify_parent(tsk, tsk->exit_signal);
/* If something other than our normal parent is ptracing us, then
* send it a SIGCHLD instead of honoring exit_signal. exit_signal
* only has special meaning to our real parent.
*/
if (tsk->exit_signal != -1) {
if (tsk->parent == tsk->real_parent)
do_notify_parent(tsk, tsk->exit_signal);
else
do_notify_parent(tsk, SIGCHLD);
}
tsk->state = TASK_ZOMBIE;
/*
......@@ -661,6 +680,9 @@ NORET_TYPE void do_exit(long code)
profile_exit_task(tsk);
if (unlikely(current->ptrace & PT_TRACE_EXIT))
ptrace_notify((PTRACE_EVENT_EXIT << 8) | SIGTRAP);
acct_process(code);
__exit_mm(tsk);
......
......@@ -1085,9 +1085,11 @@ struct task_struct *do_fork(unsigned long clone_flags,
ptrace_notify ((trace << 8) | SIGTRAP);
}
if (clone_flags & CLONE_VFORK)
if (clone_flags & CLONE_VFORK) {
wait_for_completion(&vfork);
else
if (unlikely (current->ptrace & PT_TRACE_VFORK_DONE))
ptrace_notify ((PTRACE_EVENT_VFORK_DONE << 8) | SIGTRAP);
} else
/*
* Let the child process run first, to avoid most of the
* COW overhead when the child exec()s afterwards.
......
......@@ -277,15 +277,43 @@ static int ptrace_setoptions(struct task_struct *child, long data)
else
child->ptrace &= ~PT_TRACE_EXEC;
if (data & PTRACE_O_TRACEVFORKDONE)
child->ptrace |= PT_TRACE_VFORK_DONE;
else
child->ptrace &= ~PT_TRACE_VFORK_DONE;
if (data & PTRACE_O_TRACEEXIT)
child->ptrace |= PT_TRACE_EXIT;
else
child->ptrace &= ~PT_TRACE_EXIT;
if ((data & (PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEFORK
| PTRACE_O_TRACEVFORK | PTRACE_O_TRACECLONE
| PTRACE_O_TRACEEXEC))
| PTRACE_O_TRACEEXEC | PTRACE_O_TRACEEXIT
| PTRACE_O_TRACEVFORKDONE))
!= data)
return -EINVAL;
return 0;
}
static int ptrace_getsiginfo(struct task_struct *child, long data)
{
if (child->last_siginfo == NULL)
return -EINVAL;
return copy_siginfo_to_user ((siginfo_t *) data, child->last_siginfo);
}
static int ptrace_setsiginfo(struct task_struct *child, long data)
{
if (child->last_siginfo == NULL)
return -EINVAL;
if (copy_from_user (child->last_siginfo, (siginfo_t *) data,
sizeof (siginfo_t)) != 0)
return -EFAULT;
return 0;
}
int ptrace_request(struct task_struct *child, long request,
long addr, long data)
{
......@@ -301,6 +329,12 @@ int ptrace_request(struct task_struct *child, long request,
case PTRACE_GETEVENTMSG:
ret = put_user(child->ptrace_message, (unsigned long *) data);
break;
case PTRACE_GETSIGINFO:
ret = ptrace_getsiginfo(child, data);
break;
case PTRACE_SETSIGINFO:
ret = ptrace_setsiginfo(child, data);
break;
default:
break;
}
......
......@@ -1427,17 +1427,23 @@ int get_signal_to_deliver(siginfo_t *info, struct pt_regs *regs)
/* Let the debugger run. */
current->exit_code = signr;
current->last_siginfo = info;
set_current_state(TASK_STOPPED);
notify_parent(current, SIGCHLD);
schedule();
current->last_siginfo = NULL;
/* We're back. Did the debugger cancel the sig? */
signr = current->exit_code;
if (signr == 0)
continue;
current->exit_code = 0;
/* 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;
......
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