mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-02-07 06:53:33 +00:00
Mint APE Loader v1.4
This change also incorporates more bug fixes and improvements to a wide variety of small things. For example this fixes #860 so Windows console doesn't get corrupted after exit. An system stack memory map issue with aarch64 has been fixed. We no longer use O_NONBLOCK on AF_UNIX sockets. Crash reports on Arm64 will now demangle C++ symbols, even when c++filt isn't available. Most importantly the Apple M1 version of APE Loader is brought up to date by this change. A prebuilt unsigned binary for it is being included in build/bootstrap/. One more thing: retrieving the term dimensions under --strace was causing the stack to become corrupted and now that's been solved too. PSS: We're now including an ELF PT_NOTE for APE in the binaries we build, that has the APE Loader version.
This commit is contained in:
parent
53d3f9d9c5
commit
6843150e0c
25 changed files with 524 additions and 226 deletions
446
ape/ape-m1.c
446
ape/ape-m1.c
|
@ -31,6 +31,7 @@
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#define pagesz 16384
|
||||||
#define SYSLIB_MAGIC ('s' | 'l' << 8 | 'i' << 16 | 'b' << 24)
|
#define SYSLIB_MAGIC ('s' | 'l' << 8 | 'i' << 16 | 'b' << 24)
|
||||||
#define SYSLIB_VERSION 1
|
#define SYSLIB_VERSION 1
|
||||||
|
|
||||||
|
@ -57,14 +58,14 @@ struct Syslib {
|
||||||
dispatch_time_t (*dispatch_walltime)(const struct timespec *, int64_t);
|
dispatch_time_t (*dispatch_walltime)(const struct timespec *, int64_t);
|
||||||
};
|
};
|
||||||
|
|
||||||
#define TROUBLESHOOT 0
|
|
||||||
|
|
||||||
#define ELFCLASS32 1
|
#define ELFCLASS32 1
|
||||||
#define ELFDATA2LSB 1
|
#define ELFDATA2LSB 1
|
||||||
#define EM_AARCH64 183
|
#define EM_AARCH64 183
|
||||||
#define ET_EXEC 2
|
#define ET_EXEC 2
|
||||||
|
#define ET_DYN 3
|
||||||
#define PT_LOAD 1
|
#define PT_LOAD 1
|
||||||
#define PT_DYNAMIC 2
|
#define PT_DYNAMIC 2
|
||||||
|
#define PT_INTERP 3
|
||||||
#define EI_CLASS 4
|
#define EI_CLASS 4
|
||||||
#define EI_DATA 5
|
#define EI_DATA 5
|
||||||
#define PF_X 1
|
#define PF_X 1
|
||||||
|
@ -96,15 +97,14 @@ struct Syslib {
|
||||||
#define _COMM_PAGE_APRR_WRITE_ENABLE (_COMM_PAGE_START_ADDRESS + 0x110)
|
#define _COMM_PAGE_APRR_WRITE_ENABLE (_COMM_PAGE_START_ADDRESS + 0x110)
|
||||||
#define _COMM_PAGE_APRR_WRITE_DISABLE (_COMM_PAGE_START_ADDRESS + 0x118)
|
#define _COMM_PAGE_APRR_WRITE_DISABLE (_COMM_PAGE_START_ADDRESS + 0x118)
|
||||||
|
|
||||||
#define Min(X, Y) ((Y) > (X) ? (X) : (Y))
|
#define MIN(X, Y) ((Y) > (X) ? (X) : (Y))
|
||||||
#define Roundup(X, K) (((X) + (K)-1) & -(K))
|
#define MAX(X, Y) ((Y) < (X) ? (X) : (Y))
|
||||||
#define Rounddown(X, K) ((X) & -(K))
|
|
||||||
|
|
||||||
#define Read32(S) \
|
#define READ32(S) \
|
||||||
((unsigned)(255 & (S)[3]) << 030 | (unsigned)(255 & (S)[2]) << 020 | \
|
((unsigned)(255 & (S)[3]) << 030 | (unsigned)(255 & (S)[2]) << 020 | \
|
||||||
(unsigned)(255 & (S)[1]) << 010 | (unsigned)(255 & (S)[0]) << 000)
|
(unsigned)(255 & (S)[1]) << 010 | (unsigned)(255 & (S)[0]) << 000)
|
||||||
|
|
||||||
#define Read64(S) \
|
#define READ64(S) \
|
||||||
((unsigned long)(255 & (S)[7]) << 070 | \
|
((unsigned long)(255 & (S)[7]) << 070 | \
|
||||||
(unsigned long)(255 & (S)[6]) << 060 | \
|
(unsigned long)(255 & (S)[6]) << 060 | \
|
||||||
(unsigned long)(255 & (S)[5]) << 050 | \
|
(unsigned long)(255 & (S)[5]) << 050 | \
|
||||||
|
@ -144,12 +144,12 @@ struct ElfPhdr {
|
||||||
|
|
||||||
union ElfEhdrBuf {
|
union ElfEhdrBuf {
|
||||||
struct ElfEhdr ehdr;
|
struct ElfEhdr ehdr;
|
||||||
char buf[0x1000];
|
char buf[8192];
|
||||||
};
|
};
|
||||||
|
|
||||||
union ElfPhdrBuf {
|
union ElfPhdrBuf {
|
||||||
struct ElfPhdr phdr;
|
struct ElfPhdr phdr;
|
||||||
char buf[0x1000];
|
char buf[4096];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PathSearcher {
|
struct PathSearcher {
|
||||||
|
@ -190,29 +190,19 @@ static int StrCmp(const char *l, const char *r) {
|
||||||
return (l[i] & 255) - (r[i] & 255);
|
return (l[i] & 255) - (r[i] & 255);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *MemSet(void *a, int c, unsigned long n) {
|
static void Bzero(void *a, unsigned long n) {
|
||||||
char *d = a;
|
long z;
|
||||||
unsigned long i;
|
char *p, *e;
|
||||||
for (i = 0; i < n; ++i) {
|
p = (char *)a;
|
||||||
d[i] = c;
|
e = p + n;
|
||||||
|
z = 0;
|
||||||
|
while (p + sizeof(z) <= e) {
|
||||||
|
__builtin_memcpy(p, &z, sizeof(z));
|
||||||
|
p += sizeof(z);
|
||||||
}
|
}
|
||||||
return d;
|
while (p < e) {
|
||||||
}
|
*p++ = 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 const char *MemChr(const char *s, unsigned char c, unsigned long n) {
|
static const char *MemChr(const char *s, unsigned char c, unsigned long n) {
|
||||||
|
@ -224,6 +214,36 @@ static const char *MemChr(const char *s, unsigned char c, unsigned long n) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void *MemMove(void *a, const void *b, unsigned long n) {
|
||||||
|
long w;
|
||||||
|
char *d;
|
||||||
|
const char *s;
|
||||||
|
unsigned long i;
|
||||||
|
d = (char *)a;
|
||||||
|
s = (const char *)b;
|
||||||
|
if (d > s) {
|
||||||
|
while (n >= sizeof(w)) {
|
||||||
|
n -= sizeof(w);
|
||||||
|
__builtin_memcpy(&w, s + n, sizeof(n));
|
||||||
|
__builtin_memcpy(d + n, &w, sizeof(n));
|
||||||
|
}
|
||||||
|
while (n--) {
|
||||||
|
d[n] = s[n];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
i = 0;
|
||||||
|
while (i + sizeof(w) <= n) {
|
||||||
|
__builtin_memcpy(&w, s + i, sizeof(i));
|
||||||
|
__builtin_memcpy(d + i, &w, sizeof(i));
|
||||||
|
i += sizeof(w);
|
||||||
|
}
|
||||||
|
for (; i < n; ++i) {
|
||||||
|
d[i] = s[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
static char *GetEnv(char **p, const char *s) {
|
static char *GetEnv(char **p, const char *s) {
|
||||||
unsigned long i, j;
|
unsigned long i, j;
|
||||||
if (p) {
|
if (p) {
|
||||||
|
@ -272,19 +292,29 @@ static void Emit(const char *s) {
|
||||||
write(2, s, StrLen(s));
|
write(2, s, StrLen(s));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void Perror(const char *c, int failed, const char *s) {
|
static long Print(int fd, const char *s, ...) {
|
||||||
char ibuf[21];
|
int c;
|
||||||
Emit("ape error: ");
|
unsigned n;
|
||||||
Emit(c);
|
char b[512];
|
||||||
Emit(": ");
|
__builtin_va_list va;
|
||||||
Emit(s);
|
__builtin_va_start(va, s);
|
||||||
if (failed) {
|
for (n = 0; s; s = __builtin_va_arg(va, const char *)) {
|
||||||
#include <sys/random.h>
|
while ((c = *s++)) {
|
||||||
Emit(" failed errno=");
|
if (n < sizeof(b)) {
|
||||||
Itoa(ibuf, errno);
|
b[n++] = c;
|
||||||
Emit(ibuf);
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Emit("\n");
|
__builtin_va_end(va);
|
||||||
|
return write(fd, b, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Perror(const char *thing, long rc, const char *reason) {
|
||||||
|
char ibuf[21];
|
||||||
|
ibuf[0] = 0;
|
||||||
|
if (rc) Itoa(ibuf, -rc);
|
||||||
|
Print(2, "ape error: ", thing, ": ", reason, rc ? " failed w/ errno " : "",
|
||||||
|
ibuf, "\n", 0l);
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((__noreturn__)) static void Pexit(const char *c, int failed,
|
__attribute__((__noreturn__)) static void Pexit(const char *c, int failed,
|
||||||
|
@ -416,72 +446,138 @@ static void pthread_jit_write_protect_np_workaround(int enabled) {
|
||||||
pthread_jit_write_protect_np(enabled);
|
pthread_jit_write_protect_np(enabled);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Pexit("ape-m1", 0, "failed to set jit write protection");
|
Pexit("ape", 0, "failed to set jit write protection");
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((__noreturn__)) static void Spawn(const char *exe, int fd,
|
__attribute__((__noreturn__)) static void Spawn(const char *exe, int fd,
|
||||||
long *sp, struct ElfEhdr *e,
|
long *sp, struct ElfEhdr *e,
|
||||||
struct ElfPhdr *p,
|
struct ElfPhdr *p,
|
||||||
struct Syslib *lib) {
|
struct Syslib *lib) {
|
||||||
int prot, flags;
|
long rc;
|
||||||
long code, codesize;
|
int prot;
|
||||||
unsigned long a, b, i;
|
int flags;
|
||||||
|
int found_entry;
|
||||||
|
unsigned long dynbase;
|
||||||
|
unsigned long virtmin, virtmax;
|
||||||
|
unsigned long a, b, c, d, i, j;
|
||||||
|
|
||||||
code = 0;
|
/* validate elf */
|
||||||
codesize = 0;
|
found_entry = 0;
|
||||||
for (i = e->e_phnum; i--;) {
|
virtmin = virtmax = 0;
|
||||||
if (p[i].p_type == PT_DYNAMIC) {
|
for (i = 0; i < e->e_phnum; ++i) {
|
||||||
Pexit(exe, 0, "not a static executable");
|
|
||||||
}
|
|
||||||
if (p[i].p_type != PT_LOAD) {
|
if (p[i].p_type != PT_LOAD) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!p[i].p_memsz) {
|
if (p[i].p_filesz > p[i].p_memsz) {
|
||||||
continue;
|
Pexit(exe, 0, "ELF p_filesz exceeds p_memsz");
|
||||||
}
|
|
||||||
if (p[i].p_vaddr & 0x3fff) {
|
|
||||||
Pexit(exe, 0, "APE phdr addr must be 16384-aligned");
|
|
||||||
}
|
|
||||||
if (p[i].p_offset & 0x3fff) {
|
|
||||||
Pexit(exe, 0, "APE phdr offset must be 16384-aligned");
|
|
||||||
}
|
}
|
||||||
if ((p[i].p_flags & (PF_W | PF_X)) == (PF_W | PF_X)) {
|
if ((p[i].p_flags & (PF_W | PF_X)) == (PF_W | PF_X)) {
|
||||||
Pexit(exe, 0, "Apple Silicon doesn't allow RWX memory");
|
Pexit(exe, 0, "Apple Silicon doesn't allow RWX memory");
|
||||||
}
|
}
|
||||||
prot = 0;
|
if ((p[i].p_vaddr & (pagesz - 1)) != (p[i].p_offset & (pagesz - 1))) {
|
||||||
flags = MAP_FIXED | MAP_PRIVATE;
|
Pexit(exe, 0, "ELF p_vaddr incongruent w/ p_offset modulo 16384");
|
||||||
if (p[i].p_flags & PF_R) {
|
|
||||||
prot |= PROT_READ;
|
|
||||||
}
|
}
|
||||||
if (p[i].p_flags & PF_W) {
|
if (p[i].p_vaddr + p[i].p_memsz < p[i].p_vaddr ||
|
||||||
prot |= PROT_WRITE;
|
p[i].p_vaddr + p[i].p_memsz + (pagesz - 1) < p[i].p_vaddr) {
|
||||||
|
Pexit(exe, 0, "ELF p_vaddr + p_memsz overflow");
|
||||||
|
}
|
||||||
|
if (p[i].p_offset + p[i].p_filesz < p[i].p_offset ||
|
||||||
|
p[i].p_offset + p[i].p_filesz + (pagesz - 1) < p[i].p_offset) {
|
||||||
|
Pexit(exe, 0, "ELF p_offset + p_filesz overflow");
|
||||||
|
}
|
||||||
|
a = p[i].p_vaddr & -pagesz;
|
||||||
|
b = (p[i].p_vaddr + p[i].p_memsz + (pagesz - 1)) & -pagesz;
|
||||||
|
for (j = i + 1; j < e->e_phnum; ++j) {
|
||||||
|
if (p[j].p_type != PT_LOAD) continue;
|
||||||
|
c = p[j].p_vaddr & -pagesz;
|
||||||
|
d = (p[j].p_vaddr + p[j].p_memsz + (pagesz - 1)) & -pagesz;
|
||||||
|
if (MAX(a, c) < MIN(b, d)) {
|
||||||
|
Pexit(exe, 0, "ELF segments overlap each others virtual memory");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (p[i].p_flags & PF_X) {
|
if (p[i].p_flags & PF_X) {
|
||||||
prot |= PROT_EXEC;
|
if (p[i].p_vaddr <= e->e_entry &&
|
||||||
code = p[i].p_vaddr;
|
e->e_entry < p[i].p_vaddr + p[i].p_memsz) {
|
||||||
codesize = p[i].p_filesz;
|
found_entry = 1;
|
||||||
}
|
|
||||||
if (p[i].p_filesz) {
|
|
||||||
if (mmap((char *)p[i].p_vaddr, p[i].p_filesz, prot, flags, fd,
|
|
||||||
p[i].p_offset) == MAP_FAILED) {
|
|
||||||
Pexit(exe, -1, "image mmap()");
|
|
||||||
}
|
|
||||||
if ((a = Min(-p[i].p_filesz & 0x3fff, p[i].p_memsz - p[i].p_filesz))) {
|
|
||||||
MemSet((void *)(p[i].p_vaddr + p[i].p_filesz), 0, a);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ((b = Roundup(p[i].p_memsz, 0x4000)) >
|
if (p[i].p_vaddr < virtmin) {
|
||||||
(a = Roundup(p[i].p_filesz, 0x4000))) {
|
virtmin = p[i].p_vaddr;
|
||||||
if (mmap((char *)p[i].p_vaddr + a, b - a, prot, flags | MAP_ANONYMOUS, -1,
|
}
|
||||||
0) == MAP_FAILED) {
|
if (p[i].p_vaddr + p[i].p_memsz > virtmax) {
|
||||||
Pexit(exe, -1, "bss mmap()");
|
virtmax = p[i].p_vaddr + p[i].p_memsz;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!code) {
|
if (!found_entry) {
|
||||||
Pexit(exe, 0, "ELF needs PT_LOAD phdr w/ PF_X");
|
Pexit(exe, 0, "ELF entrypoint not found in PT_LOAD with PF_X");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* choose loading address for dynamic elf executables
|
||||||
|
that maintains relative distances between segments */
|
||||||
|
if (e->e_type == ET_DYN) {
|
||||||
|
rc = (long)mmap(0, virtmax - virtmin, PROT_NONE,
|
||||||
|
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||||
|
if (rc < 0) Pexit(exe, rc, "pie mmap");
|
||||||
|
dynbase = rc;
|
||||||
|
if (dynbase & (pagesz - 1)) {
|
||||||
|
Pexit(exe, 0, "OS mmap incongruent w/ AT_PAGESZ");
|
||||||
|
}
|
||||||
|
if (dynbase + virtmin < dynbase) {
|
||||||
|
Pexit(exe, 0, "ELF dynamic base overflow");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dynbase = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* load elf */
|
||||||
|
for (i = 0; i < e->e_phnum; ++i) {
|
||||||
|
if (p[i].p_type != PT_LOAD) continue;
|
||||||
|
if (!p[i].p_memsz) continue;
|
||||||
|
|
||||||
|
/* configure mapping */
|
||||||
|
prot = 0;
|
||||||
|
flags = MAP_FIXED | MAP_PRIVATE;
|
||||||
|
if (p[i].p_flags & PF_R) prot |= PROT_READ;
|
||||||
|
if (p[i].p_flags & PF_W) prot |= PROT_WRITE;
|
||||||
|
if (p[i].p_flags & PF_X) prot |= PROT_EXEC;
|
||||||
|
|
||||||
|
/* load from file */
|
||||||
|
if (p[i].p_filesz) {
|
||||||
|
void *addr;
|
||||||
|
int prot1, prot2;
|
||||||
|
unsigned long size;
|
||||||
|
prot1 = prot;
|
||||||
|
prot2 = prot;
|
||||||
|
a = p[i].p_vaddr + p[i].p_filesz; /* end of file content */
|
||||||
|
b = (a + (pagesz - 1)) & -pagesz; /* first pure bss page */
|
||||||
|
c = p[i].p_vaddr + p[i].p_memsz; /* end of segment data */
|
||||||
|
if (b > c) b = c;
|
||||||
|
if (c > b && (~prot1 & PROT_WRITE)) {
|
||||||
|
prot1 = PROT_READ | PROT_WRITE;
|
||||||
|
}
|
||||||
|
addr = (void *)(dynbase + (p[i].p_vaddr & -pagesz));
|
||||||
|
size = (p[i].p_vaddr & (pagesz - 1)) + p[i].p_filesz;
|
||||||
|
rc = (long)mmap(addr, size, prot1, flags, fd, p[i].p_offset & -pagesz);
|
||||||
|
if (rc < 0) Pexit(exe, rc, "prog mmap");
|
||||||
|
if (c > b) Bzero((void *)(dynbase + a), b - a);
|
||||||
|
if (prot2 != prot1) {
|
||||||
|
rc = mprotect(addr, size, prot2);
|
||||||
|
if (rc < 0) Pexit(exe, rc, "prog mprotect");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* allocate extra bss */
|
||||||
|
a = p[i].p_vaddr + p[i].p_filesz;
|
||||||
|
a = (a + (pagesz - 1)) & -pagesz;
|
||||||
|
b = p[i].p_vaddr + p[i].p_memsz;
|
||||||
|
if (b > a) {
|
||||||
|
flags |= MAP_ANONYMOUS;
|
||||||
|
rc = (long)mmap((void *)(dynbase + a), b - a, prot, flags, -1, 0);
|
||||||
|
if (rc < 0) Pexit(exe, rc, "bss mmap");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* finish up */
|
||||||
close(fd);
|
close(fd);
|
||||||
|
|
||||||
register long *x0 asm("x0") = sp;
|
register long *x0 asm("x0") = sp;
|
||||||
|
@ -523,39 +619,116 @@ __attribute__((__noreturn__)) static void Spawn(const char *exe, int fd,
|
||||||
__builtin_unreachable();
|
__builtin_unreachable();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void TryElf(struct ApeLoader *M, const char *exe, int fd, long *sp,
|
static const char *TryElf(struct ApeLoader *M, const char *exe, int fd,
|
||||||
long *bp, char *execfn) {
|
long *sp, long *bp, char *execfn) {
|
||||||
|
long i, rc;
|
||||||
unsigned size;
|
unsigned size;
|
||||||
if (Read32(M->ehdr.buf) == Read32("\177ELF") && //
|
struct ElfEhdr *e;
|
||||||
M->ehdr.ehdr.e_type == ET_EXEC && //
|
struct ElfPhdr *p;
|
||||||
M->ehdr.ehdr.e_machine == EM_AARCH64 && //
|
|
||||||
M->ehdr.ehdr.e_ident[EI_CLASS] != ELFCLASS32 && //
|
/* validate elf header */
|
||||||
M->ehdr.ehdr.e_phentsize >= sizeof(M->phdr.phdr) && //
|
e = &M->ehdr.ehdr;
|
||||||
(size = (unsigned)M->ehdr.ehdr.e_phnum * M->ehdr.ehdr.e_phentsize) <=
|
if (READ32(M->ehdr.buf) != READ32("\177ELF")) {
|
||||||
sizeof(M->phdr.buf) &&
|
return "didn't embed ELF magic";
|
||||||
pread(fd, M->phdr.buf, size, M->ehdr.ehdr.e_phoff) == size) {
|
|
||||||
long auxv[][2] = {
|
|
||||||
{AT_PHDR, (long)&M->phdr.phdr}, //
|
|
||||||
{AT_PHENT, M->ehdr.ehdr.e_phentsize}, //
|
|
||||||
{AT_PHNUM, M->ehdr.ehdr.e_phnum}, //
|
|
||||||
{AT_ENTRY, M->ehdr.ehdr.e_entry}, //
|
|
||||||
{AT_PAGESZ, 0x4000}, //
|
|
||||||
{AT_UID, getuid()}, //
|
|
||||||
{AT_EUID, geteuid()}, //
|
|
||||||
{AT_GID, getgid()}, //
|
|
||||||
{AT_EGID, getegid()}, //
|
|
||||||
{AT_HWCAP, 0xffb3ffffu}, //
|
|
||||||
{AT_HWCAP2, 0x181}, //
|
|
||||||
{AT_SECURE, issetugid()}, //
|
|
||||||
{AT_RANDOM, (long)M->rando}, //
|
|
||||||
{AT_EXECFN, (long)execfn}, //
|
|
||||||
{0, 0}, //
|
|
||||||
};
|
|
||||||
_Static_assert(sizeof(auxv) == AUXV_BYTES,
|
|
||||||
"Please update the AUXV_BYTES constant");
|
|
||||||
MemMove(bp, auxv, sizeof(auxv));
|
|
||||||
Spawn(exe, fd, sp, &M->ehdr.ehdr, &M->phdr.phdr, &M->lib);
|
|
||||||
}
|
}
|
||||||
|
if (e->e_ident[EI_CLASS] == ELFCLASS32) {
|
||||||
|
return "32-bit ELF isn't supported";
|
||||||
|
}
|
||||||
|
if (e->e_type != ET_EXEC && e->e_type != ET_DYN) {
|
||||||
|
return "ELF not ET_EXEC or ET_DYN";
|
||||||
|
}
|
||||||
|
if (e->e_machine != EM_AARCH64) {
|
||||||
|
return "couldn't find ELF header with ARM64 machine type";
|
||||||
|
}
|
||||||
|
if (e->e_phentsize != sizeof(struct ElfPhdr)) {
|
||||||
|
Pexit(exe, 0, "e_phentsize is wrong");
|
||||||
|
}
|
||||||
|
size = e->e_phnum;
|
||||||
|
if ((size *= sizeof(struct ElfPhdr)) > sizeof(M->phdr.buf)) {
|
||||||
|
Pexit(exe, 0, "too many ELF program headers");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* read program headers */
|
||||||
|
rc = pread(fd, M->phdr.buf, size, M->ehdr.ehdr.e_phoff);
|
||||||
|
if (rc < 0) return "failed to read ELF program headers";
|
||||||
|
if (rc != size) return "truncated read of ELF program headers";
|
||||||
|
|
||||||
|
/* bail on recoverable program header errors */
|
||||||
|
p = &M->phdr.phdr;
|
||||||
|
for (i = 0; i < e->e_phnum; ++i) {
|
||||||
|
if (p[i].p_type == PT_INTERP) {
|
||||||
|
return "ELF has PT_INTERP which isn't supported";
|
||||||
|
}
|
||||||
|
if (p[i].p_type == PT_DYNAMIC) {
|
||||||
|
return "ELF has PT_DYNAMIC which isn't supported";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* remove empty program headers */
|
||||||
|
for (i = 0; i < e->e_phnum;) {
|
||||||
|
if (p[i].p_type == PT_LOAD && !p[i].p_memsz) {
|
||||||
|
if (i + 1 < e->e_phnum) {
|
||||||
|
MemMove(p + i, p + i + 1,
|
||||||
|
(e->e_phnum - (i + 1)) * sizeof(struct ElfPhdr));
|
||||||
|
}
|
||||||
|
--e->e_phnum;
|
||||||
|
} else {
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* merge adjacent loads that are contiguous with equal protection,
|
||||||
|
* which prevents our program header overlap check from needlessly
|
||||||
|
* failing later on; it also shaves away a microsecond of latency,
|
||||||
|
* since every program header requires invoking at least 1 syscall
|
||||||
|
*/
|
||||||
|
for (i = 0; i + 1 < e->e_phnum;) {
|
||||||
|
if (p[i].p_type == PT_LOAD && p[i + 1].p_type == PT_LOAD &&
|
||||||
|
((p[i].p_flags & (PF_R | PF_W | PF_X)) ==
|
||||||
|
(p[i + 1].p_flags & (PF_R | PF_W | PF_X))) &&
|
||||||
|
((p[i].p_offset + p[i].p_filesz + (pagesz - 1)) & -pagesz) -
|
||||||
|
(p[i + 1].p_offset & -pagesz) <=
|
||||||
|
pagesz &&
|
||||||
|
((p[i].p_vaddr + p[i].p_memsz + (pagesz - 1)) & -pagesz) -
|
||||||
|
(p[i + 1].p_vaddr & -pagesz) <=
|
||||||
|
pagesz) {
|
||||||
|
p[i].p_memsz = (p[i + 1].p_vaddr + p[i + 1].p_memsz) - p[i].p_vaddr;
|
||||||
|
p[i].p_filesz = (p[i + 1].p_offset + p[i + 1].p_filesz) - p[i].p_offset;
|
||||||
|
if (i + 2 < e->e_phnum) {
|
||||||
|
MemMove(p + i + 1, p + i + 2,
|
||||||
|
(e->e_phnum - (i + 2)) * sizeof(struct ElfPhdr));
|
||||||
|
}
|
||||||
|
--e->e_phnum;
|
||||||
|
} else {
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* simulate linux auxiliary values */
|
||||||
|
long auxv[][2] = {
|
||||||
|
{AT_PHDR, (long)&M->phdr.phdr}, //
|
||||||
|
{AT_PHENT, M->ehdr.ehdr.e_phentsize}, //
|
||||||
|
{AT_PHNUM, M->ehdr.ehdr.e_phnum}, //
|
||||||
|
{AT_ENTRY, M->ehdr.ehdr.e_entry}, //
|
||||||
|
{AT_PAGESZ, pagesz}, //
|
||||||
|
{AT_UID, getuid()}, //
|
||||||
|
{AT_EUID, geteuid()}, //
|
||||||
|
{AT_GID, getgid()}, //
|
||||||
|
{AT_EGID, getegid()}, //
|
||||||
|
{AT_HWCAP, 0xffb3ffffu}, //
|
||||||
|
{AT_HWCAP2, 0x181}, //
|
||||||
|
{AT_SECURE, issetugid()}, //
|
||||||
|
{AT_RANDOM, (long)M->rando}, //
|
||||||
|
{AT_EXECFN, (long)execfn}, //
|
||||||
|
{0, 0}, //
|
||||||
|
};
|
||||||
|
_Static_assert(sizeof(auxv) == AUXV_BYTES,
|
||||||
|
"Please update the AUXV_BYTES constant");
|
||||||
|
MemMove(bp, auxv, sizeof(auxv));
|
||||||
|
|
||||||
|
/* we're now ready to load */
|
||||||
|
Spawn(exe, fd, sp, e, p, &M->lib);
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((__noinline__)) static long sysret(long rc) {
|
__attribute__((__noinline__)) static long sysret(long rc) {
|
||||||
|
@ -590,7 +763,7 @@ int main(int argc, char **argv, char **envp) {
|
||||||
int c, i, n, fd, rc;
|
int c, i, n, fd, rc;
|
||||||
struct ApeLoader *M;
|
struct ApeLoader *M;
|
||||||
unsigned char rando[24];
|
unsigned char rando[24];
|
||||||
char *p, *tp, *exe, *prog, *execfn;
|
char *p, *pe, *tp, *exe, *prog, *execfn;
|
||||||
|
|
||||||
// generate some hard random data
|
// generate some hard random data
|
||||||
if (getentropy(rando, sizeof(rando))) {
|
if (getentropy(rando, sizeof(rando))) {
|
||||||
|
@ -673,9 +846,9 @@ int main(int argc, char **argv, char **envp) {
|
||||||
argc = sp[3] = sp[0] - 3;
|
argc = sp[3] = sp[0] - 3;
|
||||||
argv = (char **)((sp += 3) + 1);
|
argv = (char **)((sp += 3) + 1);
|
||||||
} else if (argc < 2) {
|
} else if (argc < 2) {
|
||||||
Emit("usage: ape-m1 PROG [ARGV1,ARGV2,...]\n"
|
Emit("usage: ape PROG [ARGV1,ARGV2,...]\n"
|
||||||
" ape-m1 - PROG [ARGV0,ARGV1,...]\n"
|
" ape - PROG [ARGV0,ARGV1,...]\n"
|
||||||
"actually portable executable loader (apple arm)\n"
|
"actually portable executable loader silicon 1.4\n"
|
||||||
"copyright 2023 justine alexandra roberts tunney\n"
|
"copyright 2023 justine alexandra roberts tunney\n"
|
||||||
"https://justine.lol/ape.html\n");
|
"https://justine.lol/ape.html\n");
|
||||||
_exit(1);
|
_exit(1);
|
||||||
|
@ -692,9 +865,10 @@ int main(int argc, char **argv, char **envp) {
|
||||||
Pexit(exe, -1, "open");
|
Pexit(exe, -1, "open");
|
||||||
} else if ((rc = read(fd, M->ehdr.buf, sizeof(M->ehdr.buf))) < 0) {
|
} else if ((rc = read(fd, M->ehdr.buf, sizeof(M->ehdr.buf))) < 0) {
|
||||||
Pexit(exe, -1, "read");
|
Pexit(exe, -1, "read");
|
||||||
} else if (rc != sizeof(M->ehdr.buf)) {
|
} else if ((unsigned long)rc < sizeof(M->ehdr.ehdr)) {
|
||||||
Pexit(exe, 0, "too small");
|
Pexit(exe, 0, "too small");
|
||||||
}
|
}
|
||||||
|
pe = M->ehdr.buf + rc;
|
||||||
|
|
||||||
// resolve argv[0] to reflect path search
|
// resolve argv[0] to reflect path search
|
||||||
if (argc > 0 && *prog != '/' && *exe == '/' && !StrCmp(prog, argv[0])) {
|
if (argc > 0 && *prog != '/' && *exe == '/' && !StrCmp(prog, argv[0])) {
|
||||||
|
@ -713,30 +887,22 @@ int main(int argc, char **argv, char **envp) {
|
||||||
|
|
||||||
// relocate the guest's random numbers
|
// relocate the guest's random numbers
|
||||||
MemMove(M->rando, rando, sizeof(M->rando));
|
MemMove(M->rando, rando, sizeof(M->rando));
|
||||||
MemSet(rando, 0, sizeof(rando));
|
Bzero(rando, sizeof(rando));
|
||||||
|
|
||||||
#if TROUBLESHOOT
|
|
||||||
for (i = 0; i < argc; ++i) {
|
|
||||||
Emit("argv = ");
|
|
||||||
Emit(argv[i]);
|
|
||||||
Emit("\n");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// ape intended behavior
|
// ape intended behavior
|
||||||
// 1. if file is an elf executable, it'll be used as-is
|
// 1. if file is an elf executable, it'll be used as-is
|
||||||
// 2. if ape, will scan shell script for elf printf statements
|
// 2. if ape, will scan shell script for elf printf statements
|
||||||
// 3. shell script may have multiple lines producing elf headers
|
// 3. shell script may have multiple lines producing elf headers
|
||||||
// 4. all elf printf lines must exist in the first 4096 bytes of file
|
// 4. all elf printf lines must exist in the first 8192 bytes of file
|
||||||
// 5. elf program headers may appear anywhere in the binary
|
// 5. elf program headers may appear anywhere in the binary
|
||||||
if (Read64(M->ehdr.buf) == Read64("MZqFpD='") ||
|
if (READ64(M->ehdr.buf) == READ64("MZqFpD='") ||
|
||||||
Read64(M->ehdr.buf) == Read64("jartsr='")) {
|
READ64(M->ehdr.buf) == READ64("jartsr='") ||
|
||||||
for (p = M->ehdr.buf; p < M->ehdr.buf + sizeof(M->ehdr.buf); ++p) {
|
READ64(M->ehdr.buf) == READ64("APEDBG='")) {
|
||||||
if (Read64(p) != Read64("printf '")) {
|
for (p = M->ehdr.buf; p < pe; ++p) {
|
||||||
|
if (READ64(p) != READ64("printf '")) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for (i = 0, p += 8;
|
for (i = 0, p += 8; p + 3 < pe && (c = *p++) != '\'';) {
|
||||||
p + 3 < M->ehdr.buf + sizeof(M->ehdr.buf) && (c = *p++) != '\'';) {
|
|
||||||
if (c == '\\') {
|
if (c == '\\') {
|
||||||
if ('0' <= *p && *p <= '7') {
|
if ('0' <= *p && *p <= '7') {
|
||||||
c = *p++ - '0';
|
c = *p++ - '0';
|
||||||
|
@ -751,12 +917,14 @@ int main(int argc, char **argv, char **envp) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
M->ehdr.buf[i++] = c;
|
M->ehdr.buf[i++] = c;
|
||||||
|
if (i >= sizeof(M->ehdr.buf)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (i >= sizeof(M->ehdr.ehdr)) {
|
if (i >= sizeof(M->ehdr.ehdr)) {
|
||||||
TryElf(M, exe, fd, sp, bp, execfn);
|
TryElf(M, exe, fd, sp, bp, execfn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TryElf(M, exe, fd, sp, bp, execfn);
|
Pexit(exe, 0, TryElf(M, exe, fd, sp, bp, execfn));
|
||||||
Pexit(exe, 0, "Not an acceptable APE/ELF executable for AARCH64");
|
|
||||||
}
|
}
|
||||||
|
|
21
ape/ape.S
21
ape/ape.S
|
@ -610,7 +610,7 @@ apesh: .ascii "\n@\n#'\"\n" // sixth edition shebang
|
||||||
// extract the loader into a temp folder, and use it to
|
// extract the loader into a temp folder, and use it to
|
||||||
// load the APE without modifying it.
|
// load the APE without modifying it.
|
||||||
.ascii "[ x\"$1\" != x--assimilate ] && {\n"
|
.ascii "[ x\"$1\" != x--assimilate ] && {\n"
|
||||||
.ascii "t=\"${TMPDIR:-${HOME:-.}}/.ape-1.3\"\n"
|
.ascii "t=\"${TMPDIR:-${HOME:-.}}/.ape-1.4\"\n"
|
||||||
.ascii "[ -x \"$t\" ] || {\n"
|
.ascii "[ -x \"$t\" ] || {\n"
|
||||||
.ascii "mkdir -p \"${t%/*}\" &&\n"
|
.ascii "mkdir -p \"${t%/*}\" &&\n"
|
||||||
.ascii "dd if=\"$o\" of=\"$t.$$\" skip="
|
.ascii "dd if=\"$o\" of=\"$t.$$\" skip="
|
||||||
|
@ -810,6 +810,19 @@ ape_loader_end:
|
||||||
.previous
|
.previous
|
||||||
#endif /* SupportsSystemv() || SupportsMetal() */
|
#endif /* SupportsSystemv() || SupportsMetal() */
|
||||||
|
|
||||||
|
.section .note.ape.ident,"a",@progbits
|
||||||
|
.balign 4
|
||||||
|
ape.ident:
|
||||||
|
.long 2f-1f
|
||||||
|
.long 4f-3f
|
||||||
|
.long 1
|
||||||
|
1: .asciz "APE"
|
||||||
|
2: .balign 4
|
||||||
|
3: .long 104000000
|
||||||
|
4: .size ape.ident,.-ape.ident
|
||||||
|
.type ape.ident,@object
|
||||||
|
.previous
|
||||||
|
|
||||||
#if SupportsOpenbsd()
|
#if SupportsOpenbsd()
|
||||||
.section .note.openbsd.ident,"a",@progbits
|
.section .note.openbsd.ident,"a",@progbits
|
||||||
.balign 4
|
.balign 4
|
||||||
|
@ -1054,8 +1067,8 @@ ape_pe: .ascin "PE",4
|
||||||
.long RVA(ape_pe_entry) // EntryPoint
|
.long RVA(ape_pe_entry) // EntryPoint
|
||||||
.long 0 // BaseOfCode
|
.long 0 // BaseOfCode
|
||||||
.quad ape_pe_base // ImageBase
|
.quad ape_pe_base // ImageBase
|
||||||
.long 4096 // SectionAlignment
|
.long ape_pe_sectionalignment // SectionAlignment
|
||||||
.long 4096 // FileAlignment
|
.long ape_pe_filealignment // FileAlignment
|
||||||
.short v_ntversion // MajorOperatingSystemVersion
|
.short v_ntversion // MajorOperatingSystemVersion
|
||||||
.short 0 // MinorOperatingSystemVersion
|
.short 0 // MinorOperatingSystemVersion
|
||||||
.short 0 // MajorImageVersion
|
.short 0 // MajorImageVersion
|
||||||
|
@ -1064,7 +1077,7 @@ ape_pe: .ascin "PE",4
|
||||||
.short 0 // MinorSubsystemVersion
|
.short 0 // MinorSubsystemVersion
|
||||||
.long 0 // Win32VersionValue
|
.long 0 // Win32VersionValue
|
||||||
.long RVA(_end) // SizeOfImage
|
.long RVA(_end) // SizeOfImage
|
||||||
.long RVA(_ehead) // SizeOfHeaders
|
.long ape_pe_sizeofheaders // SizeOfHeaders
|
||||||
.long 0 // Checksum
|
.long 0 // Checksum
|
||||||
.short v_ntsubsystem // Subsystem: 0=Neutral,2=GUI,3=Console
|
.short v_ntsubsystem // Subsystem: 0=Neutral,2=GUI,3=Console
|
||||||
.stub v_ntdllchar,short // DllCharacteristics
|
.stub v_ntdllchar,short // DllCharacteristics
|
||||||
|
|
|
@ -532,6 +532,11 @@ SECTIONS {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ape_pe_filealignment = 512;
|
||||||
|
ape_pe_sectionalignment = 4096;
|
||||||
|
ape_pe_sizeofheaders = SIZEOF(.head);
|
||||||
|
ape_pe_sizeofimage = ROUNDUP(_end - __executable_start, ape_pe_sectionalignment);
|
||||||
|
|
||||||
PFSTUB8(ape_elf_entry, _start);
|
PFSTUB8(ape_elf_entry, _start);
|
||||||
PFSTUB8(ape_elf_phoff, RVA(ape_phdrs));
|
PFSTUB8(ape_elf_phoff, RVA(ape_phdrs));
|
||||||
PFSTUB8(ape_elf_shoff, 0);
|
PFSTUB8(ape_elf_shoff, 0);
|
||||||
|
@ -720,6 +725,7 @@ CHURN(WinMain);
|
||||||
#define LINK_WINDOWS (SupportsWindows() && !DEFINED(EfiMain))
|
#define LINK_WINDOWS (SupportsWindows() && !DEFINED(EfiMain))
|
||||||
PFSTUB4(ape_pe_offset, ape_pe - ape_mz);
|
PFSTUB4(ape_pe_offset, ape_pe - ape_mz);
|
||||||
ape_pe_optsz = ape_pe_sections - (ape_pe + 24);
|
ape_pe_optsz = ape_pe_sections - (ape_pe + 24);
|
||||||
|
ASSERT(ape_pe_optsz % 8 == 0, "SizeOfOptionalHeader must be multiple of 8");
|
||||||
ape_pe_shnum = (ape_pe_sections_end - ape_pe_sections) / 40;
|
ape_pe_shnum = (ape_pe_sections_end - ape_pe_sections) / 40;
|
||||||
ape_pe_base = IMAGE_BASE_VIRTUAL;
|
ape_pe_base = IMAGE_BASE_VIRTUAL;
|
||||||
ape_idataz = LINK_WINDOWS ? RVA(ape_idata_iat) : 0;
|
ape_idataz = LINK_WINDOWS ? RVA(ape_idata_iat) : 0;
|
||||||
|
|
|
@ -36,6 +36,7 @@ rm -f o/tmp/ape /tmp/ape "${TMPDIR:-/tmp}/ape"
|
||||||
for x in .ape \
|
for x in .ape \
|
||||||
.ape-1.1 \
|
.ape-1.1 \
|
||||||
.ape-1.3 \
|
.ape-1.3 \
|
||||||
|
.ape-1.4 \
|
||||||
.ape-blink-0.9.2 \
|
.ape-blink-0.9.2 \
|
||||||
.ape-blink-1.0.0; do
|
.ape-blink-1.0.0; do
|
||||||
rm -f \
|
rm -f \
|
||||||
|
|
116
ape/loader.c
116
ape/loader.c
|
@ -510,8 +510,7 @@ static char SearchPath(struct PathSearcher *ps, const char *suffix) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static char FindCommand(struct PathSearcher *ps, const char *suffix) {
|
static char FindCommand(struct PathSearcher *ps, const char *suffix) {
|
||||||
if (MemChr(ps->name, '/', ps->namelen) ||
|
if (MemChr(ps->name, '/', ps->namelen)) {
|
||||||
MemChr(ps->name, '\\', ps->namelen)) {
|
|
||||||
ps->path[0] = 0;
|
ps->path[0] = 0;
|
||||||
return AccessCommand(ps, suffix, 0);
|
return AccessCommand(ps, suffix, 0);
|
||||||
}
|
}
|
||||||
|
@ -556,18 +555,9 @@ __attribute__((__noreturn__)) static void Spawn(int os, const char *exe, int fd,
|
||||||
Pexit(os, exe, 0, "AT_PAGESZ isn't two power");
|
Pexit(os, exe, 0, "AT_PAGESZ isn't two power");
|
||||||
}
|
}
|
||||||
for (i = 0; i < e->e_phnum; ++i) {
|
for (i = 0; i < e->e_phnum; ++i) {
|
||||||
if (p[i].p_type == PT_INTERP) {
|
|
||||||
Pexit(os, exe, 0, "ELF has PT_INTERP which isn't supported");
|
|
||||||
}
|
|
||||||
if (p[i].p_type == PT_DYNAMIC) {
|
|
||||||
Pexit(os, exe, 0, "ELF has PT_DYNAMIC which isn't supported");
|
|
||||||
}
|
|
||||||
if (p[i].p_type != PT_LOAD) {
|
if (p[i].p_type != PT_LOAD) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!p[i].p_memsz) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (p[i].p_filesz > p[i].p_memsz) {
|
if (p[i].p_filesz > p[i].p_memsz) {
|
||||||
Pexit(os, exe, 0, "ELF p_filesz exceeds p_memsz");
|
Pexit(os, exe, 0, "ELF p_filesz exceeds p_memsz");
|
||||||
}
|
}
|
||||||
|
@ -578,9 +568,9 @@ __attribute__((__noreturn__)) static void Spawn(int os, const char *exe, int fd,
|
||||||
p[i].p_vaddr + p[i].p_memsz + (pagesz - 1) < p[i].p_vaddr) {
|
p[i].p_vaddr + p[i].p_memsz + (pagesz - 1) < p[i].p_vaddr) {
|
||||||
Pexit(os, exe, 0, "ELF p_vaddr + p_memsz overflow");
|
Pexit(os, exe, 0, "ELF p_vaddr + p_memsz overflow");
|
||||||
}
|
}
|
||||||
if (p[i].p_vaddr + p[i].p_filesz < p[i].p_vaddr ||
|
if (p[i].p_offset + p[i].p_filesz < p[i].p_offset ||
|
||||||
p[i].p_vaddr + p[i].p_filesz + (pagesz - 1) < p[i].p_vaddr) {
|
p[i].p_offset + p[i].p_filesz + (pagesz - 1) < p[i].p_offset) {
|
||||||
Pexit(os, exe, 0, "ELF p_vaddr + p_filesz overflow");
|
Pexit(os, exe, 0, "ELF p_offset + p_filesz overflow");
|
||||||
}
|
}
|
||||||
a = p[i].p_vaddr & -pagesz;
|
a = p[i].p_vaddr & -pagesz;
|
||||||
b = (p[i].p_vaddr + p[i].p_memsz + (pagesz - 1)) & -pagesz;
|
b = (p[i].p_vaddr + p[i].p_memsz + (pagesz - 1)) & -pagesz;
|
||||||
|
@ -637,7 +627,6 @@ __attribute__((__noreturn__)) static void Spawn(int os, const char *exe, int fd,
|
||||||
/* load elf */
|
/* load elf */
|
||||||
for (i = 0; i < e->e_phnum; ++i) {
|
for (i = 0; i < e->e_phnum; ++i) {
|
||||||
if (p[i].p_type != PT_LOAD) continue;
|
if (p[i].p_type != PT_LOAD) continue;
|
||||||
if (!p[i].p_memsz) continue;
|
|
||||||
|
|
||||||
/* configure mapping */
|
/* configure mapping */
|
||||||
prot = 0;
|
prot = 0;
|
||||||
|
@ -692,53 +681,122 @@ __attribute__((__noreturn__)) static void Spawn(int os, const char *exe, int fd,
|
||||||
|
|
||||||
static const char *TryElf(struct ApeLoader *M, const char *exe, int fd,
|
static const char *TryElf(struct ApeLoader *M, const char *exe, int fd,
|
||||||
long *sp, long *auxv, unsigned long pagesz, int os) {
|
long *sp, long *auxv, unsigned long pagesz, int os) {
|
||||||
long rc;
|
long i, rc;
|
||||||
unsigned size;
|
unsigned size;
|
||||||
|
struct ElfEhdr *e;
|
||||||
|
struct ElfPhdr *p;
|
||||||
|
|
||||||
|
/* validate page size */
|
||||||
|
if (!pagesz) pagesz = 4096;
|
||||||
|
if (pagesz & (pagesz - 1)) {
|
||||||
|
Pexit(os, exe, 0, "AT_PAGESZ isn't two power");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* validate elf header */
|
||||||
|
e = &M->ehdr.ehdr;
|
||||||
if (READ32(M->ehdr.buf) != READ32("\177ELF")) {
|
if (READ32(M->ehdr.buf) != READ32("\177ELF")) {
|
||||||
return "didn't embed ELF magic";
|
return "didn't embed ELF magic";
|
||||||
}
|
}
|
||||||
if (M->ehdr.ehdr.e_ident[EI_CLASS] == ELFCLASS32) {
|
if (e->e_ident[EI_CLASS] == ELFCLASS32) {
|
||||||
return "32-bit ELF isn't supported";
|
return "32-bit ELF isn't supported";
|
||||||
}
|
}
|
||||||
if (M->ehdr.ehdr.e_type != ET_EXEC && M->ehdr.ehdr.e_type != ET_DYN) {
|
if (e->e_type != ET_EXEC && e->e_type != ET_DYN) {
|
||||||
return "ELF not ET_EXEC or ET_DYN";
|
return "ELF not ET_EXEC or ET_DYN";
|
||||||
}
|
}
|
||||||
if (M->ehdr.ehdr.e_machine != EM_NEXGEN32E) {
|
if (e->e_machine != EM_NEXGEN32E) {
|
||||||
return "couldn't find ELF header with x86-64 machine type";
|
return "couldn't find ELF header with x86-64 machine type";
|
||||||
}
|
}
|
||||||
if (M->ehdr.ehdr.e_phentsize < sizeof(M->phdr.phdr)) {
|
if (e->e_phentsize != sizeof(struct ElfPhdr)) {
|
||||||
return "e_phentsize is too small";
|
Pexit(os, exe, 0, "e_phentsize is wrong");
|
||||||
}
|
}
|
||||||
size = M->ehdr.ehdr.e_phnum;
|
size = e->e_phnum;
|
||||||
if ((size *= M->ehdr.ehdr.e_phentsize) > sizeof(M->phdr.buf)) {
|
if ((size *= sizeof(struct ElfPhdr)) > sizeof(M->phdr.buf)) {
|
||||||
return "too many ELF program headers";
|
Pexit(os, exe, 0, "too many ELF program headers");
|
||||||
}
|
}
|
||||||
rc = Pread(fd, M->phdr.buf, size, M->ehdr.ehdr.e_phoff, os);
|
|
||||||
|
/* read program headers */
|
||||||
|
rc = Pread(fd, M->phdr.buf, size, e->e_phoff, os);
|
||||||
if (rc < 0) return "failed to read ELF program headers";
|
if (rc < 0) return "failed to read ELF program headers";
|
||||||
if (rc != size) return "truncated read of ELF program headers";
|
if (rc != size) return "truncated read of ELF program headers";
|
||||||
|
|
||||||
|
/* bail on recoverable program header errors */
|
||||||
|
p = &M->phdr.phdr;
|
||||||
|
for (i = 0; i < e->e_phnum; ++i) {
|
||||||
|
if (p[i].p_type == PT_INTERP) {
|
||||||
|
return "ELF has PT_INTERP which isn't supported";
|
||||||
|
}
|
||||||
|
if (p[i].p_type == PT_DYNAMIC) {
|
||||||
|
return "ELF has PT_DYNAMIC which isn't supported";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* remove empty program headers */
|
||||||
|
for (i = 0; i < e->e_phnum;) {
|
||||||
|
if (p[i].p_type == PT_LOAD && !p[i].p_memsz) {
|
||||||
|
if (i + 1 < e->e_phnum) {
|
||||||
|
MemMove(p + i, p + i + 1,
|
||||||
|
(e->e_phnum - (i + 1)) * sizeof(struct ElfPhdr));
|
||||||
|
}
|
||||||
|
--e->e_phnum;
|
||||||
|
} else {
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* merge adjacent loads that are contiguous with equal protection,
|
||||||
|
* which prevents our program header overlap check from needlessly
|
||||||
|
* failing later on; it also shaves away a microsecond of latency,
|
||||||
|
* since every program header requires invoking at least 1 syscall
|
||||||
|
*/
|
||||||
|
for (i = 0; i + 1 < e->e_phnum;) {
|
||||||
|
if (p[i].p_type == PT_LOAD && p[i + 1].p_type == PT_LOAD &&
|
||||||
|
((p[i].p_flags & (PF_R | PF_W | PF_X)) ==
|
||||||
|
(p[i + 1].p_flags & (PF_R | PF_W | PF_X))) &&
|
||||||
|
((p[i].p_offset + p[i].p_filesz + (pagesz - 1)) & -pagesz) -
|
||||||
|
(p[i + 1].p_offset & -pagesz) <=
|
||||||
|
pagesz &&
|
||||||
|
((p[i].p_vaddr + p[i].p_memsz + (pagesz - 1)) & -pagesz) -
|
||||||
|
(p[i + 1].p_vaddr & -pagesz) <=
|
||||||
|
pagesz) {
|
||||||
|
p[i].p_memsz = (p[i + 1].p_vaddr + p[i + 1].p_memsz) - p[i].p_vaddr;
|
||||||
|
p[i].p_filesz = (p[i + 1].p_offset + p[i + 1].p_filesz) - p[i].p_offset;
|
||||||
|
if (i + 2 < e->e_phnum) {
|
||||||
|
MemMove(p + i + 1, p + i + 2,
|
||||||
|
(e->e_phnum - (i + 2)) * sizeof(struct ElfPhdr));
|
||||||
|
}
|
||||||
|
--e->e_phnum;
|
||||||
|
} else {
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* fixup operating system provided auxiliary values */
|
||||||
for (; *auxv; auxv += 2) {
|
for (; *auxv; auxv += 2) {
|
||||||
switch (*auxv) {
|
switch (*auxv) {
|
||||||
case AT_PHDR:
|
case AT_PHDR:
|
||||||
auxv[1] = (unsigned long)&M->phdr;
|
auxv[1] = (unsigned long)&M->phdr;
|
||||||
break;
|
break;
|
||||||
case AT_PHENT:
|
case AT_PHENT:
|
||||||
auxv[1] = M->ehdr.ehdr.e_phentsize;
|
auxv[1] = e->e_phentsize;
|
||||||
break;
|
break;
|
||||||
case AT_PHNUM:
|
case AT_PHNUM:
|
||||||
auxv[1] = M->ehdr.ehdr.e_phnum;
|
auxv[1] = e->e_phnum;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Spawn(os, exe, fd, sp, pagesz, &M->ehdr.ehdr, &M->phdr.phdr);
|
|
||||||
|
/* we're now ready to load */
|
||||||
|
Spawn(os, exe, fd, sp, pagesz, e, p);
|
||||||
}
|
}
|
||||||
|
|
||||||
static __attribute__((__noreturn__)) void ShowUsage(int os, int fd, int rc) {
|
static __attribute__((__noreturn__)) void ShowUsage(int os, int fd, int rc) {
|
||||||
Print(os, fd,
|
Print(os, fd,
|
||||||
"NAME\n"
|
"NAME\n"
|
||||||
"\n"
|
"\n"
|
||||||
" actually portable executable loader v1.3\n"
|
" actually portable executable loader version 1.4\n"
|
||||||
" copyright 2023 justine alexandra roberts tunney\n"
|
" copyright 2023 justine alexandra roberts tunney\n"
|
||||||
" https://justine.lol/ape.html\n"
|
" https://justine.lol/ape.html\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
|
10
ape/start.S
10
ape/start.S
|
@ -49,3 +49,13 @@ netbsd.ident:
|
||||||
3: .long 901000000
|
3: .long 901000000
|
||||||
4: .size netbsd.ident,.-netbsd.ident
|
4: .size netbsd.ident,.-netbsd.ident
|
||||||
.type netbsd.ident,@object
|
.type netbsd.ident,@object
|
||||||
|
.balign 4
|
||||||
|
ape.ident:
|
||||||
|
.long 2f-1f
|
||||||
|
.long 4f-3f
|
||||||
|
.long 1
|
||||||
|
1: .asciz "APE"
|
||||||
|
2: .balign 4
|
||||||
|
3: .long 104000000
|
||||||
|
4: .size ape.ident,.-ape.ident
|
||||||
|
.type ape.ident,@object
|
||||||
|
|
Binary file not shown.
Binary file not shown.
BIN
build/bootstrap/ape.silicon
Executable file
BIN
build/bootstrap/ape.silicon
Executable file
Binary file not shown.
|
@ -1,26 +0,0 @@
|
||||||
#if 0
|
|
||||||
/*─────────────────────────────────────────────────────────────────╗
|
|
||||||
│ To the extent possible under law, Justine Tunney has waived │
|
|
||||||
│ all copyright and related or neighboring rights to this file, │
|
|
||||||
│ as it is written in the following disclaimers: │
|
|
||||||
│ • http://unlicense.org/ │
|
|
||||||
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
|
|
||||||
╚─────────────────────────────────────────────────────────────────*/
|
|
||||||
#endif
|
|
||||||
#include "libc/calls/calls.h"
|
|
||||||
#include "libc/calls/struct/iovec.h"
|
|
||||||
#include "libc/errno.h"
|
|
||||||
#include "libc/stdio/stdio.h"
|
|
||||||
#include "libc/str/str.h"
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
|
||||||
char buf[65536];
|
|
||||||
memset(buf, '\n', sizeof(buf));
|
|
||||||
for (;;) {
|
|
||||||
ssize_t rc = writev(1, &(struct iovec){buf, sizeof(buf)}, 1);
|
|
||||||
if (rc != sizeof(buf)) {
|
|
||||||
printf("got %ld (%s)\n", rc, strerror(errno));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -85,8 +85,8 @@ int sys_execve(const char *prog, char *const argv[], char *const envp[]) {
|
||||||
(CanExecute((ape = "/usr/bin/ape")) ||
|
(CanExecute((ape = "/usr/bin/ape")) ||
|
||||||
CanExecute((ape = Join(firstnonnull(getenv("TMPDIR"),
|
CanExecute((ape = Join(firstnonnull(getenv("TMPDIR"),
|
||||||
firstnonnull(getenv("HOME"), ".")),
|
firstnonnull(getenv("HOME"), ".")),
|
||||||
".ape-1.3", buf))) ||
|
".ape-1.4", buf))) ||
|
||||||
CanExecute((ape = Join(firstnonnull(getenv("HOME"), "."), ".ape-1.3",
|
CanExecute((ape = Join(firstnonnull(getenv("HOME"), "."), ".ape-1.4",
|
||||||
buf))))) {
|
buf))))) {
|
||||||
shargs[0] = ape;
|
shargs[0] = ape;
|
||||||
shargs[1] = "-";
|
shargs[1] = "-";
|
||||||
|
|
|
@ -48,8 +48,6 @@
|
||||||
#include "libc/sysv/consts/sig.h"
|
#include "libc/sysv/consts/sig.h"
|
||||||
#include "libc/sysv/errfuns.h"
|
#include "libc/sysv/errfuns.h"
|
||||||
|
|
||||||
#undef sigaction
|
|
||||||
|
|
||||||
#ifdef SYSDEBUG
|
#ifdef SYSDEBUG
|
||||||
STATIC_YOINK("strsignal"); // for kprintf()
|
STATIC_YOINK("strsignal"); // for kprintf()
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -8,7 +8,7 @@ COSMOPOLITAN_C_START_
|
||||||
|
|
||||||
int tcgetwinsize_nt(struct Fd *, struct winsize *);
|
int tcgetwinsize_nt(struct Fd *, struct winsize *);
|
||||||
const char *DescribeWinsize(char[64], int, struct winsize *);
|
const char *DescribeWinsize(char[64], int, struct winsize *);
|
||||||
#define DescribeWinsize(rc, ws) DescribeWinsize(alloca(12), rc, ws)
|
#define DescribeWinsize(rc, ws) DescribeWinsize(alloca(64), rc, ws)
|
||||||
|
|
||||||
COSMOPOLITAN_C_END_
|
COSMOPOLITAN_C_END_
|
||||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||||
|
|
|
@ -33,11 +33,13 @@ Elf64_Shdr *FindElfSectionByName(Elf64_Ehdr *elf, size_t mapsize,
|
||||||
long i;
|
long i;
|
||||||
Elf64_Shdr *shdr;
|
Elf64_Shdr *shdr;
|
||||||
const char *secname;
|
const char *secname;
|
||||||
for (i = 0; i < elf->e_shnum; ++i) {
|
if (shdrstrtab) {
|
||||||
if ((shdr = GetElfSectionHeaderAddress(elf, mapsize, i)) &&
|
for (i = 0; i < elf->e_shnum; ++i) {
|
||||||
(secname = GetElfString(elf, mapsize, shdrstrtab, shdr->sh_name)) &&
|
if ((shdr = GetElfSectionHeaderAddress(elf, mapsize, i)) &&
|
||||||
!strcmp(name, secname)) {
|
(secname = GetElfString(elf, mapsize, shdrstrtab, shdr->sh_name)) &&
|
||||||
return shdr;
|
!strcmp(name, secname)) {
|
||||||
|
return shdr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include "libc/nt/console.h"
|
#include "libc/nt/console.h"
|
||||||
#include "libc/nt/process.h"
|
#include "libc/nt/process.h"
|
||||||
#include "libc/nt/runtime.h"
|
#include "libc/nt/runtime.h"
|
||||||
|
#include "libc/nt/thunk/msabi.h"
|
||||||
#include "libc/runtime/internal.h"
|
#include "libc/runtime/internal.h"
|
||||||
|
|
||||||
__msabi extern typeof(GetCurrentProcessId) *const __imp_GetCurrentProcessId;
|
__msabi extern typeof(GetCurrentProcessId) *const __imp_GetCurrentProcessId;
|
||||||
|
@ -28,17 +29,18 @@ __msabi extern typeof(GetStdHandle) *const __imp_GetStdHandle;
|
||||||
|
|
||||||
extern uint32_t __pid_exec;
|
extern uint32_t __pid_exec;
|
||||||
|
|
||||||
const unsigned char kConsoleHandles[3] = {
|
const signed char kConsoleHandles[3] = {
|
||||||
kNtStdInputHandle,
|
kNtStdInputHandle,
|
||||||
kNtStdOutputHandle,
|
kNtStdOutputHandle,
|
||||||
kNtStdErrorHandle,
|
kNtStdErrorHandle,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Puts cmd.exe gui back the way it was.
|
// Puts cmd.exe gui back the way it was.
|
||||||
privileged dontinstrument void _restorewintty(void) {
|
void _restorewintty(void) {
|
||||||
|
int i;
|
||||||
if (!IsWindows()) return;
|
if (!IsWindows()) return;
|
||||||
if (__imp_GetCurrentProcessId() != __pid_exec) return;
|
if (__imp_GetCurrentProcessId() != __pid_exec) return;
|
||||||
for (int i = 0; i < 3; ++i) {
|
for (i = 0; i < 3; ++i) {
|
||||||
__imp_SetConsoleMode(__imp_GetStdHandle(kConsoleHandles[i]),
|
__imp_SetConsoleMode(__imp_GetStdHandle(kConsoleHandles[i]),
|
||||||
__ntconsolemode[i]);
|
__ntconsolemode[i]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,10 +32,12 @@
|
||||||
#include "libc/log/internal.h"
|
#include "libc/log/internal.h"
|
||||||
#include "libc/log/log.h"
|
#include "libc/log/log.h"
|
||||||
#include "libc/macros.internal.h"
|
#include "libc/macros.internal.h"
|
||||||
|
#include "libc/mem/mem.h"
|
||||||
#include "libc/nexgen32e/stackframe.h"
|
#include "libc/nexgen32e/stackframe.h"
|
||||||
#include "libc/runtime/runtime.h"
|
#include "libc/runtime/runtime.h"
|
||||||
#include "libc/runtime/stack.h"
|
#include "libc/runtime/stack.h"
|
||||||
#include "libc/runtime/symbols.internal.h"
|
#include "libc/runtime/symbols.internal.h"
|
||||||
|
#include "libc/stdio/stdio.h"
|
||||||
#include "libc/str/str.h"
|
#include "libc/str/str.h"
|
||||||
#include "libc/sysv/consts/sig.h"
|
#include "libc/sysv/consts/sig.h"
|
||||||
#include "libc/thread/thread.h"
|
#include "libc/thread/thread.h"
|
||||||
|
@ -162,6 +164,17 @@ static bool AppendFileLine(struct Buffer *b, const char *addr2line,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char *GetSymbolName(struct SymbolTable *st, int symbol, char **mem,
|
||||||
|
size_t *memsz) {
|
||||||
|
char *s, *t;
|
||||||
|
if ((s = __get_symbol_name(st, symbol)) && //
|
||||||
|
s[0] == '_' && s[1] == 'Z' && //
|
||||||
|
(t = __cxa_demangle(s, *mem, memsz, 0))) {
|
||||||
|
*mem = s = t;
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
relegated void __oncrash_arm64(int sig, struct siginfo *si, void *arg) {
|
relegated void __oncrash_arm64(int sig, struct siginfo *si, void *arg) {
|
||||||
char buf[10000];
|
char buf[10000];
|
||||||
ucontext_t *ctx = arg;
|
ucontext_t *ctx = arg;
|
||||||
|
@ -200,6 +213,8 @@ relegated void __oncrash_arm64(int sig, struct siginfo *si, void *arg) {
|
||||||
if (ctx) {
|
if (ctx) {
|
||||||
long pc;
|
long pc;
|
||||||
char line[256];
|
char line[256];
|
||||||
|
char *mem = 0;
|
||||||
|
size_t memsz = 0;
|
||||||
int addend, symbol;
|
int addend, symbol;
|
||||||
const char *debugbin;
|
const char *debugbin;
|
||||||
const char *addr2line;
|
const char *addr2line;
|
||||||
|
@ -253,8 +268,7 @@ relegated void __oncrash_arm64(int sig, struct siginfo *si, void *arg) {
|
||||||
if (pc && st && (symbol = __get_symbol(st, pc))) {
|
if (pc && st && (symbol = __get_symbol(st, pc))) {
|
||||||
addend = pc - st->addr_base;
|
addend = pc - st->addr_base;
|
||||||
addend -= st->symbols[symbol].x;
|
addend -= st->symbols[symbol].x;
|
||||||
Append(b, " ");
|
Append(b, " %s", GetSymbolName(st, symbol, &mem, &memsz));
|
||||||
Append(b, "%s", __get_symbol_name(st, symbol));
|
|
||||||
if (addend) Append(b, "%+d", addend);
|
if (addend) Append(b, "%+d", addend);
|
||||||
}
|
}
|
||||||
Append(b, "\n");
|
Append(b, "\n");
|
||||||
|
@ -274,7 +288,7 @@ relegated void __oncrash_arm64(int sig, struct siginfo *si, void *arg) {
|
||||||
addend -= st->symbols[symbol].x;
|
addend -= st->symbols[symbol].x;
|
||||||
Append(b, " ");
|
Append(b, " ");
|
||||||
if (!AppendFileLine(b, addr2line, debugbin, pc)) {
|
if (!AppendFileLine(b, addr2line, debugbin, pc)) {
|
||||||
Append(b, "%s", __get_symbol_name(st, symbol));
|
Append(b, "%s", GetSymbolName(st, symbol, &mem, &memsz));
|
||||||
if (addend) Append(b, "%+d", addend);
|
if (addend) Append(b, "%+d", addend);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -313,11 +327,12 @@ relegated void __oncrash_arm64(int sig, struct siginfo *si, void *arg) {
|
||||||
}
|
}
|
||||||
Append(b, " %016lx fp %lx lr ", fp, pc);
|
Append(b, " %016lx fp %lx lr ", fp, pc);
|
||||||
if (!AppendFileLine(b, addr2line, debugbin, pc) && st) {
|
if (!AppendFileLine(b, addr2line, debugbin, pc) && st) {
|
||||||
Append(b, "%s", __get_symbol_name(st, symbol));
|
Append(b, "%s", GetSymbolName(st, symbol, &mem, &memsz));
|
||||||
if (addend) Append(b, "%+d", addend);
|
if (addend) Append(b, "%+d", addend);
|
||||||
}
|
}
|
||||||
Append(b, "\n");
|
Append(b, "\n");
|
||||||
}
|
}
|
||||||
|
free(mem);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Append(b, "got %G while crashing! pc %lx lr %lx\n", sig,
|
Append(b, "got %G while crashing! pc %lx lr %lx\n", sig,
|
||||||
|
|
|
@ -4,8 +4,20 @@
|
||||||
#include "libc/nt/struct/imagedatadirectory.internal.h"
|
#include "libc/nt/struct/imagedatadirectory.internal.h"
|
||||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Portable Executable Optional Header.
|
||||||
|
*
|
||||||
|
* @see NtImageNtHeaders which encloses this
|
||||||
|
* @see NtImageFileHeader which precedes this
|
||||||
|
* @see NtImageSectionHeader which follows this
|
||||||
|
*/
|
||||||
struct NtImageOptionalHeader {
|
struct NtImageOptionalHeader {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Must be kNtPe64bit.
|
||||||
|
*/
|
||||||
uint16_t Magic;
|
uint16_t Magic;
|
||||||
|
|
||||||
uint8_t MajorLinkerVersion;
|
uint8_t MajorLinkerVersion;
|
||||||
uint8_t MinorLinkerVersion;
|
uint8_t MinorLinkerVersion;
|
||||||
uint32_t SizeOfCode;
|
uint32_t SizeOfCode;
|
||||||
|
@ -55,8 +67,20 @@ struct NtImageOptionalHeader {
|
||||||
uint16_t MajorSubsystemVersion;
|
uint16_t MajorSubsystemVersion;
|
||||||
uint16_t MinorSubsystemVersion;
|
uint16_t MinorSubsystemVersion;
|
||||||
uint32_t Win32VersionValue;
|
uint32_t Win32VersionValue;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The size (in bytes) of the image, including all headers, as the
|
||||||
|
* image is loaded in memory. It must be a multiple of
|
||||||
|
* SectionAlignment.
|
||||||
|
*/
|
||||||
uint32_t SizeOfImage;
|
uint32_t SizeOfImage;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The combined size of an MS-DOS stub, PE header, and section headers
|
||||||
|
* rounded up to a multiple of FileAlignment.
|
||||||
|
*/
|
||||||
uint32_t SizeOfHeaders;
|
uint32_t SizeOfHeaders;
|
||||||
|
|
||||||
uint32_t CheckSum;
|
uint32_t CheckSum;
|
||||||
uint16_t Subsystem;
|
uint16_t Subsystem;
|
||||||
uint16_t DllCharacteristics;
|
uint16_t DllCharacteristics;
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include "libc/dce.h"
|
#include "libc/dce.h"
|
||||||
#include "libc/intrin/kprintf.h"
|
#include "libc/intrin/kprintf.h"
|
||||||
#include "libc/intrin/strace.internal.h"
|
#include "libc/intrin/strace.internal.h"
|
||||||
|
#include "libc/limits.h"
|
||||||
#include "libc/macros.internal.h"
|
#include "libc/macros.internal.h"
|
||||||
#include "libc/nexgen32e/rdtsc.h"
|
#include "libc/nexgen32e/rdtsc.h"
|
||||||
#include "libc/runtime/internal.h"
|
#include "libc/runtime/internal.h"
|
||||||
|
@ -32,6 +33,7 @@
|
||||||
#include "libc/sysv/consts/sig.h"
|
#include "libc/sysv/consts/sig.h"
|
||||||
#include "libc/thread/thread.h"
|
#include "libc/thread/thread.h"
|
||||||
#include "libc/thread/tls.h"
|
#include "libc/thread/tls.h"
|
||||||
|
#include "third_party/lua/lunix.h"
|
||||||
#ifndef __x86_64__
|
#ifndef __x86_64__
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -128,11 +130,13 @@ textstartup void cosmo(long *sp, struct Syslib *m1) {
|
||||||
_mmi.p = _mmi.s;
|
_mmi.p = _mmi.s;
|
||||||
__mmi_lock_obj._type = PTHREAD_MUTEX_RECURSIVE;
|
__mmi_lock_obj._type = PTHREAD_MUTEX_RECURSIVE;
|
||||||
|
|
||||||
// record provided stack to memory manager
|
// record system provided stack to memory manager
|
||||||
|
uintptr_t s = (uintptr_t)sp;
|
||||||
|
uintptr_t z = GetStackSize() << 1;
|
||||||
_mmi.i = 1;
|
_mmi.i = 1;
|
||||||
_mmi.p->x = (uintptr_t)GetStackAddr() >> 16;
|
_mmi.p->x = (s & -z) >> 16;
|
||||||
_mmi.p->y = (uintptr_t)(GetStackAddr() + (GetStackSize() - FRAMESIZE)) >> 16;
|
_mmi.p->y = MIN(((s & -z) + (z - 1)) >> 16, INT_MAX);
|
||||||
_mmi.p->size = GetStackSize();
|
_mmi.p->size = z;
|
||||||
_mmi.p->prot = PROT_READ | PROT_WRITE;
|
_mmi.p->prot = PROT_READ | PROT_WRITE;
|
||||||
__virtualmax = -1;
|
__virtualmax = -1;
|
||||||
|
|
||||||
|
|
|
@ -66,7 +66,7 @@ __msabi extern typeof(VirtualProtect) *const __imp_VirtualProtect;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
extern int64_t __wincrashearly;
|
extern int64_t __wincrashearly;
|
||||||
extern const char kConsoleHandles[3];
|
extern const signed char kConsoleHandles[3];
|
||||||
extern void cosmo(int, char **, char **, long (*)[2]) wontreturn;
|
extern void cosmo(int, char **, char **, long (*)[2]) wontreturn;
|
||||||
|
|
||||||
static const short kConsoleModes[3] = {
|
static const short kConsoleModes[3] = {
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/calls/struct/timeval.h"
|
#include "libc/calls/struct/timeval.h"
|
||||||
|
#include "libc/errno.h"
|
||||||
#include "libc/sock/goodsocket.internal.h"
|
#include "libc/sock/goodsocket.internal.h"
|
||||||
#include "libc/sock/sock.h"
|
#include "libc/sock/sock.h"
|
||||||
#include "libc/sysv/consts/so.h"
|
#include "libc/sysv/consts/so.h"
|
||||||
|
@ -33,18 +34,23 @@ static bool Tune(int fd, int a, int b, int x) {
|
||||||
*/
|
*/
|
||||||
int GoodSocket(int family, int type, int protocol, bool isserver,
|
int GoodSocket(int family, int type, int protocol, bool isserver,
|
||||||
const struct timeval *timeout) {
|
const struct timeval *timeout) {
|
||||||
int fd;
|
int e, fd;
|
||||||
if ((fd = socket(family, type, protocol)) != -1) {
|
if ((fd = socket(family, type, protocol)) != -1) {
|
||||||
|
e = errno;
|
||||||
if (isserver) {
|
if (isserver) {
|
||||||
Tune(fd, SOL_TCP, TCP_FASTOPEN, 100);
|
Tune(fd, SOL_TCP, TCP_FASTOPEN, 100);
|
||||||
Tune(fd, SOL_SOCKET, SO_REUSEADDR, 1);
|
Tune(fd, SOL_SOCKET, SO_REUSEADDR, 1);
|
||||||
} else {
|
} else {
|
||||||
Tune(fd, SOL_TCP, TCP_FASTOPEN_CONNECT, 1);
|
Tune(fd, SOL_TCP, TCP_FASTOPEN_CONNECT, 1);
|
||||||
}
|
}
|
||||||
|
errno = e;
|
||||||
if (!Tune(fd, SOL_TCP, TCP_QUICKACK, 1)) {
|
if (!Tune(fd, SOL_TCP, TCP_QUICKACK, 1)) {
|
||||||
|
e = errno;
|
||||||
Tune(fd, SOL_TCP, TCP_NODELAY, 1);
|
Tune(fd, SOL_TCP, TCP_NODELAY, 1);
|
||||||
|
errno = e;
|
||||||
}
|
}
|
||||||
if (timeout) {
|
if (timeout) {
|
||||||
|
e = errno;
|
||||||
if (timeout->tv_sec < 0) {
|
if (timeout->tv_sec < 0) {
|
||||||
Tune(fd, SOL_SOCKET, SO_KEEPALIVE, 1);
|
Tune(fd, SOL_SOCKET, SO_KEEPALIVE, 1);
|
||||||
Tune(fd, SOL_TCP, TCP_KEEPIDLE, -timeout->tv_sec);
|
Tune(fd, SOL_TCP, TCP_KEEPIDLE, -timeout->tv_sec);
|
||||||
|
@ -53,6 +59,7 @@ int GoodSocket(int family, int type, int protocol, bool isserver,
|
||||||
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, timeout, sizeof(*timeout));
|
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, timeout, sizeof(*timeout));
|
||||||
setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, timeout, sizeof(*timeout));
|
setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, timeout, sizeof(*timeout));
|
||||||
}
|
}
|
||||||
|
errno = e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return fd;
|
return fd;
|
||||||
|
|
|
@ -45,7 +45,6 @@ extern const unsigned RLIMIT_VMEM;
|
||||||
#define RLIMIT_SWAP RLIMIT_SWAP
|
#define RLIMIT_SWAP RLIMIT_SWAP
|
||||||
#define RLIMIT_VMEM RLIMIT_VMEM
|
#define RLIMIT_VMEM RLIMIT_VMEM
|
||||||
|
|
||||||
|
|
||||||
COSMOPOLITAN_C_END_
|
COSMOPOLITAN_C_END_
|
||||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||||
#endif /* COSMOPOLITAN_LIBC_SYSV_CONSTS_RLIMIT_H_ */
|
#endif /* COSMOPOLITAN_LIBC_SYSV_CONSTS_RLIMIT_H_ */
|
||||||
|
|
|
@ -16,10 +16,15 @@
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/assert.h"
|
||||||
#include "libc/stdio/rand.h"
|
#include "libc/stdio/rand.h"
|
||||||
#include "net/https/https.h"
|
#include "net/https/https.h"
|
||||||
|
|
||||||
int GenerateHardRandom(void *ctx, unsigned char *p, size_t n) {
|
int GenerateHardRandom(void *ctx, unsigned char *p, size_t n) {
|
||||||
rngset(p, n, rdseed, 0);
|
size_t i;
|
||||||
|
ssize_t rc;
|
||||||
|
for (i = 0; i < n; i += (size_t)rc) {
|
||||||
|
_npassert((rc = getrandom(p + i, n - i, 0)) != -1);
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1929,7 +1929,7 @@ int main(int argc, char *argv[]) {
|
||||||
CHECK_EQ(0, chdir("/opt/turfwar"));
|
CHECK_EQ(0, chdir("/opt/turfwar"));
|
||||||
putenv("TMPDIR=/opt/turfwar/tmp");
|
putenv("TMPDIR=/opt/turfwar/tmp");
|
||||||
|
|
||||||
if ((g_blackhole.fd = socket(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0)) == -1) {
|
if ((g_blackhole.fd = socket(AF_UNIX, SOCK_DGRAM, 0)) == -1) {
|
||||||
kprintf("error: socket(AF_UNIX) failed: %s\n", strerror(errno));
|
kprintf("error: socket(AF_UNIX) failed: %s\n", strerror(errno));
|
||||||
_Exit(3);
|
_Exit(3);
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
#include "libc/runtime/memtrack.internal.h"
|
#include "libc/runtime/memtrack.internal.h"
|
||||||
#include "libc/runtime/runtime.h"
|
#include "libc/runtime/runtime.h"
|
||||||
#include "libc/runtime/stack.h"
|
#include "libc/runtime/stack.h"
|
||||||
|
#include "libc/runtime/sysconf.h"
|
||||||
#include "libc/stdio/rand.h"
|
#include "libc/stdio/rand.h"
|
||||||
#include "libc/stdio/stdio.h"
|
#include "libc/stdio/stdio.h"
|
||||||
#include "libc/str/str.h"
|
#include "libc/str/str.h"
|
||||||
|
@ -91,6 +92,17 @@ TEST(mmap, noreplaceExistingMap) {
|
||||||
EXPECT_SYS(0, 0, munmap(p, FRAMESIZE));
|
EXPECT_SYS(0, 0, munmap(p, FRAMESIZE));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(mmap, smallerThanPage_mapsRemainder) {
|
||||||
|
long pagesz = sysconf(_SC_PAGESIZE);
|
||||||
|
char *map = mmap(0, 1, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
||||||
|
ASSERT_NE(MAP_FAILED, map);
|
||||||
|
EXPECT_TRUE(testlib_memoryexists(map));
|
||||||
|
EXPECT_TRUE(testlib_memoryexists(map + (pagesz - 1)));
|
||||||
|
EXPECT_SYS(0, 0, munmap(map, 1));
|
||||||
|
EXPECT_FALSE(testlib_memoryexists(map));
|
||||||
|
EXPECT_FALSE(testlib_memoryexists(map + (pagesz - 1)));
|
||||||
|
}
|
||||||
|
|
||||||
TEST(mmap, testMapFile) {
|
TEST(mmap, testMapFile) {
|
||||||
int fd;
|
int fd;
|
||||||
char *p;
|
char *p;
|
||||||
|
|
|
@ -143,7 +143,7 @@ STATIC_STACK_SIZE(0x80000);
|
||||||
|
|
||||||
STATIC_YOINK("zipos");
|
STATIC_YOINK("zipos");
|
||||||
|
|
||||||
#ifndef TINY
|
#ifdef USE_BLINK
|
||||||
STATIC_YOINK("blink_linux_aarch64"); // for raspberry pi
|
STATIC_YOINK("blink_linux_aarch64"); // for raspberry pi
|
||||||
STATIC_YOINK("blink_xnu_aarch64"); // is apple silicon
|
STATIC_YOINK("blink_xnu_aarch64"); // is apple silicon
|
||||||
#endif
|
#endif
|
||||||
|
@ -4928,7 +4928,7 @@ static int LuaProgramTokenBucket(lua_State *L) {
|
||||||
blackhole.addr.sun_family = AF_UNIX;
|
blackhole.addr.sun_family = AF_UNIX;
|
||||||
strlcpy(blackhole.addr.sun_path, "/var/run/blackhole.sock",
|
strlcpy(blackhole.addr.sun_path, "/var/run/blackhole.sock",
|
||||||
sizeof(blackhole.addr.sun_path));
|
sizeof(blackhole.addr.sun_path));
|
||||||
if ((blackhole.fd = socket(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0)) == -1) {
|
if ((blackhole.fd = socket(AF_UNIX, SOCK_DGRAM, 0)) == -1) {
|
||||||
WARNF("(token) socket(AF_UNIX) failed: %m");
|
WARNF("(token) socket(AF_UNIX) failed: %m");
|
||||||
ban = -1;
|
ban = -1;
|
||||||
} else if (sendto(blackhole.fd, &testip, 4, 0,
|
} else if (sendto(blackhole.fd, &testip, 4, 0,
|
||||||
|
|
Loading…
Reference in a new issue