| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | /**********************************************************************
 | 
					
						
							|  |  |  | ptrace.c | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Copyright (C) 1999 Lars Brinkhoff.  See the file COPYING for licensing | 
					
						
							|  |  |  | terms and conditions. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Jeff Dike (jdike@karaya.com) : Modified for integration into uml | 
					
						
							|  |  |  | **********************************************************************/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <errno.h>
 | 
					
						
							|  |  |  | #include <unistd.h>
 | 
					
						
							|  |  |  | #include <signal.h>
 | 
					
						
							|  |  |  | #include <sys/types.h>
 | 
					
						
							|  |  |  | #include <sys/time.h>
 | 
					
						
							|  |  |  | #include <sys/wait.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "ptproxy.h"
 | 
					
						
							|  |  |  | #include "debug.h"
 | 
					
						
							|  |  |  | #include "user_util.h"
 | 
					
						
							|  |  |  | #include "kern_util.h"
 | 
					
						
							|  |  |  | #include "ptrace_user.h"
 | 
					
						
							|  |  |  | #include "tt.h"
 | 
					
						
							| 
									
										
										
										
											2006-01-18 17:42:41 -08:00
										 |  |  | #include "os.h"
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | long proxy_ptrace(struct debugger *debugger, int arg1, pid_t arg2, | 
					
						
							|  |  |  | 		  long arg3, long arg4, pid_t child, int *ret) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	sigset_t relay; | 
					
						
							|  |  |  | 	long result; | 
					
						
							|  |  |  | 	int status; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	*ret = 0; | 
					
						
							|  |  |  | 	if(debugger->debugee->died) return(-ESRCH); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch(arg1){ | 
					
						
							|  |  |  | 	case PTRACE_ATTACH: | 
					
						
							|  |  |  | 		if(debugger->debugee->traced) return(-EPERM); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		debugger->debugee->pid = arg2; | 
					
						
							|  |  |  | 		debugger->debugee->traced = 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if(is_valid_pid(arg2) && (arg2 != child)){ | 
					
						
							|  |  |  | 			debugger->debugee->in_context = 0; | 
					
						
							|  |  |  | 			kill(arg2, SIGSTOP); | 
					
						
							|  |  |  | 			debugger->debugee->event = 1; | 
					
						
							|  |  |  | 			debugger->debugee->wait_status = W_STOPCODE(SIGSTOP); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		else { | 
					
						
							|  |  |  | 			debugger->debugee->in_context = 1; | 
					
						
							|  |  |  | 			if(debugger->debugee->stopped)  | 
					
						
							|  |  |  | 				child_proxy(child, W_STOPCODE(SIGSTOP)); | 
					
						
							|  |  |  | 			else kill(child, SIGSTOP); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return(0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case PTRACE_DETACH: | 
					
						
							|  |  |  | 		if(!debugger->debugee->traced) return(-EPERM); | 
					
						
							|  |  |  | 		 | 
					
						
							|  |  |  | 		debugger->debugee->traced = 0; | 
					
						
							|  |  |  | 		debugger->debugee->pid = 0; | 
					
						
							|  |  |  | 		if(!debugger->debugee->in_context) | 
					
						
							|  |  |  | 			kill(child, SIGCONT); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return(0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case PTRACE_CONT: | 
					
						
							|  |  |  | 		if(!debugger->debugee->in_context) return(-EPERM); | 
					
						
							|  |  |  | 		*ret = PTRACE_CONT; | 
					
						
							|  |  |  | 		return(ptrace(PTRACE_CONT, child, arg3, arg4)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef UM_HAVE_GETFPREGS
 | 
					
						
							|  |  |  | 	case PTRACE_GETFPREGS: | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		long regs[FP_FRAME_SIZE]; | 
					
						
							|  |  |  | 		int i, result; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		result = ptrace(PTRACE_GETFPREGS, child, 0, regs); | 
					
						
							|  |  |  | 		if(result == -1) return(-errno); | 
					
						
							|  |  |  | 		 | 
					
						
							|  |  |  | 		for (i = 0; i < sizeof(regs)/sizeof(regs[0]); i++) | 
					
						
							|  |  |  | 			ptrace(PTRACE_POKEDATA, debugger->pid, arg4 + 4 * i, | 
					
						
							|  |  |  | 			       regs[i]); | 
					
						
							|  |  |  | 		return(result); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef UM_HAVE_GETFPXREGS
 | 
					
						
							|  |  |  | 	case PTRACE_GETFPXREGS: | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		long regs[FPX_FRAME_SIZE]; | 
					
						
							|  |  |  | 		int i, result; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		result = ptrace(PTRACE_GETFPXREGS, child, 0, regs); | 
					
						
							|  |  |  | 		if(result == -1) return(-errno); | 
					
						
							|  |  |  | 		 | 
					
						
							|  |  |  | 		for (i = 0; i < sizeof(regs)/sizeof(regs[0]); i++) | 
					
						
							|  |  |  | 			ptrace(PTRACE_POKEDATA, debugger->pid, arg4 + 4 * i, | 
					
						
							|  |  |  | 			       regs[i]); | 
					
						
							|  |  |  | 		return(result); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef UM_HAVE_GETREGS
 | 
					
						
							|  |  |  | 	case PTRACE_GETREGS: | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		long regs[FRAME_SIZE]; | 
					
						
							|  |  |  | 		int i, result; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		result = ptrace(PTRACE_GETREGS, child, 0, regs); | 
					
						
							|  |  |  | 		if(result == -1) return(-errno); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		for (i = 0; i < sizeof(regs)/sizeof(regs[0]); i++) | 
					
						
							|  |  |  | 			ptrace (PTRACE_POKEDATA, debugger->pid, | 
					
						
							|  |  |  | 				arg4 + 4 * i, regs[i]); | 
					
						
							|  |  |  | 		return(result); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	break; | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case PTRACE_KILL: | 
					
						
							|  |  |  | 		result = ptrace(PTRACE_KILL, child, arg3, arg4); | 
					
						
							|  |  |  | 		if(result == -1) return(-errno); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return(result); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case PTRACE_PEEKDATA: | 
					
						
							|  |  |  | 	case PTRACE_PEEKTEXT: | 
					
						
							|  |  |  | 	case PTRACE_PEEKUSR: | 
					
						
							|  |  |  | 		/* The value being read out could be -1, so we have to 
 | 
					
						
							|  |  |  | 		 * check errno to see if there's an error, and zero it | 
					
						
							|  |  |  | 		 * beforehand so we're not faked out by an old error | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		errno = 0; | 
					
						
							|  |  |  | 		result = ptrace(arg1, child, arg3, 0); | 
					
						
							|  |  |  | 		if((result == -1) && (errno != 0)) return(-errno); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		result = ptrace(PTRACE_POKEDATA, debugger->pid, arg4, result); | 
					
						
							|  |  |  | 		if(result == -1) return(-errno); | 
					
						
							|  |  |  | 			 | 
					
						
							|  |  |  | 		return(result); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case PTRACE_POKEDATA: | 
					
						
							|  |  |  | 	case PTRACE_POKETEXT: | 
					
						
							|  |  |  | 	case PTRACE_POKEUSR: | 
					
						
							|  |  |  | 		result = ptrace(arg1, child, arg3, arg4); | 
					
						
							|  |  |  | 		if(result == -1) return(-errno); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if(arg1 == PTRACE_POKEUSR) ptrace_pokeuser(arg3, arg4); | 
					
						
							|  |  |  | 		return(result); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef UM_HAVE_SETFPREGS
 | 
					
						
							|  |  |  | 	case PTRACE_SETFPREGS: | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		long regs[FP_FRAME_SIZE]; | 
					
						
							|  |  |  | 		int i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		for (i = 0; i < sizeof(regs)/sizeof(regs[0]); i++) | 
					
						
							|  |  |  | 			regs[i] = ptrace (PTRACE_PEEKDATA, debugger->pid, | 
					
						
							|  |  |  | 					  arg4 + 4 * i, 0); | 
					
						
							|  |  |  | 		result = ptrace(PTRACE_SETFPREGS, child, 0, regs); | 
					
						
							|  |  |  | 		if(result == -1) return(-errno); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return(result); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef UM_HAVE_SETFPXREGS
 | 
					
						
							|  |  |  | 	case PTRACE_SETFPXREGS: | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		long regs[FPX_FRAME_SIZE]; | 
					
						
							|  |  |  | 		int i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		for (i = 0; i < sizeof(regs)/sizeof(regs[0]); i++) | 
					
						
							|  |  |  | 			regs[i] = ptrace (PTRACE_PEEKDATA, debugger->pid, | 
					
						
							|  |  |  | 					  arg4 + 4 * i, 0); | 
					
						
							|  |  |  | 		result = ptrace(PTRACE_SETFPXREGS, child, 0, regs); | 
					
						
							|  |  |  | 		if(result == -1) return(-errno); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return(result); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef UM_HAVE_SETREGS
 | 
					
						
							|  |  |  | 	case PTRACE_SETREGS: | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		long regs[FRAME_SIZE]; | 
					
						
							|  |  |  | 		int i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		for (i = 0; i < sizeof(regs)/sizeof(regs[0]); i++) | 
					
						
							|  |  |  | 			regs[i] = ptrace(PTRACE_PEEKDATA, debugger->pid, | 
					
						
							|  |  |  | 					 arg4 + 4 * i, 0); | 
					
						
							|  |  |  | 		result = ptrace(PTRACE_SETREGS, child, 0, regs); | 
					
						
							|  |  |  | 		if(result == -1) return(-errno); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return(result); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case PTRACE_SINGLESTEP: | 
					
						
							|  |  |  | 		if(!debugger->debugee->in_context) return(-EPERM); | 
					
						
							|  |  |  | 		sigemptyset(&relay); | 
					
						
							|  |  |  | 		sigaddset(&relay, SIGSEGV); | 
					
						
							|  |  |  | 		sigaddset(&relay, SIGILL); | 
					
						
							|  |  |  | 		sigaddset(&relay, SIGBUS); | 
					
						
							|  |  |  | 		result = ptrace(PTRACE_SINGLESTEP, child, arg3, arg4); | 
					
						
							|  |  |  | 		if(result == -1) return(-errno); | 
					
						
							|  |  |  | 		 | 
					
						
							|  |  |  | 		status = wait_for_stop(child, SIGTRAP, PTRACE_SINGLESTEP, | 
					
						
							|  |  |  | 				       &relay); | 
					
						
							|  |  |  | 		child_proxy(child, status); | 
					
						
							|  |  |  | 		return(result); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case PTRACE_SYSCALL: | 
					
						
							|  |  |  | 		if(!debugger->debugee->in_context) return(-EPERM); | 
					
						
							|  |  |  | 		result = ptrace(PTRACE_SYSCALL, child, arg3, arg4); | 
					
						
							|  |  |  | 		if(result == -1) return(-errno); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		*ret = PTRACE_SYSCALL; | 
					
						
							|  |  |  | 		return(result); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case PTRACE_TRACEME: | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		return(-EINVAL); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Overrides for Emacs so that we follow Linus's tabbing style. | 
					
						
							|  |  |  |  * Emacs will notice this stuff at the end of the file and automatically | 
					
						
							|  |  |  |  * adjust the settings for this buffer only.  This must remain at the end | 
					
						
							|  |  |  |  * of the file. | 
					
						
							|  |  |  |  * --------------------------------------------------------------------------- | 
					
						
							|  |  |  |  * Local variables: | 
					
						
							|  |  |  |  * c-file-style: "linux" | 
					
						
							|  |  |  |  * End: | 
					
						
							|  |  |  |  */ |