plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

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 }