aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorAndi Kleen <ak@suse.de>2005-04-16 18:24:56 -0400
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:24:56 -0400
commitaa85b9af5bdae1f8b84d80367324e4410c3f0674 (patch)
treea312656fa0b997000a859e88f357f23d2e25cdef /arch
parentb6d9a5d81c6159ad651b5c6bb9223d14e33d8033 (diff)
[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>
Diffstat (limited to 'arch')
-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 0b7b101debdf..002237c3b6a2 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)
63 return (*((unsigned long *)stack)); 63 return (*((unsigned long *)stack));
64} 64}
65 65
66static inline struct pt_regs *get_child_regs(struct task_struct *task)
67{
68 struct pt_regs *regs = (void *)task->thread.rsp0;
69 return regs - 1;
70}
71
66/* 72/*
67 * this routine will put a word on the processes privileged stack. 73 * this routine will put a word on the processes privileged stack.
68 * the offset is how far from the base addr as stored in the TSS. 74 * 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,
80 return 0; 86 return 0;
81} 87}
82 88
89static void set_singlestep(struct task_struct *child)
90{
91 struct pt_regs *regs = get_child_regs(child);
92
93 /*
94 * Always set TIF_SINGLESTEP - this guarantees that
95 * we single-step system calls etc.. This will also
96 * cause us to set TF when returning to user mode.
97 */
98 set_tsk_thread_flag(child, TIF_SINGLESTEP);
99
100 /*
101 * If TF was already set, don't do anything else
102 */
103 if (regs->eflags & TRAP_FLAG)
104 return;
105
106 /* Set TF on the kernel stack.. */
107 regs->eflags |= TRAP_FLAG;
108
109 child->ptrace |= PT_DTRACE;
110}
111
112static void clear_singlestep(struct task_struct *child)
113{
114 /* Always clear TIF_SINGLESTEP... */
115 clear_tsk_thread_flag(child, TIF_SINGLESTEP);
116
117 /* But touch TF only if it was set by us.. */
118 if (child->ptrace & PT_DTRACE) {
119 struct pt_regs *regs = get_child_regs(child);
120 regs->eflags &= ~TRAP_FLAG;
121 child->ptrace &= ~PT_DTRACE;
122 }
123}
124
83/* 125/*
84 * Called by kernel/ptrace.c when detaching.. 126 * Called by kernel/ptrace.c when detaching..
85 * 127 *
@@ -87,11 +129,7 @@ static inline long put_stack_long(struct task_struct *task, int offset,
87 */ 129 */
88void ptrace_disable(struct task_struct *child) 130void ptrace_disable(struct task_struct *child)
89{ 131{
90 long tmp; 132 clear_singlestep(child);
91
92 clear_tsk_thread_flag(child, TIF_SINGLESTEP);
93 tmp = get_stack_long(child, EFL_OFFSET) & ~TRAP_FLAG;
94 put_stack_long(child, EFL_OFFSET, tmp);
95} 133}
96 134
97static int putreg(struct task_struct *child, 135static int putreg(struct task_struct *child,
@@ -338,8 +376,7 @@ asmlinkage long sys_ptrace(long request, long pid, unsigned long addr, long data
338 } 376 }
339 break; 377 break;
340 case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ 378 case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
341 case PTRACE_CONT: { /* restart after signal. */ 379 case PTRACE_CONT: /* restart after signal. */
342 long tmp;
343 380
344 ret = -EIO; 381 ret = -EIO;
345 if ((unsigned long) data > _NSIG) 382 if ((unsigned long) data > _NSIG)
@@ -350,14 +387,11 @@ asmlinkage long sys_ptrace(long request, long pid, unsigned long addr, long data
350 clear_tsk_thread_flag(child,TIF_SYSCALL_TRACE); 387 clear_tsk_thread_flag(child,TIF_SYSCALL_TRACE);
351 clear_tsk_thread_flag(child, TIF_SINGLESTEP); 388 clear_tsk_thread_flag(child, TIF_SINGLESTEP);
352 child->exit_code = data; 389 child->exit_code = data;
353 /* make sure the single step bit is not set. */ 390 /* make sure the single step bit is not set. */
354 tmp = get_stack_long(child, EFL_OFFSET); 391 clear_singlestep(child);
355 tmp &= ~TRAP_FLAG;
356 put_stack_long(child, EFL_OFFSET,tmp);
357 wake_up_process(child); 392 wake_up_process(child);
358 ret = 0; 393 ret = 0;
359 break; 394 break;
360 }
361 395
362#ifdef CONFIG_IA32_EMULATION 396#ifdef CONFIG_IA32_EMULATION
363 /* This makes only sense with 32bit programs. Allow a 397 /* 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
394 * perhaps it should be put in the status that it wants to 428 * perhaps it should be put in the status that it wants to
395 * exit. 429 * exit.
396 */ 430 */
397 case PTRACE_KILL: { 431 case PTRACE_KILL:
398 long tmp;
399
400 ret = 0; 432 ret = 0;
401 if (child->exit_state == EXIT_ZOMBIE) /* already dead */ 433 if (child->exit_state == EXIT_ZOMBIE) /* already dead */
402 break; 434 break;
403 clear_tsk_thread_flag(child, TIF_SINGLESTEP); 435 clear_tsk_thread_flag(child, TIF_SINGLESTEP);
404 child->exit_code = SIGKILL; 436 child->exit_code = SIGKILL;
405 /* make sure the single step bit is not set. */ 437 /* make sure the single step bit is not set. */
406 tmp = get_stack_long(child, EFL_OFFSET) & ~TRAP_FLAG; 438 clear_singlestep(child);
407 put_stack_long(child, EFL_OFFSET, tmp);
408 wake_up_process(child); 439 wake_up_process(child);
409 break; 440 break;
410 }
411
412 case PTRACE_SINGLESTEP: { /* set the trap flag. */
413 long tmp;
414 441
442 case PTRACE_SINGLESTEP: /* set the trap flag. */
415 ret = -EIO; 443 ret = -EIO;
416 if ((unsigned long) data > _NSIG) 444 if ((unsigned long) data > _NSIG)
417 break; 445 break;
418 clear_tsk_thread_flag(child,TIF_SYSCALL_TRACE); 446 clear_tsk_thread_flag(child,TIF_SYSCALL_TRACE);
419 if ((child->ptrace & PT_DTRACE) == 0) { 447 set_singlestep(child);
420 /* Spurious delayed TF traps may occur */
421 child->ptrace |= PT_DTRACE;
422 }
423 tmp = get_stack_long(child, EFL_OFFSET) | TRAP_FLAG;
424 put_stack_long(child, EFL_OFFSET, tmp);
425 set_tsk_thread_flag(child, TIF_SINGLESTEP);
426 child->exit_code = data; 448 child->exit_code = data;
427 /* give it a chance to run. */ 449 /* give it a chance to run. */
428 wake_up_process(child); 450 wake_up_process(child);
429 ret = 0; 451 ret = 0;
430 break; 452 break;
431 }
432 453
433 case PTRACE_DETACH: 454 case PTRACE_DETACH:
434 /* detach a process that was attached. */ 455 /* detach a process that was attached. */