ptrace(2) modifications:
- add T_GETRANGE/T_SETRANGE to get/set ranges of values - change EIO error code to EFAULT - move common-I&D text-to-data translation to umap_local
This commit is contained in:
parent
8da928d2df
commit
e423c86009
5 changed files with 118 additions and 32 deletions
|
@ -21,6 +21,8 @@
|
|||
#define T_ATTACH 11 /* attach to a running process */
|
||||
#define T_DETACH 12 /* detach from a traced process */
|
||||
#define T_SETOPT 13 /* set trace options */
|
||||
#define T_GETRANGE 14 /* get range of values */
|
||||
#define T_SETRANGE 15 /* set range of values */
|
||||
|
||||
#define T_READB_INS 100 /* Read a byte from the text segment of an
|
||||
* untraced process (only for root)
|
||||
|
@ -33,6 +35,18 @@
|
|||
#define TO_TRACEFORK 0x1 /* automatically attach to forked children */
|
||||
#define TO_ALTEXEC 0x2 /* send SIGSTOP on successful exec() */
|
||||
|
||||
/* Trace spaces. */
|
||||
#define TS_INS 0 /* text space */
|
||||
#define TS_DATA 1 /* data space */
|
||||
|
||||
/* Trance range structure. */
|
||||
struct ptrace_range {
|
||||
int pr_space; /* space in traced process */
|
||||
long pr_addr; /* address in traced process */
|
||||
void *pr_ptr; /* buffer in caller process */
|
||||
size_t pr_size; /* size of range, in bytes */
|
||||
};
|
||||
|
||||
/* Function Prototypes. */
|
||||
#ifndef _ANSI_H
|
||||
#include <ansi.h>
|
||||
|
|
|
@ -397,6 +397,8 @@ vir_bytes bytes; /* # of bytes to be copied */
|
|||
|
||||
if (seg != T)
|
||||
seg = (vc < rp->p_memmap[D].mem_vir + rp->p_memmap[D].mem_len ? D : S);
|
||||
else if (rp->p_memmap[T].mem_len == 0) /* common I&D? */
|
||||
seg = D; /* ptrace needs this */
|
||||
|
||||
if ((vir_addr>>CLICK_SHIFT) >= rp->p_memmap[seg].mem_vir +
|
||||
rp->p_memmap[seg].mem_len) return( (phys_bytes) 0 );
|
||||
|
|
|
@ -16,8 +16,6 @@
|
|||
/*==========================================================================*
|
||||
* do_trace *
|
||||
*==========================================================================*/
|
||||
#define TR_VLSIZE ((vir_bytes) sizeof(long))
|
||||
|
||||
PUBLIC int do_trace(m_ptr)
|
||||
register message *m_ptr;
|
||||
{
|
||||
|
@ -28,16 +26,22 @@ register message *m_ptr;
|
|||
* T_GETINS return value from instruction space
|
||||
* T_GETDATA return value from data space
|
||||
* T_GETUSER return value from user process table
|
||||
* T_SETINS set value from instruction space
|
||||
* T_SETDATA set value from data space
|
||||
* T_SETINS set value in instruction space
|
||||
* T_SETDATA set value in data space
|
||||
* T_SETUSER set value in user process table
|
||||
* T_RESUME resume execution
|
||||
* T_EXIT exit
|
||||
* T_STEP set trace bit
|
||||
* T_SYSCALL trace system call
|
||||
* T_ATTACH attach to an existing process
|
||||
* T_DETACH detach from a traced process
|
||||
* T_SETOPT set trace options
|
||||
* T_GETRANGE get range of values
|
||||
* T_SETRANGE set range of values
|
||||
*
|
||||
* The T_OK and T_EXIT commands are handled completely by the process manager,
|
||||
* all others come here.
|
||||
* The T_OK, T_ATTACH, T_EXIT, and T_SETOPT commands are handled completely by
|
||||
* the process manager. T_GETRANGE and T_SETRANGE use sys_vircopy(). All others
|
||||
* come here.
|
||||
*/
|
||||
|
||||
register struct proc *rp;
|
||||
|
@ -91,12 +95,9 @@ register message *m_ptr;
|
|||
return(OK);
|
||||
|
||||
case T_GETINS: /* return value from instruction space */
|
||||
if (rp->p_memmap[T].mem_len != 0) {
|
||||
COPYFROMPROC(T, tr_addr, (vir_bytes) &tr_data, sizeof(long));
|
||||
m_ptr->CTL_DATA = tr_data;
|
||||
break;
|
||||
}
|
||||
/* Text space is actually data space - fall through. */
|
||||
|
||||
case T_GETDATA: /* return value from data space */
|
||||
COPYFROMPROC(D, tr_addr, (vir_bytes) &tr_data, sizeof(long));
|
||||
|
@ -104,7 +105,7 @@ register message *m_ptr;
|
|||
break;
|
||||
|
||||
case T_GETUSER: /* return value from process table */
|
||||
if ((tr_addr & (sizeof(long) - 1)) != 0) return(EIO);
|
||||
if ((tr_addr & (sizeof(long) - 1)) != 0) return(EFAULT);
|
||||
|
||||
if (tr_addr <= sizeof(struct proc) - sizeof(long)) {
|
||||
m_ptr->CTL_DATA = *(long *) ((char *) rp + (int) tr_addr);
|
||||
|
@ -117,18 +118,15 @@ register message *m_ptr;
|
|||
i = sizeof(long) - 1;
|
||||
tr_addr -= (sizeof(struct proc) + i) & ~i;
|
||||
|
||||
if (tr_addr > sizeof(struct priv) - sizeof(long)) return(EIO);
|
||||
if (tr_addr > sizeof(struct priv) - sizeof(long)) return(EFAULT);
|
||||
|
||||
m_ptr->CTL_DATA = *(long *) ((char *) rp->p_priv + (int) tr_addr);
|
||||
break;
|
||||
|
||||
case T_SETINS: /* set value in instruction space */
|
||||
if (rp->p_memmap[T].mem_len != 0) {
|
||||
COPYTOPROC(T, tr_addr, (vir_bytes) &tr_data, sizeof(long));
|
||||
m_ptr->CTL_DATA = 0;
|
||||
break;
|
||||
}
|
||||
/* Text space is actually data space - fall through. */
|
||||
|
||||
case T_SETDATA: /* set value in data space */
|
||||
COPYTOPROC(D, tr_addr, (vir_bytes) &tr_data, sizeof(long));
|
||||
|
@ -138,7 +136,7 @@ register message *m_ptr;
|
|||
case T_SETUSER: /* set value in process table */
|
||||
if ((tr_addr & (sizeof(reg_t) - 1)) != 0 ||
|
||||
tr_addr > sizeof(struct stackframe_s) - sizeof(reg_t))
|
||||
return(EIO);
|
||||
return(EFAULT);
|
||||
i = (int) tr_addr;
|
||||
#if (_MINIX_CHIP == _CHIP_INTEL)
|
||||
/* Altering segment registers might crash the kernel when it
|
||||
|
@ -153,7 +151,7 @@ register message *m_ptr;
|
|||
i == (int) &((struct proc *) 0)->p_reg.fs ||
|
||||
#endif
|
||||
i == (int) &((struct proc *) 0)->p_reg.ss)
|
||||
return(EIO);
|
||||
return(EFAULT);
|
||||
#endif
|
||||
if (i == (int) &((struct proc *) 0)->p_reg.psw)
|
||||
/* only selected bits are changeable */
|
||||
|
@ -185,12 +183,13 @@ register message *m_ptr;
|
|||
break;
|
||||
|
||||
case T_READB_INS: /* get value from instruction space */
|
||||
COPYFROMPROC(rp->p_memmap[T].mem_len > 0 ? T : D, tr_addr, (vir_bytes) &ub, 1);
|
||||
COPYFROMPROC(T, tr_addr, (vir_bytes) &ub, 1);
|
||||
m_ptr->CTL_DATA = ub;
|
||||
break;
|
||||
|
||||
case T_WRITEB_INS: /* set value in instruction space */
|
||||
COPYTOPROC(rp->p_memmap[T].mem_len > 0 ? T : D,tr_addr, (vir_bytes) &tr_data, 1);
|
||||
ub = (unsigned char) (tr_data & 0xff);
|
||||
COPYTOPROC(T, tr_addr, (vir_bytes) &ub, 1);
|
||||
m_ptr->CTL_DATA = 0;
|
||||
break;
|
||||
|
||||
|
|
|
@ -94,6 +94,16 @@ interpreted as an additional signal to pass to the process.
|
|||
.B T_SETOPT
|
||||
Set the given process's trace options to the bit combination of flags given
|
||||
in the \fIdata\fP argument.
|
||||
.TP
|
||||
.B T_GETRANGE
|
||||
Get a range of values from the address space of the traced process. The
|
||||
\fIaddr\fP argument must be a pointer to a fully initialized
|
||||
\fBstruct ptrace_range\fP structure.
|
||||
.TP
|
||||
.B T_SETRANGE
|
||||
Set a range of values in the address space of the traced process. The
|
||||
\fIaddr\fP argument must be a pointer to a fully initialized
|
||||
\fBstruct ptrace_range\fP structure.
|
||||
.PP
|
||||
The following option flags are currently supported for \fBT_SETOPT\fP:
|
||||
.TP 2
|
||||
|
@ -108,22 +118,56 @@ Send \fBSIGSTOP\fP instead of \fBSIGTRAP\fP upon a successful
|
|||
.BR execve (2).
|
||||
This allows the tracer to disambiguate between this case and other traps.
|
||||
.PP
|
||||
All addresses specified for the \fBT_GET\fP* and \fBT_SET\fP* requests must be
|
||||
The \fBT_GETRANGE\fP and \fBT_SETRANGE\fP calls use the following structure:
|
||||
.PP
|
||||
.RS
|
||||
.nf
|
||||
.ft B
|
||||
.ta +4n +8n
|
||||
struct ptrace_range {
|
||||
int pr_space;
|
||||
long pr_addr;
|
||||
size_t pr_size;
|
||||
void *pr_ptr;
|
||||
};
|
||||
.RE
|
||||
.PP
|
||||
The \fBpr_space\fP field specifies the address space from which to retrieve
|
||||
or set the values. It must be set to either of the following values:
|
||||
.PP
|
||||
.TP 10
|
||||
.B TS_INS
|
||||
Text space.
|
||||
.TP
|
||||
.B TS_DATA
|
||||
Data space.
|
||||
.PP
|
||||
The \fBpr_addr\fP field specifies the start address of the target area in the
|
||||
traced process. The \fBpr_size\fP field specifies the number of bytes to
|
||||
retrieve or set, and must be between 1 and LONG_MAX. The \fBpr_ptr\fP field
|
||||
must point to a buffer in the calling process that is used to store the
|
||||
retrieved values (for \fBT_GETRANGE\fP) or contains the values to set (for
|
||||
\fBT_SETRANGE\fP).
|
||||
.PP
|
||||
All addresses specified for the \fBT_GETINS\fP, \fBT_GETDATA\fP,
|
||||
\fBT_GETUSER\fP requests and their \fBT_SET\fP* counterparts must be
|
||||
aligned on \fBlong\fP boundary. Similarly, only \fBlong\fP sized values can be
|
||||
retrieved and set at a time.
|
||||
.SH "RETURN VALUE"
|
||||
All but the \fBT_GET\fP* requests return 0 upon successful completion.
|
||||
All but the \fBT_GETINS\fP, \fBT_GETDATA\fP, \fBT_GETUSER\fP requests return 0
|
||||
upon successful completion.
|
||||
Otherwise, a value of -1 is returned and \fIerrno\fP is set to indicate the
|
||||
error.
|
||||
.PP
|
||||
The \fBT_GET\fP* requests return the resulting data. Here, -1 is a legitimate
|
||||
return value. To distinguish between this and an error, clear \fIerrno\fP
|
||||
The \fBT_GETINS\fP, \fBT_GETDATA\fP, \fBT_GETUSER\fP requests return the
|
||||
resulting data. Here, -1 is a legitimate return value.
|
||||
To distinguish between this and an error, clear \fIerrno\fP
|
||||
before the \fBptrace\fP call, and check whether it is zero afterwards.
|
||||
.SH ERRORS
|
||||
The functions will fail if any of the following errors occur:
|
||||
.TP 10
|
||||
.B EINVAL
|
||||
Invalid request or signal given.
|
||||
Invalid request, signal, space, or length given.
|
||||
.TP 10
|
||||
.B ESRCH
|
||||
The given process is not found, exiting, or not traced by the caller.
|
||||
|
@ -131,8 +175,8 @@ The given process is not found, exiting, or not traced by the caller.
|
|||
.B EBUSY
|
||||
The given process is not stopped, or already being traced.
|
||||
.TP 10
|
||||
.B EIO
|
||||
The given address is out of range or not properly aligned.
|
||||
.B EFAULT
|
||||
The given address is invalid, inaccessible, or not properly aligned.
|
||||
.TP 10
|
||||
.B EPERM
|
||||
Attaching is denied, because the caller equals the given process,
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
* T_ATTACH attach to an existing process
|
||||
* T_DETACH detach from a traced process
|
||||
* T_SETOPT set trace options
|
||||
* T_GETRANGE get range of values
|
||||
* T_SETRANGE set range of values
|
||||
*
|
||||
* The T_OK, T_ATTACH, T_EXIT, and T_SETOPT commands are handled here, and the
|
||||
* T_RESUME, T_STEP, T_SYSCALL, and T_DETACH commands are partially handled
|
||||
|
@ -38,7 +40,8 @@
|
|||
PUBLIC int do_trace()
|
||||
{
|
||||
register struct mproc *child;
|
||||
int i, r, req;
|
||||
struct ptrace_range pr;
|
||||
int i, r, seg, req;
|
||||
|
||||
req = m_in.request;
|
||||
|
||||
|
@ -155,6 +158,30 @@ PUBLIC int do_trace()
|
|||
mp->mp_reply.reply_trace = 0;
|
||||
return(OK);
|
||||
|
||||
case T_GETRANGE:
|
||||
case T_SETRANGE: /* get/set range of values */
|
||||
r = sys_datacopy(who_e, (vir_bytes) m_in.PMTRACE_ADDR,
|
||||
SELF, (vir_bytes) &pr, (phys_bytes) sizeof(pr));
|
||||
if (r != OK) return(r);
|
||||
|
||||
if (pr.pr_space != TS_INS && pr.pr_space != TS_DATA) return(EINVAL);
|
||||
if (pr.pr_size == 0 || pr.pr_size > LONG_MAX) return(EINVAL);
|
||||
|
||||
seg = (pr.pr_space == TS_INS) ? T : D;
|
||||
if (req == T_GETRANGE)
|
||||
r = sys_vircopy(child->mp_endpoint, seg, (vir_bytes) pr.pr_addr,
|
||||
who_e, D, (vir_bytes) pr.pr_ptr,
|
||||
(phys_bytes) pr.pr_size);
|
||||
else
|
||||
r = sys_vircopy(who_e, D, (vir_bytes) pr.pr_ptr,
|
||||
child->mp_endpoint, seg, (vir_bytes) pr.pr_addr,
|
||||
(phys_bytes) pr.pr_size);
|
||||
|
||||
if (r != OK) return(r);
|
||||
|
||||
mp->mp_reply.reply_trace = 0;
|
||||
return(OK);
|
||||
|
||||
case T_DETACH: /* detach from traced process */
|
||||
if (m_in.data < 0 || m_in.data >= _NSIG) return(EINVAL);
|
||||
|
||||
|
|
Loading…
Reference in a new issue