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.
This commit is contained in:
parent
4289ed6cff
commit
368f6f3942
29 changed files with 15973 additions and 2 deletions
11
.gitignore
vendored
11
.gitignore
vendored
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
28
configure.ac
28
configure.ac
|
@ -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
139
exec/Makefile.in
Normal 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
3
exec/README
Normal 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
36
exec/config-mips.m4.in
Normal 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
1768
exec/config.guess
vendored
Executable file
File diff suppressed because it is too large
Load diff
202
exec/config.h.in
Normal file
202
exec/config.h.in
Normal 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
1890
exec/config.sub
vendored
Executable file
File diff suppressed because it is too large
Load diff
6940
exec/configure
vendored
Executable file
6940
exec/configure
vendored
Executable file
File diff suppressed because it is too large
Load diff
418
exec/configure.ac
Normal file
418
exec/configure.ac
Normal 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
21
exec/deps.mk
Normal 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
1016
exec/exec.c
Normal file
File diff suppressed because it is too large
Load diff
192
exec/exec.h
Normal file
192
exec/exec.h
Normal 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
88
exec/exec1.c
Normal 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
541
exec/install-sh
Executable 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
174
exec/loader-aarch64.s
Normal 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
192
exec/loader-armeabi.s
Normal 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
214
exec/loader-mips64el.s
Normal 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
221
exec/loader-mipsel.s
Normal 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
188
exec/loader-x86.s
Normal 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
180
exec/loader-x86_64.s
Normal 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
44
exec/mipsel-user.h
Normal 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
289
exec/mipsfpu.c
Normal 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
82
exec/mipsfpu.h
Normal 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
105
exec/test.c
Normal 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
972
exec/trace.c
Normal 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, ®s);
|
||||
#else /* !__aarch64__ */
|
||||
rc = ptrace (PTRACE_GETREGS, tracee->pid, NULL,
|
||||
®s);
|
||||
#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, ®s);
|
||||
|
||||
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, ®s, 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, ®s))
|
||||
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, ®s, false);
|
||||
#else /* !__aarch64__ */
|
||||
ptrace (PTRACE_SETREGS, tracee->pid, NULL, ®s);
|
||||
#endif /* __aarch64__ */
|
||||
|
||||
/* Now wait for the next system call to happen. */
|
||||
ptrace (PTRACE_SYSCALL, tracee->pid, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* 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;
|
||||
}
|
|
@ -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.
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue