1995-02-18 01:27:10 +00:00
|
|
|
/* Process tracing interface `ptrace' for GNU Hurd.
|
2020-01-01 00:14:33 +00:00
|
|
|
Copyright (C) 1991-2020 Free Software Foundation, Inc.
|
1997-06-23 21:55:26 +00:00
|
|
|
This file is part of the GNU C Library.
|
1995-02-18 01:27:10 +00:00
|
|
|
|
1997-06-23 21:55:26 +00:00
|
|
|
The GNU C Library is free software; you can redistribute it and/or
|
2001-07-06 04:58:11 +00:00
|
|
|
modify it under the terms of the GNU Lesser General Public
|
|
|
|
License as published by the Free Software Foundation; either
|
|
|
|
version 2.1 of the License, or (at your option) any later version.
|
1995-02-18 01:27:10 +00:00
|
|
|
|
1997-06-23 21:55:26 +00:00
|
|
|
The GNU C Library is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
2001-07-06 04:58:11 +00:00
|
|
|
Lesser General Public License for more details.
|
1995-02-18 01:27:10 +00:00
|
|
|
|
2001-07-06 04:58:11 +00:00
|
|
|
You should have received a copy of the GNU Lesser General Public
|
2012-02-09 23:18:22 +00:00
|
|
|
License along with the GNU C Library; if not, see
|
Prefer https to http for gnu.org and fsf.org URLs
Also, change sources.redhat.com to sourceware.org.
This patch was automatically generated by running the following shell
script, which uses GNU sed, and which avoids modifying files imported
from upstream:
sed -ri '
s,(http|ftp)(://(.*\.)?(gnu|fsf|sourceware)\.org($|[^.]|\.[^a-z])),https\2,g
s,(http|ftp)(://(.*\.)?)sources\.redhat\.com($|[^.]|\.[^a-z]),https\2sourceware.org\4,g
' \
$(find $(git ls-files) -prune -type f \
! -name '*.po' \
! -name 'ChangeLog*' \
! -path COPYING ! -path COPYING.LIB \
! -path manual/fdl-1.3.texi ! -path manual/lgpl-2.1.texi \
! -path manual/texinfo.tex ! -path scripts/config.guess \
! -path scripts/config.sub ! -path scripts/install-sh \
! -path scripts/mkinstalldirs ! -path scripts/move-if-change \
! -path INSTALL ! -path locale/programs/charmap-kw.h \
! -path po/libc.pot ! -path sysdeps/gnu/errlist.c \
! '(' -name configure \
-execdir test -f configure.ac -o -f configure.in ';' ')' \
! '(' -name preconfigure \
-execdir test -f preconfigure.ac ';' ')' \
-print)
and then by running 'make dist-prepare' to regenerate files built
from the altered files, and then executing the following to cleanup:
chmod a+x sysdeps/unix/sysv/linux/riscv/configure
# Omit irrelevant whitespace and comment-only changes,
# perhaps from a slightly-different Autoconf version.
git checkout -f \
sysdeps/csky/configure \
sysdeps/hppa/configure \
sysdeps/riscv/configure \
sysdeps/unix/sysv/linux/csky/configure
# Omit changes that caused a pre-commit check to fail like this:
# remote: *** error: sysdeps/powerpc/powerpc64/ppc-mcount.S: trailing lines
git checkout -f \
sysdeps/powerpc/powerpc64/ppc-mcount.S \
sysdeps/unix/sysv/linux/s390/s390-64/syscall.S
# Omit change that caused a pre-commit check to fail like this:
# remote: *** error: sysdeps/sparc/sparc64/multiarch/memcpy-ultra3.S: last line does not end in newline
git checkout -f sysdeps/sparc/sparc64/multiarch/memcpy-ultra3.S
2019-09-07 05:40:42 +00:00
|
|
|
<https://www.gnu.org/licenses/>. */
|
1995-02-18 01:27:10 +00:00
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
#include <sys/ptrace.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include <hurd.h>
|
|
|
|
#include <hurd/signal.h>
|
|
|
|
#include <hurd/msg.h>
|
|
|
|
#include <thread_state.h>
|
|
|
|
|
|
|
|
/* Perform process tracing functions. REQUEST is one of the values
|
|
|
|
in <sys/ptrace.h>, and determines the action to be taken.
|
|
|
|
For all requests except PTRACE_TRACEME, PID specifies the process to be
|
|
|
|
traced.
|
|
|
|
|
|
|
|
PID and the other arguments described above for the various requests should
|
|
|
|
appear (those that are used for the particular request) as:
|
|
|
|
pid_t PID, void *ADDR, int DATA, void *ADDR2
|
|
|
|
after PID. */
|
|
|
|
int
|
|
|
|
ptrace (enum __ptrace_request request, ... )
|
|
|
|
{
|
|
|
|
pid_t pid;
|
|
|
|
void *addr, *addr2;
|
|
|
|
natural_t data;
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
/* Read data from PID's address space, from ADDR for DATA bytes. */
|
|
|
|
error_t read_data (task_t task, vm_address_t *ourpage, vm_size_t *size)
|
|
|
|
{
|
|
|
|
/* Read the pages containing the addressed range. */
|
|
|
|
error_t err;
|
|
|
|
*size = round_page (addr + data) - trunc_page (addr);
|
|
|
|
err = __vm_read (task, trunc_page (addr), *size, ourpage, size);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Fetch the thread port for PID's user thread. */
|
|
|
|
error_t fetch_user_thread (task_t task, thread_t *thread)
|
|
|
|
{
|
|
|
|
thread_t threadbuf[3], *threads = threadbuf;
|
|
|
|
mach_msg_type_number_t nthreads = 3, i;
|
|
|
|
error_t err = __task_threads (task, &threads, &nthreads);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
if (nthreads == 0)
|
|
|
|
return EINVAL;
|
|
|
|
*thread = threads[0]; /* Assume user thread is first. */
|
|
|
|
for (i = 1; i < nthreads; ++i)
|
|
|
|
__mach_port_deallocate (__mach_task_self (), threads[i]);
|
|
|
|
if (threads != threadbuf)
|
|
|
|
__vm_deallocate (__mach_task_self (),
|
|
|
|
(vm_address_t) threads, nthreads * sizeof threads[0]);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Fetch a thread state structure from PID and store it at ADDR. */
|
|
|
|
int get_regs (int flavor, mach_msg_type_number_t count)
|
|
|
|
{
|
|
|
|
error_t err;
|
|
|
|
task_t task = __pid2task (pid);
|
|
|
|
thread_t thread;
|
|
|
|
if (task == MACH_PORT_NULL)
|
|
|
|
return -1;
|
|
|
|
err = fetch_user_thread (task, &thread);
|
|
|
|
__mach_port_deallocate (__mach_task_self (), task);
|
|
|
|
if (!err)
|
|
|
|
err = __thread_get_state (thread, flavor, addr, &count);
|
|
|
|
__mach_port_deallocate (__mach_task_self (), thread);
|
|
|
|
return err ? __hurd_fail (err) : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
switch (request)
|
|
|
|
{
|
|
|
|
case PTRACE_TRACEME:
|
|
|
|
/* Make this process be traced. */
|
1996-05-04 09:46:57 +00:00
|
|
|
__sigfillset (&_hurdsig_traced);
|
1995-11-08 20:12:30 +00:00
|
|
|
__USEPORT (PROC, __proc_mark_traced (port));
|
1995-02-18 01:27:10 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case PTRACE_CONT:
|
|
|
|
va_start (ap, request);
|
|
|
|
pid = va_arg (ap, pid_t);
|
|
|
|
addr = va_arg (ap, void *);
|
|
|
|
data = va_arg (ap, int);
|
|
|
|
va_end (ap);
|
|
|
|
{
|
|
|
|
/* Send a DATA signal to PID, telling it to take the signal
|
|
|
|
normally even if it's traced. */
|
1995-11-18 10:00:22 +00:00
|
|
|
error_t err;
|
|
|
|
task_t task = __pid2task (pid);
|
1995-02-18 01:27:10 +00:00
|
|
|
if (task == MACH_PORT_NULL)
|
|
|
|
return -1;
|
|
|
|
if (data == SIGKILL)
|
|
|
|
err = __task_terminate (task);
|
|
|
|
else
|
|
|
|
{
|
1995-11-18 10:00:22 +00:00
|
|
|
if (addr != (void *) 1)
|
1995-02-18 01:27:10 +00:00
|
|
|
{
|
|
|
|
/* Move the user thread's PC to ADDR. */
|
|
|
|
thread_t thread;
|
|
|
|
err = fetch_user_thread (task, &thread);
|
|
|
|
if (!err)
|
|
|
|
{
|
|
|
|
struct machine_thread_state state;
|
|
|
|
mach_msg_type_number_t count = MACHINE_THREAD_STATE_COUNT;
|
|
|
|
err = __thread_get_state (thread,
|
|
|
|
MACHINE_THREAD_STATE_FLAVOR,
|
|
|
|
(natural_t *) &state, &count);
|
|
|
|
if (!err)
|
|
|
|
{
|
|
|
|
MACHINE_THREAD_STATE_SET_PC (&state, addr);
|
|
|
|
err = __thread_set_state (thread,
|
|
|
|
MACHINE_THREAD_STATE_FLAVOR,
|
|
|
|
(natural_t *) &state, count);
|
|
|
|
}
|
1995-11-18 10:00:22 +00:00
|
|
|
|
1995-02-18 01:27:10 +00:00
|
|
|
}
|
|
|
|
__mach_port_deallocate (__mach_task_self (), thread);
|
|
|
|
}
|
1995-11-18 10:00:22 +00:00
|
|
|
else
|
|
|
|
err = 0;
|
1995-02-18 01:27:10 +00:00
|
|
|
|
|
|
|
if (! err)
|
|
|
|
/* Tell the process to take the signal (or just resume if 0). */
|
1995-11-18 10:00:22 +00:00
|
|
|
err = HURD_MSGPORT_RPC
|
|
|
|
(__USEPORT (PROC, __proc_getmsgport (port, pid, &msgport)),
|
1996-05-04 09:46:57 +00:00
|
|
|
0, 0, __msg_sig_post_untraced (msgport, data, 0, task));
|
1995-02-18 01:27:10 +00:00
|
|
|
}
|
|
|
|
__mach_port_deallocate (__mach_task_self (), task);
|
|
|
|
return err ? __hurd_fail (err) : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
case PTRACE_KILL:
|
|
|
|
va_start (ap, request);
|
|
|
|
pid = va_arg (ap, pid_t);
|
|
|
|
va_end (ap);
|
|
|
|
/* SIGKILL always just terminates the task,
|
|
|
|
so normal kill is just the same when traced. */
|
2018-04-02 19:07:52 +00:00
|
|
|
return __kill (pid, SIGKILL);
|
1995-02-18 01:27:10 +00:00
|
|
|
|
|
|
|
case PTRACE_SINGLESTEP:
|
|
|
|
/* This is a machine-dependent kernel RPC on
|
|
|
|
machines that support it. Punt. */
|
2012-10-25 16:47:38 +00:00
|
|
|
return __hurd_fail (EOPNOTSUPP);
|
1995-02-18 01:27:10 +00:00
|
|
|
|
|
|
|
case PTRACE_ATTACH:
|
|
|
|
case PTRACE_DETACH:
|
|
|
|
va_start (ap, request);
|
|
|
|
pid = va_arg (ap, pid_t);
|
|
|
|
va_end (ap);
|
|
|
|
{
|
|
|
|
/* Tell PID to set or clear its trace bit. */
|
|
|
|
error_t err;
|
|
|
|
mach_port_t msgport;
|
|
|
|
task_t task = __pid2task (pid);
|
|
|
|
if (task == MACH_PORT_NULL)
|
|
|
|
return -1;
|
|
|
|
err = __USEPORT (PROC, __proc_getmsgport (port, pid, &msgport));
|
|
|
|
if (! err)
|
|
|
|
{
|
1996-05-04 09:46:57 +00:00
|
|
|
err = __msg_set_init_int (msgport, task, INIT_TRACEMASK,
|
2019-02-25 13:19:19 +00:00
|
|
|
request == PTRACE_DETACH ? 0
|
|
|
|
: ~(sigset_t) 0);
|
1995-02-18 01:27:10 +00:00
|
|
|
if (! err)
|
|
|
|
{
|
|
|
|
if (request == PTRACE_ATTACH)
|
|
|
|
/* Now stop the process. */
|
1996-05-04 09:46:57 +00:00
|
|
|
err = __msg_sig_post (msgport, SIGSTOP, 0, task);
|
1995-02-18 01:27:10 +00:00
|
|
|
else
|
|
|
|
/* Resume the process from tracing stop. */
|
1996-05-04 09:46:57 +00:00
|
|
|
err = __msg_sig_post_untraced (msgport, 0, 0, task);
|
1995-02-18 01:27:10 +00:00
|
|
|
}
|
|
|
|
__mach_port_deallocate (__mach_task_self (), msgport);
|
|
|
|
}
|
|
|
|
__mach_port_deallocate (__mach_task_self (), task);
|
|
|
|
return err ? __hurd_fail (err) : 0;
|
1995-11-18 10:00:22 +00:00
|
|
|
}
|
1995-02-18 01:27:10 +00:00
|
|
|
|
|
|
|
case PTRACE_PEEKTEXT:
|
|
|
|
case PTRACE_PEEKDATA:
|
|
|
|
va_start (ap, request);
|
|
|
|
pid = va_arg (ap, pid_t);
|
|
|
|
addr = va_arg (ap, void *);
|
|
|
|
va_end (ap);
|
|
|
|
{
|
|
|
|
/* Read the page (or two pages, if the word lies on a boundary)
|
|
|
|
containing the addressed word. */
|
|
|
|
error_t err;
|
|
|
|
vm_address_t ourpage;
|
|
|
|
vm_size_t size;
|
|
|
|
natural_t word;
|
|
|
|
task_t task = __pid2task (pid);
|
|
|
|
if (task == MACH_PORT_NULL)
|
|
|
|
return -1;
|
|
|
|
data = sizeof word;
|
|
|
|
ourpage = 0;
|
|
|
|
size = 0;
|
|
|
|
err = read_data (task, &ourpage, &size);
|
|
|
|
__mach_port_deallocate (__mach_task_self (), task);
|
|
|
|
if (err)
|
|
|
|
return __hurd_fail (err);
|
|
|
|
word = *(natural_t *) ((vm_address_t) addr - trunc_page (addr)
|
|
|
|
+ ourpage);
|
|
|
|
__vm_deallocate (__mach_task_self (), ourpage, size);
|
|
|
|
return word;
|
|
|
|
}
|
|
|
|
|
|
|
|
case PTRACE_PEEKUSER:
|
|
|
|
case PTRACE_POKEUSER:
|
|
|
|
/* U area, what's that? */
|
2012-10-25 16:47:38 +00:00
|
|
|
return __hurd_fail (EOPNOTSUPP);
|
1995-02-18 01:27:10 +00:00
|
|
|
|
|
|
|
case PTRACE_GETREGS:
|
|
|
|
case PTRACE_SETREGS:
|
|
|
|
va_start (ap, request);
|
|
|
|
pid = va_arg (ap, pid_t);
|
|
|
|
addr = va_arg (ap, void *);
|
|
|
|
va_end (ap);
|
|
|
|
return get_regs (MACHINE_THREAD_STATE_FLAVOR,
|
|
|
|
MACHINE_THREAD_STATE_COUNT);
|
|
|
|
|
|
|
|
case PTRACE_GETFPREGS:
|
|
|
|
case PTRACE_SETFPREGS:
|
|
|
|
va_start (ap, request);
|
|
|
|
pid = va_arg (ap, pid_t);
|
|
|
|
addr = va_arg (ap, void *);
|
|
|
|
va_end (ap);
|
|
|
|
#ifdef MACHINE_THREAD_FLOAT_STATE_FLAVOR
|
|
|
|
return get_regs (MACHINE_THREAD_FLOAT_STATE_FLAVOR,
|
|
|
|
MACHINE_THREAD_FLOAT_STATE_COUNT);
|
|
|
|
#else
|
2012-10-25 16:47:38 +00:00
|
|
|
return __hurd_fail (EOPNOTSUPP);
|
1995-02-18 01:27:10 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
case PTRACE_GETFPAREGS:
|
|
|
|
case PTRACE_SETFPAREGS:
|
|
|
|
va_start (ap, request);
|
|
|
|
pid = va_arg (ap, pid_t);
|
|
|
|
addr = va_arg (ap, void *);
|
|
|
|
va_end (ap);
|
|
|
|
#ifdef MACHINE_THREAD_FPA_STATE_FLAVOR
|
|
|
|
return get_regs (MACHINE_THREAD_FPA_STATE_FLAVOR,
|
|
|
|
MACHINE_THREAD_FPA_STATE_COUNT);
|
|
|
|
#else
|
2012-10-25 16:47:38 +00:00
|
|
|
return __hurd_fail (EOPNOTSUPP);
|
1995-02-18 01:27:10 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
case PTRACE_POKETEXT:
|
|
|
|
case PTRACE_POKEDATA:
|
|
|
|
va_start (ap, request);
|
|
|
|
pid = va_arg (ap, pid_t);
|
|
|
|
addr = va_arg (ap, void *);
|
|
|
|
data = va_arg (ap, int);
|
|
|
|
va_end (ap);
|
|
|
|
{
|
|
|
|
/* Read the page (or two pages, if the word lies on a boundary)
|
|
|
|
containing the addressed word. */
|
|
|
|
error_t err;
|
|
|
|
vm_address_t ourpage;
|
|
|
|
vm_size_t size;
|
|
|
|
task_t task = __pid2task (pid);
|
|
|
|
if (task == MACH_PORT_NULL)
|
|
|
|
return -1;
|
|
|
|
data = sizeof (natural_t);
|
|
|
|
ourpage = 0;
|
|
|
|
size = 0;
|
|
|
|
err = read_data (task, &ourpage, &size);
|
|
|
|
|
|
|
|
if (!err)
|
|
|
|
{
|
|
|
|
/* Now modify the specified word and write the page back. */
|
|
|
|
*(natural_t *) ((vm_address_t) addr - trunc_page (addr)
|
|
|
|
+ ourpage) = data;
|
|
|
|
err = __vm_write (task, trunc_page (addr), ourpage, size);
|
|
|
|
__vm_deallocate (__mach_task_self (), ourpage, size);
|
|
|
|
}
|
|
|
|
|
|
|
|
__mach_port_deallocate (__mach_task_self (), task);
|
|
|
|
return err ? __hurd_fail (err) : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
case PTRACE_READDATA:
|
|
|
|
case PTRACE_READTEXT:
|
|
|
|
va_start (ap, request);
|
|
|
|
pid = va_arg (ap, pid_t);
|
|
|
|
addr = va_arg (ap, void *);
|
|
|
|
data = va_arg (ap, int);
|
|
|
|
addr2 = va_arg (ap, void *);
|
|
|
|
va_end (ap);
|
|
|
|
{
|
|
|
|
error_t err;
|
|
|
|
vm_address_t ourpage;
|
|
|
|
vm_size_t size;
|
|
|
|
task_t task = __pid2task (pid);
|
|
|
|
if (task == MACH_PORT_NULL)
|
|
|
|
return -1;
|
|
|
|
if (((vm_address_t) addr2 + data) % __vm_page_size == 0)
|
|
|
|
{
|
|
|
|
/* Perhaps we can write directly to the user's buffer. */
|
|
|
|
ourpage = (vm_address_t) addr2;
|
|
|
|
size = data;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ourpage = 0;
|
|
|
|
size = 0;
|
|
|
|
}
|
|
|
|
err = read_data (task, &ourpage, &size);
|
|
|
|
__mach_port_deallocate (__mach_task_self (), task);
|
|
|
|
if (!err && ourpage != (vm_address_t) addr2)
|
|
|
|
{
|
|
|
|
memcpy (addr2, (void *) ourpage, data);
|
|
|
|
__vm_deallocate (__mach_task_self (), ourpage, size);
|
|
|
|
}
|
|
|
|
return err ? __hurd_fail (err) : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
case PTRACE_WRITEDATA:
|
|
|
|
case PTRACE_WRITETEXT:
|
|
|
|
va_start (ap, request);
|
|
|
|
pid = va_arg (ap, pid_t);
|
|
|
|
addr = va_arg (ap, void *);
|
|
|
|
data = va_arg (ap, int);
|
|
|
|
addr2 = va_arg (ap, void *);
|
|
|
|
va_end (ap);
|
|
|
|
{
|
|
|
|
error_t err;
|
|
|
|
vm_address_t ourpage;
|
|
|
|
vm_size_t size;
|
|
|
|
task_t task = __pid2task (pid);
|
|
|
|
if (task == MACH_PORT_NULL)
|
|
|
|
return -1;
|
2019-02-26 15:01:50 +00:00
|
|
|
if ((vm_address_t) addr % __vm_page_size == 0
|
|
|
|
&& (vm_address_t) data % __vm_page_size == 0)
|
1995-02-18 01:27:10 +00:00
|
|
|
{
|
|
|
|
/* Writing whole pages; can go directly from the user's buffer. */
|
|
|
|
ourpage = (vm_address_t) addr2;
|
|
|
|
size = data;
|
|
|
|
err = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Read the task's pages and modify our own copy. */
|
|
|
|
ourpage = 0;
|
|
|
|
size = 0;
|
|
|
|
err = read_data (task, &ourpage, &size);
|
|
|
|
if (!err)
|
|
|
|
memcpy ((void *) ((vm_address_t) addr - trunc_page (addr)
|
|
|
|
+ ourpage),
|
|
|
|
addr2,
|
|
|
|
data);
|
|
|
|
}
|
|
|
|
if (!err)
|
|
|
|
/* Write back the modified pages. */
|
|
|
|
err = __vm_write (task, trunc_page (addr), ourpage, size);
|
|
|
|
__mach_port_deallocate (__mach_task_self (), task);
|
|
|
|
return err ? __hurd_fail (err) : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
errno = EINVAL;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|