diff --git a/src/kernel/emulation/linux/CMakeLists.txt b/src/kernel/emulation/linux/CMakeLists.txt index e7e1282c1..e9dbc082b 100644 --- a/src/kernel/emulation/linux/CMakeLists.txt +++ b/src/kernel/emulation/linux/CMakeLists.txt @@ -57,6 +57,7 @@ set(emulation_sources process/fork.c process/wait4.c process/waitid.c + process/execve.c signal/duct_signals.c signal/kill.c signal/sigaltstack.c diff --git a/src/kernel/emulation/linux/process/execve.c b/src/kernel/emulation/linux/process/execve.c new file mode 100644 index 000000000..fc917d50b --- /dev/null +++ b/src/kernel/emulation/linux/process/execve.c @@ -0,0 +1,86 @@ +#include "execve.h" +#include "../base.h" +#include "../errno.h" +#include +#include "../fcntl/open.h" +#include "../unistd/read.h" +#include "../unistd/close.h" +#include "../unistd/readlink.h" +#include + +#define MH_MAGIC 0xfeedface +#define MH_CIGAM 0xcefaedfe +#define MH_MAGIC_64 0xfeedfacf +#define MH_CIGAM_64 0xcffaedfe +#define FAT_MAGIC 0xcafebabe +#define FAT_CIGAM 0xbebafeca + +extern int strcmp(const char *s1, const char *s2); + +long sys_execve(char* fname, char** argvp, char** envp) +{ + int ret, fd; + uint32_t magic; + char dyld_path[256]; + + // Ideally, if everybody used binfmt_misc to allow direct + // execution of Mach-O binaries under Darling, this wouldn't + // be necessary. But we cannot rely on that. + + fd = sys_open(fname, BSD_O_RDONLY, 0); + if (fd < 0) + return fd; + + ret = sys_read(fd, &magic, sizeof(magic)); + if (ret != 4) + goto no_macho; + + if (magic == MH_MAGIC || magic == MH_CIGAM + || magic == MH_MAGIC_64 || magic == MH_CIGAM_64 + || magic == FAT_MAGIC || magic == FAT_CIGAM) + { + // It is a Mach-O file + int len, i; + char** modargvp; + + len = sys_readlink("/proc/self/exe", dyld_path, sizeof(dyld_path)-1); + if (len < 0) + goto no_macho; + + dyld_path[len] = '\0'; + + // Remove 64 or 32 suffix if present + if (strcmp(&dyld_path[len - 2], "32") == 0 + || strcmp(&dyld_path[len - 2], "64") == 0) + { + dyld_path[len-2] = '\0'; + } + + // Prepend dyld path + len = 0; + + // Count arguments + while (argvp[len++]); + + // Allocate a new argvp, execute dyld_path + modargvp = (char**) __builtin_alloca(sizeof(void*) * (len+1)); + modargvp[0] = dyld_path; + modargvp[1] = fname; + + for (i = 2; i < len+1; i++) + modargvp[i] = argvp[i-1]; + + argvp = modargvp; + fname = dyld_path; + } + +no_macho: + sys_close(fd); + + ret = LINUX_SYSCALL(__NR_execve, fname, argvp, envp); + if (ret < 0) + ret = errno_linux_to_bsd(ret); + + return ret; +} + diff --git a/src/kernel/emulation/linux/process/execve.h b/src/kernel/emulation/linux/process/execve.h new file mode 100644 index 000000000..a157af73c --- /dev/null +++ b/src/kernel/emulation/linux/process/execve.h @@ -0,0 +1,7 @@ +#ifndef LINUX_EXECVE_H +#define LINUX_EXECVE_H + +long sys_execve(char* fname, char** argvp, char** envp); + +#endif + diff --git a/src/kernel/emulation/linux/syscalls.c b/src/kernel/emulation/linux/syscalls.c index 0f80a9e96..e65af4881 100644 --- a/src/kernel/emulation/linux/syscalls.c +++ b/src/kernel/emulation/linux/syscalls.c @@ -47,6 +47,7 @@ #include "process/vfork.h" #include "process/wait4.h" #include "process/waitid.h" +#include "process/execve.h" #include "misc/ioctl.h" #include "misc/getrlimit.h" #include "misc/thread_selfid.h" @@ -116,6 +117,7 @@ void* __bsd_syscall_table[512] = { [54] = sys_ioctl, [57] = sys_symlink, [58] = sys_readlink, + [59] = sys_execve, [60] = sys_umask, [61] = sys_chroot, [66] = sys_vfork,