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 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

View file

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

View file

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

View file

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