Compare commits

...
19 Commits
Author SHA1 Message Date
Justine Tunney 23d812f116 Release Cosmopolitan v3.0.2
It's recommended that all users, especially Apple Arm, re-run:

    ape/apeinstall.sh

Within the Cosmopolitan monorepo to refresh your APE loader.
2023-11-05 15:54:47 -08:00
Justine Tunney c39eb244d4 Disable an mprotect() test for now due to SIP 2023-11-05 15:38:16 -08:00
Justine Tunney f63c4d4f52 Use /usr/local/bin/ape on Apple Silicon 2023-11-05 14:52:27 -08:00
Paul Kulchenko b893479417 Add database and statement readonly checks to sqlite in redbean (#914) 2023-11-05 13:29:09 -08:00
Justine Tunney d02cba2451 Mint APE Loader v1.9 2023-11-05 13:11:34 -08:00
Justine Tunney 4cf987ddb1 Fix usleep() definition 2023-11-05 13:06:15 -08:00
Justine Tunney 20c794a353 Add strace to aarch64 vfork() 2023-11-05 13:06:15 -08:00
Gregory Haerr 4f100d2aad Fix stack alignment and auxv for macho APE loader (#931) 2023-11-05 13:06:10 -08:00
Justine Tunney a12ad17291 Get APE Loader working on MacOS with SIP enabled 2023-11-05 01:36:47 -08:00
Justine Tunney 653c7b2f87 Fatten build/bootstrap/echo.com 2023-11-04 21:01:43 -07:00
Justine Tunney d7917ea076 Make win32 i/o signals atomic and longjmp() safe 2023-11-04 20:33:29 -07:00
Justine Tunney 585c86e2a4 Support \n in /zip/.args files 2023-11-04 07:25:20 -07:00
Justine Tunney 48e260e653 Introduce NO_ADDRESS constant 2023-11-03 13:56:17 -07:00
Justine Tunney aa106c9356 Fix Lua test suite regression 2023-11-03 07:01:46 -07:00
Justine Tunney 5e8c928f1a Introduce dlopen() support
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.

Here's the list of platforms that are supported so far:

- x86-64 Linux w/ Glibc
- x86-64 Linux w/ Musl Libc
- x86-64 FreeBSD
- x86-64 Windows
- aarch64 Linux w/ Glibc
- aarch64 MacOS

What this means is your Cosmo programs can call foreign functions on
your host operating system. However, it's important to note that any
foreign library you link won't have the ability to call functions in
your Cosmopolitan program. For example it's now technically possible
that Lua can load a module, however that almost certainly won't work
since the Lua module won't have access to Cosmo's Lua API.

Kudos to @jacereda for figuring out how to do this.
2023-11-03 06:37:18 -07:00
Justine Tunney 1eb6484c9c Rewrite getcwd()
This change addresses a bug that was reported in #923 where bash on
Windows behaved strangely. It turned out that our weak linking of
malloc() caused bash's configure script to favor its own getcwd()
function, which is implemented in the most astonishing way, using
opendir() and readdir() to recursively construct the current path.

This change moves getcwd() into LIBC_STDIO so it can strongly link
malloc(). A new __getcwd() function is now introduced, so all the
low-level runtime services can still use the actual system call. It
provides the Linux Kernel API convention across platforms, and is
overall a higher-quality implementation than what we had before.

In the future, we should probably take a closer look into why bash's
getcwd() polyfill wasn't working as intended on Windows, since there
might be a potential opportunity there to improve our readdir() too.
2023-11-02 13:16:42 -07:00
Justine Tunney a46ec61787 Make cosmocc -dumpmachine work correctly 2023-11-02 09:52:25 -07:00
Justine Tunney 024be3b009 Introduce getifaddrs()
This function was invented by the BSDs (it's not in POSIX.1). It
provides a high-level interface into ioctl(SIOCGIFCONF) which is
comparatively clumsy to use. We already made the ioctls portable
across our entire support vector back in 2021, so this interface
is portable too. See o//tool/viz/getifaddrs.com for our demo app
2023-11-02 08:33:03 -07:00
Justine Tunney 6ca5ab4680 Mention cosmocc-0.0.16 in Makefile 2023-11-01 12:08:50 -07:00
84 changed files with 2086 additions and 918 deletions

No files matched your search

+3 -1
View File
@@ -85,7 +85,7 @@ endif
ifeq ($(TOOLCHAIN),) # if TOOLCHAIN isn't defined
ifeq ("$(wildcard o/third_party/gcc/bin/x86_64-linux-cosmo-*)","") # if our gcc isn't unbundled
ifneq ($(UNAME_M)-$(UNAME_S), x86_64-Linux) # if this is not amd64 linux
$(error you need to download https://justine.lol/cosmocc-0.0.14.zip and unzip it inside the cosmo directory)
$(error you need to download https://justine.lol/cosmocc-0.0.18.zip and unzip it inside the cosmo directory)
endif
endif
endif
@@ -176,6 +176,7 @@ include libc/calls/calls.mk #─┐
include libc/irq/irq.mk # ├──SYSTEMS RUNTIME
include third_party/nsync/nsync.mk # │ You can issue system calls
include libc/runtime/runtime.mk # │
include libc/dlopen/dlopen.mk # │
include third_party/double-conversion/dc.mk # │
include libc/crt/crt.mk # │
include third_party/dlmalloc/dlmalloc.mk #─┘
@@ -373,6 +374,7 @@ COSMOPOLITAN_OBJECTS = \
THIRD_PARTY_NSYNC_MEM \
LIBC_MEM \
THIRD_PARTY_DLMALLOC \
LIBC_DLOPEN \
LIBC_RUNTIME \
THIRD_PARTY_NSYNC \
LIBC_ELF \
+2 -2
View File
@@ -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
View File
@@ -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
+1 -1
View File
@@ -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)
+5
View File
@@ -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) {
+2
View File
@@ -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
View File
@@ -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
+4
View File
@@ -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 \
+2
View File
@@ -295,6 +295,8 @@ for x; do
exit 0
elif [ x"$x" = x"-Wl,--version" ]; then
GOT_SOME=1
elif [ x"$x" = x"-dumpmachine" ]; then
GOT_SOME=1
fi
set -- "$@" "$x"
done
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+55
View File
@@ -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);
}
+2
View File
@@ -43,6 +43,7 @@ EXAMPLES_DIRECTDEPS = \
DSP_SCALE \
DSP_TTY \
LIBC_CALLS \
LIBC_DLOPEN \
LIBC_DNS \
LIBC_FMT \
LIBC_INTRIN \
@@ -92,6 +93,7 @@ EXAMPLES_DIRECTDEPS = \
THIRD_PARTY_VQSORT \
THIRD_PARTY_XED \
THIRD_PARTY_ZLIB \
TOOL_ARGS \
TOOL_BUILD_LIB \
TOOL_VIZ_LIB
+2 -1
View File
@@ -168,7 +168,7 @@ int truncate(const char *, int64_t);
int ttyname_r(int, char *, size_t);
int unlink(const char *);
int unlinkat(int, const char *, int);
int usleep(unsigned);
int usleep(uint64_t);
int vfork(void) returnstwice;
int wait(int *);
int waitpid(int, int *, int);
@@ -219,6 +219,7 @@ bool32 isexecutable(const char *);
bool32 isregularfile(const char *);
bool32 issymlink(const char *);
char *commandv(const char *, char *, size_t);
int __getcwd(char *, size_t);
int clone(void *, void *, size_t, int, void *, void *, void *, void *);
int fadvise(int, uint64_t, uint64_t, int);
int makedirs(const char *, unsigned);
-2
View File
@@ -99,8 +99,6 @@ o//libc/calls/prctl.o: \
# we always want -Os because:
# it's early runtime mandatory and quite huge without it
o//libc/calls/getcwd.greg.o \
o//libc/calls/getcwd-nt.greg.o \
o//libc/calls/getcwd-xnu.greg.o \
o//libc/calls/statfs2cosmo.o: private \
CFLAGS += \
-Os
@@ -17,10 +17,8 @@
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/internal.h"
#include "libc/calls/sig.internal.h"
#include "libc/intrin/atomic.h"
#include "libc/intrin/weaken.h"
#include "libc/sysv/errfuns.h"
#include "libc/thread/posixthread.internal.h"
#ifdef __x86_64__
@@ -34,14 +32,4 @@ textwindows int _check_cancel(void) {
return 0;
}
textwindows int _check_signal(bool restartable) {
int status;
if (_check_cancel() == -1) return -1;
if (!_weaken(__sig_check)) return 0;
if (!(status = _weaken(__sig_check)())) return 0;
if (_check_cancel() == -1) return -1;
if (status == 2 && restartable) return 0;
return eintr();
}
#endif /* __x86_64__ */
@@ -1,7 +1,7 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
╞══════════════════════════════════════════════════════════════════════════════╡
│ Copyright 2021 Justine Alexandra Roberts Tunney │
│ Copyright 2023 Justine Alexandra Roberts Tunney │
│ │
│ Permission to use, copy, modify, and/or distribute this software for │
│ any purpose with or without fee is hereby granted, provided that the │
@@ -16,25 +16,20 @@
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/runtime/dlfcn.h"
#include "libc/calls/internal.h"
#include "libc/calls/sig.internal.h"
#include "libc/intrin/weaken.h"
#include "libc/sysv/errfuns.h"
#ifdef __x86_64__
char *dlerror(void) {
return "cosmopolitan doesn't support dsos";
textwindows int _check_signal(bool restartable) {
int status;
if (_check_cancel() == -1) return -1;
if (!_weaken(__sig_check)) return 0;
if (!(status = _weaken(__sig_check)())) return 0;
if (_check_cancel() == -1) return -1;
if (status == 2 && restartable) return 0;
return eintr();
}
void *dlopen(const char *file, int mode) {
return NULL;
}
void *dlsym(void *handle, const char *name) {
return NULL;
}
int dlclose(void *handle) {
return -1;
}
int dl_iterate_phdr(int callback(void *info, size_t size, void *data),
void *data) {
return -1;
}
#endif /* __x86_64__ */
+137 -64
View File
@@ -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;
}
+3 -2
View File
@@ -86,8 +86,9 @@ static inline void InitProgramExecutableNameImpl(void) {
if (q[0] == '.' && q[1] == '/') {
q += 2;
}
if (getcwd(p, e - p - 1 - 4)) { // for / and .com
while (*p) ++p;
int got = __getcwd(p, e - p - 1 - 4); // for / and .com
if (got != -1) {
p += got - 1;
*p++ = '/';
}
}
+24 -34
View File
@@ -16,50 +16,40 @@
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/struct/sigset.internal.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/errno.h"
#include "libc/calls/sig.internal.h"
#include "libc/intrin/atomic.h"
#include "libc/nt/enum/wait.h"
#include "libc/nt/runtime.h"
#include "libc/nt/synchronization.h"
#include "libc/sysv/consts/sicode.h"
#include "libc/sysv/errfuns.h"
#include "libc/thread/posixthread.internal.h"
#include "libc/thread/thread.h"
#include "libc/thread/tls.h"
#ifdef __x86_64__
// returns 0 on timeout or spurious wakeup
// raises EINTR if a signal delivery interrupted wait operation
// raises ECANCELED if this POSIX thread was canceled in masked mode
static textwindows int _park_thread(uint32_t msdelay, sigset_t waitmask,
bool restartable) {
int rc;
int64_t sem;
sigset_t om;
uint32_t wi;
struct PosixThread *pt;
pt = _pthread_self();
pt->pt_flags &= ~PT_RESTARTABLE;
if (restartable) pt->pt_flags |= PT_RESTARTABLE;
pt->pt_semaphore = sem = CreateSemaphore(0, 0, 1, 0);
pthread_cleanup_push((void *)CloseHandle, (void *)sem);
atomic_store_explicit(&pt->pt_blocker, PT_BLOCKER_SEM, memory_order_release);
om = __sig_beginwait(waitmask);
if ((rc = _check_signal(restartable)) != -1) {
if ((wi = WaitForSingleObject(sem, msdelay)) != -1u) {
if (restartable && !(pt->pt_flags & PT_RESTARTABLE)) rc = eintr();
rc |= _check_signal(restartable);
} else {
rc = __winerr();
}
int sig;
if (_check_cancel() == -1) return -1;
if ((sig = __sig_get(waitmask))) {
int handler_was_called = __sig_relay(sig, SI_KERNEL, waitmask);
if (_check_cancel() == -1) return -1;
if (!restartable || handler_was_called == 1) return eintr();
}
__sig_finishwait(om);
atomic_store_explicit(&pt->pt_blocker, PT_BLOCKER_CPU, memory_order_release);
pt->pt_flags &= ~PT_RESTARTABLE;
pthread_cleanup_pop(true);
pt->pt_semaphore = 0;
return rc;
int expect = 0;
atomic_int futex = 0;
struct PosixThread *pt = _pthread_self();
pt->pt_blkmask = waitmask;
atomic_store_explicit(&pt->pt_blocker, &futex, memory_order_release);
bool32 ok = WaitOnAddress(&futex, &expect, sizeof(int), msdelay);
atomic_store_explicit(&pt->pt_blocker, 0, memory_order_release);
if (ok && (sig = __sig_get(waitmask))) {
int handler_was_called = __sig_relay(sig, SI_KERNEL, waitmask);
if (_check_cancel() == -1) return -1;
if (!restartable || handler_was_called == 1) return eintr();
}
return 0;
}
textwindows int _park_norestart(uint32_t msdelay, sigset_t waitmask) {
+26 -22
View File
@@ -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
View File
@@ -36,6 +36,7 @@
#include "libc/stdio/sysparam.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/sicode.h"
#include "libc/sysv/errfuns.h"
#include "libc/thread/posixthread.internal.h"
#include "libc/thread/thread.h"
@@ -67,12 +68,12 @@ sys_readwrite_nt(int fd, void *data, size_t size, ssize_t offset,
bool32 ReadOrWriteFile(int64_t, void *, uint32_t, uint32_t *,
struct NtOverlapped *)) {
bool32 ok;
uint64_t m;
int sig = 0;
uint32_t exchanged;
int olderror = errno;
bool eagained = false;
bool eintered = false;
bool canceled = false;
int handler_was_called;
struct PosixThread *pt;
struct Fd *f = g_fds.p + fd;
@@ -104,73 +105,50 @@ sys_readwrite_nt(int fd, void *data, size_t size, ssize_t offset,
}
}
// managing an overlapped i/o operation is tricky to do using just
// imperative procedural logic. its design lends itself more to be
// something that's abstracted in an object-oriented design, which
// easily manages the unusual lifecycles requirements of the thing
// the game here is to not return until win32 is done w/ `overlap`
// next we need to allow signal handlers to re-enter this function
// while we're performing a read in the same thread. this needs to
// be thread safe too of course. read() / write() are cancelation
// points so pthread_cancel() might teleport the execution here to
// pthread_exit(), so we need cleanup handlers that pthread_exit()
// can call, pushed onto the stack, so we don't leak win32 handles
// or worse trash the thread stack containing `overlap` that win32
// temporarily owns while the overlapped i/o op is being performed
// we implement a non-blocking iop by optimistically performing io
// and then aborting the operation if win32 says it needs to block
// with cancelation points, we need to be able to raise eintr when
// this thread is pre-empted to run a signal handler but only when
// that signal handler wasn't installed using this SA_RESTART flag
// in which case read() and write() will keep going afterwards. we
// support a second kind of eintr in cosmo/musl which is ecanceled
// and it's mission critical that it be relayed properly, since it
// can only be returned by a single system call in a thread's life
// another thing we do is check if any pending signals exist, then
// running as many of them as possible before entering a wait call
RestartOperation:
// signals have already been fully blocked by caller
// perform i/o operation with atomic signal/cancel checking
struct NtOverlapped overlap = {.hEvent = CreateEvent(0, 1, 0, 0),
.Pointer = offset};
struct ReadwriteResources rwc = {handle, &overlap};
pthread_cleanup_push(UnwindReadwrite, &rwc);
ok = ReadOrWriteFile(handle, data, size, 0, &overlap);
if (!ok && GetLastError() == kNtErrorIoPending) {
BlockingOperation:
pt = _pthread_self();
pt->pt_iohandle = handle;
pt->pt_ioverlap = &overlap;
pt->pt_flags |= PT_RESTARTABLE;
atomic_store_explicit(&pt->pt_blocker, PT_BLOCKER_IO, memory_order_release);
m = __sig_beginwait(waitmask);
// win32 says this i/o operation needs to block
if (f->flags & _O_NONBLOCK) {
// abort the i/o operation if file descriptor is in non-blocking mode
CancelIoEx(handle, &overlap);
eagained = true;
} else if (_check_signal(true)) {
} else if (_check_cancel()) {
// _check_cancel() can go three ways:
// 1. it'll return 0 if we're fine and no thread cancelation happened
// 2. it'll pthread_exit() and cleanup, when cancelation was deferred
// 3. it'll return -1 and raise ECANCELED if a cancelation was masked
CancelIoEx(handle, &overlap);
canceled = true;
} else if ((sig = __sig_get(waitmask))) {
// we've dequeued a signal that was pending per caller's old sigmask
// we can't call the signal handler until we release win32 resources
CancelIoEx(handle, &overlap);
if (errno == ECANCELED) {
canceled = true;
} else {
eintered = true;
}
} else {
// wait until i/o either completes or is canceled by another thread
// we avoid a race condition by having a second mask for unblocking
pt = _pthread_self();
pt->pt_blkmask = waitmask;
pt->pt_iohandle = handle;
pt->pt_ioverlap = &overlap;
atomic_store_explicit(&pt->pt_blocker, PT_BLOCKER_IO,
memory_order_release);
WaitForSingleObject(overlap.hEvent, -1u);
atomic_store_explicit(&pt->pt_blocker, 0, memory_order_release);
}
__sig_finishwait(m);
atomic_store_explicit(&pt->pt_blocker, PT_BLOCKER_CPU,
memory_order_release);
pt->pt_flags &= ~PT_RESTARTABLE;
pt->pt_ioverlap = 0;
pt->pt_iohandle = 0;
ok = true;
}
if (ok) {
bool32 should_wait = canceled || eagained;
ok = GetOverlappedResult(handle, &overlap, &exchanged, should_wait);
if (!ok && GetLastError() == kNtErrorIoIncomplete) {
goto BlockingOperation;
}
ok = GetOverlappedResult(handle, &overlap, &exchanged, true);
}
CloseHandle(overlap.hEvent);
pthread_cleanup_pop(false);
CloseHandle(overlap.hEvent);
// if we acknowledged a pending masked mode cancelation request then
// we must pass it to the caller immediately now that cleanup's done
@@ -178,8 +156,16 @@ sys_readwrite_nt(int fd, void *data, size_t size, ssize_t offset,
return ecanceled();
}
// sudden success trumps interrupts and/or failed i/o abort attempts
// plenty of code above might clobber errno, so we always restore it
// if we removed a pending signal then we must raise it
// it's now safe to call a signal handler that longjmps
if (sig) {
handler_was_called = __sig_relay(sig, SI_KERNEL, waitmask);
if (_check_cancel() == -1) return -1;
} else {
handler_was_called = 0;
}
// if i/o succeeded then return its result
if (ok) {
if (!pwriting && seekable) {
f->pointer = offset + exchanged;
@@ -188,23 +174,24 @@ sys_readwrite_nt(int fd, void *data, size_t size, ssize_t offset,
return exchanged;
}
// if we backed out of the i/o operation intentionally ignore errors
if (eagained) {
return eagain();
}
// if another thread canceled our win32 i/o operation then we should
// check and see if it was pthread_cancel() which committed the deed
// in which case _check_cancel() can acknowledge the cancelation now
// it's also fine to do nothing here; punt to next cancelation point
// only raise EINTR or EAGAIN if I/O got canceled
if (GetLastError() == kNtErrorOperationAborted) {
if (_check_cancel() == -1) return ecanceled();
if (!eintered && _check_signal(false)) return -1;
}
// if we chose to process a pending signal earlier then we preserve
// that original error explicitly here even though aborted == eintr
if (eintered) {
// raise EAGAIN if it's due to O_NONBLOCK mmode
if (eagained) {
return eagain();
}
// at this point the i/o must have been canceled due to a signal.
// this could be because we found the signal earlier and canceled
// ourself. otherwise it's due to a kill from another thread that
// added something to our mask and canceled our i/o, so we check.
if (!handler_was_called && (sig = __sig_get(waitmask))) {
handler_was_called = __sig_relay(sig, SI_KERNEL, waitmask);
if (_check_cancel() == -1) return -1;
}
// read() is @restartable unless non-SA_RESTART hands were called
if (handler_was_called != 1) {
goto RestartOperation;
}
return eintr();
}
+81 -56
View File
@@ -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;
+2
View File
@@ -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);
+2 -2
View File
@@ -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);
-1
View File
@@ -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);
+2 -3
View File
@@ -13,8 +13,6 @@ COSMOPOLITAN_C_START_
axdx_t __sys_fork(void);
axdx_t __sys_pipe(i32[hasatleast 2], i32);
axdx_t sys_getpid(void);
char *sys_getcwd(char *, u64);
char *sys_getcwd_xnu(char *, u64);
i32 __sys_dup3(i32, i32, i32);
i32 __sys_execve(const char *, char *const[], char *const[]);
i32 __sys_fcntl(i32, i32, ...);
@@ -53,6 +51,7 @@ i32 sys_fork(void);
i32 sys_fsync(i32);
i32 sys_ftruncate(i32, i64, i64);
i32 sys_getcontext(void *);
i32 sys_getcwd(char *, u64);
i32 sys_getpgid(i32);
i32 sys_getppid(void);
i32 sys_getpriority(i32, u32);
@@ -85,9 +84,9 @@ i32 sys_posix_openpt(i32);
i32 sys_renameat(i32, const char *, i32, const char *);
i32 sys_sem_close(i64);
i32 sys_sem_destroy(i64);
i32 sys_sem_destroy(i64);
i32 sys_sem_getvalue(i64, u32 *);
i32 sys_sem_init(u32, i64 *);
i32 sys_sem_destroy(i64);
i32 sys_sem_open(const char *, int, u32, i64 *);
i32 sys_sem_post(i64);
i32 sys_sem_trywait(i64);
+1 -1
View File
@@ -45,7 +45,7 @@ static void __tmpdir_init(void) {
if ((s = getenv("TMPDIR"))) {
if (*s != '/') {
if (!getcwd(__tmpdir.path, PATH_MAX)) {
if (__getcwd(__tmpdir.path, PATH_MAX) == -1) {
goto GiveUp;
}
strlcat(__tmpdir.path, "/", sizeof(__tmpdir.path));
+2 -1
View File
@@ -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);
-4
View File
@@ -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 {
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+3 -3
View File
@@ -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_ */
+581
View File
@@ -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;
}
+71
View File
@@ -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)
+10
View File
@@ -0,0 +1,10 @@
#include <dlfcn.h>
int main(int argc, char *argv[]) {
return ((int (*)(void *))argv[1])((void *[]){
dlopen,
dlsym,
dlclose,
dlerror,
});
}
+1
View File
@@ -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_
+1 -1
View File
@@ -4,7 +4,7 @@
#define __COSMOPOLITAN_MAJOR__ 3
#define __COSMOPOLITAN_MINOR__ 0
#define __COSMOPOLITAN_PATCH__ 1
#define __COSMOPOLITAN_PATCH__ 2
#define __COSMOPOLITAN__ \
(100000000 * __COSMOPOLITAN_MAJOR__ + 1000000 * __COSMOPOLITAN_MINOR__ + \
__COSMOPOLITAN_PATCH__)
+2 -2
View File
@@ -48,11 +48,11 @@ void __sig_unblock(sigset_t m) {
}
}
textwindows sigset_t __sig_beginwait(sigset_t waitmask) {
textwindows sigset_t __sig_begin(sigset_t waitmask) {
return atomic_exchange_explicit(&__get_tls()->tib_sigmask, waitmask,
memory_order_acquire);
}
textwindows void __sig_finishwait(sigset_t m) {
textwindows void __sig_finish(sigset_t m) {
atomic_store_explicit(&__get_tls()->tib_sigmask, m, memory_order_release);
}
+1 -1
View File
@@ -1,4 +1,4 @@
#ifndef _DLFCN_H
#define _DLFCN_H
#include "libc/runtime/dlfcn.h"
#include "libc/dlopen/dlfcn.h"
#endif /* _DLFCN_H */
+4
View File
@@ -0,0 +1,4 @@
#ifndef _IFADDRS_H
#define _IFADDRS_H
#include "libc/sock/ifaddrs.h"
#endif /* _IFADDRS_H */
+1
View File
@@ -16,6 +16,7 @@ endif
.PHONY: o/$(MODE)/libc
o/$(MODE)/libc: o/$(MODE)/libc/calls \
o/$(MODE)/libc/crt \
o/$(MODE)/libc/dlopen \
o/$(MODE)/libc/dns \
o/$(MODE)/libc/elf \
o/$(MODE)/libc/fmt \
+1 -1
View File
@@ -213,7 +213,7 @@ skip_readlink:
output[q] = 0;
if (output[0] != '/') {
if (!getcwd(stack, sizeof(stack))) return 0;
if (__getcwd(stack, sizeof(stack)) == -1) return 0;
l = strlen(stack);
/* Cancel any initial .. components. */
p = 0;
+6 -4
View File
@@ -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());
+5 -1
View File
@@ -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
View File
@@ -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
+2 -2
View File
@@ -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;
+6 -1
View File
@@ -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;
+114
View File
@@ -0,0 +1,114 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
╞══════════════════════════════════════════════════════════════════════════════╡
│ Copyright 2023 Justine Alexandra Roberts Tunney │
│ │
│ Permission to use, copy, modify, and/or distribute this software for │
│ any purpose with or without fee is hereby granted, provided that the │
│ above copyright notice and this permission notice appear in all copies. │
│ │
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/sock/ifaddrs.h"
#include "libc/calls/calls.h"
#include "libc/mem/mem.h"
#include "libc/sock/sock.h"
#include "libc/sock/struct/ifconf.h"
#include "libc/sock/struct/ifreq.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/af.h"
#include "libc/sysv/consts/iff.h"
#include "libc/sysv/consts/sio.h"
#include "libc/sysv/consts/sock.h"
struct IfAddr {
struct ifaddrs ifaddrs;
char name[IFNAMSIZ];
struct sockaddr_in addr;
struct sockaddr_in netmask;
struct sockaddr_in bstaddr;
};
/**
* Frees network interface address list.
*/
void freeifaddrs(struct ifaddrs *ifp) {
struct ifaddrs *next;
while (ifp) {
next = ifp->ifa_next;
free(ifp);
ifp = next;
}
}
/**
* Gets network interface address list.
*
* @return 0 on success, or -1 w/ errno
* @see tool/viz/getifaddrs.c for example code
*/
int getifaddrs(struct ifaddrs **out_ifpp) {
int rc = -1;
int fd;
if ((fd = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0)) != -1) {
char *data;
size_t size;
if ((data = malloc((size = 16384)))) {
struct ifconf conf;
conf.ifc_buf = data;
conf.ifc_len = size;
if (!ioctl(fd, SIOCGIFCONF, &conf)) {
struct ifaddrs *res = 0;
for (struct ifreq *ifr = (struct ifreq *)data;
(char *)ifr < data + conf.ifc_len; ++ifr) {
if (ifr->ifr_addr.sa_family != AF_INET) {
continue; // TODO(jart): IPv6 support
}
struct IfAddr *addr;
if ((addr = calloc(1, sizeof(struct IfAddr)))) {
memcpy(addr->name, ifr->ifr_name, IFNAMSIZ);
addr->ifaddrs.ifa_name = addr->name;
memcpy(&addr->addr, &ifr->ifr_addr, sizeof(struct sockaddr_in));
addr->ifaddrs.ifa_addr = (struct sockaddr *)&addr->addr;
addr->ifaddrs.ifa_netmask = (struct sockaddr *)&addr->netmask;
if (!ioctl(fd, SIOCGIFFLAGS, ifr)) {
addr->ifaddrs.ifa_flags = ifr->ifr_flags;
}
if (!ioctl(fd, SIOCGIFNETMASK, ifr)) {
memcpy(&addr->netmask, &ifr->ifr_addr,
sizeof(struct sockaddr_in));
}
unsigned long op;
if (addr->ifaddrs.ifa_flags & IFF_BROADCAST) {
op = SIOCGIFBRDADDR;
} else if (addr->ifaddrs.ifa_flags & IFF_POINTOPOINT) {
op = SIOCGIFDSTADDR;
} else {
op = 0;
}
if (op && !ioctl(fd, op, ifr)) {
memcpy(&addr->bstaddr, &ifr->ifr_addr,
sizeof(struct sockaddr_in));
addr->ifaddrs.ifa_broadaddr = // is union'd w/ ifu_dstaddr
(struct sockaddr *)&addr->bstaddr;
}
addr->ifaddrs.ifa_next = res;
res = (struct ifaddrs *)addr;
}
}
*out_ifpp = res;
rc = 0;
}
free(data);
}
close(fd);
}
return rc;
}
+25
View File
@@ -0,0 +1,25 @@
#ifndef COSMOPOLITAN_LIBC_SOCK_IFADDRS_H_
#define COSMOPOLITAN_LIBC_SOCK_IFADDRS_H_
#include "libc/sock/struct/sockaddr.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
struct ifaddrs {
struct ifaddrs *ifa_next;
char *ifa_name;
unsigned ifa_flags;
struct sockaddr *ifa_addr;
struct sockaddr *ifa_netmask;
union {
struct sockaddr *ifa_broadaddr;
struct sockaddr *ifa_dstaddr;
};
void *ifa_data;
};
void freeifaddrs(struct ifaddrs *);
int getifaddrs(struct ifaddrs **);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_SOCK_IFADDRS_H_ */
-27
View File
@@ -1,27 +0,0 @@
#ifndef COSMOPOLITAN_LIBC_SOCK_OVERLAPPED_H_
#define COSMOPOLITAN_LIBC_SOCK_OVERLAPPED_H_
#include "libc/nt/struct/overlapped.h"
#include "libc/thread/thread.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
#define wsa_overlapped_cleanup_push(handle, overlap) \
{ \
struct WsaOverlappedCleanup wsa_overlapped_cleanup = {handle, overlap}; \
pthread_cleanup_push(wsa_overlapped_cleanup_callback, \
&wsa_overlapped_cleanup);
#define wsa_overlapped_cleanup_pop() \
pthread_cleanup_pop(false); \
}
struct WsaOverlappedCleanup {
int64_t handle;
struct NtOverlapped *overlap;
};
void wsa_overlapped_cleanup_callback(void *);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_SOCK_OVERLAPPED_H_ */
+38 -45
View File
@@ -18,6 +18,7 @@
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/assert.h"
#include "libc/calls/internal.h"
#include "libc/calls/sig.internal.h"
#include "libc/calls/struct/fd.internal.h"
#include "libc/calls/struct/sigset.internal.h"
#include "libc/errno.h"
@@ -35,6 +36,7 @@
#include "libc/sock/internal.h"
#include "libc/sock/syscall_fd.internal.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/sicode.h"
#include "libc/sysv/errfuns.h"
#include "libc/thread/posixthread.internal.h"
#ifdef __x86_64__
@@ -60,92 +62,83 @@ static void CancelWinsockBlock(int64_t handle, struct NtOverlapped *overlap) {
textwindows ssize_t
__winsock_block(int64_t handle, uint32_t flags, bool nonblock,
uint32_t srwtimeout, sigset_t wait_signal_mask,
uint32_t srwtimeout, sigset_t waitmask,
int StartSocketOp(int64_t handle, struct NtOverlapped *overlap,
uint32_t *flags, void *arg),
void *arg) {
int rc;
uint64_t m;
int sig = 0;
uint32_t status;
uint32_t exchanged;
int olderror = errno;
bool eagained = false;
bool eintered = false;
bool canceled = false;
int handler_was_called;
struct PosixThread *pt;
RestartOperation:
struct NtOverlapped overlap = {.hEvent = WSACreateEvent()};
struct WinsockBlockResources wbr = {handle, &overlap};
pthread_cleanup_push(UnwindWinsockBlock, &wbr);
rc = StartSocketOp(handle, &overlap, &flags, arg);
if (rc && WSAGetLastError() == kNtErrorIoPending) {
BlockingOperation:
pt = _pthread_self();
pt->pt_iohandle = handle;
pt->pt_ioverlap = &overlap;
pt->pt_flags |= PT_RESTARTABLE;
atomic_store_explicit(&pt->pt_blocker, PT_BLOCKER_IO, memory_order_release);
m = __sig_beginwait(wait_signal_mask);
if (nonblock) {
CancelWinsockBlock(handle, &overlap);
eagained = true;
} else if (_check_signal(true)) {
} else if (_check_cancel()) {
CancelWinsockBlock(handle, &overlap);
canceled = true;
} else if ((sig = __sig_get(waitmask))) {
CancelWinsockBlock(handle, &overlap);
if (errno == ECANCELED) {
canceled = true;
} else {
eintered = true;
}
} else {
pt = _pthread_self();
pt->pt_blkmask = waitmask;
pt->pt_iohandle = handle;
pt->pt_ioverlap = &overlap;
atomic_store_explicit(&pt->pt_blocker, PT_BLOCKER_IO,
memory_order_release);
status = WSAWaitForMultipleEvents(1, &overlap.hEvent, 0,
srwtimeout ? srwtimeout : -1u, 0);
atomic_store_explicit(&pt->pt_blocker, 0, memory_order_release);
if (status == kNtWaitTimeout) {
// rcvtimeo or sndtimeo elapsed
// SO_RCVTIMEO or SO_SNDTIMEO elapsed
CancelWinsockBlock(handle, &overlap);
eagained = true;
} else if (status == kNtWaitFailed) {
// Failure should be an impossible condition, but MSDN lists
// WSAENETDOWN and WSA_NOT_ENOUGH_MEMORY as possible errors.
CancelWinsockBlock(handle, &overlap);
eintered = true;
}
}
__sig_finishwait(m);
atomic_store_explicit(&pt->pt_blocker, PT_BLOCKER_CPU,
memory_order_release);
pt->pt_flags &= ~PT_RESTARTABLE;
pt->pt_ioverlap = 0;
pt->pt_iohandle = 0;
rc = 0;
}
if (!rc) {
bool32 should_wait = canceled || eagained;
bool32 ok = WSAGetOverlappedResult(handle, &overlap, &exchanged,
should_wait, &flags);
if (!ok && WSAGetLastError() == kNtErrorIoIncomplete) {
goto BlockingOperation;
}
rc = ok ? 0 : -1;
rc = WSAGetOverlappedResult(handle, &overlap, &exchanged, true, &flags)
? 0
: -1;
}
WSACloseEvent(overlap.hEvent);
pthread_cleanup_pop(false);
WSACloseEvent(overlap.hEvent);
if (canceled) {
return ecanceled();
}
if (sig) {
handler_was_called = __sig_relay(sig, SI_KERNEL, waitmask);
if (_check_cancel() == -1) return -1;
} else {
handler_was_called = 0;
}
if (!rc) {
errno = olderror;
return exchanged;
}
if (eagained) {
return eagain();
}
if (GetLastError() == kNtErrorOperationAborted) {
if (_check_cancel() == -1) return ecanceled();
if (!eintered && _check_signal(false)) return -1;
}
if (eintered) {
if (WSAGetLastError() == kNtErrorOperationAborted) {
if (eagained) return eagain();
if (!handler_was_called && (sig = __sig_get(waitmask))) {
handler_was_called = __sig_relay(sig, SI_KERNEL, waitmask);
if (_check_cancel() == -1) return -1;
}
if (handler_was_called != 1) {
goto RestartOperation;
}
return eintr();
}
return __winsockerr();
@@ -17,21 +17,28 @@
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/calls.h"
#include "libc/calls/struct/stat.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"
/**
* Returns current working directory.
*
* If the `PWD` environment variable is set, and it seems legit, then
* that'll be returned.
* If the `PWD` environment variable is set, and it's correct, then
* that'll be returned in its exact canonical or non-canonical form
* instead of calling getcwd().
*
* @return pointer that must be free()'d, or NULL w/ errno
*/
char *get_current_dir_name(void) {
const char *p;
if ((p = getenv("PWD")) && *p == '/') {
return strdup(p);
const char *res;
struct stat st1, st2;
if ((res = getenv("PWD")) && *res && //
!stat(res, &st1) && //
!stat(".", &st2) && //
st1.st_dev == st2.st_dev && //
st1.st_ino == st2.st_ino) {
return strdup(res);
} else {
return getcwd(0, 0);
}
@@ -1,7 +1,7 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
╞══════════════════════════════════════════════════════════════════════════════╡
│ Copyright 2020 Justine Alexandra Roberts Tunney │
│ Copyright 2023 Justine Alexandra Roberts Tunney │
│ │
│ Permission to use, copy, modify, and/or distribute this software for │
│ any purpose with or without fee is hereby granted, provided that the │
@@ -16,69 +16,62 @@
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/macros.internal.h"
#include "libc/nt/files.h"
#include "libc/calls/calls.h"
#include "libc/mem/mem.h"
#include "libc/str/str.h"
#include "libc/str/utf16.h"
#include "libc/sysv/errfuns.h"
textwindows char *sys_getcwd_nt(char *buf, size_t size) {
uint64_t w;
wint_t x, y;
uint32_t n, i, j;
char16_t p[PATH_MAX];
if ((n = GetCurrentDirectory(ARRAYLEN(p), p))) {
if (4 + n + 1 <= size && 4 + n + 1 <= ARRAYLEN(p)) {
tprecode16to8(buf, size, p);
i = 0;
j = 0;
if (n >= 3 && isalpha(p[0]) && p[1] == ':' && p[2] == '\\') {
// turn c:\... into \c\...
p[1] = p[0];
p[0] = '\\';
} else if (n >= 7 && p[0] == '\\' && p[1] == '\\' && p[2] == '?' &&
p[3] == '\\' && isalpha(p[4]) && p[5] == ':' && p[6] == '\\') {
// turn \\?\c:\... into \c\...
buf[j++] = '/';
buf[j++] = p[4];
buf[j++] = '/';
i += 7;
}
while (i < n) {
x = p[i++] & 0xffff;
if (!IsUcs2(x)) {
if (i < n) {
y = p[i++] & 0xffff;
x = MergeUtf16(x, y);
} else {
x = 0xfffd;
}
}
if (x < 0200) {
if (x == '\\') {
x = '/';
}
w = x;
} else {
w = tpenc(x);
}
do {
if (j < size) {
buf[j++] = w;
}
w >>= 8;
} while (w);
}
if (j < size) {
buf[j] = 0;
return buf;
}
/**
* Returns current working directory, e.g.
*
* const char *dirname = gc(getcwd(0,0)); // if malloc is linked
* const char *dirname = getcwd(alloca(PATH_MAX),PATH_MAX);
*
* @param buf is where UTF-8 NUL-terminated path string gets written,
* which may be NULL to ask this function to malloc a buffer
* @param size is number of bytes available in buf, e.g. PATH_MAX+1,
* which may be 0 if buf is NULL
* @return buf containing system-normative path or NULL w/ errno
* @raise EACCES if the current directory path couldn't be accessed
* @raise ERANGE if `size` wasn't big enough for path and nul byte
* @raise ENOMEM on failure to allocate the requested buffer
* @raise EINVAL if `size` is zero and `buf` is non-null
* @raise EFAULT if `buf` points to invalid memory
*/
char *getcwd(char *buf, size_t size) {
// setup memory per api
char *path;
if (buf) {
path = buf;
if (!size) {
einval();
return 0;
}
erange();
return NULL;
} else {
__winerr();
return NULL;
if (!size) size = 4096;
if (!(path = malloc(size))) {
return 0; // enomem
}
}
// invoke the real system call
int got = __getcwd(path, size);
if (got == -1) {
if (path != buf) {
free(path);
}
return 0; // eacces, erange, or efault
}
// cut allocation down to size
if (path != buf) {
char *p;
if ((p = realloc(path, strlen(path) + 1))) {
path = p;
}
}
// return result
return path;
}
+3
View File
@@ -42,6 +42,9 @@ int IsZipEocd32(const uint8_t *p, size_t n, size_t i) {
if (ZIP_CDIR_RECORDS(p + i) * kZipCfileHdrMinSize > ZIP_CDIR_SIZE(p + i)) {
return kZipErrorEocdRecordsOverflow;
}
if (ZIP_CDIR_OFFSET(p + i) == 0xFFFFFFFFu) {
return kZipErrorEocdRecordsOverflow;
}
if (ckd_add(&offset, ZIP_CDIR_OFFSET(p + i), ZIP_CDIR_SIZE(p + i))) {
return kZipErrorEocdOffsetSizeOverflow;
}
+2 -5
View File
@@ -124,7 +124,7 @@ syscon errno ECANCELED 125 125 89 89 85 88 87 1223 # kNtError
syscon errno EOWNERDEAD 130 130 105 105 96 94 97 105 # kNtErrorSemOwnerDied; raised by pthread_cond_timedwait(3), pthread_mutex_consistent(3), pthread_mutex_getprioceiling(3), pthread_mutex_lock(3), pthread_mutex_timedlock(3), pthread_mutexattr_getrobust(3), pthread_mutexattr_setrobust(3)
syscon errno ENOTRECOVERABLE 131 131 104 104 95 93 98 0 # raised by pthread_cond_timedwait(3), pthread_mutex_consistent(3), pthread_mutex_getprioceiling(3), pthread_mutex_lock(3), pthread_mutex_timedlock(3), pthread_mutexattr_getrobust(3), pthread_mutexattr_setrobust(3)
syscon errno ENONET 64 64 317 317 317 317 317 0 # made up on BSDs; raised by accept(2)
syscon errno ERESTART 85 85 -1 -1 -1 -1 -3 0 # should only be seen in ptrace()
syscon errno ERESTART 85 85 318 318 318 318 -3 20000 # should only be seen in ptrace()
syscon errno ENODATA 61 61 96 96 0 0 89 232 # no message is available in xsi stream or named pipe is being closed; no data available; barely in posix; returned by ioctl; very close in spirit to EPIPE?
syscon errno ENOSR 63 63 98 98 0 90 90 0 # out of streams resources; something like EAGAIN; it's in POSIX; maybe some commercial UNIX returns it with openat, putmsg, putpmsg, posix_openpt, ioctl, open
syscon errno ENOSTR 60 60 99 99 0 0 91 0 # not a stream; returned by getmsg, putmsg, putpmsg, getpmsg
@@ -1351,7 +1351,7 @@ syscon iff IFF_ALLMULTI 0x0200 0x0200 0x0200 0x0200 0x0200 0x0200
syscon iff IFF_NOARP 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 # faked nt as linux; unix consensus
syscon iff IFF_POINTOPOINT 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 # point-to-point; faked nt as linux; unix consensus
syscon iff IFF_PROMISC 0x100 0x100 0x100 0x100 0x100 0x100 0x100 0 # in packet capture mode; unix consensus
syscon iff IFF_RUNNING 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0 # unix consensus
syscon iff IFF_RUNNING 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0 # unix consensus (mostly for bsd compatibility?)
syscon iff IFF_NOTRAILERS 0x20 0x20 0x20 0x20 0 0 0 0
syscon iff IFF_AUTOMEDIA 0x4000 0x4000 0 0 0 0 0 0
syscon iff IFF_DYNAMIC 0x8000 0x8000 0 0 0 0 0 0
@@ -1391,9 +1391,6 @@ syscon misc IP6F_MORE_FRAG 0x0100 0x0100 0x0100 0x0100 0x0100 0x010
syscon misc IP6F_OFF_MASK 0xf8ff 0xf8ff 0xf8ff 0xf8ff 0xf8ff 0xf8ff 0xf8ff 0xf8ff # consensus
syscon misc IP6F_RESERVED_MASK 0x0600 0x0600 0x0600 0x0600 0x0600 0x0600 0x0600 0x0600 # consensus
syscon misc NO_SENSE 0 0 0 0 0 0 0 0 # consensus
syscon misc NO_ADDRESS 4 4 4 4 4 4 4 0x2afc # unix consensus
syscon misc L_SET 0 0 0 0 0 0 0 0 # consensus
syscon misc L_INCR 1 1 1 1 1 1 1 0 # unix consensus
syscon misc L_XTND 2 2 2 2 2 2 2 0 # unix consensus
+1 -1
View File
@@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon errno,ERESTART,85,85,-1,-1,-1,-1,-3,0
.syscon errno,ERESTART,85,85,318,318,318,318,-3,20000
-2
View File
@@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon misc,NO_ADDRESS,4,4,4,4,4,4,4,0x2afc
-2
View File
@@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon misc,NO_SENSE,0,0,0,0,0,0,0,0
+2 -3
View File
@@ -14,10 +14,8 @@
#define PT_NOCANCEL 8
#define PT_MASKED 16
#define PT_INCANCEL 32
#define PT_RESTARTABLE 64
#define PT_OPENBSD_KLUDGE 128
#define PT_OPENBSD_KLUDGE 64
#define PT_BLOCKER_CPU ((atomic_int *)-0)
#define PT_BLOCKER_SEM ((atomic_int *)-1)
#define PT_BLOCKER_IO ((atomic_int *)-2)
@@ -93,6 +91,7 @@ struct PosixThread {
struct Dll list; // list of threads
struct _pthread_cleanup_buffer *pt_cleanup;
_Atomic(atomic_int *) pt_blocker;
uint64_t pt_blkmask;
int64_t pt_semaphore;
intptr_t pt_iohandle;
void *pt_ioverlap;
+39 -3
View File
@@ -17,21 +17,57 @@
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/calls.h"
#include "libc/fmt/fmt.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/fmt/libgen.h"
#include "libc/intrin/bits.h"
#include "libc/limits.h"
#include "libc/log/check.h"
#include "libc/macros.internal.h"
#include "libc/mem/gc.internal.h"
#include "libc/str/str.h"
#include "libc/testlib/testlib.h"
#include "libc/x/x.h"
void SetUpOnce(void) {
testlib_enable_tmp_setup_teardown();
ASSERT_SYS(0, 0, pledge("stdio rpath cpath fattr", 0));
}
TEST(__getcwd, zero) {
ASSERT_SYS(ERANGE, -1, __getcwd(0, 0));
}
TEST(__getcwd, returnsLengthIncludingNul) {
char cwd1[PATH_MAX];
char cwd2[PATH_MAX];
ASSERT_NE(-1, __getcwd(cwd1, PATH_MAX));
ASSERT_EQ(strlen(cwd1) + 1, __getcwd(cwd2, PATH_MAX));
}
TEST(__getcwd, tooShort_negOneReturned_bufferIsntModified) {
char cwd[4] = {0x55, 0x55, 0x55, 0x55};
ASSERT_SYS(ERANGE, -1, __getcwd(cwd, 4));
ASSERT_EQ(0x55555555, READ32LE(cwd));
}
TEST(__getcwd, noRoomForNul) {
char cwd1[PATH_MAX];
char cwd2[PATH_MAX];
ASSERT_NE(-1, __getcwd(cwd1, PATH_MAX));
ASSERT_SYS(ERANGE, -1, __getcwd(cwd2, strlen(cwd1)));
}
TEST(__getcwd, alwaysStartsWithSlash) {
char cwd[PATH_MAX];
ASSERT_NE(-1, __getcwd(cwd, PATH_MAX));
ASSERT_EQ('/', *cwd);
}
TEST(__getcwd, notInRootDir_neverEndsWithSlash) {
char cwd[PATH_MAX];
ASSERT_NE(-1, __getcwd(cwd, PATH_MAX));
ASSERT_FALSE(endswith(cwd, "/"));
}
TEST(getcwd, test) {
char buf[PATH_MAX];
EXPECT_SYS(0, 0, mkdir("subdir", 0755));
@@ -1,7 +1,7 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
╞══════════════════════════════════════════════════════════════════════════════╡
│ Copyright 2021 Justine Alexandra Roberts Tunney │
│ Copyright 2023 Justine Alexandra Roberts Tunney │
│ │
│ Permission to use, copy, modify, and/or distribute this software for │
│ any purpose with or without fee is hereby granted, provided that the │
@@ -16,54 +16,57 @@
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/atomic.h"
#include "libc/calls/calls.h"
#include "libc/calls/struct/metastat.internal.h"
#include "libc/calls/struct/stat.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/at.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/errfuns.h"
#include "libc/calls/struct/sigaction.h"
#include "libc/runtime/clktck.h"
#include "libc/runtime/runtime.h"
#include "libc/sysv/consts/sa.h"
#include "libc/sysv/consts/sig.h"
#include "libc/testlib/testlib.h"
#include "libc/thread/thread.h"
#define XNU_F_GETPATH 50
#define XNU_MAXPATHLEN 1024
jmp_buf jb;
int pfds[2];
atomic_bool isdone;
volatile bool canjmp;
static inline bool CopyString(char *d, const char *s, size_t n) {
size_t i;
for (i = 0; i < n; ++i) {
if (!(d[i] = s[i])) {
return true;
}
void OnSignal(int sig) {
if (canjmp) {
canjmp = false;
longjmp(jb, 1);
}
return false;
}
char *sys_getcwd_xnu(char *res, size_t size) {
int fd;
union metastat st[2];
char buf[XNU_MAXPATHLEN], *ret = NULL;
if ((fd = __sys_openat_nc(AT_FDCWD, ".", O_RDONLY | O_DIRECTORY, 0)) != -1) {
if (__sys_fstat(fd, &st[0]) != -1) {
if (st[0].xnu.st_dev && st[0].xnu.st_ino) {
if (__sys_fcntl(fd, XNU_F_GETPATH, (uintptr_t)buf) != -1) {
if (__sys_fstatat(AT_FDCWD, buf, &st[1], 0) != -1) {
if (st[0].xnu.st_dev == st[1].xnu.st_dev &&
st[0].xnu.st_ino == st[1].xnu.st_ino) {
if (CopyString(res, buf, size)) {
ret = res;
} else {
erange();
}
} else {
einval();
}
}
}
} else {
einval();
}
void *ReadWorker(void *arg) {
int got;
char buf[8];
while (!isdone) {
if (!(got = setjmp(jb))) {
canjmp = true;
read(pfds[0], buf, sizeof(buf));
abort();
}
close(fd);
}
return ret;
return 0;
}
TEST(eintr, longjmp) {
pthread_t th;
struct sigaction sa = {
.sa_handler = OnSignal,
.sa_flags = SA_SIGINFO | SA_NODEFER,
};
sigaction(SIGUSR1, &sa, 0);
ASSERT_SYS(0, 0, pipe(pfds));
ASSERT_EQ(0, pthread_create(&th, 0, ReadWorker, 0));
for (int i = 0; i < 10; ++i) {
pthread_kill(th, SIGUSR1);
usleep(1. / CLK_TCK * 1e6);
}
isdone = true;
pthread_kill(th, SIGUSR1);
ASSERT_EQ(0, pthread_join(th, 0));
ASSERT_SYS(0, 0, close(pfds[1]));
ASSERT_SYS(0, 0, close(pfds[0]));
}
+1
View File
@@ -197,6 +197,7 @@ TEST(mprotect, testRwxMap_vonNeumannRules) {
}
TEST(mprotect, testExecuteFlatFileMapOpenedAsReadonly) {
if (IsXnuSilicon()) return; // TODO(jart): Use APE Loader SIP workaround?
int (*p)(void);
size_t n = sizeof(kRet31337);
ASSERT_SYS(0, 3, creat("return31337", 0755));
+31
View File
@@ -0,0 +1,31 @@
-- Copyright 2023 Justine Alexandra Roberts Tunney
--
-- Permission to use, copy, modify, and/or distribute this software for
-- any purpose with or without fee is hereby granted, provided that the
-- above copyright notice and this permission notice appear in all copies.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
-- WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
-- WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
-- AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
-- DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
-- PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
-- TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-- PERFORMANCE OF THIS SOFTWARE.
local sqlite3 = require "lsqlite3"
local db = assert(sqlite3.open("file:/memdb1?vfs=memdb&mode=ro",
sqlite3.OPEN_URI + sqlite3.OPEN_READWRITE + sqlite3.OPEN_CREATE))
assert(db:readonly() == true)
db = sqlite3.open("file:/memdb1?vfs=memdb",
sqlite3.OPEN_URI + sqlite3.OPEN_READWRITE + sqlite3.OPEN_CREATE)
assert(db:readonly() == false)
assert(db:readonly("main") == false)
assert(db:readonly("foo") == nil)
assert(db:exec("create table foo(a)") == 0)
local st = assert(db:prepare("select * from foo"))
assert(st:readonly() == true)
st = assert(db:prepare("insert into foo (a) values (1)"))
assert(st:readonly() == false)
+1
View File
@@ -27,6 +27,7 @@
╚─────────────────────────────────────────────────────────────────────────────*/
#define loadlib_c
#define LUA_LIB
#include "libc/dlopen/dlfcn.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
#include "third_party/lua/lauxlib.h"
+36 -9
View File
@@ -23,6 +23,8 @@
#include "libc/calls/internal.h"
#include "libc/calls/sig.internal.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/struct/sigset.h"
#include "libc/calls/struct/sigset.internal.h"
#include "libc/calls/struct/timespec.h"
#include "libc/calls/struct/timespec.internal.h"
#include "libc/calls/syscall_support-nt.internal.h"
@@ -40,6 +42,7 @@
#include "libc/nt/synchronization.h"
#include "libc/runtime/clktck.h"
#include "libc/sysv/consts/clock.h"
#include "libc/sysv/consts/sicode.h"
#include "libc/sysv/consts/timer.h"
#include "libc/sysv/errfuns.h"
#include "libc/thread/freebsd.internal.h"
@@ -147,9 +150,11 @@ static int nsync_futex_polyfill_ (atomic_int *w, int expect, struct timespec *ab
static int nsync_futex_wait_win32_ (atomic_int *w, int expect, char pshare,
const struct timespec *timeout,
struct PosixThread *pt) {
struct PosixThread *pt,
sigset_t waitmask) {
int sig;
bool32 ok;
struct timespec deadline, interval, remain, wait, now;
struct timespec deadline, wait, now;
if (timeout) {
deadline = *timeout;
@@ -162,16 +167,36 @@ static int nsync_futex_wait_win32_ (atomic_int *w, int expect, char pshare,
if (timespec_cmp (now, deadline) > 0) {
return etimedout();
}
remain = timespec_sub (deadline, now);
interval = timespec_frommillis (5000);
wait = timespec_cmp (remain, interval) > 0 ? interval : remain;
wait = timespec_sub (deadline, now);
if (atomic_load_explicit (w, memory_order_acquire) != expect) {
return 0;
}
if (pt) atomic_store_explicit (&pt->pt_blocker, w, memory_order_release);
if (_check_signal (false) == -1) return -1;
if (pt) {
if (_check_cancel () == -1) {
return -1; /* ECANCELED */
}
if ((sig = __sig_get (waitmask))) {
__sig_relay (sig, SI_KERNEL, waitmask);
if (_check_cancel () == -1) {
return -1; /* ECANCELED */
}
return eintr ();
}
pt->pt_blkmask = waitmask;
atomic_store_explicit (&pt->pt_blocker, w, memory_order_release);
}
ok = WaitOnAddress (w, &expect, sizeof(int), timespec_tomillis (wait));
if (_check_signal (false) == -1) return -1;
if (pt) {
/* __sig_cancel wakes our futex without changing `w` after enqueing signals */
atomic_store_explicit (&pt->pt_blocker, 0, memory_order_release);
if (ok && atomic_load_explicit (w, memory_order_acquire) == expect && (sig = __sig_get (waitmask))) {
__sig_relay (sig, SI_KERNEL, waitmask);
if (_check_cancel () == -1) {
return -1; /* ECANCELED */
}
return eintr ();
}
}
if (ok) {
return 0;
} else {
@@ -233,7 +258,9 @@ int nsync_futex_wait_ (atomic_int *w, int expect, char pshare, const struct time
if (IsWindows ()) {
// Windows 8 futexes don't support multiple processes :(
if (pshare) goto Polyfill;
rc = nsync_futex_wait_win32_ (w, expect, pshare, timeout, pt);
sigset_t m = __sig_block ();
rc = nsync_futex_wait_win32_ (w, expect, pshare, timeout, pt, m);
__sig_unblock (m);
} else if (IsXnu ()) {
uint32_t op, us;
if (pshare) {
+1 -1
View File
@@ -35,7 +35,7 @@
#include "libc/nt/enum/sw.h"
#include "libc/nt/files.h"
#include "libc/nt/runtime.h"
#include "libc/runtime/dlfcn.h"
#include "libc/dlopen/dlfcn.h"
#include "libc/runtime/pathconf.h"
#include "libc/runtime/sysconf.h"
#include "libc/sock/sendfile.internal.h"
+1 -1
View File
@@ -37,7 +37,7 @@
#include "libc/nt/enum/sw.h"
#include "libc/nt/files.h"
#include "libc/nt/runtime.h"
#include "libc/runtime/dlfcn.h"
#include "libc/dlopen/dlfcn.h"
#include "libc/runtime/pathconf.h"
#include "libc/runtime/sysconf.h"
#include "libc/sock/sendfile.internal.h"
+1 -1
View File
@@ -5,7 +5,7 @@
│ https://docs.python.org/3/license.html │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/weirdtypes.h"
#include "libc/runtime/dlfcn.h"
#include "libc/dlopen/dlfcn.h"
#include "third_party/python/Include/fileutils.h"
#include "third_party/python/Include/modsupport.h"
#include "third_party/python/Include/pyerrors.h"
+1 -1
View File
@@ -7,7 +7,7 @@
#include "third_party/python/Include/pystate.h"
#include "libc/errno.h"
#include "libc/intrin/pushpop.internal.h"
#include "libc/runtime/dlfcn.h"
#include "libc/dlopen/dlfcn.h"
#include "third_party/python/Include/ceval.h"
#include "third_party/python/Include/dictobject.h"
#include "third_party/python/Include/listobject.h"
+1
View File
@@ -455,6 +455,7 @@ THIRD_PARTY_PYTHON_STAGE1_A_SRCS = \
THIRD_PARTY_PYTHON_STAGE1_A_DIRECTDEPS = \
DSP_SCALE \
LIBC_CALLS \
LIBC_DLOPEN \
LIBC_FMT \
LIBC_INTRIN \
LIBC_LOG \
+1 -1
View File
@@ -37,7 +37,7 @@
#include "libc/limits.h"
#include "libc/mem/mem.h"
#include "libc/nt/synchronization.h"
#include "libc/runtime/dlfcn.h"
#include "libc/dlopen/dlfcn.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/sysconf.h"
#include "libc/sock/select.h"
+1
View File
@@ -79,6 +79,7 @@ THIRD_PARTY_QUICKJS_A_OBJS = \
THIRD_PARTY_QUICKJS_A_DIRECTDEPS = \
LIBC_CALLS \
LIBC_DLOPEN \
LIBC_FMT \
LIBC_INTRIN \
LIBC_LOG \
+16 -1
View File
@@ -38,6 +38,21 @@ static struct ZipArgs {
char **oldargv;
} g_zipargs;
// remap \n → newline
static char *Decode(char *arg) {
int i, j;
for (i = j = 0; arg[i]; ++i) {
if (arg[i] == '\\' && arg[i + 1] == 'n') {
arg[j++] = '\n';
++i;
} else {
arg[j++] = arg[i];
}
}
arg[j] = 0;
return arg;
}
static void AddZipArg(int *argc, char ***argv, char *arg) {
*argv = xrealloc(*argv, (++(*argc) + 1) * sizeof(*(*argv)));
(*argv)[*argc - 1] = arg;
@@ -72,7 +87,7 @@ int LoadZipArgsImpl(int *argc, char ***argv, char *data) {
AddZipArg(&n, &args, (*argv)[i]);
}
} else {
AddZipArg(&n, &args, arg);
AddZipArg(&n, &args, Decode(arg));
}
start = 0;
}
+7 -6
View File
@@ -48,6 +48,8 @@
* @fileoverview Zip File Disassembler.
*/
uint8_t *map;
static __wur char *FormatDosDate(uint16_t dosdate) {
return xasprintf("%04u-%02u-%02u", ((dosdate >> 9) & 0b1111111) + 1980,
(dosdate >> 5) & 0b1111, dosdate & 0b11111);
@@ -60,9 +62,9 @@ static __wur char *FormatDosTime(uint16_t dostime) {
void AdvancePosition(uint8_t *map, size_t *pos, size_t off) {
if (off > *pos) {
printf("\n/\t<%s>\n", "LIMBO");
disassemblehex(&map[*pos], off - *pos, stdout);
printf("/\t</%s>\n", "LIMBO");
/* printf("\n/\t<%s>\n", "LIMBO"); */
/* disassemblehex(&map[*pos], off - *pos, stdout); */
/* printf("/\t</%s>\n", "LIMBO"); */
}
*pos = off;
}
@@ -288,8 +290,8 @@ void ShowLocalFileHeader(uint8_t *lf, uint16_t idx) {
}
void ShowCentralFileHeader(uint8_t *cf) {
printf("\n/\t%s (%zu %s)\n", "central directory file header",
ZIP_CFILE_HDRSIZE(cf), "bytes");
printf("\n/\t%s (%zu %s @ %#lx)\n", "central directory file header",
ZIP_CFILE_HDRSIZE(cf), "bytes", cf - map);
show(".ascii", format(b1, "%`'.*s", 4, cf), "magic");
show(".byte", _gc(xasprintf("%d", ZIP_CFILE_VERSIONMADE(cf))),
"zip version made");
@@ -485,7 +487,6 @@ void DisassembleZip(const char *path, uint8_t *p, size_t n) {
int main(int argc, char *argv[]) {
int fd;
uint8_t *map;
struct stat st;
ShowCrashReports();
CHECK_EQ(2, argc);
+38 -15
View File
@@ -234,6 +234,12 @@ static int dbvm_isopen(lua_State *L) {
return 1;
}
static int dbvm_readonly(lua_State *L) {
sdb_vm *svm = lsqlite_checkvm(L, 1);
lua_pushboolean(L, sqlite3_stmt_readonly(svm->vm));
return 1;
}
static int dbvm_tostring(lua_State *L) {
char buff[40];
sdb_vm *svm = lsqlite_getvm(L, 1);
@@ -908,6 +914,21 @@ static int pusherr(lua_State *L, int rc) {
return 2;
}
static int pusherrstr(lua_State *L, char *str) {
lua_pushnil(L);
lua_pushstring(L, str);
return 2;
}
static int db_readonly(lua_State *L) {
sdb *db = lsqlite_checkdb(L, 1);
const char *zDb = luaL_optstring(L, 2, "main");
int res = sqlite3_db_readonly(db->db, zDb);
if (res == -1) return pusherrstr(L, "unknown (not attached) database name");
lua_pushboolean(L, res);
return 1;
}
static int db_wal_checkpoint(lua_State *L) {
sdb *db = lsqlite_checkdb(L, 1);
int eMode = luaL_optinteger(L, 2, SQLITE_CHECKPOINT_PASSIVE);
@@ -1748,16 +1769,15 @@ static int db_gc(lua_State *L) {
return 0;
}
#ifdef SQLITE_ENABLE_DESERIALIZE
static int db_serialize(lua_State *L) {
sdb *db = lsqlite_getdb(L, 1);
sdb *db = lsqlite_checkdb(L, 1);
sqlite_int64 size = 0;
if (db->db == NULL) /* ignore closed databases */
return 0;
char *buffer = (char *)sqlite3_serialize(db->db, "main", &size, 0);
if (buffer == NULL) /* ignore failed database serialization */
return 0;
if (buffer == NULL)
return pusherrstr(L, "failed to serialize");
lua_pushlstring(L, buffer, size);
free(buffer);
@@ -1765,15 +1785,12 @@ static int db_serialize(lua_State *L) {
}
static int db_deserialize(lua_State *L) {
sdb *db = lsqlite_getdb(L, 1);
sdb *db = lsqlite_checkdb(L, 1);
size_t size = 0;
if (db->db == NULL) /* ignore closed databases */
return 0;
const char *buffer = luaL_checklstring(L, 2, &size);
if (buffer == NULL || size == 0) /* ignore empty database content */
return 0;
if (buffer == NULL || size == 0)
return pusherrstr(L, "failed to deserialize");
const char *sqlbuf = memcpy(sqlite3_malloc(size), buffer, size);
sqlite3_deserialize(db->db, "main", (void *)sqlbuf, size, size,
@@ -1781,6 +1798,8 @@ static int db_deserialize(lua_State *L) {
return 0;
}
#endif
#ifdef SQLITE_ENABLE_SESSION
/*
@@ -2602,6 +2621,7 @@ static const struct {
static const luaL_Reg dblib[] = {
{"isopen", db_isopen },
{"readonly", db_readonly },
{"last_insert_rowid", db_last_insert_rowid },
{"changes", db_changes },
{"total_changes", db_total_changes },
@@ -2634,9 +2654,6 @@ static const luaL_Reg dblib[] = {
{"close", db_close },
{"close_vm", db_close_vm },
{"serialize", db_serialize },
{"deserialize", db_deserialize },
#ifdef SQLITE_ENABLE_SESSION
{"create_session", db_create_session },
{"create_rebaser", db_create_rebaser },
@@ -2646,6 +2663,11 @@ static const luaL_Reg dblib[] = {
{"iterate_changeset", db_iterate_changeset },
#endif
#ifdef SQLITE_ENABLE_DESERIALIZE
{"serialize", db_serialize },
{"deserialize", db_deserialize },
#endif
{"__tostring", db_tostring },
{"__gc", db_gc },
@@ -2654,6 +2676,7 @@ static const luaL_Reg dblib[] = {
static const luaL_Reg vmlib[] = {
{"isopen", dbvm_isopen },
{"readonly", dbvm_readonly },
{"step", dbvm_step },
{"reset", dbvm_reset },
+116
View File
@@ -0,0 +1,116 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
╞══════════════════════════════════════════════════════════════════════════════╡
│ Copyright 2023 Justine Alexandra Roberts Tunney │
│ │
│ Permission to use, copy, modify, and/or distribute this software for │
│ any purpose with or without fee is hereby granted, provided that the │
│ above copyright notice and this permission notice appear in all copies. │
│ │
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/calls.h"
#include "libc/fmt/itoa.h"
#include "libc/runtime/runtime.h"
#include "libc/sock/ifaddrs.h"
#include "libc/sock/sock.h"
#include "libc/sock/struct/sockaddr.h"
#include "libc/sock/struct/sockaddr6.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/af.h"
#include "libc/sysv/consts/iff.h"
/* example output:
eth0
addr: 10.10.10.237
netmask: 255.255.255.0
broadcast: 255.255.255.0
flags: IFF_UP IFF_BROADCAST IFF_MULTICAST IFF_RUNNING
lo
addr: 127.0.0.1
netmask: 255.0.0.0
flags: IFF_UP IFF_LOOPBACK IFF_RUNNING */
const char *sockaddr2str(const struct sockaddr *sa, char *buf, size_t size) {
if (sa->sa_family == AF_INET) {
return inet_ntop(AF_INET, &(((const struct sockaddr_in *)sa)->sin_addr),
buf, size);
} else if (sa->sa_family == AF_INET6) {
return inet_ntop(AF_INET6, &(((const struct sockaddr_in6 *)sa)->sin6_addr),
buf, size);
} else {
return 0;
}
}
int main(int argc, char *argv[]) {
// get network interface list
struct ifaddrs *ifaddrs;
if (getifaddrs(&ifaddrs)) {
perror("getifaddrs");
exit(1);
}
// print network interface list
for (struct ifaddrs *ifa = ifaddrs; ifa; ifa = ifa->ifa_next) {
tinyprint(1, ifa->ifa_name, "\n", NULL);
char buf[128];
if (sockaddr2str(ifa->ifa_addr, buf, sizeof(buf))) {
tinyprint(1, "addr: ", buf, "\n", NULL);
}
if (sockaddr2str(ifa->ifa_netmask, buf, sizeof(buf))) {
tinyprint(1, "netmask: ", buf, "\n", NULL);
}
if ((ifa->ifa_flags & IFF_BROADCAST) &&
sockaddr2str(ifa->ifa_netmask, buf, sizeof(buf))) {
tinyprint(1, "broadcast: ", buf, "\n", NULL);
} else if ((ifa->ifa_flags & IFF_POINTOPOINT) &&
sockaddr2str(ifa->ifa_dstaddr, buf, sizeof(buf))) {
tinyprint(1, "dstaddr: ", buf, "\n", NULL);
}
tinyprint(1, "flags:", NULL);
if (ifa->ifa_flags & IFF_UP) {
tinyprint(1, " IFF_UP", NULL);
}
if (ifa->ifa_flags & IFF_DEBUG) {
tinyprint(1, " IFF_DEBUG", NULL);
}
if (ifa->ifa_flags & IFF_LOOPBACK) {
tinyprint(1, " IFF_LOOPBACK", NULL);
}
if (ifa->ifa_flags & IFF_MULTICAST) {
tinyprint(1, " IFF_MULTICAST", NULL);
}
if (ifa->ifa_flags & IFF_ALLMULTI) {
tinyprint(1, " IFF_ALLMULTI", NULL);
}
if (ifa->ifa_flags & IFF_NOARP) {
tinyprint(1, " IFF_NOARP", NULL);
}
if (ifa->ifa_flags & IFF_PROMISC) {
tinyprint(1, " IFF_PROMISC", NULL);
}
tinyprint(1, "\n", NULL);
tinyprint(1, "\n", NULL);
}
// perform cleanup
freeifaddrs(ifaddrs);
// report success
exit(0);
}