URI:
       tLinux.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       tLinux.c (8207B)
       ---
            1 /*
            2  * process interface for Linux.
            3  *
            4  * Uses ptrace for registers and data,
            5  * /proc for some process status.
            6  * There's not much point to worrying about
            7  * byte order here -- using ptrace means
            8  * we're running on the architecture we're debugging,
            9  * unless truly weird stuff is going on.
           10  *
           11  * It is tempting to use /proc/%d/mem along with
           12  * the sp and pc in the stat file to get a stack trace
           13  * without attaching to the program, but unfortunately
           14  * you can't read the mem file unless you've attached.
           15  */
           16 
           17 #include <u.h>
           18 #include <sys/ptrace.h>
           19 #include <sys/types.h>
           20 #include <sys/wait.h>
           21 #include <sys/procfs.h>
           22 #include <signal.h>
           23 #include <errno.h>
           24 #include <libc.h>
           25 #include <mach.h>
           26 #include <elf.h>
           27 #include "ureg386.h"
           28 
           29 Mach *machcpu = &mach386;
           30 
           31 typedef struct PtraceRegs PtraceRegs;
           32 
           33 struct PtraceRegs
           34 {
           35         Regs r;
           36         int pid;
           37 };
           38 
           39 static int ptracesegrw(Map*, Seg*, u64int, void*, uint, int);
           40 static int ptraceregrw(Regs*, char*, u64int*, int);
           41 
           42 static int attachedpids[1000];
           43 static int nattached;
           44 
           45 static int
           46 ptraceattach(int pid)
           47 {
           48         int i;
           49 
           50         for(i=0; i<nattached; i++)
           51                 if(attachedpids[i]==pid)
           52                         return 0;
           53         if(nattached == nelem(attachedpids)){
           54                 werrstr("attached to too many processes");
           55                 return -1;
           56         }
           57 
           58         if(ptrace(PTRACE_ATTACH, pid, 0, 0) < 0){
           59                 werrstr("ptrace attach %d: %r", pid);
           60                 return -1;
           61         }
           62 
           63         if(ctlproc(pid, "waitstop") < 0){
           64                 fprint(2, "waitstop: %r");
           65                 ptrace(PTRACE_DETACH, pid, 0, 0);
           66                 return -1;
           67         }
           68         attachedpids[nattached++] = pid;
           69         return 0;
           70 }
           71 
           72 void
           73 unmapproc(Map *map)
           74 {
           75         int i;
           76 
           77         if(map == nil)
           78                 return;
           79         for(i=0; i<map->nseg; i++)
           80                 while(i<map->nseg && map->seg[i].pid){
           81                         map->nseg--;
           82                         memmove(&map->seg[i], &map->seg[i+1],
           83                                 (map->nseg-i)*sizeof(map->seg[0]));
           84                 }
           85 }
           86 
           87 int
           88 mapproc(int pid, Map *map, Regs **rp)
           89 {
           90         Seg s;
           91         PtraceRegs *r;
           92 
           93         if(ptraceattach(pid) < 0)
           94                 return -1;
           95 
           96         memset(&s, 0, sizeof s);
           97         s.base = 0;
           98         s.size = 0xFFFFFFFF;
           99         s.offset = 0;
          100         s.name = "data";
          101         s.file = nil;
          102         s.rw = ptracesegrw;
          103         s.pid = pid;
          104         if(addseg(map, s) < 0){
          105                 fprint(2, "addseg: %r\n");
          106                 return -1;
          107         }
          108 
          109         if((r = mallocz(sizeof(PtraceRegs), 1)) == nil){
          110                 fprint(2, "mallocz: %r\n");
          111                 return -1;
          112         }
          113         r->r.rw = ptraceregrw;
          114         r->pid = pid;
          115         *rp = (Regs*)r;
          116         return 0;
          117 }
          118 
          119 int
          120 detachproc(int pid)
          121 {
          122         int i;
          123 
          124         for(i=0; i<nattached; i++){
          125                 if(attachedpids[i] == pid){
          126                         attachedpids[i] = attachedpids[--nattached];
          127                         break;
          128                 }
          129         }
          130         return ptrace(PTRACE_DETACH, pid, 0, 0);
          131 }
          132 
          133 static int
          134 ptracerw(int type, int xtype, int isr, int pid, ulong addr, void *v, uint n)
          135 {
          136         int i;
          137         u32int u;
          138         uchar buf[4];
          139 
          140         for(i=0; i<n; i+=4){
          141                 if(isr){
          142                         errno = 0;
          143                         u = ptrace(type, pid, addr+i, 0);
          144                         if(errno)
          145                                 goto ptraceerr;
          146                         if(n-i >= 4)
          147                                 *(u32int*)((char*)v+i) = u;
          148                         else{
          149                                 memmove(buf, &u, 4);
          150                                 memmove((char*)v+i, buf, n-i);
          151                         }
          152                 }else{
          153                         if(n-i >= 4)
          154                                 u = *(u32int*)((char*)v+i);
          155                         else{
          156                                 errno = 0;
          157                                 u = ptrace(xtype, pid, addr+i, 0);
          158                                 if(errno)
          159                                         return -1;
          160                                 memmove(buf, &u, 4);
          161                                 memmove(buf, (char*)v+i, n-i);
          162                                 memmove(&u, buf, 4);
          163                         }
          164                         if(ptrace(type, pid, addr+i, u) < 0)
          165                                 goto ptraceerr;
          166                 }
          167         }
          168         return 0;
          169 
          170 ptraceerr:
          171         werrstr("ptrace: %r");
          172         return -1;
          173 }
          174 
          175 static int
          176 ptracesegrw(Map *map, Seg *seg, u64int addr, void *v, uint n, int isr)
          177 {
          178         addr += seg->base;
          179         return ptracerw(isr ? PTRACE_PEEKDATA : PTRACE_POKEDATA, PTRACE_PEEKDATA,
          180                 isr, seg->pid, addr, v, n);
          181 }
          182 
          183 static char* linuxregs[] = {
          184         "BX",
          185         "CX",
          186         "DX",
          187         "SI",
          188         "DI",
          189         "BP",
          190         "AX",
          191         "DS",
          192         "ES",
          193         "FS",
          194         "GS",
          195         "OAX",
          196         "PC",
          197         "CS",
          198         "EFLAGS",
          199         "SP",
          200         "SS",
          201 };
          202 
          203 static ulong
          204 reg2linux(char *reg)
          205 {
          206         int i;
          207 
          208         for(i=0; i<nelem(linuxregs); i++)
          209                 if(strcmp(linuxregs[i], reg) == 0)
          210                         return 4*i;
          211         return ~(ulong)0;
          212 }
          213 
          214 static int
          215 ptraceregrw(Regs *regs, char *name, u64int *val, int isr)
          216 {
          217         int pid;
          218         ulong addr;
          219         u32int u;
          220 
          221         pid = ((PtraceRegs*)regs)->pid;
          222         addr = reg2linux(name);
          223         if(~addr == 0){
          224                 if(isr){
          225                         *val = ~(ulong)0;
          226                         return 0;
          227                 }
          228                 werrstr("register not available");
          229                 return -1;
          230         }
          231         if(isr){
          232                 errno = 0;
          233                 u = ptrace(PTRACE_PEEKUSER, pid, addr, 0);
          234                 if(errno)
          235                         goto ptraceerr;
          236                 *val = u;
          237         }else{
          238                 u = *val;
          239                 if(ptrace(PTRACE_POKEUSER, pid, addr, (void*)(uintptr)u) < 0)
          240                         goto ptraceerr;
          241         }
          242         return 0;
          243 
          244 ptraceerr:
          245         werrstr("ptrace: %r");
          246         return -1;
          247 }
          248 
          249 static int
          250 isstopped(int pid)
          251 {
          252         char buf[1024];
          253         int fd, n;
          254         char *p;
          255 
          256         snprint(buf, sizeof buf, "/proc/%d/stat", pid);
          257         if((fd = open(buf, OREAD)) < 0)
          258                 return 0;
          259         n = read(fd, buf, sizeof buf-1);
          260         close(fd);
          261         if(n <= 0)
          262                 return 0;
          263         buf[n] = 0;
          264 
          265         /* command name is in parens, no parens afterward */
          266         p = strrchr(buf, ')');
          267         if(p == nil || *++p != ' ')
          268                 return 0;
          269         ++p;
          270 
          271         /* next is state - T is stopped for tracing */
          272         return *p == 'T';
          273 }
          274 
          275 /* /proc/pid/stat contains
          276         pid
          277         command in parens
          278         0. state
          279         1. ppid
          280         2. pgrp
          281         3. session
          282         4. tty_nr
          283         5. tpgid
          284         6. flags (math=4, traced=10)
          285         7. minflt
          286         8. cminflt
          287         9. majflt
          288         10. cmajflt
          289         11. utime
          290         12. stime
          291         13. cutime
          292         14. cstime
          293         15. priority
          294         16. nice
          295         17. 0
          296         18. itrealvalue
          297         19. starttime
          298         20. vsize
          299         21. rss
          300         22. rlim
          301         23. startcode
          302         24. endcode
          303         25. startstack
          304         26. kstkesp
          305         27. kstkeip
          306         28. pending signal bitmap
          307         29. blocked signal bitmap
          308         30. ignored signal bitmap
          309         31. caught signal bitmap
          310         32. wchan
          311         33. nswap
          312         34. cnswap
          313         35. exit_signal
          314         36. processor
          315 */
          316 
          317 int
          318 procnotes(int pid, char ***pnotes)
          319 {
          320         char buf[1024], *f[40];
          321         int fd, i, n, nf;
          322         char *p, *s, **notes;
          323         ulong sigs;
          324         extern char *_p9sigstr(int, char*);
          325 
          326         *pnotes = nil;
          327         snprint(buf, sizeof buf, "/proc/%d/stat", pid);
          328         if((fd = open(buf, OREAD)) < 0){
          329                 fprint(2, "open %s: %r\n", buf);
          330                 return -1;
          331         }
          332         n = read(fd, buf, sizeof buf-1);
          333         close(fd);
          334         if(n <= 0){
          335                 fprint(2, "read %s: %r\n", buf);
          336                 return -1;
          337         }
          338         buf[n] = 0;
          339 
          340         /* command name is in parens, no parens afterward */
          341         p = strrchr(buf, ')');
          342         if(p == nil || *++p != ' '){
          343                 fprint(2, "bad format in /proc/%d/stat\n", pid);
          344                 return -1;
          345         }
          346         ++p;
          347 
          348         nf = tokenize(p, f, nelem(f));
          349         if(0) print("code 0x%lux-0x%lux stack 0x%lux kstk 0x%lux keip 0x%lux pending 0x%lux\n",
          350                 strtoul(f[23], 0, 0), strtoul(f[24], 0, 0), strtoul(f[25], 0, 0),
          351                 strtoul(f[26], 0, 0), strtoul(f[27], 0, 0), strtoul(f[28], 0, 0));
          352         if(nf <= 28)
          353                 return -1;
          354 
          355         sigs = strtoul(f[28], 0, 0) & ~(1<<SIGCONT);
          356         if(sigs == 0){
          357                 *pnotes = nil;
          358                 return 0;
          359         }
          360 
          361         notes = mallocz(32*sizeof(char*), 0);
          362         if(notes == nil)
          363                 return -1;
          364         n = 0;
          365         for(i=0; i<32; i++){
          366                 if((sigs&(1<<i)) == 0)
          367                         continue;
          368                 if((s = _p9sigstr(i, nil)) == nil)
          369                         continue;
          370                 notes[n++] = s;
          371         }
          372         *pnotes = notes;
          373         return n;
          374 }
          375 
          376 #undef waitpid
          377 
          378 int
          379 ctlproc(int pid, char *msg)
          380 {
          381         int i, p, status;
          382 
          383         if(strcmp(msg, "attached") == 0){
          384                 for(i=0; i<nattached; i++)
          385                         if(attachedpids[i]==pid)
          386                                 return 0;
          387                 if(nattached == nelem(attachedpids)){
          388                         werrstr("attached to too many processes");
          389                         return -1;
          390                 }
          391                 attachedpids[nattached++] = pid;
          392                 return 0;
          393         }
          394 
          395         if(strcmp(msg, "hang") == 0){
          396                 if(pid == getpid())
          397                         return ptrace(PTRACE_TRACEME, 0, 0, 0);
          398                 werrstr("can only hang self");
          399                 return -1;
          400         }
          401         if(strcmp(msg, "kill") == 0)
          402                 return ptrace(PTRACE_KILL, pid, 0, 0);
          403         if(strcmp(msg, "startstop") == 0){
          404                 if(ptrace(PTRACE_CONT, pid, 0, 0) < 0)
          405                         return -1;
          406                 goto waitstop;
          407         }
          408         if(strcmp(msg, "sysstop") == 0){
          409                 if(ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0)
          410                         return -1;
          411                 goto waitstop;
          412         }
          413         if(strcmp(msg, "stop") == 0){
          414                 if(kill(pid, SIGSTOP) < 0)
          415                         return -1;
          416                 goto waitstop;
          417         }
          418         if(strcmp(msg, "step") == 0){
          419                 if(ptrace(PTRACE_SINGLESTEP, pid, 0, 0) < 0)
          420                         return -1;
          421                 goto waitstop;
          422         }
          423         if(strcmp(msg, "waitstop") == 0){
          424         waitstop:
          425                 if(isstopped(pid))
          426                         return 0;
          427                 for(;;){
          428                         p = waitpid(pid, &status, WUNTRACED|__WALL);
          429                         if(p <= 0){
          430                                 if(errno == ECHILD){
          431                                         if(isstopped(pid))
          432                                                 return 0;
          433                                 }
          434                                 return -1;
          435                         }
          436 /*fprint(2, "got pid %d status %x\n", pid, status); */
          437                         if(WIFEXITED(status) || WIFSTOPPED(status))
          438                                 return 0;
          439                 }
          440         }
          441         if(strcmp(msg, "start") == 0)
          442                 return ptrace(PTRACE_CONT, pid, 0, 0);
          443         werrstr("unknown control message '%s'", msg);
          444         return -1;
          445 }
          446 
          447 char*
          448 proctextfile(int pid)
          449 {
          450         static char buf[1024], pbuf[128];
          451 
          452         snprint(pbuf, sizeof pbuf, "/proc/%d/exe", pid);
          453         if(readlink(pbuf, buf, sizeof buf) >= 0)
          454                 return buf;
          455         if(access(pbuf, AEXIST) >= 0)
          456                 return pbuf;
          457         return nil;
          458 }