459 lines · 8.0 KB · C
RawDownload
/* * 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 <u.h>#include <sys/ptrace.h>#include <sys/types.h>#include <sys/wait.h>#include <sys/procfs.h>#include <signal.h>#include <errno.h>#include <libc.h>#include <mach.h>#include <elf.h>#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 intptraceattach(int pid){	int i; 	for(i=0; i<nattached; i++)		if(attachedpids[i]==pid)			return 0;	if(nattached == nelem(attachedpids)){		werrstr("attached to too many processes");		return -1;	} 	if(ptrace(PTRACE_ATTACH, pid, 0, 0) < 0){		werrstr("ptrace attach %d: %r", pid);		return -1;	}		if(ctlproc(pid, "waitstop") < 0){		fprint(2, "waitstop: %r");		ptrace(PTRACE_DETACH, pid, 0, 0);		return -1;	}	attachedpids[nattached++] = pid;	return 0;} voidunmapproc(Map *map){	int i; 	if(map == nil)		return;	for(i=0; i<map->nseg; i++)		while(i<map->nseg && map->seg[i].pid){			map->nseg--;			memmove(&map->seg[i], &map->seg[i+1], 				(map->nseg-i)*sizeof(map->seg[0]));		}} intmapproc(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;} intdetachproc(int pid){	int i; 	for(i=0; i<nattached; i++){		if(attachedpids[i] == pid){			attachedpids[i] = attachedpids[--nattached];			break;		}	}	return ptrace(PTRACE_DETACH, pid, 0, 0);} static intptracerw(int type, int xtype, int isr, int pid, ulong addr, void *v, uint n){	int i;	u32int u;	uchar buf[4]; 	for(i=0; i<n; i+=4){		if(isr){			errno = 0;			u = ptrace(type, pid, addr+i, 0);			if(errno)				goto ptraceerr;			if(n-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 intptracesegrw(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 ulongreg2linux(char *reg){	int i; 	for(i=0; i<nelem(linuxregs); i++)		if(strcmp(linuxregs[i], reg) == 0)			return 4*i;	return ~(ulong)0;} static intptraceregrw(Regs *regs, char *name, ulong *val, int isr){	int pid;	ulong addr;	u32int u; 	pid = ((PtraceRegs*)regs)->pid;	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, (void*)(uintptr)u) < 0)			goto ptraceerr;	}	return 0; ptraceerr:	werrstr("ptrace: %r");	return -1;} static intisstopped(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*/ intprocnotes(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<<SIGCONT);	if(sigs == 0){		*pnotes = nil;		return 0;	} 	notes = mallocz(32*sizeof(char*), 0);	if(notes == nil)		return -1;	n = 0;	for(i=0; i<32; i++){		if((sigs&(1<<i)) == 0)			continue;		if((s = _p9sigstr(i, nil)) == nil)			continue;		notes[n++] = s;	}	*pnotes = notes;	return n;} #undef waitpid intctlproc(int pid, char *msg){	int i, p, status; 	if(strcmp(msg, "attached") == 0){		for(i=0; i<nattached; i++)			if(attachedpids[i]==pid)				return 0;		if(nattached == nelem(attachedpids)){			werrstr("attached to too many processes");			return -1;		}		attachedpids[nattached++] = pid;		return 0;	} 	if(strcmp(msg, "hang") == 0){		if(pid == getpid())			return ptrace(PTRACE_TRACEME, 0, 0, 0);		werrstr("can only hang self");		return -1;	}	if(strcmp(msg, "kill") == 0)		return ptrace(PTRACE_KILL, pid, 0, 0);	if(strcmp(msg, "startstop") == 0){		if(ptrace(PTRACE_CONT, pid, 0, 0) < 0)			return -1;		goto waitstop;	}	if(strcmp(msg, "sysstop") == 0){		if(ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0)			return -1;		goto waitstop;	}	if(strcmp(msg, "stop") == 0){		if(kill(pid, SIGSTOP) < 0)			return -1;		goto waitstop;	}	if(strcmp(msg, "step") == 0){		if(ptrace(PTRACE_SINGLESTEP, pid, 0, 0) < 0)			return -1;		goto waitstop;	}	if(strcmp(msg, "waitstop") == 0){	waitstop:		if(isstopped(pid))			return 0;		for(;;){			p = waitpid(pid, &status, WUNTRACED|__WALL);			if(p <= 0){				if(errno == ECHILD){					if(isstopped(pid))						return 0;				}				return -1;			}/*fprint(2, "got pid %d status %x\n", pid, status); */			if(WIFEXITED(status) || WIFSTOPPED(status))				return 0;		}	}	if(strcmp(msg, "start") == 0)		return ptrace(PTRACE_CONT, pid, 0, 0);	werrstr("unknown control message '%s'", msg);	return -1;} char*proctextfile(int pid){	static char buf[1024], pbuf[128]; 	snprint(pbuf, sizeof pbuf, "/proc/%d/exe", pid);	if(readlink(pbuf, buf, sizeof buf) >= 0)		return buf;	if(access(pbuf, AEXIST) >= 0)		return pbuf;	return nil;}