diff options
author | Andi Kleen <ak@suse.de> | 2005-04-16 18:24:56 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:24:56 -0400 |
commit | aa85b9af5bdae1f8b84d80367324e4410c3f0674 (patch) | |
tree | a312656fa0b997000a859e88f357f23d2e25cdef /arch/x86_64/kernel | |
parent | b6d9a5d81c6159ad651b5c6bb9223d14e33d8033 (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/x86_64/kernel')
-rw-r--r-- | arch/x86_64/kernel/ptrace.c | 79 |
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 | ||
66 | static 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 | ||
89 | static 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 | |||
112 | static 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 | */ |
88 | void ptrace_disable(struct task_struct *child) | 130 | void 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 | ||
97 | static int putreg(struct task_struct *child, | 135 | static 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. */ |