tLinux.c - plan9port - [fork] Plan 9 from user space
HTML git clone git://src.adamsgaard.dk/plan9port
DIR Log
DIR Files
DIR Refs
DIR README
DIR LICENSE
---
tLinux.c (8207B)
---
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 */
16
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"
28
29 Mach *machcpu = &mach386;
30
31 typedef struct PtraceRegs PtraceRegs;
32
33 struct PtraceRegs
34 {
35 Regs r;
36 int pid;
37 };
38
39 static int ptracesegrw(Map*, Seg*, u64int, void*, uint, int);
40 static int ptraceregrw(Regs*, char*, u64int*, int);
41
42 static int attachedpids[1000];
43 static int nattached;
44
45 static int
46 ptraceattach(int pid)
47 {
48 int i;
49
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 }
57
58 if(ptrace(PTRACE_ATTACH, pid, 0, 0) < 0){
59 werrstr("ptrace attach %d: %r", pid);
60 return -1;
61 }
62
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 }
71
72 void
73 unmapproc(Map *map)
74 {
75 int i;
76
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 }
86
87 int
88 mapproc(int pid, Map *map, Regs **rp)
89 {
90 Seg s;
91 PtraceRegs *r;
92
93 if(ptraceattach(pid) < 0)
94 return -1;
95
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;
107 }
108
109 if((r = mallocz(sizeof(PtraceRegs), 1)) == nil){
110 fprint(2, "mallocz: %r\n");
111 return -1;
112 }
113 r->r.rw = ptraceregrw;
114 r->pid = pid;
115 *rp = (Regs*)r;
116 return 0;
117 }
118
119 int
120 detachproc(int pid)
121 {
122 int i;
123
124 for(i=0; i<nattached; i++){
125 if(attachedpids[i] == pid){
126 attachedpids[i] = attachedpids[--nattached];
127 break;
128 }
129 }
130 return ptrace(PTRACE_DETACH, pid, 0, 0);
131 }
132
133 static int
134 ptracerw(int type, int xtype, int isr, int pid, ulong addr, void *v, uint n)
135 {
136 int i;
137 u32int u;
138 uchar buf[4];
139
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);
151 }
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);
163 }
164 if(ptrace(type, pid, addr+i, u) < 0)
165 goto ptraceerr;
166 }
167 }
168 return 0;
169
170 ptraceerr:
171 werrstr("ptrace: %r");
172 return -1;
173 }
174
175 static int
176 ptracesegrw(Map *map, Seg *seg, u64int addr, void *v, uint n, int isr)
177 {
178 addr += seg->base;
179 return ptracerw(isr ? PTRACE_PEEKDATA : PTRACE_POKEDATA, PTRACE_PEEKDATA,
180 isr, seg->pid, addr, v, n);
181 }
182
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 };
202
203 static ulong
204 reg2linux(char *reg)
205 {
206 int i;
207
208 for(i=0; i<nelem(linuxregs); i++)
209 if(strcmp(linuxregs[i], reg) == 0)
210 return 4*i;
211 return ~(ulong)0;
212 }
213
214 static int
215 ptraceregrw(Regs *regs, char *name, u64int *val, int isr)
216 {
217 int pid;
218 ulong addr;
219 u32int u;
220
221 pid = ((PtraceRegs*)regs)->pid;
222 addr = reg2linux(name);
223 if(~addr == 0){
224 if(isr){
225 *val = ~(ulong)0;
226 return 0;
227 }
228 werrstr("register not available");
229 return -1;
230 }
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;
241 }
242 return 0;
243
244 ptraceerr:
245 werrstr("ptrace: %r");
246 return -1;
247 }
248
249 static int
250 isstopped(int pid)
251 {
252 char buf[1024];
253 int fd, n;
254 char *p;
255
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;
264
265 /* command name is in parens, no parens afterward */
266 p = strrchr(buf, ')');
267 if(p == nil || *++p != ' ')
268 return 0;
269 ++p;
270
271 /* next is state - T is stopped for tracing */
272 return *p == 'T';
273 }
274
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 */
316
317 int
318 procnotes(int pid, char ***pnotes)
319 {
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*);
325
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;
331 }
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;
337 }
338 buf[n] = 0;
339
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;
345 }
346 ++p;
347
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;
354
355 sigs = strtoul(f[28], 0, 0) & ~(1<<SIGCONT);
356 if(sigs == 0){
357 *pnotes = nil;
358 return 0;
359 }
360
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;
371 }
372 *pnotes = notes;
373 return n;
374 }
375
376 #undef waitpid
377
378 int
379 ctlproc(int pid, char *msg)
380 {
381 int i, p, status;
382
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;
390 }
391 attachedpids[nattached++] = pid;
392 return 0;
393 }
394
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;
400 }
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;
407 }
408 if(strcmp(msg, "sysstop") == 0){
409 if(ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0)
410 return -1;
411 goto waitstop;
412 }
413 if(strcmp(msg, "stop") == 0){
414 if(kill(pid, SIGSTOP) < 0)
415 return -1;
416 goto waitstop;
417 }
418 if(strcmp(msg, "step") == 0){
419 if(ptrace(PTRACE_SINGLESTEP, pid, 0, 0) < 0)
420 return -1;
421 goto waitstop;
422 }
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;
433 }
434 return -1;
435 }
436 /*fprint(2, "got pid %d status %x\n", pid, status); */
437 if(WIFEXITED(status) || WIFSTOPPED(status))
438 return 0;
439 }
440 }
441 if(strcmp(msg, "start") == 0)
442 return ptrace(PTRACE_CONT, pid, 0, 0);
443 werrstr("unknown control message '%s'", msg);
444 return -1;
445 }
446
447 char*
448 proctextfile(int pid)
449 {
450 static char buf[1024], pbuf[128];
451
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;
458 }