Blob


1 /*
2 * process interface for FreeBSD
3 *
4 * we could be a little more careful about not using
5 * ptrace unless absolutely necessary. this would let us
6 * look at processes without stopping them.
7 *
8 * I'd like to make this a bit more generic (there's too much
9 * duplication with Linux and presumably other systems),
10 * but ptrace is too damn system-specific.
11 */
13 #include <u.h>
14 #include <sys/ptrace.h>
15 #include <sys/types.h>
16 #include <sys/wait.h>
17 #include <machine/reg.h>
18 #include <signal.h>
19 #include <errno.h>
20 #include <libc.h>
21 #include <mach.h>
22 #include "ureg386.h"
24 Mach *machcpu = &mach386;
26 typedef struct PtraceRegs PtraceRegs;
27 struct PtraceRegs
28 {
29 Regs r;
30 int pid;
31 };
33 static int ptracerw(Map*, Seg*, u64int, void*, uint, int);
34 static int ptraceregrw(Regs*, char*, u64int*, int);
36 void
37 unmapproc(Map *map)
38 {
39 int i;
41 if(map == nil)
42 return;
43 for(i=0; i<map->nseg; i++)
44 while(i<map->nseg && map->seg[i].pid){
45 map->nseg--;
46 memmove(&map->seg[i], &map->seg[i+1],
47 (map->nseg-i)*sizeof(map->seg[0]));
48 }
49 }
51 int
52 mapproc(int pid, Map *map, Regs **rp)
53 {
54 Seg s;
55 PtraceRegs *r;
57 if(ptrace(PT_ATTACH, pid, 0, 0) < 0)
58 if(ptrace(PT_READ_I, pid, 0, 0)<0 && errno!=EINVAL)
59 if(ptrace(PT_ATTACH, pid, 0, 0) < 0){
60 werrstr("ptrace attach %d: %r", pid);
61 return -1;
62 }
64 if(ctlproc(pid, "waitanyway") < 0){
65 ptrace(PT_DETACH, pid, 0, 0);
66 return -1;
67 }
69 memset(&s, 0, sizeof s);
70 s.base = 0;
71 s.size = 0xFFFFFFFF;
72 s.offset = 0;
73 s.name = "data";
74 s.file = nil;
75 s.rw = ptracerw;
76 s.pid = pid;
77 if(addseg(map, s) < 0)
78 return -1;
80 if((r = mallocz(sizeof(PtraceRegs), 1)) == nil)
81 return -1;
82 r->r.rw = ptraceregrw;
83 r->pid = pid;
84 *rp = (Regs*)r;
85 return 0;
86 }
88 int
89 detachproc(int pid)
90 {
91 return ptrace(PT_DETACH, pid, 0, 0);
92 }
94 static int
95 ptracerw(Map *map, Seg *seg, u64int addr, void *v, uint n, int isr)
96 {
97 int i;
98 u32int u;
99 uchar buf[4];
101 addr += seg->base;
102 for(i=0; i<n; i+=4){
103 if(isr){
104 errno = 0;
105 u = ptrace(PT_READ_D, seg->pid, (char*)addr+i, 0);
106 if(errno)
107 goto ptraceerr;
108 if(n-i >= 4)
109 *(u32int*)((char*)v+i) = u;
110 else{
111 *(u32int*)buf = u;
112 memmove((char*)v+i, buf, n-i);
114 }else{
115 if(n-i >= 4)
116 u = *(u32int*)((char*)v+i);
117 else{
118 errno = 0;
119 u = ptrace(PT_READ_D, seg->pid, (char*)addr+i, 0);
120 if(errno)
121 return -1;
122 *(u32int*)buf = u;
123 memmove(buf, (char*)v+i, n-i);
124 u = *(u32int*)buf;
126 if(ptrace(PT_WRITE_D, seg->pid, (char*)addr+i, u) < 0)
127 goto ptraceerr;
130 return 0;
132 ptraceerr:
133 werrstr("ptrace: %r");
134 return -1;
137 static char *freebsdregs[] = {
138 "FS",
139 "ES",
140 "DS",
141 "DI",
142 "SI",
143 "BP",
144 "SP",
145 "BX",
146 "DX",
147 "CX",
148 "AX",
149 "TRAP",
150 "PC",
151 "CS",
152 "EFLAGS",
153 "SP",
154 "SS",
155 "GS",
156 };
158 static ulong
159 reg2freebsd(char *reg)
161 int i;
163 for(i=0; i<nelem(freebsdregs); i++)
164 if(strcmp(freebsdregs[i], reg) == 0)
165 return 4*i;
166 return ~(ulong)0;
169 static int
170 ptraceregrw(Regs *regs, char *name, u64int *val, int isr)
172 int pid;
173 ulong addr;
174 struct reg mregs;
176 addr = reg2freebsd(name);
177 if(~addr == 0){
178 if(isr){
179 *val = ~(ulong)0;
180 return 0;
182 werrstr("register not available");
183 return -1;
186 pid = ((PtraceRegs*)regs)->pid;
187 if(ptrace(PT_GETREGS, pid, (char*)&mregs, 0) < 0)
188 return -1;
189 if(isr)
190 *val = *(u32int*)((char*)&mregs+addr);
191 else{
192 *(u32int*)((char*)&mregs+addr) = *val;
193 if(ptrace(PT_SETREGS, pid, (char*)&mregs, 0) < 0)
194 return -1;
196 return 0;
199 char*
200 proctextfile(int pid)
202 static char buf[1024], pbuf[128];
204 snprint(pbuf, sizeof pbuf, "/proc/%d/file", pid);
205 if(readlink(pbuf, buf, sizeof buf) >= 0)
206 return buf;
207 if(access(pbuf, AEXIST) >= 0)
208 return pbuf;
209 return nil;
212 /*
214 status The process status. This file is read-only and returns a single
215 line containing multiple space-separated fields as follows:
217 o command name
218 o process id
219 o parent process id
220 o process group id
221 o session id
222 o major,minor of the controlling terminal, or -1,-1 if there is
223 no controlling terminal.
224 o a list of process flags: ctty if there is a controlling ter-
225 minal, sldr if the process is a session leader, noflags if
226 neither of the other two flags are set.
227 o the process start time in seconds and microseconds, comma
228 separated.
229 o the user time in seconds and microseconds, comma separated.
230 o the system time in seconds and microseconds, comma separated.
231 o the wait channel message
232 o the process credentials consisting of the effective user id
233 and the list of groups (whose first member is the effective
234 group id) all comma separated.
235 */
237 int
238 procnotes(int pid, char ***pnotes)
240 /* figure out the set of pending notes - how? */
241 *pnotes = nil;
242 return 0;
245 static int
246 isstopped(int pid)
248 char buf[1024], *f[12];
249 int fd, n, nf;
251 snprint(buf, sizeof buf, "/proc/%d/status", pid);
252 if((fd = open(buf, OREAD)) < 0)
253 return 0;
254 n = read(fd, buf, sizeof buf-1);
255 close(fd);
256 if(n <= 0)
257 return 0;
258 buf[n] = 0;
260 if((nf = tokenize(buf, f, nelem(f))) < 11)
261 return 0;
262 if(strcmp(f[10], "nochan") == 0)
263 return 1;
264 return 0;
267 #undef waitpid
269 int
270 ctlproc(int pid, char *msg)
272 int p, status;
274 if(strcmp(msg, "hang") == 0){
275 if(pid == getpid())
276 return ptrace(PT_TRACE_ME, 0, 0, 0);
277 werrstr("can only hang self");
278 return -1;
280 if(strcmp(msg, "kill") == 0)
281 return ptrace(PT_KILL, pid, 0, 0);
282 if(strcmp(msg, "startstop") == 0){
283 if(ptrace(PT_CONTINUE, pid, 0, 0) < 0)
284 return -1;
285 goto waitstop;
287 /*
288 if(strcmp(msg, "sysstop") == 0){
289 if(ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0)
290 return -1;
291 goto waitstop;
293 */
294 if(strcmp(msg, "stop") == 0){
295 if(kill(pid, SIGSTOP) < 0)
296 return -1;
297 goto waitstop;
299 if(strcmp(msg, "waitanyway") == 0)
300 goto waitanyway;
301 if(strcmp(msg, "waitstop") == 0){
302 waitstop:
303 if(isstopped(pid))
304 return 0;
305 waitanyway:
306 for(;;){
307 p = waitpid(pid, &status, WUNTRACED);
308 if(p <= 0)
309 return -1;
310 if(WIFEXITED(status) || WIFSTOPPED(status))
311 return 0;
314 if(strcmp(msg, "start") == 0)
315 return ptrace(PT_CONTINUE, pid, 0, 0);
316 werrstr("unknown control message '%s'", msg);
317 return -1;