From 368f6f3942a1f8b9483763a6ac24b3b3021e92bf Mon Sep 17 00:00:00 2001 From: Po Lu Date: Sun, 30 Apr 2023 21:37:19 +0800 Subject: 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. --- exec/exec.c | 1016 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1016 insertions(+) create mode 100644 exec/exec.c (limited to 'exec/exec.c') diff --git a/exec/exec.c b/exec/exec.c new file mode 100644 index 00000000000..e890179a9ab --- /dev/null +++ b/exec/exec.c @@ -0,0 +1,1016 @@ +/* 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 . */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifndef MIN +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif /* MIN */ + +#ifndef MAX +#define MAX(a, b) ((a) < (b) ? (b) : (a)) +#endif /* MAX */ + +#include "exec.h" + +#if defined __mips__ && !defined MIPS_NABI +#include "mipsfpu.h" +#endif /* defined __mips__ && !defined MIPS_NABI */ + + + +/* Executable reading functions. + These functions extract information from an executable that is + about to be loaded. + + `exec_0' takes the name of the program, determines whether or not + its format is correct, and if so, returns the list of actions that + the loader should perform. + + The actions include: + + - Making the stack executable, if PT_GNU_STACK. + - Mapping PT_LOAD sections into the executable with the correct + memory protection. + - On MIPS, setting the floating point register size. + - Transferring control to the interpreter or executable. */ + + +/* Check whether or not FD starts with a #!, and return the executable + to load if it does. Value is NAME if no interpreter character was + found, or the interpreter otherwise. Value is NULL upon an IO + error. + + If an additional command line argument is specified, place it in + *EXTRA. */ + +static const char * +check_interpreter (const char *name, int fd, const char **extra) +{ + static char buffer[PATH_MAX], *start; + char first[2], *end, *ws; + ssize_t rc; + + /* Read the first character. */ + rc = read (fd, &first, 2); + + if (rc != 2) + goto fail; + + if (first[0] != '#' || first[1] != '!') + goto nomatch; + + rc = read (fd, buffer, PATH_MAX); + + if (rc < 0) + goto fail; + + /* Strip leading whitespace. */ + start = buffer; + while (*start && *start < 128 && isspace (*start)) + ++start; + + /* Look for a newline character. */ + end = memchr (start, '\n', rc); + + if (!end) + goto fail; + + /* The string containing the interpreter is now in start. NULL + terminate it. */ + *end = '\0'; + + /* Now look for any whitespace characters. */ + ws = strchr (start, ' '); + + /* If there's no whitespace, return the entire start. */ + + if (!ws) + { + if (lseek (fd, 0, SEEK_SET)) + goto fail; + + return start; + } + + /* Otherwise, split the string at the whitespace and return the + additional argument. */ + *ws = '\0'; + + if (lseek (fd, 0, SEEK_SET)) + goto fail; + + *extra = ws + 1; + return start; + + nomatch: + /* There's no interpreter. */ + if (lseek (fd, 0, SEEK_SET)) + goto fail; + + return name; + + fail: + errno = ENOEXEC; + return NULL; +} + +/* Static area used to store data placed on the loader's stack. */ +static char loader_area[65536]; + +/* Number of bytes used in that area. */ +static int loader_area_used; + + + +/* Structure definitions for commands placed in the loader area. + Arrange these so that each member is naturally aligned. */ + +struct exec_open_command +{ + /* Word identifying the type of this command. */ + USER_WORD command; + + /* NULL-terminated file name follows, padded to the size of a user + word. */ +}; + +struct exec_map_command +{ + /* Word identifying the type of this command. */ + USER_WORD command; + + /* Where the file will be mapped. */ + USER_WORD vm_address; + + /* Offset into the file to map from. */ + USER_WORD file_offset; + + /* Memory protection for mprotect. */ + USER_WORD protection; + + /* Number of bytes to be mapped. */ + USER_WORD length; + + /* Flags for mmap. */ + USER_WORD flags; + + /* Number of bytes to clear at the end of this mapping. */ + USER_WORD clear; +}; + +struct exec_jump_command +{ + /* Word identifying the type of this command. */ + USER_WORD command; + + /* Address to jump to. */ + USER_WORD entry; + + /* The value of AT_ENTRY inside the aux vector. */ + USER_WORD at_entry; + + /* The value of AT_PHENT inside the aux vector. */ + USER_WORD at_phent; + + /* The value of AT_PHNUM inside the aux vector. */ + USER_WORD at_phnum; + + /* The value of AT_PHDR inside the aux vector. */ + USER_WORD at_phdr; + + /* The value of AT_BASE inside the aux vector. */ + USER_WORD at_base; + +#if defined __mips__ && !defined MIPS_NABI + /* The FPU mode to apply. */ + USER_WORD fpu_mode; +#endif /* defined __mips__ && !defined MIPS_NABI */ +}; + + + +/* Write a command to open the file NAME to the loader area. + If ALTERNATE is true, then use the command code 16 instead + of 0. Value is 1 upon failure, else 0. */ + +static int +write_open_command (const char *name, bool alternate) +{ + struct exec_open_command command; + size_t size; + + /* First, write the command to open NAME. This is followed by NAME + itself, padded to sizeof (USER_WORD) bytes. */ + + command.command = alternate ? 16 : 0; + if (sizeof loader_area - loader_area_used < sizeof command) + return 1; + memcpy (loader_area + loader_area_used, &command, sizeof command); + loader_area_used += sizeof command; + + /* Calculate the length of NAME. */ + size = strlen (name) + 1; + + /* Round it up. */ + size = ((size + (sizeof (USER_WORD) - 1)) + & ~(sizeof (USER_WORD) - 1)); + + if (sizeof loader_area - loader_area_used < size) + return 1; + + /* Now copy name to the loader area, filling the padding with NULL + bytes. */ + strncpy (loader_area + loader_area_used, name, size); + + /* Increase loader_area_used. */ + loader_area_used += size; + return 0; +} + +/* Write the commands necessary to map the executable file into memory + for the given PT_LOAD program HEADER. Value is 1 upon failure, + else 0. If USE_ALTERNATE, use the command code 17 instead of + 1. + + Apply the given OFFSET to virtual addresses that will be mapped. */ + +static int +write_load_command (program_header *header, bool use_alternate, + USER_WORD offset) +{ + struct exec_map_command command; + struct exec_map_command command1; + USER_WORD start, end; + bool need_command1; + static long pagesize; + + /* First, write the commands necessary to map the specified segment + itself. + + This is the area between header->p_vaddr and header->p_filesz, + rounded up to the page size. */ + +#ifndef PAGE_MASK + /* This system doesn't define a fixed page size. */ + +#ifdef HAVE_GETPAGESIZE + if (!pagesize) + pagesize = getpagesize (); +#else /* HAVE_GETPAGESIZE */ + if (!pagesize) + pagesize = sysconf (_SC_PAGESIZE); + +#define PAGE_MASK (~(pagesize - 1)) +#define PAGE_SIZE (pagesize) +#endif /* HAVE_GETPAGESIZE */ +#endif /* PAGE_MASK */ + + start = header->p_vaddr & PAGE_MASK; + end = ((header->p_vaddr + header->p_filesz + + PAGE_SIZE) + & PAGE_MASK); + + command.command = use_alternate ? 17 : 1; + command.vm_address = start; + command.file_offset = header->p_offset & PAGE_MASK; + command.protection = 0; + command.length = end - start; + command.clear = 0; + command.flags = MAP_PRIVATE | MAP_FIXED; + + /* Apply the memory protection specified in the header. */ + + if (header->p_flags & 4) /* PF_R */ + command.protection |= PROT_READ; + + if (header->p_flags & 2) /* PF_W */ + command.protection |= PROT_WRITE; + + if (header->p_flags & 1) /* PF_X */ + command.protection |= PROT_EXEC; + + /* Next, write any command necessary to map pages in the area + between p_filesz and p_memsz. */ + need_command1 = false; + + if (header->p_memsz > header->p_filesz) + { + /* If there are bytes after end which need to be initialized, do + that now. */ + command.clear = end - header->p_vaddr - header->p_filesz; + start = end; + end = header->p_vaddr + header->p_memsz + PAGE_SIZE; + end &= PAGE_MASK; + + if (end > start) + { + command1.command = 4; + command1.vm_address = start; + command1.file_offset = 0; + command1.length = end - start; + command1.clear = 0; + command1.protection = command.protection; + command1.flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED; + need_command1 = true; + } + } + + /* Apply the offset to both commands if necessary. */ + + if (offset) + { + if (need_command1) + command1.vm_address += offset; + + command.vm_address += offset; + } + + /* Write both commands. */ + + if (sizeof loader_area - loader_area_used < sizeof command) + return 1; + + memcpy (loader_area + loader_area_used, &command, + sizeof command); + loader_area_used += sizeof command; + + if (!need_command1) + return 0; + + if (sizeof loader_area - loader_area_used < sizeof command1) + return 1; + + memcpy (loader_area + loader_area_used, &command1, + sizeof command1); + loader_area_used += sizeof command1; + + return 0; +} + +#if defined __mips__ && !defined MIPS_NABI + +/* Static storage used for MIPS ABI flags. */ +static struct mips_elf_abi_flags exec_abi, interpreter_abi; + +/* Static storage for interpreter headers. */ +static elf_header exec_interpreter_header; + +/* Pointer to the ELF header of this executable's interpreter. */ +static elf_header *interpreter_header; + +/* Pointer to any PT_MIPS_ABIFLAGS program header found in the + executable itself. */ +static struct mips_elf_abi_flags *exec_abiflags; + +/* Pointer to any PT_MIPS_ABIFLAGS program header found in the + executable's ELF interpreter. */ +static struct mips_elf_abi_flags *interpreter_abiflags; + +#endif /* defined __mips__ && !defined MIPS_NABI */ + +/* Process the specified program HEADER; HEADER is from the ELF + interpreter of another executable. FD is the executable file from + which it is being read, NAME is its file name, and ELF_HEADER is + its header. + + If ELF_HEADER->e_type is ET_DYN, add the base address for position + independent interpreter code to virtual addresses. + + Value is 1 upon failure, else 0. */ + +static int +process_interpreter_1 (const char *name, int fd, + program_header *header, + elf_header *elf_header) +{ + int rc; +#if defined __mips__ && !defined MIPS_NABI + ssize_t rc1; +#endif /* defined __mips__ && !defined MIPS_NABI */ + + switch (header->p_type) + { + default: /* PT_NULL, PT_NOTE, PT_DYNAMIC, PT_INTERP, et cetera */ + rc = 0; + break; + + case 1: /* PT_LOAD */ + /* This describes a segment in the file that must be loaded. + Write the appropriate load command. */ + + if (elf_header->e_type == 3) /* ET_DYN */ + rc = write_load_command (header, true, + INTERPRETER_BASE); + else + rc = write_load_command (header, true, 0); + + break; + +#if defined __mips__ && !defined MIPS_NABI + case 0x70000003: /* PT_MIPS_ABIFLAGS */ + /* Record this header for later use. */ + rc1 = pread (fd, &interpreter_abi, sizeof interpreter_abi, + header->p_offset); + + if (rc1 != sizeof interpreter_abi) + return 1; + + interpreter_abiflags = &interpreter_abi; + rc = 0; +#endif /* defined __mips__ && !defined MIPS_NABI */ + } + + return rc; +} + +/* Read the ELF interpreter specified in the given program header from + FD, and append the commands necessary to load it to the load area. + Then, return the interpreter entry point in *ENTRY. + + Value is 1 upon failure, else 0. */ + +static int +process_interpreter (int fd, program_header *prog_header, + USER_WORD *entry) +{ + char buffer[PATH_MAX + 1]; + int rc, size, i; + elf_header header; + program_header program; + + /* Read the interpreter name. */ + size = MIN (prog_header->p_filesz, PATH_MAX); + rc = pread (fd, buffer, size, prog_header->p_offset); + if (rc < size) + return 1; + + /* Make sure the name is NULL terminated. */ + buffer[size] = '\0'; + + /* Check if the file is executable. This is unfortunately not + atomic. */ + + if (access (buffer, X_OK)) + return 1; + + /* Read the interpreter's header much like exec_0. + + However, use special command codes in `process_program_header' if + it is position independent. That way, the loader knows it should + use the open interpreter instead. */ + + fd = open (buffer, O_RDONLY); + + if (fd < 0) + return 1; + + rc = read (fd, &header, sizeof header); + + if (rc < sizeof header) + goto fail; + +#if defined __mips__ && !defined MIPS_NABI + /* Record this interpreter's header for later use determining the + floating point ABI. */ + exec_interpreter_header = header; + interpreter_header = &exec_interpreter_header; +#endif /* defined __mips__ && !defined MIPS_NABI */ + + /* Verify that this is indeed an ELF file. */ + + if (header.e_ident[0] != 0x7f + || header.e_ident[1] != 'E' + || header.e_ident[2] != 'L' + || header.e_ident[3] != 'F') + goto fail; + + /* Now check that the class is correct. */ +#ifdef EXEC_64 + if (header.e_ident[4] != 2) + goto fail; +#else /* !EXEC_64 */ + if (header.e_ident[4] != 1) + goto fail; +#endif /* EXEC_64 */ + + /* And the endianness. */ +#ifndef WORDS_BIGENDIAN + if (header.e_ident[5] != 1) + goto fail; +#else /* WORDS_BIGENDIAN */ + if (header.e_ident[5] != 2) + goto fail; +#endif /* EXEC_64 */ + + /* Check that this is an executable. */ + if (header.e_type != 2 && header.e_type != 3) + goto fail; + + /* Now check that the ELF program header makes sense. */ + if (header.e_phnum > 0xffff + || (header.e_phentsize + != sizeof (program_header))) + goto fail; + + if (write_open_command (buffer, true)) + goto fail; + + for (i = 0; i < header.e_phnum; ++i) + { + rc = read (fd, &program, sizeof program); + if (rc < sizeof program) + goto fail; + + if (process_interpreter_1 (buffer, fd, &program, + &header)) + goto fail; + } + + if (header.e_type == 3) /* ET_DYN */ + *entry = header.e_entry + INTERPRETER_BASE; + else + *entry = header.e_entry; + + close (fd); + return 0; + + fail: + close (fd); + return 1; +} + +/* Process the specified program HEADER. FD is the executable file + from which it is being read, NAME is its file name, and ELF_HEADER + is its header. + + If ELF_HEADER->e_type is ET_DYN, add the base address for position + independent code to virtual addresses. + + If OFFSET is non-NULL, and *OFFSET is -1, write the virtual address + of HEADER if it describes a PT_LOAD segment. + + If an interpreter is found, set *ENTRY to its entry point. + + Value is 1 upon failure, else 0. */ + +static int +process_program_header (const char *name, int fd, + program_header *header, + elf_header *elf_header, + USER_WORD *entry, + USER_WORD *offset) +{ + int rc; +#if defined __mips__ && !defined MIPS_NABI + ssize_t rc1; +#endif /* defined __mips__ && !defined MIPS_NABI */ + + switch (header->p_type) + { + default: /* PT_NULL, PT_NOTE, PT_DYNAMIC, et cetera */ + rc = 0; + break; + + case 1: /* PT_LOAD */ + /* This describes a segment in the file that must be loaded. + Write the appropriate load command. */ + + if (elf_header->e_type == 3) /* ET_DYN */ + { + rc = write_load_command (header, false, + EXECUTABLE_BASE); + + if (!rc && offset && *offset == (USER_WORD) -1) + *offset = EXECUTABLE_BASE + header->p_vaddr; + } + else + { + rc = write_load_command (header, false, 0); + + if (!rc && offset && *offset == (USER_WORD) -1) + *offset = header->p_vaddr; + } + + break; + + case 3: /* PT_INTERP */ + /* This describes another executable that must be loaded. + Open the interpreter and process each of its headers + as well. */ + rc = process_interpreter (fd, header, entry); + break; + + case 1685382481: /* PT_GNU_STACK */ + /* TODO */ + rc = 0; + break; + +#if defined __mips__ && !defined MIPS_NABI + case 0x70000003: /* PT_MIPS_ABIFLAGS */ + /* Record this header for later use. */ + rc1 = pread (fd, &exec_abi, sizeof exec_abi, + header->p_offset); + + if (rc1 != sizeof exec_abi) + return 1; + + exec_abiflags = &exec_abi; + rc = 0; +#endif /* defined __mips__ && !defined MIPS_NABI */ + } + + return rc; +} + +/* Prepend one or two extra arguments ARG1 and ARG2 to a pending + execve system call. TRACEE is the tracee performing the system + call, and REGS are its current user registers. Value is 1 upon + failure, else 0. */ + +static int +insert_args (struct exec_tracee *tracee, USER_REGS_STRUCT *regs, + const char *arg1, const char *arg2) +{ + USER_WORD argv, argc, word, new; + USER_WORD new1, new2; + size_t text_size, effective_size; + USER_REGS_STRUCT original; + + /* First, get a pointer to the current argument vector. */ + argv = regs->SYSCALL_ARG1_REG; + + /* Now figure out how many arguments there are. */ + argc = 0; + while (true) + { + /* Clear errno. PTRACE_PEEKDATA returns the word read the same + way failure indications are returned, so the only way to + catch IO errors is by clearing errno before the call to + ptrace and checking it afterwards. */ + + errno = 0; + word = ptrace (PTRACE_PEEKDATA, tracee->pid, + (void *) argv, NULL); + argv += sizeof (USER_WORD); + + if (errno) + return 1; + + if (!word) + break; + + ++argc; + }; + + /* Allocate enough to hold that many arguments, alongside the argc + text. */ + + text_size = (strlen (arg1) + 1 + + (arg2 ? strlen (arg2) + 1 : 0)); + + /* Round it up to the user word size. */ + text_size += sizeof (USER_WORD) - 1; + text_size &= ~(sizeof (USER_WORD) - 1); + + /* Now allocate the new argv. */ + + effective_size = sizeof word * (argc + 2) + text_size; + + if (arg2) + effective_size += sizeof word; + + /* Copy regs to original so that user_alloca knows it should append + the ABI red zone. */ + + memcpy (&original, regs, sizeof *regs); + new = user_alloca (tracee, &original, regs, + effective_size); + + if (!new) + goto fail; + + /* Figure out where argv starts. */ + + new2 = new + text_size; + + /* Now write the two strings. */ + + new1 = new + strlen (arg1) + 1; + if (user_copy (tracee, (const unsigned char *) arg1, + new, new1 - new)) + goto fail; + + if (arg2 && user_copy (tracee, (const unsigned char *) arg2, + new1, new2 - new1)) + goto fail; + + /* Start copying argv back to new2. First, write the one or two new + arguments. */ + + if (ptrace (PTRACE_POKETEXT, tracee->pid, + (void *) new2, (void *) new)) + goto fail; + + new2 += sizeof new2; + + if (arg2 && ptrace (PTRACE_POKETEXT, tracee->pid, + (void *) new2, (void *) new1)) + goto fail; + else if (arg2) + new2 += sizeof new2; + + /* Copy the remaining arguments back. */ + + argv = regs->SYSCALL_ARG1_REG; + + /* Make sure the trailing NULL is included. */ + argc += 1; + + while (argc) + { + /* Read one argument. */ + word = ptrace (PTRACE_PEEKDATA, tracee->pid, + (void *) argv, NULL); + argv += sizeof argv; + argc--; + + /* Write one argument, then increment new2. */ + + if (ptrace (PTRACE_POKETEXT, tracee->pid, + (void *) new2, (void *) word)) + goto fail; + + new2 += sizeof new2; + } + + /* Assert that new2 is not out of bounds. */ + assert (new2 == new + effective_size); + + /* And that it is properly aligned. */ + assert (!(new2 & (sizeof new2 - 2))); + + /* Now modify the system call argument to point to new + + text_size. */ + + regs->SYSCALL_ARG1_REG = new + text_size; + +#ifdef __aarch64__ + if (aarch64_set_regs (tracee->pid, regs, false)) + goto fail; +#else /* !__aarch64__ */ + if (ptrace (PTRACE_SETREGS, tracee->pid, NULL, regs)) + goto fail; +#endif /* __aarch64__ */ + + /* Success. */ + + return 0; + + fail: + /* Restore the original stack pointer. */ +#ifdef __aarch64__ + aarch64_set_regs (tracee->pid, &original, false); +#else /* !__aarch64__ */ + ptrace (PTRACE_SETREGS, tracee->pid, NULL, &original); +#endif /* __aarch64__ */ + errno = ENOMEM; + return 1; +} + + + +/* Return a sequence of actions required to load the executable under + the file NAME for the given TRACEE. First, see if the file starts + with #!; in that case, find the program to open and use that + instead. + + Next, read the executable header, and add the necessary memory + mappings for each file. Finally, return the action data and its + size in *SIZE. + + Finally, use REGS to add the required interpreter arguments to the + caller's argv. + + Value is NULL upon failure, with errno set accordingly. */ + +char * +exec_0 (const char *name, struct exec_tracee *tracee, + size_t *size, USER_REGS_STRUCT *regs) +{ + int fd, rc, i; + elf_header header; + const char *interpreter_name, *extra; + program_header program; + USER_WORD entry, program_entry, offset; + USER_WORD header_offset; + struct exec_jump_command jump; +#if defined __mips__ && !defined MIPS_NABI + int fpu_mode; +#endif /* defined __mips__ && !defined MIPS_NABI */ + + fd = open (name, O_RDONLY); + if (fd < 0) + return NULL; + + /* Now read the header. */ + + extra = NULL; + interpreter_name = check_interpreter (name, fd, &extra); + if (!interpreter_name) + goto fail; + + /* Open the interpreter instead, if necessary. */ + if (interpreter_name != name) + { + close (fd); + fd = open (interpreter_name, O_RDONLY); + if (fd < 0) + return NULL; + + /* Now, rewrite the argument list to include `interpreter_name' + and perhaps `extra'. */ + + if (insert_args (tracee, regs, interpreter_name, + extra)) + goto fail1; + } + + rc = read (fd, &header, sizeof header); + + if (rc < sizeof header) + goto fail1; + + /* Verify that this is indeed an ELF file. */ + + if (header.e_ident[0] != 0x7f + || header.e_ident[1] != 'E' + || header.e_ident[2] != 'L' + || header.e_ident[3] != 'F') + goto fail1; + + /* Now check that the class is correct. */ +#ifdef EXEC_64 + if (header.e_ident[4] != 2) + goto fail1; +#else /* !EXEC_64 */ + if (header.e_ident[4] != 1) + goto fail1; +#endif /* EXEC_64 */ + + /* And the endianness. */ +#ifndef WORDS_BIGENDIAN + if (header.e_ident[5] != 1) + goto fail1; +#else /* WORDS_BIGENDIAN */ + if (header.e_ident[5] != 2) + goto fail1; +#endif /* EXEC_64 */ + + /* Check that this is an executable. */ + if (header.e_type != 2 && header.e_type != 3) + goto fail1; + + /* Now check that the ELF program header makes sense. */ + if (header.e_phnum > 0xffff + || (header.e_phentsize + != sizeof (program_header))) + goto fail1; + + /* Seek to the first program header and read each one. */ + rc = lseek (fd, header.e_phoff, SEEK_SET); + if (rc < 0) + goto fail1; + loader_area_used = 0; + + /* Write the command used to open the executable. */ + if (write_open_command (interpreter_name, false)) + goto fail1; + + /* Apply base addresses for PIC code. */ + + if (header.e_type == 3) /* ET_DYN */ + offset = EXECUTABLE_BASE; + else + offset = 0; + + /* entry and program_entry are initially the same, but entry may be + set to that of the interpreter if one is present. */ + + entry = header.e_entry + offset; + program_entry = header.e_entry; + +#if defined __mips__ && !defined MIPS_NABI + /* Clear MIPS ABI flags. */ + exec_abiflags = NULL; + interpreter_abiflags = NULL; + interpreter_header = NULL; +#endif /* defined __mips__ && !defined MIPS_NABI */ + + /* Set header_offset to -1; `process_program_header' then updates it + to that of the first mapping. */ + header_offset = -1; + + for (i = 0; i < header.e_phnum; ++i) + { + rc = read (fd, &program, sizeof program); + if (rc < sizeof program) + goto fail1; + + if (process_program_header (interpreter_name, fd, + &program, &header, + &entry, &header_offset)) + goto fail1; + } + + /* Write the entry point and program entry. */ + + jump.command = 3; + jump.entry = entry; + + /* Now calculate values for the aux vector. */ + + jump.at_entry = program_entry + offset; + jump.at_phent = header.e_phentsize; + jump.at_phnum = header.e_phnum; + jump.at_base = (entry == header.e_entry + offset + ? EXECUTABLE_BASE + : INTERPRETER_BASE); + +#if defined __mips__ && !defined MIPS_NABI + /* Finally, calculate the FPU mode wanted by the executable. */ + + if (determine_fpu_mode (&header, interpreter_header, + &fpu_mode, exec_abiflags, + interpreter_abiflags)) + /* N.B. that `determine_fpu_mode' sets errno. */ + goto fail; + + /* If the processor is too new to support FR0 operation, place the + executable in floating point emulation mode. */ + + if (fpu_mode == FP_FR0 && !cpu_supports_fr0_p ()) + fpu_mode = FP_FRE; + + jump.fpu_mode = fpu_mode; +#endif /* defined __mips__ && !defined MIPS_NABI */ + + /* The offset used for at_phdr should be that of the first + mapping. */ + + if (header_offset == (USER_WORD) -1) + header_offset = 0; + + jump.at_phdr = header.e_phoff + header_offset; + + if (sizeof loader_area - loader_area_used < sizeof jump) + goto fail1; + + memcpy (loader_area + loader_area_used, &jump, + sizeof jump); + loader_area_used += sizeof jump; + + /* Close the file descriptor and return the number of bytes + used. */ + + close (fd); + *size = loader_area_used; + + /* Make sure the loader area is properly aligned. */ + assert (!(loader_area_used & (sizeof (USER_WORD) - 1))); + return loader_area; + + fail1: + errno = ENOEXEC; + fail: + close (fd); + return NULL; +} -- cgit v1.2.1 From ddc16de86964d445309dd38175a85221c14f05ab Mon Sep 17 00:00:00 2001 From: Po Lu Date: Mon, 1 May 2023 11:28:22 +0800 Subject: Update Android port * Makefile.in (extraclean): Clean in exec as well. * configure.ac: Fix detection of absolute srcdir. Also, pass CFLAGS. * exec/Makefile.in: (.c.o): Add -I. so config.h can be found.:(.s.o): Don't create m4 temporary in srcdir. * exec/config-mips.m4.in (DADDI2, DADDI3): New macros. Define to substitute if as cannot assemble daddi. * exec/configure.ac (user_h): Look for user.h in asm/ as well. Use new user.h. Also look in ptrace.h on arm systems. Check if as supports daddi on mips64. * exec/exec.c (check_interpreter): Fix char signedness bug. * exec/loader-mips64el.s (__start): Use DADDI2 and DADDI3 for two- and 3-operand daddi. * exec/mipsel-user.h: Don't include sgidefs.h. * java/INSTALL: Document that m4 is now required. * src/android.c (android_rewrite_spawn_argv): Add missing NULL. --- exec/exec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'exec/exec.c') diff --git a/exec/exec.c b/exec/exec.c index e890179a9ab..662c8bf69d2 100644 --- a/exec/exec.c +++ b/exec/exec.c @@ -95,7 +95,7 @@ check_interpreter (const char *name, int fd, const char **extra) /* Strip leading whitespace. */ start = buffer; - while (*start && *start < 128 && isspace (*start)) + while (*start && ((unsigned char) *start) < 128 && isspace (*start)) ++start; /* Look for a newline character. */ -- cgit v1.2.1 From b9de6e35b79cbc10909a856df6b1caa770bd4ac4 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Mon, 1 May 2023 21:23:12 +0800 Subject: Fix cwd relative process execution on Android * exec/exec.c (format_pid): New function. (exec_0): Make cwd relative file names relative to /proc/pid/cwd. * exec/trace.c (handle_exec): Handle EINTR. (process_system_call): Report failure without clobbering x0. --- exec/exec.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) (limited to 'exec/exec.c') diff --git a/exec/exec.c b/exec/exec.c index 662c8bf69d2..c7a73f221f5 100644 --- a/exec/exec.c +++ b/exec/exec.c @@ -26,6 +26,7 @@ along with GNU Emacs. If not, see . */ #include #include #include +#include #include #include @@ -808,6 +809,35 @@ insert_args (struct exec_tracee *tracee, USER_REGS_STRUCT *regs, +/* Format PID, an unsigned process identifier, in base 10. Place the + result in *IN, and return a pointer to the byte after the + result. REM should be NULL. */ + +static char * +format_pid (char *in, unsigned int pid) +{ + unsigned int digits[32], *fill; + + fill = digits; + + for (; pid != 0; pid = pid / 10) + *fill++ = pid % 10; + + /* Insert 0 if the number would otherwise be empty. */ + + if (fill == digits) + *fill++ = 0; + + while (fill != digits) + { + --fill; + *in++ = '0' + *fill; + } + + *in = '\0'; + return in; +} + /* Return a sequence of actions required to load the executable under the file NAME for the given TRACEE. First, see if the file starts with #!; in that case, find the program to open and use that @@ -836,6 +866,29 @@ exec_0 (const char *name, struct exec_tracee *tracee, #if defined __mips__ && !defined MIPS_NABI int fpu_mode; #endif /* defined __mips__ && !defined MIPS_NABI */ + char buffer[PATH_MAX + 80], *rewrite; + size_t remaining; + + /* If name is not absolute, then make it relative to TRACEE's + cwd. Use stpcpy, as sprintf is not reentrant. */ + + if (name[0] && name[0] != '/') + { + /* Clear `buffer'. */ + memset (buffer, 0, sizeof buffer); + + /* Copy over /proc, the PID, and /cwd/. */ + rewrite = stpcpy (buffer, "/proc/"); + rewrite = format_pid (rewrite, tracee->pid); + rewrite = stpcpy (rewrite, "/cwd/"); + + /* Make sure there is enough free space. */ + remaining = buffer + sizeof buffer - rewrite - 1; + rewrite = stpncpy (rewrite, name, remaining); + + /* Replace name with buffer. */ + name = buffer; + } fd = open (name, O_RDONLY); if (fd < 0) -- cgit v1.2.1 From f92bdbc67754c77afcae95cb1ab237e2c5053ab2 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Mon, 1 May 2023 21:42:42 +0800 Subject: Update Android port * exec/config.h.in: Update config.h.in. * exec/configure.ac: Check for stpcpy and stpncpy. * exec/exec.c (rpl_stpcpy, rpl_stpncpy): Define replacements when they are not present on the system. (process_program_header): Fill comment. --- exec/exec.c | 103 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 99 insertions(+), 4 deletions(-) (limited to 'exec/exec.c') diff --git a/exec/exec.c b/exec/exec.c index c7a73f221f5..df8c9430236 100644 --- a/exec/exec.c +++ b/exec/exec.c @@ -21,7 +21,6 @@ along with GNU Emacs. If not, see . */ #include #include -#include #include #include #include @@ -48,6 +47,103 @@ along with GNU Emacs. If not, see . */ + +/* Define replacements for required string functions. */ + +#ifndef HAVE_STPCPY + +/* Copy SRC to DEST, returning the address of the terminating '\0' in + DEST. */ + +static char * +rpl_stpcpy (char *dest, const char *src) +{ + register char *d; + register const char *s; + + d = dest; + s = src; + + do + *d++ = *s; + while (*s++ != '\0'); + + return d - 1; +} + +#define stpcpy rpl_stpcpy +#endif /* !HAVE_STPCPY */ + +#ifndef HAVE_STPNCPY + +/* Copy no more than N bytes of SRC to DST, returning a pointer past + the last non-NUL byte written into DST. */ + +char * +rpl_stpncpy (char *dest, const char *src, size_t n) +{ + char c, *s; + size_t n4; + + s = dest; + + if (n >= 4) + { + n4 = n >> 2; + + for (;;) + { + c = *src++; + *dest++ = c; + if (c == '\0') + break; + c = *src++; + *dest++ = c; + if (c == '\0') + break; + c = *src++; + *dest++ = c; + if (c == '\0') + break; + c = *src++; + *dest++ = c; + if (c == '\0') + break; + if (--n4 == 0) + goto last_chars; + } + n -= dest - s; + goto zero_fill; + } + + last_chars: + n &= 3; + if (n == 0) + return dest; + + for (;;) + { + c = *src++; + --n; + *dest++ = c; + if (c == '\0') + break; + if (n == 0) + return dest; + } + + zero_fill: + while (n-- > 0) + dest[n] = '\0'; + + return dest - 1; +} + +#define stpncpy rpl_stpncpy +#endif /* !HAVE_STPNCPY */ + + + /* Executable reading functions. These functions extract information from an executable that is about to be loaded. @@ -624,9 +720,8 @@ process_program_header (const char *name, int fd, break; case 3: /* PT_INTERP */ - /* This describes another executable that must be loaded. - Open the interpreter and process each of its headers - as well. */ + /* This describes another executable that must be loaded. Open + the interpreter and process each of its headers as well. */ rc = process_interpreter (fd, header, entry); break; -- cgit v1.2.1 From 5a58a6bc477f290ee0b8a6111e92df56ff538719 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Tue, 2 May 2023 08:16:00 +0800 Subject: Port Android port to older Android systems * exec/config.h.in: Autoheader. * exec/configure.ac: Check for declarations of stpcpy and stpncpy. * exec/exec.c (stpcpy, stpncpy): Use replacements if declarations are not present; this happens when a new Android NDK is building for an old version of Android. --- exec/exec.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'exec/exec.c') diff --git a/exec/exec.c b/exec/exec.c index df8c9430236..7f2cc75338b 100644 --- a/exec/exec.c +++ b/exec/exec.c @@ -50,7 +50,7 @@ along with GNU Emacs. If not, see . */ /* Define replacements for required string functions. */ -#ifndef HAVE_STPCPY +#if !defined HAVE_STPCPY || !defined HAVE_DECL_STPCPY /* Copy SRC to DEST, returning the address of the terminating '\0' in DEST. */ @@ -72,14 +72,14 @@ rpl_stpcpy (char *dest, const char *src) } #define stpcpy rpl_stpcpy -#endif /* !HAVE_STPCPY */ +#endif /* !defined HAVE_STPCPY || !defined HAVE_DECL_STPCPY */ -#ifndef HAVE_STPNCPY +#if !defined HAVE_STPNCPY || !defined HAVE_DECL_STPNCPY /* Copy no more than N bytes of SRC to DST, returning a pointer past the last non-NUL byte written into DST. */ -char * +static char * rpl_stpncpy (char *dest, const char *src, size_t n) { char c, *s; @@ -140,7 +140,7 @@ rpl_stpncpy (char *dest, const char *src, size_t n) } #define stpncpy rpl_stpncpy -#endif /* !HAVE_STPNCPY */ +#endif /* !defined HAVE_STPNCPY || !defined HAVE_DECL_STPNCPY */ -- cgit v1.2.1 From c47716f95b8fda9438047d2683a415a65c18ecbd Mon Sep 17 00:00:00 2001 From: Po Lu Date: Tue, 2 May 2023 20:45:57 +0800 Subject: Update Android port * exec/config.h.in (__bool_true_false_are_defined): * exec/configure.ac (REENTRANT): New definition. (READLINKAT_SYSCALL, READLINK_SYSCALL): New defines. Set on all hosts. * exec/exec.c (MIN, MAX): Remove redundant declarations. Move to config.h. (exec_0): Copy name of executable into NAME when !REENTRANT. * exec/exec.h (struct exec_tracee): New struct `exec_file'. * exec/trace.c (remove_tracee, handle_exec, handle_readlinkat) (process_system_call, after_fork): Handle readlinkat system calls. --- exec/exec.c | 52 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 15 deletions(-) (limited to 'exec/exec.c') diff --git a/exec/exec.c b/exec/exec.c index 7f2cc75338b..17051428658 100644 --- a/exec/exec.c +++ b/exec/exec.c @@ -31,14 +31,6 @@ along with GNU Emacs. If not, see . */ #include #include -#ifndef MIN -#define MIN(a, b) ((a) < (b) ? (a) : (b)) -#endif /* MIN */ - -#ifndef MAX -#define MAX(a, b) ((a) < (b) ? (b) : (a)) -#endif /* MAX */ - #include "exec.h" #if defined __mips__ && !defined MIPS_NABI @@ -938,6 +930,10 @@ format_pid (char *in, unsigned int pid) with #!; in that case, find the program to open and use that instead. + If REENTRANT is not defined, NAME is actually a buffer of size + PATH_MAX + 80. In that case, copy over the file name actually + opened. + Next, read the executable header, and add the necessary memory mappings for each file. Finally, return the action data and its size in *SIZE. @@ -948,7 +944,7 @@ format_pid (char *in, unsigned int pid) Value is NULL upon failure, with errno set accordingly. */ char * -exec_0 (const char *name, struct exec_tracee *tracee, +exec_0 (char *name, struct exec_tracee *tracee, size_t *size, USER_REGS_STRUCT *regs) { int fd, rc, i; @@ -961,7 +957,8 @@ exec_0 (const char *name, struct exec_tracee *tracee, #if defined __mips__ && !defined MIPS_NABI int fpu_mode; #endif /* defined __mips__ && !defined MIPS_NABI */ - char buffer[PATH_MAX + 80], *rewrite; + char buffer[80], buffer1[PATH_MAX + 80], *rewrite; + ssize_t link_size; size_t remaining; /* If name is not absolute, then make it relative to TRACEE's @@ -971,18 +968,43 @@ exec_0 (const char *name, struct exec_tracee *tracee, { /* Clear `buffer'. */ memset (buffer, 0, sizeof buffer); + memset (buffer1, 0, sizeof buffer); /* Copy over /proc, the PID, and /cwd/. */ rewrite = stpcpy (buffer, "/proc/"); rewrite = format_pid (rewrite, tracee->pid); - rewrite = stpcpy (rewrite, "/cwd/"); + stpcpy (rewrite, "/cwd"); + + /* Resolve this symbolic link. */ + + link_size = readlink (buffer, buffer1, + PATH_MAX + 1); + + if (link_size < 0) + return NULL; + + /* Check that the name is a reasonable size. */ + + if (link_size > PATH_MAX) + { + /* The name is too long. */ + errno = ENAMETOOLONG; + return NULL; + } + + /* Add a directory separator if necessary. */ + + if (!link_size || buffer1[link_size - 1] != '/') + buffer1[link_size] = '/', link_size++; - /* Make sure there is enough free space. */ - remaining = buffer + sizeof buffer - rewrite - 1; + rewrite = buffer1 + link_size; + remaining = buffer1 + sizeof buffer1 - rewrite - 1; rewrite = stpncpy (rewrite, name, remaining); - /* Replace name with buffer. */ - name = buffer; + /* Replace name with buffer1. */ +#ifndef REENTRANT + strcpy (name, buffer1); +#endif /* REENTRANT */ } fd = open (name, O_RDONLY); -- cgit v1.2.1 From 2ba6c5035c904426d564eac47381480158cbbb9e Mon Sep 17 00:00:00 2001 From: Po Lu Date: Fri, 5 May 2023 12:10:14 +0800 Subject: Update Android port * doc/emacs/android.texi (Android Environment): Document lossage with SIGSTOP. * exec/exec.c (exec_0): Check X_OK on file being opened. Also handle /proc/self/exe. --- exec/exec.c | 76 +++++++++++++++++++++++++++++++++++++------------------------ 1 file changed, 46 insertions(+), 30 deletions(-) (limited to 'exec/exec.c') diff --git a/exec/exec.c b/exec/exec.c index 17051428658..a15386b51bb 100644 --- a/exec/exec.c +++ b/exec/exec.c @@ -961,52 +961,68 @@ exec_0 (char *name, struct exec_tracee *tracee, ssize_t link_size; size_t remaining; - /* If name is not absolute, then make it relative to TRACEE's - cwd. Use stpcpy, as sprintf is not reentrant. */ + /* If the process is trying to run /proc/self/exe, make it run + itself instead. */ - if (name[0] && name[0] != '/') + if (!strcmp (name, "/proc/self/exe") && tracee->exec_file) { - /* Clear `buffer'. */ - memset (buffer, 0, sizeof buffer); - memset (buffer1, 0, sizeof buffer); + strncpy (name, tracee->exec_file, PATH_MAX - 1); + name[PATH_MAX] = '\0'; + } + else + { + /* If name is not absolute, then make it relative to TRACEE's + cwd. Use stpcpy, as sprintf is not reentrant. */ - /* Copy over /proc, the PID, and /cwd/. */ - rewrite = stpcpy (buffer, "/proc/"); - rewrite = format_pid (rewrite, tracee->pid); - stpcpy (rewrite, "/cwd"); + if (name[0] && name[0] != '/') + { + /* Clear `buffer'. */ + memset (buffer, 0, sizeof buffer); + memset (buffer1, 0, sizeof buffer); - /* Resolve this symbolic link. */ + /* Copy over /proc, the PID, and /cwd/. */ + rewrite = stpcpy (buffer, "/proc/"); + rewrite = format_pid (rewrite, tracee->pid); + stpcpy (rewrite, "/cwd"); - link_size = readlink (buffer, buffer1, - PATH_MAX + 1); + /* Resolve this symbolic link. */ - if (link_size < 0) - return NULL; + link_size = readlink (buffer, buffer1, + PATH_MAX + 1); - /* Check that the name is a reasonable size. */ + if (link_size < 0) + return NULL; - if (link_size > PATH_MAX) - { - /* The name is too long. */ - errno = ENAMETOOLONG; - return NULL; - } + /* Check that the name is a reasonable size. */ + + if (link_size > PATH_MAX) + { + /* The name is too long. */ + errno = ENAMETOOLONG; + return NULL; + } - /* Add a directory separator if necessary. */ + /* Add a directory separator if necessary. */ - if (!link_size || buffer1[link_size - 1] != '/') - buffer1[link_size] = '/', link_size++; + if (!link_size || buffer1[link_size - 1] != '/') + buffer1[link_size] = '/', link_size++; - rewrite = buffer1 + link_size; - remaining = buffer1 + sizeof buffer1 - rewrite - 1; - rewrite = stpncpy (rewrite, name, remaining); + rewrite = buffer1 + link_size; + remaining = buffer1 + sizeof buffer1 - rewrite - 1; + rewrite = stpncpy (rewrite, name, remaining); - /* Replace name with buffer1. */ + /* Replace name with buffer1. */ #ifndef REENTRANT - strcpy (name, buffer1); + strcpy (name, buffer1); #endif /* REENTRANT */ + } } + /* Check that the file is accessible and executable. */ + + if (access (name, X_OK)) + return NULL; + fd = open (name, O_RDONLY); if (fd < 0) return NULL; -- cgit v1.2.1 From 1145572af2780df2e88b54372d43977db5305ded Mon Sep 17 00:00:00 2001 From: Po Lu Date: Tue, 23 May 2023 09:22:19 +0800 Subject: ; * exec/exec.c (exec_0): Use strcpy. --- exec/exec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'exec/exec.c') diff --git a/exec/exec.c b/exec/exec.c index a15386b51bb..0e077284860 100644 --- a/exec/exec.c +++ b/exec/exec.c @@ -983,7 +983,7 @@ exec_0 (char *name, struct exec_tracee *tracee, /* Copy over /proc, the PID, and /cwd/. */ rewrite = stpcpy (buffer, "/proc/"); rewrite = format_pid (rewrite, tracee->pid); - stpcpy (rewrite, "/cwd"); + strcpy (rewrite, "/cwd"); /* Resolve this symbolic link. */ -- cgit v1.2.1 From 456095ed3129f7ce2fe1ff019ea5d912a69ed2a1 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Wed, 31 May 2023 11:27:19 +0800 Subject: Update Android port * exec/exec.c (insert_args): New argument `arg3'. Replace argv[1] with that argument. (exec_0): Pass file name of script to `insert_args'. --- exec/exec.c | 93 +++++++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 63 insertions(+), 30 deletions(-) (limited to 'exec/exec.c') diff --git a/exec/exec.c b/exec/exec.c index 0e077284860..0d9187cabfa 100644 --- a/exec/exec.c +++ b/exec/exec.c @@ -740,16 +740,18 @@ process_program_header (const char *name, int fd, } /* Prepend one or two extra arguments ARG1 and ARG2 to a pending - execve system call. TRACEE is the tracee performing the system - call, and REGS are its current user registers. Value is 1 upon - failure, else 0. */ + execve system call. Replace the argument immediately after + with ARG3. + + TRACEE is the tracee performing the system call, and REGS are its + current user registers. Value is 1 upon failure, else 0. */ static int insert_args (struct exec_tracee *tracee, USER_REGS_STRUCT *regs, - const char *arg1, const char *arg2) + const char *arg1, const char *arg2, const char *arg3) { USER_WORD argv, argc, word, new; - USER_WORD new1, new2; + USER_WORD new1, new2, new3, i; size_t text_size, effective_size; USER_REGS_STRUCT original; @@ -783,15 +785,17 @@ insert_args (struct exec_tracee *tracee, USER_REGS_STRUCT *regs, text. */ text_size = (strlen (arg1) + 1 - + (arg2 ? strlen (arg2) + 1 : 0)); + + (arg2 ? strlen (arg2) + 1 : 0) + + strlen (arg3) + 1); /* Round it up to the user word size. */ text_size += sizeof (USER_WORD) - 1; text_size &= ~(sizeof (USER_WORD) - 1); - /* Now allocate the new argv. */ + /* Now allocate the new argv. Make sure argc is at least 1; it + needs to hold ARG3. */ - effective_size = sizeof word * (argc + 2) + text_size; + effective_size = sizeof word * (MAX (1, argc) + 2) + text_size; if (arg2) effective_size += sizeof word; @@ -808,11 +812,13 @@ insert_args (struct exec_tracee *tracee, USER_REGS_STRUCT *regs, /* Figure out where argv starts. */ - new2 = new + text_size; + new3 = new + text_size; - /* Now write the two strings. */ + /* Now write the first two strings. */ new1 = new + strlen (arg1) + 1; + new2 = new1 + (arg2 ? strlen (arg2) + 1 : 0); + if (user_copy (tracee, (const unsigned char *) arg1, new, new1 - new)) goto fail; @@ -821,50 +827,77 @@ insert_args (struct exec_tracee *tracee, USER_REGS_STRUCT *regs, new1, new2 - new1)) goto fail; + /* Write the replacement arg3, the file name of the executable. */ + + if (user_copy (tracee, (const unsigned char *) arg3, + new2, new3 - new2)) + goto fail; + /* Start copying argv back to new2. First, write the one or two new arguments. */ if (ptrace (PTRACE_POKETEXT, tracee->pid, - (void *) new2, (void *) new)) + (void *) new3, (void *) new)) goto fail; - new2 += sizeof new2; + new3 += sizeof new3; if (arg2 && ptrace (PTRACE_POKETEXT, tracee->pid, - (void *) new2, (void *) new1)) + (void *) new3, (void *) new1)) goto fail; else if (arg2) - new2 += sizeof new2; + new3 += sizeof new3; + + /* Next, write the third argument. */ + + if (ptrace (PTRACE_POKETEXT, tracee->pid, (void *) new3, + (void *) new2)) + goto fail; + + new3 += sizeof new3; /* Copy the remaining arguments back. */ argv = regs->SYSCALL_ARG1_REG; - /* Make sure the trailing NULL is included. */ - argc += 1; - - while (argc) + if (argc) { - /* Read one argument. */ - word = ptrace (PTRACE_PEEKDATA, tracee->pid, - (void *) argv, NULL); - argv += sizeof argv; - argc--; + /* Make sure the trailing NULL is included. */ + argc += 1; + + /* Now copy each argument in argv, starting from argv[1]. */ + + for (i = 1; i < argc; ++i) + { + /* Read one argument. */ + word = ptrace (PTRACE_PEEKDATA, tracee->pid, + (void *) (argv + i * sizeof argv), NULL); + + /* Write one argument, then increment new3. */ - /* Write one argument, then increment new2. */ + if (ptrace (PTRACE_POKETEXT, tracee->pid, + (void *) new3, (void *) word)) + goto fail; + + new3 += sizeof new3; + } + } + else + { + /* Just write the trailing NULL. */ if (ptrace (PTRACE_POKETEXT, tracee->pid, - (void *) new2, (void *) word)) + (void *) new3, (void *) 0)) goto fail; - new2 += sizeof new2; + new3 += sizeof new3; } - /* Assert that new2 is not out of bounds. */ - assert (new2 == new + effective_size); + /* Assert that new3 is not out of bounds. */ + assert (new3 == new + effective_size); /* And that it is properly aligned. */ - assert (!(new2 & (sizeof new2 - 2))); + assert (!(new3 & (sizeof new3 - 2))); /* Now modify the system call argument to point to new + text_size. */ @@ -1046,7 +1079,7 @@ exec_0 (char *name, struct exec_tracee *tracee, and perhaps `extra'. */ if (insert_args (tracee, regs, interpreter_name, - extra)) + extra, name)) goto fail1; } -- cgit v1.2.1