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 <sys/procfs.h>
22 #include <signal.h>
23 #include <errno.h>
24 #include <libc.h>
25 #include <mach.h>
26 #include <elf.h>
27 #include "ureg386.h"
29 Mach *machcpu = &mach386;
31 typedef struct PtraceRegs PtraceRegs;
33 struct PtraceRegs
34 {
35 Regs r;
36 int pid;
37 };
39 static int ptracesegrw(Map*, Seg*, u64int, void*, uint, int);
40 static int ptraceregrw(Regs*, char*, u64int*, int);
42 static int attachedpids[1000];
43 static int nattached;
45 static int
46 ptraceattach(int pid)
47 {
48 int i;
50 for(i=0; i<nattached; i++)
51 if(attachedpids[i]==pid)
52 return 0;
53 if(nattached == nelem(attachedpids)){
54 werrstr("attached to too many processes");
55 return -1;
56 }
58 if(ptrace(PTRACE_ATTACH, pid, 0, 0) < 0){
59 werrstr("ptrace attach %d: %r", pid);
60 return -1;
61 }
63 if(ctlproc(pid, "waitstop") < 0){
64 fprint(2, "waitstop: %r");
65 ptrace(PTRACE_DETACH, pid, 0, 0);
66 return -1;
67 }
68 attachedpids[nattached++] = pid;
69 return 0;
70 }
72 void
73 unmapproc(Map *map)
74 {
75 int i;
77 if(map == nil)
78 return;
79 for(i=0; i<map->nseg; i++)
80 while(i<map->nseg && map->seg[i].pid){
81 map->nseg--;
82 memmove(&map->seg[i], &map->seg[i+1],
83 (map->nseg-i)*sizeof(map->seg[0]));
84 }
85 }
87 int
88 mapproc(int pid, Map *map, Regs **rp)
89 {
90 Seg s;
91 PtraceRegs *r;
93 if(ptraceattach(pid) < 0)
94 return -1;
96 memset(&s, 0, sizeof s);
97 s.base = 0;
98 s.size = 0xFFFFFFFF;
99 s.offset = 0;
100 s.name = "data";
101 s.file = nil;
102 s.rw = ptracesegrw;
103 s.pid = pid;
104 if(addseg(map, s) < 0){
105 fprint(2, "addseg: %r\n");
106 return -1;
109 if((r = mallocz(sizeof(PtraceRegs), 1)) == nil){
110 fprint(2, "mallocz: %r\n");
111 return -1;
113 r->r.rw = ptraceregrw;
114 r->pid = pid;
115 *rp = (Regs*)r;
116 return 0;
119 int
120 detachproc(int pid)
122 int i;
124 for(i=0; i<nattached; i++){
125 if(attachedpids[i] == pid){
126 attachedpids[i] = attachedpids[--nattached];
127 break;
130 return ptrace(PTRACE_DETACH, pid, 0, 0);
133 static int
134 ptracerw(int type, int xtype, int isr, int pid, ulong addr, void *v, uint n)
136 int i;
137 u32int u;
138 uchar buf[4];
140 for(i=0; i<n; i+=4){
141 if(isr){
142 errno = 0;
143 u = ptrace(type, pid, addr+i, 0);
144 if(errno)
145 goto ptraceerr;
146 if(n-i >= 4)
147 *(u32int*)((char*)v+i) = u;
148 else{
149 memmove(buf, &u, 4);
150 memmove((char*)v+i, buf, n-i);
152 }else{
153 if(n-i >= 4)
154 u = *(u32int*)((char*)v+i);
155 else{
156 errno = 0;
157 u = ptrace(xtype, pid, addr+i, 0);
158 if(errno)
159 return -1;
160 memmove(buf, &u, 4);
161 memmove(buf, (char*)v+i, n-i);
162 memmove(&u, buf, 4);
164 if(ptrace(type, pid, addr+i, u) < 0)
165 goto ptraceerr;
168 return 0;
170 ptraceerr:
171 werrstr("ptrace: %r");
172 return -1;
175 static int
176 ptracesegrw(Map *map, Seg *seg, u64int addr, void *v, uint n, int isr)
178 addr += seg->base;
179 return ptracerw(isr ? PTRACE_PEEKDATA : PTRACE_POKEDATA, PTRACE_PEEKDATA,
180 isr, seg->pid, addr, v, n);
183 static char* linuxregs[] = {
184 "BX",
185 "CX",
186 "DX",
187 "SI",
188 "DI",
189 "BP",
190 "AX",
191 "DS",
192 "ES",
193 "FS",
194 "GS",
195 "OAX",
196 "PC",
197 "CS",
198 "EFLAGS",
199 "SP",
200 "SS",
201 };
203 static ulong
204 reg2linux(char *reg)
206 int i;
208 for(i=0; i<nelem(linuxregs); i++)
209 if(strcmp(linuxregs[i], reg) == 0)
210 return 4*i;
211 return ~(ulong)0;
214 static int
215 ptraceregrw(Regs *regs, char *name, u64int *val, int isr)
217 int pid;
218 ulong addr;
219 u32int u;
221 pid = ((PtraceRegs*)regs)->pid;
222 addr = reg2linux(name);
223 if(~addr == 0){
224 if(isr){
225 *val = ~(ulong)0;
226 return 0;
228 werrstr("register not available");
229 return -1;
231 if(isr){
232 errno = 0;
233 u = ptrace(PTRACE_PEEKUSER, pid, addr, 0);
234 if(errno)
235 goto ptraceerr;
236 *val = u;
237 }else{
238 u = *val;
239 if(ptrace(PTRACE_POKEUSER, pid, addr, (void*)(uintptr)u) < 0)
240 goto ptraceerr;
242 return 0;
244 ptraceerr:
245 werrstr("ptrace: %r");
246 return -1;
249 static int
250 isstopped(int pid)
252 char buf[1024];
253 int fd, n;
254 char *p;
256 snprint(buf, sizeof buf, "/proc/%d/stat", pid);
257 if((fd = open(buf, OREAD)) < 0)
258 return 0;
259 n = read(fd, buf, sizeof buf-1);
260 close(fd);
261 if(n <= 0)
262 return 0;
263 buf[n] = 0;
265 /* command name is in parens, no parens afterward */
266 p = strrchr(buf, ')');
267 if(p == nil || *++p != ' ')
268 return 0;
269 ++p;
271 /* next is state - T is stopped for tracing */
272 return *p == 'T';
275 /* /proc/pid/stat contains
276 pid
277 command in parens
278 0. state
279 1. ppid
280 2. pgrp
281 3. session
282 4. tty_nr
283 5. tpgid
284 6. flags (math=4, traced=10)
285 7. minflt
286 8. cminflt
287 9. majflt
288 10. cmajflt
289 11. utime
290 12. stime
291 13. cutime
292 14. cstime
293 15. priority
294 16. nice
295 17. 0
296 18. itrealvalue
297 19. starttime
298 20. vsize
299 21. rss
300 22. rlim
301 23. startcode
302 24. endcode
303 25. startstack
304 26. kstkesp
305 27. kstkeip
306 28. pending signal bitmap
307 29. blocked signal bitmap
308 30. ignored signal bitmap
309 31. caught signal bitmap
310 32. wchan
311 33. nswap
312 34. cnswap
313 35. exit_signal
314 36. processor
315 */
317 int
318 procnotes(int pid, char ***pnotes)
320 char buf[1024], *f[40];
321 int fd, i, n, nf;
322 char *p, *s, **notes;
323 ulong sigs;
324 extern char *_p9sigstr(int, char*);
326 *pnotes = nil;
327 snprint(buf, sizeof buf, "/proc/%d/stat", pid);
328 if((fd = open(buf, OREAD)) < 0){
329 fprint(2, "open %s: %r\n", buf);
330 return -1;
332 n = read(fd, buf, sizeof buf-1);
333 close(fd);
334 if(n <= 0){
335 fprint(2, "read %s: %r\n", buf);
336 return -1;
338 buf[n] = 0;
340 /* command name is in parens, no parens afterward */
341 p = strrchr(buf, ')');
342 if(p == nil || *++p != ' '){
343 fprint(2, "bad format in /proc/%d/stat\n", pid);
344 return -1;
346 ++p;
348 nf = tokenize(p, f, nelem(f));
349 if(0) print("code 0x%lux-0x%lux stack 0x%lux kstk 0x%lux keip 0x%lux pending 0x%lux\n",
350 strtoul(f[23], 0, 0), strtoul(f[24], 0, 0), strtoul(f[25], 0, 0),
351 strtoul(f[26], 0, 0), strtoul(f[27], 0, 0), strtoul(f[28], 0, 0));
352 if(nf <= 28)
353 return -1;
355 sigs = strtoul(f[28], 0, 0) & ~(1<<SIGCONT);
356 if(sigs == 0){
357 *pnotes = nil;
358 return 0;
361 notes = mallocz(32*sizeof(char*), 0);
362 if(notes == nil)
363 return -1;
364 n = 0;
365 for(i=0; i<32; i++){
366 if((sigs&(1<<i)) == 0)
367 continue;
368 if((s = _p9sigstr(i, nil)) == nil)
369 continue;
370 notes[n++] = s;
372 *pnotes = notes;
373 return n;
376 #undef waitpid
378 int
379 ctlproc(int pid, char *msg)
381 int i, p, status;
383 if(strcmp(msg, "attached") == 0){
384 for(i=0; i<nattached; i++)
385 if(attachedpids[i]==pid)
386 return 0;
387 if(nattached == nelem(attachedpids)){
388 werrstr("attached to too many processes");
389 return -1;
391 attachedpids[nattached++] = pid;
392 return 0;
395 if(strcmp(msg, "hang") == 0){
396 if(pid == getpid())
397 return ptrace(PTRACE_TRACEME, 0, 0, 0);
398 werrstr("can only hang self");
399 return -1;
401 if(strcmp(msg, "kill") == 0)
402 return ptrace(PTRACE_KILL, pid, 0, 0);
403 if(strcmp(msg, "startstop") == 0){
404 if(ptrace(PTRACE_CONT, pid, 0, 0) < 0)
405 return -1;
406 goto waitstop;
408 if(strcmp(msg, "sysstop") == 0){
409 if(ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0)
410 return -1;
411 goto waitstop;
413 if(strcmp(msg, "stop") == 0){
414 if(kill(pid, SIGSTOP) < 0)
415 return -1;
416 goto waitstop;
418 if(strcmp(msg, "step") == 0){
419 if(ptrace(PTRACE_SINGLESTEP, pid, 0, 0) < 0)
420 return -1;
421 goto waitstop;
423 if(strcmp(msg, "waitstop") == 0){
424 waitstop:
425 if(isstopped(pid))
426 return 0;
427 for(;;){
428 p = waitpid(pid, &status, WUNTRACED|__WALL);
429 if(p <= 0){
430 if(errno == ECHILD){
431 if(isstopped(pid))
432 return 0;
434 return -1;
436 /*fprint(2, "got pid %d status %x\n", pid, status); */
437 if(WIFEXITED(status) || WIFSTOPPED(status))
438 return 0;
441 if(strcmp(msg, "start") == 0)
442 return ptrace(PTRACE_CONT, pid, 0, 0);
443 werrstr("unknown control message '%s'", msg);
444 return -1;
447 char*
448 proctextfile(int pid)
450 static char buf[1024], pbuf[128];
452 snprint(pbuf, sizeof pbuf, "/proc/%d/exe", pid);
453 if(readlink(pbuf, buf, sizeof buf) >= 0)
454 return buf;
455 if(access(pbuf, AEXIST) >= 0)
456 return pbuf;
457 return nil;