summaryrefslogtreecommitdiff
path: root/exec
diff options
context:
space:
mode:
authorPo Lu <luangruo@yahoo.com>2023-05-02 20:45:57 +0800
committerPo Lu <luangruo@yahoo.com>2023-05-02 20:45:57 +0800
commitc47716f95b8fda9438047d2683a415a65c18ecbd (patch)
tree947c45293556f41ce02426889890b3442f2b7b36 /exec
parentf4512cca0b996e5343ebe57511f45a29f64c4a8e (diff)
downloademacs-c47716f95b8fda9438047d2683a415a65c18ecbd.tar.gz
Update Android port
* exec/config.h.in (__bool_true_false_are_defined): * exec/configure.ac (REENTRANT): New definition. (READLINKAT_SYSCALL, READLINK_SYSCALL): New defines. Set on all hosts. * exec/exec.c (MIN, MAX): Remove redundant declarations. Move to config.h. (exec_0): Copy name of executable into NAME when !REENTRANT. * exec/exec.h (struct exec_tracee): New struct `exec_file'. * exec/trace.c (remove_tracee, handle_exec, handle_readlinkat) (process_system_call, after_fork): Handle readlinkat system calls.
Diffstat (limited to 'exec')
-rw-r--r--exec/config.h.in30
-rw-r--r--exec/configure.ac51
-rw-r--r--exec/exec.c52
-rw-r--r--exec/exec.h7
-rw-r--r--exec/trace.c168
5 files changed, 288 insertions, 20 deletions
diff --git a/exec/config.h.in b/exec/config.h.in
index d8c225b631d..c360e54a4ba 100644
--- a/exec/config.h.in
+++ b/exec/config.h.in
@@ -76,6 +76,9 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
/* 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
@@ -115,6 +118,15 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
/* 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
@@ -129,6 +141,12 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
/* 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
@@ -217,3 +235,15 @@ typedef bool _Bool;
# 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 */
+
diff --git a/exec/configure.ac b/exec/configure.ac
index f3eef60173c..b948e184896 100644
--- a/exec/configure.ac
+++ b/exec/configure.ac
@@ -42,6 +42,11 @@ General Public License for more details.
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
@@ -56,6 +61,7 @@ AC_TYPE_PID_T
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
@@ -73,6 +79,18 @@ typedef bool _Bool;
# 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
@@ -83,6 +101,8 @@ AH_TEMPLATE([USER_REGS_STRUCT], [Define to structure holding user registers.])
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.])
@@ -94,6 +114,9 @@ AH_TEMPLATE([EXECUTABLE_BASE], [Virtual address for loading PIC executables])
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
@@ -206,6 +229,8 @@ AS_CASE([$host], [x86_64-*linux*],
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])
@@ -215,6 +240,8 @@ AS_CASE([$host], [x86_64-*linux*],
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.
@@ -232,6 +259,8 @@ AS_CASE([$host], [x86_64-*linux*],
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])
@@ -239,6 +268,8 @@ AS_CASE([$host], [x86_64-*linux*],
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.
@@ -256,6 +287,8 @@ AS_CASE([$host], [x86_64-*linux*],
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])
@@ -264,6 +297,8 @@ AS_CASE([$host], [x86_64-*linux*],
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
@@ -282,6 +317,8 @@ AS_CASE([$host], [x86_64-*linux*],
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])
@@ -289,6 +326,8 @@ AS_CASE([$host], [x86_64-*linux*],
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],
@@ -300,6 +339,8 @@ AS_CASE([$host], [x86_64-*linux*],
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])
@@ -307,6 +348,8 @@ AS_CASE([$host], [x86_64-*linux*],
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],
@@ -324,6 +367,8 @@ AS_CASE([$host], [x86_64-*linux*],
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])
@@ -331,6 +376,8 @@ AS_CASE([$host], [x86_64-*linux*],
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]),
[[
@@ -347,6 +394,8 @@ AS_CASE([$host], [x86_64-*linux*],
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])
@@ -355,6 +404,8 @@ AS_CASE([$host], [x86_64-*linux*],
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
diff --git a/exec/exec.c b/exec/exec.c
index 7f2cc75338b..17051428658 100644
--- a/exec/exec.c
+++ b/exec/exec.c
@@ -31,14 +31,6 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#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
@@ -938,6 +930,10 @@ format_pid (char *in, unsigned int pid)
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.
@@ -948,7 +944,7 @@ format_pid (char *in, unsigned int pid)
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;
@@ -961,7 +957,8 @@ exec_0 (const char *name, struct exec_tracee *tracee,
#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
@@ -971,18 +968,43 @@ exec_0 (const char *name, struct exec_tracee *tracee,
{
/* 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);
diff --git a/exec/exec.h b/exec/exec.h
index 0f6a8a893b6..625ad0bb219 100644
--- a/exec/exec.h
+++ b/exec/exec.h
@@ -154,6 +154,11 @@ struct exec_tracee
/* 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 */
};
@@ -184,7 +189,7 @@ extern pid_t exec_waitpid (pid_t, int *, int);
/* 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 *);
diff --git a/exec/trace.c b/exec/trace.c
index d9e8673ba71..cb0839c9cd9 100644
--- a/exec/trace.c
+++ b/exec/trace.c
@@ -315,6 +315,13 @@ remove_tracee (struct exec_tracee *tracee)
/* 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;
@@ -431,7 +438,7 @@ syscall_trap_p (siginfo_t *signal)
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;
@@ -517,6 +524,17 @@ handle_exec (struct exec_tracee *tracee, USER_REGS_STRUCT *regs)
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)
@@ -622,6 +640,91 @@ handle_exec (struct exec_tracee *tracee, USER_REGS_STRUCT *regs)
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. */
@@ -635,6 +738,8 @@ process_system_call (struct exec_tracee *tracee)
#ifdef __aarch64__
USER_WORD old_w1, old_w2;
#endif /* __aarch64__ */
+ USER_WORD result;
+ bool reporting_error;
#ifdef __aarch64__
rc = aarch64_get_regs (tracee->pid, &regs);
@@ -678,6 +783,24 @@ process_system_call (struct exec_tracee *tracee)
break;
+#ifdef READLINK_SYSCALL
+ case READLINK_SYSCALL:
+#endif /* READLINK_SYSCALL */
+ case READLINKAT_SYSCALL:
+
+ /* Handle this readlinkat system call. */
+ rc = handle_readlinkat (callno, &regs, 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
@@ -694,8 +817,16 @@ process_system_call (struct exec_tracee *tracee)
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. */
@@ -755,7 +886,7 @@ process_system_call (struct exec_tracee *tracee)
/* 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. */
@@ -778,6 +909,32 @@ process_system_call (struct exec_tracee *tracee)
/* 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, &regs, false);
+#else /* !__aarch64__ */
+ ptrace (PTRACE_SETREGS, tracee->pid, NULL, &regs);
+#endif /* __aarch64__ */
+
+ /* Now wait for the next system call to happen. */
+ ptrace (PTRACE_SYSCALL, tracee->pid, NULL, NULL);
+ }
}
@@ -869,6 +1026,9 @@ after_fork (pid_t pid)
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;
}