#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)
#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>
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 );
/*==========================================================================*
* do_trace *
*==========================================================================*/
-#define TR_VLSIZE ((vir_bytes) sizeof(long))
-
PUBLIC int do_trace(m_ptr)
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;
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. */
+ COPYFROMPROC(T, tr_addr, (vir_bytes) &tr_data, sizeof(long));
+ m_ptr->CTL_DATA = tr_data;
+ break;
case T_GETDATA: /* return value from data space */
COPYFROMPROC(D, tr_addr, (vir_bytes) &tr_data, sizeof(long));
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);
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. */
+ COPYTOPROC(T, tr_addr, (vir_bytes) &tr_data, sizeof(long));
+ m_ptr->CTL_DATA = 0;
+ break;
case T_SETDATA: /* set value in data space */
COPYTOPROC(D, tr_addr, (vir_bytes) &tr_data, sizeof(long));
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
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 */
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;
.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
.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.
.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,
* 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
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;
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);