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*, ulong, void*, uint, int);
40 static int ptraceregrw(Regs*, char*, ulong*, int);
42 static int attachedpids[1000];
43 static int nattached;
45 static int
46 ptraceattach(int pid)
47 {
48 int i;
50 /*
51 if(nattached==1 && attachedpids[0] == pid)
52 goto already;
53 if(nattached)
54 detachproc(attachedpids[0]);
55 */
57 for(i=0; i<nattached; i++)
58 if(attachedpids[i]==pid)
59 return 0;
60 if(nattached == nelem(attachedpids)){
61 werrstr("attached to too many processes");
62 return -1;
63 }
65 if(ptrace(PTRACE_ATTACH, pid, 0, 0) < 0){
66 werrstr("ptrace attach %d: %r", pid);
67 return -1;
68 }
70 if(ctlproc(pid, "waitstop") < 0){
71 fprint(2, "waitstop: %r");
72 ptrace(PTRACE_DETACH, pid, 0, 0);
73 return -1;
74 }
75 attachedpids[nattached++] = pid;
76 return 0;
77 }
79 void
80 unmapproc(Map *map)
81 {
82 int i;
84 if(map == nil)
85 return;
86 for(i=0; i<map->nseg; i++)
87 while(i<map->nseg && map->seg[i].pid){
88 map->nseg--;
89 memmove(&map->seg[i], &map->seg[i+1],
90 (map->nseg-i)*sizeof(map->seg[0]));
91 }
92 }
94 int
95 mapproc(int pid, Map *map, Regs **rp)
96 {
97 Seg s;
98 PtraceRegs *r;
100 if(ptraceattach(pid) < 0)
101 return -1;
103 memset(&s, 0, sizeof s);
104 s.base = 0;
105 s.size = 0xFFFFFFFF;
106 s.offset = 0;
107 s.name = "data";
108 s.file = nil;
109 s.rw = ptracesegrw;
110 s.pid = pid;
111 if(addseg(map, s) < 0){
112 fprint(2, "addseg: %r\n");
113 return -1;
116 if((r = mallocz(sizeof(PtraceRegs), 1)) == nil){
117 fprint(2, "mallocz: %r\n");
118 return -1;
120 r->r.rw = ptraceregrw;
121 r->pid = pid;
122 *rp = (Regs*)r;
123 return 0;
126 int
127 detachproc(int pid)
129 int i;
131 for(i=0; i<nattached; i++){
132 if(attachedpids[i] == pid){
133 attachedpids[i] = attachedpids[--nattached];
134 break;
137 return ptrace(PTRACE_DETACH, pid, 0, 0);
140 static int
141 ptracerw(int type, int xtype, int isr, int pid, ulong addr, void *v, uint n)
143 int i;
144 u32int u;
145 uchar buf[4];
147 for(i=0; i<n; i+=4){
148 if(isr){
149 errno = 0;
150 u = ptrace(type, pid, addr+i, 0);
151 if(errno)
152 goto ptraceerr;
153 if(n-i >= 4)
154 *(u32int*)((char*)v+i) = u;
155 else{
156 *(u32int*)buf = u;
157 memmove((char*)v+i, buf, n-i);
159 }else{
160 if(n-i >= 4)
161 u = *(u32int*)((char*)v+i);
162 else{
163 errno = 0;
164 u = ptrace(xtype, pid, addr+i, 0);
165 if(errno)
166 return -1;
167 *(u32int*)buf = u;
168 memmove(buf, (char*)v+i, n-i);
169 u = *(u32int*)buf;
171 if(ptrace(type, pid, addr+i, u) < 0)
172 goto ptraceerr;
175 return 0;
177 ptraceerr:
178 werrstr("ptrace: %r");
179 return -1;
182 static int
183 ptracesegrw(Map *map, Seg *seg, ulong addr, void *v, uint n, int isr)
185 addr += seg->base;
186 return ptracerw(isr ? PTRACE_PEEKDATA : PTRACE_POKEDATA, PTRACE_PEEKDATA,
187 isr, seg->pid, addr, v, n);
190 static char* linuxregs[] = {
191 "BX",
192 "CX",
193 "DX",
194 "SI",
195 "DI",
196 "BP",
197 "AX",
198 "DS",
199 "ES",
200 "FS",
201 "GS",
202 "OAX",
203 "PC",
204 "CS",
205 "EFLAGS",
206 "SP",
207 "SS",
208 };
210 static ulong
211 reg2linux(char *reg)
213 int i;
215 for(i=0; i<nelem(linuxregs); i++)
216 if(strcmp(linuxregs[i], reg) == 0)
217 return 4*i;
218 return ~(ulong)0;
221 static int
222 ptraceregrw(Regs *regs, char *name, ulong *val, int isr)
224 int pid;
225 ulong addr;
226 u32int u;
228 pid = ((PtraceRegs*)regs)->pid;
229 addr = reg2linux(name);
230 if(~addr == 0){
231 if(isr){
232 *val = ~(ulong)0;
233 return 0;
235 werrstr("register not available");
236 return -1;
238 if(isr){
239 errno = 0;
240 u = ptrace(PTRACE_PEEKUSER, pid, addr, 0);
241 if(errno)
242 goto ptraceerr;
243 *val = u;
244 }else{
245 u = *val;
246 if(ptrace(PTRACE_POKEUSER, pid, addr, &u) < 0)
247 goto ptraceerr;
249 return 0;
251 ptraceerr:
252 werrstr("ptrace: %r");
253 return -1;
256 static int
257 isstopped(int pid)
259 char buf[1024];
260 int fd, n;
261 char *p;
263 snprint(buf, sizeof buf, "/proc/%d/stat", pid);
264 if((fd = open(buf, OREAD)) < 0)
265 return 0;
266 n = read(fd, buf, sizeof buf-1);
267 close(fd);
268 if(n <= 0)
269 return 0;
270 buf[n] = 0;
272 /* command name is in parens, no parens afterward */
273 p = strrchr(buf, ')');
274 if(p == nil || *++p != ' ')
275 return 0;
276 ++p;
278 /* next is state - T is stopped for tracing */
279 return *p == 'T';
282 /* /proc/pid/stat contains
283 pid
284 command in parens
285 0. state
286 1. ppid
287 2. pgrp
288 3. session
289 4. tty_nr
290 5. tpgid
291 6. flags (math=4, traced=10)
292 7. minflt
293 8. cminflt
294 9. majflt
295 10. cmajflt
296 11. utime
297 12. stime
298 13. cutime
299 14. cstime
300 15. priority
301 16. nice
302 17. 0
303 18. itrealvalue
304 19. starttime
305 20. vsize
306 21. rss
307 22. rlim
308 23. startcode
309 24. endcode
310 25. startstack
311 26. kstkesp
312 27. kstkeip
313 28. pending signal bitmap
314 29. blocked signal bitmap
315 30. ignored signal bitmap
316 31. caught signal bitmap
317 32. wchan
318 33. nswap
319 34. cnswap
320 35. exit_signal
321 36. processor
322 */
325 int
326 procnotes(int pid, char ***pnotes)
328 char buf[1024], *f[40];
329 int fd, i, n, nf;
330 char *p, *s, **notes;
331 ulong sigs;
332 extern char *_p9sigstr(int, char*);
334 *pnotes = nil;
335 snprint(buf, sizeof buf, "/proc/%d/stat", pid);
336 if((fd = open(buf, OREAD)) < 0){
337 fprint(2, "open %s: %r\n", buf);
338 return -1;
340 n = read(fd, buf, sizeof buf-1);
341 close(fd);
342 if(n <= 0){
343 fprint(2, "read %s: %r\n", buf);
344 return -1;
346 buf[n] = 0;
348 /* command name is in parens, no parens afterward */
349 p = strrchr(buf, ')');
350 if(p == nil || *++p != ' '){
351 fprint(2, "bad format in /proc/%d/stat\n", pid);
352 return -1;
354 ++p;
356 nf = tokenize(p, f, nelem(f));
357 if(0) print("code 0x%lux-0x%lux stack 0x%lux kstk 0x%lux keip 0x%lux pending 0x%lux\n",
358 strtoul(f[23], 0, 0), strtoul(f[24], 0, 0), strtoul(f[25], 0, 0),
359 strtoul(f[26], 0, 0), strtoul(f[27], 0, 0), strtoul(f[28], 0, 0));
360 if(nf <= 28)
361 return -1;
363 sigs = strtoul(f[28], 0, 0) & ~(1<<SIGCONT);
364 if(sigs == 0){
365 *pnotes = nil;
366 return 0;
369 notes = mallocz(32*sizeof(char*), 0);
370 if(notes == nil)
371 return -1;
372 n = 0;
373 for(i=0; i<32; i++){
374 if((sigs&(1<<i)) == 0)
375 continue;
376 if((s = _p9sigstr(i, nil)) == nil)
377 continue;
378 notes[n++] = s;
380 *pnotes = notes;
381 return n;
384 #undef waitpid
386 int
387 ctlproc(int pid, char *msg)
389 int p, status;
391 if(strcmp(msg, "hang") == 0){
392 if(pid == getpid())
393 return ptrace(PTRACE_TRACEME, 0, 0, 0);
394 werrstr("can only hang self");
395 return -1;
397 if(strcmp(msg, "kill") == 0)
398 return ptrace(PTRACE_KILL, pid, 0, 0);
399 if(strcmp(msg, "startstop") == 0){
400 if(ptrace(PTRACE_CONT, pid, 0, 0) < 0)
401 return -1;
402 goto waitstop;
404 if(strcmp(msg, "sysstop") == 0){
405 if(ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0)
406 return -1;
407 goto waitstop;
409 if(strcmp(msg, "stop") == 0){
410 if(kill(pid, SIGSTOP) < 0)
411 return -1;
412 goto waitstop;
414 if(strcmp(msg, "waitstop") == 0){
415 waitstop:
416 if(isstopped(pid))
417 return 0;
418 for(;;){
419 p = waitpid(pid, &status, WUNTRACED|__WALL);
420 if(p <= 0){
421 if(errno == ECHILD){
422 if(isstopped(pid))
423 return 0;
425 return -1;
427 if(WIFEXITED(status) || WIFSTOPPED(status))
428 return 0;
431 if(strcmp(msg, "start") == 0)
432 return ptrace(PTRACE_CONT, pid, 0, 0);
433 werrstr("unknown control message '%s'", msg);
434 return -1;
437 char*
438 proctextfile(int pid)
440 static char buf[1024], pbuf[128];
442 snprint(pbuf, sizeof pbuf, "/proc/%d/exe", pid);
443 if(readlink(pbuf, buf, sizeof buf) >= 0)
444 return buf;
445 if(access(pbuf, AEXIST) >= 0)
446 return pbuf;
447 return nil;