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:
Justine Tunney 2023-07-25 05:43:04 -07:00
parent 53d3f9d9c5
commit 6843150e0c
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
25 changed files with 524 additions and 226 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

Binary file not shown.

View file

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

View file

@ -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] = "-";

View file

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

View file

@ -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) */

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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] = {

View file

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

View file

@ -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_ */

View file

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

View file

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

View file

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

View file

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