aboutsummaryrefslogtreecommitdiff
path: root/src/libmach/Linux.c
diff options
context:
space:
mode:
authorrsc <devnull@localhost>2004-04-19 19:29:25 +0000
committerrsc <devnull@localhost>2004-04-19 19:29:25 +0000
commita84cbb2a17c9d0b88c561d5b7cb50d79a19e7c46 (patch)
tree59a0e921597e5aa53e83d487c16727a7bf01547a /src/libmach/Linux.c
parent0e3cc9f456ea49919459bf1164d0c8309a6134fa (diff)
downloadplan9port-a84cbb2a17c9d0b88c561d5b7cb50d79a19e7c46.tar.gz
plan9port-a84cbb2a17c9d0b88c561d5b7cb50d79a19e7c46.tar.bz2
plan9port-a84cbb2a17c9d0b88c561d5b7cb50d79a19e7c46.zip
libmach
Diffstat (limited to 'src/libmach/Linux.c')
-rw-r--r--src/libmach/Linux.c450
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
+