1/* Process tracing interface `ptrace' for GNU Hurd.
2 Copyright (C) 1991-2022 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
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.
9
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.
14
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/>. */
18
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>
27
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.
32
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. */
37int
38ptrace (enum __ptrace_request request, ... )
39{
40 pid_t pid;
41 void *addr, *addr2;
42 natural_t data;
43 va_list ap;
44
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)
47 {
48 /* Read the pages containing the addressed range. */
49 error_t err;
50 *size = round_page (addr + data) - trunc_page (addr);
51 err = __vm_read (task, trunc_page (addr), *size, ourpage, size);
52 return err;
53 }
54
55 /* Fetch the thread port for PID's user thread. */
56 error_t fetch_user_thread (task_t task, thread_t *thread)
57 {
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);
61 if (err)
62 return err;
63 if (nthreads == 0)
64 return EINVAL;
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]);
71 return 0;
72 }
73
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)
76 {
77 error_t err;
78 task_t task = __pid2task (pid);
79 thread_t thread;
80 if (task == MACH_PORT_NULL)
81 return -1;
82 err = fetch_user_thread (task, &thread);
83 __mach_port_deallocate (__mach_task_self (), task);
84 if (!err)
85 err = __thread_get_state (thread, flavor, addr, &count);
86 __mach_port_deallocate (__mach_task_self (), thread);
87 return err ? __hurd_fail (err) : 0;
88 }
89
90
91 switch (request)
92 {
93 case PTRACE_TRACEME:
94 /* Make this process be traced. */
95 __sigfillset (&_hurdsig_traced);
96 __USEPORT (PROC, __proc_mark_traced (port));
97 break;
98
99 case PTRACE_CONT:
100 va_start (ap, request);
101 pid = va_arg (ap, pid_t);
102 addr = va_arg (ap, void *);
103 data = va_arg (ap, int);
104 va_end (ap);
105 {
106 /* Send a DATA signal to PID, telling it to take the signal
107 normally even if it's traced. */
108 error_t err;
109 task_t task = __pid2task (pid);
110 if (task == MACH_PORT_NULL)
111 return -1;
112 if (data == SIGKILL)
113 err = __task_terminate (task);
114 else
115 {
116 if (addr != (void *) 1)
117 {
118 /* Move the user thread's PC to ADDR. */
119 thread_t thread;
120 err = fetch_user_thread (task, &thread);
121 if (!err)
122 {
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);
128 if (!err)
129 {
130 MACHINE_THREAD_STATE_SET_PC (&state, addr);
131 err = __thread_set_state (thread,
132 MACHINE_THREAD_STATE_FLAVOR,
133 (natural_t *) &state, count);
134 }
135
136 }
137 __mach_port_deallocate (__mach_task_self (), thread);
138 }
139 else
140 err = 0;
141
142 if (! err)
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));
147 }
148 __mach_port_deallocate (__mach_task_self (), task);
149 return err ? __hurd_fail (err) : 0;
150 }
151
152 case PTRACE_KILL:
153 va_start (ap, request);
154 pid = va_arg (ap, pid_t);
155 va_end (ap);
156 /* SIGKILL always just terminates the task,
157 so normal kill is just the same when traced. */
158 return __kill (pid, SIGKILL);
159
160 case PTRACE_SINGLESTEP:
161 /* This is a machine-dependent kernel RPC on
162 machines that support it. Punt. */
163 return __hurd_fail (EOPNOTSUPP);
164
165 case PTRACE_ATTACH:
166 case PTRACE_DETACH:
167 va_start (ap, request);
168 pid = va_arg (ap, pid_t);
169 va_end (ap);
170 {
171 /* Tell PID to set or clear its trace bit. */
172 error_t err;
173 mach_port_t msgport;
174 task_t task = __pid2task (pid);
175 if (task == MACH_PORT_NULL)
176 return -1;
177 err = __USEPORT (PROC, __proc_getmsgport (port, pid, &msgport));
178 if (! err)
179 {
180 err = __msg_set_init_int (msgport, task, INIT_TRACEMASK,
181 request == PTRACE_DETACH ? 0
182 : ~(sigset_t) 0);
183 if (! err)
184 {
185 if (request == PTRACE_ATTACH)
186 /* Now stop the process. */
187 err = __msg_sig_post (msgport, SIGSTOP, 0, task);
188 else
189 /* Resume the process from tracing stop. */
190 err = __msg_sig_post_untraced (msgport, 0, 0, task);
191 }
192 __mach_port_deallocate (__mach_task_self (), msgport);
193 }
194 __mach_port_deallocate (__mach_task_self (), task);
195 return err ? __hurd_fail (err) : 0;
196 }
197
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 *);
203 va_end (ap);
204 {
205 /* Read the page (or two pages, if the word lies on a boundary)
206 containing the addressed word. */
207 error_t err;
208 vm_address_t ourpage;
209 vm_size_t size;
210 natural_t word;
211 task_t task = __pid2task (pid);
212 if (task == MACH_PORT_NULL)
213 return -1;
214 data = sizeof word;
215 ourpage = 0;
216 size = 0;
217 err = read_data (task, &ourpage, &size);
218 __mach_port_deallocate (__mach_task_self (), task);
219 if (err)
220 return __hurd_fail (err);
221 word = *(natural_t *) ((vm_address_t) addr - trunc_page (addr)
222 + ourpage);
223 __vm_deallocate (__mach_task_self (), ourpage, size);
224 return word;
225 }
226
227 case PTRACE_PEEKUSER:
228 case PTRACE_POKEUSER:
229 /* U area, what's that? */
230 return __hurd_fail (EOPNOTSUPP);
231
232 case PTRACE_GETREGS:
233 case PTRACE_SETREGS:
234 va_start (ap, request);
235 pid = va_arg (ap, pid_t);
236 addr = va_arg (ap, void *);
237 va_end (ap);
238 return get_regs (MACHINE_THREAD_STATE_FLAVOR,
239 MACHINE_THREAD_STATE_COUNT);
240
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 *);
246 va_end (ap);
247#ifdef MACHINE_THREAD_FLOAT_STATE_FLAVOR
248 return get_regs (MACHINE_THREAD_FLOAT_STATE_FLAVOR,
249 MACHINE_THREAD_FLOAT_STATE_COUNT);
250#else
251 return __hurd_fail (EOPNOTSUPP);
252#endif
253
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 *);
259 va_end (ap);
260#ifdef MACHINE_THREAD_FPA_STATE_FLAVOR
261 return get_regs (MACHINE_THREAD_FPA_STATE_FLAVOR,
262 MACHINE_THREAD_FPA_STATE_COUNT);
263#else
264 return __hurd_fail (EOPNOTSUPP);
265#endif
266
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);
273 va_end (ap);
274 {
275 /* Read the page (or two pages, if the word lies on a boundary)
276 containing the addressed word. */
277 error_t err;
278 vm_address_t ourpage;
279 vm_size_t size;
280 task_t task = __pid2task (pid);
281 if (task == MACH_PORT_NULL)
282 return -1;
283 data = sizeof (natural_t);
284 ourpage = 0;
285 size = 0;
286 err = read_data (task, &ourpage, &size);
287
288 if (!err)
289 {
290 /* Now modify the specified word and write the page back. */
291 *(natural_t *) ((vm_address_t) addr - trunc_page (addr)
292 + ourpage) = data;
293 err = __vm_write (task, trunc_page (addr), ourpage, size);
294 __vm_deallocate (__mach_task_self (), ourpage, size);
295 }
296
297 __mach_port_deallocate (__mach_task_self (), task);
298 return err ? __hurd_fail (err) : 0;
299 }
300
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 *);
308 va_end (ap);
309 {
310 error_t err;
311 vm_address_t ourpage;
312 vm_size_t size;
313 task_t task = __pid2task (pid);
314 if (task == MACH_PORT_NULL)
315 return -1;
316 if (((vm_address_t) addr2 + data) % __vm_page_size == 0)
317 {
318 /* Perhaps we can write directly to the user's buffer. */
319 ourpage = (vm_address_t) addr2;
320 size = data;
321 }
322 else
323 {
324 ourpage = 0;
325 size = 0;
326 }
327 err = read_data (task, &ourpage, &size);
328 __mach_port_deallocate (__mach_task_self (), task);
329 if (!err && ourpage != (vm_address_t) addr2)
330 {
331 memcpy (addr2, (void *) ourpage, data);
332 __vm_deallocate (__mach_task_self (), ourpage, size);
333 }
334 return err ? __hurd_fail (err) : 0;
335 }
336
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 *);
344 va_end (ap);
345 {
346 error_t err;
347 vm_address_t ourpage;
348 vm_size_t size;
349 task_t task = __pid2task (pid);
350 if (task == MACH_PORT_NULL)
351 return -1;
352 if ((vm_address_t) addr % __vm_page_size == 0
353 && (vm_address_t) data % __vm_page_size == 0)
354 {
355 /* Writing whole pages; can go directly from the user's buffer. */
356 ourpage = (vm_address_t) addr2;
357 size = data;
358 err = 0;
359 }
360 else
361 {
362 /* Read the task's pages and modify our own copy. */
363 ourpage = 0;
364 size = 0;
365 err = read_data (task, &ourpage, &size);
366 if (!err)
367 memcpy ((void *) ((vm_address_t) addr - trunc_page (addr)
368 + ourpage),
369 addr2,
370 data);
371 }
372 if (!err)
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;
377 }
378
379 default:
380 errno = EINVAL;
381 return -1;
382 }
383
384 return 0;
385}
386

source code of glibc/sysdeps/mach/hurd/ptrace.c