[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:
Andi Kleen 2005-04-16 15:24:56 -07:00 committed by Linus Torvalds
parent b6d9a5d81c
commit aa85b9af5b
1 changed files with 50 additions and 29 deletions

View File

@ -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. */