mirror of
https://github.com/jart/cosmopolitan.git
synced 2026-11-10 17:18:50 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
23d812f116 | ||
|
|
c39eb244d4 | ||
|
|
f63c4d4f52 | ||
|
|
b893479417 | ||
|
|
d02cba2451 | ||
|
|
4cf987ddb1 | ||
|
|
20c794a353 | ||
|
|
4f100d2aad | ||
|
|
a12ad17291 | ||
|
|
653c7b2f87 | ||
|
|
d7917ea076 | ||
|
|
585c86e2a4 | ||
|
|
48e260e653 | ||
|
|
aa106c9356 | ||
|
|
5e8c928f1a | ||
|
|
1eb6484c9c | ||
|
|
a46ec61787 | ||
|
|
024be3b009 | ||
|
|
6ca5ab4680 |
No files matched your search
@@ -85,7 +85,7 @@ endif
|
||||
ifeq ($(TOOLCHAIN),) # if TOOLCHAIN isn't defined
|
||||
ifeq ("$(wildcard o/third_party/gcc/bin/x86_64-linux-cosmo-*)","") # if our gcc isn't unbundled
|
||||
ifneq ($(UNAME_M)-$(UNAME_S), x86_64-Linux) # if this is not amd64 linux
|
||||
$(error you need to download https://justine.lol/cosmocc-0.0.14.zip and unzip it inside the cosmo directory)
|
||||
$(error you need to download https://justine.lol/cosmocc-0.0.18.zip and unzip it inside the cosmo directory)
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
@@ -176,6 +176,7 @@ include libc/calls/calls.mk #─┐
|
||||
include libc/irq/irq.mk # ├──SYSTEMS RUNTIME
|
||||
include third_party/nsync/nsync.mk # │ You can issue system calls
|
||||
include libc/runtime/runtime.mk # │
|
||||
include libc/dlopen/dlopen.mk # │
|
||||
include third_party/double-conversion/dc.mk # │
|
||||
include libc/crt/crt.mk # │
|
||||
include third_party/dlmalloc/dlmalloc.mk #─┘
|
||||
@@ -373,6 +374,7 @@ COSMOPOLITAN_OBJECTS = \
|
||||
THIRD_PARTY_NSYNC_MEM \
|
||||
LIBC_MEM \
|
||||
THIRD_PARTY_DLMALLOC \
|
||||
LIBC_DLOPEN \
|
||||
LIBC_RUNTIME \
|
||||
THIRD_PARTY_NSYNC \
|
||||
LIBC_ELF \
|
||||
|
||||
@@ -421,8 +421,8 @@ header file. If you're doing your development work on Linux or BSD then
|
||||
you need just five files to get started. Here's what you do on Linux:
|
||||
|
||||
```sh
|
||||
wget https://justine.lol/cosmopolitan/cosmopolitan-amalgamation-3.0.zip
|
||||
unzip cosmopolitan-amalgamation-3.0.zip
|
||||
wget https://justine.lol/cosmopolitan/cosmopolitan-amalgamation-3.0.2.zip
|
||||
unzip cosmopolitan-amalgamation-3.0.2.zip
|
||||
printf 'main() { printf("hello world\\n"); }\n' >hello.c
|
||||
gcc -g -Os -static -nostdlib -nostdinc -fno-pie -no-pie -mno-red-zone \
|
||||
-fno-omit-frame-pointer -pg -mnop-mcount -mno-tls-direct-seg-refs -gdwarf-4 \
|
||||
|
||||
+346
-369
@@ -17,6 +17,7 @@
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include <dispatch/dispatch.h>
|
||||
#include <dlfcn.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <libkern/OSCacheControl.h>
|
||||
@@ -26,6 +27,7 @@
|
||||
#include <signal.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/random.h>
|
||||
#include <sys/uio.h>
|
||||
@@ -34,7 +36,7 @@
|
||||
|
||||
#define pagesz 16384
|
||||
#define SYSLIB_MAGIC ('s' | 'l' << 8 | 'i' << 16 | 'b' << 24)
|
||||
#define SYSLIB_VERSION 5
|
||||
#define SYSLIB_VERSION 7
|
||||
|
||||
struct Syslib {
|
||||
int magic;
|
||||
@@ -91,6 +93,11 @@ struct Syslib {
|
||||
long (*sem_trywait)(int *);
|
||||
long (*getrlimit)(int, struct rlimit *);
|
||||
long (*setrlimit)(int, const struct rlimit *);
|
||||
// v6 (2023-11-03)
|
||||
void *(*dlopen)(const char *, int);
|
||||
void *(*dlsym)(void *, const char *);
|
||||
int (*dlclose)(void *);
|
||||
char *(*dlerror)(void);
|
||||
};
|
||||
|
||||
#define ELFCLASS32 1
|
||||
@@ -225,60 +232,6 @@ static const char *BaseName(const char *s) {
|
||||
return b;
|
||||
}
|
||||
|
||||
static void Bzero(void *a, unsigned long n) {
|
||||
long z;
|
||||
char *p, *e;
|
||||
p = (char *)a;
|
||||
e = p + n;
|
||||
z = 0;
|
||||
while (p + sizeof(z) <= e) {
|
||||
__builtin_memcpy(p, &z, sizeof(z));
|
||||
p += sizeof(z);
|
||||
}
|
||||
while (p < e) {
|
||||
*p++ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *MemChr(const char *s, unsigned char c, unsigned long n) {
|
||||
for (; n; --n, ++s) {
|
||||
if ((*s & 255) == c) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
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) {
|
||||
unsigned long i, j;
|
||||
if (p) {
|
||||
@@ -361,7 +314,7 @@ __attribute__((__noreturn__)) static void Pexit(const char *c, int failed,
|
||||
static char AccessCommand(struct PathSearcher *ps, unsigned long pathlen) {
|
||||
if (pathlen + 1 + ps->namelen + 1 > sizeof(ps->path)) return 0;
|
||||
if (pathlen && ps->path[pathlen - 1] != '/') ps->path[pathlen++] = '/';
|
||||
MemMove(ps->path + pathlen, ps->name, ps->namelen);
|
||||
memmove(ps->path + pathlen, ps->name, ps->namelen);
|
||||
ps->path[pathlen + ps->namelen] = 0;
|
||||
return !access(ps->path, X_OK);
|
||||
}
|
||||
@@ -390,7 +343,7 @@ static char FindCommand(struct PathSearcher *ps) {
|
||||
|
||||
/* paths are always 100% taken literally when a slash exists
|
||||
$ ape foo/bar.com arg1 arg2 */
|
||||
if (MemChr(ps->name, '/', ps->namelen)) {
|
||||
if (memchr(ps->name, '/', ps->namelen)) {
|
||||
return AccessCommand(ps, 0);
|
||||
}
|
||||
|
||||
@@ -476,310 +429,6 @@ static void pthread_jit_write_protect_np_workaround(int enabled) {
|
||||
Pexit("ape", 0, "failed to set jit write protection");
|
||||
}
|
||||
|
||||
__attribute__((__noreturn__)) static void Spawn(const char *exe, int fd,
|
||||
long *sp, struct ElfEhdr *e,
|
||||
struct ElfPhdr *p,
|
||||
struct Syslib *lib) {
|
||||
long rc;
|
||||
int prot;
|
||||
int flags;
|
||||
int found_entry;
|
||||
unsigned long dynbase;
|
||||
unsigned long virtmin, virtmax;
|
||||
unsigned long a, b, c, d, i, j;
|
||||
|
||||
/* validate elf */
|
||||
found_entry = 0;
|
||||
virtmin = virtmax = 0;
|
||||
for (i = 0; i < e->e_phnum; ++i) {
|
||||
if (p[i].p_type != PT_LOAD) {
|
||||
continue;
|
||||
}
|
||||
if (p[i].p_filesz > p[i].p_memsz) {
|
||||
Pexit(exe, 0, "ELF p_filesz exceeds p_memsz");
|
||||
}
|
||||
if ((p[i].p_flags & (PF_W | PF_X)) == (PF_W | PF_X)) {
|
||||
Pexit(exe, 0, "Apple Silicon doesn't allow RWX memory");
|
||||
}
|
||||
if ((p[i].p_vaddr & (pagesz - 1)) != (p[i].p_offset & (pagesz - 1))) {
|
||||
Pexit(exe, 0, "ELF p_vaddr incongruent w/ p_offset modulo 16384");
|
||||
}
|
||||
if (p[i].p_vaddr + p[i].p_memsz < p[i].p_vaddr ||
|
||||
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_vaddr <= e->e_entry &&
|
||||
e->e_entry < p[i].p_vaddr + p[i].p_memsz) {
|
||||
found_entry = 1;
|
||||
}
|
||||
}
|
||||
if (p[i].p_vaddr < virtmin) {
|
||||
virtmin = p[i].p_vaddr;
|
||||
}
|
||||
if (p[i].p_vaddr + p[i].p_memsz > virtmax) {
|
||||
virtmax = p[i].p_vaddr + p[i].p_memsz;
|
||||
}
|
||||
}
|
||||
if (!found_entry) {
|
||||
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) {
|
||||
void *addr;
|
||||
unsigned long size;
|
||||
if (p[i].p_type != PT_LOAD) 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) {
|
||||
int prot1, prot2;
|
||||
unsigned long wipe;
|
||||
prot1 = prot;
|
||||
prot2 = prot;
|
||||
/* when we ask the system to map the interval [vaddr,vaddr+filesz)
|
||||
it might schlep extra file content into memory on both the left
|
||||
and the righthand side. that's because elf doesn't require that
|
||||
either side of the interval be aligned on the system page size.
|
||||
normally we can get away with ignoring these junk bytes. but if
|
||||
the segment defines bss memory (i.e. memsz > filesz) then we'll
|
||||
need to clear the extra bytes in the page, if they exist. since
|
||||
we can't do that if we're mapping a read-only page, we can just
|
||||
map it with write permissions and call mprotect on it afterward */
|
||||
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 */
|
||||
wipe = MIN(b - a, c - a);
|
||||
if (wipe && (~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 (wipe) Bzero((void *)(dynbase + a), wipe);
|
||||
if (prot2 != prot1) {
|
||||
rc = mprotect(addr, size, prot2);
|
||||
if (rc < 0) Pexit(exe, rc, "prog mprotect");
|
||||
}
|
||||
/* allocate extra bss */
|
||||
if (c > b) {
|
||||
flags |= MAP_ANONYMOUS;
|
||||
rc = (long)mmap((void *)(dynbase + b), c - b, prot, flags, -1, 0);
|
||||
if (rc < 0) Pexit(exe, rc, "extra bss mmap");
|
||||
}
|
||||
} else {
|
||||
/* allocate pure bss */
|
||||
addr = (void *)(dynbase + (p[i].p_vaddr & -pagesz));
|
||||
size = (p[i].p_vaddr & (pagesz - 1)) + p[i].p_memsz;
|
||||
flags |= MAP_ANONYMOUS;
|
||||
rc = (long)mmap(addr, size, prot, flags, -1, 0);
|
||||
if (rc < 0) Pexit(exe, rc, "bss mmap");
|
||||
}
|
||||
}
|
||||
|
||||
/* finish up */
|
||||
close(fd);
|
||||
|
||||
register long *x0 __asm__("x0") = sp;
|
||||
register struct Syslib *x15 __asm__("x15") = lib;
|
||||
register long x16 __asm__("x16") = e->e_entry;
|
||||
__asm__ volatile("mov\tx1,#0\n\t"
|
||||
"mov\tx2,#0\n\t"
|
||||
"mov\tx3,#0\n\t"
|
||||
"mov\tx4,#0\n\t"
|
||||
"mov\tx5,#0\n\t"
|
||||
"mov\tx6,#0\n\t"
|
||||
"mov\tx7,#0\n\t"
|
||||
"mov\tx8,#0\n\t"
|
||||
"mov\tx9,#0\n\t"
|
||||
"mov\tx10,#0\n\t"
|
||||
"mov\tx11,#0\n\t"
|
||||
"mov\tx12,#0\n\t"
|
||||
"mov\tx13,#0\n\t"
|
||||
"mov\tx14,#0\n\t"
|
||||
"mov\tx17,#0\n\t"
|
||||
"mov\tx19,#0\n\t"
|
||||
"mov\tx20,#0\n\t"
|
||||
"mov\tx21,#0\n\t"
|
||||
"mov\tx22,#0\n\t"
|
||||
"mov\tx23,#0\n\t"
|
||||
"mov\tx24,#0\n\t"
|
||||
"mov\tx25,#0\n\t"
|
||||
"mov\tx26,#0\n\t"
|
||||
"mov\tx27,#0\n\t"
|
||||
"mov\tx28,#0\n\t"
|
||||
"mov\tx29,#0\n\t"
|
||||
"mov\tx30,#0\n\t"
|
||||
"mov\tsp,x0\n\t"
|
||||
"mov\tx0,#0\n\t"
|
||||
"br\tx16"
|
||||
: /* no outputs */
|
||||
: "r"(x0), "r"(x15), "r"(x16)
|
||||
: "memory");
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
static const char *TryElf(struct ApeLoader *M, union ElfEhdrBuf *ebuf,
|
||||
const char *exe, int fd, long *sp, long *auxv,
|
||||
char *execfn) {
|
||||
long i, rc;
|
||||
unsigned size;
|
||||
struct ElfEhdr *e;
|
||||
struct ElfPhdr *p;
|
||||
|
||||
/* validate elf header */
|
||||
e = &ebuf->ehdr;
|
||||
if (READ32(ebuf->buf) != READ32("\177ELF")) {
|
||||
return "didn't embed ELF magic";
|
||||
}
|
||||
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, ebuf->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 */
|
||||
auxv[0] = AT_PHDR;
|
||||
auxv[1] = (long)&M->phdr.phdr;
|
||||
auxv[2] = AT_PHENT;
|
||||
auxv[3] = ebuf->ehdr.e_phentsize;
|
||||
auxv[4] = AT_PHNUM;
|
||||
auxv[5] = ebuf->ehdr.e_phnum;
|
||||
auxv[6] = AT_ENTRY;
|
||||
auxv[7] = ebuf->ehdr.e_entry;
|
||||
auxv[8] = AT_PAGESZ;
|
||||
auxv[9] = pagesz;
|
||||
auxv[10] = AT_UID;
|
||||
auxv[11] = getuid();
|
||||
auxv[12] = AT_EUID;
|
||||
auxv[13] = geteuid();
|
||||
auxv[14] = AT_GID;
|
||||
auxv[15] = getgid();
|
||||
auxv[16] = AT_EGID;
|
||||
auxv[17] = getegid();
|
||||
auxv[18] = AT_HWCAP;
|
||||
auxv[19] = 0xffb3ffffu;
|
||||
auxv[20] = AT_HWCAP2;
|
||||
auxv[21] = 0x181;
|
||||
auxv[22] = AT_SECURE;
|
||||
auxv[23] = issetugid();
|
||||
auxv[24] = AT_RANDOM;
|
||||
auxv[25] = (long)M->rando;
|
||||
auxv[26] = AT_EXECFN;
|
||||
auxv[27] = (long)execfn;
|
||||
auxv[28] = 0;
|
||||
|
||||
/* we're now ready to load */
|
||||
Spawn(exe, fd, sp, e, p, &M->lib);
|
||||
}
|
||||
|
||||
__attribute__((__noinline__)) static long sysret(long rc) {
|
||||
return rc == -1 ? -errno : rc;
|
||||
}
|
||||
@@ -881,6 +530,330 @@ static long sys_pselect(int nfds, fd_set *readfds, fd_set *writefds,
|
||||
return sysret(pselect(nfds, readfds, writefds, errorfds, timeout, sigmask));
|
||||
}
|
||||
|
||||
__attribute__((__noreturn__)) static void Spawn(const char *exe, int fd,
|
||||
long *sp, struct ElfEhdr *e,
|
||||
struct ElfPhdr *p,
|
||||
struct Syslib *lib) {
|
||||
long rc;
|
||||
int prot;
|
||||
int flags;
|
||||
int found_entry;
|
||||
unsigned long dynbase;
|
||||
unsigned long virtmin, virtmax;
|
||||
unsigned long a, b, c, d, i, j;
|
||||
|
||||
/* validate elf */
|
||||
found_entry = 0;
|
||||
virtmin = virtmax = 0;
|
||||
for (i = 0; i < e->e_phnum; ++i) {
|
||||
if (p[i].p_type != PT_LOAD) {
|
||||
continue;
|
||||
}
|
||||
if (p[i].p_filesz > p[i].p_memsz) {
|
||||
Pexit(exe, 0, "ELF p_filesz exceeds p_memsz");
|
||||
}
|
||||
if ((p[i].p_flags & (PF_W | PF_X)) == (PF_W | PF_X)) {
|
||||
Pexit(exe, 0, "Apple Silicon doesn't allow RWX memory");
|
||||
}
|
||||
if ((p[i].p_vaddr & (pagesz - 1)) != (p[i].p_offset & (pagesz - 1))) {
|
||||
Pexit(exe, 0, "ELF p_vaddr incongruent w/ p_offset modulo 16384");
|
||||
}
|
||||
if (p[i].p_vaddr + p[i].p_memsz < p[i].p_vaddr ||
|
||||
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_vaddr <= e->e_entry &&
|
||||
e->e_entry < p[i].p_vaddr + p[i].p_memsz) {
|
||||
found_entry = 1;
|
||||
}
|
||||
}
|
||||
if (p[i].p_vaddr < virtmin) {
|
||||
virtmin = p[i].p_vaddr;
|
||||
}
|
||||
if (p[i].p_vaddr + p[i].p_memsz > virtmax) {
|
||||
virtmax = p[i].p_vaddr + p[i].p_memsz;
|
||||
}
|
||||
}
|
||||
if (!found_entry) {
|
||||
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 = sys_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) {
|
||||
void *addr;
|
||||
unsigned long size;
|
||||
if (p[i].p_type != PT_LOAD) 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) {
|
||||
long map1, map2;
|
||||
int prot1, prot2;
|
||||
unsigned long wipe;
|
||||
prot1 = prot;
|
||||
prot2 = prot;
|
||||
/* when we ask the system to map the interval [vaddr,vaddr+filesz)
|
||||
it might schlep extra file content into memory on both the left
|
||||
and the righthand side. that's because elf doesn't require that
|
||||
either side of the interval be aligned on the system page size.
|
||||
normally we can get away with ignoring these junk bytes. but if
|
||||
the segment defines bss memory (i.e. memsz > filesz) then we'll
|
||||
need to clear the extra bytes in the page, if they exist. since
|
||||
we can't do that if we're mapping a read-only page, we can just
|
||||
map it with write permissions and call mprotect on it afterward */
|
||||
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 */
|
||||
wipe = MIN(b - a, c - a);
|
||||
if (wipe && (~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;
|
||||
if (prot1 & PROT_EXEC) {
|
||||
// if sip is disabled then we can load the executable segments
|
||||
// of the binary into memory without needing to copy anything!
|
||||
rc = sys_mmap(addr, size, prot1, flags, fd, p[i].p_offset & -pagesz);
|
||||
if (rc < 0) {
|
||||
if (rc != -EPERM) Pexit(exe, rc, "mmap(exec)");
|
||||
// apple arm64 won't allow PROT_EXEC mapping any unsigned file
|
||||
// however we are allowed to copy it into an anonymous mapping
|
||||
map1 = sys_mmap(0, size, PROT_READ, MAP_PRIVATE, fd,
|
||||
p[i].p_offset & -pagesz);
|
||||
if (map1 < 0) Pexit(exe, map1, "mmap(exec) workaround input");
|
||||
map2 = sys_mmap(addr, size, (prot1 = PROT_READ | PROT_WRITE),
|
||||
MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS, -1, 0);
|
||||
if (map2 < 0) Pexit(exe, map2, "mmap(exec) workaround output");
|
||||
memcpy((void *)map2, (void *)map1, size);
|
||||
munmap((void *)map1, size);
|
||||
}
|
||||
} else {
|
||||
rc = sys_mmap(addr, size, prot1, flags, fd, p[i].p_offset & -pagesz);
|
||||
if (rc < 0) Pexit(exe, rc, "prog mmap");
|
||||
}
|
||||
if (wipe) memset((void *)(dynbase + a), 0, wipe);
|
||||
if (prot2 != prot1) {
|
||||
rc = sys_mprotect(addr, size, prot2);
|
||||
if (rc < 0) Pexit(exe, rc, "prog mprotect");
|
||||
}
|
||||
/* allocate extra bss */
|
||||
if (c > b) {
|
||||
flags |= MAP_ANONYMOUS;
|
||||
rc = sys_mmap((void *)(dynbase + b), c - b, prot, flags, -1, 0);
|
||||
if (rc < 0) Pexit(exe, rc, "extra bss mmap");
|
||||
}
|
||||
} else {
|
||||
/* allocate pure bss */
|
||||
addr = (void *)(dynbase + (p[i].p_vaddr & -pagesz));
|
||||
size = (p[i].p_vaddr & (pagesz - 1)) + p[i].p_memsz;
|
||||
flags |= MAP_ANONYMOUS;
|
||||
rc = sys_mmap(addr, size, prot, flags, -1, 0);
|
||||
if (rc < 0) Pexit(exe, rc, "bss mmap");
|
||||
}
|
||||
}
|
||||
|
||||
/* finish up */
|
||||
close(fd);
|
||||
|
||||
register long *x0 __asm__("x0") = sp;
|
||||
register struct Syslib *x15 __asm__("x15") = lib;
|
||||
register long x16 __asm__("x16") = e->e_entry;
|
||||
__asm__ volatile("mov\tx1,#0\n\t"
|
||||
"mov\tx2,#0\n\t"
|
||||
"mov\tx3,#0\n\t"
|
||||
"mov\tx4,#0\n\t"
|
||||
"mov\tx5,#0\n\t"
|
||||
"mov\tx6,#0\n\t"
|
||||
"mov\tx7,#0\n\t"
|
||||
"mov\tx8,#0\n\t"
|
||||
"mov\tx9,#0\n\t"
|
||||
"mov\tx10,#0\n\t"
|
||||
"mov\tx11,#0\n\t"
|
||||
"mov\tx12,#0\n\t"
|
||||
"mov\tx13,#0\n\t"
|
||||
"mov\tx14,#0\n\t"
|
||||
"mov\tx17,#0\n\t"
|
||||
"mov\tx19,#0\n\t"
|
||||
"mov\tx20,#0\n\t"
|
||||
"mov\tx21,#0\n\t"
|
||||
"mov\tx22,#0\n\t"
|
||||
"mov\tx23,#0\n\t"
|
||||
"mov\tx24,#0\n\t"
|
||||
"mov\tx25,#0\n\t"
|
||||
"mov\tx26,#0\n\t"
|
||||
"mov\tx27,#0\n\t"
|
||||
"mov\tx28,#0\n\t"
|
||||
"mov\tx29,#0\n\t"
|
||||
"mov\tx30,#0\n\t"
|
||||
"mov\tsp,x0\n\t"
|
||||
"mov\tx0,#0\n\t"
|
||||
"br\tx16"
|
||||
: /* no outputs */
|
||||
: "r"(x0), "r"(x15), "r"(x16)
|
||||
: "memory");
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
static const char *TryElf(struct ApeLoader *M, union ElfEhdrBuf *ebuf,
|
||||
const char *exe, int fd, long *sp, long *auxv,
|
||||
char *execfn) {
|
||||
long i, rc;
|
||||
unsigned size;
|
||||
struct ElfEhdr *e;
|
||||
struct ElfPhdr *p;
|
||||
|
||||
/* validate elf header */
|
||||
e = &ebuf->ehdr;
|
||||
if (READ32(ebuf->buf) != READ32("\177ELF")) {
|
||||
return "didn't embed ELF magic";
|
||||
}
|
||||
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, ebuf->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 */
|
||||
auxv[0] = AT_PHDR;
|
||||
auxv[1] = (long)&M->phdr.phdr;
|
||||
auxv[2] = AT_PHENT;
|
||||
auxv[3] = ebuf->ehdr.e_phentsize;
|
||||
auxv[4] = AT_PHNUM;
|
||||
auxv[5] = ebuf->ehdr.e_phnum;
|
||||
auxv[6] = AT_ENTRY;
|
||||
auxv[7] = ebuf->ehdr.e_entry;
|
||||
auxv[8] = AT_PAGESZ;
|
||||
auxv[9] = pagesz;
|
||||
auxv[10] = AT_UID;
|
||||
auxv[11] = getuid();
|
||||
auxv[12] = AT_EUID;
|
||||
auxv[13] = geteuid();
|
||||
auxv[14] = AT_GID;
|
||||
auxv[15] = getgid();
|
||||
auxv[16] = AT_EGID;
|
||||
auxv[17] = getegid();
|
||||
auxv[18] = AT_HWCAP;
|
||||
auxv[19] = 0xffb3ffffu;
|
||||
auxv[20] = AT_HWCAP2;
|
||||
auxv[21] = 0x181;
|
||||
auxv[22] = AT_SECURE;
|
||||
auxv[23] = issetugid();
|
||||
auxv[24] = AT_RANDOM;
|
||||
auxv[25] = (long)M->rando;
|
||||
auxv[26] = AT_EXECFN;
|
||||
auxv[27] = (long)execfn;
|
||||
auxv[28] = 0;
|
||||
|
||||
/* we're now ready to load */
|
||||
Spawn(exe, fd, sp, e, p, &M->lib);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv, char **envp) {
|
||||
unsigned i;
|
||||
int c, n, fd, rc;
|
||||
@@ -943,6 +916,10 @@ int main(int argc, char **argv, char **envp) {
|
||||
M->lib.sem_trywait = sys_sem_trywait;
|
||||
M->lib.getrlimit = sys_getrlimit;
|
||||
M->lib.setrlimit = sys_setrlimit;
|
||||
M->lib.dlopen = dlopen;
|
||||
M->lib.dlsym = dlsym;
|
||||
M->lib.dlclose = dlclose;
|
||||
M->lib.dlerror = dlerror;
|
||||
|
||||
/* getenv("_") is close enough to at_execfn */
|
||||
execfn = argc > 0 ? argv[0] : 0;
|
||||
@@ -968,7 +945,7 @@ int main(int argc, char **argv, char **envp) {
|
||||
} else if (argc < 2) {
|
||||
Emit("usage: ape PROG [ARGV1,ARGV2,...]\n"
|
||||
" ape - PROG [ARGV0,ARGV1,...]\n"
|
||||
"actually portable executable loader silicon 1.8\n"
|
||||
"actually portable executable loader silicon 1.9\n"
|
||||
"copyright 2023 justine alexandra roberts tunney\n"
|
||||
"https://justine.lol/ape.html\n");
|
||||
_exit(1);
|
||||
@@ -987,7 +964,7 @@ int main(int argc, char **argv, char **envp) {
|
||||
for (; n > 0; n -= pagesz) {
|
||||
((char *)sp2)[n - 1] = 0;
|
||||
}
|
||||
MemMove(sp2, sp, (auxv - sp) * sizeof(long));
|
||||
memmove(sp2, sp, (auxv - sp) * sizeof(long));
|
||||
argv = (char **)(sp2 + 1);
|
||||
envp = (char **)(sp2 + 1 + argc + 1);
|
||||
auxv = sp2 + (auxv - sp);
|
||||
@@ -1003,10 +980,10 @@ int main(int argc, char **argv, char **envp) {
|
||||
/* search for executable */
|
||||
if (!(exe = Commandv(&M->ps, prog, GetEnv(envp, "PATH")))) {
|
||||
Pexit(prog, 0, "not found (maybe chmod +x)");
|
||||
} else if ((fd = openat(AT_FDCWD, exe, O_RDONLY)) < 0) {
|
||||
Pexit(exe, -1, "open");
|
||||
} else if ((rc = read(fd, ebuf->buf, sizeof(ebuf->buf))) < 0) {
|
||||
Pexit(exe, -1, "read");
|
||||
} else if ((fd = sys_openat(AT_FDCWD, exe, O_RDONLY, 0)) < 0) {
|
||||
Pexit(exe, fd, "open");
|
||||
} else if ((rc = sys_read(fd, ebuf->buf, sizeof(ebuf->buf))) < 0) {
|
||||
Pexit(exe, rc, "read");
|
||||
} else if ((unsigned long)rc < sizeof(ebuf->ehdr)) {
|
||||
Pexit(exe, 0, "too small");
|
||||
}
|
||||
@@ -1019,8 +996,8 @@ int main(int argc, char **argv, char **envp) {
|
||||
}
|
||||
|
||||
/* generate some hard random data */
|
||||
if (getentropy(M->rando, sizeof(M->rando))) {
|
||||
Pexit(argv[0], -1, "getentropy");
|
||||
if ((rc = sys_getentropy(M->rando, sizeof(M->rando))) < 0) {
|
||||
Pexit(argv[0], rc, "getentropy");
|
||||
}
|
||||
|
||||
/* ape intended behavior
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#define COSMOPOLITAN_APE_APE_H_
|
||||
|
||||
#define APE_VERSION_MAJOR 1
|
||||
#define APE_VERSION_MINOR 8
|
||||
#define APE_VERSION_MINOR 9
|
||||
#define APE_VERSION_STR APE_VERSION_STR_(APE_VERSION_MAJOR, APE_VERSION_MINOR)
|
||||
#define APE_VERSION_NOTE APE_VERSION_NOTE_(APE_VERSION_MAJOR, APE_VERSION_MINOR)
|
||||
|
||||
|
||||
@@ -947,6 +947,11 @@ EXTERN_C __attribute__((__noreturn__)) void ApeLoader(long di, long *sp,
|
||||
os = OPENBSD;
|
||||
}
|
||||
|
||||
/* xnu passes auxv as an array of strings */
|
||||
if (os == XNU) {
|
||||
*auxv = 0;
|
||||
}
|
||||
|
||||
/* detect netbsd and find end of words */
|
||||
pagesz = 0;
|
||||
for (ap = auxv; ap[0]; ap += 2) {
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#ifdef __aarch64__
|
||||
|
||||
_start: mov x1,sp
|
||||
and sp,x1,#-16
|
||||
mov x29,0
|
||||
bl ApeLoader
|
||||
.endfn _start,globl
|
||||
@@ -33,6 +34,7 @@ XnuEntrypoint:
|
||||
mov $_HOSTXNU,%dl // xnu's not unix!
|
||||
ElfEntrypoint:
|
||||
mov %rsp,%rsi // save real stack
|
||||
andq $-16,%rsp // force SSE alignment
|
||||
call ApeLoader
|
||||
.endfn ElfEntrypoint,globl
|
||||
.endfn XnuEntrypoint,globl
|
||||
|
||||
+16
-2
@@ -2,6 +2,7 @@
|
||||
|
||||
PROG=${0##*/}
|
||||
MODE=${MODE:-$m}
|
||||
TMPDIR=${TMPDIR:-/tmp}
|
||||
COSMO=${COSMO:-/opt/cosmo}
|
||||
COSMOS=${COSMOS:-/opt/cosmos}
|
||||
|
||||
@@ -18,6 +19,19 @@ fi
|
||||
echo "Actually Portable Executable (APE) Installer" >&2
|
||||
echo "Author: Justine Tunney <[email protected]>" >&2
|
||||
|
||||
# special installation process for apple silicon
|
||||
if [ "$(uname -s)" = "Darwin" ] && [ "$(uname -m)" = "arm64" ]; then
|
||||
echo "cc -O -o $TMPDIR/ape.$$ ape/ape-m1.c" >&2
|
||||
cc -O -o "$TMPDIR/ape.$$" ape/ape-m1.c || exit
|
||||
if [ ! -d /usr/local/bin ]; then
|
||||
echo "$SUDO mkdir -p /usr/local/bin" >&2
|
||||
$SUDO mkdir -p /usr/local/bin || exit
|
||||
fi
|
||||
echo "$SUDO mv -f $TMPDIR/ape.$$ /usr/local/bin/ape" >&2
|
||||
$SUDO mv -f "$TMPDIR/ape.$$" /usr/local/bin/ape || exit
|
||||
exit
|
||||
fi
|
||||
|
||||
################################################################################
|
||||
# INSTALL APE LOADER SYSTEMWIDE
|
||||
|
||||
@@ -50,8 +64,8 @@ fi
|
||||
if [ "$(uname -s)" = "Darwin" ]; then
|
||||
if ! [ /usr/bin/ape -nt o//ape/ape.macho ]; then
|
||||
echo >&2
|
||||
echo "installing o//ape/ape.elf to /usr/bin/ape" >&2
|
||||
echo "$SUDO mv -f o//ape/ape.elf /usr/bin/ape" >&2
|
||||
echo "installing o//ape/ape.macho to /usr/bin/ape" >&2
|
||||
echo "$SUDO cp -f o//ape/ape.macho /usr/bin/ape" >&2
|
||||
$SUDO cp -f o//ape/ape.macho /usr/bin/ape || exit
|
||||
echo "done" >&2
|
||||
fi
|
||||
|
||||
@@ -37,6 +37,9 @@ done
|
||||
if [ -f /usr/bin/ape ]; then
|
||||
$SUDO rm -f /usr/bin/ape
|
||||
fi
|
||||
if [ -f /usr/local/bin/ape ]; then
|
||||
$SUDO rm -f /usr/local/bin/ape
|
||||
fi
|
||||
|
||||
# legacy installations
|
||||
rm -f o/tmp/ape /tmp/ape "${TMPDIR:-/tmp}/ape"
|
||||
@@ -50,6 +53,7 @@ for x in .ape \
|
||||
.ape-1.6 \
|
||||
.ape-1.7 \
|
||||
.ape-1.8 \
|
||||
.ape-1.9 \
|
||||
.ape-blink-0.9.2 \
|
||||
.ape-blink-1.0.0; do
|
||||
rm -f \
|
||||
|
||||
@@ -295,6 +295,8 @@ for x; do
|
||||
exit 0
|
||||
elif [ x"$x" = x"-Wl,--version" ]; then
|
||||
GOT_SOME=1
|
||||
elif [ x"$x" = x"-dumpmachine" ]; then
|
||||
GOT_SOME=1
|
||||
fi
|
||||
set -- "$@" "$x"
|
||||
done
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,55 @@
|
||||
#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/dlopen/dlfcn.h"
|
||||
#include "libc/fmt/itoa.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/nt/thunk/msabi.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
|
||||
/**
|
||||
* @fileoverview cosmopolitan dynamic runtime linking demo
|
||||
*
|
||||
* Our dlopen() interface currently supports:
|
||||
*
|
||||
* - x86-64 Linux w/ Glibc
|
||||
* - x86-64 Linux w/ Musl Libc
|
||||
* - x86-64 FreeBSD
|
||||
* - x86-64 Windows
|
||||
* - aarch64 Linux w/ Glibc
|
||||
* - aarch64 MacOS
|
||||
*
|
||||
*/
|
||||
|
||||
int main(int argc, char **argv, char **envp) {
|
||||
|
||||
// open the host system's zlib library
|
||||
void *libc = dlopen("libz.so", RTLD_LAZY);
|
||||
if (!libc) {
|
||||
tinyprint(2, dlerror(), "\n", NULL);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// load crc() function address
|
||||
unsigned (*crc32)(unsigned, void *, int) = dlsym(libc, "crc32");
|
||||
if (!crc32) {
|
||||
tinyprint(2, dlerror(), "\n", NULL);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// compute a checksum and print the result
|
||||
char ibuf[12];
|
||||
FormatInt32(ibuf, crc32(0, "hi", 2));
|
||||
tinyprint(1, "crc(hi) = ", ibuf, "\n", NULL);
|
||||
|
||||
// mop up
|
||||
dlclose(libc);
|
||||
exit(0);
|
||||
}
|
||||
@@ -43,6 +43,7 @@ EXAMPLES_DIRECTDEPS = \
|
||||
DSP_SCALE \
|
||||
DSP_TTY \
|
||||
LIBC_CALLS \
|
||||
LIBC_DLOPEN \
|
||||
LIBC_DNS \
|
||||
LIBC_FMT \
|
||||
LIBC_INTRIN \
|
||||
@@ -92,6 +93,7 @@ EXAMPLES_DIRECTDEPS = \
|
||||
THIRD_PARTY_VQSORT \
|
||||
THIRD_PARTY_XED \
|
||||
THIRD_PARTY_ZLIB \
|
||||
TOOL_ARGS \
|
||||
TOOL_BUILD_LIB \
|
||||
TOOL_VIZ_LIB
|
||||
|
||||
|
||||
+2
-1
@@ -168,7 +168,7 @@ int truncate(const char *, int64_t);
|
||||
int ttyname_r(int, char *, size_t);
|
||||
int unlink(const char *);
|
||||
int unlinkat(int, const char *, int);
|
||||
int usleep(unsigned);
|
||||
int usleep(uint64_t);
|
||||
int vfork(void) returnstwice;
|
||||
int wait(int *);
|
||||
int waitpid(int, int *, int);
|
||||
@@ -219,6 +219,7 @@ bool32 isexecutable(const char *);
|
||||
bool32 isregularfile(const char *);
|
||||
bool32 issymlink(const char *);
|
||||
char *commandv(const char *, char *, size_t);
|
||||
int __getcwd(char *, size_t);
|
||||
int clone(void *, void *, size_t, int, void *, void *, void *, void *);
|
||||
int fadvise(int, uint64_t, uint64_t, int);
|
||||
int makedirs(const char *, unsigned);
|
||||
|
||||
@@ -99,8 +99,6 @@ o//libc/calls/prctl.o: \
|
||||
# we always want -Os because:
|
||||
# it's early runtime mandatory and quite huge without it
|
||||
o//libc/calls/getcwd.greg.o \
|
||||
o//libc/calls/getcwd-nt.greg.o \
|
||||
o//libc/calls/getcwd-xnu.greg.o \
|
||||
o//libc/calls/statfs2cosmo.o: private \
|
||||
CFLAGS += \
|
||||
-Os
|
||||
|
||||
@@ -17,10 +17,8 @@
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
#ifdef __x86_64__
|
||||
|
||||
@@ -34,14 +32,4 @@ textwindows int _check_cancel(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
textwindows int _check_signal(bool restartable) {
|
||||
int status;
|
||||
if (_check_cancel() == -1) return -1;
|
||||
if (!_weaken(__sig_check)) return 0;
|
||||
if (!(status = _weaken(__sig_check)())) return 0;
|
||||
if (_check_cancel() == -1) return -1;
|
||||
if (status == 2 && restartable) return 0;
|
||||
return eintr();
|
||||
}
|
||||
|
||||
#endif /* __x86_64__ */
|
||||
@@ -1,7 +1,7 @@
|
||||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2021 Justine Alexandra Roberts Tunney │
|
||||
│ Copyright 2023 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
@@ -16,25 +16,20 @@
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/runtime/dlfcn.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#ifdef __x86_64__
|
||||
|
||||
char *dlerror(void) {
|
||||
return "cosmopolitan doesn't support dsos";
|
||||
textwindows int _check_signal(bool restartable) {
|
||||
int status;
|
||||
if (_check_cancel() == -1) return -1;
|
||||
if (!_weaken(__sig_check)) return 0;
|
||||
if (!(status = _weaken(__sig_check)())) return 0;
|
||||
if (_check_cancel() == -1) return -1;
|
||||
if (status == 2 && restartable) return 0;
|
||||
return eintr();
|
||||
}
|
||||
|
||||
void *dlopen(const char *file, int mode) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *dlsym(void *handle, const char *name) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int dlclose(void *handle) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int dl_iterate_phdr(int callback(void *info, size_t size, void *data),
|
||||
void *data) {
|
||||
return -1;
|
||||
}
|
||||
#endif /* __x86_64__ */
|
||||
+137
-64
@@ -16,81 +16,154 @@
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/calls/struct/metastat.internal.h"
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/log/backtrace.internal.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/nt/files.h"
|
||||
#include "libc/stdio/sysparam.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/at.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
/**
|
||||
* Returns current working directory, e.g.
|
||||
*
|
||||
* const char *dirname = gc(getcwd(0,0)); // if malloc is linked
|
||||
* const char *dirname = getcwd(alloca(PATH_MAX),PATH_MAX);
|
||||
*
|
||||
* @param buf is where UTF-8 NUL-terminated path string gets written,
|
||||
* which may be NULL to ask this function to malloc a buffer
|
||||
* @param size is number of bytes available in buf, e.g. PATH_MAX+1,
|
||||
* which may be 0 if buf is NULL
|
||||
* @return buf containing system-normative path or NULL w/ errno
|
||||
* @error ERANGE, EINVAL, ENOMEM
|
||||
*/
|
||||
char *getcwd(char *buf, size_t size) {
|
||||
char *p, *r;
|
||||
if (buf) {
|
||||
p = buf;
|
||||
if (!size) {
|
||||
einval();
|
||||
STRACE("getcwd(%p, %'zu) %m", buf, size);
|
||||
return 0;
|
||||
}
|
||||
} else if (_weaken(malloc)) {
|
||||
unassert(!__vforked);
|
||||
if (!size) size = PATH_MAX;
|
||||
if (!(p = _weaken(malloc)(size))) {
|
||||
STRACE("getcwd(%p, %'zu) %m", buf, size);
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
einval();
|
||||
STRACE("getcwd() needs buf≠0 or __static_yoink(\"malloc\")");
|
||||
return 0;
|
||||
}
|
||||
*p = '\0';
|
||||
if (!IsWindows()) {
|
||||
if (IsMetal()) {
|
||||
r = size >= 5 ? strcpy(p, "/zip") : 0;
|
||||
} else if (IsXnu()) {
|
||||
r = sys_getcwd_xnu(p, size);
|
||||
} else if (sys_getcwd(p, size) != (void *)-1) {
|
||||
r = p;
|
||||
} else {
|
||||
r = 0;
|
||||
}
|
||||
} else {
|
||||
r = sys_getcwd_nt(p, size);
|
||||
}
|
||||
if (!buf) {
|
||||
if (!r) {
|
||||
if (_weaken(free)) {
|
||||
_weaken(free)(p);
|
||||
}
|
||||
} else {
|
||||
if (_weaken(realloc)) {
|
||||
if ((p = _weaken(realloc)(r, strlen(r) + 1))) {
|
||||
r = p;
|
||||
#define XNU_F_GETPATH 50
|
||||
#define XNU_MAXPATHLEN 1024
|
||||
|
||||
static int sys_getcwd_xnu(char *res, size_t size) {
|
||||
int fd, len, rc = -1;
|
||||
union metastat st[2];
|
||||
char buf[XNU_MAXPATHLEN];
|
||||
if ((fd = __sys_openat_nc(AT_FDCWD, ".", O_RDONLY | O_DIRECTORY, 0)) != -1) {
|
||||
if (__sys_fstat(fd, &st[0]) != -1) {
|
||||
if (st[0].xnu.st_dev && st[0].xnu.st_ino) {
|
||||
if (__sys_fcntl(fd, XNU_F_GETPATH, (uintptr_t)buf) != -1) {
|
||||
if (__sys_fstatat(AT_FDCWD, buf, &st[1], 0) != -1) {
|
||||
if (st[0].xnu.st_dev == st[1].xnu.st_dev &&
|
||||
st[0].xnu.st_ino == st[1].xnu.st_ino) {
|
||||
if ((len = strlen(buf)) < size) {
|
||||
memcpy(res, buf, (rc = len + 1));
|
||||
} else {
|
||||
erange();
|
||||
}
|
||||
} else {
|
||||
einval();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
einval();
|
||||
}
|
||||
}
|
||||
sys_close(fd);
|
||||
}
|
||||
STRACE("getcwd(%p, %'zu) → %#s", buf, size, r);
|
||||
return r;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int sys_getcwd_metal(char *buf, size_t size) {
|
||||
if (size >= 5) {
|
||||
strcpy(buf, "/zip");
|
||||
return 5;
|
||||
} else {
|
||||
return erange();
|
||||
}
|
||||
}
|
||||
|
||||
static inline int IsAlpha(int c) {
|
||||
return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z');
|
||||
}
|
||||
|
||||
static dontinline textwindows int sys_getcwd_nt(char *buf, size_t size) {
|
||||
|
||||
// get current directory from the system
|
||||
char16_t p16[PATH_MAX];
|
||||
uint32_t n = GetCurrentDirectory(PATH_MAX, p16);
|
||||
if (!n) return eacces(); // system call failed
|
||||
if (n >= PATH_MAX) return erange(); // not enough room?!?
|
||||
|
||||
// convert utf-16 to utf-8
|
||||
// we can't modify `buf` until we're certain of success
|
||||
char p8[PATH_MAX], *p = p8;
|
||||
n = tprecode16to8(p, PATH_MAX, p16).ax;
|
||||
if (n >= PATH_MAX) return erange(); // utf-8 explosion
|
||||
|
||||
// turn \\?\c:\... into c:\...
|
||||
if (p[0] == '\\' && //
|
||||
p[1] == '\\' && //
|
||||
p[2] == '?' && //
|
||||
p[3] == '\\' && //
|
||||
IsAlpha(p[4]) && //
|
||||
p[5] == ':' && //
|
||||
p[6] == '\\') {
|
||||
p += 4;
|
||||
n -= 4;
|
||||
}
|
||||
|
||||
// turn c:\... into \c\...
|
||||
if (IsAlpha(p[0]) && //
|
||||
p[1] == ':' && //
|
||||
p[2] == '\\') {
|
||||
p[1] = p[0];
|
||||
p[0] = '\\';
|
||||
}
|
||||
|
||||
// we now know the final length
|
||||
// check if the user supplied a buffer large enough
|
||||
if (n >= size) {
|
||||
return erange();
|
||||
}
|
||||
|
||||
// copy bytes converting backslashes to slash
|
||||
for (int i = 0; i < n; ++i) {
|
||||
int c = p[i];
|
||||
if (c == '\\') {
|
||||
c = '/';
|
||||
}
|
||||
buf[i] = c;
|
||||
}
|
||||
|
||||
// return number of bytes including nul
|
||||
buf[n++] = 0;
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns current working directory.
|
||||
*
|
||||
* Cosmo provides this function to address the shortcomings of getcwd().
|
||||
* First, this function doesn't link malloc(). Secondly, this offers the
|
||||
* Linux and NetBSD's getcwd() API across platforms since it's the best.
|
||||
*
|
||||
* @param buf receives utf-8 path and isn't modified on error
|
||||
* @param size is byte capacity of `buf`
|
||||
* @return bytes copied including nul on success, or -1 w/ errno
|
||||
* @raise EACCES if the current directory path couldn't be accessed
|
||||
* @raise ERANGE if `size` wasn't big enough for path and nul byte
|
||||
* @raise EFAULT if `buf` points to invalid memory
|
||||
*/
|
||||
int __getcwd(char *buf, size_t size) {
|
||||
int rc;
|
||||
if (IsLinux() || IsNetbsd()) {
|
||||
rc = sys_getcwd(buf, size);
|
||||
} else if (IsXnu()) {
|
||||
rc = sys_getcwd_xnu(buf, size);
|
||||
} else if (IsWindows()) {
|
||||
rc = sys_getcwd_nt(buf, size);
|
||||
} else if (IsFreebsd() || IsOpenbsd()) {
|
||||
if (sys_getcwd(buf, size) != -1) {
|
||||
rc = strlen(buf) + 1;
|
||||
} else if (SupportsFreebsd() && (errno == ENOMEM || errno == EINVAL)) {
|
||||
rc = erange();
|
||||
} else {
|
||||
rc = -1;
|
||||
}
|
||||
} else {
|
||||
rc = sys_getcwd_metal(buf, size);
|
||||
}
|
||||
STRACE("__getcwd([%#hhs], %'zu) → %d% m", rc != -1 ? buf : "n/a", size, rc);
|
||||
return rc;
|
||||
}
|
||||
@@ -86,8 +86,9 @@ static inline void InitProgramExecutableNameImpl(void) {
|
||||
if (q[0] == '.' && q[1] == '/') {
|
||||
q += 2;
|
||||
}
|
||||
if (getcwd(p, e - p - 1 - 4)) { // for / and .com
|
||||
while (*p) ++p;
|
||||
int got = __getcwd(p, e - p - 1 - 4); // for / and .com
|
||||
if (got != -1) {
|
||||
p += got - 1;
|
||||
*p++ = '/';
|
||||
}
|
||||
}
|
||||
|
||||
+24
-34
@@ -16,50 +16,40 @@
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/nt/enum/wait.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/sysv/consts/sicode.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/thread/tls.h"
|
||||
#ifdef __x86_64__
|
||||
|
||||
// returns 0 on timeout or spurious wakeup
|
||||
// raises EINTR if a signal delivery interrupted wait operation
|
||||
// raises ECANCELED if this POSIX thread was canceled in masked mode
|
||||
static textwindows int _park_thread(uint32_t msdelay, sigset_t waitmask,
|
||||
bool restartable) {
|
||||
int rc;
|
||||
int64_t sem;
|
||||
sigset_t om;
|
||||
uint32_t wi;
|
||||
struct PosixThread *pt;
|
||||
pt = _pthread_self();
|
||||
pt->pt_flags &= ~PT_RESTARTABLE;
|
||||
if (restartable) pt->pt_flags |= PT_RESTARTABLE;
|
||||
pt->pt_semaphore = sem = CreateSemaphore(0, 0, 1, 0);
|
||||
pthread_cleanup_push((void *)CloseHandle, (void *)sem);
|
||||
atomic_store_explicit(&pt->pt_blocker, PT_BLOCKER_SEM, memory_order_release);
|
||||
om = __sig_beginwait(waitmask);
|
||||
if ((rc = _check_signal(restartable)) != -1) {
|
||||
if ((wi = WaitForSingleObject(sem, msdelay)) != -1u) {
|
||||
if (restartable && !(pt->pt_flags & PT_RESTARTABLE)) rc = eintr();
|
||||
rc |= _check_signal(restartable);
|
||||
} else {
|
||||
rc = __winerr();
|
||||
}
|
||||
int sig;
|
||||
if (_check_cancel() == -1) return -1;
|
||||
if ((sig = __sig_get(waitmask))) {
|
||||
int handler_was_called = __sig_relay(sig, SI_KERNEL, waitmask);
|
||||
if (_check_cancel() == -1) return -1;
|
||||
if (!restartable || handler_was_called == 1) return eintr();
|
||||
}
|
||||
__sig_finishwait(om);
|
||||
atomic_store_explicit(&pt->pt_blocker, PT_BLOCKER_CPU, memory_order_release);
|
||||
pt->pt_flags &= ~PT_RESTARTABLE;
|
||||
pthread_cleanup_pop(true);
|
||||
pt->pt_semaphore = 0;
|
||||
return rc;
|
||||
int expect = 0;
|
||||
atomic_int futex = 0;
|
||||
struct PosixThread *pt = _pthread_self();
|
||||
pt->pt_blkmask = waitmask;
|
||||
atomic_store_explicit(&pt->pt_blocker, &futex, memory_order_release);
|
||||
bool32 ok = WaitOnAddress(&futex, &expect, sizeof(int), msdelay);
|
||||
atomic_store_explicit(&pt->pt_blocker, 0, memory_order_release);
|
||||
if (ok && (sig = __sig_get(waitmask))) {
|
||||
int handler_was_called = __sig_relay(sig, SI_KERNEL, waitmask);
|
||||
if (_check_cancel() == -1) return -1;
|
||||
if (!restartable || handler_was_called == 1) return eintr();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
textwindows int _park_norestart(uint32_t msdelay, sigset_t waitmask) {
|
||||
|
||||
+26
-22
@@ -44,6 +44,7 @@
|
||||
#include "libc/nt/enum/creationdisposition.h"
|
||||
#include "libc/nt/enum/filesharemode.h"
|
||||
#include "libc/nt/enum/vk.h"
|
||||
#include "libc/nt/enum/wait.h"
|
||||
#include "libc/nt/errors.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/struct/inputrecord.h"
|
||||
@@ -51,6 +52,7 @@
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/str/utf16.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/sicode.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
@@ -718,10 +720,10 @@ static textwindows bool DigestConsoleInput(char *data, size_t size, int *rc) {
|
||||
}
|
||||
|
||||
static textwindows int WaitForConsole(struct Fd *f, sigset_t waitmask) {
|
||||
int rc;
|
||||
sigset_t m;
|
||||
int sig;
|
||||
int64_t sem;
|
||||
uint32_t ms = -1u;
|
||||
uint32_t wi, ms = -1;
|
||||
int handler_was_called;
|
||||
struct PosixThread *pt;
|
||||
if (!__ttyconf.vmin) {
|
||||
if (!__ttyconf.vtime) {
|
||||
@@ -733,39 +735,42 @@ static textwindows int WaitForConsole(struct Fd *f, sigset_t waitmask) {
|
||||
if (f->flags & _O_NONBLOCK) {
|
||||
return eagain(); // standard unix non-blocking
|
||||
}
|
||||
if (_check_cancel() == -1) return -1;
|
||||
if ((sig = __sig_get(waitmask))) {
|
||||
handler_was_called = __sig_relay(sig, SI_KERNEL, waitmask);
|
||||
if (_check_cancel() == -1) return -1;
|
||||
if (handler_was_called != 1) return -2;
|
||||
return eintr();
|
||||
}
|
||||
pt = _pthread_self();
|
||||
pt->pt_flags |= PT_RESTARTABLE;
|
||||
pt->pt_semaphore = sem = CreateSemaphore(0, 0, 1, 0);
|
||||
pthread_cleanup_push((void *)CloseHandle, (void *)sem);
|
||||
pt->pt_blkmask = waitmask;
|
||||
atomic_store_explicit(&pt->pt_blocker, PT_BLOCKER_SEM, memory_order_release);
|
||||
m = __sig_beginwait(waitmask);
|
||||
if ((rc = _check_signal(true)) != -1) {
|
||||
int64_t hands[2] = {sem, __keystroke.cin};
|
||||
if (WaitForMultipleObjects(2, hands, 0, ms) != -1u) {
|
||||
if (!(pt->pt_flags & PT_RESTARTABLE)) rc = eintr();
|
||||
rc |= _check_signal(true);
|
||||
} else {
|
||||
rc = __winerr();
|
||||
}
|
||||
}
|
||||
__sig_finishwait(m);
|
||||
atomic_store_explicit(&pt->pt_blocker, PT_BLOCKER_CPU, memory_order_release);
|
||||
pt->pt_flags &= ~PT_RESTARTABLE;
|
||||
wi = WaitForMultipleObjects(2, (int64_t[2]){__keystroke.cin, sem}, 0, ms);
|
||||
atomic_store_explicit(&pt->pt_blocker, 0, memory_order_release);
|
||||
pthread_cleanup_pop(true);
|
||||
return rc;
|
||||
if (wi == kNtWaitTimeout) return 0; // vtime elapsed
|
||||
if (wi == 0) return -2; // console data
|
||||
if (wi != 1) return __winerr(); // wait failed
|
||||
if (!(sig = __sig_get(waitmask))) return eintr();
|
||||
handler_was_called = __sig_relay(sig, SI_KERNEL, waitmask);
|
||||
if (_check_cancel() == -1) return -1;
|
||||
if (handler_was_called != 1) return -2;
|
||||
return eintr();
|
||||
}
|
||||
|
||||
static textwindows ssize_t ReadFromConsole(struct Fd *f, void *data,
|
||||
size_t size, sigset_t waitmask) {
|
||||
int rc;
|
||||
bool done = false;
|
||||
InitConsole();
|
||||
do {
|
||||
LockKeystrokes();
|
||||
IngestConsoleInput();
|
||||
done = DigestConsoleInput(data, size, &rc);
|
||||
bool done = DigestConsoleInput(data, size, &rc);
|
||||
UnlockKeystrokes();
|
||||
} while (!done && !(rc = WaitForConsole(f, waitmask)));
|
||||
if (done) return rc;
|
||||
} while ((rc = WaitForConsole(f, waitmask)) == -2);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -823,7 +828,6 @@ static textwindows ssize_t sys_read_nt2(int fd, const struct iovec *iov,
|
||||
total += rc;
|
||||
if (opt_offset != -1) opt_offset += rc;
|
||||
if (rc < iov[i].iov_len) break;
|
||||
waitmask = -1; // disable eintr/ecanceled for remaining iovecs
|
||||
}
|
||||
return total;
|
||||
} else {
|
||||
|
||||
+56
-69
@@ -36,6 +36,7 @@
|
||||
#include "libc/stdio/sysparam.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/sicode.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
#include "libc/thread/thread.h"
|
||||
@@ -67,12 +68,12 @@ sys_readwrite_nt(int fd, void *data, size_t size, ssize_t offset,
|
||||
bool32 ReadOrWriteFile(int64_t, void *, uint32_t, uint32_t *,
|
||||
struct NtOverlapped *)) {
|
||||
bool32 ok;
|
||||
uint64_t m;
|
||||
int sig = 0;
|
||||
uint32_t exchanged;
|
||||
int olderror = errno;
|
||||
bool eagained = false;
|
||||
bool eintered = false;
|
||||
bool canceled = false;
|
||||
int handler_was_called;
|
||||
struct PosixThread *pt;
|
||||
struct Fd *f = g_fds.p + fd;
|
||||
|
||||
@@ -104,73 +105,50 @@ sys_readwrite_nt(int fd, void *data, size_t size, ssize_t offset,
|
||||
}
|
||||
}
|
||||
|
||||
// managing an overlapped i/o operation is tricky to do using just
|
||||
// imperative procedural logic. its design lends itself more to be
|
||||
// something that's abstracted in an object-oriented design, which
|
||||
// easily manages the unusual lifecycles requirements of the thing
|
||||
// the game here is to not return until win32 is done w/ `overlap`
|
||||
// next we need to allow signal handlers to re-enter this function
|
||||
// while we're performing a read in the same thread. this needs to
|
||||
// be thread safe too of course. read() / write() are cancelation
|
||||
// points so pthread_cancel() might teleport the execution here to
|
||||
// pthread_exit(), so we need cleanup handlers that pthread_exit()
|
||||
// can call, pushed onto the stack, so we don't leak win32 handles
|
||||
// or worse trash the thread stack containing `overlap` that win32
|
||||
// temporarily owns while the overlapped i/o op is being performed
|
||||
// we implement a non-blocking iop by optimistically performing io
|
||||
// and then aborting the operation if win32 says it needs to block
|
||||
// with cancelation points, we need to be able to raise eintr when
|
||||
// this thread is pre-empted to run a signal handler but only when
|
||||
// that signal handler wasn't installed using this SA_RESTART flag
|
||||
// in which case read() and write() will keep going afterwards. we
|
||||
// support a second kind of eintr in cosmo/musl which is ecanceled
|
||||
// and it's mission critical that it be relayed properly, since it
|
||||
// can only be returned by a single system call in a thread's life
|
||||
// another thing we do is check if any pending signals exist, then
|
||||
// running as many of them as possible before entering a wait call
|
||||
RestartOperation:
|
||||
// signals have already been fully blocked by caller
|
||||
// perform i/o operation with atomic signal/cancel checking
|
||||
struct NtOverlapped overlap = {.hEvent = CreateEvent(0, 1, 0, 0),
|
||||
.Pointer = offset};
|
||||
struct ReadwriteResources rwc = {handle, &overlap};
|
||||
pthread_cleanup_push(UnwindReadwrite, &rwc);
|
||||
ok = ReadOrWriteFile(handle, data, size, 0, &overlap);
|
||||
if (!ok && GetLastError() == kNtErrorIoPending) {
|
||||
BlockingOperation:
|
||||
pt = _pthread_self();
|
||||
pt->pt_iohandle = handle;
|
||||
pt->pt_ioverlap = &overlap;
|
||||
pt->pt_flags |= PT_RESTARTABLE;
|
||||
atomic_store_explicit(&pt->pt_blocker, PT_BLOCKER_IO, memory_order_release);
|
||||
m = __sig_beginwait(waitmask);
|
||||
// win32 says this i/o operation needs to block
|
||||
if (f->flags & _O_NONBLOCK) {
|
||||
// abort the i/o operation if file descriptor is in non-blocking mode
|
||||
CancelIoEx(handle, &overlap);
|
||||
eagained = true;
|
||||
} else if (_check_signal(true)) {
|
||||
} else if (_check_cancel()) {
|
||||
// _check_cancel() can go three ways:
|
||||
// 1. it'll return 0 if we're fine and no thread cancelation happened
|
||||
// 2. it'll pthread_exit() and cleanup, when cancelation was deferred
|
||||
// 3. it'll return -1 and raise ECANCELED if a cancelation was masked
|
||||
CancelIoEx(handle, &overlap);
|
||||
canceled = true;
|
||||
} else if ((sig = __sig_get(waitmask))) {
|
||||
// we've dequeued a signal that was pending per caller's old sigmask
|
||||
// we can't call the signal handler until we release win32 resources
|
||||
CancelIoEx(handle, &overlap);
|
||||
if (errno == ECANCELED) {
|
||||
canceled = true;
|
||||
} else {
|
||||
eintered = true;
|
||||
}
|
||||
} else {
|
||||
// wait until i/o either completes or is canceled by another thread
|
||||
// we avoid a race condition by having a second mask for unblocking
|
||||
pt = _pthread_self();
|
||||
pt->pt_blkmask = waitmask;
|
||||
pt->pt_iohandle = handle;
|
||||
pt->pt_ioverlap = &overlap;
|
||||
atomic_store_explicit(&pt->pt_blocker, PT_BLOCKER_IO,
|
||||
memory_order_release);
|
||||
WaitForSingleObject(overlap.hEvent, -1u);
|
||||
atomic_store_explicit(&pt->pt_blocker, 0, memory_order_release);
|
||||
}
|
||||
__sig_finishwait(m);
|
||||
atomic_store_explicit(&pt->pt_blocker, PT_BLOCKER_CPU,
|
||||
memory_order_release);
|
||||
pt->pt_flags &= ~PT_RESTARTABLE;
|
||||
pt->pt_ioverlap = 0;
|
||||
pt->pt_iohandle = 0;
|
||||
ok = true;
|
||||
}
|
||||
if (ok) {
|
||||
bool32 should_wait = canceled || eagained;
|
||||
ok = GetOverlappedResult(handle, &overlap, &exchanged, should_wait);
|
||||
if (!ok && GetLastError() == kNtErrorIoIncomplete) {
|
||||
goto BlockingOperation;
|
||||
}
|
||||
ok = GetOverlappedResult(handle, &overlap, &exchanged, true);
|
||||
}
|
||||
CloseHandle(overlap.hEvent);
|
||||
pthread_cleanup_pop(false);
|
||||
CloseHandle(overlap.hEvent);
|
||||
|
||||
// if we acknowledged a pending masked mode cancelation request then
|
||||
// we must pass it to the caller immediately now that cleanup's done
|
||||
@@ -178,8 +156,16 @@ sys_readwrite_nt(int fd, void *data, size_t size, ssize_t offset,
|
||||
return ecanceled();
|
||||
}
|
||||
|
||||
// sudden success trumps interrupts and/or failed i/o abort attempts
|
||||
// plenty of code above might clobber errno, so we always restore it
|
||||
// if we removed a pending signal then we must raise it
|
||||
// it's now safe to call a signal handler that longjmps
|
||||
if (sig) {
|
||||
handler_was_called = __sig_relay(sig, SI_KERNEL, waitmask);
|
||||
if (_check_cancel() == -1) return -1;
|
||||
} else {
|
||||
handler_was_called = 0;
|
||||
}
|
||||
|
||||
// if i/o succeeded then return its result
|
||||
if (ok) {
|
||||
if (!pwriting && seekable) {
|
||||
f->pointer = offset + exchanged;
|
||||
@@ -188,23 +174,24 @@ sys_readwrite_nt(int fd, void *data, size_t size, ssize_t offset,
|
||||
return exchanged;
|
||||
}
|
||||
|
||||
// if we backed out of the i/o operation intentionally ignore errors
|
||||
if (eagained) {
|
||||
return eagain();
|
||||
}
|
||||
|
||||
// if another thread canceled our win32 i/o operation then we should
|
||||
// check and see if it was pthread_cancel() which committed the deed
|
||||
// in which case _check_cancel() can acknowledge the cancelation now
|
||||
// it's also fine to do nothing here; punt to next cancelation point
|
||||
// only raise EINTR or EAGAIN if I/O got canceled
|
||||
if (GetLastError() == kNtErrorOperationAborted) {
|
||||
if (_check_cancel() == -1) return ecanceled();
|
||||
if (!eintered && _check_signal(false)) return -1;
|
||||
}
|
||||
|
||||
// if we chose to process a pending signal earlier then we preserve
|
||||
// that original error explicitly here even though aborted == eintr
|
||||
if (eintered) {
|
||||
// raise EAGAIN if it's due to O_NONBLOCK mmode
|
||||
if (eagained) {
|
||||
return eagain();
|
||||
}
|
||||
// at this point the i/o must have been canceled due to a signal.
|
||||
// this could be because we found the signal earlier and canceled
|
||||
// ourself. otherwise it's due to a kill from another thread that
|
||||
// added something to our mask and canceled our i/o, so we check.
|
||||
if (!handler_was_called && (sig = __sig_get(waitmask))) {
|
||||
handler_was_called = __sig_relay(sig, SI_KERNEL, waitmask);
|
||||
if (_check_cancel() == -1) return -1;
|
||||
}
|
||||
// read() is @restartable unless non-SA_RESTART hands were called
|
||||
if (handler_was_called != 1) {
|
||||
goto RestartOperation;
|
||||
}
|
||||
return eintr();
|
||||
}
|
||||
|
||||
|
||||
+81
-56
@@ -85,7 +85,8 @@ textwindows bool __sig_ignored(int sig) {
|
||||
|
||||
textwindows void __sig_delete(int sig) {
|
||||
struct Dll *e;
|
||||
__sig.pending &= ~(1ull << (sig - 1));
|
||||
atomic_fetch_and_explicit(&__sig.pending, ~(1ull << (sig - 1)),
|
||||
memory_order_relaxed);
|
||||
BLOCK_SIGNALS;
|
||||
_pthread_lock();
|
||||
for (e = dll_last(_pthread_list); e; e = dll_prev(_pthread_list, e)) {
|
||||
@@ -96,12 +97,11 @@ textwindows void __sig_delete(int sig) {
|
||||
ALLOW_SIGNALS;
|
||||
}
|
||||
|
||||
static textwindows int __sig_getter(struct CosmoTib *tib, atomic_ulong *sigs) {
|
||||
static textwindows int __sig_getter(atomic_ulong *sigs, sigset_t masked) {
|
||||
int sig;
|
||||
sigset_t bit, pending, masked, deliverable;
|
||||
sigset_t bit, pending, deliverable;
|
||||
for (;;) {
|
||||
pending = atomic_load_explicit(sigs, memory_order_acquire);
|
||||
masked = atomic_load_explicit(&tib->tib_sigmask, memory_order_acquire);
|
||||
if ((deliverable = pending & ~masked)) {
|
||||
sig = _bsfl(deliverable) + 1;
|
||||
bit = 1ull << (sig - 1);
|
||||
@@ -114,10 +114,10 @@ static textwindows int __sig_getter(struct CosmoTib *tib, atomic_ulong *sigs) {
|
||||
}
|
||||
}
|
||||
|
||||
static textwindows int __sig_get(struct CosmoTib *tib) {
|
||||
textwindows int __sig_get(sigset_t masked) {
|
||||
int sig;
|
||||
if (!(sig = __sig_getter(tib, &tib->tib_sigpending))) {
|
||||
sig = __sig_getter(tib, &__sig.pending);
|
||||
if (!(sig = __sig_getter(&__get_tls()->tib_sigpending, masked))) {
|
||||
sig = __sig_getter(&__sig.pending, masked);
|
||||
}
|
||||
return sig;
|
||||
}
|
||||
@@ -154,7 +154,8 @@ static textwindows bool __sig_start(struct PosixThread *pt, int sig,
|
||||
STRACE("ignoring %G", sig);
|
||||
return false;
|
||||
}
|
||||
if (pt->tib->tib_sigmask & (1ull << (sig - 1))) {
|
||||
if (atomic_load_explicit(&pt->tib->tib_sigmask, memory_order_acquire) &
|
||||
(1ull << (sig - 1))) {
|
||||
STRACE("enqueing %G on %d", sig, _pthread_tid(pt));
|
||||
atomic_fetch_or_explicit(&pt->tib->tib_sigpending, 1ull << (sig - 1),
|
||||
memory_order_relaxed);
|
||||
@@ -196,7 +197,7 @@ textwindows int __sig_raise(int sig, int sic) {
|
||||
sigset_t blocksigs = __sighandmask[sig];
|
||||
if (!(flags & SA_NODEFER)) blocksigs |= 1ull << (sig - 1);
|
||||
ctx.uc_sigmask = atomic_fetch_or_explicit(
|
||||
&pt->tib->tib_sigmask, blocksigs, memory_order_acq_rel);
|
||||
&pt->tib->tib_sigmask, blocksigs, memory_order_acquire);
|
||||
|
||||
// call the user's signal handler
|
||||
char ssbuf[2][128];
|
||||
@@ -213,51 +214,41 @@ textwindows int __sig_raise(int sig, int sic) {
|
||||
handler_was_called |= (flags & SA_RESTART) ? 2 : 1;
|
||||
}
|
||||
sic = SI_KERNEL;
|
||||
} while ((sig = __sig_get(pt->tib)));
|
||||
} while ((sig = __sig_get(ctx.uc_sigmask)));
|
||||
return handler_was_called;
|
||||
}
|
||||
|
||||
textwindows int __sig_relay(int sig, int sic, sigset_t waitmask) {
|
||||
int handler_was_called;
|
||||
sigset_t m = __sig_begin(waitmask);
|
||||
handler_was_called = __sig_raise(sig, SI_KERNEL);
|
||||
__sig_finish(m);
|
||||
return handler_was_called;
|
||||
}
|
||||
|
||||
// cancels blocking operations being performed by signaled thread
|
||||
textwindows void __sig_cancel(struct PosixThread *pt, int sig, unsigned flags) {
|
||||
bool should_restart;
|
||||
atomic_int *blocker;
|
||||
// cancelation points need to set pt_blocker before entering a wait op
|
||||
blocker = atomic_load_explicit(&pt->pt_blocker, memory_order_acquire);
|
||||
// cancelation points should set it back to this after blocking
|
||||
// however, code that longjmps might mess it up a tolerable bit
|
||||
if (blocker == PT_BLOCKER_CPU) {
|
||||
STRACE("%G delivered to %d asynchronously", sig, _pthread_tid(pt));
|
||||
if (!blocker) {
|
||||
STRACE("%G sent to %d asynchronously", sig, _pthread_tid(pt));
|
||||
return;
|
||||
}
|
||||
// most cancelation points can be safely restarted w/o raising eintr
|
||||
should_restart = (flags & SA_RESTART) && (pt->pt_flags & PT_RESTARTABLE);
|
||||
// we can cancel another thread's overlapped i/o op after the freeze
|
||||
if (blocker == PT_BLOCKER_IO) {
|
||||
if (should_restart) {
|
||||
STRACE("%G restarting %d's i/o op", sig, _pthread_tid(pt));
|
||||
} else {
|
||||
STRACE("%G interupting %d's i/o op", sig, _pthread_tid(pt));
|
||||
CancelIoEx(pt->pt_iohandle, pt->pt_ioverlap);
|
||||
}
|
||||
STRACE("%G canceling %d's i/o", sig, _pthread_tid(pt));
|
||||
CancelIoEx(pt->pt_iohandle, pt->pt_ioverlap);
|
||||
return;
|
||||
}
|
||||
// threads can create semaphores on an as-needed basis
|
||||
if (blocker == PT_BLOCKER_SEM) {
|
||||
if (should_restart) {
|
||||
STRACE("%G restarting %d's semaphore", sig, _pthread_tid(pt));
|
||||
} else {
|
||||
STRACE("%G releasing %d's semaphore", sig, _pthread_tid(pt));
|
||||
pt->pt_flags &= ~PT_RESTARTABLE;
|
||||
ReleaseSemaphore(pt->pt_semaphore, 1, 0);
|
||||
}
|
||||
STRACE("%G releasing %d's semaphore", sig, _pthread_tid(pt));
|
||||
ReleaseSemaphore(pt->pt_semaphore, 1, 0);
|
||||
return;
|
||||
}
|
||||
// all other blocking ops that aren't overlap should use futexes
|
||||
// we force restartable futexes to churn by waking w/o releasing
|
||||
STRACE("%G waking %d's futex", sig, _pthread_tid(pt));
|
||||
if (!should_restart) {
|
||||
atomic_store_explicit(blocker, 1, memory_order_release);
|
||||
}
|
||||
WakeByAddressSingle(blocker);
|
||||
}
|
||||
|
||||
@@ -273,7 +264,7 @@ static textwindows wontreturn void __sig_tramp(struct SignalFrame *sf) {
|
||||
sigset_t blocksigs = __sighandmask[sig];
|
||||
if (!(sf->flags & SA_NODEFER)) blocksigs |= 1ull << (sig - 1);
|
||||
sf->ctx.uc_sigmask = atomic_fetch_or_explicit(&tib->tib_sigmask, blocksigs,
|
||||
memory_order_acq_rel);
|
||||
memory_order_acquire);
|
||||
|
||||
// call the user's signal handler
|
||||
char ssbuf[2][128];
|
||||
@@ -290,7 +281,7 @@ static textwindows wontreturn void __sig_tramp(struct SignalFrame *sf) {
|
||||
|
||||
// jump back into original code if there aren't any pending signals
|
||||
do {
|
||||
if (!(sig = __sig_get(tib))) {
|
||||
if (!(sig = __sig_get(sf->ctx.uc_sigmask))) {
|
||||
__sig_restore(&sf->ctx);
|
||||
}
|
||||
} while (!__sig_start(pt, sig, &sf->rva, &sf->flags));
|
||||
@@ -305,14 +296,24 @@ static textwindows wontreturn void __sig_tramp(struct SignalFrame *sf) {
|
||||
}
|
||||
}
|
||||
|
||||
// sends signal to another specific thread which is ref'd
|
||||
static int __sig_killer(struct PosixThread *pt, int sig, int sic) {
|
||||
unsigned rva = __sighandrvas[sig];
|
||||
unsigned flags = __sighandflags[sig];
|
||||
|
||||
// prepare for signal
|
||||
unsigned rva, flags;
|
||||
if (!__sig_start(pt, sig, &rva, &flags)) {
|
||||
// do nothing if signal is ignored
|
||||
if (rva == (intptr_t)SIG_IGN ||
|
||||
(rva == (intptr_t)SIG_DFL && __sig_ignored_by_default(sig))) {
|
||||
STRACE("ignoring %G", sig);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// if there's no handler then killing a thread kills the process
|
||||
if (rva == (intptr_t)SIG_DFL) {
|
||||
STRACE("terminating on %G due to no handler", sig);
|
||||
__sig_terminate(sig);
|
||||
}
|
||||
|
||||
// take control of thread
|
||||
// suspending the thread happens asynchronously
|
||||
// however getting the context blocks until it's frozen
|
||||
@@ -334,11 +335,13 @@ static int __sig_killer(struct PosixThread *pt, int sig, int sic) {
|
||||
}
|
||||
pthread_spin_unlock(&killer_lock);
|
||||
|
||||
// we can't preempt threads that masked sig or are blocked
|
||||
// we can't preempt threads that are running in win32 code
|
||||
if ((pt->tib->tib_sigmask & (1ull << (sig - 1))) ||
|
||||
// so we shall unblock the thread and let it signal itself
|
||||
if ((atomic_load_explicit(&pt->tib->tib_sigmask, memory_order_acquire) &
|
||||
(1ull << (sig - 1))) ||
|
||||
!((uintptr_t)__executable_start <= nc.Rip &&
|
||||
nc.Rip < (uintptr_t)__privileged_start)) {
|
||||
STRACE("enqueing %G on %d rip %p", sig, _pthread_tid(pt), nc.Rip);
|
||||
atomic_fetch_or_explicit(&pt->tib->tib_sigpending, 1ull << (sig - 1),
|
||||
memory_order_relaxed);
|
||||
ResumeThread(th);
|
||||
@@ -346,13 +349,14 @@ static int __sig_killer(struct PosixThread *pt, int sig, int sic) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// we're committed to delivering this signal now
|
||||
// preferring to live dangerously
|
||||
// the thread will be signaled asynchronously
|
||||
if (flags & SA_RESETHAND) {
|
||||
STRACE("resetting %G handler", sig);
|
||||
__sighandrvas[sig] = (int32_t)(intptr_t)SIG_DFL;
|
||||
}
|
||||
|
||||
// inject trampoline function call into thread
|
||||
// inject call to trampoline function into thread
|
||||
uintptr_t sp;
|
||||
if (__sig_should_use_altstack(flags, pt->tib)) {
|
||||
sp = (uintptr_t)pt->tib->tib_sigstack_addr + pt->tib->tib_sigstack_size;
|
||||
@@ -381,6 +385,7 @@ static int __sig_killer(struct PosixThread *pt, int sig, int sic) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// sends signal to another specific thread
|
||||
textwindows int __sig_kill(struct PosixThread *pt, int sig, int sic) {
|
||||
int rc;
|
||||
BLOCK_SIGNALS;
|
||||
@@ -391,6 +396,7 @@ textwindows int __sig_kill(struct PosixThread *pt, int sig, int sic) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
// sends signal to any other thread
|
||||
textwindows void __sig_generate(int sig, int sic) {
|
||||
struct Dll *e;
|
||||
struct PosixThread *pt, *mark = 0;
|
||||
@@ -406,22 +412,41 @@ textwindows void __sig_generate(int sig, int sic) {
|
||||
_pthread_lock();
|
||||
for (e = dll_first(_pthread_list); e; e = dll_next(_pthread_list, e)) {
|
||||
pt = POSIXTHREAD_CONTAINER(e);
|
||||
if (pt != _pthread_self() &&
|
||||
atomic_load_explicit(&pt->pt_status, memory_order_acquire) <
|
||||
kPosixThreadTerminated &&
|
||||
!(pt->tib->tib_sigmask & (1ull << (sig - 1)))) {
|
||||
_pthread_ref((mark = pt));
|
||||
// we don't want to signal ourself
|
||||
if (pt == _pthread_self()) continue;
|
||||
// we don't want to signal a thread that isn't running
|
||||
if (atomic_load_explicit(&pt->pt_status, memory_order_acquire) >=
|
||||
kPosixThreadTerminated) {
|
||||
continue;
|
||||
}
|
||||
// choose this thread if it isn't masking sig
|
||||
if (!(atomic_load_explicit(&pt->tib->tib_sigmask, memory_order_acquire) &
|
||||
(1ull << (sig - 1)))) {
|
||||
STRACE("generating %G by killing %d", sig, _pthread_tid(pt));
|
||||
_pthread_ref(pt);
|
||||
mark = pt;
|
||||
break;
|
||||
}
|
||||
// if a thread is blocking then we check to see if it's planning
|
||||
// to unblock our sig once the wait operation is completed; when
|
||||
// that's the case we can cancel the thread's i/o to deliver sig
|
||||
if (atomic_load_explicit(&pt->pt_blocker, memory_order_acquire) &&
|
||||
!(atomic_load_explicit(&pt->pt_blkmask, memory_order_relaxed) &
|
||||
(1ull << (sig - 1)))) {
|
||||
STRACE("generating %G by unblocking %d", sig, _pthread_tid(pt));
|
||||
_pthread_ref(pt);
|
||||
mark = pt;
|
||||
break;
|
||||
}
|
||||
}
|
||||
_pthread_unlock();
|
||||
if (mark) {
|
||||
STRACE("generating %G by killing %d", sig, _pthread_tid(mark));
|
||||
__sig_killer(mark, sig, sic);
|
||||
_pthread_unref(mark);
|
||||
} else {
|
||||
STRACE("all threads block %G so adding to pending signals of process", sig);
|
||||
__sig.pending |= 1ull << (sig - 1);
|
||||
atomic_fetch_or_explicit(&__sig.pending, 1ull << (sig - 1),
|
||||
memory_order_relaxed);
|
||||
}
|
||||
ALLOW_SIGNALS;
|
||||
}
|
||||
@@ -541,11 +566,10 @@ static void __sig_unmaskable(struct NtExceptionPointers *ep, int code, int sig,
|
||||
ucontext_t ctx = {0};
|
||||
siginfo_t si = {.si_signo = sig, .si_code = code, .si_addr = si_addr};
|
||||
_ntcontext2linux(&ctx, ep->ContextRecord);
|
||||
ctx.uc_sigmask = tib->tib_sigmask;
|
||||
tib->tib_sigmask |= __sighandmask[sig];
|
||||
if (!(flags & SA_NODEFER)) {
|
||||
tib->tib_sigmask |= 1ull << (sig - 1);
|
||||
}
|
||||
sigset_t blocksigs = __sighandmask[sig];
|
||||
if (!(flags & SA_NODEFER)) blocksigs |= 1ull << (sig - 1);
|
||||
ctx.uc_sigmask = atomic_fetch_or_explicit(&tib->tib_sigmask, blocksigs,
|
||||
memory_order_acquire);
|
||||
__sig_handler(rva)(sig, &si, &ctx);
|
||||
atomic_store_explicit(&tib->tib_sigmask, ctx.uc_sigmask,
|
||||
memory_order_release);
|
||||
@@ -618,7 +642,8 @@ __msabi textwindows dontinstrument bool32 __sig_console(uint32_t dwCtrlType) {
|
||||
// handlers were called (or `3` if both were the case).
|
||||
textwindows int __sig_check(void) {
|
||||
int sig;
|
||||
if ((sig = __sig_get(__get_tls()))) {
|
||||
if ((sig = __sig_get(atomic_load_explicit(&__get_tls()->tib_sigmask,
|
||||
memory_order_acquire)))) {
|
||||
return __sig_raise(sig, SI_KERNEL);
|
||||
} else {
|
||||
return 0;
|
||||
|
||||
@@ -25,7 +25,9 @@ bool __sig_ignored(int);
|
||||
int __sig_check(void);
|
||||
int __sig_kill(struct PosixThread *, int, int);
|
||||
int __sig_mask(int, const sigset_t *, sigset_t *);
|
||||
int __sig_relay(int, int, sigset_t);
|
||||
int __sig_raise(int, int);
|
||||
int __sig_get(sigset_t);
|
||||
void __sig_delete(int);
|
||||
void __sig_generate(int, int);
|
||||
void __sig_init(void);
|
||||
|
||||
@@ -16,9 +16,9 @@ COSMOPOLITAN_C_START_
|
||||
while (0)
|
||||
|
||||
sigset_t __sig_block(void);
|
||||
void __sig_finish(sigset_t);
|
||||
void __sig_unblock(sigset_t);
|
||||
void __sig_finishwait(sigset_t);
|
||||
sigset_t __sig_beginwait(sigset_t);
|
||||
sigset_t __sig_begin(sigset_t);
|
||||
int __sys_sigprocmask(int, const uint64_t *, uint64_t *, uint64_t);
|
||||
int sys_sigprocmask(int, const sigset_t *, sigset_t *);
|
||||
int sys_sigsuspend(const uint64_t *, uint64_t);
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
bool32 sys_isatty(int);
|
||||
char *sys_getcwd_nt(char *, size_t);
|
||||
int sys_chdir_nt(const char *);
|
||||
int sys_close_epoll_nt(int);
|
||||
int sys_dup_nt(int, int, int, int);
|
||||
|
||||
@@ -13,8 +13,6 @@ COSMOPOLITAN_C_START_
|
||||
axdx_t __sys_fork(void);
|
||||
axdx_t __sys_pipe(i32[hasatleast 2], i32);
|
||||
axdx_t sys_getpid(void);
|
||||
char *sys_getcwd(char *, u64);
|
||||
char *sys_getcwd_xnu(char *, u64);
|
||||
i32 __sys_dup3(i32, i32, i32);
|
||||
i32 __sys_execve(const char *, char *const[], char *const[]);
|
||||
i32 __sys_fcntl(i32, i32, ...);
|
||||
@@ -53,6 +51,7 @@ i32 sys_fork(void);
|
||||
i32 sys_fsync(i32);
|
||||
i32 sys_ftruncate(i32, i64, i64);
|
||||
i32 sys_getcontext(void *);
|
||||
i32 sys_getcwd(char *, u64);
|
||||
i32 sys_getpgid(i32);
|
||||
i32 sys_getppid(void);
|
||||
i32 sys_getpriority(i32, u32);
|
||||
@@ -85,9 +84,9 @@ i32 sys_posix_openpt(i32);
|
||||
i32 sys_renameat(i32, const char *, i32, const char *);
|
||||
i32 sys_sem_close(i64);
|
||||
i32 sys_sem_destroy(i64);
|
||||
i32 sys_sem_destroy(i64);
|
||||
i32 sys_sem_getvalue(i64, u32 *);
|
||||
i32 sys_sem_init(u32, i64 *);
|
||||
i32 sys_sem_destroy(i64);
|
||||
i32 sys_sem_open(const char *, int, u32, i64 *);
|
||||
i32 sys_sem_post(i64);
|
||||
i32 sys_sem_trywait(i64);
|
||||
|
||||
+1
-1
@@ -45,7 +45,7 @@ static void __tmpdir_init(void) {
|
||||
|
||||
if ((s = getenv("TMPDIR"))) {
|
||||
if (*s != '/') {
|
||||
if (!getcwd(__tmpdir.path, PATH_MAX)) {
|
||||
if (__getcwd(__tmpdir.path, PATH_MAX) == -1) {
|
||||
goto GiveUp;
|
||||
}
|
||||
strlcat(__tmpdir.path, "/", sizeof(__tmpdir.path));
|
||||
|
||||
+2
-1
@@ -19,6 +19,7 @@
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/sysv/consts/clock.h"
|
||||
#include "libc/sysv/consts/utime.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/time/time.h"
|
||||
|
||||
@@ -32,7 +33,7 @@
|
||||
* @cancelationpoint
|
||||
* @norestart
|
||||
*/
|
||||
int usleep(uint32_t micros) {
|
||||
int usleep(uint64_t micros) {
|
||||
errno_t err;
|
||||
struct timespec ts = timespec_frommicros(micros);
|
||||
err = clock_nanosleep(CLOCK_REALTIME, 0, &ts, 0);
|
||||
|
||||
@@ -49,7 +49,6 @@ static inline void RaiseSignal(int sig) {
|
||||
static textwindows ssize_t sys_write_nt_impl(int fd, void *data, size_t size,
|
||||
ssize_t offset,
|
||||
uint64_t waitmask) {
|
||||
uint64_t m;
|
||||
struct Fd *f = g_fds.p + fd;
|
||||
bool isconsole = f->kind == kFdConsole;
|
||||
|
||||
@@ -78,9 +77,7 @@ static textwindows ssize_t sys_write_nt_impl(int fd, void *data, size_t size,
|
||||
// return edquot(); /* handled by consts.sh */
|
||||
case kNtErrorBrokenPipe: // broken pipe
|
||||
case kNtErrorNoData: // closing named pipe
|
||||
m = __sig_beginwait(waitmask);
|
||||
RaiseSignal(SIGPIPE);
|
||||
__sig_finishwait(m);
|
||||
return epipe();
|
||||
case kNtErrorAccessDenied: // write doesn't return EACCESS
|
||||
return ebadf();
|
||||
@@ -111,7 +108,6 @@ static textwindows ssize_t sys_write_nt2(int fd, const struct iovec *iov,
|
||||
total += rc;
|
||||
if (opt_offset != -1) opt_offset += rc;
|
||||
if (rc < iov[i].iov_len) break;
|
||||
waitmask = -1; // disable eintr/ecanceled for remaining iovecs
|
||||
}
|
||||
return total;
|
||||
} else {
|
||||
|
||||
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
@@ -1,5 +1,5 @@
|
||||
#ifndef COSMOPOLITAN_LIBC_RUNTIME_DLFCN_H_
|
||||
#define COSMOPOLITAN_LIBC_RUNTIME_DLFCN_H_
|
||||
#ifndef COSMOPOLITAN_LIBC_DLFCN_H_
|
||||
#define COSMOPOLITAN_LIBC_DLFCN_H_
|
||||
|
||||
#define RTLD_LOCAL 0
|
||||
#define RTLD_LAZY 1
|
||||
@@ -20,4 +20,4 @@ int dl_iterate_phdr(int (*)(void *, size_t, void *), void *);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_LIBC_RUNTIME_DLFCN_H_ */
|
||||
#endif /* COSMOPOLITAN_LIBC_DLFCN_H_ */
|
||||
@@ -0,0 +1,581 @@
|
||||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2023 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/atomic.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/cosmo.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/dlopen/dlfcn.h"
|
||||
#include "libc/elf/def.h"
|
||||
#include "libc/elf/elf.h"
|
||||
#include "libc/elf/struct/auxv.h"
|
||||
#include "libc/elf/struct/ehdr.h"
|
||||
#include "libc/elf/struct/phdr.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/bits.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/nt/dll.h"
|
||||
#include "libc/nt/enum/filemapflags.h"
|
||||
#include "libc/nt/enum/pageflags.h"
|
||||
#include "libc/nt/errors.h"
|
||||
#include "libc/nt/memory.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/runtime/syslib.internal.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/stdio/sysparam.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/auxv.h"
|
||||
#include "libc/sysv/consts/map.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/prot.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
||||
/**
|
||||
* @fileoverview Cosmopolitan Dynamic Linker.
|
||||
*
|
||||
* Every program built using Cosmopolitan is statically-linked. However
|
||||
* there are some cases, e.g. GUIs and video drivers, where linking the
|
||||
* host platform libraries is desirable. So what we do in such cases is
|
||||
* launch a stub executable using the host platform's libc, and longjmp
|
||||
* back into this executable. The stub executable passes back to us the
|
||||
* platform-specific dlopen() implementation, which we shall then wrap.
|
||||
*
|
||||
* @kudos jacereda for figuring out how to do this
|
||||
*/
|
||||
|
||||
__static_yoink(".dlopen.x86_64.musl.elf");
|
||||
__static_yoink(".dlopen.x86_64.glibc.elf");
|
||||
__static_yoink(".dlopen.x86_64.freebsd.elf");
|
||||
__static_yoink(".dlopen.aarch64.glibc.elf");
|
||||
|
||||
#define XNU_RTLD_LAZY 1
|
||||
#define XNU_RTLD_NOW 2
|
||||
#define XNU_RTLD_LOCAL 4
|
||||
#define XNU_RTLD_GLOBAL 8
|
||||
|
||||
#define BEGIN_FOREIGN \
|
||||
{ \
|
||||
struct CosmoTib *cosmo_tib = __get_tls(); \
|
||||
pthread_mutex_lock(&foreign.lock); \
|
||||
__set_tls(foreign.tib)
|
||||
|
||||
#define END_FOREIGN \
|
||||
__set_tls(cosmo_tib); \
|
||||
pthread_mutex_unlock(&foreign.lock); \
|
||||
}
|
||||
|
||||
struct loaded {
|
||||
char *base;
|
||||
char *entry;
|
||||
Elf64_Ehdr eh;
|
||||
Elf64_Phdr ph[32];
|
||||
};
|
||||
|
||||
static struct {
|
||||
atomic_uint once;
|
||||
bool is_supported;
|
||||
struct CosmoTib *tib;
|
||||
pthread_mutex_t lock;
|
||||
void *(*dlopen)(const char *, int);
|
||||
void *(*dlsym)(void *, const char *);
|
||||
int (*dlclose)(void *);
|
||||
char *(*dlerror)(void);
|
||||
jmp_buf jb;
|
||||
} foreign;
|
||||
|
||||
static _Thread_local const char *last_dlerror;
|
||||
|
||||
static const unsigned align_mask = 4095;
|
||||
|
||||
static uintptr_t pgtrunc(uintptr_t x) {
|
||||
return x & ~align_mask;
|
||||
}
|
||||
|
||||
static uintptr_t pground(uintptr_t x) {
|
||||
return pgtrunc(x + align_mask);
|
||||
}
|
||||
|
||||
static unsigned pflags(unsigned x) {
|
||||
unsigned r = 0;
|
||||
if (x & PF_R) r += PROT_READ;
|
||||
if (x & PF_W) r += PROT_WRITE;
|
||||
if (x & PF_X) r += PROT_EXEC;
|
||||
return r;
|
||||
}
|
||||
|
||||
static char *elf_map(int fd, Elf64_Ehdr *ehdr, Elf64_Phdr *phdr) {
|
||||
uintptr_t minva = -1;
|
||||
uintptr_t maxva = 0;
|
||||
for (Elf64_Phdr *p = phdr; p < &phdr[ehdr->e_phnum]; p++) {
|
||||
if (p->p_type != PT_LOAD) continue;
|
||||
if (p->p_vaddr < minva) minva = p->p_vaddr;
|
||||
if (p->p_vaddr + p->p_memsz > maxva) maxva = p->p_vaddr + p->p_memsz;
|
||||
}
|
||||
minva = pgtrunc(minva);
|
||||
maxva = pground(maxva);
|
||||
uint8_t *base = __sys_mmap(0, maxva - minva, PROT_NONE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0, 0);
|
||||
if (base == MAP_FAILED) return MAP_FAILED;
|
||||
__sys_munmap(base, maxva - minva);
|
||||
for (Elf64_Phdr *p = phdr; p < &phdr[ehdr->e_phnum]; p++) {
|
||||
if (p->p_type != PT_LOAD) continue;
|
||||
uintptr_t off = p->p_vaddr & align_mask;
|
||||
uint8_t *start = base;
|
||||
start += pgtrunc(p->p_vaddr);
|
||||
size_t sz = pground(p->p_memsz + off);
|
||||
uint8_t *m = __sys_mmap(start, sz, PROT_WRITE,
|
||||
MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0, 0);
|
||||
if (m == MAP_FAILED) return MAP_FAILED;
|
||||
ssize_t rr = pread(fd, m + off, p->p_filesz, p->p_offset);
|
||||
if (rr != (ssize_t)p->p_filesz) return MAP_FAILED;
|
||||
sys_mprotect(m, sz, pflags(p->p_flags));
|
||||
}
|
||||
return (void *)base;
|
||||
}
|
||||
|
||||
static int elf_open(const char *file) {
|
||||
return open(file, O_RDONLY | O_CLOEXEC);
|
||||
}
|
||||
|
||||
static bool elf_slurp(struct loaded *l, int fd, const char *file) {
|
||||
if (pread(fd, &l->eh, 64, 0) != 64) return false;
|
||||
if (!IsElf64Binary(&l->eh, 64)) return false;
|
||||
if (l->eh.e_phnum > ARRAYLEN(l->ph)) return false;
|
||||
int bytes = l->eh.e_phnum * sizeof(l->ph[0]);
|
||||
if (pread(fd, l->ph, bytes, l->eh.e_phoff) != bytes) return false;
|
||||
l->entry = (char *)l->eh.e_entry;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool elf_load(struct loaded *l, const char *file) {
|
||||
int fd;
|
||||
if ((fd = elf_open(file)) == -1) return false;
|
||||
if (!elf_slurp(l, fd, file)) {
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
if ((l->base = elf_map(fd, &l->eh, l->ph)) == MAP_FAILED) {
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
l->entry += (uintptr_t)l->base;
|
||||
close(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool elf_interp(char *buf, size_t bsz, const char *file) {
|
||||
int fd;
|
||||
if ((fd = elf_open(file)) == -1) return false;
|
||||
struct loaded l;
|
||||
if (!elf_slurp(&l, fd, file)) {
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
for (unsigned i = 0; i < l.eh.e_phnum; i++) {
|
||||
if (l.ph[i].p_type == PT_INTERP) {
|
||||
if (l.ph[i].p_filesz >= bsz ||
|
||||
pread(fd, buf, l.ph[i].p_filesz, l.ph[i].p_offset) !=
|
||||
l.ph[i].p_filesz) {
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
close(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
static long *push_strs(long *sp, char **list, int count) {
|
||||
*--sp = 0;
|
||||
while (count) *--sp = (long)list[--count];
|
||||
return sp;
|
||||
}
|
||||
|
||||
static void elf_exec(const char *file, const char *iinterp, int argc,
|
||||
char **argv, char **envp) {
|
||||
|
||||
// get elf information
|
||||
struct loaded prog;
|
||||
if (!elf_load(&prog, file)) return;
|
||||
struct loaded interp;
|
||||
if (!elf_load(&interp, iinterp)) return;
|
||||
|
||||
// count environment variables
|
||||
int envc = 0;
|
||||
while (envp[envc]) envc++;
|
||||
|
||||
// create a stack for the new process just beneath our stack
|
||||
long *sp = (long *)__builtin_frame_address(0) - 256;
|
||||
|
||||
// push auxiliary values
|
||||
*--sp = 0;
|
||||
Elf64_auxv_t *av;
|
||||
unsigned long key, val;
|
||||
for (av = (Elf64_auxv_t *)__auxv; (key = av->a_type); ++av) {
|
||||
val = av->a_un.a_val;
|
||||
if (key == AT_PHDR) val = (long)(prog.base + prog.eh.e_phoff);
|
||||
if (key == AT_PHENT) val = prog.eh.e_phentsize;
|
||||
if (key == AT_PHNUM) val = prog.eh.e_phnum;
|
||||
if (key == AT_PAGESZ) val = 0x1000;
|
||||
if (key == AT_BASE) val = (long)interp.base;
|
||||
if (key == AT_FLAGS) val = 0;
|
||||
if (key == AT_ENTRY) val = (long)prog.entry;
|
||||
if (key == AT_EXECFN) val = (long)argv[0];
|
||||
*--sp = val;
|
||||
*--sp = key;
|
||||
}
|
||||
|
||||
// push main() arguments
|
||||
sp = push_strs(sp, envp, envc);
|
||||
sp = push_strs(sp, argv, argc);
|
||||
*--sp = argc;
|
||||
|
||||
#ifdef __x86_64__
|
||||
struct ps_strings {
|
||||
char **argv;
|
||||
int argc;
|
||||
char **envp;
|
||||
int envc;
|
||||
} pss = {argv, argc, envp, envc};
|
||||
asm volatile("mov\t%2,%%rsp\n\t"
|
||||
"jmpq\t*%1"
|
||||
: /* no outputs */
|
||||
: "D"(IsFreebsd() ? sp : 0), "S"(interp.entry), "d"(sp),
|
||||
"b"(IsNetbsd() ? &pss : 0)
|
||||
: "memory");
|
||||
__builtin_unreachable();
|
||||
#elif defined(__aarch64__)
|
||||
register long x0 asm("x0") = IsFreebsd() ? (long)sp : 0;
|
||||
register long x9 asm("x9") = (long)sp;
|
||||
register long x16 asm("x16") = (long)interp.entry;
|
||||
asm volatile("mov\tsp,x9\n\t"
|
||||
"br\tx16"
|
||||
: /* no outputs */
|
||||
: "r"(x0), "r"(x9), "r"(x16)
|
||||
: "memory");
|
||||
__builtin_unreachable();
|
||||
#else
|
||||
#error "unsupported architecture"
|
||||
#endif
|
||||
}
|
||||
|
||||
static wontreturn void foreign_helper(void **p) {
|
||||
foreign.dlopen = p[0];
|
||||
foreign.dlsym = p[1];
|
||||
foreign.dlclose = p[2];
|
||||
foreign.dlerror = p[3];
|
||||
longjmp(foreign.jb, 1);
|
||||
}
|
||||
|
||||
static void foreign_setup(void) {
|
||||
char interp[256] = {0};
|
||||
if (!elf_interp(interp, sizeof(interp), "/usr/bin/env")) return;
|
||||
const char *dlopen_helper = 0;
|
||||
#ifdef __x86_64__
|
||||
if (IsFreebsd()) {
|
||||
dlopen_helper = "/zip/.dlopen.x86_64.freebsd.elf";
|
||||
} else if (IsLinux()) {
|
||||
if (fileexists("/lib64/ld-linux-x86-64.so.2")) {
|
||||
dlopen_helper = "/zip/.dlopen.x86_64.glibc.elf";
|
||||
} else {
|
||||
dlopen_helper = "/zip/.dlopen.x86_64.musl.elf";
|
||||
}
|
||||
}
|
||||
#elif defined(__aarch64__)
|
||||
if (IsLinux()) {
|
||||
dlopen_helper = "/zip/.dlopen.aarch64.glibc.elf";
|
||||
}
|
||||
#endif
|
||||
if (!dlopen_helper) {
|
||||
return; // this platform isn't supported yet
|
||||
}
|
||||
struct CosmoTib *cosmo_tib = __get_tls();
|
||||
if (!setjmp(foreign.jb)) {
|
||||
elf_exec(dlopen_helper, interp, 2,
|
||||
(char *[]){
|
||||
program_invocation_name,
|
||||
(char *)foreign_helper,
|
||||
NULL,
|
||||
},
|
||||
environ);
|
||||
return; // if it returns then it failed
|
||||
}
|
||||
foreign.tib = __get_tls();
|
||||
__set_tls(cosmo_tib);
|
||||
foreign.is_supported = true;
|
||||
}
|
||||
|
||||
static bool foreign_init(void) {
|
||||
bool res;
|
||||
cosmo_once(&foreign.once, foreign_setup);
|
||||
if (!(res = foreign.is_supported)) {
|
||||
last_dlerror = "dlopen() isn't supported on this platform";
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int dlclose_nt(void *handle) {
|
||||
int res;
|
||||
if (FreeLibrary((uintptr_t)handle)) {
|
||||
res = 0;
|
||||
} else {
|
||||
last_dlerror = "FreeLibrary() failed";
|
||||
res = -1;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static void *dlopen_nt(const char *path, int mode) {
|
||||
int n;
|
||||
uintptr_t handle;
|
||||
char16_t path16[PATH_MAX + 2];
|
||||
if (mode & ~(RTLD_LOCAL | RTLD_LAZY | RTLD_NOW)) {
|
||||
last_dlerror = "invalid mode";
|
||||
return 0;
|
||||
}
|
||||
if ((n = __mkntpath(path, path16)) == -1) {
|
||||
last_dlerror = "path invalid";
|
||||
return 0;
|
||||
}
|
||||
if (n > 3 && //
|
||||
path16[n - 3] == '.' && //
|
||||
path16[n - 2] == 's' && //
|
||||
path16[n - 1] == 'o') {
|
||||
path16[n - 2] = 'd';
|
||||
path16[n - 1] = 'l';
|
||||
path16[n + 0] = 'l';
|
||||
path16[n + 1] = 0;
|
||||
}
|
||||
if (!(handle = LoadLibrary(path16))) {
|
||||
last_dlerror = "library not found";
|
||||
}
|
||||
return (void *)handle;
|
||||
}
|
||||
|
||||
static char *dlsym_nt_alloc_rwx_block(void) {
|
||||
uintptr_t h;
|
||||
char *p = 0;
|
||||
if ((h = CreateFileMapping(-1, 0, kNtPageExecuteReadwrite, 0, 65536, 0)) &&
|
||||
(p = MapViewOfFileEx(h, kNtFileMapWrite | kNtFileMapExecute, 0, 0, 65536,
|
||||
0))) {
|
||||
WRITE32LE(p, 4); // store used index
|
||||
} else {
|
||||
last_dlerror = "out of memory";
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
static void *dlsym_nt_alloc_rwx(size_t n) {
|
||||
void *res;
|
||||
static char *block;
|
||||
pthread_mutex_lock(&foreign.lock);
|
||||
if (!block || READ32LE(block) + n > 65536) {
|
||||
if (!(block = dlsym_nt_alloc_rwx_block())) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
res = block + READ32LE(block);
|
||||
WRITE32LE(block, READ32LE(block) + n);
|
||||
pthread_mutex_unlock(&foreign.lock);
|
||||
return res;
|
||||
}
|
||||
|
||||
static void *dlsym_nt_thunk(void *func, void *tramp) {
|
||||
unsigned char *code;
|
||||
if (!(code = dlsym_nt_alloc_rwx(27))) return 0;
|
||||
// push %rbp
|
||||
code[0] = 0x55;
|
||||
// mov %rsp,%rbp
|
||||
code[1] = 0x48;
|
||||
code[2] = 0x89;
|
||||
code[3] = 0xe5;
|
||||
// movabs $func,%rax
|
||||
code[4] = 0x48;
|
||||
code[5] = 0xb8;
|
||||
WRITE64LE(code + 6, (uintptr_t)func);
|
||||
// movabs $tramp,%r10
|
||||
code[14] = 0x49;
|
||||
code[15] = 0xba;
|
||||
WRITE64LE(code + 16, (uintptr_t)tramp);
|
||||
// jmp *%r10
|
||||
code[24] = 0x41;
|
||||
code[25] = 0xff;
|
||||
code[26] = 0xe2;
|
||||
return code;
|
||||
}
|
||||
|
||||
static void *dlsym_nt(void *handle, const char *name) {
|
||||
long __sysv2nt14();
|
||||
void *x64_abi_func;
|
||||
void *sysv_abi_func = 0;
|
||||
if ((x64_abi_func = GetProcAddress((uintptr_t)handle, name))) {
|
||||
sysv_abi_func = dlsym_nt_thunk(x64_abi_func, __sysv2nt14);
|
||||
} else {
|
||||
last_dlerror = "symbol not found";
|
||||
}
|
||||
return sysv_abi_func;
|
||||
}
|
||||
|
||||
static void *dlopen_silicon(const char *path, int mode) {
|
||||
int n;
|
||||
int xnu_mode = 0;
|
||||
char path2[PATH_MAX + 5];
|
||||
if (mode & ~(RTLD_LOCAL | RTLD_LAZY | RTLD_NOW | RTLD_GLOBAL)) {
|
||||
xnu_mode = -1; // punt error to system dlerror() impl
|
||||
}
|
||||
if (!(mode & RTLD_GLOBAL)) {
|
||||
xnu_mode |= XNU_RTLD_LOCAL; // unlike Linux, XNU defaults to RTLD_GLOBAL
|
||||
}
|
||||
if (mode & RTLD_NOW) {
|
||||
xnu_mode |= XNU_RTLD_NOW;
|
||||
}
|
||||
if (mode & RTLD_LAZY) {
|
||||
xnu_mode |= XNU_RTLD_LAZY;
|
||||
}
|
||||
if ((n = strlen(path)) < PATH_MAX && n > 3 && //
|
||||
path[n - 3] == '.' && //
|
||||
path[n - 2] == 's' && //
|
||||
path[n - 1] == 'o') {
|
||||
memcpy(path2, path, n);
|
||||
path2[n - 2] = 'd';
|
||||
path2[n - 1] = 'y';
|
||||
path2[n + 0] = 'l';
|
||||
path2[n + 1] = 'i';
|
||||
path2[n + 2] = 'b';
|
||||
path2[n + 3] = 0;
|
||||
path = path2;
|
||||
}
|
||||
return __syslib->__dlopen(path, xnu_mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens dynamic shared object using host platform libc.
|
||||
*
|
||||
* If a `path` ending with `.so` is passed on Windows or MacOS, then
|
||||
* this wrapper will automatically change it to `.dll` or `.dylib` to
|
||||
* increase its chance of successfully loading.
|
||||
*
|
||||
* @param mode is a bitmask that can contain:
|
||||
* - `RTLD_LOCAL` (default)
|
||||
* - `RTLD_GLOBAL` (not supported on Windows)
|
||||
* - `RTLD_LAZY`
|
||||
* - `RTLD_NOW`
|
||||
* @return dso handle, or NULL w/ dlerror()
|
||||
*/
|
||||
void *dlopen(const char *path, int mode) {
|
||||
void *res;
|
||||
if (IsWindows()) {
|
||||
res = dlopen_nt(path, mode);
|
||||
} else if (IsXnuSilicon()) {
|
||||
res = dlopen_silicon(path, mode);
|
||||
} else if (IsXnu()) {
|
||||
last_dlerror = "dlopen() isn't supported on x86-64 MacOS";
|
||||
res = 0;
|
||||
} else if (foreign_init()) {
|
||||
BEGIN_FOREIGN;
|
||||
res = foreign.dlopen(path, mode);
|
||||
END_FOREIGN;
|
||||
} else {
|
||||
res = 0;
|
||||
}
|
||||
STRACE("dlopen(%#s, %d) → %p", path, mode, res);
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains address of symbol from dynamic shared object.
|
||||
*
|
||||
* On Windows you can only use this to lookup function addresses.
|
||||
* Returned functions are trampolined to conform to System V ABI.
|
||||
*
|
||||
* @param handle was opened by dlopen()
|
||||
* @return address of symbol, or NULL w/ dlerror()
|
||||
*/
|
||||
void *dlsym(void *handle, const char *name) {
|
||||
void *res;
|
||||
if (IsWindows()) {
|
||||
res = dlsym_nt(handle, name);
|
||||
} else if (IsXnuSilicon()) {
|
||||
res = __syslib->__dlsym(handle, name);
|
||||
} else if (IsXnu()) {
|
||||
last_dlerror = "dlopen() isn't supported on x86-64 MacOS";
|
||||
res = 0;
|
||||
} else if (foreign_init()) {
|
||||
BEGIN_FOREIGN;
|
||||
res = foreign.dlsym(handle, name);
|
||||
END_FOREIGN;
|
||||
} else {
|
||||
res = 0;
|
||||
}
|
||||
STRACE("dlsym(%p, %#s) → %p", handle, name, res);
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes dynamic shared object.
|
||||
*
|
||||
* @param handle was opened by dlopen()
|
||||
* @return 0 on success, or -1 w/ dlerror()
|
||||
*/
|
||||
int dlclose(void *handle) {
|
||||
int res;
|
||||
if (IsWindows()) {
|
||||
res = dlclose_nt(handle);
|
||||
} else if (IsXnuSilicon()) {
|
||||
res = __syslib->__dlclose(handle);
|
||||
} else if (IsXnu()) {
|
||||
last_dlerror = "dlopen() isn't supported on x86-64 MacOS";
|
||||
res = -1;
|
||||
} else if (foreign_init()) {
|
||||
BEGIN_FOREIGN;
|
||||
res = foreign.dlclose(handle);
|
||||
END_FOREIGN;
|
||||
} else {
|
||||
res = -1;
|
||||
}
|
||||
STRACE("dlclose(%p) → %d", handle, res);
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns string describing last dlopen/dlsym/dlclose error.
|
||||
*/
|
||||
char *dlerror(void) {
|
||||
char *res;
|
||||
if (IsXnuSilicon()) {
|
||||
res = __syslib->__dlerror();
|
||||
} else if (IsWindows() || IsXnu()) {
|
||||
res = (char *)last_dlerror;
|
||||
} else if (foreign_init()) {
|
||||
BEGIN_FOREIGN;
|
||||
res = foreign.dlerror();
|
||||
END_FOREIGN;
|
||||
} else {
|
||||
res = (char *)last_dlerror;
|
||||
}
|
||||
STRACE("dlerror() → %#s", res);
|
||||
return res;
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐
|
||||
#───vi: set et ft=make ts=8 tw=8 fenc=utf-8 :vi───────────────────────┘
|
||||
|
||||
PKGS += LIBC_DLOPEN
|
||||
|
||||
LIBC_DLOPEN_ARTIFACTS += LIBC_DLOPEN_A
|
||||
LIBC_DLOPEN = $(LIBC_DLOPEN_A_DEPS) $(LIBC_DLOPEN_A)
|
||||
LIBC_DLOPEN_A = o/$(MODE)/libc/dlopen/dlopen.a
|
||||
LIBC_DLOPEN_A_FILES := $(wildcard libc/dlopen/*)
|
||||
LIBC_DLOPEN_A_HDRS = $(filter %.h,$(LIBC_DLOPEN_A_FILES))
|
||||
LIBC_DLOPEN_A_SRCS_S = $(filter %.S,$(LIBC_DLOPEN_A_FILES))
|
||||
LIBC_DLOPEN_A_SRCS_C = $(filter %.c,$(LIBC_DLOPEN_A_FILES))
|
||||
|
||||
LIBC_DLOPEN_A_SRCS = \
|
||||
$(LIBC_DLOPEN_A_SRCS_S) \
|
||||
$(LIBC_DLOPEN_A_SRCS_C)
|
||||
|
||||
LIBC_DLOPEN_DSOS = \
|
||||
o/$(MODE)/libc/dlopen/.dlopen.aarch64.glibc.elf.zip.o \
|
||||
o/$(MODE)/libc/dlopen/.dlopen.x86_64.freebsd.elf.zip.o \
|
||||
o/$(MODE)/libc/dlopen/.dlopen.x86_64.glibc.elf.zip.o \
|
||||
o/$(MODE)/libc/dlopen/.dlopen.x86_64.musl.elf.zip.o
|
||||
|
||||
LIBC_DLOPEN_A_OBJS = \
|
||||
$(LIBC_DLOPEN_A_SRCS_S:%.S=o/$(MODE)/%.o) \
|
||||
$(LIBC_DLOPEN_A_SRCS_C:%.c=o/$(MODE)/%.o) \
|
||||
$(LIBC_DLOPEN_DSOS)
|
||||
|
||||
LIBC_DLOPEN_A_CHECKS = \
|
||||
$(LIBC_DLOPEN_A).pkg \
|
||||
$(LIBC_DLOPEN_A_HDRS:%=o/$(MODE)/%.ok)
|
||||
|
||||
LIBC_DLOPEN_A_DIRECTDEPS = \
|
||||
LIBC_CALLS \
|
||||
LIBC_ELF \
|
||||
LIBC_INTRIN \
|
||||
LIBC_NEXGEN32E \
|
||||
LIBC_NT_KERNEL32 \
|
||||
LIBC_RUNTIME \
|
||||
LIBC_SYSV \
|
||||
LIBC_SYSV_CALLS \
|
||||
LIBC_STR \
|
||||
|
||||
LIBC_DLOPEN_A_DEPS := \
|
||||
$(call uniq,$(foreach x,$(LIBC_DLOPEN_A_DIRECTDEPS),$($(x))))
|
||||
|
||||
$(LIBC_DLOPEN_A): \
|
||||
libc/dlopen/ \
|
||||
$(LIBC_DLOPEN_A).pkg \
|
||||
$(LIBC_DLOPEN_A_OBJS)
|
||||
|
||||
$(LIBC_DLOPEN_A).pkg: \
|
||||
$(LIBC_DLOPEN_A_OBJS) \
|
||||
$(foreach x,$(LIBC_DLOPEN_A_DIRECTDEPS),$($(x)_A).pkg)
|
||||
|
||||
$(LIBC_DLOPEN_A_OBJS): private \
|
||||
COPTS += \
|
||||
-Wframe-larger-than=4096 \
|
||||
-Walloca-larger-than=4096
|
||||
|
||||
$(LIBC_DLOPEN_DSOS): private ZIPOBJ_FLAGS += -B
|
||||
|
||||
LIBC_DLOPEN_LIBS = $(foreach x,$(LIBC_DLOPEN_ARTIFACTS),$($(x)))
|
||||
LIBC_DLOPEN_SRCS = $(foreach x,$(LIBC_DLOPEN_ARTIFACTS),$($(x)_SRCS))
|
||||
LIBC_DLOPEN_HDRS = $(foreach x,$(LIBC_DLOPEN_ARTIFACTS),$($(x)_HDRS))
|
||||
LIBC_DLOPEN_CHECKS = $(foreach x,$(LIBC_DLOPEN_ARTIFACTS),$($(x)_CHECKS))
|
||||
LIBC_DLOPEN_OBJS = $(foreach x,$(LIBC_DLOPEN_ARTIFACTS),$($(x)_OBJS))
|
||||
$(LIBC_DLOPEN_OBJS): $(BUILD_FILES) libc/dlopen/dlopen.mk
|
||||
|
||||
.PHONY: o/$(MODE)/libc/dlopen
|
||||
o/$(MODE)/libc/dlopen: $(LIBC_DLOPEN_CHECKS)
|
||||
@@ -0,0 +1,10 @@
|
||||
#include <dlfcn.h>
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
return ((int (*)(void *))argv[1])((void *[]){
|
||||
dlopen,
|
||||
dlsym,
|
||||
dlclose,
|
||||
dlerror,
|
||||
});
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
#define TRY_AGAIN 2
|
||||
#define NO_RECOVERY 3
|
||||
#define NO_DATA 4
|
||||
#define NO_ADDRESS NO_DATA
|
||||
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#define __COSMOPOLITAN_MAJOR__ 3
|
||||
#define __COSMOPOLITAN_MINOR__ 0
|
||||
#define __COSMOPOLITAN_PATCH__ 1
|
||||
#define __COSMOPOLITAN_PATCH__ 2
|
||||
#define __COSMOPOLITAN__ \
|
||||
(100000000 * __COSMOPOLITAN_MAJOR__ + 1000000 * __COSMOPOLITAN_MINOR__ + \
|
||||
__COSMOPOLITAN_PATCH__)
|
||||
|
||||
+2
-2
@@ -48,11 +48,11 @@ void __sig_unblock(sigset_t m) {
|
||||
}
|
||||
}
|
||||
|
||||
textwindows sigset_t __sig_beginwait(sigset_t waitmask) {
|
||||
textwindows sigset_t __sig_begin(sigset_t waitmask) {
|
||||
return atomic_exchange_explicit(&__get_tls()->tib_sigmask, waitmask,
|
||||
memory_order_acquire);
|
||||
}
|
||||
|
||||
textwindows void __sig_finishwait(sigset_t m) {
|
||||
textwindows void __sig_finish(sigset_t m) {
|
||||
atomic_store_explicit(&__get_tls()->tib_sigmask, m, memory_order_release);
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
#ifndef _DLFCN_H
|
||||
#define _DLFCN_H
|
||||
#include "libc/runtime/dlfcn.h"
|
||||
#include "libc/dlopen/dlfcn.h"
|
||||
#endif /* _DLFCN_H */
|
||||
@@ -0,0 +1,4 @@
|
||||
#ifndef _IFADDRS_H
|
||||
#define _IFADDRS_H
|
||||
#include "libc/sock/ifaddrs.h"
|
||||
#endif /* _IFADDRS_H */
|
||||
@@ -16,6 +16,7 @@ endif
|
||||
.PHONY: o/$(MODE)/libc
|
||||
o/$(MODE)/libc: o/$(MODE)/libc/calls \
|
||||
o/$(MODE)/libc/crt \
|
||||
o/$(MODE)/libc/dlopen \
|
||||
o/$(MODE)/libc/dns \
|
||||
o/$(MODE)/libc/elf \
|
||||
o/$(MODE)/libc/fmt \
|
||||
|
||||
+1
-1
@@ -213,7 +213,7 @@ skip_readlink:
|
||||
output[q] = 0;
|
||||
|
||||
if (output[0] != '/') {
|
||||
if (!getcwd(stack, sizeof(stack))) return 0;
|
||||
if (__getcwd(stack, sizeof(stack)) == -1) return 0;
|
||||
l = strlen(stack);
|
||||
/* Cancel any initial .. components. */
|
||||
p = 0;
|
||||
|
||||
+6
-4
@@ -305,9 +305,11 @@ static int Chmod(void) {
|
||||
}
|
||||
|
||||
static int Pwd(void) {
|
||||
char path[PATH_MAX + 2];
|
||||
if (getcwd(path, PATH_MAX)) {
|
||||
strlcat(path, "\n", sizeof(path));
|
||||
int got;
|
||||
char path[PATH_MAX];
|
||||
if ((got = __getcwd(path, PATH_MAX - 1)) != -1) {
|
||||
path[got - 1] = '\n';
|
||||
path[got] = 0;
|
||||
Write(1, path);
|
||||
return 0;
|
||||
} else {
|
||||
@@ -790,7 +792,7 @@ static const char *GetVar(const char *key) {
|
||||
} else if (key[0] == '?' && !key[1]) {
|
||||
return IntToStr(exitstatus);
|
||||
} else if (!strcmp(key, "PWD")) {
|
||||
npassert(getcwd(vbuf, sizeof(vbuf)));
|
||||
npassert(__getcwd(vbuf, sizeof(vbuf)) != -1);
|
||||
return vbuf;
|
||||
} else if (!strcmp(key, "UID")) {
|
||||
FormatInt32(vbuf, getuid());
|
||||
|
||||
@@ -127,7 +127,11 @@ int sys_execve(const char *prog, char *const argv[], char *const envp[]) {
|
||||
shargs[1] = (char *)"-";
|
||||
shargs[2] = (char *)prog;
|
||||
memcpy(shargs + 3, argv, (argc + 1) * sizeof(char *));
|
||||
RetryExecve("/usr/bin/ape", shargs, envp);
|
||||
if (IsXnuSilicon()) {
|
||||
RetryExecve("/usr/local/bin/ape", shargs, envp);
|
||||
} else {
|
||||
RetryExecve("/usr/bin/ape", shargs, envp);
|
||||
}
|
||||
char *buf = alloca(PATH_MAX);
|
||||
const char *name = "/.ape-" APE_VERSION_STR;
|
||||
InitExecve();
|
||||
|
||||
+12
-1
@@ -91,7 +91,18 @@ vfork:
|
||||
ldr w0,[x0,#:lo12:__hostos]
|
||||
tbz x0,3,1f // bit 3 is xnu
|
||||
b fork // which doesn't support vfork()
|
||||
1: mov x8,#220 // __NR_clone
|
||||
1:
|
||||
|
||||
#ifdef SYSDEBUG
|
||||
stp x29,x30,[sp,-16]!
|
||||
adrp x0,.Llog
|
||||
add x0,x0,:lo12:.Llog
|
||||
mov x29,sp
|
||||
bl __stracef
|
||||
ldp x29,x30,[sp],16
|
||||
#endif
|
||||
|
||||
mov x8,#220 // __NR_clone
|
||||
mov x0,#0x4111 // SIGCHLD | CLONE_VM | CLONE_VFORK
|
||||
mov x1,#0
|
||||
svc 0
|
||||
|
||||
@@ -130,11 +130,11 @@ WaitMore:
|
||||
SpuriousWakeup:
|
||||
++*wv;
|
||||
pthread_cleanup_push(UnwindWaiterCount, wv);
|
||||
m = __sig_beginwait(waitmask);
|
||||
m = __sig_begin(waitmask);
|
||||
if ((rc = _check_signal(true)) != -1) {
|
||||
rc = nsync_cv_wait_with_deadline(cv, &__proc.lock, deadline, 0);
|
||||
}
|
||||
__sig_finishwait(m);
|
||||
__sig_finish(m);
|
||||
pthread_cleanup_pop(true);
|
||||
if (rc == -1) return -1;
|
||||
if (rc == ETIMEDOUT) goto WaitMore;
|
||||
|
||||
@@ -12,7 +12,7 @@ COSMOPOLITAN_C_START_
|
||||
*/
|
||||
|
||||
#define SYSLIB_MAGIC ('s' | 'l' << 8 | 'i' << 16 | 'b' << 24)
|
||||
#define SYSLIB_VERSION 4
|
||||
#define SYSLIB_VERSION 6
|
||||
|
||||
typedef uint64_t dispatch_time_t;
|
||||
typedef uint64_t dispatch_semaphore_t;
|
||||
@@ -70,6 +70,11 @@ struct Syslib {
|
||||
long (*__sem_trywait)(int *);
|
||||
long (*__getrlimit)(int, void *);
|
||||
long (*__setrlimit)(int, const void *);
|
||||
// v6 (2023-11-03)
|
||||
void *(*__dlopen)(const char *, int);
|
||||
void *(*__dlsym)(void *, const char *);
|
||||
int (*__dlclose)(void *);
|
||||
char *(*__dlerror)(void);
|
||||
};
|
||||
|
||||
extern struct Syslib *__syslib;
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2023 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/sock/ifaddrs.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/sock/sock.h"
|
||||
#include "libc/sock/struct/ifconf.h"
|
||||
#include "libc/sock/struct/ifreq.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/af.h"
|
||||
#include "libc/sysv/consts/iff.h"
|
||||
#include "libc/sysv/consts/sio.h"
|
||||
#include "libc/sysv/consts/sock.h"
|
||||
|
||||
struct IfAddr {
|
||||
struct ifaddrs ifaddrs;
|
||||
char name[IFNAMSIZ];
|
||||
struct sockaddr_in addr;
|
||||
struct sockaddr_in netmask;
|
||||
struct sockaddr_in bstaddr;
|
||||
};
|
||||
|
||||
/**
|
||||
* Frees network interface address list.
|
||||
*/
|
||||
void freeifaddrs(struct ifaddrs *ifp) {
|
||||
struct ifaddrs *next;
|
||||
while (ifp) {
|
||||
next = ifp->ifa_next;
|
||||
free(ifp);
|
||||
ifp = next;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets network interface address list.
|
||||
*
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
* @see tool/viz/getifaddrs.c for example code
|
||||
*/
|
||||
int getifaddrs(struct ifaddrs **out_ifpp) {
|
||||
int rc = -1;
|
||||
int fd;
|
||||
if ((fd = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0)) != -1) {
|
||||
char *data;
|
||||
size_t size;
|
||||
if ((data = malloc((size = 16384)))) {
|
||||
struct ifconf conf;
|
||||
conf.ifc_buf = data;
|
||||
conf.ifc_len = size;
|
||||
if (!ioctl(fd, SIOCGIFCONF, &conf)) {
|
||||
struct ifaddrs *res = 0;
|
||||
for (struct ifreq *ifr = (struct ifreq *)data;
|
||||
(char *)ifr < data + conf.ifc_len; ++ifr) {
|
||||
if (ifr->ifr_addr.sa_family != AF_INET) {
|
||||
continue; // TODO(jart): IPv6 support
|
||||
}
|
||||
struct IfAddr *addr;
|
||||
if ((addr = calloc(1, sizeof(struct IfAddr)))) {
|
||||
memcpy(addr->name, ifr->ifr_name, IFNAMSIZ);
|
||||
addr->ifaddrs.ifa_name = addr->name;
|
||||
memcpy(&addr->addr, &ifr->ifr_addr, sizeof(struct sockaddr_in));
|
||||
addr->ifaddrs.ifa_addr = (struct sockaddr *)&addr->addr;
|
||||
addr->ifaddrs.ifa_netmask = (struct sockaddr *)&addr->netmask;
|
||||
if (!ioctl(fd, SIOCGIFFLAGS, ifr)) {
|
||||
addr->ifaddrs.ifa_flags = ifr->ifr_flags;
|
||||
}
|
||||
if (!ioctl(fd, SIOCGIFNETMASK, ifr)) {
|
||||
memcpy(&addr->netmask, &ifr->ifr_addr,
|
||||
sizeof(struct sockaddr_in));
|
||||
}
|
||||
unsigned long op;
|
||||
if (addr->ifaddrs.ifa_flags & IFF_BROADCAST) {
|
||||
op = SIOCGIFBRDADDR;
|
||||
} else if (addr->ifaddrs.ifa_flags & IFF_POINTOPOINT) {
|
||||
op = SIOCGIFDSTADDR;
|
||||
} else {
|
||||
op = 0;
|
||||
}
|
||||
if (op && !ioctl(fd, op, ifr)) {
|
||||
memcpy(&addr->bstaddr, &ifr->ifr_addr,
|
||||
sizeof(struct sockaddr_in));
|
||||
addr->ifaddrs.ifa_broadaddr = // is union'd w/ ifu_dstaddr
|
||||
(struct sockaddr *)&addr->bstaddr;
|
||||
}
|
||||
addr->ifaddrs.ifa_next = res;
|
||||
res = (struct ifaddrs *)addr;
|
||||
}
|
||||
}
|
||||
*out_ifpp = res;
|
||||
rc = 0;
|
||||
}
|
||||
free(data);
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#ifndef COSMOPOLITAN_LIBC_SOCK_IFADDRS_H_
|
||||
#define COSMOPOLITAN_LIBC_SOCK_IFADDRS_H_
|
||||
#include "libc/sock/struct/sockaddr.h"
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
struct ifaddrs {
|
||||
struct ifaddrs *ifa_next;
|
||||
char *ifa_name;
|
||||
unsigned ifa_flags;
|
||||
struct sockaddr *ifa_addr;
|
||||
struct sockaddr *ifa_netmask;
|
||||
union {
|
||||
struct sockaddr *ifa_broadaddr;
|
||||
struct sockaddr *ifa_dstaddr;
|
||||
};
|
||||
void *ifa_data;
|
||||
};
|
||||
|
||||
void freeifaddrs(struct ifaddrs *);
|
||||
int getifaddrs(struct ifaddrs **);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_LIBC_SOCK_IFADDRS_H_ */
|
||||
@@ -1,27 +0,0 @@
|
||||
#ifndef COSMOPOLITAN_LIBC_SOCK_OVERLAPPED_H_
|
||||
#define COSMOPOLITAN_LIBC_SOCK_OVERLAPPED_H_
|
||||
#include "libc/nt/struct/overlapped.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
#define wsa_overlapped_cleanup_push(handle, overlap) \
|
||||
{ \
|
||||
struct WsaOverlappedCleanup wsa_overlapped_cleanup = {handle, overlap}; \
|
||||
pthread_cleanup_push(wsa_overlapped_cleanup_callback, \
|
||||
&wsa_overlapped_cleanup);
|
||||
|
||||
#define wsa_overlapped_cleanup_pop() \
|
||||
pthread_cleanup_pop(false); \
|
||||
}
|
||||
|
||||
struct WsaOverlappedCleanup {
|
||||
int64_t handle;
|
||||
struct NtOverlapped *overlap;
|
||||
};
|
||||
|
||||
void wsa_overlapped_cleanup_callback(void *);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_LIBC_SOCK_OVERLAPPED_H_ */
|
||||
+38
-45
@@ -18,6 +18,7 @@
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/struct/fd.internal.h"
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/errno.h"
|
||||
@@ -35,6 +36,7 @@
|
||||
#include "libc/sock/internal.h"
|
||||
#include "libc/sock/syscall_fd.internal.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/sicode.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
#ifdef __x86_64__
|
||||
@@ -60,92 +62,83 @@ static void CancelWinsockBlock(int64_t handle, struct NtOverlapped *overlap) {
|
||||
|
||||
textwindows ssize_t
|
||||
__winsock_block(int64_t handle, uint32_t flags, bool nonblock,
|
||||
uint32_t srwtimeout, sigset_t wait_signal_mask,
|
||||
uint32_t srwtimeout, sigset_t waitmask,
|
||||
int StartSocketOp(int64_t handle, struct NtOverlapped *overlap,
|
||||
uint32_t *flags, void *arg),
|
||||
void *arg) {
|
||||
|
||||
int rc;
|
||||
uint64_t m;
|
||||
int sig = 0;
|
||||
uint32_t status;
|
||||
uint32_t exchanged;
|
||||
int olderror = errno;
|
||||
bool eagained = false;
|
||||
bool eintered = false;
|
||||
bool canceled = false;
|
||||
int handler_was_called;
|
||||
struct PosixThread *pt;
|
||||
|
||||
RestartOperation:
|
||||
struct NtOverlapped overlap = {.hEvent = WSACreateEvent()};
|
||||
struct WinsockBlockResources wbr = {handle, &overlap};
|
||||
|
||||
pthread_cleanup_push(UnwindWinsockBlock, &wbr);
|
||||
rc = StartSocketOp(handle, &overlap, &flags, arg);
|
||||
if (rc && WSAGetLastError() == kNtErrorIoPending) {
|
||||
BlockingOperation:
|
||||
pt = _pthread_self();
|
||||
pt->pt_iohandle = handle;
|
||||
pt->pt_ioverlap = &overlap;
|
||||
pt->pt_flags |= PT_RESTARTABLE;
|
||||
atomic_store_explicit(&pt->pt_blocker, PT_BLOCKER_IO, memory_order_release);
|
||||
m = __sig_beginwait(wait_signal_mask);
|
||||
if (nonblock) {
|
||||
CancelWinsockBlock(handle, &overlap);
|
||||
eagained = true;
|
||||
} else if (_check_signal(true)) {
|
||||
} else if (_check_cancel()) {
|
||||
CancelWinsockBlock(handle, &overlap);
|
||||
canceled = true;
|
||||
} else if ((sig = __sig_get(waitmask))) {
|
||||
CancelWinsockBlock(handle, &overlap);
|
||||
if (errno == ECANCELED) {
|
||||
canceled = true;
|
||||
} else {
|
||||
eintered = true;
|
||||
}
|
||||
} else {
|
||||
pt = _pthread_self();
|
||||
pt->pt_blkmask = waitmask;
|
||||
pt->pt_iohandle = handle;
|
||||
pt->pt_ioverlap = &overlap;
|
||||
atomic_store_explicit(&pt->pt_blocker, PT_BLOCKER_IO,
|
||||
memory_order_release);
|
||||
status = WSAWaitForMultipleEvents(1, &overlap.hEvent, 0,
|
||||
srwtimeout ? srwtimeout : -1u, 0);
|
||||
atomic_store_explicit(&pt->pt_blocker, 0, memory_order_release);
|
||||
if (status == kNtWaitTimeout) {
|
||||
// rcvtimeo or sndtimeo elapsed
|
||||
// SO_RCVTIMEO or SO_SNDTIMEO elapsed
|
||||
CancelWinsockBlock(handle, &overlap);
|
||||
eagained = true;
|
||||
} else if (status == kNtWaitFailed) {
|
||||
// Failure should be an impossible condition, but MSDN lists
|
||||
// WSAENETDOWN and WSA_NOT_ENOUGH_MEMORY as possible errors.
|
||||
CancelWinsockBlock(handle, &overlap);
|
||||
eintered = true;
|
||||
}
|
||||
}
|
||||
__sig_finishwait(m);
|
||||
atomic_store_explicit(&pt->pt_blocker, PT_BLOCKER_CPU,
|
||||
memory_order_release);
|
||||
pt->pt_flags &= ~PT_RESTARTABLE;
|
||||
pt->pt_ioverlap = 0;
|
||||
pt->pt_iohandle = 0;
|
||||
rc = 0;
|
||||
}
|
||||
if (!rc) {
|
||||
bool32 should_wait = canceled || eagained;
|
||||
bool32 ok = WSAGetOverlappedResult(handle, &overlap, &exchanged,
|
||||
should_wait, &flags);
|
||||
if (!ok && WSAGetLastError() == kNtErrorIoIncomplete) {
|
||||
goto BlockingOperation;
|
||||
}
|
||||
rc = ok ? 0 : -1;
|
||||
rc = WSAGetOverlappedResult(handle, &overlap, &exchanged, true, &flags)
|
||||
? 0
|
||||
: -1;
|
||||
}
|
||||
WSACloseEvent(overlap.hEvent);
|
||||
pthread_cleanup_pop(false);
|
||||
WSACloseEvent(overlap.hEvent);
|
||||
|
||||
if (canceled) {
|
||||
return ecanceled();
|
||||
}
|
||||
if (sig) {
|
||||
handler_was_called = __sig_relay(sig, SI_KERNEL, waitmask);
|
||||
if (_check_cancel() == -1) return -1;
|
||||
} else {
|
||||
handler_was_called = 0;
|
||||
}
|
||||
if (!rc) {
|
||||
errno = olderror;
|
||||
return exchanged;
|
||||
}
|
||||
if (eagained) {
|
||||
return eagain();
|
||||
}
|
||||
if (GetLastError() == kNtErrorOperationAborted) {
|
||||
if (_check_cancel() == -1) return ecanceled();
|
||||
if (!eintered && _check_signal(false)) return -1;
|
||||
}
|
||||
if (eintered) {
|
||||
if (WSAGetLastError() == kNtErrorOperationAborted) {
|
||||
if (eagained) return eagain();
|
||||
if (!handler_was_called && (sig = __sig_get(waitmask))) {
|
||||
handler_was_called = __sig_relay(sig, SI_KERNEL, waitmask);
|
||||
if (_check_cancel() == -1) return -1;
|
||||
}
|
||||
if (handler_was_called != 1) {
|
||||
goto RestartOperation;
|
||||
}
|
||||
return eintr();
|
||||
}
|
||||
return __winsockerr();
|
||||
|
||||
@@ -17,21 +17,28 @@
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/struct/stat.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
|
||||
/**
|
||||
* Returns current working directory.
|
||||
*
|
||||
* If the `PWD` environment variable is set, and it seems legit, then
|
||||
* that'll be returned.
|
||||
* If the `PWD` environment variable is set, and it's correct, then
|
||||
* that'll be returned in its exact canonical or non-canonical form
|
||||
* instead of calling getcwd().
|
||||
*
|
||||
* @return pointer that must be free()'d, or NULL w/ errno
|
||||
*/
|
||||
char *get_current_dir_name(void) {
|
||||
const char *p;
|
||||
if ((p = getenv("PWD")) && *p == '/') {
|
||||
return strdup(p);
|
||||
const char *res;
|
||||
struct stat st1, st2;
|
||||
if ((res = getenv("PWD")) && *res && //
|
||||
!stat(res, &st1) && //
|
||||
!stat(".", &st2) && //
|
||||
st1.st_dev == st2.st_dev && //
|
||||
st1.st_ino == st2.st_ino) {
|
||||
return strdup(res);
|
||||
} else {
|
||||
return getcwd(0, 0);
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ Copyright 2023 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
@@ -16,69 +16,62 @@
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/nt/files.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/str/utf16.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
textwindows char *sys_getcwd_nt(char *buf, size_t size) {
|
||||
uint64_t w;
|
||||
wint_t x, y;
|
||||
uint32_t n, i, j;
|
||||
char16_t p[PATH_MAX];
|
||||
if ((n = GetCurrentDirectory(ARRAYLEN(p), p))) {
|
||||
if (4 + n + 1 <= size && 4 + n + 1 <= ARRAYLEN(p)) {
|
||||
tprecode16to8(buf, size, p);
|
||||
i = 0;
|
||||
j = 0;
|
||||
if (n >= 3 && isalpha(p[0]) && p[1] == ':' && p[2] == '\\') {
|
||||
// turn c:\... into \c\...
|
||||
p[1] = p[0];
|
||||
p[0] = '\\';
|
||||
} else if (n >= 7 && p[0] == '\\' && p[1] == '\\' && p[2] == '?' &&
|
||||
p[3] == '\\' && isalpha(p[4]) && p[5] == ':' && p[6] == '\\') {
|
||||
// turn \\?\c:\... into \c\...
|
||||
buf[j++] = '/';
|
||||
buf[j++] = p[4];
|
||||
buf[j++] = '/';
|
||||
i += 7;
|
||||
}
|
||||
while (i < n) {
|
||||
x = p[i++] & 0xffff;
|
||||
if (!IsUcs2(x)) {
|
||||
if (i < n) {
|
||||
y = p[i++] & 0xffff;
|
||||
x = MergeUtf16(x, y);
|
||||
} else {
|
||||
x = 0xfffd;
|
||||
}
|
||||
}
|
||||
if (x < 0200) {
|
||||
if (x == '\\') {
|
||||
x = '/';
|
||||
}
|
||||
w = x;
|
||||
} else {
|
||||
w = tpenc(x);
|
||||
}
|
||||
do {
|
||||
if (j < size) {
|
||||
buf[j++] = w;
|
||||
}
|
||||
w >>= 8;
|
||||
} while (w);
|
||||
}
|
||||
if (j < size) {
|
||||
buf[j] = 0;
|
||||
return buf;
|
||||
}
|
||||
/**
|
||||
* Returns current working directory, e.g.
|
||||
*
|
||||
* const char *dirname = gc(getcwd(0,0)); // if malloc is linked
|
||||
* const char *dirname = getcwd(alloca(PATH_MAX),PATH_MAX);
|
||||
*
|
||||
* @param buf is where UTF-8 NUL-terminated path string gets written,
|
||||
* which may be NULL to ask this function to malloc a buffer
|
||||
* @param size is number of bytes available in buf, e.g. PATH_MAX+1,
|
||||
* which may be 0 if buf is NULL
|
||||
* @return buf containing system-normative path or NULL w/ errno
|
||||
* @raise EACCES if the current directory path couldn't be accessed
|
||||
* @raise ERANGE if `size` wasn't big enough for path and nul byte
|
||||
* @raise ENOMEM on failure to allocate the requested buffer
|
||||
* @raise EINVAL if `size` is zero and `buf` is non-null
|
||||
* @raise EFAULT if `buf` points to invalid memory
|
||||
*/
|
||||
char *getcwd(char *buf, size_t size) {
|
||||
|
||||
// setup memory per api
|
||||
char *path;
|
||||
if (buf) {
|
||||
path = buf;
|
||||
if (!size) {
|
||||
einval();
|
||||
return 0;
|
||||
}
|
||||
erange();
|
||||
return NULL;
|
||||
} else {
|
||||
__winerr();
|
||||
return NULL;
|
||||
if (!size) size = 4096;
|
||||
if (!(path = malloc(size))) {
|
||||
return 0; // enomem
|
||||
}
|
||||
}
|
||||
|
||||
// invoke the real system call
|
||||
int got = __getcwd(path, size);
|
||||
if (got == -1) {
|
||||
if (path != buf) {
|
||||
free(path);
|
||||
}
|
||||
return 0; // eacces, erange, or efault
|
||||
}
|
||||
|
||||
// cut allocation down to size
|
||||
if (path != buf) {
|
||||
char *p;
|
||||
if ((p = realloc(path, strlen(path) + 1))) {
|
||||
path = p;
|
||||
}
|
||||
}
|
||||
|
||||
// return result
|
||||
return path;
|
||||
}
|
||||
@@ -42,6 +42,9 @@ int IsZipEocd32(const uint8_t *p, size_t n, size_t i) {
|
||||
if (ZIP_CDIR_RECORDS(p + i) * kZipCfileHdrMinSize > ZIP_CDIR_SIZE(p + i)) {
|
||||
return kZipErrorEocdRecordsOverflow;
|
||||
}
|
||||
if (ZIP_CDIR_OFFSET(p + i) == 0xFFFFFFFFu) {
|
||||
return kZipErrorEocdRecordsOverflow;
|
||||
}
|
||||
if (ckd_add(&offset, ZIP_CDIR_OFFSET(p + i), ZIP_CDIR_SIZE(p + i))) {
|
||||
return kZipErrorEocdOffsetSizeOverflow;
|
||||
}
|
||||
|
||||
+2
-5
@@ -124,7 +124,7 @@ syscon errno ECANCELED 125 125 89 89 85 88 87 1223 # kNtError
|
||||
syscon errno EOWNERDEAD 130 130 105 105 96 94 97 105 # kNtErrorSemOwnerDied; raised by pthread_cond_timedwait(3), pthread_mutex_consistent(3), pthread_mutex_getprioceiling(3), pthread_mutex_lock(3), pthread_mutex_timedlock(3), pthread_mutexattr_getrobust(3), pthread_mutexattr_setrobust(3)
|
||||
syscon errno ENOTRECOVERABLE 131 131 104 104 95 93 98 0 # raised by pthread_cond_timedwait(3), pthread_mutex_consistent(3), pthread_mutex_getprioceiling(3), pthread_mutex_lock(3), pthread_mutex_timedlock(3), pthread_mutexattr_getrobust(3), pthread_mutexattr_setrobust(3)
|
||||
syscon errno ENONET 64 64 317 317 317 317 317 0 # made up on BSDs; raised by accept(2)
|
||||
syscon errno ERESTART 85 85 -1 -1 -1 -1 -3 0 # should only be seen in ptrace()
|
||||
syscon errno ERESTART 85 85 318 318 318 318 -3 20000 # should only be seen in ptrace()
|
||||
syscon errno ENODATA 61 61 96 96 0 0 89 232 # no message is available in xsi stream or named pipe is being closed; no data available; barely in posix; returned by ioctl; very close in spirit to EPIPE?
|
||||
syscon errno ENOSR 63 63 98 98 0 90 90 0 # out of streams resources; something like EAGAIN; it's in POSIX; maybe some commercial UNIX returns it with openat, putmsg, putpmsg, posix_openpt, ioctl, open
|
||||
syscon errno ENOSTR 60 60 99 99 0 0 91 0 # not a stream; returned by getmsg, putmsg, putpmsg, getpmsg
|
||||
@@ -1351,7 +1351,7 @@ syscon iff IFF_ALLMULTI 0x0200 0x0200 0x0200 0x0200 0x0200 0x0200
|
||||
syscon iff IFF_NOARP 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 # faked nt as linux; unix consensus
|
||||
syscon iff IFF_POINTOPOINT 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 # point-to-point; faked nt as linux; unix consensus
|
||||
syscon iff IFF_PROMISC 0x100 0x100 0x100 0x100 0x100 0x100 0x100 0 # in packet capture mode; unix consensus
|
||||
syscon iff IFF_RUNNING 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0 # unix consensus
|
||||
syscon iff IFF_RUNNING 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0 # unix consensus (mostly for bsd compatibility?)
|
||||
syscon iff IFF_NOTRAILERS 0x20 0x20 0x20 0x20 0 0 0 0
|
||||
syscon iff IFF_AUTOMEDIA 0x4000 0x4000 0 0 0 0 0 0
|
||||
syscon iff IFF_DYNAMIC 0x8000 0x8000 0 0 0 0 0 0
|
||||
@@ -1391,9 +1391,6 @@ syscon misc IP6F_MORE_FRAG 0x0100 0x0100 0x0100 0x0100 0x0100 0x010
|
||||
syscon misc IP6F_OFF_MASK 0xf8ff 0xf8ff 0xf8ff 0xf8ff 0xf8ff 0xf8ff 0xf8ff 0xf8ff # consensus
|
||||
syscon misc IP6F_RESERVED_MASK 0x0600 0x0600 0x0600 0x0600 0x0600 0x0600 0x0600 0x0600 # consensus
|
||||
|
||||
syscon misc NO_SENSE 0 0 0 0 0 0 0 0 # consensus
|
||||
syscon misc NO_ADDRESS 4 4 4 4 4 4 4 0x2afc # unix consensus
|
||||
|
||||
syscon misc L_SET 0 0 0 0 0 0 0 0 # consensus
|
||||
syscon misc L_INCR 1 1 1 1 1 1 1 0 # unix consensus
|
||||
syscon misc L_XTND 2 2 2 2 2 2 2 0 # unix consensus
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
#include "libc/sysv/consts/syscon.internal.h"
|
||||
.syscon errno,ERESTART,85,85,-1,-1,-1,-1,-3,0
|
||||
.syscon errno,ERESTART,85,85,318,318,318,318,-3,20000
|
||||
@@ -1,2 +0,0 @@
|
||||
#include "libc/sysv/consts/syscon.internal.h"
|
||||
.syscon misc,NO_ADDRESS,4,4,4,4,4,4,4,0x2afc
|
||||
@@ -1,2 +0,0 @@
|
||||
#include "libc/sysv/consts/syscon.internal.h"
|
||||
.syscon misc,NO_SENSE,0,0,0,0,0,0,0,0
|
||||
@@ -14,10 +14,8 @@
|
||||
#define PT_NOCANCEL 8
|
||||
#define PT_MASKED 16
|
||||
#define PT_INCANCEL 32
|
||||
#define PT_RESTARTABLE 64
|
||||
#define PT_OPENBSD_KLUDGE 128
|
||||
#define PT_OPENBSD_KLUDGE 64
|
||||
|
||||
#define PT_BLOCKER_CPU ((atomic_int *)-0)
|
||||
#define PT_BLOCKER_SEM ((atomic_int *)-1)
|
||||
#define PT_BLOCKER_IO ((atomic_int *)-2)
|
||||
|
||||
@@ -93,6 +91,7 @@ struct PosixThread {
|
||||
struct Dll list; // list of threads
|
||||
struct _pthread_cleanup_buffer *pt_cleanup;
|
||||
_Atomic(atomic_int *) pt_blocker;
|
||||
uint64_t pt_blkmask;
|
||||
int64_t pt_semaphore;
|
||||
intptr_t pt_iohandle;
|
||||
void *pt_ioverlap;
|
||||
|
||||
@@ -17,21 +17,57 @@
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/fmt/fmt.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/libgen.h"
|
||||
#include "libc/intrin/bits.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/log/check.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/mem/gc.internal.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
#include "libc/x/x.h"
|
||||
|
||||
void SetUpOnce(void) {
|
||||
testlib_enable_tmp_setup_teardown();
|
||||
ASSERT_SYS(0, 0, pledge("stdio rpath cpath fattr", 0));
|
||||
}
|
||||
|
||||
TEST(__getcwd, zero) {
|
||||
ASSERT_SYS(ERANGE, -1, __getcwd(0, 0));
|
||||
}
|
||||
|
||||
TEST(__getcwd, returnsLengthIncludingNul) {
|
||||
char cwd1[PATH_MAX];
|
||||
char cwd2[PATH_MAX];
|
||||
ASSERT_NE(-1, __getcwd(cwd1, PATH_MAX));
|
||||
ASSERT_EQ(strlen(cwd1) + 1, __getcwd(cwd2, PATH_MAX));
|
||||
}
|
||||
|
||||
TEST(__getcwd, tooShort_negOneReturned_bufferIsntModified) {
|
||||
char cwd[4] = {0x55, 0x55, 0x55, 0x55};
|
||||
ASSERT_SYS(ERANGE, -1, __getcwd(cwd, 4));
|
||||
ASSERT_EQ(0x55555555, READ32LE(cwd));
|
||||
}
|
||||
|
||||
TEST(__getcwd, noRoomForNul) {
|
||||
char cwd1[PATH_MAX];
|
||||
char cwd2[PATH_MAX];
|
||||
ASSERT_NE(-1, __getcwd(cwd1, PATH_MAX));
|
||||
ASSERT_SYS(ERANGE, -1, __getcwd(cwd2, strlen(cwd1)));
|
||||
}
|
||||
|
||||
TEST(__getcwd, alwaysStartsWithSlash) {
|
||||
char cwd[PATH_MAX];
|
||||
ASSERT_NE(-1, __getcwd(cwd, PATH_MAX));
|
||||
ASSERT_EQ('/', *cwd);
|
||||
}
|
||||
|
||||
TEST(__getcwd, notInRootDir_neverEndsWithSlash) {
|
||||
char cwd[PATH_MAX];
|
||||
ASSERT_NE(-1, __getcwd(cwd, PATH_MAX));
|
||||
ASSERT_FALSE(endswith(cwd, "/"));
|
||||
}
|
||||
|
||||
TEST(getcwd, test) {
|
||||
char buf[PATH_MAX];
|
||||
EXPECT_SYS(0, 0, mkdir("subdir", 0755));
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2021 Justine Alexandra Roberts Tunney │
|
||||
│ Copyright 2023 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
@@ -16,54 +16,57 @@
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/atomic.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/struct/metastat.internal.h"
|
||||
#include "libc/calls/struct/stat.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/at.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "libc/runtime/clktck.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/sysv/consts/sa.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
||||
#define XNU_F_GETPATH 50
|
||||
#define XNU_MAXPATHLEN 1024
|
||||
jmp_buf jb;
|
||||
int pfds[2];
|
||||
atomic_bool isdone;
|
||||
volatile bool canjmp;
|
||||
|
||||
static inline bool CopyString(char *d, const char *s, size_t n) {
|
||||
size_t i;
|
||||
for (i = 0; i < n; ++i) {
|
||||
if (!(d[i] = s[i])) {
|
||||
return true;
|
||||
}
|
||||
void OnSignal(int sig) {
|
||||
if (canjmp) {
|
||||
canjmp = false;
|
||||
longjmp(jb, 1);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
char *sys_getcwd_xnu(char *res, size_t size) {
|
||||
int fd;
|
||||
union metastat st[2];
|
||||
char buf[XNU_MAXPATHLEN], *ret = NULL;
|
||||
if ((fd = __sys_openat_nc(AT_FDCWD, ".", O_RDONLY | O_DIRECTORY, 0)) != -1) {
|
||||
if (__sys_fstat(fd, &st[0]) != -1) {
|
||||
if (st[0].xnu.st_dev && st[0].xnu.st_ino) {
|
||||
if (__sys_fcntl(fd, XNU_F_GETPATH, (uintptr_t)buf) != -1) {
|
||||
if (__sys_fstatat(AT_FDCWD, buf, &st[1], 0) != -1) {
|
||||
if (st[0].xnu.st_dev == st[1].xnu.st_dev &&
|
||||
st[0].xnu.st_ino == st[1].xnu.st_ino) {
|
||||
if (CopyString(res, buf, size)) {
|
||||
ret = res;
|
||||
} else {
|
||||
erange();
|
||||
}
|
||||
} else {
|
||||
einval();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
einval();
|
||||
}
|
||||
void *ReadWorker(void *arg) {
|
||||
int got;
|
||||
char buf[8];
|
||||
while (!isdone) {
|
||||
if (!(got = setjmp(jb))) {
|
||||
canjmp = true;
|
||||
read(pfds[0], buf, sizeof(buf));
|
||||
abort();
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
TEST(eintr, longjmp) {
|
||||
pthread_t th;
|
||||
struct sigaction sa = {
|
||||
.sa_handler = OnSignal,
|
||||
.sa_flags = SA_SIGINFO | SA_NODEFER,
|
||||
};
|
||||
sigaction(SIGUSR1, &sa, 0);
|
||||
ASSERT_SYS(0, 0, pipe(pfds));
|
||||
ASSERT_EQ(0, pthread_create(&th, 0, ReadWorker, 0));
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
pthread_kill(th, SIGUSR1);
|
||||
usleep(1. / CLK_TCK * 1e6);
|
||||
}
|
||||
isdone = true;
|
||||
pthread_kill(th, SIGUSR1);
|
||||
ASSERT_EQ(0, pthread_join(th, 0));
|
||||
ASSERT_SYS(0, 0, close(pfds[1]));
|
||||
ASSERT_SYS(0, 0, close(pfds[0]));
|
||||
}
|
||||
@@ -197,6 +197,7 @@ TEST(mprotect, testRwxMap_vonNeumannRules) {
|
||||
}
|
||||
|
||||
TEST(mprotect, testExecuteFlatFileMapOpenedAsReadonly) {
|
||||
if (IsXnuSilicon()) return; // TODO(jart): Use APE Loader SIP workaround?
|
||||
int (*p)(void);
|
||||
size_t n = sizeof(kRet31337);
|
||||
ASSERT_SYS(0, 3, creat("return31337", 0755));
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
-- Copyright 2023 Justine Alexandra Roberts Tunney
|
||||
--
|
||||
-- Permission to use, copy, modify, and/or distribute this software for
|
||||
-- any purpose with or without fee is hereby granted, provided that the
|
||||
-- above copyright notice and this permission notice appear in all copies.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||
-- WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||
-- WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
||||
-- AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||
-- DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
|
||||
-- PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||
-- TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
-- PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
local sqlite3 = require "lsqlite3"
|
||||
local db = assert(sqlite3.open("file:/memdb1?vfs=memdb&mode=ro",
|
||||
sqlite3.OPEN_URI + sqlite3.OPEN_READWRITE + sqlite3.OPEN_CREATE))
|
||||
assert(db:readonly() == true)
|
||||
db = sqlite3.open("file:/memdb1?vfs=memdb",
|
||||
sqlite3.OPEN_URI + sqlite3.OPEN_READWRITE + sqlite3.OPEN_CREATE)
|
||||
assert(db:readonly() == false)
|
||||
assert(db:readonly("main") == false)
|
||||
assert(db:readonly("foo") == nil)
|
||||
|
||||
assert(db:exec("create table foo(a)") == 0)
|
||||
|
||||
local st = assert(db:prepare("select * from foo"))
|
||||
assert(st:readonly() == true)
|
||||
st = assert(db:prepare("insert into foo (a) values (1)"))
|
||||
assert(st:readonly() == false)
|
||||
Vendored
+1
@@ -27,6 +27,7 @@
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#define loadlib_c
|
||||
#define LUA_LIB
|
||||
#include "libc/dlopen/dlfcn.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "third_party/lua/lauxlib.h"
|
||||
|
||||
Vendored
+36
-9
@@ -23,6 +23,8 @@
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/calls/struct/timespec.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
@@ -40,6 +42,7 @@
|
||||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/runtime/clktck.h"
|
||||
#include "libc/sysv/consts/clock.h"
|
||||
#include "libc/sysv/consts/sicode.h"
|
||||
#include "libc/sysv/consts/timer.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/freebsd.internal.h"
|
||||
@@ -147,9 +150,11 @@ static int nsync_futex_polyfill_ (atomic_int *w, int expect, struct timespec *ab
|
||||
|
||||
static int nsync_futex_wait_win32_ (atomic_int *w, int expect, char pshare,
|
||||
const struct timespec *timeout,
|
||||
struct PosixThread *pt) {
|
||||
struct PosixThread *pt,
|
||||
sigset_t waitmask) {
|
||||
int sig;
|
||||
bool32 ok;
|
||||
struct timespec deadline, interval, remain, wait, now;
|
||||
struct timespec deadline, wait, now;
|
||||
|
||||
if (timeout) {
|
||||
deadline = *timeout;
|
||||
@@ -162,16 +167,36 @@ static int nsync_futex_wait_win32_ (atomic_int *w, int expect, char pshare,
|
||||
if (timespec_cmp (now, deadline) > 0) {
|
||||
return etimedout();
|
||||
}
|
||||
remain = timespec_sub (deadline, now);
|
||||
interval = timespec_frommillis (5000);
|
||||
wait = timespec_cmp (remain, interval) > 0 ? interval : remain;
|
||||
wait = timespec_sub (deadline, now);
|
||||
if (atomic_load_explicit (w, memory_order_acquire) != expect) {
|
||||
return 0;
|
||||
}
|
||||
if (pt) atomic_store_explicit (&pt->pt_blocker, w, memory_order_release);
|
||||
if (_check_signal (false) == -1) return -1;
|
||||
if (pt) {
|
||||
if (_check_cancel () == -1) {
|
||||
return -1; /* ECANCELED */
|
||||
}
|
||||
if ((sig = __sig_get (waitmask))) {
|
||||
__sig_relay (sig, SI_KERNEL, waitmask);
|
||||
if (_check_cancel () == -1) {
|
||||
return -1; /* ECANCELED */
|
||||
}
|
||||
return eintr ();
|
||||
}
|
||||
pt->pt_blkmask = waitmask;
|
||||
atomic_store_explicit (&pt->pt_blocker, w, memory_order_release);
|
||||
}
|
||||
ok = WaitOnAddress (w, &expect, sizeof(int), timespec_tomillis (wait));
|
||||
if (_check_signal (false) == -1) return -1;
|
||||
if (pt) {
|
||||
/* __sig_cancel wakes our futex without changing `w` after enqueing signals */
|
||||
atomic_store_explicit (&pt->pt_blocker, 0, memory_order_release);
|
||||
if (ok && atomic_load_explicit (w, memory_order_acquire) == expect && (sig = __sig_get (waitmask))) {
|
||||
__sig_relay (sig, SI_KERNEL, waitmask);
|
||||
if (_check_cancel () == -1) {
|
||||
return -1; /* ECANCELED */
|
||||
}
|
||||
return eintr ();
|
||||
}
|
||||
}
|
||||
if (ok) {
|
||||
return 0;
|
||||
} else {
|
||||
@@ -233,7 +258,9 @@ int nsync_futex_wait_ (atomic_int *w, int expect, char pshare, const struct time
|
||||
if (IsWindows ()) {
|
||||
// Windows 8 futexes don't support multiple processes :(
|
||||
if (pshare) goto Polyfill;
|
||||
rc = nsync_futex_wait_win32_ (w, expect, pshare, timeout, pt);
|
||||
sigset_t m = __sig_block ();
|
||||
rc = nsync_futex_wait_win32_ (w, expect, pshare, timeout, pt, m);
|
||||
__sig_unblock (m);
|
||||
} else if (IsXnu ()) {
|
||||
uint32_t op, us;
|
||||
if (pshare) {
|
||||
|
||||
+1
-1
@@ -35,7 +35,7 @@
|
||||
#include "libc/nt/enum/sw.h"
|
||||
#include "libc/nt/files.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/runtime/dlfcn.h"
|
||||
#include "libc/dlopen/dlfcn.h"
|
||||
#include "libc/runtime/pathconf.h"
|
||||
#include "libc/runtime/sysconf.h"
|
||||
#include "libc/sock/sendfile.internal.h"
|
||||
|
||||
+1
-1
@@ -37,7 +37,7 @@
|
||||
#include "libc/nt/enum/sw.h"
|
||||
#include "libc/nt/files.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/runtime/dlfcn.h"
|
||||
#include "libc/dlopen/dlfcn.h"
|
||||
#include "libc/runtime/pathconf.h"
|
||||
#include "libc/runtime/sysconf.h"
|
||||
#include "libc/sock/sendfile.internal.h"
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
│ https://docs.python.org/3/license.html │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/weirdtypes.h"
|
||||
#include "libc/runtime/dlfcn.h"
|
||||
#include "libc/dlopen/dlfcn.h"
|
||||
#include "third_party/python/Include/fileutils.h"
|
||||
#include "third_party/python/Include/modsupport.h"
|
||||
#include "third_party/python/Include/pyerrors.h"
|
||||
|
||||
Vendored
+1
-1
@@ -7,7 +7,7 @@
|
||||
#include "third_party/python/Include/pystate.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/pushpop.internal.h"
|
||||
#include "libc/runtime/dlfcn.h"
|
||||
#include "libc/dlopen/dlfcn.h"
|
||||
#include "third_party/python/Include/ceval.h"
|
||||
#include "third_party/python/Include/dictobject.h"
|
||||
#include "third_party/python/Include/listobject.h"
|
||||
|
||||
Vendored
+1
@@ -455,6 +455,7 @@ THIRD_PARTY_PYTHON_STAGE1_A_SRCS = \
|
||||
THIRD_PARTY_PYTHON_STAGE1_A_DIRECTDEPS = \
|
||||
DSP_SCALE \
|
||||
LIBC_CALLS \
|
||||
LIBC_DLOPEN \
|
||||
LIBC_FMT \
|
||||
LIBC_INTRIN \
|
||||
LIBC_LOG \
|
||||
|
||||
Vendored
+1
-1
@@ -37,7 +37,7 @@
|
||||
#include "libc/limits.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/runtime/dlfcn.h"
|
||||
#include "libc/dlopen/dlfcn.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/runtime/sysconf.h"
|
||||
#include "libc/sock/select.h"
|
||||
|
||||
Vendored
+1
@@ -79,6 +79,7 @@ THIRD_PARTY_QUICKJS_A_OBJS = \
|
||||
|
||||
THIRD_PARTY_QUICKJS_A_DIRECTDEPS = \
|
||||
LIBC_CALLS \
|
||||
LIBC_DLOPEN \
|
||||
LIBC_FMT \
|
||||
LIBC_INTRIN \
|
||||
LIBC_LOG \
|
||||
|
||||
+16
-1
@@ -38,6 +38,21 @@ static struct ZipArgs {
|
||||
char **oldargv;
|
||||
} g_zipargs;
|
||||
|
||||
// remap \n → newline
|
||||
static char *Decode(char *arg) {
|
||||
int i, j;
|
||||
for (i = j = 0; arg[i]; ++i) {
|
||||
if (arg[i] == '\\' && arg[i + 1] == 'n') {
|
||||
arg[j++] = '\n';
|
||||
++i;
|
||||
} else {
|
||||
arg[j++] = arg[i];
|
||||
}
|
||||
}
|
||||
arg[j] = 0;
|
||||
return arg;
|
||||
}
|
||||
|
||||
static void AddZipArg(int *argc, char ***argv, char *arg) {
|
||||
*argv = xrealloc(*argv, (++(*argc) + 1) * sizeof(*(*argv)));
|
||||
(*argv)[*argc - 1] = arg;
|
||||
@@ -72,7 +87,7 @@ int LoadZipArgsImpl(int *argc, char ***argv, char *data) {
|
||||
AddZipArg(&n, &args, (*argv)[i]);
|
||||
}
|
||||
} else {
|
||||
AddZipArg(&n, &args, arg);
|
||||
AddZipArg(&n, &args, Decode(arg));
|
||||
}
|
||||
start = 0;
|
||||
}
|
||||
|
||||
+7
-6
@@ -48,6 +48,8 @@
|
||||
* @fileoverview Zip File Disassembler.
|
||||
*/
|
||||
|
||||
uint8_t *map;
|
||||
|
||||
static __wur char *FormatDosDate(uint16_t dosdate) {
|
||||
return xasprintf("%04u-%02u-%02u", ((dosdate >> 9) & 0b1111111) + 1980,
|
||||
(dosdate >> 5) & 0b1111, dosdate & 0b11111);
|
||||
@@ -60,9 +62,9 @@ static __wur char *FormatDosTime(uint16_t dostime) {
|
||||
|
||||
void AdvancePosition(uint8_t *map, size_t *pos, size_t off) {
|
||||
if (off > *pos) {
|
||||
printf("\n/\t<%s>\n", "LIMBO");
|
||||
disassemblehex(&map[*pos], off - *pos, stdout);
|
||||
printf("/\t</%s>\n", "LIMBO");
|
||||
/* printf("\n/\t<%s>\n", "LIMBO"); */
|
||||
/* disassemblehex(&map[*pos], off - *pos, stdout); */
|
||||
/* printf("/\t</%s>\n", "LIMBO"); */
|
||||
}
|
||||
*pos = off;
|
||||
}
|
||||
@@ -288,8 +290,8 @@ void ShowLocalFileHeader(uint8_t *lf, uint16_t idx) {
|
||||
}
|
||||
|
||||
void ShowCentralFileHeader(uint8_t *cf) {
|
||||
printf("\n/\t%s (%zu %s)\n", "central directory file header",
|
||||
ZIP_CFILE_HDRSIZE(cf), "bytes");
|
||||
printf("\n/\t%s (%zu %s @ %#lx)\n", "central directory file header",
|
||||
ZIP_CFILE_HDRSIZE(cf), "bytes", cf - map);
|
||||
show(".ascii", format(b1, "%`'.*s", 4, cf), "magic");
|
||||
show(".byte", _gc(xasprintf("%d", ZIP_CFILE_VERSIONMADE(cf))),
|
||||
"zip version made");
|
||||
@@ -485,7 +487,6 @@ void DisassembleZip(const char *path, uint8_t *p, size_t n) {
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int fd;
|
||||
uint8_t *map;
|
||||
struct stat st;
|
||||
ShowCrashReports();
|
||||
CHECK_EQ(2, argc);
|
||||
|
||||
+38
-15
@@ -234,6 +234,12 @@ static int dbvm_isopen(lua_State *L) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int dbvm_readonly(lua_State *L) {
|
||||
sdb_vm *svm = lsqlite_checkvm(L, 1);
|
||||
lua_pushboolean(L, sqlite3_stmt_readonly(svm->vm));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int dbvm_tostring(lua_State *L) {
|
||||
char buff[40];
|
||||
sdb_vm *svm = lsqlite_getvm(L, 1);
|
||||
@@ -908,6 +914,21 @@ static int pusherr(lua_State *L, int rc) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
static int pusherrstr(lua_State *L, char *str) {
|
||||
lua_pushnil(L);
|
||||
lua_pushstring(L, str);
|
||||
return 2;
|
||||
}
|
||||
|
||||
static int db_readonly(lua_State *L) {
|
||||
sdb *db = lsqlite_checkdb(L, 1);
|
||||
const char *zDb = luaL_optstring(L, 2, "main");
|
||||
int res = sqlite3_db_readonly(db->db, zDb);
|
||||
if (res == -1) return pusherrstr(L, "unknown (not attached) database name");
|
||||
lua_pushboolean(L, res);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int db_wal_checkpoint(lua_State *L) {
|
||||
sdb *db = lsqlite_checkdb(L, 1);
|
||||
int eMode = luaL_optinteger(L, 2, SQLITE_CHECKPOINT_PASSIVE);
|
||||
@@ -1748,16 +1769,15 @@ static int db_gc(lua_State *L) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef SQLITE_ENABLE_DESERIALIZE
|
||||
|
||||
static int db_serialize(lua_State *L) {
|
||||
sdb *db = lsqlite_getdb(L, 1);
|
||||
sdb *db = lsqlite_checkdb(L, 1);
|
||||
sqlite_int64 size = 0;
|
||||
|
||||
if (db->db == NULL) /* ignore closed databases */
|
||||
return 0;
|
||||
|
||||
char *buffer = (char *)sqlite3_serialize(db->db, "main", &size, 0);
|
||||
if (buffer == NULL) /* ignore failed database serialization */
|
||||
return 0;
|
||||
if (buffer == NULL)
|
||||
return pusherrstr(L, "failed to serialize");
|
||||
|
||||
lua_pushlstring(L, buffer, size);
|
||||
free(buffer);
|
||||
@@ -1765,15 +1785,12 @@ static int db_serialize(lua_State *L) {
|
||||
}
|
||||
|
||||
static int db_deserialize(lua_State *L) {
|
||||
sdb *db = lsqlite_getdb(L, 1);
|
||||
sdb *db = lsqlite_checkdb(L, 1);
|
||||
size_t size = 0;
|
||||
|
||||
if (db->db == NULL) /* ignore closed databases */
|
||||
return 0;
|
||||
|
||||
const char *buffer = luaL_checklstring(L, 2, &size);
|
||||
if (buffer == NULL || size == 0) /* ignore empty database content */
|
||||
return 0;
|
||||
if (buffer == NULL || size == 0)
|
||||
return pusherrstr(L, "failed to deserialize");
|
||||
|
||||
const char *sqlbuf = memcpy(sqlite3_malloc(size), buffer, size);
|
||||
sqlite3_deserialize(db->db, "main", (void *)sqlbuf, size, size,
|
||||
@@ -1781,6 +1798,8 @@ static int db_deserialize(lua_State *L) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_SESSION
|
||||
|
||||
/*
|
||||
@@ -2602,6 +2621,7 @@ static const struct {
|
||||
|
||||
static const luaL_Reg dblib[] = {
|
||||
{"isopen", db_isopen },
|
||||
{"readonly", db_readonly },
|
||||
{"last_insert_rowid", db_last_insert_rowid },
|
||||
{"changes", db_changes },
|
||||
{"total_changes", db_total_changes },
|
||||
@@ -2634,9 +2654,6 @@ static const luaL_Reg dblib[] = {
|
||||
{"close", db_close },
|
||||
{"close_vm", db_close_vm },
|
||||
|
||||
{"serialize", db_serialize },
|
||||
{"deserialize", db_deserialize },
|
||||
|
||||
#ifdef SQLITE_ENABLE_SESSION
|
||||
{"create_session", db_create_session },
|
||||
{"create_rebaser", db_create_rebaser },
|
||||
@@ -2646,6 +2663,11 @@ static const luaL_Reg dblib[] = {
|
||||
{"iterate_changeset", db_iterate_changeset },
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_DESERIALIZE
|
||||
{"serialize", db_serialize },
|
||||
{"deserialize", db_deserialize },
|
||||
#endif
|
||||
|
||||
{"__tostring", db_tostring },
|
||||
{"__gc", db_gc },
|
||||
|
||||
@@ -2654,6 +2676,7 @@ static const luaL_Reg dblib[] = {
|
||||
|
||||
static const luaL_Reg vmlib[] = {
|
||||
{"isopen", dbvm_isopen },
|
||||
{"readonly", dbvm_readonly },
|
||||
|
||||
{"step", dbvm_step },
|
||||
{"reset", dbvm_reset },
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2023 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/fmt/itoa.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/sock/ifaddrs.h"
|
||||
#include "libc/sock/sock.h"
|
||||
#include "libc/sock/struct/sockaddr.h"
|
||||
#include "libc/sock/struct/sockaddr6.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/af.h"
|
||||
#include "libc/sysv/consts/iff.h"
|
||||
|
||||
/* example output:
|
||||
|
||||
eth0
|
||||
addr: 10.10.10.237
|
||||
netmask: 255.255.255.0
|
||||
broadcast: 255.255.255.0
|
||||
flags: IFF_UP IFF_BROADCAST IFF_MULTICAST IFF_RUNNING
|
||||
|
||||
lo
|
||||
addr: 127.0.0.1
|
||||
netmask: 255.0.0.0
|
||||
flags: IFF_UP IFF_LOOPBACK IFF_RUNNING */
|
||||
|
||||
const char *sockaddr2str(const struct sockaddr *sa, char *buf, size_t size) {
|
||||
if (sa->sa_family == AF_INET) {
|
||||
return inet_ntop(AF_INET, &(((const struct sockaddr_in *)sa)->sin_addr),
|
||||
buf, size);
|
||||
} else if (sa->sa_family == AF_INET6) {
|
||||
return inet_ntop(AF_INET6, &(((const struct sockaddr_in6 *)sa)->sin6_addr),
|
||||
buf, size);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
// get network interface list
|
||||
struct ifaddrs *ifaddrs;
|
||||
if (getifaddrs(&ifaddrs)) {
|
||||
perror("getifaddrs");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// print network interface list
|
||||
for (struct ifaddrs *ifa = ifaddrs; ifa; ifa = ifa->ifa_next) {
|
||||
tinyprint(1, ifa->ifa_name, "\n", NULL);
|
||||
|
||||
char buf[128];
|
||||
if (sockaddr2str(ifa->ifa_addr, buf, sizeof(buf))) {
|
||||
tinyprint(1, "addr: ", buf, "\n", NULL);
|
||||
}
|
||||
if (sockaddr2str(ifa->ifa_netmask, buf, sizeof(buf))) {
|
||||
tinyprint(1, "netmask: ", buf, "\n", NULL);
|
||||
}
|
||||
if ((ifa->ifa_flags & IFF_BROADCAST) &&
|
||||
sockaddr2str(ifa->ifa_netmask, buf, sizeof(buf))) {
|
||||
tinyprint(1, "broadcast: ", buf, "\n", NULL);
|
||||
} else if ((ifa->ifa_flags & IFF_POINTOPOINT) &&
|
||||
sockaddr2str(ifa->ifa_dstaddr, buf, sizeof(buf))) {
|
||||
tinyprint(1, "dstaddr: ", buf, "\n", NULL);
|
||||
}
|
||||
|
||||
tinyprint(1, "flags:", NULL);
|
||||
if (ifa->ifa_flags & IFF_UP) {
|
||||
tinyprint(1, " IFF_UP", NULL);
|
||||
}
|
||||
if (ifa->ifa_flags & IFF_DEBUG) {
|
||||
tinyprint(1, " IFF_DEBUG", NULL);
|
||||
}
|
||||
if (ifa->ifa_flags & IFF_LOOPBACK) {
|
||||
tinyprint(1, " IFF_LOOPBACK", NULL);
|
||||
}
|
||||
if (ifa->ifa_flags & IFF_MULTICAST) {
|
||||
tinyprint(1, " IFF_MULTICAST", NULL);
|
||||
}
|
||||
if (ifa->ifa_flags & IFF_ALLMULTI) {
|
||||
tinyprint(1, " IFF_ALLMULTI", NULL);
|
||||
}
|
||||
if (ifa->ifa_flags & IFF_NOARP) {
|
||||
tinyprint(1, " IFF_NOARP", NULL);
|
||||
}
|
||||
if (ifa->ifa_flags & IFF_PROMISC) {
|
||||
tinyprint(1, " IFF_PROMISC", NULL);
|
||||
}
|
||||
tinyprint(1, "\n", NULL);
|
||||
|
||||
tinyprint(1, "\n", NULL);
|
||||
}
|
||||
|
||||
// perform cleanup
|
||||
freeifaddrs(ifaddrs);
|
||||
|
||||
// report success
|
||||
exit(0);
|
||||
}
|
||||
Reference in new issue
Block a user