powerpc64: Fix by using the configure value $libc_cv_cc_submachine [BZ #31629]
[glibc.git] / sysdeps / mach / hurd / ptrace.c
blob45f3e2983b3825839f405a40eab445ff6e6c3478
1 /* Process tracing interface `ptrace' for GNU Hurd.
2 Copyright (C) 1991-2024 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/>. */
19 #include <errno.h>
20 #include <sys/ptrace.h>
21 #include <sys/types.h>
22 #include <stdarg.h>
23 #include <hurd.h>
24 #include <hurd/signal.h>
25 #include <hurd/msg.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
31 traced.
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
36 after PID. */
37 int
38 ptrace (enum __ptrace_request request, ... )
40 pid_t pid;
41 void *addr, *addr2;
42 natural_t data;
43 va_list ap;
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. */
49 error_t err;
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);
53 if (!err)
54 *size = nread;
55 return err;
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);
64 if (err)
65 return err;
66 if (nthreads == 0)
67 return EINVAL;
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]);
74 return 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)
80 error_t err;
81 task_t task = __pid2task (pid);
82 thread_t thread;
83 if (task == MACH_PORT_NULL)
84 return -1;
85 err = fetch_user_thread (task, &thread);
86 __mach_port_deallocate (__mach_task_self (), task);
87 if (!err)
88 err = __thread_get_state (thread, flavor, addr, &count);
89 __mach_port_deallocate (__mach_task_self (), thread);
90 return err ? __hurd_fail (err) : 0;
94 switch (request)
96 case PTRACE_TRACEME:
97 /* Make this process be traced. */
98 __sigfillset (&_hurdsig_traced);
99 __USEPORT (PROC, __proc_mark_traced (port));
100 break;
102 case PTRACE_CONT:
103 va_start (ap, request);
104 pid = va_arg (ap, pid_t);
105 addr = va_arg (ap, void *);
106 data = va_arg (ap, int);
107 va_end (ap);
109 /* Send a DATA signal to PID, telling it to take the signal
110 normally even if it's traced. */
111 error_t err;
112 task_t task = __pid2task (pid);
113 if (task == MACH_PORT_NULL)
114 return -1;
115 if (data == SIGKILL)
116 err = __task_terminate (task);
117 else
119 if (addr != (void *) 1)
121 /* Move the user thread's PC to ADDR. */
122 thread_t thread;
123 err = fetch_user_thread (task, &thread);
124 if (!err)
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);
131 if (!err)
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);
142 else
143 err = 0;
145 if (! err)
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;
155 case PTRACE_KILL:
156 va_start (ap, request);
157 pid = va_arg (ap, pid_t);
158 va_end (ap);
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);
168 case PTRACE_ATTACH:
169 case PTRACE_DETACH:
170 va_start (ap, request);
171 pid = va_arg (ap, pid_t);
172 va_end (ap);
174 /* Tell PID to set or clear its trace bit. */
175 error_t err;
176 mach_port_t msgport;
177 task_t task = __pid2task (pid);
178 if (task == MACH_PORT_NULL)
179 return -1;
180 err = __USEPORT (PROC, __proc_getmsgport (port, pid, &msgport));
181 if (! err)
183 err = __msg_set_init_int (msgport, task, INIT_TRACEMASK,
184 request == PTRACE_DETACH ? 0
185 : ~(sigset_t) 0);
186 if (! err)
188 if (request == PTRACE_ATTACH)
189 /* Now stop the process. */
190 err = __msg_sig_post (msgport, SIGSTOP, 0, task);
191 else
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 *);
206 va_end (ap);
208 /* Read the page (or two pages, if the word lies on a boundary)
209 containing the addressed word. */
210 error_t err;
211 vm_address_t ourpage;
212 vm_size_t size;
213 natural_t word;
214 task_t task = __pid2task (pid);
215 if (task == MACH_PORT_NULL)
216 return -1;
217 data = sizeof word;
218 ourpage = 0;
219 size = 0;
220 err = read_data (task, &ourpage, &size);
221 __mach_port_deallocate (__mach_task_self (), task);
222 if (err)
223 return __hurd_fail (err);
224 word = *(natural_t *) ((vm_address_t) addr - trunc_page (addr)
225 + ourpage);
226 __vm_deallocate (__mach_task_self (), ourpage, size);
227 return word;
230 case PTRACE_PEEKUSER:
231 case PTRACE_POKEUSER:
232 /* U area, what's that? */
233 return __hurd_fail (EOPNOTSUPP);
235 case PTRACE_GETREGS:
236 case PTRACE_SETREGS:
237 va_start (ap, request);
238 pid = va_arg (ap, pid_t);
239 addr = va_arg (ap, void *);
240 va_end (ap);
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 *);
249 va_end (ap);
250 #ifdef MACHINE_THREAD_FLOAT_STATE_FLAVOR
251 return get_regs (MACHINE_THREAD_FLOAT_STATE_FLAVOR,
252 MACHINE_THREAD_FLOAT_STATE_COUNT);
253 #else
254 return __hurd_fail (EOPNOTSUPP);
255 #endif
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 *);
262 va_end (ap);
263 #ifdef MACHINE_THREAD_FPA_STATE_FLAVOR
264 return get_regs (MACHINE_THREAD_FPA_STATE_FLAVOR,
265 MACHINE_THREAD_FPA_STATE_COUNT);
266 #else
267 return __hurd_fail (EOPNOTSUPP);
268 #endif
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);
276 va_end (ap);
278 /* Read the page (or two pages, if the word lies on a boundary)
279 containing the addressed word. */
280 error_t err;
281 vm_address_t ourpage;
282 vm_size_t size;
283 task_t task = __pid2task (pid);
284 if (task == MACH_PORT_NULL)
285 return -1;
286 data = sizeof (natural_t);
287 ourpage = 0;
288 size = 0;
289 err = read_data (task, &ourpage, &size);
291 if (!err)
293 /* Now modify the specified word and write the page back. */
294 *(natural_t *) ((vm_address_t) addr - trunc_page (addr)
295 + ourpage) = data;
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 *);
311 va_end (ap);
313 error_t err;
314 vm_address_t ourpage;
315 vm_size_t size;
316 task_t task = __pid2task (pid);
317 if (task == MACH_PORT_NULL)
318 return -1;
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;
323 size = data;
325 else
327 ourpage = 0;
328 size = 0;
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 *);
347 va_end (ap);
349 error_t err;
350 vm_address_t ourpage;
351 vm_size_t size;
352 task_t task = __pid2task (pid);
353 if (task == MACH_PORT_NULL)
354 return -1;
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;
360 size = data;
361 err = 0;
363 else
365 /* Read the task's pages and modify our own copy. */
366 ourpage = 0;
367 size = 0;
368 err = read_data (task, &ourpage, &size);
369 if (!err)
370 memcpy ((void *) ((vm_address_t) addr - trunc_page (addr)
371 + ourpage),
372 addr2,
373 data);
375 if (!err)
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;
382 default:
383 return __hurd_fail (EINVAL);
386 return 0;