summary refs log tree commit diff
path: root/arch/um/kernel/tt/ptproxy/proxy.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/um/kernel/tt/ptproxy/proxy.c')
-rw-r--r--arch/um/kernel/tt/ptproxy/proxy.c377
1 files changed, 377 insertions, 0 deletions
diff --git a/arch/um/kernel/tt/ptproxy/proxy.c b/arch/um/kernel/tt/ptproxy/proxy.c
new file mode 100644
index 000000000000..58800c50b10e
--- /dev/null
+++ b/arch/um/kernel/tt/ptproxy/proxy.c
@@ -0,0 +1,377 @@
+/**********************************************************************
+proxy.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
+**********************************************************************/
+
+/* XXX This file shouldn't refer to CONFIG_* */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+#include <termios.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <asm/unistd.h>
+#include "ptrace_user.h"
+
+#include "ptproxy.h"
+#include "sysdep.h"
+#include "wait.h"
+
+#include "user_util.h"
+#include "user.h"
+#include "os.h"
+#include "tempfile.h"
+
+static int debugger_wait(debugger_state *debugger, int *status, int options,
+			 int (*syscall)(debugger_state *debugger, pid_t child),
+			 int (*normal_return)(debugger_state *debugger, 
+					      pid_t unused),
+			 int (*wait_return)(debugger_state *debugger, 
+					    pid_t unused))
+{
+	if(debugger->real_wait){
+		debugger->handle_trace = normal_return;
+		syscall_continue(debugger->pid);
+		debugger->real_wait = 0;
+		return(1);
+	}
+	debugger->wait_status_ptr = status;
+	debugger->wait_options = options;
+	if((debugger->debugee != NULL) && debugger->debugee->event){
+		syscall_continue(debugger->pid);
+		wait_for_stop(debugger->pid, SIGTRAP, PTRACE_SYSCALL,
+			      NULL);
+		(*wait_return)(debugger, -1);
+		return(0);
+	}
+	else if(debugger->wait_options & WNOHANG){
+		syscall_cancel(debugger->pid, 0);
+		debugger->handle_trace = syscall;
+		return(0);
+	}
+	else {
+		syscall_pause(debugger->pid);
+		debugger->handle_trace = wait_return;
+		debugger->waiting = 1;
+	}
+	return(1);
+}
+
+/*
+ * Handle debugger trap, i.e. syscall.
+ */
+
+int debugger_syscall(debugger_state *debugger, pid_t child)
+{
+	long arg1, arg2, arg3, arg4, arg5, result;
+	int syscall, ret = 0;
+
+	syscall = get_syscall(debugger->pid, &arg1, &arg2, &arg3, &arg4, 
+			      &arg5);
+
+	switch(syscall){
+	case __NR_execve:
+		/* execve never returns */
+		debugger->handle_trace = debugger_syscall; 
+		break;
+
+	case __NR_ptrace:
+		if(debugger->debugee->pid != 0) arg2 = debugger->debugee->pid;
+		if(!debugger->debugee->in_context) 
+			child = debugger->debugee->pid;
+		result = proxy_ptrace(debugger, arg1, arg2, arg3, arg4, child,
+				      &ret);
+		syscall_cancel(debugger->pid, result);
+		debugger->handle_trace = debugger_syscall;
+		return(ret);
+
+#ifdef __NR_waitpid
+	case __NR_waitpid:
+#endif
+	case __NR_wait4:
+		if(!debugger_wait(debugger, (int *) arg2, arg3, 
+				  debugger_syscall, debugger_normal_return, 
+				  proxy_wait_return))
+			return(0);
+		break;
+
+	case __NR_kill:
+		if(!debugger->debugee->in_context) 
+			child = debugger->debugee->pid;
+		if(arg1 == debugger->debugee->pid){
+			result = kill(child, arg2);
+			syscall_cancel(debugger->pid, result);
+			debugger->handle_trace = debugger_syscall;
+			return(0);
+		}
+		else debugger->handle_trace = debugger_normal_return;
+		break;
+
+	default:
+		debugger->handle_trace = debugger_normal_return;
+	}
+
+	syscall_continue(debugger->pid);
+	return(0);
+}
+
+/* Used by the tracing thread */
+static debugger_state parent;
+static int parent_syscall(debugger_state *debugger, int pid);
+
+int init_parent_proxy(int pid)
+{
+	parent = ((debugger_state) { .pid 		= pid,
+				     .wait_options 	= 0,
+				     .wait_status_ptr 	= NULL,
+				     .waiting 		= 0,
+				     .real_wait 	= 0,
+				     .expecting_child 	= 0,
+				     .handle_trace  	= parent_syscall,
+				     .debugee 		= NULL } );
+	return(0);
+}
+
+int parent_normal_return(debugger_state *debugger, pid_t unused)
+{
+	debugger->handle_trace = parent_syscall;
+	syscall_continue(debugger->pid);
+	return(0);
+}
+
+static int parent_syscall(debugger_state *debugger, int pid)
+{
+	long arg1, arg2, arg3, arg4, arg5;
+	int syscall;
+
+	syscall = get_syscall(pid, &arg1, &arg2, &arg3, &arg4, &arg5);
+		
+	if((syscall == __NR_wait4)
+#ifdef __NR_waitpid
+	   || (syscall == __NR_waitpid)
+#endif
+	){
+		debugger_wait(&parent, (int *) arg2, arg3, parent_syscall,
+			      parent_normal_return, parent_wait_return);
+	}
+	else ptrace(PTRACE_SYSCALL, pid, 0, 0);
+	return(0);
+}
+
+int debugger_normal_return(debugger_state *debugger, pid_t unused)
+{
+	debugger->handle_trace = debugger_syscall;
+	syscall_continue(debugger->pid);
+	return(0);
+}
+
+void debugger_cancelled_return(debugger_state *debugger, int result)
+{
+	debugger->handle_trace = debugger_syscall;
+	syscall_set_result(debugger->pid, result);
+	syscall_continue(debugger->pid);
+}
+
+/* Used by the tracing thread */
+static debugger_state debugger;
+static debugee_state debugee;
+
+void init_proxy (pid_t debugger_pid, int stopped, int status)
+{
+	debugger.pid = debugger_pid;
+	debugger.handle_trace = debugger_syscall;
+	debugger.debugee = &debugee;
+	debugger.waiting = 0;
+	debugger.real_wait = 0;
+	debugger.expecting_child = 0;
+
+	debugee.pid = 0;
+	debugee.traced = 0;
+	debugee.stopped = stopped;
+	debugee.event = 0;
+	debugee.zombie = 0;
+	debugee.died = 0;
+	debugee.wait_status = status;
+	debugee.in_context = 1;
+}
+
+int debugger_proxy(int status, int pid)
+{
+	int ret = 0, sig;
+
+	if(WIFSTOPPED(status)){
+		sig = WSTOPSIG(status);
+		if (sig == SIGTRAP)
+			ret = (*debugger.handle_trace)(&debugger, pid);
+						       
+		else if(sig == SIGCHLD){
+			if(debugger.expecting_child){
+				ptrace(PTRACE_SYSCALL, debugger.pid, 0, sig);
+				debugger.expecting_child = 0;
+			}
+			else if(debugger.waiting)
+				real_wait_return(&debugger);
+			else {
+				ptrace(PTRACE_SYSCALL, debugger.pid, 0, sig);
+				debugger.real_wait = 1;
+			}
+		}
+		else ptrace(PTRACE_SYSCALL, debugger.pid, 0, sig);
+	}
+	else if(WIFEXITED(status)){
+		tracer_panic("debugger (pid %d) exited with status %d", 
+			     debugger.pid, WEXITSTATUS(status));
+	}
+	else if(WIFSIGNALED(status)){
+		tracer_panic("debugger (pid %d) exited with signal %d", 
+			     debugger.pid, WTERMSIG(status));
+	}
+	else {
+		tracer_panic("proxy got unknown status (0x%x) on debugger "
+			     "(pid %d)", status, debugger.pid);
+	}
+	return(ret);
+}
+
+void child_proxy(pid_t pid, int status)
+{
+	debugee.event = 1;
+	debugee.wait_status = status;
+
+	if(WIFSTOPPED(status)){
+		debugee.stopped = 1;
+		debugger.expecting_child = 1;
+		kill(debugger.pid, SIGCHLD);
+	}
+	else if(WIFEXITED(status) || WIFSIGNALED(status)){
+		debugee.zombie = 1;
+		debugger.expecting_child = 1;
+		kill(debugger.pid, SIGCHLD);
+	}
+	else panic("proxy got unknown status (0x%x) on child (pid %d)", 
+		   status, pid);
+}
+
+void debugger_parent_signal(int status, int pid)
+{
+	int sig;
+
+	if(WIFSTOPPED(status)){
+		sig = WSTOPSIG(status);
+		if(sig == SIGTRAP) (*parent.handle_trace)(&parent, pid);
+		else ptrace(PTRACE_SYSCALL, pid, 0, sig);
+	}
+}
+
+void fake_child_exit(void)
+{
+	int status, pid;
+
+	child_proxy(1, W_EXITCODE(0, 0));
+	while(debugger.waiting == 1){
+		CATCH_EINTR(pid = waitpid(debugger.pid, &status, WUNTRACED));
+		if(pid != debugger.pid){
+			printk("fake_child_exit - waitpid failed, "
+			       "errno = %d\n", errno);
+			return;
+		}
+		debugger_proxy(status, debugger.pid);
+	}
+	CATCH_EINTR(pid = waitpid(debugger.pid, &status, WUNTRACED));
+	if(pid != debugger.pid){
+		printk("fake_child_exit - waitpid failed, "
+		       "errno = %d\n", errno);
+		return;
+	}
+	if(ptrace(PTRACE_DETACH, debugger.pid, 0, SIGCONT) < 0)
+		printk("fake_child_exit - PTRACE_DETACH failed, errno = %d\n",
+		       errno);
+}
+
+char gdb_init_string[] = 
+"att 1 \n\
+b panic \n\
+b stop \n\
+handle SIGWINCH nostop noprint pass \n\
+";
+
+int start_debugger(char *prog, int startup, int stop, int *fd_out)
+{
+	int slave, child;
+
+	slave = open_gdb_chan();
+	child = fork();
+	if(child == 0){
+		char *tempname = NULL;
+		int fd;
+
+	        if(setsid() < 0) perror("setsid");
+		if((dup2(slave, 0) < 0) || (dup2(slave, 1) < 0) || 
+		   (dup2(slave, 2) < 0)){
+			printk("start_debugger : dup2 failed, errno = %d\n",
+			       errno);
+			exit(1);
+		}
+		if(ioctl(0, TIOCSCTTY, 0) < 0){
+			printk("start_debugger : TIOCSCTTY failed, "
+			       "errno = %d\n", errno);
+			exit(1);
+		}
+		if(tcsetpgrp (1, os_getpid()) < 0){
+			printk("start_debugger : tcsetpgrp failed, "
+			       "errno = %d\n", errno);
+#ifdef notdef
+			exit(1);
+#endif
+		}
+		fd = make_tempfile("/tmp/gdb_init-XXXXXX", &tempname, 0);
+		if(fd < 0){
+			printk("start_debugger : make_tempfile failed,"
+			       "err = %d\n", -fd);
+			exit(1);
+		}
+		os_write_file(fd, gdb_init_string, sizeof(gdb_init_string) - 1);
+		if(startup){
+			if(stop){
+				os_write_file(fd, "b start_kernel\n",
+				      strlen("b start_kernel\n"));
+			}
+			os_write_file(fd, "c\n", strlen("c\n"));
+		}
+		if(ptrace(PTRACE_TRACEME, 0, 0, 0) < 0){
+			printk("start_debugger :  PTRACE_TRACEME failed, "
+			       "errno = %d\n", errno);
+			exit(1);
+		}
+		execlp("gdb", "gdb", "--command", tempname, prog, NULL);
+		printk("start_debugger : exec of gdb failed, errno = %d\n",
+		       errno);
+	}
+	if(child < 0){
+		printk("start_debugger : fork for gdb failed, errno = %d\n",
+		       errno);
+		return(-1);
+	}
+	*fd_out = slave;
+	return(child);
+}
+
+/*
+ * 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:
+ */