diff --git a/ape/launch.S b/ape/launch.S index d4d89651a..87489f9f5 100644 --- a/ape/launch.S +++ b/ape/launch.S @@ -31,17 +31,16 @@ // // @param rdi is passed through as-is // @param rsi is address of entrypoint (becomes zero) -// @param rdx is stack pointer (becomes zero) -// @param rcx is passed through as-is +// @param rdx is passed through as-is +// @param rcx is stack pointer (becomes r8) // @noreturn Launch: #ifdef __aarch64__ mov x16,x1 - mov sp,x2 + mov sp,x3 mov x1,0 - mov x2,0 - mov x3,0 + mov x3,x4 mov x4,0 mov x5,0 mov x6,0 @@ -71,6 +70,8 @@ Launch: #else + mov %rcx,%rsp + mov %r8,%rcx xor %r8d,%r8d xor %r9d,%r9d xor %r10d,%r10d @@ -79,8 +80,6 @@ Launch: xor %r13d,%r13d xor %r14d,%r14d xor %r15d,%r15d - mov %rdx,%rsp - xor %edx,%edx push %rsi xor %esi,%esi xor %ebp,%ebp diff --git a/ape/loader.c b/ape/loader.c index b65bd147f..87369cc42 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,17 +214,18 @@ 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); -EXTERN_C void Launch(void *, long, void *, int) __attribute__((__noreturn__)); +EXTERN_C void +Launch(void *, long, void *, void *, int) __attribute__((__noreturn__)); extern char __executable_start[]; extern char _end[]; @@ -239,12 +242,13 @@ static int StrCmp(const char *l, const char *r) { return (l[i] & 255) - (r[i] & 255); } -static const char *BaseName(const char *s) { - int c; - const char *b = ""; +#if 0 + +static const char *StrRChr(const char *s, int c) { + const char *b = 0; if (s) { - while ((c = *s++)) { - if (c == '/') { + for (; *s; ++s) { + if (*s == c) { b = s; } } @@ -252,6 +256,13 @@ static const char *BaseName(const char *s) { return b; } +static const char *BaseName(const char *s) { + const char *b = StrRChr(s, '/'); + return b ? b + 1 : s; +} + +#endif + static void Bzero(void *a, unsigned long n) { long z; char *p, *e; @@ -343,7 +354,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; @@ -534,6 +545,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++] = '/'; @@ -599,8 +644,9 @@ static char *Commandv(struct PathSearcher *ps, int os, const char *name, } } -__attribute__((__noreturn__)) static void Spawn(int os, const char *exe, int fd, - long *sp, unsigned long pagesz, +__attribute__((__noreturn__)) static void Spawn(int os, const char *exe, + char *path, int fd, long *sp, + unsigned long pagesz, struct ElfEhdr *e, struct ElfPhdr *p) { long rc; @@ -757,12 +803,12 @@ __attribute__((__noreturn__)) static void Spawn(int os, const char *exe, int fd, Msyscall(dynbase + code, codesize, os); /* call program entrypoint */ - Launch(IsFreebsd() ? sp : 0, dynbase + e->e_entry, sp, os); + Launch(IsFreebsd() ? sp : 0, dynbase + e->e_entry, path, sp, os); } static const char *TryElf(struct ApeLoader *M, union ElfEhdrBuf *ebuf, - const char *exe, int fd, long *sp, long *auxv, - unsigned long pagesz, int os) { + const char *exe, char *path, int fd, long *sp, + long *auxv, unsigned long pagesz, int os) { long i, rc; unsigned size; struct ElfEhdr *e; @@ -877,7 +923,7 @@ static const char *TryElf(struct ApeLoader *M, union ElfEhdrBuf *ebuf, } /* we're now ready to load */ - Spawn(os, exe, fd, sp, pagesz, e, p); + Spawn(os, exe, path, fd, sp, pagesz, e, p); } __attribute__((__noreturn__)) static void ShowUsage(int os, int fd, int rc) { @@ -1035,6 +1081,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)) { @@ -1042,12 +1090,6 @@ 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; - } - /* ape intended behavior 1. if ape, will scan shell script for elf printf statements 2. shell script may have multiple lines producing elf headers @@ -1080,9 +1122,9 @@ EXTERN_C __attribute__((__noreturn__)) void ApeLoader(long di, long *sp, } } if (i >= sizeof(ebuf->ehdr)) { - TryElf(M, ebuf, exe, fd, sp, auxv, pagesz, os); + TryElf(M, ebuf, exe, prog, fd, sp, auxv, pagesz, os); } } } - Pexit(os, exe, 0, TryElf(M, ebuf, exe, fd, sp, auxv, pagesz, os)); + Pexit(os, exe, 0, TryElf(M, ebuf, exe, prog, fd, sp, auxv, pagesz, os)); } diff --git a/libc/crt/crt.S b/libc/crt/crt.S index 6d2a798df..07978f47b 100644 --- a/libc/crt/crt.S +++ b/libc/crt/crt.S @@ -62,6 +62,8 @@ _start: // set operating system when already detected 1: mov %cl,__hostos(%rip) + mov %rdx,__program_executable_name(%rip) + // get startup timestamp as early as possible // its used by --strace flag and kprintf() %T rdtsc diff --git a/libc/nexgen32e/program_executable_name2.c b/libc/nexgen32e/program_executable_name.c similarity index 100% rename from libc/nexgen32e/program_executable_name2.c rename to libc/nexgen32e/program_executable_name.c diff --git a/test/libc/calls/getprogramexecutablename_test.c b/test/libc/calls/getprogramexecutablename_test.c index 61c9ed914..aa5d51a32 100644 --- a/test/libc/calls/getprogramexecutablename_test.c +++ b/test/libc/calls/getprogramexecutablename_test.c @@ -28,25 +28,20 @@ #include "libc/testlib/testlib.h" static char *self; -static bool skipcosmotests; void SetUp(void) { self = GetProgramExecutableName(); } void SetUpOnce(void) { - if (!getenv("COSMOPOLITAN_PROGRAM_EXECUTABLE")) { - fprintf(stderr, - "warning: old ape loader detected; skipping some tests %m\n"); - skipcosmotests = true; - } testlib_enable_tmp_setup_teardown(); } __attribute__((__constructor__)) static void Child(int argc, char *argv[]) { if (argc >= 2 && !strcmp(argv[1], "Child")) { - ASSERT_EQ(3, argc); + ASSERT_EQ(argc, 4); EXPECT_STREQ(argv[2], GetProgramExecutableName()); + EXPECT_STREQ(argv[3], argv[0]); exit(g_testlib_failed); } } @@ -59,33 +54,33 @@ TEST(GetProgramExecutableName, ofThisFile) { TEST(GetProgramExecutableName, nullEnv) { SPAWN(fork); - execve(self, (char *[]){self, "Child", self, 0}, (char *[]){0}); + execve(self, (char *[]){self, "Child", self, self, 0}, (char *[]){0}); abort(); EXITS(0); } TEST(GetProramExecutableName, weirdArgv0NullEnv) { SPAWN(fork); - execve(self, (char *[]){"hello", "Child", self, 0}, (char *[]){0}); + execve(self, (char *[]){"hello", "Child", self, "hello", 0}, (char *[]){0}); abort(); EXITS(0); } TEST(GetProgramExecutableName, weirdArgv0CosmoVar) { - if (skipcosmotests) return; char buf[32 + PATH_MAX]; stpcpy(stpcpy(buf, "COSMOPOLITAN_PROGRAM_EXECUTABLE="), self); SPAWN(fork); - execve(self, (char *[]){"hello", "Child", self, 0}, (char *[]){buf, 0}); + execve(self, (char *[]){"hello", "Child", self, "hello", 0}, + (char *[]){buf, 0}); abort(); EXITS(0); } TEST(GetProgramExecutableName, weirdArgv0WrongCosmoVar) { - if (skipcosmotests) return; char *bad = "COSMOPOLITAN_PROGRAM_EXECUTABLE=hi"; SPAWN(fork); - execve(self, (char *[]){"hello", "Child", self, 0}, (char *[]){bad, 0}); + execve(self, (char *[]){"hello", "Child", self, "hello", 0}, + (char *[]){bad, 0}); abort(); EXITS(0); } @@ -104,7 +99,7 @@ TEST(GetProgramExecutableName, movedSelf) { ASSERT_NE(NULL, getcwd(buf, BUFSIZ - 5)); stpcpy(buf + strlen(buf), "/test"); SPAWN(fork); - execve(buf, (char *[]){"hello", "Child", buf, 0}, (char *[]){0}); + execve(buf, (char *[]){"hello", "Child", buf, "hello", 0}, (char *[]){0}); abort(); EXITS(0); }