mirror of
https://github.com/jart/cosmopolitan.git
synced 2026-11-08 08:08:04 -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 | ||
|
|
55d5d6bbc0 | ||
|
|
d259e573b6 | ||
|
|
7b284f6bda | ||
|
|
b0e3d89942 | ||
|
|
0b1acce680 | ||
|
|
fadb64a2bf | ||
|
|
a1e1e821cb | ||
|
|
2af7c802b6 | ||
|
|
3b791d2f44 | ||
|
|
b4084dd6c6 | ||
|
|
ee82f90bba | ||
|
|
a699cda5c8 | ||
|
|
f86ffa1a25 | ||
|
|
a09f62141d | ||
|
|
ed17d3008b | ||
|
|
062b2d776e |
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.12.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
|
||||
@@ -170,11 +170,14 @@ include third_party/puff/puff.mk # │
|
||||
include libc/elf/elf.mk # │
|
||||
include ape/ape.mk # │
|
||||
include libc/fmt/fmt.mk # │
|
||||
include libc/vga/vga.mk #─┘
|
||||
include libc/vga/vga.mk # │
|
||||
include libc/irq/irq.mk #─┘
|
||||
include libc/calls/calls.mk #─┐
|
||||
include third_party/nsync/nsync.mk # │
|
||||
include libc/runtime/runtime.mk # ├──SYSTEMS RUNTIME
|
||||
include third_party/double-conversion/dc.mk # │ You can issue system calls
|
||||
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 #─┘
|
||||
include libc/mem/mem.mk #─┐
|
||||
@@ -371,9 +374,11 @@ COSMOPOLITAN_OBJECTS = \
|
||||
THIRD_PARTY_NSYNC_MEM \
|
||||
LIBC_MEM \
|
||||
THIRD_PARTY_DLMALLOC \
|
||||
LIBC_DLOPEN \
|
||||
LIBC_RUNTIME \
|
||||
THIRD_PARTY_NSYNC \
|
||||
LIBC_ELF \
|
||||
LIBC_IRQ \
|
||||
LIBC_CALLS \
|
||||
LIBC_SYSV_CALLS \
|
||||
LIBC_VGA \
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -293,6 +293,10 @@ for x; do
|
||||
elif [ x"$x" = x"-dumpversion" ]; then
|
||||
echo $GCC_VERSION
|
||||
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,9 +43,11 @@ EXAMPLES_DIRECTDEPS = \
|
||||
DSP_SCALE \
|
||||
DSP_TTY \
|
||||
LIBC_CALLS \
|
||||
LIBC_DLOPEN \
|
||||
LIBC_DNS \
|
||||
LIBC_FMT \
|
||||
LIBC_INTRIN \
|
||||
LIBC_IRQ \
|
||||
LIBC_LOG \
|
||||
LIBC_MEM \
|
||||
LIBC_NEXGEN32E \
|
||||
@@ -91,6 +93,7 @@ EXAMPLES_DIRECTDEPS = \
|
||||
THIRD_PARTY_VQSORT \
|
||||
THIRD_PARTY_XED \
|
||||
THIRD_PARTY_ZLIB \
|
||||
TOOL_ARGS \
|
||||
TOOL_BUILD_LIB \
|
||||
TOOL_VIZ_LIB
|
||||
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
|
||||
__static_yoink("vga_console");
|
||||
__static_yoink("_idt");
|
||||
__static_yoink("_AcpiMadtFlags");
|
||||
__static_yoink("_AcpiBootFlags");
|
||||
__static_yoink("EfiMain");
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
+10
-6
@@ -158,6 +158,8 @@ int setregid(unsigned, unsigned);
|
||||
int setreuid(unsigned, unsigned);
|
||||
int setsid(void);
|
||||
int setuid(unsigned);
|
||||
int shm_open(const char *, int, unsigned);
|
||||
int shm_unlink(const char *);
|
||||
int sigignore(int);
|
||||
int siginterrupt(int, int);
|
||||
int symlink(const char *, const char *);
|
||||
@@ -166,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);
|
||||
@@ -211,36 +213,38 @@ int madvise(void *, uint64_t, int);
|
||||
#ifdef _COSMO_SOURCE
|
||||
bool32 fdexists(int);
|
||||
bool32 fileexists(const char *);
|
||||
bool32 ischardev(int);
|
||||
bool32 isdirectory(const char *);
|
||||
bool32 isexecutable(const char *);
|
||||
bool32 isregularfile(const char *);
|
||||
bool32 issymlink(const char *);
|
||||
bool32 ischardev(int);
|
||||
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);
|
||||
int pivot_root(const char *, const char *);
|
||||
int pledge(const char *, const char *);
|
||||
int seccomp(unsigned, unsigned, void *);
|
||||
int sys_iopl(int);
|
||||
int sys_ioprio_get(int, int);
|
||||
int sys_ioprio_set(int, int, int);
|
||||
int sys_mlock(const void *, size_t);
|
||||
int sys_mlock2(const void *, size_t, int);
|
||||
int sys_mlockall(int);
|
||||
int sys_personality(uint64_t);
|
||||
int sys_munlock(const void *, size_t);
|
||||
int sys_munlockall(void);
|
||||
int sys_personality(uint64_t);
|
||||
int sys_ptrace(int, ...);
|
||||
int sys_sysctl(const int *, unsigned, void *, size_t *, void *, size_t);
|
||||
int sys_ioprio_get(int, int);
|
||||
int sys_ioprio_set(int, int, int);
|
||||
int tmpfd(void);
|
||||
int touch(const char *, unsigned);
|
||||
int unveil(const char *, const char *);
|
||||
long ptrace(int, ...);
|
||||
int fadvise(int, uint64_t, uint64_t, int);
|
||||
ssize_t copyfd(int, int, size_t);
|
||||
ssize_t readansi(int, char *, size_t);
|
||||
ssize_t tinyprint(int, const char *, ...) nullterminated();
|
||||
void shm_path_np(const char *, char[hasatleast 78]);
|
||||
#endif /* _COSMO_SOURCE */
|
||||
|
||||
int __wifstopped(int) pureconst;
|
||||
|
||||
+2
-2
@@ -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
|
||||
@@ -145,6 +143,8 @@ o/$(MODE)/libc/calls/tailcontext.o: libc/calls/tailcontext.S
|
||||
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
|
||||
o/$(MODE)/libc/calls/stackjump.o: libc/calls/stackjump.S
|
||||
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
|
||||
o/$(MODE)/libc/calls/sched_yield.o: libc/calls/sched_yield.S
|
||||
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
|
||||
|
||||
LIBC_CALLS_LIBS = $(foreach x,$(LIBC_CALLS_ARTIFACTS),$($(x)))
|
||||
LIBC_CALLS_SRCS = $(foreach x,$(LIBC_CALLS_ARTIFACTS),$($(x)_SRCS))
|
||||
|
||||
@@ -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__ */
|
||||
@@ -0,0 +1,35 @@
|
||||
/*-*- 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/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#ifdef __x86_64__
|
||||
|
||||
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__ */
|
||||
@@ -16,6 +16,7 @@
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/blockcancel.internal.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
@@ -37,13 +38,12 @@
|
||||
* @raise EBADF on OpenBSD if `first` is greater than highest fd
|
||||
* @raise EINVAL if flags are bad or first is greater than last
|
||||
* @raise EMFILE if a weird race condition happens on Linux
|
||||
* @raise ECANCELED if thread was cancelled in masked mode
|
||||
* @raise EINTR possibly on OpenBSD
|
||||
* @raise ENOMEM on Linux maybe
|
||||
*/
|
||||
int closefrom(int first) {
|
||||
int rc, err;
|
||||
(void)err;
|
||||
int rc;
|
||||
BLOCK_CANCELATION;
|
||||
if (first < 0) {
|
||||
// consistent with openbsd
|
||||
// freebsd allows this but it's dangerous
|
||||
@@ -58,6 +58,7 @@ int closefrom(int first) {
|
||||
} else {
|
||||
rc = enosys();
|
||||
}
|
||||
ALLOW_CANCELATION;
|
||||
STRACE("closefrom(%d) → %d% m", first, rc);
|
||||
return rc;
|
||||
}
|
||||
+1
-1
@@ -44,7 +44,7 @@ ssize_t copyfd(int in, int out, size_t n) {
|
||||
if (dw != dr) {
|
||||
// POSIX requires atomic IO up to PIPE_BUF
|
||||
// The minimum permissible PIPE_BUF is 512
|
||||
abort();
|
||||
notpossible;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
|
||||
@@ -91,11 +91,9 @@ textwindows int GetNtOpenFlags(int flags, int mode, uint32_t *out_perm,
|
||||
|
||||
// Be as generous as possible in sharing file access. Not doing this
|
||||
// you'll quickly find yourself no longer able to administer Windows
|
||||
if (flags & _O_EXCL) {
|
||||
share = kNtFileShareExclusive;
|
||||
} else {
|
||||
share = kNtFileShareRead | kNtFileShareWrite | kNtFileShareDelete;
|
||||
}
|
||||
// and we need this to be the case even in O_EXCL mode otherwise the
|
||||
// shm_open_test.c won't behave consistently on Windows like on UNIX
|
||||
share = kNtFileShareRead | kNtFileShareWrite | kNtFileShareDelete;
|
||||
|
||||
// These POSIX to WIN32 mappings are relatively straightforward.
|
||||
if (flags & _O_EXCL) {
|
||||
|
||||
+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++ = '/';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,5 +25,6 @@
|
||||
bool IsApeLoadable(char buf[8]) {
|
||||
return READ32LE(buf) == READ32LE("\177ELF") ||
|
||||
READ64LE(buf) == READ64LE("MZqFpD='") ||
|
||||
READ64LE(buf) == READ64LE("JTqFpD='");
|
||||
READ64LE(buf) == READ64LE("jartsr='") ||
|
||||
READ64LE(buf) == READ64LE("APEDBG='");
|
||||
}
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/metalfile.internal.h"
|
||||
#include "libc/intrin/directmap.internal.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/mem/mem.h"
|
||||
@@ -74,6 +75,7 @@ textstartup void InitializeMetalFile(void) {
|
||||
memcpy(copied_base, (void *)(BANE + IMAGE_BASE_PHYSICAL), size);
|
||||
__ape_com_base = copied_base;
|
||||
__ape_com_size = size;
|
||||
KINFOF("%s @ %p,+%#zx", APE_COM_NAME, copied_base, size);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,12 +27,12 @@
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/calls/metalfile.internal.h"
|
||||
|
||||
.init.start 101,_init_metalfile
|
||||
.init.start 102,_init_metalfile
|
||||
push %rdi
|
||||
push %rsi
|
||||
call InitializeMetalFile
|
||||
pop %rsi
|
||||
pop %rdi
|
||||
.init.end 101,_init_metalfile
|
||||
.init.end 102,_init_metalfile
|
||||
APE_COM_NAME:
|
||||
.endobj APE_COM_NAME,globl,hidden
|
||||
+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) {
|
||||
|
||||
@@ -33,6 +33,9 @@ static int FindPromise(const char *name) {
|
||||
/**
|
||||
* Parses the arguments to pledge() into a bitmask.
|
||||
*
|
||||
* @param out receives the integral promises mask, which zero is defined
|
||||
* as the set of all promises, and -1 is defined as the empty set of
|
||||
* promises, which is equivalent to `promises` being an empty string
|
||||
* @return 0 on success, or -1 if invalid
|
||||
*/
|
||||
int ParsePromises(const char *promises, unsigned long *out,
|
||||
|
||||
@@ -535,7 +535,10 @@ static const struct thatispacked SyscallName {
|
||||
};
|
||||
|
||||
static const uint16_t kPledgeDefault[] = {
|
||||
__NR_linux_exit, // thread return / exit()
|
||||
__NR_linux_exit, // thread return / exit()
|
||||
#ifdef __NR_linux_arch_prctl //
|
||||
__NR_linux_arch_prctl, // or else launching musl process crashes (tls)
|
||||
#endif //
|
||||
};
|
||||
|
||||
// stdio contains all the benign system calls. openbsd makes the
|
||||
|
||||
File renamed without changes.
+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();
|
||||
}
|
||||
|
||||
|
||||
File renamed without changes.
@@ -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,30 @@
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/blockcancel.internal.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"
|
||||
|
||||
#define XNU_F_GETPATH 50
|
||||
#define XNU_MAXPATHLEN 1024
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
char *sys_getcwd_xnu(char *res, size_t size) {
|
||||
/**
|
||||
* Opens POSIX named memory object.
|
||||
*
|
||||
* @param name should begin with a `/` and shouldn't contain subsequent
|
||||
* slash characters, and furthermore shouldn't exceed NAME_MAX, for
|
||||
* maximum portability; POSIX defines it as opening a multi-process
|
||||
* object and leaves all other names implementation defined; and we
|
||||
* choose (in this implementation) to define those names as meaning
|
||||
* the same thing while not imposing any length limit; cosmo always
|
||||
* feeds your name through BLAKE2B to create a `.sem` file existing
|
||||
* under `/dev/shm` if available, otherwise it will go under `/tmp`
|
||||
* @return open file descriptor, or -1 w/ errno
|
||||
*/
|
||||
int shm_open(const char *name, int oflag, unsigned mode) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
return ret;
|
||||
char path[78];
|
||||
shm_path_np(name, path);
|
||||
BLOCK_CANCELATION;
|
||||
fd = openat(AT_FDCWD, path, oflag, mode);
|
||||
ALLOW_CANCELATION;
|
||||
return fd;
|
||||
}
|
||||
@@ -20,36 +20,26 @@
|
||||
#include "libc/dce.h"
|
||||
#include "libc/str/blake2.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
||||
/**
|
||||
* Returns filesystem pathname of named semaphore.
|
||||
*
|
||||
* @param name is `name` of semaphore which should begin with slash
|
||||
* @param buf is temporary storage with at least `size` bytes
|
||||
* @param size is size of `buf` in bytes
|
||||
* @param buf is temporary storage with at least 78 bytes
|
||||
* @return pointer to file system path
|
||||
* @raise ENAMETOOLONG if constructed path would exceed `size`
|
||||
*/
|
||||
const char *sem_path_np(const char *name, char *buf, size_t size) {
|
||||
void shm_path_np(const char *name, char buf[hasatleast 78]) {
|
||||
char *p;
|
||||
unsigned n;
|
||||
const char *path, *a;
|
||||
const char *a;
|
||||
uint8_t digest[BLAKE2B256_DIGEST_LENGTH];
|
||||
a = "/tmp/", n = 5;
|
||||
if (IsLinux() && isdirectory("/dev/shm")) {
|
||||
a = "/dev/shm/", n = 9;
|
||||
}
|
||||
if (n + BLAKE2B256_DIGEST_LENGTH * 2 + 4 < size) {
|
||||
BLAKE2B256(name, strlen(name), digest);
|
||||
p = mempcpy(buf, a, n);
|
||||
p = hexpcpy(p, digest, BLAKE2B256_DIGEST_LENGTH);
|
||||
p = mempcpy(p, ".sem", 5);
|
||||
path = buf;
|
||||
} else {
|
||||
enametoolong();
|
||||
path = 0;
|
||||
}
|
||||
return path;
|
||||
BLAKE2B256(name, strlen(name), digest);
|
||||
p = mempcpy(buf, a, n);
|
||||
p = hexpcpy(p, digest, BLAKE2B256_DIGEST_LENGTH);
|
||||
mempcpy(p, ".sem", 5);
|
||||
}
|
||||
@@ -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,13 @@
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/runtime/dlfcn.h"
|
||||
#include "libc/calls/calls.h"
|
||||
|
||||
char *dlerror(void) {
|
||||
return "cosmopolitan doesn't support dsos";
|
||||
}
|
||||
|
||||
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;
|
||||
/**
|
||||
* Deletes POSIX named memory object.
|
||||
*/
|
||||
int shm_unlink(const char *name) {
|
||||
char path[78];
|
||||
shm_path_np(name, path);
|
||||
return unlink(path);
|
||||
}
|
||||
+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);
|
||||
|
||||
@@ -29,6 +29,7 @@ void __stat2cosmo(struct stat *restrict st, const union metastat *ms) {
|
||||
st->st_uid = ms->linux.st_uid;
|
||||
st->st_gid = ms->linux.st_gid;
|
||||
st->st_flags = 0;
|
||||
st->st_gen = 0;
|
||||
st->st_rdev = ms->linux.st_rdev;
|
||||
st->st_size = ms->linux.st_size;
|
||||
st->st_blksize = ms->linux.st_blksize;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -12,7 +12,7 @@ struct sysinfo {
|
||||
uint64_t bufferram; /* lingering disk pages; see fadvise */
|
||||
uint64_t totalswap; /* size of emergency memory */
|
||||
uint64_t freeswap; /* hopefully equal to totalswap */
|
||||
int16_t procs; /* number of processes */
|
||||
uint16_t procs; /* number of processes */
|
||||
int16_t __ignore1; /* padding */
|
||||
int32_t __ignore2; /* padding */
|
||||
uint64_t totalhigh; /* wut */
|
||||
|
||||
@@ -47,6 +47,29 @@ static inline bool timeval_isvalid(struct timeval __tv) {
|
||||
}
|
||||
#endif /* _COSMO_SOURCE */
|
||||
|
||||
#if defined(_GNU_SOURCE) || defined(_BSD_SOURCE)
|
||||
#define timerisset(t) ((t)->tv_sec || (t)->tv_usec)
|
||||
#define timerclear(t) ((t)->tv_sec = (t)->tv_usec = 0)
|
||||
#define timercmp(s, t, op) \
|
||||
((s)->tv_sec == (t)->tv_sec ? (s)->tv_usec op(t)->tv_usec \
|
||||
: (s)->tv_sec op(t)->tv_sec)
|
||||
#define timeradd(s, t, a) \
|
||||
(void)((a)->tv_sec = (s)->tv_sec + (t)->tv_sec, \
|
||||
((a)->tv_usec = (s)->tv_usec + (t)->tv_usec) >= 1000000 && \
|
||||
((a)->tv_usec -= 1000000, (a)->tv_sec++))
|
||||
#define timersub(s, t, a) \
|
||||
(void)((a)->tv_sec = (s)->tv_sec - (t)->tv_sec, \
|
||||
((a)->tv_usec = (s)->tv_usec - (t)->tv_usec) < 0 && \
|
||||
((a)->tv_usec += 1000000, (a)->tv_sec--))
|
||||
#endif
|
||||
|
||||
#ifdef _GNU_SOURCE
|
||||
#define TIMEVAL_TO_TIMESPEC(tv, ts) \
|
||||
((ts)->tv_sec = (tv)->tv_sec, (ts)->tv_nsec = (tv)->tv_usec * 1000, (void)0)
|
||||
#define TIMESPEC_TO_TIMEVAL(tv, ts) \
|
||||
((tv)->tv_sec = (ts)->tv_sec, (tv)->tv_usec = (ts)->tv_nsec / 1000, (void)0)
|
||||
#endif
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_LIBC_CALLS_STRUCT_TIMEVAL_H_ */
|
||||
@@ -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);
|
||||
|
||||
+39
-11
@@ -16,27 +16,55 @@
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/calls/struct/sysinfo.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/calls/struct/sysinfo.internal.h"
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/nt/accounting.h"
|
||||
#include "libc/nt/process.h"
|
||||
#include "libc/nt/struct/memorystatusex.h"
|
||||
#include "libc/nt/struct/systeminfo.h"
|
||||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/nt/systeminfo.h"
|
||||
|
||||
static textwindows uint16_t GetProcessCount(void) {
|
||||
uint16_t res;
|
||||
uint32_t have, got, *pids;
|
||||
uint32_t stack_memory[1000];
|
||||
have = 0xFFFF * 4;
|
||||
if (!_weaken(malloc) || !(pids = _weaken(malloc)(have))) {
|
||||
pids = stack_memory;
|
||||
have = sizeof(stack_memory);
|
||||
}
|
||||
if (EnumProcesses(pids, have, &got)) {
|
||||
res = got / 4;
|
||||
} else {
|
||||
res = 0;
|
||||
}
|
||||
if (pids != stack_memory && _weaken(free)) {
|
||||
_weaken(free)(pids);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
textwindows int sys_sysinfo_nt(struct sysinfo *info) {
|
||||
int i;
|
||||
double load[3];
|
||||
struct NtMemoryStatusEx memstat;
|
||||
struct NtSystemInfo sysinfo;
|
||||
GetSystemInfo(&sysinfo);
|
||||
BLOCK_SIGNALS;
|
||||
if (sys_getloadavg_nt(load, 3) != -1) {
|
||||
for (i = 0; i < 3; ++i) {
|
||||
info->loads[i] = load[i] * 65536;
|
||||
}
|
||||
}
|
||||
memstat.dwLength = sizeof(struct NtMemoryStatusEx);
|
||||
if (GlobalMemoryStatusEx(&memstat)) {
|
||||
info->mem_unit = 1;
|
||||
info->totalram = memstat.ullTotalPhys;
|
||||
info->freeram = memstat.ullAvailPhys;
|
||||
info->procs = sysinfo.dwNumberOfProcessors;
|
||||
info->uptime = GetTickCount64() / 1000;
|
||||
return 0;
|
||||
} else {
|
||||
return __winerr();
|
||||
}
|
||||
info->uptime = GetTickCount64() / 1000;
|
||||
info->procs = GetProcessCount();
|
||||
info->mem_unit = 1;
|
||||
ALLOW_SIGNALS;
|
||||
return 0;
|
||||
}
|
||||
+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));
|
||||
|
||||
+1
-51
@@ -188,56 +188,6 @@ static int unveil_init(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Joins paths, e.g.
|
||||
*
|
||||
* 0 + 0 → 0
|
||||
* "" + "" → ""
|
||||
* "a" + 0 → "a"
|
||||
* "a" + "" → "a/"
|
||||
* 0 + "b" → "b"
|
||||
* "" + "b" → "b"
|
||||
* "." + "b" → "./b"
|
||||
* "b" + "." → "b/."
|
||||
* "a" + "b" → "a/b"
|
||||
* "a/" + "b" → "a/b"
|
||||
* "a" + "b/" → "a/b/"
|
||||
* "a" + "/b" → "/b"
|
||||
*
|
||||
* @return joined path, which may be `buf`, `path`, or `other`, or null
|
||||
* if (1) `buf` didn't have enough space, or (2) both `path` and
|
||||
* `other` were null
|
||||
*/
|
||||
static char *JoinPaths(char *buf, size_t size, const char *path,
|
||||
const char *other) {
|
||||
size_t pathlen, otherlen;
|
||||
if (!other) return (char *)path;
|
||||
if (!path) return (char *)other;
|
||||
pathlen = strlen(path);
|
||||
if (!pathlen || *other == '/') {
|
||||
return (/*unconst*/ char *)other;
|
||||
}
|
||||
otherlen = strlen(other);
|
||||
if (path[pathlen - 1] == '/') {
|
||||
if (pathlen + otherlen + 1 <= size) {
|
||||
memmove(buf, path, pathlen);
|
||||
memmove(buf + pathlen, other, otherlen + 1);
|
||||
return buf;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
if (pathlen + 1 + otherlen + 1 <= size) {
|
||||
memmove(buf, path, pathlen);
|
||||
buf[pathlen] = '/';
|
||||
memmove(buf + pathlen + 1, other, otherlen + 1);
|
||||
return buf;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int sys_unveil_linux(const char *path, const char *permissions) {
|
||||
#pragma GCC push_options
|
||||
#pragma GCC diagnostic ignored "-Wframe-larger-than="
|
||||
@@ -302,7 +252,7 @@ int sys_unveil_linux(const char *path, const char *permissions) {
|
||||
// next = join(dirname(next), link)
|
||||
strcpy(b.buf2, next);
|
||||
dir = dirname(b.buf2);
|
||||
if ((next = JoinPaths(b.buf3, PATH_MAX, dir, b.lbuf))) {
|
||||
if ((next = __join_paths(b.buf3, PATH_MAX, dir, b.lbuf))) {
|
||||
// next now points to either: buf3, buf2, lbuf, rodata
|
||||
strcpy(b.buf4, next);
|
||||
next = b.buf4;
|
||||
|
||||
+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_
|
||||
|
||||
@@ -728,6 +728,8 @@ void abort(void) wontreturn;
|
||||
#endif
|
||||
|
||||
#define __weak_reference(sym, alias) \
|
||||
__weak_reference_impl(sym, alias)
|
||||
#define __weak_reference_impl(sym, alias) \
|
||||
__asm__(".weak\t" #alias "\n\t" \
|
||||
".equ\t" #alias ", " #sym "\n\t" \
|
||||
".type\t" #alias ",@notype")
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
#ifdef __COSMOPOLITAN__
|
||||
#undef __COSMOPOLITAN__
|
||||
#endif
|
||||
|
||||
#define __COSMOPOLITAN_MAJOR__ 3
|
||||
#define __COSMOPOLITAN_MINOR__ 0
|
||||
#define __COSMOPOLITAN_PATCH__ 0
|
||||
#define __COSMOPOLITAN_PATCH__ 2
|
||||
#define __COSMOPOLITAN__ \
|
||||
(100000000 * __COSMOPOLITAN_MAJOR__ + 1000000 * __COSMOPOLITAN_MINOR__ + \
|
||||
__COSMOPOLITAN_PATCH__)
|
||||
|
||||
@@ -21,7 +21,10 @@ extern struct CxaAtexitBlocks __cxa_blocks;
|
||||
|
||||
void __cxa_lock(void);
|
||||
void __cxa_unlock(void);
|
||||
void __cxa_thread_finalize(void);
|
||||
void __cxa_printexits(FILE *, void *);
|
||||
int __cxa_thread_atexit(void *, void *, void *);
|
||||
int __cxa_thread_atexit_impl(void *, void *, void *);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
|
||||
@@ -25,7 +25,9 @@
|
||||
│ OTHER DEALINGS IN THE SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/runtime/pc.internal.h"
|
||||
|
||||
// Code and data structures for bare metal interrupt handling.
|
||||
|
||||
@@ -119,8 +119,6 @@ o/$(MODE)/libc/intrin/ksockoptnames.o: libc/intrin/ksockoptnames.S
|
||||
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
|
||||
o/$(MODE)/libc/intrin/ktcpoptnames.o: libc/intrin/ktcpoptnames.S
|
||||
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
|
||||
o/$(MODE)/libc/intrin/sched_yield.o: libc/intrin/sched_yield.S
|
||||
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
|
||||
o/$(MODE)/libc/intrin/stackcall.o: libc/intrin/stackcall.S
|
||||
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
|
||||
|
||||
|
||||
+30
-15
@@ -385,14 +385,31 @@ privileged long kloghandle(void) {
|
||||
return __klog_handle;
|
||||
}
|
||||
|
||||
#ifdef __x86_64__
|
||||
privileged void _klog_serial(const char *b, size_t n) {
|
||||
size_t i;
|
||||
uint16_t dx;
|
||||
unsigned char al;
|
||||
for (i = 0; i < n; ++i) {
|
||||
for (;;) {
|
||||
dx = 0x3F8 + UART_LSR;
|
||||
asm("inb\t%1,%0" : "=a"(al) : "dN"(dx));
|
||||
if (al & UART_TTYTXR) break;
|
||||
asm("pause");
|
||||
}
|
||||
dx = 0x3F8;
|
||||
asm volatile("outb\t%0,%1"
|
||||
: /* no inputs */
|
||||
: "a"(b[i]), "dN"(dx));
|
||||
}
|
||||
}
|
||||
#endif /* __x86_64__ */
|
||||
|
||||
privileged void klog(const char *b, size_t n) {
|
||||
#ifdef __x86_64__
|
||||
int e;
|
||||
long h;
|
||||
size_t i;
|
||||
uint16_t dx;
|
||||
uint32_t wrote;
|
||||
unsigned char al;
|
||||
long rax, rdi, rsi, rdx;
|
||||
if ((h = kloghandle()) == -1) {
|
||||
return;
|
||||
@@ -407,23 +424,15 @@ privileged void klog(const char *b, size_t n) {
|
||||
if (_weaken(_klog_vga)) {
|
||||
_weaken(_klog_vga)(b, n);
|
||||
}
|
||||
for (i = 0; i < n; ++i) {
|
||||
for (;;) {
|
||||
dx = 0x3F8 + UART_LSR;
|
||||
asm("inb\t%1,%0" : "=a"(al) : "dN"(dx));
|
||||
if (al & UART_TTYTXR) break;
|
||||
asm("pause");
|
||||
}
|
||||
dx = 0x3F8;
|
||||
asm volatile("outb\t%0,%1"
|
||||
: /* no inputs */
|
||||
: "a"(b[i]), "dN"(dx));
|
||||
}
|
||||
_klog_serial(b, n);
|
||||
} else {
|
||||
asm volatile("syscall"
|
||||
: "=a"(rax), "=D"(rdi), "=S"(rsi), "=d"(rdx)
|
||||
: "0"(__NR_write), "1"(h), "2"(b), "3"(n)
|
||||
: "rcx", "r8", "r9", "r10", "r11", "memory", "cc");
|
||||
if (rax < 0) {
|
||||
__klog_handle = 0;
|
||||
}
|
||||
}
|
||||
#elif defined(__aarch64__)
|
||||
// this isn't a cancelation point because we don't acknowledge eintr
|
||||
@@ -438,6 +447,9 @@ privileged void klog(const char *b, size_t n) {
|
||||
: "=r"(res_x0)
|
||||
: "r"(r0), "r"(r1), "r"(r2), "r"(r8), "r"(r16)
|
||||
: "memory");
|
||||
if (res_x0 < 0) {
|
||||
__klog_handle = 0;
|
||||
}
|
||||
#else
|
||||
#error "unsupported architecture"
|
||||
#endif
|
||||
@@ -1152,3 +1164,6 @@ privileged void kprintf(const char *fmt, ...) {
|
||||
kvprintf(fmt, v);
|
||||
va_end(v);
|
||||
}
|
||||
|
||||
__weak_reference(kprintf, uprintf);
|
||||
__weak_reference(kvprintf, uvprintf);
|
||||
+30
-6
@@ -9,25 +9,49 @@
|
||||
#define kvsnprintf __kvsnprintf
|
||||
#define kloghandle __kloghandle
|
||||
#define kisdangerous __kisdangerous
|
||||
#define uprintf __uprintf
|
||||
#define uvprintf __uvprintf
|
||||
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
void kprintf(const char *, ...);
|
||||
size_t ksnprintf(char *, size_t, const char *, ...);
|
||||
void kvprintf(const char *, va_list);
|
||||
size_t kvsnprintf(char *, size_t, const char *, va_list);
|
||||
bool kisdangerous(const void *);
|
||||
|
||||
void kprintf(const char *, ...);
|
||||
size_t ksnprintf(char *, size_t, const char *, ...);
|
||||
void kvprintf(const char *, va_list);
|
||||
size_t kvsnprintf(char *, size_t, const char *, va_list);
|
||||
|
||||
bool kisdangerous(const void *);
|
||||
|
||||
void klog(const char *, size_t);
|
||||
void _klog_serial(const char *, size_t);
|
||||
long kloghandle(void);
|
||||
|
||||
void uprintf(const char *, ...);
|
||||
void uvprintf(const char *, va_list);
|
||||
|
||||
#ifndef TINY
|
||||
#define KINFOF(FMT, ...) \
|
||||
do { \
|
||||
uprintf("\r\e[35m%s:%d: " FMT "\e[0m\n", \
|
||||
__FILE__, __LINE__, ## __VA_ARGS__); \
|
||||
} while (0)
|
||||
#define KWARNF(FMT, ...) \
|
||||
do { \
|
||||
uprintf("\r\e[94;49mwarn: %s:%d: " FMT "\e[0m\n", \
|
||||
__FILE__, __LINE__, ## __VA_ARGS__); \
|
||||
} while (0)
|
||||
#else
|
||||
#define KINFOF(FMT, ...) ((void)0)
|
||||
#define KWARNF(FMT, ...) ((void)0)
|
||||
#endif
|
||||
#define KDIEF(FMT, ...) \
|
||||
do { \
|
||||
kprintf("\r\e[30;101mfatal: %s:%d: " FMT "\e[0m\n", \
|
||||
__FILE__, __LINE__, ## __VA_ARGS__); \
|
||||
for (;;) asm volatile("cli\n\thlt"); \
|
||||
} while (0)
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* _COSMO_SOURCE */
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/nexgen32e/yield.h"
|
||||
#include "libc/runtime/internal.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/thread/tls.h"
|
||||
@@ -64,7 +65,7 @@
|
||||
* @see pthread_spin_lock()
|
||||
* @vforksafe
|
||||
*/
|
||||
int pthread_mutex_lock(pthread_mutex_t *mutex) {
|
||||
errno_t pthread_mutex_lock(pthread_mutex_t *mutex) {
|
||||
int t;
|
||||
|
||||
LOCKTRACE("pthread_mutex_lock(%t)", mutex);
|
||||
@@ -82,7 +83,7 @@ int pthread_mutex_lock(pthread_mutex_t *mutex) {
|
||||
|
||||
if (mutex->_type == PTHREAD_MUTEX_NORMAL) {
|
||||
while (atomic_exchange_explicit(&mutex->_lock, 1, memory_order_acquire)) {
|
||||
pthread_yield();
|
||||
spin_yield();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -102,7 +103,7 @@ int pthread_mutex_lock(pthread_mutex_t *mutex) {
|
||||
}
|
||||
|
||||
while (atomic_exchange_explicit(&mutex->_lock, 1, memory_order_acquire)) {
|
||||
pthread_yield();
|
||||
spin_yield();
|
||||
}
|
||||
|
||||
mutex->_depth = 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 2022 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 │
|
||||
@@ -19,27 +19,29 @@
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/runtime/internal.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/thread/tls.h"
|
||||
#include "third_party/nsync/mu.h"
|
||||
|
||||
/**
|
||||
* Locks mutex if it isn't locked already.
|
||||
* Attempts acquiring lock.
|
||||
*
|
||||
* Unlike pthread_mutex_lock() this function won't block and instead
|
||||
* returns an error immediately if the lock couldn't be acquired.
|
||||
*
|
||||
* @return 0 on success, or errno on error
|
||||
* @raise EBUSY if lock is already held
|
||||
* @raise ENOTRECOVERABLE if `mutex` is corrupted
|
||||
* @return 0 if lock was acquired, otherwise an errno
|
||||
* @raise EAGAIN if maximum number of recursive locks is held
|
||||
* @raise EBUSY if lock is currently held in read or write mode
|
||||
* @raise EINVAL if `mutex` doesn't refer to an initialized lock
|
||||
* @raise EDEADLK if `mutex` is `PTHREAD_MUTEX_ERRORCHECK` and the
|
||||
* current thread already holds this mutex
|
||||
*/
|
||||
errno_t pthread_mutex_trylock(pthread_mutex_t *mutex) {
|
||||
int t;
|
||||
|
||||
if (__tls_enabled && //
|
||||
mutex->_type == PTHREAD_MUTEX_NORMAL && //
|
||||
// delegate to *NSYNC if possible
|
||||
if (mutex->_type == PTHREAD_MUTEX_NORMAL &&
|
||||
mutex->_pshared == PTHREAD_PROCESS_PRIVATE && //
|
||||
_weaken(nsync_mu_trylock)) {
|
||||
if (_weaken(nsync_mu_trylock)((nsync_mu *)mutex)) {
|
||||
@@ -49,6 +51,7 @@ errno_t pthread_mutex_trylock(pthread_mutex_t *mutex) {
|
||||
}
|
||||
}
|
||||
|
||||
// handle normal mutexes
|
||||
if (mutex->_type == PTHREAD_MUTEX_NORMAL) {
|
||||
if (!atomic_exchange_explicit(&mutex->_lock, 1, memory_order_acquire)) {
|
||||
return 0;
|
||||
@@ -57,6 +60,7 @@ errno_t pthread_mutex_trylock(pthread_mutex_t *mutex) {
|
||||
}
|
||||
}
|
||||
|
||||
// handle recursive and error check mutexes
|
||||
t = gettid();
|
||||
if (mutex->_owner == t) {
|
||||
if (mutex->_type != PTHREAD_MUTEX_ERRORCHECK) {
|
||||
@@ -67,15 +71,17 @@ errno_t pthread_mutex_trylock(pthread_mutex_t *mutex) {
|
||||
return EAGAIN;
|
||||
}
|
||||
} else {
|
||||
return EBUSY;
|
||||
return EDEADLK;
|
||||
}
|
||||
}
|
||||
|
||||
if (!atomic_exchange_explicit(&mutex->_lock, 1, memory_order_acquire)) {
|
||||
mutex->_depth = 0;
|
||||
mutex->_owner = t;
|
||||
return 0;
|
||||
} else {
|
||||
if (atomic_exchange_explicit(&mutex->_lock, 1, memory_order_acquire)) {
|
||||
return EBUSY;
|
||||
}
|
||||
|
||||
mutex->_depth = 0;
|
||||
mutex->_owner = t;
|
||||
mutex->_pid = __pid;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -34,7 +34,7 @@
|
||||
* @raises EPERM if in error check mode and not owned by caller
|
||||
* @vforksafe
|
||||
*/
|
||||
int pthread_mutex_unlock(pthread_mutex_t *mutex) {
|
||||
errno_t pthread_mutex_unlock(pthread_mutex_t *mutex) {
|
||||
int t;
|
||||
|
||||
LOCKTRACE("pthread_mutex_unlock(%t)", mutex);
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "libc/assert.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/nexgen32e/yield.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
|
||||
intptr_t _pthread_syshand(struct PosixThread *pt) {
|
||||
@@ -27,6 +28,6 @@ intptr_t _pthread_syshand(struct PosixThread *pt) {
|
||||
for (;;) {
|
||||
syshand = atomic_load_explicit(&pt->tib->tib_syshand, memory_order_acquire);
|
||||
if (syshand) return syshand;
|
||||
pthread_yield();
|
||||
spin_yield();
|
||||
}
|
||||
}
|
||||
@@ -17,12 +17,13 @@
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/nexgen32e/yield.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
|
||||
int _pthread_tid(struct PosixThread *pt) {
|
||||
int tid = 0;
|
||||
while (pt && !(tid = atomic_load_explicit(&pt->ptid, memory_order_acquire))) {
|
||||
pthread_yield();
|
||||
spin_yield();
|
||||
}
|
||||
return tid;
|
||||
}
|
||||
+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);
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
|
||||
│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ This is free and unencumbered software released into the public domain. │
|
||||
│ │
|
||||
│ Anyone is free to copy, modify, publish, use, compile, sell, or │
|
||||
│ distribute this software, either in source code form or as a compiled │
|
||||
│ binary, for any purpose, commercial or non-commercial, and by any │
|
||||
│ means. │
|
||||
│ │
|
||||
│ In jurisdictions that recognize copyright laws, the author or authors │
|
||||
│ of this software dedicate any and all copyright interest in the │
|
||||
│ software to the public domain. We make this dedication for the benefit │
|
||||
│ of the public at large and to the detriment of our heirs and │
|
||||
│ successors. We intend this dedication to be an overt act of │
|
||||
│ relinquishment in perpetuity of all present and future rights to this │
|
||||
│ software under copyright law. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │
|
||||
│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │
|
||||
│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │
|
||||
│ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR │
|
||||
│ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │
|
||||
│ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR │
|
||||
│ OTHER DEALINGS IN THE SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/dce.h"
|
||||
#include "libc/irq/acpi.internal.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/runtime/pc.internal.h"
|
||||
|
||||
.init.start 312,_init_acpi_fadt
|
||||
push %rdi
|
||||
push %rsi
|
||||
call _AcpiFadtInit
|
||||
pop %rsi
|
||||
pop %rdi
|
||||
.init.end 312,_init_acpi_fadt
|
||||
.data
|
||||
_AcpiBootFlags:
|
||||
.short kAcpiFadtLegacyDevices | kAcpiFadt8042
|
||||
.endobj _AcpiBootFlags,globl
|
||||
.previous
|
||||
@@ -0,0 +1,80 @@
|
||||
/*-*- 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│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ This is free and unencumbered software released into the public domain. │
|
||||
│ │
|
||||
│ Anyone is free to copy, modify, publish, use, compile, sell, or │
|
||||
│ distribute this software, either in source code form or as a compiled │
|
||||
│ binary, for any purpose, commercial or non-commercial, and by any │
|
||||
│ means. │
|
||||
│ │
|
||||
│ In jurisdictions that recognize copyright laws, the author or authors │
|
||||
│ of this software dedicate any and all copyright interest in the │
|
||||
│ software to the public domain. We make this dedication for the benefit │
|
||||
│ of the public at large and to the detriment of our heirs and │
|
||||
│ successors. We intend this dedication to be an overt act of │
|
||||
│ relinquishment in perpetuity of all present and future rights to this │
|
||||
│ software under copyright law. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │
|
||||
│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │
|
||||
│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │
|
||||
│ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR │
|
||||
│ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │
|
||||
│ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR │
|
||||
│ OTHER DEALINGS IN THE SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/irq/acpi.internal.h"
|
||||
|
||||
#ifdef __x86_64__
|
||||
|
||||
textstartup static void _AcpiDsdtInit(uintptr_t dsdt_phy) {
|
||||
const AcpiTableDsdt *dsdt;
|
||||
size_t length;
|
||||
if (!dsdt_phy) {
|
||||
KWARNF("FADT: no DSDT");
|
||||
return;
|
||||
}
|
||||
dsdt = _AcpiMapTable(dsdt_phy);
|
||||
KINFOF("FADT: DSDT @ %p", dsdt);
|
||||
length = dsdt->Header.Length;
|
||||
if (length <= offsetof(AcpiTableDsdt, Aml)) {
|
||||
KWARNF("DSDT: no AML?");
|
||||
return;
|
||||
}
|
||||
/* TODO: parse AML to discover hardware configuration */
|
||||
}
|
||||
|
||||
textstartup void _AcpiFadtInit(void) {
|
||||
if (IsMetal()) {
|
||||
const AcpiTableFadt *fadt;
|
||||
size_t length;
|
||||
uint16_t flags;
|
||||
uintptr_t dsdt_phy = 0;
|
||||
if (!_AcpiSuccess(_AcpiGetTable("FACP", 0, (void **)&fadt))) {
|
||||
KINFOF("no FADT found");
|
||||
return;
|
||||
}
|
||||
length = fadt->Header.Length;
|
||||
KINFOF("FADT @ %p,+%#zx", fadt, length);
|
||||
_Static_assert(offsetof(AcpiTableFadt, Dsdt) == 40);
|
||||
_Static_assert(offsetof(AcpiTableFadt, BootFlags) == 109);
|
||||
_Static_assert(offsetof(AcpiTableFadt, XDsdt) == 140);
|
||||
if (length >= offsetof(AcpiTableFadt, BootFlags) + sizeof(fadt->BootFlags))
|
||||
{
|
||||
_AcpiBootFlags = flags = fadt->BootFlags;
|
||||
KINFOF("FADT: boot flags %#x", (unsigned)flags);
|
||||
}
|
||||
if (length >= offsetof(AcpiTableFadt, XDsdt) + sizeof(fadt->XDsdt) &&
|
||||
fadt->XDsdt) {
|
||||
dsdt_phy = fadt->XDsdt;
|
||||
} else if (length >= offsetof(AcpiTableFadt, Dsdt) + sizeof(fadt->Dsdt)) {
|
||||
dsdt_phy = fadt->Dsdt;
|
||||
}
|
||||
_AcpiDsdtInit(dsdt_phy);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* __x86_64__ */
|
||||
@@ -0,0 +1,51 @@
|
||||
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
|
||||
│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ This is free and unencumbered software released into the public domain. │
|
||||
│ │
|
||||
│ Anyone is free to copy, modify, publish, use, compile, sell, or │
|
||||
│ distribute this software, either in source code form or as a compiled │
|
||||
│ binary, for any purpose, commercial or non-commercial, and by any │
|
||||
│ means. │
|
||||
│ │
|
||||
│ In jurisdictions that recognize copyright laws, the author or authors │
|
||||
│ of this software dedicate any and all copyright interest in the │
|
||||
│ software to the public domain. We make this dedication for the benefit │
|
||||
│ of the public at large and to the detriment of our heirs and │
|
||||
│ successors. We intend this dedication to be an overt act of │
|
||||
│ relinquishment in perpetuity of all present and future rights to this │
|
||||
│ software under copyright law. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │
|
||||
│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │
|
||||
│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │
|
||||
│ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR │
|
||||
│ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │
|
||||
│ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR │
|
||||
│ OTHER DEALINGS IN THE SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/dce.h"
|
||||
#include "libc/irq/acpi.internal.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/runtime/pc.internal.h"
|
||||
|
||||
.init.start 311,_init_acpi_madt
|
||||
push %rdi
|
||||
push %rsi
|
||||
call _AcpiMadtInit
|
||||
pop %rsi
|
||||
pop %rdi
|
||||
.init.end 311,_init_acpi_madt
|
||||
.data
|
||||
_AcpiMadtFlags:
|
||||
.long kAcpiMadtPcAtCompat
|
||||
.endobj _AcpiMadtFlags,globl
|
||||
.previous
|
||||
.bss
|
||||
_AcpiNumIoApics:
|
||||
.skip 8
|
||||
.endobj _AcpiNumIoApics,globl
|
||||
_AcpiIoApics:
|
||||
.skip 8
|
||||
.endobj _AcpiIoApics,globl
|
||||
.previous
|
||||
@@ -0,0 +1,81 @@
|
||||
/*-*- 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│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ This is free and unencumbered software released into the public domain. │
|
||||
│ │
|
||||
│ Anyone is free to copy, modify, publish, use, compile, sell, or │
|
||||
│ distribute this software, either in source code form or as a compiled │
|
||||
│ binary, for any purpose, commercial or non-commercial, and by any │
|
||||
│ means. │
|
||||
│ │
|
||||
│ In jurisdictions that recognize copyright laws, the author or authors │
|
||||
│ of this software dedicate any and all copyright interest in the │
|
||||
│ software to the public domain. We make this dedication for the benefit │
|
||||
│ of the public at large and to the detriment of our heirs and │
|
||||
│ successors. We intend this dedication to be an overt act of │
|
||||
│ relinquishment in perpetuity of all present and future rights to this │
|
||||
│ software under copyright law. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │
|
||||
│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │
|
||||
│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │
|
||||
│ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR │
|
||||
│ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │
|
||||
│ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR │
|
||||
│ OTHER DEALINGS IN THE SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/dce.h"
|
||||
#include "libc/inttypes.h"
|
||||
#include "libc/irq/acpi.internal.h"
|
||||
|
||||
#ifdef __x86_64__
|
||||
|
||||
textstartup void _AcpiMadtInit(void) {
|
||||
if (IsMetal()) {
|
||||
const AcpiTableMadt *madt;
|
||||
size_t length, num_io_apics = 0;
|
||||
const char *madt_end, *p;
|
||||
const AcpiSubtableHeader *h;
|
||||
const AcpiMadtIoApic **icp;
|
||||
uint32_t flags;
|
||||
if (!_AcpiSuccess(_AcpiGetTable("APIC", 0, (void **)&madt))) {
|
||||
KINFOF("no MADT found");
|
||||
return;
|
||||
}
|
||||
length = madt->Header.Length;
|
||||
if (length < sizeof(*madt)) {
|
||||
KDIEF("MADT has short length %#zx", length);
|
||||
}
|
||||
_AcpiMadtFlags = flags = madt->Flags;
|
||||
KINFOF("MADT @ %p, flags %#" PRIx32, madt, flags);
|
||||
madt_end = (char *)madt + length;
|
||||
p = madt->Subtable;
|
||||
while (p != madt_end) {
|
||||
h = (const AcpiSubtableHeader *)p;
|
||||
switch (h->Type) {
|
||||
case kAcpiMadtIoApic:
|
||||
++num_io_apics;
|
||||
}
|
||||
p += h->Length;
|
||||
}
|
||||
KINFOF("MADT: %zu I/O APIC(s)", num_io_apics);
|
||||
icp = _AcpiOsAllocate(num_io_apics * sizeof(const AcpiMadtIoApic *));
|
||||
if (icp) {
|
||||
_AcpiIoApics = icp;
|
||||
p = madt->Subtable;
|
||||
while (p != madt_end) {
|
||||
h = (const AcpiSubtableHeader *)p;
|
||||
switch (h->Type) {
|
||||
case kAcpiMadtIoApic:
|
||||
*icp++ = (const AcpiMadtIoApic *)p;
|
||||
}
|
||||
p += h->Length;
|
||||
}
|
||||
_AcpiNumIoApics = num_io_apics;
|
||||
} else {
|
||||
KWARNF("MADT: no memory for I/O APICs");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* __x86_64__ */
|
||||
@@ -0,0 +1,46 @@
|
||||
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
|
||||
│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ This is free and unencumbered software released into the public domain. │
|
||||
│ │
|
||||
│ Anyone is free to copy, modify, publish, use, compile, sell, or │
|
||||
│ distribute this software, either in source code form or as a compiled │
|
||||
│ binary, for any purpose, commercial or non-commercial, and by any │
|
||||
│ means. │
|
||||
│ │
|
||||
│ In jurisdictions that recognize copyright laws, the author or authors │
|
||||
│ of this software dedicate any and all copyright interest in the │
|
||||
│ software to the public domain. We make this dedication for the benefit │
|
||||
│ of the public at large and to the detriment of our heirs and │
|
||||
│ successors. We intend this dedication to be an overt act of │
|
||||
│ relinquishment in perpetuity of all present and future rights to this │
|
||||
│ software under copyright law. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │
|
||||
│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │
|
||||
│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │
|
||||
│ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR │
|
||||
│ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │
|
||||
│ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR │
|
||||
│ OTHER DEALINGS IN THE SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/dce.h"
|
||||
#include "libc/irq/acpi.internal.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/runtime/pc.internal.h"
|
||||
|
||||
.init.start 310,_init_acpi_xsdt
|
||||
push %rdi
|
||||
push %rsi
|
||||
call _AcpiXsdtInit
|
||||
pop %rsi
|
||||
pop %rdi
|
||||
.init.end 310,_init_acpi_xsdt
|
||||
.bss
|
||||
_AcpiXsdtNumEntries:
|
||||
.skip 8
|
||||
.endobj _AcpiXsdtNumEntries,globl
|
||||
_AcpiXsdtEntries:
|
||||
.skip 8
|
||||
.endobj _AcpiXsdtEntries,globl
|
||||
.previous
|
||||
@@ -0,0 +1,243 @@
|
||||
/*-*- 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│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ This is free and unencumbered software released into the public domain. │
|
||||
│ │
|
||||
│ Anyone is free to copy, modify, publish, use, compile, sell, or │
|
||||
│ distribute this software, either in source code form or as a compiled │
|
||||
│ binary, for any purpose, commercial or non-commercial, and by any │
|
||||
│ means. │
|
||||
│ │
|
||||
│ In jurisdictions that recognize copyright laws, the author or authors │
|
||||
│ of this software dedicate any and all copyright interest in the │
|
||||
│ software to the public domain. We make this dedication for the benefit │
|
||||
│ of the public at large and to the detriment of our heirs and │
|
||||
│ successors. We intend this dedication to be an overt act of │
|
||||
│ relinquishment in perpetuity of all present and future rights to this │
|
||||
│ software under copyright law. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │
|
||||
│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │
|
||||
│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │
|
||||
│ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR │
|
||||
│ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │
|
||||
│ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR │
|
||||
│ OTHER DEALINGS IN THE SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/intrin/bits.h"
|
||||
#include "libc/intrin/directmap.internal.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/irq/acpi.internal.h"
|
||||
#include "libc/log/color.internal.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/nt/efi.h"
|
||||
#include "libc/runtime/pc.internal.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/map.h"
|
||||
#include "libc/sysv/consts/prot.h"
|
||||
|
||||
#ifdef __x86_64__
|
||||
|
||||
textstartup static void *_AcpiOsMapRoMemory(uintptr_t phy, size_t n) {
|
||||
__invert_and_perm_ref_memory_area(__get_mm(), __get_pml4t(), phy, n,
|
||||
PAGE_XD);
|
||||
return (void *)(BANE + phy);
|
||||
}
|
||||
|
||||
textstartup static void *_AcpiOsMapMemory(uintptr_t phy, size_t n) {
|
||||
__invert_and_perm_ref_memory_area(__get_mm(), __get_pml4t(), phy, n,
|
||||
PAGE_XD | PAGE_RW);
|
||||
return (void *)(BANE + phy);
|
||||
}
|
||||
|
||||
textstartup void *_AcpiOsMapUncachedMemory(uintptr_t phy, size_t n) {
|
||||
__invert_and_perm_ref_memory_area(__get_mm(), __get_pml4t(), phy, n,
|
||||
PAGE_XD | PAGE_PCD | PAGE_RW);
|
||||
return (void *)(BANE + phy);
|
||||
}
|
||||
|
||||
textstartup static void *_AcpiOsAllocatePages(size_t n) {
|
||||
struct DirectMap dm = sys_mmap_metal(NULL, n, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
void *addr = dm.addr;
|
||||
if (addr == (void *)-1) addr = NULL;
|
||||
return addr;
|
||||
}
|
||||
|
||||
textstartup void *_AcpiOsAllocate(size_t n) {
|
||||
static _Atomic(char *) slack = NULL;
|
||||
char *addr = NULL;
|
||||
size_t align = __BIGGEST_ALIGNMENT__, use;
|
||||
if (n >= 4096) return _AcpiOsAllocatePages(n);
|
||||
n = ROUNDUP(n, align);
|
||||
for (;;) {
|
||||
addr = atomic_exchange(&slack, NULL);
|
||||
if (!addr) {
|
||||
addr = _AcpiOsAllocatePages(4096);
|
||||
if (!addr) return NULL;
|
||||
}
|
||||
use = (uintptr_t)addr % 4096 + n;
|
||||
if (use <= 4096) {
|
||||
if (use < 4096) atomic_store(&slack, addr + n);
|
||||
return addr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
textstartup static uint8_t _AcpiTbChecksum(const uint8_t *p, size_t n) {
|
||||
uint8_t c = 0;
|
||||
while (n-- != 0) c += *p++;
|
||||
return c;
|
||||
}
|
||||
|
||||
textstartup static AcpiStatus _AcpiTbVerifyChecksum(const uint8_t *p,
|
||||
size_t n) {
|
||||
uint8_t sum = _AcpiTbChecksum(p, n);
|
||||
if (!sum) return kAcpiOk;
|
||||
KWARNF("bad ACPI table cksum %#x != 0 @ %p,+%#zx", (unsigned)sum, p, n);
|
||||
return kAcpiExBadChecksum;
|
||||
}
|
||||
|
||||
textstartup static AcpiStatus _AcpiRsdpVerifyChecksums(const uint8_t *p) {
|
||||
const AcpiTableRsdp *q = (const AcpiTableRsdp *)p;
|
||||
size_t length = offsetof(AcpiTableRsdp, Length);
|
||||
AcpiStatus sta = _AcpiTbVerifyChecksum(p, length);
|
||||
if (!_AcpiSuccess(sta)) return sta;
|
||||
if (q->Revision <= 1) return kAcpiOk;
|
||||
length = q->Length;
|
||||
if (length < offsetof(AcpiTableRsdp, Reserved)) {
|
||||
KWARNF("malformed ACPI 2+ RSDP, length %#zx < %#zx",
|
||||
length, offsetof(AcpiTableRsdp, Reserved));
|
||||
if (length < offsetof(AcpiTableRsdp, RsdtPhysicalAddress)
|
||||
+ sizeof(q->RsdtPhysicalAddress)) {
|
||||
return kAcpiExBadHeader;
|
||||
}
|
||||
return kAcpiOk;
|
||||
}
|
||||
return _AcpiTbVerifyChecksum(p, length);
|
||||
}
|
||||
|
||||
textstartup static bool _AcpiTbIsValidRsdp(const uint8_t *p) {
|
||||
const AcpiTableRsdp *q = (const AcpiTableRsdp *)p;
|
||||
if (READ64LE(q->Signature) != READ64LE("RSD PTR ")) return false;
|
||||
KINFOF("\"RSD PTR \" @ %p, ACPI rev %u", q, (unsigned)q->Revision);
|
||||
if (!_AcpiSuccess(_AcpiRsdpVerifyChecksums(p))) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
textstartup static const AcpiTableRsdp *_AcpiFindRsdp(void) {
|
||||
const size_t PARA_SZ = 0x10;
|
||||
size_t off;
|
||||
struct mman *mm = __get_mm();
|
||||
uint64_t rsdp_phy = mm->pc_acpi_rsdp;
|
||||
uint16_t ebda_para;
|
||||
const uint8_t *area;
|
||||
if (rsdp_phy) return _AcpiOsMapRoMemory(rsdp_phy, sizeof(AcpiTableRsdp));
|
||||
/*
|
||||
* "OSPM finds the Root System Description Pointer (RSDP) structure by
|
||||
* searching physical memory ranges on 16-byte boundaries for a valid
|
||||
* Root System Description Pointer structure signature and checksum match
|
||||
* as follows:
|
||||
* * The first 1 KB of the Extended BIOS Data Area (EBDA). For EISA or
|
||||
* MCA systems, the EBDA can be found in the two-byte location 40:0Eh
|
||||
* on the BIOS data area.
|
||||
* * The BIOS read-only memory space between 0E0000h and 0FFFFFh."
|
||||
* — Advanced Configuration and Power Interface (ACPI) Specification,
|
||||
* Release 6.5, §5.2.5.1
|
||||
*/
|
||||
ebda_para = *(const uint16_t *)(BANE + PC_BIOS_DATA_AREA + 0xE);
|
||||
if (ebda_para) {
|
||||
area = _AcpiOsMapMemory((uint32_t)ebda_para * PARA_SZ, 1024);
|
||||
KINFOF("search EBDA @ %p for RSDP", area);
|
||||
off = 1024;
|
||||
while (off != 0) {
|
||||
off -= PARA_SZ;
|
||||
if (_AcpiTbIsValidRsdp(area + off)) {
|
||||
return (const AcpiTableRsdp *)(area + off);
|
||||
}
|
||||
}
|
||||
}
|
||||
area = _AcpiOsMapMemory(0xE0000, 0xFFFFF + 1 - 0xE0000);
|
||||
KINFOF("search ROM BIOS for RSDP");
|
||||
off = ROUNDUP(0xFFFFF + 1 - 0xE0000, PARA_SZ);
|
||||
while (off != 0) {
|
||||
off -= PARA_SZ;
|
||||
if (_AcpiTbIsValidRsdp(area + off)) {
|
||||
return (const AcpiTableRsdp *)(area + off);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
textstartup void *_AcpiMapTable(uintptr_t phy) {
|
||||
void *p = _AcpiOsMapRoMemory(phy, sizeof(AcpiTableHeader));
|
||||
AcpiTableHeader *hdr = p;
|
||||
size_t length = hdr->Length;
|
||||
if (length < sizeof(*hdr)) {
|
||||
KDIEF("ACPI table @ %p has short length %#zx", p, length);
|
||||
} else {
|
||||
p = _AcpiOsMapRoMemory(phy, length);
|
||||
}
|
||||
_AcpiTbVerifyChecksum((const uint8_t *)p, length);
|
||||
return p;
|
||||
}
|
||||
|
||||
textstartup void _AcpiXsdtInit(void) {
|
||||
if (IsMetal()) {
|
||||
size_t nents, i;
|
||||
void **ents = NULL;
|
||||
const AcpiTableRsdp *rsdp = _AcpiFindRsdp();
|
||||
if (!rsdp) {
|
||||
KINFOF("no RSDP found");
|
||||
return;
|
||||
}
|
||||
KINFOF("RSDP @ %p", rsdp);
|
||||
if (rsdp->Revision <= 1 ||
|
||||
rsdp->Length < offsetof(AcpiTableRsdp, Reserved) ||
|
||||
!rsdp->XsdtPhysicalAddress) {
|
||||
const AcpiTableRsdt *rsdt = _AcpiMapTable(rsdp->RsdtPhysicalAddress);
|
||||
nents = (rsdt->Header.Length - sizeof(rsdt->Header)) / sizeof(uint32_t);
|
||||
KINFOF("RSDT @ %p, %#zx entries", rsdt, nents);
|
||||
ents = _AcpiOsAllocate(nents * sizeof(AcpiTableHeader *));
|
||||
if (ents) {
|
||||
for (i = 0; i < nents; ++i) {
|
||||
ents[i] = _AcpiMapTable(rsdt->TableOffsetEntry[i]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const AcpiTableXsdt *xsdt = _AcpiMapTable(rsdp->XsdtPhysicalAddress);
|
||||
nents = (xsdt->Header.Length - sizeof(xsdt->Header)) / sizeof(uint64_t);
|
||||
KINFOF("XSDT @ %p, %#zx entries", xsdt, nents);
|
||||
ents = _AcpiOsAllocate(nents * sizeof(AcpiTableHeader *));
|
||||
if (ents) {
|
||||
for (i = 0; i < nents; ++i) {
|
||||
ents[i] = _AcpiMapTable(xsdt->TableOffsetEntry[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!ents) {
|
||||
KWARNF("no memory for mapped RSDT/XSDT entries");
|
||||
nents = 0;
|
||||
}
|
||||
_AcpiXsdtEntries = ents;
|
||||
_AcpiXsdtNumEntries = nents;
|
||||
}
|
||||
}
|
||||
|
||||
textstartup AcpiStatus _AcpiGetTableImpl(uint32_t sig, uint32_t inst,
|
||||
void **phdr) {
|
||||
void **p = _AcpiXsdtEntries;
|
||||
size_t n = _AcpiXsdtNumEntries;
|
||||
while (n-- != 0) {
|
||||
AcpiTableHeader *h = *p++;
|
||||
if (READ32LE(h->Signature) != sig) continue;
|
||||
if (inst-- != 0) continue;
|
||||
*phdr = h;
|
||||
return kAcpiOk;
|
||||
}
|
||||
return kAcpiExNotFound;
|
||||
}
|
||||
|
||||
#endif /* __x86_64__ */
|
||||
@@ -0,0 +1,232 @@
|
||||
#ifndef COSMOPOLITAN_LIBC_IRQ_ACPI_INTERNAL_H_
|
||||
#define COSMOPOLITAN_LIBC_IRQ_ACPI_INTERNAL_H_
|
||||
#include "libc/intrin/bits.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/log/color.internal.h"
|
||||
|
||||
/**
|
||||
* AcpiStatus values.
|
||||
*/
|
||||
#define kAcpiOk 0x0000
|
||||
#define kAcpiExNotFound 0x0005
|
||||
#define kAcpiExBadHeader 0x2002
|
||||
#define kAcpiExBadChecksum 0x2003
|
||||
|
||||
/**
|
||||
* Flags for AcpiTableMadt::Flags.
|
||||
*/
|
||||
#define kAcpiMadtPcAtCompat 0x0001
|
||||
|
||||
/**
|
||||
* Flags for AcpiTableFadt::BootFlags.
|
||||
*/
|
||||
#define kAcpiFadtLegacyDevices 0x0001
|
||||
#define kAcpiFadt8042 0x0002
|
||||
#define kAcpiFadtNoVga 0x0004
|
||||
#define kAcpiFadtNoMsi 0x0008
|
||||
#define kAcpiFadtNoAspm 0x0010
|
||||
#define kAcpiFadtNoCmosRtc 0x0020
|
||||
|
||||
/**
|
||||
* Values for AcpiSubtableHeader::Type under an AcpiTableMadt.
|
||||
*/
|
||||
#define kAcpiMadtLocalApic 0
|
||||
#define kAcpiMadtIoApic 1
|
||||
#define kAcpiMadtIntOverride 2
|
||||
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
|
||||
/**
|
||||
* @fileoverview Declarations for bare metal code to interact with ACPI
|
||||
*
|
||||
* @see UEFI Forum, Inc. Advanced Configuration and Power Interface (ACPI)
|
||||
* Specification, Version 6.5, 2022. https://uefi.org/specifications
|
||||
* @see Intel Corporation. ACPI Component Architecture: User Guide and
|
||||
* Programmer Reference, Revision 6.2, 2017. https://acpica.org
|
||||
*/
|
||||
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* Structure of an ACPI Root System Description Pointer (RSDP) table. This
|
||||
* points to either an RSDT, or an XSDT, or both.
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t Signature[8];
|
||||
uint8_t Checksum;
|
||||
uint8_t OemId[6];
|
||||
uint8_t Revision;
|
||||
uint32_t RsdtPhysicalAddress;
|
||||
uint32_t Length;
|
||||
uint64_t XsdtPhysicalAddress;
|
||||
uint8_t ExtendedChecksum;
|
||||
uint8_t Reserved[3];
|
||||
} AcpiTableRsdp;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* Structure of a System Description Table Header, which appears at the
|
||||
* beginning of each ACPI system description table.
|
||||
*/
|
||||
typedef struct thatispacked {
|
||||
uint8_t Signature[4];
|
||||
uint32_t Length;
|
||||
uint8_t Revision;
|
||||
uint8_t Checksum;
|
||||
uint8_t OemId[6];
|
||||
uint8_t OemTableId[8];
|
||||
uint32_t OemRevision;
|
||||
uint8_t AslCompilerId[4];
|
||||
uint32_t AslCompilerRevision;
|
||||
} AcpiTableHeader;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* ACPI Root System Description Table (RSDT) structure.
|
||||
*/
|
||||
typedef struct thatispacked {
|
||||
AcpiTableHeader Header;
|
||||
uint32_t TableOffsetEntry[];
|
||||
} AcpiTableRsdt;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* ACPI Extended System Description Table (XSDT) structure.
|
||||
*/
|
||||
typedef struct thatispacked {
|
||||
AcpiTableHeader Header;
|
||||
uint64_t TableOffsetEntry[];
|
||||
} AcpiTableXsdt;
|
||||
|
||||
typedef struct thatispacked {
|
||||
uint8_t SpaceId;
|
||||
uint8_t BitWidth;
|
||||
uint8_t BitOffset;
|
||||
uint8_t AccessWidth;
|
||||
uint64_t Address;
|
||||
} AcpiGenericAddress;
|
||||
|
||||
typedef struct thatispacked {
|
||||
uint8_t Type;
|
||||
uint8_t Length;
|
||||
} AcpiSubtableHeader;
|
||||
|
||||
typedef struct thatispacked {
|
||||
AcpiSubtableHeader Header;
|
||||
uint8_t Id;
|
||||
uint8_t Reserved;
|
||||
uint32_t Address;
|
||||
uint32_t GlobalIrqBase;
|
||||
} AcpiMadtIoApic;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* ACPI Multiple APIC Description Table (MADT) structure.
|
||||
*/
|
||||
typedef struct thatispacked {
|
||||
AcpiTableHeader Header;
|
||||
uint32_t Address; /* local APIC address */
|
||||
uint32_t Flags; /* multiple APIC flags */
|
||||
char Subtable[]; /* ...interrupt controller structures... */
|
||||
} AcpiTableMadt;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* Fixed ACPI Description Table (FADT) structure.
|
||||
*/
|
||||
typedef struct thatispacked {
|
||||
AcpiTableHeader Header;
|
||||
uint32_t Facs;
|
||||
uint32_t Dsdt;
|
||||
uint8_t Model;
|
||||
uint8_t PreferredProfile;
|
||||
uint16_t SciInterrupt;
|
||||
uint32_t SmiCommand;
|
||||
uint8_t AcpiEnable;
|
||||
uint8_t AcpiDisable;
|
||||
uint8_t S4BiosRequest;
|
||||
uint8_t PstateControl;
|
||||
uint32_t Pm1aEventBlock;
|
||||
uint32_t Pm1bEventBlock;
|
||||
uint32_t Pm1aControlBlock;
|
||||
uint32_t Pm1bControlBlock;
|
||||
uint32_t Pm2ControlBlock;
|
||||
uint32_t PmTimerBlock;
|
||||
uint32_t Gpe0Block;
|
||||
uint32_t Gpe1Block;
|
||||
uint8_t Pm1EventLength;
|
||||
uint8_t Pm1ControlLength;
|
||||
uint8_t Pm2ControlLength;
|
||||
uint8_t PmTimerLength;
|
||||
uint8_t Gpe0BlockLength;
|
||||
uint8_t Gpe1BlockLength;
|
||||
uint8_t Gpe1Base;
|
||||
uint8_t CstControl;
|
||||
uint16_t C2Latency;
|
||||
uint16_t C3Latency;
|
||||
uint16_t FlushSize;
|
||||
uint16_t FlushStride;
|
||||
uint8_t DutyOffset;
|
||||
uint8_t DutyWidth;
|
||||
uint8_t DayAlarm;
|
||||
uint8_t MonthAlarm;
|
||||
uint8_t Century;
|
||||
uint16_t BootFlags;
|
||||
uint8_t Reserved;
|
||||
uint32_t Flags;
|
||||
AcpiGenericAddress ResetRegister;
|
||||
uint8_t ResetValue;
|
||||
uint16_t ArmBootFlags;
|
||||
uint8_t MinorRevision;
|
||||
uint64_t XFacs;
|
||||
uint64_t XDsdt;
|
||||
AcpiGenericAddress XPm1aEventBlock;
|
||||
AcpiGenericAddress XPm1bEventBlock;
|
||||
AcpiGenericAddress XPm1aControlBlock;
|
||||
AcpiGenericAddress XPm1bControlBlock;
|
||||
AcpiGenericAddress XPm2ControlBlock;
|
||||
AcpiGenericAddress XPmTimerBlock;
|
||||
AcpiGenericAddress XGpe0Block;
|
||||
AcpiGenericAddress XGpe1Block;
|
||||
AcpiGenericAddress SleepControl;
|
||||
AcpiGenericAddress SleepStatus;
|
||||
uint64_t HypervisorId;
|
||||
} AcpiTableFadt;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* ACPI Differentiated System Description Table (DSDT) structure.
|
||||
*/
|
||||
typedef struct thatispacked {
|
||||
AcpiTableHeader Header;
|
||||
uint8_t Aml[];
|
||||
} AcpiTableDsdt;
|
||||
|
||||
typedef uint32_t AcpiStatus;
|
||||
|
||||
extern size_t _AcpiXsdtNumEntries, _AcpiNumIoApics;
|
||||
extern void **_AcpiXsdtEntries;
|
||||
extern uint16_t _AcpiBootFlags;
|
||||
extern uint32_t _AcpiMadtFlags;
|
||||
extern const AcpiMadtIoApic **_AcpiIoApics;
|
||||
|
||||
extern void *_AcpiOsMapUncachedMemory(uintptr_t, size_t);
|
||||
extern void *_AcpiOsAllocate(size_t);
|
||||
extern void *_AcpiMapTable(uintptr_t);
|
||||
|
||||
extern AcpiStatus _AcpiGetTableImpl(uint32_t, uint32_t, void **);
|
||||
|
||||
forceinline bool _AcpiSuccess(AcpiStatus __sta) {
|
||||
return __sta == kAcpiOk;
|
||||
}
|
||||
|
||||
forceinline AcpiStatus _AcpiGetTable(const char __sig[4], uint32_t __inst,
|
||||
void **__phdr) {
|
||||
uint8_t __sig_copy[4] = { __sig[0], __sig[1], __sig[2], __sig[3] };
|
||||
return _AcpiGetTableImpl(READ32LE(__sig_copy), __inst, __phdr);
|
||||
}
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_LIBC_IRQ_ACPI_INTERNAL_H_ */
|
||||
@@ -0,0 +1,57 @@
|
||||
#-*-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_IRQ
|
||||
|
||||
LIBC_IRQ_ARTIFACTS += LIBC_IRQ_A
|
||||
LIBC_IRQ_A = o/$(MODE)/libc/irq/irq.a
|
||||
LIBC_IRQ_A_FILES := $(wildcard libc/irq/*)
|
||||
LIBC_IRQ_A_HDRS = $(filter %.h,$(LIBC_IRQ_A_FILES))
|
||||
LIBC_IRQ_A_INCS = $(filter %.inc,$(LIBC_IRQ_A_FILES))
|
||||
LIBC_IRQ_A_SRCS_S = $(filter %.S,$(LIBC_IRQ_A_FILES))
|
||||
LIBC_IRQ_A_SRCS_C = $(filter %.c,$(LIBC_IRQ_A_FILES))
|
||||
|
||||
LIBC_IRQ = \
|
||||
$(LIBC_IRQ_A_DEPS) \
|
||||
$(LIBC_IRQ_A)
|
||||
|
||||
LIBC_IRQ_A_SRCS = \
|
||||
$(LIBC_IRQ_A_SRCS_S) \
|
||||
$(LIBC_IRQ_A_SRCS_C)
|
||||
|
||||
LIBC_IRQ_A_OBJS = \
|
||||
$(LIBC_IRQ_A_SRCS_S:%.S=o/$(MODE)/%.o) \
|
||||
$(LIBC_IRQ_A_SRCS_C:%.c=o/$(MODE)/%.o)
|
||||
|
||||
LIBC_IRQ_A_CHECKS = \
|
||||
$(LIBC_IRQ_A).pkg \
|
||||
$(LIBC_IRQ_A_HDRS:%=o/$(MODE)/%.ok)
|
||||
|
||||
LIBC_IRQ_A_DIRECTDEPS = \
|
||||
LIBC_INTRIN \
|
||||
LIBC_STR \
|
||||
LIBC_SYSV
|
||||
|
||||
LIBC_IRQ_A_DEPS := \
|
||||
$(call uniq,$(foreach x,$(LIBC_IRQ_A_DIRECTDEPS),$($(x))))
|
||||
|
||||
$(LIBC_IRQ_A):libc/irq/ \
|
||||
$(LIBC_IRQ_A).pkg \
|
||||
$(LIBC_IRQ_A_OBJS)
|
||||
|
||||
$(LIBC_IRQ_A).pkg: \
|
||||
$(LIBC_IRQ_A_OBJS) \
|
||||
$(foreach x,$(LIBC_IRQ_A_DIRECTDEPS),$($(x)_A).pkg)
|
||||
|
||||
LIBC_IRQ_LIBS = $(foreach x,$(LIBC_IRQ_ARTIFACTS),$($(x)))
|
||||
LIBC_IRQ_SRCS = $(foreach x,$(LIBC_IRQ_ARTIFACTS),$($(x)_SRCS))
|
||||
LIBC_IRQ_HDRS = $(foreach x,$(LIBC_IRQ_ARTIFACTS),$($(x)_HDRS))
|
||||
LIBC_IRQ_INCS = $(foreach x,$(LIBC_IRQ_ARTIFACTS),$($(x)_INCS))
|
||||
LIBC_IRQ_BINS = $(foreach x,$(LIBC_IRQ_ARTIFACTS),$($(x)_BINS))
|
||||
LIBC_IRQ_CHECKS = $(foreach x,$(LIBC_IRQ_ARTIFACTS),$($(x)_CHECKS))
|
||||
LIBC_IRQ_OBJS = $(foreach x,$(LIBC_IRQ_ARTIFACTS),$($(x)_OBJS))
|
||||
LIBC_IRQ_TESTS = $(foreach x,$(LIBC_IRQ_ARTIFACTS),$($(x)_TESTS))
|
||||
$(LIBC_IRQ_OBJS): $(BUILD_FILES) libc/irq/irq.mk
|
||||
|
||||
.PHONY: o/$(MODE)/libc/irq
|
||||
o/$(MODE)/libc/irq: $(LIBC_IRQ_CHECKS)
|
||||
@@ -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 */
|
||||
@@ -0,0 +1,4 @@
|
||||
#ifndef COSMOPOLITAN_LIBC_ISYSTEM_MUTEX_
|
||||
#define COSMOPOLITAN_LIBC_ISYSTEM_MUTEX_
|
||||
#include "third_party/libcxx/shared_mutex"
|
||||
#endif /* COSMOPOLITAN_LIBC_ISYSTEM_MUTEX_ */
|
||||
@@ -16,10 +16,12 @@ 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 \
|
||||
o/$(MODE)/libc/intrin \
|
||||
o/$(MODE)/libc/irq \
|
||||
o/$(MODE)/libc/log \
|
||||
o/$(MODE)/libc/mem \
|
||||
o/$(MODE)/libc/nexgen32e \
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "libc/intrin/asan.internal.h"
|
||||
#include "libc/intrin/bits.h"
|
||||
#include "libc/intrin/cmpxchg.h"
|
||||
#include "libc/intrin/cxaatexit.internal.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/mem/mem.h"
|
||||
@@ -94,6 +95,7 @@ dontasan void CheckForMemoryLeaks(void) {
|
||||
exit(0);
|
||||
}
|
||||
_pthread_unwind(_pthread_self());
|
||||
__cxa_thread_finalize();
|
||||
_pthread_unkey(__get_tls());
|
||||
_pthread_ungarbage();
|
||||
__cxa_finalize(0);
|
||||
|
||||
+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;
|
||||
|
||||
@@ -103,6 +103,18 @@
|
||||
0x96, 0xFB, 0x7A, 0xDE, 0xD0, 0x80, 0x51, 0x6A \
|
||||
} \
|
||||
}
|
||||
#define ACPI_20_TABLE_GUID \
|
||||
{ \
|
||||
0x8868E871, 0xE4F1, 0x11D3, { \
|
||||
0xBC, 0x22, 0x00, 0x80, 0xC7, 0x3C, 0x88, 0x81 \
|
||||
} \
|
||||
}
|
||||
#define ACPI_10_TABLE_GUID \
|
||||
{ \
|
||||
0xEB9D2D30, 0x2D88, 0x11D3, { \
|
||||
0x9A, 0x16, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0x4D \
|
||||
} \
|
||||
}
|
||||
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
+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;
|
||||
|
||||
@@ -43,6 +43,8 @@ struct EfiArgs {
|
||||
|
||||
static EFI_GUID kEfiLoadedImageProtocol = LOADED_IMAGE_PROTOCOL;
|
||||
static EFI_GUID kEfiGraphicsOutputProtocol = GRAPHICS_OUTPUT_PROTOCOL;
|
||||
static const EFI_GUID kEfiAcpi20TableGuid = ACPI_20_TABLE_GUID;
|
||||
static const EFI_GUID kEfiAcpi10TableGuid = ACPI_10_TABLE_GUID;
|
||||
|
||||
extern const char vga_console[];
|
||||
extern void _EfiPostboot(struct mman *, uint64_t *, uintptr_t, char **);
|
||||
@@ -117,6 +119,25 @@ static void EfiInitVga(struct mman *mm, EFI_SYSTEM_TABLE *SystemTable) {
|
||||
GraphMode->FrameBufferSize, 0);
|
||||
}
|
||||
|
||||
static void EfiInitAcpi(struct mman *mm, EFI_SYSTEM_TABLE *SystemTable) {
|
||||
void *rsdp1 = NULL, *rsdp2 = NULL;
|
||||
uintptr_t n = SystemTable->NumberOfTableEntries, i;
|
||||
EFI_CONFIGURATION_TABLE *tab;
|
||||
for (i = 0, tab = SystemTable->ConfigurationTable; i < n; ++i, ++tab) {
|
||||
EFI_GUID *guid = &tab->VendorGuid;
|
||||
if (memcmp(guid, &kEfiAcpi20TableGuid, sizeof(EFI_GUID)) == 0) {
|
||||
rsdp2 = tab->VendorTable;
|
||||
} else if (memcmp(guid, &kEfiAcpi20TableGuid, sizeof(EFI_GUID)) == 0) {
|
||||
rsdp1 = tab->VendorTable;
|
||||
}
|
||||
}
|
||||
if (rsdp2) {
|
||||
mm->pc_acpi_rsdp = (uintptr_t)rsdp2;
|
||||
} else {
|
||||
mm->pc_acpi_rsdp = (uintptr_t)rsdp1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* EFI Application Entrypoint.
|
||||
*
|
||||
@@ -203,6 +224,11 @@ __msabi EFI_STATUS EfiMain(EFI_HANDLE ImageHandle,
|
||||
*/
|
||||
if (_weaken(vga_console)) EfiInitVga(mm, SystemTable);
|
||||
|
||||
/*
|
||||
* Gets a pointer to the ACPI RSDP.
|
||||
*/
|
||||
EfiInitAcpi(mm, SystemTable);
|
||||
|
||||
/*
|
||||
* Asks UEFI which parts of our RAM we're allowed to use.
|
||||
*/
|
||||
|
||||
@@ -53,9 +53,9 @@ int login_tty(int fd) {
|
||||
errno = e;
|
||||
// take control of teletypewriter (requires being leader)
|
||||
if ((rc = sys_ioctl(fd, TIOCSCTTY, 0)) != -1) {
|
||||
unassert(!sys_dup2(fd, 0, 0));
|
||||
unassert(!sys_dup2(fd, 1, 0));
|
||||
unassert(!sys_dup2(fd, 2, 0));
|
||||
unassert(sys_dup2(fd, 0, 0) == 0);
|
||||
unassert(sys_dup2(fd, 1, 0) == 1);
|
||||
unassert(sys_dup2(fd, 2, 0) == 2);
|
||||
if (fd > 2) {
|
||||
unassert(!sys_close(fd));
|
||||
}
|
||||
|
||||
@@ -39,9 +39,12 @@ struct mman {
|
||||
struct { /* 0x1d48 — starting cursor pos. */
|
||||
unsigned short y, x;
|
||||
} pc_video_curs_info;
|
||||
unsigned short pc_video_char_height; /* 0x1d4c — character height (useful
|
||||
for setting cursor shape
|
||||
in text mode) */
|
||||
unsigned short pc_video_char_height; /* 0x1d4c — character height (useful
|
||||
for setting cursor shape
|
||||
in text mode) */
|
||||
uint64_t pc_acpi_rsdp; /* 0x1d50 — pointer to ACPI RSDP;
|
||||
NULL means to search for
|
||||
it in legacy BIOS areas */
|
||||
};
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
|
||||
@@ -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;
|
||||
|
||||
+9
-24
@@ -20,22 +20,9 @@
|
||||
#include "libc/errno.h"
|
||||
#include "libc/runtime/utmpx.h"
|
||||
|
||||
void setutent(void) {
|
||||
}
|
||||
|
||||
void endutent(void) {
|
||||
}
|
||||
|
||||
void endutxent(void) {
|
||||
}
|
||||
|
||||
struct utmp *getutent(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void updwtmp(const char *x, const struct utmp *y) {
|
||||
}
|
||||
|
||||
void updwtmpx(const char *x, const struct utmpx *y) {
|
||||
}
|
||||
|
||||
@@ -46,10 +33,6 @@ struct utmpx *pututxline(const struct utmpx *p) {
|
||||
void setutxent(void) {
|
||||
}
|
||||
|
||||
struct utmp *getutid(const struct utmp *x) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct utmpx *getutxent(void) {
|
||||
return 0;
|
||||
}
|
||||
@@ -67,10 +50,12 @@ int __utmpxname() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int utmpname(const char *x) {
|
||||
return __utmpxname();
|
||||
}
|
||||
|
||||
int utmpxname(const char *x) {
|
||||
return __utmpxname();
|
||||
}
|
||||
__weak_reference(endutxent, endutent);
|
||||
__weak_reference(setutxent, setutent);
|
||||
__weak_reference(getutxent, getutent);
|
||||
__weak_reference(getutxid, getutid);
|
||||
__weak_reference(getutxline, getutline);
|
||||
__weak_reference(pututxline, pututline);
|
||||
__weak_reference(updwtmpx, updwtmp);
|
||||
__weak_reference(__utmpxname, utmpname);
|
||||
__weak_reference(__utmpxname, utmpxname);
|
||||
Loaded 100 of 168 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user