1 /* $Id: ptrace.c,v 1.17 1999/09/28 22:25:47 ralf Exp $
3 * This file is subject to the terms and conditions of the GNU General Public
4 * License. See the file "COPYING" in the main directory of this archive
7 * Copyright (C) 1992 Ross Biro
8 * Copyright (C) Linus Torvalds
9 * Copyright (C) 1994, 1995, 1996, 1997, 1998 Ralf Baechle
10 * Copyright (C) 1996 David S. Miller
12 #include <linux/kernel.h>
13 #include <linux/sched.h>
15 #include <linux/errno.h>
16 #include <linux/ptrace.h>
17 #include <linux/smp.h>
18 #include <linux/smp_lock.h>
19 #include <linux/user.h>
22 #include <asm/mipsregs.h>
23 #include <asm/pgtable.h>
25 #include <asm/system.h>
26 #include <asm/uaccess.h>
28 asmlinkage
int sys_ptrace(long request
, long pid
, long addr
, long data
)
30 struct task_struct
*child
;
32 extern void save_fp(void*);
36 printk("ptrace(r=%d,pid=%d,addr=%08lx,data=%08lx)\n",
37 (int) request
, (int) pid
, (unsigned long) addr
,
38 (unsigned long) data
);
40 if (request
== PTRACE_TRACEME
) {
41 /* are we already being traced? */
42 if (current
->ptrace
& PT_PTRACED
) {
46 /* set the ptrace bit in the process flags. */
47 current
->ptrace
|= PT_PTRACED
;
52 read_lock(&tasklist_lock
);
53 child
= find_task_by_pid(pid
);
55 get_task_struct(child
);
56 read_unlock(&tasklist_lock
);
61 if (pid
== 1) /* you may not mess with init */
64 if (request
== PTRACE_ATTACH
) {
67 if ((!child
->dumpable
||
68 (current
->uid
!= child
->euid
) ||
69 (current
->uid
!= child
->suid
) ||
70 (current
->uid
!= child
->uid
) ||
71 (current
->gid
!= child
->egid
) ||
72 (current
->gid
!= child
->sgid
) ||
73 (current
->gid
!= child
->gid
) ||
74 (!cap_issubset(child
->cap_permitted
,
75 current
->cap_permitted
)) ||
76 (current
->gid
!= child
->gid
)) && !capable(CAP_SYS_PTRACE
))
78 /* the same process cannot be attached many times */
79 if (child
->ptrace
& PT_PTRACED
)
81 child
->ptrace
|= PT_PTRACED
;
83 write_lock_irq(&tasklist_lock
);
84 if (child
->p_pptr
!= current
) {
86 child
->p_pptr
= current
;
89 write_unlock_irq(&tasklist_lock
);
91 send_sig(SIGSTOP
, child
, 1);
96 if (!(child
->ptrace
& PT_PTRACED
))
98 if (child
->state
!= TASK_STOPPED
) {
99 if (request
!= PTRACE_KILL
)
102 if (child
->p_pptr
!= current
)
105 case PTRACE_PEEKTEXT
: /* read word at location addr. */
106 case PTRACE_PEEKDATA
: {
110 copied
= access_process_vm(child
, addr
, &tmp
, sizeof(tmp
), 0);
112 if (copied
!= sizeof(tmp
))
114 res
= put_user(tmp
,(unsigned long *) data
);
119 /* Read the word at location addr in the USER area. */
120 case PTRACE_PEEKUSR
: {
121 struct pt_regs
*regs
;
124 regs
= (struct pt_regs
*) ((unsigned long) child
+
125 KERNEL_STACK_SIZE
- 32 - sizeof(struct pt_regs
));
126 tmp
= 0; /* Default return value. */
130 tmp
= regs
->regs
[addr
];
132 case FPR_BASE
... FPR_BASE
+ 31:
133 if (child
->used_math
) {
134 if (last_task_used_math
== child
) {
138 last_task_used_math
= NULL
;
140 tmp
= child
->thread
.fpu
.hard
.fp_regs
[addr
- 32];
142 tmp
= -1; /* FP not yet used */
149 tmp
= regs
->cp0_cause
;
152 tmp
= regs
->cp0_badvaddr
;
161 tmp
= child
->thread
.fpu
.hard
.control
;
163 case FPC_EIR
: { /* implementation / version register */
168 __asm__
__volatile__("cfc1\t%0,$0": "=r" (tmp
));
169 __restore_flags(flags
);
177 res
= put_user(tmp
, (unsigned long *) data
);
181 case PTRACE_POKETEXT
: /* write the word at location addr. */
182 case PTRACE_POKEDATA
:
184 if (access_process_vm(child
, addr
, &data
, sizeof(data
), 1)
190 case PTRACE_POKEUSR
: {
191 struct pt_regs
*regs
;
193 regs
= (struct pt_regs
*) ((unsigned long) child
+
194 KERNEL_STACK_SIZE
- 32 - sizeof(struct pt_regs
));
198 regs
->regs
[addr
] = data
;
200 case FPR_BASE
... FPR_BASE
+ 31: {
202 if (child
->used_math
) {
203 if (last_task_used_math
== child
) {
207 last_task_used_math
= NULL
;
208 regs
->cp0_status
&= ~ST0_CU1
;
211 /* FP not yet used */
212 memset(&child
->thread
.fpu
.hard
, ~0,
213 sizeof(child
->thread
.fpu
.hard
));
214 child
->thread
.fpu
.hard
.control
= 0;
216 fregs
= child
->thread
.fpu
.hard
.fp_regs
;
217 fregs
[addr
- FPR_BASE
] = data
;
221 regs
->cp0_epc
= data
;
230 child
->thread
.fpu
.hard
.control
= data
;
233 /* The rest are not allowed. */
240 case PTRACE_SYSCALL
: /* continue and stop at next (return from) syscall */
241 case PTRACE_CONT
: { /* restart after signal. */
243 if ((unsigned long) data
> _NSIG
)
245 if (request
== PTRACE_SYSCALL
)
246 child
->ptrace
|= PT_TRACESYS
;
248 child
->ptrace
&= ~PT_TRACESYS
;
249 child
->exit_code
= data
;
250 wake_up_process(child
);
256 * make the child exit. Best I can do is send it a sigkill.
257 * perhaps it should be put in the status that it wants to
262 if (child
->state
!= TASK_ZOMBIE
) /* already dead */
264 child
->exit_code
= SIGKILL
;
265 wake_up_process(child
);
268 case PTRACE_DETACH
: /* detach a process that was attached. */
270 if ((unsigned long) data
> _NSIG
)
272 child
->ptrace
&= ~(PT_PTRACED
|PT_TRACESYS
);
273 child
->exit_code
= data
;
274 write_lock_irq(&tasklist_lock
);
276 child
->p_pptr
= child
->p_opptr
;
278 write_unlock_irq(&tasklist_lock
);
279 wake_up_process(child
);
288 free_task_struct(child
);
294 asmlinkage
void syscall_trace(void)
296 if ((current
->ptrace
& (PT_PTRACED
|PT_TRACESYS
))
297 != (PT_PTRACED
|PT_TRACESYS
))
299 current
->exit_code
= SIGTRAP
;
300 current
->state
= TASK_STOPPED
;
301 notify_parent(current
, SIGCHLD
);
304 * this isn't the same as continuing with a signal, but it will do
305 * for normal use. strace only continues with a signal if the
306 * stopping signal is not SIGTRAP. -brl
308 if (current
->exit_code
) {
309 send_sig(current
->exit_code
, current
, 1);
310 current
->exit_code
= 0;