2 * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
3 * Licensed under the GPL
6 #include "linux/audit.h"
7 #include "linux/ptrace.h"
8 #include "linux/sched.h"
9 #include "asm/uaccess.h"
10 #include "skas_ptrace.h"
14 void user_enable_single_step(struct task_struct
*child
)
16 child
->ptrace
|= PT_DTRACE
;
17 child
->thread
.singlestep_syscall
= 0;
19 #ifdef SUBARCH_SET_SINGLESTEPPING
20 SUBARCH_SET_SINGLESTEPPING(child
, 1);
24 void user_disable_single_step(struct task_struct
*child
)
26 child
->ptrace
&= ~PT_DTRACE
;
27 child
->thread
.singlestep_syscall
= 0;
29 #ifdef SUBARCH_SET_SINGLESTEPPING
30 SUBARCH_SET_SINGLESTEPPING(child
, 0);
35 * Called by kernel/ptrace.c when detaching..
37 void ptrace_disable(struct task_struct
*child
)
39 user_disable_single_step(child
);
42 extern int peek_user(struct task_struct
* child
, long addr
, long data
);
43 extern int poke_user(struct task_struct
* child
, long addr
, long data
);
45 long arch_ptrace(struct task_struct
*child
, long request
,
46 unsigned long addr
, unsigned long data
)
49 unsigned long __user
*p
= (void __user
*)data
;
53 /* read the word at location addr in the USER area. */
55 ret
= peek_user(child
, addr
, data
);
58 /* write the word at location addr in the USER area */
60 ret
= poke_user(child
, addr
, data
);
64 case PTRACE_SYSEMU_SINGLESTEP
:
69 case PTRACE_GETREGS
: { /* Get all gp regs from the child. */
70 if (!access_ok(VERIFY_WRITE
, p
, MAX_REG_OFFSET
)) {
74 for ( i
= 0; i
< MAX_REG_OFFSET
; i
+= sizeof(long) ) {
75 __put_user(getreg(child
, i
), p
);
83 case PTRACE_SETREGS
: { /* Set all gp regs in the child. */
84 unsigned long tmp
= 0;
85 if (!access_ok(VERIFY_READ
, p
, MAX_REG_OFFSET
)) {
89 for ( i
= 0; i
< MAX_REG_OFFSET
; i
+= sizeof(long) ) {
91 putreg(child
, i
, tmp
);
98 case PTRACE_GET_THREAD_AREA
:
99 ret
= ptrace_get_thread_area(child
, addr
, vp
);
102 case PTRACE_SET_THREAD_AREA
:
103 ret
= ptrace_set_thread_area(child
, addr
, vp
);
106 case PTRACE_FAULTINFO
: {
108 * Take the info from thread->arch->faultinfo,
109 * but transfer max. sizeof(struct ptrace_faultinfo).
110 * On i386, ptrace_faultinfo is smaller!
112 ret
= copy_to_user(p
, &child
->thread
.arch
.faultinfo
,
113 sizeof(struct ptrace_faultinfo
)) ?
120 struct ptrace_ldt ldt
;
122 if (copy_from_user(&ldt
, p
, sizeof(ldt
))) {
128 * This one is confusing, so just punt and return -EIO for
136 ret
= ptrace_request(child
, request
, addr
, data
);
138 ret
= subarch_ptrace(child
, request
, addr
, data
);
145 static void send_sigtrap(struct task_struct
*tsk
, struct uml_pt_regs
*regs
,
150 memset(&info
, 0, sizeof(info
));
151 info
.si_signo
= SIGTRAP
;
152 info
.si_code
= TRAP_BRKPT
;
155 info
.si_addr
= UPT_IS_USER(regs
) ? (void __user
*) UPT_IP(regs
) : NULL
;
157 /* Send us the fake SIGTRAP */
158 force_sig_info(SIGTRAP
, &info
, tsk
);
162 * XXX Check PT_DTRACE vs TIF_SINGLESTEP for singlestepping check and
163 * PT_PTRACED vs TIF_SYSCALL_TRACE for syscall tracing check
165 void syscall_trace(struct uml_pt_regs
*regs
, int entryexit
)
167 int is_singlestep
= (current
->ptrace
& PT_DTRACE
) && entryexit
;
171 audit_syscall_entry(HOST_AUDIT_ARCH
,
172 UPT_SYSCALL_NR(regs
),
173 UPT_SYSCALL_ARG1(regs
),
174 UPT_SYSCALL_ARG2(regs
),
175 UPT_SYSCALL_ARG3(regs
),
176 UPT_SYSCALL_ARG4(regs
));
178 audit_syscall_exit(regs
);
180 /* Fake a debug trap */
182 send_sigtrap(current
, regs
, 0);
184 if (!test_thread_flag(TIF_SYSCALL_TRACE
))
187 if (!(current
->ptrace
& PT_PTRACED
))
191 * the 0x80 provides a way for the tracing parent to distinguish
192 * between a syscall stop and SIGTRAP delivery
194 tracesysgood
= (current
->ptrace
& PT_TRACESYSGOOD
);
195 ptrace_notify(SIGTRAP
| (tracesysgood
? 0x80 : 0));
197 if (entryexit
) /* force do_signal() --> is_syscall() */
198 set_thread_flag(TIF_SIGPENDING
);
201 * this isn't the same as continuing with a signal, but it will do
202 * for normal use. strace only continues with a signal if the
203 * stopping signal is not SIGTRAP. -brl
205 if (current
->exit_code
) {
206 send_sig(current
->exit_code
, current
, 1);
207 current
->exit_code
= 0;