trace.c revision 9a5420c82a2fd81681572a2e3859ea1671c3bded
1#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4#include <errno.h>
5#include <unistd.h>
6#include <sys/types.h>
7#include <sys/wait.h>
8#include "ptrace.h"
9#include <asm/unistd.h>
10#include <assert.h>
11
12#include "common.h"
13
14/* If the system headers did not provide the constants, hard-code the normal
15   values.  */
16#ifndef PTRACE_EVENT_FORK
17
18#define PTRACE_OLDSETOPTIONS    21
19#define PTRACE_SETOPTIONS       0x4200
20#define PTRACE_GETEVENTMSG      0x4201
21
22/* options set using PTRACE_SETOPTIONS */
23#define PTRACE_O_TRACESYSGOOD   0x00000001
24#define PTRACE_O_TRACEFORK      0x00000002
25#define PTRACE_O_TRACEVFORK     0x00000004
26#define PTRACE_O_TRACECLONE     0x00000008
27#define PTRACE_O_TRACEEXEC      0x00000010
28#define PTRACE_O_TRACEVFORKDONE 0x00000020
29#define PTRACE_O_TRACEEXIT      0x00000040
30
31/* Wait extended result codes for the above trace options.  */
32#define PTRACE_EVENT_FORK       1
33#define PTRACE_EVENT_VFORK      2
34#define PTRACE_EVENT_CLONE      3
35#define PTRACE_EVENT_EXEC       4
36#define PTRACE_EVENT_VFORK_DONE 5
37#define PTRACE_EVENT_EXIT       6
38
39#endif /* PTRACE_EVENT_FORK */
40
41#ifdef ARCH_HAVE_UMOVELONG
42extern int arch_umovelong (Process *, void *, long *, arg_type_info *);
43int
44umovelong (Process *proc, void *addr, long *result, arg_type_info *info) {
45	return arch_umovelong (proc, addr, result, info);
46}
47#else
48/* Read a single long from the process's memory address 'addr' */
49int
50umovelong (Process *proc, void *addr, long *result, arg_type_info *info) {
51	long pointed_to;
52
53	errno = 0;
54	pointed_to = ptrace (PTRACE_PEEKTEXT, proc->pid, addr, 0);
55	if (pointed_to == -1 && errno)
56		return -errno;
57
58	*result = pointed_to;
59	if (info) {
60		switch(info->type) {
61			case ARGTYPE_INT:
62				*result &= 0x00000000ffffffffUL;
63			default:
64				break;
65		};
66	}
67	return 0;
68}
69#endif
70
71void
72trace_me(void) {
73	debug(DEBUG_PROCESS, "trace_me: pid=%d", getpid());
74	if (ptrace(PTRACE_TRACEME, 0, 1, 0) < 0) {
75		perror("PTRACE_TRACEME");
76		exit(1);
77	}
78}
79
80int
81trace_pid(pid_t pid) {
82	debug(DEBUG_PROCESS, "trace_pid: pid=%d", pid);
83	if (ptrace(PTRACE_ATTACH, pid, 1, 0) < 0) {
84		return -1;
85	}
86
87	/* man ptrace: PTRACE_ATTACH attaches to the process specified
88	   in pid.  The child is sent a SIGSTOP, but will not
89	   necessarily have stopped by the completion of this call;
90	   use wait() to wait for the child to stop. */
91	if (waitpid (pid, NULL, __WALL) != pid) {
92		perror ("trace_pid: waitpid");
93		return -1;
94	}
95
96	return 0;
97}
98
99void
100trace_set_options(Process *proc, pid_t pid) {
101	if (proc->tracesysgood & 0x80)
102		return;
103
104	debug(DEBUG_PROCESS, "trace_set_options: pid=%d", pid);
105
106	long options = PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEFORK |
107		PTRACE_O_TRACEVFORK | PTRACE_O_TRACECLONE |
108		PTRACE_O_TRACEEXEC;
109	if (ptrace(PTRACE_SETOPTIONS, pid, 0, options) < 0 &&
110	    ptrace(PTRACE_OLDSETOPTIONS, pid, 0, options) < 0) {
111		perror("PTRACE_SETOPTIONS");
112		return;
113	}
114	proc->tracesysgood |= 0x80;
115}
116
117void
118untrace_pid(pid_t pid) {
119	debug(DEBUG_PROCESS, "untrace_pid: pid=%d", pid);
120	ptrace(PTRACE_DETACH, pid, 1, 0);
121}
122
123void
124continue_after_signal(pid_t pid, int signum) {
125	Process *proc;
126
127	debug(DEBUG_PROCESS, "continue_after_signal: pid=%d, signum=%d", pid, signum);
128
129	proc = pid2proc(pid);
130	if (proc && proc->breakpoint_being_enabled) {
131#if defined __sparc__  || defined __ia64___ || defined __mips__
132		ptrace(PTRACE_SYSCALL, pid, 0, signum);
133#else
134		ptrace(PTRACE_SINGLESTEP, pid, 0, signum);
135#endif
136	} else {
137		ptrace(PTRACE_SYSCALL, pid, 0, signum);
138	}
139}
140
141void
142continue_process(pid_t pid) {
143	/* We always trace syscalls to control fork(), clone(), execve()... */
144
145	debug(DEBUG_PROCESS, "continue_process: pid=%d", pid);
146
147	ptrace(PTRACE_SYSCALL, pid, 0, 0);
148}
149
150void
151continue_enabling_breakpoint(Process * proc, Breakpoint *sbp)
152{
153	enable_breakpoint(proc, sbp);
154	continue_process(proc->pid);
155}
156
157void
158continue_after_breakpoint(Process *proc, Breakpoint *sbp)
159{
160	if (sbp->enabled)
161		disable_breakpoint(proc, sbp);
162
163	set_instruction_pointer(proc, sbp->addr);
164	if (sbp->enabled == 0) {
165		continue_process(proc->pid);
166	} else {
167		debug(DEBUG_PROCESS,
168		      "continue_after_breakpoint: pid=%d, addr=%p",
169		      proc->pid, sbp->addr);
170		proc->breakpoint_being_enabled = sbp;
171#if defined __sparc__  || defined __ia64___ || defined __mips__
172		/* we don't want to singlestep here */
173		continue_process(proc->pid);
174#else
175		ptrace(PTRACE_SINGLESTEP, proc->pid, 0, 0);
176#endif
177	}
178}
179
180size_t
181umovebytes(Process *proc, void *addr, void *laddr, size_t len) {
182
183	union {
184		long a;
185		char c[sizeof(long)];
186	} a;
187	int started = 0;
188	size_t offset = 0, bytes_read = 0;
189
190	while (offset < len) {
191		a.a = ptrace(PTRACE_PEEKTEXT, proc->pid, addr + offset, 0);
192		if (a.a == -1 && errno) {
193			if (started && errno == EIO)
194				return bytes_read;
195			else
196				return -1;
197		}
198		started = 1;
199
200		if (len - offset >= sizeof(long)) {
201			memcpy(laddr + offset, &a.c[0], sizeof(long));
202			bytes_read += sizeof(long);
203		}
204		else {
205			memcpy(laddr + offset, &a.c[0], len - offset);
206			bytes_read += (len - offset);
207		}
208		offset += sizeof(long);
209	}
210
211	return bytes_read;
212}
213
214/* Read a series of bytes starting at the process's memory address
215   'addr' and continuing until a NUL ('\0') is seen or 'len' bytes
216   have been read.
217*/
218int
219umovestr(Process *proc, void *addr, int len, void *laddr) {
220	union {
221		long a;
222		char c[sizeof(long)];
223	} a;
224	unsigned i;
225	int offset = 0;
226
227	while (offset < len) {
228		a.a = ptrace(PTRACE_PEEKTEXT, proc->pid, addr + offset, 0);
229		for (i = 0; i < sizeof(long); i++) {
230			if (a.c[i] && offset + (signed)i < len) {
231				*(char *)(laddr + offset + i) = a.c[i];
232			} else {
233				*(char *)(laddr + offset + i) = '\0';
234				return 0;
235			}
236		}
237		offset += sizeof(long);
238	}
239	*(char *)(laddr + offset) = '\0';
240	return 0;
241}
242