|  | /* | 
|  | * process interface for DragonFly BSD | 
|  | * | 
|  | * 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; | 
|  | } |