Commit | Line | Data |
---|---|---|
c2cada66 CT |
1 | /*- |
2 | * Copyright (c) 1982, 1986, 1989 The Regents of the University of California. | |
3 | * All rights reserved. | |
4 | * | |
5 | * This software was developed by the Computer Systems Engineering group | |
6 | * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and | |
7 | * contributed to Berkeley. | |
8 | * | |
b480239a KB |
9 | * All advertising materials mentioning features or use of this software |
10 | * must display the following acknowledgement: | |
11 | * This product includes software developed by the University of | |
72539cb1 | 12 | * California, Lawrence Berkeley Laboratory. |
b480239a | 13 | * |
c2cada66 CT |
14 | * %sccs.include.proprietary.c% |
15 | * | |
72539cb1 | 16 | * @(#)sys_process.c 7.4 (Berkeley) %G% |
c2cada66 CT |
17 | */ |
18 | ||
5548a02f KB |
19 | #include <sys/param.h> |
20 | #include <sys/proc.h> | |
21 | #include <sys/vnode.h> | |
22 | #include <sys/buf.h> | |
23 | #include <sys/malloc.h> | |
24 | #include <sys/ptrace.h> | |
25 | #include <sys/user.h> | |
c2cada66 | 26 | |
5548a02f KB |
27 | #include <vm/vm.h> |
28 | #include <vm/vm_page.h> | |
c2cada66 | 29 | |
5548a02f KB |
30 | #include <machine/psl.h> |
31 | #include <machine/reg.h> | |
c2cada66 CT |
32 | |
33 | /* | |
34 | * Priority for tracing | |
35 | */ | |
36 | #define IPCPRI PZERO | |
37 | ||
38 | /* | |
39 | * Tracing variables. | |
40 | * Used to pass trace command from | |
41 | * parent to child being traced. | |
42 | * This data base cannot be | |
43 | * shared and is locked | |
44 | * per user. | |
45 | */ | |
46 | struct { | |
47 | int ip_lock; | |
48 | int ip_req; | |
49 | int ip_error; | |
50 | union { | |
51 | char un_any[4]; | |
52 | struct { | |
53 | int wd_data; | |
54 | caddr_t wd_addr; | |
55 | } un_wd; | |
56 | struct trapframe un_tf; | |
57 | struct fpstate un_f; | |
58 | } ip_un; | |
59 | } ipc; | |
60 | #define ip_any ip_un.un_any | |
61 | #define ip_data ip_un.un_wd.wd_data | |
62 | #define ip_addr ip_un.un_wd.wd_addr | |
63 | #define ip_tf ip_un.un_tf | |
64 | #define ip_f ip_un.un_f | |
65 | ||
66 | /* | |
67 | * Process debugging system call. | |
68 | */ | |
69 | struct ptrace_args { | |
70 | int req; | |
71 | int pid; | |
72 | caddr_t addr; | |
73 | int data; | |
74 | }; | |
75 | ptrace(curp, uap, retval) | |
76 | struct proc *curp; | |
77 | register struct ptrace_args *uap; | |
78 | int *retval; | |
79 | { | |
80 | register struct proc *p; | |
81 | register enum { t_oneword, t_regin, t_regout } type; | |
82 | register size_t size; | |
83 | register int error; | |
84 | ||
85 | if (uap->req == PT_TRACE_ME) { | |
86 | curp->p_flag |= STRC; | |
c2cada66 CT |
87 | return (0); |
88 | } | |
89 | if ((p = pfind(uap->pid)) == NULL) | |
90 | return (ESRCH); | |
91 | switch (uap->req) { | |
92 | ||
93 | case PT_READ_I: | |
94 | case PT_READ_D: | |
95 | case PT_WRITE_I: | |
96 | case PT_WRITE_D: | |
97 | case PT_CONTINUE: | |
98 | case PT_KILL: | |
99 | case PT_DETACH: | |
100 | type = t_oneword; | |
101 | size = 0; | |
102 | break; | |
103 | ||
104 | case PT_ATTACH: | |
105 | /* | |
72539cb1 CT |
106 | * Must be root if the process has used set user or group |
107 | * privileges or does not belong to the real user. Must | |
108 | * not be already traced. Can't attach to ourselves. | |
c2cada66 CT |
109 | */ |
110 | if ((p->p_flag & SUGID || | |
111 | p->p_cred->p_ruid != curp->p_cred->p_ruid) && | |
112 | (error = suser(p->p_ucred, &p->p_acflag)) != 0) | |
113 | return (error); | |
114 | if (p->p_flag & STRC) | |
115 | return (EALREADY); /* ??? */ | |
72539cb1 CT |
116 | if (p == curp) |
117 | return (EINVAL); | |
c2cada66 CT |
118 | /* |
119 | * It would be nice if the tracing relationship was separate | |
120 | * from the parent relationship but that would require | |
121 | * another set of links in the proc struct or for "wait" | |
122 | * to scan the entire proc table. To make life easier, | |
123 | * we just re-parent the process we're trying to trace. | |
124 | * The old parent is remembered so we can put things back | |
125 | * on a "detach". | |
126 | */ | |
127 | p->p_flag |= STRC; | |
128 | p->p_oppid = p->p_pptr->p_pid; | |
129 | proc_reparent(p, curp); | |
72539cb1 CT |
130 | if (p->p_stat == SSTOP) |
131 | setrun(p); /* long enough to stop */ | |
c2cada66 CT |
132 | psignal(p, SIGSTOP); |
133 | return (0); | |
134 | ||
135 | case PT_GETREGS: | |
136 | type = t_regout; | |
137 | size = sizeof(struct trapframe); | |
138 | break; | |
139 | ||
140 | case PT_SETREGS: | |
141 | type = t_regin; | |
142 | size = sizeof(struct trapframe); | |
143 | break; | |
144 | ||
145 | case PT_GETFPREGS: | |
146 | type = t_regout; | |
147 | size = sizeof(struct fpstate); | |
148 | break; | |
149 | ||
150 | case PT_SETFPREGS: | |
151 | type = t_regin; | |
152 | size = sizeof(struct fpstate); | |
153 | break; | |
154 | ||
155 | default: | |
156 | return (EINVAL); | |
157 | } | |
158 | if (p->p_stat != SSTOP || p->p_pptr != curp || !(p->p_flag & STRC)) | |
159 | return (ESRCH); | |
160 | while (ipc.ip_lock) | |
161 | sleep((caddr_t)&ipc, IPCPRI); | |
162 | ipc.ip_lock = p->p_pid; | |
163 | ipc.ip_req = uap->req; | |
164 | ipc.ip_error = 0; | |
165 | switch (type) { | |
166 | ||
167 | case t_oneword: | |
168 | ipc.ip_addr = uap->addr; | |
169 | ipc.ip_data = uap->data; | |
170 | break; | |
171 | ||
172 | case t_regin: | |
173 | if ((error = copyin(uap->addr, ipc.ip_any, size)) != 0) | |
174 | return (error); | |
175 | break; | |
176 | ||
177 | case t_regout: | |
178 | break; | |
179 | ||
180 | default: | |
181 | panic("ptrace"); | |
182 | } | |
183 | p->p_flag &= ~SWTED; | |
184 | do { | |
185 | if (p->p_stat == SSTOP) | |
186 | setrun(p); | |
187 | sleep((caddr_t)&ipc, IPCPRI); | |
188 | } while (ipc.ip_req > 0); | |
189 | if ((error = ipc.ip_error) == 0) { | |
190 | if (type == t_oneword) | |
191 | *retval = ipc.ip_data; | |
192 | else if (type == t_regout) | |
193 | error = copyout(ipc.ip_any, uap->addr, size); | |
194 | } | |
195 | ipc.ip_lock = 0; | |
196 | wakeup((caddr_t)&ipc); | |
197 | return (error); | |
198 | } | |
199 | ||
200 | /* | |
201 | * Write text space by unprotecting, writing, and reprotecting. | |
202 | */ | |
203 | static int | |
204 | writetext(p, addr, data, len) | |
205 | struct proc *p; | |
206 | caddr_t addr, data; | |
207 | int len; | |
208 | { | |
209 | vm_offset_t sa, ea; | |
210 | vm_map_t map; | |
211 | int error; | |
212 | ||
213 | map = &p->p_vmspace->vm_map; | |
214 | sa = trunc_page((vm_offset_t)addr); | |
72539cb1 | 215 | ea = round_page((vm_offset_t)addr + len); |
c2cada66 CT |
216 | if (vm_map_protect(map, sa, ea, VM_PROT_DEFAULT, 0) != KERN_SUCCESS) |
217 | return (-1); | |
218 | error = copyout(data, addr, len); | |
219 | (void) vm_map_protect(map, sa, ea, VM_PROT_READ|VM_PROT_EXECUTE, 0); | |
220 | return (error); | |
221 | } | |
222 | ||
223 | /* | |
224 | * Transmit a tracing request from the parent to the child process | |
225 | * being debugged. This code runs in the context of the child process | |
226 | * to fulfill the command requested by the parent. | |
227 | */ | |
228 | procxmt(p) | |
229 | register struct proc *p; | |
230 | { | |
231 | register int req, error, sig, pc, psr; | |
232 | register caddr_t addr; | |
233 | register struct trapframe *tf, *utf; | |
234 | register struct fpstate *fs, *oldfs; | |
235 | extern struct fpstate initfpstate; | |
236 | ||
237 | if (ipc.ip_lock != p->p_pid) | |
238 | return (0); | |
239 | p->p_slptime = 0; | |
240 | req = ipc.ip_req; | |
241 | ipc.ip_req = 0; | |
242 | error = 0; | |
243 | switch (req) { | |
244 | ||
245 | case PT_READ_I: /* read the child's text space */ | |
246 | case PT_READ_D: /* read the child's data space */ | |
247 | write_user_windows(); | |
248 | (void) rwindow_save(p); /* ignore unwritable windows */ | |
249 | error = copyin(ipc.ip_addr, ipc.ip_any, sizeof(int)); | |
250 | break; | |
251 | ||
252 | case PT_WRITE_I: /* write the child's text space */ | |
253 | case PT_WRITE_D: /* write the child's data space */ | |
254 | addr = ipc.ip_addr; | |
255 | write_user_windows(); | |
256 | (void) rwindow_save(p); | |
257 | error = copyout(ipc.ip_any, addr, sizeof(int)); | |
258 | if (error && req == PT_WRITE_I) | |
259 | error = writetext(p, addr, ipc.ip_any, sizeof(int)); | |
260 | break; | |
261 | ||
262 | case PT_CONTINUE: /* continue the child */ | |
263 | sig = ipc.ip_data; | |
264 | if ((unsigned)sig >= NSIG) { | |
265 | error = EINVAL; | |
266 | break; | |
267 | } | |
268 | pc = (int)ipc.ip_addr; | |
269 | if (pc & 3) { | |
270 | if (pc != 1) { | |
271 | error = EINVAL; | |
272 | break; | |
273 | } | |
274 | } else { | |
275 | tf = p->p_md.md_tf; | |
276 | tf->tf_pc = pc; | |
277 | tf->tf_npc = pc + 4; | |
278 | } | |
279 | p->p_xstat = sig; /* see issig */ | |
280 | wakeup((caddr_t)&ipc); | |
281 | return (1); | |
282 | ||
283 | case PT_KILL: /* kill the child process */ | |
284 | wakeup((caddr_t)&ipc); | |
72539cb1 | 285 | exit1(p, (int)p->p_xstat); |
c2cada66 CT |
286 | |
287 | case PT_DETACH: /* stop tracing the child */ | |
288 | sig = ipc.ip_data; | |
289 | if ((unsigned)sig >= NSIG) { | |
290 | error = EINVAL; | |
291 | break; | |
292 | } | |
293 | pc = (int)ipc.ip_addr; | |
294 | if (pc & 3) { | |
295 | if (pc != 1) { | |
296 | error = EINVAL; | |
297 | break; | |
298 | } | |
299 | } else { | |
300 | tf = p->p_md.md_tf; | |
301 | tf->tf_pc = pc; | |
302 | tf->tf_npc = pc + 4; | |
303 | } | |
304 | p->p_xstat = sig; /* see issig */ | |
305 | p->p_flag &= ~STRC; | |
306 | if (p->p_oppid != p->p_pptr->p_pid) { | |
307 | register struct proc *pp = pfind(p->p_oppid); | |
308 | ||
309 | if (pp) | |
310 | proc_reparent(p, pp); | |
311 | } | |
312 | p->p_oppid = 0; | |
313 | wakeup((caddr_t)&ipc); | |
314 | return (1); | |
315 | ||
316 | case PT_GETREGS: | |
317 | copywords((caddr_t)p->p_md.md_tf, (caddr_t)&ipc.ip_tf, | |
318 | sizeof(struct trapframe)); | |
319 | ipc.ip_tf.tf_global[0] = 0; /* XXX */ | |
320 | break; | |
321 | ||
322 | case PT_SETREGS: | |
323 | tf = p->p_md.md_tf; | |
324 | utf = &ipc.ip_tf; | |
325 | if ((utf->tf_pc | utf->tf_npc) & 3) { | |
326 | error = EINVAL; | |
327 | break; | |
328 | } | |
329 | psr = (tf->tf_psr & ~PSR_ICC) | (utf->tf_psr & PSR_ICC); | |
330 | copywords((caddr_t)utf, (caddr_t)tf, sizeof(*tf)); | |
331 | tf->tf_psr = psr; | |
332 | break; | |
333 | ||
334 | case PT_GETFPREGS: | |
335 | if ((fs = p->p_md.md_fpstate) == NULL) | |
336 | fs = &initfpstate; | |
337 | else if (p == fpproc) | |
338 | savefpstate(fs); | |
339 | copywords((caddr_t)fs, (caddr_t)&ipc.ip_f, sizeof *fs); | |
340 | break; | |
341 | ||
342 | case PT_SETFPREGS: | |
343 | fs = &ipc.ip_f; | |
344 | if ((fs->fs_fsr & FSR_MBZ) != 0 || fs->fs_qsize) { | |
345 | error = EINVAL; | |
346 | break; | |
347 | } | |
348 | oldfs = p->p_md.md_fpstate; | |
349 | if (oldfs == NULL) | |
350 | p->p_md.md_fpstate = oldfs = malloc(sizeof *oldfs, | |
351 | M_SUBPROC, M_WAITOK); | |
352 | else if (p == fpproc) { | |
353 | savefpstate(oldfs); | |
354 | fpproc = NULL; | |
355 | p->p_md.md_tf->tf_psr &= ~PSR_EF; | |
356 | } | |
357 | copywords((caddr_t)fs, (caddr_t)oldfs, sizeof *oldfs); | |
358 | break; | |
359 | ||
360 | default: | |
361 | panic("procxmt"); | |
362 | } | |
363 | ipc.ip_error = error; | |
364 | wakeup((caddr_t)&ipc); | |
365 | return (0); | |
366 | } |