/* * process interface for Linux. * * Uses ptrace for registers and data, * /proc for some process status. * There's not much point to worrying about * byte order here -- using ptrace means * we're running on the architecture we're debugging, * unless truly weird stuff is going on. * * It is tempting to use /proc/%d/mem along with * the sp and pc in the stat file to get a stack trace * without attaching to the program, but unfortunately * you can't read the mem file unless you've attached. */ #include #include #include #include #include #include #include #include #include #include #include "ureg386.h" Mach *machcpu = &mach386; typedef struct PtraceRegs PtraceRegs; struct PtraceRegs { Regs r; int pid; }; static int ptracesegrw(Map*, Seg*, u64int, void*, uint, int); static int ptraceregrw(Regs*, char*, u64int*, int); static int attachedpids[1000]; static int nattached; static int ptraceattach(int pid) { int i; for(i=0; inseg; i++) while(inseg && map->seg[i].pid){ map->nseg--; memmove(&map->seg[i], &map->seg[i+1], (map->nseg-i)*sizeof(map->seg[0])); } } int mapproc(int pid, Map *map, Regs **rp) { Seg s; PtraceRegs *r; if(ptraceattach(pid) < 0) return -1; memset(&s, 0, sizeof s); s.base = 0; s.size = 0xFFFFFFFF; s.offset = 0; s.name = "data"; s.file = nil; s.rw = ptracesegrw; s.pid = pid; if(addseg(map, s) < 0){ fprint(2, "addseg: %r\n"); return -1; } if((r = mallocz(sizeof(PtraceRegs), 1)) == nil){ fprint(2, "mallocz: %r\n"); return -1; } r->r.rw = ptraceregrw; r->pid = pid; *rp = (Regs*)r; return 0; } int detachproc(int pid) { int i; for(i=0; i= 4) *(u32int*)((char*)v+i) = u; else{ memmove(buf, &u, 4); memmove((char*)v+i, buf, n-i); } }else{ if(n-i >= 4) u = *(u32int*)((char*)v+i); else{ errno = 0; u = ptrace(xtype, pid, addr+i, 0); if(errno) return -1; memmove(buf, &u, 4); memmove(buf, (char*)v+i, n-i); memmove(&u, buf, 4); } if(ptrace(type, pid, addr+i, u) < 0) goto ptraceerr; } } return 0; ptraceerr: werrstr("ptrace: %r"); return -1; } static int ptracesegrw(Map *map, Seg *seg, u64int addr, void *v, uint n, int isr) { addr += seg->base; return ptracerw(isr ? PTRACE_PEEKDATA : PTRACE_POKEDATA, PTRACE_PEEKDATA, isr, seg->pid, addr, v, n); } static char* linuxregs[] = { "BX", "CX", "DX", "SI", "DI", "BP", "AX", "DS", "ES", "FS", "GS", "OAX", "PC", "CS", "EFLAGS", "SP", "SS", }; static ulong reg2linux(char *reg) { int i; for(i=0; ipid; addr = reg2linux(name); if(~addr == 0){ if(isr){ *val = ~(ulong)0; return 0; } werrstr("register not available"); return -1; } if(isr){ errno = 0; u = ptrace(PTRACE_PEEKUSER, pid, addr, 0); if(errno) goto ptraceerr; *val = u; }else{ u = *val; if(ptrace(PTRACE_POKEUSER, pid, addr, (void*)(uintptr)u) < 0) goto ptraceerr; } return 0; ptraceerr: werrstr("ptrace: %r"); return -1; } static int isstopped(int pid) { char buf[1024]; int fd, n; char *p; snprint(buf, sizeof buf, "/proc/%d/stat", pid); if((fd = open(buf, OREAD)) < 0) return 0; n = read(fd, buf, sizeof buf-1); close(fd); if(n <= 0) return 0; buf[n] = 0; /* command name is in parens, no parens afterward */ p = strrchr(buf, ')'); if(p == nil || *++p != ' ') return 0; ++p; /* next is state - T is stopped for tracing */ return *p == 'T'; } /* /proc/pid/stat contains pid command in parens 0. state 1. ppid 2. pgrp 3. session 4. tty_nr 5. tpgid 6. flags (math=4, traced=10) 7. minflt 8. cminflt 9. majflt 10. cmajflt 11. utime 12. stime 13. cutime 14. cstime 15. priority 16. nice 17. 0 18. itrealvalue 19. starttime 20. vsize 21. rss 22. rlim 23. startcode 24. endcode 25. startstack 26. kstkesp 27. kstkeip 28. pending signal bitmap 29. blocked signal bitmap 30. ignored signal bitmap 31. caught signal bitmap 32. wchan 33. nswap 34. cnswap 35. exit_signal 36. processor */ int procnotes(int pid, char ***pnotes) { char buf[1024], *f[40]; int fd, i, n, nf; char *p, *s, **notes; ulong sigs; extern char *_p9sigstr(int, char*); *pnotes = nil; snprint(buf, sizeof buf, "/proc/%d/stat", pid); if((fd = open(buf, OREAD)) < 0){ fprint(2, "open %s: %r\n", buf); return -1; } n = read(fd, buf, sizeof buf-1); close(fd); if(n <= 0){ fprint(2, "read %s: %r\n", buf); return -1; } buf[n] = 0; /* command name is in parens, no parens afterward */ p = strrchr(buf, ')'); if(p == nil || *++p != ' '){ fprint(2, "bad format in /proc/%d/stat\n", pid); return -1; } ++p; nf = tokenize(p, f, nelem(f)); if(0) print("code 0x%lux-0x%lux stack 0x%lux kstk 0x%lux keip 0x%lux pending 0x%lux\n", strtoul(f[23], 0, 0), strtoul(f[24], 0, 0), strtoul(f[25], 0, 0), strtoul(f[26], 0, 0), strtoul(f[27], 0, 0), strtoul(f[28], 0, 0)); if(nf <= 28) return -1; sigs = strtoul(f[28], 0, 0) & ~(1<= 0) return buf; if(access(pbuf, AEXIST) >= 0) return pbuf; return nil; }