/**********************************************************************proxy.c Copyright (C) 1999 Lars Brinkhoff. See the file COPYING for licensingterms 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: */
/**********************************************************************
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:
*/