/* * process interface for FreeBSD * * we could be a little more careful about not using * ptrace unless absolutely necessary. this would let us * look at processes without stopping them. * * I'd like to make this a bit more generic (there's too much * duplication with Linux and presumably other systems), * but ptrace is too damn system-specific. */ #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 ptracerw(Map*, Seg*, u64int, void*, uint, int); static int ptraceregrw(Regs*, char*, u64int*, int); void unmapproc(Map *map) { int i; if(map == nil) return; 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(ptrace(PT_ATTACH, pid, 0, 0) < 0) if(ptrace(PT_READ_I, pid, 0, 0)<0 && errno!=EINVAL) if(ptrace(PT_ATTACH, pid, 0, 0) < 0){ werrstr("ptrace attach %d: %r", pid); return -1; } if(ctlproc(pid, "waitanyway") < 0){ ptrace(PT_DETACH, pid, 0, 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 = ptracerw; s.pid = pid; if(addseg(map, s) < 0) return -1; if((r = mallocz(sizeof(PtraceRegs), 1)) == nil) return -1; r->r.rw = ptraceregrw; r->pid = pid; *rp = (Regs*)r; return 0; } int detachproc(int pid) { return ptrace(PT_DETACH, pid, 0, 0); } static int ptracerw(Map *map, Seg *seg, u64int addr, void *v, uint n, int isr) { int i; u32int u; uchar buf[4]; addr += seg->base; for(i=0; ipid, (char*)addr+i, 0); if(errno) goto ptraceerr; if(n-i >= 4) *(u32int*)((char*)v+i) = u; else{ *(u32int*)buf = u; memmove((char*)v+i, buf, n-i); } }else{ if(n-i >= 4) u = *(u32int*)((char*)v+i); else{ errno = 0; u = ptrace(PT_READ_D, seg->pid, (char*)addr+i, 0); if(errno) return -1; *(u32int*)buf = u; memmove(buf, (char*)v+i, n-i); u = *(u32int*)buf; } if(ptrace(PT_WRITE_D, seg->pid, (char*)addr+i, u) < 0) goto ptraceerr; } } return 0; ptraceerr: werrstr("ptrace: %r"); return -1; } static char *freebsdregs[] = { "FS", "ES", "DS", "DI", "SI", "BP", "SP", "BX", "DX", "CX", "AX", "TRAP", "PC", "CS", "EFLAGS", "SP", "SS", "GS", }; static ulong reg2freebsd(char *reg) { int i; for(i=0; ipid; if(ptrace(PT_GETREGS, pid, (char*)&mregs, 0) < 0) return -1; if(isr) *val = *(u32int*)((char*)&mregs+addr); else{ *(u32int*)((char*)&mregs+addr) = *val; if(ptrace(PT_SETREGS, pid, (char*)&mregs, 0) < 0) return -1; } return 0; } char* proctextfile(int pid) { static char buf[1024], pbuf[128]; snprint(pbuf, sizeof pbuf, "/proc/%d/file", pid); if(readlink(pbuf, buf, sizeof buf) >= 0) return buf; if(access(pbuf, AEXIST) >= 0) return pbuf; return nil; } /* status The process status. This file is read-only and returns a single line containing multiple space-separated fields as follows: o command name o process id o parent process id o process group id o session id o major,minor of the controlling terminal, or -1,-1 if there is no controlling terminal. o a list of process flags: ctty if there is a controlling ter- minal, sldr if the process is a session leader, noflags if neither of the other two flags are set. o the process start time in seconds and microseconds, comma separated. o the user time in seconds and microseconds, comma separated. o the system time in seconds and microseconds, comma separated. o the wait channel message o the process credentials consisting of the effective user id and the list of groups (whose first member is the effective group id) all comma separated. */ int procnotes(int pid, char ***pnotes) { /* figure out the set of pending notes - how? */ *pnotes = nil; return 0; } static int isstopped(int pid) { char buf[1024], *f[12]; int fd, n, nf; snprint(buf, sizeof buf, "/proc/%d/status", 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; if((nf = tokenize(buf, f, nelem(f))) < 11) return 0; if(strcmp(f[10], "nochan") == 0) return 1; return 0; } #undef waitpid int ctlproc(int pid, char *msg) { int p, status; if(strcmp(msg, "hang") == 0){ if(pid == getpid()) return ptrace(PT_TRACE_ME, 0, 0, 0); werrstr("can only hang self"); return -1; } if(strcmp(msg, "kill") == 0) return ptrace(PT_KILL, pid, 0, 0); if(strcmp(msg, "startstop") == 0){ if(ptrace(PT_CONTINUE, pid, 0, 0) < 0) return -1; goto waitstop; } /* if(strcmp(msg, "sysstop") == 0){ if(ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0) return -1; goto waitstop; } */ if(strcmp(msg, "stop") == 0){ if(kill(pid, SIGSTOP) < 0) return -1; goto waitstop; } if(strcmp(msg, "waitanyway") == 0) goto waitanyway; if(strcmp(msg, "waitstop") == 0){ waitstop: if(isstopped(pid)) return 0; waitanyway: for(;;){ p = waitpid(pid, &status, WUNTRACED); if(p <= 0) return -1; if(WIFEXITED(status) || WIFSTOPPED(status)) return 0; } } if(strcmp(msg, "start") == 0) return ptrace(PT_CONTINUE, pid, 0, 0); werrstr("unknown control message '%s'", msg); return -1; }