diff options
Diffstat (limited to 'libgcompat/unistd.c')
-rw-r--r-- | libgcompat/unistd.c | 54 |
1 files changed, 54 insertions, 0 deletions
diff --git a/libgcompat/unistd.c b/libgcompat/unistd.c index d2f3097..d632eee 100644 --- a/libgcompat/unistd.c +++ b/libgcompat/unistd.c @@ -3,6 +3,10 @@ #include <limits.h> /* NGROUPS_MAX */ #include <stddef.h> /* NULL, size_t */ #include <unistd.h> /* confstr, getcwd, getgroups, ... */ +#include <errno.h> /* ENOSYS, ENOMEM */ +#include <stdlib.h> /* calloc */ +#include <dlfcn.h> /* dlsym */ +#include <string.h> /* strcmp */ #include "alias.h" /* alias */ @@ -179,3 +183,53 @@ int group_member(gid_t gid) return 0; } + +#ifndef LOADER +#error LOADER must be defined +#endif + +static int (*real_execve)(const char *pathname, char *const argv[], char *const envp[]); +int execve(const char *pathname, char *const argv[], char *const envp[]) { + if(real_execve == NULL) { + real_execve = dlsym(RTLD_NEXT, "execve"); + if(real_execve == NULL) { + errno = ENOSYS; + return -1; + } + } + + if(!strcmp(pathname, "/proc/self/exe")) { + char **new_argv; + char target[PATH_MAX] = ""; + int argc = 0, i = 0; + while(argv[i++] != 0) argc++; + + i = readlink("/proc/self/exe", target, sizeof(target)); + if(i < 0 || i == sizeof(target)) { + errno = ENOMEM; + return -1; + } + + new_argv = calloc(argc + 6, sizeof(char *)); + new_argv[0] = LOADER; + new_argv[1] = "--argv0"; + new_argv[2] = argv[0]; + new_argv[3] = "--preload"; + new_argv[4] = "/lib/libgcompat.so.0"; + new_argv[5] = target; + for(int j = 1, i = 6; j < argc; ++i, ++j) { + new_argv[i] = argv[j]; + } + return execve(LINKER, new_argv, envp); + } + return real_execve(pathname, argv, envp); +} + +extern char **environ; +int execv(const char *pathname, char *const argv[]) { + return execve(pathname, argv, environ); +} + +int execvp(const char *file, char *const argv[]) { + return execv(file, argv); +} |