diff options
author | rsc <devnull@localhost> | 2004-04-19 19:29:25 +0000 |
---|---|---|
committer | rsc <devnull@localhost> | 2004-04-19 19:29:25 +0000 |
commit | a84cbb2a17c9d0b88c561d5b7cb50d79a19e7c46 (patch) | |
tree | 59a0e921597e5aa53e83d487c16727a7bf01547a /src/libmach/Linux.c | |
parent | 0e3cc9f456ea49919459bf1164d0c8309a6134fa (diff) | |
download | plan9port-a84cbb2a17c9d0b88c561d5b7cb50d79a19e7c46.tar.gz plan9port-a84cbb2a17c9d0b88c561d5b7cb50d79a19e7c46.tar.bz2 plan9port-a84cbb2a17c9d0b88c561d5b7cb50d79a19e7c46.zip |
libmach
Diffstat (limited to 'src/libmach/Linux.c')
-rw-r--r-- | src/libmach/Linux.c | 450 |
1 files changed, 450 insertions, 0 deletions
diff --git a/src/libmach/Linux.c b/src/libmach/Linux.c new file mode 100644 index 00000000..62796adb --- /dev/null +++ b/src/libmach/Linux.c @@ -0,0 +1,450 @@ +/* + * 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 <u.h> +#include <sys/ptrace.h> +#include <sys/types.h> +#include <sys/wait.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(PTRACE_ATTACH, pid, 0, 0) < 0) + if(ptrace(PTRACE_PEEKUSER, pid, 0, 0) < 0) + if(ptrace(PTRACE_ATTACH, pid, 0, 0) < 0){ + werrstr("ptrace attach %d: %r", pid); + return -1; + } + + if(ctlproc(pid, "waitstop") < 0){ + ptrace(PTRACE_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(PTRACE_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(PTRACE_PEEKDATA, seg->pid, 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(PTRACE_PEEKDATA, seg->pid, addr+i, 0); + if(errno) + return -1; + *(u32int*)buf = u; + memmove(buf, (char*)v+i, n-i); + u = *(u32int*)buf; + } + if(ptrace(PTRACE_POKEDATA, seg->pid, addr+i, &u) < 0) + goto ptraceerr; + } + } + return 0; + +ptraceerr: + werrstr("ptrace: %r"); + return -1; +} + +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; i<nelem(linuxregs); i++) + if(strcmp(linuxregs[i], reg) == 0) + return 4*i; + return ~(ulong)0; +} + +static int +ptraceregrw(Regs *regs, char *name, ulong *val, int isr) +{ + int pid; + ulong addr; + u32int u; + + pid = ((PtraceRegs*)regs)->pid; + 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, &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<<SIGCONT); + if(sigs == 0){ + *pnotes = nil; + return 0; + } + + notes = mallocz(32*sizeof(char*), 0); + if(notes == nil) + return -1; + n = 0; + for(i=0; i<32; i++){ + if((sigs&(1<<i)) == 0) + continue; + if((s = _p9sigstr(i, nil)) == nil) + continue; + notes[n++] = s; + } + *pnotes = notes; + return n; +} + +#undef waitpid + +int +ctlproc(int pid, char *msg) +{ + int p, status; + + if(strcmp(msg, "hang") == 0){ + if(pid == getpid()) + return ptrace(PTRACE_TRACEME, 0, 0, 0); + werrstr("can only hang self"); + return -1; + } + if(strcmp(msg, "kill") == 0) + return ptrace(PTRACE_KILL, pid, 0, 0); + if(strcmp(msg, "startstop") == 0){ + if(ptrace(PTRACE_CONT, 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, "waitstop") == 0){ + waitstop: + if(isstopped(pid)) + return 0; + 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(PTRACE_CONT, pid, 0, 0); + werrstr("unknown control message '%s'", msg); + return -1; +} + +char* +proctextfile(int pid) +{ + static char buf[1024], pbuf[128]; + + snprint(pbuf, sizeof pbuf, "/proc/%d/exe", pid); + if(readlink(pbuf, buf, sizeof buf) >= 0) + return buf; + if(access(pbuf, AEXIST) >= 0) + return pbuf; + return nil; +} + + +#if 0 + snprint(buf, sizeof buf, "/proc/%d/maps", pid); + if((b = Bopen(buf, OREAD)) == nil){ + werrstr("open %s: %r", buf); + return -1; + } + +/* + 08048000-08056000 r-xp 00000000 03:0c 64593 /usr/sbin/gpm + 08056000-08058000 rw-p 0000d000 03:0c 64593 /usr/sbin/gpm + 08058000-0805b000 rwxp 00000000 00:00 0 + 40000000-40013000 r-xp 00000000 03:0c 4165 /lib/ld-2.2.4.so + 40013000-40015000 rw-p 00012000 03:0c 4165 /lib/ld-2.2.4.so + 4001f000-40135000 r-xp 00000000 03:0c 45494 /lib/libc-2.2.4.so + 40135000-4013e000 rw-p 00115000 03:0c 45494 /lib/libc-2.2.4.so + 4013e000-40142000 rw-p 00000000 00:00 0 + bffff000-c0000000 rwxp 00000000 00:00 0 +*/ + + file = nil; + while((p = Brdline(b, '\n')) != nil){ + p[Blinelen(b)-1] = 0; + memset(f, 0, sizeof f); + if((nf = getfields(p, f, 6, 1, " ")) < 5) + continue; + base = strtoul(f[0], &p, 16); + if(*p != '-') + continue; + end = strtoul(p+1, &p, 16); + if(*p != 0) + continue; + offset = strtoul(f[2], &p, 16); + if(*p != 0) + continue; + if(nf == 6) + file = f[5]; + zero = atoi(f[4]) == 0; + print("%lux-%lux %lux %s %s\n", base, end, offset, zero ? "data" : "text", file ? file : ""); + s.base = base; + s.size = end - base; + s.offset = offset; + s.name = zero ? "data" : "text"; + s.file = strdup(file); + s.rw = ptracerw; + s.pid = pid; + if(addseg(map, s) < 0){ + Bterm(b); + ptrace(PTRACE_DETACH, pid, 0, 0); + return -1; + } + } + Bterm(b); +#endif + |