Add helper binary `exec1'

* .gitignore: New files.
* Makefile.in (mostlyclean_dirs): Add libexec, if its Makefile
exists.
* autogen.sh (do_git): Autoreconf in exec as well.
* configure.ac: Configure libexec on Android.
* exec/Makefile.in:
* exec/README:
* exec/config-mips.m4.in:
* exec/config.guess:
* exec/config.h.in:
* exec/config.sub:
* exec/configure:
* exec/configure.ac:
* exec/deps.mk:
* exec/exec.c (MIN, struct exec_open_command)
(struct exec_map_command, struct exec_jump_command)
(write_open_command, write_load_command, process_interpreter_1)
(process_interpreter, process_program_header, insert_args)
(exec_0):
* exec/exec.h (_EXEC_H_, struct elf_header_32)
(struct program_header_32, struct dt_entry_32)
(struct elf_header_64, struct program_header_64)
(struct dt_entry_64, struct exec_tracee):
* exec/exec1.c (main):
* exec/install-sh (scriptversion):
* exec/loader-aarch64.s (_start):
* exec/loader-armeabi.s (_start):
* exec/loader-mips64el.s (__start):
* exec/loader-mipsel.s (__start):
* exec/loader-x86.s (_start):
* exec/loader-x86_64.s (_start):
* exec/mipsel-user.h (_MIPSEL_USER_H_):
* exec/mipsfpu.c (MIPS_ABI_FP_ANY, fpu_reqs, valid_abi_p)
(fp_mode_for_abi, cpu_supports_fr0_p, determine_fpu_mode):
* exec/mipsfpu.h (_MIPSFPU_H_, FP_FR0):
* exec/test.c (print_usage, main):
* exec/trace.c (MAX_TRACEES, aarch64_set_regs, read_memory)
(user_alloca, user_copy, remove_tracee, handle_clone)
(syscall_trap_p, handle_exec, process_system_call, tracing_execve)
(after_fork, find_tracee, exec_waitpid, exec_init): New files.
* java/Makefile.in (CROSS_EXEC_BINS): Add exec1 and
loader.
($(CROSS_EXEC_BINS) &): New target.
feature/android
Po Lu 2023-04-30 21:37:19 +08:00
parent 4289ed6cff
commit 368f6f3942
29 changed files with 15973 additions and 2 deletions

11
.gitignore vendored
View File

@ -371,3 +371,14 @@ lib-src/seccomp-filter-exec.pfc
# GDB history
.gdb_history
_gdb_history
# Files ignored in exec/.
exec/config.status
exec/loader
exec/test
exec/exec1
exec/deps/*
exec/autom4te.cache
exec/config.h
exec/config-mips.m4
exec/*.s.s

View File

@ -1000,6 +1000,12 @@ endef
mostlyclean_dirs = src oldXMenu lwlib lib lib-src nt doc/emacs doc/misc \
doc/lispref doc/lispintro test
### Add the libexec directory to mostlyclean_dirs if its Makefile has
### been created.
ifneq ($(wildcard exec/Makefile),)
mostlyclean_dirs := $(mostlyclean_dirs) exec
endif
$(foreach dir,$(mostlyclean_dirs),$(eval $(call submake_template,$(dir),mostlyclean)))
mostlyclean: $(mostlyclean_dirs:=_mostlyclean)

View File

@ -256,6 +256,12 @@ Please report any problems with this script to bug-gnu-emacs@gnu.org .'
## Let autoreconf figure out what, if anything, needs doing.
## Use autoreconf's -f option in case autoreconf itself has changed.
autoreconf -fi -I m4 || exit
echo "Running 'autoreconf -fi' in exec ..."
# Now, run autoreconf inside the exec directory to generate its
# configure script.
autoreconf -fi exec || exit
fi

View File

@ -174,7 +174,33 @@ AS_IF([test "$XCONFIGURE" = "android"],[
with_ndk_cxx_shared="$android_ndk_cxx_shared"
with_ndk_cxx="$android_ndk_cxx"
ndk_INIT([$android_abi], [$ANDROID_SDK], [cross/ndk-build],
[$ANDROID_CFLAGS])])
[$ANDROID_CFLAGS])
# At the same time, configure libexec with the build directory
# set to `exec'.
AS_MKDIR_P([exec])
AC_MSG_NOTICE([configuring in `exec'])
# Enter exec and configure it, using the C compiler as both the
# assembler and the linker. Determine the absolute name of the
# source directory.
# N.B. that the linker is actually cc, so pass -nostdlib, lest
# the crt be linked in. Likewise for as.
AS_CASE([$ac_srcdir], [.], [emacs_srcdir=`pwd`],
[[[\\/]* | ?:[\\/]*]], [emacs_srcdir=$ac_srcdir],
[*], [emacs_srcdir=`pwd`/$ac_srcdir])
OLDCWD=`pwd`
cd exec
$CONFIG_SHELL $emacs_srcdir/exec/configure \
--host=$host CC=$CC LD=$CC AS=$CC \
AR=$AR ASFLAGS=-c
cd $OLDCWD
AS_IF([test "$?" != "0"],
[AC_MSG_ERROR([failed to configure in `exec'])])
])
case $host in
*-mingw*)

139
exec/Makefile.in Normal file
View File

@ -0,0 +1,139 @@
### @configure_input@
# Copyright (C) 2023 Free Software Foundation, Inc.
# This file is part of GNU Emacs.
# GNU Emacs is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# GNU Emacs 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 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/>.
# Configure build directory information.
srcdir = @srcdir@
VPATH = @srcdir@
builddir = @builddir@
# Set up compilation tools.
CC = @CC@
AS = @AS@
LD = @LD@
M4 = @M4@
CPP = @CPP@
ASFLAGS = @ASFLAGS@
ARFLAGS = @ARFLAGS@
CFLAGS = @CFLAGS@
CPPFLAGS = @CPPFLAGS@
LDFLAGS = @LDFLAGS@
LOADERFLAGS = @LOADERFLAGS@
FIND_DELETE = @FIND_DELETE@
# Set up object files.
LOADER = @exec_loader@
OBJS = @OBJS@
LOADOBJS = $(patsubst %.s,%.o,$(LOADER))
# Set up automatic dependency tracking.
AUTO_DEPEND = @AUTO_DEPEND@
DEPDIR = deps
ifeq ($(AUTO_DEPEND),yes)
DEPFLAGS = -MMD -MF $(DEPDIR)/$*.d -MP
-include $(OBJS:%.o=$(DEPDIR)/%.d)
-include $(DEPDIR)/test.d
-include $(DEPDIR)/exec1.d
else
DEPFLAGS =
include $(srcdir)/deps.mk
endif
# Set up the appropriate targets.
all: libexec.a loader
# Set up automatic Makefile regeneration.
$(srcdir)/configure: $(srcdir)/configure.ac
cd $(srcdir) && autoreconf
config.status: $(srcdir)/configure
if [ -x config.status ]; then \
./config.status --recheck; \
else \
$(srcdir)/configure; \
fi
Makefile: config.status Makefile.in
MAKE="$(MAKE)" ./config.status
# Set up rules to build targets.
.SUFFIXES: .c .s
.c.o:
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEPFLAGS) -I$(srcdir) $< -o $@
.s.o:
$(M4) $< > $<.s
$(AS) $(ASFLAGS) $<.s -o $@
# Set up dependencies for config-mips.m4.
config-mips.m4: config-mips.m4.in
cd $(srcdir) && ./config.status $@
$(LOADOBJS): config-mips.m4
# Set up rules to build libexec.a.
libexec.a: $(OBJS)
$(AR) cru $(ARFLAGS) $@ $^
# And loader.
loader: $(LOADOBJS)
$(LD) -o $@ $(LOADERFLAGS) $(LOADOBJS)
# And test.
test: test.o libexec.a
$(CC) $(LDFLAGS) $< libexec.a -o $@
# And exec1.
exec1: exec1.o libexec.a
$(CC) $(LDFLAGS) $< libexec.a -o $@
# Set up targets for cleaning.
.PHONY: clean distclean maintainer-clean
clean:
rm -f *.o *.a loader test *.s.s
ifeq ($(AUTO_DEPEND),yes)
rm -rf deps/*.d
endif
distclean: clean
rm -f Makefile config.status config.h config-mips.m4
maintainer-clean: distclean
### This doesn't actually appear in the coding standards, but Karl
### says GCC supports it, and that's where the configuration part of
### the coding standards seem to come from. It's like distclean, but
### it deletes backup and autosave files too.
extraclean: maintainer-clean
-rm -f config-tmp-* $(srcdir)/aclocal.m4 $(srcdir)/configure \
$(srcdir)/src/config.in
-[ "$(srcdir)" = "." ] || \
find $(srcdir) '(' -name '*~' -o -name '#*' ')' $(FIND_DELETE)
-find . '(' -name '*~' -o -name '#*' ')' $(FIND_DELETE)

3
exec/README Normal file
View File

@ -0,0 +1,3 @@
This directory holds the source code to a library used to replace the
`execve' and `execveat' system calls, used by the Android port of
Emacs to start executables without intervention from the system.

36
exec/config-mips.m4.in Normal file
View File

@ -0,0 +1,36 @@
dnl Assembler templates for MIPS computers.
dnl
dnl Copyright (C) 2023 Free Software Foundation, Inc.
dnl
dnl This file is part of GNU Emacs.
dnl
dnl GNU Emacs is free software: you can redistribute it and/or modify
dnl it under the terms of the GNU General Public License as published by
dnl the Free Software Foundation, either version 3 of the License, or
dnl (at your option) any later version.
dnl
dnl GNU Emacs is distributed in the hope that it will be useful,
dnl but WITHOUT ANY WARRANTY; without even the implied warranty of
dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
dnl GNU General Public License for more details.
dnl
dnl You should have received a copy of the GNU General Public License
dnl along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
define(`SYSCALL_open', `ifelse(`@MIPS_N32@',`yes',`6002',`4005')')
define(`SYSCALL_close', `ifelse(`@MIPS_N32@',`yes',`6003',`4006')')
define(`SYSCALL_mmap', `ifelse(`@MIPS_N32@',`yes',`6009',`4090')')
define(`SYSCALL_nanosleep', `ifelse(`@MIPS_N32@',`yes',`6034',`4166')')
define(`SYSCALL_exit', `ifelse(`@MIPS_N32@',`yes',`6058',`4001')')
define(`SYSCALL_prctl', `ifelse(`@MIPS_N32@',`yes',`6153',`4192')')
define(`SYSCALL', `ifelse(`@MIPS_N32@',`yes',` move $a4, $1
move $a5, $2
move $a6, $3
move $a7, $4',` addi $sp, -32
sw $1, 16($sp)
sw $2, 20($sp)
sw $3, 24($sp)
sw $4, 28($sp)')')
define(`RESTORE', `ifelse(`@MIPS_N32@',`yes',` nop',` addi $sp, 32')')

1768
exec/config.guess vendored Executable file

File diff suppressed because it is too large Load Diff

202
exec/config.h.in Normal file
View File

@ -0,0 +1,202 @@
/* config.h.in. Generated from configure.ac by autoheader. */
/* Copyright (C) 2023 Free Software Foundation, Inc.
This file is part of GNU Emacs.
GNU Emacs is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation, either version 3 of the License, or (at your
option) any later version.
GNU Emacs 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
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/>. */
/* Define to number of reserved bytes past the stack frame. */
#undef ABI_RED_ZONE
/* Define if building universal (internal helper macro) */
#undef AC_APPLE_UNIVERSAL_BUILD
/* Define to number of the `clone3' system call. */
#undef CLONE3_SYSCALL
/* Define to number of the `clone' system call. */
#undef CLONE_SYSCALL
/* Virtual address for loading PIC executables */
#undef EXECUTABLE_BASE
/* Define to 1 if the system utilizes 64-bit ELF. */
#undef EXEC_64
/* Define to number of the `exec' system call. */
#undef EXEC_SYSCALL
/* Define to 1 if you have the <inttypes.h> header file. */
#undef HAVE_INTTYPES_H
/* Define to 1 if stdbool.h conforms to C99. */
#undef HAVE_STDBOOL_H
/* Define to 1 if you have the <stdint.h> header file. */
#undef HAVE_STDINT_H
/* Define to 1 if you have the <stdio.h> header file. */
#undef HAVE_STDIO_H
/* Define to 1 if you have the <stdlib.h> header file. */
#undef HAVE_STDLIB_H
/* Define to 1 if you have the <strings.h> header file. */
#undef HAVE_STRINGS_H
/* Define to 1 if you have the <string.h> header file. */
#undef HAVE_STRING_H
/* Define to 1 if you have the <sys/stat.h> header file. */
#undef HAVE_SYS_STAT_H
/* Define to 1 if you have the <sys/types.h> header file. */
#undef HAVE_SYS_TYPES_H
/* Define to 1 if the system has the type `uintptr_t'. */
#undef HAVE_UINTPTR_T
/* Define to 1 if you have the <unistd.h> header file. */
#undef HAVE_UNISTD_H
/* Define to 1 if the system has the type `_Bool'. */
#undef HAVE__BOOL
/* Virtual address for loading PIC interpreters */
#undef INTERPRETER_BASE
/* Define to 1 if MIPS NABI calling convention is being used. */
#undef MIPS_NABI
/* Define to the address where bug reports for this package should be sent. */
#undef PACKAGE_BUGREPORT
/* Define to the full name of this package. */
#undef PACKAGE_NAME
/* Define to the full name and version of this package. */
#undef PACKAGE_STRING
/* Define to the one symbol short name of this package. */
#undef PACKAGE_TARNAME
/* Define to the home page for this package. */
#undef PACKAGE_URL
/* Define to the version of this package. */
#undef PACKAGE_VERSION
/* Define to 1 if the stack grows downwards. */
#undef STACK_GROWS_DOWNWARDS
/* Define to register holding the stack pointer. */
#undef STACK_POINTER
/* Define to 1 if all of the C90 standard headers exist (not just the ones
required in a freestanding environment). This macro is provided for
backward compatibility; new code need not use it. */
#undef STDC_HEADERS
/* Define to register holding arg1 to system calls. */
#undef SYSCALL_ARG1_REG
/* Define to register holding arg0 to system calls. */
#undef SYSCALL_ARG_REG
/* Define to header holding system call numbers. */
#undef SYSCALL_HEADER
/* Define to register holding the system call number. */
#undef SYSCALL_NUM_REG
/* Define to register holding value of system calls. */
#undef SYSCALL_RET_REG
/* Define to header holding USER_REGS_STRUCT. */
#undef USER_HEADER
/* Define to structure holding user registers. */
#undef USER_REGS_STRUCT
/* Define to word type used by tracees. */
#undef USER_WORD
/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
significant byte first (like Motorola and SPARC, unlike Intel). */
#if defined AC_APPLE_UNIVERSAL_BUILD
# if defined __BIG_ENDIAN__
# define WORDS_BIGENDIAN 1
# endif
#else
# ifndef WORDS_BIGENDIAN
# undef WORDS_BIGENDIAN
# endif
#endif
/* Define for Solaris 2.5.1 so the uint32_t typedef from <sys/synch.h>,
<pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
#define below would cause a syntax error. */
#undef _UINT32_T
/* Define for Solaris 2.5.1 so the uint64_t typedef from <sys/synch.h>,
<pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
#define below would cause a syntax error. */
#undef _UINT64_T
/* Define for Solaris 2.5.1 so the uint8_t typedef from <sys/synch.h>,
<pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
#define below would cause a syntax error. */
#undef _UINT8_T
/* Define as a signed integer type capable of holding a process identifier. */
#undef pid_t
/* Define to the type of an unsigned integer type of width exactly 16 bits if
such a type exists and the standard includes do not define it. */
#undef uint16_t
/* Define to the type of an unsigned integer type of width exactly 32 bits if
such a type exists and the standard includes do not define it. */
#undef uint32_t
/* Define to the type of an unsigned integer type of width exactly 64 bits if
such a type exists and the standard includes do not define it. */
#undef uint64_t
/* Define to the type of an unsigned integer type of width exactly 8 bits if
such a type exists and the standard includes do not define it. */
#undef uint8_t
/* Define to the type of an unsigned integer type wide enough to hold a
pointer, if such a type exists, and if the system does not define it. */
#undef uintptr_t
#ifdef HAVE_STDBOOL_H
# include <stdbool.h>
#else
# ifndef HAVE__BOOL
# ifdef __cplusplus
typedef bool _Bool;
# else
# define _Bool signed char
# endif
# endif
# define bool _Bool
# define false 0
# define true 1
# define __bool_true_false_are_defined 1
#endif

1890
exec/config.sub vendored Executable file

File diff suppressed because it is too large Load Diff

6940
exec/configure vendored Executable file

File diff suppressed because it is too large Load Diff

418
exec/configure.ac Normal file
View File

@ -0,0 +1,418 @@
dnl Autoconf script for GNU Emacs's exec library.
dnl To rebuild the 'configure' script from this, execute the command
dnl autoconf
dnl in the directory containing this script.
dnl If you changed any AC_DEFINES, also run autoheader.
dnl
dnl Copyright (C) 2023 Free Software Foundation, Inc.
dnl
dnl This file is part of GNU Emacs.
dnl
dnl GNU Emacs is free software: you can redistribute it and/or modify
dnl it under the terms of the GNU General Public License as published by
dnl the Free Software Foundation, either version 3 of the License, or
dnl (at your option) any later version.
dnl
dnl GNU Emacs is distributed in the hope that it will be useful,
dnl but WITHOUT ANY WARRANTY; without even the implied warranty of
dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
dnl GNU General Public License for more details.
dnl
dnl You should have received a copy of the GNU General Public License
dnl along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
AC_PREREQ([2.65])
AC_INIT([libexec], [30.0.50], [bug-gnu-emacs@gnu.org], [],
[https://www.gnu.org/software/emacs/])
AH_TOP([/* Copyright (C) 2023 Free Software Foundation, Inc.
This file is part of GNU Emacs.
GNU Emacs is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation, either version 3 of the License, or (at your
option) any later version.
GNU Emacs 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
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_PROG_CC
AC_PROG_CPP
AC_PROG_INSTALL
AC_TYPE_UINT8_T
AC_TYPE_UINT16_T
AC_TYPE_UINT32_T
AC_TYPE_UINT64_T
AC_TYPE_UINTPTR_T
AC_TYPE_PID_T
AC_HEADER_STDBOOL
AC_CHECK_FUNC([getpagesize])
AH_BOTTOM([
#ifdef HAVE_STDBOOL_H
# include <stdbool.h>
#else
# ifndef HAVE__BOOL
# ifdef __cplusplus
typedef bool _Bool;
# else
# define _Bool signed char
# endif
# endif
# define bool _Bool
# define false 0
# define true 1
# define __bool_true_false_are_defined 1
#endif
])
AC_C_BIGENDIAN
AH_TEMPLATE([SYSCALL_HEADER], [Define to header holding system call numbers.])
AH_TEMPLATE([USER_HEADER], [Define to header holding USER_REGS_STRUCT.])
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_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([USER_WORD], [Define to word type used by tracees.])
AH_TEMPLATE([EXEC_64], [Define to 1 if the system utilizes 64-bit ELF.])
AH_TEMPLATE([STACK_GROWS_DOWNWARDS], [Define to 1 if the stack grows downwards.])
AH_TEMPLATE([ABI_RED_ZONE], [Define to number of reserved bytes past the stack frame.])
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.])
AC_CANONICAL_HOST
# Look for required tools.
AC_ARG_VAR([M4], [`m4' preprocessor command.])
AC_ARG_VAR([AS], [`as' assembler command.])
AC_ARG_VAR([LD], [`ld' linker command.])
# Check for a working m4.
AC_CHECK_PROGS([M4], [gm4 m4],
[AC_MSG_ERROR([Cannot find m4])])
# Check for a working assembler.
AC_CHECK_TOOL([AS], [as],
[AC_MSG_ERROR([Cannot find a working assembler])])
# And ar.
AC_CHECK_TOOL([AR], [ar],
[AC_MSG_ERROR([Cannot find a working ar])])
# And ld.
AC_CHECK_TOOL([LD], [ld],
[AC_MSG_ERROR([Cannot find a working linker])])
# Now check if ld is a C compiler.
LDPREFIX=
AC_CACHE_CHECK([whether ld is a C compiler],
[exec_cv_ld_is_cc],
[cat <<_ACEOF > conftest.c
AC_LANG_PROGRAM(,)
_ACEOF
exec_cv_ld_is_cc=yes
$LD -c conftest.c -o conftest.$OBJEXT >&AS_MESSAGE_LOG_FD 2>&1 \
|| exec_cv_ld_is_cc=no
rm -f conftest.c conftest.$OBJEXT])
# And if as is a C compiler.
AC_CACHE_CHECK([whether as is a C compiler],
[exec_cv_as_is_cc],
[cat <<_ACEOF > conftest.c
AC_LANG_PROGRAM(,)
_ACEOF
exec_cv_as_is_cc=yes
$AS -c conftest.c -o conftest.$OBJEXT >&AS_MESSAGE_LOG_FD 2>&1 \
|| exec_cv_as_is_cc=no
rm -f conftest.c conftest.$OBJEXT])
# If ld is a C compiler, pass `-nostdlib', `-nostartfiles', and
# `-static'. Also, set LDPREFIX to -Wl,
AS_IF([test "x$exec_cv_ld_is_cc" = "xyes"],
[LOADERFLAGS="$LOADERFLAGS -nostdlib -nostartfiles -static"
LDPREFIX=-Wl,])
# If as is a C compiler, add `-c' to ASFLAGS.
AS_IF([test "x$exec_cv_as_is_cc" = "xyes"],
[ASFLAGS="$ASFLAGS -c"])
AC_DEFUN([exec_CHECK_LINUX_CLONE3],
[
AC_CHECK_DECL([__NR_clone3],
[AC_DEFINE([CLONE3_SYSCALL], [__NR_clone3])],
[], [[
#include <asm/unistd.h>
]])
])
AC_DEFUN([exec_CHECK_MIPS_NABI],
[
AC_CACHE_CHECK([whether MIPS NABI calling convention is used],
[exec_cv_mips_nabi],
[AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
#include <sgidefs.h>
]], [[
#ifndef __mips64__
#if _MIPS_SIM == _ABIO32
OABI in use.
#endif /* _MIPS_SIM == _ABIO32 */
#endif /* !__mips64__ */
]])], [exec_cv_mips_nabi=yes],
[exec_cv_mips_nabi=no])])
dnl mips64 systems use N64 calling convention, a variant of nabi
dnl calling convention.
AS_IF([test "x$exec_cv_mips_nabi" != "xno"],
[AC_DEFINE([MIPS_NABI], [1],
[Define to 1 if MIPS NABI calling convention is being used.])],
[OBJS="$OBJS mipsfpu.o"])
])
# Determine the system type and define appropriate macros.
exec_loader=
is_mips=
OBJS="exec.o trace.o"
AS_CASE([$host], [x86_64-*linux*],
[AC_CHECK_MEMBER([struct user_regs_struct.rdi],
[AC_DEFINE([SYSCALL_HEADER], [<asm/unistd.h>])
AC_DEFINE([USER_HEADER], [<sys/user.h>])
AC_DEFINE([USER_REGS_STRUCT], [struct user_regs_struct])
AC_DEFINE([SYSCALL_NUM_REG], [orig_rax])
AC_DEFINE([SYSCALL_RET_REG], [rax])
AC_DEFINE([SYSCALL_ARG_REG], [rdi])
AC_DEFINE([SYSCALL_ARG1_REG], [rsi])
AC_DEFINE([STACK_POINTER], [rsp])
AC_DEFINE([EXEC_SYSCALL], [__NR_execve])
AC_DEFINE([USER_WORD], [uintptr_t])
AC_DEFINE([EXEC_64], [1])
AC_DEFINE([ABI_RED_ZONE], [128])
AC_DEFINE([EXECUTABLE_BASE], [0x555555554000])
AC_DEFINE([INTERPRETER_BASE], [0x600000000000])
AC_DEFINE([STACK_GROWS_DOWNWARDS], [1])
AC_DEFINE([CLONE_SYSCALL], [__NR_clone])
exec_CHECK_LINUX_CLONE3
# Make sure the loader doesn't conflict with other position
# dependent code.
LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0x200000000000"
exec_loader=loader-x86_64.s],
[AC_MSG_ERROR([Missing `rdi' in user_regs_struct])],
[[
#include <sys/user.h>
]])], [i[[34567]]86-*linux*],
[AC_CHECK_MEMBER([struct user_regs_struct.edi],
[AC_DEFINE([SYSCALL_HEADER], [<asm/unistd.h>])
AC_DEFINE([USER_HEADER], [<sys/user.h>])
AC_DEFINE([USER_REGS_STRUCT], [struct user_regs_struct])
AC_DEFINE([SYSCALL_NUM_REG], [orig_eax])
AC_DEFINE([SYSCALL_RET_REG], [eax])
AC_DEFINE([SYSCALL_ARG_REG], [ebx])
AC_DEFINE([SYSCALL_ARG1_REG], [ecx])
AC_DEFINE([STACK_POINTER], [esp])
AC_DEFINE([EXEC_SYSCALL], [__NR_execve])
AC_DEFINE([USER_WORD], [uintptr_t])
AC_DEFINE([EXECUTABLE_BASE], [0x0f000000])
AC_DEFINE([INTERPRETER_BASE], [0xaf000000])
AC_DEFINE([STACK_GROWS_DOWNWARDS], [1])
AC_DEFINE([CLONE_SYSCALL], [__NR_clone])
exec_CHECK_LINUX_CLONE3
# Make sure the loader doesn't conflict with other position
# dependent code.
LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0xa0000000"
exec_loader=loader-x86.s],
[AC_MSG_ERROR([Missing `edi' in user_regs_struct])],
[[
#include <sys/user.h>
]])], [aarch64-*linux*],
[AC_CHECK_MEMBER([struct user_regs_struct.sp],
[AC_DEFINE([SYSCALL_HEADER], [<asm/unistd.h>])
AC_DEFINE([USER_HEADER], [<sys/user.h>])
AC_DEFINE([USER_REGS_STRUCT], [struct user_regs_struct])
AC_DEFINE([SYSCALL_NUM_REG], [[regs[8]]])
AC_DEFINE([SYSCALL_RET_REG], [[regs[0]]])
AC_DEFINE([SYSCALL_ARG_REG], [[regs[0]]])
AC_DEFINE([SYSCALL_ARG1_REG], [[regs[1]]])
AC_DEFINE([STACK_POINTER], [sp])
AC_DEFINE([EXEC_SYSCALL], [__NR_execve])
AC_DEFINE([USER_WORD], [uintptr_t])
AC_DEFINE([EXEC_64], [1])
AC_DEFINE([EXECUTABLE_BASE], [0x3000000000])
AC_DEFINE([INTERPRETER_BASE], [0x3f00000000])
AC_DEFINE([STACK_GROWS_DOWNWARDS], [1])
AC_DEFINE([CLONE_SYSCALL], [__NR_clone])
exec_CHECK_LINUX_CLONE3
# Make sure the loader doesn't conflict with other position
# dependent code. ARM places rather significant restrictions on
# virtual addresses for a 64 bit architecture.
LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0x2000000000"
exec_loader=loader-aarch64.s],
[AC_MSG_ERROR([Missing `sp' in user_regs_struct])],
[[
#include <sys/user.h>
]])], [arm*linux*eabi* | armv7*linux*],
[AC_CHECK_MEMBER([struct user_regs.uregs],
[AC_DEFINE([SYSCALL_HEADER], [<asm/unistd.h>])
AC_DEFINE([USER_HEADER], [<sys/user.h>])
AC_DEFINE([USER_REGS_STRUCT], [struct user_regs])
AC_DEFINE([SYSCALL_NUM_REG], [[uregs[7]]])
AC_DEFINE([SYSCALL_RET_REG], [[uregs[0]]])
AC_DEFINE([SYSCALL_ARG_REG], [[uregs[0]]])
AC_DEFINE([SYSCALL_ARG1_REG], [[uregs[1]]])
AC_DEFINE([STACK_POINTER], [[uregs[13]]])
AC_DEFINE([EXEC_SYSCALL], [__NR_execve])
AC_DEFINE([USER_WORD], [uintptr_t])
AC_DEFINE([EXECUTABLE_BASE], [0x0f000000])
AC_DEFINE([INTERPRETER_BASE], [0x1f000000])
AC_DEFINE([STACK_GROWS_DOWNWARDS], [1])
AC_DEFINE([CLONE_SYSCALL], [__NR_clone])
exec_CHECK_LINUX_CLONE3
LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0x20000000"
exec_loader=loader-armeabi.s],
[AC_MSG_ERROR([Missing `uregs' in user_regs_struct])],
[[
#include <sys/user.h>
]])], [mipsel*linux*],
[AC_DEFINE([SYSCALL_HEADER], [<asm/unistd.h>])
AC_DEFINE([USER_HEADER], ["mipsel-user.h"])
AC_DEFINE([USER_REGS_STRUCT], [struct mipsel_regs])
AC_DEFINE([SYSCALL_NUM_REG], [[gregs[2]]]) # v0
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([STACK_POINTER], [[gregs[29]]]) # sp
AC_DEFINE([EXEC_SYSCALL], [__NR_execve])
AC_DEFINE([USER_WORD], [uintptr_t])
AC_DEFINE([EXECUTABLE_BASE], [0x0f000000])
AC_DEFINE([INTERPRETER_BASE], [0x1f000000])
AC_DEFINE([STACK_GROWS_DOWNWARDS], [1])
AC_DEFINE([CLONE_SYSCALL], [__NR_clone])
AC_CHECK_DECL([_MIPS_SIM], [exec_CHECK_MIPS_NABI],
[AC_MSG_ERROR([_MIPS_SIM could not be determined]),
[[
#include <sgidefs.h>
]]])
exec_CHECK_LINUX_CLONE3
LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0x20000000"
is_mips=yes
exec_loader=loader-mipsel.s], [mips64el*linux*],
[AC_DEFINE([SYSCALL_HEADER], [<asm/unistd.h>])
AC_DEFINE([USER_HEADER], ["mipsel-user.h"])
AC_DEFINE([USER_REGS_STRUCT], [struct mipsel_regs])
AC_DEFINE([SYSCALL_NUM_REG], [[gregs[2]]]) # v0
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([STACK_POINTER], [[gregs[29]]]) # sp
AC_DEFINE([EXEC_SYSCALL], [__NR_execve])
AC_DEFINE([USER_WORD], [uintptr_t])
AC_DEFINE([EXEC_64], [1])
AC_DEFINE([EXECUTABLE_BASE], [0x400000])
AC_DEFINE([INTERPRETER_BASE], [0x3f00000000])
AC_DEFINE([STACK_GROWS_DOWNWARDS], [1])
AC_DEFINE([CLONE_SYSCALL], [__NR_clone])
exec_CHECK_LINUX_CLONE3
exec_CHECK_MIPS_NABI
LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0x3e00000000"
is_mips=yes
exec_loader=loader-mips64el.s], [*],
[AC_MSG_ERROR([Please port libexec to $host])])
MIPS_N32=$exec_cv_mips_nabi
AC_ARG_VAR([LOADERFLAGS], [Flags used to link the loader.])
AC_ARG_VAR([ARFLAGS], [Flags for the archiver.])
AC_ARG_VAR([ASFLAGS], [Flags for the assembler.])
# Make the assembler optimize for code size. Don't do this on MIPS,
# as the assembler code manages branch delays manually.
AC_CACHE_CHECK([whether as understands -O],
[exec_cv_as_O],
[exec_cv_as_O=no
cat <<_ACEOF >conftest.s
.section text
.global _start
_start:
_ACEOF
$AS $ASFLAGS -O conftest.s -o conftest.$OBJEXT \
>&AS_MESSAGE_LOG_FD 2>&1 \
&& exec_cv_as_O=yes
rm -f conftest.s conftest.$OBJEXT])
AS_IF([test "$exec_cv_as_O" = "yes" \
&& test "$is_mips" != "yes"],
[ASFLAGS="$ASFLAGS -O"])
# Make the assembler generate debug information.
AC_CACHE_CHECK([whether as understands -g],
[exec_cv_as_g],
[exec_cv_as_g=no
cat <<_ACEOF >conftest.s
.section text
.global _start
_start:
_ACEOF
$AS $ASFLAGS -g conftest.s -o conftest.$OBJEXT \
>&AS_MESSAGE_LOG_FD 2>&1 \
&& exec_cv_as_g=yes
rm -f conftest.s conftest.$OBJEXT])
AS_IF([test "$exec_cv_as_g" = "yes"], [ASFLAGS="$ASFLAGS -g"])
# Check for the ability to automatically generate dependencies for C
# source files.
AUTO_DEPEND=no
AS_IF([test "x$GCC" = xyes],
[AC_CACHE_CHECK([whether gcc understands -MMD -MF],
[exec_cv_autodepend],
[SAVE_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS -MMD -MF deps.d -MP"
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])],
[exec_cv_autodepend=yes],
[exec_cv_autodepend=no])
CFLAGS="$SAVE_CFLAGS"
test -f deps.d || emacs_cv_autodepend=no
rm -rf deps.d])
AS_IF([test "x$exec_cv_autodepend" = xyes],
[AUTO_DEPEND=yes
AS_MKDIR_P([deps])])])
# Now check for some other stuff.
AC_CACHE_CHECK([for 'find' args to delete a file],
[exec_cv_find_delete],
[AS_IF([touch conftest.tmp && find conftest.tmp -delete 2>/dev/null &&
test ! -f conftest.tmp], [exec_cv_find_delete="-delete"],
[exec_cv_find_delete="-exec rm -f {} ';'"])])
FIND_DELETE=$exec_cv_find_delete
AC_SUBST([FIND_DELETE])
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_FILES([Makefile config-mips.m4])
AC_SUBST([AUTO_DEPEND])
AC_SUBST([LOADERFLAGS])
AC_SUBST([ARFLAGS])
AC_SUBST([ASFLAGS])
AC_SUBST([exec_loader])
AC_SUBST([MIPS_N32])
AC_SUBST([OBJS])
AC_OUTPUT

21
exec/deps.mk Normal file
View File

@ -0,0 +1,21 @@
### deps.mk
## Copyright (C) 2023 Free Software Foundation, Inc.
## This file is part of GNU Emacs.
## GNU Emacs is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## GNU Emacs 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 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/>.
exec.o: exec.h config.h
trace.o: exec.h config.h

1016
exec/exec.c Normal file

File diff suppressed because it is too large Load Diff

192
exec/exec.h Normal file
View File

@ -0,0 +1,192 @@
/* Program execution for Emacs.
Copyright (C) 2023 Free Software Foundation, Inc.
This file is part of GNU Emacs.
GNU Emacs is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or (at
your option) any later version.
GNU Emacs 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 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/>. */
#ifndef _EXEC_H_
#define _EXEC_H_
#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif /* HAVE_STDINT_H */
#include <sys/types.h>
#include USER_HEADER
/* Define a replacement for `uint64_t' if it's not present in the C
library. */
#ifndef UINT64_MAX
typedef struct
{
uint32_t word1;
uint32_t word2;
} xint64_t;
#else /* UINT64_MAX */
typedef uint64_t xint64_t;
#endif /* !UINT64_MAX */
/* 32-bit ELF headers. */
struct elf_header_32
{
unsigned char e_ident[16];
uint16_t e_type;
uint16_t e_machine;
uint32_t e_version;
uint32_t e_entry;
uint32_t e_phoff;
uint32_t e_shoff;
uint32_t e_flags;
uint16_t e_ehsize;
uint16_t e_phentsize;
uint16_t e_phnum;
uint16_t e_shentsize;
uint16_t e_shnum;
uint16_t e_shstrndx;
};
struct program_header_32
{
uint32_t p_type;
uint32_t p_offset;
uint32_t p_vaddr;
uint32_t p_paddr;
uint32_t p_filesz;
uint32_t p_memsz;
uint32_t p_flags;
uint32_t p_align;
};
struct dt_entry_32
{
uint32_t d_tag;
uint32_t d_val;
};
struct elf_header_64
{
unsigned char e_ident[16];
uint16_t e_type;
uint16_t e_machine;
uint32_t e_version;
xint64_t e_entry;
xint64_t e_phoff;
xint64_t e_shoff;
uint32_t e_flags;
uint16_t e_ehsize;
uint16_t e_phentsize;
uint16_t e_phnum;
uint16_t e_shentsize;
uint16_t e_shnum;
uint16_t e_shstrndx;
};
struct program_header_64
{
uint32_t p_type;
uint32_t p_flags;
xint64_t p_offset;
xint64_t p_vaddr;
xint64_t p_paddr;
xint64_t p_filesz;
xint64_t p_memsz;
xint64_t p_align;
};
struct dt_entry_64
{
xint64_t d_tag;
xint64_t d_val;
};
/* Define some types to the correct values. */
#ifdef EXEC_64
typedef struct elf_header_64 elf_header;
typedef struct program_header_64 program_header;
typedef struct dt_entry_64 dt_entry;
#else /* !EXEC_64 */
typedef struct elf_header_32 elf_header;
typedef struct program_header_32 program_header;
typedef struct dt_entry_32 dt_entry;
#endif /* EXEC_64 */
/* Defined in trace.c. */
/* Structure describing a process being traced. */
struct exec_tracee
{
/* The next process being traced. */
struct exec_tracee *next;
/* The thread ID of this process. */
pid_t pid;
/* Whether or not the tracee is currently waiting for a system call
to complete. */
bool waiting_for_syscall;
};
#ifdef __aarch64__
extern int aarch64_get_regs (pid_t, USER_REGS_STRUCT *);
extern int aarch64_set_regs (pid_t, USER_REGS_STRUCT *, bool);
#endif /* __aarch64__ */
extern USER_WORD user_alloca (struct exec_tracee *, USER_REGS_STRUCT *,
USER_REGS_STRUCT *, USER_WORD);
extern int user_copy (struct exec_tracee *, const unsigned char *,
USER_WORD, USER_WORD);
extern void exec_init (const char *);
extern int tracing_execve (const char *, char *const *,
char *const *);
extern int after_fork (pid_t);
extern pid_t exec_waitpid (pid_t, int *, int);
/* Defined in exec.c. */
extern char *exec_0 (const char *, struct exec_tracee *,
size_t *, USER_REGS_STRUCT *);
#endif /* _EXEC_H_ */

88
exec/exec1.c Normal file
View File

@ -0,0 +1,88 @@
/* Program execution for Emacs.
Copyright (C) 2023 Free Software Foundation, Inc.
This file is part of GNU Emacs.
GNU Emacs is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or (at
your option) any later version.
GNU Emacs 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 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/>. */
#include <config.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include "exec.h"
/* exec1 is a program which takes another program and its arguments,
forks, and executes that program, all while tracing it and its
children to use the program execution mechanism defined in exec.c.
This is necessary to bypass security restrictions which prohibit
Emacs from loading executables from certain directories, by, in
effect, replacing the executable loader in the Linux kernel. */
int
main (int argc, char **argv)
{
pid_t pid, pid1;
extern char **environ;
int wstatus;
pid = fork ();
if (!pid)
{
tracing_execve (argv[2], argv + 2, environ);
/* An error occured. Exit with failure. */
exit (127);
}
else
{
/* Provide the file name of the loader. */
exec_init (argv[1]);
if (after_fork (pid))
exit (127);
/* Start waiting for the process to exit. */
while (true)
{
pid1 = exec_waitpid (-1, &wstatus, 0);
/* If the child process exits normally, exit with its status
code. If not, raise the signal that caused it to
exit. */
if (pid == pid1)
{
if (WIFEXITED (wstatus))
exit (WEXITSTATUS (wstatus));
else /* if WIFSIGNALED (wstatus) */
{
raise (WTERMSIG (wstatus));
/* Just in case the signal raised doesn't cause an
exit. */
exit (127);
}
}
/* Otherwise, continue looping. */
}
}
}

541
exec/install-sh Executable file
View File

@ -0,0 +1,541 @@
#!/usr/bin/sh
# install - install a program, script, or datafile
scriptversion=2020-11-14.01; # UTC
# This originates from X11R5 (mit/util/scripts/install.sh), which was
# later released in X11R6 (xc/config/util/install.sh) with the
# following copyright and license.
#
# Copyright (C) 1994 X Consortium
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
# Except as contained in this notice, the name of the X Consortium shall not
# be used in advertising or otherwise to promote the sale, use or other deal-
# ings in this Software without prior written authorization from the X Consor-
# tium.
#
#
# FSF changes to this file are in the public domain.
#
# Calling this script install-sh is preferred over install.sh, to prevent
# 'make' implicit rules from creating a file called install from it
# when there is no Makefile.
#
# This script is compatible with the BSD install script, but was written
# from scratch.
tab=' '
nl='
'
IFS=" $tab$nl"
# Set DOITPROG to "echo" to test this script.
doit=${DOITPROG-}
doit_exec=${doit:-exec}
# Put in absolute file names if you don't have them in your path;
# or use environment vars.
chgrpprog=${CHGRPPROG-chgrp}
chmodprog=${CHMODPROG-chmod}
chownprog=${CHOWNPROG-chown}
cmpprog=${CMPPROG-cmp}
cpprog=${CPPROG-cp}
mkdirprog=${MKDIRPROG-mkdir}
mvprog=${MVPROG-mv}
rmprog=${RMPROG-rm}
stripprog=${STRIPPROG-strip}
posix_mkdir=
# Desired mode of installed file.
mode=0755
# Create dirs (including intermediate dirs) using mode 755.
# This is like GNU 'install' as of coreutils 8.32 (2020).
mkdir_umask=22
backupsuffix=
chgrpcmd=
chmodcmd=$chmodprog
chowncmd=
mvcmd=$mvprog
rmcmd="$rmprog -f"
stripcmd=
src=
dst=
dir_arg=
dst_arg=
copy_on_change=false
is_target_a_directory=possibly
usage="\
Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
or: $0 [OPTION]... SRCFILES... DIRECTORY
or: $0 [OPTION]... -t DIRECTORY SRCFILES...
or: $0 [OPTION]... -d DIRECTORIES...
In the 1st form, copy SRCFILE to DSTFILE.
In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
In the 4th, create DIRECTORIES.
Options:
--help display this help and exit.
--version display version info and exit.
-c (ignored)
-C install only if different (preserve data modification time)
-d create directories instead of installing files.
-g GROUP $chgrpprog installed files to GROUP.
-m MODE $chmodprog installed files to MODE.
-o USER $chownprog installed files to USER.
-p pass -p to $cpprog.
-s $stripprog installed files.
-S SUFFIX attempt to back up existing files, with suffix SUFFIX.
-t DIRECTORY install into DIRECTORY.
-T report an error if DSTFILE is a directory.
Environment variables override the default commands:
CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
RMPROG STRIPPROG
By default, rm is invoked with -f; when overridden with RMPROG,
it's up to you to specify -f if you want it.
If -S is not specified, no backups are attempted.
Email bug reports to bug-automake@gnu.org.
Automake home page: https://www.gnu.org/software/automake/
"
while test $# -ne 0; do
case $1 in
-c) ;;
-C) copy_on_change=true;;
-d) dir_arg=true;;
-g) chgrpcmd="$chgrpprog $2"
shift;;
--help) echo "$usage"; exit $?;;
-m) mode=$2
case $mode in
*' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*)
echo "$0: invalid mode: $mode" >&2
exit 1;;
esac
shift;;
-o) chowncmd="$chownprog $2"
shift;;
-p) cpprog="$cpprog -p";;
-s) stripcmd=$stripprog;;
-S) backupsuffix="$2"
shift;;
-t)
is_target_a_directory=always
dst_arg=$2
# Protect names problematic for 'test' and other utilities.
case $dst_arg in
-* | [=\(\)!]) dst_arg=./$dst_arg;;
esac
shift;;
-T) is_target_a_directory=never;;
--version) echo "$0 $scriptversion"; exit $?;;
--) shift
break;;
-*) echo "$0: invalid option: $1" >&2
exit 1;;
*) break;;
esac
shift
done
# We allow the use of options -d and -T together, by making -d
# take the precedence; this is for compatibility with GNU install.
if test -n "$dir_arg"; then
if test -n "$dst_arg"; then
echo "$0: target directory not allowed when installing a directory." >&2
exit 1
fi
fi
if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
# When -d is used, all remaining arguments are directories to create.
# When -t is used, the destination is already specified.
# Otherwise, the last argument is the destination. Remove it from $@.
for arg
do
if test -n "$dst_arg"; then
# $@ is not empty: it contains at least $arg.
set fnord "$@" "$dst_arg"
shift # fnord
fi
shift # arg
dst_arg=$arg
# Protect names problematic for 'test' and other utilities.
case $dst_arg in
-* | [=\(\)!]) dst_arg=./$dst_arg;;
esac
done
fi
if test $# -eq 0; then
if test -z "$dir_arg"; then
echo "$0: no input file specified." >&2
exit 1
fi
# It's OK to call 'install-sh -d' without argument.
# This can happen when creating conditional directories.
exit 0
fi
if test -z "$dir_arg"; then
if test $# -gt 1 || test "$is_target_a_directory" = always; then
if test ! -d "$dst_arg"; then
echo "$0: $dst_arg: Is not a directory." >&2
exit 1
fi
fi
fi
if test -z "$dir_arg"; then
do_exit='(exit $ret); exit $ret'
trap "ret=129; $do_exit" 1
trap "ret=130; $do_exit" 2
trap "ret=141; $do_exit" 13
trap "ret=143; $do_exit" 15
# Set umask so as not to create temps with too-generous modes.
# However, 'strip' requires both read and write access to temps.
case $mode in
# Optimize common cases.
*644) cp_umask=133;;
*755) cp_umask=22;;
*[0-7])
if test -z "$stripcmd"; then
u_plus_rw=
else
u_plus_rw='% 200'
fi
cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
*)
if test -z "$stripcmd"; then
u_plus_rw=
else
u_plus_rw=,u+rw
fi
cp_umask=$mode$u_plus_rw;;
esac
fi
for src
do
# Protect names problematic for 'test' and other utilities.
case $src in
-* | [=\(\)!]) src=./$src;;
esac
if test -n "$dir_arg"; then
dst=$src
dstdir=$dst
test -d "$dstdir"
dstdir_status=$?
# Don't chown directories that already exist.
if test $dstdir_status = 0; then
chowncmd=""
fi
else
# Waiting for this to be detected by the "$cpprog $src $dsttmp" command
# might cause directories to be created, which would be especially bad
# if $src (and thus $dsttmp) contains '*'.
if test ! -f "$src" && test ! -d "$src"; then
echo "$0: $src does not exist." >&2
exit 1
fi
if test -z "$dst_arg"; then
echo "$0: no destination specified." >&2
exit 1
fi
dst=$dst_arg
# If destination is a directory, append the input filename.
if test -d "$dst"; then
if test "$is_target_a_directory" = never; then
echo "$0: $dst_arg: Is a directory" >&2
exit 1
fi
dstdir=$dst
dstbase=`basename "$src"`
case $dst in
*/) dst=$dst$dstbase;;
*) dst=$dst/$dstbase;;
esac
dstdir_status=0
else
dstdir=`dirname "$dst"`
test -d "$dstdir"
dstdir_status=$?
fi
fi
case $dstdir in
*/) dstdirslash=$dstdir;;
*) dstdirslash=$dstdir/;;
esac
obsolete_mkdir_used=false
if test $dstdir_status != 0; then
case $posix_mkdir in
'')
# With -d, create the new directory with the user-specified mode.
# Otherwise, rely on $mkdir_umask.
if test -n "$dir_arg"; then
mkdir_mode=-m$mode
else
mkdir_mode=
fi
posix_mkdir=false
# The $RANDOM variable is not portable (e.g., dash). Use it
# here however when possible just to lower collision chance.
tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
trap '
ret=$?
rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null
exit $ret
' 0
# Because "mkdir -p" follows existing symlinks and we likely work
# directly in world-writeable /tmp, make sure that the '$tmpdir'
# directory is successfully created first before we actually test
# 'mkdir -p'.
if (umask $mkdir_umask &&
$mkdirprog $mkdir_mode "$tmpdir" &&
exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1
then
if test -z "$dir_arg" || {
# Check for POSIX incompatibilities with -m.
# HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
# other-writable bit of parent directory when it shouldn't.
# FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
test_tmpdir="$tmpdir/a"
ls_ld_tmpdir=`ls -ld "$test_tmpdir"`
case $ls_ld_tmpdir in
d????-?r-*) different_mode=700;;
d????-?--*) different_mode=755;;
*) false;;
esac &&
$mkdirprog -m$different_mode -p -- "$test_tmpdir" && {
ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"`
test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
}
}
then posix_mkdir=:
fi
rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir"
else
# Remove any dirs left behind by ancient mkdir implementations.
rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null
fi
trap '' 0;;
esac
if
$posix_mkdir && (
umask $mkdir_umask &&
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
)
then :
else
# mkdir does not conform to POSIX,
# or it failed possibly due to a race condition. Create the
# directory the slow way, step by step, checking for races as we go.
case $dstdir in
/*) prefix='/';;
[-=\(\)!]*) prefix='./';;
*) prefix='';;
esac
oIFS=$IFS
IFS=/
set -f
set fnord $dstdir
shift
set +f
IFS=$oIFS
prefixes=
for d
do
test X"$d" = X && continue
prefix=$prefix$d
if test -d "$prefix"; then
prefixes=
else
if $posix_mkdir; then
(umask $mkdir_umask &&
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
# Don't fail if two instances are running concurrently.
test -d "$prefix" || exit 1
else
case $prefix in
*\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
*) qprefix=$prefix;;
esac
prefixes="$prefixes '$qprefix'"
fi
fi
prefix=$prefix/
done
if test -n "$prefixes"; then
# Don't fail if two instances are running concurrently.
(umask $mkdir_umask &&
eval "\$doit_exec \$mkdirprog $prefixes") ||
test -d "$dstdir" || exit 1
obsolete_mkdir_used=true
fi
fi
fi
if test -n "$dir_arg"; then
{ test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
{ test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
{ test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
else
# Make a couple of temp file names in the proper directory.
dsttmp=${dstdirslash}_inst.$$_
rmtmp=${dstdirslash}_rm.$$_
# Trap to clean up those temp files at exit.
trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
# Copy the file name to the temp name.
(umask $cp_umask &&
{ test -z "$stripcmd" || {
# Create $dsttmp read-write so that cp doesn't create it read-only,
# which would cause strip to fail.
if test -z "$doit"; then
: >"$dsttmp" # No need to fork-exec 'touch'.
else
$doit touch "$dsttmp"
fi
}
} &&
$doit_exec $cpprog "$src" "$dsttmp") &&
# and set any options; do chmod last to preserve setuid bits.
#
# If any of these fail, we abort the whole thing. If we want to
# ignore errors from any of these, just make sure not to ignore
# errors from the above "$doit $cpprog $src $dsttmp" command.
#
{ test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } &&
{ test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } &&
{ test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } &&
{ test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
# If -C, don't bother to copy if it wouldn't change the file.
if $copy_on_change &&
old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` &&
new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` &&
set -f &&
set X $old && old=:$2:$4:$5:$6 &&
set X $new && new=:$2:$4:$5:$6 &&
set +f &&
test "$old" = "$new" &&
$cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
then
rm -f "$dsttmp"
else
# If $backupsuffix is set, and the file being installed
# already exists, attempt a backup. Don't worry if it fails,
# e.g., if mv doesn't support -f.
if test -n "$backupsuffix" && test -f "$dst"; then
$doit $mvcmd -f "$dst" "$dst$backupsuffix" 2>/dev/null
fi
# Rename the file to the real destination.
$doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||
# The rename failed, perhaps because mv can't rename something else
# to itself, or perhaps because mv is so ancient that it does not
# support -f.
{
# Now remove or move aside any old file at destination location.
# We try this two ways since rm can't unlink itself on some
# systems and the destination file might be busy for other
# reasons. In this case, the final cleanup might fail but the new
# file should still install successfully.
{
test ! -f "$dst" ||
$doit $rmcmd "$dst" 2>/dev/null ||
{ $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
{ $doit $rmcmd "$rmtmp" 2>/dev/null; :; }
} ||
{ echo "$0: cannot unlink or rename $dst" >&2
(exit 1); exit 1
}
} &&
# Now rename the file to the real destination.
$doit $mvcmd "$dsttmp" "$dst"
}
fi || exit 1
trap '' 0
fi
done
# Local variables:
# eval: (add-hook 'before-save-hook 'time-stamp)
# time-stamp-start: "scriptversion="
# time-stamp-format: "%:y-%02m-%02d.%02H"
# time-stamp-time-zone: "UTC0"
# time-stamp-end: "; # UTC"
# End:

174
exec/loader-aarch64.s Normal file
View File

@ -0,0 +1,174 @@
// Copyright (C) 2023 Free Software Foundation, Inc.
//
// This file is part of GNU Emacs.
//
// GNU Emacs is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published
// by the Free Software Foundation, either version 3 of the License,
// or (at your option) any later version.
//
// GNU Emacs 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
// 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/>.
// Notice that aarch64 requires that sp be aligned to 16 bytes while
// accessing memory from sp, so x20 is used to chase down the load
// area.
.section .text
.global _start
_start:
//mov x8, 101 // SYS_nanosleep
//adr x0, timespec // req
//mov x1, #0 // rem
//svc #0 // syscall
mov x20, sp // x20 = sp
ldr x10, [x20] // x10 = original SP
add x20, x20, #16 // x20 = start of load area
mov x28, #-1 // x28 = secondary fd
.next_action:
ldr x11, [x20] // action number
and x12, x11, #-17 // actual action number
cbz x12, .open_file // open file?
cmp x12, #3 // jump?
beq .rest_of_exec
cmp x12, #4 // anonymous mmap?
beq .do_mmap_anon
.do_mmap:
ldr x0, [x20, 8] // vm_address
ldr x1, [x20, 32] // length
ldr x2, [x20, 24] // protection
ldr x3, [x20, 40] // flags
tst x11, #16 // primary fd?
mov x4, x29 // primary fd
beq .do_mmap_1
mov x4, x28 // secondary fd
.do_mmap_1:
mov x8, #222 // SYS_mmap
ldr x5, [x20, 16] // file_offset
svc #0 // syscall
ldr x9, [x20, 8] // length
cmp x0, x9 // mmap result
bne .perror // print error
ldr x3, [x20, 48] // clear
add x1, x1, x0 // x1 = vm_address + end
sub x3, x1, x3 // x3 = x1 - clear
mov x0, #0 // x0 = 0
.fill64:
sub x2, x1, x3 // x2 = x1 - x3
cmp x2, #63 // x2 >= 64?
ble .fillb // start filling bytes
stp x0, x0, [x3] // x3[0] = 0, x3[1] = 0
stp x0, x0, [x3, 16] // x3[2] = 0, x3[3] = 0
stp x0, x0, [x3, 32] // x3[4] = 0, x3[5] = 0
stp x0, x0, [x3, 48] // x3[6] = 0, x3[7] = 0
add x3, x3, #64 // x3 += 8
b .fill64
.fillb:
cmp x1, x3 // x1 == x3?
beq .continue // done
strb w0, [x3], #1 // ((char *) x3)++ = 0
b .fillb
.continue:
add x20, x20, #56 // next action
b .next_action
.do_mmap_anon:
ldr x0, [x20, 8] // vm_address
ldr x1, [x20, 32] // length
ldr x2, [x20, 24] // protection
ldr x3, [x20, 40] // flags
mov x4, #-1 // fd
b .do_mmap_1
.open_file:
mov x8, #56 // SYS_openat
mov x0, #-100 // AT_FDCWD
add x1, x20, #8 // file name
mov x2, #0 // O_RDONLY
mov x3, #0 // mode
svc #0 // syscall
cmp x0, #-1 // rc < 0?
ble .perror
.nextc:
ldrb w2, [x1], #1 // b = *x1++
cbnz w2, .nextc // b?
add x1, x1, #7 // round up x1
and x20, x1, #-8 // mask for round, set x20
tst x11, #16 // primary fd?
bne .secondary // secondary fd
mov x29, x0 // primary fd
b .next_action // next action
.secondary:
mov x28, x0 // secondary fd
b .next_action // next action.
.perror:
mov x8, #93 // SYS_exit
mvn x0, x0 // x1 = ~x0
add x0, x0, 1 // x1 += 1
svc #0 // exit
.rest_of_exec:
mov x7, x20 // x7 = x20
mov x20, x10 // x20 = x10
ldr x9, [x20] // argc
add x9, x9, #2 // x9 += 2
lsl x9, x9, #3 // argc * 8
add x20, x20, x9 // now past argv
.skipenv:
ldr x9, [x20], #8 // x9 = *envp++
cbnz x9, .skipenv // x9?
.one_auxv:
ldr x9, [x20], #16 // x9 = *sp, sp += 2
cbz x9, .cleanup // !x9?
cmp x9, #3 // is AT_PHDR?
beq .replace_phdr // replace
cmp x9, #4 // is AT_PHENT?
beq .replace_phent // replace
cmp x9, #5 // is AT_PHNUM?
beq .replace_phnum // replace
cmp x9, #9 // is AT_ENTRY?
beq .replace_entry // replace
cmp x9, #7 // is AT_BASE?
beq .replace_base // replace
b .one_auxv // next auxv
.replace_phdr:
ldr x9, [x7, 40] // at_phdr
str x9, [x20, -8] // store value
b .one_auxv
.replace_phent:
ldr x9, [x7, 24] // at_phent
str x9, [x20, -8] // store value
b .one_auxv
.replace_phnum:
ldr x9, [x7, 32] // at_phnum
str x9, [x20, -8] // store value
b .one_auxv
.replace_entry:
ldr x9, [x7, 16] // at_entry
str x9, [x20, -8] // store value
b .one_auxv
.replace_base:
ldr x9, [x7, 48] // at_base
str x9, [x20, -8] // store value
b .one_auxv
.cleanup:
cmp x28, #-1 // is secondary fd set?
bne .cleanup1 // not set
mov x8, #57 // SYS_close
mov x0, x28 // secondary fd
svc #0 // syscall
.cleanup1:
mov x8, #57 // SYS_close
mov x0, x29 // primary fd
svc #0 // syscall
.enter:
mov sp, x10 // restore original SP
mov x0, #0 // clear rtld_fini
ldr x1, [x7, 8] // branch to code
br x1
timespec:
.quad 10
.quad 10

192
exec/loader-armeabi.s Normal file
View File

@ -0,0 +1,192 @@
@ Copyright (C) 2023 Free Software Foundation, Inc.
@
@ This file is part of GNU Emacs.
@
@ GNU Emacs is free software: you can redistribute it and/or modify
@ it under the terms of the GNU General Public License as published
@ by the Free Software Foundation, either version 3 of the License,
@ or (at your option) any later version.
@
@ GNU Emacs 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
@ 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/>.
.section .text
.global _start
_start:
@mov r7, #162 @ SYS_nanosleep
@adr r0, timespec @ req
@mov r1, #0 @ rem
@swi #0 @ syscall
mov r8, sp @ r8 = sp
ldr r9, [r8], #8 @ r9 = original sp, r8 += 8
mov r14, #-1 @ r14 = secondary fd
.next_action:
ldr r11, [r8] @ r11 = action number
and r12, r11, #-17 @ actual action number
cmp r12, #0 @ open file?
beq .open_file @ open file.
cmp r12, #3 @ jump?
beq .rest_of_exec @ jump to code.
cmp r12, #4 @ anonymous mmap?
beq .do_mmap_anon @ anonymous mmap.
.do_mmap:
add r6, r8, #4 @ r6 = r8 + 4
ldm r6!, {r0, r5} @ vm_address, file_offset
ldm r6!, {r1, r2} @ protection, length
mov r3, r1 @ swap
lsr r5, #12 @ divide file offset by page size
mov r1, r2 @ swap
mov r2, r3 @ swap
ldm r6!, {r3, r12} @ flags, clear
tst r11, #16 @ primary fd?
mov r4, r10 @ primary fd
beq .do_mmap_1
mov r4, r14 @ secondary fd
.do_mmap_1:
mov r7, #192 @ SYS_mmap2
swi #0 @ syscall
ldr r2, [r8, #4] @ vm_address
cmp r2, r0 @ rc == vm_address?
bne .perror
add r0, r1, r2 @ r0 = length + vm_address
sub r3, r0, r12 @ r3 = r0 - clear
mov r1, #0 @ r1 = 0
.align:
cmp r0, r3 @ r0 == r3?
beq .continue @ continue
tst r3, #3 @ r3 & 3?
bne .fill32 @ fill aligned
strb r1, [r3], #1 @ fill byte
b .align @ align again
.fill32:
sub r2, r0, r3 @ r2 = r0 - r3
cmp r2, #31 @ r2 >= 32?
ble .fillb @ start filling bytes
str r1, [r3], #4 @ *r3++ = 0
str r1, [r3], #4 @ *r3++ = 0
str r1, [r3], #4 @ *r3++ = 0
str r1, [r3], #4 @ *r3++ = 0
str r1, [r3], #4 @ *r3++ = 0
str r1, [r3], #4 @ *r3++ = 0
str r1, [r3], #4 @ *r3++ = 0
str r1, [r3], #4 @ *r3++ = 0
b .fill32
.fillb:
cmp r0, r3 @ r0 == r3
beq .continue @ done
strb r1, [r3], #1 @ ((char *) r3)++ = 0
b .fillb
.continue:
add r8, r8, #28 @ next action
b .next_action
.do_mmap_anon:
add r6, r8, #4 @ r6 = r8 + 4
ldm r6!, {r0, r5} @ vm_address, file_offset
ldm r6!, {r1, r2} @ protection, length
mov r3, r1 @ swap
lsr r5, #12 @ divide file offset by page size
mov r1, r2 @ swap
mov r2, r3 @ swap
ldm r6!, {r3, r12} @ flags, clear
mov r4, #-1 @ fd
b .do_mmap_1
.open_file:
mov r7, #5 @ SYS_open
add r0, r8, #4 @ file name
mov r1, #0 @ O_RDONLY
mov r2, #0 @ mode
swi #0 @ syscall
cmp r0, #-1 @ r0 <= -1?
ble .perror
add r8, r8, #4 @ r8 = start of string
.nextc:
ldrb r1, [r8], #1 @ b = *r0++
cmp r1, #0 @ b?
bne .nextc @ next character
add r8, r8, #3 @ round up r8
and r8, r8, #-4 @ mask for round, set r8
tst r11, #16 @ primary fd?
bne .secondary @ secondary fd
mov r10, r0 @ primary fd
b .next_action @ next action
.secondary:
mov r14, r0 @ secondary fd
b .next_action @ next action
.perror:
mov r7, #1 @ SYS_exit
mvn r0, r0 @ r0 = ~r0
add r0, r0, #1 @ r0 += 1
swi #0
.rest_of_exec:
mov r7, r9 @ r7 = original SP
ldr r6, [r7] @ argc
add r6, r6, #2 @ argc + 2
lsl r6, r6, #2 @ argc *= 4
add r7, r7, r6 @ now past argv
.skipenv:
ldr r6, [r7], #4 @ r6 = *r7++
cmp r6, #0 @ r6?
bne .skipenv @ r6?
.one_auxv:
ldr r6, [r7], #8 @ r6 = *r7, r7 += 2
cmp r6, #0 @ !r6?
beq .cleanup @ r6?
cmp r6, #3 @ is AT_PHDR?
beq .replace_phdr @ replace
cmp r6, #4 @ is AT_PHENT?
beq .replace_phent @ replace
cmp r6, #5 @ is AT_PHNUM?
beq .replace_phnum @ replace
cmp r6, #9 @ is AT_ENTRY?
beq .replace_entry @ replace
cmp r6, #7 @ is AT_BASE?
beq .replace_base @ replace
b .one_auxv @ next auxv
.replace_phdr:
ldr r6, [r8, #20] @ at_phdr
str r6, [r7, #-4] @ store value
b .one_auxv
.replace_phent:
ldr r6, [r8, #12] @ at_phent
str r6, [r7, #-4] @ store value
b .one_auxv
.replace_phnum:
ldr r6, [r8, #16] @ at_phnum
str r6, [r7, #-4] @ store value
b .one_auxv
.replace_entry:
ldr r6, [r8, #8] @ at_entry
str r6, [r7, #-4] @ store value
b .one_auxv
.replace_base:
ldr r6, [r8, #24] @ at_base
str r6, [r7, #-4] @ store value
b .one_auxv
.cleanup:
cmp r14, #-1 @ secondary fd set?
bne .cleanup1 @ not set
mov r7, #6 @ SYS_close
mov r0, r14 @ secondary fd
swi #0 @ syscall
.cleanup1:
mov r7, #6 @ SYS_close
mov r0, r10 @ primary fd
swi #0 @ syscall
.enter:
mov sp, r9 @ restore original SP
mov r0, #0 @ clear rtld_fini
ldr r1, [r8, #4] @ branch to code
bx r1
timespec:
.long 10
.long 10
@ Local Variables:
@ asm-comment-char: 64
@ End:

214
exec/loader-mips64el.s Normal file
View File

@ -0,0 +1,214 @@
# Copyright (C) 2023 Free Software Foundation, Inc.
#
# This file is part of GNU Emacs.
#
# GNU Emacs is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published
# by the Free Software Foundation, either version 3 of the License,
# or (at your option) any later version.
#
# GNU Emacs 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
# 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/>.
.set noreorder # delay slots managed by hand
.section .text
.global __start
__start:
dnl li $v0, 5034 # SYS_nanosleep
dnl dla $a0, .timespec # rqtp
dnl li $a1, 0 # rmtp
dnl syscall # syscall
ld $s2, ($sp) # original stack pointer
daddi $s0, $sp, 16 # start of load area
daddi $sp, -16 # primary fd, secondary fd
li $t0, -1 # secondary fd
sd $t0, 8($sp) # initialize secondary fd
.next_action:
ld $s1, ($s0) # action number
andi $t0, $s1, 15 # t0 = action number & 15
beqz $t0, .open_file # open file?
nop # delay slot
daddi $t0, -3 # t0 -= 3
beqz $t0, .rest_of_exec # jump to code
nop # delay slot
li $t1, 1
beq $t0, $t1, .do_mmap_anon # anonymous mmap?
nop # delay slot
.do_mmap:
ld $t0, 8($s0) # vm address
ld $t1, 16($s0) # file_offset
ld $t2, 24($s0) # protection
ld $t3, 32($s0) # length
ld $v0, 40($s0) # flags
ld $v1, ($sp) # primary fd
andi $s3, $s1, 16 # s1 & 16?
beqz $s3, .do_mmap_1 # secondary fd?
nop # delay slot
ld $v1, 8($sp) # secondary fd
.do_mmap_1:
move $a0, $t0 # syscall arg
move $a1, $t3 # syscall arg
move $a2, $t2 # syscall arg
move $a3, $v0 # syscall arg
move $a4, $v1 # syscall arg
move $a5, $t1 # syscall arg
li $v0, 5009 # SYS_mmap
syscall # syscall
bne $a3, $zero, .perror # perror?
nop # delay slot
ld $t1, 48($s0) # clear
dadd $t0, $a0, $a1 # t0 = end of mapping
dsub $t1, $t0, $t1 # t1 = t0 - clear
.align:
beq $t0, $t1, .continue # already finished
nop # delay slot
andi $t2, $t1, 7 # t1 & 7?
bnez $t2, .filld # start filling longs
nop # delay slot
.filld:
dsub $t2, $t0, $t1 # t2 = t0 - t1
sltiu $t2, $t2, 64 # t2 < 64?
bne $t2, $zero, .fillb # fill bytes
nop # delay slot
sd $zero, ($t1) # zero doubleword
daddi $t1, 8 # next doubleword
sd $zero, ($t1) # zero doubleword
daddi $t1, 8 # next doubleword
sd $zero, ($t1) # zero doubleword
daddi $t1, 8 # next doubleword
sd $zero, ($t1) # zero doubleword
daddi $t1, 8 # next doubleword
sd $zero, ($t1) # zero doubleword
daddi $t1, 8 # next doubleword
sd $zero, ($t1) # zero doubleword
daddi $t1, 8 # next doubleword
sd $zero, ($t1) # zero doubleword
daddi $t1, 8 # next doubleword
sd $zero, ($t1) # zero doubleword
daddi $t1, 8 # next doubleword
j .filld # fill either doubleword or byte
nop # delay slot
.fillb:
beq $t0, $t1, .continue # already finished?
nop # delay slot
sb $zero, ($t1) # clear byte
daddi $t1, $t1, 1 # t1++
.continue:
daddi $s0, $s0, 56 # s0 = next action
j .next_action # next action
nop # delay slot
.do_mmap_anon:
ld $t0, 8($s0) # vm address
ld $t1, 16($s0) # file_offset
ld $t2, 24($s0) # protection
ld $t3, 32($s0) # length
ld $v0, 40($s0) # flags
li $v1, -1 # fd
j .do_mmap_1 # do mmap
nop # branch delay slot
.open_file:
li $v0, 5002 # SYS_open
daddi $a0, $s0, 8 # start of name
move $a1, $zero # flags = O_RDONLY
move $a2, $zero # mode = 0
syscall # syscall
bne $a3, $zero, .perror # perror
nop # delay slot
daddi $s0, $s0, 8 # start of string
.nextc:
lb $t0, ($s0) # load byte
daddi $s0, $s0, 1 # s0++
bne $t0, $zero, .nextc # next character?
nop # delay slot
daddi $s0, $s0, 7 # adjust for round
li $t2, -8 # t2 = -8
and $s0, $s0, $t2 # mask for round
andi $t0, $s1, 16 # t1 = s1 & 16
move $t1, $sp # address of primary fd
beqz $t0, .primary # primary fd?
nop # delay slot
daddi $t1, $t1, 8 # address of secondary fd
.primary:
sd $v0, ($t1) # store fd
j .next_action # next action
nop # delay slot
.perror:
move $a0, $v0 # errno
li $v0, 5058 # SYS_exit
syscall # syscall
.rest_of_exec:
move $s1, $s2 # original SP
ld $t0, ($s1) # argc
dsll $t0, $t0, 3 # argc *= 3
daddi $t0, $t0, 16 # argc += 16
dadd $s1, $s1, $t0 # s1 = start of envp
.skipenv:
ld $t0, ($s1) # t0 = *s1
daddi $s1, $s1, 8 # s1++
bne $t0, $zero, .skipenv # skip again
nop # delay slot
dla $t3, .auxvtab # address of auxv table
.one_auxv:
ld $t0, ($s1) # t0 = auxv type
li $t1, 10 # t1 = 10
beqz $t0, .finish # is AT_IGNORE?
nop # delay slot
sltu $t1, $t0, $t1 # t1 = t0 < num offsets
beqz $t1, .next # next auxv
nop # delay slot
dsll $t1, $t0, 2 # t1 = t0 * 4
dadd $t1, $t3, $t1 # t1 = .auxvtab + t1
lw $t2, ($t1) # t2 = *t1
beqz $t2, .next # skip auxv
nop # delay slot
dadd $t2, $s0, $t2 # t2 = s0 + t2
ld $t2, ($t2) # t2 = *t2
sd $t2, 8($s1) # set auxv value
.next:
daddi $s1, $s1, 16 # next auxv
j .one_auxv # next auxv
nop # delay slot
.finish:
ld $t0, 8($sp) # secondary fd
li $t1, -1 # t1 = -1
ld $s1, ($sp) # s1 = primary fd
li $v0, 5003 # SYS_close
beq $t0, $t2, .finish1 # secondary fd set?
nop # delay slot
move $a0, $t0 # secondary fd
syscall # syscall
li $v0, 5003 # SYS_close
.finish1:
move $a0, $s1 # primary fd
syscall # syscall
.jump:
move $v0, $zero # rtld_fini
ld $t0, 8($s0) # entry
move $sp, $s2 # restore stack pointer, delay slot
jr $t0 # enter
nop # delay slot
.auxvtab:
.long 0 # 0
.long 0 # 1
.long 0 # 2
.long 40 # 3 AT_PHDR
.long 24 # 4 AT_PHENT
.long 32 # 5 AT_PHNUM
.long 0 # 6
.long 48 # 7 AT_BASE
.long 0 # 8
.long 16 # 9 AT_ENTRY
.timespec:
.quad 10
.quad 10
# Local Variables:
# asm-comment-char: 35
# End:

221
exec/loader-mipsel.s Normal file
View File

@ -0,0 +1,221 @@
# Copyright (C) 2023 Free Software Foundation, Inc.
#
# This file is part of GNU Emacs.
#
# GNU Emacs is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published
# by the Free Software Foundation, either version 3 of the License,
# or (at your option) any later version.
#
# GNU Emacs 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
# 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/>.
include(`config-mips.m4')
# Make sure not to use t4 through t7, in order to maintain portability
# with N32 ABI systems.
.set noreorder # delay slots managed by hand
.section .text
.global __start
__start:
dnl li $v0, SYSCALL_nanosleep # SYS_nanosleep
dnl la $a0, .timespec # rqtp
dnl li $a1, 0 # rmtp
dnl syscall # syscall
lw $s6, ($sp) # original stack pointer
addi $s0, $sp, 8 # start of load area
addi $sp, -8 # primary fd, secondary fd
li $t0, -1 # secondary fd
sw $t0, 4($sp) # initialize secondary fd
.next_action:
lw $s2, ($s0) # action number
nop # delay slot
andi $t0, $s2, 15 # t0 = s2 & 15
beqz $t0, .open_file # open file?
li $t1, 3 # t1 = 3, delay slot
beq $t0, $t1, .rest_of_exec # jump to code
li $t1, 4 # t1 = 4, delay slot
beq $t0, $t1, .do_mmap_anon # anonymous mmap
.do_mmap:
lw $a0, 4($s0) # vm_address, delay slot
lw $v1, 8($s0) # file_offset
lw $a2, 12($s0) # protection
lw $a1, 16($s0) # length
lw $a3, 20($s0) # flags
lw $v0, ($sp) # primary fd
andi $t1, $s2, 16 # t1 = s2 & 16
beqz $t1, .do_mmap_1 # secondary fd?
nop # delay slot
lw $v0, 4($sp) # secondary fd
nop # delay slot
.do_mmap_1:
SYSCALL(`$v0',`$v1',`$zero',`$zero') # syscall args
li $v0, SYSCALL_mmap # SYS_mmap
syscall # syscall
bne $a3, $zero, .perror # perror
RESTORE() # delay slot, restore sp
lw $s5, 24($s0) # clear
add $t0, $a0, $a1 # t0 = length + vm_address, delay slot
sub $t1, $t0, $s5 # t1 = t0 - clear
.align:
beq $t0, $t1, .continue # already finished?
nop # delay slot
andi $t2, $t1, 3 # t1 & 3?
bnez $t2, .fillw # start filling longs
nop # delay slot
sb $zero, ($t1) # clear byte
addi $t1, $t1, 1 # t1++
j .align # continue
nop # delay slot
.fillw:
sub $t2, $t0, $t1 # t2 = t0 - t1
sltiu $t2, $t2, 32 # r2 < 32?
bne $t2, $zero, .fillb # fill bytes
nop # delay slot
sw $zero, ($t1) # zero word
addi $t1, $t1, 4 # next word
sw $zero, ($t1) # zero word
addi $t1, $t1, 4 # next word
sw $zero, ($t1) # zero word
addi $t1, $t1, 4 # next word
sw $zero, ($t1) # zero word
addi $t1, $t1, 4 # next word
sw $zero, ($t1) # zero word
addi $t1, $t1, 4 # next word
sw $zero, ($t1) # zero word
addi $t1, $t1, 4 # next word
sw $zero, ($t1) # zero word
addi $t1, $t1, 4 # next word
sw $zero, ($t1) # zero word
addi $t1, $t1, 4 # next word
j .fillw # fill either word or byte
nop # delay slot
.fillb:
beq $t0, $t1, .continue # already finished?
nop # delay slot
sb $zero, ($t1) # clear byte
addi $t1, $t1, 1 # t1++
.continue:
addi $s0, $s0, 28 # s0 = next action
j .next_action # next action
nop # delay slot
.do_mmap_anon:
lw $v1, 8($s0) # file_offset
lw $a2, 12($s0) # protection
lw $a1, 16($s0) # length
lw $a3, 20($s0) # flags
li $t4, -1 # fd
j .do_mmap_1 # do mmap
nop # delay slot
.open_file:
li $v0, SYSCALL_open # SYS_open
addi $a0, $s0, 4 # start of name
move $a1, $zero # flags = O_RDONLY
move $a2, $zero # mode = 0
syscall # syscall
bne $a3, $zero, .perror # perror
addi $s0, $s0, 4 # start of string, delay slot
.nextc:
lb $t0, ($s0) # load byte
addi $s0, $s0, 1 # s0++
bne $t0, $zero, .nextc # next character?
nop # delay slot
addi $s0, $s0, 3 # adjust for round
li $t2, -4 # t2 = -4
and $s0, $s0, $t2 # mask for round
andi $t0, $s2, 16 # t1 = s2 & 16
beqz $t0, .primary # primary fd?
move $t0, $sp # address of primary fd, delay slot
addi $t0, $t0, 4 # address of secondary fd
.primary:
sw $v0, ($t0) # store fd
j .next_action # next action
nop # delay slot
.perror:
move $a0, $v0 # errno
li $v0, SYSCALL_exit # SYS_exit
syscall # syscall
.rest_of_exec:
move $s1, $s6 # s1 = original SP
lw $t0, ($s1) # argc
nop # delay slot
sll $t0, $t0, 2 # argc *= 4
addi $t0, $t0, 8 # argc += 8
add $s1, $s1, $t0 # s1 = start of envp
.skipenv:
lw $t0, ($s1) # t0 = *s1
addi $s1, $s1, 4 # s1++
bne $t0, $zero, .skipenv # skip again
nop # delay slot
la $s2, .auxvtab # address of auxv table
.one_auxv:
lw $t0, ($s1) # t0 = auxv type
li $t1, 10 # t1 = 10, delay slot
beqz $t0, .finish # is AT_IGNORE?
sltu $t1, $t0, $t1 # t1 = t0 < num offsets, delay slot
beq $t1, $zero, .next # next auxv
sll $t1, $t0, 2 # t1 = t0 * 4, delay slot
add $t1, $s2, $t1 # t1 = .auxvtab + t1
lw $t2, ($t1) # t2 = *t1
nop # delay slot
beqz $t2, .next # skip auxv
add $t2, $s0, $t2 # t2 = s0 + t2
lw $t2, ($t2) # t2 = *t2
nop # delay slot
sw $t2, 4($s1) # set auxv value
.next:
addi $s1, $s1, 8 # next auxv
j .one_auxv # next auxv
nop # delay slot
.finish:
lw $t0, 4($sp) # secondary fd
lw $s1, ($sp) # primary fd, delay slot, preserved
li $t2, -1 # immediate -1
beq $t0, $t2, .finish1 # secondary fd set?
li $v0, SYSCALL_close # SYS_close, delay slot
move $a0, $t0 # fd
syscall # syscall
li $v0, SYSCALL_close # SYS_close
.finish1:
move $a0, $s1 # primary fd
syscall # syscall
li $v0, SYSCALL_prctl # SYS_prctl
li $a0, 45 # PR_SET_FP_MODE
lw $a1, 28($s0) # fpu_mode
move $a2, $zero # arg3
move $a3, $zero # arg4
SYSCALL(`$a2',`$a2',`$a2',`$a2') # syscall args
syscall # syscall
RESTORE() # restore sp
.jump:
move $v0, $zero # rtld_fini
lw $t0, 4($s0) # entry
move $sp, $s6 # restore stack pointer, delay slot
jr $t0 # enter
nop # delay slot
.auxvtab:
.long 0 # 0
.long 0 # 1
.long 0 # 2
.long 20 # 3 AT_PHDR
.long 12 # 4 AT_PHENT
.long 16 # 5 AT_PHNUM
.long 0 # 6
.long 24 # 7 AT_BASE
.long 0 # 8
.long 8 # 9 AT_ENTRY
.timespec:
.long 10
.long 10
# Local Variables:
# asm-comment-char: 35
# End:

188
exec/loader-x86.s Normal file
View File

@ -0,0 +1,188 @@
define(`CC', `
dnl')
CC Copyright (C) 2023 Free Software Foundation, Inc.
CC
CC This file is part of GNU Emacs.
CC
CC GNU Emacs is free software: you can redistribute it and/or modify
CC it under the terms of the GNU General Public License as published
CC by the Free Software Foundation, either version 3 of the License,
CC or (at your option) any later version.
CC
CC GNU Emacs is distributed in the hope that it will be useful, but
CC WITHOUT ANY WARRANTY; without even the implied warranty of
CC MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
CC General Public License for more details.
CC
CC You should have received a copy of the GNU General Public License
CC along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
.section .text
.global _start
_start:
#movl $162, %eax CC SYS_nanosleep
#leal timespec, %ebx
#xorl %ecx, %ecx
#int $0x80
leal 8(%esp), %ebp CC ebp = start of load area
subl $8, %esp CC (%esp) = primary fd, 4(%esp) = secondary fd
movl $-1, 4(%esp)
.next_action:
movl (%ebp), %edx CC edx = action number
andl $-17, %edx
cmpl $0, %edx CC open file?
je .open_file
cmpl $3, %edx CC jump?
je .rest_of_exec
cmpl $4, %edx CC anonymous mmap?
je .do_mmap_anon
.do_mmap:
subl $24, %esp
movl $90, %eax CC SYS_old_mmap
movl %esp, %ebx
movl 4(%ebp), %ecx CC address
movl %ecx, (%esp)
movl 16(%ebp), %ecx CC length
movl %ecx, 4(%esp)
movl 12(%ebp), %ecx CC protection
movl %ecx, 8(%esp)
movl 20(%ebp), %ecx CC flags
movl %ecx, 12(%esp)
testl $16, (%ebp) CC primary?
movl 28(%esp), %ecx
cmovzl 24(%esp), %ecx
movl %ecx, 16(%esp) CC fd
movl 8(%ebp), %ecx CC offset
movl %ecx, 20(%esp)
.do_mmap_1:
int $0x80
addl $24, %esp CC restore esp
cmpl $-1, %eax CC mmap failed?
je .perror
movl 24(%ebp), %ecx CC clear
testl %ecx, %ecx
jz .continue
movl 4(%ebp), %esi CC start of mapping
addl 16(%ebp), %esi CC end of mapping
subl %ecx, %esi CC start of clear area
.again:
testl %ecx, %ecx
jz .continue
subl $1, %ecx
movb $0, (%esi, %ecx, 1)
jmp .again
.continue:
leal 28(%ebp), %ebp
jmp .next_action
.do_mmap_anon:
subl $24, %esp
movl $90, %eax CC SYS_old_mmap
movl %esp, %ebx
movl 4(%ebp), %ecx CC address
movl %ecx, (%esp)
movl 16(%ebp), %ecx CC length
movl %ecx, 4(%esp)
movl 12(%ebp), %ecx CC protection
movl %ecx, 8(%esp)
movl 20(%ebp), %ecx CC flags
movl %ecx, 12(%esp)
movl $-1, 16(%esp) CC fd
movl 8(%ebp), %ecx CC offset
movl %ecx, 20(%esp)
jmp .do_mmap_1
.open_file:
movl $5, %eax CC SYS_open
leal 4(%ebp), %ebx CC ebx = %esp + 8
pushl %ebx
xorl %ecx, %ecx CC flags = O_RDONLY
xorl %edx, %edx CC mode = 0
int $0x80
cmpl $-1, %eax CC open failed?
jle .perror
movl %ebp, %esi CC (esi) = original action number
popl %ebp CC ebp = start of string
decl %ebp
.nextc:
incl %ebp
cmpb $0, (%ebp) CC *ebp == 0?
jne .nextc
addl $4, %ebp CC adjust past ebp prior to rounding
andl $-4, %ebp CC round ebp up to the next long
testl $16, (%esi) CC original action number & 16?
jz .primary
movl %eax, 4(%esp) CC secondary fd = eax
jmp .next_action
.primary:
movl %eax, (%esp) CC primary fd = eax
jmp .next_action
.perror:
movl %eax, %ebx
negl %ebx
movl $1, %eax
int $0x80
.rest_of_exec:
movl 8(%esp), %ecx CC ecx = original stack pointer
movl (%ecx), %esi CC esi = argc
leal 8(%ecx, %esi, 4), %ecx CC ecx = start of environ
.skip_environ:
movl (%ecx), %esi CC envp[N]
subl $4, %ecx
testl %esi, %esi CC envp[n] ?
jnz .skip_environ CC otherwise, esi is now at the start of auxv
.one_auxv:
movl (%ecx), %esi CC auxv type
leal 8(%ecx), %ecx CC skip to next auxv
testl %esi, %esi CC is 0?
jz .cleanup
cmpl $3, %esi CC is AT_PHDR
je .replace_phdr
cmpl $4, %esi CC is AT_PHENT?
je .replace_phent
cmpl $5, %esi CC is AT_PHNUM?
je .replace_phnum
cmpl $9, %esi CC is AT_ENTRY?
je .replace_entry
cmpl $7, %esi CC is AT_BASE
je .replace_base
jmp .one_auxv
.replace_phdr:
movl 20(%ebp), %esi
movl %esi, -4(%ecx)
jmp .one_auxv
.replace_phent:
movl 12(%ebp), %esi
movl %esi, -4(%ecx)
jmp .one_auxv
.replace_phnum:
movl 16(%ebp), %esi
movl %esi, -4(%ecx)
jmp .one_auxv
.replace_entry:
movl 8(%ebp), %esi
movl %esi, -4(%ecx)
jmp .one_auxv
.replace_base:
movl 24(%ebp), %esi
movl %esi, -4(%ecx)
jmp .one_auxv
.cleanup:
movl $6, %eax CC SYS_close
cmpl $1, -4(%esp) CC see if interpreter fd is set
jne .cleanup_1
movl -4(%esp), %ebx
int $0x80
.cleanup_1:
movl $6, %eax CC SYS_close
movl (%esp), %ebx
int $0x80
.enter:
pushl $0
popfl CC restore floating point state
movl 8(%esp), %esp CC restore initial stack pointer
xorl %edx, %edx CC clear rtld_fini
jmpl *4(%ebp) CC entry
timespec:
.long 10
.long 10

180
exec/loader-x86_64.s Normal file
View File

@ -0,0 +1,180 @@
define(`CC', `
dnl')
CC Copyright (C) 2023 Free Software Foundation, Inc.
CC
CC This file is part of GNU Emacs.
CC
CC GNU Emacs is free software: you can redistribute it and/or modify
CC it under the terms of the GNU General Public License as published
CC by the Free Software Foundation, either version 3 of the License,
CC or (at your option) any later version.
CC
CC GNU Emacs is distributed in the hope that it will be useful, but
CC WITHOUT ANY WARRANTY; without even the implied warranty of
CC MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
CC General Public License for more details.
CC
CC You should have received a copy of the GNU General Public License
CC along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
.section .text
.global _start
_start:
#movq $35, %rax CC SYS_nanosleep
#leaq timespec(%rip), %rdi
#xorq %rsi, %rsi
#syscall
popq %r13 CC original SP
popq %r15 CC size of load area.
movq $-1, %r12 CC r12 is the interpreter fd
.next_action:
movq (%rsp), %r14 CC action number
movq %r14, %r15 CC original action number
andq $-17, %r14
cmpq $0, %r14 CC open file?
je .open_file
cmpq $3, %r14 CC jump?
je .rest_of_exec
cmpq $4, %r14 CC anonymous mmap?
je .do_mmap_anon
.do_mmap:
movq $9, %rax CC SYS_mmap
movq 8(%rsp), %rdi CC address
movq 16(%rsp), %r9 CC offset
movq 24(%rsp), %rdx CC protection
movq 32(%rsp), %rsi CC length
movq 40(%rsp), %r10 CC flags
CC set r8 to the primary fd unless r15 & 16
testq $16, %r15
movq %r12, %r8
cmovzq %rbx, %r8
.do_mmap_1:
syscall
cmpq $-1, %rax CC mmap failed
je .perror
movq 48(%rsp), %r9 CC clear
testq %r9, %r9
jz .continue
movq 8(%rsp), %r10 CC start of mapping
addq 32(%rsp), %r10 CC end of mapping
subq %r9, %r10 CC start of clear area
.again:
testq %r9, %r9
jz .continue
subq $1, %r9
movb $0, (%r10, %r9, 1)
jmp .again
.continue:
leaq 56(%rsp), %rsp
jmp .next_action
.do_mmap_anon:
movq $9, %rax CC SYS_mmap
movq 8(%rsp), %rdi CC address
movq 16(%rsp), %r9 CC offset
movq 24(%rsp), %rdx CC protection
movq 32(%rsp), %rsi CC length
movq 40(%rsp), %r10 CC flags
movq $-1, %r8 CC -1
jmp .do_mmap_1
.open_file:
movq $2, %rax CC SYS_open
leaq 8(%rsp), %rdi CC rdi = %rsp + 8
xorq %rsi, %rsi CC flags = O_RDONLY
xorq %rdx, %rdx CC mode = 0
syscall
cmpq $-1, %rax CC open failed
jle .perror
movq %rdi, %rsp CC rsp = start of string
subq $1, %rsp
.nextc:
addq $1, %rsp
cmpb $0, (%rsp) CC *rsp == 0?
jne .nextc
addq $8, %rsp CC adjust past rsp prior to rounding
andq $-8, %rsp CC round rsp up to the next quad
testq $16, %r15 CC r15 & 16?
jz .primary
movq %rax, %r12 CC otherwise, move fd to r12
jmp .next_action
.primary:
movq %rax, %rbx CC if not, move fd to rbx
jmp .next_action
.perror:
movq %rax, %r12 CC error code
negq %r12
movq $1, %rax CC SYS_write
movq $1, %rdi CC stdout
leaq error(%rip), %rsi CC buffer
movq $23, %rdx CC count
syscall
movq $60, %rax CC SYS_exit
movq %r12, %rdi CC code
syscall
.rest_of_exec: CC rsp now points to six quads:
movq %rsp, %r8 CC now, they are r8
movq %r13, %rsp CC restore SP
popq %r10 CC argc
leaq 8(%rsp,%r10,8), %rsp CC now at start of environ
.skip_environ:
popq %r10 CC envp[N]
testq %r10, %r10 CC envp[n]?
jnz .skip_environ CC otherwise, rsp is now at the start of auxv
.one_auxv:
popq %rcx CC auxv type
addq $8, %rsp CC skip value
testq %rcx, %rcx CC is 0?
jz .cleanup
cmpq $3, %rcx CC is AT_PHDR?
je .replace_phdr
cmpq $4, %rcx CC is AT_PHENT?
je .replace_phent
cmpq $5, %rcx CC is AT_PHNUM?
je .replace_phnum
cmpq $9, %rcx CC is AT_ENTRY?
je .replace_entry
cmpq $7, %rcx CC is AT_BASE?
je .replace_base
jmp .one_auxv
.replace_phdr:
movq 40(%r8), %r9
movq %r9, -8(%rsp) CC set at_phdr
jmp .one_auxv
.replace_phent:
movq 24(%r8), %r9
movq %r9, -8(%rsp) CC set at_phent
jmp .one_auxv
.replace_phnum:
movq 32(%r8), %r9
movq %r9, -8(%rsp) CC set at_phnum
jmp .one_auxv
.replace_entry:
movq 16(%r8), %r9
movq %r9, -8(%rsp) CC set at_entry
jmp .one_auxv
.replace_base:
movq 48(%r8), %r9
movq %r9, -8(%rsp) CC set at_base
jmp .one_auxv
.cleanup:
movq $3, %rax CC SYS_close
cmpq $-1, %r12 CC see if interpreter fd is set
jne .cleanup_1
movq %r12, %rdi
syscall
.cleanup_1:
movq $3, %rax CC SYS_close
movq %rbx, %rdi
syscall
.enter:
pushq $0
popfq CC clear FP state
movq %r13, %rsp CC restore SP
xorq %rdx, %rdx CC clear rtld_fini
jmpq *8(%r8) CC entry
error:
.ascii "_start: internal error."
timespec:
.quad 10
.quad 10

44
exec/mipsel-user.h Normal file
View File

@ -0,0 +1,44 @@
/* Program execution for Emacs.
Copyright (C) 2023 Free Software Foundation, Inc.
This file is part of GNU Emacs.
GNU Emacs is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or (at
your option) any later version.
GNU Emacs 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 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/>. */
#ifndef _MIPSEL_USER_H_
#define _MIPSEL_USER_H_
#include <sgidefs.h>
#include <sys/user.h>
#ifndef ELF_NGREG
#define ELF_NGREG 45
#endif /* ELF_NGREG */
/* This file defines a structure containing user mode general purpose
registers on 32-bit mipsel systems. */
struct mipsel_regs
{
/* General purpose registers. */
uint64_t gregs[ELF_NGREG];
};
#endif /* _MIPSEL_USER_H_ */

289
exec/mipsfpu.c Normal file
View File

@ -0,0 +1,289 @@
/* Program execution for Emacs.
Copyright (C) 2023 Free Software Foundation, Inc.
This file is part of GNU Emacs.
GNU Emacs is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or (at
your option) any later version.
GNU Emacs 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 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/>. */
#include <config.h>
#include <errno.h>
#include "mipsfpu.h"
/* OABI MIPS systems support several different modes of execution.
Each mode differs in the size and utilization of the hardware
floating-point registers.
Linux normally sets the floating point mode to one appropriate for
execution, taking into account the floating point modes of the
interpreter and executable binaries. However, this logic is
forsaken when the `execve' system call is overwritten.
Thus, the correct floating point mode must be determined and set
within the loader binary. */
/* Various constants used throughout this code. */
#define MIPS_ABI_FP_ANY 0 /* FP ABI doesn't matter */
#define MIPS_ABI_FP_DOUBLE 1 /* -mdouble-float */
#define MIPS_ABI_FP_SINGLE 2 /* -msingle-float */
#define MIPS_ABI_FP_SOFT 3 /* -msoft-float */
#define MIPS_ABI_FP_OLD_64 4 /* -mips32r2 -mfp64 */
#define MIPS_ABI_FP_XX 5 /* -mfpxx */
#define MIPS_ABI_FP_64 6 /* -mips32r2 -mfp64 */
#define MIPS_ABI_FP_64A 7 /* -mips32r2 -mfp64 -mno-odd-spreg */
#define EF_MIPS_NOREORDER 1 /* A .noreorder directive was used. */
#define EF_MIPS_PIC 2 /* Contains PIC code. */
#define EF_MIPS_CPIC 4 /* Uses PIC calling sequence. */
#define EF_MIPS_XGOT 8
#define EF_MIPS_64BIT_WHIRL 16
#define EF_MIPS_ABI2 32
#define EF_MIPS_ABI_ON32 64
#define EF_MIPS_FP64 512 /* Uses FP64 (12 callee-saved). */
#define EF_MIPS_NAN2008 1024 /* Uses IEEE 754-2008 NaN encoding. */
#define EF_MIPS_ARCH 0xf0000000 /* MIPS architecture level. */
/* Structure describing the requirements of a single floating-point
ABI. */
struct mode_description
{
/* Whether or not the ABI only executes single precision
instructions, and can operate in both 32-bit or 64-bit floating
point mode. */
bool single;
/* Whether or not the ABI performs floating point operations in
software, using integer registers. */
bool soft;
/* Whether or not the ABI requires the use of 64-bit floating point
registers. */
bool fr1;
/* Whether or not the ABI requires the use of 64-bit floating point
registers on NABI systems, and 32-bit ones on OABI systems. */
bool frdefault;
/* Whether or not this ABI requires single precision floating point
emulation. */
bool fre;
};
static struct mode_description fpu_reqs[] =
{
[MIPS_ABI_FP_ANY] = { true, true, true, true, true, },
[MIPS_ABI_FP_DOUBLE] = { false, false, false, true, true, },
[MIPS_ABI_FP_SINGLE] = { true, false, false, false, false, },
[MIPS_ABI_FP_SOFT] = { false, true, false, false, false, },
[MIPS_ABI_FP_OLD_64] = { false, false, false, false, false, },
[MIPS_ABI_FP_XX] = { false, false, true, true, true, },
[MIPS_ABI_FP_64] = { false, false, true, false, false, },
[MIPS_ABI_FP_64A] = { false, false, true, false, true, },
};
/* Return whether or not the given floating-point ABI is valid. */
static bool
valid_abi_p (int abi)
{
switch (abi)
{
case MIPS_ABI_FP_ANY:
case MIPS_ABI_FP_DOUBLE:
case MIPS_ABI_FP_SINGLE:
case MIPS_ABI_FP_SOFT:
case MIPS_ABI_FP_OLD_64:
case MIPS_ABI_FP_XX:
case MIPS_ABI_FP_64:
case MIPS_ABI_FP_64A:
return true;
default:
return false;
}
}
/* Return the floating point mode appropriate for the specified
floating point ABI. */
static int
fp_mode_for_abi (int abi)
{
struct mode_description *desc;
desc = &fpu_reqs[abi];
if (desc->fre)
return FP_FRE;
else if (desc->fr1)
return FP_FR1;
return FP_FR0;
}
/* Determine whether or not the CPU is capable of operating in FR0
floating point mode. */
bool
cpu_supports_fr0_p (void)
{
#if defined __mips_isa_rev && __mips_isa_rev >= 6
return true;
#else /* !defined __mips_isa_rev | mips_isa_rev < 6 */
return false;
#endif /* defined __mips_isa_rev && mips_isa_rev >= 6 */
}
/* Determine the FPU mode for the executable whose ELF header is
HEADER. If INTERPRETER is non-NULL, also take an interpreter whose
header is INTERPRETER into account.
ABIFLAGS should be HEADER's corresponding PT_MIPS_ABIFLAGS program
header, and ABIFLAGS1 should be that of INTERPRETER, if set. Both
fields may be NULL if no PT_MIPS_ABIFLAGS header is present; in
that case, use HEADER->e_flags to determine the ABI instead.
Return the FPU mode in *MODE. Value is 0 upon success, 1
otherwise, with errno set. */
int
determine_fpu_mode (elf_header *header, elf_header *interpreter,
int *mode, struct mips_elf_abi_flags *abiflags,
struct mips_elf_abi_flags *abiflags1)
{
int exec_abi, interpreter_abi;
struct mode_description *exec_desc, *interpreter_desc, common;
/* Figure out the executable's floating point ABI. First, consult
header->e_flags, and use the old 64-bit floating point ABI if it
is specified. */
exec_abi = MIPS_ABI_FP_ANY;
/* First, check HEADER->e_flags. */
if (header->e_flags & EF_MIPS_FP64)
exec_abi = MIPS_ABI_FP_OLD_64;
/* Next, use ABIFLAGS if it exists. */
if (abiflags && valid_abi_p (abiflags->fp_abi))
exec_abi = abiflags->fp_abi;
else if (abiflags)
{
errno = ENOEXEC;
return 1;
}
/* Now determine that of the interpreter. */
interpreter_abi = MIPS_ABI_FP_ANY;
if (interpreter)
{
if (interpreter->e_flags & EF_MIPS_FP64)
interpreter_abi = MIPS_ABI_FP_OLD_64;
if (abiflags1 && valid_abi_p (abiflags->fp_abi))
interpreter_abi = abiflags->fp_abi;
else if (abiflags1)
{
errno = ELIBBAD;
return 1;
}
}
/* If no interpreter flag is set, just return that of the
executable. */
if (!interpreter)
{
*mode = fp_mode_for_abi (exec_abi);
return 0;
}
/* Otherwise, compare both ABIs and try to find one which will run
both kinds of code.
First, see if there's an easy way out: both ABIs are identical,
or one ABI is MIPS_ABI_FP_ANY. */
if (exec_abi == interpreter_abi)
{
*mode = fp_mode_for_abi (exec_abi);
return 0;
}
else if (exec_abi == MIPS_ABI_FP_ANY)
{
*mode = fp_mode_for_abi (interpreter_abi);
return 0;
}
else if (interpreter_abi == MIPS_ABI_FP_ANY)
{
*mode = fp_mode_for_abi (exec_abi);
return 0;
}
/* If that doesn't work, compare various characteristics of both
ABIs and select an appropriate floating point mode. */
exec_desc = &fpu_reqs[exec_abi];
interpreter_desc = &fpu_reqs[interpreter_abi];
/* Merge both sets of requirements. */
common.single = exec_desc->single && interpreter_desc->single;
common.soft = exec_desc->soft && interpreter_desc->soft;
common.fr1 = exec_desc->fr1 && interpreter_desc->fr1;
common.frdefault = exec_desc->frdefault && interpreter_desc->frdefault;
common.fre = exec_desc->fre && interpreter_desc->fre;
/* Default to a mode capable of running code expecting 32-bit
registers. */
if (!(header->e_flags & EF_MIPS_ABI2))
*mode = FP_FR0;
else
/* But in this case, use FR1. */
*mode = FP_FR1;
if (common.fre && !common.frdefault && !common.fr1)
/* Floating point emulation mode is required. */
*mode = FP_FRE;
else if ((common.fr1 && common.frdefault)
|| (common.single && !common.frdefault)
|| common.fr1)
/* 64-bit mode is required. */
*mode = FP_FR1;
else if (!common.fre && !common.frdefault
&& !common.fr1 && !common.single
&& !common.soft)
{
/* The floating point modes specified are incompatible. */
errno = ELIBBAD;
return -1;
}
return 0;
}

82
exec/mipsfpu.h Normal file
View File

@ -0,0 +1,82 @@
/* Program execution for Emacs.
Copyright (C) 2023 Free Software Foundation, Inc.
This file is part of GNU Emacs.
GNU Emacs is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or (at
your option) any later version.
GNU Emacs 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 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/>. */
#ifndef _MIPSFPU_H_
#define _MIPSFPU_H_
#include "exec.h"
struct mips_elf_abi_flags
{
/* Version of flags structure. */
uint16_t version;
/* The level of the ISA: 1-5, 32, 64. */
uint8_t isa_level;
/* The revision of ISA: 0 for MIPS V and below, 1-n otherwise. */
uint8_t isa_rev;
/* The size of general purpose registers. */
uint8_t gpr_size;
/* The size of co-processor 1 registers. */
uint8_t cpr1_size;
/* The size of co-processor 2 registers. */
uint8_t cpr2_size;
/* The floating-point ABI. */
uint8_t fp_abi;
/* Mask of processor-specific extensions. */
uint32_t isa_ext;
/* Mask of ASEs used. */
uint32_t ases;
/* Mask of general flags. */
uint32_t flags1;
/* Mask of general flags. */
uint32_t flags2;
};
/* Floating point modes. */
#define FP_FR0 0
#define FP_FR1 1
#define FP_FRE 3
/* Defined in mipsfpu.c. */
extern bool cpu_supports_fr0_p (void);
extern int determine_fpu_mode (elf_header *, elf_header *,
int *, struct mips_elf_abi_flags *,
struct mips_elf_abi_flags *);
#endif /* _MIPSFPU_H_ */

105
exec/test.c Normal file
View File

@ -0,0 +1,105 @@
/* Program execution for Emacs.
Copyright (C) 2023 Free Software Foundation, Inc.
This file is part of GNU Emacs.
GNU Emacs is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or (at
your option) any later version.
GNU Emacs 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 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/>. */
#include <config.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/wait.h>
#include "exec.h"
static void
print_usage (void)
{
fprintf (stderr, "test loader-name program [args...]\n"
"Run the given program using the specified loader.\n");
}
extern char **environ;
/* This program uses libexec to wrap the execution of a child
process. */
int
main (int argc, char **argv)
{
pid_t pid, child;
int sig;
sigset_t sigset;
/* Check that there are a sufficient number of arguments. */
if (argc < 3)
{
print_usage ();
return 1;
}
exec_init (argv[1]);
/* Block SIGCHLD to avoid reentrant modification of the child
process list. */
sigemptyset (&sigset);
sigaddset (&sigset, SIGCHLD);
sigprocmask (SIG_BLOCK, &sigset, NULL);
if (!(pid = fork ()))
{
tracing_execve (argv[2], argv + 2, environ);
fprintf (stderr, "tracing_execve: %s\n",
strerror (errno));
exit (1);
}
else if (after_fork (pid))
{
fprintf (stderr, "after_fork: %s\n",
strerror (errno));
exit (1);
}
/* Now start waiting for child processes to exit. */
while (true)
{
child = exec_waitpid (-1, &sig, 0);
/* If pid is -1, a system call has been handled. */
if (child == -1)
continue;
/* If the main process exits, then exit as well. */
if (child == pid && !WIFSTOPPED (sig))
return (WIFEXITED (sig)
? WEXITSTATUS (sig)
: WTERMSIG (sig));
}
}

972
exec/trace.c Normal file
View File

@ -0,0 +1,972 @@
/* Program execution for Emacs.
Copyright (C) 2023 Free Software Foundation, Inc.
This file is part of GNU Emacs.
GNU Emacs is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or (at
your option) any later version.
GNU Emacs 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 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/>. */
#include <config.h>
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <limits.h>
#include <stddef.h>
#include <string.h>
#include <assert.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include "exec.h"
#include SYSCALL_HEADER
#include USER_HEADER
#ifdef __aarch64__
#include <sys/uio.h> /* for struct iovec */
#include <linux/elf.h> /* for NT_* */
#endif /* __aarch64__ */
/* Program tracing functions.
The main entry point is the function `tracing_execve', which traces
the thread and calls exec. Each time that thread calls `clone',
the new child is traced as well.
Instead of calling `waitpid', call `exec_waitpid' instead. */
/* Number of tracees children are allowed to create. */
#define MAX_TRACEES 1024
#ifdef __aarch64__
/* Place PID's registers into *REGS. Return 1 upon failure, else
0. */
int
aarch64_get_regs (pid_t pid, USER_REGS_STRUCT *regs)
{
struct iovec iov;
iov.iov_base = regs;
iov.iov_len = sizeof *regs;
return (ptrace (PTRACE_GETREGSET, pid, NT_PRSTATUS,
&iov) != 0);
}
/* Set PID's registers to *REGS. If SYSCALL_P, also update the
current system call number to the `x8' register.
Value is 1 upon failure, else 0. */
int
aarch64_set_regs (pid_t pid, USER_REGS_STRUCT *regs,
bool syscall_p)
{
struct iovec iov;
USER_WORD callno;
long rc;
/* Write the user registers. */
iov.iov_base = regs;
iov.iov_len = sizeof *regs;
rc = ptrace (PTRACE_SETREGSET, pid, NT_PRSTATUS,
&iov);
if (rc < 0)
return 1;
/* Now, write the system call number if necessary. */
if (syscall_p)
{
callno = regs->regs[8];
iov.iov_base = &callno;
iov.iov_len = sizeof callno;
return (ptrace (PTRACE_SETREGSET, pid, NT_ARM_SYSTEM_CALL,
&iov) != 0);
}
return 0;
}
#endif /* __aarch64__ */
/* List of all processes which are being traced. */
static struct exec_tracee *tracing_processes;
/* Read N bytes from TRACEE's memory, starting at the specified user
ADDRESS. Return its contents in BUFFER. */
static void
read_memory (struct exec_tracee *tracee, char *buffer,
USER_WORD n, USER_WORD address)
{
USER_WORD word, n_words, n_bytes, i;
long rc;
/* First, read entire words from the tracee. */
n_words = n & ~(sizeof (USER_WORD) - 1);
/* Next, determine the number of bytes to read from the last
word. */
n_bytes = n & (sizeof (USER_WORD) - 1);
/* Start reading words. */
i = 0;
while (n_words)
{
rc = ptrace (PTRACE_PEEKTEXT, tracee->pid,
(void *) address + i, NULL);
word = rc;
memcpy (buffer, &word, sizeof word);
buffer += sizeof word;
i += sizeof word;
n_words -= sizeof word;
}
/* Now, read the remaining bytes. */
assert (n_bytes < sizeof (word));
if (n_bytes)
{
rc = ptrace (PTRACE_PEEKTEXT, tracee->pid,
(void *) address + i, NULL);
word = rc;
/* Copy only n_bytes to the caller. */
memcpy (buffer, &word, n_bytes);
}
}
/* Allocate N bytes of memory from TRACEE's stack. Return the address
of that memory upon success, else 0.
Place the updated user-mode registers of TRACEE in *NEW_REGS, which
should initially contain the current stack pointer of TRACEE.
REGS should contain the user mode registers of TRACEE prior to the
system call starting; it is not updated to reflect any changes. */
USER_WORD
user_alloca (struct exec_tracee *tracee, USER_REGS_STRUCT *regs,
USER_REGS_STRUCT *new_regs, USER_WORD n)
{
USER_WORD sp, old_sp;
/* Get the current stack pointer. */
old_sp = sp = new_regs->STACK_POINTER;
#if RED_ZONE_SIZE
/* Some ABI rules specify a ``red zone'' around the stack pointer
that is reserved for compiler optimizations. */
#ifdef STACK_GROWS_DOWNWARDS
if (sp == regs->STACK_POINTER)
sp -= RED_ZONE_SIZE;
#else /* !STACK_GROWS_DOWNWARDS */
if (sp == regs->STACK_POINTER)
sp += RED_ZONE_SIZE;
#endif /* STACK_GROWS_DOWNWARDS */
#endif /* RED_ZONE_SIZE */
/* Now take N off the stack. */
#ifdef STACK_GROWS_DOWNWARDS
sp = sp - n;
/* Check for overflow. */
if (sp > new_regs->STACK_POINTER)
return 0;
#else /* !STACK_GROWS_DOWNWARDS */
sp = sp + n;
/* Check for overflow. */
if (sp < new_regs->STACK_POINTER)
return 0;
#endif /* STACK_GROWS_DOWNWARDS */
/* Set the stack pointer. */
new_regs->STACK_POINTER = sp;
#ifdef __aarch64__
if (aarch64_set_regs (tracee->pid, new_regs, false))
goto fail;
#else /* !__aarch64__ */
if (ptrace (PTRACE_SETREGS, tracee->pid, NULL,
new_regs))
goto fail;
#endif /* __aarch64__ */
/* Now return the start of the new area. */
#ifdef STACK_GROWS_DOWNWARDS
return sp;
#else /* !STACK_GROWS_DOWNWARDS */
return sp - n;
#endif /* STACK_GROWS_DOWNWARDS */
fail:
/* Restore the old stack pointer. */
new_regs->STACK_POINTER = old_sp;
return 0;
}
/* Copy N bytes to ADDRESS in TRACEE's address space from BUFFER.
Value is 0 upon success, else 1. */
int
user_copy (struct exec_tracee *tracee, const unsigned char *buffer,
USER_WORD address, USER_WORD n)
{
USER_WORD start, end, word;
unsigned char *bytes;
/* Calculate the start and end positions for the write. */
start = address;
end = address + n;
/* Write from start to the last word. */
while (start < end)
{
if (start + sizeof word <= end)
{
/* Write a word by itself and increment start. */
memcpy (&word, buffer, sizeof word);
buffer += sizeof word;
if (ptrace (PTRACE_POKEDATA, tracee->pid,
(void *) start, (void *) word))
return 1;
start += sizeof word;
}
else
{
/* Only end - start bytes should be written.
Read the word at start from tracee->pid, then write
it back with changes. */
word = ptrace (PTRACE_PEEKDATA, tracee->pid,
(void *) start, NULL);
bytes = (unsigned char *) &word;
memcpy (bytes, buffer, end - start);
if (ptrace (PTRACE_POKEDATA, tracee->pid,
(void *) start, (void *) word))
return 1;
/* Writing was successful. */
return 0;
}
}
return 0;
}
/* Chain of free exec_tracee structures. */
static struct exec_tracee *free_tracees;
/* Remove the specified TRACEE from the chain of all processes being
traced. */
static void
remove_tracee (struct exec_tracee *tracee)
{
struct exec_tracee **last;
last = &tracing_processes;
while (*last)
{
if (*last == tracee)
{
*last = tracee->next;
/* Link the tracee onto the list of free tracees. */
tracee->next = free_tracees;
free_tracees = tracee;
return;
}
else
last = &(*last)->next;
}
}
/* Child process tracing. */
/* Handle the completion of a `clone' or `clone3' system call,
resulting in the creation of the process PID. Allocate a new
tracee structure from a static area for the processes's pid.
Value is 0 upon success, 1 otherwise. */
static int
handle_clone (pid_t pid)
{
static struct exec_tracee static_tracees[MAX_TRACEES];
static int tracees;
struct exec_tracee *tracee;
long rc;
int flags;
/* Now allocate a new tracee, either from static_tracees or the free
list. */
if (free_tracees)
{
tracee = free_tracees;
free_tracees = free_tracees->next;
}
else if (tracees < MAX_TRACEES)
{
tracee = &static_tracees[tracees];
tracees++;
}
else
return 1;
tracee->pid = pid;
tracee->next = tracing_processes;
tracee->waiting_for_syscall = false;
tracing_processes = tracee;
/* Apply required options to the child, so that the kernel
automatically traces children and makes it easy to differentiate
between system call traps and other kinds of traps. */
flags = PTRACE_O_TRACECLONE;
flags |= PTRACE_O_TRACEVFORK;
flags |= PTRACE_O_TRACEFORK;
flags |= PTRACE_O_TRACESYSGOOD;
flags |= PTRACE_O_TRACEEXIT;
rc = ptrace (PTRACE_SETOPTIONS, pid, 0, flags);
if (rc)
goto bail;
/* The new tracee is currently stopped. Continue it until the next
system call. */
rc = ptrace (PTRACE_SYSCALL, pid, 0, 0);
if (rc)
goto bail;
return 0;
bail:
remove_tracee (tracee);
return 1;
}
/* NOTICE: none of these functions should ever call `malloc' or
another async signal unsafe function. */
/* File name of the loader binary. */
static const char *loader_name;
/* Return whether or not the trap signal described by SIGNAL is
generated by a system call being attempted by a tracee. */
static bool
syscall_trap_p (siginfo_t *signal)
{
/* SIGTRAP delivered by the kernel means this is a system call
stop. */
return (signal->si_code == SIGTRAP
|| signal->si_code == (SIGTRAP | SI_KERNEL));
}
/* Handle an `exec' system call from the given TRACEE. REGS are the
tracee's current user-mode registers.
Rewrite the system call arguments to use the loader binary. Then,
continue the system call until the loader is loaded. Write the
information necessary to load the original executable into the
loader's stack.
Value is 0 upon success, 1 upon a generic failure before the loader
is loaded, 2 if the process has stopped, and 3 if something failed,
but it is too late to handle it.
Set errno appropriately upon returning a generic failure. */
static int
handle_exec (struct exec_tracee *tracee, USER_REGS_STRUCT *regs)
{
char buffer[PATH_MAX], *area;
USER_REGS_STRUCT original;
size_t size, loader_size;
USER_WORD loader, size1, sp;
int rc, wstatus;
siginfo_t siginfo;
/* Save the old stack pointer. */
sp = regs->STACK_POINTER;
/* Read the file name. */
read_memory (tracee, buffer, PATH_MAX,
regs->SYSCALL_ARG_REG);
/* Make sure BUFFER is NULL terminated. */
if (!memchr (buffer, '\0', PATH_MAX))
{
errno = ENAMETOOLONG;
return 1;
}
/* Copy over the registers as they originally were. */
memcpy (&original, regs, sizeof *regs);
/* Figure out what the loader needs to do. */
area = exec_0 (buffer, tracee, &size, regs);
if (!area)
return 1;
/* Rewrite the first argument to point to the loader. */
loader_size = strlen (loader_name) + 1;
loader = user_alloca (tracee, &original, regs,
loader_size);
if (!loader)
{
errno = ENOMEM;
return 1;
}
if (user_copy (tracee, (unsigned char *) loader_name,
loader, loader_size))
{
errno = EIO;
return 1;
}
regs->SYSCALL_ARG_REG = loader;
#ifdef __aarch64__
if (aarch64_set_regs (tracee->pid, regs, false))
{
errno = EIO;
return 1;
}
#else /* !__aarch64__ */
if (ptrace (PTRACE_SETREGS, tracee->pid, NULL,
regs))
{
errno = EIO;
return 1;
}
#endif /* __aarch64__ */
/* Continue the system call until loader starts. */
if (ptrace (PTRACE_SYSCALL, tracee->pid, NULL, NULL))
{
errno = EIO;
return 1;
}
again:
rc = waitpid (tracee->pid, &wstatus, __WALL);
if (rc == -1 && errno == EINTR)
goto again;
if (rc < 0)
{
errno = EIO;
return 1;
}
if (!WIFSTOPPED (wstatus))
/* The process has been killed in response to a signal.
In this case, simply return 2. */
return 2;
else
{
/* Retrieve the signal information and determine whether or not
the system call has completed. */
if (ptrace (PTRACE_GETSIGINFO, tracee->pid, 0,
&siginfo))
return 3;
if (!syscall_trap_p (&siginfo))
{
/* Continue. */
if (ptrace (PTRACE_SYSCALL, tracee->pid, 0, 0))
return 3;
goto again;
}
}
#ifdef __aarch64__
if (aarch64_get_regs (tracee->pid, &original))
return 3;
#else /* !__aarch64__ */
/* The system call has now completed. Get the registers again. */
if (ptrace (PTRACE_GETREGS, tracee->pid, NULL,
&original))
return 3;
#endif /* __aarch64__ */
*regs = original;
/* Upon failure, wait for the next system call and return
success. */
if (original.SYSCALL_RET_REG)
{
/* Restore the original stack pointer. */
regs->STACK_POINTER = sp;
#ifdef __aarch64__
aarch64_set_regs (tracee->pid, regs, false);
#else /* !__aarch64__ */
ptrace (PTRACE_SETREGS, tracee->pid, NULL, regs);
#endif /* __aarch64__ */
goto exec_failure;
}
/* Write the loader area to the stack, followed by its size and the
original stack pointer. */
loader = user_alloca (tracee, &original, regs,
size + sizeof loader * 2);
if (!loader)
return 3;
size1 = size;
#ifndef STACK_GROWS_DOWNWARDS
NOT_IMPLEMENTED;
#else /* STACK_GROWS_DOWNWARDS */
if (user_copy (tracee, (unsigned char *) area,
loader + sizeof size1 * 2, size)
|| user_copy (tracee, (unsigned char *) &size1,
loader + sizeof size1, sizeof size1))
return 3;
size1 = original.STACK_POINTER;
if (user_copy (tracee, (unsigned char *) &size1,
loader, sizeof size1))
return 3;
#endif /* STACK_GROWS_DOWNWARDS */
exec_failure:
/* Continue. */
if (ptrace (PTRACE_SYSCALL, tracee->pid, 0, 0))
return 3;
return 0;
}
/* 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. */
static void
process_system_call (struct exec_tracee *tracee)
{
USER_REGS_STRUCT regs;
int rc, wstatus;
USER_WORD callno, sp;
#ifdef __aarch64__
rc = aarch64_get_regs (tracee->pid, &regs);
#else /* !__aarch64__ */
rc = ptrace (PTRACE_GETREGS, tracee->pid, NULL,
&regs);
#endif /* __aarch64__ */
/* TODO: what to do if this fails? */
if (rc < 0)
return;
/* Save the stack pointer. */
sp = regs.STACK_POINTER;
/* Now dispatch based on the system call. */
callno = regs.SYSCALL_NUM_REG;
switch (callno)
{
case EXEC_SYSCALL:
/* exec system calls should be handled synchronously. */
assert (!tracee->waiting_for_syscall);
rc = handle_exec (tracee, &regs);
switch (rc)
{
case 3:
/* It's too late to do anything about this error,. */
break;
case 2:
/* The process has gone away. */
remove_tracee (tracee);
break;
case 1:
/* An error has occured; errno is set to the error. */
goto report_syscall_error;
}
break;
default:
/* Don't wait for the system call to finish; instead, the system
will DTRT upon the next call to PTRACE_SYSCALL after the
syscall-trap signal is delivered. */
rc = ptrace (PTRACE_SYSCALL, tracee->pid,
NULL, NULL);
if (rc < 0)
return;
tracee->waiting_for_syscall = !tracee->waiting_for_syscall;
}
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.
Make sure that the stack pointer is restored to its original
position upon exit, or bad things can happen. */
regs.SYSCALL_NUM_REG = -1;
regs.STACK_POINTER = sp;
#ifdef __aarch64__
if (aarch64_set_regs (tracee->pid, &regs, true))
return;
#else /* !__aarch64__ */
#ifdef __arm__
/* On ARM systems, a special request is used to update the system
call number as known to the kernel. In addition, the system call
number must be valid, so use `tuxcall'. Hopefully, nobody will
run this on a kernel with Tux. */
if (ptrace (PTRACE_SET_SYSCALL, tracee->pid, NULL, 222))
return;
#endif /* __arm__ */
if (ptrace (PTRACE_SETREGS, tracee->pid, NULL, &regs))
return;
#endif /* __aarch64__ */
/* Do this invalid system call. */
if (ptrace (PTRACE_SYSCALL, tracee->pid, NULL, NULL))
return;
again1:
rc = waitpid (tracee->pid, &wstatus, __WALL);
if (rc == -1 && errno == EINTR)
goto again1;
if (!WIFSTOPPED (wstatus))
/* The process has been killed in response to a signal. In this
case, simply unlink the tracee and return. */
remove_tracee (tracee);
else
{
#ifdef __mips__
/* MIPS systems place errno in v0 and set a3 to 1. */
regs.gregs[2] = errno;
regs.gregs[7] = 1;
#else /* !__mips__ */
regs.SYSCALL_RET_REG = -errno;
#endif /* __mips__ */
/* Report errno. */
#ifdef __aarch64__
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);
}
}
/* Like `execve', but asks the parent to begin tracing this thread.
Fail if tracing is unsuccessful. */
int
tracing_execve (const char *file, char *const *argv,
char *const *envp)
{
int rc;
/* Start tracing self. */
rc = ptrace (PTRACE_TRACEME, 0, NULL, NULL);
if (rc)
return rc;
/* Notify the parent to enter signal-delivery-stop. */
raise (SIGSTOP);
return execve (file, argv, envp);
}
/* Wait for PID to trace itself, and make a record of that process.
Value is 1 or 2 upon failure, 0 otherwise. Make sure that SIGCHLD
is blocked around calls to this function.
If failure occurs because PID exited, value is 2; upon any other
kind of failure, value is 1. */
int
after_fork (pid_t pid)
{
int wstatus, rc, flags;
struct exec_tracee *tracee;
/* First, wait for something to happen to PID. */
again:
rc = waitpid (pid, &wstatus, __WALL);
if (rc != pid && errno == EINTR)
goto again;
if (rc != pid)
return 1;
/* If the child exited (or in general wasn't traced), return 2. */
if (!WIFSTOPPED (wstatus))
return 2;
/* Apply required options to the child, so that the kernel
automatically traces children and makes it easy to differentiate
between system call traps and other kinds of traps. */
flags = PTRACE_O_TRACECLONE;
flags |= PTRACE_O_TRACEVFORK;
flags |= PTRACE_O_TRACEFORK;
flags |= PTRACE_O_TRACESYSGOOD;
flags |= PTRACE_O_TRACEEXIT;
rc = ptrace (PTRACE_SETOPTIONS, pid, 0, flags);
if (rc)
{
/* If the kernel can't trace child processes upon creation and
exit, then it can't work reliably. */
ptrace (PTRACE_DETACH, pid, 0, 0);
return 1;
}
/* Request that the child stop upon the next system call. */
rc = ptrace (PTRACE_SYSCALL, pid, 0, 0);
if (rc)
return 1;
/* Enter the child in `tracing_processes'. */
if (free_tracees)
{
tracee = free_tracees;
free_tracees = free_tracees->next;
}
else
tracee = malloc (sizeof *tracee);
if (!tracee)
return 1;
tracee->pid = pid;
tracee->next = tracing_processes;
tracee->waiting_for_syscall = false;
tracing_processes = tracee;
return 0;
}
/* Return the `struct exec_tracee' corresponding to the specified
PROCESS. */
static struct exec_tracee *
find_tracee (pid_t process)
{
struct exec_tracee *tracee;
for (tracee = tracing_processes; tracee; tracee = tracee->next)
{
if (tracee->pid == process)
return tracee;
}
return NULL;
}
/* Wait for a child process to exit, like `waitpid'. However, if a
child stops to perform a system call, send it on its way and return
-1. OPTIONS must not contain WUNTRACED. */
pid_t
exec_waitpid (pid_t pid, int *wstatus, int options)
{
int status;
struct exec_tracee *tracee;
siginfo_t siginfo;
pid = waitpid (pid, &status, options | __WALL);
if (pid < 0)
return pid;
/* Copy status into *WSTATUS if specified. */
if (wstatus)
*wstatus = status;
/* WIFSTOPPED (status) means that the process has been stopped in
response to a system call. Find its tracee and process the
system call. */
if (WIFSTOPPED (status))
{
tracee = find_tracee (pid);
if (!tracee)
{
if (WSTOPSIG (status) == SIGSTOP)
/* A new process has been created and stopped. Record
it now. */
handle_clone (pid);
return -1;
}
/* Now extract the stop signal, including ptrace event bits. */
status &= 0xfff00;
status = status >> 8;
switch (status)
{
case SIGTRAP:
/* Now, use PTRACE_GETSIGINFO to determine whether or not the
signal was delivered in response to a system call. */
if (ptrace (PTRACE_GETSIGINFO, pid, 0, &siginfo))
return -1;
if (!syscall_trap_p (&siginfo))
{
if (siginfo.si_code < 0)
/* SIGTRAP delivered from userspace. Pass it on. */
ptrace (PTRACE_SYSCALL, pid, 0, SIGTRAP);
else
ptrace (PTRACE_SYSCALL, pid, 0, 0);
return -1;
}
case SIGTRAP | 0x80: /* SIGTRAP | 0x80 specifically refers to
system call traps. */
/* Otherwise, process the system call and continue waiting. */
process_system_call (tracee);
return -1;
case SIGTRAP | (PTRACE_EVENT_EXIT << 8):
/* The tracee has exited. Make it finish correctly. */
ptrace (PTRACE_SYSCALL, pid, 0, 0);
remove_tracee (tracee);
return -1;
case SIGTRAP | (PTRACE_EVENT_FORK << 8):
case SIGTRAP | (PTRACE_EVENT_VFORK << 8):
case SIGTRAP | (PTRACE_EVENT_CLONE << 8):
/* These events are handled by tracing SIGSTOP signals sent
to unknown tracees. Make sure not to pass through
status, as there's no signal really being delivered. */
ptrace (PTRACE_SYSCALL, pid, 0, 0);
return -1;
default:
/* Continue the process until the next syscall. */
ptrace (PTRACE_SYSCALL, pid, 0, status);
return -1;
}
}
else
{
/* The process has exited. Unlink the associated tracee. */
tracee = find_tracee (pid);
if (tracee)
remove_tracee (tracee);
return pid;
}
}
/* Initialize the exec library. LOADER should be the file name of the
loader binary; it is not copied. */
void
exec_init (const char *loader)
{
loader_name = loader;
}

View File

@ -124,7 +124,8 @@ CROSS_LIBSRC_BINS := $(top_builddir)/cross/lib-src/ctags \
$(top_builddir)/cross/lib-src/emacsclient \
$(top_builddir)/cross/lib-src/etags
CROSS_LIBSRC_BINS_MOVEMAIL := $(top_builddir)/cross/lib-src/movemail
CROSS_BINS = $(CROSS_SRC_BINS) $(CROSS_LIBSRC_BINS)
CROSS_EXEC_BINS := $(top_builddir)/exec/exec1 $(top_builddir)/exec/loader
CROSS_BINS = $(CROSS_SRC_BINS) $(CROSS_LIBSRC_BINS) $(CROSS_EXEC_BINS)
ifneq ($(emacs_use_mailutils),yes)
CROSS_LIBSRC_BINS := $(CROSS_LIBSRC_BINS) $(CROSS_LIBSRC_BINS_MOVEMAIL)
@ -159,6 +160,12 @@ $(CROSS_LIBSRC_BINS) &: $(CROSS_ARCHIVES)
$(CROSS_ARCHIVES):
$(MAKE) -C $(top_builddir)/cross lib/libgnu.a
# These two binaries are helpers used to execute binaries on Android
# 10 and later.
$(CROSS_EXEC_BINS) &:
$(MAKE) -C $(top_builddir)/exec $(notdir $(CROSS_EXEC_BINS))
# This is needed to generate the ``.directory-tree'' file used by the
# Android emulations of readdir and faccessat.