Blob


1 /*
2 * process interface for Linux.
3 *
4 * Uses ptrace for registers and data,
5 * /proc for some process status.
6 * There's not much point to worrying about
7 * byte order here -- using ptrace means
8 * we're running on the architecture we're debugging,
9 * unless truly weird stuff is going on.
10 *
11 * It is tempting to use /proc/%d/mem along with
12 * the sp and pc in the stat file to get a stack trace
13 * without attaching to the program, but unfortunately
14 * you can't read the mem file unless you've attached.
15 */
17 #include <u.h>
18 #include <sys/ptrace.h>
19 #include <sys/types.h>
20 #include <sys/wait.h>
21 #include <signal.h>
22 #include <errno.h>
23 #include <libc.h>
24 #include <mach.h>
25 #include "ureg386.h"
27 Mach *machcpu = &mach386;
29 typedef struct PtraceRegs PtraceRegs;
31 struct PtraceRegs
32 {
33 Regs r;
34 int pid;
35 };
37 static int ptracerw(Map*, Seg*, ulong, void*, uint, int);
38 static int ptraceregrw(Regs*, char*, ulong*, int);
40 static int attachedpids[1000];
41 static int nattached;
43 void
44 unmapproc(Map *map)
45 {
46 int i;
48 if(map == nil)
49 return;
50 for(i=0; i<map->nseg; i++)
51 while(i<map->nseg && map->seg[i].pid){
52 map->nseg--;
53 memmove(&map->seg[i], &map->seg[i+1],
54 (map->nseg-i)*sizeof(map->seg[0]));
55 }
56 }
58 int
59 mapproc(int pid, Map *map, Regs **rp)
60 {
61 int i;
62 Seg s;
63 PtraceRegs *r;
65 if(nattached==1 && attachedpids[0] == pid)
66 goto already;
67 if(nattached)
68 detachproc(attachedpids[0]);
70 for(i=0; i<nattached; i++)
71 if(attachedpids[i]==pid)
72 goto already;
73 if(nattached == nelem(attachedpids)){
74 werrstr("attached to too many processes");
75 return -1;
76 }
78 if(ptrace(PTRACE_ATTACH, pid, 0, 0) < 0){
79 werrstr("ptrace attach %d: %r", pid);
80 return -1;
81 }
83 if(ctlproc(pid, "waitstop") < 0){
84 fprint(2, "waitstop: %r");
85 ptrace(PTRACE_DETACH, pid, 0, 0);
86 return -1;
87 }
88 attachedpids[nattached++] = pid;
90 already:
91 memset(&s, 0, sizeof s);
92 s.base = 0;
93 s.size = 0xFFFFFFFF;
94 s.offset = 0;
95 s.name = "data";
96 s.file = nil;
97 s.rw = ptracerw;
98 s.pid = pid;
99 if(addseg(map, s) < 0){
100 fprint(2, "addseg: %r\n");
101 return -1;
104 if((r = mallocz(sizeof(PtraceRegs), 1)) == nil){
105 fprint(2, "mallocz: %r\n");
106 return -1;
108 r->r.rw = ptraceregrw;
109 r->pid = pid;
110 *rp = (Regs*)r;
111 return 0;
114 int
115 detachproc(int pid)
117 int i;
119 for(i=0; i<nattached; i++){
120 if(attachedpids[i] == pid){
121 attachedpids[i] = attachedpids[--nattached];
122 break;
125 return ptrace(PTRACE_DETACH, pid, 0, 0);
128 static int
129 ptracerw(Map *map, Seg *seg, ulong addr, void *v, uint n, int isr)
131 int i;
132 u32int u;
133 uchar buf[4];
135 addr += seg->base;
136 for(i=0; i<n; i+=4){
137 if(isr){
138 errno = 0;
139 u = ptrace(PTRACE_PEEKDATA, seg->pid, addr+i, 0);
140 if(errno)
141 goto ptraceerr;
142 if(n-i >= 4)
143 *(u32int*)((char*)v+i) = u;
144 else{
145 *(u32int*)buf = u;
146 memmove((char*)v+i, buf, n-i);
148 }else{
149 if(n-i >= 4)
150 u = *(u32int*)((char*)v+i);
151 else{
152 errno = 0;
153 u = ptrace(PTRACE_PEEKDATA, seg->pid, addr+i, 0);
154 if(errno)
155 return -1;
156 *(u32int*)buf = u;
157 memmove(buf, (char*)v+i, n-i);
158 u = *(u32int*)buf;
160 if(ptrace(PTRACE_POKEDATA, seg->pid, addr+i, &u) < 0)
161 goto ptraceerr;
164 return 0;
166 ptraceerr:
167 werrstr("ptrace: %r");
168 return -1;
171 static char* linuxregs[] = {
172 "BX",
173 "CX",
174 "DX",
175 "SI",
176 "DI",
177 "BP",
178 "AX",
179 "DS",
180 "ES",
181 "FS",
182 "GS",
183 "OAX",
184 "PC",
185 "CS",
186 "EFLAGS",
187 "SP",
188 "SS",
189 };
191 static ulong
192 reg2linux(char *reg)
194 int i;
196 for(i=0; i<nelem(linuxregs); i++)
197 if(strcmp(linuxregs[i], reg) == 0)
198 return 4*i;
199 return ~(ulong)0;
202 static int
203 ptraceregrw(Regs *regs, char *name, ulong *val, int isr)
205 int pid;
206 ulong addr;
207 u32int u;
209 pid = ((PtraceRegs*)regs)->pid;
210 addr = reg2linux(name);
211 if(~addr == 0){
212 if(isr){
213 *val = ~(ulong)0;
214 return 0;
216 werrstr("register not available");
217 return -1;
219 if(isr){
220 errno = 0;
221 u = ptrace(PTRACE_PEEKUSER, pid, addr, 0);
222 if(errno)
223 goto ptraceerr;
224 *val = u;
225 }else{
226 u = *val;
227 if(ptrace(PTRACE_POKEUSER, pid, addr, &u) < 0)
228 goto ptraceerr;
230 return 0;
232 ptraceerr:
233 werrstr("ptrace: %r");
234 return -1;
237 static int
238 isstopped(int pid)
240 char buf[1024];
241 int fd, n;
242 char *p;
244 snprint(buf, sizeof buf, "/proc/%d/stat", pid);
245 if((fd = open(buf, OREAD)) < 0)
246 return 0;
247 n = read(fd, buf, sizeof buf-1);
248 close(fd);
249 if(n <= 0)
250 return 0;
251 buf[n] = 0;
253 /* command name is in parens, no parens afterward */
254 p = strrchr(buf, ')');
255 if(p == nil || *++p != ' ')
256 return 0;
257 ++p;
259 /* next is state - T is stopped for tracing */
260 return *p == 'T';
263 /* /proc/pid/stat contains
264 pid
265 command in parens
266 0. state
267 1. ppid
268 2. pgrp
269 3. session
270 4. tty_nr
271 5. tpgid
272 6. flags (math=4, traced=10)
273 7. minflt
274 8. cminflt
275 9. majflt
276 10. cmajflt
277 11. utime
278 12. stime
279 13. cutime
280 14. cstime
281 15. priority
282 16. nice
283 17. 0
284 18. itrealvalue
285 19. starttime
286 20. vsize
287 21. rss
288 22. rlim
289 23. startcode
290 24. endcode
291 25. startstack
292 26. kstkesp
293 27. kstkeip
294 28. pending signal bitmap
295 29. blocked signal bitmap
296 30. ignored signal bitmap
297 31. caught signal bitmap
298 32. wchan
299 33. nswap
300 34. cnswap
301 35. exit_signal
302 36. processor
303 */
306 int
307 procnotes(int pid, char ***pnotes)
309 char buf[1024], *f[40];
310 int fd, i, n, nf;
311 char *p, *s, **notes;
312 ulong sigs;
313 extern char *_p9sigstr(int, char*);
315 *pnotes = nil;
316 snprint(buf, sizeof buf, "/proc/%d/stat", pid);
317 if((fd = open(buf, OREAD)) < 0){
318 fprint(2, "open %s: %r\n", buf);
319 return -1;
321 n = read(fd, buf, sizeof buf-1);
322 close(fd);
323 if(n <= 0){
324 fprint(2, "read %s: %r\n", buf);
325 return -1;
327 buf[n] = 0;
329 /* command name is in parens, no parens afterward */
330 p = strrchr(buf, ')');
331 if(p == nil || *++p != ' '){
332 fprint(2, "bad format in /proc/%d/stat\n", pid);
333 return -1;
335 ++p;
337 nf = tokenize(p, f, nelem(f));
338 if(0) print("code 0x%lux-0x%lux stack 0x%lux kstk 0x%lux keip 0x%lux pending 0x%lux\n",
339 strtoul(f[23], 0, 0), strtoul(f[24], 0, 0), strtoul(f[25], 0, 0),
340 strtoul(f[26], 0, 0), strtoul(f[27], 0, 0), strtoul(f[28], 0, 0));
341 if(nf <= 28)
342 return -1;
344 sigs = strtoul(f[28], 0, 0) & ~(1<<SIGCONT);
345 if(sigs == 0){
346 *pnotes = nil;
347 return 0;
350 notes = mallocz(32*sizeof(char*), 0);
351 if(notes == nil)
352 return -1;
353 n = 0;
354 for(i=0; i<32; i++){
355 if((sigs&(1<<i)) == 0)
356 continue;
357 if((s = _p9sigstr(i, nil)) == nil)
358 continue;
359 notes[n++] = s;
361 *pnotes = notes;
362 return n;
365 #undef waitpid
367 int
368 ctlproc(int pid, char *msg)
370 int p, status;
372 if(strcmp(msg, "hang") == 0){
373 if(pid == getpid())
374 return ptrace(PTRACE_TRACEME, 0, 0, 0);
375 werrstr("can only hang self");
376 return -1;
378 if(strcmp(msg, "kill") == 0)
379 return ptrace(PTRACE_KILL, pid, 0, 0);
380 if(strcmp(msg, "startstop") == 0){
381 if(ptrace(PTRACE_CONT, pid, 0, 0) < 0)
382 return -1;
383 goto waitstop;
385 if(strcmp(msg, "sysstop") == 0){
386 if(ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0)
387 return -1;
388 goto waitstop;
390 if(strcmp(msg, "stop") == 0){
391 if(kill(pid, SIGSTOP) < 0)
392 return -1;
393 goto waitstop;
395 if(strcmp(msg, "waitstop") == 0){
396 waitstop:
397 if(isstopped(pid))
398 return 0;
399 for(;;){
400 p = waitpid(pid, &status, WUNTRACED|__WALL);
401 if(p <= 0){
402 if(errno == ECHILD){
403 if(isstopped(pid))
404 return 0;
406 return -1;
408 if(WIFEXITED(status) || WIFSTOPPED(status))
409 return 0;
412 if(strcmp(msg, "start") == 0)
413 return ptrace(PTRACE_CONT, pid, 0, 0);
414 werrstr("unknown control message '%s'", msg);
415 return -1;
418 char*
419 proctextfile(int pid)
421 static char buf[1024], pbuf[128];
423 snprint(pbuf, sizeof pbuf, "/proc/%d/exe", pid);
424 if(readlink(pbuf, buf, sizeof buf) >= 0)
425 return buf;
426 if(access(pbuf, AEXIST) >= 0)
427 return pbuf;
428 return nil;
432 #if 0
433 snprint(buf, sizeof buf, "/proc/%d/maps", pid);
434 if((b = Bopen(buf, OREAD)) == nil){
435 werrstr("open %s: %r", buf);
436 return -1;
439 /*
440 08048000-08056000 r-xp 00000000 03:0c 64593 /usr/sbin/gpm
441 08056000-08058000 rw-p 0000d000 03:0c 64593 /usr/sbin/gpm
442 08058000-0805b000 rwxp 00000000 00:00 0
443 40000000-40013000 r-xp 00000000 03:0c 4165 /lib/ld-2.2.4.so
444 40013000-40015000 rw-p 00012000 03:0c 4165 /lib/ld-2.2.4.so
445 4001f000-40135000 r-xp 00000000 03:0c 45494 /lib/libc-2.2.4.so
446 40135000-4013e000 rw-p 00115000 03:0c 45494 /lib/libc-2.2.4.so
447 4013e000-40142000 rw-p 00000000 00:00 0
448 bffff000-c0000000 rwxp 00000000 00:00 0
449 */
451 file = nil;
452 while((p = Brdline(b, '\n')) != nil){
453 p[Blinelen(b)-1] = 0;
454 memset(f, 0, sizeof f);
455 if((nf = getfields(p, f, 6, 1, " ")) < 5)
456 continue;
457 base = strtoul(f[0], &p, 16);
458 if(*p != '-')
459 continue;
460 end = strtoul(p+1, &p, 16);
461 if(*p != 0)
462 continue;
463 offset = strtoul(f[2], &p, 16);
464 if(*p != 0)
465 continue;
466 if(nf == 6)
467 file = f[5];
468 zero = atoi(f[4]) == 0;
469 print("%lux-%lux %lux %s %s\n", base, end, offset, zero ? "data" : "text", file ? file : "");
470 s.base = base;
471 s.size = end - base;
472 s.offset = offset;
473 s.name = zero ? "data" : "text";
474 s.file = strdup(file);
475 s.rw = ptracerw;
476 s.pid = pid;
477 if(addseg(map, s) < 0){
478 Bterm(b);
479 ptrace(PTRACE_DETACH, pid, 0, 0);
480 return -1;
483 Bterm(b);
484 #endif