HPlogo HP-UX Reference Volume 3 of 5 > p

ptrace(2)

» 

Technical documentation

Complete book in PDF

 » Table of Contents

 » Index

NAME

ptrace() — process trace

SYNOPSIS

#include <sys/ptrace.h> int ptrace( int request, pid_t pid, int addr, int data, int addr2 );

Remarks

Much of the functionality of ptrace() is highly dependent on the underlying hardware. An application that uses this system call should not be expected to be portable across architectures or implementations.

DESCRIPTION

The ptrace() system call provides a means by which a process can control the execution of another process. Its primary use is for the implementation of breakpoint debugging (see adb(1)). The traced process behaves normally until it encounters a signal (see signal(2) for the list), at which time it enters a stopped state and the tracing process is notified via wait() (see wait(2)).

A traced process may also enter the stopped state without encountering a signal. This can happen if the traced process stops in response to specific events that it encounters during the course of its execution. To make this happen, the tracing process has to set specific event flags in the context of the traced process. This mechanism will be described later in greater detail.

When the traced process is in the stopped state, the tracing process can use ptrace() to examine and modify the "core image". Also, the tracing process can cause the traced process to either terminate or continue, with the possibility of ignoring the signal that caused it to stop.

To forestall possible fraud, ptrace() inhibits the set-user-ID facility on subsequent exec*() calls. If a traced process calls exec*(), it stops before executing the first instruction of the new image, showing signal SIGTRAP.

The request argument determines the precise action to be taken by ptrace(). It is one of the values described in the rest of this section.

The following request is used by the child process that will be traced.

PT_SETTRC

This request must be issued by a child process if it is to be traced by its parent. It turns on the child's trace flag, which stipulates that the child should be left in a stopped state upon receipt of a signal rather than the state specified by func (see signal(2)). The pid, addr, data, and addr2 arguments are ignored, and a return value is not defined for this request. Peculiar results occur if the parent does not expect to trace the child.

The remainder of the requests can only be used by the tracing process. For each, pid is the process ID of the process being traced, which must be in a stopped state before these requests are made. The responsibility of ensuring that the traced process is in a stopped state before a request is issued, lies with the tracing process.

PT_RDUSER

PT_RIUSER

With these requests, the word at location addr in the address space of the traced process is returned to the tracing process. If instruction (I) and data (D) space are separated, request PT_RIUSER returns a word from I space, and request PT_RDUSER returns a word from D space. If I and D space are not separated, either request produces equivalent results. The data and addr2 arguments are ignored.

These two requests fail if addr is not the start address of a word, in which case a value of -1 is returned to the tracing process and its errno is set to [EIO].

PT_RUAREA

With this request, the word at location addr in the user area of the traced process in the system's address space (see <sys/user.h>) is returned to the tracing process. Addresses in this area are system dependent, but start at zero. The limit can be derived from <sys/user.h>. The data and addr2 arguments are ignored.

This request fails if addr is not the start address of a word or is outside the user area, in which case a value of -1 is returned to the tracing process and its errno is set to [EIO].

PT_WDUSER

PT_WIUSER

With these requests, the value given by the data argument is written into the address space of the traced process at location addr. PT_WIUSER writes a word into I space, and PT_WDUSER writes a word in D space. Upon successful completion, the value written into the address space of the traced process is returned to the tracing process. The addr2 argument is ignored.

These two requests fail if addr is not the start address of a word, or if addr is a location in a pure procedure space and either another process is executing in that space or the tracing process does not have write access for the executable file corresponding to that space. Upon failure, a value of -1 is returned to the tracing process and its errno is set to [EIO].

PT_WUAREA

This request is not supported. Therefore, it returns -1, sets errno to [EIO] and does not affect the user area of the traced process.

PT_RUREGS

With this request, the word at location addr in the save_state structure at the base of the per-process kernel stack is returned to the tracing process. addr must be word-aligned and less than STACKSIZE*NBPG (see <sys/param.h> and <machine/param.h>). The save_state structure contains the registers and other information about the process. The data and addr2 arguments are ignored.

PT_WUREGS

The save_state structure at the base of the per-process kernel stack is written as it is read with request PT_RUREGS. Only a few locations can be written in this way: the general registers, most floating-point registers, a few control registers, and certain bits of the interruption processor status word. The addr2 argument is ignored.

PT_RDDATA

PT_RDTEXT

These requests are identical to PT_RDUSER and PT_RIUSER, except that the data argument specifies the number of bytes to read and the addr2 argument specifies where to store that data in the tracing process.

PT_WRDATA

PT_WRTEXT

These requests are identical to PT_WDUSER and PT_WIUSER, except that the data argument specifies the number of bytes to write and the addr2 argument specifies where to read that data in the tracing process.

PT_CONTIN

This request causes the traced process to resume execution. If the data argument is 0, all pending signals, including the one that caused the traced process to stop, are canceled before it resumes execution. If the data argument is a valid signal number, the traced process resumes execution as if it had incurred that signal, and any other pending signals are canceled. The addr2 argument is ignored.

If the addr argument is not 1, the Instruction Address Offset Queue (program counter) is loaded with the values addr and addr+4 before execution resumes. Otherwise, execution resumes from the point where it was interrupted.

Upon successful completion, the value of data is returned to the tracing process.

This request fails if data is not 0 or a valid signal number, in which case a value of -1 is returned to the tracing process and its errno is set to [EIO].

PT_EXIT

This request causes the traced process to terminate with the same consequences as exit(). The addr, data, and addr2 arguments are ignored.

PT_SINGLE

This request causes a flag to be set so that an interrupt occurs upon the completion of one machine instruction. It then executes the same steps as listed above for request PT_CONTIN. If the processor does not provide a trace bit, this request returns an error. This effectively allows single-stepping of the traced process.

Whether or not the trace bit remains set after this interrupt is a function of the hardware.

PT_ATTACH

This request stops the process identified by pid and allows the calling process to trace it. Process pid does not have to be a child of the calling process, but the effective user ID of the calling process must match the real and saved user ID of process pid unless the effective user ID of the tracing process is superuser. The calling process can use the wait() system call to wait for process pid to stop. The addr, data, and addr2 arguments are ignored.

PT_DETACH

This request detaches the traced process pid and allows it to continue its execution in the manner of PT_CONTIN.

If the addr argument is not 1, the Instruction Address Offset Queue (program counter) is loaded with the values addr and addr2.

PT_CONTIN1

This request causes the traced process to resume execution with all its pending signals intact. If the data argument is 0, the signal that caused the traced process to stop is canceled before the traced process resumes execution. If the data argument is a valid signal number, the traced process resumes execution as if it had received that signal. The addr argument must be equal to 1 for this request. The addr2 argument is ignored. Upon successful completion, the value of data is returned to the tracing process.

This request fails if data is not 0 or a valid signal number, in which case a value of -1 is returned to the tracing process and its errno is set to [EIO].

PT_SINGLE1

This request causes a flag to be set so that an interrupt occurs upon the completion of one machine instruction. It then executes the same steps as listed above for request PT_CONTIN1. If the processor does not provide a trace bit, this request returns an error. This effectively allows single stepping of the traced process.

Whether or not the trace bit remains set after this interrupt is a function of the hardware.

As noted earlier, a tracing process can set event flags in the context of the traced process to make it respond to specific events, during its execution. These events are:

PTRACE_SIGNAL

This event flag indicates that, when processing signals, the traced process needs to examine signal mask bits set in its context by the tracing process. See the ptrace_event structure description under PT_SET_EVENT_MASK for further details.

If the signal being processed has its signal mask bit set, signal processing continues as though the process were not traced. The traced process is not stopped and the tracing process is not notified of the signal. If the signal mask bit is not set for the signal being processed, the traced process is stopped and the tracing process is notified via wait() (see wait(2)).

Note that the SIGKILL signal is an exception to this rule in that it can never be unmasked; that is, it behaves as though its mask bit were always set, regardless of whether or not its mask bit is in fact set. Consequently, a SIGKILL signal cannot be used to stop a traced process.

In this respect, a SIGTRAP signal is also special in that it is specifically used to stop traced processes. A SIGTRAP signal should therefore never be masked. Setting a mask bit for SIGTRAP will result in unexpected system behavior.

PTRACE_FORK

This event flag indicates that the traced process needs to take special action when it invokes fork(). When set, both the parent and child processes stop (the child after marking itself as a traced process and adopting its parent's debugger). Both processes log the fact that they stopped in response to a PTRACE_FORK event. Further, the child's pid is logged in the parent's context, and the parent's pid is logged in the child's context. The child does not inherit its parent's event flags. See the ptrace_state structure description under PT_GET_PROCESS_STATE for further details.

PTRACE_VFORK

This event flag indicates that the traced process needs to take special action when it invokes vfork(). When set, the child process stops after marking itself as a traced process and adopting its parent's debugger. The fact that a PTRACE_VFORK event was responded to is logged in the context of both the parent and child processes. Further, the child's pid is logged in the parent's context, and the parent's pid is logged in the child's context. The child does not inherit its parent's event flags. See the ptrace_state structure description under PT_GET_PROCESS_STATE for further details. It is important to note that the warnings with respect to vfork() (see vfork(2)), continue to apply here. In particular, it needs to be remembered that, when the child process stops, its parent process is suspended, and that the child borrows the parent's memory and thread of control until a call to exec*() or an exit (either by a call to exit() or abnormally (see exec(2) and exit(2))).

PTRACE_EXEC

This event flag indicates that the traced process needs to take special action when it invokes exec*(). When set, the traced process stops after logging the fact that it stopped in response to a PTRACE_EXEC event. It also logs information pertaining to the path or file argument of exec*(). This includes a pointer to the path name string and the length of the path name string. See the ptrace_state structure description under PT_GET_PROCESS_STATE for further details.

PTRACE_EXIT

This event flag indicates that the traced process needs to take special action when it invokes exit(). When set, the traced process stops after logging the fact that it stopped in response to a PTRACE_EXIT event.

PT_SET_EVENT_MASK

This request is used by the calling process to specify event flags and signal mask values that it wants the traced process to respond to. It does so by writing the contents of the ptrace_event data structure in the user space pointed to by addr into the context of the traced process. The data argument specifies the number of bytes to be transferred. The addr2 argument is ignored.

The request fails if the number of bytes specified is less than zero or greater than the size of the ptrace_event structure, and its errno is set to [EIO].

typedef struct ptrace_event{ sigset_t pe_signals; events_t pe_set_event; } ptrace_event_t;

Event flags are set in the pe_set_event member of the ptrace_event data structure. An event flag is set when the tracing process wants the traced process to respond to a particular event. As detailed earlier, the event flags defined are PTRACE_EXEC, PTRACE_EXIT, PTRACE_FORK, PTRACE_SIGNAL, and PTRACE_VFORK. See the definition of events_t in <sys/ptrace.h> for more details.

Signal mask values are set in the pe_signals member of the ptrace_event structure. This field is qualified by a PTRACE_SIGNAL event flag being set in the pe_set_event member. Mask values set in the pe_signals member correspond to signals that need to be masked from the tracing process when received by the traced process; that is, these are signals received by the traced process that the tracing process does not want to be informed about. The pe_signals member is described by the type definition sigset_t, which is defined in <signal.h>.

PT_GET_EVENT_MASK

This request is used by the calling process to determine the event flags and signal mask values that have been set in the traced process's context by the last PT_SET_EVENT_MASK request. The data argument specifies the number bytes to be read from the context of the traced process into the ptrace_event data structure in user space pointed to by addr . The addr2 argument is ignored.

The request fails if the number of bytes requested is less than zero or greater than the size of the ptrace_event structure, and its errno is set to [EIO].

PT_GET_PROCESS_STATE

This request is used by the calling process to access state information logged by the traced process after it (the traced process) has responded to an event. The request reads data bytes of data from the traced process's context into the ptrace_state data structure in user space pointed to by addr . The addr2 argument is ignored.

The ptrace_state data structure is described in <sys/ptrace.h> and has the following members:

typedef struct ptrace_state{ events_t pe_report_event; int pe_path_len; pid_t pe_other_pid; } ptrace_state_t;

The event that the traced process responded to and stopped is logged in the pe_report_event member. One of PTRACE_EXEC, PTRACE_EXIT, PTRACE_FORK, PTRACE_SIGNAL, or PTRACE_VFORK is logged here. See the definition of events_t in <sys/ptrace.h> for more details.

If the event that the traced process responded to was PTRACE_EXEC, then the pe_path_len member provides the length of the path name string (which is the path name of the executable file) not including the null terminating character.

If the event that the traced process responded to was PTRACE_FORK or PTRACE_VFORK, then the pe_other_pid member provides the parent's pid when accessed from the child's context, and the child's pid when accessed from the parent's context.

The request fails if the number of bytes requested is less than zero or greater than the size of the ptrace_event structure and its errno is set to [EIO].

PT_GET_PROCESS_PATHNAME

If the event that the traced process responded to and stopped was PTRACE_EXEC, then this request is used by the calling process to access the path name of the executable file provided as a path or file argument to exec*(). The request reads data bytes of data of the path name string from the traced process's context into the data buffer in user space pointed to by addr . The addr2 argument is ignored. In the typical case, data is equal to the value of the pe_path_len member of the ptrace_state structure returned via the PT_GET_PROCESS_STATE request.

If the number of bytes requested is greater than zero but less than the length of the path name string, then the number of bytes requested is returned. If the number of bytes requested is greater than the length of the path name string, then the full path name string (including the null terminating character) is returned.

The request fails if the number of bytes requested is less than zero, and its errno is set to [EIO].

EXAMPLES

The following example illustrates the use of some of the ptrace() requests by a tracing process.

#include <stdio.h> #include <signal.h> #include <sys/wait.h> #include <sys/ptrace.h> #define BUFSIZ 1024 #define MAXPATH 1024 pid_t npid, cpid, pid; int status, errors=0, pathlength; ptrace_event_t *event_addr; ptrace_state_t *state_addr; char *buf_addr; size_t event_len, state_len; int filed[2]; child() { int n, bar; close(filed[1]); /* Wait for parent to write to pipe */ while ((n = read(filed[0], &bar, BUFSIZ)) == 0); /* Now the child can exec. */ if (execlp("ls", "ls", (char *)0) < 0) /* error during exec */ printf("Child: exec failed\n"); exit(0); } parent() { close(filed[0]); /* Before child does an exec, attach it and set its event flag. */ if (ptrace(PT_ATTACH,pid)) /* failed to attach process */ printf("Parent: Failed to attach child\n"); if (pid != wait(&status)) /* wait failed */ printf("Parent: attach failed with wrong wait status\n"); if (!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGTRAP)) printf("Parent: SIGTRAP didn't stop child\n"); /* * The child process has now stopped. Set its event flag indicating * that it needs to trigger on a PTRACE_EXEC event. */ event_addr->pe_set_event = PTRACE_EXEC; if (ptrace(PT_SET_EVENT_MASK, pid, event_addr, event_len)) printf("Parent: PT_SET_EVENT_MASK ptrace request failed\n"); if (pid != wait(&status)) /* wait failed */ printf("Parent: wait() failed with wrong wait status\n"); /* * Send the child a message so it can break out of the while loop. * Get it running so it can exec. */ write(filed[1], "now run", 7); if (ptrace(PT_CONTIN, pid, 1, 0) != 0) printf("Parent: failed to get child process running\n"); /* * Wait for the traced child to stop after the exec system call in * response to an exec event set in its ptrace_event structure. */ if (pid != (npid = wait(&status))) /* wait failed */ printf("Parent: wait() failed with wrong status\n"); if (!WIFSTOPPED(status)) printf("Parent: invalid wait() completion\n"); /* * Child has stopped; fetch its process state and examine state * information. */ if (ptrace(PT_GET_PROCESS_STATE, pid, state_addr, state_len) < 0) printf("Parent: PT_GET_PROCESS_STATE ptrace request failed\n"); if (pid != wait(&status)) /* wait failed */ printf("Parent: wait() failed with wrong wait status\n"); /* Check if the pathlength value returned is non-zero */ if ((pathlength = state_addr->pe_path_len) == 0) printf("Parent: zero length pathname returned\n"); /* Fetch exec'd file pathname and store it in the buffer. */ if (ptrace(PT_GET_PROCESS_PATHNAME, pid, buf_addr, (pathlength+1)) < 0){ printf("Parent: Failed to get exec pathname\n"); } else { printf("Parent: the exec pathname is %s\n", buf_addr); if (pid != wait(&status)) /* wait failed */ printf("Parent: wait() failed with wrong status\n"); } } main() { event_len = sizeof(ptrace_event_t); state_len = sizeof(ptrace_state_t); event_addr = calloc(event_len, 1); state_addr = calloc(state_len, 1); buf_addr = calloc(MAXPATH, 1); pipe(filed); switch (pid = fork()) { case -1: exit(1); case 0: child(); break; default: parent(); break; } }

ERRORS

If ptrace() fails, errno is set to one of the following values.

[EACCES]

The executable image of the process being attached resides across an interruptible NFS mount.

[EIO]

request is an illegal number.

[EIO]

The PT_SETTRC request is used with a data argument that is less than zero or not a multiple of four, or data is not word-aligned.

[EIO]

Attempting to write to a memory segment of the traced process that is not writeable, or attempting to write to page 0, or the request argument is out of range.

[EIO]

The PT_CONTIN request is being used with an invalid data argument (signal number).

[EIO]

Attempting to write to the user area via the PT_WUAREA request.

[EPERM]

The specified process cannot be attached for tracing.

[EPERM]

The process pid is already being traced or pid refers to the calling process itself.

[ESRCH]

pid identifies a process to be traced that does not exist or has not executed a ptrace() with request PT_SETTRC.

STANDARDS CONFORMANCE

ptrace(): SVID2, SVID3, XPG2

© Hewlett-Packard Development Company, L.P.