Rewrote ID frobnication to do the right thing.
[glibc/history.git] / sysdeps / mach / hurd / ptrace.c
blobf68d3b41ed494e7dab6e65da2a1f779ca3f3d806
1 /* Process tracing interface `ptrace' for GNU Hurd.
2 Copyright (C) 1991, 1992, 1993, 1995 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 Library General Public License as
7 published by the Free Software Foundation; either version 2 of the
8 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 Library General Public License for more details.
15 You should have received a copy of the GNU Library General Public
16 License along with the GNU C Library; see the file COPYING.LIB. If
17 not, write to the Free Software Foundation, Inc., 675 Mass Ave,
18 Cambridge, MA 02139, 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 _hurd_exec_flags |= EXEC_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, 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 = (request == PTRACE_ATTACH ?
182 __msg_set_some_exec_flags :
183 __msg_clear_some_exec_flags) (msgport, task, EXEC_TRACED);
184 #ifdef notyet /* XXX */
185 if (! err)
186 /* Request (or request an end to) SIGCHLD notification
187 when PID stops or dies, and proc_wait working on PID. */
188 err = __USEPORT (PROC,
189 __proc_trace_pid (port, pid,
190 request == PTRACE_ATTACH));
191 #endif
192 if (! err)
194 if (request == PTRACE_ATTACH)
195 /* Now stop the process. */
196 err = __msg_sig_post (msgport, SIGSTOP, task);
197 else
198 /* Resume the process from tracing stop. */
199 err = __msg_sig_post_untraced (msgport, 0, task);
201 __mach_port_deallocate (__mach_task_self (), msgport);
203 __mach_port_deallocate (__mach_task_self (), task);
204 return err ? __hurd_fail (err) : 0;
207 case PTRACE_PEEKTEXT:
208 case PTRACE_PEEKDATA:
209 va_start (ap, request);
210 pid = va_arg (ap, pid_t);
211 addr = va_arg (ap, void *);
212 va_end (ap);
214 /* Read the page (or two pages, if the word lies on a boundary)
215 containing the addressed word. */
216 error_t err;
217 vm_address_t ourpage;
218 vm_size_t size;
219 natural_t word;
220 task_t task = __pid2task (pid);
221 if (task == MACH_PORT_NULL)
222 return -1;
223 data = sizeof word;
224 ourpage = 0;
225 size = 0;
226 err = read_data (task, &ourpage, &size);
227 __mach_port_deallocate (__mach_task_self (), task);
228 if (err)
229 return __hurd_fail (err);
230 word = *(natural_t *) ((vm_address_t) addr - trunc_page (addr)
231 + ourpage);
232 __vm_deallocate (__mach_task_self (), ourpage, size);
233 return word;
236 case PTRACE_PEEKUSER:
237 case PTRACE_POKEUSER:
238 /* U area, what's that? */
239 return EOPNOTSUPP;
241 case PTRACE_GETREGS:
242 case PTRACE_SETREGS:
243 va_start (ap, request);
244 pid = va_arg (ap, pid_t);
245 addr = va_arg (ap, void *);
246 va_end (ap);
247 return get_regs (MACHINE_THREAD_STATE_FLAVOR,
248 MACHINE_THREAD_STATE_COUNT);
250 case PTRACE_GETFPREGS:
251 case PTRACE_SETFPREGS:
252 va_start (ap, request);
253 pid = va_arg (ap, pid_t);
254 addr = va_arg (ap, void *);
255 va_end (ap);
256 #ifdef MACHINE_THREAD_FLOAT_STATE_FLAVOR
257 return get_regs (MACHINE_THREAD_FLOAT_STATE_FLAVOR,
258 MACHINE_THREAD_FLOAT_STATE_COUNT);
259 #else
260 return EOPNOTSUPP;
261 #endif
263 case PTRACE_GETFPAREGS:
264 case PTRACE_SETFPAREGS:
265 va_start (ap, request);
266 pid = va_arg (ap, pid_t);
267 addr = va_arg (ap, void *);
268 va_end (ap);
269 #ifdef MACHINE_THREAD_FPA_STATE_FLAVOR
270 return get_regs (MACHINE_THREAD_FPA_STATE_FLAVOR,
271 MACHINE_THREAD_FPA_STATE_COUNT);
272 #else
273 return EOPNOTSUPP;
274 #endif
276 case PTRACE_POKETEXT:
277 case PTRACE_POKEDATA:
278 va_start (ap, request);
279 pid = va_arg (ap, pid_t);
280 addr = va_arg (ap, void *);
281 data = va_arg (ap, int);
282 va_end (ap);
284 /* Read the page (or two pages, if the word lies on a boundary)
285 containing the addressed word. */
286 error_t err;
287 vm_address_t ourpage;
288 vm_size_t size;
289 task_t task = __pid2task (pid);
290 if (task == MACH_PORT_NULL)
291 return -1;
292 data = sizeof (natural_t);
293 ourpage = 0;
294 size = 0;
295 err = read_data (task, &ourpage, &size);
297 if (!err)
299 /* Now modify the specified word and write the page back. */
300 *(natural_t *) ((vm_address_t) addr - trunc_page (addr)
301 + ourpage) = data;
302 err = __vm_write (task, trunc_page (addr), ourpage, size);
303 __vm_deallocate (__mach_task_self (), ourpage, size);
306 __mach_port_deallocate (__mach_task_self (), task);
307 return err ? __hurd_fail (err) : 0;
310 case PTRACE_READDATA:
311 case PTRACE_READTEXT:
312 va_start (ap, request);
313 pid = va_arg (ap, pid_t);
314 addr = va_arg (ap, void *);
315 data = va_arg (ap, int);
316 addr2 = va_arg (ap, void *);
317 va_end (ap);
319 error_t err;
320 vm_address_t ourpage;
321 vm_size_t size;
322 task_t task = __pid2task (pid);
323 if (task == MACH_PORT_NULL)
324 return -1;
325 if (((vm_address_t) addr2 + data) % __vm_page_size == 0)
327 /* Perhaps we can write directly to the user's buffer. */
328 ourpage = (vm_address_t) addr2;
329 size = data;
331 else
333 ourpage = 0;
334 size = 0;
336 err = read_data (task, &ourpage, &size);
337 __mach_port_deallocate (__mach_task_self (), task);
338 if (!err && ourpage != (vm_address_t) addr2)
340 memcpy (addr2, (void *) ourpage, data);
341 __vm_deallocate (__mach_task_self (), ourpage, size);
343 return err ? __hurd_fail (err) : 0;
346 case PTRACE_WRITEDATA:
347 case PTRACE_WRITETEXT:
348 va_start (ap, request);
349 pid = va_arg (ap, pid_t);
350 addr = va_arg (ap, void *);
351 data = va_arg (ap, int);
352 addr2 = va_arg (ap, void *);
353 va_end (ap);
355 error_t err;
356 vm_address_t ourpage;
357 vm_size_t size;
358 task_t task = __pid2task (pid);
359 if (task == MACH_PORT_NULL)
360 return -1;
361 if ((vm_address_t) addr % __vm_page_size == 0 &&
362 (vm_address_t) data % __vm_page_size == 0)
364 /* Writing whole pages; can go directly from the user's buffer. */
365 ourpage = (vm_address_t) addr2;
366 size = data;
367 err = 0;
369 else
371 /* Read the task's pages and modify our own copy. */
372 ourpage = 0;
373 size = 0;
374 err = read_data (task, &ourpage, &size);
375 if (!err)
376 memcpy ((void *) ((vm_address_t) addr - trunc_page (addr)
377 + ourpage),
378 addr2,
379 data);
381 if (!err)
382 /* Write back the modified pages. */
383 err = __vm_write (task, trunc_page (addr), ourpage, size);
384 __mach_port_deallocate (__mach_task_self (), task);
385 return err ? __hurd_fail (err) : 0;
388 default:
389 errno = EINVAL;
390 return -1;
393 return 0;