[PATCH] x86_64: clean up ptrace single-stepping
Ported from i386 (originally from Linus) clean up ptrace single-stepping, make PT_DTRACE exact. (This makes the naming of "DTRACE" purely historical, since on x86 it now means "single step in progress"). Signed-off-by: Andi Kleen <ak@suse.de> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
This commit is contained in:
parent
b6d9a5d81c
commit
aa85b9af5b
|
@ -63,6 +63,12 @@ static inline unsigned long get_stack_long(struct task_struct *task, int offset)
|
||||||
return (*((unsigned long *)stack));
|
return (*((unsigned long *)stack));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline struct pt_regs *get_child_regs(struct task_struct *task)
|
||||||
|
{
|
||||||
|
struct pt_regs *regs = (void *)task->thread.rsp0;
|
||||||
|
return regs - 1;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* this routine will put a word on the processes privileged stack.
|
* this routine will put a word on the processes privileged stack.
|
||||||
* the offset is how far from the base addr as stored in the TSS.
|
* the offset is how far from the base addr as stored in the TSS.
|
||||||
|
@ -80,6 +86,42 @@ static inline long put_stack_long(struct task_struct *task, int offset,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void set_singlestep(struct task_struct *child)
|
||||||
|
{
|
||||||
|
struct pt_regs *regs = get_child_regs(child);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Always set TIF_SINGLESTEP - this guarantees that
|
||||||
|
* we single-step system calls etc.. This will also
|
||||||
|
* cause us to set TF when returning to user mode.
|
||||||
|
*/
|
||||||
|
set_tsk_thread_flag(child, TIF_SINGLESTEP);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If TF was already set, don't do anything else
|
||||||
|
*/
|
||||||
|
if (regs->eflags & TRAP_FLAG)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Set TF on the kernel stack.. */
|
||||||
|
regs->eflags |= TRAP_FLAG;
|
||||||
|
|
||||||
|
child->ptrace |= PT_DTRACE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void clear_singlestep(struct task_struct *child)
|
||||||
|
{
|
||||||
|
/* Always clear TIF_SINGLESTEP... */
|
||||||
|
clear_tsk_thread_flag(child, TIF_SINGLESTEP);
|
||||||
|
|
||||||
|
/* But touch TF only if it was set by us.. */
|
||||||
|
if (child->ptrace & PT_DTRACE) {
|
||||||
|
struct pt_regs *regs = get_child_regs(child);
|
||||||
|
regs->eflags &= ~TRAP_FLAG;
|
||||||
|
child->ptrace &= ~PT_DTRACE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Called by kernel/ptrace.c when detaching..
|
* Called by kernel/ptrace.c when detaching..
|
||||||
*
|
*
|
||||||
|
@ -87,11 +129,7 @@ static inline long put_stack_long(struct task_struct *task, int offset,
|
||||||
*/
|
*/
|
||||||
void ptrace_disable(struct task_struct *child)
|
void ptrace_disable(struct task_struct *child)
|
||||||
{
|
{
|
||||||
long tmp;
|
clear_singlestep(child);
|
||||||
|
|
||||||
clear_tsk_thread_flag(child, TIF_SINGLESTEP);
|
|
||||||
tmp = get_stack_long(child, EFL_OFFSET) & ~TRAP_FLAG;
|
|
||||||
put_stack_long(child, EFL_OFFSET, tmp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int putreg(struct task_struct *child,
|
static int putreg(struct task_struct *child,
|
||||||
|
@ -338,8 +376,7 @@ asmlinkage long sys_ptrace(long request, long pid, unsigned long addr, long data
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
|
case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
|
||||||
case PTRACE_CONT: { /* restart after signal. */
|
case PTRACE_CONT: /* restart after signal. */
|
||||||
long tmp;
|
|
||||||
|
|
||||||
ret = -EIO;
|
ret = -EIO;
|
||||||
if ((unsigned long) data > _NSIG)
|
if ((unsigned long) data > _NSIG)
|
||||||
|
@ -350,14 +387,11 @@ asmlinkage long sys_ptrace(long request, long pid, unsigned long addr, long data
|
||||||
clear_tsk_thread_flag(child,TIF_SYSCALL_TRACE);
|
clear_tsk_thread_flag(child,TIF_SYSCALL_TRACE);
|
||||||
clear_tsk_thread_flag(child, TIF_SINGLESTEP);
|
clear_tsk_thread_flag(child, TIF_SINGLESTEP);
|
||||||
child->exit_code = data;
|
child->exit_code = data;
|
||||||
/* make sure the single step bit is not set. */
|
/* make sure the single step bit is not set. */
|
||||||
tmp = get_stack_long(child, EFL_OFFSET);
|
clear_singlestep(child);
|
||||||
tmp &= ~TRAP_FLAG;
|
|
||||||
put_stack_long(child, EFL_OFFSET,tmp);
|
|
||||||
wake_up_process(child);
|
wake_up_process(child);
|
||||||
ret = 0;
|
ret = 0;
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CONFIG_IA32_EMULATION
|
#ifdef CONFIG_IA32_EMULATION
|
||||||
/* This makes only sense with 32bit programs. Allow a
|
/* This makes only sense with 32bit programs. Allow a
|
||||||
|
@ -394,41 +428,28 @@ asmlinkage long sys_ptrace(long request, long pid, unsigned long addr, long data
|
||||||
* perhaps it should be put in the status that it wants to
|
* perhaps it should be put in the status that it wants to
|
||||||
* exit.
|
* exit.
|
||||||
*/
|
*/
|
||||||
case PTRACE_KILL: {
|
case PTRACE_KILL:
|
||||||
long tmp;
|
|
||||||
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
if (child->exit_state == EXIT_ZOMBIE) /* already dead */
|
if (child->exit_state == EXIT_ZOMBIE) /* already dead */
|
||||||
break;
|
break;
|
||||||
clear_tsk_thread_flag(child, TIF_SINGLESTEP);
|
clear_tsk_thread_flag(child, TIF_SINGLESTEP);
|
||||||
child->exit_code = SIGKILL;
|
child->exit_code = SIGKILL;
|
||||||
/* make sure the single step bit is not set. */
|
/* make sure the single step bit is not set. */
|
||||||
tmp = get_stack_long(child, EFL_OFFSET) & ~TRAP_FLAG;
|
clear_singlestep(child);
|
||||||
put_stack_long(child, EFL_OFFSET, tmp);
|
|
||||||
wake_up_process(child);
|
wake_up_process(child);
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
case PTRACE_SINGLESTEP: { /* set the trap flag. */
|
|
||||||
long tmp;
|
|
||||||
|
|
||||||
|
case PTRACE_SINGLESTEP: /* set the trap flag. */
|
||||||
ret = -EIO;
|
ret = -EIO;
|
||||||
if ((unsigned long) data > _NSIG)
|
if ((unsigned long) data > _NSIG)
|
||||||
break;
|
break;
|
||||||
clear_tsk_thread_flag(child,TIF_SYSCALL_TRACE);
|
clear_tsk_thread_flag(child,TIF_SYSCALL_TRACE);
|
||||||
if ((child->ptrace & PT_DTRACE) == 0) {
|
set_singlestep(child);
|
||||||
/* Spurious delayed TF traps may occur */
|
|
||||||
child->ptrace |= PT_DTRACE;
|
|
||||||
}
|
|
||||||
tmp = get_stack_long(child, EFL_OFFSET) | TRAP_FLAG;
|
|
||||||
put_stack_long(child, EFL_OFFSET, tmp);
|
|
||||||
set_tsk_thread_flag(child, TIF_SINGLESTEP);
|
|
||||||
child->exit_code = data;
|
child->exit_code = data;
|
||||||
/* give it a chance to run. */
|
/* give it a chance to run. */
|
||||||
wake_up_process(child);
|
wake_up_process(child);
|
||||||
ret = 0;
|
ret = 0;
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
case PTRACE_DETACH:
|
case PTRACE_DETACH:
|
||||||
/* detach a process that was attached. */
|
/* detach a process that was attached. */
|
||||||
|
|
Loading…
Reference in New Issue