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;
451 #if 0
452 snprint(buf, sizeof buf, "/proc/%d/maps", pid);
453 if((b = Bopen(buf, OREAD)) == nil){
454 werrstr("open %s: %r", buf);
455 return -1;
458 /*
459 08048000-08056000 r-xp 00000000 03:0c 64593 /usr/sbin/gpm
460 08056000-08058000 rw-p 0000d000 03:0c 64593 /usr/sbin/gpm
461 08058000-0805b000 rwxp 00000000 00:00 0
462 40000000-40013000 r-xp 00000000 03:0c 4165 /lib/ld-2.2.4.so
463 40013000-40015000 rw-p 00012000 03:0c 4165 /lib/ld-2.2.4.so
464 4001f000-40135000 r-xp 00000000 03:0c 45494 /lib/libc-2.2.4.so
465 40135000-4013e000 rw-p 00115000 03:0c 45494 /lib/libc-2.2.4.so
466 4013e000-40142000 rw-p 00000000 00:00 0
467 bffff000-c0000000 rwxp 00000000 00:00 0
468 */
470 file = nil;
471 while((p = Brdline(b, '\n')) != nil){
472 p[Blinelen(b)-1] = 0;
473 memset(f, 0, sizeof f);
474 if((nf = getfields(p, f, 6, 1, " ")) < 5)
475 continue;
476 base = strtoul(f[0], &p, 16);
477 if(*p != '-')
478 continue;
479 end = strtoul(p+1, &p, 16);
480 if(*p != 0)
481 continue;
482 offset = strtoul(f[2], &p, 16);
483 if(*p != 0)
484 continue;
485 if(nf == 6)
486 file = f[5];
487 zero = atoi(f[4]) == 0;
488 print("%lux-%lux %lux %s %s\n", base, end, offset, zero ? "data" : "text", file ? file : "");
489 s.base = base;
490 s.size = end - base;
491 s.offset = offset;
492 s.name = zero ? "data" : "text";
493 s.file = strdup(file);
494 s.rw = ptracerw;
495 s.pid = pid;
496 if(addseg(map, s) < 0){
497 Bterm(b);
498 ptrace(PTRACE_DETACH, pid, 0, 0);
499 return -1;
502 Bterm(b);
503 #endif
505 /*
506 * bottom-end functions for libthread_db to call
507 */
508 enum
510 PS_OK,
511 PS_ERR,
512 PS_BADPID,
513 PS_BADLWPID,
514 PS_BADADDR,
515 PS_NOSYM,
516 PS_NOFPREGS,
517 };
519 pid_t
520 ps_getpid(struct ps_prochandle *ph)
522 return ph->pid;
525 int
526 ps_pstop(const struct ps_prochandle *ph)
528 return PS_ERR;
531 int
532 ps_pcontinue(const struct ps_prochandle *ph)
534 return PS_ERR;
537 int
538 ps_lstop(const struct ps_prochandle *ph)
540 return PS_ERR;
543 int
544 ps_lcontinue(const struct ps_prochandle *ph)
546 return PS_ERR;
549 /* read/write data or text memory */
550 int
551 ps_pdread(struct ps_prochandle *ph, psaddr_t addr, void *v, size_t sz)
553 //print("read %d %p %d\n", ph->pid, addr, sz);
554 if(ptracerw(PTRACE_PEEKDATA, 0, 1, ph->pid, (ulong)addr, v, sz) < 0)
555 return PS_ERR;
556 //print(" => 0x%lux\n", *(ulong*)v);
557 return PS_OK;
560 int
561 ps_pdwrite(struct ps_prochandle *ph, psaddr_t addr, void *v, size_t sz)
563 //print("write %d %p\n", ph->pid, addr);
564 if(ptracerw(PTRACE_POKEDATA, PTRACE_PEEKDATA, 0, ph->pid, (ulong)addr, v, sz) < 0)
565 return PS_ERR;
566 return PS_OK;
569 int
570 ps_ptread(struct ps_prochandle *ph, psaddr_t addr, void *v, size_t sz)
572 //print("read %d %p\n", ph->pid, addr);
573 if(ptracerw(PTRACE_PEEKTEXT, 0, 1, ph->pid, (ulong)addr, v, sz) < 0)
574 return PS_ERR;
575 return PS_OK;
578 int
579 ps_ptwrite(struct ps_prochandle *ph, psaddr_t addr, void *v, size_t sz)
581 //print("write %d %p\n", ph->pid, addr);
582 if(ptracerw(PTRACE_POKETEXT, PTRACE_PEEKTEXT, 0, ph->pid, (ulong)addr, v, sz) < 0)
583 return PS_ERR;
584 return PS_OK;
587 int
588 ps_lgetregs(struct ps_prochandle *ph, lwpid_t lwp, prgregset_t regs)
590 if(lwp == 0){
591 memset(regs, 0xfe, sizeof(regs[0])*nelem(linuxregs));
592 return PS_OK;
594 //print("getregs %d %p (%d)\n", lwp, regs, sizeof(regs[0])*nelem(linuxregs));
596 if(ptraceattach(lwp) < 0){
597 fprint(2, "ptrace attach: %r\n");
598 return PS_ERR;
601 if(ptracerw(PTRACE_PEEKUSER, 0, 1, lwp, 0, regs, sizeof(regs[0])*nelem(linuxregs)) < 0){
602 fprint(2, "ptrace: %r\n");
603 return PS_ERR;
605 return PS_OK;
608 int
609 ps_lsetregs(struct ps_prochandle *ph, lwpid_t lwp, prgregset_t regs)
611 print("setregs %d\n", lwp);
612 if(ptracerw(PTRACE_POKEUSER, PTRACE_PEEKUSER, 1, lwp, 0, regs, sizeof(regs[0])*nelem(linuxregs)) < 0)
613 return PS_ERR;
614 return PS_OK;
617 int
618 ps_lgetfpregs(struct ps_prochandle *ph, lwpid_t lwp, prfpregset_t *fpregs)
620 if(ptracerw(PTRACE_PEEKUSER, 0, 1, lwp, 18*4, fpregs, sizeof *fpregs) < 0)
621 return PS_ERR;
622 return PS_OK;
625 int
626 ps_lsetfpregs(struct ps_prochandle *ph, lwpid_t lwp, prfpregset_t *fpregs)
628 if(ptracerw(PTRACE_POKEUSER, PTRACE_PEEKUSER, 1, lwp, 18*4, fpregs, sizeof *fpregs) < 0)
629 return PS_ERR;
630 return PS_OK;
633 /* Fetch the special per-thread address associated with the given LWP.
634 This call is only used on a few platforms (most use a normal register).
635 The meaning of the `int' parameter is machine-dependent. */
636 int
637 ps_get_thread_area(struct ps_prochandle *ph, lwpid_t lwp, int xxx, psaddr_t *addr)
639 return PS_NOSYM;
642 /* Look up the named symbol in the named DSO in the symbol tables
643 associated with the process being debugged, filling in *SYM_ADDR
644 with the corresponding run-time address. */
645 int
646 ps_pglobal_lookup(struct ps_prochandle *ph, char *object_name, char *sym_name, psaddr_t *sym_addr)
648 Fhdr *fp;
649 ulong addr;
651 if((fp = findhdr(object_name)) == nil){
652 print("lookup %d %s %s => no such hdr\n", ph->pid, object_name, sym_name);
653 return PS_NOSYM;
655 if(elfsymlookup(fp->elf, sym_name, &addr) < 0){
656 print("lookup %d %s %s => name not found\n", ph->pid, object_name, sym_name);
657 return PS_NOSYM;
659 print("lookup %d %s %s => 0x%lux\n", ph->pid, object_name, sym_name, addr);
660 *sym_addr = (void*)(addr+fp->base);
661 return PS_OK;
664 Ureg*
665 _linux2ureg386(UregLinux386 *l)
667 Ureg *u;
669 u = malloc(sizeof(Ureg));
670 if(u == nil)
671 return nil;
672 u->di = l->edi;
673 u->si = l->esi;
674 u->bp = l->ebp;
675 u->nsp = l->esp;
676 u->bx = l->ebx;
677 u->dx = l->edx;
678 u->cx = l->ecx;
679 u->ax = l->eax;
680 u->gs = l->xgs;
681 u->fs = l->xfs;
682 u->es = l->xes;
683 u->ds = l->xds;
684 u->trap = ~0; // l->trapno;
685 u->ecode = ~0; // l->err;
686 u->pc = l->eip;
687 u->cs = l->xcs;
688 u->flags = l->eflags;
689 u->sp = l->esp;
690 u->ss = l->xss;
691 return u;