diff options
Diffstat (limited to 'arch/sparc/kernel/ptrace.c')
-rw-r--r-- | arch/sparc/kernel/ptrace.c | 632 |
1 files changed, 632 insertions, 0 deletions
diff --git a/arch/sparc/kernel/ptrace.c b/arch/sparc/kernel/ptrace.c new file mode 100644 index 000000000000..fc4ad69357b8 --- /dev/null +++ b/arch/sparc/kernel/ptrace.c | |||
@@ -0,0 +1,632 @@ | |||
1 | /* ptrace.c: Sparc process tracing support. | ||
2 | * | ||
3 | * Copyright (C) 1996 David S. Miller (davem@caipfs.rutgers.edu) | ||
4 | * | ||
5 | * Based upon code written by Ross Biro, Linus Torvalds, Bob Manson, | ||
6 | * and David Mosberger. | ||
7 | * | ||
8 | * Added Linux support -miguel (weird, eh?, the orignal code was meant | ||
9 | * to emulate SunOS). | ||
10 | */ | ||
11 | |||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/sched.h> | ||
14 | #include <linux/mm.h> | ||
15 | #include <linux/errno.h> | ||
16 | #include <linux/ptrace.h> | ||
17 | #include <linux/user.h> | ||
18 | #include <linux/smp.h> | ||
19 | #include <linux/smp_lock.h> | ||
20 | #include <linux/security.h> | ||
21 | |||
22 | #include <asm/pgtable.h> | ||
23 | #include <asm/system.h> | ||
24 | #include <asm/uaccess.h> | ||
25 | |||
26 | #define MAGIC_CONSTANT 0x80000000 | ||
27 | |||
28 | |||
29 | /* Returning from ptrace is a bit tricky because the syscall return | ||
30 | * low level code assumes any value returned which is negative and | ||
31 | * is a valid errno will mean setting the condition codes to indicate | ||
32 | * an error return. This doesn't work, so we have this hook. | ||
33 | */ | ||
34 | static inline void pt_error_return(struct pt_regs *regs, unsigned long error) | ||
35 | { | ||
36 | regs->u_regs[UREG_I0] = error; | ||
37 | regs->psr |= PSR_C; | ||
38 | regs->pc = regs->npc; | ||
39 | regs->npc += 4; | ||
40 | } | ||
41 | |||
42 | static inline void pt_succ_return(struct pt_regs *regs, unsigned long value) | ||
43 | { | ||
44 | regs->u_regs[UREG_I0] = value; | ||
45 | regs->psr &= ~PSR_C; | ||
46 | regs->pc = regs->npc; | ||
47 | regs->npc += 4; | ||
48 | } | ||
49 | |||
50 | static void | ||
51 | pt_succ_return_linux(struct pt_regs *regs, unsigned long value, long __user *addr) | ||
52 | { | ||
53 | if (put_user(value, addr)) { | ||
54 | pt_error_return(regs, EFAULT); | ||
55 | return; | ||
56 | } | ||
57 | regs->u_regs[UREG_I0] = 0; | ||
58 | regs->psr &= ~PSR_C; | ||
59 | regs->pc = regs->npc; | ||
60 | regs->npc += 4; | ||
61 | } | ||
62 | |||
63 | static void | ||
64 | pt_os_succ_return (struct pt_regs *regs, unsigned long val, long __user *addr) | ||
65 | { | ||
66 | if (current->personality == PER_SUNOS) | ||
67 | pt_succ_return (regs, val); | ||
68 | else | ||
69 | pt_succ_return_linux (regs, val, addr); | ||
70 | } | ||
71 | |||
72 | /* Fuck me gently with a chainsaw... */ | ||
73 | static inline void read_sunos_user(struct pt_regs *regs, unsigned long offset, | ||
74 | struct task_struct *tsk, long __user *addr) | ||
75 | { | ||
76 | struct pt_regs *cregs = tsk->thread.kregs; | ||
77 | struct thread_info *t = tsk->thread_info; | ||
78 | int v; | ||
79 | |||
80 | if(offset >= 1024) | ||
81 | offset -= 1024; /* whee... */ | ||
82 | if(offset & ((sizeof(unsigned long) - 1))) { | ||
83 | pt_error_return(regs, EIO); | ||
84 | return; | ||
85 | } | ||
86 | if(offset >= 16 && offset < 784) { | ||
87 | offset -= 16; offset >>= 2; | ||
88 | pt_os_succ_return(regs, *(((unsigned long *)(&t->reg_window[0]))+offset), addr); | ||
89 | return; | ||
90 | } | ||
91 | if(offset >= 784 && offset < 832) { | ||
92 | offset -= 784; offset >>= 2; | ||
93 | pt_os_succ_return(regs, *(((unsigned long *)(&t->rwbuf_stkptrs[0]))+offset), addr); | ||
94 | return; | ||
95 | } | ||
96 | switch(offset) { | ||
97 | case 0: | ||
98 | v = t->ksp; | ||
99 | break; | ||
100 | case 4: | ||
101 | v = t->kpc; | ||
102 | break; | ||
103 | case 8: | ||
104 | v = t->kpsr; | ||
105 | break; | ||
106 | case 12: | ||
107 | v = t->uwinmask; | ||
108 | break; | ||
109 | case 832: | ||
110 | v = t->w_saved; | ||
111 | break; | ||
112 | case 896: | ||
113 | v = cregs->u_regs[UREG_I0]; | ||
114 | break; | ||
115 | case 900: | ||
116 | v = cregs->u_regs[UREG_I1]; | ||
117 | break; | ||
118 | case 904: | ||
119 | v = cregs->u_regs[UREG_I2]; | ||
120 | break; | ||
121 | case 908: | ||
122 | v = cregs->u_regs[UREG_I3]; | ||
123 | break; | ||
124 | case 912: | ||
125 | v = cregs->u_regs[UREG_I4]; | ||
126 | break; | ||
127 | case 916: | ||
128 | v = cregs->u_regs[UREG_I5]; | ||
129 | break; | ||
130 | case 920: | ||
131 | v = cregs->u_regs[UREG_I6]; | ||
132 | break; | ||
133 | case 924: | ||
134 | if(tsk->thread.flags & MAGIC_CONSTANT) | ||
135 | v = cregs->u_regs[UREG_G1]; | ||
136 | else | ||
137 | v = 0; | ||
138 | break; | ||
139 | case 940: | ||
140 | v = cregs->u_regs[UREG_I0]; | ||
141 | break; | ||
142 | case 944: | ||
143 | v = cregs->u_regs[UREG_I1]; | ||
144 | break; | ||
145 | |||
146 | case 948: | ||
147 | /* Isn't binary compatibility _fun_??? */ | ||
148 | if(cregs->psr & PSR_C) | ||
149 | v = cregs->u_regs[UREG_I0] << 24; | ||
150 | else | ||
151 | v = 0; | ||
152 | break; | ||
153 | |||
154 | /* Rest of them are completely unsupported. */ | ||
155 | default: | ||
156 | printk("%s [%d]: Wants to read user offset %ld\n", | ||
157 | current->comm, current->pid, offset); | ||
158 | pt_error_return(regs, EIO); | ||
159 | return; | ||
160 | } | ||
161 | if (current->personality == PER_SUNOS) | ||
162 | pt_succ_return (regs, v); | ||
163 | else | ||
164 | pt_succ_return_linux (regs, v, addr); | ||
165 | return; | ||
166 | } | ||
167 | |||
168 | static inline void write_sunos_user(struct pt_regs *regs, unsigned long offset, | ||
169 | struct task_struct *tsk) | ||
170 | { | ||
171 | struct pt_regs *cregs = tsk->thread.kregs; | ||
172 | struct thread_info *t = tsk->thread_info; | ||
173 | unsigned long value = regs->u_regs[UREG_I3]; | ||
174 | |||
175 | if(offset >= 1024) | ||
176 | offset -= 1024; /* whee... */ | ||
177 | if(offset & ((sizeof(unsigned long) - 1))) | ||
178 | goto failure; | ||
179 | if(offset >= 16 && offset < 784) { | ||
180 | offset -= 16; offset >>= 2; | ||
181 | *(((unsigned long *)(&t->reg_window[0]))+offset) = value; | ||
182 | goto success; | ||
183 | } | ||
184 | if(offset >= 784 && offset < 832) { | ||
185 | offset -= 784; offset >>= 2; | ||
186 | *(((unsigned long *)(&t->rwbuf_stkptrs[0]))+offset) = value; | ||
187 | goto success; | ||
188 | } | ||
189 | switch(offset) { | ||
190 | case 896: | ||
191 | cregs->u_regs[UREG_I0] = value; | ||
192 | break; | ||
193 | case 900: | ||
194 | cregs->u_regs[UREG_I1] = value; | ||
195 | break; | ||
196 | case 904: | ||
197 | cregs->u_regs[UREG_I2] = value; | ||
198 | break; | ||
199 | case 908: | ||
200 | cregs->u_regs[UREG_I3] = value; | ||
201 | break; | ||
202 | case 912: | ||
203 | cregs->u_regs[UREG_I4] = value; | ||
204 | break; | ||
205 | case 916: | ||
206 | cregs->u_regs[UREG_I5] = value; | ||
207 | break; | ||
208 | case 920: | ||
209 | cregs->u_regs[UREG_I6] = value; | ||
210 | break; | ||
211 | case 924: | ||
212 | cregs->u_regs[UREG_I7] = value; | ||
213 | break; | ||
214 | case 940: | ||
215 | cregs->u_regs[UREG_I0] = value; | ||
216 | break; | ||
217 | case 944: | ||
218 | cregs->u_regs[UREG_I1] = value; | ||
219 | break; | ||
220 | |||
221 | /* Rest of them are completely unsupported or "no-touch". */ | ||
222 | default: | ||
223 | printk("%s [%d]: Wants to write user offset %ld\n", | ||
224 | current->comm, current->pid, offset); | ||
225 | goto failure; | ||
226 | } | ||
227 | success: | ||
228 | pt_succ_return(regs, 0); | ||
229 | return; | ||
230 | failure: | ||
231 | pt_error_return(regs, EIO); | ||
232 | return; | ||
233 | } | ||
234 | |||
235 | /* #define ALLOW_INIT_TRACING */ | ||
236 | /* #define DEBUG_PTRACE */ | ||
237 | |||
238 | #ifdef DEBUG_PTRACE | ||
239 | char *pt_rq [] = { | ||
240 | /* 0 */ "TRACEME", "PEEKTEXT", "PEEKDATA", "PEEKUSR", | ||
241 | /* 4 */ "POKETEXT", "POKEDATA", "POKEUSR", "CONT", | ||
242 | /* 8 */ "KILL", "SINGLESTEP", "SUNATTACH", "SUNDETACH", | ||
243 | /* 12 */ "GETREGS", "SETREGS", "GETFPREGS", "SETFPREGS", | ||
244 | /* 16 */ "READDATA", "WRITEDATA", "READTEXT", "WRITETEXT", | ||
245 | /* 20 */ "GETFPAREGS", "SETFPAREGS", "unknown", "unknown", | ||
246 | /* 24 */ "SYSCALL", "" | ||
247 | }; | ||
248 | #endif | ||
249 | |||
250 | /* | ||
251 | * Called by kernel/ptrace.c when detaching.. | ||
252 | * | ||
253 | * Make sure single step bits etc are not set. | ||
254 | */ | ||
255 | void ptrace_disable(struct task_struct *child) | ||
256 | { | ||
257 | /* nothing to do */ | ||
258 | } | ||
259 | |||
260 | asmlinkage void do_ptrace(struct pt_regs *regs) | ||
261 | { | ||
262 | unsigned long request = regs->u_regs[UREG_I0]; | ||
263 | unsigned long pid = regs->u_regs[UREG_I1]; | ||
264 | unsigned long addr = regs->u_regs[UREG_I2]; | ||
265 | unsigned long data = regs->u_regs[UREG_I3]; | ||
266 | unsigned long addr2 = regs->u_regs[UREG_I4]; | ||
267 | struct task_struct *child; | ||
268 | int ret; | ||
269 | |||
270 | lock_kernel(); | ||
271 | #ifdef DEBUG_PTRACE | ||
272 | { | ||
273 | char *s; | ||
274 | |||
275 | if ((request >= 0) && (request <= 24)) | ||
276 | s = pt_rq [request]; | ||
277 | else | ||
278 | s = "unknown"; | ||
279 | |||
280 | if (request == PTRACE_POKEDATA && data == 0x91d02001){ | ||
281 | printk ("do_ptrace: breakpoint pid=%d, addr=%08lx addr2=%08lx\n", | ||
282 | pid, addr, addr2); | ||
283 | } else | ||
284 | printk("do_ptrace: rq=%s(%d) pid=%d addr=%08lx data=%08lx addr2=%08lx\n", | ||
285 | s, (int) request, (int) pid, addr, data, addr2); | ||
286 | } | ||
287 | #endif | ||
288 | if (request == PTRACE_TRACEME) { | ||
289 | int my_ret; | ||
290 | |||
291 | /* are we already being traced? */ | ||
292 | if (current->ptrace & PT_PTRACED) { | ||
293 | pt_error_return(regs, EPERM); | ||
294 | goto out; | ||
295 | } | ||
296 | my_ret = security_ptrace(current->parent, current); | ||
297 | if (my_ret) { | ||
298 | pt_error_return(regs, -my_ret); | ||
299 | goto out; | ||
300 | } | ||
301 | |||
302 | /* set the ptrace bit in the process flags. */ | ||
303 | current->ptrace |= PT_PTRACED; | ||
304 | pt_succ_return(regs, 0); | ||
305 | goto out; | ||
306 | } | ||
307 | #ifndef ALLOW_INIT_TRACING | ||
308 | if (pid == 1) { | ||
309 | /* Can't dork with init. */ | ||
310 | pt_error_return(regs, EPERM); | ||
311 | goto out; | ||
312 | } | ||
313 | #endif | ||
314 | read_lock(&tasklist_lock); | ||
315 | child = find_task_by_pid(pid); | ||
316 | if (child) | ||
317 | get_task_struct(child); | ||
318 | read_unlock(&tasklist_lock); | ||
319 | |||
320 | if (!child) { | ||
321 | pt_error_return(regs, ESRCH); | ||
322 | goto out; | ||
323 | } | ||
324 | |||
325 | if ((current->personality == PER_SUNOS && request == PTRACE_SUNATTACH) | ||
326 | || (current->personality != PER_SUNOS && request == PTRACE_ATTACH)) { | ||
327 | if (ptrace_attach(child)) { | ||
328 | pt_error_return(regs, EPERM); | ||
329 | goto out_tsk; | ||
330 | } | ||
331 | pt_succ_return(regs, 0); | ||
332 | goto out_tsk; | ||
333 | } | ||
334 | |||
335 | ret = ptrace_check_attach(child, request == PTRACE_KILL); | ||
336 | if (ret < 0) { | ||
337 | pt_error_return(regs, -ret); | ||
338 | goto out_tsk; | ||
339 | } | ||
340 | |||
341 | switch(request) { | ||
342 | case PTRACE_PEEKTEXT: /* read word at location addr. */ | ||
343 | case PTRACE_PEEKDATA: { | ||
344 | unsigned long tmp; | ||
345 | |||
346 | if (access_process_vm(child, addr, | ||
347 | &tmp, sizeof(tmp), 0) == sizeof(tmp)) | ||
348 | pt_os_succ_return(regs, tmp, (long __user *)data); | ||
349 | else | ||
350 | pt_error_return(regs, EIO); | ||
351 | goto out_tsk; | ||
352 | } | ||
353 | |||
354 | case PTRACE_PEEKUSR: | ||
355 | read_sunos_user(regs, addr, child, (long __user *) data); | ||
356 | goto out_tsk; | ||
357 | |||
358 | case PTRACE_POKEUSR: | ||
359 | write_sunos_user(regs, addr, child); | ||
360 | goto out_tsk; | ||
361 | |||
362 | case PTRACE_POKETEXT: /* write the word at location addr. */ | ||
363 | case PTRACE_POKEDATA: { | ||
364 | if (access_process_vm(child, addr, | ||
365 | &data, sizeof(data), 1) == sizeof(data)) | ||
366 | pt_succ_return(regs, 0); | ||
367 | else | ||
368 | pt_error_return(regs, EIO); | ||
369 | goto out_tsk; | ||
370 | } | ||
371 | |||
372 | case PTRACE_GETREGS: { | ||
373 | struct pt_regs __user *pregs = (struct pt_regs __user *) addr; | ||
374 | struct pt_regs *cregs = child->thread.kregs; | ||
375 | int rval; | ||
376 | |||
377 | if (!access_ok(VERIFY_WRITE, pregs, sizeof(struct pt_regs))) { | ||
378 | rval = -EFAULT; | ||
379 | pt_error_return(regs, -rval); | ||
380 | goto out_tsk; | ||
381 | } | ||
382 | __put_user(cregs->psr, (&pregs->psr)); | ||
383 | __put_user(cregs->pc, (&pregs->pc)); | ||
384 | __put_user(cregs->npc, (&pregs->npc)); | ||
385 | __put_user(cregs->y, (&pregs->y)); | ||
386 | for(rval = 1; rval < 16; rval++) | ||
387 | __put_user(cregs->u_regs[rval], (&pregs->u_regs[rval - 1])); | ||
388 | pt_succ_return(regs, 0); | ||
389 | #ifdef DEBUG_PTRACE | ||
390 | printk ("PC=%x nPC=%x o7=%x\n", cregs->pc, cregs->npc, cregs->u_regs [15]); | ||
391 | #endif | ||
392 | goto out_tsk; | ||
393 | } | ||
394 | |||
395 | case PTRACE_SETREGS: { | ||
396 | struct pt_regs __user *pregs = (struct pt_regs __user *) addr; | ||
397 | struct pt_regs *cregs = child->thread.kregs; | ||
398 | unsigned long psr, pc, npc, y; | ||
399 | int i; | ||
400 | |||
401 | /* Must be careful, tracing process can only set certain | ||
402 | * bits in the psr. | ||
403 | */ | ||
404 | if (!access_ok(VERIFY_READ, pregs, sizeof(struct pt_regs))) { | ||
405 | pt_error_return(regs, EFAULT); | ||
406 | goto out_tsk; | ||
407 | } | ||
408 | __get_user(psr, (&pregs->psr)); | ||
409 | __get_user(pc, (&pregs->pc)); | ||
410 | __get_user(npc, (&pregs->npc)); | ||
411 | __get_user(y, (&pregs->y)); | ||
412 | psr &= PSR_ICC; | ||
413 | cregs->psr &= ~PSR_ICC; | ||
414 | cregs->psr |= psr; | ||
415 | if (!((pc | npc) & 3)) { | ||
416 | cregs->pc = pc; | ||
417 | cregs->npc =npc; | ||
418 | } | ||
419 | cregs->y = y; | ||
420 | for(i = 1; i < 16; i++) | ||
421 | __get_user(cregs->u_regs[i], (&pregs->u_regs[i-1])); | ||
422 | pt_succ_return(regs, 0); | ||
423 | goto out_tsk; | ||
424 | } | ||
425 | |||
426 | case PTRACE_GETFPREGS: { | ||
427 | struct fps { | ||
428 | unsigned long regs[32]; | ||
429 | unsigned long fsr; | ||
430 | unsigned long flags; | ||
431 | unsigned long extra; | ||
432 | unsigned long fpqd; | ||
433 | struct fq { | ||
434 | unsigned long *insnaddr; | ||
435 | unsigned long insn; | ||
436 | } fpq[16]; | ||
437 | }; | ||
438 | struct fps __user *fps = (struct fps __user *) addr; | ||
439 | int i; | ||
440 | |||
441 | if (!access_ok(VERIFY_WRITE, fps, sizeof(struct fps))) { | ||
442 | i = -EFAULT; | ||
443 | pt_error_return(regs, -i); | ||
444 | goto out_tsk; | ||
445 | } | ||
446 | for(i = 0; i < 32; i++) | ||
447 | __put_user(child->thread.float_regs[i], (&fps->regs[i])); | ||
448 | __put_user(child->thread.fsr, (&fps->fsr)); | ||
449 | __put_user(child->thread.fpqdepth, (&fps->fpqd)); | ||
450 | __put_user(0, (&fps->flags)); | ||
451 | __put_user(0, (&fps->extra)); | ||
452 | for(i = 0; i < 16; i++) { | ||
453 | __put_user(child->thread.fpqueue[i].insn_addr, | ||
454 | (&fps->fpq[i].insnaddr)); | ||
455 | __put_user(child->thread.fpqueue[i].insn, (&fps->fpq[i].insn)); | ||
456 | } | ||
457 | pt_succ_return(regs, 0); | ||
458 | goto out_tsk; | ||
459 | } | ||
460 | |||
461 | case PTRACE_SETFPREGS: { | ||
462 | struct fps { | ||
463 | unsigned long regs[32]; | ||
464 | unsigned long fsr; | ||
465 | unsigned long flags; | ||
466 | unsigned long extra; | ||
467 | unsigned long fpqd; | ||
468 | struct fq { | ||
469 | unsigned long *insnaddr; | ||
470 | unsigned long insn; | ||
471 | } fpq[16]; | ||
472 | }; | ||
473 | struct fps __user *fps = (struct fps __user *) addr; | ||
474 | int i; | ||
475 | |||
476 | if (!access_ok(VERIFY_READ, fps, sizeof(struct fps))) { | ||
477 | i = -EFAULT; | ||
478 | pt_error_return(regs, -i); | ||
479 | goto out_tsk; | ||
480 | } | ||
481 | copy_from_user(&child->thread.float_regs[0], &fps->regs[0], (32 * sizeof(unsigned long))); | ||
482 | __get_user(child->thread.fsr, (&fps->fsr)); | ||
483 | __get_user(child->thread.fpqdepth, (&fps->fpqd)); | ||
484 | for(i = 0; i < 16; i++) { | ||
485 | __get_user(child->thread.fpqueue[i].insn_addr, | ||
486 | (&fps->fpq[i].insnaddr)); | ||
487 | __get_user(child->thread.fpqueue[i].insn, (&fps->fpq[i].insn)); | ||
488 | } | ||
489 | pt_succ_return(regs, 0); | ||
490 | goto out_tsk; | ||
491 | } | ||
492 | |||
493 | case PTRACE_READTEXT: | ||
494 | case PTRACE_READDATA: { | ||
495 | int res = ptrace_readdata(child, addr, | ||
496 | (void __user *) addr2, data); | ||
497 | |||
498 | if (res == data) { | ||
499 | pt_succ_return(regs, 0); | ||
500 | goto out_tsk; | ||
501 | } | ||
502 | /* Partial read is an IO failure */ | ||
503 | if (res >= 0) | ||
504 | res = -EIO; | ||
505 | pt_error_return(regs, -res); | ||
506 | goto out_tsk; | ||
507 | } | ||
508 | |||
509 | case PTRACE_WRITETEXT: | ||
510 | case PTRACE_WRITEDATA: { | ||
511 | int res = ptrace_writedata(child, (void __user *) addr2, | ||
512 | addr, data); | ||
513 | |||
514 | if (res == data) { | ||
515 | pt_succ_return(regs, 0); | ||
516 | goto out_tsk; | ||
517 | } | ||
518 | /* Partial write is an IO failure */ | ||
519 | if (res >= 0) | ||
520 | res = -EIO; | ||
521 | pt_error_return(regs, -res); | ||
522 | goto out_tsk; | ||
523 | } | ||
524 | |||
525 | case PTRACE_SYSCALL: /* continue and stop at (return from) syscall */ | ||
526 | addr = 1; | ||
527 | |||
528 | case PTRACE_CONT: { /* restart after signal. */ | ||
529 | if (data > _NSIG) { | ||
530 | pt_error_return(regs, EIO); | ||
531 | goto out_tsk; | ||
532 | } | ||
533 | if (addr != 1) { | ||
534 | if (addr & 3) { | ||
535 | pt_error_return(regs, EINVAL); | ||
536 | goto out_tsk; | ||
537 | } | ||
538 | #ifdef DEBUG_PTRACE | ||
539 | printk ("Original: %08lx %08lx\n", child->thread.kregs->pc, child->thread.kregs->npc); | ||
540 | printk ("Continuing with %08lx %08lx\n", addr, addr+4); | ||
541 | #endif | ||
542 | child->thread.kregs->pc = addr; | ||
543 | child->thread.kregs->npc = addr + 4; | ||
544 | } | ||
545 | |||
546 | if (request == PTRACE_SYSCALL) | ||
547 | set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); | ||
548 | else | ||
549 | clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); | ||
550 | |||
551 | child->exit_code = data; | ||
552 | #ifdef DEBUG_PTRACE | ||
553 | printk("CONT: %s [%d]: set exit_code = %x %lx %lx\n", | ||
554 | child->comm, child->pid, child->exit_code, | ||
555 | child->thread.kregs->pc, | ||
556 | child->thread.kregs->npc); | ||
557 | #endif | ||
558 | wake_up_process(child); | ||
559 | pt_succ_return(regs, 0); | ||
560 | goto out_tsk; | ||
561 | } | ||
562 | |||
563 | /* | ||
564 | * make the child exit. Best I can do is send it a sigkill. | ||
565 | * perhaps it should be put in the status that it wants to | ||
566 | * exit. | ||
567 | */ | ||
568 | case PTRACE_KILL: { | ||
569 | if (child->exit_state == EXIT_ZOMBIE) { /* already dead */ | ||
570 | pt_succ_return(regs, 0); | ||
571 | goto out_tsk; | ||
572 | } | ||
573 | wake_up_process(child); | ||
574 | child->exit_code = SIGKILL; | ||
575 | pt_succ_return(regs, 0); | ||
576 | goto out_tsk; | ||
577 | } | ||
578 | |||
579 | case PTRACE_SUNDETACH: { /* detach a process that was attached. */ | ||
580 | int err = ptrace_detach(child, data); | ||
581 | if (err) { | ||
582 | pt_error_return(regs, EIO); | ||
583 | goto out_tsk; | ||
584 | } | ||
585 | pt_succ_return(regs, 0); | ||
586 | goto out_tsk; | ||
587 | } | ||
588 | |||
589 | /* PTRACE_DUMPCORE unsupported... */ | ||
590 | |||
591 | default: { | ||
592 | int err = ptrace_request(child, request, addr, data); | ||
593 | if (err) | ||
594 | pt_error_return(regs, -err); | ||
595 | else | ||
596 | pt_succ_return(regs, 0); | ||
597 | goto out_tsk; | ||
598 | } | ||
599 | } | ||
600 | out_tsk: | ||
601 | if (child) | ||
602 | put_task_struct(child); | ||
603 | out: | ||
604 | unlock_kernel(); | ||
605 | } | ||
606 | |||
607 | asmlinkage void syscall_trace(void) | ||
608 | { | ||
609 | #ifdef DEBUG_PTRACE | ||
610 | printk("%s [%d]: syscall_trace\n", current->comm, current->pid); | ||
611 | #endif | ||
612 | if (!test_thread_flag(TIF_SYSCALL_TRACE)) | ||
613 | return; | ||
614 | if (!(current->ptrace & PT_PTRACED)) | ||
615 | return; | ||
616 | current->thread.flags ^= MAGIC_CONSTANT; | ||
617 | ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) | ||
618 | ? 0x80 : 0)); | ||
619 | /* | ||
620 | * this isn't the same as continuing with a signal, but it will do | ||
621 | * for normal use. strace only continues with a signal if the | ||
622 | * stopping signal is not SIGTRAP. -brl | ||
623 | */ | ||
624 | #ifdef DEBUG_PTRACE | ||
625 | printk("%s [%d]: syscall_trace exit= %x\n", current->comm, | ||
626 | current->pid, current->exit_code); | ||
627 | #endif | ||
628 | if (current->exit_code) { | ||
629 | send_sig (current->exit_code, current, 1); | ||
630 | current->exit_code = 0; | ||
631 | } | ||
632 | } | ||