1 /* Process tracing interface `ptrace' for GNU Hurd.
2 Copyright (C) 1991-2016 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, see
17 <http://www.gnu.org/licenses/>. */
20 #include <sys/ptrace.h>
21 #include <sys/types.h>
24 #include <hurd/signal.h>
26 #include <thread_state.h>
28 /* Perform process tracing functions. REQUEST is one of the values
29 in <sys/ptrace.h>, and determines the action to be taken.
30 For all requests except PTRACE_TRACEME, PID specifies the process to be
33 PID and the other arguments described above for the various requests should
34 appear (those that are used for the particular request) as:
35 pid_t PID, void *ADDR, int DATA, void *ADDR2
38 ptrace (enum __ptrace_request request
, ... )
45 /* Read data from PID's address space, from ADDR for DATA bytes. */
46 error_t
read_data (task_t task
, vm_address_t
*ourpage
, vm_size_t
*size
)
48 /* Read the pages containing the addressed range. */
50 *size
= round_page (addr
+ data
) - trunc_page (addr
);
51 err
= __vm_read (task
, trunc_page (addr
), *size
, ourpage
, size
);
55 /* Fetch the thread port for PID's user thread. */
56 error_t
fetch_user_thread (task_t task
, thread_t
*thread
)
58 thread_t threadbuf
[3], *threads
= threadbuf
;
59 mach_msg_type_number_t nthreads
= 3, i
;
60 error_t err
= __task_threads (task
, &threads
, &nthreads
);
65 *thread
= threads
[0]; /* Assume user thread is first. */
66 for (i
= 1; i
< nthreads
; ++i
)
67 __mach_port_deallocate (__mach_task_self (), threads
[i
]);
68 if (threads
!= threadbuf
)
69 __vm_deallocate (__mach_task_self (),
70 (vm_address_t
) threads
, nthreads
* sizeof threads
[0]);
74 /* Fetch a thread state structure from PID and store it at ADDR. */
75 int get_regs (int flavor
, mach_msg_type_number_t count
)
78 task_t task
= __pid2task (pid
);
80 if (task
== MACH_PORT_NULL
)
82 err
= fetch_user_thread (task
, &thread
);
83 __mach_port_deallocate (__mach_task_self (), task
);
85 err
= __thread_get_state (thread
, flavor
, addr
, &count
);
86 __mach_port_deallocate (__mach_task_self (), thread
);
87 return err
? __hurd_fail (err
) : 0;
94 /* Make this process be traced. */
95 __sigfillset (&_hurdsig_traced
);
96 __USEPORT (PROC
, __proc_mark_traced (port
));
100 va_start (ap
, request
);
101 pid
= va_arg (ap
, pid_t
);
102 addr
= va_arg (ap
, void *);
103 data
= va_arg (ap
, int);
106 /* Send a DATA signal to PID, telling it to take the signal
107 normally even if it's traced. */
109 task_t task
= __pid2task (pid
);
110 if (task
== MACH_PORT_NULL
)
113 err
= __task_terminate (task
);
116 if (addr
!= (void *) 1)
118 /* Move the user thread's PC to ADDR. */
120 err
= fetch_user_thread (task
, &thread
);
123 struct machine_thread_state state
;
124 mach_msg_type_number_t count
= MACHINE_THREAD_STATE_COUNT
;
125 err
= __thread_get_state (thread
,
126 MACHINE_THREAD_STATE_FLAVOR
,
127 (natural_t
*) &state
, &count
);
130 MACHINE_THREAD_STATE_SET_PC (&state
, addr
);
131 err
= __thread_set_state (thread
,
132 MACHINE_THREAD_STATE_FLAVOR
,
133 (natural_t
*) &state
, count
);
137 __mach_port_deallocate (__mach_task_self (), thread
);
143 /* Tell the process to take the signal (or just resume if 0). */
144 err
= HURD_MSGPORT_RPC
145 (__USEPORT (PROC
, __proc_getmsgport (port
, pid
, &msgport
)),
146 0, 0, __msg_sig_post_untraced (msgport
, data
, 0, task
));
148 __mach_port_deallocate (__mach_task_self (), task
);
149 return err
? __hurd_fail (err
) : 0;
153 va_start (ap
, request
);
154 pid
= va_arg (ap
, pid_t
);
156 /* SIGKILL always just terminates the task,
157 so normal kill is just the same when traced. */
158 return kill (pid
, SIGKILL
);
160 case PTRACE_SINGLESTEP
:
161 /* This is a machine-dependent kernel RPC on
162 machines that support it. Punt. */
163 return __hurd_fail (EOPNOTSUPP
);
167 va_start (ap
, request
);
168 pid
= va_arg (ap
, pid_t
);
171 /* Tell PID to set or clear its trace bit. */
174 task_t task
= __pid2task (pid
);
175 if (task
== MACH_PORT_NULL
)
177 err
= __USEPORT (PROC
, __proc_getmsgport (port
, pid
, &msgport
));
180 err
= __msg_set_init_int (msgport
, task
, INIT_TRACEMASK
,
181 request
== PTRACE_DETACH
? 0 :
185 if (request
== PTRACE_ATTACH
)
186 /* Now stop the process. */
187 err
= __msg_sig_post (msgport
, SIGSTOP
, 0, task
);
189 /* Resume the process from tracing stop. */
190 err
= __msg_sig_post_untraced (msgport
, 0, 0, task
);
192 __mach_port_deallocate (__mach_task_self (), msgport
);
194 __mach_port_deallocate (__mach_task_self (), task
);
195 return err
? __hurd_fail (err
) : 0;
198 case PTRACE_PEEKTEXT
:
199 case PTRACE_PEEKDATA
:
200 va_start (ap
, request
);
201 pid
= va_arg (ap
, pid_t
);
202 addr
= va_arg (ap
, void *);
205 /* Read the page (or two pages, if the word lies on a boundary)
206 containing the addressed word. */
208 vm_address_t ourpage
;
211 task_t task
= __pid2task (pid
);
212 if (task
== MACH_PORT_NULL
)
217 err
= read_data (task
, &ourpage
, &size
);
218 __mach_port_deallocate (__mach_task_self (), task
);
220 return __hurd_fail (err
);
221 word
= *(natural_t
*) ((vm_address_t
) addr
- trunc_page (addr
)
223 __vm_deallocate (__mach_task_self (), ourpage
, size
);
227 case PTRACE_PEEKUSER
:
228 case PTRACE_POKEUSER
:
229 /* U area, what's that? */
230 return __hurd_fail (EOPNOTSUPP
);
234 va_start (ap
, request
);
235 pid
= va_arg (ap
, pid_t
);
236 addr
= va_arg (ap
, void *);
238 return get_regs (MACHINE_THREAD_STATE_FLAVOR
,
239 MACHINE_THREAD_STATE_COUNT
);
241 case PTRACE_GETFPREGS
:
242 case PTRACE_SETFPREGS
:
243 va_start (ap
, request
);
244 pid
= va_arg (ap
, pid_t
);
245 addr
= va_arg (ap
, void *);
247 #ifdef MACHINE_THREAD_FLOAT_STATE_FLAVOR
248 return get_regs (MACHINE_THREAD_FLOAT_STATE_FLAVOR
,
249 MACHINE_THREAD_FLOAT_STATE_COUNT
);
251 return __hurd_fail (EOPNOTSUPP
);
254 case PTRACE_GETFPAREGS
:
255 case PTRACE_SETFPAREGS
:
256 va_start (ap
, request
);
257 pid
= va_arg (ap
, pid_t
);
258 addr
= va_arg (ap
, void *);
260 #ifdef MACHINE_THREAD_FPA_STATE_FLAVOR
261 return get_regs (MACHINE_THREAD_FPA_STATE_FLAVOR
,
262 MACHINE_THREAD_FPA_STATE_COUNT
);
264 return __hurd_fail (EOPNOTSUPP
);
267 case PTRACE_POKETEXT
:
268 case PTRACE_POKEDATA
:
269 va_start (ap
, request
);
270 pid
= va_arg (ap
, pid_t
);
271 addr
= va_arg (ap
, void *);
272 data
= va_arg (ap
, int);
275 /* Read the page (or two pages, if the word lies on a boundary)
276 containing the addressed word. */
278 vm_address_t ourpage
;
280 task_t task
= __pid2task (pid
);
281 if (task
== MACH_PORT_NULL
)
283 data
= sizeof (natural_t
);
286 err
= read_data (task
, &ourpage
, &size
);
290 /* Now modify the specified word and write the page back. */
291 *(natural_t
*) ((vm_address_t
) addr
- trunc_page (addr
)
293 err
= __vm_write (task
, trunc_page (addr
), ourpage
, size
);
294 __vm_deallocate (__mach_task_self (), ourpage
, size
);
297 __mach_port_deallocate (__mach_task_self (), task
);
298 return err
? __hurd_fail (err
) : 0;
301 case PTRACE_READDATA
:
302 case PTRACE_READTEXT
:
303 va_start (ap
, request
);
304 pid
= va_arg (ap
, pid_t
);
305 addr
= va_arg (ap
, void *);
306 data
= va_arg (ap
, int);
307 addr2
= va_arg (ap
, void *);
311 vm_address_t ourpage
;
313 task_t task
= __pid2task (pid
);
314 if (task
== MACH_PORT_NULL
)
316 if (((vm_address_t
) addr2
+ data
) % __vm_page_size
== 0)
318 /* Perhaps we can write directly to the user's buffer. */
319 ourpage
= (vm_address_t
) addr2
;
327 err
= read_data (task
, &ourpage
, &size
);
328 __mach_port_deallocate (__mach_task_self (), task
);
329 if (!err
&& ourpage
!= (vm_address_t
) addr2
)
331 memcpy (addr2
, (void *) ourpage
, data
);
332 __vm_deallocate (__mach_task_self (), ourpage
, size
);
334 return err
? __hurd_fail (err
) : 0;
337 case PTRACE_WRITEDATA
:
338 case PTRACE_WRITETEXT
:
339 va_start (ap
, request
);
340 pid
= va_arg (ap
, pid_t
);
341 addr
= va_arg (ap
, void *);
342 data
= va_arg (ap
, int);
343 addr2
= va_arg (ap
, void *);
347 vm_address_t ourpage
;
349 task_t task
= __pid2task (pid
);
350 if (task
== MACH_PORT_NULL
)
352 if ((vm_address_t
) addr
% __vm_page_size
== 0 &&
353 (vm_address_t
) data
% __vm_page_size
== 0)
355 /* Writing whole pages; can go directly from the user's buffer. */
356 ourpage
= (vm_address_t
) addr2
;
362 /* Read the task's pages and modify our own copy. */
365 err
= read_data (task
, &ourpage
, &size
);
367 memcpy ((void *) ((vm_address_t
) addr
- trunc_page (addr
)
373 /* Write back the modified pages. */
374 err
= __vm_write (task
, trunc_page (addr
), ourpage
, size
);
375 __mach_port_deallocate (__mach_task_self (), task
);
376 return err
? __hurd_fail (err
) : 0;