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 void
41 unmapproc(Map *map)
42 {
43 int i;
45 if(map == nil)
46 return;
47 for(i=0; i<map->nseg; i++)
48 while(i<map->nseg && map->seg[i].pid){
49 map->nseg--;
50 memmove(&map->seg[i], &map->seg[i+1],
51 (map->nseg-i)*sizeof(map->seg[0]));
52 }
53 }
55 int
56 mapproc(int pid, Map *map, Regs **rp)
57 {
58 Seg s;
59 PtraceRegs *r;
61 if(ptrace(PTRACE_ATTACH, pid, 0, 0) < 0)
62 if(ptrace(PTRACE_PEEKUSER, pid, 0, 0) < 0)
63 if(ptrace(PTRACE_ATTACH, pid, 0, 0) < 0){
64 werrstr("ptrace attach %d: %r", pid);
65 return -1;
66 }
68 if(ctlproc(pid, "waitstop") < 0){
69 ptrace(PTRACE_DETACH, pid, 0, 0);
70 return -1;
71 }
73 memset(&s, 0, sizeof s);
74 s.base = 0;
75 s.size = 0xFFFFFFFF;
76 s.offset = 0;
77 s.name = "data";
78 s.file = nil;
79 s.rw = ptracerw;
80 s.pid = pid;
81 if(addseg(map, s) < 0)
82 return -1;
84 if((r = mallocz(sizeof(PtraceRegs), 1)) == nil)
85 return -1;
86 r->r.rw = ptraceregrw;
87 r->pid = pid;
88 *rp = (Regs*)r;
89 return 0;
90 }
92 int
93 detachproc(int pid)
94 {
95 return ptrace(PTRACE_DETACH, pid, 0, 0);
96 }
98 static int
99 ptracerw(Map *map, Seg *seg, ulong addr, void *v, uint n, int isr)
101 int i;
102 u32int u;
103 uchar buf[4];
105 addr += seg->base;
106 for(i=0; i<n; i+=4){
107 if(isr){
108 errno = 0;
109 u = ptrace(PTRACE_PEEKDATA, seg->pid, addr+i, 0);
110 if(errno)
111 goto ptraceerr;
112 if(n-i >= 4)
113 *(u32int*)((char*)v+i) = u;
114 else{
115 *(u32int*)buf = u;
116 memmove((char*)v+i, buf, n-i);
118 }else{
119 if(n-i >= 4)
120 u = *(u32int*)((char*)v+i);
121 else{
122 errno = 0;
123 u = ptrace(PTRACE_PEEKDATA, seg->pid, addr+i, 0);
124 if(errno)
125 return -1;
126 *(u32int*)buf = u;
127 memmove(buf, (char*)v+i, n-i);
128 u = *(u32int*)buf;
130 if(ptrace(PTRACE_POKEDATA, seg->pid, addr+i, &u) < 0)
131 goto ptraceerr;
134 return 0;
136 ptraceerr:
137 werrstr("ptrace: %r");
138 return -1;
141 static char* linuxregs[] = {
142 "BX",
143 "CX",
144 "DX",
145 "SI",
146 "DI",
147 "BP",
148 "AX",
149 "DS",
150 "ES",
151 "FS",
152 "GS",
153 "OAX",
154 "PC",
155 "CS",
156 "EFLAGS",
157 "SP",
158 "SS",
159 };
161 static ulong
162 reg2linux(char *reg)
164 int i;
166 for(i=0; i<nelem(linuxregs); i++)
167 if(strcmp(linuxregs[i], reg) == 0)
168 return 4*i;
169 return ~(ulong)0;
172 static int
173 ptraceregrw(Regs *regs, char *name, ulong *val, int isr)
175 int pid;
176 ulong addr;
177 u32int u;
179 pid = ((PtraceRegs*)regs)->pid;
180 addr = reg2linux(name);
181 if(~addr == 0){
182 if(isr){
183 *val = ~(ulong)0;
184 return 0;
186 werrstr("register not available");
187 return -1;
189 if(isr){
190 errno = 0;
191 u = ptrace(PTRACE_PEEKUSER, pid, addr, 0);
192 if(errno)
193 goto ptraceerr;
194 *val = u;
195 }else{
196 u = *val;
197 if(ptrace(PTRACE_POKEUSER, pid, addr, &u) < 0)
198 goto ptraceerr;
200 return 0;
202 ptraceerr:
203 werrstr("ptrace: %r");
204 return -1;
207 static int
208 isstopped(int pid)
210 char buf[1024];
211 int fd, n;
212 char *p;
214 snprint(buf, sizeof buf, "/proc/%d/stat", pid);
215 if((fd = open(buf, OREAD)) < 0)
216 return 0;
217 n = read(fd, buf, sizeof buf-1);
218 close(fd);
219 if(n <= 0)
220 return 0;
221 buf[n] = 0;
223 /* command name is in parens, no parens afterward */
224 p = strrchr(buf, ')');
225 if(p == nil || *++p != ' ')
226 return 0;
227 ++p;
229 /* next is state - T is stopped for tracing */
230 return *p == 'T';
233 /* /proc/pid/stat contains
234 pid
235 command in parens
236 0. state
237 1. ppid
238 2. pgrp
239 3. session
240 4. tty_nr
241 5. tpgid
242 6. flags (math=4, traced=10)
243 7. minflt
244 8. cminflt
245 9. majflt
246 10. cmajflt
247 11. utime
248 12. stime
249 13. cutime
250 14. cstime
251 15. priority
252 16. nice
253 17. 0
254 18. itrealvalue
255 19. starttime
256 20. vsize
257 21. rss
258 22. rlim
259 23. startcode
260 24. endcode
261 25. startstack
262 26. kstkesp
263 27. kstkeip
264 28. pending signal bitmap
265 29. blocked signal bitmap
266 30. ignored signal bitmap
267 31. caught signal bitmap
268 32. wchan
269 33. nswap
270 34. cnswap
271 35. exit_signal
272 36. processor
273 */
276 int
277 procnotes(int pid, char ***pnotes)
279 char buf[1024], *f[40];
280 int fd, i, n, nf;
281 char *p, *s, **notes;
282 ulong sigs;
283 extern char *_p9sigstr(int, char*);
285 *pnotes = nil;
286 snprint(buf, sizeof buf, "/proc/%d/stat", pid);
287 if((fd = open(buf, OREAD)) < 0){
288 fprint(2, "open %s: %r\n", buf);
289 return -1;
291 n = read(fd, buf, sizeof buf-1);
292 close(fd);
293 if(n <= 0){
294 fprint(2, "read %s: %r\n", buf);
295 return -1;
297 buf[n] = 0;
299 /* command name is in parens, no parens afterward */
300 p = strrchr(buf, ')');
301 if(p == nil || *++p != ' '){
302 fprint(2, "bad format in /proc/%d/stat\n", pid);
303 return -1;
305 ++p;
307 nf = tokenize(p, f, nelem(f));
308 if(0) print("code 0x%lux-0x%lux stack 0x%lux kstk 0x%lux keip 0x%lux pending 0x%lux\n",
309 strtoul(f[23], 0, 0), strtoul(f[24], 0, 0), strtoul(f[25], 0, 0),
310 strtoul(f[26], 0, 0), strtoul(f[27], 0, 0), strtoul(f[28], 0, 0));
311 if(nf <= 28)
312 return -1;
314 sigs = strtoul(f[28], 0, 0) & ~(1<<SIGCONT);
315 if(sigs == 0){
316 *pnotes = nil;
317 return 0;
320 notes = mallocz(32*sizeof(char*), 0);
321 if(notes == nil)
322 return -1;
323 n = 0;
324 for(i=0; i<32; i++){
325 if((sigs&(1<<i)) == 0)
326 continue;
327 if((s = _p9sigstr(i, nil)) == nil)
328 continue;
329 notes[n++] = s;
331 *pnotes = notes;
332 return n;
335 #undef waitpid
337 int
338 ctlproc(int pid, char *msg)
340 int p, status;
342 if(strcmp(msg, "hang") == 0){
343 if(pid == getpid())
344 return ptrace(PTRACE_TRACEME, 0, 0, 0);
345 werrstr("can only hang self");
346 return -1;
348 if(strcmp(msg, "kill") == 0)
349 return ptrace(PTRACE_KILL, pid, 0, 0);
350 if(strcmp(msg, "startstop") == 0){
351 if(ptrace(PTRACE_CONT, pid, 0, 0) < 0)
352 return -1;
353 goto waitstop;
355 if(strcmp(msg, "sysstop") == 0){
356 if(ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0)
357 return -1;
358 goto waitstop;
360 if(strcmp(msg, "stop") == 0){
361 if(kill(pid, SIGSTOP) < 0)
362 return -1;
363 goto waitstop;
365 if(strcmp(msg, "waitstop") == 0){
366 waitstop:
367 if(isstopped(pid))
368 return 0;
369 for(;;){
370 p = waitpid(pid, &status, WUNTRACED);
371 if(p <= 0)
372 return -1;
373 if(WIFEXITED(status) || WIFSTOPPED(status))
374 return 0;
377 if(strcmp(msg, "start") == 0)
378 return ptrace(PTRACE_CONT, pid, 0, 0);
379 werrstr("unknown control message '%s'", msg);
380 return -1;
383 char*
384 proctextfile(int pid)
386 static char buf[1024], pbuf[128];
388 snprint(pbuf, sizeof pbuf, "/proc/%d/exe", pid);
389 if(readlink(pbuf, buf, sizeof buf) >= 0)
390 return buf;
391 if(access(pbuf, AEXIST) >= 0)
392 return pbuf;
393 return nil;
397 #if 0
398 snprint(buf, sizeof buf, "/proc/%d/maps", pid);
399 if((b = Bopen(buf, OREAD)) == nil){
400 werrstr("open %s: %r", buf);
401 return -1;
404 /*
405 08048000-08056000 r-xp 00000000 03:0c 64593 /usr/sbin/gpm
406 08056000-08058000 rw-p 0000d000 03:0c 64593 /usr/sbin/gpm
407 08058000-0805b000 rwxp 00000000 00:00 0
408 40000000-40013000 r-xp 00000000 03:0c 4165 /lib/ld-2.2.4.so
409 40013000-40015000 rw-p 00012000 03:0c 4165 /lib/ld-2.2.4.so
410 4001f000-40135000 r-xp 00000000 03:0c 45494 /lib/libc-2.2.4.so
411 40135000-4013e000 rw-p 00115000 03:0c 45494 /lib/libc-2.2.4.so
412 4013e000-40142000 rw-p 00000000 00:00 0
413 bffff000-c0000000 rwxp 00000000 00:00 0
414 */
416 file = nil;
417 while((p = Brdline(b, '\n')) != nil){
418 p[Blinelen(b)-1] = 0;
419 memset(f, 0, sizeof f);
420 if((nf = getfields(p, f, 6, 1, " ")) < 5)
421 continue;
422 base = strtoul(f[0], &p, 16);
423 if(*p != '-')
424 continue;
425 end = strtoul(p+1, &p, 16);
426 if(*p != 0)
427 continue;
428 offset = strtoul(f[2], &p, 16);
429 if(*p != 0)
430 continue;
431 if(nf == 6)
432 file = f[5];
433 zero = atoi(f[4]) == 0;
434 print("%lux-%lux %lux %s %s\n", base, end, offset, zero ? "data" : "text", file ? file : "");
435 s.base = base;
436 s.size = end - base;
437 s.offset = offset;
438 s.name = zero ? "data" : "text";
439 s.file = strdup(file);
440 s.rw = ptracerw;
441 s.pid = pid;
442 if(addseg(map, s) < 0){
443 Bterm(b);
444 ptrace(PTRACE_DETACH, pid, 0, 0);
445 return -1;
448 Bterm(b);
449 #endif