.
[glibc/history.git] / sysdeps / mach / hurd / ptrace.c
blob8f04c130f77839925b3891c8b57400264286dd8d
1 /* Process tracing interface `ptrace' for GNU Hurd.
2 Copyright (C) 1991, 92, 93, 95, 96, 97 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, write to the Free
17 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18 02111-1307 USA. */
20 #include <errno.h>
21 #include <sys/ptrace.h>
22 #include <sys/types.h>
23 #include <stdarg.h>
24 #include <hurd.h>
25 #include <hurd/signal.h>
26 #include <hurd/msg.h>
27 #include <thread_state.h>
29 /* Perform process tracing functions. REQUEST is one of the values
30 in <sys/ptrace.h>, and determines the action to be taken.
31 For all requests except PTRACE_TRACEME, PID specifies the process to be
32 traced.
34 PID and the other arguments described above for the various requests should
35 appear (those that are used for the particular request) as:
36 pid_t PID, void *ADDR, int DATA, void *ADDR2
37 after PID. */
38 int
39 ptrace (enum __ptrace_request request, ... )
41 pid_t pid;
42 void *addr, *addr2;
43 natural_t data;
44 va_list ap;
46 /* Read data from PID's address space, from ADDR for DATA bytes. */
47 error_t read_data (task_t task, vm_address_t *ourpage, vm_size_t *size)
49 /* Read the pages containing the addressed range. */
50 error_t err;
51 *size = round_page (addr + data) - trunc_page (addr);
52 err = __vm_read (task, trunc_page (addr), *size, ourpage, size);
53 return err;
56 /* Fetch the thread port for PID's user thread. */
57 error_t fetch_user_thread (task_t task, thread_t *thread)
59 thread_t threadbuf[3], *threads = threadbuf;
60 mach_msg_type_number_t nthreads = 3, i;
61 error_t err = __task_threads (task, &threads, &nthreads);
62 if (err)
63 return err;
64 if (nthreads == 0)
65 return EINVAL;
66 *thread = threads[0]; /* Assume user thread is first. */
67 for (i = 1; i < nthreads; ++i)
68 __mach_port_deallocate (__mach_task_self (), threads[i]);
69 if (threads != threadbuf)
70 __vm_deallocate (__mach_task_self (),
71 (vm_address_t) threads, nthreads * sizeof threads[0]);
72 return 0;
75 /* Fetch a thread state structure from PID and store it at ADDR. */
76 int get_regs (int flavor, mach_msg_type_number_t count)
78 error_t err;
79 task_t task = __pid2task (pid);
80 thread_t thread;
81 if (task == MACH_PORT_NULL)
82 return -1;
83 err = fetch_user_thread (task, &thread);
84 __mach_port_deallocate (__mach_task_self (), task);
85 if (!err)
86 err = __thread_get_state (thread, flavor, addr, &count);
87 __mach_port_deallocate (__mach_task_self (), thread);
88 return err ? __hurd_fail (err) : 0;
92 switch (request)
94 case PTRACE_TRACEME:
95 /* Make this process be traced. */
96 __sigfillset (&_hurdsig_traced);
97 __USEPORT (PROC, __proc_mark_traced (port));
98 break;
100 case PTRACE_CONT:
101 va_start (ap, request);
102 pid = va_arg (ap, pid_t);
103 addr = va_arg (ap, void *);
104 data = va_arg (ap, int);
105 va_end (ap);
107 /* Send a DATA signal to PID, telling it to take the signal
108 normally even if it's traced. */
109 error_t err;
110 task_t task = __pid2task (pid);
111 if (task == MACH_PORT_NULL)
112 return -1;
113 if (data == SIGKILL)
114 err = __task_terminate (task);
115 else
117 if (addr != (void *) 1)
119 /* Move the user thread's PC to ADDR. */
120 thread_t thread;
121 err = fetch_user_thread (task, &thread);
122 if (!err)
124 struct machine_thread_state state;
125 mach_msg_type_number_t count = MACHINE_THREAD_STATE_COUNT;
126 err = __thread_get_state (thread,
127 MACHINE_THREAD_STATE_FLAVOR,
128 (natural_t *) &state, &count);
129 if (!err)
131 MACHINE_THREAD_STATE_SET_PC (&state, addr);
132 err = __thread_set_state (thread,
133 MACHINE_THREAD_STATE_FLAVOR,
134 (natural_t *) &state, count);
138 __mach_port_deallocate (__mach_task_self (), thread);
140 else
141 err = 0;
143 if (! err)
144 /* Tell the process to take the signal (or just resume if 0). */
145 err = HURD_MSGPORT_RPC
146 (__USEPORT (PROC, __proc_getmsgport (port, pid, &msgport)),
147 0, 0, __msg_sig_post_untraced (msgport, data, 0, task));
149 __mach_port_deallocate (__mach_task_self (), task);
150 return err ? __hurd_fail (err) : 0;
153 case PTRACE_KILL:
154 va_start (ap, request);
155 pid = va_arg (ap, pid_t);
156 va_end (ap);
157 /* SIGKILL always just terminates the task,
158 so normal kill is just the same when traced. */
159 return kill (pid, SIGKILL);
161 case PTRACE_SINGLESTEP:
162 /* This is a machine-dependent kernel RPC on
163 machines that support it. Punt. */
164 return EOPNOTSUPP;
166 case PTRACE_ATTACH:
167 case PTRACE_DETACH:
168 va_start (ap, request);
169 pid = va_arg (ap, pid_t);
170 va_end (ap);
172 /* Tell PID to set or clear its trace bit. */
173 error_t err;
174 mach_port_t msgport;
175 task_t task = __pid2task (pid);
176 if (task == MACH_PORT_NULL)
177 return -1;
178 err = __USEPORT (PROC, __proc_getmsgport (port, pid, &msgport));
179 if (! err)
181 err = __msg_set_init_int (msgport, task, INIT_TRACEMASK,
182 request == PTRACE_DETACH ? 0 :
183 ~(sigset_t) 0);
184 if (! err)
186 if (request == PTRACE_ATTACH)
187 /* Now stop the process. */
188 err = __msg_sig_post (msgport, SIGSTOP, 0, task);
189 else
190 /* Resume the process from tracing stop. */
191 err = __msg_sig_post_untraced (msgport, 0, 0, task);
193 __mach_port_deallocate (__mach_task_self (), msgport);
195 __mach_port_deallocate (__mach_task_self (), task);
196 return err ? __hurd_fail (err) : 0;
199 case PTRACE_PEEKTEXT:
200 case PTRACE_PEEKDATA:
201 va_start (ap, request);
202 pid = va_arg (ap, pid_t);
203 addr = va_arg (ap, void *);
204 va_end (ap);
206 /* Read the page (or two pages, if the word lies on a boundary)
207 containing the addressed word. */
208 error_t err;
209 vm_address_t ourpage;
210 vm_size_t size;
211 natural_t word;
212 task_t task = __pid2task (pid);
213 if (task == MACH_PORT_NULL)
214 return -1;
215 data = sizeof word;
216 ourpage = 0;
217 size = 0;
218 err = read_data (task, &ourpage, &size);
219 __mach_port_deallocate (__mach_task_self (), task);
220 if (err)
221 return __hurd_fail (err);
222 word = *(natural_t *) ((vm_address_t) addr - trunc_page (addr)
223 + ourpage);
224 __vm_deallocate (__mach_task_self (), ourpage, size);
225 return word;
228 case PTRACE_PEEKUSER:
229 case PTRACE_POKEUSER:
230 /* U area, what's that? */
231 return EOPNOTSUPP;
233 case PTRACE_GETREGS:
234 case PTRACE_SETREGS:
235 va_start (ap, request);
236 pid = va_arg (ap, pid_t);
237 addr = va_arg (ap, void *);
238 va_end (ap);
239 return get_regs (MACHINE_THREAD_STATE_FLAVOR,
240 MACHINE_THREAD_STATE_COUNT);
242 case PTRACE_GETFPREGS:
243 case PTRACE_SETFPREGS:
244 va_start (ap, request);
245 pid = va_arg (ap, pid_t);
246 addr = va_arg (ap, void *);
247 va_end (ap);
248 #ifdef MACHINE_THREAD_FLOAT_STATE_FLAVOR
249 return get_regs (MACHINE_THREAD_FLOAT_STATE_FLAVOR,
250 MACHINE_THREAD_FLOAT_STATE_COUNT);
251 #else
252 return EOPNOTSUPP;
253 #endif
255 case PTRACE_GETFPAREGS:
256 case PTRACE_SETFPAREGS:
257 va_start (ap, request);
258 pid = va_arg (ap, pid_t);
259 addr = va_arg (ap, void *);
260 va_end (ap);
261 #ifdef MACHINE_THREAD_FPA_STATE_FLAVOR
262 return get_regs (MACHINE_THREAD_FPA_STATE_FLAVOR,
263 MACHINE_THREAD_FPA_STATE_COUNT);
264 #else
265 return EOPNOTSUPP;
266 #endif
268 case PTRACE_POKETEXT:
269 case PTRACE_POKEDATA:
270 va_start (ap, request);
271 pid = va_arg (ap, pid_t);
272 addr = va_arg (ap, void *);
273 data = va_arg (ap, int);
274 va_end (ap);
276 /* Read the page (or two pages, if the word lies on a boundary)
277 containing the addressed word. */
278 error_t err;
279 vm_address_t ourpage;
280 vm_size_t size;
281 task_t task = __pid2task (pid);
282 if (task == MACH_PORT_NULL)
283 return -1;
284 data = sizeof (natural_t);
285 ourpage = 0;
286 size = 0;
287 err = read_data (task, &ourpage, &size);
289 if (!err)
291 /* Now modify the specified word and write the page back. */
292 *(natural_t *) ((vm_address_t) addr - trunc_page (addr)
293 + ourpage) = data;
294 err = __vm_write (task, trunc_page (addr), ourpage, size);
295 __vm_deallocate (__mach_task_self (), ourpage, size);
298 __mach_port_deallocate (__mach_task_self (), task);
299 return err ? __hurd_fail (err) : 0;
302 case PTRACE_READDATA:
303 case PTRACE_READTEXT:
304 va_start (ap, request);
305 pid = va_arg (ap, pid_t);
306 addr = va_arg (ap, void *);
307 data = va_arg (ap, int);
308 addr2 = va_arg (ap, void *);
309 va_end (ap);
311 error_t err;
312 vm_address_t ourpage;
313 vm_size_t size;
314 task_t task = __pid2task (pid);
315 if (task == MACH_PORT_NULL)
316 return -1;
317 if (((vm_address_t) addr2 + data) % __vm_page_size == 0)
319 /* Perhaps we can write directly to the user's buffer. */
320 ourpage = (vm_address_t) addr2;
321 size = data;
323 else
325 ourpage = 0;
326 size = 0;
328 err = read_data (task, &ourpage, &size);
329 __mach_port_deallocate (__mach_task_self (), task);
330 if (!err && ourpage != (vm_address_t) addr2)
332 memcpy (addr2, (void *) ourpage, data);
333 __vm_deallocate (__mach_task_self (), ourpage, size);
335 return err ? __hurd_fail (err) : 0;
338 case PTRACE_WRITEDATA:
339 case PTRACE_WRITETEXT:
340 va_start (ap, request);
341 pid = va_arg (ap, pid_t);
342 addr = va_arg (ap, void *);
343 data = va_arg (ap, int);
344 addr2 = va_arg (ap, void *);
345 va_end (ap);
347 error_t err;
348 vm_address_t ourpage;
349 vm_size_t size;
350 task_t task = __pid2task (pid);
351 if (task == MACH_PORT_NULL)
352 return -1;
353 if ((vm_address_t) addr % __vm_page_size == 0 &&
354 (vm_address_t) data % __vm_page_size == 0)
356 /* Writing whole pages; can go directly from the user's buffer. */
357 ourpage = (vm_address_t) addr2;
358 size = data;
359 err = 0;
361 else
363 /* Read the task's pages and modify our own copy. */
364 ourpage = 0;
365 size = 0;
366 err = read_data (task, &ourpage, &size);
367 if (!err)
368 memcpy ((void *) ((vm_address_t) addr - trunc_page (addr)
369 + ourpage),
370 addr2,
371 data);
373 if (!err)
374 /* Write back the modified pages. */
375 err = __vm_write (task, trunc_page (addr), ourpage, size);
376 __mach_port_deallocate (__mach_task_self (), task);
377 return err ? __hurd_fail (err) : 0;
380 default:
381 errno = EINVAL;
382 return -1;
385 return 0;