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