| /* |
| * 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 <u.h> |
| #include <sys/ptrace.h> |
| #include <sys/types.h> |
| #include <sys/wait.h> |
| #include <machine/reg.h> |
| #include <signal.h> |
| #include <errno.h> |
| #include <libc.h> |
| #include <mach.h> |
| #include "ureg386.h" |
| |
| Mach *machcpu = &mach386; |
| |
| typedef struct PtraceRegs PtraceRegs; |
| struct PtraceRegs |
| { |
| Regs r; |
| int pid; |
| }; |
| |
| static int ptracerw(Map*, Seg*, ulong, void*, uint, int); |
| static int ptraceregrw(Regs*, char*, ulong*, int); |
| |
| void |
| unmapproc(Map *map) |
| { |
| int i; |
| |
| if(map == nil) |
| return; |
| for(i=0; i<map->nseg; i++) |
| while(i<map->nseg && 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, ulong addr, void *v, uint n, int isr) |
| { |
| int i; |
| u32int u; |
| uchar buf[4]; |
| |
| addr += seg->base; |
| for(i=0; i<n; i+=4){ |
| if(isr){ |
| errno = 0; |
| u = ptrace(PT_READ_D, seg->pid, (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; i<nelem(freebsdregs); i++) |
| if(strcmp(freebsdregs[i], reg) == 0) |
| return 4*i; |
| return ~(ulong)0; |
| } |
| |
| static int |
| ptraceregrw(Regs *regs, char *name, ulong *val, int isr) |
| { |
| int pid; |
| ulong addr; |
| struct reg mregs; |
| |
| addr = reg2freebsd(name); |
| if(~addr == 0){ |
| if(isr){ |
| *val = ~(ulong)0; |
| return 0; |
| } |
| werrstr("register not available"); |
| return -1; |
| } |
| |
| pid = ((PtraceRegs*)regs)->pid; |
| 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; |
| } |