Enable argv[0] tests in more places (#1061)

Now we do them for assimilated binaries (except on OpenBSD or XNU
non-Silicon), for XnuSilicon, and for binaries with the preserve-
argv[0] auxv flag set. We check whether to pass the argv[0] value
at the test site rather than the Child site. We move a lot of the
test initialization into Child in the non-child case, in order to
get at the pre-init value of `__program_executable_name`. Finally,
we print out info about what we are skipping.
This commit is contained in:
Jōshin 2024-01-06 14:42:03 -05:00 committed by GitHub
parent b09096691a
commit 636bc4007b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 41 additions and 25 deletions

View file

@ -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);
}
}

View file

@ -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;
}

View file

@ -10,6 +10,8 @@
#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_

View file

@ -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,15 +84,11 @@ __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 (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);