diff --git a/ape/loader.c b/ape/loader.c index 13e6c5081..d3e95b822 100644 --- a/ape/loader.c +++ b/ape/loader.c @@ -87,6 +87,8 @@ #define MIN(X, Y) ((Y) > (X) ? (X) : (Y)) #define MAX(X, Y) ((Y) < (X) ? (X) : (Y)) +#define PATH_MAX 1024 /* XXX verify */ + #define SupportsLinux() (SUPPORT_VECTOR & LINUX) #define SupportsXnu() (SUPPORT_VECTOR & XNU) #define SupportsFreebsd() (SUPPORT_VECTOR & FREEBSD) @@ -212,13 +214,13 @@ struct PathSearcher { const char *name; const char *syspath; unsigned long namelen; - char path[1024]; + char path[PATH_MAX]; }; struct ApeLoader { union ElfPhdrBuf phdr; struct PathSearcher ps; - char path[1024]; + char path[PATH_MAX]; }; EXTERN_C long SystemCall(long, long, long, long, long, long, long, int); @@ -239,6 +241,8 @@ static int StrCmp(const char *l, const char *r) { return (l[i] & 255) - (r[i] & 255); } +#if 0 + static const char *StrRChr(const char *s, int c) { const char *b = 0; if (s) { @@ -256,6 +260,8 @@ static const char *BaseName(const char *s) { return b ? b + 1 : s; } +#endif + static void Bzero(void *a, unsigned long n) { long z; char *p, *e; @@ -347,7 +353,7 @@ static char *Utox(char p[19], unsigned long x) { return p; } -static char *Utoa(char p[21], unsigned long x) { +static char *Utoa(char p[20], unsigned long x) { char t; unsigned long i, a, b; i = 0; @@ -538,6 +544,40 @@ __attribute__((__noreturn__)) static void Pexit(int os, const char *c, int rc, Exit(127, os); } +#define PSFD "/proc/self/fd/" + +static int RealPath(int os, int fd, char *path, char **resolved) { + char buf[PATH_MAX]; + int rc; + if (IsLinux()) { + char psfd[sizeof(PSFD) + 19]; + MemMove(psfd, PSFD, sizeof(PSFD) - 1); + Utoa(psfd + sizeof(PSFD) - 1, fd); + rc = SystemCall(-100, (long)psfd, (long)buf, PATH_MAX, 0, 0, 0, + IsAarch64() ? 78 : 267); + if (rc >= 0) { + if (rc == PATH_MAX) { + rc = -36; + } else { + buf[rc] = 0; + } + } + } else if (IsXnu()) { + rc = SystemCall(fd, 50, (long)buf, 0, 0, 0, 0, 92 | 0x2000000); + } else if (IsOpenbsd()) { + rc = SystemCall((long)path, (long)buf, 0, 0, 0, 0, 0, 115); + } else { + *resolved = 0; + return 0; + } + if (rc >= 0) { + MemMove(path, buf, StrLen(buf) + 1); + *resolved = path; + rc = 0; + } + return rc; +} + static char AccessCommand(struct PathSearcher *ps, unsigned long pathlen) { if (pathlen + 1 + ps->namelen + 1 > sizeof(ps->path)) return 0; if (pathlen && ps->path[pathlen - 1] != '/') ps->path[pathlen++] = '/'; @@ -1039,6 +1079,8 @@ EXTERN_C __attribute__((__noreturn__)) void ApeLoader(long di, long *sp, Pexit(os, prog, 0, "not found (maybe chmod +x or ./ needed)"); } else if ((fd = Open(exe, O_RDONLY, 0, os)) < 0) { Pexit(os, exe, fd, "open"); + } else if ((rc = RealPath(os, fd, exe, &prog)) < 0) { + Pexit(os, exe, rc, "realpath"); } else if ((rc = Pread(fd, ebuf->buf, sizeof(ebuf->buf), 0, os)) < 0) { Pexit(os, exe, rc, "read"); } else if ((unsigned long)rc < sizeof(ebuf->ehdr)) { @@ -1046,10 +1088,9 @@ EXTERN_C __attribute__((__noreturn__)) void ApeLoader(long di, long *sp, } pe = ebuf->buf + rc; - /* change argv[0] to resolved path if it's ambiguous */ - if (argc > 0 && ((*prog != '/' && *exe == '/' && !StrCmp(prog, argv[0])) || - !StrCmp(BaseName(prog), argv[0]))) { - argv[0] = exe; + /* change argv[0] to resolved path (TODO remove) */ + if (argc > 0 && prog) { + argv[0] = prog; } /* ape intended behavior