aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndi Kleen <ak@suse.de>2005-04-16 15:24:56 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:24:56 -0700
commitaa85b9af5bdae1f8b84d80367324e4410c3f0674 (patch)
treea312656fa0b997000a859e88f357f23d2e25cdef
parentb6d9a5d81c6159ad651b5c6bb9223d14e33d8033 (diff)
downloadlinux-aa85b9af5bdae1f8b84d80367324e4410c3f0674.tar.gz
[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>
-rw-r--r--arch/x86_64/kernel/ptrace.c79
1 files changed, 50 insertions, 29 deletions
diff --git a/arch/x86_64/kernel/ptrace.c b/arch/x86_64/kernel/ptrace.c
index 0b7b101debdf4..002237c3b6a2d 100644
--- a/arch/x86_64/kernel/ptrace.c
+++ b/arch/x86_64/kernel/ptrace.c
@@ -63,6 +63,12 @@ static inline unsigned long get_stack_long(struct task_struct *task, int offset)
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.
* 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;
}
+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..
*
@@ -87,11 +129,7 @@ static inline long put_stack_long(struct task_struct *task, int offset,
*/
void ptrace_disable(struct task_struct *child)
{
- long tmp;
-
- clear_tsk_thread_flag(child, TIF_SINGLESTEP);
- tmp = get_stack_long(child, EFL_OFFSET) & ~TRAP_FLAG;
- put_stack_long(child, EFL_OFFSET, tmp);
+ clear_singlestep(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;
case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
- case PTRACE_CONT: { /* restart after signal. */
- long tmp;
+ case PTRACE_CONT: /* restart after signal. */
ret = -EIO;
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_SINGLESTEP);
child->exit_code = data;
- /* make sure the single step bit is not set. */
- tmp = get_stack_long(child, EFL_OFFSET);
- tmp &= ~TRAP_FLAG;
- put_stack_long(child, EFL_OFFSET,tmp);
+ /* make sure the single step bit is not set. */
+ clear_singlestep(child);
wake_up_process(child);
ret = 0;
break;
- }
#ifdef CONFIG_IA32_EMULATION
/* 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
* exit.
*/
- case PTRACE_KILL: {
- long tmp;
-
+ case PTRACE_KILL:
ret = 0;
if (child->exit_state == EXIT_ZOMBIE) /* already dead */
break;
clear_tsk_thread_flag(child, TIF_SINGLESTEP);
child->exit_code = SIGKILL;
/* make sure the single step bit is not set. */
- tmp = get_stack_long(child, EFL_OFFSET) & ~TRAP_FLAG;
- put_stack_long(child, EFL_OFFSET, tmp);
+ clear_singlestep(child);
wake_up_process(child);
break;
- }
-
- case PTRACE_SINGLESTEP: { /* set the trap flag. */
- long tmp;
+ case PTRACE_SINGLESTEP: /* set the trap flag. */
ret = -EIO;
if ((unsigned long) data > _NSIG)
break;
clear_tsk_thread_flag(child,TIF_SYSCALL_TRACE);
- if ((child->ptrace & PT_DTRACE) == 0) {
- /* 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);
+ set_singlestep(child);
child->exit_code = data;
/* give it a chance to run. */
wake_up_process(child);
ret = 0;
break;
- }
case PTRACE_DETACH:
/* detach a process that was attached. */