1 /* This file handles the process manager's part of debugging, using the
2 * ptrace system call. Most of the commands are passed on to the system
5 * The debugging commands available are:
6 * T_STOP stop the process
7 * T_OK enable tracing by parent for this process
8 * T_GETINS return value from instruction space
9 * T_GETDATA return value from data space
10 * T_GETUSER return value from user process table
11 * T_SETINS set value in instruction space
12 * T_SETDATA set value in data space
13 * T_SETUSER set value in user process table
14 * T_RESUME resume execution
16 * T_STEP set trace bit
17 * T_SYSCALL trace system call
18 * T_ATTACH attach to an existing process
19 * T_DETACH detach from a traced process
20 * T_SETOPT set trace options
21 * T_GETRANGE get range of values
22 * T_SETRANGE set range of values
24 * The T_OK, T_ATTACH, T_EXIT, and T_SETOPT commands are handled here, and the
25 * T_RESUME, T_STEP, T_SYSCALL, and T_DETACH commands are partially handled
26 * here and completed by the system task. The rest are handled entirely by the
31 #include <minix/com.h>
32 #include <minix/callnr.h>
33 #include <sys/ptrace.h>
38 /*===========================================================================*
40 *===========================================================================*/
44 register struct mproc
*child
;
45 struct ptrace_range pr
;
48 req
= m_in
.m_lc_pm_ptrace
.req
;
50 /* The T_OK call is made by the child fork of the debugger before it execs
51 * the process to be traced. The T_ATTACH call is made by the debugger itself
52 * to attach to an existing process.
55 case T_OK
: /* enable tracing by parent for this proc */
56 if (mp
->mp_tracer
!= NO_TRACER
) return(EBUSY
);
58 mp
->mp_tracer
= mp
->mp_parent
;
59 mp
->mp_reply
.m_pm_lc_ptrace
.data
= 0;
62 case T_ATTACH
: /* attach to an existing process */
63 if ((child
= find_proc(m_in
.m_lc_pm_ptrace
.pid
)) == NULL
) return(ESRCH
);
64 if (child
->mp_flags
& EXITING
) return(ESRCH
);
66 /* For non-root processes, user and group ID must match. */
67 if (mp
->mp_effuid
!= SUPER_USER
&&
68 (mp
->mp_effuid
!= child
->mp_effuid
||
69 mp
->mp_effgid
!= child
->mp_effgid
||
70 child
->mp_effuid
!= child
->mp_realuid
||
71 child
->mp_effgid
!= child
->mp_realgid
)) return(EPERM
);
73 /* Only root may trace system servers. */
74 if (mp
->mp_effuid
!= SUPER_USER
&& (child
->mp_flags
& PRIV_PROC
))
77 /* System servers may not trace anyone. They can use sys_trace(). */
78 if (mp
->mp_flags
& PRIV_PROC
) return(EPERM
);
80 /* Can't trace self, PM or VM. */
81 if (child
== mp
|| child
->mp_endpoint
== PM_PROC_NR
||
82 child
->mp_endpoint
== VM_PROC_NR
) return(EPERM
);
84 /* Can't trace a process that is already being traced. */
85 if (child
->mp_tracer
!= NO_TRACER
) return(EBUSY
);
87 child
->mp_tracer
= who_p
;
88 child
->mp_trace_flags
= TO_NOEXEC
;
90 sig_proc(child
, SIGSTOP
, TRUE
/*trace*/, FALSE
/* ksig */);
92 mp
->mp_reply
.m_pm_lc_ptrace
.data
= 0;
95 case T_STOP
: /* stop the process */
96 /* This call is not exposed to user programs, because its effect can be
97 * achieved better by sending the traced process a signal with kill(2).
101 case T_READB_INS
: /* special hack for reading text segments */
102 if (mp
->mp_effuid
!= SUPER_USER
) return(EPERM
);
103 if ((child
= find_proc(m_in
.m_lc_pm_ptrace
.pid
)) == NULL
) return(ESRCH
);
104 if (child
->mp_flags
& EXITING
) return(ESRCH
);
106 r
= sys_trace(req
, child
->mp_endpoint
, m_in
.m_lc_pm_ptrace
.addr
,
107 &m_in
.m_lc_pm_ptrace
.data
);
108 if (r
!= OK
) return(r
);
110 mp
->mp_reply
.m_pm_lc_ptrace
.data
= m_in
.m_lc_pm_ptrace
.data
;
113 case T_WRITEB_INS
: /* special hack for patching text segments */
114 if (mp
->mp_effuid
!= SUPER_USER
) return(EPERM
);
115 if ((child
= find_proc(m_in
.m_lc_pm_ptrace
.pid
)) == NULL
) return(ESRCH
);
116 if (child
->mp_flags
& EXITING
) return(ESRCH
);
119 /* Should check for shared text */
121 /* Make sure the text segment is not used as a source for shared
129 r
= sys_trace(req
, child
->mp_endpoint
, m_in
.m_lc_pm_ptrace
.addr
,
130 &m_in
.m_lc_pm_ptrace
.data
);
131 if (r
!= OK
) return(r
);
133 mp
->mp_reply
.m_pm_lc_ptrace
.data
= m_in
.m_lc_pm_ptrace
.data
;
137 /* All the other calls are made by the tracing process to control execution
138 * of the child. For all these calls, the child must be stopped.
140 if ((child
= find_proc(m_in
.m_lc_pm_ptrace
.pid
)) == NULL
) return(ESRCH
);
141 if (child
->mp_flags
& EXITING
) return(ESRCH
);
142 if (child
->mp_tracer
!= who_p
) return(ESRCH
);
143 if (!(child
->mp_flags
& TRACE_STOPPED
)) return(EBUSY
);
146 case T_EXIT
: /* exit */
147 child
->mp_flags
|= TRACE_EXIT
;
149 /* Defer the exit if the traced process has a call pending. */
150 if (child
->mp_flags
& (VFS_CALL
| EVENT_CALL
))
151 child
->mp_exitstatus
= m_in
.m_lc_pm_ptrace
.data
; /* save it */
153 exit_proc(child
, m_in
.m_lc_pm_ptrace
.data
,
154 FALSE
/*dump_core*/);
156 /* Do not reply to the caller until VFS has processed the exit
161 case T_SETOPT
: /* set trace options */
162 child
->mp_trace_flags
= m_in
.m_lc_pm_ptrace
.data
;
164 mp
->mp_reply
.m_pm_lc_ptrace
.data
= 0;
168 case T_SETRANGE
: /* get/set range of values */
169 r
= sys_datacopy(who_e
, m_in
.m_lc_pm_ptrace
.addr
, SELF
, (vir_bytes
)&pr
,
170 (phys_bytes
)sizeof(pr
));
171 if (r
!= OK
) return(r
);
173 if (pr
.pr_space
!= TS_INS
&& pr
.pr_space
!= TS_DATA
) return(EINVAL
);
174 if (pr
.pr_size
== 0 || pr
.pr_size
> LONG_MAX
) return(EINVAL
);
176 if (req
== T_GETRANGE
)
177 r
= sys_vircopy(child
->mp_endpoint
, (vir_bytes
) pr
.pr_addr
,
178 who_e
, (vir_bytes
) pr
.pr_ptr
,
179 (phys_bytes
) pr
.pr_size
, 0);
181 r
= sys_vircopy(who_e
, (vir_bytes
) pr
.pr_ptr
,
182 child
->mp_endpoint
, (vir_bytes
) pr
.pr_addr
,
183 (phys_bytes
) pr
.pr_size
, 0);
185 if (r
!= OK
) return(r
);
187 mp
->mp_reply
.m_pm_lc_ptrace
.data
= 0;
190 case T_DETACH
: /* detach from traced process */
191 if (m_in
.m_lc_pm_ptrace
.data
< 0 || m_in
.m_lc_pm_ptrace
.data
>= _NSIG
)
194 child
->mp_tracer
= NO_TRACER
;
196 /* Let all tracer-pending signals through the filter. */
197 for (i
= 1; i
< _NSIG
; i
++) {
198 if (sigismember(&child
->mp_sigtrace
, i
)) {
199 sigdelset(&child
->mp_sigtrace
, i
);
200 check_sig(child
->mp_pid
, i
, FALSE
/* ksig */);
204 if (m_in
.m_lc_pm_ptrace
.data
> 0) { /* issue signal */
205 sig_proc(child
, m_in
.m_lc_pm_ptrace
.data
, TRUE
/*trace*/,
209 /* Resume the child as if nothing ever happened. */
210 child
->mp_flags
&= ~TRACE_STOPPED
;
211 child
->mp_trace_flags
= 0;
213 check_pending(child
);
219 case T_SYSCALL
: /* resume execution */
220 if (m_in
.m_lc_pm_ptrace
.data
< 0 || m_in
.m_lc_pm_ptrace
.data
>= _NSIG
)
223 if (m_in
.m_lc_pm_ptrace
.data
> 0) { /* issue signal */
224 sig_proc(child
, m_in
.m_lc_pm_ptrace
.data
, FALSE
/*trace*/,
228 /* If there are any other signals waiting to be delivered,
229 * feign a successful resumption.
231 for (i
= 1; i
< _NSIG
; i
++) {
232 if (sigismember(&child
->mp_sigtrace
, i
)) {
233 mp
->mp_reply
.m_pm_lc_ptrace
.data
= 0;
238 child
->mp_flags
&= ~TRACE_STOPPED
;
240 check_pending(child
);
244 r
= sys_trace(req
, child
->mp_endpoint
, m_in
.m_lc_pm_ptrace
.addr
,
245 &m_in
.m_lc_pm_ptrace
.data
);
246 if (r
!= OK
) return(r
);
248 mp
->mp_reply
.m_pm_lc_ptrace
.data
= m_in
.m_lc_pm_ptrace
.data
;
252 /*===========================================================================*
254 *===========================================================================*/
256 trace_stop(register struct mproc
*rmp
, int signo
)
258 /* A traced process got a signal so stop it. */
260 register struct mproc
*rpmp
= mproc
+ rmp
->mp_tracer
;
263 r
= sys_trace(T_STOP
, rmp
->mp_endpoint
, 0L, (long *) 0);
264 if (r
!= OK
) panic("sys_trace failed: %d", r
);
266 rmp
->mp_flags
|= TRACE_STOPPED
;
267 if (wait_test(rpmp
, rmp
)) {
268 /* TODO: rusage support */
270 sigdelset(&rmp
->mp_sigtrace
, signo
);
272 rpmp
->mp_flags
&= ~WAITING
; /* parent is no longer waiting */
273 rpmp
->mp_reply
.m_pm_lc_wait4
.status
= W_STOPCODE(signo
);
274 reply(rmp
->mp_tracer
, rmp
->mp_pid
);