mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-07-03 17:58:30 +00:00
Fully support OpenBSD 7.3
This change (1) upgrades to OpenBSD's newer kernel ABIs, and (2) modifies APE to have a read-only data segment. Doing this required creating APE Loader v1.1, which is backwards and forwards compatible with the previous version. If you've run the following commands in the past to install your APE Loader systemwide, then you need to run them again. Ad-hoc installations shouldn't be impacted. It's also recommended that APE binaries be remade after upgrading, since they embed old versions of the APE Loader. ape/apeuninstall.sh ape/apeinstall.sh This change does more than just fix OpenBSD. The new loader is smarter and more reliable. We're now able create much tinier ELF and Mach-O data structures than we could before. Both APE Loader and execvpe() will now normalize ambiguous argv[0] resolution the same way as the UNIX shell. Badness with TLS linkage has been solved. Fixes #826
This commit is contained in:
parent
963e10b9bf
commit
40eb3b9d5d
48 changed files with 772 additions and 903 deletions
625
ape/loader.c
625
ape/loader.c
|
@ -16,11 +16,6 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "ape/loader.h"
|
||||
|
||||
#define SET_EXE_FILE 0 /* needs root ;_; */
|
||||
#define TROUBLESHOOT 0
|
||||
#define TROUBLESHOOT_OS LINUX
|
||||
|
||||
/**
|
||||
* @fileoverview APE Loader for GNU/Systemd/XNU/FreeBSD/NetBSD/OpenBSD
|
||||
|
@ -110,7 +105,7 @@
|
|||
#define MAP_ANONYMOUS (IsLinux() ? 32 : 4096)
|
||||
#define AT_EXECFN_LINUX 31
|
||||
#define AT_EXECFN_NETBSD 2014
|
||||
#define ELFCLASS64 2
|
||||
#define ELFCLASS32 1
|
||||
#define ELFDATA2LSB 1
|
||||
#define EM_NEXGEN32E 62
|
||||
#define ET_EXEC 2
|
||||
|
@ -127,6 +122,10 @@
|
|||
#define PR_SET_MM 35
|
||||
#define PR_SET_MM_EXE_FILE 13
|
||||
|
||||
#define Min(X, Y) ((Y) > (X) ? (X) : (Y))
|
||||
#define Roundup(X, K) (((X) + (K)-1) & -(K))
|
||||
#define Rounddown(X, K) ((X) & -(K))
|
||||
|
||||
#define Read32(S) \
|
||||
((unsigned)(255 & (S)[3]) << 030 | (unsigned)(255 & (S)[2]) << 020 | \
|
||||
(unsigned)(255 & (S)[1]) << 010 | (unsigned)(255 & (S)[0]) << 000)
|
||||
|
@ -141,14 +140,6 @@
|
|||
(unsigned long)(255 & (S)[1]) << 010 | \
|
||||
(unsigned long)(255 & (S)[0]) << 000)
|
||||
|
||||
struct PathSearcher {
|
||||
char os;
|
||||
unsigned long namelen;
|
||||
const char *name;
|
||||
const char *syspath;
|
||||
char path[1024];
|
||||
};
|
||||
|
||||
struct ElfEhdr {
|
||||
unsigned char e_ident[16];
|
||||
unsigned short e_type;
|
||||
|
@ -177,26 +168,43 @@ struct ElfPhdr {
|
|||
unsigned long p_align;
|
||||
};
|
||||
|
||||
extern char ehdr[];
|
||||
extern char _end[];
|
||||
static void *syscall_;
|
||||
static struct PathSearcher ps;
|
||||
extern char __syscall_loader[];
|
||||
union ElfEhdrBuf {
|
||||
struct ElfEhdr ehdr;
|
||||
char buf[4096];
|
||||
};
|
||||
|
||||
#if SET_EXE_FILE
|
||||
static char relocated;
|
||||
#endif
|
||||
union ElfPhdrBuf {
|
||||
struct ElfPhdr phdr;
|
||||
char buf[4096];
|
||||
};
|
||||
|
||||
struct PathSearcher {
|
||||
int os;
|
||||
const char *name;
|
||||
const char *syspath;
|
||||
unsigned long namelen;
|
||||
char path[1024];
|
||||
};
|
||||
|
||||
struct ApeLoader {
|
||||
union ElfEhdrBuf ehdr;
|
||||
union ElfPhdrBuf phdr;
|
||||
struct PathSearcher ps;
|
||||
};
|
||||
|
||||
long SystemCall(long arg1, //
|
||||
long arg2, //
|
||||
long arg3, //
|
||||
long arg4, //
|
||||
long arg5, //
|
||||
long arg6, //
|
||||
long arg7, //
|
||||
long magi);
|
||||
|
||||
static int ToLower(int c) {
|
||||
return 'A' <= c && c <= 'Z' ? c + ('a' - 'A') : c;
|
||||
}
|
||||
|
||||
char *MemCpy(char *d, const char *s, unsigned long n) {
|
||||
unsigned long i = 0;
|
||||
for (; i < n; ++i) d[i] = s[i];
|
||||
return d + n;
|
||||
}
|
||||
|
||||
static unsigned long StrLen(const char *s) {
|
||||
unsigned long n = 0;
|
||||
while (*s++) ++n;
|
||||
|
@ -212,6 +220,22 @@ static const char *MemChr(const char *s, unsigned char c, unsigned long n) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void *MemMove(void *a, const void *b, unsigned long n) {
|
||||
char *d = a;
|
||||
unsigned long i;
|
||||
const char *s = b;
|
||||
if (d > s) {
|
||||
for (i = n; i--;) {
|
||||
d[i] = s[i];
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < n; ++i) {
|
||||
d[i] = s[i];
|
||||
}
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
static char *GetEnv(char **p, const char *s) {
|
||||
unsigned long i, j;
|
||||
if (p) {
|
||||
|
@ -256,168 +280,105 @@ static char *Itoa(char p[21], long x) {
|
|||
return Utoa(p, x);
|
||||
}
|
||||
|
||||
#if TROUBLESHOOT
|
||||
const char *DescribeOs(int os) {
|
||||
if (IsLinux()) {
|
||||
return "GNU/SYSTEMD";
|
||||
} else if (IsXnu()) {
|
||||
return "XNU";
|
||||
} else if (IsFreebsd()) {
|
||||
return "FREEBSD";
|
||||
} else if (IsOpenbsd()) {
|
||||
return "OPENBSD";
|
||||
} else if (IsNetbsd()) {
|
||||
return "NETBSD";
|
||||
} else {
|
||||
return "WUT";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
__attribute__((__noreturn__)) static void Exit(int rc, int os) {
|
||||
asm volatile("call\t*%2"
|
||||
: /* no outputs */
|
||||
: "a"((IsLinux() ? 60 : 1) | (IsXnu() ? 0x2000000 : 0)), "D"(rc),
|
||||
"rm"(syscall_)
|
||||
: "memory");
|
||||
SystemCall(rc, 0, 0, 0, 0, 0, 0,
|
||||
(IsLinux() ? 60 : 1) | (IsXnu() ? 0x2000000 : 0));
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
static void Close(int fd, int os) {
|
||||
int ax, di;
|
||||
asm volatile("call\t*%4"
|
||||
: "=a"(ax), "=D"(di)
|
||||
: "0"((IsLinux() ? 3 : 6) | (IsXnu() ? 0x2000000 : 0)), "1"(fd),
|
||||
"rm"(syscall_)
|
||||
: "rcx", "rdx", "rsi", "r8", "r9", "r10", "r11", "memory", "cc");
|
||||
static int Close(int fd, int os) {
|
||||
return SystemCall(fd, 0, 0, 0, 0, 0, 0,
|
||||
(IsLinux() ? 3 : 6) | (IsXnu() ? 0x2000000 : 0));
|
||||
}
|
||||
|
||||
static int Read(int fd, void *data, int size, int os) {
|
||||
long si;
|
||||
int ax, di, dx;
|
||||
asm volatile("call\t*%8"
|
||||
: "=a"(ax), "=D"(di), "=S"(si), "=d"(dx)
|
||||
: "0"((IsLinux() ? 0 : 3) | (IsXnu() ? 0x2000000 : 0)), "1"(fd),
|
||||
"2"(data), "3"(size), "rm"(syscall_)
|
||||
: "rcx", "r8", "r9", "r10", "r11", "memory");
|
||||
return ax;
|
||||
static long Pread(int fd, void *data, unsigned long size, long off, int os) {
|
||||
long magi;
|
||||
if (IsLinux()) {
|
||||
magi = 0x011;
|
||||
} else if (IsXnu()) {
|
||||
magi = 0x2000099;
|
||||
} else if (IsFreebsd()) {
|
||||
magi = 0x1db;
|
||||
} else if (IsOpenbsd()) {
|
||||
magi = 0x0a9; // OpenBSD v7.3+
|
||||
} else if (IsNetbsd()) {
|
||||
magi = 0x0ad;
|
||||
} else {
|
||||
__builtin_unreachable();
|
||||
}
|
||||
return SystemCall(fd, (long)data, size, off, off, 0, 0, magi);
|
||||
}
|
||||
|
||||
static void Write(int fd, const void *data, int size, int os) {
|
||||
long si;
|
||||
int ax, di, dx;
|
||||
asm volatile("call\t*%8"
|
||||
: "=a"(ax), "=D"(di), "=S"(si), "=d"(dx)
|
||||
: "0"((IsLinux() ? 1 : 4) | (IsXnu() ? 0x2000000 : 0)), "1"(fd),
|
||||
"2"(data), "3"(size), "rm"(syscall_)
|
||||
: "rcx", "r8", "r9", "r10", "r11", "memory", "cc");
|
||||
static long Write(int fd, const void *data, unsigned long size, int os) {
|
||||
return SystemCall(fd, (long)data, size, 0, 0, 0, 0,
|
||||
(IsLinux() ? 1 : 4) | (IsXnu() ? 0x2000000 : 0));
|
||||
}
|
||||
|
||||
static void Execve(const char *prog, char **argv, char **envp, int os) {
|
||||
long ax, di, si, dx;
|
||||
asm volatile("call\t*%8"
|
||||
: "=a"(ax), "=D"(di), "=S"(si), "=d"(dx)
|
||||
: "0"(59 | (IsXnu() ? 0x2000000 : 0)), "1"(prog), "2"(argv),
|
||||
"3"(envp), "rm"(syscall_)
|
||||
: "rcx", "r8", "r9", "r10", "r11", "memory", "cc");
|
||||
static int Execve(const char *prog, char **argv, char **envp, int os) {
|
||||
return SystemCall((long)prog, (long)argv, (long)envp, 0, 0, 0, 0,
|
||||
59 | (IsXnu() ? 0x2000000 : 0));
|
||||
}
|
||||
|
||||
static int Access(const char *path, int mode, int os) {
|
||||
int ax, si;
|
||||
long dx, di;
|
||||
asm volatile("call\t*%7"
|
||||
: "=a"(ax), "=D"(di), "=S"(si), "=d"(dx)
|
||||
: "0"((IsLinux() ? 21 : 33) | (IsXnu() ? 0x2000000 : 0)),
|
||||
"1"(path), "2"(mode), "rm"(syscall_)
|
||||
: "rcx", "r8", "r9", "r10", "r11", "memory", "cc");
|
||||
return ax;
|
||||
return SystemCall((long)path, mode, 0, 0, 0, 0, 0,
|
||||
(IsLinux() ? 21 : 33) | (IsXnu() ? 0x2000000 : 0));
|
||||
}
|
||||
|
||||
static int Msyscall(long p, long n, int os) {
|
||||
int ax;
|
||||
long di, si;
|
||||
if (!IsOpenbsd()) {
|
||||
return 0;
|
||||
static int Msyscall(long p, unsigned long n, int os) {
|
||||
if (IsOpenbsd()) {
|
||||
return SystemCall(p, n, 0, 0, 0, 0, 0, 37);
|
||||
} else {
|
||||
asm volatile("call\t*%6"
|
||||
: "=a"(ax), "=D"(di), "=S"(si)
|
||||
: "0"(37), "1"(p), "2"(n), "rm"(syscall_)
|
||||
: "rcx", "rdx", "r8", "r9", "r10", "r11", "memory", "cc");
|
||||
return ax;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int Open(const char *path, int flags, int mode, int os) {
|
||||
long di;
|
||||
int ax, dx, si;
|
||||
asm volatile("call\t*%8"
|
||||
: "=a"(ax), "=D"(di), "=S"(si), "=d"(dx)
|
||||
: "0"((IsLinux() ? 2 : 5) | (IsXnu() ? 0x2000000 : 0)),
|
||||
"1"(path), "2"(flags), "3"(mode), "rm"(syscall_)
|
||||
: "rcx", "r8", "r9", "r10", "r11", "memory", "cc");
|
||||
return ax;
|
||||
return SystemCall((long)path, flags, mode, 0, 0, 0, 0,
|
||||
(IsLinux() ? 2 : 5) | (IsXnu() ? 0x2000000 : 0));
|
||||
}
|
||||
|
||||
__attribute__((__noinline__)) static long Mmap(long addr, long size, int prot,
|
||||
int flags, int fd, long off,
|
||||
int os) {
|
||||
long ax, di, si, dx;
|
||||
register int flags_ asm("r10") = flags;
|
||||
register int fd_ asm("r8") = fd;
|
||||
register long off_ asm("r9") = off;
|
||||
asm volatile("push\t%%r9\n\t"
|
||||
"push\t%%r9\n\t"
|
||||
"call\t*%7\n\t"
|
||||
"pop\t%%r9\n\t"
|
||||
"pop\t%%r9"
|
||||
: "=a"(ax), "=D"(di), "=S"(si), "=d"(dx), "+r"(flags_),
|
||||
"+r"(fd_), "+r"(off_)
|
||||
: "rm"(syscall_),
|
||||
"0"((IsLinux() ? 9
|
||||
: IsFreebsd() ? 477
|
||||
: 197) |
|
||||
(IsXnu() ? 0x2000000 : 0)),
|
||||
"1"(addr), "2"(size), "3"(prot)
|
||||
: "rcx", "r11", "memory", "cc");
|
||||
return ax;
|
||||
}
|
||||
|
||||
int MunmapLinux(const void *addr, unsigned long size) {
|
||||
int ax;
|
||||
asm volatile("syscall"
|
||||
: "=a"(ax)
|
||||
: "0"(11), "D"(addr), "S"(size)
|
||||
: "rcx", "r11", "memory");
|
||||
return ax;
|
||||
}
|
||||
|
||||
int PrctlLinux(int op, long a, long b, long c, long d) {
|
||||
int rc;
|
||||
asm volatile("mov\t%5,%%r10\n\t"
|
||||
"mov\t%6,%%r8\n\t"
|
||||
"syscall"
|
||||
: "=a"(rc)
|
||||
: "0"(157), "D"(op), "S"(a), "d"(b), "g"(c), "g"(d)
|
||||
: "rcx", "r8", "r10", "r11", "memory");
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void Emit(int os, const char *s) {
|
||||
Write(2, s, StrLen(s), os);
|
||||
}
|
||||
|
||||
static void Perror(int os, const char *c, int rc, const char *s) {
|
||||
char ibuf[21];
|
||||
Emit(os, "ape error: ");
|
||||
Emit(os, c);
|
||||
Emit(os, ": ");
|
||||
Emit(os, s);
|
||||
if (rc) {
|
||||
Emit(os, " failed errno=");
|
||||
Itoa(ibuf, -rc);
|
||||
Emit(os, ibuf);
|
||||
static long Mmap(void *addr, unsigned long size, int prot, int flags, int fd,
|
||||
long off, int os) {
|
||||
long magi;
|
||||
if (IsLinux()) {
|
||||
magi = 9;
|
||||
} else if (IsXnu()) {
|
||||
magi = 0x2000000 | 197;
|
||||
} else if (IsFreebsd()) {
|
||||
magi = 477;
|
||||
} else if (IsOpenbsd()) {
|
||||
magi = 49; // OpenBSD v7.3+
|
||||
} else if (IsNetbsd()) {
|
||||
magi = 197;
|
||||
} else {
|
||||
__builtin_unreachable();
|
||||
}
|
||||
Emit(os, "\n");
|
||||
return SystemCall((long)addr, size, prot, flags, fd, off, off, magi);
|
||||
}
|
||||
|
||||
static long Print(int os, int fd, const char *s, ...) {
|
||||
int c;
|
||||
unsigned n;
|
||||
char b[512];
|
||||
__builtin_va_list va;
|
||||
__builtin_va_start(va, s);
|
||||
for (n = 0; s; s = __builtin_va_arg(va, const char *)) {
|
||||
while ((c = *s++)) {
|
||||
if (n < sizeof(b)) {
|
||||
b[n++] = c;
|
||||
}
|
||||
}
|
||||
}
|
||||
__builtin_va_end(va);
|
||||
return Write(fd, b, n, os);
|
||||
}
|
||||
|
||||
static void Perror(int os, const char *thing, long rc, const char *reason) {
|
||||
char ibuf[21];
|
||||
ibuf[0] = 0;
|
||||
if (rc) Itoa(ibuf, -rc);
|
||||
Print(os, 2, "ape error: ", thing, ": ", reason,
|
||||
rc ? " failed w/ errno " : "", ibuf, "\n", 0l);
|
||||
}
|
||||
|
||||
__attribute__((__noreturn__)) static void Pexit(int os, const char *c, int rc,
|
||||
|
@ -432,6 +393,15 @@ static int StrCmp(const char *l, const char *r) {
|
|||
return (l[i] & 255) - (r[i] & 255);
|
||||
}
|
||||
|
||||
static void *MemSet(void *a, int c, unsigned long n) {
|
||||
char *d = a;
|
||||
unsigned long i;
|
||||
for (i = 0; i < n; ++i) {
|
||||
d[i] = c;
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
static char EndsWithIgnoreCase(const char *p, unsigned long n, const char *s) {
|
||||
unsigned long i, m;
|
||||
if (n >= (m = StrLen(s))) {
|
||||
|
@ -458,8 +428,8 @@ static char AccessCommand(struct PathSearcher *ps, const char *suffix,
|
|||
suffixlen = StrLen(suffix);
|
||||
if (pathlen + 1 + ps->namelen + suffixlen + 1 > sizeof(ps->path)) return 0;
|
||||
if (pathlen && ps->path[pathlen - 1] != '/') ps->path[pathlen++] = '/';
|
||||
MemCpy(ps->path + pathlen, ps->name, ps->namelen);
|
||||
MemCpy(ps->path + pathlen + ps->namelen, suffix, suffixlen + 1);
|
||||
MemMove(ps->path + pathlen, ps->name, ps->namelen);
|
||||
MemMove(ps->path + pathlen + ps->namelen, suffix, suffixlen + 1);
|
||||
return !Access(ps->path, X_OK, ps->os);
|
||||
}
|
||||
|
||||
|
@ -507,37 +477,30 @@ 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, char *page,
|
||||
struct ElfEhdr *e) {
|
||||
long *sp, struct ElfEhdr *e,
|
||||
struct ElfPhdr *p) {
|
||||
long rc;
|
||||
unsigned long i;
|
||||
int prot, flags;
|
||||
struct ElfPhdr *p;
|
||||
long code, codesize;
|
||||
if (e->e_type != ET_EXEC) {
|
||||
Pexit(os, exe, 0, "ELF e_type != ET_EXEC");
|
||||
}
|
||||
if (e->e_machine != EM_NEXGEN32E) {
|
||||
Pexit(os, exe, 0, "ELF e_machine != EM_NEXGEN32E");
|
||||
}
|
||||
if (e->e_ident[EI_CLASS] != ELFCLASS64) {
|
||||
Pexit(os, exe, 0, "ELF e_ident[EI_CLASS] != ELFCLASS64");
|
||||
}
|
||||
if (e->e_ident[EI_DATA] != ELFDATA2LSB) {
|
||||
Pexit(os, exe, 0, "ELF e_ident[EI_DATA] != ELFDATA2LSB");
|
||||
}
|
||||
if (e->e_phoff + e->e_phnum * sizeof(*p) > 0x1000) {
|
||||
Pexit(os, exe, 0, "ELF phdrs need to be in first page");
|
||||
}
|
||||
unsigned long a, b, i;
|
||||
|
||||
code = 0;
|
||||
codesize = 0;
|
||||
for (p = (struct ElfPhdr *)(page + e->e_phoff), i = e->e_phnum; i--;) {
|
||||
for (i = e->e_phnum; i--;) {
|
||||
if (p[i].p_type == PT_DYNAMIC) {
|
||||
Pexit(os, exe, 0, "not a real executable");
|
||||
Pexit(os, exe, 0, "not a static executable");
|
||||
}
|
||||
if (p[i].p_type != PT_LOAD) continue;
|
||||
if ((p[i].p_vaddr | p[i].p_filesz | p[i].p_memsz | p[i].p_offset) & 0xfff) {
|
||||
Pexit(os, exe, 0, "APE phdrs must be 4096-aligned and 4096-padded");
|
||||
if (p[i].p_type != PT_LOAD) {
|
||||
continue;
|
||||
}
|
||||
if (!p[i].p_memsz) {
|
||||
continue;
|
||||
}
|
||||
if (p[i].p_vaddr & 4095) {
|
||||
Pexit(os, exe, 0, "APE phdr addr must be 4096-aligned");
|
||||
}
|
||||
if (p[i].p_offset & 4095) {
|
||||
Pexit(os, exe, 0, "APE phdr offset must be 4096-aligned");
|
||||
}
|
||||
prot = 0;
|
||||
flags = MAP_FIXED | MAP_PRIVATE;
|
||||
|
@ -552,16 +515,20 @@ __attribute__((__noreturn__)) static void Spawn(int os, const char *exe, int fd,
|
|||
code = p[i].p_vaddr;
|
||||
codesize = p[i].p_filesz;
|
||||
}
|
||||
if (p[i].p_memsz > p[i].p_filesz) {
|
||||
if ((rc = Mmap(p[i].p_vaddr + p[i].p_filesz, p[i].p_memsz - p[i].p_filesz,
|
||||
prot, flags | MAP_ANONYMOUS, -1, 0, os)) < 0) {
|
||||
Pexit(os, exe, rc, "bss mmap()");
|
||||
if (p[i].p_filesz) {
|
||||
if ((rc = Mmap((void *)p[i].p_vaddr, p[i].p_filesz, prot, flags, fd,
|
||||
p[i].p_offset, os)) < 0) {
|
||||
Pexit(os, exe, rc, "image mmap");
|
||||
}
|
||||
if ((a = Min(-p[i].p_filesz & 4095, p[i].p_memsz - p[i].p_filesz))) {
|
||||
MemSet((void *)(p[i].p_vaddr + p[i].p_filesz), 0, a);
|
||||
}
|
||||
}
|
||||
if (p[i].p_filesz) {
|
||||
if ((rc = Mmap(p[i].p_vaddr, p[i].p_filesz, prot, flags, fd,
|
||||
p[i].p_offset, os)) < 0) {
|
||||
Pexit(os, exe, rc, "image mmap()");
|
||||
if ((b = Roundup(p[i].p_memsz, 4096)) >
|
||||
(a = Roundup(p[i].p_filesz, 4096))) {
|
||||
if ((rc = Mmap((void *)p[i].p_vaddr + a, b - a, prot,
|
||||
flags | MAP_ANONYMOUS, -1, 0, os)) < 0) {
|
||||
Pexit(os, exe, rc, "bss mmap");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -569,34 +536,15 @@ __attribute__((__noreturn__)) static void Spawn(int os, const char *exe, int fd,
|
|||
Pexit(os, exe, 0, "ELF needs PT_LOAD phdr w/ PF_X");
|
||||
}
|
||||
|
||||
#if SET_EXE_FILE
|
||||
// change /proc/pid/exe to new executable path
|
||||
if (IsLinux() && relocated) {
|
||||
MunmapLinux((char *)0x200000, (long)(_end - ehdr));
|
||||
PrctlLinux(PR_SET_MM, PR_SET_MM_EXE_FILE, fd, 0, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
Close(fd, os);
|
||||
|
||||
// authorize only the loaded program to issue system calls. if this
|
||||
// fails, then we pass a link to our syscall function to the program
|
||||
// since it probably means a userspace program executed this loader
|
||||
// and passed us a custom syscall function earlier.
|
||||
if (Msyscall(code, codesize, os) != -1) {
|
||||
syscall_ = 0;
|
||||
}
|
||||
|
||||
#if TROUBLESHOOT
|
||||
Emit(TROUBLESHOOT_OS, "preparing to jump\n");
|
||||
#endif
|
||||
Msyscall(code, codesize, os);
|
||||
|
||||
// we clear all the general registers we can to have some wiggle room
|
||||
// to extend the behavior of this loader in the future. we don't need
|
||||
// to clear the xmm registers since the ape loader should be compiled
|
||||
// with the -mgeneral-regs-only flag.
|
||||
register void *r8 asm("r8") = syscall_;
|
||||
asm volatile("xor\t%%eax,%%eax\n\t"
|
||||
"xor\t%%r8d,%%r8d\n\t"
|
||||
"xor\t%%r9d,%%r9d\n\t"
|
||||
"xor\t%%r10d,%%r10d\n\t"
|
||||
"xor\t%%r11d,%%r11d\n\t"
|
||||
|
@ -612,28 +560,36 @@ __attribute__((__noreturn__)) static void Spawn(int os, const char *exe, int fd,
|
|||
"xor\t%%ebp,%%ebp\n\t"
|
||||
"ret"
|
||||
: /* no outputs */
|
||||
: "D"(IsFreebsd() ? sp : 0), "S"(e->e_entry), "d"(sp), "c"(os),
|
||||
"r"(r8)
|
||||
: "D"(IsFreebsd() ? sp : 0), "S"(e->e_entry), "d"(sp), "c"(os)
|
||||
: "memory");
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
__attribute__((__noreturn__)) void ApeLoader(long di, long *sp, char dl,
|
||||
struct ApeLoader *handoff) {
|
||||
static void TryElf(struct ApeLoader *M, const char *exe, int fd, long *sp,
|
||||
int os) {
|
||||
unsigned size;
|
||||
if (Read32(M->ehdr.buf) == Read32("\177ELF") && //
|
||||
M->ehdr.ehdr.e_type == ET_EXEC && //
|
||||
M->ehdr.ehdr.e_machine == EM_NEXGEN32E && //
|
||||
M->ehdr.ehdr.e_ident[EI_CLASS] != ELFCLASS32 && //
|
||||
M->ehdr.ehdr.e_phentsize >= sizeof(M->phdr.phdr) && //
|
||||
(size = (unsigned)M->ehdr.ehdr.e_phnum * M->ehdr.ehdr.e_phentsize) <=
|
||||
sizeof(M->phdr.buf) &&
|
||||
Pread(fd, M->phdr.buf, size, M->ehdr.ehdr.e_phoff, os) == size) {
|
||||
Spawn(os, exe, fd, sp, &M->ehdr.ehdr, &M->phdr.phdr);
|
||||
}
|
||||
}
|
||||
|
||||
__attribute__((__noreturn__)) void ApeLoader(long di, long *sp, char dl) {
|
||||
int rc;
|
||||
long *auxv;
|
||||
struct ElfEhdr *eh;
|
||||
int c, i, fd, os, argc;
|
||||
char *p, *exe, *prog, **argv, **envp, *page;
|
||||
static union {
|
||||
struct ElfEhdr ehdr;
|
||||
char p[0x1000];
|
||||
} u;
|
||||
unsigned i, n;
|
||||
int c, fd, os, argc;
|
||||
struct ApeLoader *M;
|
||||
long *auxv, *ap, *ew;
|
||||
char *p, *exe, *prog, **argv, **envp;
|
||||
|
||||
// detect freebsd
|
||||
if (handoff) {
|
||||
os = handoff->os;
|
||||
} else if (SupportsXnu() && dl == XNU) {
|
||||
if (SupportsXnu() && dl == XNU) {
|
||||
os = XNU;
|
||||
} else if (SupportsFreebsd() && di) {
|
||||
os = FREEBSD;
|
||||
|
@ -653,134 +609,109 @@ __attribute__((__noreturn__)) void ApeLoader(long di, long *sp, char dl,
|
|||
}
|
||||
}
|
||||
|
||||
// get syscall function pointer
|
||||
if (handoff && handoff->systemcall) {
|
||||
syscall_ = handoff->systemcall;
|
||||
// detect openbsd
|
||||
if (SupportsOpenbsd() && !os && !auxv[0]) {
|
||||
os = OPENBSD;
|
||||
}
|
||||
|
||||
// detect netbsd and find end of words
|
||||
for (ap = auxv; ap[0]; ap += 2) {
|
||||
if (SupportsNetbsd() && !os && ap[0] == AT_EXECFN_NETBSD) {
|
||||
os = NETBSD;
|
||||
}
|
||||
}
|
||||
ew = ap + 1;
|
||||
|
||||
// allocate loader memory
|
||||
n = sizeof(*M) / sizeof(long);
|
||||
MemMove(sp - n, sp, (char *)ew - (char *)sp);
|
||||
sp -= n, argv -= n, envp -= n, auxv -= n;
|
||||
M = (struct ApeLoader *)(ew - n);
|
||||
|
||||
// default operating system
|
||||
if (!os) {
|
||||
os = LINUX;
|
||||
}
|
||||
|
||||
// we can load via shell, shebang, or binfmt_misc
|
||||
if (argc >= 3 && !StrCmp(argv[1], "-")) {
|
||||
// if the first argument is a hyphen then we give the user the
|
||||
// power to change argv[0] or omit it entirely. most operating
|
||||
// systems don't permit the omission of argv[0] but we do, b/c
|
||||
// it's specified by ANSI X3.159-1988.
|
||||
prog = (char *)sp[3];
|
||||
argc = sp[3] = sp[0] - 3;
|
||||
argv = (char **)((sp += 3) + 1);
|
||||
} else if (argc < 2) {
|
||||
Print(os, 2,
|
||||
"usage: ape PROG [ARGV1,ARGV2,...]\n"
|
||||
" ape - PROG [ARGV0,ARGV1,...]\n"
|
||||
"αcτµαlly pδrταblε εxεcµταblε loader v1.1\n"
|
||||
"copyright 2022 justine alexandra roberts tunney\n"
|
||||
"https://justine.lol/ape.html\n",
|
||||
0l);
|
||||
Exit(1, os);
|
||||
} else {
|
||||
syscall_ = __syscall_loader;
|
||||
prog = (char *)sp[2];
|
||||
argc = sp[1] = sp[0] - 1;
|
||||
argv = (char **)((sp += 1) + 1);
|
||||
}
|
||||
|
||||
if (handoff) {
|
||||
// we were called by ape_execve()
|
||||
// no argument parsing is needed
|
||||
// no path searching is needed
|
||||
exe = handoff->prog;
|
||||
fd = handoff->fd;
|
||||
exe = handoff->prog;
|
||||
page = handoff->page;
|
||||
eh = (struct ElfEhdr *)handoff->page;
|
||||
} else {
|
||||
|
||||
// detect openbsd
|
||||
if (SupportsOpenbsd() && !os && !auxv[0]) {
|
||||
os = OPENBSD;
|
||||
}
|
||||
|
||||
// detect netbsd
|
||||
if (SupportsNetbsd() && !os) {
|
||||
for (; auxv[0]; auxv += 2) {
|
||||
if (auxv[0] == AT_EXECFN_NETBSD) {
|
||||
os = NETBSD;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// default operating system
|
||||
if (!os) {
|
||||
os = LINUX;
|
||||
}
|
||||
|
||||
#if SET_EXE_FILE
|
||||
if (IsLinux() && !relocated) {
|
||||
char *b1 = (char *)0x200000;
|
||||
char *b2 = (char *)0x300000;
|
||||
void (*pApeLoader)(long, long *, char, struct ApeLoader *);
|
||||
Mmap((long)b2, (long)(_end - ehdr), PROT_READ | PROT_WRITE | PROT_EXEC,
|
||||
MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0, os);
|
||||
relocated = 1;
|
||||
MemCpy(b2, b1, (long)(_end - ehdr));
|
||||
pApeLoader = (void *)((char *)&ApeLoader - b1 + b2);
|
||||
pApeLoader(di, sp, dl, handoff);
|
||||
}
|
||||
#endif
|
||||
|
||||
// we can load via shell, shebang, or binfmt_misc
|
||||
if (argc >= 3 && !StrCmp(argv[1], "-")) {
|
||||
// if the first argument is a hyphen then we give the user the
|
||||
// power to change argv[0] or omit it entirely. most operating
|
||||
// systems don't permit the omission of argv[0] but we do, b/c
|
||||
// it's specified by ANSI X3.159-1988.
|
||||
prog = (char *)sp[3];
|
||||
argc = sp[3] = sp[0] - 3;
|
||||
argv = (char **)((sp += 3) + 1);
|
||||
} else if (argc < 2) {
|
||||
Emit(os, "usage: ape PROG [ARGV1,ARGV2,...]\n"
|
||||
" ape - PROG [ARGV0,ARGV1,...]\n"
|
||||
"αcτµαlly pδrταblε εxεcµταblε loader v1.o\n"
|
||||
"copyright 2022 justine alexandra roberts tunney\n"
|
||||
"https://justine.lol/ape.html\n");
|
||||
Exit(1, os);
|
||||
} else {
|
||||
prog = (char *)sp[2];
|
||||
argc = sp[1] = sp[0] - 1;
|
||||
argv = (char **)((sp += 1) + 1);
|
||||
}
|
||||
|
||||
if (!(exe = Commandv(&ps, os, prog, GetEnv(envp, "PATH")))) {
|
||||
Pexit(os, prog, 0, "not found (maybe chmod +x)");
|
||||
} else if ((fd = Open(exe, O_RDONLY, 0, os)) < 0) {
|
||||
Pexit(os, exe, fd, "open");
|
||||
} else if ((rc = Read(fd, u.p, sizeof(u.p), os)) < 0) {
|
||||
Pexit(os, exe, rc, "read");
|
||||
} else if (rc != sizeof(u.p) && Read32(u.p) != Read32("\177ELF")) {
|
||||
Pexit(os, exe, 0, "too small");
|
||||
}
|
||||
|
||||
page = u.p;
|
||||
eh = &u.ehdr;
|
||||
// resolve path of executable and read its first page
|
||||
if (!(exe = Commandv(&M->ps, os, prog, GetEnv(envp, "PATH")))) {
|
||||
Pexit(os, prog, 0, "not found (maybe chmod +x)");
|
||||
} else if ((fd = Open(exe, O_RDONLY, 0, os)) < 0) {
|
||||
Pexit(os, exe, fd, "open");
|
||||
} else if ((rc = Pread(fd, M->ehdr.buf, sizeof(M->ehdr.buf), 0, os)) < 0) {
|
||||
Pexit(os, exe, rc, "read");
|
||||
} else if (rc != sizeof(M->ehdr.buf)) {
|
||||
Pexit(os, exe, 0, "too small");
|
||||
}
|
||||
|
||||
#if TROUBLESHOOT
|
||||
Emit(TROUBLESHOOT_OS, "os = ");
|
||||
Emit(TROUBLESHOOT_OS, DescribeOs(os));
|
||||
Emit(TROUBLESHOOT_OS, "\n");
|
||||
for (i = 0; i < argc; ++i) {
|
||||
Emit(TROUBLESHOOT_OS, "argv = ");
|
||||
Emit(TROUBLESHOOT_OS, argv[i]);
|
||||
Emit(TROUBLESHOOT_OS, "\n");
|
||||
// change argv[0] to resolved path if it's ambiguous
|
||||
if (argc > 0 && *prog != '/' && *exe == '/' && !StrCmp(prog, argv[0])) {
|
||||
argv[0] = exe;
|
||||
}
|
||||
#endif
|
||||
|
||||
if ((IsXnu() && Read32(page) == 0xFEEDFACE + 1) ||
|
||||
(!IsXnu() && Read32(page) == Read32("\177ELF"))) {
|
||||
// ape intended behavior
|
||||
// 1. if file is a native executable, try to run it natively
|
||||
// 2. if ape, will scan shell script for elf printf statements
|
||||
// 3. shell script may have multiple lines producing elf headers
|
||||
// 4. all elf printf lines must exist in the first 4096 bytes of file
|
||||
// 5. elf program headers may appear anywhere in the binary
|
||||
if ((IsXnu() && Read32(M->ehdr.buf) == 0xFEEDFACE + 1) ||
|
||||
(!IsXnu() && Read32(M->ehdr.buf) == Read32("\177ELF"))) {
|
||||
Close(fd, os);
|
||||
Execve(exe, argv, envp, os);
|
||||
}
|
||||
|
||||
// TODO(jart): Parse Mach-O for old APE binary support on XNU.
|
||||
for (p = page; p < page + sizeof(u.p); ++p) {
|
||||
if (Read64(p) != Read64("printf '")) continue;
|
||||
for (i = 0, p += 8; p + 3 < page + sizeof(u.p) && (c = *p++) != '\'';) {
|
||||
if (c == '\\') {
|
||||
if ('0' <= *p && *p <= '7') {
|
||||
c = *p++ - '0';
|
||||
if (Read64(M->ehdr.buf) == Read64("MZqFpD='") ||
|
||||
Read64(M->ehdr.buf) == Read64("jartsr='")) {
|
||||
for (p = M->ehdr.buf; p < M->ehdr.buf + sizeof(M->ehdr.buf); ++p) {
|
||||
if (Read64(p) != Read64("printf '")) {
|
||||
continue;
|
||||
}
|
||||
for (i = 0, p += 8;
|
||||
p + 3 < M->ehdr.buf + sizeof(M->ehdr.buf) && (c = *p++) != '\'';) {
|
||||
if (c == '\\') {
|
||||
if ('0' <= *p && *p <= '7') {
|
||||
c *= 8;
|
||||
c += *p++ - '0';
|
||||
c = *p++ - '0';
|
||||
if ('0' <= *p && *p <= '7') {
|
||||
c *= 8;
|
||||
c += *p++ - '0';
|
||||
if ('0' <= *p && *p <= '7') {
|
||||
c *= 8;
|
||||
c += *p++ - '0';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
M->ehdr.buf[i++] = c;
|
||||
}
|
||||
if (i >= sizeof(M->ehdr.ehdr)) {
|
||||
TryElf(M, exe, fd, sp, os);
|
||||
}
|
||||
page[i++] = c;
|
||||
}
|
||||
if (i >= 64 && Read32(page) == Read32("\177ELF")) {
|
||||
Spawn(os, exe, fd, sp, page, eh);
|
||||
}
|
||||
}
|
||||
|
||||
Pexit(os, exe, 0, "could not find printf elf in first page");
|
||||
TryElf(M, exe, fd, sp, os);
|
||||
Pexit(os, exe, 0, "Not an acceptable APE/ELF executable for x86-64");
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue