/* Define to 1 if you have the <string.h> header file. */
#undef HAVE_STRING_H
+/* Define to 1 if you have the <sys/param.h> header file. */
+#undef HAVE_SYS_PARAM_H
+
/* Define to 1 if you have the <sys/stat.h> header file. */
#undef HAVE_SYS_STAT_H
/* Define to the version of this package. */
#undef PACKAGE_VERSION
+/* Define to number of the `readlinkat' system call. */
+#undef READLINKAT_SYSCALL
+
+/* Define to number of the `readlink' system call. */
+#undef READLINK_SYSCALL
+
+/* Define to 1 if the library is used within a signal handler. */
+#undef REENTRANT
+
/* Define to 1 if the stack grows downwards. */
#undef STACK_GROWS_DOWNWARDS
/* Define to register holding arg1 to system calls. */
#undef SYSCALL_ARG1_REG
+/* Define to register holding arg2 to system calls. */
+#undef SYSCALL_ARG2_REG
+
+/* Define to register holding arg3 to system calls. */
+#undef SYSCALL_ARG3_REG
+
/* Define to register holding arg0 to system calls. */
#undef SYSCALL_ARG_REG
# define __bool_true_false_are_defined 1
#endif
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif /* HAVE_SYS_PARAM_H */
+
+#ifndef MAX
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif /* MAX */
+
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif /* MIN */
+
You should have received a copy of the GNU General Public License
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */])
+AC_ARG_WITH([reentrancy],
+ [AS_HELP_STRING([--with-reentrancy],
+ [Generate library which can be used within a signal handler.])],
+ [AC_DEFINE([REENTRANT], [1])])
+
AC_PROG_CC
AC_PROG_CPP
AC_PROG_INSTALL
AC_HEADER_STDBOOL
AC_CHECK_FUNCS([getpagesize stpcpy stpncpy])
AC_CHECK_DECLS([stpcpy, stpncpy])
+AC_CHECK_HEADERS([sys/param.h]) dnl for MIN and MAX
AH_BOTTOM([
#ifdef HAVE_STDBOOL_H
# define true 1
# define __bool_true_false_are_defined 1
#endif
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif /* HAVE_SYS_PARAM_H */
+
+#ifndef MAX
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif /* MAX */
+
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif /* MIN */
])
AC_C_BIGENDIAN
AH_TEMPLATE([SYSCALL_NUM_REG], [Define to register holding the system call number.])
AH_TEMPLATE([SYSCALL_ARG_REG], [Define to register holding arg0 to system calls.])
AH_TEMPLATE([SYSCALL_ARG1_REG], [Define to register holding arg1 to system calls.])
+AH_TEMPLATE([SYSCALL_ARG2_REG], [Define to register holding arg2 to system calls.])
+AH_TEMPLATE([SYSCALL_ARG3_REG], [Define to register holding arg3 to system calls.])
AH_TEMPLATE([SYSCALL_RET_REG], [Define to register holding value of system calls.])
AH_TEMPLATE([STACK_POINTER], [Define to register holding the stack pointer.])
AH_TEMPLATE([EXEC_SYSCALL], [Define to number of the `exec' system call.])
AH_TEMPLATE([INTERPRETER_BASE], [Virtual address for loading PIC interpreters])
AH_TEMPLATE([CLONE_SYSCALL], [Define to number of the `clone' system call.])
AH_TEMPLATE([CLONE3_SYSCALL], [Define to number of the `clone3' system call.])
+AH_TEMPLATE([READLINK_SYSCALL], [Define to number of the `readlink' system call.])
+AH_TEMPLATE([READLINKAT_SYSCALL], [Define to number of the `readlinkat' system call.])
+AH_TEMPLATE([REENTRANT], [Define to 1 if the library is used within a signal handler.])
AC_CANONICAL_HOST
AC_DEFINE([SYSCALL_RET_REG], [rax])
AC_DEFINE([SYSCALL_ARG_REG], [rdi])
AC_DEFINE([SYSCALL_ARG1_REG], [rsi])
+ AC_DEFINE([SYSCALL_ARG2_REG], [rdx])
+ AC_DEFINE([SYSCALL_ARG3_REG], [r10])
AC_DEFINE([STACK_POINTER], [rsp])
AC_DEFINE([EXEC_SYSCALL], [__NR_execve])
AC_DEFINE([USER_WORD], [uintptr_t])
AC_DEFINE([INTERPRETER_BASE], [0x600000000000])
AC_DEFINE([STACK_GROWS_DOWNWARDS], [1])
AC_DEFINE([CLONE_SYSCALL], [__NR_clone])
+ AC_DEFINE([READLINK_SYSCALL], [__NR_readlink])
+ AC_DEFINE([READLINKAT_SYSCALL], [__NR_readlinkat])
exec_CHECK_LINUX_CLONE3
# Make sure the loader doesn't conflict with other position
# dependent code.
AC_DEFINE([SYSCALL_RET_REG], [eax])
AC_DEFINE([SYSCALL_ARG_REG], [ebx])
AC_DEFINE([SYSCALL_ARG1_REG], [ecx])
+ AC_DEFINE([SYSCALL_ARG2_REG], [edx])
+ AC_DEFINE([SYSCALL_ARG3_REG], [esi])
AC_DEFINE([STACK_POINTER], [esp])
AC_DEFINE([EXEC_SYSCALL], [__NR_execve])
AC_DEFINE([USER_WORD], [uintptr_t])
AC_DEFINE([INTERPRETER_BASE], [0xaf000000])
AC_DEFINE([STACK_GROWS_DOWNWARDS], [1])
AC_DEFINE([CLONE_SYSCALL], [__NR_clone])
+ AC_DEFINE([READLINK_SYSCALL], [__NR_readlink])
+ AC_DEFINE([READLINKAT_SYSCALL], [__NR_readlinkat])
exec_CHECK_LINUX_CLONE3
# Make sure the loader doesn't conflict with other position
# dependent code.
AC_DEFINE([SYSCALL_RET_REG], [[regs[0]]])
AC_DEFINE([SYSCALL_ARG_REG], [[regs[0]]])
AC_DEFINE([SYSCALL_ARG1_REG], [[regs[1]]])
+ AC_DEFINE([SYSCALL_ARG2_REG], [[regs[2]]])
+ AC_DEFINE([SYSCALL_ARG3_REG], [[regs[3]]])
AC_DEFINE([STACK_POINTER], [sp])
AC_DEFINE([EXEC_SYSCALL], [__NR_execve])
AC_DEFINE([USER_WORD], [uintptr_t])
AC_DEFINE([INTERPRETER_BASE], [0x3f00000000])
AC_DEFINE([STACK_GROWS_DOWNWARDS], [1])
AC_DEFINE([CLONE_SYSCALL], [__NR_clone])
+ # Note that aarch64 has no `readlink'.
+ AC_DEFINE([READLINKAT_SYSCALL], [__NR_readlinkat])
exec_CHECK_LINUX_CLONE3
# Make sure the loader doesn't conflict with other position
# dependent code. ARM places rather significant restrictions on
AC_DEFINE([SYSCALL_RET_REG], [[uregs[0]]])
AC_DEFINE([SYSCALL_ARG_REG], [[uregs[0]]])
AC_DEFINE([SYSCALL_ARG1_REG], [[uregs[1]]])
+ AC_DEFINE([SYSCALL_ARG2_REG], [[uregs[2]]])
+ AC_DEFINE([SYSCALL_ARG3_REG], [[uregs[3]]])
AC_DEFINE([STACK_POINTER], [[uregs[13]]])
AC_DEFINE([EXEC_SYSCALL], [__NR_execve])
AC_DEFINE([USER_WORD], [uintptr_t])
AC_DEFINE([INTERPRETER_BASE], [0x1f000000])
AC_DEFINE([STACK_GROWS_DOWNWARDS], [1])
AC_DEFINE([CLONE_SYSCALL], [__NR_clone])
+ AC_DEFINE([READLINK_SYSCALL], [__NR_readlink])
+ AC_DEFINE([READLINKAT_SYSCALL], [__NR_readlinkat])
exec_CHECK_LINUX_CLONE3
LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0x20000000"
exec_loader=loader-armeabi.s],
AC_DEFINE([SYSCALL_RET_REG], [[uregs[0]]])
AC_DEFINE([SYSCALL_ARG_REG], [[uregs[0]]])
AC_DEFINE([SYSCALL_ARG1_REG], [[uregs[1]]])
+ AC_DEFINE([SYSCALL_ARG2_REG], [[uregs[2]]])
+ AC_DEFINE([SYSCALL_ARG3_REG], [[uregs[3]]])
AC_DEFINE([STACK_POINTER], [[uregs[13]]])
AC_DEFINE([EXEC_SYSCALL], [__NR_execve])
AC_DEFINE([USER_WORD], [uintptr_t])
AC_DEFINE([INTERPRETER_BASE], [0x1f000000])
AC_DEFINE([STACK_GROWS_DOWNWARDS], [1])
AC_DEFINE([CLONE_SYSCALL], [__NR_clone])
+ AC_DEFINE([READLINK_SYSCALL], [__NR_readlink])
+ AC_DEFINE([READLINKAT_SYSCALL], [__NR_readlinkat])
exec_CHECK_LINUX_CLONE3
LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0x20000000"
exec_loader=loader-armeabi.s],
AC_DEFINE([SYSCALL_RET_REG], [[gregs[4]]]) # a0
AC_DEFINE([SYSCALL_ARG_REG], [[gregs[4]]]) # a0
AC_DEFINE([SYSCALL_ARG1_REG], [[gregs[5]]]) # a1
+ AC_DEFINE([SYSCALL_ARG2_REG], [[gregs[4]]]) # a2
+ AC_DEFINE([SYSCALL_ARG3_REG], [[gregs[5]]]) # a3
AC_DEFINE([STACK_POINTER], [[gregs[29]]]) # sp
AC_DEFINE([EXEC_SYSCALL], [__NR_execve])
AC_DEFINE([USER_WORD], [uintptr_t])
AC_DEFINE([INTERPRETER_BASE], [0x1f000000])
AC_DEFINE([STACK_GROWS_DOWNWARDS], [1])
AC_DEFINE([CLONE_SYSCALL], [__NR_clone])
+ AC_DEFINE([READLINK_SYSCALL], [__NR_readlink])
+ AC_DEFINE([READLINKAT_SYSCALL], [__NR_readlinkat])
AC_CHECK_DECL([_MIPS_SIM], [exec_CHECK_MIPS_NABI],
[AC_MSG_ERROR([_MIPS_SIM could not be determined]),
[[
AC_DEFINE([SYSCALL_RET_REG], [[gregs[4]]]) # a0
AC_DEFINE([SYSCALL_ARG_REG], [[gregs[4]]]) # a0
AC_DEFINE([SYSCALL_ARG1_REG], [[gregs[5]]]) # a1
+ AC_DEFINE([SYSCALL_ARG2_REG], [[gregs[4]]]) # a2
+ AC_DEFINE([SYSCALL_ARG3_REG], [[gregs[5]]]) # a3
AC_DEFINE([STACK_POINTER], [[gregs[29]]]) # sp
AC_DEFINE([EXEC_SYSCALL], [__NR_execve])
AC_DEFINE([USER_WORD], [uintptr_t])
AC_DEFINE([INTERPRETER_BASE], [0x3f00000000])
AC_DEFINE([STACK_GROWS_DOWNWARDS], [1])
AC_DEFINE([CLONE_SYSCALL], [__NR_clone])
+ AC_DEFINE([READLINK_SYSCALL], [__NR_readlink])
+ AC_DEFINE([READLINKAT_SYSCALL], [__NR_readlinkat])
AC_CACHE_CHECK([whether as understands `daddi'],
[exec_cv_as_daddi],
[exec_cv_as_daddi=no
#include <sys/param.h>
#include <sys/mman.h>
-#ifndef MIN
-#define MIN(a, b) ((a) < (b) ? (a) : (b))
-#endif /* MIN */
-
-#ifndef MAX
-#define MAX(a, b) ((a) < (b) ? (b) : (a))
-#endif /* MAX */
-
#include "exec.h"
#if defined __mips__ && !defined MIPS_NABI
with #!; in that case, find the program to open and use that
instead.
+ If REENTRANT is not defined, NAME is actually a buffer of size
+ PATH_MAX + 80. In that case, copy over the file name actually
+ opened.
+
Next, read the executable header, and add the necessary memory
mappings for each file. Finally, return the action data and its
size in *SIZE.
Value is NULL upon failure, with errno set accordingly. */
char *
-exec_0 (const char *name, struct exec_tracee *tracee,
+exec_0 (char *name, struct exec_tracee *tracee,
size_t *size, USER_REGS_STRUCT *regs)
{
int fd, rc, i;
#if defined __mips__ && !defined MIPS_NABI
int fpu_mode;
#endif /* defined __mips__ && !defined MIPS_NABI */
- char buffer[PATH_MAX + 80], *rewrite;
+ char buffer[80], buffer1[PATH_MAX + 80], *rewrite;
+ ssize_t link_size;
size_t remaining;
/* If name is not absolute, then make it relative to TRACEE's
{
/* Clear `buffer'. */
memset (buffer, 0, sizeof buffer);
+ memset (buffer1, 0, sizeof buffer);
/* Copy over /proc, the PID, and /cwd/. */
rewrite = stpcpy (buffer, "/proc/");
rewrite = format_pid (rewrite, tracee->pid);
- rewrite = stpcpy (rewrite, "/cwd/");
+ stpcpy (rewrite, "/cwd");
+
+ /* Resolve this symbolic link. */
+
+ link_size = readlink (buffer, buffer1,
+ PATH_MAX + 1);
+
+ if (link_size < 0)
+ return NULL;
+
+ /* Check that the name is a reasonable size. */
+
+ if (link_size > PATH_MAX)
+ {
+ /* The name is too long. */
+ errno = ENAMETOOLONG;
+ return NULL;
+ }
+
+ /* Add a directory separator if necessary. */
+
+ if (!link_size || buffer1[link_size - 1] != '/')
+ buffer1[link_size] = '/', link_size++;
- /* Make sure there is enough free space. */
- remaining = buffer + sizeof buffer - rewrite - 1;
+ rewrite = buffer1 + link_size;
+ remaining = buffer1 + sizeof buffer1 - rewrite - 1;
rewrite = stpncpy (rewrite, name, remaining);
- /* Replace name with buffer. */
- name = buffer;
+ /* Replace name with buffer1. */
+#ifndef REENTRANT
+ strcpy (name, buffer1);
+#endif /* REENTRANT */
}
fd = open (name, O_RDONLY);
/* Whether or not the tracee is currently waiting for a system call
to complete. */
bool waiting_for_syscall;
+
+#ifndef REENTRANT
+ /* Name of the executable being run. */
+ char *exec_file;
+#endif /* !REENTRANT */
};
\f
/* Defined in exec.c. */
-extern char *exec_0 (const char *, struct exec_tracee *,
+extern char *exec_0 (char *, struct exec_tracee *,
size_t *, USER_REGS_STRUCT *);
\f
/* Link the tracee onto the list of free tracees. */
tracee->next = free_tracees;
+
+#ifndef REENTRANT
+ /* Free the exec file, if any. */
+ free (tracee->exec_file);
+ tracee->exec_file = NULL;
+#endif /* REENTRANT */
+
free_tracees = tracee;
return;
static int
handle_exec (struct exec_tracee *tracee, USER_REGS_STRUCT *regs)
{
- char buffer[PATH_MAX], *area;
+ char buffer[PATH_MAX + 80], *area;
USER_REGS_STRUCT original;
size_t size, loader_size;
USER_WORD loader, size1, sp;
return 1;
}
+#ifndef REENTRANT
+ /* Now that the loader has started, record the value to use for
+ /proc/self/exe. Don't give up just because strdup fails.
+
+ Note that exec_0 copies the absolute file name into buffer. */
+
+ if (tracee->exec_file)
+ free (tracee->exec_file);
+ tracee->exec_file = strdup (buffer);
+#endif /* REENTRANT */
+
again:
rc = waitpid (tracee->pid, &wstatus, __WALL);
if (rc == -1 && errno == EINTR)
return 3;
}
+/* Handle a `readlink' or `readlinkat' system call.
+
+ CALLNO is the system call number, and REGS are the current user
+ registers of the TRACEE.
+
+ If the first argument of a `readlinkat' system call is AT_FDCWD,
+ and the file name specified in either a `readlink' or `readlinkat'
+ system call is `/proc/self/exe', write the name of the executable
+ being run into the buffer specified in the system call.
+
+ Return the number of bytes written to the tracee's buffer in
+ *RESULT.
+
+ Value is 0 upon success. Value is 1 upon failure, and 2 if the
+ system call has been emulated. */
+
+static int
+handle_readlinkat (USER_WORD callno, USER_REGS_STRUCT *regs,
+ struct exec_tracee *tracee, USER_WORD *result)
+{
+#ifdef REENTRANT
+ /* readlinkat cannot be handled specially when the library is built
+ to be reentrant, as the file name information cannot be
+ recorded. */
+ return 0;
+#else /* !REENTRANT */
+
+ char buffer[PATH_MAX + 1];
+ USER_WORD address, return_buffer, size;
+ size_t length;
+
+ /* Read the file name. */
+
+#ifdef READLINK_SYSCALL
+ if (callno == READLINK_SYSCALL)
+ {
+ address = regs->SYSCALL_ARG_REG;
+ return_buffer = regs->SYSCALL_ARG1_REG;
+ size = regs->SYSCALL_ARG2_REG;
+ }
+ else
+#endif /* READLINK_SYSCALL */
+ {
+ address = regs->SYSCALL_ARG1_REG;
+ return_buffer = regs->SYSCALL_ARG2_REG;
+ size = regs->SYSCALL_ARG3_REG;
+ }
+
+ read_memory (tracee, buffer, PATH_MAX, address);
+
+ /* Make sure BUFFER is NULL terminated. */
+
+ if (!memchr (buffer, '\0', PATH_MAX))
+ {
+ errno = ENAMETOOLONG;
+ return 1;
+ }
+
+ /* Now check if the caller is looking for /proc/self/exe.
+
+ dirfd can be ignored, as for now only absolute file names are
+ handled. FIXME. */
+
+ if (strcmp (buffer, "/proc/self/exe") || !tracee->exec_file)
+ return 0;
+
+ /* Copy over tracee->exec_file. Truncate it to PATH_MAX, length, or
+ size, whichever is less. */
+
+ length = strlen (tracee->exec_file);
+ length = MIN (size, MIN (PATH_MAX, length));
+ strncpy (buffer, tracee->exec_file, length);
+
+ if (user_copy (tracee, (unsigned char *) buffer,
+ return_buffer, length))
+ {
+ errno = EIO;
+ return 1;
+ }
+
+ *result = length;
+ return 2;
+#endif /* REENTRANT */
+}
+
/* Process the system call at which TRACEE is stopped. If the system
call is not known or not exec, send TRACEE on its way. Otherwise,
rewrite it to load the loader and perform an appropriate action. */
#ifdef __aarch64__
USER_WORD old_w1, old_w2;
#endif /* __aarch64__ */
+ USER_WORD result;
+ bool reporting_error;
#ifdef __aarch64__
rc = aarch64_get_regs (tracee->pid, ®s);
break;
+#ifdef READLINK_SYSCALL
+ case READLINK_SYSCALL:
+#endif /* READLINK_SYSCALL */
+ case READLINKAT_SYSCALL:
+
+ /* Handle this readlinkat system call. */
+ rc = handle_readlinkat (callno, ®s, tracee,
+ &result);
+
+ /* rc means the same as in `handle_exec'. */
+
+ if (rc == 1)
+ goto report_syscall_error;
+ else if (rc == 2)
+ goto emulate_syscall;
+
+ /* Fallthrough. */
+
default:
/* Don't wait for the system call to finish; instead, the system
will DTRT upon the next call to PTRACE_SYSCALL after the
return;
report_syscall_error:
- /* Reporting an error works by setting the system call number to -1,
- letting it continue, and then substituting errno for ENOSYS.
+ reporting_error = true;
+ goto common;
+
+ emulate_syscall:
+ reporting_error = false;
+ common:
+
+ /* Reporting an error or emulating a system call works by setting
+ the system call number to -1, letting it continue, and then
+ substituting errno for ENOSYS in the case of an error.
Make sure that the stack pointer is restored to its original
position upon exit, or bad things can happen. */
/* The process has been killed in response to a signal. In this
case, simply unlink the tracee and return. */
remove_tracee (tracee);
- else
+ else if (reporting_error)
{
#ifdef __mips__
/* MIPS systems place errno in v0 and set a3 to 1. */
ptrace (PTRACE_SETREGS, tracee->pid, NULL, ®s);
#endif /* __aarch64__ */
+ /* Now wait for the next system call to happen. */
+ ptrace (PTRACE_SYSCALL, tracee->pid, NULL, NULL);
+ }
+ else
+ {
+ /* No error is being reported. Return the result in the
+ appropriate registers. */
+
+#ifdef __mips__
+ /* MIPS systems place errno in v0 and set a3 to 1. */
+ regs.gregs[2] = result;
+ regs.gregs[7] = 0;
+#else /* !__mips__ */
+ regs.SYSCALL_RET_REG = result;
+#endif /* __mips__ */
+
+ /* Report errno. */
+#ifdef __aarch64__
+ /* Restore x1 and x2. x0 is clobbered by errno. */
+ regs.regs[1] = old_w1;
+ regs.regs[2] = old_w2;
+ aarch64_set_regs (tracee->pid, ®s, false);
+#else /* !__aarch64__ */
+ ptrace (PTRACE_SETREGS, tracee->pid, NULL, ®s);
+#endif /* __aarch64__ */
+
/* Now wait for the next system call to happen. */
ptrace (PTRACE_SYSCALL, tracee->pid, NULL, NULL);
}
tracee->pid = pid;
tracee->next = tracing_processes;
tracee->waiting_for_syscall = false;
+#ifndef REENTRANT
+ tracee->exec_file = NULL;
+#endif /* REENTRANT */
tracing_processes = tracee;
return 0;
}