Blob


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 */
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"
29 Mach *machcpu = &mach386;
31 typedef struct PtraceRegs PtraceRegs;
33 struct PtraceRegs
34 {
35 Regs r;
36 int pid;
37 };
39 static int ptracesegrw(Map*, Seg*, ulong, void*, uint, int);
40 static int ptraceregrw(Regs*, char*, ulong*, int);
42 static int attachedpids[1000];
43 static int nattached;
45 int
46 ptraceattach(int pid)
47 {
48 int i;
50 /*
51 if(nattached==1 && attachedpids[0] == pid)
52 goto already;
53 if(nattached)
54 detachproc(attachedpids[0]);
55 */
57 for(i=0; i<nattached; i++)
58 if(attachedpids[i]==pid)
59 return 0;
60 if(nattached == nelem(attachedpids)){
61 werrstr("attached to too many processes");
62 return -1;
63 }
65 if(ptrace(PTRACE_ATTACH, pid, 0, 0) < 0){
66 werrstr("ptrace attach %d: %r", pid);
67 return -1;
68 }
70 if(ctlproc(pid, "waitstop") < 0){
71 fprint(2, "waitstop: %r");
72 ptrace(PTRACE_DETACH, pid, 0, 0);
73 return -1;
74 }
75 attachedpids[nattached++] = pid;
76 return 0;
77 }
79 void
80 unmapproc(Map *map)
81 {
82 int i;
84 if(map == nil)
85 return;
86 for(i=0; i<map->nseg; i++)
87 while(i<map->nseg && map->seg[i].pid){
88 map->nseg--;
89 memmove(&map->seg[i], &map->seg[i+1],
90 (map->nseg-i)*sizeof(map->seg[0]));
91 }
92 }
96 int
97 mapproc(int pid, Map *map, Regs **rp)
98 {
99 Seg s;
100 PtraceRegs *r;
102 if(ptraceattach(pid) < 0)
103 return -1;
105 memset(&s, 0, sizeof s);
106 s.base = 0;
107 s.size = 0xFFFFFFFF;
108 s.offset = 0;
109 s.name = "data";
110 s.file = nil;
111 s.rw = ptracesegrw;
112 s.pid = pid;
113 if(addseg(map, s) < 0){
114 fprint(2, "addseg: %r\n");
115 return -1;
118 if((r = mallocz(sizeof(PtraceRegs), 1)) == nil){
119 fprint(2, "mallocz: %r\n");
120 return -1;
122 r->r.rw = ptraceregrw;
123 r->pid = pid;
124 *rp = (Regs*)r;
125 return 0;
128 int
129 detachproc(int pid)
131 int i;
133 for(i=0; i<nattached; i++){
134 if(attachedpids[i] == pid){
135 attachedpids[i] = attachedpids[--nattached];
136 break;
139 return ptrace(PTRACE_DETACH, pid, 0, 0);
142 static int
143 ptracerw(int type, int xtype, int isr, int pid, ulong addr, void *v, uint n)
145 int i;
146 u32int u;
147 uchar buf[4];
149 for(i=0; i<n; i+=4){
150 if(isr){
151 errno = 0;
152 u = ptrace(type, pid, addr+i, 0);
153 if(errno)
154 goto ptraceerr;
155 if(n-i >= 4)
156 *(u32int*)((char*)v+i) = u;
157 else{
158 *(u32int*)buf = u;
159 memmove((char*)v+i, buf, n-i);
161 }else{
162 if(n-i >= 4)
163 u = *(u32int*)((char*)v+i);
164 else{
165 errno = 0;
166 u = ptrace(xtype, pid, addr+i, 0);
167 if(errno)
168 return -1;
169 *(u32int*)buf = u;
170 memmove(buf, (char*)v+i, n-i);
171 u = *(u32int*)buf;
173 if(ptrace(type, pid, addr+i, &u) < 0)
174 goto ptraceerr;
177 return 0;
179 ptraceerr:
180 werrstr("ptrace: %r");
181 return -1;
184 static int
185 ptracesegrw(Map *map, Seg *seg, ulong addr, void *v, uint n, int isr)
187 addr += seg->base;
188 return ptracerw(isr ? PTRACE_PEEKDATA : PTRACE_POKEDATA, PTRACE_PEEKDATA,
189 isr, seg->pid, addr, v, n);
192 static char* linuxregs[] = {
193 "BX",
194 "CX",
195 "DX",
196 "SI",
197 "DI",
198 "BP",
199 "AX",
200 "DS",
201 "ES",
202 "FS",
203 "GS",
204 "OAX",
205 "PC",
206 "CS",
207 "EFLAGS",
208 "SP",
209 "SS",
210 };
212 static ulong
213 reg2linux(char *reg)
215 int i;
217 for(i=0; i<nelem(linuxregs); i++)
218 if(strcmp(linuxregs[i], reg) == 0)
219 return 4*i;
220 return ~(ulong)0;
223 static int
224 ptraceregrw(Regs *regs, char *name, ulong *val, int isr)
226 int pid;
227 ulong addr;
228 u32int u;
230 pid = ((PtraceRegs*)regs)->pid;
231 addr = reg2linux(name);
232 if(~addr == 0){
233 if(isr){
234 *val = ~(ulong)0;
235 return 0;
237 werrstr("register not available");
238 return -1;
240 if(isr){
241 errno = 0;
242 u = ptrace(PTRACE_PEEKUSER, pid, addr, 0);
243 if(errno)
244 goto ptraceerr;
245 *val = u;
246 }else{
247 u = *val;
248 if(ptrace(PTRACE_POKEUSER, pid, addr, &u) < 0)
249 goto ptraceerr;
251 return 0;
253 ptraceerr:
254 werrstr("ptrace: %r");
255 return -1;
258 static int
259 isstopped(int pid)
261 char buf[1024];
262 int fd, n;
263 char *p;
265 snprint(buf, sizeof buf, "/proc/%d/stat", pid);
266 if((fd = open(buf, OREAD)) < 0)
267 return 0;
268 n = read(fd, buf, sizeof buf-1);
269 close(fd);
270 if(n <= 0)
271 return 0;
272 buf[n] = 0;
274 /* command name is in parens, no parens afterward */
275 p = strrchr(buf, ')');
276 if(p == nil || *++p != ' ')
277 return 0;
278 ++p;
280 /* next is state - T is stopped for tracing */
281 return *p == 'T';
284 /* /proc/pid/stat contains
285 pid
286 command in parens
287 0. state
288 1. ppid
289 2. pgrp
290 3. session
291 4. tty_nr
292 5. tpgid
293 6. flags (math=4, traced=10)
294 7. minflt
295 8. cminflt
296 9. majflt
297 10. cmajflt
298 11. utime
299 12. stime
300 13. cutime
301 14. cstime
302 15. priority
303 16. nice
304 17. 0
305 18. itrealvalue
306 19. starttime
307 20. vsize
308 21. rss
309 22. rlim
310 23. startcode
311 24. endcode
312 25. startstack
313 26. kstkesp
314 27. kstkeip
315 28. pending signal bitmap
316 29. blocked signal bitmap
317 30. ignored signal bitmap
318 31. caught signal bitmap
319 32. wchan
320 33. nswap
321 34. cnswap
322 35. exit_signal
323 36. processor
324 */
327 int
328 procnotes(int pid, char ***pnotes)
330 char buf[1024], *f[40];
331 int fd, i, n, nf;
332 char *p, *s, **notes;
333 ulong sigs;
334 extern char *_p9sigstr(int, char*);
336 *pnotes = nil;
337 snprint(buf, sizeof buf, "/proc/%d/stat", pid);
338 if((fd = open(buf, OREAD)) < 0){
339 fprint(2, "open %s: %r\n", buf);
340 return -1;
342 n = read(fd, buf, sizeof buf-1);
343 close(fd);
344 if(n <= 0){
345 fprint(2, "read %s: %r\n", buf);
346 return -1;
348 buf[n] = 0;
350 /* command name is in parens, no parens afterward */
351 p = strrchr(buf, ')');
352 if(p == nil || *++p != ' '){
353 fprint(2, "bad format in /proc/%d/stat\n", pid);
354 return -1;
356 ++p;
358 nf = tokenize(p, f, nelem(f));
359 if(0) print("code 0x%lux-0x%lux stack 0x%lux kstk 0x%lux keip 0x%lux pending 0x%lux\n",
360 strtoul(f[23], 0, 0), strtoul(f[24], 0, 0), strtoul(f[25], 0, 0),
361 strtoul(f[26], 0, 0), strtoul(f[27], 0, 0), strtoul(f[28], 0, 0));
362 if(nf <= 28)
363 return -1;
365 sigs = strtoul(f[28], 0, 0) & ~(1<<SIGCONT);
366 if(sigs == 0){
367 *pnotes = nil;
368 return 0;
371 notes = mallocz(32*sizeof(char*), 0);
372 if(notes == nil)
373 return -1;
374 n = 0;
375 for(i=0; i<32; i++){
376 if((sigs&(1<<i)) == 0)
377 continue;
378 if((s = _p9sigstr(i, nil)) == nil)
379 continue;
380 notes[n++] = s;
382 *pnotes = notes;
383 return n;
386 #undef waitpid
388 int
389 ctlproc(int pid, char *msg)
391 int p, status;
393 if(strcmp(msg, "hang") == 0){
394 if(pid == getpid())
395 return ptrace(PTRACE_TRACEME, 0, 0, 0);
396 werrstr("can only hang self");
397 return -1;
399 if(strcmp(msg, "kill") == 0)
400 return ptrace(PTRACE_KILL, pid, 0, 0);
401 if(strcmp(msg, "startstop") == 0){
402 if(ptrace(PTRACE_CONT, pid, 0, 0) < 0)
403 return -1;
404 goto waitstop;
406 if(strcmp(msg, "sysstop") == 0){
407 if(ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0)
408 return -1;
409 goto waitstop;
411 if(strcmp(msg, "stop") == 0){
412 if(kill(pid, SIGSTOP) < 0)
413 return -1;
414 goto waitstop;
416 if(strcmp(msg, "waitstop") == 0){
417 waitstop:
418 if(isstopped(pid))
419 return 0;
420 for(;;){
421 p = waitpid(pid, &status, WUNTRACED|__WALL);
422 if(p <= 0){
423 if(errno == ECHILD){
424 if(isstopped(pid))
425 return 0;
427 return -1;
429 if(WIFEXITED(status) || WIFSTOPPED(status))
430 return 0;
433 if(strcmp(msg, "start") == 0)
434 return ptrace(PTRACE_CONT, pid, 0, 0);
435 werrstr("unknown control message '%s'", msg);
436 return -1;
439 char*
440 proctextfile(int pid)
442 static char buf[1024], pbuf[128];
444 snprint(pbuf, sizeof pbuf, "/proc/%d/exe", pid);
445 if(readlink(pbuf, buf, sizeof buf) >= 0)
446 return buf;
447 if(access(pbuf, AEXIST) >= 0)
448 return pbuf;
449 return nil;
453 #if 0
454 snprint(buf, sizeof buf, "/proc/%d/maps", pid);
455 if((b = Bopen(buf, OREAD)) == nil){
456 werrstr("open %s: %r", buf);
457 return -1;
460 /*
461 08048000-08056000 r-xp 00000000 03:0c 64593 /usr/sbin/gpm
462 08056000-08058000 rw-p 0000d000 03:0c 64593 /usr/sbin/gpm
463 08058000-0805b000 rwxp 00000000 00:00 0
464 40000000-40013000 r-xp 00000000 03:0c 4165 /lib/ld-2.2.4.so
465 40013000-40015000 rw-p 00012000 03:0c 4165 /lib/ld-2.2.4.so
466 4001f000-40135000 r-xp 00000000 03:0c 45494 /lib/libc-2.2.4.so
467 40135000-4013e000 rw-p 00115000 03:0c 45494 /lib/libc-2.2.4.so
468 4013e000-40142000 rw-p 00000000 00:00 0
469 bffff000-c0000000 rwxp 00000000 00:00 0
470 */
472 file = nil;
473 while((p = Brdline(b, '\n')) != nil){
474 p[Blinelen(b)-1] = 0;
475 memset(f, 0, sizeof f);
476 if((nf = getfields(p, f, 6, 1, " ")) < 5)
477 continue;
478 base = strtoul(f[0], &p, 16);
479 if(*p != '-')
480 continue;
481 end = strtoul(p+1, &p, 16);
482 if(*p != 0)
483 continue;
484 offset = strtoul(f[2], &p, 16);
485 if(*p != 0)
486 continue;
487 if(nf == 6)
488 file = f[5];
489 zero = atoi(f[4]) == 0;
490 print("%lux-%lux %lux %s %s\n", base, end, offset, zero ? "data" : "text", file ? file : "");
491 s.base = base;
492 s.size = end - base;
493 s.offset = offset;
494 s.name = zero ? "data" : "text";
495 s.file = strdup(file);
496 s.rw = ptracerw;
497 s.pid = pid;
498 if(addseg(map, s) < 0){
499 Bterm(b);
500 ptrace(PTRACE_DETACH, pid, 0, 0);
501 return -1;
504 Bterm(b);
505 #endif
507 /*
508 * bottom-end functions for libthread_db to call
509 */
510 enum
512 PS_OK,
513 PS_ERR,
514 PS_BADPID,
515 PS_BADLWPID,
516 PS_BADADDR,
517 PS_NOSYM,
518 PS_NOFPREGS,
519 };
521 pid_t
522 ps_getpid(struct ps_prochandle *ph)
524 return ph->pid;
527 int
528 ps_pstop(const struct ps_prochandle *ph)
530 return PS_ERR;
533 int
534 ps_pcontinue(const struct ps_prochandle *ph)
536 return PS_ERR;
539 int
540 ps_lstop(const struct ps_prochandle *ph)
542 return PS_ERR;
545 int
546 ps_lcontinue(const struct ps_prochandle *ph)
548 return PS_ERR;
551 /* read/write data or text memory */
552 int
553 ps_pdread(struct ps_prochandle *ph, psaddr_t addr, void *v, size_t sz)
555 //print("read %d %p %d\n", ph->pid, addr, sz);
556 if(ptracerw(PTRACE_PEEKDATA, 0, 1, ph->pid, (ulong)addr, v, sz) < 0)
557 return PS_ERR;
558 //print(" => 0x%lux\n", *(ulong*)v);
559 return PS_OK;
562 int
563 ps_pdwrite(struct ps_prochandle *ph, psaddr_t addr, void *v, size_t sz)
565 //print("write %d %p\n", ph->pid, addr);
566 if(ptracerw(PTRACE_POKEDATA, PTRACE_PEEKDATA, 0, ph->pid, (ulong)addr, v, sz) < 0)
567 return PS_ERR;
568 return PS_OK;
571 int
572 ps_ptread(struct ps_prochandle *ph, psaddr_t addr, void *v, size_t sz)
574 //print("read %d %p\n", ph->pid, addr);
575 if(ptracerw(PTRACE_PEEKTEXT, 0, 1, ph->pid, (ulong)addr, v, sz) < 0)
576 return PS_ERR;
577 return PS_OK;
580 int
581 ps_ptwrite(struct ps_prochandle *ph, psaddr_t addr, void *v, size_t sz)
583 //print("write %d %p\n", ph->pid, addr);
584 if(ptracerw(PTRACE_POKETEXT, PTRACE_PEEKTEXT, 0, ph->pid, (ulong)addr, v, sz) < 0)
585 return PS_ERR;
586 return PS_OK;
589 int
590 ps_lgetregs(struct ps_prochandle *ph, lwpid_t lwp, prgregset_t regs)
592 if(lwp == 0){
593 memset(regs, 0xfe, sizeof(regs[0])*nelem(linuxregs));
594 return PS_OK;
596 //print("getregs %d %p (%d)\n", lwp, regs, sizeof(regs[0])*nelem(linuxregs));
598 if(ptraceattach(lwp) < 0){
599 fprint(2, "ptrace attach: %r\n");
600 return PS_ERR;
603 if(ptracerw(PTRACE_PEEKUSER, 0, 1, lwp, 0, regs, sizeof(regs[0])*nelem(linuxregs)) < 0){
604 fprint(2, "ptrace: %r\n");
605 return PS_ERR;
607 return PS_OK;
610 int
611 ps_lsetregs(struct ps_prochandle *ph, lwpid_t lwp, prgregset_t regs)
613 print("setregs %d\n", lwp);
614 if(ptracerw(PTRACE_POKEUSER, PTRACE_PEEKUSER, 1, lwp, 0, regs, sizeof(regs[0])*nelem(linuxregs)) < 0)
615 return PS_ERR;
616 return PS_OK;
619 int
620 ps_lgetfpregs(struct ps_prochandle *ph, lwpid_t lwp, prfpregset_t *fpregs)
622 if(ptracerw(PTRACE_PEEKUSER, 0, 1, lwp, 18*4, fpregs, sizeof *fpregs) < 0)
623 return PS_ERR;
624 return PS_OK;
627 int
628 ps_lsetfpregs(struct ps_prochandle *ph, lwpid_t lwp, prfpregset_t *fpregs)
630 if(ptracerw(PTRACE_POKEUSER, PTRACE_PEEKUSER, 1, lwp, 18*4, fpregs, sizeof *fpregs) < 0)
631 return PS_ERR;
632 return PS_OK;
635 /* Fetch the special per-thread address associated with the given LWP.
636 This call is only used on a few platforms (most use a normal register).
637 The meaning of the `int' parameter is machine-dependent. */
638 int
639 ps_get_thread_area(struct ps_prochandle *ph, lwpid_t lwp, int xxx, psaddr_t *addr)
641 return PS_NOSYM;
644 /* Look up the named symbol in the named DSO in the symbol tables
645 associated with the process being debugged, filling in *SYM_ADDR
646 with the corresponding run-time address. */
647 int
648 ps_pglobal_lookup(struct ps_prochandle *ph, char *object_name, char *sym_name, psaddr_t *sym_addr)
650 Fhdr *fp;
651 ulong addr;
653 if((fp = findhdr(object_name)) == nil){
654 print("lookup %d %s %s => no such hdr\n", ph->pid, object_name, sym_name);
655 return PS_NOSYM;
657 if(elfsymlookup(fp->elf, sym_name, &addr) < 0){
658 print("lookup %d %s %s => name not found\n", ph->pid, object_name, sym_name);
659 return PS_NOSYM;
661 print("lookup %d %s %s => 0x%lux\n", ph->pid, object_name, sym_name, addr);
662 *sym_addr = (void*)(addr+fp->base);
663 return PS_OK;
666 Ureg*
667 _linux2ureg386(UregLinux386 *l)
669 Ureg *u;
671 u = malloc(sizeof(Ureg));
672 if(u == nil)
673 return nil;
674 u->di = l->edi;
675 u->si = l->esi;
676 u->bp = l->ebp;
677 u->nsp = l->esp;
678 u->bx = l->ebx;
679 u->dx = l->edx;
680 u->cx = l->ecx;
681 u->ax = l->eax;
682 u->gs = l->xgs;
683 u->fs = l->xfs;
684 u->es = l->xes;
685 u->ds = l->xds;
686 u->trap = ~0; // l->trapno;
687 u->ecode = ~0; // l->err;
688 u->pc = l->eip;
689 u->cs = l->xcs;
690 u->flags = l->eflags;
691 u->sp = l->esp;
692 u->ss = l->xss;
693 return u;