2 * linux/arch/arm/kernel/ptrace.c
5 * edited by Linus Torvalds
6 * ARM modifications Copyright (C) 2000 Russell King
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
12 #include <linux/kernel.h>
13 #include <linux/sched.h>
15 #include <linux/smp.h>
16 #include <linux/smp_lock.h>
17 #include <linux/ptrace.h>
18 #include <linux/user.h>
20 #include <asm/uaccess.h>
21 #include <asm/pgtable.h>
22 #include <asm/system.h>
29 * does not yet catch signals sent when the child dies.
30 * in exit.c or in signal.c.
34 * Breakpoint SWI instruction: SWI &9F0001
36 #define BREAKINST 0xef9f0001
39 * Get the address of the live pt_regs for the specified task.
40 * These are saved onto the top kernel stack when the process
43 static inline struct pt_regs
*
44 get_user_regs(struct task_struct
*task
)
46 return (struct pt_regs
*)
47 ((unsigned long)task
+ 8192 - sizeof(struct pt_regs
));
51 * this routine will get a word off of the processes privileged stack.
52 * the offset is how far from the base addr as stored in the THREAD.
53 * this routine assumes that all the privileged stacks are in our
56 static inline long get_stack_long(struct task_struct
*task
, int offset
)
58 return get_user_regs(task
)->uregs
[offset
];
62 * this routine will put a word on the processes privileged stack.
63 * the offset is how far from the base addr as stored in the THREAD.
64 * this routine assumes that all the privileged stacks are in our
68 put_stack_long(struct task_struct
*task
, int offset
, long data
)
70 struct pt_regs newregs
, *regs
= get_user_regs(task
);
74 newregs
.uregs
[offset
] = data
;
76 if (valid_user_regs(&newregs
)) {
77 regs
->uregs
[offset
] = data
;
85 read_tsk_long(struct task_struct
*child
, unsigned long addr
, unsigned long *res
)
89 copied
= access_process_vm(child
, addr
, res
, sizeof(*res
), 0);
91 return copied
!= sizeof(*res
) ? -EIO
: 0;
95 write_tsk_long(struct task_struct
*child
, unsigned long addr
, unsigned long val
)
99 copied
= access_process_vm(child
, addr
, &val
, sizeof(val
), 1);
101 return copied
!= sizeof(val
) ? -EIO
: 0;
105 * Get value of register `rn' (in the instruction)
108 ptrace_getrn(struct task_struct
*child
, unsigned long insn
)
110 unsigned int reg
= (insn
>> 16) & 15;
113 val
= get_stack_long(child
, reg
);
115 val
= pc_pointer(val
);
121 * Get value of operand 2 (in an ALU instruction)
124 ptrace_getaluop2(struct task_struct
*child
, unsigned long insn
)
130 if (insn
& 1 << 25) {
132 shift
= (insn
>> 8) & 15;
135 val
= get_stack_long (child
, insn
& 15);
138 shift
= (int)get_stack_long (child
, (insn
>> 8) & 15);
140 shift
= (insn
>> 7) & 31;
142 type
= (insn
>> 5) & 3;
146 case 0: val
<<= shift
; break;
147 case 1: val
>>= shift
; break;
149 val
= (((signed long)val
) >> shift
);
152 val
= (val
>> shift
) | (val
<< (32 - shift
));
159 * Get value of operand 2 (in a LDR instruction)
162 ptrace_getldrop2(struct task_struct
*child
, unsigned long insn
)
168 val
= get_stack_long(child
, insn
& 15);
169 shift
= (insn
>> 7) & 31;
170 type
= (insn
>> 5) & 3;
173 case 0: val
<<= shift
; break;
174 case 1: val
>>= shift
; break;
176 val
= (((signed long)val
) >> shift
);
179 val
= (val
>> shift
) | (val
<< (32 - shift
));
186 get_branch_address(struct task_struct
*child
, unsigned long pc
, unsigned long insn
)
188 unsigned long alt
= 0;
190 switch (insn
& 0x0e000000) {
196 long aluop1
, aluop2
, ccbit
;
198 if ((insn
& 0xf000) != 0xf000)
201 aluop1
= ptrace_getrn(child
, insn
);
202 aluop2
= ptrace_getaluop2(child
, insn
);
203 ccbit
= get_stack_long(child
, REG_PSR
) & CC_C_BIT
? 1 : 0;
205 switch (insn
& 0x01e00000) {
206 case 0x00000000: alt
= aluop1
& aluop2
; break;
207 case 0x00200000: alt
= aluop1
^ aluop2
; break;
208 case 0x00400000: alt
= aluop1
- aluop2
; break;
209 case 0x00600000: alt
= aluop2
- aluop1
; break;
210 case 0x00800000: alt
= aluop1
+ aluop2
; break;
211 case 0x00a00000: alt
= aluop1
+ aluop2
+ ccbit
; break;
212 case 0x00c00000: alt
= aluop1
- aluop2
+ ccbit
; break;
213 case 0x00e00000: alt
= aluop2
- aluop1
+ ccbit
; break;
214 case 0x01800000: alt
= aluop1
| aluop2
; break;
215 case 0x01a00000: alt
= aluop2
; break;
216 case 0x01c00000: alt
= aluop1
& ~aluop2
; break;
217 case 0x01e00000: alt
= ~aluop2
; break;
227 if ((insn
& 0x0010f000) == 0x0010f000) {
230 base
= ptrace_getrn(child
, insn
);
231 if (insn
& 1 << 24) {
234 if (insn
& 0x02000000)
235 aluop2
= ptrace_getldrop2(child
, insn
);
237 aluop2
= insn
& 0xfff;
244 if (read_tsk_long(child
, base
, &alt
) == 0)
245 alt
= pc_pointer(alt
);
253 if ((insn
& 0x00108000) == 0x00108000) {
255 unsigned int nr_regs
;
257 if (insn
& (1 << 23)) {
258 nr_regs
= insn
& 65535;
260 nr_regs
= (nr_regs
& 0x5555) + ((nr_regs
& 0xaaaa) >> 1);
261 nr_regs
= (nr_regs
& 0x3333) + ((nr_regs
& 0xcccc) >> 2);
262 nr_regs
= (nr_regs
& 0x0707) + ((nr_regs
& 0x7070) >> 4);
263 nr_regs
= (nr_regs
& 0x000f) + ((nr_regs
& 0x0f00) >> 8);
266 if (!(insn
& (1 << 24)))
269 if (insn
& (1 << 24))
275 base
= ptrace_getrn(child
, insn
);
277 if (read_tsk_long(child
, base
+ nr_regs
, &alt
) == 0)
278 alt
= pc_pointer (alt
);
288 /* It's a branch/branch link: instead of trying to
289 * figure out whether the branch will be taken or not,
290 * we'll put a breakpoint at both locations. This is
291 * simpler, more reliable, and probably not a whole lot
292 * slower than the alternative approach of emulating the
295 displ
= (insn
& 0x00ffffff) << 8;
296 displ
= (displ
>> 6) + 8;
297 if (displ
!= 0 && displ
!= 4)
307 add_breakpoint(struct task_struct
*child
, struct debug_info
*dbg
, unsigned long addr
)
309 int nr
= dbg
->nsaved
;
313 res
= read_tsk_long(child
, addr
, &dbg
->bp
[nr
].insn
);
315 res
= write_tsk_long(child
, addr
, BREAKINST
);
318 dbg
->bp
[nr
].address
= addr
;
322 printk(KERN_DEBUG
"add_breakpoint: too many breakpoints\n");
327 int ptrace_set_bpt(struct task_struct
*child
)
329 unsigned long insn
, pc
;
332 pc
= pc_pointer(get_stack_long(child
, REG_PC
));
334 res
= read_tsk_long(child
, pc
, &insn
);
336 struct debug_info
*dbg
= &child
->thread
.debug
;
341 alt
= get_branch_address(child
, pc
, insn
);
343 res
= add_breakpoint(child
, dbg
, alt
);
345 if (!res
&& (!alt
|| predicate(insn
) != PREDICATE_ALWAYS
))
346 res
= add_breakpoint(child
, dbg
, pc
+ 4);
353 * Ensure no single-step breakpoint is pending. Returns non-zero
354 * value if child was being single-stepped.
356 void __ptrace_cancel_bpt(struct task_struct
*child
)
358 struct debug_info
*dbg
= &child
->thread
.debug
;
359 int i
, nsaved
= dbg
->nsaved
;
364 printk("ptrace_cancel_bpt: bogus nsaved: %d!\n", nsaved
);
368 for (i
= 0; i
< nsaved
; i
++) {
371 read_tsk_long(child
, dbg
->bp
[i
].address
, &tmp
);
372 if (tmp
!= BREAKINST
)
373 printk(KERN_ERR
"ptrace_cancel_bpt: weirdness\n");
374 write_tsk_long(child
, dbg
->bp
[i
].address
, dbg
->bp
[i
].insn
);
378 static int do_ptrace(int request
, struct task_struct
*child
, long addr
, long data
)
385 * read word at location "addr" in the child process.
387 case PTRACE_PEEKTEXT
:
388 case PTRACE_PEEKDATA
:
389 ret
= read_tsk_long(child
, addr
, &tmp
);
391 ret
= put_user(tmp
, (unsigned long *) data
);
395 * read the word at location "addr" in the user registers.
399 if ((addr
& 3) || addr
< 0 || addr
>= sizeof(struct user
))
402 tmp
= 0; /* Default return condition */
403 if (addr
< sizeof(struct pt_regs
))
404 tmp
= get_stack_long(child
, (int)addr
>> 2);
405 ret
= put_user(tmp
, (unsigned long *)data
);
409 * write the word at location addr.
411 case PTRACE_POKETEXT
:
412 case PTRACE_POKEDATA
:
413 ret
= write_tsk_long(child
, addr
, data
);
417 * write the word at location addr in the user registers.
421 if ((addr
& 3) || addr
< 0 || addr
>= sizeof(struct user
))
424 if (addr
< sizeof(struct pt_regs
))
425 ret
= put_stack_long(child
, (int)addr
>> 2, data
);
429 * continue/restart and stop at next (return from) syscall
434 if ((unsigned long) data
> _NSIG
)
436 if (request
== PTRACE_SYSCALL
)
437 child
->ptrace
|= PT_TRACESYS
;
439 child
->ptrace
&= ~PT_TRACESYS
;
440 child
->exit_code
= data
;
441 /* make sure single-step breakpoint is gone. */
442 __ptrace_cancel_bpt(child
);
443 wake_up_process(child
);
448 * make the child exit. Best I can do is send it a sigkill.
449 * perhaps it should be put in the status that it wants to
455 if (child
->state
== TASK_ZOMBIE
)
457 child
->exit_code
= SIGKILL
;
458 /* make sure single-step breakpoint is gone. */
459 __ptrace_cancel_bpt(child
);
460 wake_up_process(child
);
465 * execute single instruction.
467 case PTRACE_SINGLESTEP
:
469 if ((unsigned long) data
> _NSIG
)
471 child
->thread
.debug
.nsaved
= -1;
472 child
->ptrace
&= ~PT_TRACESYS
;
473 child
->exit_code
= data
;
474 /* give it a chance to run. */
475 wake_up_process(child
);
480 * detach a process that was attached.
484 if ((unsigned long) data
> _NSIG
)
486 child
->ptrace
&= ~(PT_PTRACED
|PT_TRACESYS
);
487 child
->exit_code
= data
;
488 write_lock_irq(&tasklist_lock
);
490 child
->p_pptr
= child
->p_opptr
;
492 write_unlock_irq(&tasklist_lock
);
493 /* make sure single-step breakpoint is gone. */
494 __ptrace_cancel_bpt(child
);
495 wake_up_process (child
);
500 * Get all gp regs from the child.
502 case PTRACE_GETREGS
: {
503 struct pt_regs
*regs
= get_user_regs(child
);
506 if (copy_to_user((void *)data
, regs
,
507 sizeof(struct pt_regs
)))
514 * Set all gp regs in the child.
516 case PTRACE_SETREGS
: {
517 struct pt_regs newregs
;
520 if (copy_from_user(&newregs
, (void *)data
,
521 sizeof(struct pt_regs
)) == 0) {
522 struct pt_regs
*regs
= get_user_regs(child
);
525 if (valid_user_regs(&newregs
)) {
534 * Get the child FPU state.
536 case PTRACE_GETFPREGS
:
538 if (!access_ok(VERIFY_WRITE
, (void *)data
, sizeof(struct user_fp
)))
541 /* we should check child->used_math here */
542 ret
= __copy_to_user((void *)data
, &child
->thread
.fpstate
,
543 sizeof(struct user_fp
)) ? -EFAULT
: 0;
547 * Set the child FPU state.
549 case PTRACE_SETFPREGS
:
551 if (!access_ok(VERIFY_READ
, (void *)data
, sizeof(struct user_fp
)))
554 child
->used_math
= 1;
555 ret
= __copy_from_user(&child
->thread
.fpstate
, (void *)data
,
556 sizeof(struct user_fp
)) ? -EFAULT
: 0;
567 asmlinkage
int sys_ptrace(long request
, long pid
, long addr
, long data
)
569 struct task_struct
*child
;
574 if (request
== PTRACE_TRACEME
) {
575 /* are we already being traced? */
576 if (current
->ptrace
& PT_PTRACED
)
578 /* set the ptrace bit in the process flags. */
579 current
->ptrace
|= PT_PTRACED
;
584 read_lock(&tasklist_lock
);
585 child
= find_task_by_pid(pid
);
587 get_task_struct(child
);
588 read_unlock(&tasklist_lock
);
593 if (pid
== 1) /* you may not mess with init */
596 if (request
== PTRACE_ATTACH
) {
597 if (child
== current
)
599 if ((!child
->dumpable
||
600 (current
->uid
!= child
->euid
) ||
601 (current
->uid
!= child
->suid
) ||
602 (current
->uid
!= child
->uid
) ||
603 (current
->gid
!= child
->egid
) ||
604 (current
->gid
!= child
->sgid
) ||
605 (!cap_issubset(child
->cap_permitted
, current
->cap_permitted
)) ||
606 (current
->gid
!= child
->gid
)) && !capable(CAP_SYS_PTRACE
))
608 /* the same process cannot be attached many times */
609 if (child
->ptrace
& PT_PTRACED
)
611 child
->ptrace
|= PT_PTRACED
;
613 write_lock_irq(&tasklist_lock
);
614 if (child
->p_pptr
!= current
) {
616 child
->p_pptr
= current
;
619 write_unlock_irq(&tasklist_lock
);
621 send_sig(SIGSTOP
, child
, 1);
626 if (!(child
->ptrace
& PT_PTRACED
))
628 if (child
->state
!= TASK_STOPPED
&& request
!= PTRACE_KILL
)
630 if (child
->p_pptr
!= current
)
633 ret
= do_ptrace(request
, child
, addr
, data
);
636 free_task_struct(child
);
642 asmlinkage
void syscall_trace(void)
644 if ((current
->ptrace
& (PT_PTRACED
|PT_TRACESYS
))
645 != (PT_PTRACED
|PT_TRACESYS
))
647 current
->exit_code
= SIGTRAP
;
648 current
->state
= TASK_STOPPED
;
649 notify_parent(current
, SIGCHLD
);
652 * this isn't the same as continuing with a signal, but it will do
653 * for normal use. strace only continues with a signal if the
654 * stopping signal is not SIGTRAP. -brl
656 if (current
->exit_code
) {
657 send_sig(current
->exit_code
, current
, 1);
658 current
->exit_code
= 0;