home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Geek Gadgets 1
/
ADE-1.bin
/
ade-dist
/
ixemul-45.0-src.tgz
/
tar.out
/
contrib
/
ixemul
/
library
/
ptrace.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-09-28
|
13KB
|
474 lines
/*-
* Copyright (c) 1995 Leonard Norrgard. All rights reserved.
* Copyright (c) 1994 Christopher G. Demetriou. All rights reserved.
* Copyright (c) 1982, 1986, 1989, 1993
* The Regents of the University of California. All rights reserved.
* (c) UNIX System Laboratories, Inc.
* All or some portions of this file are derived from material licensed
* to the University of California by American Telephone and Telegraph
* Co. or Unix System Laboratories, Inc. and are reproduced herein with
* the permission of UNIX System Laboratories, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* from: @(#)sys_process.c 8.1 (Berkeley) 6/10/93
*/
/*
* References:
* (1) Bach's "The Design of the UNIX Operating System",
* (2) sys/miscfs/procfs from UCB's 4.4BSD-Lite distribution,
* (3) the "4.4BSD Programmer's Reference Manual" published
* by USENIX and O'Reilly & Associates.
* The 4.4BSD PRM does a reasonably good job of documenting what the various
* ptrace() requests should actually do, and its text is quoted several times
* in this file.
*/
#define _KERNEL
#include "ixemul.h"
#include "kprintf.h"
#include <signal.h>
#include <string.h>
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <exec/execbase.h>
int process_read_regs (struct user *p, struct reg *regs)
{
if (p->u_regs == NULL)
{
bzero(regs, sizeof(struct reg));
errno = EIO;
return -1;
}
bcopy (p->u_regs, regs, sizeof (struct reg));
return 0;
}
int process_write_regs (struct user *p, struct reg *regs)
{
if (p->u_regs == NULL)
{
errno = EIO;
return -1;
}
bcopy (regs, p->u_regs, sizeof (struct reg));
return 0;
}
int process_read_fpregs (struct user *p, struct fpreg *fpregs)
{
if (p->u_fpregs == NULL)
{
bzero(fpregs, sizeof(struct fpreg));
errno = EIO;
return -1;
}
bcopy (p->u_fpregs, fpregs, sizeof (struct fpreg));
return 0;
}
int process_write_fpregs (struct user *p, struct fpreg *fpregs)
{
if (p->u_fpregs == NULL)
{
errno = EIO;
return -1;
}
bcopy (fpregs, p->u_fpregs, sizeof (struct fpreg));
return 0;
}
int process_sstep (struct user *t, int sstep)
{
if (sstep)
if (t->u_regs == NULL)
{
errno = EIO;
return -1;
}
else
t->u_regs->r_sr |= 0x8000;
else if (t->u_regs)
t->u_regs->r_sr &= ~0x8000;
return 0;
}
int process_set_pc (struct user *t, caddr_t addr)
{
if (t->u_regs)
{
t->u_regs->r_pc = addr;
return 0;
}
errno = EIO;
return -1;
}
int ptrace (int request, pid_t pid, caddr_t addr, int data)
{
struct Task *task, *me = FindTask(0);
struct user *t, *p;
int step;
int error;
/* Find the user area of this process. */
p = me->tc_TrapData;
if (request == PT_GETIXINFO)
{
struct small_ixnet_base {
struct Library ixnet_lib;
unsigned char ix_myflags;
unsigned char ix_pad;
BPTR ix_seg_list;
};
static struct ixinfo info;
extern void sig_trampoline();
extern void sig_launch();
extern void install_vector(); /* trap.s */
extern void restore_vector(); /* trap.s */
info.version = 0;
info.ixemul_seglist = ix.ix_seg_list;
info.ixnet_seglist = (u.u_ixnetbase ? ((struct small_ixnet_base *)(u.u_ixnetbase))->ix_seg_list : NULL);
info.sigtramp_start = (long)sig_trampoline;
info.sigtramp_end = (long)sig_launch;
if (betterthan68010())
{
info.install_vector = install_vector;
info.restore_vector = restore_vector;
}
else
{
info.install_vector = NULL;
info.restore_vector = NULL;
}
return (int)&info;
}
else if (request == PT_TRACE_ME)
task = me;
else if ((request == PT_ATTACH))
{
/* have to check if the task really exists */
if (pid == 0 || (task = pfind(pid)) == NULL)
{
errno = ESRCH;
return -1;
}
}
else
task = (struct Task *) pid;
/* Temporarily until I'm convinced it all works. It makes it also easier
to debug gdb. */
#if 0
{
char *req;
switch (request)
{
case PT_TRACE_ME: req = "PT_TRACE_ME"; break;
case PT_READ_I: req = "PT_READ_I"; break;
case PT_READ_D: req = "PT_READ_D"; break;
case PT_READ_U: req = "PT_READ_U"; break;
case PT_WRITE_I: req = "PT_WRITE_I"; break;
case PT_WRITE_D: req = "PT_WRITE_D"; break;
case PT_WRITE_U: req = "PT_WRITE_U"; break;
case PT_CONTINUE: req = "PT_CONTINUE"; break;
case PT_KILL: req = "PT_KILL"; break;
case PT_STEP: req = "PT_STEP"; break;
case PT_GETSEGS: req = "PT_GETSEGS"; break;
case PT_GETIXINFO: req = "PT_GETIXINFO"; break;
case PT_GETREGS: req = "PT_GETREGS"; break;
case PT_SETREGS: req = "PT_SETREGS"; break;
case PT_GETEXENAME: req = "PT_GETEXENAME";break;
case PT_GETA4: req = "PT_GETA4"; break;
case PT_GETFPREGS: req = "PT_GETFPREGS"; break;
case PT_SETFPREGS: req = "PT_SETFPREGS"; break;
case PT_ATTACH: req = "PT_ATTACH"; break;
case PT_DETACH: req = "PT_DETACH"; break;
default: req = "*Unknown request*"; break;
}
switch (request)
{
case PT_READ_I: break;
case PT_READ_D: break;
case PT_READ_U: break;
case PT_WRITE_I: break;
case PT_WRITE_D: break;
case PT_WRITE_U: break;
default:break;
case PT_KILL:
case PT_CONTINUE:
case PT_STEP:
KPrintF("ptrace (%s, pid=%lx, addr=%lx , data=%lx);\n",
req, pid, addr, data);
}
}
#endif
/* sanity check */
if (task == NULL || (t = (struct user *) task->tc_TrapData) == NULL)
{
errno = ESRCH;
return -1;
}
/* Check that the arguments are valid. */
switch (request)
{
case PT_TRACE_ME:
/* Saying that you're being traced is always OK. */
break;
case PT_ATTACH:
/* You can't attach to a process if:
(1) it's the process that's doing the attaching or */
if (t == FindTask (0)->tc_TrapData)
{
errno = EPERM;
return -1;
}
/* (2) it's already being traced. */
if (t->p_flag & STRC)
{
errno = EPERM;
return -1;
}
break;
case PT_READ_I:
case PT_READ_D:
case PT_WRITE_I:
case PT_WRITE_D:
case PT_CONTINUE:
case PT_KILL:
case PT_DETACH:
case PT_GETFPREGS:
case PT_SETFPREGS:
/* You can't do what you want to the process if: */
/* (1) It's not being traced at all, */
if (!(t->p_flag & STRC))
{
errno = EPERM;
return -1;
}
/* (2) it's not being traced by _you_, or */
if (t->p_pptr->pr_Task.tc_TrapData != p)
{
errno = EPERM;
return -1;
}
/* (3) it's not currently stopped. */
if (t->p_stat != SSTOP
|| !(t->p_flag & SWTED))
{
errno = EPERM;
return -1;
}
break;
case PT_STEP:
case PT_GETREGS:
case PT_SETREGS:
case PT_GETSEGS: /* you can always do this */
case PT_GETEXENAME: /* you can always do this */
case PT_GETA4: /* you can always do this */
break;
default:
/* It was not a valid request. */
errno = EIO;
return -1;
}
/* Now actually do the job. */
step = 0;
switch (request)
{
case PT_TRACE_ME:
/* Child declares it's being traced, just set the trace flag. */
t->p_flag |= STRC;
break;
case PT_READ_I:
case PT_READ_D:
/* Check whether this is valid memory */
if (((int)addr & 1) || addr == 0 || ((TypeOfMem(addr)) == 0))
{
errno = EIO;
return -1;
}
return *((int *)addr);
case PT_WRITE_I:
case PT_WRITE_D:
/* Check whether this is valid memory */
if (((int)addr & 1) || addr == 0 || ((TypeOfMem(addr)) == 0))
{
errno = EIO;
return -1;
}
*((int *)addr) = data;
CacheClearE(addr, 4, CACRF_ClearI | CACRF_ClearD);
return 0;
case PT_GETSEGS:
return (t->u_segs ? (int)t->u_segs->segment : 0);
case PT_GETEXENAME:
return (t->u_segs ? (int)t->u_segs->name : 0);
case PT_GETA4:
return t->u_a4;
/* case PT_READ_U: fixme */
/* case PT_WRITE_U: fixme */
case PT_STEP:
/* From the 4.4BSD PRM:
"Execution continues as in request PT_CONTINUE; however
as soon as possible after execution of at least one
instruction, execution stops again. [ ... ]" */
step = 1;
/* fallthrough */
case PT_CONTINUE:
/* From the 4.4BSD PRM:
"The data argument is taken as a signal number and the
child's execution continues at location addr as if it
incurred that signal. Normally the signal number will
be either 0 to indicate that the signal that caused the
stop should be ignored, or that value fetched out of
the process's image indicating which signal caused
the stop. If addr is (int *)1 then execution continues
from where it stopped." */
/* step = 0 done above. */
/* Check that data is a valid signal number or zero. */
if (data < 0 || data >= NSIG)
{
errno = EIO;
return -1;
}
/* Arrange for a single-step, if that's requested and possible. */
if ((error = process_sstep (t, step)))
return error;
/* If the address parameter is not (int *)1, set the pc. */
if ((int *)addr != (int *)1)
if ((error = process_set_pc (t, addr)))
return error;
/* Finally, deliver the requested signal (or none). */
sendsig:
t->p_xstat = data;
setrun (task);
return 0;
case PT_KILL:
/* not being traced any more */
t->p_flag &= ~STRC;
/* Just send the process a KILL signal. */
data = SIGKILL;
goto sendsig;
case PT_GETREGS:
return process_read_regs (t, (struct reg *)addr);
case PT_SETREGS:
return process_write_regs (t, (struct reg *)addr);
case PT_GETFPREGS:
return process_read_fpregs (t, (struct fpreg *)addr);
case PT_SETFPREGS:
return process_write_fpregs (t, (struct fpreg *)addr);
case PT_ATTACH:
/*
* Go ahead and set the trace flag.
* Save the old parent (it's reset in
* _DETACH, and also in vfork.c:wait4()
* Reparent the process so that the tracing
* proc gets to see all the action.
* Stop the target.
*/
t->p_flag |= STRC;
t->p_xstat = 0; /* XXX ? */
if (t->p_pptr != (struct Process *)me) {
t->p_opptr = t->p_pptr;
proc_reparent((struct Process *)task, (struct Process *)me);
}
_psignal(task, SIGSTOP);
return (0);
case PT_DETACH:
/* not being traced any more */
t->p_flag &= ~STRC;
/* give process back to original parent */
if (t->p_opptr != t->p_pptr)
{
if (t->p_opptr && pfind((pid_t)t->p_opptr))
proc_reparent((struct Process *)task, t->p_opptr);
}
t->p_opptr = NULL;
t->p_flag &= ~SWTED;
/* and deliver any signal requested by tracer. */
if (t->p_stat == SSTOP)
goto sendsig;
else if (data)
_psignal(task, data);
return (0);
default:
/* Unknown request. */
errno = EIO;
return -1;
}
return 0; /* correct return value? */
}