Merge pull request #1 from mrdomino/linux-ape

ape/loader.c parity
This commit is contained in:
Jōshin 2023-12-15 10:15:23 -05:00 committed by GitHub
commit 3bb08954f7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 82 additions and 44 deletions

View file

@ -31,17 +31,16 @@
// //
// @param rdi is passed through as-is // @param rdi is passed through as-is
// @param rsi is address of entrypoint (becomes zero) // @param rsi is address of entrypoint (becomes zero)
// @param rdx is stack pointer (becomes zero) // @param rdx is passed through as-is
// @param rcx is passed through as-is // @param rcx is stack pointer (becomes r8)
// @noreturn // @noreturn
Launch: Launch:
#ifdef __aarch64__ #ifdef __aarch64__
mov x16,x1 mov x16,x1
mov sp,x2 mov sp,x3
mov x1,0 mov x1,0
mov x2,0 mov x3,x4
mov x3,0
mov x4,0 mov x4,0
mov x5,0 mov x5,0
mov x6,0 mov x6,0
@ -71,6 +70,8 @@ Launch:
#else #else
mov %rcx,%rsp
mov %r8,%rcx
xor %r8d,%r8d xor %r8d,%r8d
xor %r9d,%r9d xor %r9d,%r9d
xor %r10d,%r10d xor %r10d,%r10d
@ -79,8 +80,6 @@ Launch:
xor %r13d,%r13d xor %r13d,%r13d
xor %r14d,%r14d xor %r14d,%r14d
xor %r15d,%r15d xor %r15d,%r15d
mov %rdx,%rsp
xor %edx,%edx
push %rsi push %rsi
xor %esi,%esi xor %esi,%esi
xor %ebp,%ebp xor %ebp,%ebp

View file

@ -87,6 +87,8 @@
#define MIN(X, Y) ((Y) > (X) ? (X) : (Y)) #define MIN(X, Y) ((Y) > (X) ? (X) : (Y))
#define MAX(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 SupportsLinux() (SUPPORT_VECTOR & LINUX)
#define SupportsXnu() (SUPPORT_VECTOR & XNU) #define SupportsXnu() (SUPPORT_VECTOR & XNU)
#define SupportsFreebsd() (SUPPORT_VECTOR & FREEBSD) #define SupportsFreebsd() (SUPPORT_VECTOR & FREEBSD)
@ -212,17 +214,18 @@ struct PathSearcher {
const char *name; const char *name;
const char *syspath; const char *syspath;
unsigned long namelen; unsigned long namelen;
char path[1024]; char path[PATH_MAX];
}; };
struct ApeLoader { struct ApeLoader {
union ElfPhdrBuf phdr; union ElfPhdrBuf phdr;
struct PathSearcher ps; struct PathSearcher ps;
char path[1024]; char path[PATH_MAX];
}; };
EXTERN_C long SystemCall(long, long, long, long, long, long, long, int); 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 __executable_start[];
extern char _end[]; extern char _end[];
@ -239,12 +242,13 @@ static int StrCmp(const char *l, const char *r) {
return (l[i] & 255) - (r[i] & 255); return (l[i] & 255) - (r[i] & 255);
} }
static const char *BaseName(const char *s) { #if 0
int c;
const char *b = ""; static const char *StrRChr(const char *s, int c) {
const char *b = 0;
if (s) { if (s) {
while ((c = *s++)) { for (; *s; ++s) {
if (c == '/') { if (*s == c) {
b = s; b = s;
} }
} }
@ -252,6 +256,13 @@ static const char *BaseName(const char *s) {
return b; 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) { static void Bzero(void *a, unsigned long n) {
long z; long z;
char *p, *e; char *p, *e;
@ -343,7 +354,7 @@ static char *Utox(char p[19], unsigned long x) {
return p; return p;
} }
static char *Utoa(char p[21], unsigned long x) { static char *Utoa(char p[20], unsigned long x) {
char t; char t;
unsigned long i, a, b; unsigned long i, a, b;
i = 0; i = 0;
@ -534,6 +545,40 @@ __attribute__((__noreturn__)) static void Pexit(int os, const char *c, int rc,
Exit(127, os); 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) { static char AccessCommand(struct PathSearcher *ps, unsigned long pathlen) {
if (pathlen + 1 + ps->namelen + 1 > sizeof(ps->path)) return 0; if (pathlen + 1 + ps->namelen + 1 > sizeof(ps->path)) return 0;
if (pathlen && ps->path[pathlen - 1] != '/') ps->path[pathlen++] = '/'; 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, __attribute__((__noreturn__)) static void Spawn(int os, const char *exe,
long *sp, unsigned long pagesz, char *path, int fd, long *sp,
unsigned long pagesz,
struct ElfEhdr *e, struct ElfEhdr *e,
struct ElfPhdr *p) { struct ElfPhdr *p) {
long rc; long rc;
@ -757,12 +803,12 @@ __attribute__((__noreturn__)) static void Spawn(int os, const char *exe, int fd,
Msyscall(dynbase + code, codesize, os); Msyscall(dynbase + code, codesize, os);
/* call program entrypoint */ /* 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, static const char *TryElf(struct ApeLoader *M, union ElfEhdrBuf *ebuf,
const char *exe, int fd, long *sp, long *auxv, const char *exe, char *path, int fd, long *sp,
unsigned long pagesz, int os) { long *auxv, unsigned long pagesz, int os) {
long i, rc; long i, rc;
unsigned size; unsigned size;
struct ElfEhdr *e; struct ElfEhdr *e;
@ -877,7 +923,7 @@ static const char *TryElf(struct ApeLoader *M, union ElfEhdrBuf *ebuf,
} }
/* we're now ready to load */ /* 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) { __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)"); Pexit(os, prog, 0, "not found (maybe chmod +x or ./ needed)");
} else if ((fd = Open(exe, O_RDONLY, 0, os)) < 0) { } else if ((fd = Open(exe, O_RDONLY, 0, os)) < 0) {
Pexit(os, exe, fd, "open"); 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) { } else if ((rc = Pread(fd, ebuf->buf, sizeof(ebuf->buf), 0, os)) < 0) {
Pexit(os, exe, rc, "read"); Pexit(os, exe, rc, "read");
} else if ((unsigned long)rc < sizeof(ebuf->ehdr)) { } 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; 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 /* ape intended behavior
1. if ape, will scan shell script for elf printf statements 1. if ape, will scan shell script for elf printf statements
2. shell script may have multiple lines producing elf headers 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)) { 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));
} }

View file

@ -62,6 +62,8 @@ _start:
// set operating system when already detected // set operating system when already detected
1: mov %cl,__hostos(%rip) 1: mov %cl,__hostos(%rip)
mov %rdx,__program_executable_name(%rip)
// get startup timestamp as early as possible // get startup timestamp as early as possible
// its used by --strace flag and kprintf() %T // its used by --strace flag and kprintf() %T
rdtsc rdtsc

View file

@ -28,25 +28,20 @@
#include "libc/testlib/testlib.h" #include "libc/testlib/testlib.h"
static char *self; static char *self;
static bool skipcosmotests;
void SetUp(void) { void SetUp(void) {
self = GetProgramExecutableName(); self = GetProgramExecutableName();
} }
void SetUpOnce(void) { 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(); testlib_enable_tmp_setup_teardown();
} }
__attribute__((__constructor__)) static void Child(int argc, char *argv[]) { __attribute__((__constructor__)) static void Child(int argc, char *argv[]) {
if (argc >= 2 && !strcmp(argv[1], "Child")) { if (argc >= 2 && !strcmp(argv[1], "Child")) {
ASSERT_EQ(3, argc); ASSERT_EQ(argc, 4);
EXPECT_STREQ(argv[2], GetProgramExecutableName()); EXPECT_STREQ(argv[2], GetProgramExecutableName());
EXPECT_STREQ(argv[3], argv[0]);
exit(g_testlib_failed); exit(g_testlib_failed);
} }
} }
@ -59,33 +54,33 @@ TEST(GetProgramExecutableName, ofThisFile) {
TEST(GetProgramExecutableName, nullEnv) { TEST(GetProgramExecutableName, nullEnv) {
SPAWN(fork); SPAWN(fork);
execve(self, (char *[]){self, "Child", self, 0}, (char *[]){0}); execve(self, (char *[]){self, "Child", self, self, 0}, (char *[]){0});
abort(); abort();
EXITS(0); EXITS(0);
} }
TEST(GetProramExecutableName, weirdArgv0NullEnv) { TEST(GetProramExecutableName, weirdArgv0NullEnv) {
SPAWN(fork); SPAWN(fork);
execve(self, (char *[]){"hello", "Child", self, 0}, (char *[]){0}); execve(self, (char *[]){"hello", "Child", self, "hello", 0}, (char *[]){0});
abort(); abort();
EXITS(0); EXITS(0);
} }
TEST(GetProgramExecutableName, weirdArgv0CosmoVar) { TEST(GetProgramExecutableName, weirdArgv0CosmoVar) {
if (skipcosmotests) return;
char buf[32 + PATH_MAX]; char buf[32 + PATH_MAX];
stpcpy(stpcpy(buf, "COSMOPOLITAN_PROGRAM_EXECUTABLE="), self); stpcpy(stpcpy(buf, "COSMOPOLITAN_PROGRAM_EXECUTABLE="), self);
SPAWN(fork); SPAWN(fork);
execve(self, (char *[]){"hello", "Child", self, 0}, (char *[]){buf, 0}); execve(self, (char *[]){"hello", "Child", self, "hello", 0},
(char *[]){buf, 0});
abort(); abort();
EXITS(0); EXITS(0);
} }
TEST(GetProgramExecutableName, weirdArgv0WrongCosmoVar) { TEST(GetProgramExecutableName, weirdArgv0WrongCosmoVar) {
if (skipcosmotests) return;
char *bad = "COSMOPOLITAN_PROGRAM_EXECUTABLE=hi"; char *bad = "COSMOPOLITAN_PROGRAM_EXECUTABLE=hi";
SPAWN(fork); SPAWN(fork);
execve(self, (char *[]){"hello", "Child", self, 0}, (char *[]){bad, 0}); execve(self, (char *[]){"hello", "Child", self, "hello", 0},
(char *[]){bad, 0});
abort(); abort();
EXITS(0); EXITS(0);
} }
@ -104,7 +99,7 @@ TEST(GetProgramExecutableName, movedSelf) {
ASSERT_NE(NULL, getcwd(buf, BUFSIZ - 5)); ASSERT_NE(NULL, getcwd(buf, BUFSIZ - 5));
stpcpy(buf + strlen(buf), "/test"); stpcpy(buf + strlen(buf), "/test");
SPAWN(fork); SPAWN(fork);
execve(buf, (char *[]){"hello", "Child", buf, 0}, (char *[]){0}); execve(buf, (char *[]){"hello", "Child", buf, "hello", 0}, (char *[]){0});
abort(); abort();
EXITS(0); EXITS(0);
} }