1 /* Process tracing interface `ptrace' for GNU Hurd.
2 Copyright (C) 1991-2023 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 <https://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 mach_msg_type_number_t nread
;
51 *size
= round_page (addr
+ data
) - trunc_page (addr
);
52 err
= __vm_read (task
, trunc_page (addr
), *size
, ourpage
, &nread
);
58 /* Fetch the thread port for PID's user thread. */
59 error_t
fetch_user_thread (task_t task
, thread_t
*thread
)
61 thread_t threadbuf
[3], *threads
= threadbuf
;
62 mach_msg_type_number_t nthreads
= 3, i
;
63 error_t err
= __task_threads (task
, &threads
, &nthreads
);
68 *thread
= threads
[0]; /* Assume user thread is first. */
69 for (i
= 1; i
< nthreads
; ++i
)
70 __mach_port_deallocate (__mach_task_self (), threads
[i
]);
71 if (threads
!= threadbuf
)
72 __vm_deallocate (__mach_task_self (),
73 (vm_address_t
) threads
, nthreads
* sizeof threads
[0]);
77 /* Fetch a thread state structure from PID and store it at ADDR. */
78 int get_regs (int flavor
, mach_msg_type_number_t count
)
81 task_t task
= __pid2task (pid
);
83 if (task
== MACH_PORT_NULL
)
85 err
= fetch_user_thread (task
, &thread
);
86 __mach_port_deallocate (__mach_task_self (), task
);
88 err
= __thread_get_state (thread
, flavor
, addr
, &count
);
89 __mach_port_deallocate (__mach_task_self (), thread
);
90 return err
? __hurd_fail (err
) : 0;
97 /* Make this process be traced. */
98 __sigfillset (&_hurdsig_traced
);
99 __USEPORT (PROC
, __proc_mark_traced (port
));
103 va_start (ap
, request
);
104 pid
= va_arg (ap
, pid_t
);
105 addr
= va_arg (ap
, void *);
106 data
= va_arg (ap
, int);
109 /* Send a DATA signal to PID, telling it to take the signal
110 normally even if it's traced. */
112 task_t task
= __pid2task (pid
);
113 if (task
== MACH_PORT_NULL
)
116 err
= __task_terminate (task
);
119 if (addr
!= (void *) 1)
121 /* Move the user thread's PC to ADDR. */
123 err
= fetch_user_thread (task
, &thread
);
126 struct machine_thread_state state
;
127 mach_msg_type_number_t count
= MACHINE_THREAD_STATE_COUNT
;
128 err
= __thread_get_state (thread
,
129 MACHINE_THREAD_STATE_FLAVOR
,
130 (natural_t
*) &state
, &count
);
133 MACHINE_THREAD_STATE_SET_PC (&state
, addr
);
134 err
= __thread_set_state (thread
,
135 MACHINE_THREAD_STATE_FLAVOR
,
136 (natural_t
*) &state
, count
);
140 __mach_port_deallocate (__mach_task_self (), thread
);
146 /* Tell the process to take the signal (or just resume if 0). */
147 err
= HURD_MSGPORT_RPC
148 (__USEPORT (PROC
, __proc_getmsgport (port
, pid
, &msgport
)),
149 0, 0, __msg_sig_post_untraced (msgport
, data
, 0, task
));
151 __mach_port_deallocate (__mach_task_self (), task
);
152 return err
? __hurd_fail (err
) : 0;
156 va_start (ap
, request
);
157 pid
= va_arg (ap
, pid_t
);
159 /* SIGKILL always just terminates the task,
160 so normal kill is just the same when traced. */
161 return __kill (pid
, SIGKILL
);
163 case PTRACE_SINGLESTEP
:
164 /* This is a machine-dependent kernel RPC on
165 machines that support it. Punt. */
166 return __hurd_fail (EOPNOTSUPP
);
170 va_start (ap
, request
);
171 pid
= va_arg (ap
, pid_t
);
174 /* Tell PID to set or clear its trace bit. */
177 task_t task
= __pid2task (pid
);
178 if (task
== MACH_PORT_NULL
)
180 err
= __USEPORT (PROC
, __proc_getmsgport (port
, pid
, &msgport
));
183 err
= __msg_set_init_int (msgport
, task
, INIT_TRACEMASK
,
184 request
== PTRACE_DETACH
? 0
188 if (request
== PTRACE_ATTACH
)
189 /* Now stop the process. */
190 err
= __msg_sig_post (msgport
, SIGSTOP
, 0, task
);
192 /* Resume the process from tracing stop. */
193 err
= __msg_sig_post_untraced (msgport
, 0, 0, task
);
195 __mach_port_deallocate (__mach_task_self (), msgport
);
197 __mach_port_deallocate (__mach_task_self (), task
);
198 return err
? __hurd_fail (err
) : 0;
201 case PTRACE_PEEKTEXT
:
202 case PTRACE_PEEKDATA
:
203 va_start (ap
, request
);
204 pid
= va_arg (ap
, pid_t
);
205 addr
= va_arg (ap
, void *);
208 /* Read the page (or two pages, if the word lies on a boundary)
209 containing the addressed word. */
211 vm_address_t ourpage
;
214 task_t task
= __pid2task (pid
);
215 if (task
== MACH_PORT_NULL
)
220 err
= read_data (task
, &ourpage
, &size
);
221 __mach_port_deallocate (__mach_task_self (), task
);
223 return __hurd_fail (err
);
224 word
= *(natural_t
*) ((vm_address_t
) addr
- trunc_page (addr
)
226 __vm_deallocate (__mach_task_self (), ourpage
, size
);
230 case PTRACE_PEEKUSER
:
231 case PTRACE_POKEUSER
:
232 /* U area, what's that? */
233 return __hurd_fail (EOPNOTSUPP
);
237 va_start (ap
, request
);
238 pid
= va_arg (ap
, pid_t
);
239 addr
= va_arg (ap
, void *);
241 return get_regs (MACHINE_THREAD_STATE_FLAVOR
,
242 MACHINE_THREAD_STATE_COUNT
);
244 case PTRACE_GETFPREGS
:
245 case PTRACE_SETFPREGS
:
246 va_start (ap
, request
);
247 pid
= va_arg (ap
, pid_t
);
248 addr
= va_arg (ap
, void *);
250 #ifdef MACHINE_THREAD_FLOAT_STATE_FLAVOR
251 return get_regs (MACHINE_THREAD_FLOAT_STATE_FLAVOR
,
252 MACHINE_THREAD_FLOAT_STATE_COUNT
);
254 return __hurd_fail (EOPNOTSUPP
);
257 case PTRACE_GETFPAREGS
:
258 case PTRACE_SETFPAREGS
:
259 va_start (ap
, request
);
260 pid
= va_arg (ap
, pid_t
);
261 addr
= va_arg (ap
, void *);
263 #ifdef MACHINE_THREAD_FPA_STATE_FLAVOR
264 return get_regs (MACHINE_THREAD_FPA_STATE_FLAVOR
,
265 MACHINE_THREAD_FPA_STATE_COUNT
);
267 return __hurd_fail (EOPNOTSUPP
);
270 case PTRACE_POKETEXT
:
271 case PTRACE_POKEDATA
:
272 va_start (ap
, request
);
273 pid
= va_arg (ap
, pid_t
);
274 addr
= va_arg (ap
, void *);
275 data
= va_arg (ap
, int);
278 /* Read the page (or two pages, if the word lies on a boundary)
279 containing the addressed word. */
281 vm_address_t ourpage
;
283 task_t task
= __pid2task (pid
);
284 if (task
== MACH_PORT_NULL
)
286 data
= sizeof (natural_t
);
289 err
= read_data (task
, &ourpage
, &size
);
293 /* Now modify the specified word and write the page back. */
294 *(natural_t
*) ((vm_address_t
) addr
- trunc_page (addr
)
296 err
= __vm_write (task
, trunc_page (addr
), ourpage
, size
);
297 __vm_deallocate (__mach_task_self (), ourpage
, size
);
300 __mach_port_deallocate (__mach_task_self (), task
);
301 return err
? __hurd_fail (err
) : 0;
304 case PTRACE_READDATA
:
305 case PTRACE_READTEXT
:
306 va_start (ap
, request
);
307 pid
= va_arg (ap
, pid_t
);
308 addr
= va_arg (ap
, void *);
309 data
= va_arg (ap
, int);
310 addr2
= va_arg (ap
, void *);
314 vm_address_t ourpage
;
316 task_t task
= __pid2task (pid
);
317 if (task
== MACH_PORT_NULL
)
319 if (((vm_address_t
) addr2
+ data
) % __vm_page_size
== 0)
321 /* Perhaps we can write directly to the user's buffer. */
322 ourpage
= (vm_address_t
) addr2
;
330 err
= read_data (task
, &ourpage
, &size
);
331 __mach_port_deallocate (__mach_task_self (), task
);
332 if (!err
&& ourpage
!= (vm_address_t
) addr2
)
334 memcpy (addr2
, (void *) ourpage
, data
);
335 __vm_deallocate (__mach_task_self (), ourpage
, size
);
337 return err
? __hurd_fail (err
) : 0;
340 case PTRACE_WRITEDATA
:
341 case PTRACE_WRITETEXT
:
342 va_start (ap
, request
);
343 pid
= va_arg (ap
, pid_t
);
344 addr
= va_arg (ap
, void *);
345 data
= va_arg (ap
, int);
346 addr2
= va_arg (ap
, void *);
350 vm_address_t ourpage
;
352 task_t task
= __pid2task (pid
);
353 if (task
== MACH_PORT_NULL
)
355 if ((vm_address_t
) addr
% __vm_page_size
== 0
356 && (vm_address_t
) data
% __vm_page_size
== 0)
358 /* Writing whole pages; can go directly from the user's buffer. */
359 ourpage
= (vm_address_t
) addr2
;
365 /* Read the task's pages and modify our own copy. */
368 err
= read_data (task
, &ourpage
, &size
);
370 memcpy ((void *) ((vm_address_t
) addr
- trunc_page (addr
)
376 /* Write back the modified pages. */
377 err
= __vm_write (task
, trunc_page (addr
), ourpage
, size
);
378 __mach_port_deallocate (__mach_task_self (), task
);
379 return err
? __hurd_fail (err
) : 0;