diff --git a/ape/loader.c b/ape/loader.c index 129246764..3d23edb97 100644 --- a/ape/loader.c +++ b/ape/loader.c @@ -947,6 +947,7 @@ EXTERN_C __attribute__((__noreturn__)) void ApeLoader(long di, long *sp, } else if (SupportsNetbsd() && !os && ap[0] == AT_EXECFN_NETBSD) { os = NETBSD; } else if (SupportsLinux() && ap[0] == AT_FLAGS) { + // TODO(mrdomino): maybe set/insert this when we are called as "ape -". arg0 = !!(ap[1] & AT_FLAGS_PRESERVE_ARGV0); } } diff --git a/libc/calls/getprogramexecutablename.greg.c b/libc/calls/getprogramexecutablename.greg.c index dcc01aedf..89cb8aa77 100644 --- a/libc/calls/getprogramexecutablename.greg.c +++ b/libc/calls/getprogramexecutablename.greg.c @@ -25,6 +25,7 @@ #include "libc/errno.h" #include "libc/fmt/libgen.h" #include "libc/intrin/getenv.internal.h" +#include "libc/intrin/strace.internal.h" #include "libc/limits.h" #include "libc/macros.internal.h" #include "libc/nt/runtime.h" @@ -260,5 +261,6 @@ static void InitProgramExecutableName(void) { */ char *GetProgramExecutableName(void) { cosmo_once(&g_prog.once, InitProgramExecutableName); + STRACE("GetProgramExecutableName() → %#s", __program_executable_name); return __program_executable_name; } diff --git a/libc/sysv/consts/auxv.h b/libc/sysv/consts/auxv.h index 208b5851e..5d897de82 100644 --- a/libc/sysv/consts/auxv.h +++ b/libc/sysv/consts/auxv.h @@ -4,13 +4,15 @@ /* * integral getauxval() keys */ -#define AT_PHDR 3 -#define AT_PHENT 4 -#define AT_PHNUM 5 -#define AT_PAGESZ 6 -#define AT_BASE 7 -#define AT_FLAGS 8 -#define AT_ENTRY 9 +#define AT_PHDR 3 +#define AT_PHENT 4 +#define AT_PHNUM 5 +#define AT_PAGESZ 6 +#define AT_BASE 7 +#define AT_FLAGS 8 +#define AT_FLAGS_PRESERVE_ARGV0_BIT 0 +#define AT_FLAGS_PRESERVE_ARGV0 (1 << AT_FLAGS_PRESERVE_ARGV0_BIT) +#define AT_ENTRY 9 COSMOPOLITAN_C_START_ diff --git a/test/libc/calls/getprogramexecutablename_test.c b/test/libc/calls/getprogramexecutablename_test.c index 2665d34eb..77f4b3a61 100644 --- a/test/libc/calls/getprogramexecutablename_test.c +++ b/test/libc/calls/getprogramexecutablename_test.c @@ -27,6 +27,7 @@ #include "libc/serialize.h" #include "libc/stdio/stdio.h" #include "libc/str/str.h" +#include "libc/sysv/consts/auxv.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/ok.h" #include "libc/testlib/ezbench.h" @@ -34,14 +35,16 @@ #include "libc/testlib/testlib.h" static char *self; -static bool skiptests; +static bool loaded, skiptests, skiparg0; void SetUpOnce(void) { self = GetProgramExecutableName(); testlib_enable_tmp_setup_teardown(); if (IsMetal()) { skiptests = true; - } else if (IsOpenbsd() || (IsXnu() && !IsXnuSilicon())) { + } else if (IsWindows()) { + /* do all tests */ + } else if (!loaded) { ASSERT_STRNE(self, ""); ASSERT_SYS(0, 3, open(self, O_RDONLY)); char buf[8]; @@ -50,15 +53,24 @@ void SetUpOnce(void) { if (READ64LE(buf) != READ64LE("MZqFpD='") && READ64LE(buf) != READ64LE("jartsr='") && READ64LE(buf) != READ64LE("APEDBG='")) { - fprintf(stderr, - "we appear to be running as an assimilated binary on OpenBSD or " - "x86_64 XNU;\nGetProgramExecutableName is unreliable here\n"); - skiptests = true; + // GetProgramExecutableName does not work reliably for assimilated + // OpenBSD or XNU binaries. + skiptests = IsOpenbsd() || (IsXnu() && !IsXnuSilicon()); } + } else { + skiparg0 = + !(IsXnuSilicon() || (getauxval(AT_FLAGS) & AT_FLAGS_PRESERVE_ARGV0)); + } + fprintf(stderr, loaded ? "loaded\n" : "not loaded\n"); + if (skiptests) { + fprintf(stderr, "skipping most GetProgramExecutableName tests\n"); + } else if (skiparg0) { + fprintf(stderr, "skipping argv[0] tests\n"); } } __attribute__((__constructor__)) static void Child(int argc, char *argv[]) { + loaded = !!__program_executable_name; if (argc >= 2 && !strcmp(argv[1], "Child")) { int rc; if (!IsWindows()) { @@ -72,14 +84,10 @@ __attribute__((__constructor__)) static void Child(int argc, char *argv[]) { if (strcmp(argv[2], GetProgramExecutableName())) { exit(123); } - if (!IsXnuSilicon()) exit(0); - /* TODO(mrdomino): argv[0] tests only pass on XnuSilicon right now because - __sys_execve fails there, so the ape loader is used. - the correct check is "we have been invoked either as an - assimilated binary or via the ape loader, and not via a - raw __sys_execve." */ - if (strcmp(argv[3], argv[0])) { - exit(124); + if (argc >= 4) { + if (strcmp(argv[3], argv[0])) { + exit(124); + } } exit(0); } @@ -98,7 +106,8 @@ TEST(GetProgramExecutableName, ofThisFile) { TEST(GetProgramExecutableName, nullEnv) { if (skiptests) return; SPAWN(fork); - execve(self, (char *[]){self, "Child", self, self, 0}, (char *[]){0}); + execve(self, (char *[]){self, "Child", self, skiparg0 ? 0 : self, 0}, + (char *[]){0}); abort(); EXITS(0); } @@ -106,7 +115,8 @@ TEST(GetProgramExecutableName, nullEnv) { TEST(GetProramExecutableName, weirdArgv0NullEnv) { if (skiptests) return; SPAWN(fork); - execve(self, (char *[]){"hello", "Child", self, "hello", 0}, (char *[]){0}); + execve(self, (char *[]){"hello", "Child", self, skiparg0 ? 0 : "hello", 0}, + (char *[]){0}); abort(); EXITS(0); } @@ -133,11 +143,12 @@ TEST(GetProgramExecutableName, movedSelf) { ASSERT_NE(NULL, getcwd(buf, BUFSIZ - 5)); stpcpy(buf + strlen(buf), "/test"); SPAWN(fork); - execve(buf, (char *[]){"hello", "Child", buf, "hello", 0}, (char *[]){0}); + execve(buf, (char *[]){"hello", "Child", buf, skiparg0 ? 0 : "hello", 0}, + (char *[]){0}); abort(); EXITS(0); SPAWN(fork); - execve("./test", (char *[]){"hello", "Child", buf, "hello", 0}, + execve("./test", (char *[]){"hello", "Child", buf, skiparg0 ? 0 : "hello", 0}, (char *[]){0}); abort(); EXITS(0);