/* * 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 #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 ptracesegrw(Map*, Seg*, ulong, void*, uint, int); static int ptraceregrw(Regs*, char*, ulong*, int); static int attachedpids[1000]; static int nattached; static int ptraceattach(int pid) { int i; /* if(nattached==1 && attachedpids[0] == pid) goto already; if(nattached) detachproc(attachedpids[0]); */ 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(ptraceattach(pid) < 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 = ptracesegrw; s.pid = pid; if(addseg(map, s) < 0){ fprint(2, "addseg: %r\n"); return -1; } if((r = mallocz(sizeof(PtraceRegs), 1)) == nil){ fprint(2, "mallocz: %r\n"); return -1; } r->r.rw = ptraceregrw; r->pid = pid; *rp = (Regs*)r; return 0; } int detachproc(int pid) { int i; for(i=0; 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(xtype, pid, addr+i, 0); if(errno) return -1; *(u32int*)buf = u; memmove(buf, (char*)v+i, n-i); u = *(u32int*)buf; } if(ptrace(type, pid, addr+i, &u) < 0) goto ptraceerr; } } return 0; ptraceerr: werrstr("ptrace: %r"); return -1; } static int ptracesegrw(Map *map, Seg *seg, ulong addr, void *v, uint n, int isr) { addr += seg->base; return ptracerw(isr ? PTRACE_PEEKDATA : PTRACE_POKEDATA, PTRACE_PEEKDATA, isr, seg->pid, addr, v, n); } 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; ipid; 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<= 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 /* * bottom-end functions for libthread_db to call */ enum { PS_OK, PS_ERR, PS_BADPID, PS_BADLWPID, PS_BADADDR, PS_NOSYM, PS_NOFPREGS, }; pid_t ps_getpid(struct ps_prochandle *ph) { return ph->pid; } int ps_pstop(const struct ps_prochandle *ph) { return PS_ERR; } int ps_pcontinue(const struct ps_prochandle *ph) { return PS_ERR; } int ps_lstop(const struct ps_prochandle *ph) { return PS_ERR; } int ps_lcontinue(const struct ps_prochandle *ph) { return PS_ERR; } /* read/write data or text memory */ int ps_pdread(struct ps_prochandle *ph, psaddr_t addr, void *v, size_t sz) { //print("read %d %p %d\n", ph->pid, addr, sz); if(ptracerw(PTRACE_PEEKDATA, 0, 1, ph->pid, (ulong)addr, v, sz) < 0) return PS_ERR; //print(" => 0x%lux\n", *(ulong*)v); return PS_OK; } int ps_pdwrite(struct ps_prochandle *ph, psaddr_t addr, void *v, size_t sz) { //print("write %d %p\n", ph->pid, addr); if(ptracerw(PTRACE_POKEDATA, PTRACE_PEEKDATA, 0, ph->pid, (ulong)addr, v, sz) < 0) return PS_ERR; return PS_OK; } int ps_ptread(struct ps_prochandle *ph, psaddr_t addr, void *v, size_t sz) { //print("read %d %p\n", ph->pid, addr); if(ptracerw(PTRACE_PEEKTEXT, 0, 1, ph->pid, (ulong)addr, v, sz) < 0) return PS_ERR; return PS_OK; } int ps_ptwrite(struct ps_prochandle *ph, psaddr_t addr, void *v, size_t sz) { //print("write %d %p\n", ph->pid, addr); if(ptracerw(PTRACE_POKETEXT, PTRACE_PEEKTEXT, 0, ph->pid, (ulong)addr, v, sz) < 0) return PS_ERR; return PS_OK; } int ps_lgetregs(struct ps_prochandle *ph, lwpid_t lwp, prgregset_t regs) { if(lwp == 0){ memset(regs, 0xfe, sizeof(regs[0])*nelem(linuxregs)); return PS_OK; } //print("getregs %d %p (%d)\n", lwp, regs, sizeof(regs[0])*nelem(linuxregs)); if(ptraceattach(lwp) < 0){ fprint(2, "ptrace attach: %r\n"); return PS_ERR; } if(ptracerw(PTRACE_PEEKUSER, 0, 1, lwp, 0, regs, sizeof(regs[0])*nelem(linuxregs)) < 0){ fprint(2, "ptrace: %r\n"); return PS_ERR; } return PS_OK; } int ps_lsetregs(struct ps_prochandle *ph, lwpid_t lwp, prgregset_t regs) { print("setregs %d\n", lwp); if(ptracerw(PTRACE_POKEUSER, PTRACE_PEEKUSER, 1, lwp, 0, regs, sizeof(regs[0])*nelem(linuxregs)) < 0) return PS_ERR; return PS_OK; } int ps_lgetfpregs(struct ps_prochandle *ph, lwpid_t lwp, prfpregset_t *fpregs) { if(ptracerw(PTRACE_PEEKUSER, 0, 1, lwp, 18*4, fpregs, sizeof *fpregs) < 0) return PS_ERR; return PS_OK; } int ps_lsetfpregs(struct ps_prochandle *ph, lwpid_t lwp, prfpregset_t *fpregs) { if(ptracerw(PTRACE_POKEUSER, PTRACE_PEEKUSER, 1, lwp, 18*4, fpregs, sizeof *fpregs) < 0) return PS_ERR; return PS_OK; } /* Fetch the special per-thread address associated with the given LWP. This call is only used on a few platforms (most use a normal register). The meaning of the `int' parameter is machine-dependent. */ int ps_get_thread_area(struct ps_prochandle *ph, lwpid_t lwp, int xxx, psaddr_t *addr) { return PS_NOSYM; } /* Look up the named symbol in the named DSO in the symbol tables associated with the process being debugged, filling in *SYM_ADDR with the corresponding run-time address. */ int ps_pglobal_lookup(struct ps_prochandle *ph, char *object_name, char *sym_name, psaddr_t *sym_addr) { Fhdr *fp; ulong addr; if((fp = findhdr(object_name)) == nil){ print("lookup %d %s %s => no such hdr\n", ph->pid, object_name, sym_name); return PS_NOSYM; } if(elfsymlookup(fp->elf, sym_name, &addr) < 0){ print("lookup %d %s %s => name not found\n", ph->pid, object_name, sym_name); return PS_NOSYM; } print("lookup %d %s %s => 0x%lux\n", ph->pid, object_name, sym_name, addr); *sym_addr = (void*)(addr+fp->base); return PS_OK; } Ureg* _linux2ureg386(UregLinux386 *l) { Ureg *u; u = malloc(sizeof(Ureg)); if(u == nil) return nil; u->di = l->edi; u->si = l->esi; u->bp = l->ebp; u->nsp = l->esp; u->bx = l->ebx; u->dx = l->edx; u->cx = l->ecx; u->ax = l->eax; u->gs = l->xgs; u->fs = l->xfs; u->es = l->xes; u->ds = l->xds; u->trap = ~0; // l->trapno; u->ecode = ~0; // l->err; u->pc = l->eip; u->cs = l->xcs; u->flags = l->eflags; u->sp = l->esp; u->ss = l->xss; return u; }