Linux.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 }