mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-02-07 06:53:33 +00:00
Add torture test for zipos file descriptors
This change hardens the code for opening /zip/ files using the system call interface. Thread safety and signal safety has been improved for file descriptors in general. We now document fixed addresses that are needed for low level allocations.
This commit is contained in:
parent
579080cd4c
commit
e466dd0553
44 changed files with 2981 additions and 307 deletions
|
@ -16,6 +16,7 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/bits/weaken.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
|
@ -39,11 +40,9 @@
|
|||
* that doesn't mean the error should be ignored.
|
||||
*
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
* @error EINTR means a signal was received while closing (possibly
|
||||
* because linger is enabled) in which case close() does not need to
|
||||
* be called again, since the fd will close in the background, and
|
||||
* chances are that on linux, the fd is already closed, even if the
|
||||
* underlying resource isn't closed yet
|
||||
* @error EINTR means a signal was received while closing in which case
|
||||
* close() does not need to be called again, since the fd will close
|
||||
* in the background
|
||||
* @asyncsignalsafe
|
||||
* @vforksafe
|
||||
*/
|
||||
|
@ -54,6 +53,11 @@ int close(int fd) {
|
|||
} else if (fd < 0) {
|
||||
rc = einval();
|
||||
} else {
|
||||
// for performance reasons we want to avoid holding __fds_lock()
|
||||
// while sys_close() is happening. this leaves the kernel / libc
|
||||
// having a temporarily inconsistent state. routines that obtain
|
||||
// file descriptors the way __zipos_open() does need to retry if
|
||||
// there's indication this race condition happened.
|
||||
if (__isfdkind(fd, kFdZip)) {
|
||||
rc = weaken(__zipos_close)(fd);
|
||||
} else {
|
||||
|
@ -71,8 +75,7 @@ int close(int fd) {
|
|||
__isfdkind(fd, kFdProcess)) { //
|
||||
rc = sys_close_nt(g_fds.p + fd);
|
||||
} else {
|
||||
STRACE("close(%d) unknown kind", fd);
|
||||
rc = ebadf();
|
||||
rc = eio();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,9 +28,10 @@
|
|||
* @param mode is an octal user/group/other permission signifier, that's
|
||||
* ignored if O_CREAT or O_TMPFILE weren't passed
|
||||
* @return number needing close(), or -1 w/ errno
|
||||
* @asyncsignalsafe
|
||||
* @asyncsignalsafe (zip files may have issues)
|
||||
* @vforksafe (raises error if zip file)
|
||||
* @restartable
|
||||
* @vforksafe
|
||||
* @threadsafe
|
||||
*/
|
||||
int open(const char *file, int flags, ...) {
|
||||
va_list va;
|
||||
|
|
|
@ -45,8 +45,9 @@
|
|||
* @param mode is an octal user/group/other permission signifier, that's
|
||||
* ignored if O_CREAT or O_TMPFILE weren't passed
|
||||
* @return number needing close(), or -1 w/ errno
|
||||
* @asyncsignalsafe
|
||||
* @vforksafe
|
||||
* @asyncsignalsafe (zip files may have issues)
|
||||
* @vforksafe (raises error if zip file)
|
||||
* @threadsafe
|
||||
*/
|
||||
int openat(int dirfd, const char *file, int flags, ...) {
|
||||
int rc;
|
||||
|
|
|
@ -22,51 +22,65 @@
|
|||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/cmpxchg.h"
|
||||
#include "libc/intrin/spinlock.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/runtime/directmap.internal.h"
|
||||
#include "libc/runtime/memtrack.internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/map.h"
|
||||
#include "libc/sysv/consts/prot.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
// XXX: until we can add read locks to all the code that uses g_fds.p
|
||||
// (right now we only have write locks) we need to keep old copies
|
||||
// of g_fds.p around after it's been extended, so that threads
|
||||
// which are using an fd they de facto own can continue reading
|
||||
static void FreeOldFdsArray(void *p) {
|
||||
weaken(free)(p);
|
||||
}
|
||||
static volatile size_t mapsize;
|
||||
|
||||
/**
|
||||
* Grows file descriptor array memory if needed.
|
||||
*
|
||||
* @see libc/runtime/memtrack64.txt
|
||||
* @see libc/runtime/memtrack32.txt
|
||||
* @asyncsignalsafe
|
||||
*/
|
||||
int __ensurefds_unlocked(int fd) {
|
||||
size_t n1, n2;
|
||||
struct Fd *p1, *p2;
|
||||
uint64_t addr;
|
||||
int prot, flags;
|
||||
size_t size, chunk;
|
||||
struct DirectMap dm;
|
||||
if (fd < g_fds.n) return fd;
|
||||
STRACE("__ensurefds(%d) extending", fd);
|
||||
if (!weaken(malloc)) return emfile();
|
||||
p1 = g_fds.p;
|
||||
n1 = g_fds.n;
|
||||
if (p1 == g_fds.__init_p) {
|
||||
if (!(p2 = weaken(malloc)(sizeof(g_fds.__init_p)))) return -1;
|
||||
memcpy(p2, p1, sizeof(g_fds.__init_p));
|
||||
g_fds.p = p1 = p2;
|
||||
size = mapsize;
|
||||
chunk = FRAMESIZE;
|
||||
if (IsAsan()) chunk *= 8;
|
||||
addr = kMemtrackFdsStart + size;
|
||||
prot = PROT_READ | PROT_WRITE;
|
||||
flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED;
|
||||
dm = sys_mmap((char *)addr, chunk, prot, flags, -1, 0);
|
||||
TrackMemoryInterval(&_mmi, addr >> 16, (addr + chunk - 1) >> 16, dm.maphandle,
|
||||
prot, flags, false, false, 0, chunk);
|
||||
if (IsAsan()) {
|
||||
addr = (addr >> 3) + 0x7fff8000;
|
||||
dm = sys_mmap((char *)addr, FRAMESIZE, prot, flags, -1, 0);
|
||||
TrackMemoryInterval(&_mmi, addr >> 16, addr >> 16, dm.maphandle, prot,
|
||||
flags, false, false, 0, FRAMESIZE);
|
||||
}
|
||||
n2 = n1;
|
||||
while (n2 <= fd) n2 *= 2;
|
||||
if (!(p2 = weaken(malloc)(n2 * sizeof(*p1)))) return -1;
|
||||
__cxa_atexit(FreeOldFdsArray, p1, 0);
|
||||
memcpy(p2, p1, n1 * sizeof(*p1));
|
||||
bzero(p2 + n1, (n2 - n1) * sizeof(*p1));
|
||||
g_fds.p = p2;
|
||||
g_fds.n = n2;
|
||||
if (!size) {
|
||||
g_fds.p = memcpy((char *)kMemtrackFdsStart, g_fds.__init_p,
|
||||
sizeof(g_fds.__init_p));
|
||||
}
|
||||
g_fds.n = (size + chunk) / sizeof(*g_fds.p);
|
||||
mapsize = size + chunk;
|
||||
return fd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Grows file descriptor array memory if needed.
|
||||
* @asyncsignalsafe
|
||||
* @threadsafe
|
||||
*/
|
||||
int __ensurefds(int fd) {
|
||||
__fds_lock();
|
||||
|
@ -77,22 +91,29 @@ int __ensurefds(int fd) {
|
|||
|
||||
/**
|
||||
* Finds open file descriptor slot.
|
||||
* @asyncsignalsafe
|
||||
*/
|
||||
int __reservefd_unlocked(int start) {
|
||||
int fd;
|
||||
for (fd = MAX(start, g_fds.f); fd < g_fds.n; ++fd) {
|
||||
if (!g_fds.p[fd].kind) {
|
||||
break;
|
||||
for (;;) {
|
||||
for (fd = MAX(start, g_fds.f); fd < g_fds.n; ++fd) {
|
||||
if (!g_fds.p[fd].kind) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
fd = __ensurefds_unlocked(fd);
|
||||
bzero(g_fds.p + fd, sizeof(*g_fds.p));
|
||||
if (_cmpxchg(&g_fds.p[fd].kind, kFdEmpty, kFdReserved)) {
|
||||
_cmpxchg(&g_fds.f, fd, fd + 1);
|
||||
return fd;
|
||||
}
|
||||
}
|
||||
fd = __ensurefds_unlocked(fd);
|
||||
bzero(g_fds.p + fd, sizeof(*g_fds.p));
|
||||
g_fds.p[fd].kind = kFdReserved;
|
||||
return fd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds open file descriptor slot.
|
||||
* @asyncsignalsafe
|
||||
* @threadsafe
|
||||
*/
|
||||
int __reservefd(int start) {
|
||||
int fd;
|
||||
|
@ -101,37 +122,3 @@ int __reservefd(int start) {
|
|||
__fds_unlock();
|
||||
return fd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes non-stdio file descriptors to free dynamic memory.
|
||||
*/
|
||||
static void FreeFds(void) {
|
||||
int i, keep = 3;
|
||||
STRACE("FreeFds()");
|
||||
__fds_lock();
|
||||
for (i = keep; i < g_fds.n; ++i) {
|
||||
if (g_fds.p[i].kind) {
|
||||
__fds_unlock();
|
||||
close(i);
|
||||
__fds_lock();
|
||||
}
|
||||
}
|
||||
if (g_fds.p != g_fds.__init_p) {
|
||||
bzero(g_fds.__init_p, sizeof(g_fds.__init_p));
|
||||
memcpy(g_fds.__init_p, g_fds.p, sizeof(*g_fds.p) * keep);
|
||||
if (weaken(free)) {
|
||||
weaken(free)(g_fds.p);
|
||||
}
|
||||
g_fds.p = g_fds.__init_p;
|
||||
g_fds.n = ARRAYLEN(g_fds.__init_p);
|
||||
}
|
||||
__fds_unlock();
|
||||
}
|
||||
|
||||
static textstartup void FreeFdsInit(void) {
|
||||
atexit(FreeFds);
|
||||
}
|
||||
|
||||
const void *const FreeFdsCtor[] initarray = {
|
||||
FreeFdsInit,
|
||||
};
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
/**
|
||||
* Converts unsigned 64-bit integer to string w/ commas.
|
||||
*
|
||||
* @param p needs at least 21 bytes
|
||||
* @param p needs at least 27 bytes
|
||||
* @return pointer to nul byte
|
||||
*/
|
||||
dontinline char *FormatUint64Thousands(char p[static 27], uint64_t x) {
|
||||
|
@ -44,7 +44,7 @@ dontinline char *FormatUint64Thousands(char p[static 27], uint64_t x) {
|
|||
/**
|
||||
* Converts 64-bit integer to string w/ commas.
|
||||
*
|
||||
* @param p needs at least 21 bytes
|
||||
* @param p needs at least 27 bytes
|
||||
* @return pointer to nul byte
|
||||
*/
|
||||
char *FormatInt64Thousands(char p[static 27], int64_t x) {
|
||||
|
|
54
libc/fmt/formatmemorysize.c
Normal file
54
libc/fmt/formatmemorysize.c
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ 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/fmt/itoa.h"
|
||||
#include "libc/macros.internal.h"
|
||||
|
||||
static const struct {
|
||||
char suffix;
|
||||
uint64_t size;
|
||||
} kUnits[] = {
|
||||
{'e', 1024ULL * 1024 * 1024 * 1024 * 1024 * 1024},
|
||||
{'p', 1024ULL * 1024 * 1024 * 1024 * 1024},
|
||||
{'t', 1024ULL * 1024 * 1024 * 1024},
|
||||
{'g', 1024ULL * 1024 * 1024},
|
||||
{'m', 1024ULL * 1024},
|
||||
{'k', 1024ULL},
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents size of memory readably.
|
||||
*
|
||||
* @param p is output buffer
|
||||
* @return pointer to nul byte
|
||||
*/
|
||||
char *FormatMemorySize(char *p, uint64_t x) {
|
||||
int i, suffix;
|
||||
for (suffix = i = 0; i < ARRAYLEN(kUnits); ++i) {
|
||||
if (x >= kUnits[i].size * 9) {
|
||||
x = (x + kUnits[i].size / 2) / kUnits[i].size;
|
||||
suffix = kUnits[i].suffix;
|
||||
break;
|
||||
}
|
||||
}
|
||||
p = FormatUint64(p, x);
|
||||
if (suffix) *p++ = suffix;
|
||||
*p++ = 'b';
|
||||
*p = 0;
|
||||
return p;
|
||||
}
|
|
@ -21,6 +21,7 @@ char *FormatFlex64(char[hasatleast 24], int64_t, char);
|
|||
size_t uint64toarray_radix16(uint64_t, char[hasatleast 17]);
|
||||
size_t uint64toarray_fixed16(uint64_t, char[hasatleast 17], uint8_t);
|
||||
size_t uint64toarray_radix8(uint64_t, char[hasatleast 24]);
|
||||
char *FormatMemorySize(char *, uint64_t);
|
||||
|
||||
#ifndef __STRICT_ANSI__
|
||||
size_t int128toarray_radix10(int128_t, char *);
|
||||
|
|
|
@ -1210,38 +1210,26 @@ void __asan_unregister_globals(struct AsanGlobal g[], int n) {
|
|||
}
|
||||
}
|
||||
|
||||
void __asan_evil(uint8_t *addr, int size, const char *s1, const char *s2) {
|
||||
void __asan_evil(uint8_t *addr, int size, const char *s) {
|
||||
struct AsanTrace tr;
|
||||
__asan_rawtrace(&tr, __builtin_frame_address(0));
|
||||
kprintf(
|
||||
"WARNING: ASAN %s %s bad %d byte %s at %x bt %x %x %x\n",
|
||||
__asan_noreentry == gettid() ? "error during" : "multi-threaded crash",
|
||||
s1, size, s2, addr, tr.p[0], tr.p[1], tr.p[2], tr.p[3]);
|
||||
kprintf("WARNING: ASAN bad %d byte %s at %x bt %x %x %x\n", size, s, addr,
|
||||
tr.p[0], tr.p[1], tr.p[2], tr.p[3]);
|
||||
}
|
||||
|
||||
void __asan_report_load(uint8_t *addr, int size) {
|
||||
if (_lockcmpxchg(&__asan_noreentry, 0, gettid())) {
|
||||
if (!__vforked) {
|
||||
__asan_report_memory_fault(addr, size, "load")();
|
||||
__asan_unreachable();
|
||||
} else {
|
||||
__asan_evil(addr, size, "vfork()", "load");
|
||||
}
|
||||
} else {
|
||||
__asan_evil(addr, size, "ASAN Reporting", "load");
|
||||
__asan_evil(addr, size, "load");
|
||||
if (!__vforked && _lockcmpxchg(&__asan_noreentry, 0, 1)) {
|
||||
__asan_report_memory_fault(addr, size, "load")();
|
||||
__asan_unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
void __asan_report_store(uint8_t *addr, int size) {
|
||||
if (_lockcmpxchg(&__asan_noreentry, 0, gettid())) {
|
||||
if (!__vforked) {
|
||||
__asan_report_memory_fault(addr, size, "store")();
|
||||
__asan_unreachable();
|
||||
} else {
|
||||
__asan_evil(addr, size, "vfork()", "store");
|
||||
}
|
||||
} else {
|
||||
__asan_evil(addr, size, "ASAN reporting", "store");
|
||||
__asan_evil(addr, size, "store");
|
||||
if (!__vforked && _lockcmpxchg(&__asan_noreentry, 0, 1)) {
|
||||
__asan_report_memory_fault(addr, size, "store")();
|
||||
__asan_unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@ textstartup void InitializeFileDescriptors(void) {
|
|||
struct Fds *fds;
|
||||
fds = VEIL("r", &g_fds);
|
||||
pushmov(&fds->n, ARRAYLEN(fds->__init_p));
|
||||
fds->f = 3;
|
||||
fds->p = fds->__init_p;
|
||||
if (IsMetal()) {
|
||||
pushmov(&fds->f, 3ull);
|
||||
|
|
|
@ -21,9 +21,8 @@
|
|||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/pthread.h"
|
||||
#include "libc/nexgen32e/gettls.h"
|
||||
#include "libc/linux/futex.h"
|
||||
#include "libc/nexgen32e/threaded.h"
|
||||
#include "libc/sysv/consts/futex.h"
|
||||
|
||||
/**
|
||||
* Locks mutex.
|
||||
|
@ -43,16 +42,15 @@ int pthread_mutex_lock(pthread_mutex_t *mutex) {
|
|||
return EDEADLK;
|
||||
}
|
||||
}
|
||||
atomic_fetch_add(&mutex->waits, +1);
|
||||
if (!IsLinux() ||
|
||||
sys_futex((void *)&mutex->owner, FUTEX_WAIT, owner, 0, 0)) {
|
||||
atomic_fetch_add(&mutex->waits, 1);
|
||||
if (!IsLinux() || LinuxFutexWait((void *)&mutex->owner, owner, 0)) {
|
||||
if (++tries & 7) {
|
||||
__builtin_ia32_pause();
|
||||
} else {
|
||||
sched_yield();
|
||||
}
|
||||
}
|
||||
atomic_fetch_add(&mutex->waits, -1);
|
||||
atomic_fetch_sub(&mutex->waits, 1);
|
||||
}
|
||||
++mutex->reent;
|
||||
return 0;
|
||||
|
|
|
@ -21,8 +21,8 @@
|
|||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/pthread.h"
|
||||
#include "libc/linux/futex.h"
|
||||
#include "libc/nexgen32e/threaded.h"
|
||||
#include "libc/sysv/consts/futex.h"
|
||||
|
||||
/**
|
||||
* Releases mutex.
|
||||
|
@ -38,7 +38,7 @@ int pthread_mutex_unlock(pthread_mutex_t *mutex) {
|
|||
atomic_store_explicit(&mutex->owner, 0, memory_order_relaxed);
|
||||
if (IsLinux() &&
|
||||
atomic_load_explicit(&mutex->waits, memory_order_acquire)) {
|
||||
sys_futex((void *)&mutex->owner, FUTEX_WAKE, 1, 0, 0);
|
||||
LinuxFutexWake(&mutex->owner, 1);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
|
|
@ -18,10 +18,11 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
void __releasefd_unlocked(int fd) {
|
||||
if (0 <= fd && fd < g_fds.n) {
|
||||
g_fds.p[fd].kind = 0;
|
||||
bzero(g_fds.p + fd, sizeof(*g_fds.p));
|
||||
g_fds.f = MIN(fd, g_fds.f);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,18 +61,30 @@ privileged void *__initialize_tls(char tib[64]) {
|
|||
* Installs thread information block on main process.
|
||||
*
|
||||
* For example, to set up TLS correctly for the main thread, without
|
||||
* creating any threads using `clone` (which does this automatically),
|
||||
* it is sufficient to say:
|
||||
* creating any threads, then it's sufficient to say:
|
||||
*
|
||||
* __attribute__((__constructor__)) static void InitTls(void) {
|
||||
* static char tls[64];
|
||||
* __initialize_tls(tls);
|
||||
* __threaded = *(int *)(tls + 0x38) = gettid();
|
||||
* *(int *)(tls + 0x38) = gettid();
|
||||
* *(int *)(tls + 0x3c) = __errno;
|
||||
* __install_tls(tls);
|
||||
* }
|
||||
*
|
||||
* Since that'll ensure it happens exactly once.
|
||||
* We use a constructor here to make sure it only happens once. Please
|
||||
* note that calling `clone` will do this automatically.
|
||||
*
|
||||
* Installing TLS causes the `__tls_enabled` variable to be set. This
|
||||
* causes C library features such as `errno` and `gettid()` to use TLS.
|
||||
* This can help things like recursive mutexes go significantly faster.
|
||||
*
|
||||
* To access your TLS storage, you can call `__get_tls()` or
|
||||
* __get_tls_inline()` which return the address of the `tib`.
|
||||
*
|
||||
* @param tib is your thread information block, which must have at least
|
||||
* 64 bytes on the righthand side of the tib pointer since those are
|
||||
* the values your C library reserves for itself. memory on the left
|
||||
* side of the pointer is reserved by the linker for _Thread_local.
|
||||
*/
|
||||
privileged void __install_tls(char tib[64]) {
|
||||
int ax, dx;
|
||||
|
|
27
libc/linux/futex.h
Normal file
27
libc/linux/futex.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_LINUX_FUTEX_H_
|
||||
#define COSMOPOLITAN_LIBC_LINUX_FUTEX_H_
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
|
||||
forceinline int LinuxFutexWait(void *addr, int expect,
|
||||
struct timespec *timeout) {
|
||||
int ax;
|
||||
register void *r10 asm("r10") = timeout;
|
||||
asm volatile("syscall"
|
||||
: "=a"(ax)
|
||||
: "0"(202), "D"(addr), "S"(0), "d"(expect), "r"(r10)
|
||||
: "rcx", "r11", "memory");
|
||||
return ax;
|
||||
}
|
||||
|
||||
forceinline int LinuxFutexWake(void *addr, int count) {
|
||||
int ax;
|
||||
asm volatile("syscall"
|
||||
: "=a"(ax)
|
||||
: "0"(202), "D"(addr), "S"(1), "d"(count)
|
||||
: "rcx", "r11", "memory");
|
||||
return ax;
|
||||
}
|
||||
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_LIBC_LINUX_FUTEX_H_ */
|
|
@ -21,7 +21,9 @@
|
|||
#include "libc/bits/safemacros.internal.h"
|
||||
#include "libc/bits/weaken.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/calls/syscall_support-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
|
@ -82,6 +84,7 @@ static int PrintBacktraceUsingAddr2line(int fd, const struct StackFrame *bp) {
|
|||
}
|
||||
|
||||
// backtrace_test.com failing on windows for some reason via runitd
|
||||
// don't want to pull in the high-level syscalls here anyway
|
||||
if (IsWindows()) {
|
||||
return -1;
|
||||
}
|
||||
|
@ -118,17 +121,17 @@ static int PrintBacktraceUsingAddr2line(int fd, const struct StackFrame *bp) {
|
|||
j += uint64toarray_radix16(addr - 1, buf + j) + 1;
|
||||
}
|
||||
argv[i++] = NULL;
|
||||
pipe(pipefds);
|
||||
if (sys_pipe(pipefds) == -1) return -1;
|
||||
if (!(pid = vfork())) {
|
||||
dup2(pipefds[1], 1);
|
||||
if (pipefds[0] != 1) close(pipefds[0]);
|
||||
if (pipefds[1] != 1) close(pipefds[1]);
|
||||
execve(addr2line, argv, environ);
|
||||
_exit(127);
|
||||
sys_dup2(pipefds[1], 1);
|
||||
if (pipefds[0] != 1) sys_close(pipefds[0]);
|
||||
if (pipefds[1] != 1) sys_close(pipefds[1]);
|
||||
sys_execve(addr2line, argv, environ);
|
||||
_Exit(127);
|
||||
}
|
||||
close(pipefds[1]);
|
||||
sys_close(pipefds[1]);
|
||||
for (;;) {
|
||||
got = read(pipefds[0], buf, kBacktraceBufSize);
|
||||
got = sys_read(pipefds[0], buf, kBacktraceBufSize);
|
||||
if (!got) break;
|
||||
if (got == -1 && errno == EINTR) {
|
||||
errno = 0;
|
||||
|
@ -161,8 +164,8 @@ static int PrintBacktraceUsingAddr2line(int fd, const struct StackFrame *bp) {
|
|||
}
|
||||
}
|
||||
}
|
||||
close(pipefds[0]);
|
||||
while (waitpid(pid, &ws, 0) == -1) {
|
||||
sys_close(pipefds[0]);
|
||||
while (sys_wait4(pid, &ws, 0, 0) == -1) {
|
||||
if (errno == EINTR) continue;
|
||||
return -1;
|
||||
}
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
#include "libc/bits/atomic.h"
|
||||
#include "libc/bits/weaken.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/spinlock.h"
|
||||
#include "libc/log/backtrace.internal.h"
|
||||
#include "libc/log/log.h"
|
||||
#include "libc/macros.internal.h"
|
||||
|
@ -71,14 +70,14 @@ static struct Memlog {
|
|||
long usage;
|
||||
} __memlog;
|
||||
|
||||
_Alignas(64) static int __memlog_lock_obj;
|
||||
static pthread_mutex_t __memlog_lock_obj;
|
||||
|
||||
static void __memlog_lock(void) {
|
||||
_spinlock(&__memlog_lock_obj);
|
||||
pthread_mutex_lock(&__memlog_lock_obj);
|
||||
}
|
||||
|
||||
static void __memlog_unlock(void) {
|
||||
_spunlock(&__memlog_lock_obj);
|
||||
pthread_mutex_unlock(&__memlog_lock_obj);
|
||||
}
|
||||
|
||||
static long __memlog_size(void *p) {
|
||||
|
|
|
@ -263,8 +263,7 @@ static wontreturn relegated noinstrument void __minicrash(int sig,
|
|||
*
|
||||
* This function never returns, except for traps w/ human supervision.
|
||||
*/
|
||||
relegated noinstrument void __oncrash(int sig, struct siginfo *si,
|
||||
ucontext_t *ctx) {
|
||||
relegated void __oncrash(int sig, struct siginfo *si, ucontext_t *ctx) {
|
||||
intptr_t rip;
|
||||
int gdbpid, err;
|
||||
static bool noreentry, notpossible;
|
||||
|
|
|
@ -30,6 +30,9 @@
|
|||
* called, then it'll return the same sequence each time your program
|
||||
* runs. Faster and more modern alternatives exist to this function.
|
||||
*
|
||||
* This function is not thread safe in the sense that multiple threads
|
||||
* might simultaneously generate the same random values.
|
||||
*
|
||||
* @note this function does well on bigcrush and practrand
|
||||
* @note this function is not intended for cryptography
|
||||
* @see lemur64(), rand64(), rdrand()
|
||||
|
|
|
@ -16,42 +16,44 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/intrin/pthread.h"
|
||||
#include "libc/intrin/spinlock.h"
|
||||
#include "libc/nexgen32e/rdtsc.h"
|
||||
#include "libc/nexgen32e/threaded.h"
|
||||
#include "libc/runtime/internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/auxv.h"
|
||||
|
||||
static int thepid;
|
||||
static uint128_t thepool;
|
||||
_Alignas(64) static int rand64_lock;
|
||||
static struct {
|
||||
int thepid;
|
||||
uint128_t thepool;
|
||||
pthread_mutex_t lock;
|
||||
} g_rand64;
|
||||
|
||||
/**
|
||||
* Returns nondeterministic random data.
|
||||
*
|
||||
* This function is similar to lemur64() except that it's intended to be
|
||||
* unpredictable. This PRNG automatically seeds itself on startup using
|
||||
* a much stronger and faster random source than `srand(time(0))`. This
|
||||
* function will automatically reseed itself when new processes and
|
||||
* threads are spawned. This function is thread safe in the sense that a
|
||||
* race condition can't happen where two threads return the same result.
|
||||
* This function is similar to lemur64() except that it doesn't produce
|
||||
* the same sequences of numbers each time your program is run. This is
|
||||
* the case even across forks and threads, whose sequences will differ.
|
||||
*
|
||||
* @see rdseed(), rdrand(), rand(), random(), rngset()
|
||||
* @note this function takes 5 cycles (30 if `__threaded`)
|
||||
* @note this function is not intended for cryptography
|
||||
* @note this function passes bigcrush and practrand
|
||||
* @note this function takes at minimum 15 cycles
|
||||
* @asyncsignalsafe
|
||||
* @threadsafe
|
||||
* @vforksafe
|
||||
*/
|
||||
uint64_t rand64(void) {
|
||||
void *p;
|
||||
uint128_t s;
|
||||
_spinlock(&rand64_lock);
|
||||
if (__pid == thepid) {
|
||||
s = thepool; // normal path
|
||||
if (__threaded) pthread_mutex_lock(&g_rand64.lock);
|
||||
if (__pid == g_rand64.thepid) {
|
||||
s = g_rand64.thepool; // normal path
|
||||
} else {
|
||||
if (!thepid) {
|
||||
if (!g_rand64.thepid) {
|
||||
if (AT_RANDOM && (p = (void *)getauxval(AT_RANDOM))) {
|
||||
// linux / freebsd kernel supplied entropy
|
||||
memcpy(&s, p, 16);
|
||||
|
@ -61,13 +63,13 @@ uint64_t rand64(void) {
|
|||
}
|
||||
} else {
|
||||
// blend another timestamp on fork contention
|
||||
s = thepool ^ rdtsc();
|
||||
s = g_rand64.thepool ^ rdtsc();
|
||||
}
|
||||
// blend the pid on startup and fork contention
|
||||
s ^= __pid;
|
||||
thepid = __pid;
|
||||
g_rand64.thepid = __pid;
|
||||
}
|
||||
thepool = (s *= 15750249268501108917ull); // lemur64
|
||||
_spunlock(&rand64_lock);
|
||||
g_rand64.thepool = (s *= 15750249268501108917ull); // lemur64
|
||||
if (__threaded) pthread_mutex_unlock(&g_rand64.lock);
|
||||
return s >> 64;
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/thread.h"
|
||||
#include "libc/nt/thunk/msabi.h"
|
||||
#include "libc/runtime/internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/clone.h"
|
||||
|
@ -72,34 +73,6 @@ struct CloneArgs {
|
|||
void *arg;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// THREADING RUNTIME
|
||||
|
||||
static char tibdefault[64];
|
||||
extern int __threadcalls_end[];
|
||||
extern int __threadcalls_start[];
|
||||
|
||||
static privileged dontinline void FixupThreadCalls(void) {
|
||||
/*
|
||||
* _NOPL("__threadcalls", func)
|
||||
*
|
||||
* we have this
|
||||
*
|
||||
* 0f 1f 05 b1 19 00 00 nopl func(%rip)
|
||||
*
|
||||
* we're going to turn it into this
|
||||
*
|
||||
* 67 67 e8 b1 19 00 00 addr32 addr32 call func
|
||||
*/
|
||||
__morph_begin();
|
||||
for (int *p = __threadcalls_start; p < __threadcalls_end; ++p) {
|
||||
_base[*p + 0] = 0x67;
|
||||
_base[*p + 1] = 0x67;
|
||||
_base[*p + 2] = 0xe8;
|
||||
}
|
||||
__morph_end();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// THE NEW TECHNOLOGY
|
||||
|
||||
|
@ -522,12 +495,11 @@ int sys_clone_linux(int flags, char *stk, int *ptid, int *ctid, void *tls,
|
|||
* value from the thread by calling __get_tls(). There are a few
|
||||
* layout expectations imposed by your C library. Those are all
|
||||
* documented by __initialize_tls() which initializes the parts of
|
||||
* the first 64 bytes of tls memory that libc cares about. Also
|
||||
* note that if you decide to use tls once then you must use it
|
||||
* for everything, since this flag also flips a runtime state that
|
||||
* enables it for the main thread and functions such as
|
||||
* __errno_location() will begin assuming they can safely access
|
||||
* the tls segment register.
|
||||
* the first 64 bytes of tls memory that libc cares about. This
|
||||
* flag will transition the C runtime to the `__tls_enabled` state
|
||||
* automatically. If it's used for one thread, then it must be
|
||||
* used for all threads. The first time it's used, it must be used
|
||||
* from the main thread.
|
||||
* @param arg will be passed to your callback
|
||||
* @param tls may be used to set the thread local storage segment;
|
||||
* this parameter is ignored if `CLONE_SETTLS` is not set
|
||||
|
@ -539,30 +511,15 @@ int sys_clone_linux(int flags, char *stk, int *ptid, int *ctid, void *tls,
|
|||
*/
|
||||
int clone(int (*func)(void *), void *stk, size_t stksz, int flags, void *arg,
|
||||
int *ptid, void *tls, size_t tlssz, int *ctid) {
|
||||
int rc, maintid;
|
||||
int rc;
|
||||
struct CloneArgs *wt;
|
||||
|
||||
// transition program to threaded state
|
||||
if (!__threaded && (flags & CLONE_THREAD)) {
|
||||
FixupThreadCalls();
|
||||
}
|
||||
if ((flags & CLONE_SETTLS) && !__tls_enabled) {
|
||||
if (~flags & CLONE_THREAD) {
|
||||
STRACE("clone() tls w/o thread");
|
||||
return einval();
|
||||
}
|
||||
if (__threaded) {
|
||||
STRACE("clone() tls/non-tls mixed order");
|
||||
return einval();
|
||||
}
|
||||
maintid = gettid();
|
||||
__initialize_tls(tibdefault);
|
||||
*(int *)((char *)tibdefault + 0x38) = maintid;
|
||||
*(int *)((char *)tibdefault + 0x3c) = __errno;
|
||||
__install_tls(tibdefault);
|
||||
__threaded = maintid;
|
||||
} else if (flags & CLONE_THREAD) {
|
||||
__threaded = gettid();
|
||||
__enable_tls();
|
||||
}
|
||||
|
||||
if ((flags & CLONE_THREAD) && !__threaded) {
|
||||
__enable_threads();
|
||||
}
|
||||
|
||||
if (!func) {
|
||||
|
|
|
@ -26,6 +26,8 @@ extern unsigned char _tbss_end[];
|
|||
extern unsigned char _tls_size[];
|
||||
|
||||
void _init(void) hidden;
|
||||
void __enable_tls(void) hidden;
|
||||
void __enable_threads(void) hidden;
|
||||
void __restorewintty(void) hidden;
|
||||
void *__cxa_finalize(void *) hidden;
|
||||
void cosmo(int, char **, char **, long (*)[2]) hidden wontreturn;
|
||||
|
|
|
@ -26,6 +26,10 @@ COSMOPOLITAN_C_START_
|
|||
#define kFixedmapStart _kMem(0x300000000000, 0x000040000000)
|
||||
#define kFixedmapSize \
|
||||
_kMem(0x400000000000 - 0x300000000000, 0x000070000000 - 0x000040000000)
|
||||
#define kMemtrackFdsStart _kMem(0x6fe000000000, 0x80000000)
|
||||
#define kMemtrackFdsSize _kMem(0x001000000000, 0x04000000)
|
||||
#define kMemtrackZiposStart _kMem(0x6fd000000000, 0x84000000)
|
||||
#define kMemtrackZiposSize _kMem(0x001000000000, 0x20000000)
|
||||
#define _kMmi(VSPACE) \
|
||||
ROUNDUP(VSPACE / FRAMESIZE * (intptr_t)sizeof(struct MemoryInterval), \
|
||||
FRAMESIZE)
|
||||
|
|
83
libc/runtime/memtrack32.txt
Normal file
83
libc/runtime/memtrack32.txt
Normal file
|
@ -0,0 +1,83 @@
|
|||
# -*- conf -*-
|
||||
# Cosmopolitan Libc Legacy Memory Plan
|
||||
|
||||
00000000-0000001f 2048kb guard
|
||||
00000020-0000003f 2048kb loader
|
||||
00000040-000000ff 12mb image
|
||||
00000100-000003ff 48mb free
|
||||
00000400-000007ff 64mb free
|
||||
00000800-00000bff 64mb free
|
||||
00000c00-00000fff 64mb free
|
||||
00001000-000013ff 64mb automap
|
||||
00001400-000017ff 64mb automap
|
||||
00001800-00001bff 64mb automap
|
||||
00001c00-00001fff 64mb automap
|
||||
00002000-000023ff 64mb automap
|
||||
00002400-000027ff 64mb automap
|
||||
00002800-00002bff 64mb automap
|
||||
00002c00-00002fff 64mb automap
|
||||
00003000-000033ff 64mb automap
|
||||
00003400-000037ff 64mb automap
|
||||
00003800-00003bff 64mb automap
|
||||
00003c00-00003fe7 63mb automap
|
||||
00003fe4-00003ffb 1536kb memtrack
|
||||
00003ffc-00003fff 256kb free
|
||||
00004000-000043ff 64mb fixedmap
|
||||
00004400-000047ff 64mb fixedmap
|
||||
00004800-00004bff 64mb fixedmap
|
||||
00004c00-00004fff 64mb fixedmap
|
||||
00005000-000053ff 64mb fixedmap
|
||||
00005400-000057ff 64mb fixedmap
|
||||
00005800-00005bff 64mb fixedmap
|
||||
00005c00-00005fff 64mb fixedmap
|
||||
00006000-000063ff 64mb fixedmap
|
||||
00006400-000067ff 64mb fixedmap
|
||||
00006800-00006bff 64mb fixedmap
|
||||
00006c00-00006fff 64mb fixedmap
|
||||
00005000-000053ff 64mb arena
|
||||
00005400-000057ff 64mb arena
|
||||
00005800-00005bff 64mb arena
|
||||
00005c00-00005fff 64mb arena
|
||||
00006000-000063ff 64mb arena
|
||||
00006400-000067ff 64mb arena
|
||||
00006800-00006bff 64mb arena
|
||||
00006c00-00006fff 64mb arena
|
||||
00007000-000073ff 64mb arena
|
||||
00007400-000077ff 64mb arena
|
||||
00007800-00007bff 64mb arena
|
||||
00007c00-00007ffd 64mb arena
|
||||
00007ffe-00007fff 128kb free
|
||||
00008000-000083ff 64mb fds
|
||||
00008400-000087ff 64mb zipos
|
||||
00008800-00008bff 64mb zipos
|
||||
00008c00-00008fff 64mb zipos
|
||||
00009000-000093ff 64mb zipos
|
||||
00009400-000097ff 64mb zipos
|
||||
00009800-00009bff 64mb zipos
|
||||
00009c00-00009fff 64mb zipos
|
||||
0000a000-0000a3ff 64mb zipos
|
||||
0000a400-0000a7ff 64mb free
|
||||
0000a800-0000abff 64mb free
|
||||
0000ac00-0000afff 64mb free
|
||||
0000b000-0000b3ff 64mb free
|
||||
0000b400-0000b7ff 64mb free
|
||||
0000b800-0000bbff 64mb free
|
||||
0000bc00-0000bfff 64mb free
|
||||
0000c000-0000c3ff 64mb free
|
||||
0000c400-0000c7ff 64mb free
|
||||
0000c800-0000cbff 64mb free
|
||||
0000cc00-0000cfff 64mb free
|
||||
0000d000-0000d3ff 64mb free
|
||||
0000d400-0000d7ff 64mb free
|
||||
0000d800-0000dbff 64mb free
|
||||
0000dc00-0000dfff 64mb free
|
||||
0000e000-0000e3ff 64mb free
|
||||
0000e400-0000e7ff 64mb free
|
||||
0000e800-0000ebff 64mb free
|
||||
0000ec00-0000efff 64mb free
|
||||
0000f000-0000f3ff 64mb free
|
||||
0000f400-0000f7ff 64mb free
|
||||
0000f800-0000fbff 64mb free
|
||||
0000fc00-0000fffb 64mb free
|
||||
0000fffc-0000fffd 128kb winargs
|
||||
0000fffe-0000ffff 128kb stack
|
2073
libc/runtime/memtrack64.txt
Normal file
2073
libc/runtime/memtrack64.txt
Normal file
File diff suppressed because it is too large
Load diff
|
@ -100,7 +100,7 @@ extern char ape_stack_align[] __attribute__((__weak__));
|
|||
: "=r"(vAddr) \
|
||||
: "i"(ADDEND)); \
|
||||
} else { \
|
||||
vAddr = 0x10000000; \
|
||||
vAddr = 0x100000000 - GetStackSize(); \
|
||||
} \
|
||||
(void *)vAddr; \
|
||||
})
|
||||
|
|
56
libc/runtime/threadmode.c
Normal file
56
libc/runtime/threadmode.c
Normal file
|
@ -0,0 +1,56 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ 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/errno.h"
|
||||
#include "libc/nexgen32e/threaded.h"
|
||||
#include "libc/runtime/internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
|
||||
static char tibdefault[64];
|
||||
extern int __threadcalls_end[];
|
||||
extern int __threadcalls_start[];
|
||||
|
||||
void __enable_tls(void) {
|
||||
__initialize_tls(tibdefault);
|
||||
*(int *)((char *)tibdefault + 0x38) = gettid();
|
||||
*(int *)((char *)tibdefault + 0x3c) = __errno;
|
||||
__install_tls(tibdefault);
|
||||
}
|
||||
|
||||
privileged void __enable_threads(void) {
|
||||
__threaded = gettid();
|
||||
/*
|
||||
* _NOPL("__threadcalls", func)
|
||||
*
|
||||
* we have this
|
||||
*
|
||||
* 0f 1f 05 b1 19 00 00 nopl func(%rip)
|
||||
*
|
||||
* we're going to turn it into this
|
||||
*
|
||||
* 67 67 e8 b1 19 00 00 addr32 addr32 call func
|
||||
*/
|
||||
__morph_begin();
|
||||
for (int *p = __threadcalls_start; p < __threadcalls_end; ++p) {
|
||||
_base[*p + 0] = 0x67;
|
||||
_base[*p + 1] = 0x67;
|
||||
_base[*p + 2] = 0xe8;
|
||||
}
|
||||
__morph_end();
|
||||
}
|
16
libc/runtime/winargs.internal.h
Normal file
16
libc/runtime/winargs.internal.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_RUNTIME_WINARGS_INTERNAL_H_
|
||||
#define COSMOPOLITAN_LIBC_RUNTIME_WINARGS_INTERNAL_H_
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
struct WinArgs {
|
||||
char *argv[4096];
|
||||
char *envp[4092];
|
||||
intptr_t auxv[2][2];
|
||||
char argblock[ARG_MAX / 2];
|
||||
char envblock[ARG_MAX / 2];
|
||||
};
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_LIBC_RUNTIME_WINARGS_INTERNAL_H_ */
|
|
@ -55,6 +55,7 @@
|
|||
#include "libc/runtime/internal.h"
|
||||
#include "libc/runtime/memtrack.internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/runtime/winargs.internal.h"
|
||||
#include "libc/sock/internal.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/str/tpenc.h"
|
||||
|
@ -81,14 +82,6 @@ __msabi extern typeof(VirtualProtect) *const __imp_VirtualProtect;
|
|||
* TODO: How can we ensure we never overlap with KERNEL32.DLL?
|
||||
*/
|
||||
|
||||
struct WinArgs {
|
||||
char *argv[4096];
|
||||
char *envp[4092];
|
||||
intptr_t auxv[2][2];
|
||||
char argblock[ARG_MAX / 2];
|
||||
char envblock[ARG_MAX / 2];
|
||||
};
|
||||
|
||||
extern uint32_t __winmainpid;
|
||||
extern int64_t __wincrashearly;
|
||||
extern const char kConsoleHandles[3];
|
||||
|
|
|
@ -40,7 +40,6 @@ hidden struct NtWsaData kNtWsaData;
|
|||
|
||||
static textwindows void WinSockCleanup(void) {
|
||||
int i, rc;
|
||||
NTTRACE("WinSockCleanup()");
|
||||
rc = WSACleanup();
|
||||
NTTRACE("WSACleanup() → %d% lm", rc);
|
||||
}
|
||||
|
|
|
@ -16,19 +16,20 @@
|
|||
│ 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/state.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/zip.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/zipos/zipos.internal.h"
|
||||
|
||||
/**
|
||||
* Closes compressed object.
|
||||
*
|
||||
* @param fd is vetted by close()
|
||||
* @asyncsignalsafe
|
||||
* @threadsafe
|
||||
* @vforksafe
|
||||
*/
|
||||
int __zipos_close(int fd) {
|
||||
int rc;
|
||||
|
@ -40,8 +41,7 @@ int __zipos_close(int fd) {
|
|||
rc = 0; /* no system file descriptor needed on nt */
|
||||
}
|
||||
if (!__vforked) {
|
||||
free(h->freeme);
|
||||
free(h);
|
||||
__zipos_free(__zipos_get(), h);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
|
40
libc/zipos/free.c
Normal file
40
libc/zipos/free.c
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ 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/dce.h"
|
||||
#include "libc/intrin/asan.internal.h"
|
||||
#include "libc/intrin/asancodes.h"
|
||||
#include "libc/intrin/cmpxchg.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/zipos/zipos.internal.h"
|
||||
|
||||
/**
|
||||
* Frees ZipOS handle.
|
||||
* @asyncsignalsafe
|
||||
* @threadsafe
|
||||
*/
|
||||
void __zipos_free(struct Zipos *z, struct ZiposHandle *h) {
|
||||
if (IsAsan()) {
|
||||
__asan_poison((char *)h + sizeof(struct ZiposHandle),
|
||||
h->mapsize - sizeof(struct ZiposHandle), kAsanHeapFree);
|
||||
}
|
||||
__zipos_lock();
|
||||
do h->next = z->freelist;
|
||||
while (!_cmpxchg(&z->freelist, h->next, h));
|
||||
__zipos_unlock();
|
||||
}
|
|
@ -16,27 +16,15 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/bits/safemacros.internal.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/calls/struct/stat.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/cmpxchg.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/pthread.h"
|
||||
#include "libc/intrin/spinlock.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/mem/alloca.h"
|
||||
#include "libc/runtime/runtime.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/consts/sig.h"
|
||||
#include "libc/zip.h"
|
||||
#include "libc/zipos/zipos.internal.h"
|
||||
|
||||
|
@ -63,52 +51,52 @@ static void __zipos_munmap_unneeded(const uint8_t *base, const uint8_t *cdir,
|
|||
|
||||
/**
|
||||
* Returns pointer to zip central directory of current executable.
|
||||
* @asyncsignalsafe (TODO: verify this)
|
||||
* @asyncsignalsafe
|
||||
* @threadsafe
|
||||
*/
|
||||
struct Zipos *__zipos_get(void) {
|
||||
int fd;
|
||||
char *path;
|
||||
ssize_t size;
|
||||
const char *msg;
|
||||
static bool once;
|
||||
sigset_t neu, old;
|
||||
struct Zipos *res;
|
||||
const char *progpath;
|
||||
static struct Zipos zipos;
|
||||
uint8_t *map, *base, *cdir;
|
||||
static pthread_mutex_t lock;
|
||||
pthread_mutex_lock(&lock);
|
||||
if (_cmpxchg(&once, false, true)) {
|
||||
sigfillset(&neu);
|
||||
if (!once) {
|
||||
__zipos_lock();
|
||||
progpath = GetProgramExecutableName();
|
||||
if ((fd = open(progpath, O_RDONLY)) != -1) {
|
||||
if ((size = getfiledescriptorsize(fd)) != SIZE_MAX &&
|
||||
if ((size = getfiledescriptorsize(fd)) != -1ul &&
|
||||
(map = mmap(0, size, PROT_READ, MAP_SHARED, fd, 0)) != MAP_FAILED) {
|
||||
if ((base = FindEmbeddedApe(map, size))) {
|
||||
size -= base - map;
|
||||
} else {
|
||||
base = map;
|
||||
}
|
||||
if ((cdir = GetZipCdir(base, size))) {
|
||||
if ((cdir = GetZipCdir(base, size)) && _cmpxchg(&zipos.map, 0, base)) {
|
||||
__zipos_munmap_unneeded(base, cdir, map);
|
||||
zipos.map = base;
|
||||
zipos.cdir = cdir;
|
||||
STRACE("__zipos_get(%#s)", progpath);
|
||||
msg = "ok";
|
||||
} else {
|
||||
munmap(map, size);
|
||||
STRACE("__zipos_get(%#s) → eocd not found", progpath);
|
||||
msg = "eocd not found";
|
||||
}
|
||||
} else {
|
||||
msg = "map failed";
|
||||
}
|
||||
close(fd);
|
||||
} else {
|
||||
STRACE("__zipos_get(%#s) → open failed %m", progpath);
|
||||
msg = "open failed";
|
||||
}
|
||||
once = true;
|
||||
__zipos_unlock();
|
||||
STRACE("__zipos_get(%#s) → %s% m", progpath, msg);
|
||||
}
|
||||
if (zipos.cdir) {
|
||||
res = &zipos;
|
||||
} else {
|
||||
res = 0;
|
||||
}
|
||||
pthread_mutex_unlock(&lock);
|
||||
return res;
|
||||
}
|
||||
|
|
30
libc/zipos/lock.c
Normal file
30
libc/zipos/lock.c
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ 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/intrin/pthread.h"
|
||||
#include "libc/zipos/zipos.internal.h"
|
||||
|
||||
static pthread_mutex_t __zipos_lock_obj;
|
||||
|
||||
void(__zipos_lock)(void) {
|
||||
pthread_mutex_lock(&__zipos_lock_obj);
|
||||
}
|
||||
|
||||
void(__zipos_unlock)(void) {
|
||||
pthread_mutex_unlock(&__zipos_lock_obj);
|
||||
}
|
|
@ -16,24 +16,26 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "ape/relocations.h"
|
||||
#include "libc/assert.h"
|
||||
#include "libc/bits/weaken.h"
|
||||
#include "libc/bits/atomic.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/calls/struct/stat.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/asan.internal.h"
|
||||
#include "libc/intrin/cmpxchg.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/lockcmpxchg.h"
|
||||
#include "libc/intrin/spinlock.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/nexgen32e/crc32.h"
|
||||
#include "libc/runtime/directmap.internal.h"
|
||||
#include "libc/runtime/internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/runtime/memtrack.internal.h"
|
||||
#include "libc/sysv/consts/f.h"
|
||||
#include "libc/sysv/consts/fd.h"
|
||||
#include "libc/sysv/consts/map.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/prot.h"
|
||||
|
@ -41,64 +43,164 @@
|
|||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/zip.h"
|
||||
#include "libc/zipos/zipos.internal.h"
|
||||
#include "third_party/zlib/zlib.h"
|
||||
|
||||
static int __zipos_inflate(struct ZiposHandle *h, uint8_t *data, size_t size) {
|
||||
return !__inflate(h->freeme, h->size, data, size) ? 0 : eio();
|
||||
static volatile size_t maptotal;
|
||||
|
||||
static pureconst size_t __zipos_granularity(void) {
|
||||
return (IsWindows() ? FRAMESIZE : PAGESIZE) * (IsAsan() ? 8 : 1);
|
||||
}
|
||||
|
||||
static void *__zipos_mmap(size_t mapsize) {
|
||||
size_t offset;
|
||||
int prot, flags;
|
||||
struct DirectMap dm;
|
||||
uint64_t addr, addr2;
|
||||
do offset = maptotal;
|
||||
while (!_cmpxchg(&maptotal, offset, maptotal + mapsize));
|
||||
if (offset + mapsize <= kMemtrackZiposSize) {
|
||||
prot = PROT_READ | PROT_WRITE;
|
||||
flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED;
|
||||
addr = kMemtrackZiposStart + offset;
|
||||
if ((dm = sys_mmap((void *)addr, mapsize, prot, flags, -1, 0)).addr !=
|
||||
MAP_FAILED) {
|
||||
TrackMemoryInterval(&_mmi, addr >> 16, (addr + mapsize - 1) >> 16,
|
||||
dm.maphandle, prot, flags, false, false, 0, mapsize);
|
||||
if (IsAsan()) {
|
||||
addr2 = (addr >> 3) + 0x7fff8000;
|
||||
dm = sys_mmap((void *)addr2, mapsize >> 3, prot, flags, -1, 0);
|
||||
TrackMemoryInterval(&_mmi, addr2 >> 16,
|
||||
(addr2 + (mapsize >> 3) - 1) >> 16, dm.maphandle,
|
||||
prot, flags, false, false, 0, mapsize >> 3);
|
||||
}
|
||||
return (void *)addr;
|
||||
}
|
||||
}
|
||||
enomem();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ZiposHandle *__zipos_alloc(struct Zipos *zipos, size_t size) {
|
||||
size_t mapsize;
|
||||
struct ZiposHandle *h, **ph;
|
||||
__zipos_lock();
|
||||
mapsize = sizeof(struct ZiposHandle) + size;
|
||||
mapsize = ROUNDUP(mapsize, __zipos_granularity());
|
||||
StartOver:
|
||||
ph = &zipos->freelist;
|
||||
while ((h = *ph)) {
|
||||
if (h->mapsize >= mapsize) {
|
||||
if (!_cmpxchg(ph, h, h->next)) goto StartOver;
|
||||
h->next = 0;
|
||||
break;
|
||||
}
|
||||
ph = &h->next;
|
||||
}
|
||||
if (!h) {
|
||||
h = __zipos_mmap(mapsize);
|
||||
}
|
||||
__zipos_unlock();
|
||||
if (IsAsan()) {
|
||||
__asan_unpoison((char *)h, sizeof(struct ZiposHandle) + size);
|
||||
__asan_poison((char *)h + sizeof(struct ZiposHandle) + size,
|
||||
mapsize - (sizeof(struct ZiposHandle) + size),
|
||||
kAsanHeapOverrun);
|
||||
}
|
||||
if (h) {
|
||||
h->size = size;
|
||||
h->mapsize = mapsize;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
static int __zipos_mkfd(int minfd) {
|
||||
int e, fd;
|
||||
static bool demodernize;
|
||||
if (!demodernize) {
|
||||
e = errno;
|
||||
if ((fd = __sys_fcntl(2, F_DUPFD_CLOEXEC, minfd)) != -1) {
|
||||
return fd;
|
||||
} else if (errno == EINVAL) {
|
||||
demodernize = true;
|
||||
errno = e;
|
||||
} else {
|
||||
return fd;
|
||||
}
|
||||
}
|
||||
if ((fd = __sys_fcntl(2, F_DUPFD, minfd)) != -1) {
|
||||
__sys_fcntl(fd, F_SETFD, FD_CLOEXEC);
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int __zipos_setfd(int fd, struct ZiposHandle *h, unsigned flags,
|
||||
int mode) {
|
||||
_cmpxchg(&g_fds.f, fd, fd + 1);
|
||||
g_fds.p[fd].kind = kFdZip;
|
||||
g_fds.p[fd].handle = (intptr_t)h;
|
||||
g_fds.p[fd].flags = flags | O_CLOEXEC;
|
||||
g_fds.p[fd].mode = mode;
|
||||
g_fds.p[fd].extra = 0;
|
||||
__fds_unlock();
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int __zipos_load(struct Zipos *zipos, size_t cf, unsigned flags,
|
||||
int mode) {
|
||||
size_t lf;
|
||||
int rc, fd;
|
||||
size_t size;
|
||||
uint8_t *data;
|
||||
int rc, fd, minfd;
|
||||
struct ZiposHandle *h;
|
||||
lf = GetZipCfileOffset(zipos->map + cf);
|
||||
if (!IsTiny() &&
|
||||
(ZIP_LFILE_MAGIC(zipos->map + lf) != kZipLfileHdrMagic ||
|
||||
(ZIP_LFILE_COMPRESSIONMETHOD(zipos->map + lf) != kZipCompressionNone &&
|
||||
ZIP_LFILE_COMPRESSIONMETHOD(zipos->map + lf) !=
|
||||
kZipCompressionDeflate))) {
|
||||
return eio();
|
||||
}
|
||||
if (!(h = calloc(1, sizeof(*h)))) return -1;
|
||||
h->cfile = cf;
|
||||
h->size = GetZipLfileUncompressedSize(zipos->map + lf);
|
||||
if (ZIP_LFILE_COMPRESSIONMETHOD(zipos->map + lf)) {
|
||||
if ((h->freeme = malloc(h->size))) {
|
||||
data = ZIP_LFILE_CONTENT(zipos->map + lf);
|
||||
size = GetZipLfileCompressedSize(zipos->map + lf);
|
||||
if ((rc = __zipos_inflate(h, data, size)) != -1) {
|
||||
h->mem = h->freeme;
|
||||
assert((ZIP_LFILE_MAGIC(zipos->map + lf) == kZipLfileHdrMagic));
|
||||
size = GetZipLfileUncompressedSize(zipos->map + lf);
|
||||
switch (ZIP_LFILE_COMPRESSIONMETHOD(zipos->map + lf)) {
|
||||
case kZipCompressionNone:
|
||||
if (!(h = __zipos_alloc(zipos, 0))) return -1;
|
||||
h->mem = ZIP_LFILE_CONTENT(zipos->map + lf);
|
||||
break;
|
||||
case kZipCompressionDeflate:
|
||||
if (!(h = __zipos_alloc(zipos, size))) return -1;
|
||||
if (!__inflate(h->data, size, ZIP_LFILE_CONTENT(zipos->map + lf),
|
||||
GetZipLfileCompressedSize(zipos->map + lf))) {
|
||||
h->mem = h->data;
|
||||
} else {
|
||||
h->mem = 0;
|
||||
eio();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
h->mem = ZIP_LFILE_CONTENT(zipos->map + lf);
|
||||
break;
|
||||
default:
|
||||
return eio();
|
||||
}
|
||||
h->pos = 0;
|
||||
h->cfile = cf;
|
||||
h->size = size;
|
||||
if (!IsTiny() && h->mem &&
|
||||
crc32_z(0, h->mem, h->size) != ZIP_LFILE_CRC32(zipos->map + lf)) {
|
||||
errno = EIO;
|
||||
h->mem = NULL;
|
||||
h->mem = 0;
|
||||
eio();
|
||||
}
|
||||
if (h->mem) {
|
||||
if ((fd = IsWindows() ? __reservefd(-1) : dup(2)) != -1) {
|
||||
if (__ensurefds(fd) != -1) {
|
||||
__fds_lock();
|
||||
h->handle = g_fds.p[fd].handle;
|
||||
g_fds.p[fd].kind = kFdZip;
|
||||
g_fds.p[fd].handle = (intptr_t)h;
|
||||
g_fds.p[fd].flags = flags | O_CLOEXEC;
|
||||
g_fds.p[fd].mode = mode;
|
||||
g_fds.p[fd].extra = 0;
|
||||
__fds_unlock();
|
||||
return fd;
|
||||
minfd = 3;
|
||||
__fds_lock();
|
||||
TryAgain:
|
||||
if (IsWindows()) {
|
||||
if ((fd = __reservefd_unlocked(-1)) != -1) {
|
||||
return __zipos_setfd(fd, h, flags, mode);
|
||||
}
|
||||
close(fd);
|
||||
} else if ((fd = __zipos_mkfd(minfd)) != -1) {
|
||||
if (__ensurefds_unlocked(fd) != -1) {
|
||||
if (g_fds.p[fd].kind) {
|
||||
sys_close(fd);
|
||||
minfd = fd + 1;
|
||||
goto TryAgain;
|
||||
}
|
||||
return __zipos_setfd(fd, h, flags, mode);
|
||||
}
|
||||
sys_close(fd);
|
||||
}
|
||||
__fds_unlock();
|
||||
}
|
||||
free(h->freeme);
|
||||
free(h);
|
||||
__zipos_free(zipos, h);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -106,12 +208,12 @@ static int __zipos_load(struct Zipos *zipos, size_t cf, unsigned flags,
|
|||
* Loads compressed file from αcτµαlly pδrταblε εxεcµταblε object store.
|
||||
*
|
||||
* @param uri is obtained via __zipos_parseuri()
|
||||
* @note don't call open() from signal handlers
|
||||
* @asyncsignalsafe (todo)
|
||||
* @threadsafe
|
||||
*/
|
||||
int __zipos_open(const struct ZiposUri *name, unsigned flags, int mode) {
|
||||
int rc;
|
||||
ssize_t cf;
|
||||
sigset_t oldmask;
|
||||
struct Zipos *zipos;
|
||||
if ((flags & O_ACCMODE) == O_RDONLY) {
|
||||
if ((zipos = __zipos_get())) {
|
||||
|
|
|
@ -1,33 +1,40 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_ZIPOS_ZIPOS_H_
|
||||
#define COSMOPOLITAN_LIBC_ZIPOS_ZIPOS_H_
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/intrin/nopl.h"
|
||||
#include "libc/nexgen32e/threaded.h"
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
struct stat;
|
||||
struct iovec;
|
||||
|
||||
struct Zipos {
|
||||
uint8_t *map;
|
||||
uint8_t *cdir;
|
||||
};
|
||||
|
||||
struct ZiposUri {
|
||||
const char *path;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
struct ZiposHandle {
|
||||
uint8_t *mem; /* uncompressed file memory */
|
||||
size_t size; /* byte length of file memory */
|
||||
struct ZiposHandle *next;
|
||||
size_t size; /* byte length of `mem` */
|
||||
size_t mapsize; /* total size of this struct */
|
||||
size_t pos; /* read/write byte offset state */
|
||||
uint32_t cfile; /* central directory entry rva */
|
||||
int64_t handle;
|
||||
uint8_t *freeme;
|
||||
uint8_t *mem; /* points to inflated data or uncompressed image */
|
||||
uint8_t data[]; /* uncompressed file memory */
|
||||
};
|
||||
|
||||
struct Zipos {
|
||||
uint8_t *map;
|
||||
uint8_t *cdir;
|
||||
struct ZiposHandle *freelist;
|
||||
};
|
||||
|
||||
void __zipos_lock(void) hidden;
|
||||
void __zipos_unlock(void) hidden;
|
||||
int __zipos_close(int) hidden;
|
||||
struct Zipos *__zipos_get(void) pureconst hidden;
|
||||
void __zipos_free(struct Zipos *, struct ZiposHandle *) hidden;
|
||||
ssize_t __zipos_parseuri(const char *, struct ZiposUri *) hidden;
|
||||
ssize_t __zipos_find(struct Zipos *, const struct ZiposUri *);
|
||||
int __zipos_open(const struct ZiposUri *, unsigned, int) hidden;
|
||||
|
@ -42,6 +49,14 @@ int64_t __zipos_lseek(struct ZiposHandle *, int64_t, unsigned) hidden;
|
|||
int __zipos_fcntl(int, int, uintptr_t) hidden;
|
||||
int __zipos_notat(int, const char *) hidden;
|
||||
|
||||
#if defined(__GNUC__) && !defined(__llvm__) && !defined(__STRICT_ANSI__)
|
||||
#define __zipos_lock() _NOPL0("__threadcalls", __zipos_lock)
|
||||
#define __zipos_unlock() _NOPL0("__threadcalls", __zipos_unlock)
|
||||
#else
|
||||
#define __zipos_lock() (__threaded ? __zipos_lock() : 0)
|
||||
#define __zipos_unlock() (__threaded ? __zipos_unlock() : 0)
|
||||
#endif
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_LIBC_ZIPOS_ZIPOS_H_ */
|
||||
|
|
135
test/libc/calls/reservefd_test.c
Normal file
135
test/libc/calls/reservefd_test.c
Normal file
|
@ -0,0 +1,135 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ 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/calls/internal.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/calls/struct/itimerval.h"
|
||||
#include "libc/calls/struct/rlimit.h"
|
||||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/spinlock.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/nexgen32e/threaded.h"
|
||||
#include "libc/rand/rand.h"
|
||||
#include "libc/runtime/stack.h"
|
||||
#include "libc/sysv/consts/clone.h"
|
||||
#include "libc/sysv/consts/itimer.h"
|
||||
#include "libc/sysv/consts/map.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/prot.h"
|
||||
#include "libc/sysv/consts/rlimit.h"
|
||||
#include "libc/sysv/consts/sa.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/testlib/hyperion.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
#include "libc/time/time.h"
|
||||
|
||||
STATIC_YOINK("zip_uri_support");
|
||||
STATIC_YOINK("libc/testlib/hyperion.txt");
|
||||
|
||||
#define THREADS 8
|
||||
|
||||
void PullSomeZipFilesIntoLinkage(void) {
|
||||
gmtime(0);
|
||||
}
|
||||
|
||||
char *stack[THREADS];
|
||||
char tls[THREADS][64];
|
||||
|
||||
TEST(reservefd, testGrowthOfFdsDataStructure) {
|
||||
int i, n;
|
||||
struct rlimit rlim;
|
||||
ASSERT_EQ(g_fds.n, OPEN_MAX);
|
||||
n = 1700; // pe '2**16/40' → 1638 (likely value of g_fds.n)
|
||||
if (!getrlimit(RLIMIT_NOFILE, &rlim)) n = MIN(n, rlim.rlim_cur - 3);
|
||||
for (i = 0; i < n; ++i) {
|
||||
EXPECT_SYS(0, i + 3, open("/zip/usr/share/zoneinfo/UTC", O_RDONLY));
|
||||
}
|
||||
ASSERT_GT(g_fds.n, OPEN_MAX);
|
||||
for (i = 0; i < n; ++i) {
|
||||
EXPECT_SYS(0, 0, close(i + 3));
|
||||
}
|
||||
}
|
||||
|
||||
void OnSigAlrm(int sig, siginfo_t *si, ucontext_t *ctx) {
|
||||
int rc, fd;
|
||||
char buf[64];
|
||||
ASSERT_NE(-1, (fd = open("/zip/libc/testlib/hyperion.txt", O_RDONLY)));
|
||||
for (;;) {
|
||||
rc = read(fd, buf, 64);
|
||||
if (rc != -1) {
|
||||
ASSERT_EQ(64, rc);
|
||||
break;
|
||||
} else {
|
||||
ASSERT_EQ(EINTR, errno);
|
||||
errno = 0;
|
||||
}
|
||||
}
|
||||
ASSERT_EQ(0, memcmp(kHyperion, buf, 64));
|
||||
close(fd); // can eintr which doesn't matter
|
||||
}
|
||||
|
||||
int Worker(void *p) {
|
||||
char buf[64];
|
||||
int i, rc, fd;
|
||||
for (i = 0; i < 64; ++i) {
|
||||
ASSERT_NE(-1, (fd = open("/zip/libc/testlib/hyperion.txt", O_RDONLY)));
|
||||
usleep(rand64() % 100);
|
||||
for (;;) {
|
||||
rc = read(fd, buf, 64);
|
||||
if (rc != -1) {
|
||||
ASSERT_EQ(64, rc);
|
||||
break;
|
||||
} else {
|
||||
ASSERT_EQ(EINTR, errno);
|
||||
errno = 0;
|
||||
}
|
||||
}
|
||||
ASSERT_EQ(0, memcmp(kHyperion, buf, 64));
|
||||
close(fd);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
TEST(reservefd, tortureTest) {
|
||||
int i;
|
||||
struct sigaction oldsa;
|
||||
struct itimerval oldit;
|
||||
struct itimerval it = {{0, 10000}, {0, 100}};
|
||||
struct sigaction sa = {.sa_sigaction = OnSigAlrm,
|
||||
.sa_flags = 0 /* SA_NODEFER */};
|
||||
// ASSERT_SYS(0, 0, sigaction(SIGALRM, &sa, &oldsa));
|
||||
// ASSERT_SYS(0, 0, setitimer(ITIMER_REAL, &it, &oldit));
|
||||
for (i = 0; i < THREADS; ++i) {
|
||||
clone(Worker,
|
||||
(stack[i] = mmap(0, GetStackSize(), PROT_READ | PROT_WRITE,
|
||||
MAP_STACK | MAP_ANONYMOUS, -1, 0)),
|
||||
GetStackSize(),
|
||||
CLONE_THREAD | CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND |
|
||||
CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID | CLONE_SETTLS,
|
||||
0, 0, __initialize_tls(tls[i]), sizeof(tls[i]),
|
||||
(int *)(tls[i] + 0x38));
|
||||
}
|
||||
for (i = 0; i < THREADS; ++i) {
|
||||
_spinlock((int *)(tls[i] + 0x38));
|
||||
}
|
||||
// EXPECT_SYS(0, 0, sigaction(SIGALRM, &oldsa, 0));
|
||||
// EXPECT_SYS(0, 0, setitimer(ITIMER_REAL, &oldit, 0));
|
||||
}
|
|
@ -45,6 +45,7 @@ TEST_LIBC_STR_DIRECTDEPS = \
|
|||
LIBC_STR \
|
||||
LIBC_STUBS \
|
||||
LIBC_SYSV \
|
||||
LIBC_SYSV_CALLS \
|
||||
LIBC_TESTLIB \
|
||||
LIBC_UNICODE \
|
||||
LIBC_X \
|
||||
|
|
4
third_party/make/fcntl.c
vendored
4
third_party/make/fcntl.c
vendored
|
@ -169,10 +169,10 @@ static int klibc_fcntl(int fd, int action, /* arg */...);
|
|||
FD_CLOEXEC is portable, but other flags may be present); otherwise
|
||||
return -1 and set errno. */
|
||||
|
||||
int fcntl(int fd, int action, /* arg */...)
|
||||
int fcntl_(int fd, int action, /* arg */...)
|
||||
#undef fcntl
|
||||
#ifdef __KLIBC__
|
||||
#define fcntl klibc_fcntl
|
||||
#define fcntl_ klibc_fcntl
|
||||
#endif
|
||||
{
|
||||
va_list arg;
|
||||
|
|
1
third_party/python/pythontester.c
vendored
1
third_party/python/pythontester.c
vendored
|
@ -4,6 +4,7 @@
|
|||
│ Python 3 │
|
||||
│ https://docs.python.org/3/license.html │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/log/log.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "third_party/python/Include/yoink.h"
|
||||
#include "third_party/python/runpythonmodule.h"
|
||||
|
|
|
@ -691,6 +691,7 @@
|
|||
(define-key lua-mode-map (kbd "C-c C-r") 'cosmo-run)
|
||||
(define-key python-mode-map (kbd "C-c C-r") 'cosmo-run)
|
||||
(define-key c-mode-map (kbd "C-c C-s") 'cosmo-run-test)
|
||||
(define-key c-mode-map (kbd "C-c C-_") 'cosmo-run-win7)
|
||||
(define-key c-mode-map (kbd "C-c C-_") 'cosmo-run-win10))
|
||||
|
||||
|
||||
|
|
|
@ -6018,6 +6018,10 @@ static char *SetStatus(unsigned code, const char *reason) {
|
|||
}
|
||||
statuscode = code;
|
||||
hascontenttype = istext = false; // reset, as the headers are reset
|
||||
gotxcontenttypeoptions = 0;
|
||||
gotcachecontrol = 0;
|
||||
referrerpolicy = 0;
|
||||
branded = 0;
|
||||
stpcpy(hdrbuf.p, "HTTP/1.0 000 ");
|
||||
hdrbuf.p[7] += msg.version & 1;
|
||||
hdrbuf.p[9] += code / 100;
|
||||
|
|
94
tool/viz/memplan.c
Normal file
94
tool/viz/memplan.c
Normal file
|
@ -0,0 +1,94 @@
|
|||
#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/bits/bits.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/fmt/itoa.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/runtime/memtrack.internal.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
// clang-format off
|
||||
|
||||
struct WinArgs {
|
||||
char *argv[4096];
|
||||
char *envp[4092];
|
||||
intptr_t auxv[2][2];
|
||||
char argblock[ARG_MAX / 2];
|
||||
char envblock[ARG_MAX / 2];
|
||||
};
|
||||
|
||||
//#define INCREMENT roundup2pow(kAutomapSize/16)
|
||||
#define INCREMENT (64L*1024*1024*1024)
|
||||
|
||||
uint64_t last;
|
||||
|
||||
void plan2(uint64_t addr, uint64_t end, const char *name) {
|
||||
char sz[32];
|
||||
FormatMemorySize(sz, end-addr+1);
|
||||
printf("%08x-%08x %-6s %s\n", addr>>16, end>>16, sz, name);
|
||||
}
|
||||
|
||||
void plan(uint64_t addr, uint64_t end, const char *name) {
|
||||
uint64_t incr, next;
|
||||
while (addr > last) {
|
||||
if ((incr = last % INCREMENT)) {
|
||||
incr = INCREMENT - incr;
|
||||
} else {
|
||||
incr = INCREMENT;
|
||||
}
|
||||
plan2(last,MIN(last+incr,addr)-1,"free");
|
||||
last = MIN(last+incr,addr);
|
||||
}
|
||||
while (addr <= end) {
|
||||
if (end-addr+1 <= INCREMENT) {
|
||||
plan2(addr,end,name);
|
||||
last = end + 1;
|
||||
break;
|
||||
}
|
||||
if (!(addr % INCREMENT)) {
|
||||
plan2(addr, addr + INCREMENT - 1, name);
|
||||
last = addr + INCREMENT;
|
||||
addr += INCREMENT;
|
||||
} else {
|
||||
next = INCREMENT - addr % INCREMENT + addr;
|
||||
plan2(addr, next - 1, name);
|
||||
last = next;
|
||||
addr = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
uint64_t x, y;
|
||||
|
||||
plan(0x00000000, 0x00200000-1, "null");
|
||||
plan(0x00200000, 0x00400000-1, "loader");
|
||||
if (!IsWindows() || IsAtLeastWindows10()) {
|
||||
plan(0x00400000, 0x50000000-1, "image");
|
||||
plan(0x50000000, 0x7ffdffff, "arena");
|
||||
plan(0x7fff0000, 0x10007fffffff, "asan");
|
||||
} else {
|
||||
plan(0x00400000, 0x01000000-1, "image");
|
||||
}
|
||||
plan(kAutomapStart, kAutomapStart + kAutomapSize - 1, "automap");
|
||||
plan(kMemtrackStart, kMemtrackStart + kMemtrackSize - 1, "memtrack");
|
||||
plan(kFixedmapStart, kFixedmapStart + kFixedmapSize - 1, "fixedmap");
|
||||
if (IsWindows() && !IsAtLeastWindows10()) {
|
||||
plan(0x50000000, 0x7ffdffff, "arena");
|
||||
}
|
||||
x = (intptr_t)GetStaticStackAddr(0);
|
||||
y = ROUNDUP(sizeof(struct WinArgs), FRAMESIZE);
|
||||
plan(x - y, x - 1, "winargs");
|
||||
plan(x, x + GetStackSize() - 1, "stack");
|
||||
if (!IsWindows() || IsAtLeastWindows10()) {
|
||||
plan(0x7f0000000000, 0x800000000000 - 1, "kernel");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in a new issue