glibc/sysdeps/mach/hurd/ptrace.c
Samuel Thibault dba2bdbe75 hurd: Avoid some PLTs in libc and librt
* hurd/hurdauth.c (_S_msg_add_auth): Call __vm_allocate and
	__vm_deallocate instead of vm_allocate and vm_deallocate.
	* hurd/hurdmsg.c (_S_msg_set_env_variable): Call __setenv instead of
	setenv.
	* hurd/hurdprio.c (_hurd_priority_which_map): Call __geteuid instead
	of geteuid.
	* hurd/path-lookup.c (file_name_path_scan): Call __strdup instead of
	strdup.
	* hurd/siginfo.c: Include <libioP.h>.
	(_hurd_siginfo_handler): Call _IO_puts instead of puts.
	* hurd/xattr.c (_hurd_xattr_get, _hurd_xattr_set): Call __munmap instead of
	munmap.
	* io/fts.c (fts_build): Call __dirfd instead of dirfd.
	* mach/devstream.c: Include <libioP.h>.
	(dealloc_ref): Call __mach_port_deallocate instead of
	mach_port_deallocate.
	(mach_open_devstream): Call _IO_fopencookie instead of fopencookie.
	Call __mach_port_deallocate instead of mach_port_deallocate.
	* stdlib/canonicalize.c (__realpath): Call __pathconf instead of
	pathconf.
	* sysdeps/mach/hurd/ifreq.c (__ifreq): Call __munmap instead of
	munmap.
	* sysdeps/mach/hurd/ifreq.h (__if_freereq): Likewise.
	* sysdeps/mach/hurd/ptrace.c (ptrace): Call __kill instead of kill.
	* sysdeps/mach/hurd/sendfile64.c (sendfile64): Call __munmap instead
	of munmap.
	* sysdeps/mach/hurd/socketpair.c (__socketpair): Call __close instead
	of close.
	* sysdeps/posix/clock_getres.c (realtime_getres): Call __sysconf
	instead of sysconf.
	* sysdeps/pthread/timer_gettime.c (timer_gettime): Call
	__clock_gettime instead of clock_gettime.
	* sysdeps/pthread/timer_routines.c (thread_func): Likewise.
	* sysdeps/pthread/timer_settime.c (timer_settime): Likewise.
	* sysdeps/unix/bsd/gtty.c (gtty): Call __ioctl instead of ioctl.
	* sysdeps/unix/bsd/stty.c (stty): Likewise.
	* sysdeps/unix/bsd/tcflow.c (tcflow): Call __tcgetattr instead of
	tcgetattr.
	* sysdeps/unix/clock_nanosleep.c (__clock_nanosleep): Call
	__clock_gettime and __nanosleep instead of clock_gettime and
	nanosleep.
2018-04-02 19:07:52 +00:00

386 lines
11 KiB
C

/* Process tracing interface `ptrace' for GNU Hurd.
Copyright (C) 1991-2018 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
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.
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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
#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. */
__sigfillset (&_hurdsig_traced);
__USEPORT (PROC, __proc_mark_traced (port));
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. */
error_t err;
task_t task = __pid2task (pid);
if (task == MACH_PORT_NULL)
return -1;
if (data == SIGKILL)
err = __task_terminate (task);
else
{
if (addr != (void *) 1)
{
/* 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);
}
}
__mach_port_deallocate (__mach_task_self (), thread);
}
else
err = 0;
if (! err)
/* Tell the process to take the signal (or just resume if 0). */
err = HURD_MSGPORT_RPC
(__USEPORT (PROC, __proc_getmsgport (port, pid, &msgport)),
0, 0, __msg_sig_post_untraced (msgport, data, 0, task));
}
__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. */
return __kill (pid, SIGKILL);
case PTRACE_SINGLESTEP:
/* This is a machine-dependent kernel RPC on
machines that support it. Punt. */
return __hurd_fail (EOPNOTSUPP);
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)
{
err = __msg_set_init_int (msgport, task, INIT_TRACEMASK,
request == PTRACE_DETACH ? 0 :
~(sigset_t) 0);
if (! err)
{
if (request == PTRACE_ATTACH)
/* Now stop the process. */
err = __msg_sig_post (msgport, SIGSTOP, 0, task);
else
/* Resume the process from tracing stop. */
err = __msg_sig_post_untraced (msgport, 0, 0, task);
}
__mach_port_deallocate (__mach_task_self (), msgport);
}
__mach_port_deallocate (__mach_task_self (), task);
return err ? __hurd_fail (err) : 0;
}
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? */
return __hurd_fail (EOPNOTSUPP);
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
return __hurd_fail (EOPNOTSUPP);
#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
return __hurd_fail (EOPNOTSUPP);
#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;
if ((vm_address_t) addr % __vm_page_size == 0 &&
(vm_address_t) data % __vm_page_size == 0)
{
/* 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;
}